@bojackduy/opencode-loopd 1.9.6 → 1.9.7
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 +36 -13
- package/package.json +1 -1
package/dist/server.js
CHANGED
|
@@ -1334,7 +1334,7 @@ function createLoopEngine(options) {
|
|
|
1334
1334
|
revision: blockedState.revision
|
|
1335
1335
|
});
|
|
1336
1336
|
if (shouldNotifyBlocked) {
|
|
1337
|
-
await host.notifyOwner(goal.ownerSessionID, `Loop goal "${goal.name}" stopped: ${limitResult.reason} (child did not wrap up). Status: blocked. Last progress: ${goal.lastProgress?.summary || "none"}
|
|
1337
|
+
await host.notifyOwner(goal.ownerSessionID, `Loop goal "${goal.name}" stopped: ${limitResult.reason} (child did not wrap up). Status: blocked. Last progress: ${goal.lastProgress?.summary || "none"}.`, goal.parentAgent);
|
|
1338
1338
|
}
|
|
1339
1339
|
return true;
|
|
1340
1340
|
}
|
|
@@ -1473,7 +1473,7 @@ function createLoopEngine(options) {
|
|
|
1473
1473
|
revision: newState.revision
|
|
1474
1474
|
});
|
|
1475
1475
|
if (shouldNotify) {
|
|
1476
|
-
await host.notifyOwner(goal.ownerSessionID, `Loop goal "${goal.name}" blocked after ${runtime.consecutiveFailures + 1} failures. Last error: ${message}
|
|
1476
|
+
await host.notifyOwner(goal.ownerSessionID, `Loop goal "${goal.name}" blocked after ${runtime.consecutiveFailures + 1} failures. Last error: ${message}.`, goal.parentAgent);
|
|
1477
1477
|
}
|
|
1478
1478
|
}
|
|
1479
1479
|
return true;
|
|
@@ -1635,7 +1635,7 @@ function createLoopEngine(options) {
|
|
|
1635
1635
|
costUsed: stoppedGoal.costUsed
|
|
1636
1636
|
});
|
|
1637
1637
|
if (shouldNotify) {
|
|
1638
|
-
await host.notifyOwner(goal.ownerSessionID, `Loop goal "${goal.name}" stopped: ${reason}. Worker aborted, status: budget_limited. Resume with resume_goal to continue spending
|
|
1638
|
+
await host.notifyOwner(goal.ownerSessionID, `Loop goal "${goal.name}" stopped: ${reason}. Worker aborted, status: budget_limited. Resume with resume_goal to continue spending.`, goal.parentAgent);
|
|
1639
1639
|
}
|
|
1640
1640
|
return true;
|
|
1641
1641
|
}
|
|
@@ -1745,7 +1745,7 @@ function createLoopEngine(options) {
|
|
|
1745
1745
|
workerSessionID: goal.workerSessionID,
|
|
1746
1746
|
count: unknownRuntime?.unknownStatusCount
|
|
1747
1747
|
});
|
|
1748
|
-
await host.notifyOwner(goal.ownerSessionID, `Loop goal "${goal.name}" worker is unreachable after ${unknownRuntime?.unknownStatusCount ?? unknownStatusThreshold} status checks. The goal remains active; use inspect_background_goal, nudge_goal, pause_goal, or resume_goal to recover it
|
|
1748
|
+
await host.notifyOwner(goal.ownerSessionID, `Loop goal "${goal.name}" worker is unreachable after ${unknownRuntime?.unknownStatusCount ?? unknownStatusThreshold} status checks. The goal remains active; use inspect_background_goal, nudge_goal, pause_goal, or resume_goal to recover it.`, goal.parentAgent);
|
|
1749
1749
|
}
|
|
1750
1750
|
continue;
|
|
1751
1751
|
}
|
|
@@ -1840,7 +1840,7 @@ function createLoopEngine(options) {
|
|
|
1840
1840
|
stuckSeconds
|
|
1841
1841
|
});
|
|
1842
1842
|
const quietMinutes = Math.max(1, Math.floor(stuckSeconds / 60));
|
|
1843
|
-
await host.notifyOwner(goal.ownerSessionID, `Loop goal "${goal.name}" worker is idle but its turn will not confirm (unconfirmed for ${quietMinutes}m, no activity). The goal remains active; use inspect_background_goal to look, nudge_goal to re-prompt, or pause_goal to stop it
|
|
1843
|
+
await host.notifyOwner(goal.ownerSessionID, `Loop goal "${goal.name}" worker is idle but its turn will not confirm (unconfirmed for ${quietMinutes}m, no activity). The goal remains active; use inspect_background_goal to look, nudge_goal to re-prompt, or pause_goal to stop it.`, goal.parentAgent);
|
|
1844
1844
|
continue;
|
|
1845
1845
|
}
|
|
1846
1846
|
if (status === "idle") {
|
|
@@ -1879,7 +1879,7 @@ function createLoopEngine(options) {
|
|
|
1879
1879
|
runID: runtime.activeRunID,
|
|
1880
1880
|
stuckSeconds
|
|
1881
1881
|
});
|
|
1882
|
-
await host.notifyOwner(goal.ownerSessionID, `Loop goal "${goal.name}" worker may be stuck: no activity for ${Math.floor(stuckSeconds / 60)}m while reporting ${status}, lease expired. The goal remains active; use inspect_background_goal to look, nudge_goal to re-prompt, or pause_goal to stop it
|
|
1882
|
+
await host.notifyOwner(goal.ownerSessionID, `Loop goal "${goal.name}" worker may be stuck: no activity for ${Math.floor(stuckSeconds / 60)}m while reporting ${status}, lease expired. The goal remains active; use inspect_background_goal to look, nudge_goal to re-prompt, or pause_goal to stop it.`, goal.parentAgent);
|
|
1883
1883
|
}
|
|
1884
1884
|
}
|
|
1885
1885
|
}
|
|
@@ -2070,15 +2070,27 @@ function createRealHost(client, directory) {
|
|
|
2070
2070
|
await client.session.compact({ sessionID });
|
|
2071
2071
|
} catch {}
|
|
2072
2072
|
},
|
|
2073
|
-
async notifyOwner(ownerSessionID, message) {
|
|
2073
|
+
async notifyOwner(ownerSessionID, message, agent) {
|
|
2074
2074
|
if (shouldDedupParentNotify(ownerSessionID, message)) {
|
|
2075
2075
|
await logServerEvent(directory, "parent.notify.deduped", { ownerSessionID, preview: message.slice(0, 160) });
|
|
2076
2076
|
return;
|
|
2077
2077
|
}
|
|
2078
|
+
let resolvedAgent = agent?.trim() || undefined;
|
|
2079
|
+
if (!resolvedAgent) {
|
|
2080
|
+
try {
|
|
2081
|
+
const sess = await client.session.get({ path: { id: ownerSessionID } });
|
|
2082
|
+
const liveAgent = sess?.data?.agent;
|
|
2083
|
+
if (typeof liveAgent === "string" && liveAgent.trim())
|
|
2084
|
+
resolvedAgent = liveAgent.trim();
|
|
2085
|
+
} catch {}
|
|
2086
|
+
}
|
|
2078
2087
|
try {
|
|
2088
|
+
const body = { parts: [{ type: "text", text: message }] };
|
|
2089
|
+
if (resolvedAgent)
|
|
2090
|
+
body.agent = resolvedAgent;
|
|
2079
2091
|
const result = await withTimeout(client.session.promptAsync({
|
|
2080
2092
|
path: { id: ownerSessionID },
|
|
2081
|
-
body
|
|
2093
|
+
body
|
|
2082
2094
|
}), 1e4, "OpenCode parent notify");
|
|
2083
2095
|
if (result?.error) {
|
|
2084
2096
|
await logServerEvent(directory, "parent.notify.failed", { ownerSessionID, detail: describeError(result.error) });
|
|
@@ -2361,6 +2373,17 @@ function createGoalService(host) {
|
|
|
2361
2373
|
return withGoalOperation(id, () => startUnlocked(directory, input, id));
|
|
2362
2374
|
}
|
|
2363
2375
|
async function startUnlocked(directory, input, id) {
|
|
2376
|
+
let parentAgent = input.parentAgent;
|
|
2377
|
+
let parentModel = input.parentModel;
|
|
2378
|
+
if ((!parentAgent || !parentModel) && host.readSession) {
|
|
2379
|
+
try {
|
|
2380
|
+
const identity = await host.readSession(input.ownerSessionID);
|
|
2381
|
+
if (!parentAgent && identity?.agent)
|
|
2382
|
+
parentAgent = identity.agent;
|
|
2383
|
+
if (!parentModel && identity?.model)
|
|
2384
|
+
parentModel = `${identity.model.providerID}/${identity.model.modelID}`;
|
|
2385
|
+
} catch {}
|
|
2386
|
+
}
|
|
2364
2387
|
const goal = createGoal({
|
|
2365
2388
|
id,
|
|
2366
2389
|
name: input.name,
|
|
@@ -2375,10 +2398,10 @@ function createGoalService(host) {
|
|
|
2375
2398
|
});
|
|
2376
2399
|
if (typeof input.costBudget === "number")
|
|
2377
2400
|
goal.costBudget = input.costBudget;
|
|
2378
|
-
if (
|
|
2379
|
-
goal.parentAgent =
|
|
2380
|
-
if (
|
|
2381
|
-
goal.parentModel =
|
|
2401
|
+
if (parentAgent)
|
|
2402
|
+
goal.parentAgent = parentAgent;
|
|
2403
|
+
if (parentModel)
|
|
2404
|
+
goal.parentModel = parentModel;
|
|
2382
2405
|
const artifactDir = goalArtifactDir(directory, id);
|
|
2383
2406
|
goal.config.artifactDir = artifactDir;
|
|
2384
2407
|
if (!goal.config.progressFile)
|
|
@@ -4432,7 +4455,7 @@ var server = async ({ client, directory }, pluginOptions) => {
|
|
|
4432
4455
|
});
|
|
4433
4456
|
}
|
|
4434
4457
|
const message = parsed.status === "complete" ? `Loop goal "${goal.name}" completed: ${parsed.summary || ""}. Evidence: ${parsed.evidence || ""}. Artifacts: ${goal.config.artifactDir || "n/a"}.` : `Loop goal "${goal.name}" blocked: ${parsed.reason || ""}. Needed: ${parsed.needed || ""}.`;
|
|
4435
|
-
await host.notifyOwner(goal.ownerSessionID, message);
|
|
4458
|
+
await host.notifyOwner(goal.ownerSessionID, message, goal.parentAgent);
|
|
4436
4459
|
} catch {}
|
|
4437
4460
|
}
|
|
4438
4461
|
},
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/package.json",
|
|
3
3
|
"name": "@bojackduy/opencode-loopd",
|
|
4
|
-
"version": "1.9.
|
|
4
|
+
"version": "1.9.7",
|
|
5
5
|
"description": "Codex-inspired background goal engine for OpenCode — autonomous subagents, engine-driven loop, child worker sessions and modal TUI dashboard. Like Claude Code loop for OpenCode.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"license": "AGPL-3.0-or-later",
|