@gajae-code/coding-agent 0.4.1 → 0.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/dist/types/async/job-manager.d.ts +25 -0
  3. package/dist/types/commands/ultragoal.d.ts +1 -0
  4. package/dist/types/commit/model-selection.d.ts +1 -1
  5. package/dist/types/config/model-registry.d.ts +3 -1
  6. package/dist/types/config/model-resolver.d.ts +1 -19
  7. package/dist/types/config/models-config-schema.d.ts +12 -0
  8. package/dist/types/config/settings-schema.d.ts +26 -4
  9. package/dist/types/gjc-runtime/goal-mode-request.d.ts +8 -1
  10. package/dist/types/gjc-runtime/launch-tmux.d.ts +1 -0
  11. package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +29 -0
  12. package/dist/types/harness-control-plane/finalize.d.ts +8 -0
  13. package/dist/types/harness-control-plane/receipts.d.ts +16 -1
  14. package/dist/types/harness-control-plane/types.d.ts +16 -3
  15. package/dist/types/modes/acp/acp-event-mapper.d.ts +2 -0
  16. package/dist/types/modes/components/custom-editor.d.ts +7 -0
  17. package/dist/types/modes/shared/agent-wire/command-contract.d.ts +18 -0
  18. package/dist/types/modes/shared/agent-wire/event-contract.d.ts +84 -0
  19. package/dist/types/modes/shared/agent-wire/event-envelope.d.ts +14 -7
  20. package/dist/types/modes/shared/agent-wire/event-observation.d.ts +37 -0
  21. package/dist/types/modes/shared/agent-wire/protocol.d.ts +13 -34
  22. package/dist/types/reminders/star-reminder.d.ts +115 -0
  23. package/dist/types/session/agent-session.d.ts +30 -1
  24. package/dist/types/session/session-manager.d.ts +1 -1
  25. package/dist/types/tools/bash.d.ts +2 -0
  26. package/dist/types/tools/browser/actions.d.ts +54 -0
  27. package/dist/types/tools/browser.d.ts +80 -0
  28. package/dist/types/tools/image-gen.d.ts +1 -0
  29. package/dist/types/tools/index.d.ts +3 -1
  30. package/dist/types/tools/job.d.ts +1 -1
  31. package/examples/extensions/README.md +20 -41
  32. package/package.json +7 -7
  33. package/src/async/job-manager.ts +120 -1
  34. package/src/cli/grep-cli.ts +1 -1
  35. package/src/commands/harness.ts +42 -3
  36. package/src/commands/ultragoal.ts +8 -1
  37. package/src/commit/agentic/index.ts +2 -2
  38. package/src/commit/model-selection.ts +7 -22
  39. package/src/commit/pipeline.ts +2 -2
  40. package/src/config/model-registry.ts +17 -9
  41. package/src/config/model-resolver.ts +14 -84
  42. package/src/config/models-config-schema.ts +2 -0
  43. package/src/config/settings-schema.ts +27 -4
  44. package/src/defaults/gjc/skills/team/SKILL.md +10 -1
  45. package/src/defaults/gjc/skills/ultragoal/SKILL.md +3 -2
  46. package/src/gjc-runtime/goal-mode-request.ts +21 -1
  47. package/src/gjc-runtime/launch-tmux.ts +25 -2
  48. package/src/gjc-runtime/team-runtime.ts +78 -3
  49. package/src/gjc-runtime/ultragoal-guard.ts +18 -2
  50. package/src/gjc-runtime/ultragoal-runtime.ts +240 -30
  51. package/src/harness-control-plane/finalize.ts +84 -0
  52. package/src/harness-control-plane/owner.ts +16 -3
  53. package/src/harness-control-plane/receipts.ts +39 -1
  54. package/src/harness-control-plane/rpc-adapter.ts +7 -1
  55. package/src/harness-control-plane/types.ts +33 -12
  56. package/src/internal-urls/docs-index.generated.ts +3 -3
  57. package/src/memories/index.ts +1 -1
  58. package/src/modes/acp/acp-agent.ts +17 -9
  59. package/src/modes/acp/acp-event-mapper.ts +33 -1
  60. package/src/modes/components/custom-editor.ts +19 -3
  61. package/src/modes/controllers/input-controller.ts +27 -7
  62. package/src/modes/controllers/selector-controller.ts +7 -1
  63. package/src/modes/interactive-mode.ts +29 -1
  64. package/src/modes/rpc/rpc-client.ts +16 -3
  65. package/src/modes/rpc/rpc-mode.ts +5 -2
  66. package/src/modes/shared/agent-wire/command-contract.ts +18 -0
  67. package/src/modes/shared/agent-wire/event-contract.ts +147 -0
  68. package/src/modes/shared/agent-wire/event-envelope.ts +35 -16
  69. package/src/modes/shared/agent-wire/event-observation.ts +397 -0
  70. package/src/modes/shared/agent-wire/protocol.ts +24 -81
  71. package/src/modes/utils/context-usage.ts +2 -2
  72. package/src/prompts/agents/explore.md +1 -1
  73. package/src/prompts/agents/plan.md +1 -1
  74. package/src/prompts/agents/reviewer.md +1 -1
  75. package/src/prompts/tools/browser.md +3 -2
  76. package/src/reminders/star-reminder.ts +422 -0
  77. package/src/runtime-mcp/manager.ts +15 -2
  78. package/src/sdk.ts +3 -1
  79. package/src/session/agent-session.ts +139 -17
  80. package/src/session/session-manager.ts +1 -1
  81. package/src/task/agents.ts +1 -1
  82. package/src/tools/bash.ts +6 -1
  83. package/src/tools/browser/actions.ts +189 -0
  84. package/src/tools/browser.ts +91 -1
  85. package/src/tools/image-gen.ts +42 -15
  86. package/src/tools/index.ts +7 -1
  87. package/src/tools/inspect-image.ts +10 -8
  88. package/src/tools/job.ts +12 -2
  89. package/src/tools/monitor.ts +98 -17
  90. package/src/utils/commit-message-generator.ts +6 -13
  91. package/src/utils/title-generator.ts +1 -1
  92. package/dist/types/harness-control-plane/frame-mapper.d.ts +0 -29
  93. package/src/harness-control-plane/frame-mapper.ts +0 -286
  94. package/src/priority.json +0 -37
@@ -401,23 +401,39 @@ export function setPreferredImageProvider(provider: ImageProvider | "auto"): voi
401
401
 
402
402
  interface ParsedAntigravityCredentials {
403
403
  accessToken: string;
404
- projectId: string;
404
+ projectId?: string;
405
405
  }
406
406
 
407
407
  function parseAntigravityCredentials(raw: string): ParsedAntigravityCredentials | null {
408
408
  try {
409
- const parsed = JSON.parse(raw) as { token?: string; projectId?: string };
410
- if (parsed.token && parsed.projectId) {
411
- return { accessToken: parsed.token, projectId: parsed.projectId };
409
+ const parsed = JSON.parse(raw) as { token?: string; accessToken?: string; projectId?: string };
410
+ const token = parsed.token ?? parsed.accessToken;
411
+ if (typeof token === "string" && token.trim().length > 0) {
412
+ return { accessToken: token.trim(), projectId: parsed.projectId };
412
413
  }
414
+ // Parsed as JSON but no usable token field.
415
+ return null;
413
416
  } catch {
414
- // Invalid JSON
417
+ // Not JSON: treat the value as a raw bearer token.
415
418
  }
416
- return null;
419
+ const rawToken = raw.trim();
420
+ return rawToken.length > 0 ? { accessToken: rawToken } : null;
417
421
  }
418
422
 
419
- async function findAntigravityCredentials(modelRegistry: ModelRegistry): Promise<ImageApiKey | null> {
420
- const apiKey = await modelRegistry.getApiKeyForProvider("google-antigravity");
423
+ async function findAntigravityCredentials(
424
+ modelRegistry: ModelRegistry,
425
+ sessionId?: string,
426
+ ): Promise<ImageApiKey | null> {
427
+ const oauthAccess = await modelRegistry.authStorage.getOAuthAccess("google-antigravity", sessionId);
428
+ if (oauthAccess?.accessToken) {
429
+ return {
430
+ provider: "antigravity",
431
+ apiKey: oauthAccess.accessToken,
432
+ projectId: oauthAccess.projectId,
433
+ };
434
+ }
435
+
436
+ const apiKey = await modelRegistry.getApiKeyForProvider("google-antigravity", sessionId);
421
437
  if (!apiKey) return null;
422
438
 
423
439
  const parsed = parseAntigravityCredentials(apiKey);
@@ -457,7 +473,7 @@ async function findImageApiKey(
457
473
  if (openAI) return openAI;
458
474
  // Fall through to auto-detect if preferred provider key not found.
459
475
  } else if (preferredImageProvider === "antigravity" && modelRegistry) {
460
- const antigravity = await findAntigravityCredentials(modelRegistry);
476
+ const antigravity = await findAntigravityCredentials(modelRegistry, sessionId);
461
477
  if (antigravity) return antigravity;
462
478
  // Fall through to auto-detect if preferred provider key not found.
463
479
  } else if (preferredImageProvider === "gemini") {
@@ -477,7 +493,7 @@ async function findImageApiKey(
477
493
  if (openAI) return openAI;
478
494
 
479
495
  if (modelRegistry) {
480
- const antigravity = await findAntigravityCredentials(modelRegistry);
496
+ const antigravity = await findAntigravityCredentials(modelRegistry, sessionId);
481
497
  if (antigravity) return antigravity;
482
498
  }
483
499
 
@@ -589,12 +605,21 @@ function collectInlineImages(parts: GeminiPart[]): InlineImageData[] {
589
605
  return images;
590
606
  }
591
607
 
592
- function isOpenAIHostedImageModel(model: Model | undefined): model is Model {
608
+ export function isOpenAIHostedImageModel(model: Model | undefined): model is Model {
593
609
  if (!model) return false;
594
- if (model.provider !== "openai" && model.provider !== "openai-codex") return false;
610
+ // The hosted image_generation tool is only available over the Responses API.
595
611
  if (model.api !== "openai-responses" && model.api !== "openai-codex-responses") return false;
596
- const modelId = model.id.toLowerCase();
597
- return modelId.startsWith("gpt-") || modelId === "o3" || modelId.startsWith("o3-");
612
+ // Declarative capability: any provider (e.g. an OpenAI-compatible proxy
613
+ // fronting gpt-image) whose model advertises image output can drive
614
+ // generate_image, routed to the model's own baseUrl with registry auth.
615
+ if (model.output?.includes("image")) return true;
616
+ // First-party heuristic: OpenAI/OpenAI code GPT and o3 models generate
617
+ // images inline through the hosted tool without a declared output modality.
618
+ if (model.provider === "openai" || model.provider === "openai-codex") {
619
+ const modelId = model.id.toLowerCase();
620
+ return modelId.startsWith("gpt-") || modelId === "o3" || modelId.startsWith("o3-");
621
+ }
622
+ return false;
598
623
  }
599
624
 
600
625
  function getOpenAIHostedImageProvider(model: Model): ImageProvider {
@@ -995,7 +1020,9 @@ export const imageGenTool: CustomTool<typeof imageGenSchema, ImageGenToolDetails
995
1020
 
996
1021
  if (provider === "antigravity") {
997
1022
  if (!apiKey.projectId) {
998
- throw new Error("Missing projectId in antigravity credentials");
1023
+ throw new Error(
1024
+ "Antigravity image generation requires a projectId, but the stored google-antigravity credential only contains an access token. Run the google-antigravity login flow again so the projectId is stored, then retry.",
1025
+ );
999
1026
  }
1000
1027
 
1001
1028
  const prompt = assemblePrompt(params);
@@ -13,7 +13,11 @@ import { LspTool } from "../lsp";
13
13
  import type { WorkflowGateEmitter } from "../modes/shared/agent-wire/unattended-session";
14
14
  import type { PlanModeState } from "../plan-mode/state";
15
15
  import type { AgentRegistry } from "../registry/agent-registry";
16
- import type { ForkContextSeed, ForkContextSeedOptions } from "../session/agent-session";
16
+ import type {
17
+ ForkContextSeed,
18
+ ForkContextSeedOptions,
19
+ PurgeQueuedCustomMessagesResult,
20
+ } from "../session/agent-session";
17
21
  import type { ArtifactManager } from "../session/artifacts";
18
22
  import type { ClientBridge } from "../session/client-bridge";
19
23
  import type { CustomMessage } from "../session/messages";
@@ -162,6 +166,8 @@ export interface ToolSession {
162
166
  /** Agent identity used for IRC routing. Returns the registry id (e.g. "0-Main", "0-AuthLoader"). */
163
167
  getAgentId?: () => string | null;
164
168
  /** Look up a registered tool by name (used by the eval js backend's tool bridge). */
169
+ /** Purge undelivered queued custom messages matching the predicate. Returns counts. */
170
+ purgeQueuedCustomMessages?: (predicate: (message: CustomMessage) => boolean) => PurgeQueuedCustomMessagesResult;
165
171
  getToolByName?: (name: string) => AgentTool | undefined;
166
172
  /** Agent registry for IRC routing across live sessions. */
167
173
  agentRegistry?: AgentRegistry;
@@ -78,19 +78,21 @@ export class InspectImageTool implements AgentTool<typeof inspectImageSchema, In
78
78
  };
79
79
 
80
80
  const activeModelPattern = this.session.getActiveModelString?.() ?? this.session.getModelString?.();
81
- const model =
82
- resolvePattern("pi/vision") ??
83
- resolvePattern("pi/default") ??
84
- resolvePattern(activeModelPattern) ??
85
- availableModels[0];
81
+ let model = resolvePattern("pi/default") ?? resolvePattern(activeModelPattern) ?? availableModels[0];
86
82
  if (!model) {
87
83
  throw new ToolError("Unable to resolve a model for inspect_image.");
88
84
  }
89
85
 
86
+ // inspect_image requires image input; if the resolved model is text-only,
87
+ // fall back to any available vision-capable model before failing.
90
88
  if (!model.input.includes("image")) {
91
- throw new ToolError(
92
- `Resolved model ${model.provider}/${model.id} does not support image input. Configure a vision-capable model for modelRoles.vision.`,
93
- );
89
+ const visionModel = availableModels.find(candidate => candidate.input.includes("image"));
90
+ if (!visionModel) {
91
+ throw new ToolError(
92
+ `Resolved model ${model.provider}/${model.id} does not support image input, and no vision-capable model is available. Configure a vision-capable model.`,
93
+ );
94
+ }
95
+ model = visionModel;
94
96
  }
95
97
 
96
98
  const apiKey = await modelRegistry.getApiKey(model);
package/src/tools/job.ts CHANGED
@@ -52,7 +52,7 @@ interface JobSnapshot {
52
52
  errorText?: string;
53
53
  }
54
54
 
55
- type CancelStatus = "cancelled" | "not_found" | "already_completed";
55
+ type CancelStatus = "cancelled" | "not_found" | "already_completed" | "already_cancelled";
56
56
 
57
57
  interface CancelOutcome {
58
58
  id: string;
@@ -115,10 +115,20 @@ export class JobTool implements AgentTool<typeof jobSchema, JobToolDetails> {
115
115
  for (const id of cancelIds) {
116
116
  const existing = manager.getJob(id);
117
117
  if (!existing || (ownerId && existing.ownerId !== ownerId)) {
118
- cancelOutcomes.push({ id, status: "not_found", message: `Background job not found: ${id}` });
118
+ const tombstone = manager.purgeMonitorTombstone(id, ownerFilter);
119
+ cancelOutcomes.push(
120
+ tombstone.found
121
+ ? {
122
+ id,
123
+ status: tombstone.status === "cancelled" ? "already_cancelled" : "already_completed",
124
+ message: `Monitor job ${id} already gone; purged queued notifications.`,
125
+ }
126
+ : { id, status: "not_found", message: `Background job not found: ${id}` },
127
+ );
119
128
  continue;
120
129
  }
121
130
  if (existing.status !== "running") {
131
+ if (existing.metadata?.monitor) manager.purgeMonitorTombstone(id, ownerFilter);
122
132
  cancelOutcomes.push({
123
133
  id,
124
134
  status: "already_completed",
@@ -47,6 +47,7 @@ export interface MonitorToolDetails {
47
47
  }
48
48
 
49
49
  const MONITOR_LABEL_MAX = 120;
50
+ const MAX_PENDING_MONITOR_NOTIFICATIONS = 3;
50
51
 
51
52
  function buildMonitorLabel(params: MonitorParams): string {
52
53
  const base = `[monitor:${params.kind}] ${params.description}`;
@@ -89,36 +90,116 @@ export class MonitorTool implements AgentTool<typeof monitorSchema, MonitorToolD
89
90
  const ownerId = this.session.getAgentId?.() ?? undefined;
90
91
  const bash = new BashTool(this.session);
91
92
  let deliveredFirstLine = false;
93
+ const controller = { closed: false };
94
+ let currentJobId = "";
95
+ let sequence = 0;
96
+ let latestLine: string | undefined;
97
+ let coalescedCount = 0;
98
+ let flushScheduled = false;
99
+ // Count of notification *sends* (not live queue depth): once it exceeds the
100
+ // cap, each new send first purges older queued notifications for this task,
101
+ // keeping the queue bounded and latest-biased.
102
+ let pendingNotifications = 0;
103
+ const isMonitorMessage = (message: { customType?: string; details?: unknown }) =>
104
+ message.customType === "task-notification" &&
105
+ (message.details as { taskId?: string } | undefined)?.taskId === currentJobId;
106
+ const flushLatest = () => {
107
+ if (!persistent || latestLine === undefined) return;
108
+ const line = latestLine;
109
+ const count = coalescedCount;
110
+ latestLine = undefined;
111
+ coalescedCount = 0;
112
+ flushScheduled = false;
113
+ sendNotification(line, currentJobId, count);
114
+ };
115
+ const closeMonitor = (mode: "purge" | "flush") => {
116
+ // "flush" (natural process exit): deliver the newest pending line so the
117
+ // final state is never lost, then stop. "purge" (explicit cancel / registry
118
+ // eviction): drop the queued backlog. Non-persistent monitors keep their one
119
+ // notification, so they never purge.
120
+ if (mode === "flush") {
121
+ flushLatest();
122
+ controller.closed = true;
123
+ return;
124
+ }
125
+ controller.closed = true;
126
+ if (!persistent) return;
127
+ return this.session.purgeQueuedCustomMessages?.(isMonitorMessage);
128
+ };
129
+ const sendNotification = (line: string, jobId: string, count: number) => {
130
+ if (controller.closed) return;
131
+ const notificationId = `${jobId}:${sequence}`;
132
+ const suffix = count > 0 ? `\n(+${count} earlier lines)` : "";
133
+ const content = `<task-notification>\nMonitor task ${jobId} (${params.kind}: ${params.description}) emitted latest state:\n${line}${suffix}\n</task-notification>`;
134
+ const details = {
135
+ taskId: jobId,
136
+ kind: params.kind,
137
+ description: params.description,
138
+ monitor: true,
139
+ notificationId,
140
+ sequence,
141
+ coalescedCount: count,
142
+ };
143
+ pendingNotifications += 1;
144
+ if (pendingNotifications > MAX_PENDING_MONITOR_NOTIFICATIONS) {
145
+ this.session.purgeQueuedCustomMessages?.(
146
+ m =>
147
+ m.customType === "task-notification" &&
148
+ (m.details as { taskId?: string; notificationId?: string } | undefined)?.taskId === jobId &&
149
+ (m.details as { notificationId?: string } | undefined)?.notificationId !== notificationId,
150
+ );
151
+ pendingNotifications = MAX_PENDING_MONITOR_NOTIFICATIONS;
152
+ }
153
+ const sendPromise = this.session.sendCustomMessage?.(
154
+ { customType: "task-notification", content, display: false, attribution: "agent", details },
155
+ { triggerTurn: true, deliverAs: "followUp" },
156
+ );
157
+ if (sendPromise) {
158
+ void sendPromise.catch(error => {
159
+ logger.warn("Monitor task-notification delivery failed", {
160
+ error: error instanceof Error ? error.message : String(error),
161
+ });
162
+ });
163
+ } else {
164
+ this.session.steer?.({ customType: "task-notification", content, details });
165
+ }
166
+ };
167
+ const schedulePersistentNotification = (line: string) => {
168
+ latestLine = line;
169
+ sequence += 1;
170
+ coalescedCount += flushScheduled ? 1 : 0;
171
+ if (flushScheduled) return;
172
+ flushScheduled = true;
173
+ queueMicrotask(flushLatest);
174
+ };
92
175
  const monitorJob = await bash.startMonitorJob(
93
176
  { command: params.command, timeout: params.timeout },
94
177
  {
95
178
  ownerId,
96
179
  label,
97
180
  ctx: context,
181
+ shouldAcceptRawLine: () => !controller.closed,
182
+ lifecycle: {
183
+ onCancel: () => closeMonitor("purge"),
184
+ onTerminal: () => closeMonitor("flush"),
185
+ onEvict: () => closeMonitor("purge"),
186
+ onTombstonePurge: () => closeMonitor("purge"),
187
+ },
98
188
  onRawLine: (line, jobId) => {
189
+ if (controller.closed) return;
190
+ currentJobId = jobId;
99
191
  if (!persistent && deliveredFirstLine) return;
100
192
  deliveredFirstLine = true;
101
- const content = `<task-notification>\nMonitor task ${jobId} (${params.kind}: ${params.description}) emitted:\n${line}\n</task-notification>`;
102
- const details = { taskId: jobId, kind: params.kind, description: params.description };
103
- const sendPromise = this.session.sendCustomMessage?.(
104
- { customType: "task-notification", content, display: false, attribution: "agent", details },
105
- { triggerTurn: true, deliverAs: "followUp" },
106
- );
107
- if (sendPromise) {
108
- void sendPromise.catch(error => {
109
- logger.warn("Monitor task-notification delivery failed", {
110
- error: error instanceof Error ? error.message : String(error),
111
- });
112
- });
113
- } else {
114
- this.session.steer?.({ customType: "task-notification", content, details });
115
- }
116
- if (!persistent) {
117
- manager.cancel(jobId, ownerId ? { ownerId } : undefined);
193
+ if (persistent) {
194
+ schedulePersistentNotification(line);
195
+ return;
118
196
  }
197
+ sendNotification(line, jobId, 0);
198
+ manager.cancel(jobId, ownerId ? { ownerId } : undefined);
119
199
  },
120
200
  },
121
201
  );
202
+ currentJobId = monitorJob.jobId;
122
203
 
123
204
  const startedText = `Monitor started · task ${monitorJob.jobId} · persistent: ${persistent}`;
124
205
 
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Generate commit messages from diffs using a smol, fast model.
2
+ * Generate commit messages from diffs using the default model.
3
3
  * Follows the same pattern as title-generator.ts.
4
4
  */
5
5
  import type { ThinkingLevel } from "@gajae-code/agent-core";
@@ -9,7 +9,6 @@ import { logger, prompt } from "@gajae-code/utils";
9
9
  import type { ModelRegistry } from "../config/model-registry";
10
10
  import { resolveModelRoleValue } from "../config/model-resolver";
11
11
  import type { Settings } from "../config/settings";
12
- import MODEL_PRIO from "../priority.json" with { type: "json" };
13
12
  import commitSystemPrompt from "../prompts/system/commit-message-system.md" with { type: "text" };
14
13
  import { toReasoningEffort } from "../thinking";
15
14
 
@@ -36,7 +35,7 @@ function filterDiffNoise(diff: string): string {
36
35
  return filtered.join("\n");
37
36
  }
38
37
 
39
- function getSmolModelCandidates(
38
+ function getModelCandidates(
40
39
  registry: ModelRegistry,
41
40
  settings: Settings,
42
41
  ): Array<{ model: Model<Api>; thinkingLevel?: ThinkingLevel }> {
@@ -51,18 +50,12 @@ function getSmolModelCandidates(
51
50
  };
52
51
 
53
52
  const matchPreferences = { usageOrder: settings.getStorage()?.getModelUsageOrder() };
54
- const configuredSmol = resolveModelRoleValue(settings.getModelRole("smol"), availableModels, {
53
+ const configured = resolveModelRoleValue(settings.getModelRole("default"), availableModels, {
55
54
  settings,
56
55
  matchPreferences,
57
56
  modelRegistry: registry,
58
57
  });
59
- addCandidate(configuredSmol.model, configuredSmol.thinkingLevel);
60
-
61
- for (const pattern of MODEL_PRIO.smol) {
62
- const needle = pattern.toLowerCase();
63
- addCandidate(availableModels.find(m => m.id.toLowerCase() === needle));
64
- addCandidate(availableModels.find(m => m.id.toLowerCase().includes(needle)));
65
- }
58
+ addCandidate(configured.model, configured.thinkingLevel);
66
59
 
67
60
  for (const model of availableModels) {
68
61
  addCandidate(model);
@@ -81,9 +74,9 @@ export async function generateCommitMessage(
81
74
  settings: Settings,
82
75
  sessionId?: string,
83
76
  ): Promise<string | null> {
84
- const candidates = getSmolModelCandidates(registry, settings);
77
+ const candidates = getModelCandidates(registry, settings);
85
78
  if (candidates.length === 0) {
86
- logger.debug("commit-msg-generator: no smol model found");
79
+ logger.debug("commit-msg-generator: no model found");
87
80
  return null;
88
81
  }
89
82
 
@@ -40,7 +40,7 @@ function getTitleModel(registry: ModelRegistry, settings: Settings, currentModel
40
40
  const availableModels = registry.getAvailable();
41
41
  if (availableModels.length === 0) return undefined;
42
42
 
43
- const titleModel = resolveRoleSelection(["commit", "smol"], settings, availableModels, registry)?.model;
43
+ const titleModel = resolveRoleSelection(["default"], settings, availableModels, registry)?.model;
44
44
  if (titleModel) return titleModel;
45
45
 
46
46
  if (currentModel) return currentModel;
@@ -1,29 +0,0 @@
1
- /**
2
- * Pure mapping from `gjc --mode rpc` event frames (docs/rpc.md) to bounded owner event kinds
3
- * and {@link ObservedSignal}s. The owner feeds raw frames through this mapper and emits the
4
- * result via its single-writer #emit — the mapper itself performs NO IO and NO appends.
5
- *
6
- * Hard rule: evidence is BOUNDED — only ids, names, categories, statuses, cursors, timestamps,
7
- * and short codes/messages. Never assistant text, message deltas, command output, or raw args.
8
- */
9
- import type { ObservedSignal } from "./types";
10
- export interface MappedFrame {
11
- /** Owner event kind (rpc_*). */
12
- kind: string;
13
- /** Bounded observed signal, or null when the frame carries no user-facing signal. */
14
- signal: ObservedSignal | null;
15
- /** Bounded evidence — ids/names/statuses/cursors/timestamps/short codes only. */
16
- evidence: Record<string, unknown>;
17
- /** Severity for the emitted event. */
18
- severity: "info" | "warn" | "critical";
19
- /** Never-drop frames (must be enqueued in order, never coalesced away). */
20
- semantic: boolean;
21
- /** Coalescing key for high-frequency non-semantic frames (message id / tool id); null otherwise. */
22
- coalesceKey: string | null;
23
- }
24
- export declare function isTestRunnerTool(toolName?: unknown, command?: unknown): boolean;
25
- /**
26
- * Map a single RPC frame. Returns null for frames that carry no observability value
27
- * (or that the adapter handles itself: `ready`, `response`).
28
- */
29
- export declare function mapRpcFrame(frame: Record<string, unknown>): MappedFrame | null;