@getuserfeedback/protocol 0.6.1 → 0.8.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 (59) hide show
  1. package/dist/app-event.d.ts +779 -10
  2. package/dist/app-event.d.ts.map +1 -1
  3. package/dist/app-event.js +81 -8
  4. package/dist/flow-assignments.d.ts +7 -4
  5. package/dist/flow-assignments.d.ts.map +1 -1
  6. package/dist/flow-assignments.js +15 -5
  7. package/dist/host/constants.d.ts +2 -2
  8. package/dist/host/constants.d.ts.map +1 -1
  9. package/dist/host/constants.js +3 -1
  10. package/dist/host/external-id-argument-guards.d.ts +10 -0
  11. package/dist/host/external-id-argument-guards.d.ts.map +1 -0
  12. package/dist/host/external-id-argument-guards.js +27 -0
  13. package/dist/host/index.d.ts +1 -0
  14. package/dist/host/index.d.ts.map +1 -1
  15. package/dist/host/index.js +1 -0
  16. package/dist/host/sdk-types.d.ts +92 -27
  17. package/dist/host/sdk-types.d.ts.map +1 -1
  18. package/dist/host/sdk.d.ts.map +1 -1
  19. package/dist/identity-type.d.ts +2 -1
  20. package/dist/identity-type.d.ts.map +1 -1
  21. package/dist/identity-type.js +1 -1
  22. package/dist/index.d.ts +3 -87
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +1 -6
  25. package/dist/widget-commands.d.ts +124 -3
  26. package/dist/widget-commands.d.ts.map +1 -1
  27. package/dist/widget-commands.js +10 -0
  28. package/dist/widget-config.d.ts +23 -5
  29. package/dist/widget-config.d.ts.map +1 -1
  30. package/dist/widget-config.js +2 -1
  31. package/package.json +22 -2
  32. package/src/app-event.ts +235 -0
  33. package/src/client-meta.ts +25 -0
  34. package/src/defaults.ts +4 -0
  35. package/src/errors.ts +58 -0
  36. package/src/flow-assignments.test.ts +82 -0
  37. package/src/flow-assignments.ts +151 -0
  38. package/src/host/command-dispatch.ts +59 -0
  39. package/src/host/command-envelope.ts +41 -0
  40. package/src/host/command-settlement.ts +121 -0
  41. package/src/host/constants.ts +103 -0
  42. package/src/host/external-id-argument-guards.ts +57 -0
  43. package/src/host/host-event-contract.ts +277 -0
  44. package/src/host/index.ts +14 -0
  45. package/src/host/lazy-handle-client.ts +207 -0
  46. package/src/host/request-id.ts +3 -0
  47. package/src/host/sdk-types.ts +528 -0
  48. package/src/host/sdk.ts +43 -0
  49. package/src/host/unique-id.ts +17 -0
  50. package/src/identity-type.ts +96 -0
  51. package/src/index.ts +139 -0
  52. package/src/protocol-root.test.ts +582 -0
  53. package/src/public-grant-scope.ts +25 -0
  54. package/src/runtime-endpoints.ts +69 -0
  55. package/src/scopes.ts +79 -0
  56. package/src/trpc-envelope.ts +9 -0
  57. package/src/version-resolution.ts +18 -0
  58. package/src/widget-commands.ts +162 -0
  59. package/src/widget-config.ts +164 -0
@@ -0,0 +1,25 @@
1
+ import * as z from "zod/mini";
2
+
3
+ const publicGrantScopeIds: readonly [
4
+ "analytics.storage",
5
+ "analytics.measurement",
6
+ "personalization.storage",
7
+ "ads.storage",
8
+ "ads.user_data",
9
+ "ads.personalization",
10
+ ] = [
11
+ "analytics.storage",
12
+ "analytics.measurement",
13
+ "personalization.storage",
14
+ "ads.storage",
15
+ "ads.user_data",
16
+ "ads.personalization",
17
+ ];
18
+
19
+ export const publicGrantScopeIdSchema = z.enum(publicGrantScopeIds);
20
+
21
+ export type PublicGrantScope = z.output<typeof publicGrantScopeIdSchema>;
22
+
23
+ export function isPublicGrantScope(value: unknown): value is PublicGrantScope {
24
+ return publicGrantScopeIdSchema.safeParse(value).success;
25
+ }
@@ -0,0 +1,69 @@
1
+ import * as z from "zod/mini";
2
+
3
+ const absoluteUrlSchema = z.string().check(z.trim(), z.url());
4
+
5
+ export const runtimeEndpointsSchema = z.strictObject({
6
+ apiUrl: z.optional(absoluteUrlSchema),
7
+ coreUrl: z.optional(absoluteUrlSchema),
8
+ });
9
+
10
+ export type RuntimeEndpoints = z.output<typeof runtimeEndpointsSchema>;
11
+
12
+ const parseAbsoluteUrl = (value: string | undefined): URL | null => {
13
+ if (!value) {
14
+ return null;
15
+ }
16
+ try {
17
+ return new URL(value);
18
+ } catch {
19
+ return null;
20
+ }
21
+ };
22
+
23
+ const normalizeOrigin = (value: string | undefined): string | null => {
24
+ const parsed = parseAbsoluteUrl(value);
25
+ if (!parsed) {
26
+ return null;
27
+ }
28
+ return parsed.origin;
29
+ };
30
+
31
+ export const resolveCoreUrl = (
32
+ runtimeEndpoints: RuntimeEndpoints | undefined,
33
+ ): URL | null => {
34
+ return parseAbsoluteUrl(runtimeEndpoints?.coreUrl);
35
+ };
36
+
37
+ export const resolveApiBaseUrl = (
38
+ runtimeEndpoints: RuntimeEndpoints | undefined,
39
+ ): URL | null => {
40
+ const apiUrl = runtimeEndpoints?.apiUrl;
41
+ const parsed = parseAbsoluteUrl(apiUrl);
42
+ if (!parsed) {
43
+ return null;
44
+ }
45
+ if (!parsed.pathname || parsed.pathname === "/") {
46
+ return null;
47
+ }
48
+ if (!parsed.pathname.endsWith("/")) {
49
+ parsed.pathname = `${parsed.pathname}/`;
50
+ }
51
+ return parsed;
52
+ };
53
+
54
+ export const resolveTrpcBaseUrl = (
55
+ runtimeEndpoints: RuntimeEndpoints | undefined,
56
+ ): URL | null => {
57
+ const apiBaseUrl = resolveApiBaseUrl(runtimeEndpoints);
58
+ if (!apiBaseUrl) {
59
+ return null;
60
+ }
61
+ const normalizedApiPath = apiBaseUrl.pathname.endsWith("/")
62
+ ? apiBaseUrl.pathname.slice(0, -1)
63
+ : apiBaseUrl.pathname;
64
+ if (!normalizedApiPath.endsWith("/v1")) {
65
+ return null;
66
+ }
67
+ const trpcPath = normalizedApiPath.slice(0, -"/v1".length) || "/";
68
+ return new URL(trpcPath, `${normalizeOrigin(apiBaseUrl.toString()) ?? ""}/`);
69
+ };
package/src/scopes.ts ADDED
@@ -0,0 +1,79 @@
1
+ import * as z from "zod/mini";
2
+
3
+ export const SCOPE_IDS: readonly [
4
+ "functionality.storage",
5
+ "security.storage",
6
+ "analytics.storage",
7
+ "analytics.measurement",
8
+ "personalization.storage",
9
+ "ads.storage",
10
+ "ads.user_data",
11
+ "ads.personalization",
12
+ ] = [
13
+ "functionality.storage",
14
+ "security.storage",
15
+ "analytics.storage",
16
+ "analytics.measurement",
17
+ "personalization.storage",
18
+ "ads.storage",
19
+ "ads.user_data",
20
+ "ads.personalization",
21
+ ];
22
+
23
+ export const scopeIdSchema = z.enum(SCOPE_IDS);
24
+
25
+ export type Scope = (typeof SCOPE_IDS)[number];
26
+
27
+ export function isScope(value: unknown): value is Scope {
28
+ return scopeIdSchema.safeParse(value).success;
29
+ }
30
+
31
+ export type ScopeMeta = {
32
+ description: string;
33
+ };
34
+
35
+ const scopeMetaById: Record<Scope, ScopeMeta> = {
36
+ "functionality.storage": {
37
+ description:
38
+ "Allow essential storage for core widget behavior (e.g., frequency capping, dismissals, draft answers).",
39
+ },
40
+ "security.storage": {
41
+ description:
42
+ "Allow storage used for fraud/abuse prevention and integrity checks.",
43
+ },
44
+ "analytics.storage": {
45
+ description:
46
+ "Allow storage for analytics identifiers enabling consistent cross-session measurement.",
47
+ },
48
+ "analytics.measurement": {
49
+ description:
50
+ "Allow sending analytics events; can operate without storage when needed.",
51
+ },
52
+ "personalization.storage": {
53
+ description:
54
+ "Allow storage used to personalize the widget UI/targeting (non-ads).",
55
+ },
56
+ "ads.storage": {
57
+ description:
58
+ "Allow storage for advertising identifiers and conversion cookies.",
59
+ },
60
+ "ads.user_data": {
61
+ description:
62
+ "Allow sending user data to ad platforms when legally permitted.",
63
+ },
64
+ "ads.personalization": {
65
+ description:
66
+ "Allow personalized advertising signals and interest-based targeting.",
67
+ },
68
+ };
69
+
70
+ export function getScopeMeta(id: Scope): ScopeMeta {
71
+ return scopeMetaById[id];
72
+ }
73
+
74
+ export function listScopes(): ReadonlyArray<{ id: Scope } & ScopeMeta> {
75
+ return SCOPE_IDS.map((id) => ({
76
+ id,
77
+ ...getScopeMeta(id),
78
+ }));
79
+ }
@@ -0,0 +1,9 @@
1
+ import * as z from "zod/mini";
2
+
3
+ export const trpcSuccessResponseSchema = z.object({
4
+ result: z.object({
5
+ data: z.unknown(),
6
+ }),
7
+ });
8
+
9
+ export type TrpcSuccessResponse = z.output<typeof trpcSuccessResponseSchema>;
@@ -0,0 +1,18 @@
1
+ import * as z from "zod/mini";
2
+
3
+ const versionIdSchema = z.string().check(z.trim(), z.minLength(1));
4
+
5
+ export const flowVersionResolutionSchema = z.object({
6
+ flowVersionId: versionIdSchema,
7
+ });
8
+
9
+ export const themeVersionResolutionSchema = z.object({
10
+ themeVersionId: versionIdSchema,
11
+ });
12
+
13
+ export type FlowVersionResolution = z.output<
14
+ typeof flowVersionResolutionSchema
15
+ >;
16
+ export type ThemeVersionResolution = z.output<
17
+ typeof themeVersionResolutionSchema
18
+ >;
@@ -0,0 +1,162 @@
1
+ import * as z from "zod/mini";
2
+ import {
3
+ appEventCustomerTrackSchema,
4
+ appEventExternalIdSchema,
5
+ } from "./app-event.js";
6
+ import {
7
+ type ConfigureOptions,
8
+ configureOptionsSchema,
9
+ type InitOptions,
10
+ initOptionsSchema,
11
+ } from "./widget-config.js";
12
+
13
+ const flowHandleIdSchema = z.string().check(z.trim(), z.minLength(1));
14
+ const flowIdSchema = z.string().check(z.trim(), z.minLength(1));
15
+
16
+ /** Public command kinds accepted from host / SDK before core dispatch. */
17
+ const publicWidgetCommandKinds = [
18
+ "open",
19
+ "prefetch",
20
+ "prerender",
21
+ "identify",
22
+ "setContainer",
23
+ "setDefaultContainerPolicy",
24
+ "configure",
25
+ "init",
26
+ "close",
27
+ "reset",
28
+ "track",
29
+ ] as const;
30
+
31
+ export const publicWidgetCommandKindSchema = z.enum(publicWidgetCommandKinds);
32
+
33
+ const isHTMLElementLike = (value: unknown): value is HTMLElement => {
34
+ if (typeof value !== "object" || value === null) {
35
+ return false;
36
+ }
37
+ if (typeof HTMLElement !== "undefined" && value instanceof HTMLElement) {
38
+ return true;
39
+ }
40
+ if (!("nodeType" in value) || !("appendChild" in value)) {
41
+ return false;
42
+ }
43
+ const nodeType = Reflect.get(value, "nodeType");
44
+ const appendChild = Reflect.get(value, "appendChild");
45
+ return nodeType === 1 && typeof appendChild === "function";
46
+ };
47
+
48
+ const htmlElementOrNullSchema = z.custom<HTMLElement | null>(
49
+ (value: unknown) => value === null || isHTMLElementLike(value),
50
+ {
51
+ message: "Container must be null or an HTMLElement with nodeType=1",
52
+ },
53
+ );
54
+
55
+ const identifyTraitsSchema = z.record(z.string(), z.unknown());
56
+ const identifyOptionsSchema = z.strictObject({
57
+ externalIds: z.optional(
58
+ z.array(appEventExternalIdSchema).check(z.maxLength(20)),
59
+ ),
60
+ });
61
+ const identifyPayloadSchema = z.union([
62
+ z.strictObject({
63
+ kind: z.literal("identify"),
64
+ traits: identifyTraitsSchema,
65
+ options: z.optional(identifyOptionsSchema),
66
+ }),
67
+ z.strictObject({
68
+ kind: z.literal("identify"),
69
+ userId: z.string().check(z.trim(), z.minLength(1)),
70
+ traits: z.optional(identifyTraitsSchema),
71
+ options: z.optional(identifyOptionsSchema),
72
+ }),
73
+ ]);
74
+
75
+ // TODO(migration): After telemetry confirms no remaining `open.containerRequirement` in host command queues, remove the optional field and transform so unknown keys fail under `.strict()` again.
76
+ // [from=legacy-open-container-requirement] [to=strict-public-open-only] [scope=slice] [priority=low] [impact=medium] [risk=low]
77
+ const legacyOpenContainerRequirementSchema = z.enum(["any", "hostOnly"]);
78
+
79
+ export const containerPolicySchema = z.discriminatedUnion("kind", [
80
+ z.strictObject({
81
+ kind: z.literal("floating"),
82
+ }),
83
+ z.strictObject({
84
+ kind: z.literal("hostContainer"),
85
+ host: htmlElementOrNullSchema,
86
+ sharing: z.enum(["shared", "perFlowRun"]),
87
+ }),
88
+ ]);
89
+
90
+ const openPublicPayloadSchema = z.pipe(
91
+ z.strictObject({
92
+ kind: z.literal("open"),
93
+ flowId: flowIdSchema,
94
+ flowHandleId: z.optional(flowHandleIdSchema),
95
+ container: z.optional(htmlElementOrNullSchema),
96
+ /** Accepted for legacy browser SDK payloads; stripped before dispatch. */
97
+ containerRequirement: z.optional(legacyOpenContainerRequirementSchema),
98
+ hideCloseButton: z.optional(z.boolean()),
99
+ }),
100
+ z.transform(({ containerRequirement: _legacy, ...rest }) => rest),
101
+ );
102
+
103
+ export const publicCommandPayloadSchema = z.union([
104
+ z.discriminatedUnion("kind", [
105
+ openPublicPayloadSchema,
106
+ z.strictObject({
107
+ kind: z.literal("prefetch"),
108
+ flowId: flowIdSchema,
109
+ flowHandleId: z.optional(flowHandleIdSchema),
110
+ }),
111
+ z.strictObject({
112
+ kind: z.literal("prerender"),
113
+ flowId: flowIdSchema,
114
+ flowHandleId: z.optional(flowHandleIdSchema),
115
+ hideCloseButton: z.optional(z.boolean()),
116
+ }),
117
+ z.strictObject({
118
+ kind: z.literal("setContainer"),
119
+ flowHandleId: flowHandleIdSchema,
120
+ container: htmlElementOrNullSchema,
121
+ }),
122
+ z.strictObject({
123
+ kind: z.literal("setDefaultContainerPolicy"),
124
+ policy: containerPolicySchema,
125
+ }),
126
+ z.strictObject({
127
+ kind: z.literal("configure"),
128
+ opts: configureOptionsSchema,
129
+ }),
130
+ z.strictObject({
131
+ kind: z.literal("init"),
132
+ opts: initOptionsSchema,
133
+ }),
134
+ z.strictObject({
135
+ kind: z.literal("close"),
136
+ flowHandleId: z.optional(flowHandleIdSchema),
137
+ }),
138
+ z.strictObject({
139
+ kind: z.literal("reset"),
140
+ }),
141
+ z.extend(appEventCustomerTrackSchema, {
142
+ kind: z.literal("track"),
143
+ }),
144
+ ]),
145
+ identifyPayloadSchema,
146
+ ]);
147
+
148
+ export type PublicCommandPayload = z.output<typeof publicCommandPayloadSchema>;
149
+
150
+ export type ContainerPolicy = z.output<typeof containerPolicySchema>;
151
+
152
+ export function parseInitOptions(input: unknown): InitOptions {
153
+ return initOptionsSchema.parse(input);
154
+ }
155
+
156
+ export function parseConfigureOptions(input: unknown): ConfigureOptions {
157
+ return configureOptionsSchema.parse(input);
158
+ }
159
+
160
+ export function parsePublicCommand(input: unknown): PublicCommandPayload {
161
+ return publicCommandPayloadSchema.parse(input);
162
+ }
@@ -0,0 +1,164 @@
1
+ import * as z from "zod/mini";
2
+ import {
3
+ appEventCapabilitySchema,
4
+ appEventExternalIdSchema,
5
+ appEventFlagSchema,
6
+ } from "./app-event.js";
7
+ import { clientMetaSchema } from "./client-meta.js";
8
+ import { DEFAULT_AUTO_DETECT_COLOR_SCHEME_ATTRS } from "./defaults.js";
9
+ import { runtimeEndpointsSchema } from "./runtime-endpoints.js";
10
+ import { scopeIdSchema } from "./scopes.js";
11
+
12
+ const colorSchemeValueSchema = z.enum(["light", "dark", "system"]);
13
+
14
+ const autoDetectColorSchemeArraySchema = z
15
+ .array(z.string().check(z.trim(), z.minLength(1)))
16
+ .check(z.minLength(1));
17
+
18
+ export const colorSchemeConfigInputSchema = z.union([
19
+ z.strictObject({
20
+ autoDetectColorScheme: autoDetectColorSchemeArraySchema,
21
+ }),
22
+ colorSchemeValueSchema,
23
+ ]);
24
+
25
+ export type ColorSchemeConfigInput = z.output<
26
+ typeof colorSchemeConfigInputSchema
27
+ >;
28
+
29
+ export const telemetryConfigInputSchema = z.object({
30
+ enabled: z.optional(z.boolean()),
31
+ });
32
+
33
+ export type TelemetryConfigInput = z.output<typeof telemetryConfigInputSchema>;
34
+
35
+ export const consentConfigInputSchema = z.union([
36
+ z.enum(["granted", "denied", "pending", "revoked"]),
37
+ z.array(scopeIdSchema),
38
+ ]);
39
+
40
+ export type ConsentConfigInput = z.output<typeof consentConfigInputSchema>;
41
+
42
+ export const debugInputSchema = z.union([
43
+ z.boolean(),
44
+ z.string(),
45
+ z.array(z.string()),
46
+ ]);
47
+
48
+ export type DebugConfigInput = z.output<typeof debugInputSchema>;
49
+
50
+ export const authJwtConfigInputSchema = z.object({
51
+ token: z.string().check(z.trim(), z.minLength(1)),
52
+ });
53
+
54
+ export type AuthJwtConfigInput = z.output<typeof authJwtConfigInputSchema>;
55
+
56
+ export const authConfigInputSchema = z.object({
57
+ jwt: z.optional(z.union([authJwtConfigInputSchema, z.null()])),
58
+ });
59
+
60
+ export type AuthConfigInput = z.output<typeof authConfigInputSchema>;
61
+
62
+ export const identityConfigInputSchema = z.object({
63
+ anonymousId: z.optional(z.string().check(z.trim(), z.minLength(1))),
64
+ userId: z.optional(z.string().check(z.trim(), z.minLength(1))),
65
+ traits: z.optional(z.record(z.string(), z.unknown())),
66
+ externalIds: z.optional(
67
+ z.array(appEventExternalIdSchema).check(z.maxLength(20)),
68
+ ),
69
+ });
70
+
71
+ export type IdentityConfigInput = z.output<typeof identityConfigInputSchema>;
72
+
73
+ export const flagsConfigInputSchema = z.array(appEventFlagSchema);
74
+
75
+ export type FlagsConfigInput = z.output<typeof flagsConfigInputSchema>;
76
+
77
+ export const hostCapabilitiesConfigInputSchema = z.array(
78
+ appEventCapabilitySchema,
79
+ );
80
+
81
+ export type HostCapabilitiesConfigInput = z.output<
82
+ typeof hostCapabilitiesConfigInputSchema
83
+ >;
84
+
85
+ export const configureOptionsSchema = z.strictObject({
86
+ colorScheme: z.optional(colorSchemeConfigInputSchema),
87
+ consent: z.optional(consentConfigInputSchema),
88
+ auth: z.optional(authConfigInputSchema),
89
+ });
90
+
91
+ export type ConfigureOptions = z.output<typeof configureOptionsSchema>;
92
+
93
+ export const initOptionsSchema = z.strictObject({
94
+ apiKey: z.string().check(z.trim(), z.minLength(1)),
95
+ colorScheme: z.optional(colorSchemeConfigInputSchema),
96
+ disableTelemetry: z.optional(z.boolean()),
97
+ enableDebug: z.optional(debugInputSchema),
98
+ defaultConsent: z.optional(consentConfigInputSchema),
99
+ clientMeta: z.optional(clientMetaSchema),
100
+ capabilities: z.optional(z.array(z.string())),
101
+ flags: z.optional(flagsConfigInputSchema),
102
+ hostCapabilities: z.optional(hostCapabilitiesConfigInputSchema),
103
+ runtimeEndpoints: z.optional(runtimeEndpointsSchema),
104
+ });
105
+
106
+ export type InitOptions = z.output<typeof initOptionsSchema>;
107
+
108
+ export const DEFAULT_COLOR_SCHEME_CONFIG: ColorSchemeConfigInput = {
109
+ autoDetectColorScheme: [...DEFAULT_AUTO_DETECT_COLOR_SCHEME_ATTRS],
110
+ };
111
+
112
+ /**
113
+ * Schema for the subset of instance config that can be updated via merge (any source).
114
+ * Telemetry, enableDebug, clientMeta, SDK protocol capabilities, customer
115
+ * flags, and customer host capabilities are set at registration only.
116
+ */
117
+ export const instanceUpdateableConfigSchema = z.strictObject({
118
+ colorScheme: z.optional(colorSchemeConfigInputSchema),
119
+ consent: z.optional(consentConfigInputSchema),
120
+ auth: z.optional(authConfigInputSchema),
121
+ identity: z.optional(identityConfigInputSchema),
122
+ });
123
+
124
+ export type InstanceUpdateableConfig = z.output<
125
+ typeof instanceUpdateableConfigSchema
126
+ >;
127
+
128
+ /** Keys that may be merged into instance config (updatable subset). */
129
+ export const INSTANCE_CONFIG_UPDATE_KEYS: (keyof InstanceUpdateableConfig)[] = [
130
+ "colorScheme",
131
+ "consent",
132
+ "auth",
133
+ "identity",
134
+ ];
135
+
136
+ /**
137
+ * Schema for resolved instance config state. Used when producing or validating
138
+ * stored config (e.g. after register or merge). Defaults are applied here so
139
+ * state always has a full shape; parse partial input with this schema to get
140
+ * resolved state. For update shape use InstanceUpdateableConfig. Configure
141
+ * command opts are in widget-commands (widgetConfigUpdateSchema).
142
+ */
143
+ export const coreInstanceConfigSchema = z.strictObject({
144
+ colorScheme: z._default(z.optional(colorSchemeConfigInputSchema), {
145
+ autoDetectColorScheme: [...DEFAULT_AUTO_DETECT_COLOR_SCHEME_ATTRS],
146
+ }),
147
+ disableTelemetry: z._default(z.optional(z.boolean()), false),
148
+ enableDebug: z._default(z.optional(debugInputSchema), false),
149
+ consent: z.optional(consentConfigInputSchema),
150
+ auth: z.optional(authConfigInputSchema),
151
+ identity: z.optional(identityConfigInputSchema),
152
+ apiKey: z.string().check(z.trim(), z.minLength(1)),
153
+ clientMeta: z.optional(clientMetaSchema),
154
+ capabilities: z.optional(z.array(z.string())),
155
+ flags: z.optional(flagsConfigInputSchema),
156
+ hostCapabilities: z.optional(hostCapabilitiesConfigInputSchema),
157
+ runtimeEndpoints: z.optional(runtimeEndpointsSchema),
158
+ });
159
+
160
+ /** Input shape for instance config (partial; used for register/merge). */
161
+ export type CoreInstanceConfigInput = z.input<typeof coreInstanceConfigSchema>;
162
+
163
+ /** Resolved instance config state (defaults applied). */
164
+ export type CoreInstanceConfig = z.output<typeof coreInstanceConfigSchema>;