opencode-plugin-flow 8.0.0 → 8.1.0
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/CHANGELOG.md +19 -0
- package/README.md +2 -2
- package/dist/index.js +174 -35
- package/dist/index.js.map +13 -12
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,25 @@
|
|
|
2
2
|
|
|
3
3
|
One short entry per release, written for users deciding whether to upgrade.
|
|
4
4
|
|
|
5
|
+
## [8.1.0] - 2026-08-19
|
|
6
|
+
|
|
7
|
+
Inspect surveys can finish with blockers, and `/flow-auto` hands back a findings list.
|
|
8
|
+
|
|
9
|
+
- Compact `flow_status` and close delivery now carry a derived `findingsDigest`
|
|
10
|
+
over every identified finding across attempts. `/flow-auto` prints that list
|
|
11
|
+
when it parks or stops instead of going silent after a blocked review.
|
|
12
|
+
- **Session v5 schema:** `PlanFeature.kind` is optional `change` or `inspect`.
|
|
13
|
+
Absent is `change`. A failed inspect review completes that slice so later
|
|
14
|
+
features can start without `flow_feature_reset`. `completed` then means the
|
|
15
|
+
survey finished, not that the tree is clean. Existing documents keep the
|
|
16
|
+
repair loop.
|
|
17
|
+
|
|
18
|
+
Install or update:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
opencode plugin opencode-plugin-flow@8.1.0 --global --force
|
|
22
|
+
```
|
|
23
|
+
|
|
5
24
|
## [8.0.0] - 2026-08-18
|
|
6
25
|
|
|
7
26
|
One evidence record, a thinner run loop, and a freeze on further declarations.
|
package/README.md
CHANGED
|
@@ -40,7 +40,7 @@ expensive, and it is overhead when it is not.
|
|
|
40
40
|
Install the exact npm release through OpenCode:
|
|
41
41
|
|
|
42
42
|
```bash
|
|
43
|
-
opencode plugin opencode-plugin-flow@8.
|
|
43
|
+
opencode plugin opencode-plugin-flow@8.1.0 --global --force
|
|
44
44
|
```
|
|
45
45
|
|
|
46
46
|
Omit `--global` for project scope. Version pins are exact and never update on
|
|
@@ -51,7 +51,7 @@ The equivalent manual project configuration is:
|
|
|
51
51
|
```json
|
|
52
52
|
{
|
|
53
53
|
"$schema": "https://opencode.ai/config.json",
|
|
54
|
-
"plugin": ["opencode-plugin-flow@8.
|
|
54
|
+
"plugin": ["opencode-plugin-flow@8.1.0"]
|
|
55
55
|
}
|
|
56
56
|
```
|
|
57
57
|
|
package/dist/index.js
CHANGED
|
@@ -109,7 +109,8 @@ If a Flow tool is absent, report an incomplete plugin load; never simulate state
|
|
|
109
109
|
## Recovery
|
|
110
110
|
|
|
111
111
|
On interruption, read compact status; load \`flow-run\` for an active or blocked feature
|
|
112
|
-
and use execution or reviewer status, never prose, for lifecycle truth.
|
|
112
|
+
and use execution or reviewer status, never prose, for lifecycle truth. At a
|
|
113
|
+
checkpoint or lease stop, print compact \`findingsDigest\` before any delivery map.
|
|
113
114
|
|
|
114
115
|
Summaries keep plan/source IDs \`verified\` or \`incomplete\`. A prior finding is
|
|
115
116
|
terminally \`fixed\` only when review passes with current evidence. A failed
|
|
@@ -198,7 +199,8 @@ Save one plan with:
|
|
|
198
199
|
gate is the command every broad observation must run; a failed or
|
|
199
200
|
claimed-broad gate still vetoes review.
|
|
200
201
|
- \`features\`: ordered outcome slices, each with a stable \`id\`, \`title\`,
|
|
201
|
-
\`summary\`, bounded \`targets\`, concrete \`validation\`,
|
|
202
|
+
\`summary\`, bounded \`targets\`, concrete \`validation\`, \`dependsOn\` ids, and
|
|
203
|
+
optional \`kind\`.
|
|
202
204
|
|
|
203
205
|
Each feature needs one observable outcome judgeable from bounded evidence and
|
|
204
206
|
focused validation. Split only independent failures or true dependencies; file
|
|
@@ -213,6 +215,13 @@ outcome and its evidence.
|
|
|
213
215
|
A \`validation\` entry naming a command is recorded byte-for-byte; prose there stays
|
|
214
216
|
reviewer judgment, never a fabricated result.
|
|
215
217
|
|
|
218
|
+
If the request is inspect-only (review, audit, survey, no promised edit), invent
|
|
219
|
+
no repair features. Save at most a small set of inspect features with
|
|
220
|
+
\`kind: "inspect"\`, whose \`validation\` is reviewer inspection, whose \`targets\`
|
|
221
|
+
are existing paths, and whose \`decisions\` state that no source edit is
|
|
222
|
+
authorized. The gate may be the repo's existing check. Ask before turning an
|
|
223
|
+
inspect request into repairs.
|
|
224
|
+
|
|
216
225
|
Before saving, confirm:
|
|
217
226
|
|
|
218
227
|
- every requirement maps to a feature or an explicit non-goal;
|
|
@@ -277,17 +286,15 @@ the manager's summary. Check that:
|
|
|
277
286
|
- changed behavior is correct at public and downstream call sites;
|
|
278
287
|
- validation is strong enough for the behavior and main failure modes;
|
|
279
288
|
- scope did not drift and unrelated user work was preserved;
|
|
280
|
-
- relevant adjacent states, failure/cleanup ordering, repetition, retry,
|
|
281
|
-
interruption, reentrancy, concurrency, and overlapping invariants still work;
|
|
282
289
|
- the feature's actual base diff includes no unexplained deletion, rename, file
|
|
283
290
|
type, generated artifact, or executable/file-mode change; and
|
|
284
291
|
- persistence, concurrency, security, migration, compatibility, package, UI,
|
|
285
292
|
and recovery risks were examined when relevant.
|
|
286
293
|
|
|
287
|
-
Finish the supplied feature-specific risk checklist
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
294
|
+
Finish the supplied feature-specific risk checklist. Continue a concurrency or
|
|
295
|
+
state-machine matrix after one blocker only when \`assignment.packet.riskLenses\`
|
|
296
|
+
is non-empty or the packet summary includes a matrix. Otherwise inspect the
|
|
297
|
+
changed artifacts, the supplied validation, and live \`priorFindings\`.
|
|
291
298
|
|
|
292
299
|
Scope plan/source IDs by assignment kind. An ordinary feature review records
|
|
293
300
|
dispositions only for IDs mapped to the active feature or explicitly supplied
|
|
@@ -311,8 +318,8 @@ not a failure; a missing or conflicting baseline fact, or a material mode,
|
|
|
311
318
|
platform, race, or failure-path claim without proof, is.
|
|
312
319
|
|
|
313
320
|
Flow deliberately projects no raw command output; use the durable command, exit
|
|
314
|
-
code, completeness, digest, source binding, and your workspace inspection. A weak
|
|
315
|
-
|
|
321
|
+
code, completeness, digest, source binding, and your workspace inspection. A weak
|
|
322
|
+
coverage claim is an evidence gap.
|
|
316
323
|
|
|
317
324
|
For a final assignment, also inspect broad validation and confirm docs,
|
|
318
325
|
commands, package surfaces, and remaining gaps are consistent with completion.
|
|
@@ -326,8 +333,8 @@ IDs in summary or evidence.
|
|
|
326
333
|
|
|
327
334
|
Report every problem you find. Severity is a routing decision the runtime acts
|
|
328
335
|
on, not a filter on what to mention: \`blocking\` when the issue invalidates the
|
|
329
|
-
approved outcome, \`advisory\` otherwise.
|
|
330
|
-
\`advisory\` rather than omitting it.
|
|
336
|
+
approved outcome, \`advisory\` otherwise. If unsure, report it as
|
|
337
|
+
\`advisory\` rather than omitting it. Inspect kind completes with blockers.
|
|
331
338
|
|
|
332
339
|
Set \`scopeBlocker: true\` on a blocking finding whose repair requires material
|
|
333
340
|
work outside the approved plan, and identify the boundary in \`evidence\`. The
|
|
@@ -515,7 +522,8 @@ feature.
|
|
|
515
522
|
|
|
516
523
|
### Blocked review
|
|
517
524
|
|
|
518
|
-
Follow \`nextAction\` with the one detail projection
|
|
525
|
+
Follow \`nextAction\` with the one detail projection for routing, then print
|
|
526
|
+
compact \`findingsDigest\` as the user-facing list. The runtime already weighs
|
|
519
527
|
\`failedReviewCount\` and \`blockedFeature.scopeBlocker\`.
|
|
520
528
|
|
|
521
529
|
- Ready \`await-user-direction\` has no blocked run left to reset. Identify the
|
|
@@ -534,8 +542,8 @@ Follow \`nextAction\` with the one detail projection. The runtime already weighs
|
|
|
534
542
|
feature, pass that exact \`featureId\` as \`nextFeatureId\` on
|
|
535
543
|
\`flow_feature_reset\`.
|
|
536
544
|
|
|
537
|
-
Direct \`/flow-run\` reports
|
|
538
|
-
|
|
545
|
+
Direct \`/flow-run\` reports compact \`findingsDigest\` and \`nextAction\`, then
|
|
546
|
+
stops. Under \`/flow-auto\`, return to its lifecycle loop.
|
|
539
547
|
`;
|
|
540
548
|
|
|
541
549
|
// src/guidance/ids.ts
|
|
@@ -560,7 +568,9 @@ var FLOW_MANAGER_KERNEL = [
|
|
|
560
568
|
].join(" "),
|
|
561
569
|
[
|
|
562
570
|
"- Make one automatic fresh full retry only when the projected `nextAction`",
|
|
563
|
-
"is `flow_feature_reset`; otherwise checkpoint."
|
|
571
|
+
"is `flow_feature_reset`; otherwise checkpoint.",
|
|
572
|
+
"On `await-user-direction` or a lease stop, print compact `findingsDigest`",
|
|
573
|
+
"without inventing ids."
|
|
564
574
|
].join(" "),
|
|
565
575
|
[
|
|
566
576
|
"- Before review, require current-source evidence appropriate to the changed outcome,",
|
|
@@ -972,6 +982,9 @@ function planEvidence(plan) {
|
|
|
972
982
|
function planGate(plan) {
|
|
973
983
|
return planEvidence(plan).find((entry) => entry.scope === "gate")?.command;
|
|
974
984
|
}
|
|
985
|
+
function featureKind(feature) {
|
|
986
|
+
return feature?.kind === "inspect" ? "inspect" : "change";
|
|
987
|
+
}
|
|
975
988
|
function reviewResultSemanticIssues(result) {
|
|
976
989
|
const issues = [];
|
|
977
990
|
const blocking = result.findings.some((finding) => finding.severity === "blocking");
|
|
@@ -1713,6 +1726,7 @@ function completeFeature(session, input) {
|
|
|
1713
1726
|
}
|
|
1714
1727
|
}
|
|
1715
1728
|
const findings = assignFindingIds(input.result.findings, findingIdPrefix(run.featureId, assignment.createdRevision));
|
|
1729
|
+
const inspect = featureKind(session.plan?.features.find((feature) => feature.id === run.featureId)) === "inspect";
|
|
1716
1730
|
const next = commit(session, "feature-complete", input.operationId, input, (draft, revision) => ({
|
|
1717
1731
|
...draft,
|
|
1718
1732
|
runs: draft.runs.map((item) => {
|
|
@@ -1720,7 +1734,7 @@ function completeFeature(session, input) {
|
|
|
1720
1734
|
return item;
|
|
1721
1735
|
return {
|
|
1722
1736
|
...item,
|
|
1723
|
-
state: input.result.verdict === "passed" ? "completed" : "blocked",
|
|
1737
|
+
state: input.result.verdict === "passed" || inspect ? "completed" : "blocked",
|
|
1724
1738
|
summary: input.summary,
|
|
1725
1739
|
reviews: item.reviews.map((review) => review.id === assignment.id ? {
|
|
1726
1740
|
...review,
|
|
@@ -1815,7 +1829,7 @@ function closeSession(session, input) {
|
|
|
1815
1829
|
fail("sessionId does not match active state.");
|
|
1816
1830
|
assertMutable(session);
|
|
1817
1831
|
if (input.kind === "completed" && sessionStatus(session) !== "completed") {
|
|
1818
|
-
fail("A completed close requires every planned feature to
|
|
1832
|
+
fail("A completed close requires every planned feature to be complete.");
|
|
1819
1833
|
}
|
|
1820
1834
|
if (input.kind === "completed") {
|
|
1821
1835
|
const unsatisfied = unsatisfiedExtraEvidence(session);
|
|
@@ -1875,8 +1889,14 @@ class ArchiveCollisionError extends Error {
|
|
|
1875
1889
|
import { z } from "zod";
|
|
1876
1890
|
|
|
1877
1891
|
// src/domain/session-invariants.ts
|
|
1878
|
-
function
|
|
1879
|
-
|
|
1892
|
+
function featureSettledBefore(session, featureId, revision) {
|
|
1893
|
+
const inspect = featureKind(session.plan?.features.find((feature) => feature.id === featureId)) === "inspect";
|
|
1894
|
+
return session.runs.some((run) => run.featureId === featureId && run.reviews.some((review) => {
|
|
1895
|
+
const result = review.result;
|
|
1896
|
+
if (!result || result.recordedRevision >= revision)
|
|
1897
|
+
return false;
|
|
1898
|
+
return result.verdict === "passed" || inspect;
|
|
1899
|
+
}));
|
|
1880
1900
|
}
|
|
1881
1901
|
function sessionInvariantIssues(session) {
|
|
1882
1902
|
const issues = [];
|
|
@@ -1993,7 +2013,7 @@ function sessionInvariantIssues(session) {
|
|
|
1993
2013
|
if (referenced.some((validation) => validation.recordedRevision >= review.createdRevision)) {
|
|
1994
2014
|
issues.push(`Review '${review.id}' references later validation.`);
|
|
1995
2015
|
}
|
|
1996
|
-
const expectedKind = session.plan.features.every((feature) => feature.id === run.featureId ||
|
|
2016
|
+
const expectedKind = session.plan.features.every((feature) => feature.id === run.featureId || featureSettledBefore(session, feature.id, review.createdRevision)) ? "final" : "feature";
|
|
1997
2017
|
if (review.kind !== expectedKind) {
|
|
1998
2018
|
issues.push(`Review '${review.id}' has the wrong derived kind.`);
|
|
1999
2019
|
}
|
|
@@ -2011,7 +2031,10 @@ function sessionInvariantIssues(session) {
|
|
|
2011
2031
|
issues.push(`Active run '${run.id}' contains a recorded outcome.`);
|
|
2012
2032
|
}
|
|
2013
2033
|
if (run.state === "completed" && last?.result?.verdict !== "passed") {
|
|
2014
|
-
|
|
2034
|
+
const inspect = featureKind(session.plan?.features.find((feature) => feature.id === run.featureId)) === "inspect";
|
|
2035
|
+
if (!(inspect && last?.result?.verdict === "failed")) {
|
|
2036
|
+
issues.push(`Completed run '${run.id}' lacks a passing review.`);
|
|
2037
|
+
}
|
|
2015
2038
|
}
|
|
2016
2039
|
if (run.state === "blocked" && last?.result?.verdict !== "failed") {
|
|
2017
2040
|
issues.push(`Blocked run '${run.id}' lacks a failed review.`);
|
|
@@ -2050,7 +2073,8 @@ var PlanFeatureSchema = z.object({
|
|
|
2050
2073
|
summary: boundedText("Feature summary"),
|
|
2051
2074
|
targets: z.array(boundedText("Feature target")).max(MAX_PLAN_FEATURES).default([]),
|
|
2052
2075
|
validation: z.array(boundedText("Feature validation")).max(MAX_PLAN_FEATURES).default([]),
|
|
2053
|
-
dependsOn: z.array(FeatureIdSchema).max(MAX_PLAN_FEATURES).default([])
|
|
2076
|
+
dependsOn: z.array(FeatureIdSchema).max(MAX_PLAN_FEATURES).default([]),
|
|
2077
|
+
kind: z.enum(["change", "inspect"]).optional()
|
|
2054
2078
|
}).strict();
|
|
2055
2079
|
var EvidenceEntrySchema = z.object({
|
|
2056
2080
|
requirement: boundedText("Evidence requirement"),
|
|
@@ -2260,6 +2284,69 @@ var StatusInputSchema = z.object({
|
|
|
2260
2284
|
])
|
|
2261
2285
|
}).strict();
|
|
2262
2286
|
|
|
2287
|
+
// src/application/findings-digest.ts
|
|
2288
|
+
function plannedFeatureIds(session) {
|
|
2289
|
+
if (session.plan)
|
|
2290
|
+
return session.plan.features.map((feature) => feature.id);
|
|
2291
|
+
const ids = [];
|
|
2292
|
+
for (const run of session.runs) {
|
|
2293
|
+
if (ids.includes(run.featureId))
|
|
2294
|
+
continue;
|
|
2295
|
+
ids.push(run.featureId);
|
|
2296
|
+
}
|
|
2297
|
+
return ids;
|
|
2298
|
+
}
|
|
2299
|
+
function findingsDigest(session) {
|
|
2300
|
+
const rows = [];
|
|
2301
|
+
const indexById = new Map;
|
|
2302
|
+
for (const featureId of plannedFeatureIds(session)) {
|
|
2303
|
+
for (const run of session.runs) {
|
|
2304
|
+
if (run.featureId !== featureId)
|
|
2305
|
+
continue;
|
|
2306
|
+
for (const review of run.reviews) {
|
|
2307
|
+
const result = review.result;
|
|
2308
|
+
if (!result)
|
|
2309
|
+
continue;
|
|
2310
|
+
for (const finding of result.findings) {
|
|
2311
|
+
if (!finding.findingId)
|
|
2312
|
+
continue;
|
|
2313
|
+
const row = {
|
|
2314
|
+
featureId,
|
|
2315
|
+
findingId: finding.findingId,
|
|
2316
|
+
severity: finding.severity,
|
|
2317
|
+
summary: finding.summary,
|
|
2318
|
+
attempt: run.attempt,
|
|
2319
|
+
verdict: result.verdict,
|
|
2320
|
+
live: false,
|
|
2321
|
+
...finding.evidence === undefined ? {} : { evidence: finding.evidence }
|
|
2322
|
+
};
|
|
2323
|
+
const existing = indexById.get(finding.findingId);
|
|
2324
|
+
if (existing === undefined) {
|
|
2325
|
+
indexById.set(finding.findingId, rows.length);
|
|
2326
|
+
rows.push(row);
|
|
2327
|
+
} else {
|
|
2328
|
+
rows[existing] = row;
|
|
2329
|
+
}
|
|
2330
|
+
}
|
|
2331
|
+
}
|
|
2332
|
+
}
|
|
2333
|
+
}
|
|
2334
|
+
return rows.map((row) => ({
|
|
2335
|
+
...row,
|
|
2336
|
+
live: liveFindingIds(session, row.featureId).includes(row.findingId)
|
|
2337
|
+
}));
|
|
2338
|
+
}
|
|
2339
|
+
function digestReportLines(digest) {
|
|
2340
|
+
if (digest.length === 0)
|
|
2341
|
+
return ["Findings digest: none"];
|
|
2342
|
+
const line = (row, kind) => `- ${kind} ${row.featureId} ${row.findingId} ${row.severity}: ${row.summary}`;
|
|
2343
|
+
return [
|
|
2344
|
+
"Findings digest:",
|
|
2345
|
+
...digest.filter((row) => row.live).map((row) => line(row, "live")),
|
|
2346
|
+
...digest.filter((row) => !row.live).map((row) => line(row, "historical"))
|
|
2347
|
+
];
|
|
2348
|
+
}
|
|
2349
|
+
|
|
2263
2350
|
// src/application/delivery.ts
|
|
2264
2351
|
var LIMITATIONS = [
|
|
2265
2352
|
"Artifact paths and the canonical gate are caller declarations; Flow validates binding, not completeness or fitness.",
|
|
@@ -2340,6 +2427,7 @@ function formatReport(delivery) {
|
|
|
2340
2427
|
`Progress: ${delivery.progress.completed} of ${delivery.progress.total} features complete`,
|
|
2341
2428
|
"Features:",
|
|
2342
2429
|
...lines,
|
|
2430
|
+
...digestReportLines(delivery.findingsDigest),
|
|
2343
2431
|
`Assurance: ${delivery.assurance.conclusion.replaceAll("-", " ")}`,
|
|
2344
2432
|
"Assurance checks:",
|
|
2345
2433
|
...delivery.assurance.checks.map((item) => `- ${item.status} [${TIER_LABELS[item.tier]}] ${item.label}: ${item.explanation}`),
|
|
@@ -2361,6 +2449,7 @@ function deliveryProjection(session) {
|
|
|
2361
2449
|
const latest = grouped.flatMap(({ runs }) => runs.slice(-1));
|
|
2362
2450
|
const latestArtifacts = new Set(latest.flatMap((run) => run.artifactsChanged.map((item) => item.path)));
|
|
2363
2451
|
const allArtifacts = new Set(session.runs.flatMap((run) => run.artifactsChanged.map((item) => item.path)));
|
|
2452
|
+
const digest = findingsDigest(session);
|
|
2364
2453
|
const delivery = {
|
|
2365
2454
|
goal: session.goal,
|
|
2366
2455
|
closure: { kind: session.closure.kind, summary: session.closure.summary },
|
|
@@ -2376,17 +2465,15 @@ function deliveryProjection(session) {
|
|
|
2376
2465
|
attempts: runs.length,
|
|
2377
2466
|
latestState: run?.state ?? "not-started",
|
|
2378
2467
|
outcomeSummary: run?.summary ?? null,
|
|
2379
|
-
terminalFindings:
|
|
2380
|
-
severity,
|
|
2381
|
-
summary
|
|
2382
|
-
})) ?? []
|
|
2468
|
+
terminalFindings: digest.filter((row) => row.featureId === feature.id && row.live).map(({ severity, summary }) => ({ severity, summary }))
|
|
2383
2469
|
};
|
|
2384
2470
|
}),
|
|
2385
2471
|
reportedArtifacts: {
|
|
2386
2472
|
latestAttempts: [...latestArtifacts].sort(),
|
|
2387
2473
|
supersededAttemptsOnly: [...allArtifacts].filter((path) => !latestArtifacts.has(path)).sort()
|
|
2388
2474
|
},
|
|
2389
|
-
assurance: assuranceProjection(session)
|
|
2475
|
+
assurance: assuranceProjection(session),
|
|
2476
|
+
findingsDigest: digest
|
|
2390
2477
|
};
|
|
2391
2478
|
return { ...delivery, report: formatReport(delivery) };
|
|
2392
2479
|
}
|
|
@@ -2462,7 +2549,8 @@ function compactProjection(session, pendingReviewSourceStale = false) {
|
|
|
2462
2549
|
blockedFeature,
|
|
2463
2550
|
progress: featureProgress(session),
|
|
2464
2551
|
nextAction: nextAction(session, pendingReviewSourceStale, blockedFeature),
|
|
2465
|
-
archiveRetry: retryRequest ? { request: retryRequest } : null
|
|
2552
|
+
archiveRetry: retryRequest ? { request: retryRequest } : null,
|
|
2553
|
+
findingsDigest: findingsDigest(session)
|
|
2466
2554
|
};
|
|
2467
2555
|
}
|
|
2468
2556
|
function archivedProjection(session) {
|
|
@@ -2550,7 +2638,8 @@ function idleProjection(view) {
|
|
|
2550
2638
|
view,
|
|
2551
2639
|
status: "idle",
|
|
2552
2640
|
revision: 0,
|
|
2553
|
-
nextAction: "flow_plan_save"
|
|
2641
|
+
nextAction: "flow_plan_save",
|
|
2642
|
+
findingsDigest: []
|
|
2554
2643
|
};
|
|
2555
2644
|
}
|
|
2556
2645
|
function project(session, request, pendingReviewSourceStale = false) {
|
|
@@ -3801,6 +3890,10 @@ var CONTINUATION_ROUTE = [
|
|
|
3801
3890
|
"for a fresh close use compact session id/revision plus a fresh operation id,",
|
|
3802
3891
|
"and replay archiveRetry exactly from its projected request."
|
|
3803
3892
|
].join(" ");
|
|
3893
|
+
var HANDBACK_ROUTE = [
|
|
3894
|
+
"Call flow_status with the compact view first.",
|
|
3895
|
+
"Print findingsDigest as the user-facing list. Do not invent ids."
|
|
3896
|
+
].join(" ");
|
|
3804
3897
|
function inspectMessage(parts) {
|
|
3805
3898
|
let token = null;
|
|
3806
3899
|
let text = "";
|
|
@@ -3826,6 +3919,9 @@ function isCheckpoint(projection) {
|
|
|
3826
3919
|
function isPendingReviewer(projection) {
|
|
3827
3920
|
return projection.status === "running" && projection.nextAction === "dispatch-flow-reviewer";
|
|
3828
3921
|
}
|
|
3922
|
+
function isHandback(projection) {
|
|
3923
|
+
return projection.status === "blocked" || projection.nextAction === "flow_feature_reset" || projection.nextAction === "dispatch-flow-reviewer";
|
|
3924
|
+
}
|
|
3829
3925
|
|
|
3830
3926
|
class AutoDriveCoordinator {
|
|
3831
3927
|
#lease = null;
|
|
@@ -3875,6 +3971,31 @@ class AutoDriveCoordinator {
|
|
|
3875
3971
|
lease.lastPromptedRevision = null;
|
|
3876
3972
|
this.#setTiming("waiting-for-user");
|
|
3877
3973
|
}
|
|
3974
|
+
async#promptHandback(lease, projection) {
|
|
3975
|
+
if (!isHandback(projection))
|
|
3976
|
+
return;
|
|
3977
|
+
if (lease.handbackPromptedRevision === projection.revision)
|
|
3978
|
+
return;
|
|
3979
|
+
if (!lease.delivery)
|
|
3980
|
+
return;
|
|
3981
|
+
lease.handbackPromptedRevision = projection.revision;
|
|
3982
|
+
lease.messageId = null;
|
|
3983
|
+
this.#setTiming("active");
|
|
3984
|
+
lease.inFlight = "prompt";
|
|
3985
|
+
try {
|
|
3986
|
+
const handback = [
|
|
3987
|
+
`Flow is handing control back at compact revision ${projection.revision}.`,
|
|
3988
|
+
HANDBACK_ROUTE,
|
|
3989
|
+
`Then follow ${projection.nextAction} or stop at await-user-direction.`,
|
|
3990
|
+
"Do not expand the approved goal."
|
|
3991
|
+
].join(" ");
|
|
3992
|
+
await this.#options.prompt(lease.hostSessionId, `${handback}
|
|
3993
|
+
|
|
3994
|
+
${FLOW_MANAGER_KERNEL}`, lease.delivery, { [FLOW_AUTO_METADATA_KEY]: lease.token });
|
|
3995
|
+
} catch (error) {
|
|
3996
|
+
this.#stop(lease, `Flow auto prompt failed: ${String(error)}`);
|
|
3997
|
+
}
|
|
3998
|
+
}
|
|
3878
3999
|
async#read(lease) {
|
|
3879
4000
|
try {
|
|
3880
4001
|
return await this.#options.readProjection();
|
|
@@ -3897,6 +4018,7 @@ class AutoDriveCoordinator {
|
|
|
3897
4018
|
baseline: null,
|
|
3898
4019
|
delivery: null,
|
|
3899
4020
|
lastPromptedRevision: null,
|
|
4021
|
+
handbackPromptedRevision: null,
|
|
3900
4022
|
checkpoint: null,
|
|
3901
4023
|
pendingReply: false,
|
|
3902
4024
|
inFlight: null,
|
|
@@ -4093,8 +4215,12 @@ ${FLOW_MANAGER_KERNEL}`;
|
|
|
4093
4215
|
const mutationAdvanced = advance !== undefined && projection.revision === advance && isMechanical(projection);
|
|
4094
4216
|
if (lease.pendingReply) {
|
|
4095
4217
|
lease.pendingReply = false;
|
|
4096
|
-
if (boundary && (!checkpoint || projection.revision > checkpoint.revision))
|
|
4218
|
+
if (boundary && (!checkpoint || projection.revision > checkpoint.revision)) {
|
|
4219
|
+
await this.#promptHandback(lease, projection);
|
|
4220
|
+
if (this.#lease !== lease)
|
|
4221
|
+
return;
|
|
4097
4222
|
return void this.#waitAt(lease, projection.revision);
|
|
4223
|
+
}
|
|
4098
4224
|
if (!checkpoint || !boundary && !mutationAdvanced)
|
|
4099
4225
|
return void this.deactivate(hostSessionId);
|
|
4100
4226
|
checkpoint.answered = true;
|
|
@@ -4104,15 +4230,27 @@ ${FLOW_MANAGER_KERNEL}`;
|
|
|
4104
4230
|
if (boundary) {
|
|
4105
4231
|
if (checkpoint && projection.revision < checkpoint.revision)
|
|
4106
4232
|
return void this.deactivate(hostSessionId);
|
|
4233
|
+
await this.#promptHandback(lease, projection);
|
|
4234
|
+
if (this.#lease !== lease)
|
|
4235
|
+
return;
|
|
4107
4236
|
return void this.#waitAt(lease, projection.revision);
|
|
4108
4237
|
}
|
|
4238
|
+
if (!isMechanical(projection)) {
|
|
4239
|
+
const already = lease.handbackPromptedRevision === projection.revision;
|
|
4240
|
+
await this.#promptHandback(lease, projection);
|
|
4241
|
+
if (this.#lease !== lease)
|
|
4242
|
+
return;
|
|
4243
|
+
if (!already && lease.handbackPromptedRevision === projection.revision) {
|
|
4244
|
+
this.#setTiming("paused");
|
|
4245
|
+
return;
|
|
4246
|
+
}
|
|
4247
|
+
return void this.deactivate(hostSessionId);
|
|
4248
|
+
}
|
|
4109
4249
|
if (checkpoint) {
|
|
4110
4250
|
if (projection.revision <= checkpoint.revision || !mutationAdvanced)
|
|
4111
4251
|
return void this.deactivate(hostSessionId);
|
|
4112
4252
|
lease.checkpoint = null;
|
|
4113
4253
|
}
|
|
4114
|
-
if (!isMechanical(projection))
|
|
4115
|
-
return void this.deactivate(hostSessionId);
|
|
4116
4254
|
if (lease.lastPromptedRevision === projection.revision) {
|
|
4117
4255
|
this.#setTiming("paused");
|
|
4118
4256
|
return this.#warn(`Flow auto-drive paused after revision ${projection.revision} made no lifecycle progress.`);
|
|
@@ -4380,7 +4518,8 @@ var planFeature = host.object({
|
|
|
4380
4518
|
summary: text,
|
|
4381
4519
|
targets: host.array(text).max(MAX_PLAN_FEATURES).default([]),
|
|
4382
4520
|
validation: host.array(text).max(MAX_PLAN_FEATURES).default([]),
|
|
4383
|
-
dependsOn: host.array(featureId).max(MAX_PLAN_FEATURES).default([])
|
|
4521
|
+
dependsOn: host.array(featureId).max(MAX_PLAN_FEATURES).default([]),
|
|
4522
|
+
kind: host.enum(["change", "inspect"]).optional()
|
|
4384
4523
|
}).strict();
|
|
4385
4524
|
var plan = host.object({
|
|
4386
4525
|
summary: text,
|
|
@@ -5023,4 +5162,4 @@ export {
|
|
|
5023
5162
|
plugin_default as default
|
|
5024
5163
|
};
|
|
5025
5164
|
|
|
5026
|
-
//# debugId=
|
|
5165
|
+
//# debugId=4CB19287BD614D3B64756E2164756E21
|