@gajae-code/coding-agent 0.12.21 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +67 -2
- package/dist/types/cli/args.d.ts +5 -2
- package/dist/types/cli/root-flags.d.ts +246 -0
- package/dist/types/cli.d.ts +11 -19
- package/dist/types/commands/daemon.d.ts +0 -30
- package/dist/types/commands/launch.d.ts +11 -19
- package/dist/types/commands/quick-lane.d.ts +32 -0
- package/dist/types/commands/sdk.d.ts +21 -15
- package/dist/types/config/file-lock-gc.d.ts +2 -0
- package/dist/types/config/model-equivalence.d.ts +20 -0
- package/dist/types/config/model-profile-activation.d.ts +62 -17
- package/dist/types/config/model-profiles.d.ts +10 -0
- package/dist/types/config/model-registry.d.ts +68 -1
- package/dist/types/config/model-resolver.d.ts +15 -4
- package/dist/types/config/models-config-schema.d.ts +7 -2
- package/dist/types/config/provider-selection-policy.d.ts +62 -0
- package/dist/types/config/settings-schema.d.ts +36 -78
- package/dist/types/config/settings.d.ts +2 -0
- package/dist/types/edit/renderer.d.ts +5 -2
- package/dist/types/edit/streaming.d.ts +13 -0
- package/dist/types/extensibility/custom-tools/types.d.ts +2 -0
- package/dist/types/extensibility/extensions/compact-handler.d.ts +2 -1
- package/dist/types/extensibility/extensions/types.d.ts +3 -0
- package/dist/types/gjc-runtime/gc-runtime.d.ts +19 -5
- package/dist/types/internal-urls/local-protocol.d.ts +1 -0
- package/dist/types/modes/components/model-selector.d.ts +0 -2
- package/dist/types/modes/components/provider-order-context.d.ts +45 -0
- package/dist/types/modes/components/provider-order-editor.d.ts +24 -0
- package/dist/types/modes/components/settings-defs.d.ts +5 -1
- package/dist/types/modes/components/settings-selector.d.ts +2 -0
- package/dist/types/quick-lane/classify.d.ts +40 -0
- package/dist/types/runtime/stt-settings-setup.d.ts +18 -0
- package/dist/types/sdk/broker/broker.d.ts +56 -2
- package/dist/types/sdk/broker/operation-receipt-ledger.d.ts +25 -0
- package/dist/types/sdk/broker/session-index.d.ts +83 -2
- package/dist/types/sdk/broker/state-version.d.ts +1 -1
- package/dist/types/sdk/bus/notification-service.d.ts +6 -0
- package/dist/types/sdk/bus/prompt-reconciliation.d.ts +8 -1
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +4 -1
- package/dist/types/sdk/bus/telegram-daemon.d.ts +26 -0
- package/dist/types/sdk/cli/rows.d.ts +98 -0
- package/dist/types/sdk/cli/session-cli.d.ts +35 -3
- package/dist/types/sdk/elevation/allowlist.d.ts +20 -0
- package/dist/types/sdk/elevation/audit.d.ts +93 -0
- package/dist/types/sdk/elevation/capability.d.ts +3 -0
- package/dist/types/sdk/elevation/digest.d.ts +72 -0
- package/dist/types/sdk/elevation/dispatch-receipt.d.ts +85 -0
- package/dist/types/sdk/elevation/grant-ledger.d.ts +163 -0
- package/dist/types/sdk/elevation/owner.d.ts +48 -0
- package/dist/types/sdk/guides/cache.d.ts +98 -0
- package/dist/types/sdk/guides/catalog.d.ts +156 -0
- package/dist/types/sdk/guides/cli.d.ts +39 -0
- package/dist/types/sdk/guides/manifest.d.ts +66 -0
- package/dist/types/sdk/guides/verify.d.ts +66 -0
- package/dist/types/sdk/host/control/dispatch.d.ts +8 -0
- package/dist/types/sdk/host/control/index.d.ts +1 -1
- package/dist/types/sdk/host/control/operations.d.ts +3 -2
- package/dist/types/sdk/host/query/cursor.d.ts +10 -0
- package/dist/types/sdk/host/query/handlers.d.ts +21 -0
- package/dist/types/sdk/host/session-runtime.d.ts +9 -0
- package/dist/types/sdk/mcp/server.d.ts +2 -2
- package/dist/types/sdk/prompt-status.d.ts +11 -0
- package/dist/types/sdk/session.d.ts +4 -0
- package/dist/types/sdk/transport/index.d.ts +1 -0
- package/dist/types/sdk/transport/relay.d.ts +1 -0
- package/dist/types/sdk/transport/serve-cli.d.ts +20 -1
- package/dist/types/session/agent-session.d.ts +32 -18
- package/dist/types/session/blob-store.d.ts +13 -7
- package/dist/types/session/fallback-chain-controller.d.ts +15 -0
- package/dist/types/session/internal/managed-session-storage.d.ts +25 -1
- package/dist/types/session/internal/session-memory-sidecar.d.ts +5 -5
- package/dist/types/session/session-manager.d.ts +139 -10
- package/dist/types/session/session-storage.d.ts +48 -0
- package/dist/types/setup/provider-onboarding.d.ts +7 -1
- package/dist/types/slash-commands/builtin-registry.d.ts +14 -0
- package/dist/types/stt/setup.d.ts +2 -0
- package/dist/types/task/executor.d.ts +10 -2
- package/dist/types/tools/browser/runtime-diagnostics.d.ts +65 -0
- package/dist/types/tools/browser/tab-protocol.d.ts +4 -0
- package/dist/types/tools/browser/tab-supervisor.d.ts +2 -0
- package/dist/types/tools/browser/tab-worker.d.ts +2 -0
- package/dist/types/tools/browser.d.ts +2 -0
- package/dist/types/tools/image-gen.d.ts +14 -18
- package/dist/types/tools/index.d.ts +2 -0
- package/dist/types/tools/resource-gc.d.ts +2 -0
- package/dist/types/utils/pasted-image-path.d.ts +6 -0
- package/package.json +9 -9
- package/scripts/generate-sdk-operation-inventory.ts +8 -0
- package/scripts/run-test-manifest.ts +61 -21
- package/scripts/verify-gjc-sdk-canonicalization.ts +9 -4
- package/src/cli/args.ts +62 -9
- package/src/cli/completion-cli.ts +1 -0
- package/src/cli/fast-help.ts +1 -0
- package/src/cli/root-flags.ts +100 -0
- package/src/cli/setup-cli.ts +6 -2
- package/src/cli.ts +42 -65
- package/src/commands/acp.ts +1 -1
- package/src/commands/daemon.ts +2 -31
- package/src/commands/gc.ts +1 -1
- package/src/commands/launch.ts +8 -129
- package/src/commands/quick-lane.ts +51 -0
- package/src/commands/sdk.ts +200 -26
- package/src/commands/setup.ts +4 -2
- package/src/config/file-lock-gc.ts +4 -1
- package/src/config/model-equivalence.ts +76 -7
- package/src/config/model-profile-activation.ts +1115 -265
- package/src/config/model-profiles.ts +134 -2
- package/src/config/model-registry.ts +352 -50
- package/src/config/model-resolver.ts +289 -38
- package/src/config/models-config-schema.ts +11 -4
- package/src/config/provider-selection-policy.ts +146 -0
- package/src/config/settings-schema.ts +50 -77
- package/src/config/settings.ts +40 -0
- package/src/coordinator-mcp/server.ts +32 -2
- package/src/edit/renderer.ts +175 -223
- package/src/edit/streaming.ts +108 -1
- package/src/export/html/index.ts +57 -7
- package/src/extensibility/custom-tools/types.ts +2 -0
- package/src/extensibility/extensions/compact-handler.ts +3 -2
- package/src/extensibility/extensions/runner.ts +6 -0
- package/src/extensibility/extensions/types.ts +3 -0
- package/src/gjc-runtime/gc-runtime.ts +379 -20
- package/src/gjc-runtime/launch-worktree.ts +5 -1
- package/src/gjc-runtime/team-runtime.ts +59 -2
- package/src/gjc-runtime/tmux-owner-isolation.ts +19 -0
- package/src/gjc-runtime/ultragoal-receipt-freshness.ts +16 -1
- package/src/internal-urls/docs-index.generated.ts +17 -13
- package/src/internal-urls/local-protocol.ts +1 -0
- package/src/main.ts +40 -3
- package/src/memories/index.ts +4 -2
- package/src/modes/acp/acp-agent.ts +235 -91
- package/src/modes/components/model-selector.ts +206 -42
- package/src/modes/components/provider-order-context.ts +197 -0
- package/src/modes/components/provider-order-editor.ts +441 -0
- package/src/modes/components/settings-defs.ts +21 -1
- package/src/modes/components/settings-selector.ts +45 -2
- package/src/modes/components/tool-execution.ts +47 -15
- package/src/modes/controllers/command-controller.ts +6 -3
- package/src/modes/controllers/extension-ui-controller.ts +2 -0
- package/src/modes/controllers/input-controller.ts +73 -2
- package/src/modes/controllers/selector-controller.ts +260 -153
- package/src/modes/prompt-suggestion-controller.ts +1 -1
- package/src/modes/runtime-init.ts +1 -0
- package/src/modes/utils/ui-helpers.ts +18 -0
- package/src/prompts/tools/browser.md +1 -0
- package/src/quick-lane/classify.ts +257 -0
- package/src/rlm/index.ts +1 -1
- package/src/runtime/stt-settings-setup.ts +57 -0
- package/src/sdk/acp/adapter.ts +2 -1
- package/src/sdk/broker/broker.ts +780 -12
- package/src/sdk/broker/lifecycle.ts +70 -29
- package/src/sdk/broker/operation-receipt-ledger.ts +144 -0
- package/src/sdk/broker/session-index.ts +463 -48
- package/src/sdk/broker/state-version.ts +13 -9
- package/src/sdk/broker/transport.ts +14 -1
- package/src/sdk/bus/index.ts +34 -13
- package/src/sdk/bus/kind-aware-reconciliation.ts +21 -2
- package/src/sdk/bus/notification-service.ts +47 -1
- package/src/sdk/bus/prompt-reconciliation.ts +22 -3
- package/src/sdk/bus/telegram-daemon-contract.ts +4 -1
- package/src/sdk/bus/telegram-daemon.ts +54 -3
- package/src/sdk/cli/rows.ts +247 -0
- package/src/sdk/cli/session-cli.ts +930 -79
- package/src/sdk/elevation/allowlist.ts +54 -0
- package/src/sdk/elevation/audit.ts +208 -0
- package/src/sdk/elevation/capability.ts +41 -0
- package/src/sdk/elevation/digest.ts +129 -0
- package/src/sdk/elevation/dispatch-receipt.ts +184 -0
- package/src/sdk/elevation/grant-ledger.ts +1142 -0
- package/src/sdk/elevation/owner.ts +109 -0
- package/src/sdk/guides/cache.ts +363 -0
- package/src/sdk/guides/catalog.ts +554 -0
- package/src/sdk/guides/cli.ts +235 -0
- package/src/sdk/guides/manifest.ts +152 -0
- package/src/sdk/guides/verify.ts +178 -0
- package/src/sdk/host/control/dispatch.ts +49 -1
- package/src/sdk/host/control/index.ts +1 -0
- package/src/sdk/host/control/operations.ts +8 -1
- package/src/sdk/host/query/cursor.ts +47 -3
- package/src/sdk/host/query/handlers.ts +155 -2
- package/src/sdk/host/sdk-surface-parity.test.ts +1 -0
- package/src/sdk/host/session-runtime.test.ts +391 -1
- package/src/sdk/host/session-runtime.ts +308 -73
- package/src/sdk/host/surface-policy.ts +3 -0
- package/src/sdk/host/websocket-transport.ts +7 -1
- package/src/sdk/mcp/server.ts +88 -15
- package/src/sdk/prompt-status.ts +8 -0
- package/src/sdk/protocol/operation-inventory.generated.json +62 -0
- package/src/sdk/protocol/operation-registry.ts +3 -2
- package/src/sdk/session.ts +87 -39
- package/src/sdk/transport/index.ts +1 -0
- package/src/sdk/transport/relay.ts +3 -0
- package/src/sdk/transport/serve-cli.ts +111 -24
- package/src/secrets/obfuscator.ts +92 -12
- package/src/session/agent-session.ts +322 -93
- package/src/session/blob-store.ts +60 -8
- package/src/session/fallback-chain-controller.ts +35 -0
- package/src/session/internal/managed-session-scope.ts +25 -3
- package/src/session/internal/managed-session-storage.ts +574 -73
- package/src/session/internal/session-memory-sidecar.ts +19 -29
- package/src/session/session-manager.ts +2166 -743
- package/src/session/session-storage.ts +715 -48
- package/src/setup/provider-onboarding.ts +19 -3
- package/src/setup/provider-presets.json +3 -1
- package/src/slash-commands/builtin-registry.ts +85 -3
- package/src/stt/downloader.ts +2 -3
- package/src/stt/setup.ts +29 -4
- package/src/task/executor.ts +22 -18
- package/src/task/index.ts +24 -1
- package/src/tools/archive-reader.ts +11 -2
- package/src/tools/browser/runtime-diagnostics.ts +249 -0
- package/src/tools/browser/tab-protocol.ts +4 -0
- package/src/tools/browser/tab-supervisor.ts +5 -0
- package/src/tools/browser/tab-worker.ts +36 -1
- package/src/tools/browser.ts +7 -0
- package/src/tools/image-gen.ts +110 -166
- package/src/tools/index.ts +2 -0
- package/src/tools/read.ts +1 -1
- package/src/tools/resource-gc.ts +6 -1
- package/src/tools/tool-catalog.generated.ts +5 -1
- package/src/utils/pasted-image-path.ts +93 -0
- package/src/web/search/index.ts +3 -3
|
@@ -0,0 +1,441 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ordered provider priority editor for `modelProviderOrder`.
|
|
3
|
+
*
|
|
4
|
+
* Full-screen custom settings editor (Container + row list, mirroring the
|
|
5
|
+
* StatusLine custom editor layout) with add/remove/move-up/move-down/reset
|
|
6
|
+
* actions that persist to settings immediately. Rows are rebuilt from the live
|
|
7
|
+
* `ProviderOrderContext` snapshot before every render pass, keeping the
|
|
8
|
+
* previously selected provider highlighted when the underlying order or auth
|
|
9
|
+
* state changes. Unavailable persisted providers are retained and dimmed.
|
|
10
|
+
*
|
|
11
|
+
* The editor owns the `ProviderOrderContext` lifecycle: both normal close
|
|
12
|
+
* (`#close`) and external teardown (`dispose()`) release the context
|
|
13
|
+
* subscriptions and dispose the container, idempotently.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { Container, getKeybindings, matchesKey, Spacer, Text } from "@gajae-code/tui";
|
|
17
|
+
import { sanitizeText } from "@gajae-code/utils";
|
|
18
|
+
import { theme } from "../../modes/theme/theme";
|
|
19
|
+
import {
|
|
20
|
+
formatProviderOrderLabel,
|
|
21
|
+
type ProviderOrderContext,
|
|
22
|
+
type ProviderOrderEntry,
|
|
23
|
+
type ProviderOrderSnapshot,
|
|
24
|
+
} from "./provider-order-context";
|
|
25
|
+
|
|
26
|
+
type EditorPage = "main" | "add";
|
|
27
|
+
|
|
28
|
+
type EditorRowKind = "action" | "provider" | "providerAction" | "candidate" | "back" | "empty";
|
|
29
|
+
|
|
30
|
+
interface EditorRow {
|
|
31
|
+
/** Stable row identity used to restore the selection after a rebuild. */
|
|
32
|
+
id: string;
|
|
33
|
+
kind: EditorRowKind;
|
|
34
|
+
/** Sanitized display text (no ANSI / control chars). */
|
|
35
|
+
label: string;
|
|
36
|
+
/** Right-side status text (sanitized). */
|
|
37
|
+
status?: string;
|
|
38
|
+
/** Normalized provider id when the row belongs to a provider. */
|
|
39
|
+
providerId?: string;
|
|
40
|
+
/** Render the label dimmed (e.g. unavailable providers). */
|
|
41
|
+
dimmer?: boolean;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function entryStatus(entry: ProviderOrderEntry | undefined): string {
|
|
45
|
+
if (!entry?.available) return "unavailable";
|
|
46
|
+
return entry.authenticated ? "logged in" : "not logged in";
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function entryDetail(entry: ProviderOrderEntry | undefined, adding = false): string {
|
|
50
|
+
const base = entry?.available
|
|
51
|
+
? entry.authenticated
|
|
52
|
+
? "Credentials are available for this provider."
|
|
53
|
+
: "No credentials yet; it still wins priority once configured."
|
|
54
|
+
: "Unavailable right now (not in the model catalog); retained and skipped at runtime.";
|
|
55
|
+
return adding ? `${base} Adds this provider to the end of the order.` : base;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export class ProviderOrderEditorComponent extends Container {
|
|
59
|
+
#context: ProviderOrderContext;
|
|
60
|
+
#onClose: () => void;
|
|
61
|
+
#onError: ((message: string) => void) | undefined;
|
|
62
|
+
#page: EditorPage = "main";
|
|
63
|
+
#rows: EditorRow[] = [];
|
|
64
|
+
#selectedIndex = 0;
|
|
65
|
+
#selectedRowId: string | undefined;
|
|
66
|
+
#selectedProviderId: string | undefined;
|
|
67
|
+
#rowsContainer: Container;
|
|
68
|
+
#detailText: Text;
|
|
69
|
+
#hintText: Text;
|
|
70
|
+
#closed = false;
|
|
71
|
+
#persisting = false;
|
|
72
|
+
|
|
73
|
+
constructor(context: ProviderOrderContext, onClose: () => void, onError?: (message: string) => void) {
|
|
74
|
+
super();
|
|
75
|
+
this.#context = context;
|
|
76
|
+
this.#onClose = onClose;
|
|
77
|
+
this.#onError = onError;
|
|
78
|
+
|
|
79
|
+
this.addChild(new Text(theme.bold(theme.fg("accent", "Provider Priority Order")), 0, 0));
|
|
80
|
+
this.addChild(new Spacer(1));
|
|
81
|
+
this.addChild(
|
|
82
|
+
new Text(
|
|
83
|
+
theme.fg(
|
|
84
|
+
"muted",
|
|
85
|
+
"Providers listed earlier win ties during automatic model resolution. Unavailable saved entries are kept but skipped at runtime.",
|
|
86
|
+
),
|
|
87
|
+
0,
|
|
88
|
+
0,
|
|
89
|
+
),
|
|
90
|
+
);
|
|
91
|
+
this.addChild(new Spacer(1));
|
|
92
|
+
this.#rowsContainer = new Container();
|
|
93
|
+
this.addChild(this.#rowsContainer);
|
|
94
|
+
this.#detailText = new Text("", 0, 0);
|
|
95
|
+
this.addChild(new Spacer(1));
|
|
96
|
+
this.addChild(this.#detailText);
|
|
97
|
+
this.addChild(new Spacer(1));
|
|
98
|
+
this.#hintText = new Text("", 0, 0);
|
|
99
|
+
this.addChild(this.#hintText);
|
|
100
|
+
|
|
101
|
+
try {
|
|
102
|
+
this.#rebuild();
|
|
103
|
+
} catch (error) {
|
|
104
|
+
// The context subscribed to settings/auth in its constructor; if the
|
|
105
|
+
// initial snapshot/rebuild fails, release those subscriptions before
|
|
106
|
+
// rethrowing so a construction failure cannot leak listeners.
|
|
107
|
+
this.dispose();
|
|
108
|
+
throw error;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** Rebuild rows from the current snapshot. Called by the controller on context changes. */
|
|
113
|
+
refresh(): void {
|
|
114
|
+
if (this.#closed) return;
|
|
115
|
+
this.#rebuild();
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
#rebuild(): void {
|
|
119
|
+
const snapshot = this.#context.snapshot();
|
|
120
|
+
const preserveRowId = this.#selectedRowId;
|
|
121
|
+
const preserveProviderId = this.#selectedProviderId;
|
|
122
|
+
|
|
123
|
+
this.#rows = this.#page === "add" ? this.#buildAddRows(snapshot) : this.#buildMainRows(snapshot);
|
|
124
|
+
|
|
125
|
+
let nextIndex = 0;
|
|
126
|
+
if (preserveRowId) {
|
|
127
|
+
const exact = this.#rows.findIndex(row => row.id === preserveRowId);
|
|
128
|
+
if (exact >= 0) {
|
|
129
|
+
nextIndex = exact;
|
|
130
|
+
} else {
|
|
131
|
+
const byProvider = preserveProviderId
|
|
132
|
+
? this.#rows.findIndex(row => row.providerId === preserveProviderId)
|
|
133
|
+
: -1;
|
|
134
|
+
nextIndex =
|
|
135
|
+
byProvider >= 0 ? byProvider : Math.min(this.#selectedIndex, Math.max(0, this.#rows.length - 1));
|
|
136
|
+
}
|
|
137
|
+
} else {
|
|
138
|
+
nextIndex = Math.min(this.#selectedIndex, Math.max(0, this.#rows.length - 1));
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
this.#selectedIndex = nextIndex;
|
|
142
|
+
this.#captureSelection();
|
|
143
|
+
this.#renderRows();
|
|
144
|
+
this.#renderFooter();
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
#captureSelection(): void {
|
|
148
|
+
const row = this.#rows[this.#selectedIndex];
|
|
149
|
+
this.#selectedRowId = row?.id;
|
|
150
|
+
this.#selectedProviderId = row?.providerId;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
#buildMainRows(snapshot: ProviderOrderSnapshot): EditorRow[] {
|
|
154
|
+
const rows: EditorRow[] = [
|
|
155
|
+
{ id: "action.add", kind: "action", label: "Add provider", status: "+" },
|
|
156
|
+
{ id: "action.reset", kind: "action", label: "Reset to default order", status: "reset" },
|
|
157
|
+
];
|
|
158
|
+
|
|
159
|
+
if (snapshot.order.length === 0) {
|
|
160
|
+
rows.push({
|
|
161
|
+
id: "empty",
|
|
162
|
+
kind: "empty",
|
|
163
|
+
label: "No providers in priority order yet. Use Add provider to build the order.",
|
|
164
|
+
});
|
|
165
|
+
} else {
|
|
166
|
+
for (let index = 0; index < snapshot.order.length; index++) {
|
|
167
|
+
const providerId = snapshot.order[index]!;
|
|
168
|
+
const entry = snapshot.entries.find(candidate => candidate.id === providerId);
|
|
169
|
+
const label = sanitizeText(entry?.label ?? formatProviderOrderLabel(providerId));
|
|
170
|
+
rows.push({
|
|
171
|
+
id: `provider.${providerId}`,
|
|
172
|
+
kind: "provider",
|
|
173
|
+
label: `#${index + 1} ${label}`,
|
|
174
|
+
status: entryStatus(entry),
|
|
175
|
+
providerId,
|
|
176
|
+
dimmer: entry ? !entry.available : true,
|
|
177
|
+
});
|
|
178
|
+
rows.push({
|
|
179
|
+
id: `moveup.${providerId}`,
|
|
180
|
+
kind: "providerAction",
|
|
181
|
+
label: `Move up: ${label}`,
|
|
182
|
+
status: "up",
|
|
183
|
+
providerId,
|
|
184
|
+
});
|
|
185
|
+
rows.push({
|
|
186
|
+
id: `movedown.${providerId}`,
|
|
187
|
+
kind: "providerAction",
|
|
188
|
+
label: `Move down: ${label}`,
|
|
189
|
+
status: "down",
|
|
190
|
+
providerId,
|
|
191
|
+
});
|
|
192
|
+
rows.push({
|
|
193
|
+
id: `remove.${providerId}`,
|
|
194
|
+
kind: "providerAction",
|
|
195
|
+
label: `Remove: ${label}`,
|
|
196
|
+
status: "remove",
|
|
197
|
+
providerId,
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
rows.push({ id: "action.done", kind: "action", label: "Done", status: "done" });
|
|
203
|
+
return rows;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
#buildAddRows(snapshot: ProviderOrderSnapshot): EditorRow[] {
|
|
207
|
+
const rows: EditorRow[] = [{ id: "back", kind: "back", label: "Back to order" }];
|
|
208
|
+
const candidates = snapshot.entries.filter(entry => !entry.inOrder);
|
|
209
|
+
if (candidates.length === 0) {
|
|
210
|
+
rows.push({
|
|
211
|
+
id: "empty",
|
|
212
|
+
kind: "empty",
|
|
213
|
+
label: "Every known provider is already in the priority order.",
|
|
214
|
+
});
|
|
215
|
+
} else {
|
|
216
|
+
for (const entry of candidates) {
|
|
217
|
+
rows.push({
|
|
218
|
+
id: `candidate.${entry.id}`,
|
|
219
|
+
kind: "candidate",
|
|
220
|
+
label: sanitizeText(entry.label),
|
|
221
|
+
status: entryStatus(entry),
|
|
222
|
+
providerId: entry.id,
|
|
223
|
+
dimmer: !entry.available,
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
return rows;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
#renderRows(): void {
|
|
231
|
+
this.#rowsContainer.clear();
|
|
232
|
+
for (let index = 0; index < this.#rows.length; index++) {
|
|
233
|
+
const row = this.#rows[index];
|
|
234
|
+
if (!row) continue;
|
|
235
|
+
this.#rowsContainer.addChild(new Text(this.#renderRow(row, index === this.#selectedIndex), 0, 0));
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
#renderRow(row: EditorRow, selected: boolean): string {
|
|
240
|
+
const prefix = selected ? theme.fg("accent", `${theme.nav.cursor} `) : " ";
|
|
241
|
+
let label = row.label;
|
|
242
|
+
if (selected) {
|
|
243
|
+
label = theme.fg("accent", label);
|
|
244
|
+
} else if (row.dimmer) {
|
|
245
|
+
label = theme.fg("dim", label);
|
|
246
|
+
}
|
|
247
|
+
let line = `${prefix}${label}`;
|
|
248
|
+
if (row.status) {
|
|
249
|
+
const status =
|
|
250
|
+
selected || row.kind === "providerAction" || row.kind === "action"
|
|
251
|
+
? theme.fg("dim", row.status)
|
|
252
|
+
: row.status === "logged in"
|
|
253
|
+
? theme.fg("success", row.status)
|
|
254
|
+
: theme.fg("dim", row.status);
|
|
255
|
+
line += ` ${status}`;
|
|
256
|
+
}
|
|
257
|
+
return line;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
#renderFooter(): void {
|
|
261
|
+
const row = this.#rows[this.#selectedIndex];
|
|
262
|
+
let detail = "";
|
|
263
|
+
if (row) {
|
|
264
|
+
switch (row.kind) {
|
|
265
|
+
case "provider": {
|
|
266
|
+
const entry = this.#context.snapshot().entries.find(candidate => candidate.id === row.providerId);
|
|
267
|
+
detail = entryDetail(entry);
|
|
268
|
+
break;
|
|
269
|
+
}
|
|
270
|
+
case "candidate": {
|
|
271
|
+
const entry = this.#context.snapshot().entries.find(candidate => candidate.id === row.providerId);
|
|
272
|
+
detail = entryDetail(entry, true);
|
|
273
|
+
break;
|
|
274
|
+
}
|
|
275
|
+
case "providerAction": {
|
|
276
|
+
detail = row.id.startsWith("moveup.")
|
|
277
|
+
? "Move this provider earlier in the priority order."
|
|
278
|
+
: row.id.startsWith("movedown.")
|
|
279
|
+
? "Move this provider later in the priority order."
|
|
280
|
+
: "Remove this provider from the priority order.";
|
|
281
|
+
break;
|
|
282
|
+
}
|
|
283
|
+
case "action":
|
|
284
|
+
detail =
|
|
285
|
+
row.id === "action.add"
|
|
286
|
+
? "Append a provider to the end of the priority order."
|
|
287
|
+
: row.id === "action.reset"
|
|
288
|
+
? "Clear the saved order so default provider ranking applies."
|
|
289
|
+
: "Close the editor. Changes already saved take effect immediately.";
|
|
290
|
+
break;
|
|
291
|
+
case "back":
|
|
292
|
+
detail = "Return to the priority order without adding a provider.";
|
|
293
|
+
break;
|
|
294
|
+
default:
|
|
295
|
+
detail = "";
|
|
296
|
+
break;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
this.#detailText.setText(theme.fg("muted", detail));
|
|
300
|
+
this.#hintText.setText(
|
|
301
|
+
theme.fg(
|
|
302
|
+
"dim",
|
|
303
|
+
this.#page === "add"
|
|
304
|
+
? " Up/Down navigate · Enter add · Esc back"
|
|
305
|
+
: " Up/Down navigate · Enter select · Esc close",
|
|
306
|
+
),
|
|
307
|
+
);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
#moveSelection(delta: 1 | -1): void {
|
|
311
|
+
if (this.#rows.length === 0) return;
|
|
312
|
+
this.#selectedIndex = (this.#selectedIndex + delta + this.#rows.length) % this.#rows.length;
|
|
313
|
+
this.#captureSelection();
|
|
314
|
+
this.#rebuild();
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
handleInput(data: string): void {
|
|
318
|
+
if (this.#closed || this.#persisting) return;
|
|
319
|
+
if (matchesKey(data, "up")) {
|
|
320
|
+
this.#moveSelection(-1);
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
if (matchesKey(data, "down")) {
|
|
324
|
+
this.#moveSelection(1);
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
if (matchesKey(data, "enter") || matchesKey(data, "return") || data === "\n") {
|
|
328
|
+
this.#activate();
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
if (getKeybindings().matches(data, "tui.select.cancel")) {
|
|
332
|
+
if (this.#page === "add") {
|
|
333
|
+
this.#page = "main";
|
|
334
|
+
this.#selectedIndex = 0;
|
|
335
|
+
this.#captureSelection();
|
|
336
|
+
this.#rebuild();
|
|
337
|
+
} else {
|
|
338
|
+
this.#close();
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
#activate(): void {
|
|
344
|
+
const row = this.#rows[this.#selectedIndex];
|
|
345
|
+
if (!row) return;
|
|
346
|
+
|
|
347
|
+
if (this.#page === "add") {
|
|
348
|
+
if (row.kind === "back") {
|
|
349
|
+
this.#page = "main";
|
|
350
|
+
this.#selectedIndex = 0;
|
|
351
|
+
this.#captureSelection();
|
|
352
|
+
this.#rebuild();
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
if (row.kind === "candidate" && row.providerId) {
|
|
356
|
+
const order = this.#context.snapshot().order;
|
|
357
|
+
void this.#persist([...order, row.providerId]);
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
if (row.kind === "provider") return;
|
|
364
|
+
|
|
365
|
+
if (row.kind === "providerAction" && row.providerId) {
|
|
366
|
+
this.#mutateOrder(row);
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
switch (row.id) {
|
|
371
|
+
case "action.add":
|
|
372
|
+
this.#page = "add";
|
|
373
|
+
this.#selectedIndex = 0;
|
|
374
|
+
this.#captureSelection();
|
|
375
|
+
this.#rebuild();
|
|
376
|
+
return;
|
|
377
|
+
case "action.reset":
|
|
378
|
+
void this.#persistReset();
|
|
379
|
+
return;
|
|
380
|
+
case "action.done":
|
|
381
|
+
this.#close();
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
#mutateOrder(row: EditorRow): void {
|
|
387
|
+
const providerId = row.providerId;
|
|
388
|
+
if (!providerId) return;
|
|
389
|
+
const order = [...this.#context.snapshot().order];
|
|
390
|
+
const index = order.indexOf(providerId);
|
|
391
|
+
if (index < 0) return;
|
|
392
|
+
if (row.id.startsWith("moveup.")) {
|
|
393
|
+
if (index === 0) return;
|
|
394
|
+
order.splice(index - 1, 2, providerId, order[index - 1]!);
|
|
395
|
+
} else if (row.id.startsWith("movedown.")) {
|
|
396
|
+
if (index === order.length - 1) return;
|
|
397
|
+
order.splice(index, 2, order[index + 1]!, providerId);
|
|
398
|
+
} else if (row.id.startsWith("remove.")) {
|
|
399
|
+
order.splice(index, 1);
|
|
400
|
+
} else {
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
void this.#persist(order);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
async #persist(order: readonly string[]): Promise<void> {
|
|
407
|
+
this.#persisting = true;
|
|
408
|
+
try {
|
|
409
|
+
await this.#context.persistOrder(order);
|
|
410
|
+
} catch (error) {
|
|
411
|
+
this.#onError?.(error instanceof Error ? error.message : String(error));
|
|
412
|
+
} finally {
|
|
413
|
+
this.#persisting = false;
|
|
414
|
+
if (!this.#closed) this.#rebuild();
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
async #persistReset(): Promise<void> {
|
|
419
|
+
this.#persisting = true;
|
|
420
|
+
try {
|
|
421
|
+
await this.#context.resetOrder();
|
|
422
|
+
} catch (error) {
|
|
423
|
+
this.#onError?.(error instanceof Error ? error.message : String(error));
|
|
424
|
+
} finally {
|
|
425
|
+
this.#persisting = false;
|
|
426
|
+
if (!this.#closed) this.#rebuild();
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
#close(): void {
|
|
431
|
+
if (this.#closed) return;
|
|
432
|
+
this.dispose();
|
|
433
|
+
this.#onClose();
|
|
434
|
+
}
|
|
435
|
+
override dispose(): void {
|
|
436
|
+
if (this.#closed) return;
|
|
437
|
+
this.#closed = true;
|
|
438
|
+
this.#context.dispose();
|
|
439
|
+
super.dispose();
|
|
440
|
+
}
|
|
441
|
+
}
|
|
@@ -64,7 +64,17 @@ export interface TextInputSettingDef extends BaseSettingDef {
|
|
|
64
64
|
type: "text";
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
|
|
67
|
+
/** Array setting rendered by a dedicated ordered-list editor (currently only `modelProviderOrder`). */
|
|
68
|
+
export interface ProviderOrderSettingDef extends BaseSettingDef {
|
|
69
|
+
type: "providerOrder";
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export type SettingDef =
|
|
73
|
+
| BooleanSettingDef
|
|
74
|
+
| EnumSettingDef
|
|
75
|
+
| SubmenuSettingDef
|
|
76
|
+
| TextInputSettingDef
|
|
77
|
+
| ProviderOrderSettingDef;
|
|
68
78
|
|
|
69
79
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
70
80
|
// Condition Functions
|
|
@@ -131,6 +141,16 @@ function pathToSettingDef(path: SettingPath): SettingDef | null {
|
|
|
131
141
|
return { ...base, type: "text" };
|
|
132
142
|
}
|
|
133
143
|
|
|
144
|
+
if (schemaType === "array") {
|
|
145
|
+
// Arrays render through dedicated custom editors rather than the generic
|
|
146
|
+
// value list. `modelProviderOrder` is the only array with a provider-order
|
|
147
|
+
// editor today; other arrays with a `ui` block stay hidden here.
|
|
148
|
+
if (path === "modelProviderOrder") {
|
|
149
|
+
return { ...base, type: "providerOrder" };
|
|
150
|
+
}
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
153
|
+
|
|
134
154
|
return null;
|
|
135
155
|
}
|
|
136
156
|
|
|
@@ -34,6 +34,7 @@ import {
|
|
|
34
34
|
} from "./notifications-settings-editor";
|
|
35
35
|
import { createPetSelectItems, getPetUnavailableWarning, isPetAvailable } from "./pet-capability";
|
|
36
36
|
import { handleInputOrEscape, PluginSettingsComponent } from "./plugin-settings";
|
|
37
|
+
import { normalizeProviderOrder } from "./provider-order-context";
|
|
37
38
|
import { getSettingsForTab, type SettingDef } from "./settings-defs";
|
|
38
39
|
import { getPreset } from "./status-line/presets";
|
|
39
40
|
import { ALL_SEGMENT_IDS } from "./status-line/segments";
|
|
@@ -724,6 +725,8 @@ export interface SettingsCallbacks {
|
|
|
724
725
|
onPluginsChanged?: () => void;
|
|
725
726
|
/** Called when asynchronously rebuilt settings content needs a repaint. */
|
|
726
727
|
onRenderRequested?: () => void;
|
|
728
|
+
/** Create the ordered provider-priority editor for `modelProviderOrder`. */
|
|
729
|
+
createProviderOrderEditor?: (done: () => void) => Container;
|
|
727
730
|
/** Called when an interactive setting cannot be committed. */
|
|
728
731
|
onError?: (message: string) => void;
|
|
729
732
|
/** Called when settings panel is closed */
|
|
@@ -762,6 +765,7 @@ export class SettingsSelectorComponent extends Container {
|
|
|
762
765
|
#statusPreviewText: Text | null = null;
|
|
763
766
|
#currentTabId: SettingTab | "plugins" | "gjc-bundles" = "appearance";
|
|
764
767
|
#textInputActive = false;
|
|
768
|
+
#activeProviderOrderEditor: Container | null = null;
|
|
765
769
|
|
|
766
770
|
constructor(
|
|
767
771
|
private readonly context: SettingsRuntimeContext,
|
|
@@ -795,6 +799,11 @@ export class SettingsSelectorComponent extends Container {
|
|
|
795
799
|
if (this.#currentTabId === "notifications" && tabId !== "notifications" && !this.#disposeNotificationsEditor()) {
|
|
796
800
|
return;
|
|
797
801
|
}
|
|
802
|
+
// Release an open provider-order editor (and its context subscriptions)
|
|
803
|
+
// before switching tabs; the submenu's done() never runs on tab change,
|
|
804
|
+
// so without this the abandoned editor's listeners would survive.
|
|
805
|
+
this.#activeProviderOrderEditor?.dispose();
|
|
806
|
+
this.#activeProviderOrderEditor = null;
|
|
798
807
|
this.#currentTabId = tabId;
|
|
799
808
|
|
|
800
809
|
// Remove current content
|
|
@@ -884,6 +893,29 @@ export class SettingsSelectorComponent extends Container {
|
|
|
884
893
|
submenu: (cv, done) => this.#createSubmenu(def, cv, done),
|
|
885
894
|
};
|
|
886
895
|
|
|
896
|
+
case "providerOrder": {
|
|
897
|
+
const createEditor = this.callbacks.createProviderOrderEditor;
|
|
898
|
+
if (!createEditor) return null;
|
|
899
|
+
return {
|
|
900
|
+
id: def.path,
|
|
901
|
+
label: def.label,
|
|
902
|
+
description: def.description,
|
|
903
|
+
currentValue: `${normalizeProviderOrder(settings.getGlobal("modelProviderOrder") ?? []).length} configured`,
|
|
904
|
+
submenu: (_currentValue, done) => {
|
|
905
|
+
const editor = createEditor(() => {
|
|
906
|
+
this.#activeProviderOrderEditor = null;
|
|
907
|
+
// Rebuild the parent list from current settings so the
|
|
908
|
+
// `${count} configured` summary reflects the editor's
|
|
909
|
+
// persistence before the submenu closes.
|
|
910
|
+
this.#refreshCurrentTabItems();
|
|
911
|
+
done();
|
|
912
|
+
});
|
|
913
|
+
this.#activeProviderOrderEditor = editor;
|
|
914
|
+
return editor;
|
|
915
|
+
},
|
|
916
|
+
};
|
|
917
|
+
}
|
|
918
|
+
|
|
887
919
|
case "text":
|
|
888
920
|
return {
|
|
889
921
|
id: def.path,
|
|
@@ -1035,6 +1067,11 @@ export class SettingsSelectorComponent extends Container {
|
|
|
1035
1067
|
options,
|
|
1036
1068
|
currentValue,
|
|
1037
1069
|
value => {
|
|
1070
|
+
if (def.path === "modelProfile.default") {
|
|
1071
|
+
this.callbacks.onChange(def.path, value);
|
|
1072
|
+
done(value);
|
|
1073
|
+
return;
|
|
1074
|
+
}
|
|
1038
1075
|
if (def.path === "pet.mode") {
|
|
1039
1076
|
// The shared pet commit policy rechecks capability immediately
|
|
1040
1077
|
// before mutation and persists only on acceptance; the settings
|
|
@@ -1264,9 +1301,11 @@ export class SettingsSelectorComponent extends Container {
|
|
|
1264
1301
|
}
|
|
1265
1302
|
|
|
1266
1303
|
/** Re-evaluate condition gates against the current settings and refresh the active list. */
|
|
1267
|
-
#refreshCurrentTabItems(defs
|
|
1304
|
+
#refreshCurrentTabItems(defs?: SettingDef[]): void {
|
|
1268
1305
|
if (this.#currentTabId === "plugins" || this.#currentTabId === "gjc-bundles" || !this.#currentList) return;
|
|
1269
|
-
this.#currentList.setItems(
|
|
1306
|
+
this.#currentList.setItems(
|
|
1307
|
+
this.#buildItemsForTab(defs ?? getSettingsForTab(this.#currentTabId), this.#currentTabId),
|
|
1308
|
+
);
|
|
1270
1309
|
}
|
|
1271
1310
|
|
|
1272
1311
|
/**
|
|
@@ -1335,6 +1374,10 @@ export class SettingsSelectorComponent extends Container {
|
|
|
1335
1374
|
|
|
1336
1375
|
override dispose(): void {
|
|
1337
1376
|
if (this.#notificationsEditor?.navigationLocked) return;
|
|
1377
|
+
// Release an open provider-order editor (and its context subscriptions)
|
|
1378
|
+
// when the whole selector is torn down without a normal close.
|
|
1379
|
+
this.#activeProviderOrderEditor?.dispose();
|
|
1380
|
+
this.#activeProviderOrderEditor = null;
|
|
1338
1381
|
this.#notificationsEditor?.dispose();
|
|
1339
1382
|
this.#notificationsEditor = null;
|
|
1340
1383
|
this.#gjcBundleComponent?.dispose();
|