@spencer-kit/coder-studio 0.3.4 → 0.3.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/esm/bin.mjs CHANGED
@@ -615,6 +615,7 @@ var init_config_schema2 = __esm({
615
615
  "packages/providers/src/codex/config-schema.ts"() {
616
616
  "use strict";
617
617
  codexConfigSchema = z2.object({
618
+ model: z2.string().min(1).optional(),
618
619
  additionalArgs: z2.array(z2.string()).default([]),
619
620
  envVars: z2.record(z2.string(), z2.string()).default({})
620
621
  });
@@ -661,6 +662,7 @@ function buildCodexSupervisorEvalCommand(config, req) {
661
662
  "-s",
662
663
  "read-only",
663
664
  "--skip-git-repo-check",
665
+ ...req.model ? ["-m", req.model] : [],
664
666
  ...cfg.additionalArgs,
665
667
  req.prompt
666
668
  ],
@@ -2727,12 +2729,46 @@ function resolveSupervisorEvaluationTimeoutSec(value) {
2727
2729
  }
2728
2730
  return value;
2729
2731
  }
2730
- var DEFAULT_SUPERVISOR_EVALUATION_TIMEOUT_SEC, MAX_SUPERVISOR_EVALUATION_TIMEOUT_SEC, DEFAULT_SUPERVISOR_CONFIG;
2732
+ function resolveSupervisorRetryEnabled(value) {
2733
+ return typeof value === "boolean" ? value : DEFAULT_SUPERVISOR_RETRY_ENABLED;
2734
+ }
2735
+ function resolveSupervisorRetryMaxCount(value) {
2736
+ if (typeof value !== "number" || !Number.isFinite(value) || !Number.isSafeInteger(value)) {
2737
+ return DEFAULT_SUPERVISOR_RETRY_MAX_COUNT;
2738
+ }
2739
+ if (value < 0 || value > MAX_SUPERVISOR_RETRY_MAX_COUNT) {
2740
+ return DEFAULT_SUPERVISOR_RETRY_MAX_COUNT;
2741
+ }
2742
+ return value;
2743
+ }
2744
+ function resolveSupervisorRetryDelaySec(value) {
2745
+ if (typeof value !== "number" || !Number.isFinite(value) || !Number.isSafeInteger(value)) {
2746
+ return DEFAULT_SUPERVISOR_RETRY_DELAY_SEC;
2747
+ }
2748
+ if (value < 1 || value > MAX_SUPERVISOR_RETRY_DELAY_SEC) {
2749
+ return DEFAULT_SUPERVISOR_RETRY_DELAY_SEC;
2750
+ }
2751
+ return value;
2752
+ }
2753
+ function resolveSupervisorRetryOnTimeout(value) {
2754
+ return typeof value === "boolean" ? value : DEFAULT_SUPERVISOR_RETRY_ON_TIMEOUT;
2755
+ }
2756
+ function resolveSupervisorRetryOnEvaluatorError(value) {
2757
+ return typeof value === "boolean" ? value : DEFAULT_SUPERVISOR_RETRY_ON_EVALUATOR_ERROR;
2758
+ }
2759
+ var DEFAULT_SUPERVISOR_EVALUATION_TIMEOUT_SEC, MAX_SUPERVISOR_EVALUATION_TIMEOUT_SEC, DEFAULT_SUPERVISOR_RETRY_ENABLED, DEFAULT_SUPERVISOR_RETRY_MAX_COUNT, MAX_SUPERVISOR_RETRY_MAX_COUNT, DEFAULT_SUPERVISOR_RETRY_DELAY_SEC, MAX_SUPERVISOR_RETRY_DELAY_SEC, DEFAULT_SUPERVISOR_RETRY_ON_TIMEOUT, DEFAULT_SUPERVISOR_RETRY_ON_EVALUATOR_ERROR, DEFAULT_SUPERVISOR_CONFIG;
2731
2760
  var init_supervisor = __esm({
2732
2761
  "packages/core/src/domain/supervisor.ts"() {
2733
2762
  "use strict";
2734
2763
  DEFAULT_SUPERVISOR_EVALUATION_TIMEOUT_SEC = 600;
2735
2764
  MAX_SUPERVISOR_EVALUATION_TIMEOUT_SEC = 86400;
2765
+ DEFAULT_SUPERVISOR_RETRY_ENABLED = false;
2766
+ DEFAULT_SUPERVISOR_RETRY_MAX_COUNT = 0;
2767
+ MAX_SUPERVISOR_RETRY_MAX_COUNT = 20;
2768
+ DEFAULT_SUPERVISOR_RETRY_DELAY_SEC = 10;
2769
+ MAX_SUPERVISOR_RETRY_DELAY_SEC = 3600;
2770
+ DEFAULT_SUPERVISOR_RETRY_ON_TIMEOUT = true;
2771
+ DEFAULT_SUPERVISOR_RETRY_ON_EVALUATOR_ERROR = false;
2736
2772
  DEFAULT_SUPERVISOR_CONFIG = {
2737
2773
  maxCyclesPerSession: 100,
2738
2774
  terminalLinesForEvaluation: 500,
@@ -3760,7 +3796,7 @@ var init_database = __esm({
3760
3796
  }
3761
3797
  });
3762
3798
 
3763
- // packages/server/src/storage/db.ts
3799
+ // packages/server/src/storage/schema-version.ts
3764
3800
  import { DatabaseSync } from "node:sqlite";
3765
3801
  import { readFileSync as readFileSync4 } from "fs";
3766
3802
  import { join as join3 } from "path";
@@ -3784,15 +3820,229 @@ function listSchemaEntries(db) {
3784
3820
  sql: normalizeSql(row.sql)
3785
3821
  }));
3786
3822
  }
3787
- function buildExpectedSchemaEntries() {
3823
+ function schemaEntrySignature(entry) {
3824
+ return `${entry.type}:${entry.name}:${entry.tableName}:${entry.sql}`;
3825
+ }
3826
+ function buildSchemaEntries(schemaSql) {
3788
3827
  const db = new DatabaseSync(":memory:");
3789
3828
  try {
3790
- db.exec(SCHEMA_SQL);
3829
+ db.exec(schemaSql);
3791
3830
  return listSchemaEntries(db);
3792
3831
  } finally {
3793
3832
  db.close();
3794
3833
  }
3795
3834
  }
3835
+ function hasExactFingerprint(actualEntries, expectedEntries) {
3836
+ if (actualEntries.length !== expectedEntries.length) {
3837
+ return false;
3838
+ }
3839
+ return actualEntries.every(
3840
+ (entry, index) => schemaEntrySignature(entry) === schemaEntrySignature(expectedEntries[index])
3841
+ );
3842
+ }
3843
+ function describeSchemaMismatch(expected, actual) {
3844
+ const expectedByName = new Map(expected.map((entry) => [`${entry.type}:${entry.name}`, entry]));
3845
+ const actualByName = new Map(actual.map((entry) => [`${entry.type}:${entry.name}`, entry]));
3846
+ const keys = /* @__PURE__ */ new Set([...expectedByName.keys(), ...actualByName.keys()]);
3847
+ for (const key of keys) {
3848
+ const expectedEntry = expectedByName.get(key);
3849
+ const actualEntry = actualByName.get(key);
3850
+ if (!expectedEntry) {
3851
+ return `unexpected ${actualEntry?.type ?? "schema object"} ${actualEntry?.name ?? key}`;
3852
+ }
3853
+ if (!actualEntry) {
3854
+ return `missing ${expectedEntry.type} ${expectedEntry.name}`;
3855
+ }
3856
+ if (schemaEntrySignature(expectedEntry) !== schemaEntrySignature(actualEntry)) {
3857
+ return `definition mismatch for ${expectedEntry.type} ${expectedEntry.name}`;
3858
+ }
3859
+ }
3860
+ return "unknown schema drift";
3861
+ }
3862
+ function detectSchema(db) {
3863
+ const actualEntries = listSchemaEntries(db);
3864
+ const userVersionRow = db.prepare("PRAGMA user_version").get();
3865
+ const userVersion = userVersionRow?.user_version ?? 0;
3866
+ if (actualEntries.length === 0) {
3867
+ return {
3868
+ state: "empty",
3869
+ userVersion,
3870
+ mismatch: null
3871
+ };
3872
+ }
3873
+ if (hasExactFingerprint(actualEntries, CURRENT_SCHEMA_ENTRIES)) {
3874
+ return {
3875
+ state: "current",
3876
+ userVersion,
3877
+ mismatch: null
3878
+ };
3879
+ }
3880
+ if (hasExactFingerprint(actualEntries, V1_SCHEMA_ENTRIES)) {
3881
+ return {
3882
+ state: "v1",
3883
+ userVersion,
3884
+ mismatch: null
3885
+ };
3886
+ }
3887
+ return {
3888
+ state: "incompatible",
3889
+ userVersion,
3890
+ mismatch: describeSchemaMismatch(CURRENT_SCHEMA_ENTRIES, actualEntries)
3891
+ };
3892
+ }
3893
+ function stampCurrentSchemaVersion(db) {
3894
+ db.exec(`PRAGMA user_version = ${CURRENT_SCHEMA_VERSION}`);
3895
+ }
3896
+ var CURRENT_SCHEMA_VERSION, CURRENT_SCHEMA_PATH, CURRENT_SCHEMA_SQL, V1_SCHEMA_SQL, CURRENT_SCHEMA_ENTRIES, V1_SCHEMA_ENTRIES, IncompatibleSchemaError;
3897
+ var init_schema_version = __esm({
3898
+ "packages/server/src/storage/schema-version.ts"() {
3899
+ "use strict";
3900
+ CURRENT_SCHEMA_VERSION = 2;
3901
+ CURRENT_SCHEMA_PATH = join3(import.meta.dirname, "migrations", "001_init.sql");
3902
+ CURRENT_SCHEMA_SQL = readFileSync4(CURRENT_SCHEMA_PATH, "utf-8");
3903
+ V1_SCHEMA_SQL = `
3904
+ CREATE TABLE workspaces (
3905
+ id TEXT PRIMARY KEY,
3906
+ path TEXT NOT NULL UNIQUE,
3907
+ target_runtime TEXT NOT NULL,
3908
+ wsl_distro TEXT,
3909
+ opened_at INTEGER NOT NULL,
3910
+ last_active_at INTEGER NOT NULL,
3911
+ ui_state TEXT
3912
+ );
3913
+
3914
+ CREATE TABLE terminals (
3915
+ id TEXT PRIMARY KEY,
3916
+ workspace_id TEXT NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE,
3917
+ kind TEXT NOT NULL,
3918
+ cwd TEXT NOT NULL,
3919
+ argv TEXT NOT NULL,
3920
+ env TEXT,
3921
+ title TEXT,
3922
+ cols INTEGER NOT NULL,
3923
+ rows INTEGER NOT NULL,
3924
+ created_at INTEGER NOT NULL,
3925
+ ended_at INTEGER,
3926
+ exit_code INTEGER
3927
+ );
3928
+
3929
+ CREATE INDEX idx_terminals_workspace ON terminals(workspace_id);
3930
+ CREATE INDEX idx_terminals_kind ON terminals(workspace_id, kind);
3931
+
3932
+ CREATE TABLE sessions (
3933
+ id TEXT PRIMARY KEY,
3934
+ workspace_id TEXT NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE,
3935
+ terminal_id TEXT NOT NULL REFERENCES terminals(id) ON DELETE CASCADE,
3936
+ provider_id TEXT NOT NULL,
3937
+ capability TEXT NOT NULL,
3938
+ state TEXT NOT NULL,
3939
+ started_at INTEGER NOT NULL,
3940
+ ended_at INTEGER,
3941
+ last_active_at INTEGER NOT NULL,
3942
+ completion_percent INTEGER,
3943
+ error_reason TEXT,
3944
+ archived BOOLEAN DEFAULT 0,
3945
+ title TEXT
3946
+ );
3947
+
3948
+ CREATE INDEX idx_sessions_workspace ON sessions(workspace_id);
3949
+ CREATE UNIQUE INDEX idx_sessions_terminal ON sessions(terminal_id);
3950
+ CREATE UNIQUE INDEX idx_sessions_id_workspace ON sessions(id, workspace_id);
3951
+
3952
+ CREATE TABLE provider_configs (
3953
+ provider_id TEXT PRIMARY KEY,
3954
+ config TEXT NOT NULL
3955
+ );
3956
+
3957
+ CREATE TABLE user_settings (
3958
+ key TEXT PRIMARY KEY,
3959
+ value TEXT NOT NULL
3960
+ );
3961
+
3962
+ CREATE TABLE auth_sessions (
3963
+ token TEXT PRIMARY KEY,
3964
+ created_at INTEGER NOT NULL,
3965
+ last_seen_at INTEGER NOT NULL
3966
+ );
3967
+
3968
+ CREATE INDEX idx_auth_sessions_last_seen_at ON auth_sessions(last_seen_at);
3969
+
3970
+ CREATE TABLE supervisors (
3971
+ id TEXT PRIMARY KEY,
3972
+ session_id TEXT NOT NULL UNIQUE,
3973
+ workspace_id TEXT NOT NULL,
3974
+ state TEXT NOT NULL,
3975
+ objective TEXT NOT NULL,
3976
+ evaluator_provider_id TEXT NOT NULL,
3977
+ last_cycle_at INTEGER,
3978
+ last_evaluated_turn_id TEXT,
3979
+ error_reason TEXT,
3980
+ created_at INTEGER NOT NULL,
3981
+ updated_at INTEGER NOT NULL,
3982
+ FOREIGN KEY (session_id, workspace_id) REFERENCES sessions(id, workspace_id) ON DELETE CASCADE,
3983
+ FOREIGN KEY (workspace_id) REFERENCES workspaces(id) ON DELETE CASCADE
3984
+ );
3985
+
3986
+ CREATE INDEX idx_supervisors_workspace ON supervisors(workspace_id);
3987
+ CREATE INDEX idx_supervisors_session ON supervisors(session_id);
3988
+ CREATE UNIQUE INDEX idx_supervisors_id_session ON supervisors(id, session_id);
3989
+
3990
+ CREATE TABLE supervisor_cycles (
3991
+ id TEXT PRIMARY KEY,
3992
+ supervisor_id TEXT NOT NULL,
3993
+ session_id TEXT NOT NULL,
3994
+ status TEXT NOT NULL,
3995
+ trigger TEXT NOT NULL,
3996
+ evidence_source TEXT NOT NULL,
3997
+ objective TEXT NOT NULL,
3998
+ evaluator_provider_id TEXT NOT NULL,
3999
+ turn_id TEXT,
4000
+ progress INTEGER,
4001
+ result TEXT,
4002
+ injected_guidance TEXT,
4003
+ error_reason TEXT,
4004
+ created_at INTEGER NOT NULL,
4005
+ completed_at INTEGER,
4006
+ FOREIGN KEY (supervisor_id, session_id) REFERENCES supervisors(id, session_id) ON DELETE CASCADE,
4007
+ FOREIGN KEY (session_id) REFERENCES sessions(id) ON DELETE CASCADE
4008
+ );
4009
+
4010
+ CREATE INDEX idx_supervisor_cycles_supervisor ON supervisor_cycles(supervisor_id, created_at DESC);
4011
+ CREATE INDEX idx_supervisor_cycles_session ON supervisor_cycles(session_id, created_at DESC);
4012
+
4013
+ CREATE TABLE auth_login_blocks (
4014
+ ip TEXT PRIMARY KEY,
4015
+ failed_count INTEGER NOT NULL,
4016
+ first_failed_at INTEGER NOT NULL,
4017
+ last_failed_at INTEGER NOT NULL,
4018
+ blocked_until INTEGER
4019
+ );
4020
+
4021
+ CREATE INDEX idx_auth_login_blocks_blocked_until ON auth_login_blocks(blocked_until);
4022
+
4023
+ CREATE TABLE auth_login_failures (
4024
+ ip TEXT NOT NULL,
4025
+ failed_at INTEGER NOT NULL
4026
+ );
4027
+
4028
+ CREATE INDEX idx_auth_login_failures_ip_failed_at ON auth_login_failures(ip, failed_at);
4029
+ `;
4030
+ CURRENT_SCHEMA_ENTRIES = buildSchemaEntries(CURRENT_SCHEMA_SQL);
4031
+ V1_SCHEMA_ENTRIES = buildSchemaEntries(V1_SCHEMA_SQL);
4032
+ IncompatibleSchemaError = class extends Error {
4033
+ code = "db_incompatible_schema";
4034
+ constructor(dbPath, mismatch) {
4035
+ super(
4036
+ `db_incompatible_schema: Database schema mismatch detected at ${dbPath}: ${mismatch}. This build requires the current baseline schema. Delete the local database file and restart.`
4037
+ );
4038
+ this.name = "IncompatibleSchemaError";
4039
+ }
4040
+ };
4041
+ }
4042
+ });
4043
+
4044
+ // packages/server/src/storage/db.ts
4045
+ import { DatabaseSync as DatabaseSync2 } from "node:sqlite";
3796
4046
  function hasTable(db, tableName) {
3797
4047
  const row = db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name = ?").get(tableName);
3798
4048
  return row?.name === tableName;
@@ -3819,68 +4069,75 @@ function detectLegacySchema(db) {
3819
4069
  }
3820
4070
  return reasons;
3821
4071
  }
3822
- function schemaEntrySignature(entry) {
3823
- return `${entry.type}:${entry.name}:${entry.tableName}:${entry.sql}`;
3824
- }
3825
- function isSchemaEmpty(db) {
3826
- return listSchemaEntries(db).length === 0;
3827
- }
3828
- function assertNoLegacySchema(db, dbPath) {
4072
+ function throwIfLegacySchema(db, dbPath) {
3829
4073
  const reasons = detectLegacySchema(db);
3830
4074
  if (reasons.length === 0) {
3831
4075
  return;
3832
4076
  }
3833
- throw new Error(
3834
- `Legacy database schema detected at ${dbPath}: ${reasons.join(", ")}. This build no longer supports automatic database upgrades. Delete the local database file and restart.`
3835
- );
3836
- }
3837
- function describeSchemaMismatch(expected, actual) {
3838
- const expectedByName = new Map(expected.map((entry) => [`${entry.type}:${entry.name}`, entry]));
3839
- const actualByName = new Map(actual.map((entry) => [`${entry.type}:${entry.name}`, entry]));
3840
- const keys = /* @__PURE__ */ new Set([...expectedByName.keys(), ...actualByName.keys()]);
3841
- for (const key of keys) {
3842
- const expectedEntry = expectedByName.get(key);
3843
- const actualEntry = actualByName.get(key);
3844
- if (!expectedEntry) {
3845
- return `unexpected ${actualEntry?.type ?? "schema object"} ${actualEntry?.name ?? key}`;
3846
- }
3847
- if (!actualEntry) {
3848
- return `missing ${expectedEntry.type} ${expectedEntry.name}`;
3849
- }
3850
- if (schemaEntrySignature(expectedEntry) !== schemaEntrySignature(actualEntry)) {
3851
- return `definition mismatch for ${expectedEntry.type} ${expectedEntry.name}`;
3852
- }
3853
- }
3854
- return "unknown schema drift";
3855
- }
3856
- function assertSchemaMatchesBaseline(db, dbPath) {
3857
- const actualEntries = listSchemaEntries(db);
3858
- const expectedSignatures = EXPECTED_SCHEMA_ENTRIES.map(schemaEntrySignature);
3859
- const actualSignatures = actualEntries.map(schemaEntrySignature);
3860
- if (actualSignatures.length === expectedSignatures.length && actualSignatures.every((signature, index) => signature === expectedSignatures[index])) {
3861
- return;
3862
- }
3863
- const mismatch = describeSchemaMismatch(EXPECTED_SCHEMA_ENTRIES, actualEntries);
3864
- throw new Error(
3865
- `Database schema mismatch detected at ${dbPath}: ${mismatch}. This build requires the current baseline schema. Delete the local database file and restart.`
3866
- );
4077
+ throw new IncompatibleSchemaError(dbPath, `legacy schema detected (${reasons.join(", ")})`);
3867
4078
  }
3868
4079
  function initializeSchema(db) {
3869
4080
  withTransaction(db, () => {
3870
- db.exec(SCHEMA_SQL);
4081
+ db.exec(CURRENT_SCHEMA_SQL);
3871
4082
  });
3872
4083
  }
3873
- function initializeOrValidateSchema(db, dbPath) {
3874
- assertNoLegacySchema(db, dbPath);
3875
- if (isSchemaEmpty(db)) {
3876
- initializeSchema(db);
3877
- assertSchemaMatchesBaseline(db, dbPath);
3878
- return;
4084
+ function upgradeSchemaV1ToV2(db) {
4085
+ withTransaction(db, () => {
4086
+ db.exec("ALTER TABLE supervisors ADD COLUMN evaluator_model TEXT");
4087
+ db.exec("ALTER TABLE supervisors ADD COLUMN max_supervision_count INTEGER NOT NULL DEFAULT 0");
4088
+ db.exec(
4089
+ "ALTER TABLE supervisors ADD COLUMN completed_supervision_count INTEGER NOT NULL DEFAULT 0"
4090
+ );
4091
+ db.exec("ALTER TABLE supervisors ADD COLUMN scheduled_at INTEGER");
4092
+ db.exec("ALTER TABLE supervisors ADD COLUMN stop_reason TEXT");
4093
+ db.exec(`
4094
+ CREATE TABLE supervisor_cycle_attempts (
4095
+ id TEXT PRIMARY KEY,
4096
+ cycle_id TEXT NOT NULL REFERENCES supervisor_cycles(id) ON DELETE CASCADE,
4097
+ attempt_index INTEGER NOT NULL,
4098
+ status TEXT NOT NULL,
4099
+ started_at INTEGER NOT NULL,
4100
+ completed_at INTEGER,
4101
+ error_reason TEXT,
4102
+ provider_model TEXT
4103
+ )
4104
+ `);
4105
+ db.exec(
4106
+ "CREATE INDEX idx_supervisor_cycle_attempts_cycle ON supervisor_cycle_attempts(cycle_id, attempt_index)"
4107
+ );
4108
+ stampCurrentSchemaVersion(db);
4109
+ });
4110
+ }
4111
+ function assertCurrentSchema(db, dbPath) {
4112
+ const detection = detectSchema(db);
4113
+ if (detection.state !== "current") {
4114
+ throw new IncompatibleSchemaError(dbPath, detection.mismatch ?? "unknown schema drift");
4115
+ }
4116
+ }
4117
+ function initializeOrUpgradeSchema(db, dbPath) {
4118
+ throwIfLegacySchema(db, dbPath);
4119
+ const detection = detectSchema(db);
4120
+ switch (detection.state) {
4121
+ case "empty":
4122
+ initializeSchema(db);
4123
+ assertCurrentSchema(db, dbPath);
4124
+ return;
4125
+ case "current":
4126
+ if (detection.userVersion !== CURRENT_SCHEMA_VERSION) {
4127
+ stampCurrentSchemaVersion(db);
4128
+ }
4129
+ assertCurrentSchema(db, dbPath);
4130
+ return;
4131
+ case "v1":
4132
+ upgradeSchemaV1ToV2(db);
4133
+ assertCurrentSchema(db, dbPath);
4134
+ return;
4135
+ case "incompatible":
4136
+ throw new IncompatibleSchemaError(dbPath, detection.mismatch ?? "unknown schema drift");
3879
4137
  }
3880
- assertSchemaMatchesBaseline(db, dbPath);
3881
4138
  }
3882
4139
  function openDatabase(dbPath) {
3883
- const db = new DatabaseSync(dbPath);
4140
+ const db = new DatabaseSync2(dbPath);
3884
4141
  try {
3885
4142
  db.exec("PRAGMA journal_mode = WAL");
3886
4143
  db.exec("PRAGMA foreign_keys = ON");
@@ -3888,7 +4145,7 @@ function openDatabase(dbPath) {
3888
4145
  if (integrityResult[0]?.integrity_check !== "ok") {
3889
4146
  throw new Error(`Database integrity check failed: ${JSON.stringify(integrityResult)}`);
3890
4147
  }
3891
- initializeOrValidateSchema(db, dbPath);
4148
+ initializeOrUpgradeSchema(db, dbPath);
3892
4149
  return db;
3893
4150
  } catch (error) {
3894
4151
  try {
@@ -3905,16 +4162,14 @@ function closeDatabase(db) {
3905
4162
  db.close();
3906
4163
  }
3907
4164
  }
3908
- var SCHEMA_PATH, SCHEMA_SQL, LEGACY_TABLES, LEGACY_SESSION_COLUMNS, EXPECTED_SCHEMA_ENTRIES;
4165
+ var LEGACY_TABLES, LEGACY_SESSION_COLUMNS;
3909
4166
  var init_db = __esm({
3910
4167
  "packages/server/src/storage/db.ts"() {
3911
4168
  "use strict";
3912
4169
  init_database();
3913
- SCHEMA_PATH = join3(import.meta.dirname, "migrations", "001_init.sql");
3914
- SCHEMA_SQL = readFileSync4(SCHEMA_PATH, "utf-8");
4170
+ init_schema_version();
3915
4171
  LEGACY_TABLES = ["hook_registrations", "_migrations"];
3916
4172
  LEGACY_SESSION_COLUMNS = ["resume_id", "transcript_path"];
3917
- EXPECTED_SCHEMA_ENTRIES = buildExpectedSchemaEntries();
3918
4173
  }
3919
4174
  });
3920
4175
 
@@ -4164,6 +4419,93 @@ var init_settings_repo = __esm({
4164
4419
  }
4165
4420
  });
4166
4421
 
4422
+ // packages/server/src/storage/repositories/supervisor-cycle-attempt-repo.ts
4423
+ var SupervisorCycleAttemptRepo;
4424
+ var init_supervisor_cycle_attempt_repo = __esm({
4425
+ "packages/server/src/storage/repositories/supervisor-cycle-attempt-repo.ts"() {
4426
+ "use strict";
4427
+ SupervisorCycleAttemptRepo = class {
4428
+ constructor(db) {
4429
+ this.db = db;
4430
+ }
4431
+ db;
4432
+ create(input2) {
4433
+ this.db.prepare(
4434
+ `INSERT INTO supervisor_cycle_attempts (id, cycle_id, attempt_index, status, started_at, completed_at, error_reason, provider_model)
4435
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)`
4436
+ ).run(
4437
+ input2.id,
4438
+ input2.cycleId,
4439
+ input2.attemptIndex,
4440
+ input2.status,
4441
+ input2.startedAt,
4442
+ input2.completedAt ?? null,
4443
+ input2.errorReason ?? null,
4444
+ input2.providerModel ?? null
4445
+ );
4446
+ return this.findById(input2.id);
4447
+ }
4448
+ findById(id) {
4449
+ const row = this.db.prepare("SELECT * FROM supervisor_cycle_attempts WHERE id = ?").get(id);
4450
+ return row ? this.rowToAttempt(row) : void 0;
4451
+ }
4452
+ listForCycle(cycleId) {
4453
+ const rows = this.db.prepare(
4454
+ "SELECT * FROM supervisor_cycle_attempts WHERE cycle_id = ? ORDER BY attempt_index ASC"
4455
+ ).all(cycleId);
4456
+ return rows.map((row) => this.rowToAttempt(row));
4457
+ }
4458
+ update(id, patch) {
4459
+ const assignments = [];
4460
+ const params = { id };
4461
+ if (patch.status !== void 0) {
4462
+ assignments.push("status = @status");
4463
+ params.status = patch.status;
4464
+ }
4465
+ if (patch.completedAt !== void 0) {
4466
+ assignments.push("completed_at = @completedAt");
4467
+ params.completedAt = patch.completedAt;
4468
+ }
4469
+ if (patch.errorReason !== void 0) {
4470
+ assignments.push("error_reason = @errorReason");
4471
+ params.errorReason = patch.errorReason;
4472
+ }
4473
+ if (patch.providerModel !== void 0) {
4474
+ assignments.push("provider_model = @providerModel");
4475
+ params.providerModel = patch.providerModel;
4476
+ }
4477
+ if (assignments.length === 0) {
4478
+ const existing = this.findById(id);
4479
+ if (!existing) {
4480
+ throw new Error(`Supervisor cycle attempt not found: ${id}`);
4481
+ }
4482
+ return existing;
4483
+ }
4484
+ const result = this.db.prepare(`UPDATE supervisor_cycle_attempts SET ${assignments.join(", ")} WHERE id = @id`).run(params);
4485
+ if (result.changes === 0) {
4486
+ throw new Error(`Supervisor cycle attempt not found: ${id}`);
4487
+ }
4488
+ return this.findById(id);
4489
+ }
4490
+ deleteForCycle(cycleId) {
4491
+ this.db.prepare("DELETE FROM supervisor_cycle_attempts WHERE cycle_id = ?").run(cycleId);
4492
+ }
4493
+ rowToAttempt(row) {
4494
+ return {
4495
+ id: row.id,
4496
+ cycleId: row.cycle_id,
4497
+ attemptIndex: row.attempt_index,
4498
+ status: row.status,
4499
+ startedAt: row.started_at,
4500
+ completedAt: row.completed_at ?? void 0,
4501
+ errorReason: row.error_reason ?? void 0,
4502
+ providerModel: row.provider_model ?? void 0
4503
+ };
4504
+ }
4505
+ };
4506
+ }
4507
+ });
4508
+
4167
4509
  // packages/server/src/storage/repositories/supervisor-cycle-repo.ts
4168
4510
  var SupervisorCycleRepo;
4169
4511
  var init_supervisor_cycle_repo = __esm({
@@ -4293,8 +4635,8 @@ var init_supervisor_repo = __esm({
4293
4635
  db;
4294
4636
  create(input2) {
4295
4637
  this.db.prepare(
4296
- `INSERT INTO supervisors (id, session_id, workspace_id, state, objective, evaluator_provider_id, last_cycle_at, last_evaluated_turn_id, error_reason, created_at, updated_at)
4297
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
4638
+ `INSERT INTO supervisors (id, session_id, workspace_id, state, objective, evaluator_provider_id, evaluator_model, max_supervision_count, completed_supervision_count, scheduled_at, stop_reason, last_cycle_at, last_evaluated_turn_id, error_reason, created_at, updated_at)
4639
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
4298
4640
  ).run(
4299
4641
  input2.id,
4300
4642
  input2.sessionId,
@@ -4302,6 +4644,11 @@ var init_supervisor_repo = __esm({
4302
4644
  input2.state,
4303
4645
  input2.objective,
4304
4646
  input2.evaluatorProviderId,
4647
+ input2.evaluatorModel ?? null,
4648
+ input2.maxSupervisionCount ?? 0,
4649
+ input2.completedSupervisionCount ?? 0,
4650
+ input2.scheduledAt ?? null,
4651
+ input2.stopReason ?? null,
4305
4652
  input2.lastCycleAt ?? null,
4306
4653
  input2.lastEvaluatedTurnId ?? null,
4307
4654
  input2.errorReason ?? null,
@@ -4340,6 +4687,26 @@ var init_supervisor_repo = __esm({
4340
4687
  assignments.push("evaluator_provider_id = @evaluatorProviderId");
4341
4688
  params.evaluatorProviderId = patch.evaluatorProviderId;
4342
4689
  }
4690
+ if (patch.evaluatorModel !== void 0) {
4691
+ assignments.push("evaluator_model = @evaluatorModel");
4692
+ params.evaluatorModel = patch.evaluatorModel;
4693
+ }
4694
+ if (patch.maxSupervisionCount !== void 0) {
4695
+ assignments.push("max_supervision_count = @maxSupervisionCount");
4696
+ params.maxSupervisionCount = patch.maxSupervisionCount;
4697
+ }
4698
+ if (patch.completedSupervisionCount !== void 0) {
4699
+ assignments.push("completed_supervision_count = @completedSupervisionCount");
4700
+ params.completedSupervisionCount = patch.completedSupervisionCount;
4701
+ }
4702
+ if (patch.scheduledAt !== void 0) {
4703
+ assignments.push("scheduled_at = @scheduledAt");
4704
+ params.scheduledAt = patch.scheduledAt;
4705
+ }
4706
+ if (patch.stopReason !== void 0) {
4707
+ assignments.push("stop_reason = @stopReason");
4708
+ params.stopReason = patch.stopReason;
4709
+ }
4343
4710
  if (patch.lastCycleAt !== void 0) {
4344
4711
  assignments.push("last_cycle_at = @lastCycleAt");
4345
4712
  params.lastCycleAt = patch.lastCycleAt;
@@ -4369,6 +4736,11 @@ var init_supervisor_repo = __esm({
4369
4736
  state: row.state,
4370
4737
  objective: row.objective,
4371
4738
  evaluatorProviderId: row.evaluator_provider_id,
4739
+ evaluatorModel: row.evaluator_model ?? void 0,
4740
+ maxSupervisionCount: row.max_supervision_count,
4741
+ completedSupervisionCount: row.completed_supervision_count,
4742
+ scheduledAt: row.scheduled_at ?? void 0,
4743
+ stopReason: row.stop_reason ?? void 0,
4372
4744
  cycles: [],
4373
4745
  lastCycleAt: row.last_cycle_at ?? void 0,
4374
4746
  lastEvaluatedTurnId: row.last_evaluated_turn_id ?? void 0,
@@ -5556,12 +5928,63 @@ function getSupervisorEvaluationTimeoutMs(settingsRepo) {
5556
5928
  const timeoutSec = resolveSupervisorEvaluationTimeoutSec(storedValue);
5557
5929
  return timeoutSec * 1e3;
5558
5930
  }
5559
- var SUPERVISOR_EVALUATION_TIMEOUT_SETTING_KEY;
5931
+ function getSettingOrDefault(settingsRepo, key, fallback) {
5932
+ try {
5933
+ return settingsRepo?.get(key) ?? fallback;
5934
+ } catch {
5935
+ return fallback;
5936
+ }
5937
+ }
5938
+ function getSupervisorRetrySettings(settingsRepo) {
5939
+ return {
5940
+ retryEnabled: resolveSupervisorRetryEnabled(
5941
+ getSettingOrDefault(
5942
+ settingsRepo,
5943
+ SUPERVISOR_RETRY_ENABLED_SETTING_KEY,
5944
+ DEFAULT_SUPERVISOR_RETRY_ENABLED
5945
+ )
5946
+ ),
5947
+ retryMaxCount: resolveSupervisorRetryMaxCount(
5948
+ getSettingOrDefault(
5949
+ settingsRepo,
5950
+ SUPERVISOR_RETRY_MAX_COUNT_SETTING_KEY,
5951
+ DEFAULT_SUPERVISOR_RETRY_MAX_COUNT
5952
+ )
5953
+ ),
5954
+ retryDelaySec: resolveSupervisorRetryDelaySec(
5955
+ getSettingOrDefault(
5956
+ settingsRepo,
5957
+ SUPERVISOR_RETRY_DELAY_SEC_SETTING_KEY,
5958
+ DEFAULT_SUPERVISOR_RETRY_DELAY_SEC
5959
+ )
5960
+ ),
5961
+ retryOnTimeout: resolveSupervisorRetryOnTimeout(
5962
+ getSettingOrDefault(
5963
+ settingsRepo,
5964
+ SUPERVISOR_RETRY_ON_TIMEOUT_SETTING_KEY,
5965
+ DEFAULT_SUPERVISOR_RETRY_ON_TIMEOUT
5966
+ )
5967
+ ),
5968
+ retryOnEvaluatorError: resolveSupervisorRetryOnEvaluatorError(
5969
+ getSettingOrDefault(
5970
+ settingsRepo,
5971
+ SUPERVISOR_RETRY_ON_EVALUATOR_ERROR_SETTING_KEY,
5972
+ DEFAULT_SUPERVISOR_RETRY_ON_EVALUATOR_ERROR
5973
+ )
5974
+ )
5975
+ };
5976
+ }
5977
+ var SUPERVISOR_EVALUATION_TIMEOUT_SETTING_KEY, SUPERVISOR_RETRY_ENABLED_SETTING_KEY, SUPERVISOR_RETRY_MAX_COUNT_SETTING_KEY, SUPERVISOR_RETRY_DELAY_SEC_SETTING_KEY, SUPERVISOR_RETRY_ON_TIMEOUT_SETTING_KEY, SUPERVISOR_RETRY_ON_EVALUATOR_ERROR_SETTING_KEY;
5560
5978
  var init_settings = __esm({
5561
5979
  "packages/server/src/supervisor/settings.ts"() {
5562
5980
  "use strict";
5563
5981
  init_src3();
5564
5982
  SUPERVISOR_EVALUATION_TIMEOUT_SETTING_KEY = "supervisor.evaluationTimeoutSec";
5983
+ SUPERVISOR_RETRY_ENABLED_SETTING_KEY = "supervisor.retryEnabled";
5984
+ SUPERVISOR_RETRY_MAX_COUNT_SETTING_KEY = "supervisor.retryMaxCount";
5985
+ SUPERVISOR_RETRY_DELAY_SEC_SETTING_KEY = "supervisor.retryDelaySec";
5986
+ SUPERVISOR_RETRY_ON_TIMEOUT_SETTING_KEY = "supervisor.retryOnTimeout";
5987
+ SUPERVISOR_RETRY_ON_EVALUATOR_ERROR_SETTING_KEY = "supervisor.retryOnEvaluatorError";
5565
5988
  }
5566
5989
  });
5567
5990
 
@@ -5783,6 +6206,12 @@ function extractSupervisorMessage(output2, providerId) {
5783
6206
  }
5784
6207
  const lines = trimmed.split(/\r?\n/).filter(Boolean);
5785
6208
  if (providerId === "codex") {
6209
+ if (trimmed === "[objective complete]") {
6210
+ return trimmed;
6211
+ }
6212
+ if (!trimmed.startsWith("{") && !trimmed.startsWith("[")) {
6213
+ return stripCodeFence(trimmed);
6214
+ }
5786
6215
  const scan = scanCodexStream(lines);
5787
6216
  if (scan.turnFailure) {
5788
6217
  throw new Error(`Supervisor (codex) failed: ${scan.turnFailure}`);
@@ -5884,7 +6313,7 @@ var init_evaluator = __esm({
5884
6313
  prompt,
5885
6314
  sessionId: supervisor.sessionId,
5886
6315
  workspacePath: context.workspacePath,
5887
- model: typeof config.model === "string" ? config.model : void 0
6316
+ model: typeof supervisor.evaluatorModel === "string" && supervisor.evaluatorModel.trim() ? supervisor.evaluatorModel.trim() : typeof config.model === "string" ? config.model : void 0
5888
6317
  });
5889
6318
  if (!command) {
5890
6319
  throw {
@@ -5913,7 +6342,11 @@ var init_evaluator = __esm({
5913
6342
  );
5914
6343
  throw error;
5915
6344
  }
5916
- return { message: message.slice(0, this.config.guidanceMaxChars) };
6345
+ const normalizedMessage = message.slice(0, this.config.guidanceMaxChars);
6346
+ return {
6347
+ message: normalizedMessage,
6348
+ objectiveComplete: normalizedMessage.trim() === "[objective complete]"
6349
+ };
5917
6350
  }
5918
6351
  };
5919
6352
  }
@@ -5949,7 +6382,13 @@ var init_injector = __esm({
5949
6382
  }
5950
6383
  deps;
5951
6384
  config;
5952
- async inject(supervisor, input2, recentCycles) {
6385
+ async inject(supervisor, input2, recentCycles, options = {}) {
6386
+ if (options.signal?.aborted) {
6387
+ throw {
6388
+ code: "supervisor_eval_aborted",
6389
+ message: "Supervisor evaluator aborted"
6390
+ };
6391
+ }
5953
6392
  const session = this.deps.sessionMgr.get(supervisor.sessionId);
5954
6393
  if (!session) {
5955
6394
  throw {
@@ -5970,6 +6409,12 @@ var init_injector = __esm({
5970
6409
  if (duplicate) {
5971
6410
  return { injected: false, text };
5972
6411
  }
6412
+ if (options.signal?.aborted) {
6413
+ throw {
6414
+ code: "supervisor_eval_aborted",
6415
+ message: "Supervisor evaluator aborted"
6416
+ };
6417
+ }
5973
6418
  const BRACKETED_PASTE_START = "\x1B[200~";
5974
6419
  const BRACKETED_PASTE_END = "\x1B[201~";
5975
6420
  const SUBMIT = "\r";
@@ -5992,6 +6437,9 @@ var init_scheduler = __esm({
5992
6437
  }
5993
6438
  deps;
5994
6439
  unsubscribe = null;
6440
+ scheduledTimer = null;
6441
+ scheduledRetryDelayMs = 1e3;
6442
+ retryAtBySupervisorId = /* @__PURE__ */ new Map();
5995
6443
  start() {
5996
6444
  this.unsubscribe?.();
5997
6445
  this.unsubscribe = this.deps.eventBus.on(
@@ -6004,9 +6452,64 @@ var init_scheduler = __esm({
6004
6452
  }
6005
6453
  );
6006
6454
  }
6455
+ refresh() {
6456
+ this.clearScheduledTimer();
6457
+ const scheduled = this.deps.listScheduledSupervisors?.() ?? [];
6458
+ this.pruneRetryState(scheduled);
6459
+ if (scheduled.length === 0) {
6460
+ return;
6461
+ }
6462
+ const now = Date.now();
6463
+ const nextAt = scheduled.reduce((earliest, item) => {
6464
+ const candidate = this.getNextAttemptAt(item, now);
6465
+ return candidate < earliest ? candidate : earliest;
6466
+ }, Number.POSITIVE_INFINITY);
6467
+ if (!Number.isFinite(nextAt)) {
6468
+ return;
6469
+ }
6470
+ const delayMs = Math.max(nextAt - now, 0);
6471
+ this.scheduledTimer = setTimeout(() => {
6472
+ this.scheduledTimer = null;
6473
+ const current = this.deps.listScheduledSupervisors?.() ?? [];
6474
+ this.pruneRetryState(current);
6475
+ const dueAt = Date.now();
6476
+ const due = current.filter(
6477
+ (item) => item.scheduledAt <= dueAt && (this.retryAtBySupervisorId.get(item.supervisorId) ?? Number.NEGATIVE_INFINITY) <= dueAt
6478
+ );
6479
+ for (const item of due) {
6480
+ this.retryAtBySupervisorId.set(item.supervisorId, dueAt + this.scheduledRetryDelayMs);
6481
+ this.deps.onScheduledDue?.(item.supervisorId);
6482
+ }
6483
+ this.refresh();
6484
+ }, delayMs);
6485
+ this.scheduledTimer.unref?.();
6486
+ }
6007
6487
  stop() {
6008
6488
  this.unsubscribe?.();
6009
6489
  this.unsubscribe = null;
6490
+ this.clearScheduledTimer();
6491
+ this.retryAtBySupervisorId.clear();
6492
+ }
6493
+ clearScheduledTimer() {
6494
+ if (this.scheduledTimer) {
6495
+ clearTimeout(this.scheduledTimer);
6496
+ this.scheduledTimer = null;
6497
+ }
6498
+ }
6499
+ getNextAttemptAt(item, now) {
6500
+ if (item.scheduledAt > now) {
6501
+ return item.scheduledAt;
6502
+ }
6503
+ const retryAt = this.retryAtBySupervisorId.get(item.supervisorId);
6504
+ return retryAt && retryAt > now ? retryAt : item.scheduledAt;
6505
+ }
6506
+ pruneRetryState(scheduled) {
6507
+ const scheduledIds = new Set(scheduled.map((item) => item.supervisorId));
6508
+ for (const supervisorId of this.retryAtBySupervisorId.keys()) {
6509
+ if (!scheduledIds.has(supervisorId)) {
6510
+ this.retryAtBySupervisorId.delete(supervisorId);
6511
+ }
6512
+ }
6010
6513
  }
6011
6514
  };
6012
6515
  }
@@ -6027,6 +6530,9 @@ function generateSupervisorId() {
6027
6530
  function generateCycleId() {
6028
6531
  return `cycle_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;
6029
6532
  }
6533
+ function generateAttemptId() {
6534
+ return `attempt_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;
6535
+ }
6030
6536
  function messageOf(error, fallback) {
6031
6537
  if (error instanceof Error) {
6032
6538
  return error.message;
@@ -6054,6 +6560,7 @@ var init_manager2 = __esm({
6054
6560
  init_evaluator();
6055
6561
  init_injector();
6056
6562
  init_scheduler();
6563
+ init_settings();
6057
6564
  NOOP_LOGGER3 = {
6058
6565
  child: () => NOOP_LOGGER3,
6059
6566
  debug: () => {
@@ -6082,7 +6589,8 @@ var init_manager2 = __esm({
6082
6589
  sessionMgr: deps.sessionMgr,
6083
6590
  terminalMgr: deps.terminalMgr,
6084
6591
  providerRegistry: deps.providerRegistry,
6085
- logger: this.logger
6592
+ logger: this.logger,
6593
+ git: deps.git
6086
6594
  });
6087
6595
  this.evaluator = new SupervisorEvaluator({
6088
6596
  providerRegistry: deps.providerRegistry,
@@ -6101,10 +6609,19 @@ var init_manager2 = __esm({
6101
6609
  onTurnCompleted: (sessionId) => {
6102
6610
  const supervisorId = this.supervisorsBySession.get(sessionId);
6103
6611
  if (supervisorId) {
6104
- void this.runEvaluation(supervisorId).catch((error) => {
6612
+ void this.runEvaluation(supervisorId, "turn_completed").catch((error) => {
6105
6613
  this.logger.warn({ err: error, supervisorId }, "Supervisor auto-evaluation failed");
6106
6614
  });
6107
6615
  }
6616
+ },
6617
+ listScheduledSupervisors: () => this.listScheduledSupervisors(),
6618
+ onScheduledDue: (supervisorId) => {
6619
+ void this.runEvaluation(supervisorId, "scheduled").catch((error) => {
6620
+ this.logger.warn(
6621
+ { err: error, supervisorId },
6622
+ "Supervisor scheduled auto-evaluation failed"
6623
+ );
6624
+ });
6108
6625
  }
6109
6626
  });
6110
6627
  }
@@ -6113,6 +6630,7 @@ var init_manager2 = __esm({
6113
6630
  supervisorsBySession = /* @__PURE__ */ new Map();
6114
6631
  inFlight = /* @__PURE__ */ new Set();
6115
6632
  pendingDeletes = /* @__PURE__ */ new Set();
6633
+ pendingPauses = /* @__PURE__ */ new Set();
6116
6634
  evaluationAbortControllers = /* @__PURE__ */ new Map();
6117
6635
  inFlightCompletions = /* @__PURE__ */ new Map();
6118
6636
  scheduler;
@@ -6165,6 +6683,7 @@ var init_manager2 = __esm({
6165
6683
  }
6166
6684
  );
6167
6685
  this.scheduler.start();
6686
+ this.scheduler.refresh();
6168
6687
  }
6169
6688
  stop() {
6170
6689
  this.scheduler.stop();
@@ -6236,12 +6755,17 @@ var init_manager2 = __esm({
6236
6755
  state: "idle",
6237
6756
  objective: req.objective.trim(),
6238
6757
  evaluatorProviderId: req.evaluatorProviderId,
6758
+ evaluatorModel: req.evaluatorModel?.trim() || void 0,
6759
+ maxSupervisionCount: req.maxSupervisionCount ?? 0,
6760
+ completedSupervisionCount: 0,
6761
+ scheduledAt: req.scheduledAt,
6239
6762
  createdAt: now,
6240
6763
  updatedAt: now
6241
6764
  })
6242
6765
  );
6243
6766
  this.storeSnapshot(supervisor);
6244
6767
  this.broadcastState(supervisor, "created");
6768
+ this.scheduler.refresh();
6245
6769
  return supervisor;
6246
6770
  }
6247
6771
  async update(id, patch) {
@@ -6253,6 +6777,9 @@ var init_manager2 = __esm({
6253
6777
  this.deps.supervisorRepo.update(id, {
6254
6778
  objective: patch.objective !== void 0 ? patch.objective.trim() : current.objective,
6255
6779
  evaluatorProviderId: patch.evaluatorProviderId ?? current.evaluatorProviderId,
6780
+ evaluatorModel: patch.evaluatorModel === void 0 ? current.evaluatorModel : patch.evaluatorModel?.trim() || null,
6781
+ maxSupervisionCount: patch.maxSupervisionCount ?? current.maxSupervisionCount,
6782
+ scheduledAt: patch.scheduledAt === void 0 ? current.scheduledAt : patch.scheduledAt,
6256
6783
  state: current.state === "error" ? "idle" : current.state,
6257
6784
  errorReason: null,
6258
6785
  updatedAt: Date.now()
@@ -6260,9 +6787,14 @@ var init_manager2 = __esm({
6260
6787
  );
6261
6788
  this.storeSnapshot(updated);
6262
6789
  this.broadcastState(updated, "updated");
6790
+ this.scheduler.refresh();
6263
6791
  return updated;
6264
6792
  }
6265
6793
  async pause(id) {
6794
+ if (this.inFlight.has(id)) {
6795
+ this.pendingPauses.add(id);
6796
+ this.evaluationAbortControllers.get(id)?.abort();
6797
+ }
6266
6798
  const updated = this.attachCycles(
6267
6799
  this.deps.supervisorRepo.update(id, {
6268
6800
  state: "paused",
@@ -6271,6 +6803,7 @@ var init_manager2 = __esm({
6271
6803
  );
6272
6804
  this.storeSnapshot(updated);
6273
6805
  this.broadcastState(updated, "state_changed");
6806
+ this.scheduler.refresh();
6274
6807
  return updated;
6275
6808
  }
6276
6809
  async resume(id) {
@@ -6283,6 +6816,7 @@ var init_manager2 = __esm({
6283
6816
  );
6284
6817
  this.storeSnapshot(updated);
6285
6818
  this.broadcastState(updated, "state_changed");
6819
+ this.scheduler.refresh();
6286
6820
  return updated;
6287
6821
  }
6288
6822
  async delete(id) {
@@ -6291,6 +6825,7 @@ var init_manager2 = __esm({
6291
6825
  this.pendingDeletes.add(id);
6292
6826
  this.evaluationAbortControllers.get(id)?.abort();
6293
6827
  await this.inFlightCompletions.get(id)?.promise;
6828
+ this.scheduler.refresh();
6294
6829
  return;
6295
6830
  }
6296
6831
  this.deleteNow(supervisor);
@@ -6324,8 +6859,8 @@ var init_manager2 = __esm({
6324
6859
  * auto trigger path (scheduler) and for tests that want to observe the
6325
6860
  * final cycle outcome.
6326
6861
  */
6327
- async runEvaluation(supervisorId) {
6328
- const started = await this.beginCycle(supervisorId, "turn_completed");
6862
+ async runEvaluation(supervisorId, trigger = "turn_completed") {
6863
+ const started = await this.beginCycle(supervisorId, trigger);
6329
6864
  if (!started) {
6330
6865
  return null;
6331
6866
  }
@@ -6369,9 +6904,41 @@ var init_manager2 = __esm({
6369
6904
  }
6370
6905
  return null;
6371
6906
  }
6372
- if (trigger === "turn_completed" && (supervisor.state !== "idle" || session.state !== "running" && session.state !== "idle")) {
6907
+ if (supervisor.state === "stopped") {
6908
+ if (trigger === "manual") {
6909
+ throw {
6910
+ code: "supervisor_stopped",
6911
+ message: `Supervisor ${id} is stopped`
6912
+ };
6913
+ }
6914
+ return null;
6915
+ }
6916
+ if ((trigger === "turn_completed" || trigger === "scheduled") && (supervisor.state !== "idle" || session.state !== "running" && session.state !== "idle")) {
6917
+ return null;
6918
+ }
6919
+ if (supervisor.maxSupervisionCount > 0 && supervisor.completedSupervisionCount >= supervisor.maxSupervisionCount) {
6920
+ const stopped = this.attachCycles(
6921
+ this.deps.supervisorRepo.update(id, {
6922
+ state: "stopped",
6923
+ stopReason: "max_supervision_count_reached",
6924
+ updatedAt: Date.now()
6925
+ })
6926
+ );
6927
+ this.storeSnapshot(stopped);
6928
+ this.broadcastState(stopped, "state_changed");
6929
+ this.scheduler.refresh();
6373
6930
  return null;
6374
6931
  }
6932
+ if (trigger === "turn_completed") {
6933
+ if (supervisor.scheduledAt !== void 0 && supervisor.scheduledAt !== null && supervisor.scheduledAt > Date.now()) {
6934
+ return null;
6935
+ }
6936
+ }
6937
+ if (trigger === "scheduled") {
6938
+ if (supervisor.scheduledAt === void 0 || supervisor.scheduledAt > Date.now()) {
6939
+ return null;
6940
+ }
6941
+ }
6375
6942
  if (trigger === "manual" && !INJECTABLE_SESSION_STATES.has(session.state)) {
6376
6943
  throw {
6377
6944
  code: "supervisor_session_not_ready",
@@ -6382,20 +6949,25 @@ var init_manager2 = __esm({
6382
6949
  this.evaluationAbortControllers.set(id, new AbortController());
6383
6950
  this.inFlightCompletions.set(id, createDeferredCompletion());
6384
6951
  try {
6952
+ const retrySettings = getSupervisorRetrySettings(this.deps.settingsRepo);
6385
6953
  const context = await this.contextBuilder.build(supervisor);
6386
6954
  if (trigger === "turn_completed" && context.lastTurnId && context.lastTurnId === supervisor.lastEvaluatedTurnId) {
6387
6955
  this.releaseInFlight(id);
6388
6956
  return null;
6389
6957
  }
6958
+ const shouldConsumeScheduledAt = trigger === "scheduled" || trigger === "turn_completed" && supervisor.scheduledAt !== void 0 && supervisor.scheduledAt !== null && supervisor.scheduledAt <= Date.now();
6390
6959
  const evaluatingSupervisor = this.attachCycles(
6391
6960
  this.deps.supervisorRepo.update(supervisor.id, {
6392
6961
  state: "evaluating",
6962
+ scheduledAt: shouldConsumeScheduledAt ? null : supervisor.scheduledAt ?? void 0,
6963
+ stopReason: null,
6393
6964
  errorReason: null,
6394
6965
  updatedAt: Date.now()
6395
6966
  })
6396
6967
  );
6397
6968
  this.storeSnapshot(evaluatingSupervisor);
6398
6969
  this.broadcastState(evaluatingSupervisor, "state_changed");
6970
+ this.scheduler.refresh();
6399
6971
  const activeCycle = this.deps.cycleRepo.create({
6400
6972
  id: generateCycleId(),
6401
6973
  supervisorId: supervisor.id,
@@ -6409,7 +6981,18 @@ var init_manager2 = __esm({
6409
6981
  createdAt: Date.now()
6410
6982
  });
6411
6983
  this.broadcastCycle(evaluatingSupervisor, activeCycle, "created");
6412
- return { cycle: activeCycle, context };
6984
+ return {
6985
+ cycle: activeCycle,
6986
+ context,
6987
+ trigger,
6988
+ retry: {
6989
+ retryEnabled: retrySettings.retryEnabled,
6990
+ retryMaxCount: retrySettings.retryMaxCount,
6991
+ retryDelayMs: retrySettings.retryDelaySec * 1e3,
6992
+ retryOnTimeout: retrySettings.retryOnTimeout,
6993
+ retryOnEvaluatorError: retrySettings.retryOnEvaluatorError
6994
+ }
6995
+ };
6413
6996
  } catch (error) {
6414
6997
  this.releaseInFlight(id);
6415
6998
  this.markSupervisorError(id, error);
@@ -6426,75 +7009,20 @@ var init_manager2 = __esm({
6426
7009
  const supervisorId = activeCycle.supervisorId;
6427
7010
  try {
6428
7011
  const supervisorForEval = this.supervisors.get(supervisorId) ?? this.requireSupervisor(supervisorId);
6429
- const evaluation = await this.evaluator.evaluate(supervisorForEval, context, {
6430
- signal: this.evaluationAbortControllers.get(supervisorId)?.signal
6431
- });
6432
- let injected = false;
6433
- let injectedText;
6434
- let cycleResult;
6435
- let injectionError;
6436
- if (evaluation.message.trim()) {
6437
- const injectingSupervisor = this.attachCycles(
6438
- this.deps.supervisorRepo.update(supervisorId, {
6439
- state: "injecting",
6440
- updatedAt: Date.now()
6441
- })
6442
- );
6443
- this.storeSnapshot(injectingSupervisor);
6444
- this.broadcastState(injectingSupervisor, "state_changed");
6445
- const recentCycles = this.deps.cycleRepo.listRecentForSupervisor(supervisorId, this.config.guidanceDedupeWindow + 1).filter((cycle) => cycle.id !== activeCycle.id);
6446
- try {
6447
- const injection = await this.injector.inject(
6448
- injectingSupervisor,
6449
- {
6450
- message: evaluation.message
6451
- },
6452
- recentCycles
6453
- );
6454
- injected = injection.injected;
6455
- injectedText = injection.injected ? injection.text : void 0;
6456
- cycleResult = injection.injected ? injection.text : `Skipped duplicate: ${injection.text}`;
6457
- } catch (error) {
6458
- injectionError = messageOf(error, "Injection failed");
6459
- this.logger.warn(
6460
- { err: error, supervisorId, cycleId: activeCycle.id },
6461
- "Supervisor injection failed"
6462
- );
6463
- }
6464
- }
6465
- const finalStatus = injectionError ? "failed" : injected ? "injected" : "completed";
6466
- const finishedCycle = this.deps.cycleRepo.update(activeCycle.id, {
6467
- status: finalStatus,
6468
- result: cycleResult ?? null,
6469
- injectedGuidance: injectedText,
6470
- errorReason: injectionError ?? null,
6471
- completedAt: Date.now()
6472
- });
6473
- const latestState = this.supervisors.get(supervisorId)?.state;
6474
- const nextState = latestState === "paused" ? "paused" : injectionError ? "error" : "idle";
6475
- const finishedSupervisor = this.attachCycles(
6476
- this.deps.supervisorRepo.update(supervisorId, {
6477
- state: nextState,
6478
- lastCycleAt: finishedCycle.completedAt,
6479
- lastEvaluatedTurnId: context.lastTurnId ?? void 0,
6480
- errorReason: injectionError ?? null,
6481
- updatedAt: Date.now()
6482
- })
6483
- );
6484
- this.storeSnapshot(finishedSupervisor);
6485
- this.broadcastCycle(finishedSupervisor, finishedCycle, "updated");
6486
- this.broadcastState(finishedSupervisor, "state_changed");
6487
- this.deps.cycleRepo.pruneOldest(supervisorId, this.config.maxCyclesPerSession);
7012
+ const signal = this.evaluationAbortControllers.get(supervisorId)?.signal;
7013
+ const evaluation = await this.executeCycleWithRetry(started, supervisorForEval, signal);
7014
+ const finalized = this.finalizeSuccessfulCycle(activeCycle, context, evaluation);
6488
7015
  if (this.pendingDeletes.has(supervisorId)) {
6489
7016
  this.pendingDeletes.delete(supervisorId);
6490
- this.deleteNow(finishedSupervisor);
7017
+ this.deleteNow(finalized.supervisor);
6491
7018
  }
6492
- return finishedCycle;
7019
+ return finalized.cycle;
6493
7020
  } catch (error) {
6494
7021
  if (isSupervisorEvalAborted(error)) {
7022
+ const cancelled = this.pendingPauses.has(supervisorId);
6495
7023
  const abortedCycle = this.deps.cycleRepo.update(activeCycle.id, {
6496
- status: "failed",
6497
- errorReason: messageOf(error, "Supervisor evaluator aborted"),
7024
+ status: cancelled ? "cancelled" : "failed",
7025
+ errorReason: cancelled ? null : messageOf(error, "Supervisor evaluator aborted"),
6498
7026
  completedAt: Date.now()
6499
7027
  });
6500
7028
  const currentSupervisor = this.supervisors.get(supervisorId) ?? this.requireSupervisor(supervisorId);
@@ -6505,10 +7033,11 @@ var init_manager2 = __esm({
6505
7033
  return abortedCycle;
6506
7034
  }
6507
7035
  const latestState = this.supervisors.get(supervisorId)?.state;
6508
- const nextState = latestState === "paused" ? "paused" : "idle";
7036
+ const nextState = cancelled || latestState === "paused" ? "paused" : "idle";
6509
7037
  const recoveredSupervisor = this.attachCycles(
6510
7038
  this.deps.supervisorRepo.update(supervisorId, {
6511
7039
  state: nextState,
7040
+ stopReason: null,
6512
7041
  errorReason: null,
6513
7042
  updatedAt: Date.now()
6514
7043
  })
@@ -6517,6 +7046,8 @@ var init_manager2 = __esm({
6517
7046
  this.broadcastCycle(recoveredSupervisor, abortedCycle, "updated");
6518
7047
  this.broadcastState(recoveredSupervisor, "state_changed");
6519
7048
  this.deps.cycleRepo.pruneOldest(supervisorId, this.config.maxCyclesPerSession);
7049
+ this.scheduler.refresh();
7050
+ this.pendingPauses.delete(supervisorId);
6520
7051
  return abortedCycle;
6521
7052
  }
6522
7053
  logFailure(
@@ -6534,6 +7065,7 @@ var init_manager2 = __esm({
6534
7065
  const failedSupervisor = this.attachCycles(
6535
7066
  this.deps.supervisorRepo.update(supervisorId, {
6536
7067
  state: "error",
7068
+ stopReason: null,
6537
7069
  errorReason: reason,
6538
7070
  updatedAt: Date.now()
6539
7071
  })
@@ -6545,11 +7077,124 @@ var init_manager2 = __esm({
6545
7077
  this.pendingDeletes.delete(supervisorId);
6546
7078
  this.deleteNow(failedSupervisor);
6547
7079
  }
6548
- throw error;
6549
- } finally {
6550
- this.releaseInFlight(supervisorId);
7080
+ throw error;
7081
+ } finally {
7082
+ this.pendingPauses.delete(supervisorId);
7083
+ this.releaseInFlight(supervisorId);
7084
+ }
7085
+ }
7086
+ async executeCycleWithRetry(started, supervisor, signal) {
7087
+ for (let attemptIndex = 0; ; attemptIndex += 1) {
7088
+ const attempt = this.deps.cycleAttemptRepo.create({
7089
+ id: generateAttemptId(),
7090
+ cycleId: started.cycle.id,
7091
+ attemptIndex,
7092
+ status: "evaluating",
7093
+ startedAt: Date.now()
7094
+ });
7095
+ try {
7096
+ const evaluation = await this.evaluator.evaluate(supervisor, started.context, { signal });
7097
+ this.deps.cycleAttemptRepo.update(attempt.id, {
7098
+ status: "completed",
7099
+ completedAt: Date.now(),
7100
+ providerModel: supervisor.evaluatorModel ?? null
7101
+ });
7102
+ if (evaluation.objectiveComplete) {
7103
+ return {
7104
+ objectiveComplete: true,
7105
+ injected: false,
7106
+ cycleResult: evaluation.message
7107
+ };
7108
+ }
7109
+ if (!evaluation.message.trim()) {
7110
+ return {
7111
+ objectiveComplete: false,
7112
+ injected: false
7113
+ };
7114
+ }
7115
+ if (signal?.aborted || this.pendingPauses.has(supervisor.id)) {
7116
+ throw { code: "supervisor_eval_aborted", message: "Supervisor evaluator aborted" };
7117
+ }
7118
+ const injectingSupervisor = this.attachCycles(
7119
+ this.deps.supervisorRepo.update(supervisor.id, {
7120
+ state: "injecting",
7121
+ updatedAt: Date.now()
7122
+ })
7123
+ );
7124
+ this.storeSnapshot(injectingSupervisor);
7125
+ this.broadcastState(injectingSupervisor, "state_changed");
7126
+ const recentCycles = this.deps.cycleRepo.listRecentForSupervisor(supervisor.id, this.config.guidanceDedupeWindow + 1).filter((cycle) => cycle.id !== started.cycle.id);
7127
+ const injection = await this.injector.inject(
7128
+ injectingSupervisor,
7129
+ {
7130
+ message: evaluation.message
7131
+ },
7132
+ recentCycles,
7133
+ { signal }
7134
+ );
7135
+ return {
7136
+ objectiveComplete: false,
7137
+ injected: injection.injected,
7138
+ injectedText: injection.injected ? injection.text : void 0,
7139
+ cycleResult: injection.injected ? injection.text : `Skipped duplicate: ${injection.text}`
7140
+ };
7141
+ } catch (error) {
7142
+ if (isSupervisorEvalAborted(error)) {
7143
+ this.deps.cycleAttemptRepo.update(attempt.id, {
7144
+ status: this.pendingPauses.has(supervisor.id) ? "cancelled" : "failed",
7145
+ completedAt: Date.now(),
7146
+ errorReason: this.pendingPauses.has(supervisor.id) ? null : messageOf(error, "Supervisor evaluator aborted")
7147
+ });
7148
+ throw error;
7149
+ }
7150
+ const reason = messageOf(error, "Supervisor evaluation failed");
7151
+ this.deps.cycleAttemptRepo.update(attempt.id, {
7152
+ status: "failed",
7153
+ completedAt: Date.now(),
7154
+ errorReason: reason
7155
+ });
7156
+ if (!this.shouldRetryAttempt(error, attemptIndex, started.retry)) {
7157
+ throw error;
7158
+ }
7159
+ await this.sleep(started.retry.retryDelayMs, signal);
7160
+ const evaluatingSupervisor = this.attachCycles(
7161
+ this.deps.supervisorRepo.update(supervisor.id, {
7162
+ state: "evaluating",
7163
+ updatedAt: Date.now()
7164
+ })
7165
+ );
7166
+ this.storeSnapshot(evaluatingSupervisor);
7167
+ this.broadcastState(evaluatingSupervisor, "state_changed");
7168
+ }
6551
7169
  }
6552
7170
  }
7171
+ finalizeSuccessfulCycle(activeCycle, context, result) {
7172
+ const finalStatus = result.injected ? "injected" : result.objectiveComplete ? "completed" : "completed";
7173
+ const finishedCycle = this.deps.cycleRepo.update(activeCycle.id, {
7174
+ status: finalStatus,
7175
+ result: result.cycleResult ?? null,
7176
+ injectedGuidance: result.injectedText ?? null,
7177
+ errorReason: null,
7178
+ completedAt: Date.now()
7179
+ });
7180
+ const finishedSupervisor = this.attachCycles(
7181
+ this.deps.supervisorRepo.update(activeCycle.supervisorId, {
7182
+ state: result.objectiveComplete ? "stopped" : "idle",
7183
+ completedSupervisionCount: (this.supervisors.get(activeCycle.supervisorId)?.completedSupervisionCount ?? 0) + 1,
7184
+ stopReason: result.objectiveComplete ? "objective_complete" : null,
7185
+ lastCycleAt: finishedCycle.completedAt,
7186
+ lastEvaluatedTurnId: context.lastTurnId ?? void 0,
7187
+ errorReason: null,
7188
+ updatedAt: Date.now()
7189
+ })
7190
+ );
7191
+ this.storeSnapshot(finishedSupervisor);
7192
+ this.broadcastCycle(finishedSupervisor, finishedCycle, "updated");
7193
+ this.broadcastState(finishedSupervisor, "state_changed");
7194
+ this.deps.cycleRepo.pruneOldest(activeCycle.supervisorId, this.config.maxCyclesPerSession);
7195
+ this.scheduler.refresh();
7196
+ return { cycle: finishedCycle, supervisor: finishedSupervisor };
7197
+ }
6553
7198
  /**
6554
7199
  * Flip a supervisor to 'error' state when something blows up before we
6555
7200
  * had a chance to create a cycle. Without this the supervisor can get
@@ -6615,6 +7260,14 @@ var init_manager2 = __esm({
6615
7260
  cycles: this.deps.cycleRepo.listRecentForSupervisor(supervisor.id, 20)
6616
7261
  };
6617
7262
  }
7263
+ listScheduledSupervisors() {
7264
+ return Array.from(this.supervisors.values()).filter(
7265
+ (supervisor) => supervisor.state === "idle" && typeof supervisor.scheduledAt === "number" && Number.isFinite(supervisor.scheduledAt)
7266
+ ).map((supervisor) => ({
7267
+ supervisorId: supervisor.id,
7268
+ scheduledAt: supervisor.scheduledAt
7269
+ }));
7270
+ }
6618
7271
  storeSnapshot(supervisor) {
6619
7272
  this.supervisors.set(supervisor.id, supervisor);
6620
7273
  this.supervisorsBySession.set(supervisor.sessionId, supervisor.id);
@@ -6624,7 +7277,9 @@ var init_manager2 = __esm({
6624
7277
  this.supervisors.delete(supervisor.id);
6625
7278
  this.supervisorsBySession.delete(supervisor.sessionId);
6626
7279
  this.pendingDeletes.delete(supervisor.id);
7280
+ this.pendingPauses.delete(supervisor.id);
6627
7281
  this.releaseInFlight(supervisor.id);
7282
+ this.scheduler.refresh();
6628
7283
  this.deps.broadcaster.broadcast(
6629
7284
  Topics.supervisorState(supervisor.workspaceId, supervisor.sessionId),
6630
7285
  { supervisorId: supervisor.id, event: "deleted" }
@@ -6658,6 +7313,43 @@ var init_manager2 = __esm({
6658
7313
  { cycle, event }
6659
7314
  );
6660
7315
  }
7316
+ shouldRetryAttempt(error, attemptIndex, retry) {
7317
+ if (!retry.retryEnabled) {
7318
+ return false;
7319
+ }
7320
+ if (attemptIndex >= retry.retryMaxCount) {
7321
+ return false;
7322
+ }
7323
+ const code = error && typeof error === "object" && "code" in error ? error.code : void 0;
7324
+ if (code === "supervisor_eval_timeout") {
7325
+ return retry.retryOnTimeout;
7326
+ }
7327
+ if (code === "supervisor_eval_failed") {
7328
+ return retry.retryOnEvaluatorError;
7329
+ }
7330
+ return false;
7331
+ }
7332
+ async sleep(delayMs, signal) {
7333
+ if (delayMs <= 0) {
7334
+ return;
7335
+ }
7336
+ if (signal?.aborted) {
7337
+ throw { code: "supervisor_eval_aborted", message: "Supervisor evaluator aborted" };
7338
+ }
7339
+ await new Promise((resolve4, reject) => {
7340
+ const timer = setTimeout(() => {
7341
+ signal?.removeEventListener("abort", onAbort);
7342
+ resolve4();
7343
+ }, delayMs);
7344
+ timer.unref?.();
7345
+ const onAbort = () => {
7346
+ clearTimeout(timer);
7347
+ signal?.removeEventListener("abort", onAbort);
7348
+ reject({ code: "supervisor_eval_aborted", message: "Supervisor evaluator aborted" });
7349
+ };
7350
+ signal?.addEventListener("abort", onAbort, { once: true });
7351
+ });
7352
+ }
6661
7353
  };
6662
7354
  }
6663
7355
  });
@@ -7559,6 +8251,7 @@ var init_watcher = __esm({
7559
8251
  dirtyTimer = null;
7560
8252
  firstDirtyTime = null;
7561
8253
  pendingReason = null;
8254
+ pendingWorktreeChanged = false;
7562
8255
  DEBOUNCE_MS = 200;
7563
8256
  MAX_WAIT_MS = 1e3;
7564
8257
  /**
@@ -7571,6 +8264,9 @@ var init_watcher = __esm({
7571
8264
  if (this.firstDirtyTime === null) {
7572
8265
  this.firstDirtyTime = now;
7573
8266
  }
8267
+ if (changedPath && this.isWorktreeMetadataPath(changedPath)) {
8268
+ this.pendingWorktreeChanged = true;
8269
+ }
7574
8270
  if (changedPath && !this.isGitMetadataPath(changedPath)) {
7575
8271
  this.pendingReason = "fs_change";
7576
8272
  } else if (changedPath && this.pendingReason !== "fs_change") {
@@ -7589,13 +8285,23 @@ var init_watcher = __esm({
7589
8285
  this.broadcaster?.broadcast(Topics.workspaceFsDirty(this.workspaceId), {
7590
8286
  reason: this.pendingReason ?? "fs_change"
7591
8287
  });
8288
+ if (this.pendingWorktreeChanged) {
8289
+ this.broadcaster?.broadcast(Topics.workspaceGitState(this.workspaceId), {
8290
+ worktreeChanged: true
8291
+ });
8292
+ }
7592
8293
  this.dirtyTimer = null;
7593
8294
  this.firstDirtyTime = null;
7594
8295
  this.pendingReason = null;
8296
+ this.pendingWorktreeChanged = false;
7595
8297
  }
7596
8298
  isGitMetadataPath(changedPath) {
7597
8299
  return changedPath.replace(/\\/g, "/").includes("/.git/");
7598
8300
  }
8301
+ isWorktreeMetadataPath(changedPath) {
8302
+ const normalized = changedPath.replace(/\\/g, "/");
8303
+ return normalized.includes("/.git/worktrees");
8304
+ }
7599
8305
  /**
7600
8306
  * Stops watching and cleans up resources.
7601
8307
  */
@@ -7831,12 +8537,103 @@ var init_manager4 = __esm({
7831
8537
  }
7832
8538
  });
7833
8539
 
8540
+ // packages/server/src/ws/activation.ts
8541
+ var DEFAULT_OPTIONS, ActivationManager;
8542
+ var init_activation = __esm({
8543
+ "packages/server/src/ws/activation.ts"() {
8544
+ "use strict";
8545
+ DEFAULT_OPTIONS = {
8546
+ graceMs: 3e3
8547
+ };
8548
+ ActivationManager = class {
8549
+ options;
8550
+ lease = null;
8551
+ generation = 0;
8552
+ constructor(options) {
8553
+ this.options = { ...DEFAULT_OPTIONS, ...options };
8554
+ }
8555
+ claim(clientInstanceId, wsClientId, request) {
8556
+ const now = Date.now();
8557
+ const activeLease = this.getLease();
8558
+ if (activeLease && activeLease.clientInstanceId === clientInstanceId) {
8559
+ const isGraceRecovery = activeLease.graceUntil !== null && now <= activeLease.graceUntil;
8560
+ const displacedWsClientId2 = isGraceRecovery || activeLease.wsClientId === wsClientId ? null : activeLease.wsClientId;
8561
+ activeLease.wsClientId = wsClientId;
8562
+ activeLease.graceUntil = null;
8563
+ return {
8564
+ active: true,
8565
+ generation: activeLease.generation,
8566
+ recoveryMode: "grace_recover",
8567
+ displacedWsClientId: displacedWsClientId2
8568
+ };
8569
+ }
8570
+ const displacedWsClientId = activeLease && activeLease.clientInstanceId !== clientInstanceId ? activeLease.wsClientId : null;
8571
+ const recoveryMode = displacedWsClientId === null ? "fresh" : "takeover";
8572
+ this.generation += 1;
8573
+ this.lease = {
8574
+ clientInstanceId,
8575
+ wsClientId,
8576
+ generation: this.generation,
8577
+ issuedAt: now,
8578
+ graceUntil: null,
8579
+ ip: request.ip,
8580
+ userAgent: request.headers["user-agent"] ?? ""
8581
+ };
8582
+ return {
8583
+ active: true,
8584
+ generation: this.lease.generation,
8585
+ recoveryMode,
8586
+ displacedWsClientId
8587
+ };
8588
+ }
8589
+ release(clientInstanceId, generation) {
8590
+ const lease = this.getLease();
8591
+ if (!lease) {
8592
+ return;
8593
+ }
8594
+ if (lease.clientInstanceId !== clientInstanceId || lease.generation !== generation) {
8595
+ return;
8596
+ }
8597
+ this.lease = null;
8598
+ }
8599
+ onSocketClosed(wsClientId) {
8600
+ const lease = this.getLease();
8601
+ if (!lease || lease.wsClientId !== wsClientId) {
8602
+ return;
8603
+ }
8604
+ lease.graceUntil = Date.now() + this.options.graceMs;
8605
+ }
8606
+ getLease() {
8607
+ if (!this.lease) {
8608
+ return null;
8609
+ }
8610
+ return this.lease;
8611
+ }
8612
+ };
8613
+ }
8614
+ });
8615
+
7834
8616
  // packages/server/src/ws/dispatch.ts
7835
8617
  function registerCommand(op, schema, handler) {
7836
8618
  handlers.set(op, handler);
7837
8619
  schemas.set(op, schema);
7838
8620
  }
7839
8621
  async function dispatch(msg, ctx, clientId) {
8622
+ const isWsDispatch = clientId !== void 0 && typeof ctx.broadcaster.getRequestMetadata === "function";
8623
+ if (isWsDispatch && !ACTIVATION_ALLOWLIST.has(msg.op)) {
8624
+ const active = ctx.activationMgr.getLease();
8625
+ if (!active || active.wsClientId !== clientId) {
8626
+ return {
8627
+ kind: "result",
8628
+ id: msg.id,
8629
+ ok: false,
8630
+ error: {
8631
+ code: "activation_required",
8632
+ message: "This tab is no longer the active session"
8633
+ }
8634
+ };
8635
+ }
8636
+ }
7840
8637
  const handler = handlers.get(msg.op);
7841
8638
  if (!handler) {
7842
8639
  return {
@@ -7893,21 +8690,26 @@ function normalizeError(error) {
7893
8690
  message: candidate.message || "An internal error occurred"
7894
8691
  };
7895
8692
  }
7896
- var handlers, schemas;
8693
+ var handlers, schemas, ACTIVATION_ALLOWLIST;
7897
8694
  var init_dispatch = __esm({
7898
8695
  "packages/server/src/ws/dispatch.ts"() {
7899
8696
  "use strict";
7900
8697
  handlers = /* @__PURE__ */ new Map();
7901
8698
  schemas = /* @__PURE__ */ new Map();
8699
+ ACTIVATION_ALLOWLIST = /* @__PURE__ */ new Set([
8700
+ "activation.claim",
8701
+ "activation.release",
8702
+ "connection.probe"
8703
+ ]);
7902
8704
  }
7903
8705
  });
7904
8706
 
7905
8707
  // packages/server/src/ws/fencing.ts
7906
- var DEFAULT_OPTIONS, FencingManager;
8708
+ var DEFAULT_OPTIONS2, FencingManager;
7907
8709
  var init_fencing = __esm({
7908
8710
  "packages/server/src/ws/fencing.ts"() {
7909
8711
  "use strict";
7910
- DEFAULT_OPTIONS = {
8712
+ DEFAULT_OPTIONS2 = {
7911
8713
  visibleHeartbeatMs: 1e4,
7912
8714
  // 10 seconds
7913
8715
  hiddenHeartbeatMs: 2e4,
@@ -7926,7 +8728,7 @@ var init_fencing = __esm({
7926
8728
  // workspaceId -> { clientId, closedAt, ip, ua } (for grace period)
7927
8729
  lastWriter = /* @__PURE__ */ new Map();
7928
8730
  constructor(options) {
7929
- this.options = { ...DEFAULT_OPTIONS, ...options };
8731
+ this.options = { ...DEFAULT_OPTIONS2, ...options };
7930
8732
  }
7931
8733
  /**
7932
8734
  * Request controller status for a workspace.
@@ -8445,8 +9247,12 @@ var init_client = __esm({
8445
9247
  evictedBytesSinceLastWarn = 0;
8446
9248
  lastStreamBufferWarnAt = 0;
8447
9249
  logger;
9250
+ markAlive() {
9251
+ this.isAlive = true;
9252
+ }
8448
9253
  setupSocketHandlers() {
8449
9254
  this.socket.on("message", (data, isBinary) => {
9255
+ this.markAlive();
8450
9256
  if (isBinary) {
8451
9257
  this.messageHandler?.(data);
8452
9258
  return;
@@ -8465,7 +9271,7 @@ var init_client = __esm({
8465
9271
  this.closeHandler?.();
8466
9272
  });
8467
9273
  this.socket.on("pong", () => {
8468
- this.isAlive = true;
9274
+ this.markAlive();
8469
9275
  });
8470
9276
  }
8471
9277
  /**
@@ -8496,6 +9302,20 @@ var init_client = __esm({
8496
9302
  return false;
8497
9303
  }
8498
9304
  }
9305
+ sendControlAndClose(msg, code, reason) {
9306
+ if (this.socket.readyState !== WebSocket.OPEN) {
9307
+ return false;
9308
+ }
9309
+ try {
9310
+ this.socket.send(JSON.stringify(msg), () => {
9311
+ this.socket.close(code, reason);
9312
+ });
9313
+ return true;
9314
+ } catch (error) {
9315
+ console.error(`Failed to send message to client ${this.id}:`, error);
9316
+ return false;
9317
+ }
9318
+ }
8499
9319
  sendBinary(data) {
8500
9320
  if (this.socket.readyState !== WebSocket.OPEN) {
8501
9321
  return false;
@@ -8660,6 +9480,16 @@ var init_client = __esm({
8660
9480
  };
8661
9481
  return this.send(event);
8662
9482
  }
9483
+ sendEventAndClose(topic, data, code, reason, seq = 0) {
9484
+ const event = {
9485
+ kind: "event",
9486
+ topic,
9487
+ seq,
9488
+ timestamp: Date.now(),
9489
+ data
9490
+ };
9491
+ return this.sendControlAndClose(event, code, reason);
9492
+ }
8663
9493
  /**
8664
9494
  * Check if client subscribes to a topic (supports glob patterns)
8665
9495
  */
@@ -8769,6 +9599,7 @@ var init_hub = __esm({
8769
9599
  }
8770
9600
  deps;
8771
9601
  clients = /* @__PURE__ */ new Map();
9602
+ clientRequests = /* @__PURE__ */ new Map();
8772
9603
  eventUnsubscribers = [];
8773
9604
  nextStreamId = 1;
8774
9605
  // Per-client queue of waiters for the next inbound binary frame. The
@@ -8785,9 +9616,10 @@ var init_hub = __esm({
8785
9616
  /**
8786
9617
  * Handle a new WebSocket connection
8787
9618
  */
8788
- handleConnection(socket, _req) {
9619
+ handleConnection(socket, req) {
8789
9620
  const client = new WsClient(socket, uuidv4(), this.deps.logger);
8790
9621
  this.clients.set(client.id, client);
9622
+ this.clientRequests.set(client.id, req);
8791
9623
  client.sendEvent("connection.status", {
8792
9624
  status: "connected",
8793
9625
  clientId: client.id,
@@ -8921,8 +9753,10 @@ var init_hub = __esm({
8921
9753
  */
8922
9754
  handleClose(client) {
8923
9755
  this.clients.delete(client.id);
9756
+ this.clientRequests.delete(client.id);
8924
9757
  this.discardPendingBinaryWaiters(client.id);
8925
9758
  this.deps.commandContext?.autoFetch.unregisterViewer(client.id);
9759
+ this.deps.commandContext?.activationMgr.onSocketClosed(client.id);
8926
9760
  }
8927
9761
  /**
8928
9762
  * Takeover: Force close existing writer and accept new one
@@ -8963,6 +9797,24 @@ var init_hub = __esm({
8963
9797
  if (!client) return false;
8964
9798
  return client.sendBinary(data);
8965
9799
  }
9800
+ revokeAndCloseClient(clientId, generation) {
9801
+ const client = this.clients.get(clientId);
9802
+ if (!client) {
9803
+ return;
9804
+ }
9805
+ client.sendEventAndClose(
9806
+ "activation.revoked",
9807
+ {
9808
+ reason: "displaced",
9809
+ generation
9810
+ },
9811
+ 4001,
9812
+ "single_active_displaced"
9813
+ );
9814
+ }
9815
+ getRequestMetadata(clientId) {
9816
+ return this.clientRequests.get(clientId);
9817
+ }
8966
9818
  /**
8967
9819
  * Get the current writer client
8968
9820
  * DEPRECATED: Writer tracking now handled by FencingManager
@@ -8975,6 +9827,10 @@ var init_hub = __esm({
8975
9827
  */
8976
9828
  pingAll() {
8977
9829
  for (const client of this.clients.values()) {
9830
+ if (!client.alive) {
9831
+ client.close(1011, "keepalive_timeout");
9832
+ continue;
9833
+ }
8978
9834
  client.ping();
8979
9835
  }
8980
9836
  }
@@ -8986,6 +9842,7 @@ var init_hub = __esm({
8986
9842
  client.close();
8987
9843
  }
8988
9844
  this.clients.clear();
9845
+ this.clientRequests.clear();
8989
9846
  }
8990
9847
  /**
8991
9848
  * Subscribe to domain events and broadcast them
@@ -9233,6 +10090,63 @@ var init_workspace_activity = __esm({
9233
10090
  }
9234
10091
  });
9235
10092
 
10093
+ // packages/server/src/commands/activation.ts
10094
+ import { z as z8 } from "zod";
10095
+ var init_activation2 = __esm({
10096
+ "packages/server/src/commands/activation.ts"() {
10097
+ "use strict";
10098
+ init_dispatch();
10099
+ registerCommand(
10100
+ "activation.claim",
10101
+ z8.object({ clientInstanceId: z8.string().min(1) }),
10102
+ async (args, ctx, clientId) => {
10103
+ if (!clientId) {
10104
+ throw {
10105
+ code: "activation_request_unavailable",
10106
+ message: "Activation claim requires websocket request metadata"
10107
+ };
10108
+ }
10109
+ const request = ctx.broadcaster.getRequestMetadata?.(clientId);
10110
+ if (!request) {
10111
+ throw {
10112
+ code: "activation_request_unavailable",
10113
+ message: "Activation claim requires websocket request metadata"
10114
+ };
10115
+ }
10116
+ const claim = ctx.activationMgr.claim(args.clientInstanceId, clientId, request);
10117
+ if (claim.displacedWsClientId) {
10118
+ ctx.broadcaster.revokeAndCloseClient?.(claim.displacedWsClientId, claim.generation);
10119
+ }
10120
+ return claim;
10121
+ }
10122
+ );
10123
+ registerCommand(
10124
+ "activation.release",
10125
+ z8.object({ clientInstanceId: z8.string(), generation: z8.number().int().positive() }),
10126
+ async (args, ctx, clientId) => {
10127
+ const lease = ctx.activationMgr.getLease();
10128
+ if (!clientId || !lease || lease.wsClientId !== clientId) {
10129
+ return { ok: false };
10130
+ }
10131
+ ctx.activationMgr.release(args.clientInstanceId, args.generation);
10132
+ return { ok: true };
10133
+ }
10134
+ );
10135
+ }
10136
+ });
10137
+
10138
+ // packages/server/src/commands/connection.ts
10139
+ import { z as z9 } from "zod";
10140
+ var init_connection = __esm({
10141
+ "packages/server/src/commands/connection.ts"() {
10142
+ "use strict";
10143
+ init_dispatch();
10144
+ registerCommand("connection.probe", z9.object({}).default({}), async () => {
10145
+ return { ok: true };
10146
+ });
10147
+ }
10148
+ });
10149
+
9236
10150
  // packages/server/src/provider-runtime/runtime-status.ts
9237
10151
  function canAutoInstall(provider, platform, missingCommands, missingPrerequisites, availableCommands) {
9238
10152
  const strategies = provider.install.strategies[platform] ?? [];
@@ -9327,7 +10241,7 @@ var init_runtime_status = __esm({
9327
10241
  });
9328
10242
 
9329
10243
  // packages/server/src/commands/session.ts
9330
- import { z as z8 } from "zod";
10244
+ import { z as z10 } from "zod";
9331
10245
  function getProviderFromRegistry(providerId, registry) {
9332
10246
  return registry.find((provider) => provider.id === providerId);
9333
10247
  }
@@ -9338,8 +10252,8 @@ var init_session = __esm({
9338
10252
  init_dispatch();
9339
10253
  registerCommand(
9340
10254
  "session.list",
9341
- z8.object({
9342
- workspaceId: z8.string()
10255
+ z10.object({
10256
+ workspaceId: z10.string()
9343
10257
  }),
9344
10258
  async (args, ctx) => {
9345
10259
  return ctx.sessionMgr.getForWorkspace(args.workspaceId);
@@ -9347,10 +10261,10 @@ var init_session = __esm({
9347
10261
  );
9348
10262
  registerCommand(
9349
10263
  "session.create",
9350
- z8.object({
9351
- workspaceId: z8.string(),
9352
- providerId: z8.string(),
9353
- draft: z8.string().optional()
10264
+ z10.object({
10265
+ workspaceId: z10.string(),
10266
+ providerId: z10.string(),
10267
+ draft: z10.string().optional()
9354
10268
  }),
9355
10269
  async (args, ctx) => {
9356
10270
  const workspace = ctx.workspaceMgr.get(args.workspaceId);
@@ -9384,8 +10298,8 @@ var init_session = __esm({
9384
10298
  );
9385
10299
  registerCommand(
9386
10300
  "session.stop",
9387
- z8.object({
9388
- sessionId: z8.string()
10301
+ z10.object({
10302
+ sessionId: z10.string()
9389
10303
  }),
9390
10304
  async (args, ctx) => {
9391
10305
  await ctx.sessionMgr.stop(args.sessionId);
@@ -9393,8 +10307,8 @@ var init_session = __esm({
9393
10307
  );
9394
10308
  registerCommand(
9395
10309
  "session.remove",
9396
- z8.object({
9397
- sessionId: z8.string()
10310
+ z10.object({
10311
+ sessionId: z10.string()
9398
10312
  }),
9399
10313
  async (args, ctx) => {
9400
10314
  const session = ctx.sessionMgr.get(args.sessionId);
@@ -9559,7 +10473,7 @@ var init_tree = __esm({
9559
10473
  });
9560
10474
 
9561
10475
  // packages/server/src/commands/file.ts
9562
- import { z as z9 } from "zod";
10476
+ import { z as z11 } from "zod";
9563
10477
  var init_file = __esm({
9564
10478
  "packages/server/src/commands/file.ts"() {
9565
10479
  "use strict";
@@ -9568,9 +10482,9 @@ var init_file = __esm({
9568
10482
  init_dispatch();
9569
10483
  registerCommand(
9570
10484
  "file.readTree",
9571
- z9.object({
9572
- workspaceId: z9.string(),
9573
- subPath: z9.string().optional()
10485
+ z11.object({
10486
+ workspaceId: z11.string(),
10487
+ subPath: z11.string().optional()
9574
10488
  }),
9575
10489
  async (args, ctx) => {
9576
10490
  const workspace = ctx.workspaceMgr.get(args.workspaceId);
@@ -9582,10 +10496,10 @@ var init_file = __esm({
9582
10496
  );
9583
10497
  registerCommand(
9584
10498
  "file.search",
9585
- z9.object({
9586
- workspaceId: z9.string(),
9587
- query: z9.string(),
9588
- limit: z9.number().int().positive().max(50).optional()
10499
+ z11.object({
10500
+ workspaceId: z11.string(),
10501
+ query: z11.string(),
10502
+ limit: z11.number().int().positive().max(50).optional()
9589
10503
  }),
9590
10504
  async (args, ctx) => {
9591
10505
  const workspace = ctx.workspaceMgr.get(args.workspaceId);
@@ -9597,9 +10511,9 @@ var init_file = __esm({
9597
10511
  );
9598
10512
  registerCommand(
9599
10513
  "file.read",
9600
- z9.object({
9601
- workspaceId: z9.string(),
9602
- path: z9.string()
10514
+ z11.object({
10515
+ workspaceId: z11.string(),
10516
+ path: z11.string()
9603
10517
  }),
9604
10518
  async (args, ctx) => {
9605
10519
  const workspace = ctx.workspaceMgr.get(args.workspaceId);
@@ -9611,9 +10525,9 @@ var init_file = __esm({
9611
10525
  );
9612
10526
  registerCommand(
9613
10527
  "file.create",
9614
- z9.object({
9615
- workspaceId: z9.string(),
9616
- path: z9.string()
10528
+ z11.object({
10529
+ workspaceId: z11.string(),
10530
+ path: z11.string()
9617
10531
  }),
9618
10532
  async (args, ctx) => {
9619
10533
  const workspace = ctx.workspaceMgr.get(args.workspaceId);
@@ -9631,9 +10545,9 @@ var init_file = __esm({
9631
10545
  );
9632
10546
  registerCommand(
9633
10547
  "file.mkdir",
9634
- z9.object({
9635
- workspaceId: z9.string(),
9636
- path: z9.string()
10548
+ z11.object({
10549
+ workspaceId: z11.string(),
10550
+ path: z11.string()
9637
10551
  }),
9638
10552
  async (args, ctx) => {
9639
10553
  const workspace = ctx.workspaceMgr.get(args.workspaceId);
@@ -9651,9 +10565,9 @@ var init_file = __esm({
9651
10565
  );
9652
10566
  registerCommand(
9653
10567
  "file.delete",
9654
- z9.object({
9655
- workspaceId: z9.string(),
9656
- path: z9.string()
10568
+ z11.object({
10569
+ workspaceId: z11.string(),
10570
+ path: z11.string()
9657
10571
  }),
9658
10572
  async (args, ctx) => {
9659
10573
  const workspace = ctx.workspaceMgr.get(args.workspaceId);
@@ -9671,11 +10585,11 @@ var init_file = __esm({
9671
10585
  );
9672
10586
  registerCommand(
9673
10587
  "file.write",
9674
- z9.object({
9675
- workspaceId: z9.string(),
9676
- path: z9.string(),
9677
- content: z9.string(),
9678
- baseHash: z9.string().optional()
10588
+ z11.object({
10589
+ workspaceId: z11.string(),
10590
+ path: z11.string(),
10591
+ content: z11.string(),
10592
+ baseHash: z11.string().optional()
9679
10593
  // For conflict detection
9680
10594
  }),
9681
10595
  async (args, ctx) => {
@@ -9766,7 +10680,7 @@ var init_git_events = __esm({
9766
10680
  });
9767
10681
 
9768
10682
  // packages/server/src/commands/git.ts
9769
- import { z as z10 } from "zod";
10683
+ import { z as z12 } from "zod";
9770
10684
  async function runGitNetworkOperation(ctx, workspaceId, op) {
9771
10685
  if (!ctx.autoFetch?.runExclusive) {
9772
10686
  return op();
@@ -9781,16 +10695,16 @@ var init_git2 = __esm({
9781
10695
  init_diff();
9782
10696
  init_dispatch();
9783
10697
  init_git_events();
9784
- gitHttpAuthSchema = z10.object({
9785
- username: z10.string(),
9786
- password: z10.string()
10698
+ gitHttpAuthSchema = z12.object({
10699
+ username: z12.string(),
10700
+ password: z12.string()
9787
10701
  });
9788
- gitCommitRevisionSchema = z10.string().regex(/^[0-9a-fA-F]{7,64}$/, "Invalid git commit revision");
10702
+ gitCommitRevisionSchema = z12.string().regex(/^[0-9a-fA-F]{7,64}$/, "Invalid git commit revision");
9789
10703
  GIT_BACKGROUND_FETCH_TIMEOUT_MS = 30 * 1e3;
9790
10704
  registerCommand(
9791
10705
  "git.status",
9792
- z10.object({
9793
- workspaceId: z10.string()
10706
+ z12.object({
10707
+ workspaceId: z12.string()
9794
10708
  }),
9795
10709
  async (args, ctx) => {
9796
10710
  const workspace = ctx.workspaceMgr.get(args.workspaceId);
@@ -9802,9 +10716,9 @@ var init_git2 = __esm({
9802
10716
  );
9803
10717
  registerCommand(
9804
10718
  "git.stage",
9805
- z10.object({
9806
- workspaceId: z10.string(),
9807
- paths: z10.array(z10.string())
10719
+ z12.object({
10720
+ workspaceId: z12.string(),
10721
+ paths: z12.array(z12.string())
9808
10722
  }),
9809
10723
  async (args, ctx) => {
9810
10724
  const workspace = ctx.workspaceMgr.get(args.workspaceId);
@@ -9818,10 +10732,10 @@ var init_git2 = __esm({
9818
10732
  );
9819
10733
  registerCommand(
9820
10734
  "git.diff",
9821
- z10.object({
9822
- workspaceId: z10.string(),
9823
- path: z10.string(),
9824
- staged: z10.boolean().optional()
10735
+ z12.object({
10736
+ workspaceId: z12.string(),
10737
+ path: z12.string(),
10738
+ staged: z12.boolean().optional()
9825
10739
  }),
9826
10740
  async (args, ctx) => {
9827
10741
  const workspace = ctx.workspaceMgr.get(args.workspaceId);
@@ -9835,9 +10749,9 @@ var init_git2 = __esm({
9835
10749
  );
9836
10750
  registerCommand(
9837
10751
  "git.log",
9838
- z10.object({
9839
- workspaceId: z10.string(),
9840
- limit: z10.number().int().min(1).max(50).optional()
10752
+ z12.object({
10753
+ workspaceId: z12.string(),
10754
+ limit: z12.number().int().min(1).max(50).optional()
9841
10755
  }),
9842
10756
  async (args, ctx) => {
9843
10757
  const workspace = ctx.workspaceMgr.get(args.workspaceId);
@@ -9851,8 +10765,8 @@ var init_git2 = __esm({
9851
10765
  );
9852
10766
  registerCommand(
9853
10767
  "git.show",
9854
- z10.object({
9855
- workspaceId: z10.string(),
10768
+ z12.object({
10769
+ workspaceId: z12.string(),
9856
10770
  sha: gitCommitRevisionSchema
9857
10771
  }),
9858
10772
  async (args, ctx) => {
@@ -9867,9 +10781,9 @@ var init_git2 = __esm({
9867
10781
  );
9868
10782
  registerCommand(
9869
10783
  "git.unstage",
9870
- z10.object({
9871
- workspaceId: z10.string(),
9872
- paths: z10.array(z10.string())
10784
+ z12.object({
10785
+ workspaceId: z12.string(),
10786
+ paths: z12.array(z12.string())
9873
10787
  }),
9874
10788
  async (args, ctx) => {
9875
10789
  const workspace = ctx.workspaceMgr.get(args.workspaceId);
@@ -9883,9 +10797,9 @@ var init_git2 = __esm({
9883
10797
  );
9884
10798
  registerCommand(
9885
10799
  "git.discard",
9886
- z10.object({
9887
- workspaceId: z10.string(),
9888
- paths: z10.array(z10.string())
10800
+ z12.object({
10801
+ workspaceId: z12.string(),
10802
+ paths: z12.array(z12.string())
9889
10803
  }),
9890
10804
  async (args, ctx) => {
9891
10805
  const workspace = ctx.workspaceMgr.get(args.workspaceId);
@@ -9901,9 +10815,9 @@ var init_git2 = __esm({
9901
10815
  );
9902
10816
  registerCommand(
9903
10817
  "git.commit",
9904
- z10.object({
9905
- workspaceId: z10.string(),
9906
- message: z10.string()
10818
+ z12.object({
10819
+ workspaceId: z12.string(),
10820
+ message: z12.string()
9907
10821
  }),
9908
10822
  async (args, ctx) => {
9909
10823
  const workspace = ctx.workspaceMgr.get(args.workspaceId);
@@ -9920,11 +10834,11 @@ var init_git2 = __esm({
9920
10834
  );
9921
10835
  registerCommand(
9922
10836
  "git.push",
9923
- z10.object({
9924
- workspaceId: z10.string(),
9925
- remote: z10.string().optional(),
9926
- branch: z10.string().optional(),
9927
- force: z10.boolean().optional(),
10837
+ z12.object({
10838
+ workspaceId: z12.string(),
10839
+ remote: z12.string().optional(),
10840
+ branch: z12.string().optional(),
10841
+ force: z12.boolean().optional(),
9928
10842
  auth: gitHttpAuthSchema.optional()
9929
10843
  }),
9930
10844
  async (args, ctx) => {
@@ -9951,10 +10865,10 @@ var init_git2 = __esm({
9951
10865
  );
9952
10866
  registerCommand(
9953
10867
  "git.pull",
9954
- z10.object({
9955
- workspaceId: z10.string(),
9956
- remote: z10.string().optional(),
9957
- branch: z10.string().optional(),
10868
+ z12.object({
10869
+ workspaceId: z12.string(),
10870
+ remote: z12.string().optional(),
10871
+ branch: z12.string().optional(),
9958
10872
  auth: gitHttpAuthSchema.optional()
9959
10873
  }),
9960
10874
  async (args, ctx) => {
@@ -9982,12 +10896,12 @@ var init_git2 = __esm({
9982
10896
  );
9983
10897
  registerCommand(
9984
10898
  "git.fetch",
9985
- z10.object({
9986
- workspaceId: z10.string(),
9987
- remote: z10.string().optional(),
9988
- prune: z10.boolean().optional(),
10899
+ z12.object({
10900
+ workspaceId: z12.string(),
10901
+ remote: z12.string().optional(),
10902
+ prune: z12.boolean().optional(),
9989
10903
  auth: gitHttpAuthSchema.optional(),
9990
- background: z10.boolean().optional()
10904
+ background: z12.boolean().optional()
9991
10905
  }),
9992
10906
  async (args, ctx, clientId) => {
9993
10907
  const workspace = ctx.workspaceMgr.get(args.workspaceId);
@@ -10016,10 +10930,10 @@ var init_git2 = __esm({
10016
10930
  );
10017
10931
  registerCommand(
10018
10932
  "git.checkout",
10019
- z10.object({
10020
- workspaceId: z10.string(),
10021
- ref: z10.string(),
10022
- createBranch: z10.boolean().optional()
10933
+ z12.object({
10934
+ workspaceId: z12.string(),
10935
+ ref: z12.string(),
10936
+ createBranch: z12.boolean().optional()
10023
10937
  }),
10024
10938
  async (args, ctx) => {
10025
10939
  const workspace = ctx.workspaceMgr.get(args.workspaceId);
@@ -10041,10 +10955,10 @@ var init_git2 = __esm({
10041
10955
  );
10042
10956
  registerCommand(
10043
10957
  "git.branch",
10044
- z10.object({
10045
- workspaceId: z10.string(),
10046
- name: z10.string(),
10047
- startPoint: z10.string().optional()
10958
+ z12.object({
10959
+ workspaceId: z12.string(),
10960
+ name: z12.string(),
10961
+ startPoint: z12.string().optional()
10048
10962
  }),
10049
10963
  async (args, ctx) => {
10050
10964
  const workspace = ctx.workspaceMgr.get(args.workspaceId);
@@ -10063,8 +10977,8 @@ var init_git2 = __esm({
10063
10977
  );
10064
10978
  registerCommand(
10065
10979
  "git.branches",
10066
- z10.object({
10067
- workspaceId: z10.string()
10980
+ z12.object({
10981
+ workspaceId: z12.string()
10068
10982
  }),
10069
10983
  async (args, ctx) => {
10070
10984
  const workspace = ctx.workspaceMgr.get(args.workspaceId);
@@ -10158,7 +11072,7 @@ var init_config_io = __esm({
10158
11072
  });
10159
11073
 
10160
11074
  // packages/server/src/commands/settings.ts
10161
- import { z as z11 } from "zod";
11075
+ import { z as z13 } from "zod";
10162
11076
  function flattenSettings(obj, prefix = "") {
10163
11077
  const result = {};
10164
11078
  for (const [key, value] of Object.entries(obj)) {
@@ -10181,27 +11095,34 @@ var init_settings2 = __esm({
10181
11095
  init_provider_config_repo();
10182
11096
  init_settings();
10183
11097
  init_dispatch();
10184
- SettingsSchema = z11.object({
10185
- defaultProviderId: z11.string().optional(),
10186
- notifications: z11.object({
10187
- enabled: z11.boolean().optional(),
10188
- soundEnabled: z11.boolean().optional(),
11098
+ SettingsSchema = z13.object({
11099
+ defaultProviderId: z13.string().optional(),
11100
+ notifications: z13.object({
11101
+ enabled: z13.boolean().optional(),
11102
+ soundEnabled: z13.boolean().optional(),
10189
11103
  // Legacy field — accepted for backward compat with older clients but
10190
11104
  // no longer surfaced in the UI. The web client now picks the channel
10191
11105
  // automatically based on workspace focus + page visibility.
10192
- onlyWhenBackgrounded: z11.boolean().optional()
11106
+ onlyWhenBackgrounded: z13.boolean().optional()
10193
11107
  }).optional(),
10194
- supervisor: z11.object({
10195
- evaluationTimeoutSec: z11.number().int().min(1).max(MAX_SUPERVISOR_EVALUATION_TIMEOUT_SEC).default(DEFAULT_SUPERVISOR_EVALUATION_TIMEOUT_SEC).optional()
11108
+ supervisor: z13.object({
11109
+ evaluationTimeoutSec: z13.number().int().min(1).max(MAX_SUPERVISOR_EVALUATION_TIMEOUT_SEC).default(DEFAULT_SUPERVISOR_EVALUATION_TIMEOUT_SEC).optional(),
11110
+ retryEnabled: z13.boolean().optional(),
11111
+ retryMaxCount: z13.number().int().min(0).max(MAX_SUPERVISOR_RETRY_MAX_COUNT).optional(),
11112
+ retryDelaySec: z13.number().int().min(1).max(MAX_SUPERVISOR_RETRY_DELAY_SEC).optional(),
11113
+ retryOnTimeout: z13.boolean().optional(),
11114
+ retryOnEvaluatorError: z13.boolean().optional()
10196
11115
  }).optional(),
10197
- appearance: z11.object({
10198
- theme: z11.enum(["dark"]).optional(),
10199
- terminalRenderer: z11.enum(["standard", "compatibility"]).optional(),
10200
- locale: z11.enum(["zh", "en"]).optional()
11116
+ appearance: z13.object({
11117
+ theme: z13.enum(["dark", "light"]).optional(),
11118
+ themeId: z13.string().optional(),
11119
+ terminalRenderer: z13.enum(["standard", "compatibility"]).optional(),
11120
+ terminalCopyOnSelect: z13.boolean().optional(),
11121
+ locale: z13.enum(["zh", "en"]).optional()
10201
11122
  }).optional(),
10202
11123
  providers: ProviderSettingsSchema.optional()
10203
11124
  });
10204
- registerCommand("settings.get", z11.object({}), async (_args, ctx) => {
11125
+ registerCommand("settings.get", z13.object({}), async (_args, ctx) => {
10205
11126
  const row = ctx.db.prepare("SELECT key, value FROM user_settings").all();
10206
11127
  const settings = {};
10207
11128
  for (const { key, value } of row) {
@@ -10230,11 +11151,36 @@ var init_settings2 = __esm({
10230
11151
  settings[SUPERVISOR_EVALUATION_TIMEOUT_SETTING_KEY]
10231
11152
  );
10232
11153
  }
11154
+ if (Object.prototype.hasOwnProperty.call(settings, SUPERVISOR_RETRY_ENABLED_SETTING_KEY)) {
11155
+ settings[SUPERVISOR_RETRY_ENABLED_SETTING_KEY] = resolveSupervisorRetryEnabled(
11156
+ settings[SUPERVISOR_RETRY_ENABLED_SETTING_KEY]
11157
+ );
11158
+ }
11159
+ if (Object.prototype.hasOwnProperty.call(settings, SUPERVISOR_RETRY_MAX_COUNT_SETTING_KEY)) {
11160
+ settings[SUPERVISOR_RETRY_MAX_COUNT_SETTING_KEY] = resolveSupervisorRetryMaxCount(
11161
+ settings[SUPERVISOR_RETRY_MAX_COUNT_SETTING_KEY]
11162
+ );
11163
+ }
11164
+ if (Object.prototype.hasOwnProperty.call(settings, SUPERVISOR_RETRY_DELAY_SEC_SETTING_KEY)) {
11165
+ settings[SUPERVISOR_RETRY_DELAY_SEC_SETTING_KEY] = resolveSupervisorRetryDelaySec(
11166
+ settings[SUPERVISOR_RETRY_DELAY_SEC_SETTING_KEY]
11167
+ );
11168
+ }
11169
+ if (Object.prototype.hasOwnProperty.call(settings, SUPERVISOR_RETRY_ON_TIMEOUT_SETTING_KEY)) {
11170
+ settings[SUPERVISOR_RETRY_ON_TIMEOUT_SETTING_KEY] = resolveSupervisorRetryOnTimeout(
11171
+ settings[SUPERVISOR_RETRY_ON_TIMEOUT_SETTING_KEY]
11172
+ );
11173
+ }
11174
+ if (Object.prototype.hasOwnProperty.call(settings, SUPERVISOR_RETRY_ON_EVALUATOR_ERROR_SETTING_KEY)) {
11175
+ settings[SUPERVISOR_RETRY_ON_EVALUATOR_ERROR_SETTING_KEY] = resolveSupervisorRetryOnEvaluatorError(
11176
+ settings[SUPERVISOR_RETRY_ON_EVALUATOR_ERROR_SETTING_KEY]
11177
+ );
11178
+ }
10233
11179
  return settings;
10234
11180
  });
10235
11181
  registerCommand(
10236
11182
  "settings.update",
10237
- z11.object({
11183
+ z13.object({
10238
11184
  settings: SettingsSchema
10239
11185
  }),
10240
11186
  async (args, ctx) => {
@@ -10266,10 +11212,10 @@ var init_settings2 = __esm({
10266
11212
  );
10267
11213
  registerCommand(
10268
11214
  "settings.previewCommand",
10269
- z11.object({
10270
- providerId: z11.string(),
11215
+ z13.object({
11216
+ providerId: z13.string(),
10271
11217
  config: ProviderLaunchConfigInputSchema,
10272
- workspacePath: z11.string().optional()
11218
+ workspacePath: z13.string().optional()
10273
11219
  }),
10274
11220
  async (args, ctx) => {
10275
11221
  const provider = ctx.providerRegistry.find((item) => item.id === args.providerId);
@@ -10290,8 +11236,8 @@ var init_settings2 = __esm({
10290
11236
  );
10291
11237
  registerCommand(
10292
11238
  "settings.readConfigFile",
10293
- z11.object({
10294
- configType: z11.enum(["codex", "claude"])
11239
+ z13.object({
11240
+ configType: z13.enum(["codex", "claude"])
10295
11241
  }),
10296
11242
  async (args) => {
10297
11243
  const result = readConfigFile(args.configType);
@@ -10300,9 +11246,9 @@ var init_settings2 = __esm({
10300
11246
  );
10301
11247
  registerCommand(
10302
11248
  "settings.writeConfigFile",
10303
- z11.object({
10304
- configType: z11.enum(["codex", "claude"]),
10305
- content: z11.string()
11249
+ z13.object({
11250
+ configType: z13.enum(["codex", "claude"]),
11251
+ content: z13.string()
10306
11252
  }),
10307
11253
  async (args) => {
10308
11254
  const result = writeConfigFile(args.configType, args.content);
@@ -10313,19 +11259,19 @@ var init_settings2 = __esm({
10313
11259
  });
10314
11260
 
10315
11261
  // packages/server/src/commands/provider.ts
10316
- import { z as z12 } from "zod";
11262
+ import { z as z14 } from "zod";
10317
11263
  var init_provider = __esm({
10318
11264
  "packages/server/src/commands/provider.ts"() {
10319
11265
  "use strict";
10320
11266
  init_runtime_status();
10321
11267
  init_dispatch();
10322
- registerCommand("provider.runtimeStatus", z12.object({}), async (_args, ctx) => {
11268
+ registerCommand("provider.runtimeStatus", z14.object({}), async (_args, ctx) => {
10323
11269
  return buildProviderRuntimeStatus(ctx.providerRegistry, ctx.providerRuntimeDeps);
10324
11270
  });
10325
11271
  registerCommand(
10326
11272
  "provider.install.start",
10327
- z12.object({
10328
- providerId: z12.string()
11273
+ z14.object({
11274
+ providerId: z14.string()
10329
11275
  }),
10330
11276
  async (args, ctx) => {
10331
11277
  if (!ctx.providerInstallMgr) {
@@ -10339,8 +11285,8 @@ var init_provider = __esm({
10339
11285
  );
10340
11286
  registerCommand(
10341
11287
  "provider.install.get",
10342
- z12.object({
10343
- jobId: z12.string()
11288
+ z14.object({
11289
+ jobId: z14.string()
10344
11290
  }),
10345
11291
  async (args, ctx) => {
10346
11292
  if (!ctx.providerInstallMgr) {
@@ -10363,36 +11309,45 @@ var init_provider = __esm({
10363
11309
  });
10364
11310
 
10365
11311
  // packages/server/src/commands/supervisor.ts
10366
- import { z as z13 } from "zod";
11312
+ import { z as z15 } from "zod";
10367
11313
  var supervisorObjectiveSchema, createSupervisorSchema, updateSupervisorSchema, sessionIdSchema, supervisorIdSchema;
10368
11314
  var init_supervisor2 = __esm({
10369
11315
  "packages/server/src/commands/supervisor.ts"() {
10370
11316
  "use strict";
10371
11317
  init_dispatch();
10372
- supervisorObjectiveSchema = z13.string().trim().min(1).max(4e3);
10373
- createSupervisorSchema = z13.object({
10374
- sessionId: z13.string(),
10375
- workspaceId: z13.string(),
11318
+ supervisorObjectiveSchema = z15.string().trim().min(1).max(4e3);
11319
+ createSupervisorSchema = z15.object({
11320
+ sessionId: z15.string(),
11321
+ workspaceId: z15.string(),
10376
11322
  objective: supervisorObjectiveSchema,
10377
- evaluatorProviderId: z13.string()
11323
+ evaluatorProviderId: z15.string(),
11324
+ evaluatorModel: z15.string().trim().min(1).max(200).optional(),
11325
+ maxSupervisionCount: z15.number().int().min(0).max(Number.MAX_SAFE_INTEGER).optional(),
11326
+ scheduledAt: z15.number().int().min(0).max(Number.MAX_SAFE_INTEGER).optional()
10378
11327
  }).strict();
10379
- updateSupervisorSchema = z13.object({
10380
- id: z13.string(),
11328
+ updateSupervisorSchema = z15.object({
11329
+ id: z15.string(),
10381
11330
  objective: supervisorObjectiveSchema.optional(),
10382
- evaluatorProviderId: z13.string().optional()
11331
+ evaluatorProviderId: z15.string().optional(),
11332
+ evaluatorModel: z15.string().trim().min(1).max(200).nullable().optional(),
11333
+ maxSupervisionCount: z15.number().int().min(0).max(Number.MAX_SAFE_INTEGER).optional(),
11334
+ scheduledAt: z15.number().int().min(0).max(Number.MAX_SAFE_INTEGER).nullable().optional()
10383
11335
  }).strict().refine(
10384
- (input2) => input2.objective !== void 0 || input2.evaluatorProviderId !== void 0,
10385
- "objective or evaluatorProviderId is required"
11336
+ (input2) => input2.objective !== void 0 || input2.evaluatorProviderId !== void 0 || input2.evaluatorModel !== void 0 || input2.maxSupervisionCount !== void 0 || input2.scheduledAt !== void 0,
11337
+ "at least one supervisor field is required"
10386
11338
  );
10387
- sessionIdSchema = z13.object({ sessionId: z13.string() });
10388
- supervisorIdSchema = z13.object({ id: z13.string() });
11339
+ sessionIdSchema = z15.object({ sessionId: z15.string() });
11340
+ supervisorIdSchema = z15.object({ id: z15.string() });
10389
11341
  registerCommand("supervisor.create", createSupervisorSchema, async (args, ctx) => {
10390
11342
  return {
10391
11343
  supervisor: await ctx.supervisorMgr.create({
10392
11344
  sessionId: args.sessionId,
10393
11345
  workspaceId: args.workspaceId,
10394
11346
  objective: args.objective,
10395
- evaluatorProviderId: args.evaluatorProviderId
11347
+ evaluatorProviderId: args.evaluatorProviderId,
11348
+ evaluatorModel: args.evaluatorModel,
11349
+ maxSupervisionCount: args.maxSupervisionCount,
11350
+ scheduledAt: args.scheduledAt
10396
11351
  })
10397
11352
  };
10398
11353
  });
@@ -10403,7 +11358,10 @@ var init_supervisor2 = __esm({
10403
11358
  return {
10404
11359
  supervisor: await ctx.supervisorMgr.update(args.id, {
10405
11360
  objective: args.objective,
10406
- evaluatorProviderId: args.evaluatorProviderId
11361
+ evaluatorProviderId: args.evaluatorProviderId,
11362
+ evaluatorModel: args.evaluatorModel,
11363
+ maxSupervisionCount: args.maxSupervisionCount,
11364
+ scheduledAt: args.scheduledAt
10407
11365
  })
10408
11366
  };
10409
11367
  });
@@ -10563,7 +11521,7 @@ var init_worktree = __esm({
10563
11521
 
10564
11522
  // packages/server/src/commands/worktree.ts
10565
11523
  import path9 from "node:path";
10566
- import { z as z14 } from "zod";
11524
+ import { z as z16 } from "zod";
10567
11525
  async function findRelatedWorkspaceIds(ctx, workspacePath) {
10568
11526
  const targetCommonDir = await getGitCommonDirPath(workspacePath);
10569
11527
  const relatedWorkspaceIds = await Promise.all(
@@ -10596,7 +11554,7 @@ var init_worktree2 = __esm({
10596
11554
  init_worktree();
10597
11555
  init_dispatch();
10598
11556
  init_git_events();
10599
- registerCommand("worktree.list", z14.object({ workspaceId: z14.string() }), async (args, ctx) => {
11557
+ registerCommand("worktree.list", z16.object({ workspaceId: z16.string() }), async (args, ctx) => {
10600
11558
  const workspace = ctx.workspaceMgr.get(args.workspaceId);
10601
11559
  if (!workspace) {
10602
11560
  throw { code: "workspace_not_found", message: `Workspace not found: ${args.workspaceId}` };
@@ -10605,7 +11563,7 @@ var init_worktree2 = __esm({
10605
11563
  });
10606
11564
  registerCommand(
10607
11565
  "worktree.status",
10608
- z14.object({ workspaceId: z14.string(), worktreePath: z14.string() }),
11566
+ z16.object({ workspaceId: z16.string(), worktreePath: z16.string() }),
10609
11567
  async (args, ctx) => {
10610
11568
  const workspace = ctx.workspaceMgr.get(args.workspaceId);
10611
11569
  if (!workspace) {
@@ -10617,10 +11575,10 @@ var init_worktree2 = __esm({
10617
11575
  );
10618
11576
  registerCommand(
10619
11577
  "worktree.diff",
10620
- z14.object({
10621
- workspaceId: z14.string(),
10622
- worktreePath: z14.string(),
10623
- staged: z14.boolean().optional().default(false)
11578
+ z16.object({
11579
+ workspaceId: z16.string(),
11580
+ worktreePath: z16.string(),
11581
+ staged: z16.boolean().optional().default(false)
10624
11582
  }),
10625
11583
  async (args, ctx) => {
10626
11584
  const workspace = ctx.workspaceMgr.get(args.workspaceId);
@@ -10633,7 +11591,7 @@ var init_worktree2 = __esm({
10633
11591
  );
10634
11592
  registerCommand(
10635
11593
  "worktree.tree",
10636
- z14.object({ workspaceId: z14.string(), worktreePath: z14.string() }),
11594
+ z16.object({ workspaceId: z16.string(), worktreePath: z16.string() }),
10637
11595
  async (args, ctx) => {
10638
11596
  const workspace = ctx.workspaceMgr.get(args.workspaceId);
10639
11597
  if (!workspace) {
@@ -10645,10 +11603,10 @@ var init_worktree2 = __esm({
10645
11603
  );
10646
11604
  registerCommand(
10647
11605
  "worktree.create",
10648
- z14.object({
10649
- workspaceId: z14.string(),
10650
- branch: z14.string(),
10651
- path: z14.string()
11606
+ z16.object({
11607
+ workspaceId: z16.string(),
11608
+ branch: z16.string(),
11609
+ path: z16.string()
10652
11610
  }),
10653
11611
  async (args, ctx) => {
10654
11612
  const workspace = ctx.workspaceMgr.get(args.workspaceId);
@@ -10663,10 +11621,10 @@ var init_worktree2 = __esm({
10663
11621
  );
10664
11622
  registerCommand(
10665
11623
  "worktree.remove",
10666
- z14.object({
10667
- workspaceId: z14.string(),
10668
- worktreePath: z14.string(),
10669
- force: z14.boolean().optional().default(false)
11624
+ z16.object({
11625
+ workspaceId: z16.string(),
11626
+ worktreePath: z16.string(),
11627
+ force: z16.boolean().optional().default(false)
10670
11628
  }),
10671
11629
  async (args, ctx) => {
10672
11630
  const workspace = ctx.workspaceMgr.get(args.workspaceId);
@@ -10690,7 +11648,7 @@ var init_worktree2 = __esm({
10690
11648
  });
10691
11649
 
10692
11650
  // packages/server/src/commands/fencing.ts
10693
- import { z as z15 } from "zod";
11651
+ import { z as z17 } from "zod";
10694
11652
  function createMockFencingRequest() {
10695
11653
  return {
10696
11654
  ip: "127.0.0.1",
@@ -10703,9 +11661,9 @@ var init_fencing2 = __esm({
10703
11661
  init_dispatch();
10704
11662
  registerCommand(
10705
11663
  "fencing.request",
10706
- z15.object({
10707
- workspaceId: z15.string(),
10708
- tabId: z15.string()
11664
+ z17.object({
11665
+ workspaceId: z17.string(),
11666
+ tabId: z17.string()
10709
11667
  }),
10710
11668
  async (args, ctx, clientId) => {
10711
11669
  return ctx.fencingMgr.requestControl(
@@ -10718,7 +11676,7 @@ var init_fencing2 = __esm({
10718
11676
  );
10719
11677
  registerCommand(
10720
11678
  "fencing.heartbeat",
10721
- z15.object({ workspaceId: z15.string() }),
11679
+ z17.object({ workspaceId: z17.string() }),
10722
11680
  async (args, ctx, clientId) => {
10723
11681
  const success = ctx.fencingMgr.heartbeat(args.workspaceId, clientId);
10724
11682
  return { success };
@@ -10726,13 +11684,13 @@ var init_fencing2 = __esm({
10726
11684
  );
10727
11685
  registerCommand(
10728
11686
  "fencing.release",
10729
- z15.object({ workspaceId: z15.string() }),
11687
+ z17.object({ workspaceId: z17.string() }),
10730
11688
  async (args, ctx, clientId) => {
10731
11689
  ctx.fencingMgr.release(args.workspaceId, clientId);
10732
11690
  return {};
10733
11691
  }
10734
11692
  );
10735
- registerCommand("fencing.status", z15.object({ workspaceId: z15.string() }), async (args, ctx) => {
11693
+ registerCommand("fencing.status", z17.object({ workspaceId: z17.string() }), async (args, ctx) => {
10736
11694
  const controller = ctx.fencingMgr.getController(args.workspaceId);
10737
11695
  const isUnresponsive = ctx.fencingMgr.isControllerUnresponsive(args.workspaceId);
10738
11696
  return {
@@ -10743,9 +11701,9 @@ var init_fencing2 = __esm({
10743
11701
  });
10744
11702
  registerCommand(
10745
11703
  "fencing.takeover",
10746
- z15.object({
10747
- workspaceId: z15.string(),
10748
- tabId: z15.string()
11704
+ z17.object({
11705
+ workspaceId: z17.string(),
11706
+ tabId: z17.string()
10749
11707
  }),
10750
11708
  async (args, ctx, clientId) => {
10751
11709
  return ctx.fencingMgr.forceTakeover(
@@ -10765,6 +11723,8 @@ var init_commands = __esm({
10765
11723
  "use strict";
10766
11724
  init_workspace();
10767
11725
  init_workspace_activity();
11726
+ init_activation2();
11727
+ init_connection();
10768
11728
  init_session();
10769
11729
  init_terminal();
10770
11730
  init_file();
@@ -10783,6 +11743,7 @@ async function createServer(configOverrides) {
10783
11743
  ensureDataDir(config);
10784
11744
  const db = openDatabase(config.dataDir);
10785
11745
  const eventBus = new EventBus();
11746
+ const activationMgr = new ActivationManager();
10786
11747
  const fencingMgr = new FencingManager();
10787
11748
  const wsHub = new WsHub({ eventBus, commandContext: null, config, fencingMgr });
10788
11749
  let workspaceMgr;
@@ -10873,6 +11834,7 @@ async function createServer(configOverrides) {
10873
11834
  wsHub.setLogger(app.log);
10874
11835
  const supervisorRepo = new SupervisorRepo(db);
10875
11836
  const cycleRepo = new SupervisorCycleRepo(db);
11837
+ const cycleAttemptRepo = new SupervisorCycleAttemptRepo(db);
10876
11838
  supervisorMgr = new SupervisorManager({
10877
11839
  eventBus,
10878
11840
  broadcaster: wsHub,
@@ -10884,6 +11846,7 @@ async function createServer(configOverrides) {
10884
11846
  settingsRepo,
10885
11847
  supervisorRepo,
10886
11848
  cycleRepo,
11849
+ cycleAttemptRepo,
10887
11850
  logger: app.log
10888
11851
  });
10889
11852
  await sessionMgr.hydrate();
@@ -10908,7 +11871,8 @@ async function createServer(configOverrides) {
10908
11871
  supervisorMgr,
10909
11872
  autoFetch,
10910
11873
  providerRuntimeDeps,
10911
- providerInstallMgr
11874
+ providerInstallMgr,
11875
+ activationMgr
10912
11876
  };
10913
11877
  wsHub.setCommandContext(commandContext);
10914
11878
  await app.listen({
@@ -10933,11 +11897,16 @@ async function createServer(configOverrides) {
10933
11897
  );
10934
11898
  }, STARTUP_GC_DELAY_MS);
10935
11899
  gcTimer.unref();
11900
+ const wsKeepaliveTimer = setInterval(() => {
11901
+ wsHub.pingAll();
11902
+ }, WS_KEEPALIVE_INTERVAL_MS);
11903
+ wsKeepaliveTimer.unref();
10936
11904
  let stopped = false;
10937
11905
  const stopServer = async () => {
10938
11906
  if (stopped) return;
10939
11907
  stopped = true;
10940
11908
  clearTimeout(gcTimer);
11909
+ clearInterval(wsKeepaliveTimer);
10941
11910
  await app.close();
10942
11911
  autoFetch.stop();
10943
11912
  supervisorMgr.stop();
@@ -11053,6 +12022,7 @@ function createSessionDatabase(db) {
11053
12022
  }
11054
12023
  };
11055
12024
  }
12025
+ var WS_KEEPALIVE_INTERVAL_MS;
11056
12026
  var init_server = __esm({
11057
12027
  async "packages/server/src/server.ts"() {
11058
12028
  "use strict";
@@ -11073,6 +12043,7 @@ var init_server = __esm({
11073
12043
  init_provider_config_repo();
11074
12044
  init_session_repo();
11075
12045
  init_settings_repo();
12046
+ init_supervisor_cycle_attempt_repo();
11076
12047
  init_supervisor_cycle_repo();
11077
12048
  init_supervisor_repo();
11078
12049
  init_manager2();
@@ -11081,10 +12052,12 @@ var init_server = __esm({
11081
12052
  init_cleanup();
11082
12053
  init_constants();
11083
12054
  init_manager4();
12055
+ init_activation();
11084
12056
  init_dispatch();
11085
12057
  init_fencing();
11086
12058
  init_hub();
11087
12059
  init_commands();
12060
+ WS_KEEPALIVE_INTERVAL_MS = 15e3;
11088
12061
  if (isDirectExecution(import.meta.url)) {
11089
12062
  const server = await createServer();
11090
12063
  process.on("SIGINT", async () => {
@@ -11307,6 +12280,7 @@ var init_storage = __esm({
11307
12280
  init_provider_config_repo();
11308
12281
  init_session_repo();
11309
12282
  init_settings_repo();
12283
+ init_supervisor_cycle_attempt_repo();
11310
12284
  init_supervisor_cycle_repo();
11311
12285
  init_supervisor_repo();
11312
12286
  init_terminal_repo();
@@ -11337,6 +12311,7 @@ __export(src_exports, {
11337
12311
  RingBuffer: () => RingBuffer,
11338
12312
  SessionRepo: () => SessionRepo,
11339
12313
  SettingsRepo: () => SettingsRepo,
12314
+ SupervisorCycleAttemptRepo: () => SupervisorCycleAttemptRepo,
11340
12315
  SupervisorCycleRepo: () => SupervisorCycleRepo,
11341
12316
  SupervisorRepo: () => SupervisorRepo,
11342
12317
  TerminalManager: () => TerminalManager,
@@ -11371,8 +12346,8 @@ var init_src4 = __esm({
11371
12346
  });
11372
12347
 
11373
12348
  // packages/cli/src/cli.ts
11374
- import { existsSync as existsSync11 } from "fs";
11375
- import { dirname as dirname6, join as join10 } from "path";
12349
+ import { existsSync as existsSync11, rmSync } from "fs";
12350
+ import { dirname as dirname7, join as join10 } from "path";
11376
12351
  import { fileURLToPath as fileURLToPath3 } from "url";
11377
12352
 
11378
12353
  // packages/cli/src/auth-control.ts
@@ -12187,6 +13162,9 @@ async function getServerStatus() {
12187
13162
  }
12188
13163
 
12189
13164
  // packages/cli/src/server-runner.ts
13165
+ await init_src4();
13166
+ import { mkdirSync as mkdirSync6 } from "fs";
13167
+ import { dirname as dirname6 } from "path";
12190
13168
  import { fileURLToPath as fileURLToPath2 } from "url";
12191
13169
 
12192
13170
  // packages/cli/src/embed.ts
@@ -12228,6 +13206,14 @@ var buildServerConfig = () => {
12228
13206
  console.warn(MISSING_WEB_ASSETS_WARNING);
12229
13207
  return config;
12230
13208
  };
13209
+ var verifyLocalDatabaseCompatibility = () => {
13210
+ const config = parseServerConfig(buildServerConfig());
13211
+ if (config.dataDir !== ":memory:") {
13212
+ mkdirSync6(dirname6(config.dataDir), { recursive: true });
13213
+ }
13214
+ const db = openDatabase(config.dataDir);
13215
+ closeDatabase(db);
13216
+ };
12231
13217
  var createShutdownHandler = (server) => async () => {
12232
13218
  await server.stop();
12233
13219
  process.exit(0);
@@ -12402,7 +13388,7 @@ function formatAuthBlocks(blocks) {
12402
13388
  }
12403
13389
  function resolveManagedScriptPath() {
12404
13390
  const currentFile = fileURLToPath3(import.meta.url);
12405
- const currentDir = dirname6(currentFile);
13391
+ const currentDir = dirname7(currentFile);
12406
13392
  const candidates = [
12407
13393
  join10(currentDir, "server-runner.js"),
12408
13394
  join10(currentDir, "server-runner.mjs"),
@@ -12460,6 +13446,44 @@ async function startManagedServerFlow() {
12460
13446
  waitMs: MANAGED_SERVER_WAIT_MS
12461
13447
  });
12462
13448
  }
13449
+ function parseIncompatibleSchemaError(error) {
13450
+ if (!error || typeof error !== "object") {
13451
+ return null;
13452
+ }
13453
+ const candidate = error;
13454
+ if (candidate.code !== "db_incompatible_schema" || typeof candidate.dbPath !== "string") {
13455
+ return null;
13456
+ }
13457
+ return {
13458
+ code: "db_incompatible_schema",
13459
+ dbPath: candidate.dbPath
13460
+ };
13461
+ }
13462
+ async function handleIncompatibleSchema(error) {
13463
+ const payload = parseIncompatibleSchemaError(error);
13464
+ if (!payload) {
13465
+ return false;
13466
+ }
13467
+ const approved = isInteractiveSession() ? await confirmYesNo(
13468
+ `Local database is incompatible at ${payload.dbPath}. Delete and rebuild the local database? [y/N] `
13469
+ ) : false;
13470
+ if (!approved) {
13471
+ throw error;
13472
+ }
13473
+ rmSync(payload.dbPath, { force: true });
13474
+ return true;
13475
+ }
13476
+ async function verifyManagedDatabaseCompatibility() {
13477
+ try {
13478
+ verifyLocalDatabaseCompatibility();
13479
+ } catch (error) {
13480
+ const rebuilt = await handleIncompatibleSchema(error);
13481
+ if (!rebuilt) {
13482
+ throw error;
13483
+ }
13484
+ verifyLocalDatabaseCompatibility();
13485
+ }
13486
+ }
12463
13487
  async function openManagedServerInBrowser(existingStatus) {
12464
13488
  const status = existingStatus ?? await getServerStatus();
12465
13489
  const browserUrl = getBrowserUrl(status);
@@ -12531,6 +13555,7 @@ async function main(argv = process.argv.slice(2)) {
12531
13555
  if (args.command === "open") {
12532
13556
  const startup2 = await prepareManagedStartup(args.restart);
12533
13557
  if (startup2.existingStatus === null) {
13558
+ await verifyManagedDatabaseCompatibility();
12534
13559
  await startManagedServerFlow();
12535
13560
  }
12536
13561
  await openManagedServerInBrowser(startup2.existingStatus);
@@ -12545,13 +13570,22 @@ async function main(argv = process.argv.slice(2)) {
12545
13570
  await stopRunningServer();
12546
13571
  }
12547
13572
  console.log("Starting Coder Studio Server in foreground...");
12548
- await startServer();
13573
+ try {
13574
+ await startServer();
13575
+ } catch (error) {
13576
+ const rebuilt = await handleIncompatibleSchema(error);
13577
+ if (!rebuilt) {
13578
+ throw error;
13579
+ }
13580
+ await startServer();
13581
+ }
12549
13582
  return;
12550
13583
  }
12551
13584
  const startup = await prepareManagedStartup(args.restart);
12552
13585
  if (startup.existingStatus !== null) {
12553
13586
  return;
12554
13587
  }
13588
+ await verifyManagedDatabaseCompatibility();
12555
13589
  await startManagedServerFlow();
12556
13590
  console.log("Coder Studio server started in background.");
12557
13591
  console.log("Run `coder-studio status` to inspect the server.");