@gajae-code/coding-agent 0.18.5 → 0.18.6

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 (91) hide show
  1. package/CHANGELOG.md +101 -0
  2. package/dist/types/cli-commands.d.ts +10 -0
  3. package/dist/types/cli-ordinary.d.ts +13 -0
  4. package/dist/types/cli.d.ts +12 -3
  5. package/dist/types/commands/contribution-prep.d.ts +0 -3
  6. package/dist/types/commands/sdk.d.ts +3 -0
  7. package/dist/types/commands/update.d.ts +1 -0
  8. package/dist/types/config/model-profile-activation.d.ts +19 -0
  9. package/dist/types/coordinator-mcp/session-reaper.d.ts +5 -0
  10. package/dist/types/edit/modes/replace.d.ts +3 -3
  11. package/dist/types/edit/streaming.d.ts +2 -2
  12. package/dist/types/exec/bash-shell-supervisor.d.ts +12 -0
  13. package/dist/types/sdk/broker/discovery.d.ts +8 -0
  14. package/dist/types/sdk/broker/ensure.d.ts +1 -0
  15. package/dist/types/sdk/broker/lifecycle-ledger.d.ts +4 -0
  16. package/dist/types/sdk/broker/runtime.d.ts +1 -1
  17. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +1 -1
  18. package/dist/types/sdk/bus/telegram-daemon.d.ts +1 -0
  19. package/dist/types/sdk/cli/diagnostics-cli.d.ts +18 -0
  20. package/dist/types/sdk/diagnostics/observe-broker.d.ts +185 -0
  21. package/dist/types/sdk/host/control/operations.d.ts +1 -1
  22. package/dist/types/sdk/index.d.ts +1 -0
  23. package/dist/types/sdk/router/session-router.d.ts +2 -0
  24. package/dist/types/sdk/session.d.ts +13 -0
  25. package/dist/types/session/agent-session.d.ts +5 -1
  26. package/dist/types/tools/ask-contract.d.ts +75 -254
  27. package/dist/types/tools/bash.d.ts +1 -1
  28. package/package.json +7 -7
  29. package/scripts/build-sdk-package-smoke.ts +5 -0
  30. package/src/cli/public-command-entry.ts +5 -0
  31. package/src/cli/ssh-cli.ts +6 -3
  32. package/src/cli/web-search-cli.ts +9 -3
  33. package/src/cli-commands.ts +14 -0
  34. package/src/cli-ordinary.ts +197 -0
  35. package/src/cli.ts +25 -182
  36. package/src/commands/contribution-prep.ts +1 -3
  37. package/src/commands/sdk.ts +59 -0
  38. package/src/commands/update.ts +15 -1
  39. package/src/config/file-lock.ts +91 -2
  40. package/src/config/model-profile-activation.ts +70 -4
  41. package/src/config/model-profile-contract.ts +4 -1
  42. package/src/config/model-profiles.ts +23 -31
  43. package/src/config/model-resolver.ts +9 -1
  44. package/src/coordinator-mcp/event-webhook.ts +19 -14
  45. package/src/coordinator-mcp/policy.ts +14 -4
  46. package/src/coordinator-mcp/server.ts +142 -7
  47. package/src/coordinator-mcp/session-reaper.ts +9 -1
  48. package/src/defaults/gjc/skills/deep-interview/SKILL.md +1 -1
  49. package/src/edit/modes/replace.ts +6 -6
  50. package/src/edit/streaming.ts +15 -15
  51. package/src/exec/bash-shell-supervisor.ts +60 -3
  52. package/src/internal-urls/docs-index.generated.ts +10 -9
  53. package/src/memories/index.ts +8 -2
  54. package/src/modes/acp/acp-agent.ts +649 -367
  55. package/src/modes/components/tool-execution.ts +1 -1
  56. package/src/prompts/tools/ask.md +2 -0
  57. package/src/sdk/broker/broker.ts +46 -0
  58. package/src/sdk/broker/daemon-entry.ts +67 -6
  59. package/src/sdk/broker/discovery.ts +8 -0
  60. package/src/sdk/broker/ensure.ts +156 -20
  61. package/src/sdk/broker/lifecycle-ledger.ts +110 -12
  62. package/src/sdk/broker/lifecycle.ts +225 -341
  63. package/src/sdk/broker/runtime.ts +5 -1
  64. package/src/sdk/broker/transport.ts +5 -1
  65. package/src/sdk/bus/telegram-daemon-contract.ts +1 -1
  66. package/src/sdk/bus/telegram-daemon-control.ts +15 -12
  67. package/src/sdk/bus/telegram-daemon.ts +34 -1
  68. package/src/sdk/cli/diagnostics-cli.ts +247 -0
  69. package/src/sdk/diagnostics/observe-broker.ts +654 -0
  70. package/src/sdk/host/control/dispatch.ts +52 -4
  71. package/src/sdk/host/control/operations.ts +1 -1
  72. package/src/sdk/host/session-runtime.test.ts +40 -2
  73. package/src/sdk/host/session-runtime.ts +13 -5
  74. package/src/sdk/index.ts +1 -0
  75. package/src/sdk/lifecycle-session.ts +1 -0
  76. package/src/sdk/protocol/operation-registry.ts +1 -1
  77. package/src/sdk/router/session-router.ts +7 -1
  78. package/src/sdk/session.ts +37 -8
  79. package/src/session/agent-session.ts +114 -39
  80. package/src/session/blob-store.ts +78 -33
  81. package/src/session/internal/managed-session-storage.ts +96 -3
  82. package/src/setup/hermes/templates/operator-instructions.v1.md +19 -1
  83. package/src/setup/hermes-setup.ts +4 -1
  84. package/src/slash-commands/builtin-registry.ts +8 -5
  85. package/src/tools/ask-contract.test.ts +76 -1
  86. package/src/tools/ask-contract.ts +57 -6
  87. package/src/tools/ask.ts +16 -7
  88. package/src/tools/bash.ts +38 -29
  89. package/src/tools/descriptors.test.ts +29 -0
  90. package/src/tools/tool-catalog.generated.ts +7 -4
  91. package/src/web/search/index.ts +2 -2
package/CHANGELOG.md CHANGED
@@ -2,6 +2,107 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.18.6] - 2026-10-03
6
+
7
+ ### Added
8
+
9
+ - Explicit correlated active-turn steering through coordinator send_prompt, with operator guidance separating requested evidence, queued tasks, and verified consumption. Retargeting feedback to a new active turn requires a new idempotency key; admission still does not prove consumption.
10
+
11
+ - `gjc sdk diagnostics broker` and the exported `observeExistingBroker` facade now report a real running broker: the broker publishes a startup-fixed diagnostic generation, build snapshot and protocol, and the observation reads the publication only through the approved read-only native lease, opens exactly one client connection with no reconnect attempts, and returns a frozen snapshot with no token, endpoint, path, pid or environment value. An observed result exits 0 and a typed unavailability exits 1.
12
+
13
+ - `gjc sdk diagnostics broker`: a read-only broker observation route that can never start, ensure, retire, restart or recover a broker. This entry ships the inert public entry, grammar and bounded observation document; the observation reader is wired in the accompanying fragment.
14
+
15
+ ### Changed
16
+
17
+ - Built-in Codex profiles no longer use GPT-6 Sol or GPT-5.6 Terra. Every role that used `openai-codex/gpt-6-sol` or `openai-codex/gpt-5.6-terra` now uses `openai-codex/gpt-6.1-sol` at the same reasoning effort, so `codex-medium` and `codex-pro` are GPT-6.1 Sol in every role. Affected profiles: `codex-medium`, `codex-pro`, `astra-lite`, `astra-default`, `astra-heavy`, `opus-codex`, `codex-opencodego`, and `fable-opus-codex`. Moving Terra roles onto Sol is an intentional tier change; the catalog price of GPT-6.1 Sol is equal to or below Terra's. The `gpt-6-sol` and `gpt-5.6-terra` catalog entries are unchanged for explicit selection (#6163).
18
+ - `codex-eco` is now `codex-medium` with Sol lowered to `openai-codex/gpt-6-luna` at the same effort, so it uses GPT-6 Luna in every role. Its default, critic, and architect move from GPT-5.6 Terra to GPT-6 Luna (#6163).
19
+ - Roles that moved off GPT-5.6 Terra now compact earlier. GJC forces the Codex GPT-5.6 family to a 372K context window, while GPT-6.1 Sol and GPT-6 Luna use 272K, the window the Codex backend reports for both families (#6163).
20
+ - Installs that load a signed preset registry revision defining these profiles keep that registry binding (built-in, then registry, then user `models.yml`) until a registry revision carries this change (#6163).
21
+
22
+ - The `gjc` entry module is now inert on import: entry metadata moved to the pure `@gajae-code/utils/cli-metadata` leaf (re-exported from `dirs`), and the effectful bootstrap (startup timing, stderr drainer, malloc re-exec, managed-owner admission, public dispatch, registry) moved to `cli-ordinary` which loads lazily for every non-diagnostics command with unchanged ordering.
23
+
24
+ ### Removed
25
+
26
+ - Removed the `codex-sol61` built-in profile added in 0.18.2. A saved or requested `codex-sol61` now resolves to `codex-pro`, which differs in two roles: critic is `openai-codex/gpt-6.1-sol:max` instead of `:xhigh`, and architect is `openai-codex/gpt-6.1-sol:xhigh` instead of `openai-codex/gpt-6-astra:xhigh`. Define a custom `codex-sol61` profile in `models.yml` to keep the previous mapping; a user-defined profile takes precedence over the alias (#6163).
27
+
28
+ ### Fixed
29
+
30
+ - The isolated bash shell supervisor no longer busy-spins while its shell is idle. It reaped adopted zombies by sweeping all of `/proc` every 25 ms without waiting for the previous sweep to finish, which kept each supervisor at about 160% CPU and tens of thousands of context switches per second even during `sleep`. Sweeps now run only on `SIGCHLD`, one at a time, so an idle supervisor stays near 0% CPU (#5972).
31
+
32
+ - Run compound pipeline stages concurrently to prevent pipe-buffer deadlocks.
33
+ - Join remaining pipeline stages when a stage wait returns an error.
34
+
35
+ - ACP `session/new` now waits for the model to settle before building the response when `modelRoles.default` is configured but no `--model` is passed. This ensures the `model` config option is always present in the response, preventing ACP clients (like Paseo) from failing with `model_not_selected` on the first prompt when the session uses a configured default model.
36
+
37
+ - Resuming a session with an unavailable pinned credential no longer aborts when the startup model profile probes that provider; credential failure is reported so the user can re-pin or select AUTO.
38
+ - Resuming a session with an unavailable pinned credential now honors an explicit `--api-key` or a literal `models.yml` `apiKey` when restoring the saved model, the settings default, and extension-registered models, instead of reporting the pin unavailable. An `apiKeyEnv` key does not unblock the pin, because another stored api_key account could take precedence over it; without an explicit key the provider stays blocked until the user re-pins or selects AUTO.
39
+
40
+ - Windows Telegram daemon stop and reload now wait for the owner-fenced cooperative control request before force escalation, and post-update recovery failures exit cleanly with actionable guidance instead of an uncaught exception (#6138).
41
+ - Added an owner-fenced control wakeup for blocked Telegram polls and extended hard-termination cooperative grace before escalation (#6138).
42
+
43
+ - A process dying between the POSIX lock-removal detach and payload scrub no longer wedges every subsequent launch with `FileLockAcquireError` ("blocked by abandoned removal transition"): the dead owner's `.lock.removing` transition is reclaimed through the identity-bound guarded removal, and a refused reclamation keeps contending and still reports the abandoned transition at exhaustion (#6253).
44
+
45
+ - Allow a fresh ACP prompt to wait for a previously failed or cancelled turn to finish winding down before reporting a busy conflict.
46
+ - Admit the next ACP prompt immediately after cancellation settles instead of requiring a retry.
47
+
48
+ - Scope ACP SDK session-router attachments to sessions owned or being attached by that ACP connection, preventing foreign session hosts from being kept alive.
49
+
50
+ - Sessions without explicit `retry.*` settings now auto-retry a content-free OpenAI Codex `server_error` / `internal_error` error event, the same way a content-free `server_is_overloaded` event is already retried. Attempts that produced visible output or tool content are still never retried.
51
+
52
+ - Coordinator MCP: `GJC_COORDINATOR_MCP_ARTIFACT_BYTE_CAP`, `GJC_COORDINATOR_MCP_SESSION_IDLE_TTL_MS` and `GJC_COORDINATOR_MCP_SESSION_SWEEP_INTERVAL_MS` accept only plain decimal digits; values such as `64KB`, `1e6` or `1.5` now fall back to the default instead of being read as their leading digits (`64KB` used to set a 64-byte artifact cap).
53
+ - `gjc setup hermes --artifact-byte-cap` rejects integers above `Number.MAX_SAFE_INTEGER` (for example `9007199254740992` or `1e21`), which it used to write in a form the coordinator then ignored, silently leaving the 64 KiB default in place.
54
+
55
+ - Report out-of-scope Coordinator evidence paths as invalid input instead of service unavailability, without exposing denied paths. Clarify evidence-root restrictions in the Hermes operator guide.
56
+
57
+ - Coordinator MCP tool arguments `timeout_ms`, `poll_interval_ms`, `limit` and `lines`, and the `GJC_COORDINATOR_MCP_PROMPT_ACK_TIMEOUT_MS` env value, no longer read a leading digit prefix. A string such as `1e4` or `5s` became a 1 ms or 5 ms wait, and a one-element array such as `[50]` was accepted as `50`; strings must now be plain decimal digits and non-numeric types fall back to the defaults.
58
+
59
+ - `GJC_COORDINATOR_MCP_EVENT_WEBHOOK_TIMEOUT_MS` and `GJC_COORDINATOR_MCP_EVENT_WEBHOOK_MAX_ATTEMPTS` now accept only plain decimal digits. Values such as `1.5`, `1e4` or `10s` were read as their leading digits, shrinking the per-attempt timeout to 1 ms or the retry budget to a single attempt; they now fall back to the defaults.
60
+
61
+ - Reject active deep-interview questions missing structured round metadata instead of accepting answers that cannot be recorded, and reject extra interview questions before prompting.
62
+ - Reject empty and whitespace-only ask question bodies, including metadata-free extra questions, in deferred, loaded, and direct execution paths while preserving ordinary multi-question and free-text asks, including next-workflow choices after final-spec handoff.
63
+
64
+ - Restore cache prefix extension across LLM requests by keeping volatile project context and untrusted MCP server instructions in `agent.state.messages` after they are sent, rather than removing them after each turn. This allows subsequent requests to properly reuse the cache prefix from previous requests, improving cache hit rates and reducing unnecessary re-computation. Volatile ephemeral messages are still excluded from persistent storage as intended. Fixes #6167.
65
+
66
+ - Gate the statusless typed overload check on `managedOutcome` in `#handleRetryableError` to prevent hanging when a managed fallback chain encounters a transport 503 error followed by a typed Responses overload error. The fix ensures that on the agent_end path, the statusless overload check returns false to allow proper session termination handling instead of deadlocking. Fixes #6180.
67
+
68
+ - `AgentSession.messages` no longer exposes request-scoped ephemeral messages (`volatile-project-context`, `untrusted-mcp-server-instructions`) that #6167 retains in `agent.state.messages` for prompt-cache prefix reuse. The cache prefix behavior is unchanged; the public view and everything built on it match the pre-#6167 transcript again, which fixes the red `dev` CI in the managed fallback attempt transaction test (#6191).
69
+
70
+ - Bash commands with auto-background disabled no longer leave the foreground via the timeout fold timer after timing out.
71
+
72
+ - Lifecycle ledger compaction now evicts only safe settled identities while retaining unbound close replay fences and retirement-source dependencies.
73
+
74
+ - Context-usage and compaction token estimates count the request-scoped ephemeral messages (`volatile-project-context`, `untrusted-mcp-server-instructions`) that are retained in `agent.state.messages` for prompt-cache reuse. They are hidden from `AgentSession.messages`, so estimating over that public view undercounted the real provider request and could delay auto-compaction until the context window overflowed.
75
+
76
+ - Recover abandoned parseable lock removal transitions when their owner is proven dead.
77
+
78
+ - Let SDK steering reach the active worker despite an earlier ordinary ordered control, while waiting behind abort-and-prompt replacements and fencing to the exact runtime command/turn identity so stale feedback is rejected instead of delivered to a successor.
79
+
80
+ - Return an empty observation for missing assistant text instead of a resource-loss error, including compatibility with older running SDK hosts.
81
+
82
+ - Stubbed credential-pin methods in the login preset recommendation test mock (follow-up to #6025).
83
+
84
+ - Keep managed replacement receipts owned by their exact publishing attempt until it settles, preventing concurrent sessions in one workspace from stealing active receipts and failing transcript persistence with `identity_mismatch`. Peer recovery of publisher-bound receipts requires positive owner-exit or PID-reuse evidence; orphan receipt identity checks and external transcript replacement guards remain intact.
85
+
86
+ - Fixed the resident-cache GC reaping a *running* session's cache on Linux hosts with a slewed wall clock (WSL2 in particular), which replaced tool results with `[Session resident text blob missing: …]` placeholders and logged `Resident cache trust rejection … blob_create_failed ENOENT`. The owner lease recorded the process start from `ps -o lstart`, which procps renders as `btime + ticks` where `btime` is re-derived from the current wall clock minus uptime, so the same live process reported a start time that drifted (observed: 15 minutes over 11 hours) and the sweep read the drift as PID reuse. On Linux the lease now records the `/proc/<pid>/stat` start tick count scoped by the kernel `boot_id` (basis `linux-proc-ticks:<boot_id>`), which never moves for a live process; reuse is only proven when the basis matches, so leases written by older builds or another boot are never reaped while their PID is alive.
87
+
88
+ - Broker observation refuses everything it cannot prove: a publication without the diagnostic capability is `unsupported` (never `absent` or malformed), only the canonical loopback endpoint with an in-range port is accepted, the broker answers only from an already-owned healthy publication, one monotonic budget covers the whole observation so an expired budget never opens a socket and a late answer is discarded, and malformed arguments raise a typed argument error instead of a fabricated observation.
89
+
90
+ - The read-only diagnostic addon's trusted digest now travels with the addon through the release path: each build emits a provenance sidecar, consuming jobs rebuild the record from those sidecars against the version actually being shipped, and embedding plus platform-package staging refuse bytes that do not match it.
91
+
92
+ - Reverted the 0.18.2 readiness-cutoff reaping (#6126) because it left hosts in `terminal_uncertain` under concurrent recovery (#6143); unregistered hosts terminated by a signal at readiness cutoff again report `terminal_uncertain` until the fix re-lands.
93
+
94
+ - Coordinator MCP: a `GJC_COORDINATOR_MCP_SESSION_SWEEP_INTERVAL_MS` above `2147483647` (about 24.9 days) no longer makes the idle-session reaper sweep continuously. The interval was passed straight to `setTimeout`, which treats longer delays as 1 ms; it is now capped at the timer maximum.
95
+
96
+ - `gjc ssh add --port` now rejects ports that are not plain digits (`"22oops"`, `"2222.5"`, `"+22"`, `"1e3"`) instead of silently saving the leading digits to `ssh.json`.
97
+
98
+ - `gjc web-search --limit` now rejects `0` and negative values with an error instead of passing them to providers, where a negative limit silently dropped results from the end of the source list.
99
+
100
+ - The `web_search` tool schema now declares `limit` and `num_search_results` as positive integers, so a model-supplied `0`, negative or fractional count is rejected at validation instead of reaching providers, where a negative count silently dropped sources from the end of the list.
101
+
102
+ ### Tests
103
+
104
+ - Add regression test for issue #6004: Verify that after overflow auto-compaction without a continuation scheduled, the session remains usable for subsequent prompts. The test exercises the overflow path to ensure pending agent_end events do not block future prompt submissions (related to #6004).
105
+
5
106
  ## [0.18.5] - 2026-09-30
6
107
 
7
108
  ## [0.18.4] - 2026-09-30
@@ -0,0 +1,10 @@
1
+ import type { CommandEntry } from "@gajae-code/utils/cli";
2
+ /**
3
+ * Public-family hooks kept available for private-worker tests without loading the
4
+ * full registry.
5
+ *
6
+ * This is the single shared array: the inert entry (`cli.ts`) and the ordinary
7
+ * dispatcher (`cli-ordinary.ts`) both re-export THIS object, so a test that
8
+ * mutates or injects an entry keeps affecting the dispatcher.
9
+ */
10
+ export declare const commands: CommandEntry[];
@@ -0,0 +1,13 @@
1
+ /** Ordinary CLI graph: everything the inert entry must not load eagerly.
2
+ *
3
+ * This module owns the effectful bootstrap (startup timing, stderr drainer,
4
+ * bash-shell worker protocol) plus the unchanged ordinary routing order:
5
+ * bash-shell workers -> darwin malloc re-exec -> internal smoke -> tmux owner
6
+ * isolation -> managed-owner supervisor/admission/recovery -> private sdk
7
+ * stderr drainer -> public family dispatch -> community app -> doctor worker /
8
+ * probe / cli -> full registry. `cli.ts` imports it lazily, only after argv has
9
+ * been proven not to be a `sdk diagnostics` observation. */
10
+ import { commands } from "./cli-commands";
11
+ export { commands };
12
+ /** Run every non-diagnostics command with the original admission order. */
13
+ export declare function runOrdinaryCli(argv: string[]): Promise<void>;
@@ -1,6 +1,15 @@
1
1
  #!/usr/bin/env bun
2
- import type { CommandEntry } from "@gajae-code/utils/cli";
3
- /** Public-family hooks kept available for private-worker tests without loading the full registry. */
4
- export declare const commands: CommandEntry[];
2
+ /** Inert CLI entry.
3
+ *
4
+ * `bin/gjc.js` imports THIS module, so anything statically imported here runs
5
+ * before argv is inspected. Keep it to pure metadata: the read-only
6
+ * `sdk diagnostics` observation route must not be able to resolve directories,
7
+ * consult NSS for a missing/ambiguous HOME, load the native addon, install
8
+ * runtime globals, re-exec for malloc env vars, run managed-owner admission or
9
+ * recovery, or touch the error-evidence store. Everything effectful lives in
10
+ * `./cli-ordinary`, which is imported lazily for every other command with its
11
+ * original ordering and admission semantics. */
12
+ import { commands } from "./cli-commands";
13
+ export { commands };
5
14
  /** Run the CLI with argv excluding process.argv prefix. */
6
15
  export declare function runCli(argv: string[]): Promise<void>;
@@ -3,9 +3,6 @@ export default class ContributionPrep extends Command {
3
3
  static description: string;
4
4
  static strict: boolean;
5
5
  static flags: {
6
- "no-spawn": import("@gajae-code/utils/cli").FlagDescriptor<"boolean"> & {
7
- description: string;
8
- };
9
6
  "source-session-id": import("@gajae-code/utils/cli").FlagDescriptor<"string"> & {
10
7
  description: string;
11
8
  };
@@ -132,6 +132,9 @@ export declare function runSessionHost(timing?: {
132
132
  export type SdkInternalArgv = {
133
133
  action: "broker-internal";
134
134
  agentDir: string;
135
+ } | {
136
+ action: "broker-trampoline-internal";
137
+ agentDir: string;
135
138
  } | {
136
139
  action: "session-host-internal";
137
140
  } | {
@@ -1,4 +1,5 @@
1
1
  import { Command } from "@gajae-code/utils/cli";
2
+ export declare function runUpdateRecoveryCommand(recover?: () => Promise<void>): Promise<void>;
2
3
  export default class Update extends Command {
3
4
  static description: string;
4
5
  static flags: {
@@ -44,6 +44,7 @@ export interface PrepareModelProfileActivationOptions {
44
44
  session: ModelProfileActivationSession;
45
45
  modelRegistry: Pick<ModelRegistry, "getModelProfile" | "getModelProfiles" | "getAvailableModelProfileNames" | "getApiKeyForProvider" | "getAll" | "resolveCanonicalModel" | "getCanonicalVariants" | "getCanonicalId" | "isSelectorCircuitOpen"> & Partial<Pick<ModelRegistry, "getAvailable" | "getAvailableForProfileActivation" | "resolveModelByLookupAlias" | "lookupAliasExists" | "clearCanonicalVariant" | "seedCanonicalVariant" | "getSessionCanonicalVariant" | "restoreSessionCanonicalVariant" | "getConfiguredProviderIds" | "isKnownProvider">> & {
46
46
  getError?: ModelRegistry["getError"];
47
+ authStorage?: ModelRegistry["authStorage"];
47
48
  };
48
49
  settings: Pick<Settings, "get" | "getGlobal" | "getOverride">;
49
50
  profileName: string;
@@ -129,6 +130,24 @@ export declare class ModelProfileCredentialError extends Error {
129
130
  readonly role: string | undefined;
130
131
  constructor(profileLabel: string, providers: readonly string[], role?: string);
131
132
  }
133
+ interface SessionPinRegistry {
134
+ authStorage?: {
135
+ hasRuntimeApiKey(provider: string): boolean;
136
+ hasLiteralConfigApiKey(provider: string, owner?: object): boolean;
137
+ hasSessionCredentialUnavailable(provider: string, scopeId?: string): boolean;
138
+ };
139
+ getAuthStorageOwner?(): object;
140
+ }
141
+ /**
142
+ * Whether an unavailable session credential pin blocks `provider`.
143
+ *
144
+ * Mirrors `AuthStorage.getApiKey` precedence: a runtime `--api-key` override or a literal
145
+ * `models.yml` `apiKey` registered for this registry's owner is resolved before the
146
+ * unavailable-pin marker, so either one keeps the provider usable. An `apiKeyEnv` key does
147
+ * not: `getApiKey` prefers another stored api_key account over it, which would silently
148
+ * retarget the unavailable pin.
149
+ */
150
+ export declare function isSessionCredentialPinBlocking(registry: SessionPinRegistry, provider: string, sessionId: string | undefined): boolean;
132
151
  export declare function formatModelProfileCredentialError(profileLabel: string, providers: readonly string[]): string;
133
152
  export declare function formatModelProfileUnknownProviderError(profileLabel: string, providers: readonly string[]): string;
134
153
  /**
@@ -19,6 +19,11 @@ export declare const DEFAULT_SESSION_IDLE_TTL_MS: number;
19
19
  export declare const DEFAULT_SESSION_SWEEP_INTERVAL_MS: number;
20
20
  export declare const MIN_SESSION_IDLE_TTL_MS = 60000;
21
21
  export declare const MIN_SESSION_SWEEP_INTERVAL_MS = 30000;
22
+ /**
23
+ * Longest delay setTimeout honors. Bun, like Node, treats any larger delay as 1 ms,
24
+ * so an operator's very long sweep interval would otherwise re-sweep back to back.
25
+ */
26
+ export declare const MAX_SESSION_SWEEP_INTERVAL_MS: number;
22
27
  /** After this many consecutive reap failures the session is force-evicted from the index. */
23
28
  export declare const MAX_REAP_FAILURES = 3;
24
29
  /** Minimal projection of a coordinator session the reaper needs to decide. */
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * Fuzzy matching utilities for the edit tool.
3
3
  *
4
- * Provides both character-level and line-level fuzzy matching with progressive
5
- * fallback strategies for finding text in files.
4
+ * Provides both character-level and line-level fuzzy matching with staged
5
+ * strategies for finding text in files.
6
6
  */
7
7
  import type { AgentToolResult } from "@gajae-code/agent-core";
8
8
  import * as z from "zod/v4";
@@ -92,7 +92,7 @@ export declare function findClosestSequenceMatch(lines: string[], pattern: strin
92
92
  */
93
93
  export declare function findContextLine(lines: string[], context: string, startFrom: number, options?: {
94
94
  allowFuzzy?: boolean;
95
- skipFunctionFallback?: boolean;
95
+ skipParenRetry?: boolean;
96
96
  }): ContextLineResult;
97
97
  export declare const replaceEditEntrySchema: z.ZodObject<{
98
98
  old_text: z.ZodString;
@@ -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.
@@ -51,7 +51,7 @@ export interface EditStreamingStrategy<Args = unknown> {
51
51
  * Rendered inline while the diff hasn't been computed yet (or when the
52
52
  * compute returned `null` because args are still too partial).
53
53
  */
54
- renderStreamingFallback(args: Args, uiTheme: Theme): string;
54
+ renderStreamingPlaceholder(args: Args, uiTheme: Theme): string;
55
55
  }
56
56
  export interface EditRequestTargetInventory {
57
57
  paths: string[];
@@ -1,4 +1,16 @@
1
+ import type { EventEmitter } from "node:events";
1
2
  import { type BashShellWorkerResponse } from "./bash-shell-worker-protocol";
3
+ /**
4
+ * Reap adopted zombies only when the kernel reports a child state change.
5
+ *
6
+ * As the Linux child subreaper, the supervisor inherits orphaned descendants
7
+ * and must reap them, but a full `/proc` sweep is expensive. Polling it on a
8
+ * short interval kept every idle supervisor busy (issue #5972), so sweeps are
9
+ * driven by SIGCHLD instead. At most one sweep runs at a time, and a SIGCHLD that
10
+ * arrives mid-sweep schedules exactly one follow-up so a zombie created after
11
+ * its `/proc` entry was passed is never missed. Returns the unsubscribe hook.
12
+ */
13
+ export declare function startLinuxAdoptedZombieReaper(excludedPid: number | undefined, reap?: (excludedPid: number | undefined) => Promise<void>, signals?: Pick<EventEmitter, "on" | "off">): () => void;
2
14
  export declare function createUtf8LineDecoder(onLine: (line: string) => void, options?: {
3
15
  maxBufferedBytes?: number;
4
16
  onOverflow?: () => void;
@@ -36,6 +36,14 @@ export interface BrokerDiscovery {
36
36
  ownerId: string;
37
37
  pid: number;
38
38
  incarnation: string;
39
+ /**
40
+ * Public publication-incarnation id for diagnostic observation, fixed at startup.
41
+ * Absent on publications written before the capability existed; an observer then
42
+ * reports `unsupported` instead of synthesizing compatibility.
43
+ */
44
+ diagnosticGeneration?: string;
45
+ /** Diagnostic protocol the publisher answers; absent means no capability. */
46
+ diagnosticProtocol?: number;
39
47
  host: "127.0.0.1";
40
48
  port: number;
41
49
  url: string;
@@ -139,6 +139,7 @@ interface BrokerOwner {
139
139
  stop(): Promise<void>;
140
140
  canReuse(discovery: BrokerDiscovery | null): boolean;
141
141
  markReady(discovery: BrokerDiscovery): boolean;
142
+ setTarget(pid: number, incarnation: string): void;
142
143
  }
143
144
  interface ReapTiming {
144
145
  gracefulMs: number;
@@ -61,6 +61,8 @@ export interface LifecycleDurableEffectsReceipt {
61
61
  export interface LifecycleLedgerEntry {
62
62
  operationKey?: string;
63
63
  fingerprint?: string;
64
+ /** True only when a session.close request supplied immutable generation authority. */
65
+ closeAuthorityBound?: boolean;
64
66
  version: typeof SDK_STATE_VERSION;
65
67
  identity: string;
66
68
  requestHash: string;
@@ -158,12 +160,14 @@ export declare class LifecycleLedger {
158
160
  migrateIdentity(from: string, to: string, metadata: {
159
161
  operationKey: string;
160
162
  fingerprint: string;
163
+ closeAuthorityBound?: boolean;
161
164
  }): Promise<LifecycleLedgerEntry | undefined>;
162
165
  get warnings(): readonly string[];
163
166
  begin(identity: string, requestHash: string, metadata?: {
164
167
  operationKey?: string;
165
168
  fingerprint?: string;
166
169
  intendedSessionId?: string;
170
+ closeAuthorityBound?: boolean;
167
171
  }): Promise<BeginResult>;
168
172
  transition(identity: string, state: LifecycleState, fields?: Omit<Partial<LifecycleLedgerEntry>, "identity" | "requestHash" | "state" | "ts">): Promise<LifecycleLedgerEntry>;
169
173
  assertSupportedStateVersions(): Promise<void>;
@@ -1,4 +1,4 @@
1
- export type SdkInternalAction = "broker-internal" | "session-host-internal" | "stderr-drain-internal";
1
+ export type SdkInternalAction = "broker-internal" | "broker-trampoline-internal" | "session-host-internal" | "stderr-drain-internal";
2
2
  export type SdkInternalSpawnCommand = {
3
3
  kind: "bun-source";
4
4
  file: string;
@@ -292,7 +292,7 @@ export declare const SDK_LIFECYCLE_ROUTER_PROTOCOL_VERSION = 1;
292
292
  * Generation 193 gates streamed content on negotiated observer capabilities,
293
293
  * so existing owners are replaced before exposing the new observer path.
294
294
  */
295
- export declare const DAEMON_GENERATION = 199;
295
+ export declare const DAEMON_GENERATION = 200;
296
296
  /**
297
297
  * Serving-compatibility boundary for daemon lifecycle requests. Epoch 7
298
298
  * requires durable publication receipts and acknowledged attachment dispatch,
@@ -1181,4 +1181,5 @@ export declare class TelegramNotificationDaemon {
1181
1181
  run(): Promise<void>;
1182
1182
  /** True when a signal-driven stop or an owner-scoped control request asks the loop to exit. */
1183
1183
  private controlStopRequested;
1184
+ private checkControlStopRequested;
1184
1185
  }
@@ -0,0 +1,18 @@
1
+ /** Isolated `gjc sdk diagnostics` parser and renderer.
2
+ *
3
+ * Reached from the inert entry before any effectful branch, so this module must
4
+ * stay a pure leaf: no command registry, session CLI, runtime globals, managed
5
+ * owner admission, malloc guard, evidence publisher, broker client, directory
6
+ * resolver or logger import. Help and usage never resolve a root or open a file,
7
+ * and every outcome is a fixed bounded string with a fixed exit code.
8
+ *
9
+ * Observation itself must never start, ensure, retire, restart or recover a
10
+ * broker. The read-only observation facade (DESIGN §B3) is wired: `observe()`
11
+ * below routes to it and to nothing else, so this command can report only what a
12
+ * supporting broker publishes to the read-only reader. An unsupported runtime
13
+ * tuple, a missing or mismatched native artifact and an old broker without a
14
+ * published generation all still fail closed with the fixed `unsupported`
15
+ * outcome.
16
+ */
17
+ /** Run `gjc sdk diagnostics <argv…>`; argv excludes the `sdk diagnostics` prefix. */
18
+ export declare function runDiagnosticsCli(argv: readonly string[]): Promise<void>;
@@ -0,0 +1,185 @@
1
+ /**
2
+ * Observation-only broker diagnostics facade (SPEC "Result contract").
3
+ *
4
+ * The publication is read ONLY through the approved read-only native lease
5
+ * (`loadDiagnosticNativeReadOnly` -> `openDiagnosticSnapshot` -> bounded read ->
6
+ * revalidate -> close), so every ancestor ACL/owner/mode and filesystem check of the
7
+ * earlier slices governs this route too. Ordinary discovery is deliberately not used: it
8
+ * would bypass that gate. An unsupported runtime tuple or artifact resolves to
9
+ * `unsupported` BEFORE any publication access. The facade then opens exactly ONE client
10
+ * connection with no reconnect attempts, asks the broker for its published diagnostics
11
+ * and returns a detached frozen snapshot. It never ensures, starts, recovers, retires or spawns
12
+ * anything, never touches the session index, ledger or evidence store, and never hands
13
+ * the caller a token, socket coordinate, path, pid or environment value.
14
+ */
15
+ /** Allowed unavailability reasons; messages are fixed literals keyed by the enum. */
16
+ export type BrokerObservationUnavailableReason = "absent" | "stale" | "incompatible" | "authentication_failed" | "unsupported" | "generation_mismatch" | "transport_unavailable" | "timeout" | "invalid_response" | "unsafe_discovery";
17
+ export interface BrokerObservationBuild {
18
+ readonly packageVersion: string;
19
+ readonly buildId: string | null;
20
+ }
21
+ export interface BrokerObservationSuccess {
22
+ readonly schema: "gjc.broker-observation";
23
+ readonly version: 1;
24
+ readonly ok: true;
25
+ readonly observedAt: string;
26
+ readonly broker: {
27
+ readonly generation: string;
28
+ readonly build: BrokerObservationBuild;
29
+ readonly diagnosticProtocol: 1;
30
+ };
31
+ }
32
+ export interface BrokerObservationUnavailable {
33
+ readonly schema: "gjc.broker-observation";
34
+ readonly version: 1;
35
+ readonly ok: false;
36
+ readonly observedAt: string;
37
+ readonly unavailable: {
38
+ readonly reason: BrokerObservationUnavailableReason;
39
+ readonly message: string;
40
+ };
41
+ }
42
+ export type BrokerObservation = BrokerObservationSuccess | BrokerObservationUnavailable;
43
+ export interface ObserveExistingBrokerOptions {
44
+ readonly agentDir: string;
45
+ readonly expectedGeneration?: string;
46
+ readonly timeoutMs?: number;
47
+ }
48
+ /** The broker's published diagnostics operation. */
49
+ export declare const BROKER_DIAGNOSTICS_OPERATION = "broker.diagnostics";
50
+ /** The only diagnostic protocol this client understands. */
51
+ export declare const BROKER_DIAGNOSTIC_PROTOCOL = 1;
52
+ /** Contract timeout: default 2000 ms, accepted range 1..10000 ms. */
53
+ export declare const OBSERVATION_TIMEOUT_DEFAULT_MS = 2000;
54
+ export declare const OBSERVATION_TIMEOUT_MIN_MS = 1;
55
+ export declare const OBSERVATION_TIMEOUT_MAX_MS = 10000;
56
+ export declare function setObservationClockForTest(clock: (() => number) | null): void;
57
+ /**
58
+ * Test-only observers for the native stage boundary. Production installs none, and the
59
+ * hooks never change a branch, a budget or an outcome: they only report what the
60
+ * observation did (`onNativeOpen`) and give a test a point to disturb the environment
61
+ * between the first publication read and the connection (`afterInitialRead`), which is how
62
+ * the pre-socket identity recheck is proven instead of argued.
63
+ */
64
+ export interface ObservationTestHooks {
65
+ onNativeOpen?: (info: {
66
+ agentDir: string;
67
+ budgetMs: number;
68
+ }) => void;
69
+ afterInitialRead?: () => void;
70
+ /** Reports that the retained lease was released, on every exit path. */
71
+ onLeaseClosed?: () => void;
72
+ /**
73
+ * Decorates the retained lease so a test can identify the real operation phases and make
74
+ * a chosen phase refuse or throw. Production installs none.
75
+ */
76
+ wrapLease?: (lease: DiagnosticSnapshotLease) => DiagnosticSnapshotLease;
77
+ /** Replaces the loader result so loader-stage outcomes can be observed deterministically. */
78
+ loaderOverride?: () => DiagnosticNativeLoad;
79
+ }
80
+ /** Minimal shapes this facade relies on from the approved read-only loader. */
81
+ interface DiagnosticSnapshotRead {
82
+ readonly ok: boolean;
83
+ readonly reason?: string | null;
84
+ readonly bytes?: Uint8Array | null;
85
+ }
86
+ interface DiagnosticSnapshotStatus {
87
+ readonly ok: boolean;
88
+ readonly reason?: string | null;
89
+ }
90
+ interface DiagnosticSnapshotLease {
91
+ readonly ok: boolean;
92
+ readonly reason?: string | null;
93
+ read(): DiagnosticSnapshotRead;
94
+ revalidate(): DiagnosticSnapshotStatus;
95
+ close(): void;
96
+ }
97
+ type DiagnosticNativeLoad = {
98
+ ok: true;
99
+ value: {
100
+ openDiagnosticSnapshot(agentDir: string, budgetMs: number): DiagnosticSnapshotLease;
101
+ };
102
+ } | {
103
+ ok: false;
104
+ reason: string;
105
+ };
106
+ export declare function setObservationTestHooksForTest(hooks: ObservationTestHooks | null): void;
107
+ /** Malformed arguments are a typed caller error, never an observed unavailability. */
108
+ export declare class DiagnosticOptionsError extends Error {
109
+ readonly code: "invalid_arguments";
110
+ readonly field: string;
111
+ constructor(field: string);
112
+ }
113
+ export type ObservationOptionsVerdict = {
114
+ ok: true;
115
+ value: {
116
+ agentDir: string;
117
+ expectedGeneration?: string;
118
+ timeoutMs: number;
119
+ };
120
+ } | {
121
+ ok: false;
122
+ error: {
123
+ code: "invalid_arguments";
124
+ field: string;
125
+ };
126
+ };
127
+ /** Validate the caller's options against the exact contract. Pure and total. */
128
+ export declare function validateObservationOptions(options: unknown): ObservationOptionsVerdict;
129
+ /** Whether the endpoint is the canonical owned loopback endpoint with an in-range port. */
130
+ export declare function canonicalLoopbackEndpoint(url: string): boolean;
131
+ /**
132
+ * Parse verdicts for the publication bytes. A publication that is well formed but
133
+ * carries no diagnostic capability is NOT malformed: an old broker like that is
134
+ * `unsupported`, never `invalid_response` and never `absent`.
135
+ */
136
+ export type PublicationVerdict = {
137
+ kind: "ok";
138
+ value: ObservedPublication;
139
+ } | {
140
+ kind: "no-capability";
141
+ } | {
142
+ kind: "malformed";
143
+ };
144
+ /** The publication fields this route reads; every other field is ignored, never trusted. */
145
+ export interface ObservedPublication {
146
+ readonly url: string;
147
+ readonly token: string;
148
+ readonly ownerId: string;
149
+ readonly pid: number;
150
+ readonly incarnation: string | null;
151
+ readonly agentRoot: string | null;
152
+ readonly generation: string;
153
+ readonly diagnosticProtocol: number;
154
+ }
155
+ /** Parse the publication bytes the native lease returned. Strict, bounded, total. */
156
+ export declare function parsePublicationVerdict(bytes: Uint8Array): PublicationVerdict;
157
+ /** Convenience wrapper for callers that only need the accepted shape. */
158
+ export declare function parsePublication(bytes: Uint8Array): ObservedPublication | null;
159
+ /** The exact answered payload this client accepts; unknown fields are refused. */
160
+ interface AnsweredDiagnostics {
161
+ readonly generation: string;
162
+ readonly build: BrokerObservationBuild;
163
+ readonly identity: {
164
+ readonly ownerId: string;
165
+ readonly pid: number;
166
+ readonly agentRoot: string;
167
+ };
168
+ }
169
+ /** Strictly validate the broker's answer, mapping every refusal onto the reason enum. */
170
+ export declare function parseAnsweredDiagnostics(response: unknown): {
171
+ kind: "ok";
172
+ value: AnsweredDiagnostics;
173
+ } | {
174
+ kind: "unavailable";
175
+ reason: BrokerObservationUnavailableReason;
176
+ };
177
+ /** Two publication observations describe the same authority. */
178
+ export declare function sameAuthority(first: ObservedPublication, second: ObservedPublication): boolean;
179
+ /** The broker's answer agrees with the publication on generation and identity. */
180
+ export declare function answerMatchesAuthority(answer: AnsweredDiagnostics, publication: ObservedPublication): boolean;
181
+ /**
182
+ * Observe an already running broker. Never starts, ensures, recovers or retires one.
183
+ */
184
+ export declare function observeExistingBroker(options: ObserveExistingBrokerOptions | unknown): Promise<BrokerObservation>;
185
+ export {};
@@ -20,7 +20,7 @@ export type ControlInput = Record<string, unknown>;
20
20
  */
21
21
  export interface ControlSurface {
22
22
  prompt(text: string, images?: ControlValue, clientRef?: string): Promise<ControlValue> | ControlValue;
23
- steer(text: string, clientRef?: string): Promise<ControlValue> | ControlValue;
23
+ steer(text: string, clientRef?: string, expectedSdkRunToken?: string): Promise<ControlValue> | ControlValue;
24
24
  followUp(text: string): Promise<ControlValue> | ControlValue;
25
25
  abort(): Promise<ControlValue> | ControlValue;
26
26
  /** Terminal abort: stop the current root turn (and optionally exact owned work). */
@@ -1,6 +1,7 @@
1
1
  export { MODEL_PROFILE_DISCOVERY_QUERY, MODEL_PROFILE_ERROR_DETAIL_MAX_BYTES, type ModelProfileCatalogItem, type ModelProfileErrorCode, type ModelProfileErrorDetails, ModelProfileRegistryError, type ModelProfileRegistryErrorDetails, type UnknownModelProfileDetails, UnknownModelProfileError, } from "../config/model-profile-contract";
2
2
  export type { QueuedInputAdmission, QueuedInputDelivery, QueuedInputExecution, QueuedInputQueuePolicy, QueuedInputRemovalReason, QueuedInputSubmission, QueuedInputTerminal, SendUserMessageOptions, TrackedSendUserMessageOptions, } from "../session/agent-session";
3
3
  export * as bus from "./bus";
4
+ export * from "./diagnostics/observe-broker";
4
5
  export * as host from "./host";
5
6
  export * as lifecycle from "./lifecycle";
6
7
  export * as mcp from "./mcp";
@@ -177,6 +177,8 @@ export interface SessionRouterOptions {
177
177
  agentDir: string;
178
178
  /** Limits attachment to exact ids selected by a Broker-scoped operation. */
179
179
  sessionIds?: readonly string[];
180
+ /** Dynamically limits attachment to sessions currently owned by the caller. */
181
+ attachFilter?: (sessionId: string) => boolean;
180
182
  /** Marks default SDK clients as notification-only observers; demanding by default. */
181
183
  observer?: boolean;
182
184
  deps?: SessionRouterDeps;
@@ -188,6 +188,12 @@ export interface CreateAgentSessionOptions {
188
188
  * @internal CLI-only ordering guard; SDK callers retain immediate startup by default.
189
189
  */
190
190
  deferMemoryBackendStartup?: boolean;
191
+ /**
192
+ * Skip optional model-catalog discovery during lifecycle startup. ACP hosts must
193
+ * publish their endpoint before unrelated provider credential refreshes begin.
194
+ * @internal lifecycle-only startup guard.
195
+ */
196
+ deferOptionalModelRefresh?: boolean;
191
197
  /** Enable LSP integration (tool, formatting, diagnostics, warmup). Default: true */
192
198
  enableLsp?: boolean;
193
199
  /** Skip Python kernel availability check and prelude warmup */
@@ -297,6 +303,13 @@ export interface CreateAgentSessionResult {
297
303
  */
298
304
  gjcRuntimeSnapshot?: GjcRuntimeSnapshotProvider;
299
305
  }
306
+ /**
307
+ * Start optional provider/model discovery only when it is safe to add work to
308
+ * the caller's startup graph. Lifecycle hosts deliberately skip this during
309
+ * construction: discovery preflights every configured provider and may refresh
310
+ * an unrelated OAuth credential while several hosts are opening the same DB.
311
+ */
312
+ export declare function startOptionalModelRefresh(modelRegistry: Pick<ModelRegistry, "refreshInBackground">, credentialSessionId: string | undefined, deferred: boolean): boolean;
300
313
  export interface DeferredMcpConfigStartupResult {
301
314
  loadedToolCount: number;
302
315
  hasErrors: boolean;