@prevalentware/opencode-goal-plugin 0.1.56 → 0.1.57
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 +4 -1
- package/dist/server.js +63 -8
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -326,4 +326,7 @@ The goal sidebar shows the current status, elapsed time, token usage, auto-conti
|
|
|
326
326
|
|
|
327
327
|
Cancelling an active goal from an ACP client is durable when OpenCode emits the user-cancellation events described above. The plugin prevents subsequent goal continuations, including callbacks still preparing a prompt when cancellation arrives. OpenCode owns cancellation of in-flight model requests, tools, subprocesses, and already submitted prompts; the plugin cannot guarantee process termination if the host does not abort them or does not publish a cancellation signal. It does not interpret ordinary idle events, provider-error text, or completed tool calls as user cancellation.
|
|
328
328
|
|
|
329
|
-
|
|
329
|
+
Registered `/goal <objective>` and `/resume_goal` commands use the host's public session-wait API to remain pending across automatic continuations until the goal stops, completes, reaches a safety limit, or auto-continuation is disabled. Hosts without that API keep their previous admission behavior. The ACP adapter must also stream each execution owned by the command: an adapter that ends its event consumer after the first execution can still hide later work. Ordinary prompts retain their host-defined turn lifetime.
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
The Promise-based persistence layer uses Effect 3 under the private `effect-goal-state` npm alias. This keeps it separate from modern OpenCode's shared Effect 4 SDK runtime. It remains an external runtime dependency, and no private Effect values cross the plugin API.
|
package/dist/server.js
CHANGED
|
@@ -6,7 +6,7 @@ import { z } from "zod";
|
|
|
6
6
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
7
7
|
import { mkdir, readFile } from "fs/promises";
|
|
8
8
|
import { dirname as dirname2 } from "path";
|
|
9
|
-
import { Data, Effect, Schema } from "effect";
|
|
9
|
+
import { Data, Effect, Schema } from "effect-goal-state";
|
|
10
10
|
|
|
11
11
|
// src/atomic-write.ts
|
|
12
12
|
import { randomUUID } from "crypto";
|
|
@@ -3744,6 +3744,7 @@ async function setupV2(context) {
|
|
|
3744
3744
|
};
|
|
3745
3745
|
const registrations = [];
|
|
3746
3746
|
let disposed = false;
|
|
3747
|
+
let eventConsumerStopped = false;
|
|
3747
3748
|
function stepKey(sessionID, messageID) {
|
|
3748
3749
|
return `${sessionID}\x00${messageID}`;
|
|
3749
3750
|
}
|
|
@@ -4391,7 +4392,7 @@ async function setupV2(context) {
|
|
|
4391
4392
|
draft.add({
|
|
4392
4393
|
name: command.name,
|
|
4393
4394
|
description: command.description,
|
|
4394
|
-
execute: async (input) => {
|
|
4395
|
+
execute: async (input, execution) => {
|
|
4395
4396
|
markSessionOwnership(input.sessionID, true);
|
|
4396
4397
|
if (command.action === "pause") {
|
|
4397
4398
|
const goal = await getGoal(input.sessionID);
|
|
@@ -4414,12 +4415,63 @@ async function setupV2(context) {
|
|
|
4414
4415
|
...skills ? { skills: skills.map(stripMention) } : {}
|
|
4415
4416
|
};
|
|
4416
4417
|
}
|
|
4417
|
-
|
|
4418
|
-
|
|
4419
|
-
|
|
4420
|
-
|
|
4421
|
-
|
|
4422
|
-
|
|
4418
|
+
const args = input.prompt.text.trim().toLowerCase();
|
|
4419
|
+
const controlOnly = /^(history|status|show|current|pause|stop|cancel|clear|off|reset|none|edit)(?:\s|$)/.test(args);
|
|
4420
|
+
const pursue = command.action === "resume" || command.action === "goal" && args !== "" && !controlOnly;
|
|
4421
|
+
const signal = execution?.signal ? AbortSignal.any([abortController.signal, execution.signal]) : abortController.signal;
|
|
4422
|
+
let admitted = false;
|
|
4423
|
+
let cancellation;
|
|
4424
|
+
const cancel = () => {
|
|
4425
|
+
if (!pursue || disposed)
|
|
4426
|
+
return;
|
|
4427
|
+
goalServices.stopAutonomy?.(input.sessionID);
|
|
4428
|
+
cancellation = cancelActiveGoal(input.sessionID).catch((error) => v2ErrorLog("Failed to persist command cancellation", error));
|
|
4429
|
+
};
|
|
4430
|
+
execution?.signal?.addEventListener("abort", cancel, { once: true });
|
|
4431
|
+
try {
|
|
4432
|
+
if (execution?.signal?.aborted) {
|
|
4433
|
+
cancel();
|
|
4434
|
+
return;
|
|
4435
|
+
}
|
|
4436
|
+
await context.session.prompt({
|
|
4437
|
+
...forwardedPrompt,
|
|
4438
|
+
sessionID: input.sessionID,
|
|
4439
|
+
text: command.template.replaceAll("$ARGUMENTS", () => escapeXmlText2(input.prompt.text.trim())),
|
|
4440
|
+
delivery: input.delivery
|
|
4441
|
+
});
|
|
4442
|
+
admitted = true;
|
|
4443
|
+
if (pursue && typeof context.session.wait === "function") {
|
|
4444
|
+
let pursuedGoalID;
|
|
4445
|
+
do {
|
|
4446
|
+
await context.session.wait({ sessionID: input.sessionID }, { signal });
|
|
4447
|
+
if (eventConsumerStopped)
|
|
4448
|
+
throw new Error("goal event stream stopped");
|
|
4449
|
+
const goal = await getGoal(input.sessionID);
|
|
4450
|
+
if (signal.aborted || disposed || stoppedExecutions.has(input.sessionID) || !autoContinue || goal?.status !== "active")
|
|
4451
|
+
break;
|
|
4452
|
+
pursuedGoalID ??= goal.id;
|
|
4453
|
+
if (goal.id !== pursuedGoalID)
|
|
4454
|
+
break;
|
|
4455
|
+
await new Promise((resolve) => setTimeout(resolve, 250));
|
|
4456
|
+
} while (!disposed && !signal.aborted);
|
|
4457
|
+
}
|
|
4458
|
+
} catch (error) {
|
|
4459
|
+
if (!admitted && !disposed && !execution?.signal?.aborted)
|
|
4460
|
+
throw error;
|
|
4461
|
+
if (!disposed && !execution?.signal?.aborted) {
|
|
4462
|
+
goalServices.stopAutonomy?.(input.sessionID);
|
|
4463
|
+
v2ErrorLog("Goal command wait failed; autonomous continuation stopped", error);
|
|
4464
|
+
try {
|
|
4465
|
+
if ((await getGoal(input.sessionID))?.status === "active")
|
|
4466
|
+
await setGoalStatus(input.sessionID, "paused");
|
|
4467
|
+
} catch (stateError) {
|
|
4468
|
+
v2ErrorLog("Failed to pause goal after command failure", stateError);
|
|
4469
|
+
}
|
|
4470
|
+
}
|
|
4471
|
+
} finally {
|
|
4472
|
+
execution?.signal?.removeEventListener("abort", cancel);
|
|
4473
|
+
await cancellation;
|
|
4474
|
+
}
|
|
4423
4475
|
}
|
|
4424
4476
|
});
|
|
4425
4477
|
}
|
|
@@ -4552,6 +4604,9 @@ async function setupV2(context) {
|
|
|
4552
4604
|
} catch (error) {
|
|
4553
4605
|
if (!abortController.signal.aborted)
|
|
4554
4606
|
v2ErrorLog("V2 event consumer stopped", error);
|
|
4607
|
+
} finally {
|
|
4608
|
+
if (!disposed && !abortController.signal.aborted)
|
|
4609
|
+
eventConsumerStopped = true;
|
|
4555
4610
|
}
|
|
4556
4611
|
})();
|
|
4557
4612
|
return async () => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prevalentware/opencode-goal-plugin",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.57",
|
|
4
4
|
"description": "OpenCode goal plugin that adds Codex-style long-running goal mode, /goal commands, persistence, and TUI status for AI coding agents.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"opencode",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
],
|
|
47
47
|
"scripts": {
|
|
48
48
|
"clean": "rm -rf dist",
|
|
49
|
-
"build": "bun run clean && bun build ./src/server.ts --outdir ./dist --target bun --external @opencode-ai/plugin --external effect --external zod",
|
|
49
|
+
"build": "bun run clean && bun build ./src/server.ts --outdir ./dist --target bun --external @opencode-ai/plugin --external effect-goal-state --external zod",
|
|
50
50
|
"ci:version": "bun scripts/resolve-ci-version.ts",
|
|
51
51
|
"lint": "eslint .",
|
|
52
52
|
"pack:dry-run": "npm pack --dry-run",
|
|
@@ -58,8 +58,8 @@
|
|
|
58
58
|
},
|
|
59
59
|
"dependencies": {
|
|
60
60
|
"@opencode-ai/plugin": "^1.17.1",
|
|
61
|
-
"
|
|
62
|
-
"
|
|
61
|
+
"zod": "^4.1.8",
|
|
62
|
+
"effect-goal-state": "npm:effect@^3.21.2"
|
|
63
63
|
},
|
|
64
64
|
"peerDependencies": {
|
|
65
65
|
"@opentui/solid": ">=0.5.8",
|