@abide/abide 0.115.0 → 0.116.1
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 +19 -0
- package/docs/REGISTRY.md +17 -14
- package/docs/RULEBOOK.md +41 -5
- package/docs/abide.md +74 -22
- package/package.json +1 -1
- package/src/compiler/classifyTemplate.ts +21 -14
- package/src/compiler/emitClient.ts +5 -1
- package/src/server/BRIDGE_VERSION.ts +1 -1
- package/src/server/IMAGE_TYPES.ts +10 -0
- package/src/server/OFFLINE_STALE.ts +4 -3
- package/src/server/answerWorker.ts +85 -22
- package/src/server/assistant.ts +81 -12
- package/src/server/claudeApi.ts +25 -20
- package/src/server/claudeCode.ts +30 -10
- package/src/server/generatedSurfaces.ts +48 -14
- package/src/server/index.ts +2 -0
- package/src/shared/OFFLINE_CACHES.ts +4 -1
- package/src/shared/REACHED_MESSAGE.ts +7 -0
- package/src/shared/framing.ts +10 -2
- package/src/ui/Router.ts +28 -7
- package/src/ui/bindFiles.ts +42 -0
- package/src/ui/index.ts +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# @abide/abide
|
|
2
2
|
|
|
3
|
+
## 0.116.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 7dedb9a: the offline worker keeps an answer marked `cache-control: public` — a public file, a font a stylesheet draws with — through a sign-in, a sign-out and a form submitted, which drop only what was saved for the caller
|
|
8
|
+
- 6a69393: a copy the offline worker kept stands in for a year where the network fails, rather than a week — a page, a GET's answer and a public file carry `stale-if-error=31536000`
|
|
9
|
+
|
|
10
|
+
## 0.116.0
|
|
11
|
+
|
|
12
|
+
### Minor Changes
|
|
13
|
+
|
|
14
|
+
- c1c3924: `bind:files` — a file input's files as an array, one or many: a pick writes them, and an array written to it (one dropped, or `[]` to clear) is what the input holds. A `bind:` an element has no property for, such as `bind:file` or `bind:vlaue`, is now a build error where it compiled to an attribute that bound nothing. RULEBOOK 32.73, 32.74
|
|
15
|
+
- 48cfc3e: tools answer images — a handler whose answer is a png, jpeg, gif or webp body reaches a model as the image: `/__abide/mcp` carries it as an `image` content item (so Claude Code, through a bridge, sees it), and a `Toolbox` call answers a `ToolAnswer` of `content` — the same pieces, text or image — which `claudeApi` shows the model as the call's result. Any other image type, an svg, is answered as its text. Breaking for a hand-written `Provider`: a `ToolAnswer` is `{ content, failed }` where it was `{ text, failed }`. RULEBOOK 20.21, 44.35
|
|
16
|
+
- 48cfc3e: multimodal transcripts — an `AssistantMessage`'s `content` is its words or its pieces in order, each `AssistantContent` text or an image as base64 with its type. `assistant` refuses an image a model can't take or one outside a user turn, and hands a `Provider` every turn as its pieces, words alone as one text piece. `claudeApi` shows each image to the model as an image block in its place, and `claudeCode` sends the one message Claude Code is sent as Messages API blocks, each image in its place. RULEBOOK 44.5, 44.36, 44.37, 44.38, 44.39
|
|
17
|
+
|
|
18
|
+
### Patch Changes
|
|
19
|
+
|
|
20
|
+
- 0a78343: a page reached inside the app reloads offline — the router tells the offline worker each address a navigation reaches, and the worker keeps its page where no saved copy would stand in ([`a3975be`](https://github.com/briancray/abide/commit/a3975be224584df096fe799c6291c0dfcd80cf84))
|
|
21
|
+
|
|
3
22
|
## 0.115.0
|
|
4
23
|
|
|
5
24
|
### Minor Changes
|
package/docs/REGISTRY.md
CHANGED
|
@@ -291,8 +291,8 @@ type Gated<Gate, Accepted> = [Schema<Accepted>] extends [Gate] ? never : Failed<
|
|
|
291
291
|
| `cache-control: no-store` | `/__abide/openapi.json`, `/__abide/mcp` | Refuses caching for an answer about the app's surfaces. | 21.4 |
|
|
292
292
|
| `cache-control: public, max-age=31536000, immutable` | the built bundle | A chunk addressed by its own content hash. | 21.5 |
|
|
293
293
|
| `cache-control: public, max-age=0, must-revalidate` | a file under a `public` directory | A file at an address the build never chose. | 21.12 |
|
|
294
|
-
| `cache-control: private, stale-while-revalidate=604800, stale-if-error=
|
|
295
|
-
| `cache-control: public, max-age=0, stale-while-revalidate=604800, stale-if-error=
|
|
294
|
+
| `cache-control: private, stale-while-revalidate=604800, stale-if-error=31536000` | a page, a `GET`'s answer, where `ABIDE_OFFLINE` registers its worker | The default that lets a copy kept on the device stand in for the network. | 21.22, 21.11, 21.17 |
|
|
295
|
+
| `cache-control: public, max-age=0, stale-while-revalidate=604800, stale-if-error=31536000` | a file under a `public` directory, where `ABIDE_OFFLINE` registers its worker | The same, for a file at an address the build never chose. | 21.23 |
|
|
296
296
|
| `clear-site-data: "cache"` | an answer carrying `abide-principal`, where pages are kept offline | Drops what the browser's own cache kept for the caller signed in before. | 21.24 |
|
|
297
297
|
| `etag` | a file under a `public` directory | The answered bytes, addressed by their content. | 21.13, 21.14 |
|
|
298
298
|
| `referrer-policy: strict-origin-when-cross-origin` | a page | The browsers' own default, written down. | 21.6 |
|
|
@@ -321,25 +321,27 @@ type Gated<Gate, Accepted> = [Schema<Accepted>] extends [Gate] ? never : Failed<
|
|
|
321
321
|
|
|
322
322
|
| Name | Signature | Meaning | Rules |
|
|
323
323
|
| --- | --- | --- | --- |
|
|
324
|
-
| `/__abide/mcp` | `POST` | The streamable-http MCP endpoint. | 19.21, 19.22, 20.5, 20.7, 20.8, 20.9, 20.10, 20.12, 20.13, 20.14, 20.16, 20.17, 20.19, 20.20, 36.7, 16.78 |
|
|
324
|
+
| `/__abide/mcp` | `POST` | The streamable-http MCP endpoint. | 19.21, 19.22, 20.5, 20.7, 20.8, 20.9, 20.10, 20.12, 20.13, 20.14, 20.16, 20.17, 20.19, 20.20, 20.21, 20.22, 36.7, 16.78 |
|
|
325
325
|
|
|
326
326
|
# Assistant
|
|
327
327
|
|
|
328
328
|
| Name | Signature | Meaning | Rules |
|
|
329
329
|
| --- | --- | --- | --- |
|
|
330
|
-
| `assistant` | `(messages: AssistantMessage[], provider: Provider, options?: AssistantOptions) => AsyncGenerator<AssistantEvent>` | A conversation with a model, as the events it produces. | 44.1, 44.2, 44.3, 44.4, 44.21 |
|
|
331
|
-
| `AssistantMessage` | `{ role: 'user' \| 'assistant'; content: string }` | One turn of the transcript a caller sends. | 44.3, 44.4 |
|
|
330
|
+
| `assistant` | `(messages: AssistantMessage[], provider: Provider, options?: AssistantOptions) => AsyncGenerator<AssistantEvent>` | A conversation with a model, as the events it produces. | 44.1, 44.2, 44.3, 44.4, 44.21, 44.36, 44.37, 44.39 |
|
|
331
|
+
| `AssistantMessage` | `{ role: 'user' \| 'assistant'; content: string \| AssistantContent[] }` | One turn of the transcript a caller sends: its words, or its pieces in order. | 44.3, 44.4 |
|
|
332
|
+
| `AssistantContent` | `{ type: 'text'; text: string } \| { type: 'image'; data: string; mimeType: string }` | A piece of a turn: words, or an image as base64 with its type. | 44.5, 44.35, 44.36, 44.37, 44.38, 44.39 |
|
|
332
333
|
| `AssistantEvent` | `{ type: 'text'; text: string } \| { type: 'tool'; id: string; name: string } \| { type: 'input'; id: string; input: unknown } \| { type: 'result'; id: string; text: string; failed: boolean } \| { type: 'done'; stop: string; usage: { input: number; output: number } }` | What a conversation produces: text, a tool call as it starts, its input once written, its result, and the end. | 44.2, 44.11, 44.28 |
|
|
333
334
|
| `AssistantOptions` | `{ mcp?: boolean; maxTurns?: number }` | What `assistant` takes beside a provider. | 44.6, 44.8, 44.11 |
|
|
334
|
-
| `Provider` | `{ run(messages: AssistantMessage[], tools: Toolbox, limits: { maxTurns: number; signal: AbortSignal }): AsyncGenerator<AssistantEvent> }` | What runs a conversation. | 44.2, 44.28 |
|
|
335
|
-
| `Toolbox` | `{ list: ToolDefinition[]; call(name: string, input: unknown): Promise<
|
|
335
|
+
| `Provider` | `{ run(messages: (AssistantMessage & { content: AssistantContent[] })[], tools: Toolbox, limits: { maxTurns: number; signal: AbortSignal }): AsyncGenerator<AssistantEvent> }` | What runs a conversation. | 44.2, 44.28, 44.39 |
|
|
336
|
+
| `Toolbox` | `{ list: ToolDefinition[]; call(name: string, input: unknown): Promise<ToolAnswer> }` | The app's MCP tools, called as the asker. | 44.6, 44.7, 44.35 |
|
|
337
|
+
| `ToolAnswer` | `{ content: AssistantContent[]; failed: boolean }` | What a tool call answered: its pieces, and whether it failed. | 20.21, 20.22, 44.35 |
|
|
336
338
|
| `ToolDefinition` | `{ name: string; description: string; inputSchema: unknown; annotations: { readOnlyHint: boolean; destructiveHint: boolean; idempotentHint: boolean }; outputSchema: unknown }` | A tool as the app's MCP surface lists it. | 20.14, 20.16, 20.19, 44.6 |
|
|
337
339
|
|
|
338
340
|
## `claudeApi`
|
|
339
341
|
|
|
340
342
|
| Name | Signature | Meaning | Rules |
|
|
341
343
|
| --- | --- | --- | --- |
|
|
342
|
-
| `claudeApi` | `(config: ClaudeApiConfig) => Provider` | The Claude Messages API as a provider. | 44.9, 44.10, 44.12, 44.21 |
|
|
344
|
+
| `claudeApi` | `(config: ClaudeApiConfig) => Provider` | The Claude Messages API as a provider. | 44.9, 44.10, 44.12, 44.21, 44.35, 44.38 |
|
|
343
345
|
| `ClaudeApiConfig` | `{ apiKey: string; baseURL?: string; model: string; max_tokens: number; system?: string \| unknown[]; tools?: unknown[]; [field: string]: unknown }` | A Messages API request body, less `messages` and `stream`, with the client's key and origin. | 44.9, 44.10 |
|
|
344
346
|
|
|
345
347
|
## `claudeCode`
|
|
@@ -519,7 +521,7 @@ type Gated<Gate, Accepted> = [Schema<Accepted>] extends [Gate] ? never : Failed<
|
|
|
519
521
|
|
|
520
522
|
| Name | Signature | Meaning | Rules |
|
|
521
523
|
| --- | --- | --- | --- |
|
|
522
|
-
| `navigate` | `(url?: URL \| string, options?: { replace?: boolean, keepScroll?: boolean }) => Promise<void>` | Navigates. | 30.7, 30.9, 30.10, 30.11, 30.12, 30.13, 30.14, 30.15, 30.16, 30.17, 30.18, 30.20, 30.21, 30.22, 30.23, 30.24, 30.25, 30.27, 30.29, 30.30, 30.31, 30.32, 45.12 |
|
|
524
|
+
| `navigate` | `(url?: URL \| string, options?: { replace?: boolean, keepScroll?: boolean }) => Promise<void>` | Navigates. | 30.7, 30.9, 30.10, 30.11, 30.12, 30.13, 30.14, 30.15, 30.16, 30.17, 30.18, 30.20, 30.21, 30.22, 30.23, 30.24, 30.25, 30.27, 30.29, 30.30, 30.31, 30.32, 45.12, 45.24 |
|
|
523
525
|
|
|
524
526
|
# `.abide` files
|
|
525
527
|
|
|
@@ -551,11 +553,12 @@ type Gated<Gate, Accepted> = [Schema<Accepted>] extends [Gate] ? never : Failed<
|
|
|
551
553
|
| `name={expr}` | A reactive attribute or property. | 32.3, 32.20, 32.23, 32.37, 32.70, 32.71 |
|
|
552
554
|
| `on<event>={fn}` | A native listener on an element, and an ordinary prop on a component. | 32.4 |
|
|
553
555
|
| `name="…{expr}…"` | An interpolated quoted value. | 32.3, 32.71 |
|
|
554
|
-
| `bind:value` | Two-way bind on an element. | 5.16, 32.5, 32.58, 32.59, 32.60, 32.61, 32.62, 32.72 |
|
|
556
|
+
| `bind:value` | Two-way bind on an element. | 5.16, 32.5, 32.58, 32.59, 32.60, 32.61, 32.62, 32.72, 32.74 |
|
|
555
557
|
| `bind:prop={state}` | Hands a component prop the caller's `Reactive`, or a `{get, set}` pair, to write through. | 32.6, 33.6, 33.8, 14.18 |
|
|
556
|
-
| `bind:checked` | A boolean bind on an input. | 32.7, 32.72 |
|
|
557
|
-
| `bind:open` | The same, on a `<details>`. | 32.7, 32.72 |
|
|
558
|
-
| `bind:group` | Radio or checkbox membership. | 32.8, 32.72 |
|
|
558
|
+
| `bind:checked` | A boolean bind on an input. | 32.7, 32.72, 32.74 |
|
|
559
|
+
| `bind:open` | The same, on a `<details>`. | 32.7, 32.72, 32.74 |
|
|
560
|
+
| `bind:group` | Radio or checkbox membership. | 32.8, 32.72, 32.74 |
|
|
561
|
+
| `bind:files` | A file input's files, as a list. | 32.73, 32.72, 32.74 |
|
|
559
562
|
| `bind:value={{get, set}}` | Two-way bind over an explicit accessor pair, on an element or a component. | 32.5, 32.6 |
|
|
560
563
|
| `bind:element={Reactive<Element> \| ((element: Element) => void \| Disposer)}` | A node reference. | 32.9, 32.38, 32.66, 32.68, 32.69 |
|
|
561
564
|
| `class:name={cond}` | Toggles a class. | 32.10, 32.71 |
|
|
@@ -656,7 +659,7 @@ type Gated<Gate, Accepted> = [Schema<Accepted>] extends [Gate] ? never : Failed<
|
|
|
656
659
|
| `ABIDE_MAX_REQUEST_BODY_SIZE` | `number` | The default ceiling on a mutation's body. | 16.13, 16.54, 16.55, 16.78 |
|
|
657
660
|
| `ABIDE_MAX_SOCKET_MESSAGE_SIZE` | `number` | The ceiling on a frame received over the socket mux, in bytes. | 18.28 |
|
|
658
661
|
| `ABIDE_MAX_SOCKET_SUBSCRIPTIONS` | `number` | How many values one connection to the socket mux may follow. | 18.31, 18.32 |
|
|
659
|
-
| `ABIDE_OFFLINE` | `boolean` | Whether the app keeps its pages and its bundle on the device, for when the network is gone. | 45.1, 45.2, 45.3, 45.4, 45.5, 45.6, 45.7, 45.8, 45.9, 45.10, 45.11, 45.12, 45.13, 45.14, 45.15, 45.16, 45.17, 45.18, 45.19, 45.20, 45.21, 45.22, 45.23 |
|
|
662
|
+
| `ABIDE_OFFLINE` | `boolean` | Whether the app keeps its pages and its bundle on the device, for when the network is gone. | 45.1, 45.2, 45.3, 45.4, 45.5, 45.6, 45.7, 45.8, 45.9, 45.10, 45.11, 45.12, 45.13, 45.14, 45.15, 45.16, 45.17, 45.18, 45.19, 45.20, 45.21, 45.22, 45.23, 45.24 |
|
|
660
663
|
| `ABIDE_STOP_TIMEOUT` | `number` | How long `App.stop` drains, in ms. | 42.17, 42.18 |
|
|
661
664
|
| `ABIDE_MAX_STREAM_BUFFER_SIZE` | `number` | The cap on a stream transcript held in memory. | 36.18 |
|
|
662
665
|
| `ABIDE_MAX_LOG_BUFFER_COUNT` | `number` | How many lines `log.records` is trimmed back to. | 28.1 |
|
package/docs/RULEBOOK.md
CHANGED
|
@@ -1385,6 +1385,13 @@ whatever its handler's args schema declares there.
|
|
|
1385
1385
|
20.20 A handler `/__abide/mcp` would list whose args schema can never be an object MUST refuse the
|
|
1386
1386
|
app at boot, naming the handler.
|
|
1387
1387
|
|
|
1388
|
+
20.21 A `tools/call` that `/__abide/mcp` answers for a call answered with an image body of a type
|
|
1389
|
+
44.36 names MUST carry that body as one `image` content item, base64 with its type, in place of its
|
|
1390
|
+
text, and MUST carry no `structuredContent`.
|
|
1391
|
+
|
|
1392
|
+
20.22 A call to a tool declaring an `outputSchema` that is answered with an `image/*` body MUST be
|
|
1393
|
+
refused, its text naming the tool and the image's type, and MUST be logged per 20.18 as a 500.
|
|
1394
|
+
|
|
1388
1395
|
# 21. Generated headers
|
|
1389
1396
|
|
|
1390
1397
|
21.1 Every response MUST carry `x-content-type-options: nosniff`.
|
|
@@ -1441,11 +1448,11 @@ time those rungs took apart from what they wrapped.
|
|
|
1441
1448
|
|
|
1442
1449
|
21.22 Where 45.1 has a page register the worker `ABIDE_OFFLINE` turns on, a page, and the answer of
|
|
1443
1450
|
a `GET` of the app's own that is neither a refusal nor a stream 16.32 frames, MUST default to `cache-control: private,
|
|
1444
|
-
stale-while-revalidate=604800, stale-if-error=
|
|
1451
|
+
stale-while-revalidate=604800, stale-if-error=31536000`.
|
|
1445
1452
|
|
|
1446
1453
|
21.23 Where 45.1 has a page register the worker `ABIDE_OFFLINE` turns on, a file under a `public`
|
|
1447
1454
|
directory MUST answer `cache-control: public, max-age=0, stale-while-revalidate=604800,
|
|
1448
|
-
stale-if-error=
|
|
1455
|
+
stale-if-error=31536000`.
|
|
1449
1456
|
|
|
1450
1457
|
21.24 Where 45.1 has a page register the worker `ABIDE_OFFLINE` turns on, an answer carrying the
|
|
1451
1458
|
`abide-principal` header 26.24 has it carry MUST carry `clear-site-data: "cache"`.
|
|
@@ -2072,6 +2079,13 @@ declarations first, then each directive's in the tag's order.
|
|
|
2072
2079
|
attribute a `bind:name` names, `checked` beside `bind:group`, an `<option selected>` inside a
|
|
2073
2080
|
`<select bind:value>`, or text inside a `<textarea bind:value>`.
|
|
2074
2081
|
|
|
2082
|
+
32.73 `bind:files` MUST hold a list of the input's files, with or without `multiple`, MUST write the
|
|
2083
|
+
files picked back on change, and MUST hand the input a list written to it; the server MUST write
|
|
2084
|
+
nothing for it.
|
|
2085
|
+
|
|
2086
|
+
32.74 A `bind:` on an element naming none of `value`, `checked`, `open`, `group`, `files` and
|
|
2087
|
+
`element` MUST be a build error.
|
|
2088
|
+
|
|
2075
2089
|
# 33. Props
|
|
2076
2090
|
|
|
2077
2091
|
33.1 `props` MUST be callable from a `<script>` block only, and calling it from a `<script module>`
|
|
@@ -3160,7 +3174,7 @@ of its own.
|
|
|
3160
3174
|
each one.
|
|
3161
3175
|
|
|
3162
3176
|
44.5 `claudeCode` MUST send a transcript to Claude Code as one message, its earlier turns folded
|
|
3163
|
-
above the last.
|
|
3177
|
+
above the last and each `AssistantContent` image in its place.
|
|
3164
3178
|
|
|
3165
3179
|
44.6 With `mcp`, the `Toolbox` `assistant` hands a `Provider` MUST list the tools `/__abide/mcp`
|
|
3166
3180
|
lists, and MUST call each as a request through the app answering the request.
|
|
@@ -3251,6 +3265,23 @@ that browser carrying any other.
|
|
|
3251
3265
|
`principal.clear` made at its browser since it opened signs in, at the caller 26.9 rotates it to,
|
|
3252
3266
|
and the command it yields MUST pair that one.
|
|
3253
3267
|
|
|
3268
|
+
44.35 A `Toolbox` call MUST hand as its `ToolAnswer`'s `content` the pieces `/__abide/mcp` carries
|
|
3269
|
+
for the same call, `claudeApi` MUST show the model those pieces as the call's result, and the
|
|
3270
|
+
`result` `AssistantEvent` MUST carry their words and none of their images.
|
|
3271
|
+
|
|
3272
|
+
44.36 `assistant` MUST refuse 400, before any `Provider` is reached, a transcript holding an
|
|
3273
|
+
`AssistantContent` image whose `mimeType` is none of `image/png`, `image/jpeg`, `image/gif` and
|
|
3274
|
+
`image/webp`.
|
|
3275
|
+
|
|
3276
|
+
44.37 `assistant` MUST refuse 400, before any `Provider` is reached, a transcript holding an
|
|
3277
|
+
`AssistantContent` image in a turn that is not the user's.
|
|
3278
|
+
|
|
3279
|
+
44.38 `claudeApi` MUST send each `AssistantContent` image to the model as an image block, base64 with
|
|
3280
|
+
its type, in its place in its turn.
|
|
3281
|
+
|
|
3282
|
+
44.39 `assistant` MUST hand a `Provider` each turn as its `AssistantContent` pieces, a turn's string
|
|
3283
|
+
as one text piece.
|
|
3284
|
+
|
|
3254
3285
|
# 45. Offline
|
|
3255
3286
|
|
|
3256
3287
|
45.1 Where `ABIDE_OFFLINE` is on, a page `abide build` built MUST register the worker
|
|
@@ -3270,8 +3301,9 @@ rather than one a redirect led to, that is neither a stream 16.32 frames nor a b
|
|
|
3270
3301
|
45.6 The worker `ABIDE_OFFLINE` turns on MUST answer a file of the build's bundle from what it
|
|
3271
3302
|
saved.
|
|
3272
3303
|
|
|
3273
|
-
45.7 The worker `ABIDE_OFFLINE` turns on MUST drop everything it saved
|
|
3274
|
-
|
|
3304
|
+
45.7 The worker `ABIDE_OFFLINE` turns on MUST drop everything it saved, save an answer whose
|
|
3305
|
+
`cache-control` is `public`, where an answer it is handed carries `abide-principal`, where
|
|
3306
|
+
`principal` changes in any tab, and where a form is submitted.
|
|
3275
3307
|
|
|
3276
3308
|
45.8 The worker `ABIDE_OFFLINE` turns on, of a new build, MUST drop everything the one before it
|
|
3277
3309
|
saved, and every file of the bundle the new build does not name.
|
|
@@ -3322,6 +3354,10 @@ and that copy is stale by no more than its `stale-if-error`.
|
|
|
3322
3354
|
45.23 The worker `ABIDE_OFFLINE` turns on MUST ask for a page it saves with an `abide-keep` header,
|
|
3323
3355
|
naming `open` where its answer is the one a navigation opens.
|
|
3324
3356
|
|
|
3357
|
+
45.24 The worker `ABIDE_OFFLINE` turns on MUST save the page of an address a `navigate` reached,
|
|
3358
|
+
where it saved no copy of that address 45.19 would answer with, and MUST NOT save one of another
|
|
3359
|
+
origin.
|
|
3360
|
+
|
|
3325
3361
|
# 46. Outbox
|
|
3326
3362
|
|
|
3327
3363
|
46.1 `outbox` MUST answer, oldest first, every call queued in it that was neither delivered nor
|
package/docs/abide.md
CHANGED
|
@@ -72,10 +72,12 @@ at its address with `.md` on the end, and the links below point at those files.
|
|
|
72
72
|
- Generated surfaces — `reference/generated-surfaces.md`
|
|
73
73
|
- `assistant` — `reference/assistant.md`
|
|
74
74
|
- `AssistantMessage` — `reference/types/AssistantMessage.md`
|
|
75
|
+
- `AssistantContent` — `reference/types/AssistantContent.md`
|
|
75
76
|
- `AssistantEvent` — `reference/types/AssistantEvent.md`
|
|
76
77
|
- `AssistantOptions` — `reference/types/AssistantOptions.md`
|
|
77
78
|
- `Provider` — `reference/types/Provider.md`
|
|
78
79
|
- `Toolbox` — `reference/types/Toolbox.md`
|
|
80
|
+
- `ToolAnswer` — `reference/types/ToolAnswer.md`
|
|
79
81
|
- `ToolDefinition` — `reference/types/ToolDefinition.md`
|
|
80
82
|
- `claudeApi` — `reference/claudeApi.md`
|
|
81
83
|
- `ClaudeApiConfig` — `reference/types/ClaudeApiConfig.md`
|
|
@@ -594,6 +596,7 @@ test('typing writes the value, and the value fills the field', async () => {
|
|
|
594
596
|
| `bind:checked` | a checkbox, to a boolean |
|
|
595
597
|
| `bind:open` | a `<details>`, to whether it is open |
|
|
596
598
|
| `bind:group` | radios to one value, or checkboxes to an array of their `value`s |
|
|
599
|
+
| `bind:files` | a file input, to an array of the files picked |
|
|
597
600
|
| `bind:value={{get, set}}` | a field to a pair you write, where the value is computed |
|
|
598
601
|
| `bind:prop={state}` | a component's prop, so the child's write reaches your value |
|
|
599
602
|
|
|
@@ -5404,8 +5407,11 @@ const recorded: Provider = {
|
|
|
5404
5407
|
const input = { name: 'Ada' }
|
|
5405
5408
|
yield { type: 'tool', id: 'toolu_1', name }
|
|
5406
5409
|
yield { type: 'input', id: 'toolu_1', input }
|
|
5407
|
-
const
|
|
5408
|
-
|
|
5410
|
+
const { content, failed } = await tools.call(name, input)
|
|
5411
|
+
// A search answers words, so its one piece is text.
|
|
5412
|
+
const [words] = content
|
|
5413
|
+
const text = words?.type === 'text' ? words.text : ''
|
|
5414
|
+
yield { type: 'result', id: 'toolu_1', text, failed }
|
|
5409
5415
|
yield { type: 'text', text: 'Ada Lovelace is at Analytical Engines.' }
|
|
5410
5416
|
yield { type: 'done', stop: 'end_turn', usage: { input: 0, output: 0 } }
|
|
5411
5417
|
},
|
|
@@ -5458,11 +5464,13 @@ test('the model calls the CRM search, through the app', async () => {
|
|
|
5458
5464
|
|
|
5459
5465
|
| Name | What it does for the CRM |
|
|
5460
5466
|
| --- | --- |
|
|
5461
|
-
| `AssistantMessage` | One turn of the transcript, `{ role, content }`. The last is the user's. |
|
|
5467
|
+
| `AssistantMessage` | One turn of the transcript, `{ role, content }`. The last is the user's. Its `content` is its words, or its pieces in order. |
|
|
5468
|
+
| `AssistantContent` | One piece of a turn: `text`, or an `image` as base64 with its type, so a rep can show the assistant a photo of a business card. PNG, JPEG, GIF and WebP, in the rep's own turns. |
|
|
5462
5469
|
| `AssistantOptions` | `mcp` to offer the tools, and `maxTurns`, 10 by default, to cap the model's turns in one call. |
|
|
5463
5470
|
| `AssistantEvent` | What the page reads: `text`, `tool` as a call starts, `input` once it is written, `result`, then `done` with the stop reason and token usage. The same events from every provider. |
|
|
5464
|
-
| `Provider` | What runs the conversation: one method, `run
|
|
5471
|
+
| `Provider` | What runs the conversation: one method, `run`, handed every turn as its pieces. Write one for another model, or a recording for a test. |
|
|
5465
5472
|
| `Toolbox` | What a provider is handed: `list` of the tools, and `call` to run one as the asker. |
|
|
5473
|
+
| `ToolAnswer` | What `call` answers: its `content`, the same pieces as a turn — the tool's text, or the image a handler drew, which the model is shown as a picture — and whether it failed. |
|
|
5466
5474
|
| `ToolDefinition` | One tool in `list`: its name, description and input schema. |
|
|
5467
5475
|
| `abide:assistant` | The log namespace that writes one `debug` line per conversation once it ends: the stop reason, the transcript's length, the tool calls, the tokens and the time. |
|
|
5468
5476
|
|
|
@@ -5595,7 +5603,12 @@ test('the body is sent as written, with the transcript', async () => {
|
|
|
5595
5603
|
max_tokens: 1024,
|
|
5596
5604
|
system: 'Answer questions about contacts.',
|
|
5597
5605
|
stream: true,
|
|
5598
|
-
messages: [
|
|
5606
|
+
messages: [
|
|
5607
|
+
{
|
|
5608
|
+
role: 'user',
|
|
5609
|
+
content: [{ type: 'text', text: 'Which company is Ada at?' }],
|
|
5610
|
+
},
|
|
5611
|
+
],
|
|
5599
5612
|
})
|
|
5600
5613
|
})
|
|
5601
5614
|
```
|
|
@@ -6528,12 +6541,12 @@ abide start
|
|
|
6528
6541
|
ABIDE_OFFLINE=0 abide start
|
|
6529
6542
|
```
|
|
6530
6543
|
|
|
6531
|
-
Each page the CRM opens is kept on the device, with the answers its handlers gave, the public files it loaded and the bundle it runs, until `ABIDE_OFFLINE=0` turns that off where the CRM is served. A kept page opens at once, however weak the signal, and the network is asked for the next copy behind it. With the network gone, a page opened in the last
|
|
6544
|
+
Each page the CRM opens is kept on the device, with the answers its handlers gave, the public files it loaded and the bundle it runs, until `ABIDE_OFFLINE=0` turns that off where the CRM is served. A kept page opens at once, however weak the signal, and the network is asked for the next copy behind it. With the network gone, a page opened in the last year loads again with its data and works. One never opened says the device is offline, and a link to it leaves the page you are on standing.
|
|
6532
6545
|
|
|
6533
6546
|
| Name | What it does for the CRM |
|
|
6534
6547
|
| --- | --- |
|
|
6535
|
-
| `cache-control: private, stale-while-revalidate=604800, stale-if-error=
|
|
6536
|
-
| `cache-control: public, max-age=0, stale-while-revalidate=604800, stale-if-error=
|
|
6548
|
+
| `cache-control: private, stale-while-revalidate=604800, stale-if-error=31536000` | what a page and a handler's answer carry, so the device shows what it kept while it asks again |
|
|
6549
|
+
| `cache-control: public, max-age=0, stale-while-revalidate=604800, stale-if-error=31536000` | the same, for a public file |
|
|
6537
6550
|
| `clear-site-data: "cache"` | what a sign-in or a sign-out answers, so the browser forgets what it kept for the last person |
|
|
6538
6551
|
|
|
6539
6552
|
What is kept is what those headers allow, and nothing else decides it. A handler that writes `cache-control: no-store` through `response` is never kept, and an answer with a `ttl` is shown from the device until its `max-age` runs out. A refresh always asks the network.
|
|
@@ -6627,10 +6640,12 @@ Arrive here knowing which name you want. Each page is read off the registry: the
|
|
|
6627
6640
|
| --- | --- |
|
|
6628
6641
|
| [`assistant`](/reference/assistant.md) | `@abide/abide/server` |
|
|
6629
6642
|
| [`AssistantMessage`](/reference/types/AssistantMessage.md) | `@abide/abide/server` |
|
|
6643
|
+
| [`AssistantContent`](/reference/types/AssistantContent.md) | `@abide/abide/server` |
|
|
6630
6644
|
| [`AssistantEvent`](/reference/types/AssistantEvent.md) | `@abide/abide/server` |
|
|
6631
6645
|
| [`AssistantOptions`](/reference/types/AssistantOptions.md) | `@abide/abide/server` |
|
|
6632
6646
|
| [`Provider`](/reference/types/Provider.md) | `@abide/abide/server` |
|
|
6633
6647
|
| [`Toolbox`](/reference/types/Toolbox.md) | `@abide/abide/server` |
|
|
6648
|
+
| [`ToolAnswer`](/reference/types/ToolAnswer.md) | `@abide/abide/server` |
|
|
6634
6649
|
| [`ToolDefinition`](/reference/types/ToolDefinition.md) | `@abide/abide/server` |
|
|
6635
6650
|
| [`claudeApi`](/reference/claudeApi.md) | `@abide/abide/server` |
|
|
6636
6651
|
| [`ClaudeApiConfig`](/reference/types/ClaudeApiConfig.md) | `@abide/abide/server` |
|
|
@@ -7666,8 +7681,8 @@ Transports: what is written in place rather than imported.
|
|
|
7666
7681
|
| `cache-control: no-store` | `/__abide/openapi.json`, `/__abide/mcp` | Refuses caching for an answer about the app's surfaces. | [21.4](/rulebook#21.4) |
|
|
7667
7682
|
| `cache-control: public, max-age=31536000, immutable` | the built bundle | A chunk addressed by its own content hash. | [21.5](/rulebook#21.5) |
|
|
7668
7683
|
| `cache-control: public, max-age=0, must-revalidate` | a file under a `public` directory | A file at an address the build never chose. | [21.12](/rulebook#21.12) |
|
|
7669
|
-
| `cache-control: private, stale-while-revalidate=604800, stale-if-error=
|
|
7670
|
-
| `cache-control: public, max-age=0, stale-while-revalidate=604800, stale-if-error=
|
|
7684
|
+
| `cache-control: private, stale-while-revalidate=604800, stale-if-error=31536000` | a page, a `GET`'s answer, where `ABIDE_OFFLINE` registers its worker | The default that lets a copy kept on the device stand in for the network. | [21.22](/rulebook#21.22), [21.11](/rulebook#21.11), [21.17](/rulebook#21.17) |
|
|
7685
|
+
| `cache-control: public, max-age=0, stale-while-revalidate=604800, stale-if-error=31536000` | a file under a `public` directory, where `ABIDE_OFFLINE` registers its worker | The same, for a file at an address the build never chose. | [21.23](/rulebook#21.23) |
|
|
7671
7686
|
| `clear-site-data: "cache"` | an answer carrying `abide-principal`, where pages are kept offline | Drops what the browser's own cache kept for the caller signed in before. | [21.24](/rulebook#21.24) |
|
|
7672
7687
|
| `etag` | a file under a `public` directory | The answered bytes, addressed by their content. | [21.13](/rulebook#21.13), [21.14](/rulebook#21.14) |
|
|
7673
7688
|
| `referrer-policy: strict-origin-when-cross-origin` | a page | The browsers' own default, written down. | [21.6](/rulebook#21.6) |
|
|
@@ -7833,7 +7848,7 @@ Generated surfaces: what is written in place rather than imported.
|
|
|
7833
7848
|
|
|
7834
7849
|
| Name | Signature | Meaning | Rules |
|
|
7835
7850
|
| --- | --- | --- | --- |
|
|
7836
|
-
| `/__abide/mcp` | `POST` | The streamable-http MCP endpoint. | [19.21](/rulebook#19.21), [19.22](/rulebook#19.22), [20.5](/rulebook#20.5), [20.7](/rulebook#20.7), [20.8](/rulebook#20.8), [20.9](/rulebook#20.9), [20.10](/rulebook#20.10), [20.12](/rulebook#20.12), [20.13](/rulebook#20.13), [20.14](/rulebook#20.14), [20.16](/rulebook#20.16), [20.17](/rulebook#20.17), [20.19](/rulebook#20.19), [20.20](/rulebook#20.20), [36.7](/rulebook#36.7), [16.78](/rulebook#16.78) |
|
|
7851
|
+
| `/__abide/mcp` | `POST` | The streamable-http MCP endpoint. | [19.21](/rulebook#19.21), [19.22](/rulebook#19.22), [20.5](/rulebook#20.5), [20.7](/rulebook#20.7), [20.8](/rulebook#20.8), [20.9](/rulebook#20.9), [20.10](/rulebook#20.10), [20.12](/rulebook#20.12), [20.13](/rulebook#20.13), [20.14](/rulebook#20.14), [20.16](/rulebook#20.16), [20.17](/rulebook#20.17), [20.19](/rulebook#20.19), [20.20](/rulebook#20.20), [20.21](/rulebook#20.21), [20.22](/rulebook#20.22), [36.7](/rulebook#36.7), [16.78](/rulebook#16.78) |
|
|
7837
7852
|
|
|
7838
7853
|
## Taught in
|
|
7839
7854
|
|
|
@@ -7851,7 +7866,7 @@ Generated surfaces: what is written in place rather than imported.
|
|
|
7851
7866
|
|
|
7852
7867
|
| Name | Signature | Meaning | Rules |
|
|
7853
7868
|
| --- | --- | --- | --- |
|
|
7854
|
-
| `assistant` | `(messages: AssistantMessage[], provider: Provider, options?: AssistantOptions) => AsyncGenerator<AssistantEvent>` | A conversation with a model, as the events it produces. | [44.1](/rulebook#44.1), [44.2](/rulebook#44.2), [44.3](/rulebook#44.3), [44.4](/rulebook#44.4), [44.21](/rulebook#44.21) |
|
|
7869
|
+
| `assistant` | `(messages: AssistantMessage[], provider: Provider, options?: AssistantOptions) => AsyncGenerator<AssistantEvent>` | A conversation with a model, as the events it produces. | [44.1](/rulebook#44.1), [44.2](/rulebook#44.2), [44.3](/rulebook#44.3), [44.4](/rulebook#44.4), [44.21](/rulebook#44.21), [44.36](/rulebook#44.36), [44.37](/rulebook#44.37), [44.39](/rulebook#44.39) |
|
|
7855
7870
|
|
|
7856
7871
|
## Taught in
|
|
7857
7872
|
|
|
@@ -7869,7 +7884,25 @@ Generated surfaces: what is written in place rather than imported.
|
|
|
7869
7884
|
|
|
7870
7885
|
| Name | Signature | Meaning | Rules |
|
|
7871
7886
|
| --- | --- | --- | --- |
|
|
7872
|
-
| `AssistantMessage` | `{ role: 'user' \| 'assistant'; content: string }` | One turn of the transcript a caller sends. | [44.3](/rulebook#44.3), [44.4](/rulebook#44.4) |
|
|
7887
|
+
| `AssistantMessage` | `{ role: 'user' \| 'assistant'; content: string \| AssistantContent[] }` | One turn of the transcript a caller sends: its words, or its pieces in order. | [44.3](/rulebook#44.3), [44.4](/rulebook#44.4) |
|
|
7888
|
+
|
|
7889
|
+
## Taught in
|
|
7890
|
+
|
|
7891
|
+
- [Open it to machines](/guide/open-it-to-machines.md)
|
|
7892
|
+
|
|
7893
|
+
---
|
|
7894
|
+
|
|
7895
|
+
<!-- reference/types/AssistantContent.md -->
|
|
7896
|
+
|
|
7897
|
+
# `AssistantContent`
|
|
7898
|
+
|
|
7899
|
+
`import type { AssistantContent } from '@abide/abide/server'`
|
|
7900
|
+
|
|
7901
|
+
## Assistant
|
|
7902
|
+
|
|
7903
|
+
| Name | Signature | Meaning | Rules |
|
|
7904
|
+
| --- | --- | --- | --- |
|
|
7905
|
+
| `AssistantContent` | `{ type: 'text'; text: string } \| { type: 'image'; data: string; mimeType: string }` | A piece of a turn: words, or an image as base64 with its type. | [44.5](/rulebook#44.5), [44.35](/rulebook#44.35), [44.36](/rulebook#44.36), [44.37](/rulebook#44.37), [44.38](/rulebook#44.38), [44.39](/rulebook#44.39) |
|
|
7873
7906
|
|
|
7874
7907
|
## Taught in
|
|
7875
7908
|
|
|
@@ -7923,7 +7956,7 @@ Generated surfaces: what is written in place rather than imported.
|
|
|
7923
7956
|
|
|
7924
7957
|
| Name | Signature | Meaning | Rules |
|
|
7925
7958
|
| --- | --- | --- | --- |
|
|
7926
|
-
| `Provider` | `{ run(messages: AssistantMessage[], tools: Toolbox, limits: { maxTurns: number; signal: AbortSignal }): AsyncGenerator<AssistantEvent> }` | What runs a conversation. | [44.2](/rulebook#44.2), [44.28](/rulebook#44.28) |
|
|
7959
|
+
| `Provider` | `{ run(messages: (AssistantMessage & { content: AssistantContent[] })[], tools: Toolbox, limits: { maxTurns: number; signal: AbortSignal }): AsyncGenerator<AssistantEvent> }` | What runs a conversation. | [44.2](/rulebook#44.2), [44.28](/rulebook#44.28), [44.39](/rulebook#44.39) |
|
|
7927
7960
|
|
|
7928
7961
|
## Taught in
|
|
7929
7962
|
|
|
@@ -7941,7 +7974,25 @@ Generated surfaces: what is written in place rather than imported.
|
|
|
7941
7974
|
|
|
7942
7975
|
| Name | Signature | Meaning | Rules |
|
|
7943
7976
|
| --- | --- | --- | --- |
|
|
7944
|
-
| `Toolbox` | `{ list: ToolDefinition[]; call(name: string, input: unknown): Promise<
|
|
7977
|
+
| `Toolbox` | `{ list: ToolDefinition[]; call(name: string, input: unknown): Promise<ToolAnswer> }` | The app's MCP tools, called as the asker. | [44.6](/rulebook#44.6), [44.7](/rulebook#44.7), [44.35](/rulebook#44.35) |
|
|
7978
|
+
|
|
7979
|
+
## Taught in
|
|
7980
|
+
|
|
7981
|
+
- [Open it to machines](/guide/open-it-to-machines.md)
|
|
7982
|
+
|
|
7983
|
+
---
|
|
7984
|
+
|
|
7985
|
+
<!-- reference/types/ToolAnswer.md -->
|
|
7986
|
+
|
|
7987
|
+
# `ToolAnswer`
|
|
7988
|
+
|
|
7989
|
+
`import type { ToolAnswer } from '@abide/abide/server'`
|
|
7990
|
+
|
|
7991
|
+
## Assistant
|
|
7992
|
+
|
|
7993
|
+
| Name | Signature | Meaning | Rules |
|
|
7994
|
+
| --- | --- | --- | --- |
|
|
7995
|
+
| `ToolAnswer` | `{ content: AssistantContent[]; failed: boolean }` | What a tool call answered: its pieces, and whether it failed. | [20.21](/rulebook#20.21), [20.22](/rulebook#20.22), [44.35](/rulebook#44.35) |
|
|
7945
7996
|
|
|
7946
7997
|
## Taught in
|
|
7947
7998
|
|
|
@@ -7977,7 +8028,7 @@ Generated surfaces: what is written in place rather than imported.
|
|
|
7977
8028
|
|
|
7978
8029
|
| Name | Signature | Meaning | Rules |
|
|
7979
8030
|
| --- | --- | --- | --- |
|
|
7980
|
-
| `claudeApi` | `(config: ClaudeApiConfig) => Provider` | The Claude Messages API as a provider. | [44.9](/rulebook#44.9), [44.10](/rulebook#44.10), [44.12](/rulebook#44.12), [44.21](/rulebook#44.21) |
|
|
8031
|
+
| `claudeApi` | `(config: ClaudeApiConfig) => Provider` | The Claude Messages API as a provider. | [44.9](/rulebook#44.9), [44.10](/rulebook#44.10), [44.12](/rulebook#44.12), [44.21](/rulebook#44.21), [44.35](/rulebook#44.35), [44.38](/rulebook#44.38) |
|
|
7981
8032
|
|
|
7982
8033
|
## Taught in
|
|
7983
8034
|
|
|
@@ -8789,7 +8840,7 @@ Generated surfaces: what is written in place rather than imported.
|
|
|
8789
8840
|
|
|
8790
8841
|
| Name | Signature | Meaning | Rules |
|
|
8791
8842
|
| --- | --- | --- | --- |
|
|
8792
|
-
| `navigate` | `(url?: URL \| string, options?: { replace?: boolean, keepScroll?: boolean }) => Promise<void>` | Navigates. | [30.7](/rulebook#30.7), [30.9](/rulebook#30.9), [30.10](/rulebook#30.10), [30.11](/rulebook#30.11), [30.12](/rulebook#30.12), [30.13](/rulebook#30.13), [30.14](/rulebook#30.14), [30.15](/rulebook#30.15), [30.16](/rulebook#30.16), [30.17](/rulebook#30.17), [30.18](/rulebook#30.18), [30.20](/rulebook#30.20), [30.21](/rulebook#30.21), [30.22](/rulebook#30.22), [30.23](/rulebook#30.23), [30.24](/rulebook#30.24), [30.25](/rulebook#30.25), [30.27](/rulebook#30.27), [30.29](/rulebook#30.29), [30.30](/rulebook#30.30), [30.31](/rulebook#30.31), [30.32](/rulebook#30.32), [45.12](/rulebook#45.12) |
|
|
8843
|
+
| `navigate` | `(url?: URL \| string, options?: { replace?: boolean, keepScroll?: boolean }) => Promise<void>` | Navigates. | [30.7](/rulebook#30.7), [30.9](/rulebook#30.9), [30.10](/rulebook#30.10), [30.11](/rulebook#30.11), [30.12](/rulebook#30.12), [30.13](/rulebook#30.13), [30.14](/rulebook#30.14), [30.15](/rulebook#30.15), [30.16](/rulebook#30.16), [30.17](/rulebook#30.17), [30.18](/rulebook#30.18), [30.20](/rulebook#30.20), [30.21](/rulebook#30.21), [30.22](/rulebook#30.22), [30.23](/rulebook#30.23), [30.24](/rulebook#30.24), [30.25](/rulebook#30.25), [30.27](/rulebook#30.27), [30.29](/rulebook#30.29), [30.30](/rulebook#30.30), [30.31](/rulebook#30.31), [30.32](/rulebook#30.32), [45.12](/rulebook#45.12), [45.24](/rulebook#45.24) |
|
|
8793
8844
|
|
|
8794
8845
|
## Taught in
|
|
8795
8846
|
|
|
@@ -8829,11 +8880,12 @@ Generated surfaces: what is written in place rather than imported.
|
|
|
8829
8880
|
| `name={expr}` | A reactive attribute or property. | [32.3](/rulebook#32.3), [32.20](/rulebook#32.20), [32.23](/rulebook#32.23), [32.37](/rulebook#32.37), [32.70](/rulebook#32.70), [32.71](/rulebook#32.71) |
|
|
8830
8881
|
| `on<event>={fn}` | A native listener on an element, and an ordinary prop on a component. | [32.4](/rulebook#32.4) |
|
|
8831
8882
|
| `name="…{expr}…"` | An interpolated quoted value. | [32.3](/rulebook#32.3), [32.71](/rulebook#32.71) |
|
|
8832
|
-
| `bind:value` | Two-way bind on an element. | [5.16](/rulebook#5.16), [32.5](/rulebook#32.5), [32.58](/rulebook#32.58), [32.59](/rulebook#32.59), [32.60](/rulebook#32.60), [32.61](/rulebook#32.61), [32.62](/rulebook#32.62), [32.72](/rulebook#32.72) |
|
|
8883
|
+
| `bind:value` | Two-way bind on an element. | [5.16](/rulebook#5.16), [32.5](/rulebook#32.5), [32.58](/rulebook#32.58), [32.59](/rulebook#32.59), [32.60](/rulebook#32.60), [32.61](/rulebook#32.61), [32.62](/rulebook#32.62), [32.72](/rulebook#32.72), [32.74](/rulebook#32.74) |
|
|
8833
8884
|
| `bind:prop={state}` | Hands a component prop the caller's `Reactive`, or a `{get, set}` pair, to write through. | [32.6](/rulebook#32.6), [33.6](/rulebook#33.6), [33.8](/rulebook#33.8), [14.18](/rulebook#14.18) |
|
|
8834
|
-
| `bind:checked` | A boolean bind on an input. | [32.7](/rulebook#32.7), [32.72](/rulebook#32.72) |
|
|
8835
|
-
| `bind:open` | The same, on a `<details>`. | [32.7](/rulebook#32.7), [32.72](/rulebook#32.72) |
|
|
8836
|
-
| `bind:group` | Radio or checkbox membership. | [32.8](/rulebook#32.8), [32.72](/rulebook#32.72) |
|
|
8885
|
+
| `bind:checked` | A boolean bind on an input. | [32.7](/rulebook#32.7), [32.72](/rulebook#32.72), [32.74](/rulebook#32.74) |
|
|
8886
|
+
| `bind:open` | The same, on a `<details>`. | [32.7](/rulebook#32.7), [32.72](/rulebook#32.72), [32.74](/rulebook#32.74) |
|
|
8887
|
+
| `bind:group` | Radio or checkbox membership. | [32.8](/rulebook#32.8), [32.72](/rulebook#32.72), [32.74](/rulebook#32.74) |
|
|
8888
|
+
| `bind:files` | A file input's files, as a list. | [32.73](/rulebook#32.73), [32.72](/rulebook#32.72), [32.74](/rulebook#32.74) |
|
|
8837
8889
|
| `bind:value={{get, set}}` | Two-way bind over an explicit accessor pair, on an element or a component. | [32.5](/rulebook#32.5), [32.6](/rulebook#32.6) |
|
|
8838
8890
|
| `bind:element={Reactive<Element> \| ((element: Element) => void \| Disposer)}` | A node reference. | [32.9](/rulebook#32.9), [32.38](/rulebook#32.38), [32.66](/rulebook#32.66), [32.68](/rulebook#32.68), [32.69](/rulebook#32.69) |
|
|
8839
8891
|
| `class:name={cond}` | Toggles a class. | [32.10](/rulebook#32.10), [32.71](/rulebook#32.71) |
|
|
@@ -9043,7 +9095,7 @@ Configuration: what is written in place rather than imported.
|
|
|
9043
9095
|
| `ABIDE_MAX_REQUEST_BODY_SIZE` | `number` | The default ceiling on a mutation's body. | [16.13](/rulebook#16.13), [16.54](/rulebook#16.54), [16.55](/rulebook#16.55), [16.78](/rulebook#16.78) |
|
|
9044
9096
|
| `ABIDE_MAX_SOCKET_MESSAGE_SIZE` | `number` | The ceiling on a frame received over the socket mux, in bytes. | [18.28](/rulebook#18.28) |
|
|
9045
9097
|
| `ABIDE_MAX_SOCKET_SUBSCRIPTIONS` | `number` | How many values one connection to the socket mux may follow. | [18.31](/rulebook#18.31), [18.32](/rulebook#18.32) |
|
|
9046
|
-
| `ABIDE_OFFLINE` | `boolean` | Whether the app keeps its pages and its bundle on the device, for when the network is gone. | [45.1](/rulebook#45.1), [45.2](/rulebook#45.2), [45.3](/rulebook#45.3), [45.4](/rulebook#45.4), [45.5](/rulebook#45.5), [45.6](/rulebook#45.6), [45.7](/rulebook#45.7), [45.8](/rulebook#45.8), [45.9](/rulebook#45.9), [45.10](/rulebook#45.10), [45.11](/rulebook#45.11), [45.12](/rulebook#45.12), [45.13](/rulebook#45.13), [45.14](/rulebook#45.14), [45.15](/rulebook#45.15), [45.16](/rulebook#45.16), [45.17](/rulebook#45.17), [45.18](/rulebook#45.18), [45.19](/rulebook#45.19), [45.20](/rulebook#45.20), [45.21](/rulebook#45.21), [45.22](/rulebook#45.22), [45.23](/rulebook#45.23) |
|
|
9098
|
+
| `ABIDE_OFFLINE` | `boolean` | Whether the app keeps its pages and its bundle on the device, for when the network is gone. | [45.1](/rulebook#45.1), [45.2](/rulebook#45.2), [45.3](/rulebook#45.3), [45.4](/rulebook#45.4), [45.5](/rulebook#45.5), [45.6](/rulebook#45.6), [45.7](/rulebook#45.7), [45.8](/rulebook#45.8), [45.9](/rulebook#45.9), [45.10](/rulebook#45.10), [45.11](/rulebook#45.11), [45.12](/rulebook#45.12), [45.13](/rulebook#45.13), [45.14](/rulebook#45.14), [45.15](/rulebook#45.15), [45.16](/rulebook#45.16), [45.17](/rulebook#45.17), [45.18](/rulebook#45.18), [45.19](/rulebook#45.19), [45.20](/rulebook#45.20), [45.21](/rulebook#45.21), [45.22](/rulebook#45.22), [45.23](/rulebook#45.23), [45.24](/rulebook#45.24) |
|
|
9047
9099
|
| `ABIDE_STOP_TIMEOUT` | `number` | How long `App.stop` drains, in ms. | [42.17](/rulebook#42.17), [42.18](/rulebook#42.18) |
|
|
9048
9100
|
| `ABIDE_MAX_STREAM_BUFFER_SIZE` | `number` | The cap on a stream transcript held in memory. | [36.18](/rulebook#36.18) |
|
|
9049
9101
|
| `ABIDE_MAX_LOG_BUFFER_COUNT` | `number` | How many lines `log.records` is trimmed back to. | [28.1](/rulebook#28.1) |
|
package/package.json
CHANGED
|
@@ -84,10 +84,10 @@ export type ClassifiedAttribute = {
|
|
|
84
84
|
// 32.11 — a spread's attributes, less the ones the element names itself, kept written by a
|
|
85
85
|
// binding of its own.
|
|
86
86
|
| { kind: 'spread'; explicit: string[] }
|
|
87
|
-
// 32.5, 32.7, 32.8 — the property and the value, kept in step both ways. `reflects` is
|
|
88
|
-
// attribute the property's value stands at in the tag, and `undefined` where it is not an
|
|
87
|
+
// 32.5, 32.7, 32.8, 32.73 — the property and the value, kept in step both ways. `reflects` is
|
|
88
|
+
// the attribute the property's value stands at in the tag, and `undefined` where it is not an
|
|
89
89
|
// attribute: a `<textarea>`'s value is its text and a `<select>`'s is its options' `selected`,
|
|
90
|
-
// which the markup writes inside the tag (32.61).
|
|
90
|
+
// which the markup writes inside the tag (32.61), and a file input's files are no markup at all.
|
|
91
91
|
| {
|
|
92
92
|
kind: 'bound'
|
|
93
93
|
bind: ValueBind
|
|
@@ -135,7 +135,7 @@ export function classifyAttributes(
|
|
|
135
135
|
readsAnItem(holder: object): boolean
|
|
136
136
|
},
|
|
137
137
|
): ClassifiedAttribute[] {
|
|
138
|
-
|
|
138
|
+
refuseBinds(node)
|
|
139
139
|
const groups = mergeGroupsOf(node)
|
|
140
140
|
const sharing: ReturnType<typeof sharingOf>[] = []
|
|
141
141
|
for (const group of groups) sharing.push(sharingOf(group, lowering))
|
|
@@ -210,7 +210,8 @@ export function classifyAttributes(
|
|
|
210
210
|
kind: 'bound',
|
|
211
211
|
bind,
|
|
212
212
|
reflects:
|
|
213
|
-
bind === '
|
|
213
|
+
bind === 'files' ||
|
|
214
|
+
(bind === 'value' && node.lowerTag !== 'input')
|
|
214
215
|
? undefined
|
|
215
216
|
: bind === 'group'
|
|
216
217
|
? 'checked'
|
|
@@ -354,15 +355,22 @@ function heldClasses(groups: MergeGroup[]): string[] {
|
|
|
354
355
|
}
|
|
355
356
|
|
|
356
357
|
/**
|
|
358
|
+
* 32.74 — A `bind:` AN ELEMENT HAS NO PROPERTY FOR IS REFUSED: `bind:vlaue` would otherwise be an
|
|
359
|
+
* attribute of that name, binding nothing and saying nothing.
|
|
360
|
+
*
|
|
357
361
|
* 32.72 — WHAT A `bind:` WRITES IS THE BIND'S ALONE: `checked` beside `bind:checked` or
|
|
358
362
|
* `bind:group`, `value` beside `bind:value`, any `bind:name` beside `name` — and an
|
|
359
363
|
* `<option selected>` inside a `<select bind:value>`, and a `<textarea bind:value>`'s text. Two
|
|
360
364
|
* owners of one value render whichever the browser keeps and move with whichever wrote last.
|
|
361
365
|
*/
|
|
362
|
-
function
|
|
366
|
+
function refuseBinds(node: ElementNode): void {
|
|
363
367
|
for (const attribute of node.attributes) {
|
|
364
368
|
const lower = attribute.lowerName
|
|
365
369
|
if (!lower.startsWith('bind:') || lower === 'bind:element') continue
|
|
370
|
+
if (valueBindOf(lower) === undefined)
|
|
371
|
+
throw new Error(
|
|
372
|
+
`abide: <${node.tag}> has ${attribute.name}, which binds nothing — an element binds ${VALUE_BINDS.join(', ')} or element. RULEBOOK 32.74.`,
|
|
373
|
+
)
|
|
366
374
|
const owned =
|
|
367
375
|
lower === 'bind:group' ? 'checked' : lower.slice('bind:'.length)
|
|
368
376
|
const beside = attributeOf(node, owned)
|
|
@@ -468,18 +476,17 @@ function isElementBind(lowerName: string): boolean {
|
|
|
468
476
|
/**
|
|
469
477
|
* The binds that keep a property and a value in step.
|
|
470
478
|
*
|
|
471
|
-
* 32.5, 32.7, 32.8 — both emitters ask: the server writes the value into the tag
|
|
472
|
-
* binds the property.
|
|
479
|
+
* 32.5, 32.7, 32.8, 32.73 — both emitters ask: the server writes the value into the tag, where it
|
|
480
|
+
* has one, and the client binds the property.
|
|
473
481
|
*/
|
|
474
|
-
|
|
482
|
+
const VALUE_BINDS = ['value', 'checked', 'open', 'group', 'files'] as const
|
|
483
|
+
type ValueBind = (typeof VALUE_BINDS)[number]
|
|
475
484
|
|
|
476
485
|
/** The value bind a folded attribute name is, or `undefined` for any other attribute. */
|
|
477
486
|
function valueBindOf(lowerName: string): ValueBind | undefined {
|
|
478
|
-
if (lowerName
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
if (lowerName === 'bind:group') return 'group'
|
|
482
|
-
return undefined
|
|
487
|
+
if (!lowerName.startsWith('bind:')) return undefined
|
|
488
|
+
const bind = lowerName.slice('bind:'.length) as ValueBind
|
|
489
|
+
return VALUE_BINDS.includes(bind) ? bind : undefined
|
|
483
490
|
}
|
|
484
491
|
|
|
485
492
|
/**
|
|
@@ -769,7 +769,7 @@ function elementUnits(
|
|
|
769
769
|
handlers.push(
|
|
770
770
|
`${element}.addEventListener(${JSON.stringify(one.event)}, ${lowering.listener(one.attribute)})`,
|
|
771
771
|
)
|
|
772
|
-
// 32.5, 32.7, 32.8 — THE PROPERTY AND THE VALUE, IN STEP BOTH WAYS: a watch on this
|
|
772
|
+
// 32.5, 32.7, 32.8, 32.73 — THE PROPERTY AND THE VALUE, IN STEP BOTH WAYS: a watch on this
|
|
773
773
|
// region's scope writes the property, and a listener writes it back.
|
|
774
774
|
// 32.8 — a group compares against the input's own value, read where the value is.
|
|
775
775
|
else if (one.kind === 'bound' && one.bind === 'group')
|
|
@@ -782,6 +782,10 @@ function elementUnits(
|
|
|
782
782
|
: JSON.stringify(one.own.value)
|
|
783
783
|
}), ${SCOPE})`,
|
|
784
784
|
)
|
|
785
|
+
else if (one.kind === 'bound' && one.bind === 'files')
|
|
786
|
+
binds.push(
|
|
787
|
+
`${lowering.use('bindFiles')}(${element}, ${lowering.reference(one.attribute)}, ${SCOPE})`,
|
|
788
|
+
)
|
|
785
789
|
else if (one.kind === 'bound')
|
|
786
790
|
binds.push(
|
|
787
791
|
`${lowering.use('bindProperty')}(${element}, ${JSON.stringify(one.bind)}, ${lowering.reference(one.attribute)}, ${SCOPE})`,
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
// The `abide-bridge` release a pairing command names, so a visitor's `bunx` runs the relay this
|
|
2
2
|
// server speaks. `packages/abide/tests/claudeCode.test.ts` holds it to the bridge package's own.
|
|
3
|
-
export const BRIDGE_VERSION = '0.3.
|
|
3
|
+
export const BRIDGE_VERSION = '0.3.4'
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The image types a model is shown (44.36). A leaf, because `assistant` refuses a transcript image of
|
|
3
|
+
* any other and `/__abide/mcp` answers one of any other as its text (20.21).
|
|
4
|
+
*/
|
|
5
|
+
export const IMAGE_TYPES = new Set([
|
|
6
|
+
'image/png',
|
|
7
|
+
'image/jpeg',
|
|
8
|
+
'image/gif',
|
|
9
|
+
'image/webp',
|
|
10
|
+
])
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
// 21.22, 21.23 — HOW LONG A COPY KEPT ON THE DEVICE STAYS SERVABLE once stale, where `ABIDE_OFFLINE`
|
|
2
|
-
// registers its worker: a week
|
|
3
|
-
//
|
|
2
|
+
// registers its worker: a week while the network answers and the copy is asked for again behind it,
|
|
3
|
+
// and a year where the network fails — a copy of any age beats the page saying nothing was saved, and
|
|
4
|
+
// what may evict it sooner (Safari's week for a site not visited) is the browser's, not this header's.
|
|
4
5
|
//
|
|
5
6
|
// NO `max-age` BESIDE IT on a page or a `GET`: a copy reads `max-age` as its own life (18.14), and
|
|
6
7
|
// a followed copy stale from the moment it landed would reload on every read. With no lifetime and
|
|
7
8
|
// no `Last-Modified`, a cache holds the answer stale at once, which is all the two windows need.
|
|
8
9
|
|
|
9
10
|
export const OFFLINE_STALE =
|
|
10
|
-
'stale-while-revalidate=604800, stale-if-error=
|
|
11
|
+
'stale-while-revalidate=604800, stale-if-error=31536000'
|