@kenkaiiii/ggcoder 5.79.3 → 5.80.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Goal list rows, shared by the desktop sidecar (`app-sidecar/goal-routes.ts`)
3
+ * and ACP mode (`modes/acp-goals.ts`), so both surfaces describe a goal the
4
+ * same way.
5
+ */
6
+ import { promises as fs } from "node:fs";
7
+ import { goalWasInterrupted } from "./goals-store.js";
8
+ import { formatRelativeTime } from "./project-discovery.js";
9
+ /** The kickoff/resume instruction is runtime-authored and never shown as a user bubble. */
10
+ export const GOAL_RUNTIME_PROVENANCE = {
11
+ source: "runtime",
12
+ kind: "automation",
13
+ visibility: "hidden",
14
+ };
15
+ /** How much of a session file's end is read to find its last message. */
16
+ const TAIL_BYTES = 64 * 1024;
17
+ /**
18
+ * The last few messages of a session file (role and content), read from its
19
+ * end only, never thawing or rewriting it. Empty when it can't be read
20
+ * (archived, gone): the goal then just isn't marked interrupted.
21
+ */
22
+ async function lastMessages(file) {
23
+ let text;
24
+ try {
25
+ const handle = await fs.open(file, "r");
26
+ try {
27
+ const { size } = await handle.stat();
28
+ const start = Math.max(0, size - TAIL_BYTES);
29
+ const buf = Buffer.alloc(size - start);
30
+ await handle.read(buf, 0, buf.length, start);
31
+ text = buf.toString("utf-8");
32
+ }
33
+ finally {
34
+ await handle.close();
35
+ }
36
+ }
37
+ catch {
38
+ return [];
39
+ }
40
+ const out = [];
41
+ for (const line of text.split("\n")) {
42
+ if (!line.startsWith("{"))
43
+ continue;
44
+ try {
45
+ const entry = JSON.parse(line);
46
+ if (entry.type === "message" && entry.message?.role) {
47
+ out.push({ role: entry.message.role, content: entry.message.content });
48
+ }
49
+ }
50
+ catch {
51
+ // A line cut at the start of the read window.
52
+ }
53
+ }
54
+ return out;
55
+ }
56
+ /** A goal's status for the list (see GoalSummary.status). */
57
+ export function goalListStatus(record, busy, cutOff = false) {
58
+ if (record.pausedAt)
59
+ return "paused";
60
+ if (busy)
61
+ return "running";
62
+ if (cutOff)
63
+ return "interrupted";
64
+ return record.startedAt ? "idle" : "briefing";
65
+ }
66
+ /** Summarize one goal for a list, from its record and its coordinator sessions. */
67
+ export async function summarizeGoal(sessions, dir, record, busy = false) {
68
+ const list = await sessions.list(dir).catch(() => []);
69
+ const withMessages = list.filter((s) => s.messageCount > 0);
70
+ let lastActiveMs = Date.parse(record.createdAt) || 0;
71
+ let latestSessionPath = null;
72
+ let latestSessionId = null;
73
+ let latestMs = -1;
74
+ for (const s of withMessages) {
75
+ const ms = Date.parse(s.lastActivity) || 0;
76
+ if (ms > latestMs) {
77
+ latestMs = ms;
78
+ latestSessionPath = s.path;
79
+ latestSessionId = s.id;
80
+ }
81
+ if (ms > lastActiveMs)
82
+ lastActiveMs = ms;
83
+ }
84
+ return {
85
+ id: record.id,
86
+ name: record.name,
87
+ goal: record.goal,
88
+ context: record.context,
89
+ path: dir,
90
+ createdAt: record.createdAt,
91
+ lastActiveMs,
92
+ lastActiveDisplay: formatRelativeTime(lastActiveMs),
93
+ latestSessionPath,
94
+ latestSessionId,
95
+ status: goalListStatus(record, busy,
96
+ // Not working, but its last turn never finished: the app closed, or it
97
+ // was stopped, mid-work. Workers aren't known here, only the session.
98
+ !busy &&
99
+ Boolean(record.startedAt) &&
100
+ latestSessionPath !== null &&
101
+ goalWasInterrupted(record, await lastMessages(latestSessionPath), [])),
102
+ };
103
+ }
104
+ //# sourceMappingURL=goal-summary.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"goal-summary.js","sourceRoot":"","sources":["../../src/core/goal-summary.ts"],"names":[],"mappings":"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"}
@@ -28,3 +28,11 @@ export interface PendingPlanOfferInput {
28
28
  queued: number;
29
29
  }
30
30
  export declare function shouldOfferPendingPlan(input: Readonly<PendingPlanOfferInput>): boolean;
31
+ /** The tool result exit_plan returns while a submitted plan awaits review. */
32
+ export declare const PLAN_SUBMITTED_FOR_REVIEW = "Plan submitted for user review. Wait for the user to approve, reject, or dismiss it before implementing.";
33
+ /** The prompt run in the fresh session after a plan is accepted — the SAME
34
+ * string gg-app's PlanReviewModal sends on a manual Accept (App.tsx). */
35
+ export declare const IMPLEMENT_PLAN_PROMPT = "The plan has been approved. Implement it now, following each step in order.";
36
+ /** The revision prompt for the user's plan feedback (gg-app App.tsx
37
+ * sendPlanFeedback wording). */
38
+ export declare function buildUserPlanFeedbackPrompt(feedback: string): string;
@@ -7,4 +7,15 @@ export function shouldOfferPendingPlan(input) {
7
7
  return false;
8
8
  return input.queued === 0;
9
9
  }
10
+ /** The tool result exit_plan returns while a submitted plan awaits review. */
11
+ export const PLAN_SUBMITTED_FOR_REVIEW = "Plan submitted for user review. Wait for the user to approve, reject, or dismiss it before implementing.";
12
+ /** The prompt run in the fresh session after a plan is accepted — the SAME
13
+ * string gg-app's PlanReviewModal sends on a manual Accept (App.tsx). */
14
+ export const IMPLEMENT_PLAN_PROMPT = "The plan has been approved. Implement it now, following each step in order.";
15
+ /** The revision prompt for the user's plan feedback (gg-app App.tsx
16
+ * sendPlanFeedback wording). */
17
+ export function buildUserPlanFeedbackPrompt(feedback) {
18
+ return (`The plan was not approved. Feedback from the user:\n\n${feedback}\n\n` +
19
+ "Revise the plan based on this feedback, then call exit_plan again for review.");
20
+ }
10
21
  //# sourceMappingURL=plan-handoff.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"plan-handoff.js","sourceRoot":"","sources":["../../src/core/plan-handoff.ts"],"names":[],"mappings":"AA8BA,MAAM,UAAU,sBAAsB,CAAC,KAAsC;IAC3E,IAAI,KAAK,CAAC,QAAQ,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAC1C,IAAI,KAAK,CAAC,UAAU,KAAK,KAAK,CAAC,iBAAiB;QAAE,OAAO,KAAK,CAAC;IAC/D,IAAI,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,eAAe;QAAE,OAAO,KAAK,CAAC;IAC3E,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC;AAC5B,CAAC"}
1
+ {"version":3,"file":"plan-handoff.js","sourceRoot":"","sources":["../../src/core/plan-handoff.ts"],"names":[],"mappings":"AA8BA,MAAM,UAAU,sBAAsB,CAAC,KAAsC;IAC3E,IAAI,KAAK,CAAC,QAAQ,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAC1C,IAAI,KAAK,CAAC,UAAU,KAAK,KAAK,CAAC,iBAAiB;QAAE,OAAO,KAAK,CAAC;IAC/D,IAAI,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,eAAe;QAAE,OAAO,KAAK,CAAC;IAC3E,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC;AAC5B,CAAC;AAED,8EAA8E;AAC9E,MAAM,CAAC,MAAM,yBAAyB,GACpC,0GAA0G,CAAC;AAE7G;0EAC0E;AAC1E,MAAM,CAAC,MAAM,qBAAqB,GAChC,6EAA6E,CAAC;AAEhF;iCACiC;AACjC,MAAM,UAAU,2BAA2B,CAAC,QAAgB;IAC1D,OAAO,CACL,yDAAyD,QAAQ,MAAM;QACvE,+EAA+E,CAChF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Autopilot Ken for `ggcoder acp`: the reviewer session the autopilot cycle
3
+ * prompts. Built like the desktop's `ensureKenAutoSession` (app-sidecar.ts):
4
+ * Ken's autopilot system prompt, Ken's read-only tool set, a transient
5
+ * session, long cache retention, and the project's Ken model pin when one is
6
+ * valid (otherwise Ken follows GG Coder's model, as on the desktop).
7
+ */
8
+ import type { Provider } from "@kenkaiiii/gg-ai";
9
+ /** The reviewer the ACP autopilot cycle talks to. A seam so tests can script Ken. */
10
+ export interface AcpKenReviewer {
11
+ /** Wipe review history (start of each cycle). */
12
+ reset(): Promise<void>;
13
+ /** Prompt Ken with a digest; resolves to his final reply text. */
14
+ review(digest: string): Promise<string>;
15
+ /** Stop an in-flight review (session/cancel, queued message). */
16
+ abort(): void;
17
+ dispose(): Promise<void>;
18
+ }
19
+ export interface AcpKenReviewerOptions {
20
+ cwd: string;
21
+ /** GG Coder's current model; Ken follows it unless the project pins his own. */
22
+ buildModel: () => {
23
+ provider: Provider;
24
+ model: string;
25
+ };
26
+ }
27
+ export declare function createAcpKenReviewer(options: AcpKenReviewerOptions): AcpKenReviewer;
@@ -0,0 +1,54 @@
1
+ import { getModel } from "@kenkaiiii/gg-core";
2
+ import { AgentSession } from "../core/agent-session.js";
3
+ import { buildKenAutopilotSystemPrompt } from "../core/ken-prompt.js";
4
+ import { validateKenModelPref } from "../core/ken-model.js";
5
+ import { KEN_ALLOWED_TOOLS, lastAssistantText } from "../app-sidecar/ken-context.js";
6
+ import { loadKenModelPref } from "../app-sidecar/app-settings.js";
7
+ export function createAcpKenReviewer(options) {
8
+ let abort = new AbortController();
9
+ let ken = null;
10
+ async function ensure() {
11
+ if (ken)
12
+ return ken;
13
+ const pinned = validateKenModelPref(await loadKenModelPref(options.cwd), {
14
+ modelExists: (id) => getModel(id) !== undefined,
15
+ providerConnected: () => true,
16
+ });
17
+ const target = pinned ?? options.buildModel();
18
+ const created = new AgentSession({
19
+ provider: target.provider,
20
+ model: target.model,
21
+ cwd: options.cwd,
22
+ systemPrompt: await buildKenAutopilotSystemPrompt(options.cwd),
23
+ allowedTools: KEN_ALLOWED_TOOLS,
24
+ transient: true,
25
+ signal: abort.signal,
26
+ forceLongCacheRetention: true,
27
+ });
28
+ await created.initialize();
29
+ ken = created;
30
+ return created;
31
+ }
32
+ return {
33
+ async reset() {
34
+ await ken?.newSession().catch(() => undefined);
35
+ },
36
+ async review(digest) {
37
+ const session = await ensure();
38
+ await session.prompt(digest);
39
+ return lastAssistantText(session.getMessages());
40
+ },
41
+ abort() {
42
+ abort.abort();
43
+ abort = new AbortController();
44
+ ken?.setSignal(abort.signal);
45
+ },
46
+ async dispose() {
47
+ abort.abort();
48
+ const current = ken;
49
+ ken = null;
50
+ await current?.dispose();
51
+ },
52
+ };
53
+ }
54
+ //# sourceMappingURL=acp-autopilot.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"acp-autopilot.js","sourceRoot":"","sources":["../../src/modes/acp-autopilot.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACxD,OAAO,EAAE,6BAA6B,EAAE,MAAM,uBAAuB,CAAC;AACtE,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AACrF,OAAO,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAmBlE,MAAM,UAAU,oBAAoB,CAAC,OAA8B;IACjE,IAAI,KAAK,GAAG,IAAI,eAAe,EAAE,CAAC;IAClC,IAAI,GAAG,GAAwB,IAAI,CAAC;IAEpC,KAAK,UAAU,MAAM;QACnB,IAAI,GAAG;YAAE,OAAO,GAAG,CAAC;QACpB,MAAM,MAAM,GAAG,oBAAoB,CAAC,MAAM,gBAAgB,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE;YACvE,WAAW,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,KAAK,SAAS;YAC/C,iBAAiB,EAAE,GAAG,EAAE,CAAC,IAAI;SAC9B,CAAC,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;QAC9C,MAAM,OAAO,GAAG,IAAI,YAAY,CAAC;YAC/B,QAAQ,EAAE,MAAM,CAAC,QAAQ;YACzB,KAAK,EAAE,MAAM,CAAC,KAAK;YACnB,GAAG,EAAE,OAAO,CAAC,GAAG;YAChB,YAAY,EAAE,MAAM,6BAA6B,CAAC,OAAO,CAAC,GAAG,CAAC;YAC9D,YAAY,EAAE,iBAAiB;YAC/B,SAAS,EAAE,IAAI;YACf,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,uBAAuB,EAAE,IAAI;SAC9B,CAAC,CAAC;QACH,MAAM,OAAO,CAAC,UAAU,EAAE,CAAC;QAC3B,GAAG,GAAG,OAAO,CAAC;QACd,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,OAAO;QACL,KAAK,CAAC,KAAK;YACT,MAAM,GAAG,EAAE,UAAU,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QACjD,CAAC;QACD,KAAK,CAAC,MAAM,CAAC,MAAM;YACjB,MAAM,OAAO,GAAG,MAAM,MAAM,EAAE,CAAC;YAC/B,MAAM,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YAC7B,OAAO,iBAAiB,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;QAClD,CAAC;QACD,KAAK;YACH,KAAK,CAAC,KAAK,EAAE,CAAC;YACd,KAAK,GAAG,IAAI,eAAe,EAAE,CAAC;YAC9B,GAAG,EAAE,SAAS,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAC/B,CAAC;QACD,KAAK,CAAC,OAAO;YACX,KAAK,CAAC,KAAK,EAAE,CAAC;YACd,MAAM,OAAO,GAAG,GAAG,CAAC;YACpB,GAAG,GAAG,IAAI,CAAC;YACX,MAAM,OAAO,EAAE,OAAO,EAAE,CAAC;QAC3B,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,48 @@
1
+ import { type GoalRecord, type GoalResult } from "../core/goals-store.js";
2
+ import type { SubAgentSnapshot } from "../core/subagent-manager.js";
3
+ import type { AppAttachment } from "../app-sidecar/attachments.js";
4
+ /** `_gg/goals/list` row: the sidecar's GoalSummary, addressed by session id. */
5
+ export interface AcpGoalSummary {
6
+ id: string;
7
+ name: string;
8
+ goal: string;
9
+ context: GoalRecord["context"];
10
+ path: string;
11
+ createdAt: string;
12
+ lastActiveMs: number;
13
+ lastActiveDisplay: string;
14
+ /** The id `session/load` / `session/resume` take; null starts a fresh coordinator. */
15
+ agentSessionId: string | null;
16
+ /**
17
+ * Standing instructions, so a client's goal settings can show what is set
18
+ * (absent = none). The desktop app reads them from the record the same way.
19
+ */
20
+ instructions?: string;
21
+ /** Never `running`: ggcoder computes it without a live run (the client overlays that). */
22
+ status: "briefing" | "paused" | "interrupted" | "idle";
23
+ }
24
+ /** pew2 wire `GoalWorker`. */
25
+ export interface AcpGoalWorker {
26
+ id: string;
27
+ name: string;
28
+ agent?: string;
29
+ state: "starting" | "running" | "completed" | "failed" | "interrupted" | "closed";
30
+ activity?: string;
31
+ startedAt?: number;
32
+ elapsedMs?: number;
33
+ toolUses?: number;
34
+ model?: string;
35
+ error?: string;
36
+ }
37
+ /** Every goal, most recently active first. */
38
+ export declare function listAcpGoals(agentDir: string): Promise<AcpGoalSummary[]>;
39
+ /** Create a goal from `_gg/goals/create` params; the store's own error text on failure. */
40
+ export declare function createAcpGoal(agentDir: string, params: unknown): Promise<GoalResult<AcpGoalSummary>>;
41
+ /** The worker roster for `_gg/goal_state`. A reaped worker reads as closed. */
42
+ export declare function goalWorkers(snapshots: readonly SubAgentSnapshot[]): AcpGoalWorker[];
43
+ /**
44
+ * ACP `image` prompt blocks as the app's attachment shape, so they take the
45
+ * same `prepareAttachments` → `promptWithAttachments` path the desktop does.
46
+ * Blocks carrying only a `uri` (no inline data) are skipped.
47
+ */
48
+ export declare function acpImageAttachments(blocks: unknown): AppAttachment[];
@@ -0,0 +1,103 @@
1
+ /**
2
+ * GG Goals over ACP (`_gg/goals/*`, `_gg/goal/*`, `_gg/goal_state`): the pure
3
+ * pieces, kept out of `acp-mode.ts` so the transport stays readable. The goal
4
+ * list rows come from the same `summarizeGoal` the desktop sidecar uses.
5
+ */
6
+ import path from "node:path";
7
+ import { createGoal, goalsRoot, listGoals, } from "../core/goals-store.js";
8
+ import { summarizeGoal } from "../core/goal-summary.js";
9
+ import { SessionManager } from "../core/session-manager.js";
10
+ function toAcpSummary(summary, instructions) {
11
+ return {
12
+ ...(instructions ? { instructions } : {}),
13
+ id: summary.id,
14
+ name: summary.name,
15
+ goal: summary.goal,
16
+ context: summary.context,
17
+ path: summary.path,
18
+ createdAt: summary.createdAt,
19
+ lastActiveMs: summary.lastActiveMs,
20
+ lastActiveDisplay: summary.lastActiveDisplay,
21
+ agentSessionId: summary.latestSessionId,
22
+ // summarizeGoal is never told the goal is busy, so `running` cannot occur.
23
+ status: summary.status === "running" ? "idle" : summary.status,
24
+ };
25
+ }
26
+ function sessionsDir(agentDir) {
27
+ return path.join(agentDir, "sessions");
28
+ }
29
+ /** Every goal, most recently active first. */
30
+ export async function listAcpGoals(agentDir) {
31
+ const sessions = new SessionManager(sessionsDir(agentDir));
32
+ const goals = await listGoals(goalsRoot(agentDir));
33
+ const rows = await Promise.all(goals.map(async (g) => toAcpSummary(await summarizeGoal(sessions, g.dir, g.record), g.record.instructions)));
34
+ rows.sort((a, b) => b.lastActiveMs - a.lastActiveMs || a.id.localeCompare(b.id));
35
+ return rows;
36
+ }
37
+ /** Create a goal from `_gg/goals/create` params; the store's own error text on failure. */
38
+ export async function createAcpGoal(agentDir, params) {
39
+ const input = params && typeof params === "object"
40
+ ? params
41
+ : {};
42
+ const created = await createGoal(goalsRoot(agentDir), {
43
+ name: typeof input.name === "string" ? input.name : "",
44
+ goal: typeof input.goal === "string" ? input.goal : "",
45
+ context: Array.isArray(input.context)
46
+ ? input.context.filter((p) => typeof p === "string")
47
+ : [],
48
+ ...(typeof input.instructions === "string" ? { instructions: input.instructions } : {}),
49
+ }, new Date());
50
+ if (!created.ok)
51
+ return created;
52
+ const sessions = new SessionManager(sessionsDir(agentDir));
53
+ return {
54
+ ok: true,
55
+ value: toAcpSummary(await summarizeGoal(sessions, created.value.dir, created.value.record), created.value.record.instructions),
56
+ };
57
+ }
58
+ /** The worker roster for `_gg/goal_state`. A reaped worker reads as closed. */
59
+ export function goalWorkers(snapshots) {
60
+ return snapshots.map((s) => ({
61
+ id: s.agent_id,
62
+ name: s.task_name,
63
+ ...(s.agent_name ? { agent: s.agent_name } : {}),
64
+ state: s.state === "reaped" ? "closed" : s.state,
65
+ ...(s.current_activity ? { activity: s.current_activity } : {}),
66
+ startedAt: s.started_at,
67
+ elapsedMs: s.elapsed_ms,
68
+ toolUses: s.tool_use_count,
69
+ ...(s.model ? { model: s.model } : {}),
70
+ ...(s.error ? { error: s.error } : {}),
71
+ }));
72
+ }
73
+ const IMAGE_EXTENSIONS = {
74
+ "image/png": "png",
75
+ "image/jpeg": "jpg",
76
+ "image/gif": "gif",
77
+ "image/webp": "webp",
78
+ };
79
+ /**
80
+ * ACP `image` prompt blocks as the app's attachment shape, so they take the
81
+ * same `prepareAttachments` → `promptWithAttachments` path the desktop does.
82
+ * Blocks carrying only a `uri` (no inline data) are skipped.
83
+ */
84
+ export function acpImageAttachments(blocks) {
85
+ if (!Array.isArray(blocks))
86
+ return [];
87
+ const out = [];
88
+ for (const block of blocks) {
89
+ const entry = block;
90
+ if (entry?.type !== "image" || typeof entry.data !== "string" || !entry.data)
91
+ continue;
92
+ const mediaType = typeof entry.mimeType === "string" ? entry.mimeType : "image/png";
93
+ const ext = IMAGE_EXTENSIONS[mediaType] ?? "img";
94
+ out.push({
95
+ kind: "image",
96
+ name: `image-${out.length + 1}.${ext}`,
97
+ mediaType,
98
+ data: entry.data,
99
+ });
100
+ }
101
+ return out;
102
+ }
103
+ //# sourceMappingURL=acp-goals.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"acp-goals.js","sourceRoot":"","sources":["../../src/modes/acp-goals.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,12 @@
1
- import type { Message, Provider, ThinkingLevel } from "@kenkaiiii/gg-ai";
1
+ import type { Message, MessageProvenance, Provider, ThinkingLevel } from "@kenkaiiii/gg-ai";
2
2
  import { type ToolExecuteResult } from "@kenkaiiii/gg-agent";
3
+ import { AgentSession, type SessionAttachment } from "../core/agent-session.js";
4
+ import type { ChildModelPick, SubAgentSnapshot } from "../core/subagent-manager.js";
5
+ import { createAskUserTool } from "../tools/ask-user.js";
6
+ import { type GoalBriefExtras, type GoalRecord } from "../core/goals-store.js";
3
7
  import type { EventBus } from "../core/event-bus.js";
8
+ import type { AutopilotMarkerPayload } from "../core/session-manager.js";
9
+ import { type AcpKenReviewer } from "./acp-autopilot.js";
4
10
  /** The ACP major version this mode implements. Bumped only for breaking changes. */
5
11
  export declare const ACP_PROTOCOL_VERSION = 1;
6
12
  /**
@@ -13,7 +19,32 @@ export declare const ACP_PROTOCOL_VERSION = 1;
13
19
  export interface AcpAgentSession {
14
20
  readonly eventBus: Pick<EventBus, "on">;
15
21
  initialize(): Promise<void>;
16
- prompt(content: string): Promise<void>;
22
+ /** `provenance` marks GG Goals' hidden kickoff/resume instructions. */
23
+ prompt(content: string, provenance?: MessageProvenance): Promise<void>;
24
+ /** Image prompts (`promptCapabilities.image`); without it images are ignored. */
25
+ promptWithAttachments?(text: string, attachments: SessionAttachment[]): Promise<void>;
26
+ /**
27
+ * Mid-run steering (`_gg/queue`). Optional so a test double or alternative
28
+ * session need not support it; `_gg/queue` then answers -32602.
29
+ */
30
+ queueMessage?(text: string): number;
31
+ listQueuedMessages?(): Array<{
32
+ id: string;
33
+ text: string;
34
+ }>;
35
+ cancelQueuedMessage?(id: string): boolean;
36
+ drainQueue?(): string;
37
+ /** GG Goals' workers, for `_gg/goal_state`. */
38
+ listSubAgents?(): SubAgentSnapshot[];
39
+ /** Adds a goal's attached folder to the workspace. */
40
+ addDirectory?(dir: string): Promise<{
41
+ ok: true;
42
+ } | {
43
+ ok: false;
44
+ error: string;
45
+ }>;
46
+ /** Tell the agent a fact at its next turn (goal settings changed). */
47
+ notify?(id: string, text: string): void;
17
48
  getState(): {
18
49
  sessionId: string;
19
50
  provider: Provider;
@@ -34,6 +65,16 @@ export interface AcpAgentSession {
34
65
  setSignal(signal: AbortSignal): void;
35
66
  /** Replay a session file from disk into this session. */
36
67
  loadSession(sessionPath: string): Promise<void>;
68
+ /**
69
+ * Ken's verdict markers (desktop `persistAutopilotMarker`). Optional so a
70
+ * test double need not persist; without them a reopened session has no
71
+ * Ken bubbles.
72
+ */
73
+ persistAutopilotMarker?(phase: AutopilotMarkerPayload["phase"], extra?: {
74
+ reason?: string;
75
+ body?: string;
76
+ }): Promise<void>;
77
+ getAutopilotMarkers?(): AutopilotMarkerPayload[];
37
78
  /** The conversation as restored, used to replay history to the client. */
38
79
  getMessages(): Message[];
39
80
  switchModel(provider: string, model: string): Promise<void>;
@@ -44,6 +85,12 @@ export interface AcpAgentSession {
44
85
  setPlanMode(active: boolean): Promise<void>;
45
86
  /** Bake an approved plan into the prompt so [DONE:n] progress markers work. */
46
87
  setApprovedPlan(planPath: string | undefined): Promise<void>;
88
+ /** Host verification evidence for Ken's digest; optional for test doubles. */
89
+ getVerificationEvidence?(): AgentSession["getVerificationEvidence"] extends () => infer R ? R : never;
90
+ getVerificationProblem?(): string | null;
91
+ /** Fresh context for an accepted plan (desktop /plan/accept). Optional:
92
+ * without it an accept keeps the current context and sends no reset. */
93
+ newSession?(preserveConversation?: boolean): Promise<void>;
47
94
  /**
48
95
  * Registry commands (/model, /compact, …), including any an extension
49
96
  * registered during `initialize`. Optional so a test double need not carry a
@@ -71,6 +118,52 @@ export interface AcpPlanHooks {
71
118
  /** Returns the instruction handed back to the model after approval. */
72
119
  onExitPlan: (planPath: string) => Promise<ToolExecuteResult>;
73
120
  }
121
+ /**
122
+ * What a session is being built for, beyond the plan hooks.
123
+ *
124
+ * A third argument rather than a change to the first two so existing session
125
+ * factories (tests) keep working unchanged.
126
+ */
127
+ export interface AcpSessionContext {
128
+ /** The session's working directory: the goal folder for a goal session. */
129
+ cwd: string;
130
+ /** Set when the cwd is a goal folder: build a GG Goals coordinator. */
131
+ goal: {
132
+ dir: string;
133
+ record: GoalRecord;
134
+ } | null;
135
+ /** The goal's brief extras (memory, attached projects' rules), read once. */
136
+ goalExtras?: GoalBriefExtras;
137
+ /** Read live: true once the user has started the goal. */
138
+ isGoalActive: () => boolean;
139
+ /** Read live at each worker spawn. */
140
+ getChildModel: () => ChildModelPick | undefined;
141
+ /**
142
+ * The `ask_user` tool, present only when the client opted in with
143
+ * `clientCapabilities._meta.gg.askUser`. Goes in `additionalTools` at
144
+ * construction (the tool array is append-only for the prompt cache).
145
+ */
146
+ askUserTool?: ReturnType<typeof createAskUserTool>;
147
+ /**
148
+ * The client reviews plans itself (`clientCapabilities._meta.gg.planReview`):
149
+ * exit_plan parks the plan for a human decision instead of auto-approving,
150
+ * and the session gets the desktop's `planSubmissionGate`.
151
+ */
152
+ planReview?: boolean;
153
+ }
154
+ /**
155
+ * A plan submitted via exit_plan that awaits a decision (desktop parity:
156
+ * app-sidecar.ts pendingPlanPath/planGeneration/offeredPlanGeneration). One
157
+ * object so the autopilot cycle can read and settle it.
158
+ */
159
+ export interface AcpPendingPlanState {
160
+ planPath: string | null;
161
+ content: string;
162
+ /** Bumped on every set/clear; a stale decision is refused. */
163
+ generation: number;
164
+ /** Generation last offered to the client; -1 when none. */
165
+ offeredGeneration: number;
166
+ }
74
167
  export interface AcpModeOptions {
75
168
  provider: Provider;
76
169
  model: string;
@@ -86,7 +179,22 @@ export interface AcpModeOptions {
86
179
  * Builds the session for `session/new`. Overridden by tests; production
87
180
  * always gets a real {@link AgentSession}.
88
181
  */
89
- createSession?: (signal: AbortSignal, hooks: AcpPlanHooks) => AcpAgentSession;
182
+ createSession?: (signal: AbortSignal, hooks: AcpPlanHooks, context: AcpSessionContext) => AcpAgentSession;
183
+ /** `~/.gg` by default; where goals (`goals/`) and sessions live. */
184
+ agentDir?: string;
185
+ /**
186
+ * `ask_user` soft deadline (default: the desktop's interactive 10 min). Past
187
+ * it the model hears "no answer yet" and the client's question stays open.
188
+ */
189
+ askUserDeadlineMs?: number;
190
+ /**
191
+ * Builds Autopilot Ken's reviewer for a session's project. Overridden by
192
+ * tests; production gets {@link createAcpKenReviewer}.
193
+ */
194
+ createKenReviewer?: (cwd: string, buildModel: () => {
195
+ provider: Provider;
196
+ model: string;
197
+ }) => AcpKenReviewer;
90
198
  }
91
199
  /** The selectors this agent exposes. Ids are part of the wire contract. */
92
200
  export declare const MODEL_CONFIG_ID = "model";
@@ -107,6 +215,23 @@ export declare const MODE_CONFIG_ID = "mode";
107
215
  * the protocol needs of them.
108
216
  */
109
217
  export declare function historyUpdates(messages: readonly Message[]): Record<string, unknown>[];
218
+ /** A replayed Ken marker, as `_gg/autopilot_event` params (sans sessionId). */
219
+ export interface HistoryAutopilotEvent {
220
+ phase: string;
221
+ reason?: string;
222
+ body?: string;
223
+ history: true;
224
+ }
225
+ /**
226
+ * {@link historyUpdates} with Ken's verdict markers interleaved at their
227
+ * `afterMessageCount` (non-system messages, as the desktop history route
228
+ * counts them). Marker phase `plan_approved` replays as ACP `plan_accepted`.
229
+ */
230
+ export declare function historyFrames(messages: readonly Message[], markers: readonly AutopilotMarkerPayload[]): Array<{
231
+ update: Record<string, unknown>;
232
+ } | {
233
+ autopilot: HistoryAutopilotEvent;
234
+ }>;
110
235
  /**
111
236
  * Serve ACP on stdio until the input stream ends.
112
237
  *