@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
@@ -1,4 +1,4 @@
1
- import { A as AssistantClient, a as AssistantViewAvailableDetail, e as AssistantErrorDetail } from './client-CPFw8tfo.js';
1
+ import { A as AssistantClient, a as AssistantViewAvailableDetail, e as AssistantErrorDetail } from './client-contract-Bw9dtrhw.cjs';
2
2
 
3
3
  declare const APP_VIEW_TAG_NAME = "noodle-app-view";
4
4
  interface AssistantAppViewErrorDetail {
@@ -1,4 +1,4 @@
1
- import { A as AssistantClient, a as AssistantViewAvailableDetail, e as AssistantErrorDetail } from './client-_xivfs4q.cjs';
1
+ import { A as AssistantClient, a as AssistantViewAvailableDetail, e as AssistantErrorDetail } from './client-contract-DjU9qsoP.js';
2
2
 
3
3
  declare const APP_VIEW_TAG_NAME = "noodle-app-view";
4
4
  interface AssistantAppViewErrorDetail {
@@ -1,7 +1,7 @@
1
- export { A as APP_VIEW_TAG_NAME, a as AssistantAppViewErrorDetail, N as NoodleAppViewElement } from './app-view-element-BjRkwNN1.cjs';
2
- export { A as AssistantClient, a as AssistantViewAvailableDetail } from './client-_xivfs4q.cjs';
3
- import './appearance-BpVu3lpk.cjs';
1
+ export { A as APP_VIEW_TAG_NAME, a as AssistantAppViewErrorDetail, N as NoodleAppViewElement } from './app-view-element-kR8eqhyD.cjs';
2
+ export { A as AssistantClient, a as AssistantViewAvailableDetail } from './client-contract-Bw9dtrhw.cjs';
4
3
  import 'ai';
4
+ import './server-CMqlAnxs.cjs';
5
5
 
6
6
  /** Register the framework-neutral MCP App host. Safe to call more than once or during SSR. */
7
7
  declare function registerNoodleAppView(): void;
@@ -1,7 +1,7 @@
1
- export { A as APP_VIEW_TAG_NAME, a as AssistantAppViewErrorDetail, N as NoodleAppViewElement } from './app-view-element-mXHvcAXc.js';
2
- export { A as AssistantClient, a as AssistantViewAvailableDetail } from './client-CPFw8tfo.js';
3
- import './appearance-BpVu3lpk.js';
1
+ export { A as APP_VIEW_TAG_NAME, a as AssistantAppViewErrorDetail, N as NoodleAppViewElement } from './app-view-element-nY1svwHT.js';
2
+ export { A as AssistantClient, a as AssistantViewAvailableDetail } from './client-contract-DjU9qsoP.js';
4
3
  import 'ai';
4
+ import './server-CMqlAnxs.js';
5
5
 
6
6
  /** Register the framework-neutral MCP App host. Safe to call more than once or during SSR. */
7
7
  declare function registerNoodleAppView(): void;
@@ -4209,6 +4209,7 @@ var AssistantChatStateStore = class {
4209
4209
  #messages = [];
4210
4210
  #status = "ready";
4211
4211
  #error;
4212
+ #suggestions;
4212
4213
  #operation;
4213
4214
  #nextId = 0;
4214
4215
  subscribe(listener) {
@@ -4220,20 +4221,24 @@ var AssistantChatStateStore = class {
4220
4221
  return structuredClone({
4221
4222
  status: this.#status,
4222
4223
  messages: this.#messages,
4224
+ ...this.#suggestions ? { suggestions: this.#suggestions } : {},
4223
4225
  ...this.#error ? { error: this.#error } : {}
4224
4226
  });
4225
4227
  }
4226
4228
  handle(event) {
4227
4229
  switch (event.event) {
4228
4230
  case "message_started":
4231
+ this.#suggestions = void 0;
4229
4232
  this.#beginMessage(event.data.message);
4230
4233
  return;
4231
4234
  case "interaction_started":
4235
+ this.#suggestions = void 0;
4232
4236
  this.#beginInteraction(event.data.id);
4233
4237
  return;
4234
4238
  case "resume_started":
4235
4239
  this.#status = "submitted";
4236
4240
  this.#error = void 0;
4241
+ this.#suggestions = void 0;
4237
4242
  this.#startOperation();
4238
4243
  this.#notify();
4239
4244
  return;
@@ -4266,6 +4271,13 @@ var AssistantChatStateStore = class {
4266
4271
  case "view_available":
4267
4272
  this.#writeData("view", event.data.id, event.data);
4268
4273
  return;
4274
+ case "suggested_prompts":
4275
+ this.#suggestions = {
4276
+ phase: event.data.phase,
4277
+ prompts: [...event.data.prompts]
4278
+ };
4279
+ this.#notify();
4280
+ return;
4269
4281
  case "message_completed":
4270
4282
  this.#closeOperation("ready");
4271
4283
  return;
@@ -4306,6 +4318,7 @@ var AssistantChatStateStore = class {
4306
4318
  this.#messages = [...this.#messages, userMessage];
4307
4319
  this.#status = "submitted";
4308
4320
  this.#error = void 0;
4321
+ this.#suggestions = void 0;
4309
4322
  this.#startOperation();
4310
4323
  this.#notify();
4311
4324
  }
@@ -4635,7 +4648,8 @@ var assistantUiSchema = external_exports.object({
4635
4648
  privacyUrl: httpsUrlSchema.optional(),
4636
4649
  termsUrl: httpsUrlSchema.optional(),
4637
4650
  locale: external_exports.string().trim().min(2).max(35).optional(),
4638
- direction: external_exports.enum(["ltr", "rtl", "auto"]).optional()
4651
+ direction: external_exports.enum(["ltr", "rtl", "auto"]).optional(),
4652
+ webmcp: external_exports.object({ enabled: external_exports.boolean().optional() }).strict().optional()
4639
4653
  }).strict();
4640
4654
  var assistantConfigurationSchema = external_exports.object({ branding: brandingSchema.optional(), assistant: assistantUiSchema.optional() }).strict();
4641
4655
  function parseAssistantConfiguration(value) {
@@ -4697,7 +4711,8 @@ function parseSession(value) {
4697
4711
  const apps = value.endpoints.apps;
4698
4712
  const sandbox = value.endpoints.sandbox;
4699
4713
  const transcript = value.endpoints.transcript;
4700
- 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") {
4714
+ const suggestions = value.endpoints.suggestions;
4715
+ 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") {
4701
4716
  throw clientError("session_failed", "Assistant session response is invalid", true);
4702
4717
  }
4703
4718
  const configuration = parseAssistantConfiguration(value.configuration);
@@ -4710,7 +4725,8 @@ function parseSession(value) {
4710
4725
  ...typeof interactions === "string" ? { interactions } : {},
4711
4726
  ...typeof apps === "string" ? { apps } : {},
4712
4727
  ...typeof sandbox === "string" ? { sandbox } : {},
4713
- ...typeof transcript === "string" ? { transcript } : {}
4728
+ ...typeof transcript === "string" ? { transcript } : {},
4729
+ ...typeof suggestions === "string" ? { suggestions } : {}
4714
4730
  },
4715
4731
  ...configuration === void 0 ? {} : { configuration },
4716
4732
  ...isRecord2(value.resume) && typeof value.resume.tool === "string" ? { resume: { tool: value.resume.tool } } : {}
@@ -4794,6 +4810,10 @@ function toAssistantClientEvent(event) {
4794
4810
  return event;
4795
4811
  }
4796
4812
  break;
4813
+ case "suggested_prompts":
4814
+ if (isSuggestedPromptsDetail(value))
4815
+ return event;
4816
+ break;
4797
4817
  case "done":
4798
4818
  if (hasOptionalTurnId(value)) return event;
4799
4819
  break;
@@ -4805,6 +4825,12 @@ function toAssistantClientEvent(event) {
4805
4825
  }
4806
4826
  return { event: "unrecognized", data: { name: event.event, payload: value } };
4807
4827
  }
4828
+ function isSuggestedPromptsDetail(value) {
4829
+ if (!isRecord3(value)) return false;
4830
+ return (value.phase === "initial" || value.phase === "follow_up") && Array.isArray(value.prompts) && value.prompts.length <= 3 && value.prompts.every(
4831
+ (prompt) => typeof prompt === "string" && prompt.trim().length > 0 && prompt.length <= 240
4832
+ ) && hasOptionalTurnId(value);
4833
+ }
4808
4834
  function isViewAvailableDetail(value) {
4809
4835
  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);
4810
4836
  }
@@ -4996,6 +5022,11 @@ function parseTranscriptEntries(value) {
4996
5022
  }
4997
5023
  return parsed;
4998
5024
  }
5025
+ function parseTranscriptSuggestions(value) {
5026
+ if (typeof value !== "object" || value === null) return void 0;
5027
+ const suggestions = value.suggestions;
5028
+ return isSuggestedPromptsDetail(suggestions) && suggestions.phase === "follow_up" ? { phase: suggestions.phase, prompts: suggestions.prompts } : void 0;
5029
+ }
4999
5030
  function transcriptReplayEvents(entries) {
5000
5031
  const events = [];
5001
5032
  let open = false;
@@ -5146,6 +5177,32 @@ function isAbortError2(error) {
5146
5177
  return error instanceof DOMException && error.name === "AbortError";
5147
5178
  }
5148
5179
 
5180
+ // src/visitor-id.ts
5181
+ var STORAGE_PREFIX = "noodleseed.assistant.visitor.";
5182
+ function randomVisitorId() {
5183
+ try {
5184
+ return globalThis.crypto.randomUUID();
5185
+ } catch {
5186
+ return void 0;
5187
+ }
5188
+ }
5189
+ function visitorIdForSource(sourceKey) {
5190
+ const key = `${STORAGE_PREFIX}${sourceKey}`;
5191
+ try {
5192
+ const stored = globalThis.localStorage?.getItem(key);
5193
+ if (typeof stored === "string" && stored.length > 0) return stored;
5194
+ } catch {
5195
+ return void 0;
5196
+ }
5197
+ const created = randomVisitorId();
5198
+ if (created === void 0) return void 0;
5199
+ try {
5200
+ globalThis.localStorage?.setItem(key, created);
5201
+ } catch {
5202
+ }
5203
+ return created;
5204
+ }
5205
+
5149
5206
  // src/client.ts
5150
5207
  var DefaultAssistantClient = class {
5151
5208
  #source;
@@ -5158,6 +5215,7 @@ var DefaultAssistantClient = class {
5158
5215
  #context;
5159
5216
  #modelContext;
5160
5217
  #active;
5218
+ #initialSuggestions;
5161
5219
  #pendingAppInteractions = /* @__PURE__ */ new Map();
5162
5220
  constructor(options) {
5163
5221
  this.#source = resolveSessionSource(options);
@@ -5219,10 +5277,13 @@ var DefaultAssistantClient = class {
5219
5277
  "turn_failed"
5220
5278
  );
5221
5279
  if (!response.ok) return;
5222
- for (const event of transcriptReplayEvents(parseTranscriptEntries(await response.json()))) {
5280
+ const value = await response.json();
5281
+ for (const event of transcriptReplayEvents(parseTranscriptEntries(value))) {
5223
5282
  this.#emit(event);
5224
5283
  if (event.event === "message_completed") await this.#chat.flush();
5225
5284
  }
5285
+ const suggestions = parseTranscriptSuggestions(value);
5286
+ if (suggestions) this.#emit({ event: "suggested_prompts", data: suggestions });
5226
5287
  }
5227
5288
  /** POST the one-shot resume trigger; a 409 means nothing was pending, which renders as silence. */
5228
5289
  async #resumeTurn(session, signal) {
@@ -5231,7 +5292,10 @@ var DefaultAssistantClient = class {
5231
5292
  {
5232
5293
  method: "POST",
5233
5294
  headers: authorizedHeaders(session.token, "text/event-stream"),
5234
- body: JSON.stringify({ resume: true }),
5295
+ body: JSON.stringify({
5296
+ resume: true,
5297
+ ...session.endpoints.suggestions ? { suggestions: true } : {}
5298
+ }),
5235
5299
  signal
5236
5300
  },
5237
5301
  "turn_failed"
@@ -5254,6 +5318,7 @@ var DefaultAssistantClient = class {
5254
5318
  async sendMessage(text2) {
5255
5319
  const message = text2.trim();
5256
5320
  if (!message) return;
5321
+ this.#cancelInitialSuggestions();
5257
5322
  await this.#singleFlight(async (signal) => {
5258
5323
  await this.#runChatOperation(
5259
5324
  signal,
@@ -5261,10 +5326,42 @@ var DefaultAssistantClient = class {
5261
5326
  );
5262
5327
  });
5263
5328
  }
5329
+ async loadInitialSuggestions() {
5330
+ if (!this.#session) await this.connect();
5331
+ const session = this.#session;
5332
+ if (!session?.endpoints.suggestions) return;
5333
+ if (this.#initialSuggestions) return this.#initialSuggestions.promise;
5334
+ const controller = new AbortController();
5335
+ const clientContext = this.#resolveClientContext();
5336
+ const pageContext = this.#resolvePageContext();
5337
+ const promise = this.#request(
5338
+ session.endpoints.suggestions,
5339
+ {
5340
+ method: "POST",
5341
+ headers: authorizedHeaders(session.token, "text/event-stream"),
5342
+ body: JSON.stringify({
5343
+ ...clientContext ? { clientContext } : {},
5344
+ ...pageContext === void 0 ? {} : { pageContext },
5345
+ ...this.#modelContext ? { modelContext: this.#modelContext } : {}
5346
+ }),
5347
+ signal: controller.signal
5348
+ },
5349
+ "turn_failed"
5350
+ ).then(async (response) => {
5351
+ if (response.ok) await this.#consume(response);
5352
+ }).finally(() => {
5353
+ if (this.#initialSuggestions?.controller === controller) {
5354
+ this.#initialSuggestions = void 0;
5355
+ }
5356
+ });
5357
+ this.#initialSuggestions = { controller, promise };
5358
+ return promise;
5359
+ }
5264
5360
  async respond(id, resolution) {
5265
5361
  if (!id) {
5266
5362
  throw clientError("invalid_request", "interaction id is required", false);
5267
5363
  }
5364
+ this.#cancelInitialSuggestions();
5268
5365
  await this.#singleFlight(async (signal) => {
5269
5366
  await this.#runChatOperation(signal, async () => {
5270
5367
  const pendingApp = this.#pendingAppInteractions.get(id);
@@ -5284,6 +5381,7 @@ var DefaultAssistantClient = class {
5284
5381
  const body = modernEndpoint ? {
5285
5382
  id,
5286
5383
  action: resolution.action,
5384
+ ...session.endpoints.suggestions ? { suggestions: true } : {},
5287
5385
  ...resolution.action === "accept" && resolution.content !== void 0 ? { content: resolution.content } : {}
5288
5386
  } : { id };
5289
5387
  this.#emit({ event: "interaction_started", data: { id, action: resolution.action } });
@@ -5362,7 +5460,7 @@ var DefaultAssistantClient = class {
5362
5460
  this.#modelContext = copyAssistantModelContext(update);
5363
5461
  this.#emit({ event: "model_context_changed", data: { modelContext: this.#modelContext } });
5364
5462
  }
5365
- async requestApp(method, params) {
5463
+ async requestApp(method, params, options) {
5366
5464
  const session = this.#session;
5367
5465
  if (!session?.endpoints.apps) {
5368
5466
  throw clientError(
@@ -5376,7 +5474,11 @@ var DefaultAssistantClient = class {
5376
5474
  {
5377
5475
  method: "POST",
5378
5476
  headers: authorizedHeaders(session.token, "application/json"),
5379
- body: JSON.stringify({ method, params })
5477
+ body: JSON.stringify({
5478
+ ...options?.bridge ? { bridge: options.bridge } : {},
5479
+ method,
5480
+ params
5481
+ })
5380
5482
  },
5381
5483
  "app_request_failed"
5382
5484
  );
@@ -5391,6 +5493,7 @@ var DefaultAssistantClient = class {
5391
5493
  const value = await response.json();
5392
5494
  const interaction = parseAppInteraction(value);
5393
5495
  if (!interaction) return value;
5496
+ options?.onSuspended?.();
5394
5497
  return new Promise((resolve2, reject) => {
5395
5498
  const id = interaction.data.id;
5396
5499
  const replaced = this.#pendingAppInteractions.get(id);
@@ -5408,6 +5511,7 @@ var DefaultAssistantClient = class {
5408
5511
  return this.#session?.endpoints.sandbox;
5409
5512
  }
5410
5513
  resetSession() {
5514
+ this.#cancelInitialSuggestions();
5411
5515
  this.#rejectPendingAppInteractions(
5412
5516
  clientError("confirmation_expired", "assistant session is not active", false)
5413
5517
  );
@@ -5416,6 +5520,7 @@ var DefaultAssistantClient = class {
5416
5520
  }
5417
5521
  abort() {
5418
5522
  this.#active?.abort();
5523
+ this.#cancelInitialSuggestions();
5419
5524
  }
5420
5525
  async #sendTurn(message, mayRetry, signal, modelContext) {
5421
5526
  const session = this.#session ?? await this.#createSession(signal);
@@ -5431,7 +5536,8 @@ var DefaultAssistantClient = class {
5431
5536
  message,
5432
5537
  ...clientContext ? { clientContext } : {},
5433
5538
  ...pageContext === void 0 ? {} : { pageContext },
5434
- ...modelContext ? { modelContext } : {}
5539
+ ...modelContext ? { modelContext } : {},
5540
+ ...session.endpoints.suggestions ? { suggestions: true } : {}
5435
5541
  }),
5436
5542
  signal
5437
5543
  },
@@ -5491,11 +5597,12 @@ var DefaultAssistantClient = class {
5491
5597
  }
5492
5598
  async #createSession(signal) {
5493
5599
  const source = this.#source;
5600
+ const visitorId = source.kind === "public" ? visitorIdForSource(sessionSourceKey({ embedId: source.embedId, serviceUrl: source.url })) : void 0;
5494
5601
  const response = await this.#requestSession(source, {
5495
5602
  method: "POST",
5496
5603
  headers: { Accept: "application/json", "Content-Type": "application/json" },
5497
5604
  body: JSON.stringify(
5498
- source.kind === "public" ? { embedId: source.embedId } : this.#context ? { context: this.#context } : {}
5605
+ source.kind === "public" ? { embedId: source.embedId, ...visitorId ? { visitorId } : {} } : this.#context ? { context: this.#context } : {}
5499
5606
  ),
5500
5607
  credentials: source.kind === "public" ? "omit" : "same-origin",
5501
5608
  signal
@@ -5620,6 +5727,9 @@ var DefaultAssistantClient = class {
5620
5727
  this.#active = void 0;
5621
5728
  }
5622
5729
  }
5730
+ #cancelInitialSuggestions() {
5731
+ this.#initialSuggestions?.controller.abort();
5732
+ }
5623
5733
  async #runChatOperation(signal, operation) {
5624
5734
  try {
5625
5735
  await operation();
@@ -5688,4 +5798,4 @@ export {
5688
5798
  sessionSourceKey,
5689
5799
  createAssistantClient
5690
5800
  };
5691
- //# sourceMappingURL=chunk-WJV4MJUR.js.map
5801
+ //# sourceMappingURL=chunk-5EVZFPTD.js.map