flowviant 0.125.30 → 0.125.31
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/agentPlan.mjs +8 -0
- package/bin/lib/agentQueueContext.mjs +6 -0
- package/bin/lib/agentSplit.mjs +90 -0
- package/bin/lib/architectureContract.mjs +57 -0
- package/bin/lib/architectureReport.mjs +38 -0
- package/bin/lib/artifactContracts.mjs +21 -2
- package/bin/lib/claudeInput.mjs +1 -1
- package/bin/lib/claudeSubagents.mjs +46 -6
- package/bin/lib/claudeWorkflows.mjs +94 -0
- package/bin/lib/cliLimit.mjs +5 -0
- package/bin/lib/codexInput.mjs +191 -0
- package/bin/lib/codexSubagentEvents.mjs +3 -1
- package/bin/lib/codexSubagents.mjs +34 -9
- package/bin/lib/conversationOutput.mjs +14 -0
- package/bin/lib/crossCliChild.mjs +327 -0
- package/bin/lib/crossCliLaunch.mjs +92 -0
- package/bin/lib/crossCliPosture.mjs +55 -0
- package/bin/lib/crossCliResearch.mjs +19 -0
- package/bin/lib/fixtures/workflow-ultracode.jsonl +22 -0
- package/bin/lib/nativeAgentTools.mjs +12 -5
- package/bin/lib/orchestrator.mjs +24 -7
- package/bin/lib/orchestratorClient.mjs +8 -2
- package/bin/lib/orchestratorPrompt.mjs +13 -5
- package/bin/lib/orchestratorProtocol.mjs +18 -0
- package/bin/lib/outsideCli.mjs +124 -0
- package/bin/lib/prompts.mjs +7 -5
- package/bin/lib/runTurn.mjs +30 -8
- package/bin/lib/runtimeClaude.mjs +7 -3
- package/bin/lib/runtimeCodex.mjs +12 -6
- package/bin/lib/runtimeLimits.mjs +59 -1
- package/bin/lib/splitGate.mjs +115 -0
- package/bin/lib/subagentBounds.mjs +26 -0
- package/bin/lib/subagentLedger.mjs +110 -0
- package/bin/lib/trace.mjs +134 -6
- package/bin/lib/turnDeclaration.mjs +4 -4
- package/bin/lib/turnGaps.mjs +6 -2
- package/bin/lib/turnSubagents.mjs +23 -9
- package/bin/lib/wikiDraft.mjs +9 -0
- package/bin/lib/workAgentResume.mjs +11 -2
- package/bin/lib/workAgentTurnExecution.mjs +31 -7
- package/bin/lib/workAgentTurnOutcome.mjs +11 -0
- package/bin/lib/workAgentTurnReports.mjs +5 -2
- package/package.json +1 -1
|
@@ -17,6 +17,11 @@
|
|
|
17
17
|
* bounds and settles its own records, so only an unowned turn is counted
|
|
18
18
|
* here; and native transcript reads are readouts, so the drain at close is
|
|
19
19
|
* bounded and never holds the card's answer.
|
|
20
|
+
*
|
|
21
|
+
* EVERY UPDATE NAMES ITS CLI AND, ONCE KNOWN, ITS KEY (2026-11-06, S4.1):
|
|
22
|
+
* the identity the CLI itself keeps for the child across turns — Claude's
|
|
23
|
+
* native agent id, Codex's child thread id — so a child resumed in a later
|
|
24
|
+
* turn is the same record there (workAgentResume.mjs keeps which turns).
|
|
20
25
|
*/
|
|
21
26
|
|
|
22
27
|
import { handleStreamLine } from './claudeStream.mjs';
|
|
@@ -24,35 +29,44 @@ import { createClaudeSubagents } from './claudeSubagents.mjs';
|
|
|
24
29
|
import { createClaudeSubagentProgress } from './claudeSubagentProgress.mjs';
|
|
25
30
|
import { createCodexSubagentReader } from './codexSubagents.mjs';
|
|
26
31
|
import { humanizeClaudeTool } from './runtimeEvents.mjs';
|
|
27
|
-
import { SUBAGENT_CAP } from './subagentBounds.mjs';
|
|
32
|
+
import { SUBAGENT_CAP, SUBAGENT_KEY_MAX } from './subagentBounds.mjs';
|
|
33
|
+
|
|
34
|
+
/** The child's stable identity, `<cli>:<native id>`, or nothing yet. */
|
|
35
|
+
export function subagentKey(runtime, update) {
|
|
36
|
+
const native = runtime === 'codex' ? update.id : runtime === 'claude' ? update.nativeId : null;
|
|
37
|
+
const key = typeof native === 'string' && native ? `${runtime}:${native}` : null;
|
|
38
|
+
return key && key.length <= SUBAGENT_KEY_MAX ? key : null;
|
|
39
|
+
}
|
|
28
40
|
|
|
29
41
|
/**
|
|
30
42
|
* `sinks` are the turn's own: its console, activity, tool-event and text
|
|
31
43
|
* sinks, which a Claude child's transcript rows feed as its stream does.
|
|
32
44
|
*/
|
|
33
|
-
export function createTurnSubagents({ runtime, cwd, since, env, owned, onSubagent, onSpawnResult, resumeThreadId,
|
|
45
|
+
export function createTurnSubagents({ runtime, cwd, since, env, owned, onSubagent, onWorkflow, onSpawnResult, resumeThreadId, knownSubagents,
|
|
34
46
|
sinks: { emit, onActivity, onToolEvent, appendText, onDetail } }) {
|
|
35
47
|
// An orchestrator bounds its own records and settles them at close
|
|
36
48
|
// (orchestrator.mjs). Counted here by stream id, refused retries used up
|
|
37
49
|
// the 32 and later children's updates were dropped; the exit sweep
|
|
38
50
|
// reported 'stopped' off stream state the controller had already settled.
|
|
39
51
|
const agentStates = new Map();
|
|
40
|
-
const reportSubagent = (
|
|
41
|
-
if (!
|
|
42
|
-
|
|
43
|
-
|
|
52
|
+
const reportSubagent = (raw) => {
|
|
53
|
+
if (!raw?.id) return;
|
|
54
|
+
const key = subagentKey(runtime, raw);
|
|
55
|
+
const update = { ...raw, cli: runtime, ...(key ? { key } : {}) };
|
|
56
|
+
// Past the cap a child is still handed on: the trace relay counts it as
|
|
57
|
+
// not recorded (its `notRecorded`), and the sweep does not follow it.
|
|
58
|
+
if (!owned && raw.kind !== 'workflow' && (agentStates.has(update.id) || agentStates.size < SUBAGENT_CAP))
|
|
44
59
|
agentStates.set(update.id, update.status ?? agentStates.get(update.id) ?? 'running');
|
|
45
|
-
}
|
|
46
60
|
onSubagent?.(update);
|
|
47
61
|
};
|
|
48
|
-
const subagents = createClaudeSubagents(reportSubagent);
|
|
62
|
+
const subagents = createClaudeSubagents(reportSubagent, onWorkflow);
|
|
49
63
|
const claudeProgress = runtime === 'claude' && onSubagent ? createClaudeSubagentProgress({
|
|
50
64
|
cwd, since, env,
|
|
51
65
|
onLine: line => handleStreamLine(line, { cwd, emit, onActivity, onToolEvent, appendText,
|
|
52
66
|
answerFromResult: true, subagents, onDetail }),
|
|
53
67
|
}) : null;
|
|
54
68
|
const codexAgents = runtime === 'codex' && onSubagent ? createCodexSubagentReader({
|
|
55
|
-
since, env,
|
|
69
|
+
since, env, knownTargets: knownSubagents ?? null,
|
|
56
70
|
onSubagent: reportSubagent,
|
|
57
71
|
onActivity: (activity) => {
|
|
58
72
|
emit(activity.kind === 'error' ? activity.full ?? activity.label : activity.label + '\n', activity);
|
package/bin/lib/wikiDraft.mjs
CHANGED
|
@@ -8,6 +8,7 @@ import { ensureRealSubdir } from './ownedDirs.mjs';
|
|
|
8
8
|
import { isSafePath, assertWikiDraftFits } from './vaultDiff.mjs';
|
|
9
9
|
|
|
10
10
|
import { wikiScalars as scalars, protectedWikiPage } from './wikiOwnership.mjs';
|
|
11
|
+
import { SYSTEM_ARCHITECTURE_PATH, readSystemArchitecture } from './architectureReport.mjs';
|
|
11
12
|
|
|
12
13
|
/** Never follow links into an imported vault. A partial read cannot be a draft
|
|
13
14
|
* basis: fail before the CLI starts rather than infer missing pages as deleted. */
|
|
@@ -60,6 +61,14 @@ export function beginWikiDraft(vaultDir) {
|
|
|
60
61
|
discard: () => rmSync(dir, { recursive: true, force: true }),
|
|
61
62
|
publish({ sha, repoFullName }) {
|
|
62
63
|
const candidate = pages(dir, '', new Map(), true);
|
|
64
|
+
const architecture = candidate.get(SYSTEM_ARCHITECTURE_PATH);
|
|
65
|
+
const oldArchitecture = before.get(SYSTEM_ARCHITECTURE_PATH);
|
|
66
|
+
if (!protectedWikiPage(SYSTEM_ARCHITECTURE_PATH, oldArchitecture ?? architecture ?? '')) {
|
|
67
|
+
const parsed = readSystemArchitecture(architecture ?? '');
|
|
68
|
+
if (parsed.status === 'invalid' || (oldArchitecture && readSystemArchitecture(oldArchitecture).status === 'available' && parsed.status !== 'available')) {
|
|
69
|
+
throw new Error('Architecture report is incomplete or invalid; last good wiki retained');
|
|
70
|
+
}
|
|
71
|
+
}
|
|
63
72
|
const changes = new Map();
|
|
64
73
|
for (const [path, text] of candidate) {
|
|
65
74
|
const old = before.get(path);
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
import { existsSync, writeFileSync } from 'node:fs';
|
|
26
26
|
import { CLAUDE_SESSION_RE, CODEX_THREAD_RE, RESUME_MARKERS } from './conversationIds.mjs';
|
|
27
27
|
import { pinId, readPinnedId } from './resumeMarker.mjs';
|
|
28
|
+
import { subagentLedger } from './subagentLedger.mjs';
|
|
28
29
|
|
|
29
30
|
/**
|
|
30
31
|
* One agent turn's resume. `sessionMetaPath` is the lane's (a path in the
|
|
@@ -32,9 +33,11 @@ import { pinId, readPinnedId } from './resumeMarker.mjs';
|
|
|
32
33
|
* `resumeThreadId`, the kept `codexHome`, and what the run tells it as the
|
|
33
34
|
* CLI speaks: `sawSession` (Claude's init), `sawThread` (Codex's
|
|
34
35
|
* `thread.started`), `forgetThread` (a lost resume retried fresh), `threadNow`
|
|
35
|
-
* and, once the turn is over, `persist`.
|
|
36
|
+
* and, once the turn is over, `persist`. `subagents` is the agent's subagent
|
|
37
|
+
* ledger for this turn (subagentLedger.mjs): which turns each child appeared
|
|
38
|
+
* in, written with the markers.
|
|
36
39
|
*/
|
|
37
|
-
export function agentTurnResume({ rt, wt, agentId, sessionMetaPath }) {
|
|
40
|
+
export function agentTurnResume({ rt, wt, agentId, sessionMetaPath, turnId }) {
|
|
38
41
|
// ONE AGENT IS ONE DIRECTORY, so the CLI's own cwd-keyed resume is exactly
|
|
39
42
|
// right here — the ambiguity that forced per-tab session pinning in the
|
|
40
43
|
// Workbench (many tabs, one place) cannot arise. The marker is what
|
|
@@ -86,8 +89,13 @@ export function agentTurnResume({ rt, wt, agentId, sessionMetaPath }) {
|
|
|
86
89
|
const codexAgentHome =
|
|
87
90
|
rt === 'codex' ? sessionMetaPath(wt, 'flowviant-agent-codex-home', agentId) : null;
|
|
88
91
|
|
|
92
|
+
// Its children's turns, kept beside the markers (a child resumes only where
|
|
93
|
+
// its conversation does), per agent and per CLI: keys never cross CLIs.
|
|
94
|
+
const subagents = subagentLedger(sessionMetaPath(wt, `flowviant-agent-subagents-${rt}`, agentId), turnId);
|
|
95
|
+
|
|
89
96
|
return {
|
|
90
97
|
resume,
|
|
98
|
+
subagents,
|
|
91
99
|
resumeThreadId: codexResumeId || claudeResumeId || undefined,
|
|
92
100
|
codexHome: codexAgentHome,
|
|
93
101
|
/** Claude's session id from its init, pinned at once (a Claude turn only). */
|
|
@@ -116,6 +124,7 @@ export function agentTurnResume({ rt, wt, agentId, sessionMetaPath }) {
|
|
|
116
124
|
// id learned mid-turn is only true once the turn that learned it ends.
|
|
117
125
|
// Best-effort — the next turn runs fresh.
|
|
118
126
|
pinId(codexThreadMarker, CODEX_THREAD_RE, seenThreadId);
|
|
127
|
+
subagents.persist();
|
|
119
128
|
},
|
|
120
129
|
};
|
|
121
130
|
}
|
|
@@ -30,6 +30,7 @@ import { finishRepositoryDelivery, allResolutionsCommitted } from './repositoryD
|
|
|
30
30
|
*/
|
|
31
31
|
import { MODEL, PROJECT_ID } from './config.mjs';
|
|
32
32
|
import { setRuntimeLimit } from './daemonState.mjs';
|
|
33
|
+
import { limitResetAt } from './runtimeLimits.mjs';
|
|
33
34
|
import { gitOrNull } from './git.mjs';
|
|
34
35
|
import { assertWorktree } from './worktreeHealth.mjs';
|
|
35
36
|
import { placeTurnLockedByLivePid, recordTurnLock } from './turnLock.mjs';
|
|
@@ -48,6 +49,7 @@ import { scrub as envScrub } from './uplinkScrub.mjs';
|
|
|
48
49
|
import { canRun, runtimeById } from './runtimes.mjs';
|
|
49
50
|
import { toolEventOf, CLAUDE_TOOL_PROSE_KINDS, scrubNativeText } from './runtimeEvents.mjs';
|
|
50
51
|
import { makeTraceRelay } from './trace.mjs';
|
|
52
|
+
import { createOutsideRuns } from './outsideCli.mjs';
|
|
51
53
|
import { createWebsiteTrace } from './websiteTrace.mjs';
|
|
52
54
|
import { prepareCardTools } from './workAgentTools.mjs';
|
|
53
55
|
import { runTurnResumingOnce } from './resumeLost.mjs';
|
|
@@ -62,6 +64,7 @@ import { agentTurnResume } from './workAgentResume.mjs';
|
|
|
62
64
|
import { createDeliveryReporter } from './workDelivery.mjs';
|
|
63
65
|
import { createLiveMessages } from './workLiveMessages.mjs';
|
|
64
66
|
import { createOrchestrator } from './orchestrator.mjs';
|
|
67
|
+
import { crossCliFor } from './crossCliLaunch.mjs';
|
|
65
68
|
import { claudeOrchestrationSafe } from './orchestratorHooks.mjs';
|
|
66
69
|
import { ORCHESTRATED_POSTURES, orchestratesPosture } from './orchestratorPrompt.mjs';
|
|
67
70
|
import { knownCodexModels } from './runtimeOfferings.mjs';
|
|
@@ -78,6 +81,7 @@ import { readEnvironmentRecipe } from './reviewEnvironmentConfig.mjs';
|
|
|
78
81
|
import { environmentInstructions, environmentOverlay, holdWorktreeEnvironment } from './reviewEnvironment.mjs';
|
|
79
82
|
import { environmentCredentialNames } from './reviewDatabase.mjs';
|
|
80
83
|
import { createRuntimeReview } from './runtimeReview.mjs';
|
|
84
|
+
import { splitBrain, splitPolicy } from './splitGate.mjs';
|
|
81
85
|
|
|
82
86
|
export function createAgentTurnExecution({
|
|
83
87
|
admit,
|
|
@@ -293,7 +297,7 @@ export function createAgentTurnExecution({
|
|
|
293
297
|
|
|
294
298
|
// Which conversation this turn resumes, and the kept Codex home it is
|
|
295
299
|
// filed under: the agent's resume across turns (workAgentResume.mjs).
|
|
296
|
-
const agentResume = agentTurnResume({ rt, wt, agentId, sessionMetaPath });
|
|
300
|
+
const agentResume = agentTurnResume({ rt, wt, agentId, sessionMetaPath, turnId });
|
|
297
301
|
|
|
298
302
|
/**
|
|
299
303
|
* THE TURN'S FILES, ON DISK BEFORE THE PROMPT NAMES THEM (0.112.0) — the
|
|
@@ -370,7 +374,12 @@ export function createAgentTurnExecution({
|
|
|
370
374
|
post: postAgentTrace,
|
|
371
375
|
orchestrationVersion: orchestrating ? 2 : undefined,
|
|
372
376
|
scrub: envScrub,
|
|
377
|
+
// Each child's CLI, and the turns a resumed one appeared in (S4.1).
|
|
378
|
+
cli: rt,
|
|
379
|
+
runsOf: agentResume.subagents.runsOf ?? undefined,
|
|
373
380
|
});
|
|
381
|
+
// The lead's shell-outs to another CLI, shown as Outside rows (outsideCli.mjs).
|
|
382
|
+
const outsideRuns = createOutsideRuns(trace);
|
|
374
383
|
// A live Send can choose its path as soon as the CLI starts, including a
|
|
375
384
|
// silent solo turn. Do not leave a two-second first-flush race at spawn.
|
|
376
385
|
if (orchestrating) await trace.flush(1_000);
|
|
@@ -405,6 +414,8 @@ export function createAgentTurnExecution({
|
|
|
405
414
|
// run's children and contract: Claude always names --model (the daemon's
|
|
406
415
|
// pin when the card has none); Codex's unpinned default is its own.
|
|
407
416
|
const cardBrain = { model: brain.model ?? (rt === 'claude' ? MODEL : null), effort: brain.effort ?? null };
|
|
417
|
+
// The person's split answer or Always switch, when the server asks (splitGate.mjs).
|
|
418
|
+
const split = splitPolicy(job);
|
|
408
419
|
|
|
409
420
|
let out = '';
|
|
410
421
|
let child = null;
|
|
@@ -545,10 +556,15 @@ export function createAgentTurnExecution({
|
|
|
545
556
|
assignmentContext: () => agentChildScope(job),
|
|
546
557
|
// Codex's spawn takes only a catalog model (knownCodexModels, the
|
|
547
558
|
// measured models_cache.json rows the roster already reports).
|
|
548
|
-
children: { access: ORCHESTRATED_POSTURES[posture], ...cardBrain, ...(rt === 'codex' ? { catalog: knownCodexModels() } : {}) },
|
|
559
|
+
children: { access: ORCHESTRATED_POSTURES[posture], ...cardBrain, ...splitBrain(split, rt, cardBrain).brain, split, ...(rt === 'codex' ? { catalog: knownCodexModels() } : {}) },
|
|
549
560
|
// Whether the words reached the CLI; Codex reads its inbox from hooks.
|
|
550
561
|
notify: message => liveInput ? run.live?.inject(message) === true : false,
|
|
551
|
-
taken: id => run.live?.onTaken(id)
|
|
562
|
+
taken: id => run.live?.onTaken(id),
|
|
563
|
+
// Subagents on another CLI (crossCliLaunch.mjs), counted as live children while they run.
|
|
564
|
+
crossCli: crossCliFor({ rt, posture, wt, worktreeRepo, ledger: agentResume.subagents, trace, scrub: envScrub, onChild: ch => {
|
|
565
|
+
workChildren.set(ch, agentId); groupKillChildren?.add(ch);
|
|
566
|
+
return () => { workChildren.delete(ch); groupKillChildren?.delete(ch); };
|
|
567
|
+
} }) });
|
|
552
568
|
}
|
|
553
569
|
const tools = prepareCardTools({ rt, posture, wt, repoRoot, baseRef, sources: repositoryRun?.sources ?? [], repositoryTool: repositoryRun?.tool ?? null, orchestratorConfig: orchestrator?.config ?? null, uiReviewConfig: uiReview?.config, videoConfig: videoRender?.config ?? null, browserConfig: managedBrowser?.config, orchestrated: Boolean(orchestrator), codexHome: agentResume.codexHome,
|
|
554
570
|
runtimeConfig: runtimeReview?.config,
|
|
@@ -579,7 +595,7 @@ export function createAgentTurnExecution({
|
|
|
579
595
|
intermediateFiles,
|
|
580
596
|
researchIdentity: { projectId: PROJECT_ID, agentId, turnId, taskId: job.task?.id ?? job.repositoryTaskId },
|
|
581
597
|
prompt: agentKickoff(job, { cardFiles: files.card, messageFiles: files.message, intermediateFiles }),
|
|
582
|
-
system: agentTurnSystem({ taskKind, taskType: job.task?.taskType, runtimeLabel: runtimeById(rt)?.label, prerequisitesAccepted: job.prerequisitesAccepted === true, knowledgeDir: knowledgeDirFor(repoRoot), artifactsAccepted: getArtifactsAccepted(), toolsNote, artifactInstructions, toolInstructions: projectTools.instructions, repositoryRun, orchestration: orchestrator ? { posture, runtime: rt, ...cardBrain } : null }),
|
|
598
|
+
system: agentTurnSystem({ taskKind, taskType: job.task?.taskType, runtimeLabel: runtimeById(rt)?.label, prerequisitesAccepted: job.prerequisitesAccepted === true, knowledgeDir: knowledgeDirFor(repoRoot), artifactsAccepted: getArtifactsAccepted(), toolsNote, artifactInstructions, toolInstructions: projectTools.instructions, repositoryRun, orchestration: orchestrator ? { posture, runtime: rt, ...cardBrain, split, crossCli: orchestrator.crossCli() } : null }),
|
|
583
599
|
agentTools: projectTools,
|
|
584
600
|
writableDirs: repositoryRun?.writableDirs,
|
|
585
601
|
...((tools.envNeeded || repositoryRun?.tool || orchestrator) ? { mcpEnv: { ...tools.env, ...(repositoryRun?.tool?.env ?? {}), ...(orchestrator?.env ?? {}) } } : {}),
|
|
@@ -597,7 +613,9 @@ export function createAgentTurnExecution({
|
|
|
597
613
|
// build for code, the fenced design/research postures otherwise.
|
|
598
614
|
profile: posture,
|
|
599
615
|
cardTurn: Boolean(orchestrator),
|
|
600
|
-
|
|
616
|
+
onWorkflow: row => trace.workflow(row),
|
|
617
|
+
onSubagent: (update) => update.kind === 'workflow' || !orchestrator ? trace.agent(update) : orchestrator.observe(update),
|
|
618
|
+
...(rt === 'codex' ? { knownSubagents: agentResume.subagents.codexTargets() } : {}),
|
|
601
619
|
onSpawnResult: result => orchestrator?.spawnResult(result),
|
|
602
620
|
cwd: wt,
|
|
603
621
|
runtime: rt,
|
|
@@ -705,6 +723,7 @@ export function createAgentTurnExecution({
|
|
|
705
723
|
}
|
|
706
724
|
return;
|
|
707
725
|
}
|
|
726
|
+
outsideRuns.observe(name, input, meta);
|
|
708
727
|
brief?.append(meta?.result ? 'tool result' : 'tool call', meta?.result
|
|
709
728
|
? { id: meta.id, exitCode: meta.exitCode, isError: meta.isError, output: meta.output }
|
|
710
729
|
: { name, input });
|
|
@@ -714,7 +733,7 @@ export function createAgentTurnExecution({
|
|
|
714
733
|
trace.tool(toolEventOf(name, input, wt, envScrub));
|
|
715
734
|
},
|
|
716
735
|
// The CLI's own per-turn token counts, off the `result` event. One
|
|
717
|
-
//
|
|
736
|
+
// cumulative report per CLI invocation. Internal-wait recovery combines those
|
|
718
737
|
// counts before this callback; project accounting still happens
|
|
719
738
|
// SERVER-side, behind the settle's idempotent win,
|
|
720
739
|
// because a retried settle must not charge the same turn twice.
|
|
@@ -972,6 +991,9 @@ export function createAgentTurnExecution({
|
|
|
972
991
|
if (body.outcome === 'delivered' && job.task?.taskType === 'tests' && res?.testFindings) {
|
|
973
992
|
body.testFindings = measureTestFindings(res.testFindings, repositoryRun?.sourcesNow() ?? [{ wt, baseRef: baseRef(), id: job.repos?.length === 1 ? job.repos[0] : null }], envScrub);
|
|
974
993
|
}
|
|
994
|
+
// The split this turn ran under, when the person approved it or Always is
|
|
995
|
+
// on: the batch's CLI, model and effort, as the job carried them.
|
|
996
|
+
if (orchestrator && split && split.mode !== 'ask') body.subagents = split;
|
|
975
997
|
if (limit) {
|
|
976
998
|
setRuntimeLimit(PROJECT_ID, rt, limit);
|
|
977
999
|
// Every agent ON THIS CLI parks, because that account is shared: one
|
|
@@ -979,7 +1001,9 @@ export function createAgentTurnExecution({
|
|
|
979
1001
|
// a machine can hold a Claude login and a Codex login, and a Codex
|
|
980
1002
|
// limit is no reason to stop the Claude agents. The turn itself is
|
|
981
1003
|
// reported as `nothing` — it did not deliver and it did not ask.
|
|
982
|
-
|
|
1004
|
+
// With the CLI's measured reset, when it said one (runtimeLimits.mjs).
|
|
1005
|
+
const codexEnv = agentResume.codexHome ? { ...process.env, CODEX_HOME: agentResume.codexHome } : undefined;
|
|
1006
|
+
await postAgentParked(limit, rt, await limitResetAt(rt, { threadId: agentResume.threadNow(), env: codexEnv }));
|
|
983
1007
|
}
|
|
984
1008
|
if (job.kind === 'merge_resolve') {
|
|
985
1009
|
const resolution = () => repositoryRun
|
|
@@ -35,6 +35,7 @@ import { withoutTurnDeclaration } from './turnDeclaration.mjs';
|
|
|
35
35
|
import { ANSWER_DELIVERED_USES, researchUseOf, withResearchHeader } from './researchUse.mjs';
|
|
36
36
|
import { measuredModelExports } from './intermediateArtifacts.mjs';
|
|
37
37
|
import { gapsMarkdown } from './turnGaps.mjs';
|
|
38
|
+
import { blockedAnswer } from './agentAsk.mjs';
|
|
38
39
|
|
|
39
40
|
/** Materialization runs only for a declared delivery; settlement remains the
|
|
40
41
|
* sole parser and the sole authority deciding whether it really delivered. */
|
|
@@ -130,6 +131,16 @@ export function agentTurnSettlement({ turnId, job, kind, out, lastAnswer, usage,
|
|
|
130
131
|
*/
|
|
131
132
|
// Existing document deliveries default to the Markdown proof. Only an
|
|
132
133
|
// explicit temporary-use declaration can deliver through the stored answer.
|
|
134
|
+
const required = res.gaps?.filter(g => g.disposition === 'required') ?? [];
|
|
135
|
+
if (res.outcome === 'delivered' && required.length && job.gapContinuationAccepted !== true) {
|
|
136
|
+
return report({ turnId, outcome: 'question',
|
|
137
|
+
answer: blockedAnswer('Required work remains. Should I finish it in this agent before delivery?', {
|
|
138
|
+
context: required.map(g => `${g.item}${g.why ? `: ${g.why}` : ''}`).join('\n\n'),
|
|
139
|
+
options: ['Finish the required work', 'Explain the remaining scope'],
|
|
140
|
+
}, envScrub),
|
|
141
|
+
...(commits.length ? { commits } : {}), ...(usage ? { usage } : {}),
|
|
142
|
+
...(res.progress ? { progress: envScrub(res.progress) } : {}), branch, worktree: wt });
|
|
143
|
+
}
|
|
133
144
|
const briefUse = researchUseOf(job.task?.brief);
|
|
134
145
|
const researchUse = kind.posture === 'research' ? briefUse ?? res.researchUse : null;
|
|
135
146
|
const temporaryResearch = ANSWER_DELIVERED_USES.includes(researchUse);
|
|
@@ -148,7 +148,7 @@ export function createAgentTurnReports({ REJECT_RETRY_MS, baseDir = null }) {
|
|
|
148
148
|
}
|
|
149
149
|
};
|
|
150
150
|
|
|
151
|
-
const postAgentParked = async (reason, runtime) => {
|
|
151
|
+
const postAgentParked = async (reason, runtime, resetsAt = null) => {
|
|
152
152
|
try {
|
|
153
153
|
await fetch(AGENT_PARKED_URL, {
|
|
154
154
|
method: 'POST',
|
|
@@ -156,7 +156,10 @@ export function createAgentTurnReports({ REJECT_RETRY_MS, baseDir = null }) {
|
|
|
156
156
|
signal: AbortSignal.timeout(15_000),
|
|
157
157
|
// `runtime` scopes the park to that CLI's agents; a server that does
|
|
158
158
|
// not know the key parks every agent, which is what it always did.
|
|
159
|
-
|
|
159
|
+
// `resetsAt` is the CLI's measured reset (`limitResetAt`), sent only
|
|
160
|
+
// when measured: the server retries then, and an older server ignores
|
|
161
|
+
// the key and keeps its cooldown. A report, so no floor.
|
|
162
|
+
body: JSON.stringify({ reason, ...(runtime ? { runtime } : {}), ...(resetsAt ? { resetsAt } : {}) }),
|
|
160
163
|
});
|
|
161
164
|
} catch {
|
|
162
165
|
/* the next turn will hit the same limit and try again */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "flowviant",
|
|
3
|
-
"version": "0.125.
|
|
3
|
+
"version": "0.125.31",
|
|
4
4
|
"description": "Run your own coding CLIs as build agents for Flowviant \u2014 Claude Code, Codex or Antigravity, on your own credentials. Holds your sessions, keeps a worktree per tab, and ships branches on your word.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|