@noodleseed/assistant 1.14.0 → 1.16.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.
@@ -8383,6 +8383,11 @@ function toAssistantClientEvent(event) {
8383
8383
  return event;
8384
8384
  }
8385
8385
  break;
8386
+ case "auth_requested":
8387
+ if (hasString(value, "id") && hasString(value, "tool") && hasString(value, "continuation") && hasString(value, "expiresAt") && hasOptionalTurnId(value)) {
8388
+ return event;
8389
+ }
8390
+ break;
8386
8391
  case "interaction_resolved":
8387
8392
  if (hasString(value, "id") && (value.action === void 0 || isInteractionAction(value.action)) && hasOptionalTurnId(value)) {
8388
8393
  return event;
@@ -8567,6 +8572,24 @@ function hasToJsonProperty(value) {
8567
8572
  return false;
8568
8573
  }
8569
8574
 
8575
+ // src/session-source.ts
8576
+ var NOODLE_CLOUD_URL = "https://cloud.noodleseed.dev";
8577
+ var PUBLIC_SESSION_PATH = "/v1/assistant/public-sessions";
8578
+ function resolveSessionSource(options) {
8579
+ const { sessionEndpoint, embedId, serviceUrl } = options;
8580
+ if (Boolean(embedId) === Boolean(sessionEndpoint)) {
8581
+ throw new Error("pass either embedId or sessionEndpoint, not both and not neither");
8582
+ }
8583
+ if (embedId) {
8584
+ const base = (serviceUrl ?? NOODLE_CLOUD_URL).replace(/\/+$/, "");
8585
+ return { kind: "public", url: `${base}${PUBLIC_SESSION_PATH}`, embedId };
8586
+ }
8587
+ return { kind: "exchange", url: sessionEndpoint };
8588
+ }
8589
+ function sessionSourceKey(options) {
8590
+ return `${options.embedId ?? ""}|${options.serviceUrl ?? ""}|${options.sessionEndpoint ?? ""}`;
8591
+ }
8592
+
8570
8593
  // src/transport.ts
8571
8594
  var AssistantTransportError = class extends Error {
8572
8595
  code = "invalid_response";
@@ -8694,6 +8717,19 @@ function isAbortError2(error) {
8694
8717
  }
8695
8718
 
8696
8719
  // src/client.ts
8720
+ async function refusalCode(response) {
8721
+ try {
8722
+ const body = await response.clone().json();
8723
+ const code = body.code;
8724
+ return typeof code === "string" ? code : void 0;
8725
+ } catch {
8726
+ return void 0;
8727
+ }
8728
+ }
8729
+ var UNRETRYABLE_SERVICE_CODES = /* @__PURE__ */ new Set([
8730
+ "daily_turn_budget_exhausted",
8731
+ "daily_session_budget_exhausted"
8732
+ ]);
8697
8733
  var AssistantClientError = class extends Error {
8698
8734
  detail;
8699
8735
  constructor(detail, message, options) {
@@ -8703,7 +8739,7 @@ var AssistantClientError = class extends Error {
8703
8739
  }
8704
8740
  };
8705
8741
  var DefaultAssistantClient = class {
8706
- #sessionEndpoint;
8742
+ #source;
8707
8743
  #fetch;
8708
8744
  #listeners = /* @__PURE__ */ new Set();
8709
8745
  #chat = new AssistantChatStateStore();
@@ -8715,8 +8751,7 @@ var DefaultAssistantClient = class {
8715
8751
  #active;
8716
8752
  #pendingAppInteractions = /* @__PURE__ */ new Map();
8717
8753
  constructor(options) {
8718
- if (!options.sessionEndpoint) throw new Error("sessionEndpoint is required");
8719
- this.#sessionEndpoint = options.sessionEndpoint;
8754
+ this.#source = resolveSessionSource(options);
8720
8755
  this.#fetch = options.fetch ?? ((input, init) => globalThis.fetch(input, init));
8721
8756
  this.#context = options.context ? { ...options.context } : void 0;
8722
8757
  this.#modelContext = options.modelContext ? copyAssistantModelContext(options.modelContext) : void 0;
@@ -8932,11 +8967,14 @@ var DefaultAssistantClient = class {
8932
8967
  return;
8933
8968
  }
8934
8969
  if (!response.ok) {
8970
+ const serviceCode = await refusalCode(response);
8935
8971
  throw clientError(
8936
8972
  "turn_failed",
8937
8973
  `Assistant turn failed (${response.status})`,
8938
8974
  false,
8939
- response.status
8975
+ response.status,
8976
+ void 0,
8977
+ serviceCode
8940
8978
  );
8941
8979
  }
8942
8980
  await this.#consume(response);
@@ -8975,23 +9013,25 @@ var DefaultAssistantClient = class {
8975
9013
  }
8976
9014
  }
8977
9015
  async #createSession(signal) {
8978
- const response = await this.#request(
8979
- this.#sessionEndpoint,
8980
- {
8981
- method: "POST",
8982
- headers: { Accept: "application/json", "Content-Type": "application/json" },
8983
- body: JSON.stringify(this.#context ? { context: this.#context } : {}),
8984
- credentials: "same-origin",
8985
- signal
8986
- },
8987
- "session_failed"
8988
- );
9016
+ const source = this.#source;
9017
+ const response = await this.#requestSession(source, {
9018
+ method: "POST",
9019
+ headers: { Accept: "application/json", "Content-Type": "application/json" },
9020
+ body: JSON.stringify(
9021
+ source.kind === "public" ? { embedId: source.embedId } : this.#context ? { context: this.#context } : {}
9022
+ ),
9023
+ credentials: source.kind === "public" ? "omit" : "same-origin",
9024
+ signal
9025
+ });
8989
9026
  if (!response.ok) {
9027
+ const serviceCode = await refusalCode(response);
8990
9028
  throw clientError(
8991
9029
  "session_failed",
8992
9030
  `Assistant session failed (${response.status})`,
8993
- true,
8994
- response.status
9031
+ serviceCode === void 0 || !UNRETRYABLE_SERVICE_CODES.has(serviceCode),
9032
+ response.status,
9033
+ void 0,
9034
+ serviceCode
8995
9035
  );
8996
9036
  }
8997
9037
  let value;
@@ -9048,6 +9088,34 @@ var DefaultAssistantClient = class {
9048
9088
  );
9049
9089
  }
9050
9090
  }
9091
+ /**
9092
+ * The mint, with the one failure a public page hits that an in-app embed cannot.
9093
+ *
9094
+ * A page that loads the embed script and then blocks `connect-src` fails here as a bare network
9095
+ * rejection — no status, no body, nothing to read. "Assistant request failed" sends a developer
9096
+ * hunting through their own code; naming the directive and the origin ends the search. The message
9097
+ * covers a plain outage too, because from inside the browser the two are indistinguishable.
9098
+ */
9099
+ async #requestSession(source, init) {
9100
+ try {
9101
+ return await this.#fetch(source.url, init);
9102
+ } catch (error) {
9103
+ if (init.signal?.aborted) throw error;
9104
+ if (source.kind !== "public") {
9105
+ throw clientError("session_failed", "Assistant request failed", true, void 0, {
9106
+ cause: error
9107
+ });
9108
+ }
9109
+ throw clientError(
9110
+ "session_failed",
9111
+ `Assistant could not reach ${new URL(source.url).origin}. If the page sets a Content-Security-Policy, allow that origin in connect-src (and in script-src and frame-src).`,
9112
+ true,
9113
+ void 0,
9114
+ { cause: error },
9115
+ "blocked_by_page"
9116
+ );
9117
+ }
9118
+ }
9051
9119
  async #request(input, init, failureCode) {
9052
9120
  try {
9053
9121
  return await this.#fetch(input, init);
@@ -9169,9 +9237,14 @@ async function readStableErrorCode(response) {
9169
9237
  if (!isRecord4(value) || typeof value.code !== "string") return void 0;
9170
9238
  return /^[A-Za-z0-9_.-]{1,64}$/.test(value.code) ? value.code : void 0;
9171
9239
  }
9172
- function clientError(code, message, retryable, status, options) {
9240
+ function clientError(code, message, retryable, status, options, serviceCode) {
9173
9241
  return new AssistantClientError(
9174
- { code, ...status === void 0 ? {} : { status }, retryable },
9242
+ {
9243
+ code,
9244
+ ...status === void 0 ? {} : { status },
9245
+ retryable,
9246
+ ...serviceCode === void 0 ? {} : { serviceCode }
9247
+ },
9175
9248
  message,
9176
9249
  options
9177
9250
  );
@@ -9179,7 +9252,8 @@ function clientError(code, message, retryable, status, options) {
9179
9252
 
9180
9253
  export {
9181
9254
  copyAssistantModelContext,
9255
+ sessionSourceKey,
9182
9256
  AssistantClientError,
9183
9257
  createAssistantClient
9184
9258
  };
9185
- //# sourceMappingURL=chunk-RITHSCTD.js.map
9259
+ //# sourceMappingURL=chunk-NPV64PUX.js.map