@noodleseed/assistant 1.13.0 → 1.14.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/dist/client.cjs CHANGED
@@ -22643,7 +22643,7 @@ var AssistantChatStateStore = class {
22643
22643
  this.#writeText(event.data.delta);
22644
22644
  return;
22645
22645
  case "tool_proposed":
22646
- this.#writeConfirmation(event.data);
22646
+ this.#writeStandaloneData(() => this.#writeConfirmation(event.data));
22647
22647
  return;
22648
22648
  case "interaction_proposed":
22649
22649
  this.#writeData("confirmation", event.data.id, {
@@ -22652,10 +22652,12 @@ var AssistantChatStateStore = class {
22652
22652
  });
22653
22653
  return;
22654
22654
  case "input_requested":
22655
- this.#writeData("input-request", event.data.id, {
22656
- ...event.data,
22657
- status: "pending"
22658
- });
22655
+ this.#writeStandaloneData(
22656
+ () => this.#writeData("input-request", event.data.id, {
22657
+ ...event.data,
22658
+ status: "pending"
22659
+ })
22660
+ );
22659
22661
  return;
22660
22662
  case "tool_completed":
22661
22663
  this.#writeData("tool-result", event.data.id, event.data);
@@ -22762,6 +22764,12 @@ var AssistantChatStateStore = class {
22762
22764
  #writeConfirmation(data) {
22763
22765
  this.#writeData("confirmation", data.id, { ...data, status: "pending" });
22764
22766
  }
22767
+ #writeStandaloneData(write) {
22768
+ const standalone = this.#operation === void 0;
22769
+ if (standalone) this.#startOperation();
22770
+ write();
22771
+ if (standalone) this.#closeOperation("ready");
22772
+ }
22765
22773
  #writeData(name21, id, data) {
22766
22774
  const operation = this.#operation;
22767
22775
  if (!operation || operation.closed) return;
@@ -23240,6 +23248,7 @@ var DefaultAssistantClient = class {
23240
23248
  #context;
23241
23249
  #modelContext;
23242
23250
  #active;
23251
+ #pendingAppInteractions = /* @__PURE__ */ new Map();
23243
23252
  constructor(options) {
23244
23253
  if (!options.sessionEndpoint) throw new Error("sessionEndpoint is required");
23245
23254
  this.#sessionEndpoint = options.sessionEndpoint;
@@ -23281,6 +23290,7 @@ var DefaultAssistantClient = class {
23281
23290
  }
23282
23291
  await this.#singleFlight(async (signal) => {
23283
23292
  await this.#runChatOperation(signal, async () => {
23293
+ const pendingApp = this.#pendingAppInteractions.get(id);
23284
23294
  const session = this.#session;
23285
23295
  if (!session) {
23286
23296
  throw clientError("confirmation_expired", "assistant session is not active", false);
@@ -23300,32 +23310,67 @@ var DefaultAssistantClient = class {
23300
23310
  ...resolution.action === "accept" && resolution.content !== void 0 ? { content: resolution.content } : {}
23301
23311
  } : { id };
23302
23312
  this.#emit({ event: "interaction_started", data: { id, action: resolution.action } });
23303
- const response = await this.#request(
23304
- endpoint,
23305
- {
23306
- method: "POST",
23307
- headers: authorizedHeaders(session.token, "text/event-stream"),
23308
- body: JSON.stringify(body),
23309
- signal
23310
- },
23311
- "confirmation_failed"
23312
- );
23313
- if (response.status === 401) {
23314
- this.#session = void 0;
23315
- this.#emit({ event: "session_expired", data: {} });
23316
- }
23317
- if (!response.ok) {
23318
- const serverCode = await readStableErrorCode(response);
23319
- const expired = response.status === 401 || response.status === 409;
23320
- throw clientError(
23321
- serverCode ?? (expired ? "confirmation_expired" : "confirmation_failed"),
23322
- `Assistant interaction failed (${response.status})`,
23323
- false,
23324
- response.status
23313
+ try {
23314
+ const response = await this.#request(
23315
+ endpoint,
23316
+ {
23317
+ method: "POST",
23318
+ headers: authorizedHeaders(session.token, "text/event-stream"),
23319
+ body: JSON.stringify(body),
23320
+ signal
23321
+ },
23322
+ "confirmation_failed"
23325
23323
  );
23324
+ if (response.status === 401) {
23325
+ this.#session = void 0;
23326
+ this.#emit({ event: "session_expired", data: {} });
23327
+ }
23328
+ if (!response.ok) {
23329
+ const serverCode = await readStableErrorCode(response);
23330
+ const expired = response.status === 401 || response.status === 409;
23331
+ throw clientError(
23332
+ serverCode ?? (expired ? "confirmation_expired" : "confirmation_failed"),
23333
+ `Assistant interaction failed (${response.status})`,
23334
+ false,
23335
+ response.status
23336
+ );
23337
+ }
23338
+ let completedResult;
23339
+ let nextInteractionId;
23340
+ await this.#consume(response, (event) => {
23341
+ if (event.event === "tool_completed" && event.data.id === id) {
23342
+ completedResult = event.data.result;
23343
+ }
23344
+ if ((event.event === "tool_proposed" || event.event === "input_requested") && event.data.id !== id) {
23345
+ nextInteractionId = event.data.id;
23346
+ }
23347
+ });
23348
+ this.#emit({ event: "interaction_completed", data: { id, action: resolution.action } });
23349
+ if (pendingApp) {
23350
+ this.#pendingAppInteractions.delete(id);
23351
+ if (nextInteractionId) {
23352
+ this.#pendingAppInteractions.set(nextInteractionId, pendingApp);
23353
+ } else if (resolution.action !== "accept") {
23354
+ pendingApp.resolve(appInteractionStopped(resolution.action));
23355
+ } else if (completedResult !== void 0) {
23356
+ pendingApp.resolve(appToolResult(completedResult));
23357
+ } else {
23358
+ pendingApp.reject(
23359
+ clientError(
23360
+ "invalid_response",
23361
+ "Assistant app interaction completed without a tool result",
23362
+ false
23363
+ )
23364
+ );
23365
+ }
23366
+ }
23367
+ } catch (error51) {
23368
+ if (pendingApp && this.#pendingAppInteractions.get(id) === pendingApp) {
23369
+ this.#pendingAppInteractions.delete(id);
23370
+ pendingApp.reject(error51);
23371
+ }
23372
+ throw error51;
23326
23373
  }
23327
- await this.#consume(response);
23328
- this.#emit({ event: "interaction_completed", data: { id, action: resolution.action } });
23329
23374
  });
23330
23375
  });
23331
23376
  }
@@ -23366,12 +23411,29 @@ var DefaultAssistantClient = class {
23366
23411
  response.status
23367
23412
  );
23368
23413
  }
23369
- return response.json();
23414
+ const value = await response.json();
23415
+ const interaction = parseAppInteraction(value);
23416
+ if (!interaction) return value;
23417
+ return new Promise((resolve2, reject) => {
23418
+ const id = interaction.data.id;
23419
+ const replaced = this.#pendingAppInteractions.get(id);
23420
+ if (replaced) {
23421
+ replaced.reject(
23422
+ clientError("invalid_response", "Assistant returned a duplicate app interaction", false)
23423
+ );
23424
+ }
23425
+ this.#pendingAppInteractions.set(id, { resolve: resolve2, reject });
23426
+ this.#emit(interaction);
23427
+ void this.#chat.flush();
23428
+ });
23370
23429
  }
23371
23430
  appSandboxUrl() {
23372
23431
  return this.#session?.endpoints.sandbox;
23373
23432
  }
23374
23433
  resetSession() {
23434
+ this.#rejectPendingAppInteractions(
23435
+ clientError("confirmation_expired", "assistant session is not active", false)
23436
+ );
23375
23437
  this.#session = void 0;
23376
23438
  this.#emit({ event: "session_reset", data: {} });
23377
23439
  }
@@ -23492,12 +23554,13 @@ var DefaultAssistantClient = class {
23492
23554
  });
23493
23555
  return session;
23494
23556
  }
23495
- async #consume(response) {
23557
+ async #consume(response, observe) {
23496
23558
  let streamError;
23497
23559
  try {
23498
23560
  await consumeAssistantEvents(response, (event) => {
23499
23561
  const clientEvent = toAssistantClientEvent(event);
23500
23562
  this.#emit(clientEvent);
23563
+ observe?.(clientEvent);
23501
23564
  if (clientEvent.event !== "error" || typeof clientEvent.data.code !== "string") return;
23502
23565
  streamError = {
23503
23566
  code: clientEvent.data.code,
@@ -23566,6 +23629,10 @@ var DefaultAssistantClient = class {
23566
23629
  }
23567
23630
  }
23568
23631
  }
23632
+ #rejectPendingAppInteractions(error51) {
23633
+ for (const pending of this.#pendingAppInteractions.values()) pending.reject(error51);
23634
+ this.#pendingAppInteractions.clear();
23635
+ }
23569
23636
  };
23570
23637
  function createAssistantClient(options) {
23571
23638
  return new DefaultAssistantClient(options);
@@ -23577,6 +23644,30 @@ function authorizedHeaders(token, accept) {
23577
23644
  Accept: accept
23578
23645
  };
23579
23646
  }
23647
+ function parseAppInteraction(value) {
23648
+ if (!isRecord4(value) || !isRecord4(value.interaction)) return void 0;
23649
+ const event = value.interaction.event;
23650
+ const data = value.interaction.data;
23651
+ if (event !== "tool_proposed" && event !== "input_requested" || !isRecord4(data)) {
23652
+ return void 0;
23653
+ }
23654
+ const parsed = toAssistantClientEvent({ event, data });
23655
+ return parsed.event === "tool_proposed" || parsed.event === "input_requested" ? parsed : void 0;
23656
+ }
23657
+ function appToolResult(result) {
23658
+ const text2 = typeof result === "string" ? result : JSON.stringify(result);
23659
+ return result !== null && typeof result === "object" && !Array.isArray(result) ? {
23660
+ content: [{ type: "text", text: text2 }],
23661
+ structuredContent: result,
23662
+ isError: false
23663
+ } : { content: [{ type: "text", text: text2 }], isError: false };
23664
+ }
23665
+ function appInteractionStopped(action) {
23666
+ return {
23667
+ content: [{ type: "text", text: `interaction_${action}` }],
23668
+ isError: true
23669
+ };
23670
+ }
23580
23671
  function parseSession(value) {
23581
23672
  if (!isRecord4(value) || !isRecord4(value.endpoints)) {
23582
23673
  throw clientError("session_failed", "Assistant session response is invalid", true);