@noodleseed/one 0.132.2 → 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.
Files changed (102) hide show
  1. package/dist/commands/author-loop.d.ts.map +1 -1
  2. package/dist/commands/author-loop.js +3 -3
  3. package/dist/commands/author-loop.js.map +1 -1
  4. package/dist/commands/devtools.js +1 -1
  5. package/dist/commands/devtools.js.map +1 -1
  6. package/dist/commands/local-readiness.d.ts +2 -2
  7. package/dist/commands/local-readiness.d.ts.map +1 -1
  8. package/dist/commands/local-readiness.js +15 -7
  9. package/dist/commands/local-readiness.js.map +1 -1
  10. package/dist/dev-local-runtime.d.ts +33 -0
  11. package/dist/dev-local-runtime.d.ts.map +1 -0
  12. package/dist/dev-local-runtime.js +53 -0
  13. package/dist/dev-local-runtime.js.map +1 -0
  14. package/dist/dev.d.ts +4 -0
  15. package/dist/dev.d.ts.map +1 -1
  16. package/dist/dev.js +109 -31
  17. package/dist/dev.js.map +1 -1
  18. package/dist/devtools-harness.js +5 -3
  19. package/dist/devtools-harness.js.map +1 -1
  20. package/dist/devtools-preview.d.ts +2 -0
  21. package/dist/devtools-preview.d.ts.map +1 -1
  22. package/dist/devtools-preview.js +15 -3
  23. package/dist/devtools-preview.js.map +1 -1
  24. package/dist/devtools-rpc-forwarder.js +1 -1
  25. package/dist/devtools-rpc-forwarder.js.map +1 -1
  26. package/dist/preview-session.d.ts.map +1 -1
  27. package/dist/preview-session.js +1 -0
  28. package/dist/preview-session.js.map +1 -1
  29. package/dist/react-widget-build.d.ts +4 -0
  30. package/dist/react-widget-build.d.ts.map +1 -1
  31. package/dist/react-widget-build.js +9 -0
  32. package/dist/react-widget-build.js.map +1 -1
  33. package/node_modules/@noodle-borg/admission-limits/dist/client-address.d.ts +7 -0
  34. package/node_modules/@noodle-borg/admission-limits/dist/counter-store.d.ts +71 -0
  35. package/node_modules/@noodle-borg/admission-limits/dist/envelope.d.ts +67 -0
  36. package/node_modules/@noodle-borg/admission-limits/dist/in-memory-counter-store.d.ts +15 -0
  37. package/node_modules/@noodle-borg/admission-limits/dist/portable.d.ts +6 -0
  38. package/node_modules/@noodle-borg/admission-limits/dist/portable.js +6 -0
  39. package/node_modules/@noodle-borg/admission-limits/package.json +8 -0
  40. package/node_modules/@noodle-borg/agent-kit/dist/generated/example-files.js +6 -6
  41. package/node_modules/@noodle-borg/agent-kit/dist/skill-authoring-refs.js +1 -1
  42. package/node_modules/@noodle-borg/agent-kit/dist/skill-embedded-assistant-ref.js +4 -1
  43. package/node_modules/@noodle-borg/agent-kit/package.json +1 -1
  44. package/node_modules/@noodle-borg/assistant-gateway/dist/artifact-projection.d.ts +21 -0
  45. package/node_modules/@noodle-borg/assistant-gateway/dist/assistant-context.d.ts +25 -0
  46. package/node_modules/@noodle-borg/assistant-gateway/dist/assistant-customer-issuer.d.ts +3 -0
  47. package/node_modules/@noodle-borg/assistant-gateway/dist/assistant-customer-routing.d.ts +19 -0
  48. package/node_modules/@noodle-borg/assistant-gateway/dist/assistant-guide.d.ts +39 -0
  49. package/node_modules/@noodle-borg/assistant-gateway/dist/assistant-interaction-state.d.ts +181 -0
  50. package/node_modules/@noodle-borg/assistant-gateway/dist/assistant-interactive.d.ts +99 -0
  51. package/node_modules/@noodle-borg/assistant-gateway/dist/assistant-interactive.js +1 -1
  52. package/node_modules/@noodle-borg/assistant-gateway/dist/assistant-presentation.d.ts +35 -0
  53. package/node_modules/@noodle-borg/assistant-gateway/dist/assistant-sensitive-values.d.ts +3 -0
  54. package/node_modules/@noodle-borg/assistant-gateway/dist/assistant-store.d.ts +226 -0
  55. package/node_modules/@noodle-borg/assistant-gateway/dist/assistant-store.js +1 -1
  56. package/node_modules/@noodle-borg/assistant-gateway/dist/elevation-store.d.ts +70 -0
  57. package/node_modules/@noodle-borg/assistant-gateway/dist/elevation.d.ts +68 -0
  58. package/node_modules/@noodle-borg/assistant-gateway/dist/embed-script.d.ts +24 -0
  59. package/node_modules/@noodle-borg/assistant-gateway/dist/embed-store.d.ts +73 -0
  60. package/node_modules/@noodle-borg/assistant-gateway/dist/in-memory-elevation-store.d.ts +21 -0
  61. package/node_modules/@noodle-borg/assistant-gateway/dist/in-memory-embed-store.d.ts +28 -0
  62. package/node_modules/@noodle-borg/assistant-gateway/dist/portable.d.ts +24 -0
  63. package/node_modules/@noodle-borg/assistant-gateway/dist/public-session.d.ts +56 -0
  64. package/node_modules/@noodle-borg/assistant-gateway/dist/public-surface.d.ts +21 -0
  65. package/node_modules/@noodle-borg/assistant-gateway/dist/public-surface.js +2 -1
  66. package/node_modules/@noodle-borg/assistant-gateway/dist/public-turn.d.ts +44 -0
  67. package/node_modules/@noodle-borg/assistant-gateway/dist/session-resume.d.ts +22 -0
  68. package/node_modules/@noodle-borg/assistant-gateway/dist/surface-budget.d.ts +16 -0
  69. package/node_modules/@noodle-borg/assistant-gateway/dist/surface-budget.js +1 -1
  70. package/node_modules/@noodle-borg/assistant-gateway/dist/tenant-ref.d.ts +13 -0
  71. package/node_modules/@noodle-borg/authoring/dist/assistant.d.ts +9 -2
  72. package/node_modules/@noodle-borg/authoring/dist/assistant.js +8 -2
  73. package/node_modules/@noodle-borg/authoring/dist/connectors.d.ts +3 -1
  74. package/node_modules/@noodle-borg/authoring/dist/connectors.js +5 -1
  75. package/node_modules/@noodle-borg/authoring/dist/server.d.ts +2 -1
  76. package/node_modules/@noodle-borg/authoring/dist/server.js +4 -1
  77. package/node_modules/@noodle-borg/compiler/dist/artifact/assemble.js +1 -1
  78. package/node_modules/@noodle-borg/compiler/dist/artifact/config.js +25 -20
  79. package/node_modules/@noodle-borg/compiler/dist/manifest/schema.d.ts +16 -12
  80. package/node_modules/@noodle-borg/compiler/dist/manifest/schema.js +18 -13
  81. package/node_modules/@noodle-borg/connector-defs/dist/compile-http.js +2 -0
  82. package/node_modules/@noodle-borg/connector-defs/dist/schema.d.ts +3 -3
  83. package/node_modules/@noodle-borg/connector-defs/dist/schema.js +6 -1
  84. package/node_modules/@noodle-borg/connector-http/dist/http-connector.js +31 -8
  85. package/node_modules/@noodle-borg/protocol/dist/mapping.js +8 -0
  86. package/node_modules/@noodle-borg/runtime/dist/index.d.ts +1 -0
  87. package/node_modules/@noodle-borg/runtime/dist/index.js +1 -0
  88. package/node_modules/@noodle-borg/runtime/dist/managed-origins.d.ts +11 -0
  89. package/node_modules/@noodle-borg/runtime/dist/managed-origins.js +105 -0
  90. package/node_modules/@noodle-borg/service/dist/local-options.d.ts +9 -0
  91. package/node_modules/@noodle-borg/service/dist/registry-compile.js +12 -12
  92. package/node_modules/@noodle-borg/service/dist/routes/assistant-agent.js +14 -3
  93. package/node_modules/@noodle-borg/service/dist/routes/assistant-embeds.js +1 -1
  94. package/node_modules/@noodle-borg/service/dist/routes/assistant-public-session.js +1 -1
  95. package/node_modules/@noodle-borg/service/dist/routes/assistant-public-turn.js +1 -1
  96. package/node_modules/@noodle-borg/service/dist/routes/assistant.js +1 -1
  97. package/node_modules/@noodle-borg/service/dist/serve-local.js +2 -3
  98. package/node_modules/@noodle-borg/service/package.json +1 -1
  99. package/node_modules/@noodleseed/assistant/README.md +4 -1
  100. package/package.json +5 -3
  101. package/node_modules/@noodle-borg/admission-limits/dist/index.js +0 -15
  102. package/node_modules/@noodle-borg/admission-limits/dist/postgres-counter-store.js +0 -68
@@ -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
@@ -1,4 +1,4 @@
1
- import { clamp } from '@noodle-borg/admission-limits';
1
+ import { clamp } from '@noodle-borg/admission-limits/portable';
2
2
  /**
3
3
  * One surface's daily budget, resolved from the operator's override and the deployment's defaults.
4
4
  *
@@ -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
- readonly origins: readonly string[];
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
- readonly origins: readonly string[];
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
  }
@@ -2,7 +2,8 @@ import { serializeSecretRef, serializeVariableRef } from './config.js';
2
2
  export function authenticatedWebsite(input) {
3
3
  return {
4
4
  mode: 'authenticated',
5
- origins: [...input.origins],
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
  };
@@ -10,10 +11,14 @@ export function authenticatedWebsite(input) {
10
11
  export function publicWebsite(input) {
11
12
  return {
12
13
  mode: input.signIn === true ? 'mixed' : 'public',
13
- origins: [...input.origins],
14
+ origins: serializeOrigins(input.origins, 'assistant.access.origins'),
14
15
  capabilities: [...input.capabilities],
16
+ ...(input.instructions === undefined ? {} : { instructions: input.instructions }),
15
17
  };
16
18
  }
19
+ function serializeOrigins(origins, path) {
20
+ return origins.map((origin, index) => typeof origin === 'string' ? origin : serializeVariableRef(origin, `${path}.${index}`));
21
+ }
17
22
  export function openAICompatible(input) {
18
23
  const baseUrl = typeof input.baseUrl === 'string'
19
24
  ? input.baseUrl
@@ -39,6 +44,7 @@ export function embeddedAssistant(input) {
39
44
  surfaces: surfaces.map((surface) => ({
40
45
  mode: surface.mode,
41
46
  origins: [...surface.origins],
47
+ ...(surface.instructions === undefined ? {} : { instructions: surface.instructions }),
42
48
  ...(surface.capabilities
43
49
  ? { capabilities: normalizeCapabilities(surface.capabilities) }
44
50
  : {}),
@@ -77,8 +77,10 @@ type HttpOperationOptionsWithRefs = Omit<HttpConnectorDef['operations'][string],
77
77
  readonly input?: OperationIoOptions;
78
78
  readonly output?: OperationIoOptions;
79
79
  };
80
- export type HttpConnectorOptions = Omit<HttpConnectorDef['http'], 'baseUrl' | 'auth'> & {
80
+ export type HttpConnectorOptions = Omit<HttpConnectorDef['http'], 'baseUrl' | 'allowedOrigins' | 'auth'> & {
81
81
  readonly baseUrl: string | ConfigRef | CustomerEndpointRef;
82
+ /** Exact literal or operator-managed origins that may receive connector requests. */
83
+ readonly allowedOrigins?: readonly (string | ConfigRef)[];
82
84
  readonly auth?: HttpAuthOptions;
83
85
  } & {
84
86
  readonly credentialProfiles?: HttpConnectorDef['credentialProfiles'];
@@ -158,7 +158,11 @@ function normalizeHttpOptions(id, options) {
158
158
  const normalized = {
159
159
  ...httpOptions,
160
160
  baseUrl: normalizeBaseUrl(options.baseUrl, `connectors.${id}.http.baseUrl`),
161
- ...(options.allowedOrigins !== undefined ? { allowedOrigins: options.allowedOrigins } : {}),
161
+ ...(options.allowedOrigins !== undefined
162
+ ? {
163
+ allowedOrigins: options.allowedOrigins.map((origin, index) => normalizeVariableOrString(origin, `connectors.${id}.http.allowedOrigins.${index}`)),
164
+ }
165
+ : {}),
162
166
  ...(options.auth !== undefined
163
167
  ? { auth: normalizeAuth(options.auth, `connectors.${id}.http.auth`) }
164
168
  : {}),
@@ -4,6 +4,7 @@ import { type AgentGuideSource } from './agent-guide.js';
4
4
  import type { EmbeddedAssistantConfig } from './assistant.js';
5
5
  import { type DistributionMetadataSource, type DistributionMetadataV1 } from './distribution.js';
6
6
  export { annotations } from './annotations.js';
7
+ import { type ConfigRef } from './config.js';
7
8
  import { type ConnectorCatalogDoc, type ConnectorRef } from './connectors.js';
8
9
  import { type ServerContextOptions } from './context.js';
9
10
  import { type CustomerAuth } from './customer-auth.js';
@@ -77,7 +78,7 @@ export interface ServerOptions {
77
78
  readonly colorScheme?: 'auto' | 'light' | 'dark';
78
79
  };
79
80
  readonly handoff?: {
80
- readonly allowedDomains: readonly string[];
81
+ readonly allowedDomains: readonly (string | ConfigRef)[];
81
82
  };
82
83
  readonly state?: {
83
84
  readonly handles: Readonly<Record<string, StateHandleOptions>>;
@@ -3,6 +3,7 @@ import { z } from 'zod';
3
3
  import { manifestAgentGuide } from './agent-guide.js';
4
4
  import { projectDistributionMetadata, } from './distribution.js';
5
5
  export { annotations } from './annotations.js';
6
+ import { isConfigRef, serializeVariableRef } from './config.js';
6
7
  import { toManifestConnectorRef } from './connections.js';
7
8
  import { validateCatalog } from './connectors.js';
8
9
  import { manifestContext } from './context.js';
@@ -210,7 +211,9 @@ class ServerBuilder {
210
211
  ...(this.options.handoff
211
212
  ? {
212
213
  handoff: {
213
- allowedDomains: [...this.options.handoff.allowedDomains],
214
+ allowedDomains: this.options.handoff.allowedDomains.map((domain, index) => isConfigRef(domain)
215
+ ? serializeVariableRef(domain, `server.handoff.allowedDomains.${index}`)
216
+ : domain),
214
217
  },
215
218
  }
216
219
  : {}),
@@ -9,7 +9,7 @@ export function assembleRuntimeArtifact({ manifest, options, runtimeBranding, ru
9
9
  ...(artifactPrompts.length > 0 ? { prompts: artifactPrompts.map((p) => p.name) } : {}),
10
10
  };
11
11
  const connectorBindings = Object.fromEntries(Object.entries(declared).flatMap(([alias, connector]) => connector.binding === undefined ? [] : [[alias, connector.binding]]));
12
- const config = artifactConfig(artifactTools, allResources, artifactPrompts, connectorBindings, artifactAmbientContext?.fulfilment);
12
+ const config = artifactConfig(manifest, artifactTools, allResources, artifactPrompts, connectorBindings, artifactAmbientContext?.fulfilment);
13
13
  const artifact = {
14
14
  artifactSchemaVersion: ARTIFACT_SCHEMA_VERSION,
15
15
  resolution: options.catalog ? 'resolved' : 'shape-only',
@@ -1,19 +1,31 @@
1
- export function artifactConfig(tools, resources, prompts, connectorBindings, ambientFulfilment) {
1
+ export function artifactConfig(manifest, tools, resources, prompts, connectorBindings, ambientFulfilment) {
2
2
  const variables = new Set();
3
+ collectVariablesFromManagedOrigins(manifest, variables);
3
4
  for (const tool of tools)
4
5
  collectVariablesFromFulfilment(tool.fulfilment, variables);
5
6
  for (const resource of resources)
6
7
  collectVariablesFromFulfilment(resource.fulfilment, variables);
7
8
  for (const prompt of prompts)
8
9
  collectVariablesFromFulfilment(prompt.fulfilment, variables);
9
- if (ambientFulfilment !== undefined) {
10
+ if (ambientFulfilment !== undefined)
10
11
  collectVariablesFromFulfilment(ambientFulfilment, variables);
11
- }
12
- for (const binding of Object.values(connectorBindings)) {
12
+ for (const binding of Object.values(connectorBindings))
13
13
  collectVariablesFromConnectionSource(binding.connection.source, variables);
14
- }
15
14
  return variables.size > 0 ? { variables: [...variables].sort() } : undefined;
16
15
  }
16
+ function collectVariablesFromManagedOrigins(manifest, out) {
17
+ const assistant = manifest.server.assistant;
18
+ const values = [
19
+ ...(assistant?.allowedOrigins ?? []),
20
+ ...(assistant?.surfaces?.flatMap((surface) => surface.origins) ?? []),
21
+ ...(manifest.handoff?.allowedDomains ?? []),
22
+ ];
23
+ for (const value of values) {
24
+ const match = /^\$\{env\.([A-Za-z0-9_]+)\}$/.exec(value);
25
+ if (match?.[1] !== undefined)
26
+ out.add(match[1]);
27
+ }
28
+ }
17
29
  function collectVariablesFromConnectionSource(source, out) {
18
30
  const values = source.kind === 'clientCredentials'
19
31
  ? [source.tokenUrl, source.clientId]
@@ -51,36 +63,29 @@ function collectVariablesFromExprMap(map, out) {
51
63
  }
52
64
  function collectVariablesFromExpr(node, out) {
53
65
  if (node.kind === 'path') {
54
- if (node.root === 'env') {
55
- const first = node.segments[0];
56
- if (first?.kind === 'prop')
57
- out.add(first.name);
58
- }
59
- return;
66
+ const first = node.segments[0];
67
+ if (node.root === 'env' && first?.kind === 'prop')
68
+ out.add(first.name);
60
69
  }
61
- if (node.kind === 'template') {
70
+ else if (node.kind === 'template') {
62
71
  for (const part of node.parts) {
63
72
  if (part.kind !== 'text')
64
73
  collectVariablesFromExpr(part, out);
65
74
  }
66
- return;
67
75
  }
68
- if (node.kind === 'array') {
76
+ else if (node.kind === 'array') {
69
77
  for (const item of node.items)
70
78
  collectVariablesFromExpr(item, out);
71
- return;
72
79
  }
73
- if (node.kind === 'object') {
80
+ else if (node.kind === 'object') {
74
81
  for (const entry of node.entries)
75
82
  collectVariablesFromExpr(entry.value, out);
76
- return;
77
83
  }
78
- if (node.kind === 'coalesce') {
84
+ else if (node.kind === 'coalesce') {
79
85
  collectVariablesFromExpr(node.left, out);
80
86
  collectVariablesFromExpr(node.right, out);
81
- return;
82
87
  }
83
- if (node.kind === 'function') {
88
+ else if (node.kind === 'function') {
84
89
  for (const arg of node.args)
85
90
  collectVariablesFromExpr(arg, out);
86
91
  }
@@ -6,7 +6,7 @@ type ToolAuthorizationManifest = {
6
6
  };
7
7
  export declare const manifestV1Schema: z.ZodObject<{
8
8
  handoff: z.ZodOptional<z.ZodObject<{
9
- allowedDomains: z.ZodArray<z.ZodURL>;
9
+ allowedDomains: z.ZodArray<z.ZodUnion<readonly [z.ZodURL, z.ZodString]>>;
10
10
  }, z.core.$strict>>;
11
11
  state: z.ZodOptional<z.ZodObject<{
12
12
  handles: z.ZodRecord<z.ZodString, z.ZodObject<{
@@ -331,14 +331,15 @@ export declare const manifestV1Schema: z.ZodObject<{
331
331
  model: z.ZodString;
332
332
  apiKey: z.ZodString;
333
333
  }, z.core.$strict>;
334
- allowedOrigins: z.ZodArray<z.ZodURL>;
334
+ allowedOrigins: z.ZodArray<z.ZodString>;
335
335
  surfaces: z.ZodOptional<z.ZodArray<z.ZodObject<{
336
336
  mode: z.ZodEnum<{
337
337
  authenticated: "authenticated";
338
338
  public: "public";
339
339
  mixed: "mixed";
340
340
  }>;
341
- origins: z.ZodArray<z.ZodURL>;
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";
@@ -498,7 +499,7 @@ export declare const manifestV1Schema: z.ZodObject<{
498
499
  }, z.core.$strip>;
499
500
  export declare const manifestV2Schema: z.ZodObject<{
500
501
  handoff: z.ZodOptional<z.ZodObject<{
501
- allowedDomains: z.ZodArray<z.ZodURL>;
502
+ allowedDomains: z.ZodArray<z.ZodUnion<readonly [z.ZodURL, z.ZodString]>>;
502
503
  }, z.core.$strict>>;
503
504
  state: z.ZodOptional<z.ZodObject<{
504
505
  handles: z.ZodRecord<z.ZodString, z.ZodObject<{
@@ -781,14 +782,15 @@ export declare const manifestV2Schema: z.ZodObject<{
781
782
  model: z.ZodString;
782
783
  apiKey: z.ZodString;
783
784
  }, z.core.$strict>;
784
- allowedOrigins: z.ZodArray<z.ZodURL>;
785
+ allowedOrigins: z.ZodArray<z.ZodString>;
785
786
  surfaces: z.ZodOptional<z.ZodArray<z.ZodObject<{
786
787
  mode: z.ZodEnum<{
787
788
  authenticated: "authenticated";
788
789
  public: "public";
789
790
  mixed: "mixed";
790
791
  }>;
791
- origins: z.ZodArray<z.ZodURL>;
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";
@@ -1116,7 +1118,7 @@ export declare const manifestV2Schema: z.ZodObject<{
1116
1118
  }, z.core.$strip>;
1117
1119
  export declare const manifestSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
1118
1120
  handoff: z.ZodOptional<z.ZodObject<{
1119
- allowedDomains: z.ZodArray<z.ZodURL>;
1121
+ allowedDomains: z.ZodArray<z.ZodUnion<readonly [z.ZodURL, z.ZodString]>>;
1120
1122
  }, z.core.$strict>>;
1121
1123
  state: z.ZodOptional<z.ZodObject<{
1122
1124
  handles: z.ZodRecord<z.ZodString, z.ZodObject<{
@@ -1441,14 +1443,15 @@ export declare const manifestSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
1441
1443
  model: z.ZodString;
1442
1444
  apiKey: z.ZodString;
1443
1445
  }, z.core.$strict>;
1444
- allowedOrigins: z.ZodArray<z.ZodURL>;
1446
+ allowedOrigins: z.ZodArray<z.ZodString>;
1445
1447
  surfaces: z.ZodOptional<z.ZodArray<z.ZodObject<{
1446
1448
  mode: z.ZodEnum<{
1447
1449
  authenticated: "authenticated";
1448
1450
  public: "public";
1449
1451
  mixed: "mixed";
1450
1452
  }>;
1451
- origins: z.ZodArray<z.ZodURL>;
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";
@@ -1607,7 +1610,7 @@ export declare const manifestSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
1607
1610
  }, z.core.$strip>>>;
1608
1611
  }, z.core.$strip>, z.ZodObject<{
1609
1612
  handoff: z.ZodOptional<z.ZodObject<{
1610
- allowedDomains: z.ZodArray<z.ZodURL>;
1613
+ allowedDomains: z.ZodArray<z.ZodUnion<readonly [z.ZodURL, z.ZodString]>>;
1611
1614
  }, z.core.$strict>>;
1612
1615
  state: z.ZodOptional<z.ZodObject<{
1613
1616
  handles: z.ZodRecord<z.ZodString, z.ZodObject<{
@@ -1890,14 +1893,15 @@ export declare const manifestSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
1890
1893
  model: z.ZodString;
1891
1894
  apiKey: z.ZodString;
1892
1895
  }, z.core.$strict>;
1893
- allowedOrigins: z.ZodArray<z.ZodURL>;
1896
+ allowedOrigins: z.ZodArray<z.ZodString>;
1894
1897
  surfaces: z.ZodOptional<z.ZodArray<z.ZodObject<{
1895
1898
  mode: z.ZodEnum<{
1896
1899
  authenticated: "authenticated";
1897
1900
  public: "public";
1898
1901
  mixed: "mixed";
1899
1902
  }>;
1900
- origins: z.ZodArray<z.ZodURL>;
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";
@@ -135,9 +135,13 @@ const promptSchema = z.object({
135
135
  });
136
136
  const hexColorSchema = z.string().regex(/^#[0-9A-Fa-f]{6}$/, 'must be a 6-digit hex color');
137
137
  const httpsUrlSchema = z.url().regex(/^https:\/\//, 'must use https');
138
+ const managedVariableExpressionPattern = /^\$\{env\.[A-Za-z0-9_]+\}$/;
139
+ const managedVariableExpressionSchema = z
140
+ .string()
141
+ .regex(managedVariableExpressionPattern, 'must be an exact managed variable expression');
138
142
  // HTTP is allowed only for loopback development origins (mirrors the host-pattern loopback
139
143
  // exception used for connector/CSP origins); production embedding origins stay https-only.
140
- const canonicalAssistantOriginPattern = /^(?:https:\/\/[^/?#@\\]+|http:\/\/(?:localhost|127\.0\.0\.1|\[::1\])(?::\d+)?)$/;
144
+ const assistantOriginOrVariablePattern = /^(?:\$\{env\.[A-Za-z0-9_]+\}|https:\/\/[^/?#@\\]+|http:\/\/(?:localhost|127\.0\.0\.1|\[::1\])(?::\d+)?)$/;
141
145
  function isCanonicalAssistantOrigin(value) {
142
146
  try {
143
147
  return new URL(value).origin === value;
@@ -146,10 +150,10 @@ function isCanonicalAssistantOrigin(value) {
146
150
  return false;
147
151
  }
148
152
  }
149
- const assistantOriginSchema = z
150
- .url()
151
- .regex(canonicalAssistantOriginPattern, 'must be a canonical bare origin using https (http is allowed only for localhost/127.0.0.1/[::1] loopback development origins)')
152
- .refine(isCanonicalAssistantOrigin, 'must be a canonical bare origin');
153
+ const assistantOriginOrVariableSchema = z
154
+ .string()
155
+ .regex(assistantOriginOrVariablePattern, 'must be a canonical bare origin using https (http is allowed only for localhost/127.0.0.1/[::1] loopback development origins)')
156
+ .refine((value) => managedVariableExpressionPattern.test(value) || isCanonicalAssistantOrigin(value), 'must be a canonical bare origin');
153
157
  const packagedAssetSchema = z
154
158
  .object({
155
159
  kind: z.literal('asset'),
@@ -330,6 +334,11 @@ const assistantCapabilitySchema = z
330
334
  name: z.string().trim().min(1),
331
335
  })
332
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;
333
342
  /** Verified session claims an embedding backend may pass at exchange; undeclared keys are dropped. */
334
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());
335
344
  const embeddedAssistantSchema = z
@@ -342,7 +351,7 @@ const embeddedAssistantSchema = z
342
351
  apiKey: z.string().regex(/^[A-Za-z0-9_]+$/),
343
352
  })
344
353
  .strict(),
345
- allowedOrigins: z.array(assistantOriginSchema).min(1),
354
+ allowedOrigins: z.array(assistantOriginOrVariableSchema).min(1),
346
355
  // The front doors this assistant serves (ADR 0201, amended 2026-08-12). Absent means the pre-0201
347
356
  // authenticated shape, so released artifacts keep their meaning; `allowedOrigins` stays the union
348
357
  // and `sessionClaims` stays mirrored, so the service reads both unchanged. `capabilities` is the
@@ -351,7 +360,8 @@ const embeddedAssistantSchema = z
351
360
  .array(z
352
361
  .object({
353
362
  mode: z.enum(['authenticated', 'public', 'mixed']),
354
- origins: z.array(assistantOriginSchema).min(1),
363
+ origins: z.array(assistantOriginOrVariableSchema).min(1),
364
+ instructions: z.string().trim().min(1).max(MAX_SERVER_INSTRUCTIONS).optional(),
355
365
  capabilities: z.array(assistantCapabilitySchema).optional(),
356
366
  sessionClaims: sessionClaimsSchema.optional(),
357
367
  })
@@ -423,11 +433,6 @@ const serverShellSchema = z
423
433
  persistentActions: z.array(serverShellActionSchema).optional(),
424
434
  })
425
435
  .strict();
426
- /**
427
- * Upper bound on `server.instructions`. Hosts inject instructions into every session's context, so the
428
- * field is for a concise usage primer (a few hundred words), not documentation.
429
- */
430
- const MAX_SERVER_INSTRUCTIONS = 4000;
431
436
  const serverContextSchema = z
432
437
  .object({
433
438
  defaults: z
@@ -472,7 +477,7 @@ const serverV2Schema = serverSchema.extend({
472
477
  });
473
478
  const handoffSchema = z
474
479
  .object({
475
- allowedDomains: z.array(httpsUrlSchema).min(1),
480
+ allowedDomains: z.array(z.union([httpsUrlSchema, managedVariableExpressionSchema])).min(1),
476
481
  })
477
482
  .strict();
478
483
  const stateHandleSchema = z
@@ -22,6 +22,8 @@ export function compileHttpConnector(def, mode, errors, catalog, connectors, sec
22
22
  (stringBase === undefined || isVariableExpression(stringBase) ? [] : [stringBase]);
23
23
  if (stringBase !== undefined)
24
24
  collectVariableExpression(stringBase, variableBindings);
25
+ for (const origin of allowedOrigins)
26
+ collectVariableExpression(origin, variableBindings);
25
27
  if (def.http.auth !== undefined)
26
28
  collectVariablesFromHttpAuth(def.http.auth, variableBindings);
27
29
  if (routedBase) {
@@ -269,7 +269,7 @@ declare const httpConnectorDefSchema: z.ZodObject<{
269
269
  readonly allowedHttpsHostSuffixes: readonly string[];
270
270
  }, unknown>>;
271
271
  }, z.core.$strict>]>;
272
- allowedOrigins: z.ZodOptional<z.ZodArray<z.ZodString>>;
272
+ allowedOrigins: z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodString]>>>;
273
273
  auth: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
274
274
  kind: z.ZodLiteral<"bearer">;
275
275
  secret: z.ZodString;
@@ -551,7 +551,7 @@ declare const connectorDefSchema: z.ZodUnion<readonly [z.ZodObject<{
551
551
  readonly allowedHttpsHostSuffixes: readonly string[];
552
552
  }, unknown>>;
553
553
  }, z.core.$strict>]>;
554
- allowedOrigins: z.ZodOptional<z.ZodArray<z.ZodString>>;
554
+ allowedOrigins: z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodString]>>>;
555
555
  auth: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
556
556
  kind: z.ZodLiteral<"bearer">;
557
557
  secret: z.ZodString;
@@ -828,7 +828,7 @@ export declare const connectorFileSchema: z.ZodObject<{
828
828
  readonly allowedHttpsHostSuffixes: readonly string[];
829
829
  }, unknown>>;
830
830
  }, z.core.$strict>]>;
831
- allowedOrigins: z.ZodOptional<z.ZodArray<z.ZodString>>;
831
+ allowedOrigins: z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodString]>>>;
832
832
  auth: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
833
833
  kind: z.ZodLiteral<"bearer">;
834
834
  secret: z.ZodString;
@@ -366,6 +366,9 @@ const computeOperationSchema = z
366
366
  credentials: operationCredentialsSchema.optional(),
367
367
  })
368
368
  .strict();
369
+ const managedVariableExpressionSchema = z
370
+ .string()
371
+ .regex(/^\$\{env\.[A-Za-z0-9_]+\}$/, 'must be an exact managed variable expression');
369
372
  const connectorIdentity = {
370
373
  id: z.string().min(1),
371
374
  version: z.string().min(1),
@@ -381,7 +384,9 @@ const httpConnectorDefSchema = z
381
384
  http: z
382
385
  .object({
383
386
  baseUrl: baseUrlSchema,
384
- allowedOrigins: z.array(z.string().url()).optional(),
387
+ allowedOrigins: z
388
+ .array(z.union([z.string().url(), managedVariableExpressionSchema]))
389
+ .optional(),
385
390
  /** Connector-default auth scheme; an operation's own `auth` overrides it. */
386
391
  auth: httpAuthSchema.optional(),
387
392
  })