@dev.fast/whiteboard 0.2.5-preview.20261013.113 → 0.2.5-preview.20261015.116

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.
Files changed (127) hide show
  1. package/THIRD_PARTY_NOTICES.md +29 -0
  2. package/dist/{account-alias-D0hF0sKk.js → account-alias-ChZR3RIU.js} +94 -8
  3. package/dist/{agent-cli-04xbDj23.js → agent-cli-CR2PnA9G.js} +9 -12
  4. package/dist/agent-cli-CRPPmS3l.js +2 -0
  5. package/dist/{agent-client-P3J4y8Zn.js → agent-client-DVd-prfp.js} +31 -14
  6. package/dist/{authoring-tools-fdA0RPIT.js → authoring-tools-BXKEwhfi.js} +5 -5
  7. package/dist/background-server-Cw7CcfaZ.js +156 -0
  8. package/dist/background-server-D2Rrr_e2.js +2 -0
  9. package/dist/build-info.json +1 -1
  10. package/dist/{cli-C0ziP6hg.js → cli-CSrz_wGk.js} +1 -1
  11. package/dist/{cli-runner-C3men22A.js → cli-runner-PlGqBeMw.js} +368 -252
  12. package/dist/cli.js +3 -2
  13. package/dist/{client-DzcSt6pV.js → client-DhZzPePs.js} +5 -3
  14. package/dist/{process-error-telemetry-BCBYp2t4.js → headless-host-CVXTSIlx.js} +3285 -2585
  15. package/dist/headless-host-Cp37C-v0.js +2 -0
  16. package/dist/input-error-OLgB_h31.js +11 -0
  17. package/dist/{local-data-Dy1pNtLF.js → local-data-yfLp5PiJ.js} +113 -62
  18. package/dist/{mcp-B8kuRSXM.js → mcp-DBbDwG4y.js} +4 -4
  19. package/dist/{package-paths-B9PDoAl6.js → package-paths-BsuI4WUf.js} +1 -1
  20. package/dist/profile-BSOkA6A-.js +2 -0
  21. package/dist/{profile-tbGWuWHu.js → profile-pAzg7fxC.js} +3 -3
  22. package/dist/remote-attach-BAt2eZx3.js +137 -0
  23. package/dist/remote-extensions-2Cxf3hMo.js +2 -0
  24. package/dist/remote-extensions-BiNx6NQK.js +323 -0
  25. package/dist/remote-language-server-DQgWUNeV.js +252 -0
  26. package/dist/remote-uninstall-DLtDG0o7.js +153 -0
  27. package/dist/{request-origin-DA4K1qWY.js → request-origin-DaRIV3cB.js} +5 -2
  28. package/dist/review-app-launcher-D7I4DGoH.js +222 -0
  29. package/dist/{review-home-paths-Y9mYWkCI.js → review-home-paths-zwevIq4I.js} +7 -7
  30. package/dist/{review-telemetry-CmM6tCbj.js → review-telemetry-BqB4CiTD.js} +5 -5
  31. package/dist/{s3-SzLpeCG6.js → s3-77-qkEhs.js} +1 -1
  32. package/dist/{s3-config-BgxkSoOy.js → s3-config-0xuCh-F_.js} +1 -1
  33. package/dist/{s3-config-CAxhO9u_.js → s3-config-DDWWMh5x.js} +5 -1
  34. package/dist/server/desktop-host.js +1663 -266
  35. package/dist/{desktop-discovery-Ba3dD5Cn.js → server-discovery-CifQ3yQS.js} +88 -3
  36. package/dist/sharing/index.d.ts +88 -32
  37. package/dist/sharing/index.js +1 -1
  38. package/dist/{src-DiHP11l3.js → src-BMPuxWv4.js} +6 -11
  39. package/dist/{src-WCbabvdS.js → src-Bgvf1sEN.js} +128 -8
  40. package/dist/{stored-document-migration-u4L_1VZa.js → stored-document-migration-CFOvjRwW.js} +5 -18
  41. package/dist/{tool-failure-CJHBCHMh.js → tool-failure-BXWzQG8T.js} +2 -2
  42. package/instructions/authoring.md +16 -12
  43. package/instructions/file-lenses.md +4 -2
  44. package/package.json +8 -13
  45. package/src/agent-connect.ts +507 -0
  46. package/src/agent-selection.ts +4 -0
  47. package/src/ask/agents.ts +59 -13
  48. package/src/ask/pi-mcp.ts +7 -3
  49. package/src/ask/thread-state.ts +9 -4
  50. package/src/ask/thread.ts +88 -50
  51. package/src/ask/threads.ts +53 -7
  52. package/src/cli-install.ts +3 -1
  53. package/src/cli-runner.ts +472 -81
  54. package/src/cli.ts +1 -1
  55. package/src/connect-agents.ts +285 -0
  56. package/src/connect-prompts.ts +60 -6
  57. package/src/desktop-discovery.ts +24 -0
  58. package/src/pinned-download.ts +118 -0
  59. package/src/remote-attach.ts +130 -0
  60. package/src/remote-extensions.ts +359 -0
  61. package/src/remote-language-server.ts +428 -0
  62. package/src/remote-toolchains.ts +107 -0
  63. package/src/remote-uninstall.ts +280 -0
  64. package/src/review-api/README.md +1 -3
  65. package/src/review-api/agent-cli.ts +4 -4
  66. package/src/review-api/agent-client.ts +23 -0
  67. package/src/review-api/ask-history.ts +31 -16
  68. package/src/review-api/authoring-tools.ts +1 -1
  69. package/src/review-api/blocks/index.ts +2 -4
  70. package/src/review-api/document.ts +1 -4
  71. package/src/review-api/http.ts +265 -237
  72. package/src/review-api/local-data.ts +18 -3
  73. package/src/review-api/onboarding.ts +478 -0
  74. package/src/review-api/origin.ts +7 -0
  75. package/src/review-api/recent-folders.ts +173 -0
  76. package/src/review-api/store.ts +56 -10
  77. package/src/review-api/workspaces.ts +56 -11
  78. package/src/review-app-launcher.ts +61 -3
  79. package/src/review-stack.ts +162 -36
  80. package/src/server/background-server.ts +250 -0
  81. package/src/server/desktop-host-shutdown.ts +24 -0
  82. package/src/server/desktop-host.ts +39 -24
  83. package/src/server/desktop-server.ts +68 -118
  84. package/src/server/global-verb-relay.ts +16 -0
  85. package/src/server/headless-host.ts +128 -3
  86. package/src/server/hono-http.ts +6 -1
  87. package/src/server/legacy-tutorial-cleanup.ts +38 -0
  88. package/src/server/review-gateway-hosts.ts +379 -0
  89. package/src/server/review-gateway-list.ts +109 -0
  90. package/src/server/review-gateway-memory.ts +130 -0
  91. package/src/server/review-gateway-pushes.ts +142 -0
  92. package/src/server/review-gateway-streams.ts +415 -0
  93. package/src/server/review-gateway-transport.ts +204 -0
  94. package/src/server/review-gateway.ts +433 -0
  95. package/src/server/review-server-core.ts +36 -6
  96. package/src/server-discovery.ts +11 -0
  97. package/src/sharing/import.ts +1 -0
  98. package/src/ui-telemetry-events.ts +5 -1
  99. package/src/vsix.ts +122 -0
  100. package/dist/agent-cli-BR_Jw5BL.js +0 -2
  101. package/dist/headless-host-BLLJ7Dyu.js +0 -95
  102. package/dist/profile-aMSw1oCM.js +0 -2
  103. package/dist/server-discovery-ZVi8Ay8J.js +0 -66
  104. package/src/fixtures/blocks/tutorial.json +0 -14
  105. package/src/review-api/blocks/tutorial.ts +0 -23
  106. package/src/server/tutorial-service.ts +0 -217
  107. package/tutorial/document.json +0 -216
  108. package/tutorial/lenses.json +0 -10
  109. package/tutorial/runtime-manifest.json +0 -11
  110. package/tutorial/sample-service/package.json +0 -9
  111. package/tutorial/sample-service/src/api/checkout-api.ts +0 -17
  112. package/tutorial/sample-service/src/app.ts +0 -26
  113. package/tutorial/sample-service/src/database/drizzle-orm.d.ts +0 -16
  114. package/tutorial/sample-service/src/database/schema.ts +0 -9
  115. package/tutorial/sample-service/src/fulfillment/fulfillment-queue.ts +0 -15
  116. package/tutorial/sample-service/src/fulfillment/fulfillment-worker.ts +0 -24
  117. package/tutorial/sample-service/src/inventory/forklift-mood.ts +0 -4
  118. package/tutorial/sample-service/src/inventory/inventory-service.ts +0 -28
  119. package/tutorial/sample-service/src/inventory/warehouse-lore.ts +0 -11
  120. package/tutorial/sample-service/src/orders/order-service.ts +0 -35
  121. package/tutorial/sample-service/src/orders/order.ts +0 -43
  122. package/tutorial/sample-service/src/orders/orders-repository.ts +0 -24
  123. package/tutorial/sample-service/src/payments/fraud-vibes.ts +0 -4
  124. package/tutorial/sample-service/src/payments/payment-gateway.ts +0 -29
  125. package/tutorial/sample-service/src/payments/raccoon-ledger.ts +0 -9
  126. package/tutorial/sample-service/src/shipping/shipping-gateway.ts +0 -15
  127. package/tutorial/sample-service/tsconfig.json +0 -12
@@ -0,0 +1,2 @@
1
+ import { i as withHeadlessServerLock, n as runHeadlessServer, r as takeOverServerHome, t as HeadlessServerBusyError } from "./headless-host-CVXTSIlx.js";
2
+ export { HeadlessServerBusyError, runHeadlessServer, takeOverServerHome, withHeadlessServerLock };
@@ -0,0 +1,11 @@
1
+ //#region src/review-api/input-error.ts
2
+ /** Deliberately safe to show to API clients, unlike filesystem/provider errors. */
3
+ var ReviewInputError = class extends Error {
4
+ status;
5
+ constructor(message, status = 400) {
6
+ super(message);
7
+ this.status = status;
8
+ }
9
+ };
10
+ //#endregion
11
+ export { ReviewInputError as t };
@@ -1,9 +1,10 @@
1
- import { C as ReviewInputError, _ as readFileAtCommit, b as resolveRevision, c as diffTrees, f as gitCommonDir, g as parseGitRemote, h as listTrackedFilesAtCommit, i as diff, l as diffWorkingTree, m as listCommitRange, n as createBlobBatchReader, o as diffFileSummariesTrees, p as jjRevisionIsConflicted, r as detectLocalVcs, s as diffFileSummariesWorkingTree, t as LocalVcsToolsMissingError, u as git, v as resolveRepoContext, y as resolveRepoContextSync } from "./src-DiHP11l3.js";
1
+ import { _ as parseGitRemote, a as diff, b as resolveRepoContextSync, c as diffFileSummariesWorkingTree, d as git, g as listTrackedFilesAtCommit, h as listCommitRange, i as detectLocalVcs, l as diffTrees, m as jjRevisionIsConflicted, n as createBlobBatchReader, p as gitCommonDir, s as diffFileSummariesTrees, t as LocalVcsToolsMissingError, u as diffWorkingTree, v as readFileAtCommit, x as resolveRevision, y as resolveRepoContext } from "./src-BMPuxWv4.js";
2
2
  import { c as jsonString, o as jsonObject, u as parseJsonText } from "./src-X9phtB2j.js";
3
- import { _ as SCRATCHPAD_REVIEW_ID, b as structuralChangeCounts, g as ReviewSelectedDiffSchema, m as ReviewApiSelectionSourceSchema, y as decodeStructuralDiffEvent } from "./src-WCbabvdS.js";
4
- import { s as safeStorageSegment } from "./review-home-paths-Y9mYWkCI.js";
5
- import { $ as unionIntervals, A as applyLensEdit, C as resourceReferences, D as summarizeEdit, E as sourceReferences, F as checkSourcePath, I as fileLineRangeSchema, K as sourceAnchors, L as requireVisibleSource, N as lensSelections, O as checkReferences, P as SourceRangeError, Q as subtractIntervals, R as sliceSourceRange, S as resourceReference, X as intersectIntervals, Y as emptyCoverage, a as defineSoftwareMap, b as pinsSchema, c as traceSchema, d as applyEdit, et as updateCoverage, f as assignFreshIds, g as explicitPins, h as elements, i as SoftwareModelValidationError, j as lensEditSchema, l as textIncludesQuote, m as editSchema, n as migrateStoredDocument, p as documentSchema, q as coverageSchema, r as migrateStoredLenses, t as liftFileLenses, u as anchorPins, v as isUnit, w as reviewTargetSchema } from "./stored-document-migration-u4L_1VZa.js";
6
- import { D as processIsAlive, O as withFileLock, P as writePrivateJsonAtomic } from "./s3-config-CAxhO9u_.js";
3
+ import { C as ReviewApiSelectionSourceSchema, M as structuralChangeCounts, O as ReviewSelectedDiffSchema, j as decodeStructuralDiffEvent, k as SCRATCHPAD_REVIEW_ID } from "./src-Bgvf1sEN.js";
4
+ import { s as safeStorageSegment } from "./review-home-paths-zwevIq4I.js";
5
+ import { $ as unionIntervals, A as applyLensEdit, C as resourceReferences, D as summarizeEdit, E as sourceReferences, F as checkSourcePath, I as fileLineRangeSchema, K as sourceAnchors, L as requireVisibleSource, N as lensSelections, O as checkReferences, P as SourceRangeError, Q as subtractIntervals, R as sliceSourceRange, S as resourceReference, X as intersectIntervals, Y as emptyCoverage, a as defineSoftwareMap, b as pinsSchema, c as traceSchema, d as applyEdit, et as updateCoverage, f as assignFreshIds, g as explicitPins, h as elements, i as SoftwareModelValidationError, j as lensEditSchema, l as textIncludesQuote, m as editSchema, n as migrateStoredDocument, p as documentSchema, q as coverageSchema, r as migrateStoredLenses, t as liftFileLenses, u as anchorPins, v as isUnit, w as reviewTargetSchema } from "./stored-document-migration-CFOvjRwW.js";
6
+ import { t as ReviewInputError } from "./input-error-OLgB_h31.js";
7
+ import { A as withFileLock, I as writePrivateJsonAtomic, O as processIsAlive } from "./s3-config-DDWWMh5x.js";
7
8
  import { t as errorMessage } from "./error-message-OtiDonty.js";
8
9
  import { r as isMissingFileError } from "./fs-utils-BMPLt0cr.js";
9
10
  import { createRequire } from "node:module";
@@ -1665,26 +1666,30 @@ var ReviewActivity = class {
1665
1666
  };
1666
1667
  /** A semantic selection, independent of comment/thread creation. */
1667
1668
  const AgentSelectionSchema = z.strictObject({
1668
- target: z.discriminatedUnion("kind", [z.strictObject({
1669
- kind: z.literal("text"),
1670
- quote: z.string(),
1671
- /** Where the quote is: characters `start` to `end` of the text of the
1672
- * review block `blockId`, which was `text` then. The block keeps its
1673
- * id across versions; comparing its text then and now finds the quote
1674
- * again, or tells that it changed. */
1675
- anchor: z.strictObject({
1676
- blockId: z.string().min(1).max(200),
1677
- start: z.number().int().nonnegative(),
1678
- end: z.number().int().positive(),
1679
- text: z.string().max(2e4)
1680
- }).refine((anchor) => anchor.start < anchor.end, "an empty anchor").optional()
1681
- }), z.strictObject({
1682
- kind: z.literal("code"),
1683
- path: z.string(),
1684
- side: z.enum(["base", "head"]),
1685
- startLine: z.number().int().positive(),
1686
- endLine: z.number().int().positive()
1687
- })]),
1669
+ target: z.discriminatedUnion("kind", [
1670
+ z.strictObject({
1671
+ kind: z.literal("text"),
1672
+ quote: z.string(),
1673
+ /** Where the quote is: characters `start` to `end` of the text of the
1674
+ * review block `blockId`, which was `text` then. The block keeps its
1675
+ * id across versions; comparing its text then and now finds the quote
1676
+ * again, or tells that it changed. */
1677
+ anchor: z.strictObject({
1678
+ blockId: z.string().min(1).max(200),
1679
+ start: z.number().int().nonnegative(),
1680
+ end: z.number().int().positive(),
1681
+ text: z.string().max(2e4)
1682
+ }).refine((anchor) => anchor.start < anchor.end, "an empty anchor").optional()
1683
+ }),
1684
+ z.strictObject({
1685
+ kind: z.literal("code"),
1686
+ path: z.string(),
1687
+ side: z.enum(["base", "head"]),
1688
+ startLine: z.number().int().positive(),
1689
+ endLine: z.number().int().positive()
1690
+ }),
1691
+ z.strictObject({ kind: z.literal("review") })
1692
+ ]),
1688
1693
  title: z.string().max(500),
1689
1694
  detail: z.string().max(24e3).optional(),
1690
1695
  revision: z.string().max(200).optional(),
@@ -1694,6 +1699,7 @@ const AgentSelectionSchema = z.strictObject({
1694
1699
  function selectionMarkdown(selection, sourceExcerpt = "", diffPaths) {
1695
1700
  const target = selection.target;
1696
1701
  if (selection.selectedDiff) return selectedDiffMarkdown(selection.selectedDiff, diffPaths);
1702
+ if (target.kind === "review") return "";
1697
1703
  if (target.kind === "text") return target.quote.split(/\r?\n/).map((line) => `> ${line}`).join("\n");
1698
1704
  return [
1699
1705
  `## ${selection.title}`,
@@ -1872,6 +1878,13 @@ const statusSchema = z.enum([
1872
1878
  "idle",
1873
1879
  "failed"
1874
1880
  ]);
1881
+ /** What an agent may do without asking: nothing that changes the checkout,
1882
+ * what its own judgment calls safe, or everything. */
1883
+ const askPermissionsSchema = z.enum([
1884
+ "read-only",
1885
+ "auto",
1886
+ "bypass"
1887
+ ]);
1875
1888
  /** Everything the Ask panel renders. A watcher gets it whole once, then
1876
1889
  * follows it through `AskChange`s. */
1877
1890
  const askThreadStateSchema = z.object({
@@ -1884,8 +1897,7 @@ const askThreadStateSchema = z.object({
1884
1897
  signIn: z.string().optional(),
1885
1898
  /** The mode that keeps the agent from changing files was accepted. */
1886
1899
  readOnly: z.boolean(),
1887
- /** The agent edits and runs commands without asking. */
1888
- bypass: z.boolean(),
1900
+ permissions: askPermissionsSchema,
1889
1901
  head: z.string(),
1890
1902
  /** The checkout the agent works in. */
1891
1903
  cwd: z.string(),
@@ -1913,7 +1925,7 @@ const askChangeSchema = z.discriminatedUnion("type", [
1913
1925
  type: z.literal("set"),
1914
1926
  status: statusSchema.optional(),
1915
1927
  readOnly: z.boolean().optional(),
1916
- bypass: z.boolean().optional(),
1928
+ permissions: askPermissionsSchema.optional(),
1917
1929
  choices: askChoicesSchema.optional(),
1918
1930
  commands: z.array(askCommandSchema).optional(),
1919
1931
  accepts: askAcceptsSchema.optional(),
@@ -1969,7 +1981,7 @@ function applyAskChange(state, change) {
1969
1981
  ...rest,
1970
1982
  status: change.status ?? state.status,
1971
1983
  readOnly: change.readOnly ?? state.readOnly,
1972
- bypass: change.bypass ?? state.bypass
1984
+ permissions: change.permissions ?? state.permissions
1973
1985
  };
1974
1986
  const choices = change.choices ?? state.choices;
1975
1987
  if (choices) next.choices = choices;
@@ -2037,8 +2049,8 @@ const askRecordSchema = z.object({
2037
2049
  cwd: z.string(),
2038
2050
  /** What the panel showed last; absent until a turn ends or it closes. */
2039
2051
  entries: z.array(askEntrySchema).optional(),
2040
- /** The agent edits and runs commands without asking. */
2041
- bypass: z.boolean().optional()
2052
+ /** What the agent may do without asking, when it reopens. */
2053
+ permissions: askPermissionsSchema.optional()
2042
2054
  });
2043
2055
  const rowSchema = z.object({
2044
2056
  id: z.string(),
@@ -2053,7 +2065,8 @@ const rowSchema = z.object({
2053
2065
  created_at: z.string(),
2054
2066
  updated_at: z.string(),
2055
2067
  entries: z.string().nullable(),
2056
- bypass: z.number()
2068
+ bypass: z.number(),
2069
+ permissions: z.string().nullable()
2057
2070
  }).transform((row) => askRecordSchema.parse({
2058
2071
  id: row.id,
2059
2072
  reviewId: row.review_id,
@@ -2067,7 +2080,7 @@ const rowSchema = z.object({
2067
2080
  createdAt: row.created_at,
2068
2081
  updatedAt: row.updated_at,
2069
2082
  entries: row.entries === null ? void 0 : JSON.parse(row.entries),
2070
- bypass: row.bypass === 1
2083
+ permissions: row.permissions ?? (row.bypass === 1 ? "bypass" : "read-only")
2071
2084
  }));
2072
2085
  /** Saved Ask conversations, per review. Shared reviews are not in
2073
2086
  * `reviews`, so there is no foreign key; deleting a review deletes its rows. */
@@ -2093,9 +2106,12 @@ var AskHistory = class {
2093
2106
  updated_at TEXT NOT NULL,
2094
2107
  entries TEXT,
2095
2108
  bypass INTEGER NOT NULL DEFAULT 0,
2109
+ permissions TEXT,
2096
2110
  UNIQUE(agent, session_id));
2097
2111
  CREATE INDEX IF NOT EXISTS ask_conversations_review ON ask_conversations(review_id, updated_at);`);
2098
- if (!db.prepare("PRAGMA table_info(ask_conversations)").all().some((column) => String(column.name) === "bypass")) db.exec("ALTER TABLE ask_conversations ADD COLUMN bypass INTEGER NOT NULL DEFAULT 0");
2112
+ const columns = new Set(db.prepare("PRAGMA table_info(ask_conversations)").all().map((column) => String(column.name)));
2113
+ if (!columns.has("bypass")) db.exec("ALTER TABLE ask_conversations ADD COLUMN bypass INTEGER NOT NULL DEFAULT 0");
2114
+ if (!columns.has("permissions")) db.exec("ALTER TABLE ask_conversations ADD COLUMN permissions TEXT");
2099
2115
  db.exec("CREATE TABLE IF NOT EXISTS ask_agent_offers(agent TEXT PRIMARY KEY, offer TEXT NOT NULL)");
2100
2116
  db.exec("CREATE TABLE IF NOT EXISTS ask_agent_model_offers(agent TEXT NOT NULL, model TEXT NOT NULL, offer TEXT NOT NULL, PRIMARY KEY(agent, model))");
2101
2117
  }
@@ -2113,13 +2129,13 @@ var AskHistory = class {
2113
2129
  }
2114
2130
  save(record) {
2115
2131
  this.assertWritable(record.reviewId);
2116
- this.db.prepare(`INSERT OR REPLACE INTO ask_conversations(id,review_id,agent,session_id,version,head,cwd,selection,title,created_at,updated_at,bypass)
2117
- VALUES(?,?,?,?,?,?,?,?,?,?,?,?)`).run(record.id, record.reviewId, record.agent, record.sessionId, record.version, record.head, record.cwd, JSON.stringify(record.selection), record.title, record.createdAt, record.updatedAt, record.bypass ? 1 : 0);
2132
+ this.db.prepare(`INSERT OR REPLACE INTO ask_conversations(id,review_id,agent,session_id,version,head,cwd,selection,title,created_at,updated_at,bypass,permissions)
2133
+ VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?)`).run(record.id, record.reviewId, record.agent, record.sessionId, record.version, record.head, record.cwd, JSON.stringify(record.selection), record.title, record.createdAt, record.updatedAt, record.permissions === "bypass" ? 1 : 0, record.permissions ?? null);
2118
2134
  }
2119
- /** Whether reopening the conversation bypasses permissions. */
2120
- setBypass(id, bypass) {
2135
+ /** What the agent may do without asking when the conversation reopens. */
2136
+ setPermissions(id, permissions) {
2121
2137
  this.assertThreadWritable(id);
2122
- this.db.prepare("UPDATE ask_conversations SET bypass=? WHERE id=?").run(bypass ? 1 : 0, id);
2138
+ this.db.prepare("UPDATE ask_conversations SET bypass=?, permissions=? WHERE id=?").run(permissions === "bypass" ? 1 : 0, permissions, id);
2123
2139
  }
2124
2140
  /** Points a conversation at a new session, when its agent could not
2125
2141
  * reopen the one it had. */
@@ -2160,7 +2176,7 @@ var AskHistory = class {
2160
2176
  /** Newest first. */
2161
2177
  list(reviewId) {
2162
2178
  return this.db.prepare("SELECT * FROM ask_conversations WHERE review_id=? ORDER BY updated_at DESC").all(reviewId).map((row) => {
2163
- const { reviewId: _reviewId, sessionId: _sessionId, version: _version, cwd: _cwd, entries, bypass: _bypass, ...entry } = rowSchema.parse(row);
2179
+ const { reviewId: _reviewId, sessionId: _sessionId, version: _version, cwd: _cwd, entries, permissions: _permissions, ...entry } = rowSchema.parse(row);
2164
2180
  const question = entries?.find((entry) => entry.kind === "user")?.text;
2165
2181
  if (question?.trim()) entry.question = question;
2166
2182
  return entry;
@@ -2187,19 +2203,21 @@ function pullRequestKey(url) {
2187
2203
  return url.toLowerCase();
2188
2204
  }
2189
2205
  /** Omission preserves identity; null detaches it without changing import metadata. */
2190
- function setPullRequest(snapshot, url) {
2206
+ function setPullRequest(snapshot, url, title) {
2191
2207
  if (url === void 0) return;
2192
2208
  if (url === null) {
2193
2209
  if (snapshot.origin) {
2194
2210
  delete snapshot.origin.pullRequestUrl;
2195
2211
  delete snapshot.origin.pullRequestNumber;
2212
+ delete snapshot.origin.pullRequestTitle;
2196
2213
  }
2197
2214
  return;
2198
2215
  }
2199
2216
  snapshot.origin = {
2200
2217
  ...snapshot.origin,
2201
2218
  pullRequestUrl: url,
2202
- pullRequestNumber: Number(url.split("/").at(-1))
2219
+ pullRequestNumber: Number(url.split("/").at(-1)),
2220
+ pullRequestTitle: title ?? (snapshot.origin?.pullRequestUrl === url ? snapshot.origin.pullRequestTitle : void 0)
2203
2221
  };
2204
2222
  }
2205
2223
  //#endregion
@@ -2677,8 +2695,8 @@ var ReviewStore = class {
2677
2695
  listener();
2678
2696
  } catch {}
2679
2697
  }
2680
- /** Managed records are discoverable even if their preparation stamp was lost. */
2681
- tutorialIds() {
2698
+ /** Reviews of the tutorial earlier versions shipped, for their removal. */
2699
+ legacyTutorialIds() {
2682
2700
  return this.db.prepare(`SELECT reviews.id FROM reviews JOIN versions ON versions.review_id=reviews.id AND versions.version=reviews.version WHERE json_extract(versions.snapshot,'$.origin.tutorial') = 1`).all().map((row) => String(row.id));
2683
2701
  }
2684
2702
  list(mode = "structural") {
@@ -2696,8 +2714,7 @@ var ReviewStore = class {
2696
2714
  JOIN versions ON versions.review_id=reviews.id AND versions.version=reviews.version
2697
2715
  LEFT JOIN review_attention ON review_attention.review_id=reviews.id
2698
2716
  LEFT JOIN repositories ON repositories.id=json_extract(versions.snapshot,'$.pins.repositoryId')
2699
- WHERE COALESCE(json_extract(versions.snapshot,'$.origin.tutorial'), 0) = 0
2700
- ${id === void 0 ? "" : "AND reviews.id=?"}
2717
+ ${id === void 0 ? "" : "WHERE reviews.id=?"}
2701
2718
  ORDER BY reviews.rowid`).all(...id === void 0 ? [] : [id]).map((row) => {
2702
2719
  const summary = JSON.parse(String(row.summary));
2703
2720
  if (summary.pins) summary.target ??= {
@@ -2763,13 +2780,14 @@ var ReviewStore = class {
2763
2780
  inspect(id, targetId, version) {
2764
2781
  return inspectSnapshot(this.read(id, version), targetId);
2765
2782
  }
2766
- /** The host can seed a managed document; transport callers only supply a command. */
2783
+ /** The host can seed a document; transport callers only supply a command. */
2767
2784
  execute(input, initial) {
2768
2785
  if (this.closing) return Promise.reject(/* @__PURE__ */ new Error("Review store is closing."));
2769
2786
  const command = commandSchema.parse(input);
2770
2787
  if (initial && command.operation.type !== "create") throw new ReviewInputError("Initial content requires a create command.");
2771
2788
  const pullRequest = this.startPullRequest(command);
2772
2789
  pullRequest?.catch(() => {});
2790
+ const pullRequestTitle = pullRequest ? pullRequest.then(({ title }) => title || void 0, () => void 0) : this.startPullRequestTitle(command);
2773
2791
  const run = this.pending.then(async () => {
2774
2792
  const op = command.operation;
2775
2793
  this.purgeExpired();
@@ -2821,7 +2839,7 @@ var ReviewStore = class {
2821
2839
  }
2822
2840
  const id = op.type !== "create" ? op.reviewId : op.kind === "scratchpad" ? SCRATCHPAD_ID : randomUUID();
2823
2841
  const previous = op.type === "create" ? void 0 : this.read(id);
2824
- let snapshot = op.type === "create" ? createdSnapshot(id, op, resolvedTarget, fromPullRequest?.title) : structuredClone(previous);
2842
+ let snapshot = op.type === "create" ? createdSnapshot(id, op, resolvedTarget, fromPullRequest && (fromPullRequest.title || `PR #${op.pullRequestUrl.split("/").at(-1)}`)) : structuredClone(previous);
2825
2843
  delete snapshot.trashedAt;
2826
2844
  delete snapshot.sourceUnavailable;
2827
2845
  delete snapshot.lastEdit;
@@ -2839,6 +2857,10 @@ var ReviewStore = class {
2839
2857
  };
2840
2858
  }
2841
2859
  }
2860
+ if (requestedTarget) snapshot.origin = {
2861
+ ...snapshot.origin,
2862
+ baseBranch: requestedTarget.base === resolvedTarget?.pins.base ? void 0 : requestedTarget.base
2863
+ };
2842
2864
  switch (op.type) {
2843
2865
  case "create":
2844
2866
  if (initial) {
@@ -2849,13 +2871,13 @@ var ReviewStore = class {
2849
2871
  };
2850
2872
  for (const block of snapshot.document) assignFreshIds(block, (prefix) => `${prefix}-${++nextId}`);
2851
2873
  }
2852
- setPullRequest(snapshot, op.pullRequestUrl);
2874
+ setPullRequest(snapshot, op.pullRequestUrl, await pullRequestTitle);
2853
2875
  break;
2854
2876
  case "rename":
2855
2877
  snapshot.title = op.title;
2856
2878
  break;
2857
2879
  case "set_target":
2858
- setPullRequest(snapshot, op.pullRequestUrl ?? (op.pullRequestUrl === null || snapshot.pins?.repositoryId !== resolvedTarget.pins.repositoryId ? null : void 0));
2880
+ setPullRequest(snapshot, op.pullRequestUrl ?? (op.pullRequestUrl === null || snapshot.pins?.repositoryId !== resolvedTarget.pins.repositoryId ? null : void 0), await pullRequestTitle);
2859
2881
  snapshot.staleSources = [];
2860
2882
  snapshot.target = resolvedTarget.target;
2861
2883
  snapshot.pins = resolvedTarget.pins;
@@ -2942,12 +2964,16 @@ var ReviewStore = class {
2942
2964
  preferred: existing && this.read(existing).pins?.repositoryId
2943
2965
  });
2944
2966
  }
2967
+ startPullRequestTitle(command) {
2968
+ const op = command.operation;
2969
+ if (op.type !== "create" && op.type !== "set_target" || !op.pullRequestUrl) return Promise.resolve(void 0);
2970
+ return this.providers.pullRequestTitle?.(op.pullRequestUrl) ?? Promise.resolve(void 0);
2971
+ }
2945
2972
  /** Reviews whose PR is this one, newest version first. Summaries only. */
2946
2973
  reviewsForPullRequest(url) {
2947
2974
  return this.db.prepare(`SELECT reviews.id FROM reviews
2948
2975
  JOIN versions ON versions.review_id=reviews.id AND versions.version=reviews.version
2949
2976
  WHERE lower(json_extract(versions.snapshot,'$.origin.pullRequestUrl'))=?
2950
- AND COALESCE(json_extract(versions.snapshot,'$.origin.tutorial'), 0) = 0
2951
2977
  ORDER BY json_extract(versions.snapshot,'$.createdAt') DESC, reviews.rowid DESC`).all(pullRequestKey(url)).map((row) => String(row.id));
2952
2978
  }
2953
2979
  assertReusableReview(reviewId) {
@@ -3114,7 +3140,7 @@ function inspectSnapshot(snapshot, targetId) {
3114
3140
  }
3115
3141
  //#endregion
3116
3142
  //#region src/review-api/workspaces.ts
3117
- const OWNED_ELSEWHERE = "Another Desktop owns this review's language workspaces. Quit that Desktop, then retry.";
3143
+ const OWNED_ELSEWHERE = "Another Whiteboard server is managing this review's language workspaces. Retry once it stops.";
3118
3144
  /** Local lifecycle only: source and authored history never depend on preparation.
3119
3145
  * Status/queue/process lifecycle follows #334, retaining the legacy prepare config
3120
3146
  * and per-review managed checkout layout instead of a new settings system.
@@ -3147,10 +3173,20 @@ var ReviewWorkspaces = class {
3147
3173
  this.db.close();
3148
3174
  throw error;
3149
3175
  }
3150
- this.stop = store.subscribeCatalog(() => {
3176
+ const stopCatalog = store.subscribeCatalog(() => {
3151
3177
  this.collect();
3152
3178
  this.releaseTrashed();
3153
3179
  });
3180
+ const stopDeleted = store.subscribe(({ reviewId, deleted }) => {
3181
+ if (!deleted) return;
3182
+ const repositories = this.repositoryDirs();
3183
+ repositories.catch(() => void 0);
3184
+ this.release(reviewId, repositories, () => !this.hasReview(reviewId));
3185
+ });
3186
+ this.stop = () => {
3187
+ stopCatalog();
3188
+ stopDeleted();
3189
+ };
3154
3190
  this.collect();
3155
3191
  this.releaseTrashed();
3156
3192
  }
@@ -3169,7 +3205,11 @@ var ReviewWorkspaces = class {
3169
3205
  if (!released.length) return;
3170
3206
  const repositories = this.repositoryDirs();
3171
3207
  repositories.catch(() => void 0);
3172
- for (const reviewId of released) this.release(reviewId, repositories);
3208
+ for (const reviewId of released) this.release(reviewId, repositories, () => this.stillTrashed(reviewId));
3209
+ }
3210
+ /** Restored since the move to Trash: its checkouts may be in use. */
3211
+ stillTrashed(reviewId) {
3212
+ return this.store.trashedIds().includes(reviewId);
3173
3213
  }
3174
3214
  async repositoryDirs() {
3175
3215
  const dirs = new Set(this.all().map((item) => item.repository));
@@ -3183,21 +3223,21 @@ var ReviewWorkspaces = class {
3183
3223
  released(reviewId) {
3184
3224
  return this.releasing.get(reviewId) ?? Promise.resolve();
3185
3225
  }
3186
- release(reviewId, repositories = this.repositoryDirs()) {
3226
+ release(reviewId, repositories, stillReleased) {
3187
3227
  const requests = [...this.requests.values()];
3188
3228
  const done = this.cleanup.then(async () => {
3189
3229
  await Promise.allSettled(requests);
3190
- await this.releaseCheckouts(reviewId, await repositories);
3230
+ await this.releaseCheckouts(reviewId, await repositories, stillReleased);
3191
3231
  }).catch((error) => {
3192
- console.error(`Could not free the pinned checkouts of trashed review ${reviewId}:`, error);
3232
+ console.error(`Could not free the pinned checkouts of review ${reviewId}:`, error);
3193
3233
  }).finally(() => {
3194
3234
  if (this.releasing.get(reviewId) === done) this.releasing.delete(reviewId);
3195
3235
  });
3196
3236
  this.releasing.set(reviewId, done);
3197
3237
  this.cleanup = done;
3198
3238
  }
3199
- async releaseCheckouts(reviewId, repositories) {
3200
- if (!this.store.trashedIds().includes(reviewId)) return;
3239
+ async releaseCheckouts(reviewId, repositories, stillReleased) {
3240
+ if (!stillReleased()) return;
3201
3241
  const environments = this.all().filter((item) => item.reviewId === reviewId);
3202
3242
  const checkouts = repositories.filter((repository) => existsSync(reviewManagedCheckoutRoot(repository, reviewId)));
3203
3243
  if (!environments.length && !checkouts.length || !this.claim(reviewId)) return;
@@ -3292,7 +3332,7 @@ var ReviewWorkspaces = class {
3292
3332
  async retryCleanup(id) {
3293
3333
  const environment = this.get(id);
3294
3334
  if (!environment || environment.state !== "cleanup-failed") throw new ReviewInputError("Cleanup failure not found.", 404);
3295
- if (this.store.has(environment.reviewId)) this.release(environment.reviewId);
3335
+ if (this.store.has(environment.reviewId)) this.release(environment.reviewId, this.repositoryDirs(), () => this.stillTrashed(environment.reviewId));
3296
3336
  else this.collect(id);
3297
3337
  await this.cleanup;
3298
3338
  }
@@ -3466,6 +3506,7 @@ var ReviewWorkspaces = class {
3466
3506
  ]);
3467
3507
  }
3468
3508
  if (environment.rootPath) await removeReviewPrepareArtifacts(environment.rootPath);
3509
+ if (environment.repository && !this.all().some((other) => other.id !== environment.id && other.reviewId === environment.reviewId && other.repository === environment.repository)) await removeReviewManagedCheckouts(environment.repository, environment.reviewId);
3469
3510
  this.db.prepare("DELETE FROM pinned_environments WHERE id=?").run(environment.id);
3470
3511
  this.db.prepare("DELETE FROM workspace_leases WHERE review_id=? AND owner=? AND NOT EXISTS(SELECT 1 FROM pinned_environments WHERE value->>'reviewId'=?)").run(environment.reviewId, this.ownerId, environment.reviewId);
3471
3512
  } catch (error) {
@@ -4263,9 +4304,18 @@ var LocalReviewData = class {
4263
4304
  base,
4264
4305
  head
4265
4306
  },
4266
- title: pullRequest.title.trim() || `PR #${address.number}`
4307
+ title: pullRequest.title.trim()
4267
4308
  };
4268
4309
  }
4310
+ async pullRequestTitle(url) {
4311
+ const { provider, address } = resolvePullRequest(url);
4312
+ try {
4313
+ const { title } = await provider.readPullRequest(address, this.options.pullRequests ?? defaultPullRequestDeps);
4314
+ return title.trim() || void 0;
4315
+ } catch {
4316
+ return;
4317
+ }
4318
+ }
4269
4319
  /** A registered checkout with a remote for host/owner/repo, and that remote. */
4270
4320
  async pullRequestCheckout(provider, address, repository, deps) {
4271
4321
  const registered = this.store.repositories();
@@ -4685,6 +4735,7 @@ function openLocalReviewStore(databasePath, options = {}) {
4685
4735
  projectSource: (snapshot, pins) => data.projectSource(snapshot, pins),
4686
4736
  resolveTarget: (target, pinned) => data.resolveTarget(target, pinned),
4687
4737
  resolvePullRequest: (url, repository) => data.resolvePullRequest(url, repository),
4738
+ pullRequestTitle: (url) => data.pullRequestTitle(url),
4688
4739
  headBranch: (pins, headRef) => data.headBranch(pins, headRef),
4689
4740
  sourcePins: (snapshot) => data.sourcePins(snapshot),
4690
4741
  unavailableAnchors: (snapshot) => data.unavailableAnchors(snapshot),
@@ -4729,4 +4780,4 @@ async function readWorkspace(file) {
4729
4780
  }
4730
4781
  }
4731
4782
  //#endregion
4732
- export { invalidateStructuralComparisons as _, inspectSnapshot as a, askAgentIds as c, askQuestionSchema as d, AgentSelectionSchema as f, activityUpdateSchema as g, activityEndSchema as h, commandSchema as i, askChoiceKinds as l, activityBeginSchema as m, uploadSchema as n, initializeReviewStoreSchema as o, selectionMarkdown as p, SCRATCHPAD_ID as r, applyAskChange as s, openLocalReviewStore as t, askPicksSchema as u, diffrExecutable as v, diffrMissingError as y };
4783
+ export { activityUpdateSchema as _, inspectSnapshot as a, diffrMissingError as b, askAgentIds as c, askPicksSchema as d, askQuestionSchema as f, activityEndSchema as g, activityBeginSchema as h, commandSchema as i, askChoiceKinds as l, selectionMarkdown as m, uploadSchema as n, initializeReviewStoreSchema as o, AgentSelectionSchema as p, SCRATCHPAD_ID as r, applyAskChange as s, openLocalReviewStore as t, askPermissionsSchema as u, invalidateStructuralComparisons as v, diffrExecutable as y };
@@ -1,7 +1,7 @@
1
- import { C as ReviewInputError } from "./src-DiHP11l3.js";
2
- import { a as toolResultText, s as ReviewApiError } from "./agent-client-P3J4y8Zn.js";
3
- import { n as REVIEW_STATUS_TOOL, t as authoringTools } from "./authoring-tools-fdA0RPIT.js";
4
- import { i as publicTool, n as RECOVERY, r as callPublicTool, t as toolFailure } from "./tool-failure-CJHBCHMh.js";
1
+ import { a as toolResultText, s as ReviewApiError } from "./agent-client-DVd-prfp.js";
2
+ import { t as ReviewInputError } from "./input-error-OLgB_h31.js";
3
+ import { n as REVIEW_STATUS_TOOL, t as authoringTools } from "./authoring-tools-BXKEwhfi.js";
4
+ import { i as publicTool, n as RECOVERY, r as callPublicTool, t as toolFailure } from "./tool-failure-BXWzQG8T.js";
5
5
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
6
6
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
7
7
  import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
@@ -1,5 +1,5 @@
1
1
  import { c as jsonString, o as jsonObject, u as parseJsonText } from "./src-X9phtB2j.js";
2
- import "./review-home-paths-Y9mYWkCI.js";
2
+ import "./review-home-paths-zwevIq4I.js";
3
3
  import { readFileSync } from "node:fs";
4
4
  import path from "node:path";
5
5
  import { fileURLToPath } from "node:url";
@@ -0,0 +1,2 @@
1
+ import { t as openReviewProfile } from "./profile-pAzg7fxC.js";
2
+ export { openReviewProfile };
@@ -1,6 +1,6 @@
1
- import { o as initializeReviewStoreSchema, t as openLocalReviewStore } from "./local-data-Dy1pNtLF.js";
2
- import "./review-home-paths-Y9mYWkCI.js";
3
- import { O as withFileLock } from "./s3-config-CAxhO9u_.js";
1
+ import { o as initializeReviewStoreSchema, t as openLocalReviewStore } from "./local-data-yfLp5PiJ.js";
2
+ import "./review-home-paths-zwevIq4I.js";
3
+ import { A as withFileLock } from "./s3-config-DDWWMh5x.js";
4
4
  import { access, mkdir } from "node:fs/promises";
5
5
  import path from "node:path";
6
6
  import { DatabaseSync } from "node:sqlite";
@@ -0,0 +1,137 @@
1
+ import { i as readReviewPackageVersion } from "./package-paths-BsuI4WUf.js";
2
+ import { a as readReviewServerHealth, l as serverNotReady } from "./server-discovery-CifQ3yQS.js";
3
+ import { c as stopBackgroundServer, o as recordedBackgroundServer, r as ensureBackgroundServer } from "./background-server-Cw7CcfaZ.js";
4
+ import { t as ensureRemoteLanguageServer } from "./remote-language-server-DQgWUNeV.js";
5
+ import path from "node:path";
6
+ import { spawn } from "node:child_process";
7
+ import { gt, valid } from "semver";
8
+ //#region src/remote-toolchains.ts
9
+ const PROBE_TIMEOUT_MS = 1e4;
10
+ const TOOLCHAINS = {
11
+ rust: [["cargo", "--version"], ["rustc", "--version"]],
12
+ swift: [["swift", "--version"]],
13
+ csharp: [["dotnet", "--version"]],
14
+ go: [["go", "version"]]
15
+ };
16
+ function probe(command, env, timeoutMs) {
17
+ const shell = env.SHELL || "/bin/sh";
18
+ const flags = ["csh", "tcsh"].includes(path.basename(shell)) ? ["-ic"] : [
19
+ "-i",
20
+ "-l",
21
+ "-c"
22
+ ];
23
+ return new Promise((resolve) => {
24
+ const child = spawn(shell, [...flags, command.join(" ")], {
25
+ env,
26
+ detached: true,
27
+ stdio: "ignore"
28
+ });
29
+ const timer = setTimeout(() => {
30
+ child.removeAllListeners("exit");
31
+ try {
32
+ process.kill(-child.pid, "SIGKILL");
33
+ } catch {}
34
+ resolve(`${command.join(" ")} did not answer within ${timeoutMs / 1e3} s`);
35
+ }, timeoutMs);
36
+ child.once("error", (error) => {
37
+ clearTimeout(timer);
38
+ resolve(`the login shell ${shell} did not run: ${error.message}`);
39
+ });
40
+ child.once("exit", (code) => {
41
+ clearTimeout(timer);
42
+ resolve(code === 0 ? "found" : code === 127 ? "missing" : `${command.join(" ")} failed with exit code ${code ?? "none"}`);
43
+ });
44
+ });
45
+ }
46
+ async function missingToolchains(groups, env, { timeoutMs = PROBE_TIMEOUT_MS, toolchains = TOOLCHAINS } = {}) {
47
+ const entries = await Promise.all(groups.map(async (group) => {
48
+ const commands = Object.hasOwn(toolchains, group) ? toolchains[group] : [];
49
+ const outcomes = await Promise.all(commands.map((command) => probe(command, env, timeoutMs)));
50
+ const missing = commands.filter((_, i) => outcomes[i] === "missing").map(([tool]) => tool);
51
+ const failures = outcomes.filter((outcome) => outcome !== "found" && outcome !== "missing");
52
+ const details = [...missing.length > 0 ? [`${missing.join(" and ")} ${missing.length > 1 ? "were" : "was"} not found on the login shell's PATH`] : [], ...failures];
53
+ return details.length > 0 ? [[group, details.join("; ")]] : [];
54
+ }));
55
+ return new Map(entries.flat());
56
+ }
57
+ //#endregion
58
+ //#region src/remote-attach.ts
59
+ async function remoteAttach(input) {
60
+ const extensions = new AbortController();
61
+ const language = ensureRemoteLanguageServer({
62
+ env: input.env,
63
+ packageRoot: input.packageRoot,
64
+ groups: input.groups,
65
+ signal: extensions.signal,
66
+ ensure: input.ensureExtensions,
67
+ installTimeoutMs: input.installTimeoutMs,
68
+ cli: input.cli
69
+ });
70
+ const toolchains = missingToolchains(input.groups ?? [], input.env);
71
+ let server;
72
+ let previousVersion;
73
+ let incompatibleRunning;
74
+ try {
75
+ const version = input.version ?? readReviewPackageVersion(import.meta.url);
76
+ const other = input.replace ? await otherVersionRunning(input.stateDir, version) : void 0;
77
+ if (other && (other.startedBy === "user" || newer(other.version, version))) incompatibleRunning = other;
78
+ else if (other) {
79
+ await stopBackgroundServer({ serverPid: other.pid });
80
+ previousVersion = other.version;
81
+ }
82
+ server = await ensureBackgroundServer({
83
+ stateDir: input.stateDir,
84
+ env: input.env,
85
+ startedBy: "desktop",
86
+ cli: input.cli
87
+ });
88
+ } catch (error) {
89
+ extensions.abort();
90
+ await Promise.all([language, toolchains]);
91
+ throw error;
92
+ }
93
+ const { discovery, started } = server;
94
+ const { languageServer, languageServerDetail, languageServerPending, languageGroups } = await language;
95
+ const missing = await toolchains;
96
+ const health = await readReviewServerHealth(discovery);
97
+ if (!health) throw serverNotReady(input.stateDir);
98
+ return {
99
+ event: "remote.attach",
100
+ version: health.version ?? null,
101
+ commit: health.commit ?? null,
102
+ serverId: health.serverId ?? null,
103
+ url: discovery.url,
104
+ token: discovery.token,
105
+ startedServer: started,
106
+ languageServer,
107
+ ...languageServerDetail !== void 0 && { languageServerDetail },
108
+ ...languageServerPending && { languageServerPending },
109
+ languageGroups: languageGroups.map(({ group, installed, detail }) => {
110
+ const details = [detail, missing.get(group)].filter(Boolean).join("; ");
111
+ return {
112
+ group,
113
+ installed,
114
+ ...details && { detail: details }
115
+ };
116
+ }),
117
+ ...previousVersion !== void 0 && {
118
+ replaced: true,
119
+ previousVersion
120
+ },
121
+ ...incompatibleRunning && { incompatibleRunning }
122
+ };
123
+ }
124
+ const newer = (running, own) => valid(running) !== null && valid(own) !== null && gt(running, own);
125
+ async function otherVersionRunning(stateDir, version) {
126
+ const recorded = await recordedBackgroundServer(stateDir);
127
+ if (!recorded) return void 0;
128
+ const { discovery, health } = recorded;
129
+ const running = health.version ?? "unknown";
130
+ return running === version ? void 0 : {
131
+ version: running,
132
+ pid: discovery.serverPid,
133
+ startedBy: discovery.startedBy
134
+ };
135
+ }
136
+ //#endregion
137
+ export { remoteAttach };
@@ -0,0 +1,2 @@
1
+ import { t as ensureRemoteExtensions } from "./remote-extensions-BiNx6NQK.js";
2
+ export { ensureRemoteExtensions };