@opengeni/contracts 0.50.0 → 2.2.0-canary.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 (113) hide show
  1. package/dist/agent-authored-durable-text.d.ts +64 -0
  2. package/dist/atlassian.d.ts +1 -0
  3. package/dist/atlassian.js +14 -13
  4. package/dist/{chunk-Y3MLSLB4.js → chunk-3SIZGL3O.js} +42 -30
  5. package/dist/chunk-3SIZGL3O.js.map +1 -0
  6. package/dist/{chunk-JBCIY7QD.js → chunk-7RMTKFJW.js} +45 -22
  7. package/dist/chunk-7RMTKFJW.js.map +1 -0
  8. package/dist/{chunk-Q6A7XT2N.js → chunk-7ZZ3R27P.js} +9598 -6021
  9. package/dist/chunk-7ZZ3R27P.js.map +1 -0
  10. package/dist/{chunk-5UDNP7AO.js → chunk-MRWYRAS5.js} +56 -8
  11. package/dist/chunk-MRWYRAS5.js.map +1 -0
  12. package/dist/{chunk-LW7OH6CS.js → chunk-N6GRVXAJ.js} +6 -5
  13. package/dist/chunk-N6GRVXAJ.js.map +1 -0
  14. package/dist/{chunk-TZ6YEYN4.js → chunk-NVJMJWLL.js} +3 -1
  15. package/dist/chunk-NVJMJWLL.js.map +1 -0
  16. package/dist/chunk-PX3VCKAU.js +29 -0
  17. package/dist/chunk-PX3VCKAU.js.map +1 -0
  18. package/dist/{chunk-LSB7GRLG.js → chunk-TXTQIYCV.js} +15 -11
  19. package/dist/chunk-TXTQIYCV.js.map +1 -0
  20. package/dist/{chunk-CADSX6UN.js → chunk-Y3KNXVTD.js} +2 -2
  21. package/dist/chunk-YBW5JSA3.js +251 -0
  22. package/dist/chunk-YBW5JSA3.js.map +1 -0
  23. package/dist/company-brain-governed-writes.d.ts +475 -0
  24. package/dist/company-brain.d.ts +398 -0
  25. package/dist/company-profile.d.ts +268 -0
  26. package/dist/connection-authority.d.ts +501 -0
  27. package/dist/connection-authority.js +364 -0
  28. package/dist/connection-authority.js.map +1 -0
  29. package/dist/connector-attachments.d.ts +95 -0
  30. package/dist/connector-attachments.js +51 -0
  31. package/dist/connector-attachments.js.map +1 -0
  32. package/dist/editable-artifact-codec-registry.d.ts +16 -7
  33. package/dist/editable-artifact-codec-registry.js +18 -8
  34. package/dist/editable-artifact-committed-transaction.d.ts +2 -2
  35. package/dist/editable-artifact-committed-transaction.js +2 -2
  36. package/dist/editable-artifact-live.d.ts +11 -12
  37. package/dist/editable-artifact-live.js +111 -46
  38. package/dist/editable-artifact-live.js.map +1 -1
  39. package/dist/editable-artifact-serialized-commit.d.ts +2 -1
  40. package/dist/editable-artifact-serialized-commit.js +9 -8
  41. package/dist/editable-artifact-versions.d.ts +20 -5
  42. package/dist/editable-artifact-versions.js +23 -5
  43. package/dist/editable-artifacts.d.ts +2 -0
  44. package/dist/editable-artifacts.js +51 -39
  45. package/dist/google-drive.d.ts +89 -1
  46. package/dist/google-drive.js +78 -17
  47. package/dist/google-drive.js.map +1 -1
  48. package/dist/governed-learning-activation.d.ts +154 -0
  49. package/dist/governed-learning-evaluator.d.ts +152 -0
  50. package/dist/index.d.ts +12395 -1426
  51. package/dist/index.js +837 -115
  52. package/dist/interaction.d.ts +68 -0
  53. package/dist/knowledge.d.ts +192 -0
  54. package/dist/organization-membership-lifecycle.d.ts +516 -0
  55. package/dist/permissions.d.ts +52 -0
  56. package/dist/personal-github.d.ts +319 -0
  57. package/dist/personal-github.js +209 -0
  58. package/dist/personal-github.js.map +1 -0
  59. package/dist/preference-registry.d.ts +17 -0
  60. package/dist/remember.d.ts +331 -0
  61. package/dist/sandbox-file-artifacts.d.ts +56 -0
  62. package/dist/sandbox-snapshots.d.ts +21 -0
  63. package/dist/slack-bot-scopes.d.ts +34 -14
  64. package/dist/slack-bot-scopes.js +5 -1
  65. package/dist/spreadsheet-artifact-commands.d.ts +2 -3
  66. package/dist/spreadsheet-artifact-commands.js +4 -2
  67. package/dist/spreadsheet-artifact-query.js +3 -2
  68. package/dist/task-notes.d.ts +42 -1
  69. package/dist/tool-result-spill.d.ts +16 -0
  70. package/dist/workspace-instruction-policies.d.ts +81 -0
  71. package/dist/workspace-learning-administration.d.ts +289 -0
  72. package/package.json +13 -1
  73. package/src/agent-authored-durable-text.ts +105 -0
  74. package/src/company-brain-governed-writes.ts +356 -0
  75. package/src/company-brain.ts +201 -0
  76. package/src/company-profile.ts +122 -1
  77. package/src/connection-authority.ts +413 -0
  78. package/src/connector-attachments.ts +327 -0
  79. package/src/editable-artifact-codec-registry.ts +55 -17
  80. package/src/editable-artifact-committed-transaction.ts +44 -31
  81. package/src/editable-artifact-live.ts +123 -57
  82. package/src/editable-artifact-serialized-commit.ts +2 -1
  83. package/src/editable-artifact-versions.ts +26 -5
  84. package/src/editable-artifacts.ts +2 -0
  85. package/src/google-drive.ts +76 -4
  86. package/src/governed-learning-activation.ts +191 -0
  87. package/src/governed-learning-evaluator.ts +176 -0
  88. package/src/index.ts +3182 -483
  89. package/src/interaction.ts +55 -1
  90. package/src/knowledge.ts +133 -6
  91. package/src/organization-membership-lifecycle.ts +330 -0
  92. package/src/permissions.ts +55 -0
  93. package/src/personal-github.ts +267 -0
  94. package/src/preference-registry.ts +17 -0
  95. package/src/remember.ts +242 -0
  96. package/src/sandbox-file-artifacts.ts +65 -0
  97. package/src/sandbox-snapshots.ts +78 -0
  98. package/src/slack-bot-scopes.ts +81 -9
  99. package/src/spreadsheet-artifact-commands.ts +12 -13
  100. package/src/task-notes.ts +14 -1
  101. package/src/tool-result-spill.ts +35 -0
  102. package/src/workspace-instruction-policies.ts +30 -0
  103. package/src/workspace-learning-administration.ts +88 -0
  104. package/dist/chunk-5UDNP7AO.js.map +0 -1
  105. package/dist/chunk-BVGIYW7D.js +0 -11
  106. package/dist/chunk-BVGIYW7D.js.map +0 -1
  107. package/dist/chunk-JBCIY7QD.js.map +0 -1
  108. package/dist/chunk-LSB7GRLG.js.map +0 -1
  109. package/dist/chunk-LW7OH6CS.js.map +0 -1
  110. package/dist/chunk-Q6A7XT2N.js.map +0 -1
  111. package/dist/chunk-TZ6YEYN4.js.map +0 -1
  112. package/dist/chunk-Y3MLSLB4.js.map +0 -1
  113. /package/dist/{chunk-CADSX6UN.js.map → chunk-Y3KNXVTD.js.map} +0 -0
package/src/index.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { z } from "zod";
2
+ import { Permission } from "./permissions";
2
3
  import { ScopedKnowledgeScope } from "./scoped-knowledge";
3
4
  import {
4
5
  boundSessionEventPayload,
@@ -10,11 +11,14 @@ import { MemorySlackPublicationDistribution } from "./memory-slack-delivery";
10
11
  import { WorkspaceInstructionPolicyRoleKeyInput } from "./workspace-instruction-policies";
11
12
  import { ClientResumableVoiceInputConfig } from "./transcription-recordings";
12
13
  import { MediaGenerationResult } from "./video-generation";
14
+ import { KnowledgeProviderCitation } from "./knowledge";
15
+ import { XaiProviderAccountAuthoritySnapshotV1 } from "./xai-provider-account-authority";
13
16
 
14
17
  export * from "./slack-bot-scopes";
15
18
  export * from "./slack-task-policy";
16
19
  export * from "./atlassian";
17
20
  export * from "./connector-destinations";
21
+ export * from "./connector-attachments";
18
22
  export * from "./memory-slack-delivery";
19
23
  export * from "./image-generation";
20
24
  export * from "./video-generation";
@@ -22,7 +26,10 @@ export * from "./editable-artifacts";
22
26
  export * from "./editable-artifact-committed-transaction";
23
27
  export * from "./editable-artifact-serialized-commit";
24
28
  export * from "./tool-catalog";
29
+ export * from "./tool-result-spill";
25
30
  export * from "./interaction";
31
+ export * from "./sandbox-file-artifacts";
32
+ export * from "./permissions";
26
33
 
27
34
  export {
28
35
  CreateWorkspaceArtifactRequest,
@@ -118,15 +125,21 @@ export {
118
125
  export {
119
126
  NATIVE_SNAPSHOT_PREFIXES,
120
127
  WORKSPACE_ARCHIVE_DESCRIPTOR_VERSION,
128
+ WORKSPACE_ARCHIVE_OBJECT_REF_SCHEMA,
121
129
  backendForNativeSnapshotProvider,
122
130
  decodeNativeSnapshotRef,
123
131
  encodeNativeSnapshotRef,
132
+ omitInlineWorkspaceArchiveWhenObjectRefPresent,
124
133
  parseWorkspaceArchiveDescriptor,
134
+ parseWorkspaceArchiveObjectRef,
135
+ workspaceArchiveObjectKey,
136
+ workspaceArchivePayloadPresent,
125
137
  type NativeSnapshotDescriptor,
126
138
  type NativeSnapshotProvider,
127
139
  type NativeSnapshotRef,
128
140
  type TarWorkspaceArchiveDescriptor,
129
141
  type WorkspaceArchiveDescriptor,
142
+ type WorkspaceArchiveObjectRef,
130
143
  type WorkspaceTreeFingerprint,
131
144
  } from "./sandbox-snapshots";
132
145
 
@@ -147,11 +160,12 @@ export const SessionStatus = z.enum([
147
160
  ]);
148
161
  export type SessionStatus = z.infer<typeof SessionStatus>;
149
162
 
150
- // 11 backends; 3-way enum parity (contracts / sdk / deployment) is pinned by
163
+ // 12 backends; 3-way enum parity (contracts / sdk / deployment) is pinned by
151
164
  // `packages/sdk/test/contract-parity.test.ts`. Every member is ADDITIVE AT THE
152
165
  // END (the parity test pins positions): the original four, then the six cloud
153
166
  // backends, then `selfhosted` (bring-your-own-compute — a user's own machine
154
- // enrolled as a first-class sandbox).
167
+ // enrolled as a first-class sandbox), then `opensandbox` (an optional
168
+ // Kubernetes-native provisioned sandbox provider).
155
169
  export const SandboxBackend = z.enum([
156
170
  "docker",
157
171
  "modal",
@@ -164,6 +178,7 @@ export const SandboxBackend = z.enum([
164
178
  "cloudflare",
165
179
  "vercel",
166
180
  "selfhosted",
181
+ "opensandbox",
167
182
  ]);
168
183
  export type SandboxBackend = z.infer<typeof SandboxBackend>;
169
184
 
@@ -189,6 +204,7 @@ export const SANDBOX_PROVIDER_INSTANCE_ID_FIELDS_BY_BACKEND = {
189
204
  cloudflare: ["sandboxId"],
190
205
  vercel: ["sandboxId"],
191
206
  selfhosted: ["agentId"],
207
+ opensandbox: ["sandboxId"],
192
208
  } as const satisfies Record<SandboxBackend, readonly string[]>;
193
209
  export const LEGACY_SANDBOX_PROVIDER_INSTANCE_ID_FIELDS = [
194
210
  "sandboxId",
@@ -593,6 +609,36 @@ export const CAPABILITY_DESCRIPTORS: Record<SandboxBackend, CapabilityDescriptor
593
609
  persistable: false,
594
610
  supportsRunAs: false,
595
611
  },
612
+ // Optional Kubernetes-native provisioned sandbox through OpenSandbox.
613
+ // Desktop-class when the box image includes ttyd/browserd/Xvfb: PTY over ttyd,
614
+ // noVNC and browserd over signed URI-mode ingress. OpenGeni owns persistence
615
+ // through its portable tar checkpoint path in object storage; native
616
+ // OpenSandbox pause/resume and snapshots are deliberately not used.
617
+ opensandbox: {
618
+ backend: "opensandbox",
619
+ backendId: "opensandbox",
620
+ tier: "desktop",
621
+ os: { supported: ["linux"], default: "linux" },
622
+ capabilities: {
623
+ FileSystem: { available: true, readOnly: false },
624
+ Terminal: { available: true, transport: "sse-events", pty: true },
625
+ Git: { available: true },
626
+ DesktopStream: { available: true, transport: "vnc-ws" },
627
+ Recording: { available: true },
628
+ },
629
+ lifetime: {
630
+ requiresSnapshotRollover: false,
631
+ hasIdleKiller: false,
632
+ supportsSuspendResume: false,
633
+ resumeIsLockFree: true,
634
+ },
635
+ snapshot: { kind: "tar-only", hasTarFallback: true },
636
+ portExposure: { kind: "provider-tunnel", supportsOnDemandPorts: true },
637
+ workspaceRoot: "/workspace",
638
+ nativeBucketMount: false,
639
+ persistable: true,
640
+ supportsRunAs: false,
641
+ },
596
642
  };
597
643
 
598
644
  export const ReasoningEffort = z.enum(["none", "minimal", "low", "medium", "high", "xhigh", "max"]);
@@ -672,91 +718,6 @@ export const SessionSpawnDenial = z.object({
672
718
  });
673
719
  export type SessionSpawnDenial = z.infer<typeof SessionSpawnDenial>;
674
720
 
675
- export const Permission = z.enum([
676
- "account:read",
677
- "account:admin",
678
- "members:manage",
679
- "workspace:create",
680
- "billing:read",
681
- "billing:manage",
682
- "workspace:read",
683
- "workspace:admin",
684
- "sessions:create",
685
- "sessions:read",
686
- "sessions:control",
687
- // sandbox workspace (sandbox contract §C.3 / crosscut PART 1.2). stream:view is a
688
- // REAL, distinct permission — strictly BROADER than sessions:read — because the
689
- // pixel plane (Channel B) exposes raw pixels: a viewer can see content the
690
- // structured Channel-A event log never captured. sessions:read is NOT
691
- // permission to watch raw pixels.
692
- "stream:view",
693
- // SEPARATE from stream:view: raw input to the desktop (bypasses approvalQueue /
694
- // interrupt). NEVER granted by default in v1 (the input plane is OFF —
695
- // streamControlEnabled=false); the permission exists so later hardening is a
696
- // flag flip, not a redesign.
697
- "stream:control",
698
- // Accept the pixel-plane secret-leak acknowledgment (consent gate before the
699
- // un-redacted desktop URL is handed out).
700
- "stream:acknowledge",
701
- "files:upload",
702
- "files:read",
703
- // Channel-A structured write surface (FS writes / apply-patch); distinct from
704
- // files:read so a read-only viewer can't mutate the box filesystem.
705
- "files:write",
706
- // Attach to an interactive PTY (terminal-as-pty, Channel A); distinct from
707
- // sessions:read which only reads the command-output firehose.
708
- "terminal:attach",
709
- "documents:manage",
710
- "documents:search",
711
- "scheduled_tasks:manage",
712
- "scheduled_tasks:run",
713
- "github:manage",
714
- "github:use",
715
- "api_keys:manage",
716
- "connections:read",
717
- "connections:write",
718
- /** @deprecated alias of variable-sets:manage */
719
- "environments:manage",
720
- /** @deprecated alias of variable-sets:use */
721
- "environments:use",
722
- "variable-sets:list",
723
- "variable-sets:read",
724
- "variable-sets:write",
725
- "variable-sets:manage",
726
- "variable-sets:use",
727
- "secrets:list",
728
- "secrets:read",
729
- "secrets:write",
730
- // Attach or rotate per-session third-party MCP server credentials. Deliberately
731
- // not part of the worker's default first-party MCP permission set: a sandboxed
732
- // agent must not be able to hand itself new bearer credentials.
733
- "mcp_servers:attach",
734
- // Programmatic sandbox -> tool access through the first-party MCP gate. This is
735
- // intentionally narrow and is never part of first-party MCP defaults; callers
736
- // must receive it through an explicit delegated `ogd_` mint carrying sessionId.
737
- "codemode:call",
738
- "goals:manage",
739
- // Bring-your-own-compute (M5). enrollments:read lists a workspace's machines;
740
- // enrollments:manage approves a device-flow enrollment (the LOUD whole-machine
741
- // consent) + revokes a machine. Distinct from sessions/stream perms because an
742
- // enrollment grants WHOLE-MACHINE access to a user's own hardware — a high-trust,
743
- // admin-shaped action. workspace:admin is the super-wildcard over both.
744
- "enrollments:read",
745
- "enrollments:manage",
746
- // Rigs (workspace-scoped, versioned sandbox machine definitions). rigs:use is
747
- // read + propose-change (the agent-native, additive path a sandboxed session
748
- // is trusted with); rigs:manage is create/edit/activate/promote/delete (the
749
- // admin-shaped path that mints or rolls versions). workspace:admin is the
750
- // super-wildcard over both.
751
- "rigs:use",
752
- "rigs:manage",
753
- // Workspace-published HTML artifacts. Read permits listing/source retrieval;
754
- // publish permits create, version publication, and rollback.
755
- "artifacts:read",
756
- "artifacts:publish",
757
- ]);
758
- export type Permission = z.infer<typeof Permission>;
759
-
760
721
  /**
761
722
  * Capability-first permissions signed into a session's first-party OpenGeni
762
723
  * MCP token when a top-level creator does not explicitly narrow them.
@@ -780,8 +741,12 @@ export const DEFAULT_FIRST_PARTY_MCP_PERMISSIONS = [
780
741
  // tools resolve an already-installed workspace principal. Credentials stay
781
742
  // inside the broker and remain subject to each tool's own authorization.
782
743
  "connections:read",
744
+ "variable-sets:list",
745
+ "variable-sets:write",
746
+ "variable-sets:attach",
783
747
  "variable-sets:use",
784
- "variable-sets:manage",
748
+ "secrets:list",
749
+ "secrets:write",
785
750
  "rigs:use",
786
751
  "github:use",
787
752
  "artifacts:read",
@@ -801,6 +766,7 @@ export const FIRST_PARTY_MCP_TOOL_NAMES = [
801
766
  "goal_set",
802
767
  "goal_update",
803
768
  "goal_progress",
769
+ "goal_wait",
804
770
  "goal_complete",
805
771
  "goal_pause",
806
772
  "memory_search",
@@ -811,6 +777,18 @@ export const FIRST_PARTY_MCP_TOOL_NAMES = [
811
777
  "task_notes_list",
812
778
  "task_note_save",
813
779
  "task_note_archive",
780
+ "task_note_replace",
781
+ "knowledge_propose",
782
+ "knowledge_correct",
783
+ "task_note_promote_knowledge",
784
+ "task_note_promote_instruction_policy",
785
+ "task_note_promote_preference",
786
+ "instruction_policy_propose",
787
+ "preference_propose",
788
+ "remember",
789
+ "remember_confirm",
790
+ "company_profile_propose",
791
+ "company_profile_confirm",
814
792
  "sandboxes_list",
815
793
  "sandbox_attach",
816
794
  "sandbox_swap",
@@ -825,11 +803,13 @@ export const FIRST_PARTY_MCP_TOOL_NAMES = [
825
803
  "sessions_list",
826
804
  "session_get",
827
805
  "session_events",
806
+ "session_wait",
828
807
  "session_create",
829
808
  "session_send_message",
830
809
  "session_pause",
831
810
  "session_resume",
832
811
  "session_steer",
812
+ "session_human_input_respond",
833
813
  "set_other_session_title",
834
814
  "interaction_discover",
835
815
  "browser_open",
@@ -888,6 +868,7 @@ export const FIRST_PARTY_MCP_TOOL_NAMES = [
888
868
  "scheduled_tasks_delete",
889
869
  "scheduled_task_runs_list",
890
870
  "slack_bot_list_channels",
871
+ "slack_bot_search",
891
872
  "slack_bot_channel_history",
892
873
  "slack_bot_thread_replies",
893
874
  "slack_bot_list_users",
@@ -914,6 +895,7 @@ export const FIRST_PARTY_MCP_TOOL_NAMES = [
914
895
  "artifacts_create",
915
896
  "artifacts_publish",
916
897
  "artifacts_rollback",
898
+ "sandbox_file_publish",
917
899
  "editable_artifact_list",
918
900
  "editable_artifact_create",
919
901
  "editable_artifact_import",
@@ -964,8 +946,15 @@ const FIRST_PARTY_IN_PROCESS_TOOL_NAME_SET = new Set<FirstPartyMcpToolName>(
964
946
  * trustworthy logical-delivery identity. Server-owned Slack delivery paths
965
947
  * call the internal client directly with their own durable operation IDs.
966
948
  */
949
+ // Names kept so previously written data still parses - immutable scheduled-task
950
+ // execution snapshots recorded the tool set that was default at the time, and
951
+ // they are strictly re-parsed on replay. Retiring a tool must not strand an
952
+ // accepted occurrence. These are never registered, never default, and never
953
+ // authorized; they exist so history stays readable.
967
954
  const FIRST_PARTY_COMPATIBILITY_ONLY_TOOL_NAMES = [
968
955
  "slack_bot_post_message",
956
+ "memory_save",
957
+ "memory_correct",
969
958
  ] as const satisfies readonly FirstPartyMcpToolName[];
970
959
 
971
960
  const FIRST_PARTY_COMPATIBILITY_ONLY_TOOL_NAME_SET = new Set<FirstPartyMcpToolName>(
@@ -998,6 +987,10 @@ export const EDITABLE_ARTIFACT_MCP_CODEMODE_PATHS = {
998
987
  */
999
988
  export const DEFAULT_FIRST_PARTY_MCP_TOOLS = FIRST_PARTY_MCP_TOOL_NAMES.filter(
1000
989
  (name) =>
990
+ // Memory V1 writes are retired entirely; `remember` and task-note
991
+ // promotion own durable agent writes.
992
+ name !== "memory_save" &&
993
+ name !== "memory_correct" &&
1001
994
  !name.startsWith("social_") &&
1002
995
  !name.startsWith("x_") &&
1003
996
  !name.startsWith("reddit_") &&
@@ -1053,6 +1046,178 @@ export const PersonalResourceRetentionMode = z.enum(["retain", "delete_after"]);
1053
1046
  export type PersonalResourceRetentionMode = z.infer<typeof PersonalResourceRetentionMode>;
1054
1047
 
1055
1048
  export const SessionTenancyVisibility = z.enum(["user_private", "workspace_shared"]);
1049
+
1050
+ export const UserResourceAuthorityScope = z.literal("user");
1051
+ export const UserResourceKind = z.enum([
1052
+ "connection",
1053
+ "document",
1054
+ "variable_set",
1055
+ "rig",
1056
+ "connected_machine",
1057
+ ]);
1058
+ export type UserResourceKind = z.infer<typeof UserResourceKind>;
1059
+
1060
+ export const USER_RESOURCE_ACTION_BY_KIND = {
1061
+ connection: "connection.use",
1062
+ document: "document.read",
1063
+ variable_set: "variable_set.use",
1064
+ rig: "rig.use",
1065
+ connected_machine: "connected_machine.use",
1066
+ } as const satisfies Record<UserResourceKind, string>;
1067
+
1068
+ export const UserResourceLifecycleGrantMode = z.enum(["once", "session", "always"]);
1069
+ export const PERSONAL_RESOURCE_SHARED_OUTPUT_WARNING_VERSION = 1 as const;
1070
+ export const PERSONAL_RESOURCE_SHARED_OUTPUT_WARNING =
1071
+ "Personal resources used in a workspace-shared session may influence outputs visible to other workspace members. The underlying credentials and secret values are not shared by the attachment itself.";
1072
+
1073
+ /**
1074
+ * Owner-authored issuance intent for the fixed personal Variable Set/Rig/
1075
+ * Connected Machine closure selected by one session. The server derives every resource,
1076
+ * authority and action from the locked session; callers never nominate grants.
1077
+ */
1078
+ export const PersonalResourceAttachmentIntent = z
1079
+ .object({
1080
+ mode: UserResourceLifecycleGrantMode,
1081
+ expectedAuthorityEpoch: z.number().int().positive().optional(),
1082
+ workspaceSharedAcknowledged: z.boolean().default(false),
1083
+ sharedOutputWarningVersion: z.literal(PERSONAL_RESOURCE_SHARED_OUTPUT_WARNING_VERSION),
1084
+ })
1085
+ .strict();
1086
+ export type PersonalResourceAttachmentIntent = z.infer<typeof PersonalResourceAttachmentIntent>;
1087
+
1088
+ function requireEstablishedPersonalResourceEpoch(
1089
+ value: {
1090
+ personalResourceAttachment?: PersonalResourceAttachmentIntent | undefined;
1091
+ },
1092
+ context: z.RefinementCtx,
1093
+ ): void {
1094
+ if (
1095
+ value.personalResourceAttachment !== undefined &&
1096
+ value.personalResourceAttachment.expectedAuthorityEpoch === undefined
1097
+ ) {
1098
+ context.addIssue({
1099
+ code: z.ZodIssueCode.custom,
1100
+ path: ["personalResourceAttachment", "expectedAuthorityEpoch"],
1101
+ message: "established-session attachment requires expectedAuthorityEpoch",
1102
+ });
1103
+ }
1104
+ }
1105
+
1106
+ /** Credential-free accepted-work projection. Resource ids remain private. */
1107
+ export const PersonalResourceAttachmentSummary = z
1108
+ .object({
1109
+ mode: UserResourceLifecycleGrantMode,
1110
+ context: SessionTenancyVisibility,
1111
+ resourceCount: z.number().int().positive(),
1112
+ resourceKinds: z
1113
+ .array(z.enum(["variable_set", "rig", "connected_machine"]))
1114
+ .min(1)
1115
+ .max(3),
1116
+ sharedOutputWarningVersion: z.literal(PERSONAL_RESOURCE_SHARED_OUTPUT_WARNING_VERSION),
1117
+ })
1118
+ .strict();
1119
+ export type PersonalResourceAttachmentSummary = z.infer<typeof PersonalResourceAttachmentSummary>;
1120
+ /** Public owner-management deliberately excludes race-prone standalone `once`. */
1121
+ export const ManagedUserResourceGrantMode = z.enum(["session", "always"]);
1122
+ export const UserResourceAuthorityGrant = z.object({
1123
+ grantId: z.string().uuid(),
1124
+ targetWorkspaceId: z.string().uuid(),
1125
+ targetSessionId: z.string().uuid().nullable(),
1126
+ action: z.enum([
1127
+ "connection.use",
1128
+ "document.read",
1129
+ "variable_set.use",
1130
+ "rig.use",
1131
+ "connected_machine.use",
1132
+ ]),
1133
+ mode: UserResourceLifecycleGrantMode,
1134
+ context: SessionTenancyVisibility,
1135
+ authorityEpoch: z.number().int().positive().nullable(),
1136
+ generation: z.number().int().positive(),
1137
+ status: z.enum(["active", "consumed", "revoked", "expired"]),
1138
+ expiresAt: z.string().datetime().nullable(),
1139
+ delegation: z.lazy(() => UserResourceDelegation),
1140
+ });
1141
+ export const UserResourceAuthoritySummary = z.object({
1142
+ authorityId: z.string().uuid(),
1143
+ resourceKind: UserResourceKind,
1144
+ resourceId: z.string().uuid(),
1145
+ originWorkspaceId: z.string().uuid().nullable(),
1146
+ generation: z.number().int().positive(),
1147
+ status: z.enum(["active", "retained", "revoked"]),
1148
+ grants: z.array(UserResourceAuthorityGrant),
1149
+ });
1150
+ export const ListUserResourceAuthoritiesQuery = z.object({
1151
+ scope: UserResourceAuthorityScope,
1152
+ resourceKind: UserResourceKind,
1153
+ cursor: z.string().uuid().optional(),
1154
+ limit: z.coerce.number().int().min(1).max(100).default(50),
1155
+ });
1156
+ export const ListUserResourceAuthoritiesResponse = z.object({
1157
+ scope: UserResourceAuthorityScope,
1158
+ authorities: z.array(UserResourceAuthoritySummary),
1159
+ nextCursor: z.string().uuid().nullable(),
1160
+ });
1161
+ export const IssueUserResourceGrantRequest = z
1162
+ .object({
1163
+ scope: UserResourceAuthorityScope,
1164
+ resourceKind: UserResourceKind,
1165
+ mode: ManagedUserResourceGrantMode,
1166
+ context: SessionTenancyVisibility,
1167
+ sessionId: z.string().uuid().nullable().optional(),
1168
+ expectedAuthorityEpoch: z.number().int().positive().nullable().optional(),
1169
+ workspaceSharedAcknowledged: z.boolean().default(false),
1170
+ })
1171
+ .superRefine((value, context) => {
1172
+ if (value.context === "workspace_shared" && !value.workspaceSharedAcknowledged) {
1173
+ context.addIssue({
1174
+ code: "custom",
1175
+ path: ["workspaceSharedAcknowledged"],
1176
+ message: "workspace_shared requires durable shared-output acknowledgement",
1177
+ });
1178
+ }
1179
+ if (value.mode === "always" && (value.sessionId || value.expectedAuthorityEpoch)) {
1180
+ context.addIssue({
1181
+ code: "custom",
1182
+ path: ["sessionId"],
1183
+ message: "always is unbound from session authority",
1184
+ });
1185
+ }
1186
+ if (value.mode === "session" && (!value.sessionId || !value.expectedAuthorityEpoch)) {
1187
+ context.addIssue({
1188
+ code: "custom",
1189
+ path: ["sessionId"],
1190
+ message: "session grants require a target session and expectedAuthorityEpoch",
1191
+ });
1192
+ }
1193
+ });
1194
+ export const UserResourceGrantMutationResponse = z.object({
1195
+ scope: UserResourceAuthorityScope,
1196
+ grant: UserResourceAuthorityGrant,
1197
+ });
1198
+ export const UserResourceGrantRevocationResponse = z.object({
1199
+ scope: UserResourceAuthorityScope,
1200
+ grant: z.object({
1201
+ grantId: z.string().uuid(),
1202
+ generation: z.number().int().positive(),
1203
+ status: z.literal("revoked"),
1204
+ revokedAt: z.string().datetime(),
1205
+ }),
1206
+ });
1207
+ export const RevokeUserResourceGrantQuery = z.object({
1208
+ scope: UserResourceAuthorityScope,
1209
+ });
1210
+ export type UserResourceAuthorityGrant = z.infer<typeof UserResourceAuthorityGrant>;
1211
+ export type UserResourceAuthoritySummary = z.infer<typeof UserResourceAuthoritySummary>;
1212
+ export type ListUserResourceAuthoritiesQuery = z.infer<typeof ListUserResourceAuthoritiesQuery>;
1213
+ export type ListUserResourceAuthoritiesResponse = z.infer<
1214
+ typeof ListUserResourceAuthoritiesResponse
1215
+ >;
1216
+ export type IssueUserResourceGrantRequest = z.infer<typeof IssueUserResourceGrantRequest>;
1217
+ export type UserResourceGrantMutationResponse = z.infer<typeof UserResourceGrantMutationResponse>;
1218
+ export type UserResourceGrantRevocationResponse = z.infer<
1219
+ typeof UserResourceGrantRevocationResponse
1220
+ >;
1056
1221
  export type SessionTenancyVisibility = z.infer<typeof SessionTenancyVisibility>;
1057
1222
 
1058
1223
  /** Public/session API vocabulary. Persistence keeps the explicit tenancy names. */
@@ -1274,6 +1439,100 @@ export const SessionTenancyPublicProjection = z.object({
1274
1439
  });
1275
1440
  export type SessionTenancyPublicProjection = z.infer<typeof SessionTenancyPublicProjection>;
1276
1441
 
1442
+ export const SessionTenancyBlocker = z.enum([
1443
+ "nonterminal_turn",
1444
+ "nonterminal_attempt",
1445
+ "unsettled_interruption",
1446
+ "pending_system_update",
1447
+ "pending_human_input",
1448
+ "pending_tool_receipt",
1449
+ "run_state",
1450
+ "active_goal",
1451
+ "capacity_waiter",
1452
+ "active_realtime",
1453
+ "active_scheduled_task",
1454
+ "workspace_mutation_admission",
1455
+ "retained_process",
1456
+ "active_sandbox_access",
1457
+ "shared_sandbox_group",
1458
+ ]);
1459
+ export type SessionTenancyBlocker = z.infer<typeof SessionTenancyBlocker>;
1460
+
1461
+ export const SESSION_OPERATION_KEY_MAX_CHARS = 256;
1462
+ const SessionTenancyIdempotencyKey = z.string().trim().min(1).max(SESSION_OPERATION_KEY_MAX_CHARS);
1463
+
1464
+ export const UpdateSessionVisibilityRequest = z
1465
+ .object({
1466
+ visibility: SessionVisibility,
1467
+ expectedAuthorityEpoch: z.number().int().positive().max(Number.MAX_SAFE_INTEGER),
1468
+ idempotencyKey: SessionTenancyIdempotencyKey,
1469
+ })
1470
+ .strict();
1471
+ export type UpdateSessionVisibilityRequest = z.infer<typeof UpdateSessionVisibilityRequest>;
1472
+
1473
+ export const UpdateSessionVisibilityResponse = z
1474
+ .object({
1475
+ operationId: z.string().uuid(),
1476
+ eventId: z.string().uuid().nullable(),
1477
+ eventSequence: z.number().int().positive().nullable(),
1478
+ visibility: SessionVisibility,
1479
+ authorityEpoch: z.number().int().positive(),
1480
+ changed: z.boolean(),
1481
+ replay: z.boolean(),
1482
+ revokedGrantCount: z.number().int().nonnegative(),
1483
+ })
1484
+ .strict()
1485
+ .superRefine((value, context) => {
1486
+ if (value.changed !== (value.eventId !== null && value.eventSequence !== null)) {
1487
+ context.addIssue({
1488
+ code: "custom",
1489
+ path: ["changed"],
1490
+ message: "changed must match the presence of the durable event receipt",
1491
+ });
1492
+ }
1493
+ if ((value.eventId === null) !== (value.eventSequence === null)) {
1494
+ context.addIssue({
1495
+ code: "custom",
1496
+ path: ["eventId"],
1497
+ message: "eventId and eventSequence must be present or absent together",
1498
+ });
1499
+ }
1500
+ });
1501
+ export type UpdateSessionVisibilityResponse = z.infer<typeof UpdateSessionVisibilityResponse>;
1502
+
1503
+ export const ForkSessionRequest = z
1504
+ .object({
1505
+ idempotencyKey: SessionTenancyIdempotencyKey,
1506
+ visibility: SessionVisibility,
1507
+ workspaceSharedAcknowledged: z.boolean(),
1508
+ })
1509
+ .strict()
1510
+ .superRefine((value, context) => {
1511
+ if (value.visibility === "private" && value.workspaceSharedAcknowledged) {
1512
+ context.addIssue({
1513
+ code: "custom",
1514
+ path: ["workspaceSharedAcknowledged"],
1515
+ message: "workspaceSharedAcknowledged must be false for a private destination",
1516
+ });
1517
+ }
1518
+ });
1519
+ export type ForkSessionRequest = z.infer<typeof ForkSessionRequest>;
1520
+
1521
+ export const ForkSessionResponse = z
1522
+ .object({
1523
+ operationId: z.string().uuid(),
1524
+ eventId: z.string().uuid(),
1525
+ eventSequence: z.number().int().positive(),
1526
+ sessionId: z.string().uuid(),
1527
+ workspaceId: z.string().uuid(),
1528
+ visibility: SessionVisibility,
1529
+ authorityEpoch: z.literal(1),
1530
+ copiedHistoryItemCount: z.number().int().nonnegative(),
1531
+ replay: z.boolean(),
1532
+ })
1533
+ .strict();
1534
+ export type ForkSessionResponse = z.infer<typeof ForkSessionResponse>;
1535
+
1277
1536
  export const ManagedAccount = z.object({
1278
1537
  id: z.string().uuid(),
1279
1538
  name: z.string(),
@@ -1621,6 +1880,37 @@ export const WorkspaceVoiceInputSettings = z
1621
1880
  .strict();
1622
1881
  export type WorkspaceVoiceInputSettings = z.infer<typeof WorkspaceVoiceInputSettings>;
1623
1882
 
1883
+ /** Model policy inherited by newly created chats and scheduled tasks. */
1884
+ export const WorkspaceSessionDefaults = z
1885
+ .object({
1886
+ model: z.string().trim().min(1).max(256),
1887
+ reasoningEffort: ReasoningEffort,
1888
+ })
1889
+ .strict();
1890
+ export type WorkspaceSessionDefaults = z.infer<typeof WorkspaceSessionDefaults>;
1891
+
1892
+ /**
1893
+ * Exact capability selection inherited by new top-level sessions.
1894
+ *
1895
+ * The product UI presents understandable capability groups, but persistence
1896
+ * stays source-of-truth exact: MCP server ids and first-party tool names. This
1897
+ * keeps the runtime independent from presentation labels and lets a session
1898
+ * narrow the resulting policy without changing the workspace default.
1899
+ */
1900
+ export const WorkspaceSessionToolDefaults = z
1901
+ .object({
1902
+ mcpServerIds: z
1903
+ .array(z.string().trim().min(1).max(128))
1904
+ .max(128)
1905
+ .transform((ids) => [...new Set(ids)]),
1906
+ firstPartyMcpTools: z
1907
+ .array(FirstPartyMcpToolName)
1908
+ .max(512)
1909
+ .transform((tools) => [...new Set(tools)]),
1910
+ })
1911
+ .strict();
1912
+ export type WorkspaceSessionToolDefaults = z.infer<typeof WorkspaceSessionToolDefaults>;
1913
+
1624
1914
  /** Client-safe voice-input capability projection. Never includes provider secrets. */
1625
1915
  export const ClientVoiceInputConfig = z
1626
1916
  .object({
@@ -1714,17 +2004,79 @@ export const DEFAULT_WORKSPACE_SLACK_REACTION_SUMMON_SETTINGS = {
1714
2004
  channelPolicy: { mode: "bot_member" },
1715
2005
  } as const satisfies WorkspaceSlackReactionSummonSettings;
1716
2006
 
2007
+ /**
2008
+ * Per-workspace switches for the two Slack orchestration notices: a pointer
2009
+ * card when a child worker of a Slack-originated session blocks on human input
2010
+ * or a tool approval, and a one-line notice when that session's goal pauses for
2011
+ * budget or the continuation cap.
2012
+ *
2013
+ * Both are OFF unless this workspace explicitly turned them on. An unsolicited
2014
+ * Slack post is worse than a missed one, and the in-app rail and priority feed
2015
+ * already surface a blocked child, so an absent, malformed, or partially
2016
+ * invalid setting resolves to both disabled - see
2017
+ * `resolveWorkspaceSlackOrchestrationNoticeSettings`.
2018
+ *
2019
+ * Deliberately not `.strict()`: this is a KNOWN key of the stored settings bag,
2020
+ * so rejecting it rejects the whole bag and silently reverts memoryEnabled,
2021
+ * agentHumanInputEnabled, codexCompactionDefault, voiceInput, and the Slack
2022
+ * reaction shortcut to their defaults too. An unknown key here is a notice a
2023
+ * newer release added, and a rollback must not cost five unrelated settings.
2024
+ * An invalid VALUE still fails the bag, and therefore fails closed to silence.
2025
+ */
2026
+ export const WorkspaceSlackOrchestrationNoticeSettings = z.object({
2027
+ childRequiresAction: z.boolean().optional(),
2028
+ goalPaused: z.boolean().optional(),
2029
+ });
2030
+ export type WorkspaceSlackOrchestrationNoticeSettings = z.infer<
2031
+ typeof WorkspaceSlackOrchestrationNoticeSettings
2032
+ >;
2033
+
2034
+ /**
2035
+ * The resolved answer: every notice is an explicit boolean, never absent.
2036
+ * Spelled out rather than derived with `Required<>`, which would keep the
2037
+ * `| undefined` that the optional zod fields carry into their inferred type.
2038
+ */
2039
+ export type ResolvedWorkspaceSlackOrchestrationNoticeSettings = {
2040
+ childRequiresAction: boolean;
2041
+ goalPaused: boolean;
2042
+ };
2043
+
2044
+ export const DEFAULT_WORKSPACE_SLACK_ORCHESTRATION_NOTICE_SETTINGS = {
2045
+ childRequiresAction: false,
2046
+ goalPaused: false,
2047
+ } as const satisfies ResolvedWorkspaceSlackOrchestrationNoticeSettings;
2048
+
2049
+ // Memory V1's standing prompt block is retired, but the enum deliberately still
2050
+ // ACCEPTS `legacy_standing`. `.passthrough()` only preserves unknown keys - it
2051
+ // does not rescue a known key holding a value the enum rejects - so collapsing
2052
+ // this to one value would fail the whole settings parse for any workspace that
2053
+ // opted in. Every resolver here is "parse the bag, fall back on failure", so
2054
+ // that single rejection would silently revert memoryEnabled,
2055
+ // agentHumanInputEnabled, codexCompactionDefault, voiceInput and Slack summon
2056
+ // to their defaults. The value is accepted and then ignored: see
2057
+ // resolveWorkspaceMemoryPromptMode.
1717
2058
  export const WorkspaceMemoryPromptMode = z.enum(["legacy_standing", "retrieval_only"]);
1718
2059
  export type WorkspaceMemoryPromptMode = z.infer<typeof WorkspaceMemoryPromptMode>;
1719
2060
 
2061
+ /**
2062
+ * What an already-accepted turn recorded. Snapshots and receipts are immutable,
2063
+ * so turns accepted before the standing block was retired still read back
2064
+ * `legacy_standing`. That is a fact about history, not a mode anyone can pick -
2065
+ * keep the two apart so retiring the setting never rewrites the record.
2066
+ */
2067
+ export const HistoricalMemoryPromptMode = z.enum(["legacy_standing", "retrieval_only"]);
2068
+ export type HistoricalMemoryPromptMode = z.infer<typeof HistoricalMemoryPromptMode>;
2069
+
1720
2070
  // Validates the KNOWN keys of workspaces.settings; passthrough keeps unknown
1721
2071
  // (future) keys rather than stripping them. memoryEnabled defaults off and the
1722
- // reversible Memory V1 containment rollout defaults to legacy standing context;
2072
+ // Memory V1 prompt mode is always retrieval-only composition;
1723
2073
  // voiceInput defaults to enabled when the deployment has a provider.
1724
2074
  export const WorkspaceSettingsSchema = z
1725
2075
  .object({
1726
2076
  memoryEnabled: z.boolean().optional(),
1727
2077
  memoryPromptMode: WorkspaceMemoryPromptMode.optional(),
2078
+ sessionDefaults: WorkspaceSessionDefaults.optional(),
2079
+ sessionToolDefaults: WorkspaceSessionToolDefaults.optional(),
1728
2080
  /** Preferred workspace voice-input toggle. */
1729
2081
  voiceInput: WorkspaceVoiceInputSettings.optional(),
1730
2082
  /**
@@ -1744,6 +2096,11 @@ export const WorkspaceSettingsSchema = z
1744
2096
  // Optional Slack reaction invocation. Absent/invalid fails closed to the
1745
2097
  // disabled default via resolveWorkspaceSlackReactionSummonSettings.
1746
2098
  slackReactionSummon: WorkspaceSlackReactionSummonSettings.optional(),
2099
+ // Optional Slack orchestration notices (blocked child worker, paused goal).
2100
+ // BOTH DEFAULT OFF: absent, malformed, or partially invalid settings fail
2101
+ // closed to disabled via resolveWorkspaceSlackOrchestrationNoticeSettings,
2102
+ // because an unsolicited Slack post is worse than a missed one.
2103
+ slackOrchestrationNotices: WorkspaceSlackOrchestrationNoticeSettings.optional(),
1747
2104
  })
1748
2105
  .passthrough();
1749
2106
  export type WorkspaceSettings = z.infer<typeof WorkspaceSettingsSchema>;
@@ -1754,10 +2111,31 @@ export function resolveWorkspaceMemoryEnabled(settings: unknown): boolean {
1754
2111
  return parsed.success ? parsed.data.memoryEnabled === true : false;
1755
2112
  }
1756
2113
 
1757
- /** Reversible Memory V1 prompt rollout. Absent preserves legacy composition. */
1758
- export function resolveWorkspaceMemoryPromptMode(settings: unknown): WorkspaceMemoryPromptMode {
2114
+ /** Explicit defaults for new chats/schedules, or null for deployment defaults. */
2115
+ export function resolveWorkspaceSessionDefaults(
2116
+ settings: unknown,
2117
+ ): WorkspaceSessionDefaults | null {
2118
+ const parsed = WorkspaceSettingsSchema.safeParse(settings ?? {});
2119
+ return parsed.success ? (parsed.data.sessionDefaults ?? null) : null;
2120
+ }
2121
+
2122
+ /** Exact capability defaults for new sessions, or null for deployment defaults. */
2123
+ export function resolveWorkspaceSessionToolDefaults(
2124
+ settings: unknown,
2125
+ ): WorkspaceSessionToolDefaults | null {
1759
2126
  const parsed = WorkspaceSettingsSchema.safeParse(settings ?? {});
1760
- return parsed.success ? (parsed.data.memoryPromptMode ?? "legacy_standing") : "legacy_standing";
2127
+ return parsed.success ? (parsed.data.sessionToolDefaults ?? null) : null;
2128
+ }
2129
+
2130
+ /**
2131
+ * Memory V1 prompt mode. Always `retrieval_only`: the standing pinned/recency
2132
+ * block is retired, so an absent, unrecognized, or stored-`legacy_standing`
2133
+ * setting all resolve the same way. Kept as a function so the call sites and
2134
+ * the frozen SQL snapshot keep agreeing without each one having to learn that
2135
+ * the choice is gone.
2136
+ */
2137
+ export function resolveWorkspaceMemoryPromptMode(): "retrieval_only" {
2138
+ return "retrieval_only";
1761
2139
  }
1762
2140
 
1763
2141
  /** Default Codex compaction mode for new Codex sessions (remote_v2 when unset). */
@@ -1814,6 +2192,30 @@ export function resolveWorkspaceSlackReactionSummonSettings(
1814
2192
  : { ...configured, channelPolicy: { mode: "bot_member" } };
1815
2193
  }
1816
2194
 
2195
+ /**
2196
+ * Resolve the two Slack orchestration notice switches, failing closed.
2197
+ *
2198
+ * Off is the product decision, not a placeholder: the in-app rail and priority
2199
+ * feed already surface a blocked child worker and a paused goal, so a missed
2200
+ * Slack post costs a delay while an unsolicited one costs the thread's
2201
+ * credibility. Absent settings, a settings bag this schema rejects, and a
2202
+ * partially invalid notice object therefore all resolve to both disabled -
2203
+ * only an explicit `true` on a valid bag turns a notice on.
2204
+ */
2205
+ export function resolveWorkspaceSlackOrchestrationNoticeSettings(
2206
+ settings: unknown,
2207
+ ): ResolvedWorkspaceSlackOrchestrationNoticeSettings {
2208
+ const parsed = WorkspaceSettingsSchema.safeParse(settings ?? {});
2209
+ const configured = parsed.success ? parsed.data.slackOrchestrationNotices : undefined;
2210
+ return {
2211
+ childRequiresAction:
2212
+ configured?.childRequiresAction ??
2213
+ DEFAULT_WORKSPACE_SLACK_ORCHESTRATION_NOTICE_SETTINGS.childRequiresAction,
2214
+ goalPaused:
2215
+ configured?.goalPaused ?? DEFAULT_WORKSPACE_SLACK_ORCHESTRATION_NOTICE_SETTINGS.goalPaused,
2216
+ };
2217
+ }
2218
+
1817
2219
  export function workspaceSlackReactionChannelAllowed(
1818
2220
  settings: WorkspaceSlackReactionSummonSettings,
1819
2221
  channelId: string,
@@ -1831,6 +2233,8 @@ export const UpdateWorkspaceSettingsRequest = z
1831
2233
  .object({
1832
2234
  memoryEnabled: z.boolean().optional(),
1833
2235
  memoryPromptMode: WorkspaceMemoryPromptMode.optional(),
2236
+ sessionDefaults: WorkspaceSessionDefaults.optional(),
2237
+ sessionToolDefaults: WorkspaceSessionToolDefaults.optional(),
1834
2238
  voiceInput: WorkspaceVoiceInputSettings.optional(),
1835
2239
  /** @deprecated Prefer `voiceInput`. Kept for one compatibility release. */
1836
2240
  transcription: WorkspaceTranscriptionPolicy.optional(),
@@ -1838,6 +2242,7 @@ export const UpdateWorkspaceSettingsRequest = z
1838
2242
  codexCompactionDefault: CodexCompactionMode.optional(),
1839
2243
  agentHumanInputEnabled: z.boolean().optional(),
1840
2244
  slackReactionSummon: WorkspaceSlackReactionSummonSettings.optional(),
2245
+ slackOrchestrationNotices: WorkspaceSlackOrchestrationNoticeSettings.optional(),
1841
2246
  })
1842
2247
  .passthrough();
1843
2248
  export type UpdateWorkspaceSettingsRequest = z.infer<typeof UpdateWorkspaceSettingsRequest>;
@@ -2348,6 +2753,17 @@ export const StreamTokenPayload = z.object({
2348
2753
  // Short TTL (120s default); rotation is event-driven under the epoch fence,
2349
2754
  // not on a keepalive clock.
2350
2755
  exp: z.number().int().positive(),
2756
+ // The authenticated subject the token was minted for (0281). Optional for
2757
+ // rolling compatibility: old verifiers strip it, old mints omit it.
2758
+ subjectId: z.string().min(1).max(512).optional(),
2759
+ // The session authority epoch observed at mint (0281). The relay tracks the
2760
+ // highest value presented per LIVE channel and rejects tokens below that
2761
+ // floor. The floor is defense-in-depth, not the revocation authority: it
2762
+ // lives only as long as the channel does (both sides detached, the
2763
+ // half-open reaper, or a relay restart reset it), so a stale in-TTL token
2764
+ // can still attach to a fresh channel. Real revocation is the mint refusing
2765
+ // to issue new tokens plus the 120 s TTL bounding the old ones.
2766
+ authorityEpoch: z.number().int().positive().optional(),
2351
2767
  });
2352
2768
  export type StreamTokenPayload = z.infer<typeof StreamTokenPayload>;
2353
2769
 
@@ -2509,6 +2925,7 @@ export const ApiKey = z.object({
2509
2925
  accountId: z.string().uuid(),
2510
2926
  workspaceId: z.string().uuid().nullable(),
2511
2927
  name: z.string(),
2928
+ description: z.string().nullable(),
2512
2929
  prefix: z.string(),
2513
2930
  permissions: z.array(Permission),
2514
2931
  expiresAt: z.string().nullable(),
@@ -2521,6 +2938,7 @@ export type ApiKey = z.infer<typeof ApiKey>;
2521
2938
 
2522
2939
  export const CreateApiKeyRequest = z.object({
2523
2940
  name: z.string().min(1),
2941
+ description: z.string().trim().min(1).max(500).optional(),
2524
2942
  workspaceId: z.string().uuid().optional(),
2525
2943
  permissions: z.array(Permission).min(1),
2526
2944
  expiresAt: z.string().datetime({ offset: true }).optional(),
@@ -2674,6 +3092,59 @@ export type InsightsBillingPath = z.infer<typeof InsightsBillingPath>;
2674
3092
  export const InsightsPricingSource = z.enum(["configured_list_price", "gateway_reported"]);
2675
3093
  export type InsightsPricingSource = z.infer<typeof InsightsPricingSource>;
2676
3094
 
3095
+ export const ModelContextContributionSource = z.enum([
3096
+ "workspace_instruction_policy",
3097
+ "legacy_workspace_instructions",
3098
+ "preference_registry_descriptor",
3099
+ "company_profile",
3100
+ "legacy_memory_v1",
3101
+ "runtime_skill_catalog",
3102
+ ]);
3103
+ export type ModelContextContributionSource = z.infer<typeof ModelContextContributionSource>;
3104
+
3105
+ /** Content-free per-call summary of model-visible Company Brain material. */
3106
+ export const ModelContextContributionSummary = z
3107
+ .object({
3108
+ source: ModelContextContributionSource,
3109
+ items: z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER),
3110
+ utf8Bytes: z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER),
3111
+ estimatedTokens: z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER),
3112
+ })
3113
+ .strict();
3114
+ export type ModelContextContributionSummary = z.infer<typeof ModelContextContributionSummary>;
3115
+
3116
+ export const ModelContextContributionSummaries = z
3117
+ .array(ModelContextContributionSummary)
3118
+ .max(6)
3119
+ .superRefine((summaries, context) => {
3120
+ const seen = new Set<ModelContextContributionSource>();
3121
+ for (const [index, summary] of summaries.entries()) {
3122
+ if (seen.has(summary.source)) {
3123
+ context.addIssue({
3124
+ code: "custom",
3125
+ message: "Model context contribution sources must be unique",
3126
+ path: [index, "source"],
3127
+ });
3128
+ }
3129
+ seen.add(summary.source);
3130
+ }
3131
+ });
3132
+
3133
+ export const InsightsPromptContributionRow = ModelContextContributionSummary.extend({
3134
+ calls: z.number().int().nonnegative(),
3135
+ });
3136
+ export type InsightsPromptContributionRow = z.infer<typeof InsightsPromptContributionRow>;
3137
+
3138
+ export const InsightsPromptContributions = z.object({
3139
+ /** Sum of UTF-8 byte / 4 estimates across calls with contribution receipts. */
3140
+ estimatedTokens: z.number().int().nonnegative(),
3141
+ utf8Bytes: z.number().int().nonnegative(),
3142
+ coveredCalls: z.number().int().nonnegative(),
3143
+ totalCalls: z.number().int().nonnegative(),
3144
+ sources: z.array(InsightsPromptContributionRow),
3145
+ });
3146
+ export type InsightsPromptContributions = z.infer<typeof InsightsPromptContributions>;
3147
+
2677
3148
  export const InsightsModelUsageRow = z.object({
2678
3149
  id: z.string().min(1),
2679
3150
  model: z.string().min(1),
@@ -2840,6 +3311,13 @@ export const WorkspaceInsightsSnapshot = z.object({
2840
3311
  drivers: z.array(InsightsSpendDriver),
2841
3312
  schedules: z.array(InsightsScheduleRow),
2842
3313
  recentCalls: z.array(InsightsModelCallRow),
3314
+ promptContributions: InsightsPromptContributions.default({
3315
+ estimatedTokens: 0,
3316
+ utf8Bytes: 0,
3317
+ coveredCalls: 0,
3318
+ totalCalls: 0,
3319
+ sources: [],
3320
+ }),
2843
3321
  warmSeconds: z.number().nonnegative(),
2844
3322
  priorWarmSeconds: z.number().nonnegative(),
2845
3323
  warmGroups: z.array(InsightsWarmGroupRow),
@@ -2988,6 +3466,11 @@ export const GitCredentialRepositoryRef = z.object({
2988
3466
  access: GitRepositoryAccess.optional(),
2989
3467
  uri: z.string().min(1),
2990
3468
  ref: z.string().min(1),
3469
+ /** Immutable commit the caller expects `ref` to materialize. */
3470
+ expectedCommitSha: z
3471
+ .string()
3472
+ .regex(/^[0-9a-f]{40}$/)
3473
+ .optional(),
2991
3474
  repositoryId: GitProviderRepositoryId.optional(),
2992
3475
  installationId: GitProviderRepositoryId.optional(),
2993
3476
  projectId: GitProviderRepositoryId.optional(),
@@ -3112,9 +3595,25 @@ export type GitCredentials = {
3112
3595
  export type SandboxSecretsRequest = {
3113
3596
  accountId: string;
3114
3597
  workspaceId: string;
3115
- // The variable set the run's session declares (null = unattached;
3116
- // the provider, like the self-mint path, returns null values for it).
3598
+ // Exact live attempt authority. Hosts MUST revalidate this tuple, the
3599
+ // session's selected resource identity, and any personal-resource grant
3600
+ // immediately before returning plaintext.
3601
+ sessionId: string;
3602
+ turnId: string;
3603
+ attemptId: string;
3604
+ executionGeneration: number;
3605
+ // Organization/workspace Variable Sets may be materialized by a pure
3606
+ // service turn (for example, the scheduler). Personal Variable Sets still
3607
+ // require this causal human and an exact personal-resource grant.
3608
+ initiator: TurnInitiator;
3609
+ initiatingHumanSubjectId: string | null;
3117
3610
  variableSetId: string;
3611
+ /**
3612
+ * Exact accepted scheduled-occurrence generation. Present only for scheduled
3613
+ * attempts; ordinary live turns omit it, so hosts that predate this field keep
3614
+ * working unchanged for those.
3615
+ */
3616
+ expectedGeneration?: number | null;
3118
3617
  };
3119
3618
 
3120
3619
  export type SandboxSecrets = {
@@ -3122,8 +3621,19 @@ export type SandboxSecrets = {
3122
3621
  // `environmentsEncryptionKeyBytes` decrypt. Same shape the self-mint path
3123
3622
  // produces (plaintext name→value).
3124
3623
  values: Record<string, string>;
3125
- // workspace-scope cross-check echo: the workspace the provider scoped these secrets to.
3624
+ // Exact scope/resource echoes are mandatory so a host routing bug cannot
3625
+ // inject a sibling tenant, resource, or stale attempt's plaintext.
3626
+ accountId: string;
3126
3627
  workspaceId: string;
3628
+ sessionId: string;
3629
+ turnId: string;
3630
+ attemptId: string;
3631
+ executionGeneration: number;
3632
+ variableSetId: string;
3633
+ /** Must echo the request's `expectedGeneration` when one was supplied. */
3634
+ expectedGeneration?: number | null;
3635
+ scope: VariableSetScope;
3636
+ generation: number;
3127
3637
  // Optional variableSet metadata; when omitted the activity uses the
3128
3638
  // variableSetId as both id and name (the local decrypt carries the row's
3129
3639
  // id/name/description, but only `id` is load-bearing downstream).
@@ -3287,12 +3797,127 @@ export const McpPersonalConnectionDelegation = z
3287
3797
  .object({
3288
3798
  serverId: z.string().min(1).max(256),
3289
3799
  connectionId: z.string().uuid(),
3800
+ /**
3801
+ * Immutable physical workspace that owns an activated common-user
3802
+ * connection. It is server-resolved from the selected authority and lets
3803
+ * provider adapters load only that exact row when the accepted turn runs
3804
+ * in another workspace of the same organization.
3805
+ */
3806
+ originWorkspaceId: z.string().uuid().optional(),
3290
3807
  ownerSubjectId: z.string().min(1).max(512),
3291
3808
  providerDomain: z.string().min(1).max(2048),
3292
3809
  kind: z.enum(["oauth2", "api_key", "app_install", "delegated"]).optional(),
3293
- connectionType: z.enum(["mcp", "social", "atlassian"]).optional(),
3810
+ connectionType: z.enum(["mcp", "social", "atlassian", "github_personal"]).optional(),
3811
+ /**
3812
+ * Explicit common-authority grant selected by the owning human. Omission is
3813
+ * retained only for legacy_user connections that cannot yet participate in
3814
+ * the organization-user authority lifecycle.
3815
+ */
3816
+ userDelegation: UserResourceDelegation.optional(),
3817
+ /**
3818
+ * Personal GitHub only: the exact repository authority frozen when this
3819
+ * accepted work was admitted. The credential binding is evidence, not an
3820
+ * authorization; physical provider use must revalidate every mutable
3821
+ * connection/grant/selection fence against this snapshot.
3822
+ */
3823
+ personalGitHubRepositorySelection: z
3824
+ .object({
3825
+ credentialBindingId: z.string().uuid(),
3826
+ connectionAuthorityGeneration: z.number().int().positive(),
3827
+ selectionGeneration: z.number().int().positive(),
3828
+ repositories: z
3829
+ .array(
3830
+ z
3831
+ .object({
3832
+ repositoryId: z.string().regex(/^[1-9]\d*$/u),
3833
+ fullName: z.string().min(3).max(140),
3834
+ canonicalUrl: z.string().url().max(512),
3835
+ ref: z.string().min(1).max(255),
3836
+ access: GitRepositoryAccess,
3837
+ selectionGeneration: z.number().int().positive(),
3838
+ })
3839
+ .strict(),
3840
+ )
3841
+ .min(1)
3842
+ .max(100),
3843
+ })
3844
+ .strict()
3845
+ .optional(),
3846
+ /**
3847
+ * Google Drive publication only: the exact output destination frozen when
3848
+ * this delegation was accepted, so a later connection-settings change can
3849
+ * never redirect an already-accepted turn's publication. Structurally
3850
+ * mirrors GoogleDriveOutputDestination (defined in ./google-drive, which
3851
+ * imports this module - hence the inline shape). Absent on pre-freeze
3852
+ * turns, which keep the bounded legacy live-resolution behavior.
3853
+ */
3854
+ outputDestination: z
3855
+ .object({
3856
+ folderId: z.string().min(1).max(256),
3857
+ folderName: z.string().min(1).max(1024),
3858
+ driveId: z.string().min(1).max(256).nullable(),
3859
+ location: z.enum(["my_drive", "shared_drive"]),
3860
+ selectedAt: z.string().datetime({ offset: true }),
3861
+ })
3862
+ .strict()
3863
+ .optional(),
3294
3864
  })
3295
- .strict();
3865
+ .strict()
3866
+ .superRefine((delegation, context) => {
3867
+ const personalGitHub = delegation.connectionType === "github_personal";
3868
+ if (personalGitHub !== (delegation.personalGitHubRepositorySelection !== undefined)) {
3869
+ context.addIssue({
3870
+ code: "custom",
3871
+ path: ["personalGitHubRepositorySelection"],
3872
+ message: "personal GitHub delegation requires one repository authority snapshot",
3873
+ });
3874
+ }
3875
+ if (!personalGitHub) return;
3876
+ if (
3877
+ delegation.serverId !== "github:personal" ||
3878
+ delegation.providerDomain !== "github.com" ||
3879
+ delegation.kind !== "oauth2" ||
3880
+ !delegation.originWorkspaceId ||
3881
+ !delegation.userDelegation
3882
+ ) {
3883
+ context.addIssue({
3884
+ code: "custom",
3885
+ message: "personal GitHub delegation requires exact user-owned connection authority",
3886
+ });
3887
+ }
3888
+ const repositories = delegation.personalGitHubRepositorySelection?.repositories ?? [];
3889
+ const repositoryIds = new Set<string>();
3890
+ const canonicalUrls = new Set<string>();
3891
+ repositories.forEach((repository, index) => {
3892
+ const canonicalUrl = repository.canonicalUrl.toLowerCase();
3893
+ if (
3894
+ !/^[A-Za-z0-9](?:[A-Za-z0-9-]{0,38})\/[A-Za-z0-9_.-]{1,100}$/u.test(repository.fullName) ||
3895
+ repository.canonicalUrl !== `https://github.com/${repository.fullName}`
3896
+ ) {
3897
+ context.addIssue({
3898
+ code: "custom",
3899
+ path: ["personalGitHubRepositorySelection", "repositories", index, "canonicalUrl"],
3900
+ message: "personal GitHub repository identity must be canonical",
3901
+ });
3902
+ }
3903
+ if (repositoryIds.has(repository.repositoryId)) {
3904
+ context.addIssue({
3905
+ code: "custom",
3906
+ path: ["personalGitHubRepositorySelection", "repositories", index, "repositoryId"],
3907
+ message: "personal GitHub repositories must be unique by provider id",
3908
+ });
3909
+ }
3910
+ if (canonicalUrls.has(canonicalUrl)) {
3911
+ context.addIssue({
3912
+ code: "custom",
3913
+ path: ["personalGitHubRepositorySelection", "repositories", index, "canonicalUrl"],
3914
+ message: "personal GitHub repositories must be unique by canonical URL",
3915
+ });
3916
+ }
3917
+ repositoryIds.add(repository.repositoryId);
3918
+ canonicalUrls.add(canonicalUrl);
3919
+ });
3920
+ });
3296
3921
  export type McpPersonalConnectionDelegation = z.infer<typeof McpPersonalConnectionDelegation>;
3297
3922
 
3298
3923
  /**
@@ -3317,12 +3942,44 @@ export const McpPersonalConnectionDelegations = z
3317
3942
  }
3318
3943
  });
3319
3944
 
3320
- export const McpPersonalConnectionSummary = McpPersonalConnectionDelegation.pick({
3321
- serverId: true,
3322
- providerDomain: true,
3323
- });
3945
+ export const McpPersonalConnectionSummary = z
3946
+ .object({
3947
+ serverId: z.string().min(1).max(256),
3948
+ providerDomain: z.string().min(1).max(2048),
3949
+ })
3950
+ .strict();
3324
3951
  export type McpPersonalConnectionSummary = z.infer<typeof McpPersonalConnectionSummary>;
3325
3952
 
3953
+ /**
3954
+ * Public, credential-free selection of one exact personal connection grant for
3955
+ * one selected MCP/first-party server. The owner is always derived server-side.
3956
+ */
3957
+ export const McpConnectionAuthoritySelection = z
3958
+ .object({
3959
+ serverId: z.string().min(1).max(256),
3960
+ connectionId: z.string().uuid(),
3961
+ userDelegation: UserResourceDelegation,
3962
+ })
3963
+ .strict();
3964
+ export type McpConnectionAuthoritySelection = z.infer<typeof McpConnectionAuthoritySelection>;
3965
+
3966
+ export const McpConnectionAuthoritySelections = z
3967
+ .array(McpConnectionAuthoritySelection)
3968
+ .max(128)
3969
+ .superRefine((selections, context) => {
3970
+ const seen = new Set<string>();
3971
+ for (const [index, selection] of selections.entries()) {
3972
+ if (seen.has(selection.serverId)) {
3973
+ context.addIssue({
3974
+ code: "custom",
3975
+ message: "connection authority selections must be unique by serverId",
3976
+ path: [index, "serverId"],
3977
+ });
3978
+ }
3979
+ seen.add(selection.serverId);
3980
+ }
3981
+ });
3982
+
3326
3983
  export type McpCredentialsRequest = {
3327
3984
  accountId: string;
3328
3985
  workspaceId: string;
@@ -3353,6 +4010,10 @@ export type McpCredentialsRequest = {
3353
4010
  toolName?: string;
3354
4011
  connectionRef: McpServerConnectionRef;
3355
4012
  forceRefresh: boolean;
4013
+ /** Canonical credential-free authority frozen on the accepted turn. */
4014
+ connectionUseAuthority?: unknown;
4015
+ /** Stable idempotency key for this one physical provider request. */
4016
+ connectionUseRequestId?: string;
3356
4017
  };
3357
4018
 
3358
4019
  export type McpCredentialAuthNeededReason =
@@ -3551,13 +4212,35 @@ export const CreateCheckoutResponse = z.object({
3551
4212
  });
3552
4213
  export type CreateCheckoutResponse = z.infer<typeof CreateCheckoutResponse>;
3553
4214
 
4215
+ export const CreateBillingPortalRequest = z.object({
4216
+ accountId: z.string().uuid().optional(),
4217
+ returnUrl: z.string().url().optional(),
4218
+ });
4219
+ export type CreateBillingPortalRequest = z.infer<typeof CreateBillingPortalRequest>;
4220
+
4221
+ export const CreateBillingPortalResponse = z.object({
4222
+ portalSessionId: z.string(),
4223
+ url: z.string().url(),
4224
+ });
4225
+ export type CreateBillingPortalResponse = z.infer<typeof CreateBillingPortalResponse>;
4226
+
3554
4227
  export const RepositoryResourceRef = z.object({
3555
4228
  kind: z.literal("repository"),
3556
4229
  uri: z.string().min(1),
3557
4230
  ref: z.string().min(1),
4231
+ /**
4232
+ * Optional immutable Git object fence. Repository materialization must fail
4233
+ * when the checked-out HEAD is not this exact commit. Event-driven sessions
4234
+ * use it to prevent a mutable PR branch from changing underneath a review.
4235
+ */
4236
+ expectedCommitSha: z
4237
+ .string()
4238
+ .regex(/^[0-9a-f]{40}$/)
4239
+ .optional(),
3558
4240
  mountPath: z.string().min(1).optional(),
3559
4241
  subpath: z.string().min(1).optional(),
3560
4242
  provider: GitCredentialProvider.optional(),
4243
+ connectionType: z.literal("github_personal").optional(),
3561
4244
  credentialBindingId: GitCredentialBindingId.optional(),
3562
4245
  access: GitRepositoryAccess.optional(),
3563
4246
  repositoryId: GitProviderRepositoryId.optional(),
@@ -3717,6 +4400,8 @@ export type FileResourceRef = z.infer<typeof FileResourceRef>;
3717
4400
  * reconstruct the same typed attachment input after a model switch or retry.
3718
4401
  */
3719
4402
  export const MODEL_ATTACHMENT_REFS_FIELD = "opengeni_attachment_refs" as const;
4403
+ /** Private marker for the compact attachment-reference carrier created by compaction. */
4404
+ export const MODEL_ATTACHMENT_CATALOG_MARKER = "opengeni_attachment_catalog" as const;
3720
4405
  /**
3721
4406
  * Structured timeline annotations retained beside the deterministic user-text
3722
4407
  * projection in canonical history. Provider adapters remove this OpenGeni
@@ -3816,6 +4501,25 @@ export function defaultRepositoryMountPath(
3816
4501
  );
3817
4502
  }
3818
4503
 
4504
+ /** Durable SDK/UI artifact root. Provisioned boxes also mount this path;
4505
+ * Connected Machine execution does not treat it as an alias. */
4506
+ export const VIRTUAL_WORKSPACE_ROOT = "/workspace" as const;
4507
+
4508
+ /**
4509
+ * Cwd-relative path for shell/exec prompts. Durable receipts and `sandbox:` UI
4510
+ * links keep the durable `/workspace/...` form. Tool receipts use the relative
4511
+ * projection so they remain usable from either a provisioned box root or a
4512
+ * Connected Machine's truthful host-native cwd.
4513
+ */
4514
+ export function sandboxShellPath(virtualPath: string): string {
4515
+ if (virtualPath === VIRTUAL_WORKSPACE_ROOT) return ".";
4516
+ if (virtualPath.startsWith(`${VIRTUAL_WORKSPACE_ROOT}/`)) {
4517
+ const relative = virtualPath.slice(VIRTUAL_WORKSPACE_ROOT.length + 1);
4518
+ return relative.length > 0 ? relative : ".";
4519
+ }
4520
+ return virtualPath;
4521
+ }
4522
+
3819
4523
  /** Resolve the exact mount used by API normalization, manifests, and clone hooks. */
3820
4524
  export const DEFAULT_FILE_RESOURCE_MOUNT_ROOT = ".opengeni/files" as const;
3821
4525
 
@@ -3984,6 +4688,9 @@ export const Document = z.object({
3984
4688
  authorityKind: DocumentAuthorityKind,
3985
4689
  authorityWorkspaceId: z.string().uuid().nullable(),
3986
4690
  authoritySubjectId: z.string().nullable(),
4691
+ // Opaque handle used by the owning human to issue explicit personal-scope
4692
+ // grants. Organization/workspace and legacy anchored personal rows are null.
4693
+ authorityId: z.string().uuid().nullable().optional(),
3987
4694
  visibility: DocumentVisibility,
3988
4695
  createdBy: z.string().nullable(),
3989
4696
  agentAccess: z.boolean(),
@@ -4025,6 +4732,7 @@ export const DocumentSearchResult = z.object({
4025
4732
  authorityKind: DocumentAuthorityKind,
4026
4733
  authorityWorkspaceId: z.string().uuid().nullable(),
4027
4734
  authoritySubjectId: z.string().nullable(),
4735
+ citation: KnowledgeProviderCitation.nullable().optional(),
4028
4736
  });
4029
4737
  export type DocumentSearchResult = z.infer<typeof DocumentSearchResult>;
4030
4738
 
@@ -4054,6 +4762,7 @@ export const IndexedDocumentProvenance = z.object({
4054
4762
  authoritySubjectId: z.string().nullable(),
4055
4763
  createdBy: z.string().nullable(),
4056
4764
  createdAt: z.string(),
4765
+ citation: KnowledgeProviderCitation.nullable().optional(),
4057
4766
  });
4058
4767
  export type IndexedDocumentProvenance = z.infer<typeof IndexedDocumentProvenance>;
4059
4768
 
@@ -4134,6 +4843,77 @@ export const MoveDocumentRequest = z.object({
4134
4843
  });
4135
4844
  export type MoveDocumentRequest = z.infer<typeof MoveDocumentRequest>;
4136
4845
 
4846
+ export const DocumentAuthorityTuple = z.object({
4847
+ kind: DocumentAuthorityKind,
4848
+ workspaceId: z.string().uuid().nullable(),
4849
+ subjectId: z.string().nullable(),
4850
+ authorityId: z.string().uuid().nullable(),
4851
+ });
4852
+ export type DocumentAuthorityTuple = z.infer<typeof DocumentAuthorityTuple>;
4853
+
4854
+ export const ReclassifyDocumentAuthorityRequest = z.object({
4855
+ operationId: z.string().uuid(),
4856
+ expectedAuthority: DocumentAuthorityTuple,
4857
+ targetAuthorityKind: DocumentAuthorityKind,
4858
+ });
4859
+ export type ReclassifyDocumentAuthorityRequest = z.infer<typeof ReclassifyDocumentAuthorityRequest>;
4860
+
4861
+ export const DocumentAuthorityReclassification = z.object({
4862
+ operationId: z.string().uuid(),
4863
+ documentId: z.string().uuid(),
4864
+ previousAuthority: DocumentAuthorityTuple,
4865
+ authority: DocumentAuthorityTuple,
4866
+ createdAt: z.string().datetime({ offset: true }),
4867
+ });
4868
+ export type DocumentAuthorityReclassification = z.infer<typeof DocumentAuthorityReclassification>;
4869
+
4870
+ export const DOCUMENT_AUTHORITY_RECLASSIFICATION_LIST_DEFAULT_LIMIT = 50;
4871
+ export const DOCUMENT_AUTHORITY_RECLASSIFICATION_LIST_MAX_LIMIT = 100;
4872
+ export const DOCUMENT_AUTHORITY_RECLASSIFICATION_CURSOR_MAX_CHARS = 1_024;
4873
+
4874
+ export const ListDocumentAuthorityReclassificationsQuery = z.object({
4875
+ limit: z.coerce
4876
+ .number()
4877
+ .int()
4878
+ .positive()
4879
+ .max(DOCUMENT_AUTHORITY_RECLASSIFICATION_LIST_MAX_LIMIT)
4880
+ .default(DOCUMENT_AUTHORITY_RECLASSIFICATION_LIST_DEFAULT_LIMIT),
4881
+ cursor: z.string().min(1).max(DOCUMENT_AUTHORITY_RECLASSIFICATION_CURSOR_MAX_CHARS).optional(),
4882
+ });
4883
+ export type ListDocumentAuthorityReclassificationsQuery = z.infer<
4884
+ typeof ListDocumentAuthorityReclassificationsQuery
4885
+ >;
4886
+
4887
+ export const ListDocumentAuthorityReclassificationsResponse = z.object({
4888
+ receipts: z.array(DocumentAuthorityReclassification),
4889
+ hasMore: z.boolean(),
4890
+ nextCursor: z.string().max(DOCUMENT_AUTHORITY_RECLASSIFICATION_CURSOR_MAX_CHARS).nullable(),
4891
+ });
4892
+ export type ListDocumentAuthorityReclassificationsResponse = z.infer<
4893
+ typeof ListDocumentAuthorityReclassificationsResponse
4894
+ >;
4895
+
4896
+ export const RunDocumentDefaultCollectionBackfillRequest = z.object({
4897
+ runId: z.string().uuid(),
4898
+ operationId: z.string().uuid(),
4899
+ batchSize: z.number().int().min(1).max(100).default(50),
4900
+ });
4901
+ export type RunDocumentDefaultCollectionBackfillRequest = z.infer<
4902
+ typeof RunDocumentDefaultCollectionBackfillRequest
4903
+ >;
4904
+
4905
+ export const DocumentDefaultCollectionBackfill = z.object({
4906
+ runId: z.string().uuid(),
4907
+ operationId: z.string().uuid(),
4908
+ status: z.enum(["running", "completed"]),
4909
+ lastWorkspaceId: z.string().uuid().nullable(),
4910
+ processedCount: z.number().int().nonnegative(),
4911
+ createdCount: z.number().int().nonnegative(),
4912
+ adoptedCount: z.number().int().nonnegative(),
4913
+ completedAt: z.string().datetime({ offset: true }).nullable(),
4914
+ });
4915
+ export type DocumentDefaultCollectionBackfill = z.infer<typeof DocumentDefaultCollectionBackfill>;
4916
+
4137
4917
  export const DocumentSearchRequest = z.object({
4138
4918
  query: z.string().min(1),
4139
4919
  baseIds: z.array(z.string().uuid()).optional(),
@@ -4279,9 +5059,15 @@ export type WorkspaceMemorySearchResponse = z.infer<typeof WorkspaceMemorySearch
4279
5059
  export const ToolRef = z.object({
4280
5060
  kind: z.literal("mcp"),
4281
5061
  id: z.string().min(1),
5062
+ // Session-scoped startup choice. `true` prepares this server's authorized
5063
+ // schemas before the first model request; absent/false keeps it behind
5064
+ // progressive discovery. This never grants a server or tool permission.
5065
+ eager: z.boolean().optional(),
4282
5066
  // Non-fatal-on-connect marker for MCP server refs that can degrade
4283
5067
  // gracefully. On new input, absent/false is STRICT: the id must be configured
4284
- // and an unavailable registered server fails the turn. Persisted refs are
5068
+ // and an unavailable registered server fails closed when its preparation is
5069
+ // demanded. Only the independent eager marker makes that a startup failure.
5070
+ // Persisted refs are
4285
5071
  // intersected with the current registry at each turn boundary, so a server
4286
5072
  // disconnected after admission is retained in policy/audit truth but skipped
4287
5073
  // until it is registered again. `optional:true` additionally makes runtime
@@ -4478,10 +5264,14 @@ export function mergeToolRefs(existing: ToolRef[], additions: ToolRef[]): ToolRe
4478
5264
  // strict occurrence upgrades the merged ref so an unavailable server fails
4479
5265
  // the turn. This preserves the fail-loud default when defaults, packs, and
4480
5266
  // per-turn tool selections are combined.
4481
- if (prior.optional === true && tool.optional !== true) {
4482
- const { optional: _dropped, ...strict } = prior;
4483
- byKey.set(key, strict);
4484
- }
5267
+ const optional = prior.optional === true && tool.optional === true ? true : undefined;
5268
+ const eager = prior.eager === true || tool.eager === true ? true : undefined;
5269
+ byKey.set(key, {
5270
+ kind: "mcp",
5271
+ id: prior.id,
5272
+ ...(optional ? { optional } : {}),
5273
+ ...(eager ? { eager } : {}),
5274
+ });
4485
5275
  }
4486
5276
  return order.map((key) => byKey.get(key)!);
4487
5277
  }
@@ -4635,6 +5425,9 @@ export const SESSION_GOAL_TEXT_MAX_BYTES = 8 * 1024;
4635
5425
  export const SESSION_GOAL_SUCCESS_CRITERIA_MAX_BYTES = 8 * 1024;
4636
5426
  export const SESSION_GOAL_RATIONALE_MAX_BYTES = 2 * 1024;
4637
5427
  export const SESSION_GOAL_PROGRESS_MAX_BYTES = 4 * 1024;
5428
+ export const SESSION_GOAL_ROOT_CONSTRAINT_MAX_BYTES = 512;
5429
+ export const SESSION_GOAL_ROOT_CONSTRAINTS_MAX_BYTES = 4 * 1024;
5430
+ export const SESSION_GOAL_ROOT_CONSTRAINTS_MAX_ITEMS = 16;
4638
5431
 
4639
5432
  export function sessionGoalUtf8Bytes(value: string): number {
4640
5433
  return new TextEncoder().encode(value).byteLength;
@@ -4649,6 +5442,43 @@ function boundedSessionGoalString(maxBytes: number, field: string) {
4649
5442
  });
4650
5443
  }
4651
5444
 
5445
+ function compareUtf8(left: string, right: string): number {
5446
+ const encoder = new TextEncoder();
5447
+ const leftBytes = encoder.encode(left);
5448
+ const rightBytes = encoder.encode(right);
5449
+ const length = Math.min(leftBytes.length, rightBytes.length);
5450
+ for (let index = 0; index < length; index += 1) {
5451
+ const difference = leftBytes[index]! - rightBytes[index]!;
5452
+ if (difference !== 0) return difference;
5453
+ }
5454
+ return leftBytes.length - rightBytes.length;
5455
+ }
5456
+
5457
+ export function normalizeSessionGoalRootConstraints(values: readonly string[]): string[] {
5458
+ // PostgreSQL btrim(text) removes U+0020 at both ends. Keep the public
5459
+ // projector byte-equivalent with the storage trigger instead of using
5460
+ // JavaScript trim(), whose Unicode whitespace set is broader.
5461
+ return [...new Set(values.map((value) => value.replace(/^ +| +$/g, "")))].sort(compareUtf8);
5462
+ }
5463
+
5464
+ export const SessionGoalRootConstraintsWrite = z
5465
+ .array(boundedSessionGoalString(SESSION_GOAL_ROOT_CONSTRAINT_MAX_BYTES, "goal root constraint"))
5466
+ .transform(normalizeSessionGoalRootConstraints)
5467
+ .pipe(
5468
+ z
5469
+ .array(z.string().min(1))
5470
+ .max(SESSION_GOAL_ROOT_CONSTRAINTS_MAX_ITEMS)
5471
+ .refine(
5472
+ (values) =>
5473
+ values.reduce((total, value) => total + sessionGoalUtf8Bytes(value), 0) <=
5474
+ SESSION_GOAL_ROOT_CONSTRAINTS_MAX_BYTES,
5475
+ {
5476
+ message: `goal root constraints exceed ${SESSION_GOAL_ROOT_CONSTRAINTS_MAX_BYTES} aggregate UTF-8 bytes`,
5477
+ },
5478
+ ),
5479
+ );
5480
+ export type SessionGoalRootConstraintsWrite = z.infer<typeof SessionGoalRootConstraintsWrite>;
5481
+
4652
5482
  const SessionGoalTextWrite = boundedSessionGoalString(SESSION_GOAL_TEXT_MAX_BYTES, "goal text");
4653
5483
  const SessionGoalSuccessCriteriaWrite = boundedSessionGoalString(
4654
5484
  SESSION_GOAL_SUCCESS_CRITERIA_MAX_BYTES,
@@ -4667,6 +5497,7 @@ export const SessionGoalSnapshot = z.discriminatedUnion("state", [
4667
5497
  objectiveRevision: z.number().int().positive(),
4668
5498
  text: SessionGoalTextWrite,
4669
5499
  successCriteria: SessionGoalSuccessCriteriaWrite.nullable(),
5500
+ rootConstraints: SessionGoalRootConstraintsWrite.default([]),
4670
5501
  mutationPolicy: SessionGoalMutationPolicy,
4671
5502
  capturedAt: z.string(),
4672
5503
  }),
@@ -4685,22 +5516,57 @@ export const SessionGoalRevision = z.object({
4685
5516
  resultObjectiveRevision: z.number().int().positive().nullable(),
4686
5517
  text: z.string().min(1),
4687
5518
  successCriteria: z.string().nullable(),
5519
+ rootConstraints: z.array(z.string().min(1)).default([]),
4688
5520
  mutationPolicy: SessionGoalMutationPolicy,
4689
5521
  rationale: z.string().min(1),
4690
5522
  actor: z.enum(["agent", "api", "scheduled_task"]),
4691
5523
  actorTurnId: z.string().uuid().nullable(),
4692
5524
  actorAttemptId: z.string().uuid().nullable(),
4693
5525
  proposalId: z.string().uuid().nullable(),
5526
+ rollbackOfRevisionId: z.string().uuid().nullable(),
4694
5527
  createdAt: z.string(),
4695
5528
  });
4696
5529
  export type SessionGoalRevision = z.infer<typeof SessionGoalRevision>;
4697
5530
 
5531
+ export const SESSION_GOAL_REVISION_LIST_DEFAULT_LIMIT = 50;
5532
+ export const SESSION_GOAL_REVISION_LIST_MAX_LIMIT = 100;
5533
+
5534
+ export const ListSessionGoalRevisionsQuery = z.object({
5535
+ limit: z.coerce
5536
+ .number()
5537
+ .int()
5538
+ .positive()
5539
+ .max(SESSION_GOAL_REVISION_LIST_MAX_LIMIT)
5540
+ .default(SESSION_GOAL_REVISION_LIST_DEFAULT_LIMIT),
5541
+ before: z.string().uuid().optional(),
5542
+ });
5543
+ export type ListSessionGoalRevisionsQuery = z.infer<typeof ListSessionGoalRevisionsQuery>;
5544
+
5545
+ export const ListSessionGoalRevisionsResponse = z.object({
5546
+ revisions: z.array(SessionGoalRevision),
5547
+ hasMore: z.boolean(),
5548
+ nextCursor: z.string().uuid().nullable(),
5549
+ });
5550
+ export type ListSessionGoalRevisionsResponse = z.infer<typeof ListSessionGoalRevisionsResponse>;
5551
+
4698
5552
  export const ApplySessionGoalRevisionRequest = z.object({
4699
5553
  expectedObjectiveRevision: z.number().int().positive(),
4700
5554
  rationale: SessionGoalRationaleWrite.optional(),
4701
5555
  });
4702
5556
  export type ApplySessionGoalRevisionRequest = z.infer<typeof ApplySessionGoalRevisionRequest>;
4703
5557
 
5558
+ export const RejectSessionGoalRevisionRequest = z.object({
5559
+ expectedObjectiveRevision: z.number().int().positive(),
5560
+ rationale: SessionGoalRationaleWrite,
5561
+ });
5562
+ export type RejectSessionGoalRevisionRequest = z.infer<typeof RejectSessionGoalRevisionRequest>;
5563
+
5564
+ export const RollbackSessionGoalRevisionRequest = z.object({
5565
+ expectedObjectiveRevision: z.number().int().positive(),
5566
+ rationale: SessionGoalRationaleWrite,
5567
+ });
5568
+ export type RollbackSessionGoalRevisionRequest = z.infer<typeof RollbackSessionGoalRevisionRequest>;
5569
+
4704
5570
  export const SessionGoalPausedReason = z.enum([
4705
5571
  "agent",
4706
5572
  "user_pause",
@@ -4732,10 +5598,41 @@ export const SessionGoalContinuationReason = z.enum([
4732
5598
  "provider_backpressure",
4733
5599
  "session_cancelled",
4734
5600
  "system_work_pending",
5601
+ "held_for_input",
5602
+ // Idle backoff: consecutive no-input continuations are paced, and the next
5603
+ // evaluation is armed as a delayed workflow wake at `nextAttemptAt`.
5604
+ "backoff_pending",
4735
5605
  "missing_obligation",
4736
5606
  ]);
4737
5607
  export type SessionGoalContinuationReason = z.infer<typeof SessionGoalContinuationReason>;
4738
5608
 
5609
+ /**
5610
+ * Why a paused goal became active again. `api` is the operator PATCH; the
5611
+ * system resumes only a `max_auto_continuations` pause, and only because new
5612
+ * external input arrived: a child result, scheduled occurrence, media result,
5613
+ * Agent message, Agent Steer, or human/API Send/Steer. The cap is pacing,
5614
+ * never user intent, so a `user_pause`/`api`/`agent`/`limits` pause is never
5615
+ * auto-resumed.
5616
+ */
5617
+ export const SessionGoalResumedReason = z.enum(["api", "external_input"]);
5618
+ export type SessionGoalResumedReason = z.infer<typeof SessionGoalResumedReason>;
5619
+
5620
+ export const SessionGoalResumedEventPayload = z
5621
+ .object({
5622
+ goalId: z.string().uuid(),
5623
+ actor: z.enum(["api", "system"]),
5624
+ reason: SessionGoalResumedReason,
5625
+ cause: z
5626
+ .object({
5627
+ kind: z.string().min(1),
5628
+ updateId: z.string().uuid().optional(),
5629
+ turnId: z.string().uuid().optional(),
5630
+ })
5631
+ .optional(),
5632
+ })
5633
+ .passthrough();
5634
+ export type SessionGoalResumedEventPayload = z.infer<typeof SessionGoalResumedEventPayload>;
5635
+
4739
5636
  export const SessionGoalContinuation = z.object({
4740
5637
  state: SessionGoalContinuationState,
4741
5638
  reason: SessionGoalContinuationReason,
@@ -4743,6 +5640,12 @@ export const SessionGoalContinuation = z.object({
4743
5640
  observedRevision: z.number().int().nonnegative(),
4744
5641
  nextAttemptAt: z.string().datetime({ offset: true }).nullable(),
4745
5642
  lastError: z.string().nullable(),
5643
+ /**
5644
+ * The agent's stated reason for a `held_for_input` hold (`goal_wait`), so a
5645
+ * human can see why the goal is waiting and until when (`nextAttemptAt`).
5646
+ * Null for every other state; omitted by older servers.
5647
+ */
5648
+ holdReason: z.string().nullable().optional(),
4746
5649
  });
4747
5650
  export type SessionGoalContinuation = z.infer<typeof SessionGoalContinuation>;
4748
5651
 
@@ -4754,6 +5657,7 @@ export const SessionGoal = z.object({
4754
5657
  status: SessionGoalStatus,
4755
5658
  text: z.string(),
4756
5659
  successCriteria: z.string().nullable(),
5660
+ rootConstraints: z.array(z.string().min(1)).default([]),
4757
5661
  evidence: z.string().nullable(),
4758
5662
  rationale: z.string().nullable(),
4759
5663
  pausedReason: z.string().nullable(),
@@ -4776,6 +5680,7 @@ export type SessionGoal = z.infer<typeof SessionGoal>;
4776
5680
  export const GoalSpec = z.object({
4777
5681
  text: SessionGoalTextWrite,
4778
5682
  successCriteria: SessionGoalSuccessCriteriaWrite.optional(),
5683
+ rootConstraints: SessionGoalRootConstraintsWrite.optional(),
4779
5684
  maxAutoContinuations: z.number().int().positive().optional(),
4780
5685
  mutationPolicy: SessionGoalMutationPolicy.optional(),
4781
5686
  });
@@ -4789,6 +5694,7 @@ export const UpdateSessionGoalRequest = z.union([
4789
5694
  z.object({
4790
5695
  text: SessionGoalTextWrite,
4791
5696
  successCriteria: SessionGoalSuccessCriteriaWrite.nullable().optional(),
5697
+ rootConstraints: SessionGoalRootConstraintsWrite.optional(),
4792
5698
  mutationPolicy: SessionGoalMutationPolicy.optional(),
4793
5699
  rationale: SessionGoalRationaleWrite,
4794
5700
  expectedObjectiveRevision: z.number().int().positive(),
@@ -4836,6 +5742,40 @@ export const UpdateSessionPinRequest = z.object({
4836
5742
  });
4837
5743
  export type UpdateSessionPinRequest = z.infer<typeof UpdateSessionPinRequest>;
4838
5744
 
5745
+ /**
5746
+ * A member's durable follow-up state for one session. Reading the session does
5747
+ * not acknowledge it: `unread` changes only through this explicit mutation.
5748
+ * A foreground reader may provide `acknowledgedThroughSequence` with
5749
+ * `unread: false` so later server events remain unread instead of being
5750
+ * consumed by a delayed acknowledgement request.
5751
+ * `activelyWorking` is an independent personal label that survives read state.
5752
+ */
5753
+ export const UpdateSessionAttentionRequest = z
5754
+ .object({
5755
+ unread: z.boolean().optional(),
5756
+ acknowledgedThroughSequence: z.number().int().nonnegative().optional(),
5757
+ activelyWorking: z.boolean().optional(),
5758
+ expectedVersion: z.number().int().nonnegative().optional(),
5759
+ })
5760
+ .strict()
5761
+ .refine((value) => value.unread !== undefined || value.activelyWorking !== undefined, {
5762
+ message: "unread or activelyWorking is required",
5763
+ })
5764
+ .refine((value) => value.acknowledgedThroughSequence === undefined || value.unread === false, {
5765
+ message: "acknowledgedThroughSequence requires unread false",
5766
+ path: ["acknowledgedThroughSequence"],
5767
+ });
5768
+ export type UpdateSessionAttentionRequest = z.infer<typeof UpdateSessionAttentionRequest>;
5769
+
5770
+ /** A member's personal archive state for a root chat. */
5771
+ export const UpdateSessionArchiveRequest = z
5772
+ .object({
5773
+ archived: z.boolean(),
5774
+ expectedVersion: z.number().int().nonnegative().optional(),
5775
+ })
5776
+ .strict();
5777
+ export type UpdateSessionArchiveRequest = z.infer<typeof UpdateSessionArchiveRequest>;
5778
+
4839
5779
  // Operator context controls (slash-command palette: /clear, /compact). These
4840
5780
  // are session/operator actions, NOT a structured way to talk to the agent —
4841
5781
  // the human↔agent channel stays plain chat. Both require `sessions:control`.
@@ -4890,6 +5830,34 @@ export function isClearedRunStateBlob(serialized: string | null | undefined): bo
4890
5830
  }
4891
5831
  }
4892
5832
 
5833
+ /**
5834
+ * Sentinel written on a requires_action pause. Resume must not call
5835
+ * `RunState.fromString` on it; the open suffix on `session_pending_tool_calls`
5836
+ * plus paired history is the resume authority.
5837
+ */
5838
+ export const OPEN_SUFFIX_RUN_STATE_MARKER = "$opengeniOpenSuffix" as const;
5839
+
5840
+ /** Canonical sentinel serializedRunState for a requires_action pause. */
5841
+ export const OPEN_SUFFIX_RUN_STATE_BLOB = JSON.stringify({
5842
+ [OPEN_SUFFIX_RUN_STATE_MARKER]: true,
5843
+ });
5844
+
5845
+ export function isOpenSuffixRunStateBlob(serialized: string | null | undefined): boolean {
5846
+ if (!serialized) {
5847
+ return false;
5848
+ }
5849
+ try {
5850
+ const parsed = JSON.parse(serialized) as unknown;
5851
+ return (
5852
+ typeof parsed === "object" &&
5853
+ parsed !== null &&
5854
+ (parsed as Record<string, unknown>)[OPEN_SUFFIX_RUN_STATE_MARKER] === true
5855
+ );
5856
+ } catch {
5857
+ return false;
5858
+ }
5859
+ }
5860
+
4893
5861
  /** Trigger conversation compaction now. No body fields today (forward-room). */
4894
5862
  export const CompactSessionContextRequest = z.object({}).strict();
4895
5863
  export type CompactSessionContextRequest = z.infer<typeof CompactSessionContextRequest>;
@@ -5089,8 +6057,26 @@ export const SessionRealtimeInboundEntry = z
5089
6057
  delegationItemId: z.string().max(1024).nullable().optional(),
5090
6058
  text: z.string().max(131_072).nullable().optional(),
5091
6059
  payload: z.record(z.string(), z.unknown()).optional(),
6060
+ // Application context attached to the exact delegation/transcript message.
6061
+ // It is ordinary model-visible user-message content when materialized, not
6062
+ // a secret or instruction-authority boundary.
6063
+ modelContext: z.string().trim().min(1).max(32768).optional(),
5092
6064
  })
5093
- .strict();
6065
+ .strict()
6066
+ .superRefine((entry, context) => {
6067
+ if (
6068
+ entry.modelContext !== undefined &&
6069
+ entry.kind !== "delegation_call" &&
6070
+ entry.kind !== "user_transcript" &&
6071
+ entry.kind !== "assistant_transcript"
6072
+ ) {
6073
+ context.addIssue({
6074
+ code: "custom",
6075
+ path: ["modelContext"],
6076
+ message: "modelContext requires a delegation or finalized transcript entry",
6077
+ });
6078
+ }
6079
+ });
5094
6080
  export type SessionRealtimeInboundEntry = z.infer<typeof SessionRealtimeInboundEntry>;
5095
6081
 
5096
6082
  export const SyncSessionRealtimeLedgerRequest = SessionRealtimeOwnerProof.extend({
@@ -5153,7 +6139,12 @@ export type WorkspaceRealtimeModelCatalogResponse = z.infer<
5153
6139
  export const SessionRealtimeState = z.enum(["active", "ended"]);
5154
6140
  export type SessionRealtimeState = z.infer<typeof SessionRealtimeState>;
5155
6141
 
5156
- export const SessionRealtimeEndReason = z.enum(["user_stop", "browser_unload", "lease_expired"]);
6142
+ export const SessionRealtimeEndReason = z.enum([
6143
+ "user_stop",
6144
+ "browser_unload",
6145
+ "lease_expired",
6146
+ "authority_revoked",
6147
+ ]);
5157
6148
  export type SessionRealtimeEndReason = z.infer<typeof SessionRealtimeEndReason>;
5158
6149
 
5159
6150
  export const SessionRealtimeMode = z.object({
@@ -5221,6 +6212,9 @@ export const SessionAuthorizationOperation = z.enum([
5221
6212
  "session.secret.read",
5222
6213
  "session.codemode.call",
5223
6214
  "session.pin.write",
6215
+ "session.attention.write",
6216
+ "session.archive.write",
6217
+ "session.delete",
5224
6218
  "session.codex_account.write",
5225
6219
  "session.realtime.start",
5226
6220
  "session.realtime.control",
@@ -5237,6 +6231,7 @@ export const SessionAuthorizationOperation = z.enum([
5237
6231
  "session.child.create",
5238
6232
  "session.visibility.write",
5239
6233
  "session.fork.create",
6234
+ "session.personal_resource.grant",
5240
6235
  ]);
5241
6236
  export type SessionAuthorizationOperation = z.infer<typeof SessionAuthorizationOperation>;
5242
6237
 
@@ -5559,6 +6554,69 @@ export function renderTimelineAnnotationsForModel(
5559
6554
  ].join("\n");
5560
6555
  }
5561
6556
 
6557
+ export const MODEL_CONTEXT_LABEL = "[Application context attached to this user message]" as const;
6558
+ export const SESSION_GOAL_CONTEXT_LABEL =
6559
+ "[Session goal frozen when this turn was accepted]" as const;
6560
+
6561
+ /**
6562
+ * Render the exact goal authority frozen with one accepted logical turn. Goal
6563
+ * state belongs at the chronological input boundary, not in the mutable
6564
+ * Agent.instructions prefix.
6565
+ */
6566
+ export function renderSessionGoalContext(snapshot?: SessionGoalSnapshot): string | undefined {
6567
+ if (!snapshot || snapshot.state === "none") return undefined;
6568
+ const rootConstraints = snapshot.rootConstraints.length
6569
+ ? `\nRoot constraints (must remain satisfied):\n${snapshot.rootConstraints.map((constraint) => `- ${constraint}`).join("\n")}`
6570
+ : "";
6571
+ if (snapshot.state === "completed") {
6572
+ return `Previous session goal (frozen at logical-turn acceptance; objective revision ${snapshot.objectiveRevision}; status completed): ${snapshot.text}\nSuccess criteria: ${snapshot.successCriteria ?? "none specified"}.${rootConstraints} This goal is complete and remains as historical context. If the user provides a new long-running objective, create it with opengeni__goal_set; goal_update cannot revise a completed goal.`;
6573
+ }
6574
+ const policy =
6575
+ snapshot.mutationPolicy === "review_changes"
6576
+ ? "Semantic changes are proposals until a user applies them."
6577
+ : snapshot.mutationPolicy === "preserve_intent"
6578
+ ? "You may directly refine wording without changing intent; adaptations and replacements are proposals until a user applies them."
6579
+ : "You may autonomously refine, adapt, or replace the goal when explicit user direction or material new evidence justifies it.";
6580
+ return `Standing session goal (frozen at logical-turn acceptance; objective revision ${snapshot.objectiveRevision}; status ${snapshot.state}): ${snapshot.text}\nSuccess criteria: ${snapshot.successCriteria ?? "none specified"}.${rootConstraints}\nMutation policy: ${snapshot.mutationPolicy}. ${policy} Treat later ordinary messages as additional context unless they explicitly redirect this objective. Root constraints are user/API authority and cannot be widened, removed, or rewritten by an agent. Semantic goal changes use opengeni__goal_update with the expected objective revision, change kind, and rationale.`;
6581
+ }
6582
+
6583
+ /**
6584
+ * Build one canonical user-role message body. `modelContext` is ordinary
6585
+ * message content, while `goalSnapshot` is the exact goal authority frozen at
6586
+ * turn acceptance. Both remain in the visible message's chronological
6587
+ * position, and presentation layers may omit their leading parts.
6588
+ */
6589
+ export function renderUserMessageContentForModel(
6590
+ text: string,
6591
+ annotations: readonly TimelineAnnotation[],
6592
+ modelContext?: string | null,
6593
+ goalSnapshot?: SessionGoalSnapshot,
6594
+ ): string | Array<{ type: "input_text"; text: string }> {
6595
+ const visibleContent = renderTimelineAnnotationsForModel(text, annotations);
6596
+ const context = modelContext?.trim();
6597
+ const goalContext = renderSessionGoalContext(goalSnapshot);
6598
+ if (!context && !goalContext) return visibleContent;
6599
+ return [
6600
+ ...(goalContext
6601
+ ? [
6602
+ {
6603
+ type: "input_text" as const,
6604
+ text: `${SESSION_GOAL_CONTEXT_LABEL}\n${goalContext}`,
6605
+ },
6606
+ ]
6607
+ : []),
6608
+ ...(context
6609
+ ? [
6610
+ {
6611
+ type: "input_text" as const,
6612
+ text: `${MODEL_CONTEXT_LABEL}\n${context}`,
6613
+ },
6614
+ ]
6615
+ : []),
6616
+ { type: "input_text", text: visibleContent },
6617
+ ];
6618
+ }
6619
+
5562
6620
  export const SessionTurn = z
5563
6621
  .object({
5564
6622
  id: z.string().uuid(),
@@ -5579,7 +6637,7 @@ export const SessionTurn = z
5579
6637
  toolsProvided: z.boolean().optional(),
5580
6638
  model: z.string().min(1),
5581
6639
  reasoningEffort: ReasoningEffort,
5582
- latencyMode: LatencyMode.default("standard"),
6640
+ latencyMode: LatencyMode,
5583
6641
  sandboxBackend: SandboxBackend,
5584
6642
  // Per-turn OS override. NULL = inherit the session's sandboxOs.
5585
6643
  sandboxOs: SandboxOs.nullable(),
@@ -5592,6 +6650,8 @@ export const SessionTurn = z
5592
6650
  initiatorContext: TurnInitiatorContext,
5593
6651
  /** Secret-safe projection of the exact personal authority frozen on this turn. */
5594
6652
  personalConnections: z.array(McpPersonalConnectionSummary).default([]),
6653
+ /** Safe summary only; opaque resource/grant identity remains private. */
6654
+ personalResources: PersonalResourceAttachmentSummary.nullable().default(null),
5595
6655
  cancelledBy: z.string().nullable(),
5596
6656
  cancelReason: z.string().nullable(),
5597
6657
  startedAt: z.string().nullable(),
@@ -5653,10 +6713,18 @@ export const EffectiveSessionControl = z.object({
5653
6713
  quiescencePendingCount: z.number().int().nonnegative(),
5654
6714
  })
5655
6715
  .nullable(),
6716
+ /** Independent command-cancellation settlement. Session pause/turn state is
6717
+ * intentionally not overloaded with process lifecycle. */
6718
+ backgroundCommandSettlement: z
6719
+ .object({
6720
+ state: z.literal("stopping"),
6721
+ commandCount: z.number().int().positive(),
6722
+ })
6723
+ .nullable()
6724
+ .optional(),
5656
6725
  });
5657
6726
  export type EffectiveSessionControl = z.infer<typeof EffectiveSessionControl>;
5658
6727
 
5659
- export const SESSION_OPERATION_KEY_MAX_CHARS = 256;
5660
6728
  const SessionOperationKey = z.string().min(1).max(SESSION_OPERATION_KEY_MAX_CHARS);
5661
6729
 
5662
6730
  export const SessionCommandReceipt = z.object({
@@ -5673,6 +6741,14 @@ export const SessionCommandReceipt = z.object({
5673
6741
  });
5674
6742
  export type SessionCommandReceipt = z.infer<typeof SessionCommandReceipt>;
5675
6743
 
6744
+ /** The server-owned destination of an accepted human prompt at admission time. */
6745
+ export const SessionPromptRouting = z.enum([
6746
+ "accepted_for_execution",
6747
+ "queued_for_execution",
6748
+ "accepted_for_steering",
6749
+ ]);
6750
+ export type SessionPromptRouting = z.infer<typeof SessionPromptRouting>;
6751
+
5676
6752
  export const ComposerDraft = z.object({
5677
6753
  revision: z.number().int().nonnegative(),
5678
6754
  text: z.string(),
@@ -5680,7 +6756,7 @@ export const ComposerDraft = z.object({
5680
6756
  resources: z.array(ResourceRef),
5681
6757
  model: z.string().min(1),
5682
6758
  reasoningEffort: ReasoningEffort,
5683
- latencyMode: LatencyMode.default("standard"),
6759
+ latencyMode: LatencyMode,
5684
6760
  sourceTurnId: z.string().uuid().nullable(),
5685
6761
  sourceTurnVersion: z.number().int().positive().nullable(),
5686
6762
  updatedAt: z.string().nullable(),
@@ -5726,12 +6802,39 @@ export const SaveComposerDraftRequest = ComposerDraft.pick({
5726
6802
  }).extend({ expectedRevision: z.number().int().nonnegative() });
5727
6803
  export type SaveComposerDraftRequest = z.infer<typeof SaveComposerDraftRequest>;
5728
6804
 
6805
+ /**
6806
+ * Submit one exact established-session draft. Content is repeated only as an
6807
+ * integrity fence for outcome-unknown idempotent replay; the matching durable
6808
+ * draft revision remains authoritative and is atomically rotated on acceptance.
6809
+ */
6810
+ export const SubmitComposerDraftRequest = ComposerDraft.pick({
6811
+ text: true,
6812
+ annotations: true,
6813
+ resources: true,
6814
+ model: true,
6815
+ reasoningEffort: true,
6816
+ latencyMode: true,
6817
+ })
6818
+ .extend({
6819
+ expectedDraftRevision: z.number().int().positive(),
6820
+ clientEventId: SessionOperationKey,
6821
+ delivery: z.enum(["send", "steer"]),
6822
+ controlEtag: z.string().min(1).optional(),
6823
+ modelContext: z.string().trim().min(1).max(32768).optional(),
6824
+ mcpCredentialUpdates: z.array(SessionMcpCredentialUpdateInput).optional(),
6825
+ connectionAuthorities: McpConnectionAuthoritySelections.default([]),
6826
+ personalResourceAttachment: PersonalResourceAttachmentIntent.optional(),
6827
+ })
6828
+ .superRefine(requireEstablishedPersonalResourceEpoch);
6829
+ export type SubmitComposerDraftRequest = z.infer<typeof SubmitComposerDraftRequest>;
6830
+
5729
6831
  /**
5730
6832
  * Create-only options saved with an actor's private pre-session draft. This is
5731
6833
  * deliberately narrower than CreateSessionRequest: idempotency/event keys and
5732
6834
  * credential-bearing MCP server inputs are per-attempt data, never draft state.
5733
6835
  */
5734
6836
  export const NewSessionDraftOptions = z.object({
6837
+ visibility: SessionVisibility.optional(),
5735
6838
  sandboxBackend: SandboxBackend.optional(),
5736
6839
  targetSandboxId: z.string().uuid().optional(),
5737
6840
  workingDir: z.string().min(1).optional(),
@@ -5743,6 +6846,31 @@ export const NewSessionDraftOptions = z.object({
5743
6846
  });
5744
6847
  export type NewSessionDraftOptions = z.infer<typeof NewSessionDraftOptions>;
5745
6848
 
6849
+ /**
6850
+ * Actor-private successful-create history for the new-session composer. Project
6851
+ * entries and their machine entries are MRU ordered. A null target means the
6852
+ * managed sandbox; a null working directory means the machine's launch root.
6853
+ */
6854
+ export const NewSessionSelectionHistory = z.object({
6855
+ projects: z
6856
+ .array(
6857
+ z.object({
6858
+ channelId: z.string().uuid().nullable(),
6859
+ targetSandboxId: z.string().uuid().nullable(),
6860
+ machines: z
6861
+ .array(
6862
+ z.object({
6863
+ sandboxId: z.string().uuid(),
6864
+ workingDir: z.string().min(1).max(4096).nullable(),
6865
+ }),
6866
+ )
6867
+ .max(20),
6868
+ }),
6869
+ )
6870
+ .max(50),
6871
+ });
6872
+ export type NewSessionSelectionHistory = z.infer<typeof NewSessionSelectionHistory>;
6873
+
5746
6874
  /** Actor-private, server-authoritative composer state before a session exists. */
5747
6875
  export const NewSessionDraft = z.object({
5748
6876
  revision: z.number().int().nonnegative(),
@@ -5753,8 +6881,9 @@ export const NewSessionDraft = z.object({
5753
6881
  toolsProvided: z.boolean().default(false),
5754
6882
  model: z.string().min(1),
5755
6883
  reasoningEffort: ReasoningEffort,
5756
- latencyMode: LatencyMode.default("standard"),
6884
+ latencyMode: LatencyMode,
5757
6885
  options: NewSessionDraftOptions,
6886
+ selectionHistory: NewSessionSelectionHistory.default({ projects: [] }),
5758
6887
  updatedAt: z.string().nullable(),
5759
6888
  });
5760
6889
  export type NewSessionDraft = z.infer<typeof NewSessionDraft>;
@@ -5993,40 +7122,211 @@ export const SessionSystemUpdateKind = z.enum([
5993
7122
  "agent_steer_instruction",
5994
7123
  "child_terminal_result",
5995
7124
  "media_generation_result",
7125
+ "child_requires_action",
7126
+ "child_requires_action_resolved",
7127
+ "child_paused",
7128
+ "child_waiting_capacity",
7129
+ "child_progress",
5996
7130
  ]);
5997
7131
  export type SessionSystemUpdateKind = z.infer<typeof SessionSystemUpdateKind>;
5998
7132
 
5999
- export const SessionSystemUpdatePayload = z.discriminatedUnion("type", [
6000
- z
6001
- .object({
6002
- type: z.literal("scheduled_occurrence"),
6003
- text: z.string().min(1),
6004
- scheduledTaskId: z.string().uuid(),
6005
- scheduledTaskRunId: z.string().uuid(),
6006
- resources: z.array(ResourceRef).optional(),
6007
- tools: z.array(ToolRef).optional(),
6008
- })
6009
- .passthrough(),
6010
- z
6011
- .object({
6012
- type: z.literal("goal_continuation"),
6013
- goalId: z.string().uuid(),
6014
- goalVersion: z.number().int().positive(),
6015
- prompt: z.string().min(1),
6016
- reason: z.string().optional(),
6017
- })
6018
- .passthrough(),
6019
- z
6020
- .object({
6021
- type: z.literal("agent_message"),
6022
- text: z.string().min(1),
6023
- operationId: z.string().uuid(),
6024
- })
6025
- .passthrough(),
6026
- z
6027
- .object({
6028
- type: z.literal("agent_steer_instruction"),
6029
- instruction: z.string().min(1),
7133
+ /**
7134
+ * How a newly pending machine input affects an idle receiving session.
7135
+ * `immediate` registers a workflow wake in the same commit (the behaviour of
7136
+ * every pre-existing kind) and ends a `goal_wait` hold at the next idle
7137
+ * evaluation; `deferred` only inserts the durable pending row plus its
7138
+ * `system.update.pending` event and is delivered coalesced with the next claim.
7139
+ */
7140
+ export type SessionSystemUpdateWakeClass = "immediate" | "deferred";
7141
+
7142
+ export const SESSION_SYSTEM_UPDATE_WAKE_CLASS: Record<
7143
+ SessionSystemUpdateKind,
7144
+ SessionSystemUpdateWakeClass
7145
+ > = {
7146
+ scheduled_occurrence: "immediate",
7147
+ goal_continuation: "immediate",
7148
+ agent_message: "immediate",
7149
+ agent_steer_instruction: "immediate",
7150
+ child_terminal_result: "immediate",
7151
+ media_generation_result: "immediate",
7152
+ child_requires_action: "immediate",
7153
+ child_requires_action_resolved: "deferred",
7154
+ child_paused: "deferred",
7155
+ child_waiting_capacity: "deferred",
7156
+ child_progress: "deferred",
7157
+ };
7158
+
7159
+ /**
7160
+ * Kinds a child session's lifecycle produces for its parent. Every one of them
7161
+ * travels through `session_system_update_outbox`, and none of them may
7162
+ * autonomously wake a parent whose goal is not active.
7163
+ */
7164
+ export const CHILD_LIFECYCLE_SYSTEM_UPDATE_KINDS = [
7165
+ "child_terminal_result",
7166
+ "child_requires_action",
7167
+ "child_requires_action_resolved",
7168
+ "child_paused",
7169
+ "child_waiting_capacity",
7170
+ "child_progress",
7171
+ ] as const satisfies readonly SessionSystemUpdateKind[];
7172
+ export type ChildLifecycleSystemUpdateKind = (typeof CHILD_LIFECYCLE_SYSTEM_UPDATE_KINDS)[number];
7173
+
7174
+ const CHILD_LIFECYCLE_SYSTEM_UPDATE_KIND_SET: ReadonlySet<string> = new Set(
7175
+ CHILD_LIFECYCLE_SYSTEM_UPDATE_KINDS,
7176
+ );
7177
+
7178
+ export function isChildLifecycleSystemUpdateKind(
7179
+ kind: string,
7180
+ ): kind is ChildLifecycleSystemUpdateKind {
7181
+ return CHILD_LIFECYCLE_SYSTEM_UPDATE_KIND_SET.has(kind);
7182
+ }
7183
+
7184
+ /** A child_paused notice requested by a human/API is action-required; an agent pause is informational. */
7185
+ export function childPausedClassification(
7186
+ actorKind: "human" | "api" | "agent",
7187
+ ): "action_required" | "info" {
7188
+ return actorKind === "agent" ? "info" : "action_required";
7189
+ }
7190
+
7191
+ /** Whole child_requires_action payload bound (UTF-8 JSON bytes). */
7192
+ export const CHILD_REQUIRES_ACTION_PAYLOAD_MAX_BYTES = 8 * 1024;
7193
+ /** Bounded first-question preview inside a child_requires_action notice. */
7194
+ export const CHILD_REQUIRES_ACTION_QUESTION_PREVIEW_MAX_BYTES = 512;
7195
+ export const CHILD_REQUIRES_ACTION_MAX_REQUESTS = 20;
7196
+ export const CHILD_PAUSED_REASON_MAX_BYTES = 2 * 1024;
7197
+ export const CHILD_PROGRESS_NOTE_MAX_BYTES = 4 * 1024;
7198
+
7199
+ const boundedUtf8String = (maxBytes: number) =>
7200
+ z.string().refine((value) => new TextEncoder().encode(value).byteLength <= maxBytes, {
7201
+ message: `must be at most ${maxBytes} UTF-8 bytes`,
7202
+ });
7203
+
7204
+ export const ChildRequiresActionRequest = z.discriminatedUnion("kind", [
7205
+ z.object({
7206
+ kind: z.literal("human_input"),
7207
+ requestId: z.string().uuid(),
7208
+ questionCount: z.number().int().nonnegative(),
7209
+ firstQuestion: boundedUtf8String(CHILD_REQUIRES_ACTION_QUESTION_PREVIEW_MAX_BYTES),
7210
+ allowSkip: z.boolean(),
7211
+ expiresAt: z.string().nullable(),
7212
+ }),
7213
+ z.object({
7214
+ kind: z.literal("approval"),
7215
+ approvalId: boundedUtf8String(256),
7216
+ toolName: boundedUtf8String(128).nullable(),
7217
+ }),
7218
+ ]);
7219
+ export type ChildRequiresActionRequest = z.infer<typeof ChildRequiresActionRequest>;
7220
+
7221
+ export const ChildRequiresActionPayload = z
7222
+ .object({
7223
+ type: z.literal("child_requires_action"),
7224
+ childSessionId: z.string().uuid(),
7225
+ childTurnId: z.string().uuid(),
7226
+ childTurnGeneration: z.number().int().positive(),
7227
+ requests: z.array(ChildRequiresActionRequest).max(CHILD_REQUIRES_ACTION_MAX_REQUESTS),
7228
+ truncated: z.boolean(),
7229
+ })
7230
+ .passthrough();
7231
+ export type ChildRequiresActionPayload = z.infer<typeof ChildRequiresActionPayload>;
7232
+
7233
+ export const ChildRequiresActionResolvedOutcome = z.enum([
7234
+ "answered",
7235
+ "skipped",
7236
+ "expired",
7237
+ "cancelled",
7238
+ "approved",
7239
+ "rejected",
7240
+ ]);
7241
+ export type ChildRequiresActionResolvedOutcome = z.infer<typeof ChildRequiresActionResolvedOutcome>;
7242
+
7243
+ export const ChildRequiresActionRespondedByKind = z.enum([
7244
+ "human",
7245
+ "api",
7246
+ "agent_attempt",
7247
+ "system",
7248
+ ]);
7249
+ export type ChildRequiresActionRespondedByKind = z.infer<typeof ChildRequiresActionRespondedByKind>;
7250
+
7251
+ export const ChildRequiresActionResolvedPayload = z
7252
+ .object({
7253
+ type: z.literal("child_requires_action_resolved"),
7254
+ childSessionId: z.string().uuid(),
7255
+ childTurnId: z.string().uuid(),
7256
+ childTurnGeneration: z.number().int().positive(),
7257
+ requestId: z.string().uuid().nullable(),
7258
+ approvalId: boundedUtf8String(256).nullable(),
7259
+ outcome: ChildRequiresActionResolvedOutcome,
7260
+ respondedByKind: ChildRequiresActionRespondedByKind,
7261
+ })
7262
+ .passthrough();
7263
+ export type ChildRequiresActionResolvedPayload = z.infer<typeof ChildRequiresActionResolvedPayload>;
7264
+
7265
+ export const ChildPausedPayload = z
7266
+ .object({
7267
+ type: z.literal("child_paused"),
7268
+ childSessionId: z.string().uuid(),
7269
+ operationId: z.string().uuid(),
7270
+ actorKind: z.enum(["human", "api", "agent"]),
7271
+ reason: boundedUtf8String(CHILD_PAUSED_REASON_MAX_BYTES).nullable(),
7272
+ })
7273
+ .passthrough();
7274
+ export type ChildPausedPayload = z.infer<typeof ChildPausedPayload>;
7275
+
7276
+ export const ChildWaitingCapacityPayload = z
7277
+ .object({
7278
+ type: z.literal("child_waiting_capacity"),
7279
+ childSessionId: z.string().uuid(),
7280
+ childTurnId: z.string().uuid(),
7281
+ provider: z.enum(["codex", "xai"]),
7282
+ nextCheckAt: z.string().nullable(),
7283
+ })
7284
+ .passthrough();
7285
+ export type ChildWaitingCapacityPayload = z.infer<typeof ChildWaitingCapacityPayload>;
7286
+
7287
+ export const ChildProgressPayload = z
7288
+ .object({
7289
+ type: z.literal("child_progress"),
7290
+ childSessionId: z.string().uuid(),
7291
+ goalId: z.string().uuid(),
7292
+ objectiveRevision: z.number().int().nonnegative(),
7293
+ operationId: z.string().uuid(),
7294
+ progressNote: boundedUtf8String(CHILD_PROGRESS_NOTE_MAX_BYTES),
7295
+ })
7296
+ .passthrough();
7297
+ export type ChildProgressPayload = z.infer<typeof ChildProgressPayload>;
7298
+
7299
+ export const SessionSystemUpdatePayload = z.discriminatedUnion("type", [
7300
+ z
7301
+ .object({
7302
+ type: z.literal("scheduled_occurrence"),
7303
+ text: z.string().min(1),
7304
+ scheduledTaskId: z.string().uuid(),
7305
+ scheduledTaskRunId: z.string().uuid(),
7306
+ resources: z.array(ResourceRef).optional(),
7307
+ tools: z.array(ToolRef).optional(),
7308
+ })
7309
+ .passthrough(),
7310
+ z
7311
+ .object({
7312
+ type: z.literal("goal_continuation"),
7313
+ goalId: z.string().uuid(),
7314
+ goalVersion: z.number().int().positive(),
7315
+ prompt: z.string().min(1),
7316
+ reason: z.string().optional(),
7317
+ })
7318
+ .passthrough(),
7319
+ z
7320
+ .object({
7321
+ type: z.literal("agent_message"),
7322
+ text: z.string().min(1),
7323
+ operationId: z.string().uuid(),
7324
+ })
7325
+ .passthrough(),
7326
+ z
7327
+ .object({
7328
+ type: z.literal("agent_steer_instruction"),
7329
+ instruction: z.string().min(1),
6030
7330
  operationId: z.string().uuid(),
6031
7331
  })
6032
7332
  .passthrough(),
@@ -6038,6 +7338,11 @@ export const SessionSystemUpdatePayload = z.discriminatedUnion("type", [
6038
7338
  })
6039
7339
  .passthrough(),
6040
7340
  MediaGenerationResult,
7341
+ ChildRequiresActionPayload,
7342
+ ChildRequiresActionResolvedPayload,
7343
+ ChildPausedPayload,
7344
+ ChildWaitingCapacityPayload,
7345
+ ChildProgressPayload,
6041
7346
  ]);
6042
7347
  export type SessionSystemUpdatePayload = z.infer<typeof SessionSystemUpdatePayload>;
6043
7348
 
@@ -6154,11 +7459,16 @@ export function renderSessionSystemUpdateBatch(
6154
7459
 
6155
7460
  export function sessionSystemUpdateBatchHistoryItem(
6156
7461
  updates: Parameters<typeof renderSessionSystemUpdateBatch>[0],
7462
+ goalSnapshot?: SessionGoalSnapshot,
6157
7463
  ): { type: "message"; role: "system"; content: string } {
7464
+ const goalContext = renderSessionGoalContext(goalSnapshot);
6158
7465
  return {
6159
7466
  type: "message",
6160
7467
  role: "system",
6161
- content: renderSessionSystemUpdateBatch(updates),
7468
+ content: [
7469
+ ...(goalContext ? [`${SESSION_GOAL_CONTEXT_LABEL}\n${goalContext}`] : []),
7470
+ renderSessionSystemUpdateBatch(updates),
7471
+ ].join("\n\n"),
6162
7472
  };
6163
7473
  }
6164
7474
 
@@ -6168,12 +7478,16 @@ export const VariableSetVariableName = z
6168
7478
  .max(128);
6169
7479
  export type VariableSetVariableName = z.infer<typeof VariableSetVariableName>;
6170
7480
 
6171
- function withVariableSetIdAlias<T extends z.ZodRawShape>(shape: T) {
7481
+ function withVariableSetIdAlias<T extends z.ZodRawShape>(
7482
+ shape: T,
7483
+ options: { rejectKeys?: readonly string[] } = {},
7484
+ ) {
6172
7485
  return z.preprocess((input) => {
6173
7486
  if (!input || typeof input !== "object" || Array.isArray(input)) {
6174
7487
  return input;
6175
7488
  }
6176
7489
  const record = input as Record<string, unknown>;
7490
+ if (options.rejectKeys?.some((key) => Object.hasOwn(record, key))) return null;
6177
7491
  if (record.variableSetId !== undefined || record.environmentId === undefined) {
6178
7492
  return record;
6179
7493
  }
@@ -6204,10 +7518,16 @@ export const VariableSetSecret = z.object({
6204
7518
  });
6205
7519
  export type VariableSetSecret = z.infer<typeof VariableSetSecret>;
6206
7520
 
7521
+ export const VariableSetScope = z.enum(["organization", "workspace", "user"]);
7522
+ export type VariableSetScope = z.infer<typeof VariableSetScope>;
7523
+
6207
7524
  export const VariableSet = z.object({
6208
7525
  id: z.string().uuid(),
6209
7526
  accountId: z.string().uuid(),
6210
7527
  workspaceId: z.string().uuid(),
7528
+ scope: VariableSetScope,
7529
+ generation: z.number().int().positive(),
7530
+ status: z.enum(["active", "revoked"]),
6211
7531
  name: z.string(),
6212
7532
  description: z.string().nullable(),
6213
7533
  variables: z.array(VariableSetVariableMetadata),
@@ -6221,6 +7541,8 @@ export const WorkspaceEnvironment = VariableSet;
6221
7541
  export type WorkspaceEnvironment = VariableSet;
6222
7542
 
6223
7543
  export const CreateVariableSetRequest = z.object({
7544
+ // Omitted remains the legacy workspace-owned creation path.
7545
+ scope: VariableSetScope.default("workspace"),
6224
7546
  name: z.string().min(1).max(120),
6225
7547
  description: z.string().max(2000).optional(),
6226
7548
  variables: z
@@ -6419,6 +7741,9 @@ export const Rig = z.object({
6419
7741
  id: z.string().uuid(),
6420
7742
  accountId: z.string().uuid(),
6421
7743
  workspaceId: z.string().uuid(),
7744
+ scope: ResourceAuthorityScope,
7745
+ generation: z.number().int().positive(),
7746
+ status: z.enum(["active", "revoked"]),
6422
7747
  name: z.string(),
6423
7748
  description: z.string().nullable(),
6424
7749
  createdBy: z.string().nullable(),
@@ -6477,6 +7802,8 @@ export const RigChange = z.object({
6477
7802
  export type RigChange = z.infer<typeof RigChange>;
6478
7803
 
6479
7804
  export const CreateRigRequest = z.object({
7805
+ // Omitted remains the compatibility workspace-owned creation path.
7806
+ scope: ResourceAuthorityScope.default("workspace"),
6480
7807
  name: z.string().min(1).max(120),
6481
7808
  description: z.string().max(2000).optional(),
6482
7809
  // Initial (version 1) content, inline.
@@ -6503,6 +7830,8 @@ export const Channel = z.object({
6503
7830
  workspaceId: z.string().uuid(),
6504
7831
  name: z.string(),
6505
7832
  description: z.string().nullable(),
7833
+ pinned: z.boolean(),
7834
+ sortOrder: z.number().int().nonnegative(),
6506
7835
  createdBy: z.string().nullable(),
6507
7836
  createdAt: z.string(),
6508
7837
  updatedAt: z.string(),
@@ -6518,9 +7847,16 @@ export type CreateChannelRequest = z.infer<typeof CreateChannelRequest>;
6518
7847
  export const UpdateChannelRequest = z.object({
6519
7848
  name: z.string().trim().min(1).max(80).optional(),
6520
7849
  description: z.string().max(2000).nullable().optional(),
7850
+ pinned: z.boolean().optional(),
6521
7851
  });
6522
7852
  export type UpdateChannelRequest = z.infer<typeof UpdateChannelRequest>;
6523
7853
 
7854
+ /** Complete workspace project order. It is replaced atomically after a drag. */
7855
+ export const ReorderChannelsRequest = z
7856
+ .object({ channelIds: z.array(z.string().uuid()).min(1).max(200) })
7857
+ .strict();
7858
+ export type ReorderChannelsRequest = z.infer<typeof ReorderChannelsRequest>;
7859
+
6524
7860
  // Re-files one session (rail organization only). null moves it back to the
6525
7861
  // unfiled inbox.
6526
7862
  export const UpdateSessionChannelRequest = z.object({
@@ -6686,25 +8022,313 @@ export const ScheduledTaskScheduleSpec = /* @__PURE__ */ z.discriminatedUnion("t
6686
8022
  ]);
6687
8023
  export type ScheduledTaskScheduleSpec = z.infer<typeof ScheduledTaskScheduleSpec>;
6688
8024
 
6689
- export const ScheduledTaskAgentConfig = /* @__PURE__ */ z.object({
6690
- prompt: z.string().min(1),
6691
- resources: z.array(ResourceRef).default([]),
6692
- tools: z.array(ToolRef).default([]),
6693
- metadata: z.record(z.string(), z.unknown()).default({}),
6694
- // Explicit workspace-shared OpenGeni Slack bot binding for scheduled runs.
6695
- // The worker copies this non-secret pointer into session metadata; the
6696
- // first-party Slack tools never fall back to a personal hosted-MCP grant.
6697
- slackBotConnectionId: z.string().uuid().optional(),
6698
- model: z.string().min(1).optional(),
6699
- reasoningEffort: ReasoningEffort.optional(),
6700
- sandboxBackend: SandboxBackend.optional(),
6701
- goal: GoalSpec.optional(),
6702
- // Durable task override. Scheduled dispatch is trusted to preserve this
6703
- // snapshot even if the workspace/deployment policy narrows later.
6704
- maxNestedAgentDepth: NestedAgentDepthValue.optional(),
6705
- });
8025
+ export const IncidentTelemetryExecutionClass = z.literal("incident_telemetry");
8026
+ export type IncidentTelemetryExecutionClass = z.infer<typeof IncidentTelemetryExecutionClass>;
8027
+
8028
+ const IncidentTelemetryIdentifier = z.string().trim().min(1).max(200);
8029
+ const IncidentTelemetryMetricName = z
8030
+ .string()
8031
+ .trim()
8032
+ .min(1)
8033
+ .max(200)
8034
+ .regex(/^[A-Za-z_:][A-Za-z0-9_:]*$/);
8035
+ const IncidentTelemetryLabelName = z
8036
+ .string()
8037
+ .trim()
8038
+ .min(1)
8039
+ .max(200)
8040
+ .regex(/^[A-Za-z_][A-Za-z0-9_]*$/);
8041
+
8042
+ export const IncidentTelemetrySeriesMetadata = z
8043
+ .object({
8044
+ metric: IncidentTelemetryMetricName,
8045
+ labels: z.array(IncidentTelemetryLabelName).min(1).max(64),
8046
+ })
8047
+ .strict();
8048
+ export type IncidentTelemetrySeriesMetadata = z.infer<typeof IncidentTelemetrySeriesMetadata>;
8049
+
8050
+ export const IncidentTelemetryDataRoute = z.discriminatedUnion("kind", [
8051
+ z
8052
+ .object({
8053
+ kind: z.literal("mcp"),
8054
+ serverId: SessionMcpServerId,
8055
+ })
8056
+ .strict(),
8057
+ z
8058
+ .object({
8059
+ kind: z.literal("first_party"),
8060
+ tool: FirstPartyMcpToolName,
8061
+ })
8062
+ .strict(),
8063
+ z
8064
+ .object({
8065
+ kind: z.literal("variable_set"),
8066
+ variableSetName: z.string().trim().min(1).max(120),
8067
+ variableNames: z.array(VariableSetVariableName).min(1).max(100),
8068
+ })
8069
+ .strict(),
8070
+ z
8071
+ .object({
8072
+ kind: z.literal("rig_credential_hook"),
8073
+ credentialHookId: IncidentTelemetryIdentifier,
8074
+ })
8075
+ .strict(),
8076
+ ]);
8077
+ export type IncidentTelemetryDataRoute = z.infer<typeof IncidentTelemetryDataRoute>;
8078
+
8079
+ export const IncidentTelemetryPreflight = z
8080
+ .object({
8081
+ // These are exact selected resource refs, not discovery hints. The core
8082
+ // validator requires every entry to already be present in agentConfig.
8083
+ requiredResources: z.array(ResourceRef).max(100).default([]),
8084
+ requiredMcpServerIds: z.array(SessionMcpServerId).max(64).default([]),
8085
+ requiredFirstPartyMcpTools: z.array(FirstPartyMcpToolName).max(100).default([]),
8086
+ requiredFirstPartyMcpPermissions: z.array(Permission).max(100).default([]),
8087
+ requiredRig: z
8088
+ .object({
8089
+ name: z.string().trim().min(1).max(120),
8090
+ credentialHookIds: z.array(IncidentTelemetryIdentifier).max(50).default([]),
8091
+ })
8092
+ .strict()
8093
+ .nullable()
8094
+ .default(null),
8095
+ // Names only. The dispatch preflight never reads or decrypts values.
8096
+ requiredVariableSetNames: z.array(z.string().trim().min(1).max(120)).max(25).default([]),
8097
+ requiredVariableNames: z.array(VariableSetVariableName).max(100).default([]),
8098
+ dataSource: z
8099
+ .object({
8100
+ kind: z.literal("prometheus"),
8101
+ // Exposition endpoints such as /metrics are deliberately excluded.
8102
+ queryPath: z.enum(["/api/v1/query", "/api/v1/query_range"]),
8103
+ workspaceLabel: IncidentTelemetryLabelName,
8104
+ // Exact non-workspace labels whose values come from the validated
8105
+ // structured alert occurrence and bound every telemetry query.
8106
+ alertSelectorLabels: z.array(IncidentTelemetryLabelName).min(1).max(16),
8107
+ route: IncidentTelemetryDataRoute,
8108
+ requiredSeries: z.array(IncidentTelemetrySeriesMetadata).min(1).max(100),
8109
+ availableSeries: z.array(IncidentTelemetrySeriesMetadata).min(1).max(500),
8110
+ })
8111
+ .strict(),
8112
+ })
8113
+ .strict()
8114
+ .superRefine((value, context) => {
8115
+ for (const [index, label] of value.dataSource.alertSelectorLabels.entries()) {
8116
+ if (label === value.dataSource.workspaceLabel) {
8117
+ context.addIssue({
8118
+ code: "custom",
8119
+ path: ["dataSource", "alertSelectorLabels", index],
8120
+ message: "alert selector labels must be non-workspace labels",
8121
+ });
8122
+ }
8123
+ }
8124
+ for (const [index, series] of value.dataSource.requiredSeries.entries()) {
8125
+ if (!series.labels.includes(value.dataSource.workspaceLabel)) {
8126
+ context.addIssue({
8127
+ code: "custom",
8128
+ path: ["dataSource", "requiredSeries", index, "labels"],
8129
+ message: "required incident series must include the workspace label",
8130
+ });
8131
+ }
8132
+ for (const label of value.dataSource.alertSelectorLabels) {
8133
+ if (!series.labels.includes(label)) {
8134
+ context.addIssue({
8135
+ code: "custom",
8136
+ path: ["dataSource", "requiredSeries", index, "labels"],
8137
+ message: "required incident series must include every alert selector label",
8138
+ });
8139
+ }
8140
+ }
8141
+ }
8142
+ });
8143
+ export type IncidentTelemetryPreflight = z.infer<typeof IncidentTelemetryPreflight>;
8144
+
8145
+ /**
8146
+ * Canonical UTF-8 ingress limits for newly written scheduled execution truth.
8147
+ * They bound create/update requests and freshly accepted occurrence snapshots;
8148
+ * already-stored tasks are read back through the unbounded storage shape so a
8149
+ * legacy row can never become unreadable or undispatchable by a later cap.
8150
+ */
8151
+ export const SCHEDULED_TASK_NAME_MAX_BYTES = 512;
8152
+ export const SCHEDULED_TASK_PROMPT_MAX_BYTES = 64 * 1024;
8153
+ export const SCHEDULED_TASK_METADATA_MAX_BYTES = 32 * 1024;
8154
+ export const SCHEDULED_TASK_AGENT_CONFIG_MAX_BYTES = 128 * 1024;
8155
+ export const SCHEDULED_TASK_ACCEPTED_EXECUTION_MAX_BYTES = 512 * 1024;
8156
+ /**
8157
+ * A scheduled occurrence is delivered as one durable internal update whose
8158
+ * payload (`scheduled_occurrence` text + resources + tools + ids) must fit the
8159
+ * canonical internal-update bound. Bounding it here at ingress means every
8160
+ * stored task that passed validation can also be delivered.
8161
+ */
8162
+ export const SCHEDULED_TASK_OCCURRENCE_PAYLOAD_MAX_BYTES = 64 * 1024;
8163
+ /**
8164
+ * Ingress reserves headroom below that bound for what the worker adds to the
8165
+ * delivered payload (the first-party `opengeni` MCP tool ref and real ids), so
8166
+ * a request that passes validation can always be delivered.
8167
+ */
8168
+ export const SCHEDULED_TASK_OCCURRENCE_PAYLOAD_INGRESS_HEADROOM_BYTES = 1024;
8169
+ export const SCHEDULED_TASK_PRODUCER_KEY_MAX_BYTES = 512;
8170
+ export const SCHEDULED_TASK_RESOURCE_MAX_COUNT = 100;
8171
+ export const SCHEDULED_TASK_TOOL_MAX_COUNT = 128;
8172
+
8173
+ const scheduledTaskUtf8Encoder = new TextEncoder();
8174
+
8175
+ function scheduledTaskUtf8Bytes(value: string): number {
8176
+ return scheduledTaskUtf8Encoder.encode(value).byteLength;
8177
+ }
8178
+
8179
+ function scheduledTaskJsonUtf8Bytes(value: unknown): number {
8180
+ try {
8181
+ const encoded = JSON.stringify(value);
8182
+ return encoded === undefined ? Number.POSITIVE_INFINITY : scheduledTaskUtf8Bytes(encoded);
8183
+ } catch {
8184
+ return Number.POSITIVE_INFINITY;
8185
+ }
8186
+ }
8187
+
8188
+ /**
8189
+ * UTF-8 size of the exact `scheduled_occurrence` internal-update payload the
8190
+ * worker delivers for one occurrence of this agent config (ids use fixed-width
8191
+ * placeholder UUIDs). Shared by ingress validation and worker admission.
8192
+ */
8193
+ export function scheduledOccurrencePayloadUtf8Bytes(agentConfig: {
8194
+ prompt: string;
8195
+ resources?: readonly unknown[] | undefined;
8196
+ tools?: readonly unknown[] | undefined;
8197
+ }): number {
8198
+ const placeholder = "00000000-0000-4000-8000-000000000000";
8199
+ return scheduledTaskJsonUtf8Bytes({
8200
+ type: "scheduled_occurrence",
8201
+ text: agentConfig.prompt,
8202
+ scheduledTaskId: placeholder,
8203
+ scheduledTaskRunId: placeholder,
8204
+ ...(agentConfig.resources?.length ? { resources: agentConfig.resources } : {}),
8205
+ ...(agentConfig.tools?.length ? { tools: agentConfig.tools } : {}),
8206
+ });
8207
+ }
8208
+
8209
+ function scheduledTaskBoundedJsonObject(maxBytes: number, label: string) {
8210
+ return z.record(z.string(), z.unknown()).superRefine((value, context) => {
8211
+ if (scheduledTaskJsonUtf8Bytes(value) > maxBytes) {
8212
+ context.addIssue({
8213
+ code: "custom",
8214
+ message: `${label} exceeds ${maxBytes} UTF-8 bytes`,
8215
+ });
8216
+ }
8217
+ });
8218
+ }
8219
+
8220
+ function scheduledTaskBoundedString(maxBytes: number, label: string) {
8221
+ return z
8222
+ .string()
8223
+ .min(1)
8224
+ .superRefine((value, context) => {
8225
+ if (scheduledTaskUtf8Bytes(value) > maxBytes) {
8226
+ context.addIssue({
8227
+ code: "custom",
8228
+ message: `${label} exceeds ${maxBytes} UTF-8 bytes`,
8229
+ });
8230
+ }
8231
+ });
8232
+ }
8233
+
8234
+ /** Ingress-bounded task name for create/update requests. */
8235
+ export const ScheduledTaskNameInput = /* @__PURE__ */ scheduledTaskBoundedString(
8236
+ SCHEDULED_TASK_NAME_MAX_BYTES,
8237
+ "scheduled task name",
8238
+ );
8239
+ /** Ingress-bounded task metadata for create/update requests. */
8240
+ export const ScheduledTaskMetadataInput = /* @__PURE__ */ scheduledTaskBoundedJsonObject(
8241
+ SCHEDULED_TASK_METADATA_MAX_BYTES,
8242
+ "scheduled task metadata",
8243
+ );
8244
+
8245
+ function scheduledTaskAgentConfigShape(bounded: boolean) {
8246
+ return {
8247
+ prompt: bounded
8248
+ ? scheduledTaskBoundedString(SCHEDULED_TASK_PROMPT_MAX_BYTES, "scheduled task prompt")
8249
+ : z.string().min(1),
8250
+ resources: bounded
8251
+ ? z.array(ResourceRef).max(SCHEDULED_TASK_RESOURCE_MAX_COUNT).default([])
8252
+ : z.array(ResourceRef).default([]),
8253
+ tools: bounded
8254
+ ? z
8255
+ .array(ToolRef)
8256
+ .max(SCHEDULED_TASK_TOOL_MAX_COUNT - 1)
8257
+ .default([])
8258
+ : z.array(ToolRef).default([]),
8259
+ metadata: bounded
8260
+ ? ScheduledTaskMetadataInput.default({})
8261
+ : z.record(z.string(), z.unknown()).default({}),
8262
+ // Explicit workspace-shared OpenGeni Slack bot binding for scheduled runs.
8263
+ // The worker copies this non-secret pointer into session metadata; the
8264
+ // first-party Slack tools never fall back to a personal hosted-MCP grant.
8265
+ slackBotConnectionId: z.string().uuid().optional(),
8266
+ model: bounded
8267
+ ? scheduledTaskBoundedString(512, "scheduled task model").optional()
8268
+ : z.string().min(1).optional(),
8269
+ reasoningEffort: ReasoningEffort.optional(),
8270
+ sandboxBackend: SandboxBackend.optional(),
8271
+ goal: GoalSpec.optional(),
8272
+ // Incident telemetry is the only special execution class. Omission keeps
8273
+ // every existing task on the byte-compatible ordinary dispatch path.
8274
+ executionClass: IncidentTelemetryExecutionClass.optional(),
8275
+ incidentTelemetryPreflight: IncidentTelemetryPreflight.optional(),
8276
+ // Durable task override. Scheduled dispatch is trusted to preserve this
8277
+ // snapshot even if the workspace/deployment policy narrows later.
8278
+ maxNestedAgentDepth: NestedAgentDepthValue.optional(),
8279
+ };
8280
+ }
8281
+
8282
+ function refineScheduledTaskAgentConfig(
8283
+ value: { executionClass?: unknown; incidentTelemetryPreflight?: unknown },
8284
+ context: z.RefinementCtx,
8285
+ ): void {
8286
+ if (value.executionClass === "incident_telemetry" && !value.incidentTelemetryPreflight) {
8287
+ context.addIssue({
8288
+ code: "custom",
8289
+ path: ["incidentTelemetryPreflight"],
8290
+ message: "incident telemetry tasks require incidentTelemetryPreflight",
8291
+ });
8292
+ }
8293
+ if (value.executionClass !== "incident_telemetry" && value.incidentTelemetryPreflight) {
8294
+ context.addIssue({
8295
+ code: "custom",
8296
+ path: ["executionClass"],
8297
+ message: "incidentTelemetryPreflight requires executionClass=incident_telemetry",
8298
+ });
8299
+ }
8300
+ }
8301
+
8302
+ /** Storage/projection shape. Deliberately unbounded so stored rows always parse. */
8303
+ export const ScheduledTaskAgentConfig = /* @__PURE__ */ z
8304
+ .object(scheduledTaskAgentConfigShape(false))
8305
+ .superRefine(refineScheduledTaskAgentConfig);
6706
8306
  export type ScheduledTaskAgentConfig = z.infer<typeof ScheduledTaskAgentConfig>;
6707
8307
 
8308
+ /** Ingress-bounded agent config for create/update requests. */
8309
+ export const ScheduledTaskAgentConfigInput = /* @__PURE__ */ z
8310
+ .object(scheduledTaskAgentConfigShape(true))
8311
+ .superRefine((value, context) => {
8312
+ if (scheduledTaskJsonUtf8Bytes(value) > SCHEDULED_TASK_AGENT_CONFIG_MAX_BYTES) {
8313
+ context.addIssue({
8314
+ code: "custom",
8315
+ message: `scheduled task agent config exceeds ${SCHEDULED_TASK_AGENT_CONFIG_MAX_BYTES} UTF-8 bytes`,
8316
+ });
8317
+ }
8318
+ if (
8319
+ scheduledOccurrencePayloadUtf8Bytes(value) >
8320
+ SCHEDULED_TASK_OCCURRENCE_PAYLOAD_MAX_BYTES -
8321
+ SCHEDULED_TASK_OCCURRENCE_PAYLOAD_INGRESS_HEADROOM_BYTES
8322
+ ) {
8323
+ context.addIssue({
8324
+ code: "custom",
8325
+ message: `scheduled task prompt, resources, and tools exceed the ${SCHEDULED_TASK_OCCURRENCE_PAYLOAD_MAX_BYTES - SCHEDULED_TASK_OCCURRENCE_PAYLOAD_INGRESS_HEADROOM_BYTES} UTF-8 byte occurrence payload`,
8326
+ });
8327
+ }
8328
+ refineScheduledTaskAgentConfig(value, context);
8329
+ });
8330
+ export type ScheduledTaskAgentConfigInput = z.infer<typeof ScheduledTaskAgentConfigInput>;
8331
+
6708
8332
  export const ScheduledTask = /* @__PURE__ */ z.object({
6709
8333
  id: z.string().uuid(),
6710
8334
  accountId: z.string().uuid(),
@@ -6723,6 +8347,8 @@ export const ScheduledTask = /* @__PURE__ */ z.object({
6723
8347
  }),
6724
8348
  createdByContext: TurnInitiatorContext.default({}),
6725
8349
  personalConnections: z.array(McpPersonalConnectionSummary).default([]),
8350
+ authorityRevision: z.number().int().positive().default(1),
8351
+ executionDigest: z.string().regex(/^[0-9a-f]{64}$/u),
6726
8352
  reusableSessionId: z.string().uuid().nullable(),
6727
8353
  targetSessionId: z.string().uuid().nullable().default(null),
6728
8354
  variableSetId: z.string().uuid().nullable().default(null),
@@ -6738,31 +8364,177 @@ export const ScheduledTask = /* @__PURE__ */ z.object({
6738
8364
  });
6739
8365
  export type ScheduledTask = z.infer<typeof ScheduledTask>;
6740
8366
 
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,
8367
+ /**
8368
+ * Complete credential-free execution truth accepted for one scheduled agent
8369
+ * occurrence. Retries consume this immutable snapshot instead of a mutable
8370
+ * scheduled-task head.
8371
+ */
8372
+ export const ScheduledTaskRunAcceptedExecution = /* @__PURE__ */ z
8373
+ .object({
8374
+ version: z.literal(1),
8375
+ task: ScheduledTask,
8376
+ resolvedModel: z.string().min(1),
8377
+ resolvedReasoningEffort: ReasoningEffort,
8378
+ resolvedLatencyMode: LatencyMode,
8379
+ resolvedSandboxBackend: SandboxBackend,
8380
+ resolvedSandboxOs: SandboxOs,
8381
+ resolvedTools: z.array(ToolRef).max(SCHEDULED_TASK_TOOL_MAX_COUNT),
8382
+ resolvedFirstPartyMcpTools: z.array(FirstPartyMcpToolName),
8383
+ resolvedFirstPartyMcpPermissions: z.array(Permission),
8384
+ resolvedVariableSet: z
8385
+ .object({
8386
+ id: z.string().uuid(),
8387
+ generation: z.number().int().positive(),
8388
+ })
8389
+ .strict()
8390
+ .nullable(),
8391
+ resolvedRig: z
8392
+ .object({
8393
+ id: z.string().uuid(),
8394
+ versionId: z.string().uuid(),
8395
+ defaultVariableSets: z
8396
+ .array(
8397
+ z
8398
+ .object({
8399
+ id: z.string().uuid(),
8400
+ generation: z.number().int().positive(),
8401
+ })
8402
+ .strict(),
8403
+ )
8404
+ .max(25),
8405
+ })
8406
+ .strict()
8407
+ .nullable(),
8408
+ resolvedSlackBotConnection: z
8409
+ .object({
8410
+ id: z.string().uuid(),
8411
+ version: z.number().int().positive(),
8412
+ verifiedInstallVersion: z.number().int().positive(),
8413
+ metadata: z
8414
+ .object({
8415
+ credentialRole: z.literal("opengeni_slack_bot"),
8416
+ credentialLabel: z.literal("OpenGeni Slack bot"),
8417
+ slackTeamId: z.string().min(1).max(64),
8418
+ slackTeamName: z.string().min(1).max(256),
8419
+ botUserId: z.string().min(1).max(64),
8420
+ botId: z.string().min(1).max(64),
8421
+ botDisplayName: z.enum(["OpenGeni", "OpenGeni Staging"]),
8422
+ verifiedAt: z.string().datetime({ offset: true }),
8423
+ })
8424
+ .passthrough(),
8425
+ })
8426
+ .strict()
8427
+ .nullable(),
8428
+ /**
8429
+ * The effective policy that an existing or already-materialized reusable
8430
+ * session will execute. Generated sessions carry null because their
8431
+ * accepted task policy is used to create the session itself.
8432
+ */
8433
+ targetSessionExecution: z
8434
+ .object({
8435
+ sessionId: z.string().uuid(),
8436
+ visibility: z.enum(["user_private", "workspace_shared"]),
8437
+ authorityEpoch: z.number().int().positive(),
8438
+ model: z.string().min(1),
8439
+ reasoningEffort: ReasoningEffort,
8440
+ latencyMode: LatencyMode,
8441
+ tools: z.array(ToolRef).max(SCHEDULED_TASK_TOOL_MAX_COUNT),
8442
+ sandboxBackend: SandboxBackend,
8443
+ sandboxOs: SandboxOs,
8444
+ firstPartyMcpTools: z.array(FirstPartyMcpToolName),
8445
+ firstPartyMcpPermissions: z.array(Permission).nullable(),
8446
+ toolPolicy: SessionToolPolicy,
8447
+ mcpServerIds: z.array(z.string().min(1).max(256)).max(SCHEDULED_TASK_TOOL_MAX_COUNT),
8448
+ effectiveMcpServerIds: z
8449
+ .array(z.string().min(1).max(256))
8450
+ .max(SCHEDULED_TASK_TOOL_MAX_COUNT),
8451
+ toolPolicyVersion: z.number().int().nonnegative(),
8452
+ variableSetId: z.string().uuid().nullable(),
8453
+ variableSetGeneration: z.number().int().positive().nullable(),
8454
+ rigId: z.string().uuid().nullable(),
8455
+ rigVersionId: z.string().uuid().nullable(),
8456
+ rigDefaultVariableSets: z
8457
+ .array(
8458
+ z
8459
+ .object({
8460
+ id: z.string().uuid(),
8461
+ generation: z.number().int().positive(),
8462
+ })
8463
+ .strict(),
8464
+ )
8465
+ .max(25),
8466
+ maxNestedAgentDepthOverride: NestedAgentDepthValue.nullable(),
8467
+ effectiveMaxNestedAgentDepth: NestedAgentDepthValue,
8468
+ })
8469
+ .strict()
8470
+ .nullable(),
8471
+ generatedSessionBinding: z
8472
+ .object({
8473
+ createIdempotencyKey: z.string().min(1).max(512),
8474
+ effectiveMaxNestedAgentDepth: NestedAgentDepthValue,
8475
+ nestedAgentDepthPolicySource: NestedAgentDepthPolicySource,
8476
+ codexCompactionMode: CodexCompactionMode,
8477
+ })
8478
+ .strict()
8479
+ .nullable(),
8480
+ personalConnectionDelegations: McpPersonalConnectionDelegations,
8481
+ personalResourceAuthoritySubjectId: z.string().min(1).nullable(),
8482
+ /** One accepted human principal for every resource-bearing scheduled run. */
8483
+ causalHumanSubjectId: z.string().min(1).nullable().default(null),
8484
+ /** Exact revision-bound human membership proof; never inferred at execution. */
8485
+ causalHumanAuthority: z
8486
+ .object({
8487
+ subjectId: z.string().min(1),
8488
+ organizationMembershipId: z.string().uuid(),
8489
+ membershipAuthorizationRevision: z.number().int().positive(),
8490
+ })
8491
+ .strict()
8492
+ .nullable()
8493
+ .default(null),
8494
+ xaiProviderAccountAuthoritySnapshot: XaiProviderAccountAuthoritySnapshotV1,
8495
+ xaiAuthoritySubjectId: z.string().min(1).nullable(),
8496
+ connectionAuthoritySubjectId: z.string().min(1).nullable(),
8497
+ triggerInitiator: TurnInitiator,
8498
+ agentRunUsageIdempotencyKey: z.string().min(1).max(512).nullable(),
8499
+ incidentPreflightRequired: z.boolean(),
8500
+ alertOccurrenceLabels: z.record(z.string(), z.string()).nullable(),
8501
+ })
8502
+ .strict()
8503
+ .superRefine((value, context) => {
8504
+ if (scheduledTaskJsonUtf8Bytes(value) > SCHEDULED_TASK_ACCEPTED_EXECUTION_MAX_BYTES) {
8505
+ context.addIssue({
8506
+ code: "custom",
8507
+ message: `scheduled accepted execution exceeds ${SCHEDULED_TASK_ACCEPTED_EXECUTION_MAX_BYTES} UTF-8 bytes`,
8508
+ });
8509
+ }
8510
+ });
8511
+ export type ScheduledTaskRunAcceptedExecution = z.infer<typeof ScheduledTaskRunAcceptedExecution>;
8512
+
8513
+ export const KnowledgeSourceSyncFailure = /* @__PURE__ */ z.object({
8514
+ externalObjectId: KnowledgeSourceSyncSubject,
8515
+ code: z.enum([
8516
+ "authority_changed",
8517
+ "connection_reconnect_required",
8518
+ "provider_unavailable",
8519
+ "provider_rejected",
8520
+ "provider_payload_invalid",
8521
+ "content_unsupported",
8522
+ "content_too_large",
8523
+ "resource_limit",
8524
+ "item_processing_failed",
8525
+ "indexing_failed",
8526
+ "internal_failure",
8527
+ ]),
8528
+ retryable: z.boolean(),
8529
+ message: z.string().min(1).max(1000),
8530
+ });
8531
+ export type KnowledgeSourceSyncFailure = z.infer<typeof KnowledgeSourceSyncFailure>;
8532
+
8533
+ export const KnowledgeSourceSyncRunSummary = /* @__PURE__ */ z.object({
8534
+ phase: z
8535
+ .enum(["queued", "inventory", "transfer", "index", "checkpoint", "completed", "failed"])
8536
+ .default("queued"),
8537
+ scanned: KnowledgeSourceSyncZeroInteger,
6766
8538
  imported: KnowledgeSourceSyncZeroInteger,
6767
8539
  unchanged: KnowledgeSourceSyncZeroInteger,
6768
8540
  skipped: KnowledgeSourceSyncZeroInteger,
@@ -6788,6 +8560,12 @@ export const ScheduledTaskRun = /* @__PURE__ */ z.object({
6788
8560
  accountId: z.string().uuid(),
6789
8561
  workspaceId: z.string().uuid(),
6790
8562
  taskId: z.string().uuid(),
8563
+ taskAuthorityRevision: z.number().int().positive().nullable().default(null),
8564
+ taskExecutionDigest: z
8565
+ .string()
8566
+ .regex(/^[0-9a-f]{64}$/u)
8567
+ .nullable()
8568
+ .default(null),
6791
8569
  status: ScheduledTaskRunStatus,
6792
8570
  triggerType: ScheduledTaskTriggerType,
6793
8571
  scheduledAt: z.string().nullable(),
@@ -6805,7 +8583,7 @@ export const ScheduledTaskRun = /* @__PURE__ */ z.object({
6805
8583
  export type ScheduledTaskRun = z.infer<typeof ScheduledTaskRun>;
6806
8584
 
6807
8585
  const CreateAgentScheduledTaskRequest = /* @__PURE__ */ withVariableSetIdAlias({
6808
- name: z.string().min(1),
8586
+ name: ScheduledTaskNameInput,
6809
8587
  schedule: ScheduledTaskScheduleSpec,
6810
8588
  action: z
6811
8589
  .object({ kind: z.literal("agent_turn") })
@@ -6814,13 +8592,14 @@ const CreateAgentScheduledTaskRequest = /* @__PURE__ */ withVariableSetIdAlias({
6814
8592
  runMode: ScheduledTaskRunMode.default("new_session_per_run"),
6815
8593
  overlapPolicy: ScheduledTaskOverlapPolicy.default("allow_concurrent"),
6816
8594
  targetSessionId: z.string().uuid().nullable().optional(),
6817
- agentConfig: ScheduledTaskAgentConfig,
8595
+ connectionAuthorities: McpConnectionAuthoritySelections.default([]),
8596
+ agentConfig: ScheduledTaskAgentConfigInput,
6818
8597
  status: ScheduledTaskStatus.default("active"),
6819
8598
  variableSetId: z.string().uuid().nullable().optional(),
6820
8599
  environmentId: z.string().uuid().nullable().optional(),
6821
8600
  // The rig each run binds to (M3); its active version is resolved per fire.
6822
8601
  rigId: z.string().uuid().nullable().optional(),
6823
- metadata: z.record(z.string(), z.unknown()).default({}),
8602
+ metadata: ScheduledTaskMetadataInput.default({}),
6824
8603
  }).superRefine((value, context) => {
6825
8604
  if (value.runMode === "existing_session" && !value.targetSessionId) {
6826
8605
  context.addIssue({
@@ -6868,6 +8647,7 @@ const CreateKnowledgeSourceSyncScheduledTaskRequest = /* @__PURE__ */ z
6868
8647
  variableSetId: null,
6869
8648
  environmentId: null,
6870
8649
  rigId: null,
8650
+ connectionAuthorities: [],
6871
8651
  }));
6872
8652
 
6873
8653
  export const CreateScheduledTaskRequest = /* @__PURE__ */ z.union([
@@ -6876,59 +8656,393 @@ export const CreateScheduledTaskRequest = /* @__PURE__ */ z.union([
6876
8656
  ]);
6877
8657
  export type CreateScheduledTaskRequest = z.infer<typeof CreateScheduledTaskRequest>;
6878
8658
 
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") {
8659
+ export const UpdateScheduledTaskRequest = /* @__PURE__ */ withVariableSetIdAlias({
8660
+ name: ScheduledTaskNameInput.optional(),
8661
+ schedule: ScheduledTaskScheduleSpec.optional(),
8662
+ runMode: ScheduledTaskRunMode.optional(),
8663
+ overlapPolicy: ScheduledTaskOverlapPolicy.optional(),
8664
+ action: ScheduledTaskAction.optional(),
8665
+ targetSessionId: z.string().uuid().nullable().optional(),
8666
+ connectionAuthorities: McpConnectionAuthoritySelections.optional(),
8667
+ agentConfig: ScheduledTaskAgentConfigInput.optional(),
8668
+ status: ScheduledTaskStatus.optional(),
8669
+ variableSetId: z.string().uuid().nullable().optional(),
8670
+ environmentId: z.string().uuid().nullable().optional(),
8671
+ // The rig each run binds to (M3); null clears it. Its active version is
8672
+ // resolved per fire, so an update takes effect on the next dispatch.
8673
+ rigId: z.string().uuid().nullable().optional(),
8674
+ metadata: z.record(z.string(), z.unknown()).optional(),
8675
+ }).superRefine((value, context) => {
8676
+ if (value.targetSessionId && value.runMode && value.runMode !== "existing_session") {
8677
+ context.addIssue({
8678
+ code: "custom",
8679
+ path: ["targetSessionId"],
8680
+ message: "targetSessionId requires runMode=existing_session",
8681
+ });
8682
+ }
8683
+ if (value.runMode === "existing_session" && value.targetSessionId === null) {
8684
+ context.addIssue({
8685
+ code: "custom",
8686
+ path: ["targetSessionId"],
8687
+ message: "targetSessionId cannot be null when runMode=existing_session",
8688
+ });
8689
+ }
8690
+ if (
8691
+ value.agentConfig?.goal &&
8692
+ (value.runMode === "existing_session" || Boolean(value.targetSessionId))
8693
+ ) {
8694
+ context.addIssue({
8695
+ code: "custom",
8696
+ path: ["agentConfig", "goal"],
8697
+ message: "agentConfig.goal cannot be used with an existing-session target",
8698
+ });
8699
+ }
8700
+ });
8701
+ export type UpdateScheduledTaskRequest = z.infer<typeof UpdateScheduledTaskRequest>;
8702
+
8703
+ /**
8704
+ * Manual-trigger body. `triggerId` is a client-supplied idempotency token: a
8705
+ * retried trigger that reuses the same token charges once and starts one run.
8706
+ * Omitting it makes each call a distinct trigger (the server mints a token).
8707
+ */
8708
+ export const TriggerScheduledTaskRequest = z.object({
8709
+ triggerId: z.string().min(1).max(128).optional(),
8710
+ });
8711
+ export type TriggerScheduledTaskRequest = z.infer<typeof TriggerScheduledTaskRequest>;
8712
+
8713
+ // ============ Event-triggered automations ============
8714
+
8715
+ export const AUTOMATION_WEBHOOK_MAX_BYTES = 2 * 1024 * 1024;
8716
+ export const AUTOMATION_EVENT_PAYLOAD_MAX_BYTES = 256 * 1024;
8717
+ export const AUTOMATION_SESSION_TEMPLATE_MAX_BYTES = 512 * 1024;
8718
+ export const AUTOMATION_MAX_MATCHED_TRIGGERS = 32;
8719
+
8720
+ const AutomationBoundedJson = z.record(z.string(), z.unknown()).superRefine((value, context) => {
8721
+ let bytes = Number.POSITIVE_INFINITY;
8722
+ try {
8723
+ bytes = new TextEncoder().encode(JSON.stringify(value)).byteLength;
8724
+ } catch {
8725
+ // The shared JSON/protocol boundary reports the exact runtime path later;
8726
+ // this public ingress still fails closed on an unserializable value.
8727
+ }
8728
+ if (bytes > AUTOMATION_EVENT_PAYLOAD_MAX_BYTES) {
8729
+ context.addIssue({
8730
+ code: "custom",
8731
+ message: `automation JSON exceeds ${AUTOMATION_EVENT_PAYLOAD_MAX_BYTES} UTF-8 bytes`,
8732
+ });
8733
+ }
8734
+ });
8735
+
8736
+ export const AutomationAdapterId = z
8737
+ .string()
8738
+ .min(1)
8739
+ .max(128)
8740
+ .regex(/^[a-z0-9](?:[a-z0-9._/-]*[a-z0-9])?$/);
8741
+ export type AutomationAdapterId = z.infer<typeof AutomationAdapterId>;
8742
+
8743
+ export const AutomationSourceStatus = z.enum(["active", "disabled"]);
8744
+ export type AutomationSourceStatus = z.infer<typeof AutomationSourceStatus>;
8745
+
8746
+ export const AutomationTriggerStatus = z.enum(["active", "paused", "disabled"]);
8747
+ export type AutomationTriggerStatus = z.infer<typeof AutomationTriggerStatus>;
8748
+
8749
+ export const AutomationRunStatus = z.enum([
8750
+ "queued",
8751
+ "dispatching",
8752
+ "dispatched",
8753
+ "skipped",
8754
+ "failed",
8755
+ ]);
8756
+ export type AutomationRunStatus = z.infer<typeof AutomationRunStatus>;
8757
+
8758
+ export const AutomationEventStatus = z.enum(["accepted", "ignored", "failed"]);
8759
+ export type AutomationEventStatus = z.infer<typeof AutomationEventStatus>;
8760
+
8761
+ export const SignedJsonAutomationEnvelope = z
8762
+ .object({
8763
+ id: z.string().trim().min(1).max(1024).optional(),
8764
+ type: z.string().trim().min(1).max(256),
8765
+ occurrenceKey: z.string().trim().min(1).max(1024).optional(),
8766
+ occurredAt: z.string().datetime({ offset: true }).nullable().optional(),
8767
+ subject: z.string().trim().min(1).max(512).nullable().optional(),
8768
+ resource: z.string().trim().min(1).max(1024).nullable().optional(),
8769
+ data: z.record(z.string(), z.unknown()).default({}),
8770
+ })
8771
+ .strict();
8772
+ export type SignedJsonAutomationEnvelope = z.infer<typeof SignedJsonAutomationEnvelope>;
8773
+
8774
+ const AutomationSessionSkill = z
8775
+ .object({
8776
+ name: z
8777
+ .string()
8778
+ .min(1)
8779
+ .max(64)
8780
+ .regex(/^[A-Za-z0-9][A-Za-z0-9._-]*$/),
8781
+ description: z.string().min(1).max(2048).optional(),
8782
+ files: z
8783
+ .array(
8784
+ z.object({
8785
+ path: z
8786
+ .string()
8787
+ .min(1)
8788
+ .max(512)
8789
+ .refine(
8790
+ (path) =>
8791
+ !path.startsWith("/") &&
8792
+ !path.includes("\\") &&
8793
+ path
8794
+ .split("/")
8795
+ .every((segment) => segment.length > 0 && segment !== "." && segment !== ".."),
8796
+ "automation skill file path must be a safe relative POSIX path",
8797
+ ),
8798
+ content: z.string().max(256 * 1024),
8799
+ }),
8800
+ )
8801
+ .min(1)
8802
+ .max(64),
8803
+ })
8804
+ .superRefine((skill, context) => {
8805
+ if (!skill.files.some((file) => file.path === "SKILL.md")) {
6897
8806
  context.addIssue({
6898
8807
  code: "custom",
6899
- path: ["targetSessionId"],
6900
- message: "targetSessionId requires runMode=existing_session",
8808
+ path: ["files"],
8809
+ message: "automation skill must include a top-level SKILL.md file",
6901
8810
  });
6902
8811
  }
6903
- if (value.runMode === "existing_session" && value.targetSessionId === null) {
8812
+ });
8813
+
8814
+ export const AutomationSessionTemplate = z
8815
+ .object({
8816
+ prompt: z
8817
+ .string()
8818
+ .trim()
8819
+ .min(1)
8820
+ .max(64 * 1024),
8821
+ instructions: z
8822
+ .string()
8823
+ .trim()
8824
+ .min(1)
8825
+ .max(64 * 1024)
8826
+ .nullable()
8827
+ .default(null),
8828
+ resources: z.array(ResourceRef).max(100).default([]),
8829
+ skills: z.array(AutomationSessionSkill).max(32).default([]),
8830
+ tools: z.array(ToolRef).max(128).default([]),
8831
+ firstPartyMcpTools: z.array(FirstPartyMcpToolName).max(128).default([]),
8832
+ firstPartyMcpPermissions: z.array(Permission).max(128).default([]),
8833
+ model: z.string().trim().min(1).max(512).nullable().default(null),
8834
+ reasoningEffort: ReasoningEffort.nullable().default(null),
8835
+ sandboxBackend: SandboxBackend.nullable().default(null),
8836
+ policyRole: z.string().trim().min(1).max(128).nullable().default(null),
8837
+ metadata: AutomationBoundedJson.default({}),
8838
+ })
8839
+ .strict()
8840
+ .superRefine((value, context) => {
8841
+ let bytes = Number.POSITIVE_INFINITY;
8842
+ try {
8843
+ bytes = new TextEncoder().encode(JSON.stringify(value)).byteLength;
8844
+ } catch {
8845
+ // Fail through the size issue below.
8846
+ }
8847
+ if (bytes > AUTOMATION_SESSION_TEMPLATE_MAX_BYTES) {
6904
8848
  context.addIssue({
6905
8849
  code: "custom",
6906
- path: ["targetSessionId"],
6907
- message: "targetSessionId cannot be null when runMode=existing_session",
8850
+ message: `automation session template exceeds ${AUTOMATION_SESSION_TEMPLATE_MAX_BYTES} UTF-8 bytes`,
6908
8851
  });
6909
8852
  }
6910
- if (
6911
- value.agentConfig?.goal &&
6912
- (value.runMode === "existing_session" || Boolean(value.targetSessionId))
6913
- ) {
8853
+ });
8854
+ export type AutomationSessionTemplate = z.infer<typeof AutomationSessionTemplate>;
8855
+
8856
+ export const AutomationNormalizedEvent = z
8857
+ .object({
8858
+ adapterId: AutomationAdapterId,
8859
+ eventType: z.string().trim().min(1).max(256),
8860
+ occurrenceKey: z.string().trim().min(1).max(1024),
8861
+ occurredAt: z.string().datetime({ offset: true }).nullable().default(null),
8862
+ subject: z.string().trim().min(1).max(512).nullable().default(null),
8863
+ resource: z.string().trim().min(1).max(1024).nullable().default(null),
8864
+ payload: AutomationBoundedJson,
8865
+ })
8866
+ .strict();
8867
+ export type AutomationNormalizedEvent = z.infer<typeof AutomationNormalizedEvent>;
8868
+
8869
+ export const AutomationAcceptedExecution = z
8870
+ .object({
8871
+ version: z.literal(1),
8872
+ accountId: z.string().uuid(),
8873
+ workspaceId: z.string().uuid(),
8874
+ sourceId: z.string().uuid(),
8875
+ sourceVersion: z.number().int().positive(),
8876
+ triggerId: z.string().uuid(),
8877
+ triggerRevision: z.number().int().positive(),
8878
+ eventId: z.string().uuid(),
8879
+ adapterId: AutomationAdapterId,
8880
+ occurrenceKey: z.string().min(1).max(1024),
8881
+ initialMessage: z
8882
+ .string()
8883
+ .min(1)
8884
+ .max(256 * 1024),
8885
+ sessionTemplate: AutomationSessionTemplate,
8886
+ serviceSubjectId: z.string().min(1).max(512),
8887
+ serviceLabel: z.string().min(1).max(200),
8888
+ provenance: AutomationBoundedJson,
8889
+ })
8890
+ .strict();
8891
+ export type AutomationAcceptedExecution = z.infer<typeof AutomationAcceptedExecution>;
8892
+
8893
+ export const CreateAutomationSourceRequest = z
8894
+ .object({
8895
+ name: z.string().trim().min(1).max(200),
8896
+ adapterId: AutomationAdapterId.default("signed-json.v1"),
8897
+ webhookSecret: z.string().min(16).max(65_536),
8898
+ configuration: AutomationBoundedJson.default({}),
8899
+ })
8900
+ .strict();
8901
+ export type CreateAutomationSourceRequest = z.infer<typeof CreateAutomationSourceRequest>;
8902
+
8903
+ export const UpdateAutomationSourceRequest = z
8904
+ .object({
8905
+ name: z.string().trim().min(1).max(200).optional(),
8906
+ webhookSecret: z.string().min(16).max(65_536).optional(),
8907
+ configuration: AutomationBoundedJson.optional(),
8908
+ status: AutomationSourceStatus.optional(),
8909
+ })
8910
+ .strict()
8911
+ .refine((value) => Object.keys(value).length > 0, "automation source update is empty");
8912
+ export type UpdateAutomationSourceRequest = z.infer<typeof UpdateAutomationSourceRequest>;
8913
+
8914
+ export const AutomationSource = z.object({
8915
+ id: z.string().uuid(),
8916
+ accountId: z.string().uuid(),
8917
+ workspaceId: z.string().uuid(),
8918
+ name: z.string(),
8919
+ adapterId: AutomationAdapterId,
8920
+ configuration: z.record(z.string(), z.unknown()),
8921
+ status: AutomationSourceStatus,
8922
+ version: z.number().int().positive(),
8923
+ packInstallationId: z.string().uuid().nullable(),
8924
+ packConnectorId: z.string().min(1).max(128).nullable(),
8925
+ hasWebhookSecret: z.boolean(),
8926
+ webhookPath: z.string().min(1),
8927
+ createdBySubjectId: z.string(),
8928
+ createdAt: z.string(),
8929
+ updatedAt: z.string(),
8930
+ });
8931
+ export type AutomationSource = z.infer<typeof AutomationSource>;
8932
+
8933
+ export const CreateAutomationTriggerRequest = z
8934
+ .object({
8935
+ sourceId: z.string().uuid(),
8936
+ name: z.string().trim().min(1).max(200),
8937
+ eventTypes: z.array(z.string().trim().min(1).max(256)).min(1).max(64),
8938
+ configuration: AutomationBoundedJson.default({}),
8939
+ parameters: AutomationBoundedJson.default({}),
8940
+ sessionTemplate: AutomationSessionTemplate,
8941
+ status: AutomationTriggerStatus.default("active"),
8942
+ packInstallationId: z.string().uuid().nullable().default(null),
8943
+ packTemplateId: z.string().trim().min(1).max(128).nullable().default(null),
8944
+ })
8945
+ .strict()
8946
+ .superRefine((value, context) => {
8947
+ if ((value.packInstallationId === null) !== (value.packTemplateId === null)) {
6914
8948
  context.addIssue({
6915
8949
  code: "custom",
6916
- path: ["agentConfig", "goal"],
6917
- message: "agentConfig.goal cannot be used with an existing-session target",
8950
+ path: ["packTemplateId"],
8951
+ message: "packInstallationId and packTemplateId must be supplied together",
6918
8952
  });
6919
8953
  }
6920
8954
  });
6921
- export type UpdateScheduledTaskRequest = z.infer<typeof UpdateScheduledTaskRequest>;
8955
+ export type CreateAutomationTriggerRequest = z.infer<typeof CreateAutomationTriggerRequest>;
6922
8956
 
6923
- /**
6924
- * Manual-trigger body. `triggerId` is a client-supplied idempotency token: a
6925
- * retried trigger that reuses the same token charges once and starts one run.
6926
- * Omitting it makes each call a distinct trigger (the server mints a token).
6927
- */
6928
- export const TriggerScheduledTaskRequest = z.object({
6929
- triggerId: z.string().min(1).max(128).optional(),
8957
+ export const UpdateAutomationTriggerRequest = z
8958
+ .object({
8959
+ expectedRevision: z.number().int().positive(),
8960
+ name: z.string().trim().min(1).max(200).optional(),
8961
+ eventTypes: z.array(z.string().trim().min(1).max(256)).min(1).max(64).optional(),
8962
+ configuration: AutomationBoundedJson.optional(),
8963
+ parameters: AutomationBoundedJson.optional(),
8964
+ sessionTemplate: AutomationSessionTemplate.optional(),
8965
+ status: AutomationTriggerStatus.optional(),
8966
+ })
8967
+ .strict()
8968
+ .refine((value) => Object.keys(value).some((key) => key !== "expectedRevision"), {
8969
+ message: "automation trigger update is empty",
8970
+ });
8971
+ export type UpdateAutomationTriggerRequest = z.infer<typeof UpdateAutomationTriggerRequest>;
8972
+
8973
+ export const AutomationTrigger = z.object({
8974
+ id: z.string().uuid(),
8975
+ accountId: z.string().uuid(),
8976
+ workspaceId: z.string().uuid(),
8977
+ sourceId: z.string().uuid(),
8978
+ name: z.string(),
8979
+ adapterId: AutomationAdapterId,
8980
+ eventTypes: z.array(z.string()),
8981
+ configuration: z.record(z.string(), z.unknown()),
8982
+ parameters: z.record(z.string(), z.unknown()),
8983
+ sessionTemplate: AutomationSessionTemplate,
8984
+ status: AutomationTriggerStatus,
8985
+ revision: z.number().int().positive(),
8986
+ packInstallationId: z.string().uuid().nullable(),
8987
+ packTemplateId: z.string().nullable(),
8988
+ createdBySubjectId: z.string(),
8989
+ createdAt: z.string(),
8990
+ updatedAt: z.string(),
6930
8991
  });
6931
- export type TriggerScheduledTaskRequest = z.infer<typeof TriggerScheduledTaskRequest>;
8992
+ export type AutomationTrigger = z.infer<typeof AutomationTrigger>;
8993
+
8994
+ export const AutomationRun = z.object({
8995
+ id: z.string().uuid(),
8996
+ accountId: z.string().uuid(),
8997
+ workspaceId: z.string().uuid(),
8998
+ sourceId: z.string().uuid(),
8999
+ triggerId: z.string().uuid(),
9000
+ triggerRevision: z.number().int().positive(),
9001
+ eventId: z.string().uuid(),
9002
+ occurrenceKey: z.string(),
9003
+ status: AutomationRunStatus,
9004
+ sessionId: z.string().uuid().nullable(),
9005
+ errorCode: z.string().nullable(),
9006
+ createdAt: z.string(),
9007
+ updatedAt: z.string(),
9008
+ });
9009
+ export type AutomationRun = z.infer<typeof AutomationRun>;
9010
+
9011
+ export const AutomationWebhookResult = z.object({
9012
+ accepted: z.boolean(),
9013
+ duplicate: z.boolean(),
9014
+ ignoredReason: z.string().nullable(),
9015
+ eventId: z.string().uuid().nullable(),
9016
+ runIds: z.array(z.string().uuid()),
9017
+ });
9018
+ export type AutomationWebhookResult = z.infer<typeof AutomationWebhookResult>;
9019
+
9020
+ export const TriggerAutomationManuallyRequest = z
9021
+ .object({
9022
+ deliveryId: z.string().trim().min(1).max(512).optional(),
9023
+ eventType: z.string().trim().min(1).max(256),
9024
+ occurrenceKey: z.string().trim().min(1).max(1024),
9025
+ payload: AutomationBoundedJson.default({}),
9026
+ occurredAt: z.string().datetime({ offset: true }).nullable().default(null),
9027
+ subject: z.string().trim().min(1).max(512).nullable().default(null),
9028
+ resource: z.string().trim().min(1).max(1024).nullable().default(null),
9029
+ })
9030
+ .strict();
9031
+ export type TriggerAutomationManuallyRequest = z.infer<typeof TriggerAutomationManuallyRequest>;
9032
+
9033
+ export const CapabilityPackAutomationTemplate = z
9034
+ .object({
9035
+ id: z.string().min(1).max(128),
9036
+ name: z.string().min(1).max(200),
9037
+ description: z.string().min(1).max(4096),
9038
+ adapterId: AutomationAdapterId,
9039
+ eventTypes: z.array(z.string().min(1).max(256)).min(1).max(64),
9040
+ sessionTemplate: AutomationSessionTemplate,
9041
+ configuration: AutomationBoundedJson.default({}),
9042
+ connectionRequirement: z.string().min(1).max(128).nullable().default(null),
9043
+ })
9044
+ .strict();
9045
+ export type CapabilityPackAutomationTemplate = z.infer<typeof CapabilityPackAutomationTemplate>;
6932
9046
 
6933
9047
  export const CapabilityPackConnectorAuthModel = z.enum([
6934
9048
  "oauth2_authorization_code_pkce",
@@ -7235,6 +9349,7 @@ export const CapabilityPack = z.preprocess(
7235
9349
  connectors: z.array(CapabilityPackConnector).default([]),
7236
9350
  knowledge: z.array(CapabilityPackKnowledge).default([]),
7237
9351
  scheduledTaskTemplates: z.array(CapabilityPackScheduledTaskTemplate).default([]),
9352
+ automationTemplates: z.array(CapabilityPackAutomationTemplate).max(64).optional(),
7238
9353
  variableSet: CapabilityPackVariableSet.optional(),
7239
9354
  metadata: z.record(z.string(), z.unknown()).default({}),
7240
9355
  })
@@ -7261,6 +9376,17 @@ export const CapabilityPack = z.preprocess(
7261
9376
  }
7262
9377
  componentKeys.add(key);
7263
9378
  });
9379
+ const automationTemplateIds = new Set<string>();
9380
+ pack.automationTemplates?.forEach((template, index) => {
9381
+ if (automationTemplateIds.has(template.id)) {
9382
+ ctx.addIssue({
9383
+ code: "custom",
9384
+ message: `duplicate Pack automation template id: ${template.id}`,
9385
+ path: ["automationTemplates", index, "id"],
9386
+ });
9387
+ }
9388
+ automationTemplateIds.add(template.id);
9389
+ });
7264
9390
  if (!pack.sandboxProviderImages?.modal) {
7265
9391
  return;
7266
9392
  }
@@ -7326,6 +9452,204 @@ export const PackInstallation = z.object({
7326
9452
  });
7327
9453
  export type PackInstallation = z.infer<typeof PackInstallation>;
7328
9454
 
9455
+ // ============ OpenGeni Review Bot — provider-neutral pull-request review automation ============
9456
+
9457
+ export const OPENGENI_PR_REVIEW_PACK_ID = "pr-review" as const;
9458
+ export const OPENGENI_PR_REVIEW_SESSION_ROLE = "pull_request_review" as const;
9459
+
9460
+ export const PrReviewProvider = GitCredentialProvider;
9461
+ export type PrReviewProvider = z.infer<typeof PrReviewProvider>;
9462
+
9463
+ export const PrReviewCredentialKind = /* @__PURE__ */ z.enum(["github_app", "provider_token"]);
9464
+ export type PrReviewCredentialKind = z.infer<typeof PrReviewCredentialKind>;
9465
+
9466
+ export const PrReviewWebhookAuthKind = /* @__PURE__ */ z.enum([
9467
+ "hmac_sha256",
9468
+ "shared_token",
9469
+ "basic",
9470
+ ]);
9471
+ export type PrReviewWebhookAuthKind = z.infer<typeof PrReviewWebhookAuthKind>;
9472
+
9473
+ const PrReviewSecretInput = /* @__PURE__ */ (() => z.string().min(16).max(65_536))();
9474
+
9475
+ export const CreatePrReviewAppRegistrationRequest = /* @__PURE__ */ (() =>
9476
+ z
9477
+ .object({
9478
+ name: z.string().trim().min(1).max(200),
9479
+ provider: PrReviewProvider,
9480
+ providerBaseUrl: z.string().url().max(2048).optional(),
9481
+ appId: z.string().trim().min(1).max(512).optional(),
9482
+ credentialKind: PrReviewCredentialKind,
9483
+ privateKey: PrReviewSecretInput.optional(),
9484
+ accessToken: PrReviewSecretInput.optional(),
9485
+ accessTokenExpiresAt: z.string().datetime().nullable().optional(),
9486
+ webhookSecret: PrReviewSecretInput,
9487
+ webhookUsername: z.string().min(1).max(512).optional(),
9488
+ })
9489
+ .strict()
9490
+ .superRefine((value, context) => {
9491
+ if (value.provider === "github" && value.credentialKind !== "github_app") {
9492
+ context.addIssue({
9493
+ code: "custom",
9494
+ path: ["credentialKind"],
9495
+ message: "GitHub PR Review registrations require a dedicated GitHub App",
9496
+ });
9497
+ }
9498
+ if (value.provider === "github" && (!value.appId || !value.privateKey)) {
9499
+ context.addIssue({
9500
+ code: "custom",
9501
+ path: [!value.appId ? "appId" : "privateKey"],
9502
+ message: "GitHub PR Review registrations require the dedicated App ID and private key",
9503
+ });
9504
+ }
9505
+ if (value.provider !== "github" && value.credentialKind !== "provider_token") {
9506
+ context.addIssue({
9507
+ code: "custom",
9508
+ path: ["credentialKind"],
9509
+ message: "GitLab and Azure DevOps PR Review registrations require a provider token",
9510
+ });
9511
+ }
9512
+ if (value.credentialKind === "provider_token" && !value.accessToken) {
9513
+ context.addIssue({
9514
+ code: "custom",
9515
+ path: ["accessToken"],
9516
+ message: "provider_token credentials require accessToken",
9517
+ });
9518
+ }
9519
+ if (
9520
+ value.credentialKind === "github_app" &&
9521
+ (value.accessToken !== undefined || value.privateKey === undefined)
9522
+ ) {
9523
+ context.addIssue({
9524
+ code: "custom",
9525
+ path: [value.accessToken !== undefined ? "accessToken" : "privateKey"],
9526
+ message: "GitHub App credentials require privateKey and cannot include accessToken",
9527
+ });
9528
+ }
9529
+ if (value.credentialKind === "provider_token" && value.privateKey !== undefined) {
9530
+ context.addIssue({
9531
+ code: "custom",
9532
+ path: ["privateKey"],
9533
+ message: "provider_token credentials cannot include a GitHub privateKey",
9534
+ });
9535
+ }
9536
+ if (value.provider === "github" && value.accessTokenExpiresAt !== undefined) {
9537
+ context.addIssue({
9538
+ code: "custom",
9539
+ path: ["accessTokenExpiresAt"],
9540
+ message: "GitHub App registrations do not use accessTokenExpiresAt",
9541
+ });
9542
+ }
9543
+ if (value.provider === "azure_devops" && !value.webhookUsername) {
9544
+ context.addIssue({
9545
+ code: "custom",
9546
+ path: ["webhookUsername"],
9547
+ message: "Azure DevOps service hooks require a Basic authentication username",
9548
+ });
9549
+ }
9550
+ }))();
9551
+ export type CreatePrReviewAppRegistrationRequest = z.infer<
9552
+ typeof CreatePrReviewAppRegistrationRequest
9553
+ >;
9554
+
9555
+ export const UpdatePrReviewAppRegistrationRequest = /* @__PURE__ */ (() =>
9556
+ z
9557
+ .object({
9558
+ name: z.string().trim().min(1).max(200).optional(),
9559
+ accessToken: PrReviewSecretInput.optional(),
9560
+ privateKey: PrReviewSecretInput.optional(),
9561
+ accessTokenExpiresAt: z.string().datetime().nullable().optional(),
9562
+ webhookSecret: PrReviewSecretInput.optional(),
9563
+ webhookUsername: z.string().min(1).max(512).optional(),
9564
+ status: z.enum(["active", "disabled"]).optional(),
9565
+ })
9566
+ .strict()
9567
+ .refine((value) => Object.keys(value).length > 0, {
9568
+ message: "PR Review registration update must change at least one field",
9569
+ }))();
9570
+ export type UpdatePrReviewAppRegistrationRequest = z.infer<
9571
+ typeof UpdatePrReviewAppRegistrationRequest
9572
+ >;
9573
+
9574
+ export const PrReviewAppRegistration = /* @__PURE__ */ (() =>
9575
+ z.object({
9576
+ id: z.string().uuid(),
9577
+ sourceId: z.string().uuid(),
9578
+ accountId: z.string().uuid(),
9579
+ workspaceId: z.string().uuid(),
9580
+ name: z.string(),
9581
+ provider: PrReviewProvider,
9582
+ providerBaseUrl: z.string().url(),
9583
+ appId: z.string().nullable(),
9584
+ credentialKind: PrReviewCredentialKind,
9585
+ hasCredential: z.boolean(),
9586
+ accessTokenExpiresAt: z.string().nullable(),
9587
+ webhookAuthKind: PrReviewWebhookAuthKind,
9588
+ hasWebhookSecret: z.boolean(),
9589
+ webhookUsername: z.string().nullable(),
9590
+ webhookPath: z.string(),
9591
+ status: z.enum(["active", "disabled"]),
9592
+ createdBySubjectId: z.string(),
9593
+ createdAt: z.string(),
9594
+ updatedAt: z.string(),
9595
+ }))();
9596
+ export type PrReviewAppRegistration = z.infer<typeof PrReviewAppRegistration>;
9597
+
9598
+ export const CreatePrReviewRepositoryBindingRequest = /* @__PURE__ */ (() =>
9599
+ z
9600
+ .object({
9601
+ registrationId: z.string().uuid(),
9602
+ repositoryUri: z.string().url().max(2048),
9603
+ repositoryFullName: z.string().trim().min(1).max(1024),
9604
+ providerRepositoryId: z.union([z.string().min(1).max(512), z.number().int().positive()]),
9605
+ installationId: z.union([z.string().min(1).max(512), z.number().int().positive()]).optional(),
9606
+ projectId: z.union([z.string().min(1).max(512), z.number().int().positive()]).optional(),
9607
+ model: z.string().min(1).max(512).nullable().optional(),
9608
+ additionalInstructions: z.string().max(16_384).nullable().optional(),
9609
+ status: z.enum(["active", "disabled"]).default("active"),
9610
+ })
9611
+ .strict())();
9612
+ export type CreatePrReviewRepositoryBindingRequest = z.infer<
9613
+ typeof CreatePrReviewRepositoryBindingRequest
9614
+ >;
9615
+
9616
+ export const UpdatePrReviewRepositoryBindingRequest = /* @__PURE__ */ (() =>
9617
+ z
9618
+ .object({
9619
+ model: z.string().min(1).max(512).nullable().optional(),
9620
+ additionalInstructions: z.string().max(16_384).nullable().optional(),
9621
+ status: z.enum(["active", "disabled"]).optional(),
9622
+ })
9623
+ .strict()
9624
+ .refine((value) => Object.keys(value).length > 0, {
9625
+ message: "PR Review repository update must change at least one field",
9626
+ }))();
9627
+ export type UpdatePrReviewRepositoryBindingRequest = z.infer<
9628
+ typeof UpdatePrReviewRepositoryBindingRequest
9629
+ >;
9630
+
9631
+ export const PrReviewRepositoryBinding = /* @__PURE__ */ (() =>
9632
+ z.object({
9633
+ id: z.string().uuid(),
9634
+ triggerId: z.string().uuid(),
9635
+ accountId: z.string().uuid(),
9636
+ workspaceId: z.string().uuid(),
9637
+ registrationId: z.string().uuid(),
9638
+ provider: PrReviewProvider,
9639
+ repositoryUri: z.string(),
9640
+ repositoryFullName: z.string(),
9641
+ providerRepositoryId: z.string(),
9642
+ installationId: z.string().nullable(),
9643
+ projectId: z.string().nullable(),
9644
+ model: z.string().nullable(),
9645
+ additionalInstructions: z.string().nullable(),
9646
+ status: z.enum(["active", "disabled"]),
9647
+ createdBySubjectId: z.string(),
9648
+ createdAt: z.string(),
9649
+ updatedAt: z.string(),
9650
+ }))();
9651
+ export type PrReviewRepositoryBinding = z.infer<typeof PrReviewRepositoryBinding>;
9652
+
7329
9653
  export const EnablePackRequest = withVariableSetIdAlias({
7330
9654
  variableSetId: z.string().uuid().optional(),
7331
9655
  environmentId: z.string().uuid().optional(),
@@ -7547,6 +9871,8 @@ export const OPENGENI_PERSONAL_SLACK_MCP_URL = "https://mcp.slack.com/mcp" as co
7547
9871
  export const OPENGENI_SLACK_BOT_CREDENTIAL_ROLE = "opengeni_slack_bot" as const;
7548
9872
  export const OPENGENI_SLACK_BOT_CREDENTIAL_LABEL = "OpenGeni Slack bot" as const;
7549
9873
  export const OPENGENI_SLACK_BOT_SESSION_METADATA_KEY = "opengeniSlackBotConnectionId" as const;
9874
+ export const OpenGeniSlackBotDisplayName = z.enum(["OpenGeni", "OpenGeni Staging"]);
9875
+ export type OpenGeniSlackBotDisplayName = z.infer<typeof OpenGeniSlackBotDisplayName>;
7550
9876
  export const OpenGeniSlackBotConnectionMetadata = z
7551
9877
  .object({
7552
9878
  credentialRole: z.literal(OPENGENI_SLACK_BOT_CREDENTIAL_ROLE),
@@ -7555,7 +9881,7 @@ export const OpenGeniSlackBotConnectionMetadata = z
7555
9881
  slackTeamName: z.string().min(1).max(256),
7556
9882
  botUserId: z.string().min(1).max(64),
7557
9883
  botId: z.string().min(1).max(64),
7558
- botDisplayName: z.literal("OpenGeni"),
9884
+ botDisplayName: OpenGeniSlackBotDisplayName,
7559
9885
  verifiedAt: z.string().datetime({ offset: true }),
7560
9886
  })
7561
9887
  .passthrough();
@@ -7605,6 +9931,8 @@ export type FikenOAuthStartResponse = z.infer<typeof FikenOAuthStartResponse>;
7605
9931
 
7606
9932
  export const ConnectionMetadata = z.object({
7607
9933
  id: z.string().uuid(),
9934
+ /** Opaque owner-only handle used to manage this personal connection's grants. */
9935
+ authorityId: z.string().uuid().optional(),
7608
9936
  accountId: z.string().uuid(),
7609
9937
  workspaceId: z.string().uuid(),
7610
9938
  subjectId: z.string().nullable(),
@@ -7734,7 +10062,7 @@ export const SlackInstallationBinding = z.object({
7734
10062
  slackTeamName: z.string().min(1).max(256),
7735
10063
  botId: z.string().min(1).max(64),
7736
10064
  botUserId: z.string().min(1).max(64),
7737
- botDisplayName: z.literal("OpenGeni"),
10065
+ botDisplayName: OpenGeniSlackBotDisplayName,
7738
10066
  state: SlackInstallationBindingState,
7739
10067
  quarantineReason: z.string().min(1).nullable(),
7740
10068
  version: z.number().int().positive(),
@@ -8240,6 +10568,43 @@ export const IntegrationFacetDefinitionSummary = z
8240
10568
  .strict();
8241
10569
  export type IntegrationFacetDefinitionSummary = z.infer<typeof IntegrationFacetDefinitionSummary>;
8242
10570
 
10571
+ /**
10572
+ * Presentation-only consent copy for an integration or connector. Never grants
10573
+ * a scope, selects a connection, or replaces server-side authorization; the UI
10574
+ * falls back to generic copy for any omitted field.
10575
+ */
10576
+ export const IntegrationPresentation = z
10577
+ .object({
10578
+ providerName: z.string().min(1).max(120).optional(),
10579
+ icon: z.enum(["calendar", "cloud", "contacts", "files", "mail"]).optional(),
10580
+ introduction: z.string().min(1).max(500).optional(),
10581
+ capabilities: z
10582
+ .array(
10583
+ z
10584
+ .object({
10585
+ title: z.string().min(1).max(160),
10586
+ description: z.string().min(1).max(500),
10587
+ })
10588
+ .strict(),
10589
+ )
10590
+ .max(8)
10591
+ .optional(),
10592
+ permissionSummary: z.string().min(1).max(500).optional(),
10593
+ scopeLabels: z
10594
+ .record(
10595
+ z.string().min(1).max(1024),
10596
+ z
10597
+ .object({
10598
+ label: z.string().min(1).max(160),
10599
+ description: z.string().min(1).max(500),
10600
+ })
10601
+ .strict(),
10602
+ )
10603
+ .optional(),
10604
+ })
10605
+ .strict();
10606
+ export type IntegrationPresentation = z.infer<typeof IntegrationPresentation>;
10607
+
8243
10608
  export const IntegrationDefinitionSummary = z
8244
10609
  .object({
8245
10610
  id: z.string().min(1).max(128),
@@ -8258,6 +10623,7 @@ export const IntegrationDefinitionSummary = z
8258
10623
  scopes: z.array(z.string().min(1).max(1024)).max(256),
8259
10624
  })
8260
10625
  .strict(),
10626
+ presentation: IntegrationPresentation.optional(),
8261
10627
  facets: z.array(IntegrationFacetDefinitionSummary).max(128),
8262
10628
  })
8263
10629
  .strict();
@@ -8291,6 +10657,8 @@ export const IntegrationFacetBindingSummary = z
8291
10657
  lastErrorCode: z.string().min(1).max(120).nullable(),
8292
10658
  createdAt: z.string().datetime({ offset: true }),
8293
10659
  updatedAt: z.string().datetime({ offset: true }),
10660
+ directlyOwned: z.boolean(),
10661
+ owners: z.array(CapabilityComponentOwner),
8294
10662
  })
8295
10663
  .strict();
8296
10664
  export type IntegrationFacetBindingSummary = z.infer<typeof IntegrationFacetBindingSummary>;
@@ -8813,11 +11181,59 @@ export const UninstallPluginResult = z
8813
11181
  .strict();
8814
11182
  export type UninstallPluginResult = z.infer<typeof UninstallPluginResult>;
8815
11183
 
11184
+ export const SessionBackgroundCommandState = z.enum(["running", "stopping", "exited", "lost"]);
11185
+ export type SessionBackgroundCommandState = z.infer<typeof SessionBackgroundCommandState>;
11186
+
11187
+ export const SessionBackgroundCommandProvider = z.enum(["managed", "connected_machine"]);
11188
+ export type SessionBackgroundCommandProvider = z.infer<typeof SessionBackgroundCommandProvider>;
11189
+
11190
+ export const SessionBackgroundCommandActivity = z
11191
+ .object({
11192
+ state: z.enum(["running", "stopping"]),
11193
+ count: z.number().int().positive(),
11194
+ })
11195
+ .strict();
11196
+ export type SessionBackgroundCommandActivity = z.infer<typeof SessionBackgroundCommandActivity>;
11197
+
11198
+ export const SessionBackgroundCommand = z
11199
+ .object({
11200
+ id: z.string().uuid(),
11201
+ workspaceId: z.string().uuid(),
11202
+ sessionId: z.string().uuid(),
11203
+ provider: SessionBackgroundCommandProvider,
11204
+ state: SessionBackgroundCommandState,
11205
+ commandPreview: z.string().max(512),
11206
+ cancelRequestedAt: z.string().nullable(),
11207
+ exitCode: z.number().int().nullable(),
11208
+ settlementReason: z.string().nullable(),
11209
+ startedAt: z.string(),
11210
+ settledAt: z.string().nullable(),
11211
+ updatedAt: z.string(),
11212
+ })
11213
+ .strict();
11214
+ export type SessionBackgroundCommand = z.infer<typeof SessionBackgroundCommand>;
11215
+
11216
+ export const SessionBackgroundCommandListResponse = z
11217
+ .object({ commands: z.array(SessionBackgroundCommand).max(1000) })
11218
+ .strict();
11219
+ export type SessionBackgroundCommandListResponse = z.infer<
11220
+ typeof SessionBackgroundCommandListResponse
11221
+ >;
11222
+
11223
+ export const CancelSessionBackgroundCommandResult = z
11224
+ .object({ command: SessionBackgroundCommand, accepted: z.boolean() })
11225
+ .strict();
11226
+ export type CancelSessionBackgroundCommandResult = z.infer<
11227
+ typeof CancelSessionBackgroundCommandResult
11228
+ >;
11229
+
8816
11230
  export const Session = z.object({
8817
11231
  id: z.string().uuid(),
8818
11232
  workspaceId: z.string().uuid(),
8819
11233
  accountId: z.string().uuid(),
8820
11234
  status: SessionStatus,
11235
+ /** Additive list projection. Detail reads may omit it. */
11236
+ backgroundCommandActivity: SessionBackgroundCommandActivity.optional(),
8821
11237
  initialMessage: z.string(),
8822
11238
  title: z.string().nullable(),
8823
11239
  titleSource: z.enum(["user", "agent"]).nullable(),
@@ -8846,6 +11262,8 @@ export const Session = z.object({
8846
11262
  createdBy: TurnInitiator,
8847
11263
  createdByContext: TurnInitiatorContext,
8848
11264
  model: z.string(),
11265
+ reasoningEffort: ReasoningEffort,
11266
+ latencyMode: LatencyMode,
8849
11267
  sandboxBackend: SandboxBackend,
8850
11268
  // The OS the session's box runs. Defaults to 'linux' (today's only OS).
8851
11269
  sandboxOs: SandboxOs,
@@ -8929,6 +11347,17 @@ export const Session = z.object({
8929
11347
  pinnedAt: z.string().nullable().default(null),
8930
11348
  /** Optimistic pin-state revision; zero represents an absent pin relation. */
8931
11349
  pinVersion: z.number().int().nonnegative().default(0),
11350
+ /** Personal explicit acknowledgment state; opening the session never clears it. */
11351
+ unread: z.boolean().default(false),
11352
+ /** Personal power-user label for work the member intends to continue. */
11353
+ activelyWorking: z.boolean().default(false),
11354
+ /** Optimistic revision for unread/actively-working state. */
11355
+ attentionVersion: z.number().int().nonnegative().default(0),
11356
+ /** Personal archive state. Archived roots and their descendants leave the ordinary list. */
11357
+ archived: z.boolean().default(false),
11358
+ archivedAt: z.string().nullable().default(null),
11359
+ /** Optimistic archive-state revision; zero represents an absent personal relation. */
11360
+ archiveVersion: z.number().int().nonnegative().default(0),
8932
11361
  /**
8933
11362
  * Server-authoritative hierarchy summary populated on session-list reads.
8934
11363
  * Detail reads may omit it. The rail uses this instead of guessing a tree
@@ -8943,10 +11372,24 @@ export const Session = z.object({
8943
11372
  attentionDescendants: z.number().int().nonnegative(),
8944
11373
  pausedDescendants: z.number().int().nonnegative(),
8945
11374
  failedDescendants: z.number().int().nonnegative(),
11375
+ unreadDescendants: z.number().int().nonnegative().optional(),
11376
+ activelyWorkingDescendants: z.number().int().nonnegative().optional(),
11377
+ /**
11378
+ * Earliest moment one of the counted `attentionDescendants` entered
11379
+ * `requires_action` (the oldest still-open `requires_action` turn among
11380
+ * those descendants). Null when none is waiting; omitted by older servers.
11381
+ */
11382
+ attentionSince: z.string().nullable().optional(),
8946
11383
  /** Counts are lower bounds rather than exact totals when true. */
8947
11384
  truncated: z.boolean().default(false),
8948
11385
  })
8949
11386
  .optional(),
11387
+ /**
11388
+ * When this session's own open turn entered `requires_action`. Populated by
11389
+ * list and lineage reads for sessions whose status is `requires_action`;
11390
+ * null otherwise and omitted by older servers or detail reads.
11391
+ */
11392
+ requiresActionSince: z.string().nullable().optional(),
8950
11393
  createdAt: z.string(),
8951
11394
  updatedAt: z.string(),
8952
11395
  });
@@ -9055,6 +11498,7 @@ export type SessionLineageResponse = z.infer<typeof SessionLineageResponse>;
9055
11498
 
9056
11499
  export const SessionEventType = z.enum([
9057
11500
  "session.created",
11501
+ "session.personal_resources.attached",
9058
11502
  "session.visibility.changed",
9059
11503
  // Defensive read/transport projection for a malformed or historically
9060
11504
  // oversized retained event envelope. The original row stays durable; this
@@ -9083,13 +11527,20 @@ export const SessionEventType = z.enum([
9083
11527
  "turn.superseded",
9084
11528
  "turn.recovery.requested",
9085
11529
  "turn.capacity_waiting",
11530
+ // Compact, attempt-fenced user-visible worker preparation checkpoints. The
11531
+ // payload phase is a closed enum and terminal events carry durationMs; no
11532
+ // session-specific value is ever promoted into Prometheus labels.
11533
+ "turn.startup.phase.started",
11534
+ "turn.startup.phase.completed",
11535
+ "turn.startup.phase.failed",
9086
11536
  "agent.message.delta",
9087
11537
  "agent.message.completed",
9088
11538
  "agent.reasoning.delta",
9089
11539
  "agent.toolCall.created",
9090
11540
  "agent.toolCall.output",
9091
- // Attempt-fenced Codex Responses lifecycle metadata (request identity,
9092
- // deadlines, first-byte/terminal phase, provider request id). Never body/auth.
11541
+ // Attempt-fenced provider Responses lifecycle metadata (request identity,
11542
+ // liveness policy, first-event/terminal phase, provider request id). Never
11543
+ // request body, credentials, or model output.
9093
11544
  "agent.model.request",
9094
11545
  "agent.model.usage",
9095
11546
  "tool.auth_needed",
@@ -9102,6 +11553,8 @@ export const SessionEventType = z.enum([
9102
11553
  "sandbox.operation.started",
9103
11554
  "sandbox.operation.completed",
9104
11555
  "sandbox.operation.failed",
11556
+ "session.command.backgrounded",
11557
+ "session.command.finished",
9105
11558
  "sandbox.command.output.delta",
9106
11559
  "artifact.created",
9107
11560
  "goal.set",
@@ -9113,6 +11566,7 @@ export const SessionEventType = z.enum([
9113
11566
  "goal.paused",
9114
11567
  "goal.resumed",
9115
11568
  "goal.cleared",
11569
+ "goal.held",
9116
11570
  "goal.continuation",
9117
11571
  "system.update.pending",
9118
11572
  "system.update.delivered",
@@ -9305,6 +11759,7 @@ export const SESSION_EVENT_RAW_DELTA_TYPES = [
9305
11759
  export const SESSION_EVENT_SEMANTIC_CLASS_TYPES = {
9306
11760
  control: [
9307
11761
  "session.status.changed",
11762
+ "session.command.backgrounded",
9308
11763
  "session.requiresAction",
9309
11764
  "session.humanInput.requested",
9310
11765
  "user.pause",
@@ -9319,6 +11774,7 @@ export const SESSION_EVENT_SEMANTIC_CLASS_TYPES = {
9319
11774
  "goal.paused",
9320
11775
  "goal.resumed",
9321
11776
  "goal.cleared",
11777
+ "goal.held",
9322
11778
  "goal.continuation",
9323
11779
  "system.update.pending",
9324
11780
  "system.update.delivered",
@@ -9351,6 +11807,7 @@ export const SESSION_EVENT_SEMANTIC_CLASS_TYPES = {
9351
11807
  "recording.available",
9352
11808
  "recording.failed",
9353
11809
  "terminal.pty.exited",
11810
+ "session.command.finished",
9354
11811
  ],
9355
11812
  failure: [
9356
11813
  "session.event.envelope_omitted",
@@ -9677,7 +12134,9 @@ export type FsNodeType = z.infer<typeof FsNodeType>;
9677
12134
  // depth>0; the tree lazy-expands via repeated depth-1 lists at deeper paths.
9678
12135
  export interface FsTreeNode {
9679
12136
  name: string;
9680
- path: string; // workspace-relative POSIX, no leading slash
12137
+ // Same namespace as the request: workspace-relative for relative requests,
12138
+ // canonical target path for requests rooted at FileSystem.root.
12139
+ path: string;
9681
12140
  type: z.infer<typeof FsNodeType>;
9682
12141
  sizeBytes: number | null; // null for dirs
9683
12142
  mtimeMs: number | null;
@@ -9699,7 +12158,9 @@ export const FsTreeNode: z.ZodType<FsTreeNode> = z.lazy(() =>
9699
12158
  ) as z.ZodType<FsTreeNode>;
9700
12159
 
9701
12160
  export const FsListRequest = z.object({
9702
- path: z.string().default(""), // "" = workspace root
12161
+ // "" = workspace root; canonical absolute paths must stay within the
12162
+ // selected target's advertised FileSystem.root.
12163
+ path: z.string().default(""),
9703
12164
  depth: z.number().int().min(0).max(8).default(1),
9704
12165
  maxEntries: z.number().int().positive().max(20_000).default(2_000),
9705
12166
  includeHidden: z.boolean().default(true),
@@ -11221,134 +13682,154 @@ export const SessionControlResponse = z.object({
11221
13682
  });
11222
13683
  export type SessionControlResponse = z.infer<typeof SessionControlResponse>;
11223
13684
 
11224
- export const CreateSessionRequest = withVariableSetIdAlias({
11225
- /**
11226
- * Optional UUID preallocated by an embedding host. This lets the host durably
11227
- * link its own projection before OpenGeni admits the initial turn. Replays
11228
- * must pair it with the same idempotency key; OpenGeni never derives host
11229
- * identity or authorization from the UUID.
11230
- */
11231
- requestedSessionId: z.string().uuid().optional(),
11232
- initialMessage: z.string().min(1).optional(),
11233
- // Creates the durable session shell without fabricating a user message or
11234
- // starting an underlying agent turn. Realtime can then become the first
11235
- // interaction and use the ordinary Send/Steer path when it delegates.
11236
- startMode: z.literal("realtime").optional(),
11237
- // System-level host context for the initial turn only. Unlike `instructions`,
11238
- // this does not persist into later turns and is never emitted as a user event.
11239
- turnInstructions: z.string().trim().min(1).max(32768).optional(),
11240
- // Per-session agent persona/system instructions (org-visible metadata, NOT a
11241
- // secret). Rides the SAME system-level instructions channel the per-workspace
11242
- // agentInstructions rides, composed AFTER the workspace persona so it refines
11243
- // it for this one session how a host delivers per-agent-type prompts without
11244
- // leaking them into the user-visible timeline (it is NEVER emitted as an
11245
- // event, unlike goal/initialMessage). Trimmed, non-empty. The 32768-char cap
11246
- // matches the codebase's largest free-form string convention (workspace
11247
- // variable set variable values). Absent byte-identical to today.
11248
- instructions: z.string().trim().min(1).max(32768).optional(),
11249
- // Immutable prompt-policy role binding for matching one activated role
11250
- // policy. This never derives from or grants a human workspace membership
11251
- // role. Existing callers may continue to use normalized metadata.role as a
11252
- // compatibility fallback by omitting this field.
11253
- policyRole: WorkspaceInstructionPolicyRoleKeyInput.optional(),
11254
- // For an agent-created child, omission inherits the trusted immediate
11255
- // parent's repository/file context. An explicit array, including [], is
11256
- // authoritative. Top-level omission remains []. Presence is resolved from
11257
- // the raw request because this Zod default erases absent-vs-empty.
11258
- resources: z.array(ResourceRef).default([]),
11259
- // Inline skills are fixed onto the session. Child omission inherits the
11260
- // trusted parent's selection; an explicit array, including [], wins.
11261
- skills: SessionSkills.default([]),
11262
- // The same child omission rule applies to selected MCP tool refs. Top-level
11263
- // omission still applies workspace-default capability MCP tools; explicit []
11264
- // suppresses those defaults (the first-party OpenGeni server remains added).
11265
- tools: z.array(ToolRef).default([]),
11266
- metadata: z.record(z.string(), z.unknown()).default({}),
11267
- model: z.string().min(1).optional(),
11268
- reasoningEffort: ReasoningEffort.optional(),
11269
- latencyMode: LatencyMode.optional(),
11270
- sandboxBackend: SandboxBackend.optional(),
11271
- // The enrolled machine (a sandbox id) to run this session on; seeds the
11272
- // active-sandbox pointer at creation so the FIRST turn routes to the chosen
11273
- // machine (race-free: the pointer is committed before the worker turn
11274
- // workflow can read it). An invalid/unowned/offline target fails the create.
11275
- targetSandboxId: z.string().uuid().optional(),
11276
- // The working directory the targeted machine runs the session under — the
11277
- // path/cwd base for its agent exec, terminal, and file dock. Free-form pass-
11278
- // through: a launch-workspace_root-relative subdir or an absolute machine path
11279
- // (the agent's resolve_cwd handles both). Only valid WITH targetSandboxId
11280
- // (workingDir alone is a 422); omitted ⇒ the machine's default workspace_root.
11281
- workingDir: z.string().min(1).optional(),
11282
- // Variable set attachment is fixed at session creation; follow-up
11283
- // user.message events cannot switch or add one.
11284
- variableSetId: z.string().uuid().optional(),
11285
- environmentId: z.string().uuid().optional(),
11286
- // The rig to bind this session to (M3). Its ACTIVE version is resolved and
11287
- // FROZEN onto the session at create. Omitted inherit the workspace default;
11288
- // null explicitly create a rig-less session; UUID bind that exact rig.
11289
- // An id that does not name a rig in the workspace is a 422.
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(),
11295
- goal: GoalSpec.optional(),
11296
- clientEventId: SessionOperationKey.optional(),
11297
- // Workspace-scoped CREATE idempotency key: collapses concurrent/retried
11298
- // create calls carrying the same key to a single session (partial unique
11299
- // index on (workspace_id, create_idempotency_key)). Distinct from
11300
- // clientEventId, whose uniqueness is per-session and so cannot dedup the
11301
- // creation of a brand-new session. Absent means no create-dedup (each call
11302
- // is an independent create).
11303
- idempotencyKey: z.string().min(1).max(200).optional(),
11304
- // The exact actor-private pre-session draft revision represented by this
11305
- // create. The durable initializer consumes only this revision. A newer draft
11306
- // written by a sibling tab survives, while every failed pre-initialization
11307
- // create leaves the submitted draft intact.
11308
- expectedNewSessionDraftRevision: z.number().int().nonnegative().optional(),
11309
- // A child may lower its inherited limit freely; an increase requires
11310
- // workspace:admin and is checked again at the DB transaction boundary.
11311
- maxNestedAgentDepth: NestedAgentDepthValue.optional(),
11312
- // Permissions the session's first-party MCP token should carry. A top-level
11313
- // omission uses the deployment's worker default; a child omission inherits
11314
- // the creating session's effective grant. An explicit set is capped at
11315
- // creation: every requested permission must be held by the creating grant.
11316
- // A goal-bearing session whose explicit/effective set omits goals:manage is
11317
- // rejected; creation never silently expands a child beyond that set.
11318
- firstPartyMcpPermissions: z.array(Permission).optional(),
11319
- // Exact model-visible selection from the broad first-party OpenGeni MCP
11320
- // catalog. Omission selects the safe non-connector default; [] intentionally
11321
- // exposes none.
11322
- // This does not grant authority: every registered tool is permission-gated.
11323
- firstPartyMcpTools: z.array(FirstPartyMcpToolName).optional(),
11324
- // Third-party MCP servers attached only to this session. For an agent-created
11325
- // child, omission snapshots its trusted immediate parent's server definitions,
11326
- // policies, connection refs, and encrypted credentials. Explicit arrays,
11327
- // including [], are authoritative; non-empty explicit arrays require attach
11328
- // permission. Credential headers are write-only: create responses and events
11329
- // expose only SessionMcpServerMetadata.
11330
- mcpServers: z.array(SessionMcpServerInput).max(SESSION_MCP_SERVERS_MAX).default([]),
11331
- // Shared-sandbox placement (addendum 05 §D.1). Three-way union; OMITTED
11332
- // today's behavior (a context-dependent default resolved server-side: from
11333
- // inside a session "shared" with the creator's box, top-level → "new").
11334
- // - "shared": join the CREATOR's box. Requires a parent session (inferred
11335
- // from the worker-signed sessionId claim, never caller-supplied);
11336
- // top-level "shared" is a 422.
11337
- // - "new": mint a fresh singleton box (group the new session's id).
11338
- // - {groupId}: join a SPECIFIC sibling group in THIS workspace (manager
11339
- // fan-out). Validated workspace-scoped (cross-workspace 404).
11340
- // A shared spawn inherits the box's (backend, os) — it is literally the same
11341
- // box; the child cannot pick its own backend. Cross-workspace sharing is
11342
- // forbidden by construction (the parent/group reads are RLS-workspace-scoped).
11343
- // ENV-AWARE: the box's variable set is fixed at creation, so a share requires
11344
- // the SAME variableSetId as the creator's box. On a mismatch the inherited
11345
- // default silently falls back to an own box; an explicit "shared"/{groupId}
11346
- // request 422s at create (instead of the first turn dying on the SDK's
11347
- // manifest-env guard).
11348
- sandbox: z
11349
- .union([z.literal("shared"), z.literal("new"), z.object({ groupId: z.string().uuid() })])
11350
- .optional(),
11351
- }).superRefine((value, context) => {
13685
+ export const SESSION_INSTRUCTIONS_MAX_CHARACTERS = 65_536;
13686
+
13687
+ export const CreateSessionRequest = withVariableSetIdAlias(
13688
+ {
13689
+ /**
13690
+ * Optional UUID preallocated by an embedding host. This lets the host durably
13691
+ * link its own projection before OpenGeni admits the initial turn. Replays
13692
+ * must pair it with the same idempotency key; OpenGeni never derives host
13693
+ * identity or authorization from the UUID.
13694
+ */
13695
+ requestedSessionId: z.string().uuid().optional(),
13696
+ /** Top-level omission is workspace-visible. Agent-child omission inherits
13697
+ * the exact parent visibility; cross-visibility child creation is rejected.
13698
+ * Top-level private creation is an activated managed-cookie owning-human
13699
+ * capability, while a private child uses an exact live-parent-attempt
13700
+ * database capability. Both commit atomically. */
13701
+ visibility: SessionVisibility.default("workspace"),
13702
+ initialMessage: z.string().min(1).optional(),
13703
+ // Creates the durable session shell without fabricating a user message or
13704
+ // starting an underlying agent turn. Realtime can then become the first
13705
+ // interaction and use the ordinary Send/Steer path when it delegates.
13706
+ startMode: z.literal("realtime").optional(),
13707
+ // Model-visible application context attached to the initial user message.
13708
+ // Standard timeline rendering omits it, while full event/audit reads retain
13709
+ // it. This is ordinary user-role content, not secret or privileged input.
13710
+ modelContext: z.string().trim().min(1).max(32768).optional(),
13711
+ // Per-session agent persona/system instructions (org-visible metadata, NOT a
13712
+ // secret). Rides the SAME system-level instructions channel the per-workspace
13713
+ // agentInstructions rides, composed AFTER the workspace persona so it refines
13714
+ // it for this one session — how a host delivers per-agent-type prompts without
13715
+ // leaking them into the user-visible timeline (it is NEVER emitted as an
13716
+ // event, unlike goal/initialMessage). Trimmed, non-empty, and bounded by the
13717
+ // shared durable session-instruction contract. Absent byte-identical to
13718
+ // today.
13719
+ instructions: z.string().trim().min(1).max(SESSION_INSTRUCTIONS_MAX_CHARACTERS).optional(),
13720
+ // Immutable prompt-policy role binding for matching one activated role
13721
+ // policy. This never derives from or grants a human workspace membership
13722
+ // role. Existing callers may continue to use normalized metadata.role as a
13723
+ // compatibility fallback by omitting this field.
13724
+ policyRole: WorkspaceInstructionPolicyRoleKeyInput.optional(),
13725
+ // For an agent-created child, omission inherits the trusted immediate
13726
+ // parent's repository/file context. An explicit array, including [], is
13727
+ // authoritative. Top-level omission remains []. Presence is resolved from
13728
+ // the raw request because this Zod default erases absent-vs-empty.
13729
+ resources: z.array(ResourceRef).default([]),
13730
+ // Inline skills are fixed onto the session. Child omission inherits the
13731
+ // trusted parent's selection; an explicit array, including [], wins.
13732
+ skills: SessionSkills.default([]),
13733
+ // The same child omission rule applies to selected MCP tool refs. Top-level
13734
+ // omission still applies workspace-default capability MCP tools; explicit []
13735
+ // suppresses those defaults (the first-party OpenGeni server remains added).
13736
+ tools: z.array(ToolRef).default([]),
13737
+ metadata: z.record(z.string(), z.unknown()).default({}),
13738
+ model: z.string().min(1).optional(),
13739
+ reasoningEffort: ReasoningEffort.optional(),
13740
+ latencyMode: LatencyMode.optional(),
13741
+ sandboxBackend: SandboxBackend.optional(),
13742
+ // The enrolled machine (a sandbox id) to run this session on; seeds the
13743
+ // active-sandbox pointer at creation so the FIRST turn routes to the chosen
13744
+ // machine (race-free: the pointer is committed before the worker turn
13745
+ // workflow can read it). An invalid/unowned/offline target fails the create.
13746
+ targetSandboxId: z.string().uuid().optional(),
13747
+ // The working directory the targeted machine runs the session under. It may
13748
+ // be absolute or relative to the machine's persisted Hello root; the server
13749
+ // stores the resolved absolute value. Tilde is rejected because the control
13750
+ // plane has no authenticated home-directory fact. Only valid WITH
13751
+ // targetSandboxId; omitted selects the reported root.
13752
+ workingDir: z.string().min(1).optional(),
13753
+ // Variable set attachment is fixed at session creation; follow-up
13754
+ // user.message events cannot switch or add one.
13755
+ variableSetId: z.string().uuid().optional(),
13756
+ environmentId: z.string().uuid().optional(),
13757
+ // The rig to bind this session to (M3). Its ACTIVE version is resolved and
13758
+ // FROZEN onto the session at create. Omitted ⇒ inherit the workspace default;
13759
+ // null explicitly create a rig-less session; UUID bind that exact rig.
13760
+ // An id that does not name a rig in the workspace is a 422.
13761
+ rigId: z.string().uuid().nullable().optional(),
13762
+ // The workspace channel to file this session under (rail organization only).
13763
+ // Omitted/null unfiled (inbox). An id that does not name a channel in the
13764
+ // workspace is a 422.
13765
+ channelId: z.string().uuid().nullable().optional(),
13766
+ goal: GoalSpec.optional(),
13767
+ clientEventId: SessionOperationKey.optional(),
13768
+ // Workspace-scoped CREATE idempotency key: collapses concurrent/retried
13769
+ // create calls carrying the same key to a single session (partial unique
13770
+ // index on (workspace_id, create_idempotency_key)). Distinct from
13771
+ // clientEventId, whose uniqueness is per-session and so cannot dedup the
13772
+ // creation of a brand-new session. Absent means no create-dedup (each call
13773
+ // is an independent create).
13774
+ idempotencyKey: z.string().min(1).max(200).optional(),
13775
+ // The exact actor-private pre-session draft revision represented by this
13776
+ // create. An ordinary create consumes only this revision. A realtime create
13777
+ // preserves the editable draft and atomically updates only successful-create
13778
+ // selection history. A newer sibling draft survives, while every failed
13779
+ // pre-initialization create leaves the submitted draft intact.
13780
+ expectedNewSessionDraftRevision: z.number().int().nonnegative().optional(),
13781
+ // A child may lower its inherited limit freely; an increase requires
13782
+ // workspace:admin and is checked again at the DB transaction boundary.
13783
+ maxNestedAgentDepth: NestedAgentDepthValue.optional(),
13784
+ // Permissions the session's first-party MCP token should carry. A top-level
13785
+ // omission uses the deployment's worker default; a child omission inherits
13786
+ // the creating session's effective grant. An explicit set is capped at
13787
+ // creation: every requested permission must be held by the creating grant.
13788
+ // A goal-bearing session whose explicit/effective set omits goals:manage is
13789
+ // rejected; creation never silently expands a child beyond that set.
13790
+ firstPartyMcpPermissions: z.array(Permission).optional(),
13791
+ // Exact model-visible selection from the broad first-party OpenGeni MCP
13792
+ // catalog. Omission selects the safe non-connector default; [] intentionally
13793
+ // exposes none.
13794
+ // This does not grant authority: every registered tool is permission-gated.
13795
+ firstPartyMcpTools: z.array(FirstPartyMcpToolName).optional(),
13796
+ // Third-party MCP servers attached only to this session. For an agent-created
13797
+ // child, omission snapshots its trusted immediate parent's server definitions,
13798
+ // policies, connection refs, and encrypted credentials. Explicit arrays,
13799
+ // including [], are authoritative; non-empty explicit arrays require attach
13800
+ // permission. Credential headers are write-only: create responses and events
13801
+ // expose only SessionMcpServerMetadata.
13802
+ mcpServers: z.array(SessionMcpServerInput).max(SESSION_MCP_SERVERS_MAX).default([]),
13803
+ /** Explicit personal-connection grants for the initial accepted turn. */
13804
+ connectionAuthorities: McpConnectionAuthoritySelections.default([]),
13805
+ /** Atomic owner issuance for the selected personal Variable Set/Rig closure. */
13806
+ personalResourceAttachment: PersonalResourceAttachmentIntent.optional(),
13807
+ // Shared-sandbox placement (addendum 05 §D.1). Three-way union; OMITTED
13808
+ // today's behavior (a context-dependent default resolved server-side: from
13809
+ // inside a session → "shared" with the creator's box, top-level → "new"),
13810
+ // except a named targetSandboxId is always an own-box create ("new") because
13811
+ // a machine target is a different compute home. Explicit "shared"/{groupId}
13812
+ // plus a machine target is a 422.
13813
+ // - "shared": join the CREATOR's box. Requires a parent session (inferred
13814
+ // from the worker-signed sessionId claim, never caller-supplied);
13815
+ // top-level "shared" is a 422.
13816
+ // - "new": mint a fresh singleton box (group ≡ the new session's id).
13817
+ // - {groupId}: join a SPECIFIC sibling group in THIS workspace (manager
13818
+ // fan-out). Validated workspace-scoped (cross-workspace → 404).
13819
+ // A shared spawn inherits the box's (backend, os) — it is literally the same
13820
+ // box; the child cannot pick its own backend. Cross-workspace sharing is
13821
+ // forbidden by construction (the parent/group reads are RLS-workspace-scoped).
13822
+ // ENV-AWARE: the box's variable set is fixed at creation, so a share requires
13823
+ // the SAME variableSetId as the creator's box. On a mismatch the inherited
13824
+ // default silently falls back to an own box; an explicit "shared"/{groupId}
13825
+ // request 422s at create (instead of the first turn dying on the SDK's
13826
+ // manifest-env guard).
13827
+ sandbox: z
13828
+ .union([z.literal("shared"), z.literal("new"), z.object({ groupId: z.string().uuid() })])
13829
+ .optional(),
13830
+ },
13831
+ { rejectKeys: ["turnInstructions"] },
13832
+ ).superRefine((value, context) => {
11352
13833
  if (value.startMode !== "realtime" && value.initialMessage === undefined) {
11353
13834
  context.addIssue({
11354
13835
  code: z.ZodIssueCode.custom,
@@ -11363,9 +13844,46 @@ export const CreateSessionRequest = withVariableSetIdAlias({
11363
13844
  message: "initialMessage must be omitted when startMode is realtime",
11364
13845
  });
11365
13846
  }
13847
+ if (value.startMode === "realtime" && value.modelContext !== undefined) {
13848
+ context.addIssue({
13849
+ code: z.ZodIssueCode.custom,
13850
+ path: ["modelContext"],
13851
+ message: "modelContext requires an initialMessage; attach it to a realtime entry instead",
13852
+ });
13853
+ }
13854
+ if (value.startMode === "realtime" && value.connectionAuthorities.length > 0) {
13855
+ context.addIssue({
13856
+ code: z.ZodIssueCode.custom,
13857
+ path: ["connectionAuthorities"],
13858
+ message:
13859
+ "connectionAuthorities require an accepted initial turn and are not supported by realtime session staging",
13860
+ });
13861
+ }
13862
+ if (value.startMode === "realtime" && value.personalResourceAttachment !== undefined) {
13863
+ context.addIssue({
13864
+ code: z.ZodIssueCode.custom,
13865
+ path: ["personalResourceAttachment"],
13866
+ message:
13867
+ "personalResourceAttachment requires an accepted initial turn and is not supported by realtime session staging",
13868
+ });
13869
+ }
13870
+ if (value.personalResourceAttachment?.expectedAuthorityEpoch !== undefined) {
13871
+ context.addIssue({
13872
+ code: z.ZodIssueCode.custom,
13873
+ path: ["personalResourceAttachment", "expectedAuthorityEpoch"],
13874
+ message: "new-session attachment authority epoch is derived by the server",
13875
+ });
13876
+ }
11366
13877
  });
11367
13878
  export type CreateSessionRequest = z.infer<typeof CreateSessionRequest>;
11368
13879
 
13880
+ export const SessionTenancyCreateCapabilities = z.object({
13881
+ activated: z.boolean(),
13882
+ canCreatePrivate: z.boolean(),
13883
+ reason: z.enum(["available", "not_activated", "managed_session_required", "unavailable"]),
13884
+ });
13885
+ export type SessionTenancyCreateCapabilities = z.infer<typeof SessionTenancyCreateCapabilities>;
13886
+
11369
13887
  // Generic, host-neutral structured human input. One model tool call creates one
11370
13888
  // request containing one or more questions; the durable response resumes that
11371
13889
  // exact call. This is deliberately distinct from tool approval: an answer,
@@ -11389,6 +13907,9 @@ export const HumanInputQuestion = z
11389
13907
  helpText: z.string().max(2048).nullable().optional(),
11390
13908
  options: z.array(HumanInputOption).max(20).default([]),
11391
13909
  required: z.boolean().default(true),
13910
+ // Retained on the wire for older hosts. OpenGeni's stock runtime and
13911
+ // surfaces always expose Other for choice questions, including requests
13912
+ // that were persisted before that became the default behavior.
11392
13913
  allowOther: z.boolean().default(false),
11393
13914
  // Selection bounds only — agents invent useless text char mins/maxes.
11394
13915
  // Answer strings stay platform-capped on HumanInputAnswer (~8192).
@@ -11549,9 +14070,9 @@ export const SessionUserMessagePayload = z
11549
14070
  .object({
11550
14071
  text: z.string().default(""),
11551
14072
  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(),
14073
+ // Model-visible application context attached to this exact user message.
14074
+ // It is retained in full event/history data but omitted by standard UI.
14075
+ modelContext: z.string().trim().min(1).max(32768).optional(),
11555
14076
  resources: z.array(ResourceRef).default([]),
11556
14077
  model: z.string().min(1).optional(),
11557
14078
  reasoningEffort: ReasoningEffort.optional(),
@@ -11561,9 +14082,13 @@ export const SessionUserMessagePayload = z
11561
14082
  // Header-value rotation only. URL/name/tool settings are immutable after
11562
14083
  // session create; persisted events expose metadata, never header values.
11563
14084
  mcpCredentialUpdates: z.array(SessionMcpCredentialUpdateInput).optional(),
14085
+ /** Explicit personal-connection grants for this exact logical turn. */
14086
+ connectionAuthorities: McpConnectionAuthoritySelections.default([]),
14087
+ personalResourceAttachment: PersonalResourceAttachmentIntent.optional(),
11564
14088
  })
11565
14089
  .strict()
11566
- .superRefine(requireMessageTextOrAnnotations);
14090
+ .superRefine(requireMessageTextOrAnnotations)
14091
+ .superRefine(requireEstablishedPersonalResourceEpoch);
11567
14092
  export type SessionUserMessagePayload = z.infer<typeof SessionUserMessagePayload>;
11568
14093
 
11569
14094
  export const ClientSessionEvent = z.discriminatedUnion("type", [
@@ -11596,8 +14121,8 @@ export const SteerSessionMessageRequest = z
11596
14121
  .object({
11597
14122
  text: z.string().default(""),
11598
14123
  annotations: SubmittedTimelineAnnotations.default([]),
11599
- // Same per-turn system-level context as a queued user.message.
11600
- turnInstructions: z.string().trim().min(1).max(32768).optional(),
14124
+ // Same model-visible message context as a queued user.message.
14125
+ modelContext: z.string().trim().min(1).max(32768).optional(),
11601
14126
  resources: z.array(ResourceRef).default([]),
11602
14127
  model: z.string().min(1).optional(),
11603
14128
  reasoningEffort: ReasoningEffort.optional(),
@@ -11606,17 +14131,36 @@ export const SteerSessionMessageRequest = z
11606
14131
  controlEtag: z.string().min(1).optional(),
11607
14132
  expectedDraftRevision: z.number().int().nonnegative().optional(),
11608
14133
  mcpCredentialUpdates: z.array(SessionMcpCredentialUpdateInput).optional(),
14134
+ /** Explicit personal-connection grants for this exact steered turn. */
14135
+ connectionAuthorities: McpConnectionAuthoritySelections.default([]),
14136
+ personalResourceAttachment: PersonalResourceAttachmentIntent.optional(),
11609
14137
  })
11610
14138
  .strict()
11611
- .superRefine(requireMessageTextOrAnnotations);
14139
+ .superRefine(requireMessageTextOrAnnotations)
14140
+ .superRefine(requireEstablishedPersonalResourceEpoch);
11612
14141
  export type SteerSessionMessageRequest = z.infer<typeof SteerSessionMessageRequest>;
11613
14142
 
11614
14143
  export const SteerSessionMessageResponse = z.object({
11615
14144
  accepted: SessionEvent,
11616
14145
  turn: SessionTurn,
14146
+ receipt: SessionCommandReceipt,
14147
+ routing: SessionPromptRouting,
14148
+ interruptionCount: z.number().int().nonnegative(),
14149
+ replay: z.boolean(),
11617
14150
  });
11618
14151
  export type SteerSessionMessageResponse = z.infer<typeof SteerSessionMessageResponse>;
11619
14152
 
14153
+ export const SubmitComposerDraftResponse = z.object({
14154
+ accepted: SessionEvent,
14155
+ turn: SessionTurn,
14156
+ draft: ComposerDraft,
14157
+ receipt: SessionCommandReceipt,
14158
+ routing: SessionPromptRouting,
14159
+ interruptionCount: z.number().int().nonnegative(),
14160
+ replay: z.boolean(),
14161
+ });
14162
+ export type SubmitComposerDraftResponse = z.infer<typeof SubmitComposerDraftResponse>;
14163
+
11620
14164
  export const SessionBusMessage = z.object({
11621
14165
  workspaceId: z.string().uuid(),
11622
14166
  sessionId: z.string().uuid(),
@@ -11707,6 +14251,7 @@ export const ClientAuthConfig = z.discriminatedUnion("mode", [
11707
14251
  z.object({
11708
14252
  mode: z.literal("managedSession"),
11709
14253
  session: z.literal("cookie"),
14254
+ emailVerificationRequired: z.boolean().default(true),
11710
14255
  }),
11711
14256
  ]);
11712
14257
  export type ClientAuthConfig = z.infer<typeof ClientAuthConfig>;
@@ -11971,6 +14516,9 @@ export type DeviceEnrollmentStartResponse = z.infer<typeof DeviceEnrollmentStart
11971
14516
  export const DeviceEnrollmentApproveRequest = z.object({
11972
14517
  userCode: z.string().min(1).max(64),
11973
14518
  allowScreenControl: z.boolean().default(false),
14519
+ // Human-approved machines are private by default. Workspace/organization
14520
+ // publication is an explicit consent choice at this same loud boundary.
14521
+ scope: ResourceAuthorityScope.default("user"),
11974
14522
  });
11975
14523
  export type DeviceEnrollmentApproveRequest = z.infer<typeof DeviceEnrollmentApproveRequest>;
11976
14524
 
@@ -12043,6 +14591,8 @@ export type DeviceEnrollmentPollResponse = z.infer<typeof DeviceEnrollmentPollRe
12043
14591
  // GET /enrollments — a workspace's machines (the Machines dashboard surface).
12044
14592
  export const EnrollmentSummary = z.object({
12045
14593
  id: z.string().uuid(),
14594
+ scope: ResourceAuthorityScope,
14595
+ generation: z.number().int().positive(),
12046
14596
  pubkey: z.string(),
12047
14597
  exposure: z.literal("whole-machine"),
12048
14598
  hasDisplay: z.boolean(),
@@ -12239,9 +14789,137 @@ export const MachineState = z.enum([
12239
14789
  ]);
12240
14790
  export type MachineState = z.infer<typeof MachineState>;
12241
14791
 
12242
- export const MachineKind = z.enum(["modal", "selfhosted"]);
14792
+ export const MachineKind = z.enum(["modal", "selfhosted", "opensandbox"]);
12243
14793
  export type MachineKind = z.infer<typeof MachineKind>;
12244
14794
 
14795
+ /** Diagnostic projection of the single live Connected-Machine runner authority.
14796
+ * It contains no bearer or NATS subject material. `supersededCount` is derived
14797
+ * from the monotonic generation; duplicate-denial evidence records valid
14798
+ * competing processes that were prevented from receiving work. */
14799
+ export const MachineConnectionAuthority = z.object({
14800
+ state: z.enum(["not_applicable", "unclaimed", "active", "expired"]),
14801
+ generation: z.number().int().nonnegative(),
14802
+ supersededCount: z.number().int().nonnegative(),
14803
+ leaseExpiresAt: z.string().nullable(),
14804
+ duplicateRunnerDeniedCount: z.number().int().nonnegative(),
14805
+ duplicateRunnerDeniedAt: z.string().nullable(),
14806
+ });
14807
+ export type MachineConnectionAuthority = z.infer<typeof MachineConnectionAuthority>;
14808
+
14809
+ export const MachineRuntimeCapabilities = z.object({
14810
+ exec: z.boolean(),
14811
+ filesystem: z.boolean(),
14812
+ git: z.boolean(),
14813
+ pty: z.boolean(),
14814
+ desktop: z.boolean(),
14815
+ opStream: z.boolean(),
14816
+ browserBridge: z.boolean(),
14817
+ operationResourcePolicy: z.boolean(),
14818
+ operationCpuQuota: z.boolean(),
14819
+ });
14820
+ export type MachineRuntimeCapabilities = z.infer<typeof MachineRuntimeCapabilities>;
14821
+
14822
+ export const MachineUpdateStatus = z.enum([
14823
+ "requested",
14824
+ "accepted",
14825
+ "waiting_for_idle",
14826
+ "downloading",
14827
+ "verifying",
14828
+ "applying",
14829
+ "restarting",
14830
+ "succeeded",
14831
+ "failed",
14832
+ ]);
14833
+ export type MachineUpdateStatus = z.infer<typeof MachineUpdateStatus>;
14834
+
14835
+ export const MachineUpdateState = z.object({
14836
+ operationId: z.string().uuid(),
14837
+ status: MachineUpdateStatus,
14838
+ targetVersion: z.string(),
14839
+ expectedBinarySha256: z
14840
+ .string()
14841
+ .regex(/^[0-9a-f]{64}$/)
14842
+ .nullable(),
14843
+ errorCode: z.string().nullable(),
14844
+ retryable: z.boolean(),
14845
+ rolledBack: z.boolean(),
14846
+ requestedAt: z.string(),
14847
+ updatedAt: z.string(),
14848
+ completedAt: z.string().nullable(),
14849
+ });
14850
+ export type MachineUpdateState = z.infer<typeof MachineUpdateState>;
14851
+
14852
+ export const MachineRuntime = z.object({
14853
+ installedVersion: z.string().nullable(),
14854
+ binarySha256: z
14855
+ .string()
14856
+ .regex(/^[0-9a-f]{64}$/)
14857
+ .nullable(),
14858
+ updateChannel: z.enum(["stable", "beta"]).nullable(),
14859
+ desiredVersion: z.string().nullable(),
14860
+ versionState: z.enum(["unknown", "current", "outdated", "ahead", "updating", "update_failed"]),
14861
+ capabilities: MachineRuntimeCapabilities,
14862
+ update: MachineUpdateState.nullable(),
14863
+ });
14864
+ export type MachineRuntime = z.infer<typeof MachineRuntime>;
14865
+
14866
+ export const UpdateMachineAgentResponse = z.object({
14867
+ operationId: z.string().uuid(),
14868
+ accepted: z.boolean(),
14869
+ targetVersion: z.string(),
14870
+ });
14871
+ export type UpdateMachineAgentResponse = z.infer<typeof UpdateMachineAgentResponse>;
14872
+
14873
+ const OperationMemoryBytes = z.number().int().positive().max(Number.MAX_SAFE_INTEGER).nullable();
14874
+ const OperationCpuMillicores = z.number().int().positive().max(0xffff_ffff).nullable();
14875
+
14876
+ function operationPolicyShape(
14877
+ value: {
14878
+ memoryMaxBytes: number | null;
14879
+ memoryHighBytes: number | null;
14880
+ cpuMaxMillicores?: number | null | undefined;
14881
+ },
14882
+ ctx: z.RefinementCtx,
14883
+ ): void {
14884
+ if (
14885
+ value.memoryMaxBytes !== null &&
14886
+ value.memoryHighBytes !== null &&
14887
+ value.memoryHighBytes > value.memoryMaxBytes
14888
+ ) {
14889
+ ctx.addIssue({
14890
+ code: z.ZodIssueCode.custom,
14891
+ path: ["memoryHighBytes"],
14892
+ message: "memoryHighBytes cannot exceed memoryMaxBytes",
14893
+ });
14894
+ }
14895
+ }
14896
+
14897
+ /** Optional per-connection command resource policy. CPU is a period-independent
14898
+ * millicore ratio; runners choose a documented representable cgroup period. Null
14899
+ * values are unrestricted; revision is the optimistic concurrency fence. */
14900
+ export const MachineOperationPolicy = z
14901
+ .object({
14902
+ memoryMaxBytes: OperationMemoryBytes,
14903
+ memoryHighBytes: OperationMemoryBytes,
14904
+ cpuMaxMillicores: OperationCpuMillicores,
14905
+ revision: z.number().int().nonnegative(),
14906
+ updatedAt: z.string().nullable(),
14907
+ })
14908
+ .superRefine(operationPolicyShape);
14909
+ export type MachineOperationPolicy = z.infer<typeof MachineOperationPolicy>;
14910
+
14911
+ export const UpdateMachineOperationPolicyRequest = z
14912
+ .object({
14913
+ memoryMaxBytes: OperationMemoryBytes,
14914
+ memoryHighBytes: OperationMemoryBytes,
14915
+ cpuMaxMillicores: OperationCpuMillicores.optional(),
14916
+ expectedRevision: z.number().int().nonnegative(),
14917
+ })
14918
+ .superRefine(operationPolicyShape);
14919
+ export type UpdateMachineOperationPolicyRequest = z.infer<
14920
+ typeof UpdateMachineOperationPolicyRequest
14921
+ >;
14922
+
12245
14923
  /**
12246
14924
  * A machine as the Machines dashboard renders it. The workspace's enrolled
12247
14925
  * selfhosted machines PLUS the session's synthetic Modal group box
@@ -12253,6 +14931,8 @@ export type MachineKind = z.infer<typeof MachineKind>;
12253
14931
  export const MachineView = z.object({
12254
14932
  sandboxId: z.string(),
12255
14933
  enrollmentId: z.string().nullable(),
14934
+ scope: ResourceAuthorityScope.default("workspace"),
14935
+ generation: z.number().int().positive().default(1),
12256
14936
  name: z.string(),
12257
14937
  kind: MachineKind,
12258
14938
  state: MachineState,
@@ -12271,6 +14951,12 @@ export const MachineView = z.object({
12271
14951
  allowScreenControl: z.boolean(),
12272
14952
  sharedSessionCount: z.number().int(),
12273
14953
  lastSeenAt: z.string().nullable(),
14954
+ connectionAuthority: MachineConnectionAuthority,
14955
+ // Exact connected-agent build/capabilities and current update operation. Null
14956
+ // for managed/session sandboxes and pre-runtime-reporting synthetic rows.
14957
+ runtime: MachineRuntime.nullable().default(null),
14958
+ // Per-enrollment desired policy. Null for managed/session sandboxes.
14959
+ operationPolicy: MachineOperationPolicy.nullable().default(null),
12274
14960
  metrics: MetricSample.nullable(),
12275
14961
  });
12276
14962
  export type MachineView = z.infer<typeof MachineView>;
@@ -12347,8 +15033,9 @@ function defineModelContractSchema<Schema>(factory: () => Schema): Schema {
12347
15033
  return factory();
12348
15034
  }
12349
15035
 
12350
- export const ModelCapabilitySupportV1 =
12351
- /* @__PURE__ */ defineModelContractSchema(() => z.enum(["supported", "unsupported", "unknown"]));
15036
+ export const ModelCapabilitySupportV1 = /* @__PURE__ */ defineModelContractSchema(() =>
15037
+ z.enum(["supported", "unsupported", "unknown"]),
15038
+ );
12352
15039
  export type ModelCapabilitySupportV1 = z.infer<typeof ModelCapabilitySupportV1>;
12353
15040
 
12354
15041
  export const ModelCapabilityStateV1 = /* @__PURE__ */ defineModelContractSchema(() =>
@@ -12396,60 +15083,65 @@ export const ModelCapabilitiesV1 = /* @__PURE__ */ defineModelContractSchema(()
12396
15083
  );
12397
15084
  export type ModelCapabilitiesV1 = z.infer<typeof ModelCapabilitiesV1>;
12398
15085
 
12399
- export const ModelCredentialSourceV1 =
12400
- /* @__PURE__ */ defineModelContractSchema(() =>
12401
- z.union([
12402
- z
12403
- .object({
12404
- kind: z.literal("deployment"),
12405
- mechanism: z.enum(["api_key", "azure_ad_bearer"]),
12406
- })
12407
- .strict(),
12408
- z
12409
- .object({
12410
- kind: z.literal("connected_subscription"),
12411
- provider: z.enum(["codex", "xai"]),
12412
- })
12413
- .strict(),
12414
- z
12415
- .object({
12416
- kind: z.literal("workspace_connection"),
12417
- mechanism: z.literal("api_key"),
12418
- })
12419
- .strict(),
12420
- ]),
12421
- );
12422
- export type ModelCredentialSourceV1 = z.infer<typeof ModelCredentialSourceV1>;
12423
-
12424
- export const ModelBillingAttributionV1 =
12425
- /* @__PURE__ */ defineModelContractSchema(() =>
15086
+ export const ModelCredentialSourceV1 = /* @__PURE__ */ defineModelContractSchema(() =>
15087
+ z.union([
12426
15088
  z
12427
15089
  .object({
12428
- upstreamPayer: z.enum(["deployment", "workspace", "connected_subscription"]),
12429
- metering: z.enum(["opengeni_credits", "external"]),
15090
+ kind: z.literal("deployment"),
15091
+ mechanism: z.enum(["api_key", "azure_ad_bearer"]),
12430
15092
  })
12431
15093
  .strict(),
12432
- );
15094
+ z
15095
+ .object({
15096
+ kind: z.literal("connected_subscription"),
15097
+ provider: z.enum(["codex", "xai"]),
15098
+ })
15099
+ .strict(),
15100
+ z
15101
+ .object({
15102
+ kind: z.literal("workspace_connection"),
15103
+ mechanism: z.literal("api_key"),
15104
+ })
15105
+ .strict(),
15106
+ ]),
15107
+ );
15108
+ export type ModelCredentialSourceV1 = z.infer<typeof ModelCredentialSourceV1>;
15109
+
15110
+ const TurnExecutionCredentialSourceV1 = z.union([
15111
+ ModelCredentialSourceV1,
15112
+ z
15113
+ .object({
15114
+ kind: z.literal("deployment"),
15115
+ mechanism: z.literal("none"),
15116
+ })
15117
+ .strict(),
15118
+ ]);
15119
+
15120
+ export const ModelBillingAttributionV1 = /* @__PURE__ */ defineModelContractSchema(() =>
15121
+ z
15122
+ .object({
15123
+ upstreamPayer: z.enum(["deployment", "workspace", "connected_subscription"]),
15124
+ metering: z.enum(["opengeni_credits", "external"]),
15125
+ })
15126
+ .strict(),
15127
+ );
12433
15128
  export type ModelBillingAttributionV1 = z.infer<typeof ModelBillingAttributionV1>;
12434
15129
 
12435
15130
  export const TURN_EXECUTION_POLICY_METADATA_KEY = "turnExecutionPolicyV1" as const;
12436
15131
 
12437
- export const TurnExecutionModelSourceV1 =
12438
- /* @__PURE__ */ defineModelContractSchema(() =>
12439
- z.enum(["explicit", "session", "deployment", "continuation"]),
12440
- );
15132
+ export const TurnExecutionModelSourceV1 = /* @__PURE__ */ defineModelContractSchema(() =>
15133
+ z.enum(["explicit", "session", "deployment", "continuation"]),
15134
+ );
12441
15135
  export type TurnExecutionModelSourceV1 = z.infer<typeof TurnExecutionModelSourceV1>;
12442
15136
 
12443
- export const TurnExecutionReasoningSourceV1 =
12444
- /* @__PURE__ */ defineModelContractSchema(() =>
12445
- z.enum(["explicit", "session", "deployment", "continuation"]),
12446
- );
15137
+ export const TurnExecutionReasoningSourceV1 = /* @__PURE__ */ defineModelContractSchema(() =>
15138
+ z.enum(["explicit", "session", "deployment", "continuation"]),
15139
+ );
12447
15140
  export type TurnExecutionReasoningSourceV1 = z.infer<typeof TurnExecutionReasoningSourceV1>;
12448
15141
 
12449
- export const TurnExecutionLatencyModeSourceV1 =
12450
- /* @__PURE__ */ defineModelContractSchema(() =>
12451
- z.enum(["explicit", "session", "deployment", "continuation"]),
12452
- );
15142
+ export const TurnExecutionLatencyModeSourceV1 = /* @__PURE__ */ defineModelContractSchema(() =>
15143
+ z.enum(["explicit", "session", "deployment", "continuation"]),
15144
+ );
12453
15145
  export type TurnExecutionLatencyModeSourceV1 = z.infer<typeof TurnExecutionLatencyModeSourceV1>;
12454
15146
 
12455
15147
  /**
@@ -12477,7 +15169,7 @@ export const TurnExecutionPolicyV1 = /* @__PURE__ */ defineModelContractSchema((
12477
15169
  providerId: z.string().min(1),
12478
15170
  upstreamModelId: z.string().min(1),
12479
15171
  wireApi: z.enum(["responses", "chat"]),
12480
- credentialSource: ModelCredentialSourceV1,
15172
+ credentialSource: TurnExecutionCredentialSourceV1,
12481
15173
  billing: ModelBillingAttributionV1,
12482
15174
  definitionVersion: z.string().regex(/^sha256:[a-f0-9]{64}$/u),
12483
15175
  })
@@ -12651,60 +15343,59 @@ export const ClientModel = /* @__PURE__ */ defineModelContractSchema(() =>
12651
15343
  );
12652
15344
  export type ClientModel = z.infer<typeof ClientModel>;
12653
15345
 
12654
- export const ModelCredentialReadinessV1 =
12655
- /* @__PURE__ */ defineModelContractSchema(() =>
12656
- z
12657
- .object({
12658
- status: z.enum(["ready", "not_ready", "error"]),
12659
- reason: z
12660
- .enum([
12661
- "missing_credential",
12662
- "needs_reauth",
12663
- "prerequisites_missing",
12664
- "resolver_error",
12665
- "observation_stale",
12666
- ])
12667
- .nullable(),
12668
- basis: z.enum(["configuration", "connection", "resolver"]),
12669
- checkedAt: z.string().datetime().nullable(),
12670
- })
12671
- .strict()
12672
- .superRefine((readiness, context) => {
12673
- if ((readiness.status === "ready") !== (readiness.reason === null)) {
12674
- context.addIssue({
12675
- code: "custom",
12676
- path: ["reason"],
12677
- message: "ready credential state requires no reason; non-ready state requires a reason",
12678
- });
12679
- }
12680
- if ((readiness.status === "error") !== (readiness.reason === "resolver_error")) {
12681
- context.addIssue({
12682
- code: "custom",
12683
- path: ["reason"],
12684
- message:
12685
- "credential errors require resolver_error and resolver_error requires error status",
12686
- });
12687
- }
12688
- if (
12689
- readiness.basis === "resolver" &&
12690
- readiness.status === "ready" &&
12691
- readiness.checkedAt === null
12692
- ) {
12693
- context.addIssue({
12694
- code: "custom",
12695
- path: ["checkedAt"],
12696
- message: "resolver readiness requires an observation timestamp",
12697
- });
12698
- }
12699
- if (readiness.reason === "observation_stale" && readiness.checkedAt === null) {
12700
- context.addIssue({
12701
- code: "custom",
12702
- path: ["checkedAt"],
12703
- message: "a stale observation requires its observation timestamp",
12704
- });
12705
- }
12706
- }),
12707
- );
15346
+ export const ModelCredentialReadinessV1 = /* @__PURE__ */ defineModelContractSchema(() =>
15347
+ z
15348
+ .object({
15349
+ status: z.enum(["ready", "not_ready", "error"]),
15350
+ reason: z
15351
+ .enum([
15352
+ "missing_credential",
15353
+ "needs_reauth",
15354
+ "prerequisites_missing",
15355
+ "resolver_error",
15356
+ "observation_stale",
15357
+ ])
15358
+ .nullable(),
15359
+ basis: z.enum(["configuration", "connection", "resolver"]),
15360
+ checkedAt: z.string().datetime().nullable(),
15361
+ })
15362
+ .strict()
15363
+ .superRefine((readiness, context) => {
15364
+ if ((readiness.status === "ready") !== (readiness.reason === null)) {
15365
+ context.addIssue({
15366
+ code: "custom",
15367
+ path: ["reason"],
15368
+ message: "ready credential state requires no reason; non-ready state requires a reason",
15369
+ });
15370
+ }
15371
+ if ((readiness.status === "error") !== (readiness.reason === "resolver_error")) {
15372
+ context.addIssue({
15373
+ code: "custom",
15374
+ path: ["reason"],
15375
+ message:
15376
+ "credential errors require resolver_error and resolver_error requires error status",
15377
+ });
15378
+ }
15379
+ if (
15380
+ readiness.basis === "resolver" &&
15381
+ readiness.status === "ready" &&
15382
+ readiness.checkedAt === null
15383
+ ) {
15384
+ context.addIssue({
15385
+ code: "custom",
15386
+ path: ["checkedAt"],
15387
+ message: "resolver readiness requires an observation timestamp",
15388
+ });
15389
+ }
15390
+ if (readiness.reason === "observation_stale" && readiness.checkedAt === null) {
15391
+ context.addIssue({
15392
+ code: "custom",
15393
+ path: ["checkedAt"],
15394
+ message: "a stale observation requires its observation timestamp",
15395
+ });
15396
+ }
15397
+ }),
15398
+ );
12708
15399
  export type ModelCredentialReadinessV1 = z.infer<typeof ModelCredentialReadinessV1>;
12709
15400
 
12710
15401
  export const ModelAvailabilityV1 = /* @__PURE__ */ defineModelContractSchema(() =>
@@ -12727,21 +15418,21 @@ export const ModelAvailabilityV1 = /* @__PURE__ */ defineModelContractSchema(()
12727
15418
  );
12728
15419
  export type ModelAvailabilityV1 = z.infer<typeof ModelAvailabilityV1>;
12729
15420
 
12730
- export const WorkspaceModelCatalogModel =
12731
- /* @__PURE__ */ defineModelContractSchema(() =>
12732
- ClientModel.extend({
12733
- credentialReadiness: ModelCredentialReadinessV1,
12734
- availability: ModelAvailabilityV1,
12735
- }),
12736
- );
15421
+ export const WorkspaceModelCatalogModel = /* @__PURE__ */ defineModelContractSchema(() =>
15422
+ ClientModel.extend({
15423
+ credentialReadiness: ModelCredentialReadinessV1,
15424
+ /** Exact workspace-policy verdict without exposing provider identity. */
15425
+ policyAllowed: z.boolean().optional(),
15426
+ availability: ModelAvailabilityV1,
15427
+ }),
15428
+ );
12737
15429
  export type WorkspaceModelCatalogModel = z.infer<typeof WorkspaceModelCatalogModel>;
12738
15430
 
12739
- export const WorkspaceModelCatalogResponse =
12740
- /* @__PURE__ */ defineModelContractSchema(() =>
12741
- z.object({
12742
- models: z.array(WorkspaceModelCatalogModel),
12743
- }),
12744
- );
15431
+ export const WorkspaceModelCatalogResponse = /* @__PURE__ */ defineModelContractSchema(() =>
15432
+ z.object({
15433
+ models: z.array(WorkspaceModelCatalogModel),
15434
+ }),
15435
+ );
12745
15436
  export type WorkspaceModelCatalogResponse = z.infer<typeof WorkspaceModelCatalogResponse>;
12746
15437
 
12747
15438
  /**
@@ -12752,7 +15443,7 @@ export type WorkspaceModelCatalogResponse = z.infer<typeof WorkspaceModelCatalog
12752
15443
  * that rollout boundary. Mutating clients send this value in
12753
15444
  * `x-opengeni-api-contract`; the API rejects any other value before routing.
12754
15445
  */
12755
- export const OPENGENI_API_CONTRACT_REVISION = "2026-08-social-provider-tools-v1" as const;
15446
+ export const OPENGENI_API_CONTRACT_REVISION = "2026-08-atomic-session-fork-visibility-v1" as const;
12756
15447
  export const OPENGENI_API_CONTRACT_HEADER = "x-opengeni-api-contract" as const;
12757
15448
  /** Bounded request/response identifier shared by browser, ingress, and API diagnostics. */
12758
15449
  export const OPENGENI_CORRELATION_HEADER = "x-opengeni-correlation-id" as const;
@@ -12926,10 +15617,18 @@ export * from "./codex-fleet-policy";
12926
15617
  export * from "./xai-provider-account-authority";
12927
15618
  export * from "./workspace-instruction-policies";
12928
15619
  export * from "./company-profile";
15620
+ export * from "./company-brain";
12929
15621
  export * from "./workspace-learning-policy";
15622
+ export * from "./workspace-learning-administration";
12930
15623
  export * from "./workspace-state";
12931
15624
  export * from "./preference-registry";
12932
15625
  export * from "./scoped-knowledge";
15626
+ export * from "./company-brain-governed-writes";
15627
+ export * from "./governed-learning-evaluator";
15628
+ export * from "./governed-learning-activation";
12933
15629
  export * from "./knowledge";
12934
15630
  export * from "./task-notes";
12935
15631
  export * from "./canonical-human-identities";
15632
+ export * from "./organization-membership-lifecycle";
15633
+ export * from "./remember";
15634
+ export * from "./agent-authored-durable-text";