@vellumai/assistant 0.4.17 → 0.4.18

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 (515) hide show
  1. package/eslint.config.mjs +2 -2
  2. package/package.json +1 -1
  3. package/src/__tests__/access-request-decision.test.ts +128 -120
  4. package/src/__tests__/account-registry.test.ts +121 -110
  5. package/src/__tests__/active-skill-tools.test.ts +200 -172
  6. package/src/__tests__/actor-token-service.test.ts +341 -274
  7. package/src/__tests__/agent-loop-thinking.test.ts +28 -19
  8. package/src/__tests__/agent-loop.test.ts +798 -378
  9. package/src/__tests__/anthropic-provider.test.ts +405 -247
  10. package/src/__tests__/app-builder-tool-scripts.test.ts +97 -97
  11. package/src/__tests__/app-bundler.test.ts +112 -79
  12. package/src/__tests__/app-executors.test.ts +205 -178
  13. package/src/__tests__/app-git-history.test.ts +90 -73
  14. package/src/__tests__/app-git-service.test.ts +67 -53
  15. package/src/__tests__/app-open-proxy.test.ts +29 -25
  16. package/src/__tests__/approval-conversation-turn.test.ts +100 -81
  17. package/src/__tests__/approval-hardcoded-copy-guard.test.ts +45 -17
  18. package/src/__tests__/approval-message-composer.test.ts +119 -119
  19. package/src/__tests__/approval-primitive.test.ts +264 -233
  20. package/src/__tests__/approval-routes-http.test.ts +4 -3
  21. package/src/__tests__/asset-materialize-tool.test.ts +250 -178
  22. package/src/__tests__/asset-search-tool.test.ts +251 -191
  23. package/src/__tests__/assistant-attachment-directive.test.ts +187 -142
  24. package/src/__tests__/assistant-attachments.test.ts +254 -186
  25. package/src/__tests__/assistant-event-hub.test.ts +105 -63
  26. package/src/__tests__/assistant-event.test.ts +66 -58
  27. package/src/__tests__/assistant-events-sse-hardening.test.ts +113 -73
  28. package/src/__tests__/assistant-feature-flag-guard.test.ts +78 -52
  29. package/src/__tests__/assistant-feature-flag-guardrails.test.ts +48 -45
  30. package/src/__tests__/assistant-feature-flags-integration.test.ts +118 -77
  31. package/src/__tests__/assistant-id-boundary-guard.test.ts +158 -104
  32. package/src/__tests__/attachments-store.test.ts +240 -183
  33. package/src/__tests__/attachments.test.ts +70 -62
  34. package/src/__tests__/audit-log-rotation.test.ts +50 -35
  35. package/src/__tests__/browser-fill-credential.test.ts +169 -101
  36. package/src/__tests__/browser-manager.test.ts +97 -75
  37. package/src/__tests__/browser-runtime-check.test.ts +16 -15
  38. package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +12 -10
  39. package/src/__tests__/browser-skill-endstate.test.ts +97 -72
  40. package/src/__tests__/bundle-scanner.test.ts +47 -22
  41. package/src/__tests__/bundled-asset.test.ts +74 -47
  42. package/src/__tests__/call-constants.test.ts +19 -19
  43. package/src/__tests__/call-controller.test.ts +0 -1
  44. package/src/__tests__/call-conversation-messages.test.ts +90 -65
  45. package/src/__tests__/call-domain.test.ts +149 -121
  46. package/src/__tests__/call-pointer-message-composer.test.ts +113 -83
  47. package/src/__tests__/call-pointer-messages.test.ts +213 -154
  48. package/src/__tests__/call-pointer-no-hardcoded-copy.guard.test.ts +9 -10
  49. package/src/__tests__/call-recovery.test.ts +232 -212
  50. package/src/__tests__/call-routes-http.test.ts +0 -1
  51. package/src/__tests__/call-start-guardian-guard.test.ts +32 -30
  52. package/src/__tests__/call-state-machine.test.ts +62 -51
  53. package/src/__tests__/call-state.test.ts +89 -75
  54. package/src/__tests__/call-store.test.ts +387 -316
  55. package/src/__tests__/callback-handoff-copy.test.ts +84 -82
  56. package/src/__tests__/canonical-guardian-store.test.ts +331 -280
  57. package/src/__tests__/channel-approval-routes.test.ts +1643 -1115
  58. package/src/__tests__/channel-approval.test.ts +139 -137
  59. package/src/__tests__/channel-approvals.test.ts +0 -1
  60. package/src/__tests__/channel-delivery-store.test.ts +232 -194
  61. package/src/__tests__/channel-guardian.test.ts +5 -3
  62. package/src/__tests__/channel-invite-transport.test.ts +107 -92
  63. package/src/__tests__/channel-policy.test.ts +42 -38
  64. package/src/__tests__/channel-readiness-service.test.ts +119 -102
  65. package/src/__tests__/channel-reply-delivery.test.ts +147 -118
  66. package/src/__tests__/channel-retry-sweep.test.ts +153 -110
  67. package/src/__tests__/checker.test.ts +3309 -1850
  68. package/src/__tests__/clarification-resolver.test.ts +91 -79
  69. package/src/__tests__/classifier.test.ts +64 -54
  70. package/src/__tests__/claude-code-skill-regression.test.ts +42 -37
  71. package/src/__tests__/claude-code-tool-profiles.test.ts +31 -29
  72. package/src/__tests__/clawhub.test.ts +92 -82
  73. package/src/__tests__/cli.test.ts +30 -30
  74. package/src/__tests__/clipboard.test.ts +53 -46
  75. package/src/__tests__/commit-guarantee.test.ts +59 -52
  76. package/src/__tests__/commit-message-enrichment-service.test.ts +203 -75
  77. package/src/__tests__/compaction.benchmark.test.ts +33 -31
  78. package/src/__tests__/computer-use-session-compaction.test.ts +60 -50
  79. package/src/__tests__/computer-use-session-lifecycle.test.ts +145 -117
  80. package/src/__tests__/computer-use-session-working-dir.test.ts +62 -48
  81. package/src/__tests__/computer-use-skill-baseline.test.ts +22 -19
  82. package/src/__tests__/computer-use-skill-endstate.test.ts +45 -31
  83. package/src/__tests__/computer-use-skill-lifecycle-cleanup.test.ts +121 -88
  84. package/src/__tests__/computer-use-skill-manifest-regression.test.ts +65 -42
  85. package/src/__tests__/computer-use-skill-proxy-bridge.test.ts +33 -18
  86. package/src/__tests__/computer-use-tools.test.ts +121 -98
  87. package/src/__tests__/config-schema.test.ts +443 -347
  88. package/src/__tests__/config-watcher.test.ts +96 -81
  89. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +148 -133
  90. package/src/__tests__/conflict-intent-tokenization.test.ts +96 -78
  91. package/src/__tests__/conflict-policy.test.ts +151 -80
  92. package/src/__tests__/conflict-store.test.ts +203 -157
  93. package/src/__tests__/connection-policy.test.ts +89 -59
  94. package/src/__tests__/contacts-tools.test.ts +247 -178
  95. package/src/__tests__/context-memory-e2e.test.ts +306 -214
  96. package/src/__tests__/context-token-estimator.test.ts +114 -74
  97. package/src/__tests__/context-window-manager.test.ts +269 -167
  98. package/src/__tests__/contradiction-checker.test.ts +161 -135
  99. package/src/__tests__/conversation-attention-store.test.ts +350 -290
  100. package/src/__tests__/conversation-attention-telegram.test.ts +0 -1
  101. package/src/__tests__/conversation-pairing.test.ts +220 -113
  102. package/src/__tests__/conversation-store.test.ts +390 -235
  103. package/src/__tests__/credential-broker-browser-fill.test.ts +325 -250
  104. package/src/__tests__/credential-broker-server-use.test.ts +283 -243
  105. package/src/__tests__/credential-broker.test.ts +128 -74
  106. package/src/__tests__/credential-host-pattern-match.test.ts +64 -44
  107. package/src/__tests__/credential-metadata-store.test.ts +360 -311
  108. package/src/__tests__/credential-policy-validate.test.ts +81 -65
  109. package/src/__tests__/credential-resolve.test.ts +212 -145
  110. package/src/__tests__/credential-security-e2e.test.ts +144 -103
  111. package/src/__tests__/credential-security-invariants.test.ts +253 -208
  112. package/src/__tests__/credential-selection.test.ts +254 -146
  113. package/src/__tests__/credential-vault-unit.test.ts +531 -341
  114. package/src/__tests__/credential-vault.test.ts +761 -484
  115. package/src/__tests__/daemon-assistant-events.test.ts +91 -66
  116. package/src/__tests__/daemon-lifecycle.test.ts +258 -190
  117. package/src/__tests__/daemon-server-session-init.test.ts +0 -1
  118. package/src/__tests__/date-context.test.ts +314 -249
  119. package/src/__tests__/db-migration-rollback.test.ts +259 -130
  120. package/src/__tests__/db-schedule-syntax-migration.test.ts +78 -41
  121. package/src/__tests__/delete-managed-skill-tool.test.ts +77 -53
  122. package/src/__tests__/deterministic-verification-control-plane.test.ts +0 -1
  123. package/src/__tests__/dictation-mode-detection.test.ts +77 -55
  124. package/src/__tests__/dictation-profile-store.test.ts +70 -56
  125. package/src/__tests__/dictation-text-processing.test.ts +53 -35
  126. package/src/__tests__/diff.test.ts +102 -98
  127. package/src/__tests__/domain-normalize.test.ts +54 -54
  128. package/src/__tests__/domain-policy.test.ts +71 -55
  129. package/src/__tests__/dynamic-page-surface.test.ts +31 -33
  130. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +69 -69
  131. package/src/__tests__/edit-engine.test.ts +56 -56
  132. package/src/__tests__/elevenlabs-client.test.ts +117 -91
  133. package/src/__tests__/elevenlabs-config.test.ts +32 -31
  134. package/src/__tests__/email-classifier.test.ts +15 -12
  135. package/src/__tests__/email-cli.test.ts +121 -108
  136. package/src/__tests__/emit-signal-routing-intent.test.ts +76 -69
  137. package/src/__tests__/encrypted-store.test.ts +180 -154
  138. package/src/__tests__/entity-extractor.test.ts +108 -87
  139. package/src/__tests__/entity-search.test.ts +664 -258
  140. package/src/__tests__/ephemeral-permissions.test.ts +224 -188
  141. package/src/__tests__/event-bus.test.ts +81 -77
  142. package/src/__tests__/extract-email.test.ts +29 -20
  143. package/src/__tests__/file-edit-tool.test.ts +62 -44
  144. package/src/__tests__/file-ops-service.test.ts +131 -114
  145. package/src/__tests__/file-read-tool.test.ts +48 -31
  146. package/src/__tests__/file-write-tool.test.ts +43 -37
  147. package/src/__tests__/filesystem-tools.test.ts +238 -209
  148. package/src/__tests__/followup-tools.test.ts +237 -162
  149. package/src/__tests__/forbidden-legacy-symbols.test.ts +19 -20
  150. package/src/__tests__/frontmatter.test.ts +96 -81
  151. package/src/__tests__/fuzzy-match-property.test.ts +75 -81
  152. package/src/__tests__/fuzzy-match.test.ts +71 -65
  153. package/src/__tests__/gateway-client-managed-outbound.test.ts +76 -57
  154. package/src/__tests__/gateway-only-enforcement.test.ts +0 -1
  155. package/src/__tests__/gateway-only-guard.test.ts +0 -1
  156. package/src/__tests__/gemini-image-service.test.ts +113 -100
  157. package/src/__tests__/gemini-provider.test.ts +297 -220
  158. package/src/__tests__/get-weather.test.ts +188 -114
  159. package/src/__tests__/gmail-integration.test.ts +0 -1
  160. package/src/__tests__/guardian-action-conversation-turn.test.ts +226 -171
  161. package/src/__tests__/guardian-action-copy-generator.test.ts +111 -93
  162. package/src/__tests__/guardian-action-followup-executor.test.ts +0 -1
  163. package/src/__tests__/guardian-action-followup-store.test.ts +199 -167
  164. package/src/__tests__/guardian-action-grant-mint-consume.test.ts +297 -250
  165. package/src/__tests__/guardian-action-late-reply.test.ts +462 -316
  166. package/src/__tests__/guardian-action-no-hardcoded-copy.test.ts +23 -18
  167. package/src/__tests__/guardian-action-store.test.ts +158 -109
  168. package/src/__tests__/guardian-action-sweep.test.ts +114 -100
  169. package/src/__tests__/guardian-actions-endpoint.test.ts +440 -256
  170. package/src/__tests__/guardian-control-plane-policy.test.ts +497 -331
  171. package/src/__tests__/guardian-decision-primitive-canonical.test.ts +217 -215
  172. package/src/__tests__/guardian-dispatch.test.ts +316 -256
  173. package/src/__tests__/guardian-grant-minting.test.ts +247 -178
  174. package/src/__tests__/guardian-outbound-http.test.ts +5 -3
  175. package/src/__tests__/guardian-principal-id-roundtrip.test.ts +99 -96
  176. package/src/__tests__/guardian-question-copy.test.ts +17 -17
  177. package/src/__tests__/guardian-question-mode.test.ts +134 -100
  178. package/src/__tests__/guardian-routing-invariants.test.ts +0 -1
  179. package/src/__tests__/guardian-routing-state.test.ts +0 -1
  180. package/src/__tests__/guardian-verification-intent-routing.test.ts +94 -88
  181. package/src/__tests__/guardian-verification-voice-binding.test.ts +0 -1
  182. package/src/__tests__/guardian-verify-setup-skill-regression.test.ts +0 -1
  183. package/src/__tests__/handle-user-message-secret-resume.test.ts +0 -1
  184. package/src/__tests__/handlers-add-trust-rule-metadata.test.ts +92 -76
  185. package/src/__tests__/handlers-cu-observation-blob.test.ts +103 -70
  186. package/src/__tests__/handlers-ipc-blob-probe.test.ts +77 -51
  187. package/src/__tests__/handlers-slack-config.test.ts +63 -54
  188. package/src/__tests__/handlers-task-submit-slash.test.ts +18 -18
  189. package/src/__tests__/handlers-telegram-config.test.ts +662 -329
  190. package/src/__tests__/handlers-twitter-config.test.ts +525 -298
  191. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +3 -2
  192. package/src/__tests__/headless-browser-interactions.test.ts +444 -280
  193. package/src/__tests__/headless-browser-navigate.test.ts +116 -79
  194. package/src/__tests__/headless-browser-read-tools.test.ts +123 -86
  195. package/src/__tests__/headless-browser-snapshot.test.ts +71 -56
  196. package/src/__tests__/heartbeat-service.test.ts +76 -58
  197. package/src/__tests__/history-repair-observability.test.ts +14 -14
  198. package/src/__tests__/history-repair.test.ts +171 -167
  199. package/src/__tests__/home-base-bootstrap.test.ts +30 -27
  200. package/src/__tests__/hooks-blocking.test.ts +86 -37
  201. package/src/__tests__/hooks-cli.test.ts +104 -68
  202. package/src/__tests__/hooks-config.test.ts +81 -43
  203. package/src/__tests__/hooks-discovery.test.ts +106 -96
  204. package/src/__tests__/hooks-integration.test.ts +78 -72
  205. package/src/__tests__/hooks-manager.test.ts +99 -61
  206. package/src/__tests__/hooks-runner.test.ts +94 -71
  207. package/src/__tests__/hooks-settings.test.ts +69 -64
  208. package/src/__tests__/hooks-templates.test.ts +85 -54
  209. package/src/__tests__/hooks-ts-runner.test.ts +82 -45
  210. package/src/__tests__/hooks-watch.test.ts +32 -22
  211. package/src/__tests__/host-file-edit-tool.test.ts +190 -148
  212. package/src/__tests__/host-file-read-tool.test.ts +86 -63
  213. package/src/__tests__/host-file-write-tool.test.ts +98 -64
  214. package/src/__tests__/host-shell-tool.test.ts +342 -233
  215. package/src/__tests__/inbound-invite-redemption.test.ts +0 -1
  216. package/src/__tests__/ingress-member-store.test.ts +163 -159
  217. package/src/__tests__/ingress-reconcile.test.ts +0 -1
  218. package/src/__tests__/ingress-routes-http.test.ts +441 -356
  219. package/src/__tests__/ingress-url-consistency.test.ts +125 -64
  220. package/src/__tests__/integration-status.test.ts +93 -73
  221. package/src/__tests__/intent-routing.test.ts +148 -118
  222. package/src/__tests__/invite-redemption-service.test.ts +163 -121
  223. package/src/__tests__/ipc-blob-store.test.ts +104 -91
  224. package/src/__tests__/ipc-contract-inventory.test.ts +27 -15
  225. package/src/__tests__/ipc-contract.test.ts +24 -23
  226. package/src/__tests__/ipc-protocol.test.ts +52 -46
  227. package/src/__tests__/ipc-roundtrip.benchmark.test.ts +61 -50
  228. package/src/__tests__/ipc-snapshot.test.ts +1135 -1056
  229. package/src/__tests__/ipc-validate.test.ts +240 -179
  230. package/src/__tests__/key-migration.test.ts +123 -90
  231. package/src/__tests__/keychain.test.ts +150 -123
  232. package/src/__tests__/lifecycle-docs-guard.test.ts +65 -64
  233. package/src/__tests__/llm-usage-store.test.ts +112 -87
  234. package/src/__tests__/managed-skill-lifecycle.test.ts +147 -108
  235. package/src/__tests__/managed-store.test.ts +411 -360
  236. package/src/__tests__/mcp-cli.test.ts +189 -123
  237. package/src/__tests__/mcp-health-check.test.ts +26 -21
  238. package/src/__tests__/media-generate-image.test.ts +122 -99
  239. package/src/__tests__/media-reuse-story.e2e.test.ts +282 -214
  240. package/src/__tests__/media-visibility-policy.test.ts +86 -38
  241. package/src/__tests__/memory-context-benchmark.benchmark.test.ts +146 -100
  242. package/src/__tests__/memory-lifecycle-e2e.test.ts +385 -297
  243. package/src/__tests__/memory-query-builder.test.ts +32 -33
  244. package/src/__tests__/memory-recall-quality.test.ts +761 -407
  245. package/src/__tests__/memory-regressions.experimental.test.ts +443 -380
  246. package/src/__tests__/memory-regressions.test.ts +3725 -2642
  247. package/src/__tests__/memory-retrieval-budget.test.ts +7 -8
  248. package/src/__tests__/memory-retrieval.benchmark.test.ts +144 -109
  249. package/src/__tests__/memory-upsert-concurrency.test.ts +292 -201
  250. package/src/__tests__/messaging-send-tool.test.ts +36 -29
  251. package/src/__tests__/migration-cli-flows.test.ts +69 -53
  252. package/src/__tests__/migration-ordering.test.ts +103 -86
  253. package/src/__tests__/mime-builder.test.ts +55 -32
  254. package/src/__tests__/mock-signup-server.test.ts +384 -246
  255. package/src/__tests__/model-intents.test.ts +61 -37
  256. package/src/__tests__/no-direct-anthropic-sdk-imports.test.ts +9 -12
  257. package/src/__tests__/no-is-trusted-guard.test.ts +24 -21
  258. package/src/__tests__/non-member-access-request.test.ts +3 -2
  259. package/src/__tests__/notification-broadcaster.test.ts +99 -81
  260. package/src/__tests__/notification-decision-fallback.test.ts +223 -178
  261. package/src/__tests__/notification-decision-strategy.test.ts +375 -337
  262. package/src/__tests__/notification-deep-link.test.ts +67 -61
  263. package/src/__tests__/notification-guardian-path.test.ts +248 -206
  264. package/src/__tests__/notification-routing-intent.test.ts +166 -93
  265. package/src/__tests__/notification-thread-candidate-validation.test.ts +78 -75
  266. package/src/__tests__/notification-thread-candidates.test.ts +64 -61
  267. package/src/__tests__/oauth-callback-registry.test.ts +40 -30
  268. package/src/__tests__/oauth-connect-handler.test.ts +109 -89
  269. package/src/__tests__/oauth-scope-policy.test.ts +63 -55
  270. package/src/__tests__/oauth2-gateway-transport.test.ts +252 -174
  271. package/src/__tests__/onboarding-starter-tasks.test.ts +93 -89
  272. package/src/__tests__/onboarding-template-contract.test.ts +93 -94
  273. package/src/__tests__/openai-provider.test.ts +366 -274
  274. package/src/__tests__/pairing-concurrent.test.ts +18 -12
  275. package/src/__tests__/pairing-routes.test.ts +45 -41
  276. package/src/__tests__/parallel-tool.benchmark.test.ts +108 -58
  277. package/src/__tests__/parser.test.ts +316 -226
  278. package/src/__tests__/path-classifier.test.ts +24 -25
  279. package/src/__tests__/path-policy.test.ts +187 -147
  280. package/src/__tests__/phone.test.ts +36 -36
  281. package/src/__tests__/platform-move-helper.test.ts +48 -40
  282. package/src/__tests__/platform-socket-path.test.ts +23 -24
  283. package/src/__tests__/platform-workspace-migration.test.ts +464 -414
  284. package/src/__tests__/platform.test.ts +61 -53
  285. package/src/__tests__/playbook-execution.test.ts +397 -265
  286. package/src/__tests__/playbook-tools.test.ts +267 -196
  287. package/src/__tests__/prebuilt-home-base-seed.test.ts +30 -27
  288. package/src/__tests__/pricing.test.ts +316 -136
  289. package/src/__tests__/profile-compiler.test.ts +206 -188
  290. package/src/__tests__/provider-commit-message-generator.test.ts +114 -106
  291. package/src/__tests__/provider-error-scenarios.test.ts +212 -158
  292. package/src/__tests__/provider-fail-open-selection.test.ts +51 -44
  293. package/src/__tests__/provider-registry-ollama.test.ts +13 -9
  294. package/src/__tests__/provider-streaming.benchmark.test.ts +232 -183
  295. package/src/__tests__/proxy-approval-callback.test.ts +180 -119
  296. package/src/__tests__/public-ingress-urls.test.ts +112 -94
  297. package/src/__tests__/qdrant-manager.test.ts +147 -98
  298. package/src/__tests__/ratelimit.test.ts +152 -82
  299. package/src/__tests__/recording-handler.test.ts +273 -151
  300. package/src/__tests__/recording-intent-fallback.test.ts +94 -75
  301. package/src/__tests__/recording-intent-handler.test.ts +0 -1
  302. package/src/__tests__/recording-intent.test.ts +578 -379
  303. package/src/__tests__/recording-state-machine.test.ts +530 -316
  304. package/src/__tests__/recurrence-engine-rruleset.test.ts +150 -92
  305. package/src/__tests__/recurrence-engine.test.ts +81 -41
  306. package/src/__tests__/recurrence-types.test.ts +63 -44
  307. package/src/__tests__/relay-server.test.ts +2131 -1602
  308. package/src/__tests__/reminder-store.test.ts +158 -80
  309. package/src/__tests__/reminder.test.ts +113 -109
  310. package/src/__tests__/remote-skill-policy.test.ts +96 -72
  311. package/src/__tests__/request-file-tool.test.ts +74 -67
  312. package/src/__tests__/response-tier.test.ts +131 -74
  313. package/src/__tests__/runtime-attachment-metadata.test.ts +0 -1
  314. package/src/__tests__/runtime-events-sse-parity.test.ts +167 -145
  315. package/src/__tests__/runtime-events-sse.test.ts +0 -1
  316. package/src/__tests__/sandbox-diagnostics.test.ts +66 -56
  317. package/src/__tests__/sandbox-host-parity.test.ts +377 -301
  318. package/src/__tests__/scaffold-managed-skill-tool.test.ts +213 -161
  319. package/src/__tests__/schedule-store.test.ts +268 -205
  320. package/src/__tests__/schedule-tools.test.ts +702 -524
  321. package/src/__tests__/scheduler-recurrence.test.ts +240 -130
  322. package/src/__tests__/scoped-approval-grants.test.ts +258 -168
  323. package/src/__tests__/scoped-grant-security-matrix.test.ts +160 -146
  324. package/src/__tests__/script-proxy-certs.test.ts +38 -35
  325. package/src/__tests__/script-proxy-connect-tunnel.test.ts +71 -46
  326. package/src/__tests__/script-proxy-decision-trace.test.ts +161 -84
  327. package/src/__tests__/script-proxy-http-forwarder.test.ts +146 -129
  328. package/src/__tests__/script-proxy-injection-runtime.test.ts +139 -113
  329. package/src/__tests__/script-proxy-mitm-handler.test.ts +226 -142
  330. package/src/__tests__/script-proxy-policy-runtime.test.ts +126 -86
  331. package/src/__tests__/script-proxy-policy.test.ts +308 -153
  332. package/src/__tests__/script-proxy-rewrite-specificity.test.ts +74 -62
  333. package/src/__tests__/script-proxy-router.test.ts +111 -77
  334. package/src/__tests__/script-proxy-session-manager.test.ts +156 -113
  335. package/src/__tests__/script-proxy-session-runtime.test.ts +28 -24
  336. package/src/__tests__/secret-allowlist.test.ts +105 -90
  337. package/src/__tests__/secret-ingress-handler.test.ts +41 -30
  338. package/src/__tests__/secret-onetime-send.test.ts +67 -50
  339. package/src/__tests__/secret-prompt-log-hygiene.test.ts +35 -31
  340. package/src/__tests__/secret-response-routing.test.ts +50 -41
  341. package/src/__tests__/secret-scanner-executor.test.ts +152 -111
  342. package/src/__tests__/secret-scanner.test.ts +495 -413
  343. package/src/__tests__/secure-keys.test.ts +132 -121
  344. package/src/__tests__/send-endpoint-busy.test.ts +0 -1
  345. package/src/__tests__/send-notification-tool.test.ts +43 -42
  346. package/src/__tests__/sensitive-output-placeholders.test.ts +72 -64
  347. package/src/__tests__/sequence-store.test.ts +335 -167
  348. package/src/__tests__/server-history-render.test.ts +341 -202
  349. package/src/__tests__/session-abort-tool-results.test.ts +133 -70
  350. package/src/__tests__/session-confirmation-signals.test.ts +252 -160
  351. package/src/__tests__/session-conflict-gate.test.ts +775 -585
  352. package/src/__tests__/session-error.test.ts +222 -191
  353. package/src/__tests__/session-evictor.test.ts +79 -62
  354. package/src/__tests__/session-init.benchmark.test.ts +170 -108
  355. package/src/__tests__/session-load-history-repair.test.ts +273 -139
  356. package/src/__tests__/session-messaging-secret-redirect.test.ts +130 -90
  357. package/src/__tests__/session-pre-run-repair.test.ts +106 -59
  358. package/src/__tests__/session-profile-injection.test.ts +198 -130
  359. package/src/__tests__/session-provider-retry-repair.test.ts +223 -141
  360. package/src/__tests__/session-queue.test.ts +624 -321
  361. package/src/__tests__/session-runtime-assembly.test.ts +425 -329
  362. package/src/__tests__/session-runtime-workspace.test.ts +69 -61
  363. package/src/__tests__/session-skill-tools.test.ts +973 -678
  364. package/src/__tests__/session-slash-known.test.ts +185 -133
  365. package/src/__tests__/session-slash-queue.test.ts +147 -81
  366. package/src/__tests__/session-slash-unknown.test.ts +135 -90
  367. package/src/__tests__/session-surfaces-task-progress.test.ts +122 -87
  368. package/src/__tests__/session-tool-setup-app-refresh.test.ts +338 -177
  369. package/src/__tests__/session-tool-setup-memory-scope.test.ts +63 -40
  370. package/src/__tests__/session-tool-setup-side-effect-flag.test.ts +60 -37
  371. package/src/__tests__/session-tool-setup-tools-disabled.test.ts +28 -26
  372. package/src/__tests__/session-undo.test.ts +43 -30
  373. package/src/__tests__/session-workspace-cache-state.test.ts +108 -67
  374. package/src/__tests__/session-workspace-injection.test.ts +245 -117
  375. package/src/__tests__/session-workspace-tool-tracking.test.ts +260 -93
  376. package/src/__tests__/shared-filesystem-errors.test.ts +47 -47
  377. package/src/__tests__/shell-credential-ref.test.ts +126 -90
  378. package/src/__tests__/shell-identity.test.ts +134 -111
  379. package/src/__tests__/shell-parser-fuzz.test.ts +263 -179
  380. package/src/__tests__/shell-parser-property.test.ts +435 -288
  381. package/src/__tests__/shell-tool-proxy-mode.test.ts +142 -70
  382. package/src/__tests__/size-guard.test.ts +42 -44
  383. package/src/__tests__/skill-feature-flags-integration.test.ts +79 -52
  384. package/src/__tests__/skill-feature-flags.test.ts +75 -47
  385. package/src/__tests__/skill-include-graph.test.ts +143 -148
  386. package/src/__tests__/skill-load-feature-flag.test.ts +94 -59
  387. package/src/__tests__/skill-load-tool.test.ts +371 -199
  388. package/src/__tests__/skill-projection-feature-flag.test.ts +131 -88
  389. package/src/__tests__/skill-projection.benchmark.test.ts +93 -65
  390. package/src/__tests__/skill-script-runner-host.test.ts +460 -250
  391. package/src/__tests__/skill-script-runner-sandbox.test.ts +168 -108
  392. package/src/__tests__/skill-script-runner.test.ts +115 -74
  393. package/src/__tests__/skill-tool-factory.test.ts +140 -96
  394. package/src/__tests__/skill-tool-manifest.test.ts +306 -210
  395. package/src/__tests__/skill-version-hash.test.ts +70 -56
  396. package/src/__tests__/skills.test.ts +0 -1
  397. package/src/__tests__/slack-channel-config.test.ts +127 -84
  398. package/src/__tests__/slack-skill.test.ts +60 -47
  399. package/src/__tests__/slash-commands-catalog.test.ts +37 -31
  400. package/src/__tests__/slash-commands-parser.test.ts +71 -64
  401. package/src/__tests__/slash-commands-resolver.test.ts +143 -107
  402. package/src/__tests__/slash-commands-rewrite.test.ts +22 -22
  403. package/src/__tests__/speaker-identification.test.ts +28 -25
  404. package/src/__tests__/starter-bundle.test.ts +27 -23
  405. package/src/__tests__/starter-task-flow.test.ts +67 -52
  406. package/src/__tests__/subagent-manager-notify.test.ts +154 -108
  407. package/src/__tests__/subagent-tools.test.ts +311 -270
  408. package/src/__tests__/subagent-types.test.ts +40 -40
  409. package/src/__tests__/surface-mutex-cleanup.test.ts +42 -30
  410. package/src/__tests__/swarm-dag-pathological.test.ts +122 -111
  411. package/src/__tests__/swarm-orchestrator.test.ts +135 -101
  412. package/src/__tests__/swarm-plan-validator.test.ts +125 -73
  413. package/src/__tests__/swarm-recursion.test.ts +58 -46
  414. package/src/__tests__/swarm-router-planner.test.ts +99 -74
  415. package/src/__tests__/swarm-session-integration.test.ts +148 -91
  416. package/src/__tests__/swarm-tool.test.ts +65 -45
  417. package/src/__tests__/swarm-worker-backend.test.ts +59 -45
  418. package/src/__tests__/swarm-worker-runner.test.ts +133 -118
  419. package/src/__tests__/system-prompt.test.ts +290 -256
  420. package/src/__tests__/task-compiler.test.ts +176 -120
  421. package/src/__tests__/task-management-tools.test.ts +561 -456
  422. package/src/__tests__/task-memory-cleanup.test.ts +627 -362
  423. package/src/__tests__/task-runner.test.ts +117 -94
  424. package/src/__tests__/task-scheduler.test.ts +113 -84
  425. package/src/__tests__/task-tools.test.ts +349 -264
  426. package/src/__tests__/terminal-sandbox.test.ts +138 -108
  427. package/src/__tests__/terminal-tools.test.ts +350 -305
  428. package/src/__tests__/thread-seed-composer.test.ts +307 -180
  429. package/src/__tests__/tool-approval-handler.test.ts +238 -137
  430. package/src/__tests__/tool-audit-listener.test.ts +69 -69
  431. package/src/__tests__/tool-domain-event-publisher.test.ts +142 -132
  432. package/src/__tests__/tool-execution-abort-cleanup.test.ts +153 -146
  433. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +136 -105
  434. package/src/__tests__/tool-executor-lifecycle-events.test.ts +355 -239
  435. package/src/__tests__/tool-executor-redaction.test.ts +112 -109
  436. package/src/__tests__/tool-executor-shell-integration.test.ts +130 -79
  437. package/src/__tests__/tool-executor.test.ts +1274 -674
  438. package/src/__tests__/tool-grant-request-escalation.test.ts +401 -283
  439. package/src/__tests__/tool-metrics-listener.test.ts +97 -85
  440. package/src/__tests__/tool-notification-listener.test.ts +42 -25
  441. package/src/__tests__/tool-permission-simulate-handler.test.ts +137 -113
  442. package/src/__tests__/tool-policy.test.ts +44 -25
  443. package/src/__tests__/tool-profiling-listener.test.ts +99 -93
  444. package/src/__tests__/tool-result-truncation.test.ts +5 -4
  445. package/src/__tests__/tool-trace-listener.test.ts +131 -111
  446. package/src/__tests__/top-level-renderer.test.ts +62 -58
  447. package/src/__tests__/top-level-scanner.test.ts +68 -64
  448. package/src/__tests__/trace-emitter.test.ts +56 -56
  449. package/src/__tests__/trust-context-guards.test.ts +65 -65
  450. package/src/__tests__/trust-store.test.ts +1239 -806
  451. package/src/__tests__/trusted-contact-approval-notifier.test.ts +0 -1
  452. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +0 -1
  453. package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +3 -2
  454. package/src/__tests__/trusted-contact-multichannel.test.ts +3 -2
  455. package/src/__tests__/trusted-contact-verification.test.ts +251 -231
  456. package/src/__tests__/turn-commit.test.ts +259 -200
  457. package/src/__tests__/twilio-provider.test.ts +140 -126
  458. package/src/__tests__/twilio-rest.test.ts +22 -18
  459. package/src/__tests__/twilio-routes-elevenlabs.test.ts +0 -1
  460. package/src/__tests__/twilio-routes-twiml.test.ts +55 -55
  461. package/src/__tests__/twilio-routes.test.ts +0 -1
  462. package/src/__tests__/twitter-auth-handler.test.ts +184 -139
  463. package/src/__tests__/twitter-cli-error-shaping.test.ts +88 -73
  464. package/src/__tests__/twitter-cli-routing.test.ts +146 -99
  465. package/src/__tests__/twitter-oauth-client.test.ts +82 -65
  466. package/src/__tests__/update-bulletin-format.test.ts +69 -66
  467. package/src/__tests__/update-bulletin-state.test.ts +66 -60
  468. package/src/__tests__/update-bulletin.test.ts +150 -114
  469. package/src/__tests__/update-template-contract.test.ts +15 -10
  470. package/src/__tests__/url-safety.test.ts +288 -265
  471. package/src/__tests__/user-reference.test.ts +32 -32
  472. package/src/__tests__/view-image-tool.test.ts +118 -96
  473. package/src/__tests__/voice-invite-redemption.test.ts +111 -106
  474. package/src/__tests__/voice-quality.test.ts +117 -102
  475. package/src/__tests__/voice-scoped-grant-consumer.test.ts +204 -146
  476. package/src/__tests__/voice-session-bridge.test.ts +351 -216
  477. package/src/__tests__/weather-skill-regression.test.ts +170 -120
  478. package/src/__tests__/web-fetch.test.ts +664 -526
  479. package/src/__tests__/web-search.test.ts +379 -213
  480. package/src/__tests__/work-item-output.test.ts +90 -53
  481. package/src/__tests__/workspace-git-service.test.ts +437 -356
  482. package/src/__tests__/workspace-heartbeat-service.test.ts +125 -91
  483. package/src/__tests__/workspace-lifecycle.test.ts +98 -64
  484. package/src/__tests__/workspace-policy.test.ts +139 -71
  485. package/src/commands/__tests__/cc-command-registry.test.ts +142 -134
  486. package/src/config/__tests__/feature-flag-registry-guard.test.ts +48 -39
  487. package/src/config/bundled-skills/chatgpt-import/tools/chatgpt-import.ts +25 -10
  488. package/src/config/bundled-skills/doordash/__tests__/doordash-session.test.ts +0 -1
  489. package/src/config/bundled-skills/messaging/SKILL.md +4 -3
  490. package/src/config/bundled-skills/messaging/tools/gmail-outreach-scan.ts +15 -5
  491. package/src/config/bundled-skills/messaging/tools/gmail-sender-digest.ts +16 -5
  492. package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +34 -32
  493. package/src/config/bundled-tool-registry.ts +2 -0
  494. package/src/config/env.ts +3 -4
  495. package/src/memory/db-connection.ts +16 -10
  496. package/src/messaging/providers/gmail/adapter.ts +10 -3
  497. package/src/messaging/providers/gmail/client.ts +280 -72
  498. package/src/runtime/auth/__tests__/context.test.ts +75 -65
  499. package/src/runtime/auth/__tests__/credential-service.test.ts +137 -114
  500. package/src/runtime/auth/__tests__/guard-tests.test.ts +84 -90
  501. package/src/runtime/auth/__tests__/ipc-auth-context.test.ts +40 -40
  502. package/src/runtime/auth/__tests__/middleware.test.ts +80 -74
  503. package/src/runtime/auth/__tests__/policy.test.ts +9 -9
  504. package/src/runtime/auth/__tests__/route-policy.test.ts +76 -65
  505. package/src/runtime/auth/__tests__/scopes.test.ts +68 -60
  506. package/src/runtime/auth/__tests__/subject.test.ts +54 -54
  507. package/src/runtime/auth/__tests__/token-service.test.ts +115 -108
  508. package/src/runtime/auth/scopes.ts +3 -0
  509. package/src/runtime/auth/token-service.ts +4 -1
  510. package/src/runtime/auth/types.ts +2 -1
  511. package/src/runtime/http-server.ts +2 -1
  512. package/src/security/secure-keys.ts +103 -53
  513. package/src/tools/browser/__tests__/auth-cache.test.ts +69 -63
  514. package/src/tools/browser/__tests__/auth-detector.test.ts +218 -157
  515. package/src/tools/browser/__tests__/jit-auth.test.ts +83 -99
@@ -7,15 +7,20 @@ import {
7
7
  rmSync,
8
8
  symlinkSync,
9
9
  writeFileSync,
10
- } from 'node:fs';
11
- import { tmpdir } from 'node:os';
12
- import { join } from 'node:path';
10
+ } from "node:fs";
11
+ import { tmpdir } from "node:os";
12
+ import { join } from "node:path";
13
+ import { afterEach, describe, expect, test } from "bun:test";
13
14
 
14
- import { afterEach, describe, expect, test } from 'bun:test';
15
-
16
- import { FileSystemOps, type PathPolicy } from '../tools/shared/filesystem/file-ops-service.js';
17
- import { formatEditDiff, formatWriteSummary } from '../tools/shared/filesystem/format-diff.js';
18
- import { sandboxPolicy } from '../tools/shared/filesystem/path-policy.js';
15
+ import {
16
+ FileSystemOps,
17
+ type PathPolicy,
18
+ } from "../tools/shared/filesystem/file-ops-service.js";
19
+ import {
20
+ formatEditDiff,
21
+ formatWriteSummary,
22
+ } from "../tools/shared/filesystem/format-diff.js";
23
+ import { sandboxPolicy } from "../tools/shared/filesystem/path-policy.js";
19
24
 
20
25
  // ---------------------------------------------------------------------------
21
26
  // Helpers
@@ -24,7 +29,7 @@ import { sandboxPolicy } from '../tools/shared/filesystem/path-policy.js';
24
29
  const testDirs: string[] = [];
25
30
 
26
31
  function makeTempDir(): string {
27
- const dir = realpathSync(mkdtempSync(join(tmpdir(), 'fs-tools-test-')));
32
+ const dir = realpathSync(mkdtempSync(join(tmpdir(), "fs-tools-test-")));
28
33
  testDirs.push(dir);
29
34
  return dir;
30
35
  }
@@ -43,68 +48,71 @@ function sandboxPolicyFor(boundary: string): PathPolicy {
43
48
  // FileSystemOps: symlink handling through read/write/edit
44
49
  // ===========================================================================
45
50
 
46
- describe('FileSystemOps symlink handling', () => {
47
- test('read blocks symlink pointing outside boundary', () => {
51
+ describe("FileSystemOps symlink handling", () => {
52
+ test("read blocks symlink pointing outside boundary", () => {
48
53
  const boundary = makeTempDir();
49
54
  const outside = makeTempDir();
50
- const outsideFile = join(outside, 'secret.txt');
51
- writeFileSync(outsideFile, 'secret data');
55
+ const outsideFile = join(outside, "secret.txt");
56
+ writeFileSync(outsideFile, "secret data");
52
57
 
53
- symlinkSync(outsideFile, join(boundary, 'link.txt'));
58
+ symlinkSync(outsideFile, join(boundary, "link.txt"));
54
59
  const ops = new FileSystemOps(sandboxPolicyFor(boundary));
55
60
 
56
- const result = ops.readFileSafe({ path: 'link.txt' });
61
+ const result = ops.readFileSafe({ path: "link.txt" });
57
62
  expect(result.ok).toBe(false);
58
63
  if (result.ok) return;
59
- expect(result.error.code).toBe('PATH_OUT_OF_BOUNDS');
64
+ expect(result.error.code).toBe("PATH_OUT_OF_BOUNDS");
60
65
  });
61
66
 
62
- test('read allows symlink within boundary', () => {
67
+ test("read allows symlink within boundary", () => {
63
68
  const boundary = makeTempDir();
64
- const realFile = join(boundary, 'real.txt');
65
- writeFileSync(realFile, 'hello');
66
- symlinkSync(realFile, join(boundary, 'link.txt'));
69
+ const realFile = join(boundary, "real.txt");
70
+ writeFileSync(realFile, "hello");
71
+ symlinkSync(realFile, join(boundary, "link.txt"));
67
72
 
68
73
  const ops = new FileSystemOps(sandboxPolicyFor(boundary));
69
- const result = ops.readFileSafe({ path: 'link.txt' });
74
+ const result = ops.readFileSafe({ path: "link.txt" });
70
75
  expect(result.ok).toBe(true);
71
76
  if (!result.ok) return;
72
- expect(result.value.content).toContain('hello');
77
+ expect(result.value.content).toContain("hello");
73
78
  });
74
79
 
75
- test('write blocks creating file under symlinked dir pointing outside', () => {
80
+ test("write blocks creating file under symlinked dir pointing outside", () => {
76
81
  const boundary = makeTempDir();
77
82
  const outside = makeTempDir();
78
- symlinkSync(outside, join(boundary, 'link-dir'));
83
+ symlinkSync(outside, join(boundary, "link-dir"));
79
84
 
80
85
  const ops = new FileSystemOps(sandboxPolicyFor(boundary));
81
- const result = ops.writeFileSafe({ path: 'link-dir/evil.txt', content: 'bad' });
86
+ const result = ops.writeFileSafe({
87
+ path: "link-dir/evil.txt",
88
+ content: "bad",
89
+ });
82
90
  expect(result.ok).toBe(false);
83
91
  if (result.ok) return;
84
- expect(result.error.code).toBe('PATH_OUT_OF_BOUNDS');
92
+ expect(result.error.code).toBe("PATH_OUT_OF_BOUNDS");
85
93
  // The file must NOT have been written to the outside directory
86
- expect(existsSync(join(outside, 'evil.txt'))).toBe(false);
94
+ expect(existsSync(join(outside, "evil.txt"))).toBe(false);
87
95
  });
88
96
 
89
- test('edit blocks symlink pointing outside boundary', () => {
97
+ test("edit blocks symlink pointing outside boundary", () => {
90
98
  const boundary = makeTempDir();
91
99
  const outside = makeTempDir();
92
- const outsideFile = join(outside, 'target.txt');
93
- writeFileSync(outsideFile, 'original');
94
- symlinkSync(outsideFile, join(boundary, 'link.txt'));
100
+ const outsideFile = join(outside, "target.txt");
101
+ writeFileSync(outsideFile, "original");
102
+ symlinkSync(outsideFile, join(boundary, "link.txt"));
95
103
 
96
104
  const ops = new FileSystemOps(sandboxPolicyFor(boundary));
97
105
  const result = ops.editFileSafe({
98
- path: 'link.txt',
99
- oldString: 'original',
100
- newString: 'modified',
106
+ path: "link.txt",
107
+ oldString: "original",
108
+ newString: "modified",
101
109
  replaceAll: false,
102
110
  });
103
111
  expect(result.ok).toBe(false);
104
112
  if (result.ok) return;
105
- expect(result.error.code).toBe('PATH_OUT_OF_BOUNDS');
113
+ expect(result.error.code).toBe("PATH_OUT_OF_BOUNDS");
106
114
  // The outside file must NOT have been modified
107
- expect(readFileSync(outsideFile, 'utf-8')).toBe('original');
115
+ expect(readFileSync(outsideFile, "utf-8")).toBe("original");
108
116
  });
109
117
  });
110
118
 
@@ -112,66 +120,70 @@ describe('FileSystemOps symlink handling', () => {
112
120
  // FileSystemOps: read offset/limit edge cases
113
121
  // ===========================================================================
114
122
 
115
- describe('FileSystemOps read offset/limit edge cases', () => {
116
- test('offset beyond file length returns empty content', () => {
123
+ describe("FileSystemOps read offset/limit edge cases", () => {
124
+ test("offset beyond file length returns empty content", () => {
117
125
  const dir = makeTempDir();
118
- writeFileSync(join(dir, 'short.txt'), 'a\nb\nc');
126
+ writeFileSync(join(dir, "short.txt"), "a\nb\nc");
119
127
  const ops = new FileSystemOps(sandboxPolicyFor(dir));
120
128
 
121
- const result = ops.readFileSafe({ path: 'short.txt', offset: 100 });
129
+ const result = ops.readFileSafe({ path: "short.txt", offset: 100 });
122
130
  expect(result.ok).toBe(true);
123
131
  if (!result.ok) return;
124
- expect(result.value.content).toBe('');
132
+ expect(result.value.content).toBe("");
125
133
  });
126
134
 
127
- test('limit of zero returns empty content', () => {
135
+ test("limit of zero returns empty content", () => {
128
136
  const dir = makeTempDir();
129
- writeFileSync(join(dir, 'file.txt'), 'a\nb\nc');
137
+ writeFileSync(join(dir, "file.txt"), "a\nb\nc");
130
138
  const ops = new FileSystemOps(sandboxPolicyFor(dir));
131
139
 
132
- const result = ops.readFileSafe({ path: 'file.txt', limit: 0 });
140
+ const result = ops.readFileSafe({ path: "file.txt", limit: 0 });
133
141
  expect(result.ok).toBe(true);
134
142
  if (!result.ok) return;
135
- expect(result.value.content).toBe('');
143
+ expect(result.value.content).toBe("");
136
144
  });
137
145
 
138
- test('offset=1 reads from first line (1-indexed)', () => {
146
+ test("offset=1 reads from first line (1-indexed)", () => {
139
147
  const dir = makeTempDir();
140
- writeFileSync(join(dir, 'file.txt'), 'first\nsecond\nthird');
148
+ writeFileSync(join(dir, "file.txt"), "first\nsecond\nthird");
141
149
  const ops = new FileSystemOps(sandboxPolicyFor(dir));
142
150
 
143
- const result = ops.readFileSafe({ path: 'file.txt', offset: 1, limit: 1 });
151
+ const result = ops.readFileSafe({ path: "file.txt", offset: 1, limit: 1 });
144
152
  expect(result.ok).toBe(true);
145
153
  if (!result.ok) return;
146
- expect(result.value.content).toContain('first');
147
- expect(result.value.content).not.toContain('second');
154
+ expect(result.value.content).toContain("first");
155
+ expect(result.value.content).not.toContain("second");
148
156
  });
149
157
 
150
- test('limit exceeding file length returns all remaining lines', () => {
158
+ test("limit exceeding file length returns all remaining lines", () => {
151
159
  const dir = makeTempDir();
152
- writeFileSync(join(dir, 'file.txt'), 'a\nb');
160
+ writeFileSync(join(dir, "file.txt"), "a\nb");
153
161
  const ops = new FileSystemOps(sandboxPolicyFor(dir));
154
162
 
155
- const result = ops.readFileSafe({ path: 'file.txt', offset: 1, limit: 1000 });
163
+ const result = ops.readFileSafe({
164
+ path: "file.txt",
165
+ offset: 1,
166
+ limit: 1000,
167
+ });
156
168
  expect(result.ok).toBe(true);
157
169
  if (!result.ok) return;
158
- expect(result.value.content).toContain('a');
159
- expect(result.value.content).toContain('b');
170
+ expect(result.value.content).toContain("a");
171
+ expect(result.value.content).toContain("b");
160
172
  });
161
173
 
162
- test('read adds line numbers starting from offset', () => {
174
+ test("read adds line numbers starting from offset", () => {
163
175
  const dir = makeTempDir();
164
- writeFileSync(join(dir, 'file.txt'), 'a\nb\nc\nd\ne');
176
+ writeFileSync(join(dir, "file.txt"), "a\nb\nc\nd\ne");
165
177
  const ops = new FileSystemOps(sandboxPolicyFor(dir));
166
178
 
167
- const result = ops.readFileSafe({ path: 'file.txt', offset: 3, limit: 2 });
179
+ const result = ops.readFileSafe({ path: "file.txt", offset: 3, limit: 2 });
168
180
  expect(result.ok).toBe(true);
169
181
  if (!result.ok) return;
170
182
  // Lines should be numbered 3 and 4
171
- expect(result.value.content).toContain('3');
172
- expect(result.value.content).toContain('4');
173
- expect(result.value.content).toContain('c');
174
- expect(result.value.content).toContain('d');
183
+ expect(result.value.content).toContain("3");
184
+ expect(result.value.content).toContain("4");
185
+ expect(result.value.content).toContain("c");
186
+ expect(result.value.content).toContain("d");
175
187
  });
176
188
  });
177
189
 
@@ -179,58 +191,61 @@ describe('FileSystemOps read offset/limit edge cases', () => {
179
191
  // FileSystemOps: edit with whitespace-normalized and fuzzy matches
180
192
  // ===========================================================================
181
193
 
182
- describe('FileSystemOps edit match methods', () => {
183
- test('whitespace-normalized match succeeds', () => {
194
+ describe("FileSystemOps edit match methods", () => {
195
+ test("whitespace-normalized match succeeds", () => {
184
196
  const dir = makeTempDir();
185
- writeFileSync(join(dir, 'file.txt'), ' function foo() {\n return 1;\n }\n');
197
+ writeFileSync(
198
+ join(dir, "file.txt"),
199
+ " function foo() {\n return 1;\n }\n",
200
+ );
186
201
  const ops = new FileSystemOps(sandboxPolicyFor(dir));
187
202
 
188
203
  const result = ops.editFileSafe({
189
- path: 'file.txt',
190
- oldString: 'function foo() {\n return 1;\n}',
191
- newString: 'function bar() {\n return 2;\n}',
204
+ path: "file.txt",
205
+ oldString: "function foo() {\n return 1;\n}",
206
+ newString: "function bar() {\n return 2;\n}",
192
207
  replaceAll: false,
193
208
  });
194
209
  expect(result.ok).toBe(true);
195
210
  if (!result.ok) return;
196
- expect(result.value.matchMethod).toBe('whitespace');
211
+ expect(result.value.matchMethod).toBe("whitespace");
197
212
  expect(result.value.similarity).toBe(1);
198
- expect(result.value.newContent).toContain('bar');
213
+ expect(result.value.newContent).toContain("bar");
199
214
  });
200
215
 
201
- test('fuzzy match succeeds with near-match', () => {
216
+ test("fuzzy match succeeds with near-match", () => {
202
217
  const dir = makeTempDir();
203
- writeFileSync(join(dir, 'file.txt'), 'function fooo() {\n return 1;\n}\n');
218
+ writeFileSync(join(dir, "file.txt"), "function fooo() {\n return 1;\n}\n");
204
219
  const ops = new FileSystemOps(sandboxPolicyFor(dir));
205
220
 
206
221
  const result = ops.editFileSafe({
207
- path: 'file.txt',
208
- oldString: 'function foo() {\n return 1;\n}',
209
- newString: 'function bar() {\n return 2;\n}',
222
+ path: "file.txt",
223
+ oldString: "function foo() {\n return 1;\n}",
224
+ newString: "function bar() {\n return 2;\n}",
210
225
  replaceAll: false,
211
226
  });
212
227
  expect(result.ok).toBe(true);
213
228
  if (!result.ok) return;
214
- expect(result.value.matchMethod).toBe('fuzzy');
229
+ expect(result.value.matchMethod).toBe("fuzzy");
215
230
  expect(result.value.similarity).toBeGreaterThan(0.8);
216
231
  expect(result.value.similarity).toBeLessThan(1);
217
232
  });
218
233
 
219
- test('edit returns actualOld and actualNew for fuzzy match', () => {
234
+ test("edit returns actualOld and actualNew for fuzzy match", () => {
220
235
  const dir = makeTempDir();
221
- writeFileSync(join(dir, 'file.txt'), 'function fooo() {\n return 1;\n}\n');
236
+ writeFileSync(join(dir, "file.txt"), "function fooo() {\n return 1;\n}\n");
222
237
  const ops = new FileSystemOps(sandboxPolicyFor(dir));
223
238
 
224
239
  const result = ops.editFileSafe({
225
- path: 'file.txt',
226
- oldString: 'function foo() {\n return 1;\n}',
227
- newString: 'function bar() {\n return 2;\n}',
240
+ path: "file.txt",
241
+ oldString: "function foo() {\n return 1;\n}",
242
+ newString: "function bar() {\n return 2;\n}",
228
243
  replaceAll: false,
229
244
  });
230
245
  expect(result.ok).toBe(true);
231
246
  if (!result.ok) return;
232
247
  // actualOld should be the text as it appeared in the file
233
- expect(result.value.actualOld).toContain('fooo');
248
+ expect(result.value.actualOld).toContain("fooo");
234
249
  });
235
250
  });
236
251
 
@@ -238,39 +253,42 @@ describe('FileSystemOps edit match methods', () => {
238
253
  // FileSystemOps: write overwrites and oldContent tracking
239
254
  // ===========================================================================
240
255
 
241
- describe('FileSystemOps write content tracking', () => {
242
- test('new file has empty oldContent', () => {
256
+ describe("FileSystemOps write content tracking", () => {
257
+ test("new file has empty oldContent", () => {
243
258
  const dir = makeTempDir();
244
259
  const ops = new FileSystemOps(sandboxPolicyFor(dir));
245
260
 
246
- const result = ops.writeFileSafe({ path: 'brand-new.txt', content: 'new' });
261
+ const result = ops.writeFileSafe({ path: "brand-new.txt", content: "new" });
247
262
  expect(result.ok).toBe(true);
248
263
  if (!result.ok) return;
249
- expect(result.value.oldContent).toBe('');
264
+ expect(result.value.oldContent).toBe("");
250
265
  expect(result.value.isNewFile).toBe(true);
251
266
  });
252
267
 
253
- test('overwrite tracks oldContent and newContent', () => {
268
+ test("overwrite tracks oldContent and newContent", () => {
254
269
  const dir = makeTempDir();
255
- writeFileSync(join(dir, 'existing.txt'), 'version 1');
270
+ writeFileSync(join(dir, "existing.txt"), "version 1");
256
271
  const ops = new FileSystemOps(sandboxPolicyFor(dir));
257
272
 
258
- const result = ops.writeFileSafe({ path: 'existing.txt', content: 'version 2' });
273
+ const result = ops.writeFileSafe({
274
+ path: "existing.txt",
275
+ content: "version 2",
276
+ });
259
277
  expect(result.ok).toBe(true);
260
278
  if (!result.ok) return;
261
- expect(result.value.oldContent).toBe('version 1');
262
- expect(result.value.newContent).toBe('version 2');
279
+ expect(result.value.oldContent).toBe("version 1");
280
+ expect(result.value.newContent).toBe("version 2");
263
281
  expect(result.value.isNewFile).toBe(false);
264
282
  });
265
283
 
266
- test('write returns resolved absolute path', () => {
284
+ test("write returns resolved absolute path", () => {
267
285
  const dir = makeTempDir();
268
286
  const ops = new FileSystemOps(sandboxPolicyFor(dir));
269
287
 
270
- const result = ops.writeFileSafe({ path: 'output.txt', content: 'data' });
288
+ const result = ops.writeFileSafe({ path: "output.txt", content: "data" });
271
289
  expect(result.ok).toBe(true);
272
290
  if (!result.ok) return;
273
- expect(result.value.filePath).toBe(join(dir, 'output.txt'));
291
+ expect(result.value.filePath).toBe(join(dir, "output.txt"));
274
292
  });
275
293
  });
276
294
 
@@ -278,39 +296,41 @@ describe('FileSystemOps write content tracking', () => {
278
296
  // formatEditDiff
279
297
  // ===========================================================================
280
298
 
281
- describe('formatEditDiff', () => {
282
- test('shows removed and added lines', () => {
283
- const result = formatEditDiff('old line', 'new line');
284
- expect(result).toContain('- old line');
285
- expect(result).toContain('+ new line');
299
+ describe("formatEditDiff", () => {
300
+ test("shows removed and added lines", () => {
301
+ const result = formatEditDiff("old line", "new line");
302
+ expect(result).toContain("- old line");
303
+ expect(result).toContain("+ new line");
286
304
  });
287
305
 
288
- test('handles multi-line changes', () => {
289
- const result = formatEditDiff('a\nb\nc', 'x\ny\nz');
290
- expect(result).toContain('- a');
291
- expect(result).toContain('- b');
292
- expect(result).toContain('- c');
293
- expect(result).toContain('+ x');
294
- expect(result).toContain('+ y');
295
- expect(result).toContain('+ z');
306
+ test("handles multi-line changes", () => {
307
+ const result = formatEditDiff("a\nb\nc", "x\ny\nz");
308
+ expect(result).toContain("- a");
309
+ expect(result).toContain("- b");
310
+ expect(result).toContain("- c");
311
+ expect(result).toContain("+ x");
312
+ expect(result).toContain("+ y");
313
+ expect(result).toContain("+ z");
296
314
  });
297
315
 
298
- test('handles empty old string (pure addition)', () => {
299
- const result = formatEditDiff('', 'added');
300
- expect(result).not.toContain('- ');
301
- expect(result).toContain('+ added');
316
+ test("handles empty old string (pure addition)", () => {
317
+ const result = formatEditDiff("", "added");
318
+ expect(result).not.toContain("- ");
319
+ expect(result).toContain("+ added");
302
320
  });
303
321
 
304
- test('handles empty new string (pure deletion)', () => {
305
- const result = formatEditDiff('removed', '');
306
- expect(result).toContain('- removed');
307
- expect(result).not.toContain('+ ');
322
+ test("handles empty new string (pure deletion)", () => {
323
+ const result = formatEditDiff("removed", "");
324
+ expect(result).toContain("- removed");
325
+ expect(result).not.toContain("+ ");
308
326
  });
309
327
 
310
- test('truncates long diffs beyond 8 lines', () => {
311
- const longOld = Array.from({ length: 12 }, (_, i) => `old-line-${i}`).join('\n');
312
- const result = formatEditDiff(longOld, 'short');
313
- expect(result).toContain('more lines');
328
+ test("truncates long diffs beyond 8 lines", () => {
329
+ const longOld = Array.from({ length: 12 }, (_, i) => `old-line-${i}`).join(
330
+ "\n",
331
+ );
332
+ const result = formatEditDiff(longOld, "short");
333
+ expect(result).toContain("more lines");
314
334
  });
315
335
  });
316
336
 
@@ -318,23 +338,23 @@ describe('formatEditDiff', () => {
318
338
  // formatWriteSummary
319
339
  // ===========================================================================
320
340
 
321
- describe('formatWriteSummary', () => {
322
- test('new file summary includes line count', () => {
323
- const result = formatWriteSummary('', 'line1\nline2\nline3', true);
324
- expect(result).toContain('new file');
325
- expect(result).toContain('3 lines');
341
+ describe("formatWriteSummary", () => {
342
+ test("new file summary includes line count", () => {
343
+ const result = formatWriteSummary("", "line1\nline2\nline3", true);
344
+ expect(result).toContain("new file");
345
+ expect(result).toContain("3 lines");
326
346
  });
327
347
 
328
- test('new file with single line uses singular', () => {
329
- const result = formatWriteSummary('', 'single', true);
330
- expect(result).toContain('1 line');
331
- expect(result).not.toContain('1 lines');
348
+ test("new file with single line uses singular", () => {
349
+ const result = formatWriteSummary("", "single", true);
350
+ expect(result).toContain("1 line");
351
+ expect(result).not.toContain("1 lines");
332
352
  });
333
353
 
334
- test('overwrite summary shows line count change', () => {
335
- const result = formatWriteSummary('a\nb', 'x\ny\nz', false);
336
- expect(result).toContain('2');
337
- expect(result).toContain('3');
354
+ test("overwrite summary shows line count change", () => {
355
+ const result = formatWriteSummary("a\nb", "x\ny\nz", false);
356
+ expect(result).toContain("2");
357
+ expect(result).toContain("3");
338
358
  });
339
359
  });
340
360
 
@@ -342,62 +362,65 @@ describe('formatWriteSummary', () => {
342
362
  // FileSystemOps: path traversal patterns
343
363
  // ===========================================================================
344
364
 
345
- describe('FileSystemOps path traversal prevention', () => {
346
- test('rejects absolute path outside boundary on read', () => {
365
+ describe("FileSystemOps path traversal prevention", () => {
366
+ test("rejects absolute path outside boundary on read", () => {
347
367
  const dir = makeTempDir();
348
368
  const ops = new FileSystemOps(sandboxPolicyFor(dir));
349
369
 
350
- const result = ops.readFileSafe({ path: '/etc/passwd' });
370
+ const result = ops.readFileSafe({ path: "/etc/passwd" });
351
371
  expect(result.ok).toBe(false);
352
372
  if (result.ok) return;
353
- expect(result.error.code).toBe('PATH_OUT_OF_BOUNDS');
373
+ expect(result.error.code).toBe("PATH_OUT_OF_BOUNDS");
354
374
  });
355
375
 
356
- test('rejects absolute path outside boundary on write', () => {
376
+ test("rejects absolute path outside boundary on write", () => {
357
377
  const dir = makeTempDir();
358
378
  const ops = new FileSystemOps(sandboxPolicyFor(dir));
359
379
 
360
- const result = ops.writeFileSafe({ path: '/tmp/evil-write.txt', content: 'bad' });
380
+ const result = ops.writeFileSafe({
381
+ path: "/tmp/evil-write.txt",
382
+ content: "bad",
383
+ });
361
384
  expect(result.ok).toBe(false);
362
385
  if (result.ok) return;
363
- expect(result.error.code).toBe('PATH_OUT_OF_BOUNDS');
386
+ expect(result.error.code).toBe("PATH_OUT_OF_BOUNDS");
364
387
  });
365
388
 
366
- test('rejects absolute path outside boundary on edit', () => {
389
+ test("rejects absolute path outside boundary on edit", () => {
367
390
  const dir = makeTempDir();
368
391
  const ops = new FileSystemOps(sandboxPolicyFor(dir));
369
392
 
370
393
  const result = ops.editFileSafe({
371
- path: '/etc/hosts',
372
- oldString: 'a',
373
- newString: 'b',
394
+ path: "/etc/hosts",
395
+ oldString: "a",
396
+ newString: "b",
374
397
  replaceAll: false,
375
398
  });
376
399
  expect(result.ok).toBe(false);
377
400
  if (result.ok) return;
378
- expect(result.error.code).toBe('PATH_OUT_OF_BOUNDS');
401
+ expect(result.error.code).toBe("PATH_OUT_OF_BOUNDS");
379
402
  });
380
403
 
381
- test('rejects dot-dot traversal embedded in path on read', () => {
404
+ test("rejects dot-dot traversal embedded in path on read", () => {
382
405
  const dir = makeTempDir();
383
- mkdirSync(join(dir, 'sub'));
406
+ mkdirSync(join(dir, "sub"));
384
407
  const ops = new FileSystemOps(sandboxPolicyFor(dir));
385
408
 
386
- const result = ops.readFileSafe({ path: 'sub/../../etc/passwd' });
409
+ const result = ops.readFileSafe({ path: "sub/../../etc/passwd" });
387
410
  expect(result.ok).toBe(false);
388
411
  if (result.ok) return;
389
- expect(result.error.code).toBe('PATH_OUT_OF_BOUNDS');
412
+ expect(result.error.code).toBe("PATH_OUT_OF_BOUNDS");
390
413
  });
391
414
 
392
- test('accepts absolute path inside boundary', () => {
415
+ test("accepts absolute path inside boundary", () => {
393
416
  const dir = makeTempDir();
394
- writeFileSync(join(dir, 'inside.txt'), 'safe content');
417
+ writeFileSync(join(dir, "inside.txt"), "safe content");
395
418
  const ops = new FileSystemOps(sandboxPolicyFor(dir));
396
419
 
397
- const result = ops.readFileSafe({ path: join(dir, 'inside.txt') });
420
+ const result = ops.readFileSafe({ path: join(dir, "inside.txt") });
398
421
  expect(result.ok).toBe(true);
399
422
  if (!result.ok) return;
400
- expect(result.value.content).toContain('safe content');
423
+ expect(result.value.content).toContain("safe content");
401
424
  });
402
425
  });
403
426
 
@@ -405,14 +428,14 @@ describe('FileSystemOps path traversal prevention', () => {
405
428
  // FileSystemOps: binary file handling on read
406
429
  // ===========================================================================
407
430
 
408
- describe('FileSystemOps binary file read', () => {
409
- test('reads binary content as utf-8 without crashing', () => {
431
+ describe("FileSystemOps binary file read", () => {
432
+ test("reads binary content as utf-8 without crashing", () => {
410
433
  const dir = makeTempDir();
411
- const binaryContent = Buffer.from([0x00, 0xFF, 0x89, 0x50, 0x4E, 0x47]);
412
- writeFileSync(join(dir, 'binary.bin'), binaryContent);
434
+ const binaryContent = Buffer.from([0x00, 0xff, 0x89, 0x50, 0x4e, 0x47]);
435
+ writeFileSync(join(dir, "binary.bin"), binaryContent);
413
436
  const ops = new FileSystemOps(sandboxPolicyFor(dir));
414
437
 
415
- const result = ops.readFileSafe({ path: 'binary.bin' });
438
+ const result = ops.readFileSafe({ path: "binary.bin" });
416
439
  // Should succeed — the file is readable, even if content has replacement chars
417
440
  expect(result.ok).toBe(true);
418
441
  });
@@ -422,44 +445,44 @@ describe('FileSystemOps binary file read', () => {
422
445
  // FileSystemOps: empty file handling
423
446
  // ===========================================================================
424
447
 
425
- describe('FileSystemOps empty file operations', () => {
426
- test('reads empty file successfully', () => {
448
+ describe("FileSystemOps empty file operations", () => {
449
+ test("reads empty file successfully", () => {
427
450
  const dir = makeTempDir();
428
- writeFileSync(join(dir, 'empty.txt'), '');
451
+ writeFileSync(join(dir, "empty.txt"), "");
429
452
  const ops = new FileSystemOps(sandboxPolicyFor(dir));
430
453
 
431
- const result = ops.readFileSafe({ path: 'empty.txt' });
454
+ const result = ops.readFileSafe({ path: "empty.txt" });
432
455
  expect(result.ok).toBe(true);
433
456
  if (!result.ok) return;
434
457
  // Empty file still has one "line" (the empty string before any newline)
435
458
  expect(result.value.content).toBeDefined();
436
459
  });
437
460
 
438
- test('write empty content creates empty file', () => {
461
+ test("write empty content creates empty file", () => {
439
462
  const dir = makeTempDir();
440
463
  const ops = new FileSystemOps(sandboxPolicyFor(dir));
441
464
 
442
- const result = ops.writeFileSafe({ path: 'empty.txt', content: '' });
465
+ const result = ops.writeFileSafe({ path: "empty.txt", content: "" });
443
466
  expect(result.ok).toBe(true);
444
467
  if (!result.ok) return;
445
468
  expect(result.value.isNewFile).toBe(true);
446
- expect(readFileSync(join(dir, 'empty.txt'), 'utf-8')).toBe('');
469
+ expect(readFileSync(join(dir, "empty.txt"), "utf-8")).toBe("");
447
470
  });
448
471
 
449
- test('edit on empty file returns MATCH_NOT_FOUND', () => {
472
+ test("edit on empty file returns MATCH_NOT_FOUND", () => {
450
473
  const dir = makeTempDir();
451
- writeFileSync(join(dir, 'empty.txt'), '');
474
+ writeFileSync(join(dir, "empty.txt"), "");
452
475
  const ops = new FileSystemOps(sandboxPolicyFor(dir));
453
476
 
454
477
  const result = ops.editFileSafe({
455
- path: 'empty.txt',
456
- oldString: 'something',
457
- newString: 'else',
478
+ path: "empty.txt",
479
+ oldString: "something",
480
+ newString: "else",
458
481
  replaceAll: false,
459
482
  });
460
483
  expect(result.ok).toBe(false);
461
484
  if (result.ok) return;
462
- expect(result.error.code).toBe('MATCH_NOT_FOUND');
485
+ expect(result.error.code).toBe("MATCH_NOT_FOUND");
463
486
  });
464
487
  });
465
488
 
@@ -467,54 +490,57 @@ describe('FileSystemOps empty file operations', () => {
467
490
  // FileSystemOps: container /workspace path remapping
468
491
  // ===========================================================================
469
492
 
470
- describe('FileSystemOps /workspace path remapping', () => {
471
- test('read remaps /workspace/ path to boundary', () => {
493
+ describe("FileSystemOps /workspace path remapping", () => {
494
+ test("read remaps /workspace/ path to boundary", () => {
472
495
  const dir = makeTempDir();
473
- writeFileSync(join(dir, 'file.txt'), 'workspace content');
496
+ writeFileSync(join(dir, "file.txt"), "workspace content");
474
497
  const ops = new FileSystemOps(sandboxPolicyFor(dir));
475
498
 
476
- const result = ops.readFileSafe({ path: '/workspace/file.txt' });
499
+ const result = ops.readFileSafe({ path: "/workspace/file.txt" });
477
500
  expect(result.ok).toBe(true);
478
501
  if (!result.ok) return;
479
- expect(result.value.content).toContain('workspace content');
502
+ expect(result.value.content).toContain("workspace content");
480
503
  });
481
504
 
482
- test('write remaps /workspace/ path to boundary', () => {
505
+ test("write remaps /workspace/ path to boundary", () => {
483
506
  const dir = makeTempDir();
484
507
  const ops = new FileSystemOps(sandboxPolicyFor(dir));
485
508
 
486
- const result = ops.writeFileSafe({ path: '/workspace/new.txt', content: 'remapped' });
509
+ const result = ops.writeFileSafe({
510
+ path: "/workspace/new.txt",
511
+ content: "remapped",
512
+ });
487
513
  expect(result.ok).toBe(true);
488
514
  if (!result.ok) return;
489
- expect(existsSync(join(dir, 'new.txt'))).toBe(true);
490
- expect(readFileSync(join(dir, 'new.txt'), 'utf-8')).toBe('remapped');
515
+ expect(existsSync(join(dir, "new.txt"))).toBe(true);
516
+ expect(readFileSync(join(dir, "new.txt"), "utf-8")).toBe("remapped");
491
517
  });
492
518
 
493
- test('edit remaps /workspace/ path to boundary', () => {
519
+ test("edit remaps /workspace/ path to boundary", () => {
494
520
  const dir = makeTempDir();
495
- writeFileSync(join(dir, 'file.txt'), 'old content');
521
+ writeFileSync(join(dir, "file.txt"), "old content");
496
522
  const ops = new FileSystemOps(sandboxPolicyFor(dir));
497
523
 
498
524
  const result = ops.editFileSafe({
499
- path: '/workspace/file.txt',
500
- oldString: 'old content',
501
- newString: 'new content',
525
+ path: "/workspace/file.txt",
526
+ oldString: "old content",
527
+ newString: "new content",
502
528
  replaceAll: false,
503
529
  });
504
530
  expect(result.ok).toBe(true);
505
531
  if (!result.ok) return;
506
- expect(result.value.newContent).toBe('new content');
507
- expect(readFileSync(join(dir, 'file.txt'), 'utf-8')).toBe('new content');
532
+ expect(result.value.newContent).toBe("new content");
533
+ expect(readFileSync(join(dir, "file.txt"), "utf-8")).toBe("new content");
508
534
  });
509
535
 
510
- test('/workspace traversal escape is blocked', () => {
536
+ test("/workspace traversal escape is blocked", () => {
511
537
  const dir = makeTempDir();
512
538
  const ops = new FileSystemOps(sandboxPolicyFor(dir));
513
539
 
514
- const result = ops.readFileSafe({ path: '/workspace/../../../etc/passwd' });
540
+ const result = ops.readFileSafe({ path: "/workspace/../../../etc/passwd" });
515
541
  expect(result.ok).toBe(false);
516
542
  if (result.ok) return;
517
- expect(result.error.code).toBe('PATH_OUT_OF_BOUNDS');
543
+ expect(result.error.code).toBe("PATH_OUT_OF_BOUNDS");
518
544
  });
519
545
  });
520
546
 
@@ -522,59 +548,62 @@ describe('FileSystemOps /workspace path remapping', () => {
522
548
  // FileSystemOps: custom size limit enforcement
523
549
  // ===========================================================================
524
550
 
525
- describe('FileSystemOps custom size limit', () => {
526
- test('read rejects file exceeding custom limit', () => {
551
+ describe("FileSystemOps custom size limit", () => {
552
+ test("read rejects file exceeding custom limit", () => {
527
553
  const dir = makeTempDir();
528
- writeFileSync(join(dir, 'big.txt'), 'x'.repeat(500));
554
+ writeFileSync(join(dir, "big.txt"), "x".repeat(500));
529
555
  const ops = new FileSystemOps(sandboxPolicyFor(dir), { sizeLimit: 100 });
530
556
 
531
- const result = ops.readFileSafe({ path: 'big.txt' });
557
+ const result = ops.readFileSafe({ path: "big.txt" });
532
558
  expect(result.ok).toBe(false);
533
559
  if (result.ok) return;
534
- expect(result.error.code).toBe('SIZE_LIMIT_EXCEEDED');
560
+ expect(result.error.code).toBe("SIZE_LIMIT_EXCEEDED");
535
561
  });
536
562
 
537
- test('read accepts file within custom limit', () => {
563
+ test("read accepts file within custom limit", () => {
538
564
  const dir = makeTempDir();
539
- writeFileSync(join(dir, 'small.txt'), 'x'.repeat(50));
565
+ writeFileSync(join(dir, "small.txt"), "x".repeat(50));
540
566
  const ops = new FileSystemOps(sandboxPolicyFor(dir), { sizeLimit: 100 });
541
567
 
542
- const result = ops.readFileSafe({ path: 'small.txt' });
568
+ const result = ops.readFileSafe({ path: "small.txt" });
543
569
  expect(result.ok).toBe(true);
544
570
  });
545
571
 
546
- test('write rejects content exceeding custom limit', () => {
572
+ test("write rejects content exceeding custom limit", () => {
547
573
  const dir = makeTempDir();
548
574
  const ops = new FileSystemOps(sandboxPolicyFor(dir), { sizeLimit: 100 });
549
575
 
550
- const result = ops.writeFileSafe({ path: 'big.txt', content: 'x'.repeat(500) });
576
+ const result = ops.writeFileSafe({
577
+ path: "big.txt",
578
+ content: "x".repeat(500),
579
+ });
551
580
  expect(result.ok).toBe(false);
552
581
  if (result.ok) return;
553
- expect(result.error.code).toBe('SIZE_LIMIT_EXCEEDED');
582
+ expect(result.error.code).toBe("SIZE_LIMIT_EXCEEDED");
554
583
  });
555
584
 
556
- test('edit rejects file exceeding custom limit', () => {
585
+ test("edit rejects file exceeding custom limit", () => {
557
586
  const dir = makeTempDir();
558
- writeFileSync(join(dir, 'big.txt'), 'x'.repeat(500));
587
+ writeFileSync(join(dir, "big.txt"), "x".repeat(500));
559
588
  const ops = new FileSystemOps(sandboxPolicyFor(dir), { sizeLimit: 100 });
560
589
 
561
590
  const result = ops.editFileSafe({
562
- path: 'big.txt',
563
- oldString: 'x',
564
- newString: 'y',
591
+ path: "big.txt",
592
+ oldString: "x",
593
+ newString: "y",
565
594
  replaceAll: false,
566
595
  });
567
596
  expect(result.ok).toBe(false);
568
597
  if (result.ok) return;
569
- expect(result.error.code).toBe('SIZE_LIMIT_EXCEEDED');
598
+ expect(result.error.code).toBe("SIZE_LIMIT_EXCEEDED");
570
599
  });
571
600
 
572
- test('no size limit when not specified (defaults to 100MB)', () => {
601
+ test("no size limit when not specified (defaults to 100MB)", () => {
573
602
  const dir = makeTempDir();
574
- writeFileSync(join(dir, 'file.txt'), 'x'.repeat(1000));
603
+ writeFileSync(join(dir, "file.txt"), "x".repeat(1000));
575
604
  const ops = new FileSystemOps(sandboxPolicyFor(dir));
576
605
 
577
- const result = ops.readFileSafe({ path: 'file.txt' });
606
+ const result = ops.readFileSafe({ path: "file.txt" });
578
607
  expect(result.ok).toBe(true);
579
608
  });
580
609
  });