@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.
- package/CHANGELOG.md +13 -0
- package/dist/types/async/job-manager.d.ts +44 -1
- package/dist/types/cli/setup-cli.d.ts +14 -1
- package/dist/types/commands/coordinator.d.ts +19 -0
- package/dist/types/commands/mcp-serve.d.ts +24 -0
- package/dist/types/commands/setup.d.ts +41 -0
- package/dist/types/commit/model-selection.d.ts +1 -1
- package/dist/types/config/model-registry.d.ts +3 -1
- package/dist/types/config/model-resolver.d.ts +1 -19
- package/dist/types/config/models-config-schema.d.ts +12 -0
- package/dist/types/config/settings-schema.d.ts +15 -1
- package/dist/types/coordinator/contract.d.ts +4 -0
- package/dist/types/coordinator-mcp/policy.d.ts +24 -0
- package/dist/types/coordinator-mcp/safety.d.ts +26 -0
- package/dist/types/coordinator-mcp/server.d.ts +52 -0
- package/dist/types/extensibility/extensions/types.d.ts +13 -0
- package/dist/types/gjc-runtime/goal-mode-request.d.ts +8 -1
- package/dist/types/gjc-runtime/session-state-sidecar.d.ts +13 -0
- package/dist/types/harness-control-plane/types.d.ts +7 -2
- package/dist/types/modes/acp/acp-event-mapper.d.ts +2 -0
- package/dist/types/modes/components/custom-editor.d.ts +7 -0
- package/dist/types/modes/components/hook-selector.d.ts +11 -0
- package/dist/types/modes/shared/agent-wire/command-contract.d.ts +18 -0
- package/dist/types/modes/shared/agent-wire/event-contract.d.ts +84 -0
- package/dist/types/modes/shared/agent-wire/event-envelope.d.ts +14 -7
- package/dist/types/modes/shared/agent-wire/event-observation.d.ts +37 -0
- package/dist/types/modes/shared/agent-wire/protocol.d.ts +13 -34
- package/dist/types/session/agent-session.d.ts +12 -1
- package/dist/types/session/session-manager.d.ts +1 -1
- package/dist/types/setup/hermes-setup.d.ts +71 -0
- package/dist/types/task/render.d.ts +7 -1
- package/dist/types/tools/bash.d.ts +2 -0
- package/dist/types/tools/browser/actions.d.ts +54 -0
- package/dist/types/tools/browser.d.ts +80 -0
- package/dist/types/tools/image-gen.d.ts +1 -0
- package/dist/types/tools/index.d.ts +3 -1
- package/dist/types/tools/job.d.ts +1 -1
- package/dist/types/tools/subagent-render.d.ts +25 -0
- package/dist/types/tools/subagent.d.ts +5 -1
- package/package.json +7 -7
- package/src/async/job-manager.ts +163 -2
- package/src/cli/setup-cli.ts +86 -2
- package/src/cli.ts +2 -0
- package/src/commands/coordinator.ts +70 -0
- package/src/commands/mcp-serve.ts +62 -0
- package/src/commands/setup.ts +30 -1
- package/src/commands/ultragoal.ts +7 -1
- package/src/commit/agentic/index.ts +2 -2
- package/src/commit/model-selection.ts +7 -22
- package/src/commit/pipeline.ts +2 -2
- package/src/config/model-registry.ts +17 -9
- package/src/config/model-resolver.ts +14 -84
- package/src/config/models-config-schema.ts +2 -0
- package/src/config/settings-schema.ts +14 -1
- package/src/coordinator/contract.ts +20 -0
- package/src/coordinator-mcp/policy.ts +160 -0
- package/src/coordinator-mcp/safety.ts +80 -0
- package/src/coordinator-mcp/server.ts +1316 -0
- package/src/extensibility/extensions/types.ts +13 -0
- package/src/gjc-runtime/goal-mode-request.ts +21 -1
- package/src/gjc-runtime/session-state-sidecar.ts +79 -0
- package/src/harness-control-plane/owner.ts +3 -3
- package/src/harness-control-plane/rpc-adapter.ts +7 -1
- package/src/harness-control-plane/types.ts +8 -11
- package/src/internal-urls/docs-index.generated.ts +6 -5
- package/src/memories/index.ts +1 -1
- package/src/modes/acp/acp-agent.ts +17 -9
- package/src/modes/acp/acp-event-mapper.ts +33 -1
- package/src/modes/components/custom-editor.ts +19 -3
- package/src/modes/components/hook-selector.ts +109 -5
- package/src/modes/controllers/extension-ui-controller.ts +16 -1
- package/src/modes/controllers/input-controller.ts +27 -7
- package/src/modes/controllers/selector-controller.ts +7 -1
- package/src/modes/interactive-mode.ts +3 -1
- package/src/modes/rpc/rpc-client.ts +16 -3
- package/src/modes/rpc/rpc-mode.ts +5 -2
- package/src/modes/shared/agent-wire/command-contract.ts +18 -0
- package/src/modes/shared/agent-wire/event-contract.ts +147 -0
- package/src/modes/shared/agent-wire/event-envelope.ts +35 -16
- package/src/modes/shared/agent-wire/event-observation.ts +397 -0
- package/src/modes/shared/agent-wire/protocol.ts +24 -81
- package/src/modes/utils/context-usage.ts +2 -2
- package/src/prompts/agents/architect.md +6 -0
- package/src/prompts/agents/critic.md +6 -0
- package/src/prompts/agents/explore.md +1 -1
- package/src/prompts/agents/plan.md +1 -1
- package/src/prompts/agents/planner.md +8 -1
- package/src/prompts/agents/reviewer.md +1 -1
- package/src/prompts/tools/browser.md +3 -2
- package/src/runtime-mcp/manager.ts +15 -2
- package/src/sdk.ts +3 -1
- package/src/session/agent-session.ts +66 -4
- package/src/session/session-manager.ts +1 -1
- package/src/setup/hermes/templates/operator-instructions.v1.md +29 -0
- package/src/setup/hermes-setup.ts +429 -0
- package/src/task/agents.ts +1 -1
- package/src/task/index.ts +2 -0
- package/src/task/render.ts +14 -0
- package/src/tools/ask.ts +30 -10
- package/src/tools/bash.ts +6 -1
- package/src/tools/browser/actions.ts +189 -0
- package/src/tools/browser.ts +91 -1
- package/src/tools/image-gen.ts +42 -15
- package/src/tools/index.ts +7 -1
- package/src/tools/inspect-image.ts +10 -8
- package/src/tools/job.ts +12 -2
- package/src/tools/monitor.ts +98 -17
- package/src/tools/renderers.ts +2 -0
- package/src/tools/subagent-render.ts +160 -0
- package/src/tools/subagent.ts +49 -7
- package/src/utils/commit-message-generator.ts +6 -13
- package/src/utils/title-generator.ts +1 -1
- package/dist/types/harness-control-plane/frame-mapper.d.ts +0 -29
- package/src/harness-control-plane/frame-mapper.ts +0 -286
- package/src/priority.json +0 -37
|
@@ -122,6 +122,19 @@ export interface ExtensionUIDialogOptions {
|
|
|
122
122
|
* select-only rendering hint; non-TUI bridges drop it and do not serialize it.
|
|
123
123
|
*/
|
|
124
124
|
scrollTitleRows?: number;
|
|
125
|
+
/**
|
|
126
|
+
* For interactive TUI select dialogs, handle the option with `optionLabel`
|
|
127
|
+
* inline: selecting it keeps the title and option list on screen and opens
|
|
128
|
+
* a free-text input below the list. Submitting calls `onSubmit` with the
|
|
129
|
+
* typed text and resolves the select with `optionLabel`; Escape returns to
|
|
130
|
+
* option selection. Non-TUI bridges (RPC, ACP) drop it; callers must keep
|
|
131
|
+
* a fallback path for selects that resolve `optionLabel` without invoking
|
|
132
|
+
* `onSubmit`.
|
|
133
|
+
*/
|
|
134
|
+
customInput?: {
|
|
135
|
+
optionLabel: string;
|
|
136
|
+
onSubmit: (text: string) => void;
|
|
137
|
+
};
|
|
125
138
|
}
|
|
126
139
|
|
|
127
140
|
/** Raw terminal input listener for extensions. */
|
|
@@ -25,6 +25,12 @@ export interface PendingGoalModeRequest {
|
|
|
25
25
|
objective: string;
|
|
26
26
|
createdAt: string;
|
|
27
27
|
goalsPath?: string;
|
|
28
|
+
/**
|
|
29
|
+
* Session id that produced this request (from GJC_SESSION_ID). When present,
|
|
30
|
+
* only the originating session may consume it, so concurrent sessions sharing
|
|
31
|
+
* the same `.gjc` project state never auto-run each other's ultragoal.
|
|
32
|
+
*/
|
|
33
|
+
sessionId?: string;
|
|
28
34
|
}
|
|
29
35
|
|
|
30
36
|
export type CurrentSessionGoalModeWriteResult =
|
|
@@ -77,9 +83,11 @@ export async function writePendingGoalModeRequest(input: {
|
|
|
77
83
|
cwd: string;
|
|
78
84
|
objective: string;
|
|
79
85
|
goalsPath?: string;
|
|
86
|
+
sessionId?: string | null;
|
|
80
87
|
}): Promise<PendingGoalModeRequest> {
|
|
81
88
|
const objective = input.objective.trim();
|
|
82
89
|
if (!objective) throw new Error("goal objective is required");
|
|
90
|
+
const sessionId = input.sessionId?.trim();
|
|
83
91
|
const request: PendingGoalModeRequest = {
|
|
84
92
|
version: REQUEST_VERSION,
|
|
85
93
|
kind: "goal_mode_request",
|
|
@@ -87,6 +95,7 @@ export async function writePendingGoalModeRequest(input: {
|
|
|
87
95
|
objective,
|
|
88
96
|
createdAt: new Date().toISOString(),
|
|
89
97
|
goalsPath: input.goalsPath,
|
|
98
|
+
...(sessionId ? { sessionId } : {}),
|
|
90
99
|
};
|
|
91
100
|
const filePath = requestPath(input.cwd);
|
|
92
101
|
await writeJsonAtomic(filePath, request, {
|
|
@@ -162,7 +171,10 @@ export async function writeCurrentSessionGoalModeState(input: {
|
|
|
162
171
|
return { status: "updated", goal: state.goal, sessionFile };
|
|
163
172
|
}
|
|
164
173
|
|
|
165
|
-
export async function consumePendingGoalModeRequest(
|
|
174
|
+
export async function consumePendingGoalModeRequest(
|
|
175
|
+
cwd: string,
|
|
176
|
+
currentSessionId?: string | null,
|
|
177
|
+
): Promise<PendingGoalModeRequest | null> {
|
|
166
178
|
const filePath = requestPath(cwd);
|
|
167
179
|
let raw: unknown;
|
|
168
180
|
try {
|
|
@@ -181,6 +193,14 @@ export async function consumePendingGoalModeRequest(cwd: string): Promise<Pendin
|
|
|
181
193
|
) {
|
|
182
194
|
return null;
|
|
183
195
|
}
|
|
196
|
+
// Session isolation: a request stamped with an owning session id may only be
|
|
197
|
+
// consumed by that same session. Leave another session's request untouched
|
|
198
|
+
// (do not delete it) so its rightful owner can still pick it up. Legacy/unscoped
|
|
199
|
+
// requests (no sessionId) remain consumable by any session in this cwd.
|
|
200
|
+
const ownerSessionId = typeof candidate.sessionId === "string" ? candidate.sessionId.trim() : "";
|
|
201
|
+
if (ownerSessionId && ownerSessionId !== (currentSessionId?.trim() ?? "")) {
|
|
202
|
+
return null;
|
|
203
|
+
}
|
|
184
204
|
await removeFileAudited(filePath, {
|
|
185
205
|
cwd,
|
|
186
206
|
audit: { category: "prune", verb: "remove", owner: "gjc-runtime" },
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import * as fs from "node:fs/promises";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import type { AssistantMessage } from "@gajae-code/ai";
|
|
4
|
+
import { logger } from "@gajae-code/utils";
|
|
5
|
+
|
|
6
|
+
export const GJC_COORDINATOR_SESSION_STATE_FILE_ENV = "GJC_COORDINATOR_SESSION_STATE_FILE";
|
|
7
|
+
export const GJC_COORDINATOR_SESSION_ID_ENV = "GJC_COORDINATOR_SESSION_ID";
|
|
8
|
+
|
|
9
|
+
type RuntimeState = "ready_for_input" | "running" | "needs_user_input" | "completed" | "errored";
|
|
10
|
+
|
|
11
|
+
interface RuntimeStateEvent {
|
|
12
|
+
type: string;
|
|
13
|
+
messages?: unknown[];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface RuntimeStateContext {
|
|
17
|
+
sessionId: string;
|
|
18
|
+
cwd: string;
|
|
19
|
+
sessionFile?: string | null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function lastAssistant(messages: unknown[] | undefined): AssistantMessage | undefined {
|
|
23
|
+
if (!messages) return undefined;
|
|
24
|
+
for (let index = messages.length - 1; index >= 0; index--) {
|
|
25
|
+
const message = messages[index];
|
|
26
|
+
if (message && typeof message === "object" && (message as { role?: unknown }).role === "assistant") {
|
|
27
|
+
return message as AssistantMessage;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function stateForEvent(event: RuntimeStateEvent): RuntimeState | null {
|
|
34
|
+
if (event.type === "agent_start" || event.type === "turn_start") return "running";
|
|
35
|
+
if (event.type === "agent_end") {
|
|
36
|
+
const assistant = lastAssistant(event.messages);
|
|
37
|
+
return assistant?.stopReason === "error" ? "errored" : "completed";
|
|
38
|
+
}
|
|
39
|
+
if (event.type === "notice") return null;
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export async function persistCoordinatorRuntimeStateFromEvent(
|
|
44
|
+
event: RuntimeStateEvent,
|
|
45
|
+
context: RuntimeStateContext,
|
|
46
|
+
): Promise<void> {
|
|
47
|
+
const stateFile = process.env[GJC_COORDINATOR_SESSION_STATE_FILE_ENV]?.trim();
|
|
48
|
+
if (!stateFile) return;
|
|
49
|
+
const state = stateForEvent(event);
|
|
50
|
+
if (!state) return;
|
|
51
|
+
const now = new Date().toISOString();
|
|
52
|
+
let previous: Record<string, unknown> = {};
|
|
53
|
+
try {
|
|
54
|
+
previous = JSON.parse(await Bun.file(stateFile).text()) as Record<string, unknown>;
|
|
55
|
+
} catch {
|
|
56
|
+
previous = {};
|
|
57
|
+
}
|
|
58
|
+
const payload = {
|
|
59
|
+
schema_version: 1,
|
|
60
|
+
session_id: process.env[GJC_COORDINATOR_SESSION_ID_ENV]?.trim() || context.sessionId,
|
|
61
|
+
state,
|
|
62
|
+
ready_for_input: state === "completed" || state === "ready_for_input",
|
|
63
|
+
updated_at: now,
|
|
64
|
+
current_turn_id: typeof previous.current_turn_id === "string" ? previous.current_turn_id : null,
|
|
65
|
+
last_turn_id: typeof previous.last_turn_id === "string" ? previous.last_turn_id : null,
|
|
66
|
+
live: typeof previous.live === "boolean" ? previous.live : null,
|
|
67
|
+
reason: null,
|
|
68
|
+
source: "agent_session_event",
|
|
69
|
+
event: event.type,
|
|
70
|
+
cwd: context.cwd,
|
|
71
|
+
session_file: context.sessionFile ?? null,
|
|
72
|
+
};
|
|
73
|
+
try {
|
|
74
|
+
await fs.mkdir(path.dirname(stateFile), { recursive: true });
|
|
75
|
+
await Bun.write(stateFile, `${JSON.stringify(payload, null, 2)}\n`);
|
|
76
|
+
} catch (error) {
|
|
77
|
+
logger.warn("Failed to persist coordinator runtime state", { error: String(error), stateFile });
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -14,10 +14,10 @@
|
|
|
14
14
|
import { execFileSync } from "node:child_process";
|
|
15
15
|
import { randomBytes, randomUUID } from "node:crypto";
|
|
16
16
|
import { existsSync } from "node:fs";
|
|
17
|
+
import { observeRpcOutboundFrame } from "../modes/shared/agent-wire/event-observation";
|
|
17
18
|
import { classifyRecovery } from "./classifier";
|
|
18
19
|
import { ControlServer, type EndpointRequest } from "./control-endpoint";
|
|
19
20
|
import { defaultFinalizeChecks, type FinalizeChecks, runFinalize, type ValidationCommandSpec } from "./finalize";
|
|
20
|
-
import { mapRpcFrame } from "./frame-mapper";
|
|
21
21
|
import { type OperateResult, operate } from "./operate";
|
|
22
22
|
import { preserveDirtyWorktree } from "./preserve";
|
|
23
23
|
import {
|
|
@@ -174,7 +174,7 @@ export class RuntimeOwner {
|
|
|
174
174
|
|
|
175
175
|
/** Map an RPC frame and route it: semantic/signal-bearing -> serial emit; high-frequency progress -> coalesce. */
|
|
176
176
|
#handleFrame(frame: Record<string, unknown>): void {
|
|
177
|
-
const mapped =
|
|
177
|
+
const mapped = observeRpcOutboundFrame(frame);
|
|
178
178
|
if (!mapped) return;
|
|
179
179
|
if (mapped.semantic || (mapped.signal && !mapped.coalesceKey)) {
|
|
180
180
|
this.#framePump = this.#framePump
|
|
@@ -199,7 +199,7 @@ export class RuntimeOwner {
|
|
|
199
199
|
await this.#emit("info", "rpc_activity", { coalescedFrames });
|
|
200
200
|
}
|
|
201
201
|
|
|
202
|
-
async #emitMapped(mapped: NonNullable<ReturnType<typeof
|
|
202
|
+
async #emitMapped(mapped: NonNullable<ReturnType<typeof observeRpcOutboundFrame>>): Promise<void> {
|
|
203
203
|
await this.#emit(
|
|
204
204
|
mapped.severity,
|
|
205
205
|
mapped.kind,
|
|
@@ -174,7 +174,13 @@ export class GajaeCodeRpc implements HarnessRpc {
|
|
|
174
174
|
// Any other frame is a session/agent event: advance the cursor.
|
|
175
175
|
this.#cursor += 1;
|
|
176
176
|
this.#lastFrameAt = new Date().toISOString();
|
|
177
|
-
|
|
177
|
+
// Session events arrive as canonical `event` frames: the agent event type
|
|
178
|
+
// lives in `payload.event_type`. Non-event frames keep their flat `type`.
|
|
179
|
+
const effectiveType =
|
|
180
|
+
type === "event" && frame.payload && typeof frame.payload === "object"
|
|
181
|
+
? (frame.payload as { event_type?: unknown }).event_type
|
|
182
|
+
: type;
|
|
183
|
+
if (effectiveType === "agent_start") {
|
|
178
184
|
const cursor = this.#cursor;
|
|
179
185
|
this.#agentStartCursors.push(cursor);
|
|
180
186
|
this.#waiters = this.#waiters.filter(w => {
|
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
* v1 implements the gajae-code adapter only. omx/codex/remote/auth are deferred seams.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
+
import type { AgentWireObservedSignal } from "../modes/shared/agent-wire/event-contract";
|
|
12
|
+
|
|
11
13
|
/** Harnesses the control plane can operate. v1 implements `gajae-code` only. */
|
|
12
14
|
export type Harness = "gajae-code" | "codex" | "omx";
|
|
13
15
|
|
|
@@ -147,17 +149,12 @@ export interface SessionState {
|
|
|
147
149
|
updatedAt: string;
|
|
148
150
|
}
|
|
149
151
|
|
|
150
|
-
/**
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
| "commit-created"
|
|
157
|
-
| "completed"
|
|
158
|
-
| "error"
|
|
159
|
-
| "streaming"
|
|
160
|
-
| "idle";
|
|
152
|
+
/**
|
|
153
|
+
* Bounded observed-signal vocabulary surfaced by `observe` (the owner only ever
|
|
154
|
+
* emits these). Aliased to the canonical agent-wire signal vocabulary so there
|
|
155
|
+
* is a single source of truth shared with the observation core.
|
|
156
|
+
*/
|
|
157
|
+
export type ObservedSignal = AgentWireObservedSignal;
|
|
161
158
|
|
|
162
159
|
export const OBSERVED_SIGNALS: readonly ObservedSignal[] = [
|
|
163
160
|
"SessionStart",
|