@spencer-kit/coder-studio 0.4.7 → 0.4.8

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.
@@ -625,7 +625,15 @@ var init_definition = __esm({
625
625
  id: "claude",
626
626
  displayName: "Claude Code",
627
627
  badge: "Claude",
628
+ kind: "built_in",
628
629
  capability: "full",
630
+ capabilities: [
631
+ { key: "interactive_session", supported: true, label: "Interactive session" },
632
+ { key: "supervisor_eval", supported: true, label: "Supervisor evaluation" },
633
+ { key: "idle_detection", supported: true, label: "Idle detection" },
634
+ { key: "context_attach", supported: false, label: "Context attach" },
635
+ { key: "review", supported: false, label: "Review" }
636
+ ],
629
637
  install: claudeInstallMetadata,
630
638
  // ===== Command construction =====
631
639
  buildCommand(config, ctx) {
@@ -795,7 +803,15 @@ var init_definition2 = __esm({
795
803
  id: "codex",
796
804
  displayName: "Codex",
797
805
  badge: "Codex",
806
+ kind: "built_in",
798
807
  capability: "full",
808
+ capabilities: [
809
+ { key: "interactive_session", supported: true, label: "Interactive session" },
810
+ { key: "supervisor_eval", supported: true, label: "Supervisor evaluation" },
811
+ { key: "idle_detection", supported: true, label: "Idle detection" },
812
+ { key: "context_attach", supported: false, label: "Context attach" },
813
+ { key: "review", supported: false, label: "Review" }
814
+ ],
799
815
  install: codexInstallMetadata,
800
816
  // ===== Command construction =====
801
817
  buildCommand(config, ctx) {
@@ -828,7 +844,78 @@ var init_definition2 = __esm({
828
844
  }
829
845
  });
830
846
 
847
+ // packages/providers/src/presets.ts
848
+ function cloneCapabilities(capabilities) {
849
+ return capabilities.map((capability) => ({ ...capability }));
850
+ }
851
+ var defaultCapabilities, providerPresets;
852
+ var init_presets = __esm({
853
+ "packages/providers/src/presets.ts"() {
854
+ "use strict";
855
+ defaultCapabilities = [
856
+ { key: "interactive_session", supported: true, label: "Interactive session" },
857
+ { key: "context_attach", supported: true, label: "Context attach" },
858
+ { key: "review", supported: true, label: "Review" }
859
+ ];
860
+ providerPresets = [
861
+ {
862
+ id: "gemini-cli",
863
+ displayName: "Gemini CLI",
864
+ kind: "preset",
865
+ description: "Preset metadata for launching Google's Gemini CLI through the custom provider flow.",
866
+ command: "gemini",
867
+ args: [],
868
+ env: {},
869
+ cwdMode: "workspace_root",
870
+ sessionMode: "interactive",
871
+ startupPrompt: "Follow the workspace instructions and explain important tradeoffs.",
872
+ capabilities: cloneCapabilities(defaultCapabilities),
873
+ requiredCommands: ["gemini"]
874
+ },
875
+ {
876
+ id: "aider",
877
+ displayName: "Aider",
878
+ kind: "preset",
879
+ description: "Preset metadata for launching Aider as a workspace-root interactive coding agent.",
880
+ command: "aider",
881
+ args: [],
882
+ env: {},
883
+ cwdMode: "workspace_root",
884
+ sessionMode: "interactive",
885
+ startupPrompt: "Review the current workspace state before making edits.",
886
+ capabilities: cloneCapabilities(defaultCapabilities),
887
+ requiredCommands: ["aider"]
888
+ },
889
+ {
890
+ id: "opencode",
891
+ displayName: "OpenCode",
892
+ kind: "preset",
893
+ description: "Preset metadata for launching OpenCode from the workspace root.",
894
+ command: "opencode",
895
+ args: [],
896
+ env: {},
897
+ cwdMode: "workspace_root",
898
+ sessionMode: "interactive",
899
+ startupPrompt: "Use the repository instructions and verify changes before finishing.",
900
+ capabilities: cloneCapabilities(defaultCapabilities),
901
+ requiredCommands: ["opencode"]
902
+ }
903
+ ];
904
+ }
905
+ });
906
+
831
907
  // packages/providers/src/registry.ts
908
+ function toProviderListItem(provider) {
909
+ return {
910
+ id: provider.id,
911
+ displayName: provider.displayName,
912
+ badge: provider.badge,
913
+ kind: provider.kind,
914
+ capability: provider.capability,
915
+ capabilities: provider.capabilities.map((capability) => ({ ...capability })),
916
+ requiredCommands: [...provider.requiredCommands]
917
+ };
918
+ }
832
919
  var providerRegistry;
833
920
  var init_registry = __esm({
834
921
  "packages/providers/src/registry.ts"() {
@@ -848,6 +935,7 @@ var init_src = __esm({
848
935
  init_config_schema2();
849
936
  init_definition2();
850
937
  init_stdout_heuristics();
938
+ init_presets();
851
939
  init_registry();
852
940
  }
853
941
  });
@@ -1690,7 +1778,7 @@ import { mkdir as mkdir3, mkdtemp, rm as rm3, writeFile as writeFile2 } from "fs
1690
1778
  import os2 from "os";
1691
1779
  import path7 from "path";
1692
1780
  async function runGit(cwd, args, options = {}) {
1693
- return new Promise((resolve6, reject) => {
1781
+ return new Promise((resolve8, reject) => {
1694
1782
  const gitArgs = [
1695
1783
  ...options.config?.flatMap(([key, value]) => ["-c", `${key}=${value}`]) ?? [],
1696
1784
  ...args
@@ -1717,7 +1805,7 @@ async function runGit(cwd, args, options = {}) {
1717
1805
  if (err) {
1718
1806
  reject(new GitError(err.message, stderr));
1719
1807
  } else {
1720
- resolve6({ stdout, stderr });
1808
+ resolve8({ stdout, stderr });
1721
1809
  }
1722
1810
  }
1723
1811
  );
@@ -3598,12 +3686,12 @@ var init_auto_fetch = __esm({
3598
3686
  }
3599
3687
  acquireWorkspaceOperation(workspaceId) {
3600
3688
  const state = this.getOrCreateState(workspaceId);
3601
- return new Promise((resolve6) => {
3689
+ return new Promise((resolve8) => {
3602
3690
  const grant = () => {
3603
3691
  state.inFlight = true;
3604
3692
  state.nextFetchAt = void 0;
3605
3693
  let released = false;
3606
- resolve6(() => {
3694
+ resolve8(() => {
3607
3695
  if (released) {
3608
3696
  return;
3609
3697
  }
@@ -4606,7 +4694,7 @@ var init_manager = __esm({
4606
4694
  // packages/server/src/provider-runtime/command-runner.ts
4607
4695
  import { spawn as spawn2 } from "node:child_process";
4608
4696
  async function runCommandAsString(file, args, options) {
4609
- return new Promise((resolve6, reject) => {
4697
+ return new Promise((resolve8, reject) => {
4610
4698
  const child = spawn2(file, args, {
4611
4699
  cwd: options?.cwd,
4612
4700
  env: options?.env,
@@ -4633,7 +4721,7 @@ async function runCommandAsString(file, args, options) {
4633
4721
  const stdout = Buffer.concat(stdoutChunks).toString("utf8");
4634
4722
  const stderr = Buffer.concat(stderrChunks).toString("utf8");
4635
4723
  if (code === 0) {
4636
- resolve6({ stdout, stderr });
4724
+ resolve8({ stdout, stderr });
4637
4725
  return;
4638
4726
  }
4639
4727
  reject(
@@ -5433,6 +5521,66 @@ var init_tool_root = __esm({
5433
5521
  }
5434
5522
  });
5435
5523
 
5524
+ // packages/server/src/provider-runtime/custom-provider.ts
5525
+ import { z as z4 } from "zod";
5526
+ function deriveProviderCapability(capabilities) {
5527
+ const interactive = capabilities.find((capability) => capability.key === "interactive_session");
5528
+ if (!interactive?.supported) {
5529
+ return "unsupported";
5530
+ }
5531
+ const allSupported = capabilities.length > 0 && capabilities.every((capability) => capability.supported);
5532
+ return allSupported ? "full" : "limited";
5533
+ }
5534
+ function buildCustomProviderDefinition(config) {
5535
+ const command = config.command.trim();
5536
+ const requiredCommand = command.split(/\s+/)[0] ?? command;
5537
+ return {
5538
+ id: config.id,
5539
+ displayName: config.displayName,
5540
+ badge: "Custom",
5541
+ kind: "custom",
5542
+ capability: deriveProviderCapability(config.capabilities),
5543
+ capabilities: config.capabilities.map((capability) => ({ ...capability })),
5544
+ install: {
5545
+ prerequisites: [],
5546
+ manualGuideKeys: [],
5547
+ docUrls: {
5548
+ provider: "",
5549
+ prerequisites: {}
5550
+ },
5551
+ strategies: {}
5552
+ },
5553
+ buildCommand(_providerConfig, ctx) {
5554
+ return {
5555
+ argv: [command, ...config.args],
5556
+ env: {
5557
+ ...config.env,
5558
+ CODER_STUDIO_SESSION_ID: ctx.sessionId
5559
+ },
5560
+ cwd: ctx.workspacePath
5561
+ };
5562
+ },
5563
+ configSchema: CUSTOM_PROVIDER_CONFIG_SCHEMA,
5564
+ defaultConfig: {},
5565
+ requiredCommands: requiredCommand ? [requiredCommand] : []
5566
+ };
5567
+ }
5568
+ function upsertProviderDefinition(registry, provider) {
5569
+ const next = registry.filter((item) => item.id !== provider.id);
5570
+ next.push(provider);
5571
+ return next;
5572
+ }
5573
+ function removeProviderDefinition(registry, providerId) {
5574
+ return registry.filter((item) => item.id !== providerId);
5575
+ }
5576
+ var CUSTOM_PROVIDER_CONFIG_SCHEMA;
5577
+ var init_custom_provider = __esm({
5578
+ "packages/server/src/provider-runtime/custom-provider.ts"() {
5579
+ "use strict";
5580
+ CUSTOM_PROVIDER_CONFIG_SCHEMA = z4.object({}).passthrough();
5581
+ }
5582
+ });
5583
+
5436
5584
  // packages/server/src/provider-runtime/e2e-provider-mock.ts
5437
5585
  import { chmodSync, existsSync as existsSync5, mkdirSync as mkdirSync6, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "node:fs";
5438
5586
  import { dirname as dirname5, join as join8 } from "node:path";
@@ -5675,9 +5823,24 @@ var init_install_manager2 = __esm({
5675
5823
  deps;
5676
5824
  constructor(providers, deps = {}) {
5677
5825
  this.deps = deps;
5826
+ this.setProviders(providers);
5827
+ }
5828
+ setProviders(providers) {
5829
+ const nextIds = new Set(providers.map((provider) => provider.id));
5830
+ this.providers.clear();
5678
5831
  for (const provider of providers) {
5679
5832
  this.providers.set(provider.id, provider);
5680
5833
  }
5834
+ for (const providerId of this.activeJobIdsByProviderId.keys()) {
5835
+ if (!nextIds.has(providerId)) {
5836
+ this.activeJobIdsByProviderId.delete(providerId);
5837
+ }
5838
+ }
5839
+ for (const providerId of this.inFlightStartsByProviderId.keys()) {
5840
+ if (!nextIds.has(providerId)) {
5841
+ this.inFlightStartsByProviderId.delete(providerId);
5842
+ }
5843
+ }
5681
5844
  }
5682
5845
  async start(providerId) {
5683
5846
  const activeJob = this.getActiveJob(providerId);
@@ -6147,7 +6310,7 @@ var init_update = __esm({
6147
6310
  });
6148
6311
 
6149
6312
  // packages/core/src/protocol/messages.ts
6150
- import { z as z4 } from "zod";
6313
+ import { z as z5 } from "zod";
6151
6314
  var TERMINAL_BINARY_PROTOCOL_VERSION, TERMINAL_BINARY_HEADER_SIZE, TerminalBinaryFrameType, RECOVERY_REASONS, TERMINAL_INPUT_ACTIVITIES, encodeTerminalBinaryFrame, TERMINAL_BINARY_OUTPUT_VERSION, encodeTerminalOutputFrame, CommandMessage, ResultMessage, EventMessage, SubscribeMessage, UnsubscribeMessage, ResyncMessage, ClientMessage, ServerMessage;
6152
6315
  var init_messages = __esm({
6153
6316
  "packages/core/src/protocol/messages.ts"() {
@@ -6210,49 +6373,49 @@ var init_messages = __esm({
6210
6373
  frame.set(payload, TERMINAL_BINARY_HEADER_SIZE + topicBytes.length);
6211
6374
  return frame;
6212
6375
  };
6213
- CommandMessage = z4.object({
6214
- kind: z4.literal("command"),
6215
- id: z4.string().uuid(),
6216
- op: z4.string(),
6217
- args: z4.unknown()
6376
+ CommandMessage = z5.object({
6377
+ kind: z5.literal("command"),
6378
+ id: z5.string().uuid(),
6379
+ op: z5.string(),
6380
+ args: z5.unknown()
6218
6381
  });
6219
- ResultMessage = z4.object({
6220
- kind: z4.literal("result"),
6221
- id: z4.string().uuid(),
6222
- ok: z4.boolean(),
6223
- data: z4.unknown().optional(),
6224
- error: z4.object({
6225
- code: z4.string(),
6226
- message: z4.string(),
6227
- details: z4.unknown().optional()
6382
+ ResultMessage = z5.object({
6383
+ kind: z5.literal("result"),
6384
+ id: z5.string().uuid(),
6385
+ ok: z5.boolean(),
6386
+ data: z5.unknown().optional(),
6387
+ error: z5.object({
6388
+ code: z5.string(),
6389
+ message: z5.string(),
6390
+ details: z5.unknown().optional()
6228
6391
  }).optional()
6229
6392
  });
6230
- EventMessage = z4.object({
6231
- kind: z4.literal("event"),
6232
- topic: z4.string(),
6233
- seq: z4.number().int().nonnegative(),
6234
- timestamp: z4.number().int().positive(),
6235
- data: z4.unknown()
6393
+ EventMessage = z5.object({
6394
+ kind: z5.literal("event"),
6395
+ topic: z5.string(),
6396
+ seq: z5.number().int().nonnegative(),
6397
+ timestamp: z5.number().int().positive(),
6398
+ data: z5.unknown()
6236
6399
  });
6237
- SubscribeMessage = z4.object({
6238
- kind: z4.literal("subscribe"),
6239
- topics: z4.array(z4.string())
6400
+ SubscribeMessage = z5.object({
6401
+ kind: z5.literal("subscribe"),
6402
+ topics: z5.array(z5.string())
6240
6403
  });
6241
- UnsubscribeMessage = z4.object({
6242
- kind: z4.literal("unsubscribe"),
6243
- topics: z4.array(z4.string())
6404
+ UnsubscribeMessage = z5.object({
6405
+ kind: z5.literal("unsubscribe"),
6406
+ topics: z5.array(z5.string())
6244
6407
  });
6245
- ResyncMessage = z4.object({
6246
- kind: z4.literal("resync"),
6247
- lastSeen: z4.record(z4.string(), z4.number())
6408
+ ResyncMessage = z5.object({
6409
+ kind: z5.literal("resync"),
6410
+ lastSeen: z5.record(z5.string(), z5.number())
6248
6411
  });
6249
- ClientMessage = z4.discriminatedUnion("kind", [
6412
+ ClientMessage = z5.discriminatedUnion("kind", [
6250
6413
  CommandMessage,
6251
6414
  SubscribeMessage,
6252
6415
  UnsubscribeMessage,
6253
6416
  ResyncMessage
6254
6417
  ]);
6255
- ServerMessage = z4.discriminatedUnion("kind", [ResultMessage, EventMessage]);
6418
+ ServerMessage = z5.discriminatedUnion("kind", [ResultMessage, EventMessage]);
6256
6419
  }
6257
6420
  });
6258
6421
 
@@ -6325,7 +6488,7 @@ var init_src3 = __esm({
6325
6488
  });
6326
6489
 
6327
6490
  // packages/server/src/provider-config.ts
6328
- import { z as z5 } from "zod";
6491
+ import { z as z6 } from "zod";
6329
6492
  function isSupportedProviderId(providerId) {
6330
6493
  return supportedProviderIds.has(providerId);
6331
6494
  }
@@ -6345,17 +6508,17 @@ var init_provider_config = __esm({
6345
6508
  "use strict";
6346
6509
  SUPPORTED_PROVIDER_IDS = ["claude", "codex"];
6347
6510
  supportedProviderIds = new Set(SUPPORTED_PROVIDER_IDS);
6348
- ProviderLaunchConfigInputSchema = z5.object({
6349
- additionalArgs: z5.array(z5.string()).optional(),
6350
- envVars: z5.record(z5.string(), z5.string()).optional()
6511
+ ProviderLaunchConfigInputSchema = z6.object({
6512
+ additionalArgs: z6.array(z6.string()).optional(),
6513
+ envVars: z6.record(z6.string(), z6.string()).optional()
6351
6514
  }).strict();
6352
- ProviderSettingsSchema = z5.object({
6515
+ ProviderSettingsSchema = z6.object({
6353
6516
  claude: ProviderLaunchConfigInputSchema.optional(),
6354
6517
  codex: ProviderLaunchConfigInputSchema.optional()
6355
6518
  }).strict();
6356
- ProviderLaunchConfigSchema = z5.object({
6357
- additionalArgs: z5.array(z5.string()).default([]),
6358
- envVars: z5.record(z5.string(), z5.string()).optional()
6519
+ ProviderLaunchConfigSchema = z6.object({
6520
+ additionalArgs: z6.array(z6.string()).default([]),
6521
+ envVars: z6.record(z6.string(), z6.string()).optional()
6359
6522
  });
6360
6523
  }
6361
6524
  });
@@ -6851,6 +7014,9 @@ var init_manager3 = __esm({
6851
7014
  comparators = /* @__PURE__ */ new Map();
6852
7015
  detectorUnsubscribes = /* @__PURE__ */ new Map();
6853
7016
  logger;
7017
+ setProviderRegistry(providerRegistry2) {
7018
+ this.deps.providerRegistry = providerRegistry2;
7019
+ }
6854
7020
  /**
6855
7021
  * Create a new session with provider
6856
7022
  */
@@ -7815,15 +7981,104 @@ var init_auth_session_repo = __esm({
7815
7981
  }
7816
7982
  });
7817
7983
 
7818
- // packages/server/src/storage/repositories/provider-config-repo.ts
7984
+ // packages/server/src/storage/repositories/custom-provider-repo.ts
7819
7985
  function isRecord5(value) {
7820
7986
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
7821
7987
  }
7822
- function normalizeProviderConfigFile(value) {
7988
+ function normalizeConfig(config) {
7989
+ return {
7990
+ ...config,
7991
+ args: [...config.args],
7992
+ env: { ...config.env },
7993
+ capabilities: config.capabilities.map((capability) => ({ ...capability }))
7994
+ };
7995
+ }
7996
+ function normalizeFileConfigs(value) {
7823
7997
  if (isRecord5(value) && value.version === 1 && isRecord5(value.providers)) {
7824
- return value.providers;
7998
+ return Object.fromEntries(
7999
+ Object.entries(value.providers).map(([id, config]) => [
8000
+ id,
8001
+ normalizeConfig(config)
8002
+ ])
8003
+ );
7825
8004
  }
7826
8005
  if (isRecord5(value)) {
8006
+ return Object.fromEntries(
8007
+ Object.entries(value).map(([id, config]) => [
8008
+ id,
8009
+ normalizeConfig(config)
8010
+ ])
8011
+ );
8012
+ }
8013
+ return {};
8014
+ }
8015
+ var CustomProviderRepo;
8016
+ var init_custom_provider_repo = __esm({
8017
+ "packages/server/src/storage/repositories/custom-provider-repo.ts"() {
8018
+ "use strict";
8019
+ init_json_file_store();
8020
+ CustomProviderRepo = class {
8021
+ filePath;
8022
+ constructor(input) {
8023
+ this.filePath = input.filePath;
8024
+ }
8025
+ list() {
8026
+ return Object.values(this.loadFileConfigs()).sort(
8027
+ (left, right) => right.updatedAt - left.updatedAt || left.id.localeCompare(right.id)
8028
+ );
8029
+ }
8030
+ get(id) {
8031
+ return this.loadFileConfigs()[id];
8032
+ }
8033
+ set(config) {
8034
+ const existing = this.get(config.id);
8035
+ const createdAt = existing?.createdAt ?? config.createdAt;
8036
+ const normalized = normalizeConfig({
8037
+ ...config,
8038
+ createdAt
8039
+ });
8040
+ const next = this.loadFileConfigs();
8041
+ next[normalized.id] = normalized;
8042
+ this.saveFileConfigs(next);
8043
+ return next[normalized.id];
8044
+ }
8045
+ delete(id) {
8046
+ const next = this.loadFileConfigs();
8047
+ if (!Object.prototype.hasOwnProperty.call(next, id)) {
8048
+ return;
8049
+ }
8050
+ delete next[id];
8051
+ this.saveFileConfigs(next);
8052
+ }
8053
+ loadFileConfigs() {
8054
+ const parsed = readJsonFile(
8055
+ this.filePath
8056
+ );
8057
+ if (parsed !== void 0) {
8058
+ return normalizeFileConfigs(parsed);
8059
+ }
8060
+ return {};
8061
+ }
8062
+ saveFileConfigs(configs) {
8063
+ const payload = {
8064
+ version: 1,
8065
+ providers: configs
8066
+ };
8067
+ writeJsonFileAtomic(this.filePath, payload);
8068
+ }
8069
+ };
8070
+ }
8071
+ });
8072
+
8073
+ // packages/server/src/storage/repositories/provider-config-repo.ts
8074
+ function isRecord6(value) {
8075
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
8076
+ }
8077
+ function normalizeProviderConfigFile(value) {
8078
+ if (isRecord6(value) && value.version === 1 && isRecord6(value.providers)) {
8079
+ return value.providers;
8080
+ }
8081
+ if (isRecord6(value)) {
7827
8082
  return value;
7828
8083
  }
7829
8084
  return {};
@@ -7896,15 +8151,159 @@ var init_provider_config_repo = __esm({
7896
8151
  }
7897
8152
  });
7898
8153
 
8154
+ // packages/server/src/workspace/workspace-state.ts
8155
+ import { join as join9 } from "node:path";
8156
+ function resolveWorkspaceStateFilePath(workspacePath, fileName) {
8157
+ return join9(workspacePath, WORKSPACE_STATE_DIR, fileName);
8158
+ }
8159
+ var WORKSPACE_STATE_DIR, AGENT_INSTRUCTIONS_RELATIVE_PATH, SESSION_METADATA_FILE_NAME, SESSION_METADATA_RELATIVE_PATH;
8160
+ var init_workspace_state = __esm({
8161
+ "packages/server/src/workspace/workspace-state.ts"() {
8162
+ "use strict";
8163
+ WORKSPACE_STATE_DIR = ".coder-studio";
8164
+ AGENT_INSTRUCTIONS_RELATIVE_PATH = `${WORKSPACE_STATE_DIR}/AGENTS.md`;
8165
+ SESSION_METADATA_FILE_NAME = "session-metadata.json";
8166
+ SESSION_METADATA_RELATIVE_PATH = `${WORKSPACE_STATE_DIR}/${SESSION_METADATA_FILE_NAME}`;
8167
+ }
8168
+ });
8169
+
8170
+ // packages/server/src/storage/repositories/session-metadata-repo.ts
8171
+ function isRecord7(value) {
8172
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
8173
+ }
8174
+ function normalizeRun(run) {
8175
+ return {
8176
+ ...run
8177
+ };
8178
+ }
8179
+ function normalizeMetadata(metadata) {
8180
+ return {
8181
+ sessionId: metadata.sessionId,
8182
+ workspaceId: metadata.workspaceId,
8183
+ providerId: metadata.providerId,
8184
+ objective: metadata.objective ?? void 0,
8185
+ baselineGitHead: metadata.baselineGitHead ?? void 0,
8186
+ baselineCapturedAt: metadata.baselineCapturedAt ?? void 0,
8187
+ verificationRuns: metadata.verificationRuns.map(normalizeRun)
8188
+ };
8189
+ }
8190
+ function normalizeFileMetadata(value) {
8191
+ if (isRecord7(value) && value.version === 1 && isRecord7(value.metadata)) {
8192
+ return Object.fromEntries(
8193
+ Object.entries(value.metadata).map(([sessionId, metadata]) => [
8194
+ sessionId,
8195
+ normalizeMetadata(metadata)
8196
+ ])
8197
+ );
8198
+ }
8199
+ if (isRecord7(value)) {
8200
+ return Object.fromEntries(
8201
+ Object.entries(value).map(([sessionId, metadata]) => [
8202
+ sessionId,
8203
+ normalizeMetadata(metadata)
8204
+ ])
8205
+ );
8206
+ }
8207
+ return {};
8208
+ }
8209
+ var SessionMetadataRepo;
8210
+ var init_session_metadata_repo = __esm({
8211
+ "packages/server/src/storage/repositories/session-metadata-repo.ts"() {
8212
+ "use strict";
8213
+ init_workspace_state();
8214
+ init_json_file_store();
8215
+ SessionMetadataRepo = class {
8216
+ workspaceRepo;
8217
+ constructor(input) {
8218
+ this.workspaceRepo = input.workspaceRepo;
8219
+ }
8220
+ upsert(metadata) {
8221
+ const normalized = normalizeMetadata(metadata);
8222
+ const workspace = this.workspaceRepo.findById(normalized.workspaceId);
8223
+ if (!workspace) {
8224
+ throw new Error(`Workspace not found for session metadata: ${normalized.workspaceId}`);
8225
+ }
8226
+ const existing = this.findSessionLocation(normalized.sessionId);
8227
+ if (existing && existing.workspace.id !== workspace.id) {
8228
+ delete existing.fileMetadata[normalized.sessionId];
8229
+ this.saveWorkspaceFileMetadata(existing.workspace.path, existing.fileMetadata);
8230
+ }
8231
+ const next = existing && existing.workspace.id === workspace.id ? existing.fileMetadata : this.loadWorkspaceFileMetadata(workspace.path);
8232
+ next[normalized.sessionId] = normalized;
8233
+ this.saveWorkspaceFileMetadata(workspace.path, next);
8234
+ return next[normalized.sessionId];
8235
+ }
8236
+ get(sessionId) {
8237
+ return this.findSessionLocation(sessionId)?.fileMetadata[sessionId];
8238
+ }
8239
+ addVerificationRun(sessionId, run) {
8240
+ const existing = this.findSessionLocation(sessionId);
8241
+ if (!existing) {
8242
+ throw new Error(`Session metadata not found: ${sessionId}`);
8243
+ }
8244
+ existing.fileMetadata[sessionId] = normalizeMetadata({
8245
+ ...existing.fileMetadata[sessionId],
8246
+ verificationRuns: [...existing.fileMetadata[sessionId].verificationRuns, normalizeRun(run)]
8247
+ });
8248
+ this.saveWorkspaceFileMetadata(existing.workspace.path, existing.fileMetadata);
8249
+ return existing.fileMetadata[sessionId];
8250
+ }
8251
+ delete(sessionId) {
8252
+ this.deleteFromAnyWorkspace(sessionId);
8253
+ }
8254
+ findSessionLocation(sessionId) {
8255
+ for (const workspace of this.workspaceRepo.list()) {
8256
+ const fileMetadata = this.loadWorkspaceFileMetadata(workspace.path);
8257
+ if (Object.prototype.hasOwnProperty.call(fileMetadata, sessionId)) {
8258
+ return {
8259
+ workspace,
8260
+ fileMetadata
8261
+ };
8262
+ }
8263
+ }
8264
+ return void 0;
8265
+ }
8266
+ deleteFromAnyWorkspace(sessionId) {
8267
+ const existing = this.findSessionLocation(sessionId);
8268
+ if (!existing) {
8269
+ return false;
8270
+ }
8271
+ delete existing.fileMetadata[sessionId];
8272
+ this.saveWorkspaceFileMetadata(existing.workspace.path, existing.fileMetadata);
8273
+ return true;
8274
+ }
8275
+ loadWorkspaceFileMetadata(workspacePath) {
8276
+ const parsed = readJsonFile(
8277
+ resolveWorkspaceStateFilePath(workspacePath, SESSION_METADATA_FILE_NAME)
8278
+ );
8279
+ if (parsed !== void 0) {
8280
+ return normalizeFileMetadata(parsed);
8281
+ }
8282
+ return {};
8283
+ }
8284
+ saveWorkspaceFileMetadata(workspacePath, metadata) {
8285
+ const payload = {
8286
+ version: 1,
8287
+ metadata
8288
+ };
8289
+ writeJsonFileAtomic(
8290
+ resolveWorkspaceStateFilePath(workspacePath, SESSION_METADATA_FILE_NAME),
8291
+ payload
8292
+ );
8293
+ }
8294
+ };
8295
+ }
8296
+ });
8297
+
7899
8298
  // packages/server/src/storage/repositories/settings-repo.ts
7900
- function isRecord6(value) {
8299
+ function isRecord8(value) {
7901
8300
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
7902
8301
  }
7903
8302
  function normalizeSettingsFile(value) {
7904
- if (isRecord6(value) && value.version === 1 && isRecord6(value.settings)) {
8303
+ if (isRecord8(value) && value.version === 1 && isRecord8(value.settings)) {
7905
8304
  return { ...value.settings };
7906
8305
  }
7907
- if (isRecord6(value)) {
8306
+ if (isRecord8(value)) {
7908
8307
  return { ...value };
7909
8308
  }
7910
8309
  return {};
@@ -8055,11 +8454,11 @@ var init_supervisor_repo = __esm({
8055
8454
  });
8056
8455
 
8057
8456
  // packages/server/src/storage/repositories/terminal-repo.ts
8058
- function isRecord7(value) {
8457
+ function isRecord9(value) {
8059
8458
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
8060
8459
  }
8061
8460
  function isTerminal(value) {
8062
- if (!isRecord7(value)) {
8461
+ if (!isRecord9(value)) {
8063
8462
  return false;
8064
8463
  }
8065
8464
  return typeof value.id === "string" && typeof value.workspaceId === "string" && (value.kind === "agent" || value.kind === "shell") && typeof value.cwd === "string" && Array.isArray(value.argv) && typeof value.cols === "number" && typeof value.rows === "number" && typeof value.alive === "boolean" && typeof value.createdAt === "number";
@@ -8071,7 +8470,7 @@ function normalizeTerminal(value) {
8071
8470
  };
8072
8471
  }
8073
8472
  function normalizeTerminalFile(value) {
8074
- if (isRecord7(value) && value.version === 1 && isRecord7(value.terminals)) {
8473
+ if (isRecord9(value) && value.version === 1 && isRecord9(value.terminals)) {
8075
8474
  const normalized = {};
8076
8475
  for (const entry of Object.values(value.terminals)) {
8077
8476
  if (isTerminal(entry)) {
@@ -8089,7 +8488,7 @@ function normalizeTerminalFile(value) {
8089
8488
  }
8090
8489
  return normalized;
8091
8490
  }
8092
- if (isRecord7(value)) {
8491
+ if (isRecord9(value)) {
8093
8492
  const normalized = {};
8094
8493
  for (const [terminalId, entry] of Object.entries(value)) {
8095
8494
  if (isTerminal(entry)) {
@@ -8255,12 +8654,12 @@ var init_terminal_repo = __esm({
8255
8654
  });
8256
8655
 
8257
8656
  // packages/server/src/storage/repositories/update-state-repo.ts
8258
- function isRecord8(value) {
8657
+ function isRecord10(value) {
8259
8658
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
8260
8659
  }
8261
8660
  function normalizeUpdateState(value, currentVersion) {
8262
8661
  const defaults = createDefaultUpdateState(currentVersion);
8263
- if (!isRecord8(value)) {
8662
+ if (!isRecord10(value)) {
8264
8663
  return defaults;
8265
8664
  }
8266
8665
  return {
@@ -8323,17 +8722,17 @@ var init_update_state_repo = __esm({
8323
8722
  });
8324
8723
 
8325
8724
  // packages/server/src/storage/repositories/workspace-repo.ts
8326
- function isRecord9(value) {
8725
+ function isRecord11(value) {
8327
8726
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
8328
8727
  }
8329
8728
  function isWorkspace(value) {
8330
- if (!isRecord9(value)) {
8729
+ if (!isRecord11(value)) {
8331
8730
  return false;
8332
8731
  }
8333
- return typeof value.id === "string" && typeof value.path === "string" && (value.targetRuntime === "native" || value.targetRuntime === "wsl") && typeof value.openedAt === "number" && typeof value.lastActiveAt === "number" && isRecord9(value.uiState);
8732
+ return typeof value.id === "string" && typeof value.path === "string" && (value.targetRuntime === "native" || value.targetRuntime === "wsl") && typeof value.openedAt === "number" && typeof value.lastActiveAt === "number" && isRecord11(value.uiState);
8334
8733
  }
8335
8734
  function normalizeWorkspaceFile(value) {
8336
- if (isRecord9(value) && value.version === 1 && isRecord9(value.workspaces)) {
8735
+ if (isRecord11(value) && value.version === 1 && isRecord11(value.workspaces)) {
8337
8736
  const normalized = {};
8338
8737
  for (const entry of Object.values(value.workspaces)) {
8339
8738
  if (isWorkspace(entry)) {
@@ -8351,7 +8750,7 @@ function normalizeWorkspaceFile(value) {
8351
8750
  }
8352
8751
  return normalized;
8353
8752
  }
8354
- if (isRecord9(value)) {
8753
+ if (isRecord11(value)) {
8355
8754
  const normalized = {};
8356
8755
  for (const [workspaceId, entry] of Object.entries(value)) {
8357
8756
  if (isWorkspace(entry)) {
@@ -8596,13 +8995,13 @@ function ensureNodePtySpawnHelperExecutable(deps = {}) {
8596
8995
  return;
8597
8996
  }
8598
8997
  const arch = deps.arch ?? process.arch;
8599
- const resolve6 = deps.resolve ?? ((id) => require4.resolve(id));
8998
+ const resolve8 = deps.resolve ?? ((id) => require4.resolve(id));
8600
8999
  const fileExists = deps.existsSync ?? existsSync6;
8601
- const stat11 = deps.statSync ?? statSync;
9000
+ const stat12 = deps.statSync ?? statSync;
8602
9001
  const chmod = deps.chmodSync ?? chmodSync2;
8603
9002
  let packageJsonPath;
8604
9003
  try {
8605
- packageJsonPath = resolve6(NODE_PTY_PKG);
9004
+ packageJsonPath = resolve8(NODE_PTY_PKG);
8606
9005
  } catch {
8607
9006
  return;
8608
9007
  }
@@ -8616,7 +9015,7 @@ function ensureNodePtySpawnHelperExecutable(deps = {}) {
8616
9015
  if (!fileExists(helperPath)) {
8617
9016
  return;
8618
9017
  }
8619
- const currentMode = stat11(helperPath).mode;
9018
+ const currentMode = stat12(helperPath).mode;
8620
9019
  const executableMode = currentMode | 73;
8621
9020
  if (executableMode === currentMode) {
8622
9021
  return;
@@ -8667,7 +9066,7 @@ async function escalateKillWithPolling(pid, signal, options) {
8667
9066
  const startTime = Date.now();
8668
9067
  const deadline = startTime + timeoutMs;
8669
9068
  while (Date.now() < deadline) {
8670
- await new Promise((resolve6) => setTimeout(resolve6, pollIntervalMs));
9069
+ await new Promise((resolve8) => setTimeout(resolve8, pollIntervalMs));
8671
9070
  if (!isProcessAlive(pid)) {
8672
9071
  return true;
8673
9072
  }
@@ -9031,7 +9430,7 @@ async function runCommand(command, timeoutMs, options = {}) {
9031
9430
  if (options.signal?.aborted) {
9032
9431
  throw createSupervisorEvalAbortedError();
9033
9432
  }
9034
- return await new Promise((resolve6, reject) => {
9433
+ return await new Promise((resolve8, reject) => {
9035
9434
  const child = spawn3(command.argv[0], command.argv.slice(1), {
9036
9435
  cwd: command.cwd,
9037
9436
  detached: process.platform !== "win32",
@@ -9061,7 +9460,7 @@ async function runCommand(command, timeoutMs, options = {}) {
9061
9460
  }
9062
9461
  settled = true;
9063
9462
  cleanup();
9064
- resolve6(value);
9463
+ resolve8(value);
9065
9464
  };
9066
9465
  const terminate = (error) => {
9067
9466
  if (terminationError) {
@@ -9889,12 +10288,12 @@ var init_scheduler = __esm({
9889
10288
 
9890
10289
  // packages/server/src/supervisor/manager.ts
9891
10290
  function createDeferredCompletion() {
9892
- let resolve6 = () => {
10291
+ let resolve8 = () => {
9893
10292
  };
9894
10293
  const promise = new Promise((innerResolve) => {
9895
- resolve6 = innerResolve;
10294
+ resolve8 = innerResolve;
9896
10295
  });
9897
- return { promise, resolve: resolve6 };
10296
+ return { promise, resolve: resolve8 };
9898
10297
  }
9899
10298
  function generateSupervisorId() {
9900
10299
  return `sup_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;
@@ -10015,6 +10414,9 @@ var init_manager4 = __esm({
10015
10414
  logger;
10016
10415
  config;
10017
10416
  lifecycleUnsubscribe = null;
10417
+ setProviderRegistry(providerRegistry2) {
10418
+ this.deps.providerRegistry = providerRegistry2;
10419
+ }
10018
10420
  start() {
10019
10421
  this.lifecycleUnsubscribe?.();
10020
10422
  this.lifecycleUnsubscribe = this.deps.eventBus.on(
@@ -11249,10 +11651,10 @@ var init_manager4 = __esm({
11249
11651
  if (signal?.aborted) {
11250
11652
  throw { code: "supervisor_eval_aborted", message: "Supervisor evaluator aborted" };
11251
11653
  }
11252
- await new Promise((resolve6, reject) => {
11654
+ await new Promise((resolve8, reject) => {
11253
11655
  const timer = setTimeout(() => {
11254
11656
  signal?.removeEventListener("abort", onAbort);
11255
- resolve6();
11657
+ resolve8();
11256
11658
  }, delayMs);
11257
11659
  timer.unref?.();
11258
11660
  const onAbort = () => {
@@ -11284,30 +11686,30 @@ __export(target_store_exports, {
11284
11686
  saveTargetMeta: () => saveTargetMeta
11285
11687
  });
11286
11688
  import { mkdir as mkdir4, mkdtemp as mkdtemp2, readdir as readdir2, readFile as readFile3, rename, rm as rm6, writeFile as writeFile4 } from "node:fs/promises";
11287
- import { dirname as dirname6, join as join9 } from "node:path";
11689
+ import { dirname as dirname6, join as join10 } from "node:path";
11288
11690
  function targetDir(workspacePath, targetId) {
11289
- return join9(workspacePath, ".coder-studio", "supervisor", "targets", targetId);
11691
+ return join10(workspacePath, ".coder-studio", "supervisor", "targets", targetId);
11290
11692
  }
11291
11693
  function metaPath(workspacePath, targetId) {
11292
- return join9(targetDir(workspacePath, targetId), "meta.json");
11694
+ return join10(targetDir(workspacePath, targetId), "meta.json");
11293
11695
  }
11294
11696
  function memoryPath(workspacePath, targetId) {
11295
- return join9(targetDir(workspacePath, targetId), "memory.json");
11697
+ return join10(targetDir(workspacePath, targetId), "memory.json");
11296
11698
  }
11297
11699
  function cyclesPath(workspacePath, targetId) {
11298
- return join9(targetDir(workspacePath, targetId), "cycles.jsonl");
11700
+ return join10(targetDir(workspacePath, targetId), "cycles.jsonl");
11299
11701
  }
11300
11702
  function targetsRoot(workspacePath) {
11301
- return join9(workspacePath, ".coder-studio", "supervisor", "targets");
11703
+ return join10(workspacePath, ".coder-studio", "supervisor", "targets");
11302
11704
  }
11303
11705
  function metaFilePath(dirPath) {
11304
- return join9(dirPath, "meta.json");
11706
+ return join10(dirPath, "meta.json");
11305
11707
  }
11306
11708
  function memoryFilePath(dirPath) {
11307
- return join9(dirPath, "memory.json");
11709
+ return join10(dirPath, "memory.json");
11308
11710
  }
11309
11711
  function cyclesFilePath(dirPath) {
11310
- return join9(dirPath, "cycles.jsonl");
11712
+ return join10(dirPath, "cycles.jsonl");
11311
11713
  }
11312
11714
  function hasCode2(error, code) {
11313
11715
  return Boolean(
@@ -11326,7 +11728,7 @@ function errorMessage(error, fallback) {
11326
11728
  }
11327
11729
  return fallback;
11328
11730
  }
11329
- function isRecord10(value) {
11731
+ function isRecord12(value) {
11330
11732
  return Boolean(value) && typeof value === "object";
11331
11733
  }
11332
11734
  function readNonEmptyString(value) {
@@ -11370,7 +11772,7 @@ function fallbackAcceptanceCriteria(title) {
11370
11772
  return [`${title} is complete`];
11371
11773
  }
11372
11774
  function normalizeItem(value, fallbackKind) {
11373
- if (!isRecord10(value)) {
11775
+ if (!isRecord12(value)) {
11374
11776
  return null;
11375
11777
  }
11376
11778
  const id = readNonEmptyString(value.id);
@@ -11401,7 +11803,7 @@ function normalizeLegacyPlanItems(plan) {
11401
11803
  }
11402
11804
  return plan.flatMap((value) => {
11403
11805
  const item = normalizeItem(
11404
- isRecord10(value) ? {
11806
+ isRecord12(value) ? {
11405
11807
  id: value.id,
11406
11808
  kind: "stage",
11407
11809
  title: value.title,
@@ -11425,7 +11827,7 @@ function resolveActiveItemId(items, candidate) {
11425
11827
  return items.find((item) => item.status === "in_progress")?.id ?? items.find((item) => item.status === "pending")?.id ?? items[0]?.id;
11426
11828
  }
11427
11829
  function normalizeTargetMemory(raw, targetId) {
11428
- if (!isRecord10(raw)) {
11830
+ if (!isRecord12(raw)) {
11429
11831
  return buildTargetMemory(targetId, 0);
11430
11832
  }
11431
11833
  const updatedAt = readTimestamp(raw.updatedAt, 0);
@@ -11452,7 +11854,7 @@ function normalizeTargetMemory(raw, targetId) {
11452
11854
  };
11453
11855
  }
11454
11856
  function normalizePersistedSupervisor(raw, fallback) {
11455
- if (!isRecord10(raw)) {
11857
+ if (!isRecord12(raw)) {
11456
11858
  return void 0;
11457
11859
  }
11458
11860
  const id = readNonEmptyString(raw.id) ?? fallback.targetId;
@@ -11488,7 +11890,7 @@ function normalizePersistedSupervisor(raw, fallback) {
11488
11890
  };
11489
11891
  }
11490
11892
  function normalizeTargetMeta(raw, fallbackTargetId) {
11491
- if (!isRecord10(raw)) {
11893
+ if (!isRecord12(raw)) {
11492
11894
  const targetId2 = fallbackTargetId ?? "";
11493
11895
  return {
11494
11896
  targetId: targetId2,
@@ -11609,7 +12011,7 @@ async function resetTargetFiles(workspacePath, input) {
11609
12011
  const parentDir = dirname6(dir);
11610
12012
  const backupDir = `${dir}.backup-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
11611
12013
  await mkdir4(parentDir, { recursive: true });
11612
- const stagingDir = await mkdtemp2(join9(parentDir, `${input.targetId}.reset-`));
12014
+ const stagingDir = await mkdtemp2(join10(parentDir, `${input.targetId}.reset-`));
11613
12015
  let backupCreated = false;
11614
12016
  let promoted = false;
11615
12017
  let restored = false;
@@ -12066,8 +12468,8 @@ var init_terminal_snapshot_buffer = __esm({
12066
12468
  if (this.pendingWriteCount === 0) {
12067
12469
  return Promise.resolve();
12068
12470
  }
12069
- return new Promise((resolve6) => {
12070
- this.drainResolvers.push(resolve6);
12471
+ return new Promise((resolve8) => {
12472
+ this.drainResolvers.push(resolve8);
12071
12473
  });
12072
12474
  }
12073
12475
  resolveDrainIfIdle() {
@@ -12076,8 +12478,8 @@ var init_terminal_snapshot_buffer = __esm({
12076
12478
  }
12077
12479
  const resolvers = this.drainResolvers;
12078
12480
  this.drainResolvers = [];
12079
- for (const resolve6 of resolvers) {
12080
- resolve6();
12481
+ for (const resolve8 of resolvers) {
12482
+ resolve8();
12081
12483
  }
12082
12484
  }
12083
12485
  requireTerminal() {
@@ -12438,10 +12840,10 @@ var init_manager5 = __esm({
12438
12840
  }
12439
12841
  return existing.promise;
12440
12842
  }
12441
- let resolve6 = () => {
12843
+ let resolve8 = () => {
12442
12844
  };
12443
12845
  const promise = new Promise((innerResolve) => {
12444
- resolve6 = innerResolve;
12846
+ resolve8 = innerResolve;
12445
12847
  });
12446
12848
  let markKillCompleted = () => {
12447
12849
  };
@@ -12454,7 +12856,7 @@ var init_manager5 = __esm({
12454
12856
  markKillCompleted,
12455
12857
  finalized: false,
12456
12858
  promise,
12457
- resolve: resolve6
12859
+ resolve: resolve8
12458
12860
  });
12459
12861
  void terminal.pty.kill(signal).finally(() => {
12460
12862
  const waiter = this.explicitCloseWaiters.get(terminalId);
@@ -13024,7 +13426,7 @@ var init_validator = __esm({
13024
13426
  // packages/server/src/fs/gitignore.ts
13025
13427
  import { existsSync as existsSync7, readFileSync as readFileSync6 } from "fs";
13026
13428
  import ignore from "ignore";
13027
- import { join as join10, relative as relative2 } from "path";
13429
+ import { join as join11, relative as relative2 } from "path";
13028
13430
  function normalizePath(path14) {
13029
13431
  return path14.replace(/\\/g, "/");
13030
13432
  }
@@ -13047,7 +13449,7 @@ function isIgnoredByGitignore(ig, path14) {
13047
13449
  return ig.ignores(path14) || ig.ignores(`${path14}/`);
13048
13450
  }
13049
13451
  function createGitignoreFilter(rootPath, dirPath) {
13050
- const gitignorePath = join10(rootPath, ".gitignore");
13452
+ const gitignorePath = join11(rootPath, ".gitignore");
13051
13453
  if (!existsSync7(gitignorePath)) {
13052
13454
  return (name) => !isDefaultTreeIgnored(name);
13053
13455
  }
@@ -13057,7 +13459,7 @@ function createGitignoreFilter(rootPath, dirPath) {
13057
13459
  if (isAlwaysTreeIgnored(name)) {
13058
13460
  return false;
13059
13461
  }
13060
- const relativePath = relativeToRoot(rootPath, join10(dirPath, name));
13462
+ const relativePath = relativeToRoot(rootPath, join11(dirPath, name));
13061
13463
  return !isIgnoredByGitignore(ig, relativePath);
13062
13464
  };
13063
13465
  }
@@ -13730,7 +14132,7 @@ var init_fencing = __esm({
13730
14132
  // packages/server/src/commands/terminal.ts
13731
14133
  import { stat as stat8 } from "node:fs/promises";
13732
14134
  import { basename, isAbsolute as isAbsolute2 } from "node:path";
13733
- import { z as z6 } from "zod";
14135
+ import { z as z7 } from "zod";
13734
14136
  function decodeTerminalInput(args) {
13735
14137
  if ("bytes" in args) {
13736
14138
  return Buffer.from(args.bytes, "base64");
@@ -13802,29 +14204,29 @@ var init_terminal = __esm({
13802
14204
  init_src3();
13803
14205
  init_file_io();
13804
14206
  init_dispatch();
13805
- TerminalInputActivitySchema = z6.enum(TERMINAL_INPUT_ACTIVITIES).optional();
13806
- TerminalInputSchema = z6.union([
13807
- z6.object({
13808
- terminalId: z6.string(),
13809
- bytes: z6.string(),
14207
+ TerminalInputActivitySchema = z7.enum(TERMINAL_INPUT_ACTIVITIES).optional();
14208
+ TerminalInputSchema = z7.union([
14209
+ z7.object({
14210
+ terminalId: z7.string(),
14211
+ bytes: z7.string(),
13810
14212
  activity: TerminalInputActivitySchema,
13811
- submittedText: z6.string().optional()
14213
+ submittedText: z7.string().optional()
13812
14214
  }),
13813
- z6.object({
13814
- terminalId: z6.string(),
13815
- transport: z6.literal("binary"),
13816
- streamId: z6.number().int().nonnegative(),
13817
- size: z6.number().int().nonnegative(),
14215
+ z7.object({
14216
+ terminalId: z7.string(),
14217
+ transport: z7.literal("binary"),
14218
+ streamId: z7.number().int().nonnegative(),
14219
+ size: z7.number().int().nonnegative(),
13818
14220
  activity: TerminalInputActivitySchema,
13819
- submittedText: z6.string().optional()
14221
+ submittedText: z7.string().optional()
13820
14222
  })
13821
14223
  ]);
13822
14224
  pendingTerminalInput = /* @__PURE__ */ new Map();
13823
14225
  nextOutboundBinaryStreamId = 0;
13824
14226
  registerCommand(
13825
14227
  "terminal.list",
13826
- z6.object({
13827
- workspaceId: z6.string()
14228
+ z7.object({
14229
+ workspaceId: z7.string()
13828
14230
  }),
13829
14231
  async (args, ctx) => {
13830
14232
  return ctx.terminalMgr.getAll().map((terminal) => terminal.toDTO()).filter((terminal) => terminal.workspaceId === args.workspaceId);
@@ -13832,12 +14234,12 @@ var init_terminal = __esm({
13832
14234
  );
13833
14235
  registerCommand(
13834
14236
  "terminal.create",
13835
- z6.object({
13836
- workspaceId: z6.string(),
13837
- cols: z6.number().int().positive().optional(),
13838
- rows: z6.number().int().positive().optional(),
13839
- cwdPath: z6.string().optional(),
13840
- themeBackground: z6.string().regex(/^#[0-9a-fA-F]{3,8}$/).optional()
14237
+ z7.object({
14238
+ workspaceId: z7.string(),
14239
+ cols: z7.number().int().positive().optional(),
14240
+ rows: z7.number().int().positive().optional(),
14241
+ cwdPath: z7.string().optional(),
14242
+ themeBackground: z7.string().regex(/^#[0-9a-fA-F]{3,8}$/).optional()
13841
14243
  }),
13842
14244
  async (args, ctx) => {
13843
14245
  const workspace = ctx.workspaceMgr.get(args.workspaceId);
@@ -13883,9 +14285,9 @@ var init_terminal = __esm({
13883
14285
  );
13884
14286
  registerCommand(
13885
14287
  "terminal.replay",
13886
- z6.object({
13887
- terminalId: z6.string(),
13888
- lastSeq: z6.number().int().nonnegative().optional()
14288
+ z7.object({
14289
+ terminalId: z7.string(),
14290
+ lastSeq: z7.number().int().nonnegative().optional()
13889
14291
  }),
13890
14292
  async (args, ctx, clientId) => {
13891
14293
  const replay = ctx.terminalMgr.replay(args.terminalId, args.lastSeq ?? 0);
@@ -13910,8 +14312,8 @@ var init_terminal = __esm({
13910
14312
  );
13911
14313
  registerCommand(
13912
14314
  "terminal.snapshot",
13913
- z6.object({
13914
- terminalId: z6.string()
14315
+ z7.object({
14316
+ terminalId: z7.string()
13915
14317
  }),
13916
14318
  async (args, ctx, clientId) => {
13917
14319
  const snapshot = await ctx.terminalMgr.snapshot(args.terminalId);
@@ -13939,8 +14341,8 @@ var init_terminal = __esm({
13939
14341
  );
13940
14342
  registerCommand(
13941
14343
  "terminal.close",
13942
- z6.object({
13943
- terminalId: z6.string()
14344
+ z7.object({
14345
+ terminalId: z7.string()
13944
14346
  }),
13945
14347
  async (args, ctx) => {
13946
14348
  await ctx.terminalMgr.close(args.terminalId);
@@ -13962,10 +14364,10 @@ var init_terminal = __esm({
13962
14364
  });
13963
14365
  registerCommand(
13964
14366
  "terminal.resize",
13965
- z6.object({
13966
- terminalId: z6.string(),
13967
- cols: z6.number().int().positive(),
13968
- rows: z6.number().int().positive()
14367
+ z7.object({
14368
+ terminalId: z7.string(),
14369
+ cols: z7.number().int().positive(),
14370
+ rows: z7.number().int().positive()
13969
14371
  }),
13970
14372
  async (args, ctx) => {
13971
14373
  const sessionId = ctx.sessionMgr.findSessionIdByTerminal(args.terminalId);
@@ -14588,7 +14990,7 @@ var init_hub = __esm({
14588
14990
  }
14589
14991
  }
14590
14992
  awaitBinaryPayload(clientId) {
14591
- return new Promise((resolve6, reject) => {
14993
+ return new Promise((resolve8, reject) => {
14592
14994
  const timer = setTimeout(() => {
14593
14995
  const waiters = this.pendingBinaryWaiters.get(clientId);
14594
14996
  if (!waiters) return;
@@ -14600,7 +15002,7 @@ var init_hub = __esm({
14600
15002
  }
14601
15003
  reject(new Error("Timeout waiting for terminal input binary payload"));
14602
15004
  }, BINARY_PAYLOAD_TIMEOUT_MS);
14603
- const waiter = { resolve: resolve6, reject, timer };
15005
+ const waiter = { resolve: resolve8, reject, timer };
14604
15006
  const queue = this.pendingBinaryWaiters.get(clientId);
14605
15007
  if (queue) {
14606
15008
  queue.push(waiter);
@@ -14904,57 +15306,270 @@ var init_hub = __esm({
14904
15306
  }
14905
15307
  });
14906
15308
 
14907
- // packages/server/src/commands/workspace.ts
14908
- import { readdir as readdir3, realpath as realpath3 } from "node:fs/promises";
14909
- import { homedir as homedir2 } from "node:os";
14910
- import { isAbsolute as isAbsolute3, join as join11, resolve as resolve4 } from "node:path";
14911
- import { z as z7 } from "zod";
14912
- function resolveBrowsePath(path14) {
14913
- const home = homedir2();
14914
- if (!path14 || path14 === "~") {
14915
- return home;
15309
+ // packages/server/src/workspace/intelligence.ts
15310
+ import { access as access2, lstat as lstat2, readFile as readFile4, stat as stat9 } from "node:fs/promises";
15311
+ import { join as join12, resolve as resolve4 } from "node:path";
15312
+ async function inspectWorkspaceIntelligence(input) {
15313
+ const [packageManager, manifest, git, docsExistence, agentsExists, frameworks] = await Promise.all([
15314
+ detectPackageManager(input.rootPath),
15315
+ readPackageJson(input.rootPath),
15316
+ detectGitState(input.rootPath),
15317
+ detectDocs(input.rootPath),
15318
+ pathExists(join12(input.rootPath, AGENT_INSTRUCTIONS_RELATIVE_PATH)),
15319
+ detectFrameworks(input.rootPath)
15320
+ ]);
15321
+ const scripts = extractScripts(manifest);
15322
+ return {
15323
+ workspaceId: input.workspaceId,
15324
+ rootPath: input.rootPath,
15325
+ git,
15326
+ packageManager,
15327
+ frameworks,
15328
+ scripts,
15329
+ recommendedCommands: buildRecommendedCommands(packageManager, scripts),
15330
+ docs: docsExistence,
15331
+ agentInstructions: {
15332
+ exists: agentsExists,
15333
+ path: AGENT_INSTRUCTIONS_RELATIVE_PATH
15334
+ }
15335
+ };
15336
+ }
15337
+ async function detectPackageManager(rootPath) {
15338
+ for (const candidate of packageManagerCandidates) {
15339
+ if (await pathExists(join12(rootPath, candidate.file))) {
15340
+ return candidate.manager;
15341
+ }
14916
15342
  }
14917
- if (path14.startsWith("~/")) {
14918
- return join11(home, path14.slice(2));
15343
+ if (await pathExists(join12(rootPath, "package.json"))) {
15344
+ return "npm";
14919
15345
  }
14920
- return isAbsolute3(path14) ? path14 : resolve4(home, path14);
15346
+ return void 0;
14921
15347
  }
14922
- async function buildRootPaths(currentPath) {
14923
- const roots = /* @__PURE__ */ new Set(["/"]);
14924
- const home = homedir2();
14925
- roots.add(home);
15348
+ async function readPackageJson(rootPath) {
15349
+ const packageJsonPath = join12(rootPath, "package.json");
15350
+ if (!await pathExists(packageJsonPath)) {
15351
+ return null;
15352
+ }
14926
15353
  try {
14927
- roots.add(await realpath3(home));
15354
+ const raw = await readFile4(packageJsonPath, "utf8");
15355
+ const parsed = JSON.parse(raw);
15356
+ return parsed && typeof parsed === "object" ? parsed : null;
14928
15357
  } catch {
15358
+ return null;
14929
15359
  }
14930
- const currentSegments = currentPath.split("/").filter(Boolean);
14931
- if (currentSegments.length > 0) {
14932
- roots.add(`/${currentSegments[0]}`);
15360
+ }
15361
+ function extractScripts(manifest) {
15362
+ const scripts = manifest?.scripts ?? {};
15363
+ return {
15364
+ dev: normalizeScript(scripts.dev),
15365
+ test: normalizeScript(scripts.test),
15366
+ build: normalizeScript(scripts.build),
15367
+ lint: normalizeScript(scripts.lint)
15368
+ };
15369
+ }
15370
+ function normalizeScript(value) {
15371
+ return typeof value === "string" && value.trim().length > 0 ? value : void 0;
15372
+ }
15373
+ function buildRecommendedCommands(packageManager, scripts) {
15374
+ if (!packageManager) {
15375
+ return [];
14933
15376
  }
14934
- return Array.from(roots);
15377
+ return packageScriptKeys.flatMap((key) => {
15378
+ if (!scripts[key]) {
15379
+ return [];
15380
+ }
15381
+ return [
15382
+ {
15383
+ key,
15384
+ command: buildPackageCommand(packageManager, key),
15385
+ source: "package_json"
15386
+ }
15387
+ ];
15388
+ });
14935
15389
  }
14936
- var init_workspace = __esm({
14937
- "packages/server/src/commands/workspace.ts"() {
14938
- "use strict";
14939
- init_dispatch();
14940
- registerCommand("workspace.list", z7.object({}), async (_args, ctx) => {
14941
- return ctx.workspaceMgr.list();
14942
- });
14943
- registerCommand(
14944
- "workspace.browse",
14945
- z7.object({
14946
- path: z7.string().optional()
14947
- }),
14948
- async (args) => {
14949
- const basePath = resolveBrowsePath(args.path);
14950
- const entries = await readdir3(basePath, { withFileTypes: true });
14951
- const directories = entries.filter((entry) => entry.isDirectory()).map((entry) => ({
15390
+ function buildPackageCommand(packageManager, scriptName) {
15391
+ switch (packageManager) {
15392
+ case "pnpm":
15393
+ return `pnpm ${scriptName}`;
15394
+ case "yarn":
15395
+ return `yarn ${scriptName}`;
15396
+ case "bun":
15397
+ return `bun run ${scriptName}`;
15398
+ case "npm":
15399
+ default:
15400
+ return `npm run ${scriptName}`;
15401
+ }
15402
+ }
15403
+ async function detectFrameworks(rootPath) {
15404
+ const manifest = await readPackageJson(rootPath);
15405
+ const deps = {
15406
+ ...manifest?.dependencies ?? {},
15407
+ ...manifest?.devDependencies ?? {}
15408
+ };
15409
+ const frameworks = /* @__PURE__ */ new Set();
15410
+ if ("next" in deps) {
15411
+ frameworks.add("Next.js");
15412
+ }
15413
+ if ("react" in deps) {
15414
+ frameworks.add("React");
15415
+ }
15416
+ if ("vite" in deps || await hasAnyPath(rootPath, viteConfigFiles)) {
15417
+ frameworks.add("Vite");
15418
+ }
15419
+ if (manifest) {
15420
+ frameworks.add("Node");
15421
+ }
15422
+ if (await hasAnyPath(rootPath, ["pnpm-workspace.yaml", "turbo.json", "nx.json"])) {
15423
+ frameworks.add("Monorepo");
15424
+ }
15425
+ return frameworkOrder.filter((framework) => frameworks.has(framework));
15426
+ }
15427
+ async function detectGitState(rootPath) {
15428
+ const gitPath = join12(rootPath, ".git");
15429
+ let stats;
15430
+ try {
15431
+ stats = await lstat2(gitPath);
15432
+ } catch {
15433
+ return { isRepo: false };
15434
+ }
15435
+ const gitDir = stats.isDirectory() ? gitPath : stats.isFile() ? await resolveGitDirFromFile(rootPath, gitPath) : null;
15436
+ if (!gitDir) {
15437
+ return { isRepo: false };
15438
+ }
15439
+ const branch = await readGitBranch(gitDir);
15440
+ return branch ? { isRepo: true, branch } : { isRepo: true };
15441
+ }
15442
+ async function resolveGitDirFromFile(rootPath, gitFilePath) {
15443
+ try {
15444
+ const raw = await readFile4(gitFilePath, "utf8");
15445
+ const match = raw.match(/^gitdir:\s*(.+)\s*$/m);
15446
+ if (!match) {
15447
+ return null;
15448
+ }
15449
+ const gitDirPath = match[1];
15450
+ if (!gitDirPath) {
15451
+ return null;
15452
+ }
15453
+ return resolve4(rootPath, gitDirPath);
15454
+ } catch {
15455
+ return null;
15456
+ }
15457
+ }
15458
+ async function readGitBranch(gitDir) {
15459
+ try {
15460
+ const head = await readFile4(join12(gitDir, "HEAD"), "utf8");
15461
+ const refMatch = head.match(/^ref:\s*refs\/heads\/(.+)\s*$/);
15462
+ return refMatch?.[1];
15463
+ } catch {
15464
+ return void 0;
15465
+ }
15466
+ }
15467
+ async function detectDocs(rootPath) {
15468
+ const docs = [];
15469
+ if (await pathExists(join12(rootPath, "README.md"))) {
15470
+ docs.push({ path: "README.md", kind: "readme" });
15471
+ }
15472
+ if (await pathExists(join12(rootPath, "docs"))) {
15473
+ try {
15474
+ const docsStats = await stat9(join12(rootPath, "docs"));
15475
+ if (docsStats.isDirectory()) {
15476
+ docs.push({ path: "docs", kind: "docs" });
15477
+ }
15478
+ } catch {
15479
+ }
15480
+ }
15481
+ return docs;
15482
+ }
15483
+ async function hasAnyPath(rootPath, relativePaths) {
15484
+ for (const relativePath of relativePaths) {
15485
+ if (await pathExists(join12(rootPath, relativePath))) {
15486
+ return true;
15487
+ }
15488
+ }
15489
+ return false;
15490
+ }
15491
+ async function pathExists(path14) {
15492
+ try {
15493
+ await access2(path14);
15494
+ return true;
15495
+ } catch {
15496
+ return false;
15497
+ }
15498
+ }
15499
+ var frameworkOrder, packageManagerCandidates, packageScriptKeys, viteConfigFiles;
15500
+ var init_intelligence = __esm({
15501
+ "packages/server/src/workspace/intelligence.ts"() {
15502
+ "use strict";
15503
+ init_workspace_state();
15504
+ frameworkOrder = ["Next.js", "React", "Vite", "Node", "Monorepo"];
15505
+ packageManagerCandidates = [
15506
+ { file: "pnpm-lock.yaml", manager: "pnpm" },
15507
+ { file: "yarn.lock", manager: "yarn" },
15508
+ { file: "bun.lockb", manager: "bun" },
15509
+ { file: "package-lock.json", manager: "npm" }
15510
+ ];
15511
+ packageScriptKeys = ["dev", "test", "build", "lint"];
15512
+ viteConfigFiles = [
15513
+ "vite.config.ts",
15514
+ "vite.config.js",
15515
+ "vite.config.mjs",
15516
+ "vite.config.cjs"
15517
+ ];
15518
+ }
15519
+ });
15520
+
15521
+ // packages/server/src/commands/workspace.ts
15522
+ import { readdir as readdir3, realpath as realpath3 } from "node:fs/promises";
15523
+ import { homedir as homedir2 } from "node:os";
15524
+ import { isAbsolute as isAbsolute3, join as join13, resolve as resolve5 } from "node:path";
15525
+ import { z as z8 } from "zod";
15526
+ function resolveBrowsePath(path14) {
15527
+ const home = homedir2();
15528
+ if (!path14 || path14 === "~") {
15529
+ return home;
15530
+ }
15531
+ if (path14.startsWith("~/")) {
15532
+ return join13(home, path14.slice(2));
15533
+ }
15534
+ return isAbsolute3(path14) ? path14 : resolve5(home, path14);
15535
+ }
15536
+ async function buildRootPaths(currentPath) {
15537
+ const roots = /* @__PURE__ */ new Set(["/"]);
15538
+ const home = homedir2();
15539
+ roots.add(home);
15540
+ try {
15541
+ roots.add(await realpath3(home));
15542
+ } catch {
15543
+ }
15544
+ const currentSegments = currentPath.split("/").filter(Boolean);
15545
+ if (currentSegments.length > 0) {
15546
+ roots.add(`/${currentSegments[0]}`);
15547
+ }
15548
+ return Array.from(roots);
15549
+ }
15550
+ var init_workspace = __esm({
15551
+ "packages/server/src/commands/workspace.ts"() {
15552
+ "use strict";
15553
+ init_intelligence();
15554
+ init_dispatch();
15555
+ registerCommand("workspace.list", z8.object({}), async (_args, ctx) => {
15556
+ return ctx.workspaceMgr.list();
15557
+ });
15558
+ registerCommand(
15559
+ "workspace.browse",
15560
+ z8.object({
15561
+ path: z8.string().optional()
15562
+ }),
15563
+ async (args) => {
15564
+ const basePath = resolveBrowsePath(args.path);
15565
+ const entries = await readdir3(basePath, { withFileTypes: true });
15566
+ const directories = entries.filter((entry) => entry.isDirectory()).map((entry) => ({
14952
15567
  name: entry.name,
14953
- path: join11(basePath, entry.name)
15568
+ path: join13(basePath, entry.name)
14954
15569
  })).sort((a, b) => a.name.localeCompare(b.name));
14955
15570
  return {
14956
15571
  currentPath: basePath,
14957
- parentPath: basePath !== "/" ? join11(basePath, "..") : null,
15572
+ parentPath: basePath !== "/" ? join13(basePath, "..") : null,
14958
15573
  directories,
14959
15574
  rootPaths: await buildRootPaths(basePath)
14960
15575
  };
@@ -14962,8 +15577,8 @@ var init_workspace = __esm({
14962
15577
  );
14963
15578
  registerCommand(
14964
15579
  "workspace.open",
14965
- z7.object({
14966
- path: z7.string()
15580
+ z8.object({
15581
+ path: z8.string()
14967
15582
  }),
14968
15583
  async (args, ctx) => {
14969
15584
  return ctx.workspaceMgr.open({
@@ -14971,10 +15586,26 @@ var init_workspace = __esm({
14971
15586
  });
14972
15587
  }
14973
15588
  );
15589
+ registerCommand(
15590
+ "workspace.intelligence",
15591
+ z8.object({
15592
+ workspaceId: z8.string()
15593
+ }),
15594
+ async (args, ctx) => {
15595
+ const workspace = ctx.workspaceMgr.get(args.workspaceId);
15596
+ if (!workspace) {
15597
+ throw { code: "workspace_not_found", message: `Workspace not found: ${args.workspaceId}` };
15598
+ }
15599
+ return inspectWorkspaceIntelligence({
15600
+ workspaceId: workspace.id,
15601
+ rootPath: workspace.path
15602
+ });
15603
+ }
15604
+ );
14974
15605
  registerCommand(
14975
15606
  "workspace.close",
14976
- z7.object({
14977
- id: z7.string()
15607
+ z8.object({
15608
+ id: z8.string()
14978
15609
  }),
14979
15610
  async (args, ctx) => {
14980
15611
  await ctx.workspaceMgr.close(args.id);
@@ -14982,27 +15613,27 @@ var init_workspace = __esm({
14982
15613
  );
14983
15614
  registerCommand(
14984
15615
  "workspace.uiState.set",
14985
- z7.object({
14986
- workspaceId: z7.string(),
14987
- uiState: z7.object({
14988
- leftPanelWidth: z7.number(),
14989
- bottomPanelHeight: z7.number(),
14990
- focusMode: z7.boolean(),
14991
- activeSessionId: z7.string().optional(),
14992
- fileTreeExpandedDirs: z7.array(z7.string()).optional(),
14993
- paneLayout: z7.object({
14994
- id: z7.string(),
14995
- type: z7.enum(["leaf", "split"]),
14996
- sessionId: z7.string().optional(),
14997
- direction: z7.enum(["horizontal", "vertical"]).optional(),
14998
- children: z7.lazy(
14999
- () => z7.array(
15000
- z7.object({
15001
- id: z7.string(),
15002
- type: z7.enum(["leaf", "split"]),
15003
- sessionId: z7.string().optional(),
15004
- direction: z7.enum(["horizontal", "vertical"]).optional(),
15005
- children: z7.any().optional()
15616
+ z8.object({
15617
+ workspaceId: z8.string(),
15618
+ uiState: z8.object({
15619
+ leftPanelWidth: z8.number(),
15620
+ bottomPanelHeight: z8.number(),
15621
+ focusMode: z8.boolean(),
15622
+ activeSessionId: z8.string().optional(),
15623
+ fileTreeExpandedDirs: z8.array(z8.string()).optional(),
15624
+ paneLayout: z8.object({
15625
+ id: z8.string(),
15626
+ type: z8.enum(["leaf", "split"]),
15627
+ sessionId: z8.string().optional(),
15628
+ direction: z8.enum(["horizontal", "vertical"]).optional(),
15629
+ children: z8.lazy(
15630
+ () => z8.array(
15631
+ z8.object({
15632
+ id: z8.string(),
15633
+ type: z8.enum(["leaf", "split"]),
15634
+ sessionId: z8.string().optional(),
15635
+ direction: z8.enum(["horizontal", "vertical"]).optional(),
15636
+ children: z8.any().optional()
15006
15637
  })
15007
15638
  )
15008
15639
  ).optional()
@@ -15022,7 +15653,7 @@ var init_workspace = __esm({
15022
15653
  });
15023
15654
 
15024
15655
  // packages/server/src/commands/workspace-activity.ts
15025
- import { z as z8 } from "zod";
15656
+ import { z as z9 } from "zod";
15026
15657
  function parseWorkspaceLastViewedTarget(value) {
15027
15658
  try {
15028
15659
  const parsed = JSON.parse(value);
@@ -15038,15 +15669,15 @@ var init_workspace_activity = __esm({
15038
15669
  "use strict";
15039
15670
  init_dispatch();
15040
15671
  WORKSPACE_LAST_VIEWED_TARGET_KEY = "workspace.lastViewedTarget";
15041
- workspaceLastViewedTargetSchema = z8.object({
15042
- workspaceId: z8.string(),
15043
- sessionId: z8.string().optional(),
15044
- updatedAt: z8.number()
15672
+ workspaceLastViewedTargetSchema = z9.object({
15673
+ workspaceId: z9.string(),
15674
+ sessionId: z9.string().optional(),
15675
+ updatedAt: z9.number()
15045
15676
  });
15046
15677
  registerCommand(
15047
15678
  "workspace.activate",
15048
- z8.object({
15049
- workspaceId: z8.string()
15679
+ z9.object({
15680
+ workspaceId: z9.string()
15050
15681
  }),
15051
15682
  async (args, ctx, clientId) => {
15052
15683
  if (!clientId) {
@@ -15056,14 +15687,14 @@ var init_workspace_activity = __esm({
15056
15687
  return {};
15057
15688
  }
15058
15689
  );
15059
- registerCommand("workspace.deactivate", z8.object({}), async (_args, ctx, clientId) => {
15690
+ registerCommand("workspace.deactivate", z9.object({}), async (_args, ctx, clientId) => {
15060
15691
  if (!clientId) {
15061
15692
  return {};
15062
15693
  }
15063
15694
  ctx.autoFetch.unregisterViewer(clientId);
15064
15695
  return {};
15065
15696
  });
15066
- registerCommand("workspace.lastViewedTarget.get", z8.object({}), async (_args, ctx) => {
15697
+ registerCommand("workspace.lastViewedTarget.get", z9.object({}), async (_args, ctx) => {
15067
15698
  const value = ctx.settingsRepo.get(WORKSPACE_LAST_VIEWED_TARGET_KEY);
15068
15699
  if (value === void 0) {
15069
15700
  return null;
@@ -15072,9 +15703,9 @@ var init_workspace_activity = __esm({
15072
15703
  });
15073
15704
  registerCommand(
15074
15705
  "workspace.lastViewedTarget.set",
15075
- z8.object({
15076
- workspaceId: z8.string(),
15077
- sessionId: z8.string().optional()
15706
+ z9.object({
15707
+ workspaceId: z9.string(),
15708
+ sessionId: z9.string().optional()
15078
15709
  }),
15079
15710
  async (args, ctx) => {
15080
15711
  const workspace = ctx.workspaceMgr.get(args.workspaceId);
@@ -15098,14 +15729,14 @@ var init_workspace_activity = __esm({
15098
15729
  });
15099
15730
 
15100
15731
  // packages/server/src/commands/activation.ts
15101
- import { z as z9 } from "zod";
15732
+ import { z as z10 } from "zod";
15102
15733
  var init_activation2 = __esm({
15103
15734
  "packages/server/src/commands/activation.ts"() {
15104
15735
  "use strict";
15105
15736
  init_dispatch();
15106
15737
  registerCommand(
15107
15738
  "activation.claim",
15108
- z9.object({ clientInstanceId: z9.string().min(1) }),
15739
+ z10.object({ clientInstanceId: z10.string().min(1) }),
15109
15740
  async (args, ctx, clientId) => {
15110
15741
  if (!clientId) {
15111
15742
  throw {
@@ -15129,7 +15760,7 @@ var init_activation2 = __esm({
15129
15760
  );
15130
15761
  registerCommand(
15131
15762
  "activation.release",
15132
- z9.object({ clientInstanceId: z9.string(), generation: z9.number().int().positive() }),
15763
+ z10.object({ clientInstanceId: z10.string(), generation: z10.number().int().positive() }),
15133
15764
  async (args, ctx, clientId) => {
15134
15765
  const lease = ctx.activationMgr.getLease();
15135
15766
  if (!clientId || !lease || lease.wsClientId !== clientId) {
@@ -15143,34 +15774,34 @@ var init_activation2 = __esm({
15143
15774
  });
15144
15775
 
15145
15776
  // packages/server/src/commands/connection.ts
15146
- import { z as z10 } from "zod";
15777
+ import { z as z11 } from "zod";
15147
15778
  var init_connection = __esm({
15148
15779
  "packages/server/src/commands/connection.ts"() {
15149
15780
  "use strict";
15150
15781
  init_dispatch();
15151
- registerCommand("connection.probe", z10.object({}).default({}), async () => {
15782
+ registerCommand("connection.probe", z11.object({}).default({}), async () => {
15152
15783
  return { ok: true };
15153
15784
  });
15154
15785
  }
15155
15786
  });
15156
15787
 
15157
15788
  // packages/server/src/commands/recovery.ts
15158
- import { z as z11 } from "zod";
15789
+ import { z as z12 } from "zod";
15159
15790
  var RecoveryReasonSchema;
15160
15791
  var init_recovery = __esm({
15161
15792
  "packages/server/src/commands/recovery.ts"() {
15162
15793
  "use strict";
15163
15794
  init_src3();
15164
15795
  init_dispatch();
15165
- RecoveryReasonSchema = z11.enum(RECOVERY_REASONS);
15796
+ RecoveryReasonSchema = z12.enum(RECOVERY_REASONS);
15166
15797
  registerCommand(
15167
15798
  "recovery.reconcile",
15168
- z11.object({
15799
+ z12.object({
15169
15800
  reason: RecoveryReasonSchema,
15170
- terminals: z11.array(
15171
- z11.object({
15172
- terminalId: z11.string(),
15173
- renderedSeq: z11.number().int().nonnegative()
15801
+ terminals: z12.array(
15802
+ z12.object({
15803
+ terminalId: z12.string(),
15804
+ renderedSeq: z12.number().int().nonnegative()
15174
15805
  })
15175
15806
  )
15176
15807
  }),
@@ -15416,15 +16047,33 @@ var init_pane_layout = __esm({
15416
16047
  });
15417
16048
 
15418
16049
  // packages/server/src/commands/session.ts
15419
- import { z as z12 } from "zod";
16050
+ import { readFile as readFile5 } from "node:fs/promises";
16051
+ import { join as join14, resolve as resolve6 } from "node:path";
16052
+ import { z as z13 } from "zod";
15420
16053
  function delay(ms) {
15421
- return new Promise((resolve6) => {
15422
- setTimeout(resolve6, ms);
16054
+ return new Promise((resolve8) => {
16055
+ setTimeout(resolve8, ms);
15423
16056
  });
15424
16057
  }
15425
16058
  function getProviderFromRegistry(providerId, registry) {
15426
16059
  return registry.find((provider) => provider.id === providerId);
15427
16060
  }
16061
+ async function tryReadGitHead(workspacePath) {
16062
+ try {
16063
+ const gitEntryPath = join14(workspacePath, ".git");
16064
+ const gitEntry = await readFile5(gitEntryPath, "utf8").catch(() => null);
16065
+ let gitDir = gitEntryPath;
16066
+ if (gitEntry && gitEntry.startsWith("gitdir:")) {
16067
+ const relativeGitDir = gitEntry.slice("gitdir:".length).trim();
16068
+ gitDir = resolve6(workspacePath, relativeGitDir);
16069
+ }
16070
+ const head = await readFile5(join14(gitDir, "HEAD"), "utf8");
16071
+ const trimmed = head.trim();
16072
+ return trimmed.length > 0 && !trimmed.startsWith("ref:") ? trimmed : void 0;
16073
+ } catch {
16074
+ return void 0;
16075
+ }
16076
+ }
15428
16077
  var SESSION_CLOSE_POLL_INTERVAL_MS, SESSION_CLOSE_TIMEOUT_MS;
15429
16078
  var init_session2 = __esm({
15430
16079
  "packages/server/src/commands/session.ts"() {
@@ -15436,8 +16085,8 @@ var init_session2 = __esm({
15436
16085
  SESSION_CLOSE_TIMEOUT_MS = 5e3;
15437
16086
  registerCommand(
15438
16087
  "session.list",
15439
- z12.object({
15440
- workspaceId: z12.string()
16088
+ z13.object({
16089
+ workspaceId: z13.string()
15441
16090
  }),
15442
16091
  async (args, ctx) => {
15443
16092
  return ctx.sessionMgr.getForWorkspace(args.workspaceId);
@@ -15445,11 +16094,11 @@ var init_session2 = __esm({
15445
16094
  );
15446
16095
  registerCommand(
15447
16096
  "session.create",
15448
- z12.object({
15449
- workspaceId: z12.string(),
15450
- providerId: z12.string(),
15451
- draft: z12.string().optional(),
15452
- themeBackground: z12.string().regex(/^#[0-9a-fA-F]{3,8}$/).optional()
16097
+ z13.object({
16098
+ workspaceId: z13.string(),
16099
+ providerId: z13.string(),
16100
+ draft: z13.string().optional(),
16101
+ themeBackground: z13.string().regex(/^#[0-9a-fA-F]{3,8}$/).optional()
15453
16102
  }),
15454
16103
  async (args, ctx) => {
15455
16104
  const workspace = ctx.workspaceMgr.get(args.workspaceId);
@@ -15472,7 +16121,7 @@ var init_session2 = __esm({
15472
16121
  }
15473
16122
  };
15474
16123
  }
15475
- return ctx.sessionMgr.create({
16124
+ const session = await ctx.sessionMgr.create({
15476
16125
  workspaceId: args.workspaceId,
15477
16126
  workspacePath: workspace.path,
15478
16127
  providerId: args.providerId,
@@ -15480,12 +16129,22 @@ var init_session2 = __esm({
15480
16129
  draft: args.draft,
15481
16130
  themeBackground: args.themeBackground
15482
16131
  });
16132
+ ctx.sessionMetadataRepo?.upsert({
16133
+ sessionId: session.id,
16134
+ workspaceId: args.workspaceId,
16135
+ providerId: args.providerId,
16136
+ objective: args.draft?.trim() || void 0,
16137
+ baselineGitHead: await tryReadGitHead(workspace.path),
16138
+ baselineCapturedAt: Date.now(),
16139
+ verificationRuns: []
16140
+ });
16141
+ return session;
15483
16142
  }
15484
16143
  );
15485
16144
  registerCommand(
15486
16145
  "session.stop",
15487
- z12.object({
15488
- sessionId: z12.string()
16146
+ z13.object({
16147
+ sessionId: z13.string()
15489
16148
  }),
15490
16149
  async (args, ctx) => {
15491
16150
  await ctx.sessionMgr.stop(args.sessionId);
@@ -15493,8 +16152,8 @@ var init_session2 = __esm({
15493
16152
  );
15494
16153
  registerCommand(
15495
16154
  "session.remove",
15496
- z12.object({
15497
- sessionId: z12.string()
16155
+ z13.object({
16156
+ sessionId: z13.string()
15498
16157
  }),
15499
16158
  async (args, ctx) => {
15500
16159
  const session = ctx.sessionMgr.get(args.sessionId);
@@ -15505,13 +16164,14 @@ var init_session2 = __esm({
15505
16164
  throw { code: "invalid_state", message: `Cannot remove session in state: ${session.state}` };
15506
16165
  }
15507
16166
  ctx.sessionMgr.delete(args.sessionId);
16167
+ ctx.sessionMetadataRepo?.delete(args.sessionId);
15508
16168
  }
15509
16169
  );
15510
16170
  registerCommand(
15511
16171
  "session.close",
15512
- z12.object({
15513
- sessionId: z12.string(),
15514
- paneDisposition: z12.enum(["draft", "remove"]).default("draft")
16172
+ z13.object({
16173
+ sessionId: z13.string(),
16174
+ paneDisposition: z13.enum(["draft", "remove"]).default("draft")
15515
16175
  }),
15516
16176
  async (args, ctx) => {
15517
16177
  let session = ctx.sessionMgr.get(args.sessionId);
@@ -15567,112 +16227,553 @@ var init_session2 = __esm({
15567
16227
  };
15568
16228
  ctx.workspaceMgr.updateUiState(session.workspaceId, nextUiState);
15569
16229
  ctx.sessionMgr.delete(args.sessionId);
16230
+ ctx.sessionMetadataRepo?.delete(args.sessionId);
15570
16231
  }
15571
16232
  );
15572
16233
  }
15573
16234
  });
15574
16235
 
15575
- // packages/server/src/fs/content-search.ts
15576
- import { spawn as spawn5 } from "child_process";
15577
- import { existsSync as existsSync8 } from "fs";
15578
- import { readdir as readdir4, readFile as readFile4, stat as stat9 } from "fs/promises";
15579
- import { basename as basename2, join as join12, relative as relative3 } from "path";
15580
- import { createInterface } from "readline";
15581
- async function searchFileContents(rootPath, options) {
15582
- const query = options.query.trim();
15583
- if (!query) {
15584
- return {
15585
- files: [],
15586
- totalMatchCount: 0,
15587
- hasMoreFiles: false,
15588
- truncatedMatchFileCount: 0
16236
+ // packages/server/src/commands/session-metadata.ts
16237
+ import { randomUUID as randomUUID6 } from "node:crypto";
16238
+ import { z as z14 } from "zod";
16239
+ function requireSessionMetadataRepo(ctx) {
16240
+ if (!ctx.sessionMetadataRepo) {
16241
+ throw {
16242
+ code: "session_metadata_unavailable",
16243
+ message: "Session metadata repository is not configured"
15589
16244
  };
15590
16245
  }
15591
- const result = await searchWithRipgrep(rootPath, query, options.maxFiles).catch(
15592
- async (error) => {
15593
- if (error.code === "ENOENT") {
15594
- return searchWithNode(rootPath, query, options.maxFiles);
15595
- }
15596
- throw error;
15597
- }
15598
- );
15599
- return finalizeResults(result, options.maxFiles, options.maxMatchesPerFile);
15600
16246
  }
15601
- async function searchWithRipgrep(rootPath, query, maxFiles) {
15602
- const hasGitignore = existsSync8(join12(rootPath, ".gitignore"));
15603
- const args = [
15604
- "--json",
15605
- "--line-number",
15606
- "--column",
15607
- "--fixed-strings",
15608
- "--sort",
15609
- "path",
15610
- "--with-filename",
15611
- "--glob",
15612
- "!**/.git/**",
15613
- "--glob",
15614
- "!**/node_modules/**"
15615
- ];
15616
- if (hasGitignore) {
15617
- args.push("--hidden");
15618
- args.push("--no-require-git");
15619
- }
15620
- args.push(query, ".");
15621
- return new Promise((resolve6, reject) => {
15622
- const child = spawn5("rg", args, { cwd: rootPath, stdio: ["ignore", "pipe", "pipe"] });
15623
- const stdout = createInterface({ input: child.stdout });
15624
- const files = /* @__PURE__ */ new Map();
15625
- let totalMatchCount = 0;
15626
- let hasMoreFiles = false;
15627
- let stderr = "";
15628
- stdout.on("line", (line) => {
15629
- if (!line.trim()) {
15630
- return;
15631
- }
15632
- const event = JSON.parse(line);
15633
- if (event.type !== "match") {
15634
- return;
15635
- }
15636
- const rawPath = event.data?.path?.text;
15637
- if (!rawPath) {
15638
- return;
15639
- }
15640
- const relativePath = normalizeRelativePath(relative3(rootPath, join12(rootPath, rawPath)));
15641
- const preview = (event.data?.lines?.text ?? "").replace(/\r?\n$/, "");
15642
- const lineNumber = event.data?.line_number ?? 1;
15643
- const submatches = event.data?.submatches ?? [];
15644
- totalMatchCount += submatches.length;
15645
- if (!files.has(relativePath) && files.size >= maxFiles) {
15646
- hasMoreFiles = true;
15647
- return;
16247
+ var init_session_metadata = __esm({
16248
+ "packages/server/src/commands/session-metadata.ts"() {
16249
+ "use strict";
16250
+ init_dispatch();
16251
+ registerCommand(
16252
+ "session.metadata.get",
16253
+ z14.object({
16254
+ sessionId: z14.string()
16255
+ }),
16256
+ async (args, ctx) => {
16257
+ requireSessionMetadataRepo(ctx);
16258
+ const metadata = ctx.sessionMetadataRepo.get(args.sessionId);
16259
+ if (!metadata) {
16260
+ throw {
16261
+ code: "session_metadata_not_found",
16262
+ message: `Session metadata not found: ${args.sessionId}`
16263
+ };
16264
+ }
16265
+ return metadata;
15648
16266
  }
15649
- for (const submatch of submatches) {
15650
- pushMatch(files, relativePath, {
15651
- line: lineNumber,
15652
- column: byteOffsetToColumn(preview, submatch.start),
15653
- endColumn: byteOffsetToColumn(preview, submatch.end),
15654
- preview,
15655
- previewColumnStart: byteOffsetToColumn(preview, submatch.start),
15656
- previewColumnEnd: byteOffsetToColumn(preview, submatch.end)
16267
+ );
16268
+ registerCommand(
16269
+ "session.verification.add",
16270
+ z14.object({
16271
+ sessionId: z14.string(),
16272
+ command: z14.string().trim().min(1),
16273
+ status: z14.enum(["passed", "failed", "unknown"]),
16274
+ exitCode: z14.number().int().optional(),
16275
+ summary: z14.string().optional()
16276
+ }),
16277
+ async (args, ctx) => {
16278
+ requireSessionMetadataRepo(ctx);
16279
+ return ctx.sessionMetadataRepo.addVerificationRun(args.sessionId, {
16280
+ id: randomUUID6(),
16281
+ command: args.command,
16282
+ status: args.status,
16283
+ exitCode: args.exitCode,
16284
+ summary: args.summary?.trim() || void 0,
16285
+ createdAt: Date.now()
15657
16286
  });
15658
16287
  }
15659
- });
15660
- child.stderr.on("data", (chunk) => {
15661
- stderr += chunk.toString();
15662
- });
15663
- child.on("error", (error) => {
15664
- void stdout.close();
15665
- reject(error);
15666
- });
15667
- child.on("close", (code) => {
15668
- void stdout.close();
15669
- if (code === 0 || code === 1) {
15670
- resolve6({
15671
- files: sortAccumulators(files),
15672
- totalMatchCount,
15673
- hasMoreFiles
15674
- });
15675
- return;
16288
+ );
16289
+ }
16290
+ });
16291
+
16292
+ // packages/server/src/git/diff.ts
16293
+ import { mkdtemp as mkdtemp3, readFile as readFile6, rm as rm7 } from "fs/promises";
16294
+ import os3 from "os";
16295
+ import path11 from "path";
16296
+ async function isTrackedPath(cwd, filePath) {
16297
+ try {
16298
+ await runGit(cwd, ["ls-files", "--error-unmatch", "--", filePath]);
16299
+ return true;
16300
+ } catch {
16301
+ return false;
16302
+ }
16303
+ }
16304
+ async function getUntrackedFileDiff(cwd, filePath) {
16305
+ const tempDir = await mkdtemp3(path11.join(os3.tmpdir(), "coder-studio-git-diff-"));
16306
+ const tempIndex = path11.join(tempDir, "index");
16307
+ try {
16308
+ try {
16309
+ await runGit(cwd, ["read-tree", "HEAD"], {
16310
+ env: { GIT_INDEX_FILE: tempIndex }
16311
+ });
16312
+ } catch (error) {
16313
+ if (!(error instanceof GitError)) {
16314
+ throw error;
16315
+ }
16316
+ await runGit(cwd, ["read-tree", "--empty"], {
16317
+ env: { GIT_INDEX_FILE: tempIndex }
16318
+ });
16319
+ }
16320
+ await runGit(cwd, ["add", "-N", "--", filePath], {
16321
+ env: { GIT_INDEX_FILE: tempIndex }
16322
+ });
16323
+ const result = await runGit(cwd, ["diff", "--", filePath], {
16324
+ env: { GIT_INDEX_FILE: tempIndex }
16325
+ });
16326
+ return result.stdout;
16327
+ } finally {
16328
+ await rm7(tempDir, { recursive: true, force: true });
16329
+ }
16330
+ }
16331
+ async function pathExists2(cwd, filePath) {
16332
+ try {
16333
+ await readFile6(resolveSafe(cwd, filePath));
16334
+ return true;
16335
+ } catch {
16336
+ return false;
16337
+ }
16338
+ }
16339
+ async function readTextAtRevision(cwd, revision, filePath) {
16340
+ if (revision === "WORKTREE") {
16341
+ return readFile6(resolveSafe(cwd, filePath), "utf-8");
16342
+ }
16343
+ try {
16344
+ const gitSpec = revision === "INDEX" ? `:${filePath}` : `${revision}:${filePath}`;
16345
+ const result = await runGit(cwd, ["show", gitSpec]);
16346
+ return result.stdout;
16347
+ } catch {
16348
+ return "";
16349
+ }
16350
+ }
16351
+ async function deriveFileDiffStatus(cwd, filePath, staged) {
16352
+ const tracked = await isTrackedPath(cwd, filePath);
16353
+ const existsOnDisk = await pathExists2(cwd, filePath);
16354
+ if (!staged && !tracked) {
16355
+ return "added";
16356
+ }
16357
+ if (staged) {
16358
+ try {
16359
+ await runGit(cwd, ["cat-file", "-e", `HEAD:${filePath}`]);
16360
+ return existsOnDisk ? "modified" : "deleted";
16361
+ } catch {
16362
+ return "added";
16363
+ }
16364
+ }
16365
+ return existsOnDisk ? "modified" : "deleted";
16366
+ }
16367
+ async function buildTextDiffResult(cwd, filePath, staged, diff) {
16368
+ const status = await deriveFileDiffStatus(cwd, filePath, staged);
16369
+ if (status === "added") {
16370
+ return {
16371
+ diff,
16372
+ renderAs: "text",
16373
+ status,
16374
+ originalContent: "",
16375
+ modifiedContent: await readTextAtRevision(cwd, staged ? "INDEX" : "WORKTREE", filePath)
16376
+ };
16377
+ }
16378
+ if (status === "deleted") {
16379
+ return {
16380
+ diff,
16381
+ renderAs: "text",
16382
+ status,
16383
+ originalContent: await readTextAtRevision(cwd, staged ? "HEAD" : "INDEX", filePath),
16384
+ modifiedContent: ""
16385
+ };
16386
+ }
16387
+ return {
16388
+ diff,
16389
+ renderAs: "text",
16390
+ status,
16391
+ originalContent: await readTextAtRevision(cwd, staged ? "HEAD" : "INDEX", filePath),
16392
+ modifiedContent: await readTextAtRevision(cwd, staged ? "INDEX" : "WORKTREE", filePath)
16393
+ };
16394
+ }
16395
+ async function getFileDiff(cwd, path14, staged = false) {
16396
+ const imageType = getImageTypeInfo(path14);
16397
+ if (!staged && !await isTrackedPath(cwd, path14)) {
16398
+ const diff = await getUntrackedFileDiff(cwd, path14);
16399
+ if (imageType) {
16400
+ return {
16401
+ diff,
16402
+ renderAs: "image",
16403
+ status: "added",
16404
+ originalRevision: "HEAD",
16405
+ modifiedRevision: "WORKTREE"
16406
+ };
16407
+ }
16408
+ return buildTextDiffResult(cwd, path14, staged, diff);
16409
+ }
16410
+ const args = staged ? ["diff", "--staged", "--", path14] : ["diff", "--", path14];
16411
+ const result = await runGit(cwd, args);
16412
+ if (imageType && /Binary files .* differ/.test(result.stdout)) {
16413
+ return {
16414
+ diff: result.stdout,
16415
+ renderAs: "image",
16416
+ status: await deriveFileDiffStatus(cwd, path14, staged),
16417
+ originalRevision: staged ? "HEAD" : "INDEX",
16418
+ modifiedRevision: staged ? "INDEX" : "WORKTREE"
16419
+ };
16420
+ }
16421
+ return buildTextDiffResult(cwd, path14, staged, result.stdout);
16422
+ }
16423
+ var init_diff = __esm({
16424
+ "packages/server/src/git/diff.ts"() {
16425
+ "use strict";
16426
+ init_file_io();
16427
+ init_image();
16428
+ init_cli();
16429
+ }
16430
+ });
16431
+
16432
+ // packages/server/src/session-review/review.ts
16433
+ function isWorkspaceStatePath(path14) {
16434
+ return path14 === WORKSPACE_STATE_DIR || path14.startsWith(`${WORKSPACE_STATE_DIR}/`);
16435
+ }
16436
+ function requireSessionMetadata(metadataRepo, sessionId) {
16437
+ const metadata = metadataRepo.get(sessionId);
16438
+ if (!metadata) {
16439
+ throw {
16440
+ code: "session_metadata_not_found",
16441
+ message: `Session metadata not found: ${sessionId}`
16442
+ };
16443
+ }
16444
+ return metadata;
16445
+ }
16446
+ async function isGitRepository(workspacePath) {
16447
+ try {
16448
+ await runGit(workspacePath, ["rev-parse", "--git-dir"]);
16449
+ return true;
16450
+ } catch (error) {
16451
+ if (error instanceof GitError) {
16452
+ return false;
16453
+ }
16454
+ throw error;
16455
+ }
16456
+ }
16457
+ function compareGitChanges(a, b) {
16458
+ const pathCompare = a.path.localeCompare(b.path);
16459
+ if (pathCompare !== 0) {
16460
+ return pathCompare;
16461
+ }
16462
+ return (a.oldPath ?? "").localeCompare(b.oldPath ?? "");
16463
+ }
16464
+ function mapGitStatus(code) {
16465
+ switch (code[0]) {
16466
+ case "A":
16467
+ return "added";
16468
+ case "D":
16469
+ return "deleted";
16470
+ case "M":
16471
+ default:
16472
+ return "modified";
16473
+ }
16474
+ }
16475
+ async function listTrackedChangesSinceBaseline(workspacePath, baselineGitHead) {
16476
+ const { stdout } = await runGit(workspacePath, [
16477
+ "diff",
16478
+ "--name-status",
16479
+ "--find-renames",
16480
+ baselineGitHead,
16481
+ "--"
16482
+ ]);
16483
+ const changes = stdout.split("\n").map((line) => line.trim()).filter((line) => line.length > 0).map((line) => {
16484
+ const parts = line.split(" ");
16485
+ const statusCode = parts[0] ?? "M";
16486
+ if (statusCode.startsWith("R")) {
16487
+ const oldPath = parts[1];
16488
+ const path15 = parts[2];
16489
+ if (!oldPath || !path15) {
16490
+ return null;
16491
+ }
16492
+ return {
16493
+ oldPath,
16494
+ path: path15,
16495
+ status: "renamed"
16496
+ };
16497
+ }
16498
+ const path14 = parts[1];
16499
+ if (!path14) {
16500
+ return null;
16501
+ }
16502
+ return {
16503
+ path: path14,
16504
+ status: mapGitStatus(statusCode)
16505
+ };
16506
+ });
16507
+ return changes.filter((change) => change !== null).filter((change) => !isWorkspaceStatePath(change.path)).sort(compareGitChanges);
16508
+ }
16509
+ async function listUntrackedChanges(workspacePath) {
16510
+ const { stdout } = await runGit(workspacePath, ["ls-files", "--others", "--exclude-standard"]);
16511
+ return stdout.split("\n").map((line) => line.trim()).filter((line) => line.length > 0).map((path14) => ({
16512
+ path: path14,
16513
+ status: "untracked"
16514
+ })).filter((change) => !isWorkspaceStatePath(change.path)).sort(compareGitChanges);
16515
+ }
16516
+ async function isUntrackedPath(workspacePath, filePath) {
16517
+ const { stdout } = await runGit(workspacePath, [
16518
+ "ls-files",
16519
+ "--others",
16520
+ "--exclude-standard",
16521
+ "--",
16522
+ filePath
16523
+ ]);
16524
+ return stdout.trim().length > 0;
16525
+ }
16526
+ async function buildSessionReviewSummary(input) {
16527
+ const metadata = requireSessionMetadata(input.metadataRepo, input.sessionId);
16528
+ if (!metadata.baselineGitHead) {
16529
+ return {
16530
+ sessionId: metadata.sessionId,
16531
+ workspaceId: metadata.workspaceId,
16532
+ baselineGitHead: metadata.baselineGitHead,
16533
+ changedFiles: [],
16534
+ verificationRuns: metadata.verificationRuns,
16535
+ warnings: [MISSING_BASELINE_WARNING]
16536
+ };
16537
+ }
16538
+ if (!await isGitRepository(input.workspacePath)) {
16539
+ return {
16540
+ sessionId: metadata.sessionId,
16541
+ workspaceId: metadata.workspaceId,
16542
+ baselineGitHead: metadata.baselineGitHead,
16543
+ changedFiles: [],
16544
+ verificationRuns: metadata.verificationRuns,
16545
+ warnings: [NOT_GIT_REPO_WARNING]
16546
+ };
16547
+ }
16548
+ const trackedChanges = await listTrackedChangesSinceBaseline(
16549
+ input.workspacePath,
16550
+ metadata.baselineGitHead
16551
+ );
16552
+ const trackedPaths = new Set(trackedChanges.map((change) => change.path));
16553
+ const untrackedChanges = (await listUntrackedChanges(input.workspacePath)).filter(
16554
+ (change) => !trackedPaths.has(change.path)
16555
+ );
16556
+ return {
16557
+ sessionId: metadata.sessionId,
16558
+ workspaceId: metadata.workspaceId,
16559
+ baselineGitHead: metadata.baselineGitHead,
16560
+ changedFiles: [...trackedChanges, ...untrackedChanges],
16561
+ verificationRuns: metadata.verificationRuns,
16562
+ warnings: []
16563
+ };
16564
+ }
16565
+ async function getSessionReviewDiff(input) {
16566
+ const metadata = requireSessionMetadata(input.metadataRepo, input.sessionId);
16567
+ if (!metadata.baselineGitHead) {
16568
+ return "";
16569
+ }
16570
+ if (!await isGitRepository(input.workspacePath)) {
16571
+ return "";
16572
+ }
16573
+ if (await isUntrackedPath(input.workspacePath, input.path)) {
16574
+ return (await getFileDiff(input.workspacePath, input.path)).diff;
16575
+ }
16576
+ const { stdout } = await runGit(input.workspacePath, [
16577
+ "diff",
16578
+ metadata.baselineGitHead,
16579
+ "--",
16580
+ input.path
16581
+ ]);
16582
+ return stdout;
16583
+ }
16584
+ var MISSING_BASELINE_WARNING, NOT_GIT_REPO_WARNING;
16585
+ var init_review = __esm({
16586
+ "packages/server/src/session-review/review.ts"() {
16587
+ "use strict";
16588
+ init_cli();
16589
+ init_diff();
16590
+ init_workspace_state();
16591
+ MISSING_BASELINE_WARNING = {
16592
+ code: "missing_baseline",
16593
+ message: "Session baseline is missing."
16594
+ };
16595
+ NOT_GIT_REPO_WARNING = {
16596
+ code: "not_git_repo",
16597
+ message: "Workspace is not a Git repository."
16598
+ };
16599
+ }
16600
+ });
16601
+
16602
+ // packages/server/src/commands/session-review.ts
16603
+ import { z as z15 } from "zod";
16604
+ function requireSessionMetadataRepo2(ctx) {
16605
+ if (!ctx.sessionMetadataRepo) {
16606
+ throw {
16607
+ code: "session_metadata_unavailable",
16608
+ message: "Session metadata repository is not configured"
16609
+ };
16610
+ }
16611
+ }
16612
+ function requireWorkspace(ctx, workspaceId) {
16613
+ const workspace = ctx.workspaceMgr.get(workspaceId);
16614
+ if (!workspace) {
16615
+ throw { code: "workspace_not_found", message: `Workspace not found: ${workspaceId}` };
16616
+ }
16617
+ return workspace;
16618
+ }
16619
+ var init_session_review = __esm({
16620
+ "packages/server/src/commands/session-review.ts"() {
16621
+ "use strict";
16622
+ init_review();
16623
+ init_dispatch();
16624
+ registerCommand(
16625
+ "sessionReview.summary",
16626
+ z15.object({
16627
+ sessionId: z15.string()
16628
+ }),
16629
+ async (args, ctx) => {
16630
+ requireSessionMetadataRepo2(ctx);
16631
+ const metadata = ctx.sessionMetadataRepo.get(args.sessionId);
16632
+ if (!metadata) {
16633
+ throw {
16634
+ code: "session_metadata_not_found",
16635
+ message: `Session metadata not found: ${args.sessionId}`
16636
+ };
16637
+ }
16638
+ const workspace = requireWorkspace(ctx, metadata.workspaceId);
16639
+ return buildSessionReviewSummary({
16640
+ sessionId: args.sessionId,
16641
+ workspacePath: workspace.path,
16642
+ metadataRepo: ctx.sessionMetadataRepo
16643
+ });
16644
+ }
16645
+ );
16646
+ registerCommand(
16647
+ "sessionReview.diff",
16648
+ z15.object({
16649
+ sessionId: z15.string(),
16650
+ path: z15.string().trim().min(1)
16651
+ }),
16652
+ async (args, ctx) => {
16653
+ requireSessionMetadataRepo2(ctx);
16654
+ const metadata = ctx.sessionMetadataRepo.get(args.sessionId);
16655
+ if (!metadata) {
16656
+ throw {
16657
+ code: "session_metadata_not_found",
16658
+ message: `Session metadata not found: ${args.sessionId}`
16659
+ };
16660
+ }
16661
+ const workspace = requireWorkspace(ctx, metadata.workspaceId);
16662
+ return {
16663
+ path: args.path,
16664
+ diff: await getSessionReviewDiff({
16665
+ sessionId: args.sessionId,
16666
+ workspacePath: workspace.path,
16667
+ metadataRepo: ctx.sessionMetadataRepo,
16668
+ path: args.path
16669
+ })
16670
+ };
16671
+ }
16672
+ );
16673
+ }
16674
+ });
16675
+
16676
+ // packages/server/src/fs/content-search.ts
16677
+ import { spawn as spawn5 } from "child_process";
16678
+ import { existsSync as existsSync8 } from "fs";
16679
+ import { readdir as readdir4, readFile as readFile7, stat as stat10 } from "fs/promises";
16680
+ import { basename as basename2, join as join15, relative as relative3 } from "path";
16681
+ import { createInterface } from "readline";
16682
+ async function searchFileContents(rootPath, options) {
16683
+ const query = options.query.trim();
16684
+ if (!query) {
16685
+ return {
16686
+ files: [],
16687
+ totalMatchCount: 0,
16688
+ hasMoreFiles: false,
16689
+ truncatedMatchFileCount: 0
16690
+ };
16691
+ }
16692
+ const result = await searchWithRipgrep(rootPath, query, options.maxFiles).catch(
16693
+ async (error) => {
16694
+ if (error.code === "ENOENT") {
16695
+ return searchWithNode(rootPath, query, options.maxFiles);
16696
+ }
16697
+ throw error;
16698
+ }
16699
+ );
16700
+ return finalizeResults(result, options.maxFiles, options.maxMatchesPerFile);
16701
+ }
16702
+ async function searchWithRipgrep(rootPath, query, maxFiles) {
16703
+ const hasGitignore = existsSync8(join15(rootPath, ".gitignore"));
16704
+ const args = [
16705
+ "--json",
16706
+ "--line-number",
16707
+ "--column",
16708
+ "--fixed-strings",
16709
+ "--sort",
16710
+ "path",
16711
+ "--with-filename",
16712
+ "--glob",
16713
+ "!**/.git/**",
16714
+ "--glob",
16715
+ "!**/node_modules/**"
16716
+ ];
16717
+ if (hasGitignore) {
16718
+ args.push("--hidden");
16719
+ args.push("--no-require-git");
16720
+ }
16721
+ args.push(query, ".");
16722
+ return new Promise((resolve8, reject) => {
16723
+ const child = spawn5("rg", args, { cwd: rootPath, stdio: ["ignore", "pipe", "pipe"] });
16724
+ const stdout = createInterface({ input: child.stdout });
16725
+ const files = /* @__PURE__ */ new Map();
16726
+ let totalMatchCount = 0;
16727
+ let hasMoreFiles = false;
16728
+ let stderr = "";
16729
+ stdout.on("line", (line) => {
16730
+ if (!line.trim()) {
16731
+ return;
16732
+ }
16733
+ const event = JSON.parse(line);
16734
+ if (event.type !== "match") {
16735
+ return;
16736
+ }
16737
+ const rawPath = event.data?.path?.text;
16738
+ if (!rawPath) {
16739
+ return;
16740
+ }
16741
+ const relativePath = normalizeRelativePath(relative3(rootPath, join15(rootPath, rawPath)));
16742
+ const preview = (event.data?.lines?.text ?? "").replace(/\r?\n$/, "");
16743
+ const lineNumber = event.data?.line_number ?? 1;
16744
+ const submatches = event.data?.submatches ?? [];
16745
+ totalMatchCount += submatches.length;
16746
+ if (!files.has(relativePath) && files.size >= maxFiles) {
16747
+ hasMoreFiles = true;
16748
+ return;
16749
+ }
16750
+ for (const submatch of submatches) {
16751
+ pushMatch(files, relativePath, {
16752
+ line: lineNumber,
16753
+ column: byteOffsetToColumn(preview, submatch.start),
16754
+ endColumn: byteOffsetToColumn(preview, submatch.end),
16755
+ preview,
16756
+ previewColumnStart: byteOffsetToColumn(preview, submatch.start),
16757
+ previewColumnEnd: byteOffsetToColumn(preview, submatch.end)
16758
+ });
16759
+ }
16760
+ });
16761
+ child.stderr.on("data", (chunk) => {
16762
+ stderr += chunk.toString();
16763
+ });
16764
+ child.on("error", (error) => {
16765
+ void stdout.close();
16766
+ reject(error);
16767
+ });
16768
+ child.on("close", (code) => {
16769
+ void stdout.close();
16770
+ if (code === 0 || code === 1) {
16771
+ resolve8({
16772
+ files: sortAccumulators(files),
16773
+ totalMatchCount,
16774
+ hasMoreFiles
16775
+ });
16776
+ return;
15676
16777
  }
15677
16778
  reject(
15678
16779
  Object.assign(new Error(stderr || `rg exited with code ${code ?? "unknown"}`), { code })
@@ -15690,7 +16791,7 @@ async function searchWithNode(rootPath, query, maxFiles) {
15690
16791
  const filteredEntries = entries.filter((entry) => filter(entry.name));
15691
16792
  filteredEntries.sort((a, b) => a.name.localeCompare(b.name));
15692
16793
  for (const entry of filteredEntries) {
15693
- const fullPath = join12(dirPath, entry.name);
16794
+ const fullPath = join15(dirPath, entry.name);
15694
16795
  if (entry.isDirectory()) {
15695
16796
  await walk(fullPath);
15696
16797
  continue;
@@ -15698,11 +16799,11 @@ async function searchWithNode(rootPath, query, maxFiles) {
15698
16799
  if (!entry.isFile()) {
15699
16800
  continue;
15700
16801
  }
15701
- const fileStat = await stat9(fullPath);
16802
+ const fileStat = await stat10(fullPath);
15702
16803
  if (fileStat.size > FALLBACK_MAX_FILE_BYTES) {
15703
16804
  continue;
15704
16805
  }
15705
- const buffer = await readFile4(fullPath);
16806
+ const buffer = await readFile7(fullPath);
15706
16807
  if (isBinaryFile(buffer)) {
15707
16808
  continue;
15708
16809
  }
@@ -15813,10 +16914,10 @@ var init_content_search = __esm({
15813
16914
  });
15814
16915
 
15815
16916
  // packages/server/src/fs/tree.ts
15816
- import { readdir as readdir5, stat as stat10 } from "fs/promises";
15817
- import { join as join13, relative as relative4 } from "path";
16917
+ import { readdir as readdir5, stat as stat11 } from "fs/promises";
16918
+ import { join as join16, relative as relative4 } from "path";
15818
16919
  async function readTree(rootPath, subdir) {
15819
- const targetPath = subdir ? join13(rootPath, subdir) : rootPath;
16920
+ const targetPath = subdir ? join16(rootPath, subdir) : rootPath;
15820
16921
  const filter = createTreeVisibilityFilter();
15821
16922
  const entries = await readdir5(targetPath, { withFileTypes: true });
15822
16923
  const nodes = [];
@@ -15824,7 +16925,7 @@ async function readTree(rootPath, subdir) {
15824
16925
  if (!filter(entry.name)) {
15825
16926
  continue;
15826
16927
  }
15827
- const fullPath = join13(targetPath, entry.name);
16928
+ const fullPath = join16(targetPath, entry.name);
15828
16929
  const relPath = relative4(rootPath, fullPath);
15829
16930
  if (entry.isDirectory()) {
15830
16931
  nodes.push({
@@ -15835,7 +16936,7 @@ async function readTree(rootPath, subdir) {
15835
16936
  // Not loaded yet - client will request on expand
15836
16937
  });
15837
16938
  } else if (entry.isFile()) {
15838
- const stats = await stat10(fullPath);
16939
+ const stats = await stat11(fullPath);
15839
16940
  nodes.push({
15840
16941
  name: entry.name,
15841
16942
  path: relPath,
@@ -15868,7 +16969,7 @@ async function searchFiles(rootPath, query, limit = 10) {
15868
16969
  const filteredEntries = entries.filter((entry) => filter(entry.name));
15869
16970
  filteredEntries.sort((a, b) => a.name.localeCompare(b.name));
15870
16971
  for (const entry of filteredEntries) {
15871
- const fullPath = join13(dirPath, entry.name);
16972
+ const fullPath = join16(dirPath, entry.name);
15872
16973
  const relPath = relative4(rootPath, fullPath);
15873
16974
  if (entry.isDirectory()) {
15874
16975
  await walk(fullPath);
@@ -15904,7 +17005,7 @@ async function searchFiles(rootPath, query, limit = 10) {
15904
17005
  }
15905
17006
  return a.path.toLowerCase().localeCompare(b.path.toLowerCase());
15906
17007
  }).slice(0, limit)) {
15907
- const stats = await stat10(match.fullPath);
17008
+ const stats = await stat11(match.fullPath);
15908
17009
  files.push({
15909
17010
  name: match.name,
15910
17011
  path: match.path,
@@ -15984,7 +17085,7 @@ var init_tree = __esm({
15984
17085
  });
15985
17086
 
15986
17087
  // packages/server/src/commands/file.ts
15987
- import { z as z13 } from "zod";
17088
+ import { z as z16 } from "zod";
15988
17089
  var init_file = __esm({
15989
17090
  "packages/server/src/commands/file.ts"() {
15990
17091
  "use strict";
@@ -15994,9 +17095,9 @@ var init_file = __esm({
15994
17095
  init_dispatch();
15995
17096
  registerCommand(
15996
17097
  "file.readTree",
15997
- z13.object({
15998
- workspaceId: z13.string(),
15999
- subPath: z13.string().optional()
17098
+ z16.object({
17099
+ workspaceId: z16.string(),
17100
+ subPath: z16.string().optional()
16000
17101
  }),
16001
17102
  async (args, ctx) => {
16002
17103
  const workspace = ctx.workspaceMgr.get(args.workspaceId);
@@ -16008,10 +17109,10 @@ var init_file = __esm({
16008
17109
  );
16009
17110
  registerCommand(
16010
17111
  "file.search",
16011
- z13.object({
16012
- workspaceId: z13.string(),
16013
- query: z13.string(),
16014
- limit: z13.number().int().positive().max(50).optional()
17112
+ z16.object({
17113
+ workspaceId: z16.string(),
17114
+ query: z16.string(),
17115
+ limit: z16.number().int().positive().max(50).optional()
16015
17116
  }),
16016
17117
  async (args, ctx) => {
16017
17118
  const workspace = ctx.workspaceMgr.get(args.workspaceId);
@@ -16023,11 +17124,11 @@ var init_file = __esm({
16023
17124
  );
16024
17125
  registerCommand(
16025
17126
  "file.searchContent",
16026
- z13.object({
16027
- workspaceId: z13.string(),
16028
- query: z13.string(),
16029
- maxFiles: z13.number().int().positive().max(100),
16030
- maxMatchesPerFile: z13.number().int().positive().max(100)
17127
+ z16.object({
17128
+ workspaceId: z16.string(),
17129
+ query: z16.string(),
17130
+ maxFiles: z16.number().int().positive().max(100),
17131
+ maxMatchesPerFile: z16.number().int().positive().max(100)
16031
17132
  }),
16032
17133
  async (args, ctx) => {
16033
17134
  const workspace = ctx.workspaceMgr.get(args.workspaceId);
@@ -16043,9 +17144,9 @@ var init_file = __esm({
16043
17144
  );
16044
17145
  registerCommand(
16045
17146
  "file.read",
16046
- z13.object({
16047
- workspaceId: z13.string(),
16048
- path: z13.string()
17147
+ z16.object({
17148
+ workspaceId: z16.string(),
17149
+ path: z16.string()
16049
17150
  }),
16050
17151
  async (args, ctx) => {
16051
17152
  const workspace = ctx.workspaceMgr.get(args.workspaceId);
@@ -16057,9 +17158,9 @@ var init_file = __esm({
16057
17158
  );
16058
17159
  registerCommand(
16059
17160
  "file.create",
16060
- z13.object({
16061
- workspaceId: z13.string(),
16062
- path: z13.string()
17161
+ z16.object({
17162
+ workspaceId: z16.string(),
17163
+ path: z16.string()
16063
17164
  }),
16064
17165
  async (args, ctx) => {
16065
17166
  const workspace = ctx.workspaceMgr.get(args.workspaceId);
@@ -16077,9 +17178,9 @@ var init_file = __esm({
16077
17178
  );
16078
17179
  registerCommand(
16079
17180
  "file.mkdir",
16080
- z13.object({
16081
- workspaceId: z13.string(),
16082
- path: z13.string()
17181
+ z16.object({
17182
+ workspaceId: z16.string(),
17183
+ path: z16.string()
16083
17184
  }),
16084
17185
  async (args, ctx) => {
16085
17186
  const workspace = ctx.workspaceMgr.get(args.workspaceId);
@@ -16097,9 +17198,9 @@ var init_file = __esm({
16097
17198
  );
16098
17199
  registerCommand(
16099
17200
  "file.delete",
16100
- z13.object({
16101
- workspaceId: z13.string(),
16102
- path: z13.string()
17201
+ z16.object({
17202
+ workspaceId: z16.string(),
17203
+ path: z16.string()
16103
17204
  }),
16104
17205
  async (args, ctx) => {
16105
17206
  const workspace = ctx.workspaceMgr.get(args.workspaceId);
@@ -16117,10 +17218,10 @@ var init_file = __esm({
16117
17218
  );
16118
17219
  registerCommand(
16119
17220
  "file.rename",
16120
- z13.object({
16121
- workspaceId: z13.string(),
16122
- fromPath: z13.string(),
16123
- toPath: z13.string()
17221
+ z16.object({
17222
+ workspaceId: z16.string(),
17223
+ fromPath: z16.string(),
17224
+ toPath: z16.string()
16124
17225
  }),
16125
17226
  async (args, ctx) => {
16126
17227
  const workspace = ctx.workspaceMgr.get(args.workspaceId);
@@ -16138,167 +17239,27 @@ var init_file = __esm({
16138
17239
  );
16139
17240
  registerCommand(
16140
17241
  "file.write",
16141
- z13.object({
16142
- workspaceId: z13.string(),
16143
- path: z13.string(),
16144
- content: z13.string(),
16145
- baseHash: z13.string().optional()
17242
+ z16.object({
17243
+ workspaceId: z16.string(),
17244
+ path: z16.string(),
17245
+ content: z16.string(),
17246
+ baseHash: z16.string().optional()
16146
17247
  // For conflict detection
16147
17248
  }),
16148
- async (args, ctx) => {
16149
- const workspace = ctx.workspaceMgr.get(args.workspaceId);
16150
- if (!workspace) {
16151
- throw { code: "workspace_not_found", message: `Workspace not found: ${args.workspaceId}` };
16152
- }
16153
- const result = await writeFile(workspace.path, args.path, args.content, args.baseHash);
16154
- ctx.eventBus.emit({
16155
- type: "fs.dirty",
16156
- workspaceId: args.workspaceId,
16157
- reason: "file_content"
16158
- });
16159
- return result;
16160
- }
16161
- );
16162
- }
16163
- });
16164
-
16165
- // packages/server/src/git/diff.ts
16166
- import { mkdtemp as mkdtemp3, readFile as readFile5, rm as rm7 } from "fs/promises";
16167
- import os3 from "os";
16168
- import path11 from "path";
16169
- async function isTrackedPath(cwd, filePath) {
16170
- try {
16171
- await runGit(cwd, ["ls-files", "--error-unmatch", "--", filePath]);
16172
- return true;
16173
- } catch {
16174
- return false;
16175
- }
16176
- }
16177
- async function getUntrackedFileDiff(cwd, filePath) {
16178
- const tempDir = await mkdtemp3(path11.join(os3.tmpdir(), "coder-studio-git-diff-"));
16179
- const tempIndex = path11.join(tempDir, "index");
16180
- try {
16181
- try {
16182
- await runGit(cwd, ["read-tree", "HEAD"], {
16183
- env: { GIT_INDEX_FILE: tempIndex }
16184
- });
16185
- } catch (error) {
16186
- if (!(error instanceof GitError)) {
16187
- throw error;
16188
- }
16189
- await runGit(cwd, ["read-tree", "--empty"], {
16190
- env: { GIT_INDEX_FILE: tempIndex }
16191
- });
16192
- }
16193
- await runGit(cwd, ["add", "-N", "--", filePath], {
16194
- env: { GIT_INDEX_FILE: tempIndex }
16195
- });
16196
- const result = await runGit(cwd, ["diff", "--", filePath], {
16197
- env: { GIT_INDEX_FILE: tempIndex }
16198
- });
16199
- return result.stdout;
16200
- } finally {
16201
- await rm7(tempDir, { recursive: true, force: true });
16202
- }
16203
- }
16204
- async function pathExists(cwd, filePath) {
16205
- try {
16206
- await readFile5(resolveSafe(cwd, filePath));
16207
- return true;
16208
- } catch {
16209
- return false;
16210
- }
16211
- }
16212
- async function readTextAtRevision(cwd, revision, filePath) {
16213
- if (revision === "WORKTREE") {
16214
- return readFile5(resolveSafe(cwd, filePath), "utf-8");
16215
- }
16216
- try {
16217
- const gitSpec = revision === "INDEX" ? `:${filePath}` : `${revision}:${filePath}`;
16218
- const result = await runGit(cwd, ["show", gitSpec]);
16219
- return result.stdout;
16220
- } catch {
16221
- return "";
16222
- }
16223
- }
16224
- async function deriveFileDiffStatus(cwd, filePath, staged) {
16225
- const tracked = await isTrackedPath(cwd, filePath);
16226
- const existsOnDisk = await pathExists(cwd, filePath);
16227
- if (!staged && !tracked) {
16228
- return "added";
16229
- }
16230
- if (staged) {
16231
- try {
16232
- await runGit(cwd, ["cat-file", "-e", `HEAD:${filePath}`]);
16233
- return existsOnDisk ? "modified" : "deleted";
16234
- } catch {
16235
- return "added";
16236
- }
16237
- }
16238
- return existsOnDisk ? "modified" : "deleted";
16239
- }
16240
- async function buildTextDiffResult(cwd, filePath, staged, diff) {
16241
- const status = await deriveFileDiffStatus(cwd, filePath, staged);
16242
- if (status === "added") {
16243
- return {
16244
- diff,
16245
- renderAs: "text",
16246
- status,
16247
- originalContent: "",
16248
- modifiedContent: await readTextAtRevision(cwd, staged ? "INDEX" : "WORKTREE", filePath)
16249
- };
16250
- }
16251
- if (status === "deleted") {
16252
- return {
16253
- diff,
16254
- renderAs: "text",
16255
- status,
16256
- originalContent: await readTextAtRevision(cwd, staged ? "HEAD" : "INDEX", filePath),
16257
- modifiedContent: ""
16258
- };
16259
- }
16260
- return {
16261
- diff,
16262
- renderAs: "text",
16263
- status,
16264
- originalContent: await readTextAtRevision(cwd, staged ? "HEAD" : "INDEX", filePath),
16265
- modifiedContent: await readTextAtRevision(cwd, staged ? "INDEX" : "WORKTREE", filePath)
16266
- };
16267
- }
16268
- async function getFileDiff(cwd, path14, staged = false) {
16269
- const imageType = getImageTypeInfo(path14);
16270
- if (!staged && !await isTrackedPath(cwd, path14)) {
16271
- const diff = await getUntrackedFileDiff(cwd, path14);
16272
- if (imageType) {
16273
- return {
16274
- diff,
16275
- renderAs: "image",
16276
- status: "added",
16277
- originalRevision: "HEAD",
16278
- modifiedRevision: "WORKTREE"
16279
- };
16280
- }
16281
- return buildTextDiffResult(cwd, path14, staged, diff);
16282
- }
16283
- const args = staged ? ["diff", "--staged", "--", path14] : ["diff", "--", path14];
16284
- const result = await runGit(cwd, args);
16285
- if (imageType && /Binary files .* differ/.test(result.stdout)) {
16286
- return {
16287
- diff: result.stdout,
16288
- renderAs: "image",
16289
- status: await deriveFileDiffStatus(cwd, path14, staged),
16290
- originalRevision: staged ? "HEAD" : "INDEX",
16291
- modifiedRevision: staged ? "INDEX" : "WORKTREE"
16292
- };
16293
- }
16294
- return buildTextDiffResult(cwd, path14, staged, result.stdout);
16295
- }
16296
- var init_diff = __esm({
16297
- "packages/server/src/git/diff.ts"() {
16298
- "use strict";
16299
- init_file_io();
16300
- init_image();
16301
- init_cli();
17249
+ async (args, ctx) => {
17250
+ const workspace = ctx.workspaceMgr.get(args.workspaceId);
17251
+ if (!workspace) {
17252
+ throw { code: "workspace_not_found", message: `Workspace not found: ${args.workspaceId}` };
17253
+ }
17254
+ const result = await writeFile(workspace.path, args.path, args.content, args.baseHash);
17255
+ ctx.eventBus.emit({
17256
+ type: "fs.dirty",
17257
+ workspaceId: args.workspaceId,
17258
+ reason: "file_content"
17259
+ });
17260
+ return result;
17261
+ }
17262
+ );
16302
17263
  }
16303
17264
  });
16304
17265
 
@@ -16319,7 +17280,7 @@ var init_git_events = __esm({
16319
17280
  });
16320
17281
 
16321
17282
  // packages/server/src/commands/git.ts
16322
- import { z as z14 } from "zod";
17283
+ import { z as z17 } from "zod";
16323
17284
  async function runGitNetworkOperation(ctx, workspaceId, op) {
16324
17285
  if (!ctx.autoFetch?.runExclusive) {
16325
17286
  return op();
@@ -16334,16 +17295,16 @@ var init_git2 = __esm({
16334
17295
  init_diff();
16335
17296
  init_dispatch();
16336
17297
  init_git_events();
16337
- gitHttpAuthSchema = z14.object({
16338
- username: z14.string(),
16339
- password: z14.string()
17298
+ gitHttpAuthSchema = z17.object({
17299
+ username: z17.string(),
17300
+ password: z17.string()
16340
17301
  });
16341
- gitCommitRevisionSchema = z14.string().regex(/^[0-9a-fA-F]{7,64}$/, "Invalid git commit revision");
17302
+ gitCommitRevisionSchema = z17.string().regex(/^[0-9a-fA-F]{7,64}$/, "Invalid git commit revision");
16342
17303
  GIT_BACKGROUND_FETCH_TIMEOUT_MS = 30 * 1e3;
16343
17304
  registerCommand(
16344
17305
  "git.status",
16345
- z14.object({
16346
- workspaceId: z14.string()
17306
+ z17.object({
17307
+ workspaceId: z17.string()
16347
17308
  }),
16348
17309
  async (args, ctx) => {
16349
17310
  const workspace = ctx.workspaceMgr.get(args.workspaceId);
@@ -16355,9 +17316,9 @@ var init_git2 = __esm({
16355
17316
  );
16356
17317
  registerCommand(
16357
17318
  "git.stage",
16358
- z14.object({
16359
- workspaceId: z14.string(),
16360
- paths: z14.array(z14.string())
17319
+ z17.object({
17320
+ workspaceId: z17.string(),
17321
+ paths: z17.array(z17.string())
16361
17322
  }),
16362
17323
  async (args, ctx) => {
16363
17324
  const workspace = ctx.workspaceMgr.get(args.workspaceId);
@@ -16371,10 +17332,10 @@ var init_git2 = __esm({
16371
17332
  );
16372
17333
  registerCommand(
16373
17334
  "git.diff",
16374
- z14.object({
16375
- workspaceId: z14.string(),
16376
- path: z14.string(),
16377
- staged: z14.boolean().optional()
17335
+ z17.object({
17336
+ workspaceId: z17.string(),
17337
+ path: z17.string(),
17338
+ staged: z17.boolean().optional()
16378
17339
  }),
16379
17340
  async (args, ctx) => {
16380
17341
  const workspace = ctx.workspaceMgr.get(args.workspaceId);
@@ -16386,9 +17347,9 @@ var init_git2 = __esm({
16386
17347
  );
16387
17348
  registerCommand(
16388
17349
  "git.log",
16389
- z14.object({
16390
- workspaceId: z14.string(),
16391
- limit: z14.number().int().min(1).max(50).optional()
17350
+ z17.object({
17351
+ workspaceId: z17.string(),
17352
+ limit: z17.number().int().min(1).max(50).optional()
16392
17353
  }),
16393
17354
  async (args, ctx) => {
16394
17355
  const workspace = ctx.workspaceMgr.get(args.workspaceId);
@@ -16402,8 +17363,8 @@ var init_git2 = __esm({
16402
17363
  );
16403
17364
  registerCommand(
16404
17365
  "git.show",
16405
- z14.object({
16406
- workspaceId: z14.string(),
17366
+ z17.object({
17367
+ workspaceId: z17.string(),
16407
17368
  sha: gitCommitRevisionSchema
16408
17369
  }),
16409
17370
  async (args, ctx) => {
@@ -16418,9 +17379,9 @@ var init_git2 = __esm({
16418
17379
  );
16419
17380
  registerCommand(
16420
17381
  "git.unstage",
16421
- z14.object({
16422
- workspaceId: z14.string(),
16423
- paths: z14.array(z14.string())
17382
+ z17.object({
17383
+ workspaceId: z17.string(),
17384
+ paths: z17.array(z17.string())
16424
17385
  }),
16425
17386
  async (args, ctx) => {
16426
17387
  const workspace = ctx.workspaceMgr.get(args.workspaceId);
@@ -16434,9 +17395,9 @@ var init_git2 = __esm({
16434
17395
  );
16435
17396
  registerCommand(
16436
17397
  "git.discard",
16437
- z14.object({
16438
- workspaceId: z14.string(),
16439
- paths: z14.array(z14.string())
17398
+ z17.object({
17399
+ workspaceId: z17.string(),
17400
+ paths: z17.array(z17.string())
16440
17401
  }),
16441
17402
  async (args, ctx) => {
16442
17403
  const workspace = ctx.workspaceMgr.get(args.workspaceId);
@@ -16452,9 +17413,9 @@ var init_git2 = __esm({
16452
17413
  );
16453
17414
  registerCommand(
16454
17415
  "git.commit",
16455
- z14.object({
16456
- workspaceId: z14.string(),
16457
- message: z14.string()
17416
+ z17.object({
17417
+ workspaceId: z17.string(),
17418
+ message: z17.string()
16458
17419
  }),
16459
17420
  async (args, ctx) => {
16460
17421
  const workspace = ctx.workspaceMgr.get(args.workspaceId);
@@ -16471,11 +17432,11 @@ var init_git2 = __esm({
16471
17432
  );
16472
17433
  registerCommand(
16473
17434
  "git.push",
16474
- z14.object({
16475
- workspaceId: z14.string(),
16476
- remote: z14.string().optional(),
16477
- branch: z14.string().optional(),
16478
- force: z14.boolean().optional(),
17435
+ z17.object({
17436
+ workspaceId: z17.string(),
17437
+ remote: z17.string().optional(),
17438
+ branch: z17.string().optional(),
17439
+ force: z17.boolean().optional(),
16479
17440
  auth: gitHttpAuthSchema.optional()
16480
17441
  }),
16481
17442
  async (args, ctx) => {
@@ -16501,128 +17462,737 @@ var init_git2 = __esm({
16501
17462
  }
16502
17463
  );
16503
17464
  registerCommand(
16504
- "git.pull",
16505
- z14.object({
16506
- workspaceId: z14.string(),
16507
- remote: z14.string().optional(),
16508
- branch: z14.string().optional(),
16509
- auth: gitHttpAuthSchema.optional()
17465
+ "git.pull",
17466
+ z17.object({
17467
+ workspaceId: z17.string(),
17468
+ remote: z17.string().optional(),
17469
+ branch: z17.string().optional(),
17470
+ auth: gitHttpAuthSchema.optional()
17471
+ }),
17472
+ async (args, ctx) => {
17473
+ const workspace = ctx.workspaceMgr.get(args.workspaceId);
17474
+ if (!workspace) {
17475
+ throw { code: "workspace_not_found", message: `Workspace not found: ${args.workspaceId}` };
17476
+ }
17477
+ const result = await runGitNetworkOperation(
17478
+ ctx,
17479
+ args.workspaceId,
17480
+ () => runGitPull(workspace.path, {
17481
+ remote: args.remote,
17482
+ branch: args.branch,
17483
+ auth: args.auth
17484
+ })
17485
+ );
17486
+ ctx.workspaceMgr.recordFetch(args.workspaceId);
17487
+ emitGitStateChanged(ctx, args.workspaceId, {
17488
+ treeChanged: true,
17489
+ branchChanged: true,
17490
+ worktreeChanged: true
17491
+ });
17492
+ return result;
17493
+ }
17494
+ );
17495
+ registerCommand(
17496
+ "git.fetch",
17497
+ z17.object({
17498
+ workspaceId: z17.string(),
17499
+ remote: z17.string().optional(),
17500
+ prune: z17.boolean().optional(),
17501
+ auth: gitHttpAuthSchema.optional(),
17502
+ background: z17.boolean().optional()
17503
+ }),
17504
+ async (args, ctx, clientId) => {
17505
+ const workspace = ctx.workspaceMgr.get(args.workspaceId);
17506
+ if (!workspace) {
17507
+ throw { code: "workspace_not_found", message: `Workspace not found: ${args.workspaceId}` };
17508
+ }
17509
+ try {
17510
+ const isInternalBackgroundFetch = args.background === true && !clientId;
17511
+ const runFetch = () => runGitFetch(workspace.path, {
17512
+ remote: args.remote,
17513
+ prune: args.prune,
17514
+ auth: args.auth,
17515
+ timeoutMs: args.background ? GIT_BACKGROUND_FETCH_TIMEOUT_MS : void 0
17516
+ });
17517
+ const result = isInternalBackgroundFetch ? await runFetch() : await runGitNetworkOperation(ctx, args.workspaceId, runFetch);
17518
+ ctx.workspaceMgr.recordFetch(args.workspaceId);
17519
+ emitGitStateChanged(ctx, args.workspaceId, { branchChanged: true });
17520
+ return result;
17521
+ } catch (err) {
17522
+ if (args.background && err instanceof GitAuthError) {
17523
+ return { success: false, message: err.message, updatedRefs: [] };
17524
+ }
17525
+ throw err;
17526
+ }
17527
+ }
17528
+ );
17529
+ registerCommand(
17530
+ "git.checkout",
17531
+ z17.object({
17532
+ workspaceId: z17.string(),
17533
+ ref: z17.string(),
17534
+ createBranch: z17.boolean().optional()
17535
+ }),
17536
+ async (args, ctx) => {
17537
+ const workspace = ctx.workspaceMgr.get(args.workspaceId);
17538
+ if (!workspace) {
17539
+ throw { code: "workspace_not_found", message: `Workspace not found: ${args.workspaceId}` };
17540
+ }
17541
+ const result = await runGitCheckout(workspace.path, args.ref, {
17542
+ createBranch: args.createBranch
17543
+ });
17544
+ if (result.success) {
17545
+ emitGitStateChanged(ctx, args.workspaceId, {
17546
+ treeChanged: true,
17547
+ branchChanged: true,
17548
+ worktreeChanged: true
17549
+ });
17550
+ }
17551
+ return result;
17552
+ }
17553
+ );
17554
+ registerCommand(
17555
+ "git.branch",
17556
+ z17.object({
17557
+ workspaceId: z17.string(),
17558
+ name: z17.string(),
17559
+ startPoint: z17.string().optional()
17560
+ }),
17561
+ async (args, ctx) => {
17562
+ const workspace = ctx.workspaceMgr.get(args.workspaceId);
17563
+ if (!workspace) {
17564
+ throw { code: "workspace_not_found", message: `Workspace not found: ${args.workspaceId}` };
17565
+ }
17566
+ const result = await runGitCreateBranch(workspace.path, args.name, {
17567
+ startPoint: args.startPoint
17568
+ });
17569
+ emitGitStateChanged(ctx, args.workspaceId, {
17570
+ branchChanged: true,
17571
+ worktreeChanged: true
17572
+ });
17573
+ return result;
17574
+ }
17575
+ );
17576
+ registerCommand(
17577
+ "git.branches",
17578
+ z17.object({
17579
+ workspaceId: z17.string()
17580
+ }),
17581
+ async (args, ctx) => {
17582
+ const workspace = ctx.workspaceMgr.get(args.workspaceId);
17583
+ if (!workspace) {
17584
+ throw { code: "workspace_not_found", message: `Workspace not found: ${args.workspaceId}` };
17585
+ }
17586
+ return runGitListBranches(workspace.path);
17587
+ }
17588
+ );
17589
+ }
17590
+ });
17591
+
17592
+ // packages/server/src/agent-instructions/generator.ts
17593
+ function buildAgentInstructionsMarkdown(summary) {
17594
+ const lines = ["# Agent Instructions", ""];
17595
+ pushSection(lines, "Project Overview", buildProjectOverview(summary));
17596
+ pushSection(lines, "Development Commands", buildDevelopmentCommands(summary));
17597
+ pushSection(lines, "Working Rules", [...WORKING_RULES.map((rule) => `- ${rule}`)]);
17598
+ pushSection(
17599
+ lines,
17600
+ "Review Expectations",
17601
+ REVIEW_EXPECTATIONS.map((rule) => `- ${rule}`)
17602
+ );
17603
+ pushSection(
17604
+ lines,
17605
+ "Provider Notes",
17606
+ PROVIDER_NOTES.map((note) => `- ${note}`)
17607
+ );
17608
+ return lines.join("\n");
17609
+ }
17610
+ function buildProjectOverview(summary) {
17611
+ const lines = [];
17612
+ if (summary.git.isRepo) {
17613
+ if (summary.git.branch) {
17614
+ lines.push(`- Git branch: ${summary.git.branch}`);
17615
+ } else {
17616
+ lines.push("- Git repository: yes");
17617
+ }
17618
+ } else {
17619
+ lines.push("- Git repository: no");
17620
+ }
17621
+ if (summary.packageManager) {
17622
+ lines.push(`- Package manager: ${summary.packageManager}`);
17623
+ }
17624
+ if (summary.frameworks.length > 0) {
17625
+ lines.push(`- Frameworks: ${summary.frameworks.join(", ")}`);
17626
+ }
17627
+ if (summary.docs.length > 0) {
17628
+ lines.push(`- Docs: ${summary.docs.map((doc) => doc.path).join(", ")}`);
17629
+ }
17630
+ lines.push(
17631
+ `- ${summary.agentInstructions.path}: ${summary.agentInstructions.exists ? "exists" : "missing"}`
17632
+ );
17633
+ return lines;
17634
+ }
17635
+ function buildDevelopmentCommands(summary) {
17636
+ const lines = [];
17637
+ const commandLabels = {
17638
+ dev: "Dev",
17639
+ test: "Test",
17640
+ build: "Build",
17641
+ lint: "Lint"
17642
+ };
17643
+ for (const key of ["dev", "test", "build", "lint"]) {
17644
+ const command = summary.recommendedCommands.find((item) => item.key === key)?.command;
17645
+ if (!command) {
17646
+ continue;
17647
+ }
17648
+ lines.push(`- ${commandLabels[key]}: \`${command}\``);
17649
+ }
17650
+ return lines;
17651
+ }
17652
+ function pushSection(lines, heading, body) {
17653
+ lines.push(`## ${heading}`, "");
17654
+ lines.push(...body);
17655
+ lines.push("");
17656
+ }
17657
+ var WORKING_RULES, REVIEW_EXPECTATIONS, PROVIDER_NOTES;
17658
+ var init_generator = __esm({
17659
+ "packages/server/src/agent-instructions/generator.ts"() {
17660
+ "use strict";
17661
+ WORKING_RULES = [
17662
+ "Keep changes focused on the requested task.",
17663
+ "Do not revert user changes unless explicitly asked.",
17664
+ "Prefer the project's existing patterns.",
17665
+ "Run the relevant verification command before reporting completion."
17666
+ ];
17667
+ REVIEW_EXPECTATIONS = [
17668
+ "Summarize changed files.",
17669
+ "Report verification commands and results.",
17670
+ "Call out risks, skipped tests, and assumptions."
17671
+ ];
17672
+ PROVIDER_NOTES = [
17673
+ "Claude Code: use the project rules above.",
17674
+ "Codex: use the project rules above."
17675
+ ];
17676
+ }
17677
+ });
17678
+
17679
+ // packages/server/src/agent-instructions/health.ts
17680
+ function evaluateAgentInstructionsMarkdown(content) {
17681
+ if (!content.trim()) {
17682
+ return {
17683
+ path: AGENT_INSTRUCTIONS_RELATIVE_PATH,
17684
+ exists: false,
17685
+ status: "missing",
17686
+ checks: {
17687
+ projectOverview: false,
17688
+ developmentCommands: false,
17689
+ workingRules: false,
17690
+ reviewExpectations: false,
17691
+ safetyRules: false,
17692
+ providerNotes: false
17693
+ },
17694
+ issues: [
17695
+ {
17696
+ code: "missing_document",
17697
+ message: `${AGENT_INSTRUCTIONS_RELATIVE_PATH} is missing`
17698
+ }
17699
+ ]
17700
+ };
17701
+ }
17702
+ const sections = indexSections(content);
17703
+ const projectOverview = sections.has("Project Overview");
17704
+ const developmentCommands = hasAnyBullet(sections.get("Development Commands"));
17705
+ const workingRulesSection = sections.get("Working Rules");
17706
+ const reviewExpectationsSection = sections.get("Review Expectations");
17707
+ const providerNotesSection = sections.get("Provider Notes");
17708
+ const workingRules = hasAnyBullet(workingRulesSection);
17709
+ const reviewExpectations = hasAnyBullet(reviewExpectationsSection) && REQUIRED_REVIEW_EXPECTATIONS.every(
17710
+ (rule) => reviewExpectationsSection?.some((line) => line.includes(rule))
17711
+ );
17712
+ const providerNotes = hasAnyBullet(providerNotesSection) && PROVIDER_NOTE_MARKERS.some(
17713
+ (marker) => providerNotesSection?.some((line) => line.includes(marker))
17714
+ );
17715
+ const safetyRules = REQUIRED_WORKING_RULES.every(
17716
+ (rule) => workingRulesSection?.some((line) => line.includes(rule))
17717
+ );
17718
+ const issues = [];
17719
+ if (!projectOverview) {
17720
+ issues.push({
17721
+ code: "missing_project_overview",
17722
+ message: "Project Overview section is missing"
17723
+ });
17724
+ }
17725
+ if (!developmentCommands) {
17726
+ issues.push({
17727
+ code: "missing_development_commands",
17728
+ message: "Development Commands section is missing"
17729
+ });
17730
+ }
17731
+ if (!workingRules) {
17732
+ issues.push({
17733
+ code: "missing_working_rules",
17734
+ message: "Working Rules section is missing"
17735
+ });
17736
+ }
17737
+ if (!reviewExpectations) {
17738
+ issues.push({
17739
+ code: "missing_review_expectations",
17740
+ message: "Review Expectations section is missing"
17741
+ });
17742
+ }
17743
+ if (!safetyRules) {
17744
+ issues.push({
17745
+ code: "missing_safety_rules",
17746
+ message: "Working rules do not include the required safety rules"
17747
+ });
17748
+ }
17749
+ if (!providerNotes) {
17750
+ issues.push({
17751
+ code: "missing_provider_notes",
17752
+ message: "Provider Notes section is missing"
17753
+ });
17754
+ }
17755
+ const status = issues.length === 0 ? "healthy" : "warning";
17756
+ return {
17757
+ path: AGENT_INSTRUCTIONS_RELATIVE_PATH,
17758
+ exists: true,
17759
+ status,
17760
+ checks: {
17761
+ projectOverview,
17762
+ developmentCommands,
17763
+ workingRules,
17764
+ reviewExpectations,
17765
+ safetyRules,
17766
+ providerNotes
17767
+ },
17768
+ issues
17769
+ };
17770
+ }
17771
+ function indexSections(content) {
17772
+ const sections = /* @__PURE__ */ new Map();
17773
+ const lines = content.split(/\r?\n/);
17774
+ let currentHeading = null;
17775
+ for (const line of lines) {
17776
+ const heading = line.match(/^##\s+(.+?)\s*$/)?.[1];
17777
+ if (heading) {
17778
+ currentHeading = heading;
17779
+ if (!sections.has(heading)) {
17780
+ sections.set(heading, []);
17781
+ }
17782
+ continue;
17783
+ }
17784
+ if (currentHeading) {
17785
+ sections.get(currentHeading)?.push(line);
17786
+ }
17787
+ }
17788
+ return sections;
17789
+ }
17790
+ function hasAnyBullet(lines) {
17791
+ return Boolean(lines?.some((line) => line.trimStart().startsWith("- ")));
17792
+ }
17793
+ var REQUIRED_WORKING_RULES, REQUIRED_REVIEW_EXPECTATIONS, PROVIDER_NOTE_MARKERS;
17794
+ var init_health = __esm({
17795
+ "packages/server/src/agent-instructions/health.ts"() {
17796
+ "use strict";
17797
+ init_workspace_state();
17798
+ REQUIRED_WORKING_RULES = [
17799
+ "Keep changes focused on the requested task.",
17800
+ "Do not revert user changes unless explicitly asked.",
17801
+ "Prefer the project's existing patterns.",
17802
+ "Run the relevant verification command before reporting completion."
17803
+ ];
17804
+ REQUIRED_REVIEW_EXPECTATIONS = [
17805
+ "Summarize changed files.",
17806
+ "Report verification commands and results.",
17807
+ "Call out risks, skipped tests, and assumptions."
17808
+ ];
17809
+ PROVIDER_NOTE_MARKERS = ["Claude Code:", "Codex:"];
17810
+ }
17811
+ });
17812
+
17813
+ // packages/server/src/commands/agent-instructions.ts
17814
+ import { z as z18 } from "zod";
17815
+ async function readAgentInstructionsDocument(workspaceId, rootPath) {
17816
+ const path14 = AGENT_INSTRUCTIONS_RELATIVE_PATH;
17817
+ try {
17818
+ const result = await readFile(workspaceId, rootPath, path14);
17819
+ if (result.kind !== "text") {
17820
+ return {
17821
+ path: path14,
17822
+ exists: true,
17823
+ content: ""
17824
+ };
17825
+ }
17826
+ return {
17827
+ path: path14,
17828
+ exists: true,
17829
+ content: result.content,
17830
+ baseHash: result.baseHash
17831
+ };
17832
+ } catch {
17833
+ return {
17834
+ path: path14,
17835
+ exists: false,
17836
+ content: ""
17837
+ };
17838
+ }
17839
+ }
17840
+ var init_agent_instructions = __esm({
17841
+ "packages/server/src/commands/agent-instructions.ts"() {
17842
+ "use strict";
17843
+ init_generator();
17844
+ init_health();
17845
+ init_file_io();
17846
+ init_intelligence();
17847
+ init_workspace_state();
17848
+ init_dispatch();
17849
+ registerCommand(
17850
+ "agentInstructions.read",
17851
+ z18.object({
17852
+ workspaceId: z18.string()
17853
+ }),
17854
+ async (args, ctx) => {
17855
+ const workspace = ctx.workspaceMgr.get(args.workspaceId);
17856
+ if (!workspace) {
17857
+ throw { code: "workspace_not_found", message: `Workspace not found: ${args.workspaceId}` };
17858
+ }
17859
+ return readAgentInstructionsDocument(workspace.id, workspace.path);
17860
+ }
17861
+ );
17862
+ registerCommand(
17863
+ "agentInstructions.generate",
17864
+ z18.object({
17865
+ workspaceId: z18.string()
17866
+ }),
17867
+ async (args, ctx) => {
17868
+ const workspace = ctx.workspaceMgr.get(args.workspaceId);
17869
+ if (!workspace) {
17870
+ throw { code: "workspace_not_found", message: `Workspace not found: ${args.workspaceId}` };
17871
+ }
17872
+ const summary = await inspectWorkspaceIntelligence({
17873
+ workspaceId: workspace.id,
17874
+ rootPath: workspace.path
17875
+ });
17876
+ return {
17877
+ path: AGENT_INSTRUCTIONS_RELATIVE_PATH,
17878
+ exists: false,
17879
+ content: buildAgentInstructionsMarkdown(summary)
17880
+ };
17881
+ }
17882
+ );
17883
+ registerCommand(
17884
+ "agentInstructions.write",
17885
+ z18.object({
17886
+ workspaceId: z18.string(),
17887
+ content: z18.string(),
17888
+ overwrite: z18.boolean().optional(),
17889
+ baseHash: z18.string().optional()
16510
17890
  }),
16511
17891
  async (args, ctx) => {
16512
17892
  const workspace = ctx.workspaceMgr.get(args.workspaceId);
16513
17893
  if (!workspace) {
16514
17894
  throw { code: "workspace_not_found", message: `Workspace not found: ${args.workspaceId}` };
16515
17895
  }
16516
- const result = await runGitNetworkOperation(
16517
- ctx,
16518
- args.workspaceId,
16519
- () => runGitPull(workspace.path, {
16520
- remote: args.remote,
16521
- branch: args.branch,
16522
- auth: args.auth
16523
- })
17896
+ const existing = await readAgentInstructionsDocument(workspace.id, workspace.path);
17897
+ if (existing.exists && !args.overwrite) {
17898
+ throw {
17899
+ code: "agent_instructions_exists",
17900
+ message: `${AGENT_INSTRUCTIONS_RELATIVE_PATH} already exists`
17901
+ };
17902
+ }
17903
+ const result = await writeFile(
17904
+ workspace.path,
17905
+ AGENT_INSTRUCTIONS_RELATIVE_PATH,
17906
+ args.content,
17907
+ args.baseHash
16524
17908
  );
16525
- ctx.workspaceMgr.recordFetch(args.workspaceId);
16526
- emitGitStateChanged(ctx, args.workspaceId, {
16527
- treeChanged: true,
16528
- branchChanged: true,
16529
- worktreeChanged: true
17909
+ ctx.eventBus.emit({
17910
+ type: "fs.dirty",
17911
+ workspaceId: args.workspaceId,
17912
+ reason: "file_content"
16530
17913
  });
16531
- return result;
17914
+ return {
17915
+ path: AGENT_INSTRUCTIONS_RELATIVE_PATH,
17916
+ exists: true,
17917
+ content: args.content,
17918
+ baseHash: result.newHash
17919
+ };
16532
17920
  }
16533
17921
  );
16534
17922
  registerCommand(
16535
- "git.fetch",
16536
- z14.object({
16537
- workspaceId: z14.string(),
16538
- remote: z14.string().optional(),
16539
- prune: z14.boolean().optional(),
16540
- auth: gitHttpAuthSchema.optional(),
16541
- background: z14.boolean().optional()
17923
+ "agentInstructions.health",
17924
+ z18.object({
17925
+ workspaceId: z18.string()
16542
17926
  }),
16543
- async (args, ctx, clientId) => {
17927
+ async (args, ctx) => {
16544
17928
  const workspace = ctx.workspaceMgr.get(args.workspaceId);
16545
17929
  if (!workspace) {
16546
17930
  throw { code: "workspace_not_found", message: `Workspace not found: ${args.workspaceId}` };
16547
17931
  }
16548
- try {
16549
- const isInternalBackgroundFetch = args.background === true && !clientId;
16550
- const runFetch = () => runGitFetch(workspace.path, {
16551
- remote: args.remote,
16552
- prune: args.prune,
16553
- auth: args.auth,
16554
- timeoutMs: args.background ? GIT_BACKGROUND_FETCH_TIMEOUT_MS : void 0
16555
- });
16556
- const result = isInternalBackgroundFetch ? await runFetch() : await runGitNetworkOperation(ctx, args.workspaceId, runFetch);
16557
- ctx.workspaceMgr.recordFetch(args.workspaceId);
16558
- emitGitStateChanged(ctx, args.workspaceId, { branchChanged: true });
16559
- return result;
16560
- } catch (err) {
16561
- if (args.background && err instanceof GitAuthError) {
16562
- return { success: false, message: err.message, updatedRefs: [] };
16563
- }
16564
- throw err;
16565
- }
17932
+ const document = await readAgentInstructionsDocument(workspace.id, workspace.path);
17933
+ return evaluateAgentInstructionsMarkdown(document.content);
16566
17934
  }
16567
17935
  );
17936
+ }
17937
+ });
17938
+
17939
+ // packages/server/src/agent-context/context-package.ts
17940
+ import { randomUUID as randomUUID7 } from "node:crypto";
17941
+ import { readFile as readFile8 } from "node:fs/promises";
17942
+ function resolveOptions(options) {
17943
+ return {
17944
+ createId: options?.createId ?? randomUUID7,
17945
+ now: options?.now ?? Date.now
17946
+ };
17947
+ }
17948
+ function formatSource(source) {
17949
+ const parts = [`workspace=${source.workspaceId}`];
17950
+ if (source.sessionId) {
17951
+ parts.push(`session=${source.sessionId}`);
17952
+ }
17953
+ if (source.path) {
17954
+ parts.push(`path=${source.path}`);
17955
+ }
17956
+ if (source.terminalId) {
17957
+ parts.push(`terminal=${source.terminalId}`);
17958
+ }
17959
+ return parts.join(" ");
17960
+ }
17961
+ function wrapContext(title, source, body) {
17962
+ return `Context: ${title}
17963
+ Source: ${formatSource(source)}
17964
+
17965
+ ${body}`;
17966
+ }
17967
+ function createContextPackage(kind, title, source, body, options) {
17968
+ const resolved = resolveOptions(options);
17969
+ return {
17970
+ id: resolved.createId(),
17971
+ kind,
17972
+ title,
17973
+ body: wrapContext(title, source, body),
17974
+ source,
17975
+ createdAt: resolved.now()
17976
+ };
17977
+ }
17978
+ function requireSessionMetadata2(metadataRepo, sessionId) {
17979
+ const metadata = metadataRepo.get(sessionId);
17980
+ if (!metadata) {
17981
+ throw {
17982
+ code: "session_metadata_not_found",
17983
+ message: `Session metadata not found: ${sessionId}`
17984
+ };
17985
+ }
17986
+ return metadata;
17987
+ }
17988
+ function buildProjectSummaryBody(summary) {
17989
+ const lines = [
17990
+ `Git: ${summary.git.isRepo ? "repository detected" : "no repository detected"}`,
17991
+ `Package manager: ${summary.packageManager ?? "unknown"}`,
17992
+ `Frameworks: ${summary.frameworks.length > 0 ? summary.frameworks.join(", ") : "none"}`
17993
+ ];
17994
+ if (summary.recommendedCommands.length > 0) {
17995
+ lines.push("Recommended commands:");
17996
+ for (const command of summary.recommendedCommands) {
17997
+ lines.push(`- ${command.key}: ${command.command}`);
17998
+ }
17999
+ } else {
18000
+ lines.push("Recommended commands: none");
18001
+ }
18002
+ if (summary.docs.length > 0) {
18003
+ lines.push("Docs:");
18004
+ for (const doc of summary.docs) {
18005
+ lines.push(`- ${doc.path}`);
18006
+ }
18007
+ } else {
18008
+ lines.push("Docs: none");
18009
+ }
18010
+ lines.push(
18011
+ `Agent instructions: ${summary.agentInstructions.path} ${summary.agentInstructions.exists ? "present" : "missing"}`
18012
+ );
18013
+ return lines.join("\n");
18014
+ }
18015
+ async function buildFileContextPackage(input, options) {
18016
+ const content = await readFile8(resolveSafe(input.workspacePath, input.path), "utf8");
18017
+ return createContextPackage(
18018
+ "file",
18019
+ `File: ${input.path}`,
18020
+ {
18021
+ workspaceId: input.workspaceId,
18022
+ path: input.path
18023
+ },
18024
+ content,
18025
+ options
18026
+ );
18027
+ }
18028
+ async function buildDiffContextPackage(input, options) {
18029
+ const metadata = requireSessionMetadata2(input.metadataRepo, input.sessionId);
18030
+ const diff = await getSessionReviewDiff({
18031
+ sessionId: input.sessionId,
18032
+ workspacePath: input.workspacePath,
18033
+ metadataRepo: input.metadataRepo,
18034
+ path: input.path
18035
+ });
18036
+ return createContextPackage(
18037
+ "git_diff",
18038
+ `Git Diff: ${input.path}`,
18039
+ {
18040
+ workspaceId: metadata.workspaceId,
18041
+ sessionId: input.sessionId,
18042
+ path: input.path
18043
+ },
18044
+ diff,
18045
+ options
18046
+ );
18047
+ }
18048
+ async function buildProjectSummaryContextPackage(input, options) {
18049
+ const summary = await inspectWorkspaceIntelligence({
18050
+ workspaceId: input.workspaceId,
18051
+ rootPath: input.workspacePath
18052
+ });
18053
+ return createContextPackage(
18054
+ "project_summary",
18055
+ "Project Summary",
18056
+ {
18057
+ workspaceId: input.workspaceId
18058
+ },
18059
+ buildProjectSummaryBody(summary),
18060
+ options
18061
+ );
18062
+ }
18063
+ async function buildSessionReviewContextPackage(input, options) {
18064
+ const metadata = requireSessionMetadata2(input.metadataRepo, input.sessionId);
18065
+ const summary = await buildSessionReviewSummary({
18066
+ sessionId: input.sessionId,
18067
+ workspacePath: input.workspacePath,
18068
+ metadataRepo: input.metadataRepo
18069
+ });
18070
+ const lines = [
18071
+ `Baseline: ${summary.baselineGitHead ?? "missing"}`,
18072
+ "Changed files:",
18073
+ ...summary.changedFiles.length > 0 ? summary.changedFiles.map((change) => `- ${change.status ?? "modified"}: ${change.path}`) : ["- none"],
18074
+ "Verification runs:",
18075
+ ...summary.verificationRuns.length > 0 ? summary.verificationRuns.map((run) => `- ${run.status}: ${run.command}`) : ["- none"]
18076
+ ];
18077
+ if (summary.warnings.length > 0) {
18078
+ lines.push("Warnings:");
18079
+ for (const warning of summary.warnings) {
18080
+ lines.push(`- ${warning.code}: ${warning.message}`);
18081
+ }
18082
+ }
18083
+ return createContextPackage(
18084
+ "session_review",
18085
+ `Session Review: ${summary.sessionId}`,
18086
+ {
18087
+ workspaceId: metadata.workspaceId,
18088
+ sessionId: input.sessionId
18089
+ },
18090
+ lines.join("\n"),
18091
+ options
18092
+ );
18093
+ }
18094
+ var init_context_package = __esm({
18095
+ "packages/server/src/agent-context/context-package.ts"() {
18096
+ "use strict";
18097
+ init_file_io();
18098
+ init_review();
18099
+ init_intelligence();
18100
+ }
18101
+ });
18102
+
18103
+ // packages/server/src/commands/agent-context.ts
18104
+ import { z as z19 } from "zod";
18105
+ function requireWorkspace2(ctx, workspaceId) {
18106
+ const workspace = ctx.workspaceMgr.get(workspaceId);
18107
+ if (!workspace) {
18108
+ throw { code: "workspace_not_found", message: `Workspace not found: ${workspaceId}` };
18109
+ }
18110
+ return workspace;
18111
+ }
18112
+ function requireSessionMetadataRepo3(ctx) {
18113
+ if (!ctx.sessionMetadataRepo) {
18114
+ throw {
18115
+ code: "session_metadata_unavailable",
18116
+ message: "Session metadata repository is not configured"
18117
+ };
18118
+ }
18119
+ }
18120
+ function getSessionWorkspace(ctx, sessionId) {
18121
+ const metadata = ctx.sessionMetadataRepo.get(sessionId);
18122
+ if (!metadata) {
18123
+ throw {
18124
+ code: "session_metadata_not_found",
18125
+ message: `Session metadata not found: ${sessionId}`
18126
+ };
18127
+ }
18128
+ return {
18129
+ metadata,
18130
+ workspace: requireWorkspace2(ctx, metadata.workspaceId)
18131
+ };
18132
+ }
18133
+ var init_agent_context = __esm({
18134
+ "packages/server/src/commands/agent-context.ts"() {
18135
+ "use strict";
18136
+ init_context_package();
18137
+ init_dispatch();
16568
18138
  registerCommand(
16569
- "git.checkout",
16570
- z14.object({
16571
- workspaceId: z14.string(),
16572
- ref: z14.string(),
16573
- createBranch: z14.boolean().optional()
18139
+ "agentContext.fromFile",
18140
+ z19.object({
18141
+ workspaceId: z19.string(),
18142
+ path: z19.string().trim().min(1)
16574
18143
  }),
16575
18144
  async (args, ctx) => {
16576
- const workspace = ctx.workspaceMgr.get(args.workspaceId);
16577
- if (!workspace) {
16578
- throw { code: "workspace_not_found", message: `Workspace not found: ${args.workspaceId}` };
16579
- }
16580
- const result = await runGitCheckout(workspace.path, args.ref, {
16581
- createBranch: args.createBranch
18145
+ const workspace = requireWorkspace2(ctx, args.workspaceId);
18146
+ return buildFileContextPackage({
18147
+ workspaceId: workspace.id,
18148
+ workspacePath: workspace.path,
18149
+ path: args.path
16582
18150
  });
16583
- if (result.success) {
16584
- emitGitStateChanged(ctx, args.workspaceId, {
16585
- treeChanged: true,
16586
- branchChanged: true,
16587
- worktreeChanged: true
16588
- });
16589
- }
16590
- return result;
16591
18151
  }
16592
18152
  );
16593
18153
  registerCommand(
16594
- "git.branch",
16595
- z14.object({
16596
- workspaceId: z14.string(),
16597
- name: z14.string(),
16598
- startPoint: z14.string().optional()
18154
+ "agentContext.fromDiff",
18155
+ z19.object({
18156
+ sessionId: z19.string(),
18157
+ path: z19.string().trim().min(1)
16599
18158
  }),
16600
18159
  async (args, ctx) => {
16601
- const workspace = ctx.workspaceMgr.get(args.workspaceId);
16602
- if (!workspace) {
16603
- throw { code: "workspace_not_found", message: `Workspace not found: ${args.workspaceId}` };
16604
- }
16605
- const result = await runGitCreateBranch(workspace.path, args.name, {
16606
- startPoint: args.startPoint
18160
+ requireSessionMetadataRepo3(ctx);
18161
+ const { workspace } = getSessionWorkspace(ctx, args.sessionId);
18162
+ return buildDiffContextPackage({
18163
+ sessionId: args.sessionId,
18164
+ path: args.path,
18165
+ workspacePath: workspace.path,
18166
+ metadataRepo: ctx.sessionMetadataRepo
16607
18167
  });
16608
- emitGitStateChanged(ctx, args.workspaceId, {
16609
- branchChanged: true,
16610
- worktreeChanged: true
18168
+ }
18169
+ );
18170
+ registerCommand(
18171
+ "agentContext.fromProjectSummary",
18172
+ z19.object({
18173
+ workspaceId: z19.string()
18174
+ }),
18175
+ async (args, ctx) => {
18176
+ const workspace = requireWorkspace2(ctx, args.workspaceId);
18177
+ return buildProjectSummaryContextPackage({
18178
+ workspaceId: workspace.id,
18179
+ workspacePath: workspace.path
16611
18180
  });
16612
- return result;
16613
18181
  }
16614
18182
  );
16615
18183
  registerCommand(
16616
- "git.branches",
16617
- z14.object({
16618
- workspaceId: z14.string()
18184
+ "agentContext.fromSessionReview",
18185
+ z19.object({
18186
+ sessionId: z19.string()
16619
18187
  }),
16620
18188
  async (args, ctx) => {
16621
- const workspace = ctx.workspaceMgr.get(args.workspaceId);
16622
- if (!workspace) {
16623
- throw { code: "workspace_not_found", message: `Workspace not found: ${args.workspaceId}` };
16624
- }
16625
- return runGitListBranches(workspace.path);
18189
+ requireSessionMetadataRepo3(ctx);
18190
+ const { workspace } = getSessionWorkspace(ctx, args.sessionId);
18191
+ return buildSessionReviewContextPackage({
18192
+ sessionId: args.sessionId,
18193
+ workspacePath: workspace.path,
18194
+ metadataRepo: ctx.sessionMetadataRepo
18195
+ });
16626
18196
  }
16627
18197
  );
16628
18198
  }
@@ -16631,25 +18201,25 @@ var init_git2 = __esm({
16631
18201
  // packages/server/src/config/config-io.ts
16632
18202
  import { existsSync as existsSync9, mkdirSync as mkdirSync7, readFileSync as readFileSync7, renameSync as renameSync2, writeFileSync as writeFileSync5 } from "node:fs";
16633
18203
  import { homedir as homedir3 } from "node:os";
16634
- import { basename as basename3, dirname as dirname7, join as join14 } from "node:path";
18204
+ import { basename as basename3, dirname as dirname7, join as join17 } from "node:path";
16635
18205
  function resolveConfigPath(configType) {
16636
18206
  if (configType === "codex") {
16637
18207
  const testHome = process.env.CODER_STUDIO_CODEX_HOME;
16638
18208
  if (testHome && testHome.trim()) {
16639
- return join14(testHome, "config.toml");
18209
+ return join17(testHome, "config.toml");
16640
18210
  }
16641
18211
  const codexHome = process.env.CODEX_HOME;
16642
18212
  if (codexHome && codexHome.trim()) {
16643
- return join14(codexHome, "config.toml");
18213
+ return join17(codexHome, "config.toml");
16644
18214
  }
16645
- return join14(homedir3(), ".codex", "config.toml");
18215
+ return join17(homedir3(), ".codex", "config.toml");
16646
18216
  }
16647
18217
  if (configType === "claude") {
16648
18218
  const testHome = process.env.CODER_STUDIO_CLAUDE_HOME;
16649
18219
  if (testHome && testHome.trim()) {
16650
- return join14(testHome, "settings.json");
18220
+ return join17(testHome, "settings.json");
16651
18221
  }
16652
- return join14(homedir3(), ".claude", "settings.json");
18222
+ return join17(homedir3(), ".claude", "settings.json");
16653
18223
  }
16654
18224
  throw new Error(`Unknown config type: ${configType}`);
16655
18225
  }
@@ -16694,7 +18264,7 @@ function createBackup(filePath) {
16694
18264
  const base = basename3(filePath, `.${ext}`);
16695
18265
  const dir = dirname7(filePath);
16696
18266
  const ts = formatTimestamp(/* @__PURE__ */ new Date());
16697
- const backupPath = join14(dir, `${base}.bak.${ts}.${ext}`);
18267
+ const backupPath = join17(dir, `${base}.bak.${ts}.${ext}`);
16698
18268
  writeFileSync5(backupPath, original, "utf-8");
16699
18269
  return backupPath;
16700
18270
  }
@@ -16709,7 +18279,7 @@ var init_config_io = __esm({
16709
18279
  });
16710
18280
 
16711
18281
  // packages/server/src/commands/settings.ts
16712
- import { z as z15 } from "zod";
18282
+ import { z as z20 } from "zod";
16713
18283
  function flattenSettings(obj, prefix = "") {
16714
18284
  const result = {};
16715
18285
  for (const [key, value] of Object.entries(obj)) {
@@ -16760,13 +18330,13 @@ var init_settings2 = __esm({
16760
18330
  init_provider_config();
16761
18331
  init_settings();
16762
18332
  init_dispatch();
16763
- PersonalizationOverridesSchema = z15.object({
16764
- backgroundAssetId: z15.string().min(1).nullable().optional(),
16765
- backgroundDimness: z15.number().int().min(0).max(100).optional(),
16766
- backgroundBlur: z15.number().int().min(0).max(40).optional(),
16767
- glassEnabled: z15.boolean().optional(),
16768
- glassIntensity: z15.number().int().min(0).max(100).optional(),
16769
- surfaceOpacity: z15.number().int().min(0).max(100).optional()
18333
+ PersonalizationOverridesSchema = z20.object({
18334
+ backgroundAssetId: z20.string().min(1).nullable().optional(),
18335
+ backgroundDimness: z20.number().int().min(0).max(100).optional(),
18336
+ backgroundBlur: z20.number().int().min(0).max(40).optional(),
18337
+ glassEnabled: z20.boolean().optional(),
18338
+ glassIntensity: z20.number().int().min(0).max(100).optional(),
18339
+ surfaceOpacity: z20.number().int().min(0).max(100).optional()
16770
18340
  });
16771
18341
  PERSONALIZATION_OVERRIDE_BRANCHES = ["desktop", "mobile"];
16772
18342
  PERSONALIZATION_OVERRIDE_FIELDS = [
@@ -16777,59 +18347,59 @@ var init_settings2 = __esm({
16777
18347
  "glassIntensity",
16778
18348
  "surfaceOpacity"
16779
18349
  ];
16780
- SettingsSchema = z15.object({
16781
- defaultProviderId: z15.string().optional(),
16782
- notifications: z15.object({
16783
- enabled: z15.boolean().optional(),
16784
- soundEnabled: z15.boolean().optional(),
18350
+ SettingsSchema = z20.object({
18351
+ defaultProviderId: z20.string().optional(),
18352
+ notifications: z20.object({
18353
+ enabled: z20.boolean().optional(),
18354
+ soundEnabled: z20.boolean().optional(),
16785
18355
  // Legacy field — accepted for backward compat with older clients but
16786
18356
  // no longer surfaced in the UI. The web client now picks the channel
16787
18357
  // automatically based on workspace focus + page visibility.
16788
- onlyWhenBackgrounded: z15.boolean().optional()
18358
+ onlyWhenBackgrounded: z20.boolean().optional()
16789
18359
  }).optional(),
16790
- supervisor: z15.object({
16791
- evaluationTimeoutSec: z15.number().int().min(1).max(MAX_SUPERVISOR_EVALUATION_TIMEOUT_SEC).default(DEFAULT_SUPERVISOR_EVALUATION_TIMEOUT_SEC).optional(),
16792
- retryEnabled: z15.boolean().optional(),
16793
- retryMaxCount: z15.number().int().min(0).max(MAX_SUPERVISOR_RETRY_MAX_COUNT).optional(),
16794
- retryDelaySec: z15.number().int().min(1).max(MAX_SUPERVISOR_RETRY_DELAY_SEC).optional(),
16795
- retryOnTimeout: z15.boolean().optional(),
16796
- retryOnEvaluatorError: z15.boolean().optional()
18360
+ supervisor: z20.object({
18361
+ evaluationTimeoutSec: z20.number().int().min(1).max(MAX_SUPERVISOR_EVALUATION_TIMEOUT_SEC).default(DEFAULT_SUPERVISOR_EVALUATION_TIMEOUT_SEC).optional(),
18362
+ retryEnabled: z20.boolean().optional(),
18363
+ retryMaxCount: z20.number().int().min(0).max(MAX_SUPERVISOR_RETRY_MAX_COUNT).optional(),
18364
+ retryDelaySec: z20.number().int().min(1).max(MAX_SUPERVISOR_RETRY_DELAY_SEC).optional(),
18365
+ retryOnTimeout: z20.boolean().optional(),
18366
+ retryOnEvaluatorError: z20.boolean().optional()
16797
18367
  }).optional(),
16798
- appearance: z15.object({
16799
- theme: z15.enum(["dark", "light"]).optional(),
16800
- themeId: z15.string().optional(),
16801
- terminalRenderer: z15.enum(["standard", "compatibility"]).optional(),
16802
- terminalCopyOnSelect: z15.boolean().optional(),
16803
- terminalFontSize: z15.number().int().min(10).max(18).optional(),
16804
- desktopTerminalFontSize: z15.number().int().min(10).max(18).optional(),
16805
- mobileTerminalFontSize: z15.number().int().min(10).max(18).optional(),
16806
- locale: z15.enum(["zh", "en"]).optional(),
16807
- personalization: z15.object({
16808
- version: z15.literal(1).optional(),
16809
- common: z15.object({
16810
- backgroundMode: z15.enum(["none", "image"]).optional(),
16811
- backgroundAssetId: z15.string().min(1).nullable().optional(),
16812
- backgroundFit: z15.enum(["cover", "contain"]).optional(),
16813
- backgroundDimness: z15.number().int().min(0).max(100).optional(),
16814
- backgroundBlur: z15.number().int().min(0).max(40).optional(),
16815
- glassEnabled: z15.boolean().optional(),
16816
- glassIntensity: z15.number().int().min(0).max(100).optional(),
16817
- surfaceOpacity: z15.number().int().min(0).max(100).optional()
18368
+ appearance: z20.object({
18369
+ theme: z20.enum(["dark", "light"]).optional(),
18370
+ themeId: z20.string().optional(),
18371
+ terminalRenderer: z20.enum(["standard", "compatibility"]).optional(),
18372
+ terminalCopyOnSelect: z20.boolean().optional(),
18373
+ terminalFontSize: z20.number().int().min(10).max(18).optional(),
18374
+ desktopTerminalFontSize: z20.number().int().min(10).max(18).optional(),
18375
+ mobileTerminalFontSize: z20.number().int().min(10).max(18).optional(),
18376
+ locale: z20.enum(["zh", "en"]).optional(),
18377
+ personalization: z20.object({
18378
+ version: z20.literal(1).optional(),
18379
+ common: z20.object({
18380
+ backgroundMode: z20.enum(["none", "image"]).optional(),
18381
+ backgroundAssetId: z20.string().min(1).nullable().optional(),
18382
+ backgroundFit: z20.enum(["cover", "contain"]).optional(),
18383
+ backgroundDimness: z20.number().int().min(0).max(100).optional(),
18384
+ backgroundBlur: z20.number().int().min(0).max(40).optional(),
18385
+ glassEnabled: z20.boolean().optional(),
18386
+ glassIntensity: z20.number().int().min(0).max(100).optional(),
18387
+ surfaceOpacity: z20.number().int().min(0).max(100).optional()
16818
18388
  }).optional(),
16819
18389
  desktop: PersonalizationOverridesSchema.optional(),
16820
18390
  mobile: PersonalizationOverridesSchema.optional()
16821
18391
  }).optional()
16822
18392
  }).optional(),
16823
- lsp: z15.object({
16824
- mode: z15.enum(["auto", "off"]).optional()
18393
+ lsp: z20.object({
18394
+ mode: z20.enum(["auto", "off"]).optional()
16825
18395
  }).optional(),
16826
- updates: z15.object({
16827
- autoCheckEnabled: z15.boolean().optional(),
16828
- checkIntervalSec: z15.number().int().refine(isUpdateCheckIntervalSec).optional()
18396
+ updates: z20.object({
18397
+ autoCheckEnabled: z20.boolean().optional(),
18398
+ checkIntervalSec: z20.number().int().refine(isUpdateCheckIntervalSec).optional()
16829
18399
  }).optional(),
16830
18400
  providers: ProviderSettingsSchema.optional()
16831
18401
  });
16832
- registerCommand("settings.get", z15.object({}), async (_args, ctx) => {
18402
+ registerCommand("settings.get", z20.object({}), async (_args, ctx) => {
16833
18403
  const settings = {};
16834
18404
  for (const [key, value] of Object.entries(ctx.settingsRepo.getAll())) {
16835
18405
  if (key.startsWith("providers.")) {
@@ -16881,7 +18451,7 @@ var init_settings2 = __esm({
16881
18451
  });
16882
18452
  registerCommand(
16883
18453
  "settings.update",
16884
- z15.object({
18454
+ z20.object({
16885
18455
  settings: SettingsSchema
16886
18456
  }),
16887
18457
  async (args, ctx) => {
@@ -16914,10 +18484,10 @@ var init_settings2 = __esm({
16914
18484
  );
16915
18485
  registerCommand(
16916
18486
  "settings.previewCommand",
16917
- z15.object({
16918
- providerId: z15.string(),
18487
+ z20.object({
18488
+ providerId: z20.string(),
16919
18489
  config: ProviderLaunchConfigInputSchema,
16920
- workspacePath: z15.string().optional()
18490
+ workspacePath: z20.string().optional()
16921
18491
  }),
16922
18492
  async (args, ctx) => {
16923
18493
  const provider = ctx.providerRegistry.find((item) => item.id === args.providerId);
@@ -16938,8 +18508,8 @@ var init_settings2 = __esm({
16938
18508
  );
16939
18509
  registerCommand(
16940
18510
  "settings.readConfigFile",
16941
- z15.object({
16942
- configType: z15.enum(["codex", "claude"])
18511
+ z20.object({
18512
+ configType: z20.enum(["codex", "claude"])
16943
18513
  }),
16944
18514
  async (args) => {
16945
18515
  const result = readConfigFile(args.configType);
@@ -16948,9 +18518,9 @@ var init_settings2 = __esm({
16948
18518
  );
16949
18519
  registerCommand(
16950
18520
  "settings.writeConfigFile",
16951
- z15.object({
16952
- configType: z15.enum(["codex", "claude"]),
16953
- content: z15.string()
18521
+ z20.object({
18522
+ configType: z20.enum(["codex", "claude"]),
18523
+ content: z20.string()
16954
18524
  }),
16955
18525
  async (args) => {
16956
18526
  const result = writeConfigFile(args.configType, args.content);
@@ -16961,7 +18531,7 @@ var init_settings2 = __esm({
16961
18531
  });
16962
18532
 
16963
18533
  // packages/server/src/commands/diagnostics.ts
16964
- import { z as z16 } from "zod";
18534
+ import { z as z21 } from "zod";
16965
18535
  function isLoopbackHost(host) {
16966
18536
  return host === void 0 || host === "localhost" || host === "127.0.0.1" || host === "::1" || host === "0.0.0.0";
16967
18537
  }
@@ -17331,11 +18901,11 @@ var init_diagnostics2 = __esm({
17331
18901
  init_runtime_status();
17332
18902
  init_validator();
17333
18903
  init_dispatch();
17334
- DiagnosticsRequestSchema = z16.object({
17335
- context: z16.enum(["workspace_open", "session_start", "mobile_continue", "manual_check"]),
17336
- workspaceId: z16.string().optional(),
17337
- workspacePath: z16.string().optional(),
17338
- providerId: z16.string().optional()
18904
+ DiagnosticsRequestSchema = z21.object({
18905
+ context: z21.enum(["workspace_open", "session_start", "mobile_continue", "manual_check"]),
18906
+ workspaceId: z21.string().optional(),
18907
+ workspacePath: z21.string().optional(),
18908
+ providerId: z21.string().optional()
17339
18909
  });
17340
18910
  registerCommand("diagnostics.get", DiagnosticsRequestSchema, async (args, ctx) => {
17341
18911
  return buildDiagnostics(args, ctx);
@@ -17347,19 +18917,23 @@ var init_diagnostics2 = __esm({
17347
18917
  });
17348
18918
 
17349
18919
  // packages/server/src/commands/provider.ts
17350
- import { z as z17 } from "zod";
18920
+ import { z as z22 } from "zod";
17351
18921
  var init_provider = __esm({
17352
18922
  "packages/server/src/commands/provider.ts"() {
17353
18923
  "use strict";
18924
+ init_src();
17354
18925
  init_runtime_status();
17355
18926
  init_dispatch();
17356
- registerCommand("provider.runtimeStatus", z17.object({}), async (_args, ctx) => {
18927
+ registerCommand("provider.list", z22.object({}), async (_args, ctx) => {
18928
+ return ctx.providerRegistry.map((provider) => toProviderListItem(provider));
18929
+ });
18930
+ registerCommand("provider.runtimeStatus", z22.object({}), async (_args, ctx) => {
17357
18931
  return buildProviderRuntimeStatus(ctx.providerRegistry, ctx.providerRuntimeDeps);
17358
18932
  });
17359
18933
  registerCommand(
17360
18934
  "provider.install.start",
17361
- z17.object({
17362
- providerId: z17.string()
18935
+ z22.object({
18936
+ providerId: z22.string()
17363
18937
  }),
17364
18938
  async (args, ctx) => {
17365
18939
  if (!ctx.providerInstallMgr) {
@@ -17373,8 +18947,8 @@ var init_provider = __esm({
17373
18947
  );
17374
18948
  registerCommand(
17375
18949
  "provider.install.get",
17376
- z17.object({
17377
- jobId: z17.string()
18950
+ z22.object({
18951
+ jobId: z22.string()
17378
18952
  }),
17379
18953
  async (args, ctx) => {
17380
18954
  if (!ctx.providerInstallMgr) {
@@ -17396,45 +18970,147 @@ var init_provider = __esm({
17396
18970
  }
17397
18971
  });
17398
18972
 
18973
+ // packages/server/src/commands/custom-provider.ts
18974
+ import { z as z23 } from "zod";
18975
+ function requireCustomProviderSupport(ctx) {
18976
+ if (!ctx.customProviderRepo || !ctx.setProviderRegistry) {
18977
+ throw {
18978
+ code: "custom_provider_unavailable",
18979
+ message: "Custom provider runtime is not configured"
18980
+ };
18981
+ }
18982
+ }
18983
+ function materializeConfig(input, previous) {
18984
+ const now = Date.now();
18985
+ return {
18986
+ ...input,
18987
+ startupPrompt: input.startupPrompt?.trim() || void 0,
18988
+ createdAt: previous?.createdAt ?? now,
18989
+ updatedAt: now
18990
+ };
18991
+ }
18992
+ var CapabilitySchema, BaseCustomProviderInputSchema;
18993
+ var init_custom_provider2 = __esm({
18994
+ "packages/server/src/commands/custom-provider.ts"() {
18995
+ "use strict";
18996
+ init_src();
18997
+ init_custom_provider();
18998
+ init_dispatch();
18999
+ CapabilitySchema = z23.object({
19000
+ key: z23.enum([
19001
+ "interactive_session",
19002
+ "supervisor_eval",
19003
+ "idle_detection",
19004
+ "context_attach",
19005
+ "review"
19006
+ ]),
19007
+ supported: z23.boolean(),
19008
+ label: z23.string().min(1)
19009
+ });
19010
+ BaseCustomProviderInputSchema = z23.object({
19011
+ id: z23.string().trim().min(1).regex(/^[a-z0-9][a-z0-9-_]*$/),
19012
+ displayName: z23.string().trim().min(1),
19013
+ command: z23.string().trim().min(1),
19014
+ args: z23.array(z23.string()),
19015
+ env: z23.record(z23.string(), z23.string()),
19016
+ cwdMode: z23.literal("workspace_root"),
19017
+ sessionMode: z23.literal("interactive"),
19018
+ startupPrompt: z23.string().optional(),
19019
+ capabilities: z23.array(CapabilitySchema).min(1)
19020
+ });
19021
+ registerCommand("customProvider.list", z23.object({}), async (_args, ctx) => {
19022
+ requireCustomProviderSupport(ctx);
19023
+ return ctx.customProviderRepo.list().map((config) => toProviderListItem(buildCustomProviderDefinition(config)));
19024
+ });
19025
+ registerCommand("customProvider.create", BaseCustomProviderInputSchema, async (args, ctx) => {
19026
+ requireCustomProviderSupport(ctx);
19027
+ if (ctx.providerRegistry.some((provider) => provider.id === args.id)) {
19028
+ throw {
19029
+ code: "custom_provider_exists",
19030
+ message: `Provider already exists: ${args.id}`
19031
+ };
19032
+ }
19033
+ const config = materializeConfig(args);
19034
+ const saved = ctx.customProviderRepo.set(config);
19035
+ const definition = buildCustomProviderDefinition(saved);
19036
+ ctx.setProviderRegistry(upsertProviderDefinition(ctx.providerRegistry, definition));
19037
+ return toProviderListItem(definition);
19038
+ });
19039
+ registerCommand("customProvider.update", BaseCustomProviderInputSchema, async (args, ctx) => {
19040
+ requireCustomProviderSupport(ctx);
19041
+ const existing = ctx.customProviderRepo.get(args.id);
19042
+ if (!existing) {
19043
+ throw {
19044
+ code: "custom_provider_not_found",
19045
+ message: `Custom provider not found: ${args.id}`
19046
+ };
19047
+ }
19048
+ const saved = ctx.customProviderRepo.set(materializeConfig(args, existing));
19049
+ const definition = buildCustomProviderDefinition(saved);
19050
+ ctx.setProviderRegistry(upsertProviderDefinition(ctx.providerRegistry, definition));
19051
+ return toProviderListItem(definition);
19052
+ });
19053
+ registerCommand(
19054
+ "customProvider.delete",
19055
+ z23.object({
19056
+ id: z23.string().trim().min(1)
19057
+ }),
19058
+ async (args, ctx) => {
19059
+ requireCustomProviderSupport(ctx);
19060
+ const existing = ctx.customProviderRepo.get(args.id);
19061
+ if (!existing) {
19062
+ throw {
19063
+ code: "custom_provider_not_found",
19064
+ message: `Custom provider not found: ${args.id}`
19065
+ };
19066
+ }
19067
+ ctx.customProviderRepo.delete(args.id);
19068
+ ctx.setProviderRegistry(removeProviderDefinition(ctx.providerRegistry, args.id));
19069
+ return { deleted: true, id: args.id };
19070
+ }
19071
+ );
19072
+ }
19073
+ });
19074
+
17399
19075
  // packages/server/src/commands/supervisor.ts
17400
- import { z as z18 } from "zod";
19076
+ import { z as z24 } from "zod";
17401
19077
  var supervisorObjectiveSchema, createSupervisorSchema, updateSupervisorSchema, sessionIdSchema, workspaceIdSchema, supervisorIdSchema, restoreSupervisorSchema;
17402
19078
  var init_supervisor2 = __esm({
17403
19079
  "packages/server/src/commands/supervisor.ts"() {
17404
19080
  "use strict";
17405
19081
  init_dispatch();
17406
- supervisorObjectiveSchema = z18.string().trim().min(1).max(4e3);
17407
- createSupervisorSchema = z18.object({
17408
- sessionId: z18.string(),
17409
- workspaceId: z18.string(),
19082
+ supervisorObjectiveSchema = z24.string().trim().min(1).max(4e3);
19083
+ createSupervisorSchema = z24.object({
19084
+ sessionId: z24.string(),
19085
+ workspaceId: z24.string(),
17410
19086
  objective: supervisorObjectiveSchema,
17411
- evaluatorProviderId: z18.string(),
17412
- evaluatorModel: z18.string().trim().min(1).max(200).optional(),
17413
- maxSupervisionCount: z18.number().int().min(0).max(Number.MAX_SAFE_INTEGER).optional(),
17414
- scheduledAt: z18.number().int().min(0).max(Number.MAX_SAFE_INTEGER).optional()
19087
+ evaluatorProviderId: z24.string(),
19088
+ evaluatorModel: z24.string().trim().min(1).max(200).optional(),
19089
+ maxSupervisionCount: z24.number().int().min(0).max(Number.MAX_SAFE_INTEGER).optional(),
19090
+ scheduledAt: z24.number().int().min(0).max(Number.MAX_SAFE_INTEGER).optional()
17415
19091
  }).strict();
17416
- updateSupervisorSchema = z18.object({
17417
- id: z18.string(),
19092
+ updateSupervisorSchema = z24.object({
19093
+ id: z24.string(),
17418
19094
  objective: supervisorObjectiveSchema.optional(),
17419
- evaluatorProviderId: z18.string().optional(),
17420
- evaluatorModel: z18.string().trim().min(1).max(200).nullable().optional(),
17421
- maxSupervisionCount: z18.number().int().min(0).max(Number.MAX_SAFE_INTEGER).optional(),
17422
- scheduledAt: z18.number().int().min(0).max(Number.MAX_SAFE_INTEGER).nullable().optional()
19095
+ evaluatorProviderId: z24.string().optional(),
19096
+ evaluatorModel: z24.string().trim().min(1).max(200).nullable().optional(),
19097
+ maxSupervisionCount: z24.number().int().min(0).max(Number.MAX_SAFE_INTEGER).optional(),
19098
+ scheduledAt: z24.number().int().min(0).max(Number.MAX_SAFE_INTEGER).nullable().optional()
17423
19099
  }).strict().refine(
17424
19100
  (input) => input.objective !== void 0 || input.evaluatorProviderId !== void 0 || input.evaluatorModel !== void 0 || input.maxSupervisionCount !== void 0 || input.scheduledAt !== void 0,
17425
19101
  "at least one supervisor field is required"
17426
19102
  );
17427
- sessionIdSchema = z18.object({ sessionId: z18.string() });
17428
- workspaceIdSchema = z18.object({ workspaceId: z18.string() });
17429
- supervisorIdSchema = z18.object({ id: z18.string() });
17430
- restoreSupervisorSchema = z18.object({
17431
- sessionId: z18.string(),
17432
- workspaceId: z18.string(),
17433
- sourceTargetId: z18.string(),
17434
- evaluatorProviderId: z18.string(),
17435
- evaluatorModel: z18.string().trim().min(1).max(200).optional(),
17436
- maxSupervisionCount: z18.number().int().min(0).max(Number.MAX_SAFE_INTEGER).optional(),
17437
- scheduledAt: z18.number().int().min(0).max(Number.MAX_SAFE_INTEGER).optional()
19103
+ sessionIdSchema = z24.object({ sessionId: z24.string() });
19104
+ workspaceIdSchema = z24.object({ workspaceId: z24.string() });
19105
+ supervisorIdSchema = z24.object({ id: z24.string() });
19106
+ restoreSupervisorSchema = z24.object({
19107
+ sessionId: z24.string(),
19108
+ workspaceId: z24.string(),
19109
+ sourceTargetId: z24.string(),
19110
+ evaluatorProviderId: z24.string(),
19111
+ evaluatorModel: z24.string().trim().min(1).max(200).optional(),
19112
+ maxSupervisionCount: z24.number().int().min(0).max(Number.MAX_SAFE_INTEGER).optional(),
19113
+ scheduledAt: z24.number().int().min(0).max(Number.MAX_SAFE_INTEGER).optional()
17438
19114
  }).strict();
17439
19115
  registerCommand("supervisor.create", createSupervisorSchema, async (args, ctx) => {
17440
19116
  return {
@@ -17636,7 +19312,7 @@ var init_worktree = __esm({
17636
19312
 
17637
19313
  // packages/server/src/commands/worktree.ts
17638
19314
  import path13 from "node:path";
17639
- import { z as z19 } from "zod";
19315
+ import { z as z25 } from "zod";
17640
19316
  async function findRelatedWorkspaceIds(ctx, workspacePath) {
17641
19317
  const targetCommonDir = await getGitCommonDirPath(workspacePath);
17642
19318
  const relatedWorkspaceIds = await Promise.all(
@@ -17669,7 +19345,7 @@ var init_worktree2 = __esm({
17669
19345
  init_worktree();
17670
19346
  init_dispatch();
17671
19347
  init_git_events();
17672
- registerCommand("worktree.list", z19.object({ workspaceId: z19.string() }), async (args, ctx) => {
19348
+ registerCommand("worktree.list", z25.object({ workspaceId: z25.string() }), async (args, ctx) => {
17673
19349
  const workspace = ctx.workspaceMgr.get(args.workspaceId);
17674
19350
  if (!workspace) {
17675
19351
  throw { code: "workspace_not_found", message: `Workspace not found: ${args.workspaceId}` };
@@ -17678,7 +19354,7 @@ var init_worktree2 = __esm({
17678
19354
  });
17679
19355
  registerCommand(
17680
19356
  "worktree.status",
17681
- z19.object({ workspaceId: z19.string(), worktreePath: z19.string() }),
19357
+ z25.object({ workspaceId: z25.string(), worktreePath: z25.string() }),
17682
19358
  async (args, ctx) => {
17683
19359
  const workspace = ctx.workspaceMgr.get(args.workspaceId);
17684
19360
  if (!workspace) {
@@ -17690,10 +19366,10 @@ var init_worktree2 = __esm({
17690
19366
  );
17691
19367
  registerCommand(
17692
19368
  "worktree.diff",
17693
- z19.object({
17694
- workspaceId: z19.string(),
17695
- worktreePath: z19.string(),
17696
- staged: z19.boolean().optional().default(false)
19369
+ z25.object({
19370
+ workspaceId: z25.string(),
19371
+ worktreePath: z25.string(),
19372
+ staged: z25.boolean().optional().default(false)
17697
19373
  }),
17698
19374
  async (args, ctx) => {
17699
19375
  const workspace = ctx.workspaceMgr.get(args.workspaceId);
@@ -17706,7 +19382,7 @@ var init_worktree2 = __esm({
17706
19382
  );
17707
19383
  registerCommand(
17708
19384
  "worktree.tree",
17709
- z19.object({ workspaceId: z19.string(), worktreePath: z19.string() }),
19385
+ z25.object({ workspaceId: z25.string(), worktreePath: z25.string() }),
17710
19386
  async (args, ctx) => {
17711
19387
  const workspace = ctx.workspaceMgr.get(args.workspaceId);
17712
19388
  if (!workspace) {
@@ -17718,10 +19394,10 @@ var init_worktree2 = __esm({
17718
19394
  );
17719
19395
  registerCommand(
17720
19396
  "worktree.create",
17721
- z19.object({
17722
- workspaceId: z19.string(),
17723
- branch: z19.string(),
17724
- path: z19.string()
19397
+ z25.object({
19398
+ workspaceId: z25.string(),
19399
+ branch: z25.string(),
19400
+ path: z25.string()
17725
19401
  }),
17726
19402
  async (args, ctx) => {
17727
19403
  const workspace = ctx.workspaceMgr.get(args.workspaceId);
@@ -17736,10 +19412,10 @@ var init_worktree2 = __esm({
17736
19412
  );
17737
19413
  registerCommand(
17738
19414
  "worktree.remove",
17739
- z19.object({
17740
- workspaceId: z19.string(),
17741
- worktreePath: z19.string(),
17742
- force: z19.boolean().optional().default(false)
19415
+ z25.object({
19416
+ workspaceId: z25.string(),
19417
+ worktreePath: z25.string(),
19418
+ force: z25.boolean().optional().default(false)
17743
19419
  }),
17744
19420
  async (args, ctx) => {
17745
19421
  const workspace = ctx.workspaceMgr.get(args.workspaceId);
@@ -17763,7 +19439,7 @@ var init_worktree2 = __esm({
17763
19439
  });
17764
19440
 
17765
19441
  // packages/server/src/commands/fencing.ts
17766
- import { z as z20 } from "zod";
19442
+ import { z as z26 } from "zod";
17767
19443
  function createMockFencingRequest() {
17768
19444
  return {
17769
19445
  ip: "127.0.0.1",
@@ -17776,9 +19452,9 @@ var init_fencing2 = __esm({
17776
19452
  init_dispatch();
17777
19453
  registerCommand(
17778
19454
  "fencing.request",
17779
- z20.object({
17780
- workspaceId: z20.string(),
17781
- tabId: z20.string()
19455
+ z26.object({
19456
+ workspaceId: z26.string(),
19457
+ tabId: z26.string()
17782
19458
  }),
17783
19459
  async (args, ctx, clientId) => {
17784
19460
  return ctx.fencingMgr.requestControl(
@@ -17791,7 +19467,7 @@ var init_fencing2 = __esm({
17791
19467
  );
17792
19468
  registerCommand(
17793
19469
  "fencing.heartbeat",
17794
- z20.object({ workspaceId: z20.string() }),
19470
+ z26.object({ workspaceId: z26.string() }),
17795
19471
  async (args, ctx, clientId) => {
17796
19472
  const success = ctx.fencingMgr.heartbeat(args.workspaceId, clientId);
17797
19473
  return { success };
@@ -17799,13 +19475,13 @@ var init_fencing2 = __esm({
17799
19475
  );
17800
19476
  registerCommand(
17801
19477
  "fencing.release",
17802
- z20.object({ workspaceId: z20.string() }),
19478
+ z26.object({ workspaceId: z26.string() }),
17803
19479
  async (args, ctx, clientId) => {
17804
19480
  ctx.fencingMgr.release(args.workspaceId, clientId);
17805
19481
  return {};
17806
19482
  }
17807
19483
  );
17808
- registerCommand("fencing.status", z20.object({ workspaceId: z20.string() }), async (args, ctx) => {
19484
+ registerCommand("fencing.status", z26.object({ workspaceId: z26.string() }), async (args, ctx) => {
17809
19485
  const controller = ctx.fencingMgr.getController(args.workspaceId);
17810
19486
  const isUnresponsive = ctx.fencingMgr.isControllerUnresponsive(args.workspaceId);
17811
19487
  return {
@@ -17816,9 +19492,9 @@ var init_fencing2 = __esm({
17816
19492
  });
17817
19493
  registerCommand(
17818
19494
  "fencing.takeover",
17819
- z20.object({
17820
- workspaceId: z20.string(),
17821
- tabId: z20.string()
19495
+ z26.object({
19496
+ workspaceId: z26.string(),
19497
+ tabId: z26.string()
17822
19498
  }),
17823
19499
  async (args, ctx, clientId) => {
17824
19500
  return ctx.fencingMgr.forceTakeover(
@@ -17856,7 +19532,7 @@ var init_runtime_status2 = __esm({
17856
19532
  });
17857
19533
 
17858
19534
  // packages/server/src/commands/lsp.ts
17859
- import { z as z21 } from "zod";
19535
+ import { z as z27 } from "zod";
17860
19536
  var init_lsp2 = __esm({
17861
19537
  "packages/server/src/commands/lsp.ts"() {
17862
19538
  "use strict";
@@ -17864,16 +19540,16 @@ var init_lsp2 = __esm({
17864
19540
  init_dispatch();
17865
19541
  registerCommand(
17866
19542
  "lsp.ensureSession",
17867
- z21.object({
17868
- workspaceId: z21.string(),
17869
- path: z21.string()
19543
+ z27.object({
19544
+ workspaceId: z27.string(),
19545
+ path: z27.string()
17870
19546
  }),
17871
19547
  async (args, ctx) => ctx.lspMgr.ensureSession(args)
17872
19548
  );
17873
19549
  registerCommand(
17874
19550
  "lsp.setMode",
17875
- z21.object({
17876
- mode: z21.enum(["auto", "off"])
19551
+ z27.object({
19552
+ mode: z27.enum(["auto", "off"])
17877
19553
  }),
17878
19554
  async (args, ctx) => {
17879
19555
  await ctx.lspMgr.setRuntimeMode(args.mode);
@@ -17882,8 +19558,8 @@ var init_lsp2 = __esm({
17882
19558
  );
17883
19559
  registerCommand(
17884
19560
  "lsp.runtimeStatus",
17885
- z21.object({
17886
- workspaceId: z21.string()
19561
+ z27.object({
19562
+ workspaceId: z27.string()
17887
19563
  }),
17888
19564
  async (args, ctx) => {
17889
19565
  if (!ctx.lspToolMgr) {
@@ -17907,9 +19583,9 @@ var init_lsp2 = __esm({
17907
19583
  );
17908
19584
  registerCommand(
17909
19585
  "lsp.install.start",
17910
- z21.object({
17911
- workspaceId: z21.string(),
17912
- serverKind: z21.enum(["typescript", "python", "go", "rust"])
19586
+ z27.object({
19587
+ workspaceId: z27.string(),
19588
+ serverKind: z27.enum(["typescript", "python", "go", "rust"])
17913
19589
  }),
17914
19590
  async (args, ctx) => {
17915
19591
  if (!ctx.lspToolInstallMgr) {
@@ -17933,8 +19609,8 @@ var init_lsp2 = __esm({
17933
19609
  );
17934
19610
  registerCommand(
17935
19611
  "lsp.install.get",
17936
- z21.object({
17937
- jobId: z21.string()
19612
+ z27.object({
19613
+ jobId: z27.string()
17938
19614
  }),
17939
19615
  async (args, ctx) => {
17940
19616
  if (!ctx.lspToolInstallMgr) {
@@ -17955,86 +19631,86 @@ var init_lsp2 = __esm({
17955
19631
  );
17956
19632
  registerCommand(
17957
19633
  "lsp.openDocument",
17958
- z21.object({
17959
- workspaceId: z21.string(),
17960
- path: z21.string(),
17961
- languageId: z21.string(),
17962
- text: z21.string()
19634
+ z27.object({
19635
+ workspaceId: z27.string(),
19636
+ path: z27.string(),
19637
+ languageId: z27.string(),
19638
+ text: z27.string()
17963
19639
  }),
17964
19640
  async (args, ctx) => ctx.lspMgr.openDocument(args)
17965
19641
  );
17966
19642
  registerCommand(
17967
19643
  "lsp.changeDocument",
17968
- z21.object({
17969
- workspaceId: z21.string(),
17970
- path: z21.string(),
17971
- text: z21.string()
19644
+ z27.object({
19645
+ workspaceId: z27.string(),
19646
+ path: z27.string(),
19647
+ text: z27.string()
17972
19648
  }),
17973
19649
  async (args, ctx) => ctx.lspMgr.changeDocument(args)
17974
19650
  );
17975
19651
  registerCommand(
17976
19652
  "lsp.closeDocument",
17977
- z21.object({
17978
- workspaceId: z21.string(),
17979
- path: z21.string()
19653
+ z27.object({
19654
+ workspaceId: z27.string(),
19655
+ path: z27.string()
17980
19656
  }),
17981
19657
  async (args, ctx) => ctx.lspMgr.closeDocument(args)
17982
19658
  );
17983
19659
  registerCommand(
17984
19660
  "lsp.definition",
17985
- z21.object({
17986
- workspaceId: z21.string(),
17987
- path: z21.string(),
17988
- line: z21.number().int().positive(),
17989
- column: z21.number().int().positive()
19661
+ z27.object({
19662
+ workspaceId: z27.string(),
19663
+ path: z27.string(),
19664
+ line: z27.number().int().positive(),
19665
+ column: z27.number().int().positive()
17990
19666
  }),
17991
19667
  async (args, ctx) => ctx.lspMgr.definition(args)
17992
19668
  );
17993
19669
  registerCommand(
17994
19670
  "lsp.declaration",
17995
- z21.object({
17996
- workspaceId: z21.string(),
17997
- path: z21.string(),
17998
- line: z21.number().int().positive(),
17999
- column: z21.number().int().positive()
19671
+ z27.object({
19672
+ workspaceId: z27.string(),
19673
+ path: z27.string(),
19674
+ line: z27.number().int().positive(),
19675
+ column: z27.number().int().positive()
18000
19676
  }),
18001
19677
  async (args, ctx) => ctx.lspMgr.declaration(args)
18002
19678
  );
18003
19679
  registerCommand(
18004
19680
  "lsp.typeDefinition",
18005
- z21.object({
18006
- workspaceId: z21.string(),
18007
- path: z21.string(),
18008
- line: z21.number().int().positive(),
18009
- column: z21.number().int().positive()
19681
+ z27.object({
19682
+ workspaceId: z27.string(),
19683
+ path: z27.string(),
19684
+ line: z27.number().int().positive(),
19685
+ column: z27.number().int().positive()
18010
19686
  }),
18011
19687
  async (args, ctx) => ctx.lspMgr.typeDefinition(args)
18012
19688
  );
18013
19689
  registerCommand(
18014
19690
  "lsp.references",
18015
- z21.object({
18016
- workspaceId: z21.string(),
18017
- path: z21.string(),
18018
- line: z21.number().int().positive(),
18019
- column: z21.number().int().positive()
19691
+ z27.object({
19692
+ workspaceId: z27.string(),
19693
+ path: z27.string(),
19694
+ line: z27.number().int().positive(),
19695
+ column: z27.number().int().positive()
18020
19696
  }),
18021
19697
  async (args, ctx) => ctx.lspMgr.references(args)
18022
19698
  );
18023
19699
  registerCommand(
18024
19700
  "lsp.hover",
18025
- z21.object({
18026
- workspaceId: z21.string(),
18027
- path: z21.string(),
18028
- line: z21.number().int().positive(),
18029
- column: z21.number().int().positive()
19701
+ z27.object({
19702
+ workspaceId: z27.string(),
19703
+ path: z27.string(),
19704
+ line: z27.number().int().positive(),
19705
+ column: z27.number().int().positive()
18030
19706
  }),
18031
19707
  async (args, ctx) => ctx.lspMgr.hover(args)
18032
19708
  );
18033
19709
  registerCommand(
18034
19710
  "lsp.documentSymbols",
18035
- z21.object({
18036
- workspaceId: z21.string(),
18037
- path: z21.string()
19711
+ z27.object({
19712
+ workspaceId: z27.string(),
19713
+ path: z27.string()
18038
19714
  }),
18039
19715
  async (args, ctx) => ctx.lspMgr.documentSymbols(args)
18040
19716
  );
@@ -18042,12 +19718,12 @@ var init_lsp2 = __esm({
18042
19718
  });
18043
19719
 
18044
19720
  // packages/server/src/commands/updates.ts
18045
- import { z as z22 } from "zod";
19721
+ import { z as z28 } from "zod";
18046
19722
  var init_updates = __esm({
18047
19723
  "packages/server/src/commands/updates.ts"() {
18048
19724
  "use strict";
18049
19725
  init_dispatch();
18050
- registerCommand("updates.getState", z22.object({}).default({}), async (_args, ctx) => {
19726
+ registerCommand("updates.getState", z28.object({}).default({}), async (_args, ctx) => {
18051
19727
  if (!ctx.updateService) {
18052
19728
  throw {
18053
19729
  code: "update_unavailable",
@@ -18056,7 +19732,7 @@ var init_updates = __esm({
18056
19732
  }
18057
19733
  return ctx.updateService.getStateView();
18058
19734
  });
18059
- registerCommand("updates.check", z22.object({}).default({}), async (_args, ctx) => {
19735
+ registerCommand("updates.check", z28.object({}).default({}), async (_args, ctx) => {
18060
19736
  if (!ctx.updateService) {
18061
19737
  throw {
18062
19738
  code: "update_unavailable",
@@ -18065,7 +19741,7 @@ var init_updates = __esm({
18065
19741
  }
18066
19742
  return await ctx.updateService.checkForUpdates({ manual: true });
18067
19743
  });
18068
- registerCommand("updates.prepareInstall", z22.object({}).default({}), async (_args, ctx) => {
19744
+ registerCommand("updates.prepareInstall", z28.object({}).default({}), async (_args, ctx) => {
18069
19745
  if (!ctx.updateService) {
18070
19746
  throw {
18071
19747
  code: "update_unavailable",
@@ -18076,9 +19752,9 @@ var init_updates = __esm({
18076
19752
  });
18077
19753
  registerCommand(
18078
19754
  "updates.startInstall",
18079
- z22.object({
18080
- targetVersion: z22.string().optional(),
18081
- force: z22.boolean().optional()
19755
+ z28.object({
19756
+ targetVersion: z28.string().optional(),
19757
+ force: z28.boolean().optional()
18082
19758
  }),
18083
19759
  async (args, ctx) => {
18084
19760
  if (!ctx.updateService) {
@@ -18103,12 +19779,17 @@ var init_commands = __esm({
18103
19779
  init_connection();
18104
19780
  init_recovery();
18105
19781
  init_session2();
19782
+ init_session_metadata();
19783
+ init_session_review();
18106
19784
  init_terminal();
18107
19785
  init_file();
18108
19786
  init_git2();
19787
+ init_agent_instructions();
19788
+ init_agent_context();
18109
19789
  init_settings2();
18110
19790
  init_diagnostics2();
18111
19791
  init_provider();
19792
+ init_custom_provider2();
18112
19793
  init_supervisor2();
18113
19794
  init_worktree2();
18114
19795
  init_fencing2();
@@ -18120,12 +19801,12 @@ var init_commands = __esm({
18120
19801
  // packages/server/src/server.ts
18121
19802
  import { mkdtempSync, rmSync as rmSync2 } from "node:fs";
18122
19803
  import { tmpdir } from "node:os";
18123
- import { join as join15 } from "node:path";
19804
+ import { join as join18 } from "node:path";
18124
19805
  async function createServer(configOverrides) {
18125
19806
  const config = parseServerConfig(configOverrides);
18126
19807
  const configuredStateDir = resolveConfiguredStateDir(config);
18127
19808
  const shouldCleanupStateRoot = configuredStateDir === IN_MEMORY_STATE_DIR;
18128
- const stateRoot = shouldCleanupStateRoot ? mkdtempSync(join15(tmpdir(), "coder-studio-state-")) : configuredStateDir;
19809
+ const stateRoot = shouldCleanupStateRoot ? mkdtempSync(join18(tmpdir(), "coder-studio-state-")) : configuredStateDir;
18129
19810
  ensureStateDir(config);
18130
19811
  const eventBus = new EventBus();
18131
19812
  const activationMgr = new ActivationManager();
@@ -18135,10 +19816,10 @@ async function createServer(configOverrides) {
18135
19816
  let commandContext;
18136
19817
  let lspMgr = null;
18137
19818
  const terminalRepo = new TerminalRepo({
18138
- filePath: join15(stateRoot, "state", "terminals.json")
19819
+ filePath: join18(stateRoot, "state", "terminals.json")
18139
19820
  });
18140
19821
  const sessionRepo = new SessionRepo({
18141
- filePath: join15(stateRoot, "state", "sessions.json")
19822
+ filePath: join18(stateRoot, "state", "sessions.json")
18142
19823
  });
18143
19824
  const terminalMgr = new TerminalManager({
18144
19825
  ptyHost: createPtyHost(),
@@ -18146,10 +19827,10 @@ async function createServer(configOverrides) {
18146
19827
  db: terminalRepo
18147
19828
  });
18148
19829
  const settingsRepo = new SettingsRepo({
18149
- filePath: join15(stateRoot, "state", "settings.json")
19830
+ filePath: join18(stateRoot, "state", "settings.json")
18150
19831
  });
18151
19832
  const updateStateRepo = new UpdateStateRepo({
18152
- filePath: join15(stateRoot, "state", "update-state.json"),
19833
+ filePath: join18(stateRoot, "state", "update-state.json"),
18153
19834
  currentVersion: config.appVersion ?? "0.0.0"
18154
19835
  });
18155
19836
  const autoFetch = new AutoFetchScheduler({
@@ -18182,17 +19863,27 @@ async function createServer(configOverrides) {
18182
19863
  }
18183
19864
  });
18184
19865
  const providerConfigRepo = new ProviderConfigRepo({
18185
- filePath: join15(stateRoot, "state", "provider-configs.json")
19866
+ filePath: join18(stateRoot, "state", "provider-configs.json")
19867
+ });
19868
+ const customProviderRepo = new CustomProviderRepo({
19869
+ filePath: join18(stateRoot, "state", "custom-providers.json")
18186
19870
  });
18187
19871
  const workspaceRepo = new WorkspaceRepo({
18188
- filePath: join15(stateRoot, "state", "workspaces.json")
19872
+ filePath: join18(stateRoot, "state", "workspaces.json")
19873
+ });
19874
+ const sessionMetadataRepo = new SessionMetadataRepo({
19875
+ workspaceRepo
18189
19876
  });
19877
+ let activeProviderRegistry = [
19878
+ ...providerRegistry,
19879
+ ...customProviderRepo.list().map((config2) => buildCustomProviderDefinition(config2))
19880
+ ];
18190
19881
  const sessionMgr = new SessionManager({
18191
19882
  terminalMgr,
18192
19883
  eventBus,
18193
19884
  db: sessionRepo,
18194
19885
  broadcaster: wsHub,
18195
- providerRegistry,
19886
+ providerRegistry: activeProviderRegistry,
18196
19887
  providerConfigRepo
18197
19888
  });
18198
19889
  let supervisorMgr;
@@ -18203,13 +19894,15 @@ async function createServer(configOverrides) {
18203
19894
  broadcaster: wsHub,
18204
19895
  autoFetch,
18205
19896
  teardown: async (workspaceId) => {
19897
+ const persistedSessions = sessionRepo.findByWorkspaceId(workspaceId);
18206
19898
  await lspMgr?.disposeWorkspace(workspaceId);
18207
19899
  await supervisorMgr?.deleteForWorkspace(workspaceId);
18208
19900
  await sessionMgr.stopForWorkspace(workspaceId);
18209
19901
  await terminalMgr.closeForWorkspace(workspaceId);
18210
19902
  sessionMgr.deleteEndedForWorkspace(workspaceId);
18211
- for (const session of sessionRepo.findByWorkspaceId(workspaceId)) {
19903
+ for (const session of persistedSessions) {
18212
19904
  sessionRepo.delete(session.id);
19905
+ sessionMetadataRepo.delete(session.id);
18213
19906
  }
18214
19907
  for (const terminal of terminalRepo.listByWorkspace(workspaceId)) {
18215
19908
  terminalRepo.delete(terminal.id);
@@ -18220,13 +19913,13 @@ async function createServer(configOverrides) {
18220
19913
  )
18221
19914
  });
18222
19915
  const authSessionRepo = new AuthSessionRepo({
18223
- filePath: join15(stateRoot, "state", "auth-sessions.json")
19916
+ filePath: join18(stateRoot, "state", "auth-sessions.json")
18224
19917
  });
18225
19918
  const authLoginBlockRepo = new AuthLoginBlockRepo({
18226
- filePath: join15(stateRoot, "state", "auth-login-blocks.json")
19919
+ filePath: join18(stateRoot, "state", "auth-login-blocks.json")
18227
19920
  });
18228
19921
  const appearanceAssetRepo = new AppearanceAssetRepo({
18229
- filePath: join15(stateRoot, "state", "appearance-assets.json")
19922
+ filePath: join18(stateRoot, "state", "appearance-assets.json")
18230
19923
  });
18231
19924
  const app = await buildFastifyApp({
18232
19925
  wsHub,
@@ -18277,7 +19970,7 @@ async function createServer(configOverrides) {
18277
19970
  terminalMgr,
18278
19971
  workspaceMgr,
18279
19972
  sessionMgr,
18280
- providerRegistry,
19973
+ providerRegistry: activeProviderRegistry,
18281
19974
  providerConfigRepo,
18282
19975
  settingsRepo,
18283
19976
  supervisorRepo,
@@ -18290,7 +19983,7 @@ async function createServer(configOverrides) {
18290
19983
  const providerRuntimeDeps = providerMockOverrides ? {
18291
19984
  commandExists: providerMockOverrides.commandExists
18292
19985
  } : {};
18293
- const providerInstallMgr = new ProviderInstallManager(providerRegistry, {
19986
+ const providerInstallMgr = new ProviderInstallManager(activeProviderRegistry, {
18294
19987
  ...providerRuntimeDeps,
18295
19988
  runCommand: providerMockOverrides?.runCommand ?? runCommandAsString
18296
19989
  });
@@ -18302,7 +19995,7 @@ async function createServer(configOverrides) {
18302
19995
  ...config.update,
18303
19996
  currentVersion: config.appVersion ?? "0.0.0"
18304
19997
  },
18305
- updateWorkerLogFilePath: join15(stateRoot, "logs", "update-worker.log"),
19998
+ updateWorkerLogFilePath: join18(stateRoot, "logs", "update-worker.log"),
18306
19999
  countRunningTerminals: () => terminalMgr.getAll().filter((terminal) => terminal.alive).length,
18307
20000
  countRunningSessions: () => sessionMgr.getAll().filter((session) => session.state === "starting" || session.state === "running").length,
18308
20001
  countActiveSupervisors: () => supervisorMgr?.countActive() ?? 0
@@ -18316,7 +20009,7 @@ async function createServer(configOverrides) {
18316
20009
  broadcaster: wsHub,
18317
20010
  settingsRepo,
18318
20011
  providerConfigRepo,
18319
- providerRegistry,
20012
+ providerRegistry: activeProviderRegistry,
18320
20013
  fencingMgr,
18321
20014
  supervisorMgr,
18322
20015
  autoFetch,
@@ -18327,7 +20020,16 @@ async function createServer(configOverrides) {
18327
20020
  lspMgr,
18328
20021
  lspToolMgr,
18329
20022
  lspToolInstallMgr,
18330
- updateService
20023
+ updateService,
20024
+ customProviderRepo,
20025
+ sessionMetadataRepo,
20026
+ setProviderRegistry: (providers) => {
20027
+ activeProviderRegistry = providers;
20028
+ commandContext.providerRegistry = providers;
20029
+ providerInstallMgr.setProviders(providers);
20030
+ sessionMgr.setProviderRegistry(providers);
20031
+ supervisorMgr?.setProviderRegistry(providers);
20032
+ }
18331
20033
  };
18332
20034
  wsHub.setCommandContext(commandContext);
18333
20035
  await app.listen({
@@ -18411,13 +20113,16 @@ var init_server = __esm({
18411
20113
  init_manifest_store();
18412
20114
  init_tool_root();
18413
20115
  init_command_runner();
20116
+ init_custom_provider();
18414
20117
  init_e2e_provider_mock();
18415
20118
  init_install_manager2();
18416
20119
  init_manager3();
18417
20120
  init_appearance_asset_repo();
18418
20121
  init_auth_login_block_repo();
18419
20122
  init_auth_session_repo();
20123
+ init_custom_provider_repo();
18420
20124
  init_provider_config_repo();
20125
+ init_session_metadata_repo();
18421
20126
  init_session_repo();
18422
20127
  init_settings_repo();
18423
20128
  init_supervisor_repo();
@@ -18460,7 +20165,9 @@ var init_storage = __esm({
18460
20165
  "use strict";
18461
20166
  init_auth_login_block_repo();
18462
20167
  init_auth_session_repo();
20168
+ init_custom_provider_repo();
18463
20169
  init_provider_config_repo();
20170
+ init_session_metadata_repo();
18464
20171
  init_session_repo();
18465
20172
  init_settings_repo();
18466
20173
  init_supervisor_repo();
@@ -18488,10 +20195,12 @@ __export(src_exports, {
18488
20195
  ActiveTerminal: () => ActiveTerminal,
18489
20196
  AuthLoginBlockRepo: () => AuthLoginBlockRepo,
18490
20197
  AuthSessionRepo: () => AuthSessionRepo,
20198
+ CustomProviderRepo: () => CustomProviderRepo,
18491
20199
  EventBus: () => EventBus,
18492
20200
  NodePtyHost: () => NodePtyHost,
18493
20201
  ProviderConfigRepo: () => ProviderConfigRepo,
18494
20202
  RingBuffer: () => RingBuffer,
20203
+ SessionMetadataRepo: () => SessionMetadataRepo,
18495
20204
  SessionRepo: () => SessionRepo,
18496
20205
  SettingsRepo: () => SettingsRepo,
18497
20206
  SupervisorRepo: () => SupervisorRepo,
@@ -18533,9 +20242,9 @@ import { fileURLToPath as fileURLToPath4 } from "url";
18533
20242
  init_state_paths();
18534
20243
  import { existsSync as existsSync10, mkdirSync as mkdirSync8, readFileSync as readFileSync8, writeFileSync as writeFileSync6 } from "fs";
18535
20244
  import { homedir as homedir4 } from "os";
18536
- import { join as join16 } from "path";
20245
+ import { join as join19 } from "path";
18537
20246
  function getCliConfigPath() {
18538
- return join16(homedir4(), ".coder-studio", "config.json");
20247
+ return join19(homedir4(), ".coder-studio", "config.json");
18539
20248
  }
18540
20249
  function normalizeLegacyDataDir(input) {
18541
20250
  return normalizeLegacyStateDir(input);
@@ -18563,11 +20272,11 @@ function readCliConfig() {
18563
20272
 
18564
20273
  // packages/cli/src/embed.ts
18565
20274
  import { existsSync as existsSync11 } from "fs";
18566
- import { dirname as dirname8, resolve as resolve5 } from "path";
20275
+ import { dirname as dirname8, resolve as resolve7 } from "path";
18567
20276
  import { fileURLToPath as fileURLToPath2 } from "url";
18568
20277
  var __filename = fileURLToPath2(import.meta.url);
18569
20278
  var __dirname = dirname8(__filename);
18570
- var WEB_ASSETS_DIR = resolve5(__dirname, "../web");
20279
+ var WEB_ASSETS_DIR = resolve7(__dirname, "../web");
18571
20280
  function getStaticAssetsDir() {
18572
20281
  return WEB_ASSETS_DIR;
18573
20282
  }
@@ -18631,13 +20340,13 @@ function getCliPackageName(importMetaUrl) {
18631
20340
 
18632
20341
  // packages/cli/src/update-runtime.ts
18633
20342
  import { existsSync as existsSync13 } from "node:fs";
18634
- import { dirname as dirname9, join as join17 } from "node:path";
20343
+ import { dirname as dirname9, join as join20 } from "node:path";
18635
20344
  import { fileURLToPath as fileURLToPath3 } from "node:url";
18636
20345
  function resolveWorkerEntryPath(importMetaUrl) {
18637
20346
  const currentDir = dirname9(fileURLToPath3(importMetaUrl));
18638
20347
  const candidates = [
18639
- join17(currentDir, "update-worker.mjs"),
18640
- join17(currentDir, "../src/update-worker.ts")
20348
+ join20(currentDir, "update-worker.mjs"),
20349
+ join20(currentDir, "../src/update-worker.ts")
18641
20350
  ];
18642
20351
  return candidates.find((candidate) => existsSync13(candidate));
18643
20352
  }