@vellumai/assistant 0.5.16 → 0.6.0
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 +1 -1
- package/Dockerfile +0 -3
- package/knip.json +2 -1
- package/openapi.yaml +660 -80
- package/package.json +1 -1
- package/src/__tests__/actor-token-service.test.ts +68 -0
- package/src/__tests__/agent-loop.test.ts +0 -32
- package/src/__tests__/always-loaded-tools-guard.test.ts +2 -2
- package/src/__tests__/anthropic-provider.test.ts +57 -3
- package/src/__tests__/app-compiler.test.ts +120 -0
- package/src/__tests__/assistant-feature-flags-integration.test.ts +2 -2
- package/src/__tests__/call-conversation-messages.test.ts +2 -6
- package/src/__tests__/call-domain.test.ts +2 -6
- package/src/__tests__/call-pointer-messages.test.ts +2 -14
- package/src/__tests__/call-recovery.test.ts +2 -6
- package/src/__tests__/call-routes-http.test.ts +2 -6
- package/src/__tests__/call-store.test.ts +2 -6
- package/src/__tests__/cancel-resolves-conversation-key.test.ts +2 -6
- package/src/__tests__/canonical-guardian-store.test.ts +2 -6
- package/src/__tests__/channel-delivery-store.test.ts +2 -6
- package/src/__tests__/channel-retry-sweep.test.ts +2 -6
- package/src/__tests__/checker.test.ts +25 -3
- package/src/__tests__/clawhub.test.ts +54 -24
- package/src/__tests__/cli-command-risk-guard.test.ts +14 -0
- package/src/__tests__/cli-memory.test.ts +74 -69
- package/src/__tests__/config-schema.test.ts +1 -1
- package/src/__tests__/config-set-platform-guard.test.ts +302 -0
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +2 -6
- package/src/__tests__/contacts-tools.test.ts +31 -0
- package/src/__tests__/context-overflow-reducer.test.ts +86 -0
- package/src/__tests__/context-token-estimator.test.ts +175 -10
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +9 -0
- package/src/__tests__/conversation-agent-loop.test.ts +9 -0
- package/src/__tests__/conversation-attachments.test.ts +2 -6
- package/src/__tests__/conversation-attention-store.test.ts +2 -6
- package/src/__tests__/conversation-clear-safety.test.ts +2 -6
- package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +4 -10
- package/src/__tests__/conversation-disk-view-integration.test.ts +2 -6
- package/src/__tests__/conversation-disk-view.test.ts +2 -6
- package/src/__tests__/conversation-error.test.ts +33 -2
- package/src/__tests__/conversation-fork-crud.test.ts +2 -6
- package/src/__tests__/conversation-history-web-search.test.ts +5 -0
- package/src/__tests__/conversation-load-history-repair.test.ts +5 -1
- package/src/__tests__/conversation-media-retry.test.ts +91 -0
- package/src/__tests__/conversation-starter-routes.test.ts +20 -11
- package/src/__tests__/conversation-store.test.ts +2 -6
- package/src/__tests__/conversation-usage.test.ts +2 -6
- package/src/__tests__/conversation-wipe.test.ts +11 -408
- package/src/__tests__/credential-execution-feature-gates.test.ts +3 -3
- package/src/__tests__/credential-execution-shell-lockdown.test.ts +2 -2
- package/src/__tests__/credential-security-e2e.test.ts +2 -0
- package/src/__tests__/followup-tools.test.ts +2 -6
- package/src/__tests__/graph-extraction-event-date.test.ts +186 -0
- package/src/__tests__/guardian-action-conversation-turn.test.ts +2 -6
- package/src/__tests__/guardian-action-followup-executor.test.ts +2 -6
- package/src/__tests__/guardian-action-followup-store.test.ts +2 -6
- package/src/__tests__/guardian-action-grant-mint-consume.test.ts +2 -6
- package/src/__tests__/guardian-action-late-reply.test.ts +2 -6
- package/src/__tests__/guardian-action-store.test.ts +2 -6
- package/src/__tests__/guardian-binding-drift-heal.test.ts +2 -6
- package/src/__tests__/guardian-decision-primitive-canonical.test.ts +8 -8
- package/src/__tests__/guardian-dispatch.test.ts +2 -6
- package/src/__tests__/guardian-grant-minting.test.ts +2 -14
- package/src/__tests__/guardian-principal-id-roundtrip.test.ts +2 -6
- package/src/__tests__/guardian-routing-invariants.test.ts +192 -6
- package/src/__tests__/guardian-routing-state.test.ts +2 -6
- package/src/__tests__/guardian-verification-voice-binding.test.ts +2 -6
- package/src/__tests__/inbound-invite-redemption.test.ts +2 -6
- package/src/__tests__/injection-block.test.ts +154 -0
- package/src/__tests__/install-meta.test.ts +506 -0
- package/src/__tests__/install-skill-routing.test.ts +292 -0
- package/src/__tests__/invite-redemption-service.test.ts +2 -6
- package/src/__tests__/invite-routes-http.test.ts +2 -6
- package/src/__tests__/jobs-store-qdrant-breaker.test.ts +2 -14
- package/src/__tests__/list-messages-attachments.test.ts +2 -6
- package/src/__tests__/llm-context-route-provider.test.ts +2 -6
- package/src/__tests__/llm-request-log-turn-query.test.ts +2 -6
- package/src/__tests__/llm-usage-store.test.ts +2 -6
- package/src/__tests__/log-export-workspace.test.ts +2 -6
- package/src/__tests__/managed-store.test.ts +38 -11
- package/src/__tests__/memory-jobs-worker-backoff.test.ts +2 -8
- package/src/__tests__/memory-recall-log-store.test.ts +2 -6
- package/src/__tests__/memory-upsert-concurrency.test.ts +4 -112
- package/src/__tests__/non-member-access-request.test.ts +2 -6
- package/src/__tests__/notification-guardian-path.test.ts +2 -6
- package/src/__tests__/oauth-cli.test.ts +364 -2
- package/src/__tests__/oauth2-gateway-transport.test.ts +18 -3
- package/src/__tests__/outlook-attachments.test.ts +301 -0
- package/src/__tests__/outlook-automation-tools.test.ts +425 -0
- package/src/__tests__/outlook-categories.test.ts +212 -0
- package/src/__tests__/outlook-client-automation.test.ts +246 -0
- package/src/__tests__/outlook-compose-tools.test.ts +325 -0
- package/src/__tests__/outlook-declutter-tools.test.ts +585 -0
- package/src/__tests__/outlook-email-watcher.test.ts +322 -0
- package/src/__tests__/outlook-follow-up.test.ts +196 -0
- package/src/__tests__/outlook-messaging-provider.test.ts +498 -3
- package/src/__tests__/outlook-trash.test.ts +77 -0
- package/src/__tests__/outlook-unsubscribe.test.ts +250 -0
- package/src/__tests__/platform-callback-registration.test.ts +4 -4
- package/src/__tests__/playbook-execution.test.ts +76 -80
- package/src/__tests__/playbook-tools.test.ts +5 -7
- package/src/__tests__/provider-error-scenarios.test.ts +21 -0
- package/src/__tests__/rebuild-index-graph-nodes.test.ts +273 -0
- package/src/__tests__/registry.test.ts +2 -2
- package/src/__tests__/require-fresh-approval.test.ts +64 -2
- package/src/__tests__/runtime-events-sse-parity.test.ts +2 -6
- package/src/__tests__/runtime-events-sse.test.ts +2 -6
- package/src/__tests__/schedule-store.test.ts +2 -6
- package/src/__tests__/schedule-tools.test.ts +2 -6
- package/src/__tests__/scheduler-recurrence.test.ts +1 -5
- package/src/__tests__/scoped-approval-grants.test.ts +2 -6
- package/src/__tests__/scoped-grant-security-matrix.test.ts +2 -6
- package/src/__tests__/search-skills-unified.test.ts +421 -0
- package/src/__tests__/secret-onetime-send.test.ts +2 -0
- package/src/__tests__/send-endpoint-busy.test.ts +2 -6
- package/src/__tests__/sequence-store.test.ts +2 -6
- package/src/__tests__/server-history-render.test.ts +2 -6
- package/src/__tests__/skill-feature-flags-integration.test.ts +38 -31
- package/src/__tests__/skill-feature-flags.test.ts +6 -6
- package/src/__tests__/skill-load-feature-flag.test.ts +11 -11
- package/src/__tests__/skill-memory.test.ts +140 -98
- package/src/__tests__/skills-uninstall.test.ts +2 -2
- package/src/__tests__/skills.test.ts +1 -1
- package/src/__tests__/slack-inbound-verification.test.ts +2 -6
- package/src/__tests__/task-compiler.test.ts +2 -6
- package/src/__tests__/task-management-tools.test.ts +2 -6
- package/src/__tests__/task-memory-cleanup.test.ts +173 -229
- package/src/__tests__/task-runner.test.ts +2 -6
- package/src/__tests__/task-scheduler.test.ts +2 -6
- package/src/__tests__/test-preload.ts +3 -0
- package/src/__tests__/tool-approval-handler.test.ts +2 -6
- package/src/__tests__/tool-grant-request-escalation.test.ts +2 -6
- package/src/__tests__/tool-side-effects-slack-dm.test.ts +276 -0
- package/src/__tests__/trust-store.test.ts +1 -1
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +2 -6
- package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +2 -6
- package/src/__tests__/trusted-contact-multichannel.test.ts +2 -6
- package/src/__tests__/trusted-contact-verification.test.ts +2 -6
- package/src/__tests__/turn-boundary-resolution.test.ts +2 -6
- package/src/__tests__/usage-cache-backfill-migration.test.ts +1 -6
- package/src/__tests__/usage-routes.test.ts +2 -6
- package/src/__tests__/verification-control-plane-policy.test.ts +0 -2
- package/src/__tests__/voice-invite-redemption.test.ts +2 -6
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +2 -6
- package/src/__tests__/voice-session-bridge.test.ts +2 -6
- package/src/__tests__/volume-security-guard.test.ts +2 -0
- package/src/__tests__/workspace-lifecycle.test.ts +29 -1
- package/src/__tests__/workspace-migration-009-backfill-conversation-disk-view.test.ts +2 -6
- package/src/__tests__/workspace-migration-013-repair-conversation-disk-view.test.ts +2 -6
- package/src/__tests__/workspace-migration-026-backfill-install-meta.test.ts +558 -0
- package/src/__tests__/workspace-policy.test.ts +1 -1
- package/src/agent/attachments.ts +7 -2
- package/src/agent/image-optimize.ts +165 -0
- package/src/agent/loop.ts +1 -15
- package/src/bundler/app-compiler.ts +179 -2
- package/src/bundler/package-resolver.ts +3 -5
- package/src/cli/__tests__/notifications.test.ts +1 -2
- package/src/cli/cli-memory.ts +67 -64
- package/src/cli/commands/avatar.ts +3 -3
- package/src/cli/commands/config.ts +26 -13
- package/src/cli/commands/doctor.ts +2 -2
- package/src/cli/commands/memory.ts +41 -55
- package/src/cli/commands/oauth/__tests__/connect.test.ts +2 -2
- package/src/cli/commands/oauth/__tests__/disconnect.test.ts +2 -2
- package/src/cli/commands/oauth/__tests__/mode.test.ts +8 -1
- package/src/cli/commands/oauth/__tests__/status.test.ts +2 -2
- package/src/cli/commands/oauth/connect.ts +11 -6
- package/src/cli/commands/oauth/mode.ts +7 -0
- package/src/cli/commands/oauth/shared.ts +39 -3
- 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 +5 -5
- package/src/cli/commands/platform/index.ts +16 -16
- package/src/cli/commands/skills.ts +88 -16
- package/src/cli/commands/trust.ts +2 -2
- package/src/cli/lib/daemon-credential-client.ts +2 -3
- package/src/config/bundled-skills/acp/TOOLS.json +1 -1
- package/src/config/bundled-skills/contacts/SKILL.md +0 -1
- package/src/config/bundled-skills/contacts/TOOLS.json +0 -8
- package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +0 -4
- package/src/config/bundled-skills/gmail/SKILL.md +2 -10
- package/src/config/bundled-skills/google-calendar/SKILL.md +1 -9
- package/src/config/bundled-skills/messaging/SKILL.md +10 -18
- package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +40 -33
- package/src/config/bundled-skills/outlook/SKILL.md +189 -0
- package/src/config/bundled-skills/outlook/TOOLS.json +530 -0
- package/src/config/bundled-skills/outlook/tools/outlook-attachments.ts +85 -0
- package/src/config/bundled-skills/outlook/tools/outlook-categories.ts +77 -0
- package/src/config/bundled-skills/outlook/tools/outlook-draft.ts +84 -0
- package/src/config/bundled-skills/outlook/tools/outlook-follow-up.ts +94 -0
- package/src/config/bundled-skills/outlook/tools/outlook-forward.ts +49 -0
- package/src/config/bundled-skills/outlook/tools/outlook-outreach-scan.ts +237 -0
- package/src/config/bundled-skills/outlook/tools/outlook-rules.ts +161 -0
- package/src/config/bundled-skills/outlook/tools/outlook-send-draft.ts +32 -0
- package/src/config/bundled-skills/outlook/tools/outlook-sender-digest.ts +272 -0
- package/src/config/bundled-skills/outlook/tools/outlook-trash.ts +29 -0
- package/src/config/bundled-skills/outlook/tools/outlook-unsubscribe.ts +129 -0
- package/src/config/bundled-skills/outlook/tools/outlook-vacation.ts +87 -0
- package/src/config/bundled-skills/outlook/tools/shared.ts +20 -0
- package/src/config/bundled-skills/outlook-calendar/SKILL.md +51 -0
- package/src/config/bundled-skills/outlook-calendar/TOOLS.json +221 -0
- package/src/config/bundled-skills/outlook-calendar/calendar-client.ts +252 -0
- package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-check-availability.ts +53 -0
- package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-create-event.ts +74 -0
- package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-get-event.ts +18 -0
- package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-list-events.ts +46 -0
- package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-rsvp.ts +36 -0
- package/src/config/bundled-skills/outlook-calendar/tools/shared.ts +17 -0
- package/src/config/bundled-skills/outlook-calendar/types.ts +120 -0
- package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +47 -40
- package/src/config/bundled-skills/playbooks/tools/playbook-delete.ts +16 -29
- package/src/config/bundled-skills/playbooks/tools/playbook-list.ts +16 -18
- package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +39 -47
- package/src/config/bundled-skills/slack/SKILL.md +1 -7
- package/src/config/bundled-tool-registry.ts +56 -4
- package/src/config/env-registry.ts +15 -8
- package/src/config/feature-flag-registry.json +21 -124
- package/src/config/schemas/platform.ts +8 -0
- package/src/config/schemas/timeouts.ts +1 -1
- package/src/config/skills.ts +18 -7
- package/src/context/token-estimator.ts +25 -18
- package/src/context/window-manager.ts +6 -2
- package/src/credential-execution/process-manager.ts +3 -1
- package/src/daemon/context-overflow-reducer.ts +46 -2
- package/src/daemon/conversation-agent-loop-handlers.ts +123 -82
- package/src/daemon/conversation-agent-loop.ts +96 -61
- package/src/daemon/conversation-error.ts +31 -8
- package/src/daemon/conversation-lifecycle.ts +33 -0
- package/src/daemon/conversation-media-retry.ts +85 -7
- package/src/daemon/conversation-notifiers.ts +4 -1
- package/src/daemon/conversation-runtime-assembly.ts +5 -0
- package/src/daemon/conversation.ts +41 -2
- package/src/daemon/daemon-control.ts +8 -2
- package/src/daemon/handlers/shared.ts +22 -12
- package/src/daemon/handlers/skills.ts +416 -202
- package/src/daemon/lifecycle.ts +40 -1
- package/src/daemon/main.ts +5 -1
- package/src/daemon/message-types/conversations.ts +4 -1
- package/src/daemon/message-types/messages.ts +3 -1
- package/src/daemon/message-types/skills.ts +97 -36
- package/src/daemon/providers-setup.ts +5 -0
- package/src/daemon/server.ts +11 -2
- package/src/daemon/tool-side-effects.ts +27 -5
- package/src/heartbeat/heartbeat-service.ts +1 -0
- package/src/hooks/cli.ts +2 -2
- package/src/hooks/runner.ts +15 -38
- package/src/inbound/platform-callback-registration.ts +14 -14
- package/src/memory/admin.ts +11 -45
- package/src/memory/conversation-bootstrap.ts +2 -0
- package/src/memory/conversation-crud.ts +242 -348
- package/src/memory/conversation-group-migration.ts +157 -0
- package/src/memory/conversation-queries.ts +4 -2
- package/src/memory/db-init.ts +30 -3
- package/src/memory/embed.ts +73 -0
- package/src/memory/embedding-backend.ts +8 -14
- package/src/memory/embedding-runtime-manager.ts +12 -114
- package/src/memory/fingerprint.ts +2 -2
- package/src/memory/graph/bootstrap.ts +512 -0
- package/src/memory/graph/capability-seed.ts +297 -0
- package/src/memory/graph/consolidation.ts +691 -0
- package/src/memory/graph/conversation-graph-memory.ts +630 -0
- package/src/memory/graph/decay.test.ts +208 -0
- package/src/memory/graph/decay.ts +195 -0
- package/src/memory/graph/extraction-job.ts +69 -0
- package/src/memory/graph/extraction.test.ts +936 -0
- package/src/memory/graph/extraction.ts +1254 -0
- package/src/memory/graph/graph-search.ts +266 -0
- package/src/memory/graph/image-ref-utils.ts +29 -0
- package/src/memory/graph/injection.test.ts +513 -0
- package/src/memory/graph/injection.ts +439 -0
- package/src/memory/graph/inspect.ts +534 -0
- package/src/memory/graph/narrative.ts +267 -0
- package/src/memory/graph/pattern-scan.ts +269 -0
- package/src/memory/graph/retriever.ts +1008 -0
- package/src/memory/graph/scoring.test.ts +548 -0
- package/src/memory/graph/scoring.ts +232 -0
- package/src/memory/graph/serendipity.ts +65 -0
- package/src/memory/graph/store.test.ts +1050 -0
- package/src/memory/graph/store.ts +699 -0
- package/src/memory/graph/tool-handlers.ts +426 -0
- package/src/memory/graph/tools.ts +141 -0
- package/src/memory/graph/triggers.test.ts +487 -0
- package/src/memory/graph/triggers.ts +223 -0
- package/src/memory/graph/types.ts +271 -0
- package/src/memory/group-crud.ts +191 -0
- package/src/memory/indexer.ts +37 -19
- package/src/memory/job-handlers/cleanup.ts +0 -53
- package/src/memory/job-handlers/conversation-starters.ts +91 -53
- package/src/memory/job-handlers/embedding.ts +5 -31
- package/src/memory/job-handlers/index-maintenance.ts +23 -11
- package/src/memory/job-handlers/summarization.ts +32 -17
- package/src/memory/job-utils.ts +1 -1
- package/src/memory/jobs-store.ts +50 -70
- package/src/memory/jobs-worker.ts +147 -112
- package/src/memory/message-content.ts +1 -0
- package/src/memory/migrations/202-memory-graph-tables.ts +130 -0
- package/src/memory/migrations/203-drop-memory-items-tables.ts +23 -0
- package/src/memory/migrations/204-rename-memory-graph-type-values.ts +46 -0
- package/src/memory/migrations/205-memory-graph-image-refs.ts +11 -0
- package/src/memory/migrations/index.ts +4 -0
- package/src/memory/migrations/registry.ts +8 -0
- package/src/memory/qdrant-client.ts +44 -17
- package/src/memory/schema/index.ts +1 -0
- package/src/memory/schema/memory-graph.ts +139 -0
- package/src/memory/search/semantic.ts +47 -91
- package/src/memory/task-memory-cleanup.ts +28 -50
- package/src/messaging/providers/outlook/adapter.ts +8 -1
- package/src/messaging/providers/outlook/client.ts +299 -0
- package/src/messaging/providers/outlook/types.ts +118 -0
- package/src/notifications/adapters/macos.ts +1 -0
- package/src/notifications/copy-composer.ts +9 -0
- package/src/notifications/signal.ts +16 -0
- package/src/oauth/seed-providers.ts +2 -1
- package/src/permissions/checker.ts +24 -3
- package/src/permissions/defaults.ts +4 -4
- package/src/permissions/workspace-policy.ts +1 -1
- package/src/playbooks/playbook-compiler.ts +19 -18
- package/src/playbooks/types.ts +4 -3
- package/src/prompts/system-prompt.ts +3 -29
- package/src/providers/anthropic/client.ts +47 -19
- package/src/providers/gemini/client.ts +1 -1
- package/src/providers/openai/client.ts +1 -1
- package/src/providers/registry.ts +1 -1
- package/src/providers/retry.ts +19 -3
- package/src/runtime/actor-trust-resolver.ts +5 -1
- package/src/runtime/auth/route-policy.ts +7 -0
- package/src/runtime/guardian-reply-router.ts +5 -1
- package/src/runtime/http-server.ts +23 -3
- package/src/runtime/middleware/auth.ts +20 -0
- package/src/runtime/routes/attachment-routes.test.ts +106 -0
- package/src/runtime/routes/attachment-routes.ts +106 -16
- package/src/runtime/routes/brain-graph-routes.ts +21 -22
- package/src/runtime/routes/btw-routes.ts +8 -0
- package/src/runtime/routes/conversation-management-routes.ts +2 -0
- package/src/runtime/routes/conversation-starter-routes.ts +2 -2
- package/src/runtime/routes/debug-routes.ts +1 -1
- package/src/runtime/routes/global-search-routes.ts +21 -19
- package/src/runtime/routes/group-routes.ts +207 -0
- package/src/runtime/routes/guardian-action-routes.ts +21 -10
- package/src/runtime/routes/guardian-bootstrap-routes.ts +23 -19
- package/src/runtime/routes/inbound-message-handler.ts +19 -0
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.test.ts +292 -0
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +207 -0
- package/src/runtime/routes/memory-item-routes.test.ts +2 -14
- package/src/runtime/routes/memory-item-routes.ts +341 -388
- package/src/runtime/routes/schedule-routes.ts +2 -0
- package/src/runtime/routes/skills-routes.ts +103 -37
- package/src/runtime/routes/work-items-routes.test.ts +2 -6
- package/src/schedule/scheduler.ts +8 -1
- package/src/security/oauth2.ts +1 -1
- package/src/security/secure-keys.ts +4 -8
- package/src/shared/provider-env-vars.ts +19 -0
- package/src/skills/catalog-cache.ts +5 -0
- package/src/skills/catalog-install.ts +15 -14
- package/src/skills/clawhub.ts +134 -154
- package/src/skills/install-meta.ts +208 -0
- package/src/skills/managed-store.ts +27 -16
- package/src/skills/skill-memory.ts +152 -77
- package/src/skills/skillssh-registry.ts +19 -17
- package/src/tasks/task-runner.ts +3 -1
- package/src/telemetry/usage-telemetry-reporter.test.ts +3 -5
- package/src/tools/browser/runtime-check.ts +3 -1
- package/src/tools/memory/register.ts +63 -46
- package/src/tools/permission-checker.ts +7 -1
- package/src/tools/shared/filesystem/image-read.ts +22 -85
- package/src/tools/terminal/safe-env.ts +1 -0
- package/src/tools/tool-manifest.ts +3 -3
- package/src/util/browser.ts +25 -10
- package/src/util/bun-runtime.ts +172 -0
- package/src/watcher/providers/outlook-calendar.ts +343 -0
- package/src/watcher/providers/outlook.ts +198 -0
- package/src/workspace/migrations/025-remove-oauth-app-setup-skills.ts +76 -0
- package/src/workspace/migrations/026-backfill-install-meta.ts +325 -0
- package/src/workspace/migrations/027-remove-orphaned-optimized-images-cache.ts +42 -0
- package/src/workspace/migrations/registry.ts +6 -0
- package/src/__tests__/context-memory-e2e.test.ts +0 -415
- package/src/__tests__/journal-context.test.ts +0 -268
- package/src/__tests__/memory-context-benchmark.benchmark.test.ts +0 -297
- package/src/__tests__/memory-lifecycle-e2e.test.ts +0 -459
- package/src/__tests__/memory-query-builder.test.ts +0 -59
- package/src/__tests__/memory-recall-quality.test.ts +0 -1046
- package/src/__tests__/memory-regressions.experimental.test.ts +0 -629
- package/src/__tests__/memory-regressions.test.ts +0 -3696
- package/src/__tests__/memory-retrieval.benchmark.test.ts +0 -295
- package/src/daemon/conversation-memory.ts +0 -207
- package/src/memory/conversation-starters-cadence.ts +0 -74
- package/src/memory/items-extractor.ts +0 -860
- package/src/memory/job-handlers/batch-extraction.ts +0 -753
- package/src/memory/job-handlers/extraction.ts +0 -40
- package/src/memory/job-handlers/journal-carry-forward.test.ts +0 -355
- package/src/memory/job-handlers/journal-carry-forward.ts +0 -255
- package/src/memory/journal-memory.ts +0 -224
- package/src/memory/query-builder.ts +0 -47
- package/src/memory/query-expansion.ts +0 -83
- package/src/memory/retriever.test.ts +0 -1592
- package/src/memory/retriever.ts +0 -1331
- package/src/memory/search/formatting.test.ts +0 -140
- package/src/memory/search/formatting.ts +0 -262
- package/src/memory/search/mmr.ts +0 -139
- package/src/memory/search/ranking.ts +0 -15
- package/src/memory/search/staleness.ts +0 -40
- package/src/memory/search/tier-classifier.ts +0 -18
- package/src/memory/search/types.ts +0 -121
- package/src/prompts/journal-context.ts +0 -154
- package/src/tools/memory/definitions.ts +0 -69
- package/src/tools/memory/handlers.test.ts +0 -562
- package/src/tools/memory/handlers.ts +0 -434
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
import {
|
|
2
|
+
batchGetMessages,
|
|
3
|
+
listMessages,
|
|
4
|
+
searchMessages,
|
|
5
|
+
} from "../../../../messaging/providers/outlook/client.js";
|
|
6
|
+
import type { OutlookMessage } from "../../../../messaging/providers/outlook/types.js";
|
|
7
|
+
import { resolveOAuthConnection } from "../../../../oauth/connection-resolver.js";
|
|
8
|
+
import type {
|
|
9
|
+
ToolContext,
|
|
10
|
+
ToolExecutionResult,
|
|
11
|
+
} from "../../../../tools/types.js";
|
|
12
|
+
import { storeScanResult } from "../../gmail/tools/scan-result-store.js";
|
|
13
|
+
import { err, ok } from "./shared.js";
|
|
14
|
+
|
|
15
|
+
const MAX_MESSAGES_CAP = 10000;
|
|
16
|
+
const MAX_IDS_PER_SENDER = 5000;
|
|
17
|
+
const MAX_SAMPLE_SUBJECTS = 3;
|
|
18
|
+
|
|
19
|
+
interface SenderAggregation {
|
|
20
|
+
displayName: string;
|
|
21
|
+
email: string;
|
|
22
|
+
messageCount: number;
|
|
23
|
+
hasUnsubscribe: boolean;
|
|
24
|
+
newestMessageId: string;
|
|
25
|
+
newestUnsubscribableMessageId: string | null;
|
|
26
|
+
newestUnsubscribableEpoch: number;
|
|
27
|
+
oldestDate: string;
|
|
28
|
+
newestDate: string;
|
|
29
|
+
messageIds: string[];
|
|
30
|
+
hasMore: boolean;
|
|
31
|
+
sampleSubjects: string[];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export async function run(
|
|
35
|
+
input: Record<string, unknown>,
|
|
36
|
+
_context: ToolContext,
|
|
37
|
+
): Promise<ToolExecutionResult> {
|
|
38
|
+
const account = input.account as string | undefined;
|
|
39
|
+
const userQuery = input.query as string | undefined;
|
|
40
|
+
const maxSenders = (input.max_senders as number) ?? 50;
|
|
41
|
+
|
|
42
|
+
try {
|
|
43
|
+
const connection = await resolveOAuthConnection("outlook", {
|
|
44
|
+
account,
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// Build OData filter: inbox messages from last 90 days
|
|
48
|
+
const ninetyDaysAgo = new Date(
|
|
49
|
+
Date.now() - 90 * 24 * 60 * 60 * 1000,
|
|
50
|
+
).toISOString();
|
|
51
|
+
const ninetyDaysAgoEpoch = new Date(ninetyDaysAgo).getTime();
|
|
52
|
+
const dateFilter = `receivedDateTime ge ${ninetyDaysAgo}`;
|
|
53
|
+
|
|
54
|
+
const allMessageIds: string[] = [];
|
|
55
|
+
const fetchPromises: Promise<OutlookMessage[]>[] = [];
|
|
56
|
+
let skip = 0;
|
|
57
|
+
let truncated = false;
|
|
58
|
+
let timeBudgetExceeded = false;
|
|
59
|
+
const startTime = Date.now();
|
|
60
|
+
const TIME_BUDGET_MS = 90_000;
|
|
61
|
+
|
|
62
|
+
// When userQuery is provided, use searchMessages (Microsoft Graph
|
|
63
|
+
// doesn't support combining $filter and $search). Date filtering is
|
|
64
|
+
// applied client-side instead.
|
|
65
|
+
const useSearch = Boolean(userQuery);
|
|
66
|
+
|
|
67
|
+
// Paginate through messages
|
|
68
|
+
while (allMessageIds.length < MAX_MESSAGES_CAP) {
|
|
69
|
+
if (Date.now() - startTime > TIME_BUDGET_MS) {
|
|
70
|
+
timeBudgetExceeded = true;
|
|
71
|
+
truncated = true;
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
const pageSize = Math.min(100, MAX_MESSAGES_CAP - allMessageIds.length);
|
|
75
|
+
|
|
76
|
+
let messages: OutlookMessage[];
|
|
77
|
+
|
|
78
|
+
if (useSearch) {
|
|
79
|
+
const searchResp = await searchMessages(connection, userQuery!, {
|
|
80
|
+
top: pageSize,
|
|
81
|
+
skip,
|
|
82
|
+
});
|
|
83
|
+
messages = searchResp.value ?? [];
|
|
84
|
+
} else {
|
|
85
|
+
const listResp = await listMessages(connection, {
|
|
86
|
+
top: pageSize,
|
|
87
|
+
skip,
|
|
88
|
+
filter: dateFilter,
|
|
89
|
+
orderby: "receivedDateTime desc",
|
|
90
|
+
select: "id,from,receivedDateTime,hasAttachments,subject",
|
|
91
|
+
});
|
|
92
|
+
messages = listResp.value ?? [];
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (messages.length === 0) break;
|
|
96
|
+
|
|
97
|
+
// When using search, apply 90-day date filter client-side
|
|
98
|
+
const filtered = useSearch
|
|
99
|
+
? messages.filter((m) => {
|
|
100
|
+
const received = m.receivedDateTime
|
|
101
|
+
? new Date(m.receivedDateTime).getTime()
|
|
102
|
+
: 0;
|
|
103
|
+
return received >= ninetyDaysAgoEpoch;
|
|
104
|
+
})
|
|
105
|
+
: messages;
|
|
106
|
+
|
|
107
|
+
const ids = filtered.map((m) => m.id);
|
|
108
|
+
allMessageIds.push(...ids);
|
|
109
|
+
|
|
110
|
+
// Fetch internet message headers (List-Unsubscribe) for each batch
|
|
111
|
+
if (ids.length > 0) {
|
|
112
|
+
fetchPromises.push(
|
|
113
|
+
batchGetMessages(
|
|
114
|
+
connection,
|
|
115
|
+
ids,
|
|
116
|
+
"id,from,receivedDateTime,subject,internetMessageHeaders",
|
|
117
|
+
),
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
skip += messages.length;
|
|
122
|
+
|
|
123
|
+
// If we received fewer messages than requested, there are no more pages
|
|
124
|
+
if (messages.length < pageSize) break;
|
|
125
|
+
|
|
126
|
+
// When using search, if all messages in a page are older than
|
|
127
|
+
// 90 days we've likely passed beyond the relevant window
|
|
128
|
+
if (useSearch && filtered.length === 0) break;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Flag truncation if we hit the cap with more pages potentially available
|
|
132
|
+
if (allMessageIds.length >= MAX_MESSAGES_CAP) {
|
|
133
|
+
truncated = true;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (allMessageIds.length === 0) {
|
|
137
|
+
return ok(
|
|
138
|
+
JSON.stringify({
|
|
139
|
+
senders: [],
|
|
140
|
+
total_scanned: 0,
|
|
141
|
+
message:
|
|
142
|
+
"No emails found matching the query. Try broadening the search (e.g. extend date range).",
|
|
143
|
+
}),
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const fetchedMessages = (await Promise.all(fetchPromises)).flat();
|
|
148
|
+
|
|
149
|
+
// Group by sender email
|
|
150
|
+
const senderMap = new Map<string, SenderAggregation>();
|
|
151
|
+
|
|
152
|
+
for (const msg of fetchedMessages) {
|
|
153
|
+
const fromEmail = msg.from?.emailAddress?.address?.toLowerCase();
|
|
154
|
+
const fromName = msg.from?.emailAddress?.name ?? "";
|
|
155
|
+
const subject = msg.subject ?? "";
|
|
156
|
+
const dateStr = msg.receivedDateTime ?? "";
|
|
157
|
+
|
|
158
|
+
if (!fromEmail) continue;
|
|
159
|
+
|
|
160
|
+
// Check for List-Unsubscribe header
|
|
161
|
+
const listUnsub = msg.internetMessageHeaders?.find(
|
|
162
|
+
(h) => h.name.toLowerCase() === "list-unsubscribe",
|
|
163
|
+
)?.value;
|
|
164
|
+
|
|
165
|
+
let agg = senderMap.get(fromEmail);
|
|
166
|
+
if (!agg) {
|
|
167
|
+
agg = {
|
|
168
|
+
displayName: fromName,
|
|
169
|
+
email: fromEmail,
|
|
170
|
+
messageCount: 0,
|
|
171
|
+
hasUnsubscribe: false,
|
|
172
|
+
newestMessageId: msg.id,
|
|
173
|
+
newestUnsubscribableMessageId: null,
|
|
174
|
+
newestUnsubscribableEpoch: 0,
|
|
175
|
+
oldestDate: dateStr,
|
|
176
|
+
newestDate: dateStr,
|
|
177
|
+
messageIds: [],
|
|
178
|
+
hasMore: false,
|
|
179
|
+
sampleSubjects: [],
|
|
180
|
+
};
|
|
181
|
+
senderMap.set(fromEmail, agg);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
agg.messageCount++;
|
|
185
|
+
|
|
186
|
+
if (listUnsub) agg.hasUnsubscribe = true;
|
|
187
|
+
|
|
188
|
+
// Use displayName from earliest message that has one
|
|
189
|
+
if (!agg.displayName && fromName) agg.displayName = fromName;
|
|
190
|
+
|
|
191
|
+
// Track message IDs (cap at MAX_IDS_PER_SENDER)
|
|
192
|
+
if (agg.messageIds.length < MAX_IDS_PER_SENDER) {
|
|
193
|
+
agg.messageIds.push(msg.id);
|
|
194
|
+
} else {
|
|
195
|
+
agg.hasMore = true;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Track date range using ISO date strings
|
|
199
|
+
const msgEpoch = dateStr ? new Date(dateStr).getTime() : 0;
|
|
200
|
+
const oldestEpoch = agg.oldestDate
|
|
201
|
+
? new Date(agg.oldestDate).getTime()
|
|
202
|
+
: Infinity;
|
|
203
|
+
const newestEpoch = agg.newestDate
|
|
204
|
+
? new Date(agg.newestDate).getTime()
|
|
205
|
+
: 0;
|
|
206
|
+
|
|
207
|
+
if (msgEpoch > 0 && msgEpoch < oldestEpoch) {
|
|
208
|
+
agg.oldestDate = dateStr || agg.oldestDate;
|
|
209
|
+
}
|
|
210
|
+
if (msgEpoch > newestEpoch) {
|
|
211
|
+
agg.newestDate = dateStr || agg.newestDate;
|
|
212
|
+
agg.newestMessageId = msg.id;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Track the newest message that has List-Unsubscribe so
|
|
216
|
+
// unsubscribe actions target a message that carries the header
|
|
217
|
+
if (listUnsub && msgEpoch >= agg.newestUnsubscribableEpoch) {
|
|
218
|
+
agg.newestUnsubscribableMessageId = msg.id;
|
|
219
|
+
agg.newestUnsubscribableEpoch = msgEpoch;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Collect sample subjects
|
|
223
|
+
if (subject && agg.sampleSubjects.length < MAX_SAMPLE_SUBJECTS) {
|
|
224
|
+
agg.sampleSubjects.push(subject);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Sort by message count descending, take top N
|
|
229
|
+
const sorted = [...senderMap.values()]
|
|
230
|
+
.sort((a, b) => b.messageCount - a.messageCount)
|
|
231
|
+
.slice(0, maxSenders);
|
|
232
|
+
|
|
233
|
+
const resultSenders = sorted.map((s) => ({
|
|
234
|
+
id: Buffer.from(s.email).toString("base64url"),
|
|
235
|
+
display_name: s.displayName || s.email.split("@")[0],
|
|
236
|
+
email: s.email,
|
|
237
|
+
message_count: s.messageCount,
|
|
238
|
+
has_unsubscribe: s.hasUnsubscribe,
|
|
239
|
+
newest_message_id:
|
|
240
|
+
s.hasUnsubscribe && s.newestUnsubscribableMessageId
|
|
241
|
+
? s.newestUnsubscribableMessageId
|
|
242
|
+
: s.newestMessageId,
|
|
243
|
+
oldest_date: s.oldestDate,
|
|
244
|
+
newest_date: s.newestDate,
|
|
245
|
+
sample_subjects: s.sampleSubjects,
|
|
246
|
+
}));
|
|
247
|
+
|
|
248
|
+
// Store message IDs server-side to keep them out of LLM context
|
|
249
|
+
const scanId = storeScanResult(
|
|
250
|
+
sorted.map((s) => ({
|
|
251
|
+
id: Buffer.from(s.email).toString("base64url"),
|
|
252
|
+
messageIds: s.messageIds,
|
|
253
|
+
newestMessageId: s.newestMessageId,
|
|
254
|
+
newestUnsubscribableMessageId: s.newestUnsubscribableMessageId,
|
|
255
|
+
})),
|
|
256
|
+
);
|
|
257
|
+
|
|
258
|
+
return ok(
|
|
259
|
+
JSON.stringify({
|
|
260
|
+
scan_id: scanId,
|
|
261
|
+
senders: resultSenders,
|
|
262
|
+
total_scanned: allMessageIds.length,
|
|
263
|
+
query_used: userQuery ?? `inbox messages from last 90 days`,
|
|
264
|
+
...(truncated ? { truncated: true } : {}),
|
|
265
|
+
...(timeBudgetExceeded ? { time_budget_exceeded: true } : {}),
|
|
266
|
+
note: `message_count reflects emails found per sender within the ${allMessageIds.length} messages scanned. Use scan_id with outlook_archive to archive messages (pass scan_id + sender_ids instead of message_ids).`,
|
|
267
|
+
}),
|
|
268
|
+
);
|
|
269
|
+
} catch (e) {
|
|
270
|
+
return err(e instanceof Error ? e.message : String(e));
|
|
271
|
+
}
|
|
272
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { trashMessage } from "../../../../messaging/providers/outlook/client.js";
|
|
2
|
+
import { resolveOAuthConnection } from "../../../../oauth/connection-resolver.js";
|
|
3
|
+
import type {
|
|
4
|
+
ToolContext,
|
|
5
|
+
ToolExecutionResult,
|
|
6
|
+
} from "../../../../tools/types.js";
|
|
7
|
+
import { err, ok } from "./shared.js";
|
|
8
|
+
|
|
9
|
+
export async function run(
|
|
10
|
+
input: Record<string, unknown>,
|
|
11
|
+
_context: ToolContext,
|
|
12
|
+
): Promise<ToolExecutionResult> {
|
|
13
|
+
const account = input.account as string | undefined;
|
|
14
|
+
const messageId = input.message_id as string;
|
|
15
|
+
|
|
16
|
+
if (!messageId) {
|
|
17
|
+
return err("message_id is required.");
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
const connection = await resolveOAuthConnection("outlook", {
|
|
22
|
+
account,
|
|
23
|
+
});
|
|
24
|
+
await trashMessage(connection, messageId);
|
|
25
|
+
return ok("Message moved to Deleted Items.");
|
|
26
|
+
} catch (e) {
|
|
27
|
+
return err(e instanceof Error ? e.message : String(e));
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getMessageWithHeaders,
|
|
3
|
+
sendMessage,
|
|
4
|
+
} from "../../../../messaging/providers/outlook/client.js";
|
|
5
|
+
import { resolveOAuthConnection } from "../../../../oauth/connection-resolver.js";
|
|
6
|
+
import {
|
|
7
|
+
isPrivateOrLocalHost,
|
|
8
|
+
resolveHostAddresses,
|
|
9
|
+
} from "../../../../tools/network/url-safety.js";
|
|
10
|
+
import type {
|
|
11
|
+
ToolContext,
|
|
12
|
+
ToolExecutionResult,
|
|
13
|
+
} from "../../../../tools/types.js";
|
|
14
|
+
import {
|
|
15
|
+
err,
|
|
16
|
+
ok,
|
|
17
|
+
pinnedHttpsRequest,
|
|
18
|
+
resolveRequestAddress,
|
|
19
|
+
} from "./shared.js";
|
|
20
|
+
|
|
21
|
+
export async function run(
|
|
22
|
+
input: Record<string, unknown>,
|
|
23
|
+
context: ToolContext,
|
|
24
|
+
): Promise<ToolExecutionResult> {
|
|
25
|
+
const account = input.account as string | undefined;
|
|
26
|
+
if (!context.triggeredBySurfaceAction) {
|
|
27
|
+
return err(
|
|
28
|
+
"This tool requires user confirmation via a surface action. Present results in a selection table with action buttons and wait for the user to click before proceeding.",
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const messageId = input.message_id as string;
|
|
33
|
+
|
|
34
|
+
if (!messageId) {
|
|
35
|
+
return err("message_id is required.");
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
const connection = await resolveOAuthConnection("outlook", {
|
|
40
|
+
account,
|
|
41
|
+
});
|
|
42
|
+
const message = await getMessageWithHeaders(connection, messageId);
|
|
43
|
+
const headers = message.internetMessageHeaders ?? [];
|
|
44
|
+
const unsubHeader = headers.find(
|
|
45
|
+
(h) => h.name.toLowerCase() === "list-unsubscribe",
|
|
46
|
+
)?.value;
|
|
47
|
+
|
|
48
|
+
if (!unsubHeader) {
|
|
49
|
+
return err(
|
|
50
|
+
"No List-Unsubscribe header found. Manual unsubscribe may be required.",
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const httpsMatch = unsubHeader.match(/<(https:\/\/[^>]+)>/);
|
|
55
|
+
const mailtoMatch = unsubHeader.match(/<mailto:([^>]+)>/);
|
|
56
|
+
const postHeader = headers.find(
|
|
57
|
+
(h) => h.name.toLowerCase() === "list-unsubscribe-post",
|
|
58
|
+
)?.value;
|
|
59
|
+
|
|
60
|
+
if (httpsMatch) {
|
|
61
|
+
const url = httpsMatch[1];
|
|
62
|
+
let parsed: URL;
|
|
63
|
+
let validatedAddresses: string[];
|
|
64
|
+
try {
|
|
65
|
+
parsed = new URL(url);
|
|
66
|
+
if (parsed.protocol !== "https:") {
|
|
67
|
+
return err("Unsubscribe URL must use HTTPS.");
|
|
68
|
+
}
|
|
69
|
+
if (isPrivateOrLocalHost(parsed.hostname)) {
|
|
70
|
+
return err("Unsubscribe URL points to a private or local address.");
|
|
71
|
+
}
|
|
72
|
+
const { addresses, blockedAddress } = await resolveRequestAddress(
|
|
73
|
+
parsed.hostname,
|
|
74
|
+
resolveHostAddresses,
|
|
75
|
+
false,
|
|
76
|
+
);
|
|
77
|
+
if (blockedAddress) {
|
|
78
|
+
return err("Unsubscribe URL resolves to a private or local address.");
|
|
79
|
+
}
|
|
80
|
+
if (addresses.length === 0) {
|
|
81
|
+
return err("Unable to resolve unsubscribe URL hostname.");
|
|
82
|
+
}
|
|
83
|
+
validatedAddresses = addresses;
|
|
84
|
+
} catch {
|
|
85
|
+
return err("Invalid unsubscribe URL.");
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const method = postHeader ? "POST" : "GET";
|
|
89
|
+
const reqOpts = postHeader
|
|
90
|
+
? {
|
|
91
|
+
method: "POST" as const,
|
|
92
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
93
|
+
body: postHeader,
|
|
94
|
+
}
|
|
95
|
+
: undefined;
|
|
96
|
+
|
|
97
|
+
let lastStatus = 0;
|
|
98
|
+
for (const address of validatedAddresses) {
|
|
99
|
+
try {
|
|
100
|
+
lastStatus = await pinnedHttpsRequest(parsed, address, reqOpts);
|
|
101
|
+
if (lastStatus >= 200 && lastStatus < 400) {
|
|
102
|
+
return ok(`Successfully unsubscribed via HTTPS ${method}.`);
|
|
103
|
+
}
|
|
104
|
+
} catch {
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return err(`Unsubscribe request failed: ${lastStatus}`);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (mailtoMatch) {
|
|
112
|
+
const mailtoAddr = mailtoMatch[1].split("?")[0];
|
|
113
|
+
await sendMessage(connection, {
|
|
114
|
+
message: {
|
|
115
|
+
subject: "Unsubscribe",
|
|
116
|
+
body: { contentType: "text", content: "" },
|
|
117
|
+
toRecipients: [{ emailAddress: { address: mailtoAddr } }],
|
|
118
|
+
},
|
|
119
|
+
});
|
|
120
|
+
return ok(`Unsubscribe email sent to ${mailtoAddr}.`);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return err(
|
|
124
|
+
"No supported unsubscribe method found (requires https: or mailto: URL).",
|
|
125
|
+
);
|
|
126
|
+
} catch (e) {
|
|
127
|
+
return err(e instanceof Error ? e.message : String(e));
|
|
128
|
+
}
|
|
129
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getAutoReplySettings,
|
|
3
|
+
updateAutoReplySettings,
|
|
4
|
+
} from "../../../../messaging/providers/outlook/client.js";
|
|
5
|
+
import type { OutlookAutoReplySettings } from "../../../../messaging/providers/outlook/types.js";
|
|
6
|
+
import { resolveOAuthConnection } from "../../../../oauth/connection-resolver.js";
|
|
7
|
+
import type {
|
|
8
|
+
ToolContext,
|
|
9
|
+
ToolExecutionResult,
|
|
10
|
+
} from "../../../../tools/types.js";
|
|
11
|
+
import { err, ok } from "./shared.js";
|
|
12
|
+
|
|
13
|
+
export async function run(
|
|
14
|
+
input: Record<string, unknown>,
|
|
15
|
+
_context: ToolContext,
|
|
16
|
+
): Promise<ToolExecutionResult> {
|
|
17
|
+
const account = input.account as string | undefined;
|
|
18
|
+
const action = input.action as string;
|
|
19
|
+
|
|
20
|
+
if (!action) {
|
|
21
|
+
return err("action is required (get, enable, or disable).");
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
const connection = await resolveOAuthConnection("outlook", {
|
|
26
|
+
account,
|
|
27
|
+
});
|
|
28
|
+
switch (action) {
|
|
29
|
+
case "get": {
|
|
30
|
+
const settings = await getAutoReplySettings(connection);
|
|
31
|
+
return ok(JSON.stringify(settings, null, 2));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
case "enable": {
|
|
35
|
+
const internalMessage = input.internal_message as string;
|
|
36
|
+
if (!internalMessage)
|
|
37
|
+
return err("internal_message is required when enabling auto-reply.");
|
|
38
|
+
|
|
39
|
+
const externalAudience = (input.external_audience as string) ?? "none";
|
|
40
|
+
const startDate = input.start_date as string | undefined;
|
|
41
|
+
const endDate = input.end_date as string | undefined;
|
|
42
|
+
const timeZone =
|
|
43
|
+
(input.time_zone as string) ??
|
|
44
|
+
Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
45
|
+
|
|
46
|
+
const isScheduled = !!(startDate && endDate);
|
|
47
|
+
|
|
48
|
+
const settings: OutlookAutoReplySettings = {
|
|
49
|
+
status: isScheduled ? "scheduled" : "alwaysEnabled",
|
|
50
|
+
externalAudience: externalAudience as "none" | "contactsOnly" | "all",
|
|
51
|
+
internalReplyMessage: internalMessage,
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
if (input.external_message) {
|
|
55
|
+
settings.externalReplyMessage = input.external_message as string;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (isScheduled) {
|
|
59
|
+
settings.scheduledStartDateTime = {
|
|
60
|
+
dateTime: startDate!,
|
|
61
|
+
timeZone,
|
|
62
|
+
};
|
|
63
|
+
settings.scheduledEndDateTime = {
|
|
64
|
+
dateTime: endDate!,
|
|
65
|
+
timeZone,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
await updateAutoReplySettings(connection, settings);
|
|
70
|
+
return ok("Auto-reply enabled.");
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
case "disable": {
|
|
74
|
+
await updateAutoReplySettings(connection, {
|
|
75
|
+
status: "disabled",
|
|
76
|
+
externalAudience: "none",
|
|
77
|
+
});
|
|
78
|
+
return ok("Auto-reply disabled.");
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
default:
|
|
82
|
+
return err(`Unknown action "${action}". Use get, enable, or disable.`);
|
|
83
|
+
}
|
|
84
|
+
} catch (e) {
|
|
85
|
+
return err(e instanceof Error ? e.message : String(e));
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared utilities for Outlook skill tools.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { ToolExecutionResult } from "../../../../tools/types.js";
|
|
6
|
+
|
|
7
|
+
export function ok(content: string): ToolExecutionResult {
|
|
8
|
+
return { content, isError: false };
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function err(message: string): ToolExecutionResult {
|
|
12
|
+
return { content: message, isError: true };
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Re-export DNS rebinding protection helpers from the Gmail shared module.
|
|
16
|
+
// These are provider-agnostic and should be reused, not duplicated.
|
|
17
|
+
export { pinnedHttpsRequest } from "../../gmail/tools/shared.js";
|
|
18
|
+
|
|
19
|
+
// resolveRequestAddress lives in the shared network-safety module (not Gmail-specific).
|
|
20
|
+
export { resolveRequestAddress } from "../../../../tools/network/url-safety.js";
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: outlook-calendar
|
|
3
|
+
description: View, create, and manage Outlook Calendar events and check availability
|
|
4
|
+
compatibility: "Designed for Vellum personal assistants"
|
|
5
|
+
metadata:
|
|
6
|
+
emoji: "📅"
|
|
7
|
+
vellum:
|
|
8
|
+
display-name: "Outlook Calendar"
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
You are an Outlook Calendar assistant with full access to the user's calendar. Use the Outlook Calendar tools to help them view, create, and manage events.
|
|
12
|
+
|
|
13
|
+
## Connection Setup
|
|
14
|
+
|
|
15
|
+
Before using any Outlook Calendar tool, verify that Outlook is connected by attempting a lightweight call (e.g., `outlook_calendar_list_events` with a narrow date range). If the call fails with a token/authorization error:
|
|
16
|
+
|
|
17
|
+
1. **Try connecting directly first.** Run `assistant oauth status outlook`. This will show whether or not the user had previously connected their Outlook account. If so, they are ready to go.
|
|
18
|
+
2. **If no connections are found:** Call `skill_load` with `skill: "vellum-oauth-integrations"`. The skill will evaluate whether managed or your-own mode is appropriate and guide the user accordingly.
|
|
19
|
+
|
|
20
|
+
## Capabilities
|
|
21
|
+
|
|
22
|
+
- **List Events**: View upcoming events from any calendar within a date range.
|
|
23
|
+
- **Get Event**: Read full details of a specific calendar event.
|
|
24
|
+
- **Create Event**: Create new events with attendees, location, and description.
|
|
25
|
+
- **Check Availability**: Find free/busy times across calendars to identify open slots for scheduling.
|
|
26
|
+
- **RSVP**: Respond to event invitations (accepted, declined, tentative).
|
|
27
|
+
|
|
28
|
+
## Scheduling Playbook
|
|
29
|
+
|
|
30
|
+
When the user wants to schedule something:
|
|
31
|
+
|
|
32
|
+
1. **Always check availability first** before proposing times. Use `outlook_calendar_check_availability` to find free slots.
|
|
33
|
+
2. Propose 2-3 available time options to the user.
|
|
34
|
+
3. Once the user picks a time, create the event with `outlook_calendar_create_event`.
|
|
35
|
+
4. If adding other attendees, mention that they'll receive an invitation email.
|
|
36
|
+
|
|
37
|
+
## Date & Time Handling
|
|
38
|
+
|
|
39
|
+
- Always ask the user for their timezone if it's not already known from context or their profile.
|
|
40
|
+
- Use ISO 8601 format for dates and times (e.g., `2024-01-15T09:00:00-05:00`).
|
|
41
|
+
- For all-day events, use date-only format (e.g., `2024-01-15`).
|
|
42
|
+
- When listing events, display times in the user's local timezone.
|
|
43
|
+
|
|
44
|
+
## Confidence Scores
|
|
45
|
+
|
|
46
|
+
Medium-risk tools (create event, RSVP) require a confidence score between 0 and 1. Set this based on how certain you are the action matches the user's intent:
|
|
47
|
+
|
|
48
|
+
- **0.9-1.0**: User explicitly requested this exact action
|
|
49
|
+
- **0.7-0.8**: Action is strongly implied by context
|
|
50
|
+
- **0.5-0.6**: Reasonable inference but some ambiguity
|
|
51
|
+
- **Below 0.5**: Ask the user to confirm before proceeding
|