flowviant 0.125.24 → 0.125.26
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 +16 -2
- package/bin/lib/artifactReporter.mjs +8 -4
- package/bin/lib/intermediateArtifacts.mjs +37 -12
- package/bin/lib/uiReview.mjs +1 -1
- package/bin/lib/uiReviewArtifacts.mjs +30 -0
- package/bin/lib/uiReviewBrowser.mjs +15 -3
- package/bin/lib/workAgentTurnExecution.mjs +5 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -328,7 +328,19 @@ pinned because [npm 12 removed shrinkwrap support](https://github.com/npm/cli/bl
|
|
|
328
328
|
and omits it from tarballs; npm 12 consumers also ignore that lockfile.
|
|
329
329
|
No sibling repository or separate app deploy key is required.
|
|
330
330
|
|
|
331
|
-
###
|
|
331
|
+
### UI review evidence retention (0.125.26)
|
|
332
|
+
|
|
333
|
+
Review reports and screenshots stay attached to their run without filling the
|
|
334
|
+
same-agent producer receipt. Legacy receipts migrate on retry without deleting
|
|
335
|
+
evidence. Identical captures within one review share a file and retain their
|
|
336
|
+
scenario, viewport, phase and hash references. Producer receipts use the shared
|
|
337
|
+
agent storage limit and report cumulative overflow before upload.
|
|
338
|
+
|
|
339
|
+
0.125.26 includes the npm dependency shrinkwrap with the pinned npm 11
|
|
340
|
+
publisher. The earlier 0.125.25 npm package contains the same runtime fix but
|
|
341
|
+
was published by npm 12 without that lockfile.
|
|
342
|
+
|
|
343
|
+
### Agent worktree startup and restart protection (0.125.24)
|
|
332
344
|
|
|
333
345
|
Card launches verify their exact Git worktree and repository before starting,
|
|
334
346
|
including after tool preparation. Fresh empty leftover directories are
|
|
@@ -338,7 +350,9 @@ retirement and manual fast-forwarding honor live CLI PID/start-time receipts
|
|
|
338
350
|
across daemon restarts. Existing begun work still needs its worktree, surviving
|
|
339
351
|
branch or published recovery copy; restarting the daemon alone does not reset
|
|
340
352
|
a stuck board agent. No new wire floor or database migration is required.
|
|
341
|
-
|
|
353
|
+
Published on npm and the standalone download channel on 2026-10-26. Running
|
|
354
|
+
daemons receive the version signal from the app and update when idle, unless
|
|
355
|
+
automatic updates are disabled.
|
|
342
356
|
|
|
343
357
|
### Staged artifact handoff revisions (0.125.18)
|
|
344
358
|
|
|
@@ -297,7 +297,7 @@ export function createArtifactReporter({
|
|
|
297
297
|
|
|
298
298
|
/** Upload what this turn changed in `placeDir`. Resolves to the number of
|
|
299
299
|
* bodies SENT this call (delivered or held), for the tests and the log. */
|
|
300
|
-
const report = async ({ placeDir, before, sessionId, agentId, turnId, idle = false, requireStored = false, includeNames = null, requiredOutputs = null, referenceCapture = false }) => {
|
|
300
|
+
const report = async ({ placeDir, before, sessionId, agentId, turnId, idle = false, requireStored = false, includeNames = null, requiredOutputs = null, evidenceNames = [], referenceCapture = false }) => {
|
|
301
301
|
if (!placeDir || (!sessionId && !agentId)) return 0;
|
|
302
302
|
const changes = iterationChanges(placeDir, before);
|
|
303
303
|
const protectedNames = imageRevisionProtection(before?.stored, before?.imageRevision);
|
|
@@ -334,9 +334,13 @@ export function createArtifactReporter({
|
|
|
334
334
|
// discovery window. Re-walk safely, then verify the built bytes against
|
|
335
335
|
// the prepared version before sending anything to storage.
|
|
336
336
|
const discovered = idle ? [] : listChanged(placeDir, before);
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
337
|
+
// Verification evidence is explicitly selected too, but never becomes a
|
|
338
|
+
// required producer input. Multiple review attempts can exceed the normal
|
|
339
|
+
// discovery window; retain their reports and captures on the same turn.
|
|
340
|
+
const selected = new Set([...required.keys(), ...evidenceNames]);
|
|
341
|
+
const entries = selected.size
|
|
342
|
+
? [...scanArtifacts(placeDir, { all: true, includeNames: selected }),
|
|
343
|
+
...discovered.filter(e => !selected.has(e.name) && (!required.size || !artifactTypeFor(e.name)))]
|
|
340
344
|
: discovered;
|
|
341
345
|
const changed = idle ? [] : entries.filter((e) =>
|
|
342
346
|
!removing.has(artifactStoredName(e.name)) && (!includeNames || includeNames.has(e.name)));
|
|
@@ -11,6 +11,7 @@ import { ensureRealSubdir, realDirState } from './ownedDirs.mjs';
|
|
|
11
11
|
import { scrub, secretIn } from './uplinkScrub.mjs';
|
|
12
12
|
import { exportModelPage } from './modelExport.mjs';
|
|
13
13
|
import { usableModelGlb } from './modelExportProtocol.mjs';
|
|
14
|
+
import { uiReviewEvidenceNames } from './uiReviewArtifacts.mjs';
|
|
14
15
|
|
|
15
16
|
const hash = bytes => createHash('sha256').update(bytes).digest('hex');
|
|
16
17
|
const safeName = name => typeof name === 'string' && name.length <= 400 &&
|
|
@@ -18,6 +19,8 @@ const safeName = name => typeof name === 'string' && name.length <= 400 &&
|
|
|
18
19
|
const copyPath = (sha256, name) => `.flowviant/artifacts/.handoff/${sha256}/${name}`;
|
|
19
20
|
const taskOf = job => job.task?.id ?? job.repositoryTaskId ?? null;
|
|
20
21
|
|
|
22
|
+
export class IntermediateArtifactCapacityError extends Error {}
|
|
23
|
+
|
|
21
24
|
export function readIntermediateFile(wt, relative, max = ARTIFACT_BINARY_MODEL_MAX_BYTES) {
|
|
22
25
|
if (!safeName(relative) || !relative.startsWith('.flowviant/artifacts/')) throw new Error('Unsafe intermediate artifact path.');
|
|
23
26
|
const parts = dirname(relative).split('/');
|
|
@@ -99,9 +102,22 @@ export function createIntermediateArtifacts({ wt, agentId, path, exportPage = ex
|
|
|
99
102
|
let state;
|
|
100
103
|
try { state = JSON.parse(readFileSync(path, 'utf8')); }
|
|
101
104
|
catch (error) { if (error.code === 'ENOENT') return { version: 1, agentId, files: [], pending: null }; throw new Error('The intermediate artifact receipt could not be read. Restore it before retrying.'); }
|
|
102
|
-
if (state.version !== 1 || state.agentId !== agentId || !Array.isArray(state.files) || state.files.length >
|
|
105
|
+
if (state.version !== 1 || state.agentId !== agentId || !Array.isArray(state.files) || state.files.length > ARTIFACT_MAX_PER_AGENT ||
|
|
103
106
|
state.files.some(f => !safeName(f.name) || f.path !== copyPath(f.sha256, f.name) || !/^[a-f0-9]{64}$/.test(f.sha256)))
|
|
104
107
|
throw new Error('The intermediate artifact receipt has invalid identity or paths.');
|
|
108
|
+
// Older daemons put verification reports and captures in this producer
|
|
109
|
+
// receipt. Migrate the references, never delete the original evidence.
|
|
110
|
+
const evidence = uiReviewEvidenceNames(state.files, ref => {
|
|
111
|
+
let bytes;
|
|
112
|
+
try { bytes = readIntermediateFile(wt, ref.path, artifactMaxBytesFor(ref.name)); }
|
|
113
|
+
catch (error) {
|
|
114
|
+
if (error.code !== 'ENOENT' || realDirState(privateFiles) !== 'dir') throw error;
|
|
115
|
+
bytes = readRegular(join(privateFiles, ref.sha256), artifactMaxBytesFor(ref.name));
|
|
116
|
+
}
|
|
117
|
+
if (hash(bytes) !== ref.sha256) throw new Error('Review receipt hash mismatch');
|
|
118
|
+
return bytes;
|
|
119
|
+
});
|
|
120
|
+
state.files = state.files.filter(ref => !evidence.has(ref.name));
|
|
105
121
|
return state;
|
|
106
122
|
};
|
|
107
123
|
const save = state => {
|
|
@@ -145,7 +161,12 @@ export function createIntermediateArtifacts({ wt, agentId, path, exportPage = ex
|
|
|
145
161
|
const state = load(), taskId = taskOf(job);
|
|
146
162
|
const identity = { agentId, taskId, turnId, taskKind };
|
|
147
163
|
const standing = scanArtifacts(wt, { all: true });
|
|
148
|
-
|
|
164
|
+
const evidence = uiReviewEvidenceNames(standing, entry => readIntermediateFile(wt, `.flowviant/artifacts/${entry.name}`, artifactMaxBytesFor(entry.name)));
|
|
165
|
+
const changed = changedArtifacts(before, standing);
|
|
166
|
+
const evidenceNames = [...new Set([...changed.map(entry => entry.name), ...(state.pending?.sources ?? []).map(ref => ref.name)].filter(name => evidence.has(name)))];
|
|
167
|
+
let producerChanges = changed.filter(entry => !evidence.has(entry.name));
|
|
168
|
+
if (state.pending && !state.pending.recovery)
|
|
169
|
+
state.pending.sources = state.pending.sources.filter(ref => !evidence.has(ref.name));
|
|
149
170
|
// Failed export/storage retries are tied to a card and exact source hash.
|
|
150
171
|
// A changed page is a new version; it never reuses a previous GLB receipt.
|
|
151
172
|
// A source the agent's iteration declaration discards or replaces is the
|
|
@@ -155,11 +176,11 @@ export function createIntermediateArtifacts({ wt, agentId, path, exportPage = ex
|
|
|
155
176
|
throw new Error('An exact producer recovery is pending. Retry that producer before starting different work.');
|
|
156
177
|
if (pending?.recovery) {
|
|
157
178
|
identity.recoveredFromTurnId = pending.recoverySourceTurnId ?? pending.turnId;
|
|
158
|
-
|
|
179
|
+
producerChanges = producerChanges.filter(e => pending.sources.some(s => s.name === e.name));
|
|
159
180
|
}
|
|
160
181
|
const discarded = pending ? declaredDiscards(wt) : new Set();
|
|
161
182
|
if (pending) for (const ref of pending.sources) {
|
|
162
|
-
if (!pending.recovery && (
|
|
183
|
+
if (!pending.recovery && (producerChanges.some(e => e.name === ref.name) || discarded.has(artifactStoredName(ref.name)))) continue;
|
|
163
184
|
const entry = standing.find(e => e.name === ref.name);
|
|
164
185
|
let bytes;
|
|
165
186
|
try { bytes = readIntermediateFile(wt, `.flowviant/artifacts/${ref.name}`, artifactMaxBytesFor(ref.name)); }
|
|
@@ -167,16 +188,16 @@ export function createIntermediateArtifacts({ wt, agentId, path, exportPage = ex
|
|
|
167
188
|
if (hash(bytes) !== ref.sha256)
|
|
168
189
|
throw new Error(`Required intermediate output ${ref.name} is missing or changed since the failed handoff. Restore it or revise the producer explicitly, then retry.`);
|
|
169
190
|
if (!entry) throw new Error(`Required intermediate output ${ref.name} is outside the safe artifact walk (non-hidden paths with at most ${ARTIFACT_MAX_DEPTH} directories). Move it to a supported artifact path and retry this producer.`);
|
|
170
|
-
if (!
|
|
191
|
+
if (!producerChanges.some(e => e.name === entry.name)) producerChanges.push(entry);
|
|
171
192
|
}
|
|
172
|
-
if (taskKind === 'model' && !
|
|
193
|
+
if (taskKind === 'model' && !producerChanges.some(e => /\.html?$/i.test(e.name)) && (job.kind !== 'task' || job.redo === true) && taskId) {
|
|
173
194
|
for (const ref of state.files.filter(f => f.taskId === taskId && f.taskKind === 'model' && /\.html?$/i.test(f.name))) {
|
|
174
195
|
const entry = standing.find(e => e.name === ref.name);
|
|
175
|
-
if (entry)
|
|
196
|
+
if (entry) producerChanges.push(entry);
|
|
176
197
|
}
|
|
177
198
|
}
|
|
178
|
-
const pages = taskKind === 'model' ?
|
|
179
|
-
const outputs =
|
|
199
|
+
const pages = taskKind === 'model' ? producerChanges.filter(e => /\.html?$/i.test(e.name)) : [];
|
|
200
|
+
const outputs = producerChanges.filter(e => artifactTypeFor(e.name));
|
|
180
201
|
state.pending = { taskId, turnId, ...(pending?.recovery ? { recovery: true, recoverySourceTurnId: identity.recoveredFromTurnId } : {}), sources: outputs.map(e => ({ name: e.name, sha256: hash(readIntermediateFile(wt, `.flowviant/artifacts/${e.name}`, artifactMaxBytesFor(e.name))) })), exports: pending?.exports ?? [] };
|
|
181
202
|
save(state);
|
|
182
203
|
const exports = [];
|
|
@@ -207,7 +228,12 @@ export function createIntermediateArtifacts({ wt, agentId, path, exportPage = ex
|
|
|
207
228
|
// would prune one of this very handoff's files and then falsely claim
|
|
208
229
|
// complete delivery.
|
|
209
230
|
if (outputs.length > ARTIFACT_MAX_PER_AGENT)
|
|
210
|
-
throw new
|
|
231
|
+
throw new IntermediateArtifactCapacityError(`This producer requires ${outputs.length} output files including model exports; artifact storage retains at most ${ARTIFACT_MAX_PER_AGENT} files per agent. Narrow this producer's output set before retrying; its handoff is unfinished.`);
|
|
232
|
+
const retainedNames = new Set(state.files.map(ref => ref.name));
|
|
233
|
+
const newNames = outputs.filter(entry => !retainedNames.has(entry.name));
|
|
234
|
+
const total = retainedNames.size + newNames.length;
|
|
235
|
+
if (total > ARTIFACT_MAX_PER_AGENT)
|
|
236
|
+
throw new IntermediateArtifactCapacityError(`The intermediate artifact receipt has ${retainedNames.size} retained producer files plus ${newNames.length} new files (${total} total); artifact storage retains at most ${ARTIFACT_MAX_PER_AGENT} files per agent. Narrow the producer outputs before retrying; its handoff is unfinished.`);
|
|
211
237
|
const textures = [...state.files.flatMap(f => [...(/\.png$/i.test(f.name) ? [f] : []), ...(f.model?.textureInputs ?? [])]),
|
|
212
238
|
...outputs.filter(e => /\.png$/i.test(e.name)).map(e => snapshot(wt, e, identity, retain))]
|
|
213
239
|
.map(({ name, path, sha256, taskId, turnId, agentId, taskKind }) => ({ name, path, sha256, taskId, turnId, agentId, taskKind }));
|
|
@@ -225,10 +251,9 @@ export function createIntermediateArtifacts({ wt, agentId, path, exportPage = ex
|
|
|
225
251
|
// Retry all prepared files even if their mtime did not change this turn.
|
|
226
252
|
const uploadBefore = Object.assign(new Map(before), before);
|
|
227
253
|
for (const ref of files) uploadBefore.delete(ref.name);
|
|
228
|
-
return { modelExports: exports, requiredOutputs: files.map(({ name, sha256 }) => ({ name, sha256 })), uploadBefore, commit: () => {
|
|
254
|
+
return { modelExports: exports, requiredOutputs: files.map(({ name, sha256 }) => ({ name, sha256 })), evidenceNames, uploadBefore, commit: () => {
|
|
229
255
|
const byName = new Map(state.files.map(f => [f.name, f]));
|
|
230
256
|
for (const ref of files) byName.set(ref.name, ref);
|
|
231
|
-
if (byName.size > 160) throw new Error('The intermediate artifact receipt exceeds 160 files. Narrow the outputs before retrying.');
|
|
232
257
|
save({ ...state, files: [...byName.values()], pending: null });
|
|
233
258
|
} };
|
|
234
259
|
};
|
package/bin/lib/uiReview.mjs
CHANGED
|
@@ -157,7 +157,7 @@ export async function createUiReview({ wt, signal, capture = captureLocalReview,
|
|
|
157
157
|
const stop = () => runController.abort();
|
|
158
158
|
controller.signal.addEventListener('abort', stop, { once: true });
|
|
159
159
|
const timer = setTimeout(stop, timeoutMs);
|
|
160
|
-
const record = { status: 'unavailable', before: reviewSourceState(wt), basis: reviewDiagnostic(input.basis), fixture: reviewDiagnostic(input.fixture.description),
|
|
160
|
+
const record = { purpose: 'ui-review', status: 'unavailable', before: reviewSourceState(wt), basis: reviewDiagnostic(input.basis), fixture: reviewDiagnostic(input.fixture.description),
|
|
161
161
|
requestedScenarios: input.scenarios.map(s => ({ name: reviewDiagnostic(s.name), path: reviewDiagnostic(s.path), actions: s.actions.map(a => ({ type: a.type, ...(a.selector ? { selector: reviewDiagnostic(a.selector) } : {}) })) })),
|
|
162
162
|
scenarios: [], consoleErrors: [], failedRequests: [] };
|
|
163
163
|
let child, proxy, scratch, artifactDir, untrack;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/** Identify only files explicitly owned by a measured UI-review report.
|
|
2
|
+
* Legacy reports predate the purpose marker. A folder prefix alone never
|
|
3
|
+
* exempts producer data from handoff; unknown/unreadable reports exempt none.
|
|
4
|
+
* read receives the entry so callers can use immutable receipt bytes. */
|
|
5
|
+
export function uiReviewEvidenceNames(entries, read) {
|
|
6
|
+
const evidence = new Set();
|
|
7
|
+
const byName = new Map(entries.map(entry => [entry.name, entry]));
|
|
8
|
+
for (const entry of entries) {
|
|
9
|
+
if (!/^ui-review-[a-f0-9]{8}-[a-f0-9]{3}\/report\.json$/.test(entry.name)) continue;
|
|
10
|
+
try {
|
|
11
|
+
const report = JSON.parse(read(entry));
|
|
12
|
+
const folder = entry.name.slice(0, -'/report.json'.length);
|
|
13
|
+
if (report.folder !== `.flowviant/artifacts/${folder}` ||
|
|
14
|
+
report.artifact !== `${report.folder}/report.json` ||
|
|
15
|
+
!report.before || !report.after || !Array.isArray(report.requestedScenarios) || !Array.isArray(report.scenarios) ||
|
|
16
|
+
(report.purpose !== undefined && report.purpose !== 'ui-review') ||
|
|
17
|
+
(report.screenshots !== undefined && !Array.isArray(report.screenshots))) continue;
|
|
18
|
+
const screenshots = report.screenshots ?? [];
|
|
19
|
+
if (screenshots.some(name => typeof name !== 'string' || !/^(desktop|mobile)-\d+-(initial|final|failure)\.png$/.test(name))) continue;
|
|
20
|
+
evidence.add(entry.name);
|
|
21
|
+
for (const name of screenshots) evidence.add(`${folder}/${name}`);
|
|
22
|
+
const delivery = byName.get(`${folder}/delivery-state.json`);
|
|
23
|
+
if (delivery) {
|
|
24
|
+
const state = JSON.parse(read(delivery));
|
|
25
|
+
if (state.finalState && typeof state.latestMatches === 'boolean' && Array.isArray(state.imageInputs)) evidence.add(delivery.name);
|
|
26
|
+
}
|
|
27
|
+
} catch { /* Unknown evidence remains an ordinary producer output. */ }
|
|
28
|
+
}
|
|
29
|
+
return evidence;
|
|
30
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process';
|
|
2
|
+
import { createHash } from 'node:crypto';
|
|
2
3
|
import { mkdtempSync, rmSync, writeFileSync } from 'node:fs';
|
|
3
4
|
import { join } from 'node:path';
|
|
4
5
|
import { tmpdir, platform } from 'node:os';
|
|
@@ -39,6 +40,7 @@ export const LOCAL_REVIEW_BOOTSTRAP = `for (const name of ['RTCPeerConnection',
|
|
|
39
40
|
export async function captureLocalReview({ origin, scenarios, artifactDir, signal,
|
|
40
41
|
browser = resolveBrowser(), spawnImpl = spawn, timeoutMs = UI_REVIEW_TIMEOUT_MS, onChild = () => () => {} }) {
|
|
41
42
|
const result = { status: 'unavailable', scenarios: [], consoleErrors: [], failedRequests: [], screenshots: [] };
|
|
43
|
+
const capturesByHash = new Map();
|
|
42
44
|
if (!browser) return { ...result, reason: 'No Chrome/Chromium/Edge found; no browser checks ran.' };
|
|
43
45
|
if (browser.viaWindows) return { ...result, reason: 'WSL Windows interop cannot carry the scoped DevTools pipe.' };
|
|
44
46
|
if (signal?.aborted) return { ...result, reason: 'Review was cancelled before launch.' };
|
|
@@ -158,9 +160,19 @@ export async function captureLocalReview({ origin, scenarios, artifactDir, signa
|
|
|
158
160
|
const shot = await send('Page.captureScreenshot', { format: 'png' });
|
|
159
161
|
const bytes = Buffer.from(shot.data ?? '', 'base64');
|
|
160
162
|
if (!bytes.length || bytes.length > 2 * 1024 * 1024) throw new Error('Screenshot was empty or exceeded the retained artifact size.');
|
|
161
|
-
const
|
|
162
|
-
|
|
163
|
-
|
|
163
|
+
const sha256 = createHash('sha256').update(bytes).digest('hex');
|
|
164
|
+
let name = capturesByHash.get(sha256);
|
|
165
|
+
if (!name) {
|
|
166
|
+
name = `${viewport.name}-${index + 1}-${phase}.png`;
|
|
167
|
+
writeFileSync(join(artifactDir, name), bytes, { flag: 'wx' });
|
|
168
|
+
capturesByHash.set(sha256, name);
|
|
169
|
+
result.screenshots.push(name);
|
|
170
|
+
}
|
|
171
|
+
// Each capture keeps its viewport/scenario/phase even when its
|
|
172
|
+
// exact pixels already have a retained file in this review run.
|
|
173
|
+
row.captures ??= [];
|
|
174
|
+
row.captures.push({ phase, name, sha256 });
|
|
175
|
+
if (!row.screenshots.includes(name)) row.screenshots.push(name);
|
|
164
176
|
};
|
|
165
177
|
captureFailure = () => screenshot('failure');
|
|
166
178
|
await screenshot('initial');
|
|
@@ -67,7 +67,7 @@ import { ORCHESTRATED_POSTURES, orchestratesPosture } from './orchestratorPrompt
|
|
|
67
67
|
import { knownCodexModels } from './runtimeOfferings.mjs';
|
|
68
68
|
import { codexSubagentsSafe } from './runtimeCodex.mjs';
|
|
69
69
|
import { measuredRuntimeVersion } from './runtimeDetection.mjs';
|
|
70
|
-
import { createIntermediateArtifacts } from './intermediateArtifacts.mjs';
|
|
70
|
+
import { createIntermediateArtifacts, IntermediateArtifactCapacityError } from './intermediateArtifacts.mjs';
|
|
71
71
|
import { createUiReview } from './uiReview.mjs';
|
|
72
72
|
import { createVideoRender } from './videoRender.mjs';
|
|
73
73
|
import { withUiReview, appendUiReviewEvidence } from './uiReviewTurn.mjs';
|
|
@@ -827,6 +827,9 @@ export function createAgentTurnExecution({
|
|
|
827
827
|
try { intermediateDelivery = await intermediate.prepare({ job, turnId, taskKind, before: artifactsBefore, signal: setupController.signal }); }
|
|
828
828
|
catch (error) {
|
|
829
829
|
if (run.interrupted) throw error;
|
|
830
|
+
// A measured storage overflow must stop before sending a subset
|
|
831
|
+
// that could evict this producer's already retained inputs.
|
|
832
|
+
if (error instanceof IntermediateArtifactCapacityError) throw error;
|
|
830
833
|
// The failed page still has its preview/files for repair. Reporting
|
|
831
834
|
// those bytes cannot turn a failed export into a delivered card.
|
|
832
835
|
try { await artifacts.report({ placeDir: wt, before: artifactsBefore, agentId, turnId, requireStored: kind.artifact != null }); }
|
|
@@ -835,7 +838,7 @@ export function createAgentTurnExecution({
|
|
|
835
838
|
}
|
|
836
839
|
}
|
|
837
840
|
await artifacts.report({ placeDir: wt, before: intermediateDelivery?.uploadBefore ?? artifactsBefore, agentId, turnId,
|
|
838
|
-
requireStored: kind.artifact != null, requiredOutputs: intermediateDelivery?.requiredOutputs });
|
|
841
|
+
requireStored: kind.artifact != null, requiredOutputs: intermediateDelivery?.requiredOutputs, evidenceNames: intermediateDelivery?.evidenceNames });
|
|
839
842
|
}
|
|
840
843
|
|
|
841
844
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "flowviant",
|
|
3
|
-
"version": "0.125.
|
|
3
|
+
"version": "0.125.26",
|
|
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": {
|