@gajae-code/coding-agent 0.18.0 → 0.18.5
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 +95 -0
- package/dist/types/cli/stats-cli.d.ts +0 -5
- package/dist/types/config/autorouting-tier-map.d.ts +29 -2
- package/dist/types/config/model-registry.d.ts +16 -1
- package/dist/types/config/settings-schema.d.ts +2 -2
- package/dist/types/exa/mcp-client.d.ts +1 -5
- package/dist/types/exec/bash-shell-guardian.d.ts +26 -0
- package/dist/types/gjc-runtime/session-state-sidecar.d.ts +1 -0
- package/dist/types/hashline/parser.d.ts +28 -5
- package/dist/types/modes/acp/acp-agent.d.ts +6 -1
- package/dist/types/sdk/acp/adapter.d.ts +3 -1
- package/dist/types/sdk/broker/lifecycle.d.ts +6 -1
- package/dist/types/sdk/broker/recovery-backoff.d.ts +62 -0
- package/dist/types/sdk/broker/runtime.d.ts +20 -0
- package/dist/types/sdk/broker/startup-budget.d.ts +5 -1
- package/dist/types/sdk/bus/index.d.ts +1 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +1 -1
- package/dist/types/sdk/client/client.d.ts +5 -1
- package/dist/types/sdk/host/session-runtime.d.ts +7 -0
- package/dist/types/sdk/prompt-watchdog.d.ts +1 -1
- package/dist/types/session/agent-session.d.ts +2 -0
- package/dist/types/task/index.d.ts +0 -1
- package/dist/types/tools/bash.d.ts +1 -0
- package/dist/types/tools/descriptors.d.ts +5 -0
- package/dist/types/tools/eval-backends.d.ts +37 -0
- package/dist/types/tools/eval.d.ts +1 -6
- package/dist/types/tools/index.d.ts +1 -36
- package/dist/types/tools/session-descriptions.d.ts +22 -0
- package/package.json +7 -7
- package/scripts/generate-sdk-operation-inventory.ts +2 -0
- package/src/cli/doctor/managed-link.ts +4 -4
- package/src/cli/doctor/repairs.ts +9 -1
- package/src/cli/stats-cli.ts +4 -36
- package/src/cli-main.ts +4 -8
- package/src/cli.ts +4 -8
- package/src/commands/commit.ts +2 -2
- package/src/commands/sdk.ts +1 -2
- package/src/config/autorouting-tier-map.ts +20 -2
- package/src/config/model-preset-registry.ts +17 -2
- package/src/config/model-profile-activation.ts +3 -1
- package/src/config/model-profiles.ts +11 -3
- package/src/config/model-registry.ts +45 -17
- package/src/config/settings-schema.ts +8 -2
- package/src/exa/mcp-client.ts +41 -8
- package/src/exec/bash-shell-guardian.ts +214 -48
- package/src/gjc-runtime/session-state-sidecar.ts +88 -58
- package/src/hashline/execute.ts +51 -3
- package/src/hashline/parser.ts +63 -16
- package/src/internal-urls/docs-index.generated.ts +25 -25
- package/src/modes/acp/acp-agent.ts +136 -20
- package/src/modes/controllers/ssh-command-controller.ts +3 -1
- package/src/modes/interactive-mode.ts +10 -0
- package/src/modes/utils/context-usage.ts +2 -6
- package/src/prompts/tools/task.md +8 -27
- package/src/sdk/acp/adapter.ts +38 -3
- package/src/sdk/broker/doctor-restart.ts +38 -3
- package/src/sdk/broker/lifecycle.ts +408 -218
- package/src/sdk/broker/recovery-backoff.ts +135 -0
- package/src/sdk/broker/runtime.ts +83 -0
- package/src/sdk/broker/startup-budget.ts +6 -1
- package/src/sdk/bus/chat-daemon-control.ts +2 -2
- package/src/sdk/bus/index.ts +308 -22
- package/src/sdk/bus/telegram-daemon-contract.ts +1 -1
- package/src/sdk/client/client.ts +53 -35
- package/src/sdk/host/session-runtime.ts +99 -3
- package/src/sdk/prompt-watchdog.ts +17 -4
- package/src/sdk/protocol/operation-inventory.generated.json +11 -0
- package/src/session/agent-session.ts +79 -17
- package/src/session/internal/managed-session-storage.ts +23 -0
- package/src/task/index.ts +21 -66
- package/src/tools/bash.ts +39 -0
- package/src/tools/descriptors.ts +27 -1
- package/src/tools/eval-backends.ts +95 -0
- package/src/tools/eval.ts +3 -15
- package/src/tools/index.ts +9 -88
- package/src/tools/search.ts +2 -7
- package/src/tools/session-descriptions.ts +135 -0
- package/src/tools/tool-catalog.generated.ts +1 -1
- package/dist/types/cli/grep-cli.d.ts +0 -17
- package/dist/types/cli/shell-cli.d.ts +0 -8
- package/dist/types/commands/grep.d.ts +0 -42
- package/dist/types/commands/shell.d.ts +0 -21
- package/src/cli/grep-cli.ts +0 -160
- package/src/cli/shell-cli.ts +0 -176
- package/src/commands/grep.ts +0 -48
- package/src/commands/shell.ts +0 -29
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { ToolSession } from ".";
|
|
2
|
+
export interface EvalBackendsAllowance {
|
|
3
|
+
python: boolean;
|
|
4
|
+
js: boolean;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Parse the `GJC_PY` multi-value token into per-backend booleans.
|
|
8
|
+
*
|
|
9
|
+
* Tokens (case-insensitive):
|
|
10
|
+
* - `0` / `bash` → JavaScript only (`{ py: false, js: true }`)
|
|
11
|
+
* - `1` / `py` → Python only (`{ py: true, js: false }`)
|
|
12
|
+
* - `js` → JavaScript only (`{ py: false, js: true }`)
|
|
13
|
+
* - `mix` / `both` → both backends (`{ py: true, js: true }`)
|
|
14
|
+
*
|
|
15
|
+
* Returns `null` when `GJC_PY` is unset, empty, or holds an unrecognized
|
|
16
|
+
* token, so the caller can fall back to the legacy `PI_PY` / `PI_JS` flags or
|
|
17
|
+
* per-key settings. This matches the documented contract that invalid values
|
|
18
|
+
* are ignored.
|
|
19
|
+
*/
|
|
20
|
+
export declare function parseGjcPy(env: Record<string, string | undefined>): {
|
|
21
|
+
py: boolean;
|
|
22
|
+
js: boolean;
|
|
23
|
+
} | null;
|
|
24
|
+
/**
|
|
25
|
+
* Resolve eval-backend allowance from environment only. `GJC_PY` wins when set
|
|
26
|
+
* to a recognized token; otherwise the legacy `PI_PY` / `PI_JS` flags apply.
|
|
27
|
+
* Returns `null` when no env override is set so the caller can defer to settings.
|
|
28
|
+
*/
|
|
29
|
+
export declare function resolveEvalBackendsFromEnv(env: Record<string, string | undefined>): EvalBackendsAllowance | null;
|
|
30
|
+
/** Read per-backend allowance from settings (defaults true). */
|
|
31
|
+
export declare function readEvalBackendsAllowance(session: ToolSession): EvalBackendsAllowance;
|
|
32
|
+
/**
|
|
33
|
+
* Materialize the active eval backend allowance. `GJC_PY` takes precedence
|
|
34
|
+
* over the legacy `PI_PY` / `PI_JS` env flags, which in turn override the
|
|
35
|
+
* per-key settings (defaults true). When no env override is set, settings win.
|
|
36
|
+
*/
|
|
37
|
+
export declare function resolveEvalBackends(session: ToolSession): EvalBackendsAllowance;
|
|
@@ -42,11 +42,7 @@ export type EvalToolResult = {
|
|
|
42
42
|
details: EvalToolDetails | undefined;
|
|
43
43
|
};
|
|
44
44
|
export type EvalProxyExecutor = (params: EvalToolParams, signal?: AbortSignal) => Promise<EvalToolResult>;
|
|
45
|
-
export
|
|
46
|
-
py?: boolean;
|
|
47
|
-
js?: boolean;
|
|
48
|
-
}
|
|
49
|
-
export declare function getEvalToolDescription(options?: EvalToolDescriptionOptions): string;
|
|
45
|
+
export { type EvalToolDescriptionOptions, getEvalToolDescription } from "./session-descriptions";
|
|
50
46
|
export interface EvalToolOptions {
|
|
51
47
|
proxyExecutor?: EvalProxyExecutor;
|
|
52
48
|
}
|
|
@@ -105,4 +101,3 @@ export declare const evalToolRenderer: {
|
|
|
105
101
|
mergeCallAndResult: boolean;
|
|
106
102
|
inline: boolean;
|
|
107
103
|
};
|
|
108
|
-
export {};
|
|
@@ -405,42 +405,7 @@ export interface BuiltinCapabilityCatalogEntry {
|
|
|
405
405
|
}
|
|
406
406
|
export declare const BUILTIN_CAPABILITY_CATALOG: readonly BuiltinCapabilityCatalogEntry[];
|
|
407
407
|
export type ToolName = keyof typeof BUILTIN_TOOLS;
|
|
408
|
-
export
|
|
409
|
-
python: boolean;
|
|
410
|
-
js: boolean;
|
|
411
|
-
}
|
|
412
|
-
/**
|
|
413
|
-
* Parse the `GJC_PY` multi-value token into per-backend booleans.
|
|
414
|
-
*
|
|
415
|
-
* Tokens (case-insensitive):
|
|
416
|
-
* - `0` / `bash` → JavaScript only (`{ py: false, js: true }`)
|
|
417
|
-
* - `1` / `py` → Python only (`{ py: true, js: false }`)
|
|
418
|
-
* - `js` → JavaScript only (`{ py: false, js: true }`)
|
|
419
|
-
* - `mix` / `both` → both backends (`{ py: true, js: true }`)
|
|
420
|
-
*
|
|
421
|
-
* Returns `null` when `GJC_PY` is unset, empty, or holds an unrecognized
|
|
422
|
-
* token, so the caller can fall back to the legacy `PI_PY` / `PI_JS` flags or
|
|
423
|
-
* per-key settings. This matches the documented contract that invalid values
|
|
424
|
-
* are ignored.
|
|
425
|
-
*/
|
|
426
|
-
export declare function parseGjcPy(env: Record<string, string | undefined>): {
|
|
427
|
-
py: boolean;
|
|
428
|
-
js: boolean;
|
|
429
|
-
} | null;
|
|
430
|
-
/**
|
|
431
|
-
* Resolve eval-backend allowance from environment only. `GJC_PY` wins when set
|
|
432
|
-
* to a recognized token; otherwise the legacy `PI_PY` / `PI_JS` flags apply.
|
|
433
|
-
* Returns `null` when no env override is set so the caller can defer to settings.
|
|
434
|
-
*/
|
|
435
|
-
export declare function resolveEvalBackendsFromEnv(env: Record<string, string | undefined>): EvalBackendsAllowance | null;
|
|
436
|
-
/** Read per-backend allowance from settings (defaults true). */
|
|
437
|
-
export declare function readEvalBackendsAllowance(session: ToolSession): EvalBackendsAllowance;
|
|
438
|
-
/**
|
|
439
|
-
* Materialize the active eval backend allowance. `GJC_PY` takes precedence
|
|
440
|
-
* over the legacy `PI_PY` / `PI_JS` env flags, which in turn override the
|
|
441
|
-
* per-key settings (defaults true). When no env override is set, settings win.
|
|
442
|
-
*/
|
|
443
|
-
export declare function resolveEvalBackends(session: ToolSession): EvalBackendsAllowance;
|
|
408
|
+
export { type EvalBackendsAllowance, parseGjcPy, readEvalBackendsAllowance, resolveEvalBackends, resolveEvalBackendsFromEnv, } from "./eval-backends";
|
|
444
409
|
/**
|
|
445
410
|
* Create tools from the descriptor registry.
|
|
446
411
|
*/
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type TaskToolSchemaInstance } from "../task/types";
|
|
2
|
+
interface DescriptionSettings {
|
|
3
|
+
get(key: "task.maxConcurrency" | "task.isolation.mode" | "task.simple" | "task.disabledAgents" | "eval.py" | "eval.js" | "readLineNumbers" | "readHashLines" | "edit.mode"): unknown;
|
|
4
|
+
getEffectiveAutorouting(): {
|
|
5
|
+
active: boolean;
|
|
6
|
+
};
|
|
7
|
+
}
|
|
8
|
+
interface DescriptionSession {
|
|
9
|
+
settings: DescriptionSettings;
|
|
10
|
+
getSessionSpawns?: () => string | null;
|
|
11
|
+
hasEditTool?: boolean;
|
|
12
|
+
}
|
|
13
|
+
export interface EvalToolDescriptionOptions {
|
|
14
|
+
py?: boolean;
|
|
15
|
+
js?: boolean;
|
|
16
|
+
}
|
|
17
|
+
export declare function getEvalToolDescription(options?: EvalToolDescriptionOptions): string;
|
|
18
|
+
export declare function evalToolDescriptionForSession(session: DescriptionSession | null | undefined): string;
|
|
19
|
+
export declare function searchToolDescriptionForSession(session: DescriptionSession): string;
|
|
20
|
+
export declare function renderTaskDescription(session: DescriptionSession): string;
|
|
21
|
+
export declare function taskParametersForSession(session?: DescriptionSession): TaskToolSchemaInstance;
|
|
22
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@gajae-code/coding-agent",
|
|
4
|
-
"version": "0.18.
|
|
4
|
+
"version": "0.18.5",
|
|
5
5
|
"description": "Gajae Code CLI with read, bash, edit, write tools and session management",
|
|
6
6
|
"homepage": "https://gajae-code.com",
|
|
7
7
|
"author": "Yeachan-Heo and Gajae Code Contributors",
|
|
@@ -57,12 +57,12 @@
|
|
|
57
57
|
"@agentclientprotocol/sdk": "1.3.0",
|
|
58
58
|
"@babel/parser": "^7.29.3",
|
|
59
59
|
"@mozilla/readability": "^0.6.0",
|
|
60
|
-
"@gajae-code/stats": "0.18.
|
|
61
|
-
"@gajae-code/agent-core": "0.18.
|
|
62
|
-
"@gajae-code/ai": "0.18.
|
|
63
|
-
"@gajae-code/natives": "0.18.
|
|
64
|
-
"@gajae-code/tui": "0.18.
|
|
65
|
-
"@gajae-code/utils": "0.18.
|
|
60
|
+
"@gajae-code/stats": "0.18.5",
|
|
61
|
+
"@gajae-code/agent-core": "0.18.5",
|
|
62
|
+
"@gajae-code/ai": "0.18.5",
|
|
63
|
+
"@gajae-code/natives": "0.18.5",
|
|
64
|
+
"@gajae-code/tui": "0.18.5",
|
|
65
|
+
"@gajae-code/utils": "0.18.5",
|
|
66
66
|
"@puppeteer/browsers": "^2.13.0",
|
|
67
67
|
"@types/turndown": "5.0.6",
|
|
68
68
|
"@xterm/headless": "^6.0.0",
|
|
@@ -203,6 +203,8 @@ const LOCKED_EXCLUSIONS: Readonly<Record<string, string>> = {
|
|
|
203
203
|
"agent_session:queueDeferredMessage": "internal accessor/plumbing, not a user-facing control seam",
|
|
204
204
|
"agent_session:queueDeferredMessageForTests": "internal accessor/plumbing, not a user-facing control seam",
|
|
205
205
|
"agent_session:getContextUsageObservabilityForTests": "test-only observability, not a user-facing control seam",
|
|
206
|
+
"agent_session:getAutoCompactionThresholdTokens":
|
|
207
|
+
"internal compaction-threshold accessor feeding the /context breakdown; exposed through context.get, not an independent SDK operation",
|
|
206
208
|
"agent_session:purgeQueuedCustomMessages": "internal accessor/plumbing, not a user-facing control seam",
|
|
207
209
|
"agent_session:clearQueue": "internal accessor/plumbing, not a user-facing control seam",
|
|
208
210
|
"agent_session:popLastQueuedMessage": "internal accessor/plumbing, not a user-facing control seam",
|
|
@@ -217,11 +217,11 @@ export async function describeManagedLink(
|
|
|
217
217
|
const parent = await fs.lstat(parentPath, { bigint: true }).catch(() => undefined);
|
|
218
218
|
const trustedReceipt =
|
|
219
219
|
!!body && ownedReceipt(receipt) && publicCandidates.some(candidate => candidate.path === body.source);
|
|
220
|
+
// The recorded st_dev is not compared: macOS APFS gives the same volume a new st_dev after a reboot,
|
|
221
|
+
// which made every pre-reboot receipt look foreign (#5990). The inode binds the link, and the volume
|
|
222
|
+
// is re-derived from the parent directory observed in the same snapshot.
|
|
220
223
|
const identityMatches =
|
|
221
|
-
!!body &&
|
|
222
|
-
!!link &&
|
|
223
|
-
body.identity.dev === link.stat.dev.toString() &&
|
|
224
|
-
body.identity.ino === link.stat.ino.toString();
|
|
224
|
+
!!body && !!link && body.identity.ino === link.stat.ino.toString() && link.stat.dev === link.parent.dev;
|
|
225
225
|
const owned = trustedReceipt && (link ? identityMatches : occupant === undefined);
|
|
226
226
|
const recordedCandidate = body && publicCandidates.find(candidate => candidate.path === body.source);
|
|
227
227
|
let status: ManagedLinkDescriptor["status"] = "unknown";
|
|
@@ -892,7 +892,15 @@ export async function executeSelectedDoctorRepair(
|
|
|
892
892
|
outcome =
|
|
893
893
|
result.kind === "restarted"
|
|
894
894
|
? { kind: result.kind, successorIncarnation: result.successor.incarnation }
|
|
895
|
-
: {
|
|
895
|
+
: {
|
|
896
|
+
kind: result.kind,
|
|
897
|
+
// A broker refusal carries the broker's own code (e.g. restart_busy).
|
|
898
|
+
...("reason" in result
|
|
899
|
+
? { reason: result.reason }
|
|
900
|
+
: "code" in result
|
|
901
|
+
? { reason: result.code }
|
|
902
|
+
: {}),
|
|
903
|
+
};
|
|
896
904
|
} else {
|
|
897
905
|
settings = await Settings.loadForScope({ cwd: context.cwd, agentDir: context.agentRoot.locator });
|
|
898
906
|
const result = await restartDaemonForDoctor({
|
package/src/cli/stats-cli.ts
CHANGED
|
@@ -70,41 +70,6 @@ export interface StatsCommandArgs {
|
|
|
70
70
|
summary: boolean;
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
// =============================================================================
|
|
74
|
-
// Argument Parser
|
|
75
|
-
// =============================================================================
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Parse stats subcommand arguments.
|
|
79
|
-
* Returns undefined if not a stats command.
|
|
80
|
-
*/
|
|
81
|
-
export function parseStatsArgs(args: string[]): StatsCommandArgs | undefined {
|
|
82
|
-
if (args.length === 0 || args[0] !== "stats") {
|
|
83
|
-
return undefined;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
const result: StatsCommandArgs = {
|
|
87
|
-
port: 3847,
|
|
88
|
-
json: false,
|
|
89
|
-
summary: false,
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
for (let i = 1; i < args.length; i++) {
|
|
93
|
-
const arg = args[i];
|
|
94
|
-
if (arg === "--json" || arg === "-j") {
|
|
95
|
-
result.json = true;
|
|
96
|
-
} else if (arg === "--summary" || arg === "-s") {
|
|
97
|
-
result.summary = true;
|
|
98
|
-
} else if ((arg === "--port" || arg === "-p") && i + 1 < args.length) {
|
|
99
|
-
result.port = parseInt(args[++i], 10);
|
|
100
|
-
} else if (arg.startsWith("--port=")) {
|
|
101
|
-
result.port = parseInt(arg.split("=")[1], 10);
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
return result;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
73
|
function formatCost(n: number): string {
|
|
109
74
|
if (n < 0.01) return `$${n.toFixed(4)}`;
|
|
110
75
|
if (n < 1) return `$${n.toFixed(3)}`;
|
|
@@ -131,7 +96,10 @@ export async function runStatsCommand(cmd: StatsCommandArgs): Promise<void> {
|
|
|
131
96
|
const { processed, files } = await syncAllSessions({ onProgress: progress.onProgress });
|
|
132
97
|
progress.finish();
|
|
133
98
|
const total = await getTotalMessageCount();
|
|
134
|
-
|
|
99
|
+
const synced = `Synced ${processed} new entries from ${files} files (${total} total)\n`;
|
|
100
|
+
// `--json` stdout must be exactly the JSON document so it can be piped into a parser.
|
|
101
|
+
if (cmd.json) process.stderr.write(`${synced}\n`);
|
|
102
|
+
else console.log(synced);
|
|
135
103
|
|
|
136
104
|
if (cmd.json) {
|
|
137
105
|
const stats = await getDashboardStats();
|
package/src/cli-main.ts
CHANGED
|
@@ -95,14 +95,10 @@ async function showHelp(config: CliConfig): Promise<void> {
|
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
async function installRuntimeGlobals(): Promise<void> {
|
|
98
|
-
|
|
99
|
-
//
|
|
100
|
-
//
|
|
101
|
-
//
|
|
102
|
-
// `protocol: "http2"` per request, with transparent HTTP/1.1 fallback on
|
|
103
|
-
// `HTTP2Unsupported`. See @gajae-code/ai/utils/h2-fetch for details.
|
|
104
|
-
installH2Fetch();
|
|
105
|
-
|
|
98
|
+
// fetch() deliberately stays on HTTP/1.1. Bun's HTTP/2 client (per-request
|
|
99
|
+
// `protocol: "http2"`) wedges a pooled connection while uploading large
|
|
100
|
+
// request bodies (~300KB): every later request on it waits forever for
|
|
101
|
+
// response headers. Do not re-enable HTTP/2 without re-running that probe.
|
|
106
102
|
const { warnIfMacOSNoFileLimitTooLow } = await import("./cli/nofile-limit");
|
|
107
103
|
warnIfMacOSNoFileLimitTooLow();
|
|
108
104
|
|
package/src/cli.ts
CHANGED
|
@@ -37,14 +37,10 @@ export const commands: CommandEntry[] = [
|
|
|
37
37
|
];
|
|
38
38
|
|
|
39
39
|
async function installRuntimeGlobals(writeNoFileWarning?: (text: string) => void): Promise<void> {
|
|
40
|
-
|
|
41
|
-
//
|
|
42
|
-
//
|
|
43
|
-
//
|
|
44
|
-
// `protocol: "http2"` per request, with transparent HTTP/1.1 fallback on
|
|
45
|
-
// `HTTP2Unsupported`. See @gajae-code/ai/utils/h2-fetch for details.
|
|
46
|
-
installH2Fetch();
|
|
47
|
-
|
|
40
|
+
// fetch() deliberately stays on HTTP/1.1. Bun's HTTP/2 client (per-request
|
|
41
|
+
// `protocol: "http2"`) wedges a pooled connection while uploading large
|
|
42
|
+
// request bodies (~300KB): every later request on it waits forever for
|
|
43
|
+
// response headers. Do not re-enable HTTP/2 without re-running that probe.
|
|
48
44
|
const { warnIfMacOSNoFileLimitTooLow } = await import("./cli/nofile-limit");
|
|
49
45
|
warnIfMacOSNoFileLimitTooLow({ writeStderr: writeNoFileWarning });
|
|
50
46
|
|
package/src/commands/commit.ts
CHANGED
|
@@ -32,8 +32,8 @@ export default class Commit extends Command {
|
|
|
32
32
|
};
|
|
33
33
|
|
|
34
34
|
await initTheme();
|
|
35
|
-
// The agentic commit flow opens
|
|
36
|
-
// provider
|
|
35
|
+
// The agentic commit flow opens keep-alive sockets to the model
|
|
36
|
+
// provider and spins up an AgentSession with
|
|
37
37
|
// background async-job + extension machinery. `session.dispose()` releases
|
|
38
38
|
// what it can, but Bun's fetch keeps idle connections warm and a few
|
|
39
39
|
// timers (Settings autosave, OAuth refresh) stay armed long enough to
|
package/src/commands/sdk.ts
CHANGED
|
@@ -45,8 +45,9 @@ export const TIER_MAP_VERSION = 1;
|
|
|
45
45
|
|
|
46
46
|
export const CURATED_TIER_LABELS = {
|
|
47
47
|
"anthropic/claude-haiku-4-5": [{ tier: "fast", rank: 1 }],
|
|
48
|
-
"anthropic/claude-sonnet-5": [{ tier: "balanced", rank: 1 }],
|
|
49
|
-
"anthropic/claude-sonnet-
|
|
48
|
+
"anthropic/claude-sonnet-5-5": [{ tier: "balanced", rank: 1 }],
|
|
49
|
+
"anthropic/claude-sonnet-5": [{ tier: "balanced", rank: 2 }],
|
|
50
|
+
"anthropic/claude-sonnet-4-6": [{ tier: "balanced", rank: 3 }],
|
|
50
51
|
"anthropic/claude-opus-5-5": [{ tier: "strong", effort: "high", rank: 1 }],
|
|
51
52
|
"anthropic/claude-opus-5": [{ tier: "strong", effort: "high", rank: 2 }],
|
|
52
53
|
"anthropic/claude-opus-4-8": [{ tier: "strong", effort: "high", rank: 3 }],
|
|
@@ -55,6 +56,7 @@ export const CURATED_TIER_LABELS = {
|
|
|
55
56
|
{ tier: "balanced", effort: "medium", rank: 1 },
|
|
56
57
|
],
|
|
57
58
|
"openai-codex/gpt-6-sol": [{ tier: "strong", effort: "high", rank: 1 }],
|
|
59
|
+
"openai-codex/gpt-6.1-sol": [{ tier: "strong", effort: "high", rank: 2 }],
|
|
58
60
|
"google/gemini-3.5-flash-lite": [{ tier: "fast", rank: 1 }],
|
|
59
61
|
"google/gemini-2.5-flash-lite": [{ tier: "fast", rank: 2 }],
|
|
60
62
|
"google/gemini-3.5-flash": [{ tier: "balanced", rank: 1 }],
|
|
@@ -75,6 +77,22 @@ export const CURATED_TIER_LABELS = {
|
|
|
75
77
|
|
|
76
78
|
/** Generated from packages/ai/src/models.json at feature land: 3936 in-scope baseline skips. */
|
|
77
79
|
export const TIER_MAP_SKIP_LIST = {
|
|
80
|
+
"amazon-bedrock/anthropic.claude-sonnet-5-5": { rationale: "Sonnet 5.5 catalog addition (#6111); not yet curated" },
|
|
81
|
+
"amazon-bedrock/au.anthropic.claude-sonnet-5-5": {
|
|
82
|
+
rationale: "Sonnet 5.5 catalog addition (#6111); not yet curated",
|
|
83
|
+
},
|
|
84
|
+
"amazon-bedrock/eu.anthropic.claude-sonnet-5-5": {
|
|
85
|
+
rationale: "Sonnet 5.5 catalog addition (#6111); not yet curated",
|
|
86
|
+
},
|
|
87
|
+
"amazon-bedrock/global.anthropic.claude-sonnet-5-5": {
|
|
88
|
+
rationale: "Sonnet 5.5 catalog addition (#6111); not yet curated",
|
|
89
|
+
},
|
|
90
|
+
"amazon-bedrock/jp.anthropic.claude-sonnet-5-5": {
|
|
91
|
+
rationale: "Sonnet 5.5 catalog addition (#6111); not yet curated",
|
|
92
|
+
},
|
|
93
|
+
"amazon-bedrock/us.anthropic.claude-sonnet-5-5": {
|
|
94
|
+
rationale: "Sonnet 5.5 catalog addition (#6111); not yet curated",
|
|
95
|
+
},
|
|
78
96
|
"kiro/claude-opus-5-5": { rationale: "post-rebase catalog addition from dev; not yet curated" },
|
|
79
97
|
"kiro/claude-opus-5.5": { rationale: "post-rebase catalog addition from dev; not yet curated" },
|
|
80
98
|
"alibaba-token-plan/qwen3.8-flash": { rationale: "post-rebase catalog addition from dev; not yet curated" },
|
|
@@ -652,6 +652,9 @@ function assertRegistryIdentityPolicy(value: unknown): void {
|
|
|
652
652
|
}
|
|
653
653
|
|
|
654
654
|
function assertCanonicalUnicode(value: string): void {
|
|
655
|
+
// Native well-formedness check first; the scan below only runs to name the
|
|
656
|
+
// offending surrogate when the string is malformed.
|
|
657
|
+
if (value.isWellFormed()) return;
|
|
655
658
|
for (let index = 0; index < value.length; index++) {
|
|
656
659
|
const code = value.charCodeAt(index);
|
|
657
660
|
if (code >= 0xd800 && code <= 0xdbff) {
|
|
@@ -665,6 +668,18 @@ function assertCanonicalUnicode(value: string): void {
|
|
|
665
668
|
}
|
|
666
669
|
}
|
|
667
670
|
|
|
671
|
+
/** Characters JSON.stringify escapes in strings (lone surrogates are rejected earlier). */
|
|
672
|
+
const JSON_STRING_ESCAPES = /["\\\u0000-\u001f]/;
|
|
673
|
+
|
|
674
|
+
/**
|
|
675
|
+
* Quote a string exactly as JSON.stringify does. Registry documents are almost
|
|
676
|
+
* entirely plain ASCII identifiers, so skipping the per-call JSON.stringify for
|
|
677
|
+
* strings with nothing to escape removes most of the serializer's cost.
|
|
678
|
+
*/
|
|
679
|
+
function quoteCanonicalString(value: string): string {
|
|
680
|
+
return JSON_STRING_ESCAPES.test(value) ? JSON.stringify(value) : `"${value}"`;
|
|
681
|
+
}
|
|
682
|
+
|
|
668
683
|
function serializeCanonicalJson(value: unknown, stack: Set<object>): string {
|
|
669
684
|
if (value === null) return "null";
|
|
670
685
|
if (typeof value === "boolean") return value ? "true" : "false";
|
|
@@ -674,7 +689,7 @@ function serializeCanonicalJson(value: unknown, stack: Set<object>): string {
|
|
|
674
689
|
}
|
|
675
690
|
if (typeof value === "string") {
|
|
676
691
|
assertCanonicalUnicode(value);
|
|
677
|
-
return
|
|
692
|
+
return quoteCanonicalString(value);
|
|
678
693
|
}
|
|
679
694
|
if (typeof value !== "object") throw new TypeError(`Canonical JSON rejects ${typeof value}.`);
|
|
680
695
|
if (stack.has(value)) throw new TypeError("Canonical JSON rejects cycles.");
|
|
@@ -694,7 +709,7 @@ function serializeCanonicalJson(value: unknown, stack: Set<object>): string {
|
|
|
694
709
|
const entries: string[] = [];
|
|
695
710
|
for (const key of Object.keys(record).sort()) {
|
|
696
711
|
assertCanonicalUnicode(key);
|
|
697
|
-
entries.push(`${
|
|
712
|
+
entries.push(`${quoteCanonicalString(key)}:${serializeCanonicalJson(record[key], stack)}`);
|
|
698
713
|
}
|
|
699
714
|
return `{${entries.join(",")}}`;
|
|
700
715
|
} finally {
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
export { resolveModelProfileName } from "./model-profile-contract";
|
|
18
18
|
|
|
19
19
|
import {
|
|
20
|
+
createRegistrySelectorIndex,
|
|
20
21
|
GJC_MODEL_ASSIGNMENT_TARGETS,
|
|
21
22
|
type GjcModelAssignmentTargetId,
|
|
22
23
|
isAuthenticated,
|
|
@@ -675,6 +676,7 @@ export async function resolveMissingSessionModelRecovery(options: {
|
|
|
675
676
|
options.skips.every(skip => skip.reason === "unknown_model");
|
|
676
677
|
if (!allSelectorsUnknown) return undefined;
|
|
677
678
|
const fullCatalog = options.modelRegistry.getAll();
|
|
679
|
+
const catalogIndex = createRegistrySelectorIndex(fullCatalog);
|
|
678
680
|
const savedSelectorsMissingFromCatalog = resolveConfiguredModelPatterns(
|
|
679
681
|
options.defaultEntries,
|
|
680
682
|
options.settings,
|
|
@@ -688,7 +690,7 @@ export async function resolveMissingSessionModelRecovery(options: {
|
|
|
688
690
|
}).model
|
|
689
691
|
)
|
|
690
692
|
return false;
|
|
691
|
-
return !registrySelectorResolvesToModel(selector,
|
|
693
|
+
return !registrySelectorResolvesToModel(selector, catalogIndex);
|
|
692
694
|
});
|
|
693
695
|
const savedConcreteDefault = options.savedDefault ? parseModelString(options.savedDefault) : undefined;
|
|
694
696
|
const savedConcreteDefaultMissingFromCatalog =
|
|
@@ -93,6 +93,13 @@ export const BUILTIN_MODEL_PROFILES: readonly ModelProfileDefinition[] = [
|
|
|
93
93
|
critic: "openai-codex/gpt-6-sol:max",
|
|
94
94
|
architect: "openai-codex/gpt-6-sol:xhigh",
|
|
95
95
|
}),
|
|
96
|
+
profile("codex-sol61", ["openai-codex"], {
|
|
97
|
+
default: "openai-codex/gpt-6.1-sol:medium",
|
|
98
|
+
executor: "openai-codex/gpt-6.1-sol:medium",
|
|
99
|
+
planner: "openai-codex/gpt-6.1-sol:high",
|
|
100
|
+
critic: "openai-codex/gpt-6.1-sol:xhigh",
|
|
101
|
+
architect: "openai-codex/gpt-6-astra:xhigh",
|
|
102
|
+
}),
|
|
96
103
|
profile("lunamaxxing", ["openai-codex"], {
|
|
97
104
|
default: "openai-codex/gpt-6-luna:medium",
|
|
98
105
|
executor: "openai-codex/gpt-6-luna:xhigh",
|
|
@@ -256,14 +263,14 @@ export const BUILTIN_MODEL_PROFILES: readonly ModelProfileDefinition[] = [
|
|
|
256
263
|
}),
|
|
257
264
|
profile("claude-opus", ["anthropic"], {
|
|
258
265
|
default: ["anthropic/claude-opus-5-5:medium", "anthropic/claude-opus-4-6:xhigh"],
|
|
259
|
-
executor: "anthropic/claude-sonnet-5",
|
|
266
|
+
executor: "anthropic/claude-sonnet-5-5",
|
|
260
267
|
planner: ["anthropic/claude-opus-5-5:medium", "anthropic/claude-opus-4-6:low"],
|
|
261
268
|
critic: ["anthropic/claude-opus-5-5:high", "anthropic/claude-opus-4-6:high"],
|
|
262
269
|
architect: ["anthropic/claude-opus-5-5:high", "anthropic/claude-opus-4-6:xhigh"],
|
|
263
270
|
}),
|
|
264
271
|
profile("claude-fable", ["anthropic"], {
|
|
265
272
|
default: "anthropic/claude-fable-5-1:xhigh",
|
|
266
|
-
executor: "anthropic/claude-sonnet-5",
|
|
273
|
+
executor: "anthropic/claude-sonnet-5-5",
|
|
267
274
|
planner: "anthropic/claude-fable-5-1:low",
|
|
268
275
|
critic: "anthropic/claude-fable-5-1:high",
|
|
269
276
|
architect: "anthropic/claude-fable-5-1:xhigh",
|
|
@@ -491,7 +498,7 @@ export const BUILTIN_MODEL_PROFILES: readonly ModelProfileDefinition[] = [
|
|
|
491
498
|
profile("opus-codex", ["anthropic", "openai-codex"], {
|
|
492
499
|
default: "anthropic/claude-opus-5-5:medium",
|
|
493
500
|
executor: "openai-codex/gpt-5.6-terra:low",
|
|
494
|
-
planner: "anthropic/claude-sonnet-5",
|
|
501
|
+
planner: "anthropic/claude-sonnet-5-5",
|
|
495
502
|
critic: "openai-codex/gpt-6-sol:xhigh",
|
|
496
503
|
architect: "openai-codex/gpt-6-sol:high",
|
|
497
504
|
}),
|
|
@@ -555,6 +562,7 @@ const PROFILE_PRESENTATION: Record<string, ModelProfilePresentation> = {
|
|
|
555
562
|
"codex-eco": { displayName: "Codex Eco", providerGroup: "CODEX" },
|
|
556
563
|
"codex-medium": { displayName: "Codex Medium", providerGroup: "CODEX" },
|
|
557
564
|
"codex-pro": { displayName: "Codex Pro", providerGroup: "CODEX" },
|
|
565
|
+
"codex-sol61": { displayName: "Codex Sol 6.1", providerGroup: "CODEX" },
|
|
558
566
|
lunamaxxing: { displayName: "LunaMaxxing", providerGroup: "CODEX" },
|
|
559
567
|
"astra-lite": { displayName: "ASTRA-Lite", providerGroup: "CODEX" },
|
|
560
568
|
"astra-default": { displayName: "ASTRA-Default", providerGroup: "CODEX" },
|
|
@@ -782,26 +782,53 @@ function registryModelMetadataWithoutApiSpecificFields(model: Model<Api>): Parti
|
|
|
782
782
|
return metadata;
|
|
783
783
|
}
|
|
784
784
|
|
|
785
|
-
|
|
785
|
+
/**
|
|
786
|
+
* Lowercased lookup keys for a model catalog, so selector resolution is a set
|
|
787
|
+
* lookup instead of a scan that re-lowercases every model id per selector.
|
|
788
|
+
*/
|
|
789
|
+
export interface RegistrySelectorIndex {
|
|
790
|
+
/** `id` and `provider/id`. */
|
|
791
|
+
exact: ReadonlySet<string>;
|
|
792
|
+
/** `id` alone. */
|
|
793
|
+
ids: ReadonlySet<string>;
|
|
794
|
+
/** Model ids per provider, for parsed `provider/id` selectors (nested, so no delimiter can collide). */
|
|
795
|
+
providerIds: ReadonlyMap<string, ReadonlySet<string>>;
|
|
796
|
+
/** Every `/`-delimited suffix of each id (`a/b/c` → `b/c`, `c`). */
|
|
797
|
+
idSuffixes: ReadonlySet<string>;
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
export function createRegistrySelectorIndex(models: readonly Model<Api>[]): RegistrySelectorIndex {
|
|
801
|
+
const exact = new Set<string>();
|
|
802
|
+
const ids = new Set<string>();
|
|
803
|
+
const providerIds = new Map<string, Set<string>>();
|
|
804
|
+
const idSuffixes = new Set<string>();
|
|
805
|
+
for (const model of models) {
|
|
806
|
+
const id = model.id.toLowerCase();
|
|
807
|
+
const provider = model.provider.toLowerCase();
|
|
808
|
+
ids.add(id);
|
|
809
|
+
exact.add(id);
|
|
810
|
+
exact.add(`${provider}/${id}`);
|
|
811
|
+
let providerModelIds = providerIds.get(provider);
|
|
812
|
+
if (!providerModelIds) {
|
|
813
|
+
providerModelIds = new Set<string>();
|
|
814
|
+
providerIds.set(provider, providerModelIds);
|
|
815
|
+
}
|
|
816
|
+
providerModelIds.add(id);
|
|
817
|
+
for (let slash = id.indexOf("/"); slash !== -1; slash = id.indexOf("/", slash + 1)) {
|
|
818
|
+
idSuffixes.add(id.slice(slash + 1));
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
return { exact, ids, providerIds, idSuffixes };
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
export function registrySelectorResolvesToModel(selector: string, index: RegistrySelectorIndex): boolean {
|
|
786
825
|
const normalizedSelector = selector.trim().toLowerCase();
|
|
787
|
-
if (
|
|
788
|
-
models.some(
|
|
789
|
-
model =>
|
|
790
|
-
model.id.toLowerCase() === normalizedSelector ||
|
|
791
|
-
`${model.provider}/${model.id}`.toLowerCase() === normalizedSelector,
|
|
792
|
-
)
|
|
793
|
-
)
|
|
794
|
-
return true;
|
|
826
|
+
if (index.exact.has(normalizedSelector)) return true;
|
|
795
827
|
const suffix = splitSelectorThinkingSuffix(normalizedSelector);
|
|
796
828
|
const baseSelector = suffix.thinkingLevel === undefined ? normalizedSelector : suffix.selector;
|
|
797
829
|
const parsed = parseModelString(baseSelector);
|
|
798
|
-
if (parsed)
|
|
799
|
-
|
|
800
|
-
model => model.provider.toLowerCase() === parsed.provider && model.id.toLowerCase() === parsed.id,
|
|
801
|
-
);
|
|
802
|
-
return models.some(
|
|
803
|
-
model => model.id.toLowerCase() === baseSelector || model.id.toLowerCase().endsWith(`/${baseSelector}`),
|
|
804
|
-
);
|
|
830
|
+
if (parsed) return index.providerIds.get(parsed.provider)?.has(parsed.id) ?? false;
|
|
831
|
+
return index.ids.has(baseSelector) || index.idSuffixes.has(baseSelector);
|
|
805
832
|
}
|
|
806
833
|
|
|
807
834
|
function filterMaterializedRegistryProfiles(
|
|
@@ -810,6 +837,7 @@ function filterMaterializedRegistryProfiles(
|
|
|
810
837
|
dynamicProviders: ReadonlySet<string>,
|
|
811
838
|
): Map<string, ModelProfileDefinition> {
|
|
812
839
|
const filtered = new Map<string, ModelProfileDefinition>();
|
|
840
|
+
const index = createRegistrySelectorIndex(models);
|
|
813
841
|
for (const [name, profile] of profiles) {
|
|
814
842
|
if (
|
|
815
843
|
profile.source === "registry" &&
|
|
@@ -818,7 +846,7 @@ function filterMaterializedRegistryProfiles(
|
|
|
818
846
|
return (
|
|
819
847
|
selectors.length > 0 &&
|
|
820
848
|
!selectors.some(selector => {
|
|
821
|
-
if (registrySelectorResolvesToModel(selector,
|
|
849
|
+
if (registrySelectorResolvesToModel(selector, index)) return true;
|
|
822
850
|
const suffix = splitSelectorThinkingSuffix(selector);
|
|
823
851
|
const parsed = parseModelString(suffix.thinkingLevel ? suffix.selector : selector);
|
|
824
852
|
return parsed !== undefined && dynamicProviders.has(parsed.provider);
|
|
@@ -2050,9 +2050,15 @@ export const SETTINGS_SCHEMA = {
|
|
|
2050
2050
|
ui: {
|
|
2051
2051
|
tab: "context",
|
|
2052
2052
|
label: "Compaction Threshold",
|
|
2053
|
-
description:
|
|
2053
|
+
description:
|
|
2054
|
+
"Percent threshold for context maintenance; the 300,000-token default cap applies only with adaptive mode disabled (except during context promotion)",
|
|
2054
2055
|
options: [
|
|
2055
|
-
{
|
|
2056
|
+
{
|
|
2057
|
+
value: "default",
|
|
2058
|
+
label: "Default",
|
|
2059
|
+
description:
|
|
2060
|
+
"300,000-token default cap applies only with adaptive mode disabled (except during context promotion)",
|
|
2061
|
+
},
|
|
2056
2062
|
{ value: "10", label: "10%", description: "Extremely early maintenance" },
|
|
2057
2063
|
{ value: "20", label: "20%", description: "Very early maintenance" },
|
|
2058
2064
|
{ value: "30", label: "30%", description: "Early maintenance" },
|