@gajae-code/coding-agent 0.5.2 → 0.5.4
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 +23 -0
- package/dist/types/async/job-manager.d.ts +6 -0
- package/dist/types/config/model-profiles.d.ts +10 -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/modes/interactive-mode.d.ts +1 -0
- package/dist/types/modes/types.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 +29 -1
- package/dist/types/session/artifacts.d.ts +4 -1
- package/dist/types/session/streaming-output.d.ts +12 -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/dist/types/web/search/providers/codex.d.ts +4 -4
- package/package.json +7 -7
- package/src/async/job-manager.ts +181 -43
- package/src/config/file-lock.ts +9 -1
- package/src/config/model-profile-activation.ts +71 -3
- package/src/config/model-profiles.ts +39 -14
- package/src/dap/client.ts +105 -64
- package/src/dap/session.ts +44 -7
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +11 -2
- package/src/defaults/gjc/skills/ralplan/SKILL.md +2 -2
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +2 -2
- 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/deep-interview-runtime.ts +14 -13
- package/src/gjc-runtime/ralplan-runtime.ts +10 -0
- package/src/gjc-runtime/state-runtime.ts +73 -0
- package/src/gjc-runtime/tmux-gc.ts +86 -37
- package/src/gjc-runtime/tmux-sessions.ts +44 -6
- package/src/gjc-runtime/ultragoal-runtime.ts +8 -4
- 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 +19 -0
- package/src/modes/controllers/selector-controller.ts +6 -1
- package/src/modes/interactive-mode.ts +13 -0
- package/src/modes/types.ts +1 -0
- package/src/modes/utils/ui-helpers.ts +5 -2
- package/src/prompts/agents/executor.md +1 -1
- 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 +271 -25
- 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 +95 -3
- package/src/setup/model-onboarding-guidance.ts +10 -3
- package/src/skill-state/active-state.ts +79 -7
- 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/registry.ts +17 -1
- package/src/tools/browser/tab-supervisor.ts +22 -0
- package/src/tools/browser.ts +38 -4
- package/src/tools/cron.ts +2 -6
- package/src/tools/read.ts +11 -12
- package/src/tools/sqlite-reader.ts +19 -5
- package/src/web/search/providers/codex.ts +6 -5
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
* Messages are newline-delimited JSON.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { getProjectDir,
|
|
8
|
+
import { getProjectDir, readJsonl, Snowflake } from "@gajae-code/utils";
|
|
9
|
+
import { type OwnedProcess, spawnOwnedProcess } from "../../runtime/process-lifecycle";
|
|
9
10
|
import type {
|
|
10
11
|
JsonRpcError,
|
|
11
12
|
JsonRpcMessage,
|
|
@@ -24,7 +25,7 @@ import { toJsonRpcError } from "../../runtime-mcp/types";
|
|
|
24
25
|
const CLOSE_WAIT_MS = 1_000;
|
|
25
26
|
|
|
26
27
|
export class StdioTransport implements MCPTransport {
|
|
27
|
-
#process:
|
|
28
|
+
#process: OwnedProcess | null = null;
|
|
28
29
|
#pendingRequests = new Map<
|
|
29
30
|
string | number,
|
|
30
31
|
{
|
|
@@ -34,6 +35,8 @@ export class StdioTransport implements MCPTransport {
|
|
|
34
35
|
>();
|
|
35
36
|
#connected = false;
|
|
36
37
|
#readLoop: Promise<void> | null = null;
|
|
38
|
+
#stderrLoop: Promise<void> | null = null;
|
|
39
|
+
#closePromise: Promise<void> | null = null;
|
|
37
40
|
|
|
38
41
|
onClose?: () => void;
|
|
39
42
|
onError?: (error: Error) => void;
|
|
@@ -46,10 +49,17 @@ export class StdioTransport implements MCPTransport {
|
|
|
46
49
|
return this.#connected;
|
|
47
50
|
}
|
|
48
51
|
|
|
52
|
+
get closeBeforeReconnect(): true {
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
|
|
49
56
|
/**
|
|
50
57
|
* Start the subprocess and begin reading.
|
|
51
58
|
*/
|
|
52
59
|
async connect(): Promise<void> {
|
|
60
|
+
if (this.#closePromise) {
|
|
61
|
+
throw new Error("Transport is closing");
|
|
62
|
+
}
|
|
53
63
|
if (this.#connected) return;
|
|
54
64
|
|
|
55
65
|
const args = this.config.args ?? [];
|
|
@@ -58,11 +68,12 @@ export class StdioTransport implements MCPTransport {
|
|
|
58
68
|
...this.config.env,
|
|
59
69
|
};
|
|
60
70
|
|
|
61
|
-
this.#process =
|
|
71
|
+
this.#process = spawnOwnedProcess([this.config.command, ...args], {
|
|
62
72
|
cwd: this.config.cwd ?? getProjectDir(),
|
|
63
73
|
env,
|
|
64
74
|
stdin: "pipe",
|
|
65
|
-
|
|
75
|
+
gracefulMs: CLOSE_WAIT_MS,
|
|
76
|
+
name: `mcp-stdio:${this.config.command}`,
|
|
66
77
|
});
|
|
67
78
|
|
|
68
79
|
this.#connected = true;
|
|
@@ -71,13 +82,13 @@ export class StdioTransport implements MCPTransport {
|
|
|
71
82
|
this.#readLoop = this.#startReadLoop();
|
|
72
83
|
|
|
73
84
|
// Log stderr for debugging
|
|
74
|
-
this.#startStderrLoop();
|
|
85
|
+
this.#stderrLoop = this.#startStderrLoop();
|
|
75
86
|
}
|
|
76
87
|
|
|
77
88
|
async #startReadLoop(): Promise<void> {
|
|
78
|
-
if (!this.#process?.stdout) return;
|
|
89
|
+
if (!this.#process?.child.stdout) return;
|
|
79
90
|
try {
|
|
80
|
-
for await (const line of readJsonl(this.#process.stdout)) {
|
|
91
|
+
for await (const line of readJsonl(this.#process.child.stdout)) {
|
|
81
92
|
if (!this.#connected) break;
|
|
82
93
|
try {
|
|
83
94
|
this.#handleMessage(line as JsonRpcMessage);
|
|
@@ -95,9 +106,9 @@ export class StdioTransport implements MCPTransport {
|
|
|
95
106
|
}
|
|
96
107
|
|
|
97
108
|
async #startStderrLoop(): Promise<void> {
|
|
98
|
-
if (!this.#process?.stderr) return;
|
|
109
|
+
if (!this.#process?.child.stderr) return;
|
|
99
110
|
|
|
100
|
-
const reader = this.#process.stderr.getReader();
|
|
111
|
+
const reader = this.#process.child.stderr.getReader();
|
|
101
112
|
const decoder = new TextDecoder();
|
|
102
113
|
|
|
103
114
|
try {
|
|
@@ -168,26 +179,23 @@ export class StdioTransport implements MCPTransport {
|
|
|
168
179
|
}
|
|
169
180
|
}
|
|
170
181
|
|
|
182
|
+
#getStdin(): Bun.FileSink | null {
|
|
183
|
+
const stdin = this.#process?.child.stdin;
|
|
184
|
+
return typeof stdin === "object" && stdin !== null ? stdin : null;
|
|
185
|
+
}
|
|
186
|
+
|
|
171
187
|
#sendResponse(id: string | number, result?: unknown, error?: JsonRpcError): void {
|
|
172
|
-
|
|
188
|
+
const stdin = this.#getStdin();
|
|
189
|
+
if (!this.#connected || !stdin) return;
|
|
173
190
|
const response = error
|
|
174
191
|
? { jsonrpc: "2.0" as const, id, error }
|
|
175
192
|
: { jsonrpc: "2.0" as const, id, result: result ?? {} };
|
|
176
|
-
|
|
177
|
-
|
|
193
|
+
stdin.write(`${JSON.stringify(response)}\n`);
|
|
194
|
+
stdin.flush();
|
|
178
195
|
}
|
|
179
196
|
|
|
180
197
|
#handleClose(): void {
|
|
181
|
-
|
|
182
|
-
this.#connected = false;
|
|
183
|
-
|
|
184
|
-
// Reject all pending requests
|
|
185
|
-
for (const [, pending] of this.#pendingRequests) {
|
|
186
|
-
pending.reject(new Error("Transport closed"));
|
|
187
|
-
}
|
|
188
|
-
this.#pendingRequests.clear();
|
|
189
|
-
|
|
190
|
-
this.onClose?.();
|
|
198
|
+
void this.#closeInternal(true);
|
|
191
199
|
}
|
|
192
200
|
|
|
193
201
|
async request<T = unknown>(
|
|
@@ -195,7 +203,8 @@ export class StdioTransport implements MCPTransport {
|
|
|
195
203
|
params?: Record<string, unknown>,
|
|
196
204
|
options?: MCPRequestOptions,
|
|
197
205
|
): Promise<T> {
|
|
198
|
-
|
|
206
|
+
const stdin = this.#getStdin();
|
|
207
|
+
if (!this.#connected || !stdin) {
|
|
199
208
|
throw new Error("Transport not connected");
|
|
200
209
|
}
|
|
201
210
|
|
|
@@ -261,8 +270,8 @@ export class StdioTransport implements MCPTransport {
|
|
|
261
270
|
const message = `${JSON.stringify(request)}\n`;
|
|
262
271
|
try {
|
|
263
272
|
// Bun's FileSink has write() method directly
|
|
264
|
-
|
|
265
|
-
|
|
273
|
+
stdin.write(message);
|
|
274
|
+
stdin.flush();
|
|
266
275
|
} catch (error: unknown) {
|
|
267
276
|
cleanup();
|
|
268
277
|
reject(error instanceof Error ? error : new Error(String(error)));
|
|
@@ -272,7 +281,8 @@ export class StdioTransport implements MCPTransport {
|
|
|
272
281
|
}
|
|
273
282
|
|
|
274
283
|
async notify(method: string, params?: Record<string, unknown>): Promise<void> {
|
|
275
|
-
|
|
284
|
+
const stdin = this.#getStdin();
|
|
285
|
+
if (!this.#connected || !stdin) {
|
|
276
286
|
throw new Error("Transport not connected");
|
|
277
287
|
}
|
|
278
288
|
|
|
@@ -284,35 +294,51 @@ export class StdioTransport implements MCPTransport {
|
|
|
284
294
|
|
|
285
295
|
const message = `${JSON.stringify(notification)}\n`;
|
|
286
296
|
// Bun's FileSink has write() method directly
|
|
287
|
-
|
|
288
|
-
|
|
297
|
+
stdin.write(message);
|
|
298
|
+
stdin.flush();
|
|
289
299
|
}
|
|
290
300
|
|
|
291
301
|
async close(): Promise<void> {
|
|
292
|
-
|
|
302
|
+
await this.#closeInternal(false);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
#closeInternal(fromReadLoop: boolean): Promise<void> {
|
|
306
|
+
if (this.#closePromise) return this.#closePromise;
|
|
307
|
+
this.#closePromise = this.#finishClose(fromReadLoop).finally(() => {
|
|
308
|
+
this.#closePromise = null;
|
|
309
|
+
});
|
|
310
|
+
return this.#closePromise;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
async #finishClose(fromReadLoop: boolean): Promise<void> {
|
|
314
|
+
const wasConnected = this.#connected;
|
|
293
315
|
this.#connected = false;
|
|
294
316
|
|
|
295
|
-
// Reject pending requests
|
|
296
317
|
for (const [, pending] of this.#pendingRequests) {
|
|
297
318
|
pending.reject(new Error("Transport closed"));
|
|
298
319
|
}
|
|
299
320
|
this.#pendingRequests.clear();
|
|
300
321
|
|
|
301
|
-
|
|
322
|
+
const stdin = this.#getStdin();
|
|
302
323
|
const process = this.#process;
|
|
324
|
+
this.#process = null;
|
|
303
325
|
if (process) {
|
|
304
|
-
|
|
305
|
-
await
|
|
306
|
-
|
|
326
|
+
stdin?.end();
|
|
327
|
+
await process.dispose().catch(() => {});
|
|
328
|
+
await process.awaitExit({ timeoutMs: CLOSE_WAIT_MS }).catch(() => ({ exited: false, code: null }));
|
|
307
329
|
}
|
|
308
330
|
|
|
309
|
-
|
|
310
|
-
if (this.#readLoop) {
|
|
331
|
+
if (!fromReadLoop && this.#readLoop) {
|
|
311
332
|
await this.#readLoop.catch(() => {});
|
|
312
|
-
|
|
333
|
+
}
|
|
334
|
+
this.#readLoop = null;
|
|
335
|
+
|
|
336
|
+
if (this.#stderrLoop) {
|
|
337
|
+
await this.#stderrLoop.catch(() => {});
|
|
338
|
+
this.#stderrLoop = null;
|
|
313
339
|
}
|
|
314
340
|
|
|
315
|
-
this.onClose?.();
|
|
341
|
+
if (wasConnected) this.onClose?.();
|
|
316
342
|
}
|
|
317
343
|
}
|
|
318
344
|
|
package/src/runtime-mcp/types.ts
CHANGED
|
@@ -225,6 +225,9 @@ export interface MCPTransport {
|
|
|
225
225
|
/** Close the transport */
|
|
226
226
|
close(): Promise<void>;
|
|
227
227
|
|
|
228
|
+
/** Whether close must finish before reconnect can safely spawn a replacement. */
|
|
229
|
+
readonly closeBeforeReconnect?: boolean;
|
|
230
|
+
|
|
228
231
|
/** Whether the transport is connected */
|
|
229
232
|
readonly connected: boolean;
|
|
230
233
|
|
package/src/sdk.ts
CHANGED
|
@@ -52,6 +52,7 @@ import { resolveConfigValue } from "./config/resolve-config-value";
|
|
|
52
52
|
import { getEmbeddedDefaultGjcSkills } from "./defaults/gjc-defaults";
|
|
53
53
|
import { BUNDLED_GROK_BUILD_EXTENSION_ID, getBundledGrokBuildExtensionFactory } from "./defaults/gjc-grok-cli";
|
|
54
54
|
import { initializeWithSettings } from "./discovery";
|
|
55
|
+
import { disposeAllVmContexts, disposeVmContextsByOwner } from "./eval/js/context-manager";
|
|
55
56
|
import { disposeAllKernelSessions, disposeKernelSessionsByOwner } from "./eval/py/executor";
|
|
56
57
|
import { TtsrManager } from "./export/ttsr";
|
|
57
58
|
import type { CustomCommandsLoadResult, LoadedCustomCommand } from "./extensibility/custom-commands";
|
|
@@ -414,6 +415,7 @@ function getDefaultAgentDir(): string {
|
|
|
414
415
|
*/
|
|
415
416
|
export async function discoverAuthStorage(agentDir: string = getDefaultAgentDir()): Promise<AuthStorage> {
|
|
416
417
|
const brokerConfig = await resolveAuthBrokerConfig();
|
|
418
|
+
const credentialRankingMode = resolveCredentialRankingMode();
|
|
417
419
|
if (brokerConfig) {
|
|
418
420
|
const client = new AuthBrokerClient({ url: brokerConfig.url, token: brokerConfig.token });
|
|
419
421
|
const initialResult = await client.fetchSnapshot();
|
|
@@ -424,6 +426,7 @@ export async function discoverAuthStorage(agentDir: string = getDefaultAgentDir(
|
|
|
424
426
|
const storage = new AuthStorage(store, {
|
|
425
427
|
configValueResolver: resolveConfigValue,
|
|
426
428
|
sourceLabel: `broker ${brokerConfig.url}`,
|
|
429
|
+
credentialRankingMode,
|
|
427
430
|
});
|
|
428
431
|
await storage.reload();
|
|
429
432
|
return storage;
|
|
@@ -432,11 +435,25 @@ export async function discoverAuthStorage(agentDir: string = getDefaultAgentDir(
|
|
|
432
435
|
const storage = await AuthStorage.create(dbPath, {
|
|
433
436
|
configValueResolver: resolveConfigValue,
|
|
434
437
|
sourceLabel: `local ${dbPath}`,
|
|
438
|
+
credentialRankingMode,
|
|
435
439
|
});
|
|
436
440
|
await storage.reload();
|
|
437
441
|
return storage;
|
|
438
442
|
}
|
|
439
443
|
|
|
444
|
+
/**
|
|
445
|
+
* Opt-in multi-account credential ranking mode, read from the
|
|
446
|
+
* `GJC_CREDENTIAL_RANKING_MODE` env var. Unset/unknown → `undefined`, leaving
|
|
447
|
+
* {@link AuthStorage}'s default (`balanced`) untouched. `earliest-reset`
|
|
448
|
+
* switches to earliest-expiry-first selection so soon-to-reset tumbling-window
|
|
449
|
+
* quota is drained before it is lost.
|
|
450
|
+
*/
|
|
451
|
+
function resolveCredentialRankingMode(): "balanced" | "earliest-reset" | undefined {
|
|
452
|
+
const raw = process.env.GJC_CREDENTIAL_RANKING_MODE?.trim();
|
|
453
|
+
if (raw === "balanced" || raw === "earliest-reset") return raw;
|
|
454
|
+
return undefined;
|
|
455
|
+
}
|
|
456
|
+
|
|
440
457
|
/**
|
|
441
458
|
* Discover extensions from cwd.
|
|
442
459
|
*/
|
|
@@ -570,6 +587,14 @@ function registerPythonCleanup(): void {
|
|
|
570
587
|
postmortem.register("python-cleanup", disposeAllKernelSessions);
|
|
571
588
|
}
|
|
572
589
|
|
|
590
|
+
let jsVmCleanupRegistered = false;
|
|
591
|
+
|
|
592
|
+
function registerJsVmCleanup(): void {
|
|
593
|
+
if (jsVmCleanupRegistered) return;
|
|
594
|
+
jsVmCleanupRegistered = true;
|
|
595
|
+
postmortem.register("js-vm-cleanup", disposeAllVmContexts);
|
|
596
|
+
}
|
|
597
|
+
|
|
573
598
|
/**
|
|
574
599
|
* Resolve whether to enable append-only context mode based on the setting and provider.
|
|
575
600
|
*
|
|
@@ -806,6 +831,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
806
831
|
|
|
807
832
|
registerSshCleanup();
|
|
808
833
|
registerPythonCleanup();
|
|
834
|
+
registerJsVmCleanup();
|
|
809
835
|
|
|
810
836
|
// Pin authStorage to modelRegistry.authStorage: ModelRegistry.getApiKey() routes refresh
|
|
811
837
|
// failures through that instance, so any divergent storage handed to the bridge / mcpManager
|
|
@@ -2200,6 +2226,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2200
2226
|
} else {
|
|
2201
2227
|
if (hasRegistered) agentRegistry.unregister(resolvedAgentId);
|
|
2202
2228
|
await disposeKernelSessionsByOwner(evalKernelOwnerId);
|
|
2229
|
+
await disposeVmContextsByOwner(evalKernelOwnerId);
|
|
2203
2230
|
}
|
|
2204
2231
|
} catch (cleanupError) {
|
|
2205
2232
|
logger.warn("Failed to clean up createAgentSession resources after startup error", {
|