@vellumai/assistant 0.12.1 → 0.12.2-staging.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +4 -0
- package/ARCHITECTURE.md +20 -21
- package/docs/architecture/turn-actor.md +15 -8
- package/docs/guardian-request-flow.md +16 -0
- package/openapi.yaml +327 -52
- package/package.json +3 -1
- package/scripts/smoke-desktop-window-theme.ts +186 -0
- package/src/__tests__/always-loaded-tools-guard.test.ts +18 -10
- package/src/__tests__/anthropic-provider.test.ts +0 -63
- package/src/__tests__/app-compiler.test.ts +6 -0
- package/src/__tests__/app-control-tool-schemas.test.ts +5 -2
- package/src/__tests__/app-dir-path-guard.test.ts +1 -3
- package/src/__tests__/assistant-event-hub-machine-name.test.ts +6 -7
- package/src/__tests__/assistant-event-hub.test.ts +44 -0
- package/src/__tests__/computer-use-skill-manifest-regression.test.ts +1 -1
- package/src/__tests__/computer-use-skill-proxy-bridge.test.ts +5 -2
- package/src/__tests__/computer-use-tools.test.ts +19 -7
- package/src/__tests__/config-schema-cmd.test.ts +2 -2
- package/src/__tests__/config-schema.test.ts +28 -29
- package/src/__tests__/config-watcher.test.ts +23 -8
- package/src/__tests__/connection-model-compat.test.ts +2 -2
- package/src/__tests__/conversation-key-store-disk-view.test.ts +1 -1
- package/src/__tests__/conversation-pairing.test.ts +26 -0
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +4 -10
- package/src/__tests__/conversation-queue.test.ts +48 -207
- package/src/__tests__/conversation-retry-discard.test.ts +54 -1
- package/src/__tests__/conversation-skill-tools.test.ts +27 -0
- package/src/__tests__/conversation-surfaces-app-control.test.ts +8 -3
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +67 -0
- package/src/__tests__/conversation-title-service.test.ts +14 -10
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +3 -5
- package/src/__tests__/conversation-tool-setup-attribution.test.ts +51 -0
- package/src/__tests__/credential-record-retire.test.ts +171 -1
- package/src/__tests__/credential-routes.test.ts +212 -3
- package/src/__tests__/credential-security-invariants.test.ts +4 -2
- package/src/__tests__/cu-unified-flow.test.ts +8 -2
- package/src/__tests__/document-sync-tags.test.ts +25 -0
- package/src/__tests__/drain-kick-guard.test.ts +0 -2
- package/src/__tests__/drain-requeue-on-contention.test.ts +0 -6
- package/src/__tests__/evict-conversations-for-reload.test.ts +1 -1
- package/src/__tests__/git-porcelain.test.ts +47 -0
- package/src/__tests__/heartbeat-service.test.ts +56 -40
- package/src/__tests__/helpers/set-workspace-mcp.ts +11 -0
- package/src/__tests__/host-bash-proxy.test.ts +26 -45
- package/src/__tests__/host-browser-proxy.test.ts +6 -17
- package/src/__tests__/host-cu-proxy.test.ts +539 -0
- package/src/__tests__/host-file-proxy-targeted.test.ts +23 -0
- package/src/__tests__/host-file-proxy.test.ts +11 -23
- package/src/__tests__/host-transfer-proxy-targeted.test.ts +30 -9
- package/src/__tests__/list-messages-camera-frame.test.ts +121 -0
- package/src/__tests__/llm-catalog-parity.test.ts +25 -0
- package/src/__tests__/mcp-auth-routes.test.ts +6 -9
- package/src/__tests__/mcp-config-secret-boundary.test.ts +78 -136
- package/src/__tests__/mcp-health-check.test.ts +8 -11
- package/src/__tests__/mcp-list-plugin-servers.test.ts +8 -10
- package/src/__tests__/message-deleted-hook-dispatch.test.ts +119 -0
- package/src/__tests__/messages-after-tiebreaker.test.ts +91 -0
- package/src/__tests__/messages-read-boundary-guard.test.ts +1 -1
- package/src/__tests__/messaging-send-tool.test.ts +355 -548
- package/src/__tests__/model-intents.test.ts +3 -0
- package/src/__tests__/notification-vellum-adapter.test.ts +85 -2
- package/src/__tests__/oauth-commands-routes.test.ts +113 -0
- package/src/__tests__/oauth-provider-profiles.test.ts +18 -0
- package/src/__tests__/openai-provider.test.ts +1 -0
- package/src/__tests__/outlook-messaging-provider.test.ts +6 -6
- package/src/__tests__/platform-callback-registration.test.ts +8 -16
- package/src/__tests__/platform-client-verify-credential.test.ts +2 -0
- package/src/__tests__/plugin-import-boundary-guard.test.ts +14 -0
- package/src/__tests__/qdrant-manager.test.ts +132 -0
- package/src/__tests__/resolve-app.test.ts +135 -0
- package/src/__tests__/scaffold-managed-skill-tool.test.ts +229 -6
- package/src/__tests__/script-proxy-certs.test.ts +35 -0
- package/src/__tests__/service-account-token.test.ts +247 -0
- package/src/__tests__/skill-feature-flags-integration.test.ts +1 -1
- package/src/__tests__/skill-feature-flags.test.ts +2 -2
- package/src/__tests__/skills.test.ts +9 -4
- package/src/__tests__/source-fingerprint.test.ts +184 -0
- package/src/__tests__/subagent-tool-gate-mode.test.ts +19 -3
- package/src/__tests__/subagent-tools.test.ts +3 -2
- package/src/__tests__/telephony-tts-guard.test.ts +38 -3
- package/src/__tests__/test-support/computer-use-skill-harness.ts +3 -2
- package/src/__tests__/unicode.test.ts +13 -0
- package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +4 -0
- package/src/__tests__/watch-retro-tool-availability.test.ts +8 -17
- package/src/__tests__/workspace-git-service.test.ts +26 -0
- package/src/__tests__/workspace-migration-154-repair-retired-codex-gpt-5-4-model-ids.test.ts +336 -0
- package/src/acp/__tests__/agent-process.test.ts +258 -4
- package/src/acp/__tests__/auth-required.test.ts +96 -0
- package/src/acp/__tests__/client-handler.test.ts +90 -2
- package/src/acp/__tests__/helpers/acp-model-option.ts +51 -0
- package/src/acp/__tests__/session-manager-persistence.test.ts +5 -1
- package/src/acp/__tests__/session-manager-resume.test.ts +270 -6
- package/src/acp/__tests__/session-manager-usage.test.ts +4 -2
- package/src/acp/__tests__/session-manager.test.ts +895 -4
- package/src/acp/agent-process.test.ts +4 -1
- package/src/acp/agent-process.ts +96 -10
- package/src/acp/auth-required.ts +50 -1
- package/src/acp/auto-install.test.ts +9 -7
- package/src/acp/client-handler.ts +16 -3
- package/src/acp/model-config.test.ts +166 -0
- package/src/acp/model-config.ts +138 -0
- package/src/acp/resolve-agent.test.ts +220 -6
- package/src/acp/resolve-agent.ts +88 -23
- package/src/acp/session-manager.test.ts +196 -3
- package/src/acp/session-manager.ts +441 -18
- package/src/acp/types.ts +50 -5
- package/src/agent/loop.ts +1 -0
- package/src/api/events/acp-session-model-update.test.ts +88 -0
- package/src/api/events/acp-session-model-update.ts +46 -0
- package/src/api/events/app-files-changed.ts +4 -3
- package/src/api/events/notification-intent.ts +2 -0
- package/src/api/events/user-message-echo.ts +7 -1
- package/src/api/index.ts +6 -0
- package/src/api/responses/conversation-message.ts +5 -0
- package/src/apps/app-store.ts +1 -1
- package/src/apps/resolve-app.ts +81 -0
- package/src/apps/source-fingerprint.ts +266 -0
- package/src/avatar/identity-avatar.ts +7 -2
- package/src/bundler/app-compiler.ts +15 -0
- package/src/calls/__tests__/barge-in-guard.test.ts +81 -0
- package/src/{__tests__ → calls/__tests__}/call-controller.test.ts +503 -82
- package/src/{__tests__ → calls/__tests__}/media-stream-audio-transcode.test.ts +1 -1
- package/src/{__tests__ → calls/__tests__}/media-stream-output.test.ts +37 -8
- package/src/{__tests__ → calls/__tests__}/media-stream-parser.test.ts +1 -1
- package/src/{__tests__ → calls/__tests__}/media-stream-server-integration.test.ts +150 -57
- package/src/{__tests__ → calls/__tests__}/media-stream-stt-session.test.ts +5 -5
- package/src/{__tests__ → calls/__tests__}/media-turn-detector.test.ts +1 -1
- package/src/{live-voice → calls}/__tests__/progress-narration.test.ts +4 -4
- package/src/{live-voice → calls}/__tests__/progress-phrases.test.ts +1 -1
- package/src/calls/__tests__/voice-control-protocol.test.ts +58 -25
- package/src/calls/__tests__/voice-leg-coordinator.test.ts +229 -0
- package/src/calls/__tests__/voice-progress-cadence.test.ts +274 -0
- package/src/{__tests__/voice-session-bridge.test.ts → calls/__tests__/voice-session-bridge-turn-flow.test.ts} +20 -12
- package/src/calls/__tests__/voice-session-bridge.test.ts +37 -1
- package/src/calls/__tests__/voice-triage-escalate.test.ts +105 -0
- package/src/calls/barge-in-guard.ts +118 -0
- package/src/calls/call-constants.ts +24 -4
- package/src/calls/call-controller.ts +499 -199
- package/src/calls/call-transport.ts +16 -0
- package/src/calls/media-stream-output.ts +32 -0
- package/src/calls/media-stream-server.ts +82 -30
- package/src/calls/media-stream-stt-session.ts +15 -0
- package/src/{live-voice → calls}/progress-narration.ts +2 -2
- package/src/calls/resolve-call-tts-provider.ts +33 -2
- package/src/calls/tts-call-strategy.ts +9 -5
- package/src/calls/types.ts +2 -0
- package/src/calls/voice-control-protocol.ts +53 -21
- package/src/calls/voice-leg-coordinator.ts +189 -0
- package/src/calls/voice-progress-cadence.ts +423 -0
- package/src/calls/voice-session-bridge.ts +59 -46
- package/src/calls/voice-triage-escalate.ts +175 -56
- package/src/cli/commands/__tests__/apps.test.ts +245 -0
- package/src/cli/commands/__tests__/bash.test.ts +53 -0
- package/src/cli/commands/__tests__/roadmap.help.test.ts +19 -0
- package/src/cli/commands/apps.help.ts +67 -13
- package/src/cli/commands/apps.ts +183 -0
- package/src/cli/commands/bash.help.ts +8 -13
- package/src/cli/commands/bash.ts +7 -25
- package/src/cli/commands/channels/__tests__/send.test.ts +218 -0
- package/src/cli/commands/channels/index.help.ts +70 -2
- package/src/cli/commands/channels/index.ts +16 -3
- package/src/cli/commands/channels/send.ts +128 -0
- package/src/cli/commands/credentials.help.ts +6 -2
- package/src/cli/commands/credentials.ts +2 -2
- package/src/cli/commands/mcp.help.ts +8 -8
- package/src/cli/commands/mcp.ts +1 -1
- package/src/cli/commands/roadmap.help.ts +5 -1
- package/src/cli/commands/roadmap.ts +1 -1
- package/src/cli/lib/bundled-marketplace.json +103 -90
- package/src/config/__tests__/default-profile-catalog.test.ts +22 -0
- package/src/config/__tests__/memory-retrospective-schema.test.ts +15 -1
- package/src/config/__tests__/platform-identity.test.ts +255 -0
- package/src/config/__tests__/platform-rehydration.test.ts +99 -9
- package/src/config/acp-agent-write.ts +43 -0
- package/src/config/acp-defaults.test.ts +13 -3
- package/src/config/acp-defaults.ts +23 -6
- package/src/config/acp-schema.test.ts +81 -0
- package/src/config/acp-schema.ts +30 -2
- package/src/config/bundled-skills/acp/SKILL.md +22 -14
- package/src/config/bundled-skills/acp/TOOLS.json +5 -1
- package/src/config/bundled-skills/app-builder/SKILL.md +12 -9
- package/src/config/bundled-skills/app-builder/references/CUSTOM_ROUTES.md +1 -1
- package/src/config/bundled-skills/computer-use/SKILL.md +20 -2
- package/src/config/bundled-skills/computer-use/TOOLS.json +99 -0
- package/src/config/bundled-skills/computer-use/tools/computer-use-sequence.ts +12 -0
- package/src/config/bundled-skills/media-processing/services/preprocess.ts +2 -1
- package/src/config/bundled-skills/media-processing/services/processing-pipeline.ts +2 -1
- package/src/config/bundled-skills/media-processing/tools/generate-clip.ts +2 -1
- package/src/config/bundled-skills/messaging/SKILL.md +3 -3
- package/src/config/bundled-skills/messaging/TOOLS.json +3 -3
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +67 -139
- package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +1 -1
- package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +2 -1
- package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +2 -1
- package/src/config/bundled-skills/schedule/SKILL.md +3 -4
- package/src/config/bundled-skills/screen-annotation/SKILL.md +8 -1
- package/src/config/bundled-skills/screen-annotation/tools/screen-annotation.test.ts +4 -1
- package/src/config/bundled-skills/skill-management/TOOLS.json +6 -2
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +5 -2
- package/src/config/default-profile-catalog.ts +1 -1
- package/src/config/env.ts +2 -2
- package/src/config/feature-flag-registry.json +24 -12
- package/src/config/loader.ts +1 -0
- package/src/config/memory-v3-gate.test.ts +31 -0
- package/src/config/memory-v3-gate.ts +20 -16
- package/src/config/platform-identity.ts +201 -0
- package/src/config/platform-rehydration.ts +11 -27
- package/src/config/schema.ts +2 -2
- package/src/config/schemas/__tests__/live-voice.test.ts +8 -158
- package/src/config/schemas/__tests__/voice.test.ts +148 -0
- package/src/config/schemas/calls.ts +1 -28
- package/src/config/schemas/live-voice.ts +1 -173
- package/src/config/schemas/mcp.ts +1 -1
- package/src/config/schemas/memory-retrospective.ts +10 -0
- package/src/config/schemas/services.ts +8 -0
- package/src/config/schemas/skills.ts +0 -14
- package/src/config/schemas/voice.ts +150 -0
- package/src/credential-execution/process-manager.test.ts +18 -19
- package/src/credential-health/credential-health-service.ts +11 -17
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +66 -121
- package/src/daemon/__tests__/plugin-mcp-reconcile.test.ts +4 -1
- package/src/daemon/__tests__/skill-memory-refresh-mtime.test.ts +133 -0
- package/src/daemon/app-change-notify.ts +45 -0
- package/src/daemon/config-watcher.ts +13 -194
- package/src/daemon/conversation-error.ts +3 -2
- package/src/daemon/conversation-media-retry.ts +2 -1
- package/src/daemon/conversation-process.ts +35 -64
- package/src/daemon/conversation-runtime-assembly.ts +2 -1
- package/src/daemon/conversation-skill-tools.ts +13 -31
- package/src/daemon/conversation-store.ts +1 -1
- package/src/daemon/conversation-surfaces.ts +15 -9
- package/src/daemon/conversation-tool-setup.ts +37 -43
- package/src/daemon/conversation.ts +0 -3
- package/src/daemon/dictation-profile-store.ts +2 -1
- package/src/daemon/handlers/shared.ts +2 -1
- package/src/daemon/handlers/skills.ts +6 -3
- package/src/daemon/host-bash-proxy.ts +8 -12
- package/src/daemon/host-browser-proxy.ts +15 -1
- package/src/daemon/host-cu-proxy.ts +186 -19
- package/src/daemon/host-file-proxy.ts +8 -10
- package/src/daemon/host-proxy-preactivation.ts +2 -1
- package/src/daemon/host-transfer-proxy.ts +14 -18
- package/src/daemon/lifecycle.ts +0 -5
- package/src/daemon/mcp-reload-service.ts +5 -5
- package/src/daemon/message-types/sync.ts +1 -0
- package/src/daemon/orphan-reaper.ts +1 -1
- package/src/daemon/providers-setup.ts +1 -1
- package/src/daemon/reaction-history-render.ts +2 -1
- package/src/daemon/shutdown-handlers.ts +1 -9
- package/src/daemon/skill-memory-refresh.ts +64 -0
- package/src/daemon/tool-side-effects.ts +7 -18
- package/src/desktop/__tests__/fake-desktop.ts +122 -0
- package/src/desktop/desktop-chrome-policy.test.ts +79 -0
- package/src/desktop/desktop-chrome-policy.ts +47 -0
- package/src/desktop/desktop-chrome-session.ts +37 -0
- package/src/desktop/desktop-dependencies.test.ts +102 -0
- package/src/desktop/desktop-dependencies.ts +308 -0
- package/src/desktop/desktop-feature.test.ts +18 -0
- package/src/desktop/desktop-feature.ts +19 -0
- package/src/desktop/desktop-panel-config.test.ts +135 -0
- package/src/desktop/desktop-panel-config.ts +149 -0
- package/src/desktop/desktop-session-manager.test.ts +730 -0
- package/src/desktop/desktop-session-manager.ts +850 -0
- package/src/desktop/desktop-stream-bridge.test.ts +395 -0
- package/src/desktop/desktop-stream-bridge.ts +214 -0
- package/src/desktop/desktop-wallpaper.test.ts +191 -0
- package/src/desktop/desktop-wallpaper.ts +113 -0
- package/src/desktop/desktop-window-drag.ts +177 -0
- package/src/desktop/desktop-window-manager-config.test.ts +230 -0
- package/src/desktop/desktop-window-manager-config.ts +238 -0
- package/src/desktop/desktop-window-theme.ts +170 -0
- package/src/heartbeat/heartbeat-service.ts +21 -3
- package/src/home/__tests__/feed-writer.test.ts +22 -0
- package/src/home/feed-writer.ts +11 -2
- package/src/hooks/types.ts +33 -0
- package/src/i18n/AGENTS.md +22 -0
- package/src/i18n/__tests__/i18n.test.ts +108 -0
- package/src/i18n/index.ts +16 -0
- package/src/i18n/locales.ts +93 -0
- package/src/i18n/messages.ts +61 -0
- package/src/i18n/title.ts +25 -0
- package/src/inbound/platform-callback-registration.ts +4 -5
- package/src/live-voice/__tests__/live-voice-metrics.test.ts +0 -3
- package/src/live-voice/__tests__/live-voice-progress.test.ts +15 -15
- package/src/live-voice/__tests__/live-voice-sight-frame-echo.test.ts +238 -0
- package/src/live-voice/__tests__/live-voice-vad.test.ts +2 -2
- package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +13 -69
- package/src/live-voice/live-voice-photo.ts +25 -6
- package/src/live-voice/live-voice-session.ts +213 -693
- package/src/live-voice/protocol.ts +0 -2
- package/src/mcp/__tests__/effective-config.test.ts +2 -2
- package/src/mcp/__tests__/workspace-mcp-config.test.ts +135 -0
- package/src/mcp/effective-config.ts +3 -3
- package/src/mcp/mcp-header-store.ts +29 -45
- package/src/mcp/spec-schema.ts +111 -0
- package/src/mcp/startup.ts +8 -4
- package/src/mcp/workspace-mcp-config.ts +188 -0
- package/src/messaging/provider.ts +10 -3
- package/src/messaging/providers/__tests__/proactive-address.test.ts +95 -0
- package/src/messaging/providers/callback-routing.ts +18 -0
- package/src/messaging/providers/channel-transport.ts +56 -0
- package/src/messaging/providers/discord/transport.ts +17 -0
- package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +6 -17
- package/src/messaging/providers/slack/adapter.ts +7 -44
- package/src/messaging/providers/slack/transport.ts +16 -0
- package/src/messaging/providers/telegram-bot/adapter.ts +1 -48
- package/src/messaging/providers/telegram-bot/client.ts +0 -26
- package/src/messaging/providers/telegram-bot/transport.ts +20 -0
- package/src/messaging/providers/whatsapp/adapter.ts +1 -48
- package/src/messaging/providers/whatsapp/transport.ts +18 -0
- package/src/monitoring/__tests__/workspace-git-heartbeat.test.ts +8 -0
- package/src/notifications/__tests__/assistant-reply-producer.test.ts +55 -0
- package/src/notifications/__tests__/guardian-feed-projection.test.ts +158 -2
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +153 -3
- package/src/notifications/__tests__/schedule-result-producer.test.ts +99 -3
- package/src/notifications/adapters/macos.ts +3 -0
- package/src/notifications/assistant-reply-producer.ts +8 -3
- package/src/notifications/conversation-pairing.ts +36 -16
- package/src/notifications/delivered-post-record.ts +11 -1
- package/src/notifications/guardian-feed-projection.ts +125 -5
- package/src/notifications/home-feed-side-effect.ts +55 -7
- package/src/notifications/schedule-result-producer.ts +66 -22
- package/src/oauth/AGENTS.md +2 -0
- package/src/oauth/__tests__/seed-providers-managed.test.ts +54 -0
- package/src/oauth/byo-connection.test.ts +105 -0
- package/src/oauth/byo-connection.ts +36 -6
- package/src/oauth/connection-resolver.test.ts +73 -0
- package/src/oauth/connection-resolver.ts +95 -2
- package/src/oauth/scope-utils.ts +24 -0
- package/src/oauth/seed-providers.ts +204 -25
- package/src/outbound-proxy/certs.ts +3 -5
- package/src/permissions/checker.test.ts +11 -6
- package/src/persistence/compaction-log-store-clickhouse.ts +3 -3
- package/src/persistence/conversation-crud.ts +71 -63
- package/src/persistence/conversation-lineage.ts +12 -4
- package/src/persistence/conversation-queries.ts +2 -2
- package/src/persistence/conversation-title-placeholders.ts +23 -0
- package/src/persistence/conversation-title-service.ts +9 -26
- package/src/persistence/embeddings/qdrant-manager.ts +163 -18
- package/src/persistence/llm-request-log-sink-clickhouse.ts +3 -3
- package/src/persistence/llm-request-log-source-clickhouse.ts +3 -3
- package/src/persistence/message-cursor.ts +78 -0
- package/src/persistence/message-reads.ts +30 -0
- package/src/persistence/migrations/157-invite-contact-id.ts +18 -6
- package/src/persistence/migrations/__tests__/157-invite-contact-id.test.ts +149 -0
- package/src/persistence/schema/memory-core.ts +5 -0
- package/src/platform/client.test.ts +4 -2
- package/src/platform/client.ts +2 -10
- package/src/plugin-api/constants.ts +2 -0
- package/src/plugin-api/index.ts +1 -0
- package/src/plugin-api/types.ts +2 -0
- package/src/plugins/defaults/index.ts +2 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-accounting.test.ts +46 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-backfill.test.ts +118 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-preserve.test.ts +83 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-enqueue.test.ts +9 -5
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +20 -10
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-prompt.test.ts +5 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-state.test.ts +219 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-sweep.test.ts +31 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-trigger-check.test.ts +63 -0
- package/src/plugins/defaults/memory/__tests__/shutdown-hook.test.ts +4 -0
- package/src/plugins/defaults/memory/context-search/agent-runner.ts +3 -2
- package/src/plugins/defaults/memory/context-search/format.ts +3 -2
- package/src/plugins/defaults/memory/context-search/sources/memory-v2.ts +6 -3
- package/src/plugins/defaults/memory/context-search/sources/workspace.ts +3 -2
- package/src/plugins/defaults/memory/graph/capability-seed.ts +6 -7
- package/src/plugins/defaults/memory/hooks/init.ts +10 -0
- package/src/plugins/defaults/memory/hooks/message-deleted.ts +21 -0
- package/src/plugins/defaults/memory/hooks/shutdown.ts +2 -0
- package/src/plugins/defaults/memory/host-utils.ts +10 -0
- package/src/plugins/defaults/memory/jobs-worker.ts +13 -0
- package/src/plugins/defaults/memory/memory-retrospective-accounting.ts +34 -60
- package/src/plugins/defaults/memory/memory-retrospective-cursor-backfill.ts +126 -0
- package/src/plugins/defaults/memory/memory-retrospective-cursor-preserve.ts +79 -0
- package/src/plugins/defaults/memory/memory-retrospective-cursor.ts +28 -0
- package/src/plugins/defaults/memory/memory-retrospective-enqueue.ts +3 -5
- package/src/plugins/defaults/memory/memory-retrospective-job.ts +12 -3
- package/src/plugins/defaults/memory/memory-retrospective-prompt.ts +1 -1
- package/src/plugins/defaults/memory/memory-retrospective-state.ts +312 -71
- package/src/plugins/defaults/memory/memory-retrospective-sweep.ts +2 -1
- package/src/plugins/defaults/memory/memory-retrospective-trigger-check.ts +5 -3
- package/src/plugins/defaults/memory/skill-md-mtime-poll.ts +9 -0
- package/src/plugins/defaults/memory/startup.ts +28 -2
- package/src/plugins/defaults/memory/substrate/__tests__/skill-content.test.ts +7 -9
- package/src/plugins/defaults/memory/substrate/page-index.ts +6 -1
- package/src/plugins/defaults/memory/substrate/skill-content.ts +2 -2
- package/src/plugins/defaults/memory/v1/graph/consolidation.ts +4 -2
- package/src/plugins/defaults/memory/v1/graph/extraction.ts +4 -2
- package/src/plugins/defaults/memory/v1/graph/retriever.ts +4 -1
- package/src/plugins/defaults/memory/v2/reranker.ts +6 -1
- package/src/plugins/defaults/memory/v3/__tests__/card.test.ts +24 -0
- package/src/plugins/defaults/memory/v3/__tests__/pool-select.test.ts +125 -0
- package/src/plugins/defaults/memory/v3/__tests__/sections.test.ts +26 -0
- package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +30 -0
- package/src/plugins/defaults/memory/v3/card.ts +3 -2
- package/src/plugins/defaults/memory/v3/pool-select.ts +5 -3
- package/src/plugins/defaults/memory/v3/sections.ts +9 -3
- package/src/plugins/defaults/memory/v3/shadow-plugin.ts +5 -2
- package/src/plugins/defaults/memory/worker.ts +6 -0
- package/src/plugins/mcp-servers.ts +15 -75
- package/src/providers/__tests__/connection-model-compat.test.ts +3 -3
- package/src/providers/__tests__/retry-callsite.test.ts +41 -0
- package/src/providers/__tests__/retry-outbound-sanitize.test.ts +176 -0
- package/src/providers/anthropic/client.ts +0 -47
- package/src/providers/gemini/client.ts +9 -23
- package/src/providers/gemini-thought-signature.test.ts +63 -0
- package/src/providers/gemini-thought-signature.ts +44 -0
- package/src/providers/inference/__tests__/endpoint-probe.test.ts +42 -3
- package/src/providers/inference/auth.ts +2 -3
- package/src/providers/inference/connection-availability.ts +27 -8
- package/src/providers/inference/endpoint-probe.ts +10 -1
- package/src/providers/inference/resolve-auth.ts +22 -4
- package/src/providers/inference/service-account-token.ts +248 -0
- package/src/providers/model-catalog.ts +34 -0
- package/src/providers/model-intents.ts +10 -5
- package/src/providers/openai/__tests__/chat-completions-output-token-limit.test.ts +141 -0
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +146 -18
- package/src/providers/openai/__tests__/chat-completions-provider-thought-signature.test.ts +613 -0
- package/src/providers/openai/__tests__/google-thought-signature.test.ts +235 -0
- package/src/providers/openai/__tests__/tool-choice-mapping.test.ts +83 -1
- package/src/providers/openai/chat-completions-provider.ts +212 -79
- package/src/providers/openai/codex-models.ts +0 -4
- package/src/providers/openai/google-thought-signature.ts +320 -0
- package/src/providers/opencode/client.test.ts +73 -4
- package/src/providers/opencode/client.ts +60 -3
- package/src/providers/openrouter/client.ts +20 -0
- package/src/providers/outbound-request-sanitize.ts +96 -0
- package/src/providers/retry.ts +30 -19
- package/src/providers/unparseable-tool-args.ts +5 -1
- package/src/runtime/AGENTS.md +3 -1
- package/src/runtime/__tests__/channel-send.test.ts +443 -0
- package/src/runtime/__tests__/desktop-stream-upgrade.test.ts +89 -0
- package/src/runtime/__tests__/host-observe.test.ts +40 -1
- package/src/runtime/__tests__/live-voice-upgrade.test.ts +58 -0
- package/src/runtime/__tests__/runtime-ws-test-utils.ts +72 -0
- package/src/runtime/__tests__/watch-stream-upgrade.test.ts +59 -0
- package/src/runtime/agent-wake.ts +2 -1
- package/src/runtime/assistant-event-hub.ts +21 -3
- package/src/runtime/auth/same-actor.ts +38 -3
- package/src/runtime/channel-send.ts +316 -0
- package/src/runtime/host-observe.ts +11 -1
- package/src/runtime/http-server.ts +160 -151
- package/src/runtime/routes/__tests__/acp-routes.test.ts +159 -4
- package/src/runtime/routes/__tests__/apps-refresh-route.test.ts +205 -0
- package/src/runtime/routes/__tests__/channel-send-routes.test.ts +127 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +108 -1
- package/src/runtime/routes/__tests__/default-provider-routes.test.ts +2 -2
- package/src/runtime/routes/__tests__/dictation-cleanup-guard.test.ts +107 -0
- package/src/runtime/routes/__tests__/mcp-add-default-risk.test.ts +16 -12
- package/src/runtime/routes/acp-routes.test.ts +15 -0
- package/src/runtime/routes/acp-routes.ts +72 -9
- package/src/runtime/routes/app-management-routes.ts +73 -0
- package/src/runtime/routes/app-routes.ts +1 -1
- package/src/runtime/routes/channel-send-routes.ts +85 -0
- package/src/runtime/routes/conversation-list-routes.ts +14 -3
- package/src/runtime/routes/conversation-query-routes.ts +40 -0
- package/src/runtime/routes/conversation-routes.ts +16 -3
- package/src/runtime/routes/credential-routes.ts +19 -10
- package/src/runtime/routes/desktop-setup-routes.test.ts +74 -0
- package/src/runtime/routes/desktop-setup-routes.ts +35 -0
- package/src/runtime/routes/diagnostics-routes.ts +61 -4
- package/src/runtime/routes/events-routes.ts +4 -0
- package/src/runtime/routes/host-cu-routes.ts +6 -0
- package/src/runtime/routes/index.ts +4 -2
- package/src/runtime/routes/log-export-routes.ts +2 -0
- package/src/runtime/routes/mcp-auth-routes.ts +39 -47
- package/src/runtime/routes/migration-routes.ts +6 -20
- package/src/runtime/routes/oauth-commands-routes.ts +36 -0
- package/src/runtime/routes/platform-routes.ts +18 -15
- package/src/runtime/routes/workspace-routes.test.ts +55 -26
- package/src/runtime/routes/workspace-routes.ts +46 -63
- package/src/runtime/routes/workspace-tree-walk.test.ts +244 -0
- package/src/runtime/routes/workspace-tree-walk.ts +240 -0
- package/src/runtime/routes/workspace-utils.ts +17 -0
- package/src/runtime/services/__tests__/conversation-serializer.test.ts +28 -0
- package/src/runtime/services/conversation-serializer.ts +16 -3
- package/src/runtime/sync/resource-sync-events.ts +2 -1
- package/src/schedule/run-script.ts +2 -1
- package/src/schedule/schedule-store.ts +9 -2
- package/src/schedule/worker.ts +1 -1
- package/src/security/__tests__/ces-http-record-backend.test.ts +137 -0
- package/src/security/ces-http-record-backend.ts +120 -0
- package/src/security/ces-rpc-record-backend.ts +7 -0
- package/src/security/untrusted-content.ts +3 -1
- package/src/skills/__tests__/workspace-skill-md-signature.test.ts +60 -0
- package/src/skills/workspace-skill-md-signature.ts +50 -0
- package/src/subagent/consult-context.ts +12 -25
- package/src/tools/__tests__/side-effect-abort-guard.test.ts +1 -0
- package/src/tools/acp/list-agents.test.ts +3 -1
- package/src/tools/acp/spawn.test.ts +146 -11
- package/src/tools/acp/spawn.ts +21 -3
- package/src/tools/acp/status.test.ts +94 -0
- package/src/tools/acp/status.ts +40 -3
- package/src/tools/app-control/skill-proxy-bridge.ts +2 -1
- package/src/tools/browser/browser-execution.ts +1 -1
- package/src/tools/browser/cdp-client/accessibility-snapshot.ts +9 -3
- package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +2 -1
- package/src/tools/browser/cdp-client/extension-cdp-client.ts +3 -2
- package/src/tools/browser/network-recorder.ts +2 -1
- package/src/tools/capability-offer.test.ts +22 -2
- package/src/tools/capability-offer.ts +16 -1
- package/src/tools/computer-use/definitions.ts +121 -2
- package/src/tools/computer-use/skill-proxy-bridge.ts +2 -1
- package/src/tools/credentials/metadata-store.ts +100 -0
- package/src/tools/filesystem/search.ts +2 -1
- package/src/tools/host-filesystem/edit.test.ts +2 -1
- package/src/tools/host-filesystem/edit.ts +2 -2
- package/src/tools/host-filesystem/read.test.ts +2 -1
- package/src/tools/host-filesystem/read.ts +2 -2
- package/src/tools/host-filesystem/transfer.test.ts +2 -1
- package/src/tools/host-filesystem/transfer.ts +2 -2
- package/src/tools/host-filesystem/write.test.ts +2 -1
- package/src/tools/host-filesystem/write.ts +2 -2
- package/src/tools/host-terminal/host-shell.test.ts +16 -0
- package/src/tools/host-terminal/host-shell.ts +4 -20
- package/src/tools/network/web-search.ts +6 -1
- package/src/tools/policy-context.ts +6 -7
- package/src/tools/skills/scaffold-managed.ts +61 -3
- package/src/tools/subagent/spawn.ts +5 -8
- package/src/tools/terminal/__tests__/sanitized-bash.test.ts +57 -0
- package/src/tools/terminal/sanitized-bash.ts +105 -0
- package/src/tools/watch/watch-retro-report.ts +0 -8
- package/src/types/host-capabilities.ts +5 -3
- package/src/util/__tests__/language-subtag.test.ts +23 -1
- package/src/util/ca-bundle.ts +13 -0
- package/src/util/language-subtag.ts +16 -0
- package/src/util/platform.ts +8 -0
- package/src/util/unicode.ts +15 -1
- package/src/watch/watch-timeline.ts +2 -2
- package/src/workflows/run-manager.ts +2 -1
- package/src/workspace/git-porcelain.ts +66 -0
- package/src/workspace/git-service.ts +6 -55
- package/src/workspace/migrations/154-repair-retired-codex-gpt-5-4-model-ids.ts +258 -0
- package/src/workspace/migrations/155-move-front-model-config-to-voice.ts +78 -0
- package/src/workspace/migrations/156-extract-workspace-mcp-json.ts +214 -0
- package/src/workspace/migrations/__tests__/155-move-front-model-config-to-voice.test.ts +102 -0
- package/src/workspace/migrations/__tests__/156-extract-workspace-mcp-json.test.ts +199 -0
- package/src/workspace/migrations/registry.ts +6 -0
- package/src/__tests__/app-source-watcher.test.ts +0 -218
- package/src/__tests__/config-watcher-skill-reseed.test.ts +0 -359
- package/src/__tests__/speaker-identification.test.ts +0 -56
- package/src/calls/speaker-identification.ts +0 -233
- package/src/daemon/app-source-watcher.ts +0 -218
- package/src/messaging/providers/whatsapp/client.ts +0 -29
- package/src/runtime/routes/debug-bash-routes.ts +0 -173
- package/src/util/fs-watcher-error.ts +0 -36
- /package/src/{live-voice → calls}/progress-phrases.ts +0 -0
|
@@ -10,10 +10,10 @@ import {
|
|
|
10
10
|
broadcastMessage,
|
|
11
11
|
} from "../runtime/assistant-event-hub.js";
|
|
12
12
|
import {
|
|
13
|
-
ambiguousSameUserError,
|
|
14
13
|
enforceSameActorOrErrorResult,
|
|
15
|
-
|
|
14
|
+
pickMostRecentSameUserClient,
|
|
16
15
|
snapshotHostProxyActorPrincipalId,
|
|
16
|
+
unavailableSameUserClientError,
|
|
17
17
|
} from "../runtime/auth/same-actor.js";
|
|
18
18
|
import * as pendingInteractions from "../runtime/pending-interactions.js";
|
|
19
19
|
import type { ToolExecutionResult } from "../tools/types.js";
|
|
@@ -162,19 +162,17 @@ export class HostTransferProxy {
|
|
|
162
162
|
});
|
|
163
163
|
}
|
|
164
164
|
} else {
|
|
165
|
-
|
|
166
|
-
// (multi-machine) cases so a single targeted-style transfer cannot
|
|
167
|
-
// fan out across the user's machines.
|
|
168
|
-
const resolved = pickSameUserAutoResolve({
|
|
165
|
+
resolvedTargetClientId = pickMostRecentSameUserClient({
|
|
169
166
|
hub: assistantEventHub,
|
|
170
167
|
capability: "host_file",
|
|
171
168
|
sourceActorPrincipalId,
|
|
172
169
|
});
|
|
173
|
-
if (
|
|
174
|
-
|
|
170
|
+
if (
|
|
171
|
+
resolvedTargetClientId === undefined &&
|
|
172
|
+
sourceActorPrincipalId !== undefined
|
|
173
|
+
) {
|
|
174
|
+
return Promise.resolve(unavailableSameUserClientError("host_file"));
|
|
175
175
|
}
|
|
176
|
-
resolvedTargetClientId =
|
|
177
|
-
resolved.kind === "match" ? resolved.clientId : undefined;
|
|
178
176
|
}
|
|
179
177
|
|
|
180
178
|
if (resolvedTargetClientId != null) {
|
|
@@ -364,19 +362,17 @@ export class HostTransferProxy {
|
|
|
364
362
|
});
|
|
365
363
|
}
|
|
366
364
|
} else {
|
|
367
|
-
|
|
368
|
-
// (multi-machine) cases so a single targeted-style transfer cannot
|
|
369
|
-
// fan out across the user's machines.
|
|
370
|
-
const resolved = pickSameUserAutoResolve({
|
|
365
|
+
resolvedTargetClientId = pickMostRecentSameUserClient({
|
|
371
366
|
hub: assistantEventHub,
|
|
372
367
|
capability: "host_file",
|
|
373
368
|
sourceActorPrincipalId,
|
|
374
369
|
});
|
|
375
|
-
if (
|
|
376
|
-
|
|
370
|
+
if (
|
|
371
|
+
resolvedTargetClientId === undefined &&
|
|
372
|
+
sourceActorPrincipalId !== undefined
|
|
373
|
+
) {
|
|
374
|
+
return Promise.resolve(unavailableSameUserClientError("host_file"));
|
|
377
375
|
}
|
|
378
|
-
resolvedTargetClientId =
|
|
379
|
-
resolved.kind === "match" ? resolved.clientId : undefined;
|
|
380
376
|
}
|
|
381
377
|
|
|
382
378
|
if (resolvedTargetClientId != null) {
|
package/src/daemon/lifecycle.ts
CHANGED
|
@@ -71,7 +71,6 @@ import { ensureCompleteCustomProfiles } from "../workspace/custom-profile-ensure
|
|
|
71
71
|
import { ensureDefaultProvider } from "../workspace/default-provider-ensure.js";
|
|
72
72
|
import { WORKSPACE_MIGRATIONS } from "../workspace/migrations/registry.js";
|
|
73
73
|
import { runWorkspaceMigrations } from "../workspace/migrations/runner.js";
|
|
74
|
-
import { startAppSourceWatcher } from "./app-source-watcher.js";
|
|
75
74
|
import { startConfigWatcher } from "./config-watcher.js";
|
|
76
75
|
import { startConversationEvictor } from "./conversation-evictor.js";
|
|
77
76
|
import { writePid } from "./daemon-control.js";
|
|
@@ -706,10 +705,6 @@ export async function runDaemon(): Promise<void> {
|
|
|
706
705
|
// config, and broadcast the relevant resource-changed events to clients.
|
|
707
706
|
startConfigWatcher();
|
|
708
707
|
|
|
709
|
-
// Watch app source directories so edits recompile + refresh surfaces across
|
|
710
|
-
// all conversations.
|
|
711
|
-
startAppSourceWatcher();
|
|
712
|
-
|
|
713
708
|
// Start the CLI IPC server. Throws on EADDRINUSE to abort startup when another
|
|
714
709
|
// daemon already holds the socket, so this process never runs background jobs
|
|
715
710
|
// against the shared database as an unmanageable duplicate.
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Shared MCP reload business logic.
|
|
3
3
|
*
|
|
4
|
-
* Called by the ConfigWatcher when
|
|
4
|
+
* Called by the ConfigWatcher when mcp.json changes or a reload signal
|
|
5
5
|
* file is detected, so the daemon automatically reconnects MCP servers.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import {
|
|
8
|
+
import { invalidateConfigCache } from "../config/loader.js";
|
|
9
9
|
import {
|
|
10
10
|
buildEffectiveMcpConfig,
|
|
11
11
|
pluginMcpServersChangedSinceLastBuild,
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
import { getMcpServerManager } from "../mcp/manager.js";
|
|
14
14
|
import { migrateLegacyMcpHeaders } from "../mcp/mcp-header-store.js";
|
|
15
15
|
import { signalMcpReloaded } from "../mcp/reload-signal.js";
|
|
16
|
+
import { loadWorkspaceMcpConfig } from "../mcp/workspace-mcp-config.js";
|
|
16
17
|
import { createMcpToolsFromServer } from "../tools/mcp/mcp-tool-factory.js";
|
|
17
18
|
import { registerMcpTools, unregisterAllMcpTools } from "../tools/registry.js";
|
|
18
19
|
import { getLogger } from "../util/logger.js";
|
|
@@ -95,16 +96,15 @@ async function doReload(): Promise<McpReloadResult> {
|
|
|
95
96
|
// If the config is broken we abort early, preserving the current
|
|
96
97
|
// working MCP setup instead of leaving zero servers.
|
|
97
98
|
invalidateConfigCache();
|
|
98
|
-
const config = getConfig();
|
|
99
99
|
|
|
100
100
|
// 2. Stop existing MCP servers + unregister their tools
|
|
101
101
|
await manager.stop();
|
|
102
102
|
unregisterAllMcpTools();
|
|
103
103
|
|
|
104
104
|
// Plugins are re-read here too: installing or removing one changes the
|
|
105
|
-
// server set exactly like editing
|
|
105
|
+
// server set exactly like editing mcp.json does, and both arrive
|
|
106
106
|
// through this same reload.
|
|
107
|
-
const mcpConfig = buildEffectiveMcpConfig(
|
|
107
|
+
const mcpConfig = buildEffectiveMcpConfig(loadWorkspaceMcpConfig());
|
|
108
108
|
const serverIds = Object.keys(mcpConfig.servers);
|
|
109
109
|
|
|
110
110
|
// 3. Restart MCP servers
|
|
@@ -13,6 +13,7 @@ export const SYNC_TAGS = {
|
|
|
13
13
|
assistantConfig: "assistant:self:config",
|
|
14
14
|
assistantSounds: "assistant:self:sounds",
|
|
15
15
|
assistantSchedules: "assistant:self:schedules",
|
|
16
|
+
assistantDesktop: "assistant:self:desktop",
|
|
16
17
|
assistantTheme: "assistant:self:theme",
|
|
17
18
|
appsList: "apps:list",
|
|
18
19
|
documentsList: "documents:list",
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Tools run commands in their own process group (`detached: true`) and, on
|
|
5
5
|
* timeout/abort, group-kill with `process.kill(-pgid, SIGKILL)` (the bash and
|
|
6
|
-
* host_bash tools, the skill sandbox runner
|
|
6
|
+
* host_bash tools, and the skill sandbox runner). The
|
|
7
7
|
* immediate child is reaped by Bun/libuv, but its descendants — e.g. git's
|
|
8
8
|
* transport helpers or a skill runner's `bun` process — were never spawned by
|
|
9
9
|
* the daemon, so when the group dies they reparent to PID 1. When the daemon
|
|
@@ -63,7 +63,7 @@ export async function initializeProvidersAndTools(
|
|
|
63
63
|
// Start MCP servers — workspace-configured and plugin-declared alike —
|
|
64
64
|
// and register their tools. Shared with the schedule worker, which hosts
|
|
65
65
|
// agent turns in its own process and so needs its own connections.
|
|
66
|
-
await startConfiguredMcpServers(
|
|
66
|
+
await startConfiguredMcpServers();
|
|
67
67
|
|
|
68
68
|
log.info("Daemon startup: providers and tools initialized");
|
|
69
69
|
}
|
|
@@ -28,6 +28,7 @@ import {
|
|
|
28
28
|
unwrapExternalContentForDisplay,
|
|
29
29
|
wrapUntrustedContent,
|
|
30
30
|
} from "../security/untrusted-content.js";
|
|
31
|
+
import { safeStringSlice } from "../util/unicode.js";
|
|
31
32
|
|
|
32
33
|
/**
|
|
33
34
|
* Slack-style emoji names render in colon form. Anything else (a unicode
|
|
@@ -53,7 +54,7 @@ function snippetOf(text: string): string {
|
|
|
53
54
|
if (flat.length <= TARGET_SNIPPET_MAX_CHARS) {
|
|
54
55
|
return flat;
|
|
55
56
|
}
|
|
56
|
-
return `${flat
|
|
57
|
+
return `${safeStringSlice(flat, 0, TARGET_SNIPPET_MAX_CHARS)}...`;
|
|
57
58
|
}
|
|
58
59
|
|
|
59
60
|
/**
|
|
@@ -24,11 +24,7 @@ import { browserManager } from "../tools/browser/browser-manager.js";
|
|
|
24
24
|
import { getLogger } from "../util/logger.js";
|
|
25
25
|
import { APP_VERSION } from "../version.js";
|
|
26
26
|
import { getEnrichmentService } from "../workspace/commit-message-enrichment-service.js";
|
|
27
|
-
import {
|
|
28
|
-
commitAllPendingWorkspaceChanges,
|
|
29
|
-
stopWorkspaceHeartbeatService,
|
|
30
|
-
} from "../workspace/heartbeat-service.js";
|
|
31
|
-
import { stopAppSourceWatcher } from "./app-source-watcher.js";
|
|
27
|
+
import { commitAllPendingWorkspaceChanges } from "../workspace/heartbeat-service.js";
|
|
32
28
|
import { stopConfigWatcher } from "./config-watcher.js";
|
|
33
29
|
import { stopConversationEvictor } from "./conversation-evictor.js";
|
|
34
30
|
import { stopConversations } from "./conversation-store.js";
|
|
@@ -98,9 +94,6 @@ async function shutdown(): Promise<void> {
|
|
|
98
94
|
}, 30_000);
|
|
99
95
|
forceTimer.unref();
|
|
100
96
|
|
|
101
|
-
// Workspace git heartbeat timer lives in the monitor process. This
|
|
102
|
-
// in-process stop is a no-op unless the singleton was started here.
|
|
103
|
-
await stopWorkspaceHeartbeatService();
|
|
104
97
|
await stopHeartbeatService();
|
|
105
98
|
|
|
106
99
|
// Stop the periodic consent-cache refresh (a daemon-owned interval).
|
|
@@ -134,7 +127,6 @@ async function shutdown(): Promise<void> {
|
|
|
134
127
|
disposeAcpSessionManager();
|
|
135
128
|
stopConversationEvictor();
|
|
136
129
|
stopConfigWatcher();
|
|
137
|
-
stopAppSourceWatcher();
|
|
138
130
|
stopCliIpcServer();
|
|
139
131
|
stopConversations();
|
|
140
132
|
await stopCes();
|
|
@@ -8,16 +8,24 @@ import {
|
|
|
8
8
|
maybeSeedCapabilitySkills,
|
|
9
9
|
maybeSeedCliCommandCards,
|
|
10
10
|
} from "../plugins/defaults/memory/substrate/boot-maintenance.js";
|
|
11
|
+
import { readWorkspaceSkillMdSignature } from "../skills/workspace-skill-md-signature.js";
|
|
11
12
|
import { getLogger } from "../util/logger.js";
|
|
12
13
|
|
|
13
14
|
const log = getLogger("skill-memory-refresh");
|
|
14
15
|
|
|
16
|
+
/** Poll interval for workspace `SKILL.md` mtimes. Stats only, no tree walk. */
|
|
17
|
+
export const SKILL_MD_MTIME_POLL_MS = 1_500;
|
|
18
|
+
|
|
19
|
+
let lastSignature: string | undefined;
|
|
20
|
+
let pollTimer: ReturnType<typeof setInterval> | undefined;
|
|
21
|
+
|
|
15
22
|
export function refreshSkillCapabilityMemories(
|
|
16
23
|
config: AssistantConfig = getConfig(),
|
|
17
24
|
): void {
|
|
18
25
|
seedSkillGraphNodes();
|
|
19
26
|
maybeSeedCapabilitySkills(config);
|
|
20
27
|
maybeSeedCliCommandCards(config);
|
|
28
|
+
lastSignature = readWorkspaceSkillMdSignature();
|
|
21
29
|
void seedUninstalledCatalogSkillMemories()
|
|
22
30
|
.then(() => {
|
|
23
31
|
// Re-run after the async catalog fetch populates the cache so stale
|
|
@@ -31,3 +39,59 @@ export function refreshSkillCapabilityMemories(
|
|
|
31
39
|
),
|
|
32
40
|
);
|
|
33
41
|
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Reseed skill capability memories when the workspace `SKILL.md` set or
|
|
45
|
+
* mtimes have changed since the last refresh in this process. No-op when the
|
|
46
|
+
* signature is unchanged. First call in a process always reseeds so the
|
|
47
|
+
* memory worker, whose in-process skill cache starts empty, picks up the
|
|
48
|
+
* catalog without waiting for an install path.
|
|
49
|
+
*/
|
|
50
|
+
export function maybeRefreshSkillCapabilityMemoriesFromMtime(): boolean {
|
|
51
|
+
const signature = readWorkspaceSkillMdSignature();
|
|
52
|
+
if (lastSignature !== undefined && signature === lastSignature) {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
if (lastSignature !== undefined) {
|
|
56
|
+
log.info("Workspace SKILL.md set changed, reseeding capability memories");
|
|
57
|
+
}
|
|
58
|
+
refreshSkillCapabilityMemories();
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function pollOnce(): void {
|
|
63
|
+
try {
|
|
64
|
+
maybeRefreshSkillCapabilityMemoriesFromMtime();
|
|
65
|
+
} catch (err) {
|
|
66
|
+
log.warn({ err }, "SKILL.md mtime poll failed");
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Start a per-process poll of workspace `SKILL.md` mtimes. Idempotent.
|
|
72
|
+
* The memory worker and the assistant each run this against their own
|
|
73
|
+
* in-process skill caches. Runs once immediately so a worker that has never
|
|
74
|
+
* seeded picks up the catalog without waiting a tick.
|
|
75
|
+
*/
|
|
76
|
+
export function startWorkspaceSkillMdMtimePoll(): void {
|
|
77
|
+
if (pollTimer !== undefined) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
pollOnce();
|
|
81
|
+
pollTimer = setInterval(pollOnce, SKILL_MD_MTIME_POLL_MS);
|
|
82
|
+
pollTimer.unref?.();
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function stopWorkspaceSkillMdMtimePoll(): void {
|
|
86
|
+
if (pollTimer === undefined) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
clearInterval(pollTimer);
|
|
90
|
+
pollTimer = undefined;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** @internal Test-only: clear the in-process signature and poll timer. */
|
|
94
|
+
export function _resetSkillMdMtimeStateForTests(): void {
|
|
95
|
+
lastSignature = undefined;
|
|
96
|
+
stopWorkspaceSkillMdMtimePoll();
|
|
97
|
+
}
|
|
@@ -18,17 +18,15 @@ import { invalidateEdgeIndex } from "../plugins/defaults/memory/substrate/edge-i
|
|
|
18
18
|
import { invalidatePageIndex } from "../plugins/defaults/memory/substrate/page-index.js";
|
|
19
19
|
import { getConceptsDir } from "../plugins/defaults/memory/substrate/page-store.js";
|
|
20
20
|
import { broadcastMessage } from "../runtime/assistant-event-hub.js";
|
|
21
|
-
import {
|
|
22
|
-
publishAppsChanged,
|
|
23
|
-
publishDocumentsChanged,
|
|
24
|
-
} from "../runtime/sync/resource-sync-events.js";
|
|
25
|
-
import { updatePublishedAppDeployment } from "../services/published-app-updater.js";
|
|
21
|
+
import { publishDocumentsChanged } from "../runtime/sync/resource-sync-events.js";
|
|
26
22
|
import type { ToolExecutionResult } from "../tools/types.js";
|
|
27
23
|
import { getLogger } from "../util/logger.js";
|
|
28
24
|
import { getWorkspaceDir } from "../util/platform.js";
|
|
29
|
-
import {
|
|
25
|
+
import {
|
|
26
|
+
broadcastAppFilesChanged,
|
|
27
|
+
notifyAppSurfacesChanged,
|
|
28
|
+
} from "./app-change-notify.js";
|
|
30
29
|
import type { Conversation } from "./conversation.js";
|
|
31
|
-
import { refreshSurfacesForApp } from "./conversation-surfaces.js";
|
|
32
30
|
import { isDoordashCommand, updateDoordashProgress } from "./doordash-steps.js";
|
|
33
31
|
|
|
34
32
|
const log = getLogger("tool-side-effects");
|
|
@@ -54,7 +52,7 @@ export type PostExecutionHook = (
|
|
|
54
52
|
* Compilation is the responsibility of the tool executor (see
|
|
55
53
|
* `executeAppCreate`, `executeAppRefresh`): executors own the source→dist
|
|
56
54
|
* transform and surface `compile_errors` in their result when it fails.
|
|
57
|
-
* Post-execution hooks only observe the outcome and notify
|
|
55
|
+
* Post-execution hooks only observe the outcome and notify. They must
|
|
58
56
|
* not re-run a compile because `compileApp()` begins with `rm -rf dist/`
|
|
59
57
|
* and would race with the executor's own output (LUM-1153).
|
|
60
58
|
*/
|
|
@@ -63,14 +61,7 @@ function notifyAppChanged(
|
|
|
63
61
|
appId: string,
|
|
64
62
|
opts?: { fileChange?: boolean; status?: string },
|
|
65
63
|
): void {
|
|
66
|
-
|
|
67
|
-
broadcastAppFilesChanged(appId);
|
|
68
|
-
void updatePublishedAppDeployment(appId);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
function broadcastAppFilesChanged(appId: string): void {
|
|
72
|
-
broadcastMessage({ type: "app_files_changed", appId });
|
|
73
|
-
publishAppsChanged();
|
|
64
|
+
notifyAppSurfacesChanged(appId, opts, ctx);
|
|
74
65
|
}
|
|
75
66
|
|
|
76
67
|
/**
|
|
@@ -128,8 +119,6 @@ registerHook("app_create", (_name, _input, result, { ctx }) => {
|
|
|
128
119
|
log.warn({ err, appId }, "Failed to track conversation ID on app_create");
|
|
129
120
|
}
|
|
130
121
|
|
|
131
|
-
ensureAppSourceWatcher();
|
|
132
|
-
|
|
133
122
|
notifyAppChanged(ctx, appId);
|
|
134
123
|
|
|
135
124
|
// Seed background icon generation from the created app's canonical record
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
|
|
3
|
+
import { sleep } from "../../util/retry.js";
|
|
4
|
+
import {
|
|
5
|
+
type DesktopChild,
|
|
6
|
+
type DesktopChildRole,
|
|
7
|
+
type DesktopLoss,
|
|
8
|
+
DesktopSessionManager,
|
|
9
|
+
type DesktopSpawnRequest,
|
|
10
|
+
} from "../desktop-session-manager.js";
|
|
11
|
+
|
|
12
|
+
/** A child the test exits by hand. */
|
|
13
|
+
class FakeChild implements DesktopChild {
|
|
14
|
+
private static nextPid = 1000;
|
|
15
|
+
readonly pid = FakeChild.nextPid++;
|
|
16
|
+
readonly exited: Promise<number>;
|
|
17
|
+
private settle!: (code: number) => void;
|
|
18
|
+
|
|
19
|
+
constructor(
|
|
20
|
+
readonly role: DesktopChildRole,
|
|
21
|
+
readonly request: DesktopSpawnRequest,
|
|
22
|
+
) {
|
|
23
|
+
this.exited = new Promise((resolve) => {
|
|
24
|
+
this.settle = resolve;
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
kill(): void {}
|
|
29
|
+
|
|
30
|
+
exit(code = 0): void {
|
|
31
|
+
this.settle(code);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export const LINGER_MS = 20;
|
|
36
|
+
export const KILL_GRACE_MS = 20;
|
|
37
|
+
|
|
38
|
+
export interface FakeDesktopOptions {
|
|
39
|
+
profileDir: string;
|
|
40
|
+
/** Defaults inside `profileDir`, for tests that ignore the dock's config. */
|
|
41
|
+
panelConfigDir?: string;
|
|
42
|
+
/** Roles whose spawn throws, to exercise a start that fails partway. */
|
|
43
|
+
failSpawn?: DesktopChildRole[];
|
|
44
|
+
/** Binaries `which` cannot find. */
|
|
45
|
+
missingBinaries?: string[];
|
|
46
|
+
/** Whether a SIGTERM makes the fake child exit on its own. */
|
|
47
|
+
exitOnTerm?: boolean;
|
|
48
|
+
/** Whether a SIGKILL does; without either the child survives both. */
|
|
49
|
+
exitOnKill?: boolean;
|
|
50
|
+
sourceEnv?: NodeJS.ProcessEnv;
|
|
51
|
+
panelRestartDelayMs?: number;
|
|
52
|
+
renderWallpaper?: (width: number, height: number) => Promise<Buffer | null>;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function newFakeDesktop(options: FakeDesktopOptions) {
|
|
56
|
+
const spawned: FakeChild[] = [];
|
|
57
|
+
const killed: { child: FakeChild; signal: string }[] = [];
|
|
58
|
+
let vncReady = true;
|
|
59
|
+
let chromiumPath: () => Promise<string> = async () => "/fake/chromium";
|
|
60
|
+
const manager = new DesktopSessionManager({
|
|
61
|
+
spawn: (role, request) => {
|
|
62
|
+
if (options.failSpawn?.includes(role)) {
|
|
63
|
+
throw new Error(`spawn ${role} failed`);
|
|
64
|
+
}
|
|
65
|
+
const child = new FakeChild(role, request);
|
|
66
|
+
spawned.push(child);
|
|
67
|
+
return child;
|
|
68
|
+
},
|
|
69
|
+
which: (binary) =>
|
|
70
|
+
options.missingBinaries?.includes(binary) ? null : `/usr/bin/${binary}`,
|
|
71
|
+
probeVncPort: async () => vncReady,
|
|
72
|
+
resolveChromePath: () => chromiumPath(),
|
|
73
|
+
renderWallpaper: options.renderWallpaper ?? (async () => null),
|
|
74
|
+
killProcessGroup: (child, signal) => {
|
|
75
|
+
killed.push({ child: child as FakeChild, signal });
|
|
76
|
+
const exits =
|
|
77
|
+
signal === "SIGTERM" ? options.exitOnTerm : options.exitOnKill;
|
|
78
|
+
if (exits) {
|
|
79
|
+
(child as FakeChild).exit(0);
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
lingerMs: LINGER_MS,
|
|
83
|
+
readyDeadlineMs: 30,
|
|
84
|
+
killGraceMs: KILL_GRACE_MS,
|
|
85
|
+
profileDir: options.profileDir,
|
|
86
|
+
panelConfigDir:
|
|
87
|
+
options.panelConfigDir ?? join(options.profileDir, "desktop-panel"),
|
|
88
|
+
sourceEnv: options.sourceEnv,
|
|
89
|
+
panelRestartDelayMs: options.panelRestartDelayMs,
|
|
90
|
+
writeWindowManagerConfig: (configDir) => join(configDir, "openbox.xml"),
|
|
91
|
+
});
|
|
92
|
+
return {
|
|
93
|
+
manager,
|
|
94
|
+
spawned,
|
|
95
|
+
killed,
|
|
96
|
+
/** Children that got a SIGTERM, in order. */
|
|
97
|
+
terminated: () =>
|
|
98
|
+
killed.filter((k) => k.signal === "SIGTERM").map((k) => k.child),
|
|
99
|
+
setVncReady: (ready: boolean) => {
|
|
100
|
+
vncReady = ready;
|
|
101
|
+
},
|
|
102
|
+
setChromiumPath: (resolve: () => Promise<string>) => {
|
|
103
|
+
chromiumPath = resolve;
|
|
104
|
+
},
|
|
105
|
+
child: (role: DesktopChildRole) =>
|
|
106
|
+
spawned.filter((c) => c.role === role).at(-1)!,
|
|
107
|
+
roles: () => spawned.map((c) => c.role),
|
|
108
|
+
count: (role: DesktopChildRole) =>
|
|
109
|
+
spawned.filter((c) => c.role === role).length,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/** Let one awaited step (a path resolution, an exit callback) land. */
|
|
114
|
+
export const settle = (): Promise<void> => sleep(0);
|
|
115
|
+
|
|
116
|
+
export function newViewer() {
|
|
117
|
+
const lost: DesktopLoss[] = [];
|
|
118
|
+
return {
|
|
119
|
+
lost,
|
|
120
|
+
viewer: { onDesktopLost: (loss: DesktopLoss) => lost.push(loss) },
|
|
121
|
+
};
|
|
122
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import {
|
|
2
|
+
mkdtempSync,
|
|
3
|
+
readdirSync,
|
|
4
|
+
readFileSync,
|
|
5
|
+
rmSync,
|
|
6
|
+
statSync,
|
|
7
|
+
writeFileSync,
|
|
8
|
+
} from "node:fs";
|
|
9
|
+
import { tmpdir } from "node:os";
|
|
10
|
+
import { join } from "node:path";
|
|
11
|
+
import { afterEach, expect, test } from "bun:test";
|
|
12
|
+
|
|
13
|
+
import { writeDesktopChromePolicy } from "./desktop-chrome-policy.js";
|
|
14
|
+
|
|
15
|
+
const directories: string[] = [];
|
|
16
|
+
function policyDirectory(): string {
|
|
17
|
+
const path = mkdtempSync(join(tmpdir(), "desktop-chrome-policy-"));
|
|
18
|
+
directories.push(path);
|
|
19
|
+
return path;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
afterEach(() => {
|
|
23
|
+
for (const path of directories.splice(0)) {
|
|
24
|
+
rmSync(path, { recursive: true, force: true });
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test("preserves unrelated policies and does not rewrite on repeated startup", () => {
|
|
29
|
+
const directory = policyDirectory();
|
|
30
|
+
const path = join(directory, "vellum-desktop.json");
|
|
31
|
+
const otherPath = join(directory, "organization.json");
|
|
32
|
+
const unrelated = '{ "HomepageLocation": "https://example.com" }\n';
|
|
33
|
+
writeFileSync(otherPath, unrelated);
|
|
34
|
+
writeFileSync(
|
|
35
|
+
path,
|
|
36
|
+
JSON.stringify({
|
|
37
|
+
CommandLineFlagSecurityWarningsEnabled: false,
|
|
38
|
+
DownloadRestrictions: 1,
|
|
39
|
+
}),
|
|
40
|
+
);
|
|
41
|
+
const original = statSync(path);
|
|
42
|
+
|
|
43
|
+
writeDesktopChromePolicy(directory);
|
|
44
|
+
const content = readFileSync(path, "utf8");
|
|
45
|
+
expect(statSync(path).ino).not.toBe(original.ino);
|
|
46
|
+
expect(JSON.parse(content).DownloadRestrictions).toBe(1);
|
|
47
|
+
expect(readFileSync(otherPath, "utf8")).toBe(unrelated);
|
|
48
|
+
expect(readdirSync(directory).sort()).toEqual([
|
|
49
|
+
"organization.json",
|
|
50
|
+
"vellum-desktop.json",
|
|
51
|
+
]);
|
|
52
|
+
|
|
53
|
+
const before = statSync(path);
|
|
54
|
+
writeDesktopChromePolicy(directory);
|
|
55
|
+
expect(readFileSync(path, "utf8")).toBe(content);
|
|
56
|
+
expect(statSync(path).ino).toBe(before.ino);
|
|
57
|
+
expect(statSync(path).mtimeMs).toBe(before.mtimeMs);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test("creates missing policy directories for a fresh desktop", () => {
|
|
61
|
+
const directory = join(policyDirectory(), "policies", "managed");
|
|
62
|
+
writeDesktopChromePolicy(directory);
|
|
63
|
+
expect(readdirSync(directory)).toEqual(["vellum-desktop.json"]);
|
|
64
|
+
expect(statSync(join(directory, "vellum-desktop.json")).mode & 0o777).toBe(
|
|
65
|
+
0o644,
|
|
66
|
+
);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test.each(["{broken", "[]", "null", "false"])(
|
|
70
|
+
"leaves invalid existing policy content untouched: %s",
|
|
71
|
+
(content) => {
|
|
72
|
+
const directory = policyDirectory();
|
|
73
|
+
const path = join(directory, "vellum-desktop.json");
|
|
74
|
+
writeFileSync(path, content);
|
|
75
|
+
expect(() => writeDesktopChromePolicy(directory)).toThrow();
|
|
76
|
+
expect(readFileSync(path, "utf8")).toBe(content);
|
|
77
|
+
expect(readdirSync(directory)).toEqual(["vellum-desktop.json"]);
|
|
78
|
+
},
|
|
79
|
+
);
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import {
|
|
2
|
+
mkdirSync,
|
|
3
|
+
mkdtempSync,
|
|
4
|
+
readFileSync,
|
|
5
|
+
renameSync,
|
|
6
|
+
rmSync,
|
|
7
|
+
writeFileSync,
|
|
8
|
+
} from "node:fs";
|
|
9
|
+
import { join } from "node:path";
|
|
10
|
+
|
|
11
|
+
export function writeDesktopChromePolicy(policyDir: string): void {
|
|
12
|
+
const path = join(policyDir, "vellum-desktop.json");
|
|
13
|
+
let policy: Record<string, unknown> = {};
|
|
14
|
+
try {
|
|
15
|
+
const value: unknown = JSON.parse(readFileSync(path, "utf8"));
|
|
16
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
17
|
+
throw new Error("Desktop Chrome policy must be a JSON object");
|
|
18
|
+
}
|
|
19
|
+
policy = value as Record<string, unknown>;
|
|
20
|
+
} catch (err) {
|
|
21
|
+
if ((err as NodeJS.ErrnoException).code !== "ENOENT") {
|
|
22
|
+
throw err;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
if (
|
|
26
|
+
policy.CommandLineFlagSecurityWarningsEnabled === false &&
|
|
27
|
+
policy.PasswordManagerEnabled === false
|
|
28
|
+
) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// This suppresses command-line warnings; Chrome's sandbox stays unchanged.
|
|
33
|
+
policy.CommandLineFlagSecurityWarningsEnabled = false;
|
|
34
|
+
// Suppress save prompts while keeping previously saved passwords usable.
|
|
35
|
+
policy.PasswordManagerEnabled = false;
|
|
36
|
+
mkdirSync(policyDir, { recursive: true });
|
|
37
|
+
const staging = mkdtempSync(join(policyDir, ".vellum-desktop-"));
|
|
38
|
+
try {
|
|
39
|
+
const temporaryPath = join(staging, "policy.json");
|
|
40
|
+
writeFileSync(temporaryPath, JSON.stringify(policy, null, 2) + "\n", {
|
|
41
|
+
mode: 0o644,
|
|
42
|
+
});
|
|
43
|
+
renameSync(temporaryPath, path);
|
|
44
|
+
} finally {
|
|
45
|
+
rmSync(staging, { recursive: true, force: true });
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { basename, join } from "node:path";
|
|
3
|
+
|
|
4
|
+
export function shouldRestoreDesktopChromeSession(profileDir: string): boolean {
|
|
5
|
+
const lastUsed = readProfileMetadata(
|
|
6
|
+
join(profileDir, "Local State"),
|
|
7
|
+
)?.last_used;
|
|
8
|
+
const profileName =
|
|
9
|
+
typeof lastUsed === "string" &&
|
|
10
|
+
lastUsed !== "" &&
|
|
11
|
+
lastUsed !== "." &&
|
|
12
|
+
lastUsed !== ".." &&
|
|
13
|
+
basename(lastUsed) === lastUsed
|
|
14
|
+
? lastUsed
|
|
15
|
+
: "Default";
|
|
16
|
+
return (
|
|
17
|
+
readProfileMetadata(join(profileDir, profileName, "Preferences"))
|
|
18
|
+
?.exit_type === "Crashed"
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function readProfileMetadata(
|
|
23
|
+
path: string,
|
|
24
|
+
): { last_used?: unknown; exit_type?: unknown } | undefined {
|
|
25
|
+
try {
|
|
26
|
+
const value: unknown = JSON.parse(readFileSync(path, "utf8"));
|
|
27
|
+
if (value && typeof value === "object" && "profile" in value) {
|
|
28
|
+
const profile = value.profile;
|
|
29
|
+
if (profile && typeof profile === "object" && !Array.isArray(profile)) {
|
|
30
|
+
return profile;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
} catch {
|
|
34
|
+
// Leave missing or unreadable session metadata to Chrome's normal startup.
|
|
35
|
+
}
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|