@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.
@@ -0,0 +1,3230 @@
1
+ /**
2
+ * The platform half of the contract, generated from the Rust types of
3
+ * the `contract` crate by `npm run contract` in `apps/platform/scripts`.
4
+ * Do not edit: change the Rust types and run the command again.
5
+ */
6
+
7
+ /**
8
+ * One line of a session log: the body its `type` names, and the envelope
9
+ * every entry shares. Tree entries carry `parent`; the others are out of
10
+ * the tree and apply to the whole session.
11
+ */
12
+ type Entry = {
13
+ seq: number;
14
+ id: string;
15
+ at: string;
16
+ session: string;
17
+ by: PrincipalRef;
18
+ origin: Origin;
19
+ } & ({
20
+ app: string;
21
+ appVersion: string;
22
+ agent: string;
23
+ authoritySession: string;
24
+ project?: string;
25
+ forkedFrom?: ForkPoint;
26
+ spawnedBy?: SpawnPoint;
27
+ startedBy?: PrincipalRef;
28
+ type: 'session.created';
29
+ } | {
30
+ title: string;
31
+ type: 'session.title';
32
+ } | {
33
+ turn: string;
34
+ result: Structured;
35
+ type: 'session.result';
36
+ } | {
37
+ type: 'session.closed';
38
+ } | {
39
+ project: string | null;
40
+ pinned: boolean;
41
+ archived: boolean;
42
+ rank: number;
43
+ type: 'session.filing';
44
+ } | {
45
+ model?: string | null;
46
+ type: 'session.settings';
47
+ } | {
48
+ to: string | null;
49
+ type: 'session.leaf';
50
+ } | {
51
+ attached: SessionRoot[];
52
+ type: 'session.roots';
53
+ } | {
54
+ turn: string;
55
+ model: string;
56
+ provider: string;
57
+ location: ModelLocation;
58
+ revision: string;
59
+ type: 'turn.started';
60
+ } | {
61
+ turn: string;
62
+ stopReason: StopReason;
63
+ type: 'turn.finished';
64
+ } | {
65
+ parent: string | null;
66
+ parts: UserPart[];
67
+ type: 'user.message';
68
+ } | {
69
+ parent: string | null;
70
+ turn: string;
71
+ parts: AgentPart[];
72
+ toolCalls?: ToolCall[];
73
+ usage?: Usage;
74
+ stop: AgentStop;
75
+ model: string;
76
+ provider: string;
77
+ error?: string;
78
+ type: 'agent.message';
79
+ } | {
80
+ turn: string;
81
+ partId: string;
82
+ text: string;
83
+ type: 'agent.part';
84
+ } | {
85
+ callId: string;
86
+ turn: string;
87
+ /**
88
+ * The call this one runs under; absent for a call of the turn.
89
+ */
90
+ under?: string;
91
+ name: string;
92
+ /**
93
+ * JSON-encoded, as the model emitted them.
94
+ */
95
+ arguments: string;
96
+ context: CallContext;
97
+ pin?: string;
98
+ /**
99
+ * Absent for a core tool, and for a name that named no tool.
100
+ */
101
+ label?: CallLabel;
102
+ type: 'call.prepared';
103
+ } | {
104
+ callId: string;
105
+ turn: string;
106
+ under?: string;
107
+ executor: Executor;
108
+ type: 'call.dispatched';
109
+ } | {
110
+ /**
111
+ * The tree parent; a call under another call has none and stays
112
+ * out of the tree.
113
+ */
114
+ parent?: string | null;
115
+ turn: string;
116
+ callId: string;
117
+ under?: string;
118
+ content: Content[];
119
+ structured?: Structured;
120
+ /**
121
+ * Values for the view that renders the result; they never reach a
122
+ * model.
123
+ */
124
+ meta?: {
125
+ [k: string]: unknown;
126
+ };
127
+ isError: boolean;
128
+ outcome: ToolOutcome;
129
+ /**
130
+ * The provider's view that renders the result, as its manifest
131
+ * declares it for the tool.
132
+ */
133
+ view?: ViewRef;
134
+ /**
135
+ * The paths the call changed, and the ones it read.
136
+ */
137
+ wrote?: string[];
138
+ read?: string[];
139
+ /**
140
+ * The model tokens the body spent through `models.complete`.
141
+ */
142
+ usage?: Usage;
143
+ type: 'tool.result';
144
+ } | {
145
+ turn: string;
146
+ callId: string;
147
+ decision: Decision;
148
+ decidedBy: DecisionBy;
149
+ review?: ReviewRecord;
150
+ ask?: string;
151
+ grant?: Grant;
152
+ reason?: string;
153
+ type: 'consent.decision';
154
+ } | {
155
+ cell: number;
156
+ callId: string;
157
+ turn: string;
158
+ type: 'cell.started';
159
+ } | {
160
+ cell: number;
161
+ callId: string;
162
+ outcome: CellOutcome;
163
+ /**
164
+ * Why a program failed, in words.
165
+ */
166
+ error?: string;
167
+ /**
168
+ * The tail of the program's output.
169
+ */
170
+ output: string;
171
+ type: 'cell.ended';
172
+ } | {
173
+ process: number;
174
+ callId: string;
175
+ turn: string;
176
+ command: string;
177
+ pid: number;
178
+ startedAt: string;
179
+ type: 'process.started';
180
+ } | {
181
+ process: number;
182
+ callId: string;
183
+ outcome: ProcessOutcome;
184
+ /**
185
+ * The exit code of a command that exited.
186
+ */
187
+ code?: number;
188
+ /**
189
+ * Why a command was stopped, in words.
190
+ */
191
+ reason?: string;
192
+ /**
193
+ * The tail of the command's output.
194
+ */
195
+ output: string;
196
+ type: 'process.ended';
197
+ } | {
198
+ ask: Ask;
199
+ type: 'ask.opened';
200
+ } | {
201
+ ask: string;
202
+ outcome: AskOutcome;
203
+ answeredBy?: PrincipalRef;
204
+ option?: string;
205
+ allow?: boolean;
206
+ grant?: Grant;
207
+ /**
208
+ * The person's own words, answering a question.
209
+ */
210
+ text?: string;
211
+ type: 'ask.closed';
212
+ } | {
213
+ parent: string | null;
214
+ app: string;
215
+ kind: string;
216
+ data: unknown;
217
+ /**
218
+ * Text the model sees for the entry; absent, it is display only.
219
+ */
220
+ context?: string;
221
+ type: 'app.entry';
222
+ } | {
223
+ asset: string;
224
+ model: string | null;
225
+ /**
226
+ * The name the model that read the picture shows under.
227
+ */
228
+ label?: string;
229
+ text?: string;
230
+ reason?: DescriptionFailure;
231
+ usage?: Usage;
232
+ type: 'asset.described';
233
+ } | {
234
+ watermark: string;
235
+ rounds: number;
236
+ type: 'compaction.cleared';
237
+ } | {
238
+ tailStart: string;
239
+ summary: string;
240
+ sections: {
241
+ [k: string]: string;
242
+ };
243
+ kept: string[];
244
+ tokensBefore: number;
245
+ tokensAfter: number;
246
+ model: string;
247
+ provider: string;
248
+ location: ModelLocation;
249
+ usage?: Usage;
250
+ type: 'compaction.folded';
251
+ } | {
252
+ reason: string;
253
+ model: string;
254
+ type: 'compaction.failed';
255
+ } | {
256
+ parent: string | null;
257
+ turn: string;
258
+ what: BudgetWhat;
259
+ type: 'budget.reached';
260
+ } | {
261
+ parent: string | null;
262
+ turn: string;
263
+ calls: string[];
264
+ type: 'repair.interrupted';
265
+ });
266
+ /**
267
+ * Who acts. A surface is one attachment of a client; an app is a package;
268
+ * a view is an app's view hosted by another app; the core is the daemon
269
+ * itself, which appears only as the author of entries it writes.
270
+ */
271
+ type PrincipalRef = {
272
+ id: string;
273
+ kind: 'surface';
274
+ } | {
275
+ id: string;
276
+ kind: 'app';
277
+ } | {
278
+ id: string;
279
+ host: string;
280
+ kind: 'view';
281
+ } | {
282
+ kind: 'core';
283
+ };
284
+ /**
285
+ * Where a message or a call came from: the person, the person while a
286
+ * turn ran, an app's backend, an agent, a view, or a window's own act
287
+ * outside any conversation.
288
+ */
289
+ type Origin = 'user' | 'steer' | 'app' | 'agent' | 'view' | 'surface';
290
+ type MountMode = 'read' | 'read-write';
291
+ type RootKind = 'home' | 'workspace' | 'project' | 'memory' | 'skill' | 'folder' | 'file';
292
+ type ModelLocation = 'local' | 'lan' | 'cloud';
293
+ type StopReason = ('end_turn' | 'length' | 'budget' | 'cancelled' | 'error' | 'refused' | 'app_disabled' | 'app_update' | 'interrupted') | 'no_result';
294
+ /**
295
+ * A part of a user message.
296
+ */
297
+ type UserPart = {
298
+ text: string;
299
+ kind: 'text';
300
+ } | {
301
+ asset: string;
302
+ name: string;
303
+ mediaType: string;
304
+ kind: 'asset';
305
+ } | {
306
+ root: string;
307
+ path: string;
308
+ kind: 'file';
309
+ } | {
310
+ name: string;
311
+ kind: 'skill';
312
+ } | {
313
+ app: string;
314
+ item: MentionItem;
315
+ kind: 'mention';
316
+ } | {
317
+ app: string;
318
+ title?: string;
319
+ content: Content;
320
+ kind: 'context';
321
+ };
322
+ /**
323
+ * What a tool result carries: a closed union, never a bare string. A
324
+ * result's one structured value travels beside its content.
325
+ */
326
+ type Content = {
327
+ text: string;
328
+ kind: 'text';
329
+ } | {
330
+ asset: string;
331
+ mediaType: string;
332
+ name?: string;
333
+ kind: 'asset';
334
+ } | {
335
+ uri: string;
336
+ name?: string;
337
+ kind: 'resource_link';
338
+ };
339
+ /**
340
+ * A part of an agent message.
341
+ */
342
+ type AgentPart = {
343
+ text: string;
344
+ kind: 'text';
345
+ } | {
346
+ text: string;
347
+ kind: 'reasoning';
348
+ };
349
+ type AgentStop = 'end_turn' | 'tool_calls' | 'length' | 'cancelled' | 'error' | 'refused' | 'interrupted';
350
+ /**
351
+ * A transcript mark an app's manifest names for a tool.
352
+ */
353
+ type ChatIconRole = 'back' | 'brain' | 'camera' | 'check' | 'chevron-down' | 'alert' | 'clock' | 'code' | 'eye' | 'globe' | 'hammer' | 'image' | 'keyboard' | 'click' | 'scroll' | 'search' | 'pen' | 'terminal' | 'x' | 'compact' | 'agent' | 'lightbulb' | 'music' | 'speech' | 'transcript' | 'connector' | 'question' | 'view';
354
+ type SourceKind = 'app' | 'connector';
355
+ type ToolOutcome = 'completed' | 'failed' | 'cancelled' | 'skipped' | 'unknown';
356
+ type Decision = 'run' | 'refused';
357
+ /**
358
+ * Who or what let a call run, or refused it.
359
+ */
360
+ type DecisionBy = 'level' | 'silent' | 'grant' | 'fence' | 'judge' | 'review' | 'person' | 'floor';
361
+ type Risk = 'low' | 'high' | 'too_destructive';
362
+ type Authorisation = 'explicitly_no' | 'neutral' | 'explicitly_yes';
363
+ /**
364
+ * The rights a grant may hold; a closed list.
365
+ */
366
+ type Right = 'fs.read' | 'fs.write' | 'net.host' | 'connector' | 'capability' | 'sessions.create' | 'terminal' | 'reader';
367
+ /**
368
+ * How a program ended: at its end, with an error or a limit, stopped, or
369
+ * lost with a daemon that did not stop cleanly.
370
+ */
371
+ type CellOutcome = 'finished' | 'failed' | 'stopped' | 'lost';
372
+ /**
373
+ * How a background command ended: it exited, it was stopped, or it was
374
+ * lost with a daemon that did not stop cleanly.
375
+ */
376
+ type ProcessOutcome = 'exited' | 'stopped' | 'lost';
377
+ /**
378
+ * What a card asks about. One `tool` kind covers a core tool, an app's
379
+ * own tool, a connector tool and another app's capability; an app's own
380
+ * question is `app:<id>/<kind>`.
381
+ */
382
+ type AskKind = CoreAskKind | AppAskKind;
383
+ /**
384
+ * What a card asks about, besides an app's own question.
385
+ */
386
+ type CoreAskKind = ('tool' | 'root' | 'connector.tools' | 'connector.login' | 'app.login' | 'terminal' | 'app.dev' | 'sandbox.violation') | 'question' | 'elicitation';
387
+ /**
388
+ * An app's own question kind, `app:<id>/<kind>`.
389
+ */
390
+ type AppAskKind = `app:${string}`;
391
+ type PathShape = 'folder' | 'file';
392
+ type GrantScope = 'session' | 'always';
393
+ type AskOutcome = 'answered' | 'expired' | 'closed' | 'covered';
394
+ /**
395
+ * Why an image has no description in place of one.
396
+ */
397
+ type DescriptionFailure = 'over_budget' | 'vision_model_absent' | 'engine_failed';
398
+ type BudgetWhat = 'tokens' | 'rounds';
399
+ type EnvironmentKind = 'process' | 'vm';
400
+ /**
401
+ * The tab commands a tool body may send to the session's executor
402
+ * surface. A command drives the page of the app's first surface, in the
403
+ * manifest's order, placed in the rail or a tab: a `web` surface's own
404
+ * page, or the web page of a view's tab. `navigate` loads an address in
405
+ * the session's tab, opening it when the session has none, hidden for a
406
+ * view's tab; `evaluate` runs a script and answers its value;
407
+ * `screenshot` answers the page as an asset; `show` puts the tab in front
408
+ * of the person; `close` ends it.
409
+ */
410
+ type SurfaceOp = 'navigate' | 'evaluate' | 'screenshot' | 'show' | 'close';
411
+ /**
412
+ * One setting's value.
413
+ */
414
+ type SettingValue = string | number | boolean;
415
+ /**
416
+ * The kinds a job runs on: every kind but chat.
417
+ */
418
+ type JobKind = 'embedding' | 'image' | 'music' | 'speech' | 'transcription';
419
+ type JobStatus = 'queued' | 'running' | 'completed' | 'failed' | 'cancelled';
420
+ type SessionStatus = 'idle' | 'running' | 'requires_action' | 'terminated';
421
+ /**
422
+ * One picture for both colour schemes, or one per scheme.
423
+ */
424
+ type Schemed = ServedPicture | SchemePair;
425
+ /**
426
+ * A picture the platform lets out of the package beside its page:
427
+ * `icon.svg`, `icon.png` or a path under `assets/`.
428
+ */
429
+ type ServedPicture = string;
430
+ /**
431
+ * What an app is for: one to three per listing, from this list and no
432
+ * other.
433
+ */
434
+ type Category = 'chat' | 'writing' | 'coding' | 'research' | 'notes' | 'productivity' | 'communication' | 'data' | 'finance' | 'design' | 'audio' | 'video' | 'games' | 'education' | 'health' | 'lifestyle' | 'automation' | 'utilities';
435
+ /**
436
+ * What a listing discloses about money, never a price: nothing is paid,
437
+ * something behind it is, or the whole of it is.
438
+ */
439
+ type PaymentKind = 'free' | 'optional' | 'paid';
440
+ /**
441
+ * An entry of `environment.network`.
442
+ */
443
+ type NetworkEntry = string;
444
+ type Lifetime = 'service';
445
+ /**
446
+ * The kinds a model card answers on.
447
+ */
448
+ type ModelKind = 'chat' | 'embedding' | 'image' | 'music' | 'speech' | 'transcription';
449
+ type Reasoning = 'none' | 'auto' | 'high';
450
+ /**
451
+ * A relative path inside the package, forward slashes, never leaving it.
452
+ */
453
+ type PackagePath = string;
454
+ /**
455
+ * The well-known roots an agent may name; a project adds `project` on its
456
+ * own.
457
+ */
458
+ type AgentRoot = 'home' | 'workspace' | 'memory';
459
+ /**
460
+ * Who may call a tool, as MCP Apps names them: `model`, the agents;
461
+ * `app`, the app's own views.
462
+ */
463
+ type ToolVisibility = 'model' | 'app';
464
+ /**
465
+ * The namespaces `uses` may name: the backend's channel namespaces, the
466
+ * view extensions, and `files.open`. A backend and a view reach `store`,
467
+ * `events`, `models`, `jobs` and `peers` alike, each only when its app
468
+ * names it here. An agent's core tools need no namespace here.
469
+ */
470
+ type Uses = 'sessions' | 'sessions.create' | 'secrets' | 'notifications' | 'readers' | 'roots' | 'exec' | 'catalogue' | 'oauth' | 'surface' | 'store' | 'events' | 'models' | 'jobs' | 'terminals' | 'files.open' | 'surfaces.open' | 'peers';
471
+ /**
472
+ * What a surface is: `view`, an MCP Apps document in a sandboxed frame,
473
+ * which may reserve slots for the chat the shell draws for the app's own
474
+ * agent; `web`, a web page on the app's own partition.
475
+ */
476
+ type SurfaceDeclaration = {
477
+ id: string;
478
+ title: string;
479
+ /**
480
+ * The mark of the surface's tab and launcher row; absent, they show
481
+ * the app's icon.
482
+ */
483
+ icon?: Schemed;
484
+ /**
485
+ * @minItems 1
486
+ */
487
+ placements: Placement[];
488
+ /**
489
+ * @minItems 1
490
+ */
491
+ quickActions?: QuickAction[];
492
+ /**
493
+ * The view's document: a file in the package, or a `ui://` resource
494
+ * of one of the app's MCP servers.
495
+ */
496
+ entry: string;
497
+ /**
498
+ * The tool the host calls when the surface opens, with `{}`, or with
499
+ * the `FileInput` of the file it opens; its result is the view's first
500
+ * tool result. Absent, the view receives the tool input alone.
501
+ */
502
+ tool?: string;
503
+ /**
504
+ * The file extensions the view opens, in a tab, in place of the
505
+ * system's own app; with `https:` and `http:`, the web addresses of
506
+ * those schemes, each as the web page of a new tab of the view, which
507
+ * makes the app one the person may pick as their browser.
508
+ *
509
+ * @minItems 1
510
+ */
511
+ opens?: Opened[];
512
+ /**
513
+ * What the view shows at an address. A file a turn changed opens in
514
+ * the one usable app whose view shows its change when the working
515
+ * tree of its repository holds one, and in the one whose view shows
516
+ * files when it does not; the window first gives that app read
517
+ * access to the repository or the file's folder.
518
+ *
519
+ * @minItems 1
520
+ */
521
+ shows?: Shown[];
522
+ kind: 'view';
523
+ } | {
524
+ id: string;
525
+ title: string;
526
+ /**
527
+ * The mark of the surface's tab and launcher row; absent, they show
528
+ * the app's icon.
529
+ */
530
+ icon?: Schemed;
531
+ /**
532
+ * @minItems 1
533
+ */
534
+ placements: Placement[];
535
+ /**
536
+ * @minItems 1
537
+ */
538
+ quickActions?: QuickAction[];
539
+ /**
540
+ * The page the surface opens on: an `https:` address, or `http:` on
541
+ * this machine, whose host `environment.network` covers.
542
+ */
543
+ entry: string;
544
+ /**
545
+ * Local storage the page finds at each load: on the origin of
546
+ * `entry`, each key holds its value before the page's own scripts
547
+ * run, so a setting the site keeps there, such as its theme, is the
548
+ * app's.
549
+ */
550
+ storage?: {
551
+ [k: string]: string;
552
+ };
553
+ kind: 'web';
554
+ };
555
+ /**
556
+ * Where a surface appears: `rail` gives the app its rail button, which
557
+ * opens the surface in the focused tile; `tab` lists it in the launcher as
558
+ * a tab of a tile; `inline` draws it in a transcript, under the result of
559
+ * each of the app's tools whose `view` names it; `modal` draws it over a
560
+ * tile of its app when a view of the app opens it with
561
+ * `hearthscale/ui/open-modal`.
562
+ */
563
+ type Placement = 'rail' | 'tab' | 'inline' | 'modal';
564
+ /**
565
+ * What a view surface opens: a file extension, or a scheme of web
566
+ * addresses.
567
+ */
568
+ type Opened = string;
569
+ /**
570
+ * What a view surface shows at an address the window opens it on,
571
+ * `/?path=<absolute path>` with the path percent-encoded: `file`, a file
572
+ * of this machine in its folder; `change`, a file's change in the working
573
+ * tree of its git repository.
574
+ */
575
+ type Shown = 'file' | 'change';
576
+ /**
577
+ * The literal `object`, the type of a settings schema.
578
+ */
579
+ type ObjectType = 'object';
580
+ /**
581
+ * One setting as a JSON Schema property.
582
+ */
583
+ type SettingDeclaration = {
584
+ /**
585
+ * The setting's label.
586
+ */
587
+ title: string;
588
+ /**
589
+ * The tooltip of the setting's row.
590
+ */
591
+ description?: string;
592
+ /**
593
+ * The value the platform holds until the person changes it.
594
+ */
595
+ default: boolean;
596
+ type: 'boolean';
597
+ } | {
598
+ /**
599
+ * The setting's label.
600
+ */
601
+ title: string;
602
+ /**
603
+ * The tooltip of the setting's row.
604
+ */
605
+ description?: string;
606
+ /**
607
+ * @minItems 1
608
+ */
609
+ enum?: string[];
610
+ minLength?: number;
611
+ maxLength?: number;
612
+ pattern?: string;
613
+ /**
614
+ * The value the platform holds until the person changes it.
615
+ */
616
+ default: string;
617
+ type: 'string';
618
+ } | {
619
+ /**
620
+ * The setting's label.
621
+ */
622
+ title: string;
623
+ /**
624
+ * The tooltip of the setting's row.
625
+ */
626
+ description?: string;
627
+ minimum?: number;
628
+ maximum?: number;
629
+ multipleOf?: number;
630
+ /**
631
+ * The value the platform holds until the person changes it.
632
+ */
633
+ default: number;
634
+ type: 'number';
635
+ } | {
636
+ /**
637
+ * The setting's label.
638
+ */
639
+ title: string;
640
+ /**
641
+ * The tooltip of the setting's row.
642
+ */
643
+ description?: string;
644
+ minimum?: number;
645
+ maximum?: number;
646
+ multipleOf?: number;
647
+ /**
648
+ * The value the platform holds until the person changes it.
649
+ */
650
+ default: number;
651
+ type: 'integer';
652
+ };
653
+ /**
654
+ * One row of a settings group: a setting by its name, or a button that
655
+ * calls one of the app's tools with `{}`. A tool whose result names a view
656
+ * opens that view in a modal over the settings; any other shows its text
657
+ * in a tooltip of the button.
658
+ */
659
+ type SettingsItem = {
660
+ property: string;
661
+ kind: 'property';
662
+ } | {
663
+ tool: string;
664
+ title: string;
665
+ kind: 'tool';
666
+ };
667
+ /**
668
+ * One MCP server the app brings. Its tools, `ui://` views and resources
669
+ * are the app's.
670
+ */
671
+ type McpServerDeclaration = RemoteMcpServer | BundledMcpServer;
672
+ /**
673
+ * The device capabilities a view may ask for, each a consent fact, as the
674
+ * MCP Apps sandbox permissions name them.
675
+ */
676
+ type ViewPermission = 'camera' | 'microphone' | 'geolocation' | 'clipboard-write';
677
+ /**
678
+ * The card a request runs on, or the reason in words that none can.
679
+ */
680
+ type EffectiveModel = {
681
+ card: string;
682
+ source: ModelSource;
683
+ } | {
684
+ card: null;
685
+ reason: string;
686
+ };
687
+ /**
688
+ * Where a chosen card comes from: the session's own model, the card the
689
+ * person gave the app, the role of its kind, or the daemon's rule.
690
+ */
691
+ type ModelSource = 'session' | 'app' | 'role' | 'automatic';
692
+ /**
693
+ * One frame of a streamed completion.
694
+ */
695
+ type CompletionDelta = {
696
+ text: string;
697
+ reasoning?: boolean;
698
+ } | {
699
+ done: true;
700
+ toolCalls?: ToolCall[];
701
+ usage?: Usage;
702
+ stop: CompletionStop;
703
+ error?: string;
704
+ };
705
+ type CompletionStop = 'end_turn' | 'tool_calls' | 'length' | 'cancelled' | 'error';
706
+ type ChatRole = 'system' | 'user' | 'assistant' | 'tool';
707
+ type ChatContent = string | ChatContentPart[];
708
+ /**
709
+ * A part of a message's content: text, or a picture by its asset, which
710
+ * the model registry turns into its data URL.
711
+ */
712
+ type ChatContentPart = {
713
+ text: string;
714
+ type: 'text';
715
+ } | {
716
+ asset: string;
717
+ mediaType: string;
718
+ type: 'image';
719
+ };
720
+ /**
721
+ * A frame on `GET /terminals/:id/stream`: the snapshot once, then the
722
+ * bytes as they come, then the exit.
723
+ */
724
+ type TerminalFrame = {
725
+ snapshot: TerminalSnapshot;
726
+ type: 'snapshot';
727
+ } | {
728
+ data: string;
729
+ type: 'output';
730
+ } | {
731
+ exitCode: number;
732
+ type: 'exited';
733
+ };
734
+ type TerminalStatus = 'running' | 'exited';
735
+ /**
736
+ * What a view's `openai/resources/write` of an opened file did: saved
737
+ * with the new version's ETag; nothing written because the version on
738
+ * disk is another, which `etag` names; or nothing written because the
739
+ * bytes exceed `max_bytes`.
740
+ */
741
+ type ResourceWriteResult = {
742
+ etag: string;
743
+ outcome: 'saved';
744
+ } | {
745
+ etag: string;
746
+ outcome: 'conflict';
747
+ } | {
748
+ maxBytes: number;
749
+ outcome: 'too-large';
750
+ };
751
+ /**
752
+ * What a patch compares: the working tree with the index, the index with
753
+ * `HEAD`, or the current branch with a base.
754
+ */
755
+ type VcsScope = 'working' | 'staged' | 'branch';
756
+ /**
757
+ * What one job takes.
758
+ */
759
+ type JobInput = ImageJobInput | MusicJobInput | SpeechJobInput | TranscriptionJobInput | EmbeddingJobInput;
760
+ /**
761
+ * How a sign-in reaches the person: `loopback` opens their browser and
762
+ * comes back to this computer; `device` shows a code to enter on any
763
+ * device.
764
+ */
765
+ type OAuthGrant = 'loopback' | 'device';
766
+ interface ForkPoint {
767
+ session: string;
768
+ leaf: string;
769
+ }
770
+ interface SpawnPoint {
771
+ session: string;
772
+ callId: string;
773
+ }
774
+ /**
775
+ * A tool's structured value: the object its declared output schema
776
+ * describes.
777
+ */
778
+ interface Structured {
779
+ [k: string]: unknown;
780
+ }
781
+ /**
782
+ * One folder a session's tools reach, by the handle the model names.
783
+ */
784
+ interface SessionRoot {
785
+ handle: string;
786
+ path: string;
787
+ mode: MountMode;
788
+ kind: RootKind;
789
+ }
790
+ /**
791
+ * One item of the composer's `@` list, as an MCP resource link: the
792
+ * address the app resolves it by, its name, and the words the list shows
793
+ * in place of the name.
794
+ */
795
+ interface MentionItem {
796
+ uri: string;
797
+ name: string;
798
+ title?: string;
799
+ mimeType?: string;
800
+ }
801
+ interface ToolCall {
802
+ id: string;
803
+ name: string;
804
+ /**
805
+ * JSON-encoded arguments exactly as the model emitted them.
806
+ */
807
+ arguments: string;
808
+ }
809
+ /**
810
+ * Token counts as the runtime reported them, or as the shared estimator
811
+ * counted them with `estimated: true`.
812
+ */
813
+ interface Usage {
814
+ input: number;
815
+ output: number;
816
+ estimated?: true;
817
+ }
818
+ /**
819
+ * What every tool call, session write and job carries: who acts, where
820
+ * the words came from, and the chain of principals the call crossed,
821
+ * outermost first.
822
+ */
823
+ interface CallContext {
824
+ principal: PrincipalRef;
825
+ origin: Origin;
826
+ chain: PrincipalRef[];
827
+ session?: string;
828
+ turn?: string;
829
+ call?: string;
830
+ authoritySession?: string;
831
+ }
832
+ /**
833
+ * How the transcript shows a call of a tool an app or a connector
834
+ * declares, as the tool was when the call was prepared.
835
+ */
836
+ interface CallLabel {
837
+ title: string;
838
+ icon?: ChatIconRole;
839
+ /**
840
+ * The app or the connector that declares the tool, and the name the
841
+ * person knows it by.
842
+ */
843
+ source: CallSource;
844
+ }
845
+ interface CallSource {
846
+ kind: SourceKind;
847
+ id: string;
848
+ name: string;
849
+ }
850
+ interface Executor {
851
+ principal: PrincipalRef;
852
+ instance: string;
853
+ }
854
+ /**
855
+ * The provider's view that renders a result: the provider and the view's
856
+ * id.
857
+ */
858
+ interface ViewRef {
859
+ app: string;
860
+ id: string;
861
+ }
862
+ interface ReviewRecord {
863
+ risk: Risk;
864
+ authorisation: Authorisation;
865
+ reason: string;
866
+ model: string;
867
+ /**
868
+ * The `user` and `steer` entries the reviewer read as authorisation.
869
+ */
870
+ authorisedBy: string[];
871
+ }
872
+ /**
873
+ * One durable allowance. `fs.write` implies `fs.read`. A grant with
874
+ * `session` belongs to that session and its helpers, and ends with it.
875
+ * `pin` is the hash of a tool's name, description, schemas and flags.
876
+ */
877
+ interface Grant {
878
+ principal: PrincipalRef;
879
+ right: Right;
880
+ subject: string;
881
+ label: string;
882
+ createdAt: string;
883
+ pin?: string;
884
+ session?: string;
885
+ }
886
+ interface Ask {
887
+ id: string;
888
+ kind: AskKind;
889
+ principal: PrincipalRef;
890
+ /**
891
+ * The principals the call crossed, outermost first.
892
+ */
893
+ chain: PrincipalRef[];
894
+ session?: string;
895
+ /**
896
+ * The root of the session's tree of helpers: where a helper's card is
897
+ * drawn, and the scope a session answer covers.
898
+ */
899
+ authoritySession?: string;
900
+ turn?: string;
901
+ callId?: string;
902
+ facts: AskFacts;
903
+ options: AskOption[];
904
+ openedAt: string;
905
+ /**
906
+ * Absolute; absent on a parked ask, which waits for a window.
907
+ */
908
+ deadline?: string;
909
+ }
910
+ /**
911
+ * The facts a card draws, as the core established them.
912
+ */
913
+ interface AskFacts {
914
+ /**
915
+ * The tool's name; `source` names where it comes from when it is not
916
+ * a core tool, and `sourceName` is that app's or connector's name.
917
+ */
918
+ tool?: string;
919
+ source?: string;
920
+ sourceName?: string;
921
+ /**
922
+ * The names of the apps a call made inside other calls went through,
923
+ * after the conversation's app and before the tool's source.
924
+ */
925
+ via?: string[];
926
+ /**
927
+ * The words the card names the act by: a tool's manifest title, or an
928
+ * app question's title from its manifest.
929
+ */
930
+ title?: string;
931
+ /**
932
+ * The agent's question, on a `question` card; its options are the
933
+ * choices it offers.
934
+ */
935
+ question?: string;
936
+ /**
937
+ * The label of the control the person pressed in a view, as the view
938
+ * reported it.
939
+ */
940
+ label?: string;
941
+ arguments?: string;
942
+ /**
943
+ * The absolute path a file call would touch.
944
+ */
945
+ path?: string;
946
+ command?: string;
947
+ /**
948
+ * No fence holds the command.
949
+ */
950
+ unfenced?: true;
951
+ /**
952
+ * A fenced run tripped over these; `stderr` holds the lines that said
953
+ * so.
954
+ */
955
+ paths?: string[];
956
+ hosts?: string[];
957
+ stderr?: string;
958
+ /**
959
+ * The command still runs, and the fence holds its connection to the
960
+ * hosts until the answer.
961
+ */
962
+ running?: true;
963
+ /**
964
+ * What a backend wants a folder for, on a `root` card it raised.
965
+ */
966
+ purpose?: string;
967
+ /**
968
+ * On a `root` card: whether it is about a folder or one file.
969
+ */
970
+ shape?: PathShape;
971
+ /**
972
+ * An app's own question, or an elicitation's message: the app's words.
973
+ */
974
+ text?: string;
975
+ /**
976
+ * On an elicitation card: the form, as the JSON Schema object the
977
+ * server sent.
978
+ */
979
+ schema?: {
980
+ [k: string]: unknown;
981
+ };
982
+ /**
983
+ * A connector card: the connector, and the tools waiting on a bulk
984
+ * card, or the sign-in's issuer, or the device code the person types
985
+ * at the verification address. An app's sign-in card carries the
986
+ * issuer, the scopes, and the code or the sign-in a window starts
987
+ * with the authorization endpoint its browser opens.
988
+ */
989
+ connector?: string;
990
+ tools?: ToolSummary[];
991
+ issuer?: string;
992
+ userCode?: string;
993
+ verificationUri?: string;
994
+ scopes?: string[];
995
+ login?: string;
996
+ authorizationEndpoint?: string;
997
+ }
998
+ interface ToolSummary {
999
+ name: string;
1000
+ description: string;
1001
+ }
1002
+ /**
1003
+ * One answer a card offers, with the grant the core computed for it.
1004
+ */
1005
+ interface AskOption {
1006
+ id: string;
1007
+ label: string;
1008
+ /**
1009
+ * The option is a pick: the person chooses a folder or a file, and the
1010
+ * answer carries its path.
1011
+ */
1012
+ pick?: PathShape;
1013
+ grant?: OptionGrant;
1014
+ }
1015
+ /**
1016
+ * The grant the core computed for one answer of a card.
1017
+ */
1018
+ interface OptionGrant {
1019
+ principal: PrincipalRef;
1020
+ right: Right;
1021
+ subject: string;
1022
+ scope: GrantScope;
1023
+ pin?: string;
1024
+ }
1025
+ /**
1026
+ * What the chips of a view hold from its last `ui/update-model-context`:
1027
+ * the id the host gave that update, and its blocks.
1028
+ */
1029
+ interface ModelContextState {
1030
+ updateId: string;
1031
+ /**
1032
+ * The MCP content blocks of the update.
1033
+ */
1034
+ content?: {
1035
+ [k: string]: unknown;
1036
+ }[];
1037
+ structuredContent?: {
1038
+ [k: string]: unknown;
1039
+ };
1040
+ }
1041
+ /**
1042
+ * Every setting's value, by its name.
1043
+ */
1044
+ interface SettingValues {
1045
+ [k: string]: SettingValue;
1046
+ }
1047
+ /**
1048
+ * One room an app of this platform is in. A member is a platform in the
1049
+ * room, named by its peer id, which is new for each room and stays the
1050
+ * member's while it keeps its place: through a dropped connection and a
1051
+ * restart of its platform, until it leaves or the room ends.
1052
+ */
1053
+ interface Room {
1054
+ /**
1055
+ * The same on every member's platform.
1056
+ */
1057
+ id: string;
1058
+ app: string;
1059
+ /**
1060
+ * What a person passes on to another computer to join the room: a
1061
+ * `hearthscale://join?app=<id>&ticket=<…>` link, which opens the
1062
+ * client's join sheet with the ticket filled in, and which the sheet
1063
+ * also takes pasted.
1064
+ */
1065
+ ticket: string;
1066
+ /**
1067
+ * This platform's peer id in the room.
1068
+ */
1069
+ me: string;
1070
+ /**
1071
+ * The member that opened the room: the room ends when it leaves.
1072
+ */
1073
+ opener: string;
1074
+ /**
1075
+ * The other members this platform is with now, in the order they
1076
+ * came. A member that is with no one is out of reach of the opener
1077
+ * and dials it again.
1078
+ */
1079
+ peers: string[];
1080
+ }
1081
+ /**
1082
+ * One job as `POST /models/jobs` answers it and `job.progress` frames it.
1083
+ * The result is one asset per output: a picture, a WAV, a text or a JSON
1084
+ * document, owned by the session the job ran for or else by the app.
1085
+ */
1086
+ interface Job {
1087
+ id: string;
1088
+ kind: JobKind;
1089
+ app: string;
1090
+ session?: string;
1091
+ /**
1092
+ * The tool call that submitted it, for a job a tool started.
1093
+ */
1094
+ callId?: string;
1095
+ card: string;
1096
+ status: JobStatus;
1097
+ submittedAt: string;
1098
+ /**
1099
+ * Why the job has not started on the machine that runs it, such as
1100
+ * busy models holding the memory it needs.
1101
+ */
1102
+ waiting?: string;
1103
+ progress?: JobProgress;
1104
+ result?: string[];
1105
+ error?: string;
1106
+ finishedAt?: string;
1107
+ }
1108
+ interface JobProgress {
1109
+ fraction?: number;
1110
+ }
1111
+ /**
1112
+ * A program of a session that runs now.
1113
+ */
1114
+ interface SessionCell {
1115
+ cell: number;
1116
+ callId: string;
1117
+ }
1118
+ /**
1119
+ * A command of a session that runs in the background now.
1120
+ */
1121
+ interface SessionProcess {
1122
+ process: number;
1123
+ callId: string;
1124
+ }
1125
+ interface Manifest {
1126
+ /**
1127
+ * The address of this schema, for an editor's completion; nothing in
1128
+ * Hearthscale reads it.
1129
+ */
1130
+ $schema?: string;
1131
+ id: string;
1132
+ name: string;
1133
+ /**
1134
+ * Semver, shown and never interpreted.
1135
+ */
1136
+ version: string;
1137
+ api: 6;
1138
+ description: string;
1139
+ icon?: Schemed;
1140
+ license?: string;
1141
+ /**
1142
+ * One to three categories, each once.
1143
+ *
1144
+ * @minItems 1
1145
+ * @maxItems 3
1146
+ */
1147
+ categories?: Category[];
1148
+ payment?: PaymentKind;
1149
+ author?: string;
1150
+ environment?: Environment;
1151
+ agent?: AgentDefinition;
1152
+ agents?: {
1153
+ [k: string]: AgentDefinition;
1154
+ };
1155
+ models?: {
1156
+ chat?: ModelNeed;
1157
+ embedding?: ModelNeed;
1158
+ image?: ModelNeed;
1159
+ music?: ModelNeed;
1160
+ speech?: ModelNeed;
1161
+ transcription?: ModelNeed;
1162
+ };
1163
+ tools?: ToolDeclaration[];
1164
+ capabilities?: Capabilities;
1165
+ /**
1166
+ * The questions the app's backend raises from inside a tool call, by
1167
+ * kind, each with the title its card shows: a sign-in, a handoff.
1168
+ */
1169
+ asks?: AskDeclaration[];
1170
+ /**
1171
+ * The file formats the app's backend turns into text for the read
1172
+ * tool, each an id and the extensions it claims.
1173
+ */
1174
+ formats?: FormatDeclaration[];
1175
+ uses?: Uses[];
1176
+ recommends?: string[];
1177
+ onboarding?: Onboarding;
1178
+ /**
1179
+ * What the app shows in tiles, in the rail and in transcripts.
1180
+ *
1181
+ * @minItems 1
1182
+ */
1183
+ surfaces?: SurfaceDeclaration[];
1184
+ settings?: SettingsDeclaration;
1185
+ /**
1186
+ * The tool that answers the composer's `@` list with the app's items:
1187
+ * it takes `MentionSearch` and answers `MentionResult` as its
1188
+ * structured result. A consent fact: the person's `@` runs it at
1189
+ * every level.
1190
+ */
1191
+ mentions?: Mentions;
1192
+ /**
1193
+ * @minItems 1
1194
+ */
1195
+ mcp?: McpServerDeclaration[];
1196
+ /**
1197
+ * @minItems 1
1198
+ */
1199
+ permissions?: ViewPermission[];
1200
+ /**
1201
+ * Sample prompts the app's Marketplace listing shows.
1202
+ *
1203
+ * @minItems 1
1204
+ */
1205
+ examples?: string[];
1206
+ }
1207
+ interface SchemePair {
1208
+ light: ServedPicture;
1209
+ dark: ServedPicture;
1210
+ }
1211
+ interface Environment {
1212
+ kind: EnvironmentKind;
1213
+ /**
1214
+ * The hosts the app reaches: its backend through the relay, its views
1215
+ * and its web surfaces. Each is a consent fact.
1216
+ */
1217
+ network?: NetworkEntry[];
1218
+ lifetime?: Lifetime;
1219
+ }
1220
+ interface AgentDefinition {
1221
+ name: string;
1222
+ description?: string;
1223
+ model: ModelRequirement;
1224
+ prompt?: PackagePath[];
1225
+ compaction?: Compaction;
1226
+ skills?: PackagePath[];
1227
+ tools?: string[];
1228
+ roots?: AgentRoot[];
1229
+ budget?: Budget;
1230
+ projects?: boolean;
1231
+ /**
1232
+ * The result a session of this agent answers through `finish`, when a
1233
+ * helper's parent or an app started it: an object schema.
1234
+ */
1235
+ result?: {
1236
+ [k: string]: unknown;
1237
+ };
1238
+ }
1239
+ interface ModelRequirement {
1240
+ kind: ModelKind;
1241
+ tools?: boolean;
1242
+ vision?: boolean;
1243
+ /**
1244
+ * Models the author knows the agent runs well on, as a catalogue
1245
+ * spells them; a preference in the pick, never a requirement.
1246
+ */
1247
+ recommend?: string[];
1248
+ sampling?: Sampling;
1249
+ reasoning?: Reasoning;
1250
+ }
1251
+ interface Sampling {
1252
+ temperature?: number;
1253
+ topP?: number;
1254
+ maxTokens?: number;
1255
+ }
1256
+ interface Compaction {
1257
+ focus: string;
1258
+ }
1259
+ interface Budget {
1260
+ tokens?: number;
1261
+ rounds?: number;
1262
+ }
1263
+ /**
1264
+ * The kinds of model a package needs, each marked as required or not, in
1265
+ * the order the manifest names them.
1266
+ */
1267
+ interface ModelNeed {
1268
+ required: boolean;
1269
+ }
1270
+ interface ToolDeclaration {
1271
+ name: string;
1272
+ title: string;
1273
+ description: string;
1274
+ input: {
1275
+ [k: string]: unknown;
1276
+ };
1277
+ output?: {
1278
+ [k: string]: unknown;
1279
+ };
1280
+ mutating: boolean;
1281
+ outbound: boolean;
1282
+ timeoutMs?: number;
1283
+ /**
1284
+ * The view that renders the tool's result: a view surface placed
1285
+ * `inline`.
1286
+ */
1287
+ view?: string;
1288
+ icon?: ChatIconRole;
1289
+ /**
1290
+ * Who may call the tool. Absent, both.
1291
+ *
1292
+ * @minItems 1
1293
+ */
1294
+ visibility?: ToolVisibility[];
1295
+ }
1296
+ interface Capabilities {
1297
+ tools?: string[];
1298
+ skills?: PackagePath[];
1299
+ }
1300
+ interface AskDeclaration {
1301
+ kind: string;
1302
+ title: string;
1303
+ }
1304
+ interface FormatDeclaration {
1305
+ id: string;
1306
+ /**
1307
+ * @minItems 1
1308
+ */
1309
+ extensions: string[];
1310
+ }
1311
+ interface Onboarding {
1312
+ version: number;
1313
+ /**
1314
+ * @minItems 1
1315
+ */
1316
+ cards: OnboardingCard[];
1317
+ }
1318
+ interface OnboardingCard {
1319
+ title: string;
1320
+ text?: string;
1321
+ image?: ServedPicture;
1322
+ rows?: OnboardingRow[];
1323
+ /**
1324
+ * A model this computer's engine can pull, by the id its device row
1325
+ * gives it.
1326
+ */
1327
+ download?: string;
1328
+ }
1329
+ interface OnboardingRow {
1330
+ icon: ChatIconRole;
1331
+ title: string;
1332
+ text: string;
1333
+ }
1334
+ /**
1335
+ * A right-click entry of the app's rail button: a call of one of the
1336
+ * app's tools with these arguments, `{}` when absent. A result that names
1337
+ * a view opens that view as a tab of the focused tile.
1338
+ */
1339
+ interface QuickAction {
1340
+ title: string;
1341
+ tool: string;
1342
+ args?: {
1343
+ [k: string]: unknown;
1344
+ };
1345
+ }
1346
+ /**
1347
+ * The app's settings: a JSON Schema object of booleans, strings and
1348
+ * numbers, which the platform stores and hands to the app's backend and
1349
+ * views, and the groups the settings page draws them in. With `layout`,
1350
+ * every setting sits in exactly one group; without it, the settings show
1351
+ * in the order the schema declares them.
1352
+ */
1353
+ interface SettingsDeclaration {
1354
+ schema: SettingsSchema;
1355
+ /**
1356
+ * @minItems 1
1357
+ */
1358
+ layout?: SettingsGroup[];
1359
+ }
1360
+ interface SettingsSchema {
1361
+ type: ObjectType;
1362
+ properties: {
1363
+ [k: string]: SettingDeclaration;
1364
+ };
1365
+ }
1366
+ interface SettingsGroup {
1367
+ title: string;
1368
+ /**
1369
+ * @minItems 1
1370
+ */
1371
+ items: SettingsItem[];
1372
+ }
1373
+ interface Mentions {
1374
+ tool: string;
1375
+ }
1376
+ /**
1377
+ * A remote MCP server, by its address.
1378
+ */
1379
+ interface RemoteMcpServer {
1380
+ url: string;
1381
+ }
1382
+ /**
1383
+ * A JavaScript file in the package the platform runs in the app's
1384
+ * environment, with the environment variables it reads, each a secret the
1385
+ * person enters.
1386
+ */
1387
+ interface BundledMcpServer {
1388
+ entry: string;
1389
+ env?: string[];
1390
+ }
1391
+ /**
1392
+ * What `net.fetch` takes.
1393
+ */
1394
+ interface FetchParams {
1395
+ url: string;
1396
+ method?: string;
1397
+ headers?: {
1398
+ [k: string]: string;
1399
+ };
1400
+ body?: string;
1401
+ }
1402
+ /**
1403
+ * What `net.fetch` answers.
1404
+ */
1405
+ interface FetchAnswer {
1406
+ status: number;
1407
+ headers: {
1408
+ [k: string]: string;
1409
+ };
1410
+ body: string;
1411
+ }
1412
+ /**
1413
+ * The body of `POST /models/complete` and of one model call of a turn.
1414
+ */
1415
+ interface CompletionRequest {
1416
+ messages: ChatMessage[];
1417
+ tools?: ToolDefinition[];
1418
+ /**
1419
+ * JSON against this schema, sent to the runtime as its structured
1420
+ * output request; a runtime without the feature refuses with its own
1421
+ * reason.
1422
+ */
1423
+ format?: CompletionFormat;
1424
+ sampling?: Sampling;
1425
+ reasoning?: Reasoning;
1426
+ /**
1427
+ * Asks for the same answer to the same request as far as the runtime
1428
+ * allows: a fixed seed, and on a Hearthscale Server the whole prompt
1429
+ * read again rather than partly from an earlier request's cache.
1430
+ */
1431
+ repeatable?: true;
1432
+ }
1433
+ /**
1434
+ * One message of a request, in the OpenAI v1 roles.
1435
+ */
1436
+ interface ChatMessage {
1437
+ role: ChatRole;
1438
+ content: ChatContent;
1439
+ toolCalls?: ToolCall[];
1440
+ toolCallId?: string;
1441
+ }
1442
+ /**
1443
+ * One tool as a model sees it.
1444
+ */
1445
+ interface ToolDefinition {
1446
+ name: string;
1447
+ description: string;
1448
+ /**
1449
+ * JSON Schema for the arguments object.
1450
+ */
1451
+ parameters: {
1452
+ [k: string]: unknown;
1453
+ };
1454
+ }
1455
+ /**
1456
+ * The structured output a request asks for.
1457
+ */
1458
+ interface CompletionFormat {
1459
+ name?: string;
1460
+ schema: {
1461
+ [k: string]: unknown;
1462
+ };
1463
+ }
1464
+ /**
1465
+ * What a conversation's requests have spent, by the log.
1466
+ */
1467
+ interface SpentTokens {
1468
+ tokens: number;
1469
+ rounds: number;
1470
+ }
1471
+ /**
1472
+ * An icon as a data URL per scheme.
1473
+ */
1474
+ interface SchemedIcon {
1475
+ light: string;
1476
+ dark: string;
1477
+ }
1478
+ /**
1479
+ * One folder or file an app's backend reaches, by the handle its channel
1480
+ * names.
1481
+ */
1482
+ interface Mount {
1483
+ handle: string;
1484
+ path: string;
1485
+ mode: MountMode;
1486
+ }
1487
+ /**
1488
+ * A project: one folder under its app's workspace and the instructions
1489
+ * its sessions read.
1490
+ */
1491
+ interface ProjectRecord {
1492
+ id: string;
1493
+ app: string;
1494
+ name: string;
1495
+ instructions: string;
1496
+ rank: number;
1497
+ createdAt: string;
1498
+ folder: string;
1499
+ }
1500
+ /**
1501
+ * A file opened in an app, by an address only the host resolves.
1502
+ */
1503
+ interface OpenedFile {
1504
+ name: string;
1505
+ resourceUri: string;
1506
+ }
1507
+ /**
1508
+ * One folder as `GET /fs/list` answers it.
1509
+ */
1510
+ interface FsListing {
1511
+ path: string;
1512
+ /**
1513
+ * The drive or volume the path sits on, where walking up ends.
1514
+ */
1515
+ root: string;
1516
+ dirs: string[];
1517
+ files: string[];
1518
+ }
1519
+ /**
1520
+ * The head of one file as `GET /fs/read` answers it.
1521
+ */
1522
+ interface FsRead {
1523
+ path: string;
1524
+ size: number;
1525
+ binary: boolean;
1526
+ text?: string;
1527
+ }
1528
+ /**
1529
+ * What one repository answers about itself.
1530
+ */
1531
+ interface VcsRepo {
1532
+ root: string;
1533
+ /**
1534
+ * The checked-out branch, empty where HEAD is detached.
1535
+ */
1536
+ branch: string;
1537
+ }
1538
+ interface VcsRefs {
1539
+ current: string;
1540
+ branches: string[];
1541
+ }
1542
+ /**
1543
+ * One scope's patch.
1544
+ */
1545
+ interface VcsDiff {
1546
+ patch: string;
1547
+ /**
1548
+ * The cap cut the patch; what came back is whole files up to the cut.
1549
+ */
1550
+ truncated: boolean;
1551
+ /**
1552
+ * How many changed files the patch leaves out because they lie in the
1553
+ * deny-read set.
1554
+ */
1555
+ hidden: number;
1556
+ }
1557
+ /**
1558
+ * One file whole, as a scope's new side holds it.
1559
+ */
1560
+ interface VcsFile {
1561
+ text: string;
1562
+ }
1563
+ /**
1564
+ * One terminal as `POST /terminals` answers it and as its stream's first
1565
+ * frame carries it.
1566
+ */
1567
+ interface TerminalSnapshot {
1568
+ id: string;
1569
+ /**
1570
+ * The app whose window the terminal was opened in.
1571
+ */
1572
+ app: string;
1573
+ /**
1574
+ * The folder the shell started in.
1575
+ */
1576
+ cwd: string;
1577
+ /**
1578
+ * The shell's own name: PowerShell, zsh, bash.
1579
+ */
1580
+ shell: string;
1581
+ status: TerminalStatus;
1582
+ /**
1583
+ * The scrollback, capped at the line ring.
1584
+ */
1585
+ history: string;
1586
+ exitCode: number | null;
1587
+ }
1588
+ /**
1589
+ * One session as `GET /sessions` lists it: its record, its agent's display
1590
+ * name, its own name else the app's, and the card its next turn runs on
1591
+ * by the daemon's rule.
1592
+ */
1593
+ interface Session {
1594
+ id: string;
1595
+ app: string;
1596
+ appVersion: string;
1597
+ agent: string;
1598
+ project?: string;
1599
+ startedBy?: PrincipalRef;
1600
+ title: string;
1601
+ preview: string;
1602
+ /**
1603
+ * The card the next turn uses, when a person chose one.
1604
+ */
1605
+ model?: string;
1606
+ createdAt: string;
1607
+ updatedAt: string;
1608
+ status: SessionStatus;
1609
+ stopReason?: StopReason;
1610
+ leaf: string | null;
1611
+ entries: number;
1612
+ bytes: number;
1613
+ pinned: boolean;
1614
+ archived: boolean;
1615
+ rank: number;
1616
+ forkedFrom?: string;
1617
+ spawnedBy?: string;
1618
+ /**
1619
+ * On a helper: the parent's `spawn_agent` call that started it.
1620
+ */
1621
+ spawnedByCall?: string;
1622
+ authoritySession: string;
1623
+ spent: SpentTokens;
1624
+ roots: SessionRoot[];
1625
+ lastTurn?: LastTurn;
1626
+ /**
1627
+ * The programs running now, each with the `exec` call that started
1628
+ * it.
1629
+ */
1630
+ cells: SessionCell[];
1631
+ /**
1632
+ * The commands running in the background now, each with the `bash`
1633
+ * call that started it.
1634
+ */
1635
+ processes: SessionProcess[];
1636
+ agentName: string;
1637
+ effective: EffectiveModel;
1638
+ }
1639
+ /**
1640
+ * The turn a session ended last, and why.
1641
+ */
1642
+ interface LastTurn {
1643
+ id: string;
1644
+ stopReason: StopReason;
1645
+ }
1646
+ /**
1647
+ * `GET /vcs/diff`: one scope's patch of a repository.
1648
+ */
1649
+ interface VcsDiffQuery {
1650
+ root: string;
1651
+ scope: VcsScope;
1652
+ /**
1653
+ * The ref a `branch` scope compares the current branch against.
1654
+ */
1655
+ base?: string;
1656
+ /**
1657
+ * Root-relative pathspecs the patch is narrowed to.
1658
+ */
1659
+ paths?: string[];
1660
+ }
1661
+ /**
1662
+ * `GET /vcs/file`: one file whole, as a scope's new side holds it, which
1663
+ * the diff panel's unmodified runs are read from: the working tree's copy
1664
+ * for `working`, the index's for `staged` and `HEAD`'s for `branch`.
1665
+ */
1666
+ interface VcsFileQuery {
1667
+ root: string;
1668
+ scope: VcsScope;
1669
+ /**
1670
+ * Relative to the repository's top, as the patch names it.
1671
+ */
1672
+ path: string;
1673
+ }
1674
+ /**
1675
+ * What one job of each kind takes, by kind. Audio and images cross as
1676
+ * assets.
1677
+ */
1678
+ interface JobInputs {
1679
+ image: ImageJobInput;
1680
+ music: MusicJobInput;
1681
+ speech: SpeechJobInput;
1682
+ transcription: TranscriptionJobInput;
1683
+ embedding: EmbeddingJobInput;
1684
+ }
1685
+ interface ImageJobInput {
1686
+ prompt: string;
1687
+ size?: string;
1688
+ }
1689
+ interface MusicJobInput {
1690
+ prompt: string;
1691
+ seconds: number;
1692
+ lyrics?: string;
1693
+ }
1694
+ interface SpeechJobInput {
1695
+ text: string;
1696
+ voice?: string;
1697
+ }
1698
+ /**
1699
+ * `audio` is an asset.
1700
+ */
1701
+ interface TranscriptionJobInput {
1702
+ audio: string;
1703
+ language?: string;
1704
+ }
1705
+ interface EmbeddingJobInput {
1706
+ texts: string[];
1707
+ }
1708
+ /**
1709
+ * The executor's answer to a command: what the command answers the body,
1710
+ * or the error it met.
1711
+ */
1712
+ interface SurfaceResult {
1713
+ id: string;
1714
+ url?: string;
1715
+ title?: string;
1716
+ result?: unknown;
1717
+ asset?: string;
1718
+ shown?: boolean;
1719
+ error?: string;
1720
+ }
1721
+ /**
1722
+ * A path inside a mount, as the backend names files: the mount's handle
1723
+ * and the path under it.
1724
+ */
1725
+ interface MountPath {
1726
+ root: string;
1727
+ path: string;
1728
+ }
1729
+ /**
1730
+ * What a tool body answers: content for the model and the view that
1731
+ * renders the result.
1732
+ */
1733
+ interface ToolAnswer {
1734
+ content: Content[];
1735
+ structured?: Structured;
1736
+ /**
1737
+ * Values for the view that renders the result, as MCP's result
1738
+ * `_meta`; they never reach a model.
1739
+ */
1740
+ meta?: {
1741
+ [k: string]: unknown;
1742
+ };
1743
+ view?: string;
1744
+ /**
1745
+ * The model tokens the body spent through `models.complete`, summed
1746
+ * by the core.
1747
+ */
1748
+ usage?: Usage;
1749
+ }
1750
+ /**
1751
+ * What a surface command answers a body: the page's address and title
1752
+ * after the command, a script's value, a screenshot's asset, and whether
1753
+ * the tab is in the conversation's panel, where the person sees it, which
1754
+ * every answer but `close`'s carries.
1755
+ */
1756
+ interface SurfaceAnswer {
1757
+ url?: string;
1758
+ title?: string;
1759
+ result?: unknown;
1760
+ asset?: string;
1761
+ shown?: boolean;
1762
+ }
1763
+ /**
1764
+ * One session as `sessions.list` lists it.
1765
+ */
1766
+ interface SessionListing {
1767
+ id: string;
1768
+ app: string;
1769
+ title: string;
1770
+ createdAt: string;
1771
+ updatedAt: string;
1772
+ }
1773
+ /**
1774
+ * One session with its entries, as `sessions.open` answers it.
1775
+ */
1776
+ interface OpenedSession {
1777
+ id: string;
1778
+ title: string;
1779
+ entries: Entry[];
1780
+ }
1781
+ /**
1782
+ * A conversation `sessions.create` starts: in this app, or in another app
1783
+ * under the `sessions.create` grant, for a named agent and in a project.
1784
+ */
1785
+ interface SessionCreate {
1786
+ app?: string;
1787
+ agent?: string;
1788
+ project?: string;
1789
+ title?: string;
1790
+ message?: string;
1791
+ /**
1792
+ * A chat card `models.for` lists for the app.
1793
+ */
1794
+ model?: string;
1795
+ }
1796
+ /**
1797
+ * A session's last turn: how it ended, its last reply, and the result its
1798
+ * agent answered through `finish`.
1799
+ */
1800
+ interface TurnOutcome {
1801
+ turn: string;
1802
+ stopReason: StopReason;
1803
+ text: string;
1804
+ result?: Structured;
1805
+ }
1806
+ /**
1807
+ * One enabled app as `apps.list` lists it.
1808
+ */
1809
+ interface AppListing {
1810
+ id: string;
1811
+ name: string;
1812
+ description: string;
1813
+ /**
1814
+ * The icon's file in the app's package per colour scheme, which a page
1815
+ * loads from `/apps/<id>/ui/<file>`; null when it has none.
1816
+ */
1817
+ icon: SchemedIcon | null;
1818
+ agent: boolean;
1819
+ capabilities: string[];
1820
+ }
1821
+ /**
1822
+ * One chat card an app's sessions may run on, as `models.for` lists it.
1823
+ */
1824
+ interface ModelListing {
1825
+ id: string;
1826
+ name: string;
1827
+ provider: string;
1828
+ }
1829
+ /**
1830
+ * One project as `projects.list` lists it.
1831
+ */
1832
+ interface ProjectListing {
1833
+ id: string;
1834
+ name: string;
1835
+ folder: string;
1836
+ }
1837
+ /**
1838
+ * An app's own question to the person during a tool call: a choice, a
1839
+ * sign-in, a handoff; `kind` is one the manifest's `asks` declares. It
1840
+ * widens nothing.
1841
+ */
1842
+ interface AppQuestion {
1843
+ kind: string;
1844
+ text: string;
1845
+ }
1846
+ /**
1847
+ * The person's answer to an app's question.
1848
+ */
1849
+ interface AppAnswer {
1850
+ allow: boolean;
1851
+ option?: string;
1852
+ }
1853
+ /**
1854
+ * A durable entry in one of the app's own sessions, or a transient event
1855
+ * for the app's views.
1856
+ */
1857
+ interface AppEvent {
1858
+ name: string;
1859
+ session?: string;
1860
+ data: unknown;
1861
+ durable: boolean;
1862
+ context?: string;
1863
+ }
1864
+ /**
1865
+ * An OS notification an app asks for.
1866
+ */
1867
+ interface AppNotice {
1868
+ title: string;
1869
+ body: string;
1870
+ session?: string;
1871
+ }
1872
+ /**
1873
+ * A sign-in to an online service of the app's own, with the app's own
1874
+ * client registered at that service.
1875
+ */
1876
+ interface OAuthLogin {
1877
+ /**
1878
+ * The authorization server's identifier, an https address without a
1879
+ * query or a fragment; its metadata names the endpoints unless
1880
+ * `endpoints` does.
1881
+ */
1882
+ issuer: string;
1883
+ /**
1884
+ * Absent when the server registers clients itself (RFC 7591).
1885
+ */
1886
+ clientId?: string;
1887
+ /**
1888
+ * For a server that demands one even from a desktop client.
1889
+ */
1890
+ clientSecret?: string;
1891
+ scopes?: string[];
1892
+ /**
1893
+ * The RFC 8707 resource the token is for.
1894
+ */
1895
+ resource?: string;
1896
+ endpoints?: OAuthEndpoints;
1897
+ grant?: OAuthGrant;
1898
+ /**
1899
+ * The server's own fields on the authorization address, such as
1900
+ * `access_type`.
1901
+ */
1902
+ params?: {
1903
+ [k: string]: string;
1904
+ };
1905
+ }
1906
+ /**
1907
+ * The endpoints of an authorization server that publishes no metadata.
1908
+ */
1909
+ interface OAuthEndpoints {
1910
+ authorization?: string;
1911
+ token: string;
1912
+ device?: string;
1913
+ }
1914
+
1915
+ /**
1916
+ * The view bridge: what a view, an app's UI in a sandboxed frame, and its
1917
+ * host say to each other beyond the MCP Apps standard the view speaks.
1918
+ * Every method is a JSON-RPC request over the MCP Apps transport, and
1919
+ * every result is an object, as that protocol requires.
1920
+ *
1921
+ * Where OpenAI's MCP extensions name a key or a method, the bridge uses
1922
+ * that name, so a view written for ChatGPT runs unchanged; `hearthscale/*`
1923
+ * names only what those extensions have no name for.
1924
+ *
1925
+ * The host fills the standard capabilities of `ui/initialize` as follows:
1926
+ * `updateModelContext` and `message` take text, image, resource link and
1927
+ * embedded resource blocks, and `updateModelContext` also structured
1928
+ * content; `sampling` is offered when the app names `models` in `uses`,
1929
+ * and answers on the card the app's chat runs on; `downloadFile` saves
1930
+ * through the system's save dialog; `sandbox.permissions` holds the
1931
+ * manifest's `permissions` (`clipboard-write` as `clipboardWrite`), and
1932
+ * `sandbox.csp` the hosts of `environment.network` for connections,
1933
+ * resources and frames alike. Under `experimental`, the keys
1934
+ * `openai/modelContext`, `openai/message` and `openai/resource` each hold
1935
+ * `{}`, `openai/files` holds `{}` when the app names `files.open` in
1936
+ * `uses`, and `hearthscale` holds `HearthscaleHostCapability`. In the host
1937
+ * context, `displayMode` and `availableDisplayModes` follow
1938
+ * `MCP_DISPLAY_MODES` and the surface's placements, and
1939
+ * `containerDimensions` is the size of the view's frame, `width` and
1940
+ * `height` in a tab or a modal and `width` and `maxHeight` under a
1941
+ * result, sent again on each change.
1942
+ *
1943
+ * Every block a view sends with `ui/update-model-context` shows as a
1944
+ * removable chip above the composer of each chat on screen in the same
1945
+ * window, and rides the next message sent there as a `context` part; no
1946
+ * block reaches a model without a chip, whatever its
1947
+ * `annotations.audience` says.
1948
+ */
1949
+
1950
+ /** The extensions a view calls as `hearthscale/<extension>/…`, each only
1951
+ * when its app names it in `uses`: the app's own conversations and
1952
+ * projects, its store, its backend's events, its folders, its models,
1953
+ * its jobs, its terminals, opening a surface, and its rooms. Opening a
1954
+ * file is OpenAI's `openai/files/open`, which `files.open` in `uses`
1955
+ * gates. */
1956
+ declare const VIEW_EXTENSIONS: readonly ["sessions", "store", "events", "roots", "models", "jobs", "terminals", "surfaces.open", "peers"];
1957
+ type ViewExtension = (typeof VIEW_EXTENSIONS)[number];
1958
+ /** `hostCapabilities.experimental.hearthscale`: the extensions this view
1959
+ * may call. The `hearthscale/ui/*` methods are offered to every view. */
1960
+ interface HearthscaleHostCapability {
1961
+ extensions: ViewExtension[];
1962
+ }
1963
+ /** The keys the host adds to the MCP Apps host context, set when the view
1964
+ * starts and changed by `ui/notifications/host-context-changed`. */
1965
+ interface ViewHostContext {
1966
+ /** The look the host wears, as one style sheet of `@layer` bodies. */
1967
+ 'hearthscale/appearance': string;
1968
+ /** The app's settings; absent for an app that declares none. */
1969
+ 'hearthscale/settings'?: SettingValues;
1970
+ /** What the view's chips hold from its last `ui/update-model-context`;
1971
+ * null once no chip of it is left. */
1972
+ 'openai/modelContext': ModelContextState | null;
1973
+ /** The state the view last saved with `hearthscale/ui/set-widget-state`,
1974
+ * given back when the host loads a discarded view again; null before
1975
+ * the first save. */
1976
+ 'hearthscale/widgetState': unknown;
1977
+ /** The app-relative address, with its query, that a deep link or
1978
+ * `hearthscale/surfaces/open` opened the surface on; absent for a
1979
+ * surface opened without one. The link is
1980
+ * `hearthscale://open?app=<id>&surface=<id>&path=<address>`, the
1981
+ * address percent-encoded and starting with `/`. */
1982
+ 'openai/deepLink'?: {
1983
+ url: string;
1984
+ };
1985
+ }
1986
+ /** The `_meta` of a text or image block of `ui/update-model-context` or
1987
+ * `ui/message`. `openai/title` is the label of the block's chip: in a
1988
+ * message, a titled block becomes a chip and an untitled text block
1989
+ * becomes the message's text. */
1990
+ interface ContentBlockMeta {
1991
+ 'openai/title'?: string;
1992
+ }
1993
+ /** `_meta['openai/modelContext']` on the result of
1994
+ * `ui/update-model-context`. Each update replaces the view's last. */
1995
+ interface ModelContextAnswer {
1996
+ updateId: string;
1997
+ }
1998
+ /** `_meta['openai/message']` on a `ui/message` request. */
1999
+ interface MessageOptions {
2000
+ /** `active`, also when absent: the conversation whose transcript shows
2001
+ * the view; for a view that fills a tab, the conversation of its app
2002
+ * that its first `chat` or `composer` slot shows, made there by the
2003
+ * message when the slot shows a new one. `new`: a new conversation of
2004
+ * that app, which the slot then shows. A view that fills a tab and
2005
+ * shows no such slot sends no message, and drafts its words in the
2006
+ * composer of the conversation on screen. */
2007
+ target?: 'active' | 'new';
2008
+ /** True, also when absent, sends the message; false leaves it in the
2009
+ * composer for the person to send. */
2010
+ send?: boolean;
2011
+ }
2012
+ /** The tool input a view gets when its surface opens a file. */
2013
+ interface FileInput {
2014
+ file: OpenedFile;
2015
+ }
2016
+ /** `_meta['openai/resource']` on a `resources/read` of an opened file:
2017
+ * whether its bytes come as text or as a base64 blob; absent, by the
2018
+ * file's kind. The host answers the read, the subscription and its
2019
+ * `notifications/resources/updated` itself. */
2020
+ interface ResourceReadOptions {
2021
+ representation: 'text' | 'blob';
2022
+ }
2023
+ /** `_meta['openai/resource']` on the content a read of an opened file
2024
+ * answers: the version read, and whether `openai/resources/write` may
2025
+ * replace it. */
2026
+ interface ResourceVersion {
2027
+ etag: string;
2028
+ writable: boolean;
2029
+ }
2030
+ /** `_meta['openai/resource']` the host adds to a tool call a view showing
2031
+ * an opened file makes to its app's MCP server: the file's absolute path
2032
+ * on the platform's machine, which the view never sees. */
2033
+ interface ResourcePath {
2034
+ path: string;
2035
+ }
2036
+ /** Replaces an opened file's bytes, when the version is still `ifMatch`
2037
+ * or `ifMatch` is absent. */
2038
+ type ResourceWrite = {
2039
+ uri: string;
2040
+ ifMatch?: string;
2041
+ } & ({
2042
+ text: string;
2043
+ }
2044
+ /** Base64. */
2045
+ | {
2046
+ blob: string;
2047
+ });
2048
+ /** What the shell draws in a rectangle a view reserves: `chat`, the
2049
+ * transcript and the composer; `composer`, the composer alone; `cards`,
2050
+ * the requests of the view's app alone, for a view that has no composer;
2051
+ * `terminal`, the person's own live shell, which the window opens with
2052
+ * its own authority and the view never reads or types into; `web`, the
2053
+ * tab's web page on the app's own partition, which reaches only the hosts
2054
+ * the app declares, and which the view drives with `hearthscale/web/*`.
2055
+ * A view reserves at most one `web` slot. */
2056
+ declare const HOST_SLOT_KINDS: readonly ["chat", "composer", "cards", "terminal", "web"];
2057
+ type HostSlotKind = (typeof HOST_SLOT_KINDS)[number];
2058
+ /** The distances, in the view's CSS pixels, from each edge of the view to
2059
+ * the same edge of a slot. The shell anchors the slot by them in its own
2060
+ * layout, so a slot follows a change of its tile's size in the same frame. */
2061
+ interface SlotInsets {
2062
+ left: number;
2063
+ top: number;
2064
+ right: number;
2065
+ bottom: number;
2066
+ }
2067
+ /** One rectangle a view reserves for the shell's trusted parts. */
2068
+ interface HostSlot {
2069
+ /** The view's own name for the slot, unique among its slots. */
2070
+ id: string;
2071
+ kind: HostSlotKind;
2072
+ /** On a `chat` or `composer` slot: the conversation of the view's app
2073
+ * it shows. Null or absent, it shows a new conversation, which the
2074
+ * first message creates in `project` when one is named, and
2075
+ * `hearthscale/ui/slot-session` tells the view its id. */
2076
+ session?: string | null;
2077
+ project?: string;
2078
+ /** On a `terminal` slot: the folder its shell starts in, inside a folder
2079
+ * the person gave the view's app. The slot's id, letters, digits, `-`
2080
+ * and `_`, names the shell in the view's tab: the same id takes up the
2081
+ * shell it opened while the platform holds it, and the shell ends with
2082
+ * the tab. */
2083
+ cwd?: string;
2084
+ insets: SlotInsets;
2085
+ }
2086
+ /** The notifications the host sends every view that reserved a slot. */
2087
+ type HostSlotNotification =
2088
+ /** The height the composer or the cards in the slot take now. They lie
2089
+ * on the slot's bottom edge and grow upward, past the slot's top edge
2090
+ * when they need to; the view may make room for them. */
2091
+ {
2092
+ method: 'hearthscale/ui/slot-size';
2093
+ params: {
2094
+ id: string;
2095
+ height: number;
2096
+ };
2097
+ }
2098
+ /** The chat or the composer in the slot shows another conversation of
2099
+ * the app: the one its first message created, a fork, or a helper. The
2100
+ * shell shows it at once; the view's next report of the slot names it. */
2101
+ | {
2102
+ method: 'hearthscale/ui/slot-session';
2103
+ params: {
2104
+ id: string;
2105
+ session: string;
2106
+ };
2107
+ }
2108
+ /** The tab's web page changed: its address, title, icon, load, history
2109
+ * or zoom. Sent once when the view starts and at each change. */
2110
+ | {
2111
+ method: 'hearthscale/web/page';
2112
+ params: WebPageState;
2113
+ }
2114
+ /** The matches of the last `hearthscale/web/find`. */
2115
+ | {
2116
+ method: 'hearthscale/web/found';
2117
+ params: {
2118
+ active: number;
2119
+ total: number;
2120
+ };
2121
+ }
2122
+ /** A download of the app's pages started or moved; each view tab of the
2123
+ * app hears it, and `mine` is set for the tab whose page started it. */
2124
+ | {
2125
+ method: 'hearthscale/web/download';
2126
+ params: WebDownload & {
2127
+ mine: boolean;
2128
+ };
2129
+ }
2130
+ /** The tab's page sent a password with a sign-in form, which the host
2131
+ * holds for ten minutes and keeps in the system's keychain once the
2132
+ * view asks with `hearthscale/web/password-save`. */
2133
+ | {
2134
+ method: 'hearthscale/web/password';
2135
+ params: {
2136
+ id: string;
2137
+ } & WebLogin;
2138
+ };
2139
+ /** The web page of a view's tab as the view hears it. The page lives as
2140
+ * long as the tab, whether or not the view reserves its `web` slot, and
2141
+ * it lies over the slot unless the view covers it. */
2142
+ type WebPageState = {
2143
+ /** The address the page shows; null for a tab that has loaded none. */
2144
+ url: string | null;
2145
+ title: string;
2146
+ /** The page's own icon, an address; absent while it names none. */
2147
+ icon?: string;
2148
+ loading: boolean;
2149
+ canGoBack: boolean;
2150
+ canGoForward: boolean;
2151
+ /** The page's zoom factor, 1 at 100 %. */
2152
+ zoom: number;
2153
+ };
2154
+ type Empty = Record<never, never>;
2155
+ /** The host methods beyond MCP Apps that every view may call. */
2156
+ interface ViewHostMethods {
2157
+ /** Reserves rectangles of a view that fills a tab, each for a part the
2158
+ * shell draws and answers there: the chat, the composer or the cards.
2159
+ * The list replaces the last one; an empty list gives every rectangle
2160
+ * back, and a new document of the view starts with none. A slot is cut
2161
+ * to the view. A slot below `SLOT_MIN_WIDTH` or `SLOT_MIN_HEIGHT`, or
2162
+ * one that names a conversation of another app, is not drawn. The view
2163
+ * never receives what the person types there. Requests of the app dock
2164
+ * on the composer of its slot or in its `cards` slot, and a press on a
2165
+ * card counts only once the slot has kept its place for a moment. A
2166
+ * view in a transcript reserves none. */
2167
+ 'hearthscale/ui/slots': {
2168
+ params: {
2169
+ slots: HostSlot[];
2170
+ };
2171
+ result: Empty;
2172
+ };
2173
+ /** Gives the keyboard focus to the composer of a `chat` or `composer`
2174
+ * slot, right after the person's own click or key press in the view. */
2175
+ 'hearthscale/ui/focus-slot': {
2176
+ params: {
2177
+ id: string;
2178
+ };
2179
+ result: Empty;
2180
+ };
2181
+ /** Names the tab a view fills, in its lane and its hover card: `title`
2182
+ * its title, at most `TAB_TITLE_CHARS` characters, and `icon` its mark,
2183
+ * a picture as a `data:` address of at most `TAB_ICON_CHARS`
2184
+ * characters. A field left out stays as it is; null gives back the
2185
+ * tab's own: the surface's title or its conversation's, and the
2186
+ * surface's mark or its app's. The tab keeps its name through a
2187
+ * restart. A result's view names the tab it moves into, whenever it
2188
+ * moves. */
2189
+ 'hearthscale/ui/set-tab': {
2190
+ params: {
2191
+ title?: string | null;
2192
+ icon?: string | null;
2193
+ };
2194
+ result: Empty;
2195
+ };
2196
+ /** Shows a view surface of the same app that its manifest places in
2197
+ * `modal` over the asking view's tile, on a click inside the view. The
2198
+ * tile dims under the modal, and the modal's view fills the tile on a
2199
+ * clear plate and draws its own box. It takes `input` as its tool
2200
+ * input, `{}` when absent, and the tool its surface names is called
2201
+ * with it. One modal per tile: another replaces it. The app's
2202
+ * requests draw above the modal. */
2203
+ 'hearthscale/ui/open-modal': {
2204
+ params: {
2205
+ surface: string;
2206
+ input?: Record<string, unknown>;
2207
+ };
2208
+ result: Empty;
2209
+ };
2210
+ /** Closes the modal the calling view is in. */
2211
+ 'hearthscale/ui/close-modal': {
2212
+ params: Empty;
2213
+ result: Empty;
2214
+ };
2215
+ /** Keeps a JSON value for this view's tab, which the host gives back in
2216
+ * `hearthscale/widgetState`; it never reaches a model. The value takes
2217
+ * at most 64 KiB as JSON text. */
2218
+ 'hearthscale/ui/set-widget-state': {
2219
+ params: {
2220
+ state: unknown;
2221
+ };
2222
+ result: Empty;
2223
+ };
2224
+ /** Lets the person pick files with the system's dialog, on a click
2225
+ * inside the view and at every level, filtered by extension (`.png`),
2226
+ * media type (`image/png`) or a family of types (`image/*`); the files
2227
+ * read through `resources/read`, as files the app opened. A cancelled
2228
+ * dialog answers none. */
2229
+ 'hearthscale/ui/select-files': {
2230
+ params: {
2231
+ accept?: string[];
2232
+ multiple?: boolean;
2233
+ };
2234
+ result: {
2235
+ files: OpenedFile[];
2236
+ };
2237
+ };
2238
+ /** Stores `blob`, base64 of at most 64 MiB, as an
2239
+ * asset of the conversation the view belongs to, else of its app; the
2240
+ * view reads it back as `hearthscale-asset://<asset>` and hands `asset`
2241
+ * to its tools. */
2242
+ 'hearthscale/ui/upload-file': {
2243
+ params: {
2244
+ name: string;
2245
+ mediaType: string;
2246
+ blob: string;
2247
+ };
2248
+ result: {
2249
+ asset: string;
2250
+ };
2251
+ };
2252
+ 'openai/resources/write': {
2253
+ params: ResourceWrite;
2254
+ result: ResourceWriteResult;
2255
+ };
2256
+ /** Loads an `https:` or `http:` address in the tab's web page, which
2257
+ * starts with the first one. A host the app does not declare in
2258
+ * `environment.network` is refused before it leaves the machine. */
2259
+ 'hearthscale/web/navigate': {
2260
+ params: {
2261
+ url: string;
2262
+ };
2263
+ result: Empty;
2264
+ };
2265
+ /** Steps the page back or forward in its history, loads it again, or
2266
+ * stops its load. */
2267
+ 'hearthscale/web/go': {
2268
+ params: {
2269
+ to: 'back' | 'forward' | 'reload' | 'stop';
2270
+ };
2271
+ result: Empty;
2272
+ };
2273
+ /** Finds text in the page, the next match with `next`, backwards
2274
+ * unless `forward`; empty text ends the find and clears its marks. The
2275
+ * matches arrive as `hearthscale/web/found`. */
2276
+ 'hearthscale/web/find': {
2277
+ params: {
2278
+ text: string;
2279
+ forward?: boolean;
2280
+ next?: boolean;
2281
+ };
2282
+ result: Empty;
2283
+ };
2284
+ /** Sets the page's zoom factor, between 0.25 and 5. */
2285
+ 'hearthscale/web/zoom': {
2286
+ params: {
2287
+ factor: number;
2288
+ };
2289
+ result: Empty;
2290
+ };
2291
+ /** Opens the system's print dialog for the page; needs a click. */
2292
+ 'hearthscale/web/print': {
2293
+ params: Empty;
2294
+ result: Empty;
2295
+ };
2296
+ /** A picture of the page as it shows, a PNG in base64. */
2297
+ 'hearthscale/web/capture': {
2298
+ params: Empty;
2299
+ result: {
2300
+ png: string;
2301
+ };
2302
+ };
2303
+ /** Lays the page under the view while `covered`, so a menu or a mark the
2304
+ * view draws over the slot shows above it; the page shows through
2305
+ * where the view is transparent and takes no pointer. Uncovered, the
2306
+ * page lies over its slot. */
2307
+ 'hearthscale/web/cover': {
2308
+ params: {
2309
+ covered: boolean;
2310
+ };
2311
+ result: Empty;
2312
+ };
2313
+ /** Opens a new tab of the calling view's surface in its tile, after its
2314
+ * tab, with its page on `url` when one is given, and its view starting
2315
+ * from `state` as the state it saved; needs a click. */
2316
+ 'hearthscale/web/open-tab': {
2317
+ params: {
2318
+ url?: string;
2319
+ state?: unknown;
2320
+ };
2321
+ result: Empty;
2322
+ };
2323
+ /** Draws the page as a device's screen draws it, or as the slot's own
2324
+ * size with null. */
2325
+ 'hearthscale/web/device': {
2326
+ params: {
2327
+ device: WebDevice | null;
2328
+ };
2329
+ result: Empty;
2330
+ };
2331
+ /** The element of the page at a point of the slot, in the view's CSS
2332
+ * pixels from the slot's top left; null where the page shows none. */
2333
+ 'hearthscale/web/inspect': {
2334
+ params: {
2335
+ x: number;
2336
+ y: number;
2337
+ };
2338
+ result: {
2339
+ element: WebElement | null;
2340
+ };
2341
+ };
2342
+ /** Clears what the app's pages keep: their cookies and site storage,
2343
+ * and their cached files; needs a click. */
2344
+ 'hearthscale/web/clear': {
2345
+ params: {
2346
+ cookies: boolean;
2347
+ cache: boolean;
2348
+ };
2349
+ result: Empty;
2350
+ };
2351
+ /** Whether a download of the app's pages asks where to save it, with
2352
+ * the system's dialog, or goes to the system's downloads folder. */
2353
+ 'hearthscale/web/downloads': {
2354
+ params: {
2355
+ ask: boolean;
2356
+ };
2357
+ result: Empty;
2358
+ };
2359
+ /** The origins and accounts the system's keychain keeps passwords of
2360
+ * the app's pages for. No view reads a password: the host fills a
2361
+ * sign-in form of a page with the password of the page's own origin
2362
+ * when the person reaches for one of its fields. */
2363
+ 'hearthscale/web/passwords': {
2364
+ params: Empty;
2365
+ result: {
2366
+ logins: WebLogin[];
2367
+ };
2368
+ };
2369
+ /** Keeps a password a page sent in the system's keychain; needs a
2370
+ * click. */
2371
+ 'hearthscale/web/password-save': {
2372
+ params: {
2373
+ id: string;
2374
+ };
2375
+ result: Empty;
2376
+ };
2377
+ /** Removes a kept password; needs a click. */
2378
+ 'hearthscale/web/password-remove': {
2379
+ params: WebLogin;
2380
+ result: Empty;
2381
+ };
2382
+ /** Keeps the passwords of a passwords export file (a CSV with the
2383
+ * columns url, username and password, as Chrome writes it) that the
2384
+ * person picks with the system's dialog; answers how many, or null
2385
+ * for a cancelled pick. Needs a click. */
2386
+ 'hearthscale/web/passwords-import': {
2387
+ params: Empty;
2388
+ result: {
2389
+ count: number | null;
2390
+ };
2391
+ };
2392
+ /** Whether Hearthscale blocks ads in the person's browser's pages; null
2393
+ * for an app that is not the person's browser. */
2394
+ 'hearthscale/web/ad-blocking': {
2395
+ params: Empty;
2396
+ result: {
2397
+ enabled: boolean | null;
2398
+ };
2399
+ };
2400
+ /** Turns ad blocking in the person's browser on or off; needs a click. */
2401
+ 'hearthscale/web/set-ad-blocking': {
2402
+ params: {
2403
+ enabled: boolean;
2404
+ };
2405
+ result: Empty;
2406
+ };
2407
+ /** Whether the person's browser draws its pages dark; null for an app
2408
+ * that is not the person's browser. */
2409
+ 'hearthscale/web/dark-pages': {
2410
+ params: Empty;
2411
+ result: {
2412
+ enabled: boolean | null;
2413
+ };
2414
+ };
2415
+ /** Turns dark pages in the person's browser on or off; needs a click. */
2416
+ 'hearthscale/web/set-dark-pages': {
2417
+ params: {
2418
+ enabled: boolean;
2419
+ };
2420
+ result: Empty;
2421
+ };
2422
+ /** Opens a finished download in the system's app for its kind, shows it
2423
+ * in its folder, or cancels one under way; needs a click. */
2424
+ 'hearthscale/web/download': {
2425
+ params: {
2426
+ id: string;
2427
+ action: 'open' | 'show' | 'cancel';
2428
+ };
2429
+ result: Empty;
2430
+ };
2431
+ }
2432
+ /** A screen a page is drawn at: its size in CSS pixels, its device pixel
2433
+ * ratio, and whether it is a phone's or a tablet's, which draws the page
2434
+ * with a mobile viewport. */
2435
+ type WebDevice = {
2436
+ width: number;
2437
+ height: number;
2438
+ scale: number;
2439
+ mobile: boolean;
2440
+ };
2441
+ /** An element of a page as the person points at it. */
2442
+ type WebElement = {
2443
+ /** The tag, with its id and classes, as a CSS selector names it. */
2444
+ selector: string;
2445
+ /** Its text, its label, or its address, cut to a line. */
2446
+ text: string;
2447
+ /** Its box in the slot, in the view's CSS pixels. */
2448
+ box: {
2449
+ x: number;
2450
+ y: number;
2451
+ width: number;
2452
+ height: number;
2453
+ };
2454
+ };
2455
+ /** An account a page signs in with: its page's origin and its name. */
2456
+ type WebLogin = {
2457
+ origin: string;
2458
+ username: string;
2459
+ };
2460
+ /** A download of one of the app's pages, as its tab's view hears it. */
2461
+ type WebDownload = {
2462
+ id: string;
2463
+ url: string;
2464
+ /** The file's name as it is saved. */
2465
+ name: string;
2466
+ state: 'progressing' | 'completed' | 'cancelled' | 'interrupted';
2467
+ received: number;
2468
+ /** The size in bytes; 0 while it is unknown. */
2469
+ total: number;
2470
+ };
2471
+ /** The methods gated by `uses`, each with the entry its app must name
2472
+ * before the host answers it. A view acts as its app: what it reads and
2473
+ * changes is its app's own, a terminal line it types goes at its app's
2474
+ * level as one a page types, and the platform's log names the app. An
2475
+ * act that sends the person's words, deletes what cannot come back,
2476
+ * opens something on the person's screen or puts the app in a room runs
2477
+ * only on a click inside the view. */
2478
+ interface ViewExtensionMethods {
2479
+ /** The app's own conversations, newest first; archived ones only when
2480
+ * asked. Without `spawnedBy` the list holds the conversations no helper
2481
+ * call started; with it, the helpers that conversation started. */
2482
+ 'hearthscale/sessions/list': {
2483
+ uses: 'sessions';
2484
+ params: {
2485
+ archived?: boolean;
2486
+ spawnedBy?: string;
2487
+ };
2488
+ result: {
2489
+ sessions: Session[];
2490
+ };
2491
+ };
2492
+ /** A new conversation of the app, with `agent` or the app's own agent;
2493
+ * `message`, sent as its first message, needs a click. */
2494
+ 'hearthscale/sessions/create': {
2495
+ uses: 'sessions';
2496
+ params: {
2497
+ agent?: string;
2498
+ project?: string;
2499
+ title?: string;
2500
+ message?: string;
2501
+ };
2502
+ result: {
2503
+ session: Session;
2504
+ };
2505
+ };
2506
+ /** Renames a conversation, files it in a project or none, pins,
2507
+ * archives or orders it. */
2508
+ 'hearthscale/sessions/update': {
2509
+ uses: 'sessions';
2510
+ params: {
2511
+ id: string;
2512
+ title?: string;
2513
+ project?: string | null;
2514
+ pinned?: boolean;
2515
+ archived?: boolean;
2516
+ rank?: number;
2517
+ };
2518
+ result: {
2519
+ session: Session;
2520
+ };
2521
+ };
2522
+ /** Deletes a conversation and the helpers it started; needs a click. */
2523
+ 'hearthscale/sessions/delete': {
2524
+ uses: 'sessions';
2525
+ params: {
2526
+ id: string;
2527
+ };
2528
+ result: Empty;
2529
+ };
2530
+ /** A copy of a conversation from its current leaf, as a new
2531
+ * conversation of the app. */
2532
+ 'hearthscale/sessions/fork': {
2533
+ uses: 'sessions';
2534
+ params: {
2535
+ id: string;
2536
+ };
2537
+ result: {
2538
+ session: Session;
2539
+ };
2540
+ };
2541
+ /** Sends the person's words as the conversation's next message; needs
2542
+ * a click. */
2543
+ 'hearthscale/sessions/send': {
2544
+ uses: 'sessions';
2545
+ params: {
2546
+ id: string;
2547
+ text: string;
2548
+ };
2549
+ result: Empty;
2550
+ };
2551
+ 'hearthscale/sessions/entries': {
2552
+ uses: 'sessions';
2553
+ params: {
2554
+ id: string;
2555
+ };
2556
+ result: {
2557
+ entries: Entry[];
2558
+ };
2559
+ };
2560
+ 'hearthscale/sessions/projects/list': {
2561
+ uses: 'sessions';
2562
+ params: Empty;
2563
+ result: {
2564
+ projects: ProjectRecord[];
2565
+ };
2566
+ };
2567
+ 'hearthscale/sessions/projects/create': {
2568
+ uses: 'sessions';
2569
+ params: {
2570
+ name: string;
2571
+ };
2572
+ result: {
2573
+ project: ProjectRecord;
2574
+ };
2575
+ };
2576
+ 'hearthscale/sessions/projects/update': {
2577
+ uses: 'sessions';
2578
+ params: {
2579
+ id: string;
2580
+ name?: string;
2581
+ instructions?: string;
2582
+ rank?: number;
2583
+ };
2584
+ result: {
2585
+ project: ProjectRecord;
2586
+ };
2587
+ };
2588
+ /** Deletes a project; its conversations stay, filed under none. Needs a
2589
+ * click. */
2590
+ 'hearthscale/sessions/projects/delete': {
2591
+ uses: 'sessions';
2592
+ params: {
2593
+ id: string;
2594
+ };
2595
+ result: Empty;
2596
+ };
2597
+ /** The store the app's backend shares; `value` is null for a key that
2598
+ * holds nothing. */
2599
+ 'hearthscale/store/get': {
2600
+ uses: 'store';
2601
+ params: {
2602
+ key: string;
2603
+ };
2604
+ result: {
2605
+ value: unknown;
2606
+ };
2607
+ };
2608
+ 'hearthscale/store/set': {
2609
+ uses: 'store';
2610
+ params: {
2611
+ key: string;
2612
+ value: unknown;
2613
+ };
2614
+ result: Empty;
2615
+ };
2616
+ 'hearthscale/store/delete': {
2617
+ uses: 'store';
2618
+ params: {
2619
+ key: string;
2620
+ };
2621
+ result: Empty;
2622
+ };
2623
+ 'hearthscale/roots/list': {
2624
+ uses: 'roots';
2625
+ params: Empty;
2626
+ result: {
2627
+ mounts: Mount[];
2628
+ };
2629
+ };
2630
+ /** Opens the system's picker right after the person's own click or key
2631
+ * press; the pick becomes a grant and a mount. */
2632
+ 'hearthscale/roots/request': {
2633
+ uses: 'roots';
2634
+ params: {
2635
+ purpose: string;
2636
+ shape: 'folder' | 'file';
2637
+ mode: 'read' | 'read-write';
2638
+ };
2639
+ result: {
2640
+ mount: Mount | null;
2641
+ };
2642
+ };
2643
+ /** A folder's folders and files by name, hidden ones left out. Every
2644
+ * path of `hearthscale/fs/*` and `hearthscale/vcs/*` is absolute on the
2645
+ * platform's machine and lies inside a folder or file the person gave
2646
+ * the app; one outside, or in a folder that holds credentials, is
2647
+ * refused in words. Each reads and never writes. */
2648
+ 'hearthscale/fs/list': {
2649
+ uses: 'roots';
2650
+ params: {
2651
+ path: string;
2652
+ };
2653
+ result: FsListing;
2654
+ };
2655
+ /** A file's size, and its head as text unless it is binary. */
2656
+ 'hearthscale/fs/read': {
2657
+ uses: 'roots';
2658
+ params: {
2659
+ path: string;
2660
+ };
2661
+ result: FsRead;
2662
+ };
2663
+ /** A whole file of at most 64 MiB, in base64, with the media type of
2664
+ * its extension. */
2665
+ 'hearthscale/fs/raw': {
2666
+ uses: 'roots';
2667
+ params: {
2668
+ path: string;
2669
+ };
2670
+ result: {
2671
+ data: string;
2672
+ mediaType: string;
2673
+ };
2674
+ };
2675
+ /** The system's icon of a file's kind, or of a folder when `folder` is
2676
+ * set, as a PNG `data:` address; null where the system draws none. */
2677
+ 'hearthscale/fs/icon': {
2678
+ uses: 'roots';
2679
+ params: {
2680
+ path: string;
2681
+ folder?: boolean;
2682
+ };
2683
+ result: {
2684
+ icon: string | null;
2685
+ };
2686
+ };
2687
+ /** The repository a folder belongs to and its branch, or null where it
2688
+ * belongs to none. A repository that reaches past what the person gave
2689
+ * the app is refused, because `git` reads all of it. */
2690
+ 'hearthscale/vcs/repo': {
2691
+ uses: 'roots';
2692
+ params: {
2693
+ path: string;
2694
+ };
2695
+ result: {
2696
+ repo: VcsRepo | null;
2697
+ };
2698
+ };
2699
+ /** The branches a comparison may use, and the one checked out. */
2700
+ 'hearthscale/vcs/refs': {
2701
+ uses: 'roots';
2702
+ params: {
2703
+ root: string;
2704
+ };
2705
+ result: VcsRefs;
2706
+ };
2707
+ /** One scope's patch. */
2708
+ 'hearthscale/vcs/diff': {
2709
+ uses: 'roots';
2710
+ params: VcsDiffQuery;
2711
+ result: VcsDiff;
2712
+ };
2713
+ /** One file whole, as the scope's new side holds it. */
2714
+ 'hearthscale/vcs/file': {
2715
+ uses: 'roots';
2716
+ params: VcsFileQuery;
2717
+ result: VcsFile;
2718
+ };
2719
+ /** One streaming completion on the app's chat card or the chat role;
2720
+ * the frames arrive as `hearthscale/models/delta` with `stream`, and
2721
+ * the call answers once the stream has ended. */
2722
+ 'hearthscale/models/complete': {
2723
+ uses: 'models';
2724
+ params: {
2725
+ stream: string;
2726
+ request: CompletionRequest;
2727
+ };
2728
+ result: Empty;
2729
+ };
2730
+ 'hearthscale/models/cancel': {
2731
+ uses: 'models';
2732
+ params: {
2733
+ stream: string;
2734
+ };
2735
+ result: Empty;
2736
+ };
2737
+ 'hearthscale/jobs/submit': {
2738
+ uses: 'jobs';
2739
+ params: {
2740
+ kind: JobKind;
2741
+ input: JobInput;
2742
+ };
2743
+ result: {
2744
+ job: Job;
2745
+ };
2746
+ };
2747
+ 'hearthscale/jobs/get': {
2748
+ uses: 'jobs';
2749
+ params: {
2750
+ id: string;
2751
+ };
2752
+ result: {
2753
+ job: Job;
2754
+ };
2755
+ };
2756
+ 'hearthscale/jobs/cancel': {
2757
+ uses: 'jobs';
2758
+ params: {
2759
+ id: string;
2760
+ };
2761
+ result: {
2762
+ job: Job;
2763
+ };
2764
+ };
2765
+ /** Opens the person's own shell in a folder, at the app's level as a
2766
+ * page opens one, or takes up the one this window holds under the same
2767
+ * id; its snapshot and output arrive as `hearthscale/terminals/frame`.
2768
+ * The id is the view's own. Each line the view writes goes at the app's
2769
+ * level before it reaches the shell. */
2770
+ 'hearthscale/terminals/open': {
2771
+ uses: 'terminals';
2772
+ params: {
2773
+ id: string;
2774
+ cwd: string;
2775
+ cols: number;
2776
+ rows: number;
2777
+ };
2778
+ result: {
2779
+ terminal: TerminalSnapshot;
2780
+ };
2781
+ };
2782
+ 'hearthscale/terminals/write': {
2783
+ uses: 'terminals';
2784
+ params: {
2785
+ id: string;
2786
+ data: string;
2787
+ };
2788
+ result: Empty;
2789
+ };
2790
+ 'hearthscale/terminals/resize': {
2791
+ uses: 'terminals';
2792
+ params: {
2793
+ id: string;
2794
+ cols: number;
2795
+ rows: number;
2796
+ };
2797
+ result: Empty;
2798
+ };
2799
+ 'hearthscale/terminals/close': {
2800
+ uses: 'terminals';
2801
+ params: {
2802
+ id: string;
2803
+ };
2804
+ result: Empty;
2805
+ };
2806
+ /** Opens a file by its absolute path on the platform's machine in the
2807
+ * surface that opens its extension, else in the system's own app when
2808
+ * the system shows its kind without running it; and shows a folder of
2809
+ * the app's own, one the person gave it, its data folder or a folder of
2810
+ * one of its projects, in the system's file manager. Needs a click. */
2811
+ 'openai/files/open': {
2812
+ uses: 'files.open';
2813
+ params: {
2814
+ path: string;
2815
+ };
2816
+ result: Empty;
2817
+ };
2818
+ /** Opens a surface of this app, or of `app`, that its manifest places
2819
+ * in the rail or a tab, beside the view's tile; `session` is a
2820
+ * conversation of that app a chat opens on, `path` the address a view
2821
+ * opens on, as a deep link gives it. With `session` and no `surface`,
2822
+ * the conversation opens in its own app, as a notification's click
2823
+ * opens it; `app`, when given, must be that app. Needs a click. */
2824
+ 'hearthscale/surfaces/open': {
2825
+ uses: 'surfaces.open';
2826
+ params: {
2827
+ app?: string;
2828
+ surface?: string;
2829
+ session?: string;
2830
+ path?: string;
2831
+ };
2832
+ result: Empty;
2833
+ };
2834
+ /** The rooms the app is in. */
2835
+ 'hearthscale/peers/rooms': {
2836
+ uses: 'peers';
2837
+ params: Empty;
2838
+ result: {
2839
+ rooms: Room[];
2840
+ };
2841
+ };
2842
+ /** Opens a new room of the app, whose ticket the view shows the person
2843
+ * to pass on. Needs a click. */
2844
+ 'hearthscale/peers/create': {
2845
+ uses: 'peers';
2846
+ params: Empty;
2847
+ result: {
2848
+ room: Room;
2849
+ };
2850
+ };
2851
+ /** Asks the person for the ticket of a room in a sheet the host draws,
2852
+ * at every level, and joins that room; null when the person cancels.
2853
+ * The view never names the ticket. Needs a click. */
2854
+ 'hearthscale/peers/join': {
2855
+ uses: 'peers';
2856
+ params: Empty;
2857
+ result: {
2858
+ room: Room | null;
2859
+ };
2860
+ };
2861
+ /** Sends `data`, at most 64 KiB as JSON, to every other
2862
+ * member of a room of the app; each member's app hears it as
2863
+ * `hearthscale/peers/message`, its views and its backend alike. */
2864
+ 'hearthscale/peers/send': {
2865
+ uses: 'peers';
2866
+ params: {
2867
+ room: string;
2868
+ data: unknown;
2869
+ };
2870
+ result: Empty;
2871
+ };
2872
+ /** Takes the app out of a room; a room it opened ends for every member. */
2873
+ 'hearthscale/peers/leave': {
2874
+ uses: 'peers';
2875
+ params: {
2876
+ room: string;
2877
+ };
2878
+ result: Empty;
2879
+ };
2880
+ }
2881
+ type ViewExtensionMethod = keyof ViewExtensionMethods;
2882
+ /** The notifications the host sends a view, each only to a view whose app
2883
+ * names the extension in `uses`: a conversation of the app changed or
2884
+ * went, a project of any app changed, the app's backend emitted an
2885
+ * event, a completion or a terminal of the view moved, a job of the app
2886
+ * moved, a room of the app changed, ended or carried a message. */
2887
+ type ViewExtensionNotification = {
2888
+ method: 'hearthscale/sessions/changed';
2889
+ params: {
2890
+ session: string;
2891
+ };
2892
+ } | {
2893
+ method: 'hearthscale/sessions/deleted';
2894
+ params: {
2895
+ id: string;
2896
+ };
2897
+ } | {
2898
+ method: 'hearthscale/sessions/projects/changed';
2899
+ params: Empty;
2900
+ }
2901
+ /** A transient event the app's backend emitted. */
2902
+ | {
2903
+ method: 'hearthscale/events/event';
2904
+ params: {
2905
+ name: string;
2906
+ session?: string;
2907
+ data: unknown;
2908
+ };
2909
+ } | {
2910
+ method: 'hearthscale/models/delta';
2911
+ params: {
2912
+ stream: string;
2913
+ delta: CompletionDelta;
2914
+ };
2915
+ } | {
2916
+ method: 'hearthscale/jobs/progress';
2917
+ params: {
2918
+ job: Job;
2919
+ };
2920
+ } | {
2921
+ method: 'hearthscale/terminals/frame';
2922
+ params: {
2923
+ id: string;
2924
+ frame: TerminalFrame;
2925
+ };
2926
+ }
2927
+ /** The app entered a room, or a member came to it or left it. */
2928
+ | {
2929
+ method: 'hearthscale/peers/changed';
2930
+ params: {
2931
+ room: Room;
2932
+ };
2933
+ }
2934
+ /** The app is out of a room. */
2935
+ | {
2936
+ method: 'hearthscale/peers/ended';
2937
+ params: {
2938
+ room: string;
2939
+ };
2940
+ } | {
2941
+ method: 'hearthscale/peers/message';
2942
+ params: {
2943
+ room: string;
2944
+ from: string;
2945
+ data: unknown;
2946
+ };
2947
+ };
2948
+
2949
+ /**
2950
+ * The types a view imports: the methods every view may call
2951
+ * (`ViewHostMethods`), the `hearthscale/*` extensions and
2952
+ * `openai/files/open`, which it calls through its MCP Apps `App`, each
2953
+ * answered only when its app names the method's entry in `uses`, the
2954
+ * notifications the host sends it, the keys the host adds to the host
2955
+ * context, the `_meta` of the standard messages, and the tool input of a
2956
+ * view that opens a file (`FileInput`). The host lists what
2957
+ * the view may call in `getHostCapabilities().experimental.hearthscale`.
2958
+ */
2959
+
2960
+ /** One extension call, typed by its method: the request's params in, its
2961
+ * result out. A refusal rejects with the host's words. */
2962
+ type ViewExtensionCall = <M extends ViewExtensionMethod>(method: M, params: ViewExtensionMethods[M]['params']) => Promise<ViewExtensionMethods[M]['result']>;
2963
+
2964
+ /**
2965
+ * `@hearthscale/app`: the types an app's backend and its views import,
2966
+ * and the type of its manifest, `app.json` (`Manifest`). The
2967
+ * running implementation lives in the app host, which serves this import
2968
+ * from itself, so an app bundles nothing of the platform. Everything here
2969
+ * is wire-shaped: every method is asynchronous, every value is JSON, files
2970
+ * are handles inside mounts, and no callable crosses to the core.
2971
+ */
2972
+
2973
+ /** A relayed TCP or TLS stream. Bytes have no message boundaries; the
2974
+ * protocol on top is the app's. */
2975
+ interface Connection {
2976
+ readonly id: string;
2977
+ readonly state: 'plain' | 'tls';
2978
+ write(bytes: Uint8Array | string): Promise<void>;
2979
+ /** Upgrades the same socket to TLS after the app's protocol exchange. */
2980
+ startTls(): Promise<void>;
2981
+ close(): Promise<void>;
2982
+ on(event: 'data', fn: (bytes: Uint8Array) => void): () => void;
2983
+ on(event: 'end' | 'closed', fn: () => void): () => void;
2984
+ on(event: 'error', fn: (error: {
2985
+ code: string;
2986
+ message: string;
2987
+ }) => void): () => void;
2988
+ }
2989
+ /** A sandboxed run of one program in the app's environment. */
2990
+ interface Run {
2991
+ readonly id: string;
2992
+ wait(): Promise<{
2993
+ exitCode: number;
2994
+ output: string;
2995
+ }>;
2996
+ cancel(): Promise<void>;
2997
+ on(event: 'output', fn: (output: string) => void): () => void;
2998
+ }
2999
+ /** What a backend reaches. A namespace the manifest's `uses` does not
3000
+ * name is absent from this object. */
3001
+ interface AppContext {
3002
+ readonly app: {
3003
+ id: string;
3004
+ version: string;
3005
+ };
3006
+ readonly api: number;
3007
+ /** The folders the host reaches, by handle. */
3008
+ readonly mounts: Mount[];
3009
+ /** Aborts when the host is disposed. */
3010
+ readonly signal: AbortSignal;
3011
+ log(level: 'debug' | 'info' | 'warn' | 'error', message: string): Promise<void>;
3012
+ roots?: {
3013
+ list(): Promise<Mount[]>;
3014
+ /** Raises the folder card; the person's pick becomes a mount, which
3015
+ * the request answers. The host restarts with it once its calls and
3016
+ * folder requests in flight have ended, so the mount works from the
3017
+ * next call; asked outside any call, the answer comes first. */
3018
+ request(purpose: string, shape: 'folder' | 'file'): Promise<Mount | null>;
3019
+ };
3020
+ fs?: {
3021
+ read(path: MountPath, options?: {
3022
+ offset?: number;
3023
+ limit?: number;
3024
+ }): Promise<string>;
3025
+ list(path: MountPath): Promise<{
3026
+ dirs: string[];
3027
+ files: string[];
3028
+ }>;
3029
+ write(path: MountPath, text: string): Promise<void>;
3030
+ edit(path: MountPath, oldText: string, newText: string): Promise<void>;
3031
+ /** Copies an asset's bytes to a path inside a read-write mount. */
3032
+ export(asset: string, path: MountPath): Promise<void>;
3033
+ };
3034
+ assets: {
3035
+ get(id: string, range?: {
3036
+ offset: number;
3037
+ length: number;
3038
+ }): Promise<{
3039
+ bytes: Uint8Array;
3040
+ mediaType: string;
3041
+ }>;
3042
+ put(bytes: Uint8Array | string, mediaType: string, name?: string): Promise<string>;
3043
+ /** Copies a file from a mount into the store, in the core. */
3044
+ import(path: MountPath, mediaType: string): Promise<string>;
3045
+ release(id: string): Promise<void>;
3046
+ };
3047
+ net?: {
3048
+ fetch(params: FetchParams): Promise<FetchAnswer>;
3049
+ connect(host: string, port: number, tls: boolean): Promise<Connection>;
3050
+ };
3051
+ exec?: {
3052
+ run(argv: string[], cwd: MountPath, options?: {
3053
+ wall?: number;
3054
+ }): Promise<Run>;
3055
+ };
3056
+ surface?: {
3057
+ /** A tab command on the session's executor surface, from inside a
3058
+ * body whose tool is declared outbound: on the page of the app's
3059
+ * first surface placed in the rail or a tab, a `web` surface's own
3060
+ * page or the web page of a view's tab; `show` reaches no network and
3061
+ * runs from any body. Rejects with the error the window met. */
3062
+ command(op: SurfaceOp, args?: Record<string, unknown>): Promise<SurfaceAnswer>;
3063
+ };
3064
+ store?: {
3065
+ get(key: string): Promise<unknown>;
3066
+ set(key: string, value: unknown): Promise<void>;
3067
+ delete(key: string): Promise<void>;
3068
+ };
3069
+ secrets?: {
3070
+ get(name: string): Promise<string | null>;
3071
+ set(name: string, value: string): Promise<void>;
3072
+ delete(name: string): Promise<void>;
3073
+ };
3074
+ oauth?: {
3075
+ /** True once a token for the issuer covers the scopes: at once when
3076
+ * one is held, else after the person's sign-in card and the grant;
3077
+ * false when the person or the service said no. Rejects with the
3078
+ * reason when the request, the manifest's `network` or the service
3079
+ * does not allow a sign-in. A sign-in replaces the scopes held, so
3080
+ * each call names every scope the app needs. */
3081
+ login(request: OAuthLogin): Promise<boolean>;
3082
+ /** The access token, refreshed when it is past half its lifetime;
3083
+ * null when the app must sign in again. A token the service revoked
3084
+ * early still comes back: after a 401, `logout` and `login` again. */
3085
+ token(issuer: string): Promise<string | null>;
3086
+ /** Forgets the tokens for the issuer, revoked at the service where
3087
+ * its metadata names a revocation endpoint. Null, or the words when
3088
+ * the service did not confirm the revocation and may still hold the
3089
+ * access. */
3090
+ logout(issuer: string): Promise<string | null>;
3091
+ };
3092
+ events?: {
3093
+ emit(event: AppEvent): Promise<void>;
3094
+ };
3095
+ notify?(notice: AppNotice): Promise<void>;
3096
+ /** Drawn in the conversation at every level, from inside a tool body. */
3097
+ ask(question: AppQuestion): Promise<AppAnswer>;
3098
+ sessions?: {
3099
+ /** This app's sessions and the ones it started in other apps. */
3100
+ list(): Promise<SessionListing[]>;
3101
+ open(id: string): Promise<OpenedSession>;
3102
+ create(body: SessionCreate): Promise<{
3103
+ id: string;
3104
+ }>;
3105
+ send(id: string, text: string): Promise<void>;
3106
+ wait(id: string): Promise<TurnOutcome>;
3107
+ on(event: 'deleted', fn: (id: string) => void): () => void;
3108
+ on(event: 'finished', fn: (result: {
3109
+ session: string;
3110
+ turn: string;
3111
+ stopReason: string;
3112
+ }) => void): () => void;
3113
+ };
3114
+ apps: {
3115
+ /** The enabled apps. */
3116
+ list(): Promise<AppListing[]>;
3117
+ /** Called when the set of enabled apps changed. */
3118
+ on(event: 'changed', fn: () => void): () => void;
3119
+ };
3120
+ /** The settings the manifest declares, which the platform keeps. */
3121
+ settings: {
3122
+ /** Every setting, with the value the person set or its default;
3123
+ * rejects for an app that declares no settings. */
3124
+ get(): Promise<SettingValues>;
3125
+ /** Called with every setting's value when the person changed one. */
3126
+ on(event: 'changed', fn: (values: SettingValues) => void): () => void;
3127
+ };
3128
+ models?: {
3129
+ /** One streaming completion on the card this app runs chat on, or
3130
+ * the chat role; `format` asks for JSON against a schema. A call
3131
+ * inside a tool body counts against the budget of the turn that
3132
+ * made the call. */
3133
+ complete(request: CompletionRequest, signal?: AbortSignal): AsyncIterable<CompletionDelta>;
3134
+ /** The chat cards an app's sessions may run on: this app's, or
3135
+ * another app's under the `sessions.create` grant. */
3136
+ for(app?: string): Promise<ModelListing[]>;
3137
+ /** The card an app runs a kind on, and where the choice came from, or
3138
+ * the reason none can: this app's, or another app's under the
3139
+ * `sessions.create` grant. */
3140
+ effective(kind: ModelKind, app?: string): Promise<EffectiveModel>;
3141
+ };
3142
+ /** With `sessions.create`: the projects of an app this one may start
3143
+ * conversations in. */
3144
+ projects?: {
3145
+ list(app?: string): Promise<ProjectListing[]>;
3146
+ };
3147
+ /** With `catalogue`: the bus. A search reads the catalogue, never
3148
+ * asks, and answers the whole definition of each match; a call runs
3149
+ * another app's offered tool or an attached connector's tool, with this
3150
+ * app as the caller. A call made inside a tool body runs under that
3151
+ * call, in its conversation and at its level, and ends with it; the
3152
+ * signal cancels it earlier. A refused or failed call rejects with the
3153
+ * words. */
3154
+ capabilities?: {
3155
+ search(queries: string | string[], limit?: number): Promise<string>;
3156
+ call(name: string, input?: Record<string, unknown>, options?: {
3157
+ signal?: AbortSignal;
3158
+ }): Promise<CapabilityAnswer>;
3159
+ };
3160
+ /** With `readers`: a reader for a format the manifest declares under
3161
+ * `formats`; the read tool hands it the file's bytes. */
3162
+ readers?: {
3163
+ register(format: string, read: (bytes: Uint8Array, name: string) => Promise<string> | string): () => void;
3164
+ };
3165
+ jobs?: {
3166
+ /** One job on the card this app runs that kind on. */
3167
+ submit(kind: JobKind, input: JobInput): Promise<Job>;
3168
+ get(id: string): Promise<Job>;
3169
+ cancel(id: string): Promise<Job>;
3170
+ on(event: 'progress', fn: (job: Job) => void): () => void;
3171
+ };
3172
+ /** With `peers`: the rooms the app is in. The person opens or joins a
3173
+ * room through a view of the app; a backend sends to the room's other
3174
+ * members, hears their messages, and may leave. While the app is in a
3175
+ * room, an event of it starts the backend when it is not up. */
3176
+ peers?: {
3177
+ list(): Promise<Room[]>;
3178
+ /** Sends `data`, at most 64 KiB as JSON, to every other
3179
+ * member; inside a tool body, the tool is declared outbound. */
3180
+ send(room: string, data: unknown): Promise<void>;
3181
+ leave(room: string): Promise<void>;
3182
+ on(event: 'message', fn: (message: {
3183
+ room: string;
3184
+ from: string;
3185
+ data: unknown;
3186
+ }) => void): () => void;
3187
+ /** The app entered a room, or a member came to it or left it. */
3188
+ on(event: 'changed', fn: (room: Room) => void): () => void;
3189
+ /** The app is out of a room. */
3190
+ on(event: 'ended', fn: (room: string) => void): () => void;
3191
+ };
3192
+ }
3193
+ /** What a tool body runs with. */
3194
+ interface ToolRun {
3195
+ readonly callId: string;
3196
+ readonly context: CallContext;
3197
+ readonly signal: AbortSignal;
3198
+ /** A snapshot of the output so far, drawn under the tool's row. */
3199
+ progress(output: string): void;
3200
+ }
3201
+ /** What a capability call answers: its text, its structured object when
3202
+ * the tool answered one, and the files it answered, which this app
3203
+ * holds until it releases them. */
3204
+ interface CapabilityAnswer {
3205
+ text: string;
3206
+ structured?: Structured;
3207
+ assets: {
3208
+ id: string;
3209
+ mediaType: string;
3210
+ name?: string;
3211
+ }[];
3212
+ }
3213
+ /** A tool body: the input the model sent, checked against the tool's
3214
+ * input schema before the body runs, and the answer: text, content,
3215
+ * content with its structured object and a view, or one structured
3216
+ * object alone. An object whose `content` is an array is read as content
3217
+ * with its structured object, so a structured result that holds such a
3218
+ * field answers `{ content: [], structured }`. A tool that declares
3219
+ * `output` answers a structured object that matches it. */
3220
+ type ToolBody = (input: unknown, run: ToolRun) => Promise<string | Content[] | ToolAnswer | Structured> | string | Content[] | ToolAnswer | Structured;
3221
+ /** What `backend.js` exports. `activate` has five seconds; a body a
3222
+ * manifest tool lacks, or a manifest tool with no body, fails activation
3223
+ * with the name. */
3224
+ interface Backend {
3225
+ activate?(ctx: AppContext): Promise<void> | void;
3226
+ deactivate?(): Promise<void> | void;
3227
+ tools?: Record<string, ToolBody>;
3228
+ }
3229
+
3230
+ export type { AppAnswer, AppContext, AppEvent, AppListing, AppNotice, AppQuestion, Backend, CallContext, CapabilityAnswer, ChatContentPart, ChatMessage, CompletionDelta, CompletionRequest, CompletionStop, Connection, Content, ContentBlockMeta, EffectiveModel, FetchAnswer, FetchParams, FileInput, HearthscaleHostCapability, HostSlot, HostSlotKind, HostSlotNotification, Job, JobInput, JobInputs, JobKind, Manifest, MessageOptions, ModelContextAnswer, ModelKind, ModelListing, ModelSource, Mount, MountPath, OAuthLogin, OpenedSession, Origin, PrincipalRef, ProjectListing, ResourcePath, ResourceReadOptions, ResourceVersion, Room, Run, Sampling, Session, SessionCell, SessionCreate, SessionListing, SessionProcess, SessionRoot, SessionStatus, SettingValues, SlotInsets, StopReason, Structured, SurfaceAnswer, SurfaceOp, SurfaceResult, TerminalFrame, TerminalSnapshot, ToolAnswer, ToolBody, ToolCall, ToolDefinition, ToolRun, TurnOutcome, Usage, ViewExtension, ViewExtensionCall, ViewExtensionMethod, ViewExtensionMethods, ViewExtensionNotification, ViewHostContext, ViewHostMethods, WebPageState };