@vellumai/assistant 0.4.42 → 0.4.44
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/.env.example +1 -6
- package/.prettierignore +3 -0
- package/ARCHITECTURE.md +140 -403
- package/Dockerfile +0 -1
- package/README.md +81 -92
- package/bun.lock +8 -2
- package/docs/architecture/integrations.md +81 -104
- package/docs/architecture/memory.md +1 -1
- package/docs/architecture/scheduling.md +63 -63
- package/docs/architecture/security.md +3 -3
- package/docs/runbook-trusted-contacts.md +11 -12
- package/docs/trusted-contact-access.md +39 -39
- package/package.json +5 -8
- package/src/__tests__/access-request-decision.test.ts +4 -4
- package/src/__tests__/active-skill-tools.test.ts +49 -34
- package/src/__tests__/actor-token-service.test.ts +55 -85
- package/src/__tests__/amazon-cdp-integration.test.ts +14 -26
- package/src/__tests__/app-bundler.test.ts +14 -368
- package/src/__tests__/app-compiler.test.ts +0 -1
- package/src/__tests__/app-executors.test.ts +10 -1
- package/src/__tests__/approval-hardcoded-copy-guard.test.ts +1 -1
- package/src/__tests__/approval-primitive.test.ts +2 -4
- package/src/__tests__/approval-routes-http.test.ts +1 -3
- package/src/__tests__/asset-materialize-tool.test.ts +1 -4
- package/src/__tests__/asset-search-tool.test.ts +1 -4
- package/src/__tests__/assistant-attachments.test.ts +23 -0
- package/src/__tests__/assistant-feature-flags-integration.test.ts +4 -8
- package/src/__tests__/assistant-id-boundary-guard.test.ts +5 -5
- package/src/__tests__/attachments-store.test.ts +1 -4
- package/src/__tests__/avatar-e2e.test.ts +43 -23
- package/src/__tests__/browser-fill-credential.test.ts +1 -1
- package/src/__tests__/bundled-asset.test.ts +1 -1
- package/src/__tests__/bundled-skill-retrieval-guard.test.ts +2 -9
- package/src/__tests__/call-controller.test.ts +4 -8
- package/src/__tests__/call-conversation-messages.test.ts +1 -1
- package/src/__tests__/call-domain.test.ts +250 -8
- package/src/__tests__/call-pointer-message-composer.test.ts +14 -14
- package/src/__tests__/call-pointer-messages.test.ts +7 -11
- package/src/__tests__/call-recovery.test.ts +47 -0
- package/src/__tests__/call-routes-http.test.ts +13 -0
- package/src/__tests__/call-start-guardian-guard.test.ts +1 -1
- package/src/__tests__/callback-handoff-copy.test.ts +5 -5
- package/src/__tests__/canonical-guardian-store.test.ts +3 -3
- package/src/__tests__/channel-approval-routes.test.ts +101 -134
- package/src/__tests__/channel-approval.test.ts +0 -201
- package/src/__tests__/channel-approvals.test.ts +2 -2
- package/src/__tests__/channel-delivery-store.test.ts +16 -24
- package/src/__tests__/channel-guardian.test.ts +641 -740
- package/src/__tests__/channel-invite-transport.test.ts +1 -2
- package/src/__tests__/channel-policy.test.ts +9 -12
- package/src/__tests__/channel-readiness-service.test.ts +156 -45
- package/src/__tests__/channel-reply-delivery.test.ts +3 -3
- package/src/__tests__/channel-retry-sweep.test.ts +7 -7
- package/src/__tests__/checker.test.ts +41 -35
- package/src/__tests__/chrome-cdp.test.ts +57 -17
- package/src/__tests__/cli-help-reference-sync.test.ts +26 -0
- package/src/__tests__/compaction.benchmark.test.ts +25 -5
- package/src/__tests__/computer-use-session-lifecycle.test.ts +1 -1
- package/src/__tests__/computer-use-session-working-dir.test.ts +2 -6
- package/src/__tests__/computer-use-skill-lifecycle-cleanup.test.ts +1 -1
- package/src/__tests__/config-loader-backfill.test.ts +310 -0
- package/src/__tests__/config-watcher.test.ts +1 -5
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +3 -5
- package/src/__tests__/connection-policy.test.ts +3 -62
- package/src/__tests__/contacts-tools.test.ts +0 -2
- package/src/__tests__/context-memory-e2e.test.ts +11 -7
- package/src/__tests__/context-overflow-policy.test.ts +2 -2
- package/src/__tests__/context-window-manager.test.ts +220 -61
- package/src/__tests__/conversation-attention-store.test.ts +178 -2
- package/src/__tests__/conversation-attention-telegram.test.ts +8 -11
- package/src/__tests__/conversation-pairing.test.ts +14 -14
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +7 -7
- package/src/__tests__/conversation-store.test.ts +2 -2
- package/src/__tests__/conversation-unread-route.test.ts +155 -0
- package/src/__tests__/credential-metadata-store.test.ts +0 -2
- package/src/__tests__/credential-security-invariants.test.ts +10 -16
- package/src/__tests__/credentials-cli.test.ts +49 -5
- package/src/__tests__/daemon-assistant-events.test.ts +4 -22
- package/src/__tests__/db-migration-rollback.test.ts +2 -2
- package/src/__tests__/deterministic-verification-control-plane.test.ts +19 -19
- package/src/__tests__/dictation-mode-detection.test.ts +1 -1
- package/src/__tests__/dynamic-page-surface.test.ts +2 -2
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +2 -6
- package/src/__tests__/email-cli.test.ts +12 -12
- package/src/__tests__/email-service-config-fallback.test.ts +1 -1
- package/src/__tests__/emit-signal-routing-intent.test.ts +3 -18
- package/src/__tests__/error-handler-friendly-messages.test.ts +46 -0
- package/src/__tests__/event-bus.test.ts +0 -1
- package/src/__tests__/followup-tools.test.ts +0 -2
- package/src/__tests__/gateway-client-managed-outbound.test.ts +6 -6
- package/src/__tests__/gateway-only-enforcement.test.ts +13 -77
- package/src/__tests__/gateway-only-guard.test.ts +5 -0
- package/src/__tests__/guardian-action-conversation-turn.test.ts +3 -3
- package/src/__tests__/guardian-action-followup-executor.test.ts +29 -94
- package/src/__tests__/guardian-action-followup-store.test.ts +2 -12
- package/src/__tests__/guardian-action-grant-mint-consume.test.ts +48 -194
- package/src/__tests__/guardian-action-late-reply.test.ts +12 -12
- package/src/__tests__/guardian-action-store.test.ts +2 -2
- package/src/__tests__/guardian-action-sweep.test.ts +5 -5
- package/src/__tests__/guardian-decision-primitive-canonical.test.ts +1 -3
- package/src/__tests__/guardian-dispatch.test.ts +5 -46
- package/src/__tests__/guardian-grant-minting.test.ts +5 -44
- package/src/__tests__/guardian-outbound-http.test.ts +95 -114
- package/src/__tests__/guardian-question-mode.test.ts +1 -4
- package/src/__tests__/guardian-routing-invariants.test.ts +5 -13
- package/src/__tests__/guardian-routing-state.test.ts +3 -3
- package/src/__tests__/guardian-verification-voice-binding.test.ts +64 -7
- package/src/__tests__/guardian-verify-setup-skill-regression.test.ts +2 -2
- package/src/__tests__/handle-user-message-secret-resume.test.ts +3 -5
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +16 -34
- package/src/__tests__/headless-browser-interactions.test.ts +1 -1
- package/src/__tests__/headless-browser-navigate.test.ts +1 -1
- package/src/__tests__/headless-browser-read-tools.test.ts +1 -1
- package/src/__tests__/headless-browser-snapshot.test.ts +1 -1
- package/src/__tests__/heartbeat-service.test.ts +1 -1
- package/src/__tests__/host-shell-tool.test.ts +3 -12
- package/src/__tests__/inbound-invite-redemption.test.ts +2 -2
- package/src/__tests__/ingress-url-consistency.test.ts +0 -64
- package/src/__tests__/integration-status.test.ts +8 -8
- package/src/__tests__/intent-routing.test.ts +9 -13
- package/src/__tests__/invite-redemption-service.test.ts +4 -4
- package/src/__tests__/invite-routes-http.test.ts +10 -10
- package/src/__tests__/llm-usage-store.test.ts +45 -9
- package/src/__tests__/local-gateway-health.test.ts +209 -0
- package/src/__tests__/managed-avatar-client.test.ts +23 -12
- package/src/__tests__/managed-skill-lifecycle.test.ts +1 -2
- package/src/__tests__/managed-store.test.ts +29 -12
- package/src/__tests__/managed-twitter-guardrails.test.ts +357 -0
- package/src/__tests__/mcp-cli.test.ts +1 -1
- package/src/__tests__/mcp-health-check.test.ts +1 -1
- package/src/__tests__/media-generate-image.test.ts +1 -1
- package/src/__tests__/media-reuse-story.e2e.test.ts +1 -4
- package/src/__tests__/memory-context-benchmark.benchmark.test.ts +9 -6
- package/src/__tests__/memory-regressions.test.ts +1 -166
- package/src/__tests__/messaging-send-tool.test.ts +8 -4
- package/src/__tests__/migration-export-http.test.ts +2 -2
- package/src/__tests__/migration-transport.test.ts +44 -0
- package/src/__tests__/non-member-access-request.test.ts +49 -36
- package/src/__tests__/notification-broadcaster.test.ts +15 -15
- package/src/__tests__/notification-decision-fallback.test.ts +2 -2
- package/src/__tests__/notification-decision-strategy.test.ts +4 -4
- package/src/__tests__/notification-deep-link.test.ts +3 -3
- package/src/__tests__/notification-guardian-path.test.ts +6 -44
- package/src/__tests__/notification-routing-intent.test.ts +11 -7
- package/src/__tests__/oauth-cli.test.ts +1 -1
- package/src/__tests__/onboarding-starter-tasks.test.ts +2 -6
- package/src/__tests__/onboarding-template-contract.test.ts +2 -12
- package/src/__tests__/platform.test.ts +168 -5
- package/src/__tests__/playbook-execution.test.ts +0 -2
- package/src/__tests__/playbook-tools.test.ts +0 -2
- package/src/__tests__/pricing.test.ts +125 -0
- package/src/__tests__/provider-error-scenarios.test.ts +9 -3
- package/src/__tests__/provider-fail-open-selection.test.ts +12 -2
- package/src/__tests__/recording-handler.test.ts +46 -80
- package/src/__tests__/recording-state-machine.test.ts +112 -183
- package/src/__tests__/registry.test.ts +1 -1
- package/src/__tests__/relay-server.test.ts +69 -71
- package/src/__tests__/reminder-store.test.ts +3 -3
- package/src/__tests__/request-file-tool.test.ts +2 -2
- package/src/__tests__/ride-shotgun-handler.test.ts +2 -33
- package/src/__tests__/runtime-attachment-metadata.test.ts +3 -3
- package/src/__tests__/runtime-events-sse-parity.test.ts +1 -1
- package/src/__tests__/scaffold-managed-skill-tool.test.ts +4 -4
- package/src/__tests__/schedule-store.test.ts +13 -4
- package/src/__tests__/schedule-tools.test.ts +0 -2
- package/src/__tests__/scheduler-recurrence.test.ts +3 -4
- package/src/__tests__/scoped-approval-grants.test.ts +3 -5
- package/src/__tests__/scoped-grant-security-matrix.test.ts +6 -8
- package/src/__tests__/secret-prompt-log-hygiene.test.ts +1 -1
- package/src/__tests__/secret-response-routing.test.ts +1 -1
- package/src/__tests__/send-endpoint-busy.test.ts +1 -4
- package/src/__tests__/sequence-store.test.ts +0 -2
- package/src/__tests__/server-history-render.test.ts +2 -199
- package/src/__tests__/session-abort-tool-results.test.ts +9 -3
- package/src/__tests__/session-agent-loop.test.ts +107 -3
- package/src/__tests__/session-confirmation-signals.test.ts +17 -49
- package/src/__tests__/session-conflict-gate.test.ts +9 -3
- package/src/__tests__/session-init.benchmark.test.ts +22 -13
- package/src/__tests__/session-load-history-repair.test.ts +6 -3
- package/src/__tests__/session-pre-run-repair.test.ts +9 -3
- package/src/__tests__/session-profile-injection.test.ts +9 -3
- package/src/__tests__/session-provider-retry-repair.test.ts +10 -4
- package/src/__tests__/session-queue.test.ts +10 -4
- package/src/__tests__/session-runtime-assembly.test.ts +28 -18
- package/src/__tests__/session-skill-tools.test.ts +2 -3
- package/src/__tests__/session-slash-known.test.ts +11 -4
- package/src/__tests__/session-slash-queue.test.ts +11 -4
- package/src/__tests__/session-slash-unknown.test.ts +12 -4
- package/src/__tests__/session-surfaces-deselection.test.ts +2 -2
- package/src/__tests__/session-surfaces-task-progress.test.ts +3 -3
- package/src/__tests__/session-tool-setup-app-refresh.test.ts +1 -1
- package/src/__tests__/session-tool-setup-memory-scope.test.ts +1 -1
- package/src/__tests__/session-tool-setup-side-effect-flag.test.ts +1 -1
- package/src/__tests__/session-usage.test.ts +180 -0
- package/src/__tests__/session-workspace-cache-state.test.ts +8 -2
- package/src/__tests__/session-workspace-injection.test.ts +8 -2
- package/src/__tests__/session-workspace-tool-tracking.test.ts +8 -2
- package/src/__tests__/skill-feature-flags-integration.test.ts +5 -11
- package/src/__tests__/skill-feature-flags.test.ts +1 -0
- package/src/__tests__/skill-include-graph.test.ts +1 -0
- package/src/__tests__/skill-load-feature-flag.test.ts +3 -9
- package/src/__tests__/skill-load-tool.test.ts +90 -12
- package/src/__tests__/skill-projection-feature-flag.test.ts +14 -15
- package/src/__tests__/skills-uninstall.test.ts +131 -0
- package/src/__tests__/skills.test.ts +32 -16
- package/src/__tests__/slack-block-formatting.test.ts +1 -1
- package/src/__tests__/slack-channel-config.test.ts +71 -12
- package/src/__tests__/slack-inbound-verification.test.ts +7 -7
- package/src/__tests__/slack-share-routes.test.ts +1 -1
- package/src/__tests__/slack-skill.test.ts +2 -2
- package/src/__tests__/slash-commands-catalog.test.ts +1 -0
- package/src/__tests__/slash-commands-resolver.test.ts +1 -0
- package/src/__tests__/starter-task-flow.test.ts +10 -20
- package/src/__tests__/subagent-manager-notify.test.ts +1 -1
- package/src/__tests__/subagent-tools.test.ts +2 -2
- package/src/__tests__/system-prompt.test.ts +7 -12
- package/src/__tests__/task-compiler.test.ts +0 -2
- package/src/__tests__/task-management-tools.test.ts +0 -2
- package/src/__tests__/task-runner.test.ts +0 -2
- package/src/__tests__/task-scheduler.test.ts +2 -2
- package/src/__tests__/telegram-bot-username-resolution.test.ts +46 -44
- package/src/__tests__/terminal-tools.test.ts +1 -11
- package/src/__tests__/thread-seed-composer.test.ts +3 -1
- package/src/__tests__/tool-approval-handler.test.ts +5 -7
- package/src/__tests__/tool-executor.test.ts +2 -2
- package/src/__tests__/tool-grant-request-escalation.test.ts +3 -5
- package/src/__tests__/tool-notification-listener.test.ts +1 -1
- package/src/__tests__/tool-profiling-listener.test.ts +1 -1
- package/src/__tests__/tool-trace-listener.test.ts +1 -2
- package/src/__tests__/trace-emitter.test.ts +1 -1
- package/src/__tests__/trust-context-guards.test.ts +1 -1
- package/src/__tests__/trust-store.test.ts +48 -399
- package/src/__tests__/trusted-contact-approval-notifier.test.ts +6 -8
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +5 -7
- package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +6 -6
- package/src/__tests__/trusted-contact-multichannel.test.ts +54 -47
- package/src/__tests__/trusted-contact-verification.test.ts +12 -12
- package/src/__tests__/twilio-config.test.ts +11 -2
- package/src/__tests__/twilio-provider.test.ts +6 -4
- package/src/__tests__/twilio-routes.test.ts +408 -86
- package/src/__tests__/twitter-platform-proxy-client.test.ts +475 -0
- package/src/__tests__/update-bulletin-format.test.ts +1 -1
- package/src/__tests__/update-bulletin-state.test.ts +1 -1
- package/src/__tests__/update-bulletin.test.ts +4 -8
- package/src/__tests__/update-template-contract.test.ts +1 -1
- package/src/__tests__/usage-cache-backfill-migration.test.ts +406 -0
- package/src/__tests__/usage-routes.test.ts +23 -5
- package/src/__tests__/user-reference.test.ts +1 -1
- package/src/__tests__/{guardian-control-plane-policy.test.ts → verification-control-plane-policy.test.ts} +142 -170
- package/src/__tests__/{guardian-verification-intent-routing.test.ts → verification-session-intent-routing.test.ts} +16 -16
- package/src/__tests__/view-image-tool.test.ts +0 -2
- package/src/__tests__/voice-ingress-preflight.test.ts +36 -0
- package/src/__tests__/voice-invite-redemption.test.ts +18 -18
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +7 -7
- package/src/__tests__/voice-session-bridge.test.ts +14 -16
- package/src/__tests__/workspace-policy.test.ts +1 -1
- package/src/approvals/AGENTS.md +4 -4
- package/src/approvals/approval-primitive.ts +2 -2
- package/src/approvals/guardian-decision-primitive.ts +1 -1
- package/src/approvals/guardian-request-resolvers.ts +3 -4
- package/src/bundler/app-bundler.ts +29 -217
- package/src/calls/active-call-lease.ts +207 -0
- package/src/calls/call-constants.ts +0 -7
- package/src/calls/call-controller.ts +1 -1
- package/src/calls/call-conversation-messages.ts +6 -6
- package/src/calls/call-domain.ts +73 -38
- package/src/calls/call-pointer-message-composer.ts +6 -6
- package/src/calls/call-pointer-messages.ts +14 -13
- package/src/calls/call-recovery.ts +2 -0
- package/src/calls/call-store.ts +21 -28
- package/src/calls/guardian-action-sweep.ts +6 -8
- package/src/calls/guardian-dispatch.ts +2 -6
- package/src/calls/relay-access-wait.ts +4 -4
- package/src/calls/relay-server.ts +69 -80
- package/src/calls/relay-setup-router.ts +16 -21
- package/src/calls/relay-verification.ts +27 -28
- package/src/calls/twilio-config.ts +28 -3
- package/src/calls/twilio-provider.ts +5 -5
- package/src/calls/twilio-rest.ts +26 -27
- package/src/calls/twilio-routes.ts +67 -54
- package/src/calls/types.ts +8 -8
- package/src/calls/voice-ingress-preflight.ts +110 -0
- package/src/calls/voice-session-bridge.ts +7 -7
- package/src/channels/config.ts +1 -10
- package/src/{config/channel-permission-profiles.ts → channels/permission-profiles.ts} +1 -1
- package/src/channels/types.ts +2 -13
- package/src/cli/__tests__/notifications.test.ts +1 -1
- package/src/{amazon → cli/commands/amazon}/client.ts +99 -42
- package/src/cli/{amazon.ts → commands/amazon/index.ts} +14 -54
- package/src/{amazon → cli/commands/amazon}/request-extractor.ts +39 -3
- package/src/cli/commands/amazon/session.ts +108 -0
- package/src/cli/{audit.ts → commands/audit.ts} +2 -4
- package/src/cli/{autonomy.ts → commands/autonomy.ts} +1 -3
- package/src/cli/commands/browser-relay.ts +520 -0
- package/src/cli/commands/channel-verification-sessions.ts +442 -0
- package/src/cli/{completions.ts → commands/completions.ts} +1 -3
- package/src/cli/{config.ts → commands/config.ts} +3 -5
- package/src/cli/{contacts.ts → commands/contacts.ts} +15 -17
- package/src/cli/{credentials.ts → commands/credentials.ts} +9 -10
- package/src/cli/{default-action.ts → commands/default-action.ts} +3 -3
- package/src/cli/{dev.ts → commands/dev.ts} +4 -6
- package/src/cli/{doctor.ts → commands/doctor.ts} +36 -60
- package/src/cli/{email.ts → commands/email.ts} +2 -2
- package/src/cli/{keys.ts → commands/keys.ts} +6 -6
- package/src/cli/{map.ts → commands/map.ts} +85 -93
- package/src/cli/{mcp.ts → commands/mcp.ts} +5 -7
- package/src/cli/{memory.ts → commands/memory.ts} +6 -7
- package/src/cli/{notifications.ts → commands/notifications.ts} +8 -10
- package/src/cli/{oauth.ts → commands/oauth.ts} +2 -2
- package/src/cli/commands/platform.ts +176 -0
- package/src/cli/{sequence.ts → commands/sequence.ts} +3 -3
- package/src/cli/{sessions.ts → commands/sessions.ts} +32 -52
- package/src/cli/commands/skills.ts +498 -0
- package/src/cli/{trust.ts → commands/trust.ts} +2 -4
- package/src/cli/commands/twitter/__tests__/cli-read-routing.test.ts +345 -0
- package/src/cli/commands/twitter/__tests__/cli-routing.test.ts +252 -0
- package/src/{__tests__/twitter-oauth-client.test.ts → cli/commands/twitter/__tests__/oauth-client.test.ts} +2 -48
- package/src/cli/commands/twitter/index.ts +420 -0
- package/src/{twitter → cli/commands/twitter}/oauth-client.ts +1 -35
- package/src/cli/commands/twitter/router.ts +351 -0
- package/src/cli/commands/twitter/types.ts +30 -0
- package/src/cli/db.ts +1 -0
- package/src/cli/http-client.ts +87 -0
- package/src/cli/logger.ts +6 -0
- package/src/cli/main-screen.tsx +4 -3
- package/src/cli/output.ts +19 -0
- package/src/cli/program.ts +29 -27
- package/src/cli/reference.ts +27 -37
- package/src/cli.ts +452 -240
- package/src/config/assistant-feature-flags.ts +3 -15
- package/src/config/bundled-skills/_shared/CLI_RETRIEVAL_PATTERN.md +3 -6
- package/src/config/bundled-skills/agentmail/SKILL.md +4 -4
- package/src/config/bundled-skills/amazon/SKILL.md +15 -6
- package/src/config/bundled-skills/api-mapping/SKILL.md +4 -4
- package/src/config/bundled-skills/app-builder/SKILL.md +4 -9
- package/src/config/bundled-skills/app-builder/TOOLS.json +0 -4
- package/src/config/bundled-skills/browser/SKILL.md +4 -5
- package/src/config/bundled-skills/chatgpt-import/SKILL.md +4 -4
- package/src/config/bundled-skills/chatgpt-import/tools/chatgpt-import.ts +1 -1
- package/src/config/bundled-skills/claude-code/SKILL.md +4 -4
- package/src/config/bundled-skills/cli-discover/SKILL.md +4 -4
- package/src/config/bundled-skills/computer-use/SKILL.md +4 -4
- package/src/config/bundled-skills/contacts/SKILL.md +23 -77
- package/src/config/bundled-skills/deploy-fullstack-vercel/SKILL.md +4 -4
- package/src/config/bundled-skills/document/SKILL.md +4 -3
- package/src/config/bundled-skills/document-writer/SKILL.md +4 -4
- package/src/config/bundled-skills/doordash/SKILL.md +4 -12
- package/src/config/bundled-skills/doordash/__tests__/doordash-session.test.ts +1 -90
- package/src/config/bundled-skills/doordash/doordash-cli.ts +132 -109
- package/src/config/bundled-skills/doordash/lib/session.ts +22 -19
- package/src/config/bundled-skills/doordash/lib/shared/platform.ts +26 -9
- package/src/config/bundled-skills/elevenlabs-voice/SKILL.md +140 -0
- package/src/config/bundled-skills/email-setup/SKILL.md +4 -4
- package/src/config/bundled-skills/followups/SKILL.md +4 -3
- package/src/config/bundled-skills/frontend-design/SKILL.md +2 -0
- package/src/config/bundled-skills/google-calendar/SKILL.md +4 -4
- package/src/config/bundled-skills/google-oauth-setup/SKILL.md +4 -6
- package/src/config/bundled-skills/guardian-verify-setup/SKILL.md +26 -41
- package/src/config/bundled-skills/image-studio/SKILL.md +4 -5
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +1 -1
- package/src/config/bundled-skills/influencer/SKILL.md +19 -19
- package/src/{influencer → config/bundled-skills/influencer/scripts}/client.ts +73 -56
- package/src/config/bundled-skills/influencer/scripts/influencer.ts +267 -0
- package/src/config/bundled-skills/knowledge-graph/SKILL.md +4 -2
- package/src/config/bundled-skills/macos-automation/SKILL.md +4 -5
- package/src/config/bundled-skills/mcp-setup/SKILL.md +4 -4
- package/src/config/bundled-skills/media-processing/SKILL.md +3 -2
- package/src/config/bundled-skills/messaging/SKILL.md +6 -33
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +0 -5
- package/src/config/bundled-skills/notifications/SKILL.md +4 -4
- package/src/config/bundled-skills/notion/SKILL.md +4 -4
- package/src/config/bundled-skills/notion-oauth-setup/SKILL.md +4 -5
- package/src/config/bundled-skills/oauth-setup/SKILL.md +4 -5
- package/src/config/bundled-skills/phone-calls/SKILL.md +24 -458
- package/src/config/bundled-skills/phone-calls/references/CONFIG.md +83 -0
- package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +57 -0
- package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +67 -0
- package/src/config/bundled-skills/playbooks/SKILL.md +4 -3
- package/src/config/bundled-skills/public-ingress/SKILL.md +65 -14
- package/src/config/bundled-skills/reminder/SKILL.md +4 -3
- package/src/config/bundled-skills/restaurant-reservation/SKILL.md +4 -6
- package/src/config/bundled-skills/schedule/SKILL.md +4 -3
- package/src/config/bundled-skills/screen-recording/SKILL.md +4 -3
- package/src/config/bundled-skills/self-upgrade/SKILL.md +4 -4
- package/src/config/bundled-skills/skills-catalog/SKILL.md +4 -4
- package/src/config/bundled-skills/slack/SKILL.md +4 -8
- package/src/config/bundled-skills/slack/tools/slack-channel-permissions.ts +1 -1
- package/src/config/bundled-skills/slack-app-setup/SKILL.md +66 -88
- package/src/config/bundled-skills/slack-digest-setup/SKILL.md +4 -5
- package/src/config/bundled-skills/slack-oauth-setup/SKILL.md +4 -5
- package/src/config/bundled-skills/start-the-day/SKILL.md +4 -4
- package/src/config/bundled-skills/subagent/SKILL.md +4 -3
- package/src/config/bundled-skills/tasks/SKILL.md +4 -3
- package/src/config/bundled-skills/telegram-setup/SKILL.md +63 -112
- package/src/config/bundled-skills/time-based-actions/SKILL.md +4 -3
- package/src/config/bundled-skills/transcribe/SKILL.md +4 -3
- package/src/config/bundled-skills/twilio-setup/SKILL.md +23 -50
- package/src/config/bundled-skills/twitter/SKILL.md +73 -144
- package/src/config/bundled-skills/typescript-eval/SKILL.md +4 -4
- package/src/config/bundled-skills/vercel-token-setup/SKILL.md +4 -5
- package/src/config/bundled-skills/voice-setup/SKILL.md +19 -45
- package/src/config/bundled-skills/watcher/SKILL.md +4 -3
- package/src/config/env-registry.ts +1 -10
- package/src/config/feature-flag-registry.json +8 -16
- package/src/config/loader.ts +78 -38
- package/src/config/schema.ts +143 -106
- package/src/config/schemas/channels.ts +80 -0
- package/src/config/schemas/heartbeat.ts +51 -0
- package/src/config/schemas/inference.ts +136 -0
- package/src/config/schemas/ingress.ts +81 -0
- package/src/config/schemas/logging.ts +21 -0
- package/src/config/schemas/memory-lifecycle.ts +67 -0
- package/src/config/schemas/memory-processing.ts +215 -0
- package/src/config/schemas/memory-retrieval.ts +222 -0
- package/src/config/schemas/memory-storage.ts +83 -0
- package/src/config/schemas/memory.ts +58 -0
- package/src/config/schemas/platform.ts +64 -0
- package/src/config/schemas/security.ts +54 -0
- package/src/config/schemas/swarm.ts +50 -0
- package/src/config/schemas/timeouts.ts +47 -0
- package/src/config/{agent-schema.ts → schemas/workspace-git.ts} +0 -97
- package/src/config/skill-state.ts +3 -13
- package/src/config/skills.ts +196 -75
- package/src/config/types.ts +1 -20
- package/src/contacts/contact-store.ts +12 -49
- package/src/contacts/contacts-write.ts +1 -5
- package/src/contacts/index.ts +0 -2
- package/src/contacts/types.ts +0 -8
- package/src/context/window-manager.ts +73 -14
- package/src/daemon/assistant-attachments.ts +9 -0
- package/src/daemon/computer-use-session.ts +3 -3
- package/src/daemon/connection-policy.ts +6 -21
- package/src/daemon/context-overflow-policy.ts +1 -1
- package/src/daemon/daemon-control.ts +46 -54
- package/src/daemon/doordash-steps.ts +1 -1
- package/src/daemon/handlers/config-channels.ts +407 -71
- package/src/daemon/handlers/config-ingress.ts +17 -85
- package/src/daemon/handlers/config-model.ts +145 -123
- package/src/daemon/handlers/config-slack-channel.ts +43 -29
- package/src/daemon/handlers/config-telegram.ts +32 -27
- package/src/daemon/handlers/config-voice.ts +1 -4
- package/src/daemon/handlers/dictation.ts +11 -16
- package/src/daemon/handlers/identity.ts +5 -6
- package/src/daemon/handlers/pairing.ts +5 -13
- package/src/daemon/handlers/recording.ts +97 -199
- package/src/daemon/handlers/session-history.ts +151 -105
- package/src/daemon/handlers/session-user-message.ts +29 -57
- package/src/daemon/handlers/sessions.ts +240 -137
- package/src/daemon/handlers/shared.ts +62 -95
- package/src/daemon/handlers/skills.ts +492 -543
- package/src/daemon/lifecycle.ts +155 -55
- package/src/daemon/{ipc-contract.ts → message-protocol.ts} +49 -49
- package/src/daemon/{ipc-contract → message-types}/apps.ts +0 -25
- package/src/daemon/{ipc-contract → message-types}/computer-use.ts +0 -3
- package/src/daemon/{ipc-contract → message-types}/diagnostics.ts +0 -16
- package/src/daemon/{ipc-contract → message-types}/integrations.ts +30 -20
- package/src/daemon/{ipc-contract → message-types}/memory.ts +8 -0
- package/src/daemon/{ipc-contract → message-types}/notifications.ts +15 -1
- package/src/daemon/{ipc-contract → message-types}/sessions.ts +7 -1
- package/src/daemon/{ipc-contract → message-types}/shared.ts +0 -8
- package/src/daemon/{ipc-contract → message-types}/surfaces.ts +2 -0
- package/src/daemon/{ipc-contract → message-types}/workspace.ts +2 -2
- package/src/daemon/providers-setup.ts +0 -5
- package/src/daemon/recording-executor.ts +0 -7
- package/src/daemon/ride-shotgun-handler.ts +42 -14
- package/src/daemon/seed-files.ts +3 -27
- package/src/daemon/server.ts +134 -524
- package/src/daemon/session-agent-loop-handlers.ts +46 -9
- package/src/daemon/session-agent-loop.ts +86 -24
- package/src/daemon/session-attachments.ts +1 -1
- package/src/daemon/session-error.ts +1 -1
- package/src/daemon/session-history.ts +20 -15
- package/src/daemon/session-lifecycle.ts +9 -7
- package/src/daemon/session-memory.ts +15 -1
- package/src/daemon/session-messaging.ts +10 -6
- package/src/daemon/session-notifiers.ts +10 -8
- package/src/daemon/session-process.ts +34 -25
- package/src/daemon/session-queue-manager.ts +1 -1
- package/src/daemon/session-runtime-assembly.ts +6 -32
- package/src/daemon/session-surfaces.ts +187 -35
- package/src/daemon/session-tool-setup.ts +1 -1
- package/src/daemon/session-usage.ts +119 -18
- package/src/daemon/session.ts +11 -33
- package/src/daemon/tool-side-effects.ts +6 -5
- package/src/daemon/trace-emitter.ts +1 -1
- package/src/daemon/{guardian-verification-intent.ts → verification-session-intent.ts} +16 -16
- package/src/daemon/watch-handler.ts +2 -5
- package/src/email/service.ts +8 -8
- package/src/events/domain-events.ts +0 -1
- package/src/events/tool-notification-listener.ts +1 -1
- package/src/followups/followup-store.ts +1 -2
- package/src/followups/types.ts +0 -6
- package/src/heartbeat/heartbeat-service.ts +1 -1
- package/src/inbound/platform-callback-registration.ts +1 -1
- package/src/inbound/public-ingress-urls.ts +0 -8
- package/src/index.ts +12 -0
- package/src/mcp/client.ts +1 -1
- package/src/mcp/manager.ts +1 -1
- package/src/memory/app-store.ts +1 -60
- package/src/memory/{guardian-verification.ts → channel-verification-sessions.ts} +110 -93
- package/src/memory/conversation-attention-store.ts +154 -0
- package/src/memory/conversation-bootstrap.ts +1 -1
- package/src/memory/conversation-crud.ts +53 -1
- package/src/memory/conversation-display-order-migration.ts +2 -3
- package/src/memory/conversation-queries.ts +1 -29
- package/src/memory/conversation-title-service.ts +26 -21
- package/src/memory/db-connection.ts +1 -8
- package/src/memory/db-init.ts +20 -0
- package/src/memory/delivery-crud.ts +4 -34
- package/src/memory/external-conversation-store.ts +1 -1
- package/src/memory/format-recall.ts +47 -0
- package/src/memory/guardian-action-store.ts +4 -5
- package/src/memory/guardian-rate-limits.ts +0 -3
- package/src/memory/invite-store.ts +1 -1
- package/src/memory/job-handlers/backfill.ts +9 -2
- package/src/memory/job-handlers/extraction.ts +2 -7
- package/src/memory/job-handlers/summarization.ts +1 -1
- package/src/memory/llm-usage-store.ts +11 -0
- package/src/memory/migrations/114-notifications.ts +12 -40
- package/src/memory/migrations/140-backfill-usage-cache-accounting.ts +357 -0
- package/src/memory/migrations/141-rename-verification-table.ts +55 -0
- package/src/memory/migrations/142-rename-verification-session-id-column.ts +32 -0
- package/src/memory/migrations/143-rename-guardian-verification-values.ts +48 -0
- package/src/memory/migrations/144-rename-voice-to-phone.ts +147 -0
- package/src/memory/migrations/index.ts +5 -0
- package/src/memory/migrations/registry.ts +30 -0
- package/src/memory/qdrant-circuit-breaker.ts +5 -0
- package/src/memory/retriever.test.ts +707 -0
- package/src/memory/retriever.ts +120 -116
- package/src/memory/schema/calls.ts +3 -7
- package/src/memory/schema/guardian.ts +2 -2
- package/src/memory/schema/infrastructure.ts +0 -8
- package/src/memory/search/lexical.ts +4 -1
- package/src/memory/search/query-expansion.test.ts +70 -0
- package/src/memory/search/query-expansion.ts +118 -0
- package/src/memory/search/types.ts +18 -17
- package/src/messaging/providers/telegram-bot/adapter.ts +1 -1
- package/src/messaging/providers/whatsapp/adapter.ts +1 -4
- package/src/messaging/registry.ts +0 -1
- package/src/notifications/README.md +13 -22
- package/src/notifications/adapters/macos.ts +1 -1
- package/src/notifications/conversation-pairing.ts +2 -2
- package/src/notifications/copy-composer.ts +2 -2
- package/src/notifications/decision-engine.ts +1 -10
- package/src/notifications/destination-resolver.ts +2 -3
- package/src/notifications/emit-signal.ts +2 -8
- package/src/notifications/guardian-question-mode.ts +5 -8
- package/src/notifications/signal.ts +1 -2
- package/src/notifications/types.ts +1 -1
- package/src/oauth/token-persistence.ts +25 -1
- package/src/permissions/checker.ts +4 -29
- package/src/permissions/defaults.ts +9 -9
- package/src/permissions/prompter.ts +1 -1
- package/src/permissions/secret-prompter.ts +1 -1
- package/src/permissions/shell-identity.ts +1 -1
- package/src/permissions/trust-store.ts +13 -76
- package/src/permissions/workspace-policy.ts +1 -1
- package/src/{config → prompts}/computer-use-prompt.ts +1 -1
- package/src/{config → prompts}/system-prompt.ts +44 -26
- package/src/{config → prompts}/templates/BOOTSTRAP.md +0 -3
- package/src/providers/registry.ts +2 -4
- package/src/runtime/AGENTS.md +6 -8
- package/src/runtime/access-request-helper.ts +36 -55
- package/src/runtime/actor-trust-resolver.ts +1 -24
- package/src/runtime/approval-message-composer.ts +6 -2
- package/src/runtime/assistant-event.ts +1 -1
- package/src/runtime/auth/__tests__/guard-tests.test.ts +1 -0
- package/src/runtime/auth/__tests__/ipc-auth-context.test.ts +1 -1
- package/src/runtime/auth/__tests__/scopes.test.ts +2 -1
- package/src/runtime/auth/__tests__/subject.test.ts +32 -0
- package/src/runtime/auth/route-policy.ts +137 -25
- package/src/runtime/auth/scopes.ts +1 -0
- package/src/runtime/auth/subject.ts +9 -0
- package/src/runtime/auth/token-service.ts +12 -1
- package/src/runtime/auth/types.ts +1 -1
- package/src/runtime/channel-approval-types.ts +1 -1
- package/src/runtime/channel-approvals.ts +1 -1
- package/src/runtime/channel-invite-transport.ts +0 -2
- package/src/runtime/channel-invite-transports/slack.ts +5 -19
- package/src/runtime/channel-invite-transports/telegram.ts +17 -34
- package/src/runtime/channel-invite-transports/voice.ts +1 -1
- package/src/runtime/channel-readiness-service.ts +24 -159
- package/src/runtime/channel-readiness-types.ts +5 -1
- package/src/runtime/channel-reply-delivery.ts +43 -3
- package/src/runtime/channel-retry-sweep.ts +14 -22
- package/src/runtime/{channel-guardian-service.ts → channel-verification-service.ts} +50 -53
- package/src/runtime/confirmation-request-guardian-bridge.ts +2 -3
- package/src/runtime/gateway-client.ts +12 -15
- package/src/runtime/guardian-action-followup-executor.ts +8 -73
- package/src/runtime/guardian-action-grant-minter.ts +45 -61
- package/src/runtime/guardian-action-message-composer.ts +4 -4
- package/src/runtime/guardian-reply-router.ts +3 -3
- package/src/runtime/http-server.ts +133 -24
- package/src/runtime/http-types.ts +44 -1
- package/src/runtime/invite-instruction-generator.ts +1 -3
- package/src/runtime/invite-redemption-service.ts +5 -5
- package/src/runtime/invite-service.ts +7 -7
- package/src/runtime/local-actor-identity.ts +28 -2
- package/src/runtime/local-gateway-health.ts +275 -0
- package/src/runtime/middleware/error-handler.ts +14 -1
- package/src/runtime/middleware/twilio-validation.ts +3 -3
- package/src/runtime/migrations/migration-transport.ts +18 -3
- package/src/runtime/migrations/rebind-secrets-screen.ts +2 -2
- package/src/runtime/nl-approval-parser.ts +2 -3
- package/src/runtime/routes/access-request-decision.ts +2 -2
- package/src/runtime/routes/app-management-routes.ts +918 -0
- package/src/runtime/routes/approval-routes.ts +76 -7
- package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +38 -203
- package/src/runtime/routes/brain-graph/brain-graph.html +1845 -0
- package/src/runtime/routes/brain-graph-routes.ts +4 -42
- package/src/runtime/routes/channel-delivery-routes.ts +5 -4
- package/src/runtime/routes/channel-route-shared.ts +1 -3
- package/src/runtime/routes/channel-routes.ts +1 -4
- package/src/runtime/routes/channel-verification-routes.ts +257 -0
- package/src/runtime/routes/computer-use-routes.ts +595 -0
- package/src/runtime/routes/contact-routes.ts +1 -317
- package/src/runtime/routes/conversation-attention-routes.ts +6 -5
- package/src/runtime/routes/conversation-routes.ts +20 -24
- package/src/runtime/routes/debug-routes.ts +1 -1
- package/src/runtime/routes/diagnostics-routes.ts +890 -0
- package/src/runtime/routes/documents-routes.ts +227 -0
- package/src/runtime/routes/guardian-approval-interception.ts +25 -48
- package/src/runtime/routes/guardian-bootstrap-routes.ts +3 -3
- package/src/runtime/routes/guardian-expiry-sweep.ts +2 -2
- package/src/runtime/routes/guardian-refresh-routes.ts +11 -6
- package/src/runtime/routes/inbound-conversation.ts +3 -10
- package/src/runtime/routes/inbound-message-handler.ts +7 -6
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +22 -22
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +44 -0
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +140 -22
- package/src/runtime/routes/inbound-stages/bootstrap-intercept.ts +4 -4
- package/src/runtime/routes/inbound-stages/edit-intercept.ts +5 -5
- package/src/runtime/routes/inbound-stages/escalation-intercept.ts +3 -3
- package/src/runtime/routes/inbound-stages/secret-ingress-check.ts +4 -4
- package/src/runtime/routes/inbound-stages/verification-intercept.ts +13 -14
- package/src/runtime/routes/integrations/slack/channel.ts +72 -0
- package/src/runtime/routes/{slack-share-routes.ts → integrations/slack/share.ts} +9 -9
- package/src/runtime/routes/integrations/telegram.ts +111 -0
- package/src/runtime/routes/integrations/twilio.ts +451 -0
- package/src/runtime/routes/invite-routes.ts +2 -2
- package/src/runtime/routes/pairing-routes.ts +1 -1
- package/src/runtime/routes/recording-routes.ts +332 -0
- package/src/{daemon/handlers/config-scheduling.ts → runtime/routes/schedule-routes.ts} +91 -106
- package/src/runtime/routes/session-management-routes.ts +167 -0
- package/src/runtime/routes/session-query-routes.ts +204 -0
- package/src/runtime/routes/settings-routes.ts +887 -0
- package/src/runtime/routes/skills-routes.ts +266 -0
- package/src/runtime/routes/subagents-routes.ts +246 -0
- package/src/runtime/routes/surface-action-routes.ts +100 -10
- package/src/runtime/routes/surface-content-routes.ts +1 -1
- package/src/runtime/routes/work-items-routes.ts +809 -0
- package/src/runtime/routes/workspace-routes.test.ts +778 -0
- package/src/runtime/routes/workspace-routes.ts +410 -0
- package/src/runtime/routes/workspace-utils.ts +88 -0
- package/src/runtime/telegram-streaming-delivery.test.ts +597 -0
- package/src/runtime/telegram-streaming-delivery.ts +380 -0
- package/src/runtime/tool-grant-request-helper.ts +1 -2
- package/src/runtime/trust-context-resolver.ts +0 -1
- package/src/runtime/{guardian-outbound-actions.ts → verification-outbound-actions.ts} +23 -188
- package/src/runtime/verification-rate-limiter.ts +2 -2
- package/src/runtime/{guardian-verification-templates.ts → verification-templates.ts} +2 -28
- package/src/schedule/integration-status.ts +2 -2
- package/src/schedule/schedule-store.ts +7 -9
- package/src/sequence/engine.ts +1 -1
- package/src/skills/active-skill-tools.ts +0 -8
- package/src/skills/clawhub.ts +1 -10
- package/src/skills/managed-store.ts +14 -4
- package/src/skills/slash-commands.ts +1 -1
- package/src/subagent/manager.ts +1 -1
- package/src/subagent/types.ts +1 -1
- package/src/tasks/SPEC.md +10 -10
- package/src/tasks/task-scheduler.ts +1 -1
- package/src/telegram/bot-username.ts +13 -0
- package/src/tools/AGENTS.md +38 -0
- package/src/tools/apps/executors.ts +0 -6
- package/src/tools/assets/materialize.ts +1 -1
- package/src/tools/assets/search.ts +1 -1
- package/src/tools/browser/browser-execution.ts +2 -2
- package/src/tools/browser/browser-manager.ts +88 -11
- package/src/tools/browser/browser-screencast.ts +1 -1
- package/src/tools/browser/headless-browser.ts +0 -17
- package/src/tools/browser/jit-auth.ts +1 -1
- package/src/tools/browser/recording-store.ts +19 -1
- package/src/tools/browser/runtime-check.ts +4 -2
- package/src/tools/calls/call-start.ts +3 -3
- package/src/tools/credentials/metadata-store.ts +0 -13
- package/src/tools/credentials/vault.ts +7 -31
- package/src/tools/document/editor-template.ts +10 -8
- package/src/tools/followups/followup_create.ts +0 -8
- package/src/tools/mcp/mcp-tool-factory.ts +1 -1
- package/src/tools/memory/definitions.ts +32 -10
- package/src/tools/memory/handlers.test.ts +573 -0
- package/src/tools/memory/handlers.ts +222 -65
- package/src/tools/memory/register.ts +53 -24
- package/src/tools/network/script-proxy/session-manager.ts +1 -12
- package/src/tools/schedule/update.ts +0 -8
- package/src/tools/skills/load.ts +3 -3
- package/src/tools/subagent/read.ts +1 -1
- package/src/tools/system/voice-config.ts +2 -14
- package/src/tools/terminal/safe-env.ts +5 -18
- package/src/tools/tool-approval-handler.ts +4 -4
- package/src/tools/tool-manifest.ts +4 -2
- package/src/tools/types.ts +1 -1
- package/src/tools/{guardian-control-plane-policy.ts → verification-control-plane-policy.ts} +37 -39
- package/src/twitter/platform-proxy-client.ts +408 -0
- package/src/usage/types.ts +21 -0
- package/src/util/canonicalize-identity.ts +2 -6
- package/src/util/errors.ts +12 -0
- package/src/util/platform.ts +93 -86
- package/src/util/pricing.ts +180 -43
- package/src/work-items/work-item-runner.ts +1 -1
- package/scripts/ipc/check-contract-inventory.ts +0 -107
- package/scripts/ipc/check-swift-decoder-drift.ts +0 -184
- package/scripts/ipc/generate-swift.ts +0 -528
- package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +0 -3043
- package/src/__tests__/app-migration.test.ts +0 -148
- package/src/__tests__/config-loader-migration.test.ts +0 -85
- package/src/__tests__/daemon-lifecycle.test.ts +0 -715
- package/src/__tests__/daemon-server-session-init.test.ts +0 -864
- package/src/__tests__/guardian-actions-endpoint.test.ts +0 -1452
- package/src/__tests__/handlers-add-trust-rule-metadata.test.ts +0 -228
- package/src/__tests__/handlers-cu-observation-blob.test.ts +0 -397
- package/src/__tests__/handlers-ipc-blob-probe.test.ts +0 -218
- package/src/__tests__/handlers-slack-config.test.ts +0 -140
- package/src/__tests__/handlers-telegram-config.test.ts +0 -1317
- package/src/__tests__/handlers-twitter-config.test.ts +0 -1145
- package/src/__tests__/home-base-bootstrap.test.ts +0 -86
- package/src/__tests__/ingress-reconcile.test.ts +0 -606
- package/src/__tests__/integrations-cli.test.ts +0 -232
- package/src/__tests__/ipc-blob-store.test.ts +0 -329
- package/src/__tests__/ipc-contract-inventory.test.ts +0 -69
- package/src/__tests__/ipc-contract.test.ts +0 -76
- package/src/__tests__/ipc-protocol.test.ts +0 -120
- package/src/__tests__/ipc-roundtrip.benchmark.test.ts +0 -250
- package/src/__tests__/ipc-snapshot.test.ts +0 -2197
- package/src/__tests__/ipc-validate.test.ts +0 -471
- package/src/__tests__/migration-cli-flows.test.ts +0 -186
- package/src/__tests__/migration-ordering.test.ts +0 -267
- package/src/__tests__/oauth-connect-handler.test.ts +0 -361
- package/src/__tests__/platform-move-helper.test.ts +0 -108
- package/src/__tests__/platform-socket-path.test.ts +0 -52
- package/src/__tests__/platform-workspace-migration.test.ts +0 -1051
- package/src/__tests__/prebuilt-home-base-seed.test.ts +0 -79
- package/src/__tests__/recording-intent-handler.test.ts +0 -1155
- package/src/__tests__/script-proxy-profile-template-fallback.test.ts +0 -127
- package/src/__tests__/sms-messaging-provider.test.ts +0 -156
- package/src/__tests__/tool-permission-simulate-handler.test.ts +0 -367
- package/src/__tests__/twitter-auth-handler.test.ts +0 -561
- package/src/__tests__/twitter-cli-error-shaping.test.ts +0 -224
- package/src/__tests__/twitter-cli-routing.test.ts +0 -286
- package/src/__tests__/work-item-output.test.ts +0 -150
- package/src/amazon/session.ts +0 -58
- package/src/cli/channels.ts +0 -51
- package/src/cli/influencer.ts +0 -319
- package/src/cli/integrations.ts +0 -372
- package/src/cli/ipc-client.ts +0 -88
- package/src/cli/twitter.ts +0 -1111
- package/src/config/bundled-skills/configure-settings/SKILL.md +0 -86
- package/src/config/bundled-skills/doordash/lib/shared/ipc.ts +0 -32
- package/src/config/bundled-skills/sms-setup/SKILL.md +0 -210
- package/src/config/core-schema.ts +0 -434
- package/src/config/memory-schema.ts +0 -617
- package/src/daemon/auth-manager.ts +0 -106
- package/src/daemon/handlers/apps.ts +0 -783
- package/src/daemon/handlers/avatar.ts +0 -73
- package/src/daemon/handlers/browser.ts +0 -3
- package/src/daemon/handlers/computer-use.ts +0 -231
- package/src/daemon/handlers/config-dispatch.ts +0 -29
- package/src/daemon/handlers/config-heartbeat.ts +0 -299
- package/src/daemon/handlers/config-inbox.ts +0 -457
- package/src/daemon/handlers/config-integrations.ts +0 -409
- package/src/daemon/handlers/config-platform.ts +0 -77
- package/src/daemon/handlers/config-slack.ts +0 -41
- package/src/daemon/handlers/config-tools.ts +0 -226
- package/src/daemon/handlers/config-trust.ts +0 -135
- package/src/daemon/handlers/config.ts +0 -64
- package/src/daemon/handlers/contacts.ts +0 -193
- package/src/daemon/handlers/diagnostics.ts +0 -382
- package/src/daemon/handlers/documents.ts +0 -188
- package/src/daemon/handlers/guardian-actions.ts +0 -82
- package/src/daemon/handlers/home-base.ts +0 -82
- package/src/daemon/handlers/index.ts +0 -222
- package/src/daemon/handlers/misc.ts +0 -1139
- package/src/daemon/handlers/navigate-settings.ts +0 -29
- package/src/daemon/handlers/oauth-connect.ts +0 -202
- package/src/daemon/handlers/open-bundle-handler.ts +0 -88
- package/src/daemon/handlers/publish.ts +0 -176
- package/src/daemon/handlers/signing.ts +0 -56
- package/src/daemon/handlers/subagents.ts +0 -286
- package/src/daemon/handlers/twitter-auth.ts +0 -220
- package/src/daemon/handlers/work-items.ts +0 -796
- package/src/daemon/handlers/workspace-files.ts +0 -84
- package/src/daemon/handlers.ts +0 -16
- package/src/daemon/ipc-blob-store.ts +0 -246
- package/src/daemon/ipc-contract-inventory.json +0 -348
- package/src/daemon/ipc-contract-inventory.ts +0 -202
- package/src/daemon/ipc-handler.ts +0 -120
- package/src/daemon/ipc-protocol.ts +0 -85
- package/src/daemon/ipc-validate.ts +0 -254
- package/src/home-base/app-link-store.ts +0 -78
- package/src/home-base/bootstrap.ts +0 -74
- package/src/home-base/prebuilt/brain-graph.html +0 -1483
- package/src/home-base/prebuilt/index.html +0 -702
- package/src/home-base/prebuilt/seed-metadata.json +0 -21
- package/src/home-base/prebuilt/seed.ts +0 -122
- package/src/home-base/prebuilt-home-base-updater.ts +0 -36
- package/src/memory/app-migration.ts +0 -114
- package/src/memory/channel-delivery-store.ts +0 -40
- package/src/memory/channel-guardian-store.ts +0 -83
- package/src/memory/conversation-store.ts +0 -102
- package/src/memory/schema-migration.ts +0 -38
- package/src/messaging/providers/sms/adapter.ts +0 -232
- package/src/messaging/providers/sms/client.ts +0 -93
- package/src/messaging/providers/sms/types.ts +0 -7
- package/src/migrations/config-merge.ts +0 -62
- package/src/migrations/data-layout.ts +0 -89
- package/src/migrations/data-merge.ts +0 -44
- package/src/migrations/hooks-merge.ts +0 -118
- package/src/migrations/index.ts +0 -6
- package/src/migrations/log.ts +0 -28
- package/src/migrations/skills-merge.ts +0 -44
- package/src/migrations/workspace-layout.ts +0 -94
- package/src/notifications/adapters/sms.ts +0 -94
- package/src/runtime/channel-approval-parser.ts +0 -123
- package/src/runtime/channel-invite-transports/sms.ts +0 -53
- package/src/runtime/routes/approval-strategies/guardian-legacy-fallback-strategy.ts +0 -82
- package/src/runtime/routes/integration-routes.ts +0 -381
- package/src/runtime/routes/twilio-routes.ts +0 -1251
- package/src/twitter/client.ts +0 -979
- package/src/twitter/router.ts +0 -131
- package/src/twitter/session.ts +0 -54
- package/src/util/cookie-session.ts +0 -114
- package/src/watcher/providers/slack.ts +0 -282
- /package/src/{amazon → cli/commands/amazon}/cart.ts +0 -0
- /package/src/{amazon → cli/commands/amazon}/checkout.ts +0 -0
- /package/src/{amazon → cli/commands/amazon}/product-details.ts +0 -0
- /package/src/{amazon → cli/commands/amazon}/search.ts +0 -0
- /package/src/config/{calls-schema.ts → schemas/calls.ts} +0 -0
- /package/src/config/{elevenlabs-schema.ts → schemas/elevenlabs.ts} +0 -0
- /package/src/config/{mcp-schema.ts → schemas/mcp.ts} +0 -0
- /package/src/config/{notifications-schema.ts → schemas/notifications.ts} +0 -0
- /package/src/config/{sandbox-schema.ts → schemas/sandbox.ts} +0 -0
- /package/src/config/{skills-schema.ts → schemas/skills.ts} +0 -0
- /package/src/daemon/{ipc-contract → message-types}/browser.ts +0 -0
- /package/src/daemon/{ipc-contract → message-types}/contacts.ts +0 -0
- /package/src/daemon/{ipc-contract → message-types}/documents.ts +0 -0
- /package/src/daemon/{ipc-contract → message-types}/guardian-actions.ts +0 -0
- /package/src/daemon/{ipc-contract → message-types}/inbox.ts +0 -0
- /package/src/daemon/{ipc-contract → message-types}/messages.ts +0 -0
- /package/src/daemon/{ipc-contract → message-types}/pairing.ts +0 -0
- /package/src/daemon/{ipc-contract → message-types}/schedules.ts +0 -0
- /package/src/daemon/{ipc-contract → message-types}/settings.ts +0 -0
- /package/src/daemon/{ipc-contract → message-types}/skills.ts +0 -0
- /package/src/daemon/{ipc-contract → message-types}/subagents.ts +0 -0
- /package/src/daemon/{ipc-contract → message-types}/trust.ts +0 -0
- /package/src/daemon/{ipc-contract → message-types}/work-items.ts +0 -0
- /package/src/{cli/email-guardrails.ts → email/guardrails.ts} +0 -0
- /package/src/{config → prompts}/__tests__/build-cli-reference-section.test.ts +0 -0
- /package/src/{config → prompts}/templates/IDENTITY.md +0 -0
- /package/src/{config → prompts}/templates/SOUL.md +0 -0
- /package/src/{config → prompts}/templates/UPDATES.md +0 -0
- /package/src/{config → prompts}/templates/USER.md +0 -0
- /package/src/{config → prompts}/update-bulletin-format.ts +0 -0
- /package/src/{config → prompts}/update-bulletin-state.ts +0 -0
- /package/src/{config → prompts}/update-bulletin-template-path.ts +0 -0
- /package/src/{config → prompts}/update-bulletin.ts +0 -0
- /package/src/{config → prompts}/user-reference.ts +0 -0
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import * as net from "node:net";
|
|
2
1
|
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
3
2
|
|
|
4
3
|
// ─── Mocks (must be before any imports that depend on them) ─────────────────
|
|
@@ -53,7 +52,7 @@ mock.module("../config/loader.js", () => ({
|
|
|
53
52
|
const mockMessages: Array<{ id: string; role: string; content: string }> = [];
|
|
54
53
|
let mockMessageIdCounter = 0;
|
|
55
54
|
|
|
56
|
-
mock.module("../memory/conversation-
|
|
55
|
+
mock.module("../memory/conversation-crud.js", () => ({
|
|
57
56
|
getConversationThreadType: () => "default",
|
|
58
57
|
setConversationOriginChannelIfUnset: () => {},
|
|
59
58
|
updateConversationContextWindow: () => {},
|
|
@@ -135,7 +134,7 @@ import {
|
|
|
135
134
|
recordingHandlers,
|
|
136
135
|
} from "../daemon/handlers/recording.js";
|
|
137
136
|
import type { HandlerContext } from "../daemon/handlers/shared.js";
|
|
138
|
-
import type { RecordingStatus } from "../daemon/
|
|
137
|
+
import type { RecordingStatus } from "../daemon/message-types/computer-use.js";
|
|
139
138
|
import { executeRecordingIntent } from "../daemon/recording-executor.js";
|
|
140
139
|
import { DebouncerMap } from "../util/debounce.js";
|
|
141
140
|
|
|
@@ -147,28 +146,24 @@ const ALLOWED_RECORDINGS_DIR = `${process.env.HOME}/Library/Application Support/
|
|
|
147
146
|
function createCtx(): {
|
|
148
147
|
ctx: HandlerContext;
|
|
149
148
|
sent: Array<{ type: string; [k: string]: unknown }>;
|
|
150
|
-
fakeSocket: net.Socket;
|
|
151
149
|
} {
|
|
152
150
|
const sent: Array<{ type: string; [k: string]: unknown }> = [];
|
|
153
|
-
const fakeSocket = {} as net.Socket;
|
|
154
|
-
const socketToSession = new Map<net.Socket, string>();
|
|
155
151
|
|
|
156
152
|
const ctx: HandlerContext = {
|
|
157
153
|
sessions: new Map(),
|
|
158
|
-
socketToSession,
|
|
159
154
|
cuSessions: new Map(),
|
|
160
|
-
socketToCuSession: new Map(),
|
|
161
155
|
cuObservationParseSequence: new Map(),
|
|
162
|
-
socketSandboxOverride: new Map(),
|
|
163
156
|
sharedRequestTimestamps: [],
|
|
164
157
|
debounceTimers: new DebouncerMap({ defaultDelayMs: 200 }),
|
|
165
158
|
suppressConfigReload: false,
|
|
166
159
|
setSuppressConfigReload: noop,
|
|
167
160
|
updateConfigFingerprint: noop,
|
|
168
|
-
send: (
|
|
161
|
+
send: (msg) => {
|
|
162
|
+
sent.push(msg as { type: string; [k: string]: unknown });
|
|
163
|
+
},
|
|
164
|
+
broadcast: (msg) => {
|
|
169
165
|
sent.push(msg as { type: string; [k: string]: unknown });
|
|
170
166
|
},
|
|
171
|
-
broadcast: noop,
|
|
172
167
|
clearAllSessions: () => 0,
|
|
173
168
|
getOrCreateSession: () => {
|
|
174
169
|
throw new Error("not implemented");
|
|
@@ -176,7 +171,7 @@ function createCtx(): {
|
|
|
176
171
|
touchSession: noop,
|
|
177
172
|
};
|
|
178
173
|
|
|
179
|
-
return { ctx, sent
|
|
174
|
+
return { ctx, sent };
|
|
180
175
|
}
|
|
181
176
|
|
|
182
177
|
// ─── Restart state machine tests ────────────────────────────────────────────
|
|
@@ -189,21 +184,19 @@ describe("handleRecordingRestart", () => {
|
|
|
189
184
|
});
|
|
190
185
|
|
|
191
186
|
test("sends recording_stop and defers start until stop-ack", () => {
|
|
192
|
-
const { ctx, sent
|
|
187
|
+
const { ctx, sent } = createCtx();
|
|
193
188
|
const conversationId = "conv-restart-1";
|
|
194
|
-
ctx.socketToSession.set(fakeSocket, conversationId);
|
|
195
189
|
|
|
196
190
|
// Start a recording first
|
|
197
191
|
const originalId = handleRecordingStart(
|
|
198
192
|
conversationId,
|
|
199
193
|
undefined,
|
|
200
|
-
fakeSocket,
|
|
201
194
|
ctx,
|
|
202
195
|
);
|
|
203
196
|
expect(originalId).not.toBeNull();
|
|
204
197
|
sent.length = 0;
|
|
205
198
|
|
|
206
|
-
const result = handleRecordingRestart(conversationId,
|
|
199
|
+
const result = handleRecordingRestart(conversationId, ctx);
|
|
207
200
|
|
|
208
201
|
expect(result.initiated).toBe(true);
|
|
209
202
|
expect(result.operationToken).toBeTruthy();
|
|
@@ -222,7 +215,7 @@ describe("handleRecordingRestart", () => {
|
|
|
222
215
|
status: "stopped",
|
|
223
216
|
attachToConversationId: conversationId,
|
|
224
217
|
};
|
|
225
|
-
recordingHandlers.recording_status(stoppedStatus,
|
|
218
|
+
recordingHandlers.recording_status(stoppedStatus, ctx);
|
|
226
219
|
|
|
227
220
|
// NOW the deferred recording_start should have been sent
|
|
228
221
|
const startMsgsAfterAck = sent.filter((m) => m.type === "recording_start");
|
|
@@ -231,9 +224,9 @@ describe("handleRecordingRestart", () => {
|
|
|
231
224
|
});
|
|
232
225
|
|
|
233
226
|
test('returns "no active recording" with reason when nothing is recording', () => {
|
|
234
|
-
const { ctx
|
|
227
|
+
const { ctx } = createCtx();
|
|
235
228
|
|
|
236
|
-
const result = handleRecordingRestart("conv-no-rec",
|
|
229
|
+
const result = handleRecordingRestart("conv-no-rec", ctx);
|
|
237
230
|
|
|
238
231
|
expect(result.initiated).toBe(false);
|
|
239
232
|
expect(result.reason).toBe("no_active_recording");
|
|
@@ -241,18 +234,16 @@ describe("handleRecordingRestart", () => {
|
|
|
241
234
|
});
|
|
242
235
|
|
|
243
236
|
test("generates unique operation token for each restart", () => {
|
|
244
|
-
const { ctx, sent
|
|
237
|
+
const { ctx, sent } = createCtx();
|
|
245
238
|
const conversationId = "conv-restart-unique";
|
|
246
|
-
ctx.socketToSession.set(fakeSocket, conversationId);
|
|
247
239
|
|
|
248
240
|
// First restart cycle
|
|
249
241
|
const originalId = handleRecordingStart(
|
|
250
242
|
conversationId,
|
|
251
243
|
undefined,
|
|
252
|
-
fakeSocket,
|
|
253
244
|
ctx,
|
|
254
245
|
);
|
|
255
|
-
const result1 = handleRecordingRestart(conversationId,
|
|
246
|
+
const result1 = handleRecordingRestart(conversationId, ctx);
|
|
256
247
|
|
|
257
248
|
// Simulate the stop-ack to trigger the deferred start
|
|
258
249
|
const stoppedStatus1: RecordingStatus = {
|
|
@@ -261,7 +252,7 @@ describe("handleRecordingRestart", () => {
|
|
|
261
252
|
status: "stopped",
|
|
262
253
|
attachToConversationId: conversationId,
|
|
263
254
|
};
|
|
264
|
-
recordingHandlers.recording_status(stoppedStatus1,
|
|
255
|
+
recordingHandlers.recording_status(stoppedStatus1, ctx);
|
|
265
256
|
|
|
266
257
|
// Simulate the first restart completing (started status)
|
|
267
258
|
const startMsg1 = sent.filter((m) => m.type === "recording_start").pop();
|
|
@@ -271,11 +262,11 @@ describe("handleRecordingRestart", () => {
|
|
|
271
262
|
status: "started",
|
|
272
263
|
operationToken: result1.operationToken,
|
|
273
264
|
};
|
|
274
|
-
recordingHandlers.recording_status(status1,
|
|
265
|
+
recordingHandlers.recording_status(status1, ctx);
|
|
275
266
|
|
|
276
267
|
// Second restart cycle
|
|
277
268
|
sent.length = 0;
|
|
278
|
-
const result2 = handleRecordingRestart(conversationId,
|
|
269
|
+
const result2 = handleRecordingRestart(conversationId, ctx);
|
|
279
270
|
|
|
280
271
|
expect(result1.operationToken).not.toBe(result2.operationToken);
|
|
281
272
|
});
|
|
@@ -291,20 +282,17 @@ describe("restart_cancelled status", () => {
|
|
|
291
282
|
});
|
|
292
283
|
|
|
293
284
|
test('emits restart_cancelled response, never "new recording started"', () => {
|
|
294
|
-
const { ctx, sent
|
|
285
|
+
const { ctx, sent } = createCtx();
|
|
295
286
|
const conversationId = "conv-cancel-1";
|
|
296
|
-
ctx.socketToSession.set(fakeSocket, conversationId);
|
|
297
287
|
|
|
298
288
|
// Start -> restart
|
|
299
289
|
const originalId = handleRecordingStart(
|
|
300
290
|
conversationId,
|
|
301
291
|
undefined,
|
|
302
|
-
fakeSocket,
|
|
303
292
|
ctx,
|
|
304
293
|
);
|
|
305
294
|
const restartResult = handleRecordingRestart(
|
|
306
295
|
conversationId,
|
|
307
|
-
fakeSocket,
|
|
308
296
|
ctx,
|
|
309
297
|
);
|
|
310
298
|
expect(restartResult.initiated).toBe(true);
|
|
@@ -316,7 +304,7 @@ describe("restart_cancelled status", () => {
|
|
|
316
304
|
status: "stopped",
|
|
317
305
|
attachToConversationId: conversationId,
|
|
318
306
|
};
|
|
319
|
-
recordingHandlers.recording_status(stoppedStatus,
|
|
307
|
+
recordingHandlers.recording_status(stoppedStatus, ctx);
|
|
320
308
|
|
|
321
309
|
// Get the new recording ID from the deferred recording_start message
|
|
322
310
|
const startMsg = sent.filter((m) => m.type === "recording_start").pop();
|
|
@@ -330,7 +318,7 @@ describe("restart_cancelled status", () => {
|
|
|
330
318
|
attachToConversationId: conversationId,
|
|
331
319
|
operationToken: restartResult.operationToken,
|
|
332
320
|
};
|
|
333
|
-
recordingHandlers.recording_status(cancelStatus,
|
|
321
|
+
recordingHandlers.recording_status(cancelStatus, ctx);
|
|
334
322
|
|
|
335
323
|
// Should have emitted the cancellation message
|
|
336
324
|
const textDeltas = sent.filter((m) => m.type === "assistant_text_delta");
|
|
@@ -351,19 +339,16 @@ describe("restart_cancelled status", () => {
|
|
|
351
339
|
});
|
|
352
340
|
|
|
353
341
|
test("cleans up restart state on cancel", () => {
|
|
354
|
-
const { ctx, sent
|
|
342
|
+
const { ctx, sent } = createCtx();
|
|
355
343
|
const conversationId = "conv-cancel-cleanup";
|
|
356
|
-
ctx.socketToSession.set(fakeSocket, conversationId);
|
|
357
344
|
|
|
358
345
|
const originalId = handleRecordingStart(
|
|
359
346
|
conversationId,
|
|
360
347
|
undefined,
|
|
361
|
-
fakeSocket,
|
|
362
348
|
ctx,
|
|
363
349
|
);
|
|
364
350
|
const restartResult = handleRecordingRestart(
|
|
365
351
|
conversationId,
|
|
366
|
-
fakeSocket,
|
|
367
352
|
ctx,
|
|
368
353
|
);
|
|
369
354
|
|
|
@@ -377,7 +362,7 @@ describe("restart_cancelled status", () => {
|
|
|
377
362
|
status: "stopped",
|
|
378
363
|
attachToConversationId: conversationId,
|
|
379
364
|
};
|
|
380
|
-
recordingHandlers.recording_status(stoppedStatus,
|
|
365
|
+
recordingHandlers.recording_status(stoppedStatus, ctx);
|
|
381
366
|
|
|
382
367
|
// Still not idle — the new recording has started
|
|
383
368
|
expect(isRecordingIdle()).toBe(false);
|
|
@@ -390,7 +375,7 @@ describe("restart_cancelled status", () => {
|
|
|
390
375
|
attachToConversationId: conversationId,
|
|
391
376
|
operationToken: restartResult.operationToken,
|
|
392
377
|
};
|
|
393
|
-
recordingHandlers.recording_status(cancelStatus,
|
|
378
|
+
recordingHandlers.recording_status(cancelStatus, ctx);
|
|
394
379
|
|
|
395
380
|
// After cancel: truly idle
|
|
396
381
|
expect(isRecordingIdle()).toBe(true);
|
|
@@ -408,20 +393,17 @@ describe("stale completion guard (operation token)", () => {
|
|
|
408
393
|
});
|
|
409
394
|
|
|
410
395
|
test("rejects recording_status with stale operation token", () => {
|
|
411
|
-
const { ctx, sent
|
|
396
|
+
const { ctx, sent } = createCtx();
|
|
412
397
|
const conversationId = "conv-stale-1";
|
|
413
|
-
ctx.socketToSession.set(fakeSocket, conversationId);
|
|
414
398
|
|
|
415
399
|
// Start recording -> restart (creates operation token)
|
|
416
400
|
const originalId = handleRecordingStart(
|
|
417
401
|
conversationId,
|
|
418
402
|
undefined,
|
|
419
|
-
fakeSocket,
|
|
420
403
|
ctx,
|
|
421
404
|
);
|
|
422
405
|
const restartResult = handleRecordingRestart(
|
|
423
406
|
conversationId,
|
|
424
|
-
fakeSocket,
|
|
425
407
|
ctx,
|
|
426
408
|
);
|
|
427
409
|
expect(restartResult.initiated).toBe(true);
|
|
@@ -433,7 +415,7 @@ describe("stale completion guard (operation token)", () => {
|
|
|
433
415
|
status: "stopped",
|
|
434
416
|
attachToConversationId: conversationId,
|
|
435
417
|
};
|
|
436
|
-
recordingHandlers.recording_status(stoppedStatus,
|
|
418
|
+
recordingHandlers.recording_status(stoppedStatus, ctx);
|
|
437
419
|
|
|
438
420
|
const startMsg = sent.filter((m) => m.type === "recording_start").pop();
|
|
439
421
|
sent.length = 0;
|
|
@@ -445,7 +427,7 @@ describe("stale completion guard (operation token)", () => {
|
|
|
445
427
|
status: "started",
|
|
446
428
|
operationToken: "old-stale-token-from-previous-cycle",
|
|
447
429
|
};
|
|
448
|
-
recordingHandlers.recording_status(staleStatus,
|
|
430
|
+
recordingHandlers.recording_status(staleStatus, ctx);
|
|
449
431
|
|
|
450
432
|
// Should have been rejected — no "started" confirmation messages
|
|
451
433
|
const textDeltas = sent.filter((m) => m.type === "assistant_text_delta");
|
|
@@ -456,19 +438,16 @@ describe("stale completion guard (operation token)", () => {
|
|
|
456
438
|
});
|
|
457
439
|
|
|
458
440
|
test("accepts recording_status with matching operation token", () => {
|
|
459
|
-
const { ctx, sent
|
|
441
|
+
const { ctx, sent } = createCtx();
|
|
460
442
|
const conversationId = "conv-matching-1";
|
|
461
|
-
ctx.socketToSession.set(fakeSocket, conversationId);
|
|
462
443
|
|
|
463
444
|
const originalId = handleRecordingStart(
|
|
464
445
|
conversationId,
|
|
465
446
|
undefined,
|
|
466
|
-
fakeSocket,
|
|
467
447
|
ctx,
|
|
468
448
|
);
|
|
469
449
|
const restartResult = handleRecordingRestart(
|
|
470
450
|
conversationId,
|
|
471
|
-
fakeSocket,
|
|
472
451
|
ctx,
|
|
473
452
|
);
|
|
474
453
|
|
|
@@ -479,7 +458,7 @@ describe("stale completion guard (operation token)", () => {
|
|
|
479
458
|
status: "stopped",
|
|
480
459
|
attachToConversationId: conversationId,
|
|
481
460
|
};
|
|
482
|
-
recordingHandlers.recording_status(stoppedStatus,
|
|
461
|
+
recordingHandlers.recording_status(stoppedStatus, ctx);
|
|
483
462
|
|
|
484
463
|
const startMsg = sent.filter((m) => m.type === "recording_start").pop();
|
|
485
464
|
|
|
@@ -490,22 +469,20 @@ describe("stale completion guard (operation token)", () => {
|
|
|
490
469
|
status: "started",
|
|
491
470
|
operationToken: restartResult.operationToken,
|
|
492
471
|
};
|
|
493
|
-
recordingHandlers.recording_status(validStatus,
|
|
472
|
+
recordingHandlers.recording_status(validStatus, ctx);
|
|
494
473
|
|
|
495
474
|
// Should have been accepted — restart token cleared
|
|
496
475
|
expect(getActiveRestartToken()).toBeNull();
|
|
497
476
|
});
|
|
498
477
|
|
|
499
478
|
test("allows tokenless recording_status during active restart (old recording ack)", async () => {
|
|
500
|
-
const { ctx, sent
|
|
479
|
+
const { ctx, sent } = createCtx();
|
|
501
480
|
const conversationId = "conv-tokenless-1";
|
|
502
|
-
ctx.socketToSession.set(fakeSocket, conversationId);
|
|
503
481
|
|
|
504
482
|
// Start recording -> restart (creates operation token)
|
|
505
|
-
handleRecordingStart(conversationId, undefined,
|
|
483
|
+
handleRecordingStart(conversationId, undefined, ctx);
|
|
506
484
|
const restartResult = handleRecordingRestart(
|
|
507
485
|
conversationId,
|
|
508
|
-
fakeSocket,
|
|
509
486
|
ctx,
|
|
510
487
|
);
|
|
511
488
|
expect(restartResult.initiated).toBe(true);
|
|
@@ -525,7 +502,7 @@ describe("stale completion guard (operation token)", () => {
|
|
|
525
502
|
attachToConversationId: conversationId,
|
|
526
503
|
// No operationToken — from old recording, should be allowed
|
|
527
504
|
};
|
|
528
|
-
await recordingHandlers.recording_status(tokenlessStatus,
|
|
505
|
+
await recordingHandlers.recording_status(tokenlessStatus, ctx);
|
|
529
506
|
|
|
530
507
|
// Should have triggered the deferred restart start
|
|
531
508
|
const newStartMsgs = sent.filter((m) => m.type === "recording_start");
|
|
@@ -539,21 +516,18 @@ describe("stale completion guard (operation token)", () => {
|
|
|
539
516
|
});
|
|
540
517
|
|
|
541
518
|
test("no ghost state after restart stop/start handoff", () => {
|
|
542
|
-
const { ctx, sent
|
|
519
|
+
const { ctx, sent } = createCtx();
|
|
543
520
|
const conversationId = "conv-ghost-1";
|
|
544
|
-
ctx.socketToSession.set(fakeSocket, conversationId);
|
|
545
521
|
|
|
546
522
|
const originalId = handleRecordingStart(
|
|
547
523
|
conversationId,
|
|
548
524
|
undefined,
|
|
549
|
-
fakeSocket,
|
|
550
525
|
ctx,
|
|
551
526
|
);
|
|
552
527
|
|
|
553
528
|
// Restart sends stop and defers start until stop-ack
|
|
554
529
|
const restartResult = handleRecordingRestart(
|
|
555
530
|
conversationId,
|
|
556
|
-
fakeSocket,
|
|
557
531
|
ctx,
|
|
558
532
|
);
|
|
559
533
|
expect(restartResult.initiated).toBe(true);
|
|
@@ -565,7 +539,7 @@ describe("stale completion guard (operation token)", () => {
|
|
|
565
539
|
status: "stopped",
|
|
566
540
|
attachToConversationId: conversationId,
|
|
567
541
|
};
|
|
568
|
-
recordingHandlers.recording_status(stoppedStatus,
|
|
542
|
+
recordingHandlers.recording_status(stoppedStatus, ctx);
|
|
569
543
|
|
|
570
544
|
// The new recording should be active (not the old one)
|
|
571
545
|
const startMsgs = sent.filter((m) => m.type === "recording_start");
|
|
@@ -585,14 +559,12 @@ describe("handleRecordingPause", () => {
|
|
|
585
559
|
});
|
|
586
560
|
|
|
587
561
|
test("sends recording_pause for active recording", () => {
|
|
588
|
-
const { ctx, sent
|
|
562
|
+
const { ctx, sent } = createCtx();
|
|
589
563
|
const conversationId = "conv-pause-1";
|
|
590
|
-
ctx.socketToSession.set(fakeSocket, conversationId);
|
|
591
564
|
|
|
592
565
|
const recordingId = handleRecordingStart(
|
|
593
566
|
conversationId,
|
|
594
567
|
undefined,
|
|
595
|
-
fakeSocket,
|
|
596
568
|
ctx,
|
|
597
569
|
);
|
|
598
570
|
expect(recordingId).not.toBeNull();
|
|
@@ -614,11 +586,10 @@ describe("handleRecordingPause", () => {
|
|
|
614
586
|
});
|
|
615
587
|
|
|
616
588
|
test("resolves to globally active recording from different conversation", () => {
|
|
617
|
-
const { ctx, sent
|
|
589
|
+
const { ctx, sent } = createCtx();
|
|
618
590
|
const convA = "conv-owner-pause";
|
|
619
|
-
ctx.socketToSession.set(fakeSocket, convA);
|
|
620
591
|
|
|
621
|
-
const recordingId = handleRecordingStart(convA, undefined,
|
|
592
|
+
const recordingId = handleRecordingStart(convA, undefined, ctx);
|
|
622
593
|
sent.length = 0;
|
|
623
594
|
|
|
624
595
|
const result = handleRecordingPause("conv-other-pause", ctx);
|
|
@@ -632,14 +603,12 @@ describe("handleRecordingResume", () => {
|
|
|
632
603
|
});
|
|
633
604
|
|
|
634
605
|
test("sends recording_resume for active recording", () => {
|
|
635
|
-
const { ctx, sent
|
|
606
|
+
const { ctx, sent } = createCtx();
|
|
636
607
|
const conversationId = "conv-resume-1";
|
|
637
|
-
ctx.socketToSession.set(fakeSocket, conversationId);
|
|
638
608
|
|
|
639
609
|
const recordingId = handleRecordingStart(
|
|
640
610
|
conversationId,
|
|
641
611
|
undefined,
|
|
642
|
-
fakeSocket,
|
|
643
612
|
ctx,
|
|
644
613
|
);
|
|
645
614
|
expect(recordingId).not.toBeNull();
|
|
@@ -673,18 +642,17 @@ describe("isRecordingIdle", () => {
|
|
|
673
642
|
});
|
|
674
643
|
|
|
675
644
|
test("returns false when recording is active", () => {
|
|
676
|
-
const { ctx
|
|
677
|
-
handleRecordingStart("conv-idle-1", undefined,
|
|
645
|
+
const { ctx } = createCtx();
|
|
646
|
+
handleRecordingStart("conv-idle-1", undefined, ctx);
|
|
678
647
|
expect(isRecordingIdle()).toBe(false);
|
|
679
648
|
});
|
|
680
649
|
|
|
681
650
|
test("returns false when mid-restart (between stop-ack and start confirmation)", () => {
|
|
682
|
-
const { ctx
|
|
651
|
+
const { ctx } = createCtx();
|
|
683
652
|
const conversationId = "conv-idle-restart";
|
|
684
|
-
ctx.socketToSession.set(fakeSocket, conversationId);
|
|
685
653
|
|
|
686
|
-
handleRecordingStart(conversationId, undefined,
|
|
687
|
-
handleRecordingRestart(conversationId,
|
|
654
|
+
handleRecordingStart(conversationId, undefined, ctx);
|
|
655
|
+
handleRecordingRestart(conversationId, ctx);
|
|
688
656
|
|
|
689
657
|
// Mid-restart: the old recording maps are still present AND there's a
|
|
690
658
|
// pending restart, so the system is not idle
|
|
@@ -692,19 +660,16 @@ describe("isRecordingIdle", () => {
|
|
|
692
660
|
});
|
|
693
661
|
|
|
694
662
|
test("returns true after restart completes", () => {
|
|
695
|
-
const { ctx, sent
|
|
663
|
+
const { ctx, sent } = createCtx();
|
|
696
664
|
const conversationId = "conv-idle-complete";
|
|
697
|
-
ctx.socketToSession.set(fakeSocket, conversationId);
|
|
698
665
|
|
|
699
666
|
const originalId = handleRecordingStart(
|
|
700
667
|
conversationId,
|
|
701
668
|
undefined,
|
|
702
|
-
fakeSocket,
|
|
703
669
|
ctx,
|
|
704
670
|
);
|
|
705
671
|
const restartResult = handleRecordingRestart(
|
|
706
672
|
conversationId,
|
|
707
|
-
fakeSocket,
|
|
708
673
|
ctx,
|
|
709
674
|
);
|
|
710
675
|
|
|
@@ -715,7 +680,7 @@ describe("isRecordingIdle", () => {
|
|
|
715
680
|
status: "stopped",
|
|
716
681
|
attachToConversationId: conversationId,
|
|
717
682
|
};
|
|
718
|
-
recordingHandlers.recording_status(stoppedStatus,
|
|
683
|
+
recordingHandlers.recording_status(stoppedStatus, ctx);
|
|
719
684
|
|
|
720
685
|
// Simulate the new recording starting
|
|
721
686
|
const startMsg = sent.filter((m) => m.type === "recording_start").pop();
|
|
@@ -725,7 +690,7 @@ describe("isRecordingIdle", () => {
|
|
|
725
690
|
status: "started",
|
|
726
691
|
operationToken: restartResult.operationToken,
|
|
727
692
|
};
|
|
728
|
-
recordingHandlers.recording_status(startedStatus,
|
|
693
|
+
recordingHandlers.recording_status(startedStatus, ctx);
|
|
729
694
|
|
|
730
695
|
// Restart is complete, but recording is still active
|
|
731
696
|
expect(getActiveRestartToken()).toBeNull();
|
|
@@ -742,22 +707,20 @@ describe("executeRecordingIntent — restart/pause/resume", () => {
|
|
|
742
707
|
});
|
|
743
708
|
|
|
744
709
|
test("restart_only executes actual restart (deferred start)", () => {
|
|
745
|
-
const { ctx, sent
|
|
710
|
+
const { ctx, sent } = createCtx();
|
|
746
711
|
const conversationId = "conv-exec-restart";
|
|
747
|
-
ctx.socketToSession.set(fakeSocket, conversationId);
|
|
748
712
|
|
|
749
713
|
// Start a recording first
|
|
750
714
|
const originalId = handleRecordingStart(
|
|
751
715
|
conversationId,
|
|
752
716
|
undefined,
|
|
753
|
-
fakeSocket,
|
|
754
717
|
ctx,
|
|
755
718
|
);
|
|
756
719
|
sent.length = 0;
|
|
757
720
|
|
|
758
721
|
const result = executeRecordingIntent(
|
|
759
722
|
{ kind: "restart_only" },
|
|
760
|
-
{ conversationId,
|
|
723
|
+
{ conversationId, ctx },
|
|
761
724
|
);
|
|
762
725
|
|
|
763
726
|
expect(result.handled).toBe(true);
|
|
@@ -776,7 +739,7 @@ describe("executeRecordingIntent — restart/pause/resume", () => {
|
|
|
776
739
|
status: "stopped",
|
|
777
740
|
attachToConversationId: conversationId,
|
|
778
741
|
};
|
|
779
|
-
recordingHandlers.recording_status(stoppedStatus,
|
|
742
|
+
recordingHandlers.recording_status(stoppedStatus, ctx);
|
|
780
743
|
|
|
781
744
|
// NOW the deferred start should have been sent
|
|
782
745
|
const startMsgsAfterAck = sent.filter((m) => m.type === "recording_start");
|
|
@@ -784,11 +747,11 @@ describe("executeRecordingIntent — restart/pause/resume", () => {
|
|
|
784
747
|
});
|
|
785
748
|
|
|
786
749
|
test('restart_only returns "no active recording" when idle', () => {
|
|
787
|
-
const { ctx
|
|
750
|
+
const { ctx } = createCtx();
|
|
788
751
|
|
|
789
752
|
const result = executeRecordingIntent(
|
|
790
753
|
{ kind: "restart_only" },
|
|
791
|
-
{ conversationId: "conv-no-rec",
|
|
754
|
+
{ conversationId: "conv-no-rec", ctx },
|
|
792
755
|
);
|
|
793
756
|
|
|
794
757
|
expect(result.handled).toBe(true);
|
|
@@ -796,11 +759,11 @@ describe("executeRecordingIntent — restart/pause/resume", () => {
|
|
|
796
759
|
});
|
|
797
760
|
|
|
798
761
|
test("restart_with_remainder returns deferred restart", () => {
|
|
799
|
-
const { ctx
|
|
762
|
+
const { ctx } = createCtx();
|
|
800
763
|
|
|
801
764
|
const result = executeRecordingIntent(
|
|
802
765
|
{ kind: "restart_with_remainder", remainder: "do something else" },
|
|
803
|
-
{ conversationId: "conv-rem",
|
|
766
|
+
{ conversationId: "conv-rem", ctx },
|
|
804
767
|
);
|
|
805
768
|
|
|
806
769
|
expect(result.handled).toBe(false);
|
|
@@ -809,16 +772,15 @@ describe("executeRecordingIntent — restart/pause/resume", () => {
|
|
|
809
772
|
});
|
|
810
773
|
|
|
811
774
|
test("pause_only executes actual pause", () => {
|
|
812
|
-
const { ctx, sent
|
|
775
|
+
const { ctx, sent } = createCtx();
|
|
813
776
|
const conversationId = "conv-exec-pause";
|
|
814
|
-
ctx.socketToSession.set(fakeSocket, conversationId);
|
|
815
777
|
|
|
816
|
-
handleRecordingStart(conversationId, undefined,
|
|
778
|
+
handleRecordingStart(conversationId, undefined, ctx);
|
|
817
779
|
sent.length = 0;
|
|
818
780
|
|
|
819
781
|
const result = executeRecordingIntent(
|
|
820
782
|
{ kind: "pause_only" },
|
|
821
|
-
{ conversationId,
|
|
783
|
+
{ conversationId, ctx },
|
|
822
784
|
);
|
|
823
785
|
|
|
824
786
|
expect(result.handled).toBe(true);
|
|
@@ -829,11 +791,11 @@ describe("executeRecordingIntent — restart/pause/resume", () => {
|
|
|
829
791
|
});
|
|
830
792
|
|
|
831
793
|
test('pause_only returns "no active recording" when idle', () => {
|
|
832
|
-
const { ctx
|
|
794
|
+
const { ctx } = createCtx();
|
|
833
795
|
|
|
834
796
|
const result = executeRecordingIntent(
|
|
835
797
|
{ kind: "pause_only" },
|
|
836
|
-
{ conversationId: "conv-no-rec",
|
|
798
|
+
{ conversationId: "conv-no-rec", ctx },
|
|
837
799
|
);
|
|
838
800
|
|
|
839
801
|
expect(result.handled).toBe(true);
|
|
@@ -841,16 +803,15 @@ describe("executeRecordingIntent — restart/pause/resume", () => {
|
|
|
841
803
|
});
|
|
842
804
|
|
|
843
805
|
test("resume_only executes actual resume", () => {
|
|
844
|
-
const { ctx, sent
|
|
806
|
+
const { ctx, sent } = createCtx();
|
|
845
807
|
const conversationId = "conv-exec-resume";
|
|
846
|
-
ctx.socketToSession.set(fakeSocket, conversationId);
|
|
847
808
|
|
|
848
|
-
handleRecordingStart(conversationId, undefined,
|
|
809
|
+
handleRecordingStart(conversationId, undefined, ctx);
|
|
849
810
|
sent.length = 0;
|
|
850
811
|
|
|
851
812
|
const result = executeRecordingIntent(
|
|
852
813
|
{ kind: "resume_only" },
|
|
853
|
-
{ conversationId,
|
|
814
|
+
{ conversationId, ctx },
|
|
854
815
|
);
|
|
855
816
|
|
|
856
817
|
expect(result.handled).toBe(true);
|
|
@@ -861,11 +822,11 @@ describe("executeRecordingIntent — restart/pause/resume", () => {
|
|
|
861
822
|
});
|
|
862
823
|
|
|
863
824
|
test('resume_only returns "no active recording" when idle', () => {
|
|
864
|
-
const { ctx
|
|
825
|
+
const { ctx } = createCtx();
|
|
865
826
|
|
|
866
827
|
const result = executeRecordingIntent(
|
|
867
828
|
{ kind: "resume_only" },
|
|
868
|
-
{ conversationId: "conv-no-rec",
|
|
829
|
+
{ conversationId: "conv-no-rec", ctx },
|
|
869
830
|
);
|
|
870
831
|
|
|
871
832
|
expect(result.handled).toBe(true);
|
|
@@ -881,13 +842,12 @@ describe("recording_status paused/resumed", () => {
|
|
|
881
842
|
});
|
|
882
843
|
|
|
883
844
|
test("handles paused status without error", () => {
|
|
884
|
-
const { ctx
|
|
845
|
+
const { ctx } = createCtx();
|
|
885
846
|
const conversationId = "conv-status-paused";
|
|
886
847
|
|
|
887
848
|
const recordingId = handleRecordingStart(
|
|
888
849
|
conversationId,
|
|
889
850
|
undefined,
|
|
890
|
-
fakeSocket,
|
|
891
851
|
ctx,
|
|
892
852
|
);
|
|
893
853
|
expect(recordingId).not.toBeNull();
|
|
@@ -899,18 +859,17 @@ describe("recording_status paused/resumed", () => {
|
|
|
899
859
|
};
|
|
900
860
|
|
|
901
861
|
expect(() => {
|
|
902
|
-
recordingHandlers.recording_status(statusMsg,
|
|
862
|
+
recordingHandlers.recording_status(statusMsg, ctx);
|
|
903
863
|
}).not.toThrow();
|
|
904
864
|
});
|
|
905
865
|
|
|
906
866
|
test("handles resumed status without error", () => {
|
|
907
|
-
const { ctx
|
|
867
|
+
const { ctx } = createCtx();
|
|
908
868
|
const conversationId = "conv-status-resumed";
|
|
909
869
|
|
|
910
870
|
const recordingId = handleRecordingStart(
|
|
911
871
|
conversationId,
|
|
912
872
|
undefined,
|
|
913
|
-
fakeSocket,
|
|
914
873
|
ctx,
|
|
915
874
|
);
|
|
916
875
|
expect(recordingId).not.toBeNull();
|
|
@@ -922,7 +881,7 @@ describe("recording_status paused/resumed", () => {
|
|
|
922
881
|
};
|
|
923
882
|
|
|
924
883
|
expect(() => {
|
|
925
|
-
recordingHandlers.recording_status(statusMsg,
|
|
884
|
+
recordingHandlers.recording_status(statusMsg, ctx);
|
|
926
885
|
}).not.toThrow();
|
|
927
886
|
});
|
|
928
887
|
});
|
|
@@ -937,17 +896,15 @@ describe("failure during restart", () => {
|
|
|
937
896
|
});
|
|
938
897
|
|
|
939
898
|
test("failed status during restart clears pending restart state (old recording fails)", () => {
|
|
940
|
-
const { ctx, sent
|
|
899
|
+
const { ctx, sent } = createCtx();
|
|
941
900
|
const conversationId = "conv-fail-restart";
|
|
942
|
-
ctx.socketToSession.set(fakeSocket, conversationId);
|
|
943
901
|
|
|
944
902
|
const originalId = handleRecordingStart(
|
|
945
903
|
conversationId,
|
|
946
904
|
undefined,
|
|
947
|
-
fakeSocket,
|
|
948
905
|
ctx,
|
|
949
906
|
);
|
|
950
|
-
handleRecordingRestart(conversationId,
|
|
907
|
+
handleRecordingRestart(conversationId, ctx);
|
|
951
908
|
sent.length = 0;
|
|
952
909
|
|
|
953
910
|
// Simulate the old recording failing to stop (before stop-ack)
|
|
@@ -958,7 +915,7 @@ describe("failure during restart", () => {
|
|
|
958
915
|
error: "Permission denied",
|
|
959
916
|
attachToConversationId: conversationId,
|
|
960
917
|
};
|
|
961
|
-
recordingHandlers.recording_status(failedStatus,
|
|
918
|
+
recordingHandlers.recording_status(failedStatus, ctx);
|
|
962
919
|
|
|
963
920
|
// Restart state and deferred restart should be cleaned up
|
|
964
921
|
expect(getActiveRestartToken()).toBeNull();
|
|
@@ -966,19 +923,16 @@ describe("failure during restart", () => {
|
|
|
966
923
|
});
|
|
967
924
|
|
|
968
925
|
test("failed status during restart clears state (new recording fails after deferred start)", () => {
|
|
969
|
-
const { ctx, sent
|
|
926
|
+
const { ctx, sent } = createCtx();
|
|
970
927
|
const conversationId = "conv-fail-restart-new";
|
|
971
|
-
ctx.socketToSession.set(fakeSocket, conversationId);
|
|
972
928
|
|
|
973
929
|
const originalId = handleRecordingStart(
|
|
974
930
|
conversationId,
|
|
975
931
|
undefined,
|
|
976
|
-
fakeSocket,
|
|
977
932
|
ctx,
|
|
978
933
|
);
|
|
979
934
|
const restartResult = handleRecordingRestart(
|
|
980
935
|
conversationId,
|
|
981
|
-
fakeSocket,
|
|
982
936
|
ctx,
|
|
983
937
|
);
|
|
984
938
|
|
|
@@ -989,7 +943,7 @@ describe("failure during restart", () => {
|
|
|
989
943
|
status: "stopped",
|
|
990
944
|
attachToConversationId: conversationId,
|
|
991
945
|
};
|
|
992
|
-
recordingHandlers.recording_status(stoppedStatus,
|
|
946
|
+
recordingHandlers.recording_status(stoppedStatus, ctx);
|
|
993
947
|
|
|
994
948
|
const startMsg = sent.filter((m) => m.type === "recording_start").pop();
|
|
995
949
|
sent.length = 0;
|
|
@@ -1003,7 +957,7 @@ describe("failure during restart", () => {
|
|
|
1003
957
|
attachToConversationId: conversationId,
|
|
1004
958
|
operationToken: restartResult.operationToken,
|
|
1005
959
|
};
|
|
1006
|
-
recordingHandlers.recording_status(failedStatus,
|
|
960
|
+
recordingHandlers.recording_status(failedStatus, ctx);
|
|
1007
961
|
|
|
1008
962
|
// Restart state should be cleaned up
|
|
1009
963
|
expect(getActiveRestartToken()).toBeNull();
|
|
@@ -1019,15 +973,14 @@ describe("start_and_stop_only fallback to plain start when idle", () => {
|
|
|
1019
973
|
});
|
|
1020
974
|
|
|
1021
975
|
test("falls back to handleRecordingStart when no active recording", () => {
|
|
1022
|
-
const { ctx, sent
|
|
976
|
+
const { ctx, sent } = createCtx();
|
|
1023
977
|
const conversationId = "conv-stop-start-idle";
|
|
1024
|
-
ctx.socketToSession.set(fakeSocket, conversationId);
|
|
1025
978
|
|
|
1026
979
|
// No recording is active — start_and_stop_only should fall back to a
|
|
1027
980
|
// plain start rather than returning "No active recording to restart."
|
|
1028
981
|
const result = executeRecordingIntent(
|
|
1029
982
|
{ kind: "start_and_stop_only" },
|
|
1030
|
-
{ conversationId,
|
|
983
|
+
{ conversationId, ctx },
|
|
1031
984
|
);
|
|
1032
985
|
|
|
1033
986
|
expect(result.handled).toBe(true);
|
|
@@ -1042,15 +995,13 @@ describe("start_and_stop_only fallback to plain start when idle", () => {
|
|
|
1042
995
|
});
|
|
1043
996
|
|
|
1044
997
|
test("goes through restart when a recording is active (deferred start)", () => {
|
|
1045
|
-
const { ctx, sent
|
|
998
|
+
const { ctx, sent } = createCtx();
|
|
1046
999
|
const conversationId = "conv-stop-start-active";
|
|
1047
|
-
ctx.socketToSession.set(fakeSocket, conversationId);
|
|
1048
1000
|
|
|
1049
1001
|
// Start a recording first
|
|
1050
1002
|
const originalId = handleRecordingStart(
|
|
1051
1003
|
conversationId,
|
|
1052
1004
|
undefined,
|
|
1053
|
-
fakeSocket,
|
|
1054
1005
|
ctx,
|
|
1055
1006
|
);
|
|
1056
1007
|
expect(originalId).not.toBeNull();
|
|
@@ -1059,7 +1010,7 @@ describe("start_and_stop_only fallback to plain start when idle", () => {
|
|
|
1059
1010
|
// Now start_and_stop_only should go through handleRecordingRestart
|
|
1060
1011
|
const result = executeRecordingIntent(
|
|
1061
1012
|
{ kind: "start_and_stop_only" },
|
|
1062
|
-
{ conversationId,
|
|
1013
|
+
{ conversationId, ctx },
|
|
1063
1014
|
);
|
|
1064
1015
|
|
|
1065
1016
|
expect(result.handled).toBe(true);
|
|
@@ -1081,7 +1032,7 @@ describe("start_and_stop_only fallback to plain start when idle", () => {
|
|
|
1081
1032
|
status: "stopped",
|
|
1082
1033
|
attachToConversationId: conversationId,
|
|
1083
1034
|
};
|
|
1084
|
-
recordingHandlers.recording_status(stoppedStatus,
|
|
1035
|
+
recordingHandlers.recording_status(stoppedStatus, ctx);
|
|
1085
1036
|
|
|
1086
1037
|
// NOW the deferred start should have been sent
|
|
1087
1038
|
const startMsgsAfterAck = sent.filter((m) => m.type === "recording_start");
|
|
@@ -1097,12 +1048,12 @@ describe("start_and_stop_with_remainder fallback to plain start when idle", () =
|
|
|
1097
1048
|
});
|
|
1098
1049
|
|
|
1099
1050
|
test("sets pendingStart (not pendingRestart) when no active recording", () => {
|
|
1100
|
-
const { ctx
|
|
1051
|
+
const { ctx } = createCtx();
|
|
1101
1052
|
const conversationId = "conv-rem-idle";
|
|
1102
1053
|
|
|
1103
1054
|
const result = executeRecordingIntent(
|
|
1104
1055
|
{ kind: "start_and_stop_with_remainder", remainder: "do something" },
|
|
1105
|
-
{ conversationId,
|
|
1056
|
+
{ conversationId, ctx },
|
|
1106
1057
|
);
|
|
1107
1058
|
|
|
1108
1059
|
expect(result.handled).toBe(false);
|
|
@@ -1112,16 +1063,15 @@ describe("start_and_stop_with_remainder fallback to plain start when idle", () =
|
|
|
1112
1063
|
});
|
|
1113
1064
|
|
|
1114
1065
|
test("sets pendingRestart when a recording is active", () => {
|
|
1115
|
-
const { ctx
|
|
1066
|
+
const { ctx } = createCtx();
|
|
1116
1067
|
const conversationId = "conv-rem-active";
|
|
1117
|
-
ctx.socketToSession.set(fakeSocket, conversationId);
|
|
1118
1068
|
|
|
1119
1069
|
// Start a recording first
|
|
1120
|
-
handleRecordingStart(conversationId, undefined,
|
|
1070
|
+
handleRecordingStart(conversationId, undefined, ctx);
|
|
1121
1071
|
|
|
1122
1072
|
const result = executeRecordingIntent(
|
|
1123
1073
|
{ kind: "start_and_stop_with_remainder", remainder: "do something" },
|
|
1124
|
-
{ conversationId,
|
|
1074
|
+
{ conversationId, ctx },
|
|
1125
1075
|
);
|
|
1126
1076
|
|
|
1127
1077
|
expect(result.handled).toBe(false);
|
|
@@ -1141,14 +1091,13 @@ describe("deferred restart prevents race condition", () => {
|
|
|
1141
1091
|
});
|
|
1142
1092
|
|
|
1143
1093
|
test("recording_start is NOT sent until client acks the stop", () => {
|
|
1144
|
-
const { ctx, sent
|
|
1094
|
+
const { ctx, sent } = createCtx();
|
|
1145
1095
|
const conversationId = "conv-deferred-race";
|
|
1146
|
-
ctx.socketToSession.set(fakeSocket, conversationId);
|
|
1147
1096
|
|
|
1148
|
-
handleRecordingStart(conversationId, undefined,
|
|
1097
|
+
handleRecordingStart(conversationId, undefined, ctx);
|
|
1149
1098
|
sent.length = 0;
|
|
1150
1099
|
|
|
1151
|
-
handleRecordingRestart(conversationId,
|
|
1100
|
+
handleRecordingRestart(conversationId, ctx);
|
|
1152
1101
|
|
|
1153
1102
|
// Only recording_stop should have been sent — no recording_start yet
|
|
1154
1103
|
expect(sent.filter((m) => m.type === "recording_stop")).toHaveLength(1);
|
|
@@ -1160,12 +1109,11 @@ describe("deferred restart prevents race condition", () => {
|
|
|
1160
1109
|
|
|
1161
1110
|
test("stop-ack timeout cleans up deferred restart state", () => {
|
|
1162
1111
|
// This test uses a real timer via bun's jest-compatible API
|
|
1163
|
-
const { ctx
|
|
1112
|
+
const { ctx } = createCtx();
|
|
1164
1113
|
const conversationId = "conv-deferred-timeout";
|
|
1165
|
-
ctx.socketToSession.set(fakeSocket, conversationId);
|
|
1166
1114
|
|
|
1167
|
-
handleRecordingStart(conversationId, undefined,
|
|
1168
|
-
handleRecordingRestart(conversationId,
|
|
1115
|
+
handleRecordingStart(conversationId, undefined, ctx);
|
|
1116
|
+
handleRecordingRestart(conversationId, ctx);
|
|
1169
1117
|
|
|
1170
1118
|
// Mid-restart: not idle
|
|
1171
1119
|
expect(isRecordingIdle()).toBe(false);
|
|
@@ -1176,18 +1124,17 @@ describe("deferred restart prevents race condition", () => {
|
|
|
1176
1124
|
});
|
|
1177
1125
|
|
|
1178
1126
|
test("cross-conversation restart: conversation B restarts recording owned by A", () => {
|
|
1179
|
-
const { ctx, sent
|
|
1127
|
+
const { ctx, sent } = createCtx();
|
|
1180
1128
|
const convA = "conv-owner-A";
|
|
1181
1129
|
const convB = "conv-requester-B";
|
|
1182
|
-
ctx.socketToSession.set(fakeSocket, convA);
|
|
1183
1130
|
|
|
1184
1131
|
// Conversation A starts a recording
|
|
1185
|
-
const originalId = handleRecordingStart(convA, undefined,
|
|
1132
|
+
const originalId = handleRecordingStart(convA, undefined, ctx);
|
|
1186
1133
|
expect(originalId).not.toBeNull();
|
|
1187
1134
|
sent.length = 0;
|
|
1188
1135
|
|
|
1189
1136
|
// Conversation B requests a restart (cross-conversation via global fallback)
|
|
1190
|
-
const result = handleRecordingRestart(convB,
|
|
1137
|
+
const result = handleRecordingRestart(convB, ctx);
|
|
1191
1138
|
expect(result.initiated).toBe(true);
|
|
1192
1139
|
expect(result.operationToken).toBeTruthy();
|
|
1193
1140
|
|
|
@@ -1203,7 +1150,7 @@ describe("deferred restart prevents race condition", () => {
|
|
|
1203
1150
|
status: "stopped",
|
|
1204
1151
|
attachToConversationId: convA,
|
|
1205
1152
|
};
|
|
1206
|
-
recordingHandlers.recording_status(stoppedStatus,
|
|
1153
|
+
recordingHandlers.recording_status(stoppedStatus, ctx);
|
|
1207
1154
|
|
|
1208
1155
|
// The deferred recording_start MUST have been triggered even though the
|
|
1209
1156
|
// stopped callback resolved to conversation A (owner), not B (requester).
|
|
@@ -1223,7 +1170,7 @@ describe("deferred restart prevents race condition", () => {
|
|
|
1223
1170
|
operationToken: result.operationToken,
|
|
1224
1171
|
attachToConversationId: convB,
|
|
1225
1172
|
};
|
|
1226
|
-
recordingHandlers.recording_status(startedStatus,
|
|
1173
|
+
recordingHandlers.recording_status(startedStatus, ctx);
|
|
1227
1174
|
|
|
1228
1175
|
// Restart cycle must be fully complete: activeRestartToken cleared
|
|
1229
1176
|
expect(getActiveRestartToken()).toBeNull();
|
|
@@ -1239,20 +1186,18 @@ describe("deferred restart prevents race condition", () => {
|
|
|
1239
1186
|
status: "stopped",
|
|
1240
1187
|
attachToConversationId: convB,
|
|
1241
1188
|
};
|
|
1242
|
-
recordingHandlers.recording_status(newStoppedStatus,
|
|
1189
|
+
recordingHandlers.recording_status(newStoppedStatus, ctx);
|
|
1243
1190
|
|
|
1244
1191
|
expect(isRecordingIdle()).toBe(true);
|
|
1245
1192
|
});
|
|
1246
1193
|
|
|
1247
1194
|
test("normal stop (non-restart) does not trigger deferred start", () => {
|
|
1248
|
-
const { ctx, sent
|
|
1195
|
+
const { ctx, sent } = createCtx();
|
|
1249
1196
|
const conversationId = "conv-normal-stop";
|
|
1250
|
-
ctx.socketToSession.set(fakeSocket, conversationId);
|
|
1251
1197
|
|
|
1252
1198
|
const recordingId = handleRecordingStart(
|
|
1253
1199
|
conversationId,
|
|
1254
1200
|
undefined,
|
|
1255
|
-
fakeSocket,
|
|
1256
1201
|
ctx,
|
|
1257
1202
|
);
|
|
1258
1203
|
expect(recordingId).not.toBeNull();
|
|
@@ -1268,7 +1213,7 @@ describe("deferred restart prevents race condition", () => {
|
|
|
1268
1213
|
status: "stopped",
|
|
1269
1214
|
attachToConversationId: conversationId,
|
|
1270
1215
|
};
|
|
1271
|
-
recordingHandlers.recording_status(stoppedStatus,
|
|
1216
|
+
recordingHandlers.recording_status(stoppedStatus, ctx);
|
|
1272
1217
|
|
|
1273
1218
|
// Should NOT have sent a recording_start (no deferred restart pending)
|
|
1274
1219
|
const startMsgs = sent.filter((m) => m.type === "recording_start");
|
|
@@ -1287,15 +1232,13 @@ describe("restart finalization", () => {
|
|
|
1287
1232
|
});
|
|
1288
1233
|
|
|
1289
1234
|
test("publishes previous recording attachment on restart", async () => {
|
|
1290
|
-
const { ctx, sent
|
|
1235
|
+
const { ctx, sent } = createCtx();
|
|
1291
1236
|
const conversationId = "conv-fin-publish";
|
|
1292
|
-
ctx.socketToSession.set(fakeSocket, conversationId);
|
|
1293
1237
|
|
|
1294
1238
|
// Start a recording
|
|
1295
1239
|
const originalId = handleRecordingStart(
|
|
1296
1240
|
conversationId,
|
|
1297
1241
|
undefined,
|
|
1298
|
-
fakeSocket,
|
|
1299
1242
|
ctx,
|
|
1300
1243
|
);
|
|
1301
1244
|
expect(originalId).not.toBeNull();
|
|
@@ -1303,7 +1246,6 @@ describe("restart finalization", () => {
|
|
|
1303
1246
|
// Trigger restart
|
|
1304
1247
|
const restartResult = handleRecordingRestart(
|
|
1305
1248
|
conversationId,
|
|
1306
|
-
fakeSocket,
|
|
1307
1249
|
ctx,
|
|
1308
1250
|
);
|
|
1309
1251
|
expect(restartResult.initiated).toBe(true);
|
|
@@ -1318,7 +1260,7 @@ describe("restart finalization", () => {
|
|
|
1318
1260
|
durationMs: 5000,
|
|
1319
1261
|
attachToConversationId: conversationId,
|
|
1320
1262
|
};
|
|
1321
|
-
await recordingHandlers.recording_status(stoppedStatus,
|
|
1263
|
+
await recordingHandlers.recording_status(stoppedStatus, ctx);
|
|
1322
1264
|
|
|
1323
1265
|
// Verify: a new recording_start IPC was sent (deferred start triggered)
|
|
1324
1266
|
const startMsgs = sent.filter((m) => m.type === "recording_start");
|
|
@@ -1346,22 +1288,19 @@ describe("restart finalization", () => {
|
|
|
1346
1288
|
});
|
|
1347
1289
|
|
|
1348
1290
|
test("restart + picker cancel preserves previous publish", async () => {
|
|
1349
|
-
const { ctx, sent
|
|
1291
|
+
const { ctx, sent } = createCtx();
|
|
1350
1292
|
const conversationId = "conv-fin-cancel-preserve";
|
|
1351
|
-
ctx.socketToSession.set(fakeSocket, conversationId);
|
|
1352
1293
|
|
|
1353
1294
|
// Start a recording
|
|
1354
1295
|
const originalId = handleRecordingStart(
|
|
1355
1296
|
conversationId,
|
|
1356
1297
|
undefined,
|
|
1357
|
-
fakeSocket,
|
|
1358
1298
|
ctx,
|
|
1359
1299
|
);
|
|
1360
1300
|
|
|
1361
1301
|
// Restart
|
|
1362
1302
|
const restartResult = handleRecordingRestart(
|
|
1363
1303
|
conversationId,
|
|
1364
|
-
fakeSocket,
|
|
1365
1304
|
ctx,
|
|
1366
1305
|
);
|
|
1367
1306
|
expect(restartResult.initiated).toBe(true);
|
|
@@ -1375,7 +1314,7 @@ describe("restart finalization", () => {
|
|
|
1375
1314
|
durationMs: 3000,
|
|
1376
1315
|
attachToConversationId: conversationId,
|
|
1377
1316
|
};
|
|
1378
|
-
await recordingHandlers.recording_status(stoppedStatus,
|
|
1317
|
+
await recordingHandlers.recording_status(stoppedStatus, ctx);
|
|
1379
1318
|
|
|
1380
1319
|
// Capture sent messages so far (should include old recording's attachment)
|
|
1381
1320
|
const preCancelTextDeltas = sent.filter(
|
|
@@ -1400,7 +1339,7 @@ describe("restart finalization", () => {
|
|
|
1400
1339
|
attachToConversationId: conversationId,
|
|
1401
1340
|
operationToken: restartResult.operationToken,
|
|
1402
1341
|
};
|
|
1403
|
-
await recordingHandlers.recording_status(cancelStatus,
|
|
1342
|
+
await recordingHandlers.recording_status(cancelStatus, ctx);
|
|
1404
1343
|
|
|
1405
1344
|
// Verify: the old recording's attachment messages are still in sent
|
|
1406
1345
|
const postCancelTextDeltas = sent.filter(
|
|
@@ -1426,22 +1365,19 @@ describe("restart finalization", () => {
|
|
|
1426
1365
|
});
|
|
1427
1366
|
|
|
1428
1367
|
test("emits truthful failure text when previous finalize fails", async () => {
|
|
1429
|
-
const { ctx, sent
|
|
1368
|
+
const { ctx, sent } = createCtx();
|
|
1430
1369
|
const conversationId = "conv-fin-fail-truth";
|
|
1431
|
-
ctx.socketToSession.set(fakeSocket, conversationId);
|
|
1432
1370
|
|
|
1433
1371
|
// Start a recording
|
|
1434
1372
|
const originalId = handleRecordingStart(
|
|
1435
1373
|
conversationId,
|
|
1436
1374
|
undefined,
|
|
1437
|
-
fakeSocket,
|
|
1438
1375
|
ctx,
|
|
1439
1376
|
);
|
|
1440
1377
|
|
|
1441
1378
|
// Restart
|
|
1442
1379
|
const restartResult = handleRecordingRestart(
|
|
1443
1380
|
conversationId,
|
|
1444
|
-
fakeSocket,
|
|
1445
1381
|
ctx,
|
|
1446
1382
|
);
|
|
1447
1383
|
expect(restartResult.initiated).toBe(true);
|
|
@@ -1455,7 +1391,7 @@ describe("restart finalization", () => {
|
|
|
1455
1391
|
// No filePath — recording stopped without producing a file
|
|
1456
1392
|
attachToConversationId: conversationId,
|
|
1457
1393
|
};
|
|
1458
|
-
await recordingHandlers.recording_status(stoppedStatus,
|
|
1394
|
+
await recordingHandlers.recording_status(stoppedStatus, ctx);
|
|
1459
1395
|
|
|
1460
1396
|
// Verify: error message text is sent (not "Screen recording complete")
|
|
1461
1397
|
const textDeltas = sent.filter((m) => m.type === "assistant_text_delta");
|
|
@@ -1479,22 +1415,19 @@ describe("restart finalization", () => {
|
|
|
1479
1415
|
});
|
|
1480
1416
|
|
|
1481
1417
|
test("preserves previous attachment when new start fails", async () => {
|
|
1482
|
-
const { ctx, sent
|
|
1418
|
+
const { ctx, sent } = createCtx();
|
|
1483
1419
|
const conversationId = "conv-fin-new-fail";
|
|
1484
|
-
ctx.socketToSession.set(fakeSocket, conversationId);
|
|
1485
1420
|
|
|
1486
1421
|
// Start a recording
|
|
1487
1422
|
const originalId = handleRecordingStart(
|
|
1488
1423
|
conversationId,
|
|
1489
1424
|
undefined,
|
|
1490
|
-
fakeSocket,
|
|
1491
1425
|
ctx,
|
|
1492
1426
|
);
|
|
1493
1427
|
|
|
1494
1428
|
// Restart
|
|
1495
1429
|
const restartResult = handleRecordingRestart(
|
|
1496
1430
|
conversationId,
|
|
1497
|
-
fakeSocket,
|
|
1498
1431
|
ctx,
|
|
1499
1432
|
);
|
|
1500
1433
|
expect(restartResult.initiated).toBe(true);
|
|
@@ -1516,7 +1449,7 @@ describe("restart finalization", () => {
|
|
|
1516
1449
|
durationMs: 4000,
|
|
1517
1450
|
attachToConversationId: conversationId,
|
|
1518
1451
|
};
|
|
1519
|
-
await recordingHandlers.recording_status(stoppedStatus,
|
|
1452
|
+
await recordingHandlers.recording_status(stoppedStatus, ctx);
|
|
1520
1453
|
|
|
1521
1454
|
// Verify: old recording attachment is published (finalization succeeded)
|
|
1522
1455
|
const textDeltas = sent.filter((m) => m.type === "assistant_text_delta");
|
|
@@ -1551,22 +1484,19 @@ describe("restart finalization", () => {
|
|
|
1551
1484
|
});
|
|
1552
1485
|
|
|
1553
1486
|
test("duplicate stopped callback does not double-attach", async () => {
|
|
1554
|
-
const { ctx, sent
|
|
1487
|
+
const { ctx, sent } = createCtx();
|
|
1555
1488
|
const conversationId = "conv-fin-dup-stop";
|
|
1556
|
-
ctx.socketToSession.set(fakeSocket, conversationId);
|
|
1557
1489
|
|
|
1558
1490
|
// Start a recording
|
|
1559
1491
|
const originalId = handleRecordingStart(
|
|
1560
1492
|
conversationId,
|
|
1561
1493
|
undefined,
|
|
1562
|
-
fakeSocket,
|
|
1563
1494
|
ctx,
|
|
1564
1495
|
);
|
|
1565
1496
|
|
|
1566
1497
|
// Restart
|
|
1567
1498
|
const restartResult = handleRecordingRestart(
|
|
1568
1499
|
conversationId,
|
|
1569
|
-
fakeSocket,
|
|
1570
1500
|
ctx,
|
|
1571
1501
|
);
|
|
1572
1502
|
expect(restartResult.initiated).toBe(true);
|
|
@@ -1581,7 +1511,7 @@ describe("restart finalization", () => {
|
|
|
1581
1511
|
durationMs: 2000,
|
|
1582
1512
|
attachToConversationId: conversationId,
|
|
1583
1513
|
};
|
|
1584
|
-
await recordingHandlers.recording_status(stoppedStatus,
|
|
1514
|
+
await recordingHandlers.recording_status(stoppedStatus, ctx);
|
|
1585
1515
|
|
|
1586
1516
|
// Count attachment-related messages after first callback
|
|
1587
1517
|
const firstCallAttachmentMsgs = sent.filter(
|
|
@@ -1598,7 +1528,7 @@ describe("restart finalization", () => {
|
|
|
1598
1528
|
expect(firstCallMsgCount).toBe(1);
|
|
1599
1529
|
|
|
1600
1530
|
// Send stopped again with same recordingId (duplicate)
|
|
1601
|
-
await recordingHandlers.recording_status(stoppedStatus,
|
|
1531
|
+
await recordingHandlers.recording_status(stoppedStatus, ctx);
|
|
1602
1532
|
|
|
1603
1533
|
// Verify: only one attachment message exists in sent — the duplicate was
|
|
1604
1534
|
// rejected by the idempotency guard in finalizeAndPublishRecording
|
|
@@ -1626,15 +1556,14 @@ describe("restart finalization", () => {
|
|
|
1626
1556
|
expect(getActiveRestartToken()).toBeNull();
|
|
1627
1557
|
|
|
1628
1558
|
// Start a recording, verify not idle
|
|
1629
|
-
const { ctx
|
|
1559
|
+
const { ctx } = createCtx();
|
|
1630
1560
|
const conversationId = "conv-fin-sanity";
|
|
1631
|
-
ctx.socketToSession.set(fakeSocket, conversationId);
|
|
1632
1561
|
|
|
1633
|
-
handleRecordingStart(conversationId, undefined,
|
|
1562
|
+
handleRecordingStart(conversationId, undefined, ctx);
|
|
1634
1563
|
expect(isRecordingIdle()).toBe(false);
|
|
1635
1564
|
|
|
1636
1565
|
// Restart, verify token is set
|
|
1637
|
-
handleRecordingRestart(conversationId,
|
|
1566
|
+
handleRecordingRestart(conversationId, ctx);
|
|
1638
1567
|
expect(getActiveRestartToken()).not.toBeNull();
|
|
1639
1568
|
|
|
1640
1569
|
// Reset everything, verify clean state
|