opencode-plugin-flow 5.2.0 → 5.2.1
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 +15 -0
- package/README.md +2 -2
- package/dist/application/ports/session-repository.d.ts +4 -1
- package/dist/index.js +50 -23
- package/dist/index.js.map +5 -5
- package/dist/infrastructure/fs/workspace.d.ts +1 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
One short entry per release, written for users deciding whether to upgrade.
|
|
4
4
|
|
|
5
|
+
## [5.2.1] - 2026-07-19
|
|
6
|
+
|
|
7
|
+
Desktop helper runtime lore restores durable session closure under OpenCode
|
|
8
|
+
Desktop while preserving Flow's pinned-filesystem safety boundary:
|
|
9
|
+
|
|
10
|
+
- Electron-hosted Flow now starts its short-lived filesystem helper through the
|
|
11
|
+
host executable's Node mode. Bun-hosted OpenCode retains its dedicated CLI
|
|
12
|
+
mode, and ordinary Node hosts remain unchanged.
|
|
13
|
+
- Helper launch and protocol failures are classified separately from malformed
|
|
14
|
+
or ambiguous canonical history. Recovery guidance preserves the active
|
|
15
|
+
session and its exact durable close operation instead of blaming healthy
|
|
16
|
+
archive state.
|
|
17
|
+
- Persistence coverage exercises the Electron launch contract end to end and
|
|
18
|
+
proves that runtime failures remain atomic and receive the correct guidance.
|
|
19
|
+
|
|
5
20
|
## [5.2.0] - 2026-07-19
|
|
6
21
|
|
|
7
22
|
Runtime-owned review assignment lore removes the recovery loops seen in long
|
package/README.md
CHANGED
|
@@ -17,7 +17,7 @@ Full project documentation is available in the
|
|
|
17
17
|
## Quick start
|
|
18
18
|
|
|
19
19
|
```bash
|
|
20
|
-
opencode plugin opencode-plugin-flow@5.2.
|
|
20
|
+
opencode plugin opencode-plugin-flow@5.2.1 --global --force
|
|
21
21
|
```
|
|
22
22
|
|
|
23
23
|
Start or restart OpenCode, then give Flow a goal:
|
|
@@ -235,7 +235,7 @@ or sync command is required. To preview recoverable migration of pristine v4
|
|
|
235
235
|
global skill folders:
|
|
236
236
|
|
|
237
237
|
```bash
|
|
238
|
-
npx -y opencode-plugin-flow@5.2.
|
|
238
|
+
npx -y opencode-plugin-flow@5.2.1 legacy-cleanup --dry-run
|
|
239
239
|
```
|
|
240
240
|
|
|
241
241
|
## Development
|
|
@@ -3,7 +3,10 @@ import type { EvidenceArtifactStore } from "./evidence-artifact-store.js";
|
|
|
3
3
|
import type { SourceIdentityProvider } from "./source-identity.js";
|
|
4
4
|
export declare class ArchivedSessionLookupError extends Error {
|
|
5
5
|
readonly code = "FLOW_ARCHIVE_LOOKUP_FAILED";
|
|
6
|
-
|
|
6
|
+
readonly failureKind: "history-integrity" | "helper-runtime";
|
|
7
|
+
constructor(message: string, options?: ErrorOptions & {
|
|
8
|
+
failureKind?: "history-integrity" | "helper-runtime";
|
|
9
|
+
});
|
|
7
10
|
}
|
|
8
11
|
export type SessionTransaction = EvidenceArtifactStore & SourceIdentityProvider & {
|
|
9
12
|
load(): Promise<Session | null>;
|
package/dist/index.js
CHANGED
|
@@ -3313,9 +3313,11 @@ class UnsupportedFlowSessionVersionError extends Error {
|
|
|
3313
3313
|
// src/application/ports/session-repository.ts
|
|
3314
3314
|
class ArchivedSessionLookupError extends Error {
|
|
3315
3315
|
code = "FLOW_ARCHIVE_LOOKUP_FAILED";
|
|
3316
|
+
failureKind;
|
|
3316
3317
|
constructor(message, options) {
|
|
3317
3318
|
super(message, options);
|
|
3318
3319
|
this.name = "ArchivedSessionLookupError";
|
|
3320
|
+
this.failureKind = options?.failureKind ?? "history-integrity";
|
|
3319
3321
|
}
|
|
3320
3322
|
}
|
|
3321
3323
|
|
|
@@ -7292,6 +7294,17 @@ async function assertManagedDirectoryIdentity(path, description, expected) {
|
|
|
7292
7294
|
throw new UnsafeFlowWorkspaceLayoutError(`Flow detected that ${description} changed during a managed operation: ${path}.`);
|
|
7293
7295
|
}
|
|
7294
7296
|
}
|
|
7297
|
+
|
|
7298
|
+
class PinnedFilesystemHelperError extends Error {
|
|
7299
|
+
code = "FLOW_PINNED_HELPER_FAILED";
|
|
7300
|
+
constructor(message, options) {
|
|
7301
|
+
super(message, options);
|
|
7302
|
+
this.name = "PinnedFilesystemHelperError";
|
|
7303
|
+
}
|
|
7304
|
+
}
|
|
7305
|
+
function pinnedHelperFailure(message, cause) {
|
|
7306
|
+
return new PinnedFilesystemHelperError(message, { cause });
|
|
7307
|
+
}
|
|
7295
7308
|
function helperFailure(stderr, cause) {
|
|
7296
7309
|
let detail = null;
|
|
7297
7310
|
try {
|
|
@@ -7300,16 +7313,23 @@ function helperFailure(stderr, cause) {
|
|
|
7300
7313
|
if (detail?.code === "FLOW_PINNED_DIRECTORY_MISMATCH" || detail?.code === "FLOW_ARCHIVE_CASE_COLLISION" || detail?.code === "FLOW_ARCHIVE_UNKNOWN_JSON") {
|
|
7301
7314
|
return new UnsafeFlowWorkspaceLayoutError(detail.message ?? "Flow detected an unsafe pinned directory change.", { cause });
|
|
7302
7315
|
}
|
|
7303
|
-
|
|
7304
|
-
|
|
7305
|
-
|
|
7306
|
-
|
|
7316
|
+
return new PinnedFilesystemHelperError(detail?.message ?? "Flow pinned filesystem helper failed.", { cause });
|
|
7317
|
+
}
|
|
7318
|
+
function pinnedHelperEnvironment(runtime) {
|
|
7319
|
+
if (runtime === "electron") {
|
|
7320
|
+
return { ...process.env, ELECTRON_RUN_AS_NODE: "1" };
|
|
7321
|
+
}
|
|
7322
|
+
if (runtime === "bun") {
|
|
7323
|
+
return { ...process.env, BUN_BE_BUN: "1" };
|
|
7324
|
+
}
|
|
7325
|
+
return process.env;
|
|
7307
7326
|
}
|
|
7308
7327
|
async function runPinnedDirectoryHelper(cwd, request, input = "", afterPinned, options = {}) {
|
|
7309
7328
|
const encodedRequest = Buffer.from(JSON.stringify(request), "utf8").toString("base64");
|
|
7329
|
+
const runtime = options.pinnedHelperTestRuntime ?? (process.versions.electron ? "electron" : process.versions.bun ? "bun" : "node");
|
|
7310
7330
|
const child = spawn(options.pinnedHelperTestExecutable ?? process.execPath, ["--eval", PINNED_DIRECTORY_HELPER_SOURCE, encodedRequest], {
|
|
7311
7331
|
cwd,
|
|
7312
|
-
env:
|
|
7332
|
+
env: pinnedHelperEnvironment(runtime),
|
|
7313
7333
|
stdio: ["pipe", "pipe", "pipe"],
|
|
7314
7334
|
windowsHide: true
|
|
7315
7335
|
});
|
|
@@ -7337,7 +7357,7 @@ async function runPinnedDirectoryHelper(cwd, request, input = "", afterPinned, o
|
|
|
7337
7357
|
child.kill("SIGKILL");
|
|
7338
7358
|
};
|
|
7339
7359
|
const readyTimeout = setTimeout(() => {
|
|
7340
|
-
terminateHelper(
|
|
7360
|
+
terminateHelper(pinnedHelperFailure("Flow pinned filesystem helper timed out before readiness."));
|
|
7341
7361
|
}, positiveTimeout(options.pinnedHelperReadyTimeoutMs, PINNED_HELPER_READY_TIMEOUT_MS));
|
|
7342
7362
|
readyTimeout.unref();
|
|
7343
7363
|
let completionTimeout;
|
|
@@ -7351,18 +7371,18 @@ async function runPinnedDirectoryHelper(cwd, request, input = "", afterPinned, o
|
|
|
7351
7371
|
try {
|
|
7352
7372
|
event = JSON.parse(stdout.slice(0, newline));
|
|
7353
7373
|
} catch (error) {
|
|
7354
|
-
terminateHelper(
|
|
7374
|
+
terminateHelper(pinnedHelperFailure("Flow pinned filesystem helper returned an invalid ready event.", error));
|
|
7355
7375
|
return;
|
|
7356
7376
|
}
|
|
7357
7377
|
if (event.event !== "pinned") {
|
|
7358
|
-
terminateHelper(
|
|
7378
|
+
terminateHelper(pinnedHelperFailure("Flow pinned filesystem helper omitted its ready event."));
|
|
7359
7379
|
return;
|
|
7360
7380
|
}
|
|
7361
7381
|
stdout = stdout.slice(newline + 1);
|
|
7362
7382
|
readyState = "resolved";
|
|
7363
7383
|
clearTimeout(readyTimeout);
|
|
7364
7384
|
completionTimeout = setTimeout(() => {
|
|
7365
|
-
terminateHelper(
|
|
7385
|
+
terminateHelper(pinnedHelperFailure("Flow pinned filesystem helper timed out before completion."));
|
|
7366
7386
|
}, positiveTimeout(options.pinnedHelperCompletionTimeoutMs, PINNED_HELPER_COMPLETION_TIMEOUT_MS));
|
|
7367
7387
|
completionTimeout.unref();
|
|
7368
7388
|
resolvePinned?.();
|
|
@@ -7394,7 +7414,7 @@ async function runPinnedDirectoryHelper(cwd, request, input = "", afterPinned, o
|
|
|
7394
7414
|
return;
|
|
7395
7415
|
}
|
|
7396
7416
|
if (readyState !== "resolved") {
|
|
7397
|
-
const failure =
|
|
7417
|
+
const failure = pinnedHelperFailure("Flow could not start its pinned filesystem helper under the current host runtime.");
|
|
7398
7418
|
if (readyState === "pending") {
|
|
7399
7419
|
readyState = "rejected";
|
|
7400
7420
|
rejectPinned?.(failure);
|
|
@@ -7409,9 +7429,7 @@ async function runPinnedDirectoryHelper(cwd, request, input = "", afterPinned, o
|
|
|
7409
7429
|
try {
|
|
7410
7430
|
resolve2(JSON.parse(stdout));
|
|
7411
7431
|
} catch (error) {
|
|
7412
|
-
reject(
|
|
7413
|
-
cause: error
|
|
7414
|
-
}));
|
|
7432
|
+
reject(pinnedHelperFailure("Flow pinned filesystem helper returned invalid output.", error));
|
|
7415
7433
|
}
|
|
7416
7434
|
});
|
|
7417
7435
|
});
|
|
@@ -7645,6 +7663,12 @@ async function findCanonicalArchivedSession(worktree, predicate) {
|
|
|
7645
7663
|
} catch (error) {
|
|
7646
7664
|
if (error instanceof ArchivedSessionLookupError)
|
|
7647
7665
|
throw error;
|
|
7666
|
+
if (error instanceof PinnedFilesystemHelperError) {
|
|
7667
|
+
throw new ArchivedSessionLookupError(error.message, {
|
|
7668
|
+
cause: error,
|
|
7669
|
+
failureKind: "helper-runtime"
|
|
7670
|
+
});
|
|
7671
|
+
}
|
|
7648
7672
|
throw new ArchivedSessionLookupError("Flow could not verify archived operation history safely.", { cause: error });
|
|
7649
7673
|
}
|
|
7650
7674
|
}
|
|
@@ -8171,43 +8195,46 @@ function archivedCloseResponse(session, operationId) {
|
|
|
8171
8195
|
}, [], operationId);
|
|
8172
8196
|
}
|
|
8173
8197
|
function archivedLookupFailureResponse(error, operationId) {
|
|
8198
|
+
const helperRuntimeFailure = error.failureKind === "helper-runtime";
|
|
8174
8199
|
return rejectedMutationResponse({
|
|
8175
8200
|
status: "error",
|
|
8176
|
-
summary: "Flow could not verify archived retry history.",
|
|
8177
|
-
nextAction: "Inspect canonical Flow history integrity before retrying this close operation.",
|
|
8201
|
+
summary: helperRuntimeFailure ? "Flow could not start its filesystem helper to read archived retry history." : "Flow could not verify archived retry history.",
|
|
8202
|
+
nextAction: helperRuntimeFailure ? "Restart OpenCode with the current Flow build, then retry this close operation." : "Inspect canonical Flow history integrity before retrying this close operation.",
|
|
8178
8203
|
dataNote: WORKFLOW_DATA_NOTE,
|
|
8179
8204
|
workflowData: {
|
|
8180
8205
|
failure: {
|
|
8181
8206
|
summary: error.message,
|
|
8182
|
-
recovery: "Preserve archive files and resolve corrupt or ambiguous canonical history; quarantine records are not replay sources."
|
|
8207
|
+
recovery: helperRuntimeFailure ? "Preserve Flow state, restart OpenCode after updating Flow, and retry with the same close operation id." : "Preserve archive files and resolve corrupt or ambiguous canonical history; quarantine records are not replay sources."
|
|
8183
8208
|
}
|
|
8184
8209
|
}
|
|
8185
8210
|
}, null, operationId);
|
|
8186
8211
|
}
|
|
8187
8212
|
function archivedCloseStartLookupFailureResponse(error, session, operationId) {
|
|
8213
|
+
const helperRuntimeFailure = error.failureKind === "helper-runtime";
|
|
8188
8214
|
return rejectedMutationResponse({
|
|
8189
8215
|
status: "error",
|
|
8190
|
-
summary: "Flow could not prove that this close operation id is unique in canonical history.",
|
|
8191
|
-
nextAction: "Inspect canonical Flow history integrity before starting this close operation.",
|
|
8216
|
+
summary: helperRuntimeFailure ? "Flow could not start its filesystem helper to verify this close operation id." : "Flow could not prove that this close operation id is unique in canonical history.",
|
|
8217
|
+
nextAction: helperRuntimeFailure ? "Restart OpenCode with the current Flow build, then start this close operation again." : "Inspect canonical Flow history integrity before starting this close operation.",
|
|
8192
8218
|
dataNote: WORKFLOW_DATA_NOTE,
|
|
8193
8219
|
workflowData: {
|
|
8194
8220
|
failure: {
|
|
8195
8221
|
summary: error.message,
|
|
8196
|
-
recovery: "Preserve the active session and resolve corrupt or ambiguous canonical history before retrying with a verified operation id."
|
|
8222
|
+
recovery: helperRuntimeFailure ? "Preserve the active session, restart OpenCode after updating Flow, and retry with the same unconsumed operation id." : "Preserve the active session and resolve corrupt or ambiguous canonical history before retrying with a verified operation id."
|
|
8197
8223
|
}
|
|
8198
8224
|
}
|
|
8199
8225
|
}, session, operationId);
|
|
8200
8226
|
}
|
|
8201
8227
|
function archivedCloseRetryLookupFailureResponse(error, session, operationId) {
|
|
8228
|
+
const helperRuntimeFailure = error.failureKind === "helper-runtime";
|
|
8202
8229
|
return rejectedMutationResponse({
|
|
8203
8230
|
status: "error",
|
|
8204
|
-
summary: "Flow could not verify canonical history before publishing the pending close.",
|
|
8205
|
-
nextAction: "Inspect canonical Flow history integrity before retrying archive publication.",
|
|
8231
|
+
summary: helperRuntimeFailure ? "Flow could not start its filesystem helper before publishing the pending close." : "Flow could not verify canonical history before publishing the pending close.",
|
|
8232
|
+
nextAction: helperRuntimeFailure ? "Restart OpenCode with the current Flow build, then retry archive publication." : "Inspect canonical Flow history integrity before retrying archive publication.",
|
|
8206
8233
|
dataNote: WORKFLOW_DATA_NOTE,
|
|
8207
8234
|
workflowData: {
|
|
8208
8235
|
failure: {
|
|
8209
8236
|
summary: error.message,
|
|
8210
|
-
recovery: "Preserve the active closed session and resolve corrupt, ambiguous, or conflicting canonical history before retrying its durable close operation."
|
|
8237
|
+
recovery: helperRuntimeFailure ? "Preserve the active closed session, restart OpenCode after updating Flow, and retry its exact durable close operation." : "Preserve the active closed session and resolve corrupt, ambiguous, or conflicting canonical history before retrying its durable close operation."
|
|
8211
8238
|
}
|
|
8212
8239
|
}
|
|
8213
8240
|
}, session, operationId);
|
|
@@ -9862,4 +9889,4 @@ export {
|
|
|
9862
9889
|
plugin_default as default
|
|
9863
9890
|
};
|
|
9864
9891
|
|
|
9865
|
-
//# debugId=
|
|
9892
|
+
//# debugId=E001CAB8DE3EBE1964756E2164756E21
|