@bli-cockpit/cli 0.2.236 → 0.2.238

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 (143) hide show
  1. package/dist/adapters/attribution-core-fallbacks.js +15 -11
  2. package/dist/adapters/attribution-core-score.js +111 -87
  3. package/dist/adapters/car-state.js +38 -35
  4. package/dist/adapters/claude-attribution-discovery.js +81 -56
  5. package/dist/adapters/claude-attribution-score.js +108 -94
  6. package/dist/adapters/claude-attribution-signals.js +52 -42
  7. package/dist/adapters/codex-attribution.js +42 -33
  8. package/dist/adapters/codex-session-signals.js +11 -9
  9. package/dist/adapters/git-state.js +56 -50
  10. package/dist/adapters/local-sources.js +52 -43
  11. package/dist/adapters/raw-evidence-git-diff.js +4 -2
  12. package/dist/adapters/raw-evidence-sanitize.js +15 -12
  13. package/dist/adapters/raw-evidence-transcript-file.js +32 -31
  14. package/dist/autostart-self-heal.js +18 -2
  15. package/dist/commands/agent-session-report.js +51 -59
  16. package/dist/commands/agent-setup-observed.js +30 -23
  17. package/dist/commands/agent-setup.js +96 -45
  18. package/dist/commands/autostart-heal.js +8 -2
  19. package/dist/commands/autostart-reading.js +3 -0
  20. package/dist/commands/backfill-evidence-outcomes.js +9 -6
  21. package/dist/commands/backfill-issues.js +3 -0
  22. package/dist/commands/backfill-scan.js +27 -22
  23. package/dist/commands/backfill-upload.js +69 -60
  24. package/dist/commands/backfill.js +26 -21
  25. package/dist/commands/brief-done.js +4 -1
  26. package/dist/commands/brief-edit.js +71 -46
  27. package/dist/commands/cal-detail.js +35 -0
  28. package/dist/commands/cal-overlay.js +106 -0
  29. package/dist/commands/cal.js +40 -23
  30. package/dist/commands/clean.js +4 -1
  31. package/dist/commands/docs-read.js +5 -2
  32. package/dist/commands/doctor-backfill.js +88 -71
  33. package/dist/commands/doctor-pipeline.js +59 -46
  34. package/dist/commands/doctor-usage-telemetry.js +2 -1
  35. package/dist/commands/heartbeat-post.js +116 -0
  36. package/dist/commands/heartbeat-token-lock.js +46 -33
  37. package/dist/commands/heartbeat.js +43 -125
  38. package/dist/commands/install-update.js +17 -15
  39. package/dist/commands/issue-show.js +27 -28
  40. package/dist/commands/issue-write.js +70 -23
  41. package/dist/commands/issue.js +68 -37
  42. package/dist/commands/jarvis-live-answer.js +99 -0
  43. package/dist/commands/jarvis-render.js +1 -94
  44. package/dist/commands/jarvis-trace.js +30 -22
  45. package/dist/commands/jarvis-turn-report.js +124 -0
  46. package/dist/commands/jarvis-turn.js +108 -182
  47. package/dist/commands/jarvis.js +26 -17
  48. package/dist/commands/local-args-collector-setup.js +82 -84
  49. package/dist/commands/local-args-collector-work.js +37 -38
  50. package/dist/commands/local-args-tower-admin.js +155 -142
  51. package/dist/commands/local-args-tower-cal.js +54 -48
  52. package/dist/commands/local-args-tower-careers.js +73 -54
  53. package/dist/commands/local-args-tower-docs-msg.js +56 -188
  54. package/dist/commands/local-args-tower-github.js +26 -20
  55. package/dist/commands/local-args-tower-hits.js +32 -20
  56. package/dist/commands/local-args-tower-manager.js +85 -4
  57. package/dist/commands/local-args-tower-memory.js +139 -117
  58. package/dist/commands/local-args-tower-msg.js +149 -0
  59. package/dist/commands/local-args-tower-notes.js +346 -0
  60. package/dist/commands/local-args-tower-pages.js +100 -378
  61. package/dist/commands/local-args-tower-settings.js +28 -29
  62. package/dist/commands/local-args-tower-work.js +52 -33
  63. package/dist/commands/local-args.js +80 -129
  64. package/dist/commands/local-help-commands-tower-ops.js +15 -4
  65. package/dist/commands/local-help-commands-tower.js +11 -4
  66. package/dist/commands/local-help-commands.js +342 -341
  67. package/dist/commands/local-help.js +87 -83
  68. package/dist/commands/local-run-guards.js +100 -0
  69. package/dist/commands/local.js +137 -206
  70. package/dist/commands/login.js +13 -9
  71. package/dist/commands/logs.js +55 -38
  72. package/dist/commands/mail-attachments.js +44 -26
  73. package/dist/commands/mail.js +6 -4
  74. package/dist/commands/manager-render-home.js +208 -0
  75. package/dist/commands/manager-render.js +7 -3
  76. package/dist/commands/manager-requests.js +33 -0
  77. package/dist/commands/manager.js +39 -5
  78. package/dist/commands/memory-consolidation.js +18 -9
  79. package/dist/commands/memory-daemon-probe.js +37 -28
  80. package/dist/commands/memory-install-config.js +64 -60
  81. package/dist/commands/memory-install.js +21 -41
  82. package/dist/commands/memory-requeue.js +26 -19
  83. package/dist/commands/memory-rules.js +26 -18
  84. package/dist/commands/memory-search-print.js +26 -0
  85. package/dist/commands/memory-store.js +14 -30
  86. package/dist/commands/memory-verb-list.js +49 -48
  87. package/dist/commands/msg.js +51 -9
  88. package/dist/commands/notes-accounts.js +49 -32
  89. package/dist/commands/notes-sync-loop.js +115 -89
  90. package/dist/commands/notes-wisprflow-login.js +44 -23
  91. package/dist/commands/notes-writes.js +115 -100
  92. package/dist/commands/notes.js +33 -55
  93. package/dist/commands/onboard-flows.js +69 -51
  94. package/dist/commands/onboard.js +11 -9
  95. package/dist/commands/ops-render-memory.js +10 -7
  96. package/dist/commands/ops-render.js +33 -30
  97. package/dist/commands/ops.js +3 -0
  98. package/dist/commands/public-root.js +1 -1
  99. package/dist/commands/session-sync-outside-roots.js +40 -36
  100. package/dist/commands/session-sync-plan.js +14 -10
  101. package/dist/commands/session-sync-upload.js +24 -22
  102. package/dist/commands/session-sync.js +19 -8
  103. package/dist/commands/slack.js +25 -16
  104. package/dist/commands/sources-check.js +13 -7
  105. package/dist/commands/sources-discovery.js +25 -17
  106. package/dist/commands/sources-verdict.js +19 -18
  107. package/dist/commands/speed.js +23 -19
  108. package/dist/commands/start.js +20 -16
  109. package/dist/commands/sync-doctor.js +52 -48
  110. package/dist/commands/sync.js +22 -16
  111. package/dist/commands/usage-telemetry-toml.js +71 -45
  112. package/dist/commands/usage.js +91 -67
  113. package/dist/commands/workbook-render.js +10 -7
  114. package/dist/evidence-reconcile-client.js +75 -64
  115. package/dist/evidence-redelivery-plan.js +37 -34
  116. package/dist/evidence-redelivery.js +32 -29
  117. package/dist/evidence-transcripts-client.js +20 -20
  118. package/dist/evidence-upload-object.js +86 -89
  119. package/dist/evidence-upload-plan.js +94 -83
  120. package/dist/evidence-upload-terminal.js +45 -50
  121. package/dist/image-description-session.js +156 -159
  122. package/dist/local-state-pairing-code.js +39 -31
  123. package/dist/local-state-status.js +9 -2
  124. package/dist/onboarding-roots.js +5 -8
  125. package/dist/process-runner.js +8 -8
  126. package/dist/raw-evidence-gc.js +33 -38
  127. package/dist/raw-evidence-stuck-summary.js +7 -14
  128. package/dist/repo-identity.js +26 -23
  129. package/dist/scheduled-self-update.js +11 -15
  130. package/dist/second-install.js +3 -0
  131. package/dist/slim/codex-rules.js +23 -36
  132. package/dist/sync-lock.js +5 -15
  133. package/dist/tower-client.js +36 -53
  134. package/dist/tower-stream.js +7 -24
  135. package/dist/upload-agent-artifacts.js +58 -64
  136. package/dist/upload-envelope-build.js +60 -59
  137. package/dist/upload-evidence-delivery-reconcile.js +58 -56
  138. package/dist/upload-evidence-delivery-summary.js +32 -34
  139. package/dist/upload-ingest-receipt.js +20 -23
  140. package/dist/upload-session-reports-queue.js +4 -1
  141. package/dist/upload-session-reports-wire.js +64 -68
  142. package/dist/upload-sync.js +171 -157
  143. package/package.json +2 -2
@@ -95,18 +95,9 @@ export function hasRetryableEvidenceGap(facts, outcomes) {
95
95
  return facts.evidence_completeness.skip_reasons.some(({ reason }) => isRetryableEvidenceSkipReason(reason));
96
96
  }
97
97
  /**
98
- * Skip reasons a later sync can still turn into evidence.
99
- *
100
- * `delivery_backoff_holding` is one of them, and it has to be: a source held by
101
- * backoff is missing collection right now. If it did not land here the sync
102
- * would read clean while a transcript sat undelivered for nine days, which is
103
- * the exact shape of BLI-3066.
104
- *
105
- * BLI-4022 did NOT change that. A hold still queues the retry and still travels
106
- * to the status output; what changed is downstream, at the ledger in
107
- * `commands/session-sync-failures.ts`, which no longer files it as a FAILED
108
- * tick. Retryable and failed are two questions, and answering both with this
109
- * one set is what painted a collecting machine red for eight ticks.
98
+ * Queue retries for collection skips a later sync can rescue, including backoff.
99
+ * Retryable and failed are separate questions: a hold remains missing evidence
100
+ * without making the scheduled tick itself a failed attempt.
110
101
  */
111
102
  function isRetryableEvidenceSkipReason(reason) {
112
103
  if (reason === DELIVERY_BACKOFF_HOLDING_REASON)
@@ -158,17 +149,7 @@ export function summarizeRawEvidenceDelivery(built, outcomes, uploadedChunkCount
158
149
  const retryReason = retryRequired
159
150
  ? retryableEvidenceGapReason(built.raw_evidence_facts, outcomes)
160
151
  : null;
161
- const cursorReusedOutcomes = cursorReused.map((entry) => ({
162
- object_key: cursor.objects[entry.content_hash_sha256]?.object_key ?? "",
163
- raw_evidence_pointer_id: cursor.objects[entry.content_hash_sha256]?.object_key ?? "",
164
- kind: entry.kind,
165
- codex_session_id: entry.codex_session_id,
166
- ...(entry.artifact_metadata
167
- ? { artifact_metadata: entry.artifact_metadata }
168
- : {}),
169
- upload_state: "reused_existing",
170
- reason: "cursor_content_match",
171
- }));
152
+ const cursorReusedOutcomes = summarizeCursorReuse(cursorReused, cursor);
172
153
  return {
173
154
  raw_evidence_file_count: built.raw_evidence_upload_files.length,
174
155
  raw_evidence_uploaded_object_count: outcomes.filter((outcome) => outcome.upload_state === "uploaded").length,
@@ -188,17 +169,7 @@ export function summarizeRawEvidenceDelivery(built, outcomes, uploadedChunkCount
188
169
  .filter(Boolean)
189
170
  : [],
190
171
  raw_evidence_outcomes: [
191
- ...outcomes.map((outcome) => ({
192
- object_key: outcome.object_key,
193
- raw_evidence_pointer_id: outcome.pointer.raw_evidence_pointer_id,
194
- kind: outcome.kind,
195
- codex_session_id: outcome.codex_session_id,
196
- ...(outcome.artifact_metadata
197
- ? { artifact_metadata: outcome.artifact_metadata }
198
- : {}),
199
- upload_state: outcome.upload_state,
200
- reason: outcome.reason,
201
- })),
172
+ ...outcomes.map(summarizeUploadOutcome),
202
173
  ...cursorReusedOutcomes,
203
174
  ],
204
175
  raw_evidence_deferred_byte_budget: built.raw_evidence_facts?.deferred_byte_budget_count ?? 0,
@@ -212,4 +183,31 @@ export function summarizeRawEvidenceDelivery(built, outcomes, uploadedChunkCount
212
183
  raw_evidence_held_next_attempt_at: stuck.held_next_attempt_at,
213
184
  raw_evidence_held_last_reason: stuck.held_last_reason,
214
185
  };
186
+ }
187
+ function summarizeUploadOutcome(outcome) {
188
+ return {
189
+ object_key: outcome.object_key,
190
+ raw_evidence_pointer_id: outcome.pointer.raw_evidence_pointer_id,
191
+ kind: outcome.kind,
192
+ codex_session_id: outcome.codex_session_id,
193
+ ...(outcome.artifact_metadata
194
+ ? { artifact_metadata: outcome.artifact_metadata }
195
+ : {}),
196
+ upload_state: outcome.upload_state,
197
+ reason: outcome.reason,
198
+ };
199
+ }
200
+ function summarizeCursorReuse(cursorReused, cursor) {
201
+ const cursorReusedOutcomes = cursorReused.map((entry) => ({
202
+ object_key: cursor.objects[entry.content_hash_sha256]?.object_key ?? "",
203
+ raw_evidence_pointer_id: cursor.objects[entry.content_hash_sha256]?.object_key ?? "",
204
+ kind: entry.kind,
205
+ codex_session_id: entry.codex_session_id,
206
+ ...(entry.artifact_metadata
207
+ ? { artifact_metadata: entry.artifact_metadata }
208
+ : {}),
209
+ upload_state: "reused_existing",
210
+ reason: "cursor_content_match",
211
+ }));
212
+ return cursorReusedOutcomes;
215
213
  }
@@ -51,31 +51,12 @@ export async function postEnvelopeToIngest(options) {
51
51
  return response;
52
52
  }
53
53
  /**
54
- * The receipt that proves ingest persisted exactly what was submitted.
55
- *
56
- * Anything short of a 202 whose counts match the envelope's own totals is
57
- * treated as no receipt at all — a proxy's 200, a truncated body, or a
58
- * dashboard that accepted fewer rows than were sent all land here. They used to
59
- * land here under the SAME sentence, which is the BLI-3483 complaint: a
60
- * network appliance answering on the dashboard's behalf and a dashboard that
61
- * genuinely persisted three of four rows are opposite repairs. They are now two
62
- * reasons, and the second one names the field that disagreed and by how much.
54
+ * Advance retry state only for a 202 receipt matching every submitted count.
55
+ * Distinguish proxy/unreadable replies from partial persistence, naming each
56
+ * mismatched field and its submitted/received counts.
63
57
  */
64
58
  function assertAmbientIngestReceipt(response, value, envelope) {
65
- if (response.status !== 202 || !value || typeof value !== "object") {
66
- const detail = response.status !== 202
67
- ? `http ${response.status}; the ingest route answers 202, so something in front of it replied`
68
- : "202 with a body that is not an object";
69
- console.error("[cockpit-sync] ingest answered without a durable receipt", JSON.stringify({
70
- reason: "ingest_receipt_not_202",
71
- http_status: response.status,
72
- body_is_object: Boolean(value) && typeof value === "object",
73
- }));
74
- throw new SyncDeliveryError("ingest_receipt_not_202", {
75
- httpStatus: response.status,
76
- detail,
77
- });
78
- }
59
+ assertReceiptResponse(response, value);
79
60
  const record = value;
80
61
  const ingest = record["ingest"] && typeof record["ingest"] === "object"
81
62
  ? record["ingest"]
@@ -107,6 +88,22 @@ function assertAmbientIngestReceipt(response, value, envelope) {
107
88
  detail: mismatches.join("; "),
108
89
  });
109
90
  }
91
+ function assertReceiptResponse(response, value) {
92
+ if (response.status !== 202 || !value || typeof value !== "object") {
93
+ const detail = response.status !== 202
94
+ ? `http ${response.status}; the ingest route answers 202, so something in front of it replied`
95
+ : "202 with a body that is not an object";
96
+ console.error("[cockpit-sync] ingest answered without a durable receipt", JSON.stringify({
97
+ reason: "ingest_receipt_not_202",
98
+ http_status: response.status,
99
+ body_is_object: Boolean(value) && typeof value === "object",
100
+ }));
101
+ throw new SyncDeliveryError("ingest_receipt_not_202", {
102
+ httpStatus: response.status,
103
+ detail,
104
+ });
105
+ }
106
+ }
110
107
  /**
111
108
  * `submitted 4, receipt 3` — the sentence that tells partial persistence from a
112
109
  * receipt that never carried the field at all. Counts are numbers this
@@ -66,8 +66,11 @@ export async function flushPendingCodexSessionReports(options) {
66
66
  if (session.session_state !== "valid") {
67
67
  return await failPendingSessionReports(paths, state.pending_session_reports, attemptedAt, "collector_not_paired");
68
68
  }
69
+ return drainSessionReports(options, paths, state.pending_session_reports, sessionFile, session, attemptedAt, fetchImpl);
70
+ }
71
+ async function drainSessionReports(options, paths, pendingReports, sessionFile, session, attemptedAt, fetchImpl) {
69
72
  const results = [];
70
- for (const pending of state.pending_session_reports) {
73
+ for (const pending of pendingReports) {
71
74
  const result = await reportCodexSessionAttributions({
72
75
  fetchImpl,
73
76
  dashboardUrl: normalizeDashboardUrl(pending.dashboard_url || sessionFile.dashboard_url),
@@ -1,14 +1,6 @@
1
1
  /**
2
- * The wire: posting Codex session attribution rows and reading back a receipt.
3
- *
4
- * - `postCodexSessionReport` — the one-shot path, for a harvest that already
5
- * has a repo in hand; builds its own provenance and posts immediately.
6
- * - `reportCodexSessionAttributions` — chunk at the endpoint's session cap,
7
- * then attempt each chunk under `postCodexSessionAttributionChunk`'s
8
- * decision table of "final answer" versus "worth another attempt".
9
- *
10
- * `upload-session-reports-queue.ts` calls `reportCodexSessionAttributions`
11
- * once per queued report; nothing here touches the durable queue.
2
+ * Deliver attribution chunks with bounded retries and complete acknowledgements.
3
+ * HTTP refusals and transport failures retain distinct reason/status metadata.
12
4
  */
13
5
  import { CODEX_SESSION_REPORT_MAX_SESSIONS, CodexSessionAttributionReportResponseSchema, } from "@bli-cockpit/telemetry-core";
14
6
  import path from "node:path";
@@ -111,21 +103,9 @@ export async function reportCodexSessionAttributions(options) {
111
103
  };
112
104
  }
113
105
  /**
114
- * One chunk, up to three attempts, as a decision table on what came back:
115
- *
116
- * | what came back | verdict |
117
- * |-------------------------------------|----------------------------------------|
118
- * | 404 | this dashboard has no endpoint; stop |
119
- * | 2xx with a readable, full receipt | recorded; stop |
120
- * | 2xx malformed or short of the count | keep the reason, try again |
121
- * | 4xx other than 429 | the server's final answer; stop |
122
- * | 429 or 5xx | keep the reason, try again |
123
- * | transport error | keep the reason, try again, no status |
124
- *
125
- * The row that looks wrong is the third one, and it is the important one: a 2xx
126
- * we cannot read is retried because the server may have died between persisting
127
- * the rows and answering. Only a schema-valid receipt that accounts for every
128
- * distinct session is safe to delete a durable pending report against.
106
+ * Retry a chunk up to three times: 404 or final 4xx stops; 429, 5xx, transport
107
+ * errors and unreadable/short 2xx receipts retry. Only a schema-valid receipt
108
+ * covering every distinct session permits removal of the durable queued report.
129
109
  */
130
110
  async function postCodexSessionAttributionChunk(options) {
131
111
  const maxAttempts = options.maxAttemptsPerRequest ?? DEFAULT_REPORT_ATTEMPTS;
@@ -138,51 +118,13 @@ async function postCodexSessionAttributionChunk(options) {
138
118
  const response = await postSessionAttributionRequest(options);
139
119
  lastStatus = response.status;
140
120
  const body = await readResponseJson(response);
141
- if (response.status === 404) {
142
- return codexSessionReportChunkResult(options, {
143
- posted: false,
144
- reason: "codex_session_api_unavailable",
145
- httpStatus: response.status,
146
- recordedCount: 0,
147
- });
148
- }
149
- if (response.ok) {
150
- const receipt = readSessionReportReceipt(body, requiredAcknowledgements);
151
- if (receipt.accepted) {
152
- return codexSessionReportChunkResult(options, {
153
- posted: true,
154
- reason: "recorded",
155
- httpStatus: response.status,
156
- recordedCount: receipt.recorded_count,
157
- });
158
- }
159
- lastFailureReason = receipt.reason;
160
- }
161
- else {
162
- lastFailureReason = `report_failed_http_${response.status}`;
163
- if (isFinalHttpFailure(response.status)) {
164
- return codexSessionReportChunkResult(options, {
165
- posted: false,
166
- reason: lastFailureReason,
167
- httpStatus: response.status,
168
- recordedCount: 0,
169
- });
170
- }
171
- }
121
+ const decision = decideSessionReportResponse(options, response, body, requiredAcknowledgements);
122
+ if (decision.result)
123
+ return decision.result;
124
+ lastFailureReason = decision.reason;
172
125
  }
173
126
  catch (error) {
174
- // Logged per attempt, on purpose: the interesting failure is the one
175
- // that repeats. Three identical ECONNRESETs and one DNS failure followed
176
- // by two resets are different stories, and `report_network_error` alone
177
- // tells neither (BLI-3238).
178
- console.error("[session-reports] report attempt failed before a status came back", JSON.stringify({
179
- reason: "report_network_error",
180
- attempt,
181
- max_attempts: maxAttempts,
182
- batch_index: options.batchIndex,
183
- session_count: options.sessions.length,
184
- ...describeError(error),
185
- }));
127
+ logSessionReportTransportFailure(options, error, attempt, maxAttempts);
186
128
  lastStatus = null;
187
129
  lastFailureReason = "report_network_error";
188
130
  }
@@ -200,6 +142,60 @@ async function postCodexSessionAttributionChunk(options) {
200
142
  recordedCount: 0,
201
143
  });
202
144
  }
145
+ function decideSessionReportResponse(options, response, body, requiredAcknowledgements) {
146
+ if (response.status === 404) {
147
+ return {
148
+ result: codexSessionReportChunkResult(options, {
149
+ posted: false,
150
+ reason: "codex_session_api_unavailable",
151
+ httpStatus: response.status,
152
+ recordedCount: 0,
153
+ })
154
+ };
155
+ }
156
+ if (response.ok) {
157
+ const receipt = readSessionReportReceipt(body, requiredAcknowledgements);
158
+ if (receipt.accepted) {
159
+ return {
160
+ result: codexSessionReportChunkResult(options, {
161
+ posted: true,
162
+ reason: "recorded",
163
+ httpStatus: response.status,
164
+ recordedCount: receipt.recorded_count,
165
+ })
166
+ };
167
+ }
168
+ return { reason: receipt.reason };
169
+ }
170
+ else {
171
+ const reason = `report_failed_http_${response.status}`;
172
+ if (isFinalHttpFailure(response.status)) {
173
+ return {
174
+ result: codexSessionReportChunkResult(options, {
175
+ posted: false,
176
+ reason,
177
+ httpStatus: response.status,
178
+ recordedCount: 0,
179
+ })
180
+ };
181
+ }
182
+ return { reason };
183
+ }
184
+ }
185
+ function logSessionReportTransportFailure(options, error, attempt, maxAttempts) {
186
+ // Logged per attempt, on purpose: the interesting failure is the one
187
+ // that repeats. Three identical ECONNRESETs and one DNS failure followed
188
+ // by two resets are different stories, and `report_network_error` alone
189
+ // tells neither (BLI-3238).
190
+ console.error("[session-reports] report attempt failed before a status came back", JSON.stringify({
191
+ reason: "report_network_error",
192
+ attempt,
193
+ max_attempts: maxAttempts,
194
+ batch_index: options.batchIndex,
195
+ session_count: options.sessions.length,
196
+ ...describeError(error),
197
+ }));
198
+ }
203
199
  function postSessionAttributionRequest(options) {
204
200
  return options.fetchImpl(`${options.dashboardUrl}/api/ambient/codex-sessions`, {
205
201
  method: "POST",