flowviant 0.125.9 → 0.125.13
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 +22 -0
- package/bin/lib/agentFiles.mjs +1 -1
- package/bin/lib/agentMergeResolution.mjs +6 -2
- package/bin/lib/agentPlan.mjs +8 -0
- package/bin/lib/agentPrerequisiteContract.mjs +8 -0
- package/bin/lib/agentTaskKinds.mjs +4 -4
- package/bin/lib/agentTaskTypes.mjs +11 -7
- package/bin/lib/artifactContracts.mjs +23 -11
- package/bin/lib/artifactIteration.mjs +18 -0
- package/bin/lib/artifactPreview.mjs +2 -2
- package/bin/lib/artifactReporter.mjs +16 -3
- package/bin/lib/codexResearch.mjs +14 -1
- package/bin/lib/intermediateArtifacts.mjs +228 -0
- package/bin/lib/modelExport.mjs +19 -0
- package/bin/lib/modelExportProtocol.mjs +80 -0
- package/bin/lib/orchestratorPrompt.mjs +3 -2
- package/bin/lib/pinnedCapture.mjs +35 -7
- package/bin/lib/prompts.mjs +131 -9
- package/bin/lib/repositoryDelivery.mjs +1 -1
- package/bin/lib/resultInputs.mjs +83 -0
- package/bin/lib/testFindings.mjs +60 -0
- package/bin/lib/testFindingsContract.mjs +2 -0
- package/bin/lib/work.mjs +5 -5
- package/bin/lib/workAgentMerges.mjs +8 -8
- package/bin/lib/workAgentReview.mjs +24 -1
- package/bin/lib/workAgentTurnExecution.mjs +43 -8
- package/bin/lib/workAgentTurnOutcome.mjs +27 -6
- package/bin/lib/workAgentTurnShape.mjs +10 -8
- package/bin/lib/workAgentTurns.mjs +4 -0
- package/bin/lib/workAttachments.mjs +4 -2
- package/bin/lib/workLiveMessages.mjs +2 -2
- package/bin/lib/workSessionTurns.mjs +2 -0
- package/package.json +1 -1
|
@@ -43,7 +43,7 @@ export function createWorkAgentMerges({
|
|
|
43
43
|
baseDir,
|
|
44
44
|
gitNet,
|
|
45
45
|
baseRef,
|
|
46
|
-
|
|
46
|
+
runMergeCheck,
|
|
47
47
|
onRepoChanged,
|
|
48
48
|
landed,
|
|
49
49
|
agentRemoteAt,
|
|
@@ -157,7 +157,7 @@ export function createWorkAgentMerges({
|
|
|
157
157
|
if (job.repositoryMerge) {
|
|
158
158
|
if (!repositoryPlaces) throw new Error('Project repository worktrees are unavailable.');
|
|
159
159
|
const result = await mergeRepositoryPlace({ job, entries: repositoryPlaces.entries(place), baseDir,
|
|
160
|
-
check: (r) =>
|
|
160
|
+
check: (r) => runMergeCheck(agentId, r.wt, r),
|
|
161
161
|
progress: async (states, retry) => {
|
|
162
162
|
const response = await fleetRequest(fleetEndpoint('agent-merge-progress', FLEET_URL), {
|
|
163
163
|
body: { agentId, instance: DAEMON_INSTANCE, states, retry } });
|
|
@@ -195,12 +195,12 @@ export function createWorkAgentMerges({
|
|
|
195
195
|
});
|
|
196
196
|
return;
|
|
197
197
|
}
|
|
198
|
-
// The branch changed, so the previous check
|
|
199
|
-
//
|
|
200
|
-
//
|
|
201
|
-
//
|
|
202
|
-
//
|
|
203
|
-
const check = await
|
|
198
|
+
// The branch changed, so the previous check answered about a different
|
|
199
|
+
// tree. Re-run it before anything merges: a failed check sends the
|
|
200
|
+
// agent BACK to review. The CHECK ALONE (`runMergeCheck`): the earlier
|
|
201
|
+
// pre-review now reads as stale by `precheckSha`, and a fresh one would
|
|
202
|
+
// hold this approved merge behind a model call (2026-10-21).
|
|
203
|
+
const check = await runMergeCheck(agentId, wt);
|
|
204
204
|
if (check?.status === 'failed') {
|
|
205
205
|
await report({ agentId, ok: false, detail: check.text || check.output || 'The repository check failed after folding base.' });
|
|
206
206
|
return;
|
|
@@ -19,6 +19,8 @@ export function createWorkAgentReview({ runCheck, runPrecheck }) {
|
|
|
19
19
|
* `runCheck` directly; a second thing to run at the same moment is a second
|
|
20
20
|
* thing three call sites can forget.
|
|
21
21
|
*
|
|
22
|
+
* THE MERGE PATH IS NOT A REVIEW ENTRY (2026-10-21) — see `runMergeCheck`.
|
|
23
|
+
*
|
|
22
24
|
* THE CHECK FIRST, ALWAYS. It is a local command whose answer the board wants
|
|
23
25
|
* on the row immediately; the pre-review is a model call that may take
|
|
24
26
|
* minutes. Ordering them the other way would put a label behind a label.
|
|
@@ -42,5 +44,26 @@ export function createWorkAgentReview({ runCheck, runPrecheck }) {
|
|
|
42
44
|
return result;
|
|
43
45
|
};
|
|
44
46
|
|
|
45
|
-
|
|
47
|
+
/**
|
|
48
|
+
* THE CHECK ALONE, for the merge path (2026-10-21): the stale fold before an
|
|
49
|
+
* approved merge, each repository of a coordinated merge, and a merge
|
|
50
|
+
* resolve's settle. The check gates there — a failed one sends the agent
|
|
51
|
+
* back to Review — so it still runs. The pre-review does not: it labels and
|
|
52
|
+
* never blocks, yet run here it sat inside the place's writer lock ahead of
|
|
53
|
+
* the push and held an approved merge for up to its five-minute cap. One
|
|
54
|
+
* merge measured 13:40 check → 13:43 resolve settled → 13:43 check → 13:48
|
|
55
|
+
* landed, both waits a pre-review, the second wedged and posting nothing.
|
|
56
|
+
* A person already approved this work; a reading of it nobody is waiting on
|
|
57
|
+
* cannot be what the landing waits for. A merge that fails back to Review
|
|
58
|
+
* keeps its earlier reading, which the server marks stale by `precheckSha`.
|
|
59
|
+
*/
|
|
60
|
+
const runMergeCheck = async (agentId, wt, repository) => {
|
|
61
|
+
try {
|
|
62
|
+
return (await runCheck(agentId, wt, repository)) ?? { status: 'unknown' };
|
|
63
|
+
} catch {
|
|
64
|
+
return { status: 'unknown' };
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
return { runReviewEntry, runMergeCheck };
|
|
46
69
|
}
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { imageRevisionInstructions } from './artifactIteration.mjs';
|
|
2
|
+
import { measureTestFindings } from './testFindings.mjs';
|
|
3
|
+
import { fetchResultInputs } from './resultInputs.mjs';
|
|
1
4
|
import { agentWork } from './workActivity.mjs';
|
|
2
5
|
import { relayedText } from './prWorkflow.mjs';
|
|
3
6
|
import { measuredMergeResolution } from './agentMergeResolution.mjs';
|
|
@@ -44,7 +47,7 @@ import { runAgentWithInternalWaits } from './agentInternalWait.mjs';
|
|
|
44
47
|
import { brainFor } from './workBrain.mjs';
|
|
45
48
|
import { refreshRestartBase } from './workAgentRecovery.mjs';
|
|
46
49
|
import { begunTurnRefusal } from './workAgentTurnBegun.mjs';
|
|
47
|
-
import { agentTurnSettlement, interruptedTurnSettlement } from './workAgentTurnOutcome.mjs';
|
|
50
|
+
import { agentDeclaredDelivery, agentTurnSettlement, interruptedTurnSettlement } from './workAgentTurnOutcome.mjs';
|
|
48
51
|
import { ensureArtifactDir, ensureFenceScratch } from './artifacts.mjs';
|
|
49
52
|
import { agentBrowserHome } from './noWindowEnv.mjs';
|
|
50
53
|
import { agentTurnResume } from './workAgentResume.mjs';
|
|
@@ -56,6 +59,7 @@ import { ORCHESTRATED_POSTURES, orchestratesPosture } from './orchestratorPrompt
|
|
|
56
59
|
import { knownCodexModels } from './runtimeOfferings.mjs';
|
|
57
60
|
import { codexSubagentsSafe } from './runtimeCodex.mjs';
|
|
58
61
|
import { measuredRuntimeVersion } from './runtimeDetection.mjs';
|
|
62
|
+
import { createIntermediateArtifacts } from './intermediateArtifacts.mjs';
|
|
59
63
|
|
|
60
64
|
export function createAgentTurnExecution({
|
|
61
65
|
admit,
|
|
@@ -73,6 +77,7 @@ export function createAgentTurnExecution({
|
|
|
73
77
|
reportSessionWorktree,
|
|
74
78
|
artifacts,
|
|
75
79
|
runReviewEntry,
|
|
80
|
+
runMergeCheck,
|
|
76
81
|
workChildren,
|
|
77
82
|
agentChildren,
|
|
78
83
|
postAgentTrace,
|
|
@@ -81,6 +86,8 @@ export function createAgentTurnExecution({
|
|
|
81
86
|
postAgentParked,
|
|
82
87
|
fetchAgentFiles,
|
|
83
88
|
makeLiveMessages = createLiveMessages,
|
|
89
|
+
runTurnImpl = runTurn,
|
|
90
|
+
exportModel,
|
|
84
91
|
}) {
|
|
85
92
|
const reportDelivery = createDeliveryReporter();
|
|
86
93
|
/** How long the final flush may hold the settle. Bounded because the settle
|
|
@@ -272,6 +279,11 @@ export function createAgentTurnExecution({
|
|
|
272
279
|
* No key is no call: a turn without files touches nothing here, and its
|
|
273
280
|
* prompt is byte-for-byte what it was.
|
|
274
281
|
*/
|
|
282
|
+
await fetchResultInputs(wt, job.resultInputs);
|
|
283
|
+
const intermediate = createIntermediateArtifacts({ wt, agentId,
|
|
284
|
+
path: sessionMetaPath(wt, 'flowviant-agent-artifacts', agentId),
|
|
285
|
+
...(exportModel ? { exportPage: exportModel } : {}) });
|
|
286
|
+
const intermediateFiles = intermediate.inputs();
|
|
275
287
|
const files =
|
|
276
288
|
Array.isArray(job.attachments) && job.attachments.length > 0
|
|
277
289
|
? await fetchAgentFiles(wt, job.attachments)
|
|
@@ -376,7 +388,7 @@ export function createAgentTurnExecution({
|
|
|
376
388
|
const spawnedChildren = new Set();
|
|
377
389
|
/** The CLI, unless a person already asked to stop this turn (Interrupt
|
|
378
390
|
* now): then nothing starts — no fresh retry, no continuation. */
|
|
379
|
-
const runCli = (args) => (run.interrupted ? Promise.resolve('') :
|
|
391
|
+
const runCli = (args) => (run.interrupted ? Promise.resolve('') : runTurnImpl(args));
|
|
380
392
|
/**
|
|
381
393
|
* WHAT THIS TURN SPENT, AS THE CLI COUNTED IT (2026-09-19).
|
|
382
394
|
*
|
|
@@ -408,10 +420,15 @@ export function createAgentTurnExecution({
|
|
|
408
420
|
/** The agent's artifact directory as it stood before this turn — the
|
|
409
421
|
* tab lane's rule, in the agent's own worktree (2026-09-22). */
|
|
410
422
|
const artifactsBefore = beforeArtifacts(wt);
|
|
411
|
-
|
|
423
|
+
let artifactInstructions = getArtifactsAccepted()
|
|
412
424
|
? await artifacts.prepare?.({ placeDir: wt, before: artifactsBefore, agentId }) ?? '' : '';
|
|
425
|
+
if (job.imageRevision) {
|
|
426
|
+
artifactsBefore.imageRevision = job.imageRevision;
|
|
427
|
+
artifactInstructions += '\n\n' + imageRevisionInstructions(job.imageRevision, job.resultInputs);
|
|
428
|
+
}
|
|
413
429
|
let projectTools = null;
|
|
414
430
|
let orchestrator = null;
|
|
431
|
+
let intermediateDelivery = null;
|
|
415
432
|
try {
|
|
416
433
|
if (orchestrating) {
|
|
417
434
|
orchestrator = await createOrchestrator({ turnId, runtime: rt, trace, admit,
|
|
@@ -433,15 +450,17 @@ export function createAgentTurnExecution({
|
|
|
433
450
|
format: async (followup) => {
|
|
434
451
|
orchestrator?.note(followup);
|
|
435
452
|
const attached = followup.attachments?.length ? await fetchAgentFiles(wt, followup.attachments.map((file) => ({ ...file, from: 'message' }))) : { message: [], card: [] };
|
|
436
|
-
return agentFollowupKickoff(job, followup, { messageFiles: attached.message, cardFiles: files.card, orchestrated: Boolean(orchestrator) });
|
|
453
|
+
return agentFollowupKickoff(job, followup, { messageFiles: attached.message, cardFiles: files.card, intermediateFiles, orchestrated: Boolean(orchestrator) });
|
|
437
454
|
},
|
|
438
455
|
})) } : {}),
|
|
439
456
|
// The kickoff and the system prompt, composed by the agent lane's
|
|
440
457
|
// shape (workAgentTurnShape.mjs) from what this run measured.
|
|
441
458
|
researchHandoff: job.researchHandoff,
|
|
459
|
+
resultInputs: job.resultInputs,
|
|
460
|
+
intermediateFiles,
|
|
442
461
|
researchIdentity: { projectId: PROJECT_ID, agentId, turnId, taskId: job.task?.id ?? job.repositoryTaskId },
|
|
443
|
-
prompt: agentKickoff(job, { cardFiles: files.card, messageFiles: files.message }),
|
|
444
|
-
system: agentTurnSystem({ taskKind, 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 }),
|
|
462
|
+
prompt: agentKickoff(job, { cardFiles: files.card, messageFiles: files.message, intermediateFiles }),
|
|
463
|
+
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 }),
|
|
445
464
|
agentTools: projectTools,
|
|
446
465
|
writableDirs: repositoryRun?.writableDirs,
|
|
447
466
|
...((tools.envNeeded || repositoryRun?.tool || orchestrator) ? { mcpEnv: { ...tools.env, ...(repositoryRun?.tool?.env ?? {}), ...(orchestrator?.env ?? {}) } } : {}),
|
|
@@ -724,7 +743,18 @@ export function createAgentTurnExecution({
|
|
|
724
743
|
void reportSessionWorktree(place).catch(() => {});
|
|
725
744
|
// A file card delivers uploaded bytes, not an in-memory retry. Otherwise
|
|
726
745
|
// its dependent card can start while Review has nothing to show.
|
|
727
|
-
|
|
746
|
+
if (!run.interrupted && agentDeclaredDelivery(out, lastAnswer)) {
|
|
747
|
+
try { intermediateDelivery = await intermediate.prepare({ job, turnId, taskKind, before: artifactsBefore, signal: setupController.signal }); }
|
|
748
|
+
catch (error) {
|
|
749
|
+
if (run.interrupted) throw error;
|
|
750
|
+
// The failed page still has its preview/files for repair. Reporting
|
|
751
|
+
// those bytes cannot turn a failed export into a delivered card.
|
|
752
|
+
try { await artifacts.report({ placeDir: wt, before: artifactsBefore, agentId, turnId, requireStored: kind.artifact != null }); }
|
|
753
|
+
catch (uploadError) { throw new Error(`${error.message}; artifact reporting also failed: ${uploadError.message}`); }
|
|
754
|
+
throw error;
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
await artifacts.report({ placeDir: wt, before: intermediateDelivery?.uploadBefore ?? artifactsBefore, agentId, turnId, requireStored: kind.artifact != null });
|
|
728
758
|
}
|
|
729
759
|
|
|
730
760
|
|
|
@@ -757,8 +787,13 @@ export function createAgentTurnExecution({
|
|
|
757
787
|
artifactsBefore,
|
|
758
788
|
branch,
|
|
759
789
|
wt,
|
|
790
|
+
modelExports: intermediateDelivery?.modelExports,
|
|
760
791
|
});
|
|
792
|
+
if (body.outcome === 'delivered') intermediateDelivery?.commit();
|
|
761
793
|
if (res) setRuntimeLimit(PROJECT_ID, rt, null);
|
|
794
|
+
if (body.outcome === 'delivered' && job.task?.taskType === 'tests' && res?.testFindings) {
|
|
795
|
+
body.testFindings = measureTestFindings(res.testFindings, repositoryRun?.sourcesNow() ?? [{ wt, baseRef: baseRef(), id: job.repos?.length === 1 ? job.repos[0] : null }], envScrub);
|
|
796
|
+
}
|
|
762
797
|
if (limit) {
|
|
763
798
|
setRuntimeLimit(PROJECT_ID, rt, limit);
|
|
764
799
|
// Every agent ON THIS CLI parks, because that account is shared: one
|
|
@@ -777,7 +812,7 @@ export function createAgentTurnExecution({
|
|
|
777
812
|
const rows = repositoryRun?.sourcesNow() ?? [{ wt }];
|
|
778
813
|
for (const r of rows) {
|
|
779
814
|
const head = gitOrNull(['rev-parse', 'HEAD'], r.wt);
|
|
780
|
-
const check = await
|
|
815
|
+
const check = await runMergeCheck(agentId, r.wt, repositoryRun ? r : undefined);
|
|
781
816
|
if (check?.status === 'failed') {
|
|
782
817
|
body.mergeResolved = false;
|
|
783
818
|
body.mergeDetail = relayedText(check.text || check.output || 'The repository check failed during merge recovery.').slice(0, 2000);
|
|
@@ -33,8 +33,14 @@ import { scrub as envScrub } from './uplinkScrub.mjs';
|
|
|
33
33
|
import { usableImageArtifact } from './imageArtifact.mjs';
|
|
34
34
|
import { withoutTurnDeclaration } from './turnDeclaration.mjs';
|
|
35
35
|
import { ANSWER_DELIVERED_USES, researchUseOf, withResearchHeader } from './researchUse.mjs';
|
|
36
|
+
import { measuredModelExports } from './intermediateArtifacts.mjs';
|
|
36
37
|
|
|
37
|
-
|
|
38
|
+
/** Materialization runs only for a declared delivery; settlement remains the
|
|
39
|
+
* sole parser and the sole authority deciding whether it really delivered. */
|
|
40
|
+
export const agentDeclaredDelivery = (out, lastAnswer) =>
|
|
41
|
+
((lastAnswer && parseTurnResult(lastAnswer)) || parseTurnResult(out))?.outcome === 'delivered';
|
|
42
|
+
|
|
43
|
+
export function agentTurnSettlement({ turnId, job, kind, out, lastAnswer, usage, model = null, commits, artifactsBefore, branch, wt, modelExports = [] }) {
|
|
38
44
|
const res = (lastAnswer && parseTurnResult(lastAnswer)) || parseTurnResult(out);
|
|
39
45
|
/**
|
|
40
46
|
* A LIMIT IS ONLY A LIMIT WHEN THE TURN PRODUCED NOTHING.
|
|
@@ -94,7 +100,7 @@ export function agentTurnSettlement({ turnId, job, kind, out, lastAnswer, usage,
|
|
|
94
100
|
* Its product is under `.flowviant/artifacts/`, and the kind's table
|
|
95
101
|
* (agentTaskKinds.mjs) says which file proves it: a `.html` mockup for
|
|
96
102
|
* design, a `.html` deck, a `.md` write-up for research, and a `.html`
|
|
97
|
-
* page for a 3D model
|
|
103
|
+
* page for a 3D model plus its measured page-bound local GLB export. A turn that
|
|
98
104
|
* says "delivered" without having written one would land the agent in Review
|
|
99
105
|
* with nothing to look at and a summary describing a page that does not
|
|
100
106
|
* exist — so it settles `nothing` with the measured sentence, and the
|
|
@@ -135,7 +141,8 @@ export function agentTurnSettlement({ turnId, job, kind, out, lastAnswer, usage,
|
|
|
135
141
|
&& (kind.posture !== 'image' || usableImageArtifact(e)));
|
|
136
142
|
const standing = scanArtifacts(wt);
|
|
137
143
|
const wrote = has(changedArtifacts(artifactsBefore, standing));
|
|
138
|
-
const
|
|
144
|
+
const exported = kind.exportModel && measuredModelExports(wt, modelExports);
|
|
145
|
+
const present = wrote || exported || ((job.kind !== 'task' || job.redo === true) && has(standing));
|
|
139
146
|
if (!present) {
|
|
140
147
|
return report({
|
|
141
148
|
turnId,
|
|
@@ -147,6 +154,11 @@ export function agentTurnSettlement({ turnId, job, kind, out, lastAnswer, usage,
|
|
|
147
154
|
worktree: wt,
|
|
148
155
|
});
|
|
149
156
|
}
|
|
157
|
+
if (kind.exportModel && !exported) {
|
|
158
|
+
return report({ turnId, outcome: 'nothing',
|
|
159
|
+
answer: 'The model page has no verified local .glb export. Retry this producer card to export its exact page before dependent work starts; no manual download or Library keeping is required.',
|
|
160
|
+
...(usage ? { usage } : {}), branch, worktree: wt });
|
|
161
|
+
}
|
|
150
162
|
}
|
|
151
163
|
const prerequisite = res.prerequisite;
|
|
152
164
|
const cannotRequest = prerequisite && job.prerequisitesAccepted !== true
|
|
@@ -159,14 +171,23 @@ export function agentTurnSettlement({ turnId, job, kind, out, lastAnswer, usage,
|
|
|
159
171
|
turnId,
|
|
160
172
|
...(prerequisite && !cannotRequest ? { prerequisite } : {}),
|
|
161
173
|
outcome: res.outcome,
|
|
162
|
-
answer: cannotRequest ?? envScrub(kind.posture === 'research' && researchUse
|
|
163
|
-
? withResearchHeader(researchUse, res.answer ?? '') : res.answer ?? '')
|
|
174
|
+
answer: cannotRequest ?? envScrub((kind.posture === 'research' && researchUse
|
|
175
|
+
? withResearchHeader(researchUse, res.answer ?? '') : res.answer ?? '') +
|
|
176
|
+
(job.task?.taskType === 'tests' && res.raised?.length ? '\n\nAdditional CLI-reported findings (not filed as fixes):\n' + res.raised.map(r => `${r.title}\n${r.brief ?? ''}`).join('\n\n') : '')),
|
|
177
|
+
// CLI-authored evidence travels separately from the short answer and
|
|
178
|
+
// measured commits/checks. Older turns simply send no report.
|
|
179
|
+
...(kind.posture === 'build' && res.outcome === 'delivered' && res.verification
|
|
180
|
+
? { verification: envScrub(res.verification) } : {}),
|
|
181
|
+
...(kind.posture === 'build' && res.outcome === 'delivered' && res.handover
|
|
182
|
+
? { handover: envScrub(res.handover) } : {}),
|
|
183
|
+
...(kind.posture === 'build' && res.outcome === 'delivered' && res.automation
|
|
184
|
+
? { automation: envScrub(res.automation) } : {}),
|
|
164
185
|
...(commits.length ? { commits } : {}),
|
|
165
186
|
// SCRUBBED like the answer beside it: a raised card lands in the
|
|
166
187
|
// project doc every member reads, and a brief quoting the `.env` the
|
|
167
188
|
// agent just read ("value sk_live_… is logged in pay.ts") went out
|
|
168
189
|
// verbatim while the same value in the summary was redacted.
|
|
169
|
-
...(res.raised?.length
|
|
190
|
+
...(job.task?.taskType !== 'tests' && res.raised?.length
|
|
170
191
|
? {
|
|
171
192
|
raised: res.raised.map((r) => ({
|
|
172
193
|
title: envScrub(r.title).slice(0, 300),
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
import { AGENT_TASK_KICKOFF, AGENT_HUMAN_KICKOFF, SYSTEM_AGENT_FOR, withProjectContext } from './prompts.mjs';
|
|
26
26
|
import { INTERNAL_WAIT_CONTRACT } from './agentInternalWait.mjs';
|
|
27
27
|
import { orchestratorContract } from './orchestratorPrompt.mjs';
|
|
28
|
+
import { intermediateArtifactContext } from './intermediateArtifacts.mjs';
|
|
28
29
|
|
|
29
30
|
/** What every kickoff of this job is handed: the agent, the card and its
|
|
30
31
|
* discussion, the job's context handoffs, the card's files, the place in
|
|
@@ -35,6 +36,7 @@ const shared = (job, cardFiles) => ({
|
|
|
35
36
|
cardThread: job.cardThread,
|
|
36
37
|
investigationContext: job.investigationContext,
|
|
37
38
|
researchContext: job.researchContext,
|
|
39
|
+
resultContext: job.resultContext,
|
|
38
40
|
cardFiles,
|
|
39
41
|
position: job.position ?? 1,
|
|
40
42
|
total: job.total ?? 1,
|
|
@@ -42,8 +44,8 @@ const shared = (job, cardFiles) => ({
|
|
|
42
44
|
|
|
43
45
|
/** The turn's first prompt: the card, on a task turn; the person's words
|
|
44
46
|
* otherwise. `cardFiles` and `messageFiles` are the files already on disk. */
|
|
45
|
-
export const agentKickoff = (job, { cardFiles, messageFiles }) =>
|
|
46
|
-
job.kind === 'task' && job.task
|
|
47
|
+
export const agentKickoff = (job, { cardFiles, messageFiles, intermediateFiles }) =>
|
|
48
|
+
(job.kind === 'task' && job.task
|
|
47
49
|
? AGENT_TASK_KICKOFF({
|
|
48
50
|
// The card's discussion (0.106.0), printed inside THE CARD by
|
|
49
51
|
// the same builder the run's stash wrote down — and the files
|
|
@@ -62,31 +64,31 @@ export const agentKickoff = (job, { cardFiles, messageFiles }) =>
|
|
|
62
64
|
// The files (0.112.0): the person's inside their words, the
|
|
63
65
|
// card's at the foot of its discussion.
|
|
64
66
|
messageFiles,
|
|
65
|
-
});
|
|
67
|
+
})) + intermediateArtifactContext(intermediateFiles);
|
|
66
68
|
|
|
67
69
|
/** A live message sent while the turn runs, as the CLI is handed it — on an
|
|
68
70
|
* orchestrated card with its id, so the route it takes is reported. */
|
|
69
|
-
export const agentFollowupKickoff = (job, followup, { cardFiles, messageFiles, orchestrated }) =>
|
|
71
|
+
export const agentFollowupKickoff = (job, followup, { cardFiles, messageFiles, intermediateFiles, orchestrated }) =>
|
|
70
72
|
AGENT_HUMAN_KICKOFF({
|
|
71
73
|
...shared(job, cardFiles),
|
|
72
74
|
message: followup.body + (followup.libraryPrompt ?? '') + (orchestrated ? `\nMessage id: ${followup.id}. Choose and report its route with card_orchestrator.route_message.` : ''),
|
|
73
75
|
askedByName: followup.askedByName,
|
|
74
76
|
messageFiles,
|
|
75
|
-
});
|
|
77
|
+
}) + intermediateArtifactContext(intermediateFiles);
|
|
76
78
|
|
|
77
79
|
/** The turn's system prompt, in its fixed section order. `orchestration` is
|
|
78
80
|
* null on a solo run, else what the contract names: `{ posture, runtime,
|
|
79
81
|
* model, effort }` (orchestratorPrompt.mjs). */
|
|
80
|
-
export function agentTurnSystem({ taskKind, runtimeLabel, prerequisitesAccepted, knowledgeDir, artifactsAccepted, toolsNote, artifactInstructions, toolInstructions, repositoryRun, orchestration = null }) {
|
|
82
|
+
export function agentTurnSystem({ taskKind, taskType, runtimeLabel, prerequisitesAccepted, knowledgeDir, artifactsAccepted, toolsNote, artifactInstructions, toolInstructions, repositoryRun, orchestration = null }) {
|
|
81
83
|
// The knowledge paragraph, when this box holds a library — the same
|
|
82
84
|
// composer the tabs use, so an agent and a tab can never be told two
|
|
83
85
|
// different things about the same directory.
|
|
84
86
|
// Spoken to the CLI that runs it (0.115.0): a mockup on Codex is
|
|
85
87
|
// told it is Codex, never Claude.
|
|
86
|
-
return withProjectContext(SYSTEM_AGENT_FOR(taskKind, runtimeLabel, { prerequisitesAccepted }), {
|
|
88
|
+
return withProjectContext(SYSTEM_AGENT_FOR(taskKind, runtimeLabel, { prerequisitesAccepted, taskType }), {
|
|
87
89
|
knowledgeDir,
|
|
88
90
|
// ARTIFACTS (0.94.0), while the server can show one — an agent's
|
|
89
91
|
// land on its page, under the facts row.
|
|
90
92
|
artifacts: artifactsAccepted,
|
|
91
|
-
}) + (toolsNote ? `\n\n${toolsNote}` : '') + (artifactInstructions ? `\n\n${artifactInstructions}` : '') + `\n\n${INTERNAL_WAIT_CONTRACT}` + (toolInstructions ? `\n\n${toolInstructions}` : '') + (repositoryRun ? `\n\n${repositoryRun.note}\nRepository command argv: ${JSON.stringify([repositoryRun.tool?.config.command, ...(repositoryRun.tool?.config.args.slice(0, -1) ?? []), "__repository", "<name>"])}` : '') + (orchestration ? `\n\n${orchestratorContract(orchestration)}` : '');
|
|
93
|
+
}) + (toolsNote ? `\n\n${toolsNote}` : '') + (artifactInstructions ? `\n\n${artifactInstructions}` : '') + `\n\n${INTERNAL_WAIT_CONTRACT}` + (toolInstructions ? `\n\n${toolInstructions}` : '') + (repositoryRun ? `\n\n${repositoryRun.note}\nRepository command argv: ${JSON.stringify([repositoryRun.tool?.config.command, ...(repositoryRun.tool?.config.args.slice(0, -1) ?? []), "__repository", "<name>"])}` : '') + (orchestration ? `\n\n${orchestratorContract({ ...orchestration, taskType })}` : '');
|
|
92
94
|
}
|
|
@@ -48,6 +48,7 @@ export function createWorkAgentTurns({
|
|
|
48
48
|
reportSessionWorktree,
|
|
49
49
|
artifacts,
|
|
50
50
|
runReviewEntry,
|
|
51
|
+
runMergeCheck,
|
|
51
52
|
publishAgentBranch,
|
|
52
53
|
stopAgentPublishing = () => {},
|
|
53
54
|
admit,
|
|
@@ -59,6 +60,7 @@ export function createWorkAgentTurns({
|
|
|
59
60
|
makeBrief = createAgentBriefRunner({ admit, workChildren }),
|
|
60
61
|
/** The run's live-message relay (workLiveMessages.mjs); a test's stand-in. */
|
|
61
62
|
makeLiveMessages,
|
|
63
|
+
exportModel,
|
|
62
64
|
}) {
|
|
63
65
|
const reportDelivery = createDeliveryReporter();
|
|
64
66
|
const {
|
|
@@ -133,6 +135,8 @@ export function createWorkAgentTurns({
|
|
|
133
135
|
reportSessionWorktree,
|
|
134
136
|
artifacts,
|
|
135
137
|
runReviewEntry,
|
|
138
|
+
runMergeCheck,
|
|
139
|
+
exportModel,
|
|
136
140
|
workChildren,
|
|
137
141
|
agentChildren,
|
|
138
142
|
postAgentTrace,
|
|
@@ -17,7 +17,7 @@ import { excludeInWorktree } from './git.mjs';
|
|
|
17
17
|
import { FLOWVIANT_OWN_PATHS } from './knowledgeLibrary.mjs';
|
|
18
18
|
import { safeFileName } from './safeFileName.mjs';
|
|
19
19
|
import { fleetEndpoint } from './fleetWire.mjs';
|
|
20
|
-
import { readAgentFiles } from './agentFiles.mjs';
|
|
20
|
+
import { AGENT_FILES_PER_MESSAGE_MAX, readAgentFiles } from './agentFiles.mjs';
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
23
|
* ONE FILE'S CEILING, for both lanes (0.113.0, the owner 2026-09-29: "20 MB
|
|
@@ -27,6 +27,8 @@ import { readAgentFiles } from './agentFiles.mjs';
|
|
|
27
27
|
* is what 0.112.0's 10 MB did to anything bigger, and why the server floors a
|
|
28
28
|
* file over 10 MB at this release (`DAEMON_LARGE_FILES_MIN`).
|
|
29
29
|
*/
|
|
30
|
+
export const SESSION_ATTACHMENTS_PER_TURN_MAX = AGENT_FILES_PER_MESSAGE_MAX;
|
|
31
|
+
|
|
30
32
|
export const ATTACHMENT_MAX_BYTES = 20 * 1024 * 1024;
|
|
31
33
|
|
|
32
34
|
export function createWorkAttachments() {
|
|
@@ -206,7 +208,7 @@ export function createWorkAttachments() {
|
|
|
206
208
|
*/
|
|
207
209
|
const fetchTabFiles = async (wt, attachments) => {
|
|
208
210
|
if (!Array.isArray(attachments) || attachments.length === 0) return [];
|
|
209
|
-
const list = attachments.slice(0,
|
|
211
|
+
const list = attachments.slice(0, SESSION_ATTACHMENTS_PER_TURN_MAX);
|
|
210
212
|
const landed = await landFiles(wt, list, 'attachment', false);
|
|
211
213
|
return list.map((a, i) => (landed[i] ? { path: landed[i] } : { missed: safeUploadName(a?.name) }));
|
|
212
214
|
};
|
|
@@ -6,12 +6,12 @@ import { readFileSync, writeFileSync, renameSync, rmSync } from 'node:fs';
|
|
|
6
6
|
import { randomUUID } from 'node:crypto';
|
|
7
7
|
import { fleetRequest } from './fleetPost.mjs';
|
|
8
8
|
import { fleetEndpoint, isConsideredRefusal } from './fleetWire.mjs';
|
|
9
|
-
import { DAEMON_INSTANCE, FLEET_URL } from './config.mjs';
|
|
9
|
+
import { DAEMON_INSTANCE, FLEET_URL, VERSION } from './config.mjs';
|
|
10
10
|
import { messageIdsForSession } from './claudeSessions.mjs';
|
|
11
11
|
|
|
12
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
|
-
const identity = { scope, turnId, instance: DAEMON_INSTANCE, ...(attempt !== undefined ? { attempt } : {}) };
|
|
14
|
+
const identity = { scope, turnId, daemonVersion: VERSION, instance: DAEMON_INSTANCE, ...(attempt !== undefined ? { attempt } : {}) };
|
|
15
15
|
const file = sessionMetaPath(cwd, 'flowviant-live-messages', turnId);
|
|
16
16
|
let sessionId = null;
|
|
17
17
|
let timer = null;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { fetchResultInputs } from './resultInputs.mjs';
|
|
1
2
|
import { sessionWork, reportWorkOutcome } from './workActivity.mjs';
|
|
2
3
|
/**
|
|
3
4
|
* A WORKBENCH TAB'S TURN — offered, admitted, placed, adopted, pinned to its
|
|
@@ -693,6 +694,7 @@ export function createWorkSessionTurns({
|
|
|
693
694
|
// Files first, then the message that references them: the agent
|
|
694
695
|
// must be able to open what it is being told about. Only the ones
|
|
695
696
|
// that actually landed are named.
|
|
697
|
+
await fetchResultInputs(dir.wt, job.resultInputs);
|
|
696
698
|
const filesNote = tabFilesNote(await fetchTabFiles(dir.wt, job.attachments));
|
|
697
699
|
const message = [job.body, filesNote, adoptNote, carryNote]
|
|
698
700
|
.filter(Boolean)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "flowviant",
|
|
3
|
-
"version": "0.125.
|
|
3
|
+
"version": "0.125.13",
|
|
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": {
|