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
|
@@ -21,10 +21,12 @@
|
|
|
21
21
|
*/
|
|
22
22
|
import { existsSync } from 'node:fs';
|
|
23
23
|
import { isSafePathSegment } from './git.mjs';
|
|
24
|
+
import { AGENT_PLACE_PREFIX } from './workPlaces.mjs';
|
|
24
25
|
import { c, note } from './ui.mjs';
|
|
25
26
|
import { createAgentTurnReports } from './workAgentTurnReports.mjs';
|
|
26
27
|
import { createAgentTurnExecution } from './workAgentTurnExecution.mjs';
|
|
27
|
-
import { failedTurnSettlement } from './workAgentTurnOutcome.mjs';
|
|
28
|
+
import { failedTurnSettlement, interruptedTurnSettlement } from './workAgentTurnOutcome.mjs';
|
|
29
|
+
import { turnCommits } from './turnReceipts.mjs';
|
|
28
30
|
import { killGroup } from './processGroup.mjs';
|
|
29
31
|
import { createDeliveryReporter } from './workDelivery.mjs';
|
|
30
32
|
import { createAgentBriefRunner } from './workAgentBriefRunner.mjs';
|
|
@@ -54,6 +56,8 @@ export function createWorkAgentTurns({
|
|
|
54
56
|
/** The turn's files, fetched into its worktree (workAttachments.mjs, 0.112.0). */
|
|
55
57
|
fetchAgentFiles,
|
|
56
58
|
makeBrief = createAgentBriefRunner({ admit, workChildren }),
|
|
59
|
+
/** The run's live-message relay (workLiveMessages.mjs); a test's stand-in. */
|
|
60
|
+
makeLiveMessages,
|
|
57
61
|
}) {
|
|
58
62
|
const reportDelivery = createDeliveryReporter();
|
|
59
63
|
const {
|
|
@@ -111,7 +115,7 @@ export function createWorkAgentTurns({
|
|
|
111
115
|
* read before the stop's settle landed. Taken once, for a little while. */
|
|
112
116
|
const stoppedFor = new Map(); // agentId -> when
|
|
113
117
|
|
|
114
|
-
const { runAgentTurnInPlace
|
|
118
|
+
const { runAgentTurnInPlace } = createAgentTurnExecution({
|
|
115
119
|
admit,
|
|
116
120
|
agentRepositories,
|
|
117
121
|
makeBrief,
|
|
@@ -133,6 +137,7 @@ export function createWorkAgentTurns({
|
|
|
133
137
|
postAgentActivity,
|
|
134
138
|
postAgentParked,
|
|
135
139
|
fetchAgentFiles,
|
|
140
|
+
makeLiveMessages,
|
|
136
141
|
});
|
|
137
142
|
|
|
138
143
|
/** `releaseSlot` hands back the admission reservation the caller took on this
|
|
@@ -183,33 +188,42 @@ export function createWorkAgentTurns({
|
|
|
183
188
|
* branch, the spend, the branch and worktree. Through the same held-body
|
|
184
189
|
* wire, so a failed POST is re-sent on the next offer and the CLI is not
|
|
185
190
|
* run again.
|
|
191
|
+
*
|
|
192
|
+
* The catch carries what the run's own settles carry, the live receipts
|
|
193
|
+
* included (2026-10-18): a note the CLI already took is folded into this
|
|
194
|
+
* run only when a settle names it, and the relay's own flush is bounded,
|
|
195
|
+
* so a catch that posted without them left the note pending — offered
|
|
196
|
+
* later as its own turn, a second paid run.
|
|
186
197
|
*/
|
|
187
198
|
const measured = { wt: null, before: null, branch: null, usage: null, model: null };
|
|
188
199
|
let settled = false;
|
|
200
|
+
const withLiveReceipts = (body) => {
|
|
201
|
+
const liveMessages = agentRuns.get(turnId)?.live?.receipts();
|
|
202
|
+
return { ...body, ...(liveMessages?.ids.length ? { liveMessages } : {}) };
|
|
203
|
+
};
|
|
189
204
|
const settleOnce = (body) => {
|
|
190
205
|
settled = true;
|
|
191
|
-
|
|
192
|
-
return postAgentTurn({ ...body, ...(liveMessages?.ids.length ? { liveMessages } : {}) });
|
|
206
|
+
return postAgentTurn(withLiveReceipts(body));
|
|
193
207
|
};
|
|
194
208
|
|
|
195
209
|
// A WRITER on the agent's own place — see the lane header. Only `a-<id>`
|
|
196
210
|
// places: those are agents' by construction, and anything else here would
|
|
197
211
|
// be a tab's directory, where a turn is a reader by the product's own law.
|
|
198
212
|
const run = agentRuns.get(turnId) ?? { place, interrupted: false };
|
|
199
|
-
await inPlace(place, place.startsWith(
|
|
213
|
+
await inPlace(place, place.startsWith(AGENT_PLACE_PREFIX), async () => {
|
|
200
214
|
run.waitingReason = null;
|
|
201
215
|
try {
|
|
202
216
|
await runAgentTurnInPlace(job, { turnId, agentId, place, releaseSlot, postAgentTurn: settleOnce, measured, run });
|
|
203
217
|
if (run.interrupted && measured.ranCli) stoppedFor.set(agentId, Date.now());
|
|
204
218
|
} catch (error) {
|
|
205
219
|
if (settled) return;
|
|
206
|
-
const commits =
|
|
220
|
+
const commits = turnCommits({ repositoryReceipts: measured.repositoryReceipts, wt: measured.wt, before: measured.before, baseRef });
|
|
207
221
|
if (run.interrupted) {
|
|
208
|
-
await postAgentTurn({ turnId,
|
|
222
|
+
await postAgentTurn(withLiveReceipts(interruptedTurnSettlement({ turnId, ran: measured.ranCli === true, commits, usage: measured.usage, model: measured.model, branch: measured.branch, wt: measured.wt })));
|
|
209
223
|
return;
|
|
210
224
|
}
|
|
211
225
|
await postAgentTurn(
|
|
212
|
-
failedTurnSettlement({
|
|
226
|
+
withLiveReceipts(failedTurnSettlement({
|
|
213
227
|
turnId,
|
|
214
228
|
error,
|
|
215
229
|
commits,
|
|
@@ -217,7 +231,7 @@ export function createWorkAgentTurns({
|
|
|
217
231
|
model: measured.model,
|
|
218
232
|
branch: measured.branch,
|
|
219
233
|
wt: measured.wt,
|
|
220
|
-
})
|
|
234
|
+
}))
|
|
221
235
|
);
|
|
222
236
|
}
|
|
223
237
|
}, () => {
|
|
@@ -299,7 +313,7 @@ export function createWorkAgentTurns({
|
|
|
299
313
|
const place = String(job.placeId || '');
|
|
300
314
|
const wt = typeof held.body.worktree === 'string' ? held.body.worktree : null;
|
|
301
315
|
if (reply?.review === true && isSafePathSegment(place) && wt && existsSync(wt)) {
|
|
302
|
-
await inPlace(place, place.startsWith(
|
|
316
|
+
await inPlace(place, place.startsWith(AGENT_PLACE_PREFIX), () =>
|
|
303
317
|
runReviewEntry(String(job.agentId || ''), wt, job.agentName)
|
|
304
318
|
);
|
|
305
319
|
}
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
* number of times; the stored files stay listed, which is the truth.
|
|
16
16
|
*/
|
|
17
17
|
import { existsSync } from 'node:fs';
|
|
18
|
+
import { agentIdFromPlace } from './workPlaces.mjs';
|
|
18
19
|
|
|
19
20
|
/** Attempts per agent per process, and the wait between two of them. */
|
|
20
21
|
const MAX_TRIES = 4;
|
|
@@ -37,7 +38,7 @@ export function createWorkArtifactReconcile({
|
|
|
37
38
|
const reconcileOne = async (place) => {
|
|
38
39
|
const wt = placeDir(place);
|
|
39
40
|
if (!wt || !existsSync(wt)) return;
|
|
40
|
-
const agentId = place
|
|
41
|
+
const agentId = agentIdFromPlace(place);
|
|
41
42
|
try {
|
|
42
43
|
await inPlace(place, true, () => artifacts.reconcile({ placeDir: wt, before: beforeArtifacts(wt), agentId }));
|
|
43
44
|
done.add(place);
|
|
@@ -58,7 +59,7 @@ export function createWorkArtifactReconcile({
|
|
|
58
59
|
for (const id of done) if (!live.has(id)) done.delete(id);
|
|
59
60
|
for (const id of tries.keys()) if (!live.has(id)) tries.delete(id);
|
|
60
61
|
const due = activeIds.filter((place) => {
|
|
61
|
-
if (
|
|
62
|
+
if (agentIdFromPlace(place) === null) return false;
|
|
62
63
|
if (done.has(place) || placeLocks.has(place)) return false; // a turn or a merge is there
|
|
63
64
|
const t = tries.get(place);
|
|
64
65
|
return !t || (t.n < MAX_TRIES && now() - t.at >= RETRY_MS);
|
|
@@ -1,17 +1,19 @@
|
|
|
1
|
+
/** The daemon holds every selected place while one personal CLI integrates it. */
|
|
1
2
|
import { trackedRepositorySetup } from './repositorySetup.mjs';
|
|
2
3
|
import { git } from './git.mjs';
|
|
3
4
|
import { knowledgeDirFor } from './knowledgeFiles.mjs';
|
|
4
|
-
|
|
5
|
-
import {
|
|
5
|
+
import { integrateMergeBatch } from './mergeBatch.mjs';
|
|
6
|
+
import { mergeBatchPrompt, MERGE_BATCH_SYSTEM } from './repositoryPrompts.mjs';
|
|
6
7
|
import { runTurn } from './runTurn.mjs';
|
|
7
8
|
import { runCappedTurn } from './workCappedTurn.mjs';
|
|
8
|
-
import { candidateObjects } from './
|
|
9
|
+
import { candidateObjects } from './modelJson.mjs';
|
|
9
10
|
import { pickRuntimeFor } from './runtimeDetection.mjs';
|
|
10
11
|
import { fleetRequest } from './fleetPost.mjs';
|
|
11
12
|
import { fleetEndpoint } from './fleetWire.mjs';
|
|
12
13
|
import { FLEET_URL, DAEMON_INSTANCE } from './config.mjs';
|
|
13
14
|
import { worktreePath } from './workPlaces.mjs';
|
|
14
15
|
import { scrub } from './uplinkScrub.mjs';
|
|
16
|
+
import { relayedText } from './prWorkflow.mjs';
|
|
15
17
|
import { isSafePathSegment, stripUrlUserinfo } from './git.mjs';
|
|
16
18
|
import { killGroup } from './processGroup.mjs';
|
|
17
19
|
|
|
@@ -63,7 +65,7 @@ export function createWorkMergeBatch({ repositoryPlaces, repoRoot, baseDir, inPl
|
|
|
63
65
|
const result = await runCappedTurn({ runTurn: runCli, workChildren, releaseSlot: release,
|
|
64
66
|
transcriptCwd: context.repositories[0].integrationWorktree, capMs: 20 * 60_000, killChild: killGroup,
|
|
65
67
|
onSpawn: (ch) => { child = ch; groupKillChildren?.add(ch); },
|
|
66
|
-
turnArgs: { prompt: mergeBatchPrompt(context), system:
|
|
68
|
+
turnArgs: { prompt: mergeBatchPrompt(context), system: MERGE_BATCH_SYSTEM,
|
|
67
69
|
runtime, profile: 'build', knowledgeDir: knowledgeDirFor(repoRoot), model: leader.model || undefined, effort: leader.effort || undefined,
|
|
68
70
|
cwd: context.repositories[0].integrationWorktree, writableDirs: context.repositories.filter((r) => !r.alreadyLanded).map((r) => r.integrationWorktree),
|
|
69
71
|
answerFromResult: true, label: '[batch merge]' } });
|
|
@@ -88,7 +90,7 @@ export function createWorkMergeBatch({ repositoryPlaces, repoRoot, baseDir, inPl
|
|
|
88
90
|
// After a successful push/report attempt, leave the lease for replay: a transport
|
|
89
91
|
// failure says nothing about whether the server committed the final receipt.
|
|
90
92
|
if (claimed && !reporting) {
|
|
91
|
-
const detail =
|
|
93
|
+
const detail = relayedText([e?.stdout, e?.stderr, e?.message].filter(Boolean).join('\n')).slice(0, 2000);
|
|
92
94
|
await request('agent-merge-batch-done', { batchId: spec.id, ok: false, detail }).catch(() => {});
|
|
93
95
|
}
|
|
94
96
|
} finally {
|
|
@@ -20,7 +20,7 @@ import { RESUME_MARKERS } from './conversationIds.mjs';
|
|
|
20
20
|
import { worktreeDiff } from './worktreeDiff.mjs';
|
|
21
21
|
import { myPubB64 } from './boxIdentity.mjs';
|
|
22
22
|
import { fleetEndpoint } from './fleetWire.mjs';
|
|
23
|
-
import { isValidPlace } from './workPlaces.mjs';
|
|
23
|
+
import { isValidPlace, AGENT_PLACE_PREFIX, agentIdFromPlace } from './workPlaces.mjs';
|
|
24
24
|
|
|
25
25
|
export function createWorkPlaceReports({
|
|
26
26
|
repositoryPlaces,
|
|
@@ -176,7 +176,7 @@ export function createWorkPlaceReports({
|
|
|
176
176
|
* TABS GET NOTHING. A tab's place is shared by design and its work is a
|
|
177
177
|
* human's own directory; attributing one would be a fact with no reader.
|
|
178
178
|
*/
|
|
179
|
-
const pub = sessionId
|
|
179
|
+
const pub = agentIdFromPlace(sessionId) !== null ? myPubB64() : null;
|
|
180
180
|
/**
|
|
181
181
|
* …AND WHAT THIS MACHINE PUSHED OF IT (0.86.0).
|
|
182
182
|
*
|
|
@@ -193,7 +193,7 @@ export function createWorkPlaceReports({
|
|
|
193
193
|
* Mutually exclusive without a rule of its own, because the state it reads
|
|
194
194
|
* holds a sha or an error and never both.
|
|
195
195
|
*/
|
|
196
|
-
const pushed = sessionId.startsWith(
|
|
196
|
+
const pushed = sessionId.startsWith(AGENT_PLACE_PREFIX) ? agentPublished.get(sessionId) : null;
|
|
197
197
|
return {
|
|
198
198
|
sessionId,
|
|
199
199
|
...d,
|
package/bin/lib/workPlaces.mjs
CHANGED
|
@@ -29,6 +29,20 @@ export const REPO_PLACE = 'repo';
|
|
|
29
29
|
* audit F163). */
|
|
30
30
|
export const PERSON_PLACE_PREFIX = 'u-';
|
|
31
31
|
|
|
32
|
+
/** An AGENT's place is `a-<agentId>` — the shared AGENT_PLACE_PREFIX /
|
|
33
|
+
* agentPlaceId; a wire value held by the release parity gate (2026-10-18,
|
|
34
|
+
* the SOLID pass, as F163 holds the other two). Its writer lock, its envpub
|
|
35
|
+
* and the artifact reconcile all key on it. */
|
|
36
|
+
export const AGENT_PLACE_PREFIX = 'a-';
|
|
37
|
+
|
|
38
|
+
/** The agent a place names, or null for every other place shape — the shared
|
|
39
|
+
* `agentIdFromPlace`, mirrored: the prefix alone names nothing. */
|
|
40
|
+
export function agentIdFromPlace(place) {
|
|
41
|
+
return typeof place === 'string' && place.startsWith(AGENT_PLACE_PREFIX) && place.length > AGENT_PLACE_PREFIX.length
|
|
42
|
+
? place.slice(AGENT_PLACE_PREFIX.length)
|
|
43
|
+
: null;
|
|
44
|
+
}
|
|
45
|
+
|
|
32
46
|
/**
|
|
33
47
|
* A PLACE THIS MACHINE WILL RESOLVE: the checkout, or a safe path segment.
|
|
34
48
|
*
|
|
@@ -8,17 +8,14 @@
|
|
|
8
8
|
* THE ID SHAPES MOVED to a leaf (audit 2026-10-10, F157): conversationIds.mjs
|
|
9
9
|
* owns every CLI's conversation-id guard and marker name, because the
|
|
10
10
|
* machine readers below the lanes (runtimeLimits, the session scanners) need
|
|
11
|
-
* them too and a lane module is not theirs to import.
|
|
12
|
-
* agent lane imports from here are re-exported unchanged.
|
|
11
|
+
* them too and a lane module is not theirs to import.
|
|
13
12
|
*/
|
|
14
13
|
import { existsSync, readFileSync, realpathSync, writeFileSync } from 'node:fs';
|
|
15
14
|
import { join } from 'node:path';
|
|
16
15
|
import { homedir } from 'node:os';
|
|
17
16
|
import { canRun, RUNTIMES, runtimeById, runtimeLabel } from './runtimes.mjs';
|
|
18
17
|
import { detectRuntimes } from './runtimeDetection.mjs';
|
|
19
|
-
import { AGY_CONV_RE
|
|
20
|
-
|
|
21
|
-
export { AGY_CONV_RE, CODEX_THREAD_RE };
|
|
18
|
+
import { AGY_CONV_RE } from './conversationIds.mjs';
|
|
22
19
|
|
|
23
20
|
export function createWorkSessionRuntime({ sessionMetaPath }) {
|
|
24
21
|
/**
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* what the turn IS, plain, plan, capture or build, with its prompt pair
|
|
17
17
|
* (workTurnShape.mjs, audit 2026-10-10 F159).
|
|
18
18
|
*/
|
|
19
|
-
import { existsSync,
|
|
19
|
+
import { existsSync, realpathSync, rmSync, statSync, writeFileSync } from 'node:fs';
|
|
20
20
|
import { git } from './git.mjs';
|
|
21
21
|
import { measureRepositoryBaseline } from './repositoryBaseline.mjs';
|
|
22
22
|
import { REPOSITORY_BASELINE_PARAGRAPH } from './prompts.mjs';
|
|
@@ -38,6 +38,7 @@ import { killGroup } from './processGroup.mjs';
|
|
|
38
38
|
import { printable } from './printable.mjs';
|
|
39
39
|
import { brainFor } from './workBrain.mjs';
|
|
40
40
|
import { AGY_CONV_RE, CLAUDE_SESSION_RE, CODEX_THREAD_RE, RESUME_MARKERS } from './conversationIds.mjs';
|
|
41
|
+
import { pinId, readPinnedId } from './resumeMarker.mjs';
|
|
41
42
|
import { sessionTurnKickoff, sessionTurnShape, sessionTurnSystem } from './workTurnShape.mjs';
|
|
42
43
|
import { createToolLog } from './workToolLog.mjs';
|
|
43
44
|
import { createCommandAudit } from './workCommandAudit.mjs';
|
|
@@ -584,48 +585,16 @@ export function createWorkSessionTurns({
|
|
|
584
585
|
* directory belongs to this tab alone, which is the one case it was
|
|
585
586
|
* ever right for.
|
|
586
587
|
*/
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
if (convMarker && existsSync(convMarker)) {
|
|
591
|
-
try {
|
|
592
|
-
const v = readFileSync(convMarker, 'utf8').trim();
|
|
593
|
-
if (CLAUDE_SESSION_RE.test(v)) claudeResumeId = v;
|
|
594
|
-
} catch {
|
|
595
|
-
/* unreadable marker — run fresh */
|
|
596
|
-
}
|
|
597
|
-
}
|
|
598
|
-
}
|
|
599
|
-
let codexResumeId = null;
|
|
600
|
-
if (rt.id === 'codex') {
|
|
601
|
-
const threadMarker = sessionMetaPath(dir.wt, RESUME_MARKERS.codex, job.sessionId);
|
|
602
|
-
if (threadMarker && existsSync(threadMarker)) {
|
|
603
|
-
try {
|
|
604
|
-
const v = readFileSync(threadMarker, 'utf8').trim();
|
|
605
|
-
if (CODEX_THREAD_RE.test(v)) codexResumeId = v;
|
|
606
|
-
} catch {
|
|
607
|
-
/* unreadable marker — run fresh */
|
|
608
|
-
}
|
|
609
|
-
}
|
|
610
|
-
}
|
|
588
|
+
// An unreadable or misshapen marker is unknown: run fresh (resumeMarker.mjs).
|
|
589
|
+
const claudeResumeId = rt.id === 'claude' ? readPinnedId(sessionMetaPath(dir.wt, RESUME_MARKERS.claude, job.sessionId), CLAUDE_SESSION_RE) : null;
|
|
590
|
+
const codexResumeId = rt.id === 'codex' ? readPinnedId(sessionMetaPath(dir.wt, RESUME_MARKERS.codex, job.sessionId), CODEX_THREAD_RE) : null;
|
|
611
591
|
// AGY RESUMES BY CONVERSATION ID, learned once and pinned beside the
|
|
612
592
|
// runtime marker: an adopted tab knows it from the adopt hint; a new
|
|
613
593
|
// tab learns it from agy's own cwd registry after its first turn.
|
|
614
594
|
// The marker beats `--continue` because it is the tab's OWN identity
|
|
615
595
|
// — the registry maps a cwd to whatever ran there LAST, and a
|
|
616
596
|
// dispatch sharing the machine could overwrite that between turns.
|
|
617
|
-
|
|
618
|
-
if (rt.id === 'antigravity' && !adopting) {
|
|
619
|
-
const convMarker = sessionMetaPath(dir.wt, RESUME_MARKERS.antigravity, job.sessionId);
|
|
620
|
-
if (convMarker && existsSync(convMarker)) {
|
|
621
|
-
try {
|
|
622
|
-
const v = readFileSync(convMarker, 'utf8').trim();
|
|
623
|
-
if (AGY_CONV_RE.test(v)) agyConvId = v;
|
|
624
|
-
} catch {
|
|
625
|
-
/* unreadable marker — run fresh */
|
|
626
|
-
}
|
|
627
|
-
}
|
|
628
|
-
}
|
|
597
|
+
const agyConvId = rt.id === 'antigravity' && !adopting ? readPinnedId(sessionMetaPath(dir.wt, RESUME_MARKERS.antigravity, job.sessionId), AGY_CONV_RE) : null;
|
|
629
598
|
// Resume iff a conversation is known to live in THIS directory. For
|
|
630
599
|
// Claude that proof is the server's sessionRef — only ever a path
|
|
631
600
|
// some turn actually SPOKE from (see the settle below), and it must
|
|
@@ -716,7 +685,7 @@ export function createWorkSessionTurns({
|
|
|
716
685
|
.filter(Boolean)
|
|
717
686
|
.join('\n\n');
|
|
718
687
|
const turnArgs = {
|
|
719
|
-
...(
|
|
688
|
+
...(shape.liveInput ? { liveMessages: (run.live = makeLiveMessages({
|
|
720
689
|
scope: 'session', turnId: job.id, attempt: job.attempt, cwd: dir.wt, sessionMetaPath,
|
|
721
690
|
interrupted: () => run.interrupted,
|
|
722
691
|
format: async (followup) => {
|
|
@@ -815,10 +784,7 @@ export function createWorkSessionTurns({
|
|
|
815
784
|
// same conversation and recover its native message UUIDs.
|
|
816
785
|
if (typeof i.sessionId === 'string' && i.sessionId.trim()) {
|
|
817
786
|
seenClaudeSession = i.sessionId.trim();
|
|
818
|
-
if (CLAUDE_SESSION_RE.test(seenClaudeSession))
|
|
819
|
-
const marker = sessionMetaPath(dir.wt, RESUME_MARKERS.claude, job.sessionId);
|
|
820
|
-
if (marker) try { writeFileSync(marker, seenClaudeSession); } catch { /* best effort */ }
|
|
821
|
-
}
|
|
787
|
+
if (CLAUDE_SESSION_RE.test(seenClaudeSession)) pinId(sessionMetaPath(dir.wt, RESUME_MARKERS.claude, job.sessionId), CLAUDE_SESSION_RE, seenClaudeSession);
|
|
822
788
|
}
|
|
823
789
|
},
|
|
824
790
|
cwd: dir.wt,
|
|
@@ -838,7 +804,7 @@ export function createWorkSessionTurns({
|
|
|
838
804
|
},
|
|
839
805
|
onSpawn: (ch) => {
|
|
840
806
|
if (!ch) return;
|
|
841
|
-
if (
|
|
807
|
+
if (!shape.liveInput) reportDelivery('session', job, 'taken');
|
|
842
808
|
spawned.push(ch);
|
|
843
809
|
run.spawned.push(ch);
|
|
844
810
|
// Asked to stop between the check and the spawn.
|
|
@@ -930,16 +896,7 @@ export function createWorkSessionTurns({
|
|
|
930
896
|
// before it ever touches disk — it later rides in argv as
|
|
931
897
|
// `resume <id>` — and best-effort, like the runtime pin: an
|
|
932
898
|
// unwritable marker just means the tab runs fresh next turn.
|
|
933
|
-
if (rt.id === 'codex' && seenThreadId && CODEX_THREAD_RE.test(seenThreadId))
|
|
934
|
-
const threadMarker = sessionMetaPath(dir.wt, RESUME_MARKERS.codex, job.sessionId);
|
|
935
|
-
if (threadMarker) {
|
|
936
|
-
try {
|
|
937
|
-
writeFileSync(threadMarker, seenThreadId);
|
|
938
|
-
} catch {
|
|
939
|
-
/* best-effort */
|
|
940
|
-
}
|
|
941
|
-
}
|
|
942
|
-
}
|
|
899
|
+
if (rt.id === 'codex' && seenThreadId && CODEX_THREAD_RE.test(seenThreadId)) pinId(sessionMetaPath(dir.wt, RESUME_MARKERS.codex, job.sessionId), CODEX_THREAD_RE, seenThreadId);
|
|
943
900
|
/**
|
|
944
901
|
* A PERSON INTERRUPTED IT. Whatever the CLI printed was cut off, so
|
|
945
902
|
* it is not an answer: the turn settles `interrupted`, with what it
|
|
@@ -984,16 +941,8 @@ export function createWorkSessionTurns({
|
|
|
984
941
|
// the registry is never trusted again.
|
|
985
942
|
if (rt.id === 'antigravity' && answer.length > 0) {
|
|
986
943
|
const convMarker = sessionMetaPath(dir.wt, RESUME_MARKERS.antigravity, job.sessionId);
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
if (learned && AGY_CONV_RE.test(learned)) {
|
|
990
|
-
try {
|
|
991
|
-
writeFileSync(convMarker, learned);
|
|
992
|
-
} catch {
|
|
993
|
-
/* best-effort — an unpinned tab resumes via --continue's cwd key */
|
|
994
|
-
}
|
|
995
|
-
}
|
|
996
|
-
}
|
|
944
|
+
// Best-effort — an unpinned tab resumes via --continue's cwd key.
|
|
945
|
+
if (convMarker && !existsSync(convMarker)) pinId(convMarker, AGY_CONV_RE, adopting ? job.adopt.id : agyRegistryLookup(dir.wt));
|
|
997
946
|
}
|
|
998
947
|
/**
|
|
999
948
|
* NO WORDS, SAID AS MEASURED (2026-09-29). Claude's init event means
|
package/bin/lib/workShip.mjs
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
/** Session branch shipping and settlement. */
|
|
2
2
|
import { existsSync } from 'node:fs';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
|
-
import { git, baseBranchName, isSafePathSegment, sessionBranch
|
|
4
|
+
import { git, baseBranchName, isSafePathSegment, sessionBranch } from './git.mjs';
|
|
5
5
|
import { mergeOutward as shipMergeOutward } from './shipMerge.mjs';
|
|
6
6
|
import { gitMergeIn } from './gitMergeIdentity.mjs';
|
|
7
7
|
import { c, note, ok, warn } from './ui.mjs';
|
|
8
8
|
import { scrub as envScrub } from './uplinkScrub.mjs';
|
|
9
|
+
import { relayedText } from './prWorkflow.mjs';
|
|
9
10
|
import { REPO_PLACE } from './workPlaces.mjs';
|
|
10
11
|
import { placeTurnLockHolder } from './turnLock.mjs';
|
|
11
12
|
import { printable } from './printable.mjs';
|
|
@@ -272,15 +273,10 @@ export function createWorkShipper({
|
|
|
272
273
|
'conflicts with main — the tab is closed, so open a new session from this branch to resolve them, then ship again',
|
|
273
274
|
});
|
|
274
275
|
} else {
|
|
275
|
-
// Userinfo stripped as well as scrubbed (2026-10-10, F153)
|
|
276
|
-
// outward merge pushes to `origin`, and a push
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
String(detail)
|
|
280
|
-
.split('\n')
|
|
281
|
-
.find((l) => l.trim()) ?? 'git merge failed'
|
|
282
|
-
)
|
|
283
|
-
);
|
|
276
|
+
// Userinfo stripped as well as scrubbed (2026-10-10, F153), by
|
|
277
|
+
// relayedText: the outward merge pushes to `origin`, and a push
|
|
278
|
+
// names the remote.
|
|
279
|
+
const line = relayedText(String(detail).split('\n').find((l) => l.trim()) ?? 'git merge failed');
|
|
284
280
|
await done({ ok: false, error: `the merge failed: ${line.slice(0, 300)}` });
|
|
285
281
|
}
|
|
286
282
|
return;
|
|
@@ -331,13 +327,7 @@ export function createWorkShipper({
|
|
|
331
327
|
} else {
|
|
332
328
|
// An honest error beats a fabricated conflict — the human can
|
|
333
329
|
// only fix what they are told about.
|
|
334
|
-
const line =
|
|
335
|
-
stripUrlUserinfo(
|
|
336
|
-
String(detail)
|
|
337
|
-
.split('\n')
|
|
338
|
-
.find((l) => l.trim()) ?? 'git merge failed'
|
|
339
|
-
)
|
|
340
|
-
);
|
|
330
|
+
const line = relayedText(String(detail).split('\n').find((l) => l.trim()) ?? 'git merge failed');
|
|
341
331
|
await done({ ok: false, error: `the merge failed: ${line.slice(0, 300)}` });
|
|
342
332
|
}
|
|
343
333
|
return;
|
|
@@ -368,7 +358,7 @@ export function createWorkShipper({
|
|
|
368
358
|
warn(`ship failed: ${e?.message ?? e}`);
|
|
369
359
|
await done({
|
|
370
360
|
ok: false,
|
|
371
|
-
error:
|
|
361
|
+
error: relayedText(e?.message ?? 'the merge failed').slice(0, 500),
|
|
372
362
|
});
|
|
373
363
|
} finally {
|
|
374
364
|
if (!settled && !deferred) {
|
|
@@ -15,8 +15,8 @@ import { discoveryGuide } from './discoveryContract.mjs';
|
|
|
15
15
|
* other answer off it; the prompt pair is composed here too.
|
|
16
16
|
*
|
|
17
17
|
* The runtime facts come off the row (runtime{Claude,Codex,Antigravity}.mjs):
|
|
18
|
-
* `tab` ('plain' runs no MCP), `planTurns` (the CLI can spell plan mode)
|
|
19
|
-
* `adopt` ('fork' | 'move' | null). The LIFECYCLE stays the lane's — its
|
|
18
|
+
* `tab` ('plain' runs no MCP), `planTurns` (the CLI can spell plan mode),
|
|
19
|
+
* `adopt` ('fork' | 'move' | null) and `liveInput` (follow-ups on stdin). The LIFECYCLE stays the lane's — its
|
|
20
20
|
* refusal order is the contract — and only this composition moved.
|
|
21
21
|
*/
|
|
22
22
|
import { runtimeById } from './runtimes.mjs';
|
|
@@ -55,7 +55,7 @@ export const PLAN_TURN_SENTENCE =
|
|
|
55
55
|
* verdict that this first turn adopts a terminal session.
|
|
56
56
|
*
|
|
57
57
|
* Returns `{ refusal?, plain, plan, capture, kickoff, systemBase,
|
|
58
|
-
* planSentence, artifacts, profile, mountMcp, display }`:
|
|
58
|
+
* planSentence, artifacts, profile, mountMcp, display, liveInput }`:
|
|
59
59
|
* refusal — the sentence the turn settles in, unrun, when it has one;
|
|
60
60
|
* kickoff /
|
|
61
61
|
* systemBase — which contract the prompt pair runs: 'plain' | 'capture' | 'work';
|
|
@@ -65,7 +65,8 @@ export const PLAN_TURN_SENTENCE =
|
|
|
65
65
|
* one, as the lane reads it at spawn);
|
|
66
66
|
* profile — the permission profile: 'build' | 'plan' | 'plan-mode';
|
|
67
67
|
* mountMcp — the session's MCP config is mounted;
|
|
68
|
-
* display — the CLI keeps its environment's display
|
|
68
|
+
* display — the CLI keeps its environment's display;
|
|
69
|
+
* liveInput — follow-ups reach the running CLI on stdin.
|
|
69
70
|
*/
|
|
70
71
|
export function sessionTurnShape({ runtime, adopting = false, job = null }) {
|
|
71
72
|
const row = runtimeById(runtime);
|
|
@@ -137,6 +138,7 @@ export function sessionTurnShape({ runtime, adopting = false, job = null }) {
|
|
|
137
138
|
// only reads and stages cards, and the person talking to it may be on a
|
|
138
139
|
// phone (noWindowEnv.mjs).
|
|
139
140
|
display: !isCapture,
|
|
141
|
+
liveInput: row?.liveInput === true,
|
|
140
142
|
};
|
|
141
143
|
}
|
|
142
144
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "flowviant",
|
|
3
|
-
"version": "0.125.
|
|
3
|
+
"version": "0.125.6",
|
|
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": {
|