wontopos 2.2.34 → 2.2.36
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 +107 -9
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/wontopos.js +1582 -0
- package/dist/wontopos.d.ts +132 -60
- package/dist/wontopos.js +410 -134
- package/package.json +8 -10
package/dist/wontopos.d.ts
CHANGED
|
@@ -15,12 +15,15 @@
|
|
|
15
15
|
* so with no `userId` anywhere the zero-setup path just works.
|
|
16
16
|
*
|
|
17
17
|
* The API key picks *which memory* (your account); `model` picks *which engine*
|
|
18
|
-
* reads it.
|
|
19
|
-
*
|
|
20
|
-
*
|
|
18
|
+
* reads it. Models on the shared pool read the same memory, so you can store with
|
|
19
|
+
* one and recall with another; `listModels()` reports each model's `memory` as
|
|
20
|
+
* `"shared"` or `"isolated"`, and an isolated one starts empty. Set a default in
|
|
21
|
+
* the constructor, or override a single call with
|
|
22
|
+
* `mem.withModel("tablet-1").recall(...)`.
|
|
21
23
|
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
+
* Recall quality does not depend on which language a memory was written in: a
|
|
25
|
+
* memory stored in one language is found by a question asked in another. Storing
|
|
26
|
+
* and searching call no LLM.
|
|
24
27
|
*
|
|
25
28
|
* Reliability: every call retries transient failures with exponential backoff +
|
|
26
29
|
* jitter, honoring `Retry-After` — 429 always; 502/503 and connection errors only
|
|
@@ -39,6 +42,11 @@
|
|
|
39
42
|
*/
|
|
40
43
|
/** Test hook — the warn-once set is process-global by design. */
|
|
41
44
|
export declare function _resetStoreIdWarnings(): void;
|
|
45
|
+
export declare const SEARCH_LIMIT_MIN = 5;
|
|
46
|
+
export declare const SEARCH_LIMIT_MAX = 20;
|
|
47
|
+
/** `recall`'s surrounding-context count. 0 to 20 inclusive; 0 attaches none. */
|
|
48
|
+
export declare const CONTEXT_LIMIT_MIN = 0;
|
|
49
|
+
export declare const CONTEXT_LIMIT_MAX = 20;
|
|
42
50
|
/** Quota from a response's `X-RateLimit-*` headers, or `null` if absent. */
|
|
43
51
|
export interface RateLimit {
|
|
44
52
|
limit: number | null;
|
|
@@ -104,7 +112,7 @@ export interface Memory {
|
|
|
104
112
|
* re-sorting by `similarity` overrides the ranking and makes results worse. Take the
|
|
105
113
|
* list in the order given. There is no `score` field. */
|
|
106
114
|
similarity?: number;
|
|
107
|
-
/**
|
|
115
|
+
/** Importance weight the service assigns to the memory. */
|
|
108
116
|
importance?: number;
|
|
109
117
|
/** Month bucket the memory belongs to (e.g. "2026-07"). Omitted when temporal fields are stripped. */
|
|
110
118
|
time_bucket?: string;
|
|
@@ -128,11 +136,11 @@ export interface StoreResult {
|
|
|
128
136
|
/**
|
|
129
137
|
* Present when `status` is `"duplicate"`: the id of the memory this write collided with.
|
|
130
138
|
*
|
|
131
|
-
*
|
|
132
|
-
*
|
|
133
|
-
*
|
|
134
|
-
*
|
|
135
|
-
*
|
|
139
|
+
* A genuinely new fact that only varies a detail of one already stored ("no meetings
|
|
140
|
+
* before 10am" next to "no meetings on Fridays") can land here too, so a duplicate is
|
|
141
|
+
* not always a harmless no-op: read this id, and either store one sentence that states
|
|
142
|
+
* both or `update()` the existing memory with the combined statement. Without it a
|
|
143
|
+
* dropped write gave the caller nothing to act on.
|
|
136
144
|
*/
|
|
137
145
|
duplicate_of?: string;
|
|
138
146
|
/** Present when the store was a duplicate and something (e.g. a speaker tag) was dropped. */
|
|
@@ -153,9 +161,9 @@ export interface StatusResult {
|
|
|
153
161
|
*
|
|
154
162
|
* What the service keeps is NOT your original. An image whose long edge is over
|
|
155
163
|
* 1568px is downscaled to 1568 before anything else happens, and that smaller
|
|
156
|
-
* picture is what gets
|
|
157
|
-
*
|
|
158
|
-
*
|
|
164
|
+
* picture is what gets stored, and handed back by `getImage`. Nothing on our side
|
|
165
|
+
* ever uses more than 1568, so the extra pixels would be bytes nobody reads. This
|
|
166
|
+
* is a memory
|
|
159
167
|
* service, not a photo host: the picture it holds has the resolution of a memory.
|
|
160
168
|
*
|
|
161
169
|
* It can also come back in a different FORMAT. Downscaling means re-encoding, and
|
|
@@ -302,14 +310,15 @@ export interface UpdateResult {
|
|
|
302
310
|
/** `searchFull` → everything one search answered with, not just the merged memories.
|
|
303
311
|
*
|
|
304
312
|
* `search` returns the memories and nothing else, which is the right answer for
|
|
305
|
-
* almost every call. Two options make it the wrong one: `max_images` asks for
|
|
306
|
-
*
|
|
307
|
-
* `
|
|
313
|
+
* almost every call. Two options make it the wrong one: `max_images` asks for
|
|
314
|
+
* photos, which arrive in their own field, and `verify` is reported on by
|
|
315
|
+
* `verify_used`. Merging away both means paying for a
|
|
308
316
|
* search you shaped and never seeing what came of it. */
|
|
309
317
|
export interface SearchResult {
|
|
310
|
-
/**
|
|
318
|
+
/** What others said, and general memories. */
|
|
311
319
|
memories: Memory[];
|
|
312
|
-
/** The assistant's own words (speaker "me"); `[]` on a model
|
|
320
|
+
/** The assistant's own words (speaker "me"); `[]` on a model that does not keep
|
|
321
|
+
* them apart. */
|
|
313
322
|
self_memories: Memory[];
|
|
314
323
|
/** Image memories, when `max_images` asked for any; `[]` otherwise. */
|
|
315
324
|
images: Memory[];
|
|
@@ -355,7 +364,7 @@ export interface StatsResult {
|
|
|
355
364
|
short_term_turns?: number;
|
|
356
365
|
[key: string]: unknown;
|
|
357
366
|
}
|
|
358
|
-
/** `listMemories` → one page of a store's raw memories (
|
|
367
|
+
/** `listMemories` → one page of a store's raw memories (the text you stored, and its metadata). */
|
|
359
368
|
export interface MemoryPage {
|
|
360
369
|
memories: Memory[];
|
|
361
370
|
count: number;
|
|
@@ -363,9 +372,9 @@ export interface MemoryPage {
|
|
|
363
372
|
next_cursor: string | null;
|
|
364
373
|
[key: string]: unknown;
|
|
365
374
|
}
|
|
366
|
-
/** `searchSelf` result:
|
|
375
|
+
/** `searchSelf` result: general memories and the assistant's own, kept apart. */
|
|
367
376
|
export interface SelfSearchResult {
|
|
368
|
-
/**
|
|
377
|
+
/** What others said, and general memories. */
|
|
369
378
|
memories: Memory[];
|
|
370
379
|
/** The assistant's own words (stored with speaker "me"); `[]` on non-self models. */
|
|
371
380
|
self_memories: Memory[];
|
|
@@ -428,10 +437,11 @@ export interface SpeakersList {
|
|
|
428
437
|
[key: string]: unknown;
|
|
429
438
|
}
|
|
430
439
|
/**
|
|
431
|
-
* Narrow a search to part of a store
|
|
432
|
-
*
|
|
440
|
+
* Narrow a search to part of a store. The filter chooses what is searched, not what
|
|
441
|
+
* is kept afterwards — so a narrow filter still returns your full `limit` when that
|
|
442
|
+
* many matches sit inside it.
|
|
433
443
|
*
|
|
434
|
-
* Retrieval
|
|
444
|
+
* Retrieval behaves the same in every language, so these behave
|
|
435
445
|
* identically in every language. Unlisted keys are dropped by the API rather than
|
|
436
446
|
* rejected, so a typo silently widens the search — spell them exactly.
|
|
437
447
|
*/
|
|
@@ -465,7 +475,7 @@ export interface SearchOptions {
|
|
|
465
475
|
/**
|
|
466
476
|
* Re-ask passes, 0–3. After the first retrieval the service asks the engine again up
|
|
467
477
|
* to this many times, each time telling it what was already returned so it skips
|
|
468
|
-
* those and reaches further back.
|
|
478
|
+
* those and reaches further back. No LLM runs at any
|
|
469
479
|
* value. Stops early when a pass finds nothing new; `verify_used` in the response
|
|
470
480
|
* says how many actually ran.
|
|
471
481
|
*
|
|
@@ -495,8 +505,11 @@ export interface SearchOptions {
|
|
|
495
505
|
* `idempotencyKey` makes repeating THIS EXACT write safe: the API replays the first
|
|
496
506
|
* response instead of storing again, for 10 minutes, and answers 422 if the same key
|
|
497
507
|
* arrives with a different body. Use it when a retry is your own (a job that died and
|
|
498
|
-
* was re-run, a queue that redelivers)
|
|
499
|
-
*
|
|
508
|
+
* was re-run, a queue that redelivers). The SDK retries a write on exactly one
|
|
509
|
+
* status: 429, which the service answers before it processes anything, so nothing
|
|
510
|
+
* was stored. It never retries a write on 502 / 503 or a dropped body, where the
|
|
511
|
+
* request may already have been stored and billed — without a key it cannot know
|
|
512
|
+
* whether that first attempt landed.
|
|
500
513
|
*
|
|
501
514
|
* The key must be UNIQUE PER LOGICAL WRITE — derive it from the thing being stored
|
|
502
515
|
* (`` `import:${row.id}` ``), never a constant, or the second write replays the first
|
|
@@ -537,6 +550,22 @@ export interface ClientOptions {
|
|
|
537
550
|
* key on an options object is not an error in TS at runtime). Both names work;
|
|
538
551
|
* `maxRetries` wins if somehow both are given. */
|
|
539
552
|
retries?: number;
|
|
553
|
+
/** A total budget for one call, in ms, across every attempt.
|
|
554
|
+
*
|
|
555
|
+
* `timeoutMs` bounds ONE attempt. At the defaults — 30s, two retries — a single
|
|
556
|
+
* call can hold a connection for 30s + backoff + 30s + backoff + 30s, over a
|
|
557
|
+
* minute, and a server handler awaiting it has no way to say "I only have five
|
|
558
|
+
* seconds". This is that way. Unset means no overall budget. */
|
|
559
|
+
deadlineMs?: number;
|
|
560
|
+
/** The caller's `AbortSignal` — cancel work already in flight.
|
|
561
|
+
*
|
|
562
|
+
* The SDK aborts on its own timeout; this is the seam for the caller's reason.
|
|
563
|
+
* When someone closes the chat window, the recall that window asked for should
|
|
564
|
+
* end, and the backoff sleep waiting to retry it should end too, instead of
|
|
565
|
+
* running to completion and billing for an answer nobody will read.
|
|
566
|
+
*
|
|
567
|
+
* Per call, clone: `mem.withSignal(ctrl.signal).recall(...)`. */
|
|
568
|
+
signal?: AbortSignal;
|
|
540
569
|
/** The `fetch` used for every request (default: the global `fetch`).
|
|
541
570
|
*
|
|
542
571
|
* This is the seam for anything the runtime cannot express through options.
|
|
@@ -566,6 +595,8 @@ export declare class Client {
|
|
|
566
595
|
private readonly apiKey;
|
|
567
596
|
private readonly model;
|
|
568
597
|
private readonly maxRetries;
|
|
598
|
+
private readonly deadlineMs?;
|
|
599
|
+
private readonly signal?;
|
|
569
600
|
/** Transport. Held as a field (not read off `globalThis` per call) so a caller
|
|
570
601
|
* who passes one gets it for every request, including from cloned clients. */
|
|
571
602
|
private readonly fetchImpl;
|
|
@@ -593,6 +624,18 @@ export declare class Client {
|
|
|
593
624
|
withModel(model: string): Client;
|
|
594
625
|
/** A client bound to `userId` as its default store (everything else kept). */
|
|
595
626
|
withUser(userId: string): Client;
|
|
627
|
+
/** A client bound to a caller's `AbortSignal` (everything else kept).
|
|
628
|
+
*
|
|
629
|
+
* const ctrl = new AbortController();
|
|
630
|
+
* req.on("close", () => ctrl.abort());
|
|
631
|
+
* const r = await mem.withSignal(ctrl.signal).recall(q, user);
|
|
632
|
+
*
|
|
633
|
+
* Aborting ends the request in flight AND any backoff sleep waiting to retry it. */
|
|
634
|
+
withSignal(signal: AbortSignal): Client;
|
|
635
|
+
/** A client with a total budget per call, across every retry (everything else
|
|
636
|
+
* kept). `mem.withDeadline(5_000).search(q)` inside a handler that has five
|
|
637
|
+
* seconds. */
|
|
638
|
+
withDeadline(deadlineMs: number): Client;
|
|
596
639
|
/** A client with a different per-request timeout in ms (everything else kept).
|
|
597
640
|
* `mem.withTimeout(120_000).addBulk(bigBlob)` — this slow call only. */
|
|
598
641
|
withTimeout(timeoutMs: number): Client;
|
|
@@ -610,36 +653,36 @@ export declare class Client {
|
|
|
610
653
|
store(content: string, userId?: string, metadata?: Record<string, unknown>, opts?: WriteOptions): Promise<StoreResult>;
|
|
611
654
|
/** Store a conversation turn (user + assistant). Payload first, userId last — same shape as add/search. */
|
|
612
655
|
addTurn(userMsg: string, assistantMsg: string, userId?: string, opts?: WriteOptions): Promise<StatusResult>;
|
|
613
|
-
/** Bulk-ingest a large blob of text
|
|
656
|
+
/** Bulk-ingest a large blob of text in one call. For backfilling.
|
|
614
657
|
* The call most worth an `opts.idempotencyKey`: a backfill that dies halfway and is
|
|
615
|
-
* re-run would otherwise re-
|
|
658
|
+
* re-run would otherwise re-ingest and re-bill the whole blob. */
|
|
616
659
|
addBulk(content: string, userId?: string, category?: string, timestamp?: string, opts?: WriteOptions): Promise<StatusResult>;
|
|
617
660
|
/** Supersede an old memory with new content. Payload first, userId last — same shape as add/search. */
|
|
618
661
|
update(oldMemoryId: string, newContent: string, userId?: string, opts?: WriteOptions): Promise<UpdateResult>;
|
|
619
|
-
/**
|
|
620
|
-
*
|
|
621
|
-
* `limit` bounds
|
|
622
|
-
* (Scroll 1.2+)
|
|
623
|
-
*
|
|
624
|
-
*
|
|
625
|
-
*
|
|
626
|
-
*
|
|
662
|
+
/** Search a store's memories. Returns them most relevant first.
|
|
663
|
+
*
|
|
664
|
+
* `limit` bounds `memories`, not the returned array. On a model that keeps the
|
|
665
|
+
* assistant's own words separate (Scroll 1.2+) those come back as well, so the
|
|
666
|
+
* array can hold more than `limit`. They were retrieved and billed either way;
|
|
667
|
+
* dropping them would only hide what you already paid for. Size a prompt window
|
|
668
|
+
* on the array you get back, not on `limit`. `searchSelf()` hands the two back
|
|
669
|
+
* apart. */
|
|
627
670
|
search(query: string, userId?: string, limit?: number, opts?: SearchOptions): Promise<Memory[]>;
|
|
628
|
-
/** Search a self-memory model (Scroll 1.2+): both
|
|
629
|
-
* `{ memories, self_memories }` — `memories` is
|
|
630
|
-
*
|
|
631
|
-
*
|
|
632
|
-
*
|
|
671
|
+
/** Search a self-memory model (Scroll 1.2+): both fields from ONE call. Returns
|
|
672
|
+
* `{ memories, self_memories }` — `memories` is what others said and general
|
|
673
|
+
* memories, `self_memories` is the assistant's OWN words (stored with speaker
|
|
674
|
+
* "me"), kept apart so whoever reads them never confuses who said what. On a model
|
|
675
|
+
* that does not keep them apart, `self_memories` is `[]`. `userId` may be omitted. */
|
|
633
676
|
searchSelf(query: string, userId?: string, limit?: number, opts?: SearchOptions): Promise<SelfSearchResult>;
|
|
634
677
|
/**
|
|
635
|
-
* Search, and keep every
|
|
678
|
+
* Search, and keep every field the answer came with.
|
|
636
679
|
*
|
|
637
680
|
* Same request as {@link Client.search} — reach for this one when the options make
|
|
638
681
|
* the merged array an incomplete answer:
|
|
639
682
|
*
|
|
640
683
|
* const r = await mem.searchFull("the day we moved", "alice", 10,
|
|
641
684
|
* { max_images: 3, verify: 2 });
|
|
642
|
-
* r.images.length; // the
|
|
685
|
+
* r.images.length; // the photos, which `search` drops
|
|
643
686
|
* r.verify_used; // how many re-ask passes actually ran (you are billed per pass)
|
|
644
687
|
*
|
|
645
688
|
* Both options need Tablet 2 or newer and are refused (403) on an older engine
|
|
@@ -661,11 +704,13 @@ export declare class Client {
|
|
|
661
704
|
* did not ask for.
|
|
662
705
|
*/
|
|
663
706
|
limit?: number;
|
|
664
|
-
/**
|
|
707
|
+
/** How much surrounding context is attached around the best match, 0–20. Default 10;
|
|
665
708
|
* 0 attaches none. Same model floor as `limit`. */
|
|
666
709
|
context_limit?: number;
|
|
667
710
|
}): Promise<RecallResult>;
|
|
668
|
-
/** Run a built-in engram ("deep_recall" | "timeline" | "gather"
|
|
711
|
+
/** Run a built-in engram ("deep_recall" | "timeline" | "gather" | "equilibrium" |
|
|
712
|
+
* "tone_stabilizer"; the service is the authority — an unknown name comes back with
|
|
713
|
+
* the list it accepts). Returns the merged result.
|
|
669
714
|
* `form`/`tz` render memory times (memoir/archive) on Scroll 1.2+, same as search/recall. */
|
|
670
715
|
engram(name: string, query: string, userId?: string, opts?: {
|
|
671
716
|
form?: string;
|
|
@@ -675,13 +720,14 @@ export declare class Client {
|
|
|
675
720
|
history(userId?: string): Promise<HistoryTurn[]>;
|
|
676
721
|
/** Memory counts for a store: { total_memories, short_term_turns }. */
|
|
677
722
|
stats(userId?: string): Promise<StatsResult>;
|
|
678
|
-
/** Fetch ONE memory by id — the
|
|
723
|
+
/** Fetch ONE memory by id — the text you stored, and its metadata.
|
|
679
724
|
* The id is what `add`/`store` or `listMemories` returned. Same visibility as
|
|
680
|
-
* `listMemories`: an id from another store,
|
|
681
|
-
* invalidated memory rejects with `NotFoundError`. `
|
|
725
|
+
* `listMemories`: an id from another store, an internal record id, or an
|
|
726
|
+
* invalidated memory rejects with `NotFoundError`. Pass `undefined` as `userId` for
|
|
727
|
+
* the default store — it is positional here, not omittable as it is in Python. */
|
|
682
728
|
get(userId: string | undefined, memoryId: string): Promise<Memory>;
|
|
683
|
-
/** List a store's stored memories — the
|
|
684
|
-
*
|
|
729
|
+
/** List a store's stored memories — the text you stored, plus its metadata.
|
|
730
|
+
* Paginated: pass the returned `next_cursor` back as `cursor` for the next
|
|
685
731
|
* page; a `null` cursor means the last page. Use it to browse or export a store.
|
|
686
732
|
*
|
|
687
733
|
* let cursor: string | null = null;
|
|
@@ -697,14 +743,14 @@ export declare class Client {
|
|
|
697
743
|
cursor?: string;
|
|
698
744
|
}): Promise<MemoryPage>;
|
|
699
745
|
/** Async-iterate every stored memory in a store, paging under the hood — no cursor
|
|
700
|
-
* bookkeeping.
|
|
746
|
+
* bookkeeping. The text you stored and its metadata only.
|
|
701
747
|
*
|
|
702
748
|
* for await (const m of mem.iterMemories()) console.log(m.id, m.content);
|
|
703
749
|
*/
|
|
704
750
|
iterMemories(userId?: string, opts?: {
|
|
705
751
|
pageSize?: number;
|
|
706
752
|
}): AsyncGenerator<Memory>;
|
|
707
|
-
/** Collect ALL of a store's memories into an array (
|
|
753
|
+
/** Collect ALL of a store's memories into an array (the text you stored, and its metadata). */
|
|
708
754
|
exportMemories(userId?: string): Promise<Memory[]>;
|
|
709
755
|
/**
|
|
710
756
|
* Fetch the bytes of an image memory — the picture the SERVICE holds.
|
|
@@ -712,8 +758,8 @@ export declare class Client {
|
|
|
712
758
|
* Not necessarily your upload, in size OR in format: an image whose long edge was
|
|
713
759
|
* over 1568px was downscaled to 1568 on the way in and re-encoded — lossless
|
|
714
760
|
* formats as WebP, so a PNG comes back as `image/webp`; JPEG stays JPEG — and that
|
|
715
|
-
* smaller picture is what
|
|
716
|
-
*
|
|
761
|
+
* smaller picture is what is stored and comes back here. Nothing on our side ever
|
|
762
|
+
* uses more than 1568, so the extra pixels would be bytes nobody reads.
|
|
717
763
|
* Keep your own copy for the full-resolution file.
|
|
718
764
|
*
|
|
719
765
|
* The type is sniffed from the bytes themselves rather than from whatever the file
|
|
@@ -831,9 +877,9 @@ export declare class Client {
|
|
|
831
877
|
* assistant's own words, otherwise a person's name. Same cursor paging as
|
|
832
878
|
* `listImages`.
|
|
833
879
|
*
|
|
834
|
-
* `chunks` / `points_to_delete` report
|
|
835
|
-
*
|
|
836
|
-
*
|
|
880
|
+
* `chunks` / `points_to_delete` report how many internal records a delete would
|
|
881
|
+
* actually remove — usually more than `returned`, and worth showing to whoever is
|
|
882
|
+
* about to confirm one.
|
|
837
883
|
*/
|
|
838
884
|
bySpeaker(speaker: string, userId?: string, opts?: {
|
|
839
885
|
limit?: number;
|
|
@@ -875,7 +921,8 @@ export declare class Client {
|
|
|
875
921
|
listSpeakers(userId?: string): Promise<SpeakersList>;
|
|
876
922
|
/** Unregister a person. Their memories stay; the name tag goes. */
|
|
877
923
|
removeSpeaker(speaker: string, userId?: string): Promise<SpeakerOpResult>;
|
|
878
|
-
/** Delete a single memory by id. `userId`
|
|
924
|
+
/** Delete a single memory by id. Pass `undefined` as `userId` for the default
|
|
925
|
+
* store — it is positional here, not omittable as it is in Python. */
|
|
879
926
|
delete(userId: string | undefined, memoryId: string): Promise<StatusResult>;
|
|
880
927
|
/** Delete ALL memories for a store (GDPR erase). `userId` is required on purpose -
|
|
881
928
|
* this is destructive, so it never falls back to the default store. */
|
|
@@ -893,6 +940,31 @@ export declare class Client {
|
|
|
893
940
|
* a non-2xx we re-read the body as text and reuse `errorFor`, which keeps 404
|
|
894
941
|
* (`NotFoundError`) and 401 behaving the same as everywhere else.
|
|
895
942
|
*/
|
|
943
|
+
/** Where this call's budget runs out, or `undefined` when it has none. Computed
|
|
944
|
+
* once per call, not per attempt — a budget recomputed each attempt is not a
|
|
945
|
+
* budget. */
|
|
946
|
+
private deadlineAt;
|
|
947
|
+
/** The abort wiring for ONE attempt: this SDK's per-attempt timeout, the caller's
|
|
948
|
+
* signal, and whatever is left of the overall deadline — whichever fires first.
|
|
949
|
+
*
|
|
950
|
+
* One function, two callers (`request` and `requestBytes`), because a guard living
|
|
951
|
+
* in one of two request paths is exactly how `getImage` ended up with no retry loop
|
|
952
|
+
* while the module doc promised every call had one. */
|
|
953
|
+
private beginAttempt;
|
|
954
|
+
/** What to say when an attempt was cut short. The three reasons need different
|
|
955
|
+
* words: a timeout may have landed server-side, a cancellation certainly did not
|
|
956
|
+
* matter to the caller, and an exhausted budget is the caller's own setting. */
|
|
957
|
+
private abortMessage;
|
|
958
|
+
/** Sleep between attempts — but wake the moment the caller aborts, and give up
|
|
959
|
+
* rather than wait out a backoff the budget cannot cover. A backoff that runs to
|
|
960
|
+
* completion after the caller gave up wastes exactly as long as the request it was
|
|
961
|
+
* waiting to repeat.
|
|
962
|
+
*
|
|
963
|
+
* Clamping the wait to what is left and retrying anyway is the same answer as
|
|
964
|
+
* having no deadline at all: the call still spends the whole allowance, and the one
|
|
965
|
+
* attempt it buys has nothing left to finish in. If the wait does not fit, the
|
|
966
|
+
* budget is already decided. */
|
|
967
|
+
private backoffSleep;
|
|
896
968
|
private requestBytes;
|
|
897
969
|
/** Validate an idempotency key BEFORE the network. The API answers a malformed
|
|
898
970
|
* key with a 400, which on a retry path reads as "my write failed" when in fact
|