@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
|
@@ -3,7 +3,7 @@ import { mkdirSync, renameSync, writeFileSync } from "node:fs";
|
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
|
|
5
5
|
import { getLogger } from "../util/logger.js";
|
|
6
|
-
import {
|
|
6
|
+
import { AVATAR_IMAGE_FILENAME, getAvatarDir } from "../util/platform.js";
|
|
7
7
|
import { renderCharacterAscii } from "./ascii-renderer.js";
|
|
8
8
|
import { getCharacterComponents } from "./character-components.js";
|
|
9
9
|
import { renderCharacterPng } from "./png-renderer.js";
|
|
@@ -64,7 +64,7 @@ function writeAvatarFiles(
|
|
|
64
64
|
pngBuffer: Buffer,
|
|
65
65
|
asciiArt: string | null,
|
|
66
66
|
): boolean {
|
|
67
|
-
const pngPath = join(avatarDir,
|
|
67
|
+
const pngPath = join(avatarDir, AVATAR_IMAGE_FILENAME);
|
|
68
68
|
const pngTmp = `${pngPath}.${randomUUID()}.tmp`;
|
|
69
69
|
writeFileSync(pngTmp, pngBuffer);
|
|
70
70
|
renameSync(pngTmp, pngPath);
|
|
@@ -144,7 +144,7 @@ export function writeTraitsAndRenderAvatar(
|
|
|
144
144
|
};
|
|
145
145
|
}
|
|
146
146
|
|
|
147
|
-
const avatarDir =
|
|
147
|
+
const avatarDir = getAvatarDir();
|
|
148
148
|
const traitsPath = join(avatarDir, "character-traits.json");
|
|
149
149
|
|
|
150
150
|
try {
|
package/src/channels/types.ts
CHANGED
|
@@ -90,6 +90,11 @@ export function isInteractiveInterface(id: InterfaceId): boolean {
|
|
|
90
90
|
return INTERACTIVE_INTERFACES.has(id);
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
+
/** Whether the interface supports host proxies (bash, file, computer-use). */
|
|
94
|
+
export function supportsHostProxy(id: InterfaceId): boolean {
|
|
95
|
+
return id === "macos";
|
|
96
|
+
}
|
|
97
|
+
|
|
93
98
|
export interface TurnInterfaceContext {
|
|
94
99
|
userMessageInterface: InterfaceId;
|
|
95
100
|
assistantMessageInterface: InterfaceId;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
export interface AssistantCommandResult {
|
|
2
|
+
stdout: string;
|
|
3
|
+
stderr: string;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* CLI test utility — run an assistant CLI command via the real program,
|
|
8
|
+
* capturing stdout and stderr.
|
|
9
|
+
*
|
|
10
|
+
* Returns both stdout and stderr. For backward compatibility, the function
|
|
11
|
+
* is also callable with just a string return (use `runAssistantCommand`).
|
|
12
|
+
*/
|
|
13
|
+
export async function runAssistantCommandFull(
|
|
14
|
+
...args: string[]
|
|
15
|
+
): Promise<AssistantCommandResult> {
|
|
16
|
+
const { buildCliProgram } = await import("../program.js");
|
|
17
|
+
const program = await buildCliProgram();
|
|
18
|
+
program.exitOverride();
|
|
19
|
+
|
|
20
|
+
const stderrChunks: string[] = [];
|
|
21
|
+
program.configureOutput({
|
|
22
|
+
writeErr: (str: string) => stderrChunks.push(str),
|
|
23
|
+
writeOut: () => {},
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const stdoutChunks: string[] = [];
|
|
27
|
+
const originalWrite = process.stdout.write;
|
|
28
|
+
process.stdout.write = ((chunk: string | Uint8Array) => {
|
|
29
|
+
stdoutChunks.push(
|
|
30
|
+
typeof chunk === "string" ? chunk : new TextDecoder().decode(chunk),
|
|
31
|
+
);
|
|
32
|
+
return true;
|
|
33
|
+
}) as typeof process.stdout.write;
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
await program.parseAsync(["node", "assistant", ...args]);
|
|
37
|
+
} catch {
|
|
38
|
+
/* commander exit override throws */
|
|
39
|
+
} finally {
|
|
40
|
+
process.stdout.write = originalWrite;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return {
|
|
44
|
+
stdout: stdoutChunks.join(""),
|
|
45
|
+
stderr: stderrChunks.join(""),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* CLI test utility — run an assistant CLI command via the real program,
|
|
51
|
+
* capturing stdout (backward-compatible wrapper).
|
|
52
|
+
*/
|
|
53
|
+
export async function runAssistantCommand(...args: string[]): Promise<string> {
|
|
54
|
+
const result = await runAssistantCommandFull(...args);
|
|
55
|
+
return result.stdout;
|
|
56
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { describe, expect, it } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import { runAssistantCommandFull } from "./run-assistant-command.js";
|
|
4
|
+
|
|
5
|
+
describe("unknown command handling", () => {
|
|
6
|
+
it("reports an error for an unknown subcommand", async () => {
|
|
7
|
+
const { stderr } = await runAssistantCommandFull("invalid");
|
|
8
|
+
|
|
9
|
+
expect(stderr).toContain("unknown command 'invalid'");
|
|
10
|
+
expect(stderr).toContain("Run 'assistant --help'");
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it("reports an error for an unknown subcommand with extra arguments", async () => {
|
|
14
|
+
const { stderr } = await runAssistantCommandFull("invalid", "something");
|
|
15
|
+
|
|
16
|
+
expect(stderr).toContain("unknown command 'invalid'");
|
|
17
|
+
expect(stderr).toContain("Run 'assistant --help'");
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("suggests a similar command when the input is close", async () => {
|
|
21
|
+
const { stderr } = await runAssistantCommandFull("confg");
|
|
22
|
+
|
|
23
|
+
expect(stderr).toContain("unknown command 'confg'");
|
|
24
|
+
expect(stderr).toContain("Did you mean 'config'");
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it("does not suggest a command when the input is too far off", async () => {
|
|
28
|
+
const { stderr } = await runAssistantCommandFull("xyzzy");
|
|
29
|
+
|
|
30
|
+
expect(stderr).toContain("unknown command 'xyzzy'");
|
|
31
|
+
expect(stderr).not.toContain("Did you mean");
|
|
32
|
+
});
|
|
33
|
+
});
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import { existsSync, readFileSync, rmSync } from "node:fs";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
getMockFetchCalls,
|
|
8
|
+
mockFetch,
|
|
9
|
+
resetMockFetch,
|
|
10
|
+
} from "../../../__tests__/mock-fetch.js";
|
|
11
|
+
import { _setOverridesForTesting } from "../../../config/assistant-feature-flags.js";
|
|
12
|
+
import { setPlatformAssistantId } from "../../../config/env.js";
|
|
13
|
+
import { credentialKey } from "../../../security/credential-key.js";
|
|
14
|
+
import {
|
|
15
|
+
_resetBackend,
|
|
16
|
+
deleteSecureKeyAsync,
|
|
17
|
+
setSecureKeyAsync,
|
|
18
|
+
} from "../../../security/secure-keys.js";
|
|
19
|
+
import { runAssistantCommand } from "../../__tests__/run-assistant-command.js";
|
|
20
|
+
|
|
21
|
+
const ASSISTANT_ID = "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee";
|
|
22
|
+
const MESSAGE_ID = "msg-001";
|
|
23
|
+
const API_KEY_CREDENTIAL = credentialKey("vellum", "assistant_api_key");
|
|
24
|
+
|
|
25
|
+
const SAMPLE_MESSAGE = {
|
|
26
|
+
id: MESSAGE_ID,
|
|
27
|
+
direction: "inbound",
|
|
28
|
+
from_address: "user@example.com",
|
|
29
|
+
to_addresses: ["mybot@vellum.me"],
|
|
30
|
+
subject: "Hello bot",
|
|
31
|
+
body_text: "Hi, this is a test message.",
|
|
32
|
+
body_html: "<p>Hi, this is a <b>test</b> message.</p>",
|
|
33
|
+
in_reply_to: "",
|
|
34
|
+
references: [],
|
|
35
|
+
created_at: "2026-04-05T12:00:00Z",
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
function mockDetailSuccess(msg = SAMPLE_MESSAGE, status = 200): void {
|
|
39
|
+
mockFetch(`/emails/${msg.id}/`, {}, { body: msg, status });
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
let savedCesUrl: string | undefined;
|
|
43
|
+
let savedContainerized: string | undefined;
|
|
44
|
+
let tmpOutputPath: string;
|
|
45
|
+
|
|
46
|
+
beforeEach(async () => {
|
|
47
|
+
process.exitCode = 0;
|
|
48
|
+
|
|
49
|
+
savedCesUrl = process.env.CES_CREDENTIAL_URL;
|
|
50
|
+
savedContainerized = process.env.IS_CONTAINERIZED;
|
|
51
|
+
delete process.env.CES_CREDENTIAL_URL;
|
|
52
|
+
delete process.env.IS_CONTAINERIZED;
|
|
53
|
+
|
|
54
|
+
_resetBackend();
|
|
55
|
+
resetMockFetch();
|
|
56
|
+
_setOverridesForTesting({ "email-channel": true });
|
|
57
|
+
setPlatformAssistantId(ASSISTANT_ID);
|
|
58
|
+
await setSecureKeyAsync(API_KEY_CREDENTIAL, "test-api-key");
|
|
59
|
+
|
|
60
|
+
tmpOutputPath = join(tmpdir(), `email-download-test-${Date.now()}.txt`);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
afterEach(() => {
|
|
64
|
+
resetMockFetch();
|
|
65
|
+
_setOverridesForTesting({});
|
|
66
|
+
setPlatformAssistantId(undefined);
|
|
67
|
+
_resetBackend();
|
|
68
|
+
|
|
69
|
+
if (savedCesUrl !== undefined) process.env.CES_CREDENTIAL_URL = savedCesUrl;
|
|
70
|
+
else delete process.env.CES_CREDENTIAL_URL;
|
|
71
|
+
if (savedContainerized !== undefined)
|
|
72
|
+
process.env.IS_CONTAINERIZED = savedContainerized;
|
|
73
|
+
else delete process.env.IS_CONTAINERIZED;
|
|
74
|
+
|
|
75
|
+
if (existsSync(tmpOutputPath)) rmSync(tmpOutputPath);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
describe("assistant email download", () => {
|
|
79
|
+
test("default format shows headers and plain-text body", async () => {
|
|
80
|
+
mockDetailSuccess();
|
|
81
|
+
|
|
82
|
+
const output = await runAssistantCommand("email", "download", MESSAGE_ID);
|
|
83
|
+
|
|
84
|
+
expect(output).toContain("From: user@example.com");
|
|
85
|
+
expect(output).toContain("To: mybot@vellum.me");
|
|
86
|
+
expect(output).toContain("Subject: Hello bot");
|
|
87
|
+
expect(output).toContain("Hi, this is a test message.");
|
|
88
|
+
expect(process.exitCode).toBe(0);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
test("--format json returns full message object", async () => {
|
|
92
|
+
mockDetailSuccess();
|
|
93
|
+
|
|
94
|
+
const output = await runAssistantCommand(
|
|
95
|
+
"email",
|
|
96
|
+
"download",
|
|
97
|
+
MESSAGE_ID,
|
|
98
|
+
"--format",
|
|
99
|
+
"json",
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
const parsed = JSON.parse(output.trim());
|
|
103
|
+
expect(parsed.id).toBe(MESSAGE_ID);
|
|
104
|
+
expect(parsed.body_text).toBe("Hi, this is a test message.");
|
|
105
|
+
expect(parsed.body_html).toContain("<b>test</b>");
|
|
106
|
+
expect(process.exitCode).toBe(0);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
test("--json flag also returns JSON", async () => {
|
|
110
|
+
mockDetailSuccess();
|
|
111
|
+
|
|
112
|
+
const output = await runAssistantCommand(
|
|
113
|
+
"email",
|
|
114
|
+
"--json",
|
|
115
|
+
"download",
|
|
116
|
+
MESSAGE_ID,
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
const parsed = JSON.parse(output.trim());
|
|
120
|
+
expect(parsed.id).toBe(MESSAGE_ID);
|
|
121
|
+
expect(process.exitCode).toBe(0);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
test("--format html returns HTML body", async () => {
|
|
125
|
+
mockDetailSuccess();
|
|
126
|
+
|
|
127
|
+
const output = await runAssistantCommand(
|
|
128
|
+
"email",
|
|
129
|
+
"download",
|
|
130
|
+
MESSAGE_ID,
|
|
131
|
+
"--format",
|
|
132
|
+
"html",
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
expect(output).toContain("<p>Hi, this is a <b>test</b> message.</p>");
|
|
136
|
+
expect(process.exitCode).toBe(0);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
test("--format html with no HTML body returns error", async () => {
|
|
140
|
+
mockDetailSuccess({ ...SAMPLE_MESSAGE, body_html: "" });
|
|
141
|
+
|
|
142
|
+
const output = await runAssistantCommand(
|
|
143
|
+
"email",
|
|
144
|
+
"download",
|
|
145
|
+
MESSAGE_ID,
|
|
146
|
+
"--format",
|
|
147
|
+
"html",
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
expect(process.exitCode).toBe(1);
|
|
151
|
+
// stderr output from log.error, but stdout may be empty — check exitCode
|
|
152
|
+
expect(output).not.toContain("<p>");
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
test("--output writes to file", async () => {
|
|
156
|
+
mockDetailSuccess();
|
|
157
|
+
|
|
158
|
+
await runAssistantCommand(
|
|
159
|
+
"email",
|
|
160
|
+
"download",
|
|
161
|
+
MESSAGE_ID,
|
|
162
|
+
"-o",
|
|
163
|
+
tmpOutputPath,
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
expect(process.exitCode).toBe(0);
|
|
167
|
+
expect(existsSync(tmpOutputPath)).toBe(true);
|
|
168
|
+
const content = readFileSync(tmpOutputPath, "utf-8");
|
|
169
|
+
expect(content).toContain("From: user@example.com");
|
|
170
|
+
expect(content).toContain("Hi, this is a test message.");
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
test("calls correct URL", async () => {
|
|
174
|
+
mockDetailSuccess();
|
|
175
|
+
|
|
176
|
+
await runAssistantCommand("email", "download", MESSAGE_ID);
|
|
177
|
+
|
|
178
|
+
const calls = getMockFetchCalls();
|
|
179
|
+
expect(calls).toHaveLength(1);
|
|
180
|
+
expect(calls[0].path).toContain(
|
|
181
|
+
`/v1/assistants/${ASSISTANT_ID}/emails/${MESSAGE_ID}/`,
|
|
182
|
+
);
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
test("404 returns error", async () => {
|
|
186
|
+
mockFetch(
|
|
187
|
+
`/emails/${MESSAGE_ID}/`,
|
|
188
|
+
{},
|
|
189
|
+
{ body: { detail: "Not found." }, status: 404 },
|
|
190
|
+
);
|
|
191
|
+
|
|
192
|
+
const output = await runAssistantCommand(
|
|
193
|
+
"email",
|
|
194
|
+
"--json",
|
|
195
|
+
"download",
|
|
196
|
+
MESSAGE_ID,
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
expect(process.exitCode).toBe(1);
|
|
200
|
+
const parsed = JSON.parse(output.trim());
|
|
201
|
+
expect(parsed.error).toContain("Not found");
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
test("missing platform credentials returns error", async () => {
|
|
205
|
+
await deleteSecureKeyAsync(API_KEY_CREDENTIAL);
|
|
206
|
+
|
|
207
|
+
const output = await runAssistantCommand(
|
|
208
|
+
"email",
|
|
209
|
+
"--json",
|
|
210
|
+
"download",
|
|
211
|
+
MESSAGE_ID,
|
|
212
|
+
);
|
|
213
|
+
|
|
214
|
+
expect(process.exitCode).toBe(1);
|
|
215
|
+
const parsed = JSON.parse(output.trim());
|
|
216
|
+
expect(parsed.error).toContain("Platform credentials not configured");
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
test("missing assistant ID returns error", async () => {
|
|
220
|
+
setPlatformAssistantId("");
|
|
221
|
+
|
|
222
|
+
const output = await runAssistantCommand(
|
|
223
|
+
"email",
|
|
224
|
+
"--json",
|
|
225
|
+
"download",
|
|
226
|
+
MESSAGE_ID,
|
|
227
|
+
);
|
|
228
|
+
|
|
229
|
+
expect(process.exitCode).toBe(1);
|
|
230
|
+
const parsed = JSON.parse(output.trim());
|
|
231
|
+
expect(parsed.error).toContain("Assistant ID");
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
test("in_reply_to header shown when present", async () => {
|
|
235
|
+
mockDetailSuccess({
|
|
236
|
+
...SAMPLE_MESSAGE,
|
|
237
|
+
in_reply_to: "<orig@mail.gmail.com>",
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
const output = await runAssistantCommand("email", "download", MESSAGE_ID);
|
|
241
|
+
|
|
242
|
+
expect(output).toContain("In-Reply-To: <orig@mail.gmail.com>");
|
|
243
|
+
expect(process.exitCode).toBe(0);
|
|
244
|
+
});
|
|
245
|
+
});
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
getMockFetchCalls,
|
|
5
|
+
mockFetch,
|
|
6
|
+
resetMockFetch,
|
|
7
|
+
} from "../../../__tests__/mock-fetch.js";
|
|
8
|
+
import { _setOverridesForTesting } from "../../../config/assistant-feature-flags.js";
|
|
9
|
+
import { setPlatformAssistantId } from "../../../config/env.js";
|
|
10
|
+
import { credentialKey } from "../../../security/credential-key.js";
|
|
11
|
+
import {
|
|
12
|
+
_resetBackend,
|
|
13
|
+
deleteSecureKeyAsync,
|
|
14
|
+
setSecureKeyAsync,
|
|
15
|
+
} from "../../../security/secure-keys.js";
|
|
16
|
+
import { runAssistantCommand } from "../../__tests__/run-assistant-command.js";
|
|
17
|
+
|
|
18
|
+
const ASSISTANT_ID = "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee";
|
|
19
|
+
const API_KEY_CREDENTIAL = credentialKey("vellum", "assistant_api_key");
|
|
20
|
+
|
|
21
|
+
const SAMPLE_MESSAGES = [
|
|
22
|
+
{
|
|
23
|
+
id: "msg-001",
|
|
24
|
+
direction: "inbound",
|
|
25
|
+
from_address: "user@example.com",
|
|
26
|
+
to_addresses: ["mybot@vellum.me"],
|
|
27
|
+
subject: "Hello bot",
|
|
28
|
+
created_at: "2026-04-05T12:00:00Z",
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
id: "msg-002",
|
|
32
|
+
direction: "outbound",
|
|
33
|
+
from_address: "mybot@vellum.me",
|
|
34
|
+
to_addresses: ["user@example.com"],
|
|
35
|
+
subject: "Re: Hello bot",
|
|
36
|
+
created_at: "2026-04-05T12:01:00Z",
|
|
37
|
+
},
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
function mockListEmails(
|
|
41
|
+
results = SAMPLE_MESSAGES,
|
|
42
|
+
count?: number,
|
|
43
|
+
status = 200,
|
|
44
|
+
): void {
|
|
45
|
+
mockFetch(
|
|
46
|
+
"/emails/",
|
|
47
|
+
{},
|
|
48
|
+
{ body: { results, count: count ?? results.length }, status },
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
let savedCesUrl: string | undefined;
|
|
53
|
+
let savedContainerized: string | undefined;
|
|
54
|
+
|
|
55
|
+
beforeEach(async () => {
|
|
56
|
+
process.exitCode = 0;
|
|
57
|
+
|
|
58
|
+
savedCesUrl = process.env.CES_CREDENTIAL_URL;
|
|
59
|
+
savedContainerized = process.env.IS_CONTAINERIZED;
|
|
60
|
+
delete process.env.CES_CREDENTIAL_URL;
|
|
61
|
+
delete process.env.IS_CONTAINERIZED;
|
|
62
|
+
|
|
63
|
+
_resetBackend();
|
|
64
|
+
resetMockFetch();
|
|
65
|
+
_setOverridesForTesting({ "email-channel": true });
|
|
66
|
+
setPlatformAssistantId(ASSISTANT_ID);
|
|
67
|
+
await setSecureKeyAsync(API_KEY_CREDENTIAL, "test-api-key");
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
afterEach(() => {
|
|
71
|
+
resetMockFetch();
|
|
72
|
+
_setOverridesForTesting({});
|
|
73
|
+
setPlatformAssistantId(undefined);
|
|
74
|
+
_resetBackend();
|
|
75
|
+
|
|
76
|
+
if (savedCesUrl !== undefined) process.env.CES_CREDENTIAL_URL = savedCesUrl;
|
|
77
|
+
else delete process.env.CES_CREDENTIAL_URL;
|
|
78
|
+
if (savedContainerized !== undefined)
|
|
79
|
+
process.env.IS_CONTAINERIZED = savedContainerized;
|
|
80
|
+
else delete process.env.IS_CONTAINERIZED;
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
describe("assistant email list", () => {
|
|
84
|
+
test("--json returns messages and count", async () => {
|
|
85
|
+
mockListEmails();
|
|
86
|
+
|
|
87
|
+
const output = await runAssistantCommand("email", "--json", "list");
|
|
88
|
+
|
|
89
|
+
const parsed = JSON.parse(output.trim());
|
|
90
|
+
expect(parsed.results).toHaveLength(2);
|
|
91
|
+
expect(parsed.count).toBe(2);
|
|
92
|
+
expect(parsed.results[0].subject).toBe("Hello bot");
|
|
93
|
+
expect(parsed.results[1].direction).toBe("outbound");
|
|
94
|
+
expect(process.exitCode).toBe(0);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test("calls correct URL with no filters", async () => {
|
|
98
|
+
mockListEmails();
|
|
99
|
+
|
|
100
|
+
await runAssistantCommand("email", "--json", "list");
|
|
101
|
+
|
|
102
|
+
const calls = getMockFetchCalls();
|
|
103
|
+
expect(calls).toHaveLength(1);
|
|
104
|
+
expect(calls[0].path).toContain(`/v1/assistants/${ASSISTANT_ID}/emails/`);
|
|
105
|
+
// Default limit=20 should be in query string
|
|
106
|
+
expect(calls[0].path).toContain("limit=20");
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
test("--direction filters by direction", async () => {
|
|
110
|
+
mockListEmails();
|
|
111
|
+
|
|
112
|
+
await runAssistantCommand(
|
|
113
|
+
"email",
|
|
114
|
+
"--json",
|
|
115
|
+
"list",
|
|
116
|
+
"--direction",
|
|
117
|
+
"inbound",
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
const calls = getMockFetchCalls();
|
|
121
|
+
expect(calls[0].path).toContain("direction=inbound");
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
test("--limit sets result count", async () => {
|
|
125
|
+
mockListEmails();
|
|
126
|
+
|
|
127
|
+
await runAssistantCommand("email", "--json", "list", "--limit", "5");
|
|
128
|
+
|
|
129
|
+
const calls = getMockFetchCalls();
|
|
130
|
+
expect(calls[0].path).toContain("limit=5");
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
test("--since filters by date", async () => {
|
|
134
|
+
mockListEmails();
|
|
135
|
+
|
|
136
|
+
await runAssistantCommand(
|
|
137
|
+
"email",
|
|
138
|
+
"--json",
|
|
139
|
+
"list",
|
|
140
|
+
"--since",
|
|
141
|
+
"2026-04-01",
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
const calls = getMockFetchCalls();
|
|
145
|
+
expect(calls[0].path).toContain("since=2026-04-01");
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
test("empty results returns empty array", async () => {
|
|
149
|
+
mockListEmails([], 0);
|
|
150
|
+
|
|
151
|
+
const output = await runAssistantCommand("email", "--json", "list");
|
|
152
|
+
|
|
153
|
+
const parsed = JSON.parse(output.trim());
|
|
154
|
+
expect(parsed.results).toHaveLength(0);
|
|
155
|
+
expect(parsed.count).toBe(0);
|
|
156
|
+
expect(process.exitCode).toBe(0);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
test("endpoint failure returns error", async () => {
|
|
160
|
+
mockFetch(
|
|
161
|
+
"/emails/",
|
|
162
|
+
{},
|
|
163
|
+
{ body: { detail: "Internal server error" }, status: 500 },
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
const output = await runAssistantCommand("email", "--json", "list");
|
|
167
|
+
|
|
168
|
+
expect(process.exitCode).toBe(1);
|
|
169
|
+
const parsed = JSON.parse(output.trim());
|
|
170
|
+
expect(parsed.error).toContain("Internal server error");
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
test("missing platform credentials returns error", async () => {
|
|
174
|
+
await deleteSecureKeyAsync(API_KEY_CREDENTIAL);
|
|
175
|
+
|
|
176
|
+
const output = await runAssistantCommand("email", "--json", "list");
|
|
177
|
+
|
|
178
|
+
expect(process.exitCode).toBe(1);
|
|
179
|
+
const parsed = JSON.parse(output.trim());
|
|
180
|
+
expect(parsed.error).toContain("Platform credentials not configured");
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
test("missing assistant ID returns error", async () => {
|
|
184
|
+
setPlatformAssistantId("");
|
|
185
|
+
|
|
186
|
+
const output = await runAssistantCommand("email", "--json", "list");
|
|
187
|
+
|
|
188
|
+
expect(process.exitCode).toBe(1);
|
|
189
|
+
const parsed = JSON.parse(output.trim());
|
|
190
|
+
expect(parsed.error).toContain("Assistant ID");
|
|
191
|
+
});
|
|
192
|
+
});
|