@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.
- package/eslint.config.mjs +2 -2
- package/package.json +1 -1
- package/src/__tests__/access-request-decision.test.ts +128 -120
- package/src/__tests__/account-registry.test.ts +121 -110
- package/src/__tests__/active-skill-tools.test.ts +200 -172
- package/src/__tests__/actor-token-service.test.ts +341 -274
- package/src/__tests__/agent-loop-thinking.test.ts +28 -19
- package/src/__tests__/agent-loop.test.ts +798 -378
- package/src/__tests__/anthropic-provider.test.ts +405 -247
- package/src/__tests__/app-builder-tool-scripts.test.ts +97 -97
- package/src/__tests__/app-bundler.test.ts +112 -79
- package/src/__tests__/app-executors.test.ts +205 -178
- package/src/__tests__/app-git-history.test.ts +90 -73
- package/src/__tests__/app-git-service.test.ts +67 -53
- package/src/__tests__/app-open-proxy.test.ts +29 -25
- package/src/__tests__/approval-conversation-turn.test.ts +100 -81
- package/src/__tests__/approval-hardcoded-copy-guard.test.ts +45 -17
- package/src/__tests__/approval-message-composer.test.ts +119 -119
- package/src/__tests__/approval-primitive.test.ts +264 -233
- package/src/__tests__/approval-routes-http.test.ts +4 -3
- package/src/__tests__/asset-materialize-tool.test.ts +250 -178
- package/src/__tests__/asset-search-tool.test.ts +251 -191
- package/src/__tests__/assistant-attachment-directive.test.ts +187 -142
- package/src/__tests__/assistant-attachments.test.ts +254 -186
- package/src/__tests__/assistant-event-hub.test.ts +105 -63
- package/src/__tests__/assistant-event.test.ts +66 -58
- package/src/__tests__/assistant-events-sse-hardening.test.ts +113 -73
- package/src/__tests__/assistant-feature-flag-guard.test.ts +78 -52
- package/src/__tests__/assistant-feature-flag-guardrails.test.ts +48 -45
- package/src/__tests__/assistant-feature-flags-integration.test.ts +118 -77
- package/src/__tests__/assistant-id-boundary-guard.test.ts +158 -104
- package/src/__tests__/attachments-store.test.ts +240 -183
- package/src/__tests__/attachments.test.ts +70 -62
- package/src/__tests__/audit-log-rotation.test.ts +50 -35
- package/src/__tests__/browser-fill-credential.test.ts +169 -101
- package/src/__tests__/browser-manager.test.ts +97 -75
- package/src/__tests__/browser-runtime-check.test.ts +16 -15
- package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +12 -10
- package/src/__tests__/browser-skill-endstate.test.ts +97 -72
- package/src/__tests__/bundle-scanner.test.ts +47 -22
- package/src/__tests__/bundled-asset.test.ts +74 -47
- package/src/__tests__/call-constants.test.ts +19 -19
- package/src/__tests__/call-controller.test.ts +0 -1
- package/src/__tests__/call-conversation-messages.test.ts +90 -65
- package/src/__tests__/call-domain.test.ts +149 -121
- package/src/__tests__/call-pointer-message-composer.test.ts +113 -83
- package/src/__tests__/call-pointer-messages.test.ts +213 -154
- package/src/__tests__/call-pointer-no-hardcoded-copy.guard.test.ts +9 -10
- package/src/__tests__/call-recovery.test.ts +232 -212
- package/src/__tests__/call-routes-http.test.ts +0 -1
- package/src/__tests__/call-start-guardian-guard.test.ts +32 -30
- package/src/__tests__/call-state-machine.test.ts +62 -51
- package/src/__tests__/call-state.test.ts +89 -75
- package/src/__tests__/call-store.test.ts +387 -316
- package/src/__tests__/callback-handoff-copy.test.ts +84 -82
- package/src/__tests__/canonical-guardian-store.test.ts +331 -280
- package/src/__tests__/channel-approval-routes.test.ts +1643 -1115
- package/src/__tests__/channel-approval.test.ts +139 -137
- package/src/__tests__/channel-approvals.test.ts +0 -1
- package/src/__tests__/channel-delivery-store.test.ts +232 -194
- package/src/__tests__/channel-guardian.test.ts +5 -3
- package/src/__tests__/channel-invite-transport.test.ts +107 -92
- package/src/__tests__/channel-policy.test.ts +42 -38
- package/src/__tests__/channel-readiness-service.test.ts +119 -102
- package/src/__tests__/channel-reply-delivery.test.ts +147 -118
- package/src/__tests__/channel-retry-sweep.test.ts +153 -110
- package/src/__tests__/checker.test.ts +3309 -1850
- package/src/__tests__/clarification-resolver.test.ts +91 -79
- package/src/__tests__/classifier.test.ts +64 -54
- package/src/__tests__/claude-code-skill-regression.test.ts +42 -37
- package/src/__tests__/claude-code-tool-profiles.test.ts +31 -29
- package/src/__tests__/clawhub.test.ts +92 -82
- package/src/__tests__/cli.test.ts +30 -30
- package/src/__tests__/clipboard.test.ts +53 -46
- package/src/__tests__/commit-guarantee.test.ts +59 -52
- package/src/__tests__/commit-message-enrichment-service.test.ts +203 -75
- package/src/__tests__/compaction.benchmark.test.ts +33 -31
- package/src/__tests__/computer-use-session-compaction.test.ts +60 -50
- package/src/__tests__/computer-use-session-lifecycle.test.ts +145 -117
- package/src/__tests__/computer-use-session-working-dir.test.ts +62 -48
- package/src/__tests__/computer-use-skill-baseline.test.ts +22 -19
- package/src/__tests__/computer-use-skill-endstate.test.ts +45 -31
- package/src/__tests__/computer-use-skill-lifecycle-cleanup.test.ts +121 -88
- package/src/__tests__/computer-use-skill-manifest-regression.test.ts +65 -42
- package/src/__tests__/computer-use-skill-proxy-bridge.test.ts +33 -18
- package/src/__tests__/computer-use-tools.test.ts +121 -98
- package/src/__tests__/config-schema.test.ts +443 -347
- package/src/__tests__/config-watcher.test.ts +96 -81
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +148 -133
- package/src/__tests__/conflict-intent-tokenization.test.ts +96 -78
- package/src/__tests__/conflict-policy.test.ts +151 -80
- package/src/__tests__/conflict-store.test.ts +203 -157
- package/src/__tests__/connection-policy.test.ts +89 -59
- package/src/__tests__/contacts-tools.test.ts +247 -178
- package/src/__tests__/context-memory-e2e.test.ts +306 -214
- package/src/__tests__/context-token-estimator.test.ts +114 -74
- package/src/__tests__/context-window-manager.test.ts +269 -167
- package/src/__tests__/contradiction-checker.test.ts +161 -135
- package/src/__tests__/conversation-attention-store.test.ts +350 -290
- package/src/__tests__/conversation-attention-telegram.test.ts +0 -1
- package/src/__tests__/conversation-pairing.test.ts +220 -113
- package/src/__tests__/conversation-store.test.ts +390 -235
- package/src/__tests__/credential-broker-browser-fill.test.ts +325 -250
- package/src/__tests__/credential-broker-server-use.test.ts +283 -243
- package/src/__tests__/credential-broker.test.ts +128 -74
- package/src/__tests__/credential-host-pattern-match.test.ts +64 -44
- package/src/__tests__/credential-metadata-store.test.ts +360 -311
- package/src/__tests__/credential-policy-validate.test.ts +81 -65
- package/src/__tests__/credential-resolve.test.ts +212 -145
- package/src/__tests__/credential-security-e2e.test.ts +144 -103
- package/src/__tests__/credential-security-invariants.test.ts +253 -208
- package/src/__tests__/credential-selection.test.ts +254 -146
- package/src/__tests__/credential-vault-unit.test.ts +531 -341
- package/src/__tests__/credential-vault.test.ts +761 -484
- package/src/__tests__/daemon-assistant-events.test.ts +91 -66
- package/src/__tests__/daemon-lifecycle.test.ts +258 -190
- package/src/__tests__/daemon-server-session-init.test.ts +0 -1
- package/src/__tests__/date-context.test.ts +314 -249
- package/src/__tests__/db-migration-rollback.test.ts +259 -130
- package/src/__tests__/db-schedule-syntax-migration.test.ts +78 -41
- package/src/__tests__/delete-managed-skill-tool.test.ts +77 -53
- package/src/__tests__/deterministic-verification-control-plane.test.ts +0 -1
- package/src/__tests__/dictation-mode-detection.test.ts +77 -55
- package/src/__tests__/dictation-profile-store.test.ts +70 -56
- package/src/__tests__/dictation-text-processing.test.ts +53 -35
- package/src/__tests__/diff.test.ts +102 -98
- package/src/__tests__/domain-normalize.test.ts +54 -54
- package/src/__tests__/domain-policy.test.ts +71 -55
- package/src/__tests__/dynamic-page-surface.test.ts +31 -33
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +69 -69
- package/src/__tests__/edit-engine.test.ts +56 -56
- package/src/__tests__/elevenlabs-client.test.ts +117 -91
- package/src/__tests__/elevenlabs-config.test.ts +32 -31
- package/src/__tests__/email-classifier.test.ts +15 -12
- package/src/__tests__/email-cli.test.ts +121 -108
- package/src/__tests__/emit-signal-routing-intent.test.ts +76 -69
- package/src/__tests__/encrypted-store.test.ts +180 -154
- package/src/__tests__/entity-extractor.test.ts +108 -87
- package/src/__tests__/entity-search.test.ts +664 -258
- package/src/__tests__/ephemeral-permissions.test.ts +224 -188
- package/src/__tests__/event-bus.test.ts +81 -77
- package/src/__tests__/extract-email.test.ts +29 -20
- package/src/__tests__/file-edit-tool.test.ts +62 -44
- package/src/__tests__/file-ops-service.test.ts +131 -114
- package/src/__tests__/file-read-tool.test.ts +48 -31
- package/src/__tests__/file-write-tool.test.ts +43 -37
- package/src/__tests__/filesystem-tools.test.ts +238 -209
- package/src/__tests__/followup-tools.test.ts +237 -162
- package/src/__tests__/forbidden-legacy-symbols.test.ts +19 -20
- package/src/__tests__/frontmatter.test.ts +96 -81
- package/src/__tests__/fuzzy-match-property.test.ts +75 -81
- package/src/__tests__/fuzzy-match.test.ts +71 -65
- package/src/__tests__/gateway-client-managed-outbound.test.ts +76 -57
- package/src/__tests__/gateway-only-enforcement.test.ts +0 -1
- package/src/__tests__/gateway-only-guard.test.ts +0 -1
- package/src/__tests__/gemini-image-service.test.ts +113 -100
- package/src/__tests__/gemini-provider.test.ts +297 -220
- package/src/__tests__/get-weather.test.ts +188 -114
- package/src/__tests__/gmail-integration.test.ts +0 -1
- package/src/__tests__/guardian-action-conversation-turn.test.ts +226 -171
- package/src/__tests__/guardian-action-copy-generator.test.ts +111 -93
- package/src/__tests__/guardian-action-followup-executor.test.ts +0 -1
- package/src/__tests__/guardian-action-followup-store.test.ts +199 -167
- package/src/__tests__/guardian-action-grant-mint-consume.test.ts +297 -250
- package/src/__tests__/guardian-action-late-reply.test.ts +462 -316
- package/src/__tests__/guardian-action-no-hardcoded-copy.test.ts +23 -18
- package/src/__tests__/guardian-action-store.test.ts +158 -109
- package/src/__tests__/guardian-action-sweep.test.ts +114 -100
- package/src/__tests__/guardian-actions-endpoint.test.ts +440 -256
- package/src/__tests__/guardian-control-plane-policy.test.ts +497 -331
- package/src/__tests__/guardian-decision-primitive-canonical.test.ts +217 -215
- package/src/__tests__/guardian-dispatch.test.ts +316 -256
- package/src/__tests__/guardian-grant-minting.test.ts +247 -178
- package/src/__tests__/guardian-outbound-http.test.ts +5 -3
- package/src/__tests__/guardian-principal-id-roundtrip.test.ts +99 -96
- package/src/__tests__/guardian-question-copy.test.ts +17 -17
- package/src/__tests__/guardian-question-mode.test.ts +134 -100
- package/src/__tests__/guardian-routing-invariants.test.ts +0 -1
- package/src/__tests__/guardian-routing-state.test.ts +0 -1
- package/src/__tests__/guardian-verification-intent-routing.test.ts +94 -88
- package/src/__tests__/guardian-verification-voice-binding.test.ts +0 -1
- package/src/__tests__/guardian-verify-setup-skill-regression.test.ts +0 -1
- package/src/__tests__/handle-user-message-secret-resume.test.ts +0 -1
- package/src/__tests__/handlers-add-trust-rule-metadata.test.ts +92 -76
- package/src/__tests__/handlers-cu-observation-blob.test.ts +103 -70
- package/src/__tests__/handlers-ipc-blob-probe.test.ts +77 -51
- package/src/__tests__/handlers-slack-config.test.ts +63 -54
- package/src/__tests__/handlers-task-submit-slash.test.ts +18 -18
- package/src/__tests__/handlers-telegram-config.test.ts +662 -329
- package/src/__tests__/handlers-twitter-config.test.ts +525 -298
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +3 -2
- package/src/__tests__/headless-browser-interactions.test.ts +444 -280
- package/src/__tests__/headless-browser-navigate.test.ts +116 -79
- package/src/__tests__/headless-browser-read-tools.test.ts +123 -86
- package/src/__tests__/headless-browser-snapshot.test.ts +71 -56
- package/src/__tests__/heartbeat-service.test.ts +76 -58
- package/src/__tests__/history-repair-observability.test.ts +14 -14
- package/src/__tests__/history-repair.test.ts +171 -167
- package/src/__tests__/home-base-bootstrap.test.ts +30 -27
- package/src/__tests__/hooks-blocking.test.ts +86 -37
- package/src/__tests__/hooks-cli.test.ts +104 -68
- package/src/__tests__/hooks-config.test.ts +81 -43
- package/src/__tests__/hooks-discovery.test.ts +106 -96
- package/src/__tests__/hooks-integration.test.ts +78 -72
- package/src/__tests__/hooks-manager.test.ts +99 -61
- package/src/__tests__/hooks-runner.test.ts +94 -71
- package/src/__tests__/hooks-settings.test.ts +69 -64
- package/src/__tests__/hooks-templates.test.ts +85 -54
- package/src/__tests__/hooks-ts-runner.test.ts +82 -45
- package/src/__tests__/hooks-watch.test.ts +32 -22
- package/src/__tests__/host-file-edit-tool.test.ts +190 -148
- package/src/__tests__/host-file-read-tool.test.ts +86 -63
- package/src/__tests__/host-file-write-tool.test.ts +98 -64
- package/src/__tests__/host-shell-tool.test.ts +342 -233
- package/src/__tests__/inbound-invite-redemption.test.ts +0 -1
- package/src/__tests__/ingress-member-store.test.ts +163 -159
- package/src/__tests__/ingress-reconcile.test.ts +0 -1
- package/src/__tests__/ingress-routes-http.test.ts +441 -356
- package/src/__tests__/ingress-url-consistency.test.ts +125 -64
- package/src/__tests__/integration-status.test.ts +93 -73
- package/src/__tests__/intent-routing.test.ts +148 -118
- package/src/__tests__/invite-redemption-service.test.ts +163 -121
- package/src/__tests__/ipc-blob-store.test.ts +104 -91
- package/src/__tests__/ipc-contract-inventory.test.ts +27 -15
- package/src/__tests__/ipc-contract.test.ts +24 -23
- package/src/__tests__/ipc-protocol.test.ts +52 -46
- package/src/__tests__/ipc-roundtrip.benchmark.test.ts +61 -50
- package/src/__tests__/ipc-snapshot.test.ts +1135 -1056
- package/src/__tests__/ipc-validate.test.ts +240 -179
- package/src/__tests__/key-migration.test.ts +123 -90
- package/src/__tests__/keychain.test.ts +150 -123
- package/src/__tests__/lifecycle-docs-guard.test.ts +65 -64
- package/src/__tests__/llm-usage-store.test.ts +112 -87
- package/src/__tests__/managed-skill-lifecycle.test.ts +147 -108
- package/src/__tests__/managed-store.test.ts +411 -360
- package/src/__tests__/mcp-cli.test.ts +189 -123
- package/src/__tests__/mcp-health-check.test.ts +26 -21
- package/src/__tests__/media-generate-image.test.ts +122 -99
- package/src/__tests__/media-reuse-story.e2e.test.ts +282 -214
- package/src/__tests__/media-visibility-policy.test.ts +86 -38
- package/src/__tests__/memory-context-benchmark.benchmark.test.ts +146 -100
- package/src/__tests__/memory-lifecycle-e2e.test.ts +385 -297
- package/src/__tests__/memory-query-builder.test.ts +32 -33
- package/src/__tests__/memory-recall-quality.test.ts +761 -407
- package/src/__tests__/memory-regressions.experimental.test.ts +443 -380
- package/src/__tests__/memory-regressions.test.ts +3725 -2642
- package/src/__tests__/memory-retrieval-budget.test.ts +7 -8
- package/src/__tests__/memory-retrieval.benchmark.test.ts +144 -109
- package/src/__tests__/memory-upsert-concurrency.test.ts +292 -201
- package/src/__tests__/messaging-send-tool.test.ts +36 -29
- package/src/__tests__/migration-cli-flows.test.ts +69 -53
- package/src/__tests__/migration-ordering.test.ts +103 -86
- package/src/__tests__/mime-builder.test.ts +55 -32
- package/src/__tests__/mock-signup-server.test.ts +384 -246
- package/src/__tests__/model-intents.test.ts +61 -37
- package/src/__tests__/no-direct-anthropic-sdk-imports.test.ts +9 -12
- package/src/__tests__/no-is-trusted-guard.test.ts +24 -21
- package/src/__tests__/non-member-access-request.test.ts +3 -2
- package/src/__tests__/notification-broadcaster.test.ts +99 -81
- package/src/__tests__/notification-decision-fallback.test.ts +223 -178
- package/src/__tests__/notification-decision-strategy.test.ts +375 -337
- package/src/__tests__/notification-deep-link.test.ts +67 -61
- package/src/__tests__/notification-guardian-path.test.ts +248 -206
- package/src/__tests__/notification-routing-intent.test.ts +166 -93
- package/src/__tests__/notification-thread-candidate-validation.test.ts +78 -75
- package/src/__tests__/notification-thread-candidates.test.ts +64 -61
- package/src/__tests__/oauth-callback-registry.test.ts +40 -30
- package/src/__tests__/oauth-connect-handler.test.ts +109 -89
- package/src/__tests__/oauth-scope-policy.test.ts +63 -55
- package/src/__tests__/oauth2-gateway-transport.test.ts +252 -174
- package/src/__tests__/onboarding-starter-tasks.test.ts +93 -89
- package/src/__tests__/onboarding-template-contract.test.ts +93 -94
- package/src/__tests__/openai-provider.test.ts +366 -274
- package/src/__tests__/pairing-concurrent.test.ts +18 -12
- package/src/__tests__/pairing-routes.test.ts +45 -41
- package/src/__tests__/parallel-tool.benchmark.test.ts +108 -58
- package/src/__tests__/parser.test.ts +316 -226
- package/src/__tests__/path-classifier.test.ts +24 -25
- package/src/__tests__/path-policy.test.ts +187 -147
- package/src/__tests__/phone.test.ts +36 -36
- package/src/__tests__/platform-move-helper.test.ts +48 -40
- package/src/__tests__/platform-socket-path.test.ts +23 -24
- package/src/__tests__/platform-workspace-migration.test.ts +464 -414
- package/src/__tests__/platform.test.ts +61 -53
- package/src/__tests__/playbook-execution.test.ts +397 -265
- package/src/__tests__/playbook-tools.test.ts +267 -196
- package/src/__tests__/prebuilt-home-base-seed.test.ts +30 -27
- package/src/__tests__/pricing.test.ts +316 -136
- package/src/__tests__/profile-compiler.test.ts +206 -188
- package/src/__tests__/provider-commit-message-generator.test.ts +114 -106
- package/src/__tests__/provider-error-scenarios.test.ts +212 -158
- package/src/__tests__/provider-fail-open-selection.test.ts +51 -44
- package/src/__tests__/provider-registry-ollama.test.ts +13 -9
- package/src/__tests__/provider-streaming.benchmark.test.ts +232 -183
- package/src/__tests__/proxy-approval-callback.test.ts +180 -119
- package/src/__tests__/public-ingress-urls.test.ts +112 -94
- package/src/__tests__/qdrant-manager.test.ts +147 -98
- package/src/__tests__/ratelimit.test.ts +152 -82
- package/src/__tests__/recording-handler.test.ts +273 -151
- package/src/__tests__/recording-intent-fallback.test.ts +94 -75
- package/src/__tests__/recording-intent-handler.test.ts +0 -1
- package/src/__tests__/recording-intent.test.ts +578 -379
- package/src/__tests__/recording-state-machine.test.ts +530 -316
- package/src/__tests__/recurrence-engine-rruleset.test.ts +150 -92
- package/src/__tests__/recurrence-engine.test.ts +81 -41
- package/src/__tests__/recurrence-types.test.ts +63 -44
- package/src/__tests__/relay-server.test.ts +2131 -1602
- package/src/__tests__/reminder-store.test.ts +158 -80
- package/src/__tests__/reminder.test.ts +113 -109
- package/src/__tests__/remote-skill-policy.test.ts +96 -72
- package/src/__tests__/request-file-tool.test.ts +74 -67
- package/src/__tests__/response-tier.test.ts +131 -74
- package/src/__tests__/runtime-attachment-metadata.test.ts +0 -1
- package/src/__tests__/runtime-events-sse-parity.test.ts +167 -145
- package/src/__tests__/runtime-events-sse.test.ts +0 -1
- package/src/__tests__/sandbox-diagnostics.test.ts +66 -56
- package/src/__tests__/sandbox-host-parity.test.ts +377 -301
- package/src/__tests__/scaffold-managed-skill-tool.test.ts +213 -161
- package/src/__tests__/schedule-store.test.ts +268 -205
- package/src/__tests__/schedule-tools.test.ts +702 -524
- package/src/__tests__/scheduler-recurrence.test.ts +240 -130
- package/src/__tests__/scoped-approval-grants.test.ts +258 -168
- package/src/__tests__/scoped-grant-security-matrix.test.ts +160 -146
- package/src/__tests__/script-proxy-certs.test.ts +38 -35
- package/src/__tests__/script-proxy-connect-tunnel.test.ts +71 -46
- package/src/__tests__/script-proxy-decision-trace.test.ts +161 -84
- package/src/__tests__/script-proxy-http-forwarder.test.ts +146 -129
- package/src/__tests__/script-proxy-injection-runtime.test.ts +139 -113
- package/src/__tests__/script-proxy-mitm-handler.test.ts +226 -142
- package/src/__tests__/script-proxy-policy-runtime.test.ts +126 -86
- package/src/__tests__/script-proxy-policy.test.ts +308 -153
- package/src/__tests__/script-proxy-rewrite-specificity.test.ts +74 -62
- package/src/__tests__/script-proxy-router.test.ts +111 -77
- package/src/__tests__/script-proxy-session-manager.test.ts +156 -113
- package/src/__tests__/script-proxy-session-runtime.test.ts +28 -24
- package/src/__tests__/secret-allowlist.test.ts +105 -90
- package/src/__tests__/secret-ingress-handler.test.ts +41 -30
- package/src/__tests__/secret-onetime-send.test.ts +67 -50
- package/src/__tests__/secret-prompt-log-hygiene.test.ts +35 -31
- package/src/__tests__/secret-response-routing.test.ts +50 -41
- package/src/__tests__/secret-scanner-executor.test.ts +152 -111
- package/src/__tests__/secret-scanner.test.ts +495 -413
- package/src/__tests__/secure-keys.test.ts +132 -121
- package/src/__tests__/send-endpoint-busy.test.ts +0 -1
- package/src/__tests__/send-notification-tool.test.ts +43 -42
- package/src/__tests__/sensitive-output-placeholders.test.ts +72 -64
- package/src/__tests__/sequence-store.test.ts +335 -167
- package/src/__tests__/server-history-render.test.ts +341 -202
- package/src/__tests__/session-abort-tool-results.test.ts +133 -70
- package/src/__tests__/session-confirmation-signals.test.ts +252 -160
- package/src/__tests__/session-conflict-gate.test.ts +775 -585
- package/src/__tests__/session-error.test.ts +222 -191
- package/src/__tests__/session-evictor.test.ts +79 -62
- package/src/__tests__/session-init.benchmark.test.ts +170 -108
- package/src/__tests__/session-load-history-repair.test.ts +273 -139
- package/src/__tests__/session-messaging-secret-redirect.test.ts +130 -90
- package/src/__tests__/session-pre-run-repair.test.ts +106 -59
- package/src/__tests__/session-profile-injection.test.ts +198 -130
- package/src/__tests__/session-provider-retry-repair.test.ts +223 -141
- package/src/__tests__/session-queue.test.ts +624 -321
- package/src/__tests__/session-runtime-assembly.test.ts +425 -329
- package/src/__tests__/session-runtime-workspace.test.ts +69 -61
- package/src/__tests__/session-skill-tools.test.ts +973 -678
- package/src/__tests__/session-slash-known.test.ts +185 -133
- package/src/__tests__/session-slash-queue.test.ts +147 -81
- package/src/__tests__/session-slash-unknown.test.ts +135 -90
- package/src/__tests__/session-surfaces-task-progress.test.ts +122 -87
- package/src/__tests__/session-tool-setup-app-refresh.test.ts +338 -177
- package/src/__tests__/session-tool-setup-memory-scope.test.ts +63 -40
- package/src/__tests__/session-tool-setup-side-effect-flag.test.ts +60 -37
- package/src/__tests__/session-tool-setup-tools-disabled.test.ts +28 -26
- package/src/__tests__/session-undo.test.ts +43 -30
- package/src/__tests__/session-workspace-cache-state.test.ts +108 -67
- package/src/__tests__/session-workspace-injection.test.ts +245 -117
- package/src/__tests__/session-workspace-tool-tracking.test.ts +260 -93
- package/src/__tests__/shared-filesystem-errors.test.ts +47 -47
- package/src/__tests__/shell-credential-ref.test.ts +126 -90
- package/src/__tests__/shell-identity.test.ts +134 -111
- package/src/__tests__/shell-parser-fuzz.test.ts +263 -179
- package/src/__tests__/shell-parser-property.test.ts +435 -288
- package/src/__tests__/shell-tool-proxy-mode.test.ts +142 -70
- package/src/__tests__/size-guard.test.ts +42 -44
- package/src/__tests__/skill-feature-flags-integration.test.ts +79 -52
- package/src/__tests__/skill-feature-flags.test.ts +75 -47
- package/src/__tests__/skill-include-graph.test.ts +143 -148
- package/src/__tests__/skill-load-feature-flag.test.ts +94 -59
- package/src/__tests__/skill-load-tool.test.ts +371 -199
- package/src/__tests__/skill-projection-feature-flag.test.ts +131 -88
- package/src/__tests__/skill-projection.benchmark.test.ts +93 -65
- package/src/__tests__/skill-script-runner-host.test.ts +460 -250
- package/src/__tests__/skill-script-runner-sandbox.test.ts +168 -108
- package/src/__tests__/skill-script-runner.test.ts +115 -74
- package/src/__tests__/skill-tool-factory.test.ts +140 -96
- package/src/__tests__/skill-tool-manifest.test.ts +306 -210
- package/src/__tests__/skill-version-hash.test.ts +70 -56
- package/src/__tests__/skills.test.ts +0 -1
- package/src/__tests__/slack-channel-config.test.ts +127 -84
- package/src/__tests__/slack-skill.test.ts +60 -47
- package/src/__tests__/slash-commands-catalog.test.ts +37 -31
- package/src/__tests__/slash-commands-parser.test.ts +71 -64
- package/src/__tests__/slash-commands-resolver.test.ts +143 -107
- package/src/__tests__/slash-commands-rewrite.test.ts +22 -22
- package/src/__tests__/speaker-identification.test.ts +28 -25
- package/src/__tests__/starter-bundle.test.ts +27 -23
- package/src/__tests__/starter-task-flow.test.ts +67 -52
- package/src/__tests__/subagent-manager-notify.test.ts +154 -108
- package/src/__tests__/subagent-tools.test.ts +311 -270
- package/src/__tests__/subagent-types.test.ts +40 -40
- package/src/__tests__/surface-mutex-cleanup.test.ts +42 -30
- package/src/__tests__/swarm-dag-pathological.test.ts +122 -111
- package/src/__tests__/swarm-orchestrator.test.ts +135 -101
- package/src/__tests__/swarm-plan-validator.test.ts +125 -73
- package/src/__tests__/swarm-recursion.test.ts +58 -46
- package/src/__tests__/swarm-router-planner.test.ts +99 -74
- package/src/__tests__/swarm-session-integration.test.ts +148 -91
- package/src/__tests__/swarm-tool.test.ts +65 -45
- package/src/__tests__/swarm-worker-backend.test.ts +59 -45
- package/src/__tests__/swarm-worker-runner.test.ts +133 -118
- package/src/__tests__/system-prompt.test.ts +290 -256
- package/src/__tests__/task-compiler.test.ts +176 -120
- package/src/__tests__/task-management-tools.test.ts +561 -456
- package/src/__tests__/task-memory-cleanup.test.ts +627 -362
- package/src/__tests__/task-runner.test.ts +117 -94
- package/src/__tests__/task-scheduler.test.ts +113 -84
- package/src/__tests__/task-tools.test.ts +349 -264
- package/src/__tests__/terminal-sandbox.test.ts +138 -108
- package/src/__tests__/terminal-tools.test.ts +350 -305
- package/src/__tests__/thread-seed-composer.test.ts +307 -180
- package/src/__tests__/tool-approval-handler.test.ts +238 -137
- package/src/__tests__/tool-audit-listener.test.ts +69 -69
- package/src/__tests__/tool-domain-event-publisher.test.ts +142 -132
- package/src/__tests__/tool-execution-abort-cleanup.test.ts +153 -146
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +136 -105
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +355 -239
- package/src/__tests__/tool-executor-redaction.test.ts +112 -109
- package/src/__tests__/tool-executor-shell-integration.test.ts +130 -79
- package/src/__tests__/tool-executor.test.ts +1274 -674
- package/src/__tests__/tool-grant-request-escalation.test.ts +401 -283
- package/src/__tests__/tool-metrics-listener.test.ts +97 -85
- package/src/__tests__/tool-notification-listener.test.ts +42 -25
- package/src/__tests__/tool-permission-simulate-handler.test.ts +137 -113
- package/src/__tests__/tool-policy.test.ts +44 -25
- package/src/__tests__/tool-profiling-listener.test.ts +99 -93
- package/src/__tests__/tool-result-truncation.test.ts +5 -4
- package/src/__tests__/tool-trace-listener.test.ts +131 -111
- package/src/__tests__/top-level-renderer.test.ts +62 -58
- package/src/__tests__/top-level-scanner.test.ts +68 -64
- package/src/__tests__/trace-emitter.test.ts +56 -56
- package/src/__tests__/trust-context-guards.test.ts +65 -65
- package/src/__tests__/trust-store.test.ts +1239 -806
- package/src/__tests__/trusted-contact-approval-notifier.test.ts +0 -1
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +0 -1
- package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +3 -2
- package/src/__tests__/trusted-contact-multichannel.test.ts +3 -2
- package/src/__tests__/trusted-contact-verification.test.ts +251 -231
- package/src/__tests__/turn-commit.test.ts +259 -200
- package/src/__tests__/twilio-provider.test.ts +140 -126
- package/src/__tests__/twilio-rest.test.ts +22 -18
- package/src/__tests__/twilio-routes-elevenlabs.test.ts +0 -1
- package/src/__tests__/twilio-routes-twiml.test.ts +55 -55
- package/src/__tests__/twilio-routes.test.ts +0 -1
- package/src/__tests__/twitter-auth-handler.test.ts +184 -139
- package/src/__tests__/twitter-cli-error-shaping.test.ts +88 -73
- package/src/__tests__/twitter-cli-routing.test.ts +146 -99
- package/src/__tests__/twitter-oauth-client.test.ts +82 -65
- package/src/__tests__/update-bulletin-format.test.ts +69 -66
- package/src/__tests__/update-bulletin-state.test.ts +66 -60
- package/src/__tests__/update-bulletin.test.ts +150 -114
- package/src/__tests__/update-template-contract.test.ts +15 -10
- package/src/__tests__/url-safety.test.ts +288 -265
- package/src/__tests__/user-reference.test.ts +32 -32
- package/src/__tests__/view-image-tool.test.ts +118 -96
- package/src/__tests__/voice-invite-redemption.test.ts +111 -106
- package/src/__tests__/voice-quality.test.ts +117 -102
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +204 -146
- package/src/__tests__/voice-session-bridge.test.ts +351 -216
- package/src/__tests__/weather-skill-regression.test.ts +170 -120
- package/src/__tests__/web-fetch.test.ts +664 -526
- package/src/__tests__/web-search.test.ts +379 -213
- package/src/__tests__/work-item-output.test.ts +90 -53
- package/src/__tests__/workspace-git-service.test.ts +437 -356
- package/src/__tests__/workspace-heartbeat-service.test.ts +125 -91
- package/src/__tests__/workspace-lifecycle.test.ts +98 -64
- package/src/__tests__/workspace-policy.test.ts +139 -71
- package/src/commands/__tests__/cc-command-registry.test.ts +142 -134
- package/src/config/__tests__/feature-flag-registry-guard.test.ts +48 -39
- package/src/config/bundled-skills/chatgpt-import/tools/chatgpt-import.ts +25 -10
- package/src/config/bundled-skills/doordash/__tests__/doordash-session.test.ts +0 -1
- package/src/config/bundled-skills/messaging/SKILL.md +4 -3
- package/src/config/bundled-skills/messaging/tools/gmail-outreach-scan.ts +15 -5
- package/src/config/bundled-skills/messaging/tools/gmail-sender-digest.ts +16 -5
- package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +34 -32
- package/src/config/bundled-tool-registry.ts +2 -0
- package/src/config/env.ts +3 -4
- package/src/memory/db-connection.ts +16 -10
- package/src/messaging/providers/gmail/adapter.ts +10 -3
- package/src/messaging/providers/gmail/client.ts +280 -72
- package/src/runtime/auth/__tests__/context.test.ts +75 -65
- package/src/runtime/auth/__tests__/credential-service.test.ts +137 -114
- package/src/runtime/auth/__tests__/guard-tests.test.ts +84 -90
- package/src/runtime/auth/__tests__/ipc-auth-context.test.ts +40 -40
- package/src/runtime/auth/__tests__/middleware.test.ts +80 -74
- package/src/runtime/auth/__tests__/policy.test.ts +9 -9
- package/src/runtime/auth/__tests__/route-policy.test.ts +76 -65
- package/src/runtime/auth/__tests__/scopes.test.ts +68 -60
- package/src/runtime/auth/__tests__/subject.test.ts +54 -54
- package/src/runtime/auth/__tests__/token-service.test.ts +115 -108
- package/src/runtime/auth/scopes.ts +3 -0
- package/src/runtime/auth/token-service.ts +4 -1
- package/src/runtime/auth/types.ts +2 -1
- package/src/runtime/http-server.ts +2 -1
- package/src/security/secure-keys.ts +103 -53
- package/src/tools/browser/__tests__/auth-cache.test.ts +69 -63
- package/src/tools/browser/__tests__/auth-detector.test.ts +218 -157
- 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
|
|
11
|
-
import { tmpdir } from
|
|
12
|
-
import { join } from
|
|
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 {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
import {
|
|
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(),
|
|
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(
|
|
47
|
-
test(
|
|
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,
|
|
51
|
-
writeFileSync(outsideFile,
|
|
55
|
+
const outsideFile = join(outside, "secret.txt");
|
|
56
|
+
writeFileSync(outsideFile, "secret data");
|
|
52
57
|
|
|
53
|
-
symlinkSync(outsideFile, join(boundary,
|
|
58
|
+
symlinkSync(outsideFile, join(boundary, "link.txt"));
|
|
54
59
|
const ops = new FileSystemOps(sandboxPolicyFor(boundary));
|
|
55
60
|
|
|
56
|
-
const result = ops.readFileSafe({ path:
|
|
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(
|
|
64
|
+
expect(result.error.code).toBe("PATH_OUT_OF_BOUNDS");
|
|
60
65
|
});
|
|
61
66
|
|
|
62
|
-
test(
|
|
67
|
+
test("read allows symlink within boundary", () => {
|
|
63
68
|
const boundary = makeTempDir();
|
|
64
|
-
const realFile = join(boundary,
|
|
65
|
-
writeFileSync(realFile,
|
|
66
|
-
symlinkSync(realFile, join(boundary,
|
|
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:
|
|
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(
|
|
77
|
+
expect(result.value.content).toContain("hello");
|
|
73
78
|
});
|
|
74
79
|
|
|
75
|
-
test(
|
|
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,
|
|
83
|
+
symlinkSync(outside, join(boundary, "link-dir"));
|
|
79
84
|
|
|
80
85
|
const ops = new FileSystemOps(sandboxPolicyFor(boundary));
|
|
81
|
-
const result = ops.writeFileSafe({
|
|
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(
|
|
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,
|
|
94
|
+
expect(existsSync(join(outside, "evil.txt"))).toBe(false);
|
|
87
95
|
});
|
|
88
96
|
|
|
89
|
-
test(
|
|
97
|
+
test("edit blocks symlink pointing outside boundary", () => {
|
|
90
98
|
const boundary = makeTempDir();
|
|
91
99
|
const outside = makeTempDir();
|
|
92
|
-
const outsideFile = join(outside,
|
|
93
|
-
writeFileSync(outsideFile,
|
|
94
|
-
symlinkSync(outsideFile, join(boundary,
|
|
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:
|
|
99
|
-
oldString:
|
|
100
|
-
newString:
|
|
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(
|
|
113
|
+
expect(result.error.code).toBe("PATH_OUT_OF_BOUNDS");
|
|
106
114
|
// The outside file must NOT have been modified
|
|
107
|
-
expect(readFileSync(outsideFile,
|
|
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(
|
|
116
|
-
test(
|
|
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,
|
|
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:
|
|
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(
|
|
135
|
+
test("limit of zero returns empty content", () => {
|
|
128
136
|
const dir = makeTempDir();
|
|
129
|
-
writeFileSync(join(dir,
|
|
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:
|
|
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(
|
|
146
|
+
test("offset=1 reads from first line (1-indexed)", () => {
|
|
139
147
|
const dir = makeTempDir();
|
|
140
|
-
writeFileSync(join(dir,
|
|
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:
|
|
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(
|
|
147
|
-
expect(result.value.content).not.toContain(
|
|
154
|
+
expect(result.value.content).toContain("first");
|
|
155
|
+
expect(result.value.content).not.toContain("second");
|
|
148
156
|
});
|
|
149
157
|
|
|
150
|
-
test(
|
|
158
|
+
test("limit exceeding file length returns all remaining lines", () => {
|
|
151
159
|
const dir = makeTempDir();
|
|
152
|
-
writeFileSync(join(dir,
|
|
160
|
+
writeFileSync(join(dir, "file.txt"), "a\nb");
|
|
153
161
|
const ops = new FileSystemOps(sandboxPolicyFor(dir));
|
|
154
162
|
|
|
155
|
-
const result = ops.readFileSafe({
|
|
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(
|
|
159
|
-
expect(result.value.content).toContain(
|
|
170
|
+
expect(result.value.content).toContain("a");
|
|
171
|
+
expect(result.value.content).toContain("b");
|
|
160
172
|
});
|
|
161
173
|
|
|
162
|
-
test(
|
|
174
|
+
test("read adds line numbers starting from offset", () => {
|
|
163
175
|
const dir = makeTempDir();
|
|
164
|
-
writeFileSync(join(dir,
|
|
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:
|
|
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(
|
|
172
|
-
expect(result.value.content).toContain(
|
|
173
|
-
expect(result.value.content).toContain(
|
|
174
|
-
expect(result.value.content).toContain(
|
|
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(
|
|
183
|
-
test(
|
|
194
|
+
describe("FileSystemOps edit match methods", () => {
|
|
195
|
+
test("whitespace-normalized match succeeds", () => {
|
|
184
196
|
const dir = makeTempDir();
|
|
185
|
-
writeFileSync(
|
|
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:
|
|
190
|
-
oldString:
|
|
191
|
-
newString:
|
|
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(
|
|
211
|
+
expect(result.value.matchMethod).toBe("whitespace");
|
|
197
212
|
expect(result.value.similarity).toBe(1);
|
|
198
|
-
expect(result.value.newContent).toContain(
|
|
213
|
+
expect(result.value.newContent).toContain("bar");
|
|
199
214
|
});
|
|
200
215
|
|
|
201
|
-
test(
|
|
216
|
+
test("fuzzy match succeeds with near-match", () => {
|
|
202
217
|
const dir = makeTempDir();
|
|
203
|
-
writeFileSync(join(dir,
|
|
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:
|
|
208
|
-
oldString:
|
|
209
|
-
newString:
|
|
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(
|
|
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(
|
|
234
|
+
test("edit returns actualOld and actualNew for fuzzy match", () => {
|
|
220
235
|
const dir = makeTempDir();
|
|
221
|
-
writeFileSync(join(dir,
|
|
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:
|
|
226
|
-
oldString:
|
|
227
|
-
newString:
|
|
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(
|
|
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(
|
|
242
|
-
test(
|
|
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:
|
|
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(
|
|
268
|
+
test("overwrite tracks oldContent and newContent", () => {
|
|
254
269
|
const dir = makeTempDir();
|
|
255
|
-
writeFileSync(join(dir,
|
|
270
|
+
writeFileSync(join(dir, "existing.txt"), "version 1");
|
|
256
271
|
const ops = new FileSystemOps(sandboxPolicyFor(dir));
|
|
257
272
|
|
|
258
|
-
const result = ops.writeFileSafe({
|
|
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(
|
|
262
|
-
expect(result.value.newContent).toBe(
|
|
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(
|
|
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:
|
|
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,
|
|
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(
|
|
282
|
-
test(
|
|
283
|
-
const result = formatEditDiff(
|
|
284
|
-
expect(result).toContain(
|
|
285
|
-
expect(result).toContain(
|
|
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(
|
|
289
|
-
const result = formatEditDiff(
|
|
290
|
-
expect(result).toContain(
|
|
291
|
-
expect(result).toContain(
|
|
292
|
-
expect(result).toContain(
|
|
293
|
-
expect(result).toContain(
|
|
294
|
-
expect(result).toContain(
|
|
295
|
-
expect(result).toContain(
|
|
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(
|
|
299
|
-
const result = formatEditDiff(
|
|
300
|
-
expect(result).not.toContain(
|
|
301
|
-
expect(result).toContain(
|
|
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(
|
|
305
|
-
const result = formatEditDiff(
|
|
306
|
-
expect(result).toContain(
|
|
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(
|
|
311
|
-
const longOld = Array.from({ length: 12 }, (_, i) => `old-line-${i}`).join(
|
|
312
|
-
|
|
313
|
-
|
|
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(
|
|
322
|
-
test(
|
|
323
|
-
const result = formatWriteSummary(
|
|
324
|
-
expect(result).toContain(
|
|
325
|
-
expect(result).toContain(
|
|
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(
|
|
329
|
-
const result = formatWriteSummary(
|
|
330
|
-
expect(result).toContain(
|
|
331
|
-
expect(result).not.toContain(
|
|
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(
|
|
335
|
-
const result = formatWriteSummary(
|
|
336
|
-
expect(result).toContain(
|
|
337
|
-
expect(result).toContain(
|
|
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(
|
|
346
|
-
test(
|
|
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:
|
|
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(
|
|
373
|
+
expect(result.error.code).toBe("PATH_OUT_OF_BOUNDS");
|
|
354
374
|
});
|
|
355
375
|
|
|
356
|
-
test(
|
|
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({
|
|
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(
|
|
386
|
+
expect(result.error.code).toBe("PATH_OUT_OF_BOUNDS");
|
|
364
387
|
});
|
|
365
388
|
|
|
366
|
-
test(
|
|
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:
|
|
372
|
-
oldString:
|
|
373
|
-
newString:
|
|
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(
|
|
401
|
+
expect(result.error.code).toBe("PATH_OUT_OF_BOUNDS");
|
|
379
402
|
});
|
|
380
403
|
|
|
381
|
-
test(
|
|
404
|
+
test("rejects dot-dot traversal embedded in path on read", () => {
|
|
382
405
|
const dir = makeTempDir();
|
|
383
|
-
mkdirSync(join(dir,
|
|
406
|
+
mkdirSync(join(dir, "sub"));
|
|
384
407
|
const ops = new FileSystemOps(sandboxPolicyFor(dir));
|
|
385
408
|
|
|
386
|
-
const result = ops.readFileSafe({ path:
|
|
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(
|
|
412
|
+
expect(result.error.code).toBe("PATH_OUT_OF_BOUNDS");
|
|
390
413
|
});
|
|
391
414
|
|
|
392
|
-
test(
|
|
415
|
+
test("accepts absolute path inside boundary", () => {
|
|
393
416
|
const dir = makeTempDir();
|
|
394
|
-
writeFileSync(join(dir,
|
|
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,
|
|
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(
|
|
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(
|
|
409
|
-
test(
|
|
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,
|
|
412
|
-
writeFileSync(join(dir,
|
|
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:
|
|
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(
|
|
426
|
-
test(
|
|
448
|
+
describe("FileSystemOps empty file operations", () => {
|
|
449
|
+
test("reads empty file successfully", () => {
|
|
427
450
|
const dir = makeTempDir();
|
|
428
|
-
writeFileSync(join(dir,
|
|
451
|
+
writeFileSync(join(dir, "empty.txt"), "");
|
|
429
452
|
const ops = new FileSystemOps(sandboxPolicyFor(dir));
|
|
430
453
|
|
|
431
|
-
const result = ops.readFileSafe({ path:
|
|
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(
|
|
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:
|
|
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,
|
|
469
|
+
expect(readFileSync(join(dir, "empty.txt"), "utf-8")).toBe("");
|
|
447
470
|
});
|
|
448
471
|
|
|
449
|
-
test(
|
|
472
|
+
test("edit on empty file returns MATCH_NOT_FOUND", () => {
|
|
450
473
|
const dir = makeTempDir();
|
|
451
|
-
writeFileSync(join(dir,
|
|
474
|
+
writeFileSync(join(dir, "empty.txt"), "");
|
|
452
475
|
const ops = new FileSystemOps(sandboxPolicyFor(dir));
|
|
453
476
|
|
|
454
477
|
const result = ops.editFileSafe({
|
|
455
|
-
path:
|
|
456
|
-
oldString:
|
|
457
|
-
newString:
|
|
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(
|
|
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(
|
|
471
|
-
test(
|
|
493
|
+
describe("FileSystemOps /workspace path remapping", () => {
|
|
494
|
+
test("read remaps /workspace/ path to boundary", () => {
|
|
472
495
|
const dir = makeTempDir();
|
|
473
|
-
writeFileSync(join(dir,
|
|
496
|
+
writeFileSync(join(dir, "file.txt"), "workspace content");
|
|
474
497
|
const ops = new FileSystemOps(sandboxPolicyFor(dir));
|
|
475
498
|
|
|
476
|
-
const result = ops.readFileSafe({ path:
|
|
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(
|
|
502
|
+
expect(result.value.content).toContain("workspace content");
|
|
480
503
|
});
|
|
481
504
|
|
|
482
|
-
test(
|
|
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({
|
|
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,
|
|
490
|
-
expect(readFileSync(join(dir,
|
|
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(
|
|
519
|
+
test("edit remaps /workspace/ path to boundary", () => {
|
|
494
520
|
const dir = makeTempDir();
|
|
495
|
-
writeFileSync(join(dir,
|
|
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:
|
|
500
|
-
oldString:
|
|
501
|
-
newString:
|
|
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(
|
|
507
|
-
expect(readFileSync(join(dir,
|
|
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(
|
|
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:
|
|
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(
|
|
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(
|
|
526
|
-
test(
|
|
551
|
+
describe("FileSystemOps custom size limit", () => {
|
|
552
|
+
test("read rejects file exceeding custom limit", () => {
|
|
527
553
|
const dir = makeTempDir();
|
|
528
|
-
writeFileSync(join(dir,
|
|
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:
|
|
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(
|
|
560
|
+
expect(result.error.code).toBe("SIZE_LIMIT_EXCEEDED");
|
|
535
561
|
});
|
|
536
562
|
|
|
537
|
-
test(
|
|
563
|
+
test("read accepts file within custom limit", () => {
|
|
538
564
|
const dir = makeTempDir();
|
|
539
|
-
writeFileSync(join(dir,
|
|
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:
|
|
568
|
+
const result = ops.readFileSafe({ path: "small.txt" });
|
|
543
569
|
expect(result.ok).toBe(true);
|
|
544
570
|
});
|
|
545
571
|
|
|
546
|
-
test(
|
|
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({
|
|
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(
|
|
582
|
+
expect(result.error.code).toBe("SIZE_LIMIT_EXCEEDED");
|
|
554
583
|
});
|
|
555
584
|
|
|
556
|
-
test(
|
|
585
|
+
test("edit rejects file exceeding custom limit", () => {
|
|
557
586
|
const dir = makeTempDir();
|
|
558
|
-
writeFileSync(join(dir,
|
|
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:
|
|
563
|
-
oldString:
|
|
564
|
-
newString:
|
|
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(
|
|
598
|
+
expect(result.error.code).toBe("SIZE_LIMIT_EXCEEDED");
|
|
570
599
|
});
|
|
571
600
|
|
|
572
|
-
test(
|
|
601
|
+
test("no size limit when not specified (defaults to 100MB)", () => {
|
|
573
602
|
const dir = makeTempDir();
|
|
574
|
-
writeFileSync(join(dir,
|
|
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:
|
|
606
|
+
const result = ops.readFileSafe({ path: "file.txt" });
|
|
578
607
|
expect(result.ok).toBe(true);
|
|
579
608
|
});
|
|
580
609
|
});
|