@prevalentware/opencode-goal-plugin 0.1.61 → 0.1.63
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/dist/server.js +116 -14
- package/package.json +1 -1
package/dist/server.js
CHANGED
|
@@ -3263,6 +3263,7 @@ var server = async ({ client }, options) => {
|
|
|
3263
3263
|
const scheduledContinuations = new Map;
|
|
3264
3264
|
const turnWatchdogs = new Map;
|
|
3265
3265
|
const busySessions = new Set;
|
|
3266
|
+
const busyRevisions = new Map;
|
|
3266
3267
|
const nativeRetrySessions = new Set;
|
|
3267
3268
|
const locallyDeliveredPendingSessions = new Set;
|
|
3268
3269
|
const toolAttempts = new Map;
|
|
@@ -3458,13 +3459,52 @@ var server = async ({ client }, options) => {
|
|
|
3458
3459
|
maybeUnref.unref();
|
|
3459
3460
|
scheduledContinuations.set(sessionID, scheduled);
|
|
3460
3461
|
}
|
|
3462
|
+
async function rearmBusyContinuation(sessionID, scheduled) {
|
|
3463
|
+
if (scheduled && scheduledContinuations.get(sessionID) !== scheduled)
|
|
3464
|
+
return;
|
|
3465
|
+
const goal = await getGoalInternal(sessionID);
|
|
3466
|
+
if (!taskDeferralGoalContinuable(goal)) {
|
|
3467
|
+
cancelScheduledContinuation(sessionID);
|
|
3468
|
+
return;
|
|
3469
|
+
}
|
|
3470
|
+
scheduleSettledContinuation(sessionID, TASK_BLOCK_RETRY_MS, scheduled != null, scheduled?.purpose ?? "settle");
|
|
3471
|
+
}
|
|
3472
|
+
async function scheduledSessionBusy(sessionID, scheduled) {
|
|
3473
|
+
if (!scheduled)
|
|
3474
|
+
return busySessions.has(sessionID);
|
|
3475
|
+
const session = client.session;
|
|
3476
|
+
if (!session.status)
|
|
3477
|
+
return busySessions.has(sessionID);
|
|
3478
|
+
const busyRevision = busyRevisions.get(sessionID) ?? 0;
|
|
3479
|
+
try {
|
|
3480
|
+
const result = await session.status();
|
|
3481
|
+
if (!isRecord(result) || result.error != null)
|
|
3482
|
+
return true;
|
|
3483
|
+
const statuses = isRecord(result.data) ? result.data : ("data" in result) || ("response" in result) ? null : result;
|
|
3484
|
+
if (!statuses)
|
|
3485
|
+
return true;
|
|
3486
|
+
const status = statuses[sessionID];
|
|
3487
|
+
if (isRecord(status) && (status.type === "busy" || status.type === "retry"))
|
|
3488
|
+
return true;
|
|
3489
|
+
if ((status == null || isRecord(status) && status.type === "idle") && (busyRevisions.get(sessionID) ?? 0) === busyRevision) {
|
|
3490
|
+
busySessions.delete(sessionID);
|
|
3491
|
+
}
|
|
3492
|
+
} catch {
|
|
3493
|
+
return true;
|
|
3494
|
+
}
|
|
3495
|
+
return busySessions.has(sessionID);
|
|
3496
|
+
}
|
|
3461
3497
|
async function runAutoContinue(sessionID, fromTaskDeferral = false, scheduled) {
|
|
3462
3498
|
if (disposed)
|
|
3463
3499
|
return;
|
|
3464
|
-
if (busySessions.has(sessionID))
|
|
3500
|
+
if (busySessions.has(sessionID) && !scheduled) {
|
|
3501
|
+
await rearmBusyContinuation(sessionID);
|
|
3465
3502
|
return;
|
|
3466
|
-
|
|
3503
|
+
}
|
|
3504
|
+
if (activeContinuations.has(sessionID)) {
|
|
3505
|
+
await rearmBusyContinuation(sessionID, scheduled);
|
|
3467
3506
|
return;
|
|
3507
|
+
}
|
|
3468
3508
|
const epoch = continuationEpochs.current(sessionID);
|
|
3469
3509
|
const isCurrent = () => !disposed && epoch === continuationEpochs.current(sessionID);
|
|
3470
3510
|
activeContinuations.add(sessionID);
|
|
@@ -3472,6 +3512,10 @@ var server = async ({ client }, options) => {
|
|
|
3472
3512
|
let attemptGoalID;
|
|
3473
3513
|
let attemptID;
|
|
3474
3514
|
try {
|
|
3515
|
+
if (await scheduledSessionBusy(sessionID, scheduled)) {
|
|
3516
|
+
await rearmBusyContinuation(sessionID, scheduled);
|
|
3517
|
+
return;
|
|
3518
|
+
}
|
|
3475
3519
|
const latestAssistant = await fetchLatestAssistant(client, sessionID);
|
|
3476
3520
|
if (!isCurrent())
|
|
3477
3521
|
return;
|
|
@@ -3492,8 +3536,12 @@ var server = async ({ client }, options) => {
|
|
|
3492
3536
|
scheduleSettledContinuation(sessionID, taskStatus.retryAt != null ? taskStatus.retryAt - Date.now() : TASK_BLOCK_RETRY_MS, scheduled != null || taskStatus.retryAt != null);
|
|
3493
3537
|
return;
|
|
3494
3538
|
}
|
|
3495
|
-
if (!isCurrent()
|
|
3539
|
+
if (!isCurrent())
|
|
3540
|
+
return;
|
|
3541
|
+
if (busySessions.has(sessionID)) {
|
|
3542
|
+
await rearmBusyContinuation(sessionID, scheduled);
|
|
3496
3543
|
return;
|
|
3544
|
+
}
|
|
3497
3545
|
const observed = await recordAssistantMessage(sessionID, latestAssistant, options ?? {}, true);
|
|
3498
3546
|
await reconcileLocalMarkerAfterProgress(locallyDeliveredPendingSessions, sessionID, observed.goal);
|
|
3499
3547
|
const queued = scheduledContinuations.get(sessionID);
|
|
@@ -3510,8 +3558,10 @@ var server = async ({ client }, options) => {
|
|
|
3510
3558
|
await pauseGoalForPlanMode(sessionID);
|
|
3511
3559
|
return;
|
|
3512
3560
|
}
|
|
3513
|
-
if (busySessions.has(sessionID))
|
|
3561
|
+
if (busySessions.has(sessionID)) {
|
|
3562
|
+
await rearmBusyContinuation(sessionID, scheduled);
|
|
3514
3563
|
return;
|
|
3564
|
+
}
|
|
3515
3565
|
if (!fromTaskDeferral && taskDeferredSessions.has(sessionID)) {
|
|
3516
3566
|
scheduleSettledContinuation(sessionID);
|
|
3517
3567
|
return;
|
|
@@ -3549,8 +3599,11 @@ var server = async ({ client }, options) => {
|
|
|
3549
3599
|
attemptGoalID = goal.id;
|
|
3550
3600
|
attemptID = goal.pendingAttempt?.id;
|
|
3551
3601
|
const beforeDelivery = await getGoalInternal(sessionID);
|
|
3552
|
-
|
|
3602
|
+
const busyBeforeDelivery = await scheduledSessionBusy(sessionID, scheduled);
|
|
3603
|
+
if (!isCurrent() || !continuationStillReserved(goal, beforeDelivery) || busyBeforeDelivery || nativeRetrySessions.has(sessionID)) {
|
|
3553
3604
|
await rollbackContinuationAttempt(sessionID, { goalID: attemptGoalID, attemptID });
|
|
3605
|
+
if (isCurrent() && busyBeforeDelivery)
|
|
3606
|
+
await rearmBusyContinuation(sessionID, scheduled);
|
|
3554
3607
|
return;
|
|
3555
3608
|
}
|
|
3556
3609
|
if (scheduled && scheduledContinuations.get(sessionID) !== scheduled) {
|
|
@@ -3871,6 +3924,7 @@ var server = async ({ client }, options) => {
|
|
|
3871
3924
|
if (isRecord(status) && typeof status.type === "string") {
|
|
3872
3925
|
if (status.type === "busy") {
|
|
3873
3926
|
busySessions.add(sessionID);
|
|
3927
|
+
busyRevisions.set(sessionID, (busyRevisions.get(sessionID) ?? 0) + 1);
|
|
3874
3928
|
nativeRetrySessions.delete(sessionID);
|
|
3875
3929
|
}
|
|
3876
3930
|
if (status.type === "busy")
|
|
@@ -3934,6 +3988,7 @@ var server = async ({ client }, options) => {
|
|
|
3934
3988
|
continuationEpochs.invalidate(sessionID);
|
|
3935
3989
|
explicitResumeRequests.delete(sessionID);
|
|
3936
3990
|
busySessions.delete(sessionID);
|
|
3991
|
+
busyRevisions.delete(sessionID);
|
|
3937
3992
|
clearTurnWatchdog(sessionID);
|
|
3938
3993
|
watchdogRescuedSessions.delete(sessionID);
|
|
3939
3994
|
locallyDeliveredPendingSessions.delete(sessionID);
|
|
@@ -3987,6 +4042,7 @@ async function setupV2(context) {
|
|
|
3987
4042
|
const scheduledContinuations = new Map;
|
|
3988
4043
|
const turnWatchdogs = new Map;
|
|
3989
4044
|
const busySessions = new Set;
|
|
4045
|
+
const busyRevisions = new Map;
|
|
3990
4046
|
const nativeRetrySessions = new Set;
|
|
3991
4047
|
const locallyDeliveredPendingSessions = new Set;
|
|
3992
4048
|
const watchdogRescuedSessions = new Set;
|
|
@@ -4189,25 +4245,59 @@ async function setupV2(context) {
|
|
|
4189
4245
|
maybeUnref.unref();
|
|
4190
4246
|
scheduledContinuations.set(sessionID, scheduled);
|
|
4191
4247
|
}
|
|
4248
|
+
async function rearmBusyContinuation(sessionID, scheduled) {
|
|
4249
|
+
if (scheduled && scheduledContinuations.get(sessionID) !== scheduled)
|
|
4250
|
+
return;
|
|
4251
|
+
const goal = await getGoalInternal(sessionID);
|
|
4252
|
+
if (!taskDeferralGoalContinuable(goal)) {
|
|
4253
|
+
cancelScheduledContinuation(sessionID);
|
|
4254
|
+
return;
|
|
4255
|
+
}
|
|
4256
|
+
scheduleSettledContinuation(sessionID, TASK_BLOCK_RETRY_MS, scheduled != null, scheduled?.purpose ?? "settle");
|
|
4257
|
+
}
|
|
4192
4258
|
async function runAutoContinue(sessionID, fromTaskDeferral = false, scheduled) {
|
|
4193
4259
|
if (disposed)
|
|
4194
4260
|
return;
|
|
4195
4261
|
if (stoppedExecutions.has(sessionID))
|
|
4196
4262
|
return;
|
|
4197
|
-
if (busySessions.has(sessionID))
|
|
4263
|
+
if (busySessions.has(sessionID) && !scheduled) {
|
|
4264
|
+
await rearmBusyContinuation(sessionID);
|
|
4198
4265
|
return;
|
|
4199
|
-
|
|
4266
|
+
}
|
|
4267
|
+
if (activeContinuationsV2.has(sessionID)) {
|
|
4268
|
+
await rearmBusyContinuation(sessionID, scheduled);
|
|
4200
4269
|
return;
|
|
4270
|
+
}
|
|
4201
4271
|
const epoch = continuationEpochs.current(sessionID);
|
|
4202
4272
|
const isCurrent = () => !disposed && epoch === continuationEpochs.current(sessionID);
|
|
4203
4273
|
await taskRecoveryComplete;
|
|
4204
|
-
if (!isCurrent() || stoppedExecutions.has(sessionID)
|
|
4274
|
+
if (!isCurrent() || stoppedExecutions.has(sessionID))
|
|
4205
4275
|
return;
|
|
4206
4276
|
activeContinuationsV2.add(sessionID);
|
|
4207
4277
|
let attemptReservedAt = Date.now();
|
|
4208
4278
|
let attemptGoalID;
|
|
4209
4279
|
let attemptID;
|
|
4210
4280
|
try {
|
|
4281
|
+
if (scheduled && typeof context.session.wait === "function") {
|
|
4282
|
+
const busyRevision = busyRevisions.get(sessionID) ?? 0;
|
|
4283
|
+
try {
|
|
4284
|
+
const signal = AbortSignal.any([abortController.signal, AbortSignal.timeout(TASK_BLOCK_RETRY_MS)]);
|
|
4285
|
+
await context.session.wait({ sessionID }, { signal });
|
|
4286
|
+
} catch {
|
|
4287
|
+
if (!isCurrent() || abortController.signal.aborted)
|
|
4288
|
+
return;
|
|
4289
|
+
await rearmBusyContinuation(sessionID, scheduled);
|
|
4290
|
+
return;
|
|
4291
|
+
}
|
|
4292
|
+
if ((busyRevisions.get(sessionID) ?? 0) === busyRevision)
|
|
4293
|
+
busySessions.delete(sessionID);
|
|
4294
|
+
}
|
|
4295
|
+
if (!isCurrent() || stoppedExecutions.has(sessionID))
|
|
4296
|
+
return;
|
|
4297
|
+
if (busySessions.has(sessionID)) {
|
|
4298
|
+
await rearmBusyContinuation(sessionID, scheduled);
|
|
4299
|
+
return;
|
|
4300
|
+
}
|
|
4211
4301
|
const latestStep = latestStepBySession.get(sessionID);
|
|
4212
4302
|
if (latestStep?.messageID) {
|
|
4213
4303
|
taskTracker.observeAssistantMessage(sessionID, { info: { id: latestStep.messageID, role: "assistant" } });
|
|
@@ -4226,8 +4316,10 @@ async function setupV2(context) {
|
|
|
4226
4316
|
scheduleSettledContinuation(sessionID, taskStatus.retryAt != null ? taskStatus.retryAt - Date.now() : TASK_BLOCK_RETRY_MS, scheduled != null);
|
|
4227
4317
|
return;
|
|
4228
4318
|
}
|
|
4229
|
-
if (busySessions.has(sessionID))
|
|
4319
|
+
if (busySessions.has(sessionID)) {
|
|
4320
|
+
await rearmBusyContinuation(sessionID, scheduled);
|
|
4230
4321
|
return;
|
|
4322
|
+
}
|
|
4231
4323
|
if (latestStep) {
|
|
4232
4324
|
const beforeProgress = await getGoalInternal(sessionID);
|
|
4233
4325
|
const after = await recordAssistantProgress(sessionID, {
|
|
@@ -4256,8 +4348,10 @@ async function setupV2(context) {
|
|
|
4256
4348
|
await pauseGoalForPlanMode(sessionID);
|
|
4257
4349
|
return;
|
|
4258
4350
|
}
|
|
4259
|
-
if (busySessions.has(sessionID))
|
|
4351
|
+
if (busySessions.has(sessionID)) {
|
|
4352
|
+
await rearmBusyContinuation(sessionID, scheduled);
|
|
4260
4353
|
return;
|
|
4354
|
+
}
|
|
4261
4355
|
if (!fromTaskDeferral && taskDeferredSessions.has(sessionID)) {
|
|
4262
4356
|
scheduleSettledContinuation(sessionID);
|
|
4263
4357
|
return;
|
|
@@ -4302,6 +4396,8 @@ async function setupV2(context) {
|
|
|
4302
4396
|
const beforeDelivery = await getGoalInternal(sessionID);
|
|
4303
4397
|
if (!isCurrent() || !continuationStillReserved(goal, beforeDelivery) || busySessions.has(sessionID) || nativeRetrySessions.has(sessionID)) {
|
|
4304
4398
|
await rollbackContinuationAttempt(sessionID, { goalID: attemptGoalID, attemptID });
|
|
4399
|
+
if (isCurrent() && busySessions.has(sessionID))
|
|
4400
|
+
await rearmBusyContinuation(sessionID, scheduled);
|
|
4305
4401
|
return;
|
|
4306
4402
|
}
|
|
4307
4403
|
if (scheduled && scheduledContinuations.get(sessionID) !== scheduled) {
|
|
@@ -4436,6 +4532,7 @@ async function setupV2(context) {
|
|
|
4436
4532
|
if (status.type === "busy") {
|
|
4437
4533
|
stoppedExecutions.delete(sessionID);
|
|
4438
4534
|
busySessions.add(sessionID);
|
|
4535
|
+
busyRevisions.set(sessionID, (busyRevisions.get(sessionID) ?? 0) + 1);
|
|
4439
4536
|
nativeRetrySessions.delete(sessionID);
|
|
4440
4537
|
armTurnWatchdog(sessionID);
|
|
4441
4538
|
await markPendingContinuationStarted(sessionID);
|
|
@@ -4537,6 +4634,7 @@ async function setupV2(context) {
|
|
|
4537
4634
|
stoppedExecutions.delete(sessionID);
|
|
4538
4635
|
sessionOwnership.delete(sessionID);
|
|
4539
4636
|
busySessions.delete(sessionID);
|
|
4637
|
+
busyRevisions.delete(sessionID);
|
|
4540
4638
|
clearTurnWatchdog(sessionID);
|
|
4541
4639
|
watchdogRescuedSessions.delete(sessionID);
|
|
4542
4640
|
locallyDeliveredPendingSessions.delete(sessionID);
|
|
@@ -4865,15 +4963,19 @@ async function setupV2(context) {
|
|
|
4865
4963
|
cancelScheduledContinuation(sessionID);
|
|
4866
4964
|
}
|
|
4867
4965
|
}));
|
|
4868
|
-
|
|
4869
|
-
|
|
4870
|
-
if (
|
|
4966
|
+
const reminder = systemReminder(locale);
|
|
4967
|
+
const ensureSystemReminder = (system) => {
|
|
4968
|
+
if (system.some((part) => part.type === "text" && part.text.includes(reminder)))
|
|
4871
4969
|
return;
|
|
4872
|
-
|
|
4970
|
+
system.push({ type: "text", text: reminder });
|
|
4971
|
+
};
|
|
4972
|
+
registrations.push(await context.session.hook("context", (sessionContext) => {
|
|
4973
|
+
ensureSystemReminder(sessionContext.system);
|
|
4873
4974
|
}));
|
|
4874
4975
|
try {
|
|
4875
4976
|
const hookCompaction = context.session.hook;
|
|
4876
4977
|
registrations.push(await hookCompaction("compaction", async (event) => {
|
|
4978
|
+
ensureSystemReminder(event.system);
|
|
4877
4979
|
const goal = await getGoal(event.sessionID);
|
|
4878
4980
|
if (!goal)
|
|
4879
4981
|
return;
|
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.63",
|
|
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",
|