flowviant 0.125.1 → 0.125.3

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.
@@ -30,7 +30,7 @@ import { AGENT_TASK_KINDS, agentTaskKindOf, unknownAgentTaskKind } from './agent
30
30
  import { knowledgeDirFor } from './knowledgeFiles.mjs';
31
31
  import { scrub as envScrub } from './uplinkScrub.mjs';
32
32
  import { canRun, runtimeById } from './runtimes.mjs';
33
- import { toolEventOf, CLAUDE_TOOL_PROSE_KINDS } from './runtimeEvents.mjs';
33
+ import { toolEventOf, CLAUDE_TOOL_PROSE_KINDS, scrubNativeText } from './runtimeEvents.mjs';
34
34
  import { makeTraceRelay } from './trace.mjs';
35
35
  import { mcpJsonServerNamesAbove, readBaseTools, prepareAgentTools } from './projectTools.mjs';
36
36
  import { TURN_PROFILES } from './turnProfile.mjs';
@@ -47,8 +47,11 @@ import { agentBrowserHome } from './noWindowEnv.mjs';
47
47
  import { CLAUDE_SESSION_RE, RESUME_MARKERS } from './conversationIds.mjs';
48
48
  import { createDeliveryReporter } from './workDelivery.mjs';
49
49
  import { createLiveMessages } from './workLiveMessages.mjs';
50
+ import { createOrchestrator } from './orchestrator.mjs';
51
+ import { ORCHESTRATOR_CONTRACT } from './orchestratorPrompt.mjs';
50
52
 
51
53
  export function createAgentTurnExecution({
54
+ admit,
52
55
  agentRepositories,
53
56
  makeBrief,
54
57
  baseRef,
@@ -395,8 +398,12 @@ export function createAgentTurnExecution({
395
398
  agentId,
396
399
  turnId,
397
400
  post: postAgentTrace,
401
+ orchestrationVersion: job.orchestrationVersion,
398
402
  scrub: envScrub,
399
403
  });
404
+ // A live Send can choose its path as soon as the CLI starts, including a
405
+ // silent solo turn. Do not leave a two-second first-flush race at spawn.
406
+ if (job.orchestrationVersion === 2) await trace.flush(1_000);
400
407
  /**
401
408
  * Prose the structured event will carry anyway, dropped so a read does
402
409
  * not render twice — but ONLY on a runtime whose stream reaches
@@ -470,7 +477,14 @@ export function createAgentTurnExecution({
470
477
  const artifactInstructions = getArtifactsAccepted()
471
478
  ? await artifacts.prepare?.({ placeDir: wt, before: artifactsBefore, agentId }) ?? '' : '';
472
479
  let projectTools = null;
480
+ let orchestrator = null;
473
481
  try {
482
+ const orchestrated = job.orchestrationVersion === 2;
483
+ if (orchestrated && posture === 'build' && ['claude', 'codex'].includes(rt)) {
484
+ orchestrator = await createOrchestrator({ turnId, runtime: rt, trace, admit,
485
+ notify: message => { if (rt === 'claude') run.live?.inject(message); },
486
+ taken: id => run.live?.onTaken(id) });
487
+ }
474
488
  // A turn reads the base ref afresh. Changes the agent has made to its
475
489
  // own worktree cannot silently change the next turn's tools.
476
490
  const sources = repositoryRun?.sources ?? [];
@@ -480,7 +494,10 @@ export function createAgentTurnExecution({
480
494
  const base = readBaseTools(other.repoRoot, other.baseRef);
481
495
  snapshot.instructions.push(...[...base.instructions, ...base.skillFiles].map((file) => ({ ...file, path: `repositories/${other.folder}/${file.path}` })));
482
496
  }
483
- projectTools = prepareAgentTools(snapshot, rt, process.env, wt, { codexHome: codexAgentHome, turnServers: repositoryRun?.tool ? { card_repositories: repositoryRun.tool.config } : {} });
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
+ } });
484
501
  const mountsProjectMcp = TURN_PROFILES[posture]?.projectMcp === true && (rt === 'claude' || rt === 'codex');
485
502
  // A fenced posture on a runner that ignores its own file there (Codex's
486
503
  // `--ignore-user-config`) takes the servers as `fencedMount` instead.
@@ -501,11 +518,14 @@ export function createAgentTurnExecution({
501
518
  unmounted: [...projectTools.mcpUnmounted.map((u) => ({ ...u, source: 'project' })), ...(fencedMount ? fencedMount.unmounted : [])],
502
519
  });
503
520
  const agentTurnArgs = {
504
- ...(rt === 'claude' ? { liveMessages: (run.live = makeLiveMessages({
521
+ ...((rt === 'claude' || orchestrator) ? { liveMessages: (run.live = makeLiveMessages({
505
522
  scope: 'agent', turnId, cwd: wt, sessionMetaPath, interrupted: () => run.interrupted,
523
+ observe: message => orchestrator?.note(message),
524
+ ...(rt === 'codex' ? { nativeIds: () => null } : {}),
506
525
  format: async (followup) => {
526
+ orchestrator?.note(followup);
507
527
  const attached = followup.attachments?.length ? await fetchAgentFiles(wt, followup.attachments.map((file) => ({ ...file, from: 'message' }))) : { message: [], card: [] };
508
- return AGENT_HUMAN_KICKOFF({ agentName: job.agentName, message: followup.body + (followup.libraryPrompt ?? ''),
528
+ return AGENT_HUMAN_KICKOFF({ agentName: job.agentName, message: followup.body + (followup.libraryPrompt ?? '') + (orchestrator ? `\nMessage id: ${followup.id}. Choose and report its route with card_orchestrator.route_message.` : ''),
509
529
  askedByName: followup.askedByName, task: job.task, cardThread: job.cardThread,
510
530
  investigationContext: job.investigationContext, researchContext: job.researchContext,
511
531
  messageFiles: attached.message, cardFiles: files.card, position: job.position ?? 1, total: job.total ?? 1 });
@@ -556,10 +576,11 @@ export function createAgentTurnExecution({
556
576
  // ARTIFACTS (0.94.0), while the server can show one — an agent's
557
577
  // land on its page, under the facts row.
558
578
  artifacts: getArtifactsAccepted(),
559
- }) + (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>"])}` : ''),
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}` : ''),
560
580
  agentTools: projectTools,
561
581
  writableDirs: repositoryRun?.writableDirs,
562
- ...((projectTools.codexHome || fencedMount || repositoryRun?.tool) ? { mcpEnv: { ...(projectTools.codexHome ? { CODEX_HOME: projectTools.codexHome } : {}), ...(fencedMount?.env ?? {}), ...(repositoryRun?.tool?.env ?? {}) } } : {}),
582
+ ...((projectTools.codexHome || fencedMount || repositoryRun?.tool || orchestrator) ? { mcpEnv: { ...(projectTools.codexHome ? { CODEX_HOME: projectTools.codexHome } : {}), ...(fencedMount?.env ?? {}), ...(repositoryRun?.tool?.env ?? {}), ...(orchestrator?.env ?? {}) } } : {}),
583
+ orchestration: Boolean(orchestrator),
563
584
  // NOBODY IS AT THIS SCREEN (2026-09-29): no display, BROWSER=none, and
564
585
  // a browser profile of the agent's own, kept beside its Codex home —
565
586
  // so a Chrome it starts to look at its page never reaches the
@@ -578,15 +599,20 @@ export function createAgentTurnExecution({
578
599
  // The kind's posture, as the one turn profile (turnProfile.mjs):
579
600
  // build for code, the fenced design/research postures otherwise.
580
601
  profile: posture,
602
+ cardTurn: Boolean(orchestrator),
603
+ onSubagent: (update) => orchestrator ? orchestrator.observe(update) : trace.agent(update),
604
+ onSpawnResult: result => orchestrator?.spawnResult(result),
581
605
  cwd: wt,
582
606
  runtime: rt,
583
607
  resume,
584
608
  resumeThreadId: codexResumeId || claudeResumeId || undefined,
585
609
  onInit: (init) => {
610
+ orchestrator?.root(init.sessionId);
586
611
  if (claudeMarker && CLAUDE_SESSION_RE.test(init.sessionId ?? ''))
587
612
  try { writeFileSync(claudeMarker, init.sessionId); } catch { /* best effort */ }
588
613
  },
589
614
  onThreadId: (id) => {
615
+ orchestrator?.root(id);
590
616
  seenThreadId = String(id ?? '').trim() || seenThreadId;
591
617
  },
592
618
  onAnswer: (t) => {
@@ -600,6 +626,20 @@ export function createAgentTurnExecution({
600
626
  // narrator keeps: overwritten, never appended, and ignored by the
601
627
  // board past ~90 seconds.
602
628
  onActivity: (a) => {
629
+ if (a?.agentId) {
630
+ // Tool labels are already clipped. Their structured, scrubbed
631
+ // twin below supplies Now without leaking a credential prefix.
632
+ if (doubledKinds?.has(a.kind)) return;
633
+ const body = scrubNativeText(a.full ?? a.label, 4_000, envScrub);
634
+ const kind = a.kind;
635
+ const step = id => {
636
+ trace.agent({ id, now: body });
637
+ if (kind === 'doing') trace.doing(id, body);
638
+ else trace.prose(kind, body, id);
639
+ };
640
+ if (orchestrator) orchestrator.attributed(a.agentId, step); else step(a.agentId);
641
+ return;
642
+ }
603
643
  brief?.append(a?.kind ?? 'activity', a?.full ?? a?.label);
604
644
  let line = a?.label;
605
645
  if (!line) return;
@@ -632,6 +672,19 @@ export function createAgentTurnExecution({
632
672
  // the builder itself (bounded window BEFORE its caps; see
633
673
  // toolEventOf). A tool it does not know pushes nothing.
634
674
  onToolEvent: (name, input, meta) => {
675
+ if (meta?.agentId) {
676
+ if (!meta.result) {
677
+ const event = toolEventOf(name, input, wt, envScrub);
678
+ const now = event?.c ? `$ ${event.c}` : event?.p ?? event?.q;
679
+ const step = id => {
680
+ if (now) trace.agent({ id, now });
681
+ trace.tool(event, id);
682
+ if (name === 'Bash' && now) trace.doing(id, now);
683
+ };
684
+ if (orchestrator) orchestrator.attributed(meta.agentId, step); else step(meta.agentId);
685
+ }
686
+ return;
687
+ }
635
688
  brief?.append(meta?.result ? 'tool result' : 'tool call', meta?.result
636
689
  ? { id: meta.id, exitCode: meta.exitCode, isError: meta.isError, output: meta.output }
637
690
  : { name, input });
@@ -660,6 +713,7 @@ export function createAgentTurnExecution({
660
713
  // (runTurn.mjs, `initHarvestFor`). An agent-only box learns from the
661
714
  // startup probe and its Terminal tabs.
662
715
  onSpawn: (ch) => {
716
+ if (orchestrator && rt === 'codex') run.live?.connect((id, body) => orchestrator.input(id, body));
663
717
  if (rt !== 'claude') reportDelivery('agent', job, 'taken');
664
718
  brief?.running(true);
665
719
  ch.once('close', () => { if (child === ch) brief?.running(false); });
@@ -732,6 +786,12 @@ export function createAgentTurnExecution({
732
786
  },
733
787
  });
734
788
  } finally {
789
+ // The loopback capability stays registered until every retained CLI
790
+ // answer is scrubbed; then it can leave the machine's secret set.
791
+ out = envScrub(out);
792
+ if (lastAnswer) lastAnswer = envScrub(lastAnswer);
793
+ await run.live?.stop();
794
+ await orchestrator?.close(run.interrupted);
735
795
  await brief?.stop();
736
796
  // What the CLI counted, for the lane's catch if anything below throws.
737
797
  measured.usage = usage;
@@ -749,7 +809,24 @@ export function createAgentTurnExecution({
749
809
  * uplink that will not answer costs the tail and nothing else.
750
810
  */
751
811
  trace.stop();
752
- await trace.flush(TRACE_FINAL_FLUSH_MS);
812
+ // Trace reports remain turn-scoped and the API accepts their late tail.
813
+ // An interrupted process has already exited: a slow trace uplink must
814
+ // not delay its settlement or the queued prompt's continuation.
815
+ const tail = trace.flush(TRACE_FINAL_FLUSH_MS);
816
+ if (!setupController.signal.aborted) {
817
+ let releaseWait;
818
+ try {
819
+ // An interrupt may arrive after the CLI exited, while an ordinary
820
+ // final flush is already waiting. The same run cancellation signal
821
+ // releases that wait without cancelling the turn-scoped upload.
822
+ await Promise.race([tail, new Promise(resolve => {
823
+ releaseWait = () => resolve();
824
+ setupController.signal.addEventListener('abort', releaseWait, { once: true });
825
+ })]);
826
+ } finally {
827
+ setupController.signal.removeEventListener('abort', releaseWait);
828
+ }
829
+ }
753
830
  for (const ch of spawnedChildren) workChildren.delete(ch);
754
831
  if (agentChildren.get(place) === child) agentChildren.delete(place);
755
832
  if (ranMarker && rt === 'claude') {
@@ -6,7 +6,7 @@
6
6
  * that follow a CLI (a limit, no declared outcome, a missing artifact, the
7
7
  * parsed result) are one decision with its own rules — a limit only when the
8
8
  * turn declared nothing, an artifact kind delivered only once its file is
9
- * measured, every relayed string scrubbed BEFORE it is cut — and the run that
9
+ * measured, and every relayed string scrubbed without prose truncation — and the run that
10
10
  * feeds it (workAgentTurnExecution.mjs) changes for reasons none of those
11
11
  * rules share. Pure but for one read-only artifact scan, so each rule is a
12
12
  * unit test here instead of a source pin behind a CLI nobody spawns in tests.
@@ -55,7 +55,7 @@ export function agentTurnSettlement({ turnId, job, kind, out, lastAnswer, usage,
55
55
  return report({
56
56
  turnId,
57
57
  outcome: 'nothing',
58
- answer: limit,
58
+ answer: readableOut.trim() === limit ? limit : `${limit}\n\n${envScrub(readableOut)}`,
59
59
  ...(usage ? { usage } : {}),
60
60
  branch,
61
61
  worktree: wt,
@@ -71,7 +71,7 @@ export function agentTurnSettlement({ turnId, job, kind, out, lastAnswer, usage,
71
71
  // The CLI's own words when it produced any. A turn that explained why
72
72
  // it stopped is far more use than "the agent stopped".
73
73
  answer: raw.trim()
74
- ? readable.trim() ? envScrub(readable).slice(-1500)
74
+ ? readable.trim() ? envScrub(readable)
75
75
  : 'The turn returned an unreadable outcome declaration. This work has not been delivered.'
76
76
  : 'the turn produced no output on the machine — its CLI may be signed out',
77
77
  ...(commits.length ? { commits } : {}),
@@ -120,8 +120,8 @@ export function agentTurnSettlement({ turnId, job, kind, out, lastAnswer, usage,
120
120
  const briefUse = /^Research intended use: (tasks|context|document)(?:\r?\n|$)/.exec(job.task?.brief ?? '')?.[1];
121
121
  const researchUse = kind.posture === 'research' ? briefUse ?? res.researchUse : null;
122
122
  const temporaryResearch = ['tasks', 'context'].includes(researchUse);
123
- if (res.outcome === 'delivered' && temporaryResearch && (!res.answer?.trim() || res.answer.length > 7500)) {
124
- return report({ turnId, outcome: 'nothing', answer: res.answer?.trim() ? 'Keep the research findings, sources and uncertainties within 7500 characters so the full answer can be retained.' : 'The research turn returned no findings.', branch, worktree: wt });
123
+ if (res.outcome === 'delivered' && temporaryResearch && !res.answer?.trim()) {
124
+ return report({ turnId, outcome: 'nothing', answer: 'The research turn returned no findings.', branch, worktree: wt });
125
125
  }
126
126
  if (res.outcome === 'delivered' && kind.artifact && !temporaryResearch) {
127
127
  const want = kind.artifact.match;
@@ -154,7 +154,7 @@ export function agentTurnSettlement({ turnId, job, kind, out, lastAnswer, usage,
154
154
  ...(prerequisite && !cannotRequest ? { prerequisite } : {}),
155
155
  outcome: res.outcome,
156
156
  answer: cannotRequest ?? envScrub(kind.posture === 'research' && researchUse
157
- ? `Research intended use: ${researchUse}\n\n${res.answer ?? ''}` : res.answer ?? '').slice(0, 8000),
157
+ ? `Research intended use: ${researchUse}\n\n${res.answer ?? ''}` : res.answer ?? ''),
158
158
  ...(commits.length ? { commits } : {}),
159
159
  // SCRUBBED like the answer beside it: a raised card lands in the
160
160
  // project doc every member reads, and a brief quoting the `.env` the
@@ -164,7 +164,7 @@ export function agentTurnSettlement({ turnId, job, kind, out, lastAnswer, usage,
164
164
  ? {
165
165
  raised: res.raised.map((r) => ({
166
166
  title: envScrub(r.title).slice(0, 300),
167
- ...(r.brief ? { brief: envScrub(r.brief).slice(0, 2000) } : {}),
167
+ ...(r.brief ? { brief: envScrub(r.brief) } : {}),
168
168
  })),
169
169
  }
170
170
  : {}),
@@ -228,7 +228,7 @@ export function failedTurnSettlement({ turnId, error, commits = [], usage = null
228
228
  outcome: 'nothing',
229
229
  answer: envScrub(
230
230
  why ? `This machine could not finish the turn: ${why}` : 'This machine could not finish the turn.'
231
- ).slice(0, 1500),
231
+ ),
232
232
  ...(commits.length ? { commits } : {}),
233
233
  ...(usage ? { usage } : {}),
234
234
  ...(model ? { model } : {}),
@@ -112,6 +112,7 @@ export function createWorkAgentTurns({
112
112
  const stoppedFor = new Map(); // agentId -> when
113
113
 
114
114
  const { runAgentTurnInPlace, commitsBetween } = createAgentTurnExecution({
115
+ admit,
115
116
  agentRepositories,
116
117
  makeBrief,
117
118
  baseRef,
@@ -415,6 +416,7 @@ export function createWorkAgentTurns({
415
416
  const run = agentRuns.get(id);
416
417
  if (run) {
417
418
  if (!run.interrupted) note(`${c.cyan('agent')} ${c.dim('— interrupting the running turn')}`);
419
+ if (!run.interrupted) reportDelivery('agent', { id }, 'interrupting');
418
420
  run.interrupted = true;
419
421
  run.abortSetup?.();
420
422
  const child = agentChildren.get(run.place);
@@ -3,6 +3,10 @@
3
3
  * to, the teardown that SIGTERMs them, the count the concurrency ceiling is
4
4
  * on, and the admission built over that count.
5
5
  *
6
+ * This manager is project-scoped. Native card children hold reservations in
7
+ * the same admission while live (orchestrator.mjs); other projects have their
8
+ * own pool, per the owner's 2026-10-15 ruling.
9
+ *
6
10
  * Split out of work.mjs (2026-09-26, SOLID F037). Six lanes write
7
11
  * `workChildren` (tab turns, agent turns, plans, the check, the pre-review)
8
12
  * and three things read it (teardown, `liveTurnCount`, the machine snapshot's
@@ -9,7 +9,7 @@ import { fleetEndpoint, isConsideredRefusal } from './fleetWire.mjs';
9
9
  import { DAEMON_INSTANCE, FLEET_URL } from './config.mjs';
10
10
  import { messageIdsForSession } from './claudeSessions.mjs';
11
11
 
12
- export function createLiveMessages({ scope, turnId, attempt, cwd, sessionMetaPath, format, interrupted = () => false,
12
+ export function createLiveMessages({ scope, turnId, attempt, cwd, sessionMetaPath, format, observe = () => {}, interrupted = () => false,
13
13
  request = fleetRequest, nativeIds = messageIdsForSession, setTimer = setInterval, clearTimer = clearInterval, finalFlushMs = 1_000 }) {
14
14
  const identity = { scope, turnId, instance: DAEMON_INSTANCE, ...(attempt !== undefined ? { attempt } : {}) };
15
15
  const file = sessionMetaPath(cwd, 'flowviant-live-messages', turnId);
@@ -73,6 +73,7 @@ export function createLiveMessages({ scope, turnId, attempt, cwd, sessionMetaPat
73
73
  if (!result.json?.data?.active) return;
74
74
  const seen = sessionId ? nativeIds(cwd, sessionId) : null;
75
75
  for (const message of result.json.data.messages ?? []) {
76
+ observe(message);
76
77
  if (stopped || interrupted()) break;
77
78
  if (taken.has(message.id) || seen?.has(message.id)) { onTaken(message.id); continue; }
78
79
  if (sent.has(message.id)) continue;
@@ -83,6 +84,11 @@ export function createLiveMessages({ scope, turnId, attempt, cwd, sessionMetaPat
83
84
  return flight;
84
85
  };
85
86
  return {
87
+ inject(prompt) {
88
+ if (stopped || interrupted() || !send) return false;
89
+ const id = randomUUID(); initialIds.add(id);
90
+ return send(id, prompt) === true;
91
+ },
86
92
  turnId, onTaken, tick,
87
93
  connected: () => Boolean(send) && !stopped,
88
94
  get initialId() { return initialId; },
@@ -41,7 +41,7 @@ import { AGY_CONV_RE, CLAUDE_SESSION_RE, CODEX_THREAD_RE, RESUME_MARKERS } from
41
41
  import { sessionTurnKickoff, sessionTurnShape, sessionTurnSystem } from './workTurnShape.mjs';
42
42
  import { createToolLog } from './workToolLog.mjs';
43
43
  import { createCommandAudit } from './workCommandAudit.mjs';
44
- import { APPROVAL_REFUSED, codexApprovalSentence, codexLoginFailure, codexLoginSentence, SESSION_ANSWER_MAX, withRelay } from './codexRelay.mjs';
44
+ import { APPROVAL_REFUSED, codexApprovalSentence, codexLoginFailure, codexLoginSentence, withRelay } from './codexRelay.mjs';
45
45
 
46
46
  /**
47
47
  * HOW LONG, AND HOW MANY, SETTLED TURNS THE LANE REMEMBERS (2026-09-29) — see
@@ -1035,7 +1035,7 @@ export function createWorkSessionTurns({
1035
1035
  ? refusedLine
1036
1036
  ? withRelay(refusedLine, answer)
1037
1037
  : // Scrub: a reply can quote config or env-adjacent code.
1038
- envScrub(answer).slice(0, SESSION_ANSWER_MAX)
1038
+ envScrub(answer)
1039
1039
  : (refusedLine ?? noWords),
1040
1040
  // Only a turn that actually SPOKE proves a conversation lives
1041
1041
  // here. Recording the path unconditionally is how a crashed first
@@ -1062,7 +1062,7 @@ export function createWorkSessionTurns({
1062
1062
  // Scrub, like every string that leaves this machine: an exception
1063
1063
  // routinely quotes command output, and command output can quote a
1064
1064
  // synced secret.
1065
- answer: envScrub(String(e?.message ?? 'the session turn failed')).slice(0, 2000),
1065
+ answer: envScrub(String(e?.message ?? 'the session turn failed')),
1066
1066
  // "What did it do before it failed" is exactly the question a
1067
1067
  // crashed turn's log answers, and a turn that threw after its CLI
1068
1068
  // ran still spent what it spent — the main settle's own spread.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "flowviant",
3
- "version": "0.125.1",
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.",
3
+ "version": "0.125.3",
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": {
7
7
  "flowviant": "bin/cli.mjs"