@noodleseed/assistant 1.13.0 → 1.15.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/README.md +5 -0
- package/dist/{chunk-KRB2SXFD.js → chunk-RRJIK473.js} +211 -51
- package/dist/chunk-RRJIK473.js.map +1 -0
- package/dist/{chunk-ZPELSHUP.js → chunk-SUHNNWKP.js} +37 -20
- package/dist/chunk-SUHNNWKP.js.map +1 -0
- package/dist/{client-BKIeuSVS.d.cts → client-B_l59rWg.d.cts} +29 -2
- package/dist/{client-BccAX_om.d.ts → client-MnLfiyo2.d.ts} +29 -2
- package/dist/client.cjs +206 -50
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.cts +1 -1
- package/dist/client.d.ts +1 -1
- package/dist/client.js +1 -1
- package/dist/embed.global.js +540 -0
- package/dist/index.cjs +242 -67
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -2
- package/dist/index.d.ts +6 -2
- package/dist/index.js +2 -2
- package/dist/react/client.cjs +217 -54
- package/dist/react/client.cjs.map +1 -1
- package/dist/react/client.d.cts +3 -3
- package/dist/react/client.d.ts +3 -3
- package/dist/react/client.js +12 -5
- package/dist/react/client.js.map +1 -1
- package/dist/react.cjs +251 -70
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +7 -2
- package/dist/react.d.ts +7 -2
- package/dist/react.js +11 -5
- package/dist/react.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-KRB2SXFD.js.map +0 -1
- package/dist/chunk-ZPELSHUP.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -22191,7 +22191,7 @@ function mountAssistantApp(detail, actions, options = {}) {
|
|
|
22191
22191
|
const current = options.getDetail?.() ?? detail;
|
|
22192
22192
|
void bridge.sendSandboxResourceReady({
|
|
22193
22193
|
html: current.html,
|
|
22194
|
-
sandbox: "allow-scripts
|
|
22194
|
+
sandbox: "allow-scripts",
|
|
22195
22195
|
...current.resourceMeta ?? {}
|
|
22196
22196
|
});
|
|
22197
22197
|
};
|
|
@@ -26817,7 +26817,7 @@ var AssistantChatStateStore = class {
|
|
|
26817
26817
|
this.#writeText(event.data.delta);
|
|
26818
26818
|
return;
|
|
26819
26819
|
case "tool_proposed":
|
|
26820
|
-
this.#writeConfirmation(event.data);
|
|
26820
|
+
this.#writeStandaloneData(() => this.#writeConfirmation(event.data));
|
|
26821
26821
|
return;
|
|
26822
26822
|
case "interaction_proposed":
|
|
26823
26823
|
this.#writeData("confirmation", event.data.id, {
|
|
@@ -26826,10 +26826,12 @@ var AssistantChatStateStore = class {
|
|
|
26826
26826
|
});
|
|
26827
26827
|
return;
|
|
26828
26828
|
case "input_requested":
|
|
26829
|
-
this.#
|
|
26830
|
-
|
|
26831
|
-
|
|
26832
|
-
|
|
26829
|
+
this.#writeStandaloneData(
|
|
26830
|
+
() => this.#writeData("input-request", event.data.id, {
|
|
26831
|
+
...event.data,
|
|
26832
|
+
status: "pending"
|
|
26833
|
+
})
|
|
26834
|
+
);
|
|
26833
26835
|
return;
|
|
26834
26836
|
case "tool_completed":
|
|
26835
26837
|
this.#writeData("tool-result", event.data.id, event.data);
|
|
@@ -26936,6 +26938,12 @@ var AssistantChatStateStore = class {
|
|
|
26936
26938
|
#writeConfirmation(data) {
|
|
26937
26939
|
this.#writeData("confirmation", data.id, { ...data, status: "pending" });
|
|
26938
26940
|
}
|
|
26941
|
+
#writeStandaloneData(write) {
|
|
26942
|
+
const standalone = this.#operation === void 0;
|
|
26943
|
+
if (standalone) this.#startOperation();
|
|
26944
|
+
write();
|
|
26945
|
+
if (standalone) this.#closeOperation("ready");
|
|
26946
|
+
}
|
|
26939
26947
|
#writeData(name21, id, data) {
|
|
26940
26948
|
const operation = this.#operation;
|
|
26941
26949
|
if (!operation || operation.closed) return;
|
|
@@ -27268,6 +27276,24 @@ function hasToJsonProperty(value) {
|
|
|
27268
27276
|
return false;
|
|
27269
27277
|
}
|
|
27270
27278
|
|
|
27279
|
+
// src/session-source.ts
|
|
27280
|
+
var NOODLE_CLOUD_URL = "https://cloud.noodleseed.dev";
|
|
27281
|
+
var PUBLIC_SESSION_PATH = "/v1/assistant/public-sessions";
|
|
27282
|
+
function resolveSessionSource(options) {
|
|
27283
|
+
const { sessionEndpoint, embedId, serviceUrl } = options;
|
|
27284
|
+
if (Boolean(embedId) === Boolean(sessionEndpoint)) {
|
|
27285
|
+
throw new Error("pass either embedId or sessionEndpoint, not both and not neither");
|
|
27286
|
+
}
|
|
27287
|
+
if (embedId) {
|
|
27288
|
+
const base = (serviceUrl ?? NOODLE_CLOUD_URL).replace(/\/+$/, "");
|
|
27289
|
+
return { kind: "public", url: `${base}${PUBLIC_SESSION_PATH}`, embedId };
|
|
27290
|
+
}
|
|
27291
|
+
return { kind: "exchange", url: sessionEndpoint };
|
|
27292
|
+
}
|
|
27293
|
+
function sessionSourceKey(options) {
|
|
27294
|
+
return `${options.embedId ?? ""}|${options.serviceUrl ?? ""}|${options.sessionEndpoint ?? ""}`;
|
|
27295
|
+
}
|
|
27296
|
+
|
|
27271
27297
|
// src/transport.ts
|
|
27272
27298
|
var AssistantTransportError = class extends Error {
|
|
27273
27299
|
code = "invalid_response";
|
|
@@ -27395,6 +27421,19 @@ function isAbortError2(error51) {
|
|
|
27395
27421
|
}
|
|
27396
27422
|
|
|
27397
27423
|
// src/client.ts
|
|
27424
|
+
async function refusalCode(response) {
|
|
27425
|
+
try {
|
|
27426
|
+
const body = await response.clone().json();
|
|
27427
|
+
const code = body.code;
|
|
27428
|
+
return typeof code === "string" ? code : void 0;
|
|
27429
|
+
} catch {
|
|
27430
|
+
return void 0;
|
|
27431
|
+
}
|
|
27432
|
+
}
|
|
27433
|
+
var UNRETRYABLE_SERVICE_CODES = /* @__PURE__ */ new Set([
|
|
27434
|
+
"daily_turn_budget_exhausted",
|
|
27435
|
+
"daily_session_budget_exhausted"
|
|
27436
|
+
]);
|
|
27398
27437
|
var AssistantClientError = class extends Error {
|
|
27399
27438
|
detail;
|
|
27400
27439
|
constructor(detail, message, options) {
|
|
@@ -27404,7 +27443,7 @@ var AssistantClientError = class extends Error {
|
|
|
27404
27443
|
}
|
|
27405
27444
|
};
|
|
27406
27445
|
var DefaultAssistantClient = class {
|
|
27407
|
-
#
|
|
27446
|
+
#source;
|
|
27408
27447
|
#fetch;
|
|
27409
27448
|
#listeners = /* @__PURE__ */ new Set();
|
|
27410
27449
|
#chat = new AssistantChatStateStore();
|
|
@@ -27414,9 +27453,9 @@ var DefaultAssistantClient = class {
|
|
|
27414
27453
|
#context;
|
|
27415
27454
|
#modelContext;
|
|
27416
27455
|
#active;
|
|
27456
|
+
#pendingAppInteractions = /* @__PURE__ */ new Map();
|
|
27417
27457
|
constructor(options) {
|
|
27418
|
-
|
|
27419
|
-
this.#sessionEndpoint = options.sessionEndpoint;
|
|
27458
|
+
this.#source = resolveSessionSource(options);
|
|
27420
27459
|
this.#fetch = options.fetch ?? ((input, init) => globalThis.fetch(input, init));
|
|
27421
27460
|
this.#context = options.context ? { ...options.context } : void 0;
|
|
27422
27461
|
this.#modelContext = options.modelContext ? copyAssistantModelContext(options.modelContext) : void 0;
|
|
@@ -27455,6 +27494,7 @@ var DefaultAssistantClient = class {
|
|
|
27455
27494
|
}
|
|
27456
27495
|
await this.#singleFlight(async (signal) => {
|
|
27457
27496
|
await this.#runChatOperation(signal, async () => {
|
|
27497
|
+
const pendingApp = this.#pendingAppInteractions.get(id);
|
|
27458
27498
|
const session = this.#session;
|
|
27459
27499
|
if (!session) {
|
|
27460
27500
|
throw clientError("confirmation_expired", "assistant session is not active", false);
|
|
@@ -27474,32 +27514,67 @@ var DefaultAssistantClient = class {
|
|
|
27474
27514
|
...resolution.action === "accept" && resolution.content !== void 0 ? { content: resolution.content } : {}
|
|
27475
27515
|
} : { id };
|
|
27476
27516
|
this.#emit({ event: "interaction_started", data: { id, action: resolution.action } });
|
|
27477
|
-
|
|
27478
|
-
|
|
27479
|
-
|
|
27480
|
-
|
|
27481
|
-
|
|
27482
|
-
|
|
27483
|
-
|
|
27484
|
-
|
|
27485
|
-
|
|
27486
|
-
|
|
27487
|
-
if (response.status === 401) {
|
|
27488
|
-
this.#session = void 0;
|
|
27489
|
-
this.#emit({ event: "session_expired", data: {} });
|
|
27490
|
-
}
|
|
27491
|
-
if (!response.ok) {
|
|
27492
|
-
const serverCode = await readStableErrorCode(response);
|
|
27493
|
-
const expired = response.status === 401 || response.status === 409;
|
|
27494
|
-
throw clientError(
|
|
27495
|
-
serverCode ?? (expired ? "confirmation_expired" : "confirmation_failed"),
|
|
27496
|
-
`Assistant interaction failed (${response.status})`,
|
|
27497
|
-
false,
|
|
27498
|
-
response.status
|
|
27517
|
+
try {
|
|
27518
|
+
const response = await this.#request(
|
|
27519
|
+
endpoint,
|
|
27520
|
+
{
|
|
27521
|
+
method: "POST",
|
|
27522
|
+
headers: authorizedHeaders(session.token, "text/event-stream"),
|
|
27523
|
+
body: JSON.stringify(body),
|
|
27524
|
+
signal
|
|
27525
|
+
},
|
|
27526
|
+
"confirmation_failed"
|
|
27499
27527
|
);
|
|
27528
|
+
if (response.status === 401) {
|
|
27529
|
+
this.#session = void 0;
|
|
27530
|
+
this.#emit({ event: "session_expired", data: {} });
|
|
27531
|
+
}
|
|
27532
|
+
if (!response.ok) {
|
|
27533
|
+
const serverCode = await readStableErrorCode(response);
|
|
27534
|
+
const expired = response.status === 401 || response.status === 409;
|
|
27535
|
+
throw clientError(
|
|
27536
|
+
serverCode ?? (expired ? "confirmation_expired" : "confirmation_failed"),
|
|
27537
|
+
`Assistant interaction failed (${response.status})`,
|
|
27538
|
+
false,
|
|
27539
|
+
response.status
|
|
27540
|
+
);
|
|
27541
|
+
}
|
|
27542
|
+
let completedResult;
|
|
27543
|
+
let nextInteractionId;
|
|
27544
|
+
await this.#consume(response, (event) => {
|
|
27545
|
+
if (event.event === "tool_completed" && event.data.id === id) {
|
|
27546
|
+
completedResult = event.data.result;
|
|
27547
|
+
}
|
|
27548
|
+
if ((event.event === "tool_proposed" || event.event === "input_requested") && event.data.id !== id) {
|
|
27549
|
+
nextInteractionId = event.data.id;
|
|
27550
|
+
}
|
|
27551
|
+
});
|
|
27552
|
+
this.#emit({ event: "interaction_completed", data: { id, action: resolution.action } });
|
|
27553
|
+
if (pendingApp) {
|
|
27554
|
+
this.#pendingAppInteractions.delete(id);
|
|
27555
|
+
if (nextInteractionId) {
|
|
27556
|
+
this.#pendingAppInteractions.set(nextInteractionId, pendingApp);
|
|
27557
|
+
} else if (resolution.action !== "accept") {
|
|
27558
|
+
pendingApp.resolve(appInteractionStopped(resolution.action));
|
|
27559
|
+
} else if (completedResult !== void 0) {
|
|
27560
|
+
pendingApp.resolve(appToolResult(completedResult));
|
|
27561
|
+
} else {
|
|
27562
|
+
pendingApp.reject(
|
|
27563
|
+
clientError(
|
|
27564
|
+
"invalid_response",
|
|
27565
|
+
"Assistant app interaction completed without a tool result",
|
|
27566
|
+
false
|
|
27567
|
+
)
|
|
27568
|
+
);
|
|
27569
|
+
}
|
|
27570
|
+
}
|
|
27571
|
+
} catch (error51) {
|
|
27572
|
+
if (pendingApp && this.#pendingAppInteractions.get(id) === pendingApp) {
|
|
27573
|
+
this.#pendingAppInteractions.delete(id);
|
|
27574
|
+
pendingApp.reject(error51);
|
|
27575
|
+
}
|
|
27576
|
+
throw error51;
|
|
27500
27577
|
}
|
|
27501
|
-
await this.#consume(response);
|
|
27502
|
-
this.#emit({ event: "interaction_completed", data: { id, action: resolution.action } });
|
|
27503
27578
|
});
|
|
27504
27579
|
});
|
|
27505
27580
|
}
|
|
@@ -27540,12 +27615,29 @@ var DefaultAssistantClient = class {
|
|
|
27540
27615
|
response.status
|
|
27541
27616
|
);
|
|
27542
27617
|
}
|
|
27543
|
-
|
|
27618
|
+
const value = await response.json();
|
|
27619
|
+
const interaction = parseAppInteraction(value);
|
|
27620
|
+
if (!interaction) return value;
|
|
27621
|
+
return new Promise((resolve2, reject) => {
|
|
27622
|
+
const id = interaction.data.id;
|
|
27623
|
+
const replaced = this.#pendingAppInteractions.get(id);
|
|
27624
|
+
if (replaced) {
|
|
27625
|
+
replaced.reject(
|
|
27626
|
+
clientError("invalid_response", "Assistant returned a duplicate app interaction", false)
|
|
27627
|
+
);
|
|
27628
|
+
}
|
|
27629
|
+
this.#pendingAppInteractions.set(id, { resolve: resolve2, reject });
|
|
27630
|
+
this.#emit(interaction);
|
|
27631
|
+
void this.#chat.flush();
|
|
27632
|
+
});
|
|
27544
27633
|
}
|
|
27545
27634
|
appSandboxUrl() {
|
|
27546
27635
|
return this.#session?.endpoints.sandbox;
|
|
27547
27636
|
}
|
|
27548
27637
|
resetSession() {
|
|
27638
|
+
this.#rejectPendingAppInteractions(
|
|
27639
|
+
clientError("confirmation_expired", "assistant session is not active", false)
|
|
27640
|
+
);
|
|
27549
27641
|
this.#session = void 0;
|
|
27550
27642
|
this.#emit({ event: "session_reset", data: {} });
|
|
27551
27643
|
}
|
|
@@ -27579,11 +27671,14 @@ var DefaultAssistantClient = class {
|
|
|
27579
27671
|
return;
|
|
27580
27672
|
}
|
|
27581
27673
|
if (!response.ok) {
|
|
27674
|
+
const serviceCode = await refusalCode(response);
|
|
27582
27675
|
throw clientError(
|
|
27583
27676
|
"turn_failed",
|
|
27584
27677
|
`Assistant turn failed (${response.status})`,
|
|
27585
27678
|
false,
|
|
27586
|
-
response.status
|
|
27679
|
+
response.status,
|
|
27680
|
+
void 0,
|
|
27681
|
+
serviceCode
|
|
27587
27682
|
);
|
|
27588
27683
|
}
|
|
27589
27684
|
await this.#consume(response);
|
|
@@ -27622,23 +27717,25 @@ var DefaultAssistantClient = class {
|
|
|
27622
27717
|
}
|
|
27623
27718
|
}
|
|
27624
27719
|
async #createSession(signal) {
|
|
27625
|
-
const
|
|
27626
|
-
|
|
27627
|
-
|
|
27628
|
-
|
|
27629
|
-
|
|
27630
|
-
|
|
27631
|
-
|
|
27632
|
-
|
|
27633
|
-
|
|
27634
|
-
|
|
27635
|
-
);
|
|
27720
|
+
const source = this.#source;
|
|
27721
|
+
const response = await this.#requestSession(source, {
|
|
27722
|
+
method: "POST",
|
|
27723
|
+
headers: { Accept: "application/json", "Content-Type": "application/json" },
|
|
27724
|
+
body: JSON.stringify(
|
|
27725
|
+
source.kind === "public" ? { embedId: source.embedId } : this.#context ? { context: this.#context } : {}
|
|
27726
|
+
),
|
|
27727
|
+
credentials: source.kind === "public" ? "omit" : "same-origin",
|
|
27728
|
+
signal
|
|
27729
|
+
});
|
|
27636
27730
|
if (!response.ok) {
|
|
27731
|
+
const serviceCode = await refusalCode(response);
|
|
27637
27732
|
throw clientError(
|
|
27638
27733
|
"session_failed",
|
|
27639
27734
|
`Assistant session failed (${response.status})`,
|
|
27640
|
-
|
|
27641
|
-
response.status
|
|
27735
|
+
serviceCode === void 0 || !UNRETRYABLE_SERVICE_CODES.has(serviceCode),
|
|
27736
|
+
response.status,
|
|
27737
|
+
void 0,
|
|
27738
|
+
serviceCode
|
|
27642
27739
|
);
|
|
27643
27740
|
}
|
|
27644
27741
|
let value;
|
|
@@ -27666,12 +27763,13 @@ var DefaultAssistantClient = class {
|
|
|
27666
27763
|
});
|
|
27667
27764
|
return session;
|
|
27668
27765
|
}
|
|
27669
|
-
async #consume(response) {
|
|
27766
|
+
async #consume(response, observe) {
|
|
27670
27767
|
let streamError;
|
|
27671
27768
|
try {
|
|
27672
27769
|
await consumeAssistantEvents(response, (event) => {
|
|
27673
27770
|
const clientEvent = toAssistantClientEvent(event);
|
|
27674
27771
|
this.#emit(clientEvent);
|
|
27772
|
+
observe?.(clientEvent);
|
|
27675
27773
|
if (clientEvent.event !== "error" || typeof clientEvent.data.code !== "string") return;
|
|
27676
27774
|
streamError = {
|
|
27677
27775
|
code: clientEvent.data.code,
|
|
@@ -27694,6 +27792,34 @@ var DefaultAssistantClient = class {
|
|
|
27694
27792
|
);
|
|
27695
27793
|
}
|
|
27696
27794
|
}
|
|
27795
|
+
/**
|
|
27796
|
+
* The mint, with the one failure a public page hits that an in-app embed cannot.
|
|
27797
|
+
*
|
|
27798
|
+
* A page that loads the embed script and then blocks `connect-src` fails here as a bare network
|
|
27799
|
+
* rejection — no status, no body, nothing to read. "Assistant request failed" sends a developer
|
|
27800
|
+
* hunting through their own code; naming the directive and the origin ends the search. The message
|
|
27801
|
+
* covers a plain outage too, because from inside the browser the two are indistinguishable.
|
|
27802
|
+
*/
|
|
27803
|
+
async #requestSession(source, init) {
|
|
27804
|
+
try {
|
|
27805
|
+
return await this.#fetch(source.url, init);
|
|
27806
|
+
} catch (error51) {
|
|
27807
|
+
if (init.signal?.aborted) throw error51;
|
|
27808
|
+
if (source.kind !== "public") {
|
|
27809
|
+
throw clientError("session_failed", "Assistant request failed", true, void 0, {
|
|
27810
|
+
cause: error51
|
|
27811
|
+
});
|
|
27812
|
+
}
|
|
27813
|
+
throw clientError(
|
|
27814
|
+
"session_failed",
|
|
27815
|
+
`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).`,
|
|
27816
|
+
true,
|
|
27817
|
+
void 0,
|
|
27818
|
+
{ cause: error51 },
|
|
27819
|
+
"blocked_by_page"
|
|
27820
|
+
);
|
|
27821
|
+
}
|
|
27822
|
+
}
|
|
27697
27823
|
async #request(input, init, failureCode) {
|
|
27698
27824
|
try {
|
|
27699
27825
|
return await this.#fetch(input, init);
|
|
@@ -27740,6 +27866,10 @@ var DefaultAssistantClient = class {
|
|
|
27740
27866
|
}
|
|
27741
27867
|
}
|
|
27742
27868
|
}
|
|
27869
|
+
#rejectPendingAppInteractions(error51) {
|
|
27870
|
+
for (const pending of this.#pendingAppInteractions.values()) pending.reject(error51);
|
|
27871
|
+
this.#pendingAppInteractions.clear();
|
|
27872
|
+
}
|
|
27743
27873
|
};
|
|
27744
27874
|
function createAssistantClient(options) {
|
|
27745
27875
|
return new DefaultAssistantClient(options);
|
|
@@ -27751,6 +27881,30 @@ function authorizedHeaders(token, accept) {
|
|
|
27751
27881
|
Accept: accept
|
|
27752
27882
|
};
|
|
27753
27883
|
}
|
|
27884
|
+
function parseAppInteraction(value) {
|
|
27885
|
+
if (!isRecord4(value) || !isRecord4(value.interaction)) return void 0;
|
|
27886
|
+
const event = value.interaction.event;
|
|
27887
|
+
const data = value.interaction.data;
|
|
27888
|
+
if (event !== "tool_proposed" && event !== "input_requested" || !isRecord4(data)) {
|
|
27889
|
+
return void 0;
|
|
27890
|
+
}
|
|
27891
|
+
const parsed = toAssistantClientEvent({ event, data });
|
|
27892
|
+
return parsed.event === "tool_proposed" || parsed.event === "input_requested" ? parsed : void 0;
|
|
27893
|
+
}
|
|
27894
|
+
function appToolResult(result) {
|
|
27895
|
+
const text3 = typeof result === "string" ? result : JSON.stringify(result);
|
|
27896
|
+
return result !== null && typeof result === "object" && !Array.isArray(result) ? {
|
|
27897
|
+
content: [{ type: "text", text: text3 }],
|
|
27898
|
+
structuredContent: result,
|
|
27899
|
+
isError: false
|
|
27900
|
+
} : { content: [{ type: "text", text: text3 }], isError: false };
|
|
27901
|
+
}
|
|
27902
|
+
function appInteractionStopped(action) {
|
|
27903
|
+
return {
|
|
27904
|
+
content: [{ type: "text", text: `interaction_${action}` }],
|
|
27905
|
+
isError: true
|
|
27906
|
+
};
|
|
27907
|
+
}
|
|
27754
27908
|
function parseSession(value) {
|
|
27755
27909
|
if (!isRecord4(value) || !isRecord4(value.endpoints)) {
|
|
27756
27910
|
throw clientError("session_failed", "Assistant session response is invalid", true);
|
|
@@ -27787,9 +27941,14 @@ async function readStableErrorCode(response) {
|
|
|
27787
27941
|
if (!isRecord4(value) || typeof value.code !== "string") return void 0;
|
|
27788
27942
|
return /^[A-Za-z0-9_.-]{1,64}$/.test(value.code) ? value.code : void 0;
|
|
27789
27943
|
}
|
|
27790
|
-
function clientError(code, message, retryable, status, options) {
|
|
27944
|
+
function clientError(code, message, retryable, status, options, serviceCode) {
|
|
27791
27945
|
return new AssistantClientError(
|
|
27792
|
-
{
|
|
27946
|
+
{
|
|
27947
|
+
code,
|
|
27948
|
+
...status === void 0 ? {} : { status },
|
|
27949
|
+
retryable,
|
|
27950
|
+
...serviceCode === void 0 ? {} : { serviceCode }
|
|
27951
|
+
},
|
|
27793
27952
|
message,
|
|
27794
27953
|
options
|
|
27795
27954
|
);
|
|
@@ -30966,7 +31125,6 @@ var AssistantElementEventController = class {
|
|
|
30966
31125
|
return;
|
|
30967
31126
|
}
|
|
30968
31127
|
if (name21 === "tool_proposed" || name21 === "interaction_proposed") {
|
|
30969
|
-
if (!this.#streamKind) return;
|
|
30970
31128
|
this.#appendToolProposal(
|
|
30971
31129
|
String(data.id ?? ""),
|
|
30972
31130
|
String(data.tool ?? "action"),
|
|
@@ -30978,7 +31136,6 @@ var AssistantElementEventController = class {
|
|
|
30978
31136
|
return;
|
|
30979
31137
|
}
|
|
30980
31138
|
if (name21 === "input_requested" && isRecord5(data.requestedSchema)) {
|
|
30981
|
-
if (!this.#streamKind) return;
|
|
30982
31139
|
this.#appendInputRequest(
|
|
30983
31140
|
String(data.id ?? ""),
|
|
30984
31141
|
String(data.message ?? "More information is required"),
|
|
@@ -31882,6 +32039,10 @@ var NoodleAssistantElement = class extends HTMLElementBase {
|
|
|
31882
32039
|
#appearanceStyleSnapshot = /* @__PURE__ */ new Map();
|
|
31883
32040
|
#scroll = new AssistantElementScrollController();
|
|
31884
32041
|
sessionEndpoint = "";
|
|
32042
|
+
/** Public mount: the non-secret embed id `noodle deploy` printed. Mutually exclusive with the above. */
|
|
32043
|
+
embedId = "";
|
|
32044
|
+
/** Only for a dev or self-hosted service; the published snippet needs no origin. */
|
|
32045
|
+
serviceUrl = "";
|
|
31885
32046
|
get appearance() {
|
|
31886
32047
|
return this.#hostAppearance;
|
|
31887
32048
|
}
|
|
@@ -31936,6 +32097,8 @@ var NoodleAssistantElement = class extends HTMLElementBase {
|
|
|
31936
32097
|
connectedCallback() {
|
|
31937
32098
|
if (!this.shadowRoot) this.attachShadow({ mode: "open" });
|
|
31938
32099
|
if (!this.sessionEndpoint) this.sessionEndpoint = this.getAttribute("session-endpoint") ?? "";
|
|
32100
|
+
if (!this.embedId) this.embedId = this.getAttribute("embed-id") ?? "";
|
|
32101
|
+
if (!this.serviceUrl) this.serviceUrl = this.getAttribute("service-url") ?? "";
|
|
31939
32102
|
this.#media = globalThis.matchMedia?.("(prefers-color-scheme: dark)");
|
|
31940
32103
|
this.#media?.addEventListener("change", this.#handleSystemTheme);
|
|
31941
32104
|
document.addEventListener("keydown", this.#handleDocumentKeydown);
|
|
@@ -32107,26 +32270,31 @@ var NoodleAssistantElement = class extends HTMLElementBase {
|
|
|
32107
32270
|
}
|
|
32108
32271
|
};
|
|
32109
32272
|
#ensureClient() {
|
|
32110
|
-
|
|
32273
|
+
const key = sessionSourceKey(this);
|
|
32274
|
+
if (this.#client && this.#clientEndpoint === key) return this.#client;
|
|
32111
32275
|
this.#client?.abort();
|
|
32112
32276
|
this.#unsubscribeClient?.();
|
|
32113
32277
|
this.#sessionBootstrap = void 0;
|
|
32114
|
-
const
|
|
32115
|
-
sessionEndpoint: this.sessionEndpoint,
|
|
32278
|
+
const behavior = {
|
|
32116
32279
|
...this.#context ? { context: this.#context } : {},
|
|
32117
32280
|
clientContext: browserClientContext,
|
|
32118
32281
|
...this.#pageContext ? { pageContext: this.#pageContext } : {},
|
|
32119
32282
|
...this.#modelContext ? { modelContext: this.#modelContext } : {}
|
|
32120
|
-
}
|
|
32283
|
+
};
|
|
32284
|
+
const client = this.embedId ? createAssistantClient({
|
|
32285
|
+
embedId: this.embedId,
|
|
32286
|
+
...this.serviceUrl ? { serviceUrl: this.serviceUrl } : {},
|
|
32287
|
+
...behavior
|
|
32288
|
+
}) : createAssistantClient({ sessionEndpoint: this.sessionEndpoint, ...behavior });
|
|
32121
32289
|
this.#client = client;
|
|
32122
|
-
this.#clientEndpoint =
|
|
32290
|
+
this.#clientEndpoint = key;
|
|
32123
32291
|
this.#unsubscribeClient = client.subscribe((event) => {
|
|
32124
32292
|
if (this.#client === client) this.#handleClientEvent(event);
|
|
32125
32293
|
});
|
|
32126
32294
|
return client;
|
|
32127
32295
|
}
|
|
32128
32296
|
#primeSession() {
|
|
32129
|
-
if (!this.sessionEndpoint) return;
|
|
32297
|
+
if (!this.sessionEndpoint && !this.embedId) return;
|
|
32130
32298
|
const client = this.#ensureClient();
|
|
32131
32299
|
const generation = this.#conversationGeneration;
|
|
32132
32300
|
this.#setSessionState("loading");
|
|
@@ -32250,23 +32418,30 @@ var NoodleAssistantElement = class extends HTMLElementBase {
|
|
|
32250
32418
|
}
|
|
32251
32419
|
const detail = error51 instanceof AssistantClientError ? error51.detail : { code: fallbackCode, retryable: false };
|
|
32252
32420
|
this.#appendRecoveryError(
|
|
32253
|
-
detail.code === "session_expired" ? this.#appearance.labels.sessionExpired : this.#appearance.labels.unavailable
|
|
32421
|
+
detail.code === "session_expired" ? this.#appearance.labels.sessionExpired : this.#appearance.labels.unavailable,
|
|
32422
|
+
// A spent or switched-off daily budget offers no retry. Reconnecting cannot succeed, and every
|
|
32423
|
+
// open tab trying is exactly the load the cap was set to refuse — so the button goes, not just
|
|
32424
|
+
// the alarming words. A spent *session* keeps it: reconnect mints a fresh one, which is the fix.
|
|
32425
|
+
detail.retryable !== false || detail.serviceCode === void 0
|
|
32254
32426
|
);
|
|
32255
32427
|
this.#dispatchError(detail);
|
|
32256
32428
|
}
|
|
32257
|
-
#appendRecoveryError(message) {
|
|
32429
|
+
#appendRecoveryError(message, offerRetry = true) {
|
|
32258
32430
|
if (!this.#messages) return;
|
|
32259
32431
|
this.#messages.querySelector(".conversation-error")?.remove();
|
|
32260
32432
|
const error51 = document.createElement("section");
|
|
32261
32433
|
error51.className = "conversation-error";
|
|
32262
|
-
error51.setAttribute("role", "alert");
|
|
32434
|
+
error51.setAttribute("role", offerRetry ? "alert" : "status");
|
|
32263
32435
|
const text3 = document.createElement("p");
|
|
32264
32436
|
text3.textContent = message;
|
|
32265
|
-
|
|
32266
|
-
|
|
32267
|
-
|
|
32268
|
-
|
|
32269
|
-
|
|
32437
|
+
error51.append(text3);
|
|
32438
|
+
if (offerRetry) {
|
|
32439
|
+
const retry = document.createElement("button");
|
|
32440
|
+
retry.type = "button";
|
|
32441
|
+
retry.textContent = this.#appearance.labels.reconnect;
|
|
32442
|
+
retry.addEventListener("click", () => this.reconnect());
|
|
32443
|
+
error51.append(retry);
|
|
32444
|
+
}
|
|
32270
32445
|
this.#messages.append(error51);
|
|
32271
32446
|
this.#revealLatest();
|
|
32272
32447
|
}
|