@noodleseed/assistant 1.14.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/dist/{chunk-RITHSCTD.js → chunk-RRJIK473.js} +89 -20
- package/dist/chunk-RRJIK473.js.map +1 -0
- package/dist/{chunk-7WXGZJCA.js → chunk-SUHNNWKP.js} +36 -17
- package/dist/{chunk-7WXGZJCA.js.map → chunk-SUHNNWKP.js.map} +1 -1
- 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 +84 -19
- 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 +119 -33
- 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 +95 -23
- 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 +128 -36
- 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-RITHSCTD.js.map +0 -1
|
@@ -22,6 +22,14 @@ interface AssistantErrorDetail {
|
|
|
22
22
|
readonly code: string;
|
|
23
23
|
readonly status?: number;
|
|
24
24
|
readonly retryable: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* The service's own refusal code, when it sent one — e.g. `daily_turn_budget_exhausted`.
|
|
27
|
+
*
|
|
28
|
+
* Additive beside `code` rather than replacing it: `code` is a published closed union that shipped
|
|
29
|
+
* consumers already switch on, so remapping it would break them. This is how a renderer tells a
|
|
30
|
+
* capacity decision apart from a fault.
|
|
31
|
+
*/
|
|
32
|
+
readonly serviceCode?: string;
|
|
25
33
|
}
|
|
26
34
|
|
|
27
35
|
interface NamedEvent<Name extends string, Data> {
|
|
@@ -165,6 +173,20 @@ interface AssistantChatState {
|
|
|
165
173
|
readonly error?: AssistantChatError;
|
|
166
174
|
}
|
|
167
175
|
|
|
176
|
+
/** The exclusive choice, expressed so the wrong pair does not typecheck in the first place. */
|
|
177
|
+
type AssistantSessionSourceOptions = {
|
|
178
|
+
/** The customer backend route that exchanges an embed secret for a session. */
|
|
179
|
+
readonly sessionEndpoint: string;
|
|
180
|
+
readonly embedId?: undefined;
|
|
181
|
+
readonly serviceUrl?: undefined;
|
|
182
|
+
} | {
|
|
183
|
+
/** The non-secret public embed id `noodle deploy` printed. Safe in page source. */
|
|
184
|
+
readonly embedId: string;
|
|
185
|
+
/** Defaults to the hosted service; set it for a dev or self-hosted deployment. */
|
|
186
|
+
readonly serviceUrl?: string | undefined;
|
|
187
|
+
readonly sessionEndpoint?: undefined;
|
|
188
|
+
};
|
|
189
|
+
|
|
168
190
|
type AssistantContextValue = string | number | boolean | null;
|
|
169
191
|
type AssistantContext = Readonly<Record<string, AssistantContextValue>>;
|
|
170
192
|
interface AssistantClientContext {
|
|
@@ -192,8 +214,7 @@ interface AssistantSessionResponse {
|
|
|
192
214
|
readonly configuration?: AssistantConfiguration;
|
|
193
215
|
}
|
|
194
216
|
|
|
195
|
-
interface
|
|
196
|
-
readonly sessionEndpoint: string;
|
|
217
|
+
interface AssistantClientBehaviorOptions<TPageContext extends AssistantPageContext = AssistantContext> {
|
|
197
218
|
readonly fetch?: typeof fetch;
|
|
198
219
|
/** Untrusted page context sent only when exchanging a session. */
|
|
199
220
|
readonly context?: AssistantContext;
|
|
@@ -204,6 +225,12 @@ interface CreateAssistantClientOptions<TPageContext extends AssistantPageContext
|
|
|
204
225
|
/** Latest renderer-selected summary included as untrusted data on every message turn. */
|
|
205
226
|
readonly modelContext?: AssistantModelContextUpdate;
|
|
206
227
|
}
|
|
228
|
+
/**
|
|
229
|
+
* How the client is mounted: through the customer's backend (`sessionEndpoint`) or, on a public page
|
|
230
|
+
* with no backend at all, straight to the service with a non-secret `embedId`. The two are exclusive —
|
|
231
|
+
* see `session-source.ts` for why they are not merged.
|
|
232
|
+
*/
|
|
233
|
+
type CreateAssistantClientOptions<TPageContext extends AssistantPageContext = AssistantContext> = AssistantClientBehaviorOptions<TPageContext> & AssistantSessionSourceOptions;
|
|
207
234
|
interface AssistantClient<TPageContext extends AssistantPageContext = AssistantContext> {
|
|
208
235
|
subscribe(listener: (event: AssistantClientEvent) => void): () => void;
|
|
209
236
|
/** Subscribe to detached AI SDK UIMessage state; the listener receives the current state immediately. */
|
|
@@ -22,6 +22,14 @@ interface AssistantErrorDetail {
|
|
|
22
22
|
readonly code: string;
|
|
23
23
|
readonly status?: number;
|
|
24
24
|
readonly retryable: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* The service's own refusal code, when it sent one — e.g. `daily_turn_budget_exhausted`.
|
|
27
|
+
*
|
|
28
|
+
* Additive beside `code` rather than replacing it: `code` is a published closed union that shipped
|
|
29
|
+
* consumers already switch on, so remapping it would break them. This is how a renderer tells a
|
|
30
|
+
* capacity decision apart from a fault.
|
|
31
|
+
*/
|
|
32
|
+
readonly serviceCode?: string;
|
|
25
33
|
}
|
|
26
34
|
|
|
27
35
|
interface NamedEvent<Name extends string, Data> {
|
|
@@ -165,6 +173,20 @@ interface AssistantChatState {
|
|
|
165
173
|
readonly error?: AssistantChatError;
|
|
166
174
|
}
|
|
167
175
|
|
|
176
|
+
/** The exclusive choice, expressed so the wrong pair does not typecheck in the first place. */
|
|
177
|
+
type AssistantSessionSourceOptions = {
|
|
178
|
+
/** The customer backend route that exchanges an embed secret for a session. */
|
|
179
|
+
readonly sessionEndpoint: string;
|
|
180
|
+
readonly embedId?: undefined;
|
|
181
|
+
readonly serviceUrl?: undefined;
|
|
182
|
+
} | {
|
|
183
|
+
/** The non-secret public embed id `noodle deploy` printed. Safe in page source. */
|
|
184
|
+
readonly embedId: string;
|
|
185
|
+
/** Defaults to the hosted service; set it for a dev or self-hosted deployment. */
|
|
186
|
+
readonly serviceUrl?: string | undefined;
|
|
187
|
+
readonly sessionEndpoint?: undefined;
|
|
188
|
+
};
|
|
189
|
+
|
|
168
190
|
type AssistantContextValue = string | number | boolean | null;
|
|
169
191
|
type AssistantContext = Readonly<Record<string, AssistantContextValue>>;
|
|
170
192
|
interface AssistantClientContext {
|
|
@@ -192,8 +214,7 @@ interface AssistantSessionResponse {
|
|
|
192
214
|
readonly configuration?: AssistantConfiguration;
|
|
193
215
|
}
|
|
194
216
|
|
|
195
|
-
interface
|
|
196
|
-
readonly sessionEndpoint: string;
|
|
217
|
+
interface AssistantClientBehaviorOptions<TPageContext extends AssistantPageContext = AssistantContext> {
|
|
197
218
|
readonly fetch?: typeof fetch;
|
|
198
219
|
/** Untrusted page context sent only when exchanging a session. */
|
|
199
220
|
readonly context?: AssistantContext;
|
|
@@ -204,6 +225,12 @@ interface CreateAssistantClientOptions<TPageContext extends AssistantPageContext
|
|
|
204
225
|
/** Latest renderer-selected summary included as untrusted data on every message turn. */
|
|
205
226
|
readonly modelContext?: AssistantModelContextUpdate;
|
|
206
227
|
}
|
|
228
|
+
/**
|
|
229
|
+
* How the client is mounted: through the customer's backend (`sessionEndpoint`) or, on a public page
|
|
230
|
+
* with no backend at all, straight to the service with a non-secret `embedId`. The two are exclusive —
|
|
231
|
+
* see `session-source.ts` for why they are not merged.
|
|
232
|
+
*/
|
|
233
|
+
type CreateAssistantClientOptions<TPageContext extends AssistantPageContext = AssistantContext> = AssistantClientBehaviorOptions<TPageContext> & AssistantSessionSourceOptions;
|
|
207
234
|
interface AssistantClient<TPageContext extends AssistantPageContext = AssistantContext> {
|
|
208
235
|
subscribe(listener: (event: AssistantClientEvent) => void): () => void;
|
|
209
236
|
/** Subscribe to detached AI SDK UIMessage state; the listener receives the current state immediately. */
|
package/dist/client.cjs
CHANGED
|
@@ -23102,6 +23102,21 @@ function hasToJsonProperty(value) {
|
|
|
23102
23102
|
return false;
|
|
23103
23103
|
}
|
|
23104
23104
|
|
|
23105
|
+
// src/session-source.ts
|
|
23106
|
+
var NOODLE_CLOUD_URL = "https://cloud.noodleseed.dev";
|
|
23107
|
+
var PUBLIC_SESSION_PATH = "/v1/assistant/public-sessions";
|
|
23108
|
+
function resolveSessionSource(options) {
|
|
23109
|
+
const { sessionEndpoint, embedId, serviceUrl } = options;
|
|
23110
|
+
if (Boolean(embedId) === Boolean(sessionEndpoint)) {
|
|
23111
|
+
throw new Error("pass either embedId or sessionEndpoint, not both and not neither");
|
|
23112
|
+
}
|
|
23113
|
+
if (embedId) {
|
|
23114
|
+
const base = (serviceUrl ?? NOODLE_CLOUD_URL).replace(/\/+$/, "");
|
|
23115
|
+
return { kind: "public", url: `${base}${PUBLIC_SESSION_PATH}`, embedId };
|
|
23116
|
+
}
|
|
23117
|
+
return { kind: "exchange", url: sessionEndpoint };
|
|
23118
|
+
}
|
|
23119
|
+
|
|
23105
23120
|
// src/transport.ts
|
|
23106
23121
|
var AssistantTransportError = class extends Error {
|
|
23107
23122
|
code = "invalid_response";
|
|
@@ -23229,6 +23244,19 @@ function isAbortError2(error51) {
|
|
|
23229
23244
|
}
|
|
23230
23245
|
|
|
23231
23246
|
// src/client.ts
|
|
23247
|
+
async function refusalCode(response) {
|
|
23248
|
+
try {
|
|
23249
|
+
const body = await response.clone().json();
|
|
23250
|
+
const code = body.code;
|
|
23251
|
+
return typeof code === "string" ? code : void 0;
|
|
23252
|
+
} catch {
|
|
23253
|
+
return void 0;
|
|
23254
|
+
}
|
|
23255
|
+
}
|
|
23256
|
+
var UNRETRYABLE_SERVICE_CODES = /* @__PURE__ */ new Set([
|
|
23257
|
+
"daily_turn_budget_exhausted",
|
|
23258
|
+
"daily_session_budget_exhausted"
|
|
23259
|
+
]);
|
|
23232
23260
|
var AssistantClientError = class extends Error {
|
|
23233
23261
|
detail;
|
|
23234
23262
|
constructor(detail, message, options) {
|
|
@@ -23238,7 +23266,7 @@ var AssistantClientError = class extends Error {
|
|
|
23238
23266
|
}
|
|
23239
23267
|
};
|
|
23240
23268
|
var DefaultAssistantClient = class {
|
|
23241
|
-
#
|
|
23269
|
+
#source;
|
|
23242
23270
|
#fetch;
|
|
23243
23271
|
#listeners = /* @__PURE__ */ new Set();
|
|
23244
23272
|
#chat = new AssistantChatStateStore();
|
|
@@ -23250,8 +23278,7 @@ var DefaultAssistantClient = class {
|
|
|
23250
23278
|
#active;
|
|
23251
23279
|
#pendingAppInteractions = /* @__PURE__ */ new Map();
|
|
23252
23280
|
constructor(options) {
|
|
23253
|
-
|
|
23254
|
-
this.#sessionEndpoint = options.sessionEndpoint;
|
|
23281
|
+
this.#source = resolveSessionSource(options);
|
|
23255
23282
|
this.#fetch = options.fetch ?? ((input, init) => globalThis.fetch(input, init));
|
|
23256
23283
|
this.#context = options.context ? { ...options.context } : void 0;
|
|
23257
23284
|
this.#modelContext = options.modelContext ? copyAssistantModelContext(options.modelContext) : void 0;
|
|
@@ -23467,11 +23494,14 @@ var DefaultAssistantClient = class {
|
|
|
23467
23494
|
return;
|
|
23468
23495
|
}
|
|
23469
23496
|
if (!response.ok) {
|
|
23497
|
+
const serviceCode = await refusalCode(response);
|
|
23470
23498
|
throw clientError(
|
|
23471
23499
|
"turn_failed",
|
|
23472
23500
|
`Assistant turn failed (${response.status})`,
|
|
23473
23501
|
false,
|
|
23474
|
-
response.status
|
|
23502
|
+
response.status,
|
|
23503
|
+
void 0,
|
|
23504
|
+
serviceCode
|
|
23475
23505
|
);
|
|
23476
23506
|
}
|
|
23477
23507
|
await this.#consume(response);
|
|
@@ -23510,23 +23540,25 @@ var DefaultAssistantClient = class {
|
|
|
23510
23540
|
}
|
|
23511
23541
|
}
|
|
23512
23542
|
async #createSession(signal) {
|
|
23513
|
-
const
|
|
23514
|
-
|
|
23515
|
-
|
|
23516
|
-
|
|
23517
|
-
|
|
23518
|
-
|
|
23519
|
-
|
|
23520
|
-
|
|
23521
|
-
|
|
23522
|
-
|
|
23523
|
-
);
|
|
23543
|
+
const source = this.#source;
|
|
23544
|
+
const response = await this.#requestSession(source, {
|
|
23545
|
+
method: "POST",
|
|
23546
|
+
headers: { Accept: "application/json", "Content-Type": "application/json" },
|
|
23547
|
+
body: JSON.stringify(
|
|
23548
|
+
source.kind === "public" ? { embedId: source.embedId } : this.#context ? { context: this.#context } : {}
|
|
23549
|
+
),
|
|
23550
|
+
credentials: source.kind === "public" ? "omit" : "same-origin",
|
|
23551
|
+
signal
|
|
23552
|
+
});
|
|
23524
23553
|
if (!response.ok) {
|
|
23554
|
+
const serviceCode = await refusalCode(response);
|
|
23525
23555
|
throw clientError(
|
|
23526
23556
|
"session_failed",
|
|
23527
23557
|
`Assistant session failed (${response.status})`,
|
|
23528
|
-
|
|
23529
|
-
response.status
|
|
23558
|
+
serviceCode === void 0 || !UNRETRYABLE_SERVICE_CODES.has(serviceCode),
|
|
23559
|
+
response.status,
|
|
23560
|
+
void 0,
|
|
23561
|
+
serviceCode
|
|
23530
23562
|
);
|
|
23531
23563
|
}
|
|
23532
23564
|
let value;
|
|
@@ -23583,6 +23615,34 @@ var DefaultAssistantClient = class {
|
|
|
23583
23615
|
);
|
|
23584
23616
|
}
|
|
23585
23617
|
}
|
|
23618
|
+
/**
|
|
23619
|
+
* The mint, with the one failure a public page hits that an in-app embed cannot.
|
|
23620
|
+
*
|
|
23621
|
+
* A page that loads the embed script and then blocks `connect-src` fails here as a bare network
|
|
23622
|
+
* rejection — no status, no body, nothing to read. "Assistant request failed" sends a developer
|
|
23623
|
+
* hunting through their own code; naming the directive and the origin ends the search. The message
|
|
23624
|
+
* covers a plain outage too, because from inside the browser the two are indistinguishable.
|
|
23625
|
+
*/
|
|
23626
|
+
async #requestSession(source, init) {
|
|
23627
|
+
try {
|
|
23628
|
+
return await this.#fetch(source.url, init);
|
|
23629
|
+
} catch (error51) {
|
|
23630
|
+
if (init.signal?.aborted) throw error51;
|
|
23631
|
+
if (source.kind !== "public") {
|
|
23632
|
+
throw clientError("session_failed", "Assistant request failed", true, void 0, {
|
|
23633
|
+
cause: error51
|
|
23634
|
+
});
|
|
23635
|
+
}
|
|
23636
|
+
throw clientError(
|
|
23637
|
+
"session_failed",
|
|
23638
|
+
`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).`,
|
|
23639
|
+
true,
|
|
23640
|
+
void 0,
|
|
23641
|
+
{ cause: error51 },
|
|
23642
|
+
"blocked_by_page"
|
|
23643
|
+
);
|
|
23644
|
+
}
|
|
23645
|
+
}
|
|
23586
23646
|
async #request(input, init, failureCode) {
|
|
23587
23647
|
try {
|
|
23588
23648
|
return await this.#fetch(input, init);
|
|
@@ -23704,9 +23764,14 @@ async function readStableErrorCode(response) {
|
|
|
23704
23764
|
if (!isRecord4(value) || typeof value.code !== "string") return void 0;
|
|
23705
23765
|
return /^[A-Za-z0-9_.-]{1,64}$/.test(value.code) ? value.code : void 0;
|
|
23706
23766
|
}
|
|
23707
|
-
function clientError(code, message, retryable, status, options) {
|
|
23767
|
+
function clientError(code, message, retryable, status, options, serviceCode) {
|
|
23708
23768
|
return new AssistantClientError(
|
|
23709
|
-
{
|
|
23769
|
+
{
|
|
23770
|
+
code,
|
|
23771
|
+
...status === void 0 ? {} : { status },
|
|
23772
|
+
retryable,
|
|
23773
|
+
...serviceCode === void 0 ? {} : { serviceCode }
|
|
23774
|
+
},
|
|
23710
23775
|
message,
|
|
23711
23776
|
options
|
|
23712
23777
|
);
|