@bojackduy/opencode-loopd 1.9.1 → 1.9.2

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.
Files changed (3) hide show
  1. package/dist/server.js +138 -5
  2. package/dist/tui.js +287 -200
  3. package/package.json +1 -1
package/dist/server.js CHANGED
@@ -34,6 +34,7 @@ function acquireLease(rt, timeoutMs) {
34
34
  idleConfirmFailedAt: undefined,
35
35
  idleConfirmFailedGeneration: undefined,
36
36
  idleStuckNotifiedGeneration: undefined,
37
+ workerAbortedAt: undefined,
37
38
  activePromptObservedAt: undefined,
38
39
  activeAssistantMessageID: undefined,
39
40
  activeAssistantCompletedAt: undefined,
@@ -595,12 +596,17 @@ function createControlWorker(options) {
595
596
  if (running)
596
597
  return;
597
598
  running = true;
598
- processPending();
599
+ processPending().catch((error) => {
600
+ logServerEvent(directory, "control.worker.error", { detail: describeError(error) }).catch(() => {});
601
+ });
599
602
  pollTimer = setInterval(() => {
600
603
  if (running && lastProcessDone) {
601
604
  lastProcessDone = false;
602
605
  processPending().then(() => {
603
606
  lastProcessDone = true;
607
+ }, (error) => {
608
+ lastProcessDone = true;
609
+ logServerEvent(directory, "control.worker.error", { detail: describeError(error) }).catch(() => {});
604
610
  });
605
611
  }
606
612
  }, pollMs);
@@ -749,6 +755,21 @@ function createControlWorker(options) {
749
755
  };
750
756
  break;
751
757
  }
758
+ case "nudge": {
759
+ if (!request.goalID) {
760
+ response = { ...base, ok: false, message: "goalID is required", errorCode: "bad_request" };
761
+ break;
762
+ }
763
+ const result = await goalSvc.nudge(directory, request.goalID);
764
+ const state = await readState(directory);
765
+ response = {
766
+ ...base,
767
+ ok: result.ok,
768
+ message: result.message,
769
+ stateRevision: state.revision
770
+ };
771
+ break;
772
+ }
752
773
  case "clear": {
753
774
  await goalSvc.clear(directory, request.goalID);
754
775
  const state = await readState(directory);
@@ -770,12 +791,27 @@ function createControlWorker(options) {
770
791
  response = { ...base, ok: false, message: "goalID is required", errorCode: "bad_request" };
771
792
  break;
772
793
  }
773
- await appendGoalInbox(directory, request.goalID, "user", text);
794
+ const sent = await goalSvc.sendUserMessage(directory, request.goalID, text);
774
795
  const state = await readState(directory);
775
- const goal = state.goals.find((g) => g.id === request.goalID);
776
796
  response = {
777
797
  ...base,
778
- message: `sent to "${goal?.name || request.goalID}"`,
798
+ ok: sent.ok,
799
+ message: sent.message,
800
+ stateRevision: state.revision
801
+ };
802
+ break;
803
+ }
804
+ case "abort_worker": {
805
+ if (!request.goalID) {
806
+ response = { ...base, ok: false, message: "goalID is required", errorCode: "bad_request" };
807
+ break;
808
+ }
809
+ const result = await goalSvc.abortWorker(directory, request.goalID);
810
+ const state = await readState(directory);
811
+ response = {
812
+ ...base,
813
+ ok: result.ok,
814
+ message: result.message,
779
815
  stateRevision: state.revision
780
816
  };
781
817
  break;
@@ -2053,6 +2089,16 @@ function createWorkerManager(host) {
2053
2089
  });
2054
2090
  return result;
2055
2091
  },
2092
+ async sendBare(worker, goal, runtime, text) {
2093
+ const result = await host.promptWorker({
2094
+ sessionID: worker.workerSessionID,
2095
+ prompt: text,
2096
+ messageID: runtime.activePromptMessageID,
2097
+ agent: goal.config.agent,
2098
+ model: parseModelRef(goal.config.model)
2099
+ });
2100
+ return result;
2101
+ },
2056
2102
  async isIdle(workerSessionID) {
2057
2103
  const status = await host.sessionStatus(workerSessionID);
2058
2104
  return status === "idle";
@@ -2511,6 +2557,20 @@ function createGoalService(host) {
2511
2557
  timestamp: new Date().toISOString(),
2512
2558
  revision: state.revision
2513
2559
  });
2560
+ if (opts?.bare) {
2561
+ const bareWords = await drainGoalInbox(directory, goalID);
2562
+ const bareText = bareWords.join(`
2563
+ `).trim();
2564
+ if (bareText) {
2565
+ try {
2566
+ await workers.sendBare(session, freshGoal, freshRuntime, bareText);
2567
+ } catch (error) {
2568
+ await recordPromptFailure(directory, goalID, error);
2569
+ throw error;
2570
+ }
2571
+ return;
2572
+ }
2573
+ }
2514
2574
  const inboxMessages = await drainGoalInbox(directory, goalID);
2515
2575
  const allEvents = await readEvents(directory, 200);
2516
2576
  const progressHistory = allEvents.filter((e) => e.goalID === goalID && e.type === "goal.progress").map((e) => ({
@@ -2818,6 +2878,40 @@ function createGoalService(host) {
2818
2878
  await continueTurnUnlocked(directory, goalID, { force: true });
2819
2879
  return { ok: true, message: `Re-prompted worker for "${freshGoal.name}".` };
2820
2880
  }
2881
+ async function sendUnlocked(directory, goalID, text) {
2882
+ const trimmed = text.trim();
2883
+ if (!trimmed)
2884
+ return { ok: false, message: "Nothing to send." };
2885
+ const preState = await readState(directory);
2886
+ const goal = preState.goals.find((g) => g.id === goalID);
2887
+ if (!goal)
2888
+ return { ok: false, message: "Goal not found." };
2889
+ await appendGoalInbox(directory, goalID, "user", trimmed);
2890
+ if (goal.status !== "active") {
2891
+ return { ok: true, message: `Queued for "${goal.name}" (goal is ${goal.status}; delivers on the next active turn).` };
2892
+ }
2893
+ const cleared = await mutateState(directory, `goal.send:${goalID}`, async (s) => {
2894
+ const rt = s.runtimes.find((r) => r.goalID === goalID);
2895
+ if (!rt)
2896
+ return s;
2897
+ rt.phase = "idle";
2898
+ rt.activeRunID = undefined;
2899
+ rt.idleCandidateAt = undefined;
2900
+ rt.activePromptMessageID = undefined;
2901
+ rt.activeToolCallIDs = [];
2902
+ rt.updatedAt = new Date().toISOString();
2903
+ return s;
2904
+ });
2905
+ const freshGoal = cleared.goals.find((g) => g.id === goalID);
2906
+ if (!freshGoal || !freshGoal.workerSessionID) {
2907
+ return { ok: true, message: `Queued for "${goal.name}" (no worker session yet; delivers on the next turn).` };
2908
+ }
2909
+ await continueTurnUnlocked(directory, goalID, { force: true, bare: true });
2910
+ return { ok: true, message: `Sent to "${freshGoal.name}" as its own turn.` };
2911
+ }
2912
+ function sendUserMessage(directory, goalID, text) {
2913
+ return withGoalOperation(goalID, () => sendUnlocked(directory, goalID, text));
2914
+ }
2821
2915
  function continueTurn(directory, goalID, opts) {
2822
2916
  return withGoalOperation(goalID, () => continueTurnUnlocked(directory, goalID, opts));
2823
2917
  }
@@ -2836,10 +2930,48 @@ function createGoalService(host) {
2836
2930
  function nudge(directory, goalID) {
2837
2931
  return withGoalOperation(goalID, () => nudgeUnlocked(directory, goalID));
2838
2932
  }
2933
+ async function abortWorkerUnlocked(directory, goalID) {
2934
+ const preState = await readState(directory);
2935
+ const goal = preState.goals.find((g) => g.id === goalID);
2936
+ if (!goal)
2937
+ return { ok: false, message: "Goal not found." };
2938
+ const workerID = sessions.get(goalID)?.workerSessionID || goal.workerSessionID;
2939
+ if (!workerID)
2940
+ return { ok: false, message: `Goal "${goal.name}" has no worker session to abort.` };
2941
+ try {
2942
+ await workers.abortWorker(workerID);
2943
+ } catch {}
2944
+ sessions.delete(goalID);
2945
+ await mutateState(directory, `goal.abort-worker:${goalID}`, async (s) => {
2946
+ const rt = s.runtimes.find((r) => r.goalID === goalID);
2947
+ if (rt) {
2948
+ Object.assign(rt, releaseLease(rt));
2949
+ rt.activeRunID = undefined;
2950
+ rt.activePromptMessageID = undefined;
2951
+ rt.activeToolCallIDs = [];
2952
+ rt.idleCandidateAt = undefined;
2953
+ rt.idleCandidateGeneration = undefined;
2954
+ rt.workerAbortedAt = new Date().toISOString();
2955
+ rt.updatedAt = new Date().toISOString();
2956
+ }
2957
+ const g = s.goals.find((item) => item.id === goalID);
2958
+ if (g)
2959
+ g.updatedAt = new Date().toISOString();
2960
+ return s;
2961
+ });
2962
+ await logServerEvent(directory, "worker.aborted-manual", { goalID, workerSessionID: workerID });
2963
+ return {
2964
+ ok: true,
2965
+ message: `Worker run for "${goal.name}" aborted, session kept for inspection (status unchanged: ${goal.status}).` + (goal.status === "active" ? " Engine continues the same session next turn." : "")
2966
+ };
2967
+ }
2968
+ function abortWorker(directory, goalID) {
2969
+ return withGoalOperation(goalID, () => abortWorkerUnlocked(directory, goalID));
2970
+ }
2839
2971
  function accountUsage(directory, goalID) {
2840
2972
  return withGoalOperation(goalID, () => accountUsageUnlocked(directory, goalID));
2841
2973
  }
2842
- return { start, continueTurn, nudge, pause, resume, retry, clear, getWorker, getActiveWorkers, reconcile, accountUsage };
2974
+ return { start, continueTurn, nudge, pause, resume, retry, clear, getWorker, getActiveWorkers, reconcile, accountUsage, abortWorker, sendUserMessage };
2843
2975
  }
2844
2976
 
2845
2977
  // src/application/schedule-worker.ts
@@ -3698,6 +3830,7 @@ function ownerTools(options) {
3698
3830
  unknownStatusCount: runtime.unknownStatusCount,
3699
3831
  lastUnknownStatusAt: runtime.lastUnknownStatusAt,
3700
3832
  workerUnreachableNotifiedAt: runtime.workerUnreachableNotifiedAt,
3833
+ workerAbortedAt: runtime.workerAbortedAt,
3701
3834
  retryAfter: runtime.retryAfter,
3702
3835
  forceFinishRequested: runtime.forceFinishRequested,
3703
3836
  scheduleRunCount: runtime.scheduleRunCount,
package/dist/tui.js CHANGED
@@ -322,13 +322,15 @@ function tokenize(input) {
322
322
  function commandHelp() {
323
323
  return [
324
324
  "Modes: : insert \u2192 send/commands | Ctrl+N \u2192 normal | ? toggle help | Shift+B bug report",
325
- "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",
325
+ "Nav: j/k move | g/G top/bottom | o open child | c toggle done | p/r/R/x pause/resume/retry/clear | A abort worker | N nudge | L logs | q close",
326
326
  "Commands (insert mode, : prefix):",
327
- " :send <message> Send instruction to selected goal",
327
+ " :send <message> Send bare words now as their own turn (no steering)",
328
328
  " :open Open child session (same as o)",
329
329
  " :force <summary> --evidence <text> Force-complete (bypass checks)",
330
330
  " :block <reason> --needed <text> Force-block the selected goal",
331
331
  " :pause / :resume / :retry / :clear Quick controls (also p/r/R/x)",
332
+ " :abort Abort worker session now (status unchanged; A)",
333
+ " :nudge Force re-prompt now with full steering (N)",
332
334
  " :bug / :report Open prefilled GitHub bug report",
333
335
  " :logs / :help / :q Toggle logs / help / close",
334
336
  " Tip: create goals via /goal in the parent chat (agent clarifies first)."
@@ -427,6 +429,29 @@ function prevent(evt) {
427
429
  e.preventDefault?.();
428
430
  e.stopPropagation?.();
429
431
  }
432
+ function keyName(evt) {
433
+ return (evt.name || "").toLowerCase();
434
+ }
435
+ function keySeq(evt) {
436
+ const e = evt;
437
+ return e.sequence || e.raw || "";
438
+ }
439
+ function isEnterKey(evt) {
440
+ const name = keyName(evt);
441
+ if (name === "return" || name === "enter" || name === "kp_enter")
442
+ return true;
443
+ const seq = keySeq(evt);
444
+ return seq === "\r" || seq === `
445
+ `;
446
+ }
447
+ function isEscapeKey(evt) {
448
+ if (keyName(evt) === "escape" || keyName(evt) === "esc")
449
+ return true;
450
+ return keySeq(evt) === "\x1B";
451
+ }
452
+ function isCtrlN(evt) {
453
+ return Boolean(evt.ctrl) && keyName(evt) === "n";
454
+ }
430
455
  function statusColor(status, theme) {
431
456
  switch (status) {
432
457
  case "active":
@@ -665,9 +690,9 @@ function LoopDashboard(props) {
665
690
  onMount(() => {
666
691
  try {
667
692
  const {
668
- writeFileSync
693
+ appendFileSync
669
694
  } = __require("fs");
670
- writeFileSync(LOG_FILE, `[${new Date().toISOString()}] dashboard mounted dir=${props.directory} mode=${mode()} dialogOpen=${props.api.ui.dialog.open}
695
+ appendFileSync(LOG_FILE, `[${new Date().toISOString()}] dashboard mounted dir=${props.directory} mode=${mode()} dialogOpen=${props.api.ui.dialog.open}
671
696
  `);
672
697
  } catch {}
673
698
  debugLog("mounted", "dialogOpen", props.api.ui.dialog.open, "directory", props.directory);
@@ -703,10 +728,17 @@ function LoopDashboard(props) {
703
728
  const isQuestion = name === "?" || seq === "?" || raw === "?" || seq.includes("?") || raw.includes("?");
704
729
  debugLog("isColon", isColon, "isQuestion", isQuestion, "modeBefore", mode());
705
730
  if (mode() === "insert") {
706
- if (evt.ctrl && name.toLowerCase() === "n") {
731
+ if (isEnterKey(evt)) {
732
+ prevent(evt);
733
+ debugLog("insert enter -> execute");
734
+ executeCommand(commandInput());
735
+ return;
736
+ }
737
+ if (isEscapeKey(evt) || isCtrlN(evt)) {
707
738
  prevent(evt);
708
739
  returnToNormalMode();
709
- debugLog("insert -> normal via ctrl+n");
740
+ debugLog("insert -> normal via esc/ctrl+n");
741
+ return;
710
742
  }
711
743
  return;
712
744
  }
@@ -770,6 +802,16 @@ function LoopDashboard(props) {
770
802
  executeCommand("clear");
771
803
  return;
772
804
  }
805
+ if (key === "A") {
806
+ prevent(evt);
807
+ executeCommand("abort");
808
+ return;
809
+ }
810
+ if (key === "N") {
811
+ prevent(evt);
812
+ executeCommand("nudge");
813
+ return;
814
+ }
773
815
  if (key === "L") {
774
816
  prevent(evt);
775
817
  setShowLogs((value) => !value);
@@ -807,6 +849,9 @@ function LoopDashboard(props) {
807
849
  debugLog("empty command");
808
850
  return;
809
851
  }
852
+ if (!["help", "logs", "q", "close"].includes(parsed.command)) {
853
+ setStatusText(`sending ${parsed.command}\u2026`);
854
+ }
810
855
  try {
811
856
  switch (parsed.command) {
812
857
  case "send": {
@@ -960,6 +1005,40 @@ function LoopDashboard(props) {
960
1005
  await refresh();
961
1006
  break;
962
1007
  }
1008
+ case "abort": {
1009
+ if (!selectedGoal()) {
1010
+ setStatusText("No goal");
1011
+ break;
1012
+ }
1013
+ const r = await client.execute({
1014
+ version: 1,
1015
+ requestID: randomUUID(),
1016
+ requestedAt: new Date().toISOString(),
1017
+ command: "abort_worker",
1018
+ goalID: selectedGoal().id
1019
+ });
1020
+ setStatusText(r.ok ? r.message : `Error: ${r.message}`);
1021
+ if (r.ok)
1022
+ await refresh();
1023
+ break;
1024
+ }
1025
+ case "nudge": {
1026
+ if (!selectedGoal()) {
1027
+ setStatusText("No goal");
1028
+ break;
1029
+ }
1030
+ const r = await client.execute({
1031
+ version: 1,
1032
+ requestID: randomUUID(),
1033
+ requestedAt: new Date().toISOString(),
1034
+ command: "nudge",
1035
+ goalID: selectedGoal().id
1036
+ });
1037
+ setStatusText(r.ok ? r.message : `Error: ${r.message}`);
1038
+ if (r.ok)
1039
+ await refresh();
1040
+ break;
1041
+ }
963
1042
  case "goal": {
964
1043
  setStatusText("Create goals via /goal in the parent chat (agent clarifies first). Dashboard: :send to steer the worker.");
965
1044
  break;
@@ -1111,7 +1190,7 @@ function LoopDashboard(props) {
1111
1190
  _$setProp(_el$32, "maxHeight", 14);
1112
1191
  _$setProp(_el$32, "overflow", "hidden");
1113
1192
  _$insertNode(_el$33, _el$34);
1114
- _$insertNode(_el$34, _$createTextNode(`\u2501\u2501\u2501 Keys: ? toggle help c toggle done : insert Ctrl+N normal o open Bug report q close \u2501\u2501\u2501`));
1193
+ _$insertNode(_el$34, _$createTextNode(`\u2501\u2501\u2501 Keys: ? toggle help c toggle done : insert Ctrl+N normal o open A abort worker N nudge q close \u2501\u2501\u2501`));
1115
1194
  _$setProp(_el$34, "style", {
1116
1195
  fg: "yellow",
1117
1196
  bold: true
@@ -1596,6 +1675,26 @@ function LoopDashboard(props) {
1596
1675
  return _el$133;
1597
1676
  })()];
1598
1677
  })(), _el$127);
1678
+ _$insert(_el$122, (() => {
1679
+ var _c$11 = _$memo(() => !!rt()?.workerAbortedAt);
1680
+ return () => _c$11() && [(() => {
1681
+ var _el$137 = _$createElement("span");
1682
+ _$insertNode(_el$137, _$createTextNode(` \u2502 \u26A0 worker aborted `));
1683
+ _$effect((_$p) => _$setProp(_el$137, "style", {
1684
+ fg: theme().warning,
1685
+ bold: true
1686
+ }, _$p));
1687
+ return _el$137;
1688
+ })(), (() => {
1689
+ var _el$139 = _$createElement("span"), _el$140 = _$createTextNode(` \u2014 session kept, next turn reuses it`);
1690
+ _$insertNode(_el$139, _el$140);
1691
+ _$insert(_el$139, () => ageLabel(rt().workerAbortedAt, clock()), _el$140);
1692
+ _$effect((_$p) => _$setProp(_el$139, "style", {
1693
+ fg: theme().textMuted
1694
+ }, _$p));
1695
+ return _el$139;
1696
+ })()];
1697
+ })(), _el$127);
1599
1698
  _$insert(_el$122, () => {
1600
1699
  const agentName = goal().config.agent;
1601
1700
  const meta = agentName ? agentIndex()[agentName] ?? agentIndex()[agentName.toLowerCase()] : undefined;
@@ -1603,272 +1702,253 @@ function LoopDashboard(props) {
1603
1702
  const slash = model?.indexOf("/") ?? -1;
1604
1703
  return [`
1605
1704
  `, (() => {
1606
- var _el$137 = _$createElement("span");
1607
- _$insertNode(_el$137, _$createTextNode(`\uD83E\uDD16 `));
1608
- _$effect((_$p) => _$setProp(_el$137, "style", {
1705
+ var _el$141 = _$createElement("span");
1706
+ _$insertNode(_el$141, _$createTextNode(`\uD83E\uDD16 `));
1707
+ _$effect((_$p) => _$setProp(_el$141, "style", {
1609
1708
  fg: theme().textMuted
1610
1709
  }, _$p));
1611
- return _el$137;
1710
+ return _el$141;
1612
1711
  })(), agentName ? [(() => {
1613
- var _el$149 = _$createElement("span");
1614
- _$insert(_el$149, agentName);
1615
- _$effect((_$p) => _$setProp(_el$149, "style", {
1712
+ var _el$153 = _$createElement("span");
1713
+ _$insert(_el$153, agentName);
1714
+ _$effect((_$p) => _$setProp(_el$153, "style", {
1616
1715
  fg: agentColor(meta?.color, theme()),
1617
1716
  bold: true
1618
1717
  }, _$p));
1619
- return _el$149;
1718
+ return _el$153;
1620
1719
  })(), _$memo(() => _$memo(() => !!meta?.mode)() && (() => {
1621
- var _el$150 = _$createElement("span"), _el$151 = _$createTextNode(` (`), _el$152 = _$createTextNode(`)`);
1622
- _$insertNode(_el$150, _el$151);
1623
- _$insertNode(_el$150, _el$152);
1624
- _$insert(_el$150, () => meta.mode, _el$152);
1625
- _$effect((_$p) => _$setProp(_el$150, "style", {
1720
+ var _el$154 = _$createElement("span"), _el$155 = _$createTextNode(` (`), _el$156 = _$createTextNode(`)`);
1721
+ _$insertNode(_el$154, _el$155);
1722
+ _$insertNode(_el$154, _el$156);
1723
+ _$insert(_el$154, () => meta.mode, _el$156);
1724
+ _$effect((_$p) => _$setProp(_el$154, "style", {
1626
1725
  fg: theme().textMuted
1627
1726
  }, _$p));
1628
- return _el$150;
1727
+ return _el$154;
1629
1728
  })())] : (() => {
1630
- var _el$153 = _$createElement("span");
1631
- _$insertNode(_el$153, _$createTextNode(`parent default`));
1632
- _$effect((_$p) => _$setProp(_el$153, "style", {
1729
+ var _el$157 = _$createElement("span");
1730
+ _$insertNode(_el$157, _$createTextNode(`parent default`));
1731
+ _$effect((_$p) => _$setProp(_el$157, "style", {
1633
1732
  fg: theme().textMuted
1634
1733
  }, _$p));
1635
- return _el$153;
1734
+ return _el$157;
1636
1735
  })(), (() => {
1637
- var _el$139 = _$createElement("span");
1638
- _$insertNode(_el$139, _$createTextNode(` \u2502 \uD83E\uDDE0 `));
1639
- _$effect((_$p) => _$setProp(_el$139, "style", {
1736
+ var _el$143 = _$createElement("span");
1737
+ _$insertNode(_el$143, _$createTextNode(` \u2502 \uD83E\uDDE0 `));
1738
+ _$effect((_$p) => _$setProp(_el$143, "style", {
1640
1739
  fg: theme().textMuted
1641
1740
  }, _$p));
1642
- return _el$139;
1741
+ return _el$143;
1643
1742
  })(), model && slash > 0 ? [(() => {
1644
- var _el$155 = _$createElement("span"), _el$156 = _$createTextNode(`/`);
1645
- _$insertNode(_el$155, _el$156);
1646
- _$insert(_el$155, () => model.slice(0, slash), _el$156);
1647
- _$effect((_$p) => _$setProp(_el$155, "style", {
1743
+ var _el$159 = _$createElement("span"), _el$160 = _$createTextNode(`/`);
1744
+ _$insertNode(_el$159, _el$160);
1745
+ _$insert(_el$159, () => model.slice(0, slash), _el$160);
1746
+ _$effect((_$p) => _$setProp(_el$159, "style", {
1648
1747
  fg: theme().textMuted
1649
1748
  }, _$p));
1650
- return _el$155;
1749
+ return _el$159;
1651
1750
  })(), (() => {
1652
- var _el$157 = _$createElement("span");
1653
- _$insert(_el$157, () => model.slice(slash + 1));
1654
- _$effect((_$p) => _$setProp(_el$157, "style", {
1751
+ var _el$161 = _$createElement("span");
1752
+ _$insert(_el$161, () => model.slice(slash + 1));
1753
+ _$effect((_$p) => _$setProp(_el$161, "style", {
1655
1754
  fg: theme().info,
1656
1755
  bold: true
1657
1756
  }, _$p));
1658
- return _el$157;
1757
+ return _el$161;
1659
1758
  })()] : (() => {
1660
- var _el$158 = _$createElement("span");
1661
- _$insertNode(_el$158, _$createTextNode(`session default`));
1662
- _$effect((_$p) => _$setProp(_el$158, "style", {
1759
+ var _el$162 = _$createElement("span");
1760
+ _$insertNode(_el$162, _$createTextNode(`session default`));
1761
+ _$effect((_$p) => _$setProp(_el$162, "style", {
1663
1762
  fg: theme().textMuted
1664
1763
  }, _$p));
1665
- return _el$158;
1764
+ return _el$162;
1666
1765
  })(), (() => {
1667
- var _el$141 = _$createElement("span");
1668
- _$insertNode(_el$141, _$createTextNode(` \u2502 \uD83D\uDCB0 `));
1669
- _$effect((_$p) => _$setProp(_el$141, "style", {
1766
+ var _el$145 = _$createElement("span");
1767
+ _$insertNode(_el$145, _$createTextNode(` \u2502 \uD83D\uDCB0 `));
1768
+ _$effect((_$p) => _$setProp(_el$145, "style", {
1670
1769
  fg: theme().textMuted
1671
1770
  }, _$p));
1672
- return _el$141;
1771
+ return _el$145;
1673
1772
  })(), (() => {
1674
- var _el$143 = _$createElement("span");
1675
- _$insert(_el$143, () => formatTokens(goal().tokensUsed));
1676
- _$effect((_$p) => _$setProp(_el$143, "style", {
1773
+ var _el$147 = _$createElement("span");
1774
+ _$insert(_el$147, () => formatTokens(goal().tokensUsed));
1775
+ _$effect((_$p) => _$setProp(_el$147, "style", {
1677
1776
  fg: theme().warning,
1678
1777
  bold: true
1679
1778
  }, _$p));
1680
- return _el$143;
1779
+ return _el$147;
1681
1780
  })(), (() => {
1682
- var _el$144 = _$createElement("span");
1683
- _$insertNode(_el$144, _$createTextNode(` tokens \xB7 `));
1684
- _$effect((_$p) => _$setProp(_el$144, "style", {
1781
+ var _el$148 = _$createElement("span");
1782
+ _$insertNode(_el$148, _$createTextNode(` tokens \xB7 `));
1783
+ _$effect((_$p) => _$setProp(_el$148, "style", {
1685
1784
  fg: theme().textMuted
1686
1785
  }, _$p));
1687
- return _el$144;
1786
+ return _el$148;
1688
1787
  })(), (() => {
1689
- var _el$146 = _$createElement("span");
1690
- _$insert(_el$146, () => formatCost(goal().costUsed));
1691
- _$effect((_$p) => _$setProp(_el$146, "style", {
1788
+ var _el$150 = _$createElement("span");
1789
+ _$insert(_el$150, () => formatCost(goal().costUsed));
1790
+ _$effect((_$p) => _$setProp(_el$150, "style", {
1692
1791
  fg: theme().success,
1693
1792
  bold: true
1694
1793
  }, _$p));
1695
- return _el$146;
1794
+ return _el$150;
1696
1795
  })(), (() => {
1697
- var _el$147 = _$createElement("span"), _el$148 = _$createTextNode(` \xB7 `);
1698
- _$insertNode(_el$147, _el$148);
1699
- _$insert(_el$147, () => formatDuration(goal().timeUsedSeconds), null);
1700
- _$effect((_$p) => _$setProp(_el$147, "style", {
1796
+ var _el$151 = _$createElement("span"), _el$152 = _$createTextNode(` \xB7 `);
1797
+ _$insertNode(_el$151, _el$152);
1798
+ _$insert(_el$151, () => formatDuration(goal().timeUsedSeconds), null);
1799
+ _$effect((_$p) => _$setProp(_el$151, "style", {
1701
1800
  fg: theme().textMuted
1702
1801
  }, _$p));
1703
- return _el$147;
1802
+ return _el$151;
1704
1803
  })()];
1705
1804
  }, _el$127);
1706
1805
  _$insert(_el$128, () => goal().objective.slice(0, 160));
1707
1806
  _$insert(_el$122, (() => {
1708
- var _c$11 = _$memo(() => !!lp());
1709
- return () => _c$11() && [(() => {
1710
- var _el$160 = _$createElement("span"), _el$161 = _$createTextNode(`
1807
+ var _c$12 = _$memo(() => !!lp());
1808
+ return () => _c$12() && [(() => {
1809
+ var _el$164 = _$createElement("span"), _el$165 = _$createTextNode(`
1711
1810
  \u2714 `);
1712
- _$insertNode(_el$160, _el$161);
1713
- _$effect((_$p) => _$setProp(_el$160, "style", {
1811
+ _$insertNode(_el$164, _el$165);
1812
+ _$effect((_$p) => _$setProp(_el$164, "style", {
1714
1813
  fg: theme().success
1715
1814
  }, _$p));
1716
- return _el$160;
1815
+ return _el$164;
1717
1816
  })(), (() => {
1718
- var _el$163 = _$createElement("span");
1719
- _$insert(_el$163, () => lp().summary.slice(0, 100));
1720
- _$effect((_$p) => _$setProp(_el$163, "style", {
1817
+ var _el$167 = _$createElement("span");
1818
+ _$insert(_el$167, () => lp().summary.slice(0, 100));
1819
+ _$effect((_$p) => _$setProp(_el$167, "style", {
1721
1820
  fg: theme().text
1722
1821
  }, _$p));
1723
- return _el$163;
1822
+ return _el$167;
1724
1823
  })(), (() => {
1725
- var _el$164 = _$createElement("span"), _el$165 = _$createTextNode(` \u2192 `);
1726
- _$insertNode(_el$164, _el$165);
1727
- _$insert(_el$164, () => lp().next?.slice(0, 60) || "", null);
1728
- _$effect((_$p) => _$setProp(_el$164, "style", {
1824
+ var _el$168 = _$createElement("span"), _el$169 = _$createTextNode(` \u2192 `);
1825
+ _$insertNode(_el$168, _el$169);
1826
+ _$insert(_el$168, () => lp().next?.slice(0, 60) || "", null);
1827
+ _$effect((_$p) => _$setProp(_el$168, "style", {
1729
1828
  fg: theme().textMuted
1730
1829
  }, _$p));
1731
- return _el$164;
1830
+ return _el$168;
1732
1831
  })()];
1733
1832
  })(), null);
1734
1833
  _$insert(_el$122, (() => {
1735
- var _c$12 = _$memo(() => !!blk());
1736
- return () => _c$12() && [(() => {
1737
- var _el$166 = _$createElement("span"), _el$167 = _$createTextNode(`
1834
+ var _c$13 = _$memo(() => !!blk());
1835
+ return () => _c$13() && [(() => {
1836
+ var _el$170 = _$createElement("span"), _el$171 = _$createTextNode(`
1738
1837
  \u2716 blocked: `);
1739
- _$insertNode(_el$166, _el$167);
1740
- _$effect((_$p) => _$setProp(_el$166, "style", {
1838
+ _$insertNode(_el$170, _el$171);
1839
+ _$effect((_$p) => _$setProp(_el$170, "style", {
1741
1840
  fg: theme().error,
1742
1841
  bold: true
1743
1842
  }, _$p));
1744
- return _el$166;
1843
+ return _el$170;
1745
1844
  })(), (() => {
1746
- var _el$169 = _$createElement("span");
1747
- _$insert(_el$169, () => blk().reason.slice(0, 140));
1748
- _$effect((_$p) => _$setProp(_el$169, "style", {
1845
+ var _el$173 = _$createElement("span");
1846
+ _$insert(_el$173, () => blk().reason.slice(0, 140));
1847
+ _$effect((_$p) => _$setProp(_el$173, "style", {
1749
1848
  fg: theme().error
1750
1849
  }, _$p));
1751
- return _el$169;
1752
- })(), (() => {
1753
- var _el$170 = _$createElement("span"), _el$171 = _$createTextNode(` \u2014 `);
1754
- _$insertNode(_el$170, _el$171);
1755
- _$insert(_el$170, () => blk().needed.slice(0, 60), null);
1756
- _$effect((_$p) => _$setProp(_el$170, "style", {
1757
- fg: theme().textMuted
1758
- }, _$p));
1759
- return _el$170;
1760
- })()];
1761
- })(), null);
1762
- _$insert(_el$122, (() => {
1763
- var _c$13 = _$memo(() => !!goal().config.artifactDir);
1764
- return () => _c$13() && [(() => {
1765
- var _el$172 = _$createElement("span"), _el$173 = _$createTextNode(`
1766
- \uD83D\uDCC1 `);
1767
- _$insertNode(_el$172, _el$173);
1768
- _$effect((_$p) => _$setProp(_el$172, "style", {
1769
- fg: theme().accent
1770
- }, _$p));
1771
- return _el$172;
1850
+ return _el$173;
1772
1851
  })(), (() => {
1773
- var _el$175 = _$createElement("span");
1774
- _$insert(_el$175, () => String(goal().config.artifactDir).replace(String(props.directory), "."));
1775
- _$effect((_$p) => _$setProp(_el$175, "style", {
1852
+ var _el$174 = _$createElement("span"), _el$175 = _$createTextNode(` \u2014 `);
1853
+ _$insertNode(_el$174, _el$175);
1854
+ _$insert(_el$174, () => blk().needed.slice(0, 60), null);
1855
+ _$effect((_$p) => _$setProp(_el$174, "style", {
1776
1856
  fg: theme().textMuted
1777
1857
  }, _$p));
1778
- return _el$175;
1858
+ return _el$174;
1779
1859
  })()];
1780
1860
  })(), null);
1781
1861
  _$insert(_el$122, (() => {
1782
- var _c$14 = _$memo(() => (goal().config.checks?.length ?? 0) > 0);
1783
- return () => _c$14() ? [(() => {
1862
+ var _c$14 = _$memo(() => !!goal().config.artifactDir);
1863
+ return () => _c$14() && [(() => {
1784
1864
  var _el$176 = _$createElement("span"), _el$177 = _$createTextNode(`
1785
- \u25A3 checks: `);
1865
+ \uD83D\uDCC1 `);
1786
1866
  _$insertNode(_el$176, _el$177);
1787
1867
  _$effect((_$p) => _$setProp(_el$176, "style", {
1788
- fg: theme().warning
1868
+ fg: theme().accent
1789
1869
  }, _$p));
1790
1870
  return _el$176;
1791
1871
  })(), (() => {
1792
1872
  var _el$179 = _$createElement("span");
1793
- _$insert(_el$179, () => goal().config.checks.join(", ").slice(0, 100));
1873
+ _$insert(_el$179, () => String(goal().config.artifactDir).replace(String(props.directory), "."));
1794
1874
  _$effect((_$p) => _$setProp(_el$179, "style", {
1795
1875
  fg: theme().textMuted
1796
1876
  }, _$p));
1797
1877
  return _el$179;
1798
- })()] : null;
1878
+ })()];
1799
1879
  })(), null);
1800
1880
  _$insert(_el$122, (() => {
1801
- var _c$15 = _$memo(() => rt()?.evaluatorRejectionCount > 0);
1802
- return () => _c$15() && [(() => {
1881
+ var _c$15 = _$memo(() => (goal().config.checks?.length ?? 0) > 0);
1882
+ return () => _c$15() ? [(() => {
1803
1883
  var _el$180 = _$createElement("span"), _el$181 = _$createTextNode(`
1804
- \u26A0 rejections: `);
1884
+ \u25A3 checks: `);
1805
1885
  _$insertNode(_el$180, _el$181);
1806
1886
  _$effect((_$p) => _$setProp(_el$180, "style", {
1807
1887
  fg: theme().warning
1808
1888
  }, _$p));
1809
1889
  return _el$180;
1810
1890
  })(), (() => {
1811
- var _el$183 = _$createElement("span"), _el$184 = _$createTextNode(` \u2014 `);
1812
- _$insertNode(_el$183, _el$184);
1813
- _$insert(_el$183, () => String(rt().evaluatorRejectionCount), _el$184);
1814
- _$insert(_el$183, () => String(rt().lastRejectionDetails || "").slice(0, 80), null);
1891
+ var _el$183 = _$createElement("span");
1892
+ _$insert(_el$183, () => goal().config.checks.join(", ").slice(0, 100));
1815
1893
  _$effect((_$p) => _$setProp(_el$183, "style", {
1816
- fg: theme().warning
1894
+ fg: theme().textMuted
1817
1895
  }, _$p));
1818
1896
  return _el$183;
1819
- })()];
1897
+ })()] : null;
1820
1898
  })(), null);
1821
1899
  _$insert(_el$122, (() => {
1822
- var _c$16 = _$memo(() => rt()?.unknownStatusCount > 0);
1900
+ var _c$16 = _$memo(() => rt()?.evaluatorRejectionCount > 0);
1823
1901
  return () => _c$16() && [(() => {
1824
- var _el$185 = _$createElement("span"), _el$186 = _$createTextNode(`
1825
- \u26A0\uFE0F unreachable: `);
1826
- _$insertNode(_el$185, _el$186);
1827
- _$effect((_$p) => _$setProp(_el$185, "style", {
1828
- fg: theme().error
1829
- }, _$p));
1830
- return _el$185;
1831
- })(), (() => {
1832
- var _el$188 = _$createElement("span"), _el$189 = _$createTextNode(`/3`);
1833
- _$insertNode(_el$188, _el$189);
1834
- _$insert(_el$188, () => String(rt().unknownStatusCount), _el$189);
1835
- _$effect((_$p) => _$setProp(_el$188, "style", {
1836
- fg: theme().error
1902
+ var _el$184 = _$createElement("span"), _el$185 = _$createTextNode(`
1903
+ \u26A0 rejections: `);
1904
+ _$insertNode(_el$184, _el$185);
1905
+ _$effect((_$p) => _$setProp(_el$184, "style", {
1906
+ fg: theme().warning
1837
1907
  }, _$p));
1838
- return _el$188;
1908
+ return _el$184;
1839
1909
  })(), (() => {
1840
- var _el$190 = _$createElement("span");
1841
- _$insertNode(_el$190, _$createTextNode(` \u2014 nudge to recover`));
1842
- _$effect((_$p) => _$setProp(_el$190, "style", {
1843
- fg: theme().textMuted
1910
+ var _el$187 = _$createElement("span"), _el$188 = _$createTextNode(` \u2014 `);
1911
+ _$insertNode(_el$187, _el$188);
1912
+ _$insert(_el$187, () => String(rt().evaluatorRejectionCount), _el$188);
1913
+ _$insert(_el$187, () => String(rt().lastRejectionDetails || "").slice(0, 80), null);
1914
+ _$effect((_$p) => _$setProp(_el$187, "style", {
1915
+ fg: theme().warning
1844
1916
  }, _$p));
1845
- return _el$190;
1917
+ return _el$187;
1846
1918
  })()];
1847
1919
  })(), null);
1848
1920
  _$insert(_el$122, (() => {
1849
- var _c$17 = _$memo(() => !!rt()?.retryAfter);
1921
+ var _c$17 = _$memo(() => rt()?.unknownStatusCount > 0);
1850
1922
  return () => _c$17() && [(() => {
1851
- var _el$192 = _$createElement("span"), _el$193 = _$createTextNode(`
1852
- \u21BB retry in: `);
1923
+ var _el$189 = _$createElement("span"), _el$190 = _$createTextNode(`
1924
+ \u26A0\uFE0F unreachable: `);
1925
+ _$insertNode(_el$189, _el$190);
1926
+ _$effect((_$p) => _$setProp(_el$189, "style", {
1927
+ fg: theme().error
1928
+ }, _$p));
1929
+ return _el$189;
1930
+ })(), (() => {
1931
+ var _el$192 = _$createElement("span"), _el$193 = _$createTextNode(`/3`);
1853
1932
  _$insertNode(_el$192, _el$193);
1933
+ _$insert(_el$192, () => String(rt().unknownStatusCount), _el$193);
1854
1934
  _$effect((_$p) => _$setProp(_el$192, "style", {
1855
- fg: theme().accent
1935
+ fg: theme().error
1856
1936
  }, _$p));
1857
1937
  return _el$192;
1858
1938
  })(), (() => {
1859
- var _el$195 = _$createElement("span");
1860
- _$insert(_el$195, () => countdownLabel(rt().retryAfter, clock()));
1861
- _$effect((_$p) => _$setProp(_el$195, "style", {
1862
- fg: theme().accent
1939
+ var _el$194 = _$createElement("span");
1940
+ _$insertNode(_el$194, _$createTextNode(` \u2014 nudge to recover`));
1941
+ _$effect((_$p) => _$setProp(_el$194, "style", {
1942
+ fg: theme().textMuted
1863
1943
  }, _$p));
1864
- return _el$195;
1944
+ return _el$194;
1865
1945
  })()];
1866
1946
  })(), null);
1867
1947
  _$insert(_el$122, (() => {
1868
- var _c$18 = _$memo(() => !!rt()?.nextRunAt);
1948
+ var _c$18 = _$memo(() => !!rt()?.retryAfter);
1869
1949
  return () => _c$18() && [(() => {
1870
1950
  var _el$196 = _$createElement("span"), _el$197 = _$createTextNode(`
1871
- \u23F0 next run: `);
1951
+ \u21BB retry in: `);
1872
1952
  _$insertNode(_el$196, _el$197);
1873
1953
  _$effect((_$p) => _$setProp(_el$196, "style", {
1874
1954
  fg: theme().accent
@@ -1876,39 +1956,58 @@ function LoopDashboard(props) {
1876
1956
  return _el$196;
1877
1957
  })(), (() => {
1878
1958
  var _el$199 = _$createElement("span");
1879
- _$insert(_el$199, () => countdownLabel(rt().nextRunAt, clock()));
1959
+ _$insert(_el$199, () => countdownLabel(rt().retryAfter, clock()));
1880
1960
  _$effect((_$p) => _$setProp(_el$199, "style", {
1881
1961
  fg: theme().accent
1882
1962
  }, _$p));
1883
1963
  return _el$199;
1884
- })(), (() => {
1885
- var _el$200 = _$createElement("span"), _el$201 = _$createTextNode(` (`), _el$202 = _$createTextNode(` runs)`);
1964
+ })()];
1965
+ })(), null);
1966
+ _$insert(_el$122, (() => {
1967
+ var _c$19 = _$memo(() => !!rt()?.nextRunAt);
1968
+ return () => _c$19() && [(() => {
1969
+ var _el$200 = _$createElement("span"), _el$201 = _$createTextNode(`
1970
+ \u23F0 next run: `);
1886
1971
  _$insertNode(_el$200, _el$201);
1887
- _$insertNode(_el$200, _el$202);
1888
- _$insert(_el$200, () => String(rt().scheduleRunCount || 0), _el$202);
1889
1972
  _$effect((_$p) => _$setProp(_el$200, "style", {
1890
- fg: theme().textMuted
1973
+ fg: theme().accent
1891
1974
  }, _$p));
1892
1975
  return _el$200;
1976
+ })(), (() => {
1977
+ var _el$203 = _$createElement("span");
1978
+ _$insert(_el$203, () => countdownLabel(rt().nextRunAt, clock()));
1979
+ _$effect((_$p) => _$setProp(_el$203, "style", {
1980
+ fg: theme().accent
1981
+ }, _$p));
1982
+ return _el$203;
1983
+ })(), (() => {
1984
+ var _el$204 = _$createElement("span"), _el$205 = _$createTextNode(` (`), _el$206 = _$createTextNode(` runs)`);
1985
+ _$insertNode(_el$204, _el$205);
1986
+ _$insertNode(_el$204, _el$206);
1987
+ _$insert(_el$204, () => String(rt().scheduleRunCount || 0), _el$206);
1988
+ _$effect((_$p) => _$setProp(_el$204, "style", {
1989
+ fg: theme().textMuted
1990
+ }, _$p));
1991
+ return _el$204;
1893
1992
  })()];
1894
1993
  })(), null);
1895
1994
  _$insert(_el$122, (() => {
1896
- var _c$19 = _$memo(() => !!rt()?.lastError);
1897
- return () => _c$19() && [(() => {
1898
- var _el$203 = _$createElement("span"), _el$204 = _$createTextNode(`
1995
+ var _c$20 = _$memo(() => !!rt()?.lastError);
1996
+ return () => _c$20() && [(() => {
1997
+ var _el$207 = _$createElement("span"), _el$208 = _$createTextNode(`
1899
1998
  \u26A0 `);
1900
- _$insertNode(_el$203, _el$204);
1901
- _$effect((_$p) => _$setProp(_el$203, "style", {
1999
+ _$insertNode(_el$207, _el$208);
2000
+ _$effect((_$p) => _$setProp(_el$207, "style", {
1902
2001
  fg: theme().error
1903
2002
  }, _$p));
1904
- return _el$203;
2003
+ return _el$207;
1905
2004
  })(), (() => {
1906
- var _el$206 = _$createElement("span");
1907
- _$insert(_el$206, () => rt().lastError.slice(0, 120));
1908
- _$effect((_$p) => _$setProp(_el$206, "style", {
2005
+ var _el$210 = _$createElement("span");
2006
+ _$insert(_el$210, () => rt().lastError.slice(0, 120));
2007
+ _$effect((_$p) => _$setProp(_el$210, "style", {
1909
2008
  fg: theme().error
1910
2009
  }, _$p));
1911
- return _el$206;
2010
+ return _el$210;
1912
2011
  })()];
1913
2012
  })(), null);
1914
2013
  _$effect((_p$) => {
@@ -1958,29 +2057,29 @@ function LoopDashboard(props) {
1958
2057
  },
1959
2058
  children: (ev) => [`
1960
2059
  `, (() => {
1961
- var _el$207 = _$createElement("span");
1962
- _$insert(_el$207, () => String(ev.type));
1963
- _$effect((_$p) => _$setProp(_el$207, "style", {
2060
+ var _el$211 = _$createElement("span");
2061
+ _$insert(_el$211, () => String(ev.type));
2062
+ _$effect((_$p) => _$setProp(_el$211, "style", {
1964
2063
  fg: eventColor(String(ev.type), theme()),
1965
2064
  bold: true
1966
2065
  }, _$p));
1967
- return _el$207;
2066
+ return _el$211;
1968
2067
  })(), (() => {
1969
- var _el$208 = _$createElement("span"), _el$209 = _$createTextNode(` `);
1970
- _$insertNode(_el$208, _el$209);
1971
- _$insert(_el$208, () => ev.goalID?.slice(0, 8), null);
1972
- _$effect((_$p) => _$setProp(_el$208, "style", {
2068
+ var _el$212 = _$createElement("span"), _el$213 = _$createTextNode(` `);
2069
+ _$insertNode(_el$212, _el$213);
2070
+ _$insert(_el$212, () => ev.goalID?.slice(0, 8), null);
2071
+ _$effect((_$p) => _$setProp(_el$212, "style", {
1973
2072
  fg: theme().textMuted
1974
2073
  }, _$p));
1975
- return _el$208;
2074
+ return _el$212;
1976
2075
  })(), _$memo(() => _$memo(() => !!ev.summary)() && (() => {
1977
- var _el$210 = _$createElement("span"), _el$211 = _$createTextNode(` \u2014 `);
1978
- _$insertNode(_el$210, _el$211);
1979
- _$insert(_el$210, () => String(ev.summary).slice(0, 60), null);
1980
- _$effect((_$p) => _$setProp(_el$210, "style", {
2076
+ var _el$214 = _$createElement("span"), _el$215 = _$createTextNode(` \u2014 `);
2077
+ _$insertNode(_el$214, _el$215);
2078
+ _$insert(_el$214, () => String(ev.summary).slice(0, 60), null);
2079
+ _$effect((_$p) => _$setProp(_el$214, "style", {
1981
2080
  fg: theme().text
1982
2081
  }, _$p));
1983
- return _el$210;
2082
+ return _el$214;
1984
2083
  })())]
1985
2084
  }), null);
1986
2085
  _$effect((_p$) => {
@@ -2034,18 +2133,6 @@ function LoopDashboard(props) {
2034
2133
  prevent(evt);
2035
2134
  return;
2036
2135
  }
2037
- if (name === "return" || name === "enter") {
2038
- prevent(evt);
2039
- debugLog("input enter -> execute");
2040
- executeCommand(commandInput());
2041
- return;
2042
- }
2043
- if (evt.ctrl && name.toLowerCase() === "n") {
2044
- prevent(evt);
2045
- debugLog("input ctrl+n -> normal");
2046
- returnToNormalMode();
2047
- return;
2048
- }
2049
2136
  });
2050
2137
  _$effect((_p$) => {
2051
2138
  var _v$4 = theme().border, _v$5 = {
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.1",
4
+ "version": "1.9.2",
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",