@coool/opencode-adaptive 0.0.0-stage → 1.0.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/LICENSE +21 -0
- package/README.md +99 -2
- package/dist/adapters/codex-capacity/index.d.ts +4 -0
- package/dist/adapters/codex-capacity/index.js +5 -0
- package/dist/adapters/codex-capacity/index.js.map +1 -0
- package/dist/adapters/codex-capacity/parse.d.ts +28 -0
- package/dist/adapters/codex-capacity/parse.js +146 -0
- package/dist/adapters/codex-capacity/parse.js.map +1 -0
- package/dist/adapters/codex-capacity/reading.d.ts +9 -0
- package/dist/adapters/codex-capacity/reading.js +45 -0
- package/dist/adapters/codex-capacity/reading.js.map +1 -0
- package/dist/adapters/codex-capacity/sensor.d.ts +23 -0
- package/dist/adapters/codex-capacity/sensor.js +30 -0
- package/dist/adapters/codex-capacity/sensor.js.map +1 -0
- package/dist/adapters/codex-capacity/types.d.ts +31 -0
- package/dist/adapters/codex-capacity/types.js +18 -0
- package/dist/adapters/codex-capacity/types.js.map +1 -0
- package/dist/adapters/opencode/escalation-tool.d.ts +51 -0
- package/dist/adapters/opencode/escalation-tool.js +61 -0
- package/dist/adapters/opencode/escalation-tool.js.map +1 -0
- package/dist/adapters/opencode/events.d.ts +23 -0
- package/dist/adapters/opencode/events.js +68 -0
- package/dist/adapters/opencode/events.js.map +1 -0
- package/dist/adapters/opencode/hooks.d.ts +34 -0
- package/dist/adapters/opencode/hooks.js +29 -0
- package/dist/adapters/opencode/hooks.js.map +1 -0
- package/dist/adapters/opencode/index.d.ts +16 -0
- package/dist/adapters/opencode/index.js +9 -0
- package/dist/adapters/opencode/index.js.map +1 -0
- package/dist/adapters/opencode/inventory.d.ts +30 -0
- package/dist/adapters/opencode/inventory.js +53 -0
- package/dist/adapters/opencode/inventory.js.map +1 -0
- package/dist/adapters/opencode/report.d.ts +91 -0
- package/dist/adapters/opencode/report.js +145 -0
- package/dist/adapters/opencode/report.js.map +1 -0
- package/dist/adapters/opencode/session-ops.d.ts +39 -0
- package/dist/adapters/opencode/session-ops.js +32 -0
- package/dist/adapters/opencode/session-ops.js.map +1 -0
- package/dist/adapters/opencode/storage.d.ts +15 -0
- package/dist/adapters/opencode/storage.js +8 -0
- package/dist/adapters/opencode/storage.js.map +1 -0
- package/dist/adapters/opencode/tier-tool.d.ts +37 -0
- package/dist/adapters/opencode/tier-tool.js +72 -0
- package/dist/adapters/opencode/tier-tool.js.map +1 -0
- package/dist/adapters/opencode/types.d.ts +101 -0
- package/dist/adapters/opencode/types.js +2 -0
- package/dist/adapters/opencode/types.js.map +1 -0
- package/dist/core/compatibility/gate.d.ts +7 -0
- package/dist/core/compatibility/gate.js +51 -0
- package/dist/core/compatibility/gate.js.map +1 -0
- package/dist/core/compatibility/index.d.ts +2 -0
- package/dist/core/compatibility/index.js +3 -0
- package/dist/core/compatibility/index.js.map +1 -0
- package/dist/core/compatibility/types.d.ts +53 -0
- package/dist/core/compatibility/types.js +27 -0
- package/dist/core/compatibility/types.js.map +1 -0
- package/dist/core/scheduler/admit.d.ts +6 -0
- package/dist/core/scheduler/admit.js +154 -0
- package/dist/core/scheduler/admit.js.map +1 -0
- package/dist/core/scheduler/escalate.d.ts +6 -0
- package/dist/core/scheduler/escalate.js +45 -0
- package/dist/core/scheduler/escalate.js.map +1 -0
- package/dist/core/scheduler/index.d.ts +4 -0
- package/dist/core/scheduler/index.js +5 -0
- package/dist/core/scheduler/index.js.map +1 -0
- package/dist/core/scheduler/mode.d.ts +8 -0
- package/dist/core/scheduler/mode.js +10 -0
- package/dist/core/scheduler/mode.js.map +1 -0
- package/dist/core/scheduler/types.d.ts +136 -0
- package/dist/core/scheduler/types.js +75 -0
- package/dist/core/scheduler/types.js.map +1 -0
- package/dist/core/tier-policy/index.d.ts +3 -0
- package/dist/core/tier-policy/index.js +4 -0
- package/dist/core/tier-policy/index.js.map +1 -0
- package/dist/core/tier-policy/resolve.d.ts +18 -0
- package/dist/core/tier-policy/resolve.js +52 -0
- package/dist/core/tier-policy/resolve.js.map +1 -0
- package/dist/core/tier-policy/tier-list.d.ts +10 -0
- package/dist/core/tier-policy/tier-list.js +23 -0
- package/dist/core/tier-policy/tier-list.js.map +1 -0
- package/dist/core/tier-policy/types.d.ts +86 -0
- package/dist/core/tier-policy/types.js +49 -0
- package/dist/core/tier-policy/types.js.map +1 -0
- package/dist/core/work-unit/index.d.ts +3 -0
- package/dist/core/work-unit/index.js +4 -0
- package/dist/core/work-unit/index.js.map +1 -0
- package/dist/core/work-unit/interruption.d.ts +7 -0
- package/dist/core/work-unit/interruption.js +12 -0
- package/dist/core/work-unit/interruption.js.map +1 -0
- package/dist/core/work-unit/tracker.d.ts +53 -0
- package/dist/core/work-unit/tracker.js +156 -0
- package/dist/core/work-unit/tracker.js.map +1 -0
- package/dist/core/work-unit/types.d.ts +151 -0
- package/dist/core/work-unit/types.js +23 -0
- package/dist/core/work-unit/types.js.map +1 -0
- package/dist/execution/continuation/execute.d.ts +18 -0
- package/dist/execution/continuation/execute.js +31 -0
- package/dist/execution/continuation/execute.js.map +1 -0
- package/dist/execution/continuation/index.d.ts +4 -0
- package/dist/execution/continuation/index.js +4 -0
- package/dist/execution/continuation/index.js.map +1 -0
- package/dist/execution/continuation/plan.d.ts +12 -0
- package/dist/execution/continuation/plan.js +18 -0
- package/dist/execution/continuation/plan.js.map +1 -0
- package/dist/execution/continuation/prompts.d.ts +7 -0
- package/dist/execution/continuation/prompts.js +8 -0
- package/dist/execution/continuation/prompts.js.map +1 -0
- package/dist/execution/continuation/types.d.ts +68 -0
- package/dist/execution/continuation/types.js +15 -0
- package/dist/execution/continuation/types.js.map +1 -0
- package/dist/execution/escalation/index.d.ts +1 -0
- package/dist/execution/escalation/index.js +2 -0
- package/dist/execution/escalation/index.js.map +1 -0
- package/dist/execution/escalation/types.d.ts +30 -0
- package/dist/execution/escalation/types.js +49 -0
- package/dist/execution/escalation/types.js.map +1 -0
- package/dist/execution/index.d.ts +4 -0
- package/dist/execution/index.js +5 -0
- package/dist/execution/index.js.map +1 -0
- package/dist/execution/parking/index.d.ts +2 -0
- package/dist/execution/parking/index.js +2 -0
- package/dist/execution/parking/index.js.map +1 -0
- package/dist/execution/parking/plan.d.ts +39 -0
- package/dist/execution/parking/plan.js +34 -0
- package/dist/execution/parking/plan.js.map +1 -0
- package/dist/execution/retry/decide.d.ts +29 -0
- package/dist/execution/retry/decide.js +75 -0
- package/dist/execution/retry/decide.js.map +1 -0
- package/dist/execution/retry/index.d.ts +2 -0
- package/dist/execution/retry/index.js +3 -0
- package/dist/execution/retry/index.js.map +1 -0
- package/dist/execution/retry/types.d.ts +64 -0
- package/dist/execution/retry/types.js +9 -0
- package/dist/execution/retry/types.js.map +1 -0
- package/dist/gui-experimental.cjs +1 -0
- package/dist/gui.d.ts +16 -0
- package/dist/gui.js +73 -0
- package/dist/gui.js.map +1 -0
- package/dist/health/capacity/index.d.ts +3 -0
- package/dist/health/capacity/index.js +4 -0
- package/dist/health/capacity/index.js.map +1 -0
- package/dist/health/capacity/merge.d.ts +57 -0
- package/dist/health/capacity/merge.js +154 -0
- package/dist/health/capacity/merge.js.map +1 -0
- package/dist/health/capacity/registry.d.ts +40 -0
- package/dist/health/capacity/registry.js +106 -0
- package/dist/health/capacity/registry.js.map +1 -0
- package/dist/health/capacity/types.d.ts +73 -0
- package/dist/health/capacity/types.js +6 -0
- package/dist/health/capacity/types.js.map +1 -0
- package/dist/health/circuit-breaker/breaker.d.ts +91 -0
- package/dist/health/circuit-breaker/breaker.js +150 -0
- package/dist/health/circuit-breaker/breaker.js.map +1 -0
- package/dist/health/circuit-breaker/index.d.ts +2 -0
- package/dist/health/circuit-breaker/index.js +3 -0
- package/dist/health/circuit-breaker/index.js.map +1 -0
- package/dist/health/circuit-breaker/types.d.ts +52 -0
- package/dist/health/circuit-breaker/types.js +13 -0
- package/dist/health/circuit-breaker/types.js.map +1 -0
- package/dist/health/classifier/classify.d.ts +8 -0
- package/dist/health/classifier/classify.js +245 -0
- package/dist/health/classifier/classify.js.map +1 -0
- package/dist/health/classifier/index.d.ts +2 -0
- package/dist/health/classifier/index.js +3 -0
- package/dist/health/classifier/index.js.map +1 -0
- package/dist/health/classifier/types.d.ts +71 -0
- package/dist/health/classifier/types.js +27 -0
- package/dist/health/classifier/types.js.map +1 -0
- package/dist/health/index.d.ts +4 -0
- package/dist/health/index.js +5 -0
- package/dist/health/index.js.map +1 -0
- package/dist/health/resource-health/index.d.ts +4 -0
- package/dist/health/resource-health/index.js +5 -0
- package/dist/health/resource-health/index.js.map +1 -0
- package/dist/health/resource-health/key.d.ts +18 -0
- package/dist/health/resource-health/key.js +54 -0
- package/dist/health/resource-health/key.js.map +1 -0
- package/dist/health/resource-health/registry.d.ts +46 -0
- package/dist/health/resource-health/registry.js +276 -0
- package/dist/health/resource-health/registry.js.map +1 -0
- package/dist/health/resource-health/transitions.d.ts +9 -0
- package/dist/health/resource-health/transitions.js +157 -0
- package/dist/health/resource-health/transitions.js.map +1 -0
- package/dist/health/resource-health/types.d.ts +122 -0
- package/dist/health/resource-health/types.js +44 -0
- package/dist/health/resource-health/types.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +105 -0
- package/dist/index.js.map +1 -0
- package/dist/plugin-id.d.ts +2 -0
- package/dist/plugin-id.js +3 -0
- package/dist/plugin-id.js.map +1 -0
- package/dist/rpc/definition.d.ts +123 -0
- package/dist/rpc/definition.js +130 -0
- package/dist/rpc/definition.js.map +1 -0
- package/dist/rpc/handlers.d.ts +53 -0
- package/dist/rpc/handlers.js +125 -0
- package/dist/rpc/handlers.js.map +1 -0
- package/dist/rpc/status.d.ts +42 -0
- package/dist/rpc/status.js +45 -0
- package/dist/rpc/status.js.map +1 -0
- package/dist/rpc.d.ts +10 -0
- package/dist/rpc.js +8 -0
- package/dist/rpc.js.map +1 -0
- package/dist/state/capacity/index.d.ts +3 -0
- package/dist/state/capacity/index.js +4 -0
- package/dist/state/capacity/index.js.map +1 -0
- package/dist/state/capacity/parse.d.ts +2 -0
- package/dist/state/capacity/parse.js +122 -0
- package/dist/state/capacity/parse.js.map +1 -0
- package/dist/state/capacity/store.d.ts +18 -0
- package/dist/state/capacity/store.js +78 -0
- package/dist/state/capacity/store.js.map +1 -0
- package/dist/state/capacity/types.d.ts +37 -0
- package/dist/state/capacity/types.js +11 -0
- package/dist/state/capacity/types.js.map +1 -0
- package/dist/state/health/index.d.ts +4 -0
- package/dist/state/health/index.js +4 -0
- package/dist/state/health/index.js.map +1 -0
- package/dist/state/health/parse.d.ts +2 -0
- package/dist/state/health/parse.js +137 -0
- package/dist/state/health/parse.js.map +1 -0
- package/dist/state/health/store.d.ts +17 -0
- package/dist/state/health/store.js +69 -0
- package/dist/state/health/store.js.map +1 -0
- package/dist/state/health/types.d.ts +23 -0
- package/dist/state/health/types.js +5 -0
- package/dist/state/health/types.js.map +1 -0
- package/dist/state/parked-work/index.d.ts +4 -0
- package/dist/state/parked-work/index.js +4 -0
- package/dist/state/parked-work/index.js.map +1 -0
- package/dist/state/parked-work/parse.d.ts +6 -0
- package/dist/state/parked-work/parse.js +83 -0
- package/dist/state/parked-work/parse.js.map +1 -0
- package/dist/state/parked-work/store.d.ts +17 -0
- package/dist/state/parked-work/store.js +98 -0
- package/dist/state/parked-work/store.js.map +1 -0
- package/dist/state/parked-work/types.d.ts +41 -0
- package/dist/state/parked-work/types.js +7 -0
- package/dist/state/parked-work/types.js.map +1 -0
- package/dist/state/receipts/bound.d.ts +14 -0
- package/dist/state/receipts/bound.js +77 -0
- package/dist/state/receipts/bound.js.map +1 -0
- package/dist/state/receipts/index.d.ts +6 -0
- package/dist/state/receipts/index.js +5 -0
- package/dist/state/receipts/index.js.map +1 -0
- package/dist/state/receipts/parse.d.ts +6 -0
- package/dist/state/receipts/parse.js +226 -0
- package/dist/state/receipts/parse.js.map +1 -0
- package/dist/state/receipts/store.d.ts +34 -0
- package/dist/state/receipts/store.js +148 -0
- package/dist/state/receipts/store.js.map +1 -0
- package/dist/state/receipts/types.d.ts +61 -0
- package/dist/state/receipts/types.js +12 -0
- package/dist/state/receipts/types.js.map +1 -0
- package/dist/state/session-modes/index.d.ts +4 -0
- package/dist/state/session-modes/index.js +4 -0
- package/dist/state/session-modes/index.js.map +1 -0
- package/dist/state/session-modes/parse.d.ts +8 -0
- package/dist/state/session-modes/parse.js +62 -0
- package/dist/state/session-modes/parse.js.map +1 -0
- package/dist/state/session-modes/store.d.ts +20 -0
- package/dist/state/session-modes/store.js +87 -0
- package/dist/state/session-modes/store.js.map +1 -0
- package/dist/state/session-modes/types.d.ts +41 -0
- package/dist/state/session-modes/types.js +7 -0
- package/dist/state/session-modes/types.js.map +1 -0
- package/dist/state/settings/index.d.ts +4 -0
- package/dist/state/settings/index.js +4 -0
- package/dist/state/settings/index.js.map +1 -0
- package/dist/state/settings/parse.d.ts +33 -0
- package/dist/state/settings/parse.js +261 -0
- package/dist/state/settings/parse.js.map +1 -0
- package/dist/state/settings/store.d.ts +21 -0
- package/dist/state/settings/store.js +65 -0
- package/dist/state/settings/store.js.map +1 -0
- package/dist/state/settings/types.d.ts +62 -0
- package/dist/state/settings/types.js +12 -0
- package/dist/state/settings/types.js.map +1 -0
- package/dist/state/tier-requests/index.d.ts +4 -0
- package/dist/state/tier-requests/index.js +4 -0
- package/dist/state/tier-requests/index.js.map +1 -0
- package/dist/state/tier-requests/parse.d.ts +3 -0
- package/dist/state/tier-requests/parse.js +53 -0
- package/dist/state/tier-requests/parse.js.map +1 -0
- package/dist/state/tier-requests/store.d.ts +27 -0
- package/dist/state/tier-requests/store.js +96 -0
- package/dist/state/tier-requests/store.js.map +1 -0
- package/dist/state/tier-requests/types.d.ts +30 -0
- package/dist/state/tier-requests/types.js +7 -0
- package/dist/state/tier-requests/types.js.map +1 -0
- package/dist/supervisor/index.d.ts +4 -0
- package/dist/supervisor/index.js +4 -0
- package/dist/supervisor/index.js.map +1 -0
- package/dist/supervisor/log.d.ts +10 -0
- package/dist/supervisor/log.js +14 -0
- package/dist/supervisor/log.js.map +1 -0
- package/dist/supervisor/supervisor.d.ts +86 -0
- package/dist/supervisor/supervisor.js +1629 -0
- package/dist/supervisor/supervisor.js.map +1 -0
- package/dist/supervisor/types.d.ts +49 -0
- package/dist/supervisor/types.js +3 -0
- package/dist/supervisor/types.js.map +1 -0
- package/dist/supervisor/view.d.ts +3 -0
- package/dist/supervisor/view.js +4 -0
- package/dist/supervisor/view.js.map +1 -0
- package/dist/tui.d.ts +15 -0
- package/dist/tui.js +256 -0
- package/dist/tui.js.map +1 -0
- package/dist/ui/gui/api.d.ts +17 -0
- package/dist/ui/gui/api.js +21 -0
- package/dist/ui/gui/api.js.map +1 -0
- package/dist/ui/gui/view.d.ts +26 -0
- package/dist/ui/gui/view.js +49 -0
- package/dist/ui/gui/view.js.map +1 -0
- package/dist/ui/tui/controller.d.ts +97 -0
- package/dist/ui/tui/controller.js +415 -0
- package/dist/ui/tui/controller.js.map +1 -0
- package/dist/ui/tui/menu.d.ts +55 -0
- package/dist/ui/tui/menu.js +176 -0
- package/dist/ui/tui/menu.js.map +1 -0
- package/dist/ui/tui/options.d.ts +19 -0
- package/dist/ui/tui/options.js +17 -0
- package/dist/ui/tui/options.js.map +1 -0
- package/dist/ui/tui/receipts.d.ts +21 -0
- package/dist/ui/tui/receipts.js +112 -0
- package/dist/ui/tui/receipts.js.map +1 -0
- package/dist/ui/tui/status.d.ts +22 -0
- package/dist/ui/tui/status.js +92 -0
- package/dist/ui/tui/status.js.map +1 -0
- package/dist/ui/tui/tiers.d.ts +51 -0
- package/dist/ui/tui/tiers.js +108 -0
- package/dist/ui/tui/tiers.js.map +1 -0
- package/package.json +85 -4
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
import { TIERS } from "../../core/tier-policy/types.js";
|
|
2
|
+
import { STATUS_EMPTY_MESSAGE, statusTable } from "./status.js";
|
|
3
|
+
import { mainMenuOptions, modeOptions, modeTargetOptions, sessionModeOptions, sessionTierOptions, } from "./menu.js";
|
|
4
|
+
import { modelPickerOptions, moveResource, moveResourceToTop, removeResource, resourceLabel, tierEditorOptions, tierMenuOptions, variantOptions, } from "./tiers.js";
|
|
5
|
+
import { errorText } from "./options.js";
|
|
6
|
+
import { receiptDetail, receiptLine, receiptPage, RECEIPTS_PAGE_SIZE } from "./receipts.js";
|
|
7
|
+
export const INVITE_TITLE = "OpenCode Adaptive";
|
|
8
|
+
export const INVITE_MESSAGE = "Adaptive starts in Observe mode: routing decisions are recorded as receipts, " +
|
|
9
|
+
"but no model is switched. Open /adaptive to configure tiers or switch to Active.";
|
|
10
|
+
/** Run one API call; on failure toast and return undefined. */
|
|
11
|
+
async function tryApi(env, run) {
|
|
12
|
+
try {
|
|
13
|
+
return await run();
|
|
14
|
+
}
|
|
15
|
+
catch (error) {
|
|
16
|
+
env.view.toast(errorText(error), "error");
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Apply one patch; alert on validation errors, toast on success. The host
|
|
22
|
+
* never auto-dismisses toasts, so the success toast is shown only on the
|
|
23
|
+
* first successful save of a dialog session — later saves stay silent
|
|
24
|
+
* (the updated menu already reflects them).
|
|
25
|
+
*/
|
|
26
|
+
async function applySettings(env, patch, feedback) {
|
|
27
|
+
const result = await tryApi(env, () => env.api.setSettings(patch));
|
|
28
|
+
if (result === undefined)
|
|
29
|
+
return undefined;
|
|
30
|
+
if (!result.ok) {
|
|
31
|
+
await env.view.alert({ title: "Invalid settings", message: result.errors.join("; ") });
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
34
|
+
if (!feedback.once) {
|
|
35
|
+
env.view.toast("Settings saved", "success");
|
|
36
|
+
feedback.once = true;
|
|
37
|
+
}
|
|
38
|
+
return result.settings;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Persist one session-mode override; alert on errors, toast on success. Same
|
|
42
|
+
* once-toast semantics as `applySettings` — the success toast is shown only
|
|
43
|
+
* on the first successful save of a dialog session.
|
|
44
|
+
*/
|
|
45
|
+
async function applySessionMode(env, sessionID, mode, feedback) {
|
|
46
|
+
const result = await tryApi(env, () => env.api.setSessionMode(sessionID, mode));
|
|
47
|
+
if (result === undefined)
|
|
48
|
+
return undefined;
|
|
49
|
+
if (!result.ok) {
|
|
50
|
+
await env.view.alert({ title: "Invalid settings", message: result.errors.join("; ") });
|
|
51
|
+
return undefined;
|
|
52
|
+
}
|
|
53
|
+
if (!feedback.once) {
|
|
54
|
+
env.view.toast("Settings saved", "success");
|
|
55
|
+
feedback.once = true;
|
|
56
|
+
}
|
|
57
|
+
return result;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Persist one session tier request; alert on errors, toast on success. Same
|
|
61
|
+
* once-toast semantics as `applySettings` — the success toast is shown only
|
|
62
|
+
* on the first successful save of a dialog session.
|
|
63
|
+
*/
|
|
64
|
+
async function applyTierRequest(env, sessionID, tier, feedback) {
|
|
65
|
+
const result = await tryApi(env, () => env.api.setTierRequest(sessionID, tier));
|
|
66
|
+
if (result === undefined)
|
|
67
|
+
return undefined;
|
|
68
|
+
if (!result.ok) {
|
|
69
|
+
await env.view.alert({ title: "Invalid tier request", message: result.errors.join("; ") });
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
72
|
+
if (!feedback.once) {
|
|
73
|
+
env.view.toast("Settings saved", "success");
|
|
74
|
+
feedback.once = true;
|
|
75
|
+
}
|
|
76
|
+
return result;
|
|
77
|
+
}
|
|
78
|
+
export async function runAdaptive(env) {
|
|
79
|
+
const loaded = await tryApi(env, () => env.api.settings());
|
|
80
|
+
if (loaded === undefined)
|
|
81
|
+
return;
|
|
82
|
+
let settings = loaded;
|
|
83
|
+
const feedback = { once: false };
|
|
84
|
+
const sessionID = env.sessionID;
|
|
85
|
+
// The session's current override: fetched once when the flow starts inside
|
|
86
|
+
// a session; a failure degrades to "follow global" (the menu still works).
|
|
87
|
+
let override = sessionID === undefined
|
|
88
|
+
? null
|
|
89
|
+
: ((await tryApi(env, () => env.api.sessionMode(sessionID))) ?? null);
|
|
90
|
+
if (!settings.inviteDismissed) {
|
|
91
|
+
await env.view.confirm({ title: INVITE_TITLE, message: INVITE_MESSAGE });
|
|
92
|
+
const pick = await env.view.select({
|
|
93
|
+
title: INVITE_TITLE,
|
|
94
|
+
options: [
|
|
95
|
+
{ title: "Open the menu now", value: true },
|
|
96
|
+
{ title: "Later", value: false },
|
|
97
|
+
],
|
|
98
|
+
});
|
|
99
|
+
const dismissed = await applySettings(env, { inviteDismissed: true }, feedback);
|
|
100
|
+
if (dismissed !== undefined)
|
|
101
|
+
settings = dismissed;
|
|
102
|
+
if (pick !== true)
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
for (;;) {
|
|
106
|
+
// Fresh status and settings on every pass: the menu rows pair the two,
|
|
107
|
+
// and the server is the single source of truth (ADP-036). A failed read
|
|
108
|
+
// degrades silently to the last known values — the menu descriptions
|
|
109
|
+
// must not spam a toast on every loop iteration (the flows that need
|
|
110
|
+
// status toast through tryApi themselves).
|
|
111
|
+
const [freshSettings, status] = await Promise.all([
|
|
112
|
+
env.api.settings().catch(() => undefined),
|
|
113
|
+
env.api.status().catch(() => undefined),
|
|
114
|
+
]);
|
|
115
|
+
if (freshSettings !== undefined)
|
|
116
|
+
settings = freshSettings;
|
|
117
|
+
// The session's pinned tier request: fetched per loop so the entry
|
|
118
|
+
// description tracks the latest request; a failure degrades to the
|
|
119
|
+
// generic description (the menu still works).
|
|
120
|
+
const tierPinned = sessionID === undefined
|
|
121
|
+
? undefined
|
|
122
|
+
: await env.api.tierRequest(sessionID).catch(() => undefined);
|
|
123
|
+
const choice = await env.view.select({
|
|
124
|
+
title: `Adaptive (${override ?? settings.mode})`,
|
|
125
|
+
options: mainMenuOptions(settings, status, tierPinned, override ?? settings.mode),
|
|
126
|
+
});
|
|
127
|
+
if (choice === undefined)
|
|
128
|
+
return;
|
|
129
|
+
if (choice === "status") {
|
|
130
|
+
await runStatusView(env);
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
if (choice === "models") {
|
|
134
|
+
settings = (await runTierEditor(env, feedback)) ?? settings;
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
if (choice === "mode") {
|
|
138
|
+
const next = await runModeMenu(env, settings, feedback, sessionID, override);
|
|
139
|
+
if (next !== undefined) {
|
|
140
|
+
settings = next.settings;
|
|
141
|
+
override = next.override;
|
|
142
|
+
}
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
if (choice === "tier") {
|
|
146
|
+
if (sessionID === undefined) {
|
|
147
|
+
// The tier request only exists inside a session; on home the pick is
|
|
148
|
+
// explained (not silently dropped) and the menu re-opens.
|
|
149
|
+
await env.view.alert({
|
|
150
|
+
title: "This session",
|
|
151
|
+
message: "This session's tier request can only be changed inside a session. Open a session and run /adaptive from there.",
|
|
152
|
+
});
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
await runTierRequestMenu(env, sessionID, feedback);
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
if (choice === "pin") {
|
|
159
|
+
settings = (await runPinMenu(env, settings, feedback)) ?? settings;
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
await runReceiptsView(env);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
async function runModeMenu(env, settings, feedback, sessionID, override) {
|
|
166
|
+
// Every screen gets the same first question — which mode to steer — so a
|
|
167
|
+
// user who opens the menu on home learns the two-level structure before
|
|
168
|
+
// they ever run the command inside a session.
|
|
169
|
+
for (;;) {
|
|
170
|
+
const target = await env.view.select({
|
|
171
|
+
title: "Operating mode",
|
|
172
|
+
options: modeTargetOptions(settings.mode, override ?? settings.mode),
|
|
173
|
+
});
|
|
174
|
+
if (target === undefined)
|
|
175
|
+
return undefined;
|
|
176
|
+
if (target === "global") {
|
|
177
|
+
const pick = await env.view.select({
|
|
178
|
+
title: "Global mode",
|
|
179
|
+
options: modeOptions(settings.mode),
|
|
180
|
+
});
|
|
181
|
+
if (pick === undefined || pick === settings.mode)
|
|
182
|
+
return undefined;
|
|
183
|
+
const applied = await applySettings(env, { mode: pick }, feedback);
|
|
184
|
+
// The session override is deliberately untouched: changing the global
|
|
185
|
+
// default must not rewrite what a session was pinned to.
|
|
186
|
+
return applied === undefined ? undefined : { settings: applied, override };
|
|
187
|
+
}
|
|
188
|
+
if (sessionID === undefined) {
|
|
189
|
+
// The session override only exists inside a session; on home the pick
|
|
190
|
+
// is explained (not silently dropped) and the target question repeats.
|
|
191
|
+
await env.view.alert({
|
|
192
|
+
title: "This session",
|
|
193
|
+
message: "This session's mode can only be changed inside a session. Open a session and run /adaptive from there.",
|
|
194
|
+
});
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
const pick = await env.view.select({
|
|
198
|
+
title: "This session's mode",
|
|
199
|
+
options: sessionModeOptions({
|
|
200
|
+
global: settings.mode,
|
|
201
|
+
override,
|
|
202
|
+
effective: override ?? settings.mode,
|
|
203
|
+
}),
|
|
204
|
+
});
|
|
205
|
+
if (pick === undefined)
|
|
206
|
+
return undefined;
|
|
207
|
+
if (pick.kind === "global") {
|
|
208
|
+
if (override === null)
|
|
209
|
+
return undefined;
|
|
210
|
+
const saved = await applySessionMode(env, sessionID, null, feedback);
|
|
211
|
+
return saved === undefined ? undefined : { settings, override: null };
|
|
212
|
+
}
|
|
213
|
+
if (pick.mode === (override ?? settings.mode))
|
|
214
|
+
return undefined;
|
|
215
|
+
const saved = await applySessionMode(env, sessionID, pick.mode, feedback);
|
|
216
|
+
return saved === undefined ? undefined : { settings, override: pick.mode };
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
/** The per-session tier-request menu (ADP-055): pin a tier or clear it. */
|
|
220
|
+
async function runTierRequestMenu(env, sessionID, feedback) {
|
|
221
|
+
const pinned = await tryApi(env, () => env.api.tierRequest(sessionID));
|
|
222
|
+
if (pinned === undefined)
|
|
223
|
+
return;
|
|
224
|
+
const pick = await env.view.select({
|
|
225
|
+
title: "This session's tier",
|
|
226
|
+
options: sessionTierOptions({ pinned }),
|
|
227
|
+
});
|
|
228
|
+
if (pick === undefined)
|
|
229
|
+
return;
|
|
230
|
+
const next = pick.kind === "ladder" ? null : pick.tier;
|
|
231
|
+
if (next === pinned)
|
|
232
|
+
return;
|
|
233
|
+
await applyTierRequest(env, sessionID, next, feedback);
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* The status view: every configured model with its state as a column-aligned
|
|
237
|
+
* table (header row, tier order, priority order within tiers). No
|
|
238
|
+
* per-resource selection step — the table IS the view; Escape returns to the
|
|
239
|
+
* menu.
|
|
240
|
+
*/
|
|
241
|
+
async function runStatusView(env) {
|
|
242
|
+
const snapshot = await tryApi(env, () => env.api.status());
|
|
243
|
+
if (snapshot === undefined)
|
|
244
|
+
return;
|
|
245
|
+
const configured = TIERS.some((tier) => snapshot.tiers[tier].length > 0);
|
|
246
|
+
if (!configured) {
|
|
247
|
+
await env.view.alert({ title: "Status", message: STATUS_EMPTY_MESSAGE });
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
await env.view.table({ title: "Status", lines: statusTable(snapshot) });
|
|
251
|
+
}
|
|
252
|
+
async function runTierEditor(env, feedback) {
|
|
253
|
+
// The last confirmed server snapshot from this editor session, returned
|
|
254
|
+
// when the editor closes so the menu view stays in sync with the server.
|
|
255
|
+
let saved;
|
|
256
|
+
for (;;) {
|
|
257
|
+
// The server is the single source of truth (ADP-036): settings are
|
|
258
|
+
// re-fetched on every editor pass — entering the editor and after each
|
|
259
|
+
// save — so the rows pair fresh settings with fresh status and an edit
|
|
260
|
+
// can never clobber a change made outside this dialog.
|
|
261
|
+
const settings = await tryApi(env, () => env.api.settings());
|
|
262
|
+
if (settings === undefined)
|
|
263
|
+
return saved;
|
|
264
|
+
const status = await tryApi(env, () => env.api.status());
|
|
265
|
+
if (status === undefined)
|
|
266
|
+
return saved;
|
|
267
|
+
const tier = await env.view.select({ title: "Tier", options: tierMenuOptions(status) });
|
|
268
|
+
if (tier === undefined)
|
|
269
|
+
return saved;
|
|
270
|
+
const choice = await env.view.select({
|
|
271
|
+
title: `Tier: ${tier}`,
|
|
272
|
+
options: tierEditorOptions(settings, tier, status),
|
|
273
|
+
});
|
|
274
|
+
if (choice === undefined)
|
|
275
|
+
continue;
|
|
276
|
+
if (choice.kind === "add") {
|
|
277
|
+
const next = await runAddModel(env, settings, tier, feedback);
|
|
278
|
+
if (next !== undefined)
|
|
279
|
+
saved = next;
|
|
280
|
+
continue;
|
|
281
|
+
}
|
|
282
|
+
const next = await runResourceMenu(env, settings, tier, choice.index, feedback);
|
|
283
|
+
if (next !== undefined)
|
|
284
|
+
saved = next;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
async function runAddModel(env, settings, tier, feedback) {
|
|
288
|
+
const models = env.models();
|
|
289
|
+
const pick = await env.view.select({
|
|
290
|
+
title: "Add model",
|
|
291
|
+
placeholder: "Search provider / model / name",
|
|
292
|
+
options: modelPickerOptions(models, ""),
|
|
293
|
+
search: (query) => modelPickerOptions(models, query),
|
|
294
|
+
});
|
|
295
|
+
if (pick === undefined)
|
|
296
|
+
return undefined;
|
|
297
|
+
const info = models.find((m) => m.providerID === pick.providerID && m.modelID === pick.modelID);
|
|
298
|
+
const variants = info?.variants ?? [];
|
|
299
|
+
let variant;
|
|
300
|
+
if (variants.length > 0) {
|
|
301
|
+
const choice = await env.view.select({
|
|
302
|
+
title: `Variant: ${pick.providerID}/${pick.modelID}`,
|
|
303
|
+
options: variantOptions(variants),
|
|
304
|
+
});
|
|
305
|
+
if (choice === undefined)
|
|
306
|
+
return undefined;
|
|
307
|
+
variant = choice.variant;
|
|
308
|
+
}
|
|
309
|
+
const ref = variant === undefined
|
|
310
|
+
? { providerID: pick.providerID, id: pick.modelID }
|
|
311
|
+
: { providerID: pick.providerID, id: pick.modelID, variant };
|
|
312
|
+
const list = [...settings.tiers[tier], ref];
|
|
313
|
+
return applySettings(env, { tiers: { ...settings.tiers, [tier]: list } }, feedback);
|
|
314
|
+
}
|
|
315
|
+
function resourceMenuOptions() {
|
|
316
|
+
return [
|
|
317
|
+
{ title: "Move to top", value: "top", description: "Make this the primary resource" },
|
|
318
|
+
{ title: "Move up", value: "up", description: "Higher priority within the tier" },
|
|
319
|
+
{ title: "Move down", value: "down", description: "Lower priority within the tier" },
|
|
320
|
+
{ title: "Remove", value: "remove" },
|
|
321
|
+
];
|
|
322
|
+
}
|
|
323
|
+
async function runResourceMenu(env, settings, tier, index, feedback) {
|
|
324
|
+
const ref = settings.tiers[tier][index];
|
|
325
|
+
if (ref === undefined)
|
|
326
|
+
return undefined;
|
|
327
|
+
const pick = await env.view.select({
|
|
328
|
+
title: `Resource: ${resourceLabel(ref)}`,
|
|
329
|
+
options: resourceMenuOptions(),
|
|
330
|
+
});
|
|
331
|
+
if (pick === undefined)
|
|
332
|
+
return undefined;
|
|
333
|
+
if (pick === "remove") {
|
|
334
|
+
const confirmed = await env.view.confirm({
|
|
335
|
+
title: "Remove resource",
|
|
336
|
+
message: `Remove ${resourceLabel(ref)} from ${tier}?`,
|
|
337
|
+
});
|
|
338
|
+
if (confirmed !== true)
|
|
339
|
+
return undefined;
|
|
340
|
+
return applySettings(env, { tiers: { ...settings.tiers, [tier]: removeResource(settings.tiers[tier], index) } }, feedback);
|
|
341
|
+
}
|
|
342
|
+
const list = pick === "top"
|
|
343
|
+
? moveResourceToTop(settings.tiers[tier], index)
|
|
344
|
+
: moveResource(settings.tiers[tier], index, pick === "up" ? -1 : 1);
|
|
345
|
+
return applySettings(env, { tiers: { ...settings.tiers, [tier]: list } }, feedback);
|
|
346
|
+
}
|
|
347
|
+
function pinOptions(settings) {
|
|
348
|
+
const options = [
|
|
349
|
+
{ title: "No pin", value: { kind: "clear" }, description: "clear the pin" },
|
|
350
|
+
];
|
|
351
|
+
for (const tier of TIERS) {
|
|
352
|
+
for (const ref of settings.tiers[tier]) {
|
|
353
|
+
options.push({
|
|
354
|
+
title: `${resourceLabel(ref)} (${tier})`,
|
|
355
|
+
description: "route every turn to this resource, even in active mode",
|
|
356
|
+
value: { kind: "pin", ref },
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
return options;
|
|
361
|
+
}
|
|
362
|
+
async function runPinMenu(env, settings, feedback) {
|
|
363
|
+
const configured = TIERS.some((tier) => settings.tiers[tier].length > 0);
|
|
364
|
+
if (!configured) {
|
|
365
|
+
await env.view.alert({ title: "Pin", message: "(no configured resources to pin)" });
|
|
366
|
+
return undefined;
|
|
367
|
+
}
|
|
368
|
+
const pick = await env.view.select({ title: "Pin", options: pinOptions(settings) });
|
|
369
|
+
if (pick === undefined)
|
|
370
|
+
return undefined;
|
|
371
|
+
return applySettings(env, { pin: pick.kind === "clear" ? null : pick.ref }, feedback);
|
|
372
|
+
}
|
|
373
|
+
async function runReceiptsView(env) {
|
|
374
|
+
const all = await tryApi(env, () => env.api.receipts());
|
|
375
|
+
if (all === undefined)
|
|
376
|
+
return;
|
|
377
|
+
if (all.length === 0) {
|
|
378
|
+
await env.view.alert({
|
|
379
|
+
title: "Receipts",
|
|
380
|
+
message: "No decisions recorded yet.",
|
|
381
|
+
});
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
// Paginated browse: every page is a bounded view, and the page flips are
|
|
385
|
+
// footer actions (not list items) — they close the dialog and the loop
|
|
386
|
+
// reopens it at the new offset. Viewing a detail returns to the same
|
|
387
|
+
// page; Escape (no pick, no action) leaves the browser.
|
|
388
|
+
for (let offset = 0;;) {
|
|
389
|
+
const page = receiptPage(all.slice(offset));
|
|
390
|
+
const hasNext = offset + RECEIPTS_PAGE_SIZE < all.length;
|
|
391
|
+
const result = await env.view.selectPaged({
|
|
392
|
+
title: `Receipts (${String(all.length)}, newest first)`,
|
|
393
|
+
options: page.map((receipt) => ({ title: receiptLine(receipt), value: receipt })),
|
|
394
|
+
actions: [
|
|
395
|
+
...(offset > 0 ? [{ title: "Previous page", value: "prev", bind: "ctrl+b" }] : []),
|
|
396
|
+
...(hasNext ? [{ title: "Next page", value: "next", bind: "ctrl+f" }] : []),
|
|
397
|
+
],
|
|
398
|
+
});
|
|
399
|
+
if (result.action === "next") {
|
|
400
|
+
offset += RECEIPTS_PAGE_SIZE;
|
|
401
|
+
continue;
|
|
402
|
+
}
|
|
403
|
+
if (result.action === "prev") {
|
|
404
|
+
offset = Math.max(0, offset - RECEIPTS_PAGE_SIZE);
|
|
405
|
+
continue;
|
|
406
|
+
}
|
|
407
|
+
const pick = result.picked;
|
|
408
|
+
if (pick === undefined)
|
|
409
|
+
return;
|
|
410
|
+
await env.view.alert({ title: `Receipt #${String(pick.id)}`, message: receiptDetail(pick) });
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
/** Exported for tests: the receipts page size used by the receipts browser. */
|
|
414
|
+
export { RECEIPTS_PAGE_SIZE };
|
|
415
|
+
//# sourceMappingURL=controller.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"controller.js","sourceRoot":"","sources":["../../../src/ui/tui/controller.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The `/adaptive` keyboard-flow controller (ADP-033): a pure-ish state machine\n * over two seams — the typed RPC API and the host dialogs/toasts. It holds no\n * local settings state: every mutation goes through `settings.set` and the\n * returned snapshot becomes the new local view (the server is the single\n * source of truth, ADP-036).\n *\n * Flow contract: `undefined` selections always mean \"cancel this step\" — the\n * menu loop continues or exits; API failures toast and keep the loop alive.\n */\nimport type { OperatingMode } from \"../../core/scheduler/types.js\"\nimport type { TierName } from \"../../core/tier-policy/types.js\"\nimport { TIERS } from \"../../core/tier-policy/types.js\"\nimport type { ModelRef } from \"../../core/work-unit/types.js\"\nimport type { SettingsPatch, SettingsV1 } from \"../../state/settings/index.js\"\nimport type { PatchResult } from \"../../state/settings/types.js\"\nimport type { SessionModeSetResult } from \"../../state/session-modes/index.js\"\nimport type { TierRequestSetResult } from \"../../rpc/handlers.js\"\nimport type { Receipt } from \"../../state/receipts/types.js\"\nimport type { StatusSnapshot } from \"../../rpc/status.js\"\nimport { STATUS_EMPTY_MESSAGE, statusTable } from \"./status.js\"\nimport {\n mainMenuOptions,\n modeOptions,\n modeTargetOptions,\n sessionModeOptions,\n sessionTierOptions,\n type MainMenuChoice,\n} from \"./menu.js\"\nimport {\n modelPickerOptions,\n moveResource,\n moveResourceToTop,\n removeResource,\n resourceLabel,\n tierEditorOptions,\n tierMenuOptions,\n variantOptions,\n type ModelSummary,\n} from \"./tiers.js\"\nimport { errorText, type SelectOption } from \"./options.js\"\nimport { receiptDetail, receiptLine, receiptPage, RECEIPTS_PAGE_SIZE } from \"./receipts.js\"\n\nexport interface AdaptiveView {\n select<Value>(options: {\n title: string\n placeholder?: string\n options: readonly SelectOption<Value>[]\n current?: Value\n search?: (\n query: string,\n options: readonly SelectOption<Value>[],\n ) => readonly SelectOption<Value>[]\n }): Promise<Value | undefined>\n /** A select with footer actions (page flips): the action closes the\n * dialog and is reported separately from an option pick. */\n selectPaged<Value>(spec: {\n title: string\n options: readonly SelectOption<Value>[]\n actions: readonly { title: string; value: string; bind: string }[]\n }): Promise<{ picked: Value | undefined; action: string | undefined }>\n confirm(options: {\n title: string\n message: string\n label?: { confirm?: string; cancel?: string }\n }): Promise<boolean | undefined>\n alert(options: { title: string; message: string }): Promise<void>\n /** A column-aligned text table (header row first); Escape closes it. */\n table(spec: { title: string; lines: string[] }): Promise<void>\n toast(message: string, variant: \"info\" | \"success\" | \"warning\" | \"error\"): void\n}\n\nexport interface AdaptiveApi {\n settings: () => Promise<SettingsV1>\n setSettings: (patch: SettingsPatch) => Promise<PatchResult>\n status: () => Promise<StatusSnapshot>\n receipts: () => Promise<Receipt[]>\n /** The session's mode override (`null` = follows the global mode). */\n sessionMode: (sessionID: string) => Promise<OperatingMode | null>\n setSessionMode: (sessionID: string, mode: OperatingMode | null) => Promise<SessionModeSetResult>\n /** The session's pinned tier request (`null` = follows the routing ladder). */\n tierRequest: (sessionID: string) => Promise<TierName | null>\n setTierRequest: (sessionID: string, tier: string | null) => Promise<TierRequestSetResult>\n}\n\nexport type ModelLister = () => readonly ModelSummary[]\n\nexport interface AdaptiveEnv {\n api: AdaptiveApi\n view: AdaptiveView\n models: ModelLister\n /**\n * The session the command was opened from (ADP-054). Present only when the\n * palette command ran inside a session screen — the mode menu then steers\n * that session's override instead of the global mode.\n */\n sessionID?: string | undefined\n}\n\nexport const INVITE_TITLE = \"OpenCode Adaptive\"\nexport const INVITE_MESSAGE =\n \"Adaptive starts in Observe mode: routing decisions are recorded as receipts, \" +\n \"but no model is switched. Open /adaptive to configure tiers or switch to Active.\"\n\n/** Tracks whether this dialog session already showed the save toast. */\nexport interface SaveFeedback {\n once: boolean\n}\n\n/** Run one API call; on failure toast and return undefined. */\nasync function tryApi<T>(env: AdaptiveEnv, run: () => Promise<T>): Promise<T | undefined> {\n try {\n return await run()\n } catch (error) {\n env.view.toast(errorText(error), \"error\")\n return undefined\n }\n}\n\n/**\n * Apply one patch; alert on validation errors, toast on success. The host\n * never auto-dismisses toasts, so the success toast is shown only on the\n * first successful save of a dialog session — later saves stay silent\n * (the updated menu already reflects them).\n */\nasync function applySettings(\n env: AdaptiveEnv,\n patch: SettingsPatch,\n feedback: SaveFeedback,\n): Promise<SettingsV1 | undefined> {\n const result = await tryApi(env, () => env.api.setSettings(patch))\n if (result === undefined) return undefined\n if (!result.ok) {\n await env.view.alert({ title: \"Invalid settings\", message: result.errors.join(\"; \") })\n return undefined\n }\n if (!feedback.once) {\n env.view.toast(\"Settings saved\", \"success\")\n feedback.once = true\n }\n return result.settings\n}\n\n/**\n * Persist one session-mode override; alert on errors, toast on success. Same\n * once-toast semantics as `applySettings` — the success toast is shown only\n * on the first successful save of a dialog session.\n */\nasync function applySessionMode(\n env: AdaptiveEnv,\n sessionID: string,\n mode: OperatingMode | null,\n feedback: SaveFeedback,\n): Promise<SessionModeSetResult | undefined> {\n const result = await tryApi(env, () => env.api.setSessionMode(sessionID, mode))\n if (result === undefined) return undefined\n if (!result.ok) {\n await env.view.alert({ title: \"Invalid settings\", message: result.errors.join(\"; \") })\n return undefined\n }\n if (!feedback.once) {\n env.view.toast(\"Settings saved\", \"success\")\n feedback.once = true\n }\n return result\n}\n\n/**\n * Persist one session tier request; alert on errors, toast on success. Same\n * once-toast semantics as `applySettings` — the success toast is shown only\n * on the first successful save of a dialog session.\n */\nasync function applyTierRequest(\n env: AdaptiveEnv,\n sessionID: string,\n tier: string | null,\n feedback: SaveFeedback,\n): Promise<TierRequestSetResult | undefined> {\n const result = await tryApi(env, () => env.api.setTierRequest(sessionID, tier))\n if (result === undefined) return undefined\n if (!result.ok) {\n await env.view.alert({ title: \"Invalid tier request\", message: result.errors.join(\"; \") })\n return undefined\n }\n if (!feedback.once) {\n env.view.toast(\"Settings saved\", \"success\")\n feedback.once = true\n }\n return result\n}\n\nexport async function runAdaptive(env: AdaptiveEnv): Promise<void> {\n const loaded = await tryApi(env, () => env.api.settings())\n if (loaded === undefined) return\n let settings = loaded\n const feedback: SaveFeedback = { once: false }\n const sessionID = env.sessionID\n // The session's current override: fetched once when the flow starts inside\n // a session; a failure degrades to \"follow global\" (the menu still works).\n let override: OperatingMode | null =\n sessionID === undefined\n ? null\n : ((await tryApi(env, () => env.api.sessionMode(sessionID))) ?? null)\n if (!settings.inviteDismissed) {\n await env.view.confirm({ title: INVITE_TITLE, message: INVITE_MESSAGE })\n const pick = await env.view.select({\n title: INVITE_TITLE,\n options: [\n { title: \"Open the menu now\", value: true },\n { title: \"Later\", value: false },\n ],\n })\n const dismissed = await applySettings(env, { inviteDismissed: true }, feedback)\n if (dismissed !== undefined) settings = dismissed\n if (pick !== true) return\n }\n for (;;) {\n // Fresh status and settings on every pass: the menu rows pair the two,\n // and the server is the single source of truth (ADP-036). A failed read\n // degrades silently to the last known values — the menu descriptions\n // must not spam a toast on every loop iteration (the flows that need\n // status toast through tryApi themselves).\n const [freshSettings, status] = await Promise.all([\n env.api.settings().catch(() => undefined),\n env.api.status().catch(() => undefined),\n ])\n if (freshSettings !== undefined) settings = freshSettings\n // The session's pinned tier request: fetched per loop so the entry\n // description tracks the latest request; a failure degrades to the\n // generic description (the menu still works).\n const tierPinned =\n sessionID === undefined\n ? undefined\n : await env.api.tierRequest(sessionID).catch(() => undefined)\n const choice = await env.view.select({\n title: `Adaptive (${override ?? settings.mode})`,\n options: mainMenuOptions(settings, status, tierPinned, override ?? settings.mode),\n })\n if (choice === undefined) return\n if (choice === \"status\") {\n await runStatusView(env)\n continue\n }\n if (choice === \"models\") {\n settings = (await runTierEditor(env, feedback)) ?? settings\n continue\n }\n if (choice === \"mode\") {\n const next = await runModeMenu(env, settings, feedback, sessionID, override)\n if (next !== undefined) {\n settings = next.settings\n override = next.override\n }\n continue\n }\n if (choice === \"tier\") {\n if (sessionID === undefined) {\n // The tier request only exists inside a session; on home the pick is\n // explained (not silently dropped) and the menu re-opens.\n await env.view.alert({\n title: \"This session\",\n message:\n \"This session's tier request can only be changed inside a session. Open a session and run /adaptive from there.\",\n })\n continue\n }\n await runTierRequestMenu(env, sessionID, feedback)\n continue\n }\n if (choice === \"pin\") {\n settings = (await runPinMenu(env, settings, feedback)) ?? settings\n continue\n }\n await runReceiptsView(env)\n }\n}\n\n/** What one mode-menu pass changed (settings and/or the session override). */\ninterface ModeMenuOutcome {\n settings: SettingsV1\n override: OperatingMode | null\n}\n\nasync function runModeMenu(\n env: AdaptiveEnv,\n settings: SettingsV1,\n feedback: SaveFeedback,\n sessionID: string | undefined,\n override: OperatingMode | null,\n): Promise<ModeMenuOutcome | undefined> {\n // Every screen gets the same first question — which mode to steer — so a\n // user who opens the menu on home learns the two-level structure before\n // they ever run the command inside a session.\n for (;;) {\n const target = await env.view.select({\n title: \"Operating mode\",\n options: modeTargetOptions(settings.mode, override ?? settings.mode),\n })\n if (target === undefined) return undefined\n if (target === \"global\") {\n const pick = await env.view.select({\n title: \"Global mode\",\n options: modeOptions(settings.mode),\n })\n if (pick === undefined || pick === settings.mode) return undefined\n const applied = await applySettings(env, { mode: pick }, feedback)\n // The session override is deliberately untouched: changing the global\n // default must not rewrite what a session was pinned to.\n return applied === undefined ? undefined : { settings: applied, override }\n }\n if (sessionID === undefined) {\n // The session override only exists inside a session; on home the pick\n // is explained (not silently dropped) and the target question repeats.\n await env.view.alert({\n title: \"This session\",\n message:\n \"This session's mode can only be changed inside a session. Open a session and run /adaptive from there.\",\n })\n continue\n }\n const pick = await env.view.select({\n title: \"This session's mode\",\n options: sessionModeOptions({\n global: settings.mode,\n override,\n effective: override ?? settings.mode,\n }),\n })\n if (pick === undefined) return undefined\n if (pick.kind === \"global\") {\n if (override === null) return undefined\n const saved = await applySessionMode(env, sessionID, null, feedback)\n return saved === undefined ? undefined : { settings, override: null }\n }\n if (pick.mode === (override ?? settings.mode)) return undefined\n const saved = await applySessionMode(env, sessionID, pick.mode, feedback)\n return saved === undefined ? undefined : { settings, override: pick.mode }\n }\n}\n\n/** The per-session tier-request menu (ADP-055): pin a tier or clear it. */\nasync function runTierRequestMenu(\n env: AdaptiveEnv,\n sessionID: string,\n feedback: SaveFeedback,\n): Promise<void> {\n const pinned = await tryApi(env, () => env.api.tierRequest(sessionID))\n if (pinned === undefined) return\n const pick = await env.view.select({\n title: \"This session's tier\",\n options: sessionTierOptions({ pinned }),\n })\n if (pick === undefined) return\n const next = pick.kind === \"ladder\" ? null : pick.tier\n if (next === pinned) return\n await applyTierRequest(env, sessionID, next, feedback)\n}\n\n/**\n * The status view: every configured model with its state as a column-aligned\n * table (header row, tier order, priority order within tiers). No\n * per-resource selection step — the table IS the view; Escape returns to the\n * menu.\n */\nasync function runStatusView(env: AdaptiveEnv): Promise<void> {\n const snapshot = await tryApi(env, () => env.api.status())\n if (snapshot === undefined) return\n const configured = TIERS.some((tier) => snapshot.tiers[tier].length > 0)\n if (!configured) {\n await env.view.alert({ title: \"Status\", message: STATUS_EMPTY_MESSAGE })\n return\n }\n await env.view.table({ title: \"Status\", lines: statusTable(snapshot) })\n}\n\ntype TierEditorOutcome = SettingsV1 | undefined\n\nasync function runTierEditor(env: AdaptiveEnv, feedback: SaveFeedback): Promise<TierEditorOutcome> {\n // The last confirmed server snapshot from this editor session, returned\n // when the editor closes so the menu view stays in sync with the server.\n let saved: SettingsV1 | undefined\n for (;;) {\n // The server is the single source of truth (ADP-036): settings are\n // re-fetched on every editor pass — entering the editor and after each\n // save — so the rows pair fresh settings with fresh status and an edit\n // can never clobber a change made outside this dialog.\n const settings = await tryApi(env, () => env.api.settings())\n if (settings === undefined) return saved\n const status = await tryApi(env, () => env.api.status())\n if (status === undefined) return saved\n const tier = await env.view.select({ title: \"Tier\", options: tierMenuOptions(status) })\n if (tier === undefined) return saved\n const choice = await env.view.select({\n title: `Tier: ${tier}`,\n options: tierEditorOptions(settings, tier, status),\n })\n if (choice === undefined) continue\n if (choice.kind === \"add\") {\n const next = await runAddModel(env, settings, tier, feedback)\n if (next !== undefined) saved = next\n continue\n }\n const next = await runResourceMenu(env, settings, tier, choice.index, feedback)\n if (next !== undefined) saved = next\n }\n}\n\nasync function runAddModel(\n env: AdaptiveEnv,\n settings: SettingsV1,\n tier: TierName,\n feedback: SaveFeedback,\n): Promise<SettingsV1 | undefined> {\n const models = env.models()\n const pick = await env.view.select({\n title: \"Add model\",\n placeholder: \"Search provider / model / name\",\n options: modelPickerOptions(models, \"\"),\n search: (query) => modelPickerOptions(models, query),\n })\n if (pick === undefined) return undefined\n const info = models.find((m) => m.providerID === pick.providerID && m.modelID === pick.modelID)\n const variants = info?.variants ?? []\n let variant: string | undefined\n if (variants.length > 0) {\n const choice = await env.view.select({\n title: `Variant: ${pick.providerID}/${pick.modelID}`,\n options: variantOptions(variants),\n })\n if (choice === undefined) return undefined\n variant = choice.variant\n }\n const ref: ModelRef =\n variant === undefined\n ? { providerID: pick.providerID, id: pick.modelID }\n : { providerID: pick.providerID, id: pick.modelID, variant }\n const list = [...settings.tiers[tier], ref]\n return applySettings(env, { tiers: { ...settings.tiers, [tier]: list } }, feedback)\n}\n\ntype ResourceMenuChoice = \"top\" | \"up\" | \"down\" | \"remove\"\n\nfunction resourceMenuOptions(): SelectOption<ResourceMenuChoice>[] {\n return [\n { title: \"Move to top\", value: \"top\", description: \"Make this the primary resource\" },\n { title: \"Move up\", value: \"up\", description: \"Higher priority within the tier\" },\n { title: \"Move down\", value: \"down\", description: \"Lower priority within the tier\" },\n { title: \"Remove\", value: \"remove\" },\n ]\n}\n\nasync function runResourceMenu(\n env: AdaptiveEnv,\n settings: SettingsV1,\n tier: TierName,\n index: number,\n feedback: SaveFeedback,\n): Promise<SettingsV1 | undefined> {\n const ref = settings.tiers[tier][index]\n if (ref === undefined) return undefined\n const pick = await env.view.select({\n title: `Resource: ${resourceLabel(ref)}`,\n options: resourceMenuOptions(),\n })\n if (pick === undefined) return undefined\n if (pick === \"remove\") {\n const confirmed = await env.view.confirm({\n title: \"Remove resource\",\n message: `Remove ${resourceLabel(ref)} from ${tier}?`,\n })\n if (confirmed !== true) return undefined\n return applySettings(\n env,\n { tiers: { ...settings.tiers, [tier]: removeResource(settings.tiers[tier], index) } },\n feedback,\n )\n }\n const list =\n pick === \"top\"\n ? moveResourceToTop(settings.tiers[tier], index)\n : moveResource(settings.tiers[tier], index, pick === \"up\" ? -1 : 1)\n return applySettings(env, { tiers: { ...settings.tiers, [tier]: list } }, feedback)\n}\n\ntype PinChoice = { kind: \"clear\" } | { kind: \"pin\"; ref: ModelRef }\n\nfunction pinOptions(settings: SettingsV1): SelectOption<PinChoice>[] {\n const options: SelectOption<PinChoice>[] = [\n { title: \"No pin\", value: { kind: \"clear\" }, description: \"clear the pin\" },\n ]\n for (const tier of TIERS) {\n for (const ref of settings.tiers[tier]) {\n options.push({\n title: `${resourceLabel(ref)} (${tier})`,\n description: \"route every turn to this resource, even in active mode\",\n value: { kind: \"pin\", ref },\n })\n }\n }\n return options\n}\n\nasync function runPinMenu(\n env: AdaptiveEnv,\n settings: SettingsV1,\n feedback: SaveFeedback,\n): Promise<SettingsV1 | undefined> {\n const configured = TIERS.some((tier) => settings.tiers[tier].length > 0)\n if (!configured) {\n await env.view.alert({ title: \"Pin\", message: \"(no configured resources to pin)\" })\n return undefined\n }\n const pick = await env.view.select({ title: \"Pin\", options: pinOptions(settings) })\n if (pick === undefined) return undefined\n return applySettings(env, { pin: pick.kind === \"clear\" ? null : pick.ref }, feedback)\n}\n\nasync function runReceiptsView(env: AdaptiveEnv): Promise<void> {\n const all = await tryApi(env, () => env.api.receipts())\n if (all === undefined) return\n if (all.length === 0) {\n await env.view.alert({\n title: \"Receipts\",\n message: \"No decisions recorded yet.\",\n })\n return\n }\n // Paginated browse: every page is a bounded view, and the page flips are\n // footer actions (not list items) — they close the dialog and the loop\n // reopens it at the new offset. Viewing a detail returns to the same\n // page; Escape (no pick, no action) leaves the browser.\n for (let offset = 0; ;) {\n const page = receiptPage(all.slice(offset))\n const hasNext = offset + RECEIPTS_PAGE_SIZE < all.length\n const result = await env.view.selectPaged({\n title: `Receipts (${String(all.length)}, newest first)`,\n options: page.map((receipt) => ({ title: receiptLine(receipt), value: receipt })),\n actions: [\n ...(offset > 0 ? [{ title: \"Previous page\", value: \"prev\", bind: \"ctrl+b\" }] : []),\n ...(hasNext ? [{ title: \"Next page\", value: \"next\", bind: \"ctrl+f\" }] : []),\n ],\n })\n if (result.action === \"next\") {\n offset += RECEIPTS_PAGE_SIZE\n continue\n }\n if (result.action === \"prev\") {\n offset = Math.max(0, offset - RECEIPTS_PAGE_SIZE)\n continue\n }\n const pick = result.picked\n if (pick === undefined) return\n await env.view.alert({ title: `Receipt #${String(pick.id)}`, message: receiptDetail(pick) })\n }\n}\n\n/** Exported for tests: the receipts page size used by the receipts browser. */\nexport { RECEIPTS_PAGE_SIZE }\n\nexport type {\n MainMenuChoice,\n OperatingMode,\n StatusSnapshot,\n SettingsV1,\n SettingsPatch,\n PatchResult,\n Receipt,\n}\n"]}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { OperatingMode } from "../../core/scheduler/types.js";
|
|
2
|
+
import type { TierName } from "../../core/tier-policy/types.js";
|
|
3
|
+
import type { StatusSnapshot } from "../../rpc/status.js";
|
|
4
|
+
import type { SettingsV1 } from "../../state/settings/index.js";
|
|
5
|
+
import type { SelectOption } from "./options.js";
|
|
6
|
+
export declare const MAIN_MENU_ITEMS: readonly ["status", "models", "mode", "tier", "pin", "receipts"];
|
|
7
|
+
export type MainMenuChoice = (typeof MAIN_MENU_ITEMS)[number];
|
|
8
|
+
/** "3 resources · 1 windowed" — the status line under the menu entry. */
|
|
9
|
+
export declare function statusSummary(status: StatusSnapshot | undefined): string;
|
|
10
|
+
/** "quick: 1 · standard: 0 · advanced: 2" — what is configured where. */
|
|
11
|
+
export declare function modelsSummary(settings: SettingsV1): string;
|
|
12
|
+
export declare function mainMenuOptions(settings: SettingsV1, status: StatusSnapshot | undefined, tierRequest?: TierName | null, effectiveMode?: OperatingMode): SelectOption<MainMenuChoice>[];
|
|
13
|
+
export declare function modeOptions(current: OperatingMode): SelectOption<OperatingMode>[];
|
|
14
|
+
/** One per-session mode-menu pick: follow the global mode, or set an override. */
|
|
15
|
+
export type SessionModeChoice = {
|
|
16
|
+
kind: "global";
|
|
17
|
+
} | {
|
|
18
|
+
kind: "mode";
|
|
19
|
+
mode: OperatingMode;
|
|
20
|
+
};
|
|
21
|
+
/** Which mode the user steers inside a session: the global default or the session override. */
|
|
22
|
+
export type ModeTarget = "global" | "session";
|
|
23
|
+
/**
|
|
24
|
+
* The first select of the in-session mode flow (ADP-054): ask which mode to
|
|
25
|
+
* steer before showing any mode list — the global default or this session's
|
|
26
|
+
* override. Each row names the current value so the user can tell at a
|
|
27
|
+
* glance which side needs changing.
|
|
28
|
+
*/
|
|
29
|
+
export declare function modeTargetOptions(global: OperatingMode, effective: OperatingMode): SelectOption<ModeTarget>[];
|
|
30
|
+
/**
|
|
31
|
+
* The per-session mode menu (ADP-054): first "Follow global" (clearing the
|
|
32
|
+
* override), then every mode. An existing override is marked "(override)" —
|
|
33
|
+
* distinct from the effective mode's "(current)" — so the user can tell a
|
|
34
|
+
* set override apart from a session merely inheriting the global mode.
|
|
35
|
+
*/
|
|
36
|
+
export declare function sessionModeOptions(input: {
|
|
37
|
+
global: OperatingMode;
|
|
38
|
+
override: OperatingMode | null;
|
|
39
|
+
effective: OperatingMode;
|
|
40
|
+
}): SelectOption<SessionModeChoice>[];
|
|
41
|
+
/** One per-session tier-menu pick: follow the routing ladder, or pin a tier. */
|
|
42
|
+
export type SessionTierChoice = {
|
|
43
|
+
kind: "ladder";
|
|
44
|
+
} | {
|
|
45
|
+
kind: "tier";
|
|
46
|
+
tier: TierName;
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* The per-session tier menu (ADP-055): first "Follow routing ladder" (clearing
|
|
50
|
+
* the request), then every tier. A pinned request is marked "(pinned)" so the
|
|
51
|
+
* user can tell the sticky request apart from the ordinary ladder.
|
|
52
|
+
*/
|
|
53
|
+
export declare function sessionTierOptions(input: {
|
|
54
|
+
pinned: TierName | null;
|
|
55
|
+
}): SelectOption<SessionTierChoice>[];
|