flowviant 0.116.0 → 0.124.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/README.md +66 -12
- package/bin/cli.mjs +50 -17
- package/bin/lib/admission.mjs +7 -14
- package/bin/lib/agentAsk.mjs +123 -0
- package/bin/lib/agentBrief.mjs +36 -0
- package/bin/lib/agentFootprint.mjs +76 -0
- package/bin/lib/agentInternalWait.mjs +83 -0
- package/bin/lib/agentPlan.mjs +134 -31
- package/bin/lib/agentPrerequisiteContract.mjs +43 -0
- package/bin/lib/agentPublish.mjs +12 -13
- package/bin/lib/agentTaskKinds.mjs +5 -8
- package/bin/lib/agySessions.mjs +3 -3
- package/bin/lib/artifactContracts.mjs +91 -20
- package/bin/lib/artifactIteration.mjs +182 -0
- package/bin/lib/artifactPreview.mjs +92 -18
- package/bin/lib/artifactRecovery.mjs +36 -0
- package/bin/lib/artifactReporter.mjs +194 -22
- package/bin/lib/artifacts.mjs +70 -63
- package/bin/lib/authproxy.mjs +2 -1
- package/bin/lib/childEnv.mjs +9 -4
- package/bin/lib/claude.mjs +10 -20
- package/bin/lib/claudeAuth.mjs +15 -5
- package/bin/lib/claudePersonal.mjs +71 -6
- package/bin/lib/claudePosture.mjs +227 -54
- package/bin/lib/claudeSessions.mjs +7 -7
- package/bin/lib/claudeStream.mjs +27 -13
- package/bin/lib/cliLimit.mjs +2 -2
- package/bin/lib/codexHome.mjs +17 -0
- package/bin/lib/codexPersonal.mjs +44 -3
- package/bin/lib/codexProjectRead.mjs +63 -0
- package/bin/lib/codexRelay.mjs +21 -5
- package/bin/lib/codexResearch.mjs +193 -0
- package/bin/lib/commandSpecs.mjs +6 -0
- package/bin/lib/config.mjs +37 -24
- package/bin/lib/conversationIds.mjs +51 -0
- package/bin/lib/credentials.mjs +2 -7
- package/bin/lib/daemonLocks.mjs +90 -0
- package/bin/lib/daemonState.mjs +5 -5
- package/bin/lib/deploy.mjs +6 -9
- package/bin/lib/deployCheckout.mjs +26 -22
- package/bin/lib/deployRunner.mjs +54 -18
- package/bin/lib/deployWire.mjs +32 -23
- package/bin/lib/desktopContract.mjs +6 -5
- package/bin/lib/discoveryContract.mjs +19 -0
- package/bin/lib/env.mjs +104 -35
- package/bin/lib/fleet.mjs +53 -13
- package/bin/lib/fleetPost.mjs +172 -25
- package/bin/lib/fleetReports.mjs +172 -171
- package/bin/lib/fleetStartup.mjs +6 -6
- package/bin/lib/fleetWire.mjs +42 -0
- package/bin/lib/git.mjs +111 -61
- package/bin/lib/gitMergeIdentity.mjs +2 -8
- package/bin/lib/grant.mjs +23 -7
- package/bin/lib/holder.mjs +108 -81
- package/bin/lib/hooks/mcpAllow.mjs +60 -0
- package/bin/lib/hooks/readGuard.mjs +54 -11
- package/bin/lib/imageArtifact.mjs +74 -0
- package/bin/lib/install.mjs +80 -33
- package/bin/lib/instance.mjs +36 -83
- package/bin/lib/intakeDraft.mjs +4 -1
- package/bin/lib/knowledgeFileSync.mjs +31 -0
- package/bin/lib/knowledgeFiles.mjs +28 -83
- package/bin/lib/landed.mjs +22 -29
- package/bin/lib/launchCommand.mjs +91 -0
- package/bin/lib/listeners.mjs +0 -5
- package/bin/lib/localSessions.mjs +52 -39
- package/bin/lib/login.mjs +22 -12
- package/bin/lib/loginPath.mjs +22 -17
- package/bin/lib/machineListing.mjs +6 -3
- package/bin/lib/maintenance.mjs +47 -0
- package/bin/lib/maintenanceInventory.mjs +39 -0
- package/bin/lib/mcp-cli.mjs +9 -10
- package/bin/lib/mcpInstall.mjs +163 -0
- package/bin/lib/mcpInventory.mjs +119 -0
- package/bin/lib/noWindowEnv.mjs +23 -3
- package/bin/lib/ownedDirs.mjs +99 -0
- package/bin/lib/pinnedCapture.mjs +383 -0
- package/bin/lib/planDependencies.mjs +70 -0
- package/bin/lib/postureTools.mjs +85 -0
- package/bin/lib/prWorkflow.mjs +94 -17
- package/bin/lib/preflight.mjs +8 -25
- package/bin/lib/preview.mjs +19 -25
- package/bin/lib/previewGatePolicy.mjs +34 -9
- package/bin/lib/previewRegistry.mjs +4 -11
- package/bin/lib/printable.mjs +70 -7
- package/bin/lib/procRegistry.mjs +33 -13
- package/bin/lib/procScan.mjs +2 -3
- package/bin/lib/procStat.mjs +76 -0
- package/bin/lib/processGroup.mjs +48 -0
- package/bin/lib/processes.mjs +10 -24
- package/bin/lib/projectToolRuntimes.mjs +328 -19
- package/bin/lib/projectTools.mjs +109 -34
- package/bin/lib/prompts.mjs +205 -60
- package/bin/lib/repoPath.mjs +19 -0
- package/bin/lib/repoState.mjs +17 -19
- package/bin/lib/repositoryBaseline.mjs +68 -0
- package/bin/lib/runTurn.mjs +286 -209
- package/bin/lib/runtimeAntigravity.mjs +16 -5
- package/bin/lib/runtimeCapabilities.mjs +215 -21
- package/bin/lib/runtimeClaude.mjs +78 -10
- package/bin/lib/runtimeCodex.mjs +217 -209
- package/bin/lib/runtimeDetection.mjs +8 -2
- package/bin/lib/runtimeEvents.mjs +28 -11
- package/bin/lib/runtimeLimits.mjs +5 -6
- package/bin/lib/runtimeOfferings.mjs +99 -14
- package/bin/lib/runtimes.mjs +10 -0
- package/bin/lib/shipSweep.mjs +5 -3
- package/bin/lib/shot.mjs +91 -17
- package/bin/lib/sleep.mjs +11 -0
- package/bin/lib/startProject.mjs +5 -5
- package/bin/lib/statePaths.mjs +40 -0
- package/bin/lib/tty.mjs +25 -15
- package/bin/lib/turnDiagnostics.mjs +93 -0
- package/bin/lib/turnLock.mjs +7 -14
- package/bin/lib/turnProfile.mjs +16 -4
- package/bin/lib/ui.mjs +0 -3
- package/bin/lib/uninstall.mjs +30 -26
- package/bin/lib/update.mjs +79 -27
- package/bin/lib/uplinkScrub.mjs +296 -31
- package/bin/lib/vault.mjs +78 -10
- package/bin/lib/vaultDiff.mjs +5 -2
- package/bin/lib/views.mjs +37 -12
- package/bin/lib/wikiRunner.mjs +40 -61
- package/bin/lib/work.mjs +44 -13
- package/bin/lib/workAdoptCarry.mjs +2 -4
- package/bin/lib/workAgentBrief.mjs +92 -0
- package/bin/lib/workAgentBriefRunner.mjs +59 -0
- package/bin/lib/workAgentCheck.mjs +104 -57
- package/bin/lib/workAgentMerges.mjs +62 -57
- package/bin/lib/workAgentPlans.mjs +82 -199
- package/bin/lib/workAgentPrecheck.mjs +76 -138
- package/bin/lib/workAgentPublish.mjs +6 -8
- package/bin/lib/workAgentTurnBegun.mjs +15 -11
- package/bin/lib/workAgentTurnExecution.mjs +131 -67
- package/bin/lib/workAgentTurnOutcome.mjs +11 -2
- package/bin/lib/workAgentTurnReports.mjs +9 -37
- package/bin/lib/workAgentTurns.mjs +64 -7
- package/bin/lib/workArtifactReconcile.mjs +79 -0
- package/bin/lib/workAttachments.mjs +66 -11
- package/bin/lib/workBrain.mjs +9 -4
- package/bin/lib/workCappedTurn.mjs +119 -0
- package/bin/lib/workChildren.mjs +5 -5
- package/bin/lib/workCommandAudit.mjs +18 -2
- package/bin/lib/workDiffs.mjs +52 -6
- package/bin/lib/workIntake.mjs +35 -60
- package/bin/lib/workPlaceReports.mjs +7 -4
- package/bin/lib/workPlaces.mjs +113 -31
- package/bin/lib/workPreviews.mjs +47 -112
- package/bin/lib/workProcesses.mjs +8 -35
- package/bin/lib/workPullRequests.mjs +37 -56
- package/bin/lib/workReportQueue.mjs +100 -148
- package/bin/lib/workRetire.mjs +2 -1
- package/bin/lib/workSessionRuntime.mjs +38 -37
- package/bin/lib/workSessionTurns.mjs +256 -199
- package/bin/lib/workShip.mjs +43 -23
- package/bin/lib/workToolLog.mjs +70 -3
- package/bin/lib/workTurnShape.mjs +165 -0
- package/npm-shrinkwrap.json +69 -0
- package/package.json +9 -4
- package/bin/lib/fleetJobs.mjs +0 -120
- package/bin/lib/patchRevert.mjs +0 -60
|
@@ -1,18 +1,21 @@
|
|
|
1
1
|
/** Agent plan claims, read-only planning turns, and proposal reports. */
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
2
|
+
import { discoveryGuide } from './discoveryContract.mjs';
|
|
3
|
+
import { compareRepositoryBaselines, measureRepositoryBaseline, repositoryBaselineOf } from './repositoryBaseline.mjs';
|
|
4
|
+
import { knowledgeDirFor } from './knowledgeFiles.mjs';
|
|
5
|
+
import { createToolLog } from './workToolLog.mjs';
|
|
6
|
+
import { FLEET_URL, DAEMON_INSTANCE } from './config.mjs';
|
|
7
|
+
import { measureAgentFootprint, liveAgentContext } from './agentFootprint.mjs';
|
|
6
8
|
import { parseProposal } from './agentPlan.mjs';
|
|
7
9
|
import { c, note } from './ui.mjs';
|
|
8
10
|
import { runTurn as defaultRunTurn } from './runTurn.mjs';
|
|
9
|
-
import { SYSTEM_PLAN, AGENT_PLAN_KICKOFF } from './prompts.mjs';
|
|
11
|
+
import { SYSTEM_PLAN, AGENT_PLAN_KICKOFF, withProjectContext, REPOSITORY_BASELINE_PARAGRAPH } from './prompts.mjs';
|
|
10
12
|
import { scrub as envScrub } from './uplinkScrub.mjs';
|
|
11
|
-
import {
|
|
13
|
+
import { canRun, runtimeById, runtimeLabel } from './runtimes.mjs';
|
|
12
14
|
import { detectRuntimes as defaultDetectRuntimes, pickRuntimeFor as defaultPickRuntimeFor } from './runtimeDetection.mjs';
|
|
13
15
|
import { brainFor } from './workBrain.mjs';
|
|
14
|
-
import {
|
|
16
|
+
import { runCappedTurn } from './workCappedTurn.mjs';
|
|
15
17
|
import { fleetEndpoint } from './fleetWire.mjs';
|
|
18
|
+
import { claimFleetLease, fleetHeaders } from './fleetPost.mjs';
|
|
16
19
|
import { REPO_PLACE } from './workPlaces.mjs';
|
|
17
20
|
import { addUsage } from './turnUsage.mjs';
|
|
18
21
|
|
|
@@ -43,8 +46,11 @@ export function createWorkAgentPlans({
|
|
|
43
46
|
// creates a worktree, a branch or a card.
|
|
44
47
|
//
|
|
45
48
|
// READ-ONLY IN THE CHECKOUT. `profile: 'consult'` selects `consultPermFor` (the
|
|
46
|
-
// fenced Read/Glob rules plus a few
|
|
47
|
-
//
|
|
49
|
+
// fenced Read/Glob rules plus a few shell readers: `ls`/`cat`/`head`, which
|
|
50
|
+
// the CLI path-validates itself, and the `git` readers, which it does not —
|
|
51
|
+
// their path fence is the read guard's refusal (hooks/readGuard.mjs), of
|
|
52
|
+
// `--no-index` and of any git argument aimed off the checkout — and no
|
|
53
|
+
// Write, no Edit, no mkdir, no rm) and NO MCP is passed at all, so this
|
|
48
54
|
// turn has no control plane to reach even if the repository it reads tries
|
|
49
55
|
// to steer it. The proposal comes back as the turn's final message rather
|
|
50
56
|
// than through a tool, which is exactly what lets that permission set be
|
|
@@ -68,8 +74,8 @@ export function createWorkAgentPlans({
|
|
|
68
74
|
* on a PRESS instead of an agent.
|
|
69
75
|
*
|
|
70
76
|
* This turn was already streaming and the daemon was already reading it:
|
|
71
|
-
*
|
|
72
|
-
* it behind `[plan]` on the operator's terminal. Every one of those lines
|
|
77
|
+
* runTurn's event reader humanizes every read, grep, command and thought
|
|
78
|
+
* and prints it behind `[plan]` on the operator's terminal. Every one of those lines
|
|
73
79
|
* was then thrown away, so a Deploy press said "planning" on the board and
|
|
74
80
|
* nothing else for as long as it took — which on an overnight queue is the
|
|
75
81
|
* whole night. Forwarding the CLI's own tail costs nothing that is not
|
|
@@ -90,11 +96,7 @@ export function createWorkAgentPlans({
|
|
|
90
96
|
try {
|
|
91
97
|
await fetch(AGENT_PLAN_ACTIVITY_URL, {
|
|
92
98
|
method: 'POST',
|
|
93
|
-
headers:
|
|
94
|
-
Authorization: `Bearer ${FLEET_TOKEN}`,
|
|
95
|
-
'User-Agent': USER_AGENT,
|
|
96
|
-
'Content-Type': 'application/json',
|
|
97
|
-
},
|
|
99
|
+
headers: fleetHeaders(),
|
|
98
100
|
signal: AbortSignal.timeout(15_000),
|
|
99
101
|
body: JSON.stringify({ planId, line: text }),
|
|
100
102
|
});
|
|
@@ -131,78 +133,8 @@ export function createWorkAgentPlans({
|
|
|
131
133
|
*/
|
|
132
134
|
const PLAN_TURN_TIMEOUT_MS = 15 * 60_000;
|
|
133
135
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
const res = await fetch(AGENT_PLAN_CLAIM_URL, {
|
|
137
|
-
method: 'POST',
|
|
138
|
-
headers: {
|
|
139
|
-
Authorization: `Bearer ${FLEET_TOKEN}`,
|
|
140
|
-
'User-Agent': USER_AGENT,
|
|
141
|
-
'Content-Type': 'application/json',
|
|
142
|
-
},
|
|
143
|
-
signal: AbortSignal.timeout(15_000),
|
|
144
|
-
body: JSON.stringify({ id, instance: DAEMON_INSTANCE }),
|
|
145
|
-
});
|
|
146
|
-
const j = await res.json().catch(() => null);
|
|
147
|
-
return j?.data?.claimed === true;
|
|
148
|
-
} catch {
|
|
149
|
-
return false; // the peer may hold it; doing nothing is the safe answer
|
|
150
|
-
}
|
|
151
|
-
};
|
|
152
|
-
|
|
153
|
-
/**
|
|
154
|
-
* What a live agent has already TOUCHED, measured here rather than sent.
|
|
155
|
-
*
|
|
156
|
-
* The server could have shipped a copy of this, and deliberately does not:
|
|
157
|
-
* these are directories on THIS machine, and a server-side copy would be a
|
|
158
|
-
* second, staler source of truth for a fact the daemon is standing on top of.
|
|
159
|
-
*
|
|
160
|
-
* Uncommitted work AND commits against base, because a planner asking "will
|
|
161
|
-
* these collide" cares about both — a file this agent has already rewritten
|
|
162
|
-
* and merged into its own branch collides exactly as hard as one it is
|
|
163
|
-
* editing now.
|
|
164
|
-
*/
|
|
165
|
-
const agentChangedFiles = (placeId) => {
|
|
166
|
-
if (!placeId || !isSafePathSegment(placeId)) return [];
|
|
167
|
-
const wt = join(baseDir, 'sessions', placeId);
|
|
168
|
-
if (!existsSync(wt)) return [];
|
|
169
|
-
const out = new Set();
|
|
170
|
-
for (const args of [
|
|
171
|
-
['diff', '--name-only', 'HEAD'],
|
|
172
|
-
['diff', '--name-only', `${baseRef()}...HEAD`],
|
|
173
|
-
['ls-files', '--others', '--exclude-standard'],
|
|
174
|
-
]) {
|
|
175
|
-
/**
|
|
176
|
-
* `git()` THROWS; it does not return a non-string.
|
|
177
|
-
*
|
|
178
|
-
* The guard below was `if (typeof r !== 'string') continue`, which is a
|
|
179
|
-
* value `execFileSync` can never produce — so every one of these calls
|
|
180
|
-
* was effectively unguarded. And this runs over the LIVE AGENTS a plan
|
|
181
|
-
* job carries, which include agents still in `planning`: they have no
|
|
182
|
-
* worktree yet, so `git diff` in a directory that does not exist exits
|
|
183
|
-
* non-zero and throws straight out of `runAgentPlan` — after the press
|
|
184
|
-
* has been claimed and before the try/finally below. The press then sat
|
|
185
|
-
* `planning` until its expiry, having spent nothing and explained
|
|
186
|
-
* nothing, on what would be an ordinary second Deploy.
|
|
187
|
-
*
|
|
188
|
-
* An agent whose files cannot be read contributes none. That is the
|
|
189
|
-
* honest answer and it is what the planner should be told.
|
|
190
|
-
*/
|
|
191
|
-
let r;
|
|
192
|
-
try {
|
|
193
|
-
r = git(args, wt);
|
|
194
|
-
} catch {
|
|
195
|
-
continue;
|
|
196
|
-
}
|
|
197
|
-
if (typeof r !== 'string') continue;
|
|
198
|
-
for (const line of r.split('\n')) {
|
|
199
|
-
const f = line.trim();
|
|
200
|
-
if (f) out.add(f);
|
|
201
|
-
if (out.size >= 60) return [...out];
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
return [...out];
|
|
205
|
-
};
|
|
136
|
+
// The peer may hold it; doing nothing is the safe answer.
|
|
137
|
+
const claimAgentPlan = (id) => claimFleetLease(AGENT_PLAN_CLAIM_URL, { id });
|
|
206
138
|
|
|
207
139
|
/** See `runAgentTurn` — the admission reservation, released the moment the
|
|
208
140
|
* planner's CLI exists. */
|
|
@@ -238,16 +170,17 @@ export function createWorkAgentPlans({
|
|
|
238
170
|
: job.model != null || job.effort != null ? 'claude' : null;
|
|
239
171
|
if (pickedRuntime) {
|
|
240
172
|
rt = pickedRuntime;
|
|
241
|
-
|
|
173
|
+
const runtime = runtimeById(rt);
|
|
174
|
+
if (!runtime) {
|
|
242
175
|
await postAgentPlan({ id, error: `${rt} is not a CLI this machine knows` });
|
|
243
176
|
return;
|
|
244
177
|
}
|
|
245
|
-
const label =
|
|
178
|
+
const label = runtime.label;
|
|
246
179
|
if (!detectRuntimes().find((r) => r.id === rt)?.installed) {
|
|
247
180
|
await postAgentPlan({ id, error: `${label} is not installed on this machine` });
|
|
248
181
|
return;
|
|
249
182
|
}
|
|
250
|
-
if (!canRun(
|
|
183
|
+
if (!canRun(runtime, 'consult')) {
|
|
251
184
|
await postAgentPlan({ id, error: `${label} can't run a read-only turn on this machine` });
|
|
252
185
|
return;
|
|
253
186
|
}
|
|
@@ -288,39 +221,8 @@ export function createWorkAgentPlans({
|
|
|
288
221
|
lastPlanBeat = Date.now();
|
|
289
222
|
void postAgentPlanActivity(id, line);
|
|
290
223
|
};
|
|
291
|
-
const liveAgents = (Array.isArray(job.liveAgents) ? job.liveAgents : []).map((a) => ({
|
|
292
|
-
id: String(a?.id ?? ''),
|
|
293
|
-
name: String(a?.name ?? ''),
|
|
294
|
-
status: String(a?.status ?? ''),
|
|
295
|
-
// Belt: `agentChangedFiles` is defensive internally, but it resolves a
|
|
296
|
-
// place first and this whole block sits outside the try below.
|
|
297
|
-
changedFiles: (() => {
|
|
298
|
-
try {
|
|
299
|
-
return agentChangedFiles(a?.placeId);
|
|
300
|
-
} catch {
|
|
301
|
-
return [];
|
|
302
|
-
}
|
|
303
|
-
})(),
|
|
304
|
-
}));
|
|
305
224
|
|
|
306
225
|
let out = '';
|
|
307
|
-
/** REMOVED IN A `finally`. `workChildren` is what `workBusy()` counts, and
|
|
308
|
-
* a leaked entry keeps the daemon permanently "busy" — which blocks every
|
|
309
|
-
* auto-update from that moment on, silently, until a restart. */
|
|
310
|
-
let planChild = null;
|
|
311
|
-
let planTimer = null;
|
|
312
|
-
/**
|
|
313
|
-
* THE PLANNER'S OWN CONVERSATION, so its transcript can be removed.
|
|
314
|
-
*
|
|
315
|
-
* `claude -p` in the CHECKOUT writes `~/.claude/projects/<checkout>/<id>.jsonl`,
|
|
316
|
-
* and the checkout is exactly where the operator runs their own terminal
|
|
317
|
-
* Claude. Left behind, every Deploy press became the newest ENDED session
|
|
318
|
-
* there: it displaced the operator's real conversation from the `+` adopt
|
|
319
|
-
* menu (one row per directory), offered to fork a read-only planner into a
|
|
320
|
-
* build tab, and was what their own `claude --continue` resumed. The
|
|
321
|
-
* pre-review and the skills probe delete theirs for the same reason.
|
|
322
|
-
*/
|
|
323
|
-
let planSession = null;
|
|
324
226
|
/** The cap fired: the CLI was still running when this machine stopped it. */
|
|
325
227
|
let wedged = false;
|
|
326
228
|
/**
|
|
@@ -331,7 +233,8 @@ export function createWorkAgentPlans({
|
|
|
331
233
|
* a report the retry posts again adds nothing. Null until the CLI reports.
|
|
332
234
|
*/
|
|
333
235
|
let usage = null;
|
|
334
|
-
const
|
|
236
|
+
const discovery = createToolLog(repoRoot);
|
|
237
|
+
const spent = () => ({ ...(usage ? { usage } : {}), tools: discovery.toolLog });
|
|
335
238
|
// Said BEFORE the lock is asked for, because a reader only waits when a
|
|
336
239
|
// writer holds the checkout or is queued ahead of it — which is exactly
|
|
337
240
|
// this condition, read one line before we join the queue.
|
|
@@ -341,92 +244,72 @@ export function createWorkAgentPlans({
|
|
|
341
244
|
try {
|
|
342
245
|
await inPlace(REPO_PLACE, false, async () => {
|
|
343
246
|
/**
|
|
344
|
-
*
|
|
345
|
-
*
|
|
346
|
-
*
|
|
347
|
-
*
|
|
348
|
-
*
|
|
247
|
+
* ONE CAPPED CONSULT TURN (workCappedTurn.mjs): the child registered
|
|
248
|
+
* in `workChildren` and removed on every exit, the reservation
|
|
249
|
+
* released at the spawn, the cap ARMED AT THE SPAWN — time spent
|
|
250
|
+
* waiting for the checkout is not a wedged CLI, and a press that never
|
|
251
|
+
* got to run is what the server's own clock is for — and the
|
|
252
|
+
* planner's transcript removed from the CHECKOUT, where it would
|
|
253
|
+
* otherwise become the newest ENDED session: displacing the
|
|
254
|
+
* operator's real conversation from the `+` adopt menu, offering to
|
|
255
|
+
* fork a read-only planner into a build tab, and being what their own
|
|
256
|
+
* `claude --continue` resumed.
|
|
349
257
|
*/
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
});
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
258
|
+
const liveAgents = (Array.isArray(job.liveAgents) ? job.liveAgents : []).map(a =>
|
|
259
|
+
liveAgentContext(a, measureAgentFootprint({ placeId: a?.placeId, baseDir, baseRef })));
|
|
260
|
+
const knowledgeDir = knowledgeDirFor(repoRoot);
|
|
261
|
+
discovery.toolLog.baseline = measureRepositoryBaseline({ cwd: repoRoot, baseRef, knowledgeDir });
|
|
262
|
+
discovery.toolLog.captureBaseline = repositoryBaselineOf(job.captureBaseline);
|
|
263
|
+
discovery.toolLog.reconciliation = compareRepositoryBaselines(discovery.toolLog.baseline, discovery.toolLog.captureBaseline);
|
|
264
|
+
({ out, wedged } = await runCappedTurn({
|
|
265
|
+
runTurn,
|
|
266
|
+
capMs: PLAN_TURN_TIMEOUT_MS,
|
|
267
|
+
workChildren,
|
|
268
|
+
releaseSlot,
|
|
269
|
+
transcriptCwd: repoRoot,
|
|
270
|
+
turnArgs: {
|
|
271
|
+
diagnosticPlanId: id,
|
|
272
|
+
prompt: AGENT_PLAN_KICKOFF({
|
|
273
|
+
tasks,
|
|
274
|
+
liveAgents,
|
|
275
|
+
agentCap: Number.isInteger(job.agentCap) && job.agentCap > 0 ? job.agentCap : 3,
|
|
276
|
+
}),
|
|
277
|
+
system: withProjectContext(SYSTEM_PLAN, { knowledgeDir }) + '\n\n' + discoveryGuide(rt, 'planning') + '\n\n' + REPOSITORY_BASELINE_PARAGRAPH(discovery.toolLog.baseline, discovery.toolLog.captureBaseline, discovery.toolLog.reconciliation),
|
|
278
|
+
knowledgeDir,
|
|
279
|
+
// READ-ONLY, and no MCP: `mcpArgs` is omitted entirely rather than
|
|
280
|
+
// passed empty, so there is no control plane on this turn at all.
|
|
281
|
+
profile: 'consult',
|
|
282
|
+
cwd: repoRoot,
|
|
283
|
+
runtime: rt,
|
|
284
|
+
...brainFor(job, rt),
|
|
285
|
+
answerFromResult: true,
|
|
286
|
+
label: c.cyan('[plan]'),
|
|
287
|
+
// The CLI's own tail, forwarded to the press. Same rule as an agent
|
|
288
|
+
// turn's: throttled, overwritten rather than appended, and never
|
|
289
|
+
// awaited by the turn.
|
|
290
|
+
onActivity: (a) => {
|
|
291
|
+
if (a?.label) narrate(String(a.label));
|
|
292
|
+
},
|
|
293
|
+
onToolEvent: discovery.pushToolEvent,
|
|
294
|
+
// Tagged with the CLI that counted it; one turn, so the sum is the
|
|
295
|
+
// one report (turnUsage.mjs keeps it null-safe).
|
|
296
|
+
onUsage: (u) => {
|
|
297
|
+
usage = addUsage(usage, { ...u, runtime: rt });
|
|
298
|
+
},
|
|
384
299
|
},
|
|
385
|
-
onSpawn: (
|
|
386
|
-
|
|
387
|
-
// No id: a Deploy press is not a task, and the snapshot's per-task
|
|
388
|
-
// rows must not invent one. It still COUNTS against the machine's
|
|
389
|
-
// ceiling — see liveTurnCount.
|
|
390
|
-
workChildren.set(ch, null);
|
|
391
|
-
releaseSlot();
|
|
392
|
-
say(`${RUNTIMES[rt]?.label ?? rt} started on this machine`);
|
|
393
|
-
/**
|
|
394
|
-
* ARMED AT THE SPAWN, not at the claim: time spent waiting for the
|
|
395
|
-
* checkout is not a wedged CLI, and a press that never got to run
|
|
396
|
-
* is what the server's own clock is for.
|
|
397
|
-
*
|
|
398
|
-
* The CHILD, never its group — the rule teardown keeps. A
|
|
399
|
-
* read-only planning turn starts no dev server, so there is
|
|
400
|
-
* nothing behind it worth signalling and everything to lose by
|
|
401
|
-
* signalling somebody else's.
|
|
402
|
-
*/
|
|
403
|
-
planTimer = setTimeout(() => {
|
|
404
|
-
wedged = true;
|
|
405
|
-
try {
|
|
406
|
-
ch.kill('SIGKILL');
|
|
407
|
-
} catch {
|
|
408
|
-
/* already gone */
|
|
409
|
-
}
|
|
410
|
-
stopWaiting('');
|
|
411
|
-
}, PLAN_TURN_TIMEOUT_MS);
|
|
412
|
-
planTimer.unref?.();
|
|
300
|
+
onSpawn: () => {
|
|
301
|
+
say(`${runtimeLabel(rt)} started on this machine`);
|
|
413
302
|
},
|
|
414
|
-
});
|
|
415
|
-
out = await Promise.race([turn, capped]);
|
|
303
|
+
}));
|
|
416
304
|
});
|
|
417
305
|
} catch (e) {
|
|
306
|
+
discovery.toolLog.state = 'partial';
|
|
418
307
|
await postAgentPlan({ id, error: envScrub(String(e?.message || e)).slice(0, 500), ...spent() });
|
|
419
308
|
return;
|
|
420
|
-
} finally {
|
|
421
|
-
if (planTimer) clearTimeout(planTimer);
|
|
422
|
-
if (planChild) workChildren.delete(planChild);
|
|
423
|
-
// On EVERY exit, after the kill and on a delay — the transcript is the
|
|
424
|
-
// child's file, and removing it while the child is still dying races a
|
|
425
|
-
// recreate. The pre-review's own shape.
|
|
426
|
-
if (planSession) setTimeout(() => removeProbeTranscript(repoRoot, planSession), 750).unref?.();
|
|
427
309
|
}
|
|
428
310
|
|
|
429
311
|
if (wedged) {
|
|
312
|
+
discovery.toolLog.state = 'partial';
|
|
430
313
|
// NEVER LEAVE A CLAIMED PRESS UNREPORTED — the belt the merge lane wears
|
|
431
314
|
// beneath this one. The machine's own words, because the machine is the
|
|
432
315
|
// only side that knows: the server would have expired this press in
|