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 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 serverUpdateThreadContinuation: Schema.optionalKey(Schema.Boolean),
156 /** Only explicit false skips Live Activity seeding; older servers may still publish. */
157 agentActivityPublishing: Schema.optionalKey(Schema.Boolean),
158 projectCloneTracking: Schema.optionalKey(Schema.Boolean),
159 /** Server keeps `pinned` and `archived` from project.update. Older servers drop them. */
160 projectOrganization: Schema.optionalKey(Schema.Boolean),
161 /** Server answers projects.listIgnoredFiles. */
162 ignoredFileListing: Schema.optionalKey(Schema.Boolean),
163 environmentIcon: Schema.optionalKey(Schema.Boolean),
164 desktopAppUpdate: Schema.optionalKey(Schema.Boolean),
165 /** The desktop app switches update tracks when a server update request names one. */
166 desktopAppChannelSwitch: Schema.optionalKey(Schema.Boolean),
167 /** Subscribe to command output only when advertised; older servers never send it. */
168 commandOutputStreaming: Schema.optionalKey(Schema.Boolean),
169 /** Hosts headless Chromium tabs streamed over /api/preview-stream for clients without a local runtime. */
170 serverBrowser: Schema.optionalKey(Schema.Boolean),
171 /** Server links to other environments (`PeerLink`) and its agents can work
172 in them. Absent on servers from before linking, which clients must not
173 offer linking for. */
174 peerLinks: Schema.optionalKey(Schema.Boolean),
175 /** Server's `/mcp` narrows an OAuth client's modes from `T3-Mode-Limit`, so
176 a linked environment's calls keep its agent's limits. Another environment
177 links to this one only when it is set. */
178 mcpModeLimitHeader: Schema.optionalKey(Schema.Boolean),
179 /** Work a linked environment starts here stays within its link
180 (`linkOrigin`). Another environment links to this one only when it is
181 set, since without it that work could change the user's own threads. */
182 linkFence: Schema.optionalKey(Schema.Boolean),
183 /** Mints pairing codes that approve one agent sign-in at a set level (`AUTO-…`). */
184 mcpApprovalCodes: Schema.optionalKey(Schema.Boolean),
185 /** Agent sign-ins on its T3 Connect address are approved on the hosted app, without a code. */
186 mcpHostedApproval: Schema.optionalKey(Schema.Boolean),
187 /** Server answers server.prepareTriage. */
188 triage: Schema.optionalKey(Schema.Boolean),
189 /** Server accepts `{ type: "once" }` scheduled tasks; older servers reject them on save. */
190 oneTimeSchedules: Schema.optionalKey(Schema.Boolean),
191 /** Server honors `freshContext` on sends; older servers ignore it and keep the old context. */
192 freshContext: Schema.optionalKey(Schema.Boolean),
193 /** Server saves and runs scheduled task `threadMode`; older servers ignore it. */
194 scheduledTaskThreadModes: Schema.optionalKey(Schema.Boolean),
195 /** Server can transcribe 16 kHz mono PCM audio on the environment machine. */
196 voiceTranscription: Schema.optionalKey(Schema.Boolean),
197 cloudVoiceTranscription: Schema.optionalKey(Schema.Boolean),
198 /**
199 * The server config's flags of the same names. A server that names them here lets a client
200 * load the shell snapshot before the socket opens, and answers its `resumeFrom`.
201 */
202 shellThreadFamilies: Schema.optionalKey(Schema.Boolean),
203 shellOlderSettled: Schema.optionalKey(Schema.Boolean),
204});
205export type ExecutionEnvironmentCapabilities = typeof ExecutionEnvironmentCapabilities.Type;
206
207export const ExecutionEnvironmentDescriptor = Schema.Struct({
208 environmentId: EnvironmentId,
209 label: TrimmedNonEmptyString,
210 platform: ExecutionEnvironmentPlatform,
211 serverVersion: TrimmedNonEmptyString,
212 orchestrationProtocolVersion: Schema.optionalKey(Schema.Int),
213 /** Highest migration this server's database holds; absent on older servers. */
214 databaseMigration: Schema.optionalKey(Schema.Int),
215 capabilities: ExecutionEnvironmentCapabilities,
216});
217export type ExecutionEnvironmentDescriptor = typeof ExecutionEnvironmentDescriptor.Type;
218
219export const RepositoryIdentityLocator = Schema.Struct({
220 source: Schema.Literal("git-remote"),
221 remoteName: TrimmedNonEmptyString,
222 remoteUrl: TrimmedNonEmptyString,
223});
224export type RepositoryIdentityLocator = typeof RepositoryIdentityLocator.Type;
225
226export const RepositoryOrigin = Schema.Struct({
227 canonicalKey: TrimmedNonEmptyString,
228 remoteUrl: Schema.optionalKey(TrimmedNonEmptyString),
229 displayName: Schema.optionalKey(TrimmedNonEmptyString),
230});
231export type RepositoryOrigin = typeof RepositoryOrigin.Type;
232
233export const RepositoryIdentity = Schema.Struct({
234 canonicalKey: TrimmedNonEmptyString,
235 locator: RepositoryIdentityLocator,
236 webUrl: Schema.optionalKey(TrimmedNonEmptyString),
237 /** Work tree root, or the git dir for a bare repository. */
238 rootPath: Schema.optionalKey(TrimmedNonEmptyString),
239 displayName: Schema.optionalKey(TrimmedNonEmptyString),
240 provider: Schema.optionalKey(SourceControlProviderKind),
241 owner: Schema.optionalKey(TrimmedNonEmptyString),
242 name: Schema.optionalKey(TrimmedNonEmptyString),
243 origin: Schema.optionalKey(RepositoryOrigin),
244});
245export type RepositoryIdentity = typeof RepositoryIdentity.Type;
246
247export function repositoryGroupingKeyOf(identity: RepositoryIdentity): string {
248 return identity.origin?.canonicalKey ?? identity.canonicalKey;
249}
250
251export function repositoryGroupingDisplayNameOf(identity: RepositoryIdentity): string | undefined {
252 return identity.origin
253 ? (identity.origin.displayName ?? identity.origin.canonicalKey)
254 : identity.displayName;
255}
256
257export const ScopedProjectRef = Schema.Struct({
258 environmentId: EnvironmentId,
259 projectId: ProjectId,
260});
261export type ScopedProjectRef = typeof ScopedProjectRef.Type;
262
263export const ScopedThreadRef = Schema.Struct({
264 environmentId: EnvironmentId,
265 threadId: ThreadId,
266});
267export type ScopedThreadRef = typeof ScopedThreadRef.Type;