Open History to see the last commit
1import * as Effect from "effect/Effect";
2import * as Schema from "effect/Schema";
3
4import {
5 EnvironmentId,
6 ForwardCompatibleOptional,
7 ProjectId,
8 ThreadId,
9 TrimmedNonEmptyString,
10} from "./baseSchemas.ts";
11import { SourceControlProviderKind } from "./sourceControl.ts";
12
13export const ORCHESTRATION_PROTOCOL_VERSION = 2;
14export const ORCHESTRATION_PROTOCOL_VERSION_TEXT = "2";
15export const ORCHESTRATION_PROTOCOL_QUERY_PARAM = "orchestrationProtocol";
16export const ORCHESTRATION_PROTOCOL_HEADER = "x-t3-orchestration-protocol";
17
18/** Wire shapes a client understands, comma-separated; a client that omits one gets the older shape. */
19export const CLIENT_CAPABILITIES_QUERY_PARAM = "clientCapabilities";
20/** Secret fields may hold a secret provider reference instead of a string. */
21export const SETTINGS_SECRET_REFERENCES_CAPABILITY = "settingsSecretReferences";
22
23export const ExecutionEnvironmentPlatformOs = Schema.Literals([
24 "darwin",
25 "linux",
26 "windows",
27 "unknown",
28]);
29export type ExecutionEnvironmentPlatformOs = typeof ExecutionEnvironmentPlatformOs.Type;
30
31export const ExecutionEnvironmentPlatformArch = Schema.Literals(["arm64", "x64", "other"]);
32export type ExecutionEnvironmentPlatformArch = typeof ExecutionEnvironmentPlatformArch.Type;
33
34export const ENVIRONMENT_MACHINE_KINDS = [
35 "server",
36 "cloud",
37 "linux",
38 "desktop",
39 "laptop",
40 "mac-mini",
41 "mac-studio",
42] as const;
43export const EnvironmentMachineKind = Schema.Literals(ENVIRONMENT_MACHINE_KINDS);
44export type EnvironmentMachineKind = typeof EnvironmentMachineKind.Type;
45export const isEnvironmentMachineKind = Schema.is(EnvironmentMachineKind);
46
47export const ExecutionEnvironmentPlatform = Schema.Struct({
48 os: ExecutionEnvironmentPlatformOs,
49 arch: ExecutionEnvironmentPlatformArch,
50 machine: ForwardCompatibleOptional(EnvironmentMachineKind),
51});
52
53// Kept outside settings.ts to avoid an orchestration import cycle.
54export const ThreadEnvMode = Schema.Literals(["local", "worktree"]);
55export type ThreadEnvMode = typeof ThreadEnvMode.Type;
56
57export const WorktreeSubmodules = Schema.Literals(["recursive", "top-level", "none"]);
58export type WorktreeSubmodules = typeof WorktreeSubmodules.Type;
59export type ExecutionEnvironmentPlatform = typeof ExecutionEnvironmentPlatform.Type;
60
61export const ServerSelfUpdateMethod = Schema.Literals(["boot-service", "respawn", "desktop-app"]);
62export type ServerSelfUpdateMethod = typeof ServerSelfUpdateMethod.Type;
63
64// Not trimmed: a path that differs from the installed one would update the wrong home.
65const InstalledPath = Schema.String.check(Schema.isNonEmpty());
66
67export const ServerInstallation = Schema.Union([
68 Schema.Struct({ kind: Schema.Literals(["npx", "pnpm-dlx", "bunx"]) }),
69 Schema.Struct({ kind: Schema.Literal("npm-global"), prefix: TrimmedNonEmptyString }),
70 // A launcher (script, or a legacy symlink) into a downloaded runtime, as the install script and `t3 update` leave.
71 Schema.Struct({
72 kind: Schema.Literal("t3-launcher"),
73 launcher: InstalledPath,
74 baseDir: InstalledPath,
75 }),
76]);
77export type ServerInstallation = typeof ServerInstallation.Type;
78
79export const ServerSelfUpdateCapability = Schema.Literals([
80 "boot-service",
81 "respawn",
82 "desktop-managed",
83]);
84export type ServerSelfUpdateCapability = typeof ServerSelfUpdateCapability.Type;
85
86export const ExecutionEnvironmentCapabilities = Schema.Struct({
87 repositoryIdentity: Schema.Boolean.pipe(Schema.withDecodingDefault(Effect.succeed(false))),
88 connectionProbe: Schema.optionalKey(Schema.Boolean),
89 attachmentUploads: Schema.optionalKey(Schema.Boolean),
90 questionAttachments: Schema.optionalKey(Schema.Boolean),
91 fileAttachments: Schema.optionalKey(
92 Schema.Struct({
93 maxUploadBytes: Schema.Int.check(Schema.isGreaterThanOrEqualTo(1)),
94 }),
95 ),
96 pullRequests: Schema.optionalKey(Schema.Boolean),
97 pullRequestChecks: Schema.optionalKey(Schema.Boolean),
98 /** Side chats: hidden `lineage.sideChat` forks. Older servers ignore the fork flag. */
99 sideChats: Schema.optionalKey(Schema.Boolean),
100 vcsBranchStatus: Schema.optionalKey(Schema.Boolean),
101 /** Every protocol-2 server sends this; older mobile builds still read it. */
102 inlineMessageContext: Schema.optionalKey(Schema.Boolean),
103 requiredWorktreeBootstrap: Schema.optionalKey(Schema.Boolean),
104 threadSettlement: Schema.optionalKey(Schema.Boolean),
105 threadAutoSettlement: Schema.optionalKey(Schema.Boolean),
106 storageCleanup: Schema.optionalKey(Schema.Boolean),
107 storageCleanupRun: Schema.optionalKey(Schema.Boolean),
108 databaseStorage: Schema.optionalKey(Schema.Boolean),
109 otlpEndpointCheck: Schema.optionalKey(Schema.Boolean),
110 /** Serves `server.checkSecretSource`; hide the secret check where it is absent. */
111 secretSourceCheck: Schema.optionalKey(Schema.Boolean),
112 projectWorktreeCleanup: Schema.optionalKey(Schema.Boolean),
113 settledWorktreeCleanup: Schema.optionalKey(Schema.Boolean),
114 worktreesDirectory: Schema.optionalKey(Schema.Boolean),
115 /** Sent by macOS servers, which honor the fastWorktreeCheckout setting. */
116 fastWorktreeCheckout: Schema.optionalKey(Schema.Boolean),
117 threadAutoSettlementScope: Schema.optionalKey(Schema.Boolean),
118 threadRestartContinuation: Schema.optionalKey(Schema.Boolean),
119 projectSettingsOverrides: Schema.optionalKey(Schema.Boolean),
120 /** Hide the switch when absent; older servers silently drop per-project provider patches. */
121 projectProviderInstanceOverrides: Schema.optionalKey(Schema.Boolean),
122 threadSnooze: Schema.optionalKey(Schema.Boolean),
123 /** Serves `orchestration.forceStopThread`; hide Force stop where it is absent. */
124 threadForceStop: Schema.optionalKey(Schema.Boolean),
125 /** Every protocol-2 server sends this; older mobile builds still read it. */
126 environmentThemes: Schema.optionalKey(Schema.Boolean),
127 usageLimitSources: Schema.optionalKey(Schema.Boolean),
128 /** Honors `workspaceSnapshotsOnDemand`; older servers send every snapshot and drop them in the stream. */
129 providerWorkspaceSnapshotsOnDemand: Schema.optionalKey(Schema.Boolean),
130 /** Honors `modelCatalogOnly`; older servers drop the field and probe every provider. */
131 modelCatalogRefresh: Schema.optionalKey(Schema.Boolean),
132 usagePriceOverrides: Schema.optionalKey(Schema.Boolean),
133 usageModelAliases: Schema.optionalKey(Schema.Boolean),
134 threadPinning: Schema.optionalKey(Schema.Boolean),
135 threadPinReorder: Schema.optionalKey(Schema.Boolean),
136 threadActiveReorder: Schema.optionalKey(Schema.Boolean),
137 threadAutoSettleOptOut: Schema.optionalKey(Schema.Boolean),
138 threadProjectFromScratch: Schema.optionalKey(Schema.Boolean),
139 threadTitleRegeneration: Schema.optionalKey(Schema.Boolean),
140 threadVisitedTracking: Schema.optionalKey(Schema.Boolean),
141 threadPullRequestLinking: Schema.optionalKey(Schema.Boolean),
142 serverResolvedCommandContext: Schema.optionalKey(Schema.Boolean),
143 threadStop: Schema.optionalKey(Schema.Boolean),
144 /** Absent on older servers and on Windows hosts, which cannot stop a discovered server. */
145 localServerStop: Schema.optionalKey(Schema.Boolean),
146 threadPullRequests: Schema.optionalKey(Schema.Boolean),
147 threadPullRequestWatch: Schema.optionalKey(Schema.Boolean),
148 pullRequestStackActions: Schema.optionalKey(Schema.Boolean),
149 serverSelfUpdate: Schema.optionalKey(ServerSelfUpdateCapability),
150 /** Server answers server.getNpmRelease using its configured npm registry. */
151 npmReleaseLookup: Schema.optionalKey(Schema.Boolean),
152 /** Manual commands must update this install, not the host's default global prefix. */
153 serverInstallation: ForwardCompatibleOptional(ServerInstallation),
154 serverSelfUpdateProgress: Schema.optionalKey(Schema.Boolean),
155 serverUpdateStaging: Schema.optionalKey(Schema.Boolean),
156 serverUpdateThreadContinuation: Schema.optionalKey(Schema.Boolean),
157 /** Only explicit false skips Live Activity seeding; older servers may still publish. */
158 agentActivityPublishing: Schema.optionalKey(Schema.Boolean),
159 projectCloneTracking: Schema.optionalKey(Schema.Boolean),
160 /** Server keeps `pinned` and `archived` from project.update. Older servers drop them. */
161 projectOrganization: Schema.optionalKey(Schema.Boolean),
162 /** Server answers projects.listIgnoredFiles. */
163 ignoredFileListing: Schema.optionalKey(Schema.Boolean),
164 /** projects.listIgnoredFiles honors `query`. Older servers ignore it. */
165 ignoredFileSearch: Schema.optionalKey(Schema.Boolean),
166 environmentIcon: Schema.optionalKey(Schema.Boolean),
167 desktopAppUpdate: Schema.optionalKey(Schema.Boolean),
168 /** The desktop app switches update tracks when a server update request names one. */
169 desktopAppChannelSwitch: Schema.optionalKey(Schema.Boolean),
170 /** Subscribe to command output only when advertised; older servers never send it. */
171 commandOutputStreaming: Schema.optionalKey(Schema.Boolean),
172 /** Hosts headless Chromium tabs streamed over /api/preview-stream for clients without a local runtime. */
173 serverBrowser: Schema.optionalKey(Schema.Boolean),
174 /** Server links to other environments (`PeerLink`) and its agents can work
175 in them. Absent on servers from before linking, which clients must not
176 offer linking for. */
177 peerLinks: Schema.optionalKey(Schema.Boolean),
178 /** Server's `/mcp` narrows an OAuth client's modes from `T3-Mode-Limit`, so
179 a linked environment's calls keep its agent's limits. Another environment
180 links to this one only when it is set. */
181 mcpModeLimitHeader: Schema.optionalKey(Schema.Boolean),
182 /** Work a linked environment starts here stays within its link
183 (`linkOrigin`). Another environment links to this one only when it is
184 set, since without it that work could change the user's own threads. */
185 linkFence: Schema.optionalKey(Schema.Boolean),
186 /** Mints pairing codes that approve one agent sign-in at a set level (`AUTO-…`). */
187 mcpApprovalCodes: Schema.optionalKey(Schema.Boolean),
188 /** Agent sign-ins on its T3 Connect address are approved on the hosted app, without a code. */
189 mcpHostedApproval: Schema.optionalKey(Schema.Boolean),
190 /** Server answers server.prepareTriage. */
191 triage: Schema.optionalKey(Schema.Boolean),
192 /** Server accepts `{ type: "once" }` scheduled tasks; older servers reject them on save. */
193 oneTimeSchedules: Schema.optionalKey(Schema.Boolean),
194 /** Server honors `freshContext` on sends; older servers ignore it and keep the old context. */
195 freshContext: Schema.optionalKey(Schema.Boolean),
196 /** Server saves and runs scheduled task `threadMode`; older servers ignore it. */
197 scheduledTaskThreadModes: Schema.optionalKey(Schema.Boolean),
198 /** Server can transcribe 16 kHz mono PCM audio on the environment machine. */
199 voiceTranscription: Schema.optionalKey(Schema.Boolean),
200 cloudVoiceTranscription: Schema.optionalKey(Schema.Boolean),
201 /**
202 * The server config's flags of the same names. A server that names them here lets a client
203 * load the shell snapshot before the socket opens, and answers its `resumeFrom`.
204 */
205 shellThreadFamilies: Schema.optionalKey(Schema.Boolean),
206 shellOlderSettled: Schema.optionalKey(Schema.Boolean),
207 /** Publishes a pull request refresh when linked-thread answers change; older servers need polling. */
208 linkedThreadRefreshes: Schema.optionalKey(Schema.Boolean),
209});
210export type ExecutionEnvironmentCapabilities = typeof ExecutionEnvironmentCapabilities.Type;
211
212export const ExecutionEnvironmentDescriptor = Schema.Struct({
213 environmentId: EnvironmentId,
214 label: TrimmedNonEmptyString,
215 platform: ExecutionEnvironmentPlatform,
216 serverVersion: TrimmedNonEmptyString,
217 orchestrationProtocolVersion: Schema.optionalKey(Schema.Int),
218 /** Highest migration this server's database holds; absent on older servers. */
219 databaseMigration: Schema.optionalKey(Schema.Int),
220 capabilities: ExecutionEnvironmentCapabilities,
221});
222export type ExecutionEnvironmentDescriptor = typeof ExecutionEnvironmentDescriptor.Type;
223
224export const RepositoryIdentityLocator = Schema.Struct({
225 source: Schema.Literal("git-remote"),
226 remoteName: TrimmedNonEmptyString,
227 remoteUrl: TrimmedNonEmptyString,
228});
229export type RepositoryIdentityLocator = typeof RepositoryIdentityLocator.Type;
230
231export const RepositoryOrigin = Schema.Struct({
232 canonicalKey: TrimmedNonEmptyString,
233 remoteUrl: Schema.optionalKey(TrimmedNonEmptyString),
234 displayName: Schema.optionalKey(TrimmedNonEmptyString),
235});
236export type RepositoryOrigin = typeof RepositoryOrigin.Type;
237
238export const RepositoryIdentity = Schema.Struct({
239 canonicalKey: TrimmedNonEmptyString,
240 locator: RepositoryIdentityLocator,
241 webUrl: Schema.optionalKey(TrimmedNonEmptyString),
242 /** Work tree root, or the git dir for a bare repository. */
243 rootPath: Schema.optionalKey(TrimmedNonEmptyString),
244 displayName: Schema.optionalKey(TrimmedNonEmptyString),
245 provider: Schema.optionalKey(SourceControlProviderKind),
246 owner: Schema.optionalKey(TrimmedNonEmptyString),
247 name: Schema.optionalKey(TrimmedNonEmptyString),
248 origin: Schema.optionalKey(RepositoryOrigin),
249});
250export type RepositoryIdentity = typeof RepositoryIdentity.Type;
251
252export function repositoryGroupingKeyOf(identity: RepositoryIdentity): string {
253 return identity.origin?.canonicalKey ?? identity.canonicalKey;
254}
255
256export function repositoryGroupingDisplayNameOf(identity: RepositoryIdentity): string | undefined {
257 return identity.origin
258 ? (identity.origin.displayName ?? identity.origin.canonicalKey)
259 : identity.displayName;
260}
261
262export const ScopedProjectRef = Schema.Struct({
263 environmentId: EnvironmentId,
264 projectId: ProjectId,
265});
266export type ScopedProjectRef = typeof ScopedProjectRef.Type;
267
268export const ScopedThreadRef = Schema.Struct({
269 environmentId: EnvironmentId,
270 threadId: ThreadId,
271});
272export type ScopedThreadRef = typeof ScopedThreadRef.Type;