@bojackduy/opencode-loopd 1.4.0 → 1.5.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/dist/server.js +141 -52
- package/dist/tui.js +39 -41
- package/package.json +1 -1
package/dist/server.js
CHANGED
|
@@ -332,6 +332,76 @@ function delay(ms) {
|
|
|
332
332
|
var CURRENT_VERSION = 2, LOCK_STALE_MS = 1e4;
|
|
333
333
|
var init_state_repository = () => {};
|
|
334
334
|
|
|
335
|
+
// src/domain/runtime.ts
|
|
336
|
+
var exports_runtime = {};
|
|
337
|
+
__export(exports_runtime, {
|
|
338
|
+
acquireLease: () => acquireLease,
|
|
339
|
+
createRuntimeState: () => createRuntimeState,
|
|
340
|
+
leaseIsValid: () => leaseIsValid,
|
|
341
|
+
markParentNotified: () => markParentNotified,
|
|
342
|
+
markProgress: () => markProgress,
|
|
343
|
+
releaseLease: () => releaseLease,
|
|
344
|
+
shouldNotifyParent: () => shouldNotifyParent
|
|
345
|
+
});
|
|
346
|
+
function createRuntimeState(goalID) {
|
|
347
|
+
const now = new Date().toISOString();
|
|
348
|
+
return {
|
|
349
|
+
goalID,
|
|
350
|
+
phase: "idle",
|
|
351
|
+
consecutiveFailures: 0,
|
|
352
|
+
runCount: 0,
|
|
353
|
+
turnCount: 0,
|
|
354
|
+
noProgressCount: 0,
|
|
355
|
+
progressDuringTurn: false,
|
|
356
|
+
createdAt: now,
|
|
357
|
+
updatedAt: now
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
function acquireLease(rt, timeoutMs) {
|
|
361
|
+
const now = Date.now();
|
|
362
|
+
const expires = new Date(now + timeoutMs).toISOString();
|
|
363
|
+
return {
|
|
364
|
+
...rt,
|
|
365
|
+
phase: "running",
|
|
366
|
+
leaseExpiresAt: expires,
|
|
367
|
+
turnStartedAt: new Date(now).toISOString(),
|
|
368
|
+
progressDuringTurn: false,
|
|
369
|
+
turnTokensUsed: 0,
|
|
370
|
+
updatedAt: new Date(now).toISOString()
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
function releaseLease(rt) {
|
|
374
|
+
return {
|
|
375
|
+
...rt,
|
|
376
|
+
phase: "idle",
|
|
377
|
+
leaseExpiresAt: undefined,
|
|
378
|
+
turnStartedAt: undefined,
|
|
379
|
+
updatedAt: new Date().toISOString()
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
function leaseIsValid(rt) {
|
|
383
|
+
if (!rt.leaseExpiresAt)
|
|
384
|
+
return false;
|
|
385
|
+
return Date.now() < Date.parse(rt.leaseExpiresAt);
|
|
386
|
+
}
|
|
387
|
+
function markProgress(rt) {
|
|
388
|
+
return { ...rt, progressDuringTurn: true, lastProgressAt: new Date().toISOString() };
|
|
389
|
+
}
|
|
390
|
+
function shouldNotifyParent(runtime, type) {
|
|
391
|
+
if (!runtime.lastParentNotifiedAt || !runtime.lastParentNotifiedFor)
|
|
392
|
+
return true;
|
|
393
|
+
if (runtime.lastParentNotifiedFor !== type)
|
|
394
|
+
return true;
|
|
395
|
+
const elapsed = Date.now() - Date.parse(runtime.lastParentNotifiedAt);
|
|
396
|
+
return !Number.isFinite(elapsed) || elapsed > PARENT_NOTIFY_DEDUPE_MS;
|
|
397
|
+
}
|
|
398
|
+
function markParentNotified(runtime, type) {
|
|
399
|
+
runtime.lastParentNotifiedFor = type;
|
|
400
|
+
runtime.lastParentNotifiedAt = new Date().toISOString();
|
|
401
|
+
runtime.updatedAt = new Date().toISOString();
|
|
402
|
+
}
|
|
403
|
+
var PARENT_NOTIFY_DEDUPE_MS = 60000;
|
|
404
|
+
|
|
335
405
|
// src/application/control-worker.ts
|
|
336
406
|
init_state_repository();
|
|
337
407
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
@@ -707,52 +777,6 @@ function createGoal(input) {
|
|
|
707
777
|
return { ...input, tokensUsed: 0, timeUsedSeconds: 0, createdAt: now, updatedAt: now };
|
|
708
778
|
}
|
|
709
779
|
|
|
710
|
-
// src/domain/runtime.ts
|
|
711
|
-
function createRuntimeState(goalID) {
|
|
712
|
-
const now = new Date().toISOString();
|
|
713
|
-
return {
|
|
714
|
-
goalID,
|
|
715
|
-
phase: "idle",
|
|
716
|
-
consecutiveFailures: 0,
|
|
717
|
-
runCount: 0,
|
|
718
|
-
turnCount: 0,
|
|
719
|
-
noProgressCount: 0,
|
|
720
|
-
progressDuringTurn: false,
|
|
721
|
-
createdAt: now,
|
|
722
|
-
updatedAt: now
|
|
723
|
-
};
|
|
724
|
-
}
|
|
725
|
-
function acquireLease(rt, timeoutMs) {
|
|
726
|
-
const now = Date.now();
|
|
727
|
-
const expires = new Date(now + timeoutMs).toISOString();
|
|
728
|
-
return {
|
|
729
|
-
...rt,
|
|
730
|
-
phase: "running",
|
|
731
|
-
leaseExpiresAt: expires,
|
|
732
|
-
turnStartedAt: new Date(now).toISOString(),
|
|
733
|
-
progressDuringTurn: false,
|
|
734
|
-
turnTokensUsed: 0,
|
|
735
|
-
updatedAt: new Date(now).toISOString()
|
|
736
|
-
};
|
|
737
|
-
}
|
|
738
|
-
function releaseLease(rt) {
|
|
739
|
-
return {
|
|
740
|
-
...rt,
|
|
741
|
-
phase: "idle",
|
|
742
|
-
leaseExpiresAt: undefined,
|
|
743
|
-
turnStartedAt: undefined,
|
|
744
|
-
updatedAt: new Date().toISOString()
|
|
745
|
-
};
|
|
746
|
-
}
|
|
747
|
-
function leaseIsValid(rt) {
|
|
748
|
-
if (!rt.leaseExpiresAt)
|
|
749
|
-
return false;
|
|
750
|
-
return Date.now() < Date.parse(rt.leaseExpiresAt);
|
|
751
|
-
}
|
|
752
|
-
function markProgress(rt) {
|
|
753
|
-
return { ...rt, progressDuringTurn: true, lastProgressAt: new Date().toISOString() };
|
|
754
|
-
}
|
|
755
|
-
|
|
756
780
|
// src/application/loop-engine.ts
|
|
757
781
|
var HANDLED_EVENT_TYPES = new Set([
|
|
758
782
|
"session.idle",
|
|
@@ -768,6 +792,7 @@ function createLoopEngine(options) {
|
|
|
768
792
|
let knownWorkerSessions = new Set;
|
|
769
793
|
let knownWorkerSessionsLoaded = false;
|
|
770
794
|
const inflightContinuations = new Set;
|
|
795
|
+
const recentForceFinishBlocked = new Map;
|
|
771
796
|
async function loadWorkerSessionsIfneeded() {
|
|
772
797
|
if (knownWorkerSessionsLoaded)
|
|
773
798
|
return;
|
|
@@ -890,6 +915,12 @@ function createLoopEngine(options) {
|
|
|
890
915
|
await goalService.continueTurn(directory, goal.id, { forceFinish: true });
|
|
891
916
|
return true;
|
|
892
917
|
}
|
|
918
|
+
const blockedKey = goal.id;
|
|
919
|
+
const nowBlocked = Date.now();
|
|
920
|
+
const lastBlocked = recentForceFinishBlocked.get(blockedKey);
|
|
921
|
+
if (lastBlocked !== undefined && nowBlocked - lastBlocked < 60000)
|
|
922
|
+
return true;
|
|
923
|
+
recentForceFinishBlocked.set(blockedKey, nowBlocked);
|
|
893
924
|
goal.status = "blocked";
|
|
894
925
|
goal.updatedAt = new Date().toISOString();
|
|
895
926
|
goal.blocker = {
|
|
@@ -898,6 +929,9 @@ function createLoopEngine(options) {
|
|
|
898
929
|
at: new Date().toISOString()
|
|
899
930
|
};
|
|
900
931
|
runtime.forceFinishRequested = undefined;
|
|
932
|
+
const shouldNotify = shouldNotifyParent(runtime, "stopped");
|
|
933
|
+
if (shouldNotify)
|
|
934
|
+
markParentNotified(runtime, "stopped");
|
|
901
935
|
await writeState(directory, state);
|
|
902
936
|
await appendEvent(directory, {
|
|
903
937
|
version: 1,
|
|
@@ -909,7 +943,9 @@ function createLoopEngine(options) {
|
|
|
909
943
|
timestamp: new Date().toISOString(),
|
|
910
944
|
revision: state.revision
|
|
911
945
|
});
|
|
912
|
-
|
|
946
|
+
if (shouldNotify) {
|
|
947
|
+
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"}.`);
|
|
948
|
+
}
|
|
913
949
|
return true;
|
|
914
950
|
}
|
|
915
951
|
if (limitResult.stop === "budget") {
|
|
@@ -990,7 +1026,10 @@ function createLoopEngine(options) {
|
|
|
990
1026
|
timestamp: new Date().toISOString(),
|
|
991
1027
|
revision: state.revision
|
|
992
1028
|
});
|
|
993
|
-
|
|
1029
|
+
if (shouldNotifyParent(runtime, "failed")) {
|
|
1030
|
+
markParentNotified(runtime, "failed");
|
|
1031
|
+
await host.notifyOwner(goal.ownerSessionID, `Loop goal "${goal.name}" blocked after ${runtime.consecutiveFailures} failures. Last error: ${message}.`);
|
|
1032
|
+
}
|
|
994
1033
|
} else {
|
|
995
1034
|
const backoffMs = Math.min(30000, 1000 * Math.pow(2, runtime.consecutiveFailures));
|
|
996
1035
|
runtime.retryAfter = new Date(Date.now() + backoffMs).toISOString();
|
|
@@ -1139,7 +1178,8 @@ function createWorkerManager(host) {
|
|
|
1139
1178
|
const prompt = buildContinuationSteering(goal, runtime, context);
|
|
1140
1179
|
await host.promptWorker({
|
|
1141
1180
|
sessionID: worker.workerSessionID,
|
|
1142
|
-
prompt
|
|
1181
|
+
prompt,
|
|
1182
|
+
agent: goal.config.agent
|
|
1143
1183
|
});
|
|
1144
1184
|
},
|
|
1145
1185
|
async isIdle(workerSessionID) {
|
|
@@ -1212,6 +1252,9 @@ function buildContinuationSteering(goal, runtime, context) {
|
|
|
1212
1252
|
}
|
|
1213
1253
|
if (v.artifactSummary)
|
|
1214
1254
|
parts.push(`- artifacts: ${v.artifactSummary}`);
|
|
1255
|
+
if (v.evaluatorRejectionCount && v.evaluatorRejectionCount > 0) {
|
|
1256
|
+
parts.push(`- evaluator rejected ${v.evaluatorRejectionCount} time(s): previous completion claim had weak evidence \u2014 fix the issues and call complete_goal again with stronger evidence`);
|
|
1257
|
+
}
|
|
1215
1258
|
}
|
|
1216
1259
|
parts.push(``, `## COMPLETION AUDIT \u2014 you ARE the evaluator`, `Before deciding the goal is achieved, treat completion as unproven:`, `1. Derive concrete requirements from the objective and any referenced files/plans/specs/issues. Preserve original scope; do not redefine success.`, `2. For _every_ explicit requirement, numbered item, named artifact, command, test, gate, invariant, deliverable \u2192 identify authoritative evidence: files, command output, test results, PR state, rendered artifacts, runtime behavior.`, `3. Judge each per-requirement: proves | contradicts | incomplete | too weak/indirect | missing \u2014 matching scope narrowly (narrow check \u2260 broad claim).`, `4. Treat tests/manifests/verifiers as evidence only after confirming they cover the relevant requirement. Treat uncertain/indirect as NOT achieved.`, `5. Only call complete_goal when _every_ requirement's current-state evidence proves it and no required work remains. If any requirement is missing/incomplete/weak \u2192 keep working, do not call complete_goal.`);
|
|
1217
1260
|
if (context?.forceFinish) {
|
|
@@ -1397,6 +1440,9 @@ function createGoalService(host) {
|
|
|
1397
1440
|
const c = `checks configured: ${goal.config.checks.length} \u2014 run them before claiming completion`;
|
|
1398
1441
|
verification = { ...verification || {}, failedChecks: [c], checksPassed: undefined };
|
|
1399
1442
|
}
|
|
1443
|
+
if (runtime.evaluatorRejectionCount && runtime.evaluatorRejectionCount > 0) {
|
|
1444
|
+
verification = { ...verification || {}, evaluatorRejectionCount: runtime.evaluatorRejectionCount };
|
|
1445
|
+
}
|
|
1400
1446
|
} catch {}
|
|
1401
1447
|
const context = {
|
|
1402
1448
|
inboxMessages: inboxMessages.length > 0 ? inboxMessages : undefined,
|
|
@@ -1481,6 +1527,8 @@ function createGoalService(host) {
|
|
|
1481
1527
|
runtime.consecutiveFailures = 0;
|
|
1482
1528
|
runtime.lastError = undefined;
|
|
1483
1529
|
runtime.forceFinishRequested = undefined;
|
|
1530
|
+
runtime.lastParentNotifiedAt = undefined;
|
|
1531
|
+
runtime.lastParentNotifiedFor = undefined;
|
|
1484
1532
|
runtime.phase = "idle";
|
|
1485
1533
|
runtime.updatedAt = new Date().toISOString();
|
|
1486
1534
|
}
|
|
@@ -1583,6 +1631,21 @@ function createGoalService(host) {
|
|
|
1583
1631
|
}
|
|
1584
1632
|
|
|
1585
1633
|
// src/server/host-adapter.ts
|
|
1634
|
+
var recentParentNotifies = new Map;
|
|
1635
|
+
function shouldDedupParentNotify(ownerSessionID, message) {
|
|
1636
|
+
const key = `${ownerSessionID}:${message.slice(0, 200)}`;
|
|
1637
|
+
const now = Date.now();
|
|
1638
|
+
const last = recentParentNotifies.get(key);
|
|
1639
|
+
if (last !== undefined && now - last < 60000)
|
|
1640
|
+
return true;
|
|
1641
|
+
recentParentNotifies.set(key, now);
|
|
1642
|
+
if (recentParentNotifies.size > 200) {
|
|
1643
|
+
for (const [k, t] of recentParentNotifies.entries())
|
|
1644
|
+
if (now - t > 60000)
|
|
1645
|
+
recentParentNotifies.delete(k);
|
|
1646
|
+
}
|
|
1647
|
+
return false;
|
|
1648
|
+
}
|
|
1586
1649
|
function createRealHost(client, directory) {
|
|
1587
1650
|
return {
|
|
1588
1651
|
async createWorker({ parentID, title, agent }) {
|
|
@@ -1674,6 +1737,10 @@ function createRealHost(client, directory) {
|
|
|
1674
1737
|
} catch {}
|
|
1675
1738
|
},
|
|
1676
1739
|
async notifyOwner(ownerSessionID, message) {
|
|
1740
|
+
if (shouldDedupParentNotify(ownerSessionID, message)) {
|
|
1741
|
+
await logServerEvent(directory, "parent.notify.deduped", { ownerSessionID, preview: message.slice(0, 160) });
|
|
1742
|
+
return;
|
|
1743
|
+
}
|
|
1677
1744
|
try {
|
|
1678
1745
|
const result = await withTimeout(client.session.promptAsync({
|
|
1679
1746
|
path: { id: ownerSessionID },
|
|
@@ -1882,12 +1949,25 @@ function goalTools(dir, goalService, hostSessionID) {
|
|
|
1882
1949
|
if (goal.config.checks?.length) {
|
|
1883
1950
|
const checkResults = await runCompletionChecks(goal.config.checks);
|
|
1884
1951
|
if (!checkResults.passed) {
|
|
1952
|
+
const runtime2 = state.runtimes.find((r) => r.goalID === goal.id);
|
|
1953
|
+
if (runtime2) {
|
|
1954
|
+
runtime2.evaluatorRejectionCount = (runtime2.evaluatorRejectionCount || 0) + 1;
|
|
1955
|
+
if (runtime2.evaluatorRejectionCount >= 3) {
|
|
1956
|
+
runtime2.forceFinishRequested = true;
|
|
1957
|
+
} else {
|
|
1958
|
+
runtime2.forceFinishRequested = false;
|
|
1959
|
+
runtime2.turnCount = Math.max(0, runtime2.turnCount - 1);
|
|
1960
|
+
}
|
|
1961
|
+
runtime2.updatedAt = new Date().toISOString();
|
|
1962
|
+
await writeState(dir, state);
|
|
1963
|
+
}
|
|
1885
1964
|
return {
|
|
1886
|
-
title: "
|
|
1965
|
+
title: "Completion rejected \u2014 keep working",
|
|
1887
1966
|
output: JSON.stringify({
|
|
1888
1967
|
passed: false,
|
|
1889
1968
|
failedChecks: checkResults.failures,
|
|
1890
|
-
message: "
|
|
1969
|
+
message: "Evaluator rejected completion. Fix the issues above and try again.",
|
|
1970
|
+
rejectionCount: runtime2?.evaluatorRejectionCount || 0
|
|
1891
1971
|
})
|
|
1892
1972
|
};
|
|
1893
1973
|
}
|
|
@@ -2417,11 +2497,20 @@ var server = async ({ client, directory }) => {
|
|
|
2417
2497
|
const goalID = parsed.goalID;
|
|
2418
2498
|
if (!goalID)
|
|
2419
2499
|
return;
|
|
2420
|
-
const { readState: readState2 } = await Promise.resolve().then(() => (init_state_repository(), exports_state_repository));
|
|
2500
|
+
const { readState: readState2, writeState: writeState2 } = await Promise.resolve().then(() => (init_state_repository(), exports_state_repository));
|
|
2501
|
+
const { shouldNotifyParent: shouldNotifyParent2, markParentNotified: markParentNotified2 } = await Promise.resolve().then(() => exports_runtime);
|
|
2421
2502
|
const state = await readState2(directory);
|
|
2422
2503
|
const goal = state.goals.find((g) => g.id === goalID);
|
|
2423
2504
|
if (!goal)
|
|
2424
2505
|
return;
|
|
2506
|
+
const runtime = state.runtimes.find((r) => r.goalID === goalID);
|
|
2507
|
+
const notifyType = parsed.status === "complete" ? "complete" : "blocked";
|
|
2508
|
+
if (runtime && !shouldNotifyParent2(runtime, notifyType))
|
|
2509
|
+
return;
|
|
2510
|
+
if (runtime) {
|
|
2511
|
+
markParentNotified2(runtime, notifyType);
|
|
2512
|
+
await writeState2(directory, state);
|
|
2513
|
+
}
|
|
2425
2514
|
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 || ""}.`;
|
|
2426
2515
|
await host.notifyOwner(goal.ownerSessionID, message);
|
|
2427
2516
|
} catch {}
|
package/dist/tui.js
CHANGED
|
@@ -249,7 +249,7 @@ function tokenize(input) {
|
|
|
249
249
|
function commandHelp() {
|
|
250
250
|
return [
|
|
251
251
|
"Modes: : insert \u2192 send/commands | Ctrl+N \u2192 normal | ? toggle help | Shift+B bug report",
|
|
252
|
-
"Nav: j/k move | g/G top/bottom | o open child | p/r/R/x pause/resume/retry/clear | L logs | q close",
|
|
252
|
+
"Nav: j/k move | g/G top/bottom | o open child | c toggle done | p/r/R/x pause/resume/retry/clear | L logs | q close",
|
|
253
253
|
"Commands (insert mode, : prefix):",
|
|
254
254
|
" :send <message> Send instruction to selected goal",
|
|
255
255
|
" :open Open child session (same as o)",
|
|
@@ -464,12 +464,13 @@ function LoopDashboard(props) {
|
|
|
464
464
|
const [mode, setMode] = createSignal("normal");
|
|
465
465
|
const [selected, setSelected] = createSignal(0);
|
|
466
466
|
const [commandInput, setCommandInput] = createSignal("");
|
|
467
|
-
const [statusText, setStatusText] = createSignal("Press : to send/command, ? help, o open, q close");
|
|
467
|
+
const [statusText, setStatusText] = createSignal("Press : to send/command, ? help, c toggle done, o open, q close");
|
|
468
468
|
const [state, setState] = createSignal(null);
|
|
469
469
|
const [events, setEvents] = createSignal([]);
|
|
470
470
|
const [selectedGoal, setSelectedGoal] = createSignal(null);
|
|
471
471
|
const [showLogs, setShowLogs] = createSignal(false);
|
|
472
472
|
const [showHelp, setShowHelp] = createSignal(false);
|
|
473
|
+
const [showCompleted, setShowCompleted] = createSignal(false);
|
|
473
474
|
const [clock, setClock] = createSignal(Date.now());
|
|
474
475
|
let inputEl;
|
|
475
476
|
let focusTimer;
|
|
@@ -489,7 +490,7 @@ function LoopDashboard(props) {
|
|
|
489
490
|
try {
|
|
490
491
|
const s = await client.getState();
|
|
491
492
|
setState(s);
|
|
492
|
-
const goals2 = s.goals.filter((g) => g.status !== "complete");
|
|
493
|
+
const goals2 = s.goals.filter((g) => showCompleted() || g.status !== "complete");
|
|
493
494
|
if (goals2.length > 0 && selected() >= goals2.length)
|
|
494
495
|
setSelected(goals2.length - 1);
|
|
495
496
|
setSelectedGoal(goals2[selected()] || null);
|
|
@@ -568,7 +569,13 @@ function LoopDashboard(props) {
|
|
|
568
569
|
return;
|
|
569
570
|
}
|
|
570
571
|
const key = raw || seq || name;
|
|
571
|
-
|
|
572
|
+
if (key === "c") {
|
|
573
|
+
prevent(evt);
|
|
574
|
+
setShowCompleted((v) => !v);
|
|
575
|
+
debugLog("toggle completed");
|
|
576
|
+
return;
|
|
577
|
+
}
|
|
578
|
+
const currentGoals = state()?.goals.filter((goal) => showCompleted() || goal.status !== "complete") || [];
|
|
572
579
|
if (name === "down" || key === "j") {
|
|
573
580
|
prevent(evt);
|
|
574
581
|
setSelected((index) => Math.min(currentGoals.length - 1, index + 1));
|
|
@@ -843,7 +850,7 @@ function LoopDashboard(props) {
|
|
|
843
850
|
}
|
|
844
851
|
returnToNormalMode();
|
|
845
852
|
}
|
|
846
|
-
const activeGoals = () => state()?.goals.filter((g) => g.status !== "complete") || [];
|
|
853
|
+
const activeGoals = () => state()?.goals.filter((g) => showCompleted() || g.status !== "complete") || [];
|
|
847
854
|
const runningCount = () => state()?.runtimes.filter((runtime) => runtime.phase === "running").length || 0;
|
|
848
855
|
const runningFrame = () => ["|", "/", "-", "\\"][Math.floor(clock() / 500) % 4];
|
|
849
856
|
function handleBugReport() {
|
|
@@ -948,7 +955,7 @@ function LoopDashboard(props) {
|
|
|
948
955
|
_$setProp(_el$30, "maxHeight", 14);
|
|
949
956
|
_$setProp(_el$30, "overflow", "hidden");
|
|
950
957
|
_$insertNode(_el$31, _el$32);
|
|
951
|
-
_$insertNode(_el$32, _$createTextNode(`\u2501\u2501\u2501 Keys: ? toggle : insert Ctrl+N normal o open
|
|
958
|
+
_$insertNode(_el$32, _$createTextNode(`\u2501\u2501\u2501 Keys: ? toggle help c toggle done : insert Ctrl+N normal o open Bug report q close \u2501\u2501\u2501`));
|
|
952
959
|
_$setProp(_el$32, "style", {
|
|
953
960
|
fg: "yellow",
|
|
954
961
|
bold: true
|
|
@@ -1520,45 +1527,36 @@ function LoopDashboard(props) {
|
|
|
1520
1527
|
_$insertNode(_el$36, _el$39);
|
|
1521
1528
|
_$insertNode(_el$37, _$createTextNode(`\u25C8 Recent Events`));
|
|
1522
1529
|
_$insertNode(_el$39, _$createTextNode(` \u2014 :logs to hide`));
|
|
1523
|
-
_$insert(_el$
|
|
1530
|
+
_$insert(_el$36, _$createComponent(For, {
|
|
1524
1531
|
get each() {
|
|
1525
1532
|
return events().slice(-10);
|
|
1526
1533
|
},
|
|
1527
|
-
children: (ev) =>
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
_$
|
|
1531
|
-
_$
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
_$
|
|
1535
|
-
var _c$11 = _$memo(() => !!ev.summary);
|
|
1536
|
-
return () => _c$11() && (() => {
|
|
1537
|
-
var _el$150 = _$createElement("span"), _el$151 = _$createTextNode(` \u2014 `);
|
|
1538
|
-
_$insertNode(_el$150, _el$151);
|
|
1539
|
-
_$insert(_el$150, () => String(ev.summary).slice(0, 60), null);
|
|
1540
|
-
_$effect((_$p) => _$setProp(_el$150, "style", {
|
|
1541
|
-
fg: theme().text
|
|
1542
|
-
}, _$p));
|
|
1543
|
-
return _el$150;
|
|
1544
|
-
})();
|
|
1545
|
-
})(), null);
|
|
1546
|
-
_$effect((_p$) => {
|
|
1547
|
-
var _v$39 = {
|
|
1548
|
-
fg: eventColor(String(ev.type), theme()),
|
|
1549
|
-
bold: true
|
|
1550
|
-
}, _v$40 = {
|
|
1551
|
-
fg: theme().textMuted
|
|
1552
|
-
};
|
|
1553
|
-
_v$39 !== _p$.e && (_p$.e = _$setProp(_el$147, "style", _v$39, _p$.e));
|
|
1554
|
-
_v$40 !== _p$.t && (_p$.t = _$setProp(_el$148, "style", _v$40, _p$.t));
|
|
1555
|
-
return _p$;
|
|
1556
|
-
}, {
|
|
1557
|
-
e: undefined,
|
|
1558
|
-
t: undefined
|
|
1559
|
-
});
|
|
1534
|
+
children: (ev) => [`
|
|
1535
|
+
`, (() => {
|
|
1536
|
+
var _el$146 = _$createElement("span");
|
|
1537
|
+
_$insert(_el$146, () => String(ev.type));
|
|
1538
|
+
_$effect((_$p) => _$setProp(_el$146, "style", {
|
|
1539
|
+
fg: eventColor(String(ev.type), theme()),
|
|
1540
|
+
bold: true
|
|
1541
|
+
}, _$p));
|
|
1560
1542
|
return _el$146;
|
|
1561
|
-
})()
|
|
1543
|
+
})(), (() => {
|
|
1544
|
+
var _el$147 = _$createElement("span"), _el$148 = _$createTextNode(` `);
|
|
1545
|
+
_$insertNode(_el$147, _el$148);
|
|
1546
|
+
_$insert(_el$147, () => ev.goalID?.slice(0, 8), null);
|
|
1547
|
+
_$effect((_$p) => _$setProp(_el$147, "style", {
|
|
1548
|
+
fg: theme().textMuted
|
|
1549
|
+
}, _$p));
|
|
1550
|
+
return _el$147;
|
|
1551
|
+
})(), _$memo(() => _$memo(() => !!ev.summary)() && (() => {
|
|
1552
|
+
var _el$149 = _$createElement("span"), _el$150 = _$createTextNode(` \u2014 `);
|
|
1553
|
+
_$insertNode(_el$149, _el$150);
|
|
1554
|
+
_$insert(_el$149, () => String(ev.summary).slice(0, 60), null);
|
|
1555
|
+
_$effect((_$p) => _$setProp(_el$149, "style", {
|
|
1556
|
+
fg: theme().text
|
|
1557
|
+
}, _$p));
|
|
1558
|
+
return _el$149;
|
|
1559
|
+
})())]
|
|
1562
1560
|
}), null);
|
|
1563
1561
|
_$effect((_p$) => {
|
|
1564
1562
|
var _v$ = theme().border, _v$2 = {
|
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.
|
|
4
|
+
"version": "1.5.1",
|
|
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-only",
|