@vellumai/assistant 0.5.1 → 0.5.3
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/ARCHITECTURE.md +163 -54
- package/docs/architecture/integrations.md +62 -67
- package/docs/credential-execution-service.md +3 -3
- package/docs/skills.md +100 -0
- package/package.json +1 -1
- package/src/__tests__/agent-loop.test.ts +111 -0
- package/src/__tests__/always-loaded-tools-guard.test.ts +3 -4
- package/src/__tests__/app-builder-tool-scripts.test.ts +13 -151
- package/src/__tests__/app-dir-path-guard.test.ts +78 -0
- package/src/__tests__/app-executors.test.ts +1 -291
- package/src/__tests__/app-git-history.test.ts +4 -4
- package/src/__tests__/app-routes-csp.test.ts +1 -0
- package/src/__tests__/app-store-dir-names.test.ts +426 -0
- package/src/__tests__/attachments-store.test.ts +169 -21
- package/src/__tests__/attachments.test.ts +115 -1
- package/src/__tests__/btw-routes.test.ts +1 -0
- package/src/__tests__/canonical-guardian-store.test.ts +38 -0
- package/src/__tests__/channel-reply-delivery.test.ts +55 -0
- package/src/__tests__/checker.test.ts +54 -0
- package/src/__tests__/claude-code-skill-regression.test.ts +2 -0
- package/src/__tests__/claude-code-tool-profiles.test.ts +2 -0
- package/src/__tests__/compaction.benchmark.test.ts +2 -1
- package/src/__tests__/config-schema-cmd.test.ts +68 -21
- package/src/__tests__/config-schema.test.ts +1 -1
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +156 -5
- package/src/__tests__/conversation-agent-loop.test.ts +297 -2
- package/src/__tests__/conversation-attachments.test.ts +17 -19
- package/src/__tests__/conversation-disk-view-integration.test.ts +277 -0
- package/src/__tests__/conversation-disk-view.test.ts +810 -0
- package/src/__tests__/conversation-error.test.ts +1 -1
- package/src/__tests__/conversation-fork-crud.test.ts +551 -0
- package/src/__tests__/conversation-fork-route.test.ts +386 -0
- package/src/__tests__/conversation-history-web-search.test.ts +1 -1
- package/src/__tests__/conversation-key-store-disk-view.test.ts +130 -0
- package/src/__tests__/conversation-media-retry.test.ts +8 -2
- package/src/__tests__/conversation-memory-dirty-tail.test.ts +150 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +7 -0
- package/src/__tests__/conversation-queue.test.ts +36 -1
- package/src/__tests__/conversation-routes-disk-view.test.ts +439 -0
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +2 -2
- package/src/__tests__/conversation-routes-slash-commands.test.ts +2 -7
- package/src/__tests__/conversation-runtime-assembly.test.ts +17 -2
- package/src/__tests__/conversation-skill-tools.test.ts +4 -9
- package/src/__tests__/conversation-slash-commands.test.ts +149 -0
- package/src/__tests__/conversation-store.test.ts +24 -21
- package/src/__tests__/conversation-surfaces-state-update.test.ts +246 -0
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +1 -0
- package/src/__tests__/conversation-title-service.test.ts +137 -0
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +25 -315
- package/src/__tests__/conversation-tool-setup-memory-scope.test.ts +1 -0
- package/src/__tests__/conversation-tool-setup-side-effect-flag.test.ts +1 -0
- package/src/__tests__/conversation-wipe.test.ts +226 -0
- package/src/__tests__/conversation-workspace-cache-state.test.ts +44 -2
- package/src/__tests__/conversation-workspace-injection.test.ts +11 -0
- package/src/__tests__/credential-security-invariants.test.ts +3 -0
- package/src/__tests__/credential-vault-unit.test.ts +5 -10
- package/src/__tests__/cu-unified-flow.test.ts +1 -0
- package/src/__tests__/db-conversation-fork-lineage-migration.test.ts +241 -0
- package/src/__tests__/db-llm-request-log-provider-migration.test.ts +214 -0
- package/src/__tests__/db-memory-archive-migration.test.ts +372 -0
- package/src/__tests__/db-memory-brief-state-migration.test.ts +213 -0
- package/src/__tests__/db-memory-reducer-checkpoints.test.ts +273 -0
- package/src/__tests__/diagnostics-export.test.ts +70 -1
- package/src/__tests__/first-greeting.test.ts +80 -0
- package/src/__tests__/gateway-only-guard.test.ts +1 -0
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +3 -7
- package/src/__tests__/history-repair.test.ts +32 -10
- package/src/__tests__/http-conversation-lineage.test.ts +251 -0
- package/src/__tests__/image-source-path-reinject.test.ts +136 -0
- package/src/__tests__/inline-command-runner.test.ts +311 -0
- package/src/__tests__/inline-skill-authoring-guard.test.ts +220 -0
- package/src/__tests__/inline-skill-load-permissions.test.ts +435 -0
- package/src/__tests__/list-messages-attachments.test.ts +96 -0
- package/src/__tests__/llm-context-normalization.test.ts +1116 -0
- package/src/__tests__/llm-context-route-provider.test.ts +217 -0
- package/src/__tests__/llm-request-log-turn-query.test.ts +270 -0
- package/src/__tests__/media-generate-image.test.ts +47 -94
- package/src/__tests__/memory-brief-open-loops.test.ts +530 -0
- package/src/__tests__/memory-brief-time.test.ts +285 -0
- package/src/__tests__/memory-brief-wrapper.test.ts +311 -0
- package/src/__tests__/memory-chunk-archive.test.ts +400 -0
- package/src/__tests__/memory-chunk-dual-write.test.ts +453 -0
- package/src/__tests__/memory-episode-archive.test.ts +370 -0
- package/src/__tests__/memory-episode-dual-write.test.ts +626 -0
- package/src/__tests__/memory-lifecycle-e2e.test.ts +3 -1
- package/src/__tests__/memory-observation-archive.test.ts +375 -0
- package/src/__tests__/memory-observation-dual-write.test.ts +318 -0
- package/src/__tests__/memory-recall-quality.test.ts +7 -7
- package/src/__tests__/memory-reducer-store.test.ts +728 -0
- package/src/__tests__/memory-reducer-types.test.ts +699 -0
- package/src/__tests__/memory-reducer.test.ts +698 -0
- package/src/__tests__/memory-regressions.test.ts +6 -4
- package/src/__tests__/memory-simplified-config.test.ts +281 -0
- package/src/__tests__/migration-cross-version-compatibility.test.ts +4 -1
- package/src/__tests__/migration-export-http.test.ts +3 -1
- package/src/__tests__/migration-import-commit-http.test.ts +18 -4
- package/src/__tests__/migration-import-preflight-http.test.ts +1 -3
- package/src/__tests__/mime-builder.test.ts +3 -2
- package/src/__tests__/non-member-access-request.test.ts +12 -1
- package/src/__tests__/notification-decision-identity.test.ts +52 -0
- package/src/__tests__/oauth-apps-routes.test.ts +103 -0
- package/src/__tests__/oauth-store.test.ts +115 -0
- package/src/__tests__/parse-identity-fields.test.ts +129 -0
- package/src/__tests__/provider-error-scenarios.test.ts +1 -3
- package/src/__tests__/provider-failover-actual-provider.test.ts +66 -0
- package/src/__tests__/recording-handler.test.ts +17 -0
- package/src/__tests__/registry.test.ts +3 -8
- package/src/__tests__/relay-server.test.ts +1 -1
- package/src/__tests__/runtime-attachment-metadata.test.ts +7 -3
- package/src/__tests__/schema-transforms.test.ts +165 -5
- package/src/__tests__/server-history-render.test.ts +2 -2
- package/src/__tests__/skill-load-inline-command.test.ts +598 -0
- package/src/__tests__/skill-load-inline-includes.test.ts +644 -0
- package/src/__tests__/skills-inline-command-expansions.test.ts +301 -0
- package/src/__tests__/skills-transitive-hash.test.ts +333 -0
- package/src/__tests__/slack-app-setup-skill-regression.test.ts +3 -1
- package/src/__tests__/slack-inbound-verification.test.ts +2 -2
- package/src/__tests__/starter-task-flow.test.ts +1 -0
- package/src/__tests__/suggestion-routes.test.ts +443 -0
- package/src/__tests__/swarm-conversation-integration.test.ts +1 -0
- package/src/__tests__/swarm-recursion.test.ts +1 -0
- package/src/__tests__/swarm-tool.test.ts +1 -0
- package/src/__tests__/tool-execution-abort-cleanup.test.ts +1 -0
- package/src/__tests__/tool-preview-lifecycle.test.ts +32 -5
- package/src/__tests__/top-level-renderer.test.ts +22 -0
- package/src/__tests__/turn-boundary-resolution.test.ts +243 -0
- package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +320 -0
- package/src/__tests__/web-fetch.test.ts +6 -2
- package/src/__tests__/workspace-migration-006-services-config.test.ts +335 -0
- package/src/__tests__/workspace-migration-007-web-search-provider-rename.test.ts +312 -0
- package/src/__tests__/workspace-migration-009-backfill-conversation-disk-view.test.ts +278 -0
- package/src/__tests__/workspace-migration-010-app-dir-rename.test.ts +275 -0
- package/src/__tests__/workspace-migration-012-rename-conversation-disk-view-dirs.test.ts +77 -0
- package/src/__tests__/workspace-migration-013-repair-conversation-disk-view.test.ts +401 -0
- package/src/__tests__/workspace-migration-backfill-installation-id.test.ts +328 -0
- package/src/__tests__/workspace-migration-seed-device-id.test.ts +6 -10
- package/src/agent/attachments.ts +27 -1
- package/src/agent/loop.ts +29 -1
- package/src/avatar/traits-png-sync.ts +80 -25
- package/src/bundler/app-bundler.ts +4 -4
- package/src/calls/call-domain.ts +1 -0
- package/src/calls/voice-session-bridge.ts +1 -0
- package/src/cli/commands/auth.ts +92 -0
- package/src/cli/commands/avatar.ts +7 -6
- package/src/cli/commands/config.ts +2 -0
- package/src/cli/commands/oauth/providers.ts +29 -0
- package/src/cli/program.ts +12 -0
- package/src/cli.ts +15 -48
- package/src/config/bundled-skills/app-builder/SKILL.md +103 -28
- package/src/config/bundled-skills/app-builder/TOOLS.json +5 -199
- package/src/config/bundled-skills/app-builder/tools/{app-query.ts → app-refresh.ts} +2 -2
- package/src/config/bundled-skills/contacts/tools/google-contacts.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +6 -9
- package/src/config/bundled-skills/gmail/tools/gmail-attachments.ts +4 -6
- package/src/config/bundled-skills/gmail/tools/gmail-draft.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-filters.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-follow-up.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-forward.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-label.ts +4 -6
- package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-send-draft.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-trash.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-vacation.ts +2 -3
- package/src/config/bundled-skills/google-calendar/tools/shared.ts +1 -1
- package/src/config/bundled-skills/image-studio/SKILL.md +2 -2
- package/src/config/bundled-skills/image-studio/TOOLS.json +2 -2
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +45 -72
- package/src/config/bundled-skills/media-processing/tools/extract-keyframes.ts +2 -2
- package/src/config/bundled-skills/messaging/tools/shared.ts +1 -1
- package/src/config/bundled-skills/settings/tools/voice-config-update.ts +19 -3
- package/src/config/bundled-skills/skill-management/SKILL.md +1 -1
- package/src/config/bundled-skills/skill-management/TOOLS.json +2 -2
- package/src/config/bundled-skills/slack/tools/shared.ts +19 -4
- package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +2 -3
- package/src/config/bundled-skills/transcribe/SKILL.md +1 -1
- package/src/config/bundled-skills/transcribe/TOOLS.json +2 -6
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +19 -83
- package/src/config/bundled-tool-registry.ts +2 -14
- package/src/config/feature-flag-registry.json +24 -0
- package/src/config/loader.ts +65 -0
- package/src/config/raw-config-utils.ts +58 -0
- package/src/config/schema-utils.ts +28 -7
- package/src/config/schema.ts +20 -0
- package/src/config/schemas/elevenlabs.ts +18 -0
- package/src/config/schemas/memory-lifecycle.ts +4 -2
- package/src/config/schemas/memory-simplified.ts +101 -0
- package/src/config/schemas/memory-storage.ts +1 -1
- package/src/config/schemas/memory.ts +4 -0
- package/src/config/schemas/services.ts +8 -6
- package/src/config/skills.ts +50 -4
- package/src/contacts/contact-store.ts +13 -6
- package/src/contacts/contacts-write.ts +0 -1
- package/src/context/window-manager.ts +13 -2
- package/src/daemon/conversation-agent-loop-handlers.ts +54 -8
- package/src/daemon/conversation-agent-loop.ts +127 -20
- package/src/daemon/conversation-attachments.ts +18 -36
- package/src/daemon/conversation-error.ts +2 -1
- package/src/daemon/conversation-history.ts +18 -4
- package/src/daemon/conversation-lifecycle.ts +50 -16
- package/src/daemon/conversation-messaging.ts +70 -26
- package/src/daemon/conversation-process.ts +58 -34
- package/src/daemon/conversation-runtime-assembly.ts +22 -38
- package/src/daemon/conversation-slash.ts +121 -256
- package/src/daemon/conversation-surfaces.ts +170 -24
- package/src/daemon/conversation-tool-setup.ts +0 -6
- package/src/daemon/conversation-workspace.ts +21 -1
- package/src/daemon/conversation.ts +69 -30
- package/src/daemon/first-greeting.ts +35 -0
- package/src/daemon/handlers/config-embeddings.ts +156 -0
- package/src/daemon/handlers/config-model.ts +62 -26
- package/src/daemon/handlers/conversations.ts +0 -23
- package/src/daemon/handlers/identity.ts +12 -1
- package/src/daemon/handlers/recording.ts +26 -21
- package/src/daemon/host-cu-proxy.ts +2 -2
- package/src/daemon/lifecycle.ts +115 -65
- package/src/daemon/message-protocol.ts +3 -0
- package/src/daemon/message-types/conversations.ts +18 -0
- package/src/daemon/message-types/messages.ts +1 -0
- package/src/daemon/message-types/shared.ts +2 -0
- package/src/daemon/message-types/surfaces.ts +2 -0
- package/src/daemon/message-types/upgrades.ts +23 -0
- package/src/daemon/server.ts +83 -12
- package/src/daemon/shutdown-handlers.ts +8 -5
- package/src/daemon/startup-error.ts +9 -0
- package/src/daemon/tool-side-effects.ts +11 -28
- package/src/events/tool-permission-telemetry-listener.ts +1 -3
- package/src/followups/followup-store.ts +47 -1
- package/src/instrument.ts +0 -4
- package/src/media/app-icon-generator.ts +2 -2
- package/src/memory/app-git-service.ts +28 -16
- package/src/memory/app-store.ts +230 -41
- package/src/memory/archive-store.ts +400 -0
- package/src/memory/attachments-store.ts +558 -130
- package/src/memory/brief-formatting.ts +33 -0
- package/src/memory/brief-open-loops.ts +266 -0
- package/src/memory/brief-time.ts +161 -0
- package/src/memory/brief.ts +75 -0
- package/src/memory/conversation-attention-store.ts +70 -0
- package/src/memory/conversation-crud.ts +591 -8
- package/src/memory/conversation-directories.ts +125 -0
- package/src/memory/conversation-disk-view.ts +390 -0
- package/src/memory/conversation-key-store.ts +17 -5
- package/src/memory/conversation-queries.ts +5 -1
- package/src/memory/conversation-title-service.ts +21 -49
- package/src/memory/db-init.ts +40 -0
- package/src/memory/embedding-backend.ts +42 -53
- package/src/memory/embedding-gemini.test.ts +4 -4
- package/src/memory/embedding-local.ts +1 -3
- package/src/memory/embedding-ollama.ts +1 -3
- package/src/memory/embedding-openai.ts +1 -3
- package/src/memory/indexer.ts +114 -21
- package/src/memory/items-extractor.ts +42 -13
- package/src/memory/job-handlers/conversation-starters.ts +6 -1
- package/src/memory/job-handlers/embedding.test.ts +2 -4
- package/src/memory/job-handlers/embedding.ts +83 -0
- package/src/memory/job-utils.ts +1 -1
- package/src/memory/jobs-store.ts +6 -0
- package/src/memory/jobs-worker.ts +12 -0
- package/src/memory/llm-request-log-store.ts +100 -1
- package/src/memory/migrations/102-alter-table-columns.ts +5 -0
- package/src/memory/migrations/146-schedule-oneshot-routing.ts +3 -3
- package/src/memory/migrations/147-migrate-reminders-to-schedules.ts +66 -70
- package/src/memory/migrations/148-drop-reminders-table.ts +5 -9
- package/src/memory/migrations/160-drop-loopback-port-column.ts +1 -3
- package/src/memory/migrations/174-rename-thread-starters-table.ts +0 -7
- package/src/memory/migrations/178-oauth-providers-managed-service-config-key.ts +15 -0
- package/src/memory/migrations/179-llm-request-log-message-id.ts +16 -0
- package/src/memory/migrations/180-backfill-inline-attachments-to-disk.ts +66 -0
- package/src/memory/migrations/181-rename-thread-starters-checkpoints.ts +46 -0
- package/src/memory/migrations/182-oauth-providers-display-metadata.ts +20 -0
- package/src/memory/migrations/183-add-conversation-fork-lineage.ts +22 -0
- package/src/memory/migrations/184-llm-request-log-provider.ts +12 -0
- package/src/memory/migrations/185-memory-brief-state.ts +52 -0
- package/src/memory/migrations/186-memory-archive.ts +109 -0
- package/src/memory/migrations/187-memory-reducer-checkpoints.ts +19 -0
- package/src/memory/migrations/index.ts +10 -0
- package/src/memory/migrations/registry.ts +13 -0
- package/src/memory/qdrant-client.ts +23 -4
- package/src/memory/reducer-store.ts +271 -0
- package/src/memory/reducer-types.ts +99 -0
- package/src/memory/reducer.ts +453 -0
- package/src/memory/retriever.test.ts +601 -2
- package/src/memory/retriever.ts +85 -9
- package/src/memory/schema/conversations.ts +9 -0
- package/src/memory/schema/index.ts +2 -0
- package/src/memory/schema/infrastructure.ts +13 -7
- package/src/memory/schema/memory-archive.ts +121 -0
- package/src/memory/schema/memory-brief.ts +55 -0
- package/src/memory/schema/oauth.ts +6 -0
- package/src/memory/search/semantic.ts +17 -4
- package/src/messaging/providers/gmail/mime-builder.ts +3 -1
- package/src/notifications/copy-composer.ts +26 -0
- package/src/notifications/decision-engine.ts +14 -1
- package/src/notifications/emit-signal.ts +1 -1
- package/src/notifications/signal.ts +36 -0
- package/src/oauth/byo-connection.test.ts +1 -45
- package/src/oauth/byo-connection.ts +2 -8
- package/src/oauth/connect-orchestrator.ts +15 -11
- package/src/oauth/connection-resolver.test.ts +191 -0
- package/src/oauth/connection-resolver.ts +66 -38
- package/src/oauth/connection.ts +0 -1
- package/src/oauth/oauth-store.ts +99 -47
- package/src/oauth/platform-connection.test.ts +0 -1
- package/src/oauth/platform-connection.ts +11 -3
- package/src/oauth/seed-providers.ts +78 -3
- package/src/oauth/token-persistence.ts +16 -10
- package/src/permissions/checker.ts +160 -14
- package/src/permissions/defaults.ts +14 -0
- package/src/prompts/templates/BOOTSTRAP.md +2 -0
- package/src/providers/anthropic/client.ts +8 -1
- package/src/providers/failover.ts +4 -1
- package/src/providers/gemini/client.ts +50 -0
- package/src/providers/model-catalog.ts +92 -0
- package/src/providers/model-intents.ts +29 -20
- package/src/providers/openai/client.ts +49 -0
- package/src/providers/types.ts +2 -0
- package/src/runtime/access-request-helper.ts +16 -7
- package/src/runtime/auth/credential-service.ts +3 -1
- package/src/runtime/auth/route-policy.ts +14 -1
- package/src/runtime/btw-sidechain.ts +101 -0
- package/src/runtime/channel-reply-delivery.ts +17 -1
- package/src/runtime/http-router.ts +3 -1
- package/src/runtime/http-server.ts +196 -141
- package/src/runtime/http-types.ts +1 -0
- package/src/runtime/migrations/vbundle-builder.ts +5 -1
- package/src/runtime/routes/access-request-decision.ts +41 -0
- package/src/runtime/routes/app-management-routes.ts +6 -3
- package/src/runtime/routes/app-routes.ts +7 -3
- package/src/runtime/routes/approval-routes.ts +1 -0
- package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +34 -2
- package/src/runtime/routes/attachment-routes.ts +45 -15
- package/src/runtime/routes/btw-routes.ts +21 -61
- package/src/runtime/routes/conversation-management-routes.ts +74 -0
- package/src/runtime/routes/conversation-query-routes.ts +187 -10
- package/src/runtime/routes/conversation-routes.ts +269 -28
- package/src/runtime/routes/conversation-starter-routes.ts +9 -11
- package/src/runtime/routes/diagnostics-routes.ts +1 -0
- package/src/runtime/routes/identity-routes.ts +2 -35
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +2 -2
- package/src/runtime/routes/llm-context-normalization.ts +1212 -0
- package/src/runtime/routes/log-export-routes.ts +3 -0
- package/src/runtime/routes/memory-item-routes.test.ts +34 -0
- package/src/runtime/routes/memory-item-routes.ts +94 -5
- package/src/runtime/routes/migration-routes.ts +4 -1
- package/src/runtime/routes/oauth-apps.ts +291 -0
- package/src/runtime/routes/secret-routes.ts +30 -1
- package/src/runtime/routes/settings-routes.ts +14 -0
- package/src/runtime/routes/surface-action-routes.ts +68 -1
- package/src/runtime/routes/trace-event-routes.ts +4 -1
- package/src/schedule/schedule-store.ts +30 -21
- package/src/security/secure-keys.ts +21 -0
- package/src/signals/bash.ts +1 -1
- package/src/skills/inline-command-expansions.ts +204 -0
- package/src/skills/inline-command-render.ts +127 -0
- package/src/skills/inline-command-runner.ts +242 -0
- package/src/skills/transitive-version-hash.ts +88 -0
- package/src/swarm/backend-claude-code.ts +3 -6
- package/src/tasks/task-store.ts +43 -1
- package/src/telemetry/usage-telemetry-reporter.test.ts +3 -2
- package/src/telemetry/usage-telemetry-reporter.ts +3 -1
- package/src/tools/AGENTS.md +6 -10
- package/src/tools/apps/executors.ts +17 -232
- package/src/tools/claude-code/claude-code.ts +2 -3
- package/src/tools/credentials/vault.ts +7 -12
- package/src/tools/host-filesystem/read.ts +13 -10
- package/src/tools/network/__tests__/web-search.test.ts +4 -2
- package/src/tools/permission-checker.ts +8 -1
- package/src/tools/schedule/list.ts +2 -7
- package/src/tools/schema-transforms.ts +5 -0
- package/src/tools/shared/filesystem/format-diff.ts +2 -7
- package/src/tools/skills/execute.ts +1 -1
- package/src/tools/skills/load.ts +140 -6
- package/src/tools/tool-manifest.ts +0 -6
- package/src/tools/ui-surface/definitions.ts +2 -2
- package/src/util/device-id.ts +28 -5
- package/src/util/platform.ts +24 -0
- package/src/util/pricing.ts +1 -0
- package/src/util/retry.ts +1 -3
- package/src/workspace/migrations/003-seed-device-id.ts +3 -4
- package/src/workspace/migrations/006-services-config.ts +5 -0
- package/src/workspace/migrations/008-voice-timeout-and-max-steps.ts +12 -0
- package/src/workspace/migrations/009-backfill-conversation-disk-view.ts +10 -0
- package/src/workspace/migrations/010-app-dir-rename.ts +223 -0
- package/src/workspace/migrations/{002-backfill-installation-id.ts → 011-backfill-installation-id.ts} +24 -13
- package/src/workspace/migrations/012-rename-conversation-disk-view-dirs.ts +64 -0
- package/src/workspace/migrations/013-repair-conversation-disk-view.ts +11 -0
- package/src/workspace/migrations/rebuild-conversation-disk-view.ts +186 -0
- package/src/workspace/migrations/registry.ts +11 -1
- package/src/workspace/top-level-renderer.ts +12 -0
- package/src/__tests__/asset-materialize-tool.test.ts +0 -523
- package/src/__tests__/asset-search-tool.test.ts +0 -536
- package/src/__tests__/fixtures/media-reuse-fixtures.ts +0 -56
- package/src/__tests__/media-reuse-story.e2e.test.ts +0 -762
- package/src/__tests__/media-visibility-policy.test.ts +0 -190
- package/src/config/bundled-skills/app-builder/tools/app-file-edit.ts +0 -14
- package/src/config/bundled-skills/app-builder/tools/app-file-list.ts +0 -13
- package/src/config/bundled-skills/app-builder/tools/app-file-read.ts +0 -21
- package/src/config/bundled-skills/app-builder/tools/app-file-write.ts +0 -14
- package/src/config/bundled-skills/app-builder/tools/app-list.ts +0 -13
- package/src/config/bundled-skills/app-builder/tools/app-update.ts +0 -23
- package/src/daemon/media-visibility-policy.ts +0 -59
- package/src/tools/assets/materialize.ts +0 -248
- package/src/tools/assets/search.ts +0 -400
|
@@ -1,523 +0,0 @@
|
|
|
1
|
-
import { existsSync, mkdtempSync, readFileSync, rmSync } from "node:fs";
|
|
2
|
-
import { tmpdir } from "node:os";
|
|
3
|
-
import { join } from "node:path";
|
|
4
|
-
import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
5
|
-
|
|
6
|
-
import { RiskLevel } from "../permissions/types.js";
|
|
7
|
-
|
|
8
|
-
const testDir = mkdtempSync(join(tmpdir(), "asset-materialize-test-"));
|
|
9
|
-
const sandboxDir = join(testDir, "sandbox");
|
|
10
|
-
|
|
11
|
-
mock.module("../util/platform.js", () => ({
|
|
12
|
-
getDataDir: () => testDir,
|
|
13
|
-
isMacOS: () => process.platform === "darwin",
|
|
14
|
-
isLinux: () => process.platform === "linux",
|
|
15
|
-
isWindows: () => process.platform === "win32",
|
|
16
|
-
getPidPath: () => join(testDir, "test.pid"),
|
|
17
|
-
getDbPath: () => join(testDir, "test.db"),
|
|
18
|
-
getLogPath: () => join(testDir, "test.log"),
|
|
19
|
-
ensureDataDir: () => {},
|
|
20
|
-
getRootDir: () => testDir,
|
|
21
|
-
}));
|
|
22
|
-
|
|
23
|
-
mock.module("../util/logger.js", () => ({
|
|
24
|
-
getLogger: () =>
|
|
25
|
-
new Proxy({} as Record<string, unknown>, {
|
|
26
|
-
get: () => () => {},
|
|
27
|
-
}),
|
|
28
|
-
}));
|
|
29
|
-
|
|
30
|
-
mock.module("../config/loader.js", () => ({
|
|
31
|
-
getConfig: () => ({
|
|
32
|
-
ui: {},
|
|
33
|
-
|
|
34
|
-
model: "test",
|
|
35
|
-
provider: "test",
|
|
36
|
-
memory: { enabled: false },
|
|
37
|
-
rateLimit: { maxRequestsPerMinute: 0 },
|
|
38
|
-
}),
|
|
39
|
-
}));
|
|
40
|
-
|
|
41
|
-
import {
|
|
42
|
-
linkAttachmentToMessage,
|
|
43
|
-
uploadAttachment,
|
|
44
|
-
} from "../memory/attachments-store.js";
|
|
45
|
-
import { addMessage, createConversation } from "../memory/conversation-crud.js";
|
|
46
|
-
import { getDb, initializeDb, resetDb } from "../memory/db.js";
|
|
47
|
-
import { assetMaterializeTool } from "../tools/assets/materialize.js";
|
|
48
|
-
import type { ToolContext } from "../tools/types.js";
|
|
49
|
-
|
|
50
|
-
initializeDb();
|
|
51
|
-
|
|
52
|
-
// Ensure the sandbox directory exists
|
|
53
|
-
import { mkdirSync } from "node:fs";
|
|
54
|
-
mkdirSync(sandboxDir, { recursive: true });
|
|
55
|
-
|
|
56
|
-
afterAll(() => {
|
|
57
|
-
resetDb();
|
|
58
|
-
try {
|
|
59
|
-
rmSync(testDir, { recursive: true });
|
|
60
|
-
} catch {
|
|
61
|
-
/* best effort */
|
|
62
|
-
}
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
function resetTables() {
|
|
66
|
-
const db = getDb();
|
|
67
|
-
db.run("DELETE FROM message_attachments");
|
|
68
|
-
db.run("DELETE FROM attachments");
|
|
69
|
-
db.run("DELETE FROM messages");
|
|
70
|
-
db.run("DELETE FROM conversations");
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
const dummyContext: ToolContext = {
|
|
74
|
-
workingDir: sandboxDir,
|
|
75
|
-
conversationId: "conv-test",
|
|
76
|
-
trustClass: "guardian",
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
// ---------------------------------------------------------------------------
|
|
80
|
-
// Input validation
|
|
81
|
-
// ---------------------------------------------------------------------------
|
|
82
|
-
|
|
83
|
-
describe("AssetMaterializeTool input validation", () => {
|
|
84
|
-
test("returns error when attachment_id is missing", async () => {
|
|
85
|
-
const result = await assetMaterializeTool.execute(
|
|
86
|
-
{ destination_path: "output.png" },
|
|
87
|
-
dummyContext,
|
|
88
|
-
);
|
|
89
|
-
expect(result.isError).toBe(true);
|
|
90
|
-
expect(result.content).toContain("attachment_id is required");
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
test("returns error when destination_path is missing", async () => {
|
|
94
|
-
const result = await assetMaterializeTool.execute(
|
|
95
|
-
{ attachment_id: "some-id" },
|
|
96
|
-
dummyContext,
|
|
97
|
-
);
|
|
98
|
-
expect(result.isError).toBe(true);
|
|
99
|
-
expect(result.content).toContain("destination_path is required");
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
test("returns error when both params are missing", async () => {
|
|
103
|
-
const result = await assetMaterializeTool.execute({}, dummyContext);
|
|
104
|
-
expect(result.isError).toBe(true);
|
|
105
|
-
});
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
// ---------------------------------------------------------------------------
|
|
109
|
-
// Sandbox path enforcement
|
|
110
|
-
// ---------------------------------------------------------------------------
|
|
111
|
-
|
|
112
|
-
describe("AssetMaterializeTool sandbox path enforcement", () => {
|
|
113
|
-
beforeEach(resetTables);
|
|
114
|
-
|
|
115
|
-
test("rejects path that escapes sandbox via ../", async () => {
|
|
116
|
-
const stored = uploadAttachment("test.png", "image/png", "AAAA");
|
|
117
|
-
const result = await assetMaterializeTool.execute(
|
|
118
|
-
{ attachment_id: stored.id, destination_path: "../../etc/evil.png" },
|
|
119
|
-
dummyContext,
|
|
120
|
-
);
|
|
121
|
-
expect(result.isError).toBe(true);
|
|
122
|
-
expect(result.content).toContain("outside");
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
test("rejects absolute path outside sandbox", async () => {
|
|
126
|
-
const stored = uploadAttachment("test.png", "image/png", "AAAA");
|
|
127
|
-
const result = await assetMaterializeTool.execute(
|
|
128
|
-
{
|
|
129
|
-
attachment_id: stored.id,
|
|
130
|
-
destination_path: "/tmp/outside-sandbox/evil.png",
|
|
131
|
-
},
|
|
132
|
-
dummyContext,
|
|
133
|
-
);
|
|
134
|
-
expect(result.isError).toBe(true);
|
|
135
|
-
expect(result.content).toContain("outside");
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
test("accepts relative path inside sandbox", async () => {
|
|
139
|
-
const stored = uploadAttachment("test.png", "image/png", "AAAA");
|
|
140
|
-
const result = await assetMaterializeTool.execute(
|
|
141
|
-
{ attachment_id: stored.id, destination_path: "output.png" },
|
|
142
|
-
dummyContext,
|
|
143
|
-
);
|
|
144
|
-
expect(result.isError).toBe(false);
|
|
145
|
-
expect(result.content).toContain("Materialized");
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
test("accepts nested path inside sandbox with auto-created subdirs", async () => {
|
|
149
|
-
const stored = uploadAttachment("test.png", "image/png", "AAAA");
|
|
150
|
-
const result = await assetMaterializeTool.execute(
|
|
151
|
-
{ attachment_id: stored.id, destination_path: "subdir/deep/output.png" },
|
|
152
|
-
dummyContext,
|
|
153
|
-
);
|
|
154
|
-
expect(result.isError).toBe(false);
|
|
155
|
-
expect(existsSync(join(sandboxDir, "subdir", "deep", "output.png"))).toBe(
|
|
156
|
-
true,
|
|
157
|
-
);
|
|
158
|
-
});
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
// ---------------------------------------------------------------------------
|
|
162
|
-
// Attachment lookup
|
|
163
|
-
// ---------------------------------------------------------------------------
|
|
164
|
-
|
|
165
|
-
describe("AssetMaterializeTool attachment lookup", () => {
|
|
166
|
-
beforeEach(resetTables);
|
|
167
|
-
|
|
168
|
-
test("returns error for non-existent attachment ID", async () => {
|
|
169
|
-
const result = await assetMaterializeTool.execute(
|
|
170
|
-
{ attachment_id: "nonexistent-id", destination_path: "out.png" },
|
|
171
|
-
dummyContext,
|
|
172
|
-
);
|
|
173
|
-
expect(result.isError).toBe(true);
|
|
174
|
-
expect(result.content).toContain("not found");
|
|
175
|
-
});
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
// ---------------------------------------------------------------------------
|
|
179
|
-
// Successful materialization
|
|
180
|
-
// ---------------------------------------------------------------------------
|
|
181
|
-
|
|
182
|
-
describe("AssetMaterializeTool materialization", () => {
|
|
183
|
-
beforeEach(resetTables);
|
|
184
|
-
|
|
185
|
-
test("writes correct binary content to disk", async () => {
|
|
186
|
-
// Create known content: "Hello, World!" in base64
|
|
187
|
-
const originalContent = "Hello, World!";
|
|
188
|
-
const base64Content = Buffer.from(originalContent).toString("base64");
|
|
189
|
-
|
|
190
|
-
const stored = uploadAttachment("hello.txt", "text/plain", base64Content);
|
|
191
|
-
|
|
192
|
-
const destPath = "materialized-hello.txt";
|
|
193
|
-
const result = await assetMaterializeTool.execute(
|
|
194
|
-
{ attachment_id: stored.id, destination_path: destPath },
|
|
195
|
-
dummyContext,
|
|
196
|
-
);
|
|
197
|
-
|
|
198
|
-
expect(result.isError).toBe(false);
|
|
199
|
-
expect(result.content).toContain("Materialized");
|
|
200
|
-
expect(result.content).toContain("hello.txt");
|
|
201
|
-
expect(result.content).toContain("text/plain");
|
|
202
|
-
|
|
203
|
-
const writtenContent = readFileSync(join(sandboxDir, destPath), "utf-8");
|
|
204
|
-
expect(writtenContent).toBe(originalContent);
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
test("writes binary (image) content correctly", async () => {
|
|
208
|
-
// Small valid PNG-like bytes encoded as base64
|
|
209
|
-
const binaryBytes = Buffer.from([
|
|
210
|
-
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a,
|
|
211
|
-
]);
|
|
212
|
-
const base64Content = binaryBytes.toString("base64");
|
|
213
|
-
|
|
214
|
-
const stored = uploadAttachment("tiny.png", "image/png", base64Content);
|
|
215
|
-
|
|
216
|
-
const result = await assetMaterializeTool.execute(
|
|
217
|
-
{ attachment_id: stored.id, destination_path: "images/tiny.png" },
|
|
218
|
-
dummyContext,
|
|
219
|
-
);
|
|
220
|
-
|
|
221
|
-
expect(result.isError).toBe(false);
|
|
222
|
-
|
|
223
|
-
const writtenBytes = readFileSync(join(sandboxDir, "images", "tiny.png"));
|
|
224
|
-
expect(Buffer.compare(writtenBytes, binaryBytes)).toBe(0);
|
|
225
|
-
});
|
|
226
|
-
|
|
227
|
-
test("result includes resolved path", async () => {
|
|
228
|
-
const base64Content = Buffer.from("test").toString("base64");
|
|
229
|
-
const stored = uploadAttachment("doc.txt", "text/plain", base64Content);
|
|
230
|
-
|
|
231
|
-
const result = await assetMaterializeTool.execute(
|
|
232
|
-
{ attachment_id: stored.id, destination_path: "output/doc.txt" },
|
|
233
|
-
dummyContext,
|
|
234
|
-
);
|
|
235
|
-
|
|
236
|
-
expect(result.isError).toBe(false);
|
|
237
|
-
expect(result.content).toContain(join(sandboxDir, "output", "doc.txt"));
|
|
238
|
-
});
|
|
239
|
-
|
|
240
|
-
test("result includes filename, MIME type and size info", async () => {
|
|
241
|
-
const base64Content = Buffer.from("some data here").toString("base64");
|
|
242
|
-
const stored = uploadAttachment(
|
|
243
|
-
"report.pdf",
|
|
244
|
-
"application/pdf",
|
|
245
|
-
base64Content,
|
|
246
|
-
);
|
|
247
|
-
|
|
248
|
-
const result = await assetMaterializeTool.execute(
|
|
249
|
-
{ attachment_id: stored.id, destination_path: "report.pdf" },
|
|
250
|
-
dummyContext,
|
|
251
|
-
);
|
|
252
|
-
|
|
253
|
-
expect(result.isError).toBe(false);
|
|
254
|
-
expect(result.content).toContain("report.pdf");
|
|
255
|
-
expect(result.content).toContain("application/pdf");
|
|
256
|
-
});
|
|
257
|
-
});
|
|
258
|
-
|
|
259
|
-
// ---------------------------------------------------------------------------
|
|
260
|
-
// Size limit enforcement
|
|
261
|
-
// ---------------------------------------------------------------------------
|
|
262
|
-
|
|
263
|
-
describe("AssetMaterializeTool size limit", () => {
|
|
264
|
-
beforeEach(resetTables);
|
|
265
|
-
|
|
266
|
-
test("rejects attachment exceeding 100MB limit", async () => {
|
|
267
|
-
// Simulate a large attachment by inserting directly into the DB
|
|
268
|
-
// with a sizeBytes value over the limit
|
|
269
|
-
const db = getDb();
|
|
270
|
-
const fakeId = "oversized-attachment";
|
|
271
|
-
db.run(
|
|
272
|
-
`INSERT INTO attachments (id, original_filename, mime_type, size_bytes, kind, data_base64, created_at)
|
|
273
|
-
VALUES ('${fakeId}', 'huge.bin', 'application/octet-stream', ${
|
|
274
|
-
101 * 1024 * 1024
|
|
275
|
-
}, 'document', 'AAAA', ${Date.now()})`,
|
|
276
|
-
);
|
|
277
|
-
|
|
278
|
-
const result = await assetMaterializeTool.execute(
|
|
279
|
-
{ attachment_id: fakeId, destination_path: "huge.bin" },
|
|
280
|
-
dummyContext,
|
|
281
|
-
);
|
|
282
|
-
|
|
283
|
-
expect(result.isError).toBe(true);
|
|
284
|
-
expect(result.content).toContain("exceeds");
|
|
285
|
-
expect(result.content).toContain("materialization limit");
|
|
286
|
-
});
|
|
287
|
-
});
|
|
288
|
-
|
|
289
|
-
// ---------------------------------------------------------------------------
|
|
290
|
-
// Tool metadata
|
|
291
|
-
// ---------------------------------------------------------------------------
|
|
292
|
-
|
|
293
|
-
describe("AssetMaterializeTool metadata", () => {
|
|
294
|
-
test("tool definition has correct name", () => {
|
|
295
|
-
const def = assetMaterializeTool.getDefinition();
|
|
296
|
-
expect(def.name).toBe("asset_materialize");
|
|
297
|
-
});
|
|
298
|
-
|
|
299
|
-
test("tool definition has required params", () => {
|
|
300
|
-
const def = assetMaterializeTool.getDefinition();
|
|
301
|
-
expect((def.input_schema as Record<string, unknown>).required).toEqual([
|
|
302
|
-
"attachment_id",
|
|
303
|
-
"destination_path",
|
|
304
|
-
]);
|
|
305
|
-
});
|
|
306
|
-
|
|
307
|
-
test("tool definition has attachment_id and destination_path properties", () => {
|
|
308
|
-
const def = assetMaterializeTool.getDefinition();
|
|
309
|
-
expect(
|
|
310
|
-
(def.input_schema as Record<string, unknown>).properties,
|
|
311
|
-
).toHaveProperty("attachment_id");
|
|
312
|
-
expect(
|
|
313
|
-
(def.input_schema as Record<string, unknown>).properties,
|
|
314
|
-
).toHaveProperty("destination_path");
|
|
315
|
-
});
|
|
316
|
-
|
|
317
|
-
test("tool has LOW risk level", () => {
|
|
318
|
-
expect(assetMaterializeTool.defaultRiskLevel).toBe(RiskLevel.Low);
|
|
319
|
-
});
|
|
320
|
-
|
|
321
|
-
test("tool category is assets", () => {
|
|
322
|
-
expect(assetMaterializeTool.category).toBe("assets");
|
|
323
|
-
});
|
|
324
|
-
|
|
325
|
-
test("tool name is asset_materialize", () => {
|
|
326
|
-
expect(assetMaterializeTool.name).toBe("asset_materialize");
|
|
327
|
-
});
|
|
328
|
-
});
|
|
329
|
-
|
|
330
|
-
// ---------------------------------------------------------------------------
|
|
331
|
-
// Visibility policy enforcement
|
|
332
|
-
// ---------------------------------------------------------------------------
|
|
333
|
-
|
|
334
|
-
describe("AssetMaterializeTool visibility policy", () => {
|
|
335
|
-
beforeEach(resetTables);
|
|
336
|
-
|
|
337
|
-
test("materializing from a standard thread works from any context", async () => {
|
|
338
|
-
const standardConv = createConversation({ title: "standard-conv" });
|
|
339
|
-
const base64Content = Buffer.from("standard content").toString("base64");
|
|
340
|
-
const attachment = uploadAttachment(
|
|
341
|
-
"public.txt",
|
|
342
|
-
"text/plain",
|
|
343
|
-
base64Content,
|
|
344
|
-
);
|
|
345
|
-
const msg = await addMessage(standardConv.id, "user", "standard message");
|
|
346
|
-
linkAttachmentToMessage(msg.id, attachment.id, 0);
|
|
347
|
-
|
|
348
|
-
// Materialize from a different standard conversation
|
|
349
|
-
const otherConv = createConversation({ title: "other-conv" });
|
|
350
|
-
const context: ToolContext = {
|
|
351
|
-
workingDir: sandboxDir,
|
|
352
|
-
conversationId: otherConv.id,
|
|
353
|
-
trustClass: "guardian",
|
|
354
|
-
};
|
|
355
|
-
|
|
356
|
-
const result = await assetMaterializeTool.execute(
|
|
357
|
-
{ attachment_id: attachment.id, destination_path: "public-output.txt" },
|
|
358
|
-
context,
|
|
359
|
-
);
|
|
360
|
-
expect(result.isError).toBe(false);
|
|
361
|
-
expect(result.content).toContain("Materialized");
|
|
362
|
-
});
|
|
363
|
-
|
|
364
|
-
test("materializing from a private thread works within the same private thread", async () => {
|
|
365
|
-
const privateConv = createConversation({
|
|
366
|
-
title: "private-conv",
|
|
367
|
-
conversationType: "private",
|
|
368
|
-
});
|
|
369
|
-
const base64Content = Buffer.from("private content").toString("base64");
|
|
370
|
-
const attachment = uploadAttachment(
|
|
371
|
-
"secret.txt",
|
|
372
|
-
"text/plain",
|
|
373
|
-
base64Content,
|
|
374
|
-
);
|
|
375
|
-
const msg = await addMessage(privateConv.id, "user", "private message");
|
|
376
|
-
linkAttachmentToMessage(msg.id, attachment.id, 0);
|
|
377
|
-
|
|
378
|
-
// Materialize from the same private conversation
|
|
379
|
-
const context: ToolContext = {
|
|
380
|
-
workingDir: sandboxDir,
|
|
381
|
-
conversationId: privateConv.id,
|
|
382
|
-
trustClass: "guardian",
|
|
383
|
-
};
|
|
384
|
-
|
|
385
|
-
const result = await assetMaterializeTool.execute(
|
|
386
|
-
{ attachment_id: attachment.id, destination_path: "private-output.txt" },
|
|
387
|
-
context,
|
|
388
|
-
);
|
|
389
|
-
expect(result.isError).toBe(false);
|
|
390
|
-
expect(result.content).toContain("Materialized");
|
|
391
|
-
});
|
|
392
|
-
|
|
393
|
-
test("materializing from a private thread is REJECTED from a different conversation", async () => {
|
|
394
|
-
const privateConv = createConversation({
|
|
395
|
-
title: "private-conv",
|
|
396
|
-
conversationType: "private",
|
|
397
|
-
});
|
|
398
|
-
const base64Content = Buffer.from("private content").toString("base64");
|
|
399
|
-
const attachment = uploadAttachment(
|
|
400
|
-
"secret.txt",
|
|
401
|
-
"text/plain",
|
|
402
|
-
base64Content,
|
|
403
|
-
);
|
|
404
|
-
const msg = await addMessage(privateConv.id, "user", "private message");
|
|
405
|
-
linkAttachmentToMessage(msg.id, attachment.id, 0);
|
|
406
|
-
|
|
407
|
-
// Attempt to materialize from a different conversation
|
|
408
|
-
const otherConv = createConversation({ title: "other-conv" });
|
|
409
|
-
const context: ToolContext = {
|
|
410
|
-
workingDir: sandboxDir,
|
|
411
|
-
conversationId: otherConv.id,
|
|
412
|
-
trustClass: "guardian",
|
|
413
|
-
};
|
|
414
|
-
|
|
415
|
-
const result = await assetMaterializeTool.execute(
|
|
416
|
-
{ attachment_id: attachment.id, destination_path: "stolen.txt" },
|
|
417
|
-
context,
|
|
418
|
-
);
|
|
419
|
-
expect(result.isError).toBe(true);
|
|
420
|
-
expect(result.content).toContain("private conversation");
|
|
421
|
-
expect(result.content).toContain("cannot be accessed");
|
|
422
|
-
});
|
|
423
|
-
|
|
424
|
-
test("error message is user-actionable", async () => {
|
|
425
|
-
const privateConv = createConversation({
|
|
426
|
-
title: "private-conv",
|
|
427
|
-
conversationType: "private",
|
|
428
|
-
});
|
|
429
|
-
const base64Content = Buffer.from("private content").toString("base64");
|
|
430
|
-
const attachment = uploadAttachment(
|
|
431
|
-
"confidential.pdf",
|
|
432
|
-
"application/pdf",
|
|
433
|
-
base64Content,
|
|
434
|
-
);
|
|
435
|
-
const msg = await addMessage(privateConv.id, "user", "private message");
|
|
436
|
-
linkAttachmentToMessage(msg.id, attachment.id, 0);
|
|
437
|
-
|
|
438
|
-
// From a standard conversation
|
|
439
|
-
const standardConv = createConversation({ title: "standard-conv" });
|
|
440
|
-
const context: ToolContext = {
|
|
441
|
-
workingDir: sandboxDir,
|
|
442
|
-
conversationId: standardConv.id,
|
|
443
|
-
trustClass: "guardian",
|
|
444
|
-
};
|
|
445
|
-
|
|
446
|
-
const result = await assetMaterializeTool.execute(
|
|
447
|
-
{ attachment_id: attachment.id, destination_path: "stolen.pdf" },
|
|
448
|
-
context,
|
|
449
|
-
);
|
|
450
|
-
expect(result.isError).toBe(true);
|
|
451
|
-
// Should mention the filename so the user knows which file
|
|
452
|
-
expect(result.content).toContain("confidential.pdf");
|
|
453
|
-
// Should explain how to access it
|
|
454
|
-
expect(result.content).toContain("from within the private conversation");
|
|
455
|
-
});
|
|
456
|
-
|
|
457
|
-
test("materializing from a different private thread is REJECTED", async () => {
|
|
458
|
-
const privateConv1 = createConversation({
|
|
459
|
-
title: "private-conv-1",
|
|
460
|
-
conversationType: "private",
|
|
461
|
-
});
|
|
462
|
-
const base64Content = Buffer.from("private content").toString("base64");
|
|
463
|
-
const attachment = uploadAttachment(
|
|
464
|
-
"secret.txt",
|
|
465
|
-
"text/plain",
|
|
466
|
-
base64Content,
|
|
467
|
-
);
|
|
468
|
-
const msg = await addMessage(privateConv1.id, "user", "private message");
|
|
469
|
-
linkAttachmentToMessage(msg.id, attachment.id, 0);
|
|
470
|
-
|
|
471
|
-
// Attempt from a different private conversation
|
|
472
|
-
const privateConv2 = createConversation({
|
|
473
|
-
title: "private-conv-2",
|
|
474
|
-
conversationType: "private",
|
|
475
|
-
});
|
|
476
|
-
const context: ToolContext = {
|
|
477
|
-
workingDir: sandboxDir,
|
|
478
|
-
conversationId: privateConv2.id,
|
|
479
|
-
trustClass: "guardian",
|
|
480
|
-
};
|
|
481
|
-
|
|
482
|
-
const result = await assetMaterializeTool.execute(
|
|
483
|
-
{ attachment_id: attachment.id, destination_path: "cross-thread.txt" },
|
|
484
|
-
context,
|
|
485
|
-
);
|
|
486
|
-
expect(result.isError).toBe(true);
|
|
487
|
-
expect(result.content).toContain("private conversation");
|
|
488
|
-
});
|
|
489
|
-
|
|
490
|
-
test("attachment linked to both private and standard threads can be materialized from anywhere", async () => {
|
|
491
|
-
const privateConv = createConversation({
|
|
492
|
-
title: "private-conv",
|
|
493
|
-
conversationType: "private",
|
|
494
|
-
});
|
|
495
|
-
const standardConv = createConversation({ title: "standard-conv" });
|
|
496
|
-
const base64Content = Buffer.from("shared content").toString("base64");
|
|
497
|
-
const attachment = uploadAttachment(
|
|
498
|
-
"shared.txt",
|
|
499
|
-
"text/plain",
|
|
500
|
-
base64Content,
|
|
501
|
-
);
|
|
502
|
-
|
|
503
|
-
const msg1 = await addMessage(privateConv.id, "user", "private message");
|
|
504
|
-
const msg2 = await addMessage(standardConv.id, "user", "standard message");
|
|
505
|
-
linkAttachmentToMessage(msg1.id, attachment.id, 0);
|
|
506
|
-
linkAttachmentToMessage(msg2.id, attachment.id, 0);
|
|
507
|
-
|
|
508
|
-
// Should be materializable from a third, unrelated standard conversation
|
|
509
|
-
const otherConv = createConversation({ title: "other-conv" });
|
|
510
|
-
const context: ToolContext = {
|
|
511
|
-
workingDir: sandboxDir,
|
|
512
|
-
conversationId: otherConv.id,
|
|
513
|
-
trustClass: "guardian",
|
|
514
|
-
};
|
|
515
|
-
|
|
516
|
-
const result = await assetMaterializeTool.execute(
|
|
517
|
-
{ attachment_id: attachment.id, destination_path: "shared-output.txt" },
|
|
518
|
-
context,
|
|
519
|
-
);
|
|
520
|
-
expect(result.isError).toBe(false);
|
|
521
|
-
expect(result.content).toContain("Materialized");
|
|
522
|
-
});
|
|
523
|
-
});
|