@hearthscale/app 0.0.0-stage → 0.1.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/LICENSE ADDED
@@ -0,0 +1,25 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Simon Wagenfuehrer
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
23
+ The fonts in `dist/fonts` are licensed under the SIL Open Font License 1.1
24
+ (`dist/fonts/OFL.txt`). The Remix Icon font in `dist/remixicon` is licensed
25
+ under the Apache License 2.0 (`dist/remixicon/License`).
package/README.md CHANGED
@@ -1,3 +1,91 @@
1
- # Temporary Holding Version
1
+ # @hearthscale/app
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ The types an app imports: its manifest, `app.json` (`Manifest`), what
4
+ `backend.js` exports (`Backend`), what its
5
+ tool bodies receive (`AppContext`, `ToolRun`), and the slots a view
6
+ reserves for the shell's chat, composer, cards, live terminal and the
7
+ tab's web page (`HostSlot`). The package carries no runtime: the app host serves `require('@hearthscale/app')` from
8
+ itself, so a bundle with the package marked external ships nothing of the
9
+ platform.
10
+
11
+ ```sh
12
+ npm install --save-dev @hearthscale/app
13
+ ```
14
+
15
+ Type-check a backend with `"lib": ["ES2022", "DOM"]`: `AppContext.signal`
16
+ is an `AbortSignal` and `models.complete` answers an `AsyncIterable`. That
17
+ library also declares `fetch` and `URL`, which the backend's engine does not
18
+ have; a backend reaches the network with `ctx.net`. The manifest's schema is
19
+ `https://hearthscale.com/app.schema.json`.
20
+
21
+ A view draws with the UI kit, `@hearthscale/app-ui` beside this package:
22
+ the tokens, the `hs-*` classes, React parts and the Remix Icon font the host
23
+ loads into every view.
24
+
25
+ A view calls the methods every view may call (`ViewHostMethods`), the
26
+ `hearthscale/*` extensions and `openai/files/open` (`ViewExtensionMethods`)
27
+ as requests of its MCP Apps `App`, and hears the host's notifications
28
+ (`HostSlotNotification`, `ViewExtensionNotification`) on
29
+ `app.fallbackNotificationHandler`. `ViewHostContext` types the keys the host
30
+ adds to the host context, and `ContentBlockMeta`, `ModelContextAnswer`,
31
+ `MessageOptions`, `ResourceReadOptions`, `ResourceVersion` and
32
+ `ResourcePath` the `_meta` of the standard messages. `ViewExtensionCall`
33
+ types one call of an extension:
34
+
35
+ ```js
36
+ import { App, McpUiMessageResultSchema } from '@modelcontextprotocol/ext-apps';
37
+ const call = (method, params) => app.request({ method, params }, McpUiMessageResultSchema);
38
+ const { sessions } = await call('hearthscale/sessions/list', {});
39
+ ```
40
+
41
+ A view keeps a JSON value for its next load with
42
+ `hearthscale/ui/set-widget-state` (`{ state }`, at most 64 KiB as JSON) and
43
+ reads it back from the host context as `hearthscale/widgetState`, null
44
+ before the first save. The host loads a view in a tab again when the tab
45
+ comes back after the window discarded its surface, and after a restart.
46
+ A view that fills a tab names it with `hearthscale/ui/set-tab`
47
+ (`{ title?, icon? }`, the icon a picture as a `data:` address); null gives
48
+ back the surface's own title or mark, and the tab keeps the name through a
49
+ restart.
50
+
51
+ A view whose app names `models` in `uses` asks the model with MCP's
52
+ `sampling/createMessage` (text only, on the app's chat card).
53
+ `hearthscale/ui/select-files` (`{ accept?, multiple? }`, on a click) answers
54
+ the files the person picked, which the view reads with `resources/read`;
55
+ `hearthscale/ui/upload-file` (`{ name, mediaType, blob }`) keeps bytes as an
56
+ asset the view reads back as `hearthscale-asset://<asset>`.
57
+
58
+ A view that fills a tab has a web page of its tab: the shell loads it on
59
+ the app's own partition, where it reaches only the hosts of
60
+ `environment.network`, once the view asks for an address with
61
+ `hearthscale/web/navigate`. The page lies over the view's `web` slot, and
62
+ under the view while the view covers it (`hearthscale/web/cover`), so a
63
+ menu the view opens draws over the page and the page shows through where
64
+ the view draws nothing. The view drives the page with the other
65
+ `hearthscale/web/*` methods and hears its state as `hearthscale/web/page`
66
+ (`WebPageState`). A link the page opens in a new window opens a tab of the
67
+ same surface. The page keeps its address through a restart, and the app's
68
+ tools drive it through `surface.command`.
69
+
70
+ An app that names `peers` in `uses` talks to itself on other computers in
71
+ rooms. A view opens a room with `hearthscale/peers/create` and shows the
72
+ person its ticket to pass on; `hearthscale/peers/join` asks the person for
73
+ a ticket in a sheet the shell draws, so the view never names one. Both
74
+ need a click. The view and the backend (`ctx.peers`) then send messages,
75
+ JSON of at most 64 KiB, to the room's other members, hear
76
+ theirs (`hearthscale/peers/message`, `ctx.peers.on('message')`) and leave.
77
+ A room carries only these messages: no file, process or credential of
78
+ either platform. It ends for everyone when the computer that opened it
79
+ leaves. A member keeps its place and joins again on its own after a drop
80
+ or a restart; no message is kept.
81
+
82
+ ## Traps
83
+
84
+ - The channel carries JSON only. Bytes cross as base64 inside the host;
85
+ the context's `assets` and `net` methods hide that, but a tool body
86
+ that returns a `Uint8Array` returns nothing useful: publish it as an
87
+ asset and answer the id.
88
+ - `App.request` checks a result against the schema it is given, and drops
89
+ every field the schema does not name unless the schema keeps them.
90
+ `McpUiMessageResultSchema` keeps every field, so an extension's result
91
+ arrives whole.