@noodleseed/assistant 1.28.0 → 1.30.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.
Files changed (42) hide show
  1. package/dist/{app-view-element-mXHvcAXc.d.ts → app-view-element-kR8eqhyD.d.cts} +1 -1
  2. package/dist/{app-view-element-BjRkwNN1.d.cts → app-view-element-nY1svwHT.d.ts} +1 -1
  3. package/dist/app-view.d.cts +3 -3
  4. package/dist/app-view.d.ts +3 -3
  5. package/dist/{chunk-WJV4MJUR.js → chunk-5EVZFPTD.js} +120 -10
  6. package/dist/chunk-5EVZFPTD.js.map +1 -0
  7. package/dist/{chunk-QH7PL3GU.js → chunk-T4PS6P2W.js} +259 -54
  8. package/dist/chunk-T4PS6P2W.js.map +1 -0
  9. package/dist/{client-CPFw8tfo.d.ts → client-contract-Bw9dtrhw.d.cts} +26 -35
  10. package/dist/{client-_xivfs4q.d.cts → client-contract-DjU9qsoP.d.ts} +26 -35
  11. package/dist/client.cjs +122 -9
  12. package/dist/client.cjs.map +1 -1
  13. package/dist/client.d.cts +12 -2
  14. package/dist/client.d.ts +12 -2
  15. package/dist/client.js +1 -1
  16. package/dist/embed.global.js +46 -46
  17. package/dist/index.cjs +376 -61
  18. package/dist/index.cjs.map +1 -1
  19. package/dist/index.d.cts +3 -3
  20. package/dist/index.d.ts +3 -3
  21. package/dist/index.js +2 -2
  22. package/dist/react/client.cjs +122 -9
  23. package/dist/react/client.cjs.map +1 -1
  24. package/dist/react/client.d.cts +2 -2
  25. package/dist/react/client.d.ts +2 -2
  26. package/dist/react/client.js +1 -1
  27. package/dist/react.cjs +376 -61
  28. package/dist/react.cjs.map +1 -1
  29. package/dist/react.d.cts +3 -3
  30. package/dist/react.d.ts +3 -3
  31. package/dist/react.js +2 -2
  32. package/dist/server-CMqlAnxs.d.cts +270 -0
  33. package/dist/server-CMqlAnxs.d.ts +270 -0
  34. package/dist/server.cjs.map +1 -1
  35. package/dist/server.d.cts +1 -121
  36. package/dist/server.d.ts +1 -121
  37. package/dist/server.js.map +1 -1
  38. package/package.json +1 -1
  39. package/dist/appearance-BpVu3lpk.d.cts +0 -146
  40. package/dist/appearance-BpVu3lpk.d.ts +0 -146
  41. package/dist/chunk-QH7PL3GU.js.map +0 -1
  42. package/dist/chunk-WJV4MJUR.js.map +0 -1
package/dist/index.d.cts CHANGED
@@ -1,7 +1,7 @@
1
- import { f as AssistantAppearance, c as AssistantContext, g as AssistantModelContextUpdate, b as AssistantPageContext, j as AssistantInteractionResponse } from './client-_xivfs4q.cjs';
2
- export { k as AssistantAppearanceTheme, i as AssistantAppearanceWarning, e as AssistantErrorDetail, l as AssistantEvent, m as AssistantSurfaceAppearance, a as AssistantViewAvailableDetail, n as AssistantViewAvailableEvent } from './client-_xivfs4q.cjs';
3
- import './appearance-BpVu3lpk.cjs';
1
+ import { Q as AssistantAppearance, c as AssistantContext, y as AssistantModelContextUpdate, b as AssistantPageContext, v as AssistantInteractionResponse } from './client-contract-Bw9dtrhw.cjs';
2
+ export { S as AssistantAppearanceTheme, R as AssistantAppearanceWarning, e as AssistantErrorDetail, T as AssistantEvent, U as AssistantSurfaceAppearance, a as AssistantViewAvailableDetail, O as AssistantViewAvailableEvent } from './client-contract-Bw9dtrhw.cjs';
4
3
  import 'ai';
4
+ import './server-CMqlAnxs.cjs';
5
5
 
6
6
  declare const ASSISTANT_TAG_NAME = "noodle-assistant";
7
7
  declare const HTMLElementBase: typeof HTMLElement;
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
- import { f as AssistantAppearance, c as AssistantContext, g as AssistantModelContextUpdate, b as AssistantPageContext, j as AssistantInteractionResponse } from './client-CPFw8tfo.js';
2
- export { k as AssistantAppearanceTheme, i as AssistantAppearanceWarning, e as AssistantErrorDetail, l as AssistantEvent, m as AssistantSurfaceAppearance, a as AssistantViewAvailableDetail, n as AssistantViewAvailableEvent } from './client-CPFw8tfo.js';
3
- import './appearance-BpVu3lpk.js';
1
+ import { Q as AssistantAppearance, c as AssistantContext, y as AssistantModelContextUpdate, b as AssistantPageContext, v as AssistantInteractionResponse } from './client-contract-DjU9qsoP.js';
2
+ export { S as AssistantAppearanceTheme, R as AssistantAppearanceWarning, e as AssistantErrorDetail, T as AssistantEvent, U as AssistantSurfaceAppearance, a as AssistantViewAvailableDetail, O as AssistantViewAvailableEvent } from './client-contract-DjU9qsoP.js';
4
3
  import 'ai';
4
+ import './server-CMqlAnxs.js';
5
5
 
6
6
  declare const ASSISTANT_TAG_NAME = "noodle-assistant";
7
7
  declare const HTMLElementBase: typeof HTMLElement;
package/dist/index.js CHANGED
@@ -2,9 +2,9 @@ import {
2
2
  ASSISTANT_TAG_NAME,
3
3
  NoodleAssistantElement,
4
4
  registerNoodleAssistant
5
- } from "./chunk-QH7PL3GU.js";
5
+ } from "./chunk-T4PS6P2W.js";
6
6
  import "./chunk-XKTUXFGX.js";
7
- import "./chunk-WJV4MJUR.js";
7
+ import "./chunk-5EVZFPTD.js";
8
8
  import "./chunk-P223CYPO.js";
9
9
  import "./chunk-3YYTUJPJ.js";
10
10
  import "./chunk-5FUTL2UF.js";
@@ -22618,6 +22618,7 @@ var AssistantChatStateStore = class {
22618
22618
  #messages = [];
22619
22619
  #status = "ready";
22620
22620
  #error;
22621
+ #suggestions;
22621
22622
  #operation;
22622
22623
  #nextId = 0;
22623
22624
  subscribe(listener) {
@@ -22629,20 +22630,24 @@ var AssistantChatStateStore = class {
22629
22630
  return structuredClone({
22630
22631
  status: this.#status,
22631
22632
  messages: this.#messages,
22633
+ ...this.#suggestions ? { suggestions: this.#suggestions } : {},
22632
22634
  ...this.#error ? { error: this.#error } : {}
22633
22635
  });
22634
22636
  }
22635
22637
  handle(event) {
22636
22638
  switch (event.event) {
22637
22639
  case "message_started":
22640
+ this.#suggestions = void 0;
22638
22641
  this.#beginMessage(event.data.message);
22639
22642
  return;
22640
22643
  case "interaction_started":
22644
+ this.#suggestions = void 0;
22641
22645
  this.#beginInteraction(event.data.id);
22642
22646
  return;
22643
22647
  case "resume_started":
22644
22648
  this.#status = "submitted";
22645
22649
  this.#error = void 0;
22650
+ this.#suggestions = void 0;
22646
22651
  this.#startOperation();
22647
22652
  this.#notify();
22648
22653
  return;
@@ -22675,6 +22680,13 @@ var AssistantChatStateStore = class {
22675
22680
  case "view_available":
22676
22681
  this.#writeData("view", event.data.id, event.data);
22677
22682
  return;
22683
+ case "suggested_prompts":
22684
+ this.#suggestions = {
22685
+ phase: event.data.phase,
22686
+ prompts: [...event.data.prompts]
22687
+ };
22688
+ this.#notify();
22689
+ return;
22678
22690
  case "message_completed":
22679
22691
  this.#closeOperation("ready");
22680
22692
  return;
@@ -22715,6 +22727,7 @@ var AssistantChatStateStore = class {
22715
22727
  this.#messages = [...this.#messages, userMessage];
22716
22728
  this.#status = "submitted";
22717
22729
  this.#error = void 0;
22730
+ this.#suggestions = void 0;
22718
22731
  this.#startOperation();
22719
22732
  this.#notify();
22720
22733
  }
@@ -23044,7 +23057,8 @@ var assistantUiSchema = external_exports.object({
23044
23057
  privacyUrl: httpsUrlSchema.optional(),
23045
23058
  termsUrl: httpsUrlSchema.optional(),
23046
23059
  locale: external_exports.string().trim().min(2).max(35).optional(),
23047
- direction: external_exports.enum(["ltr", "rtl", "auto"]).optional()
23060
+ direction: external_exports.enum(["ltr", "rtl", "auto"]).optional(),
23061
+ webmcp: external_exports.object({ enabled: external_exports.boolean().optional() }).strict().optional()
23048
23062
  }).strict();
23049
23063
  var assistantConfigurationSchema = external_exports.object({ branding: brandingSchema.optional(), assistant: assistantUiSchema.optional() }).strict();
23050
23064
  function parseAssistantConfiguration(value) {
@@ -23106,7 +23120,8 @@ function parseSession(value) {
23106
23120
  const apps = value.endpoints.apps;
23107
23121
  const sandbox = value.endpoints.sandbox;
23108
23122
  const transcript = value.endpoints.transcript;
23109
- if (typeof value.token !== "string" || typeof value.expiresAt !== "string" || typeof value.endpoints.turns !== "string" || typeof value.endpoints.toolConfirmations !== "string" || interactions !== void 0 && typeof interactions !== "string" || apps !== void 0 && typeof apps !== "string" || sandbox !== void 0 && typeof sandbox !== "string" || transcript !== void 0 && typeof transcript !== "string") {
23123
+ const suggestions = value.endpoints.suggestions;
23124
+ if (typeof value.token !== "string" || typeof value.expiresAt !== "string" || typeof value.endpoints.turns !== "string" || typeof value.endpoints.toolConfirmations !== "string" || interactions !== void 0 && typeof interactions !== "string" || apps !== void 0 && typeof apps !== "string" || sandbox !== void 0 && typeof sandbox !== "string" || transcript !== void 0 && typeof transcript !== "string" || suggestions !== void 0 && typeof suggestions !== "string") {
23110
23125
  throw clientError("session_failed", "Assistant session response is invalid", true);
23111
23126
  }
23112
23127
  const configuration = parseAssistantConfiguration(value.configuration);
@@ -23119,7 +23134,8 @@ function parseSession(value) {
23119
23134
  ...typeof interactions === "string" ? { interactions } : {},
23120
23135
  ...typeof apps === "string" ? { apps } : {},
23121
23136
  ...typeof sandbox === "string" ? { sandbox } : {},
23122
- ...typeof transcript === "string" ? { transcript } : {}
23137
+ ...typeof transcript === "string" ? { transcript } : {},
23138
+ ...typeof suggestions === "string" ? { suggestions } : {}
23123
23139
  },
23124
23140
  ...configuration === void 0 ? {} : { configuration },
23125
23141
  ...isRecord2(value.resume) && typeof value.resume.tool === "string" ? { resume: { tool: value.resume.tool } } : {}
@@ -23203,6 +23219,10 @@ function toAssistantClientEvent(event) {
23203
23219
  return event;
23204
23220
  }
23205
23221
  break;
23222
+ case "suggested_prompts":
23223
+ if (isSuggestedPromptsDetail(value))
23224
+ return event;
23225
+ break;
23206
23226
  case "done":
23207
23227
  if (hasOptionalTurnId(value)) return event;
23208
23228
  break;
@@ -23214,6 +23234,12 @@ function toAssistantClientEvent(event) {
23214
23234
  }
23215
23235
  return { event: "unrecognized", data: { name: event.event, payload: value } };
23216
23236
  }
23237
+ function isSuggestedPromptsDetail(value) {
23238
+ if (!isRecord3(value)) return false;
23239
+ return (value.phase === "initial" || value.phase === "follow_up") && Array.isArray(value.prompts) && value.prompts.length <= 3 && value.prompts.every(
23240
+ (prompt) => typeof prompt === "string" && prompt.trim().length > 0 && prompt.length <= 240
23241
+ ) && hasOptionalTurnId(value);
23242
+ }
23217
23243
  function isViewAvailableDetail(value) {
23218
23244
  return hasString(value, "id") && hasString(value, "tool") && typeof value.resourceUri === "string" && value.resourceUri.startsWith("ui://") && hasOptionalString(value.title) && (value.replayed === void 0 || value.replayed === true) && isJsonValue(value.result, 0) && hasOptionalJson(value.arguments) && hasOptionalString(value.html) && (value.resourceMeta === void 0 || isRecord3(value.resourceMeta)) && (value.allowedOpenDomains === void 0 || Array.isArray(value.allowedOpenDomains) && value.allowedOpenDomains.every(isHttpsUrl2)) && hasOptionalTurnId(value);
23219
23245
  }
@@ -23383,6 +23409,9 @@ function resolveSessionSource(options) {
23383
23409
  }
23384
23410
  return { kind: "exchange", url: sessionEndpoint };
23385
23411
  }
23412
+ function sessionSourceKey(options) {
23413
+ return `${options.embedId ?? ""}|${options.serviceUrl ?? ""}|${options.sessionEndpoint ?? ""}`;
23414
+ }
23386
23415
 
23387
23416
  // src/transcript-replay.ts
23388
23417
  function parseTranscriptEntries(value) {
@@ -23397,6 +23426,11 @@ function parseTranscriptEntries(value) {
23397
23426
  }
23398
23427
  return parsed;
23399
23428
  }
23429
+ function parseTranscriptSuggestions(value) {
23430
+ if (typeof value !== "object" || value === null) return void 0;
23431
+ const suggestions = value.suggestions;
23432
+ return isSuggestedPromptsDetail(suggestions) && suggestions.phase === "follow_up" ? { phase: suggestions.phase, prompts: suggestions.prompts } : void 0;
23433
+ }
23400
23434
  function transcriptReplayEvents(entries) {
23401
23435
  const events = [];
23402
23436
  let open = false;
@@ -23547,6 +23581,32 @@ function isAbortError2(error51) {
23547
23581
  return error51 instanceof DOMException && error51.name === "AbortError";
23548
23582
  }
23549
23583
 
23584
+ // src/visitor-id.ts
23585
+ var STORAGE_PREFIX = "noodleseed.assistant.visitor.";
23586
+ function randomVisitorId() {
23587
+ try {
23588
+ return globalThis.crypto.randomUUID();
23589
+ } catch {
23590
+ return void 0;
23591
+ }
23592
+ }
23593
+ function visitorIdForSource(sourceKey) {
23594
+ const key = `${STORAGE_PREFIX}${sourceKey}`;
23595
+ try {
23596
+ const stored = globalThis.localStorage?.getItem(key);
23597
+ if (typeof stored === "string" && stored.length > 0) return stored;
23598
+ } catch {
23599
+ return void 0;
23600
+ }
23601
+ const created = randomVisitorId();
23602
+ if (created === void 0) return void 0;
23603
+ try {
23604
+ globalThis.localStorage?.setItem(key, created);
23605
+ } catch {
23606
+ }
23607
+ return created;
23608
+ }
23609
+
23550
23610
  // src/client.ts
23551
23611
  var DefaultAssistantClient = class {
23552
23612
  #source;
@@ -23559,6 +23619,7 @@ var DefaultAssistantClient = class {
23559
23619
  #context;
23560
23620
  #modelContext;
23561
23621
  #active;
23622
+ #initialSuggestions;
23562
23623
  #pendingAppInteractions = /* @__PURE__ */ new Map();
23563
23624
  constructor(options) {
23564
23625
  this.#source = resolveSessionSource(options);
@@ -23620,10 +23681,13 @@ var DefaultAssistantClient = class {
23620
23681
  "turn_failed"
23621
23682
  );
23622
23683
  if (!response.ok) return;
23623
- for (const event of transcriptReplayEvents(parseTranscriptEntries(await response.json()))) {
23684
+ const value = await response.json();
23685
+ for (const event of transcriptReplayEvents(parseTranscriptEntries(value))) {
23624
23686
  this.#emit(event);
23625
23687
  if (event.event === "message_completed") await this.#chat.flush();
23626
23688
  }
23689
+ const suggestions = parseTranscriptSuggestions(value);
23690
+ if (suggestions) this.#emit({ event: "suggested_prompts", data: suggestions });
23627
23691
  }
23628
23692
  /** POST the one-shot resume trigger; a 409 means nothing was pending, which renders as silence. */
23629
23693
  async #resumeTurn(session, signal) {
@@ -23632,7 +23696,10 @@ var DefaultAssistantClient = class {
23632
23696
  {
23633
23697
  method: "POST",
23634
23698
  headers: authorizedHeaders(session.token, "text/event-stream"),
23635
- body: JSON.stringify({ resume: true }),
23699
+ body: JSON.stringify({
23700
+ resume: true,
23701
+ ...session.endpoints.suggestions ? { suggestions: true } : {}
23702
+ }),
23636
23703
  signal
23637
23704
  },
23638
23705
  "turn_failed"
@@ -23655,6 +23722,7 @@ var DefaultAssistantClient = class {
23655
23722
  async sendMessage(text2) {
23656
23723
  const message = text2.trim();
23657
23724
  if (!message) return;
23725
+ this.#cancelInitialSuggestions();
23658
23726
  await this.#singleFlight(async (signal) => {
23659
23727
  await this.#runChatOperation(
23660
23728
  signal,
@@ -23662,10 +23730,42 @@ var DefaultAssistantClient = class {
23662
23730
  );
23663
23731
  });
23664
23732
  }
23733
+ async loadInitialSuggestions() {
23734
+ if (!this.#session) await this.connect();
23735
+ const session = this.#session;
23736
+ if (!session?.endpoints.suggestions) return;
23737
+ if (this.#initialSuggestions) return this.#initialSuggestions.promise;
23738
+ const controller = new AbortController();
23739
+ const clientContext = this.#resolveClientContext();
23740
+ const pageContext = this.#resolvePageContext();
23741
+ const promise2 = this.#request(
23742
+ session.endpoints.suggestions,
23743
+ {
23744
+ method: "POST",
23745
+ headers: authorizedHeaders(session.token, "text/event-stream"),
23746
+ body: JSON.stringify({
23747
+ ...clientContext ? { clientContext } : {},
23748
+ ...pageContext === void 0 ? {} : { pageContext },
23749
+ ...this.#modelContext ? { modelContext: this.#modelContext } : {}
23750
+ }),
23751
+ signal: controller.signal
23752
+ },
23753
+ "turn_failed"
23754
+ ).then(async (response) => {
23755
+ if (response.ok) await this.#consume(response);
23756
+ }).finally(() => {
23757
+ if (this.#initialSuggestions?.controller === controller) {
23758
+ this.#initialSuggestions = void 0;
23759
+ }
23760
+ });
23761
+ this.#initialSuggestions = { controller, promise: promise2 };
23762
+ return promise2;
23763
+ }
23665
23764
  async respond(id, resolution) {
23666
23765
  if (!id) {
23667
23766
  throw clientError("invalid_request", "interaction id is required", false);
23668
23767
  }
23768
+ this.#cancelInitialSuggestions();
23669
23769
  await this.#singleFlight(async (signal) => {
23670
23770
  await this.#runChatOperation(signal, async () => {
23671
23771
  const pendingApp = this.#pendingAppInteractions.get(id);
@@ -23685,6 +23785,7 @@ var DefaultAssistantClient = class {
23685
23785
  const body = modernEndpoint ? {
23686
23786
  id,
23687
23787
  action: resolution.action,
23788
+ ...session.endpoints.suggestions ? { suggestions: true } : {},
23688
23789
  ...resolution.action === "accept" && resolution.content !== void 0 ? { content: resolution.content } : {}
23689
23790
  } : { id };
23690
23791
  this.#emit({ event: "interaction_started", data: { id, action: resolution.action } });
@@ -23763,7 +23864,7 @@ var DefaultAssistantClient = class {
23763
23864
  this.#modelContext = copyAssistantModelContext(update);
23764
23865
  this.#emit({ event: "model_context_changed", data: { modelContext: this.#modelContext } });
23765
23866
  }
23766
- async requestApp(method, params) {
23867
+ async requestApp(method, params, options) {
23767
23868
  const session = this.#session;
23768
23869
  if (!session?.endpoints.apps) {
23769
23870
  throw clientError(
@@ -23777,7 +23878,11 @@ var DefaultAssistantClient = class {
23777
23878
  {
23778
23879
  method: "POST",
23779
23880
  headers: authorizedHeaders(session.token, "application/json"),
23780
- body: JSON.stringify({ method, params })
23881
+ body: JSON.stringify({
23882
+ ...options?.bridge ? { bridge: options.bridge } : {},
23883
+ method,
23884
+ params
23885
+ })
23781
23886
  },
23782
23887
  "app_request_failed"
23783
23888
  );
@@ -23792,6 +23897,7 @@ var DefaultAssistantClient = class {
23792
23897
  const value = await response.json();
23793
23898
  const interaction = parseAppInteraction(value);
23794
23899
  if (!interaction) return value;
23900
+ options?.onSuspended?.();
23795
23901
  return new Promise((resolve2, reject) => {
23796
23902
  const id = interaction.data.id;
23797
23903
  const replaced = this.#pendingAppInteractions.get(id);
@@ -23809,6 +23915,7 @@ var DefaultAssistantClient = class {
23809
23915
  return this.#session?.endpoints.sandbox;
23810
23916
  }
23811
23917
  resetSession() {
23918
+ this.#cancelInitialSuggestions();
23812
23919
  this.#rejectPendingAppInteractions(
23813
23920
  clientError("confirmation_expired", "assistant session is not active", false)
23814
23921
  );
@@ -23817,6 +23924,7 @@ var DefaultAssistantClient = class {
23817
23924
  }
23818
23925
  abort() {
23819
23926
  this.#active?.abort();
23927
+ this.#cancelInitialSuggestions();
23820
23928
  }
23821
23929
  async #sendTurn(message, mayRetry, signal, modelContext) {
23822
23930
  const session = this.#session ?? await this.#createSession(signal);
@@ -23832,7 +23940,8 @@ var DefaultAssistantClient = class {
23832
23940
  message,
23833
23941
  ...clientContext ? { clientContext } : {},
23834
23942
  ...pageContext === void 0 ? {} : { pageContext },
23835
- ...modelContext ? { modelContext } : {}
23943
+ ...modelContext ? { modelContext } : {},
23944
+ ...session.endpoints.suggestions ? { suggestions: true } : {}
23836
23945
  }),
23837
23946
  signal
23838
23947
  },
@@ -23892,11 +24001,12 @@ var DefaultAssistantClient = class {
23892
24001
  }
23893
24002
  async #createSession(signal) {
23894
24003
  const source = this.#source;
24004
+ const visitorId = source.kind === "public" ? visitorIdForSource(sessionSourceKey({ embedId: source.embedId, serviceUrl: source.url })) : void 0;
23895
24005
  const response = await this.#requestSession(source, {
23896
24006
  method: "POST",
23897
24007
  headers: { Accept: "application/json", "Content-Type": "application/json" },
23898
24008
  body: JSON.stringify(
23899
- source.kind === "public" ? { embedId: source.embedId } : this.#context ? { context: this.#context } : {}
24009
+ source.kind === "public" ? { embedId: source.embedId, ...visitorId ? { visitorId } : {} } : this.#context ? { context: this.#context } : {}
23900
24010
  ),
23901
24011
  credentials: source.kind === "public" ? "omit" : "same-origin",
23902
24012
  signal
@@ -24021,6 +24131,9 @@ var DefaultAssistantClient = class {
24021
24131
  this.#active = void 0;
24022
24132
  }
24023
24133
  }
24134
+ #cancelInitialSuggestions() {
24135
+ this.#initialSuggestions?.controller.abort();
24136
+ }
24024
24137
  async #runChatOperation(signal, operation) {
24025
24138
  try {
24026
24139
  await operation();