@gajae-code/coding-agent 0.4.2 → 0.4.4

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 (115) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/types/async/job-manager.d.ts +44 -1
  3. package/dist/types/cli/setup-cli.d.ts +14 -1
  4. package/dist/types/commands/coordinator.d.ts +19 -0
  5. package/dist/types/commands/mcp-serve.d.ts +24 -0
  6. package/dist/types/commands/setup.d.ts +41 -0
  7. package/dist/types/commit/model-selection.d.ts +1 -1
  8. package/dist/types/config/model-registry.d.ts +3 -1
  9. package/dist/types/config/model-resolver.d.ts +1 -19
  10. package/dist/types/config/models-config-schema.d.ts +12 -0
  11. package/dist/types/config/settings-schema.d.ts +15 -1
  12. package/dist/types/coordinator/contract.d.ts +4 -0
  13. package/dist/types/coordinator-mcp/policy.d.ts +24 -0
  14. package/dist/types/coordinator-mcp/safety.d.ts +26 -0
  15. package/dist/types/coordinator-mcp/server.d.ts +52 -0
  16. package/dist/types/extensibility/extensions/types.d.ts +13 -0
  17. package/dist/types/gjc-runtime/goal-mode-request.d.ts +8 -1
  18. package/dist/types/gjc-runtime/session-state-sidecar.d.ts +13 -0
  19. package/dist/types/harness-control-plane/types.d.ts +7 -2
  20. package/dist/types/modes/acp/acp-event-mapper.d.ts +2 -0
  21. package/dist/types/modes/components/custom-editor.d.ts +7 -0
  22. package/dist/types/modes/components/hook-selector.d.ts +11 -0
  23. package/dist/types/modes/shared/agent-wire/command-contract.d.ts +18 -0
  24. package/dist/types/modes/shared/agent-wire/event-contract.d.ts +84 -0
  25. package/dist/types/modes/shared/agent-wire/event-envelope.d.ts +14 -7
  26. package/dist/types/modes/shared/agent-wire/event-observation.d.ts +37 -0
  27. package/dist/types/modes/shared/agent-wire/protocol.d.ts +13 -34
  28. package/dist/types/session/agent-session.d.ts +12 -1
  29. package/dist/types/session/session-manager.d.ts +1 -1
  30. package/dist/types/setup/hermes-setup.d.ts +71 -0
  31. package/dist/types/task/render.d.ts +7 -1
  32. package/dist/types/tools/bash.d.ts +2 -0
  33. package/dist/types/tools/browser/actions.d.ts +54 -0
  34. package/dist/types/tools/browser.d.ts +80 -0
  35. package/dist/types/tools/image-gen.d.ts +1 -0
  36. package/dist/types/tools/index.d.ts +3 -1
  37. package/dist/types/tools/job.d.ts +1 -1
  38. package/dist/types/tools/subagent-render.d.ts +25 -0
  39. package/dist/types/tools/subagent.d.ts +5 -1
  40. package/package.json +7 -7
  41. package/src/async/job-manager.ts +163 -2
  42. package/src/cli/setup-cli.ts +86 -2
  43. package/src/cli.ts +2 -0
  44. package/src/commands/coordinator.ts +70 -0
  45. package/src/commands/mcp-serve.ts +62 -0
  46. package/src/commands/setup.ts +30 -1
  47. package/src/commands/ultragoal.ts +7 -1
  48. package/src/commit/agentic/index.ts +2 -2
  49. package/src/commit/model-selection.ts +7 -22
  50. package/src/commit/pipeline.ts +2 -2
  51. package/src/config/model-registry.ts +17 -9
  52. package/src/config/model-resolver.ts +14 -84
  53. package/src/config/models-config-schema.ts +2 -0
  54. package/src/config/settings-schema.ts +14 -1
  55. package/src/coordinator/contract.ts +20 -0
  56. package/src/coordinator-mcp/policy.ts +160 -0
  57. package/src/coordinator-mcp/safety.ts +80 -0
  58. package/src/coordinator-mcp/server.ts +1316 -0
  59. package/src/extensibility/extensions/types.ts +13 -0
  60. package/src/gjc-runtime/goal-mode-request.ts +21 -1
  61. package/src/gjc-runtime/session-state-sidecar.ts +79 -0
  62. package/src/harness-control-plane/owner.ts +3 -3
  63. package/src/harness-control-plane/rpc-adapter.ts +7 -1
  64. package/src/harness-control-plane/types.ts +8 -11
  65. package/src/internal-urls/docs-index.generated.ts +6 -5
  66. package/src/memories/index.ts +1 -1
  67. package/src/modes/acp/acp-agent.ts +17 -9
  68. package/src/modes/acp/acp-event-mapper.ts +33 -1
  69. package/src/modes/components/custom-editor.ts +19 -3
  70. package/src/modes/components/hook-selector.ts +109 -5
  71. package/src/modes/controllers/extension-ui-controller.ts +16 -1
  72. package/src/modes/controllers/input-controller.ts +27 -7
  73. package/src/modes/controllers/selector-controller.ts +7 -1
  74. package/src/modes/interactive-mode.ts +3 -1
  75. package/src/modes/rpc/rpc-client.ts +16 -3
  76. package/src/modes/rpc/rpc-mode.ts +5 -2
  77. package/src/modes/shared/agent-wire/command-contract.ts +18 -0
  78. package/src/modes/shared/agent-wire/event-contract.ts +147 -0
  79. package/src/modes/shared/agent-wire/event-envelope.ts +35 -16
  80. package/src/modes/shared/agent-wire/event-observation.ts +397 -0
  81. package/src/modes/shared/agent-wire/protocol.ts +24 -81
  82. package/src/modes/utils/context-usage.ts +2 -2
  83. package/src/prompts/agents/architect.md +6 -0
  84. package/src/prompts/agents/critic.md +6 -0
  85. package/src/prompts/agents/explore.md +1 -1
  86. package/src/prompts/agents/plan.md +1 -1
  87. package/src/prompts/agents/planner.md +8 -1
  88. package/src/prompts/agents/reviewer.md +1 -1
  89. package/src/prompts/tools/browser.md +3 -2
  90. package/src/runtime-mcp/manager.ts +15 -2
  91. package/src/sdk.ts +3 -1
  92. package/src/session/agent-session.ts +66 -4
  93. package/src/session/session-manager.ts +1 -1
  94. package/src/setup/hermes/templates/operator-instructions.v1.md +29 -0
  95. package/src/setup/hermes-setup.ts +429 -0
  96. package/src/task/agents.ts +1 -1
  97. package/src/task/index.ts +2 -0
  98. package/src/task/render.ts +14 -0
  99. package/src/tools/ask.ts +30 -10
  100. package/src/tools/bash.ts +6 -1
  101. package/src/tools/browser/actions.ts +189 -0
  102. package/src/tools/browser.ts +91 -1
  103. package/src/tools/image-gen.ts +42 -15
  104. package/src/tools/index.ts +7 -1
  105. package/src/tools/inspect-image.ts +10 -8
  106. package/src/tools/job.ts +12 -2
  107. package/src/tools/monitor.ts +98 -17
  108. package/src/tools/renderers.ts +2 -0
  109. package/src/tools/subagent-render.ts +160 -0
  110. package/src/tools/subagent.ts +49 -7
  111. package/src/utils/commit-message-generator.ts +6 -13
  112. package/src/utils/title-generator.ts +1 -1
  113. package/dist/types/harness-control-plane/frame-mapper.d.ts +0 -29
  114. package/src/harness-control-plane/frame-mapper.ts +0 -286
  115. package/src/priority.json +0 -37
@@ -0,0 +1,160 @@
1
+ /**
2
+ * TUI renderer for the `subagent` tool.
3
+ *
4
+ * The await panel surfaces each awaited subagent's live streaming status at
5
+ * parity with the inline `task` panel by reusing `renderSubagentLiveProgress`.
6
+ * Falls back to a `running, no activity yet` placeholder when a live producer
7
+ * exists but has not emitted yet, and to a static status line when no live
8
+ * producer is available (resumed-from-disk or backward-compat records).
9
+ */
10
+ import type { Component } from "@gajae-code/tui";
11
+ import { Text } from "@gajae-code/tui";
12
+ import type { RenderResultOptions } from "../extensibility/custom-tools/types";
13
+ import type { Theme } from "../modes/theme/theme";
14
+ import { renderSubagentLiveProgress } from "../task/render";
15
+ import { Ellipsis, Hasher, type RenderCache, renderStatusLine } from "../tui";
16
+ import {
17
+ formatDuration,
18
+ formatStatusIcon,
19
+ getPreviewLines,
20
+ replaceTabs,
21
+ type ToolUIStatus,
22
+ truncateToWidth,
23
+ } from "./render-utils";
24
+ import type { SubagentSnapshot, SubagentToolDetails } from "./subagent";
25
+
26
+ const PREVIEW_LINES_COLLAPSED = 1;
27
+ const PREVIEW_LINES_EXPANDED = 4;
28
+ const PREVIEW_LINE_WIDTH = 80;
29
+
30
+ function statusIconKind(status: SubagentSnapshot["status"]): ToolUIStatus {
31
+ switch (status) {
32
+ case "completed":
33
+ case "already_completed":
34
+ return "success";
35
+ case "failed":
36
+ return "error";
37
+ case "cancelled":
38
+ case "not_found":
39
+ return "warning";
40
+ case "queued":
41
+ return "pending";
42
+ default:
43
+ return "info";
44
+ }
45
+ }
46
+
47
+ function renderSubagentSnapshot(
48
+ snapshot: SubagentSnapshot,
49
+ expanded: boolean,
50
+ theme: Theme,
51
+ spinnerFrame: number | undefined,
52
+ ): string[] {
53
+ const lines: string[] = [];
54
+ const icon = formatStatusIcon(
55
+ statusIconKind(snapshot.status),
56
+ theme,
57
+ snapshot.status === "running" ? spinnerFrame : undefined,
58
+ );
59
+ const id = theme.fg("muted", snapshot.id);
60
+ const status = theme.fg("dim", snapshot.status);
61
+ const duration = theme.fg("dim", formatDuration(snapshot.durationMs));
62
+ lines.push(`${icon} ${id} ${status} ${duration}`);
63
+
64
+ // Static receipt fields (parity with the markdown content for non-await actions).
65
+ if (snapshot.jobId !== snapshot.id) lines.push(` ${theme.fg("dim", `Job: ${snapshot.jobId}`)}`);
66
+ if (snapshot.agent && snapshot.agent !== "unknown") {
67
+ lines.push(` ${theme.fg("dim", `Agent: ${snapshot.agent} (${snapshot.agentSource})`)}`);
68
+ }
69
+ if (snapshot.description) lines.push(` ${theme.fg("dim", `Description: ${snapshot.description}`)}`);
70
+ if (snapshot.outputRef) lines.push(` ${theme.fg("dim", `Output: ${snapshot.outputRef}`)}`);
71
+ if (snapshot.assignment) {
72
+ lines.push(` ${theme.fg("dim", "Assignment:")}`);
73
+ for (const al of snapshot.assignment.split("\n")) lines.push(` ${theme.fg("toolOutput", replaceTabs(al))}`);
74
+ }
75
+
76
+ if (snapshot.progress) {
77
+ // Live streaming panel (full task-panel parity), indented under the header.
78
+ for (const pl of renderSubagentLiveProgress(snapshot.progress, expanded, theme, spinnerFrame)) {
79
+ lines.push(` ${pl}`);
80
+ }
81
+ } else if (snapshot.liveProgressAvailable && (snapshot.status === "running" || snapshot.status === "queued")) {
82
+ lines.push(` ${theme.fg("dim", "running, no activity yet")}`);
83
+ }
84
+
85
+ const preview = snapshot.errorText?.trim() || snapshot.resultText?.trim();
86
+ if (preview) {
87
+ const maxLines = expanded ? PREVIEW_LINES_EXPANDED : PREVIEW_LINES_COLLAPSED;
88
+ const tone = snapshot.errorText ? "error" : "dim";
89
+ for (const pl of getPreviewLines(preview, maxLines, PREVIEW_LINE_WIDTH, Ellipsis.Unicode)) {
90
+ lines.push(` ${theme.fg(tone, replaceTabs(pl))}`);
91
+ }
92
+ if (snapshot.truncated) {
93
+ lines.push(
94
+ ` ${theme.fg("dim", "Preview truncated; use the output ref or explicit ids with `verbosity=full` for more.")}`,
95
+ );
96
+ }
97
+ }
98
+
99
+ if (snapshot.guidance) lines.push(` ${theme.fg("dim", snapshot.guidance)}`);
100
+ return lines;
101
+ }
102
+
103
+ export const subagentToolRenderer = {
104
+ inline: true,
105
+
106
+ renderCall(_args: unknown, _options: RenderResultOptions, theme: Theme): Component {
107
+ return new Text(renderStatusLine({ icon: "pending", title: "Subagent" }, theme), 0, 0);
108
+ },
109
+
110
+ renderResult(
111
+ result: { content: Array<{ type: string; text?: string }>; details?: SubagentToolDetails },
112
+ options: RenderResultOptions,
113
+ theme: Theme,
114
+ ): Component {
115
+ const subagents = result.details?.subagents ?? [];
116
+ if (subagents.length === 0) {
117
+ const fallback = result.content.find(c => c.type === "text")?.text || "No subagents";
118
+ return new Text(theme.fg("dim", truncateToWidth(fallback, 100)), 0, 0);
119
+ }
120
+
121
+ const runningCount = subagents.filter(s => s.status === "running").length;
122
+
123
+ let cached: RenderCache | undefined;
124
+ return {
125
+ render(width: number): string[] {
126
+ const expanded = options.expanded;
127
+ const spinnerFrame = options.spinnerFrame ?? 0;
128
+ const key = new Hasher().bool(expanded).u32(width).u32(spinnerFrame).digest();
129
+ if (cached?.key === key) return cached.lines;
130
+
131
+ const header = renderStatusLine(
132
+ {
133
+ icon: runningCount > 0 ? "info" : "success",
134
+ spinnerFrame: runningCount > 0 ? options.spinnerFrame : undefined,
135
+ title: "Subagent",
136
+ description:
137
+ runningCount > 0
138
+ ? `awaiting ${runningCount} of ${subagents.length}`
139
+ : `${subagents.length} ${subagents.length === 1 ? "subagent" : "subagents"}`,
140
+ },
141
+ theme,
142
+ );
143
+
144
+ const lines: string[] = [header];
145
+ for (const snapshot of subagents) {
146
+ lines.push(...renderSubagentSnapshot(snapshot, expanded, theme, options.spinnerFrame));
147
+ }
148
+
149
+ const out = lines.map(l => (l.length > 0 ? truncateToWidth(l, width, Ellipsis.Omit) : ""));
150
+ cached = { key, lines: out };
151
+ return out;
152
+ },
153
+ invalidate() {
154
+ cached = undefined;
155
+ },
156
+ };
157
+ },
158
+
159
+ mergeCallAndResult: true,
160
+ };
@@ -4,7 +4,7 @@ import { prompt } from "@gajae-code/utils";
4
4
  import * as z from "zod/v4";
5
5
  import { type AsyncJob, AsyncJobManager, type SubagentRecord } from "../async";
6
6
  import subagentDescription from "../prompts/tools/subagent.md" with { type: "text" };
7
- import type { AgentSource } from "../task/types";
7
+ import type { AgentProgress, AgentSource } from "../task/types";
8
8
  import { Ellipsis, truncateToWidth } from "../tui";
9
9
  import type { ToolSession } from "./index";
10
10
  import { replaceTabs } from "./render-utils";
@@ -63,6 +63,10 @@ export interface SubagentSnapshot {
63
63
  outputRef?: string;
64
64
  truncated?: boolean;
65
65
  guidance?: string;
66
+ /** Live streaming progress for the awaited subagent (await panel only; UI detail). */
67
+ progress?: AgentProgress;
68
+ /** True when a live in-session progress producer exists for this subagent. */
69
+ liveProgressAvailable?: boolean;
66
70
  }
67
71
 
68
72
  export interface SubagentToolDetails {
@@ -322,10 +326,10 @@ export class SubagentTool implements AgentTool<typeof subagentSchema, SubagentTo
322
326
  manager.watchJobs(watchedJobIds);
323
327
  const progressTimer = onUpdate
324
328
  ? setInterval(() => {
325
- onUpdate(this.#progressResult(manager, records));
329
+ onUpdate(this.#progressResult(manager, records, true));
326
330
  }, 500)
327
331
  : undefined;
328
- onUpdate?.(this.#progressResult(manager, records));
332
+ onUpdate?.(this.#progressResult(manager, records, true));
329
333
 
330
334
  let timedOut = false;
331
335
  try {
@@ -355,6 +359,7 @@ export class SubagentTool implements AgentTool<typeof subagentSchema, SubagentTo
355
359
  notFoundIds,
356
360
  timedOut,
357
361
  verbosity: params.verbosity ?? "receipt",
362
+ attachLiveProgress: true,
358
363
  });
359
364
  }
360
365
 
@@ -450,17 +455,29 @@ export class SubagentTool implements AgentTool<typeof subagentSchema, SubagentTo
450
455
  return ids.filter(id => !this.#findVisibleRecord(manager, id, ownerFilter));
451
456
  }
452
457
 
453
- #progressResult(manager: AsyncJobManager, records: SubagentRecord[]): AgentToolResult<SubagentToolDetails> {
458
+ #progressResult(
459
+ manager: AsyncJobManager,
460
+ records: SubagentRecord[],
461
+ attachLiveProgress = false,
462
+ ): AgentToolResult<SubagentToolDetails> {
454
463
  return {
455
464
  content: [{ type: "text", text: "" }],
456
- details: { subagents: this.#recordSnapshots(manager, records, false, "receipt", new Set()) },
465
+ details: {
466
+ subagents: this.#recordSnapshots(manager, records, false, "receipt", new Set(), attachLiveProgress),
467
+ },
457
468
  };
458
469
  }
459
470
 
460
471
  async #buildRecordResult(
461
472
  manager: AsyncJobManager,
462
473
  records: SubagentRecord[],
463
- options: { title: string; notFoundIds?: string[]; timedOut?: boolean; verbosity?: SubagentParams["verbosity"] },
474
+ options: {
475
+ title: string;
476
+ notFoundIds?: string[];
477
+ timedOut?: boolean;
478
+ verbosity?: SubagentParams["verbosity"];
479
+ attachLiveProgress?: boolean;
480
+ },
464
481
  ): Promise<AgentToolResult<SubagentToolDetails>> {
465
482
  const verifiedOutputIds = await this.#verifiedOutputIds(records);
466
483
  const snapshots = this.#recordSnapshots(
@@ -469,6 +486,7 @@ export class SubagentTool implements AgentTool<typeof subagentSchema, SubagentTo
469
486
  options.timedOut,
470
487
  options.verbosity ?? "receipt",
471
488
  verifiedOutputIds,
489
+ options.attachLiveProgress ?? false,
472
490
  );
473
491
  for (const id of options.notFoundIds ?? []) {
474
492
  snapshots.push(this.#missingSnapshot(id, "not_found", "No visible detached subagent matches this id."));
@@ -513,8 +531,28 @@ export class SubagentTool implements AgentTool<typeof subagentSchema, SubagentTo
513
531
  timedOut = false,
514
532
  verbosity: SubagentParams["verbosity"] = "receipt",
515
533
  verifiedOutputIds: ReadonlySet<string>,
534
+ attachLiveProgress = false,
516
535
  ): SubagentSnapshot[] {
517
- return records.map(record => this.#recordSnapshot(manager, record, timedOut, verbosity, verifiedOutputIds));
536
+ return records.map(record =>
537
+ this.#recordSnapshot(manager, record, timedOut, verbosity, verifiedOutputIds, attachLiveProgress),
538
+ );
539
+ }
540
+
541
+ #liveProgressFields(
542
+ manager: AsyncJobManager,
543
+ record: SubagentRecord,
544
+ attachLiveProgress: boolean,
545
+ ): Pick<SubagentSnapshot, "progress" | "liveProgressAvailable"> {
546
+ if (!attachLiveProgress) return {};
547
+ const liveProgressAvailable = manager.hasLiveSubagent(record.subagentId);
548
+ // Only surface progress when a live producer exists; stale/retained progress
549
+ // for a record with no live producer must degrade to a static snapshot (AC5).
550
+ if (!liveProgressAvailable) return { liveProgressAvailable: false };
551
+ const progress = manager.getSubagentProgress(record.subagentId);
552
+ return {
553
+ liveProgressAvailable: true,
554
+ ...(progress ? { progress } : {}),
555
+ };
518
556
  }
519
557
 
520
558
  #recordSnapshot(
@@ -523,7 +561,9 @@ export class SubagentTool implements AgentTool<typeof subagentSchema, SubagentTo
523
561
  timedOut = false,
524
562
  verbosity: SubagentParams["verbosity"] = "receipt",
525
563
  verifiedOutputIds: ReadonlySet<string>,
564
+ attachLiveProgress = false,
526
565
  ): SubagentSnapshot {
566
+ const liveFields = this.#liveProgressFields(manager, record, attachLiveProgress);
527
567
  const job = record.currentJobId ? manager.getJob(record.currentJobId) : undefined;
528
568
  if (job) {
529
569
  return {
@@ -531,6 +571,7 @@ export class SubagentTool implements AgentTool<typeof subagentSchema, SubagentTo
531
571
  id: record.subagentId,
532
572
  jobId: record.currentJobId ?? job.id,
533
573
  status: record.status,
574
+ ...liveFields,
534
575
  };
535
576
  }
536
577
  return {
@@ -542,6 +583,7 @@ export class SubagentTool implements AgentTool<typeof subagentSchema, SubagentTo
542
583
  agentSource: "bundled",
543
584
  durationMs: 0,
544
585
  ...(verifiedOutputIds.has(record.subagentId) ? { outputRef: `agent://${record.subagentId}` } : {}),
586
+ ...liveFields,
545
587
  };
546
588
  }
547
589
 
@@ -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;
@@ -1,286 +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
-
11
- export interface MappedFrame {
12
- /** Owner event kind (rpc_*). */
13
- kind: string;
14
- /** Bounded observed signal, or null when the frame carries no user-facing signal. */
15
- signal: ObservedSignal | null;
16
- /** Bounded evidence — ids/names/statuses/cursors/timestamps/short codes only. */
17
- evidence: Record<string, unknown>;
18
- /** Severity for the emitted event. */
19
- severity: "info" | "warn" | "critical";
20
- /** Never-drop frames (must be enqueued in order, never coalesced away). */
21
- semantic: boolean;
22
- /** Coalescing key for high-frequency non-semantic frames (message id / tool id); null otherwise. */
23
- coalesceKey: string | null;
24
- }
25
-
26
- const TEST_RE = /\b(bun test|npm test|yarn test|pnpm test|jest|vitest|pytest|go test|cargo test|mocha|ava)\b/i;
27
- const TOOL_STATUS_CODES = new Set([
28
- "aborted",
29
- "blocked",
30
- "cancelled",
31
- "complete",
32
- "completed",
33
- "error",
34
- "failed",
35
- "ok",
36
- "pending",
37
- "running",
38
- "skipped",
39
- "success",
40
- "timeout",
41
- ]);
42
-
43
- export function isTestRunnerTool(toolName?: unknown, command?: unknown): boolean {
44
- const name = typeof toolName === "string" ? toolName : "";
45
- const cmd = typeof command === "string" ? command : "";
46
- if (/test/i.test(name) && name !== "edit" && name !== "read") return true;
47
- return TEST_RE.test(cmd);
48
- }
49
-
50
- function str(v: unknown): string | undefined {
51
- return typeof v === "string" ? v : undefined;
52
- }
53
- function num(v: unknown): number | undefined {
54
- return typeof v === "number" ? v : undefined;
55
- }
56
- function boundedMessage(v: unknown): string | undefined {
57
- const s = typeof v === "string" ? v : undefined;
58
- return s === undefined ? undefined : s.slice(0, 200);
59
- }
60
- function boundedStatus(v: unknown): string | undefined {
61
- if (typeof v !== "string") return undefined;
62
- const status = v.trim().toLowerCase();
63
- return TOOL_STATUS_CODES.has(status) ? status : undefined;
64
- }
65
- function recordObject(v: unknown): Record<string, unknown> | undefined {
66
- return v && typeof v === "object" && !Array.isArray(v) ? (v as Record<string, unknown>) : undefined;
67
- }
68
- /** Extract a tool command from real AgentSessionEvent `args` or a flat fixture frame. Bounded use only — never persisted. */
69
- function toolCommand(frame: Record<string, unknown>): string | undefined {
70
- const args = recordObject(frame.args);
71
- const c = args?.command ?? args?.cmd ?? args?.commandLine;
72
- if (typeof c === "string") return c;
73
- return str(frame.command) ?? str(frame.commandLine);
74
- }
75
- /** Derive a tool status, honoring real `isError` booleans as well as bounded status strings. */
76
- function toolStatus(frame: Record<string, unknown>): string | undefined {
77
- if (frame.isError === true) return "error";
78
- const flatStatus = boundedStatus(frame.status);
79
- if (flatStatus) return flatStatus;
80
- for (const candidate of [frame.result, frame.partialResult]) {
81
- const result = recordObject(candidate);
82
- if (!result) continue;
83
- if (result.isError === true) return "error";
84
- const status = boundedStatus(result.status) ?? boundedStatus(recordObject(result.details)?.status);
85
- if (status) return status;
86
- }
87
- return undefined;
88
- }
89
-
90
- /**
91
- * Map a single RPC frame. Returns null for frames that carry no observability value
92
- * (or that the adapter handles itself: `ready`, `response`).
93
- */
94
- export function mapRpcFrame(frame: Record<string, unknown>): MappedFrame | null {
95
- const type = str(frame.type);
96
- if (!type || type === "ready" || type === "response") return null;
97
-
98
- switch (type) {
99
- case "agent_start":
100
- return {
101
- kind: "rpc_agent_started",
102
- signal: "SessionStart",
103
- evidence: {},
104
- severity: "info",
105
- semantic: true,
106
- coalesceKey: null,
107
- };
108
- case "turn_start":
109
- return {
110
- kind: "rpc_turn_started",
111
- signal: "prompt-accepted",
112
- evidence: {},
113
- severity: "info",
114
- semantic: true,
115
- coalesceKey: null,
116
- };
117
- case "turn_end":
118
- return {
119
- kind: "rpc_turn_ended",
120
- signal: null,
121
- evidence: {},
122
- severity: "info",
123
- semantic: false,
124
- coalesceKey: null,
125
- };
126
- case "message_start":
127
- case "message_update":
128
- case "message_end":
129
- return {
130
- kind: "rpc_message_activity",
131
- signal: null,
132
- evidence: { phase: type, messageId: str(frame.messageId) ?? null },
133
- severity: "info",
134
- semantic: false,
135
- coalesceKey: `message:${str(frame.messageId) ?? "msg"}`,
136
- };
137
- case "tool_execution_start": {
138
- const toolName = str(frame.toolName);
139
- const test = isTestRunnerTool(toolName, toolCommand(frame));
140
- return {
141
- kind: "rpc_tool_started",
142
- signal: test ? "test-running" : "tool-call",
143
- evidence: { toolId: str(frame.toolCallId) ?? null, toolName: toolName ?? null },
144
- severity: "info",
145
- semantic: true,
146
- coalesceKey: null,
147
- };
148
- }
149
- case "tool_execution_update": {
150
- const toolName = str(frame.toolName);
151
- const test = isTestRunnerTool(toolName, toolCommand(frame));
152
- return {
153
- kind: "rpc_tool_updated",
154
- signal: test ? "test-running" : null,
155
- evidence: { toolId: str(frame.toolCallId) ?? null, status: toolStatus(frame) ?? null },
156
- severity: "info",
157
- semantic: false,
158
- coalesceKey: `tool:${str(frame.toolCallId) ?? "tool"}`,
159
- };
160
- }
161
- case "tool_execution_end": {
162
- const toolName = str(frame.toolName);
163
- const test = isTestRunnerTool(toolName, toolCommand(frame));
164
- const status = toolStatus(frame);
165
- return {
166
- kind: "rpc_tool_ended",
167
- signal: test ? "test-running" : "tool-call",
168
- evidence: {
169
- toolId: str(frame.toolCallId) ?? null,
170
- toolName: toolName ?? null,
171
- status: status ?? null,
172
- exitCode: num(frame.exitCode) ?? null,
173
- },
174
- severity: status === "error" ? "warn" : "info",
175
- semantic: true,
176
- coalesceKey: null,
177
- };
178
- }
179
- case "host_tool_call":
180
- case "host_tool_cancel":
181
- return {
182
- kind: "rpc_host_tool",
183
- signal: "tool-call",
184
- evidence: { toolName: str(frame.toolName) ?? null },
185
- severity: "info",
186
- semantic: false,
187
- coalesceKey: null,
188
- };
189
- case "host_uri_request":
190
- case "host_uri_cancel":
191
- return {
192
- kind: "rpc_host_uri",
193
- signal: "tool-call",
194
- evidence: { operation: str(frame.operation) ?? null },
195
- severity: "info",
196
- semantic: false,
197
- coalesceKey: null,
198
- };
199
- case "auto_compaction_start":
200
- case "auto_compaction_end":
201
- return {
202
- kind: "rpc_compaction",
203
- signal: null,
204
- evidence: { phase: type },
205
- severity: "info",
206
- semantic: false,
207
- coalesceKey: null,
208
- };
209
- case "auto_retry_start":
210
- case "auto_retry_end":
211
- return {
212
- kind: "rpc_retry",
213
- signal: null,
214
- evidence: { phase: type, reason: boundedMessage(frame.reason) ?? null },
215
- severity: "warn",
216
- semantic: false,
217
- coalesceKey: null,
218
- };
219
- case "ttsr_triggered":
220
- return {
221
- kind: "rpc_ttsr",
222
- signal: "error",
223
- evidence: { reason: boundedMessage(frame.reason) ?? null },
224
- severity: "warn",
225
- semantic: true,
226
- coalesceKey: null,
227
- };
228
- case "todo_reminder":
229
- case "todo_auto_clear":
230
- return {
231
- kind: "rpc_todo",
232
- signal: null,
233
- evidence: { phase: type },
234
- severity: "info",
235
- semantic: false,
236
- coalesceKey: null,
237
- };
238
- case "extension_ui_request":
239
- return {
240
- kind: "rpc_extension_request",
241
- signal: "tool-call",
242
- evidence: { method: str(frame.method) ?? null },
243
- severity: "info",
244
- semantic: false,
245
- coalesceKey: null,
246
- };
247
- case "extension_error":
248
- return {
249
- kind: "rpc_extension_error",
250
- signal: "error",
251
- evidence: {
252
- code: str(frame.error) ? boundedMessage(frame.error) : null,
253
- extensionPath: str(frame.extensionPath) ?? null,
254
- },
255
- severity: "critical",
256
- semantic: true,
257
- coalesceKey: null,
258
- };
259
- case "agent_end": {
260
- const failed =
261
- Boolean(frame.error) ||
262
- frame.aborted === true ||
263
- str(frame.outcome) === "failed" ||
264
- str(frame.outcome) === "aborted";
265
- return failed
266
- ? {
267
- kind: "rpc_agent_failed",
268
- signal: "error",
269
- evidence: { outcome: str(frame.outcome) ?? "failed" },
270
- severity: "critical",
271
- semantic: true,
272
- coalesceKey: null,
273
- }
274
- : {
275
- kind: "rpc_agent_completed",
276
- signal: "completed",
277
- evidence: { outcome: "completed" },
278
- severity: "info",
279
- semantic: true,
280
- coalesceKey: null,
281
- };
282
- }
283
- default:
284
- return null;
285
- }
286
- }