@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.
- package/dist/adapters/attribution-core-fallbacks.js +15 -11
- package/dist/adapters/attribution-core-score.js +111 -87
- package/dist/adapters/car-state.js +38 -35
- package/dist/adapters/claude-attribution-discovery.js +81 -56
- package/dist/adapters/claude-attribution-score.js +108 -94
- package/dist/adapters/claude-attribution-signals.js +52 -42
- package/dist/adapters/codex-attribution.js +42 -33
- package/dist/adapters/codex-session-signals.js +11 -9
- package/dist/adapters/git-state.js +56 -50
- package/dist/adapters/local-sources.js +52 -43
- package/dist/adapters/raw-evidence-git-diff.js +4 -2
- package/dist/adapters/raw-evidence-sanitize.js +15 -12
- package/dist/adapters/raw-evidence-transcript-file.js +32 -31
- package/dist/autostart-self-heal.js +18 -2
- package/dist/commands/agent-session-report.js +51 -59
- package/dist/commands/agent-setup-observed.js +30 -23
- package/dist/commands/agent-setup.js +96 -45
- package/dist/commands/autostart-heal.js +8 -2
- package/dist/commands/autostart-reading.js +3 -0
- package/dist/commands/backfill-evidence-outcomes.js +9 -6
- package/dist/commands/backfill-issues.js +3 -0
- package/dist/commands/backfill-scan.js +27 -22
- package/dist/commands/backfill-upload.js +69 -60
- package/dist/commands/backfill.js +26 -21
- package/dist/commands/brief-done.js +4 -1
- package/dist/commands/brief-edit.js +71 -46
- package/dist/commands/cal-detail.js +35 -0
- package/dist/commands/cal-overlay.js +106 -0
- package/dist/commands/cal.js +40 -23
- package/dist/commands/clean.js +4 -1
- package/dist/commands/docs-read.js +5 -2
- package/dist/commands/doctor-backfill.js +88 -71
- package/dist/commands/doctor-pipeline.js +59 -46
- package/dist/commands/doctor-usage-telemetry.js +2 -1
- package/dist/commands/heartbeat-post.js +116 -0
- package/dist/commands/heartbeat-token-lock.js +46 -33
- package/dist/commands/heartbeat.js +43 -125
- package/dist/commands/install-update.js +17 -15
- package/dist/commands/issue-show.js +27 -28
- package/dist/commands/issue-write.js +70 -23
- package/dist/commands/issue.js +68 -37
- package/dist/commands/jarvis-live-answer.js +99 -0
- package/dist/commands/jarvis-render.js +1 -94
- package/dist/commands/jarvis-trace.js +30 -22
- package/dist/commands/jarvis-turn-report.js +124 -0
- package/dist/commands/jarvis-turn.js +108 -182
- package/dist/commands/jarvis.js +26 -17
- package/dist/commands/local-args-collector-setup.js +82 -84
- package/dist/commands/local-args-collector-work.js +37 -38
- package/dist/commands/local-args-tower-admin.js +155 -142
- package/dist/commands/local-args-tower-cal.js +54 -48
- package/dist/commands/local-args-tower-careers.js +73 -54
- package/dist/commands/local-args-tower-docs-msg.js +56 -188
- package/dist/commands/local-args-tower-github.js +26 -20
- package/dist/commands/local-args-tower-hits.js +32 -20
- package/dist/commands/local-args-tower-manager.js +85 -4
- package/dist/commands/local-args-tower-memory.js +139 -117
- package/dist/commands/local-args-tower-msg.js +149 -0
- package/dist/commands/local-args-tower-notes.js +346 -0
- package/dist/commands/local-args-tower-pages.js +100 -378
- package/dist/commands/local-args-tower-settings.js +28 -29
- package/dist/commands/local-args-tower-work.js +52 -33
- package/dist/commands/local-args.js +80 -129
- package/dist/commands/local-help-commands-tower-ops.js +15 -4
- package/dist/commands/local-help-commands-tower.js +11 -4
- package/dist/commands/local-help-commands.js +342 -341
- package/dist/commands/local-help.js +87 -83
- package/dist/commands/local-run-guards.js +100 -0
- package/dist/commands/local.js +137 -206
- package/dist/commands/login.js +13 -9
- package/dist/commands/logs.js +55 -38
- package/dist/commands/mail-attachments.js +44 -26
- package/dist/commands/mail.js +6 -4
- package/dist/commands/manager-render-home.js +208 -0
- package/dist/commands/manager-render.js +7 -3
- package/dist/commands/manager-requests.js +33 -0
- package/dist/commands/manager.js +39 -5
- package/dist/commands/memory-consolidation.js +18 -9
- package/dist/commands/memory-daemon-probe.js +37 -28
- package/dist/commands/memory-install-config.js +64 -60
- package/dist/commands/memory-install.js +21 -41
- package/dist/commands/memory-requeue.js +26 -19
- package/dist/commands/memory-rules.js +26 -18
- package/dist/commands/memory-search-print.js +26 -0
- package/dist/commands/memory-store.js +14 -30
- package/dist/commands/memory-verb-list.js +49 -48
- package/dist/commands/msg.js +51 -9
- package/dist/commands/notes-accounts.js +49 -32
- package/dist/commands/notes-sync-loop.js +115 -89
- package/dist/commands/notes-wisprflow-login.js +44 -23
- package/dist/commands/notes-writes.js +115 -100
- package/dist/commands/notes.js +33 -55
- package/dist/commands/onboard-flows.js +69 -51
- package/dist/commands/onboard.js +11 -9
- package/dist/commands/ops-render-memory.js +10 -7
- package/dist/commands/ops-render.js +33 -30
- package/dist/commands/ops.js +3 -0
- package/dist/commands/public-root.js +1 -1
- package/dist/commands/session-sync-outside-roots.js +40 -36
- package/dist/commands/session-sync-plan.js +14 -10
- package/dist/commands/session-sync-upload.js +24 -22
- package/dist/commands/session-sync.js +19 -8
- package/dist/commands/slack.js +25 -16
- package/dist/commands/sources-check.js +13 -7
- package/dist/commands/sources-discovery.js +25 -17
- package/dist/commands/sources-verdict.js +19 -18
- package/dist/commands/speed.js +23 -19
- package/dist/commands/start.js +20 -16
- package/dist/commands/sync-doctor.js +52 -48
- package/dist/commands/sync.js +22 -16
- package/dist/commands/usage-telemetry-toml.js +71 -45
- package/dist/commands/usage.js +91 -67
- package/dist/commands/workbook-render.js +10 -7
- package/dist/evidence-reconcile-client.js +75 -64
- package/dist/evidence-redelivery-plan.js +37 -34
- package/dist/evidence-redelivery.js +32 -29
- package/dist/evidence-transcripts-client.js +20 -20
- package/dist/evidence-upload-object.js +86 -89
- package/dist/evidence-upload-plan.js +94 -83
- package/dist/evidence-upload-terminal.js +45 -50
- package/dist/image-description-session.js +156 -159
- package/dist/local-state-pairing-code.js +39 -31
- package/dist/local-state-status.js +9 -2
- package/dist/onboarding-roots.js +5 -8
- package/dist/process-runner.js +8 -8
- package/dist/raw-evidence-gc.js +33 -38
- package/dist/raw-evidence-stuck-summary.js +7 -14
- package/dist/repo-identity.js +26 -23
- package/dist/scheduled-self-update.js +11 -15
- package/dist/second-install.js +3 -0
- package/dist/slim/codex-rules.js +23 -36
- package/dist/sync-lock.js +5 -15
- package/dist/tower-client.js +36 -53
- package/dist/tower-stream.js +7 -24
- package/dist/upload-agent-artifacts.js +58 -64
- package/dist/upload-envelope-build.js +60 -59
- package/dist/upload-evidence-delivery-reconcile.js +58 -56
- package/dist/upload-evidence-delivery-summary.js +32 -34
- package/dist/upload-ingest-receipt.js +20 -23
- package/dist/upload-session-reports-queue.js +4 -1
- package/dist/upload-session-reports-wire.js +64 -68
- package/dist/upload-sync.js +171 -157
- 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
|
-
*
|
|
99
|
-
*
|
|
100
|
-
*
|
|
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
|
|
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(
|
|
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
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
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
|
-
|
|
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
|
|
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
|
-
*
|
|
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
|
-
*
|
|
115
|
-
*
|
|
116
|
-
*
|
|
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
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
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
|
-
|
|
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",
|