@akanjs/cli 3.0.0-beta.4 → 3.0.0-beta.6
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/.build-stamp
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
fdc8c6e78c40accb0f6e5b895d646ae64cea4b64c0f22eb726690c373e78c22a
|
|
@@ -48,7 +48,9 @@ apps and libs never import it directly (`no-import-external-library`) — everyt
|
|
|
48
48
|
slots: `AgentLauncher`, `AgentBubble`, `AgentComposer`, `AgentApproval`,
|
|
49
49
|
`AgentQuestion`, `AgentQueued`, `AgentMenu`, `AgentMarkdown` and `AgentCode` each replace one part in `_overrides.tsx`, and
|
|
50
50
|
`akanjs/ui` exports every default beside them (`DefaultBubble`, `DefaultComposer`, …) so a skin composes the
|
|
51
|
-
one it is replacing.
|
|
51
|
+
one it is replacing. The attachment row is exported on its own as `AgentAttachments`, because a replaced
|
|
52
|
+
`AgentBubble` that redraws it by hand forks the rule for which carrier is shown how — and then keeps whatever
|
|
53
|
+
that rule was on the day it was copied. `AgentCode` is where a highlighter binds — the fence's language reaches it — and a
|
|
52
54
|
replacement for `AgentBubble` carries its own `memo`, since the transcript re-renders on every delta. Only then
|
|
53
55
|
`AgentChat`, which replaces the panel whole: it also replaces the slash commands, the compaction notice and the
|
|
54
56
|
approval gate, so reach for it when the *layout* differs, not when the look does.
|
|
@@ -110,8 +112,9 @@ apps and libs never import it directly (`no-import-external-library`) — everyt
|
|
|
110
112
|
arrives as an image. `accepts.document` is one boolean over every non-image type, so an adaptor whose API has a
|
|
111
113
|
block for only some of them — Anthropic's `document` is PDF and nothing else — names the rest in the text rather
|
|
112
114
|
than dropping them, which is the wire's own rule applied at the one layer that knows which blocks exist. Persisting keeps each
|
|
113
|
-
attachment's name and drops its content: web storage is a few megabytes, one
|
|
114
|
-
a save that fails is silent — so keeping the bytes would quietly stop keeping
|
|
115
|
+
attachment's name, type, address and `ref` and drops only its content: web storage is a few megabytes, one
|
|
116
|
+
screenshot fills a chunk of it, and a save that fails is silent — so keeping the bytes would quietly stop keeping
|
|
117
|
+
the transcript, while an id is the handle a restored conversation has left to find the file with.
|
|
115
118
|
**The ceilings are the message's, not the file's**: 4 MB per file, 8 MB and five files per message, and the same
|
|
116
119
|
file twice is refused by name. The bytes ride inside one turn's JSON, so what a provider refuses is the sum —
|
|
117
120
|
and a request that cannot be sent is one the user has to empty the composer to escape, which is why the refusal
|
|
@@ -127,11 +130,13 @@ apps and libs never import it directly (`no-import-external-library`) — everyt
|
|
|
127
130
|
Nothing downstream trusts the result: an attachment whose `mimeType` is not a string is noted like any other
|
|
128
131
|
unreadable one, and its chip renders its name with no preview, because the host wrote the object and the declared
|
|
129
132
|
type is a claim rather than a check. A field that arrives wrong costs that attachment, never the turn.
|
|
130
|
-
- **A `url` carrier is an address the *provider* fetches,
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
133
|
+
- **A `url` carrier is an address the *provider* fetches, and bytes beside it win.** A reader that uploads answers
|
|
134
|
+
`{ url }` when its storage is publicly reachable, and `{ data }` — or both — when it is not: an attachment
|
|
135
|
+
carrying both is read from its bytes and displayed from its address, so the chip draws a thumbnail and the model
|
|
136
|
+
still gets the picture. That precedence exists because the other order fails the one way nothing reports: the
|
|
137
|
+
default storage backend serves a path only the app can resolve, the adaptor would hand it over as written, and
|
|
138
|
+
the model answers about a picture it never saw. Send a provider-reachable URL **alone** — paired with bytes it
|
|
139
|
+
costs the provider hop whatever the bytes weigh, for a fetch the provider would have done itself.
|
|
135
140
|
- **An image type the provider has no block for is refused at the adaptor, not upstream.** `accepts.image` is one
|
|
136
141
|
boolean, so `AgentService.readable` passes every `image/*` through, and the built-in composer reader base64s
|
|
137
142
|
every `image/*` as well — an `AttachReader` answering `null` means "not mine" and falls through to it, so an app
|
|
@@ -464,6 +464,24 @@ Full contract — credential handshake, room revalidation, socket cleanup scopin
|
|
|
464
464
|
- **A cleanup registered with `ws.on("disconnect" | "unsubscribe", fn)` is scoped to the call that registered it**
|
|
465
465
|
— a room for a `pubsub` subscribe, the socket for a `message` handler. Cleanup that must happen either way
|
|
466
466
|
registers for both.
|
|
467
|
+
- **A call's deadline is the endpoint's `{ timeout: <ms> }`, and there is nowhere else to put one.** It bounds
|
|
468
|
+
both ends: the `Timeout` middleware — registered by default, and standing aside for every endpoint that
|
|
469
|
+
declares none — rejects the call with `base.error.gatewayTimeout`, and the same value is serialized to the
|
|
470
|
+
client as that call's request budget. Declared nowhere, a call takes the client's own default of **30
|
|
471
|
+
seconds**, so an endpoint whose real work runs longer (provisioning, a firmware flow, an external
|
|
472
|
+
orchestration) has to declare one or it dies mid-flight with a transport error that does not say whether the
|
|
473
|
+
server ever received it. A caller overrides it per call with `fetch.x(…, { timeout })` — `false` waits as long
|
|
474
|
+
as the runtime will — and an app moves its own default with `fetch.instance.setTimeout(ms)`. An upload is
|
|
475
|
+
never bounded. **Losing the race does not cancel the work**: the handler runs to completion with nobody
|
|
476
|
+
holding its result, so a deadline is an answer to the caller, not an undo.
|
|
477
|
+
- **A cached answer is the endpoint's `{ cache: <ms> }`, and only a `query` taking no internal argument may have
|
|
478
|
+
one.** The `Cache` middleware is registered by default and stands aside for every endpoint that declares
|
|
479
|
+
nothing. Internal arguments are how a call learns who is asking (`.with(Self)`), so an endpoint that has them
|
|
480
|
+
answers per caller and one shared entry would be one caller's answer handed to the next — that endpoint and
|
|
481
|
+
every `mutation` are named in the log and left uncached. Guards run on every hit; what is cached is the
|
|
482
|
+
handler's result, so `resolveReturn` still masks fields and resolves relations per call; a cache backend that
|
|
483
|
+
is down is warned about and the call runs uncached. The default chain is `Logging → Timeout → Cache → handler`,
|
|
484
|
+
and there is no `Retry` middleware.
|
|
467
485
|
|
|
468
486
|
### Authorization Defaults
|
|
469
487
|
|
|
@@ -28,6 +28,55 @@ mutation is mounted under. `conventions` carries the invariants — this is the
|
|
|
28
28
|
neither again, and cleanup that must happen either way registers for both. From a `message` handler it belongs to
|
|
29
29
|
the socket and runs at close. A handler that throws is logged and never blocks the rest of the teardown.
|
|
30
30
|
|
|
31
|
+
## Request Deadlines
|
|
32
|
+
|
|
33
|
+
- **`{ timeout: <ms> }` in an endpoint's signal option is the whole configuration surface**, and it reaches both
|
|
34
|
+
ends of the call. Server side, the `Timeout` middleware races the handler against it and answers
|
|
35
|
+
`base.error.gatewayTimeout` (504) once it is spent. Client side, `FetchSerializer` ships the value on the
|
|
36
|
+
serialized endpoint and `fetch.<endpoint>()` uses it as that request's `AbortSignal.timeout` budget, so the
|
|
37
|
+
browser does not abandon a call the server is still allowed to be working on.
|
|
38
|
+
- **The middleware is registered by default and has no default of its own.** An endpoint that declares no
|
|
39
|
+
`timeout` is passed straight through — a default here would put a deadline on every endpoint in the app that
|
|
40
|
+
nobody asked for, which is why the middleware used to be opt-in and, being opt-in, made the declared value
|
|
41
|
+
dead. It clears its timer in a `finally`: the race's loser is a pending timer per call otherwise, and with a
|
|
42
|
+
five-minute budget that is a five-minute hold on the event loop for a call that answered in a millisecond.
|
|
43
|
+
- **Without a declaration the client's own default applies — 30 seconds** (`DEFAULT_TIMEOUT_MS`,
|
|
44
|
+
`fetch/client/httpClient.ts`), and the server imposes nothing. Nothing else bounds a request: `Bun.serve` runs
|
|
45
|
+
with `idleTimeout: 0`, so a long call is only ever ended by one of these two.
|
|
46
|
+
- **Precedence is caller, then endpoint, then client default.** `fetch.x(…, { timeout: 5_000 })` overrides what
|
|
47
|
+
the endpoint declared; `{ timeout: false }` waits as long as the runtime will. An app moves its own default
|
|
48
|
+
with `fetch.instance.setTimeout(ms)` — `lib/useClient.ts` is generated, so the `timeout` option on
|
|
49
|
+
`FetchClient.build` belongs to the template that writes it, not to app code.
|
|
50
|
+
- **A file upload is never bounded**, on any of those paths: a large body on a slow uplink is a request that is
|
|
51
|
+
working, and the request body is the only thing the client can tell that from.
|
|
52
|
+
- **The deadline answers the caller; it does not undo the call.** `Promise.race` cannot cancel `next()`, so a
|
|
53
|
+
handler that timed out keeps running and keeps writing. Reach for a `timeout` to bound what a *caller* waits
|
|
54
|
+
for, not to bound an effect — an operation that must not half-happen needs its own idempotency or compensation.
|
|
55
|
+
- The failure is the same `base.error.gatewayTimeout` key whichever side gave up, so the user reads one sentence
|
|
56
|
+
in their own language either way. What differs is the status code — 504 from the server's middleware, 408 from
|
|
57
|
+
the client's own abort — and nothing downstream branches on that.
|
|
58
|
+
|
|
59
|
+
## Cached Answers
|
|
60
|
+
|
|
61
|
+
- **`{ cache: <ms> }` in an endpoint's signal option is the whole cache surface.** The `Cache` middleware is
|
|
62
|
+
registered by default and stands aside unless an endpoint declares one — it used to be opt-in and to ignore the
|
|
63
|
+
declared value for a hard-coded 60 seconds, which is the same shape the `timeout` bug had.
|
|
64
|
+
- **Only a `query` that takes no internal argument may carry one.** The handler's inputs are its declared
|
|
65
|
+
arguments plus its internal ones, so an endpoint with none of the latter answers the same thing to everyone who
|
|
66
|
+
may read it — which is the only answer a shared entry can hold. An endpoint that takes `.with(Self)` answers
|
|
67
|
+
per caller, and one entry would be one caller's answer handed to the next; that endpoint and every `mutation`
|
|
68
|
+
are named once in the log and left uncached rather than silently ignored.
|
|
69
|
+
- **The guards run on every hit.** A cache hit skips `next()`, and `next()` is what runs them, so the middleware
|
|
70
|
+
calls `context.checkGuards()` itself before handing the entry over. Shared is not public.
|
|
71
|
+
- **The entry is the handler's result, not the response.** The middleware wraps execution, so `resolveReturn`
|
|
72
|
+
still runs per call on the cached value — field masking, `hidden`/`secret` stripping and relation resolution are
|
|
73
|
+
never cached, and a relation still costs its own load. The cache saves the handler, not the serialization.
|
|
74
|
+
- **A cache backend that is down does not take the endpoint down.** A failed read is warned and the call runs
|
|
75
|
+
uncached; a failed write is warned and dropped.
|
|
76
|
+
- The default middleware chain is `Logging → Timeout → Cache → handler`: a hung cache backend is bounded by the
|
|
77
|
+
same deadline as the handler it stands in for. **`Retry` was removed** — an automatic re-run of an endpoint
|
|
78
|
+
whose failure it cannot classify replays whatever the first attempt already did.
|
|
79
|
+
|
|
31
80
|
## Binary Pubsub
|
|
32
81
|
|
|
33
82
|
- **`pubsub(Binary)` sends its payload in a websocket binary frame**, skipping the JSON `{ type: "pub" }`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@akanjs/cli",
|
|
3
|
-
"version": "3.0.0-beta.
|
|
3
|
+
"version": "3.0.0-beta.6",
|
|
4
4
|
"sourceType": "module",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"publishConfig": {
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"@langchain/openai": "^1.4.6",
|
|
34
34
|
"@tailwindcss/node": "^4.3.0",
|
|
35
35
|
"@trapezedev/project": "^7.1.4",
|
|
36
|
-
"akanjs": "3.0.0-beta.
|
|
36
|
+
"akanjs": "3.0.0-beta.6",
|
|
37
37
|
"chalk": "^5.6.2",
|
|
38
38
|
"commander": "^14.0.3",
|
|
39
39
|
"dayjs": "^1.11.20",
|