@vellumai/assistant 0.4.33 → 0.4.35
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/package.json +1 -1
- package/src/__tests__/access-request-decision.test.ts +2 -3
- package/src/__tests__/actor-token-service.test.ts +4 -11
- package/src/__tests__/approval-primitive.test.ts +0 -45
- package/src/__tests__/assistant-id-boundary-guard.test.ts +169 -0
- package/src/__tests__/callback-handoff-copy.test.ts +0 -1
- package/src/__tests__/channel-approval-routes.test.ts +5 -45
- package/src/__tests__/channel-guardian.test.ts +122 -345
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +4 -3
- package/src/__tests__/contacts-tools.test.ts +4 -5
- package/src/__tests__/conversation-attention-store.test.ts +2 -65
- package/src/__tests__/conversation-attention-telegram.test.ts +0 -2
- package/src/__tests__/conversation-pairing.test.ts +0 -1
- package/src/__tests__/deterministic-verification-control-plane.test.ts +0 -2
- package/src/__tests__/guardian-action-conversation-turn.test.ts +1 -7
- package/src/__tests__/guardian-action-grant-mint-consume.test.ts +0 -74
- package/src/__tests__/guardian-action-late-reply.test.ts +1 -8
- package/src/__tests__/guardian-grant-minting.test.ts +0 -1
- package/src/__tests__/guardian-routing-state.test.ts +0 -3
- package/src/__tests__/inbound-invite-redemption.test.ts +0 -3
- package/src/__tests__/non-member-access-request.test.ts +0 -7
- package/src/__tests__/notification-broadcaster.test.ts +1 -2
- package/src/__tests__/notification-decision-fallback.test.ts +0 -2
- package/src/__tests__/notification-decision-strategy.test.ts +0 -1
- package/src/__tests__/relay-server.test.ts +11 -83
- package/src/__tests__/scoped-approval-grants.test.ts +9 -40
- package/src/__tests__/scoped-grant-security-matrix.test.ts +0 -36
- package/src/__tests__/send-endpoint-busy.test.ts +0 -1
- package/src/__tests__/send-notification-tool.test.ts +0 -1
- package/src/__tests__/slack-inbound-verification.test.ts +2 -4
- package/src/__tests__/thread-seed-composer.test.ts +0 -1
- package/src/__tests__/tool-approval-handler.test.ts +0 -1
- package/src/__tests__/tool-grant-request-escalation.test.ts +0 -4
- package/src/__tests__/trusted-contact-approval-notifier.test.ts +1 -5
- package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +1 -17
- package/src/__tests__/trusted-contact-multichannel.test.ts +0 -13
- package/src/__tests__/trusted-contact-verification.test.ts +3 -15
- package/src/__tests__/twilio-routes.test.ts +2 -2
- package/src/__tests__/voice-invite-redemption.test.ts +0 -1
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +0 -37
- package/src/approvals/approval-primitive.ts +0 -15
- package/src/approvals/guardian-decision-primitive.ts +0 -3
- package/src/approvals/guardian-request-resolvers.ts +0 -5
- package/src/calls/call-domain.ts +0 -3
- package/src/calls/call-store.ts +0 -3
- package/src/calls/guardian-action-sweep.ts +2 -1
- package/src/calls/guardian-dispatch.ts +1 -2
- package/src/calls/relay-access-wait.ts +0 -4
- package/src/calls/relay-server.ts +3 -11
- package/src/calls/relay-setup-router.ts +1 -2
- package/src/calls/relay-verification.ts +0 -1
- package/src/calls/twilio-routes.ts +0 -3
- package/src/calls/types.ts +0 -1
- package/src/calls/voice-session-bridge.ts +0 -1
- package/src/config/bundled-skills/google-oauth-setup/SKILL.md +100 -171
- package/src/config/bundled-skills/notifications/tools/send-notification.ts +0 -1
- package/src/contacts/contact-store.ts +13 -88
- package/src/contacts/contacts-write.ts +3 -11
- package/src/contacts/types.ts +0 -1
- package/src/daemon/handlers/config-channels.ts +16 -42
- package/src/daemon/handlers/config-inbox.ts +6 -6
- package/src/daemon/handlers/contacts.ts +3 -11
- package/src/daemon/handlers/index.ts +0 -2
- package/src/daemon/session-process.ts +0 -4
- package/src/memory/conversation-attention-store.ts +4 -19
- package/src/memory/conversation-crud.ts +0 -2
- package/src/memory/db-init.ts +4 -0
- package/src/memory/guardian-action-store.ts +0 -12
- package/src/memory/guardian-approvals.ts +35 -80
- package/src/memory/guardian-rate-limits.ts +1 -14
- package/src/memory/guardian-verification.ts +6 -34
- package/src/memory/invite-store.ts +5 -14
- package/src/memory/migrations/026-guardian-verification-sessions.ts +28 -9
- package/src/memory/migrations/027a-guardian-bootstrap-token.ts +16 -3
- package/src/memory/migrations/038-actor-token-records.ts +8 -1
- package/src/memory/migrations/039-actor-refresh-token-records.ts +11 -2
- package/src/memory/migrations/110-channel-guardian.ts +27 -6
- package/src/memory/migrations/112-assistant-inbox.ts +39 -15
- package/src/memory/migrations/114-notifications.ts +37 -15
- package/src/memory/migrations/117-conversation-attention.ts +33 -9
- package/src/memory/migrations/134-contacts-notes-column.ts +64 -45
- package/src/memory/migrations/136-drop-assistant-id-columns.ts +263 -0
- package/src/memory/migrations/index.ts +1 -0
- package/src/memory/migrations/registry.ts +14 -1
- package/src/memory/migrations/schema-introspection.ts +18 -0
- package/src/memory/schema/calls.ts +0 -7
- package/src/memory/schema/contacts.ts +0 -8
- package/src/memory/schema/guardian.ts +0 -5
- package/src/memory/schema/infrastructure.ts +0 -2
- package/src/memory/schema/notifications.ts +3 -17
- package/src/memory/scoped-approval-grants.ts +2 -24
- package/src/notifications/adapters/sms.ts +2 -1
- package/src/notifications/broadcaster.ts +1 -6
- package/src/notifications/decision-engine.ts +3 -4
- package/src/notifications/deliveries-store.ts +0 -4
- package/src/notifications/destination-resolver.ts +4 -6
- package/src/notifications/deterministic-checks.ts +1 -6
- package/src/notifications/emit-signal.ts +4 -11
- package/src/notifications/events-store.ts +7 -17
- package/src/notifications/preference-summary.ts +2 -2
- package/src/notifications/preferences-store.ts +2 -9
- package/src/notifications/signal.ts +0 -1
- package/src/notifications/thread-candidates.ts +1 -11
- package/src/notifications/types.ts +0 -3
- package/src/runtime/access-request-helper.ts +3 -10
- package/src/runtime/actor-refresh-token-store.ts +0 -6
- package/src/runtime/actor-token-store.ts +3 -16
- package/src/runtime/actor-trust-resolver.ts +1 -4
- package/src/runtime/auth/__tests__/credential-service.test.ts +0 -9
- package/src/runtime/auth/credential-service.ts +1 -15
- package/src/runtime/auth/require-bound-guardian.ts +1 -4
- package/src/runtime/channel-guardian-service.ts +15 -46
- package/src/runtime/channel-invite-transport.ts +8 -0
- package/src/runtime/channel-invite-transports/email.ts +4 -0
- package/src/runtime/channel-invite-transports/slack.ts +6 -0
- package/src/runtime/channel-invite-transports/sms.ts +4 -0
- package/src/runtime/channel-invite-transports/telegram.ts +6 -0
- package/src/runtime/confirmation-request-guardian-bridge.ts +0 -1
- package/src/runtime/guardian-action-followup-executor.ts +3 -2
- package/src/runtime/guardian-action-grant-minter.ts +0 -1
- package/src/runtime/guardian-outbound-actions.ts +2 -12
- package/src/runtime/guardian-vellum-migration.ts +2 -3
- package/src/runtime/http-server.ts +3 -10
- package/src/runtime/http-types.ts +13 -1
- package/src/runtime/invite-redemption-service.ts +1 -14
- package/src/runtime/local-actor-identity.ts +2 -5
- package/src/runtime/routes/access-request-decision.ts +0 -1
- package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +0 -9
- package/src/runtime/routes/channel-readiness-routes.ts +29 -18
- package/src/runtime/routes/contact-routes.ts +15 -40
- package/src/runtime/routes/conversation-attention-routes.ts +0 -2
- package/src/runtime/routes/global-search-routes.ts +0 -2
- package/src/runtime/routes/guardian-bootstrap-routes.ts +6 -7
- package/src/runtime/routes/guardian-expiry-sweep.ts +3 -2
- package/src/runtime/routes/inbound-message-handler.ts +0 -3
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +7 -43
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +1 -4
- package/src/runtime/routes/inbound-stages/bootstrap-intercept.ts +1 -6
- package/src/runtime/routes/inbound-stages/escalation-intercept.ts +0 -1
- package/src/runtime/routes/inbound-stages/secret-ingress-check.ts +0 -1
- package/src/runtime/routes/inbound-stages/verification-intercept.ts +3 -7
- package/src/runtime/routes/pairing-routes.ts +4 -4
- package/src/runtime/routes/surface-content-routes.ts +104 -0
- package/src/runtime/tool-grant-request-helper.ts +0 -1
- package/src/tools/browser/browser-manager.ts +22 -21
- package/src/tools/browser/runtime-check.ts +111 -6
- package/src/tools/calls/call-start.ts +1 -3
- package/src/tools/followups/followup_create.ts +1 -2
- package/src/tools/tool-approval-handler.ts +0 -2
|
@@ -95,8 +95,8 @@ export async function handleVerificationIntercept(
|
|
|
95
95
|
// Only intercept when there is a pending challenge or active outbound session
|
|
96
96
|
const shouldIntercept =
|
|
97
97
|
guardianVerifyCode !== undefined &&
|
|
98
|
-
(!!getPendingChallenge(
|
|
99
|
-
!!findActiveSession(
|
|
98
|
+
(!!getPendingChallenge(sourceChannel) ||
|
|
99
|
+
!!findActiveSession(sourceChannel));
|
|
100
100
|
|
|
101
101
|
if (
|
|
102
102
|
isDuplicate ||
|
|
@@ -108,7 +108,6 @@ export async function handleVerificationIntercept(
|
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
const verifyResult = validateAndConsumeChallenge(
|
|
111
|
-
canonicalAssistantId,
|
|
112
111
|
sourceChannel,
|
|
113
112
|
guardianVerifyCode,
|
|
114
113
|
canonicalSenderId ?? rawSenderId,
|
|
@@ -144,7 +143,6 @@ export async function handleVerificationIntercept(
|
|
|
144
143
|
: actorDisplayName;
|
|
145
144
|
|
|
146
145
|
upsertMember({
|
|
147
|
-
assistantId: canonicalAssistantId,
|
|
148
146
|
sourceChannel,
|
|
149
147
|
externalUserId: canonicalSenderId ?? rawSenderId,
|
|
150
148
|
externalChatId: conversationExternalId,
|
|
@@ -181,7 +179,7 @@ export async function handleVerificationIntercept(
|
|
|
181
179
|
);
|
|
182
180
|
} else {
|
|
183
181
|
// Revoke any existing active binding before creating a new one (same-user re-verification)
|
|
184
|
-
revokeGuardianBinding(
|
|
182
|
+
revokeGuardianBinding(sourceChannel);
|
|
185
183
|
|
|
186
184
|
const metadata: Record<string, string> = {};
|
|
187
185
|
if (actorUsername && actorUsername.trim().length > 0) {
|
|
@@ -202,7 +200,6 @@ export async function handleVerificationIntercept(
|
|
|
202
200
|
rawSenderId;
|
|
203
201
|
|
|
204
202
|
createGuardianBinding({
|
|
205
|
-
assistantId: canonicalAssistantId,
|
|
206
203
|
channel: sourceChannel,
|
|
207
204
|
guardianExternalUserId: canonicalSenderId ?? rawSenderId,
|
|
208
205
|
guardianDeliveryChatId: conversationExternalId,
|
|
@@ -235,7 +232,6 @@ export async function handleVerificationIntercept(
|
|
|
235
232
|
sourceEventName: "ingress.trusted_contact.activated",
|
|
236
233
|
sourceChannel,
|
|
237
234
|
sourceSessionId: conversationId,
|
|
238
|
-
assistantId: canonicalAssistantId,
|
|
239
235
|
attentionHints: {
|
|
240
236
|
requiresAction: false,
|
|
241
237
|
urgency: "low",
|
|
@@ -37,22 +37,22 @@ function mintPairingCredentials(
|
|
|
37
37
|
platform: string,
|
|
38
38
|
): PairingCredentials | null {
|
|
39
39
|
try {
|
|
40
|
-
const assistantId = DAEMON_INTERNAL_ASSISTANT_ID;
|
|
41
40
|
// Pairing can run before a local client has touched the actor-token
|
|
42
41
|
// bootstrap path. Ensure the vellum guardian principal exists so iOS
|
|
43
42
|
// pairings always have a mint target.
|
|
44
|
-
const guardianPrincipalId = ensureVellumGuardianBinding(
|
|
43
|
+
const guardianPrincipalId = ensureVellumGuardianBinding(
|
|
44
|
+
DAEMON_INTERNAL_ASSISTANT_ID,
|
|
45
|
+
);
|
|
45
46
|
const hashedDeviceId = hashDeviceId(deviceId);
|
|
46
47
|
|
|
47
48
|
const credentials = mintCredentialPair({
|
|
48
|
-
assistantId,
|
|
49
49
|
platform,
|
|
50
50
|
deviceId,
|
|
51
51
|
guardianPrincipalId,
|
|
52
52
|
hashedDeviceId,
|
|
53
53
|
});
|
|
54
54
|
|
|
55
|
-
log.info({
|
|
55
|
+
log.info({ platform }, "Minted credentials during pairing");
|
|
56
56
|
return {
|
|
57
57
|
accessToken: credentials.accessToken,
|
|
58
58
|
accessTokenExpiresAt: credentials.accessTokenExpiresAt,
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Route handler for fetching surface content by ID.
|
|
3
|
+
*
|
|
4
|
+
* GET /v1/surfaces/:surfaceId — return the full surface payload from the
|
|
5
|
+
* session's in-memory surface state. Used by clients to re-hydrate surfaces
|
|
6
|
+
* whose data was stripped during memory compaction.
|
|
7
|
+
*/
|
|
8
|
+
import type { SurfaceData, SurfaceType } from "../../daemon/ipc-contract/surfaces.js";
|
|
9
|
+
import { getLogger } from "../../util/logger.js";
|
|
10
|
+
import { httpError } from "../http-errors.js";
|
|
11
|
+
import type { RouteDefinition } from "../http-router.js";
|
|
12
|
+
|
|
13
|
+
const log = getLogger("surface-content-routes");
|
|
14
|
+
|
|
15
|
+
/** Narrow interface for looking up surface state from a session. */
|
|
16
|
+
interface SurfaceContentTarget {
|
|
17
|
+
surfaceState: Map<
|
|
18
|
+
string,
|
|
19
|
+
{ surfaceType: SurfaceType; data: SurfaceData; title?: string }
|
|
20
|
+
>;
|
|
21
|
+
currentTurnSurfaces?: Array<{
|
|
22
|
+
surfaceId: string;
|
|
23
|
+
surfaceType: SurfaceType;
|
|
24
|
+
title?: string;
|
|
25
|
+
data: SurfaceData;
|
|
26
|
+
actions?: Array<{ id: string; label: string; style?: string }>;
|
|
27
|
+
}>;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export type SurfaceContentSessionLookup = (
|
|
31
|
+
sessionId: string,
|
|
32
|
+
) => SurfaceContentTarget | undefined;
|
|
33
|
+
|
|
34
|
+
// ---------------------------------------------------------------------------
|
|
35
|
+
// Route definitions
|
|
36
|
+
// ---------------------------------------------------------------------------
|
|
37
|
+
|
|
38
|
+
export function surfaceContentRouteDefinitions(deps: {
|
|
39
|
+
findSession?: SurfaceContentSessionLookup;
|
|
40
|
+
}): RouteDefinition[] {
|
|
41
|
+
return [
|
|
42
|
+
{
|
|
43
|
+
endpoint: "surfaces/:surfaceId",
|
|
44
|
+
method: "GET",
|
|
45
|
+
handler: ({ url, params }) => {
|
|
46
|
+
if (!deps.findSession) {
|
|
47
|
+
return httpError(
|
|
48
|
+
"NOT_IMPLEMENTED",
|
|
49
|
+
"Surface content lookup not available",
|
|
50
|
+
501,
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const sessionId = url.searchParams.get("sessionId");
|
|
55
|
+
if (!sessionId) {
|
|
56
|
+
return httpError("BAD_REQUEST", "sessionId query parameter is required", 400);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const surfaceId = params.surfaceId;
|
|
60
|
+
if (!surfaceId) {
|
|
61
|
+
return httpError("BAD_REQUEST", "surfaceId path parameter is required", 400);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const session = deps.findSession(sessionId);
|
|
65
|
+
if (!session) {
|
|
66
|
+
return httpError(
|
|
67
|
+
"NOT_FOUND",
|
|
68
|
+
"No active session found for this sessionId",
|
|
69
|
+
404,
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Look up the surface in the session's in-memory state.
|
|
74
|
+
const stored = session.surfaceState.get(surfaceId);
|
|
75
|
+
if (stored) {
|
|
76
|
+
log.info({ sessionId, surfaceId }, "Surface content served from surfaceState");
|
|
77
|
+
return Response.json({
|
|
78
|
+
surfaceId,
|
|
79
|
+
surfaceType: stored.surfaceType,
|
|
80
|
+
title: stored.title ?? null,
|
|
81
|
+
data: stored.data,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Fall back to currentTurnSurfaces in case the surface hasn't been
|
|
86
|
+
// committed to surfaceState yet (e.g. mid-turn).
|
|
87
|
+
const turnSurface = session.currentTurnSurfaces?.find(
|
|
88
|
+
(s) => s.surfaceId === surfaceId,
|
|
89
|
+
);
|
|
90
|
+
if (turnSurface) {
|
|
91
|
+
log.info({ sessionId, surfaceId }, "Surface content served from currentTurnSurfaces");
|
|
92
|
+
return Response.json({
|
|
93
|
+
surfaceId,
|
|
94
|
+
surfaceType: turnSurface.surfaceType,
|
|
95
|
+
title: turnSurface.title ?? null,
|
|
96
|
+
data: turnSurface.data,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return httpError("NOT_FOUND", "Surface not found in session", 404);
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
];
|
|
104
|
+
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { mkdirSync } from "node:fs";
|
|
1
|
+
import { existsSync, mkdirSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
|
|
4
4
|
import { getLogger } from "../../util/logger.js";
|
|
5
5
|
import { getDataDir } from "../../util/platform.js";
|
|
6
6
|
import { authSessionCache } from "./auth-cache.js";
|
|
7
7
|
import type { ExtractedCredential } from "./network-recording-types.js";
|
|
8
|
-
import {
|
|
8
|
+
import { importPlaywright } from "./runtime-check.js";
|
|
9
9
|
|
|
10
10
|
const log = getLogger("browser-manager");
|
|
11
11
|
|
|
@@ -129,20 +129,6 @@ export function setLaunchFn(fn: LaunchFn | null): void {
|
|
|
129
129
|
launchPersistentContext = fn;
|
|
130
130
|
}
|
|
131
131
|
|
|
132
|
-
async function getDefaultLaunchFn(): Promise<LaunchFn> {
|
|
133
|
-
const pw = await import("playwright");
|
|
134
|
-
// In compiled Bun binaries, CJS→ESM interop may place named exports
|
|
135
|
-
// under .default instead of at the top level of the module namespace.
|
|
136
|
-
const chromium =
|
|
137
|
-
pw.chromium ?? (pw.default as typeof pw | undefined)?.chromium;
|
|
138
|
-
if (!chromium) {
|
|
139
|
-
throw new Error(
|
|
140
|
-
"Failed to resolve Playwright chromium — the module loaded but 'chromium' is missing",
|
|
141
|
-
);
|
|
142
|
-
}
|
|
143
|
-
return chromium.launchPersistentContext.bind(chromium);
|
|
144
|
-
}
|
|
145
|
-
|
|
146
132
|
function getProfileDir(): string {
|
|
147
133
|
return join(getDataDir(), "browser-profile");
|
|
148
134
|
}
|
|
@@ -177,10 +163,24 @@ class BrowserManager {
|
|
|
177
163
|
this.contextCreating = (async () => {
|
|
178
164
|
await authSessionCache.load();
|
|
179
165
|
|
|
180
|
-
//
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
166
|
+
// Resolve launch function: use injected test launcher or resolve
|
|
167
|
+
// playwright (may install at runtime in compiled binaries).
|
|
168
|
+
let launch: LaunchFn;
|
|
169
|
+
if (launchPersistentContext) {
|
|
170
|
+
launch = launchPersistentContext;
|
|
171
|
+
} else {
|
|
172
|
+
const pw = await importPlaywright();
|
|
173
|
+
|
|
174
|
+
// Auto-install Chromium if the browser binary is missing
|
|
175
|
+
let chromiumInstalled = false;
|
|
176
|
+
try {
|
|
177
|
+
const execPath = pw.chromium.executablePath();
|
|
178
|
+
chromiumInstalled = existsSync(execPath);
|
|
179
|
+
} catch {
|
|
180
|
+
// executablePath() may throw if registry is missing
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (!chromiumInstalled) {
|
|
184
184
|
log.info("Chromium not installed, installing via playwright...");
|
|
185
185
|
const proc = Bun.spawn(
|
|
186
186
|
["bunx", "playwright", "install", "chromium"],
|
|
@@ -213,11 +213,12 @@ class BrowserManager {
|
|
|
213
213
|
throw new Error(`Failed to install Chromium: ${msg}`);
|
|
214
214
|
}
|
|
215
215
|
}
|
|
216
|
+
|
|
217
|
+
launch = pw.chromium.launchPersistentContext.bind(pw.chromium);
|
|
216
218
|
}
|
|
217
219
|
|
|
218
220
|
const profileDir = getProfileDir();
|
|
219
221
|
mkdirSync(profileDir, { recursive: true });
|
|
220
|
-
const launch = launchPersistentContext ?? (await getDefaultLaunchFn());
|
|
221
222
|
const headless = !canDisplayGui();
|
|
222
223
|
const ctx = await launch(profileDir, { headless });
|
|
223
224
|
log.info(
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import { existsSync } from "node:fs";
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
|
|
4
|
+
import { getWorkspaceDir } from "../../util/platform.js";
|
|
2
5
|
|
|
3
6
|
export interface BrowserRuntimeStatus {
|
|
4
7
|
playwrightAvailable: boolean;
|
|
@@ -7,14 +10,116 @@ export interface BrowserRuntimeStatus {
|
|
|
7
10
|
error: string | null;
|
|
8
11
|
}
|
|
9
12
|
|
|
13
|
+
/**
|
|
14
|
+
* Resolve playwright's chromium export from a module namespace object,
|
|
15
|
+
* handling CJS→ESM interop where named exports may land under .default.
|
|
16
|
+
*/
|
|
17
|
+
function resolveChromium(
|
|
18
|
+
pw: Record<string, unknown>,
|
|
19
|
+
): Record<string, unknown> | undefined {
|
|
20
|
+
if (pw.chromium) return pw;
|
|
21
|
+
const def = pw.default as Record<string, unknown> | undefined;
|
|
22
|
+
if (def?.chromium) return def;
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Try importing playwright from the bundled binary. Returns the module
|
|
28
|
+
* if chromium is resolvable, otherwise undefined. This never installs
|
|
29
|
+
* anything — safe for diagnostic/read-only use.
|
|
30
|
+
*/
|
|
31
|
+
async function tryBundledPlaywright(): Promise<
|
|
32
|
+
typeof import("playwright") | undefined
|
|
33
|
+
> {
|
|
34
|
+
try {
|
|
35
|
+
const pw = await import("playwright");
|
|
36
|
+
const mod = resolveChromium(pw as unknown as Record<string, unknown>);
|
|
37
|
+
if (mod?.chromium) return mod as unknown as typeof import("playwright");
|
|
38
|
+
} catch {
|
|
39
|
+
// Bundled import failed entirely
|
|
40
|
+
}
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Resolve the package entry point from its package.json exports/main fields.
|
|
46
|
+
*/
|
|
47
|
+
function resolvePackageEntry(pkgDir: string): string {
|
|
48
|
+
try {
|
|
49
|
+
const pkg = JSON.parse(readFileSync(join(pkgDir, "package.json"), "utf-8"));
|
|
50
|
+
// Prefer ESM entry from exports map
|
|
51
|
+
const exportsRoot = pkg.exports?.["."];
|
|
52
|
+
if (typeof exportsRoot === "object" && exportsRoot?.import) {
|
|
53
|
+
return join(pkgDir, exportsRoot.import);
|
|
54
|
+
}
|
|
55
|
+
if (pkg.module) return join(pkgDir, pkg.module);
|
|
56
|
+
if (pkg.main) return join(pkgDir, pkg.main);
|
|
57
|
+
} catch {
|
|
58
|
+
// Fall through to default
|
|
59
|
+
}
|
|
60
|
+
return join(pkgDir, "index.mjs");
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Import playwright, falling back to a runtime-installed copy if the
|
|
65
|
+
* bundled import fails (compiled Bun binaries can't initialize
|
|
66
|
+
* playwright's in-process client/server bridge correctly).
|
|
67
|
+
*/
|
|
68
|
+
export async function importPlaywright(): Promise<typeof import("playwright")> {
|
|
69
|
+
// Try bundled import (works in dev/source mode)
|
|
70
|
+
const bundled = await tryBundledPlaywright();
|
|
71
|
+
if (bundled) return bundled;
|
|
72
|
+
|
|
73
|
+
// Compiled binary fallback: install playwright to disk and import
|
|
74
|
+
// from an absolute path so the JS runtime resolves it from the
|
|
75
|
+
// filesystem instead of the compiled module cache.
|
|
76
|
+
const externalDir = join(getWorkspaceDir(), "external");
|
|
77
|
+
const pwPkg = join(externalDir, "node_modules", "playwright");
|
|
78
|
+
|
|
79
|
+
if (!existsSync(join(pwPkg, "package.json"))) {
|
|
80
|
+
mkdirSync(externalDir, { recursive: true });
|
|
81
|
+
if (!existsSync(join(externalDir, "package.json"))) {
|
|
82
|
+
writeFileSync(join(externalDir, "package.json"), '{"private":true}\n');
|
|
83
|
+
}
|
|
84
|
+
const proc = Bun.spawn(["bun", "add", "playwright"], {
|
|
85
|
+
cwd: externalDir,
|
|
86
|
+
stdout: "pipe",
|
|
87
|
+
stderr: "pipe",
|
|
88
|
+
});
|
|
89
|
+
const exitCode = await proc.exited;
|
|
90
|
+
if (exitCode !== 0) {
|
|
91
|
+
const stderr = await new Response(proc.stderr).text();
|
|
92
|
+
throw new Error(`Failed to install playwright: ${stderr}`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Dynamic import with a runtime-computed path — bun can't statically
|
|
97
|
+
// analyze this, so it resolves from the filesystem at runtime.
|
|
98
|
+
const entryPath = resolvePackageEntry(pwPkg);
|
|
99
|
+
const pw: Record<string, unknown> = await import(entryPath);
|
|
100
|
+
const mod = resolveChromium(pw);
|
|
101
|
+
if (!mod?.chromium) {
|
|
102
|
+
throw new Error(
|
|
103
|
+
"Failed to resolve Playwright chromium from runtime-installed copy",
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
return mod as unknown as typeof import("playwright");
|
|
107
|
+
}
|
|
108
|
+
|
|
10
109
|
export async function checkBrowserRuntime(): Promise<BrowserRuntimeStatus> {
|
|
11
|
-
//
|
|
110
|
+
// Diagnostic only — no side effects (no playwright installation)
|
|
12
111
|
let chromium: { executablePath: () => string };
|
|
13
112
|
try {
|
|
14
|
-
const pw = await
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
113
|
+
const pw = await tryBundledPlaywright();
|
|
114
|
+
if (!pw) {
|
|
115
|
+
return {
|
|
116
|
+
playwrightAvailable: false,
|
|
117
|
+
chromiumInstalled: false,
|
|
118
|
+
chromiumPath: null,
|
|
119
|
+
error: "playwright package not available",
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
chromium = pw.chromium;
|
|
18
123
|
} catch {
|
|
19
124
|
return {
|
|
20
125
|
playwrightAvailable: false,
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { startCall } from "../../calls/call-domain.js";
|
|
2
2
|
import { getConfig } from "../../config/loader.js";
|
|
3
|
-
import { DAEMON_INTERNAL_ASSISTANT_ID } from "../../runtime/assistant-scope.js";
|
|
4
3
|
import { findActiveSession } from "../../runtime/channel-guardian-service.js";
|
|
5
4
|
import { normalizePhoneNumber } from "../../util/phone.js";
|
|
6
5
|
import type { ToolContext, ToolExecutionResult } from "../types.js";
|
|
@@ -22,8 +21,7 @@ export async function executeCallStart(
|
|
|
22
21
|
? normalizePhoneNumber(input.phone_number)
|
|
23
22
|
: null;
|
|
24
23
|
if (requestedPhone) {
|
|
25
|
-
const
|
|
26
|
-
const activeVoiceVerification = findActiveSession(assistantId, "voice");
|
|
24
|
+
const activeVoiceVerification = findActiveSession("voice");
|
|
27
25
|
const verificationDestination =
|
|
28
26
|
activeVoiceVerification?.destinationAddress ??
|
|
29
27
|
activeVoiceVerification?.expectedPhoneE164;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { getContact } from "../../contacts/contact-store.js";
|
|
2
2
|
import { createFollowUp } from "../../followups/followup-store.js";
|
|
3
3
|
import type { FollowUp } from "../../followups/types.js";
|
|
4
|
-
import { DAEMON_INTERNAL_ASSISTANT_ID } from "../../runtime/assistant-scope.js";
|
|
5
4
|
import type { ToolContext, ToolExecutionResult } from "../types.js";
|
|
6
5
|
|
|
7
6
|
function formatFollowUp(f: FollowUp): string {
|
|
@@ -63,7 +62,7 @@ export async function executeFollowupCreate(
|
|
|
63
62
|
|
|
64
63
|
// Validate contact exists if provided
|
|
65
64
|
if (contactId) {
|
|
66
|
-
const contact = getContact(contactId
|
|
65
|
+
const contact = getContact(contactId);
|
|
67
66
|
if (!contact) {
|
|
68
67
|
return {
|
|
69
68
|
content: `Error: Contact "${contactId}" not found`,
|
|
@@ -5,7 +5,6 @@ import {
|
|
|
5
5
|
updateCanonicalGuardianRequest,
|
|
6
6
|
} from "../memory/canonical-guardian-store.js";
|
|
7
7
|
import { isUntrustedTrustClass } from "../runtime/actor-trust-resolver.js";
|
|
8
|
-
import { DAEMON_INTERNAL_ASSISTANT_ID } from "../runtime/assistant-scope.js";
|
|
9
8
|
import { createOrReuseToolGrantRequest } from "../runtime/tool-grant-request-helper.js";
|
|
10
9
|
import { computeToolApprovalDigest } from "../security/tool-approval-digest.js";
|
|
11
10
|
import { getTaskRunRules } from "../tasks/ephemeral-permissions.js";
|
|
@@ -275,7 +274,6 @@ export class ToolApprovalHandler {
|
|
|
275
274
|
inputDigest,
|
|
276
275
|
consumingRequestId:
|
|
277
276
|
context.requestId ?? `preexec-${context.sessionId}-${Date.now()}`,
|
|
278
|
-
assistantId: context.assistantId ?? DAEMON_INTERNAL_ASSISTANT_ID,
|
|
279
277
|
executionChannel: context.executionChannel,
|
|
280
278
|
conversationId: context.conversationId,
|
|
281
279
|
callSessionId: context.callSessionId,
|