flowviant 0.125.4 → 0.125.6
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/bin/lib/agentInternalWait.mjs +1 -5
- package/bin/lib/agentPlan.mjs +13 -163
- package/bin/lib/agentRepositories.mjs +3 -1
- package/bin/lib/claudeInput.mjs +28 -9
- package/bin/lib/claudeMachineMcp.mjs +85 -0
- package/bin/lib/claudePersonal.mjs +6 -6
- package/bin/lib/claudePosture.mjs +6 -15
- package/bin/lib/claudeSettingsArg.mjs +67 -0
- package/bin/lib/claudeSubagentProgress.mjs +12 -28
- package/bin/lib/claudeSubagents.mjs +4 -2
- package/bin/lib/codexFencedMount.mjs +275 -0
- package/bin/lib/codexPersonal.mjs +1 -265
- package/bin/lib/codexProjectRead.mjs +5 -4
- package/bin/lib/codexResearch.mjs +9 -3
- package/bin/lib/codexSubagentEvents.mjs +11 -7
- package/bin/lib/codexSubagents.mjs +55 -52
- package/bin/lib/codexToml.mjs +328 -0
- package/bin/lib/conversationIds.mjs +10 -0
- package/bin/lib/fleet.mjs +2 -2
- package/bin/lib/git.mjs +32 -0
- package/bin/lib/heldTool.mjs +49 -0
- package/bin/lib/hooks/mcpAllow.mjs +11 -5
- package/bin/lib/hooks/orchestrator.mjs +20 -10
- package/bin/lib/intakeDraft.mjs +1 -1
- package/bin/lib/jsonlTail.mjs +52 -0
- package/bin/lib/mcpInventory.mjs +9 -5
- package/bin/lib/mcpReasons.mjs +20 -0
- package/bin/lib/mcpReportBounds.mjs +10 -0
- package/bin/lib/mcpStdio.mjs +35 -0
- package/bin/lib/mergeBatch.mjs +6 -24
- package/bin/lib/mergeOrder.mjs +18 -0
- package/bin/lib/modelJson.mjs +173 -0
- package/bin/lib/nativeAgentTools.mjs +72 -0
- package/bin/lib/orchestrator.mjs +212 -133
- package/bin/lib/orchestratorClient.mjs +17 -21
- package/bin/lib/orchestratorHooks.mjs +21 -4
- package/bin/lib/orchestratorNative.mjs +58 -0
- package/bin/lib/orchestratorPrompt.mjs +7 -4
- package/bin/lib/orchestratorProtocol.mjs +25 -3
- package/bin/lib/postureTools.mjs +58 -13
- package/bin/lib/prWorkflow.mjs +13 -2
- package/bin/lib/projectRepositories.mjs +2 -2
- package/bin/lib/projectToolRuntimes.mjs +92 -245
- package/bin/lib/projectTools.mjs +59 -19
- package/bin/lib/repositoryDelivery.mjs +6 -4
- package/bin/lib/repositoryMerge.mjs +8 -12
- package/bin/lib/repositoryPlaces.mjs +2 -2
- package/bin/lib/repositoryPrompts.mjs +42 -0
- package/bin/lib/repositoryTool.mjs +9 -32
- package/bin/lib/researchUse.mjs +39 -0
- package/bin/lib/resumeMarker.mjs +43 -0
- package/bin/lib/runTurn.mjs +38 -52
- package/bin/lib/runtimeAntigravity.mjs +1 -1
- package/bin/lib/runtimeCapabilities.mjs +6 -16
- package/bin/lib/runtimeClaude.mjs +62 -19
- package/bin/lib/runtimeCodex.mjs +9 -6
- package/bin/lib/runtimeEvents.mjs +13 -2
- package/bin/lib/runtimeOfferings.mjs +9 -4
- package/bin/lib/runtimes.mjs +3 -1
- package/bin/lib/subagentBounds.mjs +20 -0
- package/bin/lib/trace.mjs +30 -5
- package/bin/lib/turnDeclaration.mjs +1 -1
- package/bin/lib/turnDiagnostics.mjs +9 -12
- package/bin/lib/turnReceipts.mjs +64 -0
- package/bin/lib/turnSubagents.mjs +91 -0
- package/bin/lib/turnUsage.mjs +9 -6
- package/bin/lib/versionOrder.mjs +11 -1
- package/bin/lib/work.mjs +6 -6
- package/bin/lib/workAgentMerges.mjs +5 -7
- package/bin/lib/workAgentPrecheck.mjs +5 -5
- package/bin/lib/workAgentRecovery.mjs +2 -1
- package/bin/lib/workAgentResume.mjs +121 -0
- package/bin/lib/workAgentTools.mjs +111 -0
- package/bin/lib/workAgentTurnExecution.mjs +62 -246
- package/bin/lib/workAgentTurnOutcome.mjs +30 -3
- package/bin/lib/workAgentTurnShape.mjs +90 -0
- package/bin/lib/workAgentTurns.mjs +24 -10
- package/bin/lib/workArtifactReconcile.mjs +3 -2
- package/bin/lib/workMergeBatch.mjs +7 -5
- package/bin/lib/workPlaceReports.mjs +3 -3
- package/bin/lib/workPlaces.mjs +14 -0
- package/bin/lib/workSessionRuntime.mjs +2 -5
- package/bin/lib/workSessionTurns.mjs +12 -63
- package/bin/lib/workShip.mjs +8 -18
- package/bin/lib/workTurnShape.mjs +6 -4
- package/package.json +1 -1
|
@@ -6,8 +6,9 @@ import { finishRepositoryDelivery, allResolutionsCommitted } from './repositoryD
|
|
|
6
6
|
* decides WHICH turns run and makes sure each one is settled exactly once; this
|
|
7
7
|
* runs one, inside the place lock the lane already holds — the begun-guard
|
|
8
8
|
* (workAgentTurnBegun.mjs), the worktree and its branch facts, the kind's
|
|
9
|
-
* refusals, the prompt, the CLI and its resume
|
|
10
|
-
*
|
|
9
|
+
* refusals, the prompt, the CLI and its resume (whose markers are
|
|
10
|
+
* workAgentResume.mjs's since 2026-10-18), the trace and pulse relays, and the
|
|
11
|
+
* re-measurement after it exits — then hands the
|
|
11
12
|
* measurements to the one settlement decision (workAgentTurnOutcome.mjs) and
|
|
12
13
|
* posts the body that decision chose.
|
|
13
14
|
*
|
|
@@ -17,38 +18,39 @@ import { finishRepositoryDelivery, allResolutionsCommitted } from './repositoryD
|
|
|
17
18
|
* answered. `measured` is the same bargain the other way: the branch facts and
|
|
18
19
|
* the spend, written here as they are learned, read by that catch.
|
|
19
20
|
*/
|
|
20
|
-
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
21
21
|
import { PROJECT_ID } from './config.mjs';
|
|
22
22
|
import { setRuntimeLimit } from './daemonState.mjs';
|
|
23
23
|
import { gitOrNull } from './git.mjs';
|
|
24
|
+
import { turnCommits } from './turnReceipts.mjs';
|
|
25
|
+
import { addUsage } from './turnUsage.mjs';
|
|
24
26
|
import { stashCard } from './agentCards.mjs';
|
|
25
27
|
import { c } from './ui.mjs';
|
|
26
28
|
import { runTurn } from './runTurn.mjs';
|
|
27
29
|
import { killGroup } from './processGroup.mjs';
|
|
28
|
-
import {
|
|
30
|
+
import { AGENT_TASK_SPEC } from './prompts.mjs';
|
|
31
|
+
import { agentKickoff, agentFollowupKickoff, agentTurnSystem } from './workAgentTurnShape.mjs';
|
|
29
32
|
import { AGENT_TASK_KINDS, agentTaskKindOf, unknownAgentTaskKind } from './agentTaskKinds.mjs';
|
|
30
33
|
import { knowledgeDirFor } from './knowledgeFiles.mjs';
|
|
31
34
|
import { scrub as envScrub } from './uplinkScrub.mjs';
|
|
32
35
|
import { canRun, runtimeById } from './runtimes.mjs';
|
|
33
36
|
import { toolEventOf, CLAUDE_TOOL_PROSE_KINDS, scrubNativeText } from './runtimeEvents.mjs';
|
|
34
37
|
import { makeTraceRelay } from './trace.mjs';
|
|
35
|
-
import {
|
|
36
|
-
import { TURN_PROFILES } from './turnProfile.mjs';
|
|
37
|
-
import { postureToolsNote } from './postureTools.mjs';
|
|
38
|
+
import { prepareCardTools } from './workAgentTools.mjs';
|
|
38
39
|
import { runTurnResumingOnce } from './resumeLost.mjs';
|
|
39
|
-
import {
|
|
40
|
-
import { CODEX_THREAD_RE } from './workSessionRuntime.mjs';
|
|
40
|
+
import { runAgentWithInternalWaits } from './agentInternalWait.mjs';
|
|
41
41
|
import { brainFor } from './workBrain.mjs';
|
|
42
42
|
import { refreshRestartBase } from './workAgentRecovery.mjs';
|
|
43
43
|
import { begunTurnRefusal } from './workAgentTurnBegun.mjs';
|
|
44
|
-
import { agentTurnSettlement } from './workAgentTurnOutcome.mjs';
|
|
44
|
+
import { agentTurnSettlement, interruptedTurnSettlement } from './workAgentTurnOutcome.mjs';
|
|
45
45
|
import { ensureArtifactDir, ensureFenceScratch } from './artifacts.mjs';
|
|
46
46
|
import { agentBrowserHome } from './noWindowEnv.mjs';
|
|
47
|
-
import {
|
|
47
|
+
import { agentTurnResume } from './workAgentResume.mjs';
|
|
48
48
|
import { createDeliveryReporter } from './workDelivery.mjs';
|
|
49
49
|
import { createLiveMessages } from './workLiveMessages.mjs';
|
|
50
50
|
import { createOrchestrator } from './orchestrator.mjs';
|
|
51
|
-
import {
|
|
51
|
+
import { claudeOrchestrationSafe } from './orchestratorHooks.mjs';
|
|
52
|
+
import { codexSubagentsSafe } from './runtimeCodex.mjs';
|
|
53
|
+
import { measuredRuntimeVersion } from './runtimeDetection.mjs';
|
|
52
54
|
|
|
53
55
|
export function createAgentTurnExecution({
|
|
54
56
|
admit,
|
|
@@ -82,38 +84,6 @@ export function createAgentTurnExecution({
|
|
|
82
84
|
|
|
83
85
|
const lastAgentBeat = new Map(); // agentId -> last activity POST, ms
|
|
84
86
|
|
|
85
|
-
/** Every sha that landed on this branch between two points. Measured, never
|
|
86
|
-
* asserted — this is the whole reason an agent is not asked for its own. */
|
|
87
|
-
const commitsBetween = (wt, from) => {
|
|
88
|
-
if (!from) return [];
|
|
89
|
-
/**
|
|
90
|
-
* `--not <base>` and `--no-merges`, because `from..HEAD` alone reports
|
|
91
|
-
* BASE'S commits as this turn's receipts the moment anything folds base in
|
|
92
|
-
* — which the stale path does on purpose before a merge. A receipt naming
|
|
93
|
-
* somebody else's commit is worse than a missing one.
|
|
94
|
-
*/
|
|
95
|
-
/**
|
|
96
|
-
* IT CANNOT THROW, and that guard is the whole point of it being here.
|
|
97
|
-
*
|
|
98
|
-
* `git()` throws on a non-zero exit, and `baseRef()` is built from the
|
|
99
|
-
* project's Base branch setting — free text an owner types, never verified
|
|
100
|
-
* against the remote. Point it at a branch with no tracking ref (`develop`
|
|
101
|
-
* on a repo whose remote branch is `dev`) and this exits "fatal: ambiguous
|
|
102
|
-
* argument". The throw escaped `runAgentTurn` AFTER the CLI had already run
|
|
103
|
-
* the card, so `postAgentTurn` was never reached, the server never settled
|
|
104
|
-
* the turn, and the next poll handed back the identical turn — the same
|
|
105
|
-
* card re-run every poll for six hours, on the operator's shared account,
|
|
106
|
-
* piling commits onto the review branch, silently.
|
|
107
|
-
*
|
|
108
|
-
* An unreadable range means we cannot MEASURE the receipts, which is a
|
|
109
|
-
* smaller failure than not settling: the turn still reports, and ship-time
|
|
110
|
-
* reconciliation books whatever no card claimed. Missing beats fabricated
|
|
111
|
-
* and both beat a stall.
|
|
112
|
-
*/
|
|
113
|
-
const out = gitOrNull(['log', '--format=%H', '--no-merges', `${from}..HEAD`, '--not', baseRef()], wt);
|
|
114
|
-
return out?.split('\n').map((x) => x.trim()).filter(Boolean).slice(0, 50) ?? [];
|
|
115
|
-
};
|
|
116
|
-
|
|
117
87
|
/**
|
|
118
88
|
* The turn, run inside its place's writer lock (the lane takes it). `turnId`,
|
|
119
89
|
* `agentId` and `place` are the lane's own readings of the job, handed down
|
|
@@ -179,6 +149,7 @@ export function createAgentTurnExecution({
|
|
|
179
149
|
measured.branch = branch;
|
|
180
150
|
|
|
181
151
|
const rt = job.runtime || 'claude';
|
|
152
|
+
const liveInput = runtimeById(rt)?.liveInput === true;
|
|
182
153
|
/**
|
|
183
154
|
* WHAT THIS CARD HANDS BACK, AND THE POSTURE THAT FOLLOWS (0.97.0).
|
|
184
155
|
*
|
|
@@ -279,64 +250,9 @@ export function createAgentTurnExecution({
|
|
|
279
250
|
return;
|
|
280
251
|
}
|
|
281
252
|
|
|
282
|
-
//
|
|
283
|
-
//
|
|
284
|
-
|
|
285
|
-
// distinguishes the first turn from every later one across restarts.
|
|
286
|
-
//
|
|
287
|
-
// IT MEANS "CLAUDE HAS SPOKEN HERE", and since 2026-09-24 only a Claude
|
|
288
|
-
// turn writes it: an agent can be switched between CLIs, and a marker a
|
|
289
|
-
// codex turn left behind would send the first Claude turn to
|
|
290
|
-
// `--continue` a directory holding no Claude conversation at all.
|
|
291
|
-
const ranMarker = sessionMetaPath(wt, 'flowviant-agent-ran');
|
|
292
|
-
/**
|
|
293
|
-
* CODEX RESUMES BY ITS OWN THREAD ID, PINNED PER AGENT (2026-09-24).
|
|
294
|
-
*
|
|
295
|
-
* Resume was Claude-only, because codex's `resume --last` is
|
|
296
|
-
* MACHINE-GLOBAL and would cross-resume whichever conversation spoke
|
|
297
|
-
* last anywhere on the box. The cost was that every codex card started
|
|
298
|
-
* a stranger: no memory of the card before it, and — worse — an answer
|
|
299
|
-
* to the agent's own question arrived at a codex that never asked it.
|
|
300
|
-
* The Workbench solved the same problem in 0.69.0 by pinning the id
|
|
301
|
-
* `thread.started` announces; this is that, keyed by the AGENT rather
|
|
302
|
-
* than a tab. The id is shape-checked before it rides argv.
|
|
303
|
-
*/
|
|
304
|
-
const codexThreadMarker =
|
|
305
|
-
rt === 'codex' ? sessionMetaPath(wt, 'flowviant-agent-codex-thread', agentId) : null;
|
|
306
|
-
let codexResumeId = null;
|
|
307
|
-
if (codexThreadMarker && existsSync(codexThreadMarker)) {
|
|
308
|
-
try {
|
|
309
|
-
const v = readFileSync(codexThreadMarker, 'utf8').trim();
|
|
310
|
-
if (CODEX_THREAD_RE.test(v)) codexResumeId = v;
|
|
311
|
-
} catch {
|
|
312
|
-
/* unreadable marker — run fresh */
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
let seenThreadId = null;
|
|
316
|
-
const claudeMarker = rt === 'claude' ? sessionMetaPath(wt, RESUME_MARKERS.claude, agentId) : null;
|
|
317
|
-
let claudeResumeId = null;
|
|
318
|
-
try { const id = readFileSync(claudeMarker, 'utf8').trim(); if (CLAUDE_SESSION_RE.test(id)) claudeResumeId = id; } catch { /* unknown */ }
|
|
319
|
-
const resume =
|
|
320
|
-
rt === 'codex'
|
|
321
|
-
? Boolean(codexResumeId)
|
|
322
|
-
: rt === 'claude' && (Boolean(claudeResumeId) || Boolean(ranMarker && existsSync(ranMarker)));
|
|
323
|
-
/**
|
|
324
|
-
* …AND THE HOME THAT THREAD IS FILED UNDER, KEPT PER AGENT (2026-09-29).
|
|
325
|
-
*
|
|
326
|
-
* Codex's thread index records each rollout under the CODEX_HOME the turn
|
|
327
|
-
* ran with, as that path — not the personal store the link leads to — so
|
|
328
|
-
* the per-turn home in /tmp every turn used to get died with its turn and
|
|
329
|
-
* took the pinned thread with it: the next card's `resume` failed "no
|
|
330
|
-
* rollout found" and the agent went to Stuck. Beside the thread marker,
|
|
331
|
-
* in the worktree's private git dir: outside the working tree (never
|
|
332
|
-
* committed, never seen by `git add -A`), refreshed by every turn and
|
|
333
|
-
* removed only with the worktree, when the agent retires and its threads
|
|
334
|
-
* stop mattering. One agent runs one turn at a time (its place's writer
|
|
335
|
-
* lock), so no two turns refresh it at once. Null (no git dir to hold it)
|
|
336
|
-
* is the per-turn home again, and a lost resume then runs once fresh.
|
|
337
|
-
*/
|
|
338
|
-
const codexAgentHome =
|
|
339
|
-
rt === 'codex' ? sessionMetaPath(wt, 'flowviant-agent-codex-home', agentId) : null;
|
|
253
|
+
// Which conversation this turn resumes, and the kept Codex home it is
|
|
254
|
+
// filed under: the agent's resume across turns (workAgentResume.mjs).
|
|
255
|
+
const agentResume = agentTurnResume({ rt, wt, agentId, sessionMetaPath });
|
|
340
256
|
|
|
341
257
|
/**
|
|
342
258
|
* THE TURN'S FILES, ON DISK BEFORE THE PROMPT NAMES THEM (0.112.0) — the
|
|
@@ -370,11 +286,11 @@ export function createAgentTurnExecution({
|
|
|
370
286
|
* commits it made are on the branch, and a reviewer is entitled to read
|
|
371
287
|
* what was asked for either way.
|
|
372
288
|
*
|
|
373
|
-
* ONE SPEC BUILDER (`AGENT_TASK_SPEC`) shared with the kickoff
|
|
374
|
-
* what the reviewer reads is byte-identical
|
|
375
|
-
* card's discussion included (0.106.0): both
|
|
376
|
-
* `job.cardThread`, and the builder prints it inside
|
|
377
|
-
* card's files after it (0.112.0), fetched just above.
|
|
289
|
+
* ONE SPEC BUILDER (`AGENT_TASK_SPEC`) shared with the kickoff
|
|
290
|
+
* (workAgentTurnShape.mjs), so what the reviewer reads is byte-identical
|
|
291
|
+
* to what the agent read — the card's discussion included (0.106.0): both
|
|
292
|
+
* are handed the same `job.cardThread`, and the builder prints it inside
|
|
293
|
+
* the spec — and the card's files after it (0.112.0), fetched just above.
|
|
378
294
|
* Failure is swallowed inside `stashCard`: a note about a turn may never
|
|
379
295
|
* cost the turn.
|
|
380
296
|
*/
|
|
@@ -386,6 +302,12 @@ export function createAgentTurnExecution({
|
|
|
386
302
|
);
|
|
387
303
|
}
|
|
388
304
|
|
|
305
|
+
// ORCHESTRATED ONLY WHERE THE CLI CAN BE GATED (2026-10-17). v2 reaches every
|
|
306
|
+
// job at the floor; a fenced card or a CLI below its measured floor (unknown
|
|
307
|
+
// included) has no controller, so no marker: its notes fold on receipt.
|
|
308
|
+
const codexVersion = rt === 'codex' ? measuredRuntimeVersion('codex')?.version : undefined;
|
|
309
|
+
const orchestrating = job.orchestrationVersion === 2 && posture === 'build' &&
|
|
310
|
+
(rt === 'claude' ? claudeOrchestrationSafe(measuredRuntimeVersion('claude')?.version) : rt === 'codex' && codexSubagentsSafe(codexVersion));
|
|
389
311
|
/**
|
|
390
312
|
* THE WHOLE STREAM, not just its latest line — see trace.mjs.
|
|
391
313
|
*
|
|
@@ -398,12 +320,12 @@ export function createAgentTurnExecution({
|
|
|
398
320
|
agentId,
|
|
399
321
|
turnId,
|
|
400
322
|
post: postAgentTrace,
|
|
401
|
-
orchestrationVersion:
|
|
323
|
+
orchestrationVersion: orchestrating ? 2 : undefined,
|
|
402
324
|
scrub: envScrub,
|
|
403
325
|
});
|
|
404
326
|
// A live Send can choose its path as soon as the CLI starts, including a
|
|
405
327
|
// silent solo turn. Do not leave a two-second first-flush race at spawn.
|
|
406
|
-
if (
|
|
328
|
+
if (orchestrating) await trace.flush(1_000);
|
|
407
329
|
/**
|
|
408
330
|
* Prose the structured event will carry anyway, dropped so a read does
|
|
409
331
|
* not render twice — but ONLY on a runtime whose stream reaches
|
|
@@ -479,122 +401,42 @@ export function createAgentTurnExecution({
|
|
|
479
401
|
let projectTools = null;
|
|
480
402
|
let orchestrator = null;
|
|
481
403
|
try {
|
|
482
|
-
|
|
483
|
-
if (orchestrated && posture === 'build' && ['claude', 'codex'].includes(rt)) {
|
|
404
|
+
if (orchestrating) {
|
|
484
405
|
orchestrator = await createOrchestrator({ turnId, runtime: rt, trace, admit,
|
|
485
|
-
|
|
406
|
+
// Whether the words reached the CLI; Codex reads its inbox from hooks.
|
|
407
|
+
notify: message => liveInput ? run.live?.inject(message) === true : false,
|
|
486
408
|
taken: id => run.live?.onTaken(id) });
|
|
487
409
|
}
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
const
|
|
491
|
-
const source = sources.find((r) => r.wt === wt);
|
|
492
|
-
const snapshot = readBaseTools(source?.repoRoot ?? repoRoot, source?.baseRef ?? baseRef());
|
|
493
|
-
for (const other of sources.filter((r) => r !== source)) {
|
|
494
|
-
const base = readBaseTools(other.repoRoot, other.baseRef);
|
|
495
|
-
snapshot.instructions.push(...[...base.instructions, ...base.skillFiles].map((file) => ({ ...file, path: `repositories/${other.folder}/${file.path}` })));
|
|
496
|
-
}
|
|
497
|
-
projectTools = prepareAgentTools(snapshot, rt, process.env, wt, { codexHome: codexAgentHome, turnServers: {
|
|
498
|
-
...(repositoryRun?.tool ? { card_repositories: repositoryRun.tool.config } : {}),
|
|
499
|
-
...(orchestrator ? { card_orchestrator: orchestrator.config } : {}),
|
|
500
|
-
} });
|
|
501
|
-
const mountsProjectMcp = TURN_PROFILES[posture]?.projectMcp === true && (rt === 'claude' || rt === 'codex');
|
|
502
|
-
// A fenced posture on a runner that ignores its own file there (Codex's
|
|
503
|
-
// `--ignore-user-config`) takes the servers as `fencedMount` instead.
|
|
504
|
-
const fencedMount = mountsProjectMcp && posture !== 'build' ? projectTools.fencedMount : null;
|
|
505
|
-
// THE MACHINE'S OWN SERVERS ON CLAUDE (2026-10-11, the owner): the turn
|
|
506
|
-
// is not strict, and every `.mcp.json` name from the worktree up is
|
|
507
|
-
// disabled so only the base copy speaks for the project. Null (an
|
|
508
|
-
// unboundable walk) mounts strictly: the base copy alone.
|
|
509
|
-
const claudeDisabled = mountsProjectMcp && rt === 'claude' ? mcpJsonServerNamesAbove(wt) : null;
|
|
510
|
-
// What this card's posture withholds, said to the agent — with the
|
|
511
|
-
// servers it did and did not mount, and why — so it relays the fence
|
|
512
|
-
// instead of inventing a setting (postureTools.mjs). Empty for a code card.
|
|
513
|
-
const toolsNote = postureToolsNote({
|
|
514
|
-
posture,
|
|
515
|
-
runtime: rt,
|
|
516
|
-
mounted: !mountsProjectMcp ? [] : fencedMount ? fencedMount.mounted : projectTools.mcpServers,
|
|
517
|
-
machine: mountsProjectMcp && (rt === 'codex' || claudeDisabled != null),
|
|
518
|
-
unmounted: [...projectTools.mcpUnmounted.map((u) => ({ ...u, source: 'project' })), ...(fencedMount ? fencedMount.unmounted : [])],
|
|
519
|
-
});
|
|
410
|
+
const tools = prepareCardTools({ rt, posture, wt, repoRoot, baseRef, sources: repositoryRun?.sources ?? [], repositoryTool: repositoryRun?.tool ?? null, orchestratorConfig: orchestrator?.config ?? null, orchestrated: Boolean(orchestrator), codexHome: agentResume.codexHome });
|
|
411
|
+
projectTools = tools.projectTools;
|
|
412
|
+
const { toolsNote } = tools;
|
|
520
413
|
const agentTurnArgs = {
|
|
521
|
-
...((
|
|
414
|
+
...((liveInput || orchestrator) ? { liveMessages: (run.live = makeLiveMessages({
|
|
522
415
|
scope: 'agent', turnId, cwd: wt, sessionMetaPath, interrupted: () => run.interrupted,
|
|
523
416
|
observe: message => orchestrator?.note(message),
|
|
524
417
|
...(rt === 'codex' ? { nativeIds: () => null } : {}),
|
|
525
418
|
format: async (followup) => {
|
|
526
419
|
orchestrator?.note(followup);
|
|
527
420
|
const attached = followup.attachments?.length ? await fetchAgentFiles(wt, followup.attachments.map((file) => ({ ...file, from: 'message' }))) : { message: [], card: [] };
|
|
528
|
-
return
|
|
529
|
-
askedByName: followup.askedByName, task: job.task, cardThread: job.cardThread,
|
|
530
|
-
investigationContext: job.investigationContext, researchContext: job.researchContext,
|
|
531
|
-
messageFiles: attached.message, cardFiles: files.card, position: job.position ?? 1, total: job.total ?? 1 });
|
|
421
|
+
return agentFollowupKickoff(job, followup, { messageFiles: attached.message, cardFiles: files.card, orchestrated: Boolean(orchestrator) });
|
|
532
422
|
},
|
|
533
423
|
})) } : {}),
|
|
534
|
-
prompt
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
task: job.task,
|
|
539
|
-
// The card's discussion (0.106.0), printed inside THE CARD by
|
|
540
|
-
// the same builder the stash above wrote down — and the files
|
|
541
|
-
// on it (0.112.0), after it, the same way.
|
|
542
|
-
resumeBody: job.body,
|
|
543
|
-
investigationContext: job.investigationContext,
|
|
544
|
-
researchContext: job.researchContext,
|
|
545
|
-
cardThread: job.cardThread,
|
|
546
|
-
cardFiles: files.card,
|
|
547
|
-
position: job.position ?? 1,
|
|
548
|
-
total: job.total ?? 1,
|
|
549
|
-
})
|
|
550
|
-
: AGENT_HUMAN_KICKOFF({
|
|
551
|
-
agentName: job.agentName,
|
|
552
|
-
// INTERRUPT NOW: the previous run was stopped to send this.
|
|
553
|
-
interrupted: job.interrupted === true,
|
|
554
|
-
message: job.body ?? '',
|
|
555
|
-
investigationContext: job.investigationContext,
|
|
556
|
-
researchContext: job.researchContext,
|
|
557
|
-
askedByName: job.askedByName,
|
|
558
|
-
task: job.task,
|
|
559
|
-
// The discussion on the one card this message is about, in a
|
|
560
|
-
// fence of its own after the person's words (0.106.0).
|
|
561
|
-
cardThread: job.cardThread,
|
|
562
|
-
// The files (0.112.0): the person's inside their words, the
|
|
563
|
-
// card's at the foot of its discussion.
|
|
564
|
-
messageFiles: files.message,
|
|
565
|
-
cardFiles: files.card,
|
|
566
|
-
position: job.position ?? 1,
|
|
567
|
-
total: job.total ?? 1,
|
|
568
|
-
}),
|
|
569
|
-
// The knowledge paragraph, when this box holds a library — the same
|
|
570
|
-
// composer the tabs use, so an agent and a tab can never be told two
|
|
571
|
-
// different things about the same directory.
|
|
572
|
-
// Spoken to the CLI that runs it (0.115.0): a mockup on Codex is
|
|
573
|
-
// told it is Codex, never Claude.
|
|
574
|
-
system: withProjectContext(SYSTEM_AGENT_FOR(taskKind, runtimeById(rt)?.label, { prerequisitesAccepted: job.prerequisitesAccepted === true }), {
|
|
575
|
-
knowledgeDir: knowledgeDirFor(repoRoot),
|
|
576
|
-
// ARTIFACTS (0.94.0), while the server can show one — an agent's
|
|
577
|
-
// land on its page, under the facts row.
|
|
578
|
-
artifacts: getArtifactsAccepted(),
|
|
579
|
-
}) + (toolsNote ? `\n\n${toolsNote}` : '') + (artifactInstructions ? `\n\n${artifactInstructions}` : '') + `\n\n${INTERNAL_WAIT_CONTRACT}` + (projectTools.instructions ? `\n\n${projectTools.instructions}` : '') + (repositoryRun ? `\n\n${repositoryRun.note}\nRepository command argv: ${JSON.stringify([repositoryRun.tool?.config.command, ...(repositoryRun.tool?.config.args.slice(0, -1) ?? []), "__repository", "<name>"])}` : '') + (orchestrator ? `\n\n${ORCHESTRATOR_CONTRACT}` : ''),
|
|
424
|
+
// The kickoff and the system prompt, composed by the agent lane's
|
|
425
|
+
// shape (workAgentTurnShape.mjs) from what this run measured.
|
|
426
|
+
prompt: agentKickoff(job, { cardFiles: files.card, messageFiles: files.message }),
|
|
427
|
+
system: agentTurnSystem({ taskKind, runtimeLabel: runtimeById(rt)?.label, prerequisitesAccepted: job.prerequisitesAccepted === true, knowledgeDir: knowledgeDirFor(repoRoot), artifactsAccepted: getArtifactsAccepted(), toolsNote, artifactInstructions, toolInstructions: projectTools.instructions, repositoryRun, orchestrated: Boolean(orchestrator) }),
|
|
580
428
|
agentTools: projectTools,
|
|
581
429
|
writableDirs: repositoryRun?.writableDirs,
|
|
582
|
-
...((
|
|
430
|
+
...((tools.envNeeded || repositoryRun?.tool || orchestrator) ? { mcpEnv: { ...tools.env, ...(repositoryRun?.tool?.env ?? {}), ...(orchestrator?.env ?? {}) } } : {}),
|
|
583
431
|
orchestration: Boolean(orchestrator),
|
|
432
|
+
...(rt === 'codex' ? { codexVersion } : {}),
|
|
584
433
|
// NOBODY IS AT THIS SCREEN (2026-09-29): no display, BROWSER=none, and
|
|
585
434
|
// a browser profile of the agent's own, kept beside its Codex home —
|
|
586
435
|
// so a Chrome it starts to look at its page never reaches the
|
|
587
436
|
// person's (noWindowEnv.mjs).
|
|
588
437
|
browserHome: agentBrowserHome(sessionMetaPath, wt, agentId),
|
|
589
|
-
//
|
|
590
|
-
|
|
591
|
-
...(mountsProjectMcp && rt === 'claude'
|
|
592
|
-
? {
|
|
593
|
-
mcpArgs: [...(claudeDisabled ? [] : ['--strict-mcp-config']), '--mcp-config', projectTools.mcpPath],
|
|
594
|
-
projectMcpServers: projectTools.mcpServers,
|
|
595
|
-
...(claudeDisabled ? { machineMcp: claudeDisabled } : {}),
|
|
596
|
-
}
|
|
597
|
-
: fencedMount ? { mcpArgs: fencedMount.args } : {}),
|
|
438
|
+
// What the card mounts, as its runner takes it (workAgentTools.mjs).
|
|
439
|
+
...tools.mountArgs,
|
|
598
440
|
knowledgeDir: knowledgeDirFor(repoRoot),
|
|
599
441
|
// The kind's posture, as the one turn profile (turnProfile.mjs):
|
|
600
442
|
// build for code, the fenced design/research postures otherwise.
|
|
@@ -604,16 +446,17 @@ export function createAgentTurnExecution({
|
|
|
604
446
|
onSpawnResult: result => orchestrator?.spawnResult(result),
|
|
605
447
|
cwd: wt,
|
|
606
448
|
runtime: rt,
|
|
607
|
-
resume,
|
|
608
|
-
resumeThreadId:
|
|
449
|
+
resume: agentResume.resume,
|
|
450
|
+
resumeThreadId: agentResume.resumeThreadId,
|
|
609
451
|
onInit: (init) => {
|
|
610
452
|
orchestrator?.root(init.sessionId);
|
|
611
|
-
|
|
612
|
-
|
|
453
|
+
// A server that did not connect, as the CLI said (unknown: nothing).
|
|
454
|
+
trace.mcpStatus(init.mcpServers);
|
|
455
|
+
agentResume.sawSession(init.sessionId);
|
|
613
456
|
},
|
|
614
457
|
onThreadId: (id) => {
|
|
615
458
|
orchestrator?.root(id);
|
|
616
|
-
|
|
459
|
+
agentResume.sawThread(id);
|
|
617
460
|
},
|
|
618
461
|
onAnswer: (t) => {
|
|
619
462
|
lastAnswer = t;
|
|
@@ -757,7 +600,7 @@ export function createAgentTurnExecution({
|
|
|
757
600
|
resume: rt === 'codex' && posture === 'research' ? false : args.resume,
|
|
758
601
|
runtime: rt,
|
|
759
602
|
beforeFresh: () => {
|
|
760
|
-
|
|
603
|
+
agentResume.forgetThread();
|
|
761
604
|
lastAnswer = null;
|
|
762
605
|
model = null;
|
|
763
606
|
},
|
|
@@ -775,13 +618,10 @@ export function createAgentTurnExecution({
|
|
|
775
618
|
const counted = usage;
|
|
776
619
|
return runCli({
|
|
777
620
|
...agentTurnArgs, prompt, liveContinuation: true,
|
|
778
|
-
onUsage: (next) => agentTurnArgs.onUsage(
|
|
779
|
-
['input', 'output', 'cacheCreate', 'cacheRead'].map((k) =>
|
|
780
|
-
[k, (counted?.[k] ?? 0) + (next?.[k] ?? 0)])
|
|
781
|
-
)),
|
|
621
|
+
onUsage: (next) => agentTurnArgs.onUsage(addUsage(counted, next)),
|
|
782
622
|
writableDirs: repositoryRun.sourcesNow().filter((r) => r.wt !== wt).map((r) => r.wt),
|
|
783
623
|
resume: true,
|
|
784
|
-
resumeThreadId:
|
|
624
|
+
resumeThreadId: agentResume.threadNow() ?? agentTurnArgs.resumeThreadId,
|
|
785
625
|
});
|
|
786
626
|
},
|
|
787
627
|
});
|
|
@@ -829,23 +669,9 @@ export function createAgentTurnExecution({
|
|
|
829
669
|
}
|
|
830
670
|
for (const ch of spawnedChildren) workChildren.delete(ch);
|
|
831
671
|
if (agentChildren.get(place) === child) agentChildren.delete(place);
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
} catch {
|
|
836
|
-
/* a missing marker only costs one un-resumed turn */
|
|
837
|
-
}
|
|
838
|
-
}
|
|
839
|
-
// The thread THIS turn spoke under, pinned so the next card, answer
|
|
840
|
-
// or merge-resolve resumes it. After the turn, like the tab lane: an
|
|
841
|
-
// id learned mid-turn is only true once the turn that learned it ends.
|
|
842
|
-
if (codexThreadMarker && seenThreadId && CODEX_THREAD_RE.test(seenThreadId)) {
|
|
843
|
-
try {
|
|
844
|
-
writeFileSync(codexThreadMarker, seenThreadId);
|
|
845
|
-
} catch {
|
|
846
|
-
/* best-effort — the next turn runs fresh */
|
|
847
|
-
}
|
|
848
|
-
}
|
|
672
|
+
// The ran marker and the thread this turn spoke under, written now
|
|
673
|
+
// that the turn is over (workAgentResume.mjs).
|
|
674
|
+
agentResume.persist();
|
|
849
675
|
/**
|
|
850
676
|
* AN ACTION THAT CHANGES WHAT THE MACHINE WOULD MEASURE MUST CAUSE A
|
|
851
677
|
* NEW MEASUREMENT — the rule every session settle keeps, and the one
|
|
@@ -866,7 +692,7 @@ export function createAgentTurnExecution({
|
|
|
866
692
|
}
|
|
867
693
|
|
|
868
694
|
|
|
869
|
-
const commits =
|
|
695
|
+
const commits = turnCommits({ repositoryReceipts: repositoryRun?.receipts, wt, before, baseRef });
|
|
870
696
|
/**
|
|
871
697
|
* A PERSON INTERRUPTED IT (Interrupt now). Whatever the CLI printed was
|
|
872
698
|
* cut off, so there is no outcome to read: the turn reports `interrupted`
|
|
@@ -877,17 +703,7 @@ export function createAgentTurnExecution({
|
|
|
877
703
|
*/
|
|
878
704
|
if (run.interrupted) {
|
|
879
705
|
measured.ranCli = spawnedChildren.size > 0;
|
|
880
|
-
await postAgentTurn({
|
|
881
|
-
turnId,
|
|
882
|
-
outcome: 'nothing',
|
|
883
|
-
interrupted: true,
|
|
884
|
-
ran: spawnedChildren.size > 0,
|
|
885
|
-
commits,
|
|
886
|
-
...(usage ? { usage } : {}),
|
|
887
|
-
...(model ? { model } : {}),
|
|
888
|
-
...(branch ? { branch } : {}),
|
|
889
|
-
worktree: wt,
|
|
890
|
-
});
|
|
706
|
+
await postAgentTurn(interruptedTurnSettlement({ turnId, ran: spawnedChildren.size > 0, commits, usage, model, branch, wt }));
|
|
891
707
|
return;
|
|
892
708
|
}
|
|
893
709
|
// WHAT THE TURN REPORTS is one decision, off what was measured here
|
|
@@ -926,5 +742,5 @@ export function createAgentTurnExecution({
|
|
|
926
742
|
} finally { await repositoryRun?.tool?.close(); }
|
|
927
743
|
};
|
|
928
744
|
|
|
929
|
-
return { runAgentTurnInPlace
|
|
745
|
+
return { runAgentTurnInPlace };
|
|
930
746
|
}
|
|
@@ -16,6 +16,11 @@
|
|
|
16
16
|
* and settles once, so a crash in the run can never leave a turn unsettled to
|
|
17
17
|
* be re-offered and re-run on the operator's account.
|
|
18
18
|
*
|
|
19
|
+
* `interruptedTurnSettlement` is the sixth (2026-10-18, the SOLID pass): a turn
|
|
20
|
+
* a person stopped, where no outcome is read. The run and the lane's catch
|
|
21
|
+
* both send it — the catch when the stopped run then threw — and they had
|
|
22
|
+
* already drifted apart, so its body is built here once.
|
|
23
|
+
*
|
|
19
24
|
* `agentTurnSettlement` returns `{ res, limit, body, final }`; the caller posts
|
|
20
25
|
* `body` through the one turn-done wire, clears or sets the machine's limit off
|
|
21
26
|
* `res`/`limit`, parks on `limit`, and runs the review-entry beat only after a
|
|
@@ -27,6 +32,7 @@ import { changedArtifacts, scanArtifacts } from './artifacts.mjs';
|
|
|
27
32
|
import { scrub as envScrub } from './uplinkScrub.mjs';
|
|
28
33
|
import { usableImageArtifact } from './imageArtifact.mjs';
|
|
29
34
|
import { withoutTurnDeclaration } from './turnDeclaration.mjs';
|
|
35
|
+
import { ANSWER_DELIVERED_USES, researchUseOf, withResearchHeader } from './researchUse.mjs';
|
|
30
36
|
|
|
31
37
|
export function agentTurnSettlement({ turnId, job, kind, out, lastAnswer, usage, model = null, commits, artifactsBefore, branch, wt }) {
|
|
32
38
|
const res = (lastAnswer && parseTurnResult(lastAnswer)) || parseTurnResult(out);
|
|
@@ -117,9 +123,9 @@ export function agentTurnSettlement({ turnId, job, kind, out, lastAnswer, usage,
|
|
|
117
123
|
*/
|
|
118
124
|
// Existing document deliveries default to the Markdown proof. Only an
|
|
119
125
|
// explicit temporary-use declaration can deliver through the stored answer.
|
|
120
|
-
const briefUse =
|
|
126
|
+
const briefUse = researchUseOf(job.task?.brief);
|
|
121
127
|
const researchUse = kind.posture === 'research' ? briefUse ?? res.researchUse : null;
|
|
122
|
-
const temporaryResearch =
|
|
128
|
+
const temporaryResearch = ANSWER_DELIVERED_USES.includes(researchUse);
|
|
123
129
|
if (res.outcome === 'delivered' && temporaryResearch && !res.answer?.trim()) {
|
|
124
130
|
return report({ turnId, outcome: 'nothing', answer: 'The research turn returned no findings.', branch, worktree: wt });
|
|
125
131
|
}
|
|
@@ -154,7 +160,7 @@ export function agentTurnSettlement({ turnId, job, kind, out, lastAnswer, usage,
|
|
|
154
160
|
...(prerequisite && !cannotRequest ? { prerequisite } : {}),
|
|
155
161
|
outcome: res.outcome,
|
|
156
162
|
answer: cannotRequest ?? envScrub(kind.posture === 'research' && researchUse
|
|
157
|
-
?
|
|
163
|
+
? withResearchHeader(researchUse, res.answer ?? '') : res.answer ?? ''),
|
|
158
164
|
...(commits.length ? { commits } : {}),
|
|
159
165
|
// SCRUBBED like the answer beside it: a raised card lands in the
|
|
160
166
|
// project doc every member reads, and a brief quoting the `.env` the
|
|
@@ -235,3 +241,24 @@ export function failedTurnSettlement({ turnId, error, commits = [], usage = null
|
|
|
235
241
|
...(wt ? { branch, worktree: wt } : {}),
|
|
236
242
|
};
|
|
237
243
|
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* A PERSON INTERRUPTED IT (Interrupt now), so there is no outcome to read:
|
|
247
|
+
* whatever the CLI printed was cut off. What was measured rides along — the
|
|
248
|
+
* commits it made, its spend, the model it ran on, the branch and the
|
|
249
|
+
* worktree — and nothing measured is nothing claimed. `ran` is whether its
|
|
250
|
+
* CLI started at all.
|
|
251
|
+
*/
|
|
252
|
+
export function interruptedTurnSettlement({ turnId, ran, commits, usage = null, model = null, branch = null, wt = null }) {
|
|
253
|
+
return {
|
|
254
|
+
turnId,
|
|
255
|
+
outcome: 'nothing',
|
|
256
|
+
interrupted: true,
|
|
257
|
+
ran,
|
|
258
|
+
commits,
|
|
259
|
+
...(usage ? { usage } : {}),
|
|
260
|
+
...(model ? { model } : {}),
|
|
261
|
+
...(branch ? { branch } : {}),
|
|
262
|
+
...(wt ? { worktree: wt } : {}),
|
|
263
|
+
};
|
|
264
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AN AGENT TURN'S PROMPT PAIR — its kickoff, a live message's follow-up and
|
|
3
|
+
* its system prompt (pure, 2026-10-18, the SOLID pass). The twin of
|
|
4
|
+
* workTurnShape.mjs, which is the same thing for a tab.
|
|
5
|
+
*
|
|
6
|
+
* Split out of workAgentTurnExecution.mjs, which composed all three inline in
|
|
7
|
+
* the turn: two AGENT_HUMAN_KICKOFF argument bags spelling the same ten
|
|
8
|
+
* fields, the task kickoff beside them, and a system prompt of eight sections
|
|
9
|
+
* on one line. The copies had already drifted: investigationContext reached
|
|
10
|
+
* the task and human kickoffs on 2026-10-12, the follow-up written two days
|
|
11
|
+
* later went without it, and live messages carried less context than the
|
|
12
|
+
* first prompt until 2026-10-15 added it beside researchContext at all three
|
|
13
|
+
* sites. So the fields every kickoff shares are spelled once (`shared`), and
|
|
14
|
+
* the next job context key is one edit here.
|
|
15
|
+
*
|
|
16
|
+
* ONE SPEC BUILDER (`AGENT_TASK_SPEC`) is shared by the stash the run writes
|
|
17
|
+
* before the CLI and the kickoff here, so what the reviewer reads is
|
|
18
|
+
* byte-identical to what the agent read — the card's discussion included
|
|
19
|
+
* (0.106.0): both are handed the same `job.cardThread`, and the builder
|
|
20
|
+
* prints it inside the spec — and the card's files after it (0.112.0).
|
|
21
|
+
*
|
|
22
|
+
* Pure: the run measures (the files, the roster's answers, the tools note)
|
|
23
|
+
* and hands the values in; nothing here reads a disk or a clock.
|
|
24
|
+
*/
|
|
25
|
+
import { AGENT_TASK_KICKOFF, AGENT_HUMAN_KICKOFF, SYSTEM_AGENT_FOR, withProjectContext } from './prompts.mjs';
|
|
26
|
+
import { INTERNAL_WAIT_CONTRACT } from './agentInternalWait.mjs';
|
|
27
|
+
import { ORCHESTRATOR_CONTRACT } from './orchestratorPrompt.mjs';
|
|
28
|
+
|
|
29
|
+
/** What every kickoff of this job is handed: the agent, the card and its
|
|
30
|
+
* discussion, the job's context handoffs, the card's files, the place in
|
|
31
|
+
* the run. */
|
|
32
|
+
const shared = (job, cardFiles) => ({
|
|
33
|
+
agentName: job.agentName,
|
|
34
|
+
task: job.task,
|
|
35
|
+
cardThread: job.cardThread,
|
|
36
|
+
investigationContext: job.investigationContext,
|
|
37
|
+
researchContext: job.researchContext,
|
|
38
|
+
cardFiles,
|
|
39
|
+
position: job.position ?? 1,
|
|
40
|
+
total: job.total ?? 1,
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
/** The turn's first prompt: the card, on a task turn; the person's words
|
|
44
|
+
* otherwise. `cardFiles` and `messageFiles` are the files already on disk. */
|
|
45
|
+
export const agentKickoff = (job, { cardFiles, messageFiles }) =>
|
|
46
|
+
job.kind === 'task' && job.task
|
|
47
|
+
? AGENT_TASK_KICKOFF({
|
|
48
|
+
// The card's discussion (0.106.0), printed inside THE CARD by
|
|
49
|
+
// the same builder the run's stash wrote down — and the files
|
|
50
|
+
// on it (0.112.0), after it, the same way.
|
|
51
|
+
...shared(job, cardFiles),
|
|
52
|
+
resumeBody: job.body,
|
|
53
|
+
})
|
|
54
|
+
: AGENT_HUMAN_KICKOFF({
|
|
55
|
+
// The discussion on the one card this message is about, in a
|
|
56
|
+
// fence of its own after the person's words (0.106.0).
|
|
57
|
+
...shared(job, cardFiles),
|
|
58
|
+
// INTERRUPT NOW: the previous run was stopped to send this.
|
|
59
|
+
interrupted: job.interrupted === true,
|
|
60
|
+
message: job.body ?? '',
|
|
61
|
+
askedByName: job.askedByName,
|
|
62
|
+
// The files (0.112.0): the person's inside their words, the
|
|
63
|
+
// card's at the foot of its discussion.
|
|
64
|
+
messageFiles,
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
/** A live message sent while the turn runs, as the CLI is handed it — on an
|
|
68
|
+
* orchestrated card with its id, so the route it takes is reported. */
|
|
69
|
+
export const agentFollowupKickoff = (job, followup, { cardFiles, messageFiles, orchestrated }) =>
|
|
70
|
+
AGENT_HUMAN_KICKOFF({
|
|
71
|
+
...shared(job, cardFiles),
|
|
72
|
+
message: followup.body + (followup.libraryPrompt ?? '') + (orchestrated ? `\nMessage id: ${followup.id}. Choose and report its route with card_orchestrator.route_message.` : ''),
|
|
73
|
+
askedByName: followup.askedByName,
|
|
74
|
+
messageFiles,
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
/** The turn's system prompt, in its fixed section order. */
|
|
78
|
+
export function agentTurnSystem({ taskKind, runtimeLabel, prerequisitesAccepted, knowledgeDir, artifactsAccepted, toolsNote, artifactInstructions, toolInstructions, repositoryRun, orchestrated }) {
|
|
79
|
+
// The knowledge paragraph, when this box holds a library — the same
|
|
80
|
+
// composer the tabs use, so an agent and a tab can never be told two
|
|
81
|
+
// different things about the same directory.
|
|
82
|
+
// Spoken to the CLI that runs it (0.115.0): a mockup on Codex is
|
|
83
|
+
// told it is Codex, never Claude.
|
|
84
|
+
return withProjectContext(SYSTEM_AGENT_FOR(taskKind, runtimeLabel, { prerequisitesAccepted }), {
|
|
85
|
+
knowledgeDir,
|
|
86
|
+
// ARTIFACTS (0.94.0), while the server can show one — an agent's
|
|
87
|
+
// land on its page, under the facts row.
|
|
88
|
+
artifacts: artifactsAccepted,
|
|
89
|
+
}) + (toolsNote ? `\n\n${toolsNote}` : '') + (artifactInstructions ? `\n\n${artifactInstructions}` : '') + `\n\n${INTERNAL_WAIT_CONTRACT}` + (toolInstructions ? `\n\n${toolInstructions}` : '') + (repositoryRun ? `\n\n${repositoryRun.note}\nRepository command argv: ${JSON.stringify([repositoryRun.tool?.config.command, ...(repositoryRun.tool?.config.args.slice(0, -1) ?? []), "__repository", "<name>"])}` : '') + (orchestrated ? `\n\n${ORCHESTRATOR_CONTRACT}` : '');
|
|
90
|
+
}
|