@vibe-commit/capture 0.6.1 → 0.7.0
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/README.md +80 -5
- package/dist/agent_hooks.d.ts +8 -0
- package/dist/agent_hooks.js +31 -1
- package/dist/agent_hooks.js.map +1 -1
- package/dist/agents/claude_code.d.ts +34 -0
- package/dist/agents/claude_code.js +141 -8
- package/dist/agents/claude_code.js.map +1 -1
- package/dist/agents/codex_cli.d.ts +13 -0
- package/dist/agents/codex_cli.js +241 -2
- package/dist/agents/codex_cli.js.map +1 -1
- package/dist/agents/cursor.d.ts +34 -0
- package/dist/agents/cursor.js +115 -1
- package/dist/agents/cursor.js.map +1 -1
- package/dist/agents/session_listing.d.ts +29 -0
- package/dist/agents/session_listing.js +49 -0
- package/dist/agents/session_listing.js.map +1 -0
- package/dist/agents/types.d.ts +118 -8
- package/dist/agents/types.js +17 -3
- package/dist/agents/types.js.map +1 -1
- package/dist/commands/approval_pending.d.ts +9 -0
- package/dist/commands/approval_pending.js +23 -0
- package/dist/commands/approval_pending.js.map +1 -0
- package/dist/commands/auth.js +31 -11
- package/dist/commands/auth.js.map +1 -1
- package/dist/commands/backfill.d.ts +160 -0
- package/dist/commands/backfill.js +748 -0
- package/dist/commands/backfill.js.map +1 -0
- package/dist/commands/browser_credential.d.ts +2 -0
- package/dist/commands/browser_credential.js +31 -14
- package/dist/commands/browser_credential.js.map +1 -1
- package/dist/commands/connect.d.ts +137 -0
- package/dist/commands/connect.js +203 -109
- package/dist/commands/connect.js.map +1 -1
- package/dist/commands/connect_picker.d.ts +27 -0
- package/dist/commands/connect_picker.js +415 -0
- package/dist/commands/connect_picker.js.map +1 -0
- package/dist/commands/context.d.ts +16 -0
- package/dist/commands/context.js +27 -0
- package/dist/commands/context.js.map +1 -1
- package/dist/commands/credential_problem.d.ts +15 -4
- package/dist/commands/credential_problem.js +29 -22
- package/dist/commands/credential_problem.js.map +1 -1
- package/dist/commands/device_credential.d.ts +2 -0
- package/dist/commands/device_credential.js +16 -14
- package/dist/commands/device_credential.js.map +1 -1
- package/dist/commands/finalize.d.ts +95 -0
- package/dist/commands/finalize.js +249 -74
- package/dist/commands/finalize.js.map +1 -1
- package/dist/commands/finish_mint.d.ts +2 -6
- package/dist/commands/finish_mint.js +10 -2
- package/dist/commands/finish_mint.js.map +1 -1
- package/dist/commands/report.js +38 -30
- package/dist/commands/report.js.map +1 -1
- package/dist/commands/scan.d.ts +162 -0
- package/dist/commands/scan.js +507 -0
- package/dist/commands/scan.js.map +1 -0
- package/dist/commands/status.js +119 -22
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/status_hooks.d.ts +14 -0
- package/dist/commands/status_hooks.js +81 -0
- package/dist/commands/status_hooks.js.map +1 -0
- package/dist/commands/status_machine.d.ts +18 -0
- package/dist/commands/status_machine.js +149 -0
- package/dist/commands/status_machine.js.map +1 -0
- package/dist/commands/why.js +44 -31
- package/dist/commands/why.js.map +1 -1
- package/dist/consent.d.ts +111 -0
- package/dist/consent.js +203 -1
- package/dist/consent.js.map +1 -1
- package/dist/copy/error_codes.d.ts +222 -0
- package/dist/copy/error_codes.js +105 -0
- package/dist/copy/error_codes.js.map +1 -0
- package/dist/copy/index.d.ts +2 -2
- package/dist/copy/index.js +2 -2
- package/dist/copy/index.js.map +1 -1
- package/dist/copy/strings.d.ts +497 -29
- package/dist/copy/strings.js +601 -35
- package/dist/copy/strings.js.map +1 -1
- package/dist/copy/time.d.ts +11 -0
- package/dist/copy/time.js +18 -0
- package/dist/copy/time.js.map +1 -1
- package/dist/credential.d.ts +5 -1
- package/dist/credential.js +2 -2
- package/dist/credential.js.map +1 -1
- package/dist/git.d.ts +43 -0
- package/dist/git.js +95 -0
- package/dist/git.js.map +1 -1
- package/dist/hooks/entry.d.ts +18 -1
- package/dist/hooks/entry.js +110 -13
- package/dist/hooks/entry.js.map +1 -1
- package/dist/hooks/post_commit.js +3 -3
- package/dist/hooks/post_commit.js.map +1 -1
- package/dist/hooks/post_rewrite.js +6 -4
- package/dist/hooks/post_rewrite.js.map +1 -1
- package/dist/hooks/session_start.d.ts +93 -0
- package/dist/hooks/session_start.js +199 -0
- package/dist/hooks/session_start.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +56 -8
- package/dist/index.js.map +1 -1
- package/dist/install.d.ts +57 -0
- package/dist/install.js +54 -20
- package/dist/install.js.map +1 -1
- package/dist/json.d.ts +32 -9
- package/dist/json.js +35 -8
- package/dist/json.js.map +1 -1
- package/dist/machine_state.d.ts +52 -0
- package/dist/machine_state.js +63 -0
- package/dist/machine_state.js.map +1 -0
- package/dist/oauth/session.d.ts +1 -0
- package/dist/org_pending.d.ts +52 -0
- package/dist/org_pending.js +109 -0
- package/dist/org_pending.js.map +1 -0
- package/dist/paths.d.ts +50 -0
- package/dist/paths.js +64 -0
- package/dist/paths.js.map +1 -1
- package/dist/picker.d.ts +180 -0
- package/dist/picker.js +477 -0
- package/dist/picker.js.map +1 -0
- package/dist/picker_checklist.d.ts +125 -0
- package/dist/picker_checklist.js +375 -0
- package/dist/picker_checklist.js.map +1 -0
- package/dist/policy.d.ts +54 -0
- package/dist/policy.js +47 -0
- package/dist/policy.js.map +1 -1
- package/dist/post.d.ts +50 -16
- package/dist/post.js +220 -16
- package/dist/post.js.map +1 -1
- package/dist/state.d.ts +6 -0
- package/dist/state.js +38 -1
- package/dist/state.js.map +1 -1
- package/dist/system_message.d.ts +8 -1
- package/dist/system_message.js +19 -1
- package/dist/system_message.js.map +1 -1
- package/dist/term.d.ts +28 -2
- package/dist/term.js +23 -2
- package/dist/term.js.map +1 -1
- package/dist/verb_help.d.ts +13 -0
- package/dist/verb_help.js +40 -0
- package/dist/verb_help.js.map +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +5 -9
package/dist/policy.js
CHANGED
|
@@ -55,6 +55,11 @@ export const STATUS_CLASSES = new Map([
|
|
|
55
55
|
// Org approval pending (D60 §D12). Capture has not stopped; it is waiting.
|
|
56
56
|
[403, "later"],
|
|
57
57
|
// Payload too large. Retrying the same bytes cannot work.
|
|
58
|
+
//
|
|
59
|
+
// ⚠ PR 4a: this row is now the verdict on a span that CANNOT BE SPLIT — a single record that alone exceeds a limit.
|
|
60
|
+
// `deliver()` halves a splittable span at a record boundary and sends the two halves, which are different bytes, so the
|
|
61
|
+
// reason above still holds for what reaches this table. Only a `deliver()` given no `RecordCutter` (a caller that
|
|
62
|
+
// cannot align) classifies a 413 here directly, and it is `never` there exactly as before.
|
|
58
63
|
[413, "never"],
|
|
59
64
|
// Rate limited. The definition of transient.
|
|
60
65
|
[429, "later"],
|
|
@@ -150,6 +155,48 @@ function positiveOr(raw, fallback) {
|
|
|
150
155
|
const parsed = Number(raw);
|
|
151
156
|
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
152
157
|
}
|
|
158
|
+
// ---------------------------------------------------------------------------
|
|
159
|
+
// Delta sizing — PR 4a. One request carries one DELTA, never the whole span to EOF.
|
|
160
|
+
// ---------------------------------------------------------------------------
|
|
161
|
+
/**
|
|
162
|
+
* The most RAW transcript bytes one delta carries. The server's own ceiling is 16 MiB decompressed
|
|
163
|
+
* (`MAX_DELTA_BYTES`, `ingest_session.ts:146`); half of it leaves room for redaction markers and for a record that
|
|
164
|
+
* straddles the cut.
|
|
165
|
+
*/
|
|
166
|
+
export const MAX_DELTA_RAW_BYTES = 8 * 1024 * 1024;
|
|
167
|
+
/**
|
|
168
|
+
* The most COMPRESSED bytes one delta's frame may be. The server refuses a request body over 1 MiB (`MAX_BODY_BYTES`,
|
|
169
|
+
* `src/transport/server.ts:115`, a 413); 960 KiB leaves the rest for the framing the body does not include.
|
|
170
|
+
*/
|
|
171
|
+
export const MAX_DELTA_FRAME_BYTES = 960 * 1024;
|
|
172
|
+
/**
|
|
173
|
+
* Cut the next delta from `from`: everything to `eof` when it fits, else up to the last record boundary inside `maxRaw`.
|
|
174
|
+
*
|
|
175
|
+
* Pure; the cutter is the only thing that touches a file.
|
|
176
|
+
*/
|
|
177
|
+
export function planChunk(cutter, from, eof, maxRaw = MAX_DELTA_RAW_BYTES) {
|
|
178
|
+
if (eof - from <= maxRaw)
|
|
179
|
+
return { kind: "span", to: eof };
|
|
180
|
+
const boundary = cutter.lastBoundary(from, from + maxRaw);
|
|
181
|
+
if (boundary !== null)
|
|
182
|
+
return { kind: "span", to: boundary };
|
|
183
|
+
const end = cutter.firstBoundary(from);
|
|
184
|
+
return end === null ? { kind: "wait" } : { kind: "oversize", to: end };
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Where to cut `[from, to)` in two, at a record boundary, or `null` when the span is a single record and cannot be.
|
|
188
|
+
*
|
|
189
|
+
* Halves by bytes, snapping back to the last boundary at or before the middle. When the first record is longer than
|
|
190
|
+
* half, the cut is that record's end, so the oversize record goes out alone and a second 413 on it is final.
|
|
191
|
+
*/
|
|
192
|
+
export function splitPoint(cutter, from, to) {
|
|
193
|
+
const middle = from + Math.floor((to - from) / 2);
|
|
194
|
+
const boundary = cutter.lastBoundary(from, middle);
|
|
195
|
+
if (boundary !== null)
|
|
196
|
+
return boundary;
|
|
197
|
+
const end = cutter.firstBoundary(from);
|
|
198
|
+
return end !== null && end < to ? end : null;
|
|
199
|
+
}
|
|
153
200
|
/** Undelivered bytes currently held. */
|
|
154
201
|
export function pendingBytes(state) {
|
|
155
202
|
if (state.backlog.length === 0)
|
package/dist/policy.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"policy.js","sourceRoot":"","sources":["../src/policy.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"policy.js","sourceRoot":"","sources":["../src/policy.ts"],"names":[],"mappings":"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"}
|
package/dist/post.d.ts
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
*/
|
|
20
20
|
import { type SelectedAgentId } from "./agents/types.js";
|
|
21
21
|
import type { IngestCredential } from "./credential.js";
|
|
22
|
-
import { type Disposition } from "./policy.js";
|
|
22
|
+
import { type Disposition, type RecordCutter } from "./policy.js";
|
|
23
23
|
import { type InferredGroupId, type Observed } from "./spool.js";
|
|
24
24
|
/** Production data plane. Overridable for tests and for a self-hosted server. */
|
|
25
25
|
export declare const DEFAULT_INGEST_URL = "https://api.vibecommit.ai/ingest/v1/session";
|
|
@@ -360,8 +360,29 @@ export type Delivery =
|
|
|
360
360
|
readonly inferredSetAsidePairs: number;
|
|
361
361
|
/** `X-Credential-Expires` from the 2xx that delivered this, or `null`/absent. See `SendOutcome`. */
|
|
362
362
|
readonly credentialExpires?: string | null;
|
|
363
|
-
/**
|
|
363
|
+
/**
|
|
364
|
+
* `conversation_id` from a 2xx, ONLY when the caller opted in. See `SendOutcome`.
|
|
365
|
+
*
|
|
366
|
+
* PR 4a (DX D8): a delivery of several deltas carries the id from whichever delta was acknowledged with it, even when
|
|
367
|
+
* a LATER delta came back `later`, so `connect`'s session link does not fall back to `/app/commits` on a partial run.
|
|
368
|
+
*/
|
|
364
369
|
readonly conversationId?: string;
|
|
370
|
+
/**
|
|
371
|
+
* PR 4a — records skipped because one record ALONE exceeded a limit (`record too large`): over `limits.rawBytes`
|
|
372
|
+
* raw, or over `limits.frameBytes` compressed, or refused with a 413 that no split can shrink. Each was written off
|
|
373
|
+
* with `markSkipped`, so it is in the gap counters `status` reports as "skipped … will not be sent later".
|
|
374
|
+
*/
|
|
375
|
+
readonly recordsTooLarge?: number;
|
|
376
|
+
/**
|
|
377
|
+
* PR 5 — set when the last attempt got NO response at all (a refused connection, a timeout), so a caller can tell a server
|
|
378
|
+
* that could not be reached apart from one that answered and asked to wait. Absent whenever a response arrived. It
|
|
379
|
+
* changes no decision here: both are `later`.
|
|
380
|
+
*/
|
|
381
|
+
readonly unreachable?: true;
|
|
382
|
+
/** PR 4a — deltas this call acknowledged or skipped past, when more than one. */
|
|
383
|
+
readonly deltas?: number;
|
|
384
|
+
/** PR 4a — `true` when the call stopped on the hook's budget with bytes still unsent. The offset is where it left off. */
|
|
385
|
+
readonly more?: boolean;
|
|
365
386
|
};
|
|
366
387
|
export interface DeliveryContext {
|
|
367
388
|
readonly home: string;
|
|
@@ -444,23 +465,36 @@ export interface DeliveryContext {
|
|
|
444
465
|
* reads, awaits or parses a 2xx body.
|
|
445
466
|
*/
|
|
446
467
|
readonly readConversationId?: boolean;
|
|
468
|
+
/**
|
|
469
|
+
* PR 4a — WHEN THE HOOK'S SEND BUDGET ENDS, epoch ms. Set by the hook path only. `deliver()` starts a further delta (or a
|
|
470
|
+
* 413 split's retry) only while at least `MIN_NEXT_DELTA_BUDGET_MS` of it remains, and leaves the rest of the span for the
|
|
471
|
+
* next event or `vibecommit finalize`. Unset (`finalize`, `connect`) means no deadline.
|
|
472
|
+
*/
|
|
473
|
+
readonly deadlineAtMs?: number;
|
|
474
|
+
/** PR 4a — the clock `deadlineAtMs` is measured on. A test seam; defaults to `Date.now`. */
|
|
475
|
+
readonly clock?: () => number;
|
|
476
|
+
/** PR 4a — the delta size limits. A test seam; defaults to the server-derived constants in `policy.ts`. */
|
|
477
|
+
readonly limits?: {
|
|
478
|
+
readonly rawBytes: number;
|
|
479
|
+
readonly frameBytes: number;
|
|
480
|
+
};
|
|
447
481
|
}
|
|
448
482
|
/**
|
|
449
|
-
* Send the undelivered span of one transcript file
|
|
483
|
+
* Send the undelivered span of one transcript file, in deltas — PR 4a.
|
|
450
484
|
*
|
|
451
|
-
*
|
|
452
|
-
*
|
|
453
|
-
*
|
|
454
|
-
* that already makes 1 + up to `FINALIZE_DRAIN_MAX_ITERATIONS` requests by
|
|
455
|
-
* design (VL's explicit ruling, `TODOS[182]`). On the hook path, `later` still
|
|
456
|
-
* does not retry here; it leaves the offset where it is so the hook that fires
|
|
457
|
-
* on the NEXT turn picks the same bytes up.
|
|
485
|
+
* Without a `cutter` this is the single request it always was: everything from the offset to EOF, at most one attempt.
|
|
486
|
+
* With one (every production caller), the span goes out as record-aligned deltas, each acknowledged before the offset
|
|
487
|
+
* moves past it:
|
|
458
488
|
*
|
|
459
|
-
*
|
|
460
|
-
*
|
|
489
|
+
* - **A hook sends deltas only while its budget lasts** (`ctx.deadlineAtMs`) and leaves the rest for its next event or
|
|
490
|
+
* `vibecommit finalize`; `Delivery.more` says it stopped early. `finalize` and `connect` set no deadline and run on.
|
|
491
|
+
* - **It stops at the first delta that is not `ok`** and returns that delta's outcome: `later` and `fatal` hold exactly
|
|
492
|
+
* the bytes that failed, so a resume starts at the next unacknowledged delta, with no resend and no gap. A
|
|
493
|
+
* `capture_not_approved` 403 on any delta is such a stop, so its pending beat is printed once.
|
|
494
|
+
* - **A skipped oversize record does not stop it.** The records after it still go out.
|
|
495
|
+
* - **`connect`'s conversation id survives a partial run** (DX D8): it is read from the first 2xx that carries it, no later
|
|
496
|
+
* delta reads a body, and it is returned even when a later delta came back `later`.
|
|
461
497
|
*
|
|
462
|
-
*
|
|
463
|
-
* is the one that has confined `transcript_path` to the Claude Code root
|
|
464
|
-
* (`/cso` finding 1). This module never opens a file.
|
|
498
|
+
* ⛔ A 2xx never changes a delta's outcome, and a hook reads no body (eng-2 D2): the only body read is `connect`'s opt-in.
|
|
465
499
|
*/
|
|
466
|
-
export declare function deliver(ctx: DeliveryContext, eof: number, readBody: (from: number, to: number) => Uint8Array | null): Promise<Delivery>;
|
|
500
|
+
export declare function deliver(ctx: DeliveryContext, eof: number, readBody: (from: number, to: number) => Uint8Array | null, cutter?: RecordCutter): Promise<Delivery>;
|
package/dist/post.js
CHANGED
|
@@ -22,7 +22,8 @@ import { agentForTranscript } from "./agents/registry.js";
|
|
|
22
22
|
import { UNKNOWN_AGENT_ID } from "./agents/types.js";
|
|
23
23
|
import { headerSafe, sessionIdSafe } from "./header_safety.js";
|
|
24
24
|
import { recordInferredSkipped } from "./inferred_tally.js";
|
|
25
|
-
import {
|
|
25
|
+
import { clearOrgPending, markOrgPending } from "./org_pending.js";
|
|
26
|
+
import { MAX_DELTA_FRAME_BYTES, MAX_DELTA_RAW_BYTES, classify, markDelivered, markHeld, markSkipped, nextSpan, planChunk, resolveCaps, splitPoint, } from "./policy.js";
|
|
26
27
|
import { fileState, isStopped, loadSessionState, saveSessionState, withFileState, } from "./state.js";
|
|
27
28
|
import { INFERRED_SET_ASIDE_AFTER, dropInferredGroups, dropRewrites, dropSpooled, isFullSha, recordInferredAttempt, setAsideInferredGroup, settleSpooled, } from "./spool.js";
|
|
28
29
|
import { CLIENT_VERSION, CLIENT_VERSION_HEADER } from "./version.js";
|
|
@@ -498,6 +499,15 @@ function parseFinalAck(raw) {
|
|
|
498
499
|
const v = raw?.trim().toLowerCase();
|
|
499
500
|
return v === "sealed" || v === "already" || v === "refused" ? v : null;
|
|
500
501
|
}
|
|
502
|
+
/**
|
|
503
|
+
* PR 4a — below this much remaining hook budget, do not START another delta. The same floor the sub-agent loop uses
|
|
504
|
+
* (`MIN_SUBAGENT_BUDGET_MS`, `hooks/entry.ts`), for the same reason: a request that small can only time out.
|
|
505
|
+
*/
|
|
506
|
+
const MIN_NEXT_DELTA_BUDGET_MS = 400;
|
|
507
|
+
/** The floor under one attempt's timeout once the hook budget is nearly or fully spent — `MIN_SEND_TIMEOUT_MS` in `hooks/entry.ts`. */
|
|
508
|
+
const MIN_ATTEMPT_TIMEOUT_MS = 50;
|
|
509
|
+
/** The share of what is left one further attempt may take — `SEND_BUDGET_FRACTION`'s value in `hooks/entry.ts`. */
|
|
510
|
+
const NEXT_DELTA_BUDGET_FRACTION = 0.6;
|
|
501
511
|
/**
|
|
502
512
|
* Build the delta, reduce it to the deny-list set when `lean`, validate every
|
|
503
513
|
* resulting header value, and only then call `send()` — `TODOS[182]`.
|
|
@@ -508,7 +518,12 @@ function parseFinalAck(raw) {
|
|
|
508
518
|
* by elimination, always names a REQUIRED header — there is no second
|
|
509
519
|
* optional one for the caller to drop.
|
|
510
520
|
*/
|
|
511
|
-
async function attemptSend(ctx, agent, seq, span, body, lean
|
|
521
|
+
async function attemptSend(ctx, agent, seq, span, body, lean,
|
|
522
|
+
// PR 4a. ⛔ ONLY THE DELTA THAT ENDS AT EOF CARRIES THE SESSION-LEVEL HEADERS. `X-Session-Final` seals the open turn, and
|
|
523
|
+
// sealing before the rest of the span has arrived would seal a poorer turn (`finalize.ts`'s docblock). The commits,
|
|
524
|
+
// rewrites and head describe the END of the session, and `capture_commits`' PK admits one row per (capture, sha), so they
|
|
525
|
+
// ride on one delta and not on every one. A delta that is not the last sends none of them, and the spool keeps them.
|
|
526
|
+
isLast = true) {
|
|
512
527
|
const full = buildIngestHeaders(ctx.credential, {
|
|
513
528
|
sessionId: ctx.sessionId,
|
|
514
529
|
seq,
|
|
@@ -516,11 +531,11 @@ async function attemptSend(ctx, agent, seq, span, body, lean) {
|
|
|
516
531
|
fileKey: ctx.fileKey,
|
|
517
532
|
repoSlug: ctx.repoSlug,
|
|
518
533
|
agent,
|
|
519
|
-
final: ctx.final === true,
|
|
520
|
-
head: ctx.head,
|
|
521
|
-
commits: ctx.commits,
|
|
522
|
-
rewrites: ctx.rewrites,
|
|
523
|
-
inferredRewrites: ctx.inferredRewrites,
|
|
534
|
+
final: ctx.final === true && isLast,
|
|
535
|
+
head: isLast ? ctx.head : undefined,
|
|
536
|
+
commits: isLast ? ctx.commits : undefined,
|
|
537
|
+
rewrites: isLast ? ctx.rewrites : undefined,
|
|
538
|
+
inferredRewrites: isLast ? ctx.inferredRewrites : undefined,
|
|
524
539
|
});
|
|
525
540
|
const headers = lean ? leanHeaders(full) : full;
|
|
526
541
|
const badHeader = firstUnsafeHeader(headers);
|
|
@@ -593,8 +608,10 @@ function holdLean(ctx, key, session, current, span, caps) {
|
|
|
593
608
|
/**
|
|
594
609
|
* Send the undelivered span of one transcript file and apply the failure policy.
|
|
595
610
|
*
|
|
596
|
-
* **At most one NETWORK attempt per hook, still — exactly D56 §D8's rule.**
|
|
597
|
-
* `
|
|
611
|
+
* **At most one NETWORK attempt per hook, still — exactly D56 §D8's rule — FOR ONE DELTA.** PR 4a:
|
|
612
|
+
* `deliver()` (below) calls this once per delta, and a hook calls it again only while its budget lasts; a 413 split's
|
|
613
|
+
* retry is budget-gated the same way and is a DIFFERENT, smaller body, so it is not the retry of the same bytes §D8
|
|
614
|
+
* forbids. A `finalize` lean retry (below) is the one exception D56 §D8 itself does not
|
|
598
615
|
* cover: it governs HOOKS, and `finalize` is a synchronous, user-invoked verb
|
|
599
616
|
* that already makes 1 + up to `FINALIZE_DRAIN_MAX_ITERATIONS` requests by
|
|
600
617
|
* design (VL's explicit ruling, `TODOS[182]`). On the hook path, `later` still
|
|
@@ -608,7 +625,7 @@ function holdLean(ctx, key, session, current, span, caps) {
|
|
|
608
625
|
* is the one that has confined `transcript_path` to the Claude Code root
|
|
609
626
|
* (`/cso` finding 1). This module never opens a file.
|
|
610
627
|
*/
|
|
611
|
-
|
|
628
|
+
async function deliverChunk(ctx, eof, readBody, cutter) {
|
|
612
629
|
// `CR-017d`. The consent gate already refuses a `cwd` outside a work tree, so
|
|
613
630
|
// the hook cannot reach this — but the ledger's isolation must not rest on a
|
|
614
631
|
// check in another module, the same discipline `consent.ts` states about its
|
|
@@ -630,10 +647,41 @@ export async function deliver(ctx, eof, readBody) {
|
|
|
630
647
|
}
|
|
631
648
|
if (span === null)
|
|
632
649
|
return { kind: "nothing-to-send" };
|
|
633
|
-
const
|
|
650
|
+
const caps = resolveCaps(ctx.env);
|
|
651
|
+
const limits = ctx.limits ?? { rawBytes: MAX_DELTA_RAW_BYTES, frameBytes: MAX_DELTA_FRAME_BYTES };
|
|
652
|
+
// ⭐ PR 4a — ONE DELTA, NOT THE WHOLE SPAN TO EOF. Without a `cutter` this is the old single-span path, byte for byte.
|
|
653
|
+
//
|
|
654
|
+
// The delta is cut on a record boundary (the server drops a trailing partial line), at most `limits.rawBytes` raw, and
|
|
655
|
+
// measured AFTER compression: a frame over `limits.frameBytes` is halved at a boundary and measured again. A record that
|
|
656
|
+
// alone exceeds either limit cannot be split, so it is skipped and COUNTED (`markSkipped`: the gap counters `status`
|
|
657
|
+
// reports as "skipped … will not be sent later"), never retried — retrying the same bytes cannot work.
|
|
658
|
+
//
|
|
659
|
+
// A cut that is NOT at EOF is not the last delta (`isLast`), so it carries none of the session-level headers.
|
|
660
|
+
if (cutter !== undefined && span.to > span.from) {
|
|
661
|
+
const plan = planChunk(cutter, span.from, span.to, limits.rawBytes);
|
|
662
|
+
// An unterminated record longer than the limit, still at EOF: it may be growing. Leave it for the next event.
|
|
663
|
+
if (plan.kind === "wait")
|
|
664
|
+
return { kind: "nothing-to-send" };
|
|
665
|
+
if (plan.kind === "oversize")
|
|
666
|
+
return recordTooLarge(ctx, key, session, current, { from: span.from, to: plan.to }, caps);
|
|
667
|
+
span = { from: span.from, to: plan.to };
|
|
668
|
+
}
|
|
669
|
+
let body = readBody(span.from, span.to);
|
|
634
670
|
if (body === null)
|
|
635
671
|
return { kind: "nothing-to-send" };
|
|
636
|
-
|
|
672
|
+
if (cutter !== undefined) {
|
|
673
|
+
while (body.length > limits.frameBytes) {
|
|
674
|
+
const at = splitPoint(cutter, span.from, span.to);
|
|
675
|
+
if (at === null)
|
|
676
|
+
return recordTooLarge(ctx, key, session, current, span, caps);
|
|
677
|
+
span = { from: span.from, to: at };
|
|
678
|
+
body = readBody(span.from, span.to);
|
|
679
|
+
if (body === null)
|
|
680
|
+
return { kind: "nothing-to-send" };
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
// An empty span (a `final` with nothing new) is the last delta by definition.
|
|
684
|
+
let isLast = cutter === undefined || span.to >= eof || span.to === span.from;
|
|
637
685
|
// `seq` is claimed BEFORE the attempt and persisted by the SWITCH below (every ordinary disposition —
|
|
638
686
|
// "ok"/"later"/"never"/"fatal" — builds `next` from `{ ...session, seq, … }`), so it is monotonic per
|
|
639
687
|
// session across retries once a request was actually classified. A seq reused after a failure would read
|
|
@@ -673,7 +721,7 @@ export async function deliver(ctx, eof, readBody) {
|
|
|
673
721
|
// full and lean requests depending on what the NEXT turn happens to contain,
|
|
674
722
|
// which nobody can debug.
|
|
675
723
|
let lean = current.lean;
|
|
676
|
-
let attempt = await attemptSend(ctx, agent, seq, span, body, lean);
|
|
724
|
+
let attempt = await attemptSend(withinBudget(ctx), agent, seq, span, body, lean, isLast);
|
|
677
725
|
if (attempt.kind === "unsafe") {
|
|
678
726
|
return refuse(ctx, key, session, current, span, caps, attempt.header === "authorization" ? "fatal" : "never", lean);
|
|
679
727
|
}
|
|
@@ -688,7 +736,7 @@ export async function deliver(ctx, eof, readBody) {
|
|
|
688
736
|
if (outcome.kind === "response" && outcome.status === 431) {
|
|
689
737
|
if (lean) {
|
|
690
738
|
// Already lean and STILL 431: nothing client-side can shrink this
|
|
691
|
-
// further. `never`, exactly as 413 — the one case `never` is correct
|
|
739
|
+
// further. `never`, exactly as an UNSPLITTABLE 413 (a single record) — the one case `never` is correct
|
|
692
740
|
// here, because it really is per-body now.
|
|
693
741
|
return refuse(ctx, key, session, current, span, caps, "never", true);
|
|
694
742
|
}
|
|
@@ -700,7 +748,7 @@ export async function deliver(ctx, eof, readBody) {
|
|
|
700
748
|
// in-process retry here does not reopen D56 §D8, which governs hooks. Go
|
|
701
749
|
// lean and retry ONCE, within this same invocation.
|
|
702
750
|
lean = true;
|
|
703
|
-
attempt = await attemptSend(ctx, agent, seq, span, body, lean);
|
|
751
|
+
attempt = await attemptSend(withinBudget(ctx), agent, seq, span, body, lean, isLast);
|
|
704
752
|
if (attempt.kind === "unsafe") {
|
|
705
753
|
// Unreachable in practice — every lean header is already proven safe, or
|
|
706
754
|
// it would never have reached the full build either — but stated rather
|
|
@@ -718,6 +766,35 @@ export async function deliver(ctx, eof, readBody) {
|
|
|
718
766
|
// true — the `"ok"` case's accounting reads it to skip settling data this
|
|
719
767
|
// attempt never put on the wire.
|
|
720
768
|
}
|
|
769
|
+
// ⭐ PR 4a — A 413 IS NO LONGER A WRITE-OFF WHEN THE SPAN CAN BE SPLIT. Halve it at a record boundary and send the first half
|
|
770
|
+
// as a different, smaller request. A 413 is refused before the server applies anything (`server.ts`'s body limit, then the
|
|
771
|
+
// decompressed-size check), so the SAME `seq` is reused: the server never recorded it, and it is not a replay.
|
|
772
|
+
//
|
|
773
|
+
// ⛔ Only a span that is a single record falls through to `classify`, where 413 is `never`: the bytes cannot be made
|
|
774
|
+
// smaller, so skipping them is correct and the record is counted. Everything else here sends bytes the 413 did not see.
|
|
775
|
+
//
|
|
776
|
+
// Each retry is gated on the hook's remaining budget (`budgetLeftMs`), so this is not D56 §D8's forbidden synchronous
|
|
777
|
+
// retry of the SAME bytes: it is bounded by the budget the hook already has, and out of budget it HOLDS the span.
|
|
778
|
+
let splitTooLarge = false;
|
|
779
|
+
while (cutter !== undefined && outcome.kind === "response" && outcome.status === 413) {
|
|
780
|
+
const at = splitPoint(cutter, span.from, span.to);
|
|
781
|
+
if (at === null) {
|
|
782
|
+
splitTooLarge = true;
|
|
783
|
+
break;
|
|
784
|
+
}
|
|
785
|
+
if (budgetLeftMs(ctx) < MIN_NEXT_DELTA_BUDGET_MS)
|
|
786
|
+
return holdSpan(ctx, key, session, current, span, caps, lean);
|
|
787
|
+
span = { from: span.from, to: at };
|
|
788
|
+
isLast = false;
|
|
789
|
+
const half = readBody(span.from, span.to);
|
|
790
|
+
if (half === null)
|
|
791
|
+
return { kind: "nothing-to-send" };
|
|
792
|
+
attempt = await attemptSend(withinBudget(ctx), agent, seq, span, half, lean, false);
|
|
793
|
+
if (attempt.kind === "unsafe") {
|
|
794
|
+
return refuse(ctx, key, session, current, span, caps, attempt.header === "authorization" ? "fatal" : "never", lean);
|
|
795
|
+
}
|
|
796
|
+
outcome = attempt.outcome;
|
|
797
|
+
}
|
|
721
798
|
const disposition = classify(outcome);
|
|
722
799
|
let next = ctx.byHook ? { ...session, seq, liveAt: ctx.nowMs } : { ...session, seq };
|
|
723
800
|
let inferredSetAsidePairs = 0;
|
|
@@ -797,7 +874,9 @@ export async function deliver(ctx, eof, readBody) {
|
|
|
797
874
|
// sendable, for a reason unrelated to `lean`) entries are UNAFFECTED by this gate: they are consumed
|
|
798
875
|
// only via a NORMAL, non-lean call's `capSpool`/`capSuccessors`/`capInferred`, which this gate does not
|
|
799
876
|
// touch, so a lean session's held lines still drain exactly as soon as a non-lean request reads them.
|
|
800
|
-
|
|
877
|
+
// PR 4a: AND ONLY ON THE DELTA THAT CARRIED THEM (`isLast`). A delta earlier in the span sent none of the commits,
|
|
878
|
+
// rewrites or inferred pairs, so a 2xx for it says nothing about them and the spool must keep them for the delta that did.
|
|
879
|
+
if (!lean && isLast) {
|
|
801
880
|
const retry = outcome.kind === "response" ? outcome.commitsRetry : null;
|
|
802
881
|
const nothingLanded = outcome.kind === "response" && outcome.captureId === "";
|
|
803
882
|
const holdForNextDelta = nothingLanded && (ctx.commits?.shas.length ?? 0) > 0;
|
|
@@ -880,6 +959,14 @@ export async function deliver(ctx, eof, readBody) {
|
|
|
880
959
|
break;
|
|
881
960
|
}
|
|
882
961
|
saveSessionState(ctx.home, key, next);
|
|
962
|
+
// 0.7.0: remember, for `status`, that THIS repo's upload was refused because its org has not approved capture — and
|
|
963
|
+
// forget it on the next delivery. Only that one error code sets it; a 5xx or a timeout neither sets nor clears it.
|
|
964
|
+
if (disposition === "ok")
|
|
965
|
+
clearOrgPending(ctx.home, ctx.repoSlug);
|
|
966
|
+
else if (disposition === "later" && outcome.kind === "response" && outcome.detail?.code === CAPTURE_NOT_APPROVED) {
|
|
967
|
+
// The slug is the CLIENT's (`ctx.repoSlug`), never the response body's: the 403 names nobody.
|
|
968
|
+
markOrgPending(ctx.home, ctx.nowMs, outcome.detail.ownersNotified ?? null, ctx.repoSlug);
|
|
969
|
+
}
|
|
883
970
|
// `outcome.detail` is null on every path except a non-2xx with a parseable
|
|
884
971
|
// error body, so this is a pass-through and not a second decision.
|
|
885
972
|
return {
|
|
@@ -894,6 +981,123 @@ export async function deliver(ctx, eof, readBody) {
|
|
|
894
981
|
...(outcome.kind === "response" && disposition === "ok" && outcome.conversationId !== undefined
|
|
895
982
|
? { conversationId: outcome.conversationId }
|
|
896
983
|
: {}),
|
|
984
|
+
...(outcome.kind === "unreachable" ? { unreachable: true } : {}),
|
|
985
|
+
// PR 4a: a 413 that no split could shrink was skipped by the `never` arm above; say so, so the caller can count it.
|
|
986
|
+
...(splitTooLarge && disposition === "never" ? { recordsTooLarge: 1 } : {}),
|
|
897
987
|
};
|
|
898
988
|
}
|
|
989
|
+
/**
|
|
990
|
+
* PR 4a — a single record that alone exceeds a delta limit. Nothing is sent: the same bytes would be refused, so the record
|
|
991
|
+
* is skipped, the gap is stamped (`markSkipped`, which `status` reports), and the offset moves past it. `refuse` does
|
|
992
|
+
* exactly that and persists it; this adds the count.
|
|
993
|
+
*/
|
|
994
|
+
function recordTooLarge(ctx, key, session, current, span, caps) {
|
|
995
|
+
const skipped = refuse(ctx, key, session, current, span, caps, "never", current.lean);
|
|
996
|
+
return skipped.kind === "attempted" ? { ...skipped, recordsTooLarge: 1 } : skipped;
|
|
997
|
+
}
|
|
998
|
+
/**
|
|
999
|
+
* PR 4a — out of hook budget in the middle of a 413 split: hold the span the failed request covered, as a `later` does. The
|
|
1000
|
+
* `seq` is not persisted for the same reason the split reuses it: the 413 was refused before the server applied it.
|
|
1001
|
+
*/
|
|
1002
|
+
function holdSpan(ctx, key, session, current, span, caps, lean) {
|
|
1003
|
+
saveSessionState(ctx.home, key, withFileState(session, ctx.fileKey, { ...markHeld(current, span, ctx.nowMs, caps), lean }));
|
|
1004
|
+
return { kind: "attempted", disposition: "later", detail: null, finalAck: null, inferredStored: null, inferredSetAsidePairs: 0 };
|
|
1005
|
+
}
|
|
1006
|
+
/** Hook budget left for further sends, ms. `Infinity` when the caller set no deadline (`finalize`, `connect`). */
|
|
1007
|
+
function budgetLeftMs(ctx) {
|
|
1008
|
+
return ctx.deadlineAtMs === undefined ? Infinity : ctx.deadlineAtMs - (ctx.clock ?? Date.now)();
|
|
1009
|
+
}
|
|
1010
|
+
/**
|
|
1011
|
+
* `ctx` with this attempt's timeout trimmed to what is left of the hook budget (unchanged when there is no deadline).
|
|
1012
|
+
*
|
|
1013
|
+
* ⛔ CALLED RIGHT BEFORE EACH `attemptSend`, never ahead of the read. The read, the redaction and the compression of a delta
|
|
1014
|
+
* (and every halving pass) take real time, and a timeout fixed before them lets the last delta end past `deadlineAtMs` by
|
|
1015
|
+
* exactly that. Floored like `sendTimeoutMs` (`hooks/entry.ts`): when the budget is already gone the watchdog is the real
|
|
1016
|
+
* limit, and a zero timeout would only abort a send that was about to be refused anyway.
|
|
1017
|
+
*/
|
|
1018
|
+
function withinBudget(ctx) {
|
|
1019
|
+
const left = budgetLeftMs(ctx);
|
|
1020
|
+
if (!Number.isFinite(left))
|
|
1021
|
+
return ctx;
|
|
1022
|
+
return { ...ctx, timeoutMs: Math.max(MIN_ATTEMPT_TIMEOUT_MS, Math.min(ctx.timeoutMs, Math.floor(left * NEXT_DELTA_BUDGET_FRACTION))) };
|
|
1023
|
+
}
|
|
1024
|
+
/**
|
|
1025
|
+
* Send the undelivered span of one transcript file, in deltas — PR 4a.
|
|
1026
|
+
*
|
|
1027
|
+
* Without a `cutter` this is the single request it always was: everything from the offset to EOF, at most one attempt.
|
|
1028
|
+
* With one (every production caller), the span goes out as record-aligned deltas, each acknowledged before the offset
|
|
1029
|
+
* moves past it:
|
|
1030
|
+
*
|
|
1031
|
+
* - **A hook sends deltas only while its budget lasts** (`ctx.deadlineAtMs`) and leaves the rest for its next event or
|
|
1032
|
+
* `vibecommit finalize`; `Delivery.more` says it stopped early. `finalize` and `connect` set no deadline and run on.
|
|
1033
|
+
* - **It stops at the first delta that is not `ok`** and returns that delta's outcome: `later` and `fatal` hold exactly
|
|
1034
|
+
* the bytes that failed, so a resume starts at the next unacknowledged delta, with no resend and no gap. A
|
|
1035
|
+
* `capture_not_approved` 403 on any delta is such a stop, so its pending beat is printed once.
|
|
1036
|
+
* - **A skipped oversize record does not stop it.** The records after it still go out.
|
|
1037
|
+
* - **`connect`'s conversation id survives a partial run** (DX D8): it is read from the first 2xx that carries it, no later
|
|
1038
|
+
* delta reads a body, and it is returned even when a later delta came back `later`.
|
|
1039
|
+
*
|
|
1040
|
+
* ⛔ A 2xx never changes a delta's outcome, and a hook reads no body (eng-2 D2): the only body read is `connect`'s opt-in.
|
|
1041
|
+
*/
|
|
1042
|
+
export async function deliver(ctx, eof, readBody, cutter) {
|
|
1043
|
+
if (cutter === undefined)
|
|
1044
|
+
return deliverChunk(ctx, eof, readBody, undefined);
|
|
1045
|
+
const key = { repoKey: ctx.repoKey, sessionId: ctx.sessionId };
|
|
1046
|
+
let chunkCtx = ctx;
|
|
1047
|
+
let conversationId;
|
|
1048
|
+
let tooLarge = 0;
|
|
1049
|
+
let deltas = 0;
|
|
1050
|
+
let last = null;
|
|
1051
|
+
const carry = (d, more = false) => d.kind !== "attempted"
|
|
1052
|
+
? d
|
|
1053
|
+
: {
|
|
1054
|
+
...d,
|
|
1055
|
+
...(d.conversationId === undefined && conversationId !== undefined ? { conversationId } : {}),
|
|
1056
|
+
...(tooLarge > 0 ? { recordsTooLarge: tooLarge } : {}),
|
|
1057
|
+
...(deltas > 1 ? { deltas } : {}),
|
|
1058
|
+
...(more ? { more: true } : {}),
|
|
1059
|
+
};
|
|
1060
|
+
for (;;) {
|
|
1061
|
+
const before = fileState(loadSessionState(ctx.home, key), ctx.fileKey).sentOffset;
|
|
1062
|
+
let delivery;
|
|
1063
|
+
try {
|
|
1064
|
+
delivery = await deliverChunk(chunkCtx, eof, readBody, cutter);
|
|
1065
|
+
}
|
|
1066
|
+
catch {
|
|
1067
|
+
// The cutter reads the file and a read can fail. That must read as "could not tell where the record ends", never as
|
|
1068
|
+
// "it does not end" (which would skip a record). Nothing was persisted for this delta, so the next event retries it.
|
|
1069
|
+
delivery = { kind: "nothing-to-send" };
|
|
1070
|
+
}
|
|
1071
|
+
if (delivery.kind !== "attempted")
|
|
1072
|
+
return last === null ? delivery : carry(last);
|
|
1073
|
+
deltas += 1;
|
|
1074
|
+
tooLarge += delivery.recordsTooLarge ?? 0;
|
|
1075
|
+
conversationId ??= delivery.conversationId;
|
|
1076
|
+
// `ok`, or a record skipped as too large, moved the offset on; anything else is a stop, and its outcome is the answer.
|
|
1077
|
+
if (delivery.disposition !== "ok" && delivery.recordsTooLarge === undefined)
|
|
1078
|
+
return carry(delivery);
|
|
1079
|
+
const after = fileState(loadSessionState(ctx.home, key), ctx.fileKey).sentOffset;
|
|
1080
|
+
// No progress is a bug somewhere below; end the loop rather than spin on it.
|
|
1081
|
+
if (after <= before)
|
|
1082
|
+
return carry(delivery);
|
|
1083
|
+
// ⛔ EVERY BYTE IS ACCOUNTED FOR, BUT THE LAST DELTA WAS SKIPPED RATHER THAN SENT, AND THE SESSION-LEVEL HEADERS RODE
|
|
1084
|
+
// ON IT. A skipped record (too large by the raw limit, the frame limit, or a 413 no split can shrink) puts nothing on
|
|
1085
|
+
// the wire, so the `final` assertion, the commits, the head and the rewrites that belong to the delta ending at EOF
|
|
1086
|
+
// never landed. Run ONE more pass: `nextSpan` is null at EOF, so the existing `span === null && final` branch of
|
|
1087
|
+
// `deliverChunk` sends the empty delta that carries them (`isLast` is true for an empty span). Without this a
|
|
1088
|
+
// `finalize` whose last record was oversize reported `notDelivered` after every byte had been sent or written off.
|
|
1089
|
+
// Hooks never set `final`, and their commits ride the next event as they always did.
|
|
1090
|
+
const tailSkipped = delivery.recordsTooLarge !== undefined && after >= eof && ctx.final === true;
|
|
1091
|
+
if (after >= eof && !tailSkipped)
|
|
1092
|
+
return carry(delivery);
|
|
1093
|
+
if (budgetLeftMs(ctx) < MIN_NEXT_DELTA_BUDGET_MS)
|
|
1094
|
+
return carry(delivery, true);
|
|
1095
|
+
last = delivery;
|
|
1096
|
+
chunkCtx = {
|
|
1097
|
+
...ctx,
|
|
1098
|
+
// Stop reading bodies once the id is in hand: a later delta never needs one.
|
|
1099
|
+
readConversationId: ctx.readConversationId === true && conversationId === undefined,
|
|
1100
|
+
};
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
899
1103
|
//# sourceMappingURL=post.js.map
|