@vellumai/assistant 0.4.49 → 0.4.50
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 +24 -33
- package/README.md +3 -3
- package/docs/architecture/memory.md +180 -119
- package/package.json +2 -2
- package/src/__tests__/agent-loop.test.ts +3 -1
- package/src/__tests__/anthropic-provider.test.ts +114 -23
- package/src/__tests__/approval-cascade.test.ts +1 -15
- package/src/__tests__/approval-routes-http.test.ts +2 -0
- package/src/__tests__/assistant-feature-flag-guard.test.ts +0 -23
- package/src/__tests__/canonical-guardian-store.test.ts +95 -0
- package/src/__tests__/checker.test.ts +13 -0
- package/src/__tests__/config-schema.test.ts +1 -68
- package/src/__tests__/context-memory-e2e.test.ts +11 -100
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +8 -0
- package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -0
- package/src/__tests__/credential-security-e2e.test.ts +1 -0
- package/src/__tests__/credential-vault-unit.test.ts +4 -0
- package/src/__tests__/credential-vault.test.ts +13 -1
- package/src/__tests__/cu-unified-flow.test.ts +532 -0
- package/src/__tests__/date-context.test.ts +93 -77
- package/src/__tests__/deterministic-verification-control-plane.test.ts +64 -0
- package/src/__tests__/guardian-routing-invariants.test.ts +93 -0
- package/src/__tests__/history-repair.test.ts +245 -0
- package/src/__tests__/host-cu-proxy.test.ts +165 -3
- package/src/__tests__/http-user-message-parity.test.ts +1 -0
- package/src/__tests__/invite-redemption-service.test.ts +65 -1
- package/src/__tests__/keychain-broker-client.test.ts +4 -4
- package/src/__tests__/memory-context-benchmark.benchmark.test.ts +56 -18
- package/src/__tests__/memory-lifecycle-e2e.test.ts +244 -387
- package/src/__tests__/memory-recall-quality.test.ts +244 -407
- package/src/__tests__/memory-regressions.experimental.test.ts +126 -101
- package/src/__tests__/memory-regressions.test.ts +477 -2841
- package/src/__tests__/memory-retrieval.benchmark.test.ts +33 -150
- package/src/__tests__/memory-upsert-concurrency.test.ts +5 -244
- package/src/__tests__/mime-builder.test.ts +28 -0
- package/src/__tests__/native-web-search.test.ts +1 -0
- package/src/__tests__/oauth-cli.test.ts +572 -5
- package/src/__tests__/oauth-store.test.ts +120 -6
- package/src/__tests__/qdrant-collection-migration.test.ts +53 -8
- package/src/__tests__/registry.test.ts +0 -1
- package/src/__tests__/relay-server.test.ts +46 -1
- package/src/__tests__/schedule-tools.test.ts +32 -0
- package/src/__tests__/script-proxy-certs.test.ts +1 -1
- package/src/__tests__/secret-onetime-send.test.ts +1 -0
- package/src/__tests__/secure-keys.test.ts +7 -2
- package/src/__tests__/send-endpoint-busy.test.ts +3 -0
- package/src/__tests__/session-abort-tool-results.test.ts +1 -14
- package/src/__tests__/session-agent-loop-overflow.test.ts +1583 -0
- package/src/__tests__/session-agent-loop.test.ts +19 -15
- package/src/__tests__/session-confirmation-signals.test.ts +1 -15
- package/src/__tests__/session-error.test.ts +124 -2
- package/src/__tests__/session-history-web-search.test.ts +918 -0
- package/src/__tests__/session-pre-run-repair.test.ts +1 -14
- package/src/__tests__/session-provider-retry-repair.test.ts +25 -28
- package/src/__tests__/session-queue.test.ts +37 -27
- package/src/__tests__/session-runtime-assembly.test.ts +54 -0
- package/src/__tests__/session-slash-known.test.ts +1 -15
- package/src/__tests__/session-slash-queue.test.ts +1 -15
- package/src/__tests__/session-slash-unknown.test.ts +1 -15
- package/src/__tests__/session-workspace-cache-state.test.ts +3 -33
- package/src/__tests__/session-workspace-injection.test.ts +3 -37
- package/src/__tests__/session-workspace-tool-tracking.test.ts +3 -37
- package/src/__tests__/skills-install-extract.test.ts +93 -0
- package/src/__tests__/skillssh-registry.test.ts +451 -0
- package/src/__tests__/trust-store.test.ts +15 -0
- package/src/__tests__/voice-invite-redemption.test.ts +32 -1
- package/src/agent/ax-tree-compaction.test.ts +51 -0
- package/src/agent/loop.ts +39 -12
- package/src/approvals/AGENTS.md +1 -1
- package/src/approvals/guardian-request-resolvers.ts +14 -2
- package/src/bundler/compiler-tools.ts +66 -2
- package/src/calls/call-domain.ts +132 -0
- package/src/calls/call-store.ts +6 -0
- package/src/calls/relay-server.ts +43 -5
- package/src/calls/relay-setup-router.ts +17 -1
- package/src/calls/twilio-config.ts +1 -1
- package/src/calls/types.ts +3 -1
- package/src/cli/commands/doctor.ts +4 -3
- package/src/cli/commands/mcp.ts +46 -59
- package/src/cli/commands/memory.ts +16 -165
- package/src/cli/commands/oauth/apps.ts +31 -2
- package/src/cli/commands/oauth/connections.ts +431 -97
- package/src/cli/commands/oauth/providers.ts +15 -1
- package/src/cli/commands/sessions.ts +5 -2
- package/src/cli/commands/skills.ts +173 -1
- package/src/cli/http-client.ts +0 -20
- package/src/cli/main-screen.tsx +2 -2
- package/src/cli/program.ts +5 -6
- package/src/cli.ts +4 -10
- package/src/config/bundled-skills/computer-use/TOOLS.json +1 -1
- package/src/config/bundled-skills/computer-use/tools/computer-use-observe.ts +12 -0
- package/src/config/bundled-tool-registry.ts +2 -5
- package/src/config/schema.ts +1 -12
- package/src/config/schemas/memory-lifecycle.ts +0 -9
- package/src/config/schemas/memory-processing.ts +0 -180
- package/src/config/schemas/memory-retrieval.ts +32 -104
- package/src/config/schemas/memory.ts +0 -10
- package/src/config/types.ts +0 -4
- package/src/context/window-manager.ts +4 -1
- package/src/daemon/config-watcher.ts +61 -3
- package/src/daemon/daemon-control.ts +1 -1
- package/src/daemon/date-context.ts +114 -31
- package/src/daemon/handlers/sessions.ts +18 -13
- package/src/daemon/handlers/skills.ts +20 -1
- package/src/daemon/history-repair.ts +72 -8
- package/src/daemon/host-cu-proxy.ts +55 -26
- package/src/daemon/lifecycle.ts +31 -3
- package/src/daemon/mcp-reload-service.ts +2 -2
- package/src/daemon/message-types/computer-use.ts +1 -12
- package/src/daemon/message-types/memory.ts +4 -16
- package/src/daemon/message-types/messages.ts +1 -0
- package/src/daemon/message-types/sessions.ts +4 -0
- package/src/daemon/server.ts +12 -1
- package/src/daemon/session-agent-loop-handlers.ts +38 -0
- package/src/daemon/session-agent-loop.ts +334 -48
- package/src/daemon/session-error.ts +89 -6
- package/src/daemon/session-history.ts +17 -7
- package/src/daemon/session-media-retry.ts +6 -2
- package/src/daemon/session-memory.ts +69 -149
- package/src/daemon/session-process.ts +10 -1
- package/src/daemon/session-runtime-assembly.ts +49 -19
- package/src/daemon/session-surfaces.ts +4 -1
- package/src/daemon/session-tool-setup.ts +7 -1
- package/src/daemon/session.ts +12 -2
- package/src/instrument.ts +61 -1
- package/src/memory/admin.ts +2 -191
- package/src/memory/canonical-guardian-store.ts +38 -2
- package/src/memory/conversation-crud.ts +0 -33
- package/src/memory/conversation-queries.ts +22 -3
- package/src/memory/db-init.ts +28 -0
- package/src/memory/embedding-backend.ts +84 -8
- package/src/memory/embedding-types.ts +9 -1
- package/src/memory/indexer.ts +7 -46
- package/src/memory/items-extractor.ts +274 -76
- package/src/memory/job-handlers/backfill.ts +2 -127
- package/src/memory/job-handlers/cleanup.ts +2 -16
- package/src/memory/job-handlers/extraction.ts +2 -138
- package/src/memory/job-handlers/index-maintenance.ts +1 -6
- package/src/memory/job-handlers/summarization.ts +3 -148
- package/src/memory/job-utils.ts +21 -59
- package/src/memory/jobs-store.ts +1 -159
- package/src/memory/jobs-worker.ts +9 -52
- package/src/memory/migrations/104-core-indexes.ts +3 -3
- package/src/memory/migrations/149-oauth-tables.ts +2 -0
- package/src/memory/migrations/150-oauth-apps-client-secret-path.ts +98 -0
- package/src/memory/migrations/151-oauth-providers-ping-url.ts +11 -0
- package/src/memory/migrations/152-memory-item-supersession.ts +44 -0
- package/src/memory/migrations/153-drop-entity-tables.ts +15 -0
- package/src/memory/migrations/154-drop-fts.ts +20 -0
- package/src/memory/migrations/155-drop-conflicts.ts +7 -0
- package/src/memory/migrations/156-call-session-invite-metadata.ts +24 -0
- package/src/memory/migrations/index.ts +7 -0
- package/src/memory/qdrant-client.ts +148 -51
- package/src/memory/raw-query.ts +1 -1
- package/src/memory/retriever.test.ts +294 -273
- package/src/memory/retriever.ts +421 -645
- package/src/memory/schema/calls.ts +2 -0
- package/src/memory/schema/memory-core.ts +3 -48
- package/src/memory/schema/oauth.ts +2 -0
- package/src/memory/search/formatting.ts +263 -176
- package/src/memory/search/lexical.ts +1 -254
- package/src/memory/search/ranking.ts +0 -455
- package/src/memory/search/semantic.ts +100 -14
- package/src/memory/search/staleness.ts +47 -0
- package/src/memory/search/tier-classifier.ts +21 -0
- package/src/memory/search/types.ts +15 -77
- package/src/memory/task-memory-cleanup.ts +4 -6
- package/src/messaging/providers/gmail/mime-builder.ts +17 -7
- package/src/oauth/byo-connection.test.ts +8 -1
- package/src/oauth/oauth-store.ts +113 -27
- package/src/oauth/seed-providers.ts +6 -0
- package/src/oauth/token-persistence.ts +11 -3
- package/src/permissions/defaults.ts +1 -0
- package/src/permissions/trust-store.ts +23 -1
- package/src/playbooks/playbook-compiler.ts +1 -1
- package/src/prompts/system-prompt.ts +18 -2
- package/src/providers/anthropic/client.ts +56 -126
- package/src/providers/types.ts +7 -1
- package/src/runtime/AGENTS.md +9 -0
- package/src/runtime/auth/route-policy.ts +6 -3
- package/src/runtime/guardian-reply-router.ts +24 -22
- package/src/runtime/http-server.ts +2 -2
- package/src/runtime/invite-redemption-service.ts +19 -1
- package/src/runtime/invite-service.ts +25 -0
- package/src/runtime/pending-interactions.ts +2 -2
- package/src/runtime/routes/brain-graph-routes.ts +10 -90
- package/src/runtime/routes/conversation-routes.ts +9 -1
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +21 -12
- package/src/runtime/routes/memory-item-routes.test.ts +754 -0
- package/src/runtime/routes/memory-item-routes.ts +503 -0
- package/src/runtime/routes/session-management-routes.ts +3 -3
- package/src/runtime/routes/settings-routes.ts +2 -2
- package/src/runtime/routes/trust-rules-routes.ts +14 -0
- package/src/runtime/routes/workspace-routes.ts +2 -1
- package/src/security/keychain-broker-client.ts +17 -4
- package/src/security/secure-keys.ts +25 -3
- package/src/security/token-manager.ts +36 -36
- package/src/skills/catalog-install.ts +74 -18
- package/src/skills/skillssh-registry.ts +503 -0
- package/src/tools/assets/search.ts +5 -1
- package/src/tools/computer-use/definitions.ts +0 -10
- package/src/tools/computer-use/registry.ts +1 -1
- package/src/tools/credentials/vault.ts +1 -3
- package/src/tools/memory/definitions.ts +4 -13
- package/src/tools/memory/handlers.test.ts +83 -103
- package/src/tools/memory/handlers.ts +50 -85
- package/src/tools/schedule/create.ts +8 -1
- package/src/tools/schedule/update.ts +8 -1
- package/src/tools/skills/load.ts +25 -2
- package/src/__tests__/clarification-resolver.test.ts +0 -193
- package/src/__tests__/conflict-intent-tokenization.test.ts +0 -160
- package/src/__tests__/conflict-policy.test.ts +0 -269
- package/src/__tests__/conflict-store.test.ts +0 -372
- package/src/__tests__/contradiction-checker.test.ts +0 -361
- package/src/__tests__/entity-extractor.test.ts +0 -211
- package/src/__tests__/entity-search.test.ts +0 -1117
- package/src/__tests__/profile-compiler.test.ts +0 -392
- package/src/__tests__/session-conflict-gate.test.ts +0 -1228
- package/src/__tests__/session-profile-injection.test.ts +0 -557
- package/src/config/bundled-skills/knowledge-graph/SKILL.md +0 -25
- package/src/config/bundled-skills/knowledge-graph/TOOLS.json +0 -66
- package/src/config/bundled-skills/knowledge-graph/tools/graph-query.ts +0 -211
- package/src/daemon/session-conflict-gate.ts +0 -167
- package/src/daemon/session-dynamic-profile.ts +0 -77
- package/src/memory/clarification-resolver.ts +0 -417
- package/src/memory/conflict-intent.ts +0 -205
- package/src/memory/conflict-policy.ts +0 -127
- package/src/memory/conflict-store.ts +0 -410
- package/src/memory/contradiction-checker.ts +0 -508
- package/src/memory/entity-extractor.ts +0 -535
- package/src/memory/format-recall.ts +0 -47
- package/src/memory/fts-reconciler.ts +0 -165
- package/src/memory/job-handlers/conflict.ts +0 -200
- package/src/memory/profile-compiler.ts +0 -195
- package/src/memory/recall-cache.ts +0 -117
- package/src/memory/search/entity.ts +0 -535
- package/src/memory/search/query-expansion.test.ts +0 -70
- package/src/memory/search/query-expansion.ts +0 -118
- package/src/runtime/routes/mcp-routes.ts +0 -20
|
@@ -1,18 +1,30 @@
|
|
|
1
1
|
import type { Command } from "commander";
|
|
2
2
|
|
|
3
|
+
import { orchestrateOAuthConnect } from "../../../oauth/connect-orchestrator.js";
|
|
3
4
|
import {
|
|
4
5
|
disconnectOAuthProvider,
|
|
6
|
+
getAppByProviderAndClientId,
|
|
5
7
|
getConnection,
|
|
6
8
|
getConnectionByProvider,
|
|
9
|
+
getMostRecentAppByProvider,
|
|
10
|
+
getProvider,
|
|
7
11
|
listConnections,
|
|
8
12
|
} from "../../../oauth/oauth-store.js";
|
|
13
|
+
import {
|
|
14
|
+
getProviderBehavior,
|
|
15
|
+
resolveService,
|
|
16
|
+
} from "../../../oauth/provider-behaviors.js";
|
|
9
17
|
import { credentialKey } from "../../../security/credential-key.js";
|
|
10
|
-
import {
|
|
18
|
+
import {
|
|
19
|
+
deleteSecureKeyAsync,
|
|
20
|
+
getSecureKey,
|
|
21
|
+
} from "../../../security/secure-keys.js";
|
|
11
22
|
import { withValidToken } from "../../../security/token-manager.js";
|
|
12
23
|
import {
|
|
13
24
|
assertMetadataWritable,
|
|
14
25
|
deleteCredentialMetadata,
|
|
15
26
|
} from "../../../tools/credentials/metadata-store.js";
|
|
27
|
+
import { isLinux, isMacOS } from "../../../util/platform.js";
|
|
16
28
|
import { getCliLogger } from "../../logger.js";
|
|
17
29
|
import { shouldOutputJson, writeOutput } from "../../output.js";
|
|
18
30
|
|
|
@@ -74,9 +86,14 @@ token expiry, refresh token availability, account info, and status.
|
|
|
74
86
|
Examples:
|
|
75
87
|
$ assistant oauth connections list
|
|
76
88
|
$ assistant oauth connections list --provider integration:gmail
|
|
89
|
+
$ assistant oauth connections list --client-id abc123
|
|
77
90
|
$ assistant oauth connections get --id <uuid>
|
|
78
91
|
$ assistant oauth connections get --provider integration:gmail
|
|
79
|
-
$ assistant oauth connections
|
|
92
|
+
$ assistant oauth connections get --provider integration:gmail --client-id abc123
|
|
93
|
+
$ assistant oauth connections token integration:twitter
|
|
94
|
+
$ assistant oauth connections ping integration:gmail
|
|
95
|
+
$ assistant oauth connections connect integration:gmail
|
|
96
|
+
$ assistant oauth connections connect integration:gmail --open-browser`,
|
|
80
97
|
);
|
|
81
98
|
|
|
82
99
|
// ---------------------------------------------------------------------------
|
|
@@ -90,21 +107,25 @@ Examples:
|
|
|
90
107
|
"--provider <key>",
|
|
91
108
|
"Filter by provider key (e.g. integration:gmail)",
|
|
92
109
|
)
|
|
110
|
+
.option("--client-id <id>", "Filter by OAuth client ID")
|
|
93
111
|
.addHelpText(
|
|
94
112
|
"after",
|
|
95
113
|
`
|
|
96
|
-
Lists all OAuth connections, optionally filtered by provider key.
|
|
114
|
+
Lists all OAuth connections, optionally filtered by provider key and/or client ID.
|
|
97
115
|
|
|
98
116
|
Each connection shows its ID, provider, account info, granted scopes, token
|
|
99
117
|
expiry, refresh token availability, and status.
|
|
100
118
|
|
|
101
119
|
Examples:
|
|
102
120
|
$ assistant oauth connections list
|
|
103
|
-
$ assistant oauth connections list --provider integration:gmail
|
|
121
|
+
$ assistant oauth connections list --provider integration:gmail
|
|
122
|
+
$ assistant oauth connections list --client-id abc123`,
|
|
104
123
|
)
|
|
105
|
-
.action((opts: { provider?: string }, cmd: Command) => {
|
|
124
|
+
.action((opts: { provider?: string; clientId?: string }, cmd: Command) => {
|
|
106
125
|
try {
|
|
107
|
-
const rows = listConnections(opts.provider).map(
|
|
126
|
+
const rows = listConnections(opts.provider, opts.clientId).map(
|
|
127
|
+
formatConnectionRow,
|
|
128
|
+
);
|
|
108
129
|
|
|
109
130
|
if (!shouldOutputJson(cmd)) {
|
|
110
131
|
log.info(`Found ${rows.length} connection(s)`);
|
|
@@ -130,6 +151,10 @@ Examples:
|
|
|
130
151
|
"--provider <key>",
|
|
131
152
|
"Provider key (returns most recent active connection)",
|
|
132
153
|
)
|
|
154
|
+
.option(
|
|
155
|
+
"--client-id <id>",
|
|
156
|
+
"Filter by OAuth client ID (used with --provider)",
|
|
157
|
+
)
|
|
133
158
|
.addHelpText(
|
|
134
159
|
"after",
|
|
135
160
|
`
|
|
@@ -140,39 +165,45 @@ Two lookup modes are supported:
|
|
|
140
165
|
|
|
141
166
|
2. By provider (returns the most recent active connection):
|
|
142
167
|
$ assistant oauth connections get --provider integration:gmail
|
|
168
|
+
$ assistant oauth connections get --provider integration:gmail --client-id abc123
|
|
143
169
|
|
|
144
170
|
At least --id or --provider must be specified.`,
|
|
145
171
|
)
|
|
146
|
-
.action(
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
172
|
+
.action(
|
|
173
|
+
(
|
|
174
|
+
opts: { id?: string; provider?: string; clientId?: string },
|
|
175
|
+
cmd: Command,
|
|
176
|
+
) => {
|
|
177
|
+
try {
|
|
178
|
+
let row;
|
|
179
|
+
|
|
180
|
+
if (opts.id) {
|
|
181
|
+
row = getConnection(opts.id);
|
|
182
|
+
} else if (opts.provider) {
|
|
183
|
+
row = getConnectionByProvider(opts.provider, opts.clientId);
|
|
184
|
+
} else {
|
|
185
|
+
writeOutput(cmd, {
|
|
186
|
+
ok: false,
|
|
187
|
+
error: "Provide --id or --provider",
|
|
188
|
+
});
|
|
189
|
+
process.exitCode = 1;
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (!row) {
|
|
194
|
+
writeOutput(cmd, { ok: false, error: "Connection not found" });
|
|
195
|
+
process.exitCode = 1;
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
writeOutput(cmd, formatConnectionRow(row));
|
|
200
|
+
} catch (err) {
|
|
201
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
202
|
+
writeOutput(cmd, { ok: false, error: message });
|
|
165
203
|
process.exitCode = 1;
|
|
166
|
-
return;
|
|
167
204
|
}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
} catch (err) {
|
|
171
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
172
|
-
writeOutput(cmd, { ok: false, error: message });
|
|
173
|
-
process.exitCode = 1;
|
|
174
|
-
}
|
|
175
|
-
});
|
|
205
|
+
},
|
|
206
|
+
);
|
|
176
207
|
|
|
177
208
|
// ---------------------------------------------------------------------------
|
|
178
209
|
// connections token <provider-key>
|
|
@@ -183,6 +214,10 @@ At least --id or --provider must be specified.`,
|
|
|
183
214
|
.description(
|
|
184
215
|
"Print a valid OAuth access token for a provider, refreshing if expired",
|
|
185
216
|
)
|
|
217
|
+
.option(
|
|
218
|
+
"--client-id <id>",
|
|
219
|
+
"Filter by OAuth client ID when multiple apps exist for the provider",
|
|
220
|
+
)
|
|
186
221
|
.addHelpText(
|
|
187
222
|
"after",
|
|
188
223
|
`
|
|
@@ -199,22 +234,135 @@ Exits with code 1 if no access token exists or refresh fails.
|
|
|
199
234
|
|
|
200
235
|
Examples:
|
|
201
236
|
$ assistant oauth connections token integration:twitter
|
|
202
|
-
$ assistant oauth connections token integration:gmail --json
|
|
237
|
+
$ assistant oauth connections token integration:gmail --json
|
|
238
|
+
$ assistant oauth connections token integration:gmail --client-id abc123`,
|
|
203
239
|
)
|
|
204
|
-
.action(
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
240
|
+
.action(
|
|
241
|
+
async (
|
|
242
|
+
providerKey: string,
|
|
243
|
+
opts: { clientId?: string },
|
|
244
|
+
cmd: Command,
|
|
245
|
+
) => {
|
|
246
|
+
try {
|
|
247
|
+
const token = await withValidToken(
|
|
248
|
+
providerKey,
|
|
249
|
+
async (t) => t,
|
|
250
|
+
opts.clientId,
|
|
251
|
+
);
|
|
252
|
+
if (shouldOutputJson(cmd)) {
|
|
253
|
+
writeOutput(cmd, { ok: true, token });
|
|
254
|
+
} else {
|
|
255
|
+
process.stdout.write(token + "\n");
|
|
256
|
+
}
|
|
257
|
+
} catch (err) {
|
|
258
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
259
|
+
writeOutput(cmd, { ok: false, error: message });
|
|
260
|
+
process.exitCode = 1;
|
|
211
261
|
}
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
262
|
+
},
|
|
263
|
+
);
|
|
264
|
+
|
|
265
|
+
// ---------------------------------------------------------------------------
|
|
266
|
+
// connections ping <provider-key>
|
|
267
|
+
// ---------------------------------------------------------------------------
|
|
268
|
+
|
|
269
|
+
connections
|
|
270
|
+
.command("ping <provider-key>")
|
|
271
|
+
.description(
|
|
272
|
+
"Verify that a stored OAuth token is still valid by hitting the provider's health-check endpoint",
|
|
273
|
+
)
|
|
274
|
+
.option(
|
|
275
|
+
"--client-id <id>",
|
|
276
|
+
"Filter by OAuth client ID when multiple apps exist for the provider",
|
|
277
|
+
)
|
|
278
|
+
.addHelpText(
|
|
279
|
+
"after",
|
|
280
|
+
`
|
|
281
|
+
Arguments:
|
|
282
|
+
provider-key Provider key (e.g. integration:gmail, integration:twitter)
|
|
283
|
+
|
|
284
|
+
Fetches a valid access token (refreshing if needed) and sends a GET request
|
|
285
|
+
to the provider's configured ping URL. Reports success (HTTP 2xx) or failure.
|
|
286
|
+
|
|
287
|
+
The ping URL is set per-provider in seed data or via "providers register --ping-url".
|
|
288
|
+
If no ping URL is configured for the provider, exits with an error.
|
|
289
|
+
|
|
290
|
+
Examples:
|
|
291
|
+
$ assistant oauth connections ping integration:gmail
|
|
292
|
+
$ assistant oauth connections ping integration:twitter --json
|
|
293
|
+
$ assistant oauth connections ping integration:gmail --client-id abc123`,
|
|
294
|
+
)
|
|
295
|
+
.action(
|
|
296
|
+
async (
|
|
297
|
+
providerKey: string,
|
|
298
|
+
opts: { clientId?: string },
|
|
299
|
+
cmd: Command,
|
|
300
|
+
) => {
|
|
301
|
+
try {
|
|
302
|
+
const provider = getProvider(providerKey);
|
|
303
|
+
if (!provider) {
|
|
304
|
+
writeOutput(cmd, {
|
|
305
|
+
ok: false,
|
|
306
|
+
error: `Provider not found: ${providerKey}`,
|
|
307
|
+
});
|
|
308
|
+
process.exitCode = 1;
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
if (!provider.pingUrl) {
|
|
313
|
+
writeOutput(cmd, {
|
|
314
|
+
ok: false,
|
|
315
|
+
error: `No ping URL configured for "${providerKey}"`,
|
|
316
|
+
});
|
|
317
|
+
process.exitCode = 1;
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
const pingUrl = provider.pingUrl;
|
|
322
|
+
|
|
323
|
+
const result = await withValidToken(
|
|
324
|
+
providerKey,
|
|
325
|
+
async (token) => {
|
|
326
|
+
const res = await fetch(pingUrl, {
|
|
327
|
+
method: "GET",
|
|
328
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
329
|
+
});
|
|
330
|
+
return { status: res.status, ok: res.ok };
|
|
331
|
+
},
|
|
332
|
+
opts.clientId,
|
|
333
|
+
);
|
|
334
|
+
|
|
335
|
+
if (result.ok) {
|
|
336
|
+
if (shouldOutputJson(cmd)) {
|
|
337
|
+
writeOutput(cmd, {
|
|
338
|
+
ok: true,
|
|
339
|
+
provider: providerKey,
|
|
340
|
+
status: result.status,
|
|
341
|
+
});
|
|
342
|
+
} else {
|
|
343
|
+
log.info(`${providerKey}: OK (HTTP ${result.status})`);
|
|
344
|
+
writeOutput(cmd, {
|
|
345
|
+
ok: true,
|
|
346
|
+
provider: providerKey,
|
|
347
|
+
status: result.status,
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
} else {
|
|
351
|
+
writeOutput(cmd, {
|
|
352
|
+
ok: false,
|
|
353
|
+
provider: providerKey,
|
|
354
|
+
status: result.status,
|
|
355
|
+
error: `Ping failed with HTTP ${result.status}`,
|
|
356
|
+
});
|
|
357
|
+
process.exitCode = 1;
|
|
358
|
+
}
|
|
359
|
+
} catch (err) {
|
|
360
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
361
|
+
writeOutput(cmd, { ok: false, error: message });
|
|
362
|
+
process.exitCode = 1;
|
|
363
|
+
}
|
|
364
|
+
},
|
|
365
|
+
);
|
|
218
366
|
|
|
219
367
|
// ---------------------------------------------------------------------------
|
|
220
368
|
// connections disconnect <provider-key>
|
|
@@ -225,6 +373,10 @@ Examples:
|
|
|
225
373
|
.description(
|
|
226
374
|
"Disconnect an OAuth integration and remove all associated credentials",
|
|
227
375
|
)
|
|
376
|
+
.option(
|
|
377
|
+
"--client-id <id>",
|
|
378
|
+
"Filter by OAuth client ID when multiple apps exist for the provider",
|
|
379
|
+
)
|
|
228
380
|
.addHelpText(
|
|
229
381
|
"after",
|
|
230
382
|
`
|
|
@@ -240,60 +392,242 @@ client_id, client_secret) are also cleaned up if present.
|
|
|
240
392
|
|
|
241
393
|
Examples:
|
|
242
394
|
$ assistant oauth connections disconnect integration:gmail
|
|
243
|
-
$ assistant oauth connections disconnect integration:slack
|
|
395
|
+
$ assistant oauth connections disconnect integration:slack
|
|
396
|
+
$ assistant oauth connections disconnect integration:gmail --client-id abc123`,
|
|
244
397
|
)
|
|
245
|
-
.action(
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
398
|
+
.action(
|
|
399
|
+
async (
|
|
400
|
+
providerKey: string,
|
|
401
|
+
opts: { clientId?: string },
|
|
402
|
+
cmd: Command,
|
|
403
|
+
) => {
|
|
404
|
+
try {
|
|
405
|
+
assertMetadataWritable();
|
|
406
|
+
|
|
407
|
+
let cleanedUp = false;
|
|
408
|
+
|
|
409
|
+
// 1. Disconnect the OAuth connection (new-format keys + connection row)
|
|
410
|
+
const oauthResult = await disconnectOAuthProvider(
|
|
411
|
+
providerKey,
|
|
412
|
+
opts.clientId,
|
|
413
|
+
);
|
|
414
|
+
if (oauthResult === "error") {
|
|
415
|
+
writeOutput(cmd, {
|
|
416
|
+
ok: false,
|
|
417
|
+
error: `Failed to disconnect OAuth provider "${providerKey}" — please try again`,
|
|
418
|
+
});
|
|
419
|
+
process.exitCode = 1;
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
if (oauthResult === "disconnected") cleanedUp = true;
|
|
423
|
+
|
|
424
|
+
// 2. Clean up legacy credential keys for common fields
|
|
425
|
+
const legacyFields = [
|
|
426
|
+
"access_token",
|
|
427
|
+
"refresh_token",
|
|
428
|
+
"client_id",
|
|
429
|
+
"client_secret",
|
|
430
|
+
];
|
|
431
|
+
for (const field of legacyFields) {
|
|
432
|
+
const key = credentialKey(providerKey, field);
|
|
433
|
+
const result = await deleteSecureKeyAsync(key);
|
|
434
|
+
if (result === "deleted") cleanedUp = true;
|
|
435
|
+
|
|
436
|
+
const metaDeleted = deleteCredentialMetadata(providerKey, field);
|
|
437
|
+
if (metaDeleted) cleanedUp = true;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
if (!cleanedUp) {
|
|
441
|
+
writeOutput(cmd, {
|
|
442
|
+
ok: false,
|
|
443
|
+
error: `No OAuth connection or credentials found for "${providerKey}"`,
|
|
444
|
+
});
|
|
445
|
+
process.exitCode = 1;
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
writeOutput(cmd, { ok: true, service: providerKey });
|
|
450
|
+
|
|
451
|
+
if (!shouldOutputJson(cmd)) {
|
|
452
|
+
log.info(`Disconnected ${providerKey}`);
|
|
453
|
+
}
|
|
454
|
+
} catch (err) {
|
|
455
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
456
|
+
writeOutput(cmd, { ok: false, error: message });
|
|
258
457
|
process.exitCode = 1;
|
|
259
|
-
return;
|
|
260
|
-
}
|
|
261
|
-
if (oauthResult === "disconnected") cleanedUp = true;
|
|
262
|
-
|
|
263
|
-
// 2. Clean up legacy credential keys for common fields
|
|
264
|
-
const legacyFields = [
|
|
265
|
-
"access_token",
|
|
266
|
-
"refresh_token",
|
|
267
|
-
"client_id",
|
|
268
|
-
"client_secret",
|
|
269
|
-
];
|
|
270
|
-
for (const field of legacyFields) {
|
|
271
|
-
const key = credentialKey(providerKey, field);
|
|
272
|
-
const result = await deleteSecureKeyAsync(key);
|
|
273
|
-
if (result === "deleted") cleanedUp = true;
|
|
274
|
-
|
|
275
|
-
const metaDeleted = deleteCredentialMetadata(providerKey, field);
|
|
276
|
-
if (metaDeleted) cleanedUp = true;
|
|
277
458
|
}
|
|
459
|
+
},
|
|
460
|
+
);
|
|
278
461
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
error: `No OAuth connection or credentials found for "${providerKey}"`,
|
|
283
|
-
});
|
|
284
|
-
process.exitCode = 1;
|
|
285
|
-
return;
|
|
286
|
-
}
|
|
462
|
+
// ---------------------------------------------------------------------------
|
|
463
|
+
// connections connect <provider-key>
|
|
464
|
+
// ---------------------------------------------------------------------------
|
|
287
465
|
|
|
288
|
-
|
|
466
|
+
connections
|
|
467
|
+
.command("connect <provider-key>")
|
|
468
|
+
.description("Initiate an OAuth2 authorization flow for a provider")
|
|
469
|
+
.option(
|
|
470
|
+
"--client-id <id>",
|
|
471
|
+
"Filter by OAuth client ID when multiple apps exist for the provider",
|
|
472
|
+
)
|
|
473
|
+
.option(
|
|
474
|
+
"--scopes <scopes...>",
|
|
475
|
+
"Additional scopes beyond the provider's defaults",
|
|
476
|
+
)
|
|
477
|
+
.option(
|
|
478
|
+
"--open-browser",
|
|
479
|
+
"Open the auth URL in the browser and wait for completion",
|
|
480
|
+
)
|
|
481
|
+
.addHelpText(
|
|
482
|
+
"after",
|
|
483
|
+
`
|
|
484
|
+
Arguments:
|
|
485
|
+
provider-key Provider key (e.g. integration:gmail) or alias (e.g. gmail)
|
|
289
486
|
|
|
290
|
-
|
|
291
|
-
|
|
487
|
+
Initiates an OAuth2 authorization flow for the given provider. By default,
|
|
488
|
+
prints the authorization URL to stdout — useful for headless/remote sessions.
|
|
489
|
+
The token exchange completes in the background when the user authorizes.
|
|
490
|
+
|
|
491
|
+
With --open-browser, opens the authorization URL in your browser and waits
|
|
492
|
+
for completion.
|
|
493
|
+
|
|
494
|
+
Client credentials are resolved from the OAuth app store. Use --client-id
|
|
495
|
+
to select a specific app when multiple apps exist for the same provider.
|
|
496
|
+
|
|
497
|
+
Examples:
|
|
498
|
+
$ assistant oauth connections connect integration:gmail
|
|
499
|
+
$ assistant oauth connections connect gmail --open-browser
|
|
500
|
+
$ assistant oauth connections connect integration:slack --client-id abc123
|
|
501
|
+
$ assistant oauth connections connect integration:gmail --scopes calendar.readonly --json`,
|
|
502
|
+
)
|
|
503
|
+
.action(
|
|
504
|
+
async (
|
|
505
|
+
providerKey: string,
|
|
506
|
+
opts: {
|
|
507
|
+
clientId?: string;
|
|
508
|
+
scopes?: string[];
|
|
509
|
+
openBrowser?: boolean;
|
|
510
|
+
},
|
|
511
|
+
cmd: Command,
|
|
512
|
+
) => {
|
|
513
|
+
try {
|
|
514
|
+
// a. Resolve service alias
|
|
515
|
+
const resolvedServiceKey = resolveService(providerKey);
|
|
516
|
+
|
|
517
|
+
// b. Resolve client credentials from the DB
|
|
518
|
+
const dbApp = opts.clientId
|
|
519
|
+
? getAppByProviderAndClientId(resolvedServiceKey, opts.clientId)
|
|
520
|
+
: getMostRecentAppByProvider(resolvedServiceKey);
|
|
521
|
+
|
|
522
|
+
let clientId = opts.clientId;
|
|
523
|
+
let clientSecret: string | undefined;
|
|
524
|
+
|
|
525
|
+
if (dbApp) {
|
|
526
|
+
if (!clientId) clientId = dbApp.clientId;
|
|
527
|
+
const storedSecret = getSecureKey(dbApp.clientSecretCredentialPath);
|
|
528
|
+
if (storedSecret) clientSecret = storedSecret;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
// c. Validate client_id
|
|
532
|
+
if (!clientId) {
|
|
533
|
+
writeOutput(cmd, {
|
|
534
|
+
ok: false,
|
|
535
|
+
error:
|
|
536
|
+
"No client_id found. Provide --client-id or register an app first with 'assistant oauth apps upsert'.",
|
|
537
|
+
});
|
|
538
|
+
process.exitCode = 1;
|
|
539
|
+
return;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
// d. Check if client_secret is required but missing
|
|
543
|
+
if (clientSecret === undefined) {
|
|
544
|
+
const providerRow = getProvider(resolvedServiceKey);
|
|
545
|
+
const behavior = getProviderBehavior(resolvedServiceKey);
|
|
546
|
+
|
|
547
|
+
const requiresSecret =
|
|
548
|
+
behavior?.setup?.requiresClientSecret ??
|
|
549
|
+
!!(
|
|
550
|
+
providerRow?.tokenEndpointAuthMethod || providerRow?.extraParams
|
|
551
|
+
);
|
|
552
|
+
|
|
553
|
+
if (requiresSecret) {
|
|
554
|
+
writeOutput(cmd, {
|
|
555
|
+
ok: false,
|
|
556
|
+
error: `client_secret is required for ${resolvedServiceKey} but not found. Store it first with 'assistant oauth apps upsert --client-secret'.`,
|
|
557
|
+
});
|
|
558
|
+
process.exitCode = 1;
|
|
559
|
+
return;
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
// e. Call the orchestrator
|
|
564
|
+
const result = await orchestrateOAuthConnect({
|
|
565
|
+
service: providerKey,
|
|
566
|
+
clientId,
|
|
567
|
+
clientSecret,
|
|
568
|
+
isInteractive: !!opts.openBrowser,
|
|
569
|
+
openUrl: opts.openBrowser
|
|
570
|
+
? (url) => {
|
|
571
|
+
if (isMacOS()) {
|
|
572
|
+
Bun.spawn(["open", url], {
|
|
573
|
+
stdout: "ignore",
|
|
574
|
+
stderr: "ignore",
|
|
575
|
+
});
|
|
576
|
+
} else if (isLinux()) {
|
|
577
|
+
Bun.spawn(["xdg-open", url], {
|
|
578
|
+
stdout: "ignore",
|
|
579
|
+
stderr: "ignore",
|
|
580
|
+
});
|
|
581
|
+
} else {
|
|
582
|
+
// Fallback: print URL for manual opening
|
|
583
|
+
process.stdout.write(
|
|
584
|
+
`Open this URL to authorize:\n\n${url}\n`,
|
|
585
|
+
);
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
: undefined,
|
|
589
|
+
...(opts.scopes ? { requestedScopes: opts.scopes } : {}),
|
|
590
|
+
});
|
|
591
|
+
|
|
592
|
+
// f. Handle results
|
|
593
|
+
if (!result.success) {
|
|
594
|
+
writeOutput(cmd, { ok: false, error: result.error });
|
|
595
|
+
process.exitCode = 1;
|
|
596
|
+
return;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
if (result.deferred) {
|
|
600
|
+
if (shouldOutputJson(cmd)) {
|
|
601
|
+
writeOutput(cmd, {
|
|
602
|
+
ok: true,
|
|
603
|
+
deferred: true,
|
|
604
|
+
authUrl: result.authUrl,
|
|
605
|
+
service: result.service,
|
|
606
|
+
});
|
|
607
|
+
} else {
|
|
608
|
+
process.stdout.write(
|
|
609
|
+
`Open this URL to authorize:\n\n${result.authUrl}\n\nThe connection will complete automatically once you authorize.\n`,
|
|
610
|
+
);
|
|
611
|
+
}
|
|
612
|
+
return;
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
// Interactive mode completed
|
|
616
|
+
if (shouldOutputJson(cmd)) {
|
|
617
|
+
writeOutput(cmd, {
|
|
618
|
+
ok: true,
|
|
619
|
+
grantedScopes: result.grantedScopes,
|
|
620
|
+
accountInfo: result.accountInfo,
|
|
621
|
+
});
|
|
622
|
+
} else {
|
|
623
|
+
const msg = `Connected to ${resolvedServiceKey}${result.accountInfo ? ` as ${result.accountInfo}` : ""}`;
|
|
624
|
+
process.stdout.write(msg + "\n");
|
|
625
|
+
}
|
|
626
|
+
} catch (err) {
|
|
627
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
628
|
+
writeOutput(cmd, { ok: false, error: message });
|
|
629
|
+
process.exitCode = 1;
|
|
292
630
|
}
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
writeOutput(cmd, { ok: false, error: message });
|
|
296
|
-
process.exitCode = 1;
|
|
297
|
-
}
|
|
298
|
-
});
|
|
631
|
+
},
|
|
632
|
+
);
|
|
299
633
|
}
|
|
@@ -171,6 +171,10 @@ Examples:
|
|
|
171
171
|
}
|
|
172
172
|
return port;
|
|
173
173
|
})
|
|
174
|
+
.option(
|
|
175
|
+
"--ping-url <url>",
|
|
176
|
+
"Health-check endpoint URL for token validation",
|
|
177
|
+
)
|
|
174
178
|
.addHelpText(
|
|
175
179
|
"after",
|
|
176
180
|
`
|
|
@@ -186,6 +190,9 @@ Arguments (via options):
|
|
|
186
190
|
(e.g. "client_secret_post", "client_secret_basic").
|
|
187
191
|
--callback-transport Transport method for the OAuth callback.
|
|
188
192
|
--loopback-port Port number for the local loopback callback server (1-65535).
|
|
193
|
+
--ping-url Optional URL for a lightweight health-check endpoint.
|
|
194
|
+
Used by "connections ping" to validate that a stored token
|
|
195
|
+
is still functional (e.g. "https://api.example.com/user").
|
|
189
196
|
|
|
190
197
|
Registers a new OAuth provider configuration in the local store. This is
|
|
191
198
|
used for custom integrations not covered by the built-in provider seeds.
|
|
@@ -200,7 +207,12 @@ Examples:
|
|
|
200
207
|
--provider-key integration:my-service \\
|
|
201
208
|
--auth-url https://my-service.com/auth \\
|
|
202
209
|
--token-url https://my-service.com/token \\
|
|
203
|
-
--scopes read,write --json
|
|
210
|
+
--scopes read,write --json
|
|
211
|
+
$ assistant oauth providers register \\
|
|
212
|
+
--provider-key integration:custom-api \\
|
|
213
|
+
--auth-url https://example.com/auth \\
|
|
214
|
+
--token-url https://example.com/token \\
|
|
215
|
+
--ping-url https://example.com/user`,
|
|
204
216
|
)
|
|
205
217
|
.action(
|
|
206
218
|
(
|
|
@@ -214,6 +226,7 @@ Examples:
|
|
|
214
226
|
tokenAuthMethod?: string;
|
|
215
227
|
callbackTransport?: string;
|
|
216
228
|
loopbackPort?: number;
|
|
229
|
+
pingUrl?: string;
|
|
217
230
|
},
|
|
218
231
|
cmd: Command,
|
|
219
232
|
) => {
|
|
@@ -229,6 +242,7 @@ Examples:
|
|
|
229
242
|
tokenEndpointAuthMethod: opts.tokenAuthMethod,
|
|
230
243
|
callbackTransport: opts.callbackTransport,
|
|
231
244
|
loopbackPort: opts.loopbackPort,
|
|
245
|
+
pingUrl: opts.pingUrl,
|
|
232
246
|
});
|
|
233
247
|
|
|
234
248
|
writeOutput(cmd, parseProviderRow(row));
|
|
@@ -12,7 +12,10 @@ import {
|
|
|
12
12
|
getMessages,
|
|
13
13
|
} from "../../memory/conversation-crud.js";
|
|
14
14
|
import { listConversations } from "../../memory/conversation-queries.js";
|
|
15
|
-
import {
|
|
15
|
+
import {
|
|
16
|
+
selectEmbeddingBackend,
|
|
17
|
+
SPARSE_EMBEDDING_VERSION,
|
|
18
|
+
} from "../../memory/embedding-backend.js";
|
|
16
19
|
import { initQdrantClient } from "../../memory/qdrant-client.js";
|
|
17
20
|
import { timeAgo } from "../../util/time.js";
|
|
18
21
|
import { initializeDb } from "../db.js";
|
|
@@ -226,7 +229,7 @@ Examples:
|
|
|
226
229
|
const qdrantUrl = getQdrantUrlEnv() || config.memory.qdrant.url;
|
|
227
230
|
const embeddingSelection = selectEmbeddingBackend(config);
|
|
228
231
|
const embeddingModel = embeddingSelection.backend
|
|
229
|
-
? `${embeddingSelection.backend.provider}:${embeddingSelection.backend.model}`
|
|
232
|
+
? `${embeddingSelection.backend.provider}:${embeddingSelection.backend.model}:sparse-v${SPARSE_EMBEDDING_VERSION}`
|
|
230
233
|
: undefined;
|
|
231
234
|
const qdrant = initQdrantClient({
|
|
232
235
|
url: qdrantUrl,
|