@opengeni/contracts 1.0.1 → 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 (112) 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-NX5JNF4L.js → chunk-7ZZ3R27P.js} +9322 -5798
  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 +1 -0
  46. package/dist/google-drive.js +14 -13
  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 +12224 -1328
  51. package/dist/index.js +831 -115
  52. package/dist/interaction.d.ts +68 -0
  53. package/dist/knowledge.d.ts +119 -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/governed-learning-activation.ts +191 -0
  86. package/src/governed-learning-evaluator.ts +176 -0
  87. package/src/index.ts +3033 -392
  88. package/src/interaction.ts +55 -1
  89. package/src/knowledge.ts +118 -6
  90. package/src/organization-membership-lifecycle.ts +330 -0
  91. package/src/permissions.ts +55 -0
  92. package/src/personal-github.ts +267 -0
  93. package/src/preference-registry.ts +17 -0
  94. package/src/remember.ts +242 -0
  95. package/src/sandbox-file-artifacts.ts +65 -0
  96. package/src/sandbox-snapshots.ts +78 -0
  97. package/src/slack-bot-scopes.ts +81 -9
  98. package/src/spreadsheet-artifact-commands.ts +12 -13
  99. package/src/task-notes.ts +14 -1
  100. package/src/tool-result-spill.ts +35 -0
  101. package/src/workspace-instruction-policies.ts +30 -0
  102. package/src/workspace-learning-administration.ts +88 -0
  103. package/dist/chunk-5UDNP7AO.js.map +0 -1
  104. package/dist/chunk-BVGIYW7D.js +0 -11
  105. package/dist/chunk-BVGIYW7D.js.map +0 -1
  106. package/dist/chunk-JBCIY7QD.js.map +0 -1
  107. package/dist/chunk-LSB7GRLG.js.map +0 -1
  108. package/dist/chunk-LW7OH6CS.js.map +0 -1
  109. package/dist/chunk-NX5JNF4L.js.map +0 -1
  110. package/dist/chunk-TZ6YEYN4.js.map +0 -1
  111. package/dist/chunk-Y3MLSLB4.js.map +0 -1
  112. /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,
@@ -11,11 +12,13 @@ import { WorkspaceInstructionPolicyRoleKeyInput } from "./workspace-instruction-
11
12
  import { ClientResumableVoiceInputConfig } from "./transcription-recordings";
12
13
  import { MediaGenerationResult } from "./video-generation";
13
14
  import { KnowledgeProviderCitation } from "./knowledge";
15
+ import { XaiProviderAccountAuthoritySnapshotV1 } from "./xai-provider-account-authority";
14
16
 
15
17
  export * from "./slack-bot-scopes";
16
18
  export * from "./slack-task-policy";
17
19
  export * from "./atlassian";
18
20
  export * from "./connector-destinations";
21
+ export * from "./connector-attachments";
19
22
  export * from "./memory-slack-delivery";
20
23
  export * from "./image-generation";
21
24
  export * from "./video-generation";
@@ -23,7 +26,10 @@ export * from "./editable-artifacts";
23
26
  export * from "./editable-artifact-committed-transaction";
24
27
  export * from "./editable-artifact-serialized-commit";
25
28
  export * from "./tool-catalog";
29
+ export * from "./tool-result-spill";
26
30
  export * from "./interaction";
31
+ export * from "./sandbox-file-artifacts";
32
+ export * from "./permissions";
27
33
 
28
34
  export {
29
35
  CreateWorkspaceArtifactRequest,
@@ -119,15 +125,21 @@ export {
119
125
  export {
120
126
  NATIVE_SNAPSHOT_PREFIXES,
121
127
  WORKSPACE_ARCHIVE_DESCRIPTOR_VERSION,
128
+ WORKSPACE_ARCHIVE_OBJECT_REF_SCHEMA,
122
129
  backendForNativeSnapshotProvider,
123
130
  decodeNativeSnapshotRef,
124
131
  encodeNativeSnapshotRef,
132
+ omitInlineWorkspaceArchiveWhenObjectRefPresent,
125
133
  parseWorkspaceArchiveDescriptor,
134
+ parseWorkspaceArchiveObjectRef,
135
+ workspaceArchiveObjectKey,
136
+ workspaceArchivePayloadPresent,
126
137
  type NativeSnapshotDescriptor,
127
138
  type NativeSnapshotProvider,
128
139
  type NativeSnapshotRef,
129
140
  type TarWorkspaceArchiveDescriptor,
130
141
  type WorkspaceArchiveDescriptor,
142
+ type WorkspaceArchiveObjectRef,
131
143
  type WorkspaceTreeFingerprint,
132
144
  } from "./sandbox-snapshots";
133
145
 
@@ -148,11 +160,12 @@ export const SessionStatus = z.enum([
148
160
  ]);
149
161
  export type SessionStatus = z.infer<typeof SessionStatus>;
150
162
 
151
- // 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
152
164
  // `packages/sdk/test/contract-parity.test.ts`. Every member is ADDITIVE AT THE
153
165
  // END (the parity test pins positions): the original four, then the six cloud
154
166
  // backends, then `selfhosted` (bring-your-own-compute — a user's own machine
155
- // enrolled as a first-class sandbox).
167
+ // enrolled as a first-class sandbox), then `opensandbox` (an optional
168
+ // Kubernetes-native provisioned sandbox provider).
156
169
  export const SandboxBackend = z.enum([
157
170
  "docker",
158
171
  "modal",
@@ -165,6 +178,7 @@ export const SandboxBackend = z.enum([
165
178
  "cloudflare",
166
179
  "vercel",
167
180
  "selfhosted",
181
+ "opensandbox",
168
182
  ]);
169
183
  export type SandboxBackend = z.infer<typeof SandboxBackend>;
170
184
 
@@ -190,6 +204,7 @@ export const SANDBOX_PROVIDER_INSTANCE_ID_FIELDS_BY_BACKEND = {
190
204
  cloudflare: ["sandboxId"],
191
205
  vercel: ["sandboxId"],
192
206
  selfhosted: ["agentId"],
207
+ opensandbox: ["sandboxId"],
193
208
  } as const satisfies Record<SandboxBackend, readonly string[]>;
194
209
  export const LEGACY_SANDBOX_PROVIDER_INSTANCE_ID_FIELDS = [
195
210
  "sandboxId",
@@ -594,6 +609,36 @@ export const CAPABILITY_DESCRIPTORS: Record<SandboxBackend, CapabilityDescriptor
594
609
  persistable: false,
595
610
  supportsRunAs: false,
596
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
+ },
597
642
  };
598
643
 
599
644
  export const ReasoningEffort = z.enum(["none", "minimal", "low", "medium", "high", "xhigh", "max"]);
@@ -673,91 +718,6 @@ export const SessionSpawnDenial = z.object({
673
718
  });
674
719
  export type SessionSpawnDenial = z.infer<typeof SessionSpawnDenial>;
675
720
 
676
- export const Permission = z.enum([
677
- "account:read",
678
- "account:admin",
679
- "members:manage",
680
- "workspace:create",
681
- "billing:read",
682
- "billing:manage",
683
- "workspace:read",
684
- "workspace:admin",
685
- "sessions:create",
686
- "sessions:read",
687
- "sessions:control",
688
- // sandbox workspace (sandbox contract §C.3 / crosscut PART 1.2). stream:view is a
689
- // REAL, distinct permission — strictly BROADER than sessions:read — because the
690
- // pixel plane (Channel B) exposes raw pixels: a viewer can see content the
691
- // structured Channel-A event log never captured. sessions:read is NOT
692
- // permission to watch raw pixels.
693
- "stream:view",
694
- // SEPARATE from stream:view: raw input to the desktop (bypasses approvalQueue /
695
- // interrupt). NEVER granted by default in v1 (the input plane is OFF —
696
- // streamControlEnabled=false); the permission exists so later hardening is a
697
- // flag flip, not a redesign.
698
- "stream:control",
699
- // Accept the pixel-plane secret-leak acknowledgment (consent gate before the
700
- // un-redacted desktop URL is handed out).
701
- "stream:acknowledge",
702
- "files:upload",
703
- "files:read",
704
- // Channel-A structured write surface (FS writes / apply-patch); distinct from
705
- // files:read so a read-only viewer can't mutate the box filesystem.
706
- "files:write",
707
- // Attach to an interactive PTY (terminal-as-pty, Channel A); distinct from
708
- // sessions:read which only reads the command-output firehose.
709
- "terminal:attach",
710
- "documents:manage",
711
- "documents:search",
712
- "scheduled_tasks:manage",
713
- "scheduled_tasks:run",
714
- "github:manage",
715
- "github:use",
716
- "api_keys:manage",
717
- "connections:read",
718
- "connections:write",
719
- /** @deprecated alias of variable-sets:manage */
720
- "environments:manage",
721
- /** @deprecated alias of variable-sets:use */
722
- "environments:use",
723
- "variable-sets:list",
724
- "variable-sets:read",
725
- "variable-sets:write",
726
- "variable-sets:manage",
727
- "variable-sets:use",
728
- "secrets:list",
729
- "secrets:read",
730
- "secrets:write",
731
- // Attach or rotate per-session third-party MCP server credentials. Deliberately
732
- // not part of the worker's default first-party MCP permission set: a sandboxed
733
- // agent must not be able to hand itself new bearer credentials.
734
- "mcp_servers:attach",
735
- // Programmatic sandbox -> tool access through the first-party MCP gate. This is
736
- // intentionally narrow and is never part of first-party MCP defaults; callers
737
- // must receive it through an explicit delegated `ogd_` mint carrying sessionId.
738
- "codemode:call",
739
- "goals:manage",
740
- // Bring-your-own-compute (M5). enrollments:read lists a workspace's machines;
741
- // enrollments:manage approves a device-flow enrollment (the LOUD whole-machine
742
- // consent) + revokes a machine. Distinct from sessions/stream perms because an
743
- // enrollment grants WHOLE-MACHINE access to a user's own hardware — a high-trust,
744
- // admin-shaped action. workspace:admin is the super-wildcard over both.
745
- "enrollments:read",
746
- "enrollments:manage",
747
- // Rigs (workspace-scoped, versioned sandbox machine definitions). rigs:use is
748
- // read + propose-change (the agent-native, additive path a sandboxed session
749
- // is trusted with); rigs:manage is create/edit/activate/promote/delete (the
750
- // admin-shaped path that mints or rolls versions). workspace:admin is the
751
- // super-wildcard over both.
752
- "rigs:use",
753
- "rigs:manage",
754
- // Workspace-published HTML artifacts. Read permits listing/source retrieval;
755
- // publish permits create, version publication, and rollback.
756
- "artifacts:read",
757
- "artifacts:publish",
758
- ]);
759
- export type Permission = z.infer<typeof Permission>;
760
-
761
721
  /**
762
722
  * Capability-first permissions signed into a session's first-party OpenGeni
763
723
  * MCP token when a top-level creator does not explicitly narrow them.
@@ -781,8 +741,12 @@ export const DEFAULT_FIRST_PARTY_MCP_PERMISSIONS = [
781
741
  // tools resolve an already-installed workspace principal. Credentials stay
782
742
  // inside the broker and remain subject to each tool's own authorization.
783
743
  "connections:read",
744
+ "variable-sets:list",
745
+ "variable-sets:write",
746
+ "variable-sets:attach",
784
747
  "variable-sets:use",
785
- "variable-sets:manage",
748
+ "secrets:list",
749
+ "secrets:write",
786
750
  "rigs:use",
787
751
  "github:use",
788
752
  "artifacts:read",
@@ -802,6 +766,7 @@ export const FIRST_PARTY_MCP_TOOL_NAMES = [
802
766
  "goal_set",
803
767
  "goal_update",
804
768
  "goal_progress",
769
+ "goal_wait",
805
770
  "goal_complete",
806
771
  "goal_pause",
807
772
  "memory_search",
@@ -812,6 +777,18 @@ export const FIRST_PARTY_MCP_TOOL_NAMES = [
812
777
  "task_notes_list",
813
778
  "task_note_save",
814
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",
815
792
  "sandboxes_list",
816
793
  "sandbox_attach",
817
794
  "sandbox_swap",
@@ -826,11 +803,13 @@ export const FIRST_PARTY_MCP_TOOL_NAMES = [
826
803
  "sessions_list",
827
804
  "session_get",
828
805
  "session_events",
806
+ "session_wait",
829
807
  "session_create",
830
808
  "session_send_message",
831
809
  "session_pause",
832
810
  "session_resume",
833
811
  "session_steer",
812
+ "session_human_input_respond",
834
813
  "set_other_session_title",
835
814
  "interaction_discover",
836
815
  "browser_open",
@@ -889,6 +868,7 @@ export const FIRST_PARTY_MCP_TOOL_NAMES = [
889
868
  "scheduled_tasks_delete",
890
869
  "scheduled_task_runs_list",
891
870
  "slack_bot_list_channels",
871
+ "slack_bot_search",
892
872
  "slack_bot_channel_history",
893
873
  "slack_bot_thread_replies",
894
874
  "slack_bot_list_users",
@@ -915,6 +895,7 @@ export const FIRST_PARTY_MCP_TOOL_NAMES = [
915
895
  "artifacts_create",
916
896
  "artifacts_publish",
917
897
  "artifacts_rollback",
898
+ "sandbox_file_publish",
918
899
  "editable_artifact_list",
919
900
  "editable_artifact_create",
920
901
  "editable_artifact_import",
@@ -965,8 +946,15 @@ const FIRST_PARTY_IN_PROCESS_TOOL_NAME_SET = new Set<FirstPartyMcpToolName>(
965
946
  * trustworthy logical-delivery identity. Server-owned Slack delivery paths
966
947
  * call the internal client directly with their own durable operation IDs.
967
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.
968
954
  const FIRST_PARTY_COMPATIBILITY_ONLY_TOOL_NAMES = [
969
955
  "slack_bot_post_message",
956
+ "memory_save",
957
+ "memory_correct",
970
958
  ] as const satisfies readonly FirstPartyMcpToolName[];
971
959
 
972
960
  const FIRST_PARTY_COMPATIBILITY_ONLY_TOOL_NAME_SET = new Set<FirstPartyMcpToolName>(
@@ -999,6 +987,10 @@ export const EDITABLE_ARTIFACT_MCP_CODEMODE_PATHS = {
999
987
  */
1000
988
  export const DEFAULT_FIRST_PARTY_MCP_TOOLS = FIRST_PARTY_MCP_TOOL_NAMES.filter(
1001
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" &&
1002
994
  !name.startsWith("social_") &&
1003
995
  !name.startsWith("x_") &&
1004
996
  !name.startsWith("reddit_") &&
@@ -1054,6 +1046,178 @@ export const PersonalResourceRetentionMode = z.enum(["retain", "delete_after"]);
1054
1046
  export type PersonalResourceRetentionMode = z.infer<typeof PersonalResourceRetentionMode>;
1055
1047
 
1056
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
+ >;
1057
1221
  export type SessionTenancyVisibility = z.infer<typeof SessionTenancyVisibility>;
1058
1222
 
1059
1223
  /** Public/session API vocabulary. Persistence keeps the explicit tenancy names. */
@@ -1275,6 +1439,100 @@ export const SessionTenancyPublicProjection = z.object({
1275
1439
  });
1276
1440
  export type SessionTenancyPublicProjection = z.infer<typeof SessionTenancyPublicProjection>;
1277
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
+
1278
1536
  export const ManagedAccount = z.object({
1279
1537
  id: z.string().uuid(),
1280
1538
  name: z.string(),
@@ -1622,6 +1880,37 @@ export const WorkspaceVoiceInputSettings = z
1622
1880
  .strict();
1623
1881
  export type WorkspaceVoiceInputSettings = z.infer<typeof WorkspaceVoiceInputSettings>;
1624
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
+
1625
1914
  /** Client-safe voice-input capability projection. Never includes provider secrets. */
1626
1915
  export const ClientVoiceInputConfig = z
1627
1916
  .object({
@@ -1715,17 +2004,79 @@ export const DEFAULT_WORKSPACE_SLACK_REACTION_SUMMON_SETTINGS = {
1715
2004
  channelPolicy: { mode: "bot_member" },
1716
2005
  } as const satisfies WorkspaceSlackReactionSummonSettings;
1717
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.
1718
2058
  export const WorkspaceMemoryPromptMode = z.enum(["legacy_standing", "retrieval_only"]);
1719
2059
  export type WorkspaceMemoryPromptMode = z.infer<typeof WorkspaceMemoryPromptMode>;
1720
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
+
1721
2070
  // Validates the KNOWN keys of workspaces.settings; passthrough keeps unknown
1722
2071
  // (future) keys rather than stripping them. memoryEnabled defaults off and the
1723
- // reversible Memory V1 containment rollout defaults to legacy standing context;
2072
+ // Memory V1 prompt mode is always retrieval-only composition;
1724
2073
  // voiceInput defaults to enabled when the deployment has a provider.
1725
2074
  export const WorkspaceSettingsSchema = z
1726
2075
  .object({
1727
2076
  memoryEnabled: z.boolean().optional(),
1728
2077
  memoryPromptMode: WorkspaceMemoryPromptMode.optional(),
2078
+ sessionDefaults: WorkspaceSessionDefaults.optional(),
2079
+ sessionToolDefaults: WorkspaceSessionToolDefaults.optional(),
1729
2080
  /** Preferred workspace voice-input toggle. */
1730
2081
  voiceInput: WorkspaceVoiceInputSettings.optional(),
1731
2082
  /**
@@ -1745,6 +2096,11 @@ export const WorkspaceSettingsSchema = z
1745
2096
  // Optional Slack reaction invocation. Absent/invalid fails closed to the
1746
2097
  // disabled default via resolveWorkspaceSlackReactionSummonSettings.
1747
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(),
1748
2104
  })
1749
2105
  .passthrough();
1750
2106
  export type WorkspaceSettings = z.infer<typeof WorkspaceSettingsSchema>;
@@ -1755,10 +2111,31 @@ export function resolveWorkspaceMemoryEnabled(settings: unknown): boolean {
1755
2111
  return parsed.success ? parsed.data.memoryEnabled === true : false;
1756
2112
  }
1757
2113
 
1758
- /** Reversible Memory V1 prompt rollout. Absent preserves legacy composition. */
1759
- 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 {
1760
2126
  const parsed = WorkspaceSettingsSchema.safeParse(settings ?? {});
1761
- 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";
1762
2139
  }
1763
2140
 
1764
2141
  /** Default Codex compaction mode for new Codex sessions (remote_v2 when unset). */
@@ -1815,6 +2192,30 @@ export function resolveWorkspaceSlackReactionSummonSettings(
1815
2192
  : { ...configured, channelPolicy: { mode: "bot_member" } };
1816
2193
  }
1817
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
+
1818
2219
  export function workspaceSlackReactionChannelAllowed(
1819
2220
  settings: WorkspaceSlackReactionSummonSettings,
1820
2221
  channelId: string,
@@ -1832,6 +2233,8 @@ export const UpdateWorkspaceSettingsRequest = z
1832
2233
  .object({
1833
2234
  memoryEnabled: z.boolean().optional(),
1834
2235
  memoryPromptMode: WorkspaceMemoryPromptMode.optional(),
2236
+ sessionDefaults: WorkspaceSessionDefaults.optional(),
2237
+ sessionToolDefaults: WorkspaceSessionToolDefaults.optional(),
1835
2238
  voiceInput: WorkspaceVoiceInputSettings.optional(),
1836
2239
  /** @deprecated Prefer `voiceInput`. Kept for one compatibility release. */
1837
2240
  transcription: WorkspaceTranscriptionPolicy.optional(),
@@ -1839,6 +2242,7 @@ export const UpdateWorkspaceSettingsRequest = z
1839
2242
  codexCompactionDefault: CodexCompactionMode.optional(),
1840
2243
  agentHumanInputEnabled: z.boolean().optional(),
1841
2244
  slackReactionSummon: WorkspaceSlackReactionSummonSettings.optional(),
2245
+ slackOrchestrationNotices: WorkspaceSlackOrchestrationNoticeSettings.optional(),
1842
2246
  })
1843
2247
  .passthrough();
1844
2248
  export type UpdateWorkspaceSettingsRequest = z.infer<typeof UpdateWorkspaceSettingsRequest>;
@@ -2349,6 +2753,17 @@ export const StreamTokenPayload = z.object({
2349
2753
  // Short TTL (120s default); rotation is event-driven under the epoch fence,
2350
2754
  // not on a keepalive clock.
2351
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(),
2352
2767
  });
2353
2768
  export type StreamTokenPayload = z.infer<typeof StreamTokenPayload>;
2354
2769
 
@@ -2510,6 +2925,7 @@ export const ApiKey = z.object({
2510
2925
  accountId: z.string().uuid(),
2511
2926
  workspaceId: z.string().uuid().nullable(),
2512
2927
  name: z.string(),
2928
+ description: z.string().nullable(),
2513
2929
  prefix: z.string(),
2514
2930
  permissions: z.array(Permission),
2515
2931
  expiresAt: z.string().nullable(),
@@ -2522,6 +2938,7 @@ export type ApiKey = z.infer<typeof ApiKey>;
2522
2938
 
2523
2939
  export const CreateApiKeyRequest = z.object({
2524
2940
  name: z.string().min(1),
2941
+ description: z.string().trim().min(1).max(500).optional(),
2525
2942
  workspaceId: z.string().uuid().optional(),
2526
2943
  permissions: z.array(Permission).min(1),
2527
2944
  expiresAt: z.string().datetime({ offset: true }).optional(),
@@ -2675,6 +3092,59 @@ export type InsightsBillingPath = z.infer<typeof InsightsBillingPath>;
2675
3092
  export const InsightsPricingSource = z.enum(["configured_list_price", "gateway_reported"]);
2676
3093
  export type InsightsPricingSource = z.infer<typeof InsightsPricingSource>;
2677
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
+
2678
3148
  export const InsightsModelUsageRow = z.object({
2679
3149
  id: z.string().min(1),
2680
3150
  model: z.string().min(1),
@@ -2841,6 +3311,13 @@ export const WorkspaceInsightsSnapshot = z.object({
2841
3311
  drivers: z.array(InsightsSpendDriver),
2842
3312
  schedules: z.array(InsightsScheduleRow),
2843
3313
  recentCalls: z.array(InsightsModelCallRow),
3314
+ promptContributions: InsightsPromptContributions.default({
3315
+ estimatedTokens: 0,
3316
+ utf8Bytes: 0,
3317
+ coveredCalls: 0,
3318
+ totalCalls: 0,
3319
+ sources: [],
3320
+ }),
2844
3321
  warmSeconds: z.number().nonnegative(),
2845
3322
  priorWarmSeconds: z.number().nonnegative(),
2846
3323
  warmGroups: z.array(InsightsWarmGroupRow),
@@ -2989,6 +3466,11 @@ export const GitCredentialRepositoryRef = z.object({
2989
3466
  access: GitRepositoryAccess.optional(),
2990
3467
  uri: z.string().min(1),
2991
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(),
2992
3474
  repositoryId: GitProviderRepositoryId.optional(),
2993
3475
  installationId: GitProviderRepositoryId.optional(),
2994
3476
  projectId: GitProviderRepositoryId.optional(),
@@ -3113,9 +3595,25 @@ export type GitCredentials = {
3113
3595
  export type SandboxSecretsRequest = {
3114
3596
  accountId: string;
3115
3597
  workspaceId: string;
3116
- // The variable set the run's session declares (null = unattached;
3117
- // 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;
3118
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;
3119
3617
  };
3120
3618
 
3121
3619
  export type SandboxSecrets = {
@@ -3123,8 +3621,19 @@ export type SandboxSecrets = {
3123
3621
  // `environmentsEncryptionKeyBytes` decrypt. Same shape the self-mint path
3124
3622
  // produces (plaintext name→value).
3125
3623
  values: Record<string, string>;
3126
- // 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;
3127
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;
3128
3637
  // Optional variableSet metadata; when omitted the activity uses the
3129
3638
  // variableSetId as both id and name (the local decrypt carries the row's
3130
3639
  // id/name/description, but only `id` is load-bearing downstream).
@@ -3288,12 +3797,127 @@ export const McpPersonalConnectionDelegation = z
3288
3797
  .object({
3289
3798
  serverId: z.string().min(1).max(256),
3290
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(),
3291
3807
  ownerSubjectId: z.string().min(1).max(512),
3292
3808
  providerDomain: z.string().min(1).max(2048),
3293
3809
  kind: z.enum(["oauth2", "api_key", "app_install", "delegated"]).optional(),
3294
- 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(),
3295
3864
  })
3296
- .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
+ });
3297
3921
  export type McpPersonalConnectionDelegation = z.infer<typeof McpPersonalConnectionDelegation>;
3298
3922
 
3299
3923
  /**
@@ -3318,12 +3942,44 @@ export const McpPersonalConnectionDelegations = z
3318
3942
  }
3319
3943
  });
3320
3944
 
3321
- export const McpPersonalConnectionSummary = McpPersonalConnectionDelegation.pick({
3322
- serverId: true,
3323
- providerDomain: true,
3324
- });
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();
3325
3951
  export type McpPersonalConnectionSummary = z.infer<typeof McpPersonalConnectionSummary>;
3326
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
+
3327
3983
  export type McpCredentialsRequest = {
3328
3984
  accountId: string;
3329
3985
  workspaceId: string;
@@ -3354,6 +4010,10 @@ export type McpCredentialsRequest = {
3354
4010
  toolName?: string;
3355
4011
  connectionRef: McpServerConnectionRef;
3356
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;
3357
4017
  };
3358
4018
 
3359
4019
  export type McpCredentialAuthNeededReason =
@@ -3552,13 +4212,35 @@ export const CreateCheckoutResponse = z.object({
3552
4212
  });
3553
4213
  export type CreateCheckoutResponse = z.infer<typeof CreateCheckoutResponse>;
3554
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
+
3555
4227
  export const RepositoryResourceRef = z.object({
3556
4228
  kind: z.literal("repository"),
3557
4229
  uri: z.string().min(1),
3558
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(),
3559
4240
  mountPath: z.string().min(1).optional(),
3560
4241
  subpath: z.string().min(1).optional(),
3561
4242
  provider: GitCredentialProvider.optional(),
4243
+ connectionType: z.literal("github_personal").optional(),
3562
4244
  credentialBindingId: GitCredentialBindingId.optional(),
3563
4245
  access: GitRepositoryAccess.optional(),
3564
4246
  repositoryId: GitProviderRepositoryId.optional(),
@@ -3718,6 +4400,8 @@ export type FileResourceRef = z.infer<typeof FileResourceRef>;
3718
4400
  * reconstruct the same typed attachment input after a model switch or retry.
3719
4401
  */
3720
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;
3721
4405
  /**
3722
4406
  * Structured timeline annotations retained beside the deterministic user-text
3723
4407
  * projection in canonical history. Provider adapters remove this OpenGeni
@@ -3817,6 +4501,25 @@ export function defaultRepositoryMountPath(
3817
4501
  );
3818
4502
  }
3819
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
+
3820
4523
  /** Resolve the exact mount used by API normalization, manifests, and clone hooks. */
3821
4524
  export const DEFAULT_FILE_RESOURCE_MOUNT_ROOT = ".opengeni/files" as const;
3822
4525
 
@@ -3985,6 +4688,9 @@ export const Document = z.object({
3985
4688
  authorityKind: DocumentAuthorityKind,
3986
4689
  authorityWorkspaceId: z.string().uuid().nullable(),
3987
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(),
3988
4694
  visibility: DocumentVisibility,
3989
4695
  createdBy: z.string().nullable(),
3990
4696
  agentAccess: z.boolean(),
@@ -4137,6 +4843,77 @@ export const MoveDocumentRequest = z.object({
4137
4843
  });
4138
4844
  export type MoveDocumentRequest = z.infer<typeof MoveDocumentRequest>;
4139
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
+
4140
4917
  export const DocumentSearchRequest = z.object({
4141
4918
  query: z.string().min(1),
4142
4919
  baseIds: z.array(z.string().uuid()).optional(),
@@ -4282,9 +5059,15 @@ export type WorkspaceMemorySearchResponse = z.infer<typeof WorkspaceMemorySearch
4282
5059
  export const ToolRef = z.object({
4283
5060
  kind: z.literal("mcp"),
4284
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(),
4285
5066
  // Non-fatal-on-connect marker for MCP server refs that can degrade
4286
5067
  // gracefully. On new input, absent/false is STRICT: the id must be configured
4287
- // 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
4288
5071
  // intersected with the current registry at each turn boundary, so a server
4289
5072
  // disconnected after admission is retained in policy/audit truth but skipped
4290
5073
  // until it is registered again. `optional:true` additionally makes runtime
@@ -4481,10 +5264,14 @@ export function mergeToolRefs(existing: ToolRef[], additions: ToolRef[]): ToolRe
4481
5264
  // strict occurrence upgrades the merged ref so an unavailable server fails
4482
5265
  // the turn. This preserves the fail-loud default when defaults, packs, and
4483
5266
  // per-turn tool selections are combined.
4484
- if (prior.optional === true && tool.optional !== true) {
4485
- const { optional: _dropped, ...strict } = prior;
4486
- byKey.set(key, strict);
4487
- }
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
+ });
4488
5275
  }
4489
5276
  return order.map((key) => byKey.get(key)!);
4490
5277
  }
@@ -4638,6 +5425,9 @@ export const SESSION_GOAL_TEXT_MAX_BYTES = 8 * 1024;
4638
5425
  export const SESSION_GOAL_SUCCESS_CRITERIA_MAX_BYTES = 8 * 1024;
4639
5426
  export const SESSION_GOAL_RATIONALE_MAX_BYTES = 2 * 1024;
4640
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;
4641
5431
 
4642
5432
  export function sessionGoalUtf8Bytes(value: string): number {
4643
5433
  return new TextEncoder().encode(value).byteLength;
@@ -4652,6 +5442,43 @@ function boundedSessionGoalString(maxBytes: number, field: string) {
4652
5442
  });
4653
5443
  }
4654
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
+
4655
5482
  const SessionGoalTextWrite = boundedSessionGoalString(SESSION_GOAL_TEXT_MAX_BYTES, "goal text");
4656
5483
  const SessionGoalSuccessCriteriaWrite = boundedSessionGoalString(
4657
5484
  SESSION_GOAL_SUCCESS_CRITERIA_MAX_BYTES,
@@ -4670,6 +5497,7 @@ export const SessionGoalSnapshot = z.discriminatedUnion("state", [
4670
5497
  objectiveRevision: z.number().int().positive(),
4671
5498
  text: SessionGoalTextWrite,
4672
5499
  successCriteria: SessionGoalSuccessCriteriaWrite.nullable(),
5500
+ rootConstraints: SessionGoalRootConstraintsWrite.default([]),
4673
5501
  mutationPolicy: SessionGoalMutationPolicy,
4674
5502
  capturedAt: z.string(),
4675
5503
  }),
@@ -4688,22 +5516,57 @@ export const SessionGoalRevision = z.object({
4688
5516
  resultObjectiveRevision: z.number().int().positive().nullable(),
4689
5517
  text: z.string().min(1),
4690
5518
  successCriteria: z.string().nullable(),
5519
+ rootConstraints: z.array(z.string().min(1)).default([]),
4691
5520
  mutationPolicy: SessionGoalMutationPolicy,
4692
5521
  rationale: z.string().min(1),
4693
5522
  actor: z.enum(["agent", "api", "scheduled_task"]),
4694
5523
  actorTurnId: z.string().uuid().nullable(),
4695
5524
  actorAttemptId: z.string().uuid().nullable(),
4696
5525
  proposalId: z.string().uuid().nullable(),
5526
+ rollbackOfRevisionId: z.string().uuid().nullable(),
4697
5527
  createdAt: z.string(),
4698
5528
  });
4699
5529
  export type SessionGoalRevision = z.infer<typeof SessionGoalRevision>;
4700
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
+
4701
5552
  export const ApplySessionGoalRevisionRequest = z.object({
4702
5553
  expectedObjectiveRevision: z.number().int().positive(),
4703
5554
  rationale: SessionGoalRationaleWrite.optional(),
4704
5555
  });
4705
5556
  export type ApplySessionGoalRevisionRequest = z.infer<typeof ApplySessionGoalRevisionRequest>;
4706
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
+
4707
5570
  export const SessionGoalPausedReason = z.enum([
4708
5571
  "agent",
4709
5572
  "user_pause",
@@ -4735,10 +5598,41 @@ export const SessionGoalContinuationReason = z.enum([
4735
5598
  "provider_backpressure",
4736
5599
  "session_cancelled",
4737
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",
4738
5605
  "missing_obligation",
4739
5606
  ]);
4740
5607
  export type SessionGoalContinuationReason = z.infer<typeof SessionGoalContinuationReason>;
4741
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
+
4742
5636
  export const SessionGoalContinuation = z.object({
4743
5637
  state: SessionGoalContinuationState,
4744
5638
  reason: SessionGoalContinuationReason,
@@ -4746,6 +5640,12 @@ export const SessionGoalContinuation = z.object({
4746
5640
  observedRevision: z.number().int().nonnegative(),
4747
5641
  nextAttemptAt: z.string().datetime({ offset: true }).nullable(),
4748
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(),
4749
5649
  });
4750
5650
  export type SessionGoalContinuation = z.infer<typeof SessionGoalContinuation>;
4751
5651
 
@@ -4757,6 +5657,7 @@ export const SessionGoal = z.object({
4757
5657
  status: SessionGoalStatus,
4758
5658
  text: z.string(),
4759
5659
  successCriteria: z.string().nullable(),
5660
+ rootConstraints: z.array(z.string().min(1)).default([]),
4760
5661
  evidence: z.string().nullable(),
4761
5662
  rationale: z.string().nullable(),
4762
5663
  pausedReason: z.string().nullable(),
@@ -4779,6 +5680,7 @@ export type SessionGoal = z.infer<typeof SessionGoal>;
4779
5680
  export const GoalSpec = z.object({
4780
5681
  text: SessionGoalTextWrite,
4781
5682
  successCriteria: SessionGoalSuccessCriteriaWrite.optional(),
5683
+ rootConstraints: SessionGoalRootConstraintsWrite.optional(),
4782
5684
  maxAutoContinuations: z.number().int().positive().optional(),
4783
5685
  mutationPolicy: SessionGoalMutationPolicy.optional(),
4784
5686
  });
@@ -4792,6 +5694,7 @@ export const UpdateSessionGoalRequest = z.union([
4792
5694
  z.object({
4793
5695
  text: SessionGoalTextWrite,
4794
5696
  successCriteria: SessionGoalSuccessCriteriaWrite.nullable().optional(),
5697
+ rootConstraints: SessionGoalRootConstraintsWrite.optional(),
4795
5698
  mutationPolicy: SessionGoalMutationPolicy.optional(),
4796
5699
  rationale: SessionGoalRationaleWrite,
4797
5700
  expectedObjectiveRevision: z.number().int().positive(),
@@ -4839,6 +5742,40 @@ export const UpdateSessionPinRequest = z.object({
4839
5742
  });
4840
5743
  export type UpdateSessionPinRequest = z.infer<typeof UpdateSessionPinRequest>;
4841
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
+
4842
5779
  // Operator context controls (slash-command palette: /clear, /compact). These
4843
5780
  // are session/operator actions, NOT a structured way to talk to the agent —
4844
5781
  // the human↔agent channel stays plain chat. Both require `sessions:control`.
@@ -4893,6 +5830,34 @@ export function isClearedRunStateBlob(serialized: string | null | undefined): bo
4893
5830
  }
4894
5831
  }
4895
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
+
4896
5861
  /** Trigger conversation compaction now. No body fields today (forward-room). */
4897
5862
  export const CompactSessionContextRequest = z.object({}).strict();
4898
5863
  export type CompactSessionContextRequest = z.infer<typeof CompactSessionContextRequest>;
@@ -5174,7 +6139,12 @@ export type WorkspaceRealtimeModelCatalogResponse = z.infer<
5174
6139
  export const SessionRealtimeState = z.enum(["active", "ended"]);
5175
6140
  export type SessionRealtimeState = z.infer<typeof SessionRealtimeState>;
5176
6141
 
5177
- 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
+ ]);
5178
6148
  export type SessionRealtimeEndReason = z.infer<typeof SessionRealtimeEndReason>;
5179
6149
 
5180
6150
  export const SessionRealtimeMode = z.object({
@@ -5242,6 +6212,9 @@ export const SessionAuthorizationOperation = z.enum([
5242
6212
  "session.secret.read",
5243
6213
  "session.codemode.call",
5244
6214
  "session.pin.write",
6215
+ "session.attention.write",
6216
+ "session.archive.write",
6217
+ "session.delete",
5245
6218
  "session.codex_account.write",
5246
6219
  "session.realtime.start",
5247
6220
  "session.realtime.control",
@@ -5258,6 +6231,7 @@ export const SessionAuthorizationOperation = z.enum([
5258
6231
  "session.child.create",
5259
6232
  "session.visibility.write",
5260
6233
  "session.fork.create",
6234
+ "session.personal_resource.grant",
5261
6235
  ]);
5262
6236
  export type SessionAuthorizationOperation = z.infer<typeof SessionAuthorizationOperation>;
5263
6237
 
@@ -5581,23 +6555,64 @@ export function renderTimelineAnnotationsForModel(
5581
6555
  }
5582
6556
 
5583
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
+ }
5584
6582
 
5585
6583
  /**
5586
6584
  * Build one canonical user-role message body. `modelContext` is ordinary
5587
- * message content: it is model-visible in the same chronological position as
5588
- * the visible text, but presentation layers may omit it. It is not a secret or
5589
- * an instruction-authority boundary.
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.
5590
6588
  */
5591
6589
  export function renderUserMessageContentForModel(
5592
6590
  text: string,
5593
6591
  annotations: readonly TimelineAnnotation[],
5594
6592
  modelContext?: string | null,
6593
+ goalSnapshot?: SessionGoalSnapshot,
5595
6594
  ): string | Array<{ type: "input_text"; text: string }> {
5596
6595
  const visibleContent = renderTimelineAnnotationsForModel(text, annotations);
5597
6596
  const context = modelContext?.trim();
5598
- if (!context) return visibleContent;
6597
+ const goalContext = renderSessionGoalContext(goalSnapshot);
6598
+ if (!context && !goalContext) return visibleContent;
5599
6599
  return [
5600
- { type: "input_text", text: `${MODEL_CONTEXT_LABEL}\n${context}` },
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
+ : []),
5601
6616
  { type: "input_text", text: visibleContent },
5602
6617
  ];
5603
6618
  }
@@ -5622,7 +6637,7 @@ export const SessionTurn = z
5622
6637
  toolsProvided: z.boolean().optional(),
5623
6638
  model: z.string().min(1),
5624
6639
  reasoningEffort: ReasoningEffort,
5625
- latencyMode: LatencyMode.default("standard"),
6640
+ latencyMode: LatencyMode,
5626
6641
  sandboxBackend: SandboxBackend,
5627
6642
  // Per-turn OS override. NULL = inherit the session's sandboxOs.
5628
6643
  sandboxOs: SandboxOs.nullable(),
@@ -5635,6 +6650,8 @@ export const SessionTurn = z
5635
6650
  initiatorContext: TurnInitiatorContext,
5636
6651
  /** Secret-safe projection of the exact personal authority frozen on this turn. */
5637
6652
  personalConnections: z.array(McpPersonalConnectionSummary).default([]),
6653
+ /** Safe summary only; opaque resource/grant identity remains private. */
6654
+ personalResources: PersonalResourceAttachmentSummary.nullable().default(null),
5638
6655
  cancelledBy: z.string().nullable(),
5639
6656
  cancelReason: z.string().nullable(),
5640
6657
  startedAt: z.string().nullable(),
@@ -5696,10 +6713,18 @@ export const EffectiveSessionControl = z.object({
5696
6713
  quiescencePendingCount: z.number().int().nonnegative(),
5697
6714
  })
5698
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(),
5699
6725
  });
5700
6726
  export type EffectiveSessionControl = z.infer<typeof EffectiveSessionControl>;
5701
6727
 
5702
- export const SESSION_OPERATION_KEY_MAX_CHARS = 256;
5703
6728
  const SessionOperationKey = z.string().min(1).max(SESSION_OPERATION_KEY_MAX_CHARS);
5704
6729
 
5705
6730
  export const SessionCommandReceipt = z.object({
@@ -5716,6 +6741,14 @@ export const SessionCommandReceipt = z.object({
5716
6741
  });
5717
6742
  export type SessionCommandReceipt = z.infer<typeof SessionCommandReceipt>;
5718
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
+
5719
6752
  export const ComposerDraft = z.object({
5720
6753
  revision: z.number().int().nonnegative(),
5721
6754
  text: z.string(),
@@ -5723,7 +6756,7 @@ export const ComposerDraft = z.object({
5723
6756
  resources: z.array(ResourceRef),
5724
6757
  model: z.string().min(1),
5725
6758
  reasoningEffort: ReasoningEffort,
5726
- latencyMode: LatencyMode.default("standard"),
6759
+ latencyMode: LatencyMode,
5727
6760
  sourceTurnId: z.string().uuid().nullable(),
5728
6761
  sourceTurnVersion: z.number().int().positive().nullable(),
5729
6762
  updatedAt: z.string().nullable(),
@@ -5769,12 +6802,39 @@ export const SaveComposerDraftRequest = ComposerDraft.pick({
5769
6802
  }).extend({ expectedRevision: z.number().int().nonnegative() });
5770
6803
  export type SaveComposerDraftRequest = z.infer<typeof SaveComposerDraftRequest>;
5771
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
+
5772
6831
  /**
5773
6832
  * Create-only options saved with an actor's private pre-session draft. This is
5774
6833
  * deliberately narrower than CreateSessionRequest: idempotency/event keys and
5775
6834
  * credential-bearing MCP server inputs are per-attempt data, never draft state.
5776
6835
  */
5777
6836
  export const NewSessionDraftOptions = z.object({
6837
+ visibility: SessionVisibility.optional(),
5778
6838
  sandboxBackend: SandboxBackend.optional(),
5779
6839
  targetSandboxId: z.string().uuid().optional(),
5780
6840
  workingDir: z.string().min(1).optional(),
@@ -5786,6 +6846,31 @@ export const NewSessionDraftOptions = z.object({
5786
6846
  });
5787
6847
  export type NewSessionDraftOptions = z.infer<typeof NewSessionDraftOptions>;
5788
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
+
5789
6874
  /** Actor-private, server-authoritative composer state before a session exists. */
5790
6875
  export const NewSessionDraft = z.object({
5791
6876
  revision: z.number().int().nonnegative(),
@@ -5796,8 +6881,9 @@ export const NewSessionDraft = z.object({
5796
6881
  toolsProvided: z.boolean().default(false),
5797
6882
  model: z.string().min(1),
5798
6883
  reasoningEffort: ReasoningEffort,
5799
- latencyMode: LatencyMode.default("standard"),
6884
+ latencyMode: LatencyMode,
5800
6885
  options: NewSessionDraftOptions,
6886
+ selectionHistory: NewSessionSelectionHistory.default({ projects: [] }),
5801
6887
  updatedAt: z.string().nullable(),
5802
6888
  });
5803
6889
  export type NewSessionDraft = z.infer<typeof NewSessionDraft>;
@@ -6036,53 +7122,229 @@ export const SessionSystemUpdateKind = z.enum([
6036
7122
  "agent_steer_instruction",
6037
7123
  "child_terminal_result",
6038
7124
  "media_generation_result",
7125
+ "child_requires_action",
7126
+ "child_requires_action_resolved",
7127
+ "child_paused",
7128
+ "child_waiting_capacity",
7129
+ "child_progress",
6039
7130
  ]);
6040
7131
  export type SessionSystemUpdateKind = z.infer<typeof SessionSystemUpdateKind>;
6041
7132
 
6042
- export const SessionSystemUpdatePayload = z.discriminatedUnion("type", [
6043
- z
6044
- .object({
6045
- type: z.literal("scheduled_occurrence"),
6046
- text: z.string().min(1),
6047
- scheduledTaskId: z.string().uuid(),
6048
- scheduledTaskRunId: z.string().uuid(),
6049
- resources: z.array(ResourceRef).optional(),
6050
- tools: z.array(ToolRef).optional(),
6051
- })
6052
- .passthrough(),
6053
- z
6054
- .object({
6055
- type: z.literal("goal_continuation"),
6056
- goalId: z.string().uuid(),
6057
- goalVersion: z.number().int().positive(),
6058
- prompt: z.string().min(1),
6059
- reason: z.string().optional(),
6060
- })
6061
- .passthrough(),
6062
- z
6063
- .object({
6064
- type: z.literal("agent_message"),
6065
- text: z.string().min(1),
6066
- operationId: z.string().uuid(),
6067
- })
6068
- .passthrough(),
6069
- z
6070
- .object({
6071
- type: z.literal("agent_steer_instruction"),
6072
- instruction: z.string().min(1),
6073
- operationId: z.string().uuid(),
6074
- })
6075
- .passthrough(),
6076
- z
6077
- .object({
6078
- type: z.literal("child_terminal_result"),
6079
- childSessionId: z.string().uuid(),
6080
- status: z.enum(["idle", "failed", "cancelled"]),
6081
- })
6082
- .passthrough(),
6083
- MediaGenerationResult,
6084
- ]);
6085
- export type SessionSystemUpdatePayload = z.infer<typeof SessionSystemUpdatePayload>;
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),
7330
+ operationId: z.string().uuid(),
7331
+ })
7332
+ .passthrough(),
7333
+ z
7334
+ .object({
7335
+ type: z.literal("child_terminal_result"),
7336
+ childSessionId: z.string().uuid(),
7337
+ status: z.enum(["idle", "failed", "cancelled"]),
7338
+ })
7339
+ .passthrough(),
7340
+ MediaGenerationResult,
7341
+ ChildRequiresActionPayload,
7342
+ ChildRequiresActionResolvedPayload,
7343
+ ChildPausedPayload,
7344
+ ChildWaitingCapacityPayload,
7345
+ ChildProgressPayload,
7346
+ ]);
7347
+ export type SessionSystemUpdatePayload = z.infer<typeof SessionSystemUpdatePayload>;
6086
7348
 
6087
7349
  export const SessionSystemUpdateState = z.enum([
6088
7350
  "pending",
@@ -6197,11 +7459,16 @@ export function renderSessionSystemUpdateBatch(
6197
7459
 
6198
7460
  export function sessionSystemUpdateBatchHistoryItem(
6199
7461
  updates: Parameters<typeof renderSessionSystemUpdateBatch>[0],
7462
+ goalSnapshot?: SessionGoalSnapshot,
6200
7463
  ): { type: "message"; role: "system"; content: string } {
7464
+ const goalContext = renderSessionGoalContext(goalSnapshot);
6201
7465
  return {
6202
7466
  type: "message",
6203
7467
  role: "system",
6204
- content: renderSessionSystemUpdateBatch(updates),
7468
+ content: [
7469
+ ...(goalContext ? [`${SESSION_GOAL_CONTEXT_LABEL}\n${goalContext}`] : []),
7470
+ renderSessionSystemUpdateBatch(updates),
7471
+ ].join("\n\n"),
6205
7472
  };
6206
7473
  }
6207
7474
 
@@ -6251,10 +7518,16 @@ export const VariableSetSecret = z.object({
6251
7518
  });
6252
7519
  export type VariableSetSecret = z.infer<typeof VariableSetSecret>;
6253
7520
 
7521
+ export const VariableSetScope = z.enum(["organization", "workspace", "user"]);
7522
+ export type VariableSetScope = z.infer<typeof VariableSetScope>;
7523
+
6254
7524
  export const VariableSet = z.object({
6255
7525
  id: z.string().uuid(),
6256
7526
  accountId: z.string().uuid(),
6257
7527
  workspaceId: z.string().uuid(),
7528
+ scope: VariableSetScope,
7529
+ generation: z.number().int().positive(),
7530
+ status: z.enum(["active", "revoked"]),
6258
7531
  name: z.string(),
6259
7532
  description: z.string().nullable(),
6260
7533
  variables: z.array(VariableSetVariableMetadata),
@@ -6268,6 +7541,8 @@ export const WorkspaceEnvironment = VariableSet;
6268
7541
  export type WorkspaceEnvironment = VariableSet;
6269
7542
 
6270
7543
  export const CreateVariableSetRequest = z.object({
7544
+ // Omitted remains the legacy workspace-owned creation path.
7545
+ scope: VariableSetScope.default("workspace"),
6271
7546
  name: z.string().min(1).max(120),
6272
7547
  description: z.string().max(2000).optional(),
6273
7548
  variables: z
@@ -6466,6 +7741,9 @@ export const Rig = z.object({
6466
7741
  id: z.string().uuid(),
6467
7742
  accountId: z.string().uuid(),
6468
7743
  workspaceId: z.string().uuid(),
7744
+ scope: ResourceAuthorityScope,
7745
+ generation: z.number().int().positive(),
7746
+ status: z.enum(["active", "revoked"]),
6469
7747
  name: z.string(),
6470
7748
  description: z.string().nullable(),
6471
7749
  createdBy: z.string().nullable(),
@@ -6524,6 +7802,8 @@ export const RigChange = z.object({
6524
7802
  export type RigChange = z.infer<typeof RigChange>;
6525
7803
 
6526
7804
  export const CreateRigRequest = z.object({
7805
+ // Omitted remains the compatibility workspace-owned creation path.
7806
+ scope: ResourceAuthorityScope.default("workspace"),
6527
7807
  name: z.string().min(1).max(120),
6528
7808
  description: z.string().max(2000).optional(),
6529
7809
  // Initial (version 1) content, inline.
@@ -6550,6 +7830,8 @@ export const Channel = z.object({
6550
7830
  workspaceId: z.string().uuid(),
6551
7831
  name: z.string(),
6552
7832
  description: z.string().nullable(),
7833
+ pinned: z.boolean(),
7834
+ sortOrder: z.number().int().nonnegative(),
6553
7835
  createdBy: z.string().nullable(),
6554
7836
  createdAt: z.string(),
6555
7837
  updatedAt: z.string(),
@@ -6565,9 +7847,16 @@ export type CreateChannelRequest = z.infer<typeof CreateChannelRequest>;
6565
7847
  export const UpdateChannelRequest = z.object({
6566
7848
  name: z.string().trim().min(1).max(80).optional(),
6567
7849
  description: z.string().max(2000).nullable().optional(),
7850
+ pinned: z.boolean().optional(),
6568
7851
  });
6569
7852
  export type UpdateChannelRequest = z.infer<typeof UpdateChannelRequest>;
6570
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
+
6571
7860
  // Re-files one session (rail organization only). null moves it back to the
6572
7861
  // unfiled inbox.
6573
7862
  export const UpdateSessionChannelRequest = z.object({
@@ -6733,25 +8022,313 @@ export const ScheduledTaskScheduleSpec = /* @__PURE__ */ z.discriminatedUnion("t
6733
8022
  ]);
6734
8023
  export type ScheduledTaskScheduleSpec = z.infer<typeof ScheduledTaskScheduleSpec>;
6735
8024
 
6736
- export const ScheduledTaskAgentConfig = /* @__PURE__ */ z.object({
6737
- prompt: z.string().min(1),
6738
- resources: z.array(ResourceRef).default([]),
6739
- tools: z.array(ToolRef).default([]),
6740
- metadata: z.record(z.string(), z.unknown()).default({}),
6741
- // Explicit workspace-shared OpenGeni Slack bot binding for scheduled runs.
6742
- // The worker copies this non-secret pointer into session metadata; the
6743
- // first-party Slack tools never fall back to a personal hosted-MCP grant.
6744
- slackBotConnectionId: z.string().uuid().optional(),
6745
- model: z.string().min(1).optional(),
6746
- reasoningEffort: ReasoningEffort.optional(),
6747
- sandboxBackend: SandboxBackend.optional(),
6748
- goal: GoalSpec.optional(),
6749
- // Durable task override. Scheduled dispatch is trusted to preserve this
6750
- // snapshot even if the workspace/deployment policy narrows later.
6751
- maxNestedAgentDepth: NestedAgentDepthValue.optional(),
6752
- });
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);
6753
8306
  export type ScheduledTaskAgentConfig = z.infer<typeof ScheduledTaskAgentConfig>;
6754
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
+
6755
8332
  export const ScheduledTask = /* @__PURE__ */ z.object({
6756
8333
  id: z.string().uuid(),
6757
8334
  accountId: z.string().uuid(),
@@ -6770,6 +8347,8 @@ export const ScheduledTask = /* @__PURE__ */ z.object({
6770
8347
  }),
6771
8348
  createdByContext: TurnInitiatorContext.default({}),
6772
8349
  personalConnections: z.array(McpPersonalConnectionSummary).default([]),
8350
+ authorityRevision: z.number().int().positive().default(1),
8351
+ executionDigest: z.string().regex(/^[0-9a-f]{64}$/u),
6773
8352
  reusableSessionId: z.string().uuid().nullable(),
6774
8353
  targetSessionId: z.string().uuid().nullable().default(null),
6775
8354
  variableSetId: z.string().uuid().nullable().default(null),
@@ -6785,18 +8364,164 @@ export const ScheduledTask = /* @__PURE__ */ z.object({
6785
8364
  });
6786
8365
  export type ScheduledTask = z.infer<typeof ScheduledTask>;
6787
8366
 
6788
- export const KnowledgeSourceSyncFailure = /* @__PURE__ */ z.object({
6789
- externalObjectId: KnowledgeSourceSyncSubject,
6790
- code: z.enum([
6791
- "authority_changed",
6792
- "connection_reconnect_required",
6793
- "provider_unavailable",
6794
- "provider_rejected",
6795
- "provider_payload_invalid",
6796
- "content_unsupported",
6797
- "content_too_large",
6798
- "resource_limit",
6799
- "item_processing_failed",
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",
6800
8525
  "indexing_failed",
6801
8526
  "internal_failure",
6802
8527
  ]),
@@ -6835,6 +8560,12 @@ export const ScheduledTaskRun = /* @__PURE__ */ z.object({
6835
8560
  accountId: z.string().uuid(),
6836
8561
  workspaceId: z.string().uuid(),
6837
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),
6838
8569
  status: ScheduledTaskRunStatus,
6839
8570
  triggerType: ScheduledTaskTriggerType,
6840
8571
  scheduledAt: z.string().nullable(),
@@ -6852,7 +8583,7 @@ export const ScheduledTaskRun = /* @__PURE__ */ z.object({
6852
8583
  export type ScheduledTaskRun = z.infer<typeof ScheduledTaskRun>;
6853
8584
 
6854
8585
  const CreateAgentScheduledTaskRequest = /* @__PURE__ */ withVariableSetIdAlias({
6855
- name: z.string().min(1),
8586
+ name: ScheduledTaskNameInput,
6856
8587
  schedule: ScheduledTaskScheduleSpec,
6857
8588
  action: z
6858
8589
  .object({ kind: z.literal("agent_turn") })
@@ -6861,13 +8592,14 @@ const CreateAgentScheduledTaskRequest = /* @__PURE__ */ withVariableSetIdAlias({
6861
8592
  runMode: ScheduledTaskRunMode.default("new_session_per_run"),
6862
8593
  overlapPolicy: ScheduledTaskOverlapPolicy.default("allow_concurrent"),
6863
8594
  targetSessionId: z.string().uuid().nullable().optional(),
6864
- agentConfig: ScheduledTaskAgentConfig,
8595
+ connectionAuthorities: McpConnectionAuthoritySelections.default([]),
8596
+ agentConfig: ScheduledTaskAgentConfigInput,
6865
8597
  status: ScheduledTaskStatus.default("active"),
6866
8598
  variableSetId: z.string().uuid().nullable().optional(),
6867
8599
  environmentId: z.string().uuid().nullable().optional(),
6868
8600
  // The rig each run binds to (M3); its active version is resolved per fire.
6869
8601
  rigId: z.string().uuid().nullable().optional(),
6870
- metadata: z.record(z.string(), z.unknown()).default({}),
8602
+ metadata: ScheduledTaskMetadataInput.default({}),
6871
8603
  }).superRefine((value, context) => {
6872
8604
  if (value.runMode === "existing_session" && !value.targetSessionId) {
6873
8605
  context.addIssue({
@@ -6915,6 +8647,7 @@ const CreateKnowledgeSourceSyncScheduledTaskRequest = /* @__PURE__ */ z
6915
8647
  variableSetId: null,
6916
8648
  environmentId: null,
6917
8649
  rigId: null,
8650
+ connectionAuthorities: [],
6918
8651
  }));
6919
8652
 
6920
8653
  export const CreateScheduledTaskRequest = /* @__PURE__ */ z.union([
@@ -6923,59 +8656,393 @@ export const CreateScheduledTaskRequest = /* @__PURE__ */ z.union([
6923
8656
  ]);
6924
8657
  export type CreateScheduledTaskRequest = z.infer<typeof CreateScheduledTaskRequest>;
6925
8658
 
6926
- export const UpdateScheduledTaskRequest =
6927
- /* @__PURE__ */ withVariableSetIdAlias({
6928
- name: z.string().min(1).optional(),
6929
- schedule: ScheduledTaskScheduleSpec.optional(),
6930
- runMode: ScheduledTaskRunMode.optional(),
6931
- overlapPolicy: ScheduledTaskOverlapPolicy.optional(),
6932
- action: ScheduledTaskAction.optional(),
6933
- targetSessionId: z.string().uuid().nullable().optional(),
6934
- agentConfig: ScheduledTaskAgentConfig.optional(),
6935
- status: ScheduledTaskStatus.optional(),
6936
- variableSetId: z.string().uuid().nullable().optional(),
6937
- environmentId: z.string().uuid().nullable().optional(),
6938
- // The rig each run binds to (M3); null clears it. Its active version is
6939
- // resolved per fire, so an update takes effect on the next dispatch.
6940
- rigId: z.string().uuid().nullable().optional(),
6941
- metadata: z.record(z.string(), z.unknown()).optional(),
6942
- }).superRefine((value, context) => {
6943
- 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")) {
6944
8806
  context.addIssue({
6945
8807
  code: "custom",
6946
- path: ["targetSessionId"],
6947
- message: "targetSessionId requires runMode=existing_session",
8808
+ path: ["files"],
8809
+ message: "automation skill must include a top-level SKILL.md file",
6948
8810
  });
6949
8811
  }
6950
- 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) {
6951
8848
  context.addIssue({
6952
8849
  code: "custom",
6953
- path: ["targetSessionId"],
6954
- message: "targetSessionId cannot be null when runMode=existing_session",
8850
+ message: `automation session template exceeds ${AUTOMATION_SESSION_TEMPLATE_MAX_BYTES} UTF-8 bytes`,
6955
8851
  });
6956
8852
  }
6957
- if (
6958
- value.agentConfig?.goal &&
6959
- (value.runMode === "existing_session" || Boolean(value.targetSessionId))
6960
- ) {
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)) {
6961
8948
  context.addIssue({
6962
8949
  code: "custom",
6963
- path: ["agentConfig", "goal"],
6964
- message: "agentConfig.goal cannot be used with an existing-session target",
8950
+ path: ["packTemplateId"],
8951
+ message: "packInstallationId and packTemplateId must be supplied together",
6965
8952
  });
6966
8953
  }
6967
8954
  });
6968
- export type UpdateScheduledTaskRequest = z.infer<typeof UpdateScheduledTaskRequest>;
8955
+ export type CreateAutomationTriggerRequest = z.infer<typeof CreateAutomationTriggerRequest>;
6969
8956
 
6970
- /**
6971
- * Manual-trigger body. `triggerId` is a client-supplied idempotency token: a
6972
- * retried trigger that reuses the same token charges once and starts one run.
6973
- * Omitting it makes each call a distinct trigger (the server mints a token).
6974
- */
6975
- export const TriggerScheduledTaskRequest = z.object({
6976
- 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(),
6977
8991
  });
6978
- 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>;
6979
9046
 
6980
9047
  export const CapabilityPackConnectorAuthModel = z.enum([
6981
9048
  "oauth2_authorization_code_pkce",
@@ -7282,6 +9349,7 @@ export const CapabilityPack = z.preprocess(
7282
9349
  connectors: z.array(CapabilityPackConnector).default([]),
7283
9350
  knowledge: z.array(CapabilityPackKnowledge).default([]),
7284
9351
  scheduledTaskTemplates: z.array(CapabilityPackScheduledTaskTemplate).default([]),
9352
+ automationTemplates: z.array(CapabilityPackAutomationTemplate).max(64).optional(),
7285
9353
  variableSet: CapabilityPackVariableSet.optional(),
7286
9354
  metadata: z.record(z.string(), z.unknown()).default({}),
7287
9355
  })
@@ -7308,6 +9376,17 @@ export const CapabilityPack = z.preprocess(
7308
9376
  }
7309
9377
  componentKeys.add(key);
7310
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
+ });
7311
9390
  if (!pack.sandboxProviderImages?.modal) {
7312
9391
  return;
7313
9392
  }
@@ -7345,33 +9424,231 @@ export const WorkspaceRegisteredPack = z.object({
7345
9424
  });
7346
9425
  export type WorkspaceRegisteredPack = z.infer<typeof WorkspaceRegisteredPack>;
7347
9426
 
7348
- export const PackInstallationStatus = z.enum([
7349
- "installing",
7350
- "active",
7351
- "needs_attention",
7352
- "disabled",
7353
- ]);
7354
- export type PackInstallationStatus = z.infer<typeof PackInstallationStatus>;
9427
+ export const PackInstallationStatus = z.enum([
9428
+ "installing",
9429
+ "active",
9430
+ "needs_attention",
9431
+ "disabled",
9432
+ ]);
9433
+ export type PackInstallationStatus = z.infer<typeof PackInstallationStatus>;
9434
+
9435
+ export const PackInstallation = z.object({
9436
+ id: z.string().uuid(),
9437
+ accountId: z.string().uuid(),
9438
+ workspaceId: z.string().uuid(),
9439
+ packId: z.string().min(1),
9440
+ status: PackInstallationStatus,
9441
+ version: z.number().int().positive(),
9442
+ manifestSnapshot: CapabilityPack.nullable(),
9443
+ manifestDigest: z
9444
+ .string()
9445
+ .regex(/^[0-9a-f]{64}$/)
9446
+ .nullable(),
9447
+ selectedRigId: z.string().uuid().nullable(),
9448
+ installedBySubjectId: z.string().min(1).max(1024).nullable(),
9449
+ metadata: z.record(z.string(), z.unknown()),
9450
+ enabledAt: z.string(),
9451
+ updatedAt: z.string(),
9452
+ });
9453
+ export type PackInstallation = z.infer<typeof PackInstallation>;
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
+ >;
7355
9615
 
7356
- export const PackInstallation = z.object({
7357
- id: z.string().uuid(),
7358
- accountId: z.string().uuid(),
7359
- workspaceId: z.string().uuid(),
7360
- packId: z.string().min(1),
7361
- status: PackInstallationStatus,
7362
- version: z.number().int().positive(),
7363
- manifestSnapshot: CapabilityPack.nullable(),
7364
- manifestDigest: z
7365
- .string()
7366
- .regex(/^[0-9a-f]{64}$/)
7367
- .nullable(),
7368
- selectedRigId: z.string().uuid().nullable(),
7369
- installedBySubjectId: z.string().min(1).max(1024).nullable(),
7370
- metadata: z.record(z.string(), z.unknown()),
7371
- enabledAt: z.string(),
7372
- updatedAt: z.string(),
7373
- });
7374
- export type PackInstallation = z.infer<typeof PackInstallation>;
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>;
7375
9652
 
7376
9653
  export const EnablePackRequest = withVariableSetIdAlias({
7377
9654
  variableSetId: z.string().uuid().optional(),
@@ -7594,6 +9871,8 @@ export const OPENGENI_PERSONAL_SLACK_MCP_URL = "https://mcp.slack.com/mcp" as co
7594
9871
  export const OPENGENI_SLACK_BOT_CREDENTIAL_ROLE = "opengeni_slack_bot" as const;
7595
9872
  export const OPENGENI_SLACK_BOT_CREDENTIAL_LABEL = "OpenGeni Slack bot" as const;
7596
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>;
7597
9876
  export const OpenGeniSlackBotConnectionMetadata = z
7598
9877
  .object({
7599
9878
  credentialRole: z.literal(OPENGENI_SLACK_BOT_CREDENTIAL_ROLE),
@@ -7602,7 +9881,7 @@ export const OpenGeniSlackBotConnectionMetadata = z
7602
9881
  slackTeamName: z.string().min(1).max(256),
7603
9882
  botUserId: z.string().min(1).max(64),
7604
9883
  botId: z.string().min(1).max(64),
7605
- botDisplayName: z.literal("OpenGeni"),
9884
+ botDisplayName: OpenGeniSlackBotDisplayName,
7606
9885
  verifiedAt: z.string().datetime({ offset: true }),
7607
9886
  })
7608
9887
  .passthrough();
@@ -7652,6 +9931,8 @@ export type FikenOAuthStartResponse = z.infer<typeof FikenOAuthStartResponse>;
7652
9931
 
7653
9932
  export const ConnectionMetadata = z.object({
7654
9933
  id: z.string().uuid(),
9934
+ /** Opaque owner-only handle used to manage this personal connection's grants. */
9935
+ authorityId: z.string().uuid().optional(),
7655
9936
  accountId: z.string().uuid(),
7656
9937
  workspaceId: z.string().uuid(),
7657
9938
  subjectId: z.string().nullable(),
@@ -7781,7 +10062,7 @@ export const SlackInstallationBinding = z.object({
7781
10062
  slackTeamName: z.string().min(1).max(256),
7782
10063
  botId: z.string().min(1).max(64),
7783
10064
  botUserId: z.string().min(1).max(64),
7784
- botDisplayName: z.literal("OpenGeni"),
10065
+ botDisplayName: OpenGeniSlackBotDisplayName,
7785
10066
  state: SlackInstallationBindingState,
7786
10067
  quarantineReason: z.string().min(1).nullable(),
7787
10068
  version: z.number().int().positive(),
@@ -8287,6 +10568,43 @@ export const IntegrationFacetDefinitionSummary = z
8287
10568
  .strict();
8288
10569
  export type IntegrationFacetDefinitionSummary = z.infer<typeof IntegrationFacetDefinitionSummary>;
8289
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
+
8290
10608
  export const IntegrationDefinitionSummary = z
8291
10609
  .object({
8292
10610
  id: z.string().min(1).max(128),
@@ -8305,6 +10623,7 @@ export const IntegrationDefinitionSummary = z
8305
10623
  scopes: z.array(z.string().min(1).max(1024)).max(256),
8306
10624
  })
8307
10625
  .strict(),
10626
+ presentation: IntegrationPresentation.optional(),
8308
10627
  facets: z.array(IntegrationFacetDefinitionSummary).max(128),
8309
10628
  })
8310
10629
  .strict();
@@ -8338,6 +10657,8 @@ export const IntegrationFacetBindingSummary = z
8338
10657
  lastErrorCode: z.string().min(1).max(120).nullable(),
8339
10658
  createdAt: z.string().datetime({ offset: true }),
8340
10659
  updatedAt: z.string().datetime({ offset: true }),
10660
+ directlyOwned: z.boolean(),
10661
+ owners: z.array(CapabilityComponentOwner),
8341
10662
  })
8342
10663
  .strict();
8343
10664
  export type IntegrationFacetBindingSummary = z.infer<typeof IntegrationFacetBindingSummary>;
@@ -8860,11 +11181,59 @@ export const UninstallPluginResult = z
8860
11181
  .strict();
8861
11182
  export type UninstallPluginResult = z.infer<typeof UninstallPluginResult>;
8862
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
+
8863
11230
  export const Session = z.object({
8864
11231
  id: z.string().uuid(),
8865
11232
  workspaceId: z.string().uuid(),
8866
11233
  accountId: z.string().uuid(),
8867
11234
  status: SessionStatus,
11235
+ /** Additive list projection. Detail reads may omit it. */
11236
+ backgroundCommandActivity: SessionBackgroundCommandActivity.optional(),
8868
11237
  initialMessage: z.string(),
8869
11238
  title: z.string().nullable(),
8870
11239
  titleSource: z.enum(["user", "agent"]).nullable(),
@@ -8893,6 +11262,8 @@ export const Session = z.object({
8893
11262
  createdBy: TurnInitiator,
8894
11263
  createdByContext: TurnInitiatorContext,
8895
11264
  model: z.string(),
11265
+ reasoningEffort: ReasoningEffort,
11266
+ latencyMode: LatencyMode,
8896
11267
  sandboxBackend: SandboxBackend,
8897
11268
  // The OS the session's box runs. Defaults to 'linux' (today's only OS).
8898
11269
  sandboxOs: SandboxOs,
@@ -8976,6 +11347,17 @@ export const Session = z.object({
8976
11347
  pinnedAt: z.string().nullable().default(null),
8977
11348
  /** Optimistic pin-state revision; zero represents an absent pin relation. */
8978
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),
8979
11361
  /**
8980
11362
  * Server-authoritative hierarchy summary populated on session-list reads.
8981
11363
  * Detail reads may omit it. The rail uses this instead of guessing a tree
@@ -8990,10 +11372,24 @@ export const Session = z.object({
8990
11372
  attentionDescendants: z.number().int().nonnegative(),
8991
11373
  pausedDescendants: z.number().int().nonnegative(),
8992
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(),
8993
11383
  /** Counts are lower bounds rather than exact totals when true. */
8994
11384
  truncated: z.boolean().default(false),
8995
11385
  })
8996
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(),
8997
11393
  createdAt: z.string(),
8998
11394
  updatedAt: z.string(),
8999
11395
  });
@@ -9102,6 +11498,7 @@ export type SessionLineageResponse = z.infer<typeof SessionLineageResponse>;
9102
11498
 
9103
11499
  export const SessionEventType = z.enum([
9104
11500
  "session.created",
11501
+ "session.personal_resources.attached",
9105
11502
  "session.visibility.changed",
9106
11503
  // Defensive read/transport projection for a malformed or historically
9107
11504
  // oversized retained event envelope. The original row stays durable; this
@@ -9130,13 +11527,20 @@ export const SessionEventType = z.enum([
9130
11527
  "turn.superseded",
9131
11528
  "turn.recovery.requested",
9132
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",
9133
11536
  "agent.message.delta",
9134
11537
  "agent.message.completed",
9135
11538
  "agent.reasoning.delta",
9136
11539
  "agent.toolCall.created",
9137
11540
  "agent.toolCall.output",
9138
- // Attempt-fenced Codex Responses lifecycle metadata (request identity,
9139
- // 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.
9140
11544
  "agent.model.request",
9141
11545
  "agent.model.usage",
9142
11546
  "tool.auth_needed",
@@ -9149,6 +11553,8 @@ export const SessionEventType = z.enum([
9149
11553
  "sandbox.operation.started",
9150
11554
  "sandbox.operation.completed",
9151
11555
  "sandbox.operation.failed",
11556
+ "session.command.backgrounded",
11557
+ "session.command.finished",
9152
11558
  "sandbox.command.output.delta",
9153
11559
  "artifact.created",
9154
11560
  "goal.set",
@@ -9160,6 +11566,7 @@ export const SessionEventType = z.enum([
9160
11566
  "goal.paused",
9161
11567
  "goal.resumed",
9162
11568
  "goal.cleared",
11569
+ "goal.held",
9163
11570
  "goal.continuation",
9164
11571
  "system.update.pending",
9165
11572
  "system.update.delivered",
@@ -9352,6 +11759,7 @@ export const SESSION_EVENT_RAW_DELTA_TYPES = [
9352
11759
  export const SESSION_EVENT_SEMANTIC_CLASS_TYPES = {
9353
11760
  control: [
9354
11761
  "session.status.changed",
11762
+ "session.command.backgrounded",
9355
11763
  "session.requiresAction",
9356
11764
  "session.humanInput.requested",
9357
11765
  "user.pause",
@@ -9366,6 +11774,7 @@ export const SESSION_EVENT_SEMANTIC_CLASS_TYPES = {
9366
11774
  "goal.paused",
9367
11775
  "goal.resumed",
9368
11776
  "goal.cleared",
11777
+ "goal.held",
9369
11778
  "goal.continuation",
9370
11779
  "system.update.pending",
9371
11780
  "system.update.delivered",
@@ -9398,6 +11807,7 @@ export const SESSION_EVENT_SEMANTIC_CLASS_TYPES = {
9398
11807
  "recording.available",
9399
11808
  "recording.failed",
9400
11809
  "terminal.pty.exited",
11810
+ "session.command.finished",
9401
11811
  ],
9402
11812
  failure: [
9403
11813
  "session.event.envelope_omitted",
@@ -9724,7 +12134,9 @@ export type FsNodeType = z.infer<typeof FsNodeType>;
9724
12134
  // depth>0; the tree lazy-expands via repeated depth-1 lists at deeper paths.
9725
12135
  export interface FsTreeNode {
9726
12136
  name: string;
9727
- 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;
9728
12140
  type: z.infer<typeof FsNodeType>;
9729
12141
  sizeBytes: number | null; // null for dirs
9730
12142
  mtimeMs: number | null;
@@ -9746,7 +12158,9 @@ export const FsTreeNode: z.ZodType<FsTreeNode> = z.lazy(() =>
9746
12158
  ) as z.ZodType<FsTreeNode>;
9747
12159
 
9748
12160
  export const FsListRequest = z.object({
9749
- 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(""),
9750
12164
  depth: z.number().int().min(0).max(8).default(1),
9751
12165
  maxEntries: z.number().int().positive().max(20_000).default(2_000),
9752
12166
  includeHidden: z.boolean().default(true),
@@ -11268,6 +13682,8 @@ export const SessionControlResponse = z.object({
11268
13682
  });
11269
13683
  export type SessionControlResponse = z.infer<typeof SessionControlResponse>;
11270
13684
 
13685
+ export const SESSION_INSTRUCTIONS_MAX_CHARACTERS = 65_536;
13686
+
11271
13687
  export const CreateSessionRequest = withVariableSetIdAlias(
11272
13688
  {
11273
13689
  /**
@@ -11277,6 +13693,12 @@ export const CreateSessionRequest = withVariableSetIdAlias(
11277
13693
  * identity or authorization from the UUID.
11278
13694
  */
11279
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"),
11280
13702
  initialMessage: z.string().min(1).optional(),
11281
13703
  // Creates the durable session shell without fabricating a user message or
11282
13704
  // starting an underlying agent turn. Realtime can then become the first
@@ -11291,10 +13713,10 @@ export const CreateSessionRequest = withVariableSetIdAlias(
11291
13713
  // agentInstructions rides, composed AFTER the workspace persona so it refines
11292
13714
  // it for this one session — how a host delivers per-agent-type prompts without
11293
13715
  // leaking them into the user-visible timeline (it is NEVER emitted as an
11294
- // event, unlike goal/initialMessage). Trimmed, non-empty. The 32768-char cap
11295
- // matches the codebase's largest free-form string convention (workspace
11296
- // variable set variable values). Absent ⇒ byte-identical to today.
11297
- instructions: z.string().trim().min(1).max(32768).optional(),
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(),
11298
13720
  // Immutable prompt-policy role binding for matching one activated role
11299
13721
  // policy. This never derives from or grants a human workspace membership
11300
13722
  // role. Existing callers may continue to use normalized metadata.role as a
@@ -11322,11 +13744,11 @@ export const CreateSessionRequest = withVariableSetIdAlias(
11322
13744
  // machine (race-free: the pointer is committed before the worker turn
11323
13745
  // workflow can read it). An invalid/unowned/offline target fails the create.
11324
13746
  targetSandboxId: z.string().uuid().optional(),
11325
- // The working directory the targeted machine runs the session under the
11326
- // path/cwd base for its agent exec, terminal, and file dock. Free-form pass-
11327
- // through: a launch-workspace_root-relative subdir or an absolute machine path
11328
- // (the agent's resolve_cwd handles both). Only valid WITH targetSandboxId
11329
- // (workingDir alone is a 422); omitted the machine's default workspace_root.
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.
11330
13752
  workingDir: z.string().min(1).optional(),
11331
13753
  // Variable set attachment is fixed at session creation; follow-up
11332
13754
  // user.message events cannot switch or add one.
@@ -11351,9 +13773,10 @@ export const CreateSessionRequest = withVariableSetIdAlias(
11351
13773
  // is an independent create).
11352
13774
  idempotencyKey: z.string().min(1).max(200).optional(),
11353
13775
  // The exact actor-private pre-session draft revision represented by this
11354
- // create. The durable initializer consumes only this revision. A newer draft
11355
- // written by a sibling tab survives, while every failed pre-initialization
11356
- // create leaves the submitted draft intact.
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.
11357
13780
  expectedNewSessionDraftRevision: z.number().int().nonnegative().optional(),
11358
13781
  // A child may lower its inherited limit freely; an increase requires
11359
13782
  // workspace:admin and is checked again at the DB transaction boundary.
@@ -11377,9 +13800,16 @@ export const CreateSessionRequest = withVariableSetIdAlias(
11377
13800
  // permission. Credential headers are write-only: create responses and events
11378
13801
  // expose only SessionMcpServerMetadata.
11379
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(),
11380
13807
  // Shared-sandbox placement (addendum 05 §D.1). Three-way union; OMITTED ⇒
11381
13808
  // today's behavior (a context-dependent default resolved server-side: from
11382
- // inside a session → "shared" with the creator's box, top-level → "new").
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.
11383
13813
  // - "shared": join the CREATOR's box. Requires a parent session (inferred
11384
13814
  // from the worker-signed sessionId claim, never caller-supplied);
11385
13815
  // top-level "shared" is a 422.
@@ -11421,9 +13851,39 @@ export const CreateSessionRequest = withVariableSetIdAlias(
11421
13851
  message: "modelContext requires an initialMessage; attach it to a realtime entry instead",
11422
13852
  });
11423
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
+ }
11424
13877
  });
11425
13878
  export type CreateSessionRequest = z.infer<typeof CreateSessionRequest>;
11426
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
+
11427
13887
  // Generic, host-neutral structured human input. One model tool call creates one
11428
13888
  // request containing one or more questions; the durable response resumes that
11429
13889
  // exact call. This is deliberately distinct from tool approval: an answer,
@@ -11447,6 +13907,9 @@ export const HumanInputQuestion = z
11447
13907
  helpText: z.string().max(2048).nullable().optional(),
11448
13908
  options: z.array(HumanInputOption).max(20).default([]),
11449
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.
11450
13913
  allowOther: z.boolean().default(false),
11451
13914
  // Selection bounds only — agents invent useless text char mins/maxes.
11452
13915
  // Answer strings stay platform-capped on HumanInputAnswer (~8192).
@@ -11619,9 +14082,13 @@ export const SessionUserMessagePayload = z
11619
14082
  // Header-value rotation only. URL/name/tool settings are immutable after
11620
14083
  // session create; persisted events expose metadata, never header values.
11621
14084
  mcpCredentialUpdates: z.array(SessionMcpCredentialUpdateInput).optional(),
14085
+ /** Explicit personal-connection grants for this exact logical turn. */
14086
+ connectionAuthorities: McpConnectionAuthoritySelections.default([]),
14087
+ personalResourceAttachment: PersonalResourceAttachmentIntent.optional(),
11622
14088
  })
11623
14089
  .strict()
11624
- .superRefine(requireMessageTextOrAnnotations);
14090
+ .superRefine(requireMessageTextOrAnnotations)
14091
+ .superRefine(requireEstablishedPersonalResourceEpoch);
11625
14092
  export type SessionUserMessagePayload = z.infer<typeof SessionUserMessagePayload>;
11626
14093
 
11627
14094
  export const ClientSessionEvent = z.discriminatedUnion("type", [
@@ -11664,17 +14131,36 @@ export const SteerSessionMessageRequest = z
11664
14131
  controlEtag: z.string().min(1).optional(),
11665
14132
  expectedDraftRevision: z.number().int().nonnegative().optional(),
11666
14133
  mcpCredentialUpdates: z.array(SessionMcpCredentialUpdateInput).optional(),
14134
+ /** Explicit personal-connection grants for this exact steered turn. */
14135
+ connectionAuthorities: McpConnectionAuthoritySelections.default([]),
14136
+ personalResourceAttachment: PersonalResourceAttachmentIntent.optional(),
11667
14137
  })
11668
14138
  .strict()
11669
- .superRefine(requireMessageTextOrAnnotations);
14139
+ .superRefine(requireMessageTextOrAnnotations)
14140
+ .superRefine(requireEstablishedPersonalResourceEpoch);
11670
14141
  export type SteerSessionMessageRequest = z.infer<typeof SteerSessionMessageRequest>;
11671
14142
 
11672
14143
  export const SteerSessionMessageResponse = z.object({
11673
14144
  accepted: SessionEvent,
11674
14145
  turn: SessionTurn,
14146
+ receipt: SessionCommandReceipt,
14147
+ routing: SessionPromptRouting,
14148
+ interruptionCount: z.number().int().nonnegative(),
14149
+ replay: z.boolean(),
11675
14150
  });
11676
14151
  export type SteerSessionMessageResponse = z.infer<typeof SteerSessionMessageResponse>;
11677
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
+
11678
14164
  export const SessionBusMessage = z.object({
11679
14165
  workspaceId: z.string().uuid(),
11680
14166
  sessionId: z.string().uuid(),
@@ -11765,6 +14251,7 @@ export const ClientAuthConfig = z.discriminatedUnion("mode", [
11765
14251
  z.object({
11766
14252
  mode: z.literal("managedSession"),
11767
14253
  session: z.literal("cookie"),
14254
+ emailVerificationRequired: z.boolean().default(true),
11768
14255
  }),
11769
14256
  ]);
11770
14257
  export type ClientAuthConfig = z.infer<typeof ClientAuthConfig>;
@@ -12029,6 +14516,9 @@ export type DeviceEnrollmentStartResponse = z.infer<typeof DeviceEnrollmentStart
12029
14516
  export const DeviceEnrollmentApproveRequest = z.object({
12030
14517
  userCode: z.string().min(1).max(64),
12031
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"),
12032
14522
  });
12033
14523
  export type DeviceEnrollmentApproveRequest = z.infer<typeof DeviceEnrollmentApproveRequest>;
12034
14524
 
@@ -12101,6 +14591,8 @@ export type DeviceEnrollmentPollResponse = z.infer<typeof DeviceEnrollmentPollRe
12101
14591
  // GET /enrollments — a workspace's machines (the Machines dashboard surface).
12102
14592
  export const EnrollmentSummary = z.object({
12103
14593
  id: z.string().uuid(),
14594
+ scope: ResourceAuthorityScope,
14595
+ generation: z.number().int().positive(),
12104
14596
  pubkey: z.string(),
12105
14597
  exposure: z.literal("whole-machine"),
12106
14598
  hasDisplay: z.boolean(),
@@ -12297,9 +14789,137 @@ export const MachineState = z.enum([
12297
14789
  ]);
12298
14790
  export type MachineState = z.infer<typeof MachineState>;
12299
14791
 
12300
- export const MachineKind = z.enum(["modal", "selfhosted"]);
14792
+ export const MachineKind = z.enum(["modal", "selfhosted", "opensandbox"]);
12301
14793
  export type MachineKind = z.infer<typeof MachineKind>;
12302
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
+
12303
14923
  /**
12304
14924
  * A machine as the Machines dashboard renders it. The workspace's enrolled
12305
14925
  * selfhosted machines PLUS the session's synthetic Modal group box
@@ -12311,6 +14931,8 @@ export type MachineKind = z.infer<typeof MachineKind>;
12311
14931
  export const MachineView = z.object({
12312
14932
  sandboxId: z.string(),
12313
14933
  enrollmentId: z.string().nullable(),
14934
+ scope: ResourceAuthorityScope.default("workspace"),
14935
+ generation: z.number().int().positive().default(1),
12314
14936
  name: z.string(),
12315
14937
  kind: MachineKind,
12316
14938
  state: MachineState,
@@ -12329,6 +14951,12 @@ export const MachineView = z.object({
12329
14951
  allowScreenControl: z.boolean(),
12330
14952
  sharedSessionCount: z.number().int(),
12331
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),
12332
14960
  metrics: MetricSample.nullable(),
12333
14961
  });
12334
14962
  export type MachineView = z.infer<typeof MachineView>;
@@ -12405,8 +15033,9 @@ function defineModelContractSchema<Schema>(factory: () => Schema): Schema {
12405
15033
  return factory();
12406
15034
  }
12407
15035
 
12408
- export const ModelCapabilitySupportV1 =
12409
- /* @__PURE__ */ defineModelContractSchema(() => z.enum(["supported", "unsupported", "unknown"]));
15036
+ export const ModelCapabilitySupportV1 = /* @__PURE__ */ defineModelContractSchema(() =>
15037
+ z.enum(["supported", "unsupported", "unknown"]),
15038
+ );
12410
15039
  export type ModelCapabilitySupportV1 = z.infer<typeof ModelCapabilitySupportV1>;
12411
15040
 
12412
15041
  export const ModelCapabilityStateV1 = /* @__PURE__ */ defineModelContractSchema(() =>
@@ -12454,60 +15083,65 @@ export const ModelCapabilitiesV1 = /* @__PURE__ */ defineModelContractSchema(()
12454
15083
  );
12455
15084
  export type ModelCapabilitiesV1 = z.infer<typeof ModelCapabilitiesV1>;
12456
15085
 
12457
- export const ModelCredentialSourceV1 =
12458
- /* @__PURE__ */ defineModelContractSchema(() =>
12459
- z.union([
12460
- z
12461
- .object({
12462
- kind: z.literal("deployment"),
12463
- mechanism: z.enum(["api_key", "azure_ad_bearer"]),
12464
- })
12465
- .strict(),
12466
- z
12467
- .object({
12468
- kind: z.literal("connected_subscription"),
12469
- provider: z.enum(["codex", "xai"]),
12470
- })
12471
- .strict(),
12472
- z
12473
- .object({
12474
- kind: z.literal("workspace_connection"),
12475
- mechanism: z.literal("api_key"),
12476
- })
12477
- .strict(),
12478
- ]),
12479
- );
12480
- export type ModelCredentialSourceV1 = z.infer<typeof ModelCredentialSourceV1>;
12481
-
12482
- export const ModelBillingAttributionV1 =
12483
- /* @__PURE__ */ defineModelContractSchema(() =>
15086
+ export const ModelCredentialSourceV1 = /* @__PURE__ */ defineModelContractSchema(() =>
15087
+ z.union([
12484
15088
  z
12485
15089
  .object({
12486
- upstreamPayer: z.enum(["deployment", "workspace", "connected_subscription"]),
12487
- metering: z.enum(["opengeni_credits", "external"]),
15090
+ kind: z.literal("deployment"),
15091
+ mechanism: z.enum(["api_key", "azure_ad_bearer"]),
12488
15092
  })
12489
15093
  .strict(),
12490
- );
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
+ );
12491
15128
  export type ModelBillingAttributionV1 = z.infer<typeof ModelBillingAttributionV1>;
12492
15129
 
12493
15130
  export const TURN_EXECUTION_POLICY_METADATA_KEY = "turnExecutionPolicyV1" as const;
12494
15131
 
12495
- export const TurnExecutionModelSourceV1 =
12496
- /* @__PURE__ */ defineModelContractSchema(() =>
12497
- z.enum(["explicit", "session", "deployment", "continuation"]),
12498
- );
15132
+ export const TurnExecutionModelSourceV1 = /* @__PURE__ */ defineModelContractSchema(() =>
15133
+ z.enum(["explicit", "session", "deployment", "continuation"]),
15134
+ );
12499
15135
  export type TurnExecutionModelSourceV1 = z.infer<typeof TurnExecutionModelSourceV1>;
12500
15136
 
12501
- export const TurnExecutionReasoningSourceV1 =
12502
- /* @__PURE__ */ defineModelContractSchema(() =>
12503
- z.enum(["explicit", "session", "deployment", "continuation"]),
12504
- );
15137
+ export const TurnExecutionReasoningSourceV1 = /* @__PURE__ */ defineModelContractSchema(() =>
15138
+ z.enum(["explicit", "session", "deployment", "continuation"]),
15139
+ );
12505
15140
  export type TurnExecutionReasoningSourceV1 = z.infer<typeof TurnExecutionReasoningSourceV1>;
12506
15141
 
12507
- export const TurnExecutionLatencyModeSourceV1 =
12508
- /* @__PURE__ */ defineModelContractSchema(() =>
12509
- z.enum(["explicit", "session", "deployment", "continuation"]),
12510
- );
15142
+ export const TurnExecutionLatencyModeSourceV1 = /* @__PURE__ */ defineModelContractSchema(() =>
15143
+ z.enum(["explicit", "session", "deployment", "continuation"]),
15144
+ );
12511
15145
  export type TurnExecutionLatencyModeSourceV1 = z.infer<typeof TurnExecutionLatencyModeSourceV1>;
12512
15146
 
12513
15147
  /**
@@ -12535,7 +15169,7 @@ export const TurnExecutionPolicyV1 = /* @__PURE__ */ defineModelContractSchema((
12535
15169
  providerId: z.string().min(1),
12536
15170
  upstreamModelId: z.string().min(1),
12537
15171
  wireApi: z.enum(["responses", "chat"]),
12538
- credentialSource: ModelCredentialSourceV1,
15172
+ credentialSource: TurnExecutionCredentialSourceV1,
12539
15173
  billing: ModelBillingAttributionV1,
12540
15174
  definitionVersion: z.string().regex(/^sha256:[a-f0-9]{64}$/u),
12541
15175
  })
@@ -12709,60 +15343,59 @@ export const ClientModel = /* @__PURE__ */ defineModelContractSchema(() =>
12709
15343
  );
12710
15344
  export type ClientModel = z.infer<typeof ClientModel>;
12711
15345
 
12712
- export const ModelCredentialReadinessV1 =
12713
- /* @__PURE__ */ defineModelContractSchema(() =>
12714
- z
12715
- .object({
12716
- status: z.enum(["ready", "not_ready", "error"]),
12717
- reason: z
12718
- .enum([
12719
- "missing_credential",
12720
- "needs_reauth",
12721
- "prerequisites_missing",
12722
- "resolver_error",
12723
- "observation_stale",
12724
- ])
12725
- .nullable(),
12726
- basis: z.enum(["configuration", "connection", "resolver"]),
12727
- checkedAt: z.string().datetime().nullable(),
12728
- })
12729
- .strict()
12730
- .superRefine((readiness, context) => {
12731
- if ((readiness.status === "ready") !== (readiness.reason === null)) {
12732
- context.addIssue({
12733
- code: "custom",
12734
- path: ["reason"],
12735
- message: "ready credential state requires no reason; non-ready state requires a reason",
12736
- });
12737
- }
12738
- if ((readiness.status === "error") !== (readiness.reason === "resolver_error")) {
12739
- context.addIssue({
12740
- code: "custom",
12741
- path: ["reason"],
12742
- message:
12743
- "credential errors require resolver_error and resolver_error requires error status",
12744
- });
12745
- }
12746
- if (
12747
- readiness.basis === "resolver" &&
12748
- readiness.status === "ready" &&
12749
- readiness.checkedAt === null
12750
- ) {
12751
- context.addIssue({
12752
- code: "custom",
12753
- path: ["checkedAt"],
12754
- message: "resolver readiness requires an observation timestamp",
12755
- });
12756
- }
12757
- if (readiness.reason === "observation_stale" && readiness.checkedAt === null) {
12758
- context.addIssue({
12759
- code: "custom",
12760
- path: ["checkedAt"],
12761
- message: "a stale observation requires its observation timestamp",
12762
- });
12763
- }
12764
- }),
12765
- );
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
+ );
12766
15399
  export type ModelCredentialReadinessV1 = z.infer<typeof ModelCredentialReadinessV1>;
12767
15400
 
12768
15401
  export const ModelAvailabilityV1 = /* @__PURE__ */ defineModelContractSchema(() =>
@@ -12785,21 +15418,21 @@ export const ModelAvailabilityV1 = /* @__PURE__ */ defineModelContractSchema(()
12785
15418
  );
12786
15419
  export type ModelAvailabilityV1 = z.infer<typeof ModelAvailabilityV1>;
12787
15420
 
12788
- export const WorkspaceModelCatalogModel =
12789
- /* @__PURE__ */ defineModelContractSchema(() =>
12790
- ClientModel.extend({
12791
- credentialReadiness: ModelCredentialReadinessV1,
12792
- availability: ModelAvailabilityV1,
12793
- }),
12794
- );
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
+ );
12795
15429
  export type WorkspaceModelCatalogModel = z.infer<typeof WorkspaceModelCatalogModel>;
12796
15430
 
12797
- export const WorkspaceModelCatalogResponse =
12798
- /* @__PURE__ */ defineModelContractSchema(() =>
12799
- z.object({
12800
- models: z.array(WorkspaceModelCatalogModel),
12801
- }),
12802
- );
15431
+ export const WorkspaceModelCatalogResponse = /* @__PURE__ */ defineModelContractSchema(() =>
15432
+ z.object({
15433
+ models: z.array(WorkspaceModelCatalogModel),
15434
+ }),
15435
+ );
12803
15436
  export type WorkspaceModelCatalogResponse = z.infer<typeof WorkspaceModelCatalogResponse>;
12804
15437
 
12805
15438
  /**
@@ -12810,7 +15443,7 @@ export type WorkspaceModelCatalogResponse = z.infer<typeof WorkspaceModelCatalog
12810
15443
  * that rollout boundary. Mutating clients send this value in
12811
15444
  * `x-opengeni-api-contract`; the API rejects any other value before routing.
12812
15445
  */
12813
- export const OPENGENI_API_CONTRACT_REVISION = "2026-08-model-context-v1" as const;
15446
+ export const OPENGENI_API_CONTRACT_REVISION = "2026-08-atomic-session-fork-visibility-v1" as const;
12814
15447
  export const OPENGENI_API_CONTRACT_HEADER = "x-opengeni-api-contract" as const;
12815
15448
  /** Bounded request/response identifier shared by browser, ingress, and API diagnostics. */
12816
15449
  export const OPENGENI_CORRELATION_HEADER = "x-opengeni-correlation-id" as const;
@@ -12984,10 +15617,18 @@ export * from "./codex-fleet-policy";
12984
15617
  export * from "./xai-provider-account-authority";
12985
15618
  export * from "./workspace-instruction-policies";
12986
15619
  export * from "./company-profile";
15620
+ export * from "./company-brain";
12987
15621
  export * from "./workspace-learning-policy";
15622
+ export * from "./workspace-learning-administration";
12988
15623
  export * from "./workspace-state";
12989
15624
  export * from "./preference-registry";
12990
15625
  export * from "./scoped-knowledge";
15626
+ export * from "./company-brain-governed-writes";
15627
+ export * from "./governed-learning-evaluator";
15628
+ export * from "./governed-learning-activation";
12991
15629
  export * from "./knowledge";
12992
15630
  export * from "./task-notes";
12993
15631
  export * from "./canonical-human-identities";
15632
+ export * from "./organization-membership-lifecycle";
15633
+ export * from "./remember";
15634
+ export * from "./agent-authored-durable-text";