flowviant 0.125.27 → 0.125.28
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/README.md +9 -6
- package/bin/cli.mjs +5 -0
- package/bin/lib/authproxy.mjs +51 -2
- package/bin/lib/knowledge.mjs +2 -0
- package/bin/lib/knowledgeFiles.mjs +9 -2
- package/bin/lib/knowledgeLibrary.mjs +3 -0
- package/bin/lib/knowledgeVaults.mjs +148 -0
- package/bin/lib/managedBrowser.mjs +230 -0
- package/bin/lib/managedBrowserNetwork.mjs +34 -0
- package/bin/lib/managedBrowserPage.mjs +204 -0
- package/bin/lib/prompts.mjs +40 -4
- package/bin/lib/runTurn.mjs +1 -0
- package/bin/lib/runtimeClaude.mjs +2 -2
- package/bin/lib/runtimeCodex.mjs +6 -2
- package/bin/lib/vault.mjs +2 -0
- package/bin/lib/vaultDiff.mjs +22 -0
- package/bin/lib/wikiDraft.mjs +105 -0
- package/bin/lib/wikiOwnership.mjs +19 -0
- package/bin/lib/wikiRunner.mjs +22 -12
- package/bin/lib/workAgentTools.mjs +6 -3
- package/bin/lib/workAgentTurnExecution.mjs +17 -1
- package/package.json +1 -1
- package/npm-shrinkwrap.json +0 -69
package/bin/lib/wikiRunner.mjs
CHANGED
|
@@ -30,6 +30,7 @@ import { sawSentinel } from './claude.mjs';
|
|
|
30
30
|
import { runTurn as defaultRunTurn } from './runTurn.mjs';
|
|
31
31
|
import { SYSTEM_WIKI, WIKI_KICKOFF, SYSTEM_REGROUND, REGROUND_KICKOFF } from './prompts.mjs';
|
|
32
32
|
import { ensureVault, syncVault } from './vault.mjs';
|
|
33
|
+
import { beginWikiDraft } from './wikiDraft.mjs';
|
|
33
34
|
import { scrub as envScrub } from './uplinkScrub.mjs';
|
|
34
35
|
import { runtimeLabel } from './runtimes.mjs';
|
|
35
36
|
import { pickRuntimeFor as defaultPickRuntimeFor } from './runtimeDetection.mjs';
|
|
@@ -238,7 +239,8 @@ export function createWikiRunner({
|
|
|
238
239
|
// always taken (audit 2026-10-10, F749).
|
|
239
240
|
if (!SHA1_ABBREV_RE.test(String(sha).toLowerCase())) continue;
|
|
240
241
|
try {
|
|
241
|
-
|
|
242
|
+
// Report both sides of a rename so old files: citations are affected.
|
|
243
|
+
const out = gitRaw(['show', '--no-renames', '--name-only', '--pretty=format:', String(sha)], repoRoot);
|
|
242
244
|
for (const line of out.split('\n')) {
|
|
243
245
|
const f = line.trim();
|
|
244
246
|
if (f) files.add(f);
|
|
@@ -374,6 +376,7 @@ export function createWikiRunner({
|
|
|
374
376
|
* readout. Null (and absent from the frame) when no CLI ran.
|
|
375
377
|
*/
|
|
376
378
|
let usage = null;
|
|
379
|
+
let draft = null;
|
|
377
380
|
const onUsage = (rt) => (u) => {
|
|
378
381
|
usage = addUsage(usage, { ...u, runtime: rt });
|
|
379
382
|
};
|
|
@@ -394,20 +397,23 @@ export function createWikiRunner({
|
|
|
394
397
|
resetWorktree(wikiWt, getBaseRef());
|
|
395
398
|
// No HEAD → '' — still writes the map, just ungrounded.
|
|
396
399
|
const sha = gitOrNull(['rev-parse', 'HEAD'], wikiWt) ?? '';
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
+
const repoFullName = originSlug(repoRoot) || undefined;
|
|
401
|
+
const persistentVaultDir = vaultDir;
|
|
402
|
+
draft = beginWikiDraft(persistentVaultDir);
|
|
403
|
+
vaultDir = draft.dir;
|
|
404
|
+
// Only a complete draft can replace last-good pages or prune them.
|
|
405
|
+
// The CLI's write fence names the draft, never Knowledge or sources.
|
|
400
406
|
const runSync = async (finalize) => {
|
|
401
407
|
try {
|
|
402
408
|
const r = await syncVault({
|
|
403
|
-
dir:
|
|
409
|
+
dir: persistentVaultDir,
|
|
404
410
|
url: WIKI_VAULT_URL,
|
|
405
411
|
token: FLEET_TOKEN,
|
|
406
412
|
userAgent: USER_AGENT,
|
|
407
413
|
finalize,
|
|
408
414
|
groundedAtSha: sha || undefined,
|
|
409
415
|
// Powers the GitHub blob links behind every cited file path.
|
|
410
|
-
repoFullName
|
|
416
|
+
repoFullName,
|
|
411
417
|
warn,
|
|
412
418
|
// Redact synced secrets a page may have quoted from the repo.
|
|
413
419
|
scrub: envScrub,
|
|
@@ -435,7 +441,7 @@ export function createWikiRunner({
|
|
|
435
441
|
sweepAttempts++;
|
|
436
442
|
note(`${c.cyan('wiki')} ${c.dim(`— regenerating: your ${wikiLabel} is reading the repo…`)}`);
|
|
437
443
|
const out = await runTurn({
|
|
438
|
-
prompt: WIKI_KICKOFF(sha, vaultDir),
|
|
444
|
+
prompt: WIKI_KICKOFF(sha, vaultDir, repoFullName),
|
|
439
445
|
resume: false,
|
|
440
446
|
system: SYSTEM_WIKI(vaultDir),
|
|
441
447
|
cwd: wikiWt,
|
|
@@ -454,12 +460,13 @@ export function createWikiRunner({
|
|
|
454
460
|
});
|
|
455
461
|
const complete = sawSentinel(out, 'WIKI_DONE');
|
|
456
462
|
if (complete) {
|
|
463
|
+
draft.publish({ sha, repoFullName });
|
|
457
464
|
sweepCompleted = true;
|
|
458
465
|
ok(`${c.cyan('wiki')} ${c.dim('— vault regenerated from your code.')}`);
|
|
459
466
|
} else {
|
|
460
|
-
warn('wiki sweep ended without WIKI_DONE —
|
|
467
|
+
warn('wiki sweep ended without WIKI_DONE — last good pages retained');
|
|
461
468
|
}
|
|
462
|
-
await runSync(
|
|
469
|
+
if (complete) await runSync(true);
|
|
463
470
|
} else {
|
|
464
471
|
const files = changedFilesForShas(task.shas);
|
|
465
472
|
if (files === null) {
|
|
@@ -484,6 +491,7 @@ export function createWikiRunner({
|
|
|
484
491
|
title: task.title,
|
|
485
492
|
files,
|
|
486
493
|
vaultDir,
|
|
494
|
+
repoFullName,
|
|
487
495
|
predictedPages: task.dirtiesPages ?? [],
|
|
488
496
|
}),
|
|
489
497
|
resume: false,
|
|
@@ -499,10 +507,11 @@ export function createWikiRunner({
|
|
|
499
507
|
wikiChild = ch;
|
|
500
508
|
},
|
|
501
509
|
});
|
|
502
|
-
if (sawSentinel(out, 'REGROUND_DONE'))
|
|
510
|
+
if (sawSentinel(out, 'REGROUND_DONE')) {
|
|
511
|
+
draft.publish({ sha, repoFullName });
|
|
503
512
|
ok(`${c.cyan('wiki')} ${c.dim(`— vault updated for "${task.title}".`)}`);
|
|
504
|
-
|
|
505
|
-
|
|
513
|
+
await runSync(false);
|
|
514
|
+
} else warn(`wiki re-ground for "${task.title}" ended without REGROUND_DONE — last good pages retained.`);
|
|
506
515
|
}
|
|
507
516
|
// Consume the durable job: attempted = done (success or not — the
|
|
508
517
|
// sync is idempotent and a failed turn heals on the next full
|
|
@@ -521,6 +530,7 @@ export function createWikiRunner({
|
|
|
521
530
|
} catch (e) {
|
|
522
531
|
warn(`wiki ${task.type} failed: ${e.message}`);
|
|
523
532
|
} finally {
|
|
533
|
+
draft?.discard();
|
|
524
534
|
wikiChild = null;
|
|
525
535
|
if (heartbeat) clearInterval(heartbeat);
|
|
526
536
|
/**
|
|
@@ -27,13 +27,15 @@ import { postureToolsNote } from './postureTools.mjs';
|
|
|
27
27
|
import { machineMcpServers } from './claudeMachineMcp.mjs';
|
|
28
28
|
import { ORCHESTRATOR_SERVER } from './orchestratorProtocol.mjs';
|
|
29
29
|
import { VIDEO_SERVER } from './videoRender.mjs';
|
|
30
|
+
import { BROWSER_SERVER } from './managedBrowser.mjs';
|
|
30
31
|
|
|
31
32
|
/** The servers the daemon itself mounts on a card turn, by the names the
|
|
32
33
|
* agent calls them: a multi-repository card's add_repository, an
|
|
33
34
|
* orchestrated card's routing, a build's UI review and a video card's
|
|
34
|
-
* render_video (2026-10-26)
|
|
35
|
+
* render_video (2026-10-26), and a code turn's managed browser (2026-10-30).
|
|
36
|
+
* Never a project server's, whatever the base
|
|
35
37
|
* `.mcp.json` names. */
|
|
36
|
-
export const CARD_TURN_SERVERS = Object.freeze({ repositories: 'card_repositories', orchestrator: ORCHESTRATOR_SERVER, uiReview: 'card_ui_review', video: VIDEO_SERVER });
|
|
38
|
+
export const CARD_TURN_SERVERS = Object.freeze({ repositories: 'card_repositories', orchestrator: ORCHESTRATOR_SERVER, uiReview: 'card_ui_review', video: VIDEO_SERVER, browser: BROWSER_SERVER });
|
|
37
39
|
|
|
38
40
|
/**
|
|
39
41
|
* The card turn's tools, prepared, and what it is told of them.
|
|
@@ -46,7 +48,7 @@ export const CARD_TURN_SERVERS = Object.freeze({ repositories: 'card_repositorie
|
|
|
46
48
|
* from here), the note, the mount's part of the CLI's `mcpEnv` and whether it
|
|
47
49
|
* needs one, and the keys the runner takes the mount as.
|
|
48
50
|
*/
|
|
49
|
-
export function prepareCardTools({ rt, posture, wt, repoRoot, baseRef, sources = [], repositoryTool = null, orchestratorConfig = null, uiReviewConfig = null, videoConfig = null, orchestrated = false, codexHome = null, env = process.env, machineServers = machineMcpServers }) {
|
|
51
|
+
export function prepareCardTools({ rt, posture, wt, repoRoot, baseRef, sources = [], repositoryTool = null, orchestratorConfig = null, uiReviewConfig = null, videoConfig = null, browserConfig = null, orchestrated = false, codexHome = null, env = process.env, machineServers = machineMcpServers }) {
|
|
50
52
|
// A turn reads the base ref afresh. Changes the agent has made to its
|
|
51
53
|
// own worktree cannot silently change the next turn's tools.
|
|
52
54
|
const source = sources.find((r) => r.wt === wt);
|
|
@@ -59,6 +61,7 @@ export function prepareCardTools({ rt, posture, wt, repoRoot, baseRef, sources =
|
|
|
59
61
|
...(repositoryTool ? { [CARD_TURN_SERVERS.repositories]: repositoryTool.config } : {}),
|
|
60
62
|
...(orchestratorConfig ? { [CARD_TURN_SERVERS.orchestrator]: orchestratorConfig } : {}),
|
|
61
63
|
...(posture === 'build' && uiReviewConfig ? { [CARD_TURN_SERVERS.uiReview]: uiReviewConfig } : {}),
|
|
64
|
+
...(posture === 'build' && browserConfig ? { [CARD_TURN_SERVERS.browser]: browserConfig } : {}),
|
|
62
65
|
// A video card's render tool rides its fenced posture (videoRender.mjs).
|
|
63
66
|
...(videoConfig ? { [CARD_TURN_SERVERS.video]: videoConfig } : {}),
|
|
64
67
|
} });
|
|
@@ -70,6 +70,7 @@ import { measuredRuntimeVersion } from './runtimeDetection.mjs';
|
|
|
70
70
|
import { createIntermediateArtifacts, IntermediateArtifactCapacityError } from './intermediateArtifacts.mjs';
|
|
71
71
|
import { createUiReview } from './uiReview.mjs';
|
|
72
72
|
import { createVideoRender } from './videoRender.mjs';
|
|
73
|
+
import { createManagedBrowser } from './managedBrowser.mjs';
|
|
73
74
|
import { withUiReview, appendUiReviewEvidence } from './uiReviewTurn.mjs';
|
|
74
75
|
|
|
75
76
|
export function createAgentTurnExecution({
|
|
@@ -102,6 +103,7 @@ export function createAgentTurnExecution({
|
|
|
102
103
|
exportModel,
|
|
103
104
|
makeUiReview = createUiReview,
|
|
104
105
|
makeVideoRender = createVideoRender,
|
|
106
|
+
makeManagedBrowser = createManagedBrowser,
|
|
105
107
|
}) {
|
|
106
108
|
const reportDelivery = createDeliveryReporter();
|
|
107
109
|
/** How long the final flush may hold the settle. Bounded because the settle
|
|
@@ -410,6 +412,8 @@ export function createAgentTurnExecution({
|
|
|
410
412
|
/** The CLI, unless a person already asked to stop this turn (Interrupt
|
|
411
413
|
* now): then nothing starts — no fresh retry, no continuation. */
|
|
412
414
|
let uiReview = null;
|
|
415
|
+
let managedBrowser = null;
|
|
416
|
+
let browserNote = '';
|
|
413
417
|
let uiReviewNote = '';
|
|
414
418
|
let uiReviewEvidence = '';
|
|
415
419
|
// A video card's render tool (videoRender.mjs), and the words a turn
|
|
@@ -424,6 +428,8 @@ export function createAgentTurnExecution({
|
|
|
424
428
|
args.verifiedWorktree = true;
|
|
425
429
|
args = withUiReview(args, uiReviewNote, uiReview?.env);
|
|
426
430
|
args = withUiReview(args, videoNote);
|
|
431
|
+
args = withUiReview(args, browserNote, managedBrowser?.env);
|
|
432
|
+
args.managedBrowser = Boolean(managedBrowser);
|
|
427
433
|
return runTurnImpl(args);
|
|
428
434
|
};
|
|
429
435
|
/**
|
|
@@ -473,6 +479,15 @@ export function createAgentTurnExecution({
|
|
|
473
479
|
let orchestrator = null;
|
|
474
480
|
let intermediateDelivery = null;
|
|
475
481
|
try {
|
|
482
|
+
if (posture === 'build' && job.kind !== 'merge_resolve' && ['claude', 'codex'].includes(rt)) {
|
|
483
|
+
try {
|
|
484
|
+
managedBrowser = await makeManagedBrowser({ wt, turnId, signal: setupController.signal, onChild: ch => {
|
|
485
|
+
workChildren.set(ch, agentId); groupKillChildren?.add(ch);
|
|
486
|
+
return () => { workChildren.delete(ch); groupKillChildren?.delete(ch); };
|
|
487
|
+
} });
|
|
488
|
+
browserNote = managedBrowser.instructions;
|
|
489
|
+
} catch (error) { browserNote = `The managed browser could not start: ${envScrub(error.message)}. Report interactive browser checks unavailable.`; }
|
|
490
|
+
}
|
|
476
491
|
if (posture === 'build' && job.kind !== 'merge_resolve') {
|
|
477
492
|
try {
|
|
478
493
|
uiReview = await makeUiReview({ wt, signal: setupController.signal, onChild: ch => {
|
|
@@ -499,7 +514,7 @@ export function createAgentTurnExecution({
|
|
|
499
514
|
notify: message => liveInput ? run.live?.inject(message) === true : false,
|
|
500
515
|
taken: id => run.live?.onTaken(id) });
|
|
501
516
|
}
|
|
502
|
-
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, orchestrated: Boolean(orchestrator), codexHome: agentResume.codexHome });
|
|
517
|
+
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 });
|
|
503
518
|
projectTools = tools.projectTools;
|
|
504
519
|
localCleanup?.begin(turnId);
|
|
505
520
|
const { toolsNote } = tools;
|
|
@@ -759,6 +774,7 @@ export function createAgentTurnExecution({
|
|
|
759
774
|
// owned by its review tool, then bind evidence to the final source
|
|
760
775
|
// before the existing artifact upload and delivery settlement.
|
|
761
776
|
try { await videoRender?.close(); } catch { /* a closed listener */ }
|
|
777
|
+
try { await managedBrowser?.close(); } catch { /* turn teardown continues */ }
|
|
762
778
|
try { await uiReview?.close(); }
|
|
763
779
|
finally {
|
|
764
780
|
try { uiReviewEvidence = uiReview?.finalize() ?? uiReviewEvidence; }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "flowviant",
|
|
3
|
-
"version": "0.125.
|
|
3
|
+
"version": "0.125.28",
|
|
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": {
|
package/npm-shrinkwrap.json
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "flowviant",
|
|
3
|
-
"version": "0.125.27",
|
|
4
|
-
"lockfileVersion": 3,
|
|
5
|
-
"requires": true,
|
|
6
|
-
"packages": {
|
|
7
|
-
"": {
|
|
8
|
-
"name": "flowviant",
|
|
9
|
-
"version": "0.125.27",
|
|
10
|
-
"license": "MIT",
|
|
11
|
-
"dependencies": {
|
|
12
|
-
"libsodium-wrappers-sumo": "^0.8.4",
|
|
13
|
-
"ws": "^8.18.0"
|
|
14
|
-
},
|
|
15
|
-
"bin": {
|
|
16
|
-
"flowviant": "bin/cli.mjs"
|
|
17
|
-
},
|
|
18
|
-
"devDependencies": {
|
|
19
|
-
"three": "0.180.0"
|
|
20
|
-
},
|
|
21
|
-
"engines": {
|
|
22
|
-
"node": ">=20"
|
|
23
|
-
}
|
|
24
|
-
},
|
|
25
|
-
"node_modules/libsodium-sumo": {
|
|
26
|
-
"version": "0.8.4",
|
|
27
|
-
"resolved": "https://registry.npmjs.org/libsodium-sumo/-/libsodium-sumo-0.8.4.tgz",
|
|
28
|
-
"integrity": "sha512-TMtHShQfVVsaxDygyapvUC3o7YsPgXa/hRWeIgzyFz6w5k/1hirGptCxp1U7XwW3rCskaTTYKgV10v86UiGgNw==",
|
|
29
|
-
"license": "ISC"
|
|
30
|
-
},
|
|
31
|
-
"node_modules/libsodium-wrappers-sumo": {
|
|
32
|
-
"version": "0.8.4",
|
|
33
|
-
"resolved": "https://registry.npmjs.org/libsodium-wrappers-sumo/-/libsodium-wrappers-sumo-0.8.4.tgz",
|
|
34
|
-
"integrity": "sha512-ql7hcgulKZ3ekfa2DGAogcCKsWU0diA/0nArz1CFzh93WQdb46/Kj18ka/Hifq6uA3Ush34Pc6vU/6HXeRwUkg==",
|
|
35
|
-
"license": "ISC",
|
|
36
|
-
"dependencies": {
|
|
37
|
-
"libsodium-sumo": "^0.8.0"
|
|
38
|
-
}
|
|
39
|
-
},
|
|
40
|
-
"node_modules/three": {
|
|
41
|
-
"version": "0.180.0",
|
|
42
|
-
"resolved": "https://registry.npmjs.org/three/-/three-0.180.0.tgz",
|
|
43
|
-
"integrity": "sha512-o+qycAMZrh+TsE01GqWUxUIKR1AL0S8pq7zDkYOQw8GqfX8b8VoCKYUoHbhiX5j+7hr8XsuHDVU6+gkQJQKg9w==",
|
|
44
|
-
"dev": true,
|
|
45
|
-
"license": "MIT"
|
|
46
|
-
},
|
|
47
|
-
"node_modules/ws": {
|
|
48
|
-
"version": "8.21.1",
|
|
49
|
-
"resolved": "https://registry.npmjs.org/ws/-/ws-8.21.1.tgz",
|
|
50
|
-
"integrity": "sha512-+0NTnW77fFN/DjQi6k/Sq/Yvk4Sgajw7urW8V+asjXnRgDs9gyGkdb7EzgfhA4goXsRIZKE28fzIXBHEzhuiWw==",
|
|
51
|
-
"license": "MIT",
|
|
52
|
-
"engines": {
|
|
53
|
-
"node": ">=10.0.0"
|
|
54
|
-
},
|
|
55
|
-
"peerDependencies": {
|
|
56
|
-
"bufferutil": "^4.0.1",
|
|
57
|
-
"utf-8-validate": ">=5.0.2"
|
|
58
|
-
},
|
|
59
|
-
"peerDependenciesMeta": {
|
|
60
|
-
"bufferutil": {
|
|
61
|
-
"optional": true
|
|
62
|
-
},
|
|
63
|
-
"utf-8-validate": {
|
|
64
|
-
"optional": true
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
}
|