@vincemakes/kiso-code 0.32.2 → 0.33.0

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/chat.d.ts CHANGED
@@ -183,7 +183,7 @@ export declare function parseSaferOptions(text: string): SaferOption[] | null;
183
183
  * task file has none. 50 is named here; a finding moves it, never a stray
184
184
  * edit. */
185
185
  export declare const CHECKPOINT_TURNS = 50;
186
- export declare function consumeRun(session: AgentSession, run: Run, input: LineInput, turnNo: number, faux: boolean, statusCb: ((usage: RunUsage, ctxRatio: number, costUsd?: number | null) => void) | null,
186
+ export declare function consumeRun(session: AgentSession, run: Run, input: LineInput, turnNo: number, faux: boolean, statusCb: ((usage: RunUsage, ctxRatio: number, costUsd?: number | null, tokPerSec?: number | null) => void) | null,
187
187
  /** W21: the amend words ("Yes + feedback") ride the NEXT user turn —
188
188
  * threaded from chat's submitTurn; absent in the recovery flow
189
189
  * (resume) where a dropped amend is noticed instead. */
package/dist/chat.js CHANGED
@@ -5,7 +5,7 @@
5
5
  * estimates. All bodies moved verbatim from index.ts.
6
6
  */
7
7
  import { readFileSync, statSync } from "node:fs";
8
- import { escapeTerminal, cacheHitPct, idleStatus, palette, renderEvent, renderRecap, runningStatus, toolTarget, verifyOfferView, STATUS_GLYPHS, } from "@vincemakes/kiso-tui";
8
+ import { escapeTerminal, cacheHitPct, decodeRate, idleStatus, palette, renderEvent, renderRecap, runningStatus, toolTarget, verifyOfferView, STATUS_GLYPHS, } from "@vincemakes/kiso-tui";
9
9
  import { askView, deletionRiskHint, editFileDiff, writeFileDiff } from "@vincemakes/kiso-tui";
10
10
  import { canonicalTargetPath, shellProgressPath } from "@vincemakes/kiso-tools-node";
11
11
  import { echoText } from "@vincemakes/kiso-tui-cells/render";
@@ -581,6 +581,10 @@ export async function consumeRun(session, run, input, turnNo, faux, statusCb,
581
581
  submitTurn) {
582
582
  let last;
583
583
  let usage = { in: null, out: null, cache: null, known: false };
584
+ // TPS-1: the decode clock, armed by the FIRST streamed event of a call
585
+ // and read at that call's usage event. Per CALL, not per turn — a turn
586
+ // with three model calls reports the third, and each one times itself.
587
+ let callFirstEventAt = null;
584
588
  let prevTotal = null;
585
589
  let missed = null;
586
590
  // v3 §02: the recap line derives ENTIRELY from the local event stream
@@ -644,6 +648,7 @@ submitTurn) {
644
648
  body.userLine(echoText(ev.content));
645
649
  break;
646
650
  case "thinking":
651
+ callFirstEventAt ??= Date.now(); // TPS-1: reasoning IS decoding
647
652
  body.thinkingAppend(ev.text);
648
653
  break;
649
654
  case "tool_call_end":
@@ -694,6 +699,7 @@ submitTurn) {
694
699
  break;
695
700
  }
696
701
  case "text_delta":
702
+ callFirstEventAt ??= Date.now(); // TPS-1
697
703
  body.textAppend(ev.text);
698
704
  break;
699
705
  case "text_end":
@@ -715,7 +721,12 @@ submitTurn) {
715
721
  missed = delta.missed;
716
722
  // TUI2-R1 (E): the request's canonical cost rides the same
717
723
  // callback the usage does — one settled request, one addition.
718
- statusCb?.(usage, displayCtxRatio(session), delta.costUsd);
724
+ // TPS-1: the rate of the call that just settled. The clock is
725
+ // cleared here rather than at the turn boundary, so a turn's
726
+ // second call is timed from ITS own first event.
727
+ const tokPerSec = callFirstEventAt === null ? null : decodeRate(ev.outputTokens, Date.now() - callFirstEventAt);
728
+ callFirstEventAt = null;
729
+ statusCb?.(usage, displayCtxRatio(session), delta.costUsd, tokPerSec);
719
730
  break;
720
731
  }
721
732
  case "uncertain_pending":
@@ -965,6 +976,7 @@ export async function chat(session, faux, input, autoCompact, nav) {
965
976
  // rotates every 200ms; the idle state returns after the run.
966
977
  runStart = Date.now();
967
978
  runUsage = { in: null, out: null, cache: null, known: false };
979
+ lastTokPerSec = null; // TPS-1: nothing has settled in THIS turn yet
968
980
  const stopSpinner = startStatusSpinner((g) => {
969
981
  runGlyph = g;
970
982
  paintRunning();
@@ -1156,11 +1168,25 @@ export async function chat(session, faux, input, autoCompact, nav) {
1156
1168
  let spentUsd = null;
1157
1169
  let runGlyph = "▖";
1158
1170
  let runStart = Date.now();
1171
+ // TPS-1: the decode rate of the last SETTLED call. One variable serves
1172
+ // both rows because they want the same number at different moments;
1173
+ // only the reset points differ, and they are all on the ENTRY side —
1174
+ // cleared when a turn starts (so the running row never shows the
1175
+ // previous turn's figure while a new one is in flight) and when the
1176
+ // binding changes (DF-0311-F1: a model that has not run has no
1177
+ // measurement), never at a turn's END, so the idle row can carry the
1178
+ // last call of the last turn.
1179
+ let lastTokPerSec = null;
1180
+ // DF-0330-F1: the terminal's own width, read at paint time so a resize
1181
+ // is honoured. winsize reports 0 columns in some hosts and 0 is not
1182
+ // nullish, so the guard is `> 0` rather than `??` (the same shape the
1183
+ // compositor's own width call uses).
1184
+ const rowWidth = () => (process.stdout.columns > 0 ? process.stdout.columns : 80);
1159
1185
  // KC2 §5: the STATE (the glyph, the run's start, the usage, the dock)
1160
1186
  // stays here; the ROW's text is the tui's status formatter.
1161
1187
  const paintRunning = () => {
1162
1188
  if (dock.active)
1163
- dock.setStatus(runningStatus(runGlyph, runStart, runUsage.out, displayCtxRatio(session)));
1189
+ dock.setStatus(runningStatus(runGlyph, runStart, runUsage.out, displayCtxRatio(session), lastTokPerSec));
1164
1190
  };
1165
1191
  // W19: under plan the idle row makes the posture unmistakable — the W4
1166
1192
  // parentheses idiom names the read-only constraint. The tier is the
@@ -1174,7 +1200,12 @@ export async function chat(session, faux, input, autoCompact, nav) {
1174
1200
  dock.setStatus(idleStatus(getMode() === "plan" ? "plan (read-only)" : getMode(), statusModelLabel(session), displayCtxRatio(session), {
1175
1201
  cacheHitPct: cacheHitPct(runUsage),
1176
1202
  costUsd: spentUsd,
1177
- }));
1203
+ tokPerSec: lastTokPerSec,
1204
+ },
1205
+ // DF-0330-F1: the row's budget. Without it the row is composed
1206
+ // blind and invariant ① cuts whatever sits last — which is how
1207
+ // a measured rate went missing at 100 columns.
1208
+ rowWidth()));
1178
1209
  };
1179
1210
  // TUI2-R2 ⑥ — the BOOT status line. The row is the product's one
1180
1211
  // persistent claim about itself (the tier, how to change it, the model,
@@ -1198,11 +1229,16 @@ export async function chat(session, faux, input, autoCompact, nav) {
1198
1229
  // binding's, and the row does not render it anyway.
1199
1230
  const modelSwitched = () => {
1200
1231
  runUsage = { in: null, out: null, cache: null, known: false };
1232
+ lastTokPerSec = null; // TPS-1: the rate belonged to the old binding
1201
1233
  paintIdle();
1202
1234
  };
1203
- const statusCb = (u, ctx, costUsd) => {
1235
+ const statusCb = (u, ctx, costUsd, tokPerSec) => {
1204
1236
  runUsage = u;
1205
1237
  addCost(costUsd ?? null);
1238
+ // TPS-1: a call that could not be measured leaves the row as it was
1239
+ // rather than blanking a figure the previous call earned.
1240
+ if (tokPerSec != null)
1241
+ lastTokPerSec = tokPerSec;
1206
1242
  paintRunning();
1207
1243
  };
1208
1244
  // TUI2-R1 (E): the canonical cost of one settled request, added to the
@@ -1357,6 +1393,7 @@ export async function chat(session, faux, input, autoCompact, nav) {
1357
1393
  // spent, reported as seconds it did.
1358
1394
  runStart = Date.now();
1359
1395
  runUsage = { in: null, out: null, cache: null, known: false };
1396
+ lastTokPerSec = null; // TPS-1: nothing has settled in THIS turn yet
1360
1397
  const last = await consumeRun(session, recoveryRun, input, turnNo, faux, statusCb, submitTurn);
1361
1398
  currentRun = null;
1362
1399
  failOnFauxExhaustion(last, faux, input);
package/dist/index.js CHANGED
@@ -834,7 +834,9 @@ async function pickSession(agent, input) {
834
834
  function paintBootStatus(session) {
835
835
  if (!dock.active)
836
836
  return;
837
- dock.setStatus(idleStatus(getMode() === "plan" ? "plan (read-only)" : getMode(), statusModelLabel(session), displayCtxRatio(session)));
837
+ // DF-0330-F1: the BOOT row gets the budget too — it is the same row, and
838
+ // the opening screen is where a long model id is first seen.
839
+ dock.setStatus(idleStatus(getMode() === "plan" ? "plan (read-only)" : getMode(), statusModelLabel(session), displayCtxRatio(session), undefined, process.stdout.columns > 0 ? process.stdout.columns : 80));
838
840
  }
839
841
  /** PH-1a (finding PH-F12): a usage error raised from inside the TUI —
840
842
  * main's finally still runs (dock teardown, agent close, temp cleanup)
package/dist/resume.js CHANGED
@@ -33,8 +33,11 @@ export async function resume(session, prompt, faux, input) {
33
33
  // the recovery flow prints the BARE mode (chat spells plan's posture) —
34
34
  // the extraction keeps that difference, it was not asked to settle it.
35
35
  const paintIdle = () => {
36
+ // DF-0330-F1: the recovery flow paints the same row, so it gets the
37
+ // same budget. It has no meter, so nothing here can be dropped that
38
+ // was not already at risk of being cut.
36
39
  if (dock.active)
37
- dock.setStatus(idleStatus(getMode(), agentModel, estimateCtxRatio(session)));
40
+ dock.setStatus(idleStatus(getMode(), agentModel, estimateCtxRatio(session), undefined, process.stdout.columns > 0 ? process.stdout.columns : 80));
38
41
  };
39
42
  const withRun = async (run) => {
40
43
  currentRun = run;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vincemakes/kiso-code",
3
- "version": "0.32.2",
3
+ "version": "0.33.0",
4
4
  "description": "kiso-code — the coding agent on kiso, the durable runtime for AI agents: kiso chat / kiso resume / kiso sessions.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -18,20 +18,20 @@
18
18
  "test": "vitest run"
19
19
  },
20
20
  "dependencies": {
21
- "@vincemakes/kiso-ask-ext": "0.32.2",
22
- "@vincemakes/kiso-core": "0.32.2",
23
- "@vincemakes/kiso-evals": "0.32.2",
24
- "@vincemakes/kiso-mcp-ext": "0.32.2",
25
- "@vincemakes/kiso-provider-anthropic": "0.32.2",
26
- "@vincemakes/kiso-provider-openai": "0.32.2",
27
- "@vincemakes/kiso-provider-openai-responses": "0.32.2",
28
- "@vincemakes/kiso-runtime": "0.32.2",
29
- "@vincemakes/kiso-skills-ext": "0.32.2",
30
- "@vincemakes/kiso-subagent-ext": "0.32.2",
31
- "@vincemakes/kiso-task-ext": "0.32.2",
32
- "@vincemakes/kiso-tools-node": "0.32.2",
33
- "@vincemakes/kiso-tui": "0.32.2",
34
- "@vincemakes/kiso-tui-cells": "0.32.2"
21
+ "@vincemakes/kiso-ask-ext": "0.33.0",
22
+ "@vincemakes/kiso-core": "0.33.0",
23
+ "@vincemakes/kiso-evals": "0.33.0",
24
+ "@vincemakes/kiso-mcp-ext": "0.33.0",
25
+ "@vincemakes/kiso-provider-anthropic": "0.33.0",
26
+ "@vincemakes/kiso-provider-openai": "0.33.0",
27
+ "@vincemakes/kiso-provider-openai-responses": "0.33.0",
28
+ "@vincemakes/kiso-runtime": "0.33.0",
29
+ "@vincemakes/kiso-skills-ext": "0.33.0",
30
+ "@vincemakes/kiso-subagent-ext": "0.33.0",
31
+ "@vincemakes/kiso-task-ext": "0.33.0",
32
+ "@vincemakes/kiso-tools-node": "0.33.0",
33
+ "@vincemakes/kiso-tui": "0.33.0",
34
+ "@vincemakes/kiso-tui-cells": "0.33.0"
35
35
  },
36
36
  "devDependencies": {
37
37
  "@types/node": "^26.1.2",