@bli-cockpit/cli 0.2.49 → 0.2.51

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 (57) hide show
  1. package/dist/adapters/raw-evidence-claude-reader.js +108 -0
  2. package/dist/adapters/raw-evidence-codex-reader.js +147 -0
  3. package/dist/adapters/raw-evidence-collection-state.js +199 -0
  4. package/dist/adapters/raw-evidence-facts.js +338 -0
  5. package/dist/adapters/raw-evidence-git-diff-reader.js +187 -0
  6. package/dist/adapters/raw-evidence-image-reader.js +107 -0
  7. package/dist/adapters/raw-evidence-sanitize.js +56 -0
  8. package/dist/adapters/raw-evidence-transcript-file.js +182 -0
  9. package/dist/adapters/raw-evidence.js +63 -1183
  10. package/dist/commands/backfill-batches.js +34 -0
  11. package/dist/commands/backfill-candidates.js +54 -0
  12. package/dist/commands/backfill-checkpoint.js +101 -0
  13. package/dist/commands/backfill-command-line.js +70 -0
  14. package/dist/commands/backfill-evidence-outcomes.js +104 -0
  15. package/dist/commands/backfill-issues.js +265 -0
  16. package/dist/commands/backfill-output.js +75 -0
  17. package/dist/commands/backfill-plan.js +71 -0
  18. package/dist/commands/backfill-reasons.js +107 -0
  19. package/dist/commands/backfill-report.js +298 -0
  20. package/dist/commands/backfill-result.js +150 -0
  21. package/dist/commands/backfill-scan.js +274 -0
  22. package/dist/commands/backfill-scope.js +114 -0
  23. package/dist/commands/backfill-session-report.js +145 -0
  24. package/dist/commands/backfill-types.js +1 -0
  25. package/dist/commands/backfill-upload.js +212 -0
  26. package/dist/commands/backfill.js +41 -1961
  27. package/dist/commands/doctor.js +57 -0
  28. package/dist/commands/jarvis-trace.js +184 -0
  29. package/dist/commands/jarvis.js +144 -4
  30. package/dist/commands/local-args-collector.js +26 -0
  31. package/dist/commands/local-args-tower.js +21 -0
  32. package/dist/commands/local-args.js +3 -1
  33. package/dist/commands/local-help.js +19 -2
  34. package/dist/commands/local.js +3 -0
  35. package/dist/commands/memory-install-claude.js +294 -0
  36. package/dist/commands/memory-install-codex.js +205 -0
  37. package/dist/commands/memory-install-contract.js +286 -0
  38. package/dist/commands/memory-install-files.js +63 -0
  39. package/dist/commands/memory-install-skills.js +121 -0
  40. package/dist/commands/memory-install-toml.js +265 -0
  41. package/dist/commands/memory-install.js +465 -0
  42. package/dist/commands/public-root.js +1 -1
  43. package/dist/commands/sync-followups.js +105 -0
  44. package/dist/commands/sync.js +7 -1
  45. package/dist/local-state-attributed-target.js +75 -0
  46. package/dist/local-state-config.js +147 -0
  47. package/dist/local-state-files.js +59 -0
  48. package/dist/local-state-identity.js +73 -0
  49. package/dist/local-state-pairing.js +263 -0
  50. package/dist/local-state-paths.js +61 -0
  51. package/dist/local-state-session.js +68 -0
  52. package/dist/local-state-status.js +163 -0
  53. package/dist/local-state-work-context.js +190 -0
  54. package/dist/local-state.js +34 -848
  55. package/dist/tower-client.js +3 -2
  56. package/dist/tower-stream.js +57 -3
  57. 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)
@@ -40,7 +40,7 @@ const CEILING_HINT_MS = 110_000;
40
40
  * from a dashboard that does not stream yet (yielded as the final event, after
41
41
  * a `stream_not_available` note).
42
42
  */
43
- export async function* readTowerStream(response) {
43
+ export async function* readTowerStream(response, options = {}) {
44
44
  const contentType = response.headers.get("content-type") ?? "";
45
45
  if (!contentType.includes("application/x-ndjson")) {
46
46
  yield* readPlainJsonBody(response);
@@ -49,6 +49,7 @@ export async function* readTowerStream(response) {
49
49
  let buffer = "";
50
50
  try {
51
51
  for await (const chunk of iterateBody(response)) {
52
+ options.onBytes?.();
52
53
  buffer += chunk;
53
54
  const lines = buffer.split("\n");
54
55
  buffer = lines.pop() ?? "";
@@ -92,7 +93,16 @@ export async function readTowerTurn(response, options = {}) {
92
93
  let final = null;
93
94
  let draft = "";
94
95
  let tokenFrames = 0;
95
- for await (const item of readTowerStream(response)) {
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;
96
106
  if (item.kind === "note") {
97
107
  if (item.reason === "stream_not_available")
98
108
  streamed = false;
@@ -106,6 +116,9 @@ export async function readTowerTurn(response, options = {}) {
106
116
  }
107
117
  if (item.event.type === "token") {
108
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;
109
122
  tokenFrames += 1;
110
123
  draft = item.event.reset ? text : draft + text;
111
124
  options.onToken?.(item.event);
@@ -115,6 +128,7 @@ export async function readTowerTurn(response, options = {}) {
115
128
  options.onActivity?.(item.event);
116
129
  }
117
130
  const elapsedMs = Date.now() - startedAt;
131
+ const timing = { firstByteMs, firstFrameMs, firstTokenMs };
118
132
  if (!final) {
119
133
  // The last named note is the truest reason we have; absent one, a stream
120
134
  // that simply stopped near the dashboard's ceiling is a timeout.
@@ -131,11 +145,21 @@ export async function readTowerTurn(response, options = {}) {
131
145
  drafted_chars: draft.length,
132
146
  notes: notes.length,
133
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),
134
152
  })}`);
135
153
  return { ok: false, reason, detail: streamFailureDetail(reason, terminal?.detail) };
136
154
  }
137
155
  options.log?.(`[tower stream] turn complete ${JSON.stringify({
138
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),
139
163
  activity_events: activity.length,
140
164
  // BLI-3517: zero token frames against a streaming dashboard means the
141
165
  // answer arrived all at once — worth telling apart from a quiet turn.
@@ -143,8 +167,38 @@ export async function readTowerTurn(response, options = {}) {
143
167
  drafted_chars: draft.length,
144
168
  notes: notes.length,
145
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),
146
177
  })}`);
147
- return { ok: true, final, activity, streamed, draft };
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") };
148
202
  }
149
203
  /** One plain sentence per way a turn can end without an answer. */
150
204
  export function streamFailureDetail(reason, detail) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bli-cockpit/cli",
3
- "version": "0.2.49",
3
+ "version": "0.2.51",
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.1",
30
31
  "@bli-cockpit/telemetry-core": "0.1.26"
31
32
  }
32
33
  }