@urun-sh/core 0.4.2 → 0.5.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/CHANGELOG.md +135 -1
- package/dist/{chunk-JBLMKFEF.mjs → chunk-PAXFCPIL.mjs} +2 -2
- package/dist/{chunk-HOQ5WCT6.mjs → chunk-XHXPCJYZ.mjs} +5 -5
- package/dist/index.browser.d.mts +17 -5
- package/dist/index.browser.d.ts +17 -5
- package/dist/index.browser.js +2 -2
- package/dist/index.browser.mjs +1 -1
- package/dist/index.d.mts +17 -5
- package/dist/index.d.ts +17 -5
- package/dist/index.js +5 -5
- package/dist/index.mjs +1 -1
- package/dist/internal.browser.d.mts +1 -1
- package/dist/internal.browser.d.ts +1 -1
- package/dist/internal.browser.js +2 -2
- package/dist/internal.browser.mjs +1 -1
- package/dist/internal.d.mts +1 -1
- package/dist/internal.d.ts +1 -1
- package/dist/internal.js +5 -5
- package/dist/internal.mjs +1 -1
- package/dist/{stream-data-CXrgcYCu.d.mts → stream-data-BSG8Cuxh.d.mts} +41 -15
- package/dist/{stream-data-CXrgcYCu.d.ts → stream-data-BSG8Cuxh.d.ts} +41 -15
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,140 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.5.0
|
|
4
|
+
|
|
5
|
+
- **BREAKING: `end()` PARKS, and `cancel()` is the admission-ticket verb** (owner ruling D3,
|
|
6
|
+
2026-09-06; Review A §5.1 "two end verbs", §3.3 residue).
|
|
7
|
+
- `Session.end()` / `sessionDirectory().end(id)` both go through the one implementation
|
|
8
|
+
`endSession()` → `POST /api/sessions/{id}/end` → `urun_park_session(client_ended)`.
|
|
9
|
+
It resolves `{ status: 'paused', sessionId, reason }`. `'paused'` can never throw; a
|
|
10
|
+
TERMINAL word on that route is now the loud error (`invalid_end_response`), because a
|
|
11
|
+
terminal write on the session plane is what the model forbids.
|
|
12
|
+
- `Session.cancel()` (new) → `POST /api/session-requests/{id}/cancel`. Request plane only,
|
|
13
|
+
never addressed by session id. Every outcome the control plane can answer maps to
|
|
14
|
+
`{ status: 'canceled' }`; an unrecognized word stays loud.
|
|
15
|
+
- `SessionEndResult` is now `{ status: 'paused'; sessionId; reason } | { status: 'canceled';
|
|
16
|
+
requestId; platformStatus? }` — the `'ended'` member is gone.
|
|
17
|
+
- `SessionEndError.requestId` is optional and `sessionId` is new.
|
|
18
|
+
- `allocateSession`'s `onRequest` handle carries `sessionId` as soon as the create envelope
|
|
19
|
+
names one, so a hang-up racing allocation parks the session instead of cancelling the
|
|
20
|
+
ticket of a session that already exists.
|
|
21
|
+
- `SessionGatewayStatus` gains `'paused'`.
|
|
22
|
+
- The end request is `keepalive`, like the teardown beacon: an `end()` can race the tab
|
|
23
|
+
close that triggered it. It sends NO body: the verb names the cause server-side, and
|
|
24
|
+
session-api reads a wire `reason` only to tolerate it — so `end()` now speaks the same
|
|
25
|
+
wire shape as the pagehide beacon hitting that same route.
|
|
26
|
+
- **The one routing rule holds on the paths that are not the two verbs** (review findings):
|
|
27
|
+
a page teardown that cancelled a still-queued TICKET finishes on the SESSION plane if
|
|
28
|
+
promotion won the race (otherwise the promoted session holds a GPU seat with no client),
|
|
29
|
+
and the give-up cancel parks instead of cancelling when allocation threw AFTER the control
|
|
30
|
+
plane allocated (an unusable `session_api_base` / `ws_url` — the ticket-cancel route
|
|
31
|
+
refuses those with 409 `request_has_session`).
|
|
32
|
+
- A REFUSED `cancel()` is not a cancel: the release claim and the re-attach bookmark are
|
|
33
|
+
only taken on success, so a later `pagehide` still beacons the ticket cancel instead of
|
|
34
|
+
being suppressed by a release that never happened.
|
|
35
|
+
|
|
36
|
+
## 0.4.3
|
|
37
|
+
|
|
38
|
+
- **FIX: no server status word can reach a user as a benign lie — on EITHER path.**
|
|
39
|
+
Wave 0 (0.4.0) made the SFU WebSocket path honest: `STATUS_TO_PHASE` maps every
|
|
40
|
+
server word explicitly, an unmapped one goes LOUD (`console.error` via the
|
|
41
|
+
diagnostic mirror + an `unknown-server-status` diagnostic + phase `unknown`
|
|
42
|
+
carrying the literal word), and `reason` rides every phase. **It landed on the
|
|
43
|
+
WebSocket path only.** A brand-new user's cold start spends ALL of its time on
|
|
44
|
+
the HTTP admission path, and that path was documented as *"tolerant by
|
|
45
|
+
design"*: `admissionUpdateFrom` collapsed every 202 status word except
|
|
46
|
+
`queued` into `'pending'`, dropped `reason` entirely, and `reportAdmission`
|
|
47
|
+
rendered `provisioning`.
|
|
48
|
+
|
|
49
|
+
The consequence was that **`waiting_for_capacity` could not reach any client
|
|
50
|
+
at all**. The control plane answers a starved pull pool with
|
|
51
|
+
`202 {status:"waiting_for_capacity", reason:"no runtime worker is registered
|
|
52
|
+
for this pool — waiting for GPU capacity"}`; the phase exists in this SDK and
|
|
53
|
+
renders "Waiting for GPU capacity", but it was reachable ONLY from `_onStatus`
|
|
54
|
+
— i.e. only over a WebSocket that does not exist until allocation has already
|
|
55
|
+
succeeded, by which time capacity is no longer the question. A user whose org
|
|
56
|
+
has zero registered GPU workers watched "Provisioning…" for up to 600 s and
|
|
57
|
+
then got `never-live` "the backend is busy or wedged". The whole server-side
|
|
58
|
+
capacity observer was dead end-to-end.
|
|
59
|
+
|
|
60
|
+
Both entry points now share ONE `mapServerStatus(status)` and ONE loud-unknown
|
|
61
|
+
branch. `AdmissionUpdate.status` **widens from `'queued' | 'pending'` to
|
|
62
|
+
`string`** and carries the server's `reason`, plus `decisionReason` /
|
|
63
|
+
`refusalSource` / `refusalMessage` so a `provisioning_refused` 202 delivers
|
|
64
|
+
the PROVIDER's own message ("0/12 nodes are available: 12 Insufficient
|
|
65
|
+
nvidia.com/gpu…") instead of silence. The additive `runtime_state` detail may
|
|
66
|
+
still refine the two GENERIC waiting words (`queued`/`pending`) but may no
|
|
67
|
+
longer overrule a specific verdict — a starved answer carries
|
|
68
|
+
`runtime_state:'starting'` too, and letting `starting` win is exactly how the
|
|
69
|
+
capacity truth was lost.
|
|
70
|
+
|
|
71
|
+
- **FIX: every pause cause the server can write now has human copy, and a
|
|
72
|
+
conformance guard fails the build when the two lists diverge.**
|
|
73
|
+
`describePauseReason` knew 3 of the server's causes; the other 16 fell through
|
|
74
|
+
to the verbatim arm, so the canonical first-run failure printed as
|
|
75
|
+
*"Paused — connect_deadline. Resume any time."* — and the only thing relating
|
|
76
|
+
the two vocabularies was a COMMENT claiming the list was "grepped from the
|
|
77
|
+
server sources". The vocabulary is now VENDORED at
|
|
78
|
+
`src/server-pause-reasons.json`, GENERATED by
|
|
79
|
+
`scripts/refresh-pause-reason-fixture.mjs` from the SFU `PauseReason` union,
|
|
80
|
+
the `sessions.pause_reason` VOCABULARY clause (urun-infra migration
|
|
81
|
+
`20270906000000`) and the SFU `RuntimeNotDeliveringReason` union, each with
|
|
82
|
+
its path and blob sha. `src/pause-reason-vocabulary.test.ts` fails when any
|
|
83
|
+
vendored cause still hits the default arm. Every line keeps the invariant:
|
|
84
|
+
never "ended", never "start a new session", always resumable.
|
|
85
|
+
|
|
86
|
+
- **FIX: the default copy path shows an actionable hint, not developer text.**
|
|
87
|
+
`describeSessionPhase`'s `error` arm rendered ``Session failed — ${reason}``,
|
|
88
|
+
which for a create failure is the raw gateway string — an end user read
|
|
89
|
+
`[urun] session allocation failed at https://session-api.usw2.prod.cloud.urun.sh:
|
|
90
|
+
queue_full: … (HTTP 429)`. It now delegates to `describeSessionFailure`, so
|
|
91
|
+
message + hint are one code path. The `create-failed` hint gains arms for
|
|
92
|
+
**429** (names the load shed and surfaces the server's `Retry-After`) and
|
|
93
|
+
**5xx** (names the outage, says the retry is automatic) — both previously told
|
|
94
|
+
the user to "check the app/function name and auth", which is wrong about whose
|
|
95
|
+
fault it is. A lapsed queued REQUEST (`queue_ttl_expired` /
|
|
96
|
+
`stale_poll_expired`) no longer repeats the server's *"create a new session"* —
|
|
97
|
+
the request lapsed, the named object did not.
|
|
98
|
+
|
|
99
|
+
- **FIX: the server's load-shed pacing is no longer advisory.** A 429 sets a
|
|
100
|
+
jittered `Retry-After: 2-6`; the SDK read it only on the 202
|
|
101
|
+
legacy-immediate branch and DROPPED it on the throw path, so the outer sweep
|
|
102
|
+
re-dialled an overloaded control plane on its own full-jitter schedule.
|
|
103
|
+
`SessionAllocationError.retryAfterSeconds` now carries it, the sweep waits the
|
|
104
|
+
MAX of that and its own jitter (bounded by `BACKOFF_MAX_MS`), and
|
|
105
|
+
`SessionPhaseError.retryAfterSeconds` surfaces it to the copy.
|
|
106
|
+
|
|
107
|
+
The pacing floor is the **sweep-wide maximum**, not the last url's: every
|
|
108
|
+
gateway failure overwrites `lastError`, so a paced 429 from the primary
|
|
109
|
+
followed by an unpaced retryable failure from a regional fallback used to
|
|
110
|
+
erase the primary's request entirely and re-dial it on client jitter. The
|
|
111
|
+
same floor now applies to `resolveViewerConnect`, whose refusal ALSO carries
|
|
112
|
+
the server's `Retry-After` (it paced itself against a field that was never
|
|
113
|
+
populated). `samePhase` compares `retryAfterSeconds`, so a refusal that only
|
|
114
|
+
changed how long the backend is asking for re-stamps the phase instead of
|
|
115
|
+
leaving the UI quoting the previous number.
|
|
116
|
+
|
|
117
|
+
- **FIX: an HTTP 408 is a timeout, not a full admission queue.** The
|
|
118
|
+
`create-failed` hint folded 408 into the 429 arm and told the user "its
|
|
119
|
+
admission queue is full" — a confident FALSE cause for a status that only
|
|
120
|
+
establishes that a request timed out (`backoff.ts` treats it as a generic
|
|
121
|
+
retryable status; the durable-poll path throws `poll_http_408`). 408 now has
|
|
122
|
+
its own timeout copy, and still names the server's `Retry-After` when it sent
|
|
123
|
+
one.
|
|
124
|
+
|
|
125
|
+
- **FIX: the display sanitizer no longer has holes.** It knew exactly TWO
|
|
126
|
+
gateway wrappers, so token hydration (`session allocation at <url> never got
|
|
127
|
+
an access token`), the redirect envelope / self-redirect (`session create at
|
|
128
|
+
<url> …`), the poll give-up and the region-forward loop all still showed an
|
|
129
|
+
internal hostname in end-user copy — and a reason that is NOTHING BUT a
|
|
130
|
+
wrapper (the bare allocation timeout) stripped to empty and hit the
|
|
131
|
+
`|| reason` fallback, printing the raw string the strip existed to clean.
|
|
132
|
+
Every wrapper is listed, and a residual redaction is the backstop: no gateway
|
|
133
|
+
URL survives into display copy by any route.
|
|
134
|
+
|
|
135
|
+
- `whenLive`'s patience clocks (45 s / 600 s) are deliberately UNCHANGED here —
|
|
136
|
+
they are a separate item.
|
|
137
|
+
|
|
3
138
|
## 0.4.2
|
|
4
139
|
|
|
5
140
|
- **Lockstep release** — version bump to match the `@urun-sh/openai` omp launcher cohort
|
|
@@ -7,7 +142,6 @@
|
|
|
7
142
|
|
|
8
143
|
## 0.4.1
|
|
9
144
|
|
|
10
|
-
|
|
11
145
|
- **FIX: a re-dial no longer leaves the playout element bound to a DEAD track**
|
|
12
146
|
(urun-infra#2132; the prod qwen-voice "assistant audio drops mid-session and
|
|
13
147
|
never self-heals, reload fixes it" signature). `recvTransport.consume()` /
|