flowviant 0.125.0 → 0.125.1
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/runTurn.mjs +23 -5
- package/bin/lib/runtimeCodex.mjs +15 -10
- package/bin/lib/turnDiagnostics.mjs +13 -5
- package/bin/lib/work.mjs +2 -1
- package/package.json +1 -1
package/bin/lib/runTurn.mjs
CHANGED
|
@@ -84,7 +84,7 @@ function runPlainTurn(opts) {
|
|
|
84
84
|
* Both answers stay pending instead, as `process.exit` left them.
|
|
85
85
|
*/
|
|
86
86
|
if (workLanesStopped()) return never();
|
|
87
|
-
return new Promise((resolveTurn) => {
|
|
87
|
+
return new Promise((resolveTurn, rejectTurn) => {
|
|
88
88
|
const resolve = (v) => {
|
|
89
89
|
if (!workLanesStopped()) resolveTurn(v);
|
|
90
90
|
};
|
|
@@ -167,8 +167,9 @@ function runPlainTurn(opts) {
|
|
|
167
167
|
// up by the SAME name (`claudePermFor`), so the two cannot disagree about
|
|
168
168
|
// which posture a turn is running under. There is no precedence chain left
|
|
169
169
|
// to get wrong: the caller named one profile (turnProfile.mjs).
|
|
170
|
+
const stdinText = rt.stdin?.({ prompt, system, profile: turnProfile.adapterProfile }) ?? null;
|
|
170
171
|
const args = rt.args({
|
|
171
|
-
streamInput: rt.id === 'claude' && Boolean(opts.liveMessages),
|
|
172
|
+
streamInput: stdinText !== null || (rt.id === 'claude' && Boolean(opts.liveMessages)),
|
|
172
173
|
writableDirs: opts.writableDirs,
|
|
173
174
|
personal,
|
|
174
175
|
researchIsolation: opts.researchIsolation,
|
|
@@ -277,10 +278,11 @@ function runPlainTurn(opts) {
|
|
|
277
278
|
browserHome: browser?.dir,
|
|
278
279
|
});
|
|
279
280
|
finishDiagnostics = startTurnDiagnostics({ runtime, bin: rt.bin, env: childEnv,
|
|
280
|
-
cwd, args,
|
|
281
|
+
cwd, args, stdinBytes: stdinText === null ? 0 : Buffer.byteLength(stdinText),
|
|
282
|
+
profile: turnProfile.name, planId: opts.diagnosticPlanId });
|
|
281
283
|
child = spawn(rt.bin, args, {
|
|
282
284
|
cwd,
|
|
283
|
-
stdio: [rt.id === 'claude' && opts.liveMessages ? 'pipe' : 'ignore', 'pipe', 'pipe'],
|
|
285
|
+
stdio: [stdinText !== null || (rt.id === 'claude' && opts.liveMessages) ? 'pipe' : 'ignore', 'pipe', 'pipe'],
|
|
284
286
|
/**
|
|
285
287
|
* ITS OWN PROCESS GROUP, so what the agent starts stays attributable.
|
|
286
288
|
*
|
|
@@ -327,6 +329,7 @@ function runPlainTurn(opts) {
|
|
|
327
329
|
// than this is a previous turn's (turnModel.mjs).
|
|
328
330
|
const spawnedAt = Date.now();
|
|
329
331
|
let out = '';
|
|
332
|
+
let stdinError = null;
|
|
330
333
|
const input = rt.id === 'claude' && opts.liveMessages ? createClaudeInput(child, { onTaken: opts.liveMessages.onTaken }) : null;
|
|
331
334
|
const pfx = label ? `${label} ` : '';
|
|
332
335
|
/**
|
|
@@ -497,9 +500,13 @@ function runPlainTurn(opts) {
|
|
|
497
500
|
releaseArgs();
|
|
498
501
|
opts.onExit?.(code, signal);
|
|
499
502
|
if (buf.trim()) onLine(buf);
|
|
500
|
-
finishDiagnostics({ code, signal, pid: child.pid, output: out, cliFailed });
|
|
503
|
+
finishDiagnostics({ code, signal, pid: child.pid, output: out, cliFailed, inputError: stdinError?.code });
|
|
501
504
|
const handBack = async () => {
|
|
502
505
|
if (opts.liveMessages) await opts.liveMessages.stop();
|
|
506
|
+
if (stdinError) {
|
|
507
|
+
if (!workLanesStopped()) rejectTurn(stdinError);
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
503
510
|
resolve(out);
|
|
504
511
|
// After the answer is handed back, never before it: see `onRate`.
|
|
505
512
|
if (codexThread) {
|
|
@@ -530,6 +537,17 @@ function runPlainTurn(opts) {
|
|
|
530
537
|
}
|
|
531
538
|
handBack();
|
|
532
539
|
});
|
|
540
|
+
if (stdinText !== null) {
|
|
541
|
+
// end() buffers with backpressure and closes at EOF; Codex reads the
|
|
542
|
+
// entire prompt before starting. An early close must not accept output
|
|
543
|
+
// from a turn whose complete authorized input never reached the CLI.
|
|
544
|
+
child.stdin.on('error', (error) => {
|
|
545
|
+
stdinError = Object.assign(new Error(`${rt.label} could not receive the complete turn input (${error.code || 'PIPE_ERROR'}).`), { code: error.code || 'PIPE_ERROR' });
|
|
546
|
+
opts.onCliFailure?.();
|
|
547
|
+
child.kill('SIGTERM');
|
|
548
|
+
});
|
|
549
|
+
child.stdin.end(stdinText, 'utf8');
|
|
550
|
+
}
|
|
533
551
|
if (input) {
|
|
534
552
|
const initial = opts.liveMessages.initialPrompt(prompt, resumeThreadId, resume, opts.liveContinuation === true);
|
|
535
553
|
input.send(opts.liveMessages.initialId || opts.liveMessages.turnId, initial);
|
package/bin/lib/runtimeCodex.mjs
CHANGED
|
@@ -158,6 +158,13 @@ const codexWikiFence = (vaultDir) => [
|
|
|
158
158
|
...fencedTail(),
|
|
159
159
|
];
|
|
160
160
|
|
|
161
|
+
function codexPrompt({ system, prompt, profile = 'build' }) {
|
|
162
|
+
const readers = profile === 'consult' || profile === 'plan'
|
|
163
|
+
? '\nUse exec_command with shell="/bin/bash" and login=false for project discovery. Project files, local Git history and the project knowledge directory are readable; host files are outside this turn’s read boundary.'
|
|
164
|
+
: '';
|
|
165
|
+
return `${system}${readers}\n\n---\n\n${prompt}`;
|
|
166
|
+
}
|
|
167
|
+
|
|
161
168
|
export const CODEX_RUNTIME = {
|
|
162
169
|
id: 'codex',
|
|
163
170
|
label: 'Codex',
|
|
@@ -227,13 +234,12 @@ export const CODEX_RUNTIME = {
|
|
|
227
234
|
* can diff. A write-up reads a COPY with no `.git` and never resumes
|
|
228
235
|
* (codexResearch.mjs), so its fence passes it.
|
|
229
236
|
*
|
|
230
|
-
*
|
|
231
|
-
*
|
|
232
|
-
*
|
|
233
|
-
*
|
|
234
|
-
* that parses today and stops parsing on some future clap upgrade.
|
|
237
|
+
* The prompt positional stays last; runTurn uses `-` and sends the complete
|
|
238
|
+
* framed text through stdin. A whole batch can exceed Linux's single-argument
|
|
239
|
+
* limit even when its argv plus environment is far below ARG_MAX. Both exec
|
|
240
|
+
* and exec resume support `-` (verified on Codex 0.160.0, 2026-10-16).
|
|
235
241
|
*/
|
|
236
|
-
args({ writableDirs = [], prompt, system, model, effort, resume, resumeThreadId, profile = 'build', vaultDir, cwd, mcp = [], resultSchemaArgs = [], adoptResumeId, agentTools, personal = null, researchIsolation, knowledgeDir, projectReads }) {
|
|
242
|
+
args({ streamInput = false, writableDirs = [], prompt, system, model, effort, resume, resumeThreadId, profile = 'build', vaultDir, cwd, mcp = [], resultSchemaArgs = [], adoptResumeId, agentTools, personal = null, researchIsolation, knowledgeDir, projectReads }) {
|
|
237
243
|
// Adoption resumes a conversation in ITS OWN CLI's store (claude forks,
|
|
238
244
|
// agy moves) — codex has no adoptable store wired yet. Reaching here
|
|
239
245
|
// with an adopt id is a wiring mistake upstream, and it fails loudly on
|
|
@@ -294,12 +300,11 @@ export const CODEX_RUNTIME = {
|
|
|
294
300
|
if (a.includes('--ignore-user-config')) a.push(...(personal?.args ?? []));
|
|
295
301
|
|
|
296
302
|
a.push(...mcp, ...resultSchemaArgs);
|
|
297
|
-
|
|
298
|
-
? '\nUse exec_command with shell="/bin/bash" and login=false for project discovery. Project files, local Git history and the project knowledge directory are readable; host files are outside this turn’s read boundary.'
|
|
299
|
-
: '';
|
|
300
|
-
a.push(`${system}${readers}\n\n---\n\n${prompt}`);
|
|
303
|
+
a.push(streamInput ? '-' : codexPrompt({ system, prompt, profile }));
|
|
301
304
|
return a;
|
|
302
305
|
},
|
|
306
|
+
/** One-shot UTF-8 input, closed at EOF; distinct from Claude's live JSONL. */
|
|
307
|
+
stdin: codexPrompt,
|
|
303
308
|
/**
|
|
304
309
|
* THE PERSON'S LOGIN FOR A FENCED TURN, read once per spawn by runTurn from
|
|
305
310
|
* the turn's own environment (its CODEX_HOME — an agent's own home when the
|
|
@@ -10,6 +10,7 @@ import { AUTH_ENV_VARS, claudeAuthContext, claudeCredentialPath } from './claude
|
|
|
10
10
|
import { scrub } from './uplinkScrub.mjs';
|
|
11
11
|
import { printableText } from './printable.mjs';
|
|
12
12
|
import { measuredRuntimeVersion } from './runtimeDetection.mjs';
|
|
13
|
+
import { VERSION } from './config.mjs';
|
|
13
14
|
|
|
14
15
|
let active = 0;
|
|
15
16
|
const authNames = (env) => AUTH_ENV_VARS.filter((name) =>
|
|
@@ -40,7 +41,7 @@ export function startTurnDiagnostics(options) {
|
|
|
40
41
|
}
|
|
41
42
|
|
|
42
43
|
function createTurnDiagnostics({ runtime, bin, env, cwd = process.cwd(), profile,
|
|
43
|
-
planId = null, args = [], version, write = (row) => console.info(`[cli-turn] ${JSON.stringify(row)}`) }) {
|
|
44
|
+
planId = null, args = [], stdinBytes = 0, version, write = (row) => console.info(`[cli-turn] ${JSON.stringify(row)}`) }) {
|
|
44
45
|
const startedAt = Date.now();
|
|
45
46
|
const turnId = randomUUID();
|
|
46
47
|
const executable = executableFor(bin, env, cwd);
|
|
@@ -58,7 +59,14 @@ function createTurnDiagnostics({ runtime, bin, env, cwd = process.cwd(), profile
|
|
|
58
59
|
} catch { /* CLI settings unavailable */ }
|
|
59
60
|
}
|
|
60
61
|
}
|
|
61
|
-
|
|
62
|
+
// Only byte counts, including each terminating NUL. No values or hashes.
|
|
63
|
+
const argSizes = [bin, ...args].map(value => Buffer.byteLength(value) + 1);
|
|
64
|
+
const envSizes = Object.entries(env).filter(([, value]) => value !== undefined)
|
|
65
|
+
.map(([key, value]) => Buffer.byteLength(`${key}=${value}`) + 1);
|
|
66
|
+
const sum = values => values.reduce((a, b) => a + b, 0);
|
|
67
|
+
const launch = { argvBytes: sum(argSizes), maxArgBytes: Math.max(0, ...argSizes),
|
|
68
|
+
envBytes: sum(envSizes), maxEnvEntryBytes: Math.max(0, ...envSizes), stdinBytes };
|
|
69
|
+
const base = { turnId, runtime, profile, planId, cwd, daemonPid: process.pid, daemonVersion: VERSION, launch,
|
|
62
70
|
uid: process.getuid?.() ?? null, platform: process.platform,
|
|
63
71
|
nodeVersion: process.version, executable, version: version ?? null,
|
|
64
72
|
runtimeMeasurement: measuredRuntimeVersion(runtime) };
|
|
@@ -79,15 +87,15 @@ function createTurnDiagnostics({ runtime, bin, env, cwd = process.cwd(), profile
|
|
|
79
87
|
active++;
|
|
80
88
|
emit({ ...base, phase: 'start', at: new Date(startedAt).toISOString(), activeTurns: active, auth: auth() });
|
|
81
89
|
let finished = false;
|
|
82
|
-
return ({ code = null, signal = null, pid = null, output = '', spawnError = null, cliFailed = false } = {}) => {
|
|
90
|
+
return ({ code = null, signal = null, pid = null, output = '', spawnError = null, inputError = null, cliFailed = false } = {}) => {
|
|
83
91
|
if (finished) return;
|
|
84
92
|
finished = true;
|
|
85
93
|
active--;
|
|
86
94
|
const failed = cliFailed || (code !== null && code !== 0);
|
|
87
95
|
emit({ ...base, phase: 'end', at: new Date().toISOString(), durationMs: Date.now() - startedAt,
|
|
88
|
-
pid, code, signal, spawnError, activeTurns: active, auth: auth(),
|
|
96
|
+
pid, code, signal, spawnError, inputError, activeTurns: active, auth: auth(),
|
|
89
97
|
failureKind: failed && /OAuth session expired and could not be refreshed/i.test(output)
|
|
90
98
|
? 'oauth_refresh' : failed && /Failed to authenticate/i.test(output) ? 'authentication'
|
|
91
|
-
: spawnError ? 'spawn' : code !== null && code !== 0 ? 'cli_exit' : null });
|
|
99
|
+
: spawnError ? 'spawn' : inputError ? 'input' : code !== null && code !== 0 ? 'cli_exit' : null });
|
|
92
100
|
};
|
|
93
101
|
}
|
package/bin/lib/work.mjs
CHANGED
|
@@ -54,7 +54,8 @@
|
|
|
54
54
|
* turnLock.mjs tab pid locks; workLiveMessages.mjs — live input; workDelivery.mjs — queue
|
|
55
55
|
* (and, split earlier: workDiffs, workPreviews, workPullRequests, workShip,
|
|
56
56
|
* workAgentPlans, workAgentCheck, workAgentPrecheck, workAgentReview,
|
|
57
|
-
* workAgentTurns, workAgentRecovery, workAgentMerges,
|
|
57
|
+
* workAgentTurns, workAgentRecovery, workAgentMerges, workMergeBatch,
|
|
58
|
+
* workProcesses; and out of workAgentTurns,
|
|
58
59
|
* SOLID F036: workAgentTurnReports, workAgentTurnExecution, workAgentTurnBegun,
|
|
59
60
|
* workAgentTurnOutcome; repositoryPlaces + agentRepositories: multi-repo places; the live brief: workAgentBrief, workAgentBriefRunner)
|
|
60
61
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "flowviant",
|
|
3
|
-
"version": "0.125.
|
|
3
|
+
"version": "0.125.1",
|
|
4
4
|
"description": "Run your own coding CLIs as build agents for Flowviant — 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": {
|