@vellumai/assistant 0.4.35 → 0.4.37
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 +1 -1
- package/ARCHITECTURE.md +44 -49
- package/README.md +32 -20
- package/docs/architecture/keychain-broker.md +186 -0
- package/docs/architecture/security.md +110 -116
- package/docs/runbook-trusted-contacts.md +2 -2
- package/docs/skills.md +25 -25
- package/package.json +5 -2
- package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +11 -2
- package/src/__tests__/actor-token-service.test.ts +1 -0
- package/src/__tests__/amazon-cdp-integration.test.ts +74 -0
- package/src/__tests__/assistant-feature-flags-integration.test.ts +38 -9
- package/src/__tests__/assistant-id-boundary-guard.test.ts +29 -0
- package/src/__tests__/browser-fill-credential.test.ts +1 -1
- package/src/__tests__/bundle-scanner.test.ts +1 -1
- package/src/__tests__/channel-guardian.test.ts +102 -102
- package/src/__tests__/channel-invite-transport.test.ts +155 -256
- package/src/__tests__/channel-readiness-routes.test.ts +336 -0
- package/src/__tests__/checker.test.ts +6 -6
- package/src/__tests__/chrome-cdp.test.ts +350 -0
- package/src/__tests__/computer-use-session-lifecycle.test.ts +3 -3
- package/src/__tests__/computer-use-session-working-dir.test.ts +86 -52
- package/src/__tests__/computer-use-skill-lifecycle-cleanup.test.ts +1 -1
- package/src/__tests__/config-loader-migration.test.ts +85 -0
- package/src/__tests__/conversation-pairing.test.ts +370 -5
- package/src/__tests__/credential-broker-browser-fill.test.ts +1 -10
- package/src/__tests__/credential-broker-server-use.test.ts +1 -10
- package/src/__tests__/credential-security-e2e.test.ts +7 -1
- package/src/__tests__/credential-security-invariants.test.ts +14 -20
- package/src/__tests__/credential-vault-unit.test.ts +1 -11
- package/src/__tests__/credential-vault.test.ts +5 -19
- package/src/__tests__/credentials-cli.test.ts +814 -0
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +23 -4
- package/src/__tests__/email-invite-adapter.test.ts +78 -0
- package/src/__tests__/email-service-config-fallback.test.ts +102 -0
- package/src/__tests__/encrypted-store.test.ts +6 -6
- package/src/__tests__/ephemeral-permissions.test.ts +3 -3
- package/src/__tests__/gateway-only-enforcement.test.ts +5 -1
- package/src/__tests__/guardian-actions-endpoint.test.ts +70 -12
- package/src/__tests__/guardian-outbound-http.test.ts +53 -47
- package/src/__tests__/handle-user-message-secret-resume.test.ts +23 -0
- package/src/__tests__/handlers-add-trust-rule-metadata.test.ts +32 -23
- package/src/__tests__/handlers-telegram-config.test.ts +8 -2
- package/src/__tests__/handlers-twitter-config.test.ts +2 -2
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +108 -7
- package/src/__tests__/ingress-reconcile.test.ts +6 -0
- package/src/__tests__/intent-routing.test.ts +23 -4
- package/src/__tests__/invite-routes-http.test.ts +12 -0
- package/src/__tests__/ipc-snapshot.test.ts +8 -2
- package/src/__tests__/keychain-broker-client.test.ts +543 -0
- package/src/__tests__/llm-usage-store.test.ts +344 -0
- package/src/__tests__/mcp-client-auth.test.ts +2 -2
- package/src/__tests__/media-reuse-story.e2e.test.ts +1 -1
- package/src/__tests__/migration-transport.test.ts +49 -0
- package/src/__tests__/notification-broadcaster.test.ts +205 -5
- package/src/__tests__/notification-deep-link.test.ts +365 -1
- package/src/__tests__/oauth-connect-handler.test.ts +2 -2
- package/src/__tests__/onboarding-starter-tasks.test.ts +17 -4
- package/src/__tests__/proxy-approval-callback.test.ts +1 -1
- package/src/__tests__/recording-handler.test.ts +1 -1
- package/src/__tests__/recording-intent-handler.test.ts +6 -1
- package/src/__tests__/recording-state-machine.test.ts +1 -1
- package/src/__tests__/relay-server.test.ts +9 -1
- package/src/__tests__/ride-shotgun-handler.test.ts +499 -0
- package/src/__tests__/runtime-attachment-metadata.test.ts +160 -1
- package/src/__tests__/script-proxy-injection-runtime.test.ts +299 -2
- package/src/__tests__/script-proxy-profile-template-fallback.test.ts +1 -1
- package/src/__tests__/secret-onetime-send.test.ts +8 -2
- package/src/__tests__/secure-keys.test.ts +175 -216
- package/src/__tests__/session-confirmation-signals.test.ts +1 -1
- package/src/__tests__/session-messaging-secret-redirect.test.ts +1 -1
- package/src/__tests__/session-queue.test.ts +2 -1
- package/src/__tests__/session-tool-setup-app-refresh.test.ts +2 -2
- package/src/__tests__/skill-feature-flags-integration.test.ts +29 -4
- package/src/__tests__/skill-feature-flags.test.ts +12 -9
- package/src/__tests__/skill-load-feature-flag.test.ts +26 -5
- package/src/__tests__/skill-projection.benchmark.test.ts +0 -1
- package/src/__tests__/skills.test.ts +34 -4
- package/src/__tests__/slack-channel-config.test.ts +2 -2
- package/src/__tests__/system-prompt.test.ts +26 -4
- package/src/__tests__/telegram-bot-username-resolution.test.ts +212 -0
- package/src/__tests__/telegram-invite-adapter.test.ts +164 -0
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +1 -1
- package/src/__tests__/tool-permission-simulate-handler.test.ts +8 -2
- package/src/__tests__/trusted-contact-approval-notifier.test.ts +9 -1
- package/src/__tests__/twitter-auth-handler.test.ts +2 -2
- package/src/__tests__/twitter-oauth-client.test.ts +1 -1
- package/src/__tests__/usage-routes.test.ts +339 -0
- package/src/__tests__/whatsapp-invite-adapter.test.ts +94 -0
- package/src/agent/loop.ts +3 -0
- package/src/amazon/checkout.ts +0 -1
- package/src/approvals/guardian-request-resolvers.ts +9 -1
- package/src/bundler/app-bundler.ts +28 -12
- package/src/bundler/bundle-scanner.ts +1 -1
- package/src/bundler/bundle-signer.ts +3 -3
- package/src/bundler/manifest.ts +1 -1
- package/src/bundler/signature-verifier.ts +3 -3
- package/src/channels/config.ts +1 -1
- package/src/cli/AGENTS.md +63 -0
- package/src/cli/__tests__/notifications.test.ts +470 -0
- package/src/cli/amazon.ts +344 -167
- package/src/cli/audit.ts +85 -0
- package/src/cli/autonomy.ts +369 -0
- package/src/cli/channels.ts +51 -0
- package/src/cli/completions.ts +208 -0
- package/src/cli/config.ts +220 -0
- package/src/cli/contacts.ts +471 -0
- package/src/cli/credentials.ts +564 -0
- package/src/cli/default-action.ts +14 -0
- package/src/cli/dev.ts +131 -0
- package/src/cli/doctor.ts +398 -0
- package/src/cli/email.ts +494 -0
- package/src/cli/influencer.ts +72 -0
- package/src/cli/integrations.ts +248 -57
- package/src/cli/keys.ts +114 -0
- package/src/cli/map.ts +46 -54
- package/src/cli/mcp.ts +111 -3
- package/src/cli/{config-commands.ts → memory.ts} +134 -245
- package/src/cli/notifications.ts +407 -0
- package/src/cli/program.ts +65 -0
- package/src/cli/reference.ts +48 -0
- package/src/cli/sequence.ts +154 -0
- package/src/cli/sessions.ts +262 -0
- package/src/cli/trust.ts +175 -0
- package/src/cli/twitter.ts +323 -106
- package/src/config/__tests__/build-cli-reference-section.test.ts +49 -0
- package/src/config/bundled-skills/amazon/SKILL.md +2 -2
- package/src/config/bundled-skills/app-builder/TOOLS.json +26 -0
- package/src/config/bundled-skills/app-builder/tools/app-generate-icon.ts +13 -0
- package/src/config/bundled-skills/contacts/SKILL.md +178 -10
- package/src/config/bundled-skills/doordash/doordash-cli.ts +23 -168
- package/src/config/bundled-skills/google-oauth-setup/SKILL.md +135 -34
- package/src/config/bundled-skills/messaging/tools/shared.ts +4 -1
- package/src/config/bundled-skills/twilio-setup/SKILL.md +70 -17
- package/src/config/bundled-tool-registry.ts +2 -0
- package/src/config/core-schema.ts +7 -0
- package/src/config/feature-flag-registry.json +16 -0
- package/src/config/loader.ts +26 -0
- package/src/config/schema.ts +4 -0
- package/src/config/skill-state.ts +0 -13
- package/src/config/system-prompt.ts +27 -0
- package/src/contacts/contact-store.ts +25 -0
- package/src/daemon/computer-use-session.ts +1 -1
- package/src/daemon/handlers/apps.ts +1 -0
- package/src/daemon/handlers/config-channels.ts +3 -3
- package/src/daemon/handlers/config-dispatch.ts +29 -0
- package/src/daemon/handlers/config-inbox.ts +4 -3
- package/src/daemon/handlers/config.ts +3 -43
- package/src/daemon/handlers/contacts.ts +34 -0
- package/src/daemon/handlers/index.ts +17 -3
- package/src/daemon/handlers/session-user-message.ts +7 -0
- package/src/daemon/handlers/sessions.ts +21 -2
- package/src/daemon/handlers/shared.ts +17 -0
- package/src/daemon/ipc-contract/apps.ts +2 -0
- package/src/daemon/ipc-contract/computer-use.ts +9 -0
- package/src/daemon/ipc-contract/contacts.ts +3 -3
- package/src/daemon/ipc-contract/inbox.ts +2 -0
- package/src/daemon/ipc-contract/messages.ts +4 -0
- package/src/daemon/ipc-contract/sessions.ts +8 -0
- package/src/daemon/ipc-contract-inventory.json +1 -0
- package/src/daemon/lifecycle.ts +0 -5
- package/src/daemon/ride-shotgun-handler.ts +139 -25
- package/src/daemon/session-agent-loop-handlers.ts +100 -0
- package/src/daemon/session-agent-loop.ts +72 -0
- package/src/daemon/session-tool-setup.ts +7 -0
- package/src/daemon/session.ts +23 -1
- package/src/daemon/tool-side-effects.ts +39 -1
- package/src/email/service.ts +59 -2
- package/src/index.ts +2 -60
- package/src/mcp/mcp-oauth-provider.ts +90 -8
- package/src/media/app-icon-generator.ts +86 -0
- package/src/memory/db-init.ts +11 -0
- package/src/memory/llm-usage-store.ts +186 -0
- package/src/memory/migrations/137-usage-dashboard-indexes.ts +26 -0
- package/src/memory/migrations/139-drop-usage-composite-indexes.ts +30 -0
- package/src/memory/migrations/index.ts +2 -0
- package/src/memory/schema-migration.ts +1 -0
- package/src/memory/shared-app-links-store.ts +1 -1
- package/src/messaging/registry.ts +27 -0
- package/src/notifications/README.md +79 -70
- package/src/notifications/broadcaster.ts +2 -1
- package/src/notifications/conversation-pairing.ts +147 -13
- package/src/notifications/copy-composer.ts +7 -3
- package/src/notifications/destination-resolver.ts +14 -1
- package/src/notifications/emit-signal.ts +3 -2
- package/src/notifications/signal.ts +105 -1
- package/src/notifications/types.ts +16 -0
- package/src/permissions/checker.ts +29 -3
- package/src/permissions/prompter.ts +11 -3
- package/src/runtime/access-request-helper.ts +2 -1
- package/src/runtime/auth/route-policy.ts +7 -1
- package/src/runtime/channel-invite-transport.ts +40 -63
- package/src/runtime/channel-invite-transports/email.ts +13 -39
- package/src/runtime/channel-invite-transports/slack.ts +5 -34
- package/src/runtime/channel-invite-transports/sms.ts +8 -29
- package/src/runtime/channel-invite-transports/telegram.ts +69 -28
- package/src/runtime/channel-invite-transports/voice.ts +0 -7
- package/src/runtime/channel-invite-transports/whatsapp.ts +43 -0
- package/src/runtime/channel-readiness-service.ts +202 -45
- package/src/runtime/confirmation-request-guardian-bridge.ts +2 -1
- package/src/runtime/guardian-outbound-actions.ts +8 -5
- package/src/runtime/http-server.ts +2 -0
- package/src/runtime/invite-instruction-generator.ts +178 -0
- package/src/runtime/invite-service.ts +22 -25
- package/src/runtime/migrations/migration-transport.ts +13 -0
- package/src/runtime/routes/app-routes.ts +1 -1
- package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +8 -7
- package/src/runtime/routes/channel-readiness-routes.ts +30 -11
- package/src/runtime/routes/contact-routes.ts +54 -26
- package/src/runtime/routes/inbound-stages/bootstrap-intercept.ts +1 -1
- package/src/runtime/routes/inbound-stages/escalation-intercept.ts +2 -1
- package/src/runtime/routes/inbound-stages/verification-intercept.ts +2 -1
- package/src/runtime/routes/integration-routes.ts +1 -1
- package/src/runtime/routes/invite-routes.ts +1 -1
- package/src/runtime/routes/secret-routes.ts +31 -7
- package/src/runtime/routes/twilio-routes.ts +32 -1
- package/src/runtime/routes/usage-routes.ts +114 -0
- package/src/runtime/tool-grant-request-helper.ts +2 -1
- package/src/security/encrypted-store.ts +9 -5
- package/src/security/keychain-broker-client.ts +393 -0
- package/src/security/secure-keys.ts +106 -321
- package/src/tools/apps/executors.ts +73 -0
- package/src/tools/browser/auto-navigate.ts +15 -6
- package/src/tools/browser/chrome-cdp.ts +211 -0
- package/src/tools/browser/network-recorder.test.ts +83 -0
- package/src/tools/browser/network-recorder.ts +8 -7
- package/src/tools/browser/x-auto-navigate.ts +12 -6
- package/src/tools/credentials/policy-types.ts +24 -0
- package/src/tools/credentials/vault.ts +22 -27
- package/src/tools/network/script-proxy/session-manager.ts +47 -3
- package/src/tools/permission-checker.ts +1 -0
- package/src/tools/types.ts +2 -0
- package/src/tools/ui-surface/definitions.ts +1 -2
- package/src/tools/watch/watch-state.ts +2 -0
- package/src/__tests__/key-migration.test.ts +0 -240
- package/src/__tests__/keychain.test.ts +0 -286
- package/src/cli/core-commands.ts +0 -899
- package/src/security/keychain-to-encrypted-migration.ts +0 -66
- package/src/security/keychain.ts +0 -490
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
import type { Command } from "commander";
|
|
2
|
+
|
|
3
|
+
import { getDeliverableChannels } from "../channels/config.js";
|
|
4
|
+
import { initializeDb } from "../memory/db.js";
|
|
5
|
+
import { emitNotificationSignal } from "../notifications/emit-signal.js";
|
|
6
|
+
import { listEvents } from "../notifications/events-store.js";
|
|
7
|
+
import {
|
|
8
|
+
isNotificationSourceChannel,
|
|
9
|
+
isNotificationSourceEventName,
|
|
10
|
+
NOTIFICATION_SOURCE_CHANNELS,
|
|
11
|
+
NOTIFICATION_SOURCE_EVENT_NAMES,
|
|
12
|
+
} from "../notifications/signal.js";
|
|
13
|
+
import type { NotificationChannel } from "../notifications/types.js";
|
|
14
|
+
import { getCliLogger } from "../util/logger.js";
|
|
15
|
+
import { shouldOutputJson, writeOutput } from "./integrations.js";
|
|
16
|
+
|
|
17
|
+
const log = getCliLogger("cli");
|
|
18
|
+
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
// Help text builders
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
|
|
23
|
+
function buildSourceChannelsHelpBlock(): string {
|
|
24
|
+
const lines = NOTIFICATION_SOURCE_CHANNELS.map(
|
|
25
|
+
(c) => ` ${c.id.padEnd(20)} ${c.description}`,
|
|
26
|
+
);
|
|
27
|
+
return `\nSource channels:\n${lines.join("\n")}`;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function buildSourceEventNamesHelpBlock(): string {
|
|
31
|
+
const lines = NOTIFICATION_SOURCE_EVENT_NAMES.map(
|
|
32
|
+
(e) => ` ${e.id.padEnd(50)} ${e.description}`,
|
|
33
|
+
);
|
|
34
|
+
return `\nSource event names:\n${lines.join("\n")}`;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
38
|
+
// Command registration
|
|
39
|
+
// ---------------------------------------------------------------------------
|
|
40
|
+
|
|
41
|
+
export function registerNotificationsCommand(program: Command): void {
|
|
42
|
+
const notifications = program
|
|
43
|
+
.command("notifications")
|
|
44
|
+
.description(
|
|
45
|
+
"Send and inspect notifications through the unified notification router",
|
|
46
|
+
)
|
|
47
|
+
.option("--json", "Machine-readable compact JSON output");
|
|
48
|
+
|
|
49
|
+
notifications.addHelpText(
|
|
50
|
+
"after",
|
|
51
|
+
`
|
|
52
|
+
Notifications flow through a unified pipeline: a signal is emitted with a
|
|
53
|
+
source channel, event name, and attention hints. The decision engine evaluates
|
|
54
|
+
whether and where to deliver the notification based on connected channels,
|
|
55
|
+
urgency, and user preferences.
|
|
56
|
+
${buildSourceChannelsHelpBlock()}
|
|
57
|
+
${buildSourceEventNamesHelpBlock()}
|
|
58
|
+
|
|
59
|
+
Examples:
|
|
60
|
+
$ vellum notifications send --source-channel assistant_tool --source-event-name user.send_notification --message "Build finished"
|
|
61
|
+
$ vellum notifications send --source-channel scheduler --source-event-name reminder.fired --message "Stand-up in 5 minutes" --urgency high
|
|
62
|
+
$ vellum notifications send --source-channel watcher --source-event-name watcher.notification --message "File changed" --no-requires-action --is-async-background
|
|
63
|
+
$ vellum notifications send --source-channel assistant_tool --source-event-name user.send_notification --message "Deploy complete" --preferred-channels vellum,telegram --json`,
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
// -------------------------------------------------------------------------
|
|
67
|
+
// send
|
|
68
|
+
// -------------------------------------------------------------------------
|
|
69
|
+
|
|
70
|
+
notifications
|
|
71
|
+
.command("send")
|
|
72
|
+
.description("Send a notification through the unified notification router")
|
|
73
|
+
.requiredOption(
|
|
74
|
+
"--source-channel <channel>",
|
|
75
|
+
"Source channel producing this notification",
|
|
76
|
+
)
|
|
77
|
+
.requiredOption(
|
|
78
|
+
"--source-event-name <name>",
|
|
79
|
+
"Event name for audit, routing, and dedupe grouping",
|
|
80
|
+
)
|
|
81
|
+
.requiredOption(
|
|
82
|
+
"--message <message>",
|
|
83
|
+
"Notification message the user should receive",
|
|
84
|
+
)
|
|
85
|
+
.option("--title <title>", "Optional notification title")
|
|
86
|
+
.option(
|
|
87
|
+
"--urgency <urgency>",
|
|
88
|
+
"Urgency hint: low, medium, high (default: medium)",
|
|
89
|
+
)
|
|
90
|
+
.option(
|
|
91
|
+
"--requires-action",
|
|
92
|
+
"Whether the notification expects user action (default: true)",
|
|
93
|
+
)
|
|
94
|
+
.option(
|
|
95
|
+
"--no-requires-action",
|
|
96
|
+
"Mark that the notification does not expect user action",
|
|
97
|
+
)
|
|
98
|
+
.option(
|
|
99
|
+
"--is-async-background",
|
|
100
|
+
"Whether the event is asynchronous/background work (default: false)",
|
|
101
|
+
)
|
|
102
|
+
.option(
|
|
103
|
+
"--no-is-async-background",
|
|
104
|
+
"Mark that the event is not asynchronous/background work",
|
|
105
|
+
)
|
|
106
|
+
.option(
|
|
107
|
+
"--visible-in-source-now",
|
|
108
|
+
"Set true when user is already viewing the source context (default: false)",
|
|
109
|
+
)
|
|
110
|
+
.option(
|
|
111
|
+
"--no-visible-in-source-now",
|
|
112
|
+
"Mark that the user is not viewing the source context",
|
|
113
|
+
)
|
|
114
|
+
.option(
|
|
115
|
+
"--deadline-at <epoch>",
|
|
116
|
+
"Optional deadline timestamp in epoch milliseconds",
|
|
117
|
+
)
|
|
118
|
+
.option(
|
|
119
|
+
"--preferred-channels <channels>",
|
|
120
|
+
"Comma-separated channel hints (e.g. vellum,telegram,slack)",
|
|
121
|
+
)
|
|
122
|
+
.option(
|
|
123
|
+
"--session-id <id>",
|
|
124
|
+
"Source session or conversation ID (default: cli-<timestamp>)",
|
|
125
|
+
)
|
|
126
|
+
.option(
|
|
127
|
+
"--dedupe-key <key>",
|
|
128
|
+
"Optional dedupe key to suppress duplicate notifications",
|
|
129
|
+
)
|
|
130
|
+
.addHelpText(
|
|
131
|
+
"after",
|
|
132
|
+
`
|
|
133
|
+
Arguments:
|
|
134
|
+
--source-channel One of the registered source channels (see "vellum notifications --help")
|
|
135
|
+
--source-event-name One of the registered event names (see "vellum notifications --help")
|
|
136
|
+
--message The notification body text (required, must be non-empty)
|
|
137
|
+
|
|
138
|
+
Behavioral notes:
|
|
139
|
+
- The signal is emitted through the full notification pipeline: event store,
|
|
140
|
+
decision engine, deterministic checks, and channel dispatch.
|
|
141
|
+
- --requires-action defaults to true; use --no-requires-action to disable.
|
|
142
|
+
- --urgency defaults to medium if not specified.
|
|
143
|
+
- --preferred-channels are hints only; the decision engine may override them.
|
|
144
|
+
- --dedupe-key suppresses duplicate signals with the same key.
|
|
145
|
+
|
|
146
|
+
Examples:
|
|
147
|
+
$ vellum notifications send --source-channel assistant_tool --source-event-name user.send_notification --message "Task complete"
|
|
148
|
+
$ vellum notifications send --source-channel scheduler --source-event-name reminder.fired --message "Meeting in 5 min" --urgency high --title "Reminder"
|
|
149
|
+
$ vellum notifications send --source-channel watcher --source-event-name watcher.notification --message "Detected change" --no-requires-action --is-async-background --json`,
|
|
150
|
+
)
|
|
151
|
+
.action(
|
|
152
|
+
async (
|
|
153
|
+
opts: {
|
|
154
|
+
sourceChannel: string;
|
|
155
|
+
sourceEventName: string;
|
|
156
|
+
message: string;
|
|
157
|
+
title?: string;
|
|
158
|
+
urgency?: string;
|
|
159
|
+
requiresAction: boolean;
|
|
160
|
+
isAsyncBackground: boolean;
|
|
161
|
+
visibleInSourceNow: boolean;
|
|
162
|
+
deadlineAt?: string;
|
|
163
|
+
preferredChannels?: string;
|
|
164
|
+
sessionId?: string;
|
|
165
|
+
dedupeKey?: string;
|
|
166
|
+
},
|
|
167
|
+
cmd: Command,
|
|
168
|
+
) => {
|
|
169
|
+
try {
|
|
170
|
+
// Validate --source-channel
|
|
171
|
+
if (!isNotificationSourceChannel(opts.sourceChannel)) {
|
|
172
|
+
const validChannels = NOTIFICATION_SOURCE_CHANNELS.map(
|
|
173
|
+
(c) => c.id,
|
|
174
|
+
).join(", ");
|
|
175
|
+
writeOutput(cmd, {
|
|
176
|
+
ok: false,
|
|
177
|
+
error: `Invalid source channel "${opts.sourceChannel}". Valid values: ${validChannels}`,
|
|
178
|
+
});
|
|
179
|
+
process.exitCode = 1;
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Validate --source-event-name
|
|
184
|
+
if (!isNotificationSourceEventName(opts.sourceEventName)) {
|
|
185
|
+
const validEvents = NOTIFICATION_SOURCE_EVENT_NAMES.map(
|
|
186
|
+
(e) => e.id,
|
|
187
|
+
).join(", ");
|
|
188
|
+
writeOutput(cmd, {
|
|
189
|
+
ok: false,
|
|
190
|
+
error: `Invalid source event name "${opts.sourceEventName}". Valid values: ${validEvents}`,
|
|
191
|
+
});
|
|
192
|
+
process.exitCode = 1;
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Validate --message
|
|
197
|
+
const message = opts.message.trim();
|
|
198
|
+
if (message.length === 0) {
|
|
199
|
+
writeOutput(cmd, {
|
|
200
|
+
ok: false,
|
|
201
|
+
error: "Message must be a non-empty string",
|
|
202
|
+
});
|
|
203
|
+
process.exitCode = 1;
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Validate --urgency
|
|
208
|
+
const urgency = opts.urgency ?? "medium";
|
|
209
|
+
if (urgency !== "low" && urgency !== "medium" && urgency !== "high") {
|
|
210
|
+
writeOutput(cmd, {
|
|
211
|
+
ok: false,
|
|
212
|
+
error: `Invalid urgency "${opts.urgency}". Must be one of: low, medium, high`,
|
|
213
|
+
});
|
|
214
|
+
process.exitCode = 1;
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Validate --deadline-at
|
|
219
|
+
let deadlineAt: number | undefined;
|
|
220
|
+
if (opts.deadlineAt != null) {
|
|
221
|
+
const parsed = Number(opts.deadlineAt);
|
|
222
|
+
if (!Number.isFinite(parsed)) {
|
|
223
|
+
writeOutput(cmd, {
|
|
224
|
+
ok: false,
|
|
225
|
+
error: `Invalid deadline-at "${opts.deadlineAt}". Must be a finite number (epoch milliseconds)`,
|
|
226
|
+
});
|
|
227
|
+
process.exitCode = 1;
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
deadlineAt = parsed;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Validate --preferred-channels
|
|
234
|
+
let preferredChannels: NotificationChannel[] | undefined;
|
|
235
|
+
if (opts.preferredChannels) {
|
|
236
|
+
const deliverable = getDeliverableChannels();
|
|
237
|
+
const requested = opts.preferredChannels
|
|
238
|
+
.split(",")
|
|
239
|
+
.map((ch) => ch.trim())
|
|
240
|
+
.filter((ch) => ch.length > 0);
|
|
241
|
+
|
|
242
|
+
for (const ch of requested) {
|
|
243
|
+
if (!deliverable.includes(ch as NotificationChannel)) {
|
|
244
|
+
writeOutput(cmd, {
|
|
245
|
+
ok: false,
|
|
246
|
+
error: `Invalid preferred channel "${ch}". Valid deliverable channels: ${deliverable.join(", ")}`,
|
|
247
|
+
});
|
|
248
|
+
process.exitCode = 1;
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
preferredChannels = requested as NotificationChannel[];
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
initializeDb();
|
|
256
|
+
|
|
257
|
+
const sourceSessionId = opts.sessionId ?? `cli-${Date.now()}`;
|
|
258
|
+
|
|
259
|
+
const result = await emitNotificationSignal({
|
|
260
|
+
sourceEventName: opts.sourceEventName,
|
|
261
|
+
sourceChannel: opts.sourceChannel,
|
|
262
|
+
sourceSessionId,
|
|
263
|
+
attentionHints: {
|
|
264
|
+
requiresAction: opts.requiresAction ?? true,
|
|
265
|
+
urgency,
|
|
266
|
+
deadlineAt,
|
|
267
|
+
isAsyncBackground: opts.isAsyncBackground ?? false,
|
|
268
|
+
visibleInSourceNow: opts.visibleInSourceNow ?? false,
|
|
269
|
+
},
|
|
270
|
+
contextPayload: {
|
|
271
|
+
requestedMessage: message,
|
|
272
|
+
requestedBySource: opts.sourceChannel,
|
|
273
|
+
...(opts.title ? { requestedTitle: opts.title } : {}),
|
|
274
|
+
...(preferredChannels?.length ? { preferredChannels } : {}),
|
|
275
|
+
},
|
|
276
|
+
...(opts.dedupeKey ? { dedupeKey: opts.dedupeKey } : {}),
|
|
277
|
+
throwOnError: true,
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
writeOutput(cmd, {
|
|
281
|
+
ok: true,
|
|
282
|
+
signalId: result.signalId,
|
|
283
|
+
dispatched: result.dispatched,
|
|
284
|
+
reason: result.reason,
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
if (!shouldOutputJson(cmd)) {
|
|
288
|
+
log.info(
|
|
289
|
+
`Signal ${result.signalId} emitted (dispatched: ${result.dispatched})`,
|
|
290
|
+
);
|
|
291
|
+
if (result.reason) {
|
|
292
|
+
log.info(` Reason: ${result.reason}`);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
} catch (err) {
|
|
296
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
297
|
+
writeOutput(cmd, { ok: false, error: message });
|
|
298
|
+
process.exitCode = 1;
|
|
299
|
+
}
|
|
300
|
+
},
|
|
301
|
+
);
|
|
302
|
+
|
|
303
|
+
// -------------------------------------------------------------------------
|
|
304
|
+
// list
|
|
305
|
+
// -------------------------------------------------------------------------
|
|
306
|
+
|
|
307
|
+
notifications
|
|
308
|
+
.command("list")
|
|
309
|
+
.description("List recent notification events from the local event store")
|
|
310
|
+
.option("--limit <n>", "Maximum number of events to return (default: 20)")
|
|
311
|
+
.option("--source-event-name <name>", "Filter by source event name")
|
|
312
|
+
.addHelpText(
|
|
313
|
+
"after",
|
|
314
|
+
`
|
|
315
|
+
Reads from the local notification events store, ordered by creation time
|
|
316
|
+
(newest first). Each event represents a signal that was emitted through the
|
|
317
|
+
notification pipeline.
|
|
318
|
+
${buildSourceEventNamesHelpBlock()}
|
|
319
|
+
|
|
320
|
+
Examples:
|
|
321
|
+
$ vellum notifications list
|
|
322
|
+
$ vellum notifications list --limit 5
|
|
323
|
+
$ vellum notifications list --source-event-name reminder.fired
|
|
324
|
+
$ vellum notifications list --source-event-name reminder.fired --limit 10 --json`,
|
|
325
|
+
)
|
|
326
|
+
.action(
|
|
327
|
+
(
|
|
328
|
+
opts: {
|
|
329
|
+
limit?: string;
|
|
330
|
+
sourceEventName?: string;
|
|
331
|
+
},
|
|
332
|
+
cmd: Command,
|
|
333
|
+
) => {
|
|
334
|
+
try {
|
|
335
|
+
// Validate --source-event-name (accept any non-empty string; custom
|
|
336
|
+
// event names are valid since skills can emit arbitrary names)
|
|
337
|
+
if (
|
|
338
|
+
opts.sourceEventName != null &&
|
|
339
|
+
opts.sourceEventName.trim().length === 0
|
|
340
|
+
) {
|
|
341
|
+
writeOutput(cmd, {
|
|
342
|
+
ok: false,
|
|
343
|
+
error: "Source event name must be a non-empty string",
|
|
344
|
+
});
|
|
345
|
+
process.exitCode = 1;
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// Parse and validate --limit
|
|
350
|
+
let limit = 20;
|
|
351
|
+
if (opts.limit != null) {
|
|
352
|
+
const parsed = Number(opts.limit);
|
|
353
|
+
if (
|
|
354
|
+
!Number.isFinite(parsed) ||
|
|
355
|
+
!Number.isInteger(parsed) ||
|
|
356
|
+
parsed < 1
|
|
357
|
+
) {
|
|
358
|
+
writeOutput(cmd, {
|
|
359
|
+
ok: false,
|
|
360
|
+
error: `Invalid limit "${opts.limit}". Must be a positive integer`,
|
|
361
|
+
});
|
|
362
|
+
process.exitCode = 1;
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
limit = parsed;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
initializeDb();
|
|
369
|
+
|
|
370
|
+
const rows = listEvents({
|
|
371
|
+
limit,
|
|
372
|
+
sourceEventName: opts.sourceEventName,
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
const events = rows.map((row) => ({
|
|
376
|
+
id: row.id,
|
|
377
|
+
sourceEventName: row.sourceEventName,
|
|
378
|
+
sourceChannel: row.sourceChannel,
|
|
379
|
+
sourceSessionId: row.sourceSessionId,
|
|
380
|
+
urgency: (JSON.parse(row.attentionHintsJson) as { urgency: string })
|
|
381
|
+
.urgency,
|
|
382
|
+
dedupeKey: row.dedupeKey,
|
|
383
|
+
createdAt: new Date(row.createdAt).toISOString(),
|
|
384
|
+
}));
|
|
385
|
+
|
|
386
|
+
writeOutput(cmd, { ok: true, events });
|
|
387
|
+
|
|
388
|
+
if (!shouldOutputJson(cmd)) {
|
|
389
|
+
if (events.length === 0) {
|
|
390
|
+
log.info("No notification events found");
|
|
391
|
+
} else {
|
|
392
|
+
log.info(`${events.length} event(s):\n`);
|
|
393
|
+
for (const event of events) {
|
|
394
|
+
log.info(
|
|
395
|
+
` ${event.createdAt} ${event.sourceEventName} ${event.urgency} ${event.sourceChannel}`,
|
|
396
|
+
);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
} catch (err) {
|
|
401
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
402
|
+
writeOutput(cmd, { ok: false, error: message });
|
|
403
|
+
process.exitCode = 1;
|
|
404
|
+
}
|
|
405
|
+
},
|
|
406
|
+
);
|
|
407
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
|
|
3
|
+
import { Command } from "commander";
|
|
4
|
+
|
|
5
|
+
import { registerHooksCommand } from "../hooks/cli.js";
|
|
6
|
+
import { registerAmazonCommand } from "./amazon.js";
|
|
7
|
+
import { registerAuditCommand } from "./audit.js";
|
|
8
|
+
import { registerAutonomyCommand } from "./autonomy.js";
|
|
9
|
+
import { registerChannelsCommand } from "./channels.js";
|
|
10
|
+
import { registerCompletionsCommand } from "./completions.js";
|
|
11
|
+
import { registerConfigCommand } from "./config.js";
|
|
12
|
+
import { registerContactsCommand } from "./contacts.js";
|
|
13
|
+
import { registerCredentialsCommand } from "./credentials.js";
|
|
14
|
+
import { registerDefaultAction } from "./default-action.js";
|
|
15
|
+
import { registerDevCommand } from "./dev.js";
|
|
16
|
+
import { registerDoctorCommand } from "./doctor.js";
|
|
17
|
+
import { registerEmailCommand } from "./email.js";
|
|
18
|
+
import { registerInfluencerCommand } from "./influencer.js";
|
|
19
|
+
import { registerIntegrationsCommand } from "./integrations.js";
|
|
20
|
+
import { registerKeysCommand } from "./keys.js";
|
|
21
|
+
import { registerMapCommand } from "./map.js";
|
|
22
|
+
import { registerMcpCommand } from "./mcp.js";
|
|
23
|
+
import { registerMemoryCommand } from "./memory.js";
|
|
24
|
+
import { registerNotificationsCommand } from "./notifications.js";
|
|
25
|
+
import { registerSequenceCommand } from "./sequence.js";
|
|
26
|
+
import { registerSessionsCommand } from "./sessions.js";
|
|
27
|
+
import { registerTrustCommand } from "./trust.js";
|
|
28
|
+
import { registerTwitterCommand } from "./twitter.js";
|
|
29
|
+
|
|
30
|
+
const require = createRequire(import.meta.url);
|
|
31
|
+
const { version } = require("../../package.json") as { version: string };
|
|
32
|
+
|
|
33
|
+
export function buildCliProgram(): Command {
|
|
34
|
+
const program = new Command();
|
|
35
|
+
|
|
36
|
+
program.name("assistant").description("Local AI assistant").version(version);
|
|
37
|
+
|
|
38
|
+
registerDefaultAction(program);
|
|
39
|
+
registerDevCommand(program);
|
|
40
|
+
registerSessionsCommand(program);
|
|
41
|
+
registerConfigCommand(program);
|
|
42
|
+
registerKeysCommand(program);
|
|
43
|
+
registerCredentialsCommand(program);
|
|
44
|
+
registerTrustCommand(program);
|
|
45
|
+
registerMemoryCommand(program);
|
|
46
|
+
registerAuditCommand(program);
|
|
47
|
+
registerDoctorCommand(program);
|
|
48
|
+
registerHooksCommand(program);
|
|
49
|
+
registerMcpCommand(program);
|
|
50
|
+
registerEmailCommand(program);
|
|
51
|
+
registerIntegrationsCommand(program);
|
|
52
|
+
registerContactsCommand(program);
|
|
53
|
+
registerChannelsCommand(program);
|
|
54
|
+
registerAmazonCommand(program);
|
|
55
|
+
registerAutonomyCommand(program);
|
|
56
|
+
registerCompletionsCommand(program);
|
|
57
|
+
registerNotificationsCommand(program);
|
|
58
|
+
|
|
59
|
+
registerTwitterCommand(program);
|
|
60
|
+
registerMapCommand(program);
|
|
61
|
+
registerInfluencerCommand(program);
|
|
62
|
+
registerSequenceCommand(program);
|
|
63
|
+
|
|
64
|
+
return program;
|
|
65
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// Keep this snapshot in sync with buildCliProgram().helpInformation() for the
|
|
2
|
+
// top-level assistant CLI. It deliberately avoids importing the CLI command
|
|
3
|
+
// graph so prompt assembly stays side-effect-free.
|
|
4
|
+
export const CLI_HELP_REFERENCE = `Usage: assistant [options] [command]
|
|
5
|
+
|
|
6
|
+
Local AI assistant
|
|
7
|
+
|
|
8
|
+
Options:
|
|
9
|
+
-V, --version output the version number
|
|
10
|
+
-h, --help display help for command
|
|
11
|
+
|
|
12
|
+
Commands:
|
|
13
|
+
dev [options] Run the assistant in dev mode
|
|
14
|
+
sessions Manage sessions
|
|
15
|
+
config Manage configuration
|
|
16
|
+
keys Manage API keys in secure storage
|
|
17
|
+
credentials [options] Manage credentials in the encrypted vault (API keys,
|
|
18
|
+
tokens, passwords)
|
|
19
|
+
trust Manage trust rules
|
|
20
|
+
memory Manage long-term memory indexing/retrieval
|
|
21
|
+
audit [options] Show recent tool invocations
|
|
22
|
+
doctor Run diagnostic checks
|
|
23
|
+
hooks Manage hooks
|
|
24
|
+
mcp Manage MCP (Model Context Protocol) servers
|
|
25
|
+
email [options] Email operations (provider-agnostic)
|
|
26
|
+
integrations [options] Read integration and ingress status through the
|
|
27
|
+
gateway API
|
|
28
|
+
contacts [options] Manage and query the contact graph
|
|
29
|
+
channels [options] Query channel status
|
|
30
|
+
amazon [options] Shop on Amazon and Amazon Fresh. Requires a session
|
|
31
|
+
imported from a Ride Shotgun recording.
|
|
32
|
+
autonomy [options] View and configure autonomy tiers
|
|
33
|
+
completions <shell> Generate shell completion script (e.g. vellum
|
|
34
|
+
completions bash >> ~/.bashrc)
|
|
35
|
+
notifications [options] Send and inspect notifications through the unified
|
|
36
|
+
notification router
|
|
37
|
+
x|twitter [options] Post on X and manage connections. Supports OAuth
|
|
38
|
+
(official API) and browser session paths.
|
|
39
|
+
map [options] <domain> Auto-navigate a domain and produce a deduplicated API
|
|
40
|
+
map. Launches Chrome with CDP, starts a Ride Shotgun
|
|
41
|
+
learn session, then analyzes captured network
|
|
42
|
+
traffic.
|
|
43
|
+
influencer [options] Research influencers on Instagram, TikTok, and
|
|
44
|
+
X/Twitter. Uses the Chrome extension relay to browse
|
|
45
|
+
each platform. Requires the user to be logged in on
|
|
46
|
+
each platform in Chrome.
|
|
47
|
+
sequence [options] Manage email sequences
|
|
48
|
+
`;
|