@gajae-code/coding-agent 0.12.16-nightly.20260807070805.31156495595.g91c4eff67ce2 → 0.12.16

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 (72) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/dist/types/cli/args.d.ts +2 -0
  3. package/dist/types/cli.d.ts +7 -0
  4. package/dist/types/commands/launch.d.ts +7 -0
  5. package/dist/types/config/keybindings.d.ts +5 -0
  6. package/dist/types/config/model-bindings-applier.d.ts +9 -0
  7. package/dist/types/config/model-profile-activation.d.ts +18 -3
  8. package/dist/types/config/model-registry.d.ts +11 -0
  9. package/dist/types/config/settings-schema.d.ts +31 -0
  10. package/dist/types/extensibility/extensions/compact-handler.d.ts +6 -0
  11. package/dist/types/extensibility/extensions/types.d.ts +29 -2
  12. package/dist/types/gjc-runtime/gc-runtime.d.ts +3 -0
  13. package/dist/types/gjc-runtime/gc-session-scope.d.ts +21 -0
  14. package/dist/types/modes/acp/acp-agent.d.ts +1 -0
  15. package/dist/types/modes/controllers/input-controller.d.ts +9 -0
  16. package/dist/types/modes/prompt-action-autocomplete.d.ts +1 -0
  17. package/dist/types/sdk/acp/adapter.d.ts +16 -1
  18. package/dist/types/sdk/acp/index.d.ts +1 -1
  19. package/dist/types/sdk/host/session-runtime.d.ts +11 -0
  20. package/dist/types/sdk/model-profile-model.d.ts +58 -0
  21. package/dist/types/sdk/models.d.ts +14 -0
  22. package/dist/types/sdk/session.d.ts +14 -1
  23. package/dist/types/session/agent-session.d.ts +60 -3
  24. package/dist/types/utils/clipboard.d.ts +38 -4
  25. package/package.json +8 -8
  26. package/scripts/generate-sdk-operation-inventory.ts +14 -0
  27. package/scripts/generate-tool-catalog.ts +1 -0
  28. package/src/cli/args.ts +24 -0
  29. package/src/cli/fast-help.ts +3 -0
  30. package/src/cli.ts +9 -0
  31. package/src/commands/launch.ts +7 -0
  32. package/src/config/keybindings.ts +7 -0
  33. package/src/config/model-bindings-applier.ts +60 -0
  34. package/src/config/model-profile-activation.ts +47 -12
  35. package/src/config/model-registry.ts +24 -2
  36. package/src/config/provider-ranking.ts +1 -0
  37. package/src/config/settings-schema.ts +83 -0
  38. package/src/debug/log-viewer.ts +12 -10
  39. package/src/debug/raw-sse.ts +15 -10
  40. package/src/extensibility/extensions/compact-handler.ts +14 -0
  41. package/src/extensibility/extensions/loader.ts +6 -2
  42. package/src/extensibility/extensions/runner.ts +13 -0
  43. package/src/extensibility/extensions/types.ts +44 -3
  44. package/src/gjc-runtime/gc-render.ts +17 -0
  45. package/src/gjc-runtime/gc-runtime.ts +23 -0
  46. package/src/gjc-runtime/gc-session-scope.ts +144 -0
  47. package/src/hooks/skill-keywords.ts +0 -36
  48. package/src/internal-urls/docs-index.generated.ts +9 -8
  49. package/src/main.ts +15 -0
  50. package/src/modes/acp/acp-agent.ts +99 -5
  51. package/src/modes/action-registry.ts +1 -0
  52. package/src/modes/controllers/command-controller.ts +14 -16
  53. package/src/modes/controllers/extension-ui-controller.ts +74 -19
  54. package/src/modes/controllers/input-controller.ts +73 -24
  55. package/src/modes/controllers/todo-command-controller.ts +4 -6
  56. package/src/modes/prompt-action-autocomplete.ts +8 -0
  57. package/src/modes/runtime-init.ts +68 -5
  58. package/src/prompts/system/system-prompt.md +16 -2
  59. package/src/sdk/acp/adapter.ts +43 -3
  60. package/src/sdk/acp/index.ts +1 -1
  61. package/src/sdk/bus/index.ts +156 -8
  62. package/src/sdk/host/query/revision-store.ts +40 -38
  63. package/src/sdk/host/session-runtime.ts +322 -28
  64. package/src/sdk/model-profile-model.ts +131 -0
  65. package/src/sdk/models.ts +77 -2
  66. package/src/sdk/protocol/operation-inventory.generated.json +77 -0
  67. package/src/sdk/session.ts +21 -6
  68. package/src/session/agent-session.ts +318 -9
  69. package/src/session/internal/managed-session-storage.ts +2 -1
  70. package/src/task/executor.ts +2 -2
  71. package/src/tools/tool-catalog.generated.ts +2460 -2154
  72. package/src/utils/clipboard.ts +320 -30
package/CHANGELOG.md CHANGED
@@ -1,23 +1,42 @@
1
1
  # Changelog
2
2
 
3
3
  ## [Unreleased]
4
+
5
+ ## [0.12.16] - 2026-08-08
6
+
7
+ ### Added
8
+
9
+ - `gjc gc` now reports managed session scope capacity when a scope is at or past 75% of the managed byte budget. A scope is snapshotted in full on every session start and fails closed once it exceeds the budget, but it is filled by GJC's own session records, so a working directory in sustained use can cross the limit with no prior signal — the first symptom is a launch that aborts. The probe is read-only and never fails a gc run: an absent, unreadable, or non-directory scope is reported as `unavailable`, an unreadable subtree is skipped so a partial walk still answers "am I near the budget?", and scopes below the threshold are omitted entirely so existing output is unchanged. `gc` still reclaims nothing here; the report names the scope path so stale session directories can be moved out by hand.
10
+ - `--clipboard-transport <auto|native|osc52|ssh>` and `--clipboard-ssh-host <alias>` CLI flags, plus persisted `clipboard.transport` / `clipboard.sshHost` settings (CLI overrides config, config overrides the `auto` default). `ssh` mode routes text copy/paste through `ssh -o BatchMode=yes -o ConnectTimeout=3 -- <host> pbcopy/pbpaste` via argv spawn (never a shell string) with a 5-second hard timeout covering the whole operation lifecycle, a 1 MiB payload cap enforced during streaming (never fully buffered before the check), fatal UTF-8 decoding on the inbound side (invalid bytes are rejected, not silently normalized to U+FFFD), and outbound NUL-byte/unpaired-surrogate rejection. Explicit `ssh` failures (nonzero exit, timeout, invalid host, oversize/invalid payload) surface a sanitized error and never silently fall back to native clipboard or OSC 52 — clipboard payloads are never written to logs or artifacts. `auto`/`native`/`osc52` behave exactly as before. New `app.clipboard.pasteText` action (no default key; command palette only, so it never collides with the existing image-paste binding) reads the configured `ssh` clipboard and inserts it at the cursor.
11
+
4
12
  ### Fixed
5
13
 
14
+ - SDK snapshot shutdown now reserves a revision write synchronously before any asynchronous serialization work, so an immediate `close()` cannot begin terminal cleanup ahead of an in-progress spill write under heavily contended CI scheduling. The coordinator owner-intent negative matrix also receives the same 30-second contention budget used by other filesystem-backed integration cases; its assertions and four-case coverage are unchanged.
6
15
  - `todo_write` and `ask` no longer reject valid calls before the tool loads. Both tools carried two independent copies of their raw-argument rules — one in the loaded tool, one in the cold descriptor registry that runs first — and the deferred copies had drifted: `todo_write`'s dropped the `content` synonym for `task` and the `complete`/`completed` aliases for `done`, accepted targetless `complete` entries the loaded tool rejects, and returned every rejection without its correction code, so the model saw a bare "raw arguments rejected before coercion" with nothing to fix and retried the same shape until the turn died. Both also rejected the harness's own injected `_i` intent field, failing any call carrying it with an unknown-root-key error the model could not repair. `todo_write` validation now lives in a single shared contract module (`tools/todo-contract.ts`) used by both paths, and both tools tolerate `_i` at the root while still rejecting genuinely unknown keys.
16
+ - A stalled ACP session is no longer unrecoverable. The SDK host drops a session whose client has not ponged within `HEARTBEAT_TTL_MS` (20s), but the ACP client inherited the transport's one-shot reconnect defaults — 3 attempts at a 25ms base backoff, a total budget of 175ms — so any event-loop stall long enough for the host to reap the session exceeded the client's entire retry window by two orders of magnitude and surfaced as a terminal `-32603 ACP session transport was lost: SDK WebSocket reconnect attempts exhausted`. Under machine load this killed long-running agent sessions outright while their processes stayed alive. The ACP adapter and the broker connection now share an explicit `ACP_SESSION_RECONNECT` budget derived from `HEARTBEAT_TTL_MS` rather than a magic number: backoff ramps 250ms → 500ms → 1s and holds at a 2s cap for 23 attempts (~41.75s), so the client outlives twice the host TTL while individual sleeps stay short enough to reattach promptly once the host answers again.
7
17
  - Managed scope prepare and legacy-local resume no longer report success while group/other-readable descendants remain on disk (e.g. mode `0o036`/`0o644`): prepare uses a mode-only walk (not `snapshotManagedTree("")`, which races concurrent writers and broke `/move`) to detect drift, re-secures with apply+verify, and retries once; legacy-local capture uses the same resecure helper.
8
18
  - ACP `session/request_permission` responses are now normalized from the spec-shaped `RequestPermissionResponse` (`{ outcome: { outcome, optionId } }`) into the SDK's flat permission-decision contract before reaching the permission provider. Standards-compliant ACP clients such as Paseo can now authorize permission-gated shell/eval and destructive file operations (`bash`, `monitor`, `eval`, `delete`, `move`, and `edit` only for delete/move operations) without an invalid-response failure; `write` and ordinary edits remain ungated. Nested and flat selected/cancelled responses are accepted by reconstructing the canonical SDK decision fields, while malformed or unknown decisions fail closed.
19
+ - `bun run restart:sdk-broker --close-session-hosts` no longer aborts the whole restart when one session host refuses to close. A host whose endpoint is unreachable and whose durable process identity cannot be verified — an orphan left behind by an earlier broker — made the broker reply `close_refused`, which surfaced as an uncaught `SdkClientError`, so the broker was never replaced and kept serving the source it started with: precisely the stale-code failure the flag exists to prevent, and worse than the broker-only restart because part of the teardown had already run. Every listed host is now attempted, the replacement broker starts regardless, and surviving hosts are named with their failure reason on stderr with a non-zero exit so a partial teardown is never reported as a clean one.
20
+ - An ACP turn can no longer outlive its own end and wedge the session. A prompt whose terminal frame carried no normalized outcome — what an SDK-side `terminal_uncertain` closure produces when agent-owned async work (subagents, IRC deliveries) outlives the turn — was rejected without ever publishing the matching `session_info_update`, so the client stayed in the `working` phase forever. `session/cancel` had the same open end: `turn.abort` was acknowledged but nothing settled the pending `session/prompt` when the aborted run never published a terminal, leaving the waiter pending, the composer spinning, and every later prompt refused with `conflict` (surfacing in Paseo as `cancelAgentRun: acknowledged turn still active after timeout` followed by a permanent `A foreground turn is already active`). Every prompt rejection now releases the running phase, and an acknowledged cancel settles as ACP's mandated `cancelled` stop reason once a bounded grace expires, with a real terminal still winning inside that grace.
21
+ - Intent tracing (`_i`) is no longer disabled on every headless surface. It was gated on `hasUI`, so an ACP, print-mode, or SDK-embedded top-level session ran a measurably different turn than the TUI on identical settings: the `_i` guidance line was dropped from the system prompt and the field was stripped from every tool schema, removing the model's pre-call intent statement and leaving ACP `tool_call.title` permanently falling back to `tool: <path>`. The original intent of the gate was to spare *sub-agents* the per-call token cost, so the omission now keys on canonical sub-sessions (`taskDepth`/`parentTaskPrefix`/`currentAgentType`) instead of surface shape. `tools.intentTracing` and `PI_INTENT_TRACING` are unchanged and still authoritative. **Breaking (SDK):** `resolveIntentTracingEnabled`'s second parameter is now `{ subSession: boolean }` instead of `hasUI: boolean`; a boolean is rejected at compile time rather than silently inverting behavior.
22
+ - A 61MB `bun build --compile` intermediate (`packages/coding-agent/.18c95f9fdbe9bff8-00000000.bun-build`) had been committed to `dev`, and five more sat untracked in the worktree. `bun build --compile` writes these hidden temporaries next to the entrypoint and leaves them behind when interrupted, and no ignore rule covered them. `*.bun-build` is now ignored and the committed copy is untracked.
9
23
 
10
24
  ### Added
11
25
 
26
+ - The SDK `models.list/current` (Q10) catalog now lists model profiles as synthetic `gajae-code/<profile>` entries (e.g. `gajae-code/codex-eco`), and selecting one through `model.set` (or the ACP Model picker) activates the profile for the live session only; global persistence remains the explicit `gjc --mpreset <name> --default` or TUI default-selection path. ACP/SDK clients such as Paseo can therefore offer presets like ordinary models; only availability-filtered profiles are advertised, the reserved `gajae-code` namespace fails closed on collision, and `config.patch` serializes with profile activation through the session admission boundary.
12
27
  - Added first-class `cline-pass` and `commandcode-goat` provider presets with documented API endpoints, environment-variable credentials, non-hardcoded live model discovery from models.dev and the Command Code Provider API, and prefix-based Claude routing.
28
+ - Registered the `jetbrains-junie` provider in the famous-provider ordering and the `JUNIE_API_KEY` credential help so JetBrains AI (Junie) Claude models surface in `/model`, `--list-models` and `gjc --help` alongside the other first-class providers (#3626).
13
29
  - Added lease-backed MCP connection pooling with typed recovery, shared HTTP/SSE sessions, per-lease callback demultiplexing, and authorization binding scopes that keep credential secrets out of pool keys.
14
30
  - Added plugin registry v2 as the single execution authority for plugin tools, subskills, and prompt appendices, with digest verification at final use.
15
31
  - Added module-trace and process-tree RSS verification harnesses for startup-memory regressions.
32
+ - Added `bun run clean` / `bun run clean:native` (`scripts/clean.ts`) to remove build output — `dist/`, `binaries/`, `coverage/`, stray `*.bun-build`, `*.tsbuildinfo`, and with `--native` compiled `.node` addons. Sources, `node_modules/`, `.gjc/` runtime state, and `artifacts/` test evidence are refused as targets rather than silently skipped, and `--dry-run` lists targets without deleting.
16
33
 
17
34
  ### Changed
18
35
 
36
+ - Workflow skills are no longer implicitly auto-routed. The UserPromptSubmit keyword autoroute now matches only explicit `$`-prefixed tokens (`$deep-interview`, `$ralplan`, `$ultragoal`, `$team`); natural-language phrases ("don't assume", "consensus plan", "interview me", "coordinated team") and bare skill names no longer activate workflows. The system prompt now ranks explicit user intent above every routing heuristic, forbids workflow self-invocation and plan stacking, bans task-difficulty overestimation, directs the agent to offer heuristic workflow escalation (especially deep-interview for vague requirements) through the `ask` tool with an opt-out, and adds an `<engineering>` principles section.
19
37
  - Deferred notification adapters, native bindings, provider construction, tools, skills, eval, session artifacts, and history storage until their feature paths are used, reducing the CLI startup module graph without changing default behavior.
20
38
  - Split SDK session hosting into a transport-neutral runtime and lazy notification adapters.
39
+ - `sticky-viewport-showcase.test.ts` runs in ~53s instead of ~192s, cutting the slowest CI test shard from ~369s to ~167s locally. Each of its 17 cases spawned a `bun` subprocess that re-rendered all 20 showcase frames (~9.4s per capture, ~187s of the file's ~192s) purely to obtain a pristine bundle it then corrupted. The bundle is now captured once and handed out as filesystem copies; the one case that must observe mutated `GJC_STICKY_VIEWPORT_ORACLE_COMMIT` state still captures for itself. Isolation is load-bearing and verified: sharing the directory instead of copying it fails 9 cases.
21
40
 
22
41
  ### Fixed
23
42
 
@@ -14,6 +14,8 @@ export interface Args {
14
14
  credential?: string;
15
15
  systemPrompt?: string;
16
16
  appendSystemPrompt?: string;
17
+ clipboardTransport?: "auto" | "native" | "osc52" | "ssh";
18
+ clipboardSshHost?: string;
17
19
  mcpConfig?: string;
18
20
  thinking?: Effort;
19
21
  continue?: boolean;
@@ -60,6 +60,13 @@ export declare class RootHelpCommand extends Command {
60
60
  "mcp-config": import("@gajae-code/utils/cli").FlagDescriptor<"string"> & {
61
61
  description: string;
62
62
  };
63
+ "clipboard-transport": import("@gajae-code/utils/cli").FlagDescriptor<"string"> & {
64
+ description: string;
65
+ options: string[];
66
+ };
67
+ "clipboard-ssh-host": import("@gajae-code/utils/cli").FlagDescriptor<"string"> & {
68
+ description: string;
69
+ };
63
70
  "allow-home": import("@gajae-code/utils/cli").FlagDescriptor<"boolean"> & {
64
71
  description: string;
65
72
  };
@@ -50,6 +50,13 @@ export default class Index extends Command {
50
50
  "mcp-config": import("@gajae-code/utils/cli").FlagDescriptor<"string"> & {
51
51
  description: string;
52
52
  };
53
+ "clipboard-transport": import("@gajae-code/utils/cli").FlagDescriptor<"string"> & {
54
+ description: string;
55
+ options: string[];
56
+ };
57
+ "clipboard-ssh-host": import("@gajae-code/utils/cli").FlagDescriptor<"string"> & {
58
+ description: string;
59
+ };
53
60
  "allow-home": import("@gajae-code/utils/cli").FlagDescriptor<"boolean"> & {
54
61
  description: string;
55
62
  };
@@ -22,6 +22,7 @@ interface AppKeybindings {
22
22
  "app.message.queue": true;
23
23
  "app.message.dequeue": true;
24
24
  "app.clipboard.pasteImage": true;
25
+ "app.clipboard.pasteText": true;
25
26
  "app.clipboard.copyLine": true;
26
27
  "app.clipboard.copyPrompt": true;
27
28
  "app.session.new": true;
@@ -261,6 +262,10 @@ export declare const KEYBINDINGS: {
261
262
  readonly defaultKeys: KeyId;
262
263
  readonly description: "Paste image from clipboard";
263
264
  };
265
+ readonly "app.clipboard.pasteText": {
266
+ readonly defaultKeys: [];
267
+ readonly description: "Paste text from configured clipboard transport (command palette only; no default key to avoid colliding with image paste)";
268
+ };
264
269
  readonly "app.clipboard.copyLine": {
265
270
  readonly defaultKeys: "alt+shift+l";
266
271
  readonly description: "Copy current line";
@@ -7,7 +7,16 @@ export interface ConfiguredModelBindings {
7
7
  /** Synchronizes config-owned model bindings while preserving user edits. */
8
8
  export declare class ModelBindingsApplier {
9
9
  #private;
10
+ /** The currently configured bindings (as installed at startup), for baseline lookup. */
11
+ getBindings(): ConfiguredModelBindings | undefined;
10
12
  setBindings(bindings: ConfiguredModelBindings | undefined): void;
11
13
  applyTo(targetSettings: Settings): void;
14
+ /**
15
+ * Re-assert configured bindings into the target override slots, bypassing
16
+ * the user-edit-preservation heuristic. Used after a session-scoped profile
17
+ * reset removes profile-installed keys, so configured role/agent routing is
18
+ * restored exactly as it was installed at startup.
19
+ */
20
+ forceApplyTo(targetSettings: Settings): void;
12
21
  apply(): void;
13
22
  }
@@ -9,6 +9,15 @@ type ModelProfileActivationSession = Pick<AgentSession, "model" | "thinkingLevel
9
9
  setModelTemporary?: AgentSession["setModelTemporary"];
10
10
  setActiveModelProfile?: (name: string | undefined) => void;
11
11
  getActiveModelProfile?: () => string | undefined;
12
+ /** Record which runtime override keys this activation installed (session-scoped). */
13
+ noteProfileInstalledOverrides?: (modelRoles: readonly string[], agentModelOverrides: readonly string[], preProfileModel: Model<Api> | undefined) => void;
14
+ /** Drop the recorded profile-installed override keys (e.g. after materialization). */
15
+ clearProfileInstalledOverrides?: () => void;
16
+ /** Current profile-installed override keys, for deriving the activation base. */
17
+ getProfileInstalledOverrideKeys?: () => {
18
+ modelRoles: readonly string[];
19
+ agentModelOverrides: readonly string[];
20
+ };
12
21
  getSessionDefaultModelSelector?: () => string | undefined;
13
22
  recordResumeDefaultModel?: (selector: string) => void;
14
23
  seedDefaultFallbackResolution?: (activeIndex: number, skips: Array<{
@@ -21,7 +30,7 @@ export interface PrepareModelProfileActivationOptions {
21
30
  modelRegistry: Pick<ModelRegistry, "getModelProfile" | "getModelProfiles" | "getAvailableModelProfileNames" | "getApiKeyForProvider" | "getAll" | "resolveCanonicalModel" | "getCanonicalVariants" | "getCanonicalId"> & {
22
31
  getError?: ModelRegistry["getError"];
23
32
  };
24
- settings: Pick<Settings, "get">;
33
+ settings: Pick<Settings, "get" | "getGlobal">;
25
34
  profileName: string;
26
35
  }
27
36
  export interface ApplyModelProfileActivationOptions {
@@ -38,6 +47,8 @@ export interface PreparedModelProfileActivation {
38
47
  previousThinkingLevel: ThinkingLevel | undefined;
39
48
  previousAgentModelOverrides: Record<string, ModelSelectorValue>;
40
49
  previousModelRoles: Record<string, ModelSelectorValue>;
50
+ baseAgentModelOverrides: Record<string, ModelSelectorValue>;
51
+ baseModelRoles: Record<string, ModelSelectorValue>;
41
52
  previousDefaultChain: readonly string[] | undefined;
42
53
  defaultModel: Model<Api> | undefined;
43
54
  defaultThinkingLevel: ThinkingLevel | undefined;
@@ -63,13 +74,17 @@ export interface PreparedModelProfileActivation {
63
74
  previousSessionDefaultModel: string | undefined;
64
75
  }
65
76
  export interface MaterializeModelProfileAssignmentOptions {
66
- session: Pick<ModelProfileActivationSession, "model" | "thinkingLevel" | "getConfiguredModelChain" | "setActiveModelProfile" | "getActiveModelProfile">;
77
+ session: Pick<ModelProfileActivationSession, "model" | "thinkingLevel" | "getConfiguredModelChain" | "setActiveModelProfile" | "getActiveModelProfile"> & {
78
+ clearProfileInstalledOverrides?: () => void;
79
+ };
67
80
  settings: Pick<Settings, "clearOverride" | "get" | "override" | "set" | "unset">;
68
81
  role: GjcModelAssignmentTargetId;
69
82
  selector: string;
70
83
  }
71
84
  export interface MaterializeModelProfileAssignmentsOptions {
72
- session: Pick<ModelProfileActivationSession, "model" | "thinkingLevel" | "getConfiguredModelChain" | "setActiveModelProfile" | "getActiveModelProfile">;
85
+ session: Pick<ModelProfileActivationSession, "model" | "thinkingLevel" | "getConfiguredModelChain" | "setActiveModelProfile" | "getActiveModelProfile"> & {
86
+ clearProfileInstalledOverrides?: () => void;
87
+ };
73
88
  settings: Pick<Settings, "clearOverride" | "get" | "override" | "set" | "unset">;
74
89
  assignments: ReadonlyMap<GjcModelAssignmentTargetId, string> | Partial<Record<GjcModelAssignmentTargetId, string>>;
75
90
  }
@@ -6,6 +6,7 @@ import type { AuthStorage } from "../session/auth-storage";
6
6
  import type { ActiveSearchModelContext, WebSearchMode } from "../web/search/types";
7
7
  import { type ConfigError, ConfigFile } from "./config-file";
8
8
  import { isAuthenticated, kNoAuth } from "./model-auth";
9
+ import { type ConfiguredModelBindings } from "./model-bindings-applier";
9
10
  import { type ProviderDiscoveryState } from "./model-discovery-manager";
10
11
  export type { ProviderDiscoveryState, ProviderDiscoveryStatus } from "./model-discovery-manager";
11
12
  import { type CanonicalModelIndex, type CanonicalModelRecord, type CanonicalModelVariant, type ModelEquivalenceConfig } from "./model-equivalence";
@@ -353,11 +354,21 @@ export declare class ModelRegistry {
353
354
  renameCustomModelProfile(name: string, displayName: string): Promise<ModelProfileDefinition>;
354
355
  deleteCustomModelProfile(name: string): Promise<ModelProfileConfig>;
355
356
  applyConfiguredModelBindings(targetSettings: Settings): void;
357
+ /**
358
+ * Re-assert configured modelBindings into the target override slots after a
359
+ * session-scoped profile reset removed profile-installed keys. Bypasses the
360
+ * user-edit heuristic so the startup role/agent routing is restored.
361
+ */
362
+ reapplyConfiguredModelBindings(targetSettings: Settings): void;
363
+ /** The currently configured modelBindings, for pre-profile baseline lookup. */
364
+ getConfiguredModelBindings(): ConfiguredModelBindings | undefined;
356
365
  /**
357
366
  * Get all models (built-in + custom).
358
367
  * If models.json had errors, returns only built-in models.
359
368
  */
360
369
  getAll(): Model<Api>[];
370
+ /** Provider ids declared in models.yml, including override-only providers. */
371
+ getConfiguredProviderIds(): readonly string[];
361
372
  getCanonicalModels(options?: CanonicalModelQueryOptions): CanonicalModelRecord[];
362
373
  getCanonicalVariants(canonicalId: string, options?: CanonicalModelQueryOptions): CanonicalModelVariant[];
363
374
  resolveCanonicalModel(canonicalId: string, options?: CanonicalModelQueryOptions): Model<Api> | undefined;
@@ -3133,6 +3133,26 @@ export declare const SETTINGS_SCHEMA: {
3133
3133
  readonly description: "Past soft TTL but within hard TTL, the tool returns the cached row and refreshes it in the background. Past hard TTL, the row is dropped. Default 7 days.";
3134
3134
  };
3135
3135
  };
3136
+ readonly "clipboard.transport": {
3137
+ readonly type: "enum";
3138
+ readonly values: readonly ["auto", "native", "osc52", "ssh"];
3139
+ readonly default: "auto";
3140
+ readonly ui: {
3141
+ readonly tab: "tools";
3142
+ readonly label: "Clipboard Transport";
3143
+ readonly description: "auto keeps current OSC52+native best-effort behavior. native/osc52 restrict copy to one mechanism. ssh routes text copy/paste through `ssh <clipboard.sshHost> pbcopy/pbpaste` via argv spawn and never silently falls back to native/OSC52 on failure.";
3144
+ };
3145
+ };
3146
+ readonly "clipboard.sshHost": {
3147
+ readonly type: "string";
3148
+ readonly default: "";
3149
+ readonly validate: (value: string) => boolean;
3150
+ readonly ui: {
3151
+ readonly tab: "tools";
3152
+ readonly label: "Clipboard SSH Host";
3153
+ readonly description: "SSH host alias (from ~/.ssh/config) used when clipboard.transport is ssh. Required in that mode.";
3154
+ };
3155
+ };
3136
3156
  readonly "web_search.enabled": {
3137
3157
  readonly type: "boolean";
3138
3158
  readonly default: true;
@@ -4304,6 +4324,17 @@ export type SettingsSchemaReport = {
4304
4324
  issues: SettingsSchemaIssue[];
4305
4325
  valid: boolean;
4306
4326
  };
4327
+ /**
4328
+ * Validate an external (SDK `config.patch`) path/value set against the
4329
+ * settings schema before any durable write. Dotted sub-paths of record
4330
+ * settings (e.g. `modelRoles.default`) are validated against the record's
4331
+ * value schema. Returns the offending entries so the caller can reject the
4332
+ * whole patch without durable side effects.
4333
+ */
4334
+ export declare function validateSettingPatch(patch: Record<string, unknown>): Array<{
4335
+ path: string;
4336
+ detail: string;
4337
+ }>;
4307
4338
  /** Coerce supported scalar legacy values and report unknown or invalid settings without dropping them. */
4308
4339
  export declare function reconcileSettingsSchema(raw: Record<string, unknown>): {
4309
4340
  settings: Record<string, unknown>;
@@ -18,6 +18,12 @@ interface SetModelCapableSession {
18
18
  setModel(model: Model, role?: string, options?: {
19
19
  cause?: string;
20
20
  }): Promise<unknown>;
21
+ /** Persist effective profile roles and clear its marker for a concrete default selection. */
22
+ materializeActiveDefaultModelProfileAssignment?(model: Model): boolean;
23
+ /** Drop a session-only profile marker and its runtime role overrides. */
24
+ clearSessionOnlyModelProfileState?(): void;
25
+ /** Fallback marker clear for legacy session adapters. */
26
+ setActiveModelProfile?(name: string | undefined): void;
21
27
  }
22
28
  /**
23
29
  * Helper for wiring the `setModel` action of an {@link ExtensionContext}.
@@ -283,6 +283,18 @@ export interface ExtensionContext {
283
283
  thinkingLevel: ThinkingLevel | undefined;
284
284
  } | undefined>;
285
285
  setModelProfile?(name: string): Promise<boolean>;
286
+ /** Persist a model profile as the global default (SDK host seam). */
287
+ setDefaultModelProfile?(name: string, options?: {
288
+ persistDefault?: boolean;
289
+ thinkingLevelOverride?: ThinkingLevel;
290
+ /** Internal SDK host hooks invoked inside the profile activation admission. */
291
+ onBeforeActivation?: () => void;
292
+ onAfterActivation?: () => void;
293
+ }): Promise<DefaultModelProfileActivationResult>;
294
+ /** The in-session active-profile marker; sole source of logical current state. */
295
+ getActiveModelProfile?(): string | undefined;
296
+ /** Run a control-surface mutation inside the session admission boundary. */
297
+ withSdkControlMutation?<T>(body: () => Promise<T>): Promise<T>;
286
298
  cycleThinkingLevel(): ThinkingLevel | undefined;
287
299
  setQueueMode(kind: "steering" | "follow_up" | "interrupt", mode: unknown): boolean;
288
300
  getSkillState(): unknown;
@@ -812,7 +824,7 @@ export interface ExtensionAPI {
812
824
  /** Set thinking visibility from a session or durable global control surface. */
813
825
  setThinkingVisibilityForControl(visibility: "visible" | "hidden", persist: boolean): Promise<void>;
814
826
  /** Set the model for this session only. Returns false when it is unavailable. */
815
- setModelTemporaryForControl(model: Model, expectedSessionId?: string): Promise<boolean>;
827
+ setModelTemporaryForControl(model: Model, expectedSessionId?: string, thinkingLevel?: ThinkingLevel): Promise<boolean>;
816
828
  /** Fetch provider usage through the session's canonical provider resolution. */
817
829
  fetchUsageReportsForControl(): Promise<UsageReport[] | null>;
818
830
  /** Report whether the current effort follows global config or a session override. */
@@ -965,7 +977,13 @@ export type SetModelHandler = (model: Model) => Promise<boolean>;
965
977
  export type CycleThinkingLevelHandler = () => ThinkingLevel | undefined;
966
978
  export type SetThinkingLevelForControlHandler = (level: ThinkingLevel, persist: boolean) => Promise<void>;
967
979
  export type SetThinkingVisibilityForControlHandler = (visibility: "visible" | "hidden", persist: boolean) => Promise<void>;
968
- export type SetModelTemporaryForControlHandler = (model: Model, expectedSessionId?: string) => Promise<boolean>;
980
+ export type SetModelTemporaryForControlHandler = (model: Model, expectedSessionId?: string, thinkingLevel?: ThinkingLevel) => Promise<boolean>;
981
+ /** Result of activating a model profile as the global default from a control surface. */
982
+ export interface DefaultModelProfileActivationResult {
983
+ changed: boolean;
984
+ /** The canonical (alias-resolved) profile id. */
985
+ id: string;
986
+ }
969
987
  export type FetchUsageReportsForControlHandler = () => Promise<UsageReport[] | null>;
970
988
  export type GetThinkingScopeForControlHandler = () => "session" | "global config";
971
989
  export type GetThinkingLevelHandler = () => ThinkingLevel | undefined;
@@ -1046,6 +1064,15 @@ export interface ExtensionContextActions {
1046
1064
  thinkingLevel: ThinkingLevel | undefined;
1047
1065
  } | undefined>;
1048
1066
  setModelProfile?: (name: string) => Promise<boolean>;
1067
+ setDefaultModelProfile?: (name: string, options?: {
1068
+ persistDefault?: boolean;
1069
+ thinkingLevelOverride?: ThinkingLevel;
1070
+ /** Internal SDK host hooks invoked inside the profile activation admission. */
1071
+ onBeforeActivation?: () => void;
1072
+ onAfterActivation?: () => void;
1073
+ }) => Promise<DefaultModelProfileActivationResult>;
1074
+ getActiveModelProfile?: () => string | undefined;
1075
+ withSdkControlMutation?: <T>(body: () => Promise<T>) => Promise<T>;
1049
1076
  cycleThinkingLevel?: () => ThinkingLevel | undefined;
1050
1077
  setQueueMode?: (kind: "steering" | "follow_up" | "interrupt", mode: unknown) => boolean;
1051
1078
  getSkillState?: () => unknown;
@@ -12,6 +12,7 @@
12
12
  * error all mean KEEP — a live process is never signalled or killed.
13
13
  * - Dry-run by default: nothing is deleted unless `--prune`/`--force`.
14
14
  */
15
+ import { type GcSessionScopeUsage } from "./gc-session-scope";
15
16
  export type GcStore = "harness_leases" | "team_workers" | "file_locks" | "tmux_sessions" | "registry_entries" | "local_roots";
16
17
  export declare const GC_STORES: readonly GcStore[];
17
18
  /** Why a probed pid is kept instead of treated as dead. */
@@ -119,6 +120,8 @@ export interface GcReport {
119
120
  /** Partial-result notices that do not fail the run (e.g. walk caps). */
120
121
  warnings: GcWarning[];
121
122
  session_index?: GcSessionIndexHealth;
123
+ /** Managed-scope capacity, reported only when it is near or past the budget. */
124
+ session_scope?: GcSessionScopeUsage;
122
125
  }
123
126
  export interface GcRunResult {
124
127
  stdout: string;
@@ -0,0 +1,21 @@
1
+ export type GcSessionScopeStatus = "ok" | "approaching_limit" | "over_limit" | "unavailable";
2
+ export interface GcSessionScopeUsage {
3
+ status: GcSessionScopeStatus;
4
+ /** Absolute path of the managed scope for the current working directory. */
5
+ path: string;
6
+ total_bytes: number;
7
+ limit_bytes: number;
8
+ entries: number;
9
+ /** True when the walk stopped at `MAX_WALK_ENTRIES`, so totals are a floor. */
10
+ truncated: boolean;
11
+ reason?: string;
12
+ }
13
+ /**
14
+ * Measure the managed scope directory backing `scopePath`.
15
+ *
16
+ * Never throws: an unreadable or absent scope is reported as `unavailable` so
17
+ * a capacity probe can never fail a gc run.
18
+ */
19
+ export declare function collectSessionScopeUsage(scopePath: string, limitBytes?: number): Promise<GcSessionScopeUsage>;
20
+ /** Whether the usage is worth putting in front of an operator. */
21
+ export declare function shouldReportSessionScope(usage: GcSessionScopeUsage): boolean;
@@ -249,6 +249,7 @@ export declare class AcpAgent implements Agent {
249
249
  constructor(connection: AgentSideConnection, options?: {
250
250
  agentDir?: string;
251
251
  startupOptions?: AcpStartupOptions;
252
+ cancelSettlementGraceMs?: number;
252
253
  } | unknown);
253
254
  initialize(params: InitializeRequest): Promise<InitializeResponse>;
254
255
  authenticate(params: AuthenticateRequest): Promise<AuthenticateResponse>;
@@ -35,6 +35,15 @@ export declare class InputController {
35
35
  handleForegroundToolBackgroundFold(): boolean;
36
36
  handleTextPaste(text: string, context: PasteTextContext): boolean | Promise<boolean>;
37
37
  handleImagePaste(): Promise<boolean>;
38
+ /**
39
+ * Explicit "Paste text from configured clipboard" action (P3-b v1). Only
40
+ * meaningful when `clipboard.transport` is `ssh` — reads via `pbpaste` on
41
+ * the configured host and inserts the result at the cursor. No default
42
+ * key (command palette only), so it never collides with the platform
43
+ * image-paste binding. Explicit ssh failures are surfaced verbatim and
44
+ * never fall back to native/OSC52 or leave stale text inserted.
45
+ */
46
+ handlePasteText(): Promise<boolean>;
38
47
  createAutocompleteProvider(commands: SlashCommand[], basePath: string): AutocompleteProvider;
39
48
  /** Copy the current editor line to the system clipboard. */
40
49
  handleCopyCurrentLine(): void;
@@ -14,6 +14,7 @@ interface PromptActionAutocompleteOptions {
14
14
  copyCurrentLine: () => void;
15
15
  copyPrompt: () => void;
16
16
  pasteImage: () => void;
17
+ pasteText: () => void;
17
18
  newSession: () => void;
18
19
  showHelp: () => void;
19
20
  scrollTmuxToPreviousUserInput: () => void;
@@ -1,4 +1,4 @@
1
- import { SdkClient, SdkClientError, type SdkFrame } from "../client";
1
+ import { SdkClient, SdkClientError, type SdkClientOptions, type SdkFrame } from "../client";
2
2
  type JsonObject = Record<string, unknown>;
3
3
  /** The small agent-side ACP surface used for reverse requests. */
4
4
  export interface AcpReverseConnection {
@@ -28,6 +28,21 @@ export declare class AcpSdkAdapterError extends Error {
28
28
  }
29
29
  export type AcpReconnectFailedHandler = (error: SdkClientError) => void;
30
30
  export type AcpFrameHandler = (frame: SdkFrame) => void;
31
+ type AcpSessionReconnectOptions = Required<Pick<SdkClientOptions, "reconnectAttempts" | "reconnectBackoffMs" | "reconnectMaxBackoffMs">>;
32
+ /**
33
+ * Reconnect budget for long-lived ACP sessions.
34
+ *
35
+ * Invariant: the client's total reconnect budget must outlive the host heartbeat
36
+ * TTL ({@link HEARTBEAT_TTL_MS}). The SDK host drops a session whose client has
37
+ * not ponged within that TTL, so a client that gives up reconnecting sooner turns
38
+ * every stall the host reaps into a permanently lost session ("SDK WebSocket
39
+ * reconnect attempts exhausted"). The transport defaults (3 attempts at 25ms base
40
+ * = 175ms total) are correct only for one-shot request clients.
41
+ *
42
+ * Backoff ramps 250 -> 500 -> 1000 and then holds at the 2s cap, so individual
43
+ * sleeps stay short and recovery is prompt once the host answers again.
44
+ */
45
+ export declare const ACP_SESSION_RECONNECT: AcpSessionReconnectOptions;
31
46
  /**
32
47
  * Pure ACP-to-SDK adapter. It deliberately owns neither an AgentSession nor an
33
48
  * ACP bridge: all session work is performed through authenticated v3 frames.
@@ -1,2 +1,2 @@
1
1
  export type { AcpProviderRegistration, AcpReverseConnection, AcpSdkAdapterOptions } from "./adapter";
2
- export { AcpSdkAdapter, AcpSdkAdapterError } from "./adapter";
2
+ export { ACP_SESSION_RECONNECT, AcpSdkAdapter, AcpSdkAdapterError } from "./adapter";
@@ -1,3 +1,4 @@
1
+ import { type Settings } from "../../config/settings";
1
2
  import type { ExtensionAPI, ExtensionContext } from "../../extensibility/extensions";
2
3
  import { SessionSdkHost, type SessionSdkHostOptions } from "./host";
3
4
  import { type SessionSurface } from "./query";
@@ -21,6 +22,10 @@ export interface SessionSdkTransport {
21
22
  }
22
23
  export interface SessionSdkRuntimeOptions extends Omit<SessionSdkHostOptions, "sessionId" | "stateRoot" | "token" | "sendFrame" | "onFrame"> {
23
24
  transport: SessionSdkTransport;
25
+ /** Session settings; enables `config.patch` application on this runtime. */
26
+ settings?: Settings;
27
+ /** Mutable shadow of patched config values merged into query readback. */
28
+ configOverrides?: Map<string, unknown>;
24
29
  }
25
30
  /**
26
31
  * The transport-neutral SDK session runtime.
@@ -58,6 +63,10 @@ export interface CreateSdkSessionRuntimeOptions {
58
63
  token: string;
59
64
  }): SessionSdkTransport | Promise<SessionSdkTransport>;
60
65
  onSdkRequest?: SessionSdkHostOptions["onRequest"];
66
+ /** Session settings; enables `config.patch` application on this runtime. */
67
+ settings?: Settings;
68
+ /** Mutable shadow of patched config values merged into query readback. */
69
+ configOverrides?: Map<string, unknown>;
61
70
  }
62
71
  export interface InvocationCorrelation {
63
72
  commandId: string;
@@ -93,6 +102,8 @@ export interface SdkSurfaceFactoryOptions {
93
102
  followupQueueDepth: number;
94
103
  };
95
104
  configOverrides?: ReadonlyMap<string, unknown>;
105
+ /** Session settings; used for model-usage preferences in profile-limit resolution. */
106
+ settings?: Settings;
96
107
  promptStatusLookup?: (selector: {
97
108
  commandId?: string;
98
109
  turnId?: string;
@@ -0,0 +1,58 @@
1
+ import type { ModelProfileDefinition } from "../config/model-profiles";
2
+ /**
3
+ * Reserved logical provider namespace for the preset-as-model facade.
4
+ *
5
+ * `gajae-code/<profile>` is not a real API provider: rows under this namespace
6
+ * exist only in the SDK Q10 projection and `model.set` selection routes to a
7
+ * session-scoped model-profile activation transaction. The namespace is
8
+ * reserved so a user-defined `models.yml` provider of the same name cannot
9
+ * silently shadow the facade (see {@link syntheticNamespaceCollision}).
10
+ */
11
+ export declare const SYNTHETIC_PROVIDER_ID = "gajae-code";
12
+ /**
13
+ * Display metadata used for synthetic rows whose profile default model cannot
14
+ * be resolved to a registered model. Mirrors the shared unknown-model
15
+ * constants from `@gajae-code/ai`; the real model's window is authoritative.
16
+ */
17
+ export declare const SYNTHETIC_UNKNOWN_CONTEXT_WINDOW = 222222;
18
+ export declare const SYNTHETIC_UNKNOWN_MAX_TOKENS = 8888;
19
+ export declare function isSyntheticModelId(modelId: string): boolean;
20
+ export declare function buildSyntheticModelId(profileName: string): string;
21
+ /**
22
+ * Parse a synthetic model id losslessly. Only the first namespace slash is
23
+ * consumed; the full suffix (which may itself contain slashes or punctuation,
24
+ * matching the configured-profile contract) is the profile id.
25
+ */
26
+ export declare function parseSyntheticModelId(modelId: string): {
27
+ profileName: string;
28
+ } | undefined;
29
+ /** A bounded SDK `invalid_input`-coded error for the synthetic selection branch. */
30
+ export declare function syntheticModelInputError(message: string): Error;
31
+ export interface ResolvedSyntheticModelSelection {
32
+ /** The raw profile suffix after the first namespace slash. */
33
+ profileName: string;
34
+ /** The canonical profile id (legacy aliases resolved, errors fail closed). */
35
+ canonicalName: string;
36
+ }
37
+ /**
38
+ * Canonicalize a synthetic model id against the merged profile registry.
39
+ * Profile/registry failures are converted to the SDK's `invalid_input` code so
40
+ * the ACP adapter's existing `invalid_input -> invalidParams` mapping applies.
41
+ */
42
+ export declare function resolveSyntheticModelSelection(modelId: string, profiles: ReadonlyMap<string, ModelProfileDefinition>, registryError?: unknown): ResolvedSyntheticModelSelection;
43
+ /**
44
+ * True when a real registry entry shadows the reserved logical namespace.
45
+ * Callers must omit synthetic rows (and reject synthetic selection) in that
46
+ * case instead of silently shadowing or misrouting the user's provider.
47
+ */
48
+ export declare function syntheticNamespaceCollision(models: readonly {
49
+ provider: string;
50
+ }[], configuredProviderIds?: readonly string[]): boolean;
51
+ /**
52
+ * Resolve the set of providers with usable credentials across every profile's
53
+ * strict requirements and alternative groups. This is the single availability
54
+ * derivation shared by the Q10 synthetic facade and the Q27 profile catalog so
55
+ * both surfaces agree; `kNoAuth` counts as available and per-provider lookup
56
+ * failures simply exclude that provider.
57
+ */
58
+ export declare function collectAuthenticatedProfileProviders(profiles: ReadonlyMap<string, ModelProfileDefinition>, getApiKeyForProvider: (provider: string) => Promise<string | undefined>): Promise<ReadonlySet<string>>;
@@ -1,5 +1,6 @@
1
1
  import { ThinkingLevel } from "@gajae-code/agent-core";
2
2
  import { type Api, type Effort, type Model, type ThinkingControlMode } from "@gajae-code/ai/core";
3
+ import { type ModelProfileDefinition } from "../config/model-profiles";
3
4
  export type Q10ThinkingEffort = Effort;
4
5
  export type Q10SettableThinkingLevel = typeof ThinkingLevel.Off | Q10ThinkingEffort;
5
6
  export type Q10CurrentThinkingLevel = Q10SettableThinkingLevel | typeof ThinkingLevel.Inherit;
@@ -36,6 +37,19 @@ export interface Q10ModelProjectionInput {
36
37
  currentModel?: Model<Api>;
37
38
  currentThinkingLevel?: Q10CurrentThinkingLevel;
38
39
  resolveSupportedEfforts?: (model: Model<Api>) => readonly Effort[];
40
+ /**
41
+ * Logical model-profile facade inputs. When supplied, deterministic,
42
+ * availability-filtered synthetic rows (`gajae-code/<profile>`) are
43
+ * appended after the real rows; the underlying `Model` registry entries
44
+ * are never touched.
45
+ */
46
+ profiles?: ReadonlyMap<string, ModelProfileDefinition>;
47
+ /** Profile ids whose providers are authenticated and therefore selectable. */
48
+ availableProfileIds?: ReadonlySet<string>;
49
+ /** Active in-session profile marker; its synthetic row is the logical current. */
50
+ activeProfile?: string;
51
+ /** Resolve a profile's default concrete model for display metadata. */
52
+ resolveProfileDefaultModel?: (profile: ModelProfileDefinition) => Model<Api> | undefined;
39
53
  }
40
54
  /**
41
55
  * Projects model registry entries into the Q10 public DTO without exposing
@@ -318,5 +318,18 @@ export interface BuildSystemPromptOptions {
318
318
  export declare function buildSystemPrompt(options?: BuildSystemPromptOptions): Promise<BuildSystemPromptResult>;
319
319
  export { createAppendOnlyContextManager, providerSupportsAppendOnlyAuto, resolveAppendOnlyMode };
320
320
  export declare function createPluginHooksExtension(hooks: ConstrainedPluginHook[]): ExtensionFactory;
321
- export declare function resolveIntentTracingEnabled(intentTracingSetting: boolean | undefined, hasUI: boolean): boolean;
321
+ /**
322
+ * Intent tracing (`_i`) is a model-facing reasoning aid, not a UI feature: it
323
+ * makes the model state a tool call's purpose before the call executes, and the
324
+ * loop turns that string into `tool_execution_start.intent` for every consumer
325
+ * (TUI transcript, ACP `tool_call.title`, telemetry, session dumps).
326
+ *
327
+ * The per-call token cost is only worth forcing on the operator-facing session,
328
+ * so canonical sub-sessions (role agents spawned through `task`) stay omitted.
329
+ * Surface shape must not decide this — an ACP or print-mode top-level session is
330
+ * the same conversation the operator would have run in the TUI.
331
+ */
332
+ export declare function resolveIntentTracingEnabled(intentTracingSetting: boolean | undefined, options: {
333
+ subSession: boolean;
334
+ }): boolean;
322
335
  export declare function createAgentSession(options?: CreateAgentSessionOptions): Promise<CreateAgentSessionResult>;