@gajae-code/coding-agent 0.9.0 → 0.9.2
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 +48 -0
- package/dist/types/cli/args.d.ts +1 -0
- package/dist/types/cli/notify-cli.d.ts +5 -0
- package/dist/types/cli.d.ts +3 -0
- package/dist/types/commands/launch.d.ts +3 -0
- package/dist/types/config/model-profile-activation.d.ts +7 -7
- package/dist/types/config/model-registry.d.ts +7 -3
- package/dist/types/config/model-resolver.d.ts +2 -2
- package/dist/types/config/settings-schema.d.ts +15 -1
- package/dist/types/config/settings.d.ts +7 -0
- package/dist/types/extensibility/runtime-skill-discovery.d.ts +20 -0
- package/dist/types/gjc-runtime/team-runtime.d.ts +9 -0
- package/dist/types/gjc-runtime/tmux-common.d.ts +1 -0
- package/dist/types/modes/components/settings-selector.d.ts +2 -2
- package/dist/types/modes/components/thinking-selector.d.ts +7 -2
- package/dist/types/modes/controllers/selector-controller.d.ts +1 -0
- package/dist/types/modes/interactive-mode.d.ts +2 -0
- package/dist/types/modes/rpc/rpc-mode.d.ts +1 -3
- package/dist/types/modes/rpc/rpc-socket-security.d.ts +3 -0
- package/dist/types/modes/types.d.ts +3 -0
- package/dist/types/modes/utils/injected-user-submission.d.ts +52 -0
- package/dist/types/notifications/config-commands.d.ts +38 -0
- package/dist/types/notifications/config.d.ts +16 -0
- package/dist/types/notifications/index.d.ts +14 -1
- package/dist/types/notifications/lifecycle-commands.d.ts +1 -0
- package/dist/types/notifications/lifecycle-control-runtime.d.ts +2 -1
- package/dist/types/notifications/reply-sent-store.d.ts +53 -0
- package/dist/types/notifications/rich-draft.d.ts +68 -0
- package/dist/types/notifications/rich-render.d.ts +90 -0
- package/dist/types/notifications/telegram-daemon-cli.d.ts +3 -3
- package/dist/types/notifications/telegram-daemon.d.ts +22 -0
- package/dist/types/notifications/telegram-reference.d.ts +7 -0
- package/dist/types/notifications/threaded-render.d.ts +10 -0
- package/dist/types/notifications/topic-registry.d.ts +4 -5
- package/dist/types/runtime-credential-selector.d.ts +7 -0
- package/dist/types/sdk.d.ts +7 -1
- package/dist/types/session/agent-session.d.ts +6 -3
- package/dist/types/tools/index.d.ts +1 -0
- package/dist/types/tools/skill-discovery.d.ts +40 -0
- package/dist/types/utils/pasted-image-path.d.ts +30 -0
- package/package.json +8 -11
- package/src/cli/args.ts +8 -0
- package/src/cli/notify-cli.ts +38 -10
- package/src/cli.ts +5 -0
- package/src/commands/launch.ts +5 -0
- package/src/commands/notify.ts +2 -2
- package/src/config/keybindings.ts +1 -1
- package/src/config/model-profile-activation.ts +23 -7
- package/src/config/model-registry.ts +21 -4
- package/src/config/model-resolver.ts +5 -16
- package/src/config/settings-schema.ts +11 -2
- package/src/config/settings.ts +24 -1
- package/src/extensibility/runtime-skill-discovery.ts +229 -0
- package/src/gjc-runtime/launch-tmux.ts +78 -12
- package/src/gjc-runtime/psmux-detect.ts +17 -2
- package/src/gjc-runtime/team-runtime.ts +46 -7
- package/src/gjc-runtime/tmux-common.ts +1 -0
- package/src/gjc-runtime/ultragoal-runtime.ts +10 -1
- package/src/internal-urls/docs-index.generated.ts +6 -6
- package/src/lsp/client.ts +1 -0
- package/src/main.ts +26 -3
- package/src/modes/components/footer.ts +7 -2
- package/src/modes/components/settings-selector.ts +3 -3
- package/src/modes/components/thinking-selector.ts +83 -20
- package/src/modes/controllers/event-controller.ts +11 -2
- package/src/modes/controllers/extension-ui-controller.ts +8 -1
- package/src/modes/controllers/input-controller.ts +62 -26
- package/src/modes/controllers/selector-controller.ts +43 -0
- package/src/modes/interactive-mode.ts +5 -0
- package/src/modes/rpc/rpc-mode.ts +3 -9
- package/src/modes/rpc/rpc-socket-security.ts +13 -1
- package/src/modes/types.ts +3 -0
- package/src/modes/utils/injected-user-submission.ts +94 -0
- package/src/modes/utils/ui-helpers.ts +48 -15
- package/src/notifications/config-commands.ts +90 -0
- package/src/notifications/config.ts +27 -0
- package/src/notifications/index.ts +184 -14
- package/src/notifications/lifecycle-commands.ts +37 -16
- package/src/notifications/lifecycle-control-runtime.ts +8 -3
- package/src/notifications/reply-sent-store.ts +134 -0
- package/src/notifications/rich-draft.ts +107 -0
- package/src/notifications/rich-render.ts +143 -0
- package/src/notifications/telegram-daemon-cli.ts +60 -5
- package/src/notifications/telegram-daemon.ts +431 -91
- package/src/notifications/telegram-reference.ts +17 -0
- package/src/notifications/threaded-render.ts +42 -1
- package/src/notifications/topic-registry.ts +9 -8
- package/src/prompts/tools/search-tool-bm25.md +5 -0
- package/src/prompts/tools/skill-discovery.md +13 -0
- package/src/runtime-credential-selector.ts +35 -0
- package/src/sdk.ts +104 -13
- package/src/session/agent-session.ts +48 -22
- package/src/slash-commands/builtin-registry.ts +78 -1
- package/src/system-prompt.ts +4 -6
- package/src/tools/index.ts +4 -0
- package/src/tools/skill-discovery.ts +73 -0
- package/src/tools/skill.ts +15 -2
- package/src/utils/pasted-image-path.ts +170 -0
package/src/cli/notify-cli.ts
CHANGED
|
@@ -31,6 +31,9 @@ export interface NotifyCommandDeps {
|
|
|
31
31
|
setupRedact?: boolean;
|
|
32
32
|
setupInteractive?: boolean;
|
|
33
33
|
threadedModePrompt?: (message: string) => Promise<string>;
|
|
34
|
+
tokenPrompt?: () => Promise<string>;
|
|
35
|
+
setExitCode?: (code: number) => void;
|
|
36
|
+
exitProcess?: (code: number) => void;
|
|
34
37
|
}
|
|
35
38
|
|
|
36
39
|
interface TelegramApiResponse<T> {
|
|
@@ -126,6 +129,27 @@ export async function runNotifyCommand(cmd: NotifyCommandArgs, deps: NotifyComma
|
|
|
126
129
|
}
|
|
127
130
|
}
|
|
128
131
|
|
|
132
|
+
export async function runNotifyCliCommand(cmd: NotifyCommandArgs, deps: NotifyCommandDeps = {}): Promise<void> {
|
|
133
|
+
try {
|
|
134
|
+
await runNotifyCommand(cmd, deps);
|
|
135
|
+
} catch (error) {
|
|
136
|
+
if (cmd.action !== "setup" || !(error instanceof Error)) {
|
|
137
|
+
throw error;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const cancelled = error.message === "Telegram bot token prompt cancelled.";
|
|
141
|
+
process.stderr.write(cancelled ? "Telegram notify setup cancelled.\n" : `Error: ${error.message}\n`);
|
|
142
|
+
const code = cancelled ? 130 : 1;
|
|
143
|
+
if (deps.setExitCode) {
|
|
144
|
+
deps.setExitCode(code);
|
|
145
|
+
} else {
|
|
146
|
+
process.exitCode = code;
|
|
147
|
+
}
|
|
148
|
+
const exitProcess = deps.exitProcess ?? (deps.setExitCode ? undefined : process.exit);
|
|
149
|
+
exitProcess?.(code);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
129
153
|
async function getSettings(deps: NotifyCommandDeps): Promise<Settings> {
|
|
130
154
|
if (deps.settings) return deps.settings;
|
|
131
155
|
const { Settings } = await import("../config/settings");
|
|
@@ -136,7 +160,7 @@ async function runSetup(deps: NotifyCommandDeps): Promise<void> {
|
|
|
136
160
|
const settings = await getSettings(deps);
|
|
137
161
|
const fetchImpl = deps.fetchImpl ?? globalThis.fetch;
|
|
138
162
|
const apiBase = deps.apiBase ?? DEFAULT_API_BASE;
|
|
139
|
-
const token = deps.setupToken ?? (await promptForToken());
|
|
163
|
+
const token = deps.setupToken ?? (await (deps.tokenPrompt ?? promptForToken)());
|
|
140
164
|
if (!token.trim()) {
|
|
141
165
|
throw new Error("Telegram bot token is required.");
|
|
142
166
|
}
|
|
@@ -179,6 +203,7 @@ async function runSetup(deps: NotifyCommandDeps): Promise<void> {
|
|
|
179
203
|
type TokenPromptInput = NodeJS.ReadStream & {
|
|
180
204
|
isRaw?: boolean;
|
|
181
205
|
setRawMode?: (mode: boolean) => unknown;
|
|
206
|
+
pause?: () => unknown;
|
|
182
207
|
};
|
|
183
208
|
|
|
184
209
|
type TokenPromptOutput = Pick<NodeJS.WriteStream, "write">;
|
|
@@ -206,6 +231,7 @@ export async function promptForToken(
|
|
|
206
231
|
input.off("data", onData);
|
|
207
232
|
input.off("error", onError);
|
|
208
233
|
input.setRawMode?.(wasRaw);
|
|
234
|
+
input.pause?.();
|
|
209
235
|
output.write("\n");
|
|
210
236
|
};
|
|
211
237
|
|
|
@@ -249,18 +275,18 @@ export async function promptForToken(
|
|
|
249
275
|
}
|
|
250
276
|
|
|
251
277
|
const THREADED_ENABLED_SUCCESS =
|
|
252
|
-
"Telegram Threaded Mode capability verified for this bot. GJC will request a private-chat topic per session; if Telegram ever refuses topic creation, notifications fall back to this flat chat with a one-time nudge.\n";
|
|
278
|
+
"Telegram Threaded Mode capability verified for this bot. GJC will request a private-chat topic per session; if Telegram ever refuses topic creation, notifications fall back to this flat chat with inline ask buttons only and a one-time Threaded Mode nudge.\n";
|
|
253
279
|
|
|
254
280
|
const THREADED_MISSING_WARNING =
|
|
255
|
-
"Warning: Telegram getMe did not include has_topics_enabled, so GJC cannot verify private-chat Threaded Mode capability for this bot. Setup will continue;
|
|
281
|
+
"Warning: Telegram getMe did not include has_topics_enabled, so GJC cannot verify private-chat Threaded Mode capability for this bot. Setup will continue; flat private-chat fallback supports outbound notifications and inline ask buttons only. Free-text replies and session commands require Threaded Mode/topic routing.\n";
|
|
256
282
|
|
|
257
283
|
const THREADED_NONINTERACTIVE_WARNING =
|
|
258
|
-
"Warning: Telegram Threaded Mode capability is OFF for this bot. Setup will be saved because this run is non-interactive
|
|
284
|
+
"Warning: Telegram Threaded Mode capability is OFF for this bot. Setup will be saved because this run is non-interactive. Flat private-chat fallback supports outbound notifications and inline ask buttons only; free-text replies and session commands require enabling Threaded Mode in @BotFather > Bot Settings > Threads Settings.\n";
|
|
259
285
|
|
|
260
286
|
const THREADED_DISABLED_GUIDANCE =
|
|
261
287
|
"Telegram Threaded Mode is OFF for this bot. GJC needs Telegram private-chat topics so each session can use its own thread.\n" +
|
|
262
|
-
"GJC cannot enable this through the Bot API. Open @BotFather
|
|
263
|
-
"
|
|
288
|
+
"GJC cannot enable this through the Bot API. Open @BotFather > Bot Settings > Threads Settings for this bot, enable Threaded Mode / forum topics for private chats, then return here.\n" +
|
|
289
|
+
"Without Threaded Mode, flat private-chat fallback supports outbound notifications and inline ask buttons only; free-text replies and session commands require topic routing.\n";
|
|
264
290
|
|
|
265
291
|
const THREADED_DISABLED_PROMPT =
|
|
266
292
|
"Press Enter after enabling Threaded Mode, or type skip to finish setup with a warning: ";
|
|
@@ -270,7 +296,7 @@ const THREADED_STILL_OFF = "Telegram still reports Threaded Mode OFF for this bo
|
|
|
270
296
|
const THREADED_RETRY_PROMPT = "Press Enter to check again, or type skip to finish setup with a warning: ";
|
|
271
297
|
|
|
272
298
|
const THREADED_SKIP_WARNING =
|
|
273
|
-
"Warning: continuing without verified Telegram Threaded Mode capability. Setup will be saved
|
|
299
|
+
"Warning: continuing without verified Telegram Threaded Mode capability. Setup will be saved. Flat private-chat fallback supports outbound notifications and inline ask buttons only; free-text replies and session commands require enabling Threaded Mode in BotFather.\n";
|
|
274
300
|
|
|
275
301
|
const THREADED_INVALID_INPUT = "Type Enter to retry or skip to continue with a warning.\n";
|
|
276
302
|
|
|
@@ -500,8 +526,10 @@ ${chalk.bold("Examples:")}
|
|
|
500
526
|
|
|
501
527
|
${chalk.bold("Threaded Mode:")}
|
|
502
528
|
GJC uses Telegram private-chat topics for per-session threads. Setup verifies the bot
|
|
503
|
-
capability via getMe.has_topics_enabled.
|
|
504
|
-
bots cannot toggle it through the Bot API. If Telegram refuses topic
|
|
505
|
-
GJC delivers flat to the paired private chat
|
|
529
|
+
capability via getMe.has_topics_enabled. Enable Threaded Mode in @BotFather > Bot Settings
|
|
530
|
+
> Threads Settings; bots cannot toggle it through the Bot API. If Telegram refuses topic
|
|
531
|
+
creation at runtime, GJC delivers flat to the paired private chat with outbound notifications
|
|
532
|
+
and inline ask buttons only, then nudges you to enable Threaded Mode for free-text replies
|
|
533
|
+
and session commands.
|
|
506
534
|
`);
|
|
507
535
|
}
|
package/src/cli.ts
CHANGED
|
@@ -146,6 +146,10 @@ export class RootHelpCommand extends Command {
|
|
|
146
146
|
default: Flags.boolean({ description: "Persist --mpreset as the default model profile" }),
|
|
147
147
|
provider: Flags.string({ description: "Provider to use (legacy; prefer --model)" }),
|
|
148
148
|
"api-key": Flags.string({ description: "API key (defaults to env vars)" }),
|
|
149
|
+
credential: Flags.string({
|
|
150
|
+
description:
|
|
151
|
+
"Stored credential selector: email:<addr>, id:<n>, account:<id>, project:<id>, or provider/email:<addr>",
|
|
152
|
+
}),
|
|
149
153
|
"system-prompt": Flags.string({ description: "System prompt (default: coding assistant prompt)" }),
|
|
150
154
|
"append-system-prompt": Flags.string({ description: "Append text or file contents to the system prompt" }),
|
|
151
155
|
"allow-home": Flags.boolean({ description: "Allow starting in ~ without auto-switching to a temp dir" }),
|
|
@@ -191,6 +195,7 @@ export class RootHelpCommand extends Command {
|
|
|
191
195
|
`# Launch in a sibling git worktree\n ${APP_NAME} --worktree`,
|
|
192
196
|
`# Use different model (fuzzy matching)\n ${APP_NAME} --model opus "Help me refactor this code"`,
|
|
193
197
|
`# Limit model cycling to specific models\n ${APP_NAME} --models claude-sonnet,claude-haiku,gpt-4o`,
|
|
198
|
+
`# Pin a stored credential for this session\n ${APP_NAME} --credential email:me@example.com`,
|
|
194
199
|
`# Activate a model profile for this session\n ${APP_NAME} --mpreset codex-medium`,
|
|
195
200
|
`# Persist a model profile as the default\n ${APP_NAME} --mpreset opencodego --default`,
|
|
196
201
|
`# Export a session file to HTML\n ${APP_NAME} --export ~/.gjc/agent/sessions/--path--/session.jsonl`,
|
package/src/commands/launch.ts
CHANGED
|
@@ -87,6 +87,10 @@ export default class Index extends Command {
|
|
|
87
87
|
"api-key": Flags.string({
|
|
88
88
|
description: "API key (defaults to env vars)",
|
|
89
89
|
}),
|
|
90
|
+
credential: Flags.string({
|
|
91
|
+
description:
|
|
92
|
+
"Stored credential selector: email:<addr>, id:<n>, account:<id>, project:<id>, or provider/email:<addr>",
|
|
93
|
+
}),
|
|
90
94
|
"system-prompt": Flags.string({
|
|
91
95
|
description: "System prompt (default: coding assistant prompt)",
|
|
92
96
|
}),
|
|
@@ -181,6 +185,7 @@ export default class Index extends Command {
|
|
|
181
185
|
`# Launch in a sibling git worktree\n ${APP_NAME} --worktree`,
|
|
182
186
|
`# Use different model (fuzzy matching)\n ${APP_NAME} --model opus "Help me refactor this code"`,
|
|
183
187
|
`# Limit model cycling to specific models\n ${APP_NAME} --models claude-sonnet,claude-haiku,gpt-4o`,
|
|
188
|
+
`# Pin a stored credential for this session\n ${APP_NAME} --credential email:me@example.com`,
|
|
184
189
|
`# Activate a model profile for this session\n ${APP_NAME} --mpreset codex-medium`,
|
|
185
190
|
`# Persist a model profile as the default\n ${APP_NAME} --mpreset opencodego --default`,
|
|
186
191
|
`# Export a session file to HTML\n ${APP_NAME} --export ~/.gjc/agent/sessions/--path--/session.jsonl`,
|
package/src/commands/notify.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Configure Telegram notifications.
|
|
3
3
|
*/
|
|
4
4
|
import { Args, Command, Flags } from "@gajae-code/utils/cli";
|
|
5
|
-
import { type NotifyAction, type NotifyCommandArgs,
|
|
5
|
+
import { type NotifyAction, type NotifyCommandArgs, runNotifyCliCommand } from "../cli/notify-cli";
|
|
6
6
|
import { initTheme } from "../modes/theme/theme";
|
|
7
7
|
|
|
8
8
|
const ACTIONS: NotifyAction[] = ["setup", "status", "daemon-internal"];
|
|
@@ -56,6 +56,6 @@ export default class Notify extends Command {
|
|
|
56
56
|
};
|
|
57
57
|
|
|
58
58
|
if (action !== "daemon-internal") await initTheme();
|
|
59
|
-
await
|
|
59
|
+
await runNotifyCliCommand(cmd);
|
|
60
60
|
}
|
|
61
61
|
}
|
|
@@ -61,7 +61,7 @@ declare module "@gajae-code/tui" {
|
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
export function defaultMessageQueueKeysForPlatform(platform: NodeJS.Platform = process.platform): KeyId {
|
|
64
|
-
return platform === "win32" ? "alt+q" : "alt+enter";
|
|
64
|
+
return platform === "win32" || platform === "darwin" ? "alt+q" : "alt+enter";
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { ThinkingLevel } from "@gajae-code/agent-core";
|
|
2
2
|
import type { Api, Model } from "@gajae-code/ai";
|
|
3
3
|
import type { AgentSession } from "../session/agent-session";
|
|
4
4
|
import { formatClampedModelSelector } from "../thinking";
|
|
@@ -43,7 +43,10 @@ export interface PrepareModelProfileActivationOptions {
|
|
|
43
43
|
settings: Pick<Settings, "get">;
|
|
44
44
|
profileName: string;
|
|
45
45
|
}
|
|
46
|
-
|
|
46
|
+
export interface ApplyModelProfileActivationOptions {
|
|
47
|
+
persistDefault?: boolean;
|
|
48
|
+
thinkingLevelOverride?: ThinkingLevel;
|
|
49
|
+
}
|
|
47
50
|
export interface PreparedModelProfileActivation {
|
|
48
51
|
profileName: string;
|
|
49
52
|
session: ModelProfileActivationSession & { setModelTemporary: AgentSession["setModelTemporary"] };
|
|
@@ -345,25 +348,31 @@ export async function prepareModelProfileActivation(
|
|
|
345
348
|
|
|
346
349
|
export async function applyPreparedModelProfileActivation(
|
|
347
350
|
prepared: PreparedModelProfileActivation,
|
|
348
|
-
options:
|
|
351
|
+
options: ApplyModelProfileActivationOptions = {},
|
|
349
352
|
): Promise<void> {
|
|
350
353
|
const previousModel = prepared.previousModel;
|
|
351
354
|
const previousThinkingLevel = prepared.previousThinkingLevel;
|
|
352
355
|
const previousAgentModelOverrides = prepared.previousAgentModelOverrides;
|
|
353
356
|
const previousModelRoles = prepared.previousModelRoles;
|
|
354
357
|
const previousPersistedDefault = prepared.settings.get("modelProfile.default");
|
|
358
|
+
const previousDefaultThinkingLevel = prepared.settings.get("defaultThinkingLevel");
|
|
355
359
|
const previousActiveModelProfile = prepared.previousActiveModelProfile;
|
|
356
360
|
const previousSessionDefaultModel = prepared.previousSessionDefaultModel;
|
|
357
361
|
let modelChanged = false;
|
|
358
362
|
let overridesChanged = false;
|
|
359
363
|
let defaultChanged = false;
|
|
360
364
|
let modelRolesChanged = false;
|
|
365
|
+
let defaultThinkingChanged = false;
|
|
361
366
|
|
|
362
367
|
try {
|
|
363
368
|
if (prepared.defaultModel) {
|
|
364
|
-
await prepared.session.setModelTemporary(
|
|
365
|
-
|
|
366
|
-
|
|
369
|
+
await prepared.session.setModelTemporary(
|
|
370
|
+
prepared.defaultModel,
|
|
371
|
+
options.thinkingLevelOverride ?? prepared.defaultThinkingLevel,
|
|
372
|
+
{
|
|
373
|
+
persistAsSessionDefault: true,
|
|
374
|
+
},
|
|
375
|
+
);
|
|
367
376
|
modelChanged = true;
|
|
368
377
|
}
|
|
369
378
|
if (Object.keys(prepared.modelRoles).length > 0) {
|
|
@@ -380,6 +389,10 @@ export async function applyPreparedModelProfileActivation(
|
|
|
380
389
|
if (options.persistDefault) {
|
|
381
390
|
prepared.settings.set("modelRoles", {});
|
|
382
391
|
prepared.settings.set("task.agentModelOverrides", {});
|
|
392
|
+
if (prepared.defaultThinkingLevel !== undefined && prepared.defaultThinkingLevel !== ThinkingLevel.Inherit) {
|
|
393
|
+
prepared.settings.set("defaultThinkingLevel", prepared.defaultThinkingLevel);
|
|
394
|
+
defaultThinkingChanged = true;
|
|
395
|
+
}
|
|
383
396
|
prepared.settings.set("modelProfile.default", prepared.profileName);
|
|
384
397
|
defaultChanged = true;
|
|
385
398
|
await prepared.settings.flush();
|
|
@@ -390,6 +403,9 @@ export async function applyPreparedModelProfileActivation(
|
|
|
390
403
|
prepared.settings.set("modelProfile.default", previousPersistedDefault);
|
|
391
404
|
prepared.settings.set("modelRoles", previousModelRoles);
|
|
392
405
|
prepared.settings.set("task.agentModelOverrides", previousAgentModelOverrides);
|
|
406
|
+
if (defaultThinkingChanged) {
|
|
407
|
+
prepared.settings.set("defaultThinkingLevel", previousDefaultThinkingLevel);
|
|
408
|
+
}
|
|
393
409
|
}
|
|
394
410
|
if (modelRolesChanged) {
|
|
395
411
|
prepared.settings.override("modelRoles", previousModelRoles);
|
|
@@ -505,7 +521,7 @@ export async function restoreMaterializedModelProfileForDeletion(options: {
|
|
|
505
521
|
|
|
506
522
|
export async function activateModelProfile(
|
|
507
523
|
options: PrepareModelProfileActivationOptions,
|
|
508
|
-
applyOptions:
|
|
524
|
+
applyOptions: ApplyModelProfileActivationOptions = {},
|
|
509
525
|
): Promise<void> {
|
|
510
526
|
const prepared = await prepareModelProfileActivation(options);
|
|
511
527
|
await applyPreparedModelProfileActivation(prepared, applyOptions);
|
|
@@ -3,6 +3,7 @@ import * as path from "node:path";
|
|
|
3
3
|
import {
|
|
4
4
|
type Api,
|
|
5
5
|
type AssistantMessageEventStream,
|
|
6
|
+
type AuthCredentialSelector,
|
|
6
7
|
type CacheRetention,
|
|
7
8
|
type Context,
|
|
8
9
|
createModelManager,
|
|
@@ -2667,21 +2668,37 @@ export class ModelRegistry {
|
|
|
2667
2668
|
/**
|
|
2668
2669
|
* Get API key for a model.
|
|
2669
2670
|
*/
|
|
2670
|
-
async getApiKey(
|
|
2671
|
+
async getApiKey(
|
|
2672
|
+
model: Model<Api>,
|
|
2673
|
+
sessionId?: string,
|
|
2674
|
+
options: { credentialSelector?: AuthCredentialSelector } = {},
|
|
2675
|
+
): Promise<string | undefined> {
|
|
2671
2676
|
if (this.#keylessProviders.has(model.provider) && !this.authStorage.hasAuth(model.provider)) {
|
|
2672
2677
|
return kNoAuth;
|
|
2673
2678
|
}
|
|
2674
|
-
return this.authStorage.getApiKey(model.provider, sessionId, {
|
|
2679
|
+
return this.authStorage.getApiKey(model.provider, sessionId, {
|
|
2680
|
+
baseUrl: model.baseUrl,
|
|
2681
|
+
modelId: model.id,
|
|
2682
|
+
credentialSelector: options.credentialSelector,
|
|
2683
|
+
});
|
|
2675
2684
|
}
|
|
2676
2685
|
|
|
2677
2686
|
/**
|
|
2678
2687
|
* Get API key for a provider (e.g., "openai").
|
|
2679
2688
|
*/
|
|
2680
|
-
async getApiKeyForProvider(
|
|
2689
|
+
async getApiKeyForProvider(
|
|
2690
|
+
provider: string,
|
|
2691
|
+
sessionId?: string,
|
|
2692
|
+
baseUrl?: string,
|
|
2693
|
+
options: { credentialSelector?: AuthCredentialSelector } = {},
|
|
2694
|
+
): Promise<string | undefined> {
|
|
2681
2695
|
if (this.#keylessProviders.has(provider) && !this.authStorage.hasAuth(provider)) {
|
|
2682
2696
|
return kNoAuth;
|
|
2683
2697
|
}
|
|
2684
|
-
return this.authStorage.getApiKey(provider, sessionId, {
|
|
2698
|
+
return this.authStorage.getApiKey(provider, sessionId, {
|
|
2699
|
+
baseUrl,
|
|
2700
|
+
credentialSelector: options.credentialSelector,
|
|
2701
|
+
});
|
|
2685
2702
|
}
|
|
2686
2703
|
|
|
2687
2704
|
async #peekApiKeyForProvider(provider: string): Promise<string | undefined> {
|
|
@@ -3,15 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { ThinkingLevel } from "@gajae-code/agent-core";
|
|
6
|
-
import {
|
|
7
|
-
type Api,
|
|
8
|
-
clampThinkingLevelForModel,
|
|
9
|
-
DEFAULT_MODEL_PER_PROVIDER,
|
|
10
|
-
type Effort,
|
|
11
|
-
type KnownProvider,
|
|
12
|
-
type Model,
|
|
13
|
-
modelsAreEqual,
|
|
14
|
-
} from "@gajae-code/ai";
|
|
6
|
+
import { type Api, DEFAULT_MODEL_PER_PROVIDER, type KnownProvider, type Model, modelsAreEqual } from "@gajae-code/ai";
|
|
15
7
|
import { fuzzyMatch } from "@gajae-code/tui";
|
|
16
8
|
import { logger } from "@gajae-code/utils";
|
|
17
9
|
import chalk from "chalk";
|
|
@@ -1197,7 +1189,7 @@ export async function findInitialModel(options: {
|
|
|
1197
1189
|
isContinuing: boolean;
|
|
1198
1190
|
defaultProvider?: string;
|
|
1199
1191
|
defaultModelId?: string;
|
|
1200
|
-
defaultThinkingSelector?:
|
|
1192
|
+
defaultThinkingSelector?: ThinkingLevel;
|
|
1201
1193
|
modelRegistry: InitialModelRegistry;
|
|
1202
1194
|
}): Promise<InitialModelResult> {
|
|
1203
1195
|
const {
|
|
@@ -1212,7 +1204,7 @@ export async function findInitialModel(options: {
|
|
|
1212
1204
|
} = options;
|
|
1213
1205
|
|
|
1214
1206
|
let model: Model<Api> | undefined;
|
|
1215
|
-
let thinkingLevel:
|
|
1207
|
+
let thinkingLevel: ThinkingLevel | undefined;
|
|
1216
1208
|
|
|
1217
1209
|
// 1. CLI args take priority
|
|
1218
1210
|
if (cliProvider && cliModel) {
|
|
@@ -1233,10 +1225,7 @@ export async function findInitialModel(options: {
|
|
|
1233
1225
|
: (scoped.thinkingLevel ?? defaultThinkingSelector);
|
|
1234
1226
|
return {
|
|
1235
1227
|
model: scoped.model,
|
|
1236
|
-
thinkingLevel:
|
|
1237
|
-
scopedThinkingSelector === ThinkingLevel.Off
|
|
1238
|
-
? ThinkingLevel.Off
|
|
1239
|
-
: clampThinkingLevelForModel(scoped.model, scopedThinkingSelector),
|
|
1228
|
+
thinkingLevel: resolveThinkingLevelForModel(scoped.model, scopedThinkingSelector),
|
|
1240
1229
|
fallbackMessage: undefined,
|
|
1241
1230
|
};
|
|
1242
1231
|
}
|
|
@@ -1246,7 +1235,7 @@ export async function findInitialModel(options: {
|
|
|
1246
1235
|
const found = modelRegistry.find(defaultProvider, defaultModelId);
|
|
1247
1236
|
if (found) {
|
|
1248
1237
|
model = found;
|
|
1249
|
-
thinkingLevel =
|
|
1238
|
+
thinkingLevel = resolveThinkingLevelForModel(found, defaultThinkingSelector);
|
|
1250
1239
|
return { model, thinkingLevel, fallbackMessage: undefined };
|
|
1251
1240
|
}
|
|
1252
1241
|
}
|
|
@@ -5,6 +5,7 @@ import { EDIT_MODES } from "../utils/edit-mode";
|
|
|
5
5
|
import { CONFIGURABLE_SEARCH_PROVIDER_IDS } from "../web/search/types";
|
|
6
6
|
|
|
7
7
|
const THINKING_EFFORTS = ["minimal", "low", "medium", "high", "xhigh", "max"] as readonly Effort[];
|
|
8
|
+
const DEFAULT_THINKING_LEVELS = ["off", ...THINKING_EFFORTS] as const;
|
|
8
9
|
|
|
9
10
|
import {
|
|
10
11
|
DEFAULT_DISABLED_EXTENSIONS,
|
|
@@ -258,6 +259,8 @@ export const SETTINGS_SCHEMA = {
|
|
|
258
259
|
"notifications.enabled": { type: "boolean", default: false },
|
|
259
260
|
"notifications.telegram.botToken": { type: "string", default: undefined },
|
|
260
261
|
"notifications.telegram.chatId": { type: "string", default: undefined },
|
|
262
|
+
"notifications.telegram.rich.enabled": { type: "boolean", default: true },
|
|
263
|
+
"notifications.telegram.richDraft.enabled": { type: "boolean", default: false },
|
|
261
264
|
"notifications.discord.botToken": { type: "string", default: undefined },
|
|
262
265
|
"notifications.discord.channelId": { type: "string", default: undefined },
|
|
263
266
|
"notifications.slack.botToken": { type: "string", default: undefined },
|
|
@@ -802,13 +805,13 @@ export const SETTINGS_SCHEMA = {
|
|
|
802
805
|
// Reasoning and prompts
|
|
803
806
|
defaultThinkingLevel: {
|
|
804
807
|
type: "enum",
|
|
805
|
-
values:
|
|
808
|
+
values: DEFAULT_THINKING_LEVELS,
|
|
806
809
|
default: THINKING_EFFORTS[3],
|
|
807
810
|
ui: {
|
|
808
811
|
tab: "model",
|
|
809
812
|
label: "Thinking Level",
|
|
810
813
|
description: "Reasoning depth for thinking-capable models",
|
|
811
|
-
options: [...
|
|
814
|
+
options: [...DEFAULT_THINKING_LEVELS.map(getThinkingLevelMetadata)],
|
|
812
815
|
},
|
|
813
816
|
},
|
|
814
817
|
|
|
@@ -3408,6 +3411,12 @@ export interface NotificationsSettings {
|
|
|
3408
3411
|
telegram: {
|
|
3409
3412
|
botToken: string | undefined;
|
|
3410
3413
|
chatId: string | undefined;
|
|
3414
|
+
rich: {
|
|
3415
|
+
enabled: boolean;
|
|
3416
|
+
};
|
|
3417
|
+
richDraft: {
|
|
3418
|
+
enabled: boolean;
|
|
3419
|
+
};
|
|
3411
3420
|
};
|
|
3412
3421
|
discord: {
|
|
3413
3422
|
botToken: string | undefined;
|
package/src/config/settings.ts
CHANGED
|
@@ -374,6 +374,25 @@ export class Settings {
|
|
|
374
374
|
}
|
|
375
375
|
}
|
|
376
376
|
|
|
377
|
+
/**
|
|
378
|
+
* Like {@link flush}, but rejects if the durable save fails instead of
|
|
379
|
+
* swallowing the error. Use where the caller must confirm persistence before
|
|
380
|
+
* reporting success (e.g. the Telegram `/rich` toggle). In-memory instances
|
|
381
|
+
* ({@link isolated}) short-circuit in {@link #saveNow} and never throw.
|
|
382
|
+
*/
|
|
383
|
+
async flushOrThrow(): Promise<void> {
|
|
384
|
+
if (this.#saveTimer) {
|
|
385
|
+
clearTimeout(this.#saveTimer);
|
|
386
|
+
this.#saveTimer = undefined;
|
|
387
|
+
}
|
|
388
|
+
if (this.#savePromise) {
|
|
389
|
+
await this.#savePromise;
|
|
390
|
+
}
|
|
391
|
+
if (this.#modified.size > 0) {
|
|
392
|
+
await this.#saveNow({ throwOnError: true });
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
377
396
|
async cloneForCwd(cwd: string): Promise<Settings> {
|
|
378
397
|
const cloned = new Settings({
|
|
379
398
|
cwd,
|
|
@@ -817,7 +836,7 @@ export class Settings {
|
|
|
817
836
|
}, 100);
|
|
818
837
|
}
|
|
819
838
|
|
|
820
|
-
async #saveNow(): Promise<void> {
|
|
839
|
+
async #saveNow(options: { throwOnError?: boolean } = {}): Promise<void> {
|
|
821
840
|
if (!this.#persist || !this.#configPath || this.#modified.size === 0) return;
|
|
822
841
|
|
|
823
842
|
const configPath = this.#configPath;
|
|
@@ -846,6 +865,10 @@ export class Settings {
|
|
|
846
865
|
for (const p of modifiedPaths) {
|
|
847
866
|
this.#modified.add(p);
|
|
848
867
|
}
|
|
868
|
+
if (options.throwOnError) {
|
|
869
|
+
this.#rebuildMerged();
|
|
870
|
+
throw error;
|
|
871
|
+
}
|
|
849
872
|
}
|
|
850
873
|
|
|
851
874
|
this.#rebuildMerged();
|