@cursor/july 0.1.103 → 0.1.105

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 (264) hide show
  1. package/dist/channels/change-monitors/change-monitors-channel.d.ts +5 -0
  2. package/dist/channels/change-monitors/change-monitors-channel.d.ts.map +1 -0
  3. package/dist/channels/change-monitors/change-monitors-channel.js +41 -0
  4. package/dist/channels/change-monitors/index.d.ts +4 -0
  5. package/dist/channels/change-monitors/index.d.ts.map +1 -0
  6. package/dist/channels/change-monitors/index.js +2 -0
  7. package/dist/channels/change-monitors/types.d.ts +39 -0
  8. package/dist/channels/change-monitors/types.d.ts.map +1 -0
  9. package/dist/channels/change-monitors/types.js +1 -0
  10. package/dist/channels/deployments/deployments-channel.d.ts.map +1 -1
  11. package/dist/channels/deployments/deployments-channel.js +1 -0
  12. package/dist/channels/deployments/types.d.ts +2 -0
  13. package/dist/channels/deployments/types.d.ts.map +1 -1
  14. package/dist/channels/github/defaults.d.ts.map +1 -1
  15. package/dist/channels/github/defaults.js +4 -0
  16. package/dist/channels/github/progress.d.ts +22 -0
  17. package/dist/channels/github/progress.d.ts.map +1 -1
  18. package/dist/channels/github/progress.js +32 -5
  19. package/dist/channels/issues/index.d.ts +4 -0
  20. package/dist/channels/issues/index.d.ts.map +1 -0
  21. package/dist/channels/issues/index.js +2 -0
  22. package/dist/channels/issues/issues-channel.d.ts +5 -0
  23. package/dist/channels/issues/issues-channel.d.ts.map +1 -0
  24. package/dist/channels/issues/issues-channel.js +37 -0
  25. package/dist/channels/issues/types.d.ts +33 -0
  26. package/dist/channels/issues/types.d.ts.map +1 -0
  27. package/dist/channels/issues/types.js +1 -0
  28. package/dist/docs/404.html +2 -2
  29. package/dist/docs/ab.html +4 -4
  30. package/dist/docs/assets/{app.DW9pXkpL.js → app.BVwaffdK.js} +1 -1
  31. package/dist/docs/assets/chunks/@localSearchIndexroot.CJxMklAy.js +1 -0
  32. package/dist/docs/assets/chunks/{VPLocalSearchBox.CdEIUwDg.js → VPLocalSearchBox.WuWTZ1x8.js} +1 -1
  33. package/dist/docs/assets/chunks/{theme.YZMHgPgg.js → theme.BYdI_6sF.js} +2 -2
  34. package/dist/docs/assets/{guides_github.md.DKYiT9do.js → guides_github.md.c0gdGJ-D.js} +1 -1
  35. package/dist/docs/assets/{guides_github.md.DKYiT9do.lean.js → guides_github.md.c0gdGJ-D.lean.js} +1 -1
  36. package/dist/docs/assets/guides_grokbot-agents.md.DVqdhBKb.js +9 -0
  37. package/dist/docs/assets/guides_grokbot-agents.md.DVqdhBKb.lean.js +1 -0
  38. package/dist/docs/assets/index.md.BW_6tOgR.js +5 -0
  39. package/dist/docs/assets/index.md.BW_6tOgR.lean.js +1 -0
  40. package/dist/docs/assets/{reference_channels.md.Dz8Cnudp.js → reference_channels.md.DBGcaB7m.js} +2 -2
  41. package/dist/docs/assets/{reference_channels.md.Dz8Cnudp.lean.js → reference_channels.md.DBGcaB7m.lean.js} +1 -1
  42. package/dist/docs/assets/{reference_cli.md.CJNc_xYK.js → reference_cli.md.C6Lo8SEd.js} +2 -1
  43. package/dist/docs/assets/{reference_extensions.md.DykHW3qL.js → reference_extensions.md.CGmMLblt.js} +1 -1
  44. package/dist/docs/assets/templates_grokbot-agents.md.c1s6Flp9.js +3 -0
  45. package/dist/docs/assets/templates_grokbot-agents.md.c1s6Flp9.lean.js +1 -0
  46. package/dist/docs/building-with-agents.html +4 -4
  47. package/dist/docs/concepts.html +4 -4
  48. package/dist/docs/deployment.html +4 -4
  49. package/dist/docs/design/filesystem.md +376 -0
  50. package/dist/docs/evals.html +4 -4
  51. package/dist/docs/guides/agent-to-agent.html +4 -4
  52. package/dist/docs/guides/cloud-agents.html +5 -5
  53. package/dist/docs/guides/convert-automation.html +4 -4
  54. package/dist/docs/guides/github.html +6 -6
  55. package/dist/docs/guides/github.md +5 -0
  56. package/dist/docs/guides/grokbot-agents.html +35 -0
  57. package/dist/docs/guides/grokbot-agents.md +88 -0
  58. package/dist/docs/guides/human-in-the-loop.html +4 -4
  59. package/dist/docs/guides/mcp-oauth.html +4 -4
  60. package/dist/docs/guides/opentelemetry.html +5 -5
  61. package/dist/docs/guides/slack.html +4 -4
  62. package/dist/docs/guides/webhooks.html +4 -4
  63. package/dist/docs/hashmap.json +1 -1
  64. package/dist/docs/hillclimbing.html +4 -4
  65. package/dist/docs/index.html +6 -6
  66. package/dist/docs/index.md +6 -1
  67. package/dist/docs/llms-full.txt +562 -2
  68. package/dist/docs/llms.txt +3 -0
  69. package/dist/docs/quickstart.html +4 -4
  70. package/dist/docs/reference/agent-config.html +4 -4
  71. package/dist/docs/reference/artifacts.html +4 -4
  72. package/dist/docs/reference/channels.html +6 -6
  73. package/dist/docs/reference/channels.md +20 -0
  74. package/dist/docs/reference/cli.html +7 -6
  75. package/dist/docs/reference/cli.md +2 -1
  76. package/dist/docs/reference/connections.html +4 -4
  77. package/dist/docs/reference/extensions.html +6 -6
  78. package/dist/docs/reference/extensions.md +2 -0
  79. package/dist/docs/reference/hooks.html +4 -4
  80. package/dist/docs/reference/http-api.html +4 -4
  81. package/dist/docs/reference/instructions.html +4 -4
  82. package/dist/docs/reference/playground.html +4 -4
  83. package/dist/docs/reference/project-layout.html +4 -4
  84. package/dist/docs/reference/prompt.html +4 -4
  85. package/dist/docs/reference/result.html +4 -4
  86. package/dist/docs/reference/schedules.html +4 -4
  87. package/dist/docs/reference/sessions.html +4 -4
  88. package/dist/docs/reference/skills.html +4 -4
  89. package/dist/docs/reference/subagents.html +4 -4
  90. package/dist/docs/reference/tools.html +4 -4
  91. package/dist/docs/scaffolding-agents.html +4 -4
  92. package/dist/docs/skills/ab.html +4 -4
  93. package/dist/docs/skills/create-agent.html +4 -4
  94. package/dist/docs/skills/debug.html +4 -4
  95. package/dist/docs/skills/evals.html +4 -4
  96. package/dist/docs/skills/framework-map.html +4 -4
  97. package/dist/docs/skills/github.html +4 -4
  98. package/dist/docs/skills/hillclimb.html +4 -4
  99. package/dist/docs/skills/index.html +4 -4
  100. package/dist/docs/skills/mcp-auth.html +4 -4
  101. package/dist/docs/skills/otel.html +4 -4
  102. package/dist/docs/skills/setup-slack.html +4 -4
  103. package/dist/docs/storage.html +4 -4
  104. package/dist/docs/templates/agentic-owners.html +4 -4
  105. package/dist/docs/templates/agents-md.html +4 -4
  106. package/dist/docs/templates/code-wiki.html +5 -5
  107. package/dist/docs/templates/demo.html +5 -5
  108. package/dist/docs/templates/grokbot-agents.html +29 -0
  109. package/dist/docs/templates/grokbot-agents.md +48 -0
  110. package/dist/docs/templates/pr-autofixer.html +4 -4
  111. package/dist/docs/templates/security-help.html +4 -4
  112. package/dist/docs/templates/security-reviewer.html +4 -4
  113. package/dist/docs/templates/triage.html +4 -4
  114. package/dist/docs/troubleshooting.html +4 -4
  115. package/dist/files.d.ts +7 -2
  116. package/dist/files.d.ts.map +1 -1
  117. package/dist/files.js +7 -2
  118. package/dist/filesystem.d.ts +440 -0
  119. package/dist/filesystem.d.ts.map +1 -0
  120. package/dist/filesystem.js +1694 -0
  121. package/dist/index.d.ts +2 -0
  122. package/dist/index.d.ts.map +1 -1
  123. package/dist/index.js +1 -0
  124. package/dist/internal/continuation-channel.d.ts +4 -2
  125. package/dist/internal/continuation-channel.d.ts.map +1 -1
  126. package/dist/internal/continuation-channel.js +21 -12
  127. package/dist/internal/continuation-identity.d.ts +13 -5
  128. package/dist/internal/continuation-identity.d.ts.map +1 -1
  129. package/dist/internal/continuation-identity.js +74 -35
  130. package/dist/internal/cursor-agent-template.d.ts +1 -1
  131. package/dist/internal/cursor-agent-template.d.ts.map +1 -1
  132. package/dist/internal/cursor-agent-template.js +1 -0
  133. package/dist/internal/discovery/agent.d.ts.map +1 -1
  134. package/dist/internal/discovery/agent.js +80 -8
  135. package/dist/internal/discovery/info.d.ts.map +1 -1
  136. package/dist/internal/discovery/info.js +3 -0
  137. package/dist/internal/factory-resource-channel.d.ts +33 -0
  138. package/dist/internal/factory-resource-channel.d.ts.map +1 -0
  139. package/dist/internal/factory-resource-channel.js +77 -0
  140. package/dist/internal/filesystem/errors.d.ts +64 -0
  141. package/dist/internal/filesystem/errors.d.ts.map +1 -0
  142. package/dist/internal/filesystem/errors.js +175 -0
  143. package/dist/internal/filesystem/format.d.ts +28 -0
  144. package/dist/internal/filesystem/format.d.ts.map +1 -0
  145. package/dist/internal/filesystem/format.js +213 -0
  146. package/dist/internal/filesystem/merge.d.ts +34 -0
  147. package/dist/internal/filesystem/merge.d.ts.map +1 -0
  148. package/dist/internal/filesystem/merge.js +196 -0
  149. package/dist/internal/filesystem/scratch.d.ts +59 -0
  150. package/dist/internal/filesystem/scratch.d.ts.map +1 -0
  151. package/dist/internal/filesystem/scratch.js +212 -0
  152. package/dist/internal/filesystem/sdk-schemas.d.ts +29 -0
  153. package/dist/internal/filesystem/sdk-schemas.d.ts.map +1 -0
  154. package/dist/internal/filesystem/sdk-schemas.js +153 -0
  155. package/dist/internal/filesystem/tools.d.ts +42 -0
  156. package/dist/internal/filesystem/tools.d.ts.map +1 -0
  157. package/dist/internal/filesystem/tools.js +618 -0
  158. package/dist/internal/filesystem/walk.d.ts +74 -0
  159. package/dist/internal/filesystem/walk.d.ts.map +1 -0
  160. package/dist/internal/filesystem/walk.js +339 -0
  161. package/dist/internal/framework-file-storage.d.ts +3 -1
  162. package/dist/internal/framework-file-storage.d.ts.map +1 -1
  163. package/dist/internal/framework-file-storage.js +3 -1
  164. package/dist/internal/guest-network.d.ts +5 -2
  165. package/dist/internal/guest-network.d.ts.map +1 -1
  166. package/dist/internal/guest-network.js +7 -2
  167. package/dist/internal/hosted-delivery.d.ts +7 -0
  168. package/dist/internal/hosted-delivery.d.ts.map +1 -1
  169. package/dist/internal/hosted-delivery.js +41 -3
  170. package/dist/internal/hosted-managed-automation.d.ts.map +1 -1
  171. package/dist/internal/hosted-managed-automation.js +27 -2
  172. package/dist/internal/init-project.d.ts.map +1 -1
  173. package/dist/internal/init-project.js +4 -1
  174. package/dist/internal/sdk-runner.d.ts +13 -1
  175. package/dist/internal/sdk-runner.d.ts.map +1 -1
  176. package/dist/internal/sdk-runner.js +21 -1
  177. package/dist/internal/server.d.ts.map +1 -1
  178. package/dist/internal/server.js +11 -3
  179. package/dist/internal/session-engine.d.ts +30 -0
  180. package/dist/internal/session-engine.d.ts.map +1 -1
  181. package/dist/internal/session-engine.js +484 -82
  182. package/dist/internal/store-mirror.d.ts +2 -2
  183. package/dist/internal/store-mirror.js +2 -2
  184. package/dist/internal/tenant-slack/relay.d.ts +2 -0
  185. package/dist/internal/tenant-slack/relay.d.ts.map +1 -1
  186. package/dist/internal/tenant-slack/relay.js +18 -8
  187. package/dist/internal/testing/factory-resource-fixtures.d.ts +48 -0
  188. package/dist/internal/testing/factory-resource-fixtures.d.ts.map +1 -0
  189. package/dist/internal/testing/factory-resource-fixtures.js +41 -0
  190. package/dist/internal/workspace-sync.d.ts +31 -6
  191. package/dist/internal/workspace-sync.d.ts.map +1 -1
  192. package/dist/internal/workspace-sync.js +67 -12
  193. package/dist/internal/workspace.d.ts +9 -0
  194. package/dist/internal/workspace.d.ts.map +1 -1
  195. package/dist/internal/workspace.js +16 -0
  196. package/dist/playground/assets/{index-59KvgREd.js → index-BaQzjaNK.js} +40 -40
  197. package/dist/playground/assets/index-CceqnLTX.css +1 -0
  198. package/dist/playground/index.html +2 -2
  199. package/dist/types.d.ts +60 -11
  200. package/dist/types.d.ts.map +1 -1
  201. package/docs/README.md +6 -1
  202. package/docs/design/filesystem.md +376 -0
  203. package/docs/guides/github.md +5 -0
  204. package/docs/guides/grokbot-agents.md +93 -0
  205. package/docs/reference/channels.md +20 -0
  206. package/docs/reference/cli.md +2 -1
  207. package/docs/reference/extensions.md +2 -0
  208. package/docs/templates/grokbot-agents.md +53 -0
  209. package/package.json +25 -2
  210. package/src/channels/change-monitors/change-monitors-channel.ts +62 -0
  211. package/src/channels/change-monitors/index.ts +8 -0
  212. package/src/channels/change-monitors/types.ts +44 -0
  213. package/src/channels/deployments/deployments-channel.ts +1 -0
  214. package/src/channels/deployments/types.ts +2 -0
  215. package/src/channels/github/defaults.ts +4 -0
  216. package/src/channels/github/progress.ts +76 -10
  217. package/src/channels/issues/index.ts +9 -0
  218. package/src/channels/issues/issues-channel.ts +51 -0
  219. package/src/channels/issues/types.ts +41 -0
  220. package/src/files.ts +9 -2
  221. package/src/filesystem.ts +2074 -0
  222. package/src/index.ts +28 -0
  223. package/src/internal/continuation-channel.ts +29 -12
  224. package/src/internal/continuation-identity.ts +122 -37
  225. package/src/internal/cursor-agent-template.ts +1 -0
  226. package/src/internal/discovery/agent.ts +87 -1
  227. package/src/internal/discovery/info.ts +3 -0
  228. package/src/internal/factory-resource-channel.ts +122 -0
  229. package/src/internal/filesystem/errors.ts +220 -0
  230. package/src/internal/filesystem/format.ts +276 -0
  231. package/src/internal/filesystem/merge.ts +195 -0
  232. package/src/internal/filesystem/scratch.ts +255 -0
  233. package/src/internal/filesystem/sdk-schemas.ts +204 -0
  234. package/src/internal/filesystem/tools.ts +821 -0
  235. package/src/internal/filesystem/walk.ts +369 -0
  236. package/src/internal/framework-file-storage.ts +3 -1
  237. package/src/internal/guest-network.ts +8 -2
  238. package/src/internal/hosted-delivery.ts +71 -2
  239. package/src/internal/hosted-managed-automation.ts +49 -5
  240. package/src/internal/init-project.ts +4 -1
  241. package/src/internal/sdk-runner.ts +23 -0
  242. package/src/internal/server.ts +16 -0
  243. package/src/internal/session-engine.ts +595 -73
  244. package/src/internal/store-mirror.ts +2 -2
  245. package/src/internal/tenant-slack/relay.ts +28 -4
  246. package/src/internal/testing/factory-resource-fixtures.ts +85 -0
  247. package/src/internal/workspace-sync.ts +84 -12
  248. package/src/internal/workspace.ts +20 -0
  249. package/src/types.ts +58 -11
  250. package/templates/grokbot-agents/README.md +26 -0
  251. package/templates/grokbot-agents/agent/agent.ts +6 -0
  252. package/templates/grokbot-agents/agent/extensions/grokbot.ts +5 -0
  253. package/templates/grokbot-agents/agent/instructions.md +7 -0
  254. package/templates/grokbot-agents/evals/evals.config.ts +5 -0
  255. package/templates/grokbot-agents/evals/grokbot-agents.eval.ts +18 -0
  256. package/templates/grokbot-agents/init.json +18 -0
  257. package/templates/grokbot-agents/package.json +17 -0
  258. package/templates/grokbot-agents/tsconfig.json +12 -0
  259. package/dist/docs/assets/chunks/@localSearchIndexroot.nrl1ifBq.js +0 -1
  260. package/dist/docs/assets/index.md.D75GWhPl.js +0 -5
  261. package/dist/docs/assets/index.md.D75GWhPl.lean.js +0 -1
  262. package/dist/playground/assets/index-MbMxXg-y.css +0 -1
  263. /package/dist/docs/assets/{reference_cli.md.CJNc_xYK.lean.js → reference_cli.md.C6Lo8SEd.lean.js} +0 -0
  264. /package/dist/docs/assets/{reference_extensions.md.DykHW3qL.lean.js → reference_extensions.md.CGmMLblt.lean.js} +0 -0
@@ -25,6 +25,7 @@ import { SlackAskDedupeStore, SlackNudgeStore, } from "../channels/slack/nudge-s
25
25
  import { resolveDedicatedSlackHostBinding } from "../channels/slack/slack-channel.js";
26
26
  import { AGENT_SERVE_USER_STORE_FILES_SOURCE_ID, agentStoreKeys, DEPLOYMENT_STORE_MOUNT_PATH, USER_STORE_MOUNT_PATH, } from "../files.js";
27
27
  import { cursorHostedFiles, isCursorHostedFilesAvailable, isCursorUserStoreFilesAvailable, } from "../files-backends/cursor-hosted.js";
28
+ import { isFileSystemToolAlias, isMutatingFileSystemTool, localFileSystem, resolveFileSystemTools, resolveHostHarnessToolOptions, tempFs, unionFs, writeWorkspaceFilesToRoot, } from "../filesystem.js";
28
29
  import { storageKeysForArchitecture } from "../storage.js";
29
30
  import { DEFAULT_MODEL, DIRECT_TOOL_CALL_CHANNEL_ID, isCursorAccountTransport, isHostOauthConnectionTransport, isSymbolicConnectionTransport, } from "../types.js";
30
31
  import { ABCollector } from "./ab-collector.js";
@@ -43,6 +44,8 @@ import { describeError } from "./describe-error.js";
43
44
  import { isDevMode } from "./dev-mode.js";
44
45
  import { EventLogStore } from "./event-log.js";
45
46
  import { TurnEventMapper } from "./event-mapper.js";
47
+ import { applyWebScratchUpdate, ingestHostAgentToolsScratch, remapScratchPaths, } from "./filesystem/scratch.js";
48
+ import { fileSystemTool, hostFileToolsFromAuthored, rootFileToolsFromAuthored, synthesizeFileSystemTools, } from "./filesystem/tools.js";
46
49
  import { selectFrameworkStorageDefinition } from "./framework-storage-selection.js";
47
50
  import { createHostFilesApi } from "./host-files.js";
48
51
  import { createHostKvApi } from "./host-kv.js";
@@ -69,8 +72,8 @@ import { TurnGovernor } from "./turn-governor.js";
69
72
  import { TurnLeaseStore } from "./turn-lease-store.js";
70
73
  import { TurnOutbox } from "./turn-outbox.js";
71
74
  import { resolveAgentV2Hosting } from "./v2-hosting.js";
72
- import { buildAgentsMdContent, buildCloudMemorySection, buildCloudWorkspaceSection, ensureOwnGitRoot, isNestedInGitRepo, materializeWorkspace, SERVER_TOOLS_MCP_SERVER_NAME, writeWorkspaceFiles, } from "./workspace.js";
73
- import { syncWorkspaceToStore } from "./workspace-sync.js";
75
+ import { buildAgentsMdContent, buildCloudMemorySection, buildCloudSessionWorkspaceSection, buildCloudWorkspaceSection, ensureOwnGitRoot, isNestedInGitRepo, materializeWorkspace, SERVER_TOOLS_MCP_SERVER_NAME, } from "./workspace.js";
76
+ import { syncSessionWorkspaceFiles, syncWorkspaceToStore, WorkspaceFileRefusal, wipeSessionWorkspace, } from "./workspace-sync.js";
74
77
  export const HTTP_CHANNEL_ID = "http";
75
78
  export { SCHEDULE_CHANNEL_PREFIX } from "./prompt-context.js";
76
79
  export class SessionBusyError extends Error {
@@ -218,6 +221,12 @@ export class SessionEngine {
218
221
  this.cloudCostBackfillDone = new Set();
219
222
  /** Deduped in-flight cloud cost backfills, keyed by session id. */
220
223
  this.cloudCostBackfill = new Map();
224
+ /** Per-session FileSystem handle cache (not a tree snapshot). */
225
+ this.sessionRoots = new Map();
226
+ /** In-flight factory resolves, so the first turn and a racing callTool share one handle. */
227
+ this.sessionRootInflight = new Map();
228
+ /** Per-turn `unionFs({ "/": authored, "/tmp": tempFs() })`. Dropped at end of turn. */
229
+ this.turnRoots = new Map();
221
230
  this.project = options.project;
222
231
  applyAgentContinuationDefaults(this.project);
223
232
  this.stateRoot = options.stateRoot;
@@ -1252,6 +1261,224 @@ export class SessionEngine {
1252
1261
  return this.interrupt(existing.sessionId);
1253
1262
  });
1254
1263
  }
1264
+ hasRootOverride() {
1265
+ return this.project.agent.root !== undefined;
1266
+ }
1267
+ /**
1268
+ * FileSystem for this turn or `callTool`. Authored `root` (static or
1269
+ * factory) is cached per session when `cache` is set; unscoped
1270
+ * `callTool` passes the invocation id and does not cache, so factories
1271
+ * cannot share one tree across callers. A static handle is used as
1272
+ * authored — every session sees that same object. Host-default agents
1273
+ * get `localFileSystem` against the harness cwd — same path verbs as
1274
+ * the Cursor SDK natives, always through the synthesized tools.
1275
+ */
1276
+ resolveFileSystem(options) {
1277
+ return __awaiter(this, void 0, void 0, function* () {
1278
+ const authored = this.project.agent.root;
1279
+ if (authored === undefined) {
1280
+ return localFileSystem({ cwd: options.cwd });
1281
+ }
1282
+ const authoredRoot = yield this.resolveAuthoredFileSystem(options);
1283
+ if (options.cache) {
1284
+ const existing = this.turnRoots.get(options.sessionId);
1285
+ if (existing !== undefined) {
1286
+ return existing.root;
1287
+ }
1288
+ }
1289
+ const root = unionFs({
1290
+ "/": authoredRoot,
1291
+ "/tmp": tempFs(),
1292
+ });
1293
+ if (options.cache && this.isTurnRunning(options.sessionId)) {
1294
+ this.turnRoots.set(options.sessionId, { root, cwd: options.cwd });
1295
+ }
1296
+ return root;
1297
+ });
1298
+ }
1299
+ resolveAuthoredFileSystem(options) {
1300
+ return __awaiter(this, void 0, void 0, function* () {
1301
+ const authored = this.project.agent.root;
1302
+ if (authored === undefined) {
1303
+ throw new Error("resolveAuthoredFileSystem requires an authored root");
1304
+ }
1305
+ if (options.cache) {
1306
+ const cached = this.sessionRoots.get(options.sessionId);
1307
+ if (cached !== undefined) {
1308
+ return cached;
1309
+ }
1310
+ const inflight = this.sessionRootInflight.get(options.sessionId);
1311
+ if (inflight !== undefined) {
1312
+ return inflight;
1313
+ }
1314
+ }
1315
+ const resolve = () => __awaiter(this, void 0, void 0, function* () {
1316
+ if (typeof authored !== "function") {
1317
+ return authored;
1318
+ }
1319
+ const host = this.hostForSession(options.cache ? options.sessionId : undefined);
1320
+ const ctx = Object.assign(Object.assign({ sessionId: options.sessionId, agentName: this.project.agent.name }, (options.continuationKey === undefined
1321
+ ? {}
1322
+ : { continuationKey: options.continuationKey })), { host: { files: host.files, reminders: host.reminders } });
1323
+ return yield authored(ctx);
1324
+ });
1325
+ if (!options.cache) {
1326
+ return resolve();
1327
+ }
1328
+ const pending = resolve();
1329
+ this.sessionRootInflight.set(options.sessionId, pending);
1330
+ try {
1331
+ const resolved = yield pending;
1332
+ this.sessionRoots.set(options.sessionId, resolved);
1333
+ return resolved;
1334
+ }
1335
+ finally {
1336
+ this.sessionRootInflight.delete(options.sessionId);
1337
+ }
1338
+ });
1339
+ }
1340
+ dropTurnFileSystem(sessionId) {
1341
+ this.turnRoots.delete(sessionId);
1342
+ }
1343
+ prepareScratchToolInput(options) {
1344
+ return __awaiter(this, void 0, void 0, function* () {
1345
+ if (options.sessionId === undefined) {
1346
+ return options.input;
1347
+ }
1348
+ const turn = this.turnRoots.get(options.sessionId);
1349
+ if (turn === undefined) {
1350
+ return options.input;
1351
+ }
1352
+ yield ingestHostAgentToolsScratch({
1353
+ cwd: turn.cwd,
1354
+ root: turn.root,
1355
+ });
1356
+ return remapScratchPaths(options.input, turn.cwd);
1357
+ });
1358
+ }
1359
+ withTurnScratchTools(tools, sessionId) {
1360
+ const turn = this.turnRoots.get(sessionId);
1361
+ if (turn === undefined) {
1362
+ return tools;
1363
+ }
1364
+ return tools.map((tool) => {
1365
+ const execute = tool.definition.execute;
1366
+ if (typeof execute !== "function") {
1367
+ return tool;
1368
+ }
1369
+ return Object.assign(Object.assign({}, tool), { definition: Object.assign(Object.assign({}, tool.definition), { execute: (input, ctx) => __awaiter(this, void 0, void 0, function* () {
1370
+ const remapped = yield this.prepareScratchToolInput({
1371
+ sessionId,
1372
+ input: input !== null &&
1373
+ typeof input === "object" &&
1374
+ !Array.isArray(input)
1375
+ ? input
1376
+ : {},
1377
+ });
1378
+ return execute(remapped, ctx);
1379
+ }) }) });
1380
+ });
1381
+ }
1382
+ nativeFileSystemOutcome(options) {
1383
+ return __awaiter(this, void 0, void 0, function* () {
1384
+ var _a;
1385
+ const { toolName, input, callId } = options;
1386
+ if (!isFileSystemToolAlias(toolName)) {
1387
+ return undefined;
1388
+ }
1389
+ // Same policy the authored callTool path asks: a dry-run write is
1390
+ // answered by the host. Native edit/delete used to return before
1391
+ // decideToolCall and persist through the FileSystem.
1392
+ const decision = decideToolCall({
1393
+ dryRun: options.dryRun === true,
1394
+ effect: isMutatingFileSystemTool(toolName) ? "write" : "read",
1395
+ input,
1396
+ });
1397
+ if (decision.kind === "answer") {
1398
+ return {
1399
+ callId,
1400
+ toolName,
1401
+ isError: false,
1402
+ result: decision.result,
1403
+ durationMs: 0,
1404
+ sessionId: options.sessionId,
1405
+ };
1406
+ }
1407
+ const root = yield this.resolveFileSystem({
1408
+ sessionId: (_a = options.sessionId) !== null && _a !== void 0 ? _a : callId,
1409
+ continuationKey: options.continuationKey,
1410
+ cache: options.sessionId !== undefined,
1411
+ cwd: options.cwd,
1412
+ });
1413
+ const rawArgs = input !== null && typeof input === "object" && !Array.isArray(input)
1414
+ ? input
1415
+ : {};
1416
+ const args = yield this.prepareScratchToolInput({
1417
+ sessionId: options.sessionId,
1418
+ input: rawArgs,
1419
+ });
1420
+ const tool = fileSystemTool(root, toolName, args);
1421
+ const execute = tool === null || tool === void 0 ? void 0 : tool.definition.execute;
1422
+ if (tool === undefined || typeof execute !== "function") {
1423
+ // A resolved root owns this native name. Falling through would let
1424
+ // an authored lookalike run, or surface UnknownToolError after we
1425
+ // already advertised the FileSystem tool.
1426
+ return {
1427
+ callId,
1428
+ toolName,
1429
+ isError: true,
1430
+ result: {
1431
+ status: "error",
1432
+ error: { message: `FileSystem cannot handle ${toolName}` },
1433
+ },
1434
+ durationMs: 0,
1435
+ sessionId: options.sessionId,
1436
+ errorMessage: `FileSystem cannot handle ${toolName}`,
1437
+ };
1438
+ }
1439
+ const validation = validateToolInput(tool.definition.inputSchema, args);
1440
+ if (!validation.ok) {
1441
+ return {
1442
+ callId,
1443
+ toolName,
1444
+ isError: true,
1445
+ result: {
1446
+ status: "error",
1447
+ error: { message: validation.message },
1448
+ },
1449
+ durationMs: 0,
1450
+ sessionId: options.sessionId,
1451
+ errorMessage: validation.message,
1452
+ };
1453
+ }
1454
+ try {
1455
+ const value = yield execute(validation.value, {
1456
+ toolCallId: callId,
1457
+ workspaceDir: options.cwd,
1458
+ });
1459
+ return {
1460
+ callId,
1461
+ toolName,
1462
+ isError: false,
1463
+ result: normalizeToolResult(value),
1464
+ durationMs: 0,
1465
+ sessionId: options.sessionId,
1466
+ };
1467
+ }
1468
+ catch (error) {
1469
+ const message = error instanceof Error ? error.message : String(error);
1470
+ return {
1471
+ callId,
1472
+ toolName,
1473
+ isError: true,
1474
+ result: { status: "error", error: { message } },
1475
+ durationMs: 0,
1476
+ sessionId: options.sessionId,
1477
+ errorMessage: message,
1478
+ };
1479
+ }
1480
+ });
1481
+ }
1255
1482
  /**
1256
1483
  * Local harness cwd for a session: per-send override, else the shared
1257
1484
  * `agent.local.workspaceDir`, else `<agent.local.cwd>/<sessionId>`, else
@@ -1298,14 +1525,16 @@ export class SessionEngine {
1298
1525
  const workspaceDir = (_a = agent.local) === null || _a === void 0 ? void 0 : _a.workspaceDir;
1299
1526
  if (workspaceDir === undefined ||
1300
1527
  agent.runtime !== "local" ||
1528
+ this.hasRootOverride() ||
1301
1529
  this.runner.prewarm === undefined) {
1302
1530
  return;
1303
1531
  }
1304
1532
  const startedAt = Date.now();
1305
1533
  try {
1306
- const outcome = yield this.runner.prewarm(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({ workspaceDir, runnerStateDir: join(this.stateRoot, "runner"), sandbox: ((_b = agent.local) === null || _b === void 0 ? void 0 : _b.sandbox) === true }, (agent.allowedTools === undefined
1534
+ const harness = resolveHostHarnessToolOptions(agent.allowedTools);
1535
+ const outcome = yield this.runner.prewarm(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({ workspaceDir, runnerStateDir: join(this.stateRoot, "runner"), sandbox: ((_b = agent.local) === null || _b === void 0 ? void 0 : _b.sandbox) === true }, (harness.tools === undefined ? {} : { tools: harness.tools })), (harness.disallowedTools === undefined
1307
1536
  ? {}
1308
- : { tools: agent.allowedTools })), {
1537
+ : { disallowedTools: harness.disallowedTools })), {
1309
1538
  // No session yet, so auth'd connections are omitted rather than
1310
1539
  // warmed without an identity; the per-send flag still has to match
1311
1540
  // the turns' requests or the warmed executor is one no turn asks for.
@@ -1333,7 +1562,7 @@ export class SessionEngine {
1333
1562
  }
1334
1563
  executeTurn(record, turnId, message, isFirstTurn, signal) {
1335
1564
  return __awaiter(this, void 0, void 0, function* () {
1336
- var _a, _b, _c, _d, _e, _f, _g;
1565
+ var _a, _b, _c, _d, _e, _f, _g, _h;
1337
1566
  const sessionId = record.sessionId;
1338
1567
  const workspaceDir = this.scaffoldDir(record);
1339
1568
  const harnessCwd = this.harnessCwd(record);
@@ -1380,35 +1609,48 @@ export class SessionEngine {
1380
1609
  // cwd is overridden (e.g. a PR worktree). A/B variant instruction
1381
1610
  // overlays are applied via agentForSession.
1382
1611
  const agent = this.agentForSession(record);
1383
- yield materializeWorkspace({
1384
- agent,
1385
- workspaceDir,
1386
- seed: isFirstTurn,
1387
- stateRoot: this.stateRoot,
1388
- });
1389
- this.materializedWorkspaces.add(sessionId);
1390
- // Must precede the carrier: the harness resolves rules, skills,
1391
- // `mcp.json` and hooks from its git root, so a workspace nested in an
1392
- // outer repo would load that repo's hooks instead of the ones written
1393
- // just below. Only for directories this session owns a borrowed
1394
- // checkout or worktree keeps its own root.
1395
- if (!this.hasBorrowedWorkspace(record)) {
1396
- yield ensureOwnGitRoot(harnessCwd);
1612
+ if (!this.hasRootOverride()) {
1613
+ yield materializeWorkspace({
1614
+ agent,
1615
+ workspaceDir,
1616
+ seed: isFirstTurn,
1617
+ stateRoot: this.stateRoot,
1618
+ });
1619
+ this.materializedWorkspaces.add(sessionId);
1620
+ // Must precede the carrier: the harness resolves rules, skills,
1621
+ // `mcp.json` and hooks from its git root, so a workspace nested in an
1622
+ // outer repo would load that repo's hooks instead of the ones written
1623
+ // just below. Only for directories this session owns — a borrowed
1624
+ // checkout or worktree keeps its own root.
1625
+ if (!this.hasBorrowedWorkspace(record)) {
1626
+ yield ensureOwnGitRoot(harnessCwd);
1627
+ }
1628
+ yield this.materializeNudgeCarrier(record, harnessCwd);
1397
1629
  }
1398
- yield this.materializeNudgeCarrier(record, harnessCwd);
1399
1630
  // Baseline before the agent can run, so a follow-up that arrives and
1400
1631
  // drains before the first tool boundary still counts as an increase.
1401
1632
  yield this.baselineDeliveredNudges(record);
1402
1633
  const pendingFiles = record.pendingWorkspaceFiles;
1403
1634
  const pendingImages = record.pendingImages;
1404
- if (pendingFiles !== undefined &&
1405
- Object.keys(pendingFiles).length > 0) {
1406
- yield writeWorkspaceFiles(harnessCwd, pendingFiles);
1635
+ const runtime = resolveSessionRuntime({
1636
+ agentRuntime: agent.runtime,
1637
+ cloudOverride: record.cloudOverride,
1638
+ });
1639
+ const hasPendingFiles = pendingFiles !== undefined && Object.keys(pendingFiles).length > 0;
1640
+ if (hasPendingFiles && runtime !== "cloud") {
1641
+ const root = yield this.resolveFileSystem({
1642
+ sessionId,
1643
+ continuationKey: (_a = record.continuationKey) !== null && _a !== void 0 ? _a : undefined,
1644
+ cache: true,
1645
+ cwd: harnessCwd,
1646
+ });
1647
+ yield writeWorkspaceFilesToRoot(root, pendingFiles);
1407
1648
  }
1408
- if ((pendingFiles !== undefined &&
1409
- Object.keys(pendingFiles).length > 0) ||
1649
+ if ((hasPendingFiles && runtime !== "cloud") ||
1410
1650
  (pendingImages !== undefined && pendingImages.length > 0)) {
1411
- yield this.updateRecord(sessionId, (current) => (Object.assign(Object.assign({}, current), { pendingWorkspaceFiles: current.pendingWorkspaceFiles === pendingFiles
1651
+ yield this.updateRecord(sessionId, (current) => (Object.assign(Object.assign({}, current), { pendingWorkspaceFiles: hasPendingFiles &&
1652
+ runtime !== "cloud" &&
1653
+ current.pendingWorkspaceFiles === pendingFiles
1412
1654
  ? undefined
1413
1655
  : current.pendingWorkspaceFiles, pendingImages: current.pendingImages === pendingImages
1414
1656
  ? undefined
@@ -1423,6 +1665,14 @@ export class SessionEngine {
1423
1665
  (this.hasBorrowedWorkspace(record) ||
1424
1666
  (yield isNestedInGitRepo(harnessCwd)));
1425
1667
  const request = yield this.buildTurnRequest(record, agent, turnId, message, isFirstTurn, instructionsInvisibleToHarness, emit, (update) => __awaiter(this, void 0, void 0, function* () {
1668
+ const turn = this.turnRoots.get(sessionId);
1669
+ if (turn !== undefined) {
1670
+ update = yield applyWebScratchUpdate({
1671
+ cwd: turn.cwd,
1672
+ root: turn.root,
1673
+ update,
1674
+ });
1675
+ }
1426
1676
  mapper.handleUpdate(update);
1427
1677
  // Yield so the HTTP NDJSON writer can flush tool start/result
1428
1678
  // events while sibling parallel tools are still executing.
@@ -1454,6 +1704,7 @@ export class SessionEngine {
1454
1704
  }
1455
1705
  finally {
1456
1706
  permit.release();
1707
+ this.dropTurnFileSystem(sessionId);
1457
1708
  this.approvals.cancelSession(sessionId, "deny");
1458
1709
  this.auths.cancelSession(sessionId, "deny");
1459
1710
  yield this.clearPendingApprovals(sessionId);
@@ -1468,15 +1719,15 @@ export class SessionEngine {
1468
1719
  const title = mapper.title;
1469
1720
  yield this.updateRecord(sessionId, (current) => (Object.assign(Object.assign({}, current), { title })));
1470
1721
  }
1471
- const finalResult = (_a = outcome.result) !== null && _a !== void 0 ? _a : mapper.finalText;
1472
- const finalUsage = (_b = outcome.usage) !== null && _b !== void 0 ? _b : mapper.usage;
1722
+ const finalResult = (_b = outcome.result) !== null && _b !== void 0 ? _b : mapper.finalText;
1723
+ const finalUsage = (_c = outcome.usage) !== null && _c !== void 0 ? _c : mapper.usage;
1473
1724
  const finalCost = finalUsage === undefined
1474
1725
  ? undefined
1475
1726
  : yield this.estimateTurnCost({
1476
- model: (_c = this.project.agent.model) !== null && _c !== void 0 ? _c : DEFAULT_MODEL,
1727
+ model: (_d = this.project.agent.model) !== null && _d !== void 0 ? _d : DEFAULT_MODEL,
1477
1728
  usage: finalUsage,
1478
1729
  agentId: record.sdkAgentId,
1479
- conversationId: (_d = record.sdkAgentId) !== null && _d !== void 0 ? _d : sessionId,
1730
+ conversationId: (_e = record.sdkAgentId) !== null && _e !== void 0 ? _e : sessionId,
1480
1731
  });
1481
1732
  if (finalCost !== undefined) {
1482
1733
  outcome = Object.assign(Object.assign({}, outcome), { cost: finalCost });
@@ -1524,22 +1775,23 @@ export class SessionEngine {
1524
1775
  data: {
1525
1776
  message: interrupted
1526
1777
  ? "turn interrupted"
1527
- : ((_e = outcome.errorMessage) !== null && _e !== void 0 ? _e : (outcome.status === "cancelled"
1778
+ : ((_f = outcome.errorMessage) !== null && _f !== void 0 ? _f : (outcome.status === "cancelled"
1528
1779
  ? "turn cancelled"
1529
1780
  : "turn failed")),
1530
1781
  },
1531
1782
  });
1532
1783
  }
1533
1784
  const current = yield this.sessions.get(sessionId);
1534
- const mode = (_f = current === null || current === void 0 ? void 0 : current.mode) !== null && _f !== void 0 ? _f : record.mode;
1785
+ const mode = (_g = current === null || current === void 0 ? void 0 : current.mode) !== null && _g !== void 0 ? _g : record.mode;
1535
1786
  if (mode === "task") {
1536
1787
  yield this.appendEvent(sessionId, outcome.status === "finished"
1537
1788
  ? { type: "session.completed", turnId, data: {} }
1538
1789
  : {
1539
1790
  type: "session.failed",
1540
1791
  turnId,
1541
- data: { message: (_g = outcome.errorMessage) !== null && _g !== void 0 ? _g : "task failed" },
1792
+ data: { message: (_h = outcome.errorMessage) !== null && _h !== void 0 ? _h : "task failed" },
1542
1793
  });
1794
+ yield this.wipeCloudSessionWorkspace(sessionId);
1543
1795
  }
1544
1796
  else {
1545
1797
  yield this.appendEvent(sessionId, {
@@ -1582,6 +1834,58 @@ export class SessionEngine {
1582
1834
  workspacePrefix: store.workspacePrefix,
1583
1835
  }));
1584
1836
  }
1837
+ /**
1838
+ * Replace this session's store prefix with the pending `workspaceFiles`.
1839
+ * Undefined when the write did not land; the prompt must not name the
1840
+ * mount, matching ensureCloudWorkspaceSynced.
1841
+ */
1842
+ syncCloudSessionWorkspaceFiles(store, sessionId, files) {
1843
+ return __awaiter(this, void 0, void 0, function* () {
1844
+ const workspacePrefix = store.sessionWorkspacePrefix(sessionId);
1845
+ try {
1846
+ const relPaths = yield syncSessionWorkspaceFiles({
1847
+ sink: store.sink,
1848
+ files,
1849
+ workspacePrefix,
1850
+ });
1851
+ return {
1852
+ dir: `${store.mountPath}/${workspacePrefix}`,
1853
+ relPaths,
1854
+ };
1855
+ }
1856
+ catch (error) {
1857
+ this.logger(`[workspace] ${store.label} session files sync failed: ${error instanceof Error ? error.message : String(error)}`);
1858
+ if (error instanceof WorkspaceFileRefusal) {
1859
+ yield this.clearPendingWorkspaceFiles(sessionId, files);
1860
+ }
1861
+ return undefined;
1862
+ }
1863
+ });
1864
+ }
1865
+ wipeCloudSessionWorkspace(sessionId) {
1866
+ return __awaiter(this, void 0, void 0, function* () {
1867
+ const store = this.cloudStore();
1868
+ if (store === undefined) {
1869
+ return;
1870
+ }
1871
+ try {
1872
+ yield wipeSessionWorkspace({
1873
+ sink: store.sink,
1874
+ workspacePrefix: store.sessionWorkspacePrefix(sessionId),
1875
+ });
1876
+ }
1877
+ catch (error) {
1878
+ this.logger(`[workspace] ${store.label} session files wipe failed: ${error instanceof Error ? error.message : String(error)}`);
1879
+ }
1880
+ });
1881
+ }
1882
+ clearPendingWorkspaceFiles(sessionId, pending) {
1883
+ return __awaiter(this, void 0, void 0, function* () {
1884
+ yield this.updateRecord(sessionId, (current) => (Object.assign(Object.assign({}, current), { pendingWorkspaceFiles: current.pendingWorkspaceFiles === pending
1885
+ ? undefined
1886
+ : current.pendingWorkspaceFiles })));
1887
+ });
1888
+ }
1585
1889
  /**
1586
1890
  * With files authored, every first cloud turn re-copies the authored
1587
1891
  * bytes and the memo only dedupes concurrent first turns. The mount is
@@ -1623,6 +1927,7 @@ export class SessionEngine {
1623
1927
  mountPath: DEPLOYMENT_STORE_MOUNT_PATH,
1624
1928
  skillsPrefix: agentStoreKeys.skillsPrefix(),
1625
1929
  workspacePrefix: agentStoreKeys.workspacePrefix(),
1930
+ sessionWorkspacePrefix: (sessionId) => agentStoreKeys.sessionWorkspacePrefix(sessionId),
1626
1931
  label: "deployment store",
1627
1932
  };
1628
1933
  if (v2Hosting.kind === "cursor-managed") {
@@ -1641,6 +1946,7 @@ export class SessionEngine {
1641
1946
  mountPath: USER_STORE_MOUNT_PATH,
1642
1947
  skillsPrefix: agentStoreKeys.userSkillsPrefix(this.project.name),
1643
1948
  workspacePrefix: agentStoreKeys.userWorkspacePrefix(this.project.name),
1949
+ sessionWorkspacePrefix: (sessionId) => agentStoreKeys.userSessionWorkspacePrefix(this.project.name, sessionId),
1644
1950
  label: "user store",
1645
1951
  };
1646
1952
  }
@@ -1649,6 +1955,7 @@ export class SessionEngine {
1649
1955
  buildTurnRequest(record, agent, turnId, prompt, isFirstTurn, instructionsInvisibleToHarness, emit, onUpdate, signal, images) {
1650
1956
  return __awaiter(this, void 0, void 0, function* () {
1651
1957
  var _a, _b, _c;
1958
+ var _d;
1652
1959
  // Per-session cloud attach (channel/tool handoff) forces cloud runtime
1653
1960
  // even when the agent defaults to local for chat server tools.
1654
1961
  const runtime = resolveSessionRuntime({
@@ -1659,6 +1966,15 @@ export class SessionEngine {
1659
1966
  // allowlist, and a restriction that silently does not apply is worse
1660
1967
  // than a refused turn. Non-local agents already fail discovery; this
1661
1968
  // guards hybrid agents' per-send cloud sessions.
1969
+ //
1970
+ // Factory roots omit `allowedTools` at discovery (the live handle is
1971
+ // resolved per session). The allowlist guard below therefore never
1972
+ // fires for `defineAgent({ root: (ctx) => … })` with omitted `tools`,
1973
+ // and a per-send `cloud` attach would skip the virtual-root allowlist
1974
+ // and run unrestricted on a VM. Any authored root refuses non-local turns.
1975
+ if (runtime !== "local" && this.hasRootOverride()) {
1976
+ throw new Error(`Agent "${agent.name}" declares root, which cannot be served on ${runtime} turns. Refusing the session rather than running it against the host VM.`);
1977
+ }
1662
1978
  if (runtime !== "local" && agent.allowedTools !== undefined) {
1663
1979
  throw new Error(`Agent "${agent.name}" declares a built-in tool allowlist (tools), which cannot be enforced on ${runtime} turns. Refusing the session rather than running it unrestricted.`);
1664
1980
  }
@@ -1679,6 +1995,38 @@ export class SessionEngine {
1679
1995
  override: record.cloudOverride,
1680
1996
  })
1681
1997
  : undefined;
1998
+ const authoredRoot = this.hasRootOverride();
1999
+ const root = runtime === "local"
2000
+ ? yield this.resolveFileSystem({
2001
+ sessionId: record.sessionId,
2002
+ continuationKey: (_d = record.continuationKey) !== null && _d !== void 0 ? _d : undefined,
2003
+ cache: true,
2004
+ cwd: this.harnessCwd(record),
2005
+ })
2006
+ : undefined;
2007
+ const harnessTools = runtime === "local" && root !== undefined
2008
+ ? authoredRoot
2009
+ ? {
2010
+ tools: resolveFileSystemTools({
2011
+ root,
2012
+ tools: agent.allowedTools,
2013
+ }),
2014
+ }
2015
+ : resolveHostHarnessToolOptions(agent.allowedTools)
2016
+ : undefined;
2017
+ const fileTools = root === undefined
2018
+ ? []
2019
+ : synthesizeFileSystemTools(root, authoredRoot
2020
+ ? rootFileToolsFromAuthored(agent.allowedTools)
2021
+ : hostFileToolsFromAuthored(agent.allowedTools));
2022
+ // Host-default keeps native SemanticSearch (the host index).
2023
+ // `localFileSystem.search` is empty, so synthesizing would
2024
+ // replace a working index with an empty custom tool.
2025
+ const synthesized = root === undefined
2026
+ ? []
2027
+ : this.withTurnScratchTools(authoredRoot
2028
+ ? fileTools
2029
+ : fileTools.filter((tool) => tool.name !== "SemanticSearch"), record.sessionId);
1682
2030
  let turnPrompt = prompt;
1683
2031
  if (isFirstTurn) {
1684
2032
  const contextBlock = buildSessionContextBlock({
@@ -1690,32 +2038,37 @@ export class SessionEngine {
1690
2038
  });
1691
2039
  turnPrompt = `${contextBlock}\n\n${prompt}`;
1692
2040
  }
1693
- if (isFirstTurn) {
1694
- if (runtime === "cloud") {
1695
- // Cloud VMs have no materialized workspace: ship the full context,
1696
- // including agent tool script bodies. Authored skills and workspace
1697
- // seeds land on the store mount (skills for native discovery, seeds
1698
- // for the model's file tools), and on hosted deployments the shared
1699
- // memory journal is on that mount too — say where. When the turn
1700
- // carries the session-bound server-tool MCP endpoint, the preamble
1701
- // catalogs server tools under that MCP server name *before* the
1702
- // agent-tool "write the script if missing" recipe, so a model told
1703
- // to "use <server tool>" calls MCP instead of inventing a .sh.
1704
- const preamble = buildAgentsMdContent(agent, {
2041
+ if (runtime === "cloud") {
2042
+ // Cloud VMs have no materialized workspace: ship the full context,
2043
+ // including agent tool script bodies. Authored skills and workspace
2044
+ // seeds land on the store mount (skills for native discovery, seeds
2045
+ // for the model's file tools), per-turn workspaceFiles land under
2046
+ // the session prefix, and on hosted deployments the shared memory
2047
+ // journal is on that mount too — say where. When the turn carries
2048
+ // the session-bound server-tool MCP endpoint, the preamble catalogs
2049
+ // server tools under that MCP server name *before* the agent-tool
2050
+ // "write the script if missing" recipe, so a model told to "use
2051
+ // <server tool>" calls MCP instead of inventing a .sh.
2052
+ const hosted = isCursorHostedFilesAvailable();
2053
+ const store = hosted || isCursorUserStoreFilesAvailable()
2054
+ ? this.cloudStore()
2055
+ : undefined;
2056
+ let preamble = "";
2057
+ const sections = [];
2058
+ const pending = record.pendingWorkspaceFiles;
2059
+ if (isFirstTurn) {
2060
+ preamble = buildAgentsMdContent(agent, {
1705
2061
  includeScripts: true,
1706
2062
  serverToolsMcpName: this.cloudServerToolsConnection === undefined
1707
2063
  ? undefined
1708
2064
  : SERVER_TOOLS_MCP_SERVER_NAME,
1709
2065
  });
1710
- const hosted = isCursorHostedFilesAvailable();
1711
- // Synced whenever a store is reachable, not just when skills or
1712
- // seeds exist: removing the last one has to delete its store copies
1713
- // too.
1714
- const store = hosted || isCursorUserStoreFilesAvailable()
1715
- ? this.cloudStore()
1716
- : undefined;
1717
- let sections = "";
1718
- if (store !== undefined) {
2066
+ }
2067
+ if (store !== undefined) {
2068
+ if (isFirstTurn) {
2069
+ // Synced whenever a store is reachable, not just when skills or
2070
+ // seeds exist: removing the last one has to delete its store copies
2071
+ // too.
1719
2072
  const [, seedsSynced] = yield Promise.all([
1720
2073
  this.ensureCloudSkillsSynced(store),
1721
2074
  this.ensureCloudWorkspaceSynced(store),
@@ -1723,32 +2076,62 @@ export class SessionEngine {
1723
2076
  // Only name the mount when the bytes landed; a failed sync must
1724
2077
  // not send the model looking for files that are not there.
1725
2078
  if (seedsSynced && agent.seedFiles.length > 0) {
1726
- const dir = `${store.mountPath}/${store.workspacePrefix}`;
1727
- sections += `\n\n${buildCloudWorkspaceSection(dir)}`;
2079
+ sections.push(buildCloudWorkspaceSection(`${store.mountPath}/${store.workspacePrefix}`));
1728
2080
  }
1729
2081
  }
1730
- if (hosted) {
1731
- sections += `\n\n${buildCloudMemorySection(this.project.name)}`;
2082
+ if (pending !== undefined && Object.keys(pending).length > 0) {
2083
+ const synced = yield this.syncCloudSessionWorkspaceFiles(store, record.sessionId, pending);
2084
+ if (synced !== undefined) {
2085
+ sections.push(buildCloudSessionWorkspaceSection(synced.dir, synced.relPaths));
2086
+ yield this.clearPendingWorkspaceFiles(record.sessionId, pending);
2087
+ }
1732
2088
  }
1733
- turnPrompt = `${preamble}${sections}\n\n${turnPrompt}`;
1734
2089
  }
1735
- else if (runtime === "local" && instructionsInvisibleToHarness) {
1736
- // Local workspace whose AGENTS.md the harness will not load. With a
1737
- // borrowed harness cwd, agent tool scripts live only in the durable
1738
- // scaffold dir embed bodies like cloud. Otherwise the catalog can
1739
- // reference on-disk paths under the harness cwd. Grokbot turns are
1740
- // excluded: their instructions ride the request's `instructions`
1741
- // field into the Sand system prompt instead.
1742
- const includeScripts = this.hasBorrowedWorkspace(record);
1743
- const preamble = buildAgentsMdContent(agent, { includeScripts });
1744
- turnPrompt = `${preamble}\n\n${turnPrompt}`;
2090
+ else if (pending !== undefined && Object.keys(pending).length > 0) {
2091
+ yield this.clearPendingWorkspaceFiles(record.sessionId, pending);
2092
+ }
2093
+ if (isFirstTurn && hosted) {
2094
+ sections.push(buildCloudMemorySection(this.project.name));
2095
+ }
2096
+ if (preamble !== "" || sections.length > 0) {
2097
+ turnPrompt = [preamble, ...sections, turnPrompt]
2098
+ .filter((part) => part !== "")
2099
+ .join("\n\n");
1745
2100
  }
1746
2101
  }
2102
+ else if (isFirstTurn && runtime === "local" && authoredRoot) {
2103
+ // Virtual roots skip materialize, so AGENTS.md never lands on disk.
2104
+ // Ship the same preamble the borrowed-cwd local path uses.
2105
+ const preamble = buildAgentsMdContent(Object.assign(Object.assign({}, agent), { tools: [...agent.tools, ...synthesized] }), { includeScripts: true });
2106
+ const workspaceInfo = `<user_info>\nWorkspace Path: /\n</user_info>`;
2107
+ turnPrompt =
2108
+ preamble === ""
2109
+ ? `${workspaceInfo}\n\n${turnPrompt}`
2110
+ : `${workspaceInfo}\n\n${preamble}\n\n${turnPrompt}`;
2111
+ }
2112
+ else if (isFirstTurn &&
2113
+ runtime === "local" &&
2114
+ instructionsInvisibleToHarness) {
2115
+ // Local workspace whose AGENTS.md the harness will not load. With a
2116
+ // borrowed harness cwd, agent tool scripts live only in the durable
2117
+ // scaffold dir — embed bodies like cloud. Otherwise the catalog can
2118
+ // reference on-disk paths under the harness cwd. Grokbot turns are
2119
+ // excluded: their instructions ride the request's `instructions`
2120
+ // field into the Sand system prompt instead.
2121
+ const includeScripts = this.hasBorrowedWorkspace(record);
2122
+ const preamble = buildAgentsMdContent(agent, { includeScripts });
2123
+ turnPrompt = `${preamble}\n\n${turnPrompt}`;
2124
+ }
1747
2125
  return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({ sessionId: record.sessionId }, (record.sdkAgentId === undefined
1748
2126
  ? {}
1749
- : { sdkAgentId: record.sdkAgentId })), { workspaceDir: this.harnessCwd(record), sandbox: ((_c = this.project.agent.local) === null || _c === void 0 ? void 0 : _c.sandbox) === true }), (runtime === "local" && agent.allowedTools !== undefined
1750
- ? { tools: agent.allowedTools }
1751
- : {})), { runnerStateDir: join(this.stateRoot, "runner"), prompt: turnPrompt, isFirstTurn }), (images !== undefined && images.length > 0 ? { images } : {})), (agent.model === undefined ? {} : { model: agent.model })), { runtime, agentName: agent.name }), (mergedCloud === undefined ? {} : { cloud: mergedCloud })), {
2127
+ : { sdkAgentId: record.sdkAgentId })), { workspaceDir: this.harnessCwd(record), sandbox: authoredRoot
2128
+ ? false
2129
+ : ((_c = this.project.agent.local) === null || _c === void 0 ? void 0 : _c.sandbox) === true }), (runtime === "local" && harnessTools !== undefined
2130
+ ? Object.assign(Object.assign({}, (harnessTools.tools === undefined
2131
+ ? {}
2132
+ : { tools: harnessTools.tools })), (harnessTools.disallowedTools === undefined
2133
+ ? {}
2134
+ : { disallowedTools: harnessTools.disallowedTools })) : {})), { runnerStateDir: join(this.stateRoot, "runner"), prompt: turnPrompt, isFirstTurn }), (images !== undefined && images.length > 0 ? { images } : {})), (agent.model === undefined ? {} : { model: agent.model })), { runtime, agentName: agent.name }), (mergedCloud === undefined ? {} : { cloud: mergedCloud })), {
1752
2135
  // The hosted Grok Bot harness reads no workspace AGENTS.md: grokbot
1753
2136
  // turns carry the composed instructions into the hosted session,
1754
2137
  // which surfaces them in the Sand system prompt. The box never sees
@@ -1759,7 +2142,7 @@ export class SessionEngine {
1759
2142
  : undefined }), (runtime === "cloud" && !isCursorHostedFilesAvailable()
1760
2143
  ? { agentServeAgent: this.project.name }
1761
2144
  : {})), { customTools: runtime === "local"
1762
- ? yield this.buildCustomTools(record, agent, turnId, emit)
2145
+ ? yield this.buildCustomTools(record, agent, turnId, emit, synthesized)
1763
2146
  : {}, mcpServers: this.buildMcpServers(runtime, record.sessionId, yield this.evaluateConnectionAuth(record)) }), (runtime === "local" && this.hasAttachedAuthedConnections()
1764
2147
  ? { perSendMcpServers: true }
1765
2148
  : {})), { subagents: buildRunnerSubagents(agent), onUpdate,
@@ -1982,8 +2365,8 @@ export class SessionEngine {
1982
2365
  return evaluated;
1983
2366
  });
1984
2367
  }
1985
- buildCustomTools(record, agent, turnId, emit) {
1986
- return __awaiter(this, void 0, void 0, function* () {
2368
+ buildCustomTools(record_1, agent_1, turnId_1, emit_1) {
2369
+ return __awaiter(this, arguments, void 0, function* (record, agent, turnId, emit, extraTools = []) {
1987
2370
  const tools = {};
1988
2371
  const register = (tool) => {
1989
2372
  tools[tool.name] = Object.assign(Object.assign(Object.assign({ description: tool.description, inputSchema: tool.inputSchema }, (tool.outputSchema === undefined
@@ -2003,7 +2386,7 @@ export class SessionEngine {
2003
2386
  });
2004
2387
  }) });
2005
2388
  };
2006
- for (const tool of agent.tools) {
2389
+ for (const tool of [...agent.tools, ...extraTools]) {
2007
2390
  if (tool.execution !== "server") {
2008
2391
  continue;
2009
2392
  }
@@ -2672,6 +3055,7 @@ export class SessionEngine {
2672
3055
  callTool(toolName_1) {
2673
3056
  return __awaiter(this, arguments, void 0, function* (toolName, input = {}, options = {}) {
2674
3057
  var _a;
3058
+ var _b, _c;
2675
3059
  const callId = newToolCallId(toolName);
2676
3060
  // Identity resolves before the tool lookup: advertised connection tools
2677
3061
  // only exist relative to a session (per-session auth), so finding them
@@ -2681,6 +3065,24 @@ export class SessionEngine {
2681
3065
  const session = identity.session;
2682
3066
  let workspaceDir = identity.workspaceDir;
2683
3067
  let scratchDir = identity.scratchDir;
3068
+ if (isFileSystemToolAlias(toolName) &&
3069
+ identity.busy &&
3070
+ record !== undefined &&
3071
+ isMutatingFileSystemTool(toolName)) {
3072
+ throw new SessionBusyError(record.sessionId);
3073
+ }
3074
+ const native = yield this.nativeFileSystemOutcome({
3075
+ toolName,
3076
+ input,
3077
+ callId,
3078
+ sessionId: record === null || record === void 0 ? void 0 : record.sessionId,
3079
+ continuationKey: (_c = (_b = record === null || record === void 0 ? void 0 : record.continuationKey) !== null && _b !== void 0 ? _b : session.continuationKey) !== null && _c !== void 0 ? _c : undefined,
3080
+ cwd: workspaceDir,
3081
+ dryRun: (record === null || record === void 0 ? void 0 : record.dryRun) === true,
3082
+ });
3083
+ if (native !== undefined) {
3084
+ return native;
3085
+ }
2684
3086
  let tool = this.project.agent.tools.find((t) => t.name === toolName);
2685
3087
  if (tool === undefined &&
2686
3088
  this.project.agent.connections.some((c) => c.advertiseTools === true)) {
@@ -2769,7 +3171,8 @@ export class SessionEngine {
2769
3171
  const turnMaterialized = record !== undefined &&
2770
3172
  identity.busy &&
2771
3173
  this.materializedWorkspaces.has(record.sessionId);
2772
- if (record === undefined || workspaceUnavailable || !turnMaterialized) {
3174
+ if (!this.hasRootOverride() &&
3175
+ (record === undefined || workspaceUnavailable || !turnMaterialized)) {
2773
3176
  yield materializeWorkspace({
2774
3177
  agent: this.project.agent,
2775
3178
  workspaceDir: record === undefined || workspaceUnavailable
@@ -3548,7 +3951,8 @@ export class SessionEngine {
3548
3951
  }
3549
3952
  runCloudTurnCostBackfill(sessionId) {
3550
3953
  return __awaiter(this, void 0, void 0, function* () {
3551
- var _a, _b;
3954
+ var _a;
3955
+ var _b, _c;
3552
3956
  const log = yield this.logs.get(sessionId);
3553
3957
  const events = yield log.snapshot(0);
3554
3958
  if (!events.some((event) => isUnpricedCompletedTurn(event))) {
@@ -3559,9 +3963,7 @@ export class SessionEngine {
3559
3963
  return;
3560
3964
  }
3561
3965
  const record = yield this.sessions.get(sessionId);
3562
- const sdkAgentId = (_a = ((record === null || record === void 0 ? void 0 : record.sdkAgentId) !== undefined && record.sdkAgentId.startsWith("bc-")
3563
- ? record.sdkAgentId
3564
- : undefined)) !== null && _a !== void 0 ? _a : cloudSdkAgentIdFromEvents(events);
3966
+ const sdkAgentId = (_b = (((_a = record === null || record === void 0 ? void 0 : record.sdkAgentId) === null || _a === void 0 ? void 0 : _a.startsWith("bc-")) ? record.sdkAgentId : undefined)) !== null && _b !== void 0 ? _b : cloudSdkAgentIdFromEvents(events);
3565
3967
  if (sdkAgentId === undefined) {
3566
3968
  return;
3567
3969
  }
@@ -3569,7 +3971,7 @@ export class SessionEngine {
3569
3971
  if (billed === undefined) {
3570
3972
  return;
3571
3973
  }
3572
- const model = (_b = this.project.agent.model) !== null && _b !== void 0 ? _b : DEFAULT_MODEL;
3974
+ const model = (_c = this.project.agent.model) !== null && _c !== void 0 ? _c : DEFAULT_MODEL;
3573
3975
  const patched = applyCloudUsageToEvents(events, billed, model);
3574
3976
  if (patched === undefined) {
3575
3977
  return;