@opengeni/contracts 0.40.0 → 0.50.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 (148) hide show
  1. package/dist/atlassian.d.ts +278 -0
  2. package/dist/atlassian.js +57 -0
  3. package/dist/atlassian.js.map +1 -0
  4. package/dist/canonical-human-identities.d.ts +157 -0
  5. package/dist/canonical-human-identities.js +23 -0
  6. package/dist/canonical-human-identities.js.map +1 -0
  7. package/dist/{chunk-7JZMI22I.js → chunk-5UDNP7AO.js} +34 -2
  8. package/dist/chunk-5UDNP7AO.js.map +1 -0
  9. package/dist/chunk-BVGIYW7D.js +11 -0
  10. package/dist/chunk-BVGIYW7D.js.map +1 -0
  11. package/dist/chunk-CADSX6UN.js +1145 -0
  12. package/dist/chunk-CADSX6UN.js.map +1 -0
  13. package/dist/chunk-CM6BMECR.js +24 -0
  14. package/dist/chunk-CM6BMECR.js.map +1 -0
  15. package/dist/chunk-D2HKF2EF.js +1460 -0
  16. package/dist/chunk-D2HKF2EF.js.map +1 -0
  17. package/dist/chunk-JBCIY7QD.js +194 -0
  18. package/dist/chunk-JBCIY7QD.js.map +1 -0
  19. package/dist/chunk-JUFK3T4Q.js +80 -0
  20. package/dist/chunk-JUFK3T4Q.js.map +1 -0
  21. package/dist/chunk-LSB7GRLG.js +779 -0
  22. package/dist/chunk-LSB7GRLG.js.map +1 -0
  23. package/dist/chunk-LW7OH6CS.js +382 -0
  24. package/dist/chunk-LW7OH6CS.js.map +1 -0
  25. package/dist/chunk-M5ZGRVIQ.js +719 -0
  26. package/dist/chunk-M5ZGRVIQ.js.map +1 -0
  27. package/dist/chunk-MYBZRDTY.js +340 -0
  28. package/dist/chunk-MYBZRDTY.js.map +1 -0
  29. package/dist/chunk-Q6A7XT2N.js +16734 -0
  30. package/dist/chunk-Q6A7XT2N.js.map +1 -0
  31. package/dist/chunk-RIY7GYQS.js +1204 -0
  32. package/dist/chunk-RIY7GYQS.js.map +1 -0
  33. package/dist/chunk-TZ6YEYN4.js +386 -0
  34. package/dist/chunk-TZ6YEYN4.js.map +1 -0
  35. package/dist/chunk-Y3MLSLB4.js +392 -0
  36. package/dist/chunk-Y3MLSLB4.js.map +1 -0
  37. package/dist/chunk-YRKRZ6OF.js +134 -0
  38. package/dist/chunk-YRKRZ6OF.js.map +1 -0
  39. package/dist/codex-provider-account-authority.d.ts +23 -0
  40. package/dist/codex-provider-account-authority.js +23 -0
  41. package/dist/codex-provider-account-authority.js.map +1 -0
  42. package/dist/company-profile.d.ts +629 -0
  43. package/dist/document-artifact-commands.d.ts +282 -0
  44. package/dist/document-artifact-commands.js +54 -0
  45. package/dist/document-artifact-commands.js.map +1 -0
  46. package/dist/document-artifact-query.d.ts +2 -0
  47. package/dist/document-artifact-query.js +54 -0
  48. package/dist/document-artifact-query.js.map +1 -0
  49. package/dist/editable-artifact-binary.d.ts +56 -0
  50. package/dist/editable-artifact-causal-frontier.d.ts +12 -0
  51. package/dist/editable-artifact-causal-frontier.js +18 -0
  52. package/dist/editable-artifact-causal-frontier.js.map +1 -0
  53. package/dist/editable-artifact-codec-registry.d.ts +48 -0
  54. package/dist/editable-artifact-codec-registry.js +20 -0
  55. package/dist/editable-artifact-codec-registry.js.map +1 -0
  56. package/dist/editable-artifact-committed-transaction.d.ts +35 -0
  57. package/dist/editable-artifact-committed-transaction.js +19 -0
  58. package/dist/editable-artifact-committed-transaction.js.map +1 -0
  59. package/dist/editable-artifact-live.d.ts +276 -0
  60. package/dist/editable-artifact-live.js +907 -0
  61. package/dist/editable-artifact-live.js.map +1 -0
  62. package/dist/editable-artifact-serialized-commit.d.ts +53 -0
  63. package/dist/editable-artifact-serialized-commit.js +26 -0
  64. package/dist/editable-artifact-serialized-commit.js.map +1 -0
  65. package/dist/editable-artifact-versions.d.ts +6 -0
  66. package/dist/editable-artifact-versions.js +11 -0
  67. package/dist/editable-artifact-versions.js.map +1 -0
  68. package/dist/editable-artifacts.d.ts +46 -0
  69. package/dist/editable-artifacts.js +236 -0
  70. package/dist/editable-artifacts.js.map +1 -0
  71. package/dist/event-preview.d.ts +1 -1
  72. package/dist/google-drive.d.ts +114 -2
  73. package/dist/google-drive.js +72 -14
  74. package/dist/google-drive.js.map +1 -1
  75. package/dist/image-generation.d.ts +70 -0
  76. package/dist/index.d.ts +5661 -166
  77. package/dist/index.js +1438 -21
  78. package/dist/interaction.d.ts +8290 -0
  79. package/dist/knowledge.d.ts +543 -0
  80. package/dist/memory-slack-delivery.d.ts +397 -0
  81. package/dist/presentation-artifact-commands.d.ts +349 -0
  82. package/dist/presentation-artifact-commands.js +66 -0
  83. package/dist/presentation-artifact-commands.js.map +1 -0
  84. package/dist/presentation-artifact-query.d.ts +2 -0
  85. package/dist/presentation-artifact-query.js +66 -0
  86. package/dist/presentation-artifact-query.js.map +1 -0
  87. package/dist/retained-output.d.ts +24 -0
  88. package/dist/sandbox-snapshots.d.ts +9 -0
  89. package/dist/scoped-knowledge.d.ts +14 -13
  90. package/dist/slack-bot-scopes.d.ts +4 -0
  91. package/dist/slack-bot-scopes.js +3 -1
  92. package/dist/slack-task-policy.d.ts +294 -0
  93. package/dist/spreadsheet-artifact-commands.d.ts +101 -0
  94. package/dist/spreadsheet-artifact-commands.js +27 -0
  95. package/dist/spreadsheet-artifact-commands.js.map +1 -0
  96. package/dist/spreadsheet-artifact-date.d.ts +4 -0
  97. package/dist/spreadsheet-artifact-query.d.ts +120 -0
  98. package/dist/spreadsheet-artifact-query.js +48 -0
  99. package/dist/spreadsheet-artifact-query.js.map +1 -0
  100. package/dist/task-notes.d.ts +128 -0
  101. package/dist/tool-catalog.d.ts +576 -0
  102. package/dist/video-generation.d.ts +580 -0
  103. package/dist/video-generation.js +59 -0
  104. package/dist/video-generation.js.map +1 -0
  105. package/dist/workspace-learning-policy.d.ts +208 -0
  106. package/dist/xai-provider-account-authority.d.ts +23 -0
  107. package/dist/xai-provider-account-authority.js +9 -0
  108. package/dist/xai-provider-account-authority.js.map +1 -0
  109. package/package.json +73 -1
  110. package/src/artifacts.ts +1 -1
  111. package/src/atlassian.ts +160 -0
  112. package/src/canonical-human-identities.ts +93 -0
  113. package/src/codex-provider-account-authority.ts +37 -0
  114. package/src/company-profile.ts +306 -0
  115. package/src/document-artifact-commands.ts +1709 -0
  116. package/src/document-artifact-query.ts +2 -0
  117. package/src/editable-artifact-binary.ts +396 -0
  118. package/src/editable-artifact-causal-frontier.ts +154 -0
  119. package/src/editable-artifact-codec-registry.ts +261 -0
  120. package/src/editable-artifact-committed-transaction.ts +478 -0
  121. package/src/editable-artifact-live.ts +1379 -0
  122. package/src/editable-artifact-serialized-commit.ts +494 -0
  123. package/src/editable-artifact-versions.ts +8 -0
  124. package/src/editable-artifacts.ts +473 -0
  125. package/src/google-drive.ts +68 -15
  126. package/src/image-generation.ts +106 -0
  127. package/src/index.ts +2920 -326
  128. package/src/interaction.ts +3173 -0
  129. package/src/knowledge.ts +207 -0
  130. package/src/memory-slack-delivery.ts +183 -0
  131. package/src/presentation-artifact-commands.ts +2035 -0
  132. package/src/presentation-artifact-query.ts +2 -0
  133. package/src/retained-output.ts +149 -2
  134. package/src/sandbox-snapshots.ts +28 -1
  135. package/src/scoped-knowledge.ts +18 -4
  136. package/src/slack-bot-scopes.ts +34 -1
  137. package/src/slack-task-policy.ts +215 -0
  138. package/src/spreadsheet-artifact-commands.ts +999 -0
  139. package/src/spreadsheet-artifact-date.ts +21 -0
  140. package/src/spreadsheet-artifact-query.ts +1411 -0
  141. package/src/task-notes.ts +92 -0
  142. package/src/tool-catalog.ts +334 -0
  143. package/src/video-generation.ts +431 -0
  144. package/src/workspace-learning-policy.ts +233 -0
  145. package/src/xai-provider-account-authority.ts +37 -0
  146. package/dist/chunk-5HR2GPSC.js +0 -11031
  147. package/dist/chunk-5HR2GPSC.js.map +0 -1
  148. package/dist/chunk-7JZMI22I.js.map +0 -1
package/src/index.ts CHANGED
@@ -1,15 +1,28 @@
1
1
  import { z } from "zod";
2
+ import { ScopedKnowledgeScope } from "./scoped-knowledge";
2
3
  import {
3
4
  boundSessionEventPayload,
4
5
  measureSessionEventJson,
5
6
  sessionEventJsonBytes,
6
7
  type SessionEventBoundarySurface,
7
8
  } from "./event-preview";
9
+ import { MemorySlackPublicationDistribution } from "./memory-slack-delivery";
8
10
  import { WorkspaceInstructionPolicyRoleKeyInput } from "./workspace-instruction-policies";
9
11
  import { ClientResumableVoiceInputConfig } from "./transcription-recordings";
12
+ import { MediaGenerationResult } from "./video-generation";
10
13
 
11
14
  export * from "./slack-bot-scopes";
15
+ export * from "./slack-task-policy";
16
+ export * from "./atlassian";
12
17
  export * from "./connector-destinations";
18
+ export * from "./memory-slack-delivery";
19
+ export * from "./image-generation";
20
+ export * from "./video-generation";
21
+ export * from "./editable-artifacts";
22
+ export * from "./editable-artifact-committed-transaction";
23
+ export * from "./editable-artifact-serialized-commit";
24
+ export * from "./tool-catalog";
25
+ export * from "./interaction";
13
26
 
14
27
  export {
15
28
  CreateWorkspaceArtifactRequest,
@@ -71,6 +84,8 @@ export {
71
84
  COMPUTER_SCREENSHOT_MAX_PIXELS,
72
85
  COMPUTER_SCREENSHOT_RETENTION_MS,
73
86
  COMPUTER_SCREENSHOT_WORKSPACE_QUOTA_BYTES,
87
+ GENERATED_IMAGE_MAX_BYTES,
88
+ GENERATED_VIDEO_MAX_BYTES,
74
89
  RETAINED_OUTPUT_DEFAULT_PAGE_BYTES,
75
90
  RETAINED_OUTPUT_MAX_PAGE_BYTES,
76
91
  RETAINED_OUTPUT_RECEIPT_MAX_BYTES,
@@ -81,6 +96,8 @@ export {
81
96
  RetainedOutputKind,
82
97
  RetainedOutputUnavailableReason,
83
98
  retainedArtifactReferenceFromFile,
99
+ retainedGeneratedImageReferenceFromFile,
100
+ retainedGeneratedVideoReferenceFromFile,
84
101
  retainedScreenshotReferenceFromFile,
85
102
  retainedOutputUnavailable,
86
103
  resolveRetainedOutputRange,
@@ -88,6 +105,8 @@ export {
88
105
  type RetainedArtifactFileInput,
89
106
  type RetainedArtifactMetadata,
90
107
  type RetainedArtifactReference,
108
+ type RetainedGeneratedImageArtifactInput,
109
+ type RetainedGeneratedVideoArtifactInput,
91
110
  type RetainedScreenshotArtifactInput,
92
111
  type RetainedArtifactUnavailable,
93
112
  type RetainedOutputAvailableEvidence,
@@ -101,6 +120,7 @@ export {
101
120
  WORKSPACE_ARCHIVE_DESCRIPTOR_VERSION,
102
121
  backendForNativeSnapshotProvider,
103
122
  decodeNativeSnapshotRef,
123
+ encodeNativeSnapshotRef,
104
124
  parseWorkspaceArchiveDescriptor,
105
125
  type NativeSnapshotDescriptor,
106
126
  type NativeSnapshotProvider,
@@ -147,6 +167,54 @@ export const SandboxBackend = z.enum([
147
167
  ]);
148
168
  export type SandboxBackend = z.infer<typeof SandboxBackend>;
149
169
 
170
+ // OpenGeni-owned identity carried beside the provider's opaque SDK envelope.
171
+ // Provider serializers intentionally use different keys (`sandboxId`,
172
+ // `devboxId`, `sandboxName`, ...); durable lease logic must not need to learn a
173
+ // new provider's private state shape. Existing envelopes remain readable via
174
+ // the legacy keys below, while every newly-serialized envelope carries this
175
+ // stable field.
176
+ export const OPENGENI_SANDBOX_PROVIDER_INSTANCE_ID_FIELD = "opengeniProviderInstanceId" as const;
177
+ export const SANDBOX_PROVIDER_INSTANCE_ID_FIELDS_BY_BACKEND = {
178
+ docker: ["containerId"],
179
+ modal: ["sandboxId"],
180
+ local: ["workspaceRootPath"],
181
+ none: [],
182
+ daytona: ["sandboxId"],
183
+ runloop: ["devboxId"],
184
+ e2b: ["sandboxId"],
185
+ // A Blaxel name is reusable after deletion. The SDK persists a canonical
186
+ // metadata identity (name + creation timestamp + workspace) specifically to
187
+ // distinguish a later sandbox created under the same name.
188
+ blaxel: ["sandboxIdentity"],
189
+ cloudflare: ["sandboxId"],
190
+ vercel: ["sandboxId"],
191
+ selfhosted: ["agentId"],
192
+ } as const satisfies Record<SandboxBackend, readonly string[]>;
193
+ export const LEGACY_SANDBOX_PROVIDER_INSTANCE_ID_FIELDS = [
194
+ "sandboxId",
195
+ "devboxId",
196
+ "sandboxName",
197
+ "sandboxIdentity",
198
+ "containerId",
199
+ "workspaceRootPath",
200
+ "agentId",
201
+ ] as const;
202
+
203
+ /**
204
+ * Durable proof that a provider may replace an execution wrapper without
205
+ * replacing the workspace OpenGeni owns. This is intentionally narrower than
206
+ * generic provider resume: only a provider adapter that can prove the same
207
+ * continuity key may consume it, and only a cold->warming owner or teardown
208
+ * claimant may authorize that replacement.
209
+ */
210
+ export type SandboxProviderContinuityRecovery = {
211
+ version: 1;
212
+ backend: SandboxBackend;
213
+ kind: "docker_workspace";
214
+ sourceInstanceId: string;
215
+ continuityKey: string;
216
+ };
217
+
150
218
  // OS axis. Only "linux" is reachable in v1; macos/windows are seam placeholders.
151
219
  export const SandboxOs = z.enum(["linux", "macos", "windows"]);
152
220
  export type SandboxOs = z.infer<typeof SandboxOs>;
@@ -178,7 +246,7 @@ export type CapabilityDescriptor = {
178
246
  FileSystem: { available: boolean; readOnly: boolean };
179
247
  Terminal: {
180
248
  available: boolean;
181
- transport: "sse-events" | "pty-ws" | null;
249
+ transport: "sse-events" | "pty-ws" | "relay-pty" | null;
182
250
  pty: boolean;
183
251
  };
184
252
  Git: { available: boolean };
@@ -217,6 +285,7 @@ export const DESKTOP_STREAM_PORT = 6080;
217
285
  // ttyd's default; the box bakes ttyd and launches it on this port. The pty-ws
218
286
  // Terminal cell's `url` is the tunnel address resolved against this port.
219
287
  export const TERMINAL_STREAM_PORT = 7681;
288
+ export const BROWSER_CONTROL_PORT = 7682;
220
289
 
221
290
  // The provider capability matrix (sandbox contract PART D + module 03-providers). One row per
222
291
  // backend (10 rows). v1 reachable cells are all Linux; macos/windows are seam
@@ -499,7 +568,7 @@ export const CAPABILITY_DESCRIPTORS: Record<SandboxBackend, CapabilityDescriptor
499
568
  os: { supported: ["linux", "macos", "windows"], default: "linux" },
500
569
  capabilities: {
501
570
  FileSystem: { available: true, readOnly: false },
502
- Terminal: { available: true, transport: "pty-ws", pty: true }, // real PTY over the relay
571
+ Terminal: { available: true, transport: "relay-pty", pty: true }, // real PTY over the relay
503
572
  Git: { available: true },
504
573
  DesktopStream: { available: true, transport: "vnc-ws" }, // proclaimed; consent-gated at enroll
505
574
  Recording: { available: true }, // boot invariant: == DesktopStream.available
@@ -557,6 +626,7 @@ export const ErrorEnvelope = z.object({
557
626
  code: ErrorCode,
558
627
  message: z.string(),
559
628
  retryable: z.boolean(),
629
+ outcomeUnknown: z.boolean().optional(),
560
630
  requestId: z.string().optional(),
561
631
  details: z.record(z.string(), z.unknown()).optional(),
562
632
  }),
@@ -664,7 +734,7 @@ export const Permission = z.enum([
664
734
  // Programmatic sandbox -> tool access through the first-party MCP gate. This is
665
735
  // intentionally narrow and is never part of first-party MCP defaults; callers
666
736
  // must receive it through an explicit delegated `ogd_` mint carrying sessionId.
667
- "toolspace:call",
737
+ "codemode:call",
668
738
  "goals:manage",
669
739
  // Bring-your-own-compute (M5). enrollments:read lists a workspace's machines;
670
740
  // enrollments:manage approves a device-flow enrollment (the LOUD whole-machine
@@ -697,6 +767,7 @@ export type Permission = z.infer<typeof Permission>;
697
767
  */
698
768
  export const DEFAULT_FIRST_PARTY_MCP_PERMISSIONS = [
699
769
  "workspace:read",
770
+ "files:upload",
700
771
  "files:read",
701
772
  "documents:search",
702
773
  "scheduled_tasks:manage",
@@ -729,6 +800,7 @@ export const FIRST_PARTY_MCP_TOOL_NAMES = [
729
800
  "set_session_title",
730
801
  "goal_set",
731
802
  "goal_update",
803
+ "goal_progress",
732
804
  "goal_complete",
733
805
  "goal_pause",
734
806
  "memory_search",
@@ -736,6 +808,9 @@ export const FIRST_PARTY_MCP_TOOL_NAMES = [
736
808
  "memory_correct",
737
809
  "preference_registry_summary",
738
810
  "preference_registry_get",
811
+ "task_notes_list",
812
+ "task_note_save",
813
+ "task_note_archive",
739
814
  "sandboxes_list",
740
815
  "sandbox_attach",
741
816
  "sandbox_swap",
@@ -756,11 +831,31 @@ export const FIRST_PARTY_MCP_TOOL_NAMES = [
756
831
  "session_resume",
757
832
  "session_steer",
758
833
  "set_other_session_title",
834
+ "interaction_discover",
835
+ "browser_open",
836
+ "browser_tabs",
837
+ "browser_observe",
838
+ "browser_act",
839
+ "browser_clipboard",
840
+ "browser_debug",
841
+ "browser_auth",
842
+ "interaction_request_human",
843
+ "browser_identity",
844
+ "browser_publish",
845
+ "browser_lifecycle",
846
+ "computer_open",
847
+ "computer_targets",
848
+ "computer_observe",
849
+ "computer_clipboard",
850
+ "computer_act",
851
+ "computer_lifecycle",
759
852
  "variable_set_list",
760
853
  "environment_list",
761
854
  "variable_set_get_variable",
762
855
  "variable_set_set_variable",
763
856
  "environment_set_variable",
857
+ "capability_catalog_search",
858
+ "capability_authorization_request",
764
859
  "github_connect_link",
765
860
  "github_repositories_list",
766
861
  "social_connections_list",
@@ -771,6 +866,18 @@ export const FIRST_PARTY_MCP_TOOL_NAMES = [
771
866
  "social_thread_fetch",
772
867
  "social_posts_sync",
773
868
  "social_post_reply",
869
+ "x_accounts_list",
870
+ "x_search_live",
871
+ "x_mentions_live",
872
+ "x_thread_fetch",
873
+ "x_posts_sync",
874
+ "x_post_reply",
875
+ "reddit_accounts_list",
876
+ "reddit_search_live",
877
+ "reddit_mentions_live",
878
+ "reddit_thread_fetch",
879
+ "reddit_posts_sync",
880
+ "reddit_post_reply",
774
881
  "scheduled_tasks_list",
775
882
  "scheduled_tasks_get",
776
883
  "scheduled_tasks_create",
@@ -789,22 +896,114 @@ export const FIRST_PARTY_MCP_TOOL_NAMES = [
789
896
  "slack_bot_file_content",
790
897
  "slack_bot_post_message",
791
898
  "slack_bot_delete_message",
899
+ "fiken_companies_list",
900
+ "fiken_contacts_list",
901
+ "fiken_contact_create",
902
+ "fiken_products_list",
903
+ "fiken_invoices_list",
904
+ "fiken_invoice_get",
905
+ "fiken_invoice_draft_create",
906
+ "fiken_bank_accounts_list",
907
+ "fiken_purchases_list",
908
+ "fiken_sales_list",
909
+ "atlassian_sources_list",
910
+ "atlassian_search",
911
+ "atlassian_get",
792
912
  "artifacts_list",
793
913
  "artifacts_get_source",
794
914
  "artifacts_create",
795
915
  "artifacts_publish",
796
916
  "artifacts_rollback",
917
+ "editable_artifact_list",
918
+ "editable_artifact_create",
919
+ "editable_artifact_import",
920
+ "editable_artifact_get",
921
+ "editable_artifact_inspect",
922
+ "editable_artifact_apply",
923
+ "editable_artifact_export",
924
+ "editable_artifact_export_status",
797
925
  ] as const;
798
926
  export const FirstPartyMcpToolName = z.enum(FIRST_PARTY_MCP_TOOL_NAMES);
799
927
  export type FirstPartyMcpToolName = z.infer<typeof FirstPartyMcpToolName>;
800
928
 
929
+ /**
930
+ * First-party interaction tools executed inside the frozen attempt rather than
931
+ * registered on the remote `opengeni` MCP server. They still belong to the
932
+ * same user-selectable first-party catalog and use the same attempt executor.
933
+ */
934
+ export const FIRST_PARTY_IN_PROCESS_TOOL_NAMES = [
935
+ "interaction_discover",
936
+ "browser_open",
937
+ "browser_tabs",
938
+ "browser_observe",
939
+ "browser_act",
940
+ "browser_clipboard",
941
+ "browser_debug",
942
+ "browser_auth",
943
+ "interaction_request_human",
944
+ "browser_identity",
945
+ "browser_publish",
946
+ "browser_lifecycle",
947
+ "computer_open",
948
+ "computer_targets",
949
+ "computer_observe",
950
+ "computer_clipboard",
951
+ "computer_act",
952
+ "computer_lifecycle",
953
+ ] as const satisfies readonly FirstPartyMcpToolName[];
954
+
955
+ const FIRST_PARTY_IN_PROCESS_TOOL_NAME_SET = new Set<FirstPartyMcpToolName>(
956
+ FIRST_PARTY_IN_PROCESS_TOOL_NAMES,
957
+ );
958
+
959
+ /**
960
+ * Names accepted for stored-selection compatibility but intentionally omitted
961
+ * from the remote first-party `opengeni` MCP server.
962
+ *
963
+ * `slack_bot_post_message` cannot accept a model/caller-supplied UUID as a
964
+ * trustworthy logical-delivery identity. Server-owned Slack delivery paths
965
+ * call the internal client directly with their own durable operation IDs.
966
+ */
967
+ const FIRST_PARTY_COMPATIBILITY_ONLY_TOOL_NAMES = [
968
+ "slack_bot_post_message",
969
+ ] as const satisfies readonly FirstPartyMcpToolName[];
970
+
971
+ const FIRST_PARTY_COMPATIBILITY_ONLY_TOOL_NAME_SET = new Set<FirstPartyMcpToolName>(
972
+ FIRST_PARTY_COMPATIBILITY_ONLY_TOOL_NAMES,
973
+ );
974
+
975
+ /** Exact catalog registered by the remote first-party `opengeni` MCP server. */
976
+ export const FIRST_PARTY_REMOTE_MCP_TOOL_NAMES = FIRST_PARTY_MCP_TOOL_NAMES.filter(
977
+ (name) =>
978
+ !FIRST_PARTY_IN_PROCESS_TOOL_NAME_SET.has(name) &&
979
+ !FIRST_PARTY_COMPATIBILITY_ONLY_TOOL_NAME_SET.has(name),
980
+ ) satisfies readonly FirstPartyMcpToolName[];
981
+
982
+ /** Authored CodeMode paths for the canonical collaborative artifact surface. */
983
+ export const EDITABLE_ARTIFACT_MCP_CODEMODE_PATHS = {
984
+ editable_artifact_list: ["artifacts", "list"],
985
+ editable_artifact_create: ["artifacts", "create"],
986
+ editable_artifact_import: ["artifacts", "import"],
987
+ editable_artifact_get: ["artifacts", "get"],
988
+ editable_artifact_inspect: ["artifacts", "inspect"],
989
+ editable_artifact_apply: ["artifacts", "apply"],
990
+ editable_artifact_export: ["artifacts", "export"],
991
+ editable_artifact_export_status: ["artifacts", "exportStatus"],
992
+ } as const satisfies Partial<Record<FirstPartyMcpToolName, readonly [string, string]>>;
993
+
801
994
  /**
802
995
  * Connector-wide tools are explicit-only. Ordinary session omission selects
803
996
  * the non-connector catalog, while an explicit session policy may still select
804
997
  * any catalogued connector tool and remains independently permission-gated.
805
998
  */
806
999
  export const DEFAULT_FIRST_PARTY_MCP_TOOLS = FIRST_PARTY_MCP_TOOL_NAMES.filter(
807
- (name) => !name.startsWith("social_") && !name.startsWith("slack_bot_"),
1000
+ (name) =>
1001
+ !name.startsWith("social_") &&
1002
+ !name.startsWith("x_") &&
1003
+ !name.startsWith("reddit_") &&
1004
+ !name.startsWith("slack_bot_") &&
1005
+ !name.startsWith("fiken_") &&
1006
+ !name.startsWith("atlassian_"),
808
1007
  ) satisfies readonly FirstPartyMcpToolName[];
809
1008
 
810
1009
  export function prefixedMcpToolName(registryId: string, toolName: string): string {
@@ -826,6 +1025,255 @@ export type UsageLimitsMode = z.infer<typeof UsageLimitsMode>;
826
1025
  export const AccountRole = z.enum(["owner", "admin", "member"]);
827
1026
  export type AccountRole = z.infer<typeof AccountRole>;
828
1027
 
1028
+ /**
1029
+ * Settled tenancy vocabulary for resources that may outlive or be used from
1030
+ * more than one workspace. This names authority only; parsing one of these
1031
+ * contracts never authorizes access by itself.
1032
+ */
1033
+ export const ResourceAuthorityScope = z.enum(["organization", "workspace", "user"]);
1034
+ export type ResourceAuthorityScope = z.infer<typeof ResourceAuthorityScope>;
1035
+
1036
+ export const ResourceAuthorityListScope = z.enum([
1037
+ "effective",
1038
+ "organization",
1039
+ "workspace",
1040
+ "user",
1041
+ ]);
1042
+ export type ResourceAuthorityListScope = z.infer<typeof ResourceAuthorityListScope>;
1043
+
1044
+ export const OrganizationMembershipStatus = z.enum([
1045
+ "provisioning",
1046
+ "active",
1047
+ "suspended",
1048
+ "revoked",
1049
+ ]);
1050
+ export type OrganizationMembershipStatus = z.infer<typeof OrganizationMembershipStatus>;
1051
+
1052
+ export const PersonalResourceRetentionMode = z.enum(["retain", "delete_after"]);
1053
+ export type PersonalResourceRetentionMode = z.infer<typeof PersonalResourceRetentionMode>;
1054
+
1055
+ export const SessionTenancyVisibility = z.enum(["user_private", "workspace_shared"]);
1056
+ export type SessionTenancyVisibility = z.infer<typeof SessionTenancyVisibility>;
1057
+
1058
+ /** Public/session API vocabulary. Persistence keeps the explicit tenancy names. */
1059
+ export const SessionVisibility = z.enum(["private", "workspace"]);
1060
+ export type SessionVisibility = z.infer<typeof SessionVisibility>;
1061
+
1062
+ export function sessionVisibilityToPublic(value: SessionTenancyVisibility): SessionVisibility {
1063
+ return value === "user_private" ? "private" : "workspace";
1064
+ }
1065
+
1066
+ export function sessionVisibilityFromPublic(value: SessionVisibility): SessionTenancyVisibility {
1067
+ return value === "private" ? "user_private" : "workspace_shared";
1068
+ }
1069
+
1070
+ export const UserResourceGrantMode = z.enum(["once", "session", "always"]);
1071
+ export type UserResourceGrantMode = z.infer<typeof UserResourceGrantMode>;
1072
+
1073
+ /** Canonical non-wildcard action named by a personal-resource grant. */
1074
+ export const UserResourceGrantAction = z
1075
+ .string()
1076
+ .min(1)
1077
+ .max(64)
1078
+ .regex(/^[a-z0-9](?:[a-z0-9._:-]*[a-z0-9])?$/u);
1079
+ export type UserResourceGrantAction = z.infer<typeof UserResourceGrantAction>;
1080
+
1081
+ export const UserResourceGrantStatus = z.enum(["active", "consumed", "revoked", "expired"]);
1082
+ export type UserResourceGrantStatus = z.infer<typeof UserResourceGrantStatus>;
1083
+
1084
+ function refineUserResourceGrantFence(
1085
+ value: {
1086
+ mode: UserResourceGrantMode;
1087
+ sessionId: string | null;
1088
+ authorityEpoch: number | null;
1089
+ },
1090
+ context: z.RefinementCtx,
1091
+ ): void {
1092
+ const sessionBoundMode = value.mode === "once" || value.mode === "session";
1093
+ const hasSession = value.sessionId !== null;
1094
+ const hasAuthorityEpoch = value.authorityEpoch !== null;
1095
+
1096
+ if (sessionBoundMode && !hasSession) {
1097
+ context.addIssue({
1098
+ code: "custom",
1099
+ path: ["sessionId"],
1100
+ message: "once and session grants require an exact sessionId",
1101
+ });
1102
+ }
1103
+ if (sessionBoundMode && !hasAuthorityEpoch) {
1104
+ context.addIssue({
1105
+ code: "custom",
1106
+ path: ["authorityEpoch"],
1107
+ message: "once and session grants require an authorityEpoch fence",
1108
+ });
1109
+ }
1110
+ if (!sessionBoundMode && (hasSession || hasAuthorityEpoch)) {
1111
+ context.addIssue({
1112
+ code: "custom",
1113
+ path: ["mode"],
1114
+ message: "always grants must not carry a session or authority-epoch fence",
1115
+ });
1116
+ }
1117
+ if (hasSession !== hasAuthorityEpoch) {
1118
+ context.addIssue({
1119
+ code: "custom",
1120
+ path: ["authorityEpoch"],
1121
+ message: "sessionId and authorityEpoch must be present or absent together",
1122
+ });
1123
+ }
1124
+ }
1125
+
1126
+ /** Self-only organization membership projection; no subject identifier leaks. */
1127
+ export const OrganizationMembershipProjection = z.object({
1128
+ id: z.string().uuid(),
1129
+ organizationId: z.string().uuid(),
1130
+ status: OrganizationMembershipStatus,
1131
+ personalWorkspaceId: z.string().uuid().nullable(),
1132
+ personalRetentionUntil: z.string().datetime({ offset: true }).nullable(),
1133
+ });
1134
+ export type OrganizationMembershipProjection = z.infer<typeof OrganizationMembershipProjection>;
1135
+
1136
+ /**
1137
+ * Exact active-membership facts returned by managed-human login provisioning.
1138
+ * Retention is intentionally absent because the narrow provisioning capability
1139
+ * neither reads nor mutates offboarding policy.
1140
+ */
1141
+ export const ManagedOrganizationMembershipProjection = z.object({
1142
+ id: z.string().uuid(),
1143
+ organizationId: z.string().uuid(),
1144
+ status: z.literal("active"),
1145
+ personalWorkspaceId: z.string().uuid(),
1146
+ });
1147
+ export type ManagedOrganizationMembershipProjection = z.infer<
1148
+ typeof ManagedOrganizationMembershipProjection
1149
+ >;
1150
+
1151
+ export const ListManagedOrganizationMembershipsResponse = z.object({
1152
+ memberships: z.array(ManagedOrganizationMembershipProjection),
1153
+ });
1154
+ export type ListManagedOrganizationMembershipsResponse = z.infer<
1155
+ typeof ListManagedOrganizationMembershipsResponse
1156
+ >;
1157
+
1158
+ /**
1159
+ * Opaque user-resource authority projection. Ownership is represented by the
1160
+ * server-issued authority id, never a raw subject or membership id.
1161
+ */
1162
+ export const UserResourceAuthorityProjection = z.object({
1163
+ id: z.string().uuid(),
1164
+ organizationId: z.string().uuid(),
1165
+ scope: z.literal("user"),
1166
+ resourceKind: z.string().min(1).max(64),
1167
+ originWorkspaceId: z.string().uuid().nullable(),
1168
+ generation: z.number().int().positive(),
1169
+ status: z.enum(["active", "retained", "revoked"]),
1170
+ });
1171
+ export type UserResourceAuthorityProjection = z.infer<typeof UserResourceAuthorityProjection>;
1172
+
1173
+ /**
1174
+ * Opaque grant projection. A grant is still inert until a server-side access
1175
+ * boundary proves the organization, owner, workspace, session context, and
1176
+ * current authority epoch.
1177
+ */
1178
+ export const UserResourceGrantProjection = z
1179
+ .object({
1180
+ id: z.string().uuid(),
1181
+ authorityId: z.string().uuid(),
1182
+ organizationId: z.string().uuid(),
1183
+ workspaceId: z.string().uuid(),
1184
+ sessionId: z.string().uuid().nullable(),
1185
+ action: UserResourceGrantAction,
1186
+ mode: UserResourceGrantMode,
1187
+ context: SessionTenancyVisibility,
1188
+ authorityEpoch: z.number().int().positive().nullable(),
1189
+ generation: z.number().int().positive(),
1190
+ status: UserResourceGrantStatus,
1191
+ expiresAt: z.string().datetime({ offset: true }).nullable(),
1192
+ })
1193
+ .superRefine(refineUserResourceGrantFence);
1194
+ export type UserResourceGrantProjection = z.infer<typeof UserResourceGrantProjection>;
1195
+
1196
+ /**
1197
+ * Extensible immutable delegation payload for accepted work. It contains only
1198
+ * opaque authority/grant identity and execution fences, never owner identity
1199
+ * or credential material.
1200
+ */
1201
+ export const UserResourceDelegation = z
1202
+ .object({
1203
+ authorityId: z.string().uuid(),
1204
+ grantId: z.string().uuid(),
1205
+ organizationId: z.string().uuid(),
1206
+ workspaceId: z.string().uuid(),
1207
+ sessionId: z.string().uuid().nullable(),
1208
+ action: UserResourceGrantAction,
1209
+ mode: UserResourceGrantMode,
1210
+ context: SessionTenancyVisibility,
1211
+ authorityEpoch: z.number().int().positive().nullable(),
1212
+ authorityGeneration: z.number().int().positive(),
1213
+ grantGeneration: z.number().int().positive(),
1214
+ resourceVersionId: z.string().uuid().nullable().optional(),
1215
+ })
1216
+ .superRefine(refineUserResourceGrantFence);
1217
+ export type UserResourceDelegation = z.infer<typeof UserResourceDelegation>;
1218
+
1219
+ /**
1220
+ * Compatibility envelope for future resource selectors. Omitted scope and
1221
+ * authority parse to workspace-only behavior; user authority is impossible
1222
+ * without one complete opaque delegation.
1223
+ */
1224
+ export const ResourceAuthorityEnvelope = z
1225
+ .object({
1226
+ scope: ResourceAuthorityScope.default("workspace"),
1227
+ userDelegation: UserResourceDelegation.optional(),
1228
+ })
1229
+ .superRefine((value, context) => {
1230
+ if (value.scope === "user" && value.userDelegation === undefined) {
1231
+ context.addIssue({
1232
+ code: "custom",
1233
+ path: ["userDelegation"],
1234
+ message: "user scope requires an explicit immutable delegation",
1235
+ });
1236
+ }
1237
+ if (value.scope !== "user" && value.userDelegation !== undefined) {
1238
+ context.addIssue({
1239
+ code: "custom",
1240
+ path: ["userDelegation"],
1241
+ message: "only user scope may carry a user delegation",
1242
+ });
1243
+ }
1244
+ });
1245
+ export type ResourceAuthorityEnvelope = z.infer<typeof ResourceAuthorityEnvelope>;
1246
+
1247
+ /** Secret-safe generic session tenancy metadata for an authorized viewer. */
1248
+ export const SessionTenancyProjection = z.object({
1249
+ visibility: SessionTenancyVisibility,
1250
+ authorityEpoch: z.number().int().positive(),
1251
+ ownedByCurrentUser: z.boolean(),
1252
+ fork: z
1253
+ .object({
1254
+ sourceVisibility: SessionTenancyVisibility,
1255
+ sourceAuthorityEpoch: z.number().int().positive(),
1256
+ forkedAt: z.string().datetime({ offset: true }),
1257
+ })
1258
+ .nullable(),
1259
+ });
1260
+ export type SessionTenancyProjection = z.infer<typeof SessionTenancyProjection>;
1261
+
1262
+ /** Secret-safe public projection used by activated session APIs. */
1263
+ export const SessionTenancyPublicProjection = z.object({
1264
+ visibility: SessionVisibility,
1265
+ authorityEpoch: z.number().int().positive(),
1266
+ ownedByCurrentUser: z.boolean(),
1267
+ fork: z
1268
+ .object({
1269
+ sourceVisibility: SessionVisibility,
1270
+ sourceAuthorityEpoch: z.number().int().positive(),
1271
+ forkedAt: z.string().datetime({ offset: true }),
1272
+ })
1273
+ .nullable(),
1274
+ });
1275
+ export type SessionTenancyPublicProjection = z.infer<typeof SessionTenancyPublicProjection>;
1276
+
829
1277
  export const ManagedAccount = z.object({
830
1278
  id: z.string().uuid(),
831
1279
  name: z.string(),
@@ -1220,12 +1668,7 @@ export * from "./transcription-recordings";
1220
1668
  export const CodexCompactionMode = z.enum(["remote_v2", "portable"]);
1221
1669
  export type CodexCompactionMode = z.infer<typeof CodexCompactionMode>;
1222
1670
 
1223
- export const SlackReactionEmojiName = z
1224
- .string()
1225
- .trim()
1226
- .min(1)
1227
- .max(64)
1228
- .regex(/^[a-z0-9_+-]+$/, "use the exact Slack emoji name without surrounding colons");
1671
+ export const SlackReactionEmojiName = z.literal("genie");
1229
1672
  export type SlackReactionEmojiName = z.infer<typeof SlackReactionEmojiName>;
1230
1673
 
1231
1674
  export const WorkspaceSlackReactionChannelPolicy = z.discriminatedUnion("mode", [
@@ -1271,12 +1714,17 @@ export const DEFAULT_WORKSPACE_SLACK_REACTION_SUMMON_SETTINGS = {
1271
1714
  channelPolicy: { mode: "bot_member" },
1272
1715
  } as const satisfies WorkspaceSlackReactionSummonSettings;
1273
1716
 
1717
+ export const WorkspaceMemoryPromptMode = z.enum(["legacy_standing", "retrieval_only"]);
1718
+ export type WorkspaceMemoryPromptMode = z.infer<typeof WorkspaceMemoryPromptMode>;
1719
+
1274
1720
  // Validates the KNOWN keys of workspaces.settings; passthrough keeps unknown
1275
- // (future) keys rather than stripping them. memoryEnabled defaults off;
1721
+ // (future) keys rather than stripping them. memoryEnabled defaults off and the
1722
+ // reversible Memory V1 containment rollout defaults to legacy standing context;
1276
1723
  // voiceInput defaults to enabled when the deployment has a provider.
1277
1724
  export const WorkspaceSettingsSchema = z
1278
1725
  .object({
1279
1726
  memoryEnabled: z.boolean().optional(),
1727
+ memoryPromptMode: WorkspaceMemoryPromptMode.optional(),
1280
1728
  /** Preferred workspace voice-input toggle. */
1281
1729
  voiceInput: WorkspaceVoiceInputSettings.optional(),
1282
1730
  /**
@@ -1306,6 +1754,12 @@ export function resolveWorkspaceMemoryEnabled(settings: unknown): boolean {
1306
1754
  return parsed.success ? parsed.data.memoryEnabled === true : false;
1307
1755
  }
1308
1756
 
1757
+ /** Reversible Memory V1 prompt rollout. Absent preserves legacy composition. */
1758
+ export function resolveWorkspaceMemoryPromptMode(settings: unknown): WorkspaceMemoryPromptMode {
1759
+ const parsed = WorkspaceSettingsSchema.safeParse(settings ?? {});
1760
+ return parsed.success ? (parsed.data.memoryPromptMode ?? "legacy_standing") : "legacy_standing";
1761
+ }
1762
+
1309
1763
  /** Default Codex compaction mode for new Codex sessions (remote_v2 when unset). */
1310
1764
  export function resolveWorkspaceCodexCompactionDefault(settings: unknown): CodexCompactionMode {
1311
1765
  const parsed = WorkspaceSettingsSchema.safeParse(settings ?? {});
@@ -1376,6 +1830,7 @@ export function workspaceSlackReactionChannelAllowed(
1376
1830
  export const UpdateWorkspaceSettingsRequest = z
1377
1831
  .object({
1378
1832
  memoryEnabled: z.boolean().optional(),
1833
+ memoryPromptMode: WorkspaceMemoryPromptMode.optional(),
1379
1834
  voiceInput: WorkspaceVoiceInputSettings.optional(),
1380
1835
  /** @deprecated Prefer `voiceInput`. Kept for one compatibility release. */
1381
1836
  transcription: WorkspaceTranscriptionPolicy.optional(),
@@ -1443,6 +1898,8 @@ const reservedServiceTurnInitiatorContextKeys = new Set([
1443
1898
  "backfill",
1444
1899
  "label",
1445
1900
  "provenanceError",
1901
+ "opengeniSiteAuthConnectionId",
1902
+ "opengeniSiteAuthMaintenanceOperationId",
1446
1903
  "via",
1447
1904
  "viaTruncated",
1448
1905
  ]);
@@ -1544,6 +2001,11 @@ export const DelegatedAccessTokenPayload = z
1544
2001
  // Model-visible first-party tool selection for a worker-bound session.
1545
2002
  // This is visibility only; permissions remain the authorization boundary.
1546
2003
  firstPartyMcpTools: z.array(FirstPartyMcpToolName).optional(),
2004
+ // Trusted root-relative depth facts frozen when the worker prepares this
2005
+ // attempt. They shape only the model-visible tool catalog; the database
2006
+ // remains authoritative for admission and concurrent policy changes.
2007
+ nestedAgentDepth: NestedAgentDepthValue.optional(),
2008
+ effectiveMaxNestedAgentDepth: NestedAgentDepthValue.optional(),
1547
2009
  // The turn making the call (the caller's identity), HMAC-signed by the worker
1548
2010
  // at turn setup. Lets a tool classify WHO is calling from the token itself,
1549
2011
  // instead of racily re-reading the session's live active_turn_id — e.g. the
@@ -1564,6 +2026,23 @@ export const DelegatedAccessTokenPayload = z
1564
2026
  payload.executionGeneration,
1565
2027
  ];
1566
2028
  const exactAttemptClaimCount = exactAttemptClaims.filter((value) => value !== undefined).length;
2029
+ const depthClaimCount = [payload.nestedAgentDepth, payload.effectiveMaxNestedAgentDepth].filter(
2030
+ (value) => value !== undefined,
2031
+ ).length;
2032
+ if (depthClaimCount !== 0 && depthClaimCount !== 2) {
2033
+ ctx.addIssue({
2034
+ code: z.ZodIssueCode.custom,
2035
+ path: ["nestedAgentDepth"],
2036
+ message: "nested-agent depth claims must be supplied together",
2037
+ });
2038
+ }
2039
+ if (depthClaimCount > 0 && payload.principalKind !== "agent_attempt") {
2040
+ ctx.addIssue({
2041
+ code: z.ZodIssueCode.custom,
2042
+ path: ["principalKind"],
2043
+ message: "nested-agent depth claims require an agent_attempt principal",
2044
+ });
2045
+ }
1567
2046
  if (
1568
2047
  payload.principalKind === "human_session" &&
1569
2048
  (exactAttemptClaimCount !== 0 || payload.serviceInitiator !== undefined)
@@ -2086,6 +2565,61 @@ export const UpdateWorkspaceMemberRequest = z.object({
2086
2565
  });
2087
2566
  export type UpdateWorkspaceMemberRequest = z.infer<typeof UpdateWorkspaceMemberRequest>;
2088
2567
 
2568
+ export const SlackUserLinkAccessRequestStatus = z.enum([
2569
+ "prepared",
2570
+ "pending",
2571
+ "completed",
2572
+ "denied",
2573
+ "cancelled",
2574
+ "expired",
2575
+ ]);
2576
+ export type SlackUserLinkAccessRequestStatus = z.infer<typeof SlackUserLinkAccessRequestStatus>;
2577
+
2578
+ /**
2579
+ * Durable, token-free projection of one signed Slack identity-link intent.
2580
+ * The original bearer and its digest are deliberately absent from every
2581
+ * public response.
2582
+ */
2583
+ export const SlackUserLinkAccessRequest = z.object({
2584
+ id: z.string().uuid(),
2585
+ workspaceId: z.string().uuid(),
2586
+ workspaceDisplayName: z.string().min(1).max(256).nullable(),
2587
+ subjectLabel: z.string().min(1).max(512).nullable(),
2588
+ status: SlackUserLinkAccessRequestStatus,
2589
+ version: z.number().int().positive(),
2590
+ expiresAt: z.string().datetime({ offset: true }),
2591
+ requestedAt: z.string().datetime({ offset: true }).nullable(),
2592
+ decidedAt: z.string().datetime({ offset: true }).nullable(),
2593
+ completedAt: z.string().datetime({ offset: true }).nullable(),
2594
+ createdAt: z.string().datetime({ offset: true }),
2595
+ updatedAt: z.string().datetime({ offset: true }),
2596
+ });
2597
+ export type SlackUserLinkAccessRequest = z.infer<typeof SlackUserLinkAccessRequest>;
2598
+
2599
+ export const PrepareSlackUserLinkAccessRequest = z.object({
2600
+ linkToken: z.string().min(1).max(2_048),
2601
+ });
2602
+ export type PrepareSlackUserLinkAccessRequest = z.infer<typeof PrepareSlackUserLinkAccessRequest>;
2603
+
2604
+ export const SlackUserLinkAccessMutationRequest = z.object({
2605
+ expectedVersion: z.number().int().positive(),
2606
+ idempotencyKey: z.string().trim().min(1).max(200),
2607
+ });
2608
+ export type SlackUserLinkAccessMutationRequest = z.infer<typeof SlackUserLinkAccessMutationRequest>;
2609
+
2610
+ export const ApproveSlackUserLinkAccessRequest = SlackUserLinkAccessMutationRequest.extend({
2611
+ role: z.string().trim().min(1).max(128).optional(),
2612
+ permissions: z.array(Permission).min(1),
2613
+ });
2614
+ export type ApproveSlackUserLinkAccessRequest = z.infer<typeof ApproveSlackUserLinkAccessRequest>;
2615
+
2616
+ export const ListSlackUserLinkAccessRequestsResponse = z.object({
2617
+ requests: z.array(SlackUserLinkAccessRequest),
2618
+ });
2619
+ export type ListSlackUserLinkAccessRequestsResponse = z.infer<
2620
+ typeof ListSlackUserLinkAccessRequestsResponse
2621
+ >;
2622
+
2089
2623
  export const UsageEventType = z.enum([
2090
2624
  "agent_run.created",
2091
2625
  "agent_run.completed",
@@ -2095,6 +2629,10 @@ export const UsageEventType = z.enum([
2095
2629
  "file.deleted",
2096
2630
  "document.indexed",
2097
2631
  "scheduled_task.fired",
2632
+ "knowledge_source_sync.fired",
2633
+ "knowledge_source_sync.completed",
2634
+ "knowledge_source_sync.items",
2635
+ "knowledge_source_sync.bytes",
2098
2636
  "api_key.request",
2099
2637
  // --- sandbox warm-time metering (P2.1) ---
2100
2638
  // Wall-clock seconds a box was warm — the billable warm-time meter. Accrued on
@@ -2133,6 +2671,9 @@ export type InsightsRange = z.infer<typeof InsightsRange>;
2133
2671
  export const InsightsBillingPath = z.enum(["opengeni_credits", "external"]);
2134
2672
  export type InsightsBillingPath = z.infer<typeof InsightsBillingPath>;
2135
2673
 
2674
+ export const InsightsPricingSource = z.enum(["configured_list_price", "gateway_reported"]);
2675
+ export type InsightsPricingSource = z.infer<typeof InsightsPricingSource>;
2676
+
2136
2677
  export const InsightsModelUsageRow = z.object({
2137
2678
  id: z.string().min(1),
2138
2679
  model: z.string().min(1),
@@ -2142,20 +2683,37 @@ export const InsightsModelUsageRow = z.object({
2142
2683
  inputTokens: z.number().nonnegative(),
2143
2684
  outputTokens: z.number().nonnegative(),
2144
2685
  cachedTokens: z.number().nonnegative(),
2686
+ cacheInputTokens: z.number().nonnegative(),
2145
2687
  cacheWriteTokens: z.number().nonnegative(),
2146
2688
  reasoningTokens: z.number().nonnegative(),
2689
+ totalTokens: z.number().nonnegative(),
2690
+ tokenKnownCalls: z.number().int().nonnegative(),
2691
+ cacheKnownCalls: z.number().int().nonnegative(),
2147
2692
  /** Priced OpenGeni credit $ for this model×provider (from model_call_facts). */
2148
2693
  creditUsd: z.number().nonnegative(),
2694
+ /** Hypothetical provider-rate USD; never an OpenGeni charge. */
2695
+ estimatedProviderUsd: z.number().nonnegative(),
2696
+ estimatedProviderCostKnownCalls: z.number().int().nonnegative(),
2149
2697
  });
2150
2698
  export type InsightsModelUsageRow = z.infer<typeof InsightsModelUsageRow>;
2151
2699
 
2152
2700
  export const InsightsSeriesPoint = z.object({
2153
2701
  label: z.string().min(1),
2154
- /** Day-bucketed sum of usage_events.model.cost (workspace-wide) or filtered facts when provider/model set. */
2702
+ /** UTC hour/day-bucketed sum of usage_events.model.cost (workspace-wide) or filtered facts when provider/model set. */
2155
2703
  modelCostUsd: z.number().nonnegative(),
2704
+ /** UTC hour/day-bucketed hypothetical provider-rate USD for calls with captured pricing. */
2705
+ estimatedProviderUsd: z.number().nonnegative(),
2706
+ estimatedProviderCostKnownCalls: z.number().int().nonnegative(),
2156
2707
  warmSeconds: z.number().nonnegative(),
2157
2708
  inputTokens: z.number().nonnegative(),
2709
+ outputTokens: z.number().nonnegative(),
2158
2710
  cachedTokens: z.number().nonnegative(),
2711
+ cacheInputTokens: z.number().nonnegative(),
2712
+ cacheWriteTokens: z.number().nonnegative(),
2713
+ reasoningTokens: z.number().nonnegative(),
2714
+ totalTokens: z.number().nonnegative(),
2715
+ tokenKnownCalls: z.number().int().nonnegative(),
2716
+ cacheKnownCalls: z.number().int().nonnegative(),
2159
2717
  cacheHitPct: z.number().int().min(0).max(100),
2160
2718
  calls: z.number().int().nonnegative(),
2161
2719
  });
@@ -2178,9 +2736,12 @@ export const InsightsSpendDriver = z.object({
2178
2736
  groupBy: z.enum(["root_session", "schedule"]),
2179
2737
  label: z.string().min(1),
2180
2738
  creditUsd: z.number().nonnegative(),
2739
+ estimatedProviderUsd: z.number().nonnegative(),
2740
+ estimatedProviderCostKnownCalls: z.number().int().nonnegative(),
2181
2741
  tokens: z.number().nonnegative(),
2182
2742
  cacheHitPct: z.number().int().min(0).max(100),
2183
2743
  pctOfCreditUsd: z.number().int().min(0).max(100),
2744
+ pctOfTokens: z.number().int().min(0).max(100),
2184
2745
  deltaUsdVsPrior: z.number(),
2185
2746
  });
2186
2747
  export type InsightsSpendDriver = z.infer<typeof InsightsSpendDriver>;
@@ -2227,18 +2788,48 @@ export const InsightsScheduleRow = z.object({
2227
2788
  fires: z.number().int().nonnegative(),
2228
2789
  /** Null when no facts carry scheduled_task_id for this window. */
2229
2790
  creditUsd: z.number().nonnegative().nullable(),
2791
+ estimatedProviderUsd: z.number().nonnegative().nullable(),
2792
+ estimatedProviderCostKnownCalls: z.number().int().nonnegative().nullable(),
2230
2793
  tokens: z.number().nonnegative().nullable(),
2231
2794
  cacheHitPct: z.number().int().min(0).max(100).nullable(),
2232
2795
  billing: InsightsBillingPath.nullable(),
2233
2796
  });
2234
2797
  export type InsightsScheduleRow = z.infer<typeof InsightsScheduleRow>;
2235
2798
 
2799
+ export const InsightsModelCallRow = z.object({
2800
+ id: z.string().uuid(),
2801
+ occurredAt: z.string().datetime(),
2802
+ recordedAt: z.string().datetime(),
2803
+ sessionId: z.string().uuid(),
2804
+ sessionTitle: z.string(),
2805
+ turnId: z.string().uuid(),
2806
+ provider: z.string().min(1),
2807
+ providerApi: z.string().min(1),
2808
+ model: z.string().min(1),
2809
+ billing: InsightsBillingPath,
2810
+ inputTokens: z.number().nonnegative().nullable(),
2811
+ outputTokens: z.number().nonnegative().nullable(),
2812
+ cachedTokens: z.number().nonnegative().nullable(),
2813
+ cacheWriteTokens: z.number().nonnegative().nullable(),
2814
+ reasoningTokens: z.number().nonnegative().nullable(),
2815
+ totalTokens: z.number().nonnegative().nullable(),
2816
+ /** OpenGeni credit price for this call. External calls are always zero. */
2817
+ creditUsd: z.number().nonnegative(),
2818
+ /** Hypothetical provider-rate USD; null when historical pricing is unavailable. */
2819
+ estimatedProviderUsd: z.number().nonnegative().nullable(),
2820
+ pricingSource: InsightsPricingSource.nullable(),
2821
+ });
2822
+ export type InsightsModelCallRow = z.infer<typeof InsightsModelCallRow>;
2823
+
2236
2824
  export const WorkspaceInsightsSnapshot = z.object({
2237
2825
  range: InsightsRange,
2238
2826
  rangeLabel: z.string().min(1),
2239
2827
  priorLabel: z.string().min(1),
2240
2828
  seriesLabel: z.string().min(1),
2241
2829
  cacheSeriesLabel: z.string().min(1),
2830
+ windowStart: z.string().datetime(),
2831
+ windowEnd: z.string().datetime(),
2832
+ generatedAt: z.string().datetime(),
2242
2833
  /** All ranges/series are UTC. */
2243
2834
  timezone: z.literal("UTC"),
2244
2835
  models: z.array(InsightsModelUsageRow),
@@ -2248,6 +2839,7 @@ export const WorkspaceInsightsSnapshot = z.object({
2248
2839
  depth: z.array(InsightsDepthBucket),
2249
2840
  drivers: z.array(InsightsSpendDriver),
2250
2841
  schedules: z.array(InsightsScheduleRow),
2842
+ recentCalls: z.array(InsightsModelCallRow),
2251
2843
  warmSeconds: z.number().nonnegative(),
2252
2844
  priorWarmSeconds: z.number().nonnegative(),
2253
2845
  warmGroups: z.array(InsightsWarmGroupRow),
@@ -2261,7 +2853,14 @@ export const WorkspaceInsightsSnapshot = z.object({
2261
2853
  /** Model-filterable credit $ from facts (equals workspace when unfiltered, ignoring late-reject drift). */
2262
2854
  creditUsd: z.number().nonnegative(),
2263
2855
  priorCreditUsd: z.number().nonnegative(),
2856
+ /** Hypothetical provider-rate USD for calls whose historical price was captured. */
2857
+ estimatedProviderUsd: z.number().nonnegative(),
2858
+ priorEstimatedProviderUsd: z.number().nonnegative(),
2859
+ estimatedProviderCostKnownCalls: z.number().int().nonnegative(),
2860
+ priorEstimatedProviderCostKnownCalls: z.number().int().nonnegative(),
2861
+ modelCalls: z.number().int().nonnegative(),
2264
2862
  priorInputTokens: z.number().nonnegative(),
2863
+ priorTotalTokens: z.number().nonnegative(),
2265
2864
  priorCacheHitPct: z.number().int().min(0).max(100),
2266
2865
  priorCalls: z.number().int().nonnegative(),
2267
2866
  /** Lifetime workspace topology (not scoped to the selected Insights range). */
@@ -2408,7 +3007,7 @@ export type GitCredentialRepositoryRef = z.infer<typeof GitCredentialRepositoryR
2408
3007
  // `loadVariableSetForRun` (today decrypted with
2409
3008
  // `environmentsEncryptionKeyBytes(settings)`).
2410
3009
  // - MCP credentials: request-time transport headers for connection-backed
2411
- // servers, shared by normal model tools and Toolspace/Code Mode.
3010
+ // servers, shared by normal model tools and Codemode/Code Mode.
2412
3011
  //
2413
3012
  // In embedded/separate topologies the HOST owns these external connections
2414
3013
  // (its GitHub App, its secret vault + encryption key). When a host binds this
@@ -2691,7 +3290,7 @@ export const McpPersonalConnectionDelegation = z
2691
3290
  ownerSubjectId: z.string().min(1).max(512),
2692
3291
  providerDomain: z.string().min(1).max(2048),
2693
3292
  kind: z.enum(["oauth2", "api_key", "app_install", "delegated"]).optional(),
2694
- connectionType: z.enum(["mcp", "social"]).optional(),
3293
+ connectionType: z.enum(["mcp", "social", "atlassian"]).optional(),
2695
3294
  })
2696
3295
  .strict();
2697
3296
  export type McpPersonalConnectionDelegation = z.infer<typeof McpPersonalConnectionDelegation>;
@@ -2727,7 +3326,7 @@ export type McpPersonalConnectionSummary = z.infer<typeof McpPersonalConnectionS
2727
3326
  export type McpCredentialsRequest = {
2728
3327
  accountId: string;
2729
3328
  workspaceId: string;
2730
- /** Immediate session whose model or Toolspace call needs the credential. */
3329
+ /** Immediate session whose model or Codemode call needs the credential. */
2731
3330
  sessionId: string;
2732
3331
  /** Workspace-scoped lineage root for host authorization and binding lookup. */
2733
3332
  rootSessionId: string;
@@ -2740,9 +3339,16 @@ export type McpCredentialsRequest = {
2740
3339
  initiatorContext: TurnInitiatorContext;
2741
3340
  /** Immediate technical caller, retained only as non-authoritative audit context. */
2742
3341
  callerSubjectId?: string;
2743
- surface: "model" | "toolspace";
3342
+ surface: "model" | "codemode";
2744
3343
  /** Canonical MCP destination that will receive the resolved headers. */
2745
3344
  destinationUrl: string;
3345
+ /**
3346
+ * Credential transport requested by the caller. Omitted means the existing
3347
+ * header-only MCP transport. The additive `http_api` target allows embedding
3348
+ * hosts to return query/cookie API-key placements for local API integrations
3349
+ * without making those placements eligible for a remote MCP request.
3350
+ */
3351
+ credentialTarget?: "mcp" | "http_api";
2746
3352
  serverId: string;
2747
3353
  toolName?: string;
2748
3354
  connectionRef: McpServerConnectionRef;
@@ -2755,6 +3361,13 @@ export type McpCredentialAuthNeededReason =
2755
3361
  | "unsupported_auth"
2756
3362
  | "resource_scope_unavailable";
2757
3363
 
3364
+ export type ConnectionCredentialPlacement = {
3365
+ carrier: "header" | "query" | "cookie";
3366
+ name: string;
3367
+ value: string;
3368
+ prefix?: string;
3369
+ };
3370
+
2758
3371
  export type McpCredentialResolution =
2759
3372
  | {
2760
3373
  status: "ok";
@@ -2763,6 +3376,12 @@ export type McpCredentialResolution =
2763
3376
  workspaceId: string;
2764
3377
  sessionId: string;
2765
3378
  headers: Record<string, string>;
3379
+ /**
3380
+ * Optional normalized HTTP credential placements. When present, header
3381
+ * placements must exactly match `headers`. Query/cookie placements are
3382
+ * accepted only for a request whose credentialTarget is `http_api`.
3383
+ */
3384
+ placements?: ConnectionCredentialPlacement[];
2766
3385
  connectionId: string;
2767
3386
  providerDomain: string;
2768
3387
  provider?: string;
@@ -2803,7 +3422,7 @@ export type ConnectionCredentialsPort = {
2803
3422
  * Resolve rotating MCP transport credentials at request time. Embedded hosts
2804
3423
  * use this to keep their provider connection as the sole credential source;
2805
3424
  * OpenGeni never requires a duplicate connection record. The same resolver is
2806
- * used by model-visible MCP tools and the additive Toolspace/Code Mode proxy.
3425
+ * used by model-visible MCP tools and the exact-attempt Codemode projection.
2807
3426
  */
2808
3427
  mcpCredentials?(input: McpCredentialsRequest): Promise<McpCredentialResolution>;
2809
3428
  };
@@ -3098,6 +3717,12 @@ export type FileResourceRef = z.infer<typeof FileResourceRef>;
3098
3717
  * reconstruct the same typed attachment input after a model switch or retry.
3099
3718
  */
3100
3719
  export const MODEL_ATTACHMENT_REFS_FIELD = "opengeni_attachment_refs" as const;
3720
+ /**
3721
+ * Structured timeline annotations retained beside the deterministic user-text
3722
+ * projection in canonical history. Provider adapters remove this OpenGeni
3723
+ * extension field; the numbered projection in `content` remains model-visible.
3724
+ */
3725
+ export const MODEL_TIMELINE_ANNOTATIONS_FIELD = "opengeni_timeline_annotations" as const;
3101
3726
 
3102
3727
  export const ResourceRef = z.discriminatedUnion("kind", [RepositoryResourceRef, FileResourceRef]);
3103
3728
  export type ResourceRef = z.infer<typeof ResourceRef>;
@@ -3408,6 +4033,56 @@ export const DocumentSearchResponse = z.object({
3408
4033
  });
3409
4034
  export type DocumentSearchResponse = z.infer<typeof DocumentSearchResponse>;
3410
4035
 
4036
+ export const IndexedDocumentSource = z.object({
4037
+ kind: KnowledgeSourceKind,
4038
+ uri: z.string().nullable(),
4039
+ externalId: z.string().nullable(),
4040
+ title: z.string().nullable(),
4041
+ author: z.string().nullable(),
4042
+ createdAt: z.string().nullable(),
4043
+ updatedAt: z.string().nullable(),
4044
+ version: z.string().nullable(),
4045
+ });
4046
+ export type IndexedDocumentSource = z.infer<typeof IndexedDocumentSource>;
4047
+
4048
+ export const IndexedDocumentProvenance = z.object({
4049
+ ingestionWorkspaceId: z.string().uuid(),
4050
+ baseId: z.string().uuid(),
4051
+ fileId: z.string().uuid(),
4052
+ authorityKind: DocumentAuthorityKind,
4053
+ authorityWorkspaceId: z.string().uuid().nullable(),
4054
+ authoritySubjectId: z.string().nullable(),
4055
+ createdBy: z.string().nullable(),
4056
+ createdAt: z.string(),
4057
+ });
4058
+ export type IndexedDocumentProvenance = z.infer<typeof IndexedDocumentProvenance>;
4059
+
4060
+ export const IndexedDocumentSummary = z.object({
4061
+ id: z.string().uuid(),
4062
+ title: z.string(),
4063
+ parser: z.string(),
4064
+ chunkCount: z.number().int().nonnegative(),
4065
+ indexedAt: z.string(),
4066
+ summary: z.string().nullable(),
4067
+ topics: z.array(z.string()),
4068
+ source: IndexedDocumentSource,
4069
+ provenance: IndexedDocumentProvenance,
4070
+ });
4071
+ export type IndexedDocumentSummary = z.infer<typeof IndexedDocumentSummary>;
4072
+
4073
+ export const ListIndexedDocumentsRequest = z.object({
4074
+ checkpoint: z.string().min(1).max(1_024).optional(),
4075
+ limit: z.number().int().positive().max(100).default(50),
4076
+ });
4077
+ export type ListIndexedDocumentsRequest = z.infer<typeof ListIndexedDocumentsRequest>;
4078
+
4079
+ export const ListIndexedDocumentsResponse = z.object({
4080
+ documents: z.array(IndexedDocumentSummary),
4081
+ nextCheckpoint: z.string().min(1).max(1_024),
4082
+ hasMore: z.boolean(),
4083
+ });
4084
+ export type ListIndexedDocumentsResponse = z.infer<typeof ListIndexedDocumentsResponse>;
4085
+
3411
4086
  export const CreateDocumentBaseRequest = z.object({
3412
4087
  name: z.string().min(1),
3413
4088
  description: z.string().optional(),
@@ -3547,6 +4222,7 @@ export const CreateKnowledgeMemoryRequest = z.object({
3547
4222
  createdBySessionId: z.string().uuid().optional(),
3548
4223
  pinned: z.boolean().optional(),
3549
4224
  replacesId: z.string().min(1).optional(),
4225
+ slackPublication: MemorySlackPublicationDistribution.optional(),
3550
4226
  });
3551
4227
  export type CreateKnowledgeMemoryRequest = z.infer<typeof CreateKnowledgeMemoryRequest>;
3552
4228
 
@@ -3945,6 +4621,86 @@ export type SessionGoalStatus = z.infer<typeof SessionGoalStatus>;
3945
4621
  export const SessionGoalCreatedBy = z.enum(["api", "agent", "scheduled_task"]);
3946
4622
  export type SessionGoalCreatedBy = z.infer<typeof SessionGoalCreatedBy>;
3947
4623
 
4624
+ export const SessionGoalMutationPolicy = z.enum([
4625
+ "review_changes",
4626
+ "preserve_intent",
4627
+ "autonomous_adaptation",
4628
+ ]);
4629
+ export type SessionGoalMutationPolicy = z.infer<typeof SessionGoalMutationPolicy>;
4630
+
4631
+ export const SessionGoalChangeKind = z.enum(["refinement", "adaptation", "replacement"]);
4632
+ export type SessionGoalChangeKind = z.infer<typeof SessionGoalChangeKind>;
4633
+
4634
+ export const SESSION_GOAL_TEXT_MAX_BYTES = 8 * 1024;
4635
+ export const SESSION_GOAL_SUCCESS_CRITERIA_MAX_BYTES = 8 * 1024;
4636
+ export const SESSION_GOAL_RATIONALE_MAX_BYTES = 2 * 1024;
4637
+ export const SESSION_GOAL_PROGRESS_MAX_BYTES = 4 * 1024;
4638
+
4639
+ export function sessionGoalUtf8Bytes(value: string): number {
4640
+ return new TextEncoder().encode(value).byteLength;
4641
+ }
4642
+
4643
+ function boundedSessionGoalString(maxBytes: number, field: string) {
4644
+ return z
4645
+ .string()
4646
+ .min(1)
4647
+ .refine((value) => sessionGoalUtf8Bytes(value) <= maxBytes, {
4648
+ message: `${field} exceeds ${maxBytes} UTF-8 bytes`,
4649
+ });
4650
+ }
4651
+
4652
+ const SessionGoalTextWrite = boundedSessionGoalString(SESSION_GOAL_TEXT_MAX_BYTES, "goal text");
4653
+ const SessionGoalSuccessCriteriaWrite = boundedSessionGoalString(
4654
+ SESSION_GOAL_SUCCESS_CRITERIA_MAX_BYTES,
4655
+ "goal success criteria",
4656
+ );
4657
+ const SessionGoalRationaleWrite = boundedSessionGoalString(
4658
+ SESSION_GOAL_RATIONALE_MAX_BYTES,
4659
+ "goal rationale",
4660
+ );
4661
+
4662
+ export const SessionGoalSnapshot = z.discriminatedUnion("state", [
4663
+ z.object({ state: z.literal("none"), capturedAt: z.string() }),
4664
+ z.object({
4665
+ state: z.enum(["active", "paused", "completed"]),
4666
+ goalId: z.string().uuid(),
4667
+ objectiveRevision: z.number().int().positive(),
4668
+ text: SessionGoalTextWrite,
4669
+ successCriteria: SessionGoalSuccessCriteriaWrite.nullable(),
4670
+ mutationPolicy: SessionGoalMutationPolicy,
4671
+ capturedAt: z.string(),
4672
+ }),
4673
+ ]);
4674
+ export type SessionGoalSnapshot = z.infer<typeof SessionGoalSnapshot>;
4675
+
4676
+ export const SessionGoalRevision = z.object({
4677
+ id: z.string().uuid(),
4678
+ accountId: z.string().uuid(),
4679
+ workspaceId: z.string().uuid(),
4680
+ sessionId: z.string().uuid(),
4681
+ goalId: z.string().uuid(),
4682
+ disposition: z.enum(["applied", "proposed", "rejected"]),
4683
+ changeKind: SessionGoalChangeKind,
4684
+ baseObjectiveRevision: z.number().int().nonnegative(),
4685
+ resultObjectiveRevision: z.number().int().positive().nullable(),
4686
+ text: z.string().min(1),
4687
+ successCriteria: z.string().nullable(),
4688
+ mutationPolicy: SessionGoalMutationPolicy,
4689
+ rationale: z.string().min(1),
4690
+ actor: z.enum(["agent", "api", "scheduled_task"]),
4691
+ actorTurnId: z.string().uuid().nullable(),
4692
+ actorAttemptId: z.string().uuid().nullable(),
4693
+ proposalId: z.string().uuid().nullable(),
4694
+ createdAt: z.string(),
4695
+ });
4696
+ export type SessionGoalRevision = z.infer<typeof SessionGoalRevision>;
4697
+
4698
+ export const ApplySessionGoalRevisionRequest = z.object({
4699
+ expectedObjectiveRevision: z.number().int().positive(),
4700
+ rationale: SessionGoalRationaleWrite.optional(),
4701
+ });
4702
+ export type ApplySessionGoalRevisionRequest = z.infer<typeof ApplySessionGoalRevisionRequest>;
4703
+
3948
4704
  export const SessionGoalPausedReason = z.enum([
3949
4705
  "agent",
3950
4706
  "user_pause",
@@ -4003,6 +4759,8 @@ export const SessionGoal = z.object({
4003
4759
  pausedReason: z.string().nullable(),
4004
4760
  createdBy: SessionGoalCreatedBy,
4005
4761
  version: z.number().int().positive(),
4762
+ objectiveRevision: z.number().int().positive(),
4763
+ mutationPolicy: SessionGoalMutationPolicy,
4006
4764
  autoContinuations: z.number().int().nonnegative(),
4007
4765
  noProgressStreak: z.number().int().nonnegative(),
4008
4766
  maxAutoContinuations: z.number().int().positive().nullable(),
@@ -4016,16 +4774,26 @@ export const SessionGoal = z.object({
4016
4774
  export type SessionGoal = z.infer<typeof SessionGoal>;
4017
4775
 
4018
4776
  export const GoalSpec = z.object({
4019
- text: z.string().min(1),
4020
- successCriteria: z.string().min(1).optional(),
4777
+ text: SessionGoalTextWrite,
4778
+ successCriteria: SessionGoalSuccessCriteriaWrite.optional(),
4021
4779
  maxAutoContinuations: z.number().int().positive().optional(),
4780
+ mutationPolicy: SessionGoalMutationPolicy.optional(),
4022
4781
  });
4023
4782
  export type GoalSpec = z.infer<typeof GoalSpec>;
4024
4783
 
4025
- export const UpdateSessionGoalRequest = z.object({
4026
- status: z.enum(["paused", "active"]),
4027
- rationale: z.string().min(1).optional(),
4028
- });
4784
+ export const UpdateSessionGoalRequest = z.union([
4785
+ z.object({
4786
+ status: z.enum(["paused", "active"]),
4787
+ rationale: SessionGoalRationaleWrite.optional(),
4788
+ }),
4789
+ z.object({
4790
+ text: SessionGoalTextWrite,
4791
+ successCriteria: SessionGoalSuccessCriteriaWrite.nullable().optional(),
4792
+ mutationPolicy: SessionGoalMutationPolicy.optional(),
4793
+ rationale: SessionGoalRationaleWrite,
4794
+ expectedObjectiveRevision: z.number().int().positive(),
4795
+ }),
4796
+ ]);
4029
4797
  export type UpdateSessionGoalRequest = z.infer<typeof UpdateSessionGoalRequest>;
4030
4798
 
4031
4799
  export const UpdateSessionRequest = z.object({
@@ -4161,7 +4929,7 @@ export const SessionAuthorizationSurface = z.enum([
4161
4929
  "core",
4162
4930
  "stream",
4163
4931
  "first_party_mcp",
4164
- "toolspace",
4932
+ "codemode",
4165
4933
  ]);
4166
4934
  export type SessionAuthorizationSurface = z.infer<typeof SessionAuthorizationSurface>;
4167
4935
 
@@ -4354,6 +5122,7 @@ export type SyncSessionRealtimeLedgerResponse = z.infer<typeof SyncSessionRealti
4354
5122
 
4355
5123
  export const SessionRealtimeModel = z.enum([
4356
5124
  "gpt-live-1-boulder-alpha",
5125
+ "supergrok/grok-voice-think-fast-2.0",
4357
5126
  "opengeni-gateway/openai/gpt-realtime-2.1",
4358
5127
  "opengeni-gateway/openai/gpt-realtime-mini",
4359
5128
  "opengeni-gateway/xai/grok-voice-think-fast-2.0",
@@ -4366,7 +5135,7 @@ export type SessionRealtimeModel = z.infer<typeof SessionRealtimeModel>;
4366
5135
  export const WorkspaceRealtimeModelCatalogItem = z.object({
4367
5136
  id: SessionRealtimeModel,
4368
5137
  label: z.string().min(1),
4369
- provider: z.enum(["OpenGeni", "Connected Codex", "Your Gateway"]),
5138
+ provider: z.enum(["OpenGeni", "Connected Codex", "Connected SuperGrok", "Your Gateway"]),
4370
5139
  description: z.string().min(1),
4371
5140
  available: z.boolean(),
4372
5141
  unavailableReason: z.string().nullable(),
@@ -4450,7 +5219,7 @@ export const SessionAuthorizationOperation = z.enum([
4450
5219
  "session.viewer.control",
4451
5220
  "session.first_party_mcp.call",
4452
5221
  "session.secret.read",
4453
- "session.toolspace.call",
5222
+ "session.codemode.call",
4454
5223
  "session.pin.write",
4455
5224
  "session.codex_account.write",
4456
5225
  "session.realtime.start",
@@ -4460,11 +5229,14 @@ export const SessionAuthorizationOperation = z.enum([
4460
5229
  "session.human_input.read",
4461
5230
  "session.human_input.write",
4462
5231
  "session.title.write",
5232
+ "session.channel.write",
4463
5233
  "session.mcp.approval_policy.write",
4464
5234
  "session.tool_policy.write",
4465
5235
  "session.goal.read",
4466
5236
  "session.goal.write",
4467
5237
  "session.child.create",
5238
+ "session.visibility.write",
5239
+ "session.fork.create",
4468
5240
  ]);
4469
5241
  export type SessionAuthorizationOperation = z.infer<typeof SessionAuthorizationOperation>;
4470
5242
 
@@ -4486,6 +5258,8 @@ export const SessionAuthorizationActor = z.discriminatedUnion("kind", [
4486
5258
  /** Frozen authority that admitted the calling turn. */
4487
5259
  initiator: TurnInitiator,
4488
5260
  initiatorContext: TurnInitiatorContext,
5261
+ /** Durable causal human for delegated/service work; null for pure service work. */
5262
+ initiatingHumanSubjectId: z.string().min(1).max(1024).nullable(),
4489
5263
  }),
4490
5264
  ]);
4491
5265
  export type SessionAuthorizationActor = z.infer<typeof SessionAuthorizationActor>;
@@ -4564,44 +5338,276 @@ export type SessionAuthorizationPort = {
4564
5338
  ): Promise<SessionAuthorizationListScope>;
4565
5339
  };
4566
5340
 
4567
- export const SessionTurn = z.object({
4568
- id: z.string().uuid(),
4569
- workspaceId: z.string().uuid(),
4570
- sessionId: z.string().uuid(),
4571
- triggerEventId: z.string().uuid(),
4572
- temporalWorkflowId: z.string(),
4573
- status: SessionTurnStatus,
4574
- source: SessionTurnSource,
4575
- position: z.number().int(),
4576
- prompt: z.string().min(1),
4577
- resources: z.array(ResourceRef),
4578
- tools: z.array(ToolRef),
4579
- // Omitted/default discovery and explicit `tools: []` are distinct. False
4580
- // inherits the durable session policy; true replaces it for this turn after
4581
- // admission proves the selection is a subset.
4582
- toolsProvided: z.boolean().optional(),
4583
- model: z.string().min(1),
4584
- reasoningEffort: ReasoningEffort,
4585
- latencyMode: LatencyMode.default("standard"),
4586
- sandboxBackend: SandboxBackend,
4587
- // Per-turn OS override. NULL = inherit the session's sandboxOs.
4588
- sandboxOs: SandboxOs.nullable(),
4589
- metadata: z.record(z.string(), z.unknown()),
4590
- version: z.number().int().positive(),
4591
- executionGeneration: z.number().int().nonnegative(),
4592
- activeAttemptId: z.string().uuid().nullable(),
4593
- lineage: z.record(z.string(), z.unknown()),
4594
- initiator: TurnInitiator,
4595
- initiatorContext: TurnInitiatorContext,
4596
- /** Secret-safe projection of the exact personal authority frozen on this turn. */
4597
- personalConnections: z.array(McpPersonalConnectionSummary).default([]),
4598
- cancelledBy: z.string().nullable(),
4599
- cancelReason: z.string().nullable(),
4600
- startedAt: z.string().nullable(),
4601
- finishedAt: z.string().nullable(),
4602
- createdAt: z.string(),
4603
- updatedAt: z.string(),
4604
- });
5341
+ export const TIMELINE_ANNOTATION_MAX_COUNT = 12;
5342
+ export const TIMELINE_ANNOTATION_QUOTE_MAX_BYTES = 16 * 1024;
5343
+ export const TIMELINE_ANNOTATION_NOTE_MAX_BYTES = 2 * 1024;
5344
+ export const TIMELINE_ANNOTATION_CONTEXT_MAX_BYTES = 512;
5345
+ export const TIMELINE_ANNOTATION_LABEL_MAX_BYTES = 256;
5346
+ export const TIMELINE_ANNOTATIONS_MAX_BYTES = 64 * 1024;
5347
+
5348
+ export const TimelineAnnotationSourceKind = z.enum([
5349
+ "user_message",
5350
+ "assistant_message",
5351
+ "tool_output",
5352
+ ]);
5353
+ export type TimelineAnnotationSourceKind = z.infer<typeof TimelineAnnotationSourceKind>;
5354
+
5355
+ export const TimelineAnnotationSourceEventType = z.enum([
5356
+ "user.message",
5357
+ "agent.message.completed",
5358
+ "agent.toolCall.output",
5359
+ ]);
5360
+ export type TimelineAnnotationSourceEventType = z.infer<typeof TimelineAnnotationSourceEventType>;
5361
+
5362
+ function timelineAnnotationUtf8Bytes(value: string): number {
5363
+ return new TextEncoder().encode(value).byteLength;
5364
+ }
5365
+
5366
+ function timelineAnnotationBoundedText(maxBytes: number, label: string) {
5367
+ return z.string().superRefine((value, ctx) => {
5368
+ if (timelineAnnotationUtf8Bytes(value) > maxBytes) {
5369
+ ctx.addIssue({
5370
+ code: z.ZodIssueCode.custom,
5371
+ message: `${label} must be at most ${maxBytes} UTF-8 bytes`,
5372
+ });
5373
+ }
5374
+ });
5375
+ }
5376
+
5377
+ export const TimelineAnnotationSource = z
5378
+ .object({
5379
+ kind: TimelineAnnotationSourceKind,
5380
+ eventId: z.string().uuid(),
5381
+ eventType: TimelineAnnotationSourceEventType,
5382
+ sequence: z.number().int().positive(),
5383
+ turnId: z.string().uuid().nullable(),
5384
+ startOffset: z.number().int().nonnegative(),
5385
+ endOffset: z.number().int().nonnegative(),
5386
+ contextBefore: timelineAnnotationBoundedText(
5387
+ TIMELINE_ANNOTATION_CONTEXT_MAX_BYTES,
5388
+ "annotation source contextBefore",
5389
+ ),
5390
+ contextAfter: timelineAnnotationBoundedText(
5391
+ TIMELINE_ANNOTATION_CONTEXT_MAX_BYTES,
5392
+ "annotation source contextAfter",
5393
+ ),
5394
+ label: timelineAnnotationBoundedText(
5395
+ TIMELINE_ANNOTATION_LABEL_MAX_BYTES,
5396
+ "annotation source label",
5397
+ ).optional(),
5398
+ })
5399
+ .strict()
5400
+ .superRefine((source, ctx) => {
5401
+ if (source.endOffset < source.startOffset) {
5402
+ ctx.addIssue({
5403
+ code: z.ZodIssueCode.custom,
5404
+ path: ["endOffset"],
5405
+ message: "annotation source endOffset must not precede startOffset",
5406
+ });
5407
+ }
5408
+ const expectedKind =
5409
+ source.eventType === "user.message"
5410
+ ? "user_message"
5411
+ : source.eventType === "agent.message.completed"
5412
+ ? "assistant_message"
5413
+ : "tool_output";
5414
+ if (source.kind !== expectedKind) {
5415
+ ctx.addIssue({
5416
+ code: z.ZodIssueCode.custom,
5417
+ path: ["kind"],
5418
+ message: "annotation source kind does not match eventType",
5419
+ });
5420
+ }
5421
+ });
5422
+ export type TimelineAnnotationSource = z.infer<typeof TimelineAnnotationSource>;
5423
+
5424
+ export const DraftTimelineAnnotation = z
5425
+ .object({
5426
+ id: z.string().uuid(),
5427
+ source: TimelineAnnotationSource,
5428
+ quote: timelineAnnotationBoundedText(TIMELINE_ANNOTATION_QUOTE_MAX_BYTES, "annotation quote"),
5429
+ note: timelineAnnotationBoundedText(TIMELINE_ANNOTATION_NOTE_MAX_BYTES, "annotation note"),
5430
+ })
5431
+ .strict()
5432
+ .superRefine((annotation, ctx) => {
5433
+ if (!annotation.quote.trim()) {
5434
+ ctx.addIssue({
5435
+ code: z.ZodIssueCode.custom,
5436
+ path: ["quote"],
5437
+ message: "annotation quote must contain non-whitespace text",
5438
+ });
5439
+ }
5440
+ });
5441
+ export type DraftTimelineAnnotation = z.infer<typeof DraftTimelineAnnotation>;
5442
+
5443
+ export const SubmittedTimelineAnnotation = DraftTimelineAnnotation.superRefine(
5444
+ (annotation, ctx) => {
5445
+ if (!annotation.note.trim()) {
5446
+ ctx.addIssue({
5447
+ code: z.ZodIssueCode.custom,
5448
+ path: ["note"],
5449
+ message: "annotation note must contain non-whitespace text",
5450
+ });
5451
+ }
5452
+ },
5453
+ );
5454
+ export type SubmittedTimelineAnnotation = z.infer<typeof SubmittedTimelineAnnotation>;
5455
+
5456
+ export const TimelineAnnotation = z
5457
+ .object({
5458
+ id: z.string().uuid(),
5459
+ source: TimelineAnnotationSource,
5460
+ quote: timelineAnnotationBoundedText(TIMELINE_ANNOTATION_QUOTE_MAX_BYTES, "annotation quote"),
5461
+ note: timelineAnnotationBoundedText(
5462
+ TIMELINE_ANNOTATION_NOTE_MAX_BYTES,
5463
+ "annotation note",
5464
+ ).refine((value) => value.trim().length > 0, {
5465
+ message: "annotation note must contain non-whitespace text",
5466
+ }),
5467
+ ordinal: z.number().int().positive(),
5468
+ })
5469
+ .strict();
5470
+ export type TimelineAnnotation = z.infer<typeof TimelineAnnotation>;
5471
+
5472
+ function timelineAnnotationArray<T extends z.ZodTypeAny>(item: T) {
5473
+ return z
5474
+ .array(item)
5475
+ .max(TIMELINE_ANNOTATION_MAX_COUNT)
5476
+ .superRefine((annotations, ctx) => {
5477
+ const ids = new Set<string>();
5478
+ for (let index = 0; index < annotations.length; index += 1) {
5479
+ const id = (annotations[index] as { id?: unknown }).id;
5480
+ if (typeof id === "string" && ids.has(id)) {
5481
+ ctx.addIssue({
5482
+ code: z.ZodIssueCode.custom,
5483
+ path: [index, "id"],
5484
+ message: "annotation ids must be unique",
5485
+ });
5486
+ }
5487
+ if (typeof id === "string") ids.add(id);
5488
+ }
5489
+ if (
5490
+ timelineAnnotationUtf8Bytes(JSON.stringify(annotations)) > TIMELINE_ANNOTATIONS_MAX_BYTES
5491
+ ) {
5492
+ ctx.addIssue({
5493
+ code: z.ZodIssueCode.custom,
5494
+ message: `annotations must be at most ${TIMELINE_ANNOTATIONS_MAX_BYTES} UTF-8 bytes`,
5495
+ });
5496
+ }
5497
+ });
5498
+ }
5499
+
5500
+ export const DraftTimelineAnnotations = timelineAnnotationArray(DraftTimelineAnnotation);
5501
+ export type DraftTimelineAnnotations = z.infer<typeof DraftTimelineAnnotations>;
5502
+
5503
+ export const SubmittedTimelineAnnotations = timelineAnnotationArray(SubmittedTimelineAnnotation);
5504
+ export type SubmittedTimelineAnnotations = z.infer<typeof SubmittedTimelineAnnotations>;
5505
+
5506
+ export const TimelineAnnotations = timelineAnnotationArray(TimelineAnnotation).superRefine(
5507
+ (annotations, ctx) => {
5508
+ for (let index = 0; index < annotations.length; index += 1) {
5509
+ if (annotations[index]!.ordinal !== index + 1) {
5510
+ ctx.addIssue({
5511
+ code: z.ZodIssueCode.custom,
5512
+ path: [index, "ordinal"],
5513
+ message: "annotation ordinals must be contiguous and match array order",
5514
+ });
5515
+ }
5516
+ }
5517
+ },
5518
+ );
5519
+ export type TimelineAnnotations = z.infer<typeof TimelineAnnotations>;
5520
+
5521
+ export function numberTimelineAnnotations(
5522
+ annotations: readonly SubmittedTimelineAnnotation[],
5523
+ ): TimelineAnnotation[] {
5524
+ return annotations.map((annotation, index) => ({
5525
+ ...annotation,
5526
+ source: { ...annotation.source },
5527
+ ordinal: index + 1,
5528
+ }));
5529
+ }
5530
+
5531
+ export function renderTimelineAnnotationsForModel(
5532
+ text: string,
5533
+ annotations: readonly TimelineAnnotation[],
5534
+ ): string {
5535
+ if (annotations.length === 0) return text;
5536
+ const sections = annotations.flatMap((annotation) => [
5537
+ `Annotation ${annotation.ordinal}`,
5538
+ `Source: ${JSON.stringify({
5539
+ kind: annotation.source.kind,
5540
+ eventId: annotation.source.eventId,
5541
+ eventType: annotation.source.eventType,
5542
+ sequence: annotation.source.sequence,
5543
+ turnId: annotation.source.turnId,
5544
+ startOffset: annotation.source.startOffset,
5545
+ endOffset: annotation.source.endOffset,
5546
+ contextBefore: annotation.source.contextBefore,
5547
+ contextAfter: annotation.source.contextAfter,
5548
+ ...(annotation.source.label ? { label: annotation.source.label } : {}),
5549
+ })}`,
5550
+ `Exact quote: ${JSON.stringify(annotation.quote)}`,
5551
+ `User note: ${JSON.stringify(annotation.note)}`,
5552
+ ]);
5553
+ return [
5554
+ ...(text.length > 0 ? [text, ""] : []),
5555
+ "[OpenGeni timeline annotations]",
5556
+ ...sections.flatMap((line, index) =>
5557
+ index > 0 && line.startsWith("Annotation ") ? ["", line] : [line],
5558
+ ),
5559
+ ].join("\n");
5560
+ }
5561
+
5562
+ export const SessionTurn = z
5563
+ .object({
5564
+ id: z.string().uuid(),
5565
+ workspaceId: z.string().uuid(),
5566
+ sessionId: z.string().uuid(),
5567
+ triggerEventId: z.string().uuid(),
5568
+ temporalWorkflowId: z.string(),
5569
+ status: SessionTurnStatus,
5570
+ source: SessionTurnSource,
5571
+ position: z.number().int(),
5572
+ prompt: z.string(),
5573
+ annotations: TimelineAnnotations.default([]),
5574
+ resources: z.array(ResourceRef),
5575
+ tools: z.array(ToolRef),
5576
+ // Omitted/default discovery and explicit `tools: []` are distinct. False
5577
+ // inherits the durable session policy; true replaces it for this turn after
5578
+ // admission proves the selection is a subset.
5579
+ toolsProvided: z.boolean().optional(),
5580
+ model: z.string().min(1),
5581
+ reasoningEffort: ReasoningEffort,
5582
+ latencyMode: LatencyMode.default("standard"),
5583
+ sandboxBackend: SandboxBackend,
5584
+ // Per-turn OS override. NULL = inherit the session's sandboxOs.
5585
+ sandboxOs: SandboxOs.nullable(),
5586
+ metadata: z.record(z.string(), z.unknown()),
5587
+ version: z.number().int().positive(),
5588
+ executionGeneration: z.number().int().nonnegative(),
5589
+ activeAttemptId: z.string().uuid().nullable(),
5590
+ lineage: z.record(z.string(), z.unknown()),
5591
+ initiator: TurnInitiator,
5592
+ initiatorContext: TurnInitiatorContext,
5593
+ /** Secret-safe projection of the exact personal authority frozen on this turn. */
5594
+ personalConnections: z.array(McpPersonalConnectionSummary).default([]),
5595
+ cancelledBy: z.string().nullable(),
5596
+ cancelReason: z.string().nullable(),
5597
+ startedAt: z.string().nullable(),
5598
+ finishedAt: z.string().nullable(),
5599
+ createdAt: z.string(),
5600
+ updatedAt: z.string(),
5601
+ })
5602
+ .superRefine((turn, ctx) => {
5603
+ if (turn.prompt.length === 0 && turn.annotations.length === 0) {
5604
+ ctx.addIssue({
5605
+ code: z.ZodIssueCode.custom,
5606
+ path: ["prompt"],
5607
+ message: "turn prompt or annotations are required",
5608
+ });
5609
+ }
5610
+ });
4605
5611
  export type SessionTurn = z.infer<typeof SessionTurn>;
4606
5612
 
4607
5613
  export const EffectiveControlBlocker = z.object({
@@ -4670,6 +5676,7 @@ export type SessionCommandReceipt = z.infer<typeof SessionCommandReceipt>;
4670
5676
  export const ComposerDraft = z.object({
4671
5677
  revision: z.number().int().nonnegative(),
4672
5678
  text: z.string(),
5679
+ annotations: DraftTimelineAnnotations.default([]),
4673
5680
  resources: z.array(ResourceRef),
4674
5681
  model: z.string().min(1),
4675
5682
  reasoningEffort: ReasoningEffort,
@@ -4711,6 +5718,7 @@ export type DeleteSessionQueueItemRequest = z.infer<typeof DeleteSessionQueueIte
4711
5718
 
4712
5719
  export const SaveComposerDraftRequest = ComposerDraft.pick({
4713
5720
  text: true,
5721
+ annotations: true,
4714
5722
  resources: true,
4715
5723
  model: true,
4716
5724
  reasoningEffort: true,
@@ -4984,6 +5992,7 @@ export const SessionSystemUpdateKind = z.enum([
4984
5992
  "agent_message",
4985
5993
  "agent_steer_instruction",
4986
5994
  "child_terminal_result",
5995
+ "media_generation_result",
4987
5996
  ]);
4988
5997
  export type SessionSystemUpdateKind = z.infer<typeof SessionSystemUpdateKind>;
4989
5998
 
@@ -5028,6 +6037,7 @@ export const SessionSystemUpdatePayload = z.discriminatedUnion("type", [
5028
6037
  status: z.enum(["idle", "failed", "cancelled"]),
5029
6038
  })
5030
6039
  .passthrough(),
6040
+ MediaGenerationResult,
5031
6041
  ]);
5032
6042
  export type SessionSystemUpdatePayload = z.infer<typeof SessionSystemUpdatePayload>;
5033
6043
 
@@ -5259,6 +6269,126 @@ export const RigCheck = z.object({
5259
6269
  });
5260
6270
  export type RigCheck = z.infer<typeof RigCheck>;
5261
6271
 
6272
+ export const RigProviderImageBuildStatus = z.enum(["building", "ready", "failed", "unsupported"]);
6273
+ export type RigProviderImageBuildStatus = z.infer<typeof RigProviderImageBuildStatus>;
6274
+
6275
+ export const RigProviderImage = z
6276
+ .object({
6277
+ backend: SandboxBackend,
6278
+ provider: z.string().min(1).max(64),
6279
+ status: RigProviderImageBuildStatus,
6280
+ // Exact immutable rig-definition/setup identity used to reject stale image
6281
+ // reuse when a version or its effective base image changes.
6282
+ contentHash: z.string().regex(/^sha256:[0-9a-f]{64}$/u),
6283
+ setupHash: z.string().regex(/^sha256:[0-9a-f]{64}$/u),
6284
+ sourceImage: z.string().max(2048).nullable(),
6285
+ buildRequestId: z.string().uuid(),
6286
+ imageId: z.string().min(1).max(512).nullable(),
6287
+ imageDigest: z.string().min(1).max(512).nullable(),
6288
+ artifactId: z.string().uuid().nullable(),
6289
+ providerBindingKeyHash: z
6290
+ .string()
6291
+ .regex(/^sha256:[0-9a-f]{64}$/u)
6292
+ .nullable(),
6293
+ // Added after provider-image v1 shipped. Absence is retained for rolling
6294
+ // compatibility but means runtime must use logical-image + setup fallback
6295
+ // until an explicit verification cold-boots this exact image.
6296
+ coldBootValidation: z
6297
+ .object({
6298
+ version: z.literal(1),
6299
+ checkedAt: z.string().datetime(),
6300
+ })
6301
+ .optional(),
6302
+ provenance: z.object({
6303
+ kind: z.literal("rig_verification"),
6304
+ targetKind: z.enum(["change", "version"]),
6305
+ targetId: z.string().uuid(),
6306
+ }),
6307
+ startedAt: z.string().datetime(),
6308
+ finishedAt: z.string().datetime().nullable(),
6309
+ error: z
6310
+ .object({
6311
+ code: z.string().min(1).max(120),
6312
+ message: z.string().min(1).max(2000),
6313
+ retryable: z.boolean(),
6314
+ })
6315
+ .nullable(),
6316
+ })
6317
+ .superRefine((value, ctx) => {
6318
+ if (value.status === "ready" && !value.imageId && !value.imageDigest) {
6319
+ ctx.addIssue({
6320
+ code: "custom",
6321
+ path: ["imageId"],
6322
+ message: "ready provider images require an immutable image id or digest",
6323
+ });
6324
+ }
6325
+ if (
6326
+ value.status === "ready" &&
6327
+ value.backend === "modal" &&
6328
+ (value.artifactId === null || value.providerBindingKeyHash === null)
6329
+ ) {
6330
+ ctx.addIssue({
6331
+ code: "custom",
6332
+ path: [value.artifactId === null ? "artifactId" : "providerBindingKeyHash"],
6333
+ message: "ready Modal provider images require durable artifact ownership and binding",
6334
+ });
6335
+ }
6336
+ if (value.status !== "ready" && value.artifactId !== null) {
6337
+ ctx.addIssue({
6338
+ code: "custom",
6339
+ path: ["artifactId"],
6340
+ message: "only ready provider images may retain a durable artifact",
6341
+ });
6342
+ }
6343
+ if (value.status === "building") {
6344
+ if (value.finishedAt !== null) {
6345
+ ctx.addIssue({
6346
+ code: "custom",
6347
+ path: ["finishedAt"],
6348
+ message: "building provider images cannot be finished",
6349
+ });
6350
+ }
6351
+ if (value.error !== null) {
6352
+ ctx.addIssue({
6353
+ code: "custom",
6354
+ path: ["error"],
6355
+ message: "building provider images cannot have a terminal error",
6356
+ });
6357
+ }
6358
+ } else if (value.finishedAt === null) {
6359
+ ctx.addIssue({
6360
+ code: "custom",
6361
+ path: ["finishedAt"],
6362
+ message: "terminal provider images require a finish timestamp",
6363
+ });
6364
+ }
6365
+ if ((value.status === "failed" || value.status === "unsupported") && value.error === null) {
6366
+ ctx.addIssue({
6367
+ code: "custom",
6368
+ path: ["error"],
6369
+ message: `${value.status} provider images require failure truth`,
6370
+ });
6371
+ }
6372
+ if (value.status === "ready" && value.error !== null) {
6373
+ ctx.addIssue({
6374
+ code: "custom",
6375
+ path: ["error"],
6376
+ message: "ready provider images cannot retain an error",
6377
+ });
6378
+ }
6379
+ if (value.status !== "ready" && value.coldBootValidation !== undefined) {
6380
+ ctx.addIssue({
6381
+ code: "custom",
6382
+ path: ["coldBootValidation"],
6383
+ message: "only ready provider images may retain cold-boot validation",
6384
+ });
6385
+ }
6386
+ });
6387
+ export type RigProviderImage = z.infer<typeof RigProviderImage>;
6388
+
6389
+ export const RigProviderImages = z.partialRecord(SandboxBackend, RigProviderImage);
6390
+ export type RigProviderImages = z.infer<typeof RigProviderImages>;
6391
+
5262
6392
  export const RigVersion = z.object({
5263
6393
  id: z.string().uuid(),
5264
6394
  rigId: z.string().uuid(),
@@ -5269,6 +6399,9 @@ export const RigVersion = z.object({
5269
6399
  credentialHooks: z.array(z.string()),
5270
6400
  defaultVariableSetIds: z.array(z.string().uuid()),
5271
6401
  changelog: z.string().nullable(),
6402
+ // Operational build metadata is version-bound but not part of the immutable
6403
+ // rig definition. Each backend records its own truthful build state.
6404
+ providerImages: RigProviderImages.default({}),
5272
6405
  // Attribution: 'user:<subject>' | 'session:<id>' | 'system'.
5273
6406
  createdBy: z.string().nullable(),
5274
6407
  active: z.boolean(),
@@ -5361,6 +6494,40 @@ export const UpdateRigRequest = z.object({
5361
6494
  });
5362
6495
  export type UpdateRigRequest = z.infer<typeof UpdateRigRequest>;
5363
6496
 
6497
+ // Workspace-shared channels organize root sessions ("workstreams") by work
6498
+ // type in the rail. Pure organizational metadata: filing a session into a
6499
+ // channel never affects execution, authority, memory, or history.
6500
+ export const Channel = z.object({
6501
+ id: z.string().uuid(),
6502
+ accountId: z.string().uuid(),
6503
+ workspaceId: z.string().uuid(),
6504
+ name: z.string(),
6505
+ description: z.string().nullable(),
6506
+ createdBy: z.string().nullable(),
6507
+ createdAt: z.string(),
6508
+ updatedAt: z.string(),
6509
+ });
6510
+ export type Channel = z.infer<typeof Channel>;
6511
+
6512
+ export const CreateChannelRequest = z.object({
6513
+ name: z.string().trim().min(1).max(80),
6514
+ description: z.string().max(2000).optional(),
6515
+ });
6516
+ export type CreateChannelRequest = z.infer<typeof CreateChannelRequest>;
6517
+
6518
+ export const UpdateChannelRequest = z.object({
6519
+ name: z.string().trim().min(1).max(80).optional(),
6520
+ description: z.string().max(2000).nullable().optional(),
6521
+ });
6522
+ export type UpdateChannelRequest = z.infer<typeof UpdateChannelRequest>;
6523
+
6524
+ // Re-files one session (rail organization only). null moves it back to the
6525
+ // unfiled inbox.
6526
+ export const UpdateSessionChannelRequest = z.object({
6527
+ channelId: z.string().uuid().nullable(),
6528
+ });
6529
+ export type UpdateSessionChannelRequest = z.infer<typeof UpdateSessionChannelRequest>;
6530
+
5364
6531
  // setup_append: the exact command that already worked (+ an optional note).
5365
6532
  export const RigSetupAppendPayload = z.object({
5366
6533
  command: z.string().min(1).max(8192),
@@ -5389,26 +6556,112 @@ export const ProposeRigChangeRequest = z.discriminatedUnion("kind", [
5389
6556
  ]);
5390
6557
  export type ProposeRigChangeRequest = z.infer<typeof ProposeRigChangeRequest>;
5391
6558
 
5392
- export const ScheduledTaskStatus = z.enum(["active", "paused"]);
6559
+ export const ScheduledTaskStatus = /* @__PURE__ */ z.enum(["active", "paused"]);
5393
6560
  export type ScheduledTaskStatus = z.infer<typeof ScheduledTaskStatus>;
5394
6561
 
5395
- export const ScheduledTaskRunStatus = z.enum(["queued", "dispatched", "failed"]);
6562
+ export const ScheduledTaskRunStatus = /* @__PURE__ */ z.enum([
6563
+ "queued",
6564
+ "dispatched",
6565
+ "succeeded",
6566
+ "skipped",
6567
+ "failed",
6568
+ ]);
5396
6569
  export type ScheduledTaskRunStatus = z.infer<typeof ScheduledTaskRunStatus>;
5397
6570
 
5398
- export const ScheduledTaskRunMode = z.enum([
6571
+ export const ScheduledTaskRunMode = /* @__PURE__ */ z.enum([
5399
6572
  "new_session_per_run",
5400
6573
  "reusable_session",
5401
6574
  "existing_session",
5402
6575
  ]);
5403
6576
  export type ScheduledTaskRunMode = z.infer<typeof ScheduledTaskRunMode>;
5404
6577
 
5405
- export const ScheduledTaskOverlapPolicy = z.enum(["allow_concurrent", "skip", "buffer_one"]);
6578
+ export const ScheduledTaskOverlapPolicy = /* @__PURE__ */ z.enum([
6579
+ "allow_concurrent",
6580
+ "skip",
6581
+ "buffer_one",
6582
+ ]);
5406
6583
  export type ScheduledTaskOverlapPolicy = z.infer<typeof ScheduledTaskOverlapPolicy>;
5407
6584
 
5408
- export const ScheduledTaskTriggerType = z.enum(["scheduled", "manual"]);
6585
+ export const ScheduledTaskTriggerType = /* @__PURE__ */ z.enum([
6586
+ "scheduled",
6587
+ "manual",
6588
+ "initial",
6589
+ "provider_event",
6590
+ "retry",
6591
+ "repair",
6592
+ ]);
5409
6593
  export type ScheduledTaskTriggerType = z.infer<typeof ScheduledTaskTriggerType>;
5410
6594
 
5411
- export const ScheduledTaskScheduleSpec = z.discriminatedUnion("type", [
6595
+ export const ScheduledTaskActionKind = /* @__PURE__ */ z.enum([
6596
+ "agent_turn",
6597
+ "knowledge_source_sync",
6598
+ ]);
6599
+ export type ScheduledTaskActionKind = z.infer<typeof ScheduledTaskActionKind>;
6600
+
6601
+ const KnowledgeSourceSyncPositiveInteger = z.number().int().positive();
6602
+ const KnowledgeSourceSyncNonnegativeInteger = z.number().int().nonnegative();
6603
+ const KnowledgeSourceSyncZeroInteger = KnowledgeSourceSyncNonnegativeInteger.default(0);
6604
+ const KnowledgeSourceSyncUuid = z.string().uuid();
6605
+ const KnowledgeSourceSyncSubject = z.string().min(1).max(1024);
6606
+ const KnowledgeSourceSyncEnabled = z.boolean().default(true);
6607
+ const KnowledgeSourceSyncDisabled = z.boolean().default(false);
6608
+
6609
+ export const KnowledgeSourceSyncLimits = /* @__PURE__ */ z.object({
6610
+ maxItems: KnowledgeSourceSyncPositiveInteger.max(10_000).default(500),
6611
+ maxBytes: KnowledgeSourceSyncPositiveInteger.max(5_000_000_000).default(500_000_000),
6612
+ maxFileBytes: KnowledgeSourceSyncPositiveInteger.max(5_000_000_000).default(100_000_000),
6613
+ maxProviderRequests: KnowledgeSourceSyncPositiveInteger.max(10_000).default(1_000),
6614
+ maxElapsedSeconds: KnowledgeSourceSyncPositiveInteger.max(3_600).default(300),
6615
+ maxConcurrency: KnowledgeSourceSyncPositiveInteger.max(32).default(4),
6616
+ maxFailureDetails: KnowledgeSourceSyncPositiveInteger.max(100).default(25),
6617
+ });
6618
+ export type KnowledgeSourceSyncLimits = z.infer<typeof KnowledgeSourceSyncLimits>;
6619
+
6620
+ export const KnowledgeSourceSyncConnectionAuthority = /* @__PURE__ */ z.object({
6621
+ connectionId: KnowledgeSourceSyncUuid,
6622
+ connectionVersion: KnowledgeSourceSyncPositiveInteger,
6623
+ providerDomain: z.string().min(1).max(2048),
6624
+ kind: z.enum(["oauth2", "api_key", "app_install", "delegated"]),
6625
+ ownerSubjectId: KnowledgeSourceSyncSubject,
6626
+ });
6627
+ export type KnowledgeSourceSyncConnectionAuthority = z.infer<
6628
+ typeof KnowledgeSourceSyncConnectionAuthority
6629
+ >;
6630
+
6631
+ export const KnowledgeSourceSyncAction = /* @__PURE__ */ z
6632
+ .object({
6633
+ kind: z.literal("knowledge_source_sync"),
6634
+ sourceId: KnowledgeSourceSyncUuid,
6635
+ sourceGeneration: KnowledgeSourceSyncNonnegativeInteger,
6636
+ sourceLifecycleGeneration: KnowledgeSourceSyncPositiveInteger,
6637
+ sourceConfigGeneration: KnowledgeSourceSyncPositiveInteger,
6638
+ controlWorkspaceId: KnowledgeSourceSyncUuid,
6639
+ providerCoordinationKey: z.string().trim().min(1).max(1024),
6640
+ connection: KnowledgeSourceSyncConnectionAuthority,
6641
+ destination: ScopedKnowledgeScope,
6642
+ initiatingSubjectId: KnowledgeSourceSyncSubject,
6643
+ allDescendants: KnowledgeSourceSyncEnabled,
6644
+ limits: KnowledgeSourceSyncLimits.prefault({}),
6645
+ })
6646
+ .strict();
6647
+ export type KnowledgeSourceSyncAction = z.infer<typeof KnowledgeSourceSyncAction>;
6648
+
6649
+ export const KnowledgeSourceSyncScheduleControl = /* @__PURE__ */ z
6650
+ .object({
6651
+ sourceEnabled: KnowledgeSourceSyncEnabled,
6652
+ connectionPaused: KnowledgeSourceSyncDisabled,
6653
+ })
6654
+ .strict();
6655
+ export type KnowledgeSourceSyncScheduleControl = z.infer<typeof KnowledgeSourceSyncScheduleControl>;
6656
+
6657
+ export const ScheduledTaskAction = /* @__PURE__ */ z.discriminatedUnion("kind", [
6658
+ z.object({ kind: z.literal("agent_turn") }).strict(),
6659
+ KnowledgeSourceSyncAction,
6660
+ ]);
6661
+ export type ScheduledTaskAction = z.infer<typeof ScheduledTaskAction>;
6662
+
6663
+ export const ScheduledTaskScheduleSpec = /* @__PURE__ */ z.discriminatedUnion("type", [
6664
+ z.object({ type: z.literal("manual") }).strict(),
5412
6665
  z.object({
5413
6666
  type: z.literal("once"),
5414
6667
  runAt: z.string().datetime({ offset: true }),
@@ -5433,7 +6686,7 @@ export const ScheduledTaskScheduleSpec = z.discriminatedUnion("type", [
5433
6686
  ]);
5434
6687
  export type ScheduledTaskScheduleSpec = z.infer<typeof ScheduledTaskScheduleSpec>;
5435
6688
 
5436
- export const ScheduledTaskAgentConfig = z.object({
6689
+ export const ScheduledTaskAgentConfig = /* @__PURE__ */ z.object({
5437
6690
  prompt: z.string().min(1),
5438
6691
  resources: z.array(ResourceRef).default([]),
5439
6692
  tools: z.array(ToolRef).default([]),
@@ -5452,7 +6705,7 @@ export const ScheduledTaskAgentConfig = z.object({
5452
6705
  });
5453
6706
  export type ScheduledTaskAgentConfig = z.infer<typeof ScheduledTaskAgentConfig>;
5454
6707
 
5455
- export const ScheduledTask = z.object({
6708
+ export const ScheduledTask = /* @__PURE__ */ z.object({
5456
6709
  id: z.string().uuid(),
5457
6710
  accountId: z.string().uuid(),
5458
6711
  workspaceId: z.string().uuid(),
@@ -5462,6 +6715,7 @@ export const ScheduledTask = z.object({
5462
6715
  temporalScheduleId: z.string(),
5463
6716
  runMode: ScheduledTaskRunMode,
5464
6717
  overlapPolicy: ScheduledTaskOverlapPolicy,
6718
+ action: ScheduledTaskAction.default({ kind: "agent_turn" }),
5465
6719
  agentConfig: ScheduledTaskAgentConfig,
5466
6720
  createdBy: TurnInitiator.default({
5467
6721
  kind: "service",
@@ -5484,7 +6738,52 @@ export const ScheduledTask = z.object({
5484
6738
  });
5485
6739
  export type ScheduledTask = z.infer<typeof ScheduledTask>;
5486
6740
 
5487
- export const ScheduledTaskRun = z.object({
6741
+ export const KnowledgeSourceSyncFailure = /* @__PURE__ */ z.object({
6742
+ externalObjectId: KnowledgeSourceSyncSubject,
6743
+ code: z.enum([
6744
+ "authority_changed",
6745
+ "connection_reconnect_required",
6746
+ "provider_unavailable",
6747
+ "provider_rejected",
6748
+ "provider_payload_invalid",
6749
+ "content_unsupported",
6750
+ "content_too_large",
6751
+ "resource_limit",
6752
+ "item_processing_failed",
6753
+ "indexing_failed",
6754
+ "internal_failure",
6755
+ ]),
6756
+ retryable: z.boolean(),
6757
+ message: z.string().min(1).max(1000),
6758
+ });
6759
+ export type KnowledgeSourceSyncFailure = z.infer<typeof KnowledgeSourceSyncFailure>;
6760
+
6761
+ export const KnowledgeSourceSyncRunSummary = /* @__PURE__ */ z.object({
6762
+ phase: z
6763
+ .enum(["queued", "inventory", "transfer", "index", "checkpoint", "completed", "failed"])
6764
+ .default("queued"),
6765
+ scanned: KnowledgeSourceSyncZeroInteger,
6766
+ imported: KnowledgeSourceSyncZeroInteger,
6767
+ unchanged: KnowledgeSourceSyncZeroInteger,
6768
+ skipped: KnowledgeSourceSyncZeroInteger,
6769
+ failed: KnowledgeSourceSyncZeroInteger,
6770
+ bytes: KnowledgeSourceSyncZeroInteger,
6771
+ providerRequests: KnowledgeSourceSyncZeroInteger,
6772
+ elapsedMs: KnowledgeSourceSyncZeroInteger,
6773
+ indexed: KnowledgeSourceSyncZeroInteger,
6774
+ aclPending: KnowledgeSourceSyncZeroInteger,
6775
+ retryable: KnowledgeSourceSyncDisabled,
6776
+ limitReached: z
6777
+ .enum(["items", "bytes", "file_bytes", "provider_requests", "elapsed_time"])
6778
+ .nullable()
6779
+ .default(null),
6780
+ checkpointed: KnowledgeSourceSyncDisabled,
6781
+ reconnectRequired: KnowledgeSourceSyncDisabled,
6782
+ failures: z.array(KnowledgeSourceSyncFailure).max(100).default([]),
6783
+ });
6784
+ export type KnowledgeSourceSyncRunSummary = z.infer<typeof KnowledgeSourceSyncRunSummary>;
6785
+
6786
+ export const ScheduledTaskRun = /* @__PURE__ */ z.object({
5488
6787
  id: z.string().uuid(),
5489
6788
  accountId: z.string().uuid(),
5490
6789
  workspaceId: z.string().uuid(),
@@ -5495,15 +6794,23 @@ export const ScheduledTaskRun = z.object({
5495
6794
  firedAt: z.string(),
5496
6795
  sessionId: z.string().uuid().nullable(),
5497
6796
  triggerEventId: z.string().uuid().nullable(),
6797
+ actionKind: ScheduledTaskActionKind.default("agent_turn"),
6798
+ knowledgeSyncRunId: KnowledgeSourceSyncUuid.nullable().default(null),
6799
+ knowledgeSummary: KnowledgeSourceSyncRunSummary.nullable().default(null),
6800
+ completedAt: z.string().nullable().default(null),
5498
6801
  error: z.string().nullable(),
5499
6802
  createdAt: z.string(),
5500
6803
  updatedAt: z.string(),
5501
6804
  });
5502
6805
  export type ScheduledTaskRun = z.infer<typeof ScheduledTaskRun>;
5503
6806
 
5504
- export const CreateScheduledTaskRequest = withVariableSetIdAlias({
6807
+ const CreateAgentScheduledTaskRequest = /* @__PURE__ */ withVariableSetIdAlias({
5505
6808
  name: z.string().min(1),
5506
6809
  schedule: ScheduledTaskScheduleSpec,
6810
+ action: z
6811
+ .object({ kind: z.literal("agent_turn") })
6812
+ .strict()
6813
+ .default({ kind: "agent_turn" }),
5507
6814
  runMode: ScheduledTaskRunMode.default("new_session_per_run"),
5508
6815
  overlapPolicy: ScheduledTaskOverlapPolicy.default("allow_concurrent"),
5509
6816
  targetSessionId: z.string().uuid().nullable().optional(),
@@ -5537,48 +6844,80 @@ export const CreateScheduledTaskRequest = withVariableSetIdAlias({
5537
6844
  });
5538
6845
  }
5539
6846
  });
6847
+
6848
+ const CreateKnowledgeSourceSyncScheduledTaskRequest = /* @__PURE__ */ z
6849
+ .object({
6850
+ name: z.string().min(1),
6851
+ schedule: ScheduledTaskScheduleSpec,
6852
+ action: KnowledgeSourceSyncAction,
6853
+ overlapPolicy: z.enum(["skip", "buffer_one"]).default("buffer_one"),
6854
+ status: ScheduledTaskStatus.default("active"),
6855
+ metadata: z.record(z.string(), z.unknown()).default({}),
6856
+ })
6857
+ .strict()
6858
+ .transform((value) => ({
6859
+ ...value,
6860
+ runMode: "new_session_per_run" as const,
6861
+ targetSessionId: null,
6862
+ agentConfig: {
6863
+ prompt: "Knowledge source synchronization",
6864
+ resources: [],
6865
+ tools: [],
6866
+ metadata: {},
6867
+ },
6868
+ variableSetId: null,
6869
+ environmentId: null,
6870
+ rigId: null,
6871
+ }));
6872
+
6873
+ export const CreateScheduledTaskRequest = /* @__PURE__ */ z.union([
6874
+ CreateKnowledgeSourceSyncScheduledTaskRequest,
6875
+ CreateAgentScheduledTaskRequest,
6876
+ ]);
5540
6877
  export type CreateScheduledTaskRequest = z.infer<typeof CreateScheduledTaskRequest>;
5541
6878
 
5542
- export const UpdateScheduledTaskRequest = withVariableSetIdAlias({
5543
- name: z.string().min(1).optional(),
5544
- schedule: ScheduledTaskScheduleSpec.optional(),
5545
- runMode: ScheduledTaskRunMode.optional(),
5546
- overlapPolicy: ScheduledTaskOverlapPolicy.optional(),
5547
- targetSessionId: z.string().uuid().nullable().optional(),
5548
- agentConfig: ScheduledTaskAgentConfig.optional(),
5549
- status: ScheduledTaskStatus.optional(),
5550
- variableSetId: z.string().uuid().nullable().optional(),
5551
- environmentId: z.string().uuid().nullable().optional(),
5552
- // The rig each run binds to (M3); null clears it. Its active version is
5553
- // resolved per fire, so an update takes effect on the next dispatch.
5554
- rigId: z.string().uuid().nullable().optional(),
5555
- metadata: z.record(z.string(), z.unknown()).optional(),
5556
- }).superRefine((value, context) => {
5557
- if (value.targetSessionId && value.runMode && value.runMode !== "existing_session") {
5558
- context.addIssue({
5559
- code: "custom",
5560
- path: ["targetSessionId"],
5561
- message: "targetSessionId requires runMode=existing_session",
5562
- });
5563
- }
5564
- if (value.runMode === "existing_session" && value.targetSessionId === null) {
5565
- context.addIssue({
5566
- code: "custom",
5567
- path: ["targetSessionId"],
5568
- message: "targetSessionId cannot be null when runMode=existing_session",
5569
- });
5570
- }
5571
- if (
5572
- value.agentConfig?.goal &&
5573
- (value.runMode === "existing_session" || Boolean(value.targetSessionId))
5574
- ) {
5575
- context.addIssue({
5576
- code: "custom",
5577
- path: ["agentConfig", "goal"],
5578
- message: "agentConfig.goal cannot be used with an existing-session target",
5579
- });
5580
- }
5581
- });
6879
+ export const UpdateScheduledTaskRequest =
6880
+ /* @__PURE__ */ withVariableSetIdAlias({
6881
+ name: z.string().min(1).optional(),
6882
+ schedule: ScheduledTaskScheduleSpec.optional(),
6883
+ runMode: ScheduledTaskRunMode.optional(),
6884
+ overlapPolicy: ScheduledTaskOverlapPolicy.optional(),
6885
+ action: ScheduledTaskAction.optional(),
6886
+ targetSessionId: z.string().uuid().nullable().optional(),
6887
+ agentConfig: ScheduledTaskAgentConfig.optional(),
6888
+ status: ScheduledTaskStatus.optional(),
6889
+ variableSetId: z.string().uuid().nullable().optional(),
6890
+ environmentId: z.string().uuid().nullable().optional(),
6891
+ // The rig each run binds to (M3); null clears it. Its active version is
6892
+ // resolved per fire, so an update takes effect on the next dispatch.
6893
+ rigId: z.string().uuid().nullable().optional(),
6894
+ metadata: z.record(z.string(), z.unknown()).optional(),
6895
+ }).superRefine((value, context) => {
6896
+ if (value.targetSessionId && value.runMode && value.runMode !== "existing_session") {
6897
+ context.addIssue({
6898
+ code: "custom",
6899
+ path: ["targetSessionId"],
6900
+ message: "targetSessionId requires runMode=existing_session",
6901
+ });
6902
+ }
6903
+ if (value.runMode === "existing_session" && value.targetSessionId === null) {
6904
+ context.addIssue({
6905
+ code: "custom",
6906
+ path: ["targetSessionId"],
6907
+ message: "targetSessionId cannot be null when runMode=existing_session",
6908
+ });
6909
+ }
6910
+ if (
6911
+ value.agentConfig?.goal &&
6912
+ (value.runMode === "existing_session" || Boolean(value.targetSessionId))
6913
+ ) {
6914
+ context.addIssue({
6915
+ code: "custom",
6916
+ path: ["agentConfig", "goal"],
6917
+ message: "agentConfig.goal cannot be used with an existing-session target",
6918
+ });
6919
+ }
6920
+ });
5582
6921
  export type UpdateScheduledTaskRequest = z.infer<typeof UpdateScheduledTaskRequest>;
5583
6922
 
5584
6923
  /**
@@ -5729,11 +7068,86 @@ function isSafePackSkillRelativePath(path: string): boolean {
5729
7068
  .every((segment) => segment.length > 0 && segment !== "." && segment !== "..");
5730
7069
  }
5731
7070
 
5732
- const CapabilityPackVariableSet = z.object({
5733
- description: z.string().min(1),
5734
- requiredVariables: z.array(VariableSetVariableName).default([]),
5735
- required: z.boolean().default(false),
5736
- });
7071
+ const CapabilityPackVariableSet = z
7072
+ .object({
7073
+ description: z.string().min(1).max(2048),
7074
+ requiredVariables: z.array(VariableSetVariableName).max(256).default([]),
7075
+ required: z.boolean().default(false),
7076
+ })
7077
+ .strict();
7078
+
7079
+ const CapabilityPackComponentKey = z
7080
+ .string()
7081
+ .min(1)
7082
+ .max(128)
7083
+ .regex(/^[a-z0-9](?:[a-z0-9._/-]*[a-z0-9])?$/);
7084
+
7085
+ const CapabilityPackInstanceKey = z
7086
+ .string()
7087
+ .min(1)
7088
+ .max(128)
7089
+ .regex(/^[a-z0-9](?:[a-z0-9._/-]*[a-z0-9])?$/);
7090
+
7091
+ /**
7092
+ * Immutable requirements adopted by a Pack installation. The manifest names
7093
+ * portable identities; installation resolves them to exact workspace-local
7094
+ * component rows and records Pack ownership in the shared component ledger.
7095
+ */
7096
+ export const CapabilityPackComponentReference = z.discriminatedUnion("kind", [
7097
+ z
7098
+ .object({
7099
+ key: CapabilityPackComponentKey,
7100
+ kind: z.literal("plugin"),
7101
+ pluginKey: z.string().min(1).max(200),
7102
+ version: z.string().min(1).max(128),
7103
+ manifestDigest: z.string().regex(/^[0-9a-f]{64}$/),
7104
+ required: z.boolean().default(true),
7105
+ })
7106
+ .strict(),
7107
+ z
7108
+ .object({
7109
+ key: CapabilityPackComponentKey,
7110
+ kind: z.literal("skill"),
7111
+ capabilityId: z.string().min(1).max(512),
7112
+ contentSha256: z.string().regex(/^[0-9a-f]{64}$/),
7113
+ required: z.boolean().default(true),
7114
+ })
7115
+ .strict(),
7116
+ z
7117
+ .object({
7118
+ key: CapabilityPackComponentKey,
7119
+ kind: z.literal("integration"),
7120
+ capabilityId: z.string().min(1).max(512),
7121
+ instanceKey: CapabilityPackInstanceKey,
7122
+ revisionId: z.string().min(1).max(512),
7123
+ contentSha256: z.string().regex(/^[0-9a-f]{64}$/),
7124
+ required: z.boolean().default(true),
7125
+ })
7126
+ .strict(),
7127
+ z
7128
+ .object({
7129
+ key: CapabilityPackComponentKey,
7130
+ kind: z.literal("facet"),
7131
+ capabilityId: z.string().min(1).max(512),
7132
+ instanceKey: CapabilityPackInstanceKey,
7133
+ facetKey: z.string().min(1).max(200),
7134
+ bindingKey: CapabilityPackInstanceKey,
7135
+ configDigest: z.string().regex(/^[0-9a-f]{64}$/),
7136
+ required: z.boolean().default(true),
7137
+ })
7138
+ .strict(),
7139
+ ]);
7140
+ export type CapabilityPackComponentReference = z.infer<typeof CapabilityPackComponentReference>;
7141
+
7142
+ export const CapabilityPackRigRequirement = z
7143
+ .object({
7144
+ description: z.string().min(1).max(2048).optional(),
7145
+ required: z.boolean().default(true),
7146
+ rigId: z.string().uuid().optional(),
7147
+ requireVerified: z.boolean().default(false),
7148
+ })
7149
+ .strict();
7150
+ export type CapabilityPackRigRequirement = z.infer<typeof CapabilityPackRigRequirement>;
5737
7151
 
5738
7152
  export const CapabilityPack = z.preprocess(
5739
7153
  (input) => {
@@ -5763,15 +7177,19 @@ export const CapabilityPack = z.preprocess(
5763
7177
  },
5764
7178
  z
5765
7179
  .object({
5766
- id: z.string().min(1),
5767
- name: z.string().min(1),
5768
- description: z.string().min(1),
5769
- role: z.string().min(1),
5770
- category: z.string().min(1),
5771
- version: z.string().min(1),
5772
- // Container image ref (digest-pinned recommended) the pack's sessions run
5773
- // in. At most one enabled pack per workspace may declare one; with none,
5774
- // sessions use the deployment-wide image settings.
7180
+ id: z
7181
+ .string()
7182
+ .min(1)
7183
+ .max(100)
7184
+ .regex(/^[a-z0-9](?:[a-z0-9._/-]*[a-z0-9])?$/),
7185
+ name: z.string().min(1).max(200),
7186
+ description: z.string().min(1).max(4096),
7187
+ role: z.string().min(1).max(128),
7188
+ category: z.string().min(1).max(128),
7189
+ version: z.string().min(1).max(128),
7190
+ // Legacy manifest compatibility. V2 installation resolves this image to
7191
+ // an explicit Rig requirement; the Pack no longer changes workspace
7192
+ // runtime settings directly.
5775
7193
  sandboxImage: z.string().trim().min(1).max(512).optional(),
5776
7194
  // Optional provider-native immutable identities for the exact logical
5777
7195
  // sandboxImage above. These avoid re-importing a private registry image on
@@ -5791,7 +7209,8 @@ export const CapabilityPack = z.preprocess(
5791
7209
  })
5792
7210
  .strict()
5793
7211
  .optional(),
5794
- // Skills delivered into the sandbox skill index when the pack is enabled.
7212
+ // Legacy inline Skills are migrated into immutable Skill components by
7213
+ // the V2 Pack installer. They are not loaded directly by V2 runtime.
5795
7214
  skills: z
5796
7215
  .array(CapabilityPackSkill)
5797
7216
  .max(32)
@@ -5810,6 +7229,8 @@ export const CapabilityPack = z.preprocess(
5810
7229
  });
5811
7230
  })
5812
7231
  .default([]),
7232
+ components: z.array(CapabilityPackComponentReference).max(128).default([]),
7233
+ rig: CapabilityPackRigRequirement.optional(),
5813
7234
  tools: z.array(ToolRef).default([]),
5814
7235
  connectors: z.array(CapabilityPackConnector).default([]),
5815
7236
  knowledge: z.array(CapabilityPackKnowledge).default([]),
@@ -5818,6 +7239,28 @@ export const CapabilityPack = z.preprocess(
5818
7239
  metadata: z.record(z.string(), z.unknown()).default({}),
5819
7240
  })
5820
7241
  .superRefine((pack, ctx) => {
7242
+ const componentKeys = new Set<string>();
7243
+ pack.components.forEach((component, index) => {
7244
+ if (componentKeys.has(component.key)) {
7245
+ ctx.addIssue({
7246
+ code: "custom",
7247
+ message: `duplicate Pack component key: ${component.key}`,
7248
+ path: ["components", index, "key"],
7249
+ });
7250
+ }
7251
+ componentKeys.add(component.key);
7252
+ });
7253
+ pack.skills.forEach((skill, index) => {
7254
+ const key = `inline-skill/${skill.name.toLowerCase()}`;
7255
+ if (componentKeys.has(key)) {
7256
+ ctx.addIssue({
7257
+ code: "custom",
7258
+ message: `Pack component key conflicts with inline Skill ${skill.name}: ${key}`,
7259
+ path: ["skills", index, "name"],
7260
+ });
7261
+ }
7262
+ componentKeys.add(key);
7263
+ });
5821
7264
  if (!pack.sandboxProviderImages?.modal) {
5822
7265
  return;
5823
7266
  }
@@ -5855,7 +7298,12 @@ export const WorkspaceRegisteredPack = z.object({
5855
7298
  });
5856
7299
  export type WorkspaceRegisteredPack = z.infer<typeof WorkspaceRegisteredPack>;
5857
7300
 
5858
- export const PackInstallationStatus = z.enum(["active", "disabled"]);
7301
+ export const PackInstallationStatus = z.enum([
7302
+ "installing",
7303
+ "active",
7304
+ "needs_attention",
7305
+ "disabled",
7306
+ ]);
5859
7307
  export type PackInstallationStatus = z.infer<typeof PackInstallationStatus>;
5860
7308
 
5861
7309
  export const PackInstallation = z.object({
@@ -5864,6 +7312,14 @@ export const PackInstallation = z.object({
5864
7312
  workspaceId: z.string().uuid(),
5865
7313
  packId: z.string().min(1),
5866
7314
  status: PackInstallationStatus,
7315
+ version: z.number().int().positive(),
7316
+ manifestSnapshot: CapabilityPack.nullable(),
7317
+ manifestDigest: z
7318
+ .string()
7319
+ .regex(/^[0-9a-f]{64}$/)
7320
+ .nullable(),
7321
+ selectedRigId: z.string().uuid().nullable(),
7322
+ installedBySubjectId: z.string().min(1).max(1024).nullable(),
5867
7323
  metadata: z.record(z.string(), z.unknown()),
5868
7324
  enabledAt: z.string(),
5869
7325
  updatedAt: z.string(),
@@ -5877,6 +7333,116 @@ export const EnablePackRequest = withVariableSetIdAlias({
5877
7333
  });
5878
7334
  export type EnablePackRequest = z.infer<typeof EnablePackRequest>;
5879
7335
 
7336
+ export const PackComponentResolutionStatus = z.enum(["ready", "missing", "mismatch"]);
7337
+ export type PackComponentResolutionStatus = z.infer<typeof PackComponentResolutionStatus>;
7338
+
7339
+ export const PackComponentResolution = z
7340
+ .object({
7341
+ key: CapabilityPackComponentKey,
7342
+ kind: z.enum(["plugin", "skill", "integration", "facet", "inline_skill"]),
7343
+ capabilityId: z.string().min(1).max(512),
7344
+ required: z.boolean(),
7345
+ status: PackComponentResolutionStatus,
7346
+ expectedDigest: z.string().regex(/^[0-9a-f]{64}$/),
7347
+ actualDigest: z
7348
+ .string()
7349
+ .regex(/^[0-9a-f]{64}$/)
7350
+ .nullable(),
7351
+ resolvedId: z.string().min(1).max(512).nullable(),
7352
+ label: z.string().min(1).max(200),
7353
+ })
7354
+ .strict();
7355
+ export type PackComponentResolution = z.infer<typeof PackComponentResolution>;
7356
+
7357
+ export const PackRigResolution = z
7358
+ .object({
7359
+ required: z.boolean(),
7360
+ status: z.enum(["not_required", "ready", "missing", "mismatch", "unverified"]),
7361
+ requestedRigId: z.string().uuid().nullable(),
7362
+ rigId: z.string().uuid().nullable(),
7363
+ rigVersionId: z.string().uuid().nullable(),
7364
+ name: z.string().min(1).max(200).nullable(),
7365
+ image: z.string().min(1).max(512).nullable(),
7366
+ })
7367
+ .strict();
7368
+ export type PackRigResolution = z.infer<typeof PackRigResolution>;
7369
+
7370
+ export const PreviewPackInstallationRequest = z
7371
+ .object({
7372
+ rigId: z.string().uuid().optional(),
7373
+ variableSetId: z.string().uuid().optional(),
7374
+ })
7375
+ .strict();
7376
+ export type PreviewPackInstallationRequest = z.infer<typeof PreviewPackInstallationRequest>;
7377
+
7378
+ export const PackInstallationPreview = z
7379
+ .object({
7380
+ packId: z.string().min(1),
7381
+ packVersion: z.string().min(1),
7382
+ manifestDigest: z.string().regex(/^[0-9a-f]{64}$/),
7383
+ installationVersion: z.number().int().positive().nullable(),
7384
+ action: z.enum(["install", "update", "repair"]),
7385
+ ready: z.boolean(),
7386
+ blockers: z.array(z.string().min(1).max(500)).max(256),
7387
+ components: z.array(PackComponentResolution).max(160),
7388
+ rig: PackRigResolution,
7389
+ variableSetId: z.string().uuid().nullable(),
7390
+ legacyInlineSkillCount: z.number().int().nonnegative().max(32),
7391
+ legacySandboxImage: z.string().min(1).max(512).nullable(),
7392
+ })
7393
+ .strict();
7394
+ export type PackInstallationPreview = z.infer<typeof PackInstallationPreview>;
7395
+
7396
+ export const InstallPackRequest = z
7397
+ .object({
7398
+ expectedManifestDigest: z.string().regex(/^[0-9a-f]{64}$/),
7399
+ expectedInstallationVersion: z.number().int().positive().optional(),
7400
+ rigId: z.string().uuid().optional(),
7401
+ variableSetId: z.string().uuid().optional(),
7402
+ idempotencyKey: z.string().uuid(),
7403
+ metadata: z.record(z.string(), z.unknown()).default({}),
7404
+ })
7405
+ .strict();
7406
+ export type InstallPackRequest = z.infer<typeof InstallPackRequest>;
7407
+
7408
+ export const PackUninstallPreview = z
7409
+ .object({
7410
+ packId: z.string().min(1),
7411
+ installed: z.boolean(),
7412
+ installationVersion: z.number().int().positive().nullable(),
7413
+ components: z
7414
+ .array(
7415
+ z
7416
+ .object({
7417
+ key: CapabilityPackComponentKey,
7418
+ kind: z.enum(["plugin", "skill", "integration", "facet", "inline_skill"]),
7419
+ capabilityId: z.string().min(1).max(512),
7420
+ retainedByOtherOwners: z.boolean(),
7421
+ })
7422
+ .strict(),
7423
+ )
7424
+ .max(160),
7425
+ })
7426
+ .strict();
7427
+ export type PackUninstallPreview = z.infer<typeof PackUninstallPreview>;
7428
+
7429
+ export const UninstallPackRequest = z
7430
+ .object({
7431
+ expectedInstallationVersion: z.number().int().positive(),
7432
+ idempotencyKey: z.string().uuid(),
7433
+ })
7434
+ .strict();
7435
+ export type UninstallPackRequest = z.infer<typeof UninstallPackRequest>;
7436
+
7437
+ export const UninstallPackResult = z
7438
+ .object({
7439
+ packId: z.string().min(1),
7440
+ status: z.enum(["not_installed", "uninstalled"]),
7441
+ retainedComponents: z.array(z.string().min(1).max(512)).max(160),
7442
+ })
7443
+ .strict();
7444
+ export type UninstallPackResult = z.infer<typeof UninstallPackResult>;
7445
+
5880
7446
  export const SocialProvider = z.enum([
5881
7447
  "x",
5882
7448
  "reddit",
@@ -5995,6 +7561,48 @@ export const OpenGeniSlackBotConnectionMetadata = z
5995
7561
  .passthrough();
5996
7562
  export type OpenGeniSlackBotConnectionMetadata = z.infer<typeof OpenGeniSlackBotConnectionMetadata>;
5997
7563
 
7564
+ export const FIKEN_PROVIDER_DOMAIN = "fiken.no" as const;
7565
+ export const FIKEN_CREDENTIAL_ROLE = "fiken_api_token" as const;
7566
+ export const FIKEN_CREDENTIAL_LABEL = "Fiken API token" as const;
7567
+
7568
+ export const FikenCompanySummary = z.object({
7569
+ slug: z.string().min(1).max(128),
7570
+ name: z.string().min(1).max(256),
7571
+ organizationNumber: z.string().max(64).nullable(),
7572
+ });
7573
+ export type FikenCompanySummary = z.infer<typeof FikenCompanySummary>;
7574
+
7575
+ export const FikenConnectionMetadata = z
7576
+ .object({
7577
+ credentialRole: z.literal(FIKEN_CREDENTIAL_ROLE),
7578
+ credentialLabel: z.literal(FIKEN_CREDENTIAL_LABEL),
7579
+ companies: z.array(FikenCompanySummary).max(100),
7580
+ defaultCompanySlug: z.string().min(1).max(128).nullable(),
7581
+ verifiedAt: z.string().datetime({ offset: true }),
7582
+ })
7583
+ .passthrough();
7584
+ export type FikenConnectionMetadata = z.infer<typeof FikenConnectionMetadata>;
7585
+
7586
+ export const FikenInstallRequest = z.object({
7587
+ apiToken: z.string().min(16).max(512),
7588
+ defaultCompanySlug: z.string().min(1).max(128).optional(),
7589
+ /** Existing Fiken connection to rewrite in place (reconnect). */
7590
+ connectionId: z.string().uuid().optional(),
7591
+ });
7592
+ export type FikenInstallRequest = z.infer<typeof FikenInstallRequest>;
7593
+
7594
+ export const FikenOAuthStartRequest = z.object({
7595
+ /** Existing Fiken connection to re-authorize in place (reconnect). */
7596
+ connectionId: z.string().uuid().optional(),
7597
+ });
7598
+ export type FikenOAuthStartRequest = z.infer<typeof FikenOAuthStartRequest>;
7599
+
7600
+ export const FikenOAuthStartResponse = z.object({
7601
+ authorizationUrl: z.string().url(),
7602
+ expiresAt: z.string().datetime({ offset: true }),
7603
+ });
7604
+ export type FikenOAuthStartResponse = z.infer<typeof FikenOAuthStartResponse>;
7605
+
5998
7606
  export const ConnectionMetadata = z.object({
5999
7607
  id: z.string().uuid(),
6000
7608
  accountId: z.string().uuid(),
@@ -6110,6 +7718,38 @@ export const OpenGeniSlackBotInstallStart = z.object({
6110
7718
  });
6111
7719
  export type OpenGeniSlackBotInstallStart = z.infer<typeof OpenGeniSlackBotInstallStart>;
6112
7720
 
7721
+ export const SlackInstallationBindingState = z.enum(["active", "quarantined"]);
7722
+ export type SlackInstallationBindingState = z.infer<typeof SlackInstallationBindingState>;
7723
+
7724
+ export const SlackInstallationBinding = z.object({
7725
+ id: z.string().uuid(),
7726
+ accountId: z.string().uuid(),
7727
+ accountName: z.string().min(1),
7728
+ workspaceId: z.string().uuid(),
7729
+ workspaceName: z.string().min(1),
7730
+ connectionId: z.string().uuid(),
7731
+ connectionStatus: ConnectionStatus,
7732
+ connectionVersion: z.number().int().positive(),
7733
+ slackTeamId: z.string().min(1).max(64),
7734
+ slackTeamName: z.string().min(1).max(256),
7735
+ botId: z.string().min(1).max(64),
7736
+ botUserId: z.string().min(1).max(64),
7737
+ botDisplayName: z.literal("OpenGeni"),
7738
+ state: SlackInstallationBindingState,
7739
+ quarantineReason: z.string().min(1).nullable(),
7740
+ version: z.number().int().positive(),
7741
+ createdAt: z.string(),
7742
+ updatedAt: z.string(),
7743
+ });
7744
+ export type SlackInstallationBinding = z.infer<typeof SlackInstallationBinding>;
7745
+
7746
+ export const ListSlackInstallationBindingsResponse = z.object({
7747
+ bindings: z.array(SlackInstallationBinding),
7748
+ });
7749
+ export type ListSlackInstallationBindingsResponse = z.infer<
7750
+ typeof ListSlackInstallationBindingsResponse
7751
+ >;
7752
+
6113
7753
  export const UpdateConnectionRequest = z.object({
6114
7754
  providerDomain: z.string().min(1).optional(),
6115
7755
  subjectId: z.string().min(1).nullable().optional(),
@@ -6201,176 +7841,977 @@ export const CapabilitySource = z.enum([
6201
7841
  ]);
6202
7842
  export type CapabilitySource = z.infer<typeof CapabilitySource>;
6203
7843
 
6204
- export const CapabilityInstallationStatus = z.enum(["active", "disabled"]);
6205
- export type CapabilityInstallationStatus = z.infer<typeof CapabilityInstallationStatus>;
7844
+ export const CapabilityInstallationStatus = z.enum(["active", "disabled"]);
7845
+ export type CapabilityInstallationStatus = z.infer<typeof CapabilityInstallationStatus>;
7846
+
7847
+ export const CapabilityCatalogAuthKind = z.enum(["oauth2", "api_key", "none", "unknown"]);
7848
+ export type CapabilityCatalogAuthKind = z.infer<typeof CapabilityCatalogAuthKind>;
7849
+
7850
+ export const CapabilityCatalogTier = z.enum(["verified", "community"]);
7851
+ export type CapabilityCatalogTier = z.infer<typeof CapabilityCatalogTier>;
7852
+
7853
+ /**
7854
+ * User-facing lifecycle truth for the Capabilities control center. This is a
7855
+ * read-model projection only: each action is still executed by the owning
7856
+ * Plugin, Integration, Connection, Skill, or Pack domain.
7857
+ */
7858
+ export const CapabilityLifecycleStatus = z.enum([
7859
+ "available",
7860
+ "installed",
7861
+ "connected",
7862
+ "ready",
7863
+ "needs_attention",
7864
+ "unavailable",
7865
+ "managed",
7866
+ ]);
7867
+ export type CapabilityLifecycleStatus = z.infer<typeof CapabilityLifecycleStatus>;
7868
+
7869
+ export const CapabilityReadiness = z.enum(["ready", "setup_required", "attention", "unavailable"]);
7870
+ export type CapabilityReadiness = z.infer<typeof CapabilityReadiness>;
7871
+
7872
+ export const CapabilityAction = z.enum([
7873
+ "install",
7874
+ "connect",
7875
+ "configure",
7876
+ "update",
7877
+ "repair",
7878
+ "disconnect",
7879
+ "uninstall",
7880
+ "inspect",
7881
+ ]);
7882
+ export type CapabilityAction = z.infer<typeof CapabilityAction>;
7883
+
7884
+ export const CapabilityLifecycle = z.object({
7885
+ status: CapabilityLifecycleStatus,
7886
+ readiness: CapabilityReadiness,
7887
+ detail: z.string().nullable().default(null),
7888
+ managedBy: z.enum(["deployment", "platform", "workspace"]).nullable().default(null),
7889
+ });
7890
+ export type CapabilityLifecycle = z.infer<typeof CapabilityLifecycle>;
7891
+
7892
+ export const CapabilityRuntime = z.object({
7893
+ available: z.boolean().default(false),
7894
+ mcpServerId: z.string().min(1).optional(),
7895
+ transport: z.string().min(1).optional(),
7896
+ notes: z.string().nullable().default(null),
7897
+ // Registry exposure provenance is server-derived and contains no endpoint or
7898
+ // credential material.
7899
+ catalogTrust: z
7900
+ .object({
7901
+ state: z.enum(["trusted", "legacy_active", "unverified"]),
7902
+ reason: z.enum([
7903
+ "trusted_source",
7904
+ "verified_probe",
7905
+ "active_installation_compatibility",
7906
+ "missing_verification",
7907
+ ]),
7908
+ })
7909
+ .optional(),
7910
+ });
7911
+ export type CapabilityRuntime = z.infer<typeof CapabilityRuntime>;
7912
+
7913
+ export const CapabilityCatalogItem = z.object({
7914
+ id: z.string().min(1),
7915
+ accountId: z.string().uuid().optional(),
7916
+ workspaceId: z.string().uuid().optional(),
7917
+ kind: CapabilityKind,
7918
+ source: CapabilitySource,
7919
+ name: z.string().min(1),
7920
+ description: z.string().nullable().default(null),
7921
+ category: z.string().min(1).default("custom"),
7922
+ tags: z.array(z.string().min(1)).default([]),
7923
+ homepageUrl: z.string().url().nullable().default(null),
7924
+ endpointUrl: z.string().url().nullable().default(null),
7925
+ installUrl: z.string().url().nullable().default(null),
7926
+ authModel: z.string().min(1).nullable().default(null),
7927
+ providerDomain: z.string().min(1).nullable().default(null),
7928
+ surfaceType: z.string().min(1).nullable().default(null),
7929
+ transport: z.string().min(1).nullable().default(null),
7930
+ mcpUrl: z.string().url().nullable().default(null),
7931
+ authKind: CapabilityCatalogAuthKind.nullable().default(null),
7932
+ credentialFacts: z.array(z.record(z.string(), z.unknown())).default([]),
7933
+ tier: CapabilityCatalogTier.nullable().default(null),
7934
+ provenance: z.string().min(1).nullable().default(null),
7935
+ logoAssetPath: z.string().min(1).nullable().default(null),
7936
+ importBatchId: z.string().uuid().nullable().default(null),
7937
+ stale: z.boolean().default(false),
7938
+ staleAt: z.string().nullable().default(null),
7939
+ tools: z.array(ToolRef).default([]),
7940
+ runtime: CapabilityRuntime.default({ available: false, notes: null }),
7941
+ lifecycle: CapabilityLifecycle.default({
7942
+ status: "available",
7943
+ readiness: "setup_required",
7944
+ detail: null,
7945
+ managedBy: null,
7946
+ }),
7947
+ actions: z.array(CapabilityAction).default([]),
7948
+ /**
7949
+ * @deprecated Compatibility projection for existing clients. New surfaces
7950
+ * must use lifecycle and actions rather than treating every type as an
7951
+ * enable/disable toggle.
7952
+ */
7953
+ enabled: z.boolean().default(false),
7954
+ /** @deprecated Compatibility explanation paired with enabled. */
7955
+ enabledReason: z.string().nullable().default(null),
7956
+ // The non-secret connection binding stored with an enabled installation.
7957
+ // Workspace refs retain an exact row id. Subject refs deliberately omit it:
7958
+ // each caller resolves their own visible row by provider/kind at runtime.
7959
+ connectionRef: z
7960
+ .object({
7961
+ connectionId: z.string().min(1).optional(),
7962
+ providerDomain: z.string().min(1),
7963
+ kind: z.string().min(1),
7964
+ subjectScope: z.enum(["workspace", "subject"]).optional(),
7965
+ })
7966
+ .nullable()
7967
+ .default(null),
7968
+ metadata: z.record(z.string(), z.unknown()).default({}),
7969
+ createdAt: z.string().optional(),
7970
+ updatedAt: z.string().optional(),
7971
+ });
7972
+ export type CapabilityCatalogItem = z.infer<typeof CapabilityCatalogItem>;
7973
+
7974
+ /**
7975
+ * Shared trust gate for catalog visibility and runtime selection. Registry rows
7976
+ * remain durable for provenance and audit, but only a reviewed real-MCP probe
7977
+ * with known authentication is exposable. API-key rows additionally need a
7978
+ * machine-actionable header contract; prose credential instructions are not a
7979
+ * runtime contract and must fail closed.
7980
+ */
7981
+ export function capabilityCatalogItemIsTrustedForExposure(
7982
+ item: Pick<CapabilityCatalogItem, "source" | "stale" | "authKind" | "metadata">,
7983
+ ): boolean {
7984
+ if (item.stale) return false;
7985
+ if (item.source !== "registry") return true;
7986
+ const probe = item.metadata.mcpProbe;
7987
+ if (!probe || typeof probe !== "object" || Array.isArray(probe)) return false;
7988
+ if ((probe as Record<string, unknown>).status !== "real") return false;
7989
+ if (item.authKind === null || item.authKind === "unknown") return false;
7990
+ if (item.authKind !== "api_key") return true;
7991
+ const contract = item.metadata.authContract;
7992
+ if (!contract || typeof contract !== "object" || Array.isArray(contract)) return false;
7993
+ const record = contract as Record<string, unknown>;
7994
+ return (
7995
+ typeof record.headerName === "string" &&
7996
+ /^[A-Za-z0-9!#$%&'*+.^_`|~-]+$/.test(record.headerName) &&
7997
+ typeof record.scheme === "string" &&
7998
+ record.scheme.trim().length > 0
7999
+ );
8000
+ }
8001
+
8002
+ export const CapabilityInstallation = z.object({
8003
+ id: z.string().uuid(),
8004
+ accountId: z.string().uuid(),
8005
+ workspaceId: z.string().uuid(),
8006
+ capabilityId: z.string().min(1),
8007
+ kind: CapabilityKind,
8008
+ status: CapabilityInstallationStatus,
8009
+ config: z.record(z.string(), z.unknown()),
8010
+ metadata: z.record(z.string(), z.unknown()),
8011
+ enabledAt: z.string(),
8012
+ updatedAt: z.string(),
8013
+ });
8014
+ export type CapabilityInstallation = z.infer<typeof CapabilityInstallation>;
8015
+
8016
+ export const CreateCapabilityCatalogItemRequest = z.object({
8017
+ id: z.string().min(1).optional(),
8018
+ kind: z.literal("mcp"),
8019
+ source: CapabilitySource.default("manual"),
8020
+ name: z.string().min(1),
8021
+ description: z.string().min(1).optional(),
8022
+ category: z.string().min(1).default("custom"),
8023
+ tags: z.array(z.string().min(1)).default([]),
8024
+ homepageUrl: z.string().url().optional(),
8025
+ endpointUrl: z.string().url().optional(),
8026
+ installUrl: z.string().url().optional(),
8027
+ authModel: z.string().min(1).optional(),
8028
+ metadata: z.record(z.string(), z.unknown()).default({}),
8029
+ });
8030
+ export type CreateCapabilityCatalogItemRequest = z.infer<typeof CreateCapabilityCatalogItemRequest>;
8031
+
8032
+ export const EnableCapabilityRequest = z.object({
8033
+ config: z.record(z.string(), z.unknown()).default({}),
8034
+ metadata: z.record(z.string(), z.unknown()).default({}),
8035
+ connectionRef: McpServerConnectionRef.optional(),
8036
+ /**
8037
+ * Credential headers for remote MCP capabilities (for example an
8038
+ * Authorization bearer token). Values are encrypted at rest with the
8039
+ * workspace-variable-sets key, injected only into the runtime MCP client,
8040
+ * and never returned by the API — responses expose header names only.
8041
+ */
8042
+ headers: z.record(z.string(), z.string()).default({}),
8043
+ });
8044
+ export type EnableCapabilityRequest = z.infer<typeof EnableCapabilityRequest>;
8045
+
8046
+ export const CapabilityCatalogResponse = z.object({
8047
+ items: z.array(CapabilityCatalogItem),
8048
+ installations: z.array(CapabilityInstallation),
8049
+ });
8050
+ export type CapabilityCatalogResponse = z.infer<typeof CapabilityCatalogResponse>;
8051
+
8052
+ export const DiscoverMcpCapabilitiesResponse = z.object({
8053
+ items: z.array(CapabilityCatalogItem),
8054
+ source: z.literal("official_mcp_registry"),
8055
+ sourceUrl: z.string().url(),
8056
+ });
8057
+ export type DiscoverMcpCapabilitiesResponse = z.infer<typeof DiscoverMcpCapabilitiesResponse>;
8058
+
8059
+ export const SkillImportSource = z.enum(["github", "skills_sh"]);
8060
+ export type SkillImportSource = z.infer<typeof SkillImportSource>;
8061
+
8062
+ export const SkillInstallationSource = z.enum(["library", "github", "skills_sh", "pack"]);
8063
+ export type SkillInstallationSource = z.infer<typeof SkillInstallationSource>;
8064
+
8065
+ export const PreviewSkillImportRequest = z.object({
8066
+ url: z.string().url().max(2048),
8067
+ });
8068
+ export type PreviewSkillImportRequest = z.infer<typeof PreviewSkillImportRequest>;
8069
+
8070
+ export const SkillImportFileSummary = z.object({
8071
+ path: z.string().min(1).max(1024),
8072
+ byteSize: z.number().int().nonnegative().max(262144),
8073
+ contentSha256: z.string().regex(/^[0-9a-f]{64}$/),
8074
+ });
8075
+ export type SkillImportFileSummary = z.infer<typeof SkillImportFileSummary>;
8076
+
8077
+ export const SkillImportPreview = z.object({
8078
+ source: SkillImportSource,
8079
+ sourceUrl: z.string().url(),
8080
+ repositoryUrl: z.string().url(),
8081
+ owner: z.string().min(1).max(100),
8082
+ repository: z.string().min(1).max(100),
8083
+ sourcePath: z.string().min(1).max(1024),
8084
+ sourceCommit: z.string().regex(/^[0-9a-f]{40,64}$/),
8085
+ name: z.string().regex(/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/),
8086
+ description: z.string().min(1).max(2048),
8087
+ contentSha256: z.string().regex(/^[0-9a-f]{64}$/),
8088
+ totalBytes: z.number().int().positive().max(1048576),
8089
+ files: z.array(SkillImportFileSummary).min(1).max(128),
8090
+ warnings: z.array(z.string().min(1).max(500)).max(32).default([]),
8091
+ installed: z.boolean().default(false),
8092
+ installationVersion: z.number().int().positive().nullable().default(null),
8093
+ });
8094
+ export type SkillImportPreview = z.infer<typeof SkillImportPreview>;
8095
+
8096
+ export const InstallSkillRequest = z.object({
8097
+ url: z.string().url().max(2048),
8098
+ expectedSourceCommit: z.string().regex(/^[0-9a-f]{40,64}$/),
8099
+ expectedContentSha256: z.string().regex(/^[0-9a-f]{64}$/),
8100
+ expectedInstallationVersion: z.number().int().positive().optional(),
8101
+ });
8102
+ export type InstallSkillRequest = z.infer<typeof InstallSkillRequest>;
8103
+
8104
+ export const InstallLibrarySkillRequest = z
8105
+ .object({
8106
+ expectedVersion: z.string().min(1).max(96),
8107
+ expectedContentSha256: z.string().regex(/^[0-9a-f]{64}$/),
8108
+ expectedInstallationVersion: z.number().int().positive().optional(),
8109
+ })
8110
+ .strict();
8111
+ export type InstallLibrarySkillRequest = z.infer<typeof InstallLibrarySkillRequest>;
8112
+
8113
+ export const InstalledSkill = z.object({
8114
+ capabilityId: z.string().min(1),
8115
+ pluginId: z.string().uuid(),
8116
+ pluginVersionId: z.string().uuid(),
8117
+ facetId: z.string().uuid(),
8118
+ pluginInstallationId: z.string().uuid(),
8119
+ facetInstallationId: z.string().uuid(),
8120
+ installationVersion: z.number().int().positive(),
8121
+ source: SkillInstallationSource,
8122
+ version: z.string().min(1).max(96),
8123
+ sourceUrl: z.string().url(),
8124
+ sourceCommit: z.string().regex(/^[0-9a-f]{40,64}$/),
8125
+ contentSha256: z.string().regex(/^[0-9a-f]{64}$/),
8126
+ name: z.string(),
8127
+ status: z.literal("installed"),
8128
+ });
8129
+ export type InstalledSkill = z.infer<typeof InstalledSkill>;
8130
+
8131
+ export const CapabilityComponentOwner = z.object({
8132
+ kind: z.enum(["direct", "plugin", "pack", "migration"]),
8133
+ id: z.string().min(1).max(512),
8134
+ removable: z.boolean(),
8135
+ });
8136
+ export type CapabilityComponentOwner = z.infer<typeof CapabilityComponentOwner>;
8137
+
8138
+ export const InstalledSkillSummary = z
8139
+ .object({
8140
+ capabilityId: z.string().min(1),
8141
+ pluginKey: z.string().min(1).max(200),
8142
+ installationVersion: z.number().int().positive(),
8143
+ name: z.string().min(1).max(200),
8144
+ description: z.string().max(4000),
8145
+ category: z.string().min(1).max(100),
8146
+ tags: z.array(z.string().min(1).max(100)).max(64),
8147
+ provenance: z.string().min(1).max(4000),
8148
+ source: SkillInstallationSource,
8149
+ version: z.string().min(1).max(96),
8150
+ sourceUrl: z.string().url().max(2048),
8151
+ repositoryUrl: z.string().url().max(2048),
8152
+ sourceCommit: z.string().regex(/^[0-9a-f]{40,64}$/),
8153
+ sourcePath: z.string().min(1).max(1024),
8154
+ contentSha256: z.string().regex(/^[0-9a-f]{64}$/),
8155
+ fileCount: z.number().int().positive().max(128),
8156
+ totalBytes: z.number().int().positive().max(1048576),
8157
+ license: z.string().min(1).max(200).nullable(),
8158
+ installedAt: z.string().datetime({ offset: true }),
8159
+ updatedAt: z.string().datetime({ offset: true }),
8160
+ owners: z.array(CapabilityComponentOwner).min(1),
8161
+ })
8162
+ .strict();
8163
+ export type InstalledSkillSummary = z.infer<typeof InstalledSkillSummary>;
8164
+
8165
+ export const ListInstalledSkillsResponse = z
8166
+ .object({ skills: z.array(InstalledSkillSummary).max(1000) })
8167
+ .strict();
8168
+ export type ListInstalledSkillsResponse = z.infer<typeof ListInstalledSkillsResponse>;
8169
+
8170
+ export const SkillUninstallPreview = z.object({
8171
+ capabilityId: z.string().min(1),
8172
+ installed: z.boolean(),
8173
+ installationVersion: z.number().int().positive().nullable(),
8174
+ directOwner: CapabilityComponentOwner.nullable(),
8175
+ remainingOwners: z.array(CapabilityComponentOwner),
8176
+ removesRuntimeSkill: z.boolean(),
8177
+ });
8178
+ export type SkillUninstallPreview = z.infer<typeof SkillUninstallPreview>;
8179
+
8180
+ export const UninstallSkillRequest = z.object({
8181
+ expectedInstallationVersion: z.number().int().positive(),
8182
+ });
8183
+ export type UninstallSkillRequest = z.infer<typeof UninstallSkillRequest>;
8184
+
8185
+ export const UninstallSkillResult = z.object({
8186
+ capabilityId: z.string().min(1),
8187
+ status: z.enum(["not_installed", "uninstalled", "retained_by_other_owners"]),
8188
+ remainingOwners: z.array(CapabilityComponentOwner),
8189
+ });
8190
+ export type UninstallSkillResult = z.infer<typeof UninstallSkillResult>;
8191
+
8192
+ export const ApiIntegrationProtocol = z.enum(["openapi", "graphql"]);
8193
+ export type ApiIntegrationProtocol = z.infer<typeof ApiIntegrationProtocol>;
8194
+
8195
+ export const IntegrationDefinitionProvenance = z.enum(["curated", "workspace"]);
8196
+ export type IntegrationDefinitionProvenance = z.infer<typeof IntegrationDefinitionProvenance>;
8197
+
8198
+ export const IntegrationFacetKey = z
8199
+ .string()
8200
+ .min(1)
8201
+ .max(200)
8202
+ .regex(/^[a-z0-9](?:[a-z0-9._/-]*[a-z0-9])?$/);
8203
+ export type IntegrationFacetKey = z.infer<typeof IntegrationFacetKey>;
8204
+
8205
+ export const IntegrationFacetKind = z.enum([
8206
+ "tools",
8207
+ "knowledge_source",
8208
+ "inbound_trigger",
8209
+ "delivery_destination",
8210
+ "identity_link",
8211
+ ]);
8212
+ export type IntegrationFacetKind = z.infer<typeof IntegrationFacetKind>;
8213
+
8214
+ export const IntegrationFacetStatus = z.enum(["active", "paused", "needs_attention", "disabled"]);
8215
+ export type IntegrationFacetStatus = z.infer<typeof IntegrationFacetStatus>;
8216
+
8217
+ const IntegrationFacetJsonObject = z.record(z.string(), z.unknown()).superRefine((value, ctx) => {
8218
+ let serialized: string;
8219
+ try {
8220
+ serialized = JSON.stringify(value);
8221
+ } catch {
8222
+ ctx.addIssue({ code: "custom", message: "must be JSON serializable" });
8223
+ return;
8224
+ }
8225
+ if (new TextEncoder().encode(serialized).byteLength > 131_072) {
8226
+ ctx.addIssue({
8227
+ code: "custom",
8228
+ message: "must not exceed 131072 UTF-8 bytes",
8229
+ });
8230
+ }
8231
+ });
8232
+
8233
+ export const IntegrationFacetDefinitionSummary = z
8234
+ .object({
8235
+ facetKey: IntegrationFacetKey,
8236
+ kind: IntegrationFacetKind.exclude(["tools"]),
8237
+ configSchema: IntegrationFacetJsonObject,
8238
+ capabilities: IntegrationFacetJsonObject,
8239
+ })
8240
+ .strict();
8241
+ export type IntegrationFacetDefinitionSummary = z.infer<typeof IntegrationFacetDefinitionSummary>;
8242
+
8243
+ export const IntegrationDefinitionSummary = z
8244
+ .object({
8245
+ id: z.string().min(1).max(128),
8246
+ name: z.string().min(1).max(200),
8247
+ summary: z.string().min(1).max(1000),
8248
+ protocol: z.literal("openapi"),
8249
+ provider: z
8250
+ .object({
8251
+ id: z.enum(["google", "microsoft"]),
8252
+ domain: z.string().min(1).max(253),
8253
+ })
8254
+ .strict(),
8255
+ authentication: z
8256
+ .object({
8257
+ kind: z.literal("oauth2"),
8258
+ scopes: z.array(z.string().min(1).max(1024)).max(256),
8259
+ })
8260
+ .strict(),
8261
+ facets: z.array(IntegrationFacetDefinitionSummary).max(128),
8262
+ })
8263
+ .strict();
8264
+ export type IntegrationDefinitionSummary = z.infer<typeof IntegrationDefinitionSummary>;
8265
+
8266
+ export const ListIntegrationDefinitionsResponse = z
8267
+ .object({ definitions: z.array(IntegrationDefinitionSummary).max(128) })
8268
+ .strict();
8269
+ export type ListIntegrationDefinitionsResponse = z.infer<typeof ListIntegrationDefinitionsResponse>;
8270
+
8271
+ export const IntegrationInstanceKey = z
8272
+ .string()
8273
+ .min(1)
8274
+ .max(128)
8275
+ .regex(/^[a-z0-9](?:[a-z0-9._/-]*[a-z0-9])?$/);
8276
+ export type IntegrationInstanceKey = z.infer<typeof IntegrationInstanceKey>;
8277
+
8278
+ export const IntegrationFacetBindingSummary = z
8279
+ .object({
8280
+ id: z.string().uuid(),
8281
+ facetKey: IntegrationFacetKey,
8282
+ kind: IntegrationFacetKind.exclude(["tools"]),
8283
+ bindingKey: IntegrationInstanceKey,
8284
+ displayName: z.string().min(1).max(200),
8285
+ connectionId: z.string().uuid().nullable(),
8286
+ status: IntegrationFacetStatus,
8287
+ config: IntegrationFacetJsonObject,
8288
+ version: z.number().int().positive(),
8289
+ hasCursor: z.boolean(),
8290
+ lastSuccessAt: z.string().datetime({ offset: true }).nullable(),
8291
+ lastErrorCode: z.string().min(1).max(120).nullable(),
8292
+ createdAt: z.string().datetime({ offset: true }),
8293
+ updatedAt: z.string().datetime({ offset: true }),
8294
+ })
8295
+ .strict();
8296
+ export type IntegrationFacetBindingSummary = z.infer<typeof IntegrationFacetBindingSummary>;
8297
+
8298
+ export const IntegrationInstanceFacetsResponse = z
8299
+ .object({
8300
+ capabilityId: z.string().min(1).max(512),
8301
+ instanceKey: IntegrationInstanceKey,
8302
+ providerDomain: z.string().min(1).max(253),
8303
+ connectionId: z.string().uuid().nullable(),
8304
+ facets: z
8305
+ .array(
8306
+ z
8307
+ .object({
8308
+ definition: IntegrationFacetDefinitionSummary,
8309
+ binding: IntegrationFacetBindingSummary.nullable(),
8310
+ })
8311
+ .strict(),
8312
+ )
8313
+ .max(128),
8314
+ })
8315
+ .strict();
8316
+ export type IntegrationInstanceFacetsResponse = z.infer<typeof IntegrationInstanceFacetsResponse>;
8317
+
8318
+ export const UpsertIntegrationFacetRequest = z
8319
+ .object({
8320
+ displayName: z.string().min(1).max(200),
8321
+ config: IntegrationFacetJsonObject.default({}),
8322
+ expectedVersion: z.number().int().positive().optional(),
8323
+ idempotencyKey: z.string().uuid(),
8324
+ })
8325
+ .strict();
8326
+ export type UpsertIntegrationFacetRequest = z.infer<typeof UpsertIntegrationFacetRequest>;
8327
+
8328
+ export const MutateIntegrationFacetRequest = z
8329
+ .object({
8330
+ expectedVersion: z.number().int().positive(),
8331
+ idempotencyKey: z.string().uuid(),
8332
+ })
8333
+ .strict();
8334
+ export type MutateIntegrationFacetRequest = z.infer<typeof MutateIntegrationFacetRequest>;
8335
+
8336
+ export const IntegrationFacetMutationResult = z
8337
+ .object({
8338
+ capabilityId: z.string().min(1).max(512),
8339
+ instanceKey: IntegrationInstanceKey,
8340
+ facetKey: IntegrationFacetKey,
8341
+ status: z.enum(["configured", "paused", "active"]),
8342
+ binding: IntegrationFacetBindingSummary,
8343
+ })
8344
+ .strict();
8345
+ export type IntegrationFacetMutationResult = z.infer<typeof IntegrationFacetMutationResult>;
8346
+
8347
+ export const IntegrationFacetRemovalResult = z
8348
+ .object({
8349
+ capabilityId: z.string().min(1).max(512),
8350
+ instanceKey: IntegrationInstanceKey,
8351
+ facetKey: IntegrationFacetKey,
8352
+ status: z.enum(["not_configured", "removed", "retained_by_other_owners"]),
8353
+ binding: IntegrationFacetBindingSummary.nullable(),
8354
+ remainingOwners: z.array(CapabilityComponentOwner),
8355
+ })
8356
+ .strict();
8357
+ export type IntegrationFacetRemovalResult = z.infer<typeof IntegrationFacetRemovalResult>;
8358
+
8359
+ export const IntegrationSource = z.discriminatedUnion("kind", [
8360
+ z
8361
+ .object({
8362
+ kind: z.literal("definition"),
8363
+ definitionId: z.string().min(1).max(128),
8364
+ })
8365
+ .strict(),
8366
+ z
8367
+ .object({
8368
+ kind: z.literal("openapi"),
8369
+ url: z.string().url().max(2048),
8370
+ baseUrl: z.string().url().max(2048).optional(),
8371
+ })
8372
+ .strict(),
8373
+ z
8374
+ .object({
8375
+ kind: z.literal("graphql"),
8376
+ endpoint: z.string().url().max(2048),
8377
+ name: z.string().min(1).max(200).optional(),
8378
+ })
8379
+ .strict(),
8380
+ z
8381
+ .object({
8382
+ kind: z.literal("auto"),
8383
+ url: z.string().url().max(2048),
8384
+ baseUrl: z.string().url().max(2048).optional(),
8385
+ })
8386
+ .strict(),
8387
+ ]);
8388
+ export type IntegrationSource = z.infer<typeof IntegrationSource>;
8389
+
8390
+ export const PreviewApiIntegrationRequest = z
8391
+ .object({
8392
+ source: IntegrationSource,
8393
+ connectionId: z.string().uuid().optional(),
8394
+ ownership: ConnectionOwnership.optional(),
8395
+ })
8396
+ .strict();
8397
+ export type PreviewApiIntegrationRequest = z.infer<typeof PreviewApiIntegrationRequest>;
8398
+
8399
+ export const ApiIntegrationOAuthStartRequest = z
8400
+ .object({
8401
+ definitionId: z.string().min(1).max(128),
8402
+ ownership: ConnectionOwnership.optional(),
8403
+ connectionId: z.string().uuid().optional(),
8404
+ returnPath: z.string().min(1).max(2048).optional(),
8405
+ })
8406
+ .strict();
8407
+ export type ApiIntegrationOAuthStartRequest = z.infer<typeof ApiIntegrationOAuthStartRequest>;
8408
+
8409
+ export const API_INTEGRATION_OAUTH_CREDENTIAL_ROLE = "api_integration_oauth" as const;
8410
+
8411
+ export const ApiIntegrationOAuthConnectionMetadata = z
8412
+ .object({
8413
+ credentialRole: z.literal(API_INTEGRATION_OAUTH_CREDENTIAL_ROLE),
8414
+ providerFamily: z.enum(["google", "microsoft"]),
8415
+ providerPrincipalId: z.string().min(1).max(512),
8416
+ providerEmail: z.string().min(1).max(512).nullable(),
8417
+ providerDisplayName: z.string().min(1).max(512).nullable(),
8418
+ authorizedDefinitionIds: z.array(z.string().min(1).max(128)).min(1).max(32),
8419
+ verifiedAt: z.string().datetime({ offset: true }),
8420
+ })
8421
+ .passthrough();
8422
+ export type ApiIntegrationOAuthConnectionMetadata = z.infer<
8423
+ typeof ApiIntegrationOAuthConnectionMetadata
8424
+ >;
8425
+
8426
+ export const ApiIntegrationAuthPreview = z.discriminatedUnion("kind", [
8427
+ z.object({ kind: z.literal("none") }).strict(),
8428
+ z
8429
+ .object({
8430
+ kind: z.literal("oauth2"),
8431
+ providerDomain: z.string().min(1).max(253),
8432
+ scopes: z.array(z.string().min(1).max(1024)).max(256),
8433
+ })
8434
+ .strict(),
8435
+ z
8436
+ .object({
8437
+ kind: z.literal("api_key"),
8438
+ providerDomain: z.string().min(1).max(253),
8439
+ carrier: z.enum(["header", "query", "cookie"]),
8440
+ name: z.string().min(1).max(256),
8441
+ })
8442
+ .strict(),
8443
+ z
8444
+ .object({
8445
+ kind: z.literal("http"),
8446
+ providerDomain: z.string().min(1).max(253),
8447
+ scheme: z.string().min(1).max(64),
8448
+ })
8449
+ .strict(),
8450
+ ]);
8451
+ export type ApiIntegrationAuthPreview = z.infer<typeof ApiIntegrationAuthPreview>;
8452
+
8453
+ export const ApiIntegrationToolPreview = z
8454
+ .object({
8455
+ id: z.string().min(1).max(200),
8456
+ operationKey: z.string().min(1).max(512),
8457
+ name: z.string().min(1).max(200),
8458
+ description: z.string().max(4000),
8459
+ safety: z.enum(["read", "write", "destructive"]),
8460
+ approvalMode: z.enum(["never", "ask"]),
8461
+ deprecated: z.boolean(),
8462
+ })
8463
+ .strict();
8464
+ export type ApiIntegrationToolPreview = z.infer<typeof ApiIntegrationToolPreview>;
8465
+
8466
+ export const ApiIntegrationPreview = z
8467
+ .object({
8468
+ source: IntegrationSource,
8469
+ definitionId: z.string().min(1).max(200),
8470
+ definitionProvenance: IntegrationDefinitionProvenance,
8471
+ protocol: ApiIntegrationProtocol,
8472
+ capabilityId: z.string().min(1).max(512),
8473
+ pluginKey: z.string().min(1).max(200),
8474
+ serverId: SessionMcpServerId,
8475
+ name: z.string().min(1).max(200),
8476
+ description: z.string().max(4000).nullable(),
8477
+ provider: z.string().min(1).max(128).nullable(),
8478
+ providerDomain: z.string().min(1).max(253),
8479
+ baseUrl: z.string().url().max(2048),
8480
+ sourceUrl: z.string().url().max(2048).nullable(),
8481
+ revisionId: z.string().min(1).max(96),
8482
+ contentSha256: z.string().regex(/^[0-9a-f]{64}$/),
8483
+ auth: ApiIntegrationAuthPreview,
8484
+ connectionId: z.string().uuid().nullable(),
8485
+ connectionOwnership: ConnectionOwnership.nullable(),
8486
+ tools: z.array(ApiIntegrationToolPreview).min(1).max(2000),
8487
+ warnings: z.array(z.string().min(1).max(500)).max(32).default([]),
8488
+ })
8489
+ .strict();
8490
+ export type ApiIntegrationPreview = z.infer<typeof ApiIntegrationPreview>;
8491
+
8492
+ export const InstallApiIntegrationRequest = z
8493
+ .object({
8494
+ source: IntegrationSource,
8495
+ expectedRevisionId: z.string().min(1).max(96),
8496
+ expectedContentSha256: z.string().regex(/^[0-9a-f]{64}$/),
8497
+ connectionId: z.string().uuid().optional(),
8498
+ ownership: ConnectionOwnership.optional(),
8499
+ instanceKey: IntegrationInstanceKey.optional(),
8500
+ displayName: z.string().min(1).max(200).optional(),
8501
+ expectedInstanceVersion: z.number().int().positive().optional(),
8502
+ allowedTools: z.array(z.string().min(1).max(200)).max(2000).optional(),
8503
+ })
8504
+ .strict();
8505
+ export type InstallApiIntegrationRequest = z.infer<typeof InstallApiIntegrationRequest>;
8506
+
8507
+ export const InstalledApiIntegration = z
8508
+ .object({
8509
+ capabilityId: z.string().min(1),
8510
+ pluginId: z.string().uuid(),
8511
+ pluginVersionId: z.string().uuid(),
8512
+ integrationFacetId: z.string().uuid(),
8513
+ apiFacetId: z.string().uuid(),
8514
+ pluginInstallationId: z.string().uuid(),
8515
+ integrationFacetInstallationId: z.string().uuid(),
8516
+ apiFacetInstallationId: z.string().uuid(),
8517
+ installationVersion: z.number().int().positive(),
8518
+ instanceId: z.string().uuid(),
8519
+ instanceKey: IntegrationInstanceKey,
8520
+ displayName: z.string().min(1).max(200),
8521
+ instanceVersion: z.number().int().positive(),
8522
+ revisionId: z.string().min(1),
8523
+ serverId: SessionMcpServerId,
8524
+ status: z.literal("installed"),
8525
+ })
8526
+ .strict();
8527
+ export type InstalledApiIntegration = z.infer<typeof InstalledApiIntegration>;
8528
+
8529
+ export const ApiIntegrationInstallationSummary = z
8530
+ .object({
8531
+ capabilityId: z.string().min(1),
8532
+ pluginKey: z.string().min(1),
8533
+ installationVersion: z.number().int().positive(),
8534
+ instanceId: z.string().uuid(),
8535
+ instanceKey: IntegrationInstanceKey,
8536
+ displayName: z.string().min(1).max(200),
8537
+ instanceVersion: z.number().int().positive(),
8538
+ serverId: SessionMcpServerId,
8539
+ name: z.string().min(1),
8540
+ description: z.string().nullable(),
8541
+ protocol: ApiIntegrationProtocol,
8542
+ definitionId: z.string().min(1).max(200),
8543
+ definitionProvenance: IntegrationDefinitionProvenance,
8544
+ providerDomain: z.string().min(1),
8545
+ baseUrl: z.string().url(),
8546
+ sourceUrl: z.string().url().nullable(),
8547
+ connected: z.boolean(),
8548
+ requiresConnection: z.boolean(),
8549
+ connectionId: z.string().uuid().nullable(),
8550
+ ownership: z.enum(["workspace", "personal", "none"]),
8551
+ allowedTools: z.array(z.string().min(1).max(200)).max(2000),
8552
+ toolCount: z.number().int().nonnegative(),
8553
+ approvalRequiredToolCount: z.number().int().nonnegative(),
8554
+ revisionId: z.string().min(1),
8555
+ contentSha256: z.string().regex(/^[0-9a-f]{64}$/),
8556
+ })
8557
+ .strict();
8558
+ export type ApiIntegrationInstallationSummary = z.infer<typeof ApiIntegrationInstallationSummary>;
8559
+
8560
+ export const ListApiIntegrationsResponse = z
8561
+ .object({ integrations: z.array(ApiIntegrationInstallationSummary) })
8562
+ .strict();
8563
+ export type ListApiIntegrationsResponse = z.infer<typeof ListApiIntegrationsResponse>;
8564
+
8565
+ export const ApiIntegrationUninstallPreview = z
8566
+ .object({
8567
+ capabilityId: z.string().min(1),
8568
+ instanceKey: IntegrationInstanceKey,
8569
+ displayName: z.string().min(1).max(200).nullable(),
8570
+ installed: z.boolean(),
8571
+ installationVersion: z.number().int().positive().nullable(),
8572
+ instanceVersion: z.number().int().positive().nullable(),
8573
+ directOwner: CapabilityComponentOwner.nullable(),
8574
+ remainingOwners: z.array(CapabilityComponentOwner),
8575
+ removesRuntimeIntegration: z.boolean(),
8576
+ removesDefinition: z.boolean(),
8577
+ })
8578
+ .strict();
8579
+ export type ApiIntegrationUninstallPreview = z.infer<typeof ApiIntegrationUninstallPreview>;
8580
+
8581
+ export const UninstallApiIntegrationRequest = z
8582
+ .object({
8583
+ expectedInstallationVersion: z.number().int().positive(),
8584
+ expectedInstanceVersion: z.number().int().positive(),
8585
+ })
8586
+ .strict();
8587
+ export type UninstallApiIntegrationRequest = z.infer<typeof UninstallApiIntegrationRequest>;
8588
+
8589
+ export const UninstallApiIntegrationResult = z
8590
+ .object({
8591
+ capabilityId: z.string().min(1),
8592
+ instanceKey: IntegrationInstanceKey,
8593
+ status: z.enum(["not_installed", "uninstalled", "retained_by_other_owners"]),
8594
+ remainingOwners: z.array(CapabilityComponentOwner),
8595
+ definitionStatus: z.enum(["retained", "disabled"]),
8596
+ })
8597
+ .strict();
8598
+ export type UninstallApiIntegrationResult = z.infer<typeof UninstallApiIntegrationResult>;
8599
+
8600
+ const PluginComponentKey = z
8601
+ .string()
8602
+ .min(1)
8603
+ .max(128)
8604
+ .regex(/^[a-z0-9](?:[a-z0-9._/-]*[a-z0-9])?$/);
8605
+
8606
+ export const PluginManifest = z
8607
+ .object({
8608
+ schemaVersion: z.literal(1),
8609
+ pluginKey: z
8610
+ .string()
8611
+ .min(1)
8612
+ .max(200)
8613
+ .regex(/^[a-z0-9](?:[a-z0-9._/-]*[a-z0-9])?$/),
8614
+ version: z.string().min(1).max(128),
8615
+ name: z.string().min(1).max(200),
8616
+ description: z.string().max(4000).default(""),
8617
+ category: z.string().min(1).max(100).default("plugins"),
8618
+ tags: z.array(z.string().min(1).max(100)).max(64).default([]),
8619
+ components: z
8620
+ .array(
8621
+ z.discriminatedUnion("kind", [
8622
+ z
8623
+ .object({
8624
+ key: PluginComponentKey,
8625
+ kind: z.literal("skill"),
8626
+ url: z.string().url(),
8627
+ })
8628
+ .strict(),
8629
+ z
8630
+ .object({
8631
+ key: PluginComponentKey,
8632
+ kind: z.literal("integration"),
8633
+ source: IntegrationSource,
8634
+ })
8635
+ .strict(),
8636
+ z
8637
+ .object({
8638
+ key: PluginComponentKey,
8639
+ kind: z.literal("mcp"),
8640
+ serverId: SessionMcpServerId,
8641
+ })
8642
+ .strict(),
8643
+ ]),
8644
+ )
8645
+ .min(1)
8646
+ .max(64),
8647
+ })
8648
+ .strict()
8649
+ .superRefine((manifest, context) => {
8650
+ const seen = new Set<string>();
8651
+ for (const [index, component] of manifest.components.entries()) {
8652
+ if (seen.has(component.key)) {
8653
+ context.addIssue({
8654
+ code: "custom",
8655
+ message: "Plugin component keys must be unique",
8656
+ path: ["components", index, "key"],
8657
+ });
8658
+ }
8659
+ seen.add(component.key);
8660
+ }
8661
+ });
8662
+ export type PluginManifest = z.infer<typeof PluginManifest>;
8663
+
8664
+ export const PluginComponentBinding = z
8665
+ .object({
8666
+ connectionId: z.string().uuid().optional(),
8667
+ instanceKey: IntegrationInstanceKey.optional(),
8668
+ displayName: z.string().min(1).max(200).optional(),
8669
+ })
8670
+ .strict();
8671
+ export type PluginComponentBinding = z.infer<typeof PluginComponentBinding>;
6206
8672
 
6207
- export const CapabilityCatalogAuthKind = z.enum(["oauth2", "api_key", "none", "unknown"]);
6208
- export type CapabilityCatalogAuthKind = z.infer<typeof CapabilityCatalogAuthKind>;
8673
+ export const PreviewPluginRequest = z
8674
+ .object({
8675
+ url: z.string().url().max(2048),
8676
+ bindings: z.record(PluginComponentKey, PluginComponentBinding).default({}),
8677
+ })
8678
+ .strict();
8679
+ export type PreviewPluginRequest = z.infer<typeof PreviewPluginRequest>;
6209
8680
 
6210
- export const CapabilityCatalogTier = z.enum(["verified", "community"]);
6211
- export type CapabilityCatalogTier = z.infer<typeof CapabilityCatalogTier>;
8681
+ export const PluginComponentPreview = z
8682
+ .object({
8683
+ key: PluginComponentKey,
8684
+ kind: z.enum(["skill", "integration", "mcp"]),
8685
+ name: z.string().min(1).max(200),
8686
+ capabilityId: z.string().min(1).max(512),
8687
+ digest: z.string().regex(/^[0-9a-f]{64}$/),
8688
+ connectionRequired: z.boolean(),
8689
+ connectionId: z.string().uuid().nullable(),
8690
+ instanceKey: IntegrationInstanceKey.nullable(),
8691
+ displayName: z.string().min(1).max(200).nullable(),
8692
+ facts: z.record(z.string(), z.unknown()),
8693
+ })
8694
+ .strict();
8695
+ export type PluginComponentPreview = z.infer<typeof PluginComponentPreview>;
6212
8696
 
6213
- export const CapabilityRuntime = z.object({
6214
- available: z.boolean().default(false),
6215
- mcpServerId: z.string().min(1).optional(),
6216
- transport: z.string().min(1).optional(),
6217
- notes: z.string().nullable().default(null),
6218
- // Registry exposure provenance is server-derived and contains no endpoint or
6219
- // credential material.
6220
- catalogTrust: z
6221
- .object({
6222
- state: z.enum(["trusted", "legacy_active", "unverified"]),
6223
- reason: z.enum([
6224
- "trusted_source",
6225
- "verified_probe",
6226
- "active_installation_compatibility",
6227
- "missing_verification",
6228
- ]),
6229
- })
6230
- .optional(),
6231
- });
6232
- export type CapabilityRuntime = z.infer<typeof CapabilityRuntime>;
8697
+ export const PluginUpdateDiff = z
8698
+ .object({
8699
+ fromVersion: z.string().nullable(),
8700
+ toVersion: z.string().min(1),
8701
+ added: z.array(PluginComponentKey),
8702
+ removed: z.array(PluginComponentKey),
8703
+ changed: z.array(PluginComponentKey),
8704
+ unchanged: z.array(PluginComponentKey),
8705
+ })
8706
+ .strict();
8707
+ export type PluginUpdateDiff = z.infer<typeof PluginUpdateDiff>;
6233
8708
 
6234
- export const CapabilityCatalogItem = z.object({
6235
- id: z.string().min(1),
6236
- accountId: z.string().uuid().optional(),
6237
- workspaceId: z.string().uuid().optional(),
6238
- kind: CapabilityKind,
6239
- source: CapabilitySource,
6240
- name: z.string().min(1),
6241
- description: z.string().nullable().default(null),
6242
- category: z.string().min(1).default("custom"),
6243
- tags: z.array(z.string().min(1)).default([]),
6244
- homepageUrl: z.string().url().nullable().default(null),
6245
- endpointUrl: z.string().url().nullable().default(null),
6246
- installUrl: z.string().url().nullable().default(null),
6247
- authModel: z.string().min(1).nullable().default(null),
6248
- providerDomain: z.string().min(1).nullable().default(null),
6249
- surfaceType: z.string().min(1).nullable().default(null),
6250
- transport: z.string().min(1).nullable().default(null),
6251
- mcpUrl: z.string().url().nullable().default(null),
6252
- authKind: CapabilityCatalogAuthKind.nullable().default(null),
6253
- credentialFacts: z.array(z.record(z.string(), z.unknown())).default([]),
6254
- tier: CapabilityCatalogTier.nullable().default(null),
6255
- provenance: z.string().min(1).nullable().default(null),
6256
- logoAssetPath: z.string().min(1).nullable().default(null),
6257
- importBatchId: z.string().uuid().nullable().default(null),
6258
- stale: z.boolean().default(false),
6259
- staleAt: z.string().nullable().default(null),
6260
- tools: z.array(ToolRef).default([]),
6261
- runtime: CapabilityRuntime.default({ available: false, notes: null }),
6262
- enabled: z.boolean().default(false),
6263
- enabledReason: z.string().nullable().default(null),
6264
- // The non-secret connection binding stored with an enabled installation.
6265
- // Workspace refs retain an exact row id. Subject refs deliberately omit it:
6266
- // each caller resolves their own visible row by provider/kind at runtime.
6267
- connectionRef: z
6268
- .object({
6269
- connectionId: z.string().min(1).optional(),
6270
- providerDomain: z.string().min(1),
6271
- kind: z.string().min(1),
6272
- subjectScope: z.enum(["workspace", "subject"]).optional(),
6273
- })
6274
- .nullable()
6275
- .default(null),
6276
- metadata: z.record(z.string(), z.unknown()).default({}),
6277
- createdAt: z.string().optional(),
6278
- updatedAt: z.string().optional(),
6279
- });
6280
- export type CapabilityCatalogItem = z.infer<typeof CapabilityCatalogItem>;
8709
+ export const PluginPreview = z
8710
+ .object({
8711
+ sourceUrl: z.string().url(),
8712
+ manifest: PluginManifest,
8713
+ manifestDigest: z.string().regex(/^[0-9a-f]{64}$/),
8714
+ installed: z.boolean(),
8715
+ installationVersion: z.number().int().positive().nullable(),
8716
+ components: z.array(PluginComponentPreview).min(1).max(64),
8717
+ diff: PluginUpdateDiff,
8718
+ })
8719
+ .strict();
8720
+ export type PluginPreview = z.infer<typeof PluginPreview>;
6281
8721
 
6282
- /**
6283
- * Shared trust gate for catalog visibility and runtime selection. Registry rows
6284
- * remain durable for provenance and audit, but only a reviewed real-MCP probe
6285
- * with known authentication is exposable. API-key rows additionally need a
6286
- * machine-actionable header contract; prose credential instructions are not a
6287
- * runtime contract and must fail closed.
6288
- */
6289
- export function capabilityCatalogItemIsTrustedForExposure(
6290
- item: Pick<CapabilityCatalogItem, "source" | "stale" | "authKind" | "metadata">,
6291
- ): boolean {
6292
- if (item.stale) return false;
6293
- if (item.source !== "registry") return true;
6294
- const probe = item.metadata.mcpProbe;
6295
- if (!probe || typeof probe !== "object" || Array.isArray(probe)) return false;
6296
- if ((probe as Record<string, unknown>).status !== "real") return false;
6297
- if (item.authKind === null || item.authKind === "unknown") return false;
6298
- if (item.authKind !== "api_key") return true;
6299
- const contract = item.metadata.authContract;
6300
- if (!contract || typeof contract !== "object" || Array.isArray(contract)) return false;
6301
- const record = contract as Record<string, unknown>;
6302
- return (
6303
- typeof record.headerName === "string" &&
6304
- /^[A-Za-z0-9!#$%&'*+.^_`|~-]+$/.test(record.headerName) &&
6305
- typeof record.scheme === "string" &&
6306
- record.scheme.trim().length > 0
6307
- );
6308
- }
8722
+ export const InstallPluginRequest = z
8723
+ .object({
8724
+ url: z.string().url().max(2048),
8725
+ expectedManifestDigest: z.string().regex(/^[0-9a-f]{64}$/),
8726
+ expectedComponents: z
8727
+ .array(
8728
+ z
8729
+ .object({
8730
+ key: PluginComponentKey,
8731
+ digest: z.string().regex(/^[0-9a-f]{64}$/),
8732
+ })
8733
+ .strict(),
8734
+ )
8735
+ .min(1)
8736
+ .max(64),
8737
+ bindings: z.record(PluginComponentKey, PluginComponentBinding).default({}),
8738
+ idempotencyKey: z.string().uuid(),
8739
+ expectedInstallationVersion: z.number().int().positive().optional(),
8740
+ })
8741
+ .strict();
8742
+ export type InstallPluginRequest = z.infer<typeof InstallPluginRequest>;
6309
8743
 
6310
- export const CapabilityInstallation = z.object({
6311
- id: z.string().uuid(),
6312
- accountId: z.string().uuid(),
6313
- workspaceId: z.string().uuid(),
6314
- capabilityId: z.string().min(1),
6315
- kind: CapabilityKind,
6316
- status: CapabilityInstallationStatus,
6317
- config: z.record(z.string(), z.unknown()),
6318
- metadata: z.record(z.string(), z.unknown()),
6319
- enabledAt: z.string(),
6320
- updatedAt: z.string(),
6321
- });
6322
- export type CapabilityInstallation = z.infer<typeof CapabilityInstallation>;
8744
+ export const InstalledPlugin = z
8745
+ .object({
8746
+ pluginKey: z.string().min(1),
8747
+ version: z.string().min(1),
8748
+ pluginId: z.string().uuid(),
8749
+ pluginVersionId: z.string().uuid(),
8750
+ pluginInstallationId: z.string().uuid(),
8751
+ installationVersion: z.number().int().positive(),
8752
+ componentCount: z.number().int().positive(),
8753
+ status: z.literal("installed"),
8754
+ })
8755
+ .strict();
8756
+ export type InstalledPlugin = z.infer<typeof InstalledPlugin>;
6323
8757
 
6324
- export const CreateCapabilityCatalogItemRequest = z.object({
6325
- id: z.string().min(1).optional(),
6326
- kind: CapabilityKind.exclude(["pack"]),
6327
- source: CapabilitySource.default("manual"),
6328
- name: z.string().min(1),
6329
- description: z.string().min(1).optional(),
6330
- category: z.string().min(1).default("custom"),
6331
- tags: z.array(z.string().min(1)).default([]),
6332
- homepageUrl: z.string().url().optional(),
6333
- endpointUrl: z.string().url().optional(),
6334
- installUrl: z.string().url().optional(),
6335
- authModel: z.string().min(1).optional(),
6336
- metadata: z.record(z.string(), z.unknown()).default({}),
6337
- });
6338
- export type CreateCapabilityCatalogItemRequest = z.infer<typeof CreateCapabilityCatalogItemRequest>;
8758
+ export const PluginInstallationSummary = z
8759
+ .object({
8760
+ pluginKey: z.string().min(1).max(200),
8761
+ version: z.string().min(1).max(128),
8762
+ name: z.string().min(1).max(200),
8763
+ description: z.string().max(4000),
8764
+ category: z.string().min(1).max(100),
8765
+ tags: z.array(z.string().min(1).max(100)).max(64),
8766
+ sourceUrl: z.string().url().max(2048).nullable(),
8767
+ manifestDigest: z.string().regex(/^[0-9a-f]{64}$/),
8768
+ installationVersion: z.number().int().positive(),
8769
+ componentCount: z.number().int().nonnegative().max(64),
8770
+ status: z.enum(["active", "needs_attention"]),
8771
+ installedAt: z.string().datetime({ offset: true }),
8772
+ updatedAt: z.string().datetime({ offset: true }),
8773
+ })
8774
+ .strict();
8775
+ export type PluginInstallationSummary = z.infer<typeof PluginInstallationSummary>;
6339
8776
 
6340
- export const EnableCapabilityRequest = withVariableSetIdAlias({
6341
- config: z.record(z.string(), z.unknown()).default({}),
6342
- metadata: z.record(z.string(), z.unknown()).default({}),
6343
- connectionRef: McpServerConnectionRef.optional(),
6344
- /**
6345
- * Credential headers for remote MCP capabilities (for example an
6346
- * Authorization bearer token). Values are encrypted at rest with the
6347
- * workspace-variable-sets key, injected only into the runtime MCP client,
6348
- * and never returned by the API — responses expose header names only.
6349
- */
6350
- headers: z.record(z.string(), z.string()).default({}),
6351
- /**
6352
- * Initial variableSet attachment for kind=pack capabilities. Mirrors the
6353
- * dedicated POST /packs/:id/enable body: required to enable an
6354
- * variableSet.required pack through the unified capability-enable path,
6355
- * optional otherwise. Ignored by non-pack capabilities.
6356
- */
6357
- variableSetId: z.string().uuid().optional(),
6358
- environmentId: z.string().uuid().optional(),
6359
- });
6360
- export type EnableCapabilityRequest = z.infer<typeof EnableCapabilityRequest>;
8777
+ export const ListInstalledPluginsResponse = z
8778
+ .object({ plugins: z.array(PluginInstallationSummary).max(1000) })
8779
+ .strict();
8780
+ export type ListInstalledPluginsResponse = z.infer<typeof ListInstalledPluginsResponse>;
6361
8781
 
6362
- export const CapabilityCatalogResponse = z.object({
6363
- items: z.array(CapabilityCatalogItem),
6364
- installations: z.array(CapabilityInstallation),
6365
- });
6366
- export type CapabilityCatalogResponse = z.infer<typeof CapabilityCatalogResponse>;
8782
+ export const PluginUninstallPreview = z
8783
+ .object({
8784
+ pluginKey: z.string().min(1),
8785
+ installed: z.boolean(),
8786
+ version: z.string().nullable(),
8787
+ installationVersion: z.number().int().positive().nullable(),
8788
+ components: z.array(
8789
+ z.object({
8790
+ capabilityId: z.string().min(1),
8791
+ kind: z.enum(["skill", "integration", "mcp"]),
8792
+ retainedByOtherOwners: z.boolean(),
8793
+ }),
8794
+ ),
8795
+ })
8796
+ .strict();
8797
+ export type PluginUninstallPreview = z.infer<typeof PluginUninstallPreview>;
6367
8798
 
6368
- export const DiscoverMcpCapabilitiesResponse = z.object({
6369
- items: z.array(CapabilityCatalogItem),
6370
- source: z.literal("official_mcp_registry"),
6371
- sourceUrl: z.string().url(),
6372
- });
6373
- export type DiscoverMcpCapabilitiesResponse = z.infer<typeof DiscoverMcpCapabilitiesResponse>;
8799
+ export const UninstallPluginRequest = z
8800
+ .object({
8801
+ expectedInstallationVersion: z.number().int().positive(),
8802
+ idempotencyKey: z.string().uuid(),
8803
+ })
8804
+ .strict();
8805
+ export type UninstallPluginRequest = z.infer<typeof UninstallPluginRequest>;
8806
+
8807
+ export const UninstallPluginResult = z
8808
+ .object({
8809
+ pluginKey: z.string().min(1),
8810
+ status: z.enum(["not_installed", "uninstalled"]),
8811
+ retainedComponents: z.array(z.string().min(1)),
8812
+ })
8813
+ .strict();
8814
+ export type UninstallPluginResult = z.infer<typeof UninstallPluginResult>;
6374
8815
 
6375
8816
  export const Session = z.object({
6376
8817
  id: z.string().uuid(),
@@ -6399,6 +8840,8 @@ export const Session = z.object({
6399
8840
  // the workspace runtime registry.
6400
8841
  effectiveToolPolicy: SessionEffectiveToolPolicy.optional(),
6401
8842
  metadata: z.record(z.string(), z.unknown()),
8843
+ /** Additive public tenancy projection; omitted by legacy/internal readers. */
8844
+ tenancy: SessionTenancyPublicProjection.optional(),
6402
8845
  /** Frozen creator fact used only for creation attribution/idempotent repair. */
6403
8846
  createdBy: TurnInitiator,
6404
8847
  createdByContext: TurnInitiatorContext,
@@ -6430,6 +8873,9 @@ export const Session = z.object({
6430
8873
  // Both null ⇒ a rig-less session (byte-for-byte today's behavior).
6431
8874
  rigId: z.string().uuid().nullable().default(null),
6432
8875
  rigVersionId: z.string().uuid().nullable().default(null),
8876
+ // Workspace channel this session is filed under (rail organization only;
8877
+ // a session tree is grouped by its ROOT session's channel). Null = unfiled.
8878
+ channelId: z.string().uuid().nullable().default(null),
6433
8879
  // Non-default first-party MCP token permissions (manager-style sessions);
6434
8880
  // null means the fixed worker default set.
6435
8881
  firstPartyMcpPermissions: z.array(Permission).nullable(),
@@ -6535,6 +8981,57 @@ export const SessionListResponse = z.object({
6535
8981
  });
6536
8982
  export type SessionListResponse = z.infer<typeof SessionListResponse>;
6537
8983
 
8984
+ /** Compact, bounded session projection for workspace agent-topology browsers. */
8985
+ export const AgentTopologySession = z.object({
8986
+ id: z.string().uuid(),
8987
+ title: z.string().nullable(),
8988
+ titleTruncated: z.boolean(),
8989
+ parentSessionId: z.string().uuid().nullable(),
8990
+ rootSessionId: z.string().uuid(),
8991
+ nestedAgentDepth: NestedAgentDepthValue,
8992
+ ancestorPath: z.array(
8993
+ z.object({
8994
+ id: z.string().uuid(),
8995
+ title: z.string().nullable(),
8996
+ titleTruncated: z.boolean(),
8997
+ }),
8998
+ ),
8999
+ status: SessionStatus,
9000
+ pause: z.object({
9001
+ state: z.enum(["active", "paused"]),
9002
+ additionalBlockerCount: z.number().int().nonnegative(),
9003
+ source: z
9004
+ .object({
9005
+ kind: z.enum(["session", "workspace"]),
9006
+ sessionId: z.string().uuid().optional(),
9007
+ displayName: z.string(),
9008
+ displayNameTruncated: z.boolean(),
9009
+ })
9010
+ .nullable(),
9011
+ }),
9012
+ children: z.object({
9013
+ directChildren: z.number().int().nonnegative(),
9014
+ totalDescendants: z.number().int().nonnegative(),
9015
+ runningDescendants: z.number().int().nonnegative(),
9016
+ queuedDescendants: z.number().int().nonnegative(),
9017
+ attentionDescendants: z.number().int().nonnegative(),
9018
+ pausedDescendants: z.number().int().nonnegative(),
9019
+ failedDescendants: z.number().int().nonnegative(),
9020
+ truncated: z.boolean(),
9021
+ }),
9022
+ createdAt: z.string(),
9023
+ updatedAt: z.string(),
9024
+ });
9025
+ export type AgentTopologySession = z.infer<typeof AgentTopologySession>;
9026
+
9027
+ export const AgentTopologyPageResponse = z.object({
9028
+ sessions: z.array(AgentTopologySession),
9029
+ total: z.number().int().nonnegative(),
9030
+ hasMore: z.boolean(),
9031
+ nextCursor: z.string().nullable(),
9032
+ });
9033
+ export type AgentTopologyPageResponse = z.infer<typeof AgentTopologyPageResponse>;
9034
+
6538
9035
  // Recursive: the TS type is declared first so the schema annotation can carry
6539
9036
  // the FULL recursive shape (a shallow annotation loses type information for
6540
9037
  // contracts consumers after one level of nesting).
@@ -6558,6 +9055,7 @@ export type SessionLineageResponse = z.infer<typeof SessionLineageResponse>;
6558
9055
 
6559
9056
  export const SessionEventType = z.enum([
6560
9057
  "session.created",
9058
+ "session.visibility.changed",
6561
9059
  // Defensive read/transport projection for a malformed or historically
6562
9060
  // oversized retained event envelope. The original row stays durable; this
6563
9061
  // explicit synthetic type prevents unbounded free-form envelope fields from
@@ -6608,6 +9106,9 @@ export const SessionEventType = z.enum([
6608
9106
  "artifact.created",
6609
9107
  "goal.set",
6610
9108
  "goal.updated",
9109
+ "goal.progress",
9110
+ "goal.rewrite.proposed",
9111
+ "goal.rewrite.rejected",
6611
9112
  "goal.completed",
6612
9113
  "goal.paused",
6613
9114
  "goal.resumed",
@@ -6811,6 +9312,9 @@ export const SESSION_EVENT_SEMANTIC_CLASS_TYPES = {
6811
9312
  "user.humanInputResponse",
6812
9313
  "goal.set",
6813
9314
  "goal.updated",
9315
+ "goal.progress",
9316
+ "goal.rewrite.proposed",
9317
+ "goal.rewrite.rejected",
6814
9318
  "goal.completed",
6815
9319
  "goal.paused",
6816
9320
  "goal.resumed",
@@ -6953,6 +9457,21 @@ export const ToolAuthNeededPayload = z.object({
6953
9457
  selectedResources: McpConnectionResourceScopes.optional(),
6954
9458
  authorizationUrl: z.string().url().optional(),
6955
9459
  subjectId: z.string().min(1).nullable().optional(),
9460
+ // A catalog recommendation is still a tool-level authorization condition:
9461
+ // the agent may describe and request it, but only the authenticated host UI
9462
+ // can start setup. Keeping this nested and optional preserves the established
9463
+ // auth-needed event for ordinary failed MCP calls.
9464
+ capability: z
9465
+ .object({
9466
+ id: z.string().min(1).max(512),
9467
+ name: z.string().min(1).max(256),
9468
+ kind: CapabilityKind,
9469
+ source: CapabilitySource,
9470
+ action: z.enum(["connect", "add_credentials", "enable"]),
9471
+ rationale: z.string().min(1).max(2000),
9472
+ requiredVariables: z.array(VariableSetVariableName).max(64).default([]),
9473
+ })
9474
+ .optional(),
6956
9475
  });
6957
9476
  export type ToolAuthNeededPayload = z.infer<typeof ToolAuthNeededPayload>;
6958
9477
 
@@ -7307,6 +9826,9 @@ export type GitStatusRequest = z.infer<typeof GitStatusRequest>;
7307
9826
  export const GitStatusResponse = z.object({
7308
9827
  isRepo: z.boolean(),
7309
9828
  head: z.string().nullable(), // branch name
9829
+ // Exact commit object identity. null for unborn/non-repositories; optional
9830
+ // only so older adapters and serialized captures remain readable.
9831
+ headOid: z.string().nullable().optional(),
7310
9832
  detached: z.boolean().default(false),
7311
9833
  upstream: z.string().nullable(),
7312
9834
  ahead: z.number().int().nonnegative().default(0),
@@ -7432,6 +9954,9 @@ export type WorkspaceCaptureFile = z.infer<typeof WorkspaceCaptureFile>;
7432
9954
  export const WorkspaceCaptureRepo = z.object({
7433
9955
  root: z.string(),
7434
9956
  head: z.string().nullable(),
9957
+ // Exact HEAD commit object identity. null for unborn repositories; optional
9958
+ // for workspace captures written before commit identity was retained.
9959
+ headOid: z.string().nullable().optional(),
7435
9960
  detached: z.boolean().default(false),
7436
9961
  upstream: z.string().nullable(),
7437
9962
  ahead: z.number().int().nonnegative().default(0),
@@ -8763,6 +11288,10 @@ export const CreateSessionRequest = withVariableSetIdAlias({
8763
11288
  // null ⇒ explicitly create a rig-less session; UUID ⇒ bind that exact rig.
8764
11289
  // An id that does not name a rig in the workspace is a 422.
8765
11290
  rigId: z.string().uuid().nullable().optional(),
11291
+ // The workspace channel to file this session under (rail organization only).
11292
+ // Omitted/null ⇒ unfiled (inbox). An id that does not name a channel in the
11293
+ // workspace is a 422.
11294
+ channelId: z.string().uuid().nullable().optional(),
8766
11295
  goal: GoalSpec.optional(),
8767
11296
  clientEventId: SessionOperationKey.optional(),
8768
11297
  // Workspace-scoped CREATE idempotency key: collapses concurrent/retried
@@ -9003,27 +11532,45 @@ export function approvalIdentifier(value: unknown): string | null {
9003
11532
  return String(candidate);
9004
11533
  }
9005
11534
 
11535
+ function requireMessageTextOrAnnotations(
11536
+ value: { text: string; annotations: readonly unknown[] },
11537
+ ctx: z.RefinementCtx,
11538
+ ): void {
11539
+ if (value.text.length === 0 && value.annotations.length === 0) {
11540
+ ctx.addIssue({
11541
+ code: z.ZodIssueCode.custom,
11542
+ path: ["text"],
11543
+ message: "message text or annotations are required",
11544
+ });
11545
+ }
11546
+ }
11547
+
11548
+ export const SessionUserMessagePayload = z
11549
+ .object({
11550
+ text: z.string().default(""),
11551
+ annotations: SubmittedTimelineAnnotations.default([]),
11552
+ // System-level host context for this exact turn only. Persisted on the
11553
+ // turn for retry/recovery, never copied into the visible user message.
11554
+ turnInstructions: z.string().trim().min(1).max(32768).optional(),
11555
+ resources: z.array(ResourceRef).default([]),
11556
+ model: z.string().min(1).optional(),
11557
+ reasoningEffort: ReasoningEffort.optional(),
11558
+ latencyMode: LatencyMode.optional(),
11559
+ controlEtag: z.string().min(1).optional(),
11560
+ expectedDraftRevision: z.number().int().nonnegative().optional(),
11561
+ // Header-value rotation only. URL/name/tool settings are immutable after
11562
+ // session create; persisted events expose metadata, never header values.
11563
+ mcpCredentialUpdates: z.array(SessionMcpCredentialUpdateInput).optional(),
11564
+ })
11565
+ .strict()
11566
+ .superRefine(requireMessageTextOrAnnotations);
11567
+ export type SessionUserMessagePayload = z.infer<typeof SessionUserMessagePayload>;
11568
+
9006
11569
  export const ClientSessionEvent = z.discriminatedUnion("type", [
9007
11570
  z.object({
9008
11571
  type: z.literal("user.message"),
9009
11572
  clientEventId: SessionOperationKey.optional(),
9010
- payload: z
9011
- .object({
9012
- text: z.string().min(1),
9013
- // System-level host context for this exact turn only. Persisted on the
9014
- // turn for retry/recovery, never copied into the visible user message.
9015
- turnInstructions: z.string().trim().min(1).max(32768).optional(),
9016
- resources: z.array(ResourceRef).default([]),
9017
- model: z.string().min(1).optional(),
9018
- reasoningEffort: ReasoningEffort.optional(),
9019
- latencyMode: LatencyMode.optional(),
9020
- controlEtag: z.string().min(1).optional(),
9021
- expectedDraftRevision: z.number().int().nonnegative().optional(),
9022
- // Header-value rotation only. URL/name/tool settings are immutable after
9023
- // session create; persisted events expose metadata, never header values.
9024
- mcpCredentialUpdates: z.array(SessionMcpCredentialUpdateInput).optional(),
9025
- })
9026
- .strict(),
11573
+ payload: SessionUserMessagePayload,
9027
11574
  }),
9028
11575
  z.object({
9029
11576
  type: z.literal("user.approvalDecision"),
@@ -9047,7 +11594,8 @@ export type ClientSessionEvent = z.infer<typeof ClientSessionEvent>;
9047
11594
 
9048
11595
  export const SteerSessionMessageRequest = z
9049
11596
  .object({
9050
- text: z.string().min(1),
11597
+ text: z.string().default(""),
11598
+ annotations: SubmittedTimelineAnnotations.default([]),
9051
11599
  // Same per-turn system-level context as a queued user.message.
9052
11600
  turnInstructions: z.string().trim().min(1).max(32768).optional(),
9053
11601
  resources: z.array(ResourceRef).default([]),
@@ -9059,7 +11607,8 @@ export const SteerSessionMessageRequest = z
9059
11607
  expectedDraftRevision: z.number().int().nonnegative().optional(),
9060
11608
  mcpCredentialUpdates: z.array(SessionMcpCredentialUpdateInput).optional(),
9061
11609
  })
9062
- .strict();
11610
+ .strict()
11611
+ .superRefine(requireMessageTextOrAnnotations);
9063
11612
  export type SteerSessionMessageRequest = z.infer<typeof SteerSessionMessageRequest>;
9064
11613
 
9065
11614
  export const SteerSessionMessageResponse = z.object({
@@ -9202,7 +11751,7 @@ export const SessionCapabilities = z.object({
9202
11751
  reason: CapabilityUnavailableReason.nullable(),
9203
11752
  }),
9204
11753
  Terminal: z.object({
9205
- transport: z.enum(["sse-events", "pty-ws"]).nullable(),
11754
+ transport: z.enum(["sse-events", "pty-ws", "relay-pty"]).nullable(),
9206
11755
  ptyCapable: z.boolean(),
9207
11756
  shell: z.string(),
9208
11757
  // The direct-to-provider ttyd PTY-over-websocket URL (pty-ws) resolved on the
@@ -9278,19 +11827,21 @@ export type SessionCapabilities = z.infer<typeof SessionCapabilities>;
9278
11827
  // POST .../viewers — acquire a viewer holder. An omitted viewerId mints a fresh
9279
11828
  // one (returned in the response, to carry through heartbeats + detach).
9280
11829
  //
9281
- // `desktop` declares intent to attach the UN-REDACTED pixel plane (noVNC). ONLY
9282
- // that plane carries the consent gate (the un-redacted/shared acknowledgment). A
9283
- // terminal-only warm attach (`desktop:false`, the default) needs NO consent — a
9284
- // shell is interactive by nature and the gate is the scoped tunnel URL + stream
9285
- // token so it warms the box and mints the pty-ws terminal cell WITHOUT a 409.
9286
- // Omitted defaults to `false` so a terminal-only client never trips the gate.
9287
- export const AttachViewerRequest = z.object({
11830
+ // Each optional plane flag declares exactly which live capability is being
11831
+ // acquired. The holder itself is shared liveness; credentials are minted only
11832
+ // for explicitly requested planes. `desktop` alone carries the un-redacted pixel
11833
+ // consent gate, `terminal` carries terminal:attach, and `files` carries
11834
+ // files:write. For compatibility, an entirely omitted plane set retains the old
11835
+ // terminal-only meaning; new clients always send all three flags explicitly.
11836
+ export const AttachViewerRequest = /* @__PURE__ */ z.object({
9288
11837
  viewerId: z.string().uuid().optional(),
9289
11838
  desktop: z.boolean().optional(),
11839
+ terminal: z.boolean().optional(),
11840
+ files: z.boolean().optional(),
9290
11841
  });
9291
11842
  export type AttachViewerRequest = z.infer<typeof AttachViewerRequest>;
9292
11843
 
9293
- export const ViewerHolder = z.object({
11844
+ export const ViewerHolder = /* @__PURE__ */ z.object({
9294
11845
  viewerId: z.string().uuid(),
9295
11846
  sandboxGroupId: z.string().uuid(),
9296
11847
  liveness: z.enum(["cold", "warming", "warm", "draining"]),
@@ -9306,6 +11857,21 @@ export const ViewerHolder = z.object({
9306
11857
  });
9307
11858
  export type ViewerHolder = z.infer<typeof ViewerHolder>;
9308
11859
 
11860
+ /** Exact POST /viewers response. Keep the credential cells in the contracts
11861
+ * package so API and zero-runtime SDK mirrors cannot silently drift. */
11862
+ export const AttachViewerResponse = /* @__PURE__ */ ViewerHolder.extend({
11863
+ streamToken: z.string().nullable(),
11864
+ streamExpiresAt: z.string().nullable(),
11865
+ resolution: z.tuple([z.number().int().positive(), z.number().int().positive()]).nullable(),
11866
+ transport: z.enum(["vnc-ws", "relay-frames"]).nullable(),
11867
+ client: z.enum(["novnc", "frames"]).nullable(),
11868
+ terminalUrl: z.string().nullable(),
11869
+ terminalToken: z.string().nullable(),
11870
+ terminalExpiresAt: z.string().nullable(),
11871
+ terminalTransport: z.enum(["pty-ws", "relay-pty"]).nullable(),
11872
+ });
11873
+ export type AttachViewerResponse = z.infer<typeof AttachViewerResponse>;
11874
+
9309
11875
  // POST .../stream-capabilities/acknowledge — record the calling principal's
9310
11876
  // acknowledgment of the un-redacted pixel plane. Reuses the acknowledgment
9311
11877
  // machinery — no new endpoint
@@ -9315,7 +11881,7 @@ export type ViewerHolder = z.infer<typeof ViewerHolder>;
9315
11881
  // session): the un-redacted desktop path returns 409 shared_acknowledgment_required
9316
11882
  // until a shared box is acknowledged WITH the shared-exposure consent. For a
9317
11883
  // solo box `acknowledgeShared` is irrelevant (the un-redacted ack alone gates).
9318
- export const AcknowledgeStreamRequest = z.object({
11884
+ export const AcknowledgeStreamRequest = /* @__PURE__ */ z.object({
9319
11885
  // The principal accepts that the desktop pixel plane is un-redacted (can show
9320
11886
  // cloud creds the agent cat's into a terminal). Always true to record consent;
9321
11887
  // present for self-documentation + a future explicit withdraw.
@@ -9326,7 +11892,7 @@ export const AcknowledgeStreamRequest = z.object({
9326
11892
  });
9327
11893
  export type AcknowledgeStreamRequest = z.infer<typeof AcknowledgeStreamRequest>;
9328
11894
 
9329
- export const AcknowledgeStreamResponse = z.object({
11895
+ export const AcknowledgeStreamResponse = /* @__PURE__ */ z.object({
9330
11896
  acknowledged: z.boolean(),
9331
11897
  acknowledgedShared: z.boolean(),
9332
11898
  });
@@ -9334,12 +11900,12 @@ export type AcknowledgeStreamResponse = z.infer<typeof AcknowledgeStreamResponse
9334
11900
 
9335
11901
  // POST .../viewers/:viewerId/heartbeat — refresh the holder TTL. Epoch-fenced:
9336
11902
  // a stale-epoch beat (a box re-established under a newer epoch) is rejected.
9337
- export const ViewerHeartbeatRequest = z.object({
11903
+ export const ViewerHeartbeatRequest = /* @__PURE__ */ z.object({
9338
11904
  leaseEpoch: z.number().int().nonnegative(),
9339
11905
  });
9340
11906
  export type ViewerHeartbeatRequest = z.infer<typeof ViewerHeartbeatRequest>;
9341
11907
 
9342
- export const ViewerHeartbeatResponse = z.object({
11908
+ export const ViewerHeartbeatResponse = /* @__PURE__ */ z.object({
9343
11909
  // false ⇒ the holder was reaped or the epoch is stale; the client re-attaches.
9344
11910
  alive: z.boolean(),
9345
11911
  });
@@ -9508,10 +12074,23 @@ export const RevokeEnrollmentResponse = z.object({
9508
12074
  lastSeenAt: z.string().datetime({ offset: true }).nullable(),
9509
12075
  revokedAt: z.string().datetime({ offset: true }).nullable(),
9510
12076
  code: z
9511
- .enum(["active_route", "active_commands", "active_lease", "recovery_pending", "not_selfhosted"])
12077
+ .enum([
12078
+ "active_route",
12079
+ "active_commands",
12080
+ "machine_home",
12081
+ "active_lease",
12082
+ "recovery_pending",
12083
+ "not_selfhosted",
12084
+ ])
9512
12085
  .nullable(),
9513
12086
  message: z.string(),
9514
12087
  action: z.string(),
12088
+ dependentSessions: z.array(
12089
+ z.object({
12090
+ id: z.string().uuid(),
12091
+ title: z.string().nullable(),
12092
+ }),
12093
+ ),
9515
12094
  });
9516
12095
  export type RevokeEnrollmentResponse = z.infer<typeof RevokeEnrollmentResponse>;
9517
12096
 
@@ -9829,7 +12408,7 @@ export const ModelCredentialSourceV1 =
9829
12408
  z
9830
12409
  .object({
9831
12410
  kind: z.literal("connected_subscription"),
9832
- provider: z.literal("codex"),
12411
+ provider: z.enum(["codex", "xai"]),
9833
12412
  })
9834
12413
  .strict(),
9835
12414
  z
@@ -10040,7 +12619,7 @@ export const ClientModel = /* @__PURE__ */ defineModelContractSchema(() =>
10040
12619
  provider: z.string(), // provider id
10041
12620
  providerLabel: z.string(),
10042
12621
  api: z.enum(["responses", "chat"]),
10043
- source: z.enum(["opengeni", "codex", "workspace_gateway"]).optional(),
12622
+ source: z.enum(["opengeni", "codex", "supergrok", "workspace_gateway"]).optional(),
10044
12623
  contextWindowTokens: z.number().int().positive().optional(),
10045
12624
  // Additive normalized definition metadata. Optional so older server payloads
10046
12625
  // remain parseable; current servers project the complete V1 set.
@@ -10173,7 +12752,7 @@ export type WorkspaceModelCatalogResponse = z.infer<typeof WorkspaceModelCatalog
10173
12752
  * that rollout boundary. Mutating clients send this value in
10174
12753
  * `x-opengeni-api-contract`; the API rejects any other value before routing.
10175
12754
  */
10176
- export const OPENGENI_API_CONTRACT_REVISION = "2026-07-workspace-artifacts-v1" as const;
12755
+ export const OPENGENI_API_CONTRACT_REVISION = "2026-08-social-provider-tools-v1" as const;
10177
12756
  export const OPENGENI_API_CONTRACT_HEADER = "x-opengeni-api-contract" as const;
10178
12757
  /** Bounded request/response identifier shared by browser, ingress, and API diagnostics. */
10179
12758
  export const OPENGENI_CORRELATION_HEADER = "x-opengeni-correlation-id" as const;
@@ -10201,6 +12780,15 @@ export const ClientConfig = /* @__PURE__ */ defineModelContractSchema(() =>
10201
12780
  }),
10202
12781
  )
10203
12782
  .default([]),
12783
+ firstPartyMcpTools: z
12784
+ .object({
12785
+ default: z.array(FirstPartyMcpToolName),
12786
+ allowed: z.array(FirstPartyMcpToolName),
12787
+ })
12788
+ .default({
12789
+ default: [...DEFAULT_FIRST_PARTY_MCP_TOOLS],
12790
+ allowed: [...FIRST_PARTY_MCP_TOOL_NAMES],
12791
+ }),
10204
12792
  fileUploads: z.object({
10205
12793
  enabled: z.boolean(),
10206
12794
  maxSizeBytes: z.number().int().positive(),
@@ -10335,7 +12923,13 @@ export function evaluateWorkspaceModelPolicy(
10335
12923
  }
10336
12924
 
10337
12925
  export * from "./codex-fleet-policy";
12926
+ export * from "./xai-provider-account-authority";
10338
12927
  export * from "./workspace-instruction-policies";
12928
+ export * from "./company-profile";
12929
+ export * from "./workspace-learning-policy";
10339
12930
  export * from "./workspace-state";
10340
12931
  export * from "./preference-registry";
10341
12932
  export * from "./scoped-knowledge";
12933
+ export * from "./knowledge";
12934
+ export * from "./task-notes";
12935
+ export * from "./canonical-human-identities";