@holokai/harbor-sdk 0.3.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,613 @@
1
+ /**
2
+ * THE PUBLIC WIRE of harbor's extension to MCP Apps — spelled once, here, with
3
+ * ZERO imports. Harbor's gate imports this module for runtime validation and
4
+ * the SDK client imports it for its types, so the two cannot drift.
5
+ *
6
+ * Everything here is a PROJECTION of harbor, never harbor's internals: no
7
+ * `RunView`, no `FusionEventMap`, no IPC `*View` — each public shape names
8
+ * what it keeps, and a field a hosted app needs that is not here becomes a
9
+ * new public field, never an import (ADR-HARBOR-012, decision 5).
10
+ *
11
+ * Versioning: `HARBOR_EXTENSION_VERSION` rides the handshake; methods and
12
+ * events are additive within a major.
13
+ */
14
+ /** The key under `hostCapabilities.experimental` (and under a tool's `_meta`). */
15
+ export declare const HARBOR_EXTENSION_ID = "ai.holokai/harbor";
16
+ /** The page reader's tool name, and the `reads` mark under `_meta[HARBOR_EXTENSION_ID]` that names it. */
17
+ export declare const HARBOR_PAGE_READER: {
18
+ readonly tool: "read_page";
19
+ readonly reads: "page";
20
+ };
21
+ /** The extension's own version — independent of the MCP Apps protocol version. */
22
+ export declare const HARBOR_EXTENSION_VERSION = "0.2";
23
+ /** The MCP Apps protocol revision harbor speaks. */
24
+ export declare const MCP_APPS_PROTOCOL_VERSION = "2026-01-26";
25
+ export declare const HARBOR_METHODS: {
26
+ readonly workflowsList: "harbor/workflows/list";
27
+ readonly workflowsSubmit: "harbor/workflows/submit";
28
+ readonly workflowsGet: "harbor/workflows/get";
29
+ readonly capabilitiesList: "harbor/capabilities/list";
30
+ readonly eventsSubscribe: "harbor/events/subscribe";
31
+ readonly eventsUnsubscribe: "harbor/events/unsubscribe";
32
+ readonly filesOpen: "harbor/files/open";
33
+ readonly filesAdd: "harbor/files/add";
34
+ readonly filesLink: "harbor/files/link";
35
+ readonly browserOpen: "harbor/browser/open";
36
+ readonly barSet: "harbor/bar/set";
37
+ readonly viewsOpen: "harbor/views/open";
38
+ readonly viewStatus: "harbor/view/status";
39
+ };
40
+ export type HarborMethod = (typeof HARBOR_METHODS)[keyof typeof HARBOR_METHODS];
41
+ /** The host → view notification that carries subscribed events. */
42
+ export declare const HARBOR_EVENT_NOTIFICATION = "harbor/notifications/event";
43
+ /** The host → view notification that carries what the person typed or chose in the window bar. */
44
+ export declare const HARBOR_BAR_NOTIFICATION = "harbor/notifications/bar";
45
+ /**
46
+ * A run's public status. The contract's OWN union, mapped exhaustively from
47
+ * harbor's internal vocabulary with `unknown` as the registered fallback.
48
+ */
49
+ export type WorkflowStatus = 'running' | 'held' | 'done' | 'done_with_errors' | 'failed' | 'cancelled' | 'unknown';
50
+ /** What harbor says about one run. `title` is CONTENT (the user's goal text) and rides its own grant. */
51
+ export interface WorkflowSummary {
52
+ id: string;
53
+ chatId: string;
54
+ /** Empty when the `workflows.content` grant is absent — never a placeholder. */
55
+ title: string;
56
+ status: WorkflowStatus;
57
+ stepCount: number;
58
+ startedAtMs: number | null;
59
+ endedAtMs: number | null;
60
+ }
61
+ export interface WorkflowsListParams {
62
+ /** 1..50, default 20. Newest first. */
63
+ limit?: number;
64
+ }
65
+ export interface WorkflowsListResult {
66
+ workflows: WorkflowSummary[];
67
+ }
68
+ export declare const WORKFLOWS_LIST_LIMIT: {
69
+ readonly min: 1;
70
+ readonly max: 50;
71
+ readonly default: 20;
72
+ };
73
+ /**
74
+ * One capability a DAG may name: the same four fields harbor pins for its
75
+ * own planner, plus whether it is the calling app's OWN tool
76
+ * (`mcp/<this instance>/<tool>`). Harbor's built-ins (`desktop/*`), Holo
77
+ * applications (`holo/<slug>`) and other connectors' tools all appear here;
78
+ * nothing that is disabled at the global scope does.
79
+ */
80
+ export interface CapabilityEntry {
81
+ type: string;
82
+ description?: string;
83
+ inputSchema?: Record<string, unknown>;
84
+ outputSchema?: Record<string, unknown>;
85
+ own: boolean;
86
+ }
87
+ export interface CapabilitiesListResult {
88
+ capabilities: CapabilityEntry[];
89
+ }
90
+ /**
91
+ * One task of a submitted DAG. `capability` must be a type from
92
+ * `harbor/capabilities/list`. `inputMappings` binds a dot-path INTO `input`
93
+ * (`messages.0.content`) to a dot-path into an upstream task's result
94
+ * (`results.<taskId>.data.text`) — the same convention harbor's own planner
95
+ * emits; the mapped slot in `input` is a `null` placeholder that the
96
+ * dispatcher overwrites. A mapping implies a dependency; `dependsOn` adds
97
+ * ordering-only edges. Every task's output is the ONE capability envelope
98
+ * (`{ok, version, data | error}`), so result paths start at `results.<id>.data`.
99
+ */
100
+ export interface WorkflowTaskSpec {
101
+ id: string;
102
+ capability: string;
103
+ description?: string;
104
+ input?: Record<string, unknown>;
105
+ dependsOn?: string[];
106
+ inputMappings?: Record<string, string>;
107
+ }
108
+ export interface WorkflowsSubmitParams {
109
+ name: string;
110
+ description?: string;
111
+ tasks: WorkflowTaskSpec[];
112
+ }
113
+ export interface WorkflowsSubmitResult {
114
+ workflowId: string;
115
+ /** Task ids dispatched by the start — the DAG's roots. */
116
+ dispatched: string[];
117
+ }
118
+ export declare const WORKFLOW_TASKS_MAX = 50;
119
+ /** A task's public state. Mapped exhaustively from harbor's internal vocabulary. */
120
+ export type WorkflowTaskState = 'pending' | 'running' | 'done' | 'failed' | 'skipped' | 'cancelled' | 'unknown';
121
+ export interface WorkflowTaskView {
122
+ id: string;
123
+ capability: string;
124
+ state: WorkflowTaskState;
125
+ /** The task's result envelope once it has one, else null. CONTENT: an app sees only its own workflows'. */
126
+ output: unknown;
127
+ error: {
128
+ code: string | null;
129
+ message: string | null;
130
+ } | null;
131
+ }
132
+ export interface WorkflowsGetParams {
133
+ workflowId: string;
134
+ }
135
+ export interface WorkflowsGetResult {
136
+ workflowId: string;
137
+ name: string;
138
+ status: WorkflowStatus;
139
+ tasks: WorkflowTaskView[];
140
+ }
141
+ /**
142
+ * Ask harbor to open a file this app identifies. Harbor calls `tool` on the
143
+ * app's OWN server with `arguments`; the tool answers a `CallToolResult` whose
144
+ * content holds exactly one `{type: 'resource', resource: {uri, mimeType?, blob}}`
145
+ * with the file's bytes base64 in `blob`. Harbor keeps its own read-only copy
146
+ * named `name` (a bare file name, no folders) and opens it in its editor.
147
+ */
148
+ export interface FilesOpenParams {
149
+ name: string;
150
+ tool: string;
151
+ arguments?: Record<string, unknown>;
152
+ }
153
+ export interface FilesOpenResult {
154
+ /** The file name harbor opened the copy under. */
155
+ name: string;
156
+ }
157
+ /**
158
+ * Ask harbor to add a file this app identifies to the workspace the person
159
+ * opened this page to pick for. The same call as `harbor/files/open`: harbor
160
+ * calls `tool` on the app's OWN server with `arguments` for the bytes. The
161
+ * page never names the workspace; harbor decided it when it opened the page.
162
+ * Offered only to a page opened to pick files.
163
+ */
164
+ export type FilesAddParams = FilesOpenParams;
165
+ export interface FilesAddResult {
166
+ /** The name it was added under: a file's in the workspace, which may differ from the one asked for, or a linked folder's. */
167
+ name: string;
168
+ /** True when the workspace already had it, so nothing changed. */
169
+ alreadyAdded?: boolean;
170
+ }
171
+ /**
172
+ * Ask harbor to link a folder or a single file this app identifies to the
173
+ * workspace the person opened this page to pick for: the workspace's chats then
174
+ * read it, read-only, through this app's own tools, and nothing is copied.
175
+ * Harbor asks the app's `get_session` for the account signed in and its
176
+ * `item_parents` for the item, and keeps only the app, the item's id, whether
177
+ * it is a folder or a file, and that account. Offered only to a first-party
178
+ * file app's page opened to pick files.
179
+ */
180
+ export interface FilesLinkParams {
181
+ /** A folder or file id this app's own tools gave. */
182
+ id: string;
183
+ /** The folder's address on its provider's own site, kept only when the folder is picked as a workspace's file location. */
184
+ url?: string;
185
+ }
186
+ /**
187
+ * Ask harbor to open a URL in the person's own browser. A view's frame is
188
+ * sandboxed without `allow-popups`, so a link of its own goes nowhere; this
189
+ * is the one way out of the frame, and the URL leaves harbor for the
190
+ * operating system.
191
+ *
192
+ * HTTPS ONLY, and harbor decides that on the PARSED url, not this type: a
193
+ * page's own check is a convenience. Anything else — `http:`, `file:`,
194
+ * `javascript:`, an application's own scheme, an address that does not parse
195
+ * — is refused with `-32602`.
196
+ */
197
+ export interface BrowserOpenParams {
198
+ url: string;
199
+ }
200
+ export interface BrowserOpenResult {
201
+ /** The URL harbor handed over, as it parsed it. */
202
+ url: string;
203
+ }
204
+ /**
205
+ * A page hands harbor its search field and account menu, and harbor draws
206
+ * them in the window bar. Plain values only: harbor never runs page code or
207
+ * loads page markup there. Each call replaces the whole bar, and an absent or
208
+ * null field leaves that slot empty.
209
+ */
210
+ export interface BarSearch {
211
+ placeholder: string;
212
+ /** The field's accessible label. */
213
+ label: string;
214
+ /** The current text: the person's own words. */
215
+ value: string;
216
+ }
217
+ export interface BarMenuItem {
218
+ id: string;
219
+ label: string;
220
+ }
221
+ export interface BarAccount {
222
+ /** Who is signed in, shown at the top of the menu. */
223
+ name: string;
224
+ items: BarMenuItem[];
225
+ }
226
+ export interface BarSetParams {
227
+ search?: BarSearch | null;
228
+ account?: BarAccount | null;
229
+ }
230
+ /** An empty object. */
231
+ export type BarSetResult = Record<string, never>;
232
+ /**
233
+ * The `harbor/notifications/bar` params: text typed in the bar's search, the
234
+ * id of a chosen menu item, or the id of a trail step the person pressed in
235
+ * harbor's window bar. A crumb's id is one the page reported in its view
236
+ * names' `trail`, and the page alone knows what going there means.
237
+ */
238
+ export type BarInput = {
239
+ kind: 'search';
240
+ value: string;
241
+ } | {
242
+ kind: 'action';
243
+ id: string;
244
+ } | {
245
+ kind: 'crumb';
246
+ id: string;
247
+ };
248
+ /** The caps on page-supplied bar words, in characters, and on the menu's length. Harbor's gate is the one place they are enforced. */
249
+ export declare const BAR_LIMITS: {
250
+ readonly placeholderChars: 60;
251
+ readonly labelChars: 60;
252
+ readonly valueChars: 500;
253
+ readonly nameChars: 80;
254
+ readonly items: 8;
255
+ readonly itemLabelChars: 60;
256
+ readonly itemIdChars: 40;
257
+ };
258
+ /**
259
+ * What a page's tray button shows about the work behind the page: harbor's
260
+ * own tray vocabulary, chosen by the page. `running` draws the loader in
261
+ * place of the extension's icon, `waiting` a dot that says the work needs
262
+ * the person, `completed` and `failed` a mark on the corner, and `idle` the
263
+ * icon alone. The button is the extension's, so the status is shared by every
264
+ * view of it, and the last report wins. Harbor puts it back to `idle` when
265
+ * the extension's last view closes.
266
+ */
267
+ export declare const VIEW_STATUSES: readonly ["idle", "running", "waiting", "completed", "failed"];
268
+ export type ViewStatus = (typeof VIEW_STATUSES)[number];
269
+ export interface ViewStatusParams {
270
+ status: ViewStatus;
271
+ }
272
+ /** An empty object. */
273
+ export type ViewStatusResult = Record<string, never>;
274
+ /**
275
+ * Ask harbor to open THIS SAME page — the same app, the same server, the same
276
+ * `ui://` document — in a new window beside the one asking, booted with
277
+ * `view` as its saved view. That is the whole reach of the method: the params
278
+ * carry no app, no tool and no address, so a page can open only itself.
279
+ *
280
+ * `view` is a {@link HarborViewState}, held to {@link validateViewState}: the
281
+ * new page reads it back through `savedView` exactly as it would a view it
282
+ * reported itself, so it shows the item `view.item` names. `title` is what the
283
+ * new window is called until its page reports names of its own: a noun, held to
284
+ * {@link HARBOR_VIEW_NAME_LIMITS} like the names a page reports, and kept in
285
+ * memory only.
286
+ *
287
+ * The new window is the person's like any other: resized, arranged and closed
288
+ * by harbor, never by the page.
289
+ */
290
+ export interface ViewsOpenParams {
291
+ title: string;
292
+ view: HarborViewState;
293
+ }
294
+ /** An empty object: the new window is harbor's, and the page is told nothing about it. */
295
+ export type ViewsOpenResult = Record<string, never>;
296
+ /**
297
+ * The five facts a page reports about its own view, spelled ONCE for every
298
+ * page that has a view. Mail and the file browser say the same things under
299
+ * different nouns — a place and an item, how the list is narrowed, what was
300
+ * typed, how wide the panes are — and naming them twice is how the two drift.
301
+ *
302
+ * place a mail place id · a folder id
303
+ * item the open message id · the selected entry id
304
+ * filter Unread / Flagged · All / Folders / Documents
305
+ * query the search box text · the search box text
306
+ * panes list + places widths · details width
307
+ *
308
+ * Every field is optional: a page reports what it has. `panes` is the one
309
+ * bag, because harbor cannot know what a page calls its panes; its keys are
310
+ * validated by pattern and its values are finite numbers.
311
+ *
312
+ * This is the WHOLE view, never a patch — the slot it rides is
313
+ * last-write-wins, so an omitted field is an erased field.
314
+ */
315
+ export interface HarborViewState {
316
+ place?: string;
317
+ item?: string;
318
+ filter?: string;
319
+ query?: string;
320
+ panes?: Record<string, number>;
321
+ }
322
+ /** The caps a reported view is held to. Strings are capped; the bag is bounded both ways. */
323
+ export declare const HARBOR_VIEW_STATE_LIMITS: {
324
+ /** Longest a `place`, `item`, `filter` or `query` may be. */
325
+ readonly valueChars: 256;
326
+ /** Most entries `panes` may carry. */
327
+ readonly paneEntries: 8;
328
+ /** Longest a pane name may be. */
329
+ readonly paneNameChars: 32;
330
+ };
331
+ /** The view state's capped string fields, in one list: the validator's loop and the size a saved view is held to. */
332
+ export declare const HARBOR_VIEW_STATE_STRING_KEYS: readonly ["place", "item", "filter", "query"];
333
+ export type HarborViewStateVerdict = {
334
+ ok: true;
335
+ value: HarborViewState;
336
+ } | {
337
+ ok: false;
338
+ message: string;
339
+ };
340
+ /**
341
+ * Grade one reported view: unknown top-level keys are refused, strings are
342
+ * capped, and the pane bag is checked key by key. The schema IS the allowlist,
343
+ * so nothing undeclared survives.
344
+ */
345
+ export declare function validateViewState(value: unknown): HarborViewStateVerdict;
346
+ /**
347
+ * One step of the trail outside what a page has open: its name for the bar,
348
+ * and the page's own id for it. The id is opaque to harbor: it is handed back
349
+ * unchanged in a `crumb` bar input when the person presses the step, and the
350
+ * page resolves it against its own state.
351
+ */
352
+ export interface HarborTrailStep {
353
+ id: string;
354
+ name: string;
355
+ }
356
+ /**
357
+ * The nouns a page offers for a sentence about it: what it has open, where
358
+ * that sits, and the steps outside it. The person's own content, so they ride
359
+ * the CONTENT grant and the live rail — held in memory for as long as the view
360
+ * is mounted and written nowhere. The view state's `item` and `place` are the
361
+ * first two as ids, and those are what harbor remembers.
362
+ *
363
+ * Nouns, never clauses: the host spells every other word of the sentence
364
+ * these appear in.
365
+ */
366
+ export interface HarborViewNames {
367
+ /** What the page has open — a message's subject, a file's name. */
368
+ item?: string;
369
+ /** Where that sits — a mail place, a folder. */
370
+ place?: string;
371
+ /**
372
+ * Every step outside `item`, outermost first, each with an id the page
373
+ * answers to. Harbor draws them as the pressable part of its trail; a
374
+ * page that gives none has `place` drawn as text. A first step named for
375
+ * the application is its root, and makes the application's own crumb
376
+ * pressable.
377
+ */
378
+ trail?: HarborTrailStep[];
379
+ }
380
+ /** The key the nouns ride under `structuredContent`, beside the view state's own. */
381
+ export declare const HARBOR_VIEW_NAMES_ID = "ai.holokai/harbor/names";
382
+ /** The caps a noun is held to. Short: it goes in a sentence, not a document. */
383
+ export declare const HARBOR_VIEW_NAME_LIMITS: {
384
+ /** Longest an `item`, `place` or step name may be; a longer one is not kept. */
385
+ readonly nameChars: 120;
386
+ /** Most steps a trail may hold; past it, the first step and the innermost ones are kept. */
387
+ readonly trailSteps: 12;
388
+ /** Longest a step's id may be; a step with a longer one, or one holding a control character, is not kept. */
389
+ readonly stepIdChars: 256;
390
+ };
391
+ export type HarborViewNamesVerdict = {
392
+ ok: true;
393
+ value: HarborViewNames;
394
+ } | {
395
+ ok: false;
396
+ message: string;
397
+ };
398
+ /**
399
+ * Grade the nouns: unknown keys are refused and a wrong type is refused, but
400
+ * a name that is too long — or empty once its control characters are gone —
401
+ * is simply not kept, so one unusable noun never costs the report the other.
402
+ * A trail keeps its first steps up to the cap, and drops a step whose name or
403
+ * id is unusable; a trail with no usable step is not kept.
404
+ */
405
+ export declare function validateViewNames(value: unknown): HarborViewNamesVerdict;
406
+ /**
407
+ * The params of `ui/update-model-context`, the spec's own method for telling
408
+ * the host what the view is showing. Harbor accepts it under the two `view.*`
409
+ * grants: the `structuredContent` half carries the view state under
410
+ * {@link HARBOR_EXTENSION_ID} and the nouns under {@link HARBOR_VIEW_NAMES_ID},
411
+ * the `content` half carries text for the model. Harbor advertises the
412
+ * modalities it takes, so a page feature-detects rather than probing.
413
+ */
414
+ export declare const UPDATE_MODEL_CONTEXT_PARAMS_SCHEMA: JsonSchemaObject;
415
+ /** What harbor hands a booting page back under {@link HARBOR_EXTENSION_ID} in `hostContext`. */
416
+ export interface HarborHostContext {
417
+ /** The view harbor saved for this page, or null when it has none. */
418
+ viewState: HarborViewState | null;
419
+ }
420
+ /**
421
+ * Every public event, with the payload a view receives. Each is a projection
422
+ * of one harbor-internal event; the projection (which fields survive) lives
423
+ * in harbor, and the view only ever sees these shapes.
424
+ */
425
+ export interface HarborEventMap {
426
+ /** Harbor's liveness tick — diagnostic, ungated. */
427
+ 'harbor.heartbeat': {
428
+ count: number;
429
+ };
430
+ /** A run was created or changed. An INVALIDATION signal: re-read on a `seq` gap. */
431
+ 'run.changed': {
432
+ workflow: WorkflowSummary;
433
+ };
434
+ /** A task of a workflow THIS app submitted moved. Progress is numbers only. */
435
+ 'workflow.task': {
436
+ workflowId: string;
437
+ taskId: string;
438
+ state: WorkflowTaskState;
439
+ progress?: {
440
+ percent?: number;
441
+ outputTokens?: number;
442
+ reasoningTokens?: number;
443
+ };
444
+ };
445
+ /** A workflow this app submitted started or ended. On a terminal status, read it back. */
446
+ 'workflow.changed': {
447
+ workflowId: string;
448
+ status: WorkflowStatus;
449
+ };
450
+ /**
451
+ * The app's own server told its open views something, named by the server. Delivered as sent,
452
+ * not a projection of anything harbor holds; on a `seq` gap the view re-reads from its server.
453
+ */
454
+ 'server.notification': {
455
+ name: string;
456
+ params: Record<string, unknown>;
457
+ };
458
+ }
459
+ export type HarborEventName = keyof HarborEventMap;
460
+ /** The closed set, as data, for runtime checks. */
461
+ export declare const HARBOR_EVENT_NAMES: readonly HarborEventName[];
462
+ export interface EventsSubscribeParams {
463
+ events: HarborEventName[];
464
+ }
465
+ export interface EventsSubscribeResult {
466
+ subscriptionId: string;
467
+ events: HarborEventName[];
468
+ }
469
+ export interface EventsUnsubscribeParams {
470
+ subscriptionId: string;
471
+ }
472
+ /** An empty object — JSON-RPC results are objects, never bare scalars. */
473
+ export type EventsUnsubscribeResult = Record<string, never>;
474
+ /**
475
+ * The `harbor/notifications/event` params. `seq` is PER SUBSCRIPTION, starts
476
+ * at 1 and increments by one per delivered event; a skipped number means an
477
+ * event was lost and the view re-reads its snapshot. There is no replay.
478
+ */
479
+ export interface HarborEventNotificationParams<K extends HarborEventName = HarborEventName> {
480
+ subscriptionId: string;
481
+ seq: number;
482
+ event: K;
483
+ payload: HarborEventMap[K];
484
+ }
485
+ /** Method → its params and result, for typed clients and typed gates alike. */
486
+ export interface HarborMethodMap {
487
+ 'harbor/workflows/list': {
488
+ params: WorkflowsListParams;
489
+ result: WorkflowsListResult;
490
+ };
491
+ 'harbor/workflows/submit': {
492
+ params: WorkflowsSubmitParams;
493
+ result: WorkflowsSubmitResult;
494
+ };
495
+ 'harbor/workflows/get': {
496
+ params: WorkflowsGetParams;
497
+ result: WorkflowsGetResult;
498
+ };
499
+ 'harbor/capabilities/list': {
500
+ params: Record<string, never>;
501
+ result: CapabilitiesListResult;
502
+ };
503
+ 'harbor/events/subscribe': {
504
+ params: EventsSubscribeParams;
505
+ result: EventsSubscribeResult;
506
+ };
507
+ 'harbor/events/unsubscribe': {
508
+ params: EventsUnsubscribeParams;
509
+ result: EventsUnsubscribeResult;
510
+ };
511
+ 'harbor/files/open': {
512
+ params: FilesOpenParams;
513
+ result: FilesOpenResult;
514
+ };
515
+ 'harbor/files/add': {
516
+ params: FilesAddParams;
517
+ result: FilesAddResult;
518
+ };
519
+ 'harbor/files/link': {
520
+ params: FilesLinkParams;
521
+ result: FilesAddResult;
522
+ };
523
+ 'harbor/browser/open': {
524
+ params: BrowserOpenParams;
525
+ result: BrowserOpenResult;
526
+ };
527
+ 'harbor/bar/set': {
528
+ params: BarSetParams;
529
+ result: BarSetResult;
530
+ };
531
+ 'harbor/views/open': {
532
+ params: ViewsOpenParams;
533
+ result: ViewsOpenResult;
534
+ };
535
+ 'harbor/view/status': {
536
+ params: ViewStatusParams;
537
+ result: ViewStatusResult;
538
+ };
539
+ }
540
+ /**
541
+ * The JSON Schema subset the wire is validated against: top-level `type`,
542
+ * `properties`, `required`, and per-property `type` / `enum`. Undeclared
543
+ * fields are refused — the schema IS the allowlist. Nested shapes are checked
544
+ * by the handler, which is why `events` names its closed set again in
545
+ * `HARBOR_EVENT_NAMES`, and why the bar's caps (`BAR_LIMITS`) are enforced by
546
+ * harbor's gate and nowhere here.
547
+ */
548
+ export interface JsonSchemaObject {
549
+ type: 'object';
550
+ properties: Record<string, {
551
+ type: string;
552
+ enum?: readonly string[];
553
+ description?: string;
554
+ }>;
555
+ required?: readonly string[];
556
+ }
557
+ export declare const HARBOR_METHOD_PARAMS_SCHEMA: Record<HarborMethod, JsonSchemaObject>;
558
+ /**
559
+ * The closed grant vocabulary. Content (a run's title) and metadata (its
560
+ * status, timestamps) are DIFFERENT grants from day one, and a reported view
561
+ * splits the same way: `view.state` lets harbor remember the structured view
562
+ * and hand it back, `view.content` lets what a page says it is showing — its
563
+ * words and the names of what it has open — reach the window's report.
564
+ * `bar.set` lets a page hand its search field and account menu to the window
565
+ * bar, and receive what the person types or chooses there. `views.open` lets
566
+ * a page open itself again in a new window beside its own. `files.add` lets a
567
+ * page opened to pick files add one to the workspace it was opened for, or
568
+ * link a folder to it. `view.state` also admits `harbor/view/status`: the
569
+ * standing a page gives its own tray button is part of the view harbor keeps
570
+ * for it, and carries none of the page's words.
571
+ */
572
+ export type HarborGrant = 'workflows.read' | 'workflows.content' | 'workflows.submit' | 'capabilities.read' | 'server.tools' | 'server.resources' | 'events.runs' | 'events.diagnostic' | 'files.open' | 'files.add' | 'browser.open' | 'view.state' | 'view.content' | 'bar.set' | 'views.open';
573
+ /**
574
+ * The grants an extension must declare in its manifest to be given: harbor
575
+ * gives one to no extension that does not name it, whatever else it is given.
576
+ */
577
+ export type DeclaredGrant = Extract<HarborGrant, 'files.add'>;
578
+ /**
579
+ * Every method and every event, tied to its grant — exhaustive, so a new
580
+ * name cannot ship ungated. `by-params` is an explicit row, not an omission:
581
+ * the method itself is open, and the grant check runs against the names its
582
+ * params carry (a subscribe is gated per event).
583
+ */
584
+ export declare const HARBOR_GRANT_OF: Record<HarborMethod | HarborEventName, HarborGrant | 'by-params'>;
585
+ /**
586
+ * The grants behind the STANDARD MCP Apps methods harbor answers, which are
587
+ * not `harbor/*` names and so live outside `HARBOR_GRANT_OF`'s key set.
588
+ * Spelled here so the vocabulary stays one table.
589
+ *
590
+ * A LIST means any one of them admits the method, and each half of its params
591
+ * is then gated on its own: withholding one grant leaves the other half
592
+ * working, and withholding both makes the method absent like any other
593
+ * ungranted one.
594
+ */
595
+ export declare const STANDARD_METHOD_GRANT_OF: {
596
+ readonly 'tools/call': "server.tools";
597
+ readonly 'resources/list': "server.resources";
598
+ readonly 'resources/read': "server.resources";
599
+ readonly 'ui/update-model-context': readonly ["view.state", "view.content"];
600
+ };
601
+ /** The grant a field inside a result needs, beyond the method's own. */
602
+ export declare const WORKFLOW_TITLE_GRANT: HarborGrant;
603
+ /**
604
+ * What harbor advertises under `hostCapabilities.experimental[HARBOR_EXTENSION_ID]`
605
+ * in the `ui/initialize` result: ONLY what this view is granted. The list is
606
+ * therefore the permission disclosure too, and a client feature-detects per
607
+ * method. An ungranted method answers `-32601` like an absent one.
608
+ */
609
+ export interface HarborHostExtension {
610
+ version: string;
611
+ methods: HarborMethod[];
612
+ events: HarborEventName[];
613
+ }
@@ -0,0 +1,51 @@
1
+ import {createRequire as __harborCreateRequire} from 'node:module'; const require = __harborCreateRequire(import.meta.url);
2
+ import {
3
+ BAR_LIMITS,
4
+ HARBOR_BAR_NOTIFICATION,
5
+ HARBOR_EVENT_NAMES,
6
+ HARBOR_EVENT_NOTIFICATION,
7
+ HARBOR_EXTENSION_ID,
8
+ HARBOR_EXTENSION_VERSION,
9
+ HARBOR_GRANT_OF,
10
+ HARBOR_METHODS,
11
+ HARBOR_METHOD_PARAMS_SCHEMA,
12
+ HARBOR_PAGE_READER,
13
+ HARBOR_VIEW_NAMES_ID,
14
+ HARBOR_VIEW_NAME_LIMITS,
15
+ HARBOR_VIEW_STATE_LIMITS,
16
+ HARBOR_VIEW_STATE_STRING_KEYS,
17
+ MCP_APPS_PROTOCOL_VERSION,
18
+ STANDARD_METHOD_GRANT_OF,
19
+ UPDATE_MODEL_CONTEXT_PARAMS_SCHEMA,
20
+ VIEW_STATUSES,
21
+ WORKFLOWS_LIST_LIMIT,
22
+ WORKFLOW_TASKS_MAX,
23
+ WORKFLOW_TITLE_GRANT,
24
+ validateViewNames,
25
+ validateViewState
26
+ } from "./chunks/chunk-G4KJ2OCU.js";
27
+ export {
28
+ BAR_LIMITS,
29
+ HARBOR_BAR_NOTIFICATION,
30
+ HARBOR_EVENT_NAMES,
31
+ HARBOR_EVENT_NOTIFICATION,
32
+ HARBOR_EXTENSION_ID,
33
+ HARBOR_EXTENSION_VERSION,
34
+ HARBOR_GRANT_OF,
35
+ HARBOR_METHODS,
36
+ HARBOR_METHOD_PARAMS_SCHEMA,
37
+ HARBOR_PAGE_READER,
38
+ HARBOR_VIEW_NAMES_ID,
39
+ HARBOR_VIEW_NAME_LIMITS,
40
+ HARBOR_VIEW_STATE_LIMITS,
41
+ HARBOR_VIEW_STATE_STRING_KEYS,
42
+ MCP_APPS_PROTOCOL_VERSION,
43
+ STANDARD_METHOD_GRANT_OF,
44
+ UPDATE_MODEL_CONTEXT_PARAMS_SCHEMA,
45
+ VIEW_STATUSES,
46
+ WORKFLOWS_LIST_LIMIT,
47
+ WORKFLOW_TASKS_MAX,
48
+ WORKFLOW_TITLE_GRANT,
49
+ validateViewNames,
50
+ validateViewState
51
+ };
@@ -0,0 +1,5 @@
1
+ /**
2
+ * The Holokai logo, the holokai.ai website's PNG (566 by 566 pixels), as a data URI, so a page
3
+ * shows it with no file beside it and no request.
4
+ */
5
+ export declare const HOLOKAI_LOGO_PNG = "data:image/png;base64,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";