@opengeni/contracts 0.38.3 → 0.40.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.
package/src/index.ts CHANGED
@@ -6,6 +6,7 @@ import {
6
6
  type SessionEventBoundarySurface,
7
7
  } from "./event-preview";
8
8
  import { WorkspaceInstructionPolicyRoleKeyInput } from "./workspace-instruction-policies";
9
+ import { ClientResumableVoiceInputConfig } from "./transcription-recordings";
9
10
 
10
11
  export * from "./slack-bot-scopes";
11
12
  export * from "./connector-destinations";
@@ -35,6 +36,19 @@ export {
35
36
  normalizeWorkspaceArtifactSlug,
36
37
  } from "./artifacts";
37
38
 
39
+ export {
40
+ MCP_MUTATION_RECEIPT_MAX_BYTES,
41
+ MCP_MUTATION_RECEIPT_VERSION,
42
+ McpMutationReceipt,
43
+ McpMutationReceiptIdempotencyStatus,
44
+ McpMutationReceiptOutcome,
45
+ McpMutationResource,
46
+ type McpMutationReceipt as McpMutationReceiptType,
47
+ type McpMutationReceiptIdempotencyStatus as McpMutationReceiptIdempotencyStatusType,
48
+ type McpMutationReceiptOutcome as McpMutationReceiptOutcomeType,
49
+ type McpMutationResource as McpMutationResourceType,
50
+ } from "./mcp-receipts";
51
+
38
52
  export {
39
53
  SESSION_EVENT_PAYLOAD_MAX_BYTES,
40
54
  approximateSessionEventTokens,
@@ -52,6 +66,11 @@ export {
52
66
  } from "./event-preview";
53
67
 
54
68
  export {
69
+ COMPUTER_SCREENSHOT_MAX_BYTES,
70
+ COMPUTER_SCREENSHOT_MAX_DIMENSION,
71
+ COMPUTER_SCREENSHOT_MAX_PIXELS,
72
+ COMPUTER_SCREENSHOT_RETENTION_MS,
73
+ COMPUTER_SCREENSHOT_WORKSPACE_QUOTA_BYTES,
55
74
  RETAINED_OUTPUT_DEFAULT_PAGE_BYTES,
56
75
  RETAINED_OUTPUT_MAX_PAGE_BYTES,
57
76
  RETAINED_OUTPUT_RECEIPT_MAX_BYTES,
@@ -62,12 +81,14 @@ export {
62
81
  RetainedOutputKind,
63
82
  RetainedOutputUnavailableReason,
64
83
  retainedArtifactReferenceFromFile,
84
+ retainedScreenshotReferenceFromFile,
65
85
  retainedOutputUnavailable,
66
86
  resolveRetainedOutputRange,
67
87
  validateRetainedOutputEvidence,
68
88
  type RetainedArtifactFileInput,
69
89
  type RetainedArtifactMetadata,
70
90
  type RetainedArtifactReference,
91
+ type RetainedScreenshotArtifactInput,
71
92
  type RetainedArtifactUnavailable,
72
93
  type RetainedOutputAvailableEvidence,
73
94
  type RetainedOutputEvidence,
@@ -595,9 +616,9 @@ export const Permission = z.enum([
595
616
  "sessions:control",
596
617
  // sandbox workspace (sandbox contract §C.3 / crosscut PART 1.2). stream:view is a
597
618
  // REAL, distinct permission — strictly BROADER than sessions:read — because the
598
- // pixel plane (Channel B) is UN-REDACTED: a viewer of raw pixels can see cloud
599
- // creds the agent cat's into a terminal, which the redacted Channel-A event log
600
- // never exposes. sessions:read is NOT permission to watch raw pixels.
619
+ // pixel plane (Channel B) exposes raw pixels: a viewer can see content the
620
+ // structured Channel-A event log never captured. sessions:read is NOT
621
+ // permission to watch raw pixels.
601
622
  "stream:view",
602
623
  // SEPARATE from stream:view: raw input to the desktop (bypasses approvalQueue /
603
624
  // interrupt). NEVER granted by default in v1 (the input plane is OFF —
@@ -628,8 +649,14 @@ export const Permission = z.enum([
628
649
  "environments:manage",
629
650
  /** @deprecated alias of variable-sets:use */
630
651
  "environments:use",
652
+ "variable-sets:list",
653
+ "variable-sets:read",
654
+ "variable-sets:write",
631
655
  "variable-sets:manage",
632
656
  "variable-sets:use",
657
+ "secrets:list",
658
+ "secrets:read",
659
+ "secrets:write",
633
660
  // Attach or rotate per-session third-party MCP server credentials. Deliberately
634
661
  // not part of the worker's default first-party MCP permission set: a sandboxed
635
662
  // agent must not be able to hand itself new bearer credentials.
@@ -714,6 +741,7 @@ export const FIRST_PARTY_MCP_TOOL_NAMES = [
714
741
  "sandbox_swap",
715
742
  "run_on",
716
743
  "sandbox_provision",
744
+ "connected_machine_remove",
717
745
  "rig_list",
718
746
  "rig_get",
719
747
  "rig_propose_change",
@@ -730,6 +758,7 @@ export const FIRST_PARTY_MCP_TOOL_NAMES = [
730
758
  "set_other_session_title",
731
759
  "variable_set_list",
732
760
  "environment_list",
761
+ "variable_set_get_variable",
733
762
  "variable_set_set_variable",
734
763
  "environment_set_variable",
735
764
  "github_connect_link",
@@ -1155,6 +1184,7 @@ export const ClientVoiceInputConfig = z
1155
1184
  .positive()
1156
1185
  .max(25 * 1024 * 1024),
1157
1186
  acceptedMimeTypes: z.array(z.string().trim().min(1).max(128)).min(1).max(32),
1187
+ resumable: ClientResumableVoiceInputConfig.optional(),
1158
1188
  })
1159
1189
  .strict();
1160
1190
  export type ClientVoiceInputConfig = z.infer<typeof ClientVoiceInputConfig>;
@@ -1184,6 +1214,8 @@ export const VOICE_INPUT_ACCEPTED_MIME_TYPES = [
1184
1214
  "audio/m4a",
1185
1215
  ] as const;
1186
1216
 
1217
+ export * from "./transcription-recordings";
1218
+
1187
1219
  /** Per-session / workspace Codex compaction strategy. */
1188
1220
  export const CodexCompactionMode = z.enum(["remote_v2", "portable"]);
1189
1221
  export type CodexCompactionMode = z.infer<typeof CodexCompactionMode>;
@@ -1258,6 +1290,9 @@ export const WorkspaceSettingsSchema = z
1258
1290
  // Default compaction strategy for NEW Codex sessions created in this
1259
1291
  // workspace. Absent ⇒ remote_v2. Non-Codex sessions always freeze portable.
1260
1292
  codexCompactionDefault: CodexCompactionMode.optional(),
1293
+ // Whether agents may expose and invoke the built-in structured human-input
1294
+ // tool. Absent preserves the historical enabled behavior.
1295
+ agentHumanInputEnabled: z.boolean().optional(),
1261
1296
  // Optional Slack reaction invocation. Absent/invalid fails closed to the
1262
1297
  // disabled default via resolveWorkspaceSlackReactionSummonSettings.
1263
1298
  slackReactionSummon: WorkspaceSlackReactionSummonSettings.optional(),
@@ -1278,6 +1313,12 @@ export function resolveWorkspaceCodexCompactionDefault(settings: unknown): Codex
1278
1313
  return parsed.data.codexCompactionDefault ?? "remote_v2";
1279
1314
  }
1280
1315
 
1316
+ /** Whether agents may request structured human input (enabled when unset). */
1317
+ export function resolveWorkspaceAgentHumanInputEnabled(settings: unknown): boolean {
1318
+ const parsed = WorkspaceSettingsSchema.safeParse(settings ?? {});
1319
+ return parsed.success ? parsed.data.agentHumanInputEnabled !== false : true;
1320
+ }
1321
+
1281
1322
  /**
1282
1323
  * Resolve whether voice input is enabled for a workspace.
1283
1324
  *
@@ -1303,7 +1344,9 @@ export function resolveWorkspaceSlackReactionSummonSettings(
1303
1344
  return {
1304
1345
  enabled: DEFAULT_WORKSPACE_SLACK_REACTION_SUMMON_SETTINGS.enabled,
1305
1346
  emoji: DEFAULT_WORKSPACE_SLACK_REACTION_SUMMON_SETTINGS.emoji,
1306
- channelPolicy: { ...DEFAULT_WORKSPACE_SLACK_REACTION_SUMMON_SETTINGS.channelPolicy },
1347
+ channelPolicy: {
1348
+ ...DEFAULT_WORKSPACE_SLACK_REACTION_SUMMON_SETTINGS.channelPolicy,
1349
+ },
1307
1350
  };
1308
1351
  }
1309
1352
  return configured.channelPolicy.mode === "allowlist"
@@ -1338,6 +1381,7 @@ export const UpdateWorkspaceSettingsRequest = z
1338
1381
  transcription: WorkspaceTranscriptionPolicy.optional(),
1339
1382
  maxNestedAgentDepth: NestedAgentDepthValue.nullable().optional(),
1340
1383
  codexCompactionDefault: CodexCompactionMode.optional(),
1384
+ agentHumanInputEnabled: z.boolean().optional(),
1341
1385
  slackReactionSummon: WorkspaceSlackReactionSummonSettings.optional(),
1342
1386
  })
1343
1387
  .passthrough();
@@ -1665,6 +1709,11 @@ export const EnrollmentBearerPayload = z.object({
1665
1709
  workspaceId: z.string().uuid(),
1666
1710
  agentId: z.string().uuid(),
1667
1711
  enrollmentId: z.string().uuid(),
1712
+ // Backward-compatible credential-family fence. Generationless bearers minted
1713
+ // before migration 0061 parse ONLY as generation 1, matching the migration's
1714
+ // default for existing rows. signEnrollmentBearer serializes the parsed output,
1715
+ // so every newly signed bearer carries this claim explicitly.
1716
+ credentialGeneration: z.number().int().positive().default(1),
1668
1717
  // The Account-scoped control-plane subject prefix the agent subscribes to.
1669
1718
  subjectPrefix: z.string().min(1),
1670
1719
  exp: z.number().int().positive(),
@@ -1699,7 +1748,13 @@ export async function verifyEnrollmentBearer(
1699
1748
  if (!constantTimeEqual(signature, expected)) {
1700
1749
  return null;
1701
1750
  }
1702
- const payload = EnrollmentBearerPayload.safeParse(JSON.parse(base64UrlDecode(encodedPayload)));
1751
+ let decoded: unknown;
1752
+ try {
1753
+ decoded = JSON.parse(base64UrlDecode(encodedPayload));
1754
+ } catch {
1755
+ return null;
1756
+ }
1757
+ const payload = EnrollmentBearerPayload.safeParse(decoded);
1703
1758
  if (!payload.success || payload.data.exp < nowSeconds) {
1704
1759
  return null;
1705
1760
  }
@@ -2506,13 +2561,6 @@ export type RunCredentialAuthNeeded = {
2506
2561
  message?: string;
2507
2562
  };
2508
2563
 
2509
- export type RunCredentialRedaction = {
2510
- /** Bounded diagnostic label used only in the replacement marker. */
2511
- name: string;
2512
- /** One atomic secret value that must be removed from streamed/audit output. */
2513
- value: string;
2514
- };
2515
-
2516
2564
  export type RunCredentialsRequest = {
2517
2565
  accountId: string;
2518
2566
  workspaceId: string;
@@ -2558,12 +2606,6 @@ export type RunCredentialsResolution =
2558
2606
  files?: RunCredentialFile[];
2559
2607
  /** Environment name to one returned relative file path. */
2560
2608
  fileEnvironment?: Record<string, string>;
2561
- /**
2562
- * Atomic sensitive values embedded inside credential files or derived
2563
- * material. Environment values are registered automatically; hosts list
2564
- * additional file-contained values here so chunked output is redacted.
2565
- */
2566
- redactions?: RunCredentialRedaction[];
2567
2609
  /** Earliest material expiry. Null/omitted uses a bounded refresh cadence. */
2568
2610
  expiresAt?: string | null;
2569
2611
  /** Partial degradation: usable material may coexist with reconnect notices. */
@@ -2955,6 +2997,93 @@ export function gitCredentialBindingIdForRepository(
2955
2997
  );
2956
2998
  }
2957
2999
 
3000
+ type GitRemotePathSemantics = "dot_git_alias" | "exact";
3001
+
3002
+ /**
3003
+ * Provider-declared remote-path behavior. Keeping this exhaustive makes a new
3004
+ * provider choose its semantics instead of inheriting GitHub conventions.
3005
+ */
3006
+ const GIT_REMOTE_PATH_SEMANTICS = {
3007
+ github: "dot_git_alias",
3008
+ gitlab: "dot_git_alias",
3009
+ azure_devops: "exact",
3010
+ } as const satisfies Record<GitCredentialProvider, GitRemotePathSemantics>;
3011
+
3012
+ function gitRemotePathSemantics(
3013
+ provider: GitCredentialProvider | null | undefined,
3014
+ ): GitRemotePathSemantics {
3015
+ return provider ? GIT_REMOTE_PATH_SEMANTICS[provider] : "exact";
3016
+ }
3017
+
3018
+ export class RepositoryUriError extends Error {
3019
+ constructor(message: string) {
3020
+ super(message);
3021
+ this.name = "RepositoryUriError";
3022
+ }
3023
+ }
3024
+
3025
+ /**
3026
+ * Normalize only the safe, provider-neutral parts of an HTTPS clone URI.
3027
+ *
3028
+ * The provider-defined path is opaque: this helper never adds or removes a
3029
+ * `.git` suffix. Embedded user info, query parameters, and fragments are not
3030
+ * durable resource identity and are omitted, matching the existing secret-free
3031
+ * resource contract.
3032
+ */
3033
+ export function normalizeRepositoryTransportUri(uri: string): string {
3034
+ let url: URL;
3035
+ try {
3036
+ url = new URL(uri.trim());
3037
+ } catch {
3038
+ throw new RepositoryUriError(`invalid repository URI: ${uri}`);
3039
+ }
3040
+ if (url.protocol !== "https:" || !url.hostname) {
3041
+ throw new RepositoryUriError("repository resources must use HTTPS Git URLs");
3042
+ }
3043
+ const path = url.pathname.replace(/^\/+|\/+$/g, "");
3044
+ if (path.split("/").filter(Boolean).length < 2) {
3045
+ throw new RepositoryUriError("repository URL must include owner and repo");
3046
+ }
3047
+ return `https://${url.host.toLowerCase()}/${path}`;
3048
+ }
3049
+
3050
+ /**
3051
+ * Return every URI spelling that a provider explicitly declares equivalent.
3052
+ * Exact-path and unqualified providers return only the normalized input URI.
3053
+ */
3054
+ export function gitRemoteUriAliases(
3055
+ uri: string,
3056
+ provider: GitCredentialProvider | null | undefined,
3057
+ ): string[] {
3058
+ const normalizedUri = normalizeRepositoryTransportUri(uri);
3059
+ if (gitRemotePathSemantics(provider) === "exact") {
3060
+ return [normalizedUri];
3061
+ }
3062
+ const base = normalizedUri.replace(/\.git$/, "");
3063
+ return [...new Set([normalizedUri, base, `${base}.git`])];
3064
+ }
3065
+
3066
+ /** Stable remote identity for deduplication and credential-binding ownership. */
3067
+ export function gitRemoteIdentity(
3068
+ uri: string,
3069
+ provider: GitCredentialProvider | null | undefined,
3070
+ ): string {
3071
+ const normalizedUri = normalizeRepositoryTransportUri(uri);
3072
+ return gitRemotePathSemantics(provider) === "dot_git_alias"
3073
+ ? normalizedUri.replace(/\.git$/, "")
3074
+ : normalizedUri;
3075
+ }
3076
+
3077
+ /** Provider-aware Git credential-helper path aliases, without a leading slash. */
3078
+ export function gitRemotePathAliases(
3079
+ uri: string,
3080
+ provider: GitCredentialProvider | null | undefined,
3081
+ ): string[] {
3082
+ return gitRemoteUriAliases(uri, provider).map((alias) =>
3083
+ new URL(alias).pathname.replace(/^\/+|\/+$/g, ""),
3084
+ );
3085
+ }
3086
+
2958
3087
  export const FileResourceRef = z.object({
2959
3088
  kind: z.literal("file"),
2960
3089
  fileId: z.string().uuid(),
@@ -3035,7 +3164,10 @@ export function resourceMountPathCollisionKey(path: string): string {
3035
3164
  * GitLab, Azure DevOps, or a custom host do not collide. Encoding the host keeps
3036
3165
  * IPv6/custom-port identities inside one portable path segment.
3037
3166
  */
3038
- export function defaultRepositoryMountPath(uri: string): string {
3167
+ export function defaultRepositoryMountPath(
3168
+ uri: string,
3169
+ provider?: GitCredentialProvider | null,
3170
+ ): string {
3039
3171
  let url: URL;
3040
3172
  try {
3041
3173
  url = new URL(uri);
@@ -3045,7 +3177,11 @@ export function defaultRepositoryMountPath(uri: string): string {
3045
3177
  if (url.protocol !== "https:" || !url.host) {
3046
3178
  throw new ResourceMountPathError(`invalid repository URI for mount path: ${uri}`);
3047
3179
  }
3048
- const repositoryPath = url.pathname.replace(/^\/+|\/+$/g, "").replace(/\.git$/, "");
3180
+ const remotePath = url.pathname.replace(/^\/+|\/+$/g, "");
3181
+ const repositoryPath =
3182
+ gitRemotePathSemantics(provider) === "dot_git_alias"
3183
+ ? remotePath.replace(/\.git$/, "")
3184
+ : remotePath;
3049
3185
  const segments = repositoryPath.split("/").filter(Boolean);
3050
3186
  if (segments.length < 2) {
3051
3187
  throw new ResourceMountPathError(`repository URI must include owner and repo: ${uri}`);
@@ -3062,7 +3198,7 @@ export function resourceMountPath(resource: ResourceRef): string {
3062
3198
  if (resource.mountPath) return normalizeResourceMountPath(resource.mountPath);
3063
3199
  return resource.kind === "file"
3064
3200
  ? normalizeResourceMountPath(`${DEFAULT_FILE_RESOURCE_MOUNT_ROOT}/${resource.fileId}`)
3065
- : defaultRepositoryMountPath(resource.uri);
3201
+ : defaultRepositoryMountPath(resource.uri, gitCredentialProviderForRepository(resource));
3066
3202
  }
3067
3203
 
3068
3204
  /** Fail before sandbox execution when two resources share a portable path. */
@@ -3753,7 +3889,10 @@ export function resourceIdentityKey(resource: ResourceRef): string {
3753
3889
  if (resource.kind === "file") {
3754
3890
  return `file:${resource.fileId}`;
3755
3891
  }
3756
- return `repository:${resource.uri}`;
3892
+ return `repository:${gitRemoteIdentity(
3893
+ resource.uri,
3894
+ gitCredentialProviderForRepository(resource),
3895
+ )}`;
3757
3896
  }
3758
3897
 
3759
3898
  function sortJson(value: unknown): unknown {
@@ -4310,6 +4449,7 @@ export const SessionAuthorizationOperation = z.enum([
4310
4449
  "session.viewer.read",
4311
4450
  "session.viewer.control",
4312
4451
  "session.first_party_mcp.call",
4452
+ "session.secret.read",
4313
4453
  "session.toolspace.call",
4314
4454
  "session.pin.write",
4315
4455
  "session.codex_account.write",
@@ -5031,9 +5171,9 @@ function withVariableSetIdAlias<T extends z.ZodRawShape>(shape: T) {
5031
5171
  }, z.object(shape));
5032
5172
  }
5033
5173
 
5034
- // Metadata only by design: no schema in this file ever carries a variable value
5035
- // back to a client. Values are write-only and decrypted exclusively inside the
5036
- // worker at sandbox materialization time.
5174
+ // Generic variable-set reads remain metadata-only. Exact plaintext has one
5175
+ // dedicated response schema so callers cannot accidentally widen another
5176
+ // workspace/session response with secret material.
5037
5177
  export const VariableSetVariableMetadata = z.object({
5038
5178
  name: VariableSetVariableName,
5039
5179
  version: z.number().int().positive(),
@@ -5046,6 +5186,14 @@ export const WorkspaceEnvironmentVariableMetadata = VariableSetVariableMetadata;
5046
5186
  /** @deprecated use VariableSetVariableMetadata */
5047
5187
  export type WorkspaceEnvironmentVariableMetadata = VariableSetVariableMetadata;
5048
5188
 
5189
+ export const VariableSetSecret = z.object({
5190
+ variableSetId: z.string().uuid(),
5191
+ name: VariableSetVariableName,
5192
+ version: z.number().int().positive(),
5193
+ value: z.string(),
5194
+ });
5195
+ export type VariableSetSecret = z.infer<typeof VariableSetSecret>;
5196
+
5049
5197
  export const VariableSet = z.object({
5050
5198
  id: z.string().uuid(),
5051
5199
  accountId: z.string().uuid(),
@@ -5247,7 +5395,11 @@ export type ScheduledTaskStatus = z.infer<typeof ScheduledTaskStatus>;
5247
5395
  export const ScheduledTaskRunStatus = z.enum(["queued", "dispatched", "failed"]);
5248
5396
  export type ScheduledTaskRunStatus = z.infer<typeof ScheduledTaskRunStatus>;
5249
5397
 
5250
- export const ScheduledTaskRunMode = z.enum(["new_session_per_run", "reusable_session"]);
5398
+ export const ScheduledTaskRunMode = z.enum([
5399
+ "new_session_per_run",
5400
+ "reusable_session",
5401
+ "existing_session",
5402
+ ]);
5251
5403
  export type ScheduledTaskRunMode = z.infer<typeof ScheduledTaskRunMode>;
5252
5404
 
5253
5405
  export const ScheduledTaskOverlapPolicy = z.enum(["allow_concurrent", "skip", "buffer_one"]);
@@ -5311,10 +5463,14 @@ export const ScheduledTask = z.object({
5311
5463
  runMode: ScheduledTaskRunMode,
5312
5464
  overlapPolicy: ScheduledTaskOverlapPolicy,
5313
5465
  agentConfig: ScheduledTaskAgentConfig,
5314
- createdBy: TurnInitiator.default({ kind: "service", subjectId: "unattributed-legacy" }),
5466
+ createdBy: TurnInitiator.default({
5467
+ kind: "service",
5468
+ subjectId: "unattributed-legacy",
5469
+ }),
5315
5470
  createdByContext: TurnInitiatorContext.default({}),
5316
5471
  personalConnections: z.array(McpPersonalConnectionSummary).default([]),
5317
5472
  reusableSessionId: z.string().uuid().nullable(),
5473
+ targetSessionId: z.string().uuid().nullable().default(null),
5318
5474
  variableSetId: z.string().uuid().nullable().default(null),
5319
5475
  /** @deprecated use variableSetId */
5320
5476
  environmentId: z.string().uuid().nullable().default(null),
@@ -5350,6 +5506,7 @@ export const CreateScheduledTaskRequest = withVariableSetIdAlias({
5350
5506
  schedule: ScheduledTaskScheduleSpec,
5351
5507
  runMode: ScheduledTaskRunMode.default("new_session_per_run"),
5352
5508
  overlapPolicy: ScheduledTaskOverlapPolicy.default("allow_concurrent"),
5509
+ targetSessionId: z.string().uuid().nullable().optional(),
5353
5510
  agentConfig: ScheduledTaskAgentConfig,
5354
5511
  status: ScheduledTaskStatus.default("active"),
5355
5512
  variableSetId: z.string().uuid().nullable().optional(),
@@ -5357,6 +5514,28 @@ export const CreateScheduledTaskRequest = withVariableSetIdAlias({
5357
5514
  // The rig each run binds to (M3); its active version is resolved per fire.
5358
5515
  rigId: z.string().uuid().nullable().optional(),
5359
5516
  metadata: z.record(z.string(), z.unknown()).default({}),
5517
+ }).superRefine((value, context) => {
5518
+ if (value.runMode === "existing_session" && !value.targetSessionId) {
5519
+ context.addIssue({
5520
+ code: "custom",
5521
+ path: ["targetSessionId"],
5522
+ message: "targetSessionId is required when runMode=existing_session",
5523
+ });
5524
+ }
5525
+ if (value.runMode !== "existing_session" && value.targetSessionId) {
5526
+ context.addIssue({
5527
+ code: "custom",
5528
+ path: ["targetSessionId"],
5529
+ message: "targetSessionId requires runMode=existing_session",
5530
+ });
5531
+ }
5532
+ if (value.runMode === "existing_session" && value.agentConfig.goal) {
5533
+ context.addIssue({
5534
+ code: "custom",
5535
+ path: ["agentConfig", "goal"],
5536
+ message: "agentConfig.goal cannot be used with an existing-session target",
5537
+ });
5538
+ }
5360
5539
  });
5361
5540
  export type CreateScheduledTaskRequest = z.infer<typeof CreateScheduledTaskRequest>;
5362
5541
 
@@ -5365,6 +5544,7 @@ export const UpdateScheduledTaskRequest = withVariableSetIdAlias({
5365
5544
  schedule: ScheduledTaskScheduleSpec.optional(),
5366
5545
  runMode: ScheduledTaskRunMode.optional(),
5367
5546
  overlapPolicy: ScheduledTaskOverlapPolicy.optional(),
5547
+ targetSessionId: z.string().uuid().nullable().optional(),
5368
5548
  agentConfig: ScheduledTaskAgentConfig.optional(),
5369
5549
  status: ScheduledTaskStatus.optional(),
5370
5550
  variableSetId: z.string().uuid().nullable().optional(),
@@ -5373,6 +5553,31 @@ export const UpdateScheduledTaskRequest = withVariableSetIdAlias({
5373
5553
  // resolved per fire, so an update takes effect on the next dispatch.
5374
5554
  rigId: z.string().uuid().nullable().optional(),
5375
5555
  metadata: z.record(z.string(), z.unknown()).optional(),
5556
+ }).superRefine((value, context) => {
5557
+ if (value.targetSessionId && value.runMode && value.runMode !== "existing_session") {
5558
+ context.addIssue({
5559
+ code: "custom",
5560
+ path: ["targetSessionId"],
5561
+ message: "targetSessionId requires runMode=existing_session",
5562
+ });
5563
+ }
5564
+ if (value.runMode === "existing_session" && value.targetSessionId === null) {
5565
+ context.addIssue({
5566
+ code: "custom",
5567
+ path: ["targetSessionId"],
5568
+ message: "targetSessionId cannot be null when runMode=existing_session",
5569
+ });
5570
+ }
5571
+ if (
5572
+ value.agentConfig?.goal &&
5573
+ (value.runMode === "existing_session" || Boolean(value.targetSessionId))
5574
+ ) {
5575
+ context.addIssue({
5576
+ code: "custom",
5577
+ path: ["agentConfig", "goal"],
5578
+ message: "agentConfig.goal cannot be used with an existing-session target",
5579
+ });
5580
+ }
5376
5581
  });
5377
5582
  export type UpdateScheduledTaskRequest = z.infer<typeof UpdateScheduledTaskRequest>;
5378
5583
 
@@ -6253,9 +6458,9 @@ export const Session = z.object({
6253
6458
  createIdempotencyKey: z.string().nullable(),
6254
6459
  temporalWorkflowId: z.string().nullable(),
6255
6460
  activeTurnId: z.string().uuid().nullable(),
6256
- // Actual input tokens of the last model call of the most recent turn; the
6257
- // pre-turn portable context-compaction trigger reads it as its budget
6258
- // signal. Null until a turn with usage has completed.
6461
+ // Provider-reported input tokens of the latest authoritative terminal
6462
+ // response. Null after a context transition or whenever that latest response
6463
+ // supplied no usable count, so an older response can never drive compaction.
6259
6464
  lastInputTokens: z.number().int().nonnegative().nullable(),
6260
6465
  queueVersion: z.number().int().nonnegative(),
6261
6466
  queueHeadPosition: z.number().int(),
@@ -6859,8 +7064,8 @@ export const RecordingFailedPayload = z.object({
6859
7064
  recordingId: z.string().uuid(),
6860
7065
  turnId: z.string().uuid().nullable(),
6861
7066
  reason: RecordingFailedReason,
6862
- // ffmpeg-stderr tail / error detail agent/ffmpeg-controlled, so the producer
6863
- // caps + scrubs it before emit (it rides redact() like every payload).
7067
+ // Exact ffmpeg stderr/error detail. Event transport limits must reject or
7068
+ // paginate rather than rewriting this canonical diagnostic.
6864
7069
  detail: z.string().nullable().optional(),
6865
7070
  });
6866
7071
  export type RecordingFailedPayload = z.infer<typeof RecordingFailedPayload>;
@@ -6988,6 +7193,18 @@ export const FsListResponse = z.object({
6988
7193
  });
6989
7194
  export type FsListResponse = z.infer<typeof FsListResponse>;
6990
7195
 
7196
+ /** Several independent directory listings served behind one Channel-A lease.
7197
+ * The response order exactly matches `requests`; callers can paint a root and
7198
+ * hydrate a bounded lazy-tree frontier without repeating provider attach work. */
7199
+ export const FsListBatchRequest = z.object({
7200
+ requests: z.array(FsListRequest).min(1).max(16),
7201
+ });
7202
+ export type FsListBatchRequest = z.infer<typeof FsListBatchRequest>;
7203
+ export const FsListBatchResponse = z.object({
7204
+ results: z.array(FsListResponse),
7205
+ });
7206
+ export type FsListBatchResponse = z.infer<typeof FsListBatchResponse>;
7207
+
6991
7208
  export const FsEncoding = z.enum(["utf8", "base64"]);
6992
7209
  export type FsEncoding = z.infer<typeof FsEncoding>;
6993
7210
  export const FsReadRequest = z.object({
@@ -7156,6 +7373,27 @@ export const GitDiffResponse = z.object({
7156
7373
  });
7157
7374
  export type GitDiffResponse = z.infer<typeof GitDiffResponse>;
7158
7375
 
7376
+ /** One repository read unit: status metadata plus an optional comparison.
7377
+ * Multiple units execute behind one Channel-A lease and preserve input order. */
7378
+ export const GitReadBatchItemRequest = z.object({
7379
+ status: GitStatusRequest,
7380
+ diff: GitDiffRequest.optional(),
7381
+ });
7382
+ export type GitReadBatchItemRequest = z.infer<typeof GitReadBatchItemRequest>;
7383
+ export const GitReadBatchRequest = z.object({
7384
+ requests: z.array(GitReadBatchItemRequest).min(1).max(32),
7385
+ });
7386
+ export type GitReadBatchRequest = z.infer<typeof GitReadBatchRequest>;
7387
+ export const GitReadBatchItemResponse = z.object({
7388
+ status: GitStatusResponse,
7389
+ diff: GitDiffResponse.optional(),
7390
+ });
7391
+ export type GitReadBatchItemResponse = z.infer<typeof GitReadBatchItemResponse>;
7392
+ export const GitReadBatchResponse = z.object({
7393
+ results: z.array(GitReadBatchItemResponse),
7394
+ });
7395
+ export type GitReadBatchResponse = z.infer<typeof GitReadBatchResponse>;
7396
+
7159
7397
  // ─── Workbench v2 turn-end workspace capture ────────────
7160
7398
  // A capture is a point-in-time snapshot of the session workspace's CHANGES,
7161
7399
  // probed live off the box at turn end (detectRepos → gitStatus/gitDiff → fsRead
@@ -7187,10 +7425,10 @@ export const WorkspaceCaptureFile = z.object({
7187
7425
  });
7188
7426
  export type WorkspaceCaptureFile = z.infer<typeof WorkspaceCaptureFile>;
7189
7427
 
7190
- // One repo discovered in the workspace. `diff` is `git diff HEAD` (combined
7191
- // staged+unstaged tracked changes vs HEAD the review diff); `status` is the
7192
- // full porcelain file list (drives the rail glyphs incl. untracked, which the
7193
- // HEAD diff omits). root "" = the workspace root repo.
7428
+ // One repo discovered in the workspace. `diff` is the working/index surface vs
7429
+ // HEAD; `branchDiff`, when available, is the complete current branch vs the
7430
+ // remote default branch and therefore retains committed agent work. `status`
7431
+ // is the full porcelain file list. root "" = the workspace root repo.
7194
7432
  export const WorkspaceCaptureRepo = z.object({
7195
7433
  root: z.string(),
7196
7434
  head: z.string().nullable(),
@@ -7200,6 +7438,7 @@ export const WorkspaceCaptureRepo = z.object({
7200
7438
  behind: z.number().int().nonnegative().default(0),
7201
7439
  status: z.array(GitFileStatus),
7202
7440
  diff: z.array(GitFileDiff),
7441
+ branchDiff: z.array(GitFileDiff).optional(),
7203
7442
  });
7204
7443
  export type WorkspaceCaptureRepo = z.infer<typeof WorkspaceCaptureRepo>;
7205
7444
 
@@ -9263,9 +9502,26 @@ export type ListEnrollmentsResponse = z.infer<typeof ListEnrollmentsResponse>;
9263
9502
 
9264
9503
  export const RevokeEnrollmentResponse = z.object({
9265
9504
  revoked: z.boolean(),
9505
+ outcome: z.enum(["removed", "already_removed", "blocked"]),
9506
+ enrollmentId: z.string().uuid(),
9507
+ machineName: z.string().nullable(),
9508
+ lastSeenAt: z.string().datetime({ offset: true }).nullable(),
9509
+ revokedAt: z.string().datetime({ offset: true }).nullable(),
9510
+ code: z
9511
+ .enum(["active_route", "active_commands", "active_lease", "recovery_pending", "not_selfhosted"])
9512
+ .nullable(),
9513
+ message: z.string(),
9514
+ action: z.string(),
9266
9515
  });
9267
9516
  export type RevokeEnrollmentResponse = z.infer<typeof RevokeEnrollmentResponse>;
9268
9517
 
9518
+ /** POST /v1/workspaces/:workspaceId/enrollments/:id/revoke body. */
9519
+ export const RemoveEnrollmentRequest = z.object({
9520
+ expectedUpdatedAt: z.string().datetime({ offset: true }).optional(),
9521
+ idempotencyKey: z.string().trim().min(1).max(200).optional(),
9522
+ });
9523
+ export type RemoveEnrollmentRequest = z.infer<typeof RemoveEnrollmentRequest>;
9524
+
9269
9525
  // =============================================================================
9270
9526
  // Enrollment UX (self-hosted enrollment UX, design 11): the click-Grant approve
9271
9527
  // page lookup/deny + the headless enroll-token mint/exchange. These sit beside the
@@ -9611,9 +9867,10 @@ export const TurnExecutionReasoningSourceV1 =
9611
9867
  );
9612
9868
  export type TurnExecutionReasoningSourceV1 = z.infer<typeof TurnExecutionReasoningSourceV1>;
9613
9869
 
9614
- export const TurnExecutionLatencyModeSourceV1 = /* @__PURE__ */ defineModelContractSchema(() =>
9615
- z.enum(["explicit", "session", "deployment", "continuation"]),
9616
- );
9870
+ export const TurnExecutionLatencyModeSourceV1 =
9871
+ /* @__PURE__ */ defineModelContractSchema(() =>
9872
+ z.enum(["explicit", "session", "deployment", "continuation"]),
9873
+ );
9617
9874
  export type TurnExecutionLatencyModeSourceV1 = z.infer<typeof TurnExecutionLatencyModeSourceV1>;
9618
9875
 
9619
9876
  /**
@@ -10078,7 +10335,6 @@ export function evaluateWorkspaceModelPolicy(
10078
10335
  }
10079
10336
 
10080
10337
  export * from "./codex-fleet-policy";
10081
- export * from "./secret-redaction";
10082
10338
  export * from "./workspace-instruction-policies";
10083
10339
  export * from "./workspace-state";
10084
10340
  export * from "./preference-registry";