@bojackduy/opencode-loopd 1.9.5 → 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 +106 -20
- package/dist/tui.js +187 -143
- package/package.json +1 -1
- package/skills/loopd/SKILL.md +5 -5
package/dist/server.js
CHANGED
|
@@ -499,10 +499,12 @@ function resolveGoalCreationConfig(input) {
|
|
|
499
499
|
const defaults = input.defaults || {};
|
|
500
500
|
const explicitAgent = cleanText(requested.agent);
|
|
501
501
|
const defaultAgent = cleanText(defaults.defaultAgent);
|
|
502
|
-
const
|
|
502
|
+
const parentAgent = cleanText(defaults.parentAgent);
|
|
503
|
+
const agent = explicitAgent || parentAgent || defaultAgent || undefined;
|
|
503
504
|
const explicitModel = cleanText(requested.model);
|
|
504
505
|
const defaultModel = cleanText(defaults.defaultModel);
|
|
505
|
-
const
|
|
506
|
+
const parentModel = cleanText(defaults.parentModel);
|
|
507
|
+
const model = explicitModel || parentModel || defaultModel || undefined;
|
|
506
508
|
if (model && !isValidModelRef(model)) {
|
|
507
509
|
return {
|
|
508
510
|
ok: false,
|
|
@@ -532,8 +534,8 @@ function resolveGoalCreationConfig(input) {
|
|
|
532
534
|
checkCwd: requested.checkCwd || (workspaceWrite ? input.directory : undefined)
|
|
533
535
|
},
|
|
534
536
|
defaultsApplied: {
|
|
535
|
-
agent: !explicitAgent && Boolean(defaultAgent),
|
|
536
|
-
model: !explicitModel && Boolean(defaultModel),
|
|
537
|
+
agent: !explicitAgent && Boolean(parentAgent || defaultAgent),
|
|
538
|
+
model: !explicitModel && Boolean(parentModel || defaultModel),
|
|
537
539
|
checks: explicitChecks.length === 0 && defaultChecks.length > 0
|
|
538
540
|
}
|
|
539
541
|
};
|
|
@@ -1332,7 +1334,7 @@ function createLoopEngine(options) {
|
|
|
1332
1334
|
revision: blockedState.revision
|
|
1333
1335
|
});
|
|
1334
1336
|
if (shouldNotifyBlocked) {
|
|
1335
|
-
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);
|
|
1336
1338
|
}
|
|
1337
1339
|
return true;
|
|
1338
1340
|
}
|
|
@@ -1471,7 +1473,7 @@ function createLoopEngine(options) {
|
|
|
1471
1473
|
revision: newState.revision
|
|
1472
1474
|
});
|
|
1473
1475
|
if (shouldNotify) {
|
|
1474
|
-
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);
|
|
1475
1477
|
}
|
|
1476
1478
|
}
|
|
1477
1479
|
return true;
|
|
@@ -1633,7 +1635,7 @@ function createLoopEngine(options) {
|
|
|
1633
1635
|
costUsed: stoppedGoal.costUsed
|
|
1634
1636
|
});
|
|
1635
1637
|
if (shouldNotify) {
|
|
1636
|
-
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);
|
|
1637
1639
|
}
|
|
1638
1640
|
return true;
|
|
1639
1641
|
}
|
|
@@ -1743,7 +1745,7 @@ function createLoopEngine(options) {
|
|
|
1743
1745
|
workerSessionID: goal.workerSessionID,
|
|
1744
1746
|
count: unknownRuntime?.unknownStatusCount
|
|
1745
1747
|
});
|
|
1746
|
-
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);
|
|
1747
1749
|
}
|
|
1748
1750
|
continue;
|
|
1749
1751
|
}
|
|
@@ -1838,7 +1840,7 @@ function createLoopEngine(options) {
|
|
|
1838
1840
|
stuckSeconds
|
|
1839
1841
|
});
|
|
1840
1842
|
const quietMinutes = Math.max(1, Math.floor(stuckSeconds / 60));
|
|
1841
|
-
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);
|
|
1842
1844
|
continue;
|
|
1843
1845
|
}
|
|
1844
1846
|
if (status === "idle") {
|
|
@@ -1877,7 +1879,7 @@ function createLoopEngine(options) {
|
|
|
1877
1879
|
runID: runtime.activeRunID,
|
|
1878
1880
|
stuckSeconds
|
|
1879
1881
|
});
|
|
1880
|
-
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);
|
|
1881
1883
|
}
|
|
1882
1884
|
}
|
|
1883
1885
|
}
|
|
@@ -1975,6 +1977,30 @@ function createRealHost(client, directory) {
|
|
|
1975
1977
|
await logServerEvent(directory, "worker.prompted", { sessionID });
|
|
1976
1978
|
return { messageID: result?.data?.messageID };
|
|
1977
1979
|
},
|
|
1980
|
+
async readSession(sessionID) {
|
|
1981
|
+
try {
|
|
1982
|
+
const result = await client.session.get({ path: { id: sessionID } });
|
|
1983
|
+
if (result?.error)
|
|
1984
|
+
return;
|
|
1985
|
+
const data = result?.data;
|
|
1986
|
+
if (!data || typeof data !== "object")
|
|
1987
|
+
return;
|
|
1988
|
+
const agent = typeof data.agent === "string" ? data.agent : undefined;
|
|
1989
|
+
const rawModel = data.model;
|
|
1990
|
+
let model;
|
|
1991
|
+
if (rawModel && typeof rawModel === "object") {
|
|
1992
|
+
const modelID = typeof rawModel.modelID === "string" ? rawModel.modelID : typeof rawModel.id === "string" ? rawModel.id : undefined;
|
|
1993
|
+
const providerID = typeof rawModel.providerID === "string" ? rawModel.providerID : undefined;
|
|
1994
|
+
if (modelID && providerID)
|
|
1995
|
+
model = { providerID, modelID };
|
|
1996
|
+
}
|
|
1997
|
+
if (!agent && !model)
|
|
1998
|
+
return;
|
|
1999
|
+
return { agent, model };
|
|
2000
|
+
} catch {
|
|
2001
|
+
return;
|
|
2002
|
+
}
|
|
2003
|
+
},
|
|
1978
2004
|
async sessionStatus(sessionID) {
|
|
1979
2005
|
try {
|
|
1980
2006
|
const result = await client.session.status({});
|
|
@@ -2044,15 +2070,27 @@ function createRealHost(client, directory) {
|
|
|
2044
2070
|
await client.session.compact({ sessionID });
|
|
2045
2071
|
} catch {}
|
|
2046
2072
|
},
|
|
2047
|
-
async notifyOwner(ownerSessionID, message) {
|
|
2073
|
+
async notifyOwner(ownerSessionID, message, agent) {
|
|
2048
2074
|
if (shouldDedupParentNotify(ownerSessionID, message)) {
|
|
2049
2075
|
await logServerEvent(directory, "parent.notify.deduped", { ownerSessionID, preview: message.slice(0, 160) });
|
|
2050
2076
|
return;
|
|
2051
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
|
+
}
|
|
2052
2087
|
try {
|
|
2088
|
+
const body = { parts: [{ type: "text", text: message }] };
|
|
2089
|
+
if (resolvedAgent)
|
|
2090
|
+
body.agent = resolvedAgent;
|
|
2053
2091
|
const result = await withTimeout(client.session.promptAsync({
|
|
2054
2092
|
path: { id: ownerSessionID },
|
|
2055
|
-
body
|
|
2093
|
+
body
|
|
2056
2094
|
}), 1e4, "OpenCode parent notify");
|
|
2057
2095
|
if (result?.error) {
|
|
2058
2096
|
await logServerEvent(directory, "parent.notify.failed", { ownerSessionID, detail: describeError(result.error) });
|
|
@@ -2335,6 +2373,17 @@ function createGoalService(host) {
|
|
|
2335
2373
|
return withGoalOperation(id, () => startUnlocked(directory, input, id));
|
|
2336
2374
|
}
|
|
2337
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
|
+
}
|
|
2338
2387
|
const goal = createGoal({
|
|
2339
2388
|
id,
|
|
2340
2389
|
name: input.name,
|
|
@@ -2349,6 +2398,10 @@ function createGoalService(host) {
|
|
|
2349
2398
|
});
|
|
2350
2399
|
if (typeof input.costBudget === "number")
|
|
2351
2400
|
goal.costBudget = input.costBudget;
|
|
2401
|
+
if (parentAgent)
|
|
2402
|
+
goal.parentAgent = parentAgent;
|
|
2403
|
+
if (parentModel)
|
|
2404
|
+
goal.parentModel = parentModel;
|
|
2352
2405
|
const artifactDir = goalArtifactDir(directory, id);
|
|
2353
2406
|
goal.config.artifactDir = artifactDir;
|
|
2354
2407
|
if (!goal.config.progressFile)
|
|
@@ -3125,15 +3178,15 @@ function appendVerificationAttempt(recent, attempt) {
|
|
|
3125
3178
|
import { exec as execChild } from "child_process";
|
|
3126
3179
|
import { promisify } from "util";
|
|
3127
3180
|
var execAsync = promisify(execChild);
|
|
3128
|
-
function goalTools(dir, goalService, hostSessionID, defaults = {}) {
|
|
3181
|
+
function goalTools(dir, goalService, hostSessionID, defaults = {}, host) {
|
|
3129
3182
|
return {
|
|
3130
3183
|
loopd_create_goal: tool({
|
|
3131
|
-
description: "Create a new background loop goal (contract: objective + checks + agent/model + workspaceWrite). " + "The engine spawns a dedicated worker session that does the work autonomously \u2014 it never runs in this chat. " + "Call this after clarifying the contract with the user. " + "Worker identity is free-form: agent is any OpenCode agent name (built-in, ~/.config/opencode/agents/*.md, or opencode.jsonc agent.* \u2014 discover with `opencode agent list`), " + 'model is any "providerID/modelID" (discover with `opencode models [provider]`).
|
|
3184
|
+
description: "Create a new background loop goal (contract: objective + checks + agent/model + workspaceWrite). " + "The engine spawns a dedicated worker session that does the work autonomously \u2014 it never runs in this chat. " + "Call this after clarifying the contract with the user. " + "Worker identity is free-form: agent is any OpenCode agent name (built-in, ~/.config/opencode/agents/*.md, or opencode.jsonc agent.* \u2014 discover with `opencode agent list`), " + 'model is any "providerID/modelID" (discover with `opencode models [provider]`). When omitted, both inherit the CALLING session\'s live agent/model (read at creation), then plugin defaultAgent/defaultModel. ' + "Host is the acceptance authority: checks must pass for complete_goal (free retry if rejected <3, blocked after 3). " + "Workspace-writing goals are serialized (only one active writer) and require checks.",
|
|
3132
3185
|
args: {
|
|
3133
3186
|
name: tool.schema.string().describe("Short goal name (used in the dashboard)."),
|
|
3134
3187
|
objective: tool.schema.string().describe("What the goal should accomplish, in detail."),
|
|
3135
|
-
agent: tool.schema.string().optional().describe(`Agent to run the worker as (e.g. "researcher", "smart-agent"). Optional \u2014
|
|
3136
|
-
model: tool.schema.string().optional().describe(`Model to run the worker as, as "providerID/modelID" (e.g. "openai/gpt-5.6-sol", "ollama/qwen3.8:27b"). Optional \u2014
|
|
3188
|
+
agent: tool.schema.string().optional().describe(`Agent to run the worker as (e.g. "researcher", "smart-agent"). Optional \u2014 inherits the calling session's agent if omitted, else plugin defaultAgent.`),
|
|
3189
|
+
model: tool.schema.string().optional().describe(`Model to run the worker as, as "providerID/modelID" (e.g. "openai/gpt-5.6-sol", "ollama/qwen3.8:27b"). Optional \u2014 inherits the calling session's live model if omitted, else plugin defaultModel.`),
|
|
3137
3190
|
costBudget: tool.schema.number().optional().describe("Max provider cost in dollars before the engine stops the goal as budget_limited (e.g. 0.5). Optional \u2014 unlimited if omitted."),
|
|
3138
3191
|
checks: tool.schema.array(tool.schema.string()).optional().describe('Shell commands that must pass for completion to be accepted. E.g. ["npm test"].'),
|
|
3139
3192
|
checkCwd: tool.schema.string().optional().describe("Directory where completion checks run. Workspace-writing goals default to the project root."),
|
|
@@ -3219,7 +3272,7 @@ function goalTools(dir, goalService, hostSessionID, defaults = {}) {
|
|
|
3219
3272
|
directory: dir,
|
|
3220
3273
|
objective: args.objective,
|
|
3221
3274
|
config,
|
|
3222
|
-
defaults
|
|
3275
|
+
defaults: await withParentIdentity(defaults, host, sessionID)
|
|
3223
3276
|
});
|
|
3224
3277
|
if (!resolution.ok) {
|
|
3225
3278
|
return {
|
|
@@ -3232,12 +3285,15 @@ function goalTools(dir, goalService, hostSessionID, defaults = {}) {
|
|
|
3232
3285
|
};
|
|
3233
3286
|
}
|
|
3234
3287
|
try {
|
|
3288
|
+
const parentDefaults = await withParentIdentity(defaults, host, sessionID);
|
|
3235
3289
|
const { goal, worker } = await goalService.start(dir, {
|
|
3236
3290
|
name: args.name,
|
|
3237
3291
|
objective: args.objective,
|
|
3238
3292
|
ownerSessionID: sessionID,
|
|
3239
3293
|
config: resolution.config,
|
|
3240
|
-
costBudget
|
|
3294
|
+
costBudget,
|
|
3295
|
+
parentAgent: parentDefaults.parentAgent,
|
|
3296
|
+
parentModel: parentDefaults.parentModel
|
|
3241
3297
|
});
|
|
3242
3298
|
return {
|
|
3243
3299
|
title: "Goal created",
|
|
@@ -3595,6 +3651,34 @@ ${failureDetails.slice(0, 500)}`,
|
|
|
3595
3651
|
})
|
|
3596
3652
|
};
|
|
3597
3653
|
}
|
|
3654
|
+
var parentIdentityCache = new Map;
|
|
3655
|
+
async function withParentIdentity(defaults, host, ownerSessionID) {
|
|
3656
|
+
if (!host?.readSession)
|
|
3657
|
+
return defaults;
|
|
3658
|
+
let cached = parentIdentityCache.get(ownerSessionID);
|
|
3659
|
+
if (!cached) {
|
|
3660
|
+
try {
|
|
3661
|
+
const identity = await host.readSession(ownerSessionID);
|
|
3662
|
+
cached = {
|
|
3663
|
+
agent: identity?.agent,
|
|
3664
|
+
model: identity?.model ? `${identity.model.providerID}/${identity.model.modelID}` : undefined
|
|
3665
|
+
};
|
|
3666
|
+
} catch {
|
|
3667
|
+
cached = {};
|
|
3668
|
+
}
|
|
3669
|
+
parentIdentityCache.set(ownerSessionID, cached);
|
|
3670
|
+
if (parentIdentityCache.size > 200) {
|
|
3671
|
+
const first = parentIdentityCache.keys().next();
|
|
3672
|
+
if (!first.done)
|
|
3673
|
+
parentIdentityCache.delete(first.value);
|
|
3674
|
+
}
|
|
3675
|
+
}
|
|
3676
|
+
return {
|
|
3677
|
+
...defaults,
|
|
3678
|
+
parentAgent: cached.agent,
|
|
3679
|
+
parentModel: cached.model
|
|
3680
|
+
};
|
|
3681
|
+
}
|
|
3598
3682
|
function findGoalByWorkerSession(state, sessionID) {
|
|
3599
3683
|
if (!sessionID)
|
|
3600
3684
|
return;
|
|
@@ -3809,6 +3893,8 @@ function ownerTools(options) {
|
|
|
3809
3893
|
workspaceWrite: goal.config.workspaceWrite,
|
|
3810
3894
|
agent: goal.config.agent,
|
|
3811
3895
|
model: goal.config.model,
|
|
3896
|
+
parentAgent: goal.parentAgent,
|
|
3897
|
+
parentModel: goal.parentModel,
|
|
3812
3898
|
schedule: goal.config.schedule
|
|
3813
3899
|
},
|
|
3814
3900
|
lastProgress: goal.lastProgress,
|
|
@@ -4296,7 +4382,7 @@ var server = async ({ client, directory }, pluginOptions) => {
|
|
|
4296
4382
|
if (type?.startsWith("session."))
|
|
4297
4383
|
reconcileInBackground();
|
|
4298
4384
|
},
|
|
4299
|
-
tool: { ...goalTools(directory, goalService, undefined, defaults), ...ownerTools({ directory, host, goalService }) },
|
|
4385
|
+
tool: { ...goalTools(directory, goalService, undefined, defaults, host), ...ownerTools({ directory, host, goalService }) },
|
|
4300
4386
|
"tool.execute.before": async (input, _output) => {
|
|
4301
4387
|
const activeWorkers = goalService.getActiveWorkers();
|
|
4302
4388
|
let matchedGoalID;
|
|
@@ -4369,7 +4455,7 @@ var server = async ({ client, directory }, pluginOptions) => {
|
|
|
4369
4455
|
});
|
|
4370
4456
|
}
|
|
4371
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 || ""}.`;
|
|
4372
|
-
await host.notifyOwner(goal.ownerSessionID, message);
|
|
4458
|
+
await host.notifyOwner(goal.ownerSessionID, message, goal.parentAgent);
|
|
4373
4459
|
} catch {}
|
|
4374
4460
|
}
|
|
4375
4461
|
},
|
package/dist/tui.js
CHANGED
|
@@ -1736,7 +1736,7 @@ function LoopDashboard(props) {
|
|
|
1736
1736
|
bold: true
|
|
1737
1737
|
}, _$p));
|
|
1738
1738
|
return _el$145;
|
|
1739
|
-
})(), agentName ? [(() => {
|
|
1739
|
+
})(), _$memo(() => agentName ? [(() => {
|
|
1740
1740
|
var _el$161 = _$createElement("span");
|
|
1741
1741
|
_$insert(_el$161, agentName);
|
|
1742
1742
|
_$effect((_$p) => _$setProp(_el$161, "style", {
|
|
@@ -1753,14 +1753,36 @@ function LoopDashboard(props) {
|
|
|
1753
1753
|
fg: theme().textMuted
|
|
1754
1754
|
}, _$p));
|
|
1755
1755
|
return _el$162;
|
|
1756
|
-
})())] : (() => {
|
|
1756
|
+
})())] : goal().parentAgent ? [(() => {
|
|
1757
1757
|
var _el$165 = _$createElement("span");
|
|
1758
|
-
_$insertNode(_el$165, _$createTextNode(`
|
|
1758
|
+
_$insertNode(_el$165, _$createTextNode(`\u21A9 `));
|
|
1759
1759
|
_$effect((_$p) => _$setProp(_el$165, "style", {
|
|
1760
1760
|
fg: theme().textMuted
|
|
1761
1761
|
}, _$p));
|
|
1762
1762
|
return _el$165;
|
|
1763
1763
|
})(), (() => {
|
|
1764
|
+
var _el$167 = _$createElement("span");
|
|
1765
|
+
_$insert(_el$167, () => goal().parentAgent);
|
|
1766
|
+
_$effect((_$p) => _$setProp(_el$167, "style", {
|
|
1767
|
+
fg: theme().text,
|
|
1768
|
+
bold: true
|
|
1769
|
+
}, _$p));
|
|
1770
|
+
return _el$167;
|
|
1771
|
+
})(), (() => {
|
|
1772
|
+
var _el$168 = _$createElement("span");
|
|
1773
|
+
_$insertNode(_el$168, _$createTextNode(` (parent)`));
|
|
1774
|
+
_$effect((_$p) => _$setProp(_el$168, "style", {
|
|
1775
|
+
fg: theme().textMuted
|
|
1776
|
+
}, _$p));
|
|
1777
|
+
return _el$168;
|
|
1778
|
+
})()] : (() => {
|
|
1779
|
+
var _el$170 = _$createElement("span");
|
|
1780
|
+
_$insertNode(_el$170, _$createTextNode(`parent`));
|
|
1781
|
+
_$effect((_$p) => _$setProp(_el$170, "style", {
|
|
1782
|
+
fg: theme().textMuted
|
|
1783
|
+
}, _$p));
|
|
1784
|
+
return _el$170;
|
|
1785
|
+
})()), (() => {
|
|
1764
1786
|
var _el$147 = _$createElement("span");
|
|
1765
1787
|
_$insertNode(_el$147, _$createTextNode(` \u2502 \uD83E\uDDE0 `));
|
|
1766
1788
|
_$effect((_$p) => _$setProp(_el$147, "style", {
|
|
@@ -1775,30 +1797,52 @@ function LoopDashboard(props) {
|
|
|
1775
1797
|
bold: true
|
|
1776
1798
|
}, _$p));
|
|
1777
1799
|
return _el$149;
|
|
1778
|
-
})(), model && slash > 0 ? [(() => {
|
|
1779
|
-
var _el$
|
|
1780
|
-
_$insertNode(_el$
|
|
1781
|
-
_$insert(_el$
|
|
1782
|
-
_$effect((_$p) => _$setProp(_el$
|
|
1800
|
+
})(), _$memo(() => model && slash > 0 ? [(() => {
|
|
1801
|
+
var _el$172 = _$createElement("span"), _el$173 = _$createTextNode(`/`);
|
|
1802
|
+
_$insertNode(_el$172, _el$173);
|
|
1803
|
+
_$insert(_el$172, () => model.slice(0, slash), _el$173);
|
|
1804
|
+
_$effect((_$p) => _$setProp(_el$172, "style", {
|
|
1783
1805
|
fg: theme().textMuted
|
|
1784
1806
|
}, _$p));
|
|
1785
|
-
return _el$
|
|
1807
|
+
return _el$172;
|
|
1786
1808
|
})(), (() => {
|
|
1787
|
-
var _el$
|
|
1788
|
-
_$insert(_el$
|
|
1789
|
-
_$effect((_$p) => _$setProp(_el$
|
|
1809
|
+
var _el$174 = _$createElement("span");
|
|
1810
|
+
_$insert(_el$174, () => model.slice(slash + 1));
|
|
1811
|
+
_$effect((_$p) => _$setProp(_el$174, "style", {
|
|
1790
1812
|
fg: theme().info,
|
|
1791
1813
|
bold: true
|
|
1792
1814
|
}, _$p));
|
|
1793
|
-
return _el$
|
|
1794
|
-
})()] : (() => {
|
|
1795
|
-
var _el$
|
|
1796
|
-
_$insertNode(_el$
|
|
1797
|
-
_$effect((_$p) => _$setProp(_el$
|
|
1815
|
+
return _el$174;
|
|
1816
|
+
})()] : goal().parentModel ? [(() => {
|
|
1817
|
+
var _el$175 = _$createElement("span");
|
|
1818
|
+
_$insertNode(_el$175, _$createTextNode(`\u21A9 `));
|
|
1819
|
+
_$effect((_$p) => _$setProp(_el$175, "style", {
|
|
1798
1820
|
fg: theme().textMuted
|
|
1799
1821
|
}, _$p));
|
|
1800
|
-
return _el$
|
|
1822
|
+
return _el$175;
|
|
1801
1823
|
})(), (() => {
|
|
1824
|
+
var _el$177 = _$createElement("span");
|
|
1825
|
+
_$insert(_el$177, () => goal().parentModel);
|
|
1826
|
+
_$effect((_$p) => _$setProp(_el$177, "style", {
|
|
1827
|
+
fg: theme().info,
|
|
1828
|
+
bold: true
|
|
1829
|
+
}, _$p));
|
|
1830
|
+
return _el$177;
|
|
1831
|
+
})(), (() => {
|
|
1832
|
+
var _el$178 = _$createElement("span");
|
|
1833
|
+
_$insertNode(_el$178, _$createTextNode(` (parent)`));
|
|
1834
|
+
_$effect((_$p) => _$setProp(_el$178, "style", {
|
|
1835
|
+
fg: theme().textMuted
|
|
1836
|
+
}, _$p));
|
|
1837
|
+
return _el$178;
|
|
1838
|
+
})()] : (() => {
|
|
1839
|
+
var _el$180 = _$createElement("span");
|
|
1840
|
+
_$insertNode(_el$180, _$createTextNode(`parent`));
|
|
1841
|
+
_$effect((_$p) => _$setProp(_el$180, "style", {
|
|
1842
|
+
fg: theme().textMuted
|
|
1843
|
+
}, _$p));
|
|
1844
|
+
return _el$180;
|
|
1845
|
+
})()), (() => {
|
|
1802
1846
|
var _el$151 = _$createElement("span");
|
|
1803
1847
|
_$insertNode(_el$151, _$createTextNode(` \u2502 \uD83D\uDCB0 `));
|
|
1804
1848
|
_$effect((_$p) => _$setProp(_el$151, "style", {
|
|
@@ -1851,240 +1895,240 @@ function LoopDashboard(props) {
|
|
|
1851
1895
|
_$insert(_el$122, (() => {
|
|
1852
1896
|
var _c$12 = _$memo(() => !!lp());
|
|
1853
1897
|
return () => _c$12() && [(() => {
|
|
1854
|
-
var _el$
|
|
1898
|
+
var _el$182 = _$createElement("span"), _el$183 = _$createTextNode(`
|
|
1855
1899
|
\u2714 `);
|
|
1856
|
-
_$insertNode(_el$
|
|
1857
|
-
_$effect((_$p) => _$setProp(_el$
|
|
1900
|
+
_$insertNode(_el$182, _el$183);
|
|
1901
|
+
_$effect((_$p) => _$setProp(_el$182, "style", {
|
|
1858
1902
|
fg: theme().success
|
|
1859
1903
|
}, _$p));
|
|
1860
|
-
return _el$
|
|
1904
|
+
return _el$182;
|
|
1861
1905
|
})(), (() => {
|
|
1862
|
-
var _el$
|
|
1863
|
-
_$insertNode(_el$
|
|
1864
|
-
_$effect((_$p) => _$setProp(_el$
|
|
1906
|
+
var _el$185 = _$createElement("span");
|
|
1907
|
+
_$insertNode(_el$185, _$createTextNode(`Progress: `));
|
|
1908
|
+
_$effect((_$p) => _$setProp(_el$185, "style", {
|
|
1865
1909
|
fg: theme().success,
|
|
1866
1910
|
bold: true
|
|
1867
1911
|
}, _$p));
|
|
1868
|
-
return _el$
|
|
1912
|
+
return _el$185;
|
|
1869
1913
|
})(), (() => {
|
|
1870
|
-
var _el$
|
|
1871
|
-
_$insert(_el$
|
|
1872
|
-
_$effect((_$p) => _$setProp(_el$
|
|
1914
|
+
var _el$187 = _$createElement("span");
|
|
1915
|
+
_$insert(_el$187, () => lp().summary.slice(0, 100));
|
|
1916
|
+
_$effect((_$p) => _$setProp(_el$187, "style", {
|
|
1873
1917
|
fg: theme().text
|
|
1874
1918
|
}, _$p));
|
|
1875
|
-
return _el$
|
|
1919
|
+
return _el$187;
|
|
1876
1920
|
})(), (() => {
|
|
1877
|
-
var _el$
|
|
1878
|
-
_$insertNode(_el$
|
|
1879
|
-
_$insert(_el$
|
|
1880
|
-
_$effect((_$p) => _$setProp(_el$
|
|
1921
|
+
var _el$188 = _$createElement("span"), _el$189 = _$createTextNode(` \u2192 `);
|
|
1922
|
+
_$insertNode(_el$188, _el$189);
|
|
1923
|
+
_$insert(_el$188, () => lp().next?.slice(0, 60) || "", null);
|
|
1924
|
+
_$effect((_$p) => _$setProp(_el$188, "style", {
|
|
1881
1925
|
fg: theme().textMuted
|
|
1882
1926
|
}, _$p));
|
|
1883
|
-
return _el$
|
|
1927
|
+
return _el$188;
|
|
1884
1928
|
})()];
|
|
1885
1929
|
})(), null);
|
|
1886
1930
|
_$insert(_el$122, (() => {
|
|
1887
1931
|
var _c$13 = _$memo(() => !!blk());
|
|
1888
1932
|
return () => _c$13() && [(() => {
|
|
1889
|
-
var _el$
|
|
1933
|
+
var _el$190 = _$createElement("span"), _el$191 = _$createTextNode(`
|
|
1890
1934
|
\u2716 Blocked: `);
|
|
1891
|
-
_$insertNode(_el$
|
|
1892
|
-
_$effect((_$p) => _$setProp(_el$
|
|
1935
|
+
_$insertNode(_el$190, _el$191);
|
|
1936
|
+
_$effect((_$p) => _$setProp(_el$190, "style", {
|
|
1893
1937
|
fg: theme().error,
|
|
1894
1938
|
bold: true
|
|
1895
1939
|
}, _$p));
|
|
1896
|
-
return _el$
|
|
1940
|
+
return _el$190;
|
|
1897
1941
|
})(), (() => {
|
|
1898
|
-
var _el$
|
|
1899
|
-
_$insert(_el$
|
|
1900
|
-
_$effect((_$p) => _$setProp(_el$
|
|
1942
|
+
var _el$193 = _$createElement("span");
|
|
1943
|
+
_$insert(_el$193, () => blk().reason.slice(0, 140));
|
|
1944
|
+
_$effect((_$p) => _$setProp(_el$193, "style", {
|
|
1901
1945
|
fg: theme().error
|
|
1902
1946
|
}, _$p));
|
|
1903
|
-
return _el$
|
|
1947
|
+
return _el$193;
|
|
1904
1948
|
})(), (() => {
|
|
1905
|
-
var _el$
|
|
1906
|
-
_$insertNode(_el$
|
|
1907
|
-
_$insert(_el$
|
|
1908
|
-
_$effect((_$p) => _$setProp(_el$
|
|
1949
|
+
var _el$194 = _$createElement("span"), _el$195 = _$createTextNode(` \u2014 `);
|
|
1950
|
+
_$insertNode(_el$194, _el$195);
|
|
1951
|
+
_$insert(_el$194, () => blk().needed.slice(0, 60), null);
|
|
1952
|
+
_$effect((_$p) => _$setProp(_el$194, "style", {
|
|
1909
1953
|
fg: theme().textMuted
|
|
1910
1954
|
}, _$p));
|
|
1911
|
-
return _el$
|
|
1955
|
+
return _el$194;
|
|
1912
1956
|
})()];
|
|
1913
1957
|
})(), null);
|
|
1914
1958
|
_$insert(_el$122, (() => {
|
|
1915
1959
|
var _c$14 = _$memo(() => !!goal().config.artifactDir);
|
|
1916
1960
|
return () => _c$14() && [(() => {
|
|
1917
|
-
var _el$
|
|
1961
|
+
var _el$196 = _$createElement("span"), _el$197 = _$createTextNode(`
|
|
1918
1962
|
\uD83D\uDCC1 `);
|
|
1919
|
-
_$insertNode(_el$
|
|
1920
|
-
_$effect((_$p) => _$setProp(_el$
|
|
1963
|
+
_$insertNode(_el$196, _el$197);
|
|
1964
|
+
_$effect((_$p) => _$setProp(_el$196, "style", {
|
|
1921
1965
|
fg: theme().accent
|
|
1922
1966
|
}, _$p));
|
|
1923
|
-
return _el$
|
|
1967
|
+
return _el$196;
|
|
1924
1968
|
})(), (() => {
|
|
1925
|
-
var _el$
|
|
1926
|
-
_$insertNode(_el$
|
|
1927
|
-
_$effect((_$p) => _$setProp(_el$
|
|
1969
|
+
var _el$199 = _$createElement("span");
|
|
1970
|
+
_$insertNode(_el$199, _$createTextNode(`Artifacts: `));
|
|
1971
|
+
_$effect((_$p) => _$setProp(_el$199, "style", {
|
|
1928
1972
|
fg: theme().accent,
|
|
1929
1973
|
bold: true
|
|
1930
1974
|
}, _$p));
|
|
1931
|
-
return _el$
|
|
1975
|
+
return _el$199;
|
|
1932
1976
|
})(), (() => {
|
|
1933
|
-
var _el$
|
|
1934
|
-
_$insert(_el$
|
|
1935
|
-
_$effect((_$p) => _$setProp(_el$
|
|
1977
|
+
var _el$201 = _$createElement("span");
|
|
1978
|
+
_$insert(_el$201, () => String(goal().config.artifactDir).replace(String(props.directory), "."));
|
|
1979
|
+
_$effect((_$p) => _$setProp(_el$201, "style", {
|
|
1936
1980
|
fg: theme().textMuted
|
|
1937
1981
|
}, _$p));
|
|
1938
|
-
return _el$
|
|
1982
|
+
return _el$201;
|
|
1939
1983
|
})()];
|
|
1940
1984
|
})(), null);
|
|
1941
1985
|
_$insert(_el$122, (() => {
|
|
1942
1986
|
var _c$15 = _$memo(() => (goal().config.checks?.length ?? 0) > 0);
|
|
1943
1987
|
return () => _c$15() ? [(() => {
|
|
1944
|
-
var _el$
|
|
1988
|
+
var _el$202 = _$createElement("span"), _el$203 = _$createTextNode(`
|
|
1945
1989
|
\u25A3 `);
|
|
1946
|
-
_$insertNode(_el$
|
|
1947
|
-
_$effect((_$p) => _$setProp(_el$
|
|
1990
|
+
_$insertNode(_el$202, _el$203);
|
|
1991
|
+
_$effect((_$p) => _$setProp(_el$202, "style", {
|
|
1948
1992
|
fg: theme().warning
|
|
1949
1993
|
}, _$p));
|
|
1950
|
-
return _el$
|
|
1994
|
+
return _el$202;
|
|
1951
1995
|
})(), (() => {
|
|
1952
|
-
var _el$
|
|
1953
|
-
_$insertNode(_el$
|
|
1954
|
-
_$effect((_$p) => _$setProp(_el$
|
|
1996
|
+
var _el$205 = _$createElement("span");
|
|
1997
|
+
_$insertNode(_el$205, _$createTextNode(`Checks: `));
|
|
1998
|
+
_$effect((_$p) => _$setProp(_el$205, "style", {
|
|
1955
1999
|
fg: theme().warning,
|
|
1956
2000
|
bold: true
|
|
1957
2001
|
}, _$p));
|
|
1958
|
-
return _el$
|
|
2002
|
+
return _el$205;
|
|
1959
2003
|
})(), (() => {
|
|
1960
|
-
var _el$
|
|
1961
|
-
_$insert(_el$
|
|
1962
|
-
_$effect((_$p) => _$setProp(_el$
|
|
2004
|
+
var _el$207 = _$createElement("span");
|
|
2005
|
+
_$insert(_el$207, () => goal().config.checks.join(", ").slice(0, 100));
|
|
2006
|
+
_$effect((_$p) => _$setProp(_el$207, "style", {
|
|
1963
2007
|
fg: theme().textMuted
|
|
1964
2008
|
}, _$p));
|
|
1965
|
-
return _el$
|
|
2009
|
+
return _el$207;
|
|
1966
2010
|
})()] : null;
|
|
1967
2011
|
})(), null);
|
|
1968
2012
|
_$insert(_el$122, (() => {
|
|
1969
2013
|
var _c$16 = _$memo(() => rt()?.evaluatorRejectionCount > 0);
|
|
1970
2014
|
return () => _c$16() && [(() => {
|
|
1971
|
-
var _el$
|
|
2015
|
+
var _el$208 = _$createElement("span"), _el$209 = _$createTextNode(`
|
|
1972
2016
|
\u26A0 rejections: `);
|
|
1973
|
-
_$insertNode(_el$
|
|
1974
|
-
_$effect((_$p) => _$setProp(_el$
|
|
2017
|
+
_$insertNode(_el$208, _el$209);
|
|
2018
|
+
_$effect((_$p) => _$setProp(_el$208, "style", {
|
|
1975
2019
|
fg: theme().warning
|
|
1976
2020
|
}, _$p));
|
|
1977
|
-
return _el$
|
|
2021
|
+
return _el$208;
|
|
1978
2022
|
})(), (() => {
|
|
1979
|
-
var _el$
|
|
1980
|
-
_$insertNode(_el$
|
|
1981
|
-
_$insert(_el$
|
|
1982
|
-
_$insert(_el$
|
|
1983
|
-
_$effect((_$p) => _$setProp(_el$
|
|
2023
|
+
var _el$211 = _$createElement("span"), _el$212 = _$createTextNode(` \u2014 `);
|
|
2024
|
+
_$insertNode(_el$211, _el$212);
|
|
2025
|
+
_$insert(_el$211, () => String(rt().evaluatorRejectionCount), _el$212);
|
|
2026
|
+
_$insert(_el$211, () => String(rt().lastRejectionDetails || "").slice(0, 80), null);
|
|
2027
|
+
_$effect((_$p) => _$setProp(_el$211, "style", {
|
|
1984
2028
|
fg: theme().warning
|
|
1985
2029
|
}, _$p));
|
|
1986
|
-
return _el$
|
|
2030
|
+
return _el$211;
|
|
1987
2031
|
})()];
|
|
1988
2032
|
})(), null);
|
|
1989
2033
|
_$insert(_el$122, (() => {
|
|
1990
2034
|
var _c$17 = _$memo(() => rt()?.unknownStatusCount > 0);
|
|
1991
2035
|
return () => _c$17() && [(() => {
|
|
1992
|
-
var _el$
|
|
2036
|
+
var _el$213 = _$createElement("span"), _el$214 = _$createTextNode(`
|
|
1993
2037
|
\u26A0\uFE0F unreachable: `);
|
|
1994
|
-
_$insertNode(_el$
|
|
1995
|
-
_$effect((_$p) => _$setProp(_el$
|
|
2038
|
+
_$insertNode(_el$213, _el$214);
|
|
2039
|
+
_$effect((_$p) => _$setProp(_el$213, "style", {
|
|
1996
2040
|
fg: theme().error
|
|
1997
2041
|
}, _$p));
|
|
1998
|
-
return _el$
|
|
2042
|
+
return _el$213;
|
|
1999
2043
|
})(), (() => {
|
|
2000
|
-
var _el$
|
|
2001
|
-
_$insertNode(_el$
|
|
2002
|
-
_$insert(_el$
|
|
2003
|
-
_$effect((_$p) => _$setProp(_el$
|
|
2044
|
+
var _el$216 = _$createElement("span"), _el$217 = _$createTextNode(`/3`);
|
|
2045
|
+
_$insertNode(_el$216, _el$217);
|
|
2046
|
+
_$insert(_el$216, () => String(rt().unknownStatusCount), _el$217);
|
|
2047
|
+
_$effect((_$p) => _$setProp(_el$216, "style", {
|
|
2004
2048
|
fg: theme().error
|
|
2005
2049
|
}, _$p));
|
|
2006
|
-
return _el$
|
|
2050
|
+
return _el$216;
|
|
2007
2051
|
})(), (() => {
|
|
2008
|
-
var _el$
|
|
2009
|
-
_$insertNode(_el$
|
|
2010
|
-
_$effect((_$p) => _$setProp(_el$
|
|
2052
|
+
var _el$218 = _$createElement("span");
|
|
2053
|
+
_$insertNode(_el$218, _$createTextNode(` \u2014 nudge to recover`));
|
|
2054
|
+
_$effect((_$p) => _$setProp(_el$218, "style", {
|
|
2011
2055
|
fg: theme().textMuted
|
|
2012
2056
|
}, _$p));
|
|
2013
|
-
return _el$
|
|
2057
|
+
return _el$218;
|
|
2014
2058
|
})()];
|
|
2015
2059
|
})(), null);
|
|
2016
2060
|
_$insert(_el$122, (() => {
|
|
2017
2061
|
var _c$18 = _$memo(() => !!rt()?.retryAfter);
|
|
2018
2062
|
return () => _c$18() && [(() => {
|
|
2019
|
-
var _el$
|
|
2063
|
+
var _el$220 = _$createElement("span"), _el$221 = _$createTextNode(`
|
|
2020
2064
|
\u21BB retry in: `);
|
|
2021
|
-
_$insertNode(_el$
|
|
2022
|
-
_$effect((_$p) => _$setProp(_el$
|
|
2065
|
+
_$insertNode(_el$220, _el$221);
|
|
2066
|
+
_$effect((_$p) => _$setProp(_el$220, "style", {
|
|
2023
2067
|
fg: theme().accent
|
|
2024
2068
|
}, _$p));
|
|
2025
|
-
return _el$
|
|
2069
|
+
return _el$220;
|
|
2026
2070
|
})(), (() => {
|
|
2027
|
-
var _el$
|
|
2028
|
-
_$insert(_el$
|
|
2029
|
-
_$effect((_$p) => _$setProp(_el$
|
|
2071
|
+
var _el$223 = _$createElement("span");
|
|
2072
|
+
_$insert(_el$223, () => countdownLabel(rt().retryAfter, clock()));
|
|
2073
|
+
_$effect((_$p) => _$setProp(_el$223, "style", {
|
|
2030
2074
|
fg: theme().accent
|
|
2031
2075
|
}, _$p));
|
|
2032
|
-
return _el$
|
|
2076
|
+
return _el$223;
|
|
2033
2077
|
})()];
|
|
2034
2078
|
})(), null);
|
|
2035
2079
|
_$insert(_el$122, (() => {
|
|
2036
2080
|
var _c$19 = _$memo(() => !!rt()?.nextRunAt);
|
|
2037
2081
|
return () => _c$19() && [(() => {
|
|
2038
|
-
var _el$
|
|
2082
|
+
var _el$224 = _$createElement("span"), _el$225 = _$createTextNode(`
|
|
2039
2083
|
\u23F0 next run: `);
|
|
2040
|
-
_$insertNode(_el$
|
|
2041
|
-
_$effect((_$p) => _$setProp(_el$
|
|
2084
|
+
_$insertNode(_el$224, _el$225);
|
|
2085
|
+
_$effect((_$p) => _$setProp(_el$224, "style", {
|
|
2042
2086
|
fg: theme().accent
|
|
2043
2087
|
}, _$p));
|
|
2044
|
-
return _el$
|
|
2088
|
+
return _el$224;
|
|
2045
2089
|
})(), (() => {
|
|
2046
|
-
var _el$
|
|
2047
|
-
_$insert(_el$
|
|
2048
|
-
_$effect((_$p) => _$setProp(_el$
|
|
2090
|
+
var _el$227 = _$createElement("span");
|
|
2091
|
+
_$insert(_el$227, () => countdownLabel(rt().nextRunAt, clock()));
|
|
2092
|
+
_$effect((_$p) => _$setProp(_el$227, "style", {
|
|
2049
2093
|
fg: theme().accent
|
|
2050
2094
|
}, _$p));
|
|
2051
|
-
return _el$
|
|
2095
|
+
return _el$227;
|
|
2052
2096
|
})(), (() => {
|
|
2053
|
-
var _el$
|
|
2054
|
-
_$insertNode(_el$
|
|
2055
|
-
_$insertNode(_el$
|
|
2056
|
-
_$insert(_el$
|
|
2057
|
-
_$effect((_$p) => _$setProp(_el$
|
|
2097
|
+
var _el$228 = _$createElement("span"), _el$229 = _$createTextNode(` (`), _el$230 = _$createTextNode(` runs)`);
|
|
2098
|
+
_$insertNode(_el$228, _el$229);
|
|
2099
|
+
_$insertNode(_el$228, _el$230);
|
|
2100
|
+
_$insert(_el$228, () => String(rt().scheduleRunCount || 0), _el$230);
|
|
2101
|
+
_$effect((_$p) => _$setProp(_el$228, "style", {
|
|
2058
2102
|
fg: theme().textMuted
|
|
2059
2103
|
}, _$p));
|
|
2060
|
-
return _el$
|
|
2104
|
+
return _el$228;
|
|
2061
2105
|
})()];
|
|
2062
2106
|
})(), null);
|
|
2063
2107
|
_$insert(_el$122, (() => {
|
|
2064
2108
|
var _c$20 = _$memo(() => !!rt()?.lastError);
|
|
2065
2109
|
return () => _c$20() && [(() => {
|
|
2066
|
-
var _el$
|
|
2110
|
+
var _el$231 = _$createElement("span"), _el$232 = _$createTextNode(`
|
|
2067
2111
|
\u26A0 `);
|
|
2068
|
-
_$insertNode(_el$
|
|
2069
|
-
_$effect((_$p) => _$setProp(_el$
|
|
2112
|
+
_$insertNode(_el$231, _el$232);
|
|
2113
|
+
_$effect((_$p) => _$setProp(_el$231, "style", {
|
|
2070
2114
|
fg: theme().error
|
|
2071
2115
|
}, _$p));
|
|
2072
|
-
return _el$
|
|
2116
|
+
return _el$231;
|
|
2073
2117
|
})(), (() => {
|
|
2074
|
-
var _el$
|
|
2075
|
-
_$insertNode(_el$
|
|
2076
|
-
_$effect((_$p) => _$setProp(_el$
|
|
2118
|
+
var _el$234 = _$createElement("span");
|
|
2119
|
+
_$insertNode(_el$234, _$createTextNode(`Error: `));
|
|
2120
|
+
_$effect((_$p) => _$setProp(_el$234, "style", {
|
|
2077
2121
|
fg: theme().error,
|
|
2078
2122
|
bold: true
|
|
2079
2123
|
}, _$p));
|
|
2080
|
-
return _el$
|
|
2124
|
+
return _el$234;
|
|
2081
2125
|
})(), (() => {
|
|
2082
|
-
var _el$
|
|
2083
|
-
_$insert(_el$
|
|
2084
|
-
_$effect((_$p) => _$setProp(_el$
|
|
2126
|
+
var _el$236 = _$createElement("span");
|
|
2127
|
+
_$insert(_el$236, () => rt().lastError.slice(0, 120));
|
|
2128
|
+
_$effect((_$p) => _$setProp(_el$236, "style", {
|
|
2085
2129
|
fg: theme().error
|
|
2086
2130
|
}, _$p));
|
|
2087
|
-
return _el$
|
|
2131
|
+
return _el$236;
|
|
2088
2132
|
})()];
|
|
2089
2133
|
})(), null);
|
|
2090
2134
|
_$effect((_p$) => {
|
|
@@ -2139,29 +2183,29 @@ function LoopDashboard(props) {
|
|
|
2139
2183
|
},
|
|
2140
2184
|
children: (ev) => [`
|
|
2141
2185
|
`, (() => {
|
|
2142
|
-
var _el$
|
|
2143
|
-
_$insert(_el$
|
|
2144
|
-
_$effect((_$p) => _$setProp(_el$
|
|
2186
|
+
var _el$237 = _$createElement("span");
|
|
2187
|
+
_$insert(_el$237, () => String(ev.type));
|
|
2188
|
+
_$effect((_$p) => _$setProp(_el$237, "style", {
|
|
2145
2189
|
fg: eventColor(String(ev.type), theme()),
|
|
2146
2190
|
bold: true
|
|
2147
2191
|
}, _$p));
|
|
2148
|
-
return _el$
|
|
2192
|
+
return _el$237;
|
|
2149
2193
|
})(), (() => {
|
|
2150
|
-
var _el$
|
|
2151
|
-
_$insertNode(_el$
|
|
2152
|
-
_$insert(_el$
|
|
2153
|
-
_$effect((_$p) => _$setProp(_el$
|
|
2194
|
+
var _el$238 = _$createElement("span"), _el$239 = _$createTextNode(` `);
|
|
2195
|
+
_$insertNode(_el$238, _el$239);
|
|
2196
|
+
_$insert(_el$238, () => ev.goalID?.slice(0, 8), null);
|
|
2197
|
+
_$effect((_$p) => _$setProp(_el$238, "style", {
|
|
2154
2198
|
fg: theme().textMuted
|
|
2155
2199
|
}, _$p));
|
|
2156
|
-
return _el$
|
|
2200
|
+
return _el$238;
|
|
2157
2201
|
})(), _$memo(() => _$memo(() => !!ev.summary)() && (() => {
|
|
2158
|
-
var _el$
|
|
2159
|
-
_$insertNode(_el$
|
|
2160
|
-
_$insert(_el$
|
|
2161
|
-
_$effect((_$p) => _$setProp(_el$
|
|
2202
|
+
var _el$240 = _$createElement("span"), _el$241 = _$createTextNode(` \u2014 `);
|
|
2203
|
+
_$insertNode(_el$240, _el$241);
|
|
2204
|
+
_$insert(_el$240, () => String(ev.summary).slice(0, 60), null);
|
|
2205
|
+
_$effect((_$p) => _$setProp(_el$240, "style", {
|
|
2162
2206
|
fg: theme().text
|
|
2163
2207
|
}, _$p));
|
|
2164
|
-
return _el$
|
|
2208
|
+
return _el$240;
|
|
2165
2209
|
})())]
|
|
2166
2210
|
}), null);
|
|
2167
2211
|
_$effect((_p$) => {
|
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",
|
package/skills/loopd/SKILL.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: loopd
|
|
3
3
|
description: "Agent skill for loopd background goals. Teaches when to create goals, how to inspect/steer/pause them, and how to send instructions to workers."
|
|
4
4
|
metadata:
|
|
5
|
-
version: "1.
|
|
5
|
+
version: "1.2.0"
|
|
6
6
|
status: active
|
|
7
7
|
tags: [opencode, loop, goal, background, automation]
|
|
8
8
|
---
|
|
@@ -46,8 +46,8 @@ Every goal has an **immutable contract** at creation — the source of truth for
|
|
|
46
46
|
|
|
47
47
|
* **Objective** — semantic requirements (free text, self-contained). The worker derives concrete requirements from it.
|
|
48
48
|
* **Checks** — deterministic shell commands that **must pass** for `complete_goal` to be accepted. For `workspaceWrite:true` goals they are **mandatory** (explicit `checks` or plugin `defaultChecks`), and they run from `checkCwd` (writers default to project root; artifact-only jobs run from their `artifactDir`).
|
|
49
|
-
* **Agent** — any OpenCode agent name: built-in (`build`, `plan`, `explore`, `general`), file `~/.config/opencode/agents/*.md`, or `opencode.jsonc` `agent.*`. Discover with `opencode agent list`. Prefer `subagent`-mode agents for workers; `primary`-mode agents work but may expect user interaction.
|
|
50
|
-
* **Model** — any model as `"providerID/modelID"` (e.g. `openai/gpt-5.6-sol`, `ollama/qwen3.8:27b`, `openrouter/google/gemma-4-31b-it:free`). Discover with `opencode models [provider]`. Sent on every worker prompt
|
|
49
|
+
* **Agent** — any OpenCode agent name: built-in (`build`, `plan`, `explore`, `general`), file `~/.config/opencode/agents/*.md`, or `opencode.jsonc` `agent.*`. Discover with `opencode agent list`. Prefer `subagent`-mode agents for workers; `primary`-mode agents work but may expect user interaction. Resolution: explicit arg → **calling session's live agent** (read at creation) → `defaultAgent` → OpenCode global default. So a parent in Plan spawns a Plan worker unless told otherwise.
|
|
50
|
+
* **Model** — any model as `"providerID/modelID"` (e.g. `openai/gpt-5.6-sol`, `ollama/qwen3.8:27b`, `openrouter/google/gemma-4-31b-it:free`). Discover with `opencode models [provider]`. Sent on every worker prompt. Resolution: explicit arg → **calling session's live model** → `defaultModel` → agent config → session history → global default. Caveat: OpenCode exposes no live-model field for history-derived models, so those fall through to agent config.
|
|
51
51
|
* **WorkspaceWrite** — `true` (default) = may touch the shared repo; **only one active writer at a time** is allowed (enforced on `start`/`resume`/`retry` with rollback). Set `false` explicitly for artifact-only/read-only work to allow concurrency.
|
|
52
52
|
* **Budgets** — `tokenBudget` (tokens) and `costBudget` (dollars, e.g. `0.5`). When either is exceeded the engine aborts the worker mid-turn and sets `budget_limited`. The worker is stopped immediately, not just at the next turn boundary.
|
|
53
53
|
* **Limits** — `maxTurns` (default 50), `maxNoProgress`, `maxFailures`, `maxEvaluatorRejections` (default 3), `timeoutMs`, `compactEvery`, `progressFile`.
|
|
@@ -73,8 +73,8 @@ Use `loopd_create_goal` **after** clarifying the objective with the user (what /
|
|
|
73
73
|
loopd_create_goal({
|
|
74
74
|
name: "short-name",
|
|
75
75
|
objective: "Detailed description of what the goal should accomplish.",
|
|
76
|
-
agent: "researcher", // any agent name; optional,
|
|
77
|
-
model: "openai/gpt-5.6-sol", // any provider/model; optional,
|
|
76
|
+
agent: "researcher", // any agent name; optional, inherits calling session then defaultAgent
|
|
77
|
+
model: "openai/gpt-5.6-sol", // any provider/model; optional, inherits calling session then defaultModel
|
|
78
78
|
costBudget: 0.5, // optional dollars; aborts worker when exceeded
|
|
79
79
|
checks: ["npm test"], // mandatory if workspaceWrite:true (or configure defaultChecks)
|
|
80
80
|
checkCwd: "/project/root", // optional; writers default to project root, readers to artifactDir
|