@bli-cockpit/cli 0.2.48 → 0.2.50

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.
Files changed (83) hide show
  1. package/dist/adapters/raw-evidence-attribution-gaps.js +133 -0
  2. package/dist/adapters/raw-evidence-claude-reader.js +108 -0
  3. package/dist/adapters/raw-evidence-codex-reader.js +147 -0
  4. package/dist/adapters/raw-evidence-collection-state.js +199 -0
  5. package/dist/adapters/raw-evidence-facts.js +338 -0
  6. package/dist/adapters/raw-evidence-git-diff-reader.js +187 -0
  7. package/dist/adapters/raw-evidence-image-reader.js +107 -0
  8. package/dist/adapters/raw-evidence-sanitize.js +56 -0
  9. package/dist/adapters/raw-evidence-transcript-file.js +182 -0
  10. package/dist/adapters/raw-evidence.js +94 -1203
  11. package/dist/autostart-contract.js +79 -0
  12. package/dist/autostart-darwin-plist.js +265 -0
  13. package/dist/autostart-darwin.js +171 -0
  14. package/dist/autostart-windows-scripts.js +310 -0
  15. package/dist/autostart-windows-task-xml.js +260 -0
  16. package/dist/autostart-windows.js +237 -0
  17. package/dist/autostart-xml.js +23 -0
  18. package/dist/autostart.js +35 -1148
  19. package/dist/commands/agent-rules-command.js +55 -0
  20. package/dist/commands/agent-session-report.js +290 -0
  21. package/dist/commands/analyze.js +131 -0
  22. package/dist/commands/autostart-command.js +105 -0
  23. package/dist/commands/backfill-batches.js +34 -0
  24. package/dist/commands/backfill-candidates.js +54 -0
  25. package/dist/commands/backfill-checkpoint.js +101 -0
  26. package/dist/commands/backfill-command-line.js +70 -0
  27. package/dist/commands/backfill-evidence-outcomes.js +104 -0
  28. package/dist/commands/backfill-issues.js +265 -0
  29. package/dist/commands/backfill-output.js +75 -0
  30. package/dist/commands/backfill-plan.js +71 -0
  31. package/dist/commands/backfill-reasons.js +107 -0
  32. package/dist/commands/backfill-report.js +298 -0
  33. package/dist/commands/backfill-result.js +150 -0
  34. package/dist/commands/backfill-scan.js +274 -0
  35. package/dist/commands/backfill-scope.js +114 -0
  36. package/dist/commands/backfill-session-report.js +145 -0
  37. package/dist/commands/backfill-types.js +1 -0
  38. package/dist/commands/backfill-upload.js +212 -0
  39. package/dist/commands/backfill.js +58 -1705
  40. package/dist/commands/cli-io.js +13 -0
  41. package/dist/commands/doctor.js +57 -0
  42. package/dist/commands/jarvis-trace.js +184 -0
  43. package/dist/commands/jarvis.js +323 -7
  44. package/dist/commands/local-arg-values.js +169 -0
  45. package/dist/commands/local-args-collector.js +604 -0
  46. package/dist/commands/local-args-tower.js +891 -0
  47. package/dist/commands/local-args.js +10 -1549
  48. package/dist/commands/local-help.js +30 -5
  49. package/dist/commands/local.js +21 -1786
  50. package/dist/commands/login.js +53 -0
  51. package/dist/commands/logout.js +66 -0
  52. package/dist/commands/memory-install-claude.js +294 -0
  53. package/dist/commands/memory-install-codex.js +205 -0
  54. package/dist/commands/memory-install-contract.js +231 -0
  55. package/dist/commands/memory-install-files.js +63 -0
  56. package/dist/commands/memory-install-skills.js +121 -0
  57. package/dist/commands/memory-install-toml.js +265 -0
  58. package/dist/commands/memory-install.js +378 -0
  59. package/dist/commands/onboard-receipts.js +66 -0
  60. package/dist/commands/onboard-report.js +274 -0
  61. package/dist/commands/onboard.js +449 -0
  62. package/dist/commands/ops-render.js +36 -0
  63. package/dist/commands/public-root.js +1 -1
  64. package/dist/commands/serve.js +13 -0
  65. package/dist/commands/session-sync.js +513 -534
  66. package/dist/commands/settings-render.js +28 -0
  67. package/dist/commands/settings.js +66 -2
  68. package/dist/commands/start.js +47 -0
  69. package/dist/commands/sync-followups.js +308 -0
  70. package/dist/commands/sync.js +387 -0
  71. package/dist/local-state-attributed-target.js +75 -0
  72. package/dist/local-state-config.js +147 -0
  73. package/dist/local-state-files.js +59 -0
  74. package/dist/local-state-identity.js +73 -0
  75. package/dist/local-state-pairing.js +263 -0
  76. package/dist/local-state-paths.js +61 -0
  77. package/dist/local-state-session.js +68 -0
  78. package/dist/local-state-status.js +163 -0
  79. package/dist/local-state-work-context.js +190 -0
  80. package/dist/local-state.js +34 -848
  81. package/dist/tower-client.js +3 -2
  82. package/dist/tower-stream.js +76 -6
  83. package/package.json +2 -1
@@ -81,14 +81,15 @@ export async function towerRequest(options) {
81
81
  };
82
82
  try {
83
83
  const response = await options.fetch(url, init);
84
+ const respondedMs = Date.now() - startedAt;
84
85
  log(`[tower client] responded ${JSON.stringify({
85
86
  request: options.label,
86
87
  http_status: response.status,
87
88
  content_type: response.headers.get("content-type") ?? "none",
88
89
  streamed: (response.headers.get("content-type") ?? "").includes("application/x-ndjson"),
89
- elapsed_ms: Date.now() - startedAt,
90
+ elapsed_ms: respondedMs,
90
91
  })}`);
91
- return { ok: true, response };
92
+ return { ok: true, response, startedAt, respondedMs };
92
93
  }
93
94
  catch (error) {
94
95
  const reason = isAbortError(error)
@@ -2,8 +2,9 @@
2
2
  * Reading a Tower turn as it happens (BLI-3457).
3
3
  *
4
4
  * The dashboard's conversation routes speak one JSON object per line
5
- * (`application/x-ndjson`): `activity` events as tools start and settle, then
6
- * exactly one `final` event carrying what the plain JSON response used to be.
5
+ * (`application/x-ndjson`): `activity` events as tools start and settle,
6
+ * `token` events carrying the answer as it is written (BLI-3517), then exactly
7
+ * one `final` event carrying what the plain JSON response used to be.
7
8
  * That is the same wire format the browser already reads
8
9
  * (`apps/dashboard/src/lib/webchat/tool-trace.ts`); this is the terminal's
9
10
  * reader for it.
@@ -39,7 +40,7 @@ const CEILING_HINT_MS = 110_000;
39
40
  * from a dashboard that does not stream yet (yielded as the final event, after
40
41
  * a `stream_not_available` note).
41
42
  */
42
- export async function* readTowerStream(response) {
43
+ export async function* readTowerStream(response, options = {}) {
43
44
  const contentType = response.headers.get("content-type") ?? "";
44
45
  if (!contentType.includes("application/x-ndjson")) {
45
46
  yield* readPlainJsonBody(response);
@@ -48,6 +49,7 @@ export async function* readTowerStream(response) {
48
49
  let buffer = "";
49
50
  try {
50
51
  for await (const chunk of iterateBody(response)) {
52
+ options.onBytes?.();
51
53
  buffer += chunk;
52
54
  const lines = buffer.split("\n");
53
55
  buffer = lines.pop() ?? "";
@@ -89,7 +91,18 @@ export async function readTowerTurn(response, options = {}) {
89
91
  const notes = [];
90
92
  let streamed = true;
91
93
  let final = null;
92
- for await (const item of readTowerStream(response)) {
94
+ let draft = "";
95
+ let tokenFrames = 0;
96
+ let firstByteMs = null;
97
+ let firstFrameMs = null;
98
+ let firstTokenMs = null;
99
+ for await (const item of readTowerStream(response, {
100
+ onBytes: () => {
101
+ firstByteMs ??= Date.now() - startedAt;
102
+ },
103
+ })) {
104
+ if (item.kind === "event")
105
+ firstFrameMs ??= Date.now() - startedAt;
93
106
  if (item.kind === "note") {
94
107
  if (item.reason === "stream_not_available")
95
108
  streamed = false;
@@ -101,10 +114,21 @@ export async function readTowerTurn(response, options = {}) {
101
114
  final = item.event;
102
115
  break;
103
116
  }
117
+ if (item.event.type === "token") {
118
+ const text = item.event.text ?? "";
119
+ // The mark a person actually feels: the first frame with WORDS in it.
120
+ if (text)
121
+ firstTokenMs ??= Date.now() - startedAt;
122
+ tokenFrames += 1;
123
+ draft = item.event.reset ? text : draft + text;
124
+ options.onToken?.(item.event);
125
+ continue;
126
+ }
104
127
  activity.push(item.event);
105
128
  options.onActivity?.(item.event);
106
129
  }
107
130
  const elapsedMs = Date.now() - startedAt;
131
+ const timing = { firstByteMs, firstFrameMs, firstTokenMs };
108
132
  if (!final) {
109
133
  // The last named note is the truest reason we have; absent one, a stream
110
134
  // that simply stopped near the dashboard's ceiling is a timeout.
@@ -117,18 +141,64 @@ export async function readTowerTurn(response, options = {}) {
117
141
  options.log?.(`[tower stream] turn incomplete ${JSON.stringify({
118
142
  reason,
119
143
  activity_events: activity.length,
144
+ token_frames: tokenFrames,
145
+ drafted_chars: draft.length,
120
146
  notes: notes.length,
121
147
  elapsed_ms: elapsedMs,
148
+ // BLI-3591: a turn that died still says how far it got — a stream that
149
+ // never produced a byte and one that produced words and then broke are
150
+ // different failures.
151
+ ...turnTimingFields(timing),
122
152
  })}`);
123
153
  return { ok: false, reason, detail: streamFailureDetail(reason, terminal?.detail) };
124
154
  }
125
155
  options.log?.(`[tower stream] turn complete ${JSON.stringify({
126
156
  streamed,
157
+ // BLI-3582: the dashboard's own split of the wait, relayed verbatim off
158
+ // the final frame — `wire_ms` (request accepted → first byte out),
159
+ // `prep_ms` (→ the model call started) and `model_ttft_ms` (→ its first
160
+ // token). All null against a dashboard that does not send them, which is
161
+ // a different fact from a turn that was instant.
162
+ ...finalLatencyFields(final),
127
163
  activity_events: activity.length,
164
+ // BLI-3517: zero token frames against a streaming dashboard means the
165
+ // answer arrived all at once — worth telling apart from a quiet turn.
166
+ token_frames: tokenFrames,
167
+ drafted_chars: draft.length,
128
168
  notes: notes.length,
129
169
  elapsed_ms: elapsedMs,
170
+ // BLI-3591: this side's own three marks, beside the dashboard's three
171
+ // above. `first_byte_ms` is the socket opening (the dashboard's flushed
172
+ // newline), `first_frame_ms` the first readable frame and
173
+ // `first_token_ms` the first WORDS — so the 1-2 s that used to sit
174
+ // unexplained between the dashboard's first token and the terminal's
175
+ // first visible character now has a number on each end of it.
176
+ ...turnTimingFields(timing),
130
177
  })}`);
131
- return { ok: true, final, activity, streamed };
178
+ return { ok: true, final, activity, streamed, draft, timing };
179
+ }
180
+ /** The same three marks as log fields, snake_case like every other line here. */
181
+ export function turnTimingFields(timing) {
182
+ return {
183
+ first_byte_ms: timing.firstByteMs,
184
+ first_frame_ms: timing.firstFrameMs,
185
+ first_token_ms: timing.firstTokenMs,
186
+ };
187
+ }
188
+ /**
189
+ * The three latency spans off a final frame, as log fields (BLI-3582).
190
+ *
191
+ * Read defensively: this module knows the ENVELOPE, not the body, and a body
192
+ * shape that drifts must degrade to three nulls rather than a thrown turn.
193
+ */
194
+ function finalLatencyFields(final) {
195
+ const raw = final["latency"];
196
+ const latency = raw && typeof raw === "object" && !Array.isArray(raw) ? raw : {};
197
+ const ms = (key) => {
198
+ const value = latency[key];
199
+ return typeof value === "number" && Number.isFinite(value) ? value : null;
200
+ };
201
+ return { wire_ms: ms("wireMs"), prep_ms: ms("prepMs"), model_ttft_ms: ms("modelTtftMs") };
132
202
  }
133
203
  /** One plain sentence per way a turn can end without an answer. */
134
204
  export function streamFailureDetail(reason, detail) {
@@ -207,7 +277,7 @@ function parseLine(line) {
207
277
  return { kind: "note", reason: "unexpected_event_type" };
208
278
  }
209
279
  const type = parsed.type;
210
- if (type !== "activity" && type !== "final") {
280
+ if (type !== "activity" && type !== "token" && type !== "final") {
211
281
  return { kind: "note", reason: "unexpected_event_type" };
212
282
  }
213
283
  return { kind: "event", event: parsed };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bli-cockpit/cli",
3
- "version": "0.2.48",
3
+ "version": "0.2.50",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "bin": {
@@ -27,6 +27,7 @@
27
27
  "test": "node dist/cli.js --help && node ../../scripts/assert-public-cli-routing.mjs && node ../../scripts/assert-public-cli-runtime-files.mjs && node ../../scripts/assert-public-cli-no-fleet-posts.mjs && node ../../scripts/assert-public-package-pack.mjs --workspace=@bli-cockpit/cli"
28
28
  },
29
29
  "dependencies": {
30
+ "@bli-cockpit/memory-mcp": "0.1.0",
30
31
  "@bli-cockpit/telemetry-core": "0.1.26"
31
32
  }
32
33
  }