@kenkaiiii/ggcoder 5.77.0 → 5.79.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.
- package/dist/app-sidecar/autopilot-runner.js +2 -5
- package/dist/app-sidecar/autopilot-runner.js.map +1 -1
- package/dist/app-sidecar/goal-routes.d.ts +26 -0
- package/dist/app-sidecar/goal-routes.js +444 -0
- package/dist/app-sidecar/goal-routes.js.map +1 -0
- package/dist/app-sidecar/history-routes.d.ts +13 -0
- package/dist/app-sidecar/history-routes.js +36 -25
- package/dist/app-sidecar/history-routes.js.map +1 -1
- package/dist/app-sidecar/route-context.d.ts +12 -0
- package/dist/app-sidecar/run-control-routes.js +2 -1
- package/dist/app-sidecar/run-control-routes.js.map +1 -1
- package/dist/app-sidecar/session-routes.js +41 -0
- package/dist/app-sidecar/session-routes.js.map +1 -1
- package/dist/app-sidecar/session-types.d.ts +2 -0
- package/dist/app-sidecar/workspace-routes.js +3 -0
- package/dist/app-sidecar/workspace-routes.js.map +1 -1
- package/dist/app-sidecar.js +113 -9
- package/dist/app-sidecar.js.map +1 -1
- package/dist/core/acceptance-checks.js +17 -4
- package/dist/core/acceptance-checks.js.map +1 -1
- package/dist/core/agent-notifications.d.ts +1 -1
- package/dist/core/agent-session/verification-tracker.d.ts +5 -1
- package/dist/core/agent-session/verification-tracker.js +25 -6
- package/dist/core/agent-session/verification-tracker.js.map +1 -1
- package/dist/core/agent-session.d.ts +50 -0
- package/dist/core/agent-session.js +49 -11
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/ask-user.d.ts +11 -0
- package/dist/core/ask-user.js +6 -1
- package/dist/core/ask-user.js.map +1 -1
- package/dist/core/autopilot-cycle.d.ts +2 -0
- package/dist/core/autopilot-cycle.js +21 -2
- package/dist/core/autopilot-cycle.js.map +1 -1
- package/dist/core/context-files.d.ts +8 -0
- package/dist/core/context-files.js +46 -0
- package/dist/core/context-files.js.map +1 -0
- package/dist/core/goals-store.d.ts +208 -0
- package/dist/core/goals-store.js +520 -0
- package/dist/core/goals-store.js.map +1 -0
- package/dist/core/project-discovery.d.ts +7 -0
- package/dist/core/project-discovery.js +8 -2
- package/dist/core/project-discovery.js.map +1 -1
- package/dist/core/prompt-commands.js +29 -4
- package/dist/core/prompt-commands.js.map +1 -1
- package/dist/core/session-manager.d.ts +7 -1
- package/dist/core/session-manager.js +17 -7
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/session-storage.js +5 -1
- package/dist/core/session-storage.js.map +1 -1
- package/dist/core/subagent-manager.d.ts +51 -0
- package/dist/core/subagent-manager.js +95 -15
- package/dist/core/subagent-manager.js.map +1 -1
- package/dist/core/subagent-store.js +2 -1
- package/dist/core/subagent-store.js.map +1 -1
- package/dist/core/verification-evidence.d.ts +13 -2
- package/dist/core/verification-evidence.js +256 -3
- package/dist/core/verification-evidence.js.map +1 -1
- package/dist/goals-agent/goal-team.d.ts +2 -0
- package/dist/goals-agent/goal-team.js +82 -0
- package/dist/goals-agent/goal-team.js.map +1 -0
- package/dist/goals-agent/goals-agent.d.ts +90 -0
- package/dist/goals-agent/goals-agent.js +246 -0
- package/dist/goals-agent/goals-agent.js.map +1 -0
- package/dist/modes/subagent-worker-mode.d.ts +12 -0
- package/dist/modes/subagent-worker-mode.js +22 -1
- package/dist/modes/subagent-worker-mode.js.map +1 -1
- package/dist/system-prompt.js +20 -11
- package/dist/system-prompt.js.map +1 -1
- package/dist/tools/bash-read-evidence.d.ts +13 -0
- package/dist/tools/bash-read-evidence.js +48 -1
- package/dist/tools/bash-read-evidence.js.map +1 -1
- package/dist/tools/bash.d.ts +3 -1
- package/dist/tools/bash.js +8 -4
- package/dist/tools/bash.js.map +1 -1
- package/dist/tools/edit.d.ts +1 -0
- package/dist/tools/edit.js +84 -1
- package/dist/tools/edit.js.map +1 -1
- package/dist/tools/index.d.ts +7 -1
- package/dist/tools/index.js +13 -4
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/read-only-bash.js +3 -0
- package/dist/tools/read-only-bash.js.map +1 -1
- package/dist/tools/read-tracker.d.ts +6 -12
- package/dist/tools/read-tracker.js +21 -36
- package/dist/tools/read-tracker.js.map +1 -1
- package/dist/tools/read.js +2 -2
- package/dist/tools/read.js.map +1 -1
- package/dist/tools/subagent-control.js +22 -3
- package/dist/tools/subagent-control.js.map +1 -1
- package/dist/tools/write.js +110 -8
- package/dist/tools/write.js.map +1 -1
- package/package.json +4 -4
|
@@ -0,0 +1,520 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { encodeCwd } from "./encode-cwd.js";
|
|
5
|
+
/**
|
|
6
|
+
* Goals: a long-running objective the user hands to GG Coder ("Bring p95
|
|
7
|
+
* latency under 200 ms across the whole API"). Each goal is its own folder under
|
|
8
|
+
* `~/.gg/goals/<id>/` holding `goal.json`; that folder is also the goal's
|
|
9
|
+
* workspace (the coordinator session's cwd) and, later, its shared library.
|
|
10
|
+
* Context folders and files the user attaches live elsewhere on disk and are
|
|
11
|
+
* referenced by absolute path.
|
|
12
|
+
*/
|
|
13
|
+
export const GOAL_FILE = "goal.json";
|
|
14
|
+
/** New names stay short enough to read at a glance in the goal list. */
|
|
15
|
+
export const GOAL_NAME_MAX = 15;
|
|
16
|
+
/** Stored names may be longer (created before the limit); still bounded. */
|
|
17
|
+
const GOAL_NAME_STORED_MAX = 80;
|
|
18
|
+
export const GOAL_TEXT_MAX = 2000;
|
|
19
|
+
export const GOAL_CONTEXT_MAX = 20;
|
|
20
|
+
/** Standing instructions the user gives a goal (rules that hold throughout). */
|
|
21
|
+
export const GOAL_INSTRUCTIONS_MAX = 4000;
|
|
22
|
+
/** The goal's memory file in its folder: decisions and preferences learned along the way. */
|
|
23
|
+
export const GOAL_MEMORY_FILE = "MEMORY.md";
|
|
24
|
+
/** How much of the memory file is loaded into GG Goals' brief. */
|
|
25
|
+
export const GOAL_MEMORY_MAX = 16_000;
|
|
26
|
+
const contextItemSchema = z.object({
|
|
27
|
+
path: z.string().min(1),
|
|
28
|
+
kind: z.enum(["folder", "file"]),
|
|
29
|
+
});
|
|
30
|
+
const goalRecordSchema = z.object({
|
|
31
|
+
schemaVersion: z.literal(1),
|
|
32
|
+
id: z.string().min(1),
|
|
33
|
+
name: z.string().min(1).max(GOAL_NAME_STORED_MAX),
|
|
34
|
+
goal: z.string().min(1).max(GOAL_TEXT_MAX),
|
|
35
|
+
context: z.array(contextItemSchema).max(GOAL_CONTEXT_MAX),
|
|
36
|
+
createdAt: z.string(),
|
|
37
|
+
/** When the coordinator's first, automatic reply was started. Set once. */
|
|
38
|
+
kickoffAt: z.string().optional(),
|
|
39
|
+
/** When the user chose to start the goal (briefing over, work begins). Set once. */
|
|
40
|
+
startedAt: z.string().optional(),
|
|
41
|
+
/** The model GG Goals' workers ("critters") run on; absent: GG Goals' own. */
|
|
42
|
+
critterModel: z.object({ provider: z.string().min(1), model: z.string().min(1) }).optional(),
|
|
43
|
+
/** Standing instructions: rules for the whole goal ("don't touch CI"). */
|
|
44
|
+
instructions: z.string().max(GOAL_INSTRUCTIONS_MAX).optional(),
|
|
45
|
+
/** Paused by the user: GG Goals stops at the next safe point and doesn't carry on by itself. */
|
|
46
|
+
pausedAt: z.string().optional(),
|
|
47
|
+
});
|
|
48
|
+
/** What the app sends to create a goal. Context kinds are re-derived on disk. */
|
|
49
|
+
export const createGoalInputSchema = z.object({
|
|
50
|
+
name: z
|
|
51
|
+
.string()
|
|
52
|
+
.trim()
|
|
53
|
+
.min(1, "Give the goal a name")
|
|
54
|
+
.max(GOAL_NAME_MAX, `Keep the name to ${GOAL_NAME_MAX} characters`),
|
|
55
|
+
goal: z.string().trim().min(1, "Describe the goal").max(GOAL_TEXT_MAX),
|
|
56
|
+
context: z.array(z.string().min(1)).max(GOAL_CONTEXT_MAX).default([]),
|
|
57
|
+
instructions: z
|
|
58
|
+
.string()
|
|
59
|
+
.trim()
|
|
60
|
+
.max(GOAL_INSTRUCTIONS_MAX, `Keep instructions to ${GOAL_INSTRUCTIONS_MAX} characters`)
|
|
61
|
+
.optional(),
|
|
62
|
+
});
|
|
63
|
+
/** What the app sends to change a goal's context or instructions after it's created. */
|
|
64
|
+
export const updateGoalInputSchema = z.object({
|
|
65
|
+
context: z.array(z.string().min(1)).max(GOAL_CONTEXT_MAX).optional(),
|
|
66
|
+
instructions: z
|
|
67
|
+
.string()
|
|
68
|
+
.trim()
|
|
69
|
+
.max(GOAL_INSTRUCTIONS_MAX, `Keep instructions to ${GOAL_INSTRUCTIONS_MAX} characters`)
|
|
70
|
+
.optional(),
|
|
71
|
+
});
|
|
72
|
+
/** Folder that holds every goal. */
|
|
73
|
+
export function goalsRoot(agentDir) {
|
|
74
|
+
return path.join(agentDir, "goals");
|
|
75
|
+
}
|
|
76
|
+
/** Lowercase dashed folder name; empty when the name has no usable characters. */
|
|
77
|
+
export function goalSlug(name) {
|
|
78
|
+
return name
|
|
79
|
+
.toLowerCase()
|
|
80
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
81
|
+
.replace(/^-+|-+$/g, "")
|
|
82
|
+
.slice(0, 48)
|
|
83
|
+
.replace(/-+$/g, "");
|
|
84
|
+
}
|
|
85
|
+
/** True when `dir` is exactly one goal folder directly inside `root`. */
|
|
86
|
+
export function isGoalDir(root, dir) {
|
|
87
|
+
const relative = path.relative(path.resolve(root), path.resolve(dir));
|
|
88
|
+
return (relative !== "" &&
|
|
89
|
+
!relative.startsWith("..") &&
|
|
90
|
+
!path.isAbsolute(relative) &&
|
|
91
|
+
!relative.includes(path.sep));
|
|
92
|
+
}
|
|
93
|
+
/** True when `target` is `root` or anything beneath it. */
|
|
94
|
+
export function isInsideGoalsRoot(root, target) {
|
|
95
|
+
const relative = path.relative(path.resolve(root), path.resolve(target));
|
|
96
|
+
return relative === "" || (!relative.startsWith("..") && !path.isAbsolute(relative));
|
|
97
|
+
}
|
|
98
|
+
/** Read and validate one goal folder's record; null when absent or invalid. */
|
|
99
|
+
export async function readGoal(dir) {
|
|
100
|
+
let raw;
|
|
101
|
+
try {
|
|
102
|
+
raw = await fs.readFile(path.join(dir, GOAL_FILE), "utf-8");
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
let parsed;
|
|
108
|
+
try {
|
|
109
|
+
parsed = JSON.parse(raw);
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
const result = goalRecordSchema.safeParse(parsed);
|
|
115
|
+
return result.success ? result.data : null;
|
|
116
|
+
}
|
|
117
|
+
async function writeGoal(dir, record) {
|
|
118
|
+
const file = path.join(dir, GOAL_FILE);
|
|
119
|
+
const tmp = `${file}.${process.pid}.tmp`;
|
|
120
|
+
await fs.writeFile(tmp, `${JSON.stringify(record, null, 2)}\n`, "utf-8");
|
|
121
|
+
await fs.rename(tmp, file);
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Resolve the user's context picks to absolute, existing paths. Kinds come
|
|
125
|
+
* from the filesystem, never from the client. Duplicates collapse; order is
|
|
126
|
+
* kept so the list reads the way the user built it.
|
|
127
|
+
*/
|
|
128
|
+
async function resolveContext(paths) {
|
|
129
|
+
const items = [];
|
|
130
|
+
const seen = new Set();
|
|
131
|
+
for (const entry of paths) {
|
|
132
|
+
if (!path.isAbsolute(entry))
|
|
133
|
+
return { ok: false, error: `Not an absolute path: ${entry}` };
|
|
134
|
+
const resolved = path.resolve(entry);
|
|
135
|
+
if (seen.has(resolved))
|
|
136
|
+
continue;
|
|
137
|
+
seen.add(resolved);
|
|
138
|
+
let stat;
|
|
139
|
+
try {
|
|
140
|
+
stat = await fs.stat(resolved);
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
return { ok: false, error: `Not found: ${resolved}` };
|
|
144
|
+
}
|
|
145
|
+
if (stat.isDirectory())
|
|
146
|
+
items.push({ path: resolved, kind: "folder" });
|
|
147
|
+
else if (stat.isFile())
|
|
148
|
+
items.push({ path: resolved, kind: "file" });
|
|
149
|
+
else
|
|
150
|
+
return { ok: false, error: `Not a file or folder: ${resolved}` };
|
|
151
|
+
}
|
|
152
|
+
return { ok: true, value: items };
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Create a goal folder and its record. The folder name is the slug, suffixed
|
|
156
|
+
* `-2`, `-3`… when taken; `mkdir` without `recursive` makes the claim atomic.
|
|
157
|
+
*/
|
|
158
|
+
export async function createGoal(root, input, now) {
|
|
159
|
+
const parsed = createGoalInputSchema.safeParse(input);
|
|
160
|
+
if (!parsed.success) {
|
|
161
|
+
return { ok: false, error: parsed.error.issues[0]?.message ?? "Invalid goal" };
|
|
162
|
+
}
|
|
163
|
+
const { name, goal, context, instructions } = parsed.data;
|
|
164
|
+
const resolved = await resolveContext(context);
|
|
165
|
+
if (!resolved.ok)
|
|
166
|
+
return resolved;
|
|
167
|
+
const base = goalSlug(name) || "goal";
|
|
168
|
+
await fs.mkdir(root, { recursive: true });
|
|
169
|
+
for (let attempt = 1; attempt <= 100; attempt++) {
|
|
170
|
+
const id = attempt === 1 ? base : `${base}-${attempt}`;
|
|
171
|
+
const dir = path.join(root, id);
|
|
172
|
+
try {
|
|
173
|
+
await fs.mkdir(dir);
|
|
174
|
+
}
|
|
175
|
+
catch (error) {
|
|
176
|
+
if (error.code === "EEXIST")
|
|
177
|
+
continue;
|
|
178
|
+
throw error;
|
|
179
|
+
}
|
|
180
|
+
const record = {
|
|
181
|
+
schemaVersion: 1,
|
|
182
|
+
id,
|
|
183
|
+
name,
|
|
184
|
+
goal,
|
|
185
|
+
context: resolved.value,
|
|
186
|
+
createdAt: now.toISOString(),
|
|
187
|
+
...(instructions ? { instructions } : {}),
|
|
188
|
+
};
|
|
189
|
+
await writeGoal(dir, record);
|
|
190
|
+
return { ok: true, value: { dir, record } };
|
|
191
|
+
}
|
|
192
|
+
return { ok: false, error: "Too many goals share that name" };
|
|
193
|
+
}
|
|
194
|
+
/** Every valid goal under `root` with its folder. Unreadable folders are skipped. */
|
|
195
|
+
export async function listGoals(root) {
|
|
196
|
+
let entries;
|
|
197
|
+
try {
|
|
198
|
+
entries = await fs.readdir(root);
|
|
199
|
+
}
|
|
200
|
+
catch {
|
|
201
|
+
return [];
|
|
202
|
+
}
|
|
203
|
+
const goals = [];
|
|
204
|
+
for (const entry of entries.sort()) {
|
|
205
|
+
const dir = path.join(root, entry);
|
|
206
|
+
const record = await readGoal(dir);
|
|
207
|
+
if (record)
|
|
208
|
+
goals.push({ dir, record });
|
|
209
|
+
}
|
|
210
|
+
return goals;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* True while a goal still needs GG Goals' automatic first reply: the kickoff
|
|
214
|
+
* was never started and the conversation has no user or assistant message yet.
|
|
215
|
+
* The system prompt is not conversation, so it does not count.
|
|
216
|
+
*/
|
|
217
|
+
export function goalNeedsKickoff(record, messages) {
|
|
218
|
+
return !record.kickoffAt && !messages.some((m) => m.role !== "system");
|
|
219
|
+
}
|
|
220
|
+
export function goalPhase(record) {
|
|
221
|
+
return record.startedAt ? "active" : "briefing";
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* True when an ask_user result is the user choosing to start the goal. An
|
|
225
|
+
* answer comes back as the option's `value` when it has one, so pass the
|
|
226
|
+
* questions asked: an option labelled GOAL_START_OPTION counts by its value
|
|
227
|
+
* too (live bug: GG Goals gave it the value "start", the answer came back as
|
|
228
|
+
* "start", and the goal never started).
|
|
229
|
+
*/
|
|
230
|
+
export function isGoalStartAnswer(result, questions = []) {
|
|
231
|
+
if (result.action !== "answer")
|
|
232
|
+
return false;
|
|
233
|
+
const wanted = GOAL_START_OPTION.toLowerCase();
|
|
234
|
+
const startValues = new Set(questions.flatMap((question) => (question.options ?? [])
|
|
235
|
+
.filter((option) => option.label.trim().toLowerCase() === wanted && option.value)
|
|
236
|
+
.map((option) => (option.value ?? "").trim().toLowerCase())));
|
|
237
|
+
return Object.values(result.answers).some((answer) => (Array.isArray(answer) ? answer : [answer]).some((value) => {
|
|
238
|
+
const picked = value.trim().toLowerCase();
|
|
239
|
+
return picked === wanted || startValues.has(picked);
|
|
240
|
+
}));
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Start the goal if `result` is the user choosing to, whether it came back
|
|
244
|
+
* while GG Goals was still waiting on its question or after the question's
|
|
245
|
+
* deadline passed (a late answer). Returns the started record, or null when
|
|
246
|
+
* the answer isn't the start option or the goal had already started.
|
|
247
|
+
*/
|
|
248
|
+
export async function startGoalOnAnswer(dir, record, result, now, questions = []) {
|
|
249
|
+
if (record.startedAt || !isGoalStartAnswer(result, questions))
|
|
250
|
+
return null;
|
|
251
|
+
return markGoalStarted(dir, now);
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Pin the model the goal's workers run on, or clear it (null) so they follow
|
|
255
|
+
* GG Goals' model. Returns the updated record; null when the goal is gone.
|
|
256
|
+
*/
|
|
257
|
+
export async function setGoalCritterModel(dir, pick) {
|
|
258
|
+
const record = await readGoal(dir);
|
|
259
|
+
if (!record)
|
|
260
|
+
return null;
|
|
261
|
+
const { critterModel: _previous, ...rest } = record;
|
|
262
|
+
const next = pick ? { ...rest, critterModel: { ...pick } } : rest;
|
|
263
|
+
await writeGoal(dir, next);
|
|
264
|
+
return next;
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Record that the user started the goal. Returns the updated record, or null
|
|
268
|
+
* when it had already started (or the goal is gone), so it is set only once.
|
|
269
|
+
*/
|
|
270
|
+
export async function markGoalStarted(dir, now) {
|
|
271
|
+
const record = await readGoal(dir);
|
|
272
|
+
if (!record || record.startedAt)
|
|
273
|
+
return null;
|
|
274
|
+
const next = { ...record, startedAt: now.toISOString() };
|
|
275
|
+
await writeGoal(dir, next);
|
|
276
|
+
return next;
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Change a goal's attached context or standing instructions. Paths are
|
|
280
|
+
* re-resolved on disk like at creation; an empty instructions string clears
|
|
281
|
+
* them. Takes effect for the goal's next session (GG Goals' brief is fixed
|
|
282
|
+
* for a session, so its prompt cache holds).
|
|
283
|
+
*/
|
|
284
|
+
export async function updateGoal(dir, input) {
|
|
285
|
+
const parsed = updateGoalInputSchema.safeParse(input);
|
|
286
|
+
if (!parsed.success) {
|
|
287
|
+
return { ok: false, error: parsed.error.issues[0]?.message ?? "Invalid change" };
|
|
288
|
+
}
|
|
289
|
+
const record = await readGoal(dir);
|
|
290
|
+
if (!record)
|
|
291
|
+
return { ok: false, error: "Goal not found" };
|
|
292
|
+
let next = record;
|
|
293
|
+
if (parsed.data.context) {
|
|
294
|
+
const resolved = await resolveContext(parsed.data.context);
|
|
295
|
+
if (!resolved.ok)
|
|
296
|
+
return resolved;
|
|
297
|
+
next = { ...next, context: resolved.value };
|
|
298
|
+
}
|
|
299
|
+
if (parsed.data.instructions !== undefined) {
|
|
300
|
+
const { instructions: _old, ...rest } = next;
|
|
301
|
+
next = parsed.data.instructions ? { ...rest, instructions: parsed.data.instructions } : rest;
|
|
302
|
+
}
|
|
303
|
+
await writeGoal(dir, next);
|
|
304
|
+
return { ok: true, value: next };
|
|
305
|
+
}
|
|
306
|
+
/** Pause a goal (true) or let it carry on (false). Returns the updated record, or null if missing. */
|
|
307
|
+
export async function setGoalPaused(dir, paused, now) {
|
|
308
|
+
const record = await readGoal(dir);
|
|
309
|
+
if (!record)
|
|
310
|
+
return null;
|
|
311
|
+
const { pausedAt: _was, ...rest } = record;
|
|
312
|
+
const next = paused ? { ...rest, pausedAt: now.toISOString() } : rest;
|
|
313
|
+
await writeGoal(dir, next);
|
|
314
|
+
return next;
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* The goal's memory: what GG Goals has written down in its folder's
|
|
318
|
+
* MEMORY.md (decisions, the user's preferences and corrections, what's done
|
|
319
|
+
* and what's left). Empty when there's none yet. Long files are cut to
|
|
320
|
+
* GOAL_MEMORY_MAX, with a note, so the brief stays bounded.
|
|
321
|
+
*/
|
|
322
|
+
export async function readGoalMemory(dir) {
|
|
323
|
+
let text;
|
|
324
|
+
try {
|
|
325
|
+
text = await fs.readFile(path.join(dir, GOAL_MEMORY_FILE), "utf-8");
|
|
326
|
+
}
|
|
327
|
+
catch {
|
|
328
|
+
return "";
|
|
329
|
+
}
|
|
330
|
+
const trimmed = text.trim();
|
|
331
|
+
if (trimmed.length <= GOAL_MEMORY_MAX)
|
|
332
|
+
return trimmed;
|
|
333
|
+
return `${trimmed.slice(0, GOAL_MEMORY_MAX)}\n\n(Memory cut at ${GOAL_MEMORY_MAX} characters: keep ${GOAL_MEMORY_FILE} short, folding old entries into a summary.)`;
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* Delete one goal: its folder (record, notes, artifacts) and the coordinator
|
|
337
|
+
* sessions stored for it. Attached context lives elsewhere and is untouched.
|
|
338
|
+
* `id` must name a direct child of `root`; anything else is refused.
|
|
339
|
+
*/
|
|
340
|
+
export async function deleteGoal(root, sessionsDir, id) {
|
|
341
|
+
const dir = path.join(root, id);
|
|
342
|
+
if (!id || id !== path.basename(id) || !isGoalDir(root, dir)) {
|
|
343
|
+
return { ok: false, error: "Unknown goal" };
|
|
344
|
+
}
|
|
345
|
+
if (!(await readGoal(dir)))
|
|
346
|
+
return { ok: false, error: "Unknown goal" };
|
|
347
|
+
await fs.rm(dir, { recursive: true, force: true });
|
|
348
|
+
await fs.rm(path.join(sessionsDir, encodeCwd(dir)), { recursive: true, force: true });
|
|
349
|
+
return { ok: true, value: { dir } };
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* Record that the coordinator's first reply has started. Returns null when it
|
|
353
|
+
* already had (or the goal is gone), so a second caller never kicks off twice.
|
|
354
|
+
*/
|
|
355
|
+
export async function markGoalKickoff(dir, now) {
|
|
356
|
+
const record = await readGoal(dir);
|
|
357
|
+
if (!record || record.kickoffAt)
|
|
358
|
+
return null;
|
|
359
|
+
const next = { ...record, kickoffAt: now.toISOString() };
|
|
360
|
+
await writeGoal(dir, next);
|
|
361
|
+
return next;
|
|
362
|
+
}
|
|
363
|
+
/** The two answers to the coordinator's "ready to start?" question. */
|
|
364
|
+
export const GOAL_START_OPTION = "Start the goal now";
|
|
365
|
+
export const GOAL_HOLD_OPTION = "Hang on, I want to discuss it more";
|
|
366
|
+
export function renderGoalPrompt(dir, record, extras = {}) {
|
|
367
|
+
const context = record.context.length > 0
|
|
368
|
+
? record.context.map((item) => `- ${item.kind}: ${item.path}`).join("\n")
|
|
369
|
+
: "- (none attached yet)";
|
|
370
|
+
const rules = (extras.projectRules ?? []).filter((entry) => entry.rules.length > 0);
|
|
371
|
+
const memoryPath = path.join(dir, GOAL_MEMORY_FILE);
|
|
372
|
+
return [
|
|
373
|
+
"# Goal",
|
|
374
|
+
"",
|
|
375
|
+
`You are coordinating the goal "${record.name}". It is an ongoing piece of work that spans many sessions, not a single task.`,
|
|
376
|
+
"",
|
|
377
|
+
"Goal:",
|
|
378
|
+
record.goal,
|
|
379
|
+
"",
|
|
380
|
+
"Context the user attached (folders are in your workspace and every worker's; read files by path and name them in the briefs of workers that need them):",
|
|
381
|
+
context,
|
|
382
|
+
"",
|
|
383
|
+
`Goal folder (shared notes and artifacts for this goal): ${dir}`,
|
|
384
|
+
"",
|
|
385
|
+
...(record.instructions
|
|
386
|
+
? [
|
|
387
|
+
"## Standing instructions from the user",
|
|
388
|
+
"They hold for the whole goal and override your own defaults.",
|
|
389
|
+
record.instructions,
|
|
390
|
+
"",
|
|
391
|
+
]
|
|
392
|
+
: []),
|
|
393
|
+
...rules.flatMap((entry) => [
|
|
394
|
+
`## Project rules: ${entry.folder}`,
|
|
395
|
+
"The instruction files of an attached project (nearest last). Follow them when you work there, and give a worker the parts that apply to its task.",
|
|
396
|
+
...entry.rules,
|
|
397
|
+
"",
|
|
398
|
+
]),
|
|
399
|
+
"## Goal memory",
|
|
400
|
+
`Your memory for this goal lives in ${memoryPath}. It's loaded here at the start of every session, so it's how you remember across sessions and app restarts.`,
|
|
401
|
+
extras.memory ? `Current memory:\n${extras.memory}` : "Nothing written yet.",
|
|
402
|
+
"- Write to it (create it the first time) when you learn something that should hold " +
|
|
403
|
+
"for the rest of the goal: a decision, the user's preferences and corrections, what " +
|
|
404
|
+
"is done and what is left. Update it in place; keep it short and current, not a log.",
|
|
405
|
+
"- Give a worker the parts of it that bear on its task in its brief.",
|
|
406
|
+
"",
|
|
407
|
+
"## Before the goal starts",
|
|
408
|
+
"A goal begins with a short briefing, not with work. Until the user has chosen " +
|
|
409
|
+
`"${GOAL_START_OPTION}", do not edit files, run builds, install anything or start ` +
|
|
410
|
+
"tasks. Reading the context to understand it is fine.",
|
|
411
|
+
"- Your first reply opens with a written message, before any tool call: greet the " +
|
|
412
|
+
"user warmly in a line, then acknowledge the goal in your own words so they can " +
|
|
413
|
+
"see you understood it. Questions come after that, never first.",
|
|
414
|
+
"- Look at the attached context and say briefly what you found that matters.",
|
|
415
|
+
"- Once the goal starts the user may walk away until it's done, so ask now for " +
|
|
416
|
+
"everything the work will need from them: choices only they can make, preferences " +
|
|
417
|
+
"that change the result, access or credentials. Questions after the start should be " +
|
|
418
|
+
"rare.",
|
|
419
|
+
"- Ask only what changes the outcome and that you can't find out yourself. Decide " +
|
|
420
|
+
"small details yourself (formatting, file names, wording, sensible defaults) and say " +
|
|
421
|
+
"them in your plan instead of asking. If nothing is unclear, ask nothing but the " +
|
|
422
|
+
"start question.",
|
|
423
|
+
"- Then say in two or three lines how you would approach it, and ask with one " +
|
|
424
|
+
"`ask_user` call: your questions first (choices where you can), then last, whether " +
|
|
425
|
+
`to start (kind \`choice\`), with exactly these option labels and no custom values: ` +
|
|
426
|
+
`"${GOAL_START_OPTION}" (recommended) and "${GOAL_HOLD_OPTION}". One card, so the ` +
|
|
427
|
+
"user answers once. Only if an answer could change the plan a lot, ask those first " +
|
|
428
|
+
"and the start question after.",
|
|
429
|
+
"- If they want to discuss more, keep talking it through and ask the same question " +
|
|
430
|
+
"again when it is settled.",
|
|
431
|
+
"",
|
|
432
|
+
"## Once the goal has started",
|
|
433
|
+
"Start the work right after the user starts the goal. Do it yourself unless it " +
|
|
434
|
+
"splits into big independent parts; for those, start their workers together in one " +
|
|
435
|
+
"spawn_agent call (cwd: the attached folder each one works in) and handle each as " +
|
|
436
|
+
'soon as it reports (wait_agent, condition "any"). Keep the user informed about ' +
|
|
437
|
+
"what is happening and what needs them.",
|
|
438
|
+
"- When the work is done, write what was done and what's left to your memory.",
|
|
439
|
+
].join("\n");
|
|
440
|
+
}
|
|
441
|
+
/** What GG Goals is told when the user changes the goal's settings mid-session. */
|
|
442
|
+
export function goalSettingsNote(record) {
|
|
443
|
+
const context = record.context.length > 0
|
|
444
|
+
? record.context.map((item) => `- ${item.kind}: ${item.path}`).join("\n")
|
|
445
|
+
: "- (none)";
|
|
446
|
+
return [
|
|
447
|
+
"The user changed this goal's settings. These replace what your brief says:",
|
|
448
|
+
`Standing instructions: ${record.instructions ?? "(none)"}`,
|
|
449
|
+
"Attached context:",
|
|
450
|
+
context,
|
|
451
|
+
].join("\n");
|
|
452
|
+
}
|
|
453
|
+
/**
|
|
454
|
+
* Whether a started goal was cut off mid-work and should pick itself back up
|
|
455
|
+
* when its session opens again: it isn't paused, and either a worker was
|
|
456
|
+
* interrupted by the restart or GG Goals' own last turn never finished (its
|
|
457
|
+
* last message is a tool call, or a tool result or message with no reply
|
|
458
|
+
* after it).
|
|
459
|
+
*/
|
|
460
|
+
export function goalWasInterrupted(record, messages, workers) {
|
|
461
|
+
if (record.pausedAt)
|
|
462
|
+
return false;
|
|
463
|
+
// Still briefing: only its first reply can be cut off (the window closed
|
|
464
|
+
// or switched away mid-question), which leaves the user with no question
|
|
465
|
+
// to answer and the goal stuck. Started: anything mid-work counts too.
|
|
466
|
+
if (!record.startedAt && !record.kickoffAt)
|
|
467
|
+
return false;
|
|
468
|
+
if (workers.some((w) => w.recovered && w.state === "interrupted"))
|
|
469
|
+
return true;
|
|
470
|
+
const last = [...messages].reverse().find((m) => m.role !== "system");
|
|
471
|
+
if (!last)
|
|
472
|
+
return false;
|
|
473
|
+
// A tool result or a message with no reply after it: the turn never finished.
|
|
474
|
+
if (last.role === "tool" || last.role === "user")
|
|
475
|
+
return true;
|
|
476
|
+
if (last.role !== "assistant" || !Array.isArray(last.content))
|
|
477
|
+
return false;
|
|
478
|
+
return last.content.some((part) => typeof part === "object" && part !== null && part.type === "tool_call");
|
|
479
|
+
}
|
|
480
|
+
/**
|
|
481
|
+
* The note behind GG Goals' first run after the goal was interrupted (the
|
|
482
|
+
* app closed or crashed mid-work): workers don't survive a restart, so it
|
|
483
|
+
* checks what got done and carries on with the rest. Cut off mid-briefing,
|
|
484
|
+
* it asks its question again instead.
|
|
485
|
+
*/
|
|
486
|
+
export function goalResumePrompt(record) {
|
|
487
|
+
if (!record.startedAt) {
|
|
488
|
+
return [
|
|
489
|
+
`The briefing for the goal "${record.name}" was cut off before the user could ` +
|
|
490
|
+
"answer your question.",
|
|
491
|
+
"Ask it again now, in one ask_user card: what you still need (if anything) and " +
|
|
492
|
+
"whether to start. Don't repeat your greeting or what you already found; one short " +
|
|
493
|
+
"line before the card is enough.",
|
|
494
|
+
].join("\n");
|
|
495
|
+
}
|
|
496
|
+
return [
|
|
497
|
+
`The app was closed while you were working on the goal "${record.name}", so any ` +
|
|
498
|
+
"workers you had running were stopped.",
|
|
499
|
+
"Pick it up where it left off: check what is already done (your memory, the files " +
|
|
500
|
+
"themselves, and this conversation), then carry on with what's left, the same way " +
|
|
501
|
+
"as before. Don't redo finished work, and don't ask the user anything you could " +
|
|
502
|
+
"check yourself. Start with one short line saying you're picking it back up.",
|
|
503
|
+
].join("\n");
|
|
504
|
+
}
|
|
505
|
+
/** The hidden instruction behind the coordinator's first, automatic reply. */
|
|
506
|
+
export function goalKickoffPrompt(record) {
|
|
507
|
+
return [
|
|
508
|
+
`The user just created the goal "${record.name}" and opened it for the first time.`,
|
|
509
|
+
"Start the briefing described in your Goal instructions, in this order:",
|
|
510
|
+
"1. Before any tool call, write a short message: greet the user, then say the goal " +
|
|
511
|
+
"back in your own words.",
|
|
512
|
+
"2. Look through the attached context if there is any, with read, ls and find only " +
|
|
513
|
+
"(bash, edit and write stay locked until the user starts the goal), and say briefly " +
|
|
514
|
+
"what matters in it.",
|
|
515
|
+
"3. Say how you'd approach it, and in one ask_user card ask what you still need " +
|
|
516
|
+
"(if anything) and whether to start.",
|
|
517
|
+
"Do not start the work itself in this reply.",
|
|
518
|
+
].join("\n");
|
|
519
|
+
}
|
|
520
|
+
//# sourceMappingURL=goals-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"goals-store.js","sourceRoot":"","sources":["../../src/core/goals-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -10,6 +10,11 @@ export interface DiscoverProjectsOptions {
|
|
|
10
10
|
extraRoots?: readonly string[];
|
|
11
11
|
/** Project paths the user dismissed from the picker. */
|
|
12
12
|
hiddenPaths?: readonly string[];
|
|
13
|
+
/**
|
|
14
|
+
* Folders whose contents are never projects (Goal workspaces under
|
|
15
|
+
* `~/.gg/goals`). Anything at or below one of these is dropped.
|
|
16
|
+
*/
|
|
17
|
+
excludeRoots?: readonly string[];
|
|
13
18
|
}
|
|
14
19
|
export interface DiscoveredProject {
|
|
15
20
|
name: string;
|
|
@@ -41,6 +46,8 @@ export declare function discoverProjects(options?: DiscoverProjectsOptions): Pro
|
|
|
41
46
|
* every project/session vanished from the picker on Windows.
|
|
42
47
|
*/
|
|
43
48
|
export declare function isAbsoluteCwd(value: string): boolean;
|
|
49
|
+
/** Short "5m ago" style label for a millisecond timestamp; "—" for 0. */
|
|
50
|
+
export declare function formatRelativeTime(ms: number): string;
|
|
44
51
|
export interface RecentSession {
|
|
45
52
|
/** Session id. */
|
|
46
53
|
id: string;
|
|
@@ -50,8 +50,13 @@ export async function discoverProjects(options = {}) {
|
|
|
50
50
|
// Hiding is by resolved path so a row dismissed once stays gone regardless of
|
|
51
51
|
// which store re-surfaces it later.
|
|
52
52
|
const hidden = new Set((options.hiddenPaths ?? []).map((p) => path.resolve(p)));
|
|
53
|
+
const excluded = (options.excludeRoots ?? []).map((root) => path.resolve(root));
|
|
54
|
+
const isExcluded = (target) => excluded.some((root) => {
|
|
55
|
+
const relative = path.relative(root, path.resolve(target));
|
|
56
|
+
return relative === "" || (!relative.startsWith("..") && !path.isAbsolute(relative));
|
|
57
|
+
});
|
|
53
58
|
const merged = Array.from(byPath.values())
|
|
54
|
-
.filter((p) => !hidden.has(p.path))
|
|
59
|
+
.filter((p) => !hidden.has(p.path) && !isExcluded(p.path))
|
|
55
60
|
.map((p) => ({
|
|
56
61
|
...p,
|
|
57
62
|
lastActiveDisplay: formatRelativeTime(p.lastActiveMs),
|
|
@@ -663,7 +668,8 @@ function fallbackDashDecode(entry) {
|
|
|
663
668
|
}
|
|
664
669
|
return "/" + body.replace(/-/g, "/");
|
|
665
670
|
}
|
|
666
|
-
|
|
671
|
+
/** Short "5m ago" style label for a millisecond timestamp; "—" for 0. */
|
|
672
|
+
export function formatRelativeTime(ms) {
|
|
667
673
|
if (ms === 0)
|
|
668
674
|
return "—";
|
|
669
675
|
const diff = Date.now() - ms;
|