@kehto/shell 0.25.0 → 0.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -305,6 +305,10 @@ for outer URL encoding, review, routing, and verified launcher behavior.
305
305
 
306
306
  This non-normative guidance defers to the living [NAP-GAMEPAD](https://github.com/napplet/naps/pull/108). Services export `createGamepadService` and `browserGamepadOptions`; shell exports `denyNappletControllerAccess`. Paja and the playground register one host reader and deny native gamepad/HID/USB paths before frame execution. Source-bound subscriptions receive immediate full snapshots; only the visible host's input owner gets live controls. Focus loss supplies neutral controls and timestamp zero; unsubscribe, window destruction, and service removal release polling. Non-browser or denied native readers report the proposal's unavailable reasons. See [Controller input](https://github.com/kehto/web/blob/main/docs/gamepad.md).
307
307
 
308
+ ## LLM integration
309
+
310
+ This non-normative guidance defers to the living [NAP-LLM](https://github.com/napplet/naps/pull/134) (checked at `3e10939`). Services export `createLlmService`, `LlmServiceError`, the provider mappers and `LLM_PROVIDER_PRESETS`; shell exports `getShellDomainState` and the `LlmHooks` adapter field; acl exports `llm:generate`. The runtime routes `llm.*` to one registered service and answers every sanctioned request with exactly one `llm.<op>.result`, shaping missing-service and ACL failures as `{ code: 'unsupported' }`. Hosts implement `LlmConnectionHost`; the service enforces schema, handle ownership, consent before submission, duplicate ids, cancellation and deadline races, ordered deltas and safe errors. Advertisement stays binary on the wire while hosts report a third `unconfigured` state until the user adds a connection. See [Language models](https://github.com/kehto/web/blob/main/docs/llm.md).
311
+
308
312
  ## UUID support on HTTP origins
309
313
 
310
314
  Shell event injection, cache queries, and source-session registration use native
package/dist/index.d.ts CHANGED
@@ -243,6 +243,30 @@ interface WebrtcHooks {
243
243
  /** True when the host has shell-mediated WebRTC sessions wired. */
244
244
  isAvailable(): boolean;
245
245
  }
246
+ /**
247
+ * Host wiring behind the NAP-LLM `llm` domain.
248
+ *
249
+ * NAP-LLM support is not binary: a host may have the runtime and service wired
250
+ * while the user has not yet configured any model connection. `isAvailable()`
251
+ * decides whether the domain is advertised at all; `isConfigured()` reports
252
+ * whether a connection exists so host UIs can show an "unconfigured" state and
253
+ * prompt for setup when a napplet first calls `llm.select`.
254
+ */
255
+ interface LlmHooks {
256
+ /** True when the host can route `llm.*` to a registered service. */
257
+ isAvailable(): boolean;
258
+ /** True when at least one model connection is configured. Omitted means configured. */
259
+ isConfigured?(): boolean;
260
+ }
261
+ /**
262
+ * Host-facing advertisement state of one NAP domain.
263
+ *
264
+ * - `supported`: advertised to napplets and ready to serve requests.
265
+ * - `unconfigured`: advertised, but the host still needs user configuration
266
+ * (for example an LLM connection); the first request prompts for setup.
267
+ * - `disabled`: not advertised; the host disabled it or its backend is not live.
268
+ */
269
+ type ShellDomainState = 'supported' | 'unconfigured' | 'disabled';
246
270
  /**
247
271
  * Optional host override for the static shell.init capability handshake.
248
272
  *
@@ -417,6 +441,12 @@ interface ShellAdapter {
417
441
  * registers the concrete service via `registerService('webrtc')`.
418
442
  */
419
443
  webrtc?: WebrtcHooks;
444
+ /**
445
+ * Optional NAP-LLM wiring state. When omitted, a registered `llm` service is
446
+ * advertised as supported; when present, `isAvailable()` gates advertisement
447
+ * and `isConfigured()` feeds the host's unconfigured indicator.
448
+ */
449
+ llm?: LlmHooks;
420
450
  /**
421
451
  * Optional capability advertisement override. Omitted by production hosts.
422
452
  */
@@ -982,6 +1012,24 @@ declare function buildShellCapabilities(hooks: ShellAdapter): ShellCapabilities;
982
1012
  * @returns A fresh immutable environment whose entries are exact live subsets.
983
1013
  */
984
1014
  declare function resolveShellEnvironment(hooks: ShellAdapter, identity: OriginIdentity): ShellEnvironment;
1015
+ /**
1016
+ * Report the host-facing advertisement state of one bare NAP domain.
1017
+ *
1018
+ * Advertisement itself stays binary on the wire (NIP-5D domain presence), but a
1019
+ * host UI needs the third state: the domain is advertised while the user has not
1020
+ * configured its backend yet. Only hooks that expose `isConfigured()` (today
1021
+ * `llm`) can report `unconfigured`; every other advertised domain is
1022
+ * `supported`, and anything not advertised is `disabled`.
1023
+ *
1024
+ * @param hooks - The ShellAdapter provided by the host app.
1025
+ * @param domain - Bare NAP domain name such as `llm`.
1026
+ * @returns `supported`, `unconfigured` or `disabled`.
1027
+ * @example
1028
+ * ```ts
1029
+ * getShellDomainState(hooks, 'llm'); // 'unconfigured' until a connection is saved
1030
+ * ```
1031
+ */
1032
+ declare function getShellDomainState(hooks: ShellAdapter, domain: string): ShellDomainState;
985
1033
 
986
1034
  /**
987
1035
  * identity-proxy.ts — Shell-side per-domain proxy for identity.* envelopes.
@@ -1568,4 +1616,4 @@ type ResourceInbound = ResourceInfoRequest | ResourceBytesRequest | ResourceByte
1568
1616
  */
1569
1617
  type ResourceOutbound = ResourceInfoResult | ResourceInfoError | ResourceBytesResult | ResourceBytesError | ResourceBytesManyResult | ResourceBytesManyError;
1570
1618
 
1571
- export { type AclCheckEvent, type AclEntry, type AclStore, type AudioManager, type AudioSource, type AuthHooks, type BleHooks, type BrowserDeps, type CapabilityHooks, type CommonHooks, type ConfigHooks, type CryptoHooks, type DmHooks, type HotkeyHooks, type IdentityProxy, type IdentityProxyDeps, type IntentHooks, type KeysProxy, type KeysProxyDeps, type LinkHooks, type ListsHooks, type ManifestCache, type ManifestCacheEntry, type MediaProxy, type MediaProxyDeps, type NappletCspDirective, type NappletCspDirectives, type NappletCspOptions, type NappletNamespacePreludeOptions, type NappletSrcdocOptions, type NormalizedIntentUri, type NotifyProxy, type NotifyProxyDeps, type OriginIdentity, type OriginRegistry, type ProxyOriginRegistry, type RelayConfigHooks, type RelayPoolHooks, type RelayPoolLike, type ResourceBytesError, type ResourceBytesManyError, type ResourceBytesManyItem, type ResourceBytesManyRequest, type ResourceBytesManyResult, type ResourceBytesRequest, type ResourceBytesResult, type ResourceCancelRequest, type ResourceErrorCode, type ResourceInbound, type ResourceInfoError, type ResourceInfoRequest, type ResourceInfoResult, type ResourceOutbound, type ResourceRequestId, type SerialHooks, type SessionRegistry, type ShellAdapter, type ShellBridge, type ShellCapabilities, type ShellEnvironment, type ThemeProxy, type ThemeProxyDeps, type UnroutedMessageInfo, type UploadBackendLike, type UploadHooks, type WebrtcHooks, type WindowManagerHooks, type WorkerRelayHooks, type WorkerRelayLike, adaptHooks, audioManager, buildNappletCsp, buildShellCapabilities, createIdentityProxy, createKeysProxy, createMediaProxy, createNotifyProxy, createShellBridge, createThemeProxy, denyNappletControllerAccess, injectNappletCsp, injectNappletNamespacePrelude, manifestCache, nappKeyRegistry, normalizeIntentUri, originRegistry, prepareNappletSrcdoc, renderNappletCspMeta, renderNappletNamespacePrelude, resolveShellEnvironment, sessionRegistry };
1619
+ export { type AclCheckEvent, type AclEntry, type AclStore, type AudioManager, type AudioSource, type AuthHooks, type BleHooks, type BrowserDeps, type CapabilityHooks, type CommonHooks, type ConfigHooks, type CryptoHooks, type DmHooks, type HotkeyHooks, type IdentityProxy, type IdentityProxyDeps, type IntentHooks, type KeysProxy, type KeysProxyDeps, type LinkHooks, type ListsHooks, type LlmHooks, type ManifestCache, type ManifestCacheEntry, type MediaProxy, type MediaProxyDeps, type NappletCspDirective, type NappletCspDirectives, type NappletCspOptions, type NappletNamespacePreludeOptions, type NappletSrcdocOptions, type NormalizedIntentUri, type NotifyProxy, type NotifyProxyDeps, type OriginIdentity, type OriginRegistry, type ProxyOriginRegistry, type RelayConfigHooks, type RelayPoolHooks, type RelayPoolLike, type ResourceBytesError, type ResourceBytesManyError, type ResourceBytesManyItem, type ResourceBytesManyRequest, type ResourceBytesManyResult, type ResourceBytesRequest, type ResourceBytesResult, type ResourceCancelRequest, type ResourceErrorCode, type ResourceInbound, type ResourceInfoError, type ResourceInfoRequest, type ResourceInfoResult, type ResourceOutbound, type ResourceRequestId, type SerialHooks, type SessionRegistry, type ShellAdapter, type ShellBridge, type ShellCapabilities, type ShellDomainState, type ShellEnvironment, type ThemeProxy, type ThemeProxyDeps, type UnroutedMessageInfo, type UploadBackendLike, type UploadHooks, type WebrtcHooks, type WindowManagerHooks, type WorkerRelayHooks, type WorkerRelayLike, adaptHooks, audioManager, buildNappletCsp, buildShellCapabilities, createIdentityProxy, createKeysProxy, createMediaProxy, createNotifyProxy, createShellBridge, createThemeProxy, denyNappletControllerAccess, getShellDomainState, injectNappletCsp, injectNappletNamespacePrelude, manifestCache, nappKeyRegistry, normalizeIntentUri, originRegistry, prepareNappletSrcdoc, renderNappletCspMeta, renderNappletNamespacePrelude, resolveShellEnvironment, sessionRegistry };
package/dist/index.js CHANGED
@@ -1013,7 +1013,8 @@ var SERVICE_ONLY_DOMAINS = [
1013
1013
  "dm",
1014
1014
  "count",
1015
1015
  "fs",
1016
- "gamepad"
1016
+ "gamepad",
1017
+ "llm"
1017
1018
  ];
1018
1019
  function buildShellCapabilities(hooks) {
1019
1020
  const disabled = new Set(hooks.capabilities?.disabledDomains ?? []);
@@ -1060,10 +1061,18 @@ function isServiceDomainLive(hooks, domain) {
1060
1061
  return hooks.ble?.isAvailable() === true;
1061
1062
  case "webrtc":
1062
1063
  return hooks.webrtc?.isAvailable() === true;
1064
+ case "llm":
1065
+ return hooks.llm ? hooks.llm.isAvailable() : true;
1063
1066
  default:
1064
1067
  return true;
1065
1068
  }
1066
1069
  }
1070
+ function getShellDomainState(hooks, domain) {
1071
+ if (!buildShellCapabilities(hooks).domains.includes(domain)) return "disabled";
1072
+ const configurable = Reflect.get(hooks, domain);
1073
+ const isConfigured = configurable && typeof configurable === "object" ? configurable.isConfigured : void 0;
1074
+ return typeof isConfigured === "function" && !isConfigured.call(configurable) ? "unconfigured" : "supported";
1075
+ }
1067
1076
  function getLiveServices(hooks, domains, disabled) {
1068
1077
  const liveDomains = new Set(domains);
1069
1078
  return Object.keys(hooks.services ?? {}).filter((name) => {
@@ -2907,6 +2916,57 @@ function nappletNamespacePrelude(domains, normalizeIntentUri2) {
2907
2916
  onChanged: (callback) => domainEventHandle("fs.changed", callback, (msg) => msg.change)
2908
2917
  };
2909
2918
  }
2919
+ function makeLlm() {
2920
+ const asFailure = (value) => {
2921
+ if (!value || typeof value !== "object") return null;
2922
+ const { code, message } = value;
2923
+ return typeof code === "string" && typeof message === "string" ? { code, message } : null;
2924
+ };
2925
+ const failure = (entry) => Object.assign(new Error(entry.message), { code: entry.code });
2926
+ function call(type, payload, field, options = {}) {
2927
+ const requestId = id();
2928
+ const promise = new Promise((resolve, reject) => {
2929
+ let nextSequence = 0;
2930
+ const onAbort = () => {
2931
+ post({ type: "llm.cancel", id: id(), requestId });
2932
+ };
2933
+ const off = listen((event) => {
2934
+ if (!isParentMessage(event)) return;
2935
+ const msg = event.data;
2936
+ if (typeof msg !== "object" || msg === null) return;
2937
+ if (type === "llm.generate" && msg.type === "llm.delta" && msg.requestId === requestId) {
2938
+ if (msg.sequence !== nextSequence || typeof msg.text !== "string") return;
2939
+ nextSequence += 1;
2940
+ options.onDelta?.({ requestId, sequence: msg.sequence, text: msg.text });
2941
+ return;
2942
+ }
2943
+ if (msg.type !== `${type}.result` || msg.id !== requestId) return;
2944
+ off();
2945
+ options.signal?.removeEventListener("abort", onAbort);
2946
+ const error = asFailure(msg.error);
2947
+ if (error) {
2948
+ reject(failure(error));
2949
+ return;
2950
+ }
2951
+ if (!Object.prototype.hasOwnProperty.call(msg, field)) {
2952
+ reject(failure({ code: "provider-error", message: `${type}.result missing ${field}` }));
2953
+ return;
2954
+ }
2955
+ resolve(msg[field]);
2956
+ });
2957
+ post({ type, id: requestId, ...payload });
2958
+ if (options.signal?.aborted) onAbort();
2959
+ else options.signal?.addEventListener("abort", onAbort, { once: true });
2960
+ });
2961
+ return Object.assign(promise, { requestId });
2962
+ }
2963
+ return {
2964
+ select: (requirements) => call("llm.select", requirements === void 0 ? {} : { requirements }, "model"),
2965
+ describe: (handle) => call("llm.describe", { handle }, "model"),
2966
+ generate: (request2, options) => call("llm.generate", { request: request2 }, "result", options ?? {}),
2967
+ cancel: (requestId) => call("llm.cancel", { requestId }, "cancelled")
2968
+ };
2969
+ }
2910
2970
  const intentDelivery = allowed.has("intent") ? makeIntentDelivery() : void 0;
2911
2971
  const shell = allowed.has("shell") ? makeShell() : void 0;
2912
2972
  let gamepad;
@@ -2981,6 +3041,8 @@ function nappletNamespacePrelude(domains, normalizeIntentUri2) {
2981
3041
  return makeDm();
2982
3042
  case "fs":
2983
3043
  return makeFs();
3044
+ case "llm":
3045
+ return makeLlm();
2984
3046
  default:
2985
3047
  return {};
2986
3048
  }
@@ -3251,6 +3313,7 @@ export {
3251
3313
  createThemeProxy,
3252
3314
  denyNappletControllerAccess,
3253
3315
  formatDenialReason,
3316
+ getShellDomainState,
3254
3317
  injectNappletCsp,
3255
3318
  injectNappletNamespacePrelude,
3256
3319
  manifestCache,