@gajae-code/coding-agent 0.16.0 → 0.16.3

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 (164) hide show
  1. package/CHANGELOG.md +52 -1
  2. package/dist/types/async/job-manager.d.ts +32 -1
  3. package/dist/types/browser-backend.d.ts +12 -0
  4. package/dist/types/cli/update-cli.d.ts +1 -0
  5. package/dist/types/commands/sdk.d.ts +2 -0
  6. package/dist/types/config/billing-path.d.ts +25 -0
  7. package/dist/types/config/config-schema-version.d.ts +13 -1
  8. package/dist/types/config/model-registry.d.ts +16 -0
  9. package/dist/types/config/settings-schema.d.ts +17 -7
  10. package/dist/types/extensibility/extensions/types.d.ts +8 -3
  11. package/dist/types/gjc-runtime/launch-worktree.d.ts +25 -1
  12. package/dist/types/gjc-runtime/session-state-lock.d.ts +22 -0
  13. package/dist/types/main.d.ts +1 -0
  14. package/dist/types/modes/acp/acp-agent.d.ts +21 -21
  15. package/dist/types/modes/components/model-selector.d.ts +4 -0
  16. package/dist/types/modes/components/session-selector.d.ts +1 -1
  17. package/dist/types/modes/components/sessions-dashboard.d.ts +2 -1
  18. package/dist/types/modes/interactive-mode.d.ts +16 -2
  19. package/dist/types/modes/theme/theme.d.ts +2 -1
  20. package/dist/types/modes/types.d.ts +2 -0
  21. package/dist/types/sdk/broker/broker.d.ts +2 -1
  22. package/dist/types/sdk/broker/ensure.d.ts +3 -0
  23. package/dist/types/sdk/broker/lifecycle-ledger.d.ts +6 -0
  24. package/dist/types/sdk/broker/lifecycle.d.ts +14 -0
  25. package/dist/types/sdk/broker/startup-budget.d.ts +52 -0
  26. package/dist/types/sdk/broker/state-version.d.ts +17 -0
  27. package/dist/types/sdk/bus/discord-daemon.d.ts +1 -1
  28. package/dist/types/sdk/bus/index.d.ts +3 -1
  29. package/dist/types/sdk/bus/kind-aware-reconciliation.d.ts +1 -1
  30. package/dist/types/sdk/bus/prompt-reconciliation.d.ts +1 -1
  31. package/dist/types/sdk/bus/slack-conversation.d.ts +1 -1
  32. package/dist/types/sdk/bus/slack-daemon.d.ts +1 -1
  33. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +1 -1
  34. package/dist/types/sdk/cli/rows.d.ts +15 -2
  35. package/dist/types/sdk/cli/session-cli.d.ts +2 -1
  36. package/dist/types/sdk/host/events.d.ts +2 -0
  37. package/dist/types/sdk/host/host.d.ts +2 -0
  38. package/dist/types/sdk/host/query/cursor.d.ts +4 -2
  39. package/dist/types/sdk/host/query/handlers.d.ts +1 -0
  40. package/dist/types/sdk/host/query/revision-store.d.ts +1 -0
  41. package/dist/types/sdk/host/session-runtime.d.ts +18 -17
  42. package/dist/types/sdk/lifecycle-session.d.ts +1 -1
  43. package/dist/types/sdk/prompt-deadline-manager.d.ts +2 -5
  44. package/dist/types/sdk/receipt-state.d.ts +2 -0
  45. package/dist/types/sdk/router/session-router.d.ts +4 -0
  46. package/dist/types/sdk/turn-result.d.ts +6 -0
  47. package/dist/types/session/agent-session.d.ts +47 -7
  48. package/dist/types/session/fold-coordinator.d.ts +29 -2
  49. package/dist/types/session/session-manager.d.ts +14 -2
  50. package/dist/types/tools/ask.d.ts +1 -0
  51. package/dist/types/tools/bash.d.ts +12 -0
  52. package/dist/types/tools/browser/launch.d.ts +10 -0
  53. package/dist/types/tools/browser/registry.d.ts +8 -0
  54. package/dist/types/tools/index.d.ts +4 -10
  55. package/dist/types/tools/job.d.ts +3 -1
  56. package/dist/types/utils/external-editor.d.ts +12 -0
  57. package/examples/extensions/README.md +1 -1
  58. package/examples/extensions/pirate.ts +7 -4
  59. package/package.json +7 -7
  60. package/scripts/generate-sdk-operation-inventory.ts +7 -1
  61. package/src/async/job-manager.ts +107 -9
  62. package/src/browser-backend.ts +29 -0
  63. package/src/cli/plugin-cli.ts +29 -0
  64. package/src/cli/update-cli.ts +63 -12
  65. package/src/commands/sdk.ts +22 -7
  66. package/src/config/billing-path.ts +42 -0
  67. package/src/config/config-schema-version.ts +18 -1
  68. package/src/config/model-preset-registry.ts +177 -18
  69. package/src/config/model-registry.ts +268 -42
  70. package/src/config/settings-schema.ts +19 -7
  71. package/src/config/settings.ts +46 -15
  72. package/src/coordinator-mcp/projection-scan.ts +6 -6
  73. package/src/coordinator-mcp/server.ts +21 -1
  74. package/src/defaults/gjc/skills/deep-interview/SKILL.md +1 -1
  75. package/src/extensibility/extensions/runner.ts +27 -0
  76. package/src/extensibility/extensions/types.ts +8 -3
  77. package/src/extensibility/gjc-plugins/installer.ts +51 -11
  78. package/src/extensibility/runtime-skill-discovery.ts +110 -3
  79. package/src/extensibility/skills.ts +22 -12
  80. package/src/gjc-runtime/launch-worktree.ts +285 -4
  81. package/src/gjc-runtime/session-state-lock.ts +156 -32
  82. package/src/gjc-runtime/session-state-sidecar.ts +10 -17
  83. package/src/gjc-runtime/tmux-owner-isolation.ts +16 -1
  84. package/src/internal-urls/docs-index.generated.ts +11 -11
  85. package/src/main.ts +4 -1
  86. package/src/modes/acp/acp-agent.ts +34 -19
  87. package/src/modes/components/model-selector.ts +76 -8
  88. package/src/modes/components/session-selector.ts +23 -15
  89. package/src/modes/components/sessions-dashboard.ts +8 -5
  90. package/src/modes/controllers/command-controller.ts +1 -1
  91. package/src/modes/controllers/event-controller.ts +30 -4
  92. package/src/modes/controllers/extension-ui-controller.ts +13 -13
  93. package/src/modes/controllers/input-controller.ts +2 -2
  94. package/src/modes/controllers/selector-controller.ts +10 -3
  95. package/src/modes/interactive-mode.ts +36 -12
  96. package/src/modes/prompt-action-autocomplete.ts +30 -4
  97. package/src/modes/runtime-init.ts +12 -9
  98. package/src/modes/theme/theme.ts +5 -0
  99. package/src/modes/types.ts +2 -0
  100. package/src/modes/utils/ui-helpers.ts +22 -3
  101. package/src/prompts/agent-fragments/browser-aside.md +22 -0
  102. package/src/prompts/tools/ask.md +1 -1
  103. package/src/prompts/tools/bash.md +1 -0
  104. package/src/prompts/tools/job.md +1 -1
  105. package/src/sdk/acp/adapter.ts +6 -1
  106. package/src/sdk/broker/broker.ts +10 -1
  107. package/src/sdk/broker/ensure.ts +76 -3
  108. package/src/sdk/broker/lifecycle-ledger.ts +6 -0
  109. package/src/sdk/broker/lifecycle.ts +254 -34
  110. package/src/sdk/broker/spawn-substrate.ts +45 -2
  111. package/src/sdk/broker/startup-budget.ts +144 -2
  112. package/src/sdk/broker/state-version.ts +17 -0
  113. package/src/sdk/bus/chat-daemon-control.ts +2 -2
  114. package/src/sdk/bus/chat-daemon-runtime.ts +118 -24
  115. package/src/sdk/bus/discord-daemon.ts +50 -10
  116. package/src/sdk/bus/index.ts +33 -3
  117. package/src/sdk/bus/kind-aware-reconciliation.ts +296 -48
  118. package/src/sdk/bus/lifecycle-commands.ts +4 -0
  119. package/src/sdk/bus/notification-service.ts +22 -5
  120. package/src/sdk/bus/prompt-reconciliation.ts +7 -2
  121. package/src/sdk/bus/reconciliation-store.ts +55 -31
  122. package/src/sdk/bus/slack-conversation.ts +1 -1
  123. package/src/sdk/bus/slack-daemon.ts +72 -38
  124. package/src/sdk/bus/telegram-daemon-contract.ts +1 -1
  125. package/src/sdk/cli/rows.ts +75 -7
  126. package/src/sdk/cli/session-cli.ts +68 -35
  127. package/src/sdk/host/control/dispatch.ts +2 -2
  128. package/src/sdk/host/events.ts +26 -2
  129. package/src/sdk/host/host.ts +6 -1
  130. package/src/sdk/host/query/cursor.ts +52 -7
  131. package/src/sdk/host/query/handlers.ts +33 -12
  132. package/src/sdk/host/query/revision-store.ts +12 -0
  133. package/src/sdk/host/session-runtime.test.ts +497 -31
  134. package/src/sdk/host/session-runtime.ts +387 -54
  135. package/src/sdk/host/surface-policy.ts +3 -3
  136. package/src/sdk/lifecycle-session.ts +7 -2
  137. package/src/sdk/prompt-deadline-manager.ts +2 -1
  138. package/src/sdk/protocol/operation-inventory.generated.json +54 -3
  139. package/src/sdk/protocol/operation-registry.ts +1 -1
  140. package/src/sdk/receipt-state.ts +10 -0
  141. package/src/sdk/router/session-router.ts +210 -94
  142. package/src/sdk/session.ts +59 -41
  143. package/src/sdk/turn-result.ts +22 -0
  144. package/src/session/agent-session.ts +1033 -260
  145. package/src/session/fold-coordinator.ts +40 -3
  146. package/src/session/session-manager.ts +517 -77
  147. package/src/slash-commands/builtin-registry.ts +41 -0
  148. package/src/task/executor.ts +9 -4
  149. package/src/tools/ask-contract.test.ts +64 -0
  150. package/src/tools/ask-contract.ts +36 -1
  151. package/src/tools/ask.ts +10 -8
  152. package/src/tools/bash-interactive.ts +11 -2
  153. package/src/tools/bash.ts +158 -33
  154. package/src/tools/browser/launch.ts +37 -0
  155. package/src/tools/browser/registry.ts +99 -9
  156. package/src/tools/cron.ts +6 -4
  157. package/src/tools/descriptors.test.ts +15 -0
  158. package/src/tools/descriptors.ts +5 -1
  159. package/src/tools/index.ts +4 -6
  160. package/src/tools/job.ts +4 -1
  161. package/src/tools/monitor.ts +12 -9
  162. package/src/tools/resolve.ts +30 -10
  163. package/src/tools/tool-catalog.generated.ts +2 -2
  164. package/src/utils/external-editor.ts +86 -1
package/CHANGELOG.md CHANGED
@@ -2,6 +2,55 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.16.3] - 2026-09-04
6
+
7
+ ## [0.16.2] - 2026-09-04
8
+
9
+ - Primary model binding now rejects GPT-5.6 Sol when the selected openai-codex OAuth account is known to be below the Pro tier, preventing a raw entitlement failure on the first turn and preserving the prior model on a failed selection.
10
+
11
+ - Fixed the steer-triggered bash fold being skipped under `toolInterruptPolicy: finish_tools`: a composer steer over a long foreground `bash` was aborted instead of folded because the fold gate wrongly treated the policy as an exclusion. `finish_tools` governs sibling tools in the batch, not the fold; the fold kills nothing and now applies under both policies. A tmux dogfood driver (`scripts/dogfood/steer-fold-tmux.sh`) proves the real binary folds, answers the steer in the same turn, and wakes on completion.
12
+ - SDK session tails now preserve authoritative transcript revisions in item identity and lifecycle ordering, close the pre-checkpoint live/replay dedupe race, and recover held live frames across reconnect replay without losing ordered delivery.
13
+ - Native Windows external editors now reassert runtime raw input for ConPTY-backed terminals after returning from the editor, preventing cooked-mode handoff from swallowing the first keystroke.
14
+ - ACP reconnect cancellation now reports `cancelled` only after the SDK acknowledges the abort; pre-ack owner loss remains `connection_closed`, while failed aborts preserve non-cancelled prompt outcomes and leave the session reusable.
15
+
16
+ ## [0.16.1] - 2026-09-03
17
+
18
+ - Broker-launched SDK sessions now activate an already-cached default model profile before refreshing provider catalogs in the background, preventing redundant online discovery from consuming the fixed semantic-readiness window while preserving strict refresh fallback when cached profile resolution fails.
19
+ - Native Windows external editors now launch executable commands directly instead of through `cmd.exe`, preventing the shell handoff from swallowing the first editor keystroke; `.cmd` and `.bat` editor commands retain shell execution.
20
+ - Slack Socket Mode now accepts real `event_callback` payloads, routes ordinary active-thread text as new user turns when no ask is pending, keeps idle notifications non-replyable, deduplicates distinct messages without treating shared `event_context` metadata as event identity, and scopes deterministic root message IDs to the configured workspace and channel.
21
+ - Ask now rejects standalone custom-input pseudo-option labels such as `Other (type your own)`, `custom input`, and `직접 입력` while preserving genuine labels that merely contain those words; single-option asks no longer publish a recommended marker, including through workflow-gate/SDK question projection.
22
+ - Session-state lock acquisition now retries immediately after positively proving and identity-reclaiming a dead transition owner, so forced exits no longer add a retry delay to the next resume while unproven or live claims retain the bounded backoff path.
23
+ - Coordinator persist failures now include typed lock-cause fields and use a per-document, per-failure-class 30-second warn window; proven-refused local model discovery is debug-level, and `session_closed` delivery failures route by attached-client count.
24
+ - `gjc update` now identifies the active Linux libc instead of treating an optional installed musl loader as proof that a glibc-based Ubuntu host runs musl.
25
+ - The interactive TUI now warns when a turn ends with `stopReason: "length"` before producing any visible text or tool call — a reasoning model whose thinking alone consumed the budget — instead of returning to the prompt with no explanation, and the completion notification for that turn no longer reports success. Other surfaces (SDK, ACP, print mode, telemetry) already reported this state; no length-specific automatic continuation is added and existing continuation policies are unchanged.
26
+ - Sessions can now be marked with `/star` and `/unstar`; starred sessions persist in append-only metadata, display a `★` in resume and read-only dashboard views, and sort ahead of unstarred sessions without changing deletion or retention behavior.
27
+ - Steering is now admitted only into a live agent run. A steer submitted when no run is live — after a turn ended, during the post-prompt unwind window where the session still reports streaming, while another prompt is in preflight, or while an abort is unwinding — is no longer parked in the Agent steering queue where it was invisible to the queue display and consumed by whichever unrelated later prompt polled it first (appearing turn after turn, or being drained silently by the first Esc). The session now routes such a steer as a sequential follow-up owned by the next turn and auto-continues into it, so `session.steer()`, `prompt(..., { streamingBehavior: "steer" })`, `sendUserMessage(..., { deliverAs: "steer" })`, `sendCustomMessage(..., { deliverAs: "steer" })`, and SDK `turn.steer` all behave identically. A steer-behavior prompt submitted while another prompt is still acquiring session admission is queued against that prompt's turn instead of being rejected with `busy` and lost.
28
+ - The `interruptMode` setting is renamed to `toolInterruptPolicy` with values `abort_tools` (was `immediate`, default) and `finish_tools` (was `wait`); existing config files are migrated once (config schema version 2). The setting only decides whether a steer aborts the tools still running in the current batch and never changes when a steer is consumed — the previous label implied queue semantics it never had. The ACP config option, its SDK control operation (`queue.interrupt_mode.set` → `queue.tool_interrupt_policy.set`, `setQueueMode("tool_interrupt", ...)`), and `AgentSession.setToolInterruptPolicy()` follow the rename. `ToolSession.steer` is removed: the `cron`, `monitor`, and `resolve` tools deliver through `sendCustomMessage` (`deliverAs: "steer"` for the resolve reminder), so no tool pushes onto the Agent queue directly. Composer-submitted steers request sequential delivery (`steerQueuePolicy: "sequential"`) so they still arrive one at a time under `steeringMode: "all"`.
29
+ - Steering a run leaves unconsumed when it ends is now settled in one place. The session consumes the run's `agent_end.disownedSteering` once: a user interrupt (Esc) and a natural end re-route it as sequential follow-ups of the next turn (queue and pending display together), a terminal abort drops everything except client steers admitted after the abort's admission snapshot (which are re-routed the same way and rearmed as a fresh turn), and every other abort cause (teardown, session switch, compaction, handoff, tool abort) drops it and settles its SDK submission with a removal disposition. This replaces the three cause-specific recovery patches (user-interrupt rearm, terminal-abort steering purge, and steering rearm) that each reasoned about the Agent queue separately. `cancelAndSubmit` now keeps the aborted turn's steers as steers of the new turn — restored into the new run once it is accepted — instead of re-labelling them as follow-ups that drained turn after turn; with a selected queue entry the unselected messages still become follow-ups and their pending display now mirrors that.
30
+ - Rerouted steering preserves its queue policy: `toolInterruptPolicy: "finish_tools"` with `steeringMode: "all"` keeps one ordered successor-turn cohort across run disown and abort unwind, while composer submissions explicitly marked sequential still deliver one per turn.
31
+ - A user steer admitted into a live run now folds a running foreground `bash` call (managed non-PTY, ACP client-terminal, and PTY) into a background job once the command has run for a fixed 2 s grace window (`STEER_FOLD_GRACE_MS`), mirroring how a steer ends a subagent await: the call returns the chord-fold background-start result plus `Folded into background job <id> because a user steer arrived; the command keeps running with its original timeout and its result will wake a later turn.`, the same run consumes the steer at the tool boundary, and the job's completion wakes a later turn. Steers inside the grace window let the command finish; `busyPromptMode=queue` or `toolInterruptPolicy=finish_tools` never fold; Esc still aborts. Every fold (steer, chord, auto-background timer, `bash.background`) publishes one replayable `bash_folded` SDK event and exposes `foldReason` in job snapshots.
32
+ - The TUI activity indicator itemizes running background work by kind (`Working… · 2 subagents, 1 background bash, 1 monitor` / `Background: 2 subagents, 1 background bash, 1 monitor…`) instead of a single `N background task(s)` count.
33
+ - Explicit static `vllm` and `sglang` providers no longer acquire the generic custom-provider `/v1/models` auto-discovery lane; their configured catalog remains authoritative unless `discovery` is explicitly enabled.
34
+ - Coordinator projection scans now use identity-bracketed directory authority on macOS instead of failing every scan as unsupported, while root and candidate replacement races remain fail-closed.
35
+ - The `/model` selector no longer stalls the UI thread on open, model selection, provider-tab switch, and every catalog change for credentialed users. Each `refresh("offline")` re-ran the built-in-discovery freshness filter across the whole cached catalog (thousands of models) while recomputing per model values that only vary per provider — `#getProviderEvidenceGeneration` (an AuthStorage lookup) and `#getProviderBaseUrlForDiscovery` + endpoint normalization (URL parsing). That provider-keyed work is now memoized once per provider per refresh, so on the bundled catalog with credentials `refresh("offline")` drops from ~40ms to ~15ms per interaction with identical freshness results. Complements the earlier batch canonical-selection fix.
36
+ - Skill slash commands now autocomplete from subsequent whitespace-delimited tokens, including later prompt lines and case-insensitive `/Skill:` prefixes, while runtime invocation matching uses the same case-insensitive canonical recognition and case-variant skill discovery preserves project-over-user precedence.
37
+ - `/model` preset authentication refreshes now resolve each repeated role selector once per refresh instead of once per profile assignment, avoiding synchronous UI stalls as the preset catalog grows.
38
+ - Interactive startup no longer re-verifies the signed model preset registry once per subsystem. A launch read the accepted registry four times, and each read verified every cached generation in both the primary and backup state copies from several recovery angles, re-running Ed25519 verification, Zod schema parsing, canonical-JSON re-serialization, and the confusable-identity walk over the same documents. On a 2.5MB accepted state (4278 presets, 58 profiles) that cost about 3.0s of the pre-paint startup path. Verification results are now memoized per generation, the already-schema-parsed documents are no longer re-parsed to derive an identical value, and an accepted-registry read is reused for as long as the state, backup, and control files hash unchanged, so any write or tampering by any process is still verified from scratch. Measured startup drops from ~5.5s to ~1.9s.
39
+ ### Added
40
+
41
+ - Added `browser.backend` (`native` | `aside`, default `native`) to select the browser surface offered to the model. Operators who drive a real logged-in browser through the Aside CLI had no way to keep the model off the built-in Puppeteer `browser` tool short of `browser.enabled: false`, which also dropped every hint about the replacement surface, so the model would still reach for headless Chromium or invent a browser MCP. With `aside`, the built-in `browser` tool is neither active nor discoverable (`descriptors.ts` availability gate) and the system prompt gains a `<browser-backend>` block (`prompts/agent-fragments/browser-aside.md`, threaded from the session settings through `buildSystemPrompt`) that requires browser work to run through Bash as direct `aside repl` / `aside exec` invocations with the attach-verify-snapshot discipline. Nothing is spawned, supervised, or registered by GJC: no MCP server, no in-process Aside driver, no fallback to native when the CLI is missing. `native` is byte-for-byte the previous behavior, and `browser.enabled` keeps its meaning for the native backend.
42
+ - Interactive OAuth account selection now handles a stale or missing pinned credential in place, preserving the session and actionable `/login` or AUTO guidance instead of creating an unhandled rejection and crash record.
43
+ - Custom OpenAI-compatible providers in `models.yml` now auto-populate `/model` from their live `/v1/models` catalog without a manual `discovery:` block, and auto-classify each discovered model's wire API family. Any custom provider with a `baseUrl` and an OpenAI-family `api` (either provider-level, or the dominant OpenAI-family `api` among its statically-configured models) gets an implicit optional `openai-models-list` discovery lane, so a subscription-style gateway (e.g. CLIProxyAPI) shows its full model list with zero extra config and degrades to the configured static models when the endpoint has no `/v1/models`. During discovery each model's API type is inferred from the OpenAI `owned_by` owner (`anthropic` → `anthropic-messages`, `openai` → OpenAI) and then the model id (`claude-*` → `anthropic-messages`, `gpt-*`/`o1`/`codex`/… → OpenAI); OpenAI-family models keep the provider's configured OpenAI transport (`openai-responses` vs `openai-completions`) rather than being forced to chat-completions, an explicit `discovery.apiByModelPrefix` still wins, and an inconclusive model falls back to the provider default api.
44
+ - Named-worktree SDK session.create now gives git worktree add/reuse and workspace install independent 30s budgets instead of charging them against the child 10s semantic-readiness clock. Prep timeouts stay typed and terminal (`worktree_preparation_timeout` / `dependency_preparation_timeout`); Coordinator and ACP preserve those codes instead of collapsing them to `unavailable` or `broker_compensation_unobserved`. Ordinary no-worktree sessions stay 10s/21s.
45
+ - SDK session-index scan/replay now explicitly documents and regression-tests the
46
+ v4 compatibility boundary: supported v4 session-index events and snapshots are
47
+ accepted at every entry surface (scan, replay, tail, append, repair), future
48
+ versions stay fail-closed with data-preserving quarantine, and the generic
49
+ SDK-state guard (`maximum supported version is 1`) is documented as never
50
+ applying to session-index rows so the #5181 crash class cannot recur.
51
+
52
+ - GJC plugin TAR/TGZ installs now bound archive input and aggregate gzip expansion before extraction, preventing compressed bundles from exhausting memory while preserving valid concatenated gzip members.
53
+
5
54
  ## [0.16.0] - 2026-09-02
6
55
 
7
56
  - Added master mode (`gjc --master [--scope repo|pwd|global]`) with authenticated, broker-authoritative child orchestration: `gjc sdk spawn` creates task-seeded children, scoped `gjc sdk search` discovers broker-visible sessions with paginated results, and locator v2 session rows carry canonical cwd, worktree-root, and state-root identity.
@@ -18,7 +67,6 @@
18
67
 
19
68
  - A runtime-state marker recorded against a different workspace path now reports that mismatch instead of claiming the file is unreadable, and a terminal, not-live marker that travelled into the current workspace with its session directory is adopted rather than refused. Live, non-terminal, and out-of-workspace markers are still refused untouched.
20
69
  - An unavailable provider-qualified `modelRoles.default` selection now reports the requested model instead of silently starting on another provider's baseline. This keeps signed-registry omissions and failed catalog admission actionable rather than routing requests to an unrelated retired model. (#5144)
21
-
22
70
  - Headless SDK substrate close now reports success when exact teardown observes the recorded process gone even if cleanup unlinks the durable proof first; live or identity-ambiguous substrates still report `substrate_mismatch`. (#5130)
23
71
 
24
72
  - Windows session-state locks now drain late terminal reconciliation writes before disposal returns and safely reclaim valid dead transition claims during resume. (#5102)
@@ -32,6 +80,8 @@
32
80
 
33
81
  ### Fixed
34
82
 
83
+ - The shipped pirate extension example now appends its instructions through the supported `systemPrompt` result, and unsupported `before_agent_start` result fields emit a bounded diagnostic instead of being silently ignored.
84
+
35
85
  - File locks now use a descriptor-relative exclusive directory-publication fallback
36
86
  when a filesystem returns `EINVAL` for `renameat2(RENAME_NOREPLACE)` (for example
37
87
  WSL2 DrvFs), so `edit` and terminal persistence no longer surface `Failed to publish
@@ -53,6 +103,7 @@
53
103
  - Concurrent coordinator mutations sharing one idempotency key now join the same in-process flight before taking the durable key lock, so a slow accepted prompt cannot make its retry time out as unavailable. Conflicting in-flight requests still fail closed, completed responses remain durably replayable, and different keys remain isolated. (#5110)
54
104
  - Custom providers declaring `transport: pi-native` in `models.yml` now actually dispatch through the auth-gateway native endpoint. The provider-level transport was silently dropped for models.yml-declared models, so requests went to `<baseUrl>/chat/completions` and the gateway answered 404; and the pi-native client now serializes only wire-safe tool fields, so runtime tool state carrying fs-stat BigInts no longer aborts request serialization with `JSON.stringify cannot serialize BigInt`.
55
105
  - ACP prompts now settle immediately on correlated explicit failure outcomes without waiting for a later `agent_end`; generic error-only `agent_failed` events remain additive diagnostics until an authoritative terminal, while late duplicates and unrelated correlations stay isolated. (#5107)
106
+ - Session disposal no longer waits behind an abort-ignoring tool's prompt admission or cwd read lease before reaching its bounded Agent abort. Disposal now fences new prompt and cwd-move admissions without draining unbounded reader work, while preserving cwd moves admitted before teardown and suppressing late run events. (#5100)
56
107
 
57
108
  - Managed-session file identities now use one canonical unsigned 64-bit representation across stat/native boundaries and replacement cleanup receipts. Native Windows file IDs surfaced as signed negative bigints no longer create double-hyphen receipt names that block persistence or reopen; already-written signed receipt names and interrupted pending receipts are recovered only through the existing identity-bound, fail-closed reconciliation paths. (#5095)
58
109
 
@@ -43,9 +43,23 @@ export interface AsyncJob {
43
43
  * incrementing a completed job's timer.
44
44
  */
45
45
  export declare function jobElapsedMs(job: Pick<AsyncJob, "startTime" | "endTime">, now?: number): number;
46
+ /**
47
+ * Why a foreground wait left the foreground. Every fold path names its
48
+ * trigger so job snapshots and SDK clients can tell a user steer from the
49
+ * chord, the auto-background timer, or the explicit `bash.background` control.
50
+ */
51
+ export type FoldReason = "steer" | "chord" | "timer" | "sdk_control";
52
+ /** A fold observed by the manager; published to SDK clients as `bash_folded`. */
53
+ export interface JobFoldEvent {
54
+ jobId: string;
55
+ generation: string;
56
+ reason: FoldReason;
57
+ }
46
58
  export interface AsyncJobMetadata {
47
59
  /** Set once a foreground wait has been folded, so surfacing can show folded work. */
48
60
  backgrounded?: boolean;
61
+ /** Why a foreground wait was folded; absent for jobs started directly in the background (`async: true`). */
62
+ foldReason?: FoldReason;
49
63
  subagent?: {
50
64
  id: string;
51
65
  agent: string;
@@ -197,6 +211,8 @@ export interface AsyncJobSnapshotEntry {
197
211
  status: AsyncJob["status"];
198
212
  generation: string;
199
213
  backgrounded: boolean;
214
+ /** Present when `backgrounded` was set by a fold; absent for direct background starts. */
215
+ foldReason?: FoldReason;
200
216
  deliveryState: JobDeliveryState;
201
217
  }
202
218
  /** Scalar delivery-failure evidence that may outlive its AsyncJob record. */
@@ -546,11 +562,25 @@ export declare class AsyncJobManager {
546
562
  timeoutMs?: number;
547
563
  }): Promise<boolean>;
548
564
  getLastDisposeDiagnostics(): AsyncJobDisposeDiagnostics;
565
+ /**
566
+ * Subscribe to fold events. Fires once per successful `markBackgrounded`
567
+ * transition (never on the idempotent repeat), so a subscriber can publish
568
+ * exactly one `bash_folded` frame per fold. Listener errors are isolated.
569
+ */
570
+ onFold(cb: (event: JobFoldEvent) => void): () => void;
571
+ /**
572
+ * Mark a job that was launched directly in the background (`bash` with
573
+ * `async: true`). It never had a foreground wait, so this is not a fold: no
574
+ * `foldReason` is recorded and no fold listener fires.
575
+ */
576
+ markStartedInBackground(jobId: string, generation: string): boolean;
549
577
  /**
550
578
  * Mark a running job as backgrounded (folded out of its foreground call).
551
579
  * Read by getJobsSnapshot so folded work stays visible; safe to call twice.
580
+ * The first transition records `reason` and notifies fold listeners; a
581
+ * repeat keeps the original reason and emits nothing.
552
582
  */
553
- markBackgrounded(jobId: string, generation: string): boolean;
583
+ markBackgrounded(jobId: string, generation: string, reason: FoldReason): boolean;
554
584
  waitForAll(): Promise<void>;
555
585
  drainDeliveries(options?: {
556
586
  timeoutMs?: number;
@@ -559,5 +589,6 @@ export declare class AsyncJobManager {
559
589
  dispose(options?: {
560
590
  timeoutMs?: number;
561
591
  }): Promise<boolean>;
592
+ awaitRetainedDisposalCompletion(): Promise<void>;
562
593
  isDeliverySuppressed(jobId: string, generation?: string): boolean;
563
594
  }
@@ -0,0 +1,12 @@
1
+ export type BrowserBackendId = "native" | "aside";
2
+ export interface BrowserBackend {
3
+ readonly id: BrowserBackendId;
4
+ readonly exposesBuiltinTool: boolean;
5
+ readonly developerInstructions?: string;
6
+ }
7
+ interface BrowserBackendSettingsSource {
8
+ get(key: string): unknown;
9
+ }
10
+ /** Resolve the configured browser backend. Aside is strictly opt-in. */
11
+ export declare function resolveBrowserBackend(settings: BrowserBackendSettingsSource): BrowserBackend;
12
+ export {};
@@ -150,6 +150,7 @@ export interface BinaryUpdateFlow {
150
150
  * possibly-truncated binary.
151
151
  */
152
152
  export declare function runBinaryUpdateFlow(targetPath: string, url: string, expectedVersion: string, flow: BinaryUpdateFlow): Promise<InstalledVersionVerification>;
153
+ export declare function assertSupportedLinuxLibcForTest(glibcVersionRuntime: string | undefined, muslLoaderPresent: boolean): void;
153
154
  /**
154
155
  * Run the update command.
155
156
  */
@@ -1,5 +1,6 @@
1
1
  import { Command } from "@gajae-code/utils/cli";
2
2
  import type { Args as ParsedArgs } from "../cli/args";
3
+ import { applyStartupModelProfiles } from "../main";
3
4
  import { type SessionLifecycleLaunchRequest } from "../sdk/broker/lifecycle";
4
5
  import { SessionManager } from "../session/session-manager";
5
6
  export declare function lifecycleArgs(request: SessionLifecycleLaunchRequest, cwd: string, agentDir: string): Promise<ParsedArgs>;
@@ -109,6 +110,7 @@ export declare function runSessionHost(timing?: {
109
110
  sleep?: (ms: number) => Promise<void>;
110
111
  cwd?: string;
111
112
  processIncarnation?: (pid: number) => string | undefined;
113
+ applyStartupModelProfiles?: typeof applyStartupModelProfiles;
112
114
  }): Promise<void>;
113
115
  export type SdkInternalArgv = {
114
116
  action: "broker-internal";
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Billing path for a resolved binding's credential source — audit-reversible.
3
+ * Derived from (model.api, credential-provenance) where the binding actually
4
+ * resolves credentials, not from a provider-name allowlist.
5
+ */
6
+ import type { Api } from "@gajae-code/ai/core";
7
+ import type { EffectiveProviderAuth } from "./provider-selection-policy";
8
+ /**
9
+ * Label shown in the selector row + active-model line. Only the category is
10
+ * disclosed (never the key material or project id).
11
+ */
12
+ export type BillingPathKind = "metered-api" | "cloud-project" | "bundled";
13
+ export interface BillingPath {
14
+ kind: BillingPathKind;
15
+ /** Stable per-credential-source key for one-time notice dismissal. */
16
+ dismissKey: string;
17
+ label: string;
18
+ }
19
+ /**
20
+ * Derive from the concrete binding (api + credential provenance).
21
+ * - oauth/keyless/unknown => bundled (plan/proxied/kNoAuth-like)
22
+ * - key + cloud-project api (google-vertex / bedrock) => cloud-project
23
+ * - key + any other api (including custom OpenAI-compatible) => metered-api
24
+ */
25
+ export declare function deriveBillingPath(provider: string, api: Api | string | undefined, effectiveAuth: EffectiveProviderAuth): BillingPath | undefined;
@@ -7,4 +7,16 @@
7
7
  * this dependency-light module so the resolver can import it without pulling in
8
8
  * the TUI/AI-heavy settings schema.
9
9
  */
10
- export declare const CONFIG_SCHEMA_VERSION = 1;
10
+ export declare const CONFIG_SCHEMA_VERSION = 2;
11
+ /**
12
+ * Normalize a durable `configSchemaVersion` marker into a comparable version.
13
+ *
14
+ * Every version decision (future-schema detection, migration-pending state, and
15
+ * the ordered migration registry) must read the SAME normalized value: a
16
+ * malformed marker read as a future schema by one guard and as unmigrated by
17
+ * the next would skip legacy migrations while still re-running v0 transforms.
18
+ * A finite malformed value floors to the nearest completed version; a
19
+ * non-finite or non-numeric one carries no version information and normalizes
20
+ * to 0 (fully unmigrated). `undefined` stays `undefined` (no marker written).
21
+ */
22
+ export declare function normalizeConfigSchemaVersion(value: unknown): number | undefined;
@@ -4,6 +4,7 @@ import { type ThemeColor } from "../modes/theme/theme";
4
4
  import { type ActiveProviderDescriptor } from "../sdk/providers";
5
5
  import type { AuthStorage } from "../session/auth-storage";
6
6
  import type { ActiveSearchModelContext, WebSearchMode } from "../web/search/types";
7
+ import { type BillingPath } from "./billing-path";
7
8
  import { ConfigError, ConfigFile } from "./config-file";
8
9
  import { isAuthenticated, kNoAuth } from "./model-auth";
9
10
  import { type ConfiguredModelBindings } from "./model-bindings-applier";
@@ -15,9 +16,16 @@ import { type ModelProfileDefinition } from "./model-profiles";
15
16
  import { GJC_MODEL_ASSIGNMENT_TARGET_IDS, type ModelProfileConfig } from "./models-config-schema";
16
17
  import { type EffectiveProviderAuth } from "./provider-selection-policy";
17
18
  import { type Settings } from "./settings";
19
+ export type { BillingPath, BillingPathKind } from "./billing-path";
18
20
  export type { EffectiveProviderAuth, ProviderSelectionPolicy } from "./provider-selection-policy";
19
21
  export type { CanonicalModelIndex, CanonicalModelRecord, CanonicalModelVariant, ModelEquivalenceConfig };
20
22
  export { isAuthenticated, kNoAuth };
23
+ /**
24
+ * A loopback discovery endpoint that is proven to have no listener is the normal state of
25
+ * a machine without a local model server. Structured transport evidence is required when
26
+ * available; DNS, routing, proxy, TLS, and HTTP failures remain warnings.
27
+ */
28
+ export declare function isQuietLocalDiscoveryFailure(baseUrl: string, _warning: string, evidence?: unknown): boolean;
21
29
  export type ModelRole = "default";
22
30
  export interface ModelRoleInfo {
23
31
  tag?: string;
@@ -466,6 +474,14 @@ export declare class ModelRegistry {
466
474
  * unknown/keyless providers fall back to non-OAuth.
467
475
  */
468
476
  getEffectiveProviderAuth(provider: string, sessionId?: string): EffectiveProviderAuth;
477
+ /**
478
+ * Billing path for a resolved binding — derived at the point the binding
479
+ * resolves credentials (model.api + credential provenance). Not a
480
+ * provider-name allowlist: a custom OpenAI-compatible provider bound
481
+ * to a metered gateway is "metered-api" exactly like a first-party
482
+ * key. Only the billing category is ever disclosed.
483
+ */
484
+ getBillingPath(model: Model<Api>, sessionId?: string): BillingPath | undefined;
469
485
  /**
470
486
  * Forget a session's remembered canonical variant so the next resolution
471
487
  * for that session re-ranks from scratch (explicit reselection
@@ -1738,14 +1738,14 @@ export declare const SETTINGS_SCHEMA: {
1738
1738
  readonly description: "How to drain follow-up messages after a turn completes";
1739
1739
  };
1740
1740
  };
1741
- readonly interruptMode: {
1741
+ readonly toolInterruptPolicy: {
1742
1742
  readonly type: "enum";
1743
- readonly values: readonly ["immediate", "wait"];
1744
- readonly default: "immediate";
1743
+ readonly values: readonly ["abort_tools", "finish_tools"];
1744
+ readonly default: "abort_tools";
1745
1745
  readonly ui: {
1746
1746
  readonly tab: "interaction";
1747
- readonly label: "Interrupt Mode";
1748
- readonly description: "When steering messages interrupt tool execution";
1747
+ readonly label: "Tool Interrupt Policy";
1748
+ readonly description: "Whether a steering message aborts the tools still running in the current batch (abort_tools) or lets them finish first (finish_tools). Never changes when a steer is consumed: it is always picked up at the next tool/turn boundary either way.";
1749
1749
  };
1750
1750
  };
1751
1751
  readonly busyPromptMode: {
@@ -3442,6 +3442,16 @@ export declare const SETTINGS_SCHEMA: {
3442
3442
  readonly description: "Enable the browser tool (Ulixee Hero)";
3443
3443
  };
3444
3444
  };
3445
+ readonly "browser.backend": {
3446
+ readonly type: "enum";
3447
+ readonly values: readonly ["native", "aside"];
3448
+ readonly default: "native";
3449
+ readonly ui: {
3450
+ readonly tab: "tools";
3451
+ readonly label: "Browser Backend";
3452
+ readonly description: "Browser surface offered to the model. 'native' exposes the built-in Puppeteer browser tool. 'aside' hides it and requires every browser task to run through the Aside CLI in your live logged-in browser (requires the Aside CLI).";
3453
+ };
3454
+ };
3445
3455
  readonly "browser.headless": {
3446
3456
  readonly type: "boolean";
3447
3457
  readonly default: true;
@@ -3467,7 +3477,7 @@ export declare const SETTINGS_SCHEMA: {
3467
3477
  readonly ui: {
3468
3478
  readonly tab: "tools";
3469
3479
  readonly label: "Profile reuse posture";
3470
- readonly description: "'auto' (default): when a usable real Chrome profile is available, the browser tool uses an isolated copy of it (cookies/session/cache) for stronger stealth, warns, and falls back to synthetic. 'opt-in': stay synthetic unless a real profile is explicitly requested.";
3480
+ readonly description: "'auto' (default): when a usable real Chrome profile is available, the browser tool uses an isolated copy of it (cookies/session/cache) for stronger stealth, warns, and falls back to synthetic. 'opt-in': stay synthetic unless a real profile is explicitly requested. On macOS, cookie decryption is brand-specific (Chrome Safe Storage vs Chromium Safe Storage Keychain); a Chrome profile copied into bundled Chromium decrypts to garbage, so auto reuses the profile only when the headless executable can decrypt it — detected system Chrome/Chromium or PUPPETEER_EXECUTABLE_PATH — otherwise it falls back to synthetic with a warning.";
3471
3481
  };
3472
3482
  };
3473
3483
  readonly "browser.geo.timezone": {
@@ -4547,7 +4557,7 @@ export declare function getPathsForTab(tab: SettingTab): SettingPath[];
4547
4557
  export declare function getType(path: SettingPath): SettingDef["type"];
4548
4558
  /** Get enum values for an enum setting */
4549
4559
  export declare function getEnumValues(path: SettingPath): readonly string[] | undefined;
4550
- export { CONFIG_SCHEMA_VERSION } from "./config-schema-version";
4560
+ export { CONFIG_SCHEMA_VERSION, normalizeConfigSchemaVersion } from "./config-schema-version";
4551
4561
  export type SettingsSchemaIssue = {
4552
4562
  path: string;
4553
4563
  kind: "unknown" | "invalid" | "coerced" | "pending-migration";
@@ -14,6 +14,7 @@ import type { Api, AssistantMessageEvent, AssistantMessageEventStream, Context,
14
14
  import type { OAuthCredentials, OAuthLoginCallbacks } from "@gajae-code/ai/utils/oauth/types";
15
15
  import type * as piCodingAgent from "@gajae-code/coding-agent";
16
16
  import type { AutocompleteItem, Component, EditorTheme, KeyId, TUI } from "@gajae-code/tui";
17
+ import type { JobFoldEvent } from "../../async/job-manager";
17
18
  import type { KeybindingsManager } from "../../config/keybindings";
18
19
  import type { ModelRegistry } from "../../config/model-registry";
19
20
  import type { ModelSelectorValue } from "../../config/model-selector-value";
@@ -319,7 +320,7 @@ export interface ExtensionContext {
319
320
  /** Run a control-surface mutation inside the session admission boundary. */
320
321
  withSdkControlMutation?<T>(body: () => Promise<T>): Promise<T>;
321
322
  cycleThinkingLevel(): ThinkingLevel | undefined;
322
- setQueueMode(kind: "steering" | "follow_up" | "interrupt", mode: unknown): boolean;
323
+ setQueueMode(kind: "steering" | "follow_up" | "tool_interrupt", mode: unknown): boolean;
323
324
  getSkillState(): unknown;
324
325
  getConfigItems(): unknown;
325
326
  getBranchCandidates(): unknown;
@@ -333,6 +334,8 @@ export interface ExtensionContext {
333
334
  totalBytes: number;
334
335
  } | undefined>;
335
336
  getJobs(): unknown;
337
+ /** Subscribe to foreground-wait folds (`bash_folded`). Returns an unsubscribe function. */
338
+ onJobFold?(listener: (event: JobFoldEvent) => void): () => void;
336
339
  /** Typed skill and mode controls exposed to the SDK host. */
337
340
  invokeSkill?(name: string, args?: string, options?: {
338
341
  onPreflightAccepted?: () => void;
@@ -722,7 +725,7 @@ export interface UserPythonEventResult {
722
725
  export type { ToolResultEventResult } from "../shared-events";
723
726
  export interface BeforeAgentStartEventResult {
724
727
  message?: Pick<CustomMessage, "customType" | "content" | "display" | "details" | "attribution">;
725
- /** Replace the system prompt for this turn. If multiple extensions return this, they are chained. */
728
+ /** Replace the system prompt for this turn. To append, spread event.systemPrompt into the returned array. */
726
729
  systemPrompt?: string[];
727
730
  }
728
731
  export type { SessionBeforeBranchResult, SessionBeforeCompactResult, SessionBeforeSwitchResult, SessionBeforeTreeResult, SessionCompactingResult, } from "../shared-events";
@@ -1138,7 +1141,7 @@ export interface ExtensionContextActions {
1138
1141
  getActiveModelProfile?: () => string | undefined;
1139
1142
  withSdkControlMutation?: <T>(body: () => Promise<T>) => Promise<T>;
1140
1143
  cycleThinkingLevel?: () => ThinkingLevel | undefined;
1141
- setQueueMode?: (kind: "steering" | "follow_up" | "interrupt", mode: unknown) => boolean;
1144
+ setQueueMode?: (kind: "steering" | "follow_up" | "tool_interrupt", mode: unknown) => boolean;
1142
1145
  getSkillState?: () => unknown;
1143
1146
  getConfigItems?: () => unknown;
1144
1147
  getBranchCandidates?: () => unknown;
@@ -1152,6 +1155,8 @@ export interface ExtensionContextActions {
1152
1155
  totalBytes: number;
1153
1156
  } | undefined>;
1154
1157
  getJobs?: () => unknown;
1158
+ /** Subscribe to foreground-wait folds so the SDK host can publish `bash_folded`. Returns an unsubscribe. */
1159
+ onJobFold?: (listener: (event: JobFoldEvent) => void) => () => void;
1155
1160
  setSdkPermissionProvider?: (provider: ((toolCall: ClientBridgePermissionToolCall, options: ClientBridgePermissionOption[], signal?: AbortSignal) => Promise<ClientBridgePermissionOutcome>) | undefined) => void;
1156
1161
  setSdkClientBridge?: (bridge: ClientBridge | undefined) => void;
1157
1162
  sdkControl?: (operation: string, input: Record<string, unknown>) => unknown | Promise<unknown>;
@@ -28,6 +28,19 @@ export interface GjcLaunchWorktreeResult extends GjcLaunchWorktreePlan {
28
28
  createdBranch: boolean;
29
29
  dirty?: boolean;
30
30
  }
31
+ export interface LaunchWorktreeAbortOptions {
32
+ signal?: AbortSignal;
33
+ deadlineAt?: number;
34
+ now?: () => number;
35
+ }
36
+ export declare class WorktreePreparationTimeoutError extends Error {
37
+ readonly code = "worktree_preparation_timeout";
38
+ constructor(message?: string);
39
+ }
40
+ export declare class DependencyPreparationTimeoutError extends Error {
41
+ readonly code = "dependency_preparation_timeout";
42
+ constructor(message?: string);
43
+ }
31
44
  /**
32
45
  * Pure core of {@link resolveWorktreeBucket}, exported for tests.
33
46
  *
@@ -41,11 +54,17 @@ export declare function planLaunchWorktree(cwd: string, mode: GjcLaunchWorktreeM
41
54
  };
42
55
  export declare function ensureLaunchWorktree(plan: GjcLaunchWorktreePlan | {
43
56
  enabled: false;
44
- }): GjcLaunchWorktreeResult | {
57
+ }, options?: LaunchWorktreeAbortOptions): GjcLaunchWorktreeResult | {
45
58
  enabled: false;
46
59
  };
60
+ export declare function ensureLaunchWorktreeCancellable(plan: GjcLaunchWorktreePlan | {
61
+ enabled: false;
62
+ }, options?: LaunchWorktreeAbortOptions): Promise<GjcLaunchWorktreeResult | {
63
+ enabled: false;
64
+ }>;
47
65
  /** Workspace worktrees must own their complete lockfile-resolved dependency graph (#4620). */
48
66
  export declare function ensureReusableNodeModules(sourceRoot: string, worktreePath: string): "symlink" | "present" | "missing";
67
+ export declare function ensureReusableNodeModulesCancellable(sourceRoot: string, worktreePath: string, options?: LaunchWorktreeAbortOptions): Promise<"symlink" | "present" | "missing">;
49
68
  /** Result of {@link prepareLaunchWorktree}: the effective working directory, remaining args, and resolved worktree plan. */
50
69
  export interface PreparedLaunchWorktree {
51
70
  cwd: string;
@@ -55,3 +74,8 @@ export interface PreparedLaunchWorktree {
55
74
  };
56
75
  }
57
76
  export declare function prepareLaunchWorktree(cwd: string, args: string[]): PreparedLaunchWorktree;
77
+ export declare function removeOwnedLaunchWorktree(plan: GjcLaunchWorktreePlan, receipt: {
78
+ created: boolean;
79
+ reused: boolean;
80
+ createdBranch: boolean;
81
+ }): void;
@@ -83,6 +83,8 @@ export declare const SessionStateLockTestHooks: {
83
83
  forcedQuarantineName?: string;
84
84
  /** @internal Observes bounded acquisition retries without changing their timing. */
85
85
  afterAcquireContention?: (lockFile: string, attempt: number, elapsedMs: number) => void;
86
+ /** @internal Runs after transition mkdir contention and before stale-claim inspection. */
87
+ afterTransitionClaimContention?: (transitionDir: string) => void | Promise<void>;
86
88
  };
87
89
  /** Raised when the lock could not be acquired; callers map it to their own refusal. */
88
90
  export type SessionStateLockUnavailableReason = "acquire_timeout" | "legacy_directory_owner_unprovenanced" | "lock_initialization_failed" | "lock_inspection_failed" | "lock_owner_live_or_unverifiable" | "lock_owner_record_fresh" | "lock_owner_record_unprovenanced" | "lock_release_failed" | "transition_claim_timeout" | "unsafe_lock_path_type";
@@ -100,6 +102,26 @@ export declare class SessionStateLockUnavailableError extends Error {
100
102
  readonly elapsedMs?: number;
101
103
  constructor(causeOrDetails?: unknown | SessionStateLockUnavailableDetails);
102
104
  }
105
+ /**
106
+ * Log fields describing a persist failure: the error text plus, when a session state
107
+ * lock refusal sits anywhere in the cause chain, its typed `reason` and `lockPath`.
108
+ * Wrapping errors (`PreviousRuntimeStateReadError`, `AggregateError`) are searched
109
+ * so the actionable detail is never buried behind a generic message.
110
+ */
111
+ export declare function sessionStateLockFailureFields(error: unknown): {
112
+ error: string;
113
+ reason?: SessionStateLockUnavailableReason;
114
+ lockPath?: string;
115
+ };
116
+ export declare const PERSIST_FAILURE_WARN_WINDOW_MS = 30000;
117
+ /**
118
+ * Whether a persist failure should be logged at warn level. The first failure for each
119
+ * document and stable failure class in a 30s window warns; repeats are the caller's cue
120
+ * to log at debug instead.
121
+ */
122
+ export declare function shouldWarnPersistFailure(key: string, error: unknown, now?: number): boolean;
123
+ /** @internal Test seam: forget every persist-failure warn window. */
124
+ export declare function resetPersistFailureWarnWindows(): void;
103
125
  /**
104
126
  * The identity-bound deletion primitives this lock is built on.
105
127
  *
@@ -58,6 +58,7 @@ type StartupModelProfileArgs = {
58
58
  startupModel?: CreateAgentSessionOptions["model"];
59
59
  startupThinkingLevel?: CreateAgentSessionOptions["thinkingLevel"];
60
60
  preferCachedModels?: boolean;
61
+ preferCachedDefaultProfile?: boolean;
61
62
  };
62
63
  export declare function applyStartupModelProfiles(args: StartupModelProfileArgs): Promise<void>;
63
64
  export declare function applyStartupModelProfilesOrExit(args: StartupModelProfileArgs): Promise<void>;
@@ -123,11 +123,11 @@ export declare function acpSessionStateFromConfig(query: unknown, modelCatalogQu
123
123
  readonly value: "one-at-a-time";
124
124
  readonly name: "One at a time";
125
125
  } | {
126
- readonly value: "immediate";
127
- readonly name: "Immediate";
126
+ readonly value: "abort_tools";
127
+ readonly name: "Abort running tools";
128
128
  } | {
129
- readonly value: "wait";
130
- readonly name: "Wait";
129
+ readonly value: "finish_tools";
130
+ readonly name: "Finish running tools";
131
131
  })[];
132
132
  } | {
133
133
  id: "thinking";
@@ -151,11 +151,11 @@ export declare function acpSessionStateFromConfig(query: unknown, modelCatalogQu
151
151
  readonly value: "one-at-a-time";
152
152
  readonly name: "One at a time";
153
153
  } | {
154
- readonly value: "immediate";
155
- readonly name: "Immediate";
154
+ readonly value: "abort_tools";
155
+ readonly name: "Abort running tools";
156
156
  } | {
157
- readonly value: "wait";
158
- readonly name: "Wait";
157
+ readonly value: "finish_tools";
158
+ readonly name: "Finish running tools";
159
159
  })[];
160
160
  } | {
161
161
  id: "steeringMode";
@@ -178,11 +178,11 @@ export declare function acpSessionStateFromConfig(query: unknown, modelCatalogQu
178
178
  readonly value: "one-at-a-time";
179
179
  readonly name: "One at a time";
180
180
  } | {
181
- readonly value: "immediate";
182
- readonly name: "Immediate";
181
+ readonly value: "abort_tools";
182
+ readonly name: "Abort running tools";
183
183
  } | {
184
- readonly value: "wait";
185
- readonly name: "Wait";
184
+ readonly value: "finish_tools";
185
+ readonly name: "Finish running tools";
186
186
  })[];
187
187
  } | {
188
188
  id: "followUpMode";
@@ -205,14 +205,14 @@ export declare function acpSessionStateFromConfig(query: unknown, modelCatalogQu
205
205
  readonly value: "one-at-a-time";
206
206
  readonly name: "One at a time";
207
207
  } | {
208
- readonly value: "immediate";
209
- readonly name: "Immediate";
208
+ readonly value: "abort_tools";
209
+ readonly name: "Abort running tools";
210
210
  } | {
211
- readonly value: "wait";
212
- readonly name: "Wait";
211
+ readonly value: "finish_tools";
212
+ readonly name: "Finish running tools";
213
213
  })[];
214
214
  } | {
215
- id: "interruptMode";
215
+ id: "toolInterruptPolicy";
216
216
  name: string;
217
217
  type: "select";
218
218
  currentValue: string;
@@ -232,11 +232,11 @@ export declare function acpSessionStateFromConfig(query: unknown, modelCatalogQu
232
232
  readonly value: "one-at-a-time";
233
233
  readonly name: "One at a time";
234
234
  } | {
235
- readonly value: "immediate";
236
- readonly name: "Immediate";
235
+ readonly value: "abort_tools";
236
+ readonly name: "Abort running tools";
237
237
  } | {
238
- readonly value: "wait";
239
- readonly name: "Wait";
238
+ readonly value: "finish_tools";
239
+ readonly name: "Finish running tools";
240
240
  })[];
241
241
  })[];
242
242
  modes: {
@@ -58,6 +58,10 @@ export declare class ModelSelectorComponent extends Container {
58
58
  smartRoutingPreview?: (draft: AutoroutingSetup) => SmartRoutingPreview;
59
59
  /** Open the smart-routing panel directly instead of the preset landing. */
60
60
  smartRoutingOnly?: boolean;
61
+ /** Test seam: synchronous persisted-dismissal check to avoid async render churn. */
62
+ isBillingDismissed?: (key: string) => boolean;
63
+ /** Test seam: record that a billing notice was shown/dismissed. */
64
+ markBillingDismissed?: (key: string) => void;
61
65
  });
62
66
  dispose(): void;
63
67
  refreshRoleAssignments(options?: {
@@ -1,5 +1,5 @@
1
1
  import { type Component, Container } from "@gajae-code/tui";
2
- import type { ResumeSessionIdentity, ResumeTailInspection, SessionInfo } from "../../session/session-manager";
2
+ import { type ResumeSessionIdentity, type ResumeTailInspection, type SessionInfo } from "../../session/session-manager";
3
3
  export interface SelectedSession {
4
4
  kind: "selected";
5
5
  path: string;
@@ -1,10 +1,11 @@
1
1
  import { Container } from "@gajae-code/tui";
2
- import type { SessionInfo } from "../../session/session-manager";
2
+ import { type SessionInfo } from "../../session/session-manager";
3
3
  export type SessionLiveness = "active" | "stale" | "unknown";
4
4
  export interface DashboardSession {
5
5
  id: string;
6
6
  cwd: string;
7
7
  title: string;
8
+ starred: boolean;
8
9
  modified: Date;
9
10
  messageCount: number;
10
11
  messageCountIsEstimate?: boolean;