@vellumai/assistant 0.3.3 → 0.3.4
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/README.md +8 -16
- package/package.json +1 -1
- package/src/__tests__/call-orchestrator.test.ts +321 -0
- package/src/__tests__/channel-approval-routes.test.ts +382 -124
- package/src/__tests__/channel-approvals.test.ts +51 -2
- package/src/__tests__/channel-delivery-store.test.ts +30 -4
- package/src/__tests__/channel-guardian.test.ts +187 -0
- package/src/__tests__/config-schema.test.ts +1 -1
- package/src/__tests__/daemon-lifecycle.test.ts +635 -0
- package/src/__tests__/gateway-only-enforcement.test.ts +19 -13
- package/src/__tests__/handlers-twilio-config.test.ts +73 -0
- package/src/__tests__/secret-scanner.test.ts +223 -0
- package/src/__tests__/shell-parser-property.test.ts +357 -2
- package/src/__tests__/system-prompt.test.ts +25 -1
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +34 -1
- package/src/__tests__/user-reference.test.ts +68 -0
- package/src/calls/call-orchestrator.ts +63 -11
- package/src/cli/map.ts +6 -0
- package/src/commands/__tests__/cc-command-registry.test.ts +67 -0
- package/src/commands/cc-command-registry.ts +14 -1
- package/src/config/bundled-skills/claude-code/TOOLS.json +10 -3
- package/src/config/bundled-skills/messaging/SKILL.md +4 -0
- package/src/config/defaults.ts +1 -1
- package/src/config/schema.ts +3 -3
- package/src/config/skills.ts +5 -32
- package/src/config/system-prompt.ts +16 -0
- package/src/config/user-reference.ts +29 -0
- package/src/config/vellum-skills/catalog.json +52 -0
- package/src/config/vellum-skills/telegram-setup/SKILL.md +6 -1
- package/src/config/vellum-skills/twilio-setup/SKILL.md +38 -0
- package/src/daemon/auth-manager.ts +103 -0
- package/src/daemon/computer-use-session.ts +8 -1
- package/src/daemon/config-watcher.ts +253 -0
- package/src/daemon/handlers/config.ts +36 -13
- package/src/daemon/handlers/skills.ts +6 -7
- package/src/daemon/ipc-contract.ts +6 -0
- package/src/daemon/ipc-handler.ts +87 -0
- package/src/daemon/lifecycle.ts +16 -4
- package/src/daemon/ride-shotgun-handler.ts +11 -1
- package/src/daemon/server.ts +105 -502
- package/src/daemon/session-agent-loop.ts +5 -14
- package/src/daemon/session-runtime-assembly.ts +60 -44
- package/src/daemon/session.ts +8 -1
- package/src/memory/db-connection.ts +28 -0
- package/src/memory/db-init.ts +1019 -0
- package/src/memory/db.ts +2 -2007
- package/src/memory/embedding-backend.ts +79 -11
- package/src/memory/indexer.ts +2 -0
- package/src/memory/job-utils.ts +64 -4
- package/src/memory/jobs-worker.ts +7 -1
- package/src/memory/recall-cache.ts +107 -0
- package/src/memory/retriever.ts +30 -1
- package/src/memory/schema-migration.ts +984 -0
- package/src/memory/schema.ts +1 -0
- package/src/memory/search/types.ts +2 -0
- package/src/permissions/prompter.ts +14 -3
- package/src/permissions/trust-store.ts +7 -0
- package/src/runtime/channel-approvals.ts +17 -3
- package/src/runtime/gateway-client.ts +2 -1
- package/src/runtime/http-server.ts +15 -4
- package/src/runtime/routes/channel-routes.ts +172 -84
- package/src/runtime/routes/run-routes.ts +7 -1
- package/src/runtime/run-orchestrator.ts +8 -1
- package/src/security/secret-scanner.ts +218 -0
- package/src/skills/frontmatter.ts +63 -0
- package/src/skills/slash-commands.ts +23 -0
- package/src/skills/vellum-catalog-remote.ts +107 -0
- package/src/tools/browser/auto-navigate.ts +132 -24
- package/src/tools/browser/browser-manager.ts +67 -61
- package/src/tools/claude-code/claude-code.ts +55 -3
- package/src/tools/executor.ts +10 -2
- package/src/tools/skills/vellum-catalog.ts +61 -156
- package/src/tools/terminal/parser.ts +21 -5
- package/src/util/platform.ts +8 -1
- package/src/util/retry.ts +4 -4
package/src/memory/schema.ts
CHANGED
|
@@ -129,6 +129,7 @@ export const memoryEmbeddings = sqliteTable('memory_embeddings', {
|
|
|
129
129
|
model: text('model').notNull(),
|
|
130
130
|
dimensions: integer('dimensions').notNull(),
|
|
131
131
|
vectorJson: text('vector_json').notNull(),
|
|
132
|
+
contentHash: text('content_hash'),
|
|
132
133
|
createdAt: integer('created_at').notNull(),
|
|
133
134
|
updatedAt: integer('updated_at').notNull(),
|
|
134
135
|
});
|
|
@@ -94,6 +94,8 @@ export interface CollectedCandidates {
|
|
|
94
94
|
relationNeighborEntityCount: number;
|
|
95
95
|
relationExpandedItemCount: number;
|
|
96
96
|
earlyTerminated: boolean;
|
|
97
|
+
/** True when semantic search was attempted but threw an error. */
|
|
98
|
+
semanticSearchFailed: boolean;
|
|
97
99
|
merged: Candidate[];
|
|
98
100
|
}
|
|
99
101
|
|
|
@@ -10,7 +10,12 @@ import { redactSensitiveFields } from '../security/redaction.js';
|
|
|
10
10
|
const log = getLogger('permission-prompter');
|
|
11
11
|
|
|
12
12
|
interface PendingPrompt {
|
|
13
|
-
resolve: (value: {
|
|
13
|
+
resolve: (value: {
|
|
14
|
+
decision: UserDecision;
|
|
15
|
+
selectedPattern?: string;
|
|
16
|
+
selectedScope?: string;
|
|
17
|
+
decisionContext?: string;
|
|
18
|
+
}) => void;
|
|
14
19
|
reject: (reason: Error) => void;
|
|
15
20
|
timer: ReturnType<typeof setTimeout>;
|
|
16
21
|
}
|
|
@@ -38,7 +43,12 @@ export class PermissionPrompter {
|
|
|
38
43
|
sessionId?: string,
|
|
39
44
|
executionTarget?: ExecutionTarget,
|
|
40
45
|
persistentDecisionsAllowed?: boolean,
|
|
41
|
-
): Promise<{
|
|
46
|
+
): Promise<{
|
|
47
|
+
decision: UserDecision;
|
|
48
|
+
selectedPattern?: string;
|
|
49
|
+
selectedScope?: string;
|
|
50
|
+
decisionContext?: string;
|
|
51
|
+
}> {
|
|
42
52
|
const requestId = uuid();
|
|
43
53
|
|
|
44
54
|
return new Promise((resolve, reject) => {
|
|
@@ -77,6 +87,7 @@ export class PermissionPrompter {
|
|
|
77
87
|
decision: UserDecision,
|
|
78
88
|
selectedPattern?: string,
|
|
79
89
|
selectedScope?: string,
|
|
90
|
+
decisionContext?: string,
|
|
80
91
|
): void {
|
|
81
92
|
const pending = this.pending.get(requestId);
|
|
82
93
|
if (!pending) {
|
|
@@ -85,7 +96,7 @@ export class PermissionPrompter {
|
|
|
85
96
|
}
|
|
86
97
|
clearTimeout(pending.timer);
|
|
87
98
|
this.pending.delete(requestId);
|
|
88
|
-
pending.resolve({ decision, selectedPattern, selectedScope });
|
|
99
|
+
pending.resolve({ decision, selectedPattern, selectedScope, decisionContext });
|
|
89
100
|
}
|
|
90
101
|
|
|
91
102
|
dispose(): void {
|
|
@@ -27,13 +27,17 @@ let cachedStarterBundleAccepted: boolean | null = null;
|
|
|
27
27
|
* on every tool-call permission check.
|
|
28
28
|
*/
|
|
29
29
|
const compiledPatterns = new Map<string, Minimatch>();
|
|
30
|
+
/** Patterns that failed compilation — cached to avoid repeated attempts and log spam. */
|
|
31
|
+
const invalidPatterns = new Set<string>();
|
|
30
32
|
|
|
31
33
|
/** Get or compile a Minimatch object for the given pattern. Returns null if the pattern is invalid. */
|
|
32
34
|
function getCompiledPattern(pattern: string): Minimatch | null {
|
|
35
|
+
if (invalidPatterns.has(pattern)) return null;
|
|
33
36
|
let compiled = compiledPatterns.get(pattern);
|
|
34
37
|
if (!compiled) {
|
|
35
38
|
if (typeof pattern !== 'string') {
|
|
36
39
|
log.warn({ pattern }, 'Cannot compile non-string pattern');
|
|
40
|
+
invalidPatterns.add(pattern as string);
|
|
37
41
|
return null;
|
|
38
42
|
}
|
|
39
43
|
try {
|
|
@@ -41,6 +45,7 @@ function getCompiledPattern(pattern: string): Minimatch | null {
|
|
|
41
45
|
compiledPatterns.set(pattern, compiled);
|
|
42
46
|
} catch (err) {
|
|
43
47
|
log.warn({ pattern, err }, 'Failed to compile pattern');
|
|
48
|
+
invalidPatterns.add(pattern);
|
|
44
49
|
return null;
|
|
45
50
|
}
|
|
46
51
|
}
|
|
@@ -50,6 +55,7 @@ function getCompiledPattern(pattern: string): Minimatch | null {
|
|
|
50
55
|
/** Rebuild the compiled pattern cache from the current rule set. */
|
|
51
56
|
function rebuildPatternCache(rules: TrustRule[]): void {
|
|
52
57
|
compiledPatterns.clear();
|
|
58
|
+
invalidPatterns.clear();
|
|
53
59
|
for (const rule of rules) {
|
|
54
60
|
if (typeof rule.pattern !== 'string') {
|
|
55
61
|
log.warn({ ruleId: rule.id, pattern: rule.pattern }, 'Skipping rule with non-string pattern during cache rebuild');
|
|
@@ -509,6 +515,7 @@ export function clearCache(): void {
|
|
|
509
515
|
cachedRules = null;
|
|
510
516
|
cachedStarterBundleAccepted = null;
|
|
511
517
|
compiledPatterns.clear();
|
|
518
|
+
invalidPatterns.clear();
|
|
512
519
|
}
|
|
513
520
|
|
|
514
521
|
// ─── Starter approval bundle ────────────────────────────────────────────────
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
import { getPendingConfirmationsByConversation, getRun } from '../memory/runs-store.js';
|
|
14
14
|
import type { PendingRunInfo } from '../memory/runs-store.js';
|
|
15
15
|
import { addRule } from '../permissions/trust-store.js';
|
|
16
|
+
import { getTool } from '../tools/registry.js';
|
|
16
17
|
import type { RunOrchestrator } from './run-orchestrator.js';
|
|
17
18
|
import { DEFAULT_APPROVAL_ACTIONS } from './channel-approval-types.js';
|
|
18
19
|
import type {
|
|
@@ -101,11 +102,17 @@ export function handleChannelDecision(
|
|
|
101
102
|
conversationId: string,
|
|
102
103
|
decision: ApprovalDecisionResult,
|
|
103
104
|
orchestrator: RunOrchestrator,
|
|
105
|
+
decisionContext?: string,
|
|
104
106
|
): HandleDecisionResult {
|
|
105
107
|
const pending = getPendingConfirmationsByConversation(conversationId);
|
|
106
108
|
if (pending.length === 0) return { applied: false };
|
|
107
109
|
|
|
108
|
-
|
|
110
|
+
// Callback-based decisions include a run ID and must resolve to that exact
|
|
111
|
+
// pending confirmation. Plain-text decisions still apply to the first prompt.
|
|
112
|
+
const info = decision.runId
|
|
113
|
+
? pending.find((candidate) => candidate.runId === decision.runId)
|
|
114
|
+
: pending[0];
|
|
115
|
+
if (!info) return { applied: false };
|
|
109
116
|
|
|
110
117
|
if (decision.action === 'approve_always') {
|
|
111
118
|
// Only persist a trust rule when the confirmation explicitly allows persistence
|
|
@@ -121,8 +128,13 @@ export function handleChannelDecision(
|
|
|
121
128
|
) {
|
|
122
129
|
const pattern = confirmation.allowlistOptions[0].pattern;
|
|
123
130
|
const scope = confirmation.scopeOptions[0].scope;
|
|
131
|
+
// Only persist executionTarget for skill-origin tools — core tools don't
|
|
132
|
+
// set it in their PolicyContext, so a persisted value would prevent the
|
|
133
|
+
// rule from ever matching on subsequent permission checks.
|
|
134
|
+
const tool = getTool(confirmation.toolName);
|
|
135
|
+
const executionTarget = tool?.origin === 'skill' ? confirmation.executionTarget : undefined;
|
|
124
136
|
addRule(confirmation.toolName, pattern, scope, 'allow', 100, {
|
|
125
|
-
executionTarget
|
|
137
|
+
executionTarget,
|
|
126
138
|
});
|
|
127
139
|
}
|
|
128
140
|
// When persistence is not allowed or options are missing, the decision
|
|
@@ -131,7 +143,9 @@ export function handleChannelDecision(
|
|
|
131
143
|
|
|
132
144
|
// Map channel-level action to the permission system's UserDecision type.
|
|
133
145
|
const userDecision = decision.action === 'reject' ? 'deny' as const : 'allow' as const;
|
|
134
|
-
const result =
|
|
146
|
+
const result = decisionContext === undefined
|
|
147
|
+
? orchestrator.submitDecision(info.runId, userDecision)
|
|
148
|
+
: orchestrator.submitDecision(info.runId, userDecision, decisionContext);
|
|
135
149
|
|
|
136
150
|
return {
|
|
137
151
|
applied: result === 'applied',
|
|
@@ -52,7 +52,8 @@ export async function deliverApprovalPrompt(
|
|
|
52
52
|
chatId: string,
|
|
53
53
|
text: string,
|
|
54
54
|
approval: ApprovalUIMetadata,
|
|
55
|
+
assistantId?: string,
|
|
55
56
|
bearerToken?: string,
|
|
56
57
|
): Promise<void> {
|
|
57
|
-
await deliverChannelReply(callbackUrl, { chatId, text, approval }, bearerToken);
|
|
58
|
+
await deliverChannelReply(callbackUrl, { chatId, text, approval, assistantId }, bearerToken);
|
|
58
59
|
}
|
|
@@ -41,7 +41,6 @@ import {
|
|
|
41
41
|
handleChannelDeliveryAck,
|
|
42
42
|
handleListDeadLetters,
|
|
43
43
|
handleReplayDeadLetters,
|
|
44
|
-
isChannelApprovalsEnabled,
|
|
45
44
|
startGuardianExpirySweep,
|
|
46
45
|
stopGuardianExpirySweep,
|
|
47
46
|
} from './routes/channel-routes.js';
|
|
@@ -414,8 +413,10 @@ export class RuntimeHttpServer {
|
|
|
414
413
|
}, 30_000);
|
|
415
414
|
}
|
|
416
415
|
|
|
417
|
-
// Start proactive guardian approval expiry sweep
|
|
418
|
-
|
|
416
|
+
// Start proactive guardian approval expiry sweep whenever orchestrator
|
|
417
|
+
// support is available. Guardian approvals can be created even when the
|
|
418
|
+
// generic channel-approval UX flag is disabled.
|
|
419
|
+
if (this.runOrchestrator) {
|
|
419
420
|
startGuardianExpirySweep(this.runOrchestrator, getGatewayBaseUrl(), this.bearerToken);
|
|
420
421
|
log.info('Guardian approval expiry sweep started');
|
|
421
422
|
}
|
|
@@ -939,8 +940,16 @@ export class RuntimeHttpServer {
|
|
|
939
940
|
const externalChatId = typeof payload.externalChatId === 'string'
|
|
940
941
|
? payload.externalChatId
|
|
941
942
|
: undefined;
|
|
943
|
+
const assistantId = typeof payload.assistantId === 'string'
|
|
944
|
+
? payload.assistantId
|
|
945
|
+
: undefined;
|
|
942
946
|
if (externalChatId) {
|
|
943
|
-
await this.deliverReplyViaCallback(
|
|
947
|
+
await this.deliverReplyViaCallback(
|
|
948
|
+
event.conversationId,
|
|
949
|
+
externalChatId,
|
|
950
|
+
replyCallbackUrl,
|
|
951
|
+
assistantId,
|
|
952
|
+
);
|
|
944
953
|
}
|
|
945
954
|
}
|
|
946
955
|
} catch (err) {
|
|
@@ -954,6 +963,7 @@ export class RuntimeHttpServer {
|
|
|
954
963
|
conversationId: string,
|
|
955
964
|
externalChatId: string,
|
|
956
965
|
callbackUrl: string,
|
|
966
|
+
assistantId?: string,
|
|
957
967
|
): Promise<void> {
|
|
958
968
|
const msgs = conversationStore.getMessages(conversationId);
|
|
959
969
|
for (let i = msgs.length - 1; i >= 0; i--) {
|
|
@@ -976,6 +986,7 @@ export class RuntimeHttpServer {
|
|
|
976
986
|
chatId: externalChatId,
|
|
977
987
|
text: rendered.text || undefined,
|
|
978
988
|
attachments: replyAttachments.length > 0 ? replyAttachments : undefined,
|
|
989
|
+
assistantId,
|
|
979
990
|
}, this.bearerToken);
|
|
980
991
|
}
|
|
981
992
|
break;
|