@opengeni/contracts 0.27.0 → 0.30.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.
@@ -0,0 +1,233 @@
1
+ import { z } from "zod";
2
+ /** Tenant scope for normalized knowledge and source provenance. */
3
+ export declare const ScopedKnowledgeScopeKind: z.ZodEnum<{
4
+ organization: "organization";
5
+ personal: "personal";
6
+ workspace: "workspace";
7
+ }>;
8
+ export type ScopedKnowledgeScopeKind = z.infer<typeof ScopedKnowledgeScopeKind>;
9
+ export type ScopedKnowledgeScope = {
10
+ kind: "organization";
11
+ workspaceId: null;
12
+ subjectId: null;
13
+ } | {
14
+ kind: "workspace";
15
+ workspaceId: string;
16
+ subjectId: null;
17
+ } | {
18
+ kind: "personal";
19
+ workspaceId: string | null;
20
+ subjectId: string;
21
+ };
22
+ export declare const ScopedKnowledgeActorKind: z.ZodEnum<{
23
+ human: "human";
24
+ service: "service";
25
+ }>;
26
+ export type ScopedKnowledgeActorKind = z.infer<typeof ScopedKnowledgeActorKind>;
27
+ /**
28
+ * Immutable write provenance. A service actor may retain a causal human, but
29
+ * the service identity never substitutes for that human on personal reads.
30
+ */
31
+ export type ScopedKnowledgeActor = {
32
+ kind: ScopedKnowledgeActorKind;
33
+ subjectId: string;
34
+ initiatingHumanSubjectId: string | null;
35
+ };
36
+ export declare const KnowledgeLifecycleState: z.ZodEnum<{
37
+ active: "active";
38
+ deleted: "deleted";
39
+ revoked: "revoked";
40
+ }>;
41
+ export type KnowledgeLifecycleState = z.infer<typeof KnowledgeLifecycleState>;
42
+ export declare const KnowledgeLifecycleEventType: z.ZodEnum<{
43
+ acl_changed: "acl_changed";
44
+ deleted: "deleted";
45
+ object_version_added: "object_version_added";
46
+ restored: "restored";
47
+ revoked: "revoked";
48
+ sync_failed: "sync_failed";
49
+ sync_succeeded: "sync_succeeded";
50
+ }>;
51
+ export type KnowledgeLifecycleEventType = z.infer<typeof KnowledgeLifecycleEventType>;
52
+ export declare const KnowledgeSyncRunState: z.ZodEnum<{
53
+ failed: "failed";
54
+ started: "started";
55
+ succeeded: "succeeded";
56
+ }>;
57
+ export type KnowledgeSyncRunState = z.infer<typeof KnowledgeSyncRunState>;
58
+ export declare const KnowledgeClaimOrigin: z.ZodEnum<{
59
+ explicit: "explicit";
60
+ inferred: "inferred";
61
+ }>;
62
+ export type KnowledgeClaimOrigin = z.infer<typeof KnowledgeClaimOrigin>;
63
+ export declare const KnowledgeClaimRelationType: z.ZodEnum<{
64
+ conflicts_with: "conflicts_with";
65
+ supersedes: "supersedes";
66
+ }>;
67
+ export type KnowledgeClaimRelationType = z.infer<typeof KnowledgeClaimRelationType>;
68
+ export declare const KnowledgeClaimEvidencePolarity: z.ZodEnum<{
69
+ contradicts: "contradicts";
70
+ supports: "supports";
71
+ }>;
72
+ export type KnowledgeClaimEvidencePolarity = z.infer<typeof KnowledgeClaimEvidencePolarity>;
73
+ export declare const KnowledgeClaimReviewState: z.ZodEnum<{
74
+ approved: "approved";
75
+ proposed: "proposed";
76
+ rejected: "rejected";
77
+ revoked: "revoked";
78
+ }>;
79
+ export type KnowledgeClaimReviewState = z.infer<typeof KnowledgeClaimReviewState>;
80
+ export declare const KnowledgeFactObjectKind: z.ZodEnum<{
81
+ boolean: "boolean";
82
+ entity: "entity";
83
+ json: "json";
84
+ number: "number";
85
+ text: "text";
86
+ timestamp: "timestamp";
87
+ }>;
88
+ export type KnowledgeFactObjectKind = z.infer<typeof KnowledgeFactObjectKind>;
89
+ export declare const KnowledgeChangeProposalTargetKind: z.ZodEnum<{
90
+ instruction_policy: "instruction_policy";
91
+ preference: "preference";
92
+ }>;
93
+ export type KnowledgeChangeProposalTargetKind = z.infer<typeof KnowledgeChangeProposalTargetKind>;
94
+ export type KnowledgeProviderRecord = {
95
+ id: string;
96
+ accountId: string;
97
+ scope: ScopedKnowledgeScope;
98
+ providerKey: string;
99
+ externalTenantId: string;
100
+ lifecycleState: KnowledgeLifecycleState;
101
+ lifecycleGeneration: number;
102
+ createdAt: string;
103
+ updatedAt: string;
104
+ };
105
+ export type KnowledgeSourceRecord = {
106
+ id: string;
107
+ accountId: string;
108
+ providerId: string;
109
+ scope: ScopedKnowledgeScope;
110
+ externalSourceId: string;
111
+ sourceKind: string;
112
+ sourceUri: string | null;
113
+ currentAclGeneration: number | null;
114
+ syncGeneration: number;
115
+ syncCursor: string | null;
116
+ lifecycleState: KnowledgeLifecycleState;
117
+ lifecycleGeneration: number;
118
+ createdAt: string;
119
+ updatedAt: string;
120
+ };
121
+ export type KnowledgeSourceAclVersionRecord = {
122
+ id: string;
123
+ accountId: string;
124
+ sourceId: string;
125
+ generation: number;
126
+ aclVersion: string | null;
127
+ aclHash: string;
128
+ audience: ScopedKnowledgeScope;
129
+ agentAccess: boolean;
130
+ createdAt: string;
131
+ };
132
+ export type KnowledgeSyncRunRecord = {
133
+ id: string;
134
+ accountId: string;
135
+ sourceId: string;
136
+ operationId: string;
137
+ state: KnowledgeSyncRunState;
138
+ inputSyncGeneration: number;
139
+ inputLifecycleGeneration: number;
140
+ inputCursor: string | null;
141
+ outputCursor: string | null;
142
+ watermark: string | null;
143
+ metadata: Record<string, unknown>;
144
+ errorCode: string | null;
145
+ startedAt: string;
146
+ completedAt: string | null;
147
+ };
148
+ export type KnowledgeSourceObjectRecord = {
149
+ id: string;
150
+ accountId: string;
151
+ sourceId: string;
152
+ scope: ScopedKnowledgeScope;
153
+ externalObjectId: string;
154
+ documentId: string | null;
155
+ lifecycleState: KnowledgeLifecycleState;
156
+ lifecycleGeneration: number;
157
+ versionGeneration: number;
158
+ currentVersionId: string | null;
159
+ createdAt: string;
160
+ updatedAt: string;
161
+ };
162
+ export type KnowledgeDocumentVersionRecord = {
163
+ id: string;
164
+ accountId: string;
165
+ sourceId: string;
166
+ objectId: string;
167
+ scope: ScopedKnowledgeScope;
168
+ versionGeneration: number;
169
+ externalVersionId: string;
170
+ contentSha256: string;
171
+ ingestionKey: string;
172
+ aclVersionId: string;
173
+ aclGeneration: number;
174
+ documentId: string | null;
175
+ fileId: string | null;
176
+ createdAt: string;
177
+ };
178
+ export type KnowledgeEntityRecord = {
179
+ id: string;
180
+ accountId: string;
181
+ scope: ScopedKnowledgeScope;
182
+ entityType: string;
183
+ normalizedKey: string;
184
+ displayName: string;
185
+ createdAt: string;
186
+ };
187
+ export type KnowledgeFactRecord = {
188
+ id: string;
189
+ accountId: string;
190
+ scope: ScopedKnowledgeScope;
191
+ subjectEntityId: string;
192
+ predicateKey: string;
193
+ objectKind: KnowledgeFactObjectKind;
194
+ objectEntityId: string | null;
195
+ objectValue: unknown | null;
196
+ objectHash: string;
197
+ createdAt: string;
198
+ };
199
+ export type KnowledgeClaimRecord = {
200
+ id: string;
201
+ accountId: string;
202
+ scope: ScopedKnowledgeScope;
203
+ factId: string;
204
+ origin: KnowledgeClaimOrigin;
205
+ confidenceBps: number;
206
+ effectiveAt: string;
207
+ expiresAt: string | null;
208
+ extractionMethod: string;
209
+ modelProvider: string | null;
210
+ modelName: string | null;
211
+ modelVersion: string | null;
212
+ createdAt: string;
213
+ };
214
+ export type EligibleKnowledgeClaim = {
215
+ claim: KnowledgeClaimRecord;
216
+ fact: KnowledgeFactRecord;
217
+ reviewState: "approved";
218
+ supportingEvidenceCount: number;
219
+ };
220
+ export type KnowledgeChangeProposalRecord = {
221
+ id: string;
222
+ accountId: string;
223
+ scope: ScopedKnowledgeScope;
224
+ targetKind: KnowledgeChangeProposalTargetKind;
225
+ targetScope: string;
226
+ targetKey: string | null;
227
+ content: string;
228
+ contentHash: string;
229
+ claimId: string;
230
+ evidenceId: string;
231
+ status: "proposed";
232
+ createdAt: string;
233
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengeni/contracts",
3
- "version": "0.27.0",
3
+ "version": "0.30.0",
4
4
  "description": "Shared zod schemas and wire-contract types for the OpenGeni API.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
package/src/index.ts CHANGED
@@ -676,6 +676,10 @@ export const DEFAULT_FIRST_PARTY_MCP_PERMISSIONS = [
676
676
  "sessions:read",
677
677
  "sessions:create",
678
678
  "sessions:control",
679
+ // Read-only connection discovery lets the selected first-party connector
680
+ // tools resolve an already-installed workspace principal. Credentials stay
681
+ // inside the broker and remain subject to each tool's own authorization.
682
+ "connections:read",
679
683
  "variable-sets:use",
680
684
  "variable-sets:manage",
681
685
  "rigs:use",
@@ -732,6 +736,11 @@ export const FIRST_PARTY_MCP_TOOL_NAMES = [
732
736
  "social_connections_list",
733
737
  "social_posts_recent",
734
738
  "social_daily_analysis_context",
739
+ "social_search_live",
740
+ "social_mentions_live",
741
+ "social_thread_fetch",
742
+ "social_posts_sync",
743
+ "social_post_reply",
735
744
  "scheduled_tasks_list",
736
745
  "scheduled_tasks_get",
737
746
  "scheduled_tasks_create",
@@ -760,12 +769,13 @@ export const FirstPartyMcpToolName = z.enum(FIRST_PARTY_MCP_TOOL_NAMES);
760
769
  export type FirstPartyMcpToolName = z.infer<typeof FirstPartyMcpToolName>;
761
770
 
762
771
  /**
763
- * Every catalogued OpenGeni tool is selected by default. Registration-time
764
- * authorization remains the independent access boundary; an explicit session
765
- * policy may narrow this model-visible set.
772
+ * Connector-wide tools are explicit-only. Ordinary session omission selects
773
+ * the non-connector catalog, while an explicit session policy may still select
774
+ * any catalogued connector tool and remains independently permission-gated.
766
775
  */
767
- export const DEFAULT_FIRST_PARTY_MCP_TOOLS =
768
- FIRST_PARTY_MCP_TOOL_NAMES satisfies readonly FirstPartyMcpToolName[];
776
+ export const DEFAULT_FIRST_PARTY_MCP_TOOLS = FIRST_PARTY_MCP_TOOL_NAMES.filter(
777
+ (name) => !name.startsWith("social_") && !name.startsWith("slack_bot_"),
778
+ ) satisfies readonly FirstPartyMcpToolName[];
769
779
 
770
780
  export function prefixedMcpToolName(registryId: string, toolName: string): string {
771
781
  return `${registryId}__${toolName}`;
@@ -2538,6 +2548,46 @@ export const McpServerConnectionRef = z
2538
2548
  });
2539
2549
  export type McpServerConnectionRef = z.infer<typeof McpServerConnectionRef>;
2540
2550
 
2551
+ /** Internal frozen authority for one personal MCP connection. */
2552
+ export const McpPersonalConnectionDelegation = z
2553
+ .object({
2554
+ serverId: z.string().min(1).max(256),
2555
+ connectionId: z.string().uuid(),
2556
+ ownerSubjectId: z.string().min(1).max(512),
2557
+ providerDomain: z.string().min(1).max(2048),
2558
+ kind: z.enum(["oauth2", "api_key", "app_install", "delegated"]).optional(),
2559
+ })
2560
+ .strict();
2561
+ export type McpPersonalConnectionDelegation = z.infer<typeof McpPersonalConnectionDelegation>;
2562
+
2563
+ /**
2564
+ * Exact personal MCP authority frozen on one causal turn or scheduled task.
2565
+ * One server can have at most one grant; bounded validation keeps corrupt JSON
2566
+ * from becoming executable credential authority at a DB read boundary.
2567
+ */
2568
+ export const McpPersonalConnectionDelegations = z
2569
+ .array(McpPersonalConnectionDelegation)
2570
+ .max(128)
2571
+ .superRefine((delegations, context) => {
2572
+ const seen = new Set<string>();
2573
+ for (const [index, delegation] of delegations.entries()) {
2574
+ if (seen.has(delegation.serverId)) {
2575
+ context.addIssue({
2576
+ code: "custom",
2577
+ message: "personal MCP delegations must be unique by serverId",
2578
+ path: [index, "serverId"],
2579
+ });
2580
+ }
2581
+ seen.add(delegation.serverId);
2582
+ }
2583
+ });
2584
+
2585
+ export const McpPersonalConnectionSummary = McpPersonalConnectionDelegation.pick({
2586
+ serverId: true,
2587
+ providerDomain: true,
2588
+ });
2589
+ export type McpPersonalConnectionSummary = z.infer<typeof McpPersonalConnectionSummary>;
2590
+
2541
2591
  export type McpCredentialsRequest = {
2542
2592
  accountId: string;
2543
2593
  workspaceId: string;
@@ -2565,6 +2615,7 @@ export type McpCredentialsRequest = {
2565
2615
 
2566
2616
  export type McpCredentialAuthNeededReason =
2567
2617
  | CredentialAuthNeededReason
2618
+ | "personal_authority_unavailable"
2568
2619
  | "unsupported_auth"
2569
2620
  | "resource_scope_unavailable";
2570
2621
 
@@ -4011,6 +4062,8 @@ export const SessionTurn = z.object({
4011
4062
  lineage: z.record(z.string(), z.unknown()),
4012
4063
  initiator: TurnInitiator,
4013
4064
  initiatorContext: TurnInitiatorContext,
4065
+ /** Secret-safe projection of the exact personal authority frozen on this turn. */
4066
+ personalConnections: z.array(McpPersonalConnectionSummary).default([]),
4014
4067
  cancelledBy: z.string().nullable(),
4015
4068
  cancelReason: z.string().nullable(),
4016
4069
  startedAt: z.string().nullable(),
@@ -4498,6 +4551,8 @@ export type SessionPendingInputPreview = z.infer<typeof SessionPendingInputPrevi
4498
4551
  export const SessionQueueSnapshot = z.object({
4499
4552
  version: z.number().int().nonnegative(),
4500
4553
  effectiveControl: EffectiveSessionControl,
4554
+ /** Secret-safe personal MCP summaries frozen on the exact active turn. */
4555
+ activePersonalConnections: z.array(McpPersonalConnectionSummary).default([]),
4501
4556
  /**
4502
4557
  * True while the latest attempt is interrupted but has not durably proved
4503
4558
  * quiescence: no more inference, user-visible output, or workspace-persistence
@@ -4865,6 +4920,9 @@ export const ScheduledTask = z.object({
4865
4920
  runMode: ScheduledTaskRunMode,
4866
4921
  overlapPolicy: ScheduledTaskOverlapPolicy,
4867
4922
  agentConfig: ScheduledTaskAgentConfig,
4923
+ createdBy: TurnInitiator.default({ kind: "service", subjectId: "unattributed-legacy" }),
4924
+ createdByContext: TurnInitiatorContext.default({}),
4925
+ personalConnections: z.array(McpPersonalConnectionSummary).default([]),
4868
4926
  reusableSessionId: z.string().uuid().nullable(),
4869
4927
  variableSetId: z.string().uuid().nullable().default(null),
4870
4928
  /** @deprecated use variableSetId */
@@ -5185,6 +5243,7 @@ export type EnablePackRequest = z.infer<typeof EnablePackRequest>;
5185
5243
 
5186
5244
  export const SocialProvider = z.enum([
5187
5245
  "x",
5246
+ "reddit",
5188
5247
  "linkedin",
5189
5248
  "instagram",
5190
5249
  "facebook",
@@ -5257,6 +5316,19 @@ export const CreateSocialPostRequest = z.object({
5257
5316
  });
5258
5317
  export type CreateSocialPostRequest = z.infer<typeof CreateSocialPostRequest>;
5259
5318
 
5319
+ // Social OAuth is first-party (X / Reddit REST APIs), distinct from the MCP
5320
+ // integrations OAuth flow: these providers have no MCP resource metadata, so
5321
+ // endpoints are pinned per provider and tokens live in social_connections.
5322
+ export const SocialOAuthProviderId = z.enum(["x", "reddit"]);
5323
+ export type SocialOAuthProviderId = z.infer<typeof SocialOAuthProviderId>;
5324
+
5325
+ export const SocialOAuthStartRequest = z.object({
5326
+ provider: SocialOAuthProviderId,
5327
+ scopes: z.array(z.string().min(1)).optional(),
5328
+ returnPath: z.string().optional(),
5329
+ });
5330
+ export type SocialOAuthStartRequest = z.infer<typeof SocialOAuthStartRequest>;
5331
+
5260
5332
  export const ConnectionKind = z.enum(["oauth2", "api_key", "app_install", "delegated"]);
5261
5333
  export type ConnectionKind = z.infer<typeof ConnectionKind>;
5262
5334
 
@@ -5306,12 +5378,81 @@ export const ConnectionMetadata = z.object({
5306
5378
  });
5307
5379
  export type ConnectionMetadata = z.infer<typeof ConnectionMetadata>;
5308
5380
 
5381
+ type PersonalSlackCanonicalConnection = Pick<
5382
+ ConnectionMetadata,
5383
+ "id" | "status" | "createdAt" | "updatedAt"
5384
+ >;
5385
+
5386
+ const PERSONAL_SLACK_CONNECTION_STATUS_RANK = {
5387
+ active: 0,
5388
+ needs_reauth: 1,
5389
+ error: 2,
5390
+ revoked: 3,
5391
+ } as const satisfies Record<ConnectionStatus, number>;
5392
+
5393
+ /**
5394
+ * Canonical ordering for duplicate subject-owned Personal Slack rows.
5395
+ *
5396
+ * PostgreSQL broker lookup mirrors this exact sequence: usable status first,
5397
+ * then newest update, newest creation, and immutable UUID descending.
5398
+ */
5399
+ export function comparePersonalSlackCanonicalConnections(
5400
+ left: PersonalSlackCanonicalConnection,
5401
+ right: PersonalSlackCanonicalConnection,
5402
+ ): number {
5403
+ const statusDelta =
5404
+ PERSONAL_SLACK_CONNECTION_STATUS_RANK[left.status] -
5405
+ PERSONAL_SLACK_CONNECTION_STATUS_RANK[right.status];
5406
+ if (statusDelta !== 0) return statusDelta;
5407
+
5408
+ const updatedAtDelta = compareDescending(
5409
+ canonicalConnectionTimestamp(left.updatedAt),
5410
+ canonicalConnectionTimestamp(right.updatedAt),
5411
+ );
5412
+ if (updatedAtDelta !== 0) return updatedAtDelta;
5413
+
5414
+ const createdAtDelta = compareDescending(
5415
+ canonicalConnectionTimestamp(left.createdAt),
5416
+ canonicalConnectionTimestamp(right.createdAt),
5417
+ );
5418
+ if (createdAtDelta !== 0) return createdAtDelta;
5419
+
5420
+ return compareDescending(left.id, right.id);
5421
+ }
5422
+
5423
+ export function selectCanonicalPersonalSlackConnection<T extends PersonalSlackCanonicalConnection>(
5424
+ connections: readonly T[],
5425
+ ): T | null {
5426
+ let selected: T | null = null;
5427
+ for (const connection of connections) {
5428
+ if (!selected || comparePersonalSlackCanonicalConnections(connection, selected) < 0) {
5429
+ selected = connection;
5430
+ }
5431
+ }
5432
+ return selected;
5433
+ }
5434
+
5435
+ function canonicalConnectionTimestamp(value: string): number {
5436
+ const timestamp = Date.parse(value);
5437
+ return Number.isFinite(timestamp) ? timestamp : Number.NEGATIVE_INFINITY;
5438
+ }
5439
+
5440
+ function compareDescending(left: number | string, right: number | string): number {
5441
+ if (left === right) return 0;
5442
+ return left > right ? -1 : 1;
5443
+ }
5444
+
5309
5445
  export const ConnectionCredentialBundle = z.record(z.string(), z.unknown());
5310
5446
  export type ConnectionCredentialBundle = z.infer<typeof ConnectionCredentialBundle>;
5311
5447
 
5448
+ export const ConnectionOwnership = z.enum(["workspace", "personal"]);
5449
+ export type ConnectionOwnership = z.infer<typeof ConnectionOwnership>;
5450
+
5312
5451
  export const CreateConnectionRequest = z.object({
5313
5452
  providerDomain: z.string().min(1),
5314
5453
  kind: ConnectionKind,
5454
+ ownership: ConnectionOwnership.optional(),
5455
+ /** @deprecated use ownership */
5315
5456
  subjectId: z.string().min(1).nullable().optional(),
5316
5457
  credential: ConnectionCredentialBundle,
5317
5458
  grantedScopes: z.array(z.string().min(1)).default([]),
@@ -5361,6 +5502,7 @@ export const OAuthStartRequest = z
5361
5502
  requestedScopes: z.array(z.string().min(1)).default([]),
5362
5503
  returnPath: z.string().min(1).optional(),
5363
5504
  connectionId: z.string().uuid().optional(),
5505
+ ownership: ConnectionOwnership.optional(),
5364
5506
  oauthClient: z
5365
5507
  .object({
5366
5508
  clientId: z.string().min(1),
@@ -5646,8 +5788,8 @@ export const Session = z.object({
5646
5788
  // Non-default first-party MCP token permissions (manager-style sessions);
5647
5789
  // null means the fixed worker default set.
5648
5790
  firstPartyMcpPermissions: z.array(Permission).nullable(),
5649
- // Exact model-visible OpenGeni selection. All catalogued tools are selected
5650
- // by default; [] intentionally selects none.
5791
+ // Exact model-visible OpenGeni selection. The default omits connector-wide
5792
+ // tools; [] intentionally selects none.
5651
5793
  firstPartyMcpTools: z.array(FirstPartyMcpToolName),
5652
5794
  // Per-session third-party MCP servers, metadata only. Credential values are
5653
5795
  // write-only and never appear here.
@@ -6155,6 +6297,7 @@ export const ToolAuthNeededPayload = z.object({
6155
6297
  "expired",
6156
6298
  "insufficient_scope",
6157
6299
  "refresh_failed",
6300
+ "personal_authority_unavailable",
6158
6301
  "unsupported_auth",
6159
6302
  "resource_scope_unavailable",
6160
6303
  ]),
@@ -7948,7 +8091,8 @@ export const CreateSessionRequest = withVariableSetIdAlias({
7948
8091
  // rejected; creation never silently expands a child beyond that set.
7949
8092
  firstPartyMcpPermissions: z.array(Permission).optional(),
7950
8093
  // Exact model-visible selection from the broad first-party OpenGeni MCP
7951
- // catalog. Omission selects the full catalog; [] intentionally exposes none.
8094
+ // catalog. Omission selects the safe non-connector default; [] intentionally
8095
+ // exposes none.
7952
8096
  // This does not grant authority: every registered tool is permission-gated.
7953
8097
  firstPartyMcpTools: z.array(FirstPartyMcpToolName).optional(),
7954
8098
  // Third-party MCP servers attached only to this session. For an agent-created
@@ -9326,6 +9470,35 @@ export const ClientConfig = /* @__PURE__ */ defineModelContractSchema(() =>
9326
9470
  }),
9327
9471
  productAccessMode: ProductAccessMode,
9328
9472
  auth: ClientAuthConfig.default({ mode: "none" }),
9473
+ analytics: z
9474
+ .object({
9475
+ consentRequired: z.boolean(),
9476
+ providers: z.object({
9477
+ reo: z
9478
+ .object({
9479
+ clientId: z
9480
+ .string()
9481
+ .max(128)
9482
+ .regex(/^[A-Za-z0-9_-]+$/u),
9483
+ })
9484
+ .optional(),
9485
+ posthog: z
9486
+ .object({
9487
+ projectKey: z.string().min(1).max(256),
9488
+ host: z.string().url().max(2_048),
9489
+ })
9490
+ .optional(),
9491
+ ga4: z
9492
+ .object({
9493
+ measurementId: z
9494
+ .string()
9495
+ .max(32)
9496
+ .regex(/^G-[A-Z0-9]+$/u),
9497
+ })
9498
+ .optional(),
9499
+ }),
9500
+ })
9501
+ .default({ consentRequired: true, providers: {} }),
9329
9502
  // Server-wide hint: does this deployment support Channel-A structured services
9330
9503
  // at all (P4.4). Per-session availability is negotiated on /stream-capabilities
9331
9504
  // (it depends on the session's pinned backend); this is the coarse on/off the
@@ -9421,3 +9594,4 @@ export * from "./secret-redaction";
9421
9594
  export * from "./workspace-instruction-policies";
9422
9595
  export * from "./workspace-state";
9423
9596
  export * from "./preference-registry";
9597
+ export * from "./scoped-knowledge";