@agenit/cli 2.0.0 → 2.1.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/config/flow.toml CHANGED
@@ -4,8 +4,17 @@
4
4
  # /opt/homebrew/bin on macOS, /usr/local/bin on Linux, %APPDATA%\npm on
5
5
  # Windows). Set an absolute path only if you need to pin a specific install.
6
6
  binary = "gemini"
7
- # Model override — leave commented out to use Gemini CLI's own default
8
- # model = "gemini-2.5-pro"
7
+ # Model override — pinned to 2.5-pro to avoid 2.5-flash's RESOURCE_EXHAUSTED on the free OAuth pool.
8
+ model = "gemini-2.5-pro"
9
+ # Fallback model — used for one transparent retry when the primary model
10
+ # returns HTTP 429 / "No capacity available". A one-line warning is
11
+ # printed to stderr when this fires. Unset or equal-to-`model` disables.
12
+ fallback_model = "gemini-2.5-flash"
13
+ # Per-request wall-clock timeout in seconds. Caps gemini-cli's internal
14
+ # retry-on-429 hangs (which can otherwise run 3+ minutes silently). On
15
+ # timeout the spawned process is killed and the fallback retry path
16
+ # triggers. Default 90s. Set to 0 to disable.
17
+ request_timeout_secs = 90
9
18
  # Working dir handed to the spawned Gemini-CLI as cwd. Resolved against the
10
19
  # directory the user invoked `agenit` from (NOT the directory holding this
11
20
  # file). Leave unset (or "." ) to follow the user's project — every
package/control/app.mjs CHANGED
@@ -83,7 +83,7 @@ function PanelPlaceholder({ panel }) {
83
83
  * mission can keep an eye on phase / squad / job state without
84
84
  * switching screens.
85
85
  */
86
- function Stage({ active, state, events }) {
86
+ function Stage({ active, state, events, tuiEvents }) {
87
87
  if (active === "chat") {
88
88
  return html`
89
89
  <section class="shell-stage" aria-label="Chat">
@@ -99,10 +99,10 @@ function Stage({ active, state, events }) {
99
99
  main = html`<${VModel} state=${state} events=${events} />`;
100
100
  break;
101
101
  case "squad":
102
- main = html`<${Squad} state=${state} />`;
102
+ main = html`<${Squad} state=${state} tuiEvents=${tuiEvents} />`;
103
103
  break;
104
104
  case "goal":
105
- main = html`<${Goal} state=${state} events=${events} />`;
105
+ main = html`<${Goal} state=${state} events=${events} tuiEvents=${tuiEvents} />`;
106
106
  break;
107
107
  case "design":
108
108
  main = html`<${Design} />`;
@@ -182,7 +182,7 @@ function App() {
182
182
  model=${"—"}
183
183
  />
184
184
  <${LeftNav} active=${active} onSelect=${setActive} />
185
- <${Stage} active=${active} state=${state} events=${snap.events} />
185
+ <${Stage} active=${active} state=${state} events=${snap.events} tuiEvents=${snap.tuiEvents} />
186
186
  <${Footer} events=${snap.events} connection=${overallConn} />
187
187
  <${CommandPalette}
188
188
  open=${paletteOpen}
@@ -85,7 +85,60 @@ function dispatch(input) {
85
85
  return api("/api/command", { method: "POST", body: { input } });
86
86
  }
87
87
 
88
- export function Goal({ state, events }) {
88
+ function TickTimeline({ tuiEvents, goalId }) {
89
+ const ticks = (tuiEvents || []).filter(
90
+ (e) => typeof e.type === "string" && e.type.startsWith("goal.tick.") &&
91
+ (!goalId || e.goalId === goalId),
92
+ );
93
+ if (ticks.length === 0) {
94
+ return html`<div class="placeholder muted">no tick events yet</div>`;
95
+ }
96
+ // Group by turn so each row shows start → advise? → test* → end.
97
+ const byTurn = new Map();
98
+ for (const ev of ticks) {
99
+ const k = ev.turn;
100
+ const arr = byTurn.get(k) || [];
101
+ arr.push(ev);
102
+ byTurn.set(k, arr);
103
+ }
104
+ const turns = [...byTurn.entries()].sort((a, b) => b[0] - a[0]).slice(0, 20);
105
+ return html`
106
+ <ul class="tick-timeline">
107
+ ${turns.map(([turn, evs]) => {
108
+ const end = evs.find((e) => e.type === "goal.tick.end");
109
+ const tests = evs.filter((e) => e.type === "goal.tick.test");
110
+ return html`
111
+ <li class="tick-row" key=${turn}>
112
+ <span class="tick-turn">#${turn}</span>
113
+ ${evs.find((e) => e.type === "goal.tick.start")
114
+ ? html`<span class="chip">start</span>`
115
+ : ""}
116
+ ${evs.some((e) => e.type === "goal.tick.advise")
117
+ ? html`<span class="chip chip-advise">advise</span>`
118
+ : ""}
119
+ ${tests.map(
120
+ (t) => html`
121
+ <span
122
+ class=${"chip " + (t.pass ? "chip-pass" : "chip-fail")}
123
+ title=${(t.step?.name || "") + " " + (t.step?.detail || "")}
124
+ >
125
+ ${t.step?.name || (t.pass ? "test" : "fail")}
126
+ </span>
127
+ `,
128
+ )}
129
+ ${end
130
+ ? html`<span class=${"chip chip-" + (end.outcome || "end")}>
131
+ ${end.outcome || "end"}
132
+ </span>`
133
+ : ""}
134
+ </li>
135
+ `;
136
+ })}
137
+ </ul>
138
+ `;
139
+ }
140
+
141
+ export function Goal({ state, events, tuiEvents }) {
89
142
  const goal = findGoalJob(state);
90
143
  const [objective, setObjective] = useState("");
91
144
  const [pending, setPending] = useState(false);
@@ -210,6 +263,11 @@ export function Goal({ state, events }) {
210
263
  <${CostStrip} events=${events} />
211
264
  </div>
212
265
 
266
+ <div class="panel">
267
+ <h2 class="panel-title">Live ticks</h2>
268
+ <${TickTimeline} tuiEvents=${tuiEvents} goalId=${goal?.id ? goal.id.replace(/^goal:/, "") : null} />
269
+ </div>
270
+
213
271
  <div class="panel">
214
272
  <h2 class="panel-title">Goal log</h2>
215
273
  <pre class="goal-log">${logText || "(empty)"}</pre>
@@ -59,7 +59,34 @@ function HelperRail({ helpers }) {
59
59
  `;
60
60
  }
61
61
 
62
- export function Squad({ state }) {
62
+ function HelperTimeline({ tuiEvents }) {
63
+ const rows = (tuiEvents || []).filter(
64
+ (e) => typeof e.type === "string" && e.type.startsWith("squad.helper."),
65
+ );
66
+ if (rows.length === 0) {
67
+ return html`<div class="placeholder muted">no helper events yet</div>`;
68
+ }
69
+ const recent = rows.slice(0, 40);
70
+ return html`
71
+ <ul class="helper-timeline">
72
+ ${recent.map(
73
+ (ev, i) => html`
74
+ <li class=${"helper-event helper-evt-" + ev.type.replace(/\./g, "-")} key=${i}>
75
+ <span class="muted">${ev.type.replace("squad.helper.", "")}</span>
76
+ <span>${ev.name || "?"}</span>
77
+ ${ev.phase !== undefined ? html`<span class="chip">phase ${ev.phase}</span>` : ""}
78
+ ${ev.status ? html`<span class=${"chip chip-" + ev.status}>${ev.status}</span>` : ""}
79
+ ${ev.elapsedMs !== undefined
80
+ ? html`<span class="muted">${Math.round(ev.elapsedMs / 1000)}s</span>`
81
+ : ""}
82
+ </li>
83
+ `,
84
+ )}
85
+ </ul>
86
+ `;
87
+ }
88
+
89
+ export function Squad({ state, tuiEvents }) {
63
90
  const squad = state?.squad;
64
91
  const [template, setTemplate] = useState("implement");
65
92
  const [topology, setTopology] = useState("");
@@ -160,6 +187,10 @@ ${lastResult.error || lastResult.text || ""}</pre>`
160
187
  </h2>
161
188
  <${HelperRail} helpers=${squad?.helpers} />
162
189
  </div>
190
+ <div class="panel">
191
+ <h2 class="panel-title">Helper timeline</h2>
192
+ <${HelperTimeline} tuiEvents=${tuiEvents} />
193
+ </div>
163
194
  </section>
164
195
  `;
165
196
  }
package/control/store.mjs CHANGED
@@ -12,12 +12,17 @@
12
12
  import { wsUrl, apiJson } from "./api.mjs";
13
13
 
14
14
  const EVENT_RING = 500;
15
+ const TUI_EVENT_RING = 200;
15
16
 
16
17
  const listeners = new Set();
17
18
  let snapshot = {
18
19
  state: null,
19
20
  activeProject: null,
20
21
  events: [], // newest first
22
+ // Structured tuiBus events forwarded by the orchestrator's
23
+ // wireBus.onEvent — squad.helper.*, goal.tick.*, routing.downshift.
24
+ // Newest first. Panels filter by `type` prefix.
25
+ tuiEvents: [],
21
26
  connection: { state: "offline", events: "offline", chat: "idle" },
22
27
  };
23
28
 
@@ -54,6 +59,12 @@ function pushEvent(ev) {
54
59
  emit({ events: next });
55
60
  }
56
61
 
62
+ function pushTuiEvent(ev) {
63
+ const next = [{ at: Date.now(), ...ev }].concat(snapshot.tuiEvents);
64
+ if (next.length > TUI_EVENT_RING) next.length = TUI_EVENT_RING;
65
+ emit({ tuiEvents: next });
66
+ }
67
+
57
68
  function setBackfill(events) {
58
69
  // backfill arrives oldest-first from the server ring; flip so the
59
70
  // store's invariant of newest-first stays true.
@@ -140,6 +151,7 @@ export function startStore() {
140
151
  onClose: () => setConnection("state", "offline"),
141
152
  onMessage: (msg) => {
142
153
  if (msg.type === "state") emit({ state: msg.state });
154
+ else if (msg.type === "event") pushTuiEvent(msg.event);
143
155
  },
144
156
  });
145
157
 
package/control/style.css CHANGED
@@ -1969,3 +1969,47 @@ button.trace-cell {
1969
1969
  border-radius: 4px;
1970
1970
  }
1971
1971
  ::-webkit-scrollbar-thumb:hover { background: var(--cyan-dim); }
1972
+
1973
+ /* ── tick + helper timelines (Opp 4 panels) ───────────────────────────── */
1974
+ .tick-timeline,
1975
+ .helper-timeline {
1976
+ list-style: none;
1977
+ margin: 0;
1978
+ padding: 0;
1979
+ display: flex;
1980
+ flex-direction: column;
1981
+ gap: 4px;
1982
+ max-height: 320px;
1983
+ overflow-y: auto;
1984
+ }
1985
+ .tick-row,
1986
+ .helper-event {
1987
+ display: flex;
1988
+ flex-wrap: wrap;
1989
+ gap: 6px;
1990
+ align-items: center;
1991
+ padding: 4px 6px;
1992
+ border-radius: 4px;
1993
+ background: var(--bg-elev);
1994
+ }
1995
+ .tick-turn {
1996
+ min-width: 3em;
1997
+ font-variant-numeric: tabular-nums;
1998
+ color: var(--fg-dim);
1999
+ }
2000
+ .chip {
2001
+ display: inline-flex;
2002
+ align-items: center;
2003
+ gap: 4px;
2004
+ padding: 1px 6px;
2005
+ border-radius: 999px;
2006
+ font-size: 0.75rem;
2007
+ background: var(--bg);
2008
+ border: 1px solid var(--border);
2009
+ }
2010
+ .chip-pass, .chip-complete, .chip-success { color: var(--green); border-color: var(--green); }
2011
+ .chip-fail, .chip-error { color: var(--red); border-color: var(--red); }
2012
+ .chip-advise, .chip-continue { color: var(--cyan); border-color: var(--cyan); }
2013
+ .chip-blocked { color: var(--yellow); border-color: var(--yellow); }
2014
+ .helper-evt-squad-helper-start { border-left: 2px solid var(--cyan); padding-left: 8px; }
2015
+ .helper-evt-squad-helper-end { border-left: 2px solid var(--green); padding-left: 8px; }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agenit/cli",
3
- "version": "2.0.0",
3
+ "version": "2.1.1",
4
4
  "description": "agenIT — Profile-driven AI dev co-pilot CLI (TypeScript / Ink). ASPICE-aligned V-Model workflow on top of Gemini CLI for embedded engineering teams.",
5
5
  "keywords": [
6
6
  "agenit",