@vellumai/assistant 0.6.0 → 0.6.2
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/AGENTS.md +4 -0
- package/ARCHITECTURE.md +68 -15
- package/Dockerfile +2 -2
- package/bun.lock +6 -2
- package/docker-entrypoint.sh +42 -1
- package/docs/architecture/integrations.md +1 -1
- package/docs/architecture/memory.md +21 -24
- package/node_modules/@vellumai/ces-contracts/src/handles.ts +7 -9
- package/openapi.yaml +539 -4
- package/package.json +5 -1
- package/src/__tests__/anthropic-provider.test.ts +160 -95
- package/src/__tests__/app-dir-path-guard.test.ts +1 -0
- package/src/__tests__/app-executors.test.ts +47 -1
- package/src/__tests__/app-source-watcher.test.ts +159 -0
- package/src/__tests__/assistant-event-hub.test.ts +30 -0
- package/src/__tests__/checker.test.ts +138 -172
- package/src/__tests__/cli-command-risk-guard.test.ts +1 -1
- package/src/__tests__/config-schema.test.ts +5 -0
- package/src/__tests__/context-overflow-approval.test.ts +5 -5
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +4 -6
- package/src/__tests__/conversation-agent-loop.test.ts +4 -51
- package/src/__tests__/conversation-analysis-routes.test.ts +169 -0
- package/src/__tests__/conversation-directories-parse.test.ts +105 -0
- package/src/__tests__/conversation-history-web-search.test.ts +1 -1
- package/src/__tests__/conversation-runtime-assembly.test.ts +653 -832
- package/src/__tests__/conversation-runtime-workspace.test.ts +1 -93
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +17 -4
- package/src/__tests__/conversation-wipe.test.ts +2 -6
- package/src/__tests__/conversation-workspace-cache-state.test.ts +6 -12
- package/src/__tests__/conversation-workspace-injection.test.ts +25 -26
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +1 -1
- package/src/__tests__/copy-composer-tc-templates.test.ts +335 -0
- package/src/__tests__/credential-execution-approval-bridge.test.ts +0 -2
- package/src/__tests__/date-context.test.ts +76 -210
- package/src/__tests__/db-schedule-syntax-migration.test.ts +16 -1
- package/src/__tests__/file-list-tool.test.ts +219 -0
- package/src/__tests__/first-greeting.test.ts +1 -1
- package/src/__tests__/heartbeat-service.test.ts +180 -3
- package/src/__tests__/identity-routes.test.ts +328 -0
- package/src/__tests__/init-feature-flag-overrides.test.ts +167 -0
- package/src/__tests__/injection-block.test.ts +24 -0
- package/src/__tests__/inline-command-runner.test.ts +7 -5
- package/src/__tests__/install-skill-routing.test.ts +7 -6
- package/src/__tests__/jobs-store-qdrant-breaker.test.ts +15 -14
- package/src/__tests__/list-messages-tool-merge.test.ts +300 -0
- package/src/__tests__/llm-context-normalization.test.ts +18 -18
- package/src/__tests__/llm-context-route-provider.test.ts +101 -0
- package/src/__tests__/llm-request-log-turn-query.test.ts +162 -0
- package/src/__tests__/log-export-workspace.test.ts +257 -100
- package/src/__tests__/managed-credential-catalog-cli.test.ts +12 -14
- package/src/__tests__/mcp-abort-signal.test.ts +5 -0
- package/src/__tests__/mcp-client-auth.test.ts +5 -0
- package/src/__tests__/memory-recall-log-store.test.ts +132 -0
- package/src/__tests__/migration-export-streaming.test.ts +304 -0
- package/src/__tests__/migration-import-commit-http.test.ts +11 -10
- package/src/__tests__/mock-fetch.ts +87 -0
- package/src/__tests__/navigate-settings-tab.test.ts +14 -1
- package/src/__tests__/notification-broadcaster.test.ts +65 -0
- package/src/__tests__/notification-decision-recipient-context.test.ts +282 -0
- package/src/__tests__/onboarding-template-contract.test.ts +63 -14
- package/src/__tests__/parser.test.ts +32 -0
- package/src/__tests__/permission-checker-host-gate.test.ts +452 -0
- package/src/__tests__/permission-controls-v2-flag.test.ts +55 -0
- package/src/__tests__/permission-mode-sse.test.ts +418 -0
- package/src/__tests__/permission-mode-store.test.ts +277 -0
- package/src/__tests__/permission-mode.test.ts +101 -0
- package/src/__tests__/pkb-autoinject.test.ts +96 -0
- package/src/__tests__/platform-bash-auto-approve.test.ts +359 -0
- package/src/__tests__/profiler-routes.test.ts +502 -0
- package/src/__tests__/profiler-run-store.test.ts +441 -0
- package/src/__tests__/proxy-approval-callback.test.ts +4 -75
- package/src/__tests__/registry.test.ts +1 -1
- package/src/__tests__/require-fresh-approval.test.ts +0 -2
- package/src/__tests__/sandbox-diagnostics.test.ts +1 -32
- package/src/__tests__/sandbox-host-parity.test.ts +5 -4
- package/src/__tests__/scheduler-reuse-conversation.test.ts +368 -0
- package/src/__tests__/scrub-corrupted-image-attachments.test.ts +278 -0
- package/src/__tests__/search-skills-unified.test.ts +4 -3
- package/src/__tests__/send-endpoint-busy.test.ts +42 -3
- package/src/__tests__/set-permission-mode.test.ts +274 -0
- package/src/__tests__/skill-load-feature-flag.test.ts +12 -0
- package/src/__tests__/skill-memory.test.ts +2 -783
- package/src/__tests__/strip-memory-injections.test.ts +187 -0
- package/src/__tests__/subagent-detail.test.ts +84 -0
- package/src/__tests__/subagent-disposal.test.ts +308 -0
- package/src/__tests__/subagent-manager-notify.test.ts +19 -10
- package/src/__tests__/subagent-notify-parent.test.ts +390 -0
- package/src/__tests__/subagent-role-registry.test.ts +108 -0
- package/src/__tests__/subagent-tool-filtering.test.ts +71 -0
- package/src/__tests__/subagent-tools.test.ts +464 -4
- package/src/__tests__/system-prompt-ask-mode.test.ts +139 -0
- package/src/__tests__/task-memory-cleanup.test.ts +12 -12
- package/src/__tests__/terminal-sandbox.test.ts +1 -1
- package/src/__tests__/terminal-tools.test.ts +16 -29
- package/src/__tests__/test-preload.ts +18 -0
- package/src/__tests__/tool-domain-event-publisher.test.ts +0 -1
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -8
- package/src/__tests__/tool-executor.test.ts +4 -27
- package/src/__tests__/tool-side-effects-slack-dm.test.ts +1 -0
- package/src/__tests__/top-level-renderer.test.ts +10 -13
- package/src/__tests__/transport-hints-queue.test.ts +77 -0
- package/src/__tests__/trust-store.test.ts +4 -4
- package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +116 -2
- package/src/__tests__/workspace-migration-028-recover-conversations-from-disk-view.test.ts +387 -0
- package/src/__tests__/workspace-migration-030-seed-pkb-autoinject.test.ts +168 -0
- package/src/__tests__/workspace-policy.test.ts +2 -7
- package/src/agent/loop.ts +6 -29
- package/src/approvals/guardian-request-resolvers.ts +24 -0
- package/src/avatar/traits-png-sync.ts +3 -3
- package/src/channels/types.ts +5 -0
- package/src/cli/__tests__/run-assistant-command.ts +56 -0
- package/src/cli/__tests__/unknown-command.test.ts +33 -0
- package/src/cli/commands/__tests__/email-download.test.ts +245 -0
- package/src/cli/commands/__tests__/email-list.test.ts +192 -0
- package/src/cli/commands/__tests__/email-register.test.ts +186 -0
- package/src/cli/commands/__tests__/email-send.test.ts +291 -0
- package/src/cli/commands/__tests__/email-status.test.ts +181 -0
- package/src/cli/commands/__tests__/email-unregister.test.ts +139 -0
- package/src/cli/commands/__tests__/routes.test.ts +562 -0
- package/src/cli/commands/conversations.ts +1 -8
- package/src/cli/commands/default-action.ts +68 -1
- package/src/cli/commands/email.ts +584 -835
- package/src/cli/commands/memory.ts +1 -34
- package/src/cli/commands/notifications.ts +7 -2
- package/src/cli/commands/oauth/__tests__/connect.test.ts +27 -0
- package/src/cli/commands/oauth/connect.ts +25 -5
- package/src/cli/commands/platform/__tests__/connect.test.ts +1 -1
- package/src/cli/commands/platform/__tests__/disconnect.test.ts +1 -1
- package/src/cli/commands/platform/__tests__/status.test.ts +1 -1
- package/src/cli/commands/routes.ts +396 -0
- package/src/cli/commands/skills.ts +130 -20
- package/src/cli/program.ts +11 -2
- package/src/cli.ts +1 -120
- package/src/config/assistant-feature-flags.ts +59 -55
- package/src/config/bundled-skills/app-builder/SKILL.md +91 -5
- package/src/config/bundled-skills/gmail/SKILL.md +13 -8
- package/src/config/bundled-skills/gmail/TOOLS.json +1 -1
- package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +2 -1
- package/src/config/bundled-skills/messaging/SKILL.md +7 -0
- package/src/config/bundled-skills/schedule/SKILL.md +22 -2
- package/src/config/bundled-skills/schedule/TOOLS.json +8 -0
- package/src/config/bundled-skills/settings/TOOLS.json +1 -1
- package/src/config/bundled-skills/settings/tools/avatar-get.ts +3 -13
- package/src/config/bundled-skills/settings/tools/avatar-remove.ts +2 -4
- package/src/config/bundled-skills/settings/tools/avatar-update.ts +5 -2
- package/src/config/bundled-skills/settings/tools/navigate-settings-tab.ts +8 -3
- package/src/config/bundled-skills/slack/SKILL.md +2 -0
- package/src/config/bundled-skills/subagent/SKILL.md +43 -3
- package/src/config/bundled-skills/subagent/TOOLS.json +29 -4
- package/src/config/env-registry.ts +63 -0
- package/src/config/feature-flag-registry.json +17 -1
- package/src/config/schema.ts +8 -0
- package/src/config/schemas/filing.ts +51 -0
- package/src/config/schemas/heartbeat.ts +15 -12
- package/src/config/schemas/memory-lifecycle.ts +12 -0
- package/src/config/schemas/security.ts +14 -0
- package/src/config/schemas/services.ts +8 -0
- package/src/credential-execution/approval-bridge.ts +0 -1
- package/src/credential-execution/managed-catalog.ts +3 -7
- package/src/daemon/app-source-watcher.ts +93 -0
- package/src/daemon/config-watcher.ts +85 -3
- package/src/daemon/context-overflow-approval.ts +0 -1
- package/src/daemon/conversation-agent-loop-handlers.ts +20 -0
- package/src/daemon/conversation-agent-loop.ts +179 -65
- package/src/daemon/conversation-attachments.ts +0 -1
- package/src/daemon/conversation-history.ts +4 -19
- package/src/daemon/conversation-lifecycle.ts +8 -14
- package/src/daemon/conversation-messaging.ts +3 -0
- package/src/daemon/conversation-process.ts +30 -8
- package/src/daemon/conversation-queue-manager.ts +8 -0
- package/src/daemon/conversation-runtime-assembly.ts +359 -308
- package/src/daemon/conversation-surfaces.ts +65 -0
- package/src/daemon/conversation-tool-setup.ts +44 -17
- package/src/daemon/conversation-workspace.ts +1 -2
- package/src/daemon/conversation.ts +19 -3
- package/src/daemon/date-context.ts +26 -53
- package/src/daemon/first-greeting.ts +1 -1
- package/src/daemon/handlers/conversations.ts +5 -7
- package/src/daemon/handlers/shared.test.ts +143 -0
- package/src/daemon/handlers/shared.ts +70 -5
- package/src/daemon/handlers/skills.ts +11 -18
- package/src/daemon/lifecycle.ts +220 -158
- package/src/daemon/message-types/conversations.ts +29 -6
- package/src/daemon/message-types/messages.ts +9 -2
- package/src/daemon/message-types/notifications.ts +12 -0
- package/src/daemon/message-types/schedules.ts +1 -0
- package/src/daemon/message-types/settings.ts +18 -0
- package/src/daemon/profiler-run-store.ts +557 -0
- package/src/daemon/server.ts +87 -10
- package/src/daemon/shutdown-handlers.ts +5 -0
- package/src/daemon/tool-side-effects.ts +23 -3
- package/src/daemon/transport-hints.ts +33 -0
- package/src/export/transcript-formatter.ts +148 -0
- package/src/filing/filing-service.ts +228 -0
- package/src/heartbeat/heartbeat-service.ts +96 -7
- package/src/index.ts +1 -1
- package/src/mcp/client.ts +6 -0
- package/src/mcp/mcp-oauth-provider.ts +149 -27
- package/src/memory/admin.ts +33 -32
- package/src/memory/app-store.ts +69 -0
- package/src/memory/conversation-bootstrap.ts +1 -1
- package/src/memory/conversation-crud.ts +151 -117
- package/src/memory/conversation-directories.ts +39 -0
- package/src/memory/conversation-group-migration.ts +66 -6
- package/src/memory/conversation-queries.ts +58 -12
- package/src/memory/conversation-title-service.ts +1 -0
- package/src/memory/db-init.ts +182 -376
- package/src/memory/embedding-local.ts +1 -1
- package/src/memory/graph/bootstrap.ts +75 -66
- package/src/memory/graph/capability-seed.ts +167 -17
- package/src/memory/graph/consolidation.ts +38 -4
- package/src/memory/graph/conversation-graph-memory.ts +133 -104
- package/src/memory/graph/extraction-job.ts +9 -4
- package/src/memory/graph/extraction.ts +66 -23
- package/src/memory/graph/graph-memory-state-store.ts +37 -0
- package/src/memory/graph/graph-search.ts +29 -15
- package/src/memory/graph/injection.ts +38 -8
- package/src/memory/graph/inspect.ts +12 -3
- package/src/memory/graph/retriever.ts +365 -262
- package/src/memory/graph/store.test.ts +48 -0
- package/src/memory/graph/store.ts +150 -11
- package/src/memory/graph/tool-handlers.ts +84 -209
- package/src/memory/graph/tools.ts +8 -52
- package/src/memory/graph/types.ts +24 -0
- package/src/memory/group-crud.ts +25 -9
- package/src/memory/job-handlers/cleanup.ts +44 -1
- package/src/memory/jobs-store.ts +70 -60
- package/src/memory/jobs-worker.ts +44 -28
- package/src/memory/llm-request-log-store.ts +96 -12
- package/src/memory/memory-recall-log-store.ts +49 -5
- package/src/memory/migrations/203-drop-memory-items-tables.ts +33 -1
- package/src/memory/migrations/206-memory-graph-node-edits.ts +19 -0
- package/src/memory/migrations/206-scrub-corrupted-image-attachments.ts +131 -0
- package/src/memory/migrations/207-conversation-graph-memory-state.ts +20 -0
- package/src/memory/migrations/208-conversations-last-message-at.ts +35 -0
- package/src/memory/migrations/209-strip-thinking-from-consolidated.ts +85 -0
- package/src/memory/migrations/210-schedule-reuse-conversation.ts +13 -0
- package/src/memory/migrations/211-memory-recall-logs-query-context.ts +21 -0
- package/src/memory/migrations/212-llm-request-logs-created-at-index.ts +19 -0
- package/src/memory/migrations/index.ts +8 -0
- package/src/memory/migrations/registry.ts +8 -0
- package/src/memory/schema/conversations.ts +14 -0
- package/src/memory/schema/infrastructure.ts +8 -1
- package/src/memory/schema/memory-core.ts +0 -51
- package/src/memory/schema/memory-graph.ts +15 -0
- package/src/memory/task-memory-cleanup.ts +30 -11
- package/src/messaging/provider.ts +1 -1
- package/src/notifications/broadcaster.ts +6 -0
- package/src/notifications/conversation-pairing.ts +12 -4
- package/src/notifications/copy-composer.ts +86 -0
- package/src/notifications/decision-engine.ts +35 -0
- package/src/notifications/emit-signal.ts +14 -0
- package/src/notifications/signal.ts +11 -0
- package/src/oauth/platform-connection.test.ts +2 -2
- package/src/oauth/seed-providers.ts +1 -0
- package/src/permissions/checker.ts +15 -4
- package/src/permissions/defaults.ts +7 -8
- package/src/permissions/permission-mode-store.ts +180 -0
- package/src/permissions/permission-mode.ts +31 -0
- package/src/permissions/prompter.ts +0 -2
- package/src/permissions/workspace-policy.ts +9 -0
- package/src/platform/client.ts +1 -1
- package/src/prompts/system-prompt.ts +59 -7
- package/src/prompts/templates/BOOTSTRAP-REFERENCE.md +100 -0
- package/src/prompts/templates/BOOTSTRAP.md +76 -162
- package/src/prompts/templates/HEARTBEAT.md +3 -1
- package/src/prompts/templates/SOUL.md +30 -9
- package/src/prompts/templates/UPDATES.md +8 -0
- package/src/providers/anthropic/client.ts +107 -219
- package/src/runtime/assistant-event-hub.ts +22 -0
- package/src/runtime/auth/route-policy.ts +23 -0
- package/src/runtime/auth/token-service.ts +8 -0
- package/src/runtime/http-server.ts +32 -2
- package/src/runtime/http-types.ts +12 -1
- package/src/runtime/migrations/vbundle-builder.ts +389 -3
- package/src/runtime/migrations/vbundle-importer.ts +8 -6
- package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +378 -0
- package/src/runtime/routes/app-management-routes.ts +1 -11
- package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +26 -0
- package/src/runtime/routes/archive-utils.ts +29 -0
- package/src/runtime/routes/avatar-routes.ts +2 -9
- package/src/runtime/routes/btw-routes.ts +14 -1
- package/src/runtime/routes/conversation-analysis-routes.ts +185 -0
- package/src/runtime/routes/conversation-management-routes.ts +1 -14
- package/src/runtime/routes/conversation-query-routes.ts +49 -3
- package/src/runtime/routes/conversation-routes.ts +270 -44
- package/src/runtime/routes/group-routes.ts +22 -8
- package/src/runtime/routes/heartbeat-routes.ts +4 -10
- package/src/runtime/routes/identity-routes.ts +53 -18
- package/src/runtime/routes/llm-context-normalization.ts +14 -10
- package/src/runtime/routes/log-export/AGENTS.md +104 -0
- package/src/runtime/routes/log-export/__tests__/workspace-allowlist-error-contract.test.ts +103 -0
- package/src/runtime/routes/log-export/__tests__/workspace-allowlist.test.ts +716 -0
- package/src/runtime/routes/log-export/workspace-allowlist.ts +458 -0
- package/src/runtime/routes/log-export-routes.ts +41 -278
- package/src/runtime/routes/memory-item-routes.test.ts +168 -233
- package/src/runtime/routes/migration-routes.ts +18 -7
- package/src/runtime/routes/profiler-routes.ts +350 -0
- package/src/runtime/routes/schedule-routes.ts +27 -12
- package/src/runtime/routes/settings-routes.ts +95 -8
- package/src/runtime/routes/subagents-routes.ts +28 -7
- package/src/runtime/routes/user-route-dispatcher.ts +223 -0
- package/src/runtime/routes/user-routes.ts +41 -0
- package/src/runtime/routes/workspace-routes.ts +0 -1
- package/src/schedule/schedule-store.ts +30 -0
- package/src/schedule/scheduler.ts +45 -18
- package/src/skills/catalog-install.ts +10 -2
- package/src/skills/inline-command-runner.ts +12 -14
- package/src/skills/managed-store.ts +2 -2
- package/src/skills/skill-memory.ts +1 -293
- package/src/subagent/index.ts +13 -3
- package/src/subagent/manager.ts +308 -29
- package/src/subagent/types.ts +68 -0
- package/src/tasks/task-runner.ts +4 -4
- package/src/tools/apps/executors.ts +29 -4
- package/src/tools/filesystem/list.ts +93 -0
- package/src/tools/permission-checker.ts +78 -18
- package/src/tools/registry.ts +4 -0
- package/src/tools/schedule/create.ts +3 -0
- package/src/tools/schedule/list.ts +1 -0
- package/src/tools/schedule/update.ts +6 -0
- package/src/tools/secret-detection-handler.ts +0 -1
- package/src/tools/shared/filesystem/errors.ts +5 -0
- package/src/tools/shared/filesystem/file-ops-service.ts +90 -2
- package/src/tools/shared/filesystem/types.ts +17 -0
- package/src/tools/shared/shell-output.ts +31 -2
- package/src/tools/skills/sandbox-runner.ts +3 -6
- package/src/tools/subagent/abort.ts +12 -2
- package/src/tools/subagent/message.ts +9 -2
- package/src/tools/subagent/notify-parent.ts +79 -0
- package/src/tools/subagent/read.ts +29 -8
- package/src/tools/subagent/resolve.ts +21 -0
- package/src/tools/subagent/spawn.ts +2 -0
- package/src/tools/subagent/status.ts +11 -1
- package/src/tools/system/avatar-generator.ts +3 -3
- package/src/tools/system/register.ts +23 -0
- package/src/tools/system/set-permission-mode.ts +103 -0
- package/src/tools/terminal/parser.ts +30 -5
- package/src/tools/terminal/safe-env.ts +16 -1
- package/src/tools/terminal/sandbox-diagnostics.ts +4 -4
- package/src/tools/terminal/sandbox.ts +4 -1
- package/src/tools/terminal/shell.ts +3 -5
- package/src/tools/tool-manifest.ts +6 -0
- package/src/tools/types.ts +2 -3
- package/src/util/logger.ts +1 -1
- package/src/util/platform.ts +50 -17
- package/src/watcher/provider-types.ts +1 -1
- package/src/workspace/migrations/023-move-config-files-to-workspace.ts +2 -2
- package/src/workspace/migrations/024-move-runtime-files-to-workspace.ts +2 -2
- package/src/workspace/migrations/028-recover-conversations-from-disk-view.ts +270 -0
- package/src/workspace/migrations/029-seed-pkb.ts +85 -0
- package/src/workspace/migrations/030-seed-pkb-autoinject.ts +73 -0
- package/src/workspace/migrations/registry.ts +6 -0
- package/src/workspace/top-level-renderer.ts +5 -9
- package/src/__tests__/cli-memory.test.ts +0 -377
- package/src/__tests__/clipboard.test.ts +0 -88
- package/src/cli/cli-memory.ts +0 -179
- package/src/util/clipboard.ts +0 -34
|
@@ -2,14 +2,12 @@
|
|
|
2
2
|
* HTTP route handler for exporting audit data and daemon log files.
|
|
3
3
|
*
|
|
4
4
|
* A single POST /v1/export endpoint allows clients (e.g. macOS Export Logs)
|
|
5
|
-
* to retrieve audit database records, daemon log files,
|
|
6
|
-
*
|
|
5
|
+
* to retrieve audit database records, daemon log files, and a sanitized
|
|
6
|
+
* config snapshot as a tar.gz archive.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import { spawnSync } from "node:child_process";
|
|
10
9
|
import {
|
|
11
10
|
existsSync,
|
|
12
|
-
lstatSync,
|
|
13
11
|
mkdirSync,
|
|
14
12
|
mkdtempSync,
|
|
15
13
|
readdirSync,
|
|
@@ -19,7 +17,7 @@ import {
|
|
|
19
17
|
writeFileSync,
|
|
20
18
|
} from "node:fs";
|
|
21
19
|
import { tmpdir } from "node:os";
|
|
22
|
-
import { join
|
|
20
|
+
import { join } from "node:path";
|
|
23
21
|
|
|
24
22
|
import { and, desc, eq, gte, lte } from "drizzle-orm";
|
|
25
23
|
import { z } from "zod";
|
|
@@ -31,25 +29,23 @@ import {
|
|
|
31
29
|
messages,
|
|
32
30
|
toolInvocations,
|
|
33
31
|
} from "../../memory/schema.js";
|
|
34
|
-
import { getLogger } from "../../util/logger.js";
|
|
32
|
+
import { getLogger, LOG_FILE_PATTERN } from "../../util/logger.js";
|
|
35
33
|
import {
|
|
36
34
|
getDaemonStderrLogPath,
|
|
37
35
|
getDataDir,
|
|
38
36
|
getWorkspaceConfigPath,
|
|
39
|
-
getWorkspaceDir,
|
|
40
37
|
} from "../../util/platform.js";
|
|
41
38
|
import { APP_VERSION, COMMIT_SHA } from "../../version.js";
|
|
42
39
|
import { httpError } from "../http-errors.js";
|
|
43
40
|
import type { RouteDefinition } from "../http-router.js";
|
|
41
|
+
import { createTarGz } from "./archive-utils.js";
|
|
42
|
+
import { collectWorkspaceData } from "./log-export/workspace-allowlist.js";
|
|
44
43
|
|
|
45
44
|
const log = getLogger("log-export-routes");
|
|
46
45
|
|
|
47
46
|
/** Maximum total payload size for log file contents (10 MB). */
|
|
48
47
|
const MAX_LOG_PAYLOAD_BYTES = 10 * 1024 * 1024;
|
|
49
48
|
|
|
50
|
-
/** Maximum compressed archive size before pruning workspace directories (50 MB). */
|
|
51
|
-
const MAX_ARCHIVE_BYTES = 50 * 1024 * 1024;
|
|
52
|
-
|
|
53
49
|
interface ExportRequestBody {
|
|
54
50
|
auditLimit?: number;
|
|
55
51
|
conversationId?: string; // scope to a single conversation
|
|
@@ -58,14 +54,14 @@ interface ExportRequestBody {
|
|
|
58
54
|
}
|
|
59
55
|
|
|
60
56
|
/**
|
|
61
|
-
* Collect audit data, daemon log files,
|
|
62
|
-
*
|
|
57
|
+
* Collect audit data, daemon log files, and a sanitized config snapshot,
|
|
58
|
+
* then package everything into a tar.gz archive.
|
|
63
59
|
*
|
|
64
60
|
* Archive layout:
|
|
65
|
-
* audit-data.json
|
|
66
|
-
* config-snapshot.json
|
|
67
|
-
* daemon-logs/<name>
|
|
68
|
-
* workspace/<
|
|
61
|
+
* audit-data.json — tool invocation records
|
|
62
|
+
* config-snapshot.json — sanitized workspace config
|
|
63
|
+
* daemon-logs/<name> — daemon log files
|
|
64
|
+
* workspace/conversations/<dir>/ — allowlisted workspace data (see ./log-export/AGENTS.md)
|
|
69
65
|
*/
|
|
70
66
|
async function handleExport(body: ExportRequestBody): Promise<Response> {
|
|
71
67
|
const staging = mkdtempSync(join(tmpdir(), "vellum-export-"));
|
|
@@ -167,9 +163,19 @@ async function handleExport(body: ExportRequestBody): Promise<Response> {
|
|
|
167
163
|
|
|
168
164
|
const logsDir = join(getDataDir(), "logs");
|
|
169
165
|
const collectedLogFiles: string[] = [];
|
|
166
|
+
const startDate = startTime ? new Date(startTime) : undefined;
|
|
167
|
+
const endDate = endTime ? new Date(endTime) : undefined;
|
|
170
168
|
if (existsSync(logsDir)) {
|
|
171
169
|
const entries = readdirSync(logsDir);
|
|
172
170
|
for (const entry of entries) {
|
|
171
|
+
// Filter dated log files by time range
|
|
172
|
+
const dateMatch = entry.match(LOG_FILE_PATTERN);
|
|
173
|
+
if (dateMatch && (startDate || endDate)) {
|
|
174
|
+
const fileDate = new Date(dateMatch[1] + "T23:59:59.999Z"); // end of day
|
|
175
|
+
const fileDateStart = new Date(dateMatch[1] + "T00:00:00.000Z");
|
|
176
|
+
if (startDate && fileDate < startDate) continue; // entire day is before range
|
|
177
|
+
if (endDate && fileDateStart > endDate) continue; // entire day is after range
|
|
178
|
+
}
|
|
173
179
|
const filePath = join(logsDir, entry);
|
|
174
180
|
try {
|
|
175
181
|
const stat = statSync(filePath);
|
|
@@ -237,6 +243,17 @@ async function handleExport(body: ExportRequestBody): Promise<Response> {
|
|
|
237
243
|
}
|
|
238
244
|
}
|
|
239
245
|
|
|
246
|
+
// --- Workspace allowlist ---
|
|
247
|
+
// Includes specific subpaths from <workspace>/ governed by the rules in
|
|
248
|
+
// ./log-export/AGENTS.md. Honors the same time + conversation filters as
|
|
249
|
+
// the rest of the export.
|
|
250
|
+
const workspaceResult = collectWorkspaceData({
|
|
251
|
+
staging,
|
|
252
|
+
conversationId: conversationId || undefined,
|
|
253
|
+
startTime: startTime || undefined,
|
|
254
|
+
endTime: endTime || undefined,
|
|
255
|
+
});
|
|
256
|
+
|
|
240
257
|
// --- Sanitized config snapshot ---
|
|
241
258
|
const configSnapshot = readSanitizedConfig();
|
|
242
259
|
if (configSnapshot) {
|
|
@@ -247,20 +264,6 @@ async function handleExport(body: ExportRequestBody): Promise<Response> {
|
|
|
247
264
|
);
|
|
248
265
|
}
|
|
249
266
|
|
|
250
|
-
// --- Workspace files (skip for conversation-scoped exports) ---
|
|
251
|
-
let workspaceFileCount = 0;
|
|
252
|
-
if (!conversationId) {
|
|
253
|
-
const workspaceFiles = collectWorkspaceFiles();
|
|
254
|
-
const workspaceDir = join(staging, "workspace");
|
|
255
|
-
mkdirSync(workspaceDir, { recursive: true });
|
|
256
|
-
for (const [relPath, content] of Object.entries(workspaceFiles)) {
|
|
257
|
-
const dest = join(workspaceDir, relPath);
|
|
258
|
-
mkdirSync(join(dest, ".."), { recursive: true });
|
|
259
|
-
writeFileSync(dest, content, "utf-8");
|
|
260
|
-
}
|
|
261
|
-
workspaceFileCount = Object.keys(workspaceFiles).length;
|
|
262
|
-
}
|
|
263
|
-
|
|
264
267
|
// --- Export manifest ---
|
|
265
268
|
const manifest = conversationId
|
|
266
269
|
? {
|
|
@@ -290,64 +293,17 @@ async function handleExport(body: ExportRequestBody): Promise<Response> {
|
|
|
290
293
|
logFileCount,
|
|
291
294
|
totalBytes,
|
|
292
295
|
hasConfig: configSnapshot !== undefined,
|
|
293
|
-
workspaceFileCount,
|
|
294
296
|
conversationId: conversationId ?? null,
|
|
297
|
+
workspaceEntries: workspaceResult.entries.length,
|
|
298
|
+
workspaceBytes: workspaceResult.totalBytes,
|
|
295
299
|
},
|
|
296
300
|
"Export collected, creating tar.gz archive",
|
|
297
301
|
);
|
|
298
302
|
|
|
299
|
-
// --- Create tar.gz archive
|
|
300
|
-
const
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
while (!archiveBytes) {
|
|
304
|
-
// Conversation-scoped exports have no workspace directory to prune —
|
|
305
|
-
// if the archive still exceeds the size limit, report a clear error.
|
|
306
|
-
if (conversationId) {
|
|
307
|
-
log.error(
|
|
308
|
-
"Conversation-scoped export exceeds archive size limit with no workspace dirs to prune",
|
|
309
|
-
);
|
|
310
|
-
return httpError(
|
|
311
|
-
"INTERNAL_ERROR",
|
|
312
|
-
"Conversation export exceeds the maximum archive size",
|
|
313
|
-
500,
|
|
314
|
-
);
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
// Find the largest top-level directory under workspace/ and remove it
|
|
318
|
-
const wsDir = join(staging, "workspace");
|
|
319
|
-
const largest = findLargestSubdirectory(wsDir);
|
|
320
|
-
if (!largest) {
|
|
321
|
-
log.error("tar command failed and no workspace dirs to prune");
|
|
322
|
-
return httpError("INTERNAL_ERROR", "Failed to create archive", 500);
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
log.warn(
|
|
326
|
-
{ dir: largest.name, bytes: largest.bytes },
|
|
327
|
-
"Archive exceeds size limit, removing largest workspace directory",
|
|
328
|
-
);
|
|
329
|
-
excludedDirs.push(
|
|
330
|
-
`workspace/${largest.name} (${formatBytes(largest.bytes)})`,
|
|
331
|
-
);
|
|
332
|
-
rmSync(join(wsDir, largest.name), { recursive: true, force: true });
|
|
333
|
-
archiveBytes = createTarGz(staging);
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
if (excludedDirs.length > 0) {
|
|
337
|
-
const errorLines = [
|
|
338
|
-
"The following workspace directories were excluded because the archive exceeded the size limit:",
|
|
339
|
-
"",
|
|
340
|
-
...excludedDirs.map((d) => ` - ${d}`),
|
|
341
|
-
"",
|
|
342
|
-
"Use the streaming export endpoint for full workspace exports.",
|
|
343
|
-
];
|
|
344
|
-
writeFileSync(join(staging, "error.log"), errorLines.join("\n"), "utf-8");
|
|
345
|
-
|
|
346
|
-
// Re-create the archive now that error.log is included
|
|
347
|
-
const withErrorLog = createTarGz(staging);
|
|
348
|
-
if (withErrorLog) {
|
|
349
|
-
archiveBytes = withErrorLog;
|
|
350
|
-
}
|
|
303
|
+
// --- Create tar.gz archive ---
|
|
304
|
+
const archiveBytes = createTarGz(staging);
|
|
305
|
+
if (!archiveBytes) {
|
|
306
|
+
return httpError("INTERNAL_ERROR", "Failed to create archive", 500);
|
|
351
307
|
}
|
|
352
308
|
|
|
353
309
|
return new Response(archiveBytes, {
|
|
@@ -371,199 +327,6 @@ async function handleExport(body: ExportRequestBody): Promise<Response> {
|
|
|
371
327
|
}
|
|
372
328
|
}
|
|
373
329
|
|
|
374
|
-
/**
|
|
375
|
-
* Attempts to create a tar.gz archive of `staging` into a Buffer.
|
|
376
|
-
* Returns the Buffer on success, or `undefined` if the archive exceeds
|
|
377
|
-
* the size limit or tar otherwise fails.
|
|
378
|
-
*/
|
|
379
|
-
function createTarGz(staging: string): ArrayBuffer | undefined {
|
|
380
|
-
const proc = spawnSync("tar", ["czf", "-", "-C", staging, "."], {
|
|
381
|
-
maxBuffer: MAX_ARCHIVE_BYTES,
|
|
382
|
-
timeout: 30_000,
|
|
383
|
-
});
|
|
384
|
-
if (proc.status !== 0) return undefined;
|
|
385
|
-
const buf = Buffer.isBuffer(proc.stdout)
|
|
386
|
-
? proc.stdout
|
|
387
|
-
: Buffer.from(proc.stdout);
|
|
388
|
-
return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
/**
|
|
392
|
-
* Returns the name and total byte size of the largest top-level subdirectory
|
|
393
|
-
* inside `dir`, or `undefined` if `dir` has no subdirectories.
|
|
394
|
-
*/
|
|
395
|
-
function findLargestSubdirectory(
|
|
396
|
-
dir: string,
|
|
397
|
-
): { name: string; bytes: number } | undefined {
|
|
398
|
-
if (!existsSync(dir)) return undefined;
|
|
399
|
-
|
|
400
|
-
let largest: { name: string; bytes: number } | undefined;
|
|
401
|
-
|
|
402
|
-
for (const entry of readdirSync(dir)) {
|
|
403
|
-
const fullPath = join(dir, entry);
|
|
404
|
-
try {
|
|
405
|
-
if (!statSync(fullPath).isDirectory()) continue;
|
|
406
|
-
} catch {
|
|
407
|
-
continue;
|
|
408
|
-
}
|
|
409
|
-
const bytes = directorySize(fullPath);
|
|
410
|
-
if (!largest || bytes > largest.bytes) {
|
|
411
|
-
largest = { name: entry, bytes };
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
return largest;
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
/** Recursively sums the byte size of all files in `dir`. */
|
|
419
|
-
function directorySize(dir: string): number {
|
|
420
|
-
let total = 0;
|
|
421
|
-
try {
|
|
422
|
-
for (const entry of readdirSync(dir)) {
|
|
423
|
-
const fullPath = join(dir, entry);
|
|
424
|
-
try {
|
|
425
|
-
const stat = statSync(fullPath);
|
|
426
|
-
if (stat.isDirectory()) {
|
|
427
|
-
total += directorySize(fullPath);
|
|
428
|
-
} else if (stat.isFile()) {
|
|
429
|
-
total += stat.size;
|
|
430
|
-
}
|
|
431
|
-
} catch {
|
|
432
|
-
// skip
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
} catch {
|
|
436
|
-
// skip
|
|
437
|
-
}
|
|
438
|
-
return total;
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
/** Formats a byte count as a human-readable string (e.g. "12.3 MB"). */
|
|
442
|
-
function formatBytes(bytes: number): string {
|
|
443
|
-
if (bytes < 1024) return `${bytes} B`;
|
|
444
|
-
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
445
|
-
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
/** Directory prefixes to skip when collecting workspace files. */
|
|
449
|
-
const WORKSPACE_SKIP_DIRS = new Set([
|
|
450
|
-
"embedding-models",
|
|
451
|
-
"data/qdrant",
|
|
452
|
-
"data/attachments",
|
|
453
|
-
"data/sounds",
|
|
454
|
-
"conversations",
|
|
455
|
-
"signals",
|
|
456
|
-
"deprecated",
|
|
457
|
-
]);
|
|
458
|
-
|
|
459
|
-
/** Files at the workspace root to skip (already covered by sanitized fields). */
|
|
460
|
-
const WORKSPACE_SKIP_ROOT_FILES = new Set(["config.json"]);
|
|
461
|
-
|
|
462
|
-
/** Maximum cumulative size for workspace file contents (10 MB). */
|
|
463
|
-
const MAX_WORKSPACE_PAYLOAD_BYTES = 10 * 1024 * 1024;
|
|
464
|
-
|
|
465
|
-
/**
|
|
466
|
-
* Recursively collects files from the workspace directory into a
|
|
467
|
-
* `Record<string, string>` map of relative path to content.
|
|
468
|
-
*
|
|
469
|
-
* - Skips `config.json` at the workspace root (already exported as a
|
|
470
|
-
* sanitized `configSnapshot`; the raw file contains secrets).
|
|
471
|
-
* - Skips symlinks to prevent reading files outside the workspace.
|
|
472
|
-
* - Skips directories in `WORKSPACE_SKIP_DIRS`.
|
|
473
|
-
* - For `.db` files, shells out to `sqlite3 <path> .dump` and stores the
|
|
474
|
-
* SQL text output with a `.sql` suffix appended to the key.
|
|
475
|
-
* - Skips binary files (detected via null-byte heuristic).
|
|
476
|
-
* - Stops collecting once `MAX_WORKSPACE_PAYLOAD_BYTES` is reached.
|
|
477
|
-
*/
|
|
478
|
-
function collectWorkspaceFiles(): Record<string, string> {
|
|
479
|
-
const wsDir = getWorkspaceDir();
|
|
480
|
-
if (!existsSync(wsDir)) return {};
|
|
481
|
-
|
|
482
|
-
const result: Record<string, string> = {};
|
|
483
|
-
let totalBytes = 0;
|
|
484
|
-
|
|
485
|
-
function walk(dir: string): void {
|
|
486
|
-
let entries: string[];
|
|
487
|
-
try {
|
|
488
|
-
entries = readdirSync(dir);
|
|
489
|
-
} catch {
|
|
490
|
-
return;
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
for (const entry of entries) {
|
|
494
|
-
const fullPath = join(dir, entry);
|
|
495
|
-
const relPath = relative(wsDir, fullPath);
|
|
496
|
-
|
|
497
|
-
// Check if this path falls under a skipped directory prefix
|
|
498
|
-
if (
|
|
499
|
-
[...WORKSPACE_SKIP_DIRS].some(
|
|
500
|
-
(prefix) => relPath === prefix || relPath.startsWith(prefix + "/"),
|
|
501
|
-
)
|
|
502
|
-
) {
|
|
503
|
-
continue;
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
// Skip root-level files that are already exported separately
|
|
507
|
-
if (dir === wsDir && WORKSPACE_SKIP_ROOT_FILES.has(entry)) {
|
|
508
|
-
continue;
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
try {
|
|
512
|
-
// Use lstatSync to avoid following symlinks
|
|
513
|
-
const stat = lstatSync(fullPath);
|
|
514
|
-
|
|
515
|
-
// Skip symlinks — they could point outside the workspace
|
|
516
|
-
if (stat.isSymbolicLink()) continue;
|
|
517
|
-
|
|
518
|
-
if (stat.isDirectory()) {
|
|
519
|
-
walk(fullPath);
|
|
520
|
-
continue;
|
|
521
|
-
}
|
|
522
|
-
if (!stat.isFile()) continue;
|
|
523
|
-
|
|
524
|
-
// SQLite DB handling: dump as SQL text, then enforce size cap
|
|
525
|
-
if (entry.endsWith(".db")) {
|
|
526
|
-
// Skip the dump entirely if the budget is already exhausted
|
|
527
|
-
if (totalBytes >= MAX_WORKSPACE_PAYLOAD_BYTES) continue;
|
|
528
|
-
try {
|
|
529
|
-
const proc = spawnSync("sqlite3", [fullPath, ".dump"], {
|
|
530
|
-
timeout: 10_000,
|
|
531
|
-
});
|
|
532
|
-
if (proc.status === 0 && proc.stdout) {
|
|
533
|
-
const output =
|
|
534
|
-
proc.stdout instanceof Buffer
|
|
535
|
-
? proc.stdout.toString("utf-8")
|
|
536
|
-
: String(proc.stdout);
|
|
537
|
-
const outputBytes = Buffer.byteLength(output, "utf-8");
|
|
538
|
-
if (totalBytes + outputBytes > MAX_WORKSPACE_PAYLOAD_BYTES)
|
|
539
|
-
continue;
|
|
540
|
-
result[relPath + ".sql"] = output;
|
|
541
|
-
totalBytes += outputBytes;
|
|
542
|
-
}
|
|
543
|
-
} catch {
|
|
544
|
-
// Skip if dump fails
|
|
545
|
-
}
|
|
546
|
-
continue;
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
// Enforce cumulative size cap for non-DB files
|
|
550
|
-
if (totalBytes + stat.size > MAX_WORKSPACE_PAYLOAD_BYTES) continue;
|
|
551
|
-
|
|
552
|
-
// Read as UTF-8 and skip binary files (null-byte heuristic)
|
|
553
|
-
const content = readFileSync(fullPath, "utf-8");
|
|
554
|
-
if (content.includes("\0")) continue;
|
|
555
|
-
result[relPath] = content;
|
|
556
|
-
totalBytes += stat.size;
|
|
557
|
-
} catch {
|
|
558
|
-
// Skip unreadable files
|
|
559
|
-
}
|
|
560
|
-
}
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
walk(wsDir);
|
|
564
|
-
return result;
|
|
565
|
-
}
|
|
566
|
-
|
|
567
330
|
/**
|
|
568
331
|
* Replaces a string value with a presence flag: "(set)" if truthy, "(empty)" otherwise.
|
|
569
332
|
*/
|
|
@@ -689,7 +452,7 @@ export function logExportRouteDefinitions(): RouteDefinition[] {
|
|
|
689
452
|
policyKey: "export",
|
|
690
453
|
summary: "Export logs and audit data",
|
|
691
454
|
description:
|
|
692
|
-
"Export audit records, assistant logs,
|
|
455
|
+
"Export audit records, assistant logs, and config as a tar.gz archive.",
|
|
693
456
|
tags: ["export"],
|
|
694
457
|
requestBody: exportRequestBody,
|
|
695
458
|
handler: async ({ req }) => {
|
|
@@ -703,7 +466,7 @@ export function logExportRouteDefinitions(): RouteDefinition[] {
|
|
|
703
466
|
policyKey: "export",
|
|
704
467
|
summary: "Export logs and audit data (alias)",
|
|
705
468
|
description:
|
|
706
|
-
"Alias for /v1/export. Export audit records, assistant logs,
|
|
469
|
+
"Alias for /v1/export. Export audit records, assistant logs, and config as a tar.gz archive.",
|
|
707
470
|
tags: ["export"],
|
|
708
471
|
requestBody: exportRequestBody,
|
|
709
472
|
handler: async ({ req }) => {
|