@hosty-sdk/app 0.7.0 → 0.8.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.
@@ -37,6 +37,21 @@ export interface DelegatedTokenRequestIntent {
37
37
  * is.
38
38
  */
39
39
  export declare function parseActiveFrameDelegatedTokenRequest(event: EmbedderMessage, activeFrameWindow: unknown, activeFrameUrl: string): DelegatedTokenRequestIntent | null;
40
+ /** A verified ask, and the one thing its payload carries. */
41
+ export interface AskAssistantIntent {
42
+ /** Trimmed and capped here, so no embedder has to remember to do it. */
43
+ text: string;
44
+ }
45
+ /**
46
+ * Verifies that a `message` event is an ask-the-assistant intent from the active app frame, using
47
+ * the same sender checks as the pair above.
48
+ *
49
+ * The text is capped in the parser rather than by the caller: a cap is part of the contract, and an
50
+ * embedder that forgot it would let one app's page paste itself into the operator's draft. What the
51
+ * embedder must still decide is *where* the text goes — this function says an app asked, never that
52
+ * the assistant should send anything. Nothing here may be auto-sent.
53
+ */
54
+ export declare function parseActiveFrameAskAssistant(event: EmbedderMessage, activeFrameWindow: unknown, activeFrameUrl: string): AskAssistantIntent | null;
40
55
  /**
41
56
  * Per-app reissue throttle: a frame that keeps re-posting (e.g. it never accepts the new
42
57
  * code) must not drive an unbounded reissue storm. One reissue per app per interval is
package/dist/embedder.js CHANGED
@@ -12,7 +12,7 @@
12
12
  // you have decided to grant one, mint a token from Core with your own session and post it back
13
13
  // to that frame's origin. Both requests are unauthenticated by design; what makes them safe is
14
14
  // that the embedder answers from facts about its own DOM rather than from what the frame claims.
15
- import { AUTH_REQUIRED_INTENT_TYPE, DELEGATED_TOKEN_REQUEST_TYPE } from "./index.js";
15
+ import { ASK_ASSISTANT_MAX_CHARS, ASK_ASSISTANT_TYPE, AUTH_REQUIRED_INTENT_TYPE, DELEGATED_TOKEN_REQUEST_TYPE, } from "./index.js";
16
16
  /**
17
17
  * Verifies that a `message` event is a genuine auth-required intent from the active app
18
18
  * frame. Every check is about facts only the embedder can know:
@@ -57,6 +57,26 @@ export function parseActiveFrameDelegatedTokenRequest(event, activeFrameWindow,
57
57
  }
58
58
  return { refresh: candidate.refresh === true };
59
59
  }
60
+ /**
61
+ * Verifies that a `message` event is an ask-the-assistant intent from the active app frame, using
62
+ * the same sender checks as the pair above.
63
+ *
64
+ * The text is capped in the parser rather than by the caller: a cap is part of the contract, and an
65
+ * embedder that forgot it would let one app's page paste itself into the operator's draft. What the
66
+ * embedder must still decide is *where* the text goes — this function says an app asked, never that
67
+ * the assistant should send anything. Nothing here may be auto-sent.
68
+ */
69
+ export function parseActiveFrameAskAssistant(event, activeFrameWindow, activeFrameUrl) {
70
+ if (!isActiveFrameMessage(event, activeFrameWindow, activeFrameUrl)) {
71
+ return null;
72
+ }
73
+ const candidate = event.data;
74
+ if (candidate.type !== ASK_ASSISTANT_TYPE || typeof candidate.text !== "string") {
75
+ return null;
76
+ }
77
+ const text = candidate.text.trim().slice(0, ASK_ASSISTANT_MAX_CHARS);
78
+ return text ? { text } : null;
79
+ }
60
80
  /** The sender half both parsers share: right frame, right origin, object payload. */
61
81
  function isActiveFrameMessage(event, activeFrameWindow, activeFrameUrl) {
62
82
  if (!activeFrameWindow || event.source !== activeFrameWindow) {
package/dist/index.d.ts CHANGED
@@ -21,6 +21,34 @@ export declare function createAuthRequiredIntent(appId: string): AuthRequiredInt
21
21
  * token is a user-scoped credential, so an embedder answers only for apps it deliberately grants
22
22
  * one (Hosty Shell: the assistant gateway, which it already mints tokens for).
23
23
  */
24
+ /**
25
+ * An embedded app asking its embedder to put text in the assistant's draft.
26
+ *
27
+ * The embedder reveals its assistant surface and forwards the text there; the assistant fills the
28
+ * **draft** and stops. Nothing is sent. That is the contract, not a UX choice: app-provided text
29
+ * entering a model that holds host shell is the same trust boundary that keeps MCP providers off by
30
+ * default, and an error message is exactly the shape a prompt injection arrives in. The operator
31
+ * reads it, with its source shown, and decides.
32
+ *
33
+ * Plain text only, and no reply. A structured payload would invite apps to smuggle shape the
34
+ * operator cannot read at a glance, and an answer would turn a hand-off into a channel.
35
+ */
36
+ export declare const ASK_ASSISTANT_TYPE = "hosty:ask-assistant";
37
+ /** Longer than a prompt fragment is a page dumping itself into someone's draft. */
38
+ export declare const ASK_ASSISTANT_MAX_CHARS = 4000;
39
+ export interface AskAssistantMessage {
40
+ type: typeof ASK_ASSISTANT_TYPE;
41
+ /** Trimmed and capped by the embedder's parser; the app need not pre-truncate. */
42
+ text: string;
43
+ }
44
+ /**
45
+ * Asks the embedder to put `text` in the operator's assistant draft.
46
+ *
47
+ * Safe to call unconditionally: standalone there is no embedder to hear it, and an embedder that
48
+ * does not offer an assistant simply ignores it. Returns whether the message could be posted at
49
+ * all — never whether anyone acted on it, which is the operator's business and not the app's.
50
+ */
51
+ export declare function askAssistant(text: string): boolean;
24
52
  export declare const DELEGATED_TOKEN_REQUEST_TYPE = "hosty:request-delegated-token";
25
53
  export declare const DELEGATED_TOKEN_TYPE = "hosty:delegated-token";
26
54
  export interface DelegatedTokenRequest {
package/dist/index.js CHANGED
@@ -24,6 +24,46 @@ export function createAuthRequiredIntent(appId) {
24
24
  * token is a user-scoped credential, so an embedder answers only for apps it deliberately grants
25
25
  * one (Hosty Shell: the assistant gateway, which it already mints tokens for).
26
26
  */
27
+ /**
28
+ * An embedded app asking its embedder to put text in the assistant's draft.
29
+ *
30
+ * The embedder reveals its assistant surface and forwards the text there; the assistant fills the
31
+ * **draft** and stops. Nothing is sent. That is the contract, not a UX choice: app-provided text
32
+ * entering a model that holds host shell is the same trust boundary that keeps MCP providers off by
33
+ * default, and an error message is exactly the shape a prompt injection arrives in. The operator
34
+ * reads it, with its source shown, and decides.
35
+ *
36
+ * Plain text only, and no reply. A structured payload would invite apps to smuggle shape the
37
+ * operator cannot read at a glance, and an answer would turn a hand-off into a channel.
38
+ */
39
+ export const ASK_ASSISTANT_TYPE = "hosty:ask-assistant";
40
+ /** Longer than a prompt fragment is a page dumping itself into someone's draft. */
41
+ export const ASK_ASSISTANT_MAX_CHARS = 4000;
42
+ /**
43
+ * Asks the embedder to put `text` in the operator's assistant draft.
44
+ *
45
+ * Safe to call unconditionally: standalone there is no embedder to hear it, and an embedder that
46
+ * does not offer an assistant simply ignores it. Returns whether the message could be posted at
47
+ * all — never whether anyone acted on it, which is the operator's business and not the app's.
48
+ */
49
+ export function askAssistant(text) {
50
+ if (typeof window === "undefined" || window.parent === window) {
51
+ return false;
52
+ }
53
+ const trimmed = text.trim();
54
+ if (!trimmed) {
55
+ return false;
56
+ }
57
+ // Broadcast: the message carries no secret, and the embedder verifies the sender against its own
58
+ // DOM rather than trusting anything claimed here.
59
+ //
60
+ // Capped on the way out too. The parser remains where the contract is enforced — an app is not
61
+ // trusted to have done this — but there is no reason to structure-clone a megabyte across the
62
+ // boundary just to have the embedder discard it.
63
+ const capped = trimmed.slice(0, ASK_ASSISTANT_MAX_CHARS);
64
+ window.parent.postMessage({ type: ASK_ASSISTANT_TYPE, text: capped }, "*");
65
+ return true;
66
+ }
27
67
  export const DELEGATED_TOKEN_REQUEST_TYPE = "hosty:request-delegated-token";
28
68
  export const DELEGATED_TOKEN_TYPE = "hosty:delegated-token";
29
69
  export function createDelegatedTokenRequest(options = {}) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hosty-sdk/app",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "description": "Hosty app auth: session state machine, recovery bridge, Core revalidation, and the embedder responder.",
5
5
  "license": "AGPL-3.0-only",
6
6
  "type": "module",