@gajae-code/coding-agent 0.15.4 → 0.15.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 +20 -1
- package/dist/types/cli/update-cli.d.ts +25 -5
- package/dist/types/config/autorouting-tier-map.d.ts +3 -0
- package/dist/types/extensibility/extensions/types.d.ts +3 -0
- package/dist/types/extensibility/shared-events.d.ts +2 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +8 -1
- package/dist/types/sdk/cli/session-cli.d.ts +42 -1
- package/dist/types/sdk/host/host.d.ts +2 -0
- package/dist/types/sdk/host/session-runtime.d.ts +4 -0
- package/dist/types/session/agent-session.d.ts +4 -2
- package/dist/types/session/internal/managed-session-storage.d.ts +4 -3
- package/dist/types/tools/path-utils.d.ts +5 -6
- package/dist/types/tools/tmux-self-injection-guard.d.ts +15 -0
- package/package.json +7 -7
- package/src/cli/update-cli.ts +102 -1
- package/src/commands/sdk.ts +5 -0
- package/src/config/autorouting-tier-map.ts +1 -0
- package/src/config/model-profiles.ts +5 -5
- package/src/config/model-registry.ts +10 -4
- package/src/coordinator-mcp/policy.ts +1 -1
- package/src/extensibility/extensions/types.ts +3 -0
- package/src/extensibility/shared-events.ts +2 -0
- package/src/gjc-runtime/launch-worktree.ts +44 -7
- package/src/gjc-runtime/ralplan-runtime.ts +14 -1
- package/src/gjc-runtime/tmux-sessions.ts +145 -48
- package/src/internal-urls/docs-index.generated.ts +7 -7
- package/src/modes/prompt-action-autocomplete.ts +26 -47
- package/src/sdk/bus/chat-daemon-control.ts +2 -2
- package/src/sdk/bus/kind-aware-reconciliation.ts +10 -2
- package/src/sdk/bus/prompt-reconciliation.ts +5 -2
- package/src/sdk/bus/telegram-daemon-contract.ts +8 -1
- package/src/sdk/cli/session-cli.ts +149 -4
- package/src/sdk/host/host.ts +7 -0
- package/src/sdk/host/session-runtime.test.ts +821 -22
- package/src/sdk/host/session-runtime.ts +669 -67
- package/src/sdk/router/session-router.ts +69 -15
- package/src/session/agent-session.ts +232 -15
- package/src/session/internal/managed-session-storage.ts +64 -26
- package/src/session/session-manager.ts +26 -8
- package/src/tools/bash.ts +14 -0
- package/src/tools/path-utils.ts +77 -29
- package/src/tools/tmux-self-injection-guard.ts +359 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,25 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.15.5] - 2026-08-29
|
|
6
|
+
|
|
7
|
+
- Bash now blocks tmux input commands that resolve to the agent's own pane before spawning the shell, including direct pane targets, session aliases, explicit socket forms, shell wrappers, and inspected script indirection. Read-only inspection and cross-session orchestration remain available, while blocked input cannot reach the transcript as a user turn. (#5039)
|
|
8
|
+
- Saved-session staged model-selection appends now re-authenticate metadata-only drift on the same managed transcript object using its unchanged inode, link count, length, and content digest. A changed successor still fails closed, so native Windows persistence no longer turns routine timestamp updates into `managed_append_identity_mismatch` setup failures.
|
|
9
|
+
- The GLM tiers now spend `zai/glm-5.3-flash` on the high-volume lanes and keep judgment roles on `zai/glm-5.3`: `glm-eco` moves default, executor, and planner to `glm-5.3-flash:low` (critic and architect stay `glm-5.3:high`), `glm-medium` swaps only the executor to `glm-5.3-flash:low`, and `glm-pro` swaps the executor to `glm-5.3-flash:high`. GLM-5.3-Flash also joins the bundled `zai` catalog with GLM-5.3's text contract pinned in `applyGeneratedModelPolicy` (1M context, 128K output, always-on `low`/`high`/`max` reasoning effort), because models.dev currently lists it with null limits and a catalog regeneration would otherwise degrade it to the unknown-window fallback; it is skip-listed in the autorouting tier map as not yet curated.
|
|
10
|
+
- A content-free generic OpenAI Responses capacity overload no longer ends the turn under the default retry configuration. OpenAI reports it inside an HTTP 200 terminal envelope (`response.failed`, or `response.completed` with a `failed` status), so the turn surfaced `server_is_overloaded: …` and went idle for a failure the provider says to retry. The bare-default admission now also accepts that exact typed code, read only from transport facts and never from prose: the failed attempt must be on the `openai-responses` API, carry `openaiErrorCode` and `providerCode` equal to `server_is_overloaded` (matched case-sensitively) and no other transport fact (no status, no retained retry header, no second typed code, no error kind, no request size, first-event timing, endpoint class, or provider retry ceiling), and carry no assistant text, thinking, or tool call. Under bare defaults, an overload that follows observable work earlier in the same run is still terminal, `retry.enabled: false` still surfaces immediately, and the Codex and Anthropic overload admissions are unchanged. (#5018)
|
|
11
|
+
- A metadata-only touch of the managed transcript file (AV scanning, indexing, backup sync — anything that bumps `mtimeNs`/`ctimeNs` without changing bytes) no longer crashes an interactive session with an unhandled `managed_append_identity_mismatch` rejection. `appendExpectedIdentitySync` compared only `dev`/`ino`/`size`/`mtimeNs`/`ctimeNs` against the persisted expectation, so a present-but-drifted predecessor was rethrown as a genuine successor and killed the process at the next prompt. The append receipt now carries the content digest the append path already computes (`append_managed` binds `sha256` to the final identity), `#captureManagedPersistIdentity` persists that descriptor-bound digest with the expectation, and a mismatching expectation is re-authenticated: identical bytes on the same unlinked file object with an unchanged link count refresh the expectation and proceed, while a changed inode, length, link count, or digest keeps failing closed so a concurrent successor is never overwritten. Regression-tested with two consecutive drifts, an intact successor guarantee, and an interactive input-loop containment test. (#4892)
|
|
12
|
+
- Behavior change: this overload is now treated as a bounded provider-overload replay under explicit `retry.*` settings as well. It previously fell into the unbounded transient-prose retry class (its message matches `overloaded`), so a configured session retried it without limit; it now stops at `retry.maxRetries` with the existing capped exponential backoff, the same bound bare defaults use. (#5018)
|
|
13
|
+
- Managed fallback behavior is preserved for the typed Responses overload: the new transport facts do not change the agent loop's managed transaction handling or the managed chain's retry/advance policy for this failure (the `server` classification remains a session-side classifier only). (#5018)
|
|
14
|
+
- A single refused notification publication no longer kills a live session's chat mirroring for the rest of that session. `SessionRouter.#dispatchNotificationFrame` treated any rejection from the provider hook as terminal and called `#detachNotification(attached, "cancelled")`, which latches `notificationCancelled` on the `AttachedSession`; only a new attachment clears that latch, and a session that keeps running never gets one. The Telegram daemon throws exactly such a per-frame rejection from `#failPublicationPreSend` (for example `Telegram publication rejected before send: trusted attachment lease is stale`) when its topic or attachment lease momentarily fails `#leaseAllows` while another session attaches or detaches, so one transient authority flip permanently silenced that session's topic while every other session kept delivering — the user-visible symptom being Telegram that "works sometimes". Measured on one install over a single day: 20 such rejections across 20 distinct sessions, exactly one each, and 19 of the 20 never emitted another notification frame afterwards (their topic lease heartbeat stopped one to five seconds after the rejection, because the detach removed the endpoint from the daemon). Dispatch now tolerates a bounded run of consecutive local failures before conceding, mirroring `DELIVERY_ATTEMPT_LIMIT` on the ordered-delivery path in the same file: the refused frame is still dropped (the provider has already settled that publication as rejected on its own side), the next delivered frame resets the run, and a provider that never accepts anything is still cancelled after the limit. The `NOTIFICATION_WORK_TIMEOUT` path is deliberately unchanged — a hung provider is a different failure class and keeps its immediate detach — and core attachment authority, replay, lease renewal, and every other detach reason (`removed`, `replaced`, `replaced_same_generation`) are untouched.
|
|
15
|
+
- `gjc update` now recognizes an already verified standalone binary during package-manager shim migration, avoiding repeated downloads and post-update work while clearly explaining the required PATH ordering.
|
|
16
|
+
|
|
17
|
+
- GJC-managed launch worktrees now default to `<repo>/.worktrees/<slug>` instead of a `<repo>.gajae-code-worktrees` sibling beside the checkout, keeping each repository's isolated branches in one predictable local folder. Launch fails with an actionable diagnostic unless an in-repository bucket is ignored by Git, preventing nested worktrees from polluting the source checkout; external `GJC_WORKTREE_DIR` buckets and already registered sibling worktrees are unchanged.
|
|
18
|
+
- Launch worktree reuse now revalidates Git-registered paths before adoption and reports locked or replaced stale entries instead of running Git commands in an unavailable or occupied directory; coordinator policy and Stream Deck project grouping use the repository-local default while retaining legacy sibling-path recognition.
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
|
|
22
|
+
- A one-shot `gjc sdk` command no longer idles for five seconds after its response is written. `SessionRouter` bounded four waits with `Bun.sleep` inside `Promise.race`; the race settles on the winner but does not cancel the loser, and a pending `Bun.sleep` keeps the event loop alive, so work that finished in milliseconds still owed the rest of the budget before the process could exit. `gjc sdk session list` wrote its result at 0.50s and exited at 5.51s. The startup attach budget, shutdown timeout, bounded notification work, and replay backoff now use the existing `cancellableSleep` helper with an `AbortController` cancelled once the race settles, and the reconcile interval is unref'd so a Router started without a matching `stop()` does not hold a host process open. The same command now exits at 1.65s, and every command routed through `withRouter` (`session.list`, `session.create`, `session.delete`, `session.fork`, `turn.prompt`, `turn.result`, `queue.*`, `compaction.*`) is affected.
|
|
23
|
+
|
|
5
24
|
## [0.15.4] - 2026-08-29
|
|
6
25
|
|
|
7
26
|
- The `ask` tool now rejects empty or whitespace-only custom answers on every local and remote input path. Remote invalid answers receive a visible explanation and are retried at most three times with an event-loop yield between attempts before the ask is cancelled; local empty custom input is never returned as a valid answer. (#5001)
|
|
@@ -41,6 +60,7 @@
|
|
|
41
60
|
- Foreground activity animation now uses layout-only TUI repaints, avoiding repeated reconstruction of unchanged transcript content during long sessions.
|
|
42
61
|
- Notification adapters can now opt into one live representation per native effect: adapters that accept a positioned event are excluded from its matching raw fan-out, while ordinary direct SDK and raw-only legacy subscribers retain their existing delivery. Telegram opts in, preventing turn output, tool activity, and reasoning summaries from rendering twice after #4570; acknowledged terminal shutdown delivery is unchanged.
|
|
43
62
|
- Lean notifications no longer replay a retained completion receipt at idle when identical text was already delivered immediately before an autonomous `ask`; distinct receipts and other settlement windows remain preserved.
|
|
63
|
+
- SDK prompts now publish a bounded structured terminal failure when a provider immediately returns HTTP 402 or 429. The accepted receipt remains non-terminal until the correlated `agent_end`, while `turn.result`/`turn.prompt_status` no longer strand the prompt as `in_flight`; replacement turns remain independently abortable and recoverable. (#4941)
|
|
44
64
|
|
|
45
65
|
## [0.15.2] - 2026-08-25
|
|
46
66
|
|
|
@@ -49,7 +69,6 @@
|
|
|
49
69
|
- Version 0.15.1 was tagged but never published: release automation failed while deriving release notes, before any package reached npm. Everything listed under `## [0.15.1]` below ships in this release.
|
|
50
70
|
|
|
51
71
|
## [0.15.1] - 2026-08-25
|
|
52
|
-
|
|
53
72
|
### Added
|
|
54
73
|
|
|
55
74
|
- Added `/language [en|ko]`, the interactive slash command for the persisted `ui.language` selection. Without arguments it reports the current language; with a canonical code, locale tag (`en-US`, `ko-KR`), endonym (`한국어`), English name, or common aliases (`eng`, `kr`, `kor`) it persists the canonical `en`/`ko` value through settings and confirms in the selected language. An unsupported value is rejected with the available list and changes nothing. See `docs/ui-language.md`.
|
|
@@ -53,6 +53,11 @@ export type PackageManagerTarget = {
|
|
|
53
53
|
manager: "npm";
|
|
54
54
|
packageName: string;
|
|
55
55
|
};
|
|
56
|
+
type MigrationUpdateTarget = {
|
|
57
|
+
method: "migrate";
|
|
58
|
+
path: string;
|
|
59
|
+
previousPath?: string;
|
|
60
|
+
};
|
|
56
61
|
export type UpdateTarget = {
|
|
57
62
|
method: "bun";
|
|
58
63
|
} | {
|
|
@@ -61,11 +66,7 @@ export type UpdateTarget = {
|
|
|
61
66
|
} | {
|
|
62
67
|
method: "binary";
|
|
63
68
|
path: string;
|
|
64
|
-
} |
|
|
65
|
-
method: "migrate";
|
|
66
|
-
path: string;
|
|
67
|
-
previousPath?: string;
|
|
68
|
-
};
|
|
69
|
+
} | MigrationUpdateTarget;
|
|
69
70
|
type PathPlatform = NodeJS.Platform;
|
|
70
71
|
type PackageExists = (packageName: string, packageRoot: string) => boolean;
|
|
71
72
|
export declare function resolveUpdateMethodForTest(ompPath: string, bunBinDir: string | undefined): "bun" | "npm" | "binary";
|
|
@@ -85,6 +86,24 @@ export declare function resolveGjcPathForTest(options: {
|
|
|
85
86
|
whichPath: string | undefined;
|
|
86
87
|
}): string | undefined;
|
|
87
88
|
export declare function parseReportedVersionForTest(output: string): string | undefined;
|
|
89
|
+
interface MigrationTargetVerificationOptions {
|
|
90
|
+
runtimePath: string;
|
|
91
|
+
verifyChecksum: () => Promise<void>;
|
|
92
|
+
verifyRuntime: () => Promise<InstalledVersionVerification>;
|
|
93
|
+
}
|
|
94
|
+
interface MigrationChecksumOptions {
|
|
95
|
+
tag: string;
|
|
96
|
+
assetName: string;
|
|
97
|
+
filePath: string;
|
|
98
|
+
}
|
|
99
|
+
type MigrationChecksumVerifier = (options: MigrationChecksumOptions) => Promise<unknown>;
|
|
100
|
+
export declare function verifyMigrationTargetForTest(options: MigrationTargetVerificationOptions): Promise<InstalledVersionVerification>;
|
|
101
|
+
export declare function verifyMigrationTargetAdapterForTest(options: {
|
|
102
|
+
release: Pick<ReleaseInfo, "tag" | "version">;
|
|
103
|
+
runtimePath: string;
|
|
104
|
+
verifyChecksum: MigrationChecksumVerifier;
|
|
105
|
+
verifyRuntime: (expectedVersion: string, runtimePath: string) => Promise<InstalledVersionVerification>;
|
|
106
|
+
}): Promise<InstalledVersionVerification>;
|
|
88
107
|
export declare function formatBinaryDownloadFailureMessageForTest(binaryName: string, url: string, status: string | number, platform?: NodeJS.Platform, registryNote?: string): string;
|
|
89
108
|
export declare function buildReleaseBinaryUrlForTest(version: string, platform?: NodeJS.Platform, arch?: string): string;
|
|
90
109
|
export declare function formatManualUpdateInstructionsForTest(platform?: NodeJS.Platform): string;
|
|
@@ -123,6 +142,7 @@ export declare function runBinaryUpdateFlow(targetPath: string, url: string, exp
|
|
|
123
142
|
export interface UpdateCommandDependencies {
|
|
124
143
|
getLatestRelease?: (options?: LatestReleaseLookupOptions) => Promise<ReleaseInfo>;
|
|
125
144
|
resolveUpdateTarget?: () => Promise<UpdateTarget>;
|
|
145
|
+
verifyMigrationTarget?: (release: ReleaseInfo, runtimePath: string) => Promise<InstalledVersionVerification>;
|
|
126
146
|
performUpdate?: (target: UpdateTarget, expectedVersion: string, registry?: string) => Promise<InstalledVersionVerification | undefined>;
|
|
127
147
|
refreshInstalledDefaultSkills?: () => Promise<void>;
|
|
128
148
|
settings?: () => Promise<Settings>;
|
|
@@ -250,6 +250,9 @@ export declare const TIER_MAP_SKIP_LIST: {
|
|
|
250
250
|
readonly "zai/glm-5.3": {
|
|
251
251
|
readonly rationale: "post-rebase catalog addition from dev; not yet curated";
|
|
252
252
|
};
|
|
253
|
+
readonly "zai/glm-5.3-flash": {
|
|
254
|
+
readonly rationale: "post-feature catalog addition; not yet curated";
|
|
255
|
+
};
|
|
253
256
|
readonly "cursor/gemini-3.7-flash-high": {
|
|
254
257
|
readonly rationale: "post-rebase catalog addition from dev; not yet curated";
|
|
255
258
|
};
|
|
@@ -344,6 +344,7 @@ export interface ExtensionContext {
|
|
|
344
344
|
cleanedArgs?: string;
|
|
345
345
|
}) => void;
|
|
346
346
|
preflightSignal?: AbortSignal;
|
|
347
|
+
sdkRunToken?: string;
|
|
347
348
|
}): Promise<unknown>;
|
|
348
349
|
setPlanMode?(on: boolean): unknown;
|
|
349
350
|
operateGoal?(op: "create" | "get" | "resume" | "pause" | "complete" | "drop", objective?: string): Promise<unknown>;
|
|
@@ -492,6 +493,8 @@ export interface AgentStartEvent extends SharedAgentStartEvent {
|
|
|
492
493
|
export interface AgentFailedEvent {
|
|
493
494
|
type: "agent_failed";
|
|
494
495
|
error: AgentFailureDiagnostic;
|
|
496
|
+
/** Internal SDK queue-owner binding for exact lifecycle attribution. */
|
|
497
|
+
sdkRunToken?: string;
|
|
495
498
|
/** Attempt correlation for the failing run, when scoped (matches the
|
|
496
499
|
* agent_start/agent_end scope contract). */
|
|
497
500
|
scope?: AttemptScope;
|
|
@@ -148,6 +148,8 @@ export interface AgentStartEvent {
|
|
|
148
148
|
export interface AgentEndEvent {
|
|
149
149
|
type: "agent_end";
|
|
150
150
|
messages: AgentMessage[];
|
|
151
|
+
/** Internal SDK queue-owner binding for exact lifecycle attribution. */
|
|
152
|
+
sdkRunToken?: string;
|
|
151
153
|
/** Indicates whether the loop ended normally, suspended, cancelled, or entered maintenance. */
|
|
152
154
|
stopReason?: "completed" | "paused" | "cancelled" | "maintenance";
|
|
153
155
|
/** Present for maintenance checkpoints; non-aborted checkpoints are not terminal. */
|
|
@@ -278,8 +278,15 @@ export declare const SDK_LIFECYCLE_ROUTER_PROTOCOL_VERSION = 1;
|
|
|
278
278
|
* Generation 177 consumes an exact same-window lean settlement receipt only
|
|
279
279
|
* after its autonomous ask lead-in publishes successfully, preventing idle
|
|
280
280
|
* replay without changing the serving protocol.
|
|
281
|
+
* Generation 178: idle publication waits for positioned identity delivery to
|
|
282
|
+
* cross the native writer barrier before the independent broadcast lane.
|
|
283
|
+
* Generation 179 tolerates a bounded consecutive run of refused notification
|
|
284
|
+
* publications in the router instead of cancelling the subscription on the
|
|
285
|
+
* first refusal, so an owner still running the generation-178 router keeps
|
|
286
|
+
* killing a session's mirroring after one transient rejection and must be
|
|
287
|
+
* replaced across this upgrade.
|
|
281
288
|
*/
|
|
282
|
-
export declare const DAEMON_GENERATION =
|
|
289
|
+
export declare const DAEMON_GENERATION = 179;
|
|
283
290
|
/**
|
|
284
291
|
* Serving-compatibility boundary for daemon lifecycle requests. Epoch 7
|
|
285
292
|
* requires durable publication receipts and acknowledged attachment dispatch,
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { SessionRouter } from "../router";
|
|
2
|
-
|
|
2
|
+
import { type SdkSessionRowV1 } from "./rows";
|
|
3
|
+
export type SdkSessionCliAction = "list" | "inspect" | "send" | "status" | "tail" | "retire" | "global" | "raw" | "control" | "query" | "global";
|
|
4
|
+
export type SdkSessionListScope = "repo" | "cwd" | "worktree" | "all";
|
|
3
5
|
export type SdkSessionCliRawKind = "control" | "query" | "global";
|
|
4
6
|
export interface SdkSessionCliArgs {
|
|
5
7
|
action?: string;
|
|
@@ -13,6 +15,7 @@ export interface SdkSessionCliArgs {
|
|
|
13
15
|
jsonInputFile?: string;
|
|
14
16
|
jsonInputStdin?: boolean;
|
|
15
17
|
idempotencyKey?: string;
|
|
18
|
+
scope?: string;
|
|
16
19
|
confirm?: boolean;
|
|
17
20
|
cursor?: string;
|
|
18
21
|
wait?: boolean;
|
|
@@ -27,6 +30,43 @@ export interface RetainedTranscriptTailReader {
|
|
|
27
30
|
readonly size: number;
|
|
28
31
|
readRange(start: number, end: number): Promise<Uint8Array>;
|
|
29
32
|
}
|
|
33
|
+
interface WorkspaceIdentity {
|
|
34
|
+
/** Canonical workspace path (realpath when it exists, lexical resolve otherwise). */
|
|
35
|
+
canonicalPath: string;
|
|
36
|
+
/** Canonical containing worktree root; null outside a Git checkout. */
|
|
37
|
+
repoRoot: string | null;
|
|
38
|
+
/** Canonical Git common dir; shared by the main checkout and linked worktrees. */
|
|
39
|
+
commonDir: string | null;
|
|
40
|
+
}
|
|
41
|
+
export interface SdkSessionListSelection {
|
|
42
|
+
scope: SdkSessionListScope;
|
|
43
|
+
selection: WorkspaceIdentity;
|
|
44
|
+
/** Bounded, credential-free descriptor included in list output. */
|
|
45
|
+
descriptor: {
|
|
46
|
+
scope: SdkSessionListScope;
|
|
47
|
+
path: string;
|
|
48
|
+
worktreeRoot?: string;
|
|
49
|
+
commonDir?: string;
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
/** Parses `--scope`; missing means `repo`, anything else invalid is a usage error. */
|
|
53
|
+
export declare function parseSessionListScope(value: string | undefined): SdkSessionListScope;
|
|
54
|
+
/**
|
|
55
|
+
* Resolves the list selection from `--repo` (default: process cwd) under the
|
|
56
|
+
* effective scope. Outside Git, `repo`/`worktree` fail typed and actionable —
|
|
57
|
+
* they never broaden to `all`; `cwd` remains an exact canonical match.
|
|
58
|
+
*/
|
|
59
|
+
export declare function resolveSessionListSelection(scope: SdkSessionListScope, repoArg: string | undefined): Promise<SdkSessionListSelection>;
|
|
60
|
+
/**
|
|
61
|
+
* Filters fully traversed broker rows by the selection scope. Row workspaces
|
|
62
|
+
* are canonicalized and cached per distinct locator; `repo` matches the shared
|
|
63
|
+
* common dir across the main checkout and linked worktrees, `worktree` the
|
|
64
|
+
* containing worktree only, and `cwd` the exact canonical workspace.
|
|
65
|
+
*/
|
|
66
|
+
export declare function filterSessionRowsByScope(rows: readonly SdkSessionRowV1[], scope: SdkSessionListScope, selection: SdkSessionListSelection): Promise<{
|
|
67
|
+
sessions: SdkSessionRowV1[];
|
|
68
|
+
warnings: string[];
|
|
69
|
+
}>;
|
|
30
70
|
/** Creates a lowercase ULID operation reference for prompt reconciliation. */
|
|
31
71
|
export declare function createOperationRef(now?: number): string;
|
|
32
72
|
/** Exported for tests: each status poll must stay inside the caller's wait window. */
|
|
@@ -39,3 +79,4 @@ export declare function scanRetainedTranscriptTail(reader: RetainedTranscriptTai
|
|
|
39
79
|
export declare function runTail(repo: string, agentDir: string, sessionId: string, args: SdkSessionCliArgs): Promise<unknown>;
|
|
40
80
|
/** Runs the broker-bound `gjc sdk session` command family without exposing endpoint credentials. */
|
|
41
81
|
export declare function runSdkSessionCli(args: SdkSessionCliArgs, writeOutput?: (value: unknown) => void, setExitCode?: (exitCode: 1 | 2) => void): Promise<void>;
|
|
82
|
+
export {};
|
|
@@ -36,6 +36,8 @@ export interface SessionSdkHostOptions extends HostEndpointAdapters {
|
|
|
36
36
|
query?: (connectionId: string, frame: SdkFrame) => unknown | Promise<unknown>;
|
|
37
37
|
/** Test/lifecycle seam invoked synchronously before fire-and-forget dispatch. */
|
|
38
38
|
onFrameAdmitted?: (connectionId: string, frame: SdkFrame) => void;
|
|
39
|
+
/** Synchronously rejects external input while a runtime is being replaced. */
|
|
40
|
+
onFrameAdmission?: (connectionId: string, frame: SdkFrame) => SdkFrame | undefined;
|
|
39
41
|
/** Best-effort diagnostic observation of accepted control/query frames. */
|
|
40
42
|
onRequest?: SdkRequestObserver;
|
|
41
43
|
/** Runs before a control response is sent; identity transitions use sendTerminal. */
|
|
@@ -175,6 +175,10 @@ export interface CreateSdkSessionRuntimeOptions {
|
|
|
175
175
|
terminalAbortSeams?: SdkOnlyTerminalAbortSeams;
|
|
176
176
|
/** Callback when a frame is admitted to the runtime (test harness). */
|
|
177
177
|
onFrameAdmitted?: () => void;
|
|
178
|
+
/** Test-only observation of a genuinely timed-out lifecycle drain. */
|
|
179
|
+
onLifecycleDrainTimeoutForTests?: () => void;
|
|
180
|
+
/** Test-only observation of bounded failure-diagnostic deduplication state. */
|
|
181
|
+
onFailureDiagnosticKeyCountForTests?: (count: number) => void;
|
|
178
182
|
}
|
|
179
183
|
export interface InvocationCorrelation {
|
|
180
184
|
commandId: string;
|
|
@@ -426,6 +426,8 @@ export interface PromptOptions {
|
|
|
426
426
|
}) => void;
|
|
427
427
|
/** Optional invocation-scoped cancellation fence used before an accepted skill starts execution. */
|
|
428
428
|
preflightSignal?: AbortSignal;
|
|
429
|
+
/** Internal SDK lifecycle owner token. */
|
|
430
|
+
sdkRunToken?: string;
|
|
429
431
|
}
|
|
430
432
|
/** Result from a handoff operation. */
|
|
431
433
|
export interface HandoffResult {
|
|
@@ -945,7 +947,7 @@ export declare class AgentSession {
|
|
|
945
947
|
/** Live SDK configuration values exposed through the session query surface. */
|
|
946
948
|
getSdkConfigItems(): Record<string, string>;
|
|
947
949
|
setPlanModeState(state: PlanModeState | undefined): void;
|
|
948
|
-
invokeSkill(name: string, args?: string, options?: Pick<PromptOptions, "onPreflightAccepted" | "onPreflightAcceptCommit" | "onSkillPrepared" | "preflightSignal">): Promise<{
|
|
950
|
+
invokeSkill(name: string, args?: string, options?: Pick<PromptOptions, "onPreflightAccepted" | "onPreflightAcceptCommit" | "onSkillPrepared" | "preflightSignal" | "sdkRunToken">): Promise<{
|
|
949
951
|
name: string;
|
|
950
952
|
path: string;
|
|
951
953
|
args?: string;
|
|
@@ -1000,7 +1002,7 @@ export declare class AgentSession {
|
|
|
1000
1002
|
* @throws Error if no model selected or no API key available (when not streaming)
|
|
1001
1003
|
*/
|
|
1002
1004
|
prompt(text: string, options?: PromptOptions): Promise<void>;
|
|
1003
|
-
promptCustomMessage<T = unknown>(message: Pick<CustomMessage<T>, "customType" | "content" | "display" | "details" | "attribution">, options?: Pick<PromptOptions, "streamingBehavior" | "toolChoice" | "followUpQueuePolicy" | "onPreflightAccepted" | "onPreflightAcceptCommit" | "preflightSignal">): Promise<void>;
|
|
1005
|
+
promptCustomMessage<T = unknown>(message: Pick<CustomMessage<T>, "customType" | "content" | "display" | "details" | "attribution">, options?: Pick<PromptOptions, "streamingBehavior" | "toolChoice" | "followUpQueuePolicy" | "onPreflightAccepted" | "onPreflightAcceptCommit" | "preflightSignal" | "sdkRunToken">): Promise<void>;
|
|
1004
1006
|
/**
|
|
1005
1007
|
* Queue a steering message to interrupt the agent mid-run.
|
|
1006
1008
|
*/
|
|
@@ -65,9 +65,8 @@ export interface ManagedFileSnapshot {
|
|
|
65
65
|
};
|
|
66
66
|
}
|
|
67
67
|
/**
|
|
68
|
-
* Descriptor identity of one managed file object.
|
|
69
|
-
* content
|
|
70
|
-
* re-reading the whole payload.
|
|
68
|
+
* Descriptor identity of one managed file object. Append receipts include a
|
|
69
|
+
* content digest when the append implementation has already computed it.
|
|
71
70
|
*/
|
|
72
71
|
export interface ManagedFileIdentity {
|
|
73
72
|
dev: bigint;
|
|
@@ -76,6 +75,7 @@ export interface ManagedFileIdentity {
|
|
|
76
75
|
size: number;
|
|
77
76
|
mtimeNs: bigint;
|
|
78
77
|
ctimeNs: bigint;
|
|
78
|
+
sha256?: string;
|
|
79
79
|
}
|
|
80
80
|
/**
|
|
81
81
|
* Post-operation evidence for one managed append. `descriptor` is the
|
|
@@ -91,6 +91,7 @@ export interface ManagedAppendReceipt {
|
|
|
91
91
|
export interface ManagedBoundedAppendExpectation {
|
|
92
92
|
readonly dev: string;
|
|
93
93
|
readonly ino: string;
|
|
94
|
+
readonly nlink: string;
|
|
94
95
|
readonly size: string;
|
|
95
96
|
readonly mtimeNs: string;
|
|
96
97
|
readonly ctimeNs: string;
|
|
@@ -8,12 +8,11 @@ export declare function splitPathAndSel(rawPath: string): {
|
|
|
8
8
|
/**
|
|
9
9
|
* Variant of {@link splitPathAndSel} for internal URLs (`scheme://...`).
|
|
10
10
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* skill name from its selector without making path-utils depend on skills.
|
|
11
|
+
* Selector-capable internal authorities use identifier-shaped resource names,
|
|
12
|
+
* so an authority-only `:<tail>` is an explicit read selector even when the
|
|
13
|
+
* selector is malformed. Skill authorities are the exception: namespaced
|
|
14
|
+
* skill names already contain a colon, so the active registry gets first
|
|
15
|
+
* refusal and the fallback recognizes a later colon as the selector boundary.
|
|
17
16
|
*/
|
|
18
17
|
export declare function splitInternalUrlSel(rawPath: string, options?: {
|
|
19
18
|
activeSkillNames?: readonly string[];
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface TmuxSelfInjectionResult {
|
|
2
|
+
block: boolean;
|
|
3
|
+
reason?: string;
|
|
4
|
+
}
|
|
5
|
+
export type TmuxPaneResolver = (options: {
|
|
6
|
+
socketArgs: string[];
|
|
7
|
+
target: string;
|
|
8
|
+
env: Record<string, string>;
|
|
9
|
+
}) => Promise<string | undefined>;
|
|
10
|
+
export interface TmuxSelfInjectionOptions {
|
|
11
|
+
env?: Record<string, string | undefined>;
|
|
12
|
+
cwd?: string;
|
|
13
|
+
resolvePaneId?: TmuxPaneResolver;
|
|
14
|
+
}
|
|
15
|
+
export declare function checkTmuxSelfInjection(command: string, options?: TmuxSelfInjectionOptions, depth?: number): Promise<TmuxSelfInjectionResult>;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@gajae-code/coding-agent",
|
|
4
|
-
"version": "0.15.
|
|
4
|
+
"version": "0.15.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",
|
|
@@ -56,12 +56,12 @@
|
|
|
56
56
|
"@agentclientprotocol/sdk": "1.3.0",
|
|
57
57
|
"@babel/parser": "^7.29.3",
|
|
58
58
|
"@mozilla/readability": "^0.6.0",
|
|
59
|
-
"@gajae-code/stats": "0.15.
|
|
60
|
-
"@gajae-code/agent-core": "0.15.
|
|
61
|
-
"@gajae-code/ai": "0.15.
|
|
62
|
-
"@gajae-code/natives": "0.15.
|
|
63
|
-
"@gajae-code/tui": "0.15.
|
|
64
|
-
"@gajae-code/utils": "0.15.
|
|
59
|
+
"@gajae-code/stats": "0.15.5",
|
|
60
|
+
"@gajae-code/agent-core": "0.15.5",
|
|
61
|
+
"@gajae-code/ai": "0.15.5",
|
|
62
|
+
"@gajae-code/natives": "0.15.5",
|
|
63
|
+
"@gajae-code/tui": "0.15.5",
|
|
64
|
+
"@gajae-code/utils": "0.15.5",
|
|
65
65
|
"@puppeteer/browsers": "^2.13.0",
|
|
66
66
|
"@types/turndown": "5.0.6",
|
|
67
67
|
"@xterm/headless": "^6.0.0",
|
package/src/cli/update-cli.ts
CHANGED
|
@@ -163,11 +163,12 @@ function isPathInDirectory(filePath: string, directoryPath: string): boolean {
|
|
|
163
163
|
}
|
|
164
164
|
|
|
165
165
|
export type PackageManagerTarget = { manager: "npm"; packageName: string };
|
|
166
|
+
type MigrationUpdateTarget = { method: "migrate"; path: string; previousPath?: string };
|
|
166
167
|
export type UpdateTarget =
|
|
167
168
|
| { method: "bun" }
|
|
168
169
|
| { method: "npm"; packageName: string }
|
|
169
170
|
| { method: "binary"; path: string }
|
|
170
|
-
|
|
|
171
|
+
| MigrationUpdateTarget;
|
|
171
172
|
|
|
172
173
|
type PathPlatform = NodeJS.Platform;
|
|
173
174
|
type PackageExists = (packageName: string, packageRoot: string) => boolean;
|
|
@@ -506,6 +507,73 @@ async function verifyInstalledRuntime(
|
|
|
506
507
|
}
|
|
507
508
|
}
|
|
508
509
|
|
|
510
|
+
interface MigrationTargetVerificationOptions {
|
|
511
|
+
runtimePath: string;
|
|
512
|
+
verifyChecksum: () => Promise<void>;
|
|
513
|
+
verifyRuntime: () => Promise<InstalledVersionVerification>;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
interface MigrationChecksumOptions {
|
|
517
|
+
tag: string;
|
|
518
|
+
assetName: string;
|
|
519
|
+
filePath: string;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
type MigrationChecksumVerifier = (options: MigrationChecksumOptions) => Promise<unknown>;
|
|
523
|
+
|
|
524
|
+
async function verifyMigrationTargetWith(
|
|
525
|
+
options: MigrationTargetVerificationOptions,
|
|
526
|
+
): Promise<InstalledVersionVerification> {
|
|
527
|
+
try {
|
|
528
|
+
await options.verifyChecksum();
|
|
529
|
+
} catch {
|
|
530
|
+
return { ok: false, path: options.runtimePath };
|
|
531
|
+
}
|
|
532
|
+
return await options.verifyRuntime();
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
async function verifyMigrationTarget(
|
|
536
|
+
release: Pick<ReleaseInfo, "tag" | "version">,
|
|
537
|
+
runtimePath: string,
|
|
538
|
+
verifyChecksum: MigrationChecksumVerifier = verifyDownloadedBinaryChecksum,
|
|
539
|
+
verifyRuntime: (
|
|
540
|
+
expectedVersion: string,
|
|
541
|
+
runtimePath: string,
|
|
542
|
+
) => Promise<InstalledVersionVerification> = verifyInstalledRuntime,
|
|
543
|
+
): Promise<InstalledVersionVerification> {
|
|
544
|
+
return await verifyMigrationTargetWith({
|
|
545
|
+
runtimePath,
|
|
546
|
+
verifyChecksum: async () => {
|
|
547
|
+
await verifyChecksum({
|
|
548
|
+
tag: release.tag,
|
|
549
|
+
assetName: getBinaryName(),
|
|
550
|
+
filePath: runtimePath,
|
|
551
|
+
});
|
|
552
|
+
},
|
|
553
|
+
verifyRuntime: async () => await verifyRuntime(release.version, runtimePath),
|
|
554
|
+
});
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
export async function verifyMigrationTargetForTest(
|
|
558
|
+
options: MigrationTargetVerificationOptions,
|
|
559
|
+
): Promise<InstalledVersionVerification> {
|
|
560
|
+
return await verifyMigrationTargetWith(options);
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
export async function verifyMigrationTargetAdapterForTest(options: {
|
|
564
|
+
release: Pick<ReleaseInfo, "tag" | "version">;
|
|
565
|
+
runtimePath: string;
|
|
566
|
+
verifyChecksum: MigrationChecksumVerifier;
|
|
567
|
+
verifyRuntime: (expectedVersion: string, runtimePath: string) => Promise<InstalledVersionVerification>;
|
|
568
|
+
}): Promise<InstalledVersionVerification> {
|
|
569
|
+
return await verifyMigrationTarget(
|
|
570
|
+
options.release,
|
|
571
|
+
options.runtimePath,
|
|
572
|
+
options.verifyChecksum,
|
|
573
|
+
options.verifyRuntime,
|
|
574
|
+
);
|
|
575
|
+
}
|
|
576
|
+
|
|
509
577
|
function printRestartGuidance(): void {
|
|
510
578
|
console.log(chalk.dim(`Restart ${APP_NAME} to use the new version`));
|
|
511
579
|
}
|
|
@@ -1062,6 +1130,7 @@ async function updateViaBinaryAt(
|
|
|
1062
1130
|
export interface UpdateCommandDependencies {
|
|
1063
1131
|
getLatestRelease?: (options?: LatestReleaseLookupOptions) => Promise<ReleaseInfo>;
|
|
1064
1132
|
resolveUpdateTarget?: () => Promise<UpdateTarget>;
|
|
1133
|
+
verifyMigrationTarget?: (release: ReleaseInfo, runtimePath: string) => Promise<InstalledVersionVerification>;
|
|
1065
1134
|
performUpdate?: (
|
|
1066
1135
|
target: UpdateTarget,
|
|
1067
1136
|
expectedVersion: string,
|
|
@@ -1265,6 +1334,24 @@ export function resolveUpdateDecision(options: {
|
|
|
1265
1334
|
return { install: true, kind: options.comparison > 0 ? "new-version" : "force" };
|
|
1266
1335
|
}
|
|
1267
1336
|
|
|
1337
|
+
function printVerifiedMigrationTarget(target: MigrationUpdateTarget, version: string): void {
|
|
1338
|
+
console.log(
|
|
1339
|
+
chalk.green(
|
|
1340
|
+
`${theme.status.success} Standalone ${APP_NAME} ${version} is already installed and verified at ${target.path}`,
|
|
1341
|
+
),
|
|
1342
|
+
);
|
|
1343
|
+
if (target.previousPath) {
|
|
1344
|
+
console.log(chalk.yellow(`${target.previousPath} shadows it on PATH.`));
|
|
1345
|
+
console.log(
|
|
1346
|
+
chalk.cyan(
|
|
1347
|
+
`The standalone directory ${path.dirname(target.path)} must precede the shim directory ${path.dirname(target.previousPath)} on PATH.`,
|
|
1348
|
+
),
|
|
1349
|
+
);
|
|
1350
|
+
return;
|
|
1351
|
+
}
|
|
1352
|
+
console.log(chalk.cyan(`Ensure the standalone directory ${path.dirname(target.path)} is on PATH.`));
|
|
1353
|
+
}
|
|
1354
|
+
|
|
1268
1355
|
export async function runUpdateCommand(
|
|
1269
1356
|
opts: UpdateCommandOptions,
|
|
1270
1357
|
deps: UpdateCommandDependencies = {},
|
|
@@ -1272,6 +1359,7 @@ export async function runUpdateCommand(
|
|
|
1272
1359
|
const channel = opts.channel ?? "stable";
|
|
1273
1360
|
const lookupRelease = deps.getLatestRelease ?? getLatestRelease;
|
|
1274
1361
|
const resolveTarget = deps.resolveUpdateTarget ?? resolveUpdateTarget;
|
|
1362
|
+
const verifyTarget = deps.verifyMigrationTarget ?? verifyMigrationTarget;
|
|
1275
1363
|
const update = deps.performUpdate ?? performUpdate;
|
|
1276
1364
|
const refreshDefaults = deps.refreshInstalledDefaultSkills ?? refreshInstalledDefaultSkills;
|
|
1277
1365
|
const exit = deps.exit ?? process.exit;
|
|
@@ -1323,6 +1411,19 @@ export async function runUpdateCommand(
|
|
|
1323
1411
|
migrate: target?.method === "migrate",
|
|
1324
1412
|
});
|
|
1325
1413
|
|
|
1414
|
+
if (target.method === "migrate" && decision.install && !opts.force) {
|
|
1415
|
+
const releaseLock = await acquireBinaryUpdateLock(target.path);
|
|
1416
|
+
try {
|
|
1417
|
+
const verification = await verifyTarget(release, target.path);
|
|
1418
|
+
if (verification.ok) {
|
|
1419
|
+
printVerifiedMigrationTarget(target, release.version);
|
|
1420
|
+
return;
|
|
1421
|
+
}
|
|
1422
|
+
} finally {
|
|
1423
|
+
await releaseLock();
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
|
|
1326
1427
|
if (!decision.install) {
|
|
1327
1428
|
console.log(chalk.green(`${theme.status.success} Already up to date`));
|
|
1328
1429
|
return;
|
package/src/commands/sdk.ts
CHANGED
|
@@ -838,6 +838,10 @@ class SdkSessionHelp extends Command {
|
|
|
838
838
|
description: "Workspace directory for saved-session resolution (default: current directory)",
|
|
839
839
|
}),
|
|
840
840
|
op: Flags.string({ description: "Raw control or global operation" }),
|
|
841
|
+
scope: Flags.string({
|
|
842
|
+
description: "list scope: repo (default), cwd, worktree, or all",
|
|
843
|
+
options: ["repo", "cwd", "worktree", "all"],
|
|
844
|
+
}),
|
|
841
845
|
query: Flags.string({ description: "Raw query name" }),
|
|
842
846
|
"json-input": Flags.string({ description: "SDK request JSON object" }),
|
|
843
847
|
"json-input-file": Flags.string({ description: "Read SDK request JSON from a 0600 file" }),
|
|
@@ -894,6 +898,7 @@ class SdkSessionCommand extends Command {
|
|
|
894
898
|
allEvents: Boolean(flagRec["all-events"]),
|
|
895
899
|
agentDir: flagRec["agent-dir"] as string | undefined,
|
|
896
900
|
repo: flagRec.repo as string | undefined,
|
|
901
|
+
scope: flagRec.scope as string | undefined,
|
|
897
902
|
});
|
|
898
903
|
}
|
|
899
904
|
}
|
|
@@ -146,6 +146,7 @@ export const TIER_MAP_SKIP_LIST = {
|
|
|
146
146
|
},
|
|
147
147
|
"vercel-ai-gateway/google/gemini-3.7-flash": { rationale: "post-rebase catalog addition from dev; not yet curated" },
|
|
148
148
|
"zai/glm-5.3": { rationale: "post-rebase catalog addition from dev; not yet curated" },
|
|
149
|
+
"zai/glm-5.3-flash": { rationale: "post-feature catalog addition; not yet curated" },
|
|
149
150
|
"cursor/gemini-3.7-flash-high": {
|
|
150
151
|
rationale: "post-rebase catalog addition from dev; not yet curated",
|
|
151
152
|
},
|
|
@@ -247,22 +247,22 @@ export const BUILTIN_MODEL_PROFILES: readonly ModelProfileDefinition[] = [
|
|
|
247
247
|
architect: "anthropic/claude-fable-5:xhigh",
|
|
248
248
|
}),
|
|
249
249
|
profile("glm-eco", ["zai"], {
|
|
250
|
-
default: "zai/glm-5.3:low",
|
|
251
|
-
executor: "zai/glm-5.3:low",
|
|
252
|
-
planner: "zai/glm-5.3:low",
|
|
250
|
+
default: "zai/glm-5.3-flash:low",
|
|
251
|
+
executor: "zai/glm-5.3-flash:low",
|
|
252
|
+
planner: "zai/glm-5.3-flash:low",
|
|
253
253
|
critic: "zai/glm-5.3:high",
|
|
254
254
|
architect: "zai/glm-5.3:high",
|
|
255
255
|
}),
|
|
256
256
|
profile("glm-medium", ["zai"], {
|
|
257
257
|
default: "zai/glm-5.3:high",
|
|
258
|
-
executor: "zai/glm-5.3:low",
|
|
258
|
+
executor: "zai/glm-5.3-flash:low",
|
|
259
259
|
planner: "zai/glm-5.3:high",
|
|
260
260
|
critic: "zai/glm-5.3:high",
|
|
261
261
|
architect: "zai/glm-5.3:max",
|
|
262
262
|
}),
|
|
263
263
|
profile("glm-pro", ["zai"], {
|
|
264
264
|
default: "zai/glm-5.3:max",
|
|
265
|
-
executor: "zai/glm-5.3:high",
|
|
265
|
+
executor: "zai/glm-5.3-flash:high",
|
|
266
266
|
planner: "zai/glm-5.3:high",
|
|
267
267
|
critic: "zai/glm-5.3:max",
|
|
268
268
|
architect: "zai/glm-5.3:max",
|
|
@@ -2154,7 +2154,9 @@ export class ModelRegistry {
|
|
|
2154
2154
|
});
|
|
2155
2155
|
if (localCompatResolvedKey) {
|
|
2156
2156
|
this.#customProviderApiKeys.set(providerName, localCompatResolvedKey);
|
|
2157
|
-
this.authStorage.setConfigApiKey(providerName, localCompatResolvedKey
|
|
2157
|
+
this.authStorage.setConfigApiKey(providerName, localCompatResolvedKey, {
|
|
2158
|
+
envSourced: !localOpenAICompat.apiKey,
|
|
2159
|
+
});
|
|
2158
2160
|
} else {
|
|
2159
2161
|
keylessProviders.add(providerName);
|
|
2160
2162
|
this.#optionalAuthProviders.add(providerName);
|
|
@@ -2216,7 +2218,9 @@ export class ModelRegistry {
|
|
|
2216
2218
|
? resolveApiKeyEnvConfig(providerConfig.apiKeyEnv)
|
|
2217
2219
|
: undefined;
|
|
2218
2220
|
if (resolved) this.#customProviderApiKeys.set(providerName, resolved);
|
|
2219
|
-
if (resolved)
|
|
2221
|
+
if (resolved) {
|
|
2222
|
+
this.authStorage.setConfigApiKey(providerName, resolved, { envSourced: !providerConfig.apiKey });
|
|
2223
|
+
}
|
|
2220
2224
|
}
|
|
2221
2225
|
|
|
2222
2226
|
// Parse per-model overrides
|
|
@@ -3806,7 +3810,9 @@ export class ModelRegistry {
|
|
|
3806
3810
|
? resolveApiKeyEnvConfig(providerConfig.apiKeyEnv)
|
|
3807
3811
|
: undefined;
|
|
3808
3812
|
if (resolved) this.#customProviderApiKeys.set(providerName, resolved);
|
|
3809
|
-
if (resolved)
|
|
3813
|
+
if (resolved) {
|
|
3814
|
+
this.authStorage.setConfigApiKey(providerName, resolved, { envSourced: !providerConfig.apiKey });
|
|
3815
|
+
}
|
|
3810
3816
|
}
|
|
3811
3817
|
for (const modelDef of modelDefs) {
|
|
3812
3818
|
const providerCompat = providerConfig.disableStrictTools
|
|
@@ -4547,7 +4553,7 @@ export class ModelRegistry {
|
|
|
4547
4553
|
return;
|
|
4548
4554
|
}
|
|
4549
4555
|
this.#customProviderApiKeys.set(provider, resolved);
|
|
4550
|
-
this.authStorage.setConfigApiKey(provider, resolved);
|
|
4556
|
+
this.authStorage.setConfigApiKey(provider, resolved, { envSourced: true });
|
|
4551
4557
|
}
|
|
4552
4558
|
|
|
4553
4559
|
async #peekApiKeyForProvider(
|
|
@@ -112,7 +112,7 @@ function parseRootList(value: string | undefined): string[] {
|
|
|
112
112
|
}
|
|
113
113
|
|
|
114
114
|
function resolveManagedWorktreeRoot(root: string, configured: string | undefined): string {
|
|
115
|
-
const template = (configured?.trim() || "{repo}
|
|
115
|
+
const template = (configured?.trim() || "{repo}/.worktrees").replace(/^~(?=\/|$)/, os.homedir());
|
|
116
116
|
const resolved = template.replaceAll("{repo}", path.basename(root));
|
|
117
117
|
return path.resolve(path.isAbsolute(resolved) ? resolved : path.join(path.dirname(root), resolved));
|
|
118
118
|
}
|