@gajae-code/coding-agent 0.18.5 → 0.18.7

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 (133) hide show
  1. package/CHANGELOG.md +140 -0
  2. package/dist/types/capability/types.d.ts +4 -0
  3. package/dist/types/cli-commands.d.ts +10 -0
  4. package/dist/types/cli-ordinary.d.ts +13 -0
  5. package/dist/types/cli.d.ts +12 -3
  6. package/dist/types/commands/contribution-prep.d.ts +0 -3
  7. package/dist/types/commands/sdk.d.ts +3 -0
  8. package/dist/types/commands/update.d.ts +1 -0
  9. package/dist/types/config/model-profile-activation.d.ts +19 -0
  10. package/dist/types/config/settings.d.ts +4 -0
  11. package/dist/types/coordinator-mcp/session-reaper.d.ts +5 -0
  12. package/dist/types/discovery/helpers.d.ts +2 -2
  13. package/dist/types/edit/modes/replace.d.ts +3 -3
  14. package/dist/types/edit/streaming.d.ts +2 -2
  15. package/dist/types/exec/bash-shell-supervisor.d.ts +12 -0
  16. package/dist/types/runtime-mcp/elicitation.d.ts +12 -0
  17. package/dist/types/sdk/broker/discovery.d.ts +8 -0
  18. package/dist/types/sdk/broker/ensure.d.ts +1 -0
  19. package/dist/types/sdk/broker/lifecycle-ledger.d.ts +4 -0
  20. package/dist/types/sdk/broker/lifecycle.d.ts +9 -0
  21. package/dist/types/sdk/broker/runtime.d.ts +1 -1
  22. package/dist/types/sdk/broker/task-artifact-owner-validation.d.ts +55 -0
  23. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +1 -1
  24. package/dist/types/sdk/bus/telegram-daemon.d.ts +4 -2
  25. package/dist/types/sdk/cli/diagnostics-cli.d.ts +18 -0
  26. package/dist/types/sdk/diagnostics/observe-broker.d.ts +185 -0
  27. package/dist/types/sdk/host/control/operations.d.ts +1 -1
  28. package/dist/types/sdk/host/host.d.ts +6 -0
  29. package/dist/types/sdk/index.d.ts +1 -0
  30. package/dist/types/sdk/router/session-router.d.ts +9 -0
  31. package/dist/types/sdk/session.d.ts +13 -0
  32. package/dist/types/session/agent-session.d.ts +5 -1
  33. package/dist/types/session/internal/managed-session-storage.d.ts +11 -1
  34. package/dist/types/session/internal/task-artifact-owner-access.d.ts +17 -0
  35. package/dist/types/session/internal/task-artifact-owner-transcript.d.ts +3 -0
  36. package/dist/types/session/session-retirement.d.ts +47 -0
  37. package/dist/types/session/session-storage.d.ts +0 -46
  38. package/dist/types/session/session-transcript-header.d.ts +2 -0
  39. package/dist/types/session/task-artifact-owner-codec.d.ts +107 -0
  40. package/dist/types/session/task-artifact-owner-retirement.d.ts +7 -0
  41. package/dist/types/session/task-artifact-owner.d.ts +10 -0
  42. package/dist/types/tools/ask-contract.d.ts +75 -254
  43. package/dist/types/tools/bash.d.ts +1 -1
  44. package/package.json +7 -7
  45. package/scripts/build-sdk-package-smoke.ts +5 -0
  46. package/scripts/resident-memory-bench.ts +174 -20
  47. package/src/capability/index.ts +1 -0
  48. package/src/capability/types.ts +4 -0
  49. package/src/cli/config-cli.ts +8 -18
  50. package/src/cli/public-command-entry.ts +5 -0
  51. package/src/cli/ssh-cli.ts +6 -3
  52. package/src/cli/web-search-cli.ts +9 -3
  53. package/src/cli-commands.ts +14 -0
  54. package/src/cli-main.ts +7 -8
  55. package/src/cli-ordinary.ts +197 -0
  56. package/src/cli.ts +25 -182
  57. package/src/commands/contribution-prep.ts +1 -3
  58. package/src/commands/sdk.ts +171 -21
  59. package/src/commands/update.ts +15 -1
  60. package/src/config/file-lock.ts +91 -2
  61. package/src/config/model-profile-activation.ts +70 -4
  62. package/src/config/model-profile-contract.ts +4 -1
  63. package/src/config/model-profiles.ts +23 -31
  64. package/src/config/model-resolver.ts +9 -1
  65. package/src/config/settings.ts +44 -4
  66. package/src/coordinator-mcp/event-webhook.ts +19 -14
  67. package/src/coordinator-mcp/policy.ts +14 -4
  68. package/src/coordinator-mcp/server.ts +142 -7
  69. package/src/coordinator-mcp/session-reaper.ts +9 -1
  70. package/src/defaults/gjc/skills/deep-interview/SKILL.md +1 -1
  71. package/src/discovery/helpers.ts +9 -3
  72. package/src/discovery/ssh.ts +8 -3
  73. package/src/edit/modes/replace.ts +6 -6
  74. package/src/edit/streaming.ts +15 -15
  75. package/src/exec/bash-shell-supervisor.ts +60 -3
  76. package/src/gjc-runtime/gc-runtime.ts +2 -1
  77. package/src/hindsight/config.ts +9 -3
  78. package/src/internal-urls/docs-index.generated.ts +11 -10
  79. package/src/memories/index.ts +8 -2
  80. package/src/modes/acp/acp-agent.ts +724 -370
  81. package/src/modes/components/tool-execution.ts +1 -1
  82. package/src/prompts/tools/ask.md +2 -0
  83. package/src/runtime-mcp/client.ts +13 -4
  84. package/src/runtime-mcp/elicitation.ts +142 -0
  85. package/src/sdk/broker/broker.ts +54 -1
  86. package/src/sdk/broker/daemon-entry.ts +67 -6
  87. package/src/sdk/broker/discovery.ts +8 -0
  88. package/src/sdk/broker/ensure.ts +156 -20
  89. package/src/sdk/broker/lifecycle-ledger.ts +110 -12
  90. package/src/sdk/broker/lifecycle.ts +334 -348
  91. package/src/sdk/broker/runtime.ts +5 -1
  92. package/src/sdk/broker/task-artifact-owner-validation.ts +259 -0
  93. package/src/sdk/broker/transport.ts +5 -1
  94. package/src/sdk/bus/chat-daemon-control.ts +2 -2
  95. package/src/sdk/bus/chat-daemon-runtime.ts +3 -0
  96. package/src/sdk/bus/index.ts +3 -3
  97. package/src/sdk/bus/telegram-daemon-contract.ts +1 -1
  98. package/src/sdk/bus/telegram-daemon-control.ts +15 -12
  99. package/src/sdk/bus/telegram-daemon.ts +38 -3
  100. package/src/sdk/cli/diagnostics-cli.ts +247 -0
  101. package/src/sdk/diagnostics/observe-broker.ts +654 -0
  102. package/src/sdk/host/control/dispatch.ts +52 -4
  103. package/src/sdk/host/control/operations.ts +1 -1
  104. package/src/sdk/host/host.ts +6 -0
  105. package/src/sdk/host/session-runtime.test.ts +83 -4
  106. package/src/sdk/host/session-runtime.ts +15 -7
  107. package/src/sdk/index.ts +1 -0
  108. package/src/sdk/lifecycle-session.ts +1 -0
  109. package/src/sdk/mcp/server.ts +15 -3
  110. package/src/sdk/protocol/operation-registry.ts +1 -1
  111. package/src/sdk/router/session-router.ts +26 -3
  112. package/src/sdk/session.ts +51 -8
  113. package/src/session/agent-session.ts +169 -43
  114. package/src/session/blob-store.ts +78 -33
  115. package/src/session/internal/managed-session-storage.ts +203 -4
  116. package/src/session/internal/task-artifact-owner-access.ts +229 -0
  117. package/src/session/internal/task-artifact-owner-transcript.ts +68 -0
  118. package/src/session/session-retirement.ts +155 -0
  119. package/src/session/session-storage.ts +1 -160
  120. package/src/session/session-transcript-header.ts +17 -0
  121. package/src/session/task-artifact-owner-codec.ts +578 -0
  122. package/src/session/task-artifact-owner-retirement.ts +475 -0
  123. package/src/session/task-artifact-owner.ts +289 -0
  124. package/src/setup/hermes/templates/operator-instructions.v1.md +19 -1
  125. package/src/setup/hermes-setup.ts +4 -1
  126. package/src/slash-commands/builtin-registry.ts +8 -5
  127. package/src/tools/ask-contract.test.ts +76 -1
  128. package/src/tools/ask-contract.ts +57 -6
  129. package/src/tools/ask.ts +16 -7
  130. package/src/tools/bash.ts +38 -29
  131. package/src/tools/descriptors.test.ts +29 -0
  132. package/src/tools/tool-catalog.generated.ts +7 -4
  133. package/src/web/search/index.ts +2 -2
@@ -7,7 +7,7 @@
7
7
  * - compute unified diff previews for the in-flight args
8
8
  * (`computeDiffPreview`), and
9
9
  * - render a text placeholder while no diff exists yet
10
- * (`renderStreamingFallback`).
10
+ * (`renderStreamingPlaceholder`).
11
11
  *
12
12
  * The shared renderer / `ToolExecutionComponent` consult the strategy via
13
13
  * the injected `editMode` rather than probing argument shape.
@@ -72,7 +72,7 @@ export interface EditStreamingStrategy<Args = unknown> {
72
72
  * Rendered inline while the diff hasn't been computed yet (or when the
73
73
  * compute returned `null` because args are still too partial).
74
74
  */
75
- renderStreamingFallback(args: Args, uiTheme: Theme): string;
75
+ renderStreamingPlaceholder(args: Args, uiTheme: Theme): string;
76
76
  }
77
77
  export interface EditRequestTargetInventory {
78
78
  paths: string[];
@@ -169,7 +169,7 @@ export function getEditRequestTargetInventory(
169
169
  if (editMode === "hashline") {
170
170
  const input = typeof values.input === "string" ? values.input : "";
171
171
  const headerPaths = getHashlineTargetPaths(input);
172
- // `path` is only a fallback for headerless input; explicit §PATH headers are the full target set.
172
+ // `path` is only the target for headerless input; explicit §PATH headers are the full target set.
173
173
  if (headerPaths.length > 0) return { paths: orderedDistinctPaths(headerPaths) };
174
174
  return { paths: orderedDistinctPaths([topLevelPath]) };
175
175
  }
@@ -197,8 +197,8 @@ export function getEditRequestTargetInventory(
197
197
  return { paths: orderedDistinctPaths([topLevelPath, ...editPaths, partialPath]) };
198
198
  }
199
199
 
200
- const STREAMING_FALLBACK_LINES = 12;
201
- const STREAMING_FALLBACK_WIDTH = 80;
200
+ const STREAMING_PLACEHOLDER_LINES = 12;
201
+ const STREAMING_PLACEHOLDER_WIDTH = 80;
202
202
 
203
203
  function isHashlineHeaderLine(line: string): boolean {
204
204
  return line.trimEnd().startsWith(HL_FILE_PREFIX);
@@ -236,15 +236,15 @@ function trimHashlineStreamingSyntax(lines: string[]): string[] {
236
236
  return lines.slice(index).filter(line => !isHashlineEnvelopeMarkerLine(line));
237
237
  }
238
238
 
239
- function renderHashlineInputFallback(input: string, uiTheme: Theme): string {
239
+ function renderHashlineInputPlaceholder(input: string, uiTheme: Theme): string {
240
240
  const lines = trimHashlineStreamingSyntax(sanitizeText(input).split("\n"));
241
241
  if (!lines.some(line => line.trim().length > 0)) return "";
242
242
 
243
- const displayLines = lines.slice(-STREAMING_FALLBACK_LINES);
243
+ const displayLines = lines.slice(-STREAMING_PLACEHOLDER_LINES);
244
244
  const hidden = lines.length - displayLines.length;
245
245
  let text = "\n\n";
246
246
  text += displayLines
247
- .map(line => uiTheme.fg("toolOutput", truncateToWidth(replaceTabs(line), STREAMING_FALLBACK_WIDTH)))
247
+ .map(line => uiTheme.fg("toolOutput", truncateToWidth(replaceTabs(line), STREAMING_PLACEHOLDER_WIDTH)))
248
248
  .join("\n");
249
249
  if (hidden > 0) {
250
250
  text += uiTheme.fg("dim", `\n… (streaming +${hidden} lines)`);
@@ -376,7 +376,7 @@ const replaceStrategy: EditStreamingStrategy<ReplaceArgs> = {
376
376
  ctx.signal.throwIfAborted();
377
377
  return [toPerFilePreview(args.path, result)];
378
378
  },
379
- renderStreamingFallback() {
379
+ renderStreamingPlaceholder() {
380
380
  return "";
381
381
  },
382
382
  };
@@ -405,7 +405,7 @@ const patchStrategy: EditStreamingStrategy<PatchArgs> = {
405
405
  ctx.signal.throwIfAborted();
406
406
  return [toPerFilePreview(args.path, result)];
407
407
  },
408
- renderStreamingFallback() {
408
+ renderStreamingPlaceholder() {
409
409
  return "";
410
410
  },
411
411
  };
@@ -552,7 +552,7 @@ const hashlineStrategy: EditStreamingStrategy<HashlineArgs> = {
552
552
  try {
553
553
  sections = splitHashlineInputs(input, { cwd: ctx.cwd, path: args.path });
554
554
  } catch {
555
- // Single-section fallback keeps the original error rendering for the
555
+ // Single-section handling keeps the original error rendering for the
556
556
  // "haven't typed `§ PATH` yet" case.
557
557
  const result = await computeHashlineDiff({ input, path: args.path }, ctx.cwd, {
558
558
  autoDropPureInsertDuplicates: ctx.hashlineAutoDropPureInsertDuplicates,
@@ -590,8 +590,8 @@ const hashlineStrategy: EditStreamingStrategy<HashlineArgs> = {
590
590
  }
591
591
  return previews.length > 0 ? previews : null;
592
592
  },
593
- renderStreamingFallback(args, uiTheme) {
594
- return typeof args.input === "string" ? renderHashlineInputFallback(args.input, uiTheme) : "";
593
+ renderStreamingPlaceholder(args, uiTheme) {
594
+ return typeof args.input === "string" ? renderHashlineInputPlaceholder(args.input, uiTheme) : "";
595
595
  },
596
596
  };
597
597
 
@@ -642,7 +642,7 @@ const applyPatchStrategy: EditStreamingStrategy<ApplyPatchArgs> = {
642
642
  }
643
643
  return previews.length > 0 ? previews : null;
644
644
  },
645
- renderStreamingFallback() {
645
+ renderStreamingPlaceholder() {
646
646
  return "";
647
647
  },
648
648
  };
@@ -656,7 +656,7 @@ const vimStrategy: EditStreamingStrategy<unknown> = {
656
656
  async computeDiffPreview() {
657
657
  return null;
658
658
  },
659
- renderStreamingFallback() {
659
+ renderStreamingPlaceholder() {
660
660
  return "";
661
661
  },
662
662
  };
@@ -1,6 +1,7 @@
1
1
  import { dlopen, FFIType, ptr } from "bun:ffi";
2
2
  import * as childProcess from "node:child_process";
3
3
  import { createHmac } from "node:crypto";
4
+ import type { EventEmitter } from "node:events";
4
5
  import * as fs from "node:fs/promises";
5
6
  import * as os from "node:os";
6
7
  import * as path from "node:path";
@@ -121,6 +122,63 @@ async function reapLinuxAdoptedZombies(excludedPid: number | undefined): Promise
121
122
  }
122
123
  }
123
124
 
125
+ /**
126
+ * Reap adopted zombies only when the kernel reports a child state change.
127
+ *
128
+ * As the Linux child subreaper, the supervisor inherits orphaned descendants
129
+ * and must reap them, but a full `/proc` sweep is expensive. Polling it on a
130
+ * short interval kept every idle supervisor busy (issue #5972), so sweeps are
131
+ * driven by SIGCHLD instead. At most one sweep runs at a time, and a SIGCHLD that
132
+ * arrives mid-sweep schedules exactly one follow-up so a zombie created after
133
+ * its `/proc` entry was passed is never missed. Returns the unsubscribe hook.
134
+ */
135
+ export function startLinuxAdoptedZombieReaper(
136
+ excludedPid: number | undefined,
137
+ reap: (excludedPid: number | undefined) => Promise<void> = reapLinuxAdoptedZombies,
138
+ signals: Pick<EventEmitter, "on" | "off"> = process,
139
+ ): () => void {
140
+ if (process.platform !== "linux") return () => undefined;
141
+ let running = false;
142
+ let rerun = false;
143
+ let stopped = false;
144
+ let sweepPending = false;
145
+ const sweep = (): void => {
146
+ if (stopped) return;
147
+ if (running) {
148
+ rerun = true;
149
+ return;
150
+ }
151
+ if (sweepPending) return;
152
+ sweepPending = true;
153
+ running = true;
154
+ void reap(excludedPid)
155
+ .catch(() => undefined)
156
+ .finally(() => {
157
+ running = false;
158
+ sweepPending = false;
159
+ if (!rerun || stopped) return;
160
+ rerun = false;
161
+ // Defer recursive call to avoid stack overflow
162
+ setImmediate(() => {
163
+ if (!stopped && !running) sweep();
164
+ });
165
+ });
166
+ };
167
+ try {
168
+ signals.on("SIGCHLD", sweep);
169
+ } catch {
170
+ // Signal handler installation may fail in some environments
171
+ }
172
+ return () => {
173
+ stopped = true;
174
+ try {
175
+ signals.off("SIGCHLD", sweep);
176
+ } catch {
177
+ // Signal handler removal may fail in some environments
178
+ }
179
+ };
180
+ }
181
+
124
182
  function signalFromExit(code: number | null, signal: NodeJS.Signals | null): NodeJS.Signals | null {
125
183
  if (signal) return signal;
126
184
  if (code === null || code <= 128) return null;
@@ -210,8 +268,7 @@ export async function runBashShellSupervisor(): Promise<void> {
210
268
  stdio: ["pipe", "pipe", "pipe"],
211
269
  windowsHide: true,
212
270
  });
213
- const zombieReaper = setInterval(() => void reapLinuxAdoptedZombies(worker.pid), 25);
214
- zombieReaper.unref();
271
+ const stopZombieReaper = startLinuxAdoptedZombieReaper(worker.pid);
215
272
  const pendingRuns: number[] = [];
216
273
  let terminating = false;
217
274
  let protocolToken: string | undefined;
@@ -315,7 +372,7 @@ export async function runBashShellSupervisor(): Promise<void> {
315
372
  );
316
373
  });
317
374
  worker.once("exit", async (code, signal) => {
318
- clearInterval(zombieReaper);
375
+ stopZombieReaper();
319
376
  const resultSignal = signalFromExit(code, signal);
320
377
  if (code === 0 && resultSignal === null) {
321
378
  await reapLinuxSubreaperChildren();
@@ -30,7 +30,8 @@ import {
30
30
  listCanonicalBlobs,
31
31
  removeCanonicalBlob,
32
32
  } from "../session/blob-store";
33
- import { FileSessionStorage, probeSessionRetirement, retireSessionTranscript } from "../session/session-storage";
33
+ import { probeSessionRetirement, retireSessionTranscript } from "../session/session-retirement";
34
+ import { FileSessionStorage } from "../session/session-storage";
34
35
  import {
35
36
  collectEmptyDeleteReceipts,
36
37
  type EmptyDeleteGcRecord,
@@ -62,11 +62,17 @@ function envBool(value: string | undefined): boolean | undefined {
62
62
  return ["true", "1", "yes"].includes(value.toLowerCase());
63
63
  }
64
64
 
65
- /** Coerce an env var value into an int, returning undefined for non-numeric input. */
65
+ /**
66
+ * Coerce an env var value into a non-negative int, returning undefined (so the
67
+ * settings value applies) for anything that is not plain digits. `parseInt`
68
+ * alone would read "5turns" as 5, "1e3" as 1 and "-3" as -3.
69
+ */
66
70
  function envInt(value: string | undefined): number | undefined {
67
71
  if (value === undefined) return undefined;
68
- const n = Number.parseInt(value, 10);
69
- return Number.isFinite(n) ? n : undefined;
72
+ const trimmed = value.trim();
73
+ if (!/^\d+$/.test(trimmed)) return undefined;
74
+ const n = Number(trimmed);
75
+ return Number.isSafeInteger(n) ? n : undefined;
70
76
  }
71
77
 
72
78
  function envString(value: string | undefined): string | undefined {