jaitus 0.3.2 → 0.3.3

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.2",
3
+ "version": "0.3.3",
4
4
  "description": "Jaitus — a coding agent in your terminal.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -16,7 +16,7 @@
16
16
  "node": ">=20"
17
17
  },
18
18
  "scripts": {
19
- "test": "node tests/source-sanity.js && node tests/rules.js && node tests/run.js && node tests/commands.js && node tests/login.js && node tests/session.js && node tests/openUrl.js && node tests/ink-tui.js && node tests/tui-render.js && node tests/mouse-e2e.js && node tests/environment.js && node tests/update.js && node tests/continuation.js",
19
+ "test": "node tests/source-sanity.js && node tests/rules.js && node tests/run.js && node tests/commands.js && node tests/login.js && node tests/session.js && node tests/openUrl.js && node tests/ink-tui.js && node tests/tui-render.js && node tests/mouse-e2e.js && node tests/environment.js && node tests/update.js && node tests/continuation.js && node tests/context-budget.js",
20
20
  "preview": "node scripts/tui-preview.js",
21
21
  "prepublishOnly": "npm test"
22
22
  },
package/src/api.js CHANGED
@@ -10,6 +10,10 @@ import { fromGatewayBody, fromNetwork, JaitusError } from './errors.js';
10
10
 
11
11
  export class NotSignedIn extends Error {}
12
12
 
13
+ // The context window the gateway last advertised, or null before it has been asked.
14
+ let contextWindowSeen = null;
15
+ export const advertisedContextWindow = () => contextWindowSeen;
16
+
13
17
  export function createClient() {
14
18
  const { baseUrl } = endpoints();
15
19
  if (!signedIn()) throw new NotSignedIn('Not signed in. Run `jaitus login` first.');
@@ -63,6 +67,9 @@ export function createClient() {
63
67
  const body = await res.json().catch(() => ({}));
64
68
  if (!res.ok) throw fromGatewayBody(res.status, body, { requestId: res.headers.get('x-request-id') || '' });
65
69
  const models = (body?.data || []).map((m) => m?.id).filter(Boolean);
70
+ // How much conversation fits, as the gateway computed it for this account.
71
+ const ctx = (body?.data || []).map((m) => Number(m?.context_window)).find((n) => n > 0);
72
+ if (ctx) contextWindowSeen = ctx;
66
73
  // An empty list is a service fault, not something the user can fix — and naming
67
74
  // the gateway's model map to them would be naming our internals.
68
75
  if (!models.length) throw new JaitusError({ code: 'unavailable', status: 502 });
package/src/index.js CHANGED
@@ -9,7 +9,7 @@
9
9
  import path from 'node:path';
10
10
  import fs from 'node:fs';
11
11
  import { spawn, spawnSync } from 'node:child_process';
12
- import { createClient, NotSignedIn } from './api.js';
12
+ import { createClient, NotSignedIn, advertisedContextWindow } from './api.js';
13
13
  import { fromResponse, isJaitusError } from './errors.js';
14
14
  import { signedIn } from './session.js';
15
15
  import {
@@ -300,10 +300,18 @@ function gitBranch(cwd) {
300
300
  }
301
301
  }
302
302
 
303
- // Used only to fill the context meter. The real ceiling is the model's, which the
304
- // gateway does not advertise; this is the conservative floor of what any model it
305
- // serves accepts, so the bar reads "getting full" before anything actually breaks.
306
- const CONTEXT_BUDGET = 180_000;
303
+ // How much conversation a session carries before it is compacted. The gateway works it
304
+ // out from the providers' real limits and the admin's cap, and advertises it on its
305
+ // model list; this floor is only used until that answer arrives, or if it never does.
306
+ const CONTEXT_FALLBACK = 128_000;
307
+ let CONTEXT_BUDGET = CONTEXT_FALLBACK;
308
+ async function loadContextBudget(client) {
309
+ try {
310
+ if (!advertisedContextWindow()) await client.models();
311
+ CONTEXT_BUDGET = advertisedContextWindow() || CONTEXT_FALLBACK;
312
+ } catch { /* keep the floor */ }
313
+ return CONTEXT_BUDGET;
314
+ }
307
315
 
308
316
  // Compact at this fraction of the budget. Low enough that the summarisation request
309
317
  // itself still fits comfortably, high enough that a normal session never sees it.
@@ -548,6 +556,7 @@ async function session({ flags }) {
548
556
  let [model, device] = await Promise.all([
549
557
  resolveModel(client, flags.model),
550
558
  probeDevice({}).catch(() => ({ vt: !!process.stdout.isTTY, sixel: false })),
559
+ loadContextBudget(client),
551
560
  ]);
552
561
  // Degrade on EVIDENCE, not on silence.
553
562
  //