@matterailab/orbcode 0.1.7 → 0.1.10

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/bin/orbcode.js CHANGED
@@ -1,2 +1,5 @@
1
1
  #!/usr/bin/env node
2
+ // Override Node's default process title so terminals (iTerm2 "current job name",
3
+ // VSCode terminal status, etc.) don't append " (node)" next to our own title.
4
+ process.title = "orbcode"
2
5
  import "../dist/index.js"
@@ -61,6 +61,12 @@ export class Agent {
61
61
  sessionApproveCommands = false;
62
62
  abortController;
63
63
  totalCost = 0;
64
+ /**
65
+ * Latest context window usage (input + output tokens from the most recent
66
+ * `usage` chunk). Mirrors `totalCost` so the status bar can show it across
67
+ * /resume, /clear and process restarts.
68
+ */
69
+ contextTokens = 0;
64
70
  title = "";
65
71
  createdAt = new Date().toISOString();
66
72
  taskId;
@@ -71,6 +77,7 @@ export class Agent {
71
77
  this.messages = options.resume.messages;
72
78
  this.todos = options.resume.todos;
73
79
  this.totalCost = options.resume.totalCost;
80
+ this.contextTokens = options.resume.contextTokens;
74
81
  this.title = options.resume.title;
75
82
  this.createdAt = options.resume.createdAt;
76
83
  this.firstMessageSent = this.messages.length > 0;
@@ -100,6 +107,15 @@ export class Agent {
100
107
  get modelId() {
101
108
  return this.options.modelId;
102
109
  }
110
+ /**
111
+ * Latest context window usage in tokens. Falls back to 0 for fresh
112
+ * sessions; equals the persisted value after a resume so the TUI can
113
+ * repopulate its `contextTokens` state without waiting for the next
114
+ * streaming chunk.
115
+ */
116
+ get lastContextTokens() {
117
+ return this.contextTokens;
118
+ }
103
119
  /** Replace the prompt-derived title with the backend-generated one. */
104
120
  setTitle(title) {
105
121
  this.title = title;
@@ -118,6 +134,7 @@ export class Agent {
118
134
  this.todos = "";
119
135
  this.firstMessageSent = false;
120
136
  this.totalCost = 0;
137
+ this.contextTokens = 0;
121
138
  this.title = "";
122
139
  }
123
140
  /** Write the current conversation to the sessions directory. */
@@ -133,6 +150,7 @@ export class Agent {
133
150
  createdAt: this.createdAt,
134
151
  updatedAt: new Date().toISOString(),
135
152
  totalCost: this.totalCost,
153
+ contextTokens: this.contextTokens,
136
154
  todos: this.todos,
137
155
  messages: this.messages,
138
156
  });
@@ -251,6 +269,7 @@ User time zone: ${timeZone}, UTC${timeZoneOffsetStr}`;
251
269
  }
252
270
  else if (chunk.type === "usage") {
253
271
  this.totalCost += chunk.totalCost ?? 0;
272
+ this.contextTokens = (chunk.inputTokens ?? 0) + (chunk.outputTokens ?? 0);
254
273
  onEvent({
255
274
  type: "usage",
256
275
  inputTokens: chunk.inputTokens,
@@ -330,6 +349,7 @@ User time zone: ${timeZone}, UTC${timeZoneOffsetStr}`;
330
349
  break;
331
350
  case "usage":
332
351
  this.totalCost += chunk.totalCost ?? 0;
352
+ this.contextTokens = (chunk.inputTokens ?? 0) + (chunk.outputTokens ?? 0);
333
353
  onEvent({
334
354
  type: "usage",
335
355
  inputTokens: chunk.inputTokens,
package/dist/index.js CHANGED
@@ -68,6 +68,10 @@ function takeFlagValue(args, name) {
68
68
  return value;
69
69
  }
70
70
  async function main() {
71
+ // Override Node's default process title so terminals (iTerm2 "current job
72
+ // name", VSCode terminal status, etc.) don't append " (node)" next to our
73
+ // own title. The bundled bin/orbcode.js also does this for the npm case.
74
+ process.title = "orbcode";
71
75
  const args = process.argv.slice(2);
72
76
  if (args.includes("--version") || args.includes("-v")) {
73
77
  console.log(VERSION);
package/dist/ui/App.js CHANGED
@@ -220,7 +220,7 @@ export function App({ initialView, initialPrompt, initialSession, updateCheck, }
220
220
  void fetchTaskTitle(agent.taskId, token).then((title) => {
221
221
  if (title && agentRef.current?.taskId === agent.taskId) {
222
222
  setSessionTitle(title);
223
- setTerminalTitle(`${title} (orbcode)`);
223
+ setTerminalTitle(title);
224
224
  agent.setTitle(title);
225
225
  }
226
226
  });
@@ -363,12 +363,16 @@ export function App({ initialView, initialPrompt, initialSession, updateCheck, }
363
363
  resetTranscript();
364
364
  setTasks(session.todos ?? "");
365
365
  setTotalCost(session.totalCost ?? 0);
366
+ // Repopulate the status bar immediately. Without this the context
367
+ // number only appears once the next streaming `usage` chunk arrives,
368
+ // which can be after several seconds of "ctx 0".
369
+ setContextTokens(session.contextTokens ?? 0);
366
370
  if (session.title) {
367
371
  // The stored title is already the backend one (or the prompt
368
372
  // fallback); don't re-fetch for this task.
369
373
  titleTaskRef.current = session.id;
370
374
  setSessionTitle(session.title);
371
- setTerminalTitle(`${session.title} (orbcode)`);
375
+ setTerminalTitle(session.title);
372
376
  }
373
377
  // Replay the conversation into the transcript.
374
378
  for (const message of session.messages) {
@@ -33,25 +33,18 @@ function truncate(text, max) {
33
33
  return text.length > max ? text.slice(0, max - 1) + "…" : text;
34
34
  }
35
35
  /**
36
- * Picks the most constrained window (highest percentage used) and returns a
37
- * compact string like "5hr 12% · resets in 3h 15m".
36
+ * Formats the 5hr window as "5hr limit XX% · resets <relative>".
37
+ * Returns null when tiered usage is unavailable so the line can be hidden.
38
38
  */
39
- function mostConstrainedSummary(tu) {
40
- const windows = [
41
- ["5hr", tu.fiveHour.percentage, tu.fiveHour.resetsAt],
42
- ["wk", tu.weekly.percentage, tu.weekly.resetsAt],
43
- ["mo", tu.monthly.percentage, tu.monthly.resetsAt],
44
- ];
45
- const [label, pct, resetsAt] = windows.reduce((best, cur) => cur[1] >= best[1] ? cur : best);
46
- return `${label} ${Math.round(pct)}% · resets ${formatRelativeTime(resetsAt)}`;
39
+ function fiveHourSummary(tu) {
40
+ if (!tu?.fiveHour)
41
+ return null;
42
+ const { percentage, resetsAt } = tu.fiveHour;
43
+ return `5hr limit ${Math.round(percentage)}% · resets ${formatRelativeTime(resetsAt)}`;
47
44
  }
48
- export function StatusBar({ modelId, contextTokens, totalCost, state, approvalMode, busy, title, plan, usagePercentage, tieredUsage, }) {
45
+ export function StatusBar({ modelId, contextTokens, totalCost: _totalCost, state, approvalMode, busy, title, plan: _plan, usagePercentage: _usagePercentage, tieredUsage, }) {
49
46
  const model = getModel(modelId);
50
47
  const contextPct = Math.min(100, Math.round((contextTokens / model.contextWindow) * 100));
51
- const constrainedLine = tieredUsage
52
- ? mostConstrainedSummary(tieredUsage)
53
- : typeof usagePercentage === "number"
54
- ? `${Math.round(usagePercentage)}% used`
55
- : null;
56
- return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { justifyContent: "space-between", children: [_jsxs(Text, { dimColor: true, children: [_jsx(Text, { color: approvalMode === "ask" ? undefined : COLORS.warning, children: MODE_LABELS[approvalMode] }), " (shift+tab to cycle)", busy && " · esc to interrupt", state ? _jsxs(Text, { color: COLORS.thinking, children: [" \u00B7 ", state] }) : null] }), _jsxs(Text, { dimColor: true, children: [title ? `${truncate(title, 32)} · ` : "", model.name, " \u00B7 ctx ", contextTokens.toLocaleString(), " (", contextPct, "%)", model.free ? " · free" : ` · $${totalCost.toFixed(4)}`] })] }), (plan || constrainedLine) && (_jsx(Box, { justifyContent: "flex-end", children: _jsxs(Text, { dimColor: true, children: [plan && constrainedLine ? " · " : "", constrainedLine] }) }))] }));
48
+ const fiveHourLine = fiveHourSummary(tieredUsage);
49
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { justifyContent: "space-between", children: [_jsxs(Text, { dimColor: true, children: [_jsx(Text, { color: approvalMode === "ask" ? undefined : COLORS.warning, children: MODE_LABELS[approvalMode] }), " (shift+tab to cycle)", busy && " · esc to interrupt", state ? _jsxs(Text, { color: COLORS.thinking, children: [" \u00B7 ", state] }) : null] }), _jsxs(Text, { dimColor: true, children: [title ? `${truncate(title, 32)} · ` : "", model.name, " \u00B7 ctx ", contextTokens.toLocaleString(), " (", contextPct, "%)"] })] }), fiveHourLine && (_jsx(Box, { justifyContent: "flex-end", children: _jsx(Text, { dimColor: true, children: fiveHourLine }) }))] }));
57
50
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@matterailab/orbcode",
3
- "version": "0.1.7",
3
+ "version": "0.1.10",
4
4
  "description": "OrbCode CLI — agentic coding in your terminal, powered by Axon models by MatterAI",
5
5
  "type": "module",
6
6
  "bin": {