@gajae-code/coding-agent 0.9.1 → 0.9.3
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 +36 -0
- package/dist/types/capability/mcp.d.ts +3 -1
- 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/commands/mcp.d.ts +1 -1
- 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 +1 -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/modes/components/settings-selector.d.ts +2 -2
- package/dist/types/modes/components/status-line/types.d.ts +6 -0
- package/dist/types/modes/components/status-line.d.ts +1 -0
- package/dist/types/modes/components/thinking-selector.d.ts +6 -1
- package/dist/types/modes/types.d.ts +1 -0
- package/dist/types/modes/utils/ui-helpers.d.ts +12 -0
- package/dist/types/notifications/config-commands.d.ts +29 -0
- package/dist/types/notifications/index.d.ts +12 -1
- package/dist/types/notifications/telegram-daemon-cli.d.ts +3 -3
- package/dist/types/notifications/threaded-render.d.ts +2 -0
- package/dist/types/notifications/topic-registry.d.ts +4 -5
- package/dist/types/runtime-credential-selector.d.ts +7 -0
- package/dist/types/runtime-mcp/config-writer.d.ts +4 -3
- package/dist/types/runtime-mcp/types.d.ts +5 -4
- package/dist/types/sdk.d.ts +7 -1
- package/dist/types/session/agent-session.d.ts +22 -3
- package/dist/types/session/session-manager.d.ts +1 -0
- package/dist/types/tools/index.d.ts +1 -0
- package/dist/types/tools/skill-discovery.d.ts +40 -0
- package/dist/types/tools/skill.d.ts +9 -8
- package/dist/types/utils/linkedom.d.ts +4 -0
- package/dist/types/utils/pasted-image-path.d.ts +5 -3
- package/package.json +11 -7
- package/scripts/build-binary.ts +2 -40
- package/scripts/compile-args.ts +91 -0
- package/scripts/generate-docs-index.ts +1 -0
- package/src/capability/mcp.ts +3 -1
- package/src/cli/args.ts +8 -0
- package/src/cli/mcp-cli.ts +108 -26
- package/src/cli/notify-cli.ts +27 -1
- package/src/cli.ts +26 -0
- package/src/commands/launch.ts +5 -0
- package/src/commands/mcp.ts +8 -8
- package/src/commands/notify.ts +2 -2
- package/src/commands/stats.ts +6 -2
- package/src/config/mcp-schema.json +21 -17
- 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 +3 -2
- package/src/config/settings.ts +24 -1
- package/src/coordinator-mcp/server.ts +7 -4
- package/src/discovery/builtin.ts +14 -0
- package/src/discovery/mcp-json.ts +14 -0
- package/src/extensibility/runtime-skill-discovery.ts +229 -0
- package/src/gjc-runtime/launch-tmux.ts +72 -12
- package/src/gjc-runtime/psmux-detect.ts +17 -2
- package/src/gjc-runtime/state-runtime.ts +105 -12
- package/src/gjc-runtime/team-runtime.ts +35 -3
- package/src/gjc-runtime/workflow-manifest.generated.json +4 -28
- package/src/gjc-runtime/workflow-manifest.ts +1 -7
- package/src/internal-urls/docs-index.generated.ts +3 -2
- package/src/internal-urls/gjc-protocol.ts +14 -1
- package/src/main.ts +26 -3
- package/src/modes/components/assistant-message.ts +1 -1
- package/src/modes/components/settings-selector.ts +3 -3
- package/src/modes/components/status-line/presets.ts +5 -7
- package/src/modes/components/status-line/segments.ts +14 -0
- package/src/modes/components/status-line/types.ts +6 -0
- package/src/modes/components/status-line.ts +7 -1
- package/src/modes/components/thinking-selector.ts +79 -18
- package/src/modes/components/tool-execution.ts +1 -1
- package/src/modes/controllers/event-controller.ts +9 -9
- package/src/modes/controllers/input-controller.ts +52 -11
- package/src/modes/controllers/selector-controller.ts +7 -5
- package/src/modes/interactive-mode.ts +12 -4
- package/src/modes/types.ts +1 -0
- package/src/modes/utils/ui-helpers.ts +202 -47
- package/src/notifications/config-commands.ts +70 -0
- package/src/notifications/index.ts +156 -9
- package/src/notifications/rich-render.ts +1 -0
- package/src/notifications/telegram-daemon-cli.ts +13 -5
- package/src/notifications/telegram-daemon.ts +87 -4
- package/src/notifications/threaded-render.ts +14 -0
- package/src/notifications/topic-registry.ts +9 -8
- package/src/prompts/tools/skill-discovery.md +13 -0
- package/src/prompts/tools/skill.md +2 -2
- package/src/runtime-credential-selector.ts +35 -0
- package/src/runtime-mcp/config-writer.ts +4 -3
- package/src/runtime-mcp/config.ts +1 -0
- package/src/runtime-mcp/types.ts +5 -4
- package/src/sdk.ts +68 -5
- package/src/session/agent-session.ts +159 -21
- package/src/session/session-manager.ts +68 -40
- package/src/system-prompt.ts +4 -6
- package/src/tools/bash-interactive.ts +9 -4
- package/src/tools/browser/readable.ts +2 -2
- package/src/tools/fetch.ts +5 -5
- package/src/tools/index.ts +4 -0
- package/src/tools/skill-discovery.ts +73 -0
- package/src/tools/skill.ts +29 -13
- package/src/utils/linkedom.ts +14 -0
- package/src/utils/pasted-image-path.ts +23 -9
- package/src/web/scrapers/arxiv.ts +2 -2
- package/src/web/scrapers/go-pkg.ts +2 -2
- package/src/web/scrapers/iacr.ts +2 -2
- package/src/web/scrapers/readthedocs.ts +2 -2
- package/src/web/scrapers/twitter.ts +3 -2
- package/src/web/scrapers/wikipedia.ts +2 -2
- package/src/export/html/template.css +0 -1060
- package/src/export/html/template.html +0 -47
- package/src/export/html/template.js +0 -2314
- package/vendor/insane-search/engine/tests/test_hardening.py +0 -57
- package/vendor/insane-search/engine/tests/test_smoke.py +0 -152
- package/vendor/insane-search/engine/tests/test_u1.py +0 -200
- package/vendor/insane-search/engine/tests/test_u4.py +0 -131
- package/vendor/insane-search/engine/tests/test_u5.py +0 -163
- package/vendor/insane-search/engine/tests/test_u7.py +0 -124
- package/vendor/insane-search/engine/tests/test_u8.py +0 -216
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
|
|
package/src/cli.ts
CHANGED
|
@@ -87,6 +87,23 @@ async function installRuntimeGlobals(): Promise<void> {
|
|
|
87
87
|
delete process.env.MallocStackLoggingNoCompact;
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
+
function isStatsHelpFastPath(argv: string[]): boolean {
|
|
91
|
+
return argv[0] === "stats" && (argv.includes("--help") || argv.includes("-h"));
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function showStatsFastHelp(): void {
|
|
95
|
+
process.stdout.write(`Usage: ${APP_NAME} stats [options]
|
|
96
|
+
|
|
97
|
+
View usage statistics
|
|
98
|
+
|
|
99
|
+
Options:
|
|
100
|
+
-p, --port <number> Port for the dashboard server (default: 3847)
|
|
101
|
+
-j, --json Output stats as JSON
|
|
102
|
+
-s, --summary Print summary to console
|
|
103
|
+
-h, --help Show this help
|
|
104
|
+
`);
|
|
105
|
+
}
|
|
106
|
+
|
|
90
107
|
function isNotifyDaemonInternalFastPath(argv: string[]): boolean {
|
|
91
108
|
return argv[0] === "notify" && argv[1] === "daemon-internal";
|
|
92
109
|
}
|
|
@@ -146,6 +163,10 @@ export class RootHelpCommand extends Command {
|
|
|
146
163
|
default: Flags.boolean({ description: "Persist --mpreset as the default model profile" }),
|
|
147
164
|
provider: Flags.string({ description: "Provider to use (legacy; prefer --model)" }),
|
|
148
165
|
"api-key": Flags.string({ description: "API key (defaults to env vars)" }),
|
|
166
|
+
credential: Flags.string({
|
|
167
|
+
description:
|
|
168
|
+
"Stored credential selector: email:<addr>, id:<n>, account:<id>, project:<id>, or provider/email:<addr>",
|
|
169
|
+
}),
|
|
149
170
|
"system-prompt": Flags.string({ description: "System prompt (default: coding assistant prompt)" }),
|
|
150
171
|
"append-system-prompt": Flags.string({ description: "Append text or file contents to the system prompt" }),
|
|
151
172
|
"allow-home": Flags.boolean({ description: "Allow starting in ~ without auto-switching to a temp dir" }),
|
|
@@ -191,6 +212,7 @@ export class RootHelpCommand extends Command {
|
|
|
191
212
|
`# Launch in a sibling git worktree\n ${APP_NAME} --worktree`,
|
|
192
213
|
`# Use different model (fuzzy matching)\n ${APP_NAME} --model opus "Help me refactor this code"`,
|
|
193
214
|
`# Limit model cycling to specific models\n ${APP_NAME} --models claude-sonnet,claude-haiku,gpt-4o`,
|
|
215
|
+
`# Pin a stored credential for this session\n ${APP_NAME} --credential email:me@example.com`,
|
|
194
216
|
`# Activate a model profile for this session\n ${APP_NAME} --mpreset codex-medium`,
|
|
195
217
|
`# Persist a model profile as the default\n ${APP_NAME} --mpreset opencodego --default`,
|
|
196
218
|
`# Export a session file to HTML\n ${APP_NAME} --export ~/.gjc/agent/sessions/--path--/session.jsonl`,
|
|
@@ -259,6 +281,10 @@ export async function runCli(argv: string[]): Promise<void> {
|
|
|
259
281
|
process.exitCode = await runFixtureReport(id);
|
|
260
282
|
return;
|
|
261
283
|
}
|
|
284
|
+
if (isStatsHelpFastPath(argv)) {
|
|
285
|
+
showStatsFastHelp();
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
262
288
|
if (hasRootHelpFlag(argv)) {
|
|
263
289
|
const { renderRootHelp } = await import("@gajae-code/utils/cli");
|
|
264
290
|
const { getExtraHelpText } = await import("./cli/fast-help");
|
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/mcp.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Explicit MCP server config storage for standalone GJC.
|
|
3
3
|
*/
|
|
4
4
|
import { Args, Command, Flags } from "@gajae-code/utils/cli";
|
|
5
5
|
import { type MCPAction, type MCPCommandArgs, runMCPCommand } from "../cli/mcp-cli";
|
|
@@ -7,7 +7,7 @@ import { type MCPAction, type MCPCommandArgs, runMCPCommand } from "../cli/mcp-c
|
|
|
7
7
|
const ACTIONS: MCPAction[] = ["add", "list", "remove"];
|
|
8
8
|
|
|
9
9
|
export default class MCP extends Command {
|
|
10
|
-
static description = "
|
|
10
|
+
static description = "Store standalone MCP server definitions without loading them at runtime";
|
|
11
11
|
static delegateHelp = true;
|
|
12
12
|
|
|
13
13
|
static examples = [
|
|
@@ -81,15 +81,15 @@ export default class MCP extends Command {
|
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
private printHelp(): void {
|
|
84
|
-
process.stdout.write(`
|
|
84
|
+
process.stdout.write(`Store standalone MCP server definitions in GJC config without loading them at runtime
|
|
85
85
|
|
|
86
86
|
USAGE
|
|
87
87
|
$ gjc mcp [add|list|remove] [NAME] [COMMAND_OR_URL] [ARGS...] [FLAGS]
|
|
88
88
|
|
|
89
89
|
COMMANDS
|
|
90
|
-
add
|
|
91
|
-
list List
|
|
92
|
-
remove Remove a
|
|
90
|
+
add Store an explicit user-provided MCP server definition
|
|
91
|
+
list List stored servers with env/header/auth values redacted
|
|
92
|
+
remove Remove a stored server and print the removed definition redacted
|
|
93
93
|
|
|
94
94
|
FLAGS
|
|
95
95
|
--project Use project scope (./.gjc/mcp.json) instead of user scope
|
|
@@ -110,8 +110,8 @@ EXAMPLES
|
|
|
110
110
|
$ gjc mcp list --json
|
|
111
111
|
$ gjc mcp remove context7
|
|
112
112
|
|
|
113
|
-
|
|
114
|
-
This command
|
|
113
|
+
STORAGE/RUNTIME
|
|
114
|
+
This command stores only the server definition supplied on this invocation. It does not import or inherit Claude Code, Codex, OpenCode, or other live MCP configs. Normal standalone gjc sessions do not load stored MCP registrations today. Public output redacts env, header, auth, and OAuth credential values.
|
|
115
115
|
`);
|
|
116
116
|
}
|
|
117
117
|
}
|
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
|
}
|
package/src/commands/stats.ts
CHANGED
|
@@ -2,8 +2,7 @@
|
|
|
2
2
|
* View usage statistics dashboard.
|
|
3
3
|
*/
|
|
4
4
|
import { Command, Flags } from "@gajae-code/utils/cli";
|
|
5
|
-
import {
|
|
6
|
-
import { initTheme } from "../modes/theme/theme";
|
|
5
|
+
import type { StatsCommandArgs } from "../cli/stats-cli";
|
|
7
6
|
|
|
8
7
|
export default class Stats extends Command {
|
|
9
8
|
static description = "View usage statistics";
|
|
@@ -23,6 +22,11 @@ export default class Stats extends Command {
|
|
|
23
22
|
summary: flags.summary,
|
|
24
23
|
};
|
|
25
24
|
|
|
25
|
+
const [{ initTheme }, { runStatsCommand }] = await Promise.all([
|
|
26
|
+
import("../modes/theme/theme"),
|
|
27
|
+
import("../cli/stats-cli"),
|
|
28
|
+
]);
|
|
29
|
+
|
|
26
30
|
await initTheme();
|
|
27
31
|
await runStatsCommand(cmd);
|
|
28
32
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
3
|
"$id": "https://raw.githubusercontent.com/Yeachan-Heo/gajae-code/main/packages/coding-agent/src/config/mcp-schema.json",
|
|
4
4
|
"title": "GJC MCP configuration",
|
|
5
|
-
"description": "Schema for mcp.json, .mcp.json, .gjc/mcp.json, and ~/.gjc/agent/mcp.json
|
|
5
|
+
"description": "Schema for stored MCP server definitions in mcp.json, .mcp.json, .gjc/mcp.json, and ~/.gjc/agent/mcp.json. Normal standalone gjc sessions do not consume gjc mcp registrations as runtime tools today.",
|
|
6
6
|
"type": "object",
|
|
7
7
|
"additionalProperties": false,
|
|
8
8
|
"properties": {
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
},
|
|
13
13
|
"mcpServers": {
|
|
14
14
|
"type": "object",
|
|
15
|
-
"description": "Map of MCP server name to server configuration.",
|
|
15
|
+
"description": "Map of MCP server name to stored server configuration.",
|
|
16
16
|
"propertyNames": {
|
|
17
17
|
"pattern": "^[a-zA-Z0-9_.-]{1,100}$"
|
|
18
18
|
},
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
},
|
|
23
23
|
"disabledServers": {
|
|
24
24
|
"type": "array",
|
|
25
|
-
"description": "
|
|
25
|
+
"description": "Denylist for explicit runtime MCP consumers that load discovered or stored servers by name.",
|
|
26
26
|
"items": {
|
|
27
27
|
"type": "string",
|
|
28
28
|
"minLength": 1
|
|
@@ -94,11 +94,11 @@
|
|
|
94
94
|
"properties": {
|
|
95
95
|
"enabled": {
|
|
96
96
|
"type": "boolean",
|
|
97
|
-
"description": "Whether
|
|
97
|
+
"description": "Whether explicit runtime MCP consumers should try to connect this server."
|
|
98
98
|
},
|
|
99
99
|
"autoload": {
|
|
100
100
|
"type": "boolean",
|
|
101
|
-
"description": "Whether
|
|
101
|
+
"description": "Whether an explicit runtime MCP consumer should connect this server automatically when that consumer starts (default: true). Normal standalone gjc, gjc --tmux, and print-mode sessions do not consume gjc mcp registrations today; false keeps the server configured for consumers that support explicit connection."
|
|
102
102
|
},
|
|
103
103
|
"timeout": {
|
|
104
104
|
"type": "number",
|
|
@@ -114,13 +114,13 @@
|
|
|
114
114
|
}
|
|
115
115
|
},
|
|
116
116
|
"stdioServer": {
|
|
117
|
+
"type": "object",
|
|
118
|
+
"unevaluatedProperties": false,
|
|
117
119
|
"allOf": [
|
|
118
120
|
{
|
|
119
121
|
"$ref": "#/$defs/serverBase"
|
|
120
122
|
},
|
|
121
123
|
{
|
|
122
|
-
"type": "object",
|
|
123
|
-
"additionalProperties": false,
|
|
124
124
|
"required": ["command"],
|
|
125
125
|
"properties": {
|
|
126
126
|
"type": {
|
|
@@ -138,11 +138,15 @@
|
|
|
138
138
|
"items": {
|
|
139
139
|
"type": "string"
|
|
140
140
|
},
|
|
141
|
-
"description": "Arguments passed to the stdio server process."
|
|
141
|
+
"description": "Arguments passed to the stdio server process. Public CLI output redacts sensitive-looking argument values."
|
|
142
142
|
},
|
|
143
143
|
"env": {
|
|
144
144
|
"$ref": "#/$defs/stringMap",
|
|
145
|
-
"description": "Environment variables passed to the stdio process."
|
|
145
|
+
"description": "Environment variables passed to the stdio process. Public CLI output redacts values."
|
|
146
|
+
},
|
|
147
|
+
"noInheritEnv": {
|
|
148
|
+
"type": "boolean",
|
|
149
|
+
"description": "When true, explicit runtime consumers should avoid inheriting the host environment and pass only a minimal allowlist plus explicit env keys."
|
|
146
150
|
},
|
|
147
151
|
"cwd": {
|
|
148
152
|
"type": "string",
|
|
@@ -156,13 +160,13 @@
|
|
|
156
160
|
]
|
|
157
161
|
},
|
|
158
162
|
"httpServer": {
|
|
163
|
+
"type": "object",
|
|
164
|
+
"unevaluatedProperties": false,
|
|
159
165
|
"allOf": [
|
|
160
166
|
{
|
|
161
167
|
"$ref": "#/$defs/serverBase"
|
|
162
168
|
},
|
|
163
169
|
{
|
|
164
|
-
"type": "object",
|
|
165
|
-
"additionalProperties": false,
|
|
166
170
|
"required": ["type", "url"],
|
|
167
171
|
"properties": {
|
|
168
172
|
"type": {
|
|
@@ -173,11 +177,11 @@
|
|
|
173
177
|
"url": {
|
|
174
178
|
"type": "string",
|
|
175
179
|
"minLength": 1,
|
|
176
|
-
"description": "MCP endpoint URL."
|
|
180
|
+
"description": "MCP endpoint URL. Public CLI output redacts userinfo, query values, and fragments."
|
|
177
181
|
},
|
|
178
182
|
"headers": {
|
|
179
183
|
"$ref": "#/$defs/stringMap",
|
|
180
|
-
"description": "HTTP headers sent with MCP requests."
|
|
184
|
+
"description": "HTTP headers sent with MCP requests. Public CLI output redacts values."
|
|
181
185
|
}
|
|
182
186
|
},
|
|
183
187
|
"not": {
|
|
@@ -187,13 +191,13 @@
|
|
|
187
191
|
]
|
|
188
192
|
},
|
|
189
193
|
"sseServer": {
|
|
194
|
+
"type": "object",
|
|
195
|
+
"unevaluatedProperties": false,
|
|
190
196
|
"allOf": [
|
|
191
197
|
{
|
|
192
198
|
"$ref": "#/$defs/serverBase"
|
|
193
199
|
},
|
|
194
200
|
{
|
|
195
|
-
"type": "object",
|
|
196
|
-
"additionalProperties": false,
|
|
197
201
|
"required": ["type", "url"],
|
|
198
202
|
"properties": {
|
|
199
203
|
"type": {
|
|
@@ -204,11 +208,11 @@
|
|
|
204
208
|
"url": {
|
|
205
209
|
"type": "string",
|
|
206
210
|
"minLength": 1,
|
|
207
|
-
"description": "Legacy SSE endpoint URL."
|
|
211
|
+
"description": "Legacy SSE endpoint URL. Public CLI output redacts userinfo, query values, and fragments."
|
|
208
212
|
},
|
|
209
213
|
"headers": {
|
|
210
214
|
"$ref": "#/$defs/stringMap",
|
|
211
|
-
"description": "HTTP headers sent with the SSE transport."
|
|
215
|
+
"description": "HTTP headers sent with the SSE transport. Public CLI output redacts values."
|
|
212
216
|
}
|
|
213
217
|
},
|
|
214
218
|
"not": {
|
|
@@ -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,
|
|
@@ -804,13 +805,13 @@ export const SETTINGS_SCHEMA = {
|
|
|
804
805
|
// Reasoning and prompts
|
|
805
806
|
defaultThinkingLevel: {
|
|
806
807
|
type: "enum",
|
|
807
|
-
values:
|
|
808
|
+
values: DEFAULT_THINKING_LEVELS,
|
|
808
809
|
default: THINKING_EFFORTS[3],
|
|
809
810
|
ui: {
|
|
810
811
|
tab: "model",
|
|
811
812
|
label: "Thinking Level",
|
|
812
813
|
description: "Reasoning depth for thinking-capable models",
|
|
813
|
-
options: [...
|
|
814
|
+
options: [...DEFAULT_THINKING_LEVELS.map(getThinkingLevelMetadata)],
|
|
814
815
|
},
|
|
815
816
|
},
|
|
816
817
|
|
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();
|
|
@@ -2000,10 +2000,13 @@ export function createCoordinatorMcpServer(options: CoordinatorMcpServerOptions
|
|
|
2000
2000
|
let matched = filterCoordinatorEvents(events, args, limit);
|
|
2001
2001
|
let timedOut = false;
|
|
2002
2002
|
if (matched.length === 0 && timeoutMs > 0) {
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2003
|
+
const deadline = Date.now() + timeoutMs;
|
|
2004
|
+
while (matched.length === 0 && Date.now() < deadline) {
|
|
2005
|
+
await waitForCoordinatorEvents(namespaceDir, Math.min(50, Math.max(1, deadline - Date.now())));
|
|
2006
|
+
await reconcileActiveTurnAcknowledgements();
|
|
2007
|
+
events = await readCoordinatorEvents(namespaceDir);
|
|
2008
|
+
matched = filterCoordinatorEvents(events, args, limit);
|
|
2009
|
+
}
|
|
2007
2010
|
timedOut = matched.length === 0;
|
|
2008
2011
|
}
|
|
2009
2012
|
return {
|
package/src/discovery/builtin.ts
CHANGED
|
@@ -176,6 +176,19 @@ async function loadMCPServers(ctx: LoadContext): Promise<LoadResult<MCPServer>>
|
|
|
176
176
|
autoload = undefined;
|
|
177
177
|
}
|
|
178
178
|
|
|
179
|
+
// Validate noInheritEnv: boolean only, warn on other types
|
|
180
|
+
let noInheritEnv: boolean | undefined;
|
|
181
|
+
if (serverConfig.noInheritEnv === undefined || serverConfig.noInheritEnv === null) {
|
|
182
|
+
noInheritEnv = undefined;
|
|
183
|
+
} else if (typeof serverConfig.noInheritEnv === "boolean") {
|
|
184
|
+
noInheritEnv = serverConfig.noInheritEnv;
|
|
185
|
+
} else {
|
|
186
|
+
logger.warn(
|
|
187
|
+
`MCP server "${serverName}": invalid noInheritEnv type ${typeof serverConfig.noInheritEnv}, ignoring`,
|
|
188
|
+
);
|
|
189
|
+
noInheritEnv = undefined;
|
|
190
|
+
}
|
|
191
|
+
|
|
179
192
|
result.push({
|
|
180
193
|
name: serverName,
|
|
181
194
|
enabled,
|
|
@@ -184,6 +197,7 @@ async function loadMCPServers(ctx: LoadContext): Promise<LoadResult<MCPServer>>
|
|
|
184
197
|
command: serverConfig.command as string | undefined,
|
|
185
198
|
args: serverConfig.args as string[] | undefined,
|
|
186
199
|
env: serverConfig.env as Record<string, string> | undefined,
|
|
200
|
+
noInheritEnv,
|
|
187
201
|
cwd: serverConfig.cwd as string | undefined,
|
|
188
202
|
url: serverConfig.url as string | undefined,
|
|
189
203
|
headers: serverConfig.headers as Record<string, string> | undefined,
|