@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
|
@@ -55,6 +55,8 @@ export interface TabSession {
|
|
|
55
55
|
pending: Map<string, PendingRun>;
|
|
56
56
|
dialogPolicy?: DialogPolicy;
|
|
57
57
|
kindTag: BrowserKindTag;
|
|
58
|
+
/** Session that acquired this tab; used for session-scoped teardown (F13). */
|
|
59
|
+
ownerId?: string;
|
|
58
60
|
}
|
|
59
61
|
|
|
60
62
|
export interface AcquireTabOptions {
|
|
@@ -65,6 +67,8 @@ export interface AcquireTabOptions {
|
|
|
65
67
|
signal?: AbortSignal;
|
|
66
68
|
timeoutMs: number;
|
|
67
69
|
dialogs?: DialogPolicy;
|
|
70
|
+
/** Owning session id so dispose can release only this session's tabs (F13). */
|
|
71
|
+
ownerId?: string;
|
|
68
72
|
}
|
|
69
73
|
|
|
70
74
|
export interface AcquireTabResult {
|
|
@@ -161,6 +165,7 @@ export async function acquireTab(
|
|
|
161
165
|
pending: new Map(),
|
|
162
166
|
dialogPolicy: opts.dialogs,
|
|
163
167
|
kindTag: browser.kind.kind,
|
|
168
|
+
ownerId: opts.ownerId,
|
|
164
169
|
};
|
|
165
170
|
worker.onMessage(msg => handleTabMessage(tab, msg));
|
|
166
171
|
tabs.set(name, tab);
|
|
@@ -254,6 +259,23 @@ export async function releaseAllTabs(opts: ReleaseTabOptions = {}): Promise<numb
|
|
|
254
259
|
return count;
|
|
255
260
|
}
|
|
256
261
|
|
|
262
|
+
/**
|
|
263
|
+
* Release only the tabs owned by `ownerId` (F13 session-scoped teardown). Tabs acquired
|
|
264
|
+
* by other sessions (or with no owner) are left untouched. No-op for a null/empty owner.
|
|
265
|
+
*/
|
|
266
|
+
export async function releaseTabsForOwner(
|
|
267
|
+
ownerId: string | null | undefined,
|
|
268
|
+
opts: ReleaseTabOptions = {},
|
|
269
|
+
): Promise<number> {
|
|
270
|
+
if (!ownerId) return 0;
|
|
271
|
+
const names = [...tabs.entries()].filter(([, tab]) => tab.ownerId === ownerId).map(([name]) => name);
|
|
272
|
+
let count = 0;
|
|
273
|
+
for (const name of names) {
|
|
274
|
+
if (await releaseTab(name, opts)) count++;
|
|
275
|
+
}
|
|
276
|
+
return count;
|
|
277
|
+
}
|
|
278
|
+
|
|
257
279
|
export async function dropHeadlessTabs(): Promise<void> {
|
|
258
280
|
const names = [...tabs.values()].filter(tab => tab.kindTag === "headless").map(tab => tab.name);
|
|
259
281
|
for (const name of names) await releaseTab(name);
|
package/src/tools/browser.ts
CHANGED
|
@@ -213,6 +213,7 @@ export class BrowserTool implements AgentTool<typeof browserSchema, BrowserToolD
|
|
|
213
213
|
|
|
214
214
|
const result = await untilAborted(signal, () =>
|
|
215
215
|
acquireTab(name, browser, {
|
|
216
|
+
ownerId: this.session.getSessionId?.() ?? undefined,
|
|
216
217
|
url: params.url,
|
|
217
218
|
waitUntil: params.wait_until,
|
|
218
219
|
viewport: params.viewport
|
|
@@ -381,11 +382,44 @@ function sameBrowserKind(a: BrowserKind, b: BrowserKind): boolean {
|
|
|
381
382
|
return false;
|
|
382
383
|
}
|
|
383
384
|
|
|
385
|
+
/** Max chars of a browser return value surfaced into the tool result (F22). */
|
|
386
|
+
const MAX_BROWSER_RETURN_CHARS = 256 * 1024;
|
|
387
|
+
|
|
388
|
+
const BROWSER_RETURN_BUDGET_EXCEEDED = Symbol("browser-return-budget-exceeded");
|
|
389
|
+
|
|
390
|
+
/** Hard-cap any surfaced browser return string at the byte/char limit with a notice. */
|
|
391
|
+
function capBrowserReturn(text: string): string {
|
|
392
|
+
if (text.length <= MAX_BROWSER_RETURN_CHARS) return text;
|
|
393
|
+
return `${text.slice(0, MAX_BROWSER_RETURN_CHARS)}\n\n[Browser return value truncated: ${text.length} chars exceeds the ${MAX_BROWSER_RETURN_CHARS}-char cap.]`;
|
|
394
|
+
}
|
|
395
|
+
|
|
384
396
|
function stringifyReturnValue(value: unknown): string {
|
|
385
|
-
if (typeof value === "string") return value;
|
|
397
|
+
if (typeof value === "string") return capBrowserReturn(value);
|
|
398
|
+
// F22: bound the serialization itself — the replacer tracks running size and aborts early so a
|
|
399
|
+
// huge object/array cannot build megabytes before truncation — AND hard-cap the final string,
|
|
400
|
+
// since pretty-print structural overhead (indent/braces/commas) is not counted by the budget.
|
|
401
|
+
let budget = MAX_BROWSER_RETURN_CHARS;
|
|
386
402
|
try {
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
403
|
+
const text = JSON.stringify(
|
|
404
|
+
value,
|
|
405
|
+
(_key, val) => {
|
|
406
|
+
if (typeof val === "string") budget -= val.length + 4;
|
|
407
|
+
else if (typeof val === "number" || typeof val === "boolean") budget -= 8;
|
|
408
|
+
else budget -= 2;
|
|
409
|
+
if (budget < 0) throw BROWSER_RETURN_BUDGET_EXCEEDED;
|
|
410
|
+
return val;
|
|
411
|
+
},
|
|
412
|
+
2,
|
|
413
|
+
);
|
|
414
|
+
return text === undefined ? capBrowserReturn(String(value)) : capBrowserReturn(text);
|
|
415
|
+
} catch (error) {
|
|
416
|
+
if (error === BROWSER_RETURN_BUDGET_EXCEEDED) {
|
|
417
|
+
return `[Browser return value too large to serialize (exceeds the ${MAX_BROWSER_RETURN_CHARS}-char cap). Return a smaller or summarized value from the page script.]`;
|
|
418
|
+
}
|
|
419
|
+
try {
|
|
420
|
+
return capBrowserReturn(String(value));
|
|
421
|
+
} catch {
|
|
422
|
+
return "[unserializable browser return value]";
|
|
423
|
+
}
|
|
390
424
|
}
|
|
391
425
|
}
|
package/src/tools/cron.ts
CHANGED
|
@@ -700,13 +700,9 @@ export class CronDeleteTool implements AgentTool<typeof cronDeleteSchema, CronDe
|
|
|
700
700
|
): Promise<AgentToolResult<CronDeleteToolDetails>> {
|
|
701
701
|
const ownerId = this.session.getAgentId?.() ?? undefined;
|
|
702
702
|
const deleted = deleteRecord(ownerId, params.id);
|
|
703
|
+
const text = deleted ? `Cancelled ${params.id}` : `No scheduled task '${params.id}' found; nothing to cancel.`;
|
|
703
704
|
return {
|
|
704
|
-
content: [
|
|
705
|
-
{
|
|
706
|
-
type: "text",
|
|
707
|
-
text: deleted ? `Cancelled ${params.id}` : `Failed to remove scheduled task '${params.id}'`,
|
|
708
|
-
},
|
|
709
|
-
],
|
|
705
|
+
content: [{ type: "text", text }],
|
|
710
706
|
details: { id: params.id, deleted },
|
|
711
707
|
};
|
|
712
708
|
}
|
package/src/tools/read.ts
CHANGED
|
@@ -1270,19 +1270,18 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
1270
1270
|
}
|
|
1271
1271
|
case "raw": {
|
|
1272
1272
|
const result = executeReadQuery(db, selector.sql);
|
|
1273
|
+
const table = renderTable(result.columns, result.rows, {
|
|
1274
|
+
totalCount: result.rows.length,
|
|
1275
|
+
offset: 0,
|
|
1276
|
+
limit: result.rows.length || DEFAULT_MAX_LINES,
|
|
1277
|
+
table: "query",
|
|
1278
|
+
dbPath: resolvedSqlitePath.absolutePath,
|
|
1279
|
+
});
|
|
1280
|
+
const body = result.truncated
|
|
1281
|
+
? `${table}\n\n[Output truncated to the first ${result.rows.length} rows; add a LIMIT clause to the query to bound or page the result.]`
|
|
1282
|
+
: table;
|
|
1273
1283
|
return toolResult<ReadToolDetails>(details)
|
|
1274
|
-
.text(
|
|
1275
|
-
prependSuffixResolutionNotice(
|
|
1276
|
-
renderTable(result.columns, result.rows, {
|
|
1277
|
-
totalCount: result.rows.length,
|
|
1278
|
-
offset: 0,
|
|
1279
|
-
limit: result.rows.length || DEFAULT_MAX_LINES,
|
|
1280
|
-
table: "query",
|
|
1281
|
-
dbPath: resolvedSqlitePath.absolutePath,
|
|
1282
|
-
}),
|
|
1283
|
-
resolvedSqlitePath.suffixResolution,
|
|
1284
|
-
),
|
|
1285
|
-
)
|
|
1284
|
+
.text(prependSuffixResolutionNotice(body, resolvedSqlitePath.suffixResolution))
|
|
1286
1285
|
.sourcePath(resolvedSqlitePath.absolutePath)
|
|
1287
1286
|
.done();
|
|
1288
1287
|
}
|
|
@@ -590,15 +590,29 @@ export function getRowByRowId(db: Database, table: string, key: string): Record<
|
|
|
590
590
|
.get(binding);
|
|
591
591
|
}
|
|
592
592
|
|
|
593
|
-
|
|
593
|
+
const MAX_RAW_QUERY_ROWS = 1000;
|
|
594
|
+
|
|
595
|
+
export function executeReadQuery(
|
|
596
|
+
db: Database,
|
|
597
|
+
sql: string,
|
|
598
|
+
maxRows: number = MAX_RAW_QUERY_ROWS,
|
|
599
|
+
): { columns: string[]; rows: Record<string, unknown>[]; truncated: boolean } {
|
|
594
600
|
const statement = db.prepare<SqliteRow, []>(sql);
|
|
595
601
|
if (statement.paramsCount > 0) {
|
|
596
602
|
throw new ToolError("SQLite raw queries do not support bound parameters");
|
|
597
603
|
}
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
604
|
+
// Stream rows and stop at the cap (F20): a raw `q=SELECT ...` over a huge table
|
|
605
|
+
// must not materialize every row into memory via statement.all().
|
|
606
|
+
const rows: Record<string, unknown>[] = [];
|
|
607
|
+
let truncated = false;
|
|
608
|
+
for (const row of statement.iterate()) {
|
|
609
|
+
if (rows.length >= maxRows) {
|
|
610
|
+
truncated = true;
|
|
611
|
+
break;
|
|
612
|
+
}
|
|
613
|
+
rows.push(row as Record<string, unknown>);
|
|
614
|
+
}
|
|
615
|
+
return { columns: [...statement.columnNames], rows, truncated };
|
|
602
616
|
}
|
|
603
617
|
|
|
604
618
|
export function insertRow(db: Database, table: string, data: Record<string, unknown>): void {
|
|
@@ -19,8 +19,9 @@ import { classifyProviderHttpError, withHardTimeout } from "./utils";
|
|
|
19
19
|
|
|
20
20
|
const CODEX_BASE_URL = "https://chatgpt.com/backend-api";
|
|
21
21
|
const CODEX_RESPONSES_PATH = "/codex/responses";
|
|
22
|
-
const FALLBACK_MODEL = "gpt-5.
|
|
22
|
+
const FALLBACK_MODEL = "gpt-5.5";
|
|
23
23
|
const DEFAULT_MODEL_PREFERENCES = [
|
|
24
|
+
"gpt-5.5",
|
|
24
25
|
"gpt-5.4",
|
|
25
26
|
"gpt-5-codex",
|
|
26
27
|
"gpt-5",
|
|
@@ -453,12 +454,12 @@ async function callCodexSearch(
|
|
|
453
454
|
* Executes a web search using OpenAI code provider's built-in web search tool.
|
|
454
455
|
*
|
|
455
456
|
* Default-model behavior:
|
|
456
|
-
* - If `
|
|
457
|
+
* - If `PI_CODEX_WEB_SEARCH_MODEL` is set, use it exactly once and surface any
|
|
457
458
|
* upstream error verbatim.
|
|
458
|
-
* - Otherwise prefer ChatGPT-account-safe bundled defaults (GPT-5.
|
|
459
|
-
*
|
|
459
|
+
* - Otherwise prefer ChatGPT-account-safe bundled defaults (GPT-5.5, GPT-5.4,
|
|
460
|
+
* GPT-5 code backend, …) and retry the next candidate only when OpenAI code backend returns the
|
|
460
461
|
* known 400 "model is not supported" family. This avoids selecting
|
|
461
|
-
* `gpt-5-
|
|
462
|
+
* `gpt-5-codex-mini` first on ChatGPT accounts, which OpenAI rejects.
|
|
462
463
|
*/
|
|
463
464
|
export async function searchCodex(params: SearchParams): Promise<SearchResponse> {
|
|
464
465
|
const auth = await findCodexAuth(params.authStorage, params.sessionId, params.signal);
|