@gajae-code/coding-agent 0.4.4 → 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 (132) hide show
  1. package/CHANGELOG.md +83 -0
  2. package/dist/types/cli/fast-help.d.ts +1 -0
  3. package/dist/types/cli/setup-cli.d.ts +2 -0
  4. package/dist/types/commands/harness.d.ts +6 -0
  5. package/dist/types/commands/setup.d.ts +6 -0
  6. package/dist/types/config/model-profile-activation.d.ts +11 -2
  7. package/dist/types/config/model-profiles.d.ts +7 -0
  8. package/dist/types/config/model-registry.d.ts +6 -0
  9. package/dist/types/config/model-resolver.d.ts +2 -0
  10. package/dist/types/config/models-config-schema.d.ts +35 -0
  11. package/dist/types/config/settings-schema.d.ts +4 -3
  12. package/dist/types/coordinator/contract.d.ts +1 -1
  13. package/dist/types/coordinator-mcp/server.d.ts +8 -2
  14. package/dist/types/gjc-runtime/team-runtime.d.ts +0 -1
  15. package/dist/types/gjc-runtime/tmux-common.d.ts +3 -0
  16. package/dist/types/harness-control-plane/finalize.d.ts +5 -0
  17. package/dist/types/harness-control-plane/owner.d.ts +1 -1
  18. package/dist/types/harness-control-plane/phase-rollup.d.ts +23 -0
  19. package/dist/types/harness-control-plane/receipt-ingest.d.ts +19 -0
  20. package/dist/types/harness-control-plane/receipt-spool.d.ts +19 -0
  21. package/dist/types/harness-control-plane/receipts.d.ts +46 -0
  22. package/dist/types/harness-control-plane/rpc-adapter.d.ts +3 -0
  23. package/dist/types/harness-control-plane/state-machine.d.ts +6 -1
  24. package/dist/types/harness-control-plane/types.d.ts +13 -1
  25. package/dist/types/hindsight/mental-models.d.ts +5 -5
  26. package/dist/types/main.d.ts +2 -2
  27. package/dist/types/modes/components/model-selector.d.ts +1 -12
  28. package/dist/types/modes/rpc/rpc-client.d.ts +2 -2
  29. package/dist/types/modes/rpc/rpc-types.d.ts +4 -1
  30. package/dist/types/modes/utils/abort-message.d.ts +4 -0
  31. package/dist/types/sdk.d.ts +5 -0
  32. package/dist/types/session/agent-session.d.ts +2 -0
  33. package/dist/types/session/blob-store.d.ts +20 -1
  34. package/dist/types/session/session-manager.d.ts +32 -6
  35. package/dist/types/session/streaming-output.d.ts +3 -2
  36. package/dist/types/session/tool-choice-queue.d.ts +6 -0
  37. package/dist/types/setup/hermes-setup.d.ts +7 -0
  38. package/dist/types/task/fork-context-advisory.d.ts +13 -0
  39. package/dist/types/task/receipt.d.ts +2 -0
  40. package/dist/types/task/roi-reconciliation.d.ts +27 -0
  41. package/dist/types/task/types.d.ts +17 -0
  42. package/dist/types/thinking-metadata.d.ts +16 -0
  43. package/dist/types/thinking.d.ts +3 -12
  44. package/dist/types/tools/index.d.ts +2 -0
  45. package/dist/types/tools/resolve.d.ts +0 -10
  46. package/dist/types/utils/tool-choice.d.ts +14 -1
  47. package/package.json +8 -7
  48. package/scripts/build-binary.ts +4 -0
  49. package/src/cli/fast-help.ts +80 -0
  50. package/src/cli/setup-cli.ts +12 -3
  51. package/src/cli.ts +112 -17
  52. package/src/commands/coordinator.ts +44 -1
  53. package/src/commands/harness.ts +128 -11
  54. package/src/commands/launch.ts +2 -2
  55. package/src/commands/mcp-serve.ts +3 -2
  56. package/src/commands/session.ts +3 -1
  57. package/src/commands/setup.ts +4 -0
  58. package/src/config/model-profile-activation.ts +15 -3
  59. package/src/config/model-profiles.ts +255 -56
  60. package/src/config/model-resolver.ts +9 -6
  61. package/src/config/models-config-schema.ts +2 -0
  62. package/src/config/settings-schema.ts +6 -3
  63. package/src/coordinator/contract.ts +1 -0
  64. package/src/coordinator-mcp/server.ts +427 -193
  65. package/src/cursor.ts +46 -4
  66. package/src/defaults/gjc/skills/team/SKILL.md +3 -2
  67. package/src/defaults/gjc/skills/ultragoal/SKILL.md +8 -2
  68. package/src/export/html/index.ts +13 -9
  69. package/src/gjc-runtime/launch-worktree.ts +12 -1
  70. package/src/gjc-runtime/session-state-sidecar.ts +38 -0
  71. package/src/gjc-runtime/team-runtime.ts +33 -7
  72. package/src/gjc-runtime/tmux-common.ts +15 -0
  73. package/src/gjc-runtime/tmux-sessions.ts +19 -11
  74. package/src/gjc-runtime/ultragoal-runtime.ts +505 -41
  75. package/src/gjc-runtime/workflow-manifest.generated.json +27 -1
  76. package/src/gjc-runtime/workflow-manifest.ts +16 -1
  77. package/src/harness-control-plane/finalize.ts +39 -5
  78. package/src/harness-control-plane/owner.ts +87 -28
  79. package/src/harness-control-plane/phase-rollup.ts +96 -0
  80. package/src/harness-control-plane/receipt-ingest.ts +127 -0
  81. package/src/harness-control-plane/receipt-spool.ts +128 -0
  82. package/src/harness-control-plane/receipts.ts +229 -1
  83. package/src/harness-control-plane/rpc-adapter.ts +8 -0
  84. package/src/harness-control-plane/state-machine.ts +27 -6
  85. package/src/harness-control-plane/storage.ts +23 -0
  86. package/src/harness-control-plane/types.ts +33 -1
  87. package/src/hindsight/mental-models.ts +17 -16
  88. package/src/internal-urls/docs-index.generated.ts +8 -7
  89. package/src/main.ts +7 -3
  90. package/src/modes/components/assistant-message.ts +26 -14
  91. package/src/modes/components/diff.ts +97 -0
  92. package/src/modes/components/model-selector.ts +353 -181
  93. package/src/modes/components/status-line.ts +6 -6
  94. package/src/modes/components/tool-execution.ts +30 -13
  95. package/src/modes/controllers/event-controller.ts +5 -4
  96. package/src/modes/controllers/selector-controller.ts +33 -42
  97. package/src/modes/interactive-mode.ts +4 -5
  98. package/src/modes/print-mode.ts +1 -1
  99. package/src/modes/rpc/rpc-client.ts +3 -2
  100. package/src/modes/rpc/rpc-mode.ts +44 -14
  101. package/src/modes/rpc/rpc-types.ts +5 -2
  102. package/src/modes/shared/agent-wire/command-dispatch.ts +10 -5
  103. package/src/modes/shared/agent-wire/command-validation.ts +11 -0
  104. package/src/modes/theme/theme.ts +2 -2
  105. package/src/modes/utils/abort-message.ts +41 -0
  106. package/src/modes/utils/context-usage.ts +15 -8
  107. package/src/modes/utils/ui-helpers.ts +5 -6
  108. package/src/sdk.ts +38 -6
  109. package/src/secrets/obfuscator.ts +102 -27
  110. package/src/session/agent-session.ts +121 -25
  111. package/src/session/blob-store.ts +89 -3
  112. package/src/session/session-manager.ts +328 -57
  113. package/src/session/streaming-output.ts +185 -122
  114. package/src/session/tool-choice-queue.ts +23 -0
  115. package/src/setup/hermes/templates/operator-instructions.v1.md +3 -2
  116. package/src/setup/hermes-setup.ts +63 -8
  117. package/src/task/executor.ts +69 -6
  118. package/src/task/fork-context-advisory.ts +99 -0
  119. package/src/task/index.ts +31 -2
  120. package/src/task/receipt.ts +7 -0
  121. package/src/task/render.ts +21 -1
  122. package/src/task/roi-reconciliation.ts +90 -0
  123. package/src/task/types.ts +15 -0
  124. package/src/thinking-metadata.ts +51 -0
  125. package/src/thinking.ts +26 -46
  126. package/src/tools/bash.ts +1 -1
  127. package/src/tools/index.ts +4 -2
  128. package/src/tools/resolve.ts +93 -18
  129. package/src/tools/subagent-render.ts +10 -1
  130. package/src/utils/edit-mode.ts +1 -1
  131. package/src/utils/title-generator.ts +16 -2
  132. package/src/utils/tool-choice.ts +45 -16
@@ -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`. */
@@ -7,7 +7,7 @@
7
7
  import type { Args } from "./cli/args";
8
8
  import { ModelRegistry } from "./config/model-registry";
9
9
  import { Settings } from "./config/settings";
10
- import { InteractiveMode, runAcpMode } from "./modes";
10
+ import type { InteractiveMode } from "./modes/interactive-mode";
11
11
  import type { SubmittedUserInput } from "./modes/types";
12
12
  import { type CreateAgentSessionOptions, type CreateAgentSessionResult, createAgentSession, discoverAuthStorage } from "./sdk";
13
13
  import type { AgentSession } from "./session/agent-session";
@@ -51,7 +51,7 @@ export declare function createAcpSessionFactory(args: AcpSessionFactoryOptions):
51
51
  interface RunRootCommandDependencies {
52
52
  createAgentSession?: typeof createAgentSession;
53
53
  discoverAuthStorage?: typeof discoverAuthStorage;
54
- runAcpMode?: typeof runAcpMode;
54
+ runAcpMode?: (createSession: AcpSessionFactory) => Promise<void>;
55
55
  settings?: Settings;
56
56
  }
57
57
  export declare function runRootCommand(parsed: Args, rawArgs: string[], deps?: RunRootCommandDependencies): Promise<void>;
@@ -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;
@@ -0,0 +1,4 @@
1
+ export declare function buildAbortDisplayMessage({ errorMessage, retryAttempt }: {
2
+ errorMessage?: string;
3
+ retryAttempt: number;
4
+ }): 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;
@@ -401,6 +424,14 @@ export declare class SessionManager {
401
424
  appendCompaction<T = unknown>(summary: string, shortSummary: string | undefined, firstKeptEntryId: string, tokensBefore: number, details?: T, fromExtension?: boolean, preserveData?: Record<string, unknown>): string;
402
425
  /** Append a custom entry (for extensions) as child of current leaf, then advance leaf. Returns entry id. */
403
426
  appendCustomEntry(customType: string, data?: unknown): string;
427
+ /**
428
+ * Write mutated message entries back into the canonical entry store by id.
429
+ *
430
+ * `getBranch()` materializes resident-blob entries into copies, so in-place
431
+ * mutation of returned entries (e.g. pruning tool outputs) does not affect
432
+ * the canonical store. This applies such mutations for real.
433
+ */
434
+ applyEntryMessageUpdates(entries: readonly SessionMessageEntry[]): void;
404
435
  /**
405
436
  * Rewrite the session file after in-place entry updates.
406
437
  * Use sparingly (e.g., pruning old tool outputs).
@@ -472,11 +503,6 @@ export declare class SessionManager {
472
503
  * Get session header.
473
504
  */
474
505
  getHeader(): SessionHeader | null;
475
- /**
476
- * Get all session entries (excludes header). Returns a shallow copy.
477
- * The session is append-only: use appendXXX() to add entries, branch() to
478
- * change the leaf pointer. Entries cannot be modified or deleted.
479
- */
480
506
  getEntries(): SessionEntry[];
481
507
  /**
482
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. */
@@ -11,6 +11,8 @@ export interface HermesSetupFlags {
11
11
  repo?: string;
12
12
  profile?: string;
13
13
  sessionCommand?: string;
14
+ noWorktree?: boolean;
15
+ worktreeName?: string;
14
16
  stateRoot?: string;
15
17
  mutation?: string[];
16
18
  artifactByteCap?: string;
@@ -33,6 +35,11 @@ export interface CoordinatorSetupSpec {
33
35
  repo?: string;
34
36
  };
35
37
  sessionCommand?: string;
38
+ sessionCommandSource: "default" | "explicit";
39
+ worktree: {
40
+ enabled: boolean;
41
+ name?: string;
42
+ };
36
43
  stateRoot?: string;
37
44
  mutationPolicy: {
38
45
  classes: HermesMutationClass[];
@@ -0,0 +1,13 @@
1
+ import type { ForkContextMode } from "./types";
2
+ export interface ForkContextAdvisory {
3
+ recommendedMode: ForkContextMode;
4
+ reasons: string[];
5
+ estimatedClonedTokens: Record<ForkContextMode, number>;
6
+ callerModeRespected: true;
7
+ }
8
+ export declare function adviseForkContextMode(input: {
9
+ assignment: string;
10
+ context?: string;
11
+ explicitMode?: ForkContextMode;
12
+ parentContextTokens?: number;
13
+ }): ForkContextAdvisory;
@@ -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;
@@ -56,6 +57,7 @@ export interface TaskResultReceipt {
56
57
  };
57
58
  extractedToolCounts?: Record<string, number>;
58
59
  forkContext?: SingleResult["forkContext"];
60
+ forkContextAdvisory?: SingleResult["forkContextAdvisory"];
59
61
  roi?: TaskRoi;
60
62
  }
61
63
  /**
@@ -0,0 +1,27 @@
1
+ import type { TaskResultReceipt } from "./receipt";
2
+ import type { SpawnPlanReceipt } from "./spawn-gate";
3
+ /**
4
+ * Pure, advisory-only reconciliation between a spawn plan's inline-token promise
5
+ * and receipt-safe child outputs. These signals never change task success/failure
6
+ * semantics or runtime behavior; they only describe budget/ROI observations for
7
+ * model-facing summaries.
8
+ */
9
+ export interface SpawnRoiChildReconciliation {
10
+ id: string;
11
+ inlineTokens: number;
12
+ maxInlineTokens: number;
13
+ overBudget: boolean;
14
+ overageTokens: number;
15
+ lowRoi: boolean;
16
+ }
17
+ export interface SpawnRoiReconciliation {
18
+ childCount: number;
19
+ promisedMaxInlineTokens: number;
20
+ children: SpawnRoiChildReconciliation[];
21
+ overBudgetChildIds: string[];
22
+ lowRoiChildIds: string[];
23
+ totalInlineTokens: number;
24
+ totalOverageTokens: number;
25
+ advisoryFlags: string[];
26
+ }
27
+ export declare function reconcileSpawnRoi(plan: SpawnPlanReceipt | undefined, receipts: readonly TaskResultReceipt[]): SpawnRoiReconciliation | undefined;
@@ -2,6 +2,7 @@ import type { ThinkingLevel } from "@gajae-code/agent-core";
2
2
  import type { Usage } from "@gajae-code/ai";
3
3
  import * as z from "zod/v4";
4
4
  import type { TaskResultReceipt } from "./receipt";
5
+ import type { SpawnRoiReconciliation } from "./roi-reconciliation";
5
6
  import { type TaskSimpleMode } from "./simple-mode";
6
7
  import type { SpawnPlanReceipt } from "./spawn-gate";
7
8
  import type { NestedRepoPatch } from "./worktree";
@@ -279,6 +280,11 @@ export interface AgentDefinition {
279
280
  source: AgentSource;
280
281
  filePath?: string;
281
282
  }
283
+ export interface ModelSubstitutionWarning {
284
+ requested: string;
285
+ effective: string;
286
+ reason: "auth_unavailable" | "assistant_model_mismatch";
287
+ }
282
288
  /** Progress tracking for a single agent */
283
289
  export interface AgentProgress {
284
290
  index: number;
@@ -315,6 +321,7 @@ export interface AgentProgress {
315
321
  cost: number;
316
322
  durationMs: number;
317
323
  modelOverride?: string | string[];
324
+ modelSubstitutionWarning?: ModelSubstitutionWarning;
318
325
  /** Data extracted by registered subprocess tool handlers (keyed by tool name) */
319
326
  extractedToolData?: Record<string, unknown[]>;
320
327
  /**
@@ -373,6 +380,7 @@ export interface SingleResult {
373
380
  /** Model's context window in tokens, when known. */
374
381
  contextWindow?: number;
375
382
  modelOverride?: string | string[];
383
+ modelSubstitutionWarning?: ModelSubstitutionWarning;
376
384
  error?: string;
377
385
  aborted?: boolean;
378
386
  abortReason?: string;
@@ -413,6 +421,14 @@ export interface SingleResult {
413
421
  mode: ForkContextMode;
414
422
  clonedTokens: number;
415
423
  };
424
+ /**
425
+ * Advisory fork-context mode recommendation for this task (logged only;
426
+ * never changes the actual mode selection).
427
+ */
428
+ forkContextAdvisory?: {
429
+ recommendedMode: ForkContextMode;
430
+ reasons: string[];
431
+ };
416
432
  }
417
433
  /** Tool details for TUI rendering */
418
434
  export interface TaskToolDetails {
@@ -431,6 +447,7 @@ export interface TaskToolDetails {
431
447
  /** Advisory ids for terminal children that spent tokens without detectable output/review/changes. */
432
448
  lowRoiChildIds: string[];
433
449
  };
450
+ roiReconciliation?: SpawnRoiReconciliation;
434
451
  progress?: AgentProgress[];
435
452
  async?: {
436
453
  state: "running" | "paused" | "queued" | "completed" | "failed";
@@ -0,0 +1,16 @@
1
+ export type ThinkingLevelValue = "inherit" | "off" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max";
2
+ /**
3
+ * Metadata used to render thinking selector values in the coding-agent UI.
4
+ *
5
+ * This module is intentionally provider/native-free so schema generation can
6
+ * import settings metadata before native addons have been built in CI.
7
+ */
8
+ export interface ThinkingLevelMetadata {
9
+ value: ThinkingLevelValue;
10
+ label: string;
11
+ description: string;
12
+ }
13
+ /**
14
+ * Returns display metadata for a thinking selector.
15
+ */
16
+ export declare function getThinkingLevelMetadata(level: ThinkingLevelValue): ThinkingLevelMetadata;
@@ -1,14 +1,7 @@
1
1
  import { type ResolvedThinkingLevel, ThinkingLevel } from "@gajae-code/agent-core/thinking";
2
2
  import { type Effort } from "@gajae-code/ai/model-thinking";
3
3
  import type { Model } from "@gajae-code/ai/types";
4
- /**
5
- * Metadata used to render thinking selector values in the coding-agent UI.
6
- */
7
- export interface ThinkingLevelMetadata {
8
- value: ThinkingLevel;
9
- label: string;
10
- description: string;
11
- }
4
+ export { getThinkingLevelMetadata, type ThinkingLevelMetadata } from "./thinking-metadata";
12
5
  /**
13
6
  * Parses a provider-facing effort value.
14
7
  */
@@ -17,10 +10,6 @@ export declare function parseEffort(value: string | null | undefined): Effort |
17
10
  * Parses an agent-local thinking selector.
18
11
  */
19
12
  export declare function parseThinkingLevel(value: string | null | undefined): ThinkingLevel | undefined;
20
- /**
21
- * Returns display metadata for a thinking selector.
22
- */
23
- export declare function getThinkingLevelMetadata(level: ThinkingLevel): ThinkingLevelMetadata;
24
13
  /**
25
14
  * Converts an agent-local selector into the effort sent to providers.
26
15
  */
@@ -29,3 +18,5 @@ export declare function toReasoningEffort(level: ThinkingLevel | undefined): Eff
29
18
  * Resolves a selector against the current model while preserving explicit "off".
30
19
  */
31
20
  export declare function resolveThinkingLevelForModel(model: Model | undefined, level: ThinkingLevel | undefined): ResolvedThinkingLevel | undefined;
21
+ export declare function clampExplicitThinkingLevelForModel(model: Model | undefined, level: ThinkingLevel | undefined): ThinkingLevel | undefined;
22
+ export declare function formatClampedModelSelector(selector: string, model: Model | undefined): string;
@@ -193,6 +193,8 @@ export interface ToolSession {
193
193
  getToolChoiceQueue?(): ToolChoiceQueue;
194
194
  /** Build a model-provider-specific ToolChoice that targets the named tool, or undefined if unsupported. */
195
195
  buildToolChoice?(toolName: string): ToolChoice | undefined;
196
+ /** Build a named tool-choice decision, preserving whether exact named forcing survived capability degradation. */
197
+ buildToolChoiceResult?(toolName: string): import("../utils/tool-choice").NamedToolChoiceResult;
196
198
  /** Steer a hidden custom message into the conversation (e.g. a preview reminder). */
197
199
  steer?(message: {
198
200
  customType: string;
@@ -21,16 +21,6 @@ export interface ResolveToolDetails {
21
21
  label?: string;
22
22
  sourceResultDetails?: unknown;
23
23
  }
24
- /**
25
- * Queue a resolve-protocol handler on the tool-choice queue. Forces the next
26
- * LLM call to invoke the hidden `resolve` tool, wraps the caller's apply/reject
27
- * callbacks into an onInvoked closure that matches the resolve schema, and
28
- * steers a preview reminder so the model understands why.
29
- *
30
- * This is the canonical entry point for any tool that wants preview/apply
31
- * semantics. No session-level abstraction is needed: callers pass their
32
- * apply/reject functions directly.
33
- */
34
24
  export declare function queueResolveHandler(session: ToolSession, options: {
35
25
  label: string;
36
26
  sourceToolName: string;
@@ -1,7 +1,20 @@
1
- import type { Api, Model, ToolChoice } from "@gajae-code/ai";
1
+ import type { Api, Model, ResolveToolChoiceResult, ToolChoice } from "@gajae-code/ai";
2
2
  /**
3
3
  * Build a provider-aware tool choice that targets one specific tool when supported.
4
4
  * Providers that only expose required/any forcing may still honor named choices by
5
5
  * narrowing their request tool list before transport.
6
6
  */
7
+ export interface NamedToolChoiceResult {
8
+ choice: ToolChoice | undefined;
9
+ exactNamed: boolean;
10
+ resolved?: ResolveToolChoiceResult;
11
+ }
12
+ export declare function buildNamedToolChoiceResult(toolName: string, model?: Model<Api>): NamedToolChoiceResult;
13
+ /**
14
+ * Legacy capability-aware wrapper. May return a lossy `"required"` when named
15
+ * forcing degrades (e.g. Google APIs, or compat `toolChoiceSupport: "required"`),
16
+ * which forces *some* tool rather than `toolName` specifically. Queue directives
17
+ * that need exact tool identity (resolve / todo_write / yield) MUST use
18
+ * `buildNamedToolChoiceResult` and gate on `exactNamed` instead.
19
+ */
7
20
  export declare function buildNamedToolChoice(toolName: string, model?: Model<Api>): ToolChoice | undefined;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@gajae-code/coding-agent",
4
- "version": "0.4.4",
4
+ "version": "0.5.0",
5
5
  "description": "Gajae Code CLI with read, bash, edit, write tools and session management",
6
6
  "homepage": "https://gaebal-gajae.dev",
7
7
  "author": "Yeachan-Heo",
@@ -36,6 +36,7 @@
36
36
  "check:types": "tsgo -p tsconfig.json --noEmit",
37
37
  "lint": "biome lint .",
38
38
  "test": "bun test",
39
+ "bench:context": "bun run bench/context-optimization.bench.ts",
39
40
  "generate-schemas": "bun ../../scripts/generate-json-schemas.ts",
40
41
  "check:schemas": "bun ../../scripts/generate-json-schemas.ts --check",
41
42
  "fix": "biome check --write --unsafe . && bun run format-prompts && bun run generate-docs-index",
@@ -50,12 +51,12 @@
50
51
  "@agentclientprotocol/sdk": "0.21.0",
51
52
  "@babel/parser": "^7.29.3",
52
53
  "@mozilla/readability": "^0.6.0",
53
- "@gajae-code/stats": "0.4.4",
54
- "@gajae-code/agent-core": "0.4.4",
55
- "@gajae-code/ai": "0.4.4",
56
- "@gajae-code/natives": "0.4.4",
57
- "@gajae-code/tui": "0.4.4",
58
- "@gajae-code/utils": "0.4.4",
54
+ "@gajae-code/stats": "0.5.0",
55
+ "@gajae-code/agent-core": "0.5.0",
56
+ "@gajae-code/ai": "0.5.0",
57
+ "@gajae-code/natives": "0.5.0",
58
+ "@gajae-code/tui": "0.5.0",
59
+ "@gajae-code/utils": "0.5.0",
59
60
  "@puppeteer/browsers": "^2.13.0",
60
61
  "@types/turndown": "5.0.6",
61
62
  "@xterm/headless": "^6.0.0",
@@ -39,6 +39,10 @@ async function main(): Promise<void> {
39
39
  "bun",
40
40
  "build",
41
41
  "--compile",
42
+ // Minify shrinks the bundled JS the compiled binary must parse at
43
+ // startup (302MB → ~114MB --help RSS measured on darwin-arm64).
44
+ // --keep-names below preserves identifiers for error reports.
45
+ "--minify",
42
46
  "--no-compile-autoload-bunfig",
43
47
  "--no-compile-autoload-dotenv",
44
48
  "--no-compile-autoload-tsconfig",