jaitus 0.3.25 → 0.3.26

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jaitus",
3
- "version": "0.3.25",
3
+ "version": "0.3.26",
4
4
  "description": "Jaitus — a coding agent in your terminal.",
5
5
  "type": "module",
6
6
  "bin": {
package/src/adSlots.js CHANGED
@@ -51,12 +51,18 @@ export function createAdSlots({ source, isVisible, onChange, now = () => Date.no
51
51
 
52
52
  function tick() {
53
53
  const t = now();
54
- const elapsed = Math.max(0, t - lastTick);
54
+ const prev = lastTick;
55
55
  lastTick = t;
56
56
  if (!current) { stopTimer(); return; }
57
+ // Only time since THIS line went up counts: the timer may have been running for an
58
+ // earlier one, and crediting the whole tick reported views after ~0.8 s on screen.
59
+ const elapsed = Math.max(0, t - Math.max(prev, current.countFrom ?? current.at));
57
60
  if (!current.reported) {
58
61
  if (isVisible(current)) {
59
- current.visibleMs += elapsed;
62
+ // Uncovered somewhere since the last tick, but when exactly is unknowable from a
63
+ // poll: count from this tick on, never from before it (the advertiser's side).
64
+ if (current.countFrom === Infinity) current.countFrom = t;
65
+ else current.visibleMs += elapsed;
60
66
  if (current.visibleMs >= VIEWABLE_MS) {
61
67
  current.reported = true;
62
68
  const shown = Math.round(current.visibleMs);
@@ -64,6 +70,7 @@ export function createAdSlots({ source, isVisible, onChange, now = () => Date.no
64
70
  }
65
71
  } else {
66
72
  current.visibleMs = 0; // continuous, or it does not count
73
+ current.countFrom = Infinity; // and restarts at the first tick that sees it again
67
74
  }
68
75
  }
69
76
  if (current.placement === 'thinking' && wanted === 'thinking' && t - current.at >= ROTATE_THINKING_MS) {
@@ -102,6 +109,9 @@ export function createAdSlots({ source, isVisible, onChange, now = () => Date.no
102
109
  if (current?.placement === wanted) return;
103
110
  if (wanted === 'idle' && lastIdle && now() - lastIdle.at < IDLE_REUSE_MS) {
104
111
  // Back to waiting moments after the last wait: the same line, not a new serve.
112
+ // Its clock starts again from now — it was off screen in between.
113
+ if (!lastIdle.slot.reported) { lastIdle.slot.visibleMs = 0; }
114
+ lastIdle.slot.countFrom = now();
105
115
  set(lastIdle.slot);
106
116
  ensureTimer();
107
117
  return;
@@ -134,7 +144,10 @@ export function adSourceFor(client, { timeoutMs = 2500 } = {}) {
134
144
  return r.ok ? (r.body?.data?.ad || null) : null;
135
145
  },
136
146
  async seen(token, shownMs) {
137
- await call('/ads/cli/seen', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ token, shownMs }) });
147
+ // No content-type here: client.api already sends one, and a second made the header
148
+ // "application/json, application/json", which the server's JSON parser does not
149
+ // recognise — so in 0.3.25 every report arrived with no body and was refused.
150
+ await call('/ads/cli/seen', { method: 'POST', body: JSON.stringify({ token, shownMs }) });
138
151
  },
139
152
  };
140
153
  }
package/src/tui-ink.js CHANGED
@@ -1493,6 +1493,7 @@ export function createTui({ slashCommands = [], fileProvider = null, meta = {},
1493
1493
  },
1494
1494
  }) : null;
1495
1495
 
1496
+ let adBusyTimer = null;
1496
1497
  let inkInstance = null;
1497
1498
  let update = () => {};
1498
1499
  let pending = null;
@@ -2893,6 +2894,7 @@ export function createTui({ slashCommands = [], fileProvider = null, meta = {},
2893
2894
  stop() {
2894
2895
  state.active = false;
2895
2896
  exited = true;
2897
+ clearTimeout(adBusyTimer);
2896
2898
  adSlots?.stop();
2897
2899
  if (pending) { clearTimeout(pending); pending = null; }
2898
2900
  // Before unmount: the terminal keeps reporting after the process is gone
@@ -3028,7 +3030,19 @@ export function createTui({ slashCommands = [], fileProvider = null, meta = {},
3028
3030
  state.busy = !!value;
3029
3031
  if (value) { state.turnStartedAt = Date.now(); state.turnTokens = 0; state.turnCredits = 0; }
3030
3032
  else { state.status = ''; state.turnStartedAt = null; }
3031
- adSlots?.want(value ? 'thinking' : 'idle');
3033
+ if (adSlots) {
3034
+ clearTimeout(adBusyTimer);
3035
+ if (value) {
3036
+ // Only work that lasts gets a "thinking" line: an instant command (/help,
3037
+ // /usage) would otherwise fetch one just to throw it away.
3038
+ adBusyTimer = setTimeout(() => { if (state.busy && !state.panel?.adSlot) adSlots.want('thinking'); }, 800);
3039
+ adBusyTimer.unref?.();
3040
+ } else {
3041
+ // /usage runs as a command: its panel is still open when the command ends, and
3042
+ // it keeps its own slot until it is closed.
3043
+ adSlots.want(state.panel?.adSlot ? 'usage' : 'idle');
3044
+ }
3045
+ }
3032
3046
  notifyNow();
3033
3047
  },
3034
3048
  setStatus(value) { state.status = plain(value); notify(); },