@chatpanel/events 0.91.0 → 0.91.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/index.js CHANGED
@@ -197,7 +197,7 @@ export { fixedPlan, parsePlan, plannerPrompt, waves, breakCycles, TEAM_PLAN_SCHE
197
197
  export { createBoard, parseFindings, boardText, findingsInstruction, toBriefClaims, FINDINGS_SCHEMA, FINDING_KINDS, THREAD_KINDS, THREAD_STATUSES, POST_KINDS, POST_STATUSES, ASK_TYPES, emptyBoardState, foldBoard, findingsOf } from './team-board.js';
198
198
  export { boardToolProvider, boardToolSpec, createAnswerBox, withBoardTool, BOARD_TOOL_NAME, DEFAULT_ASK_TIMEOUT_MS } from './board-tool.js';
199
199
  export { createRunCache, withRunCache } from './team-cache.js';
200
- export { emptyRun, foldRun, runFromEvents, checkpointFrom, isResumable, LIVE_RUN_STATUSES, RESUMABLE_RUN_STATUSES } from './team-record.js';
200
+ export { emptyRun, foldRun, runFromEvents, checkpointFrom, isResumable, LIVE_RUN_STATUSES, RESUMABLE_RUN_STATUSES , spendOf, describeSpend } from './team-record.js';
201
201
  export { validateProject, normalizeProject, defineProject, canTransition as canProjectTransition, blankProject, projectFromForm, emptyProjectRecord, foldProject, projectProgress, ProjectError, PROJECT_STATUSES, PROJECT_ID_RE } from './project.js';
202
202
  // Job POSTINGS (F8 §12) — `jobs.js` is the scheduler and keeps `defineJob`; a posting is a JobPost here.
203
203
  export { validateJob as validateJobPost, normalizeJob as normalizeJobPost, defineJob as defineJobPost, canTransition as canJobPostTransition, applyAll, jobToRole, readyJobs, blankJob as blankJobPost, jobFromForm as jobPostFromForm, JobError as JobPostError, JOB_STATUSES as JOB_POST_STATUSES, JOB_ID_RE as JOB_POST_ID_RE } from './job.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chatpanel/events",
3
- "version": "0.91.0",
3
+ "version": "0.91.1",
4
4
  "description": "The canonical ChatPanel event-log and capability contracts \u2014 typed durable facts, clock-free deterministic linearization, schema upcasting, and the invariants the replay harness asserts. Pure, dependency-free ESM shared by the ChatPanel extension, gateway and bridge.",
5
5
  "type": "module",
6
6
  "main": "index.js",
package/team-record.js CHANGED
@@ -122,3 +122,32 @@ export function isResumable(run) {
122
122
  if (RESUMABLE_RUN_STATUSES.includes(run.status)) return true;
123
123
  return !!run.stale && LIVE_RUN_STATUSES.includes(run.status); // its client went away mid-run
124
124
  }
125
+
126
+ /**
127
+ * The run's spend against its cap, as a board shows it: the record's last `run.usage` (or
128
+ * nothing spent yet) with `ms` measured LIVE for a run still going — the stored figure is as
129
+ * of the last task's end, and a board read "0 s" through a ten-minute research task.
130
+ */
131
+ export function spendOf(run, { now = Date.now() } = {}) {
132
+ const cap = run?.usage?.cap || run?.budget || null;
133
+ if (!cap || !Object.keys(cap).length) return null;
134
+ const spent = { tokens: 0, calls: 0, usd: 0, ms: 0, ...(run?.usage?.spent || {}) };
135
+ if (LIVE_RUN_STATUSES.includes(run?.status) && run?.startedAt) spent.ms = Math.max(spent.ms || 0, now - run.startedAt);
136
+ const pct = cap.tokens ? Math.min(100, Math.round(((spent.tokens || 0) / cap.tokens) * 100)) : cap.ms ? Math.min(100, Math.round(((spent.ms || 0) / cap.ms) * 100)) : null;
137
+ return { cap, spent, pct, exhausted: run?.usage?.exhausted || null };
138
+ }
139
+
140
+ const secs = (ms) => { const s = Math.max(0, Math.round((Number(ms) || 0) / 1000)); return s >= 60 ? `${Math.floor(s / 60)}m${String(s % 60).padStart(2, '0')}s` : `${s}s`; };
141
+ const num = (n) => (Number(n) || 0).toLocaleString('en-US');
142
+
143
+ /** One line: `1,240 / 40,000 tokens · 3 / 20 calls · 2m10s / 15m00s`. Only the capped dimensions. */
144
+ export function describeSpend(spend) {
145
+ if (!spend?.cap) return '';
146
+ const { cap, spent } = spend;
147
+ return [
148
+ cap.tokens ? `${num(spent.tokens)} / ${num(cap.tokens)} tokens` : '',
149
+ cap.calls ? `${num(spent.calls)} / ${num(cap.calls)} calls` : '',
150
+ cap.usd ? `$${(Number(spent.usd) || 0).toFixed(2)} / $${Number(cap.usd).toFixed(2)}` : '',
151
+ cap.ms ? `${secs(spent.ms)} / ${secs(cap.ms)}` : '',
152
+ ].filter(Boolean).join(' · ');
153
+ }