@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.
Files changed (99) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/dist/types/async/job-manager.d.ts +6 -0
  3. package/dist/types/config/model-profiles.d.ts +10 -0
  4. package/dist/types/dap/client.d.ts +2 -1
  5. package/dist/types/edit/read-file.d.ts +6 -0
  6. package/dist/types/eval/js/context-manager.d.ts +3 -0
  7. package/dist/types/eval/js/executor.d.ts +1 -0
  8. package/dist/types/exec/bash-executor.d.ts +2 -0
  9. package/dist/types/gjc-runtime/tmux-sessions.d.ts +7 -1
  10. package/dist/types/lsp/types.d.ts +2 -0
  11. package/dist/types/modes/bridge/bridge-mode.d.ts +1 -0
  12. package/dist/types/modes/components/model-selector.d.ts +2 -0
  13. package/dist/types/modes/components/oauth-selector.d.ts +1 -0
  14. package/dist/types/modes/components/runtime-mcp-add-wizard.d.ts +1 -0
  15. package/dist/types/modes/components/tool-execution.d.ts +1 -0
  16. package/dist/types/modes/interactive-mode.d.ts +1 -0
  17. package/dist/types/modes/types.d.ts +1 -0
  18. package/dist/types/runtime/process-lifecycle.d.ts +108 -0
  19. package/dist/types/runtime-mcp/transports/stdio.d.ts +1 -0
  20. package/dist/types/runtime-mcp/types.d.ts +2 -0
  21. package/dist/types/session/agent-session.d.ts +29 -1
  22. package/dist/types/session/artifacts.d.ts +4 -1
  23. package/dist/types/session/streaming-output.d.ts +12 -0
  24. package/dist/types/slash-commands/helpers/fast-status-report.d.ts +76 -0
  25. package/dist/types/tools/bash.d.ts +1 -0
  26. package/dist/types/tools/browser/tab-supervisor.d.ts +9 -0
  27. package/dist/types/tools/sqlite-reader.d.ts +2 -1
  28. package/dist/types/web/search/providers/codex.d.ts +4 -4
  29. package/package.json +7 -7
  30. package/src/async/job-manager.ts +181 -43
  31. package/src/config/file-lock.ts +9 -1
  32. package/src/config/model-profile-activation.ts +71 -3
  33. package/src/config/model-profiles.ts +39 -14
  34. package/src/dap/client.ts +105 -64
  35. package/src/dap/session.ts +44 -7
  36. package/src/defaults/gjc/skills/deep-interview/SKILL.md +11 -2
  37. package/src/defaults/gjc/skills/ralplan/SKILL.md +2 -2
  38. package/src/defaults/gjc/skills/ultragoal/SKILL.md +2 -2
  39. package/src/edit/read-file.ts +19 -1
  40. package/src/eval/js/context-manager.ts +228 -65
  41. package/src/eval/js/executor.ts +2 -0
  42. package/src/eval/js/index.ts +1 -0
  43. package/src/eval/js/worker-core.ts +10 -6
  44. package/src/eval/py/executor.ts +68 -19
  45. package/src/eval/py/kernel.ts +46 -22
  46. package/src/eval/py/runner.py +68 -14
  47. package/src/exec/bash-executor.ts +49 -13
  48. package/src/gjc-runtime/deep-interview-runtime.ts +14 -13
  49. package/src/gjc-runtime/ralplan-runtime.ts +10 -0
  50. package/src/gjc-runtime/state-runtime.ts +73 -0
  51. package/src/gjc-runtime/tmux-gc.ts +86 -37
  52. package/src/gjc-runtime/tmux-sessions.ts +44 -6
  53. package/src/gjc-runtime/ultragoal-runtime.ts +8 -4
  54. package/src/internal-urls/artifact-protocol.ts +10 -1
  55. package/src/internal-urls/docs-index.generated.ts +2 -2
  56. package/src/lsp/client.ts +64 -26
  57. package/src/lsp/index.ts +2 -1
  58. package/src/lsp/lspmux.ts +33 -9
  59. package/src/lsp/types.ts +2 -0
  60. package/src/modes/bridge/bridge-mode.ts +21 -0
  61. package/src/modes/components/assistant-message.ts +10 -2
  62. package/src/modes/components/bash-execution.ts +5 -1
  63. package/src/modes/components/eval-execution.ts +5 -1
  64. package/src/modes/components/model-selector.ts +34 -2
  65. package/src/modes/components/oauth-selector.ts +5 -0
  66. package/src/modes/components/runtime-mcp-add-wizard.ts +58 -7
  67. package/src/modes/components/skill-message.ts +24 -16
  68. package/src/modes/components/tool-execution.ts +6 -0
  69. package/src/modes/controllers/extension-ui-controller.ts +33 -6
  70. package/src/modes/controllers/input-controller.ts +19 -0
  71. package/src/modes/controllers/selector-controller.ts +6 -1
  72. package/src/modes/interactive-mode.ts +13 -0
  73. package/src/modes/types.ts +1 -0
  74. package/src/modes/utils/ui-helpers.ts +5 -2
  75. package/src/prompts/agents/executor.md +1 -1
  76. package/src/runtime/process-lifecycle.ts +400 -0
  77. package/src/runtime-mcp/manager.ts +164 -50
  78. package/src/runtime-mcp/transports/http.ts +12 -11
  79. package/src/runtime-mcp/transports/stdio.ts +64 -38
  80. package/src/runtime-mcp/types.ts +3 -0
  81. package/src/sdk.ts +27 -0
  82. package/src/session/agent-session.ts +271 -25
  83. package/src/session/artifacts.ts +17 -2
  84. package/src/session/blob-store.ts +36 -2
  85. package/src/session/session-manager.ts +29 -13
  86. package/src/session/streaming-output.ts +95 -3
  87. package/src/setup/model-onboarding-guidance.ts +10 -3
  88. package/src/skill-state/active-state.ts +79 -7
  89. package/src/slash-commands/builtin-registry.ts +30 -3
  90. package/src/slash-commands/helpers/fast-status-report.ts +111 -0
  91. package/src/tools/archive-reader.ts +10 -1
  92. package/src/tools/bash.ts +11 -4
  93. package/src/tools/browser/registry.ts +17 -1
  94. package/src/tools/browser/tab-supervisor.ts +22 -0
  95. package/src/tools/browser.ts +38 -4
  96. package/src/tools/cron.ts +2 -6
  97. package/src/tools/read.ts +11 -12
  98. package/src/tools/sqlite-reader.ts +19 -5
  99. 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);
@@ -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
- return JSON.stringify(value, null, 2) ?? String(value);
388
- } catch {
389
- return String(value);
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
- export function executeReadQuery(db: Database, sql: string): { columns: string[]; rows: Record<string, unknown>[] } {
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
- return {
599
- columns: [...statement.columnNames],
600
- rows: statement.all(),
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.4";
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 `PI_OPENAI_CODE_WEB_SEARCH_MODEL` is set, use it exactly once and surface any
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.4, GPT-5
459
- * OpenAI code backend, GPT-5, …) and retry the next candidate only when OpenAI code backend returns the
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-OpenAI code backend-mini` first on ChatGPT accounts, which OpenAI rejects.
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);