@gajae-code/coding-agent 0.11.11 → 0.12.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 +49 -3
- package/dist/types/cli/read-cli.d.ts +1 -0
- package/dist/types/commands/deep-interview.d.ts +9 -0
- package/dist/types/commands/harness.d.ts +22 -0
- package/dist/types/commands/read.d.ts +6 -0
- package/dist/types/config/model-registry.d.ts +4 -0
- package/dist/types/config/provider-auth-health.d.ts +14 -0
- package/dist/types/config/provider-ranking.d.ts +59 -0
- package/dist/types/config/settings-schema.d.ts +29 -1
- package/dist/types/config/settings.d.ts +2 -0
- package/dist/types/extensibility/extensions/types.d.ts +22 -2
- package/dist/types/extensibility/shared-events.d.ts +6 -0
- package/dist/types/gjc-runtime/deep-interview-ambiguity.d.ts +5 -12
- package/dist/types/gjc-runtime/deep-interview-recorder.d.ts +17 -32
- package/dist/types/gjc-runtime/deep-interview-stage.d.ts +37 -0
- package/dist/types/gjc-runtime/deep-interview-state.d.ts +4 -135
- package/dist/types/gjc-runtime/gc-runtime.d.ts +1 -1
- package/dist/types/gjc-runtime/memory-guard-owner-claims.d.ts +47 -0
- package/dist/types/gjc-runtime/ralplan-runtime.d.ts +52 -1
- package/dist/types/gjc-runtime/state-writer.d.ts +5 -46
- package/dist/types/gjc-runtime/team-launch.d.ts +1 -1
- package/dist/types/gjc-runtime/team-runtime.d.ts +2 -1
- package/dist/types/gjc-runtime/team-store.d.ts +12 -0
- package/dist/types/gjc-runtime/team-worker-memory-guard.d.ts +92 -0
- package/dist/types/gjc-runtime/tmux-owner-isolation.d.ts +7 -0
- package/dist/types/gjc-runtime/ultragoal-receipt-freshness.d.ts +8 -0
- package/dist/types/gjc-runtime/workflow-manifest.d.ts +3 -5
- package/dist/types/internal-urls/local-root-gc.d.ts +2 -0
- package/dist/types/main.d.ts +1 -1
- package/dist/types/memories/index.d.ts +2 -0
- package/dist/types/modes/acp/acp-agent.d.ts +8 -0
- package/dist/types/modes/components/assistant-message.d.ts +3 -1
- package/dist/types/modes/components/irc-sidebar.d.ts +41 -0
- package/dist/types/modes/components/read-tool-group.d.ts +4 -4
- package/dist/types/modes/components/session-observer-overlay.d.ts +4 -0
- package/dist/types/modes/components/settings-selector.d.ts +2 -0
- package/dist/types/modes/components/tool-execution.d.ts +8 -0
- package/dist/types/modes/controllers/event-controller.d.ts +3 -0
- package/dist/types/modes/interactive-mode.d.ts +2 -0
- package/dist/types/modes/irc-observation-ledger.d.ts +17 -1
- package/dist/types/modes/types.d.ts +2 -0
- package/dist/types/modes/utils/ui-helpers.d.ts +16 -0
- package/dist/types/runtime-mcp/smithery-auth.d.ts +5 -0
- package/dist/types/sdk/broker/lifecycle.d.ts +5 -0
- package/dist/types/sdk/bus/control-drain-lease.d.ts +52 -0
- package/dist/types/sdk/bus/index.d.ts +15 -1
- package/dist/types/sdk/bus/kind-aware-reconciliation.d.ts +37 -0
- package/dist/types/sdk/bus/prompt-reconciliation.d.ts +2 -1
- package/dist/types/sdk/bus/reconciliation-store.d.ts +65 -0
- package/dist/types/sdk/host/control/operations.d.ts +1 -1
- package/dist/types/sdk/host/host.d.ts +2 -0
- package/dist/types/sdk/host/query/handlers.d.ts +5 -0
- package/dist/types/session/agent-session.d.ts +44 -10
- package/dist/types/session/memory-guard-checkpoint-participant.d.ts +69 -0
- package/dist/types/session/session-manager.d.ts +25 -13
- package/dist/types/session/streaming-output.d.ts +60 -5
- package/dist/types/skill-state/active-state.d.ts +0 -4
- package/dist/types/skill-state/workflow-hud.d.ts +3 -0
- package/dist/types/tools/browser/launch.d.ts +7 -0
- package/dist/types/tools/fetch.d.ts +17 -1
- package/dist/types/tools/output-meta.d.ts +21 -1
- package/dist/types/tools/read-internals.d.ts +12 -0
- package/dist/types/tools/read.d.ts +45 -1
- package/dist/types/tools/resource-gc.d.ts +4 -0
- package/dist/types/tools/tool-result.d.ts +7 -1
- package/package.json +9 -13
- package/scripts/benchmark-sticky-viewport-pr1.ts +239 -0
- package/scripts/capture-sticky-viewport-showcase.ts +292 -0
- package/scripts/generate-sdk-operation-inventory.ts +2 -0
- package/scripts/verify-gjc-sdk-canonicalization.ts +122 -18
- package/scripts/verify-sticky-viewport-showcase.ts +611 -0
- package/src/capability/index.ts +8 -0
- package/src/cli/read-cli.ts +2 -1
- package/src/cli.ts +1 -0
- package/src/commands/deep-interview.ts +29 -9
- package/src/commands/harness.ts +35 -12
- package/src/commands/read.ts +11 -2
- package/src/commands/team.ts +1 -1
- package/src/config/model-registry.ts +21 -2
- package/src/config/provider-auth-health.ts +42 -0
- package/src/config/provider-ranking.ts +119 -0
- package/src/config/settings-schema.ts +23 -1
- package/src/config/settings.ts +246 -108
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +229 -82
- package/src/defaults/gjc/skills/ralplan/SKILL.md +58 -25
- package/src/defaults/gjc/skills/team/SKILL.md +1 -1
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +25 -15
- package/src/eval/py/executor.ts +7 -2
- package/src/extensibility/extensions/types.ts +28 -4
- package/src/extensibility/shared-events.ts +5 -0
- package/src/gjc-runtime/deep-interview-ambiguity.ts +47 -82
- package/src/gjc-runtime/deep-interview-recorder.ts +227 -310
- package/src/gjc-runtime/deep-interview-runtime.ts +109 -270
- package/src/gjc-runtime/deep-interview-stage.ts +1016 -0
- package/src/gjc-runtime/deep-interview-state.ts +12 -943
- package/src/gjc-runtime/gc-render.ts +1 -0
- package/src/gjc-runtime/gc-runtime.ts +11 -1
- package/src/gjc-runtime/memory-guard-owner-claims.ts +389 -0
- package/src/gjc-runtime/ralplan-runtime.ts +742 -85
- package/src/gjc-runtime/state-runtime.ts +2 -1
- package/src/gjc-runtime/state-writer.ts +70 -222
- package/src/gjc-runtime/team-launch.ts +34 -5
- package/src/gjc-runtime/team-runtime.ts +970 -33
- package/src/gjc-runtime/team-store.ts +32 -0
- package/src/gjc-runtime/team-worker-memory-guard.ts +363 -0
- package/src/gjc-runtime/team-workers.ts +1 -0
- package/src/gjc-runtime/tmux-owner-isolation.ts +18 -0
- package/src/gjc-runtime/ultragoal-receipt-freshness.ts +20 -0
- package/src/gjc-runtime/ultragoal-runtime.ts +13 -7
- package/src/gjc-runtime/workflow-manifest.generated.json +97 -350
- package/src/gjc-runtime/workflow-manifest.ts +34 -179
- package/src/hooks/native-skill-hook.ts +17 -4
- package/src/hooks/skill-state.ts +1 -23
- package/src/internal-urls/docs-index.generated.ts +8 -9
- package/src/internal-urls/local-root-gc.ts +127 -0
- package/src/main.ts +9 -5
- package/src/memories/index.ts +10 -0
- package/src/modes/DESIGN.md +47 -0
- package/src/modes/acp/acp-agent.ts +43 -7
- package/src/modes/acp/acp-event-mapper.ts +14 -7
- package/src/modes/acp/acp-mode.ts +39 -3
- package/src/modes/components/assistant-message.ts +123 -22
- package/src/modes/components/irc-sidebar.ts +176 -62
- package/src/modes/components/model-selector.ts +61 -4
- package/src/modes/components/oauth-selector.ts +42 -12
- package/src/modes/components/read-tool-group.ts +82 -14
- package/src/modes/components/session-observer-overlay.ts +187 -32
- package/src/modes/components/settings-selector.ts +60 -18
- package/src/modes/components/tool-execution.ts +138 -27
- package/src/modes/controllers/event-controller.ts +109 -27
- package/src/modes/controllers/extension-ui-controller.ts +19 -2
- package/src/modes/controllers/input-controller.ts +18 -3
- package/src/modes/controllers/selector-controller.ts +83 -37
- package/src/modes/interactive-mode.ts +62 -3
- package/src/modes/irc-observation-ledger.ts +71 -15
- package/src/modes/runtime-init.ts +19 -2
- package/src/modes/types.ts +2 -0
- package/src/modes/utils/ui-helpers.ts +94 -1
- package/src/prompts/agents/architect.md +9 -1
- package/src/prompts/agents/critic.md +9 -0
- package/src/prompts/system/system-prompt.md +0 -1
- package/src/prompts/tools/read.md +4 -2
- package/src/runtime-mcp/smithery-auth.ts +17 -2
- package/src/runtime-mcp/smithery-connect.ts +11 -1
- package/src/sdk/broker/lifecycle.ts +25 -6
- package/src/sdk/bus/control-drain-lease.ts +104 -0
- package/src/sdk/bus/index.ts +341 -38
- package/src/sdk/bus/kind-aware-reconciliation.ts +242 -0
- package/src/sdk/bus/prompt-reconciliation.ts +2 -1
- package/src/sdk/bus/reconciliation-store.ts +224 -0
- package/src/sdk/host/control/dispatch.ts +5 -1
- package/src/sdk/host/control/operations.ts +1 -1
- package/src/sdk/host/host.ts +23 -1
- package/src/sdk/host/query/handlers.ts +55 -0
- package/src/sdk/mcp/server.ts +46 -18
- package/src/sdk/protocol/operation-inventory.generated.json +29 -0
- package/src/sdk/protocol/operation-registry.ts +7 -2
- package/src/sdk/session.ts +13 -1
- package/src/session/agent-session.ts +466 -50
- package/src/session/memory-guard-checkpoint-participant.ts +131 -0
- package/src/session/session-manager.ts +764 -17
- package/src/session/streaming-output.ts +262 -43
- package/src/setup/provider-onboarding.ts +19 -4
- package/src/skill-state/active-state.ts +0 -10
- package/src/skill-state/workflow-hud.ts +25 -2
- package/src/skill-state/workflow-mutation-guard.ts +65 -7
- package/src/tools/ask.ts +89 -69
- package/src/tools/browser/launch.ts +41 -6
- package/src/tools/fetch.ts +114 -23
- package/src/tools/output-meta.ts +176 -9
- package/src/tools/path-utils.ts +5 -1
- package/src/tools/read-internals.ts +44 -0
- package/src/tools/read.ts +1542 -310
- package/src/tools/resource-gc.ts +182 -18
- package/src/tools/tool-result.ts +14 -1
- package/src/web/search/providers/codex.ts +1 -1
- package/dist/types/gjc-runtime/deep-interview-draft.d.ts +0 -8
- package/dist/types/gjc-runtime/deep-interview-payload.d.ts +0 -41
- package/dist/types/gjc-runtime/deep-interview-repair.d.ts +0 -17
- package/src/gjc-runtime/deep-interview-draft.ts +0 -843
- package/src/gjc-runtime/deep-interview-payload.ts +0 -262
- package/src/gjc-runtime/deep-interview-repair.ts +0 -1674
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import * as crypto from "node:crypto";
|
|
1
2
|
import {
|
|
2
3
|
type Component,
|
|
3
4
|
padding,
|
|
@@ -21,6 +22,46 @@ const IRC_SIDEBAR_MAX_RENDER_SOURCE_UTF8_BYTES = 64 * 1_024;
|
|
|
21
22
|
const IRC_SIDEBAR_OLDER_MESSAGES_ELISION = "… older IRC messages elided …";
|
|
22
23
|
const IRC_SIDEBAR_MESSAGE_ELISION = " … message elided …";
|
|
23
24
|
|
|
25
|
+
export const __ircSidebarPerfCounters = {
|
|
26
|
+
enabled: false,
|
|
27
|
+
projectionMemoHits: 0,
|
|
28
|
+
projectionMemoMisses: 0,
|
|
29
|
+
recordsVisited: 0,
|
|
30
|
+
sourceBytes: 0,
|
|
31
|
+
rows: 0,
|
|
32
|
+
styledCacheHits: 0,
|
|
33
|
+
styledCacheMisses: 0,
|
|
34
|
+
wrapCalls: 0,
|
|
35
|
+
snapshot() {
|
|
36
|
+
return {
|
|
37
|
+
projectionMemoHits: this.projectionMemoHits,
|
|
38
|
+
projectionMemoMisses: this.projectionMemoMisses,
|
|
39
|
+
recordsVisited: this.recordsVisited,
|
|
40
|
+
sourceBytes: this.sourceBytes,
|
|
41
|
+
rows: this.rows,
|
|
42
|
+
styledCacheHits: this.styledCacheHits,
|
|
43
|
+
styledCacheMisses: this.styledCacheMisses,
|
|
44
|
+
wrapCalls: this.wrapCalls,
|
|
45
|
+
};
|
|
46
|
+
},
|
|
47
|
+
enable(): void {
|
|
48
|
+
this.enabled = true;
|
|
49
|
+
},
|
|
50
|
+
disable(): void {
|
|
51
|
+
this.enabled = false;
|
|
52
|
+
},
|
|
53
|
+
reset(): void {
|
|
54
|
+
this.projectionMemoHits = 0;
|
|
55
|
+
this.projectionMemoMisses = 0;
|
|
56
|
+
this.recordsVisited = 0;
|
|
57
|
+
this.sourceBytes = 0;
|
|
58
|
+
this.rows = 0;
|
|
59
|
+
this.styledCacheHits = 0;
|
|
60
|
+
this.styledCacheMisses = 0;
|
|
61
|
+
this.wrapCalls = 0;
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
|
|
24
65
|
/** Computes transcript/sidebar widths while preserving at least half the terminal for the transcript. */
|
|
25
66
|
export function computeIrcSplitWidths(width: number): {
|
|
26
67
|
leftWidth: number;
|
|
@@ -34,12 +75,7 @@ export function computeIrcSplitWidths(width: number): {
|
|
|
34
75
|
0,
|
|
35
76
|
Math.min(preferredSidebar, normalizedWidth - IRC_SEPARATOR_WIDTH - transcriptFloor),
|
|
36
77
|
);
|
|
37
|
-
|
|
38
|
-
// The sidebar yields entirely rather than rendering below its readable minimum.
|
|
39
|
-
if (sidebarWidth < IRC_SIDEBAR_MIN_WIDTH) {
|
|
40
|
-
return { leftWidth: normalizedWidth, separatorWidth: 0, rightWidth: 0 };
|
|
41
|
-
}
|
|
42
|
-
|
|
78
|
+
if (sidebarWidth < IRC_SIDEBAR_MIN_WIDTH) return { leftWidth: normalizedWidth, separatorWidth: 0, rightWidth: 0 };
|
|
43
79
|
return {
|
|
44
80
|
leftWidth: normalizedWidth - IRC_SEPARATOR_WIDTH - sidebarWidth,
|
|
45
81
|
separatorWidth: IRC_SEPARATOR_WIDTH,
|
|
@@ -47,52 +83,99 @@ export function computeIrcSplitWidths(width: number): {
|
|
|
47
83
|
};
|
|
48
84
|
}
|
|
49
85
|
|
|
50
|
-
|
|
51
|
-
|
|
86
|
+
type SemanticLine = Readonly<{
|
|
87
|
+
kind: "header" | "body" | "elision" | "blank";
|
|
88
|
+
text: string;
|
|
89
|
+
sender?: string;
|
|
90
|
+
recipient?: string;
|
|
91
|
+
time?: string;
|
|
92
|
+
}>;
|
|
93
|
+
export type IrcSidebarSemanticProjection = Readonly<{ token: string; lines: readonly SemanticLine[] }>;
|
|
94
|
+
const projectionMemo = new WeakMap<
|
|
95
|
+
IrcObservationLedger,
|
|
96
|
+
{ epoch: number; width: number; projection: IrcSidebarSemanticProjection }
|
|
97
|
+
>();
|
|
98
|
+
|
|
99
|
+
function semanticProjectionToken(lines: readonly SemanticLine[]): string {
|
|
100
|
+
const hash = crypto.createHash("sha256");
|
|
101
|
+
const length = Buffer.allocUnsafe(4);
|
|
102
|
+
const writeField = (value: string | undefined): void => {
|
|
103
|
+
length.writeUInt32BE(value === undefined ? 0xffffffff : Buffer.byteLength(value, "utf8"));
|
|
104
|
+
hash.update(length);
|
|
105
|
+
if (value !== undefined) hash.update(value, "utf8");
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
hash.update("gjc:irc:sidebar-semantic-projection:v1\0");
|
|
109
|
+
length.writeUInt32BE(lines.length);
|
|
110
|
+
hash.update(length);
|
|
111
|
+
for (const line of lines) {
|
|
112
|
+
writeField(line.kind);
|
|
113
|
+
writeField(line.text);
|
|
114
|
+
writeField(line.sender);
|
|
115
|
+
writeField(line.recipient);
|
|
116
|
+
writeField(line.time);
|
|
117
|
+
}
|
|
118
|
+
return hash.digest("hex");
|
|
52
119
|
}
|
|
53
120
|
|
|
54
|
-
function
|
|
121
|
+
function projectRecord(
|
|
55
122
|
record: IrcObservationRecord,
|
|
56
123
|
width: number,
|
|
57
|
-
componentTheme: IrcSidebarTheme,
|
|
58
124
|
maxRows: number,
|
|
59
125
|
maxSourceUtf8Bytes: number,
|
|
60
|
-
): { lines:
|
|
126
|
+
): { lines: SemanticLine[]; sourceUtf8Bytes: number; truncated: boolean } {
|
|
61
127
|
const bodyWidth = Math.max(1, width - 2);
|
|
62
128
|
const sourceLimit = Math.min(maxSourceUtf8Bytes, Math.max(0, maxRows - 1) * bodyWidth);
|
|
63
129
|
const clipped = projectIrcText(record.text, sourceLimit);
|
|
64
130
|
const block = formatIrcMessageBlock({ ...record, text: clipped.text });
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
131
|
+
const lines: SemanticLine[] = [
|
|
132
|
+
{
|
|
133
|
+
kind: "header",
|
|
134
|
+
text: `${block.sender} → ${block.recipient} · ${block.time}`,
|
|
135
|
+
sender: block.sender,
|
|
136
|
+
recipient: block.recipient,
|
|
137
|
+
time: block.time,
|
|
138
|
+
},
|
|
139
|
+
];
|
|
68
140
|
let truncated = clipped.truncated;
|
|
69
|
-
|
|
70
141
|
body: for (const bodyLine of block.bodyLines) {
|
|
142
|
+
if (__ircSidebarPerfCounters.enabled) __ircSidebarPerfCounters.wrapCalls++;
|
|
71
143
|
for (const wrappedLine of wrapTextWithAnsi(bodyLine, bodyWidth)) {
|
|
72
144
|
if (lines.length >= maxRows) {
|
|
73
145
|
truncated = true;
|
|
74
146
|
break body;
|
|
75
147
|
}
|
|
76
|
-
lines.push(
|
|
148
|
+
lines.push({ kind: "body", text: ` ${wrappedLine}` });
|
|
77
149
|
}
|
|
78
150
|
}
|
|
79
151
|
if (truncated && maxRows > 1) {
|
|
80
|
-
const marker =
|
|
81
|
-
if (lines.length >= maxRows) lines[maxRows - 1] =
|
|
82
|
-
else lines.push(
|
|
152
|
+
const marker: SemanticLine = { kind: "elision", text: IRC_SIDEBAR_MESSAGE_ELISION };
|
|
153
|
+
if (lines.length >= maxRows) lines[maxRows - 1] = marker;
|
|
154
|
+
else lines.push(marker);
|
|
83
155
|
}
|
|
84
156
|
return { lines, sourceUtf8Bytes: clipped.utf8Bytes, truncated };
|
|
85
157
|
}
|
|
86
158
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
159
|
+
/** The authoritative bounded, display-neutral IRC sidebar projection. */
|
|
160
|
+
export function getIrcSidebarSemanticProjection(
|
|
161
|
+
ledger: IrcObservationLedger,
|
|
162
|
+
width: number,
|
|
163
|
+
): IrcSidebarSemanticProjection {
|
|
164
|
+
if (width <= 0) return { token: "", lines: [] };
|
|
165
|
+
const memo = projectionMemo.get(ledger);
|
|
166
|
+
const cachedProjection = memo?.epoch === ledger.mutationEpoch && memo.width === width ? memo.projection : undefined;
|
|
167
|
+
if (cachedProjection) {
|
|
168
|
+
if (__ircSidebarPerfCounters.enabled) __ircSidebarPerfCounters.projectionMemoHits++;
|
|
169
|
+
return cachedProjection;
|
|
170
|
+
}
|
|
171
|
+
if (__ircSidebarPerfCounters.enabled) __ircSidebarPerfCounters.projectionMemoMisses++;
|
|
172
|
+
const newestFirstBlocks: SemanticLine[][] = [];
|
|
92
173
|
let renderedRows = 0;
|
|
93
174
|
let renderedSourceUtf8Bytes = 0;
|
|
94
175
|
let omittedOlderRecords = false;
|
|
176
|
+
const records = ledger.getSidebarRecords();
|
|
95
177
|
for (let index = records.length - 1; index >= 0; index--) {
|
|
178
|
+
if (__ircSidebarPerfCounters.enabled) __ircSidebarPerfCounters.recordsVisited++;
|
|
96
179
|
const separatorRows = newestFirstBlocks.length > 0 ? 1 : 0;
|
|
97
180
|
const availableRows = IRC_SIDEBAR_MAX_RENDER_ROWS - renderedRows - separatorRows - 2;
|
|
98
181
|
const availableSourceUtf8Bytes = IRC_SIDEBAR_MAX_RENDER_SOURCE_UTF8_BYTES - renderedSourceUtf8Bytes;
|
|
@@ -101,32 +184,32 @@ function renderSidebarRecords(ledger: IrcObservationLedger, width: number, compo
|
|
|
101
184
|
omittedOlderRecords = true;
|
|
102
185
|
break;
|
|
103
186
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
availableSourceUtf8Bytes,
|
|
111
|
-
);
|
|
112
|
-
newestFirstBlocks.push(rendered.lines);
|
|
113
|
-
renderedRows += separatorRows + rendered.lines.length;
|
|
114
|
-
renderedSourceUtf8Bytes += rendered.sourceUtf8Bytes;
|
|
115
|
-
if (rendered.truncated) {
|
|
187
|
+
const projected = projectRecord(records[index], width, availableRows, availableSourceUtf8Bytes);
|
|
188
|
+
newestFirstBlocks.push(projected.lines);
|
|
189
|
+
renderedRows += separatorRows + projected.lines.length;
|
|
190
|
+
renderedSourceUtf8Bytes += projected.sourceUtf8Bytes;
|
|
191
|
+
if (__ircSidebarPerfCounters.enabled) __ircSidebarPerfCounters.sourceBytes += projected.sourceUtf8Bytes;
|
|
192
|
+
if (projected.truncated) {
|
|
116
193
|
omittedOlderRecords = index > 0;
|
|
117
194
|
break;
|
|
118
195
|
}
|
|
119
196
|
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
lines.push(truncateToWidth(componentTheme.fg("dim", IRC_SIDEBAR_OLDER_MESSAGES_ELISION), width));
|
|
124
|
-
}
|
|
197
|
+
const lines: SemanticLine[] = omittedOlderRecords
|
|
198
|
+
? [{ kind: "elision", text: IRC_SIDEBAR_OLDER_MESSAGES_ELISION }]
|
|
199
|
+
: [];
|
|
125
200
|
for (const block of newestFirstBlocks.reverse()) {
|
|
126
|
-
if (lines.length > 0) lines.push("");
|
|
201
|
+
if (lines.length > 0) lines.push({ kind: "blank", text: "" });
|
|
127
202
|
lines.push(...block);
|
|
128
203
|
}
|
|
129
|
-
|
|
204
|
+
const bounded = lines.slice(0, IRC_SIDEBAR_MAX_RENDER_ROWS);
|
|
205
|
+
if (__ircSidebarPerfCounters.enabled) __ircSidebarPerfCounters.rows += bounded.length;
|
|
206
|
+
const projection = { token: semanticProjectionToken(bounded), lines: bounded };
|
|
207
|
+
projectionMemo.set(ledger, { epoch: ledger.mutationEpoch, width, projection });
|
|
208
|
+
return projection;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
export function getIrcSidebarSemanticToken(ledger: IrcObservationLedger, width: number): string {
|
|
212
|
+
return getIrcSidebarSemanticProjection(ledger, width).token;
|
|
130
213
|
}
|
|
131
214
|
|
|
132
215
|
export interface IrcSidebarTheme {
|
|
@@ -134,64 +217,96 @@ export interface IrcSidebarTheme {
|
|
|
134
217
|
bold(text: string): string;
|
|
135
218
|
readonly boxSharp: { readonly vertical: string };
|
|
136
219
|
}
|
|
137
|
-
|
|
138
220
|
export type IrcSidebarThemeSource = IrcSidebarTheme | (() => IrcSidebarTheme);
|
|
139
221
|
|
|
140
222
|
/** Read-only IRC history alongside the active transcript. */
|
|
141
223
|
export class IrcSplitViewComponent implements ViewportAnchorProvider {
|
|
142
224
|
#visible = false;
|
|
225
|
+
#themeGeneration = 0;
|
|
226
|
+
#styledMemo:
|
|
227
|
+
| { token: string; width: number; themeGeneration: number; theme: IrcSidebarTheme; lines: string[] }
|
|
228
|
+
| undefined;
|
|
143
229
|
|
|
144
230
|
constructor(
|
|
145
231
|
private readonly leftPane: Component,
|
|
146
232
|
private readonly ledger: IrcObservationLedger,
|
|
147
233
|
private readonly componentTheme: IrcSidebarThemeSource,
|
|
148
234
|
) {}
|
|
149
|
-
|
|
150
235
|
get visible(): boolean {
|
|
151
236
|
return this.#visible;
|
|
152
237
|
}
|
|
153
|
-
|
|
154
238
|
effectiveSidebarVisible(width = process.stdout.columns ?? 0): boolean {
|
|
155
239
|
return this.#visible && computeIrcSplitWidths(width).rightWidth > 0;
|
|
156
240
|
}
|
|
157
|
-
|
|
158
241
|
setVisible(visible: boolean): void {
|
|
159
|
-
if (this.#visible
|
|
160
|
-
|
|
161
|
-
|
|
242
|
+
if (this.#visible !== visible) {
|
|
243
|
+
this.#visible = visible;
|
|
244
|
+
this.invalidate();
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
invalidateTheme(): void {
|
|
248
|
+
this.#themeGeneration++;
|
|
249
|
+
this.#styledMemo = undefined;
|
|
250
|
+
}
|
|
251
|
+
resetSource(): void {
|
|
252
|
+
projectionMemo.delete(this.ledger);
|
|
253
|
+
this.#styledMemo = undefined;
|
|
162
254
|
}
|
|
163
|
-
|
|
164
255
|
render(width: number): string[] {
|
|
165
256
|
return this.renderWithViewportAnchors(width).lines;
|
|
166
257
|
}
|
|
167
|
-
|
|
258
|
+
#renderSidebar(width: number, componentTheme: IrcSidebarTheme): string[] {
|
|
259
|
+
const projection = getIrcSidebarSemanticProjection(this.ledger, width);
|
|
260
|
+
const cached = this.#styledMemo;
|
|
261
|
+
if (
|
|
262
|
+
cached?.token === projection.token &&
|
|
263
|
+
cached.width === width &&
|
|
264
|
+
cached.themeGeneration === this.#themeGeneration &&
|
|
265
|
+
cached.theme === componentTheme
|
|
266
|
+
) {
|
|
267
|
+
if (__ircSidebarPerfCounters.enabled) __ircSidebarPerfCounters.styledCacheHits++;
|
|
268
|
+
return cached.lines;
|
|
269
|
+
}
|
|
270
|
+
if (__ircSidebarPerfCounters.enabled) __ircSidebarPerfCounters.styledCacheMisses++;
|
|
271
|
+
const lines = projection.lines.map(line => {
|
|
272
|
+
if (line.kind === "header")
|
|
273
|
+
return truncateToWidth(
|
|
274
|
+
`${componentTheme.fg("accent", componentTheme.bold(line.sender!))} → ${line.recipient} · ${componentTheme.fg("dim", line.time!)}`,
|
|
275
|
+
width,
|
|
276
|
+
);
|
|
277
|
+
return truncateToWidth(line.kind === "elision" ? componentTheme.fg("dim", line.text) : line.text, width);
|
|
278
|
+
});
|
|
279
|
+
this.#styledMemo = {
|
|
280
|
+
token: projection.token,
|
|
281
|
+
width,
|
|
282
|
+
themeGeneration: this.#themeGeneration,
|
|
283
|
+
theme: componentTheme,
|
|
284
|
+
lines,
|
|
285
|
+
};
|
|
286
|
+
return lines;
|
|
287
|
+
}
|
|
168
288
|
renderWithViewportAnchors(width: number): ViewportAnchorRender {
|
|
169
289
|
if (!this.#visible) return renderComponentWithViewportAnchors(this.leftPane, width);
|
|
170
|
-
|
|
171
290
|
const componentTheme = typeof this.componentTheme === "function" ? this.componentTheme() : this.componentTheme;
|
|
172
291
|
const { leftWidth, separatorWidth, rightWidth } = computeIrcSplitWidths(width);
|
|
173
292
|
if (rightWidth === 0) return renderComponentWithViewportAnchors(this.leftPane, width);
|
|
174
|
-
|
|
175
|
-
const separatorText = componentTheme.fg("dim", ` ${componentTheme.boxSharp.vertical} `);
|
|
176
|
-
const separator = separatorWidth > 0 ? separatorText : "";
|
|
293
|
+
const separator = separatorWidth > 0 ? componentTheme.fg("dim", ` ${componentTheme.boxSharp.vertical} `) : "";
|
|
177
294
|
const leftRender = withTerminalGraphicsFallback(
|
|
178
295
|
() => renderComponentWithViewportAnchors(this.leftPane, leftWidth),
|
|
179
296
|
{ allowCursorNeutralImages: true },
|
|
180
297
|
);
|
|
181
|
-
const rightLines =
|
|
298
|
+
const rightLines = this.#renderSidebar(rightWidth, componentTheme);
|
|
182
299
|
const lineCount = Math.max(leftRender.lines.length, rightLines.length);
|
|
183
300
|
const lines: string[] = [];
|
|
184
301
|
const anchors: ViewportAnchorRender["anchors"] = [];
|
|
185
|
-
|
|
186
302
|
const leftOffset = lineCount - leftRender.lines.length;
|
|
187
303
|
const rightOffset = lineCount - rightLines.length;
|
|
188
304
|
for (let index = 0; index < lineCount; index++) {
|
|
189
305
|
const leftIndex = index - leftOffset;
|
|
190
306
|
const leftRaw = leftRender.lines[leftIndex] ?? "";
|
|
191
307
|
const right = truncateToWidth(rightLines[index - rightOffset] ?? "", rightWidth);
|
|
192
|
-
if (TERMINAL.isImageLine(leftRaw))
|
|
193
|
-
|
|
194
|
-
} else {
|
|
308
|
+
if (TERMINAL.isImageLine(leftRaw)) lines.push(leftRaw + padding(leftWidth) + separator + right);
|
|
309
|
+
else {
|
|
195
310
|
const left = truncateToWidth(leftRaw, leftWidth);
|
|
196
311
|
lines.push(left + padding(Math.max(0, leftWidth - visibleWidth(left))) + separator + right);
|
|
197
312
|
}
|
|
@@ -199,7 +314,6 @@ export class IrcSplitViewComponent implements ViewportAnchorProvider {
|
|
|
199
314
|
}
|
|
200
315
|
return { lines, anchors };
|
|
201
316
|
}
|
|
202
|
-
|
|
203
317
|
invalidate(): void {
|
|
204
318
|
this.leftPane.invalidate?.();
|
|
205
319
|
}
|
|
@@ -31,6 +31,8 @@ import {
|
|
|
31
31
|
} from "../../config/model-resolver";
|
|
32
32
|
import { type ModelSelectorValue, normalizeModelSelectorValue, selectorHead } from "../../config/model-selector-value";
|
|
33
33
|
import type { ModelProfileConfig } from "../../config/models-config-schema";
|
|
34
|
+
import { getProviderAuthHealth } from "../../config/provider-auth-health";
|
|
35
|
+
import { compareRankedProviders, type ProviderAuthState } from "../../config/provider-ranking";
|
|
34
36
|
import type { Settings } from "../../config/settings";
|
|
35
37
|
import { type ThemeColor, theme } from "../../modes/theme/theme";
|
|
36
38
|
import { formatModelOnboardingInlineHint } from "../../setup/model-onboarding-guidance";
|
|
@@ -542,10 +544,22 @@ export class ModelSelectorComponent extends Container {
|
|
|
542
544
|
this.#applyTabFilter();
|
|
543
545
|
}
|
|
544
546
|
|
|
547
|
+
#resolveProviderAuthState(providerId: string): ProviderAuthState {
|
|
548
|
+
const health = getProviderAuthHealth(this.#modelRegistry.authStorage, providerId);
|
|
549
|
+
if (health) return health;
|
|
550
|
+
return this.#modelRegistry.hasConfiguredProviderAuth(providerId) ? "configured" : "none";
|
|
551
|
+
}
|
|
552
|
+
|
|
545
553
|
#sortModels(models: ModelItem[]): void {
|
|
546
|
-
// Sort: default-tagged model first, then MRU, then
|
|
554
|
+
// Sort: default-tagged model first, then MRU, then provider ranking
|
|
547
555
|
const mruOrder = this.#settings.getStorage()?.getModelUsageOrder() ?? [];
|
|
548
556
|
const mruIndex = new Map(mruOrder.map((key, i) => [key, i]));
|
|
557
|
+
const providerAuthStateById = new Map<string, ProviderAuthState>();
|
|
558
|
+
for (const item of models) {
|
|
559
|
+
if (!providerAuthStateById.has(item.provider)) {
|
|
560
|
+
providerAuthStateById.set(item.provider, this.#resolveProviderAuthState(item.provider));
|
|
561
|
+
}
|
|
562
|
+
}
|
|
549
563
|
|
|
550
564
|
const modelRank = (item: ModelItem) => computeModelRank(item.model, this.#roles);
|
|
551
565
|
|
|
@@ -566,7 +580,18 @@ export class ModelSelectorComponent extends Container {
|
|
|
566
580
|
if (aMru !== bMru) return aMru - bMru;
|
|
567
581
|
|
|
568
582
|
// By provider, then recency within provider
|
|
569
|
-
const providerCmp =
|
|
583
|
+
const providerCmp = compareRankedProviders(
|
|
584
|
+
{
|
|
585
|
+
id: a.provider,
|
|
586
|
+
label: a.provider,
|
|
587
|
+
authState: providerAuthStateById.get(a.provider) ?? "none",
|
|
588
|
+
},
|
|
589
|
+
{
|
|
590
|
+
id: b.provider,
|
|
591
|
+
label: b.provider,
|
|
592
|
+
authState: providerAuthStateById.get(b.provider) ?? "none",
|
|
593
|
+
},
|
|
594
|
+
);
|
|
570
595
|
if (providerCmp !== 0) return providerCmp;
|
|
571
596
|
|
|
572
597
|
// Priority field (lower = better, e.g. OpenAI code backend priority values)
|
|
@@ -608,6 +633,12 @@ export class ModelSelectorComponent extends Container {
|
|
|
608
633
|
#sortCanonicalModels(models: CanonicalModelItem[]): void {
|
|
609
634
|
const mruOrder = this.#settings.getStorage()?.getModelUsageOrder() ?? [];
|
|
610
635
|
const mruIndex = new Map(mruOrder.map((key, i) => [key, i]));
|
|
636
|
+
const providerAuthStateById = new Map<string, ProviderAuthState>();
|
|
637
|
+
for (const item of models) {
|
|
638
|
+
if (!providerAuthStateById.has(item.model.provider)) {
|
|
639
|
+
providerAuthStateById.set(item.model.provider, this.#resolveProviderAuthState(item.model.provider));
|
|
640
|
+
}
|
|
641
|
+
}
|
|
611
642
|
|
|
612
643
|
const modelRank = (item: CanonicalModelItem) => computeModelRank(item.model, this.#roles);
|
|
613
644
|
|
|
@@ -620,7 +651,18 @@ export class ModelSelectorComponent extends Container {
|
|
|
620
651
|
const bMru = mruIndex.get(`${b.model.provider}/${b.model.id}`) ?? Number.MAX_SAFE_INTEGER;
|
|
621
652
|
if (aMru !== bMru) return aMru - bMru;
|
|
622
653
|
|
|
623
|
-
const providerCmp =
|
|
654
|
+
const providerCmp = compareRankedProviders(
|
|
655
|
+
{
|
|
656
|
+
id: a.model.provider,
|
|
657
|
+
label: a.model.provider,
|
|
658
|
+
authState: providerAuthStateById.get(a.model.provider) ?? "none",
|
|
659
|
+
},
|
|
660
|
+
{
|
|
661
|
+
id: b.model.provider,
|
|
662
|
+
label: b.model.provider,
|
|
663
|
+
authState: providerAuthStateById.get(b.model.provider) ?? "none",
|
|
664
|
+
},
|
|
665
|
+
);
|
|
624
666
|
if (providerCmp !== 0) return providerCmp;
|
|
625
667
|
|
|
626
668
|
return a.id.localeCompare(b.id);
|
|
@@ -735,8 +777,23 @@ export class ModelSelectorComponent extends Container {
|
|
|
735
777
|
for (const provider of this.#modelRegistry.getDiscoverableProviders()) {
|
|
736
778
|
providerSet.add(provider);
|
|
737
779
|
}
|
|
780
|
+
const providerAuthStateById = new Map<string, ProviderAuthState>();
|
|
781
|
+
for (const provider of providerSet) {
|
|
782
|
+
providerAuthStateById.set(provider, this.#resolveProviderAuthState(provider));
|
|
783
|
+
}
|
|
738
784
|
const sortedProviderIds = Array.from(providerSet).sort((left, right) =>
|
|
739
|
-
|
|
785
|
+
compareRankedProviders(
|
|
786
|
+
{
|
|
787
|
+
id: left,
|
|
788
|
+
label: formatProviderTabLabel(left),
|
|
789
|
+
authState: providerAuthStateById.get(left) ?? "none",
|
|
790
|
+
},
|
|
791
|
+
{
|
|
792
|
+
id: right,
|
|
793
|
+
label: formatProviderTabLabel(right),
|
|
794
|
+
authState: providerAuthStateById.get(right) ?? "none",
|
|
795
|
+
},
|
|
796
|
+
),
|
|
740
797
|
);
|
|
741
798
|
this.#providers = [...STATIC_PROVIDER_TABS, ...sortedProviderIds.map(createProviderTab)];
|
|
742
799
|
const activeIndex = this.#providers.findIndex(tab => tab.id === activeTabId);
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { getOAuthProviders } from "@gajae-code/ai/utils/oauth";
|
|
2
2
|
import type { OAuthProviderInfo } from "@gajae-code/ai/utils/oauth/types";
|
|
3
3
|
import { Container, matchesKey, Spacer, TruncatedText } from "@gajae-code/tui";
|
|
4
|
+
import { recordProviderAuthHealth } from "../../config/provider-auth-health";
|
|
5
|
+
import { compareRankedProviders, type ProviderAuthState } from "../../config/provider-ranking";
|
|
4
6
|
import { theme } from "../../modes/theme/theme";
|
|
5
7
|
import { matchesSelectCancel } from "../../modes/utils/keybinding-matchers";
|
|
6
8
|
import type { AuthStorage } from "../../session/auth-storage";
|
|
@@ -14,6 +16,7 @@ const OAUTH_SELECTOR_MAX_VISIBLE = 10;
|
|
|
14
16
|
export class OAuthSelectorComponent extends Container {
|
|
15
17
|
#listContainer: Container;
|
|
16
18
|
#allProviders: OAuthProviderInfo[] = [];
|
|
19
|
+
#sortedProviders: OAuthProviderInfo[] = [];
|
|
17
20
|
#selectedIndex: number = 0;
|
|
18
21
|
#mode: "login" | "logout";
|
|
19
22
|
#authStorage: AuthStorage;
|
|
@@ -91,7 +94,7 @@ export class OAuthSelectorComponent extends Container {
|
|
|
91
94
|
}
|
|
92
95
|
this.#authState.set(provider.id, "checking");
|
|
93
96
|
pending += 1;
|
|
94
|
-
void this.#validateProvider(provider.id, generation);
|
|
97
|
+
void this.#validateProvider(provider.id, generation, this.#authStorage.getGeneration());
|
|
95
98
|
}
|
|
96
99
|
|
|
97
100
|
if (pending > 0) {
|
|
@@ -101,7 +104,7 @@ export class OAuthSelectorComponent extends Container {
|
|
|
101
104
|
}
|
|
102
105
|
}
|
|
103
106
|
|
|
104
|
-
async #validateProvider(providerId: string, generation: number): Promise<void> {
|
|
107
|
+
async #validateProvider(providerId: string, generation: number, authGeneration: number): Promise<void> {
|
|
105
108
|
if (!this.#validateAuthCallback) return;
|
|
106
109
|
let isValid = false;
|
|
107
110
|
try {
|
|
@@ -112,6 +115,11 @@ export class OAuthSelectorComponent extends Container {
|
|
|
112
115
|
|
|
113
116
|
if (generation !== this.#validationGeneration) return;
|
|
114
117
|
this.#authState.set(providerId, isValid ? "valid" : "invalid");
|
|
118
|
+
// Only record the ordering hint when the credentials validated are still the
|
|
119
|
+
// current ones; a result from a superseded generation must not describe them.
|
|
120
|
+
if (authGeneration === this.#authStorage.getGeneration()) {
|
|
121
|
+
recordProviderAuthHealth(this.#authStorage, providerId, isValid ? "valid" : "invalid");
|
|
122
|
+
}
|
|
115
123
|
if (![...this.#authState.values()].includes("checking")) {
|
|
116
124
|
this.#stopSpinner();
|
|
117
125
|
}
|
|
@@ -138,6 +146,10 @@ export class OAuthSelectorComponent extends Container {
|
|
|
138
146
|
}
|
|
139
147
|
}
|
|
140
148
|
|
|
149
|
+
#getProviderAuthState(providerId: string): ProviderAuthState {
|
|
150
|
+
return this.#authState.get(providerId) ?? "none";
|
|
151
|
+
}
|
|
152
|
+
|
|
141
153
|
#getStatusIndicator(providerId: string): string {
|
|
142
154
|
const state = this.#authState.get(providerId);
|
|
143
155
|
if (state === "checking") {
|
|
@@ -154,9 +166,25 @@ export class OAuthSelectorComponent extends Container {
|
|
|
154
166
|
return this.#authStorage.hasAuth(providerId) ? theme.fg("success", ` ${theme.status.success} logged in`) : "";
|
|
155
167
|
}
|
|
156
168
|
#updateList(): void {
|
|
169
|
+
const selectedProviderId = this.#sortedProviders[this.#selectedIndex]?.id;
|
|
170
|
+
const rankedProviders = this.#allProviders.map(provider => ({
|
|
171
|
+
provider,
|
|
172
|
+
id: provider.id,
|
|
173
|
+
label: provider.name,
|
|
174
|
+
authState: this.#getProviderAuthState(provider.id),
|
|
175
|
+
}));
|
|
176
|
+
rankedProviders.sort(compareRankedProviders);
|
|
177
|
+
this.#sortedProviders = rankedProviders.map(({ provider }) => provider);
|
|
178
|
+
if (selectedProviderId !== undefined) {
|
|
179
|
+
const selectedIndex = this.#sortedProviders.findIndex(provider => provider.id === selectedProviderId);
|
|
180
|
+
if (selectedIndex >= 0) this.#selectedIndex = selectedIndex;
|
|
181
|
+
}
|
|
182
|
+
if (this.#selectedIndex >= this.#sortedProviders.length) {
|
|
183
|
+
this.#selectedIndex = Math.max(0, this.#sortedProviders.length - 1);
|
|
184
|
+
}
|
|
157
185
|
this.#listContainer.clear();
|
|
158
186
|
|
|
159
|
-
const total = this.#
|
|
187
|
+
const total = this.#sortedProviders.length;
|
|
160
188
|
const maxVisible = OAUTH_SELECTOR_MAX_VISIBLE;
|
|
161
189
|
const startIndex =
|
|
162
190
|
total <= maxVisible
|
|
@@ -165,7 +193,7 @@ export class OAuthSelectorComponent extends Container {
|
|
|
165
193
|
const endIndex = Math.min(startIndex + maxVisible, total);
|
|
166
194
|
|
|
167
195
|
for (let i = startIndex; i < endIndex; i++) {
|
|
168
|
-
const provider = this.#
|
|
196
|
+
const provider = this.#sortedProviders[i];
|
|
169
197
|
if (!provider) continue;
|
|
170
198
|
const isSelected = i === this.#selectedIndex;
|
|
171
199
|
const isAvailable = provider.available;
|
|
@@ -218,23 +246,25 @@ export class OAuthSelectorComponent extends Container {
|
|
|
218
246
|
handleInput(keyData: string): void {
|
|
219
247
|
// Up arrow
|
|
220
248
|
if (matchesKey(keyData, "up")) {
|
|
221
|
-
if (this.#
|
|
222
|
-
this.#selectedIndex =
|
|
249
|
+
if (this.#sortedProviders.length > 0) {
|
|
250
|
+
this.#selectedIndex =
|
|
251
|
+
this.#selectedIndex === 0 ? this.#sortedProviders.length - 1 : this.#selectedIndex - 1;
|
|
223
252
|
}
|
|
224
253
|
this.#statusMessage = undefined;
|
|
225
254
|
this.#updateList();
|
|
226
255
|
}
|
|
227
256
|
// Down arrow
|
|
228
257
|
else if (matchesKey(keyData, "down")) {
|
|
229
|
-
if (this.#
|
|
230
|
-
this.#selectedIndex =
|
|
258
|
+
if (this.#sortedProviders.length > 0) {
|
|
259
|
+
this.#selectedIndex =
|
|
260
|
+
this.#selectedIndex === this.#sortedProviders.length - 1 ? 0 : this.#selectedIndex + 1;
|
|
231
261
|
}
|
|
232
262
|
this.#statusMessage = undefined;
|
|
233
263
|
this.#updateList();
|
|
234
264
|
}
|
|
235
265
|
// Page up - jump up by one visible page
|
|
236
266
|
else if (matchesKey(keyData, "pageUp")) {
|
|
237
|
-
if (this.#
|
|
267
|
+
if (this.#sortedProviders.length > 0) {
|
|
238
268
|
this.#selectedIndex = Math.max(0, this.#selectedIndex - OAUTH_SELECTOR_MAX_VISIBLE);
|
|
239
269
|
}
|
|
240
270
|
this.#statusMessage = undefined;
|
|
@@ -242,9 +272,9 @@ export class OAuthSelectorComponent extends Container {
|
|
|
242
272
|
}
|
|
243
273
|
// Page down - jump down by one visible page
|
|
244
274
|
else if (matchesKey(keyData, "pageDown")) {
|
|
245
|
-
if (this.#
|
|
275
|
+
if (this.#sortedProviders.length > 0) {
|
|
246
276
|
this.#selectedIndex = Math.min(
|
|
247
|
-
this.#
|
|
277
|
+
this.#sortedProviders.length - 1,
|
|
248
278
|
this.#selectedIndex + OAUTH_SELECTOR_MAX_VISIBLE,
|
|
249
279
|
);
|
|
250
280
|
}
|
|
@@ -253,7 +283,7 @@ export class OAuthSelectorComponent extends Container {
|
|
|
253
283
|
}
|
|
254
284
|
// Enter
|
|
255
285
|
else if (matchesKey(keyData, "enter") || matchesKey(keyData, "return") || keyData === "\n") {
|
|
256
|
-
const selectedProvider = this.#
|
|
286
|
+
const selectedProvider = this.#sortedProviders[this.#selectedIndex];
|
|
257
287
|
if (selectedProvider?.available) {
|
|
258
288
|
this.#statusMessage = undefined;
|
|
259
289
|
this.stopValidation();
|