lody 0.86.2 → 0.88.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -4325,7 +4325,7 @@ Upgrade Node, then re-run: npx lody@latest`;
4325
4325
  }
4326
4326
  const name$2 = "lody";
4327
4327
  const name$1 = "@lody/cli-cloud";
4328
- const version$9 = "0.86.2";
4328
+ const version$9 = "0.88.0";
4329
4329
  const type$2 = "module";
4330
4330
  const scripts = {
4331
4331
  "dev": "node dev.mjs",
@@ -4664,7 +4664,7 @@ Upgrade Node, then re-run: npx lody@latest`;
4664
4664
  return "none";
4665
4665
  }
4666
4666
  const MODEL_THOUGHT_LEVEL_META_KEY = "lodyThoughtLevel";
4667
- const ACP_CAPABILITY_CACHE_VERSION = 5;
4667
+ const ACP_CAPABILITY_CACHE_VERSION = 6;
4668
4668
  const getAcpCapabilityCacheKey = (configId) => configId;
4669
4669
  const isBuiltinAgentType = (agentType) => BUILTIN_AGENTS.some((agent2) => agent2.agentType === agentType);
4670
4670
  const isManagedBuiltinAgentType = (agentType) => MANAGED_BUILTIN_RUNTIMES.some((runtime) => runtime.agentType === agentType);
@@ -5202,6 +5202,14 @@ Requirements:
5202
5202
  const SESSION_FILE_MAX_SIZE_BYTES = 100 * 1024 * 1024;
5203
5203
  const SESSION_FILE_MAX_COUNT = 8;
5204
5204
  const SESSION_FILE_PREVIEW_SNIFF_BYTES = 8 * 1024;
5205
+ function isSessionFileSourcePath(value) {
5206
+ if (value.length === 0 || value.includes("\0")) return false;
5207
+ const normalized = value.replace(/\\/g, "/");
5208
+ if (normalized === "." || normalized.startsWith("/") || /^[A-Za-z]:/.test(normalized)) {
5209
+ return false;
5210
+ }
5211
+ return !normalized.split("/").some((segment) => segment === "..");
5212
+ }
5205
5213
  const MESSAGE_TEXT_SPAN_KINDS = [
5206
5214
  "file",
5207
5215
  "dir",
@@ -6147,7 +6155,9 @@ Requirements:
6147
6155
  path2.add(value);
6148
6156
  }
6149
6157
  const keys2 = Object.keys(value);
6150
- const strippedValues = new Array(keys2.length);
6158
+ const strippedValues = Array.from({
6159
+ length: keys2.length
6160
+ });
6151
6161
  let changed = false;
6152
6162
  for (let i2 = 0; i2 < keys2.length; i2++) {
6153
6163
  const key2 = keys2[i2];
@@ -6451,7 +6461,7 @@ Requirements:
6451
6461
  function isContainerSchema(schema2) {
6452
6462
  return !!schema2 && (schema2.type === "loro-map" || schema2.type === "loro-list" || schema2.type === "loro-text" || schema2.type === "loro-movable-list" || schema2.type === "loro-tree");
6453
6463
  }
6454
- function validateSchema(schema2, value) {
6464
+ function validateSchema(schema2, value, options) {
6455
6465
  const errors2 = [];
6456
6466
  const actualType = schema2.type;
6457
6467
  if (schema2.options.required && (value === void 0 || value === null)) {
@@ -6466,7 +6476,8 @@ Requirements:
6466
6476
  valid: true
6467
6477
  };
6468
6478
  }
6469
- if (isSchemaValidated(schema2, value)) {
6479
+ const useCache = !options?.ignoreUnknownProperties;
6480
+ if (useCache && isSchemaValidated(schema2, value)) {
6470
6481
  return {
6471
6482
  valid: true
6472
6483
  };
@@ -6579,6 +6590,7 @@ Requirements:
6579
6590
  }
6580
6591
  for (const key2 in value) {
6581
6592
  if (!Object.prototype.hasOwnProperty.call(schema2.definition, key2)) {
6593
+ if (options?.ignoreUnknownProperties) continue;
6582
6594
  errors2.push(`Unknown property: ${key2}`);
6583
6595
  }
6584
6596
  }
@@ -6602,7 +6614,7 @@ Requirements:
6602
6614
  }
6603
6615
  }
6604
6616
  if (errors2.length === 0) {
6605
- markSchemaValidated(schema2, value);
6617
+ if (useCache) markSchemaValidated(schema2, value);
6606
6618
  return {
6607
6619
  valid: true
6608
6620
  };
@@ -9307,6 +9319,7 @@ Requirements:
9307
9319
  schema: options.schema,
9308
9320
  initialState: options.initialState || {},
9309
9321
  validateUpdates: options.validateUpdates !== false,
9322
+ ignoreUnknownProperties: options.ignoreUnknownProperties || false,
9310
9323
  debug: options.debug || false,
9311
9324
  checkStateConsistency: options.checkStateConsistency || false,
9312
9325
  inferOptions: options.inferOptions || {}
@@ -10465,7 +10478,9 @@ Requirements:
10465
10478
  }) : Object.assign({}, this.state, updater);
10466
10479
  hardenCidDescriptors(newState, this.state);
10467
10480
  if (this.options.validateUpdates) {
10468
- const validation2 = this.schema && validateSchema(this.schema, newState);
10481
+ const validation2 = this.schema && validateSchema(this.schema, newState, {
10482
+ ignoreUnknownProperties: this.options.ignoreUnknownProperties
10483
+ });
10469
10484
  if (validation2 && !validation2.valid) {
10470
10485
  const errorMessage2 = `State validation failed: ${validation2.errors?.join(", ")}`;
10471
10486
  throw new Error(errorMessage2);
@@ -10643,6 +10658,15 @@ Requirements:
10643
10658
  root[key2] = this.containerToMirrorState(container2, options);
10644
10659
  }
10645
10660
  }
10661
+ if (this.options.ignoreUnknownProperties) {
10662
+ const fullDocState = toNormalizedJson(this.doc);
10663
+ for (const key2 of Object.keys(fullDocState)) {
10664
+ if (Object.prototype.hasOwnProperty.call(rootSchema.definition, key2)) {
10665
+ continue;
10666
+ }
10667
+ root[key2] = fullDocState[key2];
10668
+ }
10669
+ }
10646
10670
  return root;
10647
10671
  }
10648
10672
  registerContainerWithRegistry(containerId, schemaType) {
@@ -11857,6 +11881,7 @@ Requirements:
11857
11881
  sizeBytes: number$4().int().nonnegative().max(SESSION_FILE_MAX_SIZE_BYTES),
11858
11882
  sha256: string$1(),
11859
11883
  textPreview: boolean(),
11884
+ sourcePath: string$1().refine(isSessionFileSourcePath).optional(),
11860
11885
  transport: _enum$1([
11861
11886
  "r2",
11862
11887
  "local"
@@ -13238,6 +13263,13 @@ Requirements:
13238
13263
  localProjectId: LocalProjectIdSchema,
13239
13264
  requestedByUserId: string$1().trim().min(1).optional()
13240
13265
  }).strict();
13266
+ const LocalProjectRemovalPreflightRequestSchema = object$1({
13267
+ type: literal$1("local-project/removal-preflight"),
13268
+ machineId: MachineIdSchema$1,
13269
+ workspaceId: WorkspaceIdSchema,
13270
+ localProjectId: LocalProjectIdSchema,
13271
+ requestedByUserId: string$1().trim().min(1).optional()
13272
+ }).strict();
13241
13273
  const LocalProjectListRequestSchema = object$1({
13242
13274
  type: literal$1("local-project/list"),
13243
13275
  machineId: MachineIdSchema$1
@@ -13377,6 +13409,7 @@ Requirements:
13377
13409
  LocalProjectListRootsRequestSchema,
13378
13410
  LocalProjectBrowseDirRequestSchema,
13379
13411
  LocalProjectDeleteRequestSchema,
13412
+ LocalProjectRemovalPreflightRequestSchema,
13380
13413
  LocalProjectListRequestSchema,
13381
13414
  LocalProjectGitStateRequestSchema,
13382
13415
  LocalProjectListFilesRequestSchema,
@@ -13543,6 +13576,19 @@ Requirements:
13543
13576
  status: literal$1("resolved"),
13544
13577
  catalog: LocalProjectHistoryCatalogResultSchema
13545
13578
  }).strict();
13579
+ const LocalProjectWorktreeCleanupItemSchema = object$1({
13580
+ sessionId: SessionIdSchema$1,
13581
+ title: string$1(),
13582
+ path: string$1()
13583
+ }).strict();
13584
+ const LocalProjectWorktreeCleanupFailureSchema = LocalProjectWorktreeCleanupItemSchema.extend({
13585
+ message: string$1()
13586
+ }).strict();
13587
+ const LocalProjectWorktreeCleanupPreflightResultSchema = object$1({
13588
+ clean: array$2(LocalProjectWorktreeCleanupItemSchema),
13589
+ dirty: array$2(LocalProjectWorktreeCleanupItemSchema),
13590
+ failed: array$2(LocalProjectWorktreeCleanupFailureSchema)
13591
+ }).strict();
13546
13592
  const LocalProjectControlErrorCodeSchema = _enum$1([
13547
13593
  "invalid_request",
13548
13594
  "machine_mismatch",
@@ -13563,6 +13609,7 @@ Requirements:
13563
13609
  "local-project/list-roots",
13564
13610
  "local-project/browse-dir",
13565
13611
  "local-project/delete",
13612
+ "local-project/removal-preflight",
13566
13613
  "local-project/list",
13567
13614
  "local-project/git-state",
13568
13615
  "local-project/list-files",
@@ -13626,6 +13673,11 @@ Requirements:
13626
13673
  workspaceIds: array$2(WorkspaceIdSchema)
13627
13674
  }).strict()
13628
13675
  }).strict(),
13676
+ object$1({
13677
+ ok: literal$1(true),
13678
+ type: literal$1("local-project/removal-preflight"),
13679
+ result: LocalProjectWorktreeCleanupPreflightResultSchema
13680
+ }).strict(),
13629
13681
  object$1({
13630
13682
  ok: literal$1(true),
13631
13683
  type: literal$1("local-project/list"),
@@ -14522,12 +14574,12 @@ Requirements:
14522
14574
  {
14523
14575
  id: "auggie",
14524
14576
  name: "Auggie CLI",
14525
- version: "0.35.0",
14577
+ version: "0.36.0",
14526
14578
  description: "Augment Code's powerful software agent, backed by industry-leading context engine",
14527
14579
  icon: "https://cdn.agentclientprotocol.com/registry/v1/latest/auggie.svg",
14528
14580
  distribution: {
14529
14581
  npx: {
14530
- package: "@augmentcode/auggie@0.35.0",
14582
+ package: "@augmentcode/auggie@0.36.0",
14531
14583
  args: [
14532
14584
  "--acp"
14533
14585
  ],
@@ -14552,12 +14604,12 @@ Requirements:
14552
14604
  {
14553
14605
  id: "cline",
14554
14606
  name: "Cline",
14555
- version: "3.0.55",
14607
+ version: "3.0.60",
14556
14608
  description: "Autonomous coding agent CLI - capable of creating/editing files, running commands, using the browser, and more",
14557
14609
  icon: "https://cdn.agentclientprotocol.com/registry/v1/latest/cline.svg",
14558
14610
  distribution: {
14559
14611
  npx: {
14560
- package: "cline@3.0.55",
14612
+ package: "cline@3.0.60",
14561
14613
  args: [
14562
14614
  "--acp"
14563
14615
  ]
@@ -14567,12 +14619,12 @@ Requirements:
14567
14619
  {
14568
14620
  id: "codebuddy-code",
14569
14621
  name: "Codebuddy Code",
14570
- version: "2.137.1",
14622
+ version: "2.140.0",
14571
14623
  description: "Tencent Cloud's official intelligent coding tool",
14572
14624
  icon: "https://cdn.agentclientprotocol.com/registry/v1/latest/codebuddy-code.svg",
14573
14625
  distribution: {
14574
14626
  npx: {
14575
- package: "@tencent-ai/codebuddy-code@2.137.1",
14627
+ package: "@tencent-ai/codebuddy-code@2.140.0",
14576
14628
  args: [
14577
14629
  "--acp"
14578
14630
  ]
@@ -14744,48 +14796,48 @@ Requirements:
14744
14796
  {
14745
14797
  id: "devin",
14746
14798
  name: "Devin",
14747
- version: "3000.4.25",
14799
+ version: "3000.6.2",
14748
14800
  description: "Devin CLI coding agent by Cognition",
14749
14801
  icon: "https://cdn.agentclientprotocol.com/registry/v1/latest/devin.svg",
14750
14802
  distribution: {
14751
14803
  binary: {
14752
14804
  "darwin-aarch64": {
14753
- archive: "https://static.devin.ai/cli/3000.4.25/devin-3000.4.25-aarch64-apple-darwin.tar.gz",
14805
+ archive: "https://static.devin.ai/cli/3000.6.2/devin-3000.6.2-aarch64-apple-darwin.tar.gz",
14754
14806
  cmd: "./bin/devin",
14755
14807
  args: [
14756
14808
  "acp"
14757
14809
  ]
14758
14810
  },
14759
14811
  "darwin-x86_64": {
14760
- archive: "https://static.devin.ai/cli/3000.4.25/devin-3000.4.25-x86_64-apple-darwin.tar.gz",
14812
+ archive: "https://static.devin.ai/cli/3000.6.2/devin-3000.6.2-x86_64-apple-darwin.tar.gz",
14761
14813
  cmd: "./bin/devin",
14762
14814
  args: [
14763
14815
  "acp"
14764
14816
  ]
14765
14817
  },
14766
14818
  "linux-aarch64": {
14767
- archive: "https://static.devin.ai/cli/3000.4.25/devin-3000.4.25-aarch64-unknown-linux.tar.gz",
14819
+ archive: "https://static.devin.ai/cli/3000.6.2/devin-3000.6.2-aarch64-unknown-linux.tar.gz",
14768
14820
  cmd: "./bin/devin",
14769
14821
  args: [
14770
14822
  "acp"
14771
14823
  ]
14772
14824
  },
14773
14825
  "linux-x86_64": {
14774
- archive: "https://static.devin.ai/cli/3000.4.25/devin-3000.4.25-x86_64-unknown-linux.tar.gz",
14826
+ archive: "https://static.devin.ai/cli/3000.6.2/devin-3000.6.2-x86_64-unknown-linux.tar.gz",
14775
14827
  cmd: "./bin/devin",
14776
14828
  args: [
14777
14829
  "acp"
14778
14830
  ]
14779
14831
  },
14780
14832
  "windows-aarch64": {
14781
- archive: "https://static.devin.ai/cli/3000.4.25/devin-3000.4.25-aarch64-pc-windows.zip",
14833
+ archive: "https://static.devin.ai/cli/3000.6.2/devin-3000.6.2-aarch64-pc-windows.zip",
14782
14834
  cmd: "./bin\\devin.exe",
14783
14835
  args: [
14784
14836
  "acp"
14785
14837
  ]
14786
14838
  },
14787
14839
  "windows-x86_64": {
14788
- archive: "https://static.devin.ai/cli/3000.4.25/devin-3000.4.25-x86_64-pc-windows.zip",
14840
+ archive: "https://static.devin.ai/cli/3000.6.2/devin-3000.6.2-x86_64-pc-windows.zip",
14789
14841
  cmd: "./bin\\devin.exe",
14790
14842
  args: [
14791
14843
  "acp"
@@ -14797,12 +14849,12 @@ Requirements:
14797
14849
  {
14798
14850
  id: "dimcode",
14799
14851
  name: "DimCode",
14800
- version: "0.3.17",
14852
+ version: "0.3.21",
14801
14853
  description: "A coding agent that puts leading models at your command.",
14802
14854
  icon: "https://cdn.agentclientprotocol.com/registry/v1/latest/dimcode.svg",
14803
14855
  distribution: {
14804
14856
  npx: {
14805
- package: "dimcode@0.3.17",
14857
+ package: "dimcode@0.3.21",
14806
14858
  args: [
14807
14859
  "acp"
14808
14860
  ]
@@ -14812,12 +14864,12 @@ Requirements:
14812
14864
  {
14813
14865
  id: "dirac",
14814
14866
  name: "Dirac",
14815
- version: "0.4.37",
14867
+ version: "0.5.1",
14816
14868
  description: "Reduces API costs by more than 50%, produces better and faster work. Uses Hash anchored parallel edits, AST manipulation and a whole lot of neat optimizations. Fully Open Source.",
14817
14869
  icon: "https://cdn.agentclientprotocol.com/registry/v1/latest/dirac.svg",
14818
14870
  distribution: {
14819
14871
  npx: {
14820
- package: "dirac-cli@0.4.37",
14872
+ package: "dirac-cli@0.5.1",
14821
14873
  args: [
14822
14874
  "--acp"
14823
14875
  ]
@@ -14827,12 +14879,12 @@ Requirements:
14827
14879
  {
14828
14880
  id: "factory-droid",
14829
14881
  name: "Factory Droid",
14830
- version: "0.200.0",
14882
+ version: "0.205.1",
14831
14883
  description: "Factory Droid - AI coding agent powered by Factory AI",
14832
14884
  icon: "https://cdn.agentclientprotocol.com/registry/v1/latest/factory-droid.svg",
14833
14885
  distribution: {
14834
14886
  npx: {
14835
- package: "droid@0.200.0",
14887
+ package: "droid@0.205.1",
14836
14888
  args: [
14837
14889
  "exec",
14838
14890
  "--output-format",
@@ -14866,12 +14918,12 @@ Requirements:
14866
14918
  {
14867
14919
  id: "gemini",
14868
14920
  name: "Gemini CLI",
14869
- version: "0.56.0",
14921
+ version: "0.57.0",
14870
14922
  description: "Google's official CLI for Gemini",
14871
14923
  icon: "https://cdn.agentclientprotocol.com/registry/v1/latest/gemini.svg",
14872
14924
  distribution: {
14873
14925
  npx: {
14874
- package: "@google/gemini-cli@0.56.0",
14926
+ package: "@google/gemini-cli@0.57.0",
14875
14927
  args: [
14876
14928
  "--acp"
14877
14929
  ]
@@ -14881,12 +14933,12 @@ Requirements:
14881
14933
  {
14882
14934
  id: "github-copilot-cli",
14883
14935
  name: "GitHub Copilot",
14884
- version: "1.0.80",
14936
+ version: "1.0.81",
14885
14937
  description: "GitHub's AI pair programmer",
14886
14938
  icon: "https://cdn.agentclientprotocol.com/registry/v1/latest/github-copilot-cli.svg",
14887
14939
  distribution: {
14888
14940
  npx: {
14889
- package: "@github/copilot@1.0.80",
14941
+ package: "@github/copilot@1.0.81",
14890
14942
  args: [
14891
14943
  "--acp"
14892
14944
  ]
@@ -14896,19 +14948,19 @@ Requirements:
14896
14948
  {
14897
14949
  id: "glm-acp-agent",
14898
14950
  name: "GLM Agent",
14899
- version: "1.6.0",
14951
+ version: "1.6.1",
14900
14952
  description: "ACP agent powered by Zhipu AI's GLM Coding Plan models (glm-5.1, glm-5-turbo, glm-4.7, glm-4.5-air). Supports streaming, tool calls, mid-session model switching, image input via Z.AI Coding Plan Vision MCP, and session load/fork/resume with on-disk persistence.",
14901
14953
  icon: "https://cdn.agentclientprotocol.com/registry/v1/latest/glm-acp-agent.svg",
14902
14954
  distribution: {
14903
14955
  npx: {
14904
- package: "glm-acp-agent@1.6.0"
14956
+ package: "glm-acp-agent@1.6.1"
14905
14957
  }
14906
14958
  }
14907
14959
  },
14908
14960
  {
14909
14961
  id: "goose",
14910
14962
  name: "goose",
14911
- version: "1.46.0",
14963
+ version: "1.48.0",
14912
14964
  description: "A local, extensible, open source AI agent that automates engineering tasks",
14913
14965
  icon: "https://cdn.agentclientprotocol.com/registry/v1/latest/goose.svg",
14914
14966
  distribution: {
@@ -14926,13 +14978,13 @@ Requirements:
14926
14978
  {
14927
14979
  id: "harn",
14928
14980
  name: "Harn",
14929
- version: "0.10.108",
14981
+ version: "0.10.118",
14930
14982
  description: "Harn runs .harn agent pipelines as a native ACP coding agent over stdio.",
14931
14983
  icon: "https://cdn.agentclientprotocol.com/registry/v1/latest/harn.svg",
14932
14984
  distribution: {
14933
14985
  binary: {
14934
14986
  "darwin-aarch64": {
14935
- archive: "https://github.com/burin-labs/harn/releases/download/v0.10.108/harn-aarch64-apple-darwin.tar.gz",
14987
+ archive: "https://github.com/burin-labs/harn/releases/download/v0.10.118/harn-aarch64-apple-darwin.tar.gz",
14936
14988
  cmd: "./harn",
14937
14989
  args: [
14938
14990
  "serve",
@@ -14940,7 +14992,7 @@ Requirements:
14940
14992
  ]
14941
14993
  },
14942
14994
  "darwin-x86_64": {
14943
- archive: "https://github.com/burin-labs/harn/releases/download/v0.10.108/harn-x86_64-apple-darwin.tar.gz",
14995
+ archive: "https://github.com/burin-labs/harn/releases/download/v0.10.118/harn-x86_64-apple-darwin.tar.gz",
14944
14996
  cmd: "./harn",
14945
14997
  args: [
14946
14998
  "serve",
@@ -14948,7 +15000,7 @@ Requirements:
14948
15000
  ]
14949
15001
  },
14950
15002
  "linux-aarch64": {
14951
- archive: "https://github.com/burin-labs/harn/releases/download/v0.10.108/harn-aarch64-unknown-linux-gnu.tar.gz",
15003
+ archive: "https://github.com/burin-labs/harn/releases/download/v0.10.118/harn-aarch64-unknown-linux-gnu.tar.gz",
14952
15004
  cmd: "./harn",
14953
15005
  args: [
14954
15006
  "serve",
@@ -14956,7 +15008,7 @@ Requirements:
14956
15008
  ]
14957
15009
  },
14958
15010
  "linux-x86_64": {
14959
- archive: "https://github.com/burin-labs/harn/releases/download/v0.10.108/harn-x86_64-unknown-linux-gnu.tar.gz",
15011
+ archive: "https://github.com/burin-labs/harn/releases/download/v0.10.118/harn-x86_64-unknown-linux-gnu.tar.gz",
14960
15012
  cmd: "./harn",
14961
15013
  args: [
14962
15014
  "serve",
@@ -14964,7 +15016,7 @@ Requirements:
14964
15016
  ]
14965
15017
  },
14966
15018
  "windows-x86_64": {
14967
- archive: "https://github.com/burin-labs/harn/releases/download/v0.10.108/harn-x86_64-pc-windows-msvc.zip",
15019
+ archive: "https://github.com/burin-labs/harn/releases/download/v0.10.118/harn-x86_64-pc-windows-msvc.zip",
14968
15020
  cmd: "harn.exe",
14969
15021
  args: [
14970
15022
  "serve",
@@ -14977,7 +15029,7 @@ Requirements:
14977
15029
  {
14978
15030
  id: "junie",
14979
15031
  name: "Junie",
14980
- version: "2913.6.0",
15032
+ version: "3032.2.0",
14981
15033
  description: "AI Coding Agent by JetBrains",
14982
15034
  icon: "https://cdn.agentclientprotocol.com/registry/v1/latest/junie.svg",
14983
15035
  distribution: {
@@ -14995,47 +15047,47 @@ Requirements:
14995
15047
  {
14996
15048
  id: "kilo",
14997
15049
  name: "Kilo",
14998
- version: "7.4.23",
15050
+ version: "7.5.5",
14999
15051
  description: "The open source coding agent",
15000
15052
  icon: "https://cdn.agentclientprotocol.com/registry/v1/latest/kilo.svg",
15001
15053
  distribution: {
15002
15054
  npx: {
15003
- package: "@kilocode/cli@7.4.23",
15055
+ package: "@kilocode/cli@7.5.5",
15004
15056
  args: [
15005
15057
  "acp"
15006
15058
  ]
15007
15059
  },
15008
15060
  binary: {
15009
15061
  "darwin-aarch64": {
15010
- archive: "https://github.com/Kilo-Org/kilocode/releases/download/v7.4.23/kilo-darwin-arm64.zip",
15062
+ archive: "https://github.com/Kilo-Org/kilocode/releases/download/v7.5.5/kilo-darwin-arm64.zip",
15011
15063
  cmd: "./kilo",
15012
15064
  args: [
15013
15065
  "acp"
15014
15066
  ]
15015
15067
  },
15016
15068
  "darwin-x86_64": {
15017
- archive: "https://github.com/Kilo-Org/kilocode/releases/download/v7.4.23/kilo-darwin-x64.zip",
15069
+ archive: "https://github.com/Kilo-Org/kilocode/releases/download/v7.5.5/kilo-darwin-x64.zip",
15018
15070
  cmd: "./kilo",
15019
15071
  args: [
15020
15072
  "acp"
15021
15073
  ]
15022
15074
  },
15023
15075
  "linux-aarch64": {
15024
- archive: "https://github.com/Kilo-Org/kilocode/releases/download/v7.4.23/kilo-linux-arm64.tar.gz",
15076
+ archive: "https://github.com/Kilo-Org/kilocode/releases/download/v7.5.5/kilo-linux-arm64.tar.gz",
15025
15077
  cmd: "./kilo",
15026
15078
  args: [
15027
15079
  "acp"
15028
15080
  ]
15029
15081
  },
15030
15082
  "linux-x86_64": {
15031
- archive: "https://github.com/Kilo-Org/kilocode/releases/download/v7.4.23/kilo-linux-x64.tar.gz",
15083
+ archive: "https://github.com/Kilo-Org/kilocode/releases/download/v7.5.5/kilo-linux-x64.tar.gz",
15032
15084
  cmd: "./kilo",
15033
15085
  args: [
15034
15086
  "acp"
15035
15087
  ]
15036
15088
  },
15037
15089
  "windows-x86_64": {
15038
- archive: "https://github.com/Kilo-Org/kilocode/releases/download/v7.4.23/kilo-windows-x64.zip",
15090
+ archive: "https://github.com/Kilo-Org/kilocode/releases/download/v7.5.5/kilo-windows-x64.zip",
15039
15091
  cmd: "./kilo.exe",
15040
15092
  args: [
15041
15093
  "acp"
@@ -15117,12 +15169,12 @@ Requirements:
15117
15169
  {
15118
15170
  id: "nova",
15119
15171
  name: "Nova",
15120
- version: "1.1.35",
15172
+ version: "1.1.37",
15121
15173
  description: "Nova by Compass AI - a fully-fledged software engineer at your command",
15122
15174
  icon: "https://cdn.agentclientprotocol.com/registry/v1/latest/nova.svg",
15123
15175
  distribution: {
15124
15176
  npx: {
15125
- package: "@compass-ai/nova@1.1.35",
15177
+ package: "@compass-ai/nova@1.1.37",
15126
15178
  args: [
15127
15179
  "acp"
15128
15180
  ]
@@ -15132,7 +15184,7 @@ Requirements:
15132
15184
  {
15133
15185
  id: "opencode",
15134
15186
  name: "OpenCode",
15135
- version: "1.18.19",
15187
+ version: "1.18.23",
15136
15188
  description: "The open source coding agent",
15137
15189
  icon: "https://cdn.agentclientprotocol.com/registry/v1/latest/opencode.svg",
15138
15190
  distribution: {
@@ -15230,12 +15282,12 @@ Requirements:
15230
15282
  {
15231
15283
  id: "qwen-code",
15232
15284
  name: "Qwen Code",
15233
- version: "0.21.15",
15285
+ version: "0.22.2",
15234
15286
  description: "Alibaba's Qwen coding assistant",
15235
15287
  icon: "https://cdn.agentclientprotocol.com/registry/v1/latest/qwen-code.svg",
15236
15288
  distribution: {
15237
15289
  npx: {
15238
- package: "@qwen-code/qwen-code@0.21.15",
15290
+ package: "@qwen-code/qwen-code@0.22.2",
15239
15291
  args: [
15240
15292
  "--acp",
15241
15293
  "--experimental-skills"
@@ -17911,14 +17963,29 @@ ${outputTail}` : blockTail.output;
17911
17963
  };
17912
17964
  function extractAskUserQuestionAnswersFromOutcome(meta, outcome) {
17913
17965
  if (!outcome || !isRecord$b(outcome._meta)) return null;
17914
- const rawAnswers = meta.source === "lody" ? getLodyOutcomeAnswers(outcome._meta) : meta.source === "codex" ? getCodexOutcomeAnswers(outcome._meta) : getClaudeOutcomeAnswers(outcome._meta);
17915
- if (!rawAnswers) return null;
17966
+ const answerSources = [
17967
+ {
17968
+ source: "lody",
17969
+ answers: getLodyOutcomeAnswers(outcome._meta)
17970
+ },
17971
+ {
17972
+ source: "codex",
17973
+ answers: getCodexOutcomeAnswers(outcome._meta)
17974
+ },
17975
+ {
17976
+ source: "claude",
17977
+ answers: getClaudeOutcomeAnswers(outcome._meta)
17978
+ }
17979
+ ];
17980
+ const answerSource = answerSources.find(({ answers }) => answers !== null);
17981
+ if (!answerSource?.answers) return null;
17916
17982
  const result = {};
17917
17983
  for (const [index, question] of meta.questions.entries()) {
17918
17984
  const key2 = getAskUserQuestionAnswerKey(meta.questions, index);
17919
- const raw2 = rawAnswers[key2];
17985
+ const sourceKey = answerSource.source === "claude" ? getAskUserQuestionAnswerKey(meta.questions.map(({ id: _id, ...item }) => item), index) : key2;
17986
+ const raw2 = answerSource.answers[sourceKey];
17920
17987
  if (raw2 === void 0) continue;
17921
- if (meta.source === "codex") {
17988
+ if (answerSource.source === "codex") {
17922
17989
  if (!isRecord$b(raw2)) continue;
17923
17990
  const inner = raw2.answers;
17924
17991
  if (!Array.isArray(inner)) continue;
@@ -20600,10 +20667,13 @@ ${tailedOutput}` : null;
20600
20667
  return parsed;
20601
20668
  };
20602
20669
  const MACHINE_PROTOCOL_CAPABILITIES = {
20670
+ localProjectRemoval: "localProjectRemoval",
20603
20671
  providerSetup: "providerSetup"
20604
20672
  };
20673
+ const LOCAL_PROJECT_REMOVAL_PROTOCOL_VERSION = 1;
20605
20674
  const PROVIDER_SETUP_PROTOCOL_VERSION = 1;
20606
20675
  const CURRENT_MACHINE_PROTOCOL_CAPABILITIES = {
20676
+ [MACHINE_PROTOCOL_CAPABILITIES.localProjectRemoval]: LOCAL_PROJECT_REMOVAL_PROTOCOL_VERSION,
20607
20677
  [MACHINE_PROTOCOL_CAPABILITIES.providerSetup]: PROVIDER_SETUP_PROTOCOL_VERSION
20608
20678
  };
20609
20679
  const isLoroRepoDocDeleted = (value) => {
@@ -20694,6 +20764,7 @@ ${tailedOutput}` : null;
20694
20764
  };
20695
20765
  const toFilePayload = (block) => {
20696
20766
  const machineId = typeof block.machineId === "string" ? block.machineId : void 0;
20767
+ const sourcePath = typeof block.sourcePath === "string" && isSessionFileSourcePath(block.sourcePath) ? block.sourcePath : void 0;
20697
20768
  const storageSessionId = typeof block.storageSessionId === "string" ? block.storageSessionId : void 0;
20698
20769
  return {
20699
20770
  type: "file",
@@ -20703,6 +20774,9 @@ ${tailedOutput}` : null;
20703
20774
  sizeBytes: block.sizeBytes,
20704
20775
  sha256: block.sha256,
20705
20776
  textPreview: block.textPreview,
20777
+ ...sourcePath === void 0 ? {} : {
20778
+ sourcePath
20779
+ },
20706
20780
  transport: block.transport,
20707
20781
  ...machineId === void 0 ? {} : {
20708
20782
  machineId
@@ -23862,8 +23936,65 @@ ${context2.authorReply.trim()}
23862
23936
  }
23863
23937
  command2.requestedBy = value.requestedBy;
23864
23938
  }
23939
+ if (!isMissing(value.projectName)) {
23940
+ if (typeof value.projectName !== "string") return void 0;
23941
+ command2.projectName = value.projectName;
23942
+ }
23943
+ if (!isMissing(value.originalRootPath)) {
23944
+ if (typeof value.originalRootPath !== "string") return void 0;
23945
+ command2.originalRootPath = value.originalRootPath;
23946
+ }
23947
+ if (!isMissing(value.cleanupWorktrees)) {
23948
+ if (value.cleanupWorktrees !== true) return void 0;
23949
+ command2.cleanupWorktrees = true;
23950
+ }
23951
+ if (!isMissing(value.status)) {
23952
+ if (value.status !== "completed") return void 0;
23953
+ command2.status = "completed";
23954
+ }
23955
+ if (!isMissing(value.cleanupResult)) {
23956
+ const result = normalizeLocalProjectWorktreeCleanupResult(value.cleanupResult);
23957
+ if (!result) return void 0;
23958
+ command2.cleanupResult = result;
23959
+ }
23865
23960
  return command2;
23866
23961
  };
23962
+ const normalizeLocalProjectWorktreeCleanupItem = (value) => {
23963
+ if (!isRecord$8(value)) return void 0;
23964
+ if (typeof value.sessionId !== "string") return void 0;
23965
+ if (typeof value.title !== "string") return void 0;
23966
+ if (typeof value.path !== "string") return void 0;
23967
+ return {
23968
+ sessionId: value.sessionId,
23969
+ title: value.title,
23970
+ path: value.path
23971
+ };
23972
+ };
23973
+ const normalizeLocalProjectWorktreeCleanupResult = (value) => {
23974
+ if (!isRecord$8(value) || typeof value.completedAt !== "number") return void 0;
23975
+ if (!Array.isArray(value.deleted)) return void 0;
23976
+ if (!Array.isArray(value.skippedDirty)) return void 0;
23977
+ if (!Array.isArray(value.failed)) return void 0;
23978
+ const deleted = value.deleted.map(normalizeLocalProjectWorktreeCleanupItem);
23979
+ const skippedDirty = value.skippedDirty.map(normalizeLocalProjectWorktreeCleanupItem);
23980
+ const failed = value.failed.map((entry) => {
23981
+ const item = normalizeLocalProjectWorktreeCleanupItem(entry);
23982
+ if (!item || !isRecord$8(entry) || typeof entry.message !== "string") return void 0;
23983
+ return {
23984
+ ...item,
23985
+ message: entry.message
23986
+ };
23987
+ });
23988
+ if (deleted.includes(void 0)) return void 0;
23989
+ if (skippedDirty.includes(void 0)) return void 0;
23990
+ if (failed.includes(void 0)) return void 0;
23991
+ return {
23992
+ completedAt: value.completedAt,
23993
+ deleted,
23994
+ skippedDirty,
23995
+ failed
23996
+ };
23997
+ };
23867
23998
  const normalizeLocalProjectMeta = (value) => {
23868
23999
  if (!isRecord$8(value) || !isNonEmptyString$2(value.id) || !isNonEmptyString$2(value.name) || !isNonEmptyString$2(value.rootPath) || typeof value.createdAtMs !== "number") {
23869
24000
  return void 0;
@@ -74113,6 +74244,7 @@ ${val.stack}`;
74113
74244
  return repo;
74114
74245
  }
74115
74246
  async ready() {
74247
+ await this.syncRunner.ensureMetaLiveMonitor();
74116
74248
  if (this.metaPersister) {
74117
74249
  const version2 = this.metaFlock.version();
74118
74250
  this.metaPersister.start(version2);
@@ -104210,14 +104342,14 @@ ${value}`;
104210
104342
  }
104211
104343
  await this.machine.updateRateLimits(cliType, limits);
104212
104344
  }
104213
- async updateAcpCapabilities(machineId, configId, cliType, agentType, modes, models, configOptions, availableCommands, sessionFork, sourceVersion, modelReasoningEfforts, options = {}) {
104345
+ async updateAcpCapabilities(machineId, configId, cliType, agentType, modes, models, configOptions, availableCommands, sessionFork, sourceVersion, modelReasoningEfforts, acknowledgedSteer = false, options = {}) {
104214
104346
  options.signal?.throwIfAborted();
104215
104347
  if (!this.machine) {
104216
104348
  this.machine = this.createMachineDocument(machineId);
104217
104349
  await this.machine.init();
104218
104350
  }
104219
104351
  options.signal?.throwIfAborted();
104220
- await this.machine.updateAcpCapabilities(configId, cliType, agentType, modes, models, configOptions, availableCommands, sessionFork, sourceVersion, modelReasoningEfforts, options);
104352
+ await this.machine.updateAcpCapabilities(configId, cliType, agentType, modes, models, configOptions, availableCommands, sessionFork, sourceVersion, modelReasoningEfforts, acknowledgedSteer, options);
104221
104353
  }
104222
104354
  async getAcpCapabilities(machineId, configId) {
104223
104355
  if (!this.machine) {
@@ -104397,6 +104529,7 @@ ${value}`;
104397
104529
  this.mirror = new Mirror({
104398
104530
  doc: handle.doc,
104399
104531
  schema: sessionDocSchema,
104532
+ ignoreUnknownProperties: true,
104400
104533
  initialState: merged
104401
104534
  });
104402
104535
  this.historyAutoReadHandle = attachAutoMarkLatestUserHistoryAsRead(this.mirror);
@@ -105323,6 +105456,7 @@ ${value}`;
105323
105456
  configOptions: entry.configOptions,
105324
105457
  availableCommands: entry.availableCommands,
105325
105458
  sessionFork: entry.sessionFork,
105459
+ acknowledgedSteer: entry.acknowledgedSteer,
105326
105460
  sessionForkWorktree: entry.sessionForkWorktree
105327
105461
  });
105328
105462
  class MachineDocument {
@@ -105378,7 +105512,7 @@ ${value}`;
105378
105512
  }
105379
105513
  });
105380
105514
  }
105381
- async updateAcpCapabilities(configId, cliType, agentType, modes, models, configOptions, availableCommands, sessionFork, sourceVersion, modelReasoningEfforts, options = {}) {
105515
+ async updateAcpCapabilities(configId, cliType, agentType, modes, models, configOptions, availableCommands, sessionFork, sourceVersion, modelReasoningEfforts, acknowledgedSteer = false, options = {}) {
105382
105516
  options.signal?.throwIfAborted();
105383
105517
  const normalizedModes = modes.map((mode2) => ({
105384
105518
  id: mode2.id,
@@ -105401,6 +105535,7 @@ ${value}`;
105401
105535
  configOptions: configOptions?.length ? configOptions : void 0,
105402
105536
  availableCommands: availableCommands?.length ? availableCommands : void 0,
105403
105537
  sessionFork,
105538
+ acknowledgedSteer,
105404
105539
  sessionForkWorktree: sessionFork,
105405
105540
  modelReasoningEfforts: modelReasoningEfforts && Object.keys(modelReasoningEfforts).length > 0 ? modelReasoningEfforts : void 0,
105406
105541
  fetchedAt: getServerNow()
@@ -113532,6 +113667,7 @@ ${incoming}`).output;
113532
113667
  configOptions,
113533
113668
  availableCommands,
113534
113669
  sessionFork: lifecycleCapabilities.sessionFork === true,
113670
+ acknowledgedSteer: lifecycleCapabilities.acknowledgedSteer === true,
113535
113671
  ...modelReasoningEfforts ? {
113536
113672
  modelReasoningEfforts
113537
113673
  } : {}
@@ -114635,7 +114771,8 @@ ${incoming}`).output;
114635
114771
  const toolCallId = typeof params.toolCallId === "string" ? params.toolCallId : void 0;
114636
114772
  const requestId = randomUUID$1();
114637
114773
  const firstQuestion = elicitation.meta.questions[0];
114638
- const autoResolveAtEpochSeconds = typeof elicitation.autoResolutionMs === "number" && Number.isFinite(elicitation.autoResolutionMs) ? Math.floor((getServerNow() + Math.max(0, elicitation.autoResolutionMs)) / 1e3) : void 0;
114774
+ const autoResolveAt = typeof elicitation.autoResolutionMs === "number" && Number.isFinite(elicitation.autoResolutionMs) ? getServerNow() + Math.max(0, elicitation.autoResolutionMs) : void 0;
114775
+ const autoResolveAtEpochSeconds = autoResolveAt === void 0 ? void 0 : Math.floor(autoResolveAt / 1e3);
114639
114776
  const lodyElicitation = {
114640
114777
  version: 1,
114641
114778
  questions: elicitation.meta.questions,
@@ -114643,6 +114780,27 @@ ${incoming}`).output;
114643
114780
  autoResolveAtEpochSeconds
114644
114781
  } : {}
114645
114782
  };
114783
+ const legacyQuestionMeta = this.options.agentConfig?.agentType === "codex" ? {
114784
+ codex: {
114785
+ requestUserInput: {
114786
+ version: 1,
114787
+ allowCustomAnswer: elicitation.meta.allowCustomAnswer,
114788
+ questions: elicitation.meta.questions,
114789
+ ...autoResolveAt !== void 0 ? {
114790
+ autoResolveAt
114791
+ } : {}
114792
+ }
114793
+ }
114794
+ } : this.options.agentConfig?.agentType === "claude" ? {
114795
+ claudeCode: {
114796
+ requestType: "askUserQuestion",
114797
+ askUserQuestion: {
114798
+ version: 1,
114799
+ allowCustomAnswer: elicitation.meta.allowCustomAnswer,
114800
+ questions: elicitation.meta.questions
114801
+ }
114802
+ }
114803
+ } : {};
114646
114804
  const syntheticRequest = {
114647
114805
  sessionId,
114648
114806
  toolCall: {
@@ -114669,7 +114827,8 @@ ${incoming}`).output;
114669
114827
  _meta: {
114670
114828
  lody: {
114671
114829
  elicitation: lodyElicitation
114672
- }
114830
+ },
114831
+ ...legacyQuestionMeta
114673
114832
  }
114674
114833
  };
114675
114834
  this.logger.debug(`[${this.options.sessionId}] Bridging AskUserQuestion elicitation (toolCallId=${toolCallId ?? "<none>"}, questions=${elicitation.meta.questions.length})`);
@@ -129178,7 +129337,8 @@ ${fallbackStderrTail}` : errorMessage2;
129178
129337
  try {
129179
129338
  return {
129180
129339
  ...normalizeAcpSessionCapabilities(sessionResponse, {
129181
- sessionFork: client2.supportsSessionFork?.() === true
129340
+ sessionFork: client2.supportsSessionFork?.() === true,
129341
+ acknowledgedSteer: client2.supportsAcknowledgedSteer()
129182
129342
  }),
129183
129343
  capabilitySourceVersion
129184
129344
  };
@@ -131440,6 +131600,32 @@ path=/${options.repoFullName}.git
131440
131600
  isClean
131441
131601
  };
131442
131602
  }
131603
+ async inspectWorktree(sessionId) {
131604
+ return withRepoLock(this.repoId, async () => {
131605
+ assertSafeSessionId(sessionId);
131606
+ const worktreePath = this.getWorktreeHostPath(sessionId);
131607
+ if (!fs.existsSync(worktreePath)) {
131608
+ return {
131609
+ state: "missing",
131610
+ path: worktreePath
131611
+ };
131612
+ }
131613
+ try {
131614
+ const info = await this.getWorktreeInfo(sessionId);
131615
+ return {
131616
+ state: info.isClean ? "clean" : "dirty",
131617
+ path: worktreePath,
131618
+ info
131619
+ };
131620
+ } catch (error2) {
131621
+ return {
131622
+ state: "failed",
131623
+ path: worktreePath,
131624
+ message: error2 instanceof Error ? error2.message : String(error2)
131625
+ };
131626
+ }
131627
+ });
131628
+ }
131443
131629
  async removeWorktree(sessionId, force = false, branchName, options) {
131444
131630
  return withRepoLock(this.repoId, async () => {
131445
131631
  const resolvedBranchName = await this.removeWorktreeInternal(sessionId, {
@@ -131539,7 +131725,10 @@ path=/${options.repoFullName}.git
131539
131725
  ];
131540
131726
  if (options.force) args2.splice(2, 0, "--force");
131541
131727
  await this.runGit(args2, this.getGitAdminCwd());
131542
- } catch {
131728
+ } catch (error2) {
131729
+ if (!options.force) {
131730
+ throw error2;
131731
+ }
131543
131732
  this.logger.debug(`[${this.repoId}] git worktree remove failed, removing directory manually`);
131544
131733
  fs.rmSync(worktreePath, {
131545
131734
  recursive: true,
@@ -132882,6 +133071,9 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
132882
133071
  function shouldApplyMachineDeleteLocalProjectCommand(project, command2) {
132883
133072
  return project.createdAtMs <= command2.requestedAt;
132884
133073
  }
133074
+ function isSessionInLocalProjectRemovalScope(session2, target) {
133075
+ return session2.machineId === target.machineId && session2.project?.kind === "local" && session2.project.localProjectId === target.localProjectId;
133076
+ }
132885
133077
  function readLegacyMachineLocalProjects(machineMeta) {
132886
133078
  const localProjects = machineMeta?.localProjects;
132887
133079
  if (!localProjects || typeof localProjects !== "object") {
@@ -136245,7 +136437,7 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
136245
136437
  });
136246
136438
  const existing = await this.deps.workspaceDocument.getAcpCapabilities(this.deps.machineId, agentConfigId);
136247
136439
  const availableCommands = capabilities.availableCommands !== void 0 ? capabilities.availableCommands : existing?.sourceVersion === sourceVersion ? existing.availableCommands : void 0;
136248
- await this.deps.workspaceDocument.updateAcpCapabilities(this.deps.machineId, agentConfigId, config2.agentCliType, config2.agentType, capabilities.modes, capabilities.models, capabilities.configOptions, availableCommands, capabilities.sessionFork, sourceVersion, capabilities.modelReasoningEfforts);
136440
+ await this.deps.workspaceDocument.updateAcpCapabilities(this.deps.machineId, agentConfigId, config2.agentCliType, config2.agentType, capabilities.modes, capabilities.models, capabilities.configOptions, availableCommands, capabilities.sessionFork, sourceVersion, capabilities.modelReasoningEfforts, capabilities.acknowledgedSteer);
136249
136441
  })().catch((error2) => {
136250
136442
  this.deps.logger.debug(`[${session2.sessionId}] Failed to update ACP capabilities from created session: ${formatErrorMessage(error2)}`);
136251
136443
  });
@@ -136419,7 +136611,7 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
136419
136611
  options.signal?.throwIfAborted();
136420
136612
  await this.emitBuiltinRuntimeStatusForRefresh(message, options.onAcpBinaryProgress);
136421
136613
  options.signal?.throwIfAborted();
136422
- const { modes, models, configOptions, availableCommands, sessionFork, modelReasoningEfforts, capabilitySourceVersion } = await this.deps.fetchAcpCapabilities(message.cliType, message.agentType, message.env, message.customAcp, message.runtimeOverrides, {
136614
+ const { modes, models, configOptions, availableCommands, sessionFork, acknowledgedSteer, modelReasoningEfforts, capabilitySourceVersion } = await this.deps.fetchAcpCapabilities(message.cliType, message.agentType, message.env, message.customAcp, message.runtimeOverrides, {
136423
136615
  signal: options.signal,
136424
136616
  onManagedRuntimeProgress: (event) => {
136425
136617
  if (options.signal?.aborted) return;
@@ -136432,7 +136624,7 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
136432
136624
  agentType: message.agentType,
136433
136625
  customAcp: message.customAcp,
136434
136626
  runtimeOverrides: message.runtimeOverrides
136435
- }), modelReasoningEfforts, {
136627
+ }), modelReasoningEfforts, acknowledgedSteer, {
136436
136628
  signal: options.signal
136437
136629
  });
136438
136630
  return {
@@ -137241,18 +137433,21 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
137241
137433
  }
137242
137434
  }
137243
137435
  }
137244
- peekStashedRpcTurn(sessionId, meta) {
137436
+ peekStashedRpcTurn(sessionId, meta, history = []) {
137245
137437
  const stash = this.rpcTurnStash.get(sessionId);
137246
137438
  if (!stash) {
137247
137439
  return null;
137248
137440
  }
137249
137441
  const now2 = Date.now();
137250
137442
  for (const [userTurnId, stashed] of stash) {
137251
- const isTerminal2 = meta.lastHandledUserMsgId === userTurnId || this.deps.executionService.getTerminalUserTurnStatusWithoutEntry?.(sessionId, userTurnId) !== void 0;
137443
+ const isTerminal2 = meta.lastHandledUserMsgId === userTurnId || this.deps.executionService.getTerminalUserTurnStatusWithoutEntry?.(sessionId, userTurnId) !== void 0 || history.some((entry) => entry.id === userTurnId && entry.role === "user" && (entry.status === "handled" || entry.status === "failed" || entry.status === "canceled"));
137252
137444
  if (stashed.expiresAtMs <= now2 || isTerminal2) {
137253
137445
  stash.delete(userTurnId);
137254
137446
  continue;
137255
137447
  }
137448
+ if (userTurnId === meta.lastMissingHistoryUserMsgId) {
137449
+ continue;
137450
+ }
137256
137451
  this.turnSourceHints.set(`${sessionId}:${userTurnId}`, "rpc");
137257
137452
  return stashed.entry;
137258
137453
  }
@@ -138206,7 +138401,8 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
138206
138401
  if (!isActive()) {
138207
138402
  return null;
138208
138403
  }
138209
- this.deps.logger.debug(`[${sessionId}] Pending user turn metadata is visible but history is missing it; waiting up to ${SessionDispatchWatcher.HISTORY_SYNC_WAIT_TIMEOUT_MS / 1e3}s for history CRDT sync`);
138404
+ const pendingUserTurnId = getPendingUserTurnActivationId(meta);
138405
+ this.deps.logger.debug(`[${sessionId}] Pending user turn ${getPendingUserTurnActivationId(meta) ?? "unknown"} metadata is visible but history is missing it; waiting up to ${SessionDispatchWatcher.HISTORY_SYNC_WAIT_TIMEOUT_MS / 1e3}s for history CRDT sync (pendingUserMsgId=${pendingUserTurnId ?? "unknown"})`);
138210
138406
  return await new Promise((resolve2) => {
138211
138407
  let settled = false;
138212
138408
  let currentMeta = meta;
@@ -138336,7 +138532,7 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
138336
138532
  }
138337
138533
  };
138338
138534
  timer2 = SessionDispatchWatcher.setUnrefTimeout(() => {
138339
- this.deps.logger.warn(`[${sessionId}] User turn did not arrive in history after ${SessionDispatchWatcher.HISTORY_SYNC_WAIT_TIMEOUT_MS / 1e3}s; entering dispatch recovery`);
138535
+ this.deps.logger.warn(`[${sessionId}] User turn ${pendingUserTurnId ?? "unknown"} did not arrive in history after ${SessionDispatchWatcher.HISTORY_SYNC_WAIT_TIMEOUT_MS / 1e3}s; entering dispatch recovery`);
138340
138536
  finish(null);
138341
138537
  }, SessionDispatchWatcher.HISTORY_SYNC_WAIT_TIMEOUT_MS);
138342
138538
  progressTimer = setInterval(() => {
@@ -138346,7 +138542,7 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
138346
138542
  }
138347
138543
  return;
138348
138544
  }
138349
- this.deps.logger.warn(`[${sessionId}] Still waiting for pending user turn history sync (elapsed=${Date.now() - waitStartedAt}ms docRoom=${sessionDoc.getDocRoomStatus() ?? "unknown"})`);
138545
+ this.deps.logger.warn(`[${sessionId}] Still waiting for pending user turn history sync (pendingUserMsgId=${pendingUserTurnId ?? "unknown"} elapsed=${Date.now() - waitStartedAt}ms docRoom=${sessionDoc.getDocRoomStatus() ?? "unknown"})`);
138350
138546
  }, SessionDispatchWatcher.HISTORY_SYNC_PROGRESS_LOG_MS);
138351
138547
  progressTimer.unref?.();
138352
138548
  if (!isActive()) {
@@ -138424,7 +138620,9 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
138424
138620
  if (pendingUserMsgId) {
138425
138621
  recoveryPatch.lastMissingHistoryUserMsgId = pendingUserMsgId;
138426
138622
  }
138623
+ this.deps.logger.warn(`[${sessionId}] Marking missing-history recovery for user turn ${pendingUserMsgId ?? "unknown"} (message_delivery_failed)`);
138427
138624
  await this.deps.workspaceDocument.repo.upsertDocMeta?.(roomId, recoveryPatch);
138625
+ this.deps.logger.info(`[${sessionId}] Recorded missing-history recovery for user turn ${pendingUserMsgId ?? "unknown"}; the turn stays undispatchable until the user explicitly redelivers it`);
138428
138626
  if (this.deps.recordChatFailure) {
138429
138627
  try {
138430
138628
  await this.deps.recordChatFailure(sessionDoc, "message_delivery_failed", "The user message could not be delivered because its history payload did not sync to this machine. Please resend it after sync recovers.");
@@ -138464,7 +138662,7 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
138464
138662
  this.turnSourceHints.set(`${meta.id}:${promoted.id}`, "queue");
138465
138663
  return promoted;
138466
138664
  }
138467
- return this.peekStashedRpcTurn(meta.id, meta);
138665
+ return this.peekStashedRpcTurn(meta.id, meta, history);
138468
138666
  }
138469
138667
  async maybeRepairAlreadyHandledTurn(sessionDoc, meta, turn, history) {
138470
138668
  const sessionId = meta.id;
@@ -139863,6 +140061,7 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
139863
140061
  string$1(),
139864
140062
  boolean()
139865
140063
  ])).optional(),
140064
+ taskToolsEnabled: boolean().optional(),
139866
140065
  inheritSessionDefaults: literal$1(false).optional()
139867
140066
  }).strict().nullable()).optional()
139868
140067
  }).strict();
@@ -151704,6 +151903,7 @@ ${part.truncated ? TRUNCATION_MARKER : ""}${part.content}`).join("\n\n");
151704
151903
  const mirror = new Mirror({
151705
151904
  doc: handle.doc,
151706
151905
  schema: taskDocSchema,
151906
+ ignoreUnknownProperties: true,
151707
151907
  ...options?.seedEmptyDocument ? {
151708
151908
  initialState: emptyTaskDocState(taskId)
151709
151909
  } : {}
@@ -166005,7 +166205,7 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
166005
166205
  "local-project/set-worktree-cleanup"
166006
166206
  ]);
166007
166207
  function isRemoteLocalProjectControlRequest(request2) {
166008
- return HISTORY_REQUEST_TYPES.has(request2.type) || SETUP_REQUEST_TYPES.has(request2.type);
166208
+ return request2.type === "local-project/removal-preflight" || HISTORY_REQUEST_TYPES.has(request2.type) || SETUP_REQUEST_TYPES.has(request2.type);
166009
166209
  }
166010
166210
  function precheckLocalProjectHistoryRequest(args2) {
166011
166211
  const { request: request2, expectedMachineId, expectedWorkspaceId } = args2;
@@ -166044,6 +166244,91 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
166044
166244
  requesterUserId
166045
166245
  };
166046
166246
  }
166247
+ function getTargetSessions(target) {
166248
+ return target.sessions.filter((session2) => session2.machineId === target.machineId && !session2.parentSessionId && session2.isWorktree === true && session2.project?.kind === "local" && session2.project.localProjectId === target.localProjectId);
166249
+ }
166250
+ function toCleanupItem(session2, path2) {
166251
+ return {
166252
+ sessionId: session2.id,
166253
+ title: session2.title?.trim() || session2.id,
166254
+ path: path2
166255
+ };
166256
+ }
166257
+ function getManager(target) {
166258
+ return getWorktreeManager({
166259
+ repoId: deriveRepoIdFromLocalProjectPath(target.originalRootPath),
166260
+ source: {
166261
+ kind: "local-shared",
166262
+ originalRootPath: target.originalRootPath
166263
+ },
166264
+ logger: target.logger
166265
+ });
166266
+ }
166267
+ async function preflightLocalProjectWorktreeRemoval(target) {
166268
+ const result = {
166269
+ clean: [],
166270
+ dirty: [],
166271
+ failed: []
166272
+ };
166273
+ const manager = getManager(target);
166274
+ for (const session2 of getTargetSessions(target)) {
166275
+ const inspection = await manager.inspectWorktree(session2.id);
166276
+ if (inspection.state === "missing") continue;
166277
+ const item = toCleanupItem(session2, inspection.path);
166278
+ if (inspection.state === "clean") result.clean.push(item);
166279
+ if (inspection.state === "dirty") result.dirty.push(item);
166280
+ if (inspection.state === "failed") {
166281
+ result.failed.push({
166282
+ ...item,
166283
+ message: inspection.message
166284
+ });
166285
+ }
166286
+ }
166287
+ return result;
166288
+ }
166289
+ async function cleanupLocalProjectWorktrees(target) {
166290
+ const result = {
166291
+ completedAt: getServerNow(),
166292
+ deleted: [],
166293
+ skippedDirty: [],
166294
+ failed: []
166295
+ };
166296
+ const manager = getManager(target);
166297
+ for (const session2 of getTargetSessions(target)) {
166298
+ const inspection = await manager.inspectWorktree(session2.id);
166299
+ if (inspection.state === "missing") continue;
166300
+ const item = toCleanupItem(session2, inspection.path);
166301
+ if (inspection.state === "dirty") {
166302
+ result.skippedDirty.push(item);
166303
+ continue;
166304
+ }
166305
+ if (inspection.state === "failed") {
166306
+ result.failed.push({
166307
+ ...item,
166308
+ message: inspection.message
166309
+ });
166310
+ continue;
166311
+ }
166312
+ try {
166313
+ await manager.removeWorktree(session2.id, false, session2.branchName, {
166314
+ baseBranchName: session2.baseBranch
166315
+ });
166316
+ result.deleted.push(item);
166317
+ } catch (error2) {
166318
+ const latest2 = await manager.inspectWorktree(session2.id);
166319
+ if (latest2.state === "dirty") {
166320
+ result.skippedDirty.push(toCleanupItem(session2, latest2.path));
166321
+ } else if (latest2.state !== "missing") {
166322
+ result.failed.push({
166323
+ ...item,
166324
+ message: formatErrorMessage(error2)
166325
+ });
166326
+ }
166327
+ }
166328
+ }
166329
+ result.completedAt = getServerNow();
166330
+ return result;
166331
+ }
166047
166332
  const SESSION_IMAGE_DOWNLOAD_RETRY_DELAYS_MS = [
166048
166333
  250,
166049
166334
  750,
@@ -166427,6 +166712,9 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
166427
166712
  if (elapsedMs < day) return `${Math.max(1, Math.floor(elapsedMs / hour))}h`;
166428
166713
  return `${Math.max(1, Math.floor(elapsedMs / day))}d`;
166429
166714
  }
166715
+ function normalizeSessionFileSourcePath(sourcePath, separator = path__default$1.sep) {
166716
+ return separator === "/" ? sourcePath : sourcePath.split(separator).join("/");
166717
+ }
166430
166718
  const SESSION_FILE_MAX_PART_RETRIES = 3;
166431
166719
  const SESSION_FILE_MIME_TYPE_BY_EXTENSION = {
166432
166720
  txt: "text/plain",
@@ -168771,7 +169059,7 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
168771
169059
  }
168772
169060
  void this.processDeleteLocalProjectRequests();
168773
169061
  }
168774
- async archiveSessionResources(sessionId) {
169062
+ async archiveSessionResources(sessionId, options) {
168775
169063
  this.logger.debug(`[${sessionId}] Archiving session resources`);
168776
169064
  this.clearSessionActivePresence(sessionId);
168777
169065
  this.closeSessionTerminals?.(sessionId);
@@ -168792,55 +169080,58 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
168792
169080
  this.logger.debug(`[${sessionId}] Terminating active session`);
168793
169081
  await this.sessionManager.terminateSession(sessionId, true);
168794
169082
  }
168795
- const cleanupTarget = this.resolveWorktreeCleanupTarget({
168796
- sessionMeta,
168797
- machineMeta: archiveMachineMeta
168798
- });
168799
- if (cleanupTarget) {
168800
- const worktreeManager = getWorktreeManager(this.buildWorktreeManagerConfig(cleanupTarget));
168801
- const worktreePath = worktreeManager.getWorktreeHostPath(sessionId);
168802
- if (fs__default.existsSync(worktreePath)) {
168803
- const sessionDoc = await this.workspaceDocument.getOrCreateSessionDoc(sessionId);
168804
- await runWorktreeCleanup({
168805
- config: await resolveSessionWorktreeCleanupConfig({
168806
- token: this.token,
168807
- workspaceId: this.workspaceId,
168808
- machineId: this.machineId,
168809
- sessionId,
168810
- sessionMeta,
168811
- workspaceDocument: this.workspaceDocument,
168812
- logger: this.logger
168813
- }),
168814
- sessionId,
168815
- workspaceId: this.workspaceId,
168816
- workdir: worktreePath,
168817
- branch: cleanupTarget.branchName ?? sessionMeta?.branchName?.trim() ?? "",
168818
- repoFullName: sessionMeta?.project?.kind === "local" ? void 0 : sessionMeta?.repoFullName,
168819
- localProjectId: sessionMeta?.project?.kind === "local" ? sessionMeta.project.localProjectId : void 0,
168820
- logger: this.logger,
168821
- events: createWorktreeScriptHistoryRecorder({
168822
- sessionDoc,
169083
+ if (!options?.preserveWorktree) {
169084
+ const cleanupTarget = this.resolveWorktreeCleanupTarget({
169085
+ sessionMeta,
169086
+ machineMeta: archiveMachineMeta
169087
+ });
169088
+ if (cleanupTarget) {
169089
+ const worktreeManager = getWorktreeManager(this.buildWorktreeManagerConfig(cleanupTarget));
169090
+ const worktreePath = worktreeManager.getWorktreeHostPath(sessionId);
169091
+ if (fs__default.existsSync(worktreePath)) {
169092
+ const sessionDoc = await this.workspaceDocument.getOrCreateSessionDoc(sessionId);
169093
+ await runWorktreeCleanup({
169094
+ config: await resolveSessionWorktreeCleanupConfig({
169095
+ token: this.token,
169096
+ workspaceId: this.workspaceId,
169097
+ machineId: this.machineId,
169098
+ sessionId,
169099
+ sessionMeta,
169100
+ workspaceDocument: this.workspaceDocument,
169101
+ logger: this.logger
169102
+ }),
168823
169103
  sessionId,
168824
- phase: "cleanup",
168825
- logger: this.logger
168826
- })
168827
- });
168828
- }
168829
- try {
168830
- const archiveResult = await worktreeManager.archiveWorktree(sessionId);
168831
- if (archiveResult.branchName && archiveResult.branchName !== sessionMeta?.branchName?.trim()) {
168832
- await this.workspaceDocument.repo.upsertDocMeta(sessionRoomId, {
168833
- branchName: archiveResult.branchName
169104
+ workspaceId: this.workspaceId,
169105
+ workdir: worktreePath,
169106
+ branch: cleanupTarget.branchName ?? sessionMeta?.branchName?.trim() ?? "",
169107
+ repoFullName: sessionMeta?.project?.kind === "local" ? void 0 : sessionMeta?.repoFullName,
169108
+ localProjectId: sessionMeta?.project?.kind === "local" ? sessionMeta.project.localProjectId : void 0,
169109
+ logger: this.logger,
169110
+ events: createWorktreeScriptHistoryRecorder({
169111
+ sessionDoc,
169112
+ sessionId,
169113
+ phase: "cleanup",
169114
+ logger: this.logger
169115
+ })
168834
169116
  });
168835
169117
  }
168836
- } catch (error2) {
168837
- this.logger.debug(`[${sessionId}] Failed to archive worktree: ${formatErrorMessage(error2)}`);
169118
+ try {
169119
+ const archiveResult = await worktreeManager.archiveWorktree(sessionId);
169120
+ if (archiveResult.branchName && archiveResult.branchName !== sessionMeta?.branchName?.trim()) {
169121
+ await this.workspaceDocument.repo.upsertDocMeta(sessionRoomId, {
169122
+ branchName: archiveResult.branchName
169123
+ });
169124
+ }
169125
+ } catch (error2) {
169126
+ this.logger.debug(`[${sessionId}] Failed to archive worktree: ${formatErrorMessage(error2)}`);
169127
+ }
168838
169128
  }
168839
169129
  }
168840
169130
  await this.sessionManager.archiveSession(sessionId);
168841
169131
  this.logger.debug(`[${sessionId}] Session doc cleaned`);
168842
169132
  await this.workspaceDocument.repo.upsertDocMeta(sessionRoomId, {
168843
- isArchived: true
169133
+ isArchived: true,
169134
+ status: SessionStatusFactory.idle()
168844
169135
  });
168845
169136
  this.logger.debug(`[${sessionId}] Session meta archived`);
168846
169137
  this.store.get(sessionId).logger = null;
@@ -168900,14 +169191,28 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
168900
169191
  }
168901
169192
  this.logger.debug(`[local-project] Processing ${localProjectEntries.length} delete request(s)`);
168902
169193
  for (const [localProjectId, command2] of localProjectEntries) {
169194
+ if (command2.status === "completed") {
169195
+ continue;
169196
+ }
168903
169197
  if (this.deleteLocalProjectInFlight.has(localProjectId)) {
168904
169198
  this.logger.debug(`[local-project] ${localProjectId} already in progress`);
168905
169199
  continue;
168906
169200
  }
168907
169201
  this.deleteLocalProjectInFlight.add(localProjectId);
168908
169202
  try {
168909
- await this.deleteLocalProjectResources(localProjectId, command2);
168910
- await this.removeDeleteLocalProjectRequest(localProjectId);
169203
+ const cleanupResult = await this.deleteLocalProjectResources(localProjectId, command2);
169204
+ if (command2.cleanupWorktrees && cleanupResult) {
169205
+ await this.writeMachineFlockRow({
169206
+ key: machineFlockKeys.deleteLocalProjectCommand(localProjectId),
169207
+ value: {
169208
+ ...command2,
169209
+ status: "completed",
169210
+ cleanupResult
169211
+ }
169212
+ });
169213
+ } else {
169214
+ await this.removeDeleteLocalProjectRequest(localProjectId);
169215
+ }
168911
169216
  } catch (error2) {
168912
169217
  this.logger.error(`[local-project] Failed to delete ${localProjectId}: ${formatErrorMessage(error2)}`);
168913
169218
  } finally {
@@ -168925,56 +169230,68 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
168925
169230
  this.logger.debug(`[local-project] Failed to cleanup worktree setup for ${localProjectId}: ${formatErrorMessage(error2)}`);
168926
169231
  }
168927
169232
  }
168928
- async stopActiveLocalProjectSessions(localProjectId) {
168929
- const sessionIds = [
168930
- ...this.workspaceDocument.sessions.keys()
168931
- ];
168932
- if (sessionIds.length === 0) return;
168933
- let stoppedCount = 0;
168934
- for (const sessionId of sessionIds) {
168935
- try {
168936
- const sessionDoc = await this.workspaceDocument.getOrCreateSessionDoc(sessionId);
168937
- const meta = await sessionDoc.getMetaState();
168938
- if (!meta) continue;
168939
- if (meta.machineId !== this.machineId) continue;
168940
- if (meta.project?.kind !== "local") continue;
168941
- if (meta.project.localProjectId !== localProjectId) continue;
168942
- const hasRunningProcess = this.sessionManager.hasSession(sessionId);
168943
- if (!hasRunningProcess && !isActiveSessionStatus(meta.status)) continue;
168944
- this.clearSessionActivePresence(sessionId);
168945
- this.closeSessionTerminals?.(sessionId);
168946
- await this.finalizeACPState(sessionId);
168947
- if (hasRunningProcess) {
168948
- await this.sessionManager.terminateSession(sessionId, true);
168949
- }
168950
- await sessionDoc.setStatus(SessionStatusFactory.idle());
168951
- stoppedCount += 1;
168952
- } catch (error2) {
168953
- this.logger.debug(`[${sessionId}] Failed to stop active session before local project removal: ${formatErrorMessage(error2)}`);
168954
- }
169233
+ async archiveLocalProjectSessions(localProjectId) {
169234
+ const sessions = (await listAliveSessionMetas(this.workspaceDocument)).filter(({ meta }) => isSessionInLocalProjectRemovalScope(meta, {
169235
+ machineId: this.machineId,
169236
+ localProjectId
169237
+ }));
169238
+ if (sessions.length === 0) return;
169239
+ const rootSessions = sessions.filter(({ meta }) => meta.isArchived !== true && !meta.parentSessionId);
169240
+ const archivedRootSessionIds = new Set(rootSessions.map(({ meta }) => meta.id));
169241
+ for (const { meta } of rootSessions) {
169242
+ await this.archiveSessionResources(meta.id, {
169243
+ preserveWorktree: true
169244
+ });
168955
169245
  }
168956
- if (stoppedCount > 0) {
168957
- this.logger.debug(`[local-project] Stopped ${stoppedCount} active session(s) before removing ${localProjectId}`);
169246
+ for (const { roomId, meta } of sessions) {
169247
+ if (archivedRootSessionIds.has(meta.id)) continue;
169248
+ if (this.sessionManager.hasSession(meta.id)) {
169249
+ await this.archiveSessionResources(meta.id, {
169250
+ preserveWorktree: true
169251
+ });
169252
+ continue;
169253
+ }
169254
+ if (meta.isArchived === true) continue;
169255
+ await this.workspaceDocument.repo.upsertDocMeta(roomId, {
169256
+ isArchived: true,
169257
+ status: SessionStatusFactory.idle()
169258
+ });
168958
169259
  }
169260
+ this.logger.debug(`[local-project] Processed ${sessions.length} session(s) before removing ${localProjectId}`);
168959
169261
  }
168960
169262
  async deleteLocalProjectResources(localProjectId, command2) {
168961
169263
  const existingProject = await resolveWorkspaceLocalProject(this.workspaceDocument.repo, this.workspaceId, this.machineId, localProjectId);
168962
- if (!existingProject) {
169264
+ if (!existingProject && !command2.cleanupWorktrees) {
168963
169265
  this.logger.debug(`[local-project] ${localProjectId} already removed`);
168964
169266
  await this.cleanupLocalProjectWorktreeSetup(localProjectId);
168965
- return;
169267
+ return void 0;
168966
169268
  }
168967
- if (!shouldApplyMachineDeleteLocalProjectCommand(existingProject, command2)) {
169269
+ if (existingProject && !shouldApplyMachineDeleteLocalProjectCommand(existingProject, command2)) {
168968
169270
  this.logger.debug(`[local-project] Skipping stale delete request for ${localProjectId}; project was created after the request`);
168969
- return;
169271
+ return void 0;
169272
+ }
169273
+ await this.archiveLocalProjectSessions(localProjectId);
169274
+ let cleanupResult;
169275
+ const originalRootPath = existingProject?.rootPath ?? command2.originalRootPath;
169276
+ if (command2.cleanupWorktrees && originalRootPath) {
169277
+ const sessions = (await listAliveSessionMetas(this.workspaceDocument)).map(({ meta }) => meta);
169278
+ cleanupResult = await cleanupLocalProjectWorktrees({
169279
+ machineId: this.machineId,
169280
+ localProjectId,
169281
+ originalRootPath,
169282
+ sessions,
169283
+ logger: this.logger
169284
+ });
169285
+ }
169286
+ if (existingProject) {
169287
+ await removeMachineLocalProject(this.workspaceDocument.repo, this.workspaceId, this.machineId, localProjectId, void 0, {
169288
+ sync: this.workspaceDocument,
169289
+ reason: "local-project-delete-command"
169290
+ });
169291
+ this.logger.debug(`[local-project] Removed ${existingProject.name} (${existingProject.rootPath})`);
168970
169292
  }
168971
- await this.stopActiveLocalProjectSessions(localProjectId);
168972
- await removeMachineLocalProject(this.workspaceDocument.repo, this.workspaceId, this.machineId, localProjectId, void 0, {
168973
- sync: this.workspaceDocument,
168974
- reason: "local-project-delete-command"
168975
- });
168976
- this.logger.debug(`[local-project] Removed ${existingProject.name} (${existingProject.rootPath})`);
168977
169293
  await this.cleanupLocalProjectWorktreeSetup(localProjectId);
169294
+ return cleanupResult;
168978
169295
  }
168979
169296
  toDeleteRequestRecord(request2) {
168980
169297
  if (!request2 || typeof request2 !== "object") {
@@ -171449,13 +171766,19 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
171449
171766
  }
171450
171767
  const uploadedFiles = [];
171451
171768
  const failures2 = [];
171769
+ const canonicalWorkspaceRoot = await fs__default.promises.realpath(workspaceRoot);
171452
171770
  for (const file2 of validatedFiles) {
171453
171771
  try {
171454
- uploadedFiles.push(await this.uploadValidatedSessionFile({
171772
+ const uploaded = await this.uploadValidatedSessionFile({
171455
171773
  workspaceId: this.workspaceId,
171456
171774
  sessionId,
171457
171775
  file: file2
171458
- }));
171776
+ });
171777
+ const sourcePath = normalizeSessionFileSourcePath(path__default$1.relative(canonicalWorkspaceRoot, file2.absolutePath));
171778
+ uploadedFiles.push({
171779
+ ...uploaded,
171780
+ sourcePath
171781
+ });
171459
171782
  } catch (error2) {
171460
171783
  failures2.push(`${file2.fileName}: ${formatErrorMessage(error2)}`);
171461
171784
  }
@@ -172701,6 +173024,20 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
172701
173024
  return authorized.response;
172702
173025
  }
172703
173026
  const { rootPath } = authorized;
173027
+ if (request2.type === "local-project/removal-preflight") {
173028
+ const sessions = (await listAliveSessionMetas(this.workspaceDocument)).map(({ meta }) => meta);
173029
+ return {
173030
+ ok: true,
173031
+ type: "local-project/removal-preflight",
173032
+ result: await preflightLocalProjectWorktreeRemoval({
173033
+ machineId: this.machineId,
173034
+ localProjectId: request2.localProjectId,
173035
+ originalRootPath: rootPath,
173036
+ sessions,
173037
+ logger: this.logger
173038
+ })
173039
+ };
173040
+ }
172704
173041
  if (isLocalProjectWorktreeConfigRequest(request2)) {
172705
173042
  return await handleLocalProjectWorktreeConfigRequest(request2);
172706
173043
  }
@@ -181854,7 +182191,8 @@ export PATH=${toSingleQuotedShellString(ghShimBinDir)}:"$PATH"
181854
182191
  client2 = started.client;
181855
182192
  acpSessionId = started.acpSessionId;
181856
182193
  acpCapabilities = normalizeAcpSessionCapabilities(started.sessionResponse, {
181857
- sessionFork: started.client.supportsSessionFork()
182194
+ sessionFork: started.client.supportsSessionFork(),
182195
+ acknowledgedSteer: started.client.supportsAcknowledgedSteer()
181858
182196
  });
181859
182197
  } catch (error2) {
181860
182198
  if (error2 instanceof AcpStartupProcessExitError || error2 instanceof AcpStartupProcessError) {
@@ -191381,6 +191719,22 @@ ${trimmed2}${closing}`;
191381
191719
  }
191382
191720
  };
191383
191721
  }
191722
+ if (message.type === "local-project/removal-preflight") {
191723
+ const runtime = await this.resolveWorkspaceRuntime(message.workspaceId);
191724
+ const rootPath = await this.resolveWorkspaceProjectRootPath(runtime, message.localProjectId);
191725
+ const sessions = (await listAliveSessionMetas(runtime.lody.documentManager)).map(({ meta }) => meta);
191726
+ return {
191727
+ ok: true,
191728
+ type: "local-project/removal-preflight",
191729
+ result: await preflightLocalProjectWorktreeRemoval({
191730
+ machineId: this.machineId,
191731
+ localProjectId: message.localProjectId,
191732
+ originalRootPath: rootPath,
191733
+ sessions,
191734
+ logger: this.logger
191735
+ })
191736
+ };
191737
+ }
191384
191738
  if (message.type === "local-project/list") {
191385
191739
  return {
191386
191740
  ok: true,
@@ -220289,7 +220643,7 @@ ${page}${helpTipBottom}${choiceDescription}${ansiEscapes.cursorHide}`;
220289
220643
  data = createReviewBundleSnapshot(bundle);
220290
220644
  }
220291
220645
  const { injectReviewSnapshot } = await import("./chunks/index-VoI6Ds2-.js");
220292
- const { resolveReviewViewerTemplate } = await import("./chunks/review-viewer-Bcj5lkU7.js").then(async (m) => {
220646
+ const { resolveReviewViewerTemplate } = await import("./chunks/review-viewer-D1hRLw2L.js").then(async (m) => {
220293
220647
  await m.__tla;
220294
220648
  return m;
220295
220649
  });
@@ -238735,19 +239089,14 @@ ${result.stdout ?? ""}`;
238735
239089
  "agentConfigId",
238736
239090
  ...Object.keys(SessionRunConfigInputShape)
238737
239091
  ];
238738
- const addAgentRoleOverrideIssues = (value, ctx, pathPrefix = []) => {
238739
- if (value.agentRoleId === void 0) return;
239092
+ const omitAgentRoleManualOverrides = (input2) => {
239093
+ const normalized = {
239094
+ ...input2
239095
+ };
238740
239096
  for (const field of AGENT_ROLE_MANUAL_OVERRIDE_FIELDS) {
238741
- if (value[field] === void 0) continue;
238742
- ctx.addIssue({
238743
- code: "custom",
238744
- path: [
238745
- ...pathPrefix,
238746
- field
238747
- ],
238748
- message: `${field} cannot be combined with agentRoleId.`
238749
- });
239097
+ delete normalized[field];
238750
239098
  }
239099
+ return normalized;
238751
239100
  };
238752
239101
  const SessionCreateRuntimeInputSchema = xor([
238753
239102
  object$1({
@@ -238755,7 +239104,7 @@ ${result.stdout ?? ""}`;
238755
239104
  resume: literal$1(true)
238756
239105
  }).strict(),
238757
239106
  ...sessionCreateCommandSchemas
238758
- ]).superRefine((value, ctx) => addAgentRoleOverrideIssues(value, ctx));
239107
+ ]);
238759
239108
  const SessionCreateToolInputSchema = object$1({
238760
239109
  ...SessionCreateCommandInputShape,
238761
239110
  prompt: SessionCreateCommandInputShape.prompt.optional(),
@@ -238882,18 +239231,7 @@ ${result.stdout ?? ""}`;
238882
239231
  }).strict(),
238883
239232
  items: array$2(SessionCreateBatchItemWithInheritedWorkContextSchema).max(1e3)
238884
239233
  }).strict()
238885
- ]).superRefine((value, ctx) => {
238886
- value.items.forEach((item, index) => {
238887
- const expanded = {
238888
- ...value.defaults ?? {},
238889
- ...item
238890
- };
238891
- addAgentRoleOverrideIssues(expanded, ctx, [
238892
- "items",
238893
- index
238894
- ]);
238895
- });
238896
- });
239234
+ ]);
238897
239235
  const SessionCreateBatchPublishedItemSchema = object$1({
238898
239236
  ...SessionCreateBatchItemShape,
238899
239237
  useCurrentSessionAsParent: boolean().optional().describe("Create a child of the current Session; cannot be combined with workContext."),
@@ -239241,7 +239579,7 @@ ${prompt2}` : prompt2;
239241
239579
  useCurrentSessionAsParent = true;
239242
239580
  }
239243
239581
  const resolvedInput = {
239244
- ...input2,
239582
+ ...omitAgentRoleManualOverrides(input2),
239245
239583
  machineId: role.machineId,
239246
239584
  agentConfigId: role.agentConfigId,
239247
239585
  ...useCurrentSessionAsParent !== void 0 ? {
@@ -240268,6 +240606,15 @@ ${prompt2}` : prompt2;
240268
240606
  throw new Error("Create result did not preserve preallocated target ids.");
240269
240607
  }
240270
240608
  await withOperationStore((store) => store.markItemInputDurable(ctx.sessionId, args2.operationId, 0, materializationClaimToken));
240609
+ captureSessionCommandEvent("session_create_succeeded", {
240610
+ created_via: "mcp",
240611
+ mcp_create_mode: "single",
240612
+ session_id: result.sessionId,
240613
+ has_agent_role: Boolean(resolved.role),
240614
+ is_child_session: Boolean(result.parentSessionId)
240615
+ }, {
240616
+ distinctId: auth.machineId
240617
+ });
240271
240618
  }
240272
240619
  return snapshotOperation(ctx.sessionId, args2.operationId);
240273
240620
  });
@@ -240594,8 +240941,17 @@ ${prompt2}` : prompt2;
240594
240941
  options.chainDepth = invoking.chainDepth + 1;
240595
240942
  options.bypassSessionQuota = shouldBypassSessionQuota("session_create_many");
240596
240943
  options.workspaceMetaPrewriteSatisfied = true;
240597
- await createSessionResult(auth, workspace, manager, resolved.prompt, options, targetDispatchConfigs[index] ?? resolved.dispatchConfig);
240944
+ const result = await createSessionResult(auth, workspace, manager, resolved.prompt, options, targetDispatchConfigs[index] ?? resolved.dispatchConfig);
240598
240945
  await withOperationStore((store) => store.markItemInputDurable(ctx.sessionId, args2.operationId, index, materializationClaimToken));
240946
+ captureSessionCommandEvent("session_create_succeeded", {
240947
+ created_via: "mcp",
240948
+ mcp_create_mode: "batch",
240949
+ session_id: result.sessionId,
240950
+ has_agent_role: Boolean(resolved.role),
240951
+ is_child_session: Boolean(result.parentSessionId)
240952
+ }, {
240953
+ distinctId: auth.machineId
240954
+ });
240599
240955
  return markOperationItemInputDurable(storedItem);
240600
240956
  } catch {
240601
240957
  return storedItem;
@@ -240965,6 +241321,7 @@ ${prompt2}` : prompt2;
240965
241321
  };
240966
241322
  };
240967
241323
  function buildLodyMcpServer(config2 = {}) {
241324
+ initCliAnalytics();
240968
241325
  const server = new McpServer({
240969
241326
  name: "lody",
240970
241327
  version: "0.1.0"
@@ -240997,6 +241354,14 @@ ${prompt2}` : prompt2;
240997
241354
  const workspace = await resolveWorkspaceOrThrow$1(auth, getMcpWorkspaceId(ctx));
240998
241355
  return await withWorkspaceManager$1(auth, workspace, "mcp", async (manager) => {
240999
241356
  const result = await configureWorkspaceMcpServer(manager, workspace.id, auth.userId, args2);
241357
+ captureCli("workspace/mcp_created", {
241358
+ workspace_id: workspace.id,
241359
+ source: "mcp",
241360
+ transport: result.server.transport,
241361
+ enabled_by_default: result.server.enabledByDefault === true,
241362
+ has_description: Boolean(result.server.description),
241363
+ synced: result.synced
241364
+ });
241000
241365
  return jsonTextResult({
241001
241366
  ok: true,
241002
241367
  ...result,
@@ -241154,7 +241519,7 @@ ${lines2.join("\n")}` : ""}${suffix}`);
241154
241519
  });
241155
241520
  server.registerTool(SESSION_CREATE_TOOL_NAME, {
241156
241521
  title: "Create a Lody session",
241157
- description: "Start durable asynchronous work that creates a Lody session. Supply operationId; the result arrives automatically as a continuation, so do not poll operation_get. To recover an already accepted create without resending its prompt, send only operationId with resume=true. To use an Agent Role, pass its agentRoleId and do not pass machineId, agentConfigId, modelId, reasoningEffort, fastMode, or planMode; the current workspace catalog row supplies them. useCurrentSessionAsParent=true and workContext are mutually exclusive schema branches. Machine/config ids and runConfig values for non-Role creates come from lody_session_create_options. The wait field is temporary legacy compatibility only.",
241522
+ description: "Start durable asynchronous work that creates a Lody session. Supply operationId; the result arrives automatically as a continuation, so do not poll operation_get. To use an Agent Role, pass agentRoleId; the current workspace catalog row supplies the exact Machine, Agent config, model, reasoning, and permission mode. If manual machine or run-config fields are also present, the Role takes precedence and those fields are ignored. To recover an already accepted create without resending its prompt, send only operationId with resume=true. useCurrentSessionAsParent=true and workContext are mutually exclusive schema branches. Machine/config ids and runConfig values for non-Role creates come from lody_session_create_options. The wait field is temporary legacy compatibility only.",
241158
241523
  inputSchema: SessionCreateToolInputSchema
241159
241524
  }, async (input2) => {
241160
241525
  try {
@@ -241183,6 +241548,15 @@ ${lines2.join("\n")}` : ""}${suffix}`);
241183
241548
  bindAgentRoleCreateOptions(options, resolved.role);
241184
241549
  options.workspaceMetaPrewriteSatisfied = true;
241185
241550
  const result = await createSessionResult(auth, workspace, manager, resolved.prompt, options, resolved.dispatchConfig, buildStructuredOutputOptions(args2));
241551
+ captureSessionCommandEvent("session_create_succeeded", {
241552
+ created_via: "mcp",
241553
+ mcp_create_mode: "legacy_single",
241554
+ session_id: result.sessionId,
241555
+ has_agent_role: Boolean(resolved.role),
241556
+ is_child_session: Boolean(result.parentSessionId)
241557
+ }, {
241558
+ distinctId: auth.machineId
241559
+ });
241186
241560
  const response2 = {
241187
241561
  ok: true,
241188
241562
  sessionId: result.sessionId,
@@ -241261,7 +241635,7 @@ ${lines2.join("\n")}` : ""}${suffix}`);
241261
241635
  });
241262
241636
  server.registerTool(SESSION_CREATE_MANY_TOOL_NAME, {
241263
241637
  title: "Create multiple Lody sessions",
241264
- description: "Start one durable batch Operation for 1-20 Session creates. defaults and items shallow-merge; nested objects replace wholesale. Each item may use an agentRoleId from the workspace catalog; Role items cannot also override machine, agent config, or run config. Non-Role items accept modelId, reasoningEffort, fastMode, and planMode. Ordered item failures are isolated. Completion arrives automatically as one continuation, so do not poll operation_get in a loop.",
241638
+ description: "Start one durable batch Operation for 1-20 Session creates. defaults and items shallow-merge; nested objects replace wholesale. Each item may use an agentRoleId from the workspace catalog. When a Role item also includes manual machine, agent config, or run-config fields, the Role takes precedence and those fields are ignored. Non-Role items accept modelId, reasoningEffort, fastMode, and planMode. Ordered item failures are isolated. Completion arrives automatically as one continuation, so do not poll operation_get in a loop.",
241265
241639
  inputSchema: SessionCreateManyToolInputSchema
241266
241640
  }, async (input2) => {
241267
241641
  try {
@@ -243627,6 +244001,13 @@ Other machines will not see it until this machine syncs.`);
243627
244001
  createdBy: userId
243628
244002
  };
243629
244003
  const result = await upsertWorkspaceMcpCatalogEntry(repo, workspaceId, entry, writeOptions);
244004
+ captureCli("workspace/mcp_created", {
244005
+ workspace_id: workspaceId,
244006
+ source: "cli",
244007
+ transport: entry.transport,
244008
+ enabled_by_default: entry.enabledByDefault === true,
244009
+ has_description: Boolean(entry.description)
244010
+ });
243630
244011
  reportWrite(options, {
243631
244012
  workspaceId,
243632
244013
  server: entry