@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
@@ -1,100 +1,43 @@
1
1
  /**
2
2
  * Shared agent-wire protocol primitives for GJC bridge surfaces.
3
3
  *
4
- * This module is the transport-agnostic, versioned frame contract that the
5
- * RPC mode and the (in-progress) `--mode bridge` wiring site both build on.
6
- * It carries the SEMANTIC agent surface events, responses, and UI/permission
7
- * requests — never pixels. See `.gjc/specs/deep-interview-gjc-backend-bridge.md`
8
- * and `.gjc/plans/ralplan/gjc-backend-bridge/pending-approval.md`.
4
+ * The canonical event/frame contract now lives in `event-contract.ts`. This
5
+ * module re-exports it under the historical `Bridge*` names so existing RPC and
6
+ * Bridge code keeps compiling while the adapters migrate to the canonical
7
+ * `AgentWire*` names. See `.gjc/specs/deep-interview-reconcile-rpc-adapters.md`.
9
8
  */
10
- import type { AgentSessionEvent } from "../../../session/agent-session";
9
+ import type {
10
+ AgentWireEventFrame,
11
+ AgentWireEventPayload,
12
+ AgentWireEventType,
13
+ AgentWireFrameEnvelope,
14
+ AgentWireFrameType,
15
+ } from "./event-contract";
16
+ import { AGENT_WIRE_EVENT_TYPES, AGENT_WIRE_PROTOCOL_VERSION } from "./event-contract";
11
17
 
12
18
  /** Wire protocol version. Bump on breaking envelope/semantic changes. */
13
- export const BRIDGE_PROTOCOL_VERSION = 1 as const;
19
+ export const BRIDGE_PROTOCOL_VERSION = AGENT_WIRE_PROTOCOL_VERSION;
14
20
 
15
21
  /** The discriminant of every `AgentSessionEvent` the agent can emit. */
16
- export type AgentSessionEventType = AgentSessionEvent["type"];
17
-
18
- /**
19
- * Compile-time exhaustive registry of every `AgentSessionEvent` variant.
20
- *
21
- * Adding a new variant to `AgentSessionEvent` without registering it here is a
22
- * type error. This keeps the bridge wire surface in lockstep with the agent
23
- * event union — the "event/element drift → silent incompleteness" mitigation
24
- * from the plan's pre-mortem.
25
- */
26
- const AGENT_SESSION_EVENT_TYPE_REGISTRY: Record<AgentSessionEventType, true> = {
27
- agent_start: true,
28
- agent_end: true,
29
- turn_start: true,
30
- turn_end: true,
31
- message_start: true,
32
- message_update: true,
33
- message_end: true,
34
- tool_execution_start: true,
35
- tool_execution_update: true,
36
- tool_execution_end: true,
37
- auto_compaction_start: true,
38
- auto_compaction_end: true,
39
- auto_retry_start: true,
40
- auto_retry_end: true,
41
- retry_fallback_applied: true,
42
- retry_fallback_succeeded: true,
43
- ttsr_triggered: true,
44
- todo_reminder: true,
45
- todo_auto_clear: true,
46
- irc_message: true,
47
- notice: true,
48
- thinking_level_changed: true,
49
- goal_updated: true,
50
- };
22
+ export type AgentSessionEventType = AgentWireEventType;
51
23
 
52
24
  /** Every agent-session event type, derived from the exhaustive registry. */
53
- export const AGENT_SESSION_EVENT_TYPES: readonly AgentSessionEventType[] = Object.keys(
54
- AGENT_SESSION_EVENT_TYPE_REGISTRY,
55
- ) as AgentSessionEventType[];
25
+ export const AGENT_SESSION_EVENT_TYPES: readonly AgentSessionEventType[] = AGENT_WIRE_EVENT_TYPES;
56
26
 
57
27
  /** Top-level frame categories carried over any bridge transport. */
58
- export type BridgeFrameType =
59
- | "ready"
60
- | "event"
61
- | "response"
62
- | "ui_request"
63
- | "permission_request"
64
- | "host_tool_call"
65
- | "host_uri_request"
66
- | "reset"
67
- | "workflow_gate"
68
- | "error";
28
+ export type BridgeFrameType = AgentWireFrameType;
69
29
 
70
- /**
71
- * Universal frame envelope. Every frame on every transport carries these
72
- * fields so clients can order (`seq`), resume (`seq` cursor), and correlate
73
- * request/response pairs (`correlation_id`). `session_id` is present from v1
74
- * even though v1 runs one session per process, so in-process multiplexing is
75
- * an additive, non-breaking change later.
76
- */
77
- export interface BridgeFrameEnvelope<TType extends BridgeFrameType = BridgeFrameType, TPayload = unknown> {
78
- protocol_version: typeof BRIDGE_PROTOCOL_VERSION;
79
- session_id: string;
80
- /** Monotonic per-session sequence number, starting at 1. */
81
- seq: number;
82
- /** Unique id for this frame. */
83
- frame_id: string;
84
- /** Ties a request frame to its response frame, when applicable. */
85
- correlation_id?: string;
86
- type: TType;
87
- payload: TPayload;
88
- }
30
+ /** Universal frame envelope. See {@link AgentWireFrameEnvelope}. */
31
+ export type BridgeFrameEnvelope<
32
+ TType extends BridgeFrameType = BridgeFrameType,
33
+ TPayload = unknown,
34
+ > = AgentWireFrameEnvelope<TType, TPayload>;
89
35
 
90
- /** Payload carried by an `event` frame. */
91
- export interface BridgeEventPayload {
92
- event_type: AgentSessionEventType;
93
- event: AgentSessionEvent;
94
- }
36
+ /** Payload carried by an `event` frame. See {@link AgentWireEventPayload}. */
37
+ export type BridgeEventPayload = AgentWireEventPayload;
95
38
 
96
39
  /** An `AgentSessionEvent` serialized into a versioned wire frame. */
97
- export type BridgeEventFrame = BridgeFrameEnvelope<"event", BridgeEventPayload>;
40
+ export type BridgeEventFrame = AgentWireEventFrame;
98
41
 
99
42
  /** A `workflow_gate` event serialized into a versioned wire frame (#321). */
100
43
  export type BridgeWorkflowGateFrame = BridgeFrameEnvelope<
@@ -197,14 +197,14 @@ export function computeContextBreakdown(
197
197
  if (contextWindow > 0) {
198
198
  const compactionSettings = session.settings.getGroup("compaction") as CompactionSettings;
199
199
  if (compactionSettings.enabled && compactionSettings.strategy !== "off") {
200
- const threshold = resolveThresholdTokens(contextWindow, compactionSettings);
200
+ const threshold = resolveThresholdTokens(contextWindow, compactionSettings, model?.maxTokens ?? 0);
201
201
  autoCompactBufferTokens = Math.max(0, contextWindow - threshold);
202
202
  } else {
203
203
  autoCompactBufferTokens = 0;
204
204
  }
205
205
  // Even when fully disabled, fall back to a sensible reserve floor for display.
206
206
  if (autoCompactBufferTokens === 0 && compactionSettings.enabled) {
207
- autoCompactBufferTokens = effectiveReserveTokens(contextWindow, compactionSettings);
207
+ autoCompactBufferTokens = effectiveReserveTokens(contextWindow, compactionSettings, model?.maxTokens ?? 0);
208
208
  }
209
209
  }
210
210
  autoCompactBufferTokens = Math.min(autoCompactBufferTokens, Math.max(0, contextWindow - usedTokens));
@@ -83,4 +83,10 @@ Prioritized concrete actions.
83
83
 
84
84
  ## Trade-offs
85
85
  Table or bullets comparing viable options when relevant.
86
+
87
+ Persist this full review as the durable artifact via the restricted bash CLI, passing the markdown inline (never a file path, never `/tmp`):
88
+
89
+ gjc ralplan --write --stage architect --stage_n <N> --artifact "<full review markdown>" --json
90
+
91
+ Then return to the caller ONLY the write receipt (`run_id`, `path`, `sha256`, `stage`, `stage_n`) plus the compact verdict (Architectural Status + Code Review Recommendation). Never paste the full review body back into your response — the caller reads the persisted artifact when it needs the full text.
86
92
  </output_contract>
@@ -56,4 +56,10 @@ Review plan clarity, completeness, verification, big-picture fit, referenced fil
56
56
  - Risk/Verification Rigor
57
57
 
58
58
  If not OKAY, list concrete required fixes.
59
+
60
+ Persist this full evaluation as the durable artifact via the restricted bash CLI, passing the markdown inline (never a file path, never `/tmp`):
61
+
62
+ gjc ralplan --write --stage critic --stage_n <N> --artifact "<full evaluation markdown>" --json
63
+
64
+ Then return to the caller ONLY the write receipt (`run_id`, `path`, `sha256`, `stage`, `stage_n`) plus the compact verdict (OKAY / ITERATE / REJECT). Never paste the full evaluation body back into your response — the caller reads the persisted artifact when it needs the full text.
59
65
  </output_contract>
@@ -2,7 +2,7 @@
2
2
  name: explore
3
3
  description: Fast read-only codebase scout returning compressed context for handoff
4
4
  tools: read, search, find, web_search
5
- model: pi/smol
5
+ model: pi/default
6
6
  thinking-level: med
7
7
  output:
8
8
  properties:
@@ -3,7 +3,7 @@ name: plan
3
3
  description: Software architect for complex multi-file architectural decisions. NOT for simple tasks, single-file changes, or tasks completable in <5 tool calls.
4
4
  tools: read, search, find, bash, lsp, web_search, ast_grep
5
5
  spawns: explore
6
- model: pi/plan, pi/slow
6
+ model: pi/default
7
7
  thinking-level: high
8
8
  hide: true
9
9
  ---
@@ -18,6 +18,7 @@ Leave execution with a right-sized, evidence-grounded plan: scope, steps, accept
18
18
  <constraints>
19
19
  - Read-only: never write, edit, format, commit, push, or mutate files.
20
20
  - Exception: you may use the restricted `bash` tool only for sanctioned GJC workflow CLI persistence (`gjc ralplan --write ...`) and GJC workflow state read/write/contract commands (`gjc state ...`). For `gjc ralplan --write`, pass the plan markdown inline in `--artifact`, not as a file path. Do not use bash for product-source writes, direct handoffs, state clears, or general shell work.
21
+ - Persist durable plans only through `gjc ralplan --write`. Never write plan files to `/tmp`, the repository, or any other path, and never rely on a file the caller must read back. The CLI is your only persistence channel.
21
22
  - Inspect the repository before asking about code facts.
22
23
  - Ask only about priorities, tradeoffs, scope decisions, timelines, or preferences that repository inspection cannot resolve.
23
24
  - Right-size the step count to the task; do not default to a fixed number of steps.
@@ -42,7 +43,7 @@ Leave execution with a right-sized, evidence-grounded plan: scope, steps, accept
42
43
  </success_criteria>
43
44
 
44
45
  <output_contract>
45
- Return:
46
+ Build the full plan as a single markdown document containing:
46
47
  - Summary
47
48
  - In scope / out of scope
48
49
  - File-level changes
@@ -50,4 +51,10 @@ Return:
50
51
  - Acceptance criteria
51
52
  - Verification
52
53
  - Risks and mitigations
54
+
55
+ Persist that markdown as the durable artifact via the restricted bash CLI, passing the plan inline (never a file path, never `/tmp`):
56
+
57
+ gjc ralplan --write --stage planner --stage_n <N> --artifact "<full plan markdown>" --json
58
+
59
+ Then return to the caller ONLY the write receipt (`run_id`, `path`, `sha256`, `stage`, `stage_n`) plus a compact plan summary (<=10 lines). Never paste the full plan body back into your response — the caller reads the persisted artifact when it needs the full text.
53
60
  </output_contract>
@@ -3,7 +3,7 @@ name: reviewer
3
3
  description: "Code review specialist for quality/security analysis"
4
4
  tools: read, search, find, bash, lsp, web_search, ast_grep, report_finding
5
5
  spawns: explore
6
- model: pi/slow
6
+ model: pi/default
7
7
  thinking-level: high
8
8
  blocking: true
9
9
  output:
@@ -2,10 +2,11 @@ Drives a real Chromium tab with full puppeteer access via JS execution.
2
2
 
3
3
  <instruction>
4
4
  - For static web content (articles, docs, issues/PRs, JSON, PDFs, feeds), prefer the `read` tool with a URL — reader-mode text without spinning up a browser. Use this tool when you need JS execution, authentication, or interactive actions.
5
- - Three actions only:
5
+ - Four actions:
6
6
  - `open` — acquire (or reuse) a named tab. `name` defaults to `"main"`. Optional `url` navigates after the tab is ready. Optional `viewport` sets dimensions. Optional `dialogs: "accept" | "dismiss"` auto-handles `alert`/`confirm`/`beforeunload` so navigation/clicks don't hang (default: leave dialogs unhandled — page hangs until caller wires `page.on('dialog', …)`).
7
7
  - `close` — release a tab by `name`, or every tab with `all: true`. For spawned-app browsers, set `kill: true` to terminate the process tree (default leaves it running).
8
8
  - `run` — execute JS against an existing tab. `code` is the body of an async function with `page`, `browser`, `tab`, `display`, `assert`, `wait` in scope. The function's return value is JSON-stringified into the tool result; multiple `display(value)` calls accumulate text/images.
9
+ - `act` — run a list of structured `actions` against an existing tab without writing JS (preferred for routine navigation/interaction). Each step is `{ verb, … }`; verbs: `navigate {url, wait_until?}`, `click {id|selector}`, `type {id|selector, text}`, `fill {selector, value}`, `select {selector, values}`, `press {key, selector?}`, `scroll {dx?, dy?}`, `back`, `wait {selector?|ms?}`, `observe {viewport_only?, include_all?}`, `extract {format?}`, `screenshot`. Address elements by the numeric `id` from a prior `observe` (preferred) or a selector. Steps run in order; the tool returns an array of per-step results (observations/extracted content included). Use `run` only when a verb does not cover what you need.
9
10
  - Tabs survive across `run` calls and across in-process subagents. Open once, reuse many times.
10
11
  - Browser kinds, selected by the `app` field on `open`:
11
12
  - default (no `app`) → headless Chromium with stealth patches.
@@ -32,7 +33,7 @@ Drives a real Chromium tab with full puppeteer access via JS execution.
32
33
  </instruction>
33
34
 
34
35
  <critical>
35
- - You MUST call `open` before `run`. `run` does not implicitly create a tab.
36
+ - You MUST call `open` before `run` or `act`. Neither implicitly creates a tab.
36
37
  - You NEVER screenshot just to "see what's on the page" — `tab.observe()` returns structured data with element ids you can act on immediately.
37
38
  - After a `tab.goto()` or any navigation, prior element ids from `tab.observe()` are invalidated. Re-observe before referencing them.
38
39
  - `code` runs with full Node access. Treat it as your code, not sandboxed code.
@@ -304,6 +304,7 @@ export class MCPManager {
304
304
  config: MCPServerConfig;
305
305
  tracked: TrackedPromise<ToolLoadResult>;
306
306
  toolsPromise: Promise<ToolLoadResult>;
307
+ connectionAbort: AbortController;
307
308
  };
308
309
 
309
310
  const errors = new Map<string, string>();
@@ -424,7 +425,7 @@ export class MCPManager {
424
425
  this.#pendingToolLoads.set(name, toolsPromise);
425
426
 
426
427
  const tracked = trackPromise(toolsPromise);
427
- connectionTasks.push({ name, config, tracked, toolsPromise });
428
+ connectionTasks.push({ name, config, tracked, toolsPromise, connectionAbort });
428
429
 
429
430
  void toolsPromise
430
431
  .then(async ({ connection, serverTools }) => {
@@ -475,7 +476,19 @@ export class MCPManager {
475
476
 
476
477
  const pendingWithoutCache = pendingTasks.filter(task => !cachedTools.has(task.name));
477
478
  if (pendingWithoutCache.length > 0) {
478
- await Promise.allSettled(pendingWithoutCache.map(task => task.tracked.promise));
479
+ for (const task of pendingWithoutCache) {
480
+ const message = `MCP server connection timed out during startup: ${task.name}`;
481
+ errors.set(task.name, message);
482
+ reportedErrors.add(task.name);
483
+ task.connectionAbort.abort(new Error(message));
484
+ if (this.#pendingConnections.has(task.name)) this.#pendingConnections.delete(task.name);
485
+ if (this.#pendingToolLoads.get(task.name) === task.toolsPromise)
486
+ this.#pendingToolLoads.delete(task.name);
487
+ this.#pendingConnectionControllers.delete(task.name);
488
+ }
489
+ // Do not await these promises here: a misbehaving stdio/MCP transport can ignore
490
+ // AbortSignal and keep startup blocked indefinitely. The background toolsPromise
491
+ // handler will clean up if it eventually settles.
479
492
  }
480
493
  }
481
494
 
package/src/sdk.ts CHANGED
@@ -868,7 +868,8 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
868
868
  imageProvider === "auto" ||
869
869
  imageProvider === "openai" ||
870
870
  imageProvider === "gemini" ||
871
- imageProvider === "openrouter"
871
+ imageProvider === "openrouter" ||
872
+ imageProvider === "antigravity"
872
873
  ) {
873
874
  setPreferredImageProvider(imageProvider);
874
875
  }
@@ -1222,6 +1223,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
1222
1223
  timestamp: Date.now(),
1223
1224
  }),
1224
1225
  sendCustomMessage: (msg, opts) => session.sendCustomMessage(msg, opts),
1226
+ purgeQueuedCustomMessages: predicate => session.purgeQueuedCustomMessages(predicate),
1225
1227
  peekQueueInvoker: () => session.peekQueueInvoker(),
1226
1228
  peekStandingResolveHandler: () => session.peekStandingResolveHandler(),
1227
1229
  setStandingResolveHandler: handler => session.setStandingResolveHandler(handler),
@@ -89,6 +89,15 @@ export interface ForkContextSeedMetadata {
89
89
  skippedReasons: Record<string, number>;
90
90
  }
91
91
 
92
+ export interface PurgeQueuedCustomMessagesResult {
93
+ agentSteering: number;
94
+ agentFollowUp: number;
95
+ pendingNextTurn: number;
96
+ displaySteering: number;
97
+ displayFollowUp: number;
98
+ totalExecutable: number;
99
+ }
100
+
92
101
  export interface ForkContextSeed {
93
102
  messages: Message[];
94
103
  agentMessages: AgentMessage[];
@@ -171,6 +180,7 @@ import type { HookCommandContext } from "../extensibility/hooks/types";
171
180
  import type { Skill, SkillWarning } from "../extensibility/skills";
172
181
  import { expandSlashCommand, type FileSlashCommand } from "../extensibility/slash-commands";
173
182
  import { buildGjcRuntimeSessionEnv, consumePendingGoalModeRequest } from "../gjc-runtime/goal-mode-request";
183
+ import { persistCoordinatorRuntimeStateFromEvent } from "../gjc-runtime/session-state-sidecar";
174
184
  import { writeArtifact } from "../gjc-runtime/state-writer";
175
185
  import { requestGjcWorkerIntegrationAttempt } from "../gjc-runtime/team-runtime";
176
186
  import { GoalRuntime } from "../goals/runtime";
@@ -1617,6 +1627,11 @@ export class AgentSession {
1617
1627
  }
1618
1628
 
1619
1629
  async #emitSessionEvent(event: AgentSessionEvent): Promise<void> {
1630
+ await persistCoordinatorRuntimeStateFromEvent(event, {
1631
+ sessionId: this.sessionId,
1632
+ cwd: this.sessionManager.getCwd(),
1633
+ sessionFile: this.sessionManager.getSessionFile(),
1634
+ });
1620
1635
  if (event.type === "message_update") {
1621
1636
  this.#emit(event);
1622
1637
  void this.#queueExtensionEvent(event);
@@ -4363,7 +4378,10 @@ export class AgentSession {
4363
4378
 
4364
4379
  async #activatePendingGjcGoalModeRequest(): Promise<boolean> {
4365
4380
  if (!this.settings.get("goal.enabled")) return false;
4366
- const pendingGoal = await consumePendingGoalModeRequest(this.sessionManager.getCwd());
4381
+ const pendingGoal = await consumePendingGoalModeRequest(
4382
+ this.sessionManager.getCwd(),
4383
+ this.sessionManager.getSessionId(),
4384
+ );
4367
4385
  if (!pendingGoal) return false;
4368
4386
  const currentState = this.getGoalModeState();
4369
4387
  if (currentState?.goal && currentState.goal.status !== "complete" && currentState.goal.status !== "dropped") {
@@ -5058,6 +5076,10 @@ export class AgentSession {
5058
5076
  this.#queueHiddenNextTurnMessage(message, true);
5059
5077
  }
5060
5078
 
5079
+ queueDeferredMessageForTests(message: CustomMessage, triggerTurn = true): void {
5080
+ this.#queueHiddenNextTurnMessage(message, triggerTurn);
5081
+ }
5082
+
5061
5083
  #queueHiddenNextTurnMessage(message: CustomMessage, triggerTurn: boolean): void {
5062
5084
  this.#pendingNextTurnMessages.push(message);
5063
5085
  if (!triggerTurn) return;
@@ -5230,6 +5252,46 @@ export class AgentSession {
5230
5252
  );
5231
5253
  }
5232
5254
 
5255
+ /** Remove undelivered queued custom messages matching `predicate` from executable queues and tagged display mirrors. */
5256
+ purgeQueuedCustomMessages(predicate: (message: CustomMessage) => boolean): PurgeQueuedCustomMessagesResult {
5257
+ const isMatch = (m: AgentMessage): boolean => m.role === "custom" && predicate(m as CustomMessage);
5258
+ const removedTags = new Set<string>();
5259
+ for (const m of [...this.agent.snapshotSteering(), ...this.agent.snapshotFollowUp()]) {
5260
+ if (isMatch(m)) {
5261
+ const tag = readPendingDisplayTag((m as CustomMessage).details);
5262
+ if (tag) removedTags.add(tag);
5263
+ }
5264
+ }
5265
+ const agentRemoved = this.agent.removeQueuedMessages(isMatch);
5266
+ const beforeNext = this.#pendingNextTurnMessages.length;
5267
+ for (const m of this.#pendingNextTurnMessages) {
5268
+ if (predicate(m)) {
5269
+ const tag = readPendingDisplayTag(m.details);
5270
+ if (tag) removedTags.add(tag);
5271
+ }
5272
+ }
5273
+ this.#pendingNextTurnMessages = this.#pendingNextTurnMessages.filter(m => !predicate(m));
5274
+ const pendingNextTurn = beforeNext - this.#pendingNextTurnMessages.length;
5275
+ let displaySteering = 0;
5276
+ let displayFollowUp = 0;
5277
+ if (removedTags.size > 0) {
5278
+ const beforeS = this.#steeringMessages.length;
5279
+ this.#steeringMessages = this.#steeringMessages.filter(e => !(e.tag && removedTags.has(e.tag)));
5280
+ displaySteering = beforeS - this.#steeringMessages.length;
5281
+ const beforeF = this.#followUpMessages.length;
5282
+ this.#followUpMessages = this.#followUpMessages.filter(e => !(e.tag && removedTags.has(e.tag)));
5283
+ displayFollowUp = beforeF - this.#followUpMessages.length;
5284
+ }
5285
+ return {
5286
+ agentSteering: agentRemoved.steering,
5287
+ agentFollowUp: agentRemoved.followUp,
5288
+ pendingNextTurn,
5289
+ displaySteering,
5290
+ displayFollowUp,
5291
+ totalExecutable: agentRemoved.total + pendingNextTurn,
5292
+ };
5293
+ }
5294
+
5233
5295
  /**
5234
5296
  * Send a user message to the agent.
5235
5297
  * When deliverAs is set, queue the message instead of starting a new turn.
@@ -5704,7 +5766,7 @@ export class AgentSession {
5704
5766
  /**
5705
5767
  * Cycle through configured role models in a fixed order.
5706
5768
  * Skips missing roles.
5707
- * @param roleOrder - Order of roles to cycle through (e.g., ["slow", "default", "smol"])
5769
+ * @param roleOrder - Order of roles to cycle through (e.g., ["default"])
5708
5770
  * @param options - Optional settings: `temporary` to not persist to settings
5709
5771
  */
5710
5772
  async cycleRoleModels(
@@ -6450,7 +6512,7 @@ export class AgentSession {
6450
6512
  if (pruneResult) {
6451
6513
  contextTokens = Math.max(0, contextTokens - pruneResult.tokensSaved);
6452
6514
  }
6453
- if (shouldCompact(contextTokens, contextWindow, compactionSettings)) {
6515
+ if (shouldCompact(contextTokens, contextWindow, compactionSettings, this.model?.maxTokens ?? 0)) {
6454
6516
  // Try promotion first — if a larger model is available, switch instead of compacting
6455
6517
  const promoted = await this.#tryContextPromotion(assistantMessage);
6456
6518
  if (!promoted) {
@@ -7086,7 +7148,7 @@ export class AgentSession {
7086
7148
  }
7087
7149
  return new Error(
7088
7150
  `Compaction requires usable credentials for ${currentModel.provider}/${currentModel.id}. ` +
7089
- `Configure ${currentModel.provider} credentials or assign an authenticated fallback role such as modelRoles.smol.`,
7151
+ `Configure ${currentModel.provider} credentials or assign an authenticated fallback via modelRoles.default.`,
7090
7152
  );
7091
7153
  }
7092
7154
 
@@ -104,7 +104,7 @@ export interface ModelChangeEntry extends SessionEntryBase {
104
104
  type: "model_change";
105
105
  /** Model in "provider/modelId" format */
106
106
  model: string;
107
- /** Role: "default", "smol", "slow", etc. Undefined treated as "default" */
107
+ /** Role: "default" or an agent role. Undefined treated as "default" */
108
108
  role?: string;
109
109
  }
110
110
 
@@ -0,0 +1,29 @@
1
+ # GJC Hermes operator instructions v{{TEMPLATE_VERSION}}
2
+
3
+ Server key: {{SERVER_KEY}}
4
+
5
+ These instructions teach a Hermes-style coordinator how to operate GJC through the `{{TOOL_PREFIX}}_*` MCP tools. They are setup guidance, not a GJC workflow skill.
6
+
7
+ ## Core loop
8
+
9
+ 1. Use `{{TOOL_PREFIX}}_list_sessions` to find an existing session, or `{{TOOL_PREFIX}}_start_session` when a new session is required and mutation is enabled.
10
+ 2. Send exactly one bounded task prompt with `{{TOOL_PREFIX}}_send_prompt`.
11
+ 3. Store the returned `turn_id`.
12
+ 4. Poll `{{TOOL_PREFIX}}_read_turn` or `{{TOOL_PREFIX}}_await_turn` for that `turn_id` until the turn is terminal.
13
+ 5. If GJC asks a structured question, use `{{TOOL_PREFIX}}_list_questions` and answer with `{{TOOL_PREFIX}}_submit_question_answer`.
14
+ 6. Use `{{TOOL_PREFIX}}_report_status` for coordinator-visible status and final reports.
15
+ 7. Use `{{TOOL_PREFIX}}_read_tail` only as advisory debug output when structured turn state is insufficient.
16
+
17
+ Do not report completion to the user until the GJC turn is terminal. Do not infer completion from terminal scrollback alone.
18
+
19
+ ## Model and provider policy
20
+
21
+ The Hermes bridge does not choose a model/provider. When no session command is configured, GJC uses its normal local model/provider resolution. If the operator config supplies `GJC_COORDINATOR_MCP_SESSION_COMMAND`, preserve it as explicit user intent.
22
+
23
+ Provider-specific commands are examples only, never product defaults.
24
+
25
+ ## Safety
26
+
27
+ - Mutating tools require bridge startup mutation classes and per-call consent.
28
+ - Allowed roots restrict workdir and artifact paths.
29
+ - Artifact reads are bounded and should be treated as evidence, not unlimited filesystem access.