@noodleseed/one 0.133.0 → 0.134.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/bin.js +0 -0
- package/dist/commands/author-loop.d.ts.map +1 -1
- package/dist/commands/author-loop.js +3 -3
- package/dist/commands/author-loop.js.map +1 -1
- package/dist/commands/devtools.js +1 -1
- package/dist/commands/devtools.js.map +1 -1
- package/dist/commands/local-readiness.d.ts +2 -2
- package/dist/commands/local-readiness.d.ts.map +1 -1
- package/dist/commands/local-readiness.js +15 -7
- package/dist/commands/local-readiness.js.map +1 -1
- package/dist/dev-local-runtime.d.ts +33 -0
- package/dist/dev-local-runtime.d.ts.map +1 -0
- package/dist/dev-local-runtime.js +53 -0
- package/dist/dev-local-runtime.js.map +1 -0
- package/dist/dev.d.ts +4 -0
- package/dist/dev.d.ts.map +1 -1
- package/dist/dev.js +109 -31
- package/dist/dev.js.map +1 -1
- package/dist/devtools-harness.js +5 -3
- package/dist/devtools-harness.js.map +1 -1
- package/dist/devtools-preview.d.ts +2 -0
- package/dist/devtools-preview.d.ts.map +1 -1
- package/dist/devtools-preview.js +15 -3
- package/dist/devtools-preview.js.map +1 -1
- package/dist/devtools-rpc-forwarder.js +1 -1
- package/dist/devtools-rpc-forwarder.js.map +1 -1
- package/dist/preview-session.d.ts.map +1 -1
- package/dist/preview-session.js +1 -0
- package/dist/preview-session.js.map +1 -1
- package/dist/react-widget-build.d.ts +4 -0
- package/dist/react-widget-build.d.ts.map +1 -1
- package/dist/react-widget-build.js +9 -0
- package/dist/react-widget-build.js.map +1 -1
- package/node_modules/@noodle-borg/admission-limits/dist/client-address.d.ts +7 -0
- package/node_modules/@noodle-borg/admission-limits/dist/counter-store.d.ts +71 -0
- package/node_modules/@noodle-borg/admission-limits/dist/envelope.d.ts +67 -0
- package/node_modules/@noodle-borg/admission-limits/dist/in-memory-counter-store.d.ts +15 -0
- package/node_modules/@noodle-borg/admission-limits/dist/portable.d.ts +6 -0
- package/node_modules/@noodle-borg/admission-limits/dist/portable.js +6 -0
- package/node_modules/@noodle-borg/admission-limits/package.json +8 -0
- package/node_modules/@noodle-borg/agent-kit/dist/generated/example-files.js +3 -3
- package/node_modules/@noodle-borg/agent-kit/dist/skill-embedded-assistant-ref.js +4 -1
- package/node_modules/@noodle-borg/agent-kit/package.json +1 -1
- package/node_modules/@noodle-borg/assistant-gateway/dist/artifact-projection.d.ts +21 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/assistant-context.d.ts +25 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/assistant-customer-issuer.d.ts +3 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/assistant-customer-routing.d.ts +19 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/assistant-guide.d.ts +39 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/assistant-interaction-state.d.ts +181 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/assistant-interactive.d.ts +99 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/assistant-interactive.js +1 -1
- package/node_modules/@noodle-borg/assistant-gateway/dist/assistant-presentation.d.ts +35 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/assistant-sensitive-values.d.ts +3 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/assistant-store.d.ts +226 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/assistant-store.js +1 -1
- package/node_modules/@noodle-borg/assistant-gateway/dist/elevation-store.d.ts +70 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/elevation.d.ts +68 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/embed-script.d.ts +24 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/embed-store.d.ts +73 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/in-memory-elevation-store.d.ts +21 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/in-memory-embed-store.d.ts +28 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/portable.d.ts +24 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/public-session.d.ts +56 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/public-surface.d.ts +21 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/public-surface.js +2 -1
- package/node_modules/@noodle-borg/assistant-gateway/dist/public-turn.d.ts +44 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/session-resume.d.ts +22 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/surface-budget.d.ts +16 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/surface-budget.js +1 -1
- package/node_modules/@noodle-borg/assistant-gateway/dist/tenant-ref.d.ts +13 -0
- package/node_modules/@noodle-borg/authoring/dist/assistant.d.ts +7 -0
- package/node_modules/@noodle-borg/authoring/dist/assistant.js +3 -0
- package/node_modules/@noodle-borg/compiler/dist/manifest/schema.d.ts +4 -0
- package/node_modules/@noodle-borg/compiler/dist/manifest/schema.js +6 -5
- package/node_modules/@noodle-borg/protocol/dist/mapping.js +8 -0
- package/node_modules/@noodle-borg/service/dist/local-options.d.ts +9 -0
- package/node_modules/@noodle-borg/service/dist/routes/assistant-agent.js +14 -3
- package/node_modules/@noodle-borg/service/dist/routes/assistant-embeds.js +1 -1
- package/node_modules/@noodle-borg/service/dist/routes/assistant-public-session.js +1 -1
- package/node_modules/@noodle-borg/service/dist/routes/assistant-public-turn.js +1 -1
- package/node_modules/@noodle-borg/service/dist/routes/assistant.js +1 -1
- package/node_modules/@noodle-borg/service/dist/serve-local.js +2 -3
- package/node_modules/@noodle-borg/service/package.json +1 -1
- package/node_modules/@noodleseed/assistant/README.md +4 -1
- package/package.json +5 -3
- package/node_modules/@noodle-borg/admission-limits/dist/index.js +0 -15
- package/node_modules/@noodle-borg/admission-limits/dist/postgres-counter-store.js +0 -68
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { TenantRef } from './tenant-ref.js';
|
|
2
|
+
/**
|
|
3
|
+
* Mid-conversation sign-in on a `mixed` surface (ADR 0201, 5.6b).
|
|
4
|
+
*
|
|
5
|
+
* **Not an interaction record, deliberately.** The plan proposed a new `auth_requested` kind on the
|
|
6
|
+
* existing interaction machinery, but that machinery is *visitor-resolvable*: accept/decline/cancel are
|
|
7
|
+
* driven from the widget. An elevation resolved by a visitor click would be an elevation with no
|
|
8
|
+
* credentials, so reusing it would have meant bolting a "this kind is not visitor-resolvable" exception
|
|
9
|
+
* onto every resolution path and hoping none was missed. A separate record has no such path to forget.
|
|
10
|
+
*
|
|
11
|
+
* **The continuation is a capability that travels through the browser**, so possession alone must be
|
|
12
|
+
* worthless: spending one also requires the customer's client credentials, and the claim checks that the
|
|
13
|
+
* client's tenant owns the session. It is stored hashed for the same reason session tokens are — the
|
|
14
|
+
* store never holds a value that could be replayed if it leaked.
|
|
15
|
+
*/
|
|
16
|
+
export interface AssistantElevationRecord {
|
|
17
|
+
readonly id: string;
|
|
18
|
+
readonly sessionId: string;
|
|
19
|
+
readonly tenant: TenantRef;
|
|
20
|
+
/** The capability the visitor was refused, carried so the audit says what they signed in for. */
|
|
21
|
+
readonly tool: string;
|
|
22
|
+
readonly continuationHash: string;
|
|
23
|
+
readonly createdAt: string;
|
|
24
|
+
readonly expiresAt: string;
|
|
25
|
+
readonly claimedAt?: string;
|
|
26
|
+
}
|
|
27
|
+
export type AssistantElevationClaim = {
|
|
28
|
+
readonly ok: true;
|
|
29
|
+
readonly elevation: AssistantElevationRecord;
|
|
30
|
+
} | {
|
|
31
|
+
readonly ok: false;
|
|
32
|
+
/**
|
|
33
|
+
* One code per refusal, because they are operationally different: `unknown` is a bad or already
|
|
34
|
+
* spent continuation, `expired` is a visitor who took too long, and `tenant_mismatch` is a client
|
|
35
|
+
* spending a continuation for a conversation it does not own — the last is the one worth alerting
|
|
36
|
+
* on, so it must not collapse into the first.
|
|
37
|
+
*/
|
|
38
|
+
readonly reason: 'unknown' | 'expired' | 'tenant_mismatch';
|
|
39
|
+
};
|
|
40
|
+
/** Ten minutes: long enough for a real login round trip, short enough that a leaked value is stale. */
|
|
41
|
+
export declare const ASSISTANT_ELEVATION_TTL_MS: number;
|
|
42
|
+
export interface AssistantElevationStore {
|
|
43
|
+
/**
|
|
44
|
+
* Open one elevation for a session. At most one may be live at a time — a second request supersedes
|
|
45
|
+
* the first rather than accumulating, so a model that asks twice cannot mint unbounded capabilities.
|
|
46
|
+
*/
|
|
47
|
+
request(input: {
|
|
48
|
+
readonly sessionId: string;
|
|
49
|
+
readonly tenant: TenantRef;
|
|
50
|
+
readonly tool: string;
|
|
51
|
+
readonly now: Date;
|
|
52
|
+
}): Promise<{
|
|
53
|
+
readonly elevation: AssistantElevationRecord;
|
|
54
|
+
readonly continuation: string;
|
|
55
|
+
}>;
|
|
56
|
+
/**
|
|
57
|
+
* Spend a continuation exactly once. Atomic: two backends racing the same value must not both win,
|
|
58
|
+
* which is why this is one store seam rather than a read-then-write in the route.
|
|
59
|
+
*/
|
|
60
|
+
claim(input: {
|
|
61
|
+
readonly continuation: string;
|
|
62
|
+
readonly tenant: TenantRef;
|
|
63
|
+
readonly now: Date;
|
|
64
|
+
}): Promise<AssistantElevationClaim>;
|
|
65
|
+
}
|
|
66
|
+
export declare function elevationContinuation(): string;
|
|
67
|
+
export declare function elevationDigest(value: string): string;
|
|
68
|
+
export declare function elevationHashesEqual(left: string, right: string): boolean;
|
|
69
|
+
export declare function sameTenant(left: TenantRef, right: TenantRef): boolean;
|
|
70
|
+
//# sourceMappingURL=elevation-store.d.ts.map
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { AssistantPendingResume, AssistantSessionRecord } from './assistant-store.js';
|
|
2
|
+
import type { AssistantElevationStore } from './elevation-store.js';
|
|
3
|
+
import type { TenantRef } from './tenant-ref.js';
|
|
4
|
+
/**
|
|
5
|
+
* The elevation decision, with no HTTP in it.
|
|
6
|
+
*
|
|
7
|
+
* Kept transport-free for the same reason `mintPublicSession` is: the interesting part is an ordering of
|
|
8
|
+
* refusals, and an ordering is only testable without standing up a server. `packages/service` adapts.
|
|
9
|
+
*
|
|
10
|
+
* **The order is the contract.** The continuation is claimed *before* the session is touched, and the
|
|
11
|
+
* claim is tenant-checked, so a client reaching for a conversation it does not own never reaches the
|
|
12
|
+
* mutation at all. Claiming first also means a refused elevation cannot leave a spent continuation
|
|
13
|
+
* behind — and a spent one cannot be replayed, which is the same statement read from the other side.
|
|
14
|
+
*/
|
|
15
|
+
export interface ElevationPorts {
|
|
16
|
+
readonly elevations: Pick<AssistantElevationStore, 'claim'>;
|
|
17
|
+
readonly elevateSession: (input: {
|
|
18
|
+
readonly sessionId: string;
|
|
19
|
+
readonly caller: AssistantSessionRecord['caller'];
|
|
20
|
+
readonly clientId: string;
|
|
21
|
+
readonly origin: string;
|
|
22
|
+
readonly customerRouting?: AssistantSessionRecord['customerRouting'];
|
|
23
|
+
readonly pendingResume?: AssistantPendingResume;
|
|
24
|
+
readonly now: Date;
|
|
25
|
+
}) => Promise<{
|
|
26
|
+
readonly ok: true;
|
|
27
|
+
readonly session: AssistantSessionRecord;
|
|
28
|
+
readonly token: string;
|
|
29
|
+
} | {
|
|
30
|
+
readonly ok: false;
|
|
31
|
+
readonly reason: 'unknown_session' | 'already_elevated';
|
|
32
|
+
}>;
|
|
33
|
+
readonly now: () => Date;
|
|
34
|
+
}
|
|
35
|
+
export interface ElevationRequest {
|
|
36
|
+
/** Presented by the customer's backend. Possession alone is not enough; see the route. */
|
|
37
|
+
readonly continuation: string;
|
|
38
|
+
/** The tenant of the *authenticated client*, never a value the caller supplied loose. */
|
|
39
|
+
readonly tenant: TenantRef;
|
|
40
|
+
readonly caller: AssistantSessionRecord['caller'];
|
|
41
|
+
/** The authenticated client's id: rebinds the session's issuer basis on success (ADR 0152). */
|
|
42
|
+
readonly clientId: string;
|
|
43
|
+
/**
|
|
44
|
+
* Where the conversation continues: allowlist-validated by the route BEFORE the ticket is
|
|
45
|
+
* claimed, then re-pinned onto the session so its CORS follows the visitor's post-login page.
|
|
46
|
+
*/
|
|
47
|
+
readonly origin: string;
|
|
48
|
+
/** Backend-verified customer routes, validated by the route; absent means unchanged. */
|
|
49
|
+
readonly customerRouting?: AssistantSessionRecord['customerRouting'];
|
|
50
|
+
/**
|
|
51
|
+
* Arm the one-shot resume of the intercepted tool on success. Only an explicit `true` arms —
|
|
52
|
+
* the route owns the default policy, and probes (the doctor's synthetic elevation) pass nothing.
|
|
53
|
+
*/
|
|
54
|
+
readonly resume?: boolean;
|
|
55
|
+
}
|
|
56
|
+
export type ElevationResult = {
|
|
57
|
+
readonly ok: true;
|
|
58
|
+
readonly session: AssistantSessionRecord;
|
|
59
|
+
readonly token: string;
|
|
60
|
+
/** What the visitor was refused before signing in, for the audit trail. */
|
|
61
|
+
readonly tool: string;
|
|
62
|
+
} | {
|
|
63
|
+
readonly ok: false;
|
|
64
|
+
readonly status: 403 | 409;
|
|
65
|
+
readonly code: 'elevation_ticket_invalid' | 'elevation_ticket_expired' | 'elevation_tenant_mismatch' | 'elevation_session_unavailable' | 'elevation_already_signed_in';
|
|
66
|
+
};
|
|
67
|
+
export declare function completeElevation(request: ElevationRequest, ports: ElevationPorts): Promise<ElevationResult>;
|
|
68
|
+
//# sourceMappingURL=elevation.d.ts.map
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The browser embed script, read from the published `@noodleseed/assistant` build.
|
|
3
|
+
*
|
|
4
|
+
* Served from the service origin rather than a CDN (ADR 0201): there is no new release lane to run, the
|
|
5
|
+
* script is always the one that matches the deployed service, and a CDN can front the URL later without
|
|
6
|
+
* changing a single byte a customer pasted.
|
|
7
|
+
*
|
|
8
|
+
* Read once and cached. The file is immutable for the life of the process — it ships inside the image —
|
|
9
|
+
* so re-reading it per request would buy nothing but syscalls.
|
|
10
|
+
*/
|
|
11
|
+
export interface EmbedScript {
|
|
12
|
+
readonly bytes: Buffer;
|
|
13
|
+
/** Content-derived, so a redeployed service with an unchanged bundle keeps browsers' caches warm. */
|
|
14
|
+
readonly etag: string;
|
|
15
|
+
}
|
|
16
|
+
export type EmbedScriptResult = {
|
|
17
|
+
readonly ok: true;
|
|
18
|
+
readonly script: EmbedScript;
|
|
19
|
+
} | {
|
|
20
|
+
readonly ok: false;
|
|
21
|
+
readonly reason: string;
|
|
22
|
+
};
|
|
23
|
+
export declare function loadEmbedScript(): EmbedScriptResult;
|
|
24
|
+
//# sourceMappingURL=embed-script.d.ts.map
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public embed identifiers.
|
|
3
|
+
*
|
|
4
|
+
* These live in their own table rather than as a flag on the authenticated `assistant_clients` row, and
|
|
5
|
+
* the reason is structural rather than tidiness: a public embed id is printed in page source, so it must
|
|
6
|
+
* never be a credential. A record with no secret column cannot accidentally acquire one — there is
|
|
7
|
+
* nothing for a future code path to populate, hash, or compare. Reusing the client table would have
|
|
8
|
+
* inherited its tooling for free while keeping a `secret_hash` one bug away from a public identifier.
|
|
9
|
+
*
|
|
10
|
+
* The id is stable across deploys. A customer pastes the snippet once; redeploys and rollbacks swap the
|
|
11
|
+
* projection underneath a page that never changes. Origins, capabilities, and UI therefore come from the
|
|
12
|
+
* *active* deployment at mint time, never from the embed record.
|
|
13
|
+
*/
|
|
14
|
+
export interface PublicEmbedRecord {
|
|
15
|
+
/** Printed in page source. Non-secret by construction. */
|
|
16
|
+
readonly embedId: string;
|
|
17
|
+
readonly org: string;
|
|
18
|
+
readonly app: string;
|
|
19
|
+
readonly env: string;
|
|
20
|
+
/**
|
|
21
|
+
* Which surface of the assistant this id addresses, so budgets and kill switches key per surface.
|
|
22
|
+
* Provisioning-time record only: `ensure` never updates a live row, so this goes stale when a
|
|
23
|
+
* redeploy changes the mode — readers report the active deployment's mode instead.
|
|
24
|
+
*/
|
|
25
|
+
readonly surfaceMode: 'public' | 'mixed';
|
|
26
|
+
readonly createdAt: Date;
|
|
27
|
+
readonly revokedAt?: Date;
|
|
28
|
+
/**
|
|
29
|
+
* Operator overrides for this surface's daily caps. Undefined means "use the deployment defaults";
|
|
30
|
+
* `0` is a real value and is the kill switch, so the two must stay distinguishable everywhere.
|
|
31
|
+
*/
|
|
32
|
+
readonly turnsPerDay?: number;
|
|
33
|
+
readonly mintsPerDay?: number;
|
|
34
|
+
}
|
|
35
|
+
/** What an operator may change about a surface. An omitted field leaves that cap as it was. */
|
|
36
|
+
export interface PublicEmbedBudget {
|
|
37
|
+
readonly turnsPerDay?: number;
|
|
38
|
+
readonly mintsPerDay?: number;
|
|
39
|
+
}
|
|
40
|
+
export interface PublicEmbedStore {
|
|
41
|
+
/**
|
|
42
|
+
* Return the existing id for this tenant surface, or create one. Deploy calls this, so it must be
|
|
43
|
+
* idempotent: a redeploy that minted a fresh id would silently break every page already carrying the
|
|
44
|
+
* old one.
|
|
45
|
+
*/
|
|
46
|
+
ensure(input: {
|
|
47
|
+
readonly org: string;
|
|
48
|
+
readonly app: string;
|
|
49
|
+
readonly env: string;
|
|
50
|
+
readonly surfaceMode: 'public' | 'mixed';
|
|
51
|
+
readonly now: Date;
|
|
52
|
+
}): Promise<PublicEmbedRecord>;
|
|
53
|
+
/** Resolve an id presented by a browser. Revoked ids resolve to undefined. */
|
|
54
|
+
lookup(embedId: string): Promise<PublicEmbedRecord | undefined>;
|
|
55
|
+
/**
|
|
56
|
+
* Raise, lower, or switch off a surface's daily caps. Separate from `ensure` on purpose: `ensure` runs
|
|
57
|
+
* on every deploy and never overwrites a live row, so a redeploy cannot reset an operator's decision.
|
|
58
|
+
*/
|
|
59
|
+
setBudget(embedId: string, budget: PublicEmbedBudget, now: Date): Promise<PublicEmbedRecord | undefined>;
|
|
60
|
+
list(tenant: {
|
|
61
|
+
readonly org: string;
|
|
62
|
+
readonly app: string;
|
|
63
|
+
readonly env: string;
|
|
64
|
+
}): Promise<readonly PublicEmbedRecord[]>;
|
|
65
|
+
revoke(embedId: string, now: Date): Promise<boolean>;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* `pub_` prefixed so it is self-describing in page source, logs, and support threads — someone reading a
|
|
69
|
+
* customer's HTML can tell at a glance that it is not a secret that leaked.
|
|
70
|
+
*/
|
|
71
|
+
export declare const PUBLIC_EMBED_ID_PREFIX = "pub_";
|
|
72
|
+
export declare function isPublicEmbedId(value: string): boolean;
|
|
73
|
+
//# sourceMappingURL=embed-store.d.ts.map
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type AssistantElevationClaim, type AssistantElevationRecord, type AssistantElevationStore } from './elevation-store.js';
|
|
2
|
+
import type { TenantRef } from './tenant-ref.js';
|
|
3
|
+
/** The in-memory half of the pair. Both run the shared parity suite; neither is the only proof. */
|
|
4
|
+
export declare class InMemoryAssistantElevationStore implements AssistantElevationStore {
|
|
5
|
+
#private;
|
|
6
|
+
request(input: {
|
|
7
|
+
readonly sessionId: string;
|
|
8
|
+
readonly tenant: TenantRef;
|
|
9
|
+
readonly tool: string;
|
|
10
|
+
readonly now: Date;
|
|
11
|
+
}): Promise<{
|
|
12
|
+
readonly elevation: AssistantElevationRecord;
|
|
13
|
+
readonly continuation: string;
|
|
14
|
+
}>;
|
|
15
|
+
claim(input: {
|
|
16
|
+
readonly continuation: string;
|
|
17
|
+
readonly tenant: TenantRef;
|
|
18
|
+
readonly now: Date;
|
|
19
|
+
}): Promise<AssistantElevationClaim>;
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=in-memory-elevation-store.d.ts.map
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type PublicEmbedBudget, type PublicEmbedRecord, type PublicEmbedStore } from './embed-store.js';
|
|
2
|
+
/** 120 bits of randomness, base36: unguessable enough that an id is never a discovery oracle. */
|
|
3
|
+
export declare function newPublicEmbedId(): string;
|
|
4
|
+
export declare function tenantKey(tenant: {
|
|
5
|
+
readonly org: string;
|
|
6
|
+
readonly app: string;
|
|
7
|
+
readonly env: string;
|
|
8
|
+
}): string;
|
|
9
|
+
/** Process-local embeds for local development and tests. */
|
|
10
|
+
export declare class InMemoryPublicEmbedStore implements PublicEmbedStore {
|
|
11
|
+
#private;
|
|
12
|
+
ensure(input: {
|
|
13
|
+
readonly org: string;
|
|
14
|
+
readonly app: string;
|
|
15
|
+
readonly env: string;
|
|
16
|
+
readonly surfaceMode: 'public' | 'mixed';
|
|
17
|
+
readonly now: Date;
|
|
18
|
+
}): Promise<PublicEmbedRecord>;
|
|
19
|
+
setBudget(embedId: string, budget: PublicEmbedBudget, _now: Date): Promise<PublicEmbedRecord | undefined>;
|
|
20
|
+
lookup(embedId: string): Promise<PublicEmbedRecord | undefined>;
|
|
21
|
+
list(tenant: {
|
|
22
|
+
readonly org: string;
|
|
23
|
+
readonly app: string;
|
|
24
|
+
readonly env: string;
|
|
25
|
+
}): Promise<readonly PublicEmbedRecord[]>;
|
|
26
|
+
revoke(embedId: string, now: Date): Promise<boolean>;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=in-memory-embed-store.d.ts.map
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/** Assistant gateway contracts and in-memory adapters safe for the published CLI runtime. */
|
|
2
|
+
export * from './artifact-projection.js';
|
|
3
|
+
export * from './assistant-context.js';
|
|
4
|
+
export * from './assistant-customer-issuer.js';
|
|
5
|
+
export * from './assistant-customer-routing.js';
|
|
6
|
+
export * from './assistant-guide.js';
|
|
7
|
+
export * from './assistant-interaction-state.js';
|
|
8
|
+
export * from './assistant-interactive.js';
|
|
9
|
+
export * from './assistant-presentation.js';
|
|
10
|
+
export * from './assistant-sensitive-values.js';
|
|
11
|
+
export * from './assistant-store.js';
|
|
12
|
+
export * from './elevation.js';
|
|
13
|
+
export * from './elevation-store.js';
|
|
14
|
+
export * from './embed-script.js';
|
|
15
|
+
export * from './embed-store.js';
|
|
16
|
+
export * from './in-memory-elevation-store.js';
|
|
17
|
+
export * from './in-memory-embed-store.js';
|
|
18
|
+
export * from './public-session.js';
|
|
19
|
+
export * from './public-surface.js';
|
|
20
|
+
export * from './public-turn.js';
|
|
21
|
+
export * from './session-resume.js';
|
|
22
|
+
export * from './surface-budget.js';
|
|
23
|
+
export * from './tenant-ref.js';
|
|
24
|
+
//# sourceMappingURL=portable.d.ts.map
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { AdmissionEnvelope, DailyCounterStore } from '@noodle-borg/admission-limits/portable';
|
|
2
|
+
import { type PublicEmbedRecord, type PublicEmbedStore } from './embed-store.js';
|
|
3
|
+
import type { PublicSurface } from './public-surface.js';
|
|
4
|
+
/**
|
|
5
|
+
* Minting an anonymous session for a public page.
|
|
6
|
+
*
|
|
7
|
+
* Transport-free on purpose: the hosting service owns the HTTP adapter (parse body, map result), and
|
|
8
|
+
* everything that decides *whether* a stranger gets a session lives here, behind narrow ports. That is
|
|
9
|
+
* what keeps the decision testable without a server and the service-side residue to a few lines.
|
|
10
|
+
*
|
|
11
|
+
* The refusal order is part of the contract, not an implementation detail. ADR 0201 §8 requires
|
|
12
|
+
* admission to complete before any metered work, so the store-durability and budget checks come before
|
|
13
|
+
* anything that would read deployment state or reach a model.
|
|
14
|
+
*/
|
|
15
|
+
export interface PublicSessionPorts {
|
|
16
|
+
readonly embeds: Pick<PublicEmbedStore, 'lookup'>;
|
|
17
|
+
readonly counters: DailyCounterStore;
|
|
18
|
+
/** The live surface for this tenant, read from the *active* deployment at mint time. */
|
|
19
|
+
resolveActiveSurface(embed: PublicEmbedRecord): Promise<PublicSurface | undefined>;
|
|
20
|
+
/** Persist the anonymous session and return its bearer token and expiry. */
|
|
21
|
+
createSession(input: {
|
|
22
|
+
readonly embed: PublicEmbedRecord;
|
|
23
|
+
readonly origin: string;
|
|
24
|
+
readonly subject: string;
|
|
25
|
+
readonly envelope: AdmissionEnvelope;
|
|
26
|
+
}): Promise<{
|
|
27
|
+
readonly token: string;
|
|
28
|
+
readonly expiresAt: string;
|
|
29
|
+
}>;
|
|
30
|
+
/** Opaque, deployment-scoped, unlinkable to any person. */
|
|
31
|
+
newAnonymousSubject(): string;
|
|
32
|
+
now(): Date;
|
|
33
|
+
}
|
|
34
|
+
export interface PublicSessionRequest {
|
|
35
|
+
readonly embedId: unknown;
|
|
36
|
+
readonly origin: unknown;
|
|
37
|
+
/**
|
|
38
|
+
* The visitor's address bucket (admission tier 3), already hashed by the transport — a raw address
|
|
39
|
+
* must never reach this decision, a counter key, or an audit payload. Absent when ingress could not
|
|
40
|
+
* be parsed, in which case the surface tier alone applies: fairness degrades, solvency does not.
|
|
41
|
+
*/
|
|
42
|
+
readonly addressBucket?: string | undefined;
|
|
43
|
+
}
|
|
44
|
+
export type PublicSessionResult = {
|
|
45
|
+
readonly ok: true;
|
|
46
|
+
readonly token: string;
|
|
47
|
+
readonly expiresAt: string;
|
|
48
|
+
readonly embed: PublicEmbedRecord;
|
|
49
|
+
} | {
|
|
50
|
+
readonly ok: false;
|
|
51
|
+
readonly status: number;
|
|
52
|
+
readonly code: string;
|
|
53
|
+
readonly message: string;
|
|
54
|
+
};
|
|
55
|
+
export declare function mintPublicSession(request: PublicSessionRequest, envelope: AdmissionEnvelope, ports: PublicSessionPorts): Promise<PublicSessionResult>;
|
|
56
|
+
//# sourceMappingURL=public-session.d.ts.map
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { SurfaceCapabilityRef } from './artifact-projection.js';
|
|
2
|
+
/**
|
|
3
|
+
* The one reader of "does this deployment face the public, and on what terms".
|
|
4
|
+
*
|
|
5
|
+
* Three callers ask that question and must never disagree: deploy decides whether to provision an embed
|
|
6
|
+
* id, minting checks the browser's origin against the surface, and the session's projection narrows the
|
|
7
|
+
* artifact to the surface's capabilities. When each answered locally, a surface shape one of them
|
|
8
|
+
* accepted and another rejected would silently mint sessions that could reach nothing, or provision an id
|
|
9
|
+
* for a surface that refuses to serve it.
|
|
10
|
+
*
|
|
11
|
+
* The argument is `unknown` because the assistant block arrives as compiled artifact data rather than a
|
|
12
|
+
* typed authoring value; the shape is validated here, once, rather than asserted at three call sites.
|
|
13
|
+
*/
|
|
14
|
+
export interface PublicSurface {
|
|
15
|
+
readonly mode: 'public' | 'mixed';
|
|
16
|
+
readonly origins: readonly string[];
|
|
17
|
+
readonly capabilities: readonly SurfaceCapabilityRef[];
|
|
18
|
+
readonly instructions?: string;
|
|
19
|
+
}
|
|
20
|
+
export declare function publicSurfaceOf(assistant: unknown): PublicSurface | undefined;
|
|
21
|
+
//# sourceMappingURL=public-surface.d.ts.map
|
|
@@ -3,7 +3,7 @@ export function publicSurfaceOf(assistant) {
|
|
|
3
3
|
if (!Array.isArray(surfaces))
|
|
4
4
|
return undefined;
|
|
5
5
|
for (const entry of surfaces) {
|
|
6
|
-
const { mode, origins, capabilities } = entry;
|
|
6
|
+
const { mode, origins, capabilities, instructions } = entry;
|
|
7
7
|
// A mixed surface admits strangers exactly as a public one does — sign-in only widens what a visitor
|
|
8
8
|
// reaches after elevation, so both provision an embed id and both project.
|
|
9
9
|
if (mode !== 'public' && mode !== 'mixed')
|
|
@@ -17,6 +17,7 @@ export function publicSurfaceOf(assistant) {
|
|
|
17
17
|
capabilities: Array.isArray(capabilities)
|
|
18
18
|
? capabilities
|
|
19
19
|
: [],
|
|
20
|
+
...(typeof instructions === 'string' ? { instructions } : {}),
|
|
20
21
|
};
|
|
21
22
|
}
|
|
22
23
|
return undefined;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { AdmissionEnvelope, DailyCounterStore } from '@noodle-borg/admission-limits/portable';
|
|
2
|
+
import type { AssistantTurnConsumption } from './assistant-store.js';
|
|
3
|
+
import type { PublicEmbedStore } from './embed-store.js';
|
|
4
|
+
/**
|
|
5
|
+
* Whether an anonymous visitor's next turn runs.
|
|
6
|
+
*
|
|
7
|
+
* Transport-free for the same reason {@link ./public-session.ts} is: the decision about who gets to spend
|
|
8
|
+
* a customer's model budget should be provable without standing up a server. The service adapts it.
|
|
9
|
+
*
|
|
10
|
+
* The envelope arrives per call rather than being snapshotted onto the session at mint time, and that is
|
|
11
|
+
* load-bearing: `noodle assistant budget set --turns-per-day 0` must stop conversations already under
|
|
12
|
+
* way, which is precisely the case an operator reaches for the kill switch to stop. Re-reading it each
|
|
13
|
+
* turn makes the switch fall out of the ordinary path instead of needing a second mechanism.
|
|
14
|
+
*/
|
|
15
|
+
export interface PublicTurnPorts {
|
|
16
|
+
readonly counters: DailyCounterStore;
|
|
17
|
+
/** Re-read per turn: it carries the operator's live budget, and whether the surface still exists. */
|
|
18
|
+
readonly embeds: Pick<PublicEmbedStore, 'lookup'>;
|
|
19
|
+
/** Spend one of this session's turns, atomically (admission tier 1). */
|
|
20
|
+
consumeTurn(id: string, limit: number): Promise<AssistantTurnConsumption>;
|
|
21
|
+
now(): Date;
|
|
22
|
+
}
|
|
23
|
+
export interface PublicTurnRequest {
|
|
24
|
+
readonly sessionId: string;
|
|
25
|
+
/** The surface the session was admitted through; daily spend is charged against it. */
|
|
26
|
+
readonly publicEmbedId: string;
|
|
27
|
+
readonly message: string;
|
|
28
|
+
/**
|
|
29
|
+
* The visitor's hashed address bucket (admission tier 3), or absent when ingress could not be parsed.
|
|
30
|
+
* A raw address must never reach this decision, a counter key, or an audit payload.
|
|
31
|
+
*/
|
|
32
|
+
readonly addressBucket?: string | undefined;
|
|
33
|
+
}
|
|
34
|
+
export type PublicTurnResult = {
|
|
35
|
+
readonly ok: true;
|
|
36
|
+
readonly turnCount: number;
|
|
37
|
+
} | {
|
|
38
|
+
readonly ok: false;
|
|
39
|
+
readonly status: number;
|
|
40
|
+
readonly code: string;
|
|
41
|
+
readonly message: string;
|
|
42
|
+
};
|
|
43
|
+
export declare function admitPublicTurn(request: PublicTurnRequest, defaults: AdmissionEnvelope, ports: PublicTurnPorts): Promise<PublicTurnResult>;
|
|
44
|
+
//# sourceMappingURL=public-turn.d.ts.map
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The post-sign-in resume decision and its platform message (issue #1177). When a spent ticket
|
|
3
|
+
* elevates a session, the service may run one assistant turn so the model re-attempts the
|
|
4
|
+
* intercepted intent under the new principal — the visitor signed in to get an answer, and the
|
|
5
|
+
* panel's first content should be that answer, not silence. Transport-free like the elevation
|
|
6
|
+
* decision; `packages/service` adapts.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Whether a successful elevation arms the one-shot resume. ON by default — the demo-magic case is
|
|
10
|
+
* the default experience — with the exchange-side `resume: false` as the single disable knob (it
|
|
11
|
+
* lives in the integrator's own session endpoint, the code that spends the ticket).
|
|
12
|
+
*/
|
|
13
|
+
export declare function shouldAutoResume(override: boolean | undefined): boolean;
|
|
14
|
+
/**
|
|
15
|
+
* The `[platform]` message that drives the resume turn, persisted to history as a user-role
|
|
16
|
+
* message like the interaction-resolution narration. It must countermand the interception's own
|
|
17
|
+
* guidance ("ask them to sign in, then offer to try again") — the sign-in already happened, and
|
|
18
|
+
* asking the visitor to repeat themselves is the silence this feature removes. Arguments are not
|
|
19
|
+
* replayed: the model re-derives them from history under the new principal.
|
|
20
|
+
*/
|
|
21
|
+
export declare function resumeTurnMessage(tool: string): string;
|
|
22
|
+
//# sourceMappingURL=session-resume.d.ts.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type AdmissionEnvelope } from '@noodle-borg/admission-limits/portable';
|
|
2
|
+
import type { PublicEmbedRecord } from './embed-store.js';
|
|
3
|
+
/**
|
|
4
|
+
* One surface's daily budget, resolved from the operator's override and the deployment's defaults.
|
|
5
|
+
*
|
|
6
|
+
* Per surface rather than per service, because the question a customer actually asks is "does capping
|
|
7
|
+
* my marketing bot throttle my in-app assistant" — and the answer has to be an obvious no. The override
|
|
8
|
+
* lives on the embed row, which `ensure` never overwrites, so a redeploy cannot silently reset a kill
|
|
9
|
+
* switch someone reached for.
|
|
10
|
+
*
|
|
11
|
+
* Routed through `clamp` rather than used directly: the structural maximum is not negotiable by an
|
|
12
|
+
* operator any more than by app code, so an over-eager `--turns-per-day 999999999` degrades to the
|
|
13
|
+
* ceiling instead of becoming the ceiling. Zero survives clamping and is the kill switch.
|
|
14
|
+
*/
|
|
15
|
+
export declare function surfaceEnvelope(base: AdmissionEnvelope, embed: Pick<PublicEmbedRecord, 'turnsPerDay' | 'mintsPerDay'>): AdmissionEnvelope;
|
|
16
|
+
//# sourceMappingURL=surface-budget.d.ts.map
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The tenant coordinate an assistant session is bound to.
|
|
3
|
+
*
|
|
4
|
+
* Structurally re-declared rather than imported: `packages/service`'s `store.ts` remains the canonical
|
|
5
|
+
* definition, but the gateway must not depend on the hosted service (the dependency runs the other way).
|
|
6
|
+
* The two shapes are identical by contract — widen them together.
|
|
7
|
+
*/
|
|
8
|
+
export interface TenantRef {
|
|
9
|
+
readonly org: string;
|
|
10
|
+
readonly app: string;
|
|
11
|
+
readonly env: string;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=tenant-ref.d.ts.map
|
|
@@ -143,6 +143,7 @@ export type AssistantSurfaceMode = 'authenticated' | 'public' | 'mixed';
|
|
|
143
143
|
export interface AuthenticatedWebsiteAccess {
|
|
144
144
|
readonly mode: 'authenticated';
|
|
145
145
|
readonly origins: readonly string[];
|
|
146
|
+
readonly instructions?: string;
|
|
146
147
|
/** Allowlist of verified session claims; undeclared claims are dropped at session exchange. */
|
|
147
148
|
readonly sessionClaims?: Readonly<Record<string, SessionClaimDeclaration>>;
|
|
148
149
|
readonly capabilities?: readonly CapabilityRef[];
|
|
@@ -151,16 +152,21 @@ export interface PublicWebsiteAccess {
|
|
|
151
152
|
readonly mode: 'public' | 'mixed';
|
|
152
153
|
readonly origins: readonly string[];
|
|
153
154
|
readonly capabilities: readonly CapabilityRef[];
|
|
155
|
+
readonly instructions?: string;
|
|
154
156
|
}
|
|
155
157
|
export type AssistantAccess = AuthenticatedWebsiteAccess | PublicWebsiteAccess;
|
|
156
158
|
export interface AuthenticatedWebsiteInput {
|
|
157
159
|
readonly origins: readonly (string | ConfigRef)[];
|
|
160
|
+
/** Assistant-only behavior for this exact signed-in website audience. */
|
|
161
|
+
readonly instructions?: string;
|
|
158
162
|
readonly sessionClaims?: Readonly<Record<string, SessionClaimDeclaration>>;
|
|
159
163
|
/** Optional narrowing; omitted means the signed-in surface projects the whole server. */
|
|
160
164
|
readonly capabilities?: readonly CapabilityRef[];
|
|
161
165
|
}
|
|
162
166
|
export interface PublicWebsiteInput {
|
|
163
167
|
readonly origins: readonly (string | ConfigRef)[];
|
|
168
|
+
/** Assistant-only behavior for anonymous visitors on this website surface. */
|
|
169
|
+
readonly instructions?: string;
|
|
164
170
|
/**
|
|
165
171
|
* Required: on a public page this list *is* the externally reachable surface, so it must be an
|
|
166
172
|
* explicit positive choice rather than whatever the server happens to declare.
|
|
@@ -186,6 +192,7 @@ export interface EmbeddedAssistantOptions extends AssistantUiOptions {
|
|
|
186
192
|
export interface AssistantSurfaceConfig {
|
|
187
193
|
readonly mode: AssistantSurfaceMode;
|
|
188
194
|
readonly origins: readonly string[];
|
|
195
|
+
readonly instructions?: string;
|
|
189
196
|
readonly capabilities?: readonly AssistantCapability[];
|
|
190
197
|
readonly sessionClaims?: Readonly<Record<string, SessionClaimDeclaration>>;
|
|
191
198
|
}
|
|
@@ -3,6 +3,7 @@ export function authenticatedWebsite(input) {
|
|
|
3
3
|
return {
|
|
4
4
|
mode: 'authenticated',
|
|
5
5
|
origins: serializeOrigins(input.origins, 'assistant.access.origins'),
|
|
6
|
+
...(input.instructions === undefined ? {} : { instructions: input.instructions }),
|
|
6
7
|
...(input.sessionClaims ? { sessionClaims: structuredClone(input.sessionClaims) } : {}),
|
|
7
8
|
...(input.capabilities ? { capabilities: [...input.capabilities] } : {}),
|
|
8
9
|
};
|
|
@@ -12,6 +13,7 @@ export function publicWebsite(input) {
|
|
|
12
13
|
mode: input.signIn === true ? 'mixed' : 'public',
|
|
13
14
|
origins: serializeOrigins(input.origins, 'assistant.access.origins'),
|
|
14
15
|
capabilities: [...input.capabilities],
|
|
16
|
+
...(input.instructions === undefined ? {} : { instructions: input.instructions }),
|
|
15
17
|
};
|
|
16
18
|
}
|
|
17
19
|
function serializeOrigins(origins, path) {
|
|
@@ -42,6 +44,7 @@ export function embeddedAssistant(input) {
|
|
|
42
44
|
surfaces: surfaces.map((surface) => ({
|
|
43
45
|
mode: surface.mode,
|
|
44
46
|
origins: [...surface.origins],
|
|
47
|
+
...(surface.instructions === undefined ? {} : { instructions: surface.instructions }),
|
|
45
48
|
...(surface.capabilities
|
|
46
49
|
? { capabilities: normalizeCapabilities(surface.capabilities) }
|
|
47
50
|
: {}),
|
|
@@ -339,6 +339,7 @@ export declare const manifestV1Schema: z.ZodObject<{
|
|
|
339
339
|
mixed: "mixed";
|
|
340
340
|
}>;
|
|
341
341
|
origins: z.ZodArray<z.ZodString>;
|
|
342
|
+
instructions: z.ZodOptional<z.ZodString>;
|
|
342
343
|
capabilities: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
343
344
|
kind: z.ZodEnum<{
|
|
344
345
|
tool: "tool";
|
|
@@ -789,6 +790,7 @@ export declare const manifestV2Schema: z.ZodObject<{
|
|
|
789
790
|
mixed: "mixed";
|
|
790
791
|
}>;
|
|
791
792
|
origins: z.ZodArray<z.ZodString>;
|
|
793
|
+
instructions: z.ZodOptional<z.ZodString>;
|
|
792
794
|
capabilities: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
793
795
|
kind: z.ZodEnum<{
|
|
794
796
|
tool: "tool";
|
|
@@ -1449,6 +1451,7 @@ export declare const manifestSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
1449
1451
|
mixed: "mixed";
|
|
1450
1452
|
}>;
|
|
1451
1453
|
origins: z.ZodArray<z.ZodString>;
|
|
1454
|
+
instructions: z.ZodOptional<z.ZodString>;
|
|
1452
1455
|
capabilities: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
1453
1456
|
kind: z.ZodEnum<{
|
|
1454
1457
|
tool: "tool";
|
|
@@ -1898,6 +1901,7 @@ export declare const manifestSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
1898
1901
|
mixed: "mixed";
|
|
1899
1902
|
}>;
|
|
1900
1903
|
origins: z.ZodArray<z.ZodString>;
|
|
1904
|
+
instructions: z.ZodOptional<z.ZodString>;
|
|
1901
1905
|
capabilities: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
1902
1906
|
kind: z.ZodEnum<{
|
|
1903
1907
|
tool: "tool";
|
|
@@ -334,6 +334,11 @@ const assistantCapabilitySchema = z
|
|
|
334
334
|
name: z.string().trim().min(1),
|
|
335
335
|
})
|
|
336
336
|
.strict();
|
|
337
|
+
/**
|
|
338
|
+
* Upper bound on server- and surface-level instructions. These are concise behavior primers, not
|
|
339
|
+
* documentation; the shared cap keeps prompt ownership predictable across both layers.
|
|
340
|
+
*/
|
|
341
|
+
const MAX_SERVER_INSTRUCTIONS = 4000;
|
|
337
342
|
/** Verified session claims an embedding backend may pass at exchange; undeclared keys are dropped. */
|
|
338
343
|
const sessionClaimsSchema = z.record(z.string().regex(/^[A-Za-z][A-Za-z0-9_]{0,63}$/, 'claim keys are identifier-style'), z.object({ exposeToModel: z.boolean().optional() }).strict());
|
|
339
344
|
const embeddedAssistantSchema = z
|
|
@@ -356,6 +361,7 @@ const embeddedAssistantSchema = z
|
|
|
356
361
|
.object({
|
|
357
362
|
mode: z.enum(['authenticated', 'public', 'mixed']),
|
|
358
363
|
origins: z.array(assistantOriginOrVariableSchema).min(1),
|
|
364
|
+
instructions: z.string().trim().min(1).max(MAX_SERVER_INSTRUCTIONS).optional(),
|
|
359
365
|
capabilities: z.array(assistantCapabilitySchema).optional(),
|
|
360
366
|
sessionClaims: sessionClaimsSchema.optional(),
|
|
361
367
|
})
|
|
@@ -427,11 +433,6 @@ const serverShellSchema = z
|
|
|
427
433
|
persistentActions: z.array(serverShellActionSchema).optional(),
|
|
428
434
|
})
|
|
429
435
|
.strict();
|
|
430
|
-
/**
|
|
431
|
-
* Upper bound on `server.instructions`. Hosts inject instructions into every session's context, so the
|
|
432
|
-
* field is for a concise usage primer (a few hundred words), not documentation.
|
|
433
|
-
*/
|
|
434
|
-
const MAX_SERVER_INSTRUCTIONS = 4000;
|
|
435
436
|
const serverContextSchema = z
|
|
436
437
|
.object({
|
|
437
438
|
defaults: z
|