@gajae-code/coding-agent 0.5.2 → 0.5.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 +14 -0
- package/dist/types/async/job-manager.d.ts +6 -0
- package/dist/types/dap/client.d.ts +2 -1
- package/dist/types/edit/read-file.d.ts +6 -0
- package/dist/types/eval/js/context-manager.d.ts +3 -0
- package/dist/types/eval/js/executor.d.ts +1 -0
- package/dist/types/exec/bash-executor.d.ts +2 -0
- package/dist/types/gjc-runtime/tmux-sessions.d.ts +7 -1
- package/dist/types/lsp/types.d.ts +2 -0
- package/dist/types/modes/bridge/bridge-mode.d.ts +1 -0
- package/dist/types/modes/components/model-selector.d.ts +2 -0
- package/dist/types/modes/components/oauth-selector.d.ts +1 -0
- package/dist/types/modes/components/runtime-mcp-add-wizard.d.ts +1 -0
- package/dist/types/modes/components/tool-execution.d.ts +1 -0
- package/dist/types/runtime/process-lifecycle.d.ts +108 -0
- package/dist/types/runtime-mcp/transports/stdio.d.ts +1 -0
- package/dist/types/runtime-mcp/types.d.ts +2 -0
- package/dist/types/session/agent-session.d.ts +17 -1
- package/dist/types/session/artifacts.d.ts +4 -1
- package/dist/types/session/streaming-output.d.ts +5 -0
- package/dist/types/slash-commands/helpers/fast-status-report.d.ts +76 -0
- package/dist/types/tools/bash.d.ts +1 -0
- package/dist/types/tools/browser/tab-supervisor.d.ts +9 -0
- package/dist/types/tools/sqlite-reader.d.ts +2 -1
- package/package.json +7 -7
- package/src/async/job-manager.ts +153 -39
- package/src/config/file-lock.ts +9 -1
- package/src/dap/client.ts +105 -64
- package/src/dap/session.ts +44 -7
- package/src/edit/read-file.ts +19 -1
- package/src/eval/js/context-manager.ts +228 -65
- package/src/eval/js/executor.ts +2 -0
- package/src/eval/js/index.ts +1 -0
- package/src/eval/js/worker-core.ts +10 -6
- package/src/eval/py/executor.ts +68 -19
- package/src/eval/py/kernel.ts +46 -22
- package/src/eval/py/runner.py +68 -14
- package/src/exec/bash-executor.ts +49 -13
- package/src/gjc-runtime/tmux-gc.ts +86 -37
- package/src/gjc-runtime/tmux-sessions.ts +44 -6
- package/src/internal-urls/artifact-protocol.ts +10 -1
- package/src/internal-urls/docs-index.generated.ts +2 -2
- package/src/lsp/client.ts +64 -26
- package/src/lsp/index.ts +2 -1
- package/src/lsp/lspmux.ts +33 -9
- package/src/lsp/types.ts +2 -0
- package/src/modes/bridge/bridge-mode.ts +21 -0
- package/src/modes/components/assistant-message.ts +10 -2
- package/src/modes/components/bash-execution.ts +5 -1
- package/src/modes/components/eval-execution.ts +5 -1
- package/src/modes/components/model-selector.ts +34 -2
- package/src/modes/components/oauth-selector.ts +5 -0
- package/src/modes/components/runtime-mcp-add-wizard.ts +58 -7
- package/src/modes/components/skill-message.ts +24 -16
- package/src/modes/components/tool-execution.ts +6 -0
- package/src/modes/controllers/extension-ui-controller.ts +33 -6
- package/src/modes/controllers/input-controller.ts +5 -0
- package/src/modes/controllers/selector-controller.ts +6 -1
- package/src/modes/utils/ui-helpers.ts +5 -2
- package/src/runtime/process-lifecycle.ts +400 -0
- package/src/runtime-mcp/manager.ts +164 -50
- package/src/runtime-mcp/transports/http.ts +12 -11
- package/src/runtime-mcp/transports/stdio.ts +64 -38
- package/src/runtime-mcp/types.ts +3 -0
- package/src/sdk.ts +27 -0
- package/src/session/agent-session.ts +168 -22
- package/src/session/artifacts.ts +17 -2
- package/src/session/blob-store.ts +36 -2
- package/src/session/session-manager.ts +29 -13
- package/src/session/streaming-output.ts +54 -3
- package/src/slash-commands/builtin-registry.ts +30 -3
- package/src/slash-commands/helpers/fast-status-report.ts +111 -0
- package/src/tools/archive-reader.ts +10 -1
- package/src/tools/bash.ts +11 -4
- package/src/tools/browser/tab-supervisor.ts +22 -0
- package/src/tools/browser.ts +38 -4
- package/src/tools/read.ts +11 -12
- package/src/tools/sqlite-reader.ts +19 -5
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.5.3] - 2026-06-16
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Added `GJC_CREDENTIAL_RANKING_MODE` env var (`balanced` (default) | `earliest-reset`), wired through `discoverAuthStorage` into `AuthStorage.credentialRankingMode`. `earliest-reset` selects multi-account OAuth credentials earliest-expiry-first so soon-to-reset tumbling-window quota (e.g. Claude 5h/7d) is drained before it is lost at reset; unset/unknown leaves the default `balanced` behavior unchanged.
|
|
10
|
+
- The `/model` selector and `/fast` status now show a per-model fast-mode indicator (`⚡`) resolved with the provider-aware predicate, including subagent (role) models evaluated against the effective `task.serviceTier` (falling back to the session tier when `inherit`), so it is visible at a glance which models will run with Anthropic `speed:"fast"` / OpenAI `service_tier=priority`. Display-only: no `serviceTier`/`modelRoles`/`agentModelOverrides` writes (#691).
|
|
11
|
+
- Added an opt-in `GJC_BRIDGE_ENDPOINTS` env var (`all`, or a comma list of matrix keys: `events`, `commands`, `control`, `uiResponses`, `hostToolResults`, `hostUriResults`) to enable bridge-mode session-control endpoints, which were previously permanently fail-closed (`403 endpoint_disabled`) with no supported way to turn them on. Unset stays fail-closed and backward compatible (#663).
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- Auto-compaction no longer silently requires OpenAI when the active route is a custom Anthropic-capable provider. The compaction model-candidate selection already prefers the active session model, but its last-resort "largest-context model" fallback scanned the entire bundled catalog across all providers, so a stray OpenAI credential (e.g. an out-of-credit key left in the environment) could be picked when the active provider's compaction credential was unusable — turning OpenAI into an implicit hard dependency. The implicit fallback is now scoped to the active model's provider; cross-provider compaction still works but only when explicitly configured via `modelRoles`. When the active provider cannot compact and no role is configured, compaction now fails with the existing clear, provider-specific credential error instead of reaching for OpenAI (#697).
|
|
16
|
+
- Long-running-session freeze/leak remediation across the TUI, agent, and tool runtime: the TUI render loop, component-dispose lifecycle, and markdown highlighting are hardened against huge frames and reuse leaks (#716); agent context append, an emergency-compaction floor that cannot be disabled, token accounting, and session resource teardown (own-session browser tabs, LSP clients, Cursor conversation cache) are bounded (#717); oversized tool inputs/outputs are capped (8 MiB edit/read guard ahead of the notebook fast-path, 1000-row SQLite raw-query cap, 16 MiB artifact / 256 MiB archive read caps, budget-bounded browser return serialization) (#721); native synchronous entrypoints add defense-in-depth caps for tokenization, highlighting, and fuzzy edit matching (#744); and the session blob store is LRU-bounded (64 MiB / 4096 entries) with bounded-concurrency blob resume (limit 8) (#719).
|
|
17
|
+
- Process & resource lifecycle hardening so child processes and external resources are reliably reaped on disconnect, abort, and shutdown, built on a new owned-process foundation — process-group ownership with escalating SIGTERM→SIGKILL tree termination, idempotent dispose, and a postmortem reap hook (F1). Owned-process handles are terminalized on clean drain so a retained handle can never signal a recycled PID/process group (B1); the native blocking-task boundary, PTY lifecycle, and pi-shell timeout/abort reaping are hardened (U1–U3); the Python eval kernel (U4) and JS eval worker/VM (U5) coalesce concurrent first cells, settle queued/pending runs on teardown, and return worker/kernel counts to baseline; bash shell sessions are owner-scoped with one-shot async/monitor jobs and a hard artifact byte cap (U6); DAP adapters and LSP servers are spawned as owned processes and killed on terminate/timeout/reload (U7); MCP stdio/HTTP/SSE transports and the manager close idempotently with stale-publication identity guards (U8); the async job-manager bounds dispose, the delivery queue/retry (with dead-lettering), and terminal purge (U9); and tmux GC never prunes live/attached sessions and reaps only durably-owned orphans (U10).
|
|
18
|
+
|
|
5
19
|
## [0.5.2] - 2026-06-15
|
|
6
20
|
|
|
7
21
|
### Fixed
|
|
@@ -109,11 +109,16 @@ export interface AsyncJobManagerOptions {
|
|
|
109
109
|
maxRunningJobs?: number;
|
|
110
110
|
retentionMs?: number;
|
|
111
111
|
}
|
|
112
|
+
export interface AsyncJobDisposeDiagnostics {
|
|
113
|
+
stuckJobIds: string[];
|
|
114
|
+
deliveriesDrained: boolean;
|
|
115
|
+
}
|
|
112
116
|
export interface AsyncJobDeliveryState {
|
|
113
117
|
queued: number;
|
|
114
118
|
delivering: boolean;
|
|
115
119
|
nextRetryAt?: number;
|
|
116
120
|
pendingJobIds: string[];
|
|
121
|
+
deadLettered: number;
|
|
117
122
|
}
|
|
118
123
|
export interface AsyncJobLifecycleCleanup {
|
|
119
124
|
onCancel?: (job: AsyncJob) => void;
|
|
@@ -308,6 +313,7 @@ export declare class AsyncJobManager {
|
|
|
308
313
|
* (used by `dispose()` to nuke the manager's state).
|
|
309
314
|
*/
|
|
310
315
|
cancelAll(filter?: AsyncJobFilter): void;
|
|
316
|
+
getLastDisposeDiagnostics(): AsyncJobDisposeDiagnostics;
|
|
311
317
|
waitForAll(): Promise<void>;
|
|
312
318
|
drainDeliveries(options?: {
|
|
313
319
|
timeoutMs?: number;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type OwnedProcess } from "../runtime/process-lifecycle";
|
|
1
2
|
import type { DapCapabilities, DapClientState, DapEventMessage, DapInitializeArguments, DapRequestMessage, DapResolvedAdapter } from "./types";
|
|
2
3
|
interface DapSpawnOptions {
|
|
3
4
|
adapter: DapResolvedAdapter;
|
|
@@ -15,7 +16,7 @@ export declare class DapClient {
|
|
|
15
16
|
readonly adapter: DapResolvedAdapter;
|
|
16
17
|
readonly cwd: string;
|
|
17
18
|
readonly proc: DapClientState["proc"];
|
|
18
|
-
constructor(adapter: DapResolvedAdapter, cwd: string,
|
|
19
|
+
constructor(adapter: DapResolvedAdapter, cwd: string, owner: OwnedProcess, options?: {
|
|
19
20
|
readable?: ReadableStream<Uint8Array>;
|
|
20
21
|
writeSink?: DapWriteSink;
|
|
21
22
|
socket?: {
|
|
@@ -1,2 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Max byte size of a file the edit modes will load whole. Editing loads + normalizes +
|
|
3
|
+
* fuzzy-matches + diffs the entire file on the main thread, so a multi-MB/generated file
|
|
4
|
+
* would block the event loop (F19). Above this, fail fast with an actionable error.
|
|
5
|
+
*/
|
|
6
|
+
export declare const MAX_EDIT_FILE_BYTES: number;
|
|
1
7
|
export declare function readEditFileText(absolutePath: string, path: string): Promise<string>;
|
|
2
8
|
export declare function serializeEditFileText(absolutePath: string, path: string, content: string): Promise<string>;
|
|
@@ -12,6 +12,7 @@ export declare function executeInVmContext(options: {
|
|
|
12
12
|
sessionId: string;
|
|
13
13
|
cwd: string;
|
|
14
14
|
session: ToolSession;
|
|
15
|
+
ownerId?: string;
|
|
15
16
|
reset?: boolean;
|
|
16
17
|
code: string;
|
|
17
18
|
filename: string;
|
|
@@ -21,4 +22,6 @@ export declare function executeInVmContext(options: {
|
|
|
21
22
|
value: unknown;
|
|
22
23
|
}>;
|
|
23
24
|
export declare function resetVmContext(sessionKey: string): Promise<void>;
|
|
25
|
+
export declare function disposeVmContextsByOwner(ownerId: string): Promise<void>;
|
|
24
26
|
export declare function disposeAllVmContexts(): Promise<void>;
|
|
27
|
+
export declare function liveVmContextCount(): number;
|
|
@@ -19,6 +19,8 @@ export interface BashExecutorOptions {
|
|
|
19
19
|
/** Artifact path/id for full output storage */
|
|
20
20
|
artifactPath?: string;
|
|
21
21
|
artifactId?: string;
|
|
22
|
+
/** Execute without retaining a native Shell in the persistent session registry. */
|
|
23
|
+
oneShot?: boolean;
|
|
22
24
|
/**
|
|
23
25
|
* Invoked when the native minimizer rewrote the command's output, giving
|
|
24
26
|
* the caller a chance to persist the lossless original capture (typically
|
|
@@ -8,15 +8,21 @@ export interface GjcTmuxSessionStatus {
|
|
|
8
8
|
branch?: string;
|
|
9
9
|
branchSlug?: string;
|
|
10
10
|
project?: string;
|
|
11
|
+
panePids: number[];
|
|
12
|
+
profile?: string;
|
|
11
13
|
}
|
|
12
14
|
export interface GjcTmuxSessionTagsForGc {
|
|
13
15
|
profile?: string;
|
|
14
16
|
project?: string;
|
|
15
17
|
branch?: string;
|
|
18
|
+
branchSlug?: string;
|
|
19
|
+
createdAt?: string;
|
|
20
|
+
attached?: boolean;
|
|
21
|
+
panePids?: number[];
|
|
16
22
|
}
|
|
17
23
|
export interface GjcTmuxSessionsForGc {
|
|
18
24
|
tagged: GjcTmuxSessionStatus[];
|
|
19
|
-
untagged:
|
|
25
|
+
untagged: GjcTmuxSessionStatus[];
|
|
20
26
|
}
|
|
21
27
|
export declare function listGjcTmuxSessions(env?: NodeJS.ProcessEnv): GjcTmuxSessionStatus[];
|
|
22
28
|
/** @internal */
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ptree } from "@gajae-code/utils";
|
|
2
2
|
import * as z from "zod/v4";
|
|
3
|
+
import type { OwnedProcess } from "../runtime/process-lifecycle";
|
|
3
4
|
export declare const lspSchema: z.ZodObject<{
|
|
4
5
|
action: z.ZodEnum<{
|
|
5
6
|
capabilities: "capabilities";
|
|
@@ -261,6 +262,7 @@ export interface LspClient {
|
|
|
261
262
|
cwd: string;
|
|
262
263
|
config: ServerConfig;
|
|
263
264
|
proc: ptree.ChildProcess<"pipe">;
|
|
265
|
+
owner?: OwnedProcess;
|
|
264
266
|
requestId: number;
|
|
265
267
|
diagnostics: Map<string, PublishedDiagnostics>;
|
|
266
268
|
diagnosticsVersion: number;
|
|
@@ -41,6 +41,7 @@ interface BridgeIdempotencyRecord {
|
|
|
41
41
|
pending?: boolean;
|
|
42
42
|
}
|
|
43
43
|
type BridgeIdempotencyCache = Map<string, BridgeIdempotencyRecord>;
|
|
44
|
+
export declare function parseBridgeEndpoints(value: string | undefined): Partial<BridgeEndpointMatrix> | undefined;
|
|
44
45
|
export declare function createBridgeFetchHandler(options: BridgeFetchHandlerOptions): (request: Request) => Promise<Response>;
|
|
45
46
|
export declare function runBridgeMode(session: AgentSession, setToolUIContext?: (uiContext: ExtensionUIContext, hasUI: boolean) => void): Promise<never>;
|
|
46
47
|
export {};
|
|
@@ -30,6 +30,8 @@ export declare class ModelSelectorComponent extends Container {
|
|
|
30
30
|
temporaryOnly?: boolean;
|
|
31
31
|
initialSearchInput?: string;
|
|
32
32
|
sessionId?: string;
|
|
33
|
+
isFastForProvider?: (provider?: string) => boolean;
|
|
34
|
+
isFastForSubagentProvider?: (provider?: string) => boolean;
|
|
33
35
|
});
|
|
34
36
|
handleInput(keyData: string): void;
|
|
35
37
|
getSearchInput(): Input;
|
|
@@ -20,6 +20,7 @@ export interface MCPAddWizardOAuthResult {
|
|
|
20
20
|
export declare class MCPAddWizard extends Container {
|
|
21
21
|
#private;
|
|
22
22
|
constructor(onComplete: (name: string, config: MCPServerConfig, scope: Scope) => void, onCancel: () => void, onOAuth?: (authUrl: string, tokenUrl: string, clientId: string, clientSecret: string, scopes: string) => Promise<MCPAddWizardOAuthResult>, onTestConnection?: (config: MCPServerConfig) => Promise<void>, onRender?: () => void, initialName?: string);
|
|
23
|
+
dispose(): void;
|
|
23
24
|
handleInput(keyData: string): void;
|
|
24
25
|
}
|
|
25
26
|
export {};
|
|
@@ -47,6 +47,7 @@ export declare class ToolExecutionComponent extends Container {
|
|
|
47
47
|
* Stop spinner animation and cleanup resources.
|
|
48
48
|
*/
|
|
49
49
|
stopAnimation(): void;
|
|
50
|
+
dispose(): void;
|
|
50
51
|
setExpanded(expanded: boolean): void;
|
|
51
52
|
setShowImages(show: boolean): void;
|
|
52
53
|
invalidate(): void;
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared runtime lifecycle foundation.
|
|
3
|
+
*
|
|
4
|
+
* Two minimal, deliberately small primitives that subsystem runtimes
|
|
5
|
+
* (DAP/LSP/MCP stdio, eval workers, etc.) adopt so spawned children and
|
|
6
|
+
* non-process resources cannot outlive their owner:
|
|
7
|
+
*
|
|
8
|
+
* F1(a) `spawnOwnedProcess` — wraps `ptree.spawn` with explicit
|
|
9
|
+
* process-group ownership, escalating (SIGTERM -> grace -> SIGKILL)
|
|
10
|
+
* tree termination, bounded `awaitExit`, abort-listener cleanup on
|
|
11
|
+
* settle, idempotent `dispose`, and a single postmortem hook that
|
|
12
|
+
* reaps every still-live owned process group on fatal/normal shutdown.
|
|
13
|
+
*
|
|
14
|
+
* F1(b) `registerResourceOwner` — a generic, idempotent postmortem adapter
|
|
15
|
+
* for non-process resources (Bun Workers, VM contexts, timers,
|
|
16
|
+
* sockets) built on the existing `postmortem.register` facility.
|
|
17
|
+
*
|
|
18
|
+
* Ownership is keyed to the *process group*, not the root process. A root that
|
|
19
|
+
* exits after backgrounding descendants (`sh -c "worker & exit 0"`) keeps the
|
|
20
|
+
* owner registered until the group is actually gone, so the descendant tree is
|
|
21
|
+
* still reaped by `dispose()`/postmortem.
|
|
22
|
+
*
|
|
23
|
+
* This module intentionally owns only these primitives. It does not migrate
|
|
24
|
+
* existing call sites; subsystem PRs adopt it incrementally.
|
|
25
|
+
*
|
|
26
|
+
* Note: `ptree.spawn` always pipes stdout/stderr. Adopters that expect output
|
|
27
|
+
* (DAP/LSP/MCP protocol servers) must consume `owner.child.stdout`; F1 does not
|
|
28
|
+
* drain it, so a chatty child whose stdout is never read can still block on a
|
|
29
|
+
* full pipe. That draining is the adopter's responsibility.
|
|
30
|
+
*/
|
|
31
|
+
import { ptree } from "@gajae-code/utils";
|
|
32
|
+
/** Options for {@link spawnOwnedProcess}. */
|
|
33
|
+
export interface SpawnOwnedOptions {
|
|
34
|
+
cwd?: string;
|
|
35
|
+
env?: Record<string, string | undefined>;
|
|
36
|
+
/** stdin mode passed through to the child. Defaults to `"ignore"`. */
|
|
37
|
+
stdin?: "pipe" | "ignore";
|
|
38
|
+
/** When aborted, the owned process tree is disposed (escalating kill). */
|
|
39
|
+
signal?: AbortSignal;
|
|
40
|
+
/** Grace period (ms) between SIGTERM and SIGKILL on dispose. Default 2000. */
|
|
41
|
+
gracefulMs?: number;
|
|
42
|
+
/**
|
|
43
|
+
* Spawn the child as its own process-group leader so the whole descendant
|
|
44
|
+
* tree can be signalled on dispose. Defaults to `true` on POSIX. Has no
|
|
45
|
+
* effect on Windows, where teardown falls back to single-process kill.
|
|
46
|
+
*/
|
|
47
|
+
processGroup?: boolean;
|
|
48
|
+
/** Label used in diagnostics. */
|
|
49
|
+
name?: string;
|
|
50
|
+
}
|
|
51
|
+
/** Result of a bounded {@link OwnedProcess.awaitExit}. */
|
|
52
|
+
export interface AwaitExitResult {
|
|
53
|
+
/** `true` when the process has exited; `false` when the timeout fired first. */
|
|
54
|
+
exited: boolean;
|
|
55
|
+
/** Exit code if known, else `null`. */
|
|
56
|
+
code: number | null;
|
|
57
|
+
}
|
|
58
|
+
/** A spawned child process owned by the runtime with guaranteed teardown. */
|
|
59
|
+
export interface OwnedProcess {
|
|
60
|
+
readonly child: ptree.ChildProcess;
|
|
61
|
+
readonly pid: number | undefined;
|
|
62
|
+
/** Resolves/rejects when the root child exits (mirrors ptree's `exited`). */
|
|
63
|
+
readonly exited: Promise<number>;
|
|
64
|
+
/** `true` once `dispose()` has started. */
|
|
65
|
+
readonly disposed: boolean;
|
|
66
|
+
/**
|
|
67
|
+
* Wait for the root child to exit, optionally bounded by `timeoutMs`. With no
|
|
68
|
+
* timeout it resolves only when the child exits. Never rejects.
|
|
69
|
+
*/
|
|
70
|
+
awaitExit(opts?: {
|
|
71
|
+
timeoutMs?: number;
|
|
72
|
+
}): Promise<AwaitExitResult>;
|
|
73
|
+
/**
|
|
74
|
+
* Idempotently terminate the owned process *group*: SIGTERM the group, wait
|
|
75
|
+
* `gracefulMs`, then SIGKILL, polling group liveness throughout. Removes the
|
|
76
|
+
* abort listener and deregisters from the live-owner set only after teardown
|
|
77
|
+
* has completed. Repeated/concurrent calls return the same in-flight promise.
|
|
78
|
+
*/
|
|
79
|
+
dispose(): Promise<void>;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Spawn a child process owned by the runtime. The returned {@link OwnedProcess}
|
|
83
|
+
* is registered for postmortem cleanup and tears down its whole process group
|
|
84
|
+
* on dispose/abort.
|
|
85
|
+
*/
|
|
86
|
+
export declare function spawnOwnedProcess(cmd: string[], opts?: SpawnOwnedOptions): OwnedProcess;
|
|
87
|
+
/** Number of currently live owned processes. Exposed for leak assertions/tests. */
|
|
88
|
+
export declare function liveOwnedProcessCount(): number;
|
|
89
|
+
/** Dispose every live owned process. For owner-scoped teardown and tests. */
|
|
90
|
+
export declare function disposeAllOwnedProcesses(): Promise<void>;
|
|
91
|
+
type ResourceDisposer = () => void | Promise<void>;
|
|
92
|
+
/**
|
|
93
|
+
* Register a non-process resource for postmortem/fatal-exit cleanup.
|
|
94
|
+
*
|
|
95
|
+
* Idempotent by `name`: re-registering the same name replaces the prior
|
|
96
|
+
* disposer (last wins). Returns an unregister function that removes the owner
|
|
97
|
+
* only while it is still the active registration for that name.
|
|
98
|
+
*/
|
|
99
|
+
export declare function registerResourceOwner(name: string, disposer: ResourceDisposer): () => void;
|
|
100
|
+
/** Number of registered resource owners. Exposed for leak assertions/tests. */
|
|
101
|
+
export declare function resourceOwnerCount(): number;
|
|
102
|
+
/**
|
|
103
|
+
* Run and clear every registered resource disposer. Attempts all disposers even
|
|
104
|
+
* if some throw, then surfaces the failures as an `AggregateError` so callers
|
|
105
|
+
* can distinguish "all closed" from "a resource may still be alive".
|
|
106
|
+
*/
|
|
107
|
+
export declare function disposeAllResourceOwners(): Promise<void>;
|
|
108
|
+
export {};
|
|
@@ -14,6 +14,7 @@ export declare class StdioTransport implements MCPTransport {
|
|
|
14
14
|
onRequest?: (method: string, params: unknown) => Promise<unknown>;
|
|
15
15
|
constructor(config: MCPStdioServerConfig);
|
|
16
16
|
get connected(): boolean;
|
|
17
|
+
get closeBeforeReconnect(): true;
|
|
17
18
|
/**
|
|
18
19
|
* Start the subprocess and begin reading.
|
|
19
20
|
*/
|
|
@@ -185,6 +185,8 @@ export interface MCPTransport {
|
|
|
185
185
|
notify(method: string, params?: Record<string, unknown>): Promise<void>;
|
|
186
186
|
/** Close the transport */
|
|
187
187
|
close(): Promise<void>;
|
|
188
|
+
/** Whether close must finish before reconnect can safely spawn a replacement. */
|
|
189
|
+
readonly closeBeforeReconnect?: boolean;
|
|
188
190
|
/** Whether the transport is connected */
|
|
189
191
|
readonly connected: boolean;
|
|
190
192
|
/** Event handlers */
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* Modes use this class and add their own I/O layer on top.
|
|
14
14
|
*/
|
|
15
15
|
import { type Agent, type AgentEvent, type AgentMessage, type AgentState, type AgentTool, type StablePrefixSnapshot, ThinkingLevel } from "@gajae-code/agent-core";
|
|
16
|
-
import { type CompactionResult } from "@gajae-code/agent-core/compaction";
|
|
16
|
+
import { type CompactionResult, type EmergencyCompactionSample } from "@gajae-code/agent-core/compaction";
|
|
17
17
|
import type { AssistantMessage, Effort, ImageContent, Message, MessageAttribution, Model, ProviderSessionState, ServiceTier, SimpleStreamOptions, TextContent, ToolChoice, UsageReport } from "@gajae-code/ai";
|
|
18
18
|
export interface ForkContextSeedMetadata {
|
|
19
19
|
sourceSessionId: string;
|
|
@@ -726,6 +726,20 @@ export declare class AgentSession {
|
|
|
726
726
|
* {@link isFastModeActive} instead — that one respects the model's provider.
|
|
727
727
|
*/
|
|
728
728
|
isFastModeEnabled(): boolean;
|
|
729
|
+
/**
|
|
730
|
+
* True when the configured `serviceTier` resolves to `"priority"` for the
|
|
731
|
+
* given model `provider`. Returns false for scoped tiers that don't match
|
|
732
|
+
* (e.g. `"openai-only"` on an anthropic provider) and when `provider` is
|
|
733
|
+
* undefined. This is the canonical provider-aware fast-mode predicate.
|
|
734
|
+
*/
|
|
735
|
+
isFastForProvider(provider?: string): boolean;
|
|
736
|
+
/**
|
|
737
|
+
* Provider-aware fast-mode predicate for task-tool subagent roles, evaluated
|
|
738
|
+
* against the effective subagent tier (`task.serviceTier`) rather than the
|
|
739
|
+
* main session tier. Use this for `task.agentModelOverrides` role rows so the
|
|
740
|
+
* ⚡ glyph reflects the tier the subagent actually runs under.
|
|
741
|
+
*/
|
|
742
|
+
isFastForSubagentProvider(provider?: string): boolean;
|
|
729
743
|
/**
|
|
730
744
|
* True when the configured `serviceTier` resolves to `"priority"` for the
|
|
731
745
|
* *currently selected model's provider*. Returns false for scoped tiers
|
|
@@ -789,6 +803,8 @@ export declare class AgentSession {
|
|
|
789
803
|
*/
|
|
790
804
|
handoff(customInstructions?: string, options?: SessionHandoffOptions): Promise<HandoffResult | undefined>;
|
|
791
805
|
prepareContributionPrep(options?: ContributionPrepOptions): Promise<ContributionPrepResult>;
|
|
806
|
+
/** Test seam: override the emergency-compaction resource sampler so tests never read real RSS. */
|
|
807
|
+
setResourceSampler(sampler: () => EmergencyCompactionSample): void;
|
|
792
808
|
/**
|
|
793
809
|
* Toggle auto-compaction setting.
|
|
794
810
|
*/
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
export interface ArtifactSaveOptions {
|
|
2
|
+
maxBytes?: number;
|
|
3
|
+
}
|
|
1
4
|
/**
|
|
2
5
|
* Manages artifact storage for a session.
|
|
3
6
|
*
|
|
@@ -40,7 +43,7 @@ export declare class ArtifactManager {
|
|
|
40
43
|
* @param toolType Tool name for file extension (e.g., "bash", "read")
|
|
41
44
|
* @returns Artifact ID (numeric string)
|
|
42
45
|
*/
|
|
43
|
-
save(content: string, toolType: string): Promise<string>;
|
|
46
|
+
save(content: string, toolType: string, options?: ArtifactSaveOptions): Promise<string>;
|
|
44
47
|
/**
|
|
45
48
|
* Check if an artifact exists.
|
|
46
49
|
* @param id Artifact ID (numeric string)
|
|
@@ -2,6 +2,7 @@ import type { AgentToolUpdateCallback } from "@gajae-code/agent-core";
|
|
|
2
2
|
export declare const DEFAULT_MAX_LINES = 3000;
|
|
3
3
|
export declare const DEFAULT_MAX_BYTES: number;
|
|
4
4
|
export declare const DEFAULT_MAX_COLUMN = 1024;
|
|
5
|
+
export declare const DEFAULT_ARTIFACT_MAX_BYTES: number;
|
|
5
6
|
export interface OutputSummary {
|
|
6
7
|
output: string;
|
|
7
8
|
truncated: boolean;
|
|
@@ -19,6 +20,8 @@ export interface OutputSummary {
|
|
|
19
20
|
columnTruncatedLines?: number;
|
|
20
21
|
/** Artifact ID for internal URL access (artifact://<id>) when truncated */
|
|
21
22
|
artifactId?: string;
|
|
23
|
+
/** Bytes omitted from artifact storage after the artifact hard cap was reached. */
|
|
24
|
+
artifactTruncatedBytes?: number;
|
|
22
25
|
}
|
|
23
26
|
export interface OutputSinkOptions {
|
|
24
27
|
artifactPath?: string;
|
|
@@ -38,6 +41,8 @@ export interface OutputSinkOptions {
|
|
|
38
41
|
* writes still respect the budget. Default 0 = no per-line cap.
|
|
39
42
|
*/
|
|
40
43
|
maxColumns?: number;
|
|
44
|
+
/** Hard cap for artifact writes/pending replay. Default DEFAULT_ARTIFACT_MAX_BYTES. */
|
|
45
|
+
artifactMaxBytes?: number;
|
|
41
46
|
onChunk?: (chunk: string) => void;
|
|
42
47
|
/** Minimum ms between onChunk calls. 0 = every chunk (default). */
|
|
43
48
|
chunkThrottleMs?: number;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import type { Model } from "@gajae-code/ai";
|
|
2
|
+
/**
|
|
3
|
+
* A single line in the `/fast status` report: a labelled model and whether fast
|
|
4
|
+
* mode is effective for it. The `fast` flag is resolved by the caller
|
|
5
|
+
* (`buildFastStatusReport`) so each row can use the correct service tier — the
|
|
6
|
+
* main session tier for the current model / `modelRoles` roles, or the subagent
|
|
7
|
+
* tier (`task.serviceTier`) for `task.agentModelOverrides` roles.
|
|
8
|
+
*/
|
|
9
|
+
export interface FastStatusRow {
|
|
10
|
+
/** Display label, e.g. "현재 모델", "DEFAULT", "EXECUTOR". */
|
|
11
|
+
label: string;
|
|
12
|
+
/** Resolved model for this row, if any. */
|
|
13
|
+
model?: Model;
|
|
14
|
+
/** Whether fast mode is effective for this row's model. */
|
|
15
|
+
fast: boolean;
|
|
16
|
+
}
|
|
17
|
+
export interface FormatFastStatusReportArgs {
|
|
18
|
+
rows: FastStatusRow[];
|
|
19
|
+
/** The active theme's fast icon token (`theme.icon.fast`). */
|
|
20
|
+
iconFast: string;
|
|
21
|
+
/** Optional decorator for inactive ("off") text, e.g. theme dim in the TUI. */
|
|
22
|
+
formatInactive?: (text: string) => string;
|
|
23
|
+
}
|
|
24
|
+
/** Title line of the `/fast status` report. */
|
|
25
|
+
export declare const FAST_STATUS_TITLE = "Fast \uBAA8\uB4DC \uC0C1\uD0DC";
|
|
26
|
+
/** The inactive marker shown for rows where fast mode does not apply. */
|
|
27
|
+
export declare const FAST_STATUS_OFF = "off";
|
|
28
|
+
/**
|
|
29
|
+
* Format a multiline `/fast status` report. Pure and shared by the CLI
|
|
30
|
+
* (`handle`) and TUI (`handleTui`) command branches so the two never drift.
|
|
31
|
+
* Each row's fast/off state is decided by the caller (see
|
|
32
|
+
* {@link buildFastStatusReport}) so per-row service-tier differences are honored.
|
|
33
|
+
*/
|
|
34
|
+
export declare function formatFastStatusReport(args: FormatFastStatusReportArgs): string;
|
|
35
|
+
/** Minimal session surface needed to build the `/fast status` report. */
|
|
36
|
+
export interface FastStatusSessionLike {
|
|
37
|
+
readonly model?: Model;
|
|
38
|
+
/** Fast predicate against the main session tier (current model + `modelRoles`). */
|
|
39
|
+
isFastForProvider(provider?: string): boolean;
|
|
40
|
+
/** Fast predicate against the effective subagent tier (`task.agentModelOverrides` roles). */
|
|
41
|
+
isFastForSubagentProvider(provider?: string): boolean;
|
|
42
|
+
resolveRoleModelWithThinking(role: string): {
|
|
43
|
+
model?: Model;
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
/** A role to enumerate in the report, with the tier source its subagent runs under. */
|
|
47
|
+
export interface FastStatusRoleTarget {
|
|
48
|
+
id: string;
|
|
49
|
+
label: string;
|
|
50
|
+
/**
|
|
51
|
+
* True for `task.agentModelOverrides` roles (executor/architect/planner/critic)
|
|
52
|
+
* that run under `task.serviceTier`; false for `modelRoles` roles (default)
|
|
53
|
+
* that run under the main session tier.
|
|
54
|
+
*/
|
|
55
|
+
isSubagentRole: boolean;
|
|
56
|
+
}
|
|
57
|
+
export interface BuildFastStatusReportArgs {
|
|
58
|
+
session: FastStatusSessionLike;
|
|
59
|
+
/** Role targets to enumerate, in display order. */
|
|
60
|
+
roleTargets: ReadonlyArray<FastStatusRoleTarget>;
|
|
61
|
+
/** The active theme's fast icon token (`theme.icon.fast`). */
|
|
62
|
+
iconFast: string;
|
|
63
|
+
/** Optional decorator for inactive ("off") text, e.g. theme dim in the TUI. */
|
|
64
|
+
formatInactive?: (text: string) => string;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Build the `/fast status` report from a live session: the active/current model
|
|
68
|
+
* followed by each assigned role (subagent) model. Unassigned roles are skipped
|
|
69
|
+
* so the report mirrors the `/model` selector, which only badges assigned roles.
|
|
70
|
+
*
|
|
71
|
+
* Subagent roles (`task.agentModelOverrides`) are evaluated against the
|
|
72
|
+
* effective subagent tier (`task.serviceTier`), while the current model and
|
|
73
|
+
* `modelRoles` roles use the main session tier — matching where each model
|
|
74
|
+
* actually runs.
|
|
75
|
+
*/
|
|
76
|
+
export declare function buildFastStatusReport(args: BuildFastStatusReportArgs): string;
|
|
@@ -6,6 +6,7 @@ import { type Theme } from "../modes/theme/theme";
|
|
|
6
6
|
import type { ToolSession } from ".";
|
|
7
7
|
import { type OutputMeta } from "./output-meta";
|
|
8
8
|
export declare const BASH_DEFAULT_PREVIEW_LINES = 10;
|
|
9
|
+
export declare function saveBashOriginalArtifactForTests(session: ToolSession, originalText: string): Promise<string | undefined>;
|
|
9
10
|
declare const bashSchemaBase: z.ZodObject<{
|
|
10
11
|
command: z.ZodString;
|
|
11
12
|
env: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
@@ -26,6 +26,8 @@ export interface TabSession {
|
|
|
26
26
|
pending: Map<string, PendingRun>;
|
|
27
27
|
dialogPolicy?: DialogPolicy;
|
|
28
28
|
kindTag: BrowserKindTag;
|
|
29
|
+
/** Session that acquired this tab; used for session-scoped teardown (F13). */
|
|
30
|
+
ownerId?: string;
|
|
29
31
|
}
|
|
30
32
|
export interface AcquireTabOptions {
|
|
31
33
|
url?: string;
|
|
@@ -39,6 +41,8 @@ export interface AcquireTabOptions {
|
|
|
39
41
|
signal?: AbortSignal;
|
|
40
42
|
timeoutMs: number;
|
|
41
43
|
dialogs?: DialogPolicy;
|
|
44
|
+
/** Owning session id so dispose can release only this session's tabs (F13). */
|
|
45
|
+
ownerId?: string;
|
|
42
46
|
}
|
|
43
47
|
export interface AcquireTabResult {
|
|
44
48
|
tab: TabSession;
|
|
@@ -58,6 +62,11 @@ export declare function acquireTab(name: string, browser: BrowserHandle, opts: A
|
|
|
58
62
|
export declare function runInTab(name: string, opts: RunInTabOptions): Promise<RunResultOk>;
|
|
59
63
|
export declare function releaseTab(name: string, opts?: ReleaseTabOptions): Promise<boolean>;
|
|
60
64
|
export declare function releaseAllTabs(opts?: ReleaseTabOptions): Promise<number>;
|
|
65
|
+
/**
|
|
66
|
+
* Release only the tabs owned by `ownerId` (F13 session-scoped teardown). Tabs acquired
|
|
67
|
+
* by other sessions (or with no owner) are left untouched. No-op for a null/empty owner.
|
|
68
|
+
*/
|
|
69
|
+
export declare function releaseTabsForOwner(ownerId: string | null | undefined, opts?: ReleaseTabOptions): Promise<number>;
|
|
61
70
|
export declare function dropHeadlessTabs(): Promise<void>;
|
|
62
71
|
export declare function initializeTabWorkerForTest(worker: WorkerHandle, payload: WorkerInitPayload, timeoutMs: number): Promise<ReadyInfo>;
|
|
63
72
|
export {};
|
|
@@ -60,9 +60,10 @@ export declare function getRowByKey(db: Database, table: string, pk: {
|
|
|
60
60
|
type?: string;
|
|
61
61
|
}, key: string): Record<string, unknown> | null;
|
|
62
62
|
export declare function getRowByRowId(db: Database, table: string, key: string): Record<string, unknown> | null;
|
|
63
|
-
export declare function executeReadQuery(db: Database, sql: string): {
|
|
63
|
+
export declare function executeReadQuery(db: Database, sql: string, maxRows?: number): {
|
|
64
64
|
columns: string[];
|
|
65
65
|
rows: Record<string, unknown>[];
|
|
66
|
+
truncated: boolean;
|
|
66
67
|
};
|
|
67
68
|
export declare function insertRow(db: Database, table: string, data: Record<string, unknown>): void;
|
|
68
69
|
export declare function updateRowByKey(db: Database, table: string, pk: {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@gajae-code/coding-agent",
|
|
4
|
-
"version": "0.5.
|
|
4
|
+
"version": "0.5.3",
|
|
5
5
|
"description": "Gajae Code CLI with read, bash, edit, write tools and session management",
|
|
6
6
|
"homepage": "https://gaebal-gajae.dev",
|
|
7
7
|
"author": "Yeachan-Heo",
|
|
@@ -51,12 +51,12 @@
|
|
|
51
51
|
"@agentclientprotocol/sdk": "0.21.0",
|
|
52
52
|
"@babel/parser": "^7.29.3",
|
|
53
53
|
"@mozilla/readability": "^0.6.0",
|
|
54
|
-
"@gajae-code/stats": "0.5.
|
|
55
|
-
"@gajae-code/agent-core": "0.5.
|
|
56
|
-
"@gajae-code/ai": "0.5.
|
|
57
|
-
"@gajae-code/natives": "0.5.
|
|
58
|
-
"@gajae-code/tui": "0.5.
|
|
59
|
-
"@gajae-code/utils": "0.5.
|
|
54
|
+
"@gajae-code/stats": "0.5.3",
|
|
55
|
+
"@gajae-code/agent-core": "0.5.3",
|
|
56
|
+
"@gajae-code/ai": "0.5.3",
|
|
57
|
+
"@gajae-code/natives": "0.5.3",
|
|
58
|
+
"@gajae-code/tui": "0.5.3",
|
|
59
|
+
"@gajae-code/utils": "0.5.3",
|
|
60
60
|
"@puppeteer/browsers": "^2.13.0",
|
|
61
61
|
"@types/turndown": "5.0.6",
|
|
62
62
|
"@xterm/headless": "^6.0.0",
|