@bojackduy/opencode-loopd 1.5.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 CHANGED
@@ -1252,6 +1252,9 @@ function buildContinuationSteering(goal, runtime, context) {
1252
1252
  }
1253
1253
  if (v.artifactSummary)
1254
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
+ }
1255
1258
  }
1256
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.`);
1257
1260
  if (context?.forceFinish) {
@@ -1437,6 +1440,9 @@ function createGoalService(host) {
1437
1440
  const c = `checks configured: ${goal.config.checks.length} \u2014 run them before claiming completion`;
1438
1441
  verification = { ...verification || {}, failedChecks: [c], checksPassed: undefined };
1439
1442
  }
1443
+ if (runtime.evaluatorRejectionCount && runtime.evaluatorRejectionCount > 0) {
1444
+ verification = { ...verification || {}, evaluatorRejectionCount: runtime.evaluatorRejectionCount };
1445
+ }
1440
1446
  } catch {}
1441
1447
  const context = {
1442
1448
  inboxMessages: inboxMessages.length > 0 ? inboxMessages : undefined,
@@ -1943,12 +1949,25 @@ function goalTools(dir, goalService, hostSessionID) {
1943
1949
  if (goal.config.checks?.length) {
1944
1950
  const checkResults = await runCompletionChecks(goal.config.checks);
1945
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
+ }
1946
1964
  return {
1947
- title: "Checks failed",
1965
+ title: "Completion rejected \u2014 keep working",
1948
1966
  output: JSON.stringify({
1949
1967
  passed: false,
1950
1968
  failedChecks: checkResults.failures,
1951
- message: "Completion checks failed. Fix issues and try again."
1969
+ message: "Evaluator rejected completion. Fix the issues above and try again.",
1970
+ rejectionCount: runtime2?.evaluatorRejectionCount || 0
1952
1971
  })
1953
1972
  };
1954
1973
  }
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
- const currentGoals = state()?.goals.filter((goal) => goal.status !== "complete") || [];
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 B bug q close \u2501\u2501\u2501`));
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$35, _$createComponent(For, {
1530
+ _$insert(_el$36, _$createComponent(For, {
1524
1531
  get each() {
1525
1532
  return events().slice(-10);
1526
1533
  },
1527
- children: (ev) => (() => {
1528
- var _el$146 = _$createElement("text"), _el$147 = _$createElement("span"), _el$148 = _$createElement("span"), _el$149 = _$createTextNode(` `);
1529
- _$insertNode(_el$146, _el$147);
1530
- _$insertNode(_el$146, _el$148);
1531
- _$insert(_el$147, () => String(ev.type));
1532
- _$insertNode(_el$148, _el$149);
1533
- _$insert(_el$148, () => ev.goalID?.slice(0, 8), null);
1534
- _$insert(_el$146, (() => {
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.5.0",
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",