@vellumai/assistant 0.4.35 → 0.4.37
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +1 -1
- package/ARCHITECTURE.md +44 -49
- package/README.md +32 -20
- package/docs/architecture/keychain-broker.md +186 -0
- package/docs/architecture/security.md +110 -116
- package/docs/runbook-trusted-contacts.md +2 -2
- package/docs/skills.md +25 -25
- package/package.json +5 -2
- package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +11 -2
- package/src/__tests__/actor-token-service.test.ts +1 -0
- package/src/__tests__/amazon-cdp-integration.test.ts +74 -0
- package/src/__tests__/assistant-feature-flags-integration.test.ts +38 -9
- package/src/__tests__/assistant-id-boundary-guard.test.ts +29 -0
- package/src/__tests__/browser-fill-credential.test.ts +1 -1
- package/src/__tests__/bundle-scanner.test.ts +1 -1
- package/src/__tests__/channel-guardian.test.ts +102 -102
- package/src/__tests__/channel-invite-transport.test.ts +155 -256
- package/src/__tests__/channel-readiness-routes.test.ts +336 -0
- package/src/__tests__/checker.test.ts +6 -6
- package/src/__tests__/chrome-cdp.test.ts +350 -0
- package/src/__tests__/computer-use-session-lifecycle.test.ts +3 -3
- package/src/__tests__/computer-use-session-working-dir.test.ts +86 -52
- package/src/__tests__/computer-use-skill-lifecycle-cleanup.test.ts +1 -1
- package/src/__tests__/config-loader-migration.test.ts +85 -0
- package/src/__tests__/conversation-pairing.test.ts +370 -5
- package/src/__tests__/credential-broker-browser-fill.test.ts +1 -10
- package/src/__tests__/credential-broker-server-use.test.ts +1 -10
- package/src/__tests__/credential-security-e2e.test.ts +7 -1
- package/src/__tests__/credential-security-invariants.test.ts +14 -20
- package/src/__tests__/credential-vault-unit.test.ts +1 -11
- package/src/__tests__/credential-vault.test.ts +5 -19
- package/src/__tests__/credentials-cli.test.ts +814 -0
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +23 -4
- package/src/__tests__/email-invite-adapter.test.ts +78 -0
- package/src/__tests__/email-service-config-fallback.test.ts +102 -0
- package/src/__tests__/encrypted-store.test.ts +6 -6
- package/src/__tests__/ephemeral-permissions.test.ts +3 -3
- package/src/__tests__/gateway-only-enforcement.test.ts +5 -1
- package/src/__tests__/guardian-actions-endpoint.test.ts +70 -12
- package/src/__tests__/guardian-outbound-http.test.ts +53 -47
- package/src/__tests__/handle-user-message-secret-resume.test.ts +23 -0
- package/src/__tests__/handlers-add-trust-rule-metadata.test.ts +32 -23
- package/src/__tests__/handlers-telegram-config.test.ts +8 -2
- package/src/__tests__/handlers-twitter-config.test.ts +2 -2
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +108 -7
- package/src/__tests__/ingress-reconcile.test.ts +6 -0
- package/src/__tests__/intent-routing.test.ts +23 -4
- package/src/__tests__/invite-routes-http.test.ts +12 -0
- package/src/__tests__/ipc-snapshot.test.ts +8 -2
- package/src/__tests__/keychain-broker-client.test.ts +543 -0
- package/src/__tests__/llm-usage-store.test.ts +344 -0
- package/src/__tests__/mcp-client-auth.test.ts +2 -2
- package/src/__tests__/media-reuse-story.e2e.test.ts +1 -1
- package/src/__tests__/migration-transport.test.ts +49 -0
- package/src/__tests__/notification-broadcaster.test.ts +205 -5
- package/src/__tests__/notification-deep-link.test.ts +365 -1
- package/src/__tests__/oauth-connect-handler.test.ts +2 -2
- package/src/__tests__/onboarding-starter-tasks.test.ts +17 -4
- package/src/__tests__/proxy-approval-callback.test.ts +1 -1
- package/src/__tests__/recording-handler.test.ts +1 -1
- package/src/__tests__/recording-intent-handler.test.ts +6 -1
- package/src/__tests__/recording-state-machine.test.ts +1 -1
- package/src/__tests__/relay-server.test.ts +9 -1
- package/src/__tests__/ride-shotgun-handler.test.ts +499 -0
- package/src/__tests__/runtime-attachment-metadata.test.ts +160 -1
- package/src/__tests__/script-proxy-injection-runtime.test.ts +299 -2
- package/src/__tests__/script-proxy-profile-template-fallback.test.ts +1 -1
- package/src/__tests__/secret-onetime-send.test.ts +8 -2
- package/src/__tests__/secure-keys.test.ts +175 -216
- package/src/__tests__/session-confirmation-signals.test.ts +1 -1
- package/src/__tests__/session-messaging-secret-redirect.test.ts +1 -1
- package/src/__tests__/session-queue.test.ts +2 -1
- package/src/__tests__/session-tool-setup-app-refresh.test.ts +2 -2
- package/src/__tests__/skill-feature-flags-integration.test.ts +29 -4
- package/src/__tests__/skill-feature-flags.test.ts +12 -9
- package/src/__tests__/skill-load-feature-flag.test.ts +26 -5
- package/src/__tests__/skill-projection.benchmark.test.ts +0 -1
- package/src/__tests__/skills.test.ts +34 -4
- package/src/__tests__/slack-channel-config.test.ts +2 -2
- package/src/__tests__/system-prompt.test.ts +26 -4
- package/src/__tests__/telegram-bot-username-resolution.test.ts +212 -0
- package/src/__tests__/telegram-invite-adapter.test.ts +164 -0
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +1 -1
- package/src/__tests__/tool-permission-simulate-handler.test.ts +8 -2
- package/src/__tests__/trusted-contact-approval-notifier.test.ts +9 -1
- package/src/__tests__/twitter-auth-handler.test.ts +2 -2
- package/src/__tests__/twitter-oauth-client.test.ts +1 -1
- package/src/__tests__/usage-routes.test.ts +339 -0
- package/src/__tests__/whatsapp-invite-adapter.test.ts +94 -0
- package/src/agent/loop.ts +3 -0
- package/src/amazon/checkout.ts +0 -1
- package/src/approvals/guardian-request-resolvers.ts +9 -1
- package/src/bundler/app-bundler.ts +28 -12
- package/src/bundler/bundle-scanner.ts +1 -1
- package/src/bundler/bundle-signer.ts +3 -3
- package/src/bundler/manifest.ts +1 -1
- package/src/bundler/signature-verifier.ts +3 -3
- package/src/channels/config.ts +1 -1
- package/src/cli/AGENTS.md +63 -0
- package/src/cli/__tests__/notifications.test.ts +470 -0
- package/src/cli/amazon.ts +344 -167
- package/src/cli/audit.ts +85 -0
- package/src/cli/autonomy.ts +369 -0
- package/src/cli/channels.ts +51 -0
- package/src/cli/completions.ts +208 -0
- package/src/cli/config.ts +220 -0
- package/src/cli/contacts.ts +471 -0
- package/src/cli/credentials.ts +564 -0
- package/src/cli/default-action.ts +14 -0
- package/src/cli/dev.ts +131 -0
- package/src/cli/doctor.ts +398 -0
- package/src/cli/email.ts +494 -0
- package/src/cli/influencer.ts +72 -0
- package/src/cli/integrations.ts +248 -57
- package/src/cli/keys.ts +114 -0
- package/src/cli/map.ts +46 -54
- package/src/cli/mcp.ts +111 -3
- package/src/cli/{config-commands.ts → memory.ts} +134 -245
- package/src/cli/notifications.ts +407 -0
- package/src/cli/program.ts +65 -0
- package/src/cli/reference.ts +48 -0
- package/src/cli/sequence.ts +154 -0
- package/src/cli/sessions.ts +262 -0
- package/src/cli/trust.ts +175 -0
- package/src/cli/twitter.ts +323 -106
- package/src/config/__tests__/build-cli-reference-section.test.ts +49 -0
- package/src/config/bundled-skills/amazon/SKILL.md +2 -2
- package/src/config/bundled-skills/app-builder/TOOLS.json +26 -0
- package/src/config/bundled-skills/app-builder/tools/app-generate-icon.ts +13 -0
- package/src/config/bundled-skills/contacts/SKILL.md +178 -10
- package/src/config/bundled-skills/doordash/doordash-cli.ts +23 -168
- package/src/config/bundled-skills/google-oauth-setup/SKILL.md +135 -34
- package/src/config/bundled-skills/messaging/tools/shared.ts +4 -1
- package/src/config/bundled-skills/twilio-setup/SKILL.md +70 -17
- package/src/config/bundled-tool-registry.ts +2 -0
- package/src/config/core-schema.ts +7 -0
- package/src/config/feature-flag-registry.json +16 -0
- package/src/config/loader.ts +26 -0
- package/src/config/schema.ts +4 -0
- package/src/config/skill-state.ts +0 -13
- package/src/config/system-prompt.ts +27 -0
- package/src/contacts/contact-store.ts +25 -0
- package/src/daemon/computer-use-session.ts +1 -1
- package/src/daemon/handlers/apps.ts +1 -0
- package/src/daemon/handlers/config-channels.ts +3 -3
- package/src/daemon/handlers/config-dispatch.ts +29 -0
- package/src/daemon/handlers/config-inbox.ts +4 -3
- package/src/daemon/handlers/config.ts +3 -43
- package/src/daemon/handlers/contacts.ts +34 -0
- package/src/daemon/handlers/index.ts +17 -3
- package/src/daemon/handlers/session-user-message.ts +7 -0
- package/src/daemon/handlers/sessions.ts +21 -2
- package/src/daemon/handlers/shared.ts +17 -0
- package/src/daemon/ipc-contract/apps.ts +2 -0
- package/src/daemon/ipc-contract/computer-use.ts +9 -0
- package/src/daemon/ipc-contract/contacts.ts +3 -3
- package/src/daemon/ipc-contract/inbox.ts +2 -0
- package/src/daemon/ipc-contract/messages.ts +4 -0
- package/src/daemon/ipc-contract/sessions.ts +8 -0
- package/src/daemon/ipc-contract-inventory.json +1 -0
- package/src/daemon/lifecycle.ts +0 -5
- package/src/daemon/ride-shotgun-handler.ts +139 -25
- package/src/daemon/session-agent-loop-handlers.ts +100 -0
- package/src/daemon/session-agent-loop.ts +72 -0
- package/src/daemon/session-tool-setup.ts +7 -0
- package/src/daemon/session.ts +23 -1
- package/src/daemon/tool-side-effects.ts +39 -1
- package/src/email/service.ts +59 -2
- package/src/index.ts +2 -60
- package/src/mcp/mcp-oauth-provider.ts +90 -8
- package/src/media/app-icon-generator.ts +86 -0
- package/src/memory/db-init.ts +11 -0
- package/src/memory/llm-usage-store.ts +186 -0
- package/src/memory/migrations/137-usage-dashboard-indexes.ts +26 -0
- package/src/memory/migrations/139-drop-usage-composite-indexes.ts +30 -0
- package/src/memory/migrations/index.ts +2 -0
- package/src/memory/schema-migration.ts +1 -0
- package/src/memory/shared-app-links-store.ts +1 -1
- package/src/messaging/registry.ts +27 -0
- package/src/notifications/README.md +79 -70
- package/src/notifications/broadcaster.ts +2 -1
- package/src/notifications/conversation-pairing.ts +147 -13
- package/src/notifications/copy-composer.ts +7 -3
- package/src/notifications/destination-resolver.ts +14 -1
- package/src/notifications/emit-signal.ts +3 -2
- package/src/notifications/signal.ts +105 -1
- package/src/notifications/types.ts +16 -0
- package/src/permissions/checker.ts +29 -3
- package/src/permissions/prompter.ts +11 -3
- package/src/runtime/access-request-helper.ts +2 -1
- package/src/runtime/auth/route-policy.ts +7 -1
- package/src/runtime/channel-invite-transport.ts +40 -63
- package/src/runtime/channel-invite-transports/email.ts +13 -39
- package/src/runtime/channel-invite-transports/slack.ts +5 -34
- package/src/runtime/channel-invite-transports/sms.ts +8 -29
- package/src/runtime/channel-invite-transports/telegram.ts +69 -28
- package/src/runtime/channel-invite-transports/voice.ts +0 -7
- package/src/runtime/channel-invite-transports/whatsapp.ts +43 -0
- package/src/runtime/channel-readiness-service.ts +202 -45
- package/src/runtime/confirmation-request-guardian-bridge.ts +2 -1
- package/src/runtime/guardian-outbound-actions.ts +8 -5
- package/src/runtime/http-server.ts +2 -0
- package/src/runtime/invite-instruction-generator.ts +178 -0
- package/src/runtime/invite-service.ts +22 -25
- package/src/runtime/migrations/migration-transport.ts +13 -0
- package/src/runtime/routes/app-routes.ts +1 -1
- package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +8 -7
- package/src/runtime/routes/channel-readiness-routes.ts +30 -11
- package/src/runtime/routes/contact-routes.ts +54 -26
- package/src/runtime/routes/inbound-stages/bootstrap-intercept.ts +1 -1
- package/src/runtime/routes/inbound-stages/escalation-intercept.ts +2 -1
- package/src/runtime/routes/inbound-stages/verification-intercept.ts +2 -1
- package/src/runtime/routes/integration-routes.ts +1 -1
- package/src/runtime/routes/invite-routes.ts +1 -1
- package/src/runtime/routes/secret-routes.ts +31 -7
- package/src/runtime/routes/twilio-routes.ts +32 -1
- package/src/runtime/routes/usage-routes.ts +114 -0
- package/src/runtime/tool-grant-request-helper.ts +2 -1
- package/src/security/encrypted-store.ts +9 -5
- package/src/security/keychain-broker-client.ts +393 -0
- package/src/security/secure-keys.ts +106 -321
- package/src/tools/apps/executors.ts +73 -0
- package/src/tools/browser/auto-navigate.ts +15 -6
- package/src/tools/browser/chrome-cdp.ts +211 -0
- package/src/tools/browser/network-recorder.test.ts +83 -0
- package/src/tools/browser/network-recorder.ts +8 -7
- package/src/tools/browser/x-auto-navigate.ts +12 -6
- package/src/tools/credentials/policy-types.ts +24 -0
- package/src/tools/credentials/vault.ts +22 -27
- package/src/tools/network/script-proxy/session-manager.ts +47 -3
- package/src/tools/permission-checker.ts +1 -0
- package/src/tools/types.ts +2 -0
- package/src/tools/ui-surface/definitions.ts +1 -2
- package/src/tools/watch/watch-state.ts +2 -0
- package/src/__tests__/key-migration.test.ts +0 -240
- package/src/__tests__/keychain.test.ts +0 -286
- package/src/cli/core-commands.ts +0 -899
- package/src/security/keychain-to-encrypted-migration.ts +0 -66
- package/src/security/keychain.ts +0 -490
|
@@ -218,11 +218,11 @@ export function revokeGuardianForChannel(
|
|
|
218
218
|
// Guardian verification handler
|
|
219
219
|
// ---------------------------------------------------------------------------
|
|
220
220
|
|
|
221
|
-
export function handleGuardianVerification(
|
|
221
|
+
export async function handleGuardianVerification(
|
|
222
222
|
msg: GuardianVerificationRequest,
|
|
223
223
|
socket: net.Socket,
|
|
224
224
|
ctx: HandlerContext,
|
|
225
|
-
): void {
|
|
225
|
+
): Promise<void> {
|
|
226
226
|
const channel = msg.channel ?? "telegram";
|
|
227
227
|
|
|
228
228
|
try {
|
|
@@ -240,7 +240,7 @@ export function handleGuardianVerification(
|
|
|
240
240
|
const result = revokeGuardianForChannel(channel);
|
|
241
241
|
ctx.send(socket, { type: "guardian_verification_response", ...result });
|
|
242
242
|
} else if (msg.action === "start_outbound") {
|
|
243
|
-
const result = startOutbound({
|
|
243
|
+
const result = await startOutbound({
|
|
244
244
|
channel,
|
|
245
245
|
destination: msg.destination,
|
|
246
246
|
rebind: msg.rebind,
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { channelHandlers } from "./config-channels.js";
|
|
2
|
+
import { heartbeatHandlers } from "./config-heartbeat.js";
|
|
3
|
+
import { ingressHandlers } from "./config-ingress.js";
|
|
4
|
+
import { integrationHandlers } from "./config-integrations.js";
|
|
5
|
+
import { modelHandlers } from "./config-model.js";
|
|
6
|
+
import { platformHandlers } from "./config-platform.js";
|
|
7
|
+
import { schedulingHandlers } from "./config-scheduling.js";
|
|
8
|
+
import { slackHandlers } from "./config-slack.js";
|
|
9
|
+
import { telegramHandlers } from "./config-telegram.js";
|
|
10
|
+
import { toolHandlers } from "./config-tools.js";
|
|
11
|
+
import { trustHandlers } from "./config-trust.js";
|
|
12
|
+
import { voiceHandlers } from "./config-voice.js";
|
|
13
|
+
|
|
14
|
+
// Keep the dispatch map isolated from the public config barrel so direct
|
|
15
|
+
// handler imports do not race with index.ts when Bun evaluates modules eagerly.
|
|
16
|
+
export const configHandlers = {
|
|
17
|
+
...modelHandlers,
|
|
18
|
+
...trustHandlers,
|
|
19
|
+
...schedulingHandlers,
|
|
20
|
+
...slackHandlers,
|
|
21
|
+
...ingressHandlers,
|
|
22
|
+
...platformHandlers,
|
|
23
|
+
...integrationHandlers,
|
|
24
|
+
...telegramHandlers,
|
|
25
|
+
...channelHandlers,
|
|
26
|
+
...toolHandlers,
|
|
27
|
+
...heartbeatHandlers,
|
|
28
|
+
...voiceHandlers,
|
|
29
|
+
};
|
|
@@ -32,19 +32,20 @@ import {
|
|
|
32
32
|
renderHistoryContent,
|
|
33
33
|
} from "./shared.js";
|
|
34
34
|
|
|
35
|
-
export function handleContactsInvite(
|
|
35
|
+
export async function handleContactsInvite(
|
|
36
36
|
msg: ContactsInviteRequest,
|
|
37
37
|
socket: net.Socket,
|
|
38
38
|
ctx: HandlerContext,
|
|
39
|
-
): void {
|
|
39
|
+
): Promise<void> {
|
|
40
40
|
try {
|
|
41
41
|
switch (msg.action) {
|
|
42
42
|
case "create": {
|
|
43
|
-
const result = createIngressInvite({
|
|
43
|
+
const result = await createIngressInvite({
|
|
44
44
|
sourceChannel: msg.sourceChannel,
|
|
45
45
|
note: msg.note,
|
|
46
46
|
maxUses: msg.maxUses,
|
|
47
47
|
expiresInMs: msg.expiresInMs,
|
|
48
|
+
contactName: msg.contactName,
|
|
48
49
|
friendName: msg.friendName,
|
|
49
50
|
guardianName: msg.guardianName,
|
|
50
51
|
});
|
|
@@ -1,18 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Config handler barrel — re-exports
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* Individual handlers live in domain-specific files:
|
|
6
|
-
* config-model.ts — Model selection (LLM + image gen)
|
|
7
|
-
* config-trust.ts — Trust rules (permissions allowlist)
|
|
8
|
-
* config-scheduling.ts — Schedules & reminders
|
|
9
|
-
* config-slack.ts — Slack webhook sharing
|
|
10
|
-
* config-ingress.ts — Public ingress URL & gateway reconciliation
|
|
11
|
-
* config-platform.ts — Platform base URL configuration
|
|
12
|
-
* config-integrations.ts — Vercel API & Twitter integration
|
|
13
|
-
* config-telegram.ts — Telegram bot configuration
|
|
14
|
-
* config-channels.ts — Channel guardian verification
|
|
15
|
-
* config-tools.ts — Env vars, tool permission simulation, tool names
|
|
2
|
+
* Config handler barrel — re-exports direct handler entry points for tests and
|
|
3
|
+
* feature code. The aggregate dispatch map now lives in config-dispatch.ts so
|
|
4
|
+
* direct imports do not participate in the runtime handler graph.
|
|
16
5
|
*/
|
|
17
6
|
|
|
18
7
|
// Re-export individual handlers for direct import by tests and other modules
|
|
@@ -76,32 +65,3 @@ export {
|
|
|
76
65
|
handleVoiceConfigUpdate,
|
|
77
66
|
normalizeActivationKey,
|
|
78
67
|
} from "./config-voice.js";
|
|
79
|
-
|
|
80
|
-
// Assemble the combined dispatch map from domain-specific handler groups
|
|
81
|
-
import { channelHandlers } from "./config-channels.js";
|
|
82
|
-
import { heartbeatHandlers } from "./config-heartbeat.js";
|
|
83
|
-
import { ingressHandlers } from "./config-ingress.js";
|
|
84
|
-
import { integrationHandlers } from "./config-integrations.js";
|
|
85
|
-
import { modelHandlers } from "./config-model.js";
|
|
86
|
-
import { platformHandlers } from "./config-platform.js";
|
|
87
|
-
import { schedulingHandlers } from "./config-scheduling.js";
|
|
88
|
-
import { slackHandlers } from "./config-slack.js";
|
|
89
|
-
import { telegramHandlers } from "./config-telegram.js";
|
|
90
|
-
import { toolHandlers } from "./config-tools.js";
|
|
91
|
-
import { trustHandlers } from "./config-trust.js";
|
|
92
|
-
import { voiceHandlers } from "./config-voice.js";
|
|
93
|
-
|
|
94
|
-
export const configHandlers = {
|
|
95
|
-
...modelHandlers,
|
|
96
|
-
...trustHandlers,
|
|
97
|
-
...schedulingHandlers,
|
|
98
|
-
...slackHandlers,
|
|
99
|
-
...ingressHandlers,
|
|
100
|
-
...platformHandlers,
|
|
101
|
-
...integrationHandlers,
|
|
102
|
-
...telegramHandlers,
|
|
103
|
-
...channelHandlers,
|
|
104
|
-
...toolHandlers,
|
|
105
|
-
...heartbeatHandlers,
|
|
106
|
-
...voiceHandlers,
|
|
107
|
-
};
|
|
@@ -2,6 +2,7 @@ import * as net from "node:net";
|
|
|
2
2
|
|
|
3
3
|
import { resolveGuardianName } from "../../config/user-reference.js";
|
|
4
4
|
import {
|
|
5
|
+
deleteContact,
|
|
5
6
|
getContact,
|
|
6
7
|
listContacts,
|
|
7
8
|
updateChannelStatus,
|
|
@@ -135,6 +136,39 @@ export function handleContacts(
|
|
|
135
136
|
return;
|
|
136
137
|
}
|
|
137
138
|
|
|
139
|
+
case "delete": {
|
|
140
|
+
if (!msg.contactId) {
|
|
141
|
+
ctx.send(socket, {
|
|
142
|
+
type: "contacts_response",
|
|
143
|
+
success: false,
|
|
144
|
+
error: "contactId is required for delete",
|
|
145
|
+
});
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
const result = deleteContact(msg.contactId);
|
|
149
|
+
if (result === "not_found") {
|
|
150
|
+
ctx.send(socket, {
|
|
151
|
+
type: "contacts_response",
|
|
152
|
+
success: false,
|
|
153
|
+
error: `Contact "${msg.contactId}" not found`,
|
|
154
|
+
});
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
if (result === "is_guardian") {
|
|
158
|
+
ctx.send(socket, {
|
|
159
|
+
type: "contacts_response",
|
|
160
|
+
success: false,
|
|
161
|
+
error: "Cannot delete a guardian contact",
|
|
162
|
+
});
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
ctx.send(socket, {
|
|
166
|
+
type: "contacts_response",
|
|
167
|
+
success: true,
|
|
168
|
+
});
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
|
|
138
172
|
default: {
|
|
139
173
|
ctx.send(socket, {
|
|
140
174
|
type: "contacts_response",
|
|
@@ -16,7 +16,7 @@ import { appHandlers } from "./apps.js";
|
|
|
16
16
|
import { avatarHandlers } from "./avatar.js";
|
|
17
17
|
import { browserHandlers } from "./browser.js";
|
|
18
18
|
import { computerUseHandlers } from "./computer-use.js";
|
|
19
|
-
import { configHandlers } from "./config.js";
|
|
19
|
+
import { configHandlers } from "./config-dispatch.js";
|
|
20
20
|
import { inboxInviteHandlers } from "./config-inbox.js";
|
|
21
21
|
import { contactsHandlers } from "./contacts.js";
|
|
22
22
|
import { diagnosticsHandlers } from "./diagnostics.js";
|
|
@@ -198,11 +198,25 @@ export function handleMessage(
|
|
|
198
198
|
if (msg.type === "auth") return;
|
|
199
199
|
|
|
200
200
|
const handler = handlers[msg.type] as
|
|
201
|
-
| ((
|
|
201
|
+
| ((
|
|
202
|
+
msg: ClientMessage,
|
|
203
|
+
socket: net.Socket,
|
|
204
|
+
ctx: HandlerContext,
|
|
205
|
+
) => void | Promise<void>)
|
|
202
206
|
| undefined;
|
|
203
207
|
if (!handler) {
|
|
204
208
|
log.warn({ type: msg.type }, "Unknown message type, ignoring");
|
|
205
209
|
return;
|
|
206
210
|
}
|
|
207
|
-
|
|
211
|
+
// Handlers may be async — catch rejected promises so they don't become
|
|
212
|
+
// unhandled rejections at the process level.
|
|
213
|
+
const result = handler(msg, socket, ctx);
|
|
214
|
+
if (result && typeof result.catch === "function") {
|
|
215
|
+
result.catch((err: unknown) => {
|
|
216
|
+
log.error(
|
|
217
|
+
{ err, type: msg.type },
|
|
218
|
+
"Unhandled error in async message handler",
|
|
219
|
+
);
|
|
220
|
+
});
|
|
221
|
+
}
|
|
208
222
|
}
|
|
@@ -539,6 +539,11 @@ async function handlePendingConfirmationReply(
|
|
|
539
539
|
causedByRequestId: requestId,
|
|
540
540
|
decisionText: messageText.trim(),
|
|
541
541
|
});
|
|
542
|
+
// Notify agent loop so the outcome is persisted on the tool_use block
|
|
543
|
+
session.onConfirmationOutcome?.(
|
|
544
|
+
routerResult.requestId,
|
|
545
|
+
"resolved_stale",
|
|
546
|
+
);
|
|
542
547
|
}
|
|
543
548
|
|
|
544
549
|
const consumedChannelMeta = {
|
|
@@ -646,6 +651,8 @@ function autoDenyPendingConfirmations(
|
|
|
646
651
|
source: "auto_deny",
|
|
647
652
|
causedByRequestId: requestId,
|
|
648
653
|
});
|
|
654
|
+
// Notify agent loop so the outcome is persisted on the tool_use block
|
|
655
|
+
session.onConfirmationOutcome?.(interaction.requestId, "denied");
|
|
649
656
|
}
|
|
650
657
|
}
|
|
651
658
|
session.denyAllPendingConfirmations();
|
|
@@ -61,6 +61,23 @@ import {
|
|
|
61
61
|
export { handleUserMessage } from "./session-user-message.js";
|
|
62
62
|
import { handleUserMessage } from "./session-user-message.js";
|
|
63
63
|
|
|
64
|
+
/**
|
|
65
|
+
* Extract a valid ChannelId from a binding's sourceChannel, which may carry a
|
|
66
|
+
* `notification:` namespace prefix (e.g. `"notification:telegram"` -> `"telegram"`).
|
|
67
|
+
* Returns the ChannelId if valid, or null otherwise.
|
|
68
|
+
*/
|
|
69
|
+
function parseBindingSourceChannel(
|
|
70
|
+
sourceChannel: string,
|
|
71
|
+
): import("../../channels/types.js").ChannelId | null {
|
|
72
|
+
if (isChannelId(sourceChannel)) return sourceChannel;
|
|
73
|
+
const NOTIFICATION_PREFIX = "notification:";
|
|
74
|
+
if (sourceChannel.startsWith(NOTIFICATION_PREFIX)) {
|
|
75
|
+
const inner = sourceChannel.slice(NOTIFICATION_PREFIX.length);
|
|
76
|
+
if (isChannelId(inner)) return inner;
|
|
77
|
+
}
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
|
|
64
81
|
export function syncCanonicalStatusFromIpcConfirmationDecision(
|
|
65
82
|
requestId: string,
|
|
66
83
|
decision: ConfirmationResponse["decision"],
|
|
@@ -283,10 +300,12 @@ export function handleSessionList(
|
|
|
283
300
|
updatedAt: c.updatedAt,
|
|
284
301
|
threadType: normalizeThreadType(c.threadType),
|
|
285
302
|
source: c.source ?? "user",
|
|
286
|
-
...(binding &&
|
|
303
|
+
...(binding && parseBindingSourceChannel(binding.sourceChannel)
|
|
287
304
|
? {
|
|
288
305
|
channelBinding: {
|
|
289
|
-
sourceChannel:
|
|
306
|
+
sourceChannel: parseBindingSourceChannel(
|
|
307
|
+
binding.sourceChannel,
|
|
308
|
+
)!,
|
|
290
309
|
externalChatId: binding.externalChatId,
|
|
291
310
|
externalUserId: binding.externalUserId,
|
|
292
311
|
displayName: binding.displayName,
|
|
@@ -70,6 +70,14 @@ export interface HistoryToolCall {
|
|
|
70
70
|
isError?: boolean;
|
|
71
71
|
/** Base64-encoded image data from tool contentBlocks (e.g. browser_screenshot). */
|
|
72
72
|
imageData?: string;
|
|
73
|
+
/** Unix ms when the tool started executing. */
|
|
74
|
+
startedAt?: number;
|
|
75
|
+
/** Unix ms when the tool completed. */
|
|
76
|
+
completedAt?: number;
|
|
77
|
+
/** Confirmation decision for this tool call: "approved" | "denied" | "timed_out". */
|
|
78
|
+
confirmationDecision?: string;
|
|
79
|
+
/** Friendly label for the confirmation (e.g. "Edit File", "Run Command"). */
|
|
80
|
+
confirmationLabel?: string;
|
|
73
81
|
}
|
|
74
82
|
|
|
75
83
|
export interface HistorySurface {
|
|
@@ -468,6 +476,15 @@ export function renderHistoryContent(content: unknown): RenderedHistoryContent {
|
|
|
468
476
|
: {};
|
|
469
477
|
const id = typeof block.id === "string" ? block.id : "";
|
|
470
478
|
const entry: HistoryToolCall = { name, input };
|
|
479
|
+
// Extract persisted timing/confirmation metadata
|
|
480
|
+
if (typeof block._startedAt === "number")
|
|
481
|
+
entry.startedAt = block._startedAt;
|
|
482
|
+
if (typeof block._completedAt === "number")
|
|
483
|
+
entry.completedAt = block._completedAt;
|
|
484
|
+
if (typeof block._confirmationDecision === "string")
|
|
485
|
+
entry.confirmationDecision = block._confirmationDecision;
|
|
486
|
+
if (typeof block._confirmationLabel === "string")
|
|
487
|
+
entry.confirmationLabel = block._confirmationLabel;
|
|
471
488
|
toolCalls.push(entry);
|
|
472
489
|
if (id) pendingToolUses.set(id, entry);
|
|
473
490
|
contentOrder.push(`tool:${toolCalls.length - 1}`);
|
|
@@ -238,6 +238,8 @@ export interface ForkSharedAppResponse {
|
|
|
238
238
|
export interface BundleAppResponse {
|
|
239
239
|
type: "bundle_app_response";
|
|
240
240
|
bundlePath: string;
|
|
241
|
+
/** Base64-encoded PNG of the generated app icon, if available. */
|
|
242
|
+
iconImageBase64?: string;
|
|
241
243
|
manifest: {
|
|
242
244
|
format_version: number;
|
|
243
245
|
name: string;
|
|
@@ -217,6 +217,14 @@ export interface WatchCompleteRequest {
|
|
|
217
217
|
watchId: string;
|
|
218
218
|
}
|
|
219
219
|
|
|
220
|
+
/** Server → Client: bootstrap failure during learn-mode recording setup. */
|
|
221
|
+
export interface RideShotgunError {
|
|
222
|
+
type: "ride_shotgun_error";
|
|
223
|
+
watchId: string;
|
|
224
|
+
sessionId: string;
|
|
225
|
+
message: string;
|
|
226
|
+
}
|
|
227
|
+
|
|
220
228
|
// --- Domain-level union aliases (consumed by the barrel file) ---
|
|
221
229
|
|
|
222
230
|
export type _ComputerUseClientMessages =
|
|
@@ -236,6 +244,7 @@ export type _ComputerUseServerMessages =
|
|
|
236
244
|
| TaskRouted
|
|
237
245
|
| RideShotgunProgress
|
|
238
246
|
| RideShotgunResult
|
|
247
|
+
| RideShotgunError
|
|
239
248
|
| WatchStarted
|
|
240
249
|
| WatchCompleteRequest
|
|
241
250
|
| RecordingStart
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
// Contact management: list, get,
|
|
1
|
+
// Contact management: list, get, update channel status, and delete.
|
|
2
2
|
|
|
3
3
|
// === Client → Server ===
|
|
4
4
|
|
|
5
5
|
export interface ContactsRequest {
|
|
6
6
|
type: "contacts";
|
|
7
|
-
action: "list" | "get" | "update_channel";
|
|
8
|
-
/** Contact ID (get
|
|
7
|
+
action: "list" | "get" | "update_channel" | "delete";
|
|
8
|
+
/** Contact ID (get and delete). */
|
|
9
9
|
contactId?: string;
|
|
10
10
|
/** Channel ID (update_channel only). */
|
|
11
11
|
channelId?: string;
|
|
@@ -25,6 +25,8 @@ export interface ContactsInviteRequest {
|
|
|
25
25
|
status?: string;
|
|
26
26
|
/** Invitee's first name (voice invite create only). */
|
|
27
27
|
friendName?: string;
|
|
28
|
+
/** Contact display name for personalizing invite instructions (create only). */
|
|
29
|
+
contactName?: string;
|
|
28
30
|
/** Guardian's first name (voice invite create only). */
|
|
29
31
|
guardianName?: string;
|
|
30
32
|
}
|
|
@@ -80,6 +80,8 @@ export interface ToolUseStart {
|
|
|
80
80
|
toolName: string;
|
|
81
81
|
input: Record<string, unknown>;
|
|
82
82
|
sessionId?: string;
|
|
83
|
+
/** The tool_use block ID for client-side correlation. */
|
|
84
|
+
toolUseId?: string;
|
|
83
85
|
}
|
|
84
86
|
|
|
85
87
|
export interface ToolOutputChunk {
|
|
@@ -240,6 +242,8 @@ export interface ConfirmationStateChanged {
|
|
|
240
242
|
causedByRequestId?: string;
|
|
241
243
|
/** Normalized user text for analytics/debug (e.g. "approve", "deny"). */
|
|
242
244
|
decisionText?: string;
|
|
245
|
+
/** The tool_use block ID this confirmation applies to, for disambiguating parallel tool calls. */
|
|
246
|
+
toolUseId?: string;
|
|
243
247
|
}
|
|
244
248
|
|
|
245
249
|
/**
|
|
@@ -288,6 +288,14 @@ export interface HistoryResponseToolCall {
|
|
|
288
288
|
isError?: boolean;
|
|
289
289
|
/** Base64-encoded image data from tool contentBlocks (e.g. browser_screenshot). */
|
|
290
290
|
imageData?: string;
|
|
291
|
+
/** Unix ms when the tool started executing. */
|
|
292
|
+
startedAt?: number;
|
|
293
|
+
/** Unix ms when the tool completed. */
|
|
294
|
+
completedAt?: number;
|
|
295
|
+
/** Confirmation decision for this tool call: "approved" | "denied" | "timed_out". */
|
|
296
|
+
confirmationDecision?: string;
|
|
297
|
+
/** Friendly label for the confirmation (e.g. "Edit File", "Run Command"). */
|
|
298
|
+
confirmationLabel?: string;
|
|
291
299
|
}
|
|
292
300
|
|
|
293
301
|
export interface HistoryResponseSurface {
|
package/src/daemon/lifecycle.ts
CHANGED
|
@@ -47,7 +47,6 @@ import {
|
|
|
47
47
|
import { ensureVellumGuardianBinding } from "../runtime/guardian-vellum-migration.js";
|
|
48
48
|
import { RuntimeHttpServer } from "../runtime/http-server.js";
|
|
49
49
|
import { startScheduler } from "../schedule/scheduler.js";
|
|
50
|
-
import { migrateKeychainToEncrypted } from "../security/keychain-to-encrypted-migration.js";
|
|
51
50
|
import { getLogger, initLogger } from "../util/logger.js";
|
|
52
51
|
import {
|
|
53
52
|
ensureDataDir,
|
|
@@ -143,10 +142,6 @@ export async function runDaemon(): Promise<void> {
|
|
|
143
142
|
migrateToWorkspaceLayout();
|
|
144
143
|
ensureDataDir();
|
|
145
144
|
|
|
146
|
-
// Copy any existing macOS keychain secrets into the encrypted file store
|
|
147
|
-
// before config loads, so the new encrypted-store-first read path sees them.
|
|
148
|
-
migrateKeychainToEncrypted();
|
|
149
|
-
|
|
150
145
|
// Load (or generate + persist) the auth signing key so tokens survive
|
|
151
146
|
// daemon restarts. Must happen after ensureDataDir() creates the
|
|
152
147
|
// protected directory.
|
|
@@ -2,6 +2,11 @@ import { randomUUID } from "node:crypto";
|
|
|
2
2
|
import type * as net from "node:net";
|
|
3
3
|
|
|
4
4
|
import { autoNavigate } from "../tools/browser/auto-navigate.js";
|
|
5
|
+
import {
|
|
6
|
+
type CdpSession,
|
|
7
|
+
ensureChromeWithCdp,
|
|
8
|
+
minimizeChromeWindow,
|
|
9
|
+
} from "../tools/browser/chrome-cdp.js";
|
|
5
10
|
import { NetworkRecorder } from "../tools/browser/network-recorder.js";
|
|
6
11
|
import type { SessionRecording } from "../tools/browser/network-recording-types.js";
|
|
7
12
|
import { saveRecording } from "../tools/browser/recording-store.js";
|
|
@@ -24,6 +29,9 @@ const log = getLogger("ride-shotgun-handler");
|
|
|
24
29
|
/** Active network recorders keyed by watchId. */
|
|
25
30
|
const activeRecorders = new Map<string, NetworkRecorder>();
|
|
26
31
|
|
|
32
|
+
/** Active CDP sessions keyed by watchId — tracks browser ownership for cleanup. */
|
|
33
|
+
const activeCdpSessions = new Map<string, CdpSession>();
|
|
34
|
+
|
|
27
35
|
/** Active progress interval timers keyed by watchId, cleared on session completion. */
|
|
28
36
|
const activeProgressIntervals = new Map<string, NodeJS.Timeout>();
|
|
29
37
|
|
|
@@ -71,20 +79,48 @@ async function completeSession(session: WatchSession): Promise<void> {
|
|
|
71
79
|
|
|
72
80
|
// In learn mode, stop recording and save — skip the LLM summary (not needed)
|
|
73
81
|
if (session.isLearnMode && session.recordingId) {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
session.
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
+
const hasRecorder = activeRecorders.has(watchId);
|
|
83
|
+
|
|
84
|
+
if (hasRecorder) {
|
|
85
|
+
session.savedRecordingPath = await finalizeLearnRecording(
|
|
86
|
+
watchId,
|
|
87
|
+
session,
|
|
88
|
+
session.recordingId,
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Clean up the CDP session — minimize if we launched Chrome, leave it alone otherwise
|
|
93
|
+
const cdpSession = activeCdpSessions.get(watchId);
|
|
94
|
+
if (cdpSession) {
|
|
95
|
+
activeCdpSessions.delete(watchId);
|
|
96
|
+
if (cdpSession.launchedByUs) {
|
|
97
|
+
try {
|
|
98
|
+
await minimizeChromeWindow(cdpSession.baseUrl);
|
|
99
|
+
log.info({ watchId }, "Minimized assistant-launched Chrome window");
|
|
100
|
+
} catch (err) {
|
|
101
|
+
log.debug({ err, watchId }, "Failed to minimize Chrome window");
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Use bootstrapFailureReason as the primary discriminator — hasRecorder
|
|
107
|
+
// alone can't distinguish "browser never launched" from "recorder failed
|
|
108
|
+
// after retries" since both leave activeRecorders empty.
|
|
109
|
+
const summary = session.bootstrapFailureReason
|
|
110
|
+
? `Learn session failed — ${session.bootstrapFailureReason}`
|
|
111
|
+
: session.savedRecordingPath
|
|
82
112
|
? "Learn session completed — recording saved."
|
|
83
|
-
: "Learn session completed — recording failed to save."
|
|
84
|
-
|
|
113
|
+
: "Learn session completed — recording failed to save.";
|
|
114
|
+
|
|
115
|
+
lastSummaryBySession.set(sessionId, summary);
|
|
85
116
|
session.status = "completed";
|
|
86
117
|
log.info(
|
|
87
|
-
{
|
|
118
|
+
{
|
|
119
|
+
watchId,
|
|
120
|
+
sessionId,
|
|
121
|
+
hasRecorder,
|
|
122
|
+
bootstrapFailureReason: session.bootstrapFailureReason,
|
|
123
|
+
},
|
|
88
124
|
"Learn session complete — firing completion notifier",
|
|
89
125
|
);
|
|
90
126
|
fireWatchCompletionNotifier(sessionId, session);
|
|
@@ -142,10 +178,67 @@ export async function handleRideShotgunStart(
|
|
|
142
178
|
"Session created and stored in watchSessions map",
|
|
143
179
|
);
|
|
144
180
|
|
|
145
|
-
// In learn mode,
|
|
146
|
-
// Retry a few times since Chrome may still be starting up after the Swift client restarts it.
|
|
181
|
+
// In learn mode, ensure Chrome is available with CDP, then connect for network recording.
|
|
147
182
|
if (isLearnMode) {
|
|
148
183
|
const startRecording = async () => {
|
|
184
|
+
// Ensure Chrome is running with CDP — launches it if needed
|
|
185
|
+
let cdpSession: CdpSession;
|
|
186
|
+
try {
|
|
187
|
+
cdpSession = await ensureChromeWithCdp({
|
|
188
|
+
startUrl: targetDomain ? `https://${targetDomain}` : undefined,
|
|
189
|
+
});
|
|
190
|
+
// If session completed while we were awaiting Chrome, skip storing to avoid a stale map entry
|
|
191
|
+
if (session.status !== "active") {
|
|
192
|
+
log.info(
|
|
193
|
+
{ watchId, status: session.status },
|
|
194
|
+
"Session no longer active after CDP launch — skipping recording",
|
|
195
|
+
);
|
|
196
|
+
// If we launched Chrome, minimize it since completeSession already ran and won't find it
|
|
197
|
+
if (cdpSession.launchedByUs) {
|
|
198
|
+
try {
|
|
199
|
+
await minimizeChromeWindow(cdpSession.baseUrl);
|
|
200
|
+
log.info(
|
|
201
|
+
{ watchId },
|
|
202
|
+
"Minimized assistant-launched Chrome window (post-session)",
|
|
203
|
+
);
|
|
204
|
+
} catch (err) {
|
|
205
|
+
log.debug(
|
|
206
|
+
{ err, watchId },
|
|
207
|
+
"Failed to minimize Chrome window (post-session)",
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
activeCdpSessions.set(watchId, cdpSession);
|
|
214
|
+
log.info(
|
|
215
|
+
{
|
|
216
|
+
watchId,
|
|
217
|
+
launchedByUs: cdpSession.launchedByUs,
|
|
218
|
+
baseUrl: cdpSession.baseUrl,
|
|
219
|
+
},
|
|
220
|
+
"CDP session established",
|
|
221
|
+
);
|
|
222
|
+
} catch (err) {
|
|
223
|
+
log.warn(
|
|
224
|
+
{ err, watchId },
|
|
225
|
+
"Failed to ensure Chrome with CDP — cannot start recording",
|
|
226
|
+
);
|
|
227
|
+
ctx.send(socket, {
|
|
228
|
+
type: "ride_shotgun_error",
|
|
229
|
+
watchId,
|
|
230
|
+
sessionId,
|
|
231
|
+
message:
|
|
232
|
+
"Failed to start browser — Chrome CDP could not be launched.",
|
|
233
|
+
});
|
|
234
|
+
// Fail-fast: complete the session immediately instead of waiting for timeout
|
|
235
|
+
session.bootstrapFailureReason = "browser could not be started.";
|
|
236
|
+
await completeSession(session);
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
const cdpBaseUrl = cdpSession.baseUrl;
|
|
241
|
+
|
|
149
242
|
for (let attempt = 0; attempt < 10; attempt++) {
|
|
150
243
|
// Check if session is still active before each attempt
|
|
151
244
|
if (session.status !== "active") {
|
|
@@ -156,7 +249,7 @@ export async function handleRideShotgunStart(
|
|
|
156
249
|
return;
|
|
157
250
|
}
|
|
158
251
|
try {
|
|
159
|
-
const recorder = new NetworkRecorder(targetDomain);
|
|
252
|
+
const recorder = new NetworkRecorder(targetDomain, cdpBaseUrl);
|
|
160
253
|
recorder.loginSignals = getLoginSignals(targetDomain);
|
|
161
254
|
await recorder.startDirect();
|
|
162
255
|
// If session completed while we were connecting, stop immediately to avoid leak
|
|
@@ -248,7 +341,7 @@ export async function handleRideShotgunStart(
|
|
|
248
341
|
clearInterval(checkInterval);
|
|
249
342
|
}
|
|
250
343
|
}, 1000);
|
|
251
|
-
navigateXPages(abortSignal)
|
|
344
|
+
navigateXPages({ abortSignal, cdpBaseUrl })
|
|
252
345
|
.then((completed) => {
|
|
253
346
|
clearInterval(checkInterval);
|
|
254
347
|
log.info(
|
|
@@ -275,16 +368,20 @@ export async function handleRideShotgunStart(
|
|
|
275
368
|
clearInterval(checkInterval);
|
|
276
369
|
}
|
|
277
370
|
}, 1000);
|
|
278
|
-
autoNavigate(navDomain,
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
371
|
+
autoNavigate(navDomain, {
|
|
372
|
+
abortSignal,
|
|
373
|
+
onProgress: (progress) => {
|
|
374
|
+
// Send progress to connected client
|
|
375
|
+
if (progress.type === "visiting" && progress.url) {
|
|
376
|
+
const shortUrl = progress.url.replace(/^https?:\/\//, "");
|
|
377
|
+
ctx.send(socket, {
|
|
378
|
+
type: "ride_shotgun_progress",
|
|
379
|
+
watchId,
|
|
380
|
+
message: `[${progress.pageNumber || "?"}] ${shortUrl}`,
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
},
|
|
384
|
+
cdpBaseUrl,
|
|
288
385
|
})
|
|
289
386
|
.then((visited) => {
|
|
290
387
|
clearInterval(checkInterval);
|
|
@@ -326,6 +423,15 @@ export async function handleRideShotgunStart(
|
|
|
326
423
|
{ err, watchId },
|
|
327
424
|
"Failed to start network recording after 10 attempts",
|
|
328
425
|
);
|
|
426
|
+
ctx.send(socket, {
|
|
427
|
+
type: "ride_shotgun_error",
|
|
428
|
+
watchId,
|
|
429
|
+
sessionId,
|
|
430
|
+
message: "Failed to start network recording after 10 attempts.",
|
|
431
|
+
});
|
|
432
|
+
session.bootstrapFailureReason =
|
|
433
|
+
"network recording could not be started after 10 attempts.";
|
|
434
|
+
await completeSession(session);
|
|
329
435
|
}
|
|
330
436
|
}
|
|
331
437
|
}
|
|
@@ -336,6 +442,14 @@ export async function handleRideShotgunStart(
|
|
|
336
442
|
|
|
337
443
|
// Set timeout for duration expiry
|
|
338
444
|
session.timeoutHandle = setTimeout(() => {
|
|
445
|
+
if (
|
|
446
|
+
session.isLearnMode &&
|
|
447
|
+
!activeRecorders.has(watchId) &&
|
|
448
|
+
!session.bootstrapFailureReason
|
|
449
|
+
) {
|
|
450
|
+
session.bootstrapFailureReason =
|
|
451
|
+
"session timed out before recording could start.";
|
|
452
|
+
}
|
|
339
453
|
completeSession(session);
|
|
340
454
|
}, durationSeconds * 1000);
|
|
341
455
|
|