@gajae-code/coding-agent 0.4.5 → 0.5.0

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 (87) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/dist/types/commands/harness.d.ts +3 -0
  3. package/dist/types/config/model-profile-activation.d.ts +11 -2
  4. package/dist/types/config/model-profiles.d.ts +7 -0
  5. package/dist/types/config/model-registry.d.ts +3 -0
  6. package/dist/types/config/model-resolver.d.ts +2 -0
  7. package/dist/types/config/models-config-schema.d.ts +30 -0
  8. package/dist/types/config/settings-schema.d.ts +4 -3
  9. package/dist/types/gjc-runtime/team-runtime.d.ts +0 -1
  10. package/dist/types/gjc-runtime/tmux-common.d.ts +3 -0
  11. package/dist/types/harness-control-plane/owner.d.ts +1 -1
  12. package/dist/types/harness-control-plane/receipt-spool.d.ts +19 -0
  13. package/dist/types/harness-control-plane/state-machine.d.ts +6 -1
  14. package/dist/types/harness-control-plane/types.d.ts +4 -0
  15. package/dist/types/hindsight/mental-models.d.ts +5 -5
  16. package/dist/types/modes/components/model-selector.d.ts +1 -12
  17. package/dist/types/modes/rpc/rpc-client.d.ts +2 -2
  18. package/dist/types/modes/rpc/rpc-types.d.ts +4 -1
  19. package/dist/types/sdk.d.ts +5 -0
  20. package/dist/types/session/agent-session.d.ts +2 -0
  21. package/dist/types/session/blob-store.d.ts +20 -1
  22. package/dist/types/session/session-manager.d.ts +24 -6
  23. package/dist/types/session/streaming-output.d.ts +3 -2
  24. package/dist/types/session/tool-choice-queue.d.ts +6 -0
  25. package/dist/types/task/receipt.d.ts +1 -0
  26. package/dist/types/task/types.d.ts +7 -0
  27. package/dist/types/thinking-metadata.d.ts +16 -0
  28. package/dist/types/thinking.d.ts +3 -12
  29. package/dist/types/tools/index.d.ts +2 -0
  30. package/dist/types/tools/resolve.d.ts +0 -10
  31. package/dist/types/utils/tool-choice.d.ts +14 -1
  32. package/package.json +7 -7
  33. package/src/cli.ts +8 -4
  34. package/src/commands/harness.ts +36 -2
  35. package/src/commands/launch.ts +2 -2
  36. package/src/commands/session.ts +3 -1
  37. package/src/config/model-profile-activation.ts +15 -3
  38. package/src/config/model-profiles.ts +255 -56
  39. package/src/config/model-resolver.ts +9 -6
  40. package/src/config/models-config-schema.ts +1 -0
  41. package/src/config/settings-schema.ts +6 -3
  42. package/src/coordinator-mcp/server.ts +54 -23
  43. package/src/cursor.ts +16 -2
  44. package/src/defaults/gjc/skills/team/SKILL.md +3 -2
  45. package/src/defaults/gjc/skills/ultragoal/SKILL.md +8 -2
  46. package/src/export/html/index.ts +13 -9
  47. package/src/gjc-runtime/team-runtime.ts +33 -7
  48. package/src/gjc-runtime/tmux-common.ts +15 -0
  49. package/src/gjc-runtime/tmux-sessions.ts +19 -11
  50. package/src/gjc-runtime/ultragoal-runtime.ts +505 -41
  51. package/src/gjc-runtime/workflow-manifest.generated.json +27 -1
  52. package/src/gjc-runtime/workflow-manifest.ts +16 -1
  53. package/src/harness-control-plane/owner.ts +78 -27
  54. package/src/harness-control-plane/receipt-spool.ts +128 -0
  55. package/src/harness-control-plane/state-machine.ts +27 -6
  56. package/src/harness-control-plane/storage.ts +23 -0
  57. package/src/harness-control-plane/types.ts +4 -0
  58. package/src/hindsight/mental-models.ts +17 -16
  59. package/src/internal-urls/docs-index.generated.ts +2 -2
  60. package/src/modes/components/assistant-message.ts +26 -14
  61. package/src/modes/components/diff.ts +97 -0
  62. package/src/modes/components/model-selector.ts +353 -181
  63. package/src/modes/components/tool-execution.ts +30 -13
  64. package/src/modes/controllers/selector-controller.ts +33 -42
  65. package/src/modes/rpc/rpc-client.ts +3 -2
  66. package/src/modes/rpc/rpc-mode.ts +44 -14
  67. package/src/modes/rpc/rpc-types.ts +5 -2
  68. package/src/modes/shared/agent-wire/command-dispatch.ts +10 -5
  69. package/src/modes/shared/agent-wire/command-validation.ts +11 -0
  70. package/src/sdk.ts +29 -2
  71. package/src/secrets/obfuscator.ts +102 -27
  72. package/src/session/agent-session.ts +105 -20
  73. package/src/session/blob-store.ts +89 -3
  74. package/src/session/session-manager.ts +309 -58
  75. package/src/session/streaming-output.ts +185 -122
  76. package/src/session/tool-choice-queue.ts +23 -0
  77. package/src/task/executor.ts +69 -6
  78. package/src/task/receipt.ts +5 -0
  79. package/src/task/render.ts +21 -1
  80. package/src/task/types.ts +8 -0
  81. package/src/thinking-metadata.ts +51 -0
  82. package/src/thinking.ts +26 -46
  83. package/src/tools/bash.ts +1 -1
  84. package/src/tools/index.ts +2 -0
  85. package/src/tools/resolve.ts +93 -18
  86. package/src/utils/edit-mode.ts +1 -1
  87. package/src/utils/tool-choice.ts +45 -16
package/CHANGELOG.md CHANGED
@@ -2,6 +2,48 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.5.0] - 2026-06-13
6
+
7
+ ### Fixed
8
+
9
+ - Fixed forced `tool_choice` 400s ("tool_choice forces tool use is not compatible with this model") looping after `ast_edit` previews: named queue directives (resolve protocol, eager `todo_write` enforcement, subagent `yield` reminders) now enqueue only when the model supports exact named forcing; otherwise they degrade silently to the existing steer reminder without a forced `tool_choice`, and a runtime-discovered incapability drops the in-flight directive instead of requeueing it.
10
+ - `models.yml` compat blocks now accept the `toolChoiceSupport` enum (`none`/`auto`/`required`/`named`) alongside the legacy `supportsToolChoice`/`supportsForcedToolChoice` booleans, mirrored in the generated JSON schema.
11
+ ### Added
12
+
13
+ - Made `/model` open to a preset-first landing view: provider-grouped presets with live auth checkmarks, highlight-to-expand tiers, a full clamped role→model preview before applying, and a session/default apply scope choice; typing still jumps straight to model search, "Browse all models" opens the classic tabbed selector, and temporary-only quick-switch bypasses the landing entirely.
14
+ - Rebuilt the builtin model profile catalog as 25 profiles: `codex-{eco,medium,pro}` on `gpt-5.5` effort spreads, a single `opencodego` preset, `claude-opus`, `{glm,kimi-coding-plan,mimo,grok,cursor,minimax}-{eco,medium,pro}` trios with thinking levels clamped to provider support, and `opus-codex`/`codex-opencodego` combos. Legacy profile names (including the `*-standard` family and retired Fable presets) were removed clean-break and now fail with the available-profile listing.
15
+ - Added a post-`/login` smart preset recommendation: when login succeeds and no profile is active, prompts "Apply <preset> now?" (session-only on confirm); when a profile is active, prints a one-line hint instead. The active profile is tracked in-memory on the session with rollback-safe activation.
16
+ - Bundled `kimi-code/kimi-k2.7-code` and `minimax-code/minimax-v3` model entries; MiniMax presets use the canonical `minimax-code` provider id throughout.
17
+ - Added a harness receipt JSONL spool exporter for gajae receipt-runtime interop: configured `gjc harness --receipt-spool-dir <dir>` / `GJC_RECEIPT_SPOOL_DIR` now appends persisted native `ReceiptEnvelope` records as `{cursor,envelope}` lines to `spool.jsonl`, with restart-safe 12-digit cursors and installed-package smoke coverage (#545).
18
+ - Added Gajae Trinity compatibility golden fixtures and tests that pin ReceiptEnvelope hash basis, validator compatibility, and replayable RPC exchange shape for downstream receipt-runtime interop.
19
+ - Optimization Suite v3 Lane 1 (RSS): large resident text in persisted sessions is now backed by an ephemeral session-scoped disk cache (`EphemeralBlobStore`) instead of being pinned in JS heap for the whole session lifetime; canonical JSONL persistence, reload, and export semantics are byte-identical (resident refs never persist). Missing resident text cache blobs now surface a typed `ResidentBlobMissingError` instead of silently leaking `blob:sha256:` refs into provider payloads, UI, or exports. `getEntries()`/`buildSessionContext()` are served from revision-keyed WeakRef caches below the public ownership boundary (callers still receive caller-owned copies). Fixture retained heap −82%, RSS −55%, warm `getEntries()` p95 −80% on 10k-entry sessions; one-shot `exportFromFile()` now closes its session manager.
20
+ - Added process-isolated deterministic TUI render-golden capture and fixtures for interactive editor overlays, rich-text resizing, multiplexer viewport repaint, sixel image line preservation, Termux height diffs, and transcript shrink/clear regressions.
21
+
22
+ ### Removed
23
+
24
+ - Removed the hardcoded OpenAI Codex role-preset action from the model selector; builtin model profiles are now the only preset concept.
25
+ - Removed retired Fable model profiles (`claude-fable`, `fable-codex`) after `claude-fable-5` was removed upstream.
26
+
27
+ ### Changed
28
+
29
+ - Optimization Suite v3 Lane 3 (serialization): session-switch message comparison now uses per-message cached source strings + xxHash64 as an accelerator (source-string compare remains the authority; collision fallback tested) — unchanged-session compares −95% median. The secret obfuscator precomputes a longest-first combined regex (single-pass replace, −70% median/−77% p95 on 100 secrets × 1MiB) with a conservative sequential fallback whenever secrets overlap each other or any replacement/placeholder contains a secret — output bytes are identical in all cases. Intra-line diff rendering gains byte-identical fast paths for identical lines and whitespace-token-aligned prefix/suffix spans (identical −67%, single-token −60%; long lines skip the scan). Mental-model LCS keeps legacy dense-DP tie-break semantics (a Hunt-Szymanski variant was rejected for changing rendered bytes). Provider-visible fork-context seeds use JSON-semantic cloning instead of structuredClone.
30
+ - Tightened tool-block rendering to remove vertical padding and rely on Spacer-only separation, reducing transcript noise while preserving stable render-golden output.
31
+ - Improved the Bun runtime version guard diagnostic: when the Bun running `gjc` is older than the required version, the error now names the exact detected Bun runtime path and prints a platform-specific upgrade and PATH fix (Windows gets the `irm bun.sh/install.ps1|iex` reinstall plus a `%USERPROFILE%\.bun\bin` PATH hint) instead of a bare `bun upgrade` (#525).
32
+ - Aligned the `codex-standard` and `codex-pro` model profiles on the `openai-codex/gpt-5.5` baseline so they no longer default to stale mixed model generations (`gpt-5.4`, `gpt-5.2-codex`, `gpt-5.1-codex-max`, `gpt-5.3-codex-spark`); the profiles now differentiate purely by per-role reasoning effort (#532).
33
+ - Reduced the default RPC `get_state` payload by omitting static `dumpTools` and `systemPrompt` fields unless requested via `include: ["tools", "systemPrompt"]` (#539).
34
+ - Updated `/model` documentation and generated docs index for the rebuilt preset catalog and preset-first selector.
35
+
36
+ ### Fixed
37
+
38
+ - Tightened the Windows/psmux tmux provider boundary: `gjc team` now honors `GJC_TMUX_COMMAND` (not just `GJC_TEAM_TMUX_COMMAND`) so the team leader resolves the same multiplexer as `gjc session`/`gjc --tmux`; and when a multiplexer lists a session that lacks GJC's `@gjc-profile` ownership tag, `gjc session status` now returns `gjc_tmux_session_untagged` with a `detail` hint and `gjc team` reports the same cause, instead of a bare `gjc_tmux_session_not_found` / `unmanaged_tmux_session`. Documented that alternative multiplexers such as psmux on Windows are not fully supported because they do not round-trip tmux user options (#531).
39
+ - Hardened RPC stdio lifecycle behavior: `gjc --mode rpc` now reports malformed JSONL frames as parse-error responses without killing the session, flushes durable session state before exiting on EOF/shutdown, and has red-team coverage for attached persistence, reload, malformed-frame recovery, and concurrent child-session isolation.
40
+ - Hardened the harness RPC submit/router contract so `submit` is no longer advertised or accepted during finalizing/non-idle lifecycle windows, non-idle RPC state reports `submitted:false` with a retryable gate, and degraded owner endpoints fall back to `owner-not-live` without false acceptance (#544).
41
+ - Ran estimated context maintenance before sending a new prompt, including tool-output pruning and threshold compaction, so large tool results appended after the last assistant turn cannot push the next model request over the context window.
42
+ - `gjc team` now self-heals a missing `@gjc-profile` ownership tag when the current leader pane was genuinely launched by `gjc --tmux` (detected via `GJC_TMUX_LAUNCHED=1`): the session is re-tagged with `set-option` and startup proceeds, instead of hard-failing with `unmanaged_tmux_session` after a mid-startup attach failure or registry race stripped the tag. Sessions without the GJC launch marker are still rejected unchanged, so foreign tmux sessions cannot be hijacked.
43
+ - Subagent task receipts and live render output now warn when requested role-agent models are substituted by auth fallback or provider-reported assistant model mismatch, including session model-change annotations for server-side substitutions (#559).
44
+ - Converted Cursor wire shell timeouts from millisecond values to bash-tool seconds so delegated Cursor-native shell calls honor the expected timeout units.
45
+ - Fixed pi-shell bash fixups on multibyte UTF-8 commands by converting parser source indexes to byte offsets before stripping `head`/`tail` pipelines.
46
+
5
47
  ## [0.4.5] - 2026-06-12
6
48
 
7
49
  ### Added
@@ -17,6 +59,7 @@
17
59
  - Reduced compiled CLI startup and native bundle pressure with default-small grammar loading, tokenizer tiering, and compiled fast-help paths.
18
60
  - Preserved dev/main release metadata and changelog consistency for the 0.4.5 lockstep release.
19
61
 
62
+ - Added native `gjc ultragoal steer --kind` support for documented steering mutations beyond `add_subgoal`, including split, reorder, wording revision, ledger annotation, and blocked-goal supersession contracts with structured audit expectations.
20
63
  ### Fixed
21
64
 
22
65
  - Kept the unified `goal` tool registered and active by default whenever `goal.enabled` is true, including explicit tool subsets and `gjc ultragoal create-goals` arming flows.
@@ -34,6 +34,9 @@ export default class Harness extends Command {
34
34
  description: string;
35
35
  default: boolean;
36
36
  };
37
+ "receipt-spool-dir": import("@gajae-code/utils/cli").FlagDescriptor<"string"> & {
38
+ description: string;
39
+ };
37
40
  };
38
41
  static examples: string[];
39
42
  run(): Promise<void>;
@@ -3,15 +3,22 @@ import type { Api, Model } from "@gajae-code/ai";
3
3
  import type { AgentSession } from "../session/agent-session";
4
4
  import { type ModelRegistry } from "./model-registry";
5
5
  import type { Settings } from "./settings";
6
+ type ModelProfileActivationSession = Pick<AgentSession, "model" | "thinkingLevel" | "sessionId"> & {
7
+ setModelTemporary?: AgentSession["setModelTemporary"];
8
+ setActiveModelProfile?: (name: string | undefined) => void;
9
+ getActiveModelProfile?: () => string | undefined;
10
+ };
6
11
  export interface PrepareModelProfileActivationOptions {
7
- session: Pick<AgentSession, "model" | "thinkingLevel" | "sessionId">;
12
+ session: ModelProfileActivationSession;
8
13
  modelRegistry: Pick<ModelRegistry, "getModelProfile" | "getModelProfiles" | "getAvailableModelProfileNames" | "getApiKeyForProvider" | "getAll" | "resolveCanonicalModel" | "getCanonicalVariants" | "getCanonicalId">;
9
14
  settings: Pick<Settings, "get">;
10
15
  profileName: string;
11
16
  }
12
17
  export interface PreparedModelProfileActivation {
13
18
  profileName: string;
14
- session: Pick<AgentSession, "model" | "thinkingLevel" | "sessionId" | "setModelTemporary">;
19
+ session: ModelProfileActivationSession & {
20
+ setModelTemporary: AgentSession["setModelTemporary"];
21
+ };
15
22
  settings: Pick<Settings, "get" | "override" | "set" | "flush">;
16
23
  previousModel: Model<Api> | undefined;
17
24
  previousThinkingLevel: ThinkingLevel | undefined;
@@ -19,6 +26,7 @@ export interface PreparedModelProfileActivation {
19
26
  defaultModel: Model<Api> | undefined;
20
27
  defaultThinkingLevel: ThinkingLevel | undefined;
21
28
  agentModelOverrides: Record<string, string>;
29
+ previousActiveModelProfile: string | undefined;
22
30
  }
23
31
  export declare function formatModelProfileCredentialError(profileName: string, providers: readonly string[]): string;
24
32
  export declare function prepareModelProfileActivation(options: PrepareModelProfileActivationOptions): Promise<PreparedModelProfileActivation>;
@@ -28,3 +36,4 @@ export declare function applyPreparedModelProfileActivation(prepared: PreparedMo
28
36
  export declare function activateModelProfile(options: PrepareModelProfileActivationOptions, applyOptions?: {
29
37
  persistDefault?: boolean;
30
38
  }): Promise<void>;
39
+ export {};
@@ -14,6 +14,13 @@ export interface ResolvedProfileBinding {
14
14
  export declare function deriveModelProfileMappedProviders(definition: Pick<ModelProfileDefinition, "modelMapping">): string[];
15
15
  export declare function aggregateModelProfileRequiredProviders(requiredProviders: readonly string[], definition: Pick<ModelProfileDefinition, "modelMapping">): string[];
16
16
  export declare const BUILTIN_MODEL_PROFILES: readonly ModelProfileDefinition[];
17
+ export interface ModelProfilePresentation {
18
+ displayName: string;
19
+ providerGroup: string;
20
+ }
21
+ export declare function getModelProfilePresentation(name: string): ModelProfilePresentation;
22
+ export declare function groupModelProfilesForPresetLanding(profiles: ReadonlyMap<string, ModelProfileDefinition>): Map<string, ModelProfileDefinition[]>;
23
+ export declare function recommendModelProfileForProvider(providerId: string, profiles: ReadonlyMap<string, ModelProfileDefinition>): ModelProfileDefinition | undefined;
17
24
  export declare function mergeModelProfiles(userProfiles?: ModelsConfig["profiles"]): Map<string, ModelProfileDefinition>;
18
25
  export declare function resolveProfileBindings(definition: ModelProfileDefinition): ResolvedProfileBinding;
19
26
  export declare function formatAvailableProfileNames(profiles: ReadonlyMap<string, ModelProfileDefinition>): string;
@@ -71,6 +71,7 @@ export declare const ModelsConfigFile: ConfigFile<{
71
71
  requiresAssistantContentForToolCalls?: boolean | undefined;
72
72
  supportsToolChoice?: boolean | undefined;
73
73
  supportsForcedToolChoice?: boolean | undefined;
74
+ toolChoiceSupport?: "auto" | "named" | "none" | "required" | undefined;
74
75
  disableReasoningOnForcedToolChoice?: boolean | undefined;
75
76
  disableReasoningOnToolChoice?: boolean | undefined;
76
77
  thinkingFormat?: "openai" | "openrouter" | "qwen" | "qwen-chat-template" | "zai" | undefined;
@@ -147,6 +148,7 @@ export declare const ModelsConfigFile: ConfigFile<{
147
148
  requiresAssistantContentForToolCalls?: boolean | undefined;
148
149
  supportsToolChoice?: boolean | undefined;
149
150
  supportsForcedToolChoice?: boolean | undefined;
151
+ toolChoiceSupport?: "auto" | "named" | "none" | "required" | undefined;
150
152
  disableReasoningOnForcedToolChoice?: boolean | undefined;
151
153
  disableReasoningOnToolChoice?: boolean | undefined;
152
154
  thinkingFormat?: "openai" | "openrouter" | "qwen" | "qwen-chat-template" | "zai" | undefined;
@@ -219,6 +221,7 @@ export declare const ModelsConfigFile: ConfigFile<{
219
221
  requiresAssistantContentForToolCalls?: boolean | undefined;
220
222
  supportsToolChoice?: boolean | undefined;
221
223
  supportsForcedToolChoice?: boolean | undefined;
224
+ toolChoiceSupport?: "auto" | "named" | "none" | "required" | undefined;
222
225
  disableReasoningOnForcedToolChoice?: boolean | undefined;
223
226
  disableReasoningOnToolChoice?: boolean | undefined;
224
227
  thinkingFormat?: "openai" | "openrouter" | "qwen" | "qwen-chat-template" | "zai" | undefined;
@@ -133,6 +133,8 @@ export declare function resolveModelOverrideWithAuthFallback(modelPatterns: stri
133
133
  thinkingLevel?: ThinkingLevel;
134
134
  explicitThinkingLevel: boolean;
135
135
  authFallbackUsed: boolean;
136
+ requestedModel?: Model<Api>;
137
+ fallbackReason?: "auth_unavailable";
136
138
  }>;
137
139
  /**
138
140
  * Resolve a list of role patterns to the first matching model.
@@ -31,6 +31,12 @@ export declare const OpenAICompatSchema: z.ZodObject<{
31
31
  requiresAssistantContentForToolCalls: z.ZodOptional<z.ZodBoolean>;
32
32
  supportsToolChoice: z.ZodOptional<z.ZodBoolean>;
33
33
  supportsForcedToolChoice: z.ZodOptional<z.ZodBoolean>;
34
+ toolChoiceSupport: z.ZodOptional<z.ZodEnum<{
35
+ auto: "auto";
36
+ named: "named";
37
+ none: "none";
38
+ required: "required";
39
+ }>>;
34
40
  disableReasoningOnForcedToolChoice: z.ZodOptional<z.ZodBoolean>;
35
41
  disableReasoningOnToolChoice: z.ZodOptional<z.ZodBoolean>;
36
42
  thinkingFormat: z.ZodOptional<z.ZodEnum<{
@@ -184,6 +190,12 @@ export declare const ModelOverrideSchema: z.ZodObject<{
184
190
  requiresAssistantContentForToolCalls: z.ZodOptional<z.ZodBoolean>;
185
191
  supportsToolChoice: z.ZodOptional<z.ZodBoolean>;
186
192
  supportsForcedToolChoice: z.ZodOptional<z.ZodBoolean>;
193
+ toolChoiceSupport: z.ZodOptional<z.ZodEnum<{
194
+ auto: "auto";
195
+ named: "named";
196
+ none: "none";
197
+ required: "required";
198
+ }>>;
187
199
  disableReasoningOnForcedToolChoice: z.ZodOptional<z.ZodBoolean>;
188
200
  disableReasoningOnToolChoice: z.ZodOptional<z.ZodBoolean>;
189
201
  thinkingFormat: z.ZodOptional<z.ZodEnum<{
@@ -291,6 +303,12 @@ export declare const ModelsConfigSchema: z.ZodObject<{
291
303
  requiresAssistantContentForToolCalls: z.ZodOptional<z.ZodBoolean>;
292
304
  supportsToolChoice: z.ZodOptional<z.ZodBoolean>;
293
305
  supportsForcedToolChoice: z.ZodOptional<z.ZodBoolean>;
306
+ toolChoiceSupport: z.ZodOptional<z.ZodEnum<{
307
+ auto: "auto";
308
+ named: "named";
309
+ none: "none";
310
+ required: "required";
311
+ }>>;
294
312
  disableReasoningOnForcedToolChoice: z.ZodOptional<z.ZodBoolean>;
295
313
  disableReasoningOnToolChoice: z.ZodOptional<z.ZodBoolean>;
296
314
  thinkingFormat: z.ZodOptional<z.ZodEnum<{
@@ -446,6 +464,12 @@ export declare const ModelsConfigSchema: z.ZodObject<{
446
464
  requiresAssistantContentForToolCalls: z.ZodOptional<z.ZodBoolean>;
447
465
  supportsToolChoice: z.ZodOptional<z.ZodBoolean>;
448
466
  supportsForcedToolChoice: z.ZodOptional<z.ZodBoolean>;
467
+ toolChoiceSupport: z.ZodOptional<z.ZodEnum<{
468
+ auto: "auto";
469
+ named: "named";
470
+ none: "none";
471
+ required: "required";
472
+ }>>;
449
473
  disableReasoningOnForcedToolChoice: z.ZodOptional<z.ZodBoolean>;
450
474
  disableReasoningOnToolChoice: z.ZodOptional<z.ZodBoolean>;
451
475
  thinkingFormat: z.ZodOptional<z.ZodEnum<{
@@ -580,6 +604,12 @@ export declare const ModelsConfigSchema: z.ZodObject<{
580
604
  requiresAssistantContentForToolCalls: z.ZodOptional<z.ZodBoolean>;
581
605
  supportsToolChoice: z.ZodOptional<z.ZodBoolean>;
582
606
  supportsForcedToolChoice: z.ZodOptional<z.ZodBoolean>;
607
+ toolChoiceSupport: z.ZodOptional<z.ZodEnum<{
608
+ auto: "auto";
609
+ named: "named";
610
+ none: "none";
611
+ required: "required";
612
+ }>>;
583
613
  disableReasoningOnForcedToolChoice: z.ZodOptional<z.ZodBoolean>;
584
614
  disableReasoningOnToolChoice: z.ZodOptional<z.ZodBoolean>;
585
615
  thinkingFormat: z.ZodOptional<z.ZodEnum<{
@@ -1,3 +1,4 @@
1
+ import type { Effort } from "@gajae-code/ai/model-thinking";
1
2
  import { type SkillDiscoverySettings } from "./skill-settings-defaults";
2
3
  /** Unified settings schema - single source of truth for all settings.
3
4
  *
@@ -697,13 +698,13 @@ export declare const SETTINGS_SCHEMA: {
697
698
  };
698
699
  readonly defaultThinkingLevel: {
699
700
  readonly type: "enum";
700
- readonly values: readonly import("@gajae-code/ai").Effort[];
701
- readonly default: "high";
701
+ readonly values: readonly Effort[];
702
+ readonly default: Effort;
702
703
  readonly ui: {
703
704
  readonly tab: "model";
704
705
  readonly label: "Thinking Level";
705
706
  readonly description: "Reasoning depth for thinking-capable models";
706
- readonly options: readonly import("../thinking").ThinkingLevelMetadata[];
707
+ readonly options: readonly import("../thinking-metadata").ThinkingLevelMetadata[];
707
708
  };
708
709
  };
709
710
  readonly hideThinkingBlock: {
@@ -275,7 +275,6 @@ export declare function resolveGjcTeamStateRoot(cwd?: string, env?: NodeJS.Proce
275
275
  export declare function persistGjcTeamModeStateSummary(snapshot: GjcTeamSnapshot, cwd?: string): Promise<void>;
276
276
  export declare function recoverGjcTeamStaleClaims(teamName: string, cwd?: string, env?: NodeJS.ProcessEnv): Promise<GjcTeamLivenessRecoveryResult>;
277
277
  type GjcTeamTaskMetadataInput = Partial<Pick<GjcTeamTask, "owner" | "lane" | "required_role" | "allowed_roles" | "depends_on" | "blocked_by">>;
278
- export declare function resolveGjcTmuxCommand(env?: NodeJS.ProcessEnv): string;
279
278
  export declare function resolveGjcWorkerCommand(cwd?: string, env?: NodeJS.ProcessEnv): string;
280
279
  export type GjcWorkerCheckpointClassification = {
281
280
  kind: "clean";
@@ -21,6 +21,9 @@ export interface TmuxCommandResult {
21
21
  export type TmuxCommandRunner = (args: string[]) => TmuxCommandResult;
22
22
  export declare function envDisabled(value: string | undefined): boolean;
23
23
  export declare function resolveGjcTmuxCommand(env?: NodeJS.ProcessEnv): string;
24
+ export declare const GJC_TMUX_UNTAGGED_REASON = "gjc_tmux_session_untagged";
25
+ export declare function buildGjcTmuxUntaggedSessionHint(tmuxCommand: string): string;
26
+ export declare function buildGjcTmuxUntaggedSessionError(sessionName: string, tmuxCommand: string): string;
24
27
  export declare function sanitizeTmuxToken(value: string): string;
25
28
  export declare function buildGjcTmuxSessionSlug(value: string): string;
26
29
  export declare function buildGjcTmuxSessionName(env?: NodeJS.ProcessEnv, context?: {
@@ -11,7 +11,7 @@
11
11
  * Stateless `gjc harness` CLI calls reach the owner via {@link resolveOwner} + the endpoint.
12
12
  */
13
13
  import { type FinalizeChecks, type ValidationCommandSpec } from "./finalize";
14
- import type { HarnessRpc } from "./rpc-adapter";
14
+ import { type HarnessRpc } from "./rpc-adapter";
15
15
  import { type SessionLease } from "./session-lease";
16
16
  export interface OwnerOptions {
17
17
  root: string;
@@ -0,0 +1,19 @@
1
+ import type { ReceiptEnvelope } from "./receipts";
2
+ export declare const RECEIPT_SPOOL_DIR_ENV = "GJC_RECEIPT_SPOOL_DIR";
3
+ export declare const RECEIPT_SPOOL_FILENAME = "spool.jsonl";
4
+ export declare const RECEIPT_SPOOL_CURSOR_WIDTH = 12;
5
+ export interface ReceiptSpoolRecord {
6
+ cursor: string;
7
+ envelope: ReceiptEnvelope<unknown>;
8
+ }
9
+ export interface ReceiptSpoolAppendResult {
10
+ cursor: string;
11
+ path: string;
12
+ }
13
+ export declare function withReceiptSpoolDir<T>(spoolDir: string, fn: () => Promise<T>): Promise<T>;
14
+ export declare function resolveReceiptSpoolDir(env?: NodeJS.ProcessEnv): string | undefined;
15
+ export declare function receiptSpoolPath(spoolDir: string): string;
16
+ export declare function formatReceiptSpoolCursor(cursor: bigint): string;
17
+ export declare function readHighestReceiptSpoolCursor(spoolDir: string): Promise<bigint>;
18
+ export declare function appendReceiptToSpool(spoolDir: string, envelope: ReceiptEnvelope<unknown>): Promise<ReceiptSpoolAppendResult>;
19
+ export declare function appendReceiptToConfiguredSpool(envelope: ReceiptEnvelope<unknown>, env?: NodeJS.ProcessEnv): Promise<ReceiptSpoolAppendResult | undefined>;
@@ -9,11 +9,16 @@ import type { HarnessLifecycle, NextAllowedAction, PrimitiveResponse, SessionSta
9
9
  export declare function isTerminal(lifecycle: HarnessLifecycle): boolean;
10
10
  export declare function canTransition(from: HarnessLifecycle, to: HarnessLifecycle): boolean;
11
11
  export declare function assertTransition(from: HarnessLifecycle, to: HarnessLifecycle): void;
12
+ export interface NextAllowedActionsOptions {
13
+ /** Additional live-owner/RPC readiness gate for submit, e.g. rpc-not-idle. */
14
+ submitUnavailableReason?: string | null;
15
+ }
16
+ export declare function submitUnavailableReason(lifecycle: HarnessLifecycle, ownerLive: boolean, gateReason?: string | null): string | null;
12
17
  /**
13
18
  * Derive the permitted next actions for a session given its lifecycle and whether
14
19
  * a live owner currently holds the lease.
15
20
  */
16
- export declare function nextAllowedActions(lifecycle: HarnessLifecycle, ownerLive: boolean): NextAllowedAction[];
21
+ export declare function nextAllowedActions(lifecycle: HarnessLifecycle, ownerLive: boolean, options?: NextAllowedActionsOptions): NextAllowedAction[];
17
22
  export declare function buildStateView(state: SessionState, ownerLive: boolean): SessionStateView;
18
23
  /** Build the universal contract response carried by every primitive. */
19
24
  export declare function buildResponse<E extends Record<string, unknown>>(state: SessionState, ownerLive: boolean, evidence: E, ok?: boolean): PrimitiveResponse<E>;
@@ -138,6 +138,10 @@ export interface Observation {
138
138
  rpcLive?: boolean;
139
139
  /** ISO timestamp of the most recent RPC frame the owner observed, if any. */
140
140
  rpcLastFrameAt?: string | null;
141
+ /** True only when owner/rpc/lifecycle gates indicate a prompt can be submitted now. */
142
+ readyForSubmit?: boolean;
143
+ /** Present when readyForSubmit is false; mirrors submit's nextAllowedActions reason. */
144
+ submitUnavailableReason?: string | null;
141
145
  }
142
146
  /** Input to the deterministic recovery classifier. */
143
147
  export interface ClassifyInput {
@@ -108,12 +108,12 @@ export declare function summarizeMentalModel(model: MentalModelSummary): string;
108
108
  * snapshot only; the diff is computed locally for display purposes.
109
109
  *
110
110
  * This is intentionally minimal — for "what changed" at a glance, not for a
111
- * full structural diff. Each side is capped at `MAX_LCS_LINES` lines BEFORE
112
- * the O(n*m) LCS table is built so a long curated model can never hang the
113
- * TUI; output is then capped at `maxLines` so the rendered diff stays
114
- * readable. The cap is signalled inline.
111
+ * full structural diff. Each side is capped at `MAX_LCS_LINES` lines before
112
+ * the Hunt-Szymanski LCS pass so a long curated model can never hang the TUI;
113
+ * output is then capped at `maxLines` so the rendered diff stays readable. The
114
+ * cap is signalled inline.
115
115
  */
116
- /** Hard cap on input line count per side before LCS. Keeps the O(n*m) table tractable. */
116
+ /** Hard cap on input line count per side before LCS. Keeps worst-case repeated-line matching bounded. */
117
117
  export declare const MAX_LCS_LINES = 1000;
118
118
  export declare function diffMentalModelContent(previous: string | null, current: string, maxLines?: number): string;
119
119
  /** Awaited only by the first-turn race in `beforeAgentStartPrompt`. */
@@ -5,24 +5,12 @@ import type { GjcModelAssignmentTargetId, ModelRegistry } from "../../config/mod
5
5
  import { type ScopedModelSelection } from "../../config/model-resolver";
6
6
  import type { Settings } from "../../config/settings";
7
7
  type ScopedModelItem = ScopedModelSelection;
8
- export interface ModelAssignmentPreset {
9
- id: "openai-codex";
10
- label: string;
11
- description: string;
12
- assignments: Partial<Record<GjcModelAssignmentTargetId, ThinkingLevel>>;
13
- }
14
8
  export type ModelSelectorSelection = {
15
9
  kind: "assignment";
16
10
  model: Model;
17
11
  role: GjcModelAssignmentTargetId | null;
18
12
  thinkingLevel?: ThinkingLevel;
19
13
  selector?: string;
20
- } | {
21
- kind: "preset";
22
- model: Model;
23
- selector: string;
24
- preset: ModelAssignmentPreset;
25
- assignments: Record<GjcModelAssignmentTargetId, ThinkingLevel>;
26
14
  } | {
27
15
  kind: "profile";
28
16
  profileName: string;
@@ -41,6 +29,7 @@ export declare class ModelSelectorComponent extends Container {
41
29
  constructor(tui: TUI, _currentModel: Model | undefined, settings: Settings, modelRegistry: ModelRegistry, scopedModels: ReadonlyArray<ScopedModelItem>, onSelect: RoleSelectCallback, onCancel: () => void, options?: {
42
30
  temporaryOnly?: boolean;
43
31
  initialSearchInput?: string;
32
+ sessionId?: string;
44
33
  });
45
34
  handleInput(keyData: string): void;
46
35
  getSearchInput(): Input;
@@ -8,7 +8,7 @@ import type { CompactionResult } from "@gajae-code/agent-core/compaction";
8
8
  import type { ImageContent, Model } from "@gajae-code/ai";
9
9
  import type { BashResult } from "../../exec/bash-executor";
10
10
  import type { SessionStats } from "../../session/agent-session";
11
- import type { RpcExtensionUIRequest, RpcHandoffResult, RpcHostToolDefinition, RpcSessionState, RpcUnattendedAccepted, RpcUnattendedDeclaration, RpcWorkflowGate, RpcWorkflowGateResolution } from "./rpc-types";
11
+ import type { RpcExtensionUIRequest, RpcGetStateInclude, RpcHandoffResult, RpcHostToolDefinition, RpcSessionState, RpcUnattendedAccepted, RpcUnattendedDeclaration, RpcWorkflowGate, RpcWorkflowGateResolution } from "./rpc-types";
12
12
  export interface RpcClientOptions {
13
13
  /** Path to the CLI entry point (default: searches for dist/cli.js) */
14
14
  cliPath?: string;
@@ -115,7 +115,7 @@ export declare class RpcClient {
115
115
  /**
116
116
  * Get current session state.
117
117
  */
118
- getState(): Promise<RpcSessionState>;
118
+ getState(include?: RpcGetStateInclude[]): Promise<RpcSessionState>;
119
119
  /**
120
120
  * Set model by provider and ID.
121
121
  */
@@ -11,6 +11,7 @@ import type { BashResult } from "../../exec/bash-executor";
11
11
  import type { ContextUsage } from "../../extensibility/extensions/types";
12
12
  import type { SessionStats } from "../../session/agent-session";
13
13
  import type { TodoPhase } from "../../tools/todo-write";
14
+ export type RpcGetStateInclude = "tools" | "dumpTools" | "systemPrompt";
14
15
  export type RpcCommand = {
15
16
  id?: string;
16
17
  type: "prompt";
@@ -42,6 +43,7 @@ export type RpcCommand = {
42
43
  } | {
43
44
  id?: string;
44
45
  type: "get_state";
46
+ include?: RpcGetStateInclude[];
45
47
  } | {
46
48
  id?: string;
47
49
  type: "set_todos";
@@ -168,8 +170,9 @@ export interface RpcSessionState {
168
170
  messageCount: number;
169
171
  queuedMessageCount: number;
170
172
  todoPhases: TodoPhase[];
171
- /** For session dump / export (plain-text parity with /dump). */
173
+ /** Optional static system prompt blocks. Omitted by default; request with get_state include ["systemPrompt"]. */
172
174
  systemPrompt?: string[];
175
+ /** Optional static tool schemas. Omitted by default; request with get_state include ["tools"]. */
173
176
  dumpTools?: Array<{
174
177
  name: string;
175
178
  description: string;
@@ -40,6 +40,11 @@ export interface CreateAgentSessionOptions {
40
40
  modelPattern?: string;
41
41
  /** Thinking selector. Default: from settings, else unset */
42
42
  thinkingLevel?: ThinkingLevel;
43
+ /** Runtime substitution metadata for the initial model_change session event. */
44
+ modelSubstitution?: {
45
+ requestedModel: Model;
46
+ reason: string;
47
+ };
43
48
  /** Models available for cycling (Ctrl+P in interactive mode) */
44
49
  scopedModels?: ScopedModelSelection[];
45
50
  /** System prompt blocks. Array replaces default, function receives default blocks and returns final blocks. */
@@ -678,6 +678,8 @@ export declare class AgentSession {
678
678
  selector?: string;
679
679
  thinkingLevel?: ThinkingLevel;
680
680
  }): Promise<void>;
681
+ setActiveModelProfile(name: string | undefined): void;
682
+ getActiveModelProfile(): string | undefined;
681
683
  /**
682
684
  * Set model temporarily (for this session only).
683
685
  * Validates API key, saves to session log but NOT to settings.
@@ -31,6 +31,14 @@ export declare class BlobStore {
31
31
  /** Check if a blob exists. */
32
32
  has(hash: string): Promise<boolean>;
33
33
  }
34
+ export declare class EphemeralBlobStore extends BlobStore {
35
+ #private;
36
+ constructor(dir: string);
37
+ putSync(data: Buffer): BlobPutResult;
38
+ getSync(hash: string): Buffer | null;
39
+ clear(): void;
40
+ dispose(): void;
41
+ }
34
42
  export declare class MemoryBlobStore extends BlobStore {
35
43
  #private;
36
44
  constructor();
@@ -40,6 +48,13 @@ export declare class MemoryBlobStore extends BlobStore {
40
48
  getSync(hash: string): Buffer | null;
41
49
  has(hash: string): Promise<boolean>;
42
50
  }
51
+ export declare class ResidentBlobMissingError extends Error {
52
+ readonly hash: string;
53
+ readonly kind: "text" | "imageUrl" | "imageData";
54
+ readonly sessionId?: string | undefined;
55
+ readonly sessionFile?: string | undefined;
56
+ constructor(hash: string, kind: "text" | "imageUrl" | "imageData", sessionId?: string | undefined, sessionFile?: string | undefined);
57
+ }
43
58
  /** Check if a data string is a blob reference. */
44
59
  export declare function isBlobRef(data: string): boolean;
45
60
  /** Extract the SHA-256 hash from a blob reference string. */
@@ -77,4 +92,8 @@ export declare function resolveImageDataUrlSync(blobStore: BlobStore, data: stri
77
92
  /** Synchronously resolve a blob reference back to base64 data. */
78
93
  export declare function resolveImageDataSync(blobStore: BlobStore, data: string): string;
79
94
  /** Synchronously resolve a blob reference back to utf8 text. */
80
- export declare function resolveTextBlobSync(blobStore: BlobStore, data: string): string;
95
+ export declare function resolveTextBlobSync(blobStore: BlobStore, data: string, context?: {
96
+ kind?: "text";
97
+ sessionId?: string;
98
+ sessionFile?: string;
99
+ }): string;
@@ -40,6 +40,12 @@ export interface ModelChangeEntry extends SessionEntryBase {
40
40
  model: string;
41
41
  /** Role: "default" or an agent role. Undefined treated as "default" */
42
42
  role?: string;
43
+ /** Requested model before a runtime substitution/fallback, in "provider/modelId" format. */
44
+ previousModel?: string;
45
+ /** Machine-readable reason for runtime model substitution/fallback. */
46
+ reason?: string;
47
+ /** Effective thinking level when the change was recorded. */
48
+ thinkingLevel?: string | null;
43
49
  }
44
50
  export interface ServiceTierChangeEntry extends SessionEntryBase {
45
51
  type: "service_tier_change";
@@ -262,6 +268,7 @@ interface SessionManagerStateSnapshot {
262
268
  flushed: boolean;
263
269
  needsFullRewriteOnNextPersist: boolean;
264
270
  fileEntries: FileEntry[];
271
+ materializedFileEntries: FileEntry[];
265
272
  }
266
273
  export declare class SessionManager {
267
274
  #private;
@@ -270,6 +277,18 @@ export declare class SessionManager {
270
277
  private readonly persist;
271
278
  private readonly storage;
272
279
  private constructor();
280
+ /**
281
+ * Snapshot of the five cache-invalidation revision domains (plan: Lane 1
282
+ * revision contract). Tests assert the invalidation mapping through this;
283
+ * future export/label-view caches key off their respective domains.
284
+ */
285
+ revisionSnapshot(): {
286
+ entry: number;
287
+ leaf: number;
288
+ headerExport: number;
289
+ label: number;
290
+ replayMetadata: number;
291
+ };
273
292
  /** Puts a binary blob into the blob store and returns the blob reference */
274
293
  putBlob(data: Buffer): Promise<BlobPutResult>;
275
294
  captureState(): SessionManagerStateSnapshot;
@@ -388,7 +407,11 @@ export declare class SessionManager {
388
407
  * @param model Model in "provider/modelId" format
389
408
  * @param role Optional role (default: "default")
390
409
  */
391
- appendModelChange(model: string, role?: string): string;
410
+ appendModelChange(model: string, role?: string, metadata?: {
411
+ previousModel?: string;
412
+ reason?: string;
413
+ thinkingLevel?: string | null;
414
+ }): string;
392
415
  /** Append session init metadata (for subagent debugging/replay). Returns entry id. */
393
416
  appendSessionInit(init: {
394
417
  systemPrompt: string;
@@ -480,11 +503,6 @@ export declare class SessionManager {
480
503
  * Get session header.
481
504
  */
482
505
  getHeader(): SessionHeader | null;
483
- /**
484
- * Get all session entries (excludes header). Returns a shallow copy.
485
- * The session is append-only: use appendXXX() to add entries, branch() to
486
- * change the leaf pointer. Entries cannot be modified or deleted.
487
- */
488
506
  getEntries(): SessionEntry[];
489
507
  /**
490
508
  * Get the session as a tree structure. Returns a shallow defensive copy of all entries.
@@ -159,8 +159,9 @@ export declare class OutputSink {
159
159
  #private;
160
160
  constructor(options?: OutputSinkOptions);
161
161
  /**
162
- * Push a chunk of output. The buffer management and onChunk callback run
163
- * synchronously. File sink writes are deferred and serialized internally.
162
+ * Push a chunk of output. Raw bytes are mirrored to artifacts, while the
163
+ * visible retention windows are selected from the sanitized/column-capped
164
+ * stream so production-default display matches the historical processed view.
164
165
  */
165
166
  push(chunk: string): void;
166
167
  createInput(): WritableStream<Uint8Array | string>;
@@ -63,6 +63,12 @@ export declare class ToolChoiceQueue {
63
63
  * lost value at the head of the queue; anything else drops it.
64
64
  */
65
65
  reject(reason: RejectInfo["reason"]): void;
66
+ /**
67
+ * Drop an in-flight yield after runtime tool_choice degradation. This bypasses
68
+ * onRejected so forced directives cannot requeue themselves after the model
69
+ * proved incapable of honoring the forced choice.
70
+ */
71
+ degradeInFlight(reason?: string): string | undefined;
66
72
  /** True if there is an in-flight yield that hasn't been resolved or rejected. */
67
73
  get hasInFlight(): boolean;
68
74
  /** Peek the in-flight directive's onInvoked handler, if any. */
@@ -28,6 +28,7 @@ export interface TaskResultReceipt {
28
28
  contextTokens?: number;
29
29
  contextWindow?: number;
30
30
  modelOverride?: string | string[];
31
+ modelSubstitutionWarning?: SingleResult["modelSubstitutionWarning"];
31
32
  usage?: SingleResult["usage"];
32
33
  cost?: number;
33
34
  branchName?: string;
@@ -280,6 +280,11 @@ export interface AgentDefinition {
280
280
  source: AgentSource;
281
281
  filePath?: string;
282
282
  }
283
+ export interface ModelSubstitutionWarning {
284
+ requested: string;
285
+ effective: string;
286
+ reason: "auth_unavailable" | "assistant_model_mismatch";
287
+ }
283
288
  /** Progress tracking for a single agent */
284
289
  export interface AgentProgress {
285
290
  index: number;
@@ -316,6 +321,7 @@ export interface AgentProgress {
316
321
  cost: number;
317
322
  durationMs: number;
318
323
  modelOverride?: string | string[];
324
+ modelSubstitutionWarning?: ModelSubstitutionWarning;
319
325
  /** Data extracted by registered subprocess tool handlers (keyed by tool name) */
320
326
  extractedToolData?: Record<string, unknown[]>;
321
327
  /**
@@ -374,6 +380,7 @@ export interface SingleResult {
374
380
  /** Model's context window in tokens, when known. */
375
381
  contextWindow?: number;
376
382
  modelOverride?: string | string[];
383
+ modelSubstitutionWarning?: ModelSubstitutionWarning;
377
384
  error?: string;
378
385
  aborted?: boolean;
379
386
  abortReason?: string;