@vellumai/assistant 0.4.9 → 0.4.11
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 -0
- package/Dockerfile +1 -1
- package/README.md +16 -9
- package/package.json +1 -1
- package/src/__tests__/account-registry.test.ts +1 -0
- package/src/__tests__/actor-token-service.test.ts +1 -0
- package/src/__tests__/app-builder-tool-scripts.test.ts +1 -0
- package/src/__tests__/asset-materialize-tool.test.ts +7 -0
- package/src/__tests__/asset-search-tool.test.ts +7 -0
- package/src/__tests__/browser-fill-credential.test.ts +1 -0
- package/src/__tests__/call-start-guardian-guard.test.ts +1 -0
- package/src/__tests__/channel-approval-routes.test.ts +29 -0
- package/src/__tests__/channel-guardian.test.ts +2143 -1546
- package/src/__tests__/channel-retry-sweep.test.ts +169 -14
- package/src/__tests__/claude-code-tool-profiles.test.ts +1 -0
- package/src/__tests__/computer-use-tools.test.ts +1 -0
- package/src/__tests__/contacts-tools.test.ts +1 -0
- package/src/__tests__/conversation-attention-telegram.test.ts +1 -0
- package/src/__tests__/credential-policy-validate.test.ts +97 -0
- package/src/__tests__/credential-security-e2e.test.ts +1 -0
- package/src/__tests__/credential-vault-unit.test.ts +1 -0
- package/src/__tests__/credential-vault.test.ts +1 -0
- package/src/__tests__/delete-managed-skill-tool.test.ts +1 -0
- package/src/__tests__/file-edit-tool.test.ts +1 -0
- package/src/__tests__/file-read-tool.test.ts +1 -0
- package/src/__tests__/file-write-tool.test.ts +1 -0
- package/src/__tests__/followup-tools.test.ts +1 -0
- package/src/__tests__/gateway-only-guard.test.ts +1 -1
- package/src/__tests__/guardian-control-plane-policy.test.ts +5 -4
- package/src/__tests__/guardian-grant-minting.test.ts +3 -0
- package/src/__tests__/guardian-principal-id-roundtrip.test.ts +4 -3
- package/src/__tests__/guardian-routing-state.test.ts +8 -0
- package/src/__tests__/headless-browser-interactions.test.ts +1 -0
- package/src/__tests__/headless-browser-navigate.test.ts +1 -0
- package/src/__tests__/headless-browser-read-tools.test.ts +1 -0
- package/src/__tests__/headless-browser-snapshot.test.ts +1 -0
- package/src/__tests__/host-file-edit-tool.test.ts +1 -0
- package/src/__tests__/host-file-read-tool.test.ts +1 -0
- package/src/__tests__/host-file-write-tool.test.ts +1 -0
- package/src/__tests__/host-shell-tool.test.ts +1 -0
- package/src/__tests__/lifecycle-docs-guard.test.ts +207 -0
- package/src/__tests__/managed-skill-lifecycle.test.ts +1 -0
- package/src/__tests__/media-reuse-story.e2e.test.ts +8 -0
- package/src/__tests__/messaging-send-tool.test.ts +1 -0
- package/src/__tests__/playbook-execution.test.ts +1 -0
- package/src/__tests__/playbook-tools.test.ts +1 -0
- package/src/__tests__/relay-server.test.ts +4 -0
- package/src/__tests__/scaffold-managed-skill-tool.test.ts +1 -0
- package/src/__tests__/schedule-tools.test.ts +1 -0
- package/src/__tests__/secret-onetime-send.test.ts +4 -0
- package/src/__tests__/secret-scanner-executor.test.ts +2 -0
- package/src/__tests__/send-notification-tool.test.ts +2 -0
- package/src/__tests__/shell-credential-ref.test.ts +1 -0
- package/src/__tests__/shell-tool-proxy-mode.test.ts +1 -0
- package/src/__tests__/skill-load-feature-flag.test.ts +1 -0
- package/src/__tests__/skill-load-tool.test.ts +1 -0
- package/src/__tests__/skill-script-runner-host.test.ts +1 -0
- package/src/__tests__/skill-script-runner-sandbox.test.ts +1 -0
- package/src/__tests__/skill-script-runner.test.ts +1 -0
- package/src/__tests__/skill-tool-factory.test.ts +1 -0
- package/src/__tests__/subagent-tools.test.ts +1 -1
- package/src/__tests__/swarm-recursion.test.ts +1 -0
- package/src/__tests__/swarm-session-integration.test.ts +1 -0
- package/src/__tests__/swarm-tool.test.ts +1 -0
- package/src/__tests__/task-management-tools.test.ts +1 -0
- package/src/__tests__/task-tools.test.ts +1 -0
- package/src/__tests__/terminal-tools.test.ts +1 -0
- package/src/__tests__/tool-approval-handler.test.ts +2 -2
- package/src/__tests__/tool-execution-abort-cleanup.test.ts +1 -0
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +1 -0
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +2 -0
- package/src/__tests__/tool-executor-shell-integration.test.ts +1 -0
- package/src/__tests__/tool-executor.test.ts +1 -0
- package/src/__tests__/trust-context-guards.test.ts +218 -0
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +6 -0
- package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +6 -0
- package/src/__tests__/trusted-contact-multichannel.test.ts +1 -0
- package/src/__tests__/trusted-contact-verification.test.ts +1 -0
- package/src/__tests__/view-image-tool.test.ts +1 -0
- package/src/calls/guardian-dispatch.ts +4 -4
- package/src/cli/mcp.ts +183 -3
- package/src/config/bundled-skills/agentmail/SKILL.md +4 -4
- package/src/config/bundled-skills/google-oauth-setup/SKILL.md +1 -0
- package/src/config/bundled-skills/phone-calls/SKILL.md +17 -119
- package/src/config/system-prompt.ts +4 -2
- package/src/config/vellum-skills/twilio-setup/SKILL.md +1 -1
- package/src/daemon/computer-use-session.ts +1 -0
- package/src/daemon/session-agent-loop.ts +1 -1
- package/src/daemon/session-memory.ts +2 -2
- package/src/daemon/session-runtime-assembly.ts +2 -2
- package/src/daemon/session-tool-setup.ts +1 -1
- package/src/mcp/client.ts +55 -6
- package/src/mcp/manager.ts +9 -0
- package/src/mcp/mcp-oauth-provider.ts +347 -0
- package/src/memory/channel-delivery-store.ts +1 -0
- package/src/memory/db-init.ts +4 -0
- package/src/memory/delivery-status.ts +43 -0
- package/src/memory/guardian-bindings.ts +3 -3
- package/src/memory/migrations/127-guardian-principal-id-not-null.ts +108 -0
- package/src/memory/migrations/index.ts +1 -0
- package/src/memory/migrations/registry.ts +6 -0
- package/src/memory/schema.ts +1 -1
- package/src/runtime/actor-trust-resolver.ts +13 -4
- package/src/runtime/channel-retry-sweep.ts +31 -14
- package/src/runtime/guardian-context-resolver.ts +25 -64
- package/src/runtime/guardian-outbound-actions.ts +399 -108
- package/src/runtime/guardian-vellum-migration.ts +1 -23
- package/src/runtime/guardian-verification-templates.ts +66 -30
- package/src/runtime/local-actor-identity.ts +4 -6
- package/src/runtime/middleware/actor-token.ts +2 -8
- package/src/runtime/routes/channel-route-shared.ts +0 -1
- package/src/runtime/routes/inbound-message-handler.ts +3 -4
- package/src/runtime/tool-grant-request-helper.ts +1 -1
- package/src/tools/credentials/policy-validate.ts +22 -0
- package/src/tools/guardian-control-plane-policy.ts +2 -2
- package/src/tools/types.ts +1 -1
|
@@ -5,22 +5,17 @@
|
|
|
5
5
|
* 'vellum' channel with a guardianPrincipalId. This is required for
|
|
6
6
|
* the identity-bound hatch bootstrap flow.
|
|
7
7
|
*
|
|
8
|
-
* - If a vellum binding already exists
|
|
9
|
-
* - If a vellum binding exists but lacks guardianPrincipalId, backfill it
|
|
10
|
-
* from the binding's guardianExternalUserId.
|
|
8
|
+
* - If a vellum binding already exists, returns its guardianPrincipalId.
|
|
11
9
|
* - If no vellum binding exists, creates one with a fresh principal.
|
|
12
10
|
* - Preserves existing guardian bindings for other channels unchanged.
|
|
13
11
|
*/
|
|
14
12
|
|
|
15
|
-
import { eq } from 'drizzle-orm';
|
|
16
13
|
import { v4 as uuid } from 'uuid';
|
|
17
14
|
|
|
18
|
-
import { getDb } from '../memory/db.js';
|
|
19
15
|
import {
|
|
20
16
|
createBinding,
|
|
21
17
|
getActiveBinding,
|
|
22
18
|
} from '../memory/guardian-bindings.js';
|
|
23
|
-
import { channelGuardianBindings } from '../memory/schema.js';
|
|
24
19
|
import { getLogger } from '../util/logger.js';
|
|
25
20
|
import { DAEMON_INTERNAL_ASSISTANT_ID } from './assistant-scope.js';
|
|
26
21
|
|
|
@@ -36,23 +31,6 @@ const log = getLogger('guardian-vellum-migration');
|
|
|
36
31
|
export function ensureVellumGuardianBinding(assistantId: string = DAEMON_INTERNAL_ASSISTANT_ID): string {
|
|
37
32
|
const existing = getActiveBinding(assistantId, 'vellum');
|
|
38
33
|
if (existing) {
|
|
39
|
-
// If the binding exists but is missing guardianPrincipalId, backfill it
|
|
40
|
-
// from the binding's guardianExternalUserId (the canonical identity).
|
|
41
|
-
if (!existing.guardianPrincipalId) {
|
|
42
|
-
const principalId = existing.guardianExternalUserId;
|
|
43
|
-
const db = getDb();
|
|
44
|
-
db.update(channelGuardianBindings)
|
|
45
|
-
.set({ guardianPrincipalId: principalId, updatedAt: Date.now() })
|
|
46
|
-
.where(eq(channelGuardianBindings.id, existing.id))
|
|
47
|
-
.run();
|
|
48
|
-
|
|
49
|
-
log.info(
|
|
50
|
-
{ assistantId, guardianPrincipalId: principalId },
|
|
51
|
-
'Backfilled guardianPrincipalId on existing vellum binding',
|
|
52
|
-
);
|
|
53
|
-
return principalId;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
34
|
log.debug(
|
|
57
35
|
{ assistantId, guardianPrincipalId: existing.guardianPrincipalId },
|
|
58
36
|
'Vellum guardian binding already exists with principal',
|
|
@@ -12,43 +12,50 @@
|
|
|
12
12
|
|
|
13
13
|
export const GUARDIAN_VERIFY_TEMPLATE_KEYS = {
|
|
14
14
|
/** Initial outbound SMS with verification code. */
|
|
15
|
-
CHALLENGE_REQUEST:
|
|
15
|
+
CHALLENGE_REQUEST: "guardian_verify.sms.challenge_request",
|
|
16
16
|
/** Resend SMS with verification code. */
|
|
17
|
-
RESEND:
|
|
17
|
+
RESEND: "guardian_verify.sms.resend",
|
|
18
18
|
/** Response when the user is already verified. */
|
|
19
|
-
ALREADY_VERIFIED:
|
|
19
|
+
ALREADY_VERIFIED: "guardian_verify.already_verified",
|
|
20
20
|
/** Initial outbound Telegram verification prompt (code is not included). */
|
|
21
|
-
TELEGRAM_CHALLENGE_REQUEST:
|
|
21
|
+
TELEGRAM_CHALLENGE_REQUEST: "guardian_verify.telegram.challenge_request",
|
|
22
22
|
/** Resend Telegram verification prompt (code is not included). */
|
|
23
|
-
TELEGRAM_RESEND:
|
|
23
|
+
TELEGRAM_RESEND: "guardian_verify.telegram.resend",
|
|
24
|
+
/** Initial outbound Slack DM verification prompt. */
|
|
25
|
+
SLACK_CHALLENGE_REQUEST: "guardian_verify.slack.challenge_request",
|
|
26
|
+
/** Resend Slack DM verification prompt. */
|
|
27
|
+
SLACK_RESEND: "guardian_verify.slack.resend",
|
|
24
28
|
/** Outbound voice call intro prompt: asks guardian to enter verification code via keypad. */
|
|
25
|
-
VOICE_CALL_INTRO:
|
|
29
|
+
VOICE_CALL_INTRO: "guardian_verify.voice.call_intro",
|
|
26
30
|
/** Voice retry prompt after an incorrect code entry. */
|
|
27
|
-
VOICE_RETRY:
|
|
31
|
+
VOICE_RETRY: "guardian_verify.voice.retry",
|
|
28
32
|
/** Voice success prompt after successful verification. */
|
|
29
|
-
VOICE_SUCCESS:
|
|
33
|
+
VOICE_SUCCESS: "guardian_verify.voice.success",
|
|
30
34
|
/** Voice failure prompt after too many incorrect attempts. */
|
|
31
|
-
VOICE_FAILURE:
|
|
35
|
+
VOICE_FAILURE: "guardian_verify.voice.failure",
|
|
32
36
|
/** Deterministic reply after successful channel verification command. */
|
|
33
|
-
CHANNEL_VERIFY_SUCCESS:
|
|
37
|
+
CHANNEL_VERIFY_SUCCESS: "guardian_verify.channel.success",
|
|
34
38
|
/** Deterministic reply after failed channel verification command. */
|
|
35
|
-
CHANNEL_VERIFY_FAILED:
|
|
39
|
+
CHANNEL_VERIFY_FAILED: "guardian_verify.channel.failed",
|
|
36
40
|
/** Deterministic reply for bootstrap deep-link success. */
|
|
37
|
-
CHANNEL_BOOTSTRAP_BOUND:
|
|
41
|
+
CHANNEL_BOOTSTRAP_BOUND: "guardian_verify.channel.bootstrap_bound",
|
|
38
42
|
/** Deterministic reply after successful trusted contact verification. */
|
|
39
|
-
CHANNEL_TRUSTED_CONTACT_VERIFY_SUCCESS:
|
|
43
|
+
CHANNEL_TRUSTED_CONTACT_VERIFY_SUCCESS:
|
|
44
|
+
"guardian_verify.channel.trusted_contact_success",
|
|
40
45
|
} as const;
|
|
41
46
|
|
|
42
47
|
export type GuardianVerifyTemplateKey =
|
|
43
48
|
(typeof GUARDIAN_VERIFY_TEMPLATE_KEYS)[keyof typeof GUARDIAN_VERIFY_TEMPLATE_KEYS];
|
|
44
49
|
|
|
45
|
-
/** Template keys for SMS/Telegram text-based verification messages. */
|
|
50
|
+
/** Template keys for SMS/Telegram/Slack text-based verification messages. */
|
|
46
51
|
type TextVerifyTemplateKey =
|
|
47
52
|
| typeof GUARDIAN_VERIFY_TEMPLATE_KEYS.CHALLENGE_REQUEST
|
|
48
53
|
| typeof GUARDIAN_VERIFY_TEMPLATE_KEYS.RESEND
|
|
49
54
|
| typeof GUARDIAN_VERIFY_TEMPLATE_KEYS.ALREADY_VERIFIED
|
|
50
55
|
| typeof GUARDIAN_VERIFY_TEMPLATE_KEYS.TELEGRAM_CHALLENGE_REQUEST
|
|
51
|
-
| typeof GUARDIAN_VERIFY_TEMPLATE_KEYS.TELEGRAM_RESEND
|
|
56
|
+
| typeof GUARDIAN_VERIFY_TEMPLATE_KEYS.TELEGRAM_RESEND
|
|
57
|
+
| typeof GUARDIAN_VERIFY_TEMPLATE_KEYS.SLACK_CHALLENGE_REQUEST
|
|
58
|
+
| typeof GUARDIAN_VERIFY_TEMPLATE_KEYS.SLACK_RESEND;
|
|
52
59
|
|
|
53
60
|
/** Template keys for deterministic channel verification reply messages. */
|
|
54
61
|
export type ChannelVerifyReplyTemplateKey =
|
|
@@ -81,25 +88,36 @@ export interface ChannelVerifyReplyVars {
|
|
|
81
88
|
// Template Composers
|
|
82
89
|
// ---------------------------------------------------------------------------
|
|
83
90
|
|
|
84
|
-
const templates: Record<
|
|
91
|
+
const templates: Record<
|
|
92
|
+
TextVerifyTemplateKey,
|
|
93
|
+
(vars: GuardianVerifyTemplateVars) => string
|
|
94
|
+
> = {
|
|
85
95
|
[GUARDIAN_VERIFY_TEMPLATE_KEYS.CHALLENGE_REQUEST]: (_vars) => {
|
|
86
|
-
return
|
|
96
|
+
return "Vellum assistant guardian verification requested. Reply with the 6-digit code you were given.";
|
|
87
97
|
},
|
|
88
98
|
|
|
89
99
|
[GUARDIAN_VERIFY_TEMPLATE_KEYS.RESEND]: (_vars) => {
|
|
90
|
-
return
|
|
100
|
+
return "Vellum assistant guardian verification requested. Reply with the 6-digit code you were given. (resent)";
|
|
91
101
|
},
|
|
92
102
|
|
|
93
103
|
[GUARDIAN_VERIFY_TEMPLATE_KEYS.ALREADY_VERIFIED]: (_vars) => {
|
|
94
|
-
return
|
|
104
|
+
return "This channel is already verified. No further action is needed.";
|
|
95
105
|
},
|
|
96
106
|
|
|
97
107
|
[GUARDIAN_VERIFY_TEMPLATE_KEYS.TELEGRAM_CHALLENGE_REQUEST]: (_vars) => {
|
|
98
|
-
return
|
|
108
|
+
return "Vellum assistant guardian verification requested. Reply with the 6-digit code you were given.";
|
|
99
109
|
},
|
|
100
110
|
|
|
101
111
|
[GUARDIAN_VERIFY_TEMPLATE_KEYS.TELEGRAM_RESEND]: (_vars) => {
|
|
102
|
-
return
|
|
112
|
+
return "Vellum assistant guardian verification requested. Reply with the 6-digit code you were given. (resent)";
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
[GUARDIAN_VERIFY_TEMPLATE_KEYS.SLACK_CHALLENGE_REQUEST]: (_vars) => {
|
|
116
|
+
return "Vellum assistant guardian verification requested. Reply with the 6-digit code you were given.";
|
|
117
|
+
},
|
|
118
|
+
|
|
119
|
+
[GUARDIAN_VERIFY_TEMPLATE_KEYS.SLACK_RESEND]: (_vars) => {
|
|
120
|
+
return "Vellum assistant guardian verification requested. Reply with the 6-digit code you were given. (resent)";
|
|
103
121
|
},
|
|
104
122
|
};
|
|
105
123
|
|
|
@@ -115,6 +133,18 @@ export function composeVerificationSms(
|
|
|
115
133
|
return composer(vars);
|
|
116
134
|
}
|
|
117
135
|
|
|
136
|
+
/**
|
|
137
|
+
* Compose an outbound verification Slack DM from a template key and typed variables.
|
|
138
|
+
* Returns plain string content suitable for Slack delivery.
|
|
139
|
+
*/
|
|
140
|
+
export function composeVerificationSlack(
|
|
141
|
+
templateKey: TextVerifyTemplateKey,
|
|
142
|
+
vars: GuardianVerifyTemplateVars,
|
|
143
|
+
): string {
|
|
144
|
+
const composer = templates[templateKey];
|
|
145
|
+
return composer(vars);
|
|
146
|
+
}
|
|
147
|
+
|
|
118
148
|
/**
|
|
119
149
|
* Compose an outbound verification Telegram message from a template key and typed variables.
|
|
120
150
|
* Returns plain string content suitable for Telegram delivery.
|
|
@@ -137,18 +167,21 @@ type VoiceTemplateKey =
|
|
|
137
167
|
| typeof GUARDIAN_VERIFY_TEMPLATE_KEYS.VOICE_SUCCESS
|
|
138
168
|
| typeof GUARDIAN_VERIFY_TEMPLATE_KEYS.VOICE_FAILURE;
|
|
139
169
|
|
|
140
|
-
const voiceTemplates: Record<
|
|
170
|
+
const voiceTemplates: Record<
|
|
171
|
+
VoiceTemplateKey,
|
|
172
|
+
(vars: GuardianVerifyVoiceTemplateVars) => string
|
|
173
|
+
> = {
|
|
141
174
|
[GUARDIAN_VERIFY_TEMPLATE_KEYS.VOICE_CALL_INTRO]: (vars) =>
|
|
142
175
|
`You are receiving a guardian verification call for your Vellum assistant. Please enter your ${vars.codeDigits}-digit verification code using your keypad.`,
|
|
143
176
|
|
|
144
177
|
[GUARDIAN_VERIFY_TEMPLATE_KEYS.VOICE_RETRY]: (_vars) =>
|
|
145
|
-
|
|
178
|
+
"That code was incorrect. Please try again.",
|
|
146
179
|
|
|
147
180
|
[GUARDIAN_VERIFY_TEMPLATE_KEYS.VOICE_SUCCESS]: (_vars) =>
|
|
148
|
-
|
|
181
|
+
"Verification successful. Thank you. Goodbye.",
|
|
149
182
|
|
|
150
183
|
[GUARDIAN_VERIFY_TEMPLATE_KEYS.VOICE_FAILURE]: (_vars) =>
|
|
151
|
-
|
|
184
|
+
"Too many incorrect attempts. Goodbye.",
|
|
152
185
|
};
|
|
153
186
|
|
|
154
187
|
/**
|
|
@@ -167,18 +200,21 @@ export function composeVerificationVoice(
|
|
|
167
200
|
// Channel Verification Reply Templates (deterministic, non-agent)
|
|
168
201
|
// ---------------------------------------------------------------------------
|
|
169
202
|
|
|
170
|
-
const channelVerifyReplyTemplates: Record<
|
|
203
|
+
const channelVerifyReplyTemplates: Record<
|
|
204
|
+
ChannelVerifyReplyTemplateKey,
|
|
205
|
+
(vars: ChannelVerifyReplyVars) => string
|
|
206
|
+
> = {
|
|
171
207
|
[GUARDIAN_VERIFY_TEMPLATE_KEYS.CHANNEL_VERIFY_SUCCESS]: () =>
|
|
172
|
-
|
|
208
|
+
"Verification successful. You are now set as the guardian for this channel.",
|
|
173
209
|
|
|
174
210
|
[GUARDIAN_VERIFY_TEMPLATE_KEYS.CHANNEL_VERIFY_FAILED]: (vars) =>
|
|
175
|
-
vars.failureReason ??
|
|
211
|
+
vars.failureReason ?? "The verification code is invalid or has expired.",
|
|
176
212
|
|
|
177
213
|
[GUARDIAN_VERIFY_TEMPLATE_KEYS.CHANNEL_BOOTSTRAP_BOUND]: () =>
|
|
178
|
-
|
|
214
|
+
"Welcome! Your identity has been linked. Please check for a verification code message.",
|
|
179
215
|
|
|
180
216
|
[GUARDIAN_VERIFY_TEMPLATE_KEYS.CHANNEL_TRUSTED_CONTACT_VERIFY_SUCCESS]: () =>
|
|
181
|
-
|
|
217
|
+
"Verification successful! You can now message the assistant.",
|
|
182
218
|
};
|
|
183
219
|
|
|
184
220
|
/**
|
|
@@ -16,10 +16,7 @@ import type { GuardianRuntimeContext } from '../daemon/session-runtime-assembly.
|
|
|
16
16
|
import { getActiveBinding } from '../memory/guardian-bindings.js';
|
|
17
17
|
import { getLogger } from '../util/logger.js';
|
|
18
18
|
import { DAEMON_INTERNAL_ASSISTANT_ID } from './assistant-scope.js';
|
|
19
|
-
import {
|
|
20
|
-
resolveGuardianContext,
|
|
21
|
-
toGuardianRuntimeContext,
|
|
22
|
-
} from './guardian-context-resolver.js';
|
|
19
|
+
import { resolveGuardianContext } from './guardian-context-resolver.js';
|
|
23
20
|
import { ensureVellumGuardianBinding } from './guardian-vellum-migration.js';
|
|
24
21
|
|
|
25
22
|
const log = getLogger('local-actor-identity');
|
|
@@ -58,7 +55,8 @@ export function resolveLocalIpcGuardianContext(
|
|
|
58
55
|
conversationExternalId: 'local',
|
|
59
56
|
actorExternalId: principalId,
|
|
60
57
|
});
|
|
61
|
-
|
|
58
|
+
// Overlay the caller's actual sourceChannel onto the resolved context.
|
|
59
|
+
return { ...guardianCtx, sourceChannel };
|
|
62
60
|
}
|
|
63
61
|
|
|
64
62
|
const guardianPrincipalId = binding.guardianExternalUserId;
|
|
@@ -78,5 +76,5 @@ export function resolveLocalIpcGuardianContext(
|
|
|
78
76
|
|
|
79
77
|
// Overlay the caller's actual sourceChannel onto the resolved context
|
|
80
78
|
// so downstream consumers see the correct channel provenance.
|
|
81
|
-
return
|
|
79
|
+
return { ...guardianCtx, sourceChannel };
|
|
82
80
|
}
|
|
@@ -14,17 +14,13 @@
|
|
|
14
14
|
* guardian context pathway when no actor token is present.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
import type { ChannelId } from '../../channels/types.js';
|
|
18
17
|
import type { GuardianRuntimeContext } from '../../daemon/session-runtime-assembly.js';
|
|
19
18
|
import { getActiveBinding } from '../../memory/guardian-bindings.js';
|
|
20
19
|
import { getLogger } from '../../util/logger.js';
|
|
21
20
|
import { type ActorTokenClaims, hashToken, verifyActorToken } from '../actor-token-service.js';
|
|
22
21
|
import { findActiveByTokenHash } from '../actor-token-store.js';
|
|
23
22
|
import { DAEMON_INTERNAL_ASSISTANT_ID } from '../assistant-scope.js';
|
|
24
|
-
import {
|
|
25
|
-
resolveGuardianContext,
|
|
26
|
-
toGuardianRuntimeContext,
|
|
27
|
-
} from '../guardian-context-resolver.js';
|
|
23
|
+
import { resolveGuardianContext } from '../guardian-context-resolver.js';
|
|
28
24
|
import { resolveLocalIpcGuardianContext } from '../local-actor-identity.js';
|
|
29
25
|
|
|
30
26
|
const log = getLogger('actor-token-middleware');
|
|
@@ -120,12 +116,10 @@ export function verifyHttpActorToken(req: Request): ActorTokenVerification {
|
|
|
120
116
|
actorExternalId: claims.guardianPrincipalId,
|
|
121
117
|
});
|
|
122
118
|
|
|
123
|
-
const guardianContext = toGuardianRuntimeContext('vellum' as ChannelId, guardianCtx);
|
|
124
|
-
|
|
125
119
|
return {
|
|
126
120
|
ok: true,
|
|
127
121
|
claims,
|
|
128
|
-
guardianContext,
|
|
122
|
+
guardianContext: guardianCtx,
|
|
129
123
|
};
|
|
130
124
|
}
|
|
131
125
|
|
|
@@ -12,7 +12,6 @@ import type {
|
|
|
12
12
|
} from '../channel-approval-types.js';
|
|
13
13
|
import type { DenialReason } from '../guardian-context-resolver.js';
|
|
14
14
|
export type { ActorTrustClass, DenialReason, GuardianContext } from '../guardian-context-resolver.js';
|
|
15
|
-
export { toGuardianRuntimeContext } from '../guardian-context-resolver.js';
|
|
16
15
|
|
|
17
16
|
/** Canonicalize assistantId for channel ingress paths. */
|
|
18
17
|
export function canonicalChannelAssistantId(_assistantId: string): string {
|
|
@@ -70,7 +70,6 @@ import {
|
|
|
70
70
|
GUARDIAN_APPROVAL_TTL_MS,
|
|
71
71
|
type GuardianContext,
|
|
72
72
|
stripVerificationFailurePrefix,
|
|
73
|
-
toGuardianRuntimeContext,
|
|
74
73
|
verifyGatewayOrigin,
|
|
75
74
|
} from './channel-route-shared.js';
|
|
76
75
|
import { handleApprovalInterception } from './guardian-approval-interception.js';
|
|
@@ -628,7 +627,7 @@ export async function handleChannelInbound(
|
|
|
628
627
|
conversationId: result.conversationId,
|
|
629
628
|
requesterExternalUserId: canonicalSenderId ?? rawSenderId ?? undefined,
|
|
630
629
|
guardianExternalUserId: binding.guardianExternalUserId,
|
|
631
|
-
guardianPrincipalId: binding.guardianPrincipalId
|
|
630
|
+
guardianPrincipalId: binding.guardianPrincipalId,
|
|
632
631
|
toolName: 'ingress_message',
|
|
633
632
|
questionText: 'Ingress policy requires guardian approval',
|
|
634
633
|
expiresAt: new Date(Date.now() + GUARDIAN_APPROVAL_TTL_MS).toISOString(),
|
|
@@ -1141,7 +1140,7 @@ export async function handleChannelInbound(
|
|
|
1141
1140
|
senderName: body.actorDisplayName,
|
|
1142
1141
|
senderExternalUserId: body.actorExternalId,
|
|
1143
1142
|
senderUsername: body.actorUsername,
|
|
1144
|
-
guardianCtx
|
|
1143
|
+
guardianCtx,
|
|
1145
1144
|
replyCallbackUrl,
|
|
1146
1145
|
assistantId: canonicalAssistantId,
|
|
1147
1146
|
});
|
|
@@ -1687,7 +1686,7 @@ function processChannelMessageInBackground(params: BackgroundProcessingParams):
|
|
|
1687
1686
|
uxBrief: metadataUxBrief,
|
|
1688
1687
|
},
|
|
1689
1688
|
assistantId,
|
|
1690
|
-
guardianContext:
|
|
1689
|
+
guardianContext: guardianCtx,
|
|
1691
1690
|
isInteractive: resolveRoutingState(guardianCtx).promptWaitingAllowed,
|
|
1692
1691
|
...(cmdIntent ? { commandIntent: cmdIntent } : {}),
|
|
1693
1692
|
},
|
|
@@ -123,7 +123,7 @@ export function createOrReuseToolGrantRequest(
|
|
|
123
123
|
requesterExternalUserId,
|
|
124
124
|
requesterChatId: requesterChatId ?? undefined,
|
|
125
125
|
guardianExternalUserId: binding.guardianExternalUserId,
|
|
126
|
-
guardianPrincipalId: binding.guardianPrincipalId
|
|
126
|
+
guardianPrincipalId: binding.guardianPrincipalId,
|
|
127
127
|
toolName,
|
|
128
128
|
inputDigest,
|
|
129
129
|
questionText,
|
|
@@ -8,6 +8,20 @@
|
|
|
8
8
|
|
|
9
9
|
import type { CredentialPolicy, CredentialPolicyInput } from './policy-types.js';
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Host tools that must never appear in credential allowed_tools.
|
|
13
|
+
*
|
|
14
|
+
* Credentials should only be accessible from sandboxed tools, not from
|
|
15
|
+
* host-level tools that execute directly on the user's machine.
|
|
16
|
+
* Map each blocked tool to its safe sandboxed equivalent.
|
|
17
|
+
*/
|
|
18
|
+
const BLOCKED_HOST_TOOLS: ReadonlyMap<string, string> = new Map([
|
|
19
|
+
['host_bash', 'bash'],
|
|
20
|
+
['host_file_read', 'file_read'],
|
|
21
|
+
['host_file_write', 'file_write'],
|
|
22
|
+
['host_file_edit', 'file_edit'],
|
|
23
|
+
]);
|
|
24
|
+
|
|
11
25
|
export interface ValidationResult {
|
|
12
26
|
valid: boolean;
|
|
13
27
|
errors: string[];
|
|
@@ -29,6 +43,14 @@ export function validatePolicyInput(input: CredentialPolicyInput): ValidationRes
|
|
|
29
43
|
const tool = input.allowed_tools[i];
|
|
30
44
|
if (typeof tool !== 'string' || tool.trim().length === 0) {
|
|
31
45
|
errors.push(`allowed_tools[${i}] must be a non-empty string`);
|
|
46
|
+
} else {
|
|
47
|
+
const replacement = BLOCKED_HOST_TOOLS.get(tool);
|
|
48
|
+
if (replacement !== undefined) {
|
|
49
|
+
errors.push(
|
|
50
|
+
`allowed_tools[${i}] "${tool}" is a host tool and cannot be used for credential access. ` +
|
|
51
|
+
`Use "${replacement}" instead.`,
|
|
52
|
+
);
|
|
53
|
+
}
|
|
32
54
|
}
|
|
33
55
|
}
|
|
34
56
|
}
|
|
@@ -124,13 +124,13 @@ export function isGuardianControlPlaneInvocation(
|
|
|
124
124
|
export function enforceGuardianOnlyPolicy(
|
|
125
125
|
toolName: string,
|
|
126
126
|
input: Record<string, unknown>,
|
|
127
|
-
trustClass: string
|
|
127
|
+
trustClass: string,
|
|
128
128
|
): { denied: boolean; reason?: string } {
|
|
129
129
|
if (!isGuardianControlPlaneInvocation(toolName, input)) {
|
|
130
130
|
return { denied: false };
|
|
131
131
|
}
|
|
132
132
|
|
|
133
|
-
if (trustClass === 'guardian'
|
|
133
|
+
if (trustClass === 'guardian') {
|
|
134
134
|
return { denied: false };
|
|
135
135
|
}
|
|
136
136
|
|
package/src/tools/types.ts
CHANGED
|
@@ -138,7 +138,7 @@ export interface ToolContext {
|
|
|
138
138
|
/** Optional principal identifier propagated to sub-tool confirmation flows. */
|
|
139
139
|
principal?: string;
|
|
140
140
|
/** Inbound trust classification for the session — used by trust/policy gates. */
|
|
141
|
-
guardianTrustClass
|
|
141
|
+
guardianTrustClass: 'guardian' | 'trusted_contact' | 'unknown';
|
|
142
142
|
/** Channel through which the tool invocation originates (e.g. 'telegram', 'voice'). Used for scoped grant consumption. */
|
|
143
143
|
executionChannel?: string;
|
|
144
144
|
/** Voice/call session ID, if the invocation originates from a call. Used for scoped grant consumption. */
|