@vellumai/assistant 0.12.5 → 0.12.6-dev.202609292116.41f7caa
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 +2 -0
- package/ARCHITECTURE.md +26 -3
- package/docs/automatic-done.md +32 -0
- package/docs/desktop-browser-cli.md +3 -2
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/package.json +2 -1
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/mcp-oauth.test.ts +53 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/mcp-oauth.ts +60 -0
- package/node_modules/@vellumai/environments/src/shell.test.ts +13 -1
- package/node_modules/@vellumai/environments/src/shell.ts +22 -1
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/package.json +2 -1
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/mcp-oauth.test.ts +53 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/mcp-oauth.ts +60 -0
- package/node_modules/@vellumai/gateway-client/src/__tests__/status-contracts.test.ts +39 -0
- package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +25 -0
- package/node_modules/@vellumai/service-contracts/package.json +2 -1
- package/node_modules/@vellumai/service-contracts/src/__tests__/mcp-oauth.test.ts +53 -0
- package/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/service-contracts/src/mcp-oauth.ts +60 -0
- package/openapi.yaml +270 -10
- package/package.json +1 -1
- package/src/__tests__/agent-loop.test.ts +76 -0
- package/src/__tests__/anthropic-provider.test.ts +2 -0
- package/src/__tests__/byok-default-profile-ensure.test.ts +7 -0
- package/src/__tests__/config-get-vision-flag.test.ts +1 -1
- package/src/__tests__/config-loader-validation-reset-notice.test.ts +2 -2
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +160 -154
- package/src/__tests__/conversation-evictor.test.ts +54 -0
- package/src/__tests__/conversation-pairing.test.ts +112 -0
- package/src/__tests__/document-append-idempotency.test.ts +13 -0
- package/src/__tests__/document-create-dedupe.test.ts +13 -0
- package/src/__tests__/document-find-replace.test.ts +13 -0
- package/src/__tests__/document-revision-history.test.ts +390 -0
- package/src/__tests__/document-revision.test.ts +425 -0
- package/src/__tests__/document-tool-security.test.ts +13 -0
- package/src/__tests__/document-update-default-surface.test.ts +13 -0
- package/src/__tests__/emit-signal-routing-intent.test.ts +270 -0
- package/src/__tests__/gateway-flag-listener.test.ts +59 -6
- package/src/__tests__/headless-browser-navigate.test.ts +20 -30
- package/src/__tests__/host-cu-proxy.test.ts +79 -0
- package/src/__tests__/host-cu-routes-targeted.test.ts +14 -5
- package/src/__tests__/host-shell-tool.test.ts +6 -2
- package/src/__tests__/identity-routes.test.ts +4 -3
- package/src/__tests__/list-messages-client-message-id.test.ts +43 -0
- package/src/__tests__/llm-catalog-parity.test.ts +4 -4
- package/src/__tests__/llm-schema.test.ts +1 -1
- package/src/__tests__/notification-platform-adapter.test.ts +83 -1
- package/src/__tests__/oauth2-gateway-transport.test.ts +2 -0
- package/src/__tests__/openai-provider.test.ts +38 -11
- package/src/__tests__/pricing.test.ts +11 -0
- package/src/__tests__/public-ingress-urls.test.ts +26 -0
- package/src/__tests__/retry-thinking-tool-choice.test.ts +47 -0
- package/src/__tests__/skill-execute-input.test.ts +11 -0
- package/src/__tests__/tool-approval-handler.test.ts +4 -0
- package/src/__tests__/tool-preview-lifecycle.test.ts +26 -0
- package/src/__tests__/usage-attribution.test.ts +31 -0
- package/src/__tests__/workspace-migration-133-collapse-provider-connections.test.ts +3 -3
- package/src/__tests__/workspace-migration-160-repair-retired-fireworks-model-ids.test.ts +262 -0
- package/src/__tests__/workspace-migration-161-repair-retired-fireworks-glm-5p2-model-id.test.ts +233 -0
- package/src/acp/agent-process.ts +6 -1
- package/src/acp/client-handler.ts +6 -1
- package/src/agent/loop-unparseable-tool-retry.test.ts +333 -0
- package/src/agent/loop.ts +161 -17
- package/src/api/events/document-editor-show.ts +5 -0
- package/src/api/events/document-editor-update.ts +10 -0
- package/src/api/events/tool-result.ts +1 -0
- package/src/calls/__tests__/call-controller.test.ts +273 -1
- package/src/calls/__tests__/media-stream-server-integration.test.ts +40 -0
- package/src/calls/__tests__/media-stream-stt-session.test.ts +33 -0
- package/src/calls/__tests__/voice-session-bridge.test.ts +54 -0
- package/src/calls/call-constants.ts +10 -0
- package/src/calls/call-controller.ts +359 -25
- package/src/calls/channel-admission-reader.ts +15 -9
- package/src/calls/media-stream-server.ts +18 -0
- package/src/calls/media-stream-stt-session.ts +30 -9
- package/src/calls/twilio-provider.ts +14 -31
- package/src/calls/voice-session-bridge.ts +18 -1
- package/src/cli/commands/browser.ts +1 -2
- package/src/cli/lib/__tests__/plugin-artifact.test.ts +23 -0
- package/src/cli/lib/__tests__/plugin-catalog-visibility.test.ts +2 -0
- package/src/cli/lib/__tests__/plugin-details.test.ts +16 -0
- package/src/cli/lib/bundled-marketplace.json +54 -2
- package/src/cli/lib/inspect-plugin.ts +15 -15
- package/src/cli/lib/list-installed-plugins.ts +29 -28
- package/src/cli/lib/plugin-artifact.ts +74 -63
- package/src/cli/lib/plugin-catalog-visibility.ts +1 -0
- package/src/cli/lib/plugin-details.ts +27 -42
- package/src/cli/lib/plugin-marketplace.ts +6 -8
- package/src/cli/lib/plugin-surfaces.ts +8 -7
- package/src/cli.ts +1 -3
- package/src/config/__tests__/chat-settings.test.ts +121 -0
- package/src/config/assistant-feature-flags.ts +28 -23
- package/src/config/bundled-skills/computer-use/SKILL.md +20 -0
- package/src/config/bundled-skills/computer-use/TOOLS.json +11 -11
- package/src/config/chat-settings.ts +100 -0
- package/src/config/default-profile-catalog.ts +1 -1
- package/src/config/feature-flag-cache.ts +7 -25
- package/src/config/feature-flag-registry.json +9 -9
- package/src/config/loader.ts +5 -1
- package/src/config/profile-text-generation.ts +25 -21
- package/src/config/schemas/conversations.ts +14 -0
- package/src/config/schemas/llm.ts +1 -0
- package/src/config/schemas/notifications.ts +8 -1
- package/src/contacts/gateway-channel-read.ts +1 -1
- package/src/conversations/__tests__/auto-archive-activity.test.ts +136 -0
- package/src/conversations/__tests__/auto-archive.test.ts +304 -0
- package/src/conversations/auto-archive-activity.ts +61 -0
- package/src/conversations/auto-archive.ts +201 -0
- package/src/daemon/computer-use-observation.test.ts +81 -0
- package/src/daemon/computer-use-observation.ts +23 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +18 -2
- package/src/daemon/conversation-agent-loop.ts +14 -6
- package/src/daemon/conversation-evictor.ts +50 -16
- package/src/daemon/conversation-messaging.ts +4 -0
- package/src/daemon/conversation-process.ts +15 -0
- package/src/daemon/conversation-queue-manager.ts +2 -0
- package/src/daemon/conversation.ts +2 -0
- package/src/daemon/doordash-steps.ts +1 -3
- package/src/daemon/handlers/__tests__/config-a2a-accept.test.ts +43 -1
- package/src/daemon/handlers/__tests__/config-channels.test.ts +1 -1
- package/src/daemon/handlers/config-a2a.ts +75 -2
- package/src/daemon/handlers/shared.ts +13 -2
- package/src/daemon/host-cu-proxy.ts +34 -3
- package/src/daemon/lifecycle.ts +15 -2
- package/src/daemon/process-message.ts +5 -0
- package/src/daemon/shutdown-handlers.ts +3 -0
- package/src/daemon/turn-finalization.ts +4 -0
- package/src/desktop/desktop-browser-endpoint.ts +11 -2
- package/src/desktop/desktop-chrome-policy.test.ts +1 -0
- package/src/desktop/desktop-chrome-policy.ts +7 -1
- package/src/desktop/desktop-computer-use.test.ts +14 -4
- package/src/desktop/desktop-session-manager.ts +5 -3
- package/src/documents/document-revisions-store.ts +153 -0
- package/src/documents/document-store.ts +471 -133
- package/src/followups/followup-store.ts +0 -4
- package/src/i18n/__tests__/i18n.test.ts +25 -0
- package/src/i18n/assistant-text.ts +18 -0
- package/src/i18n/index.ts +1 -0
- package/src/i18n/messages.ts +11 -0
- package/src/inbound/__tests__/oauth-callback-url.test.ts +25 -2
- package/src/inbound/oauth-callback-url.ts +30 -1
- package/src/inbound/public-ingress-urls.ts +12 -2
- package/src/ipc/gateway-client.test.ts +40 -0
- package/src/ipc/gateway-client.ts +12 -29
- package/src/ipc/gateway-flag-listener.ts +7 -10
- package/src/live-voice/__tests__/live-voice-agent-turn.test.ts +71 -0
- package/src/live-voice/__tests__/live-voice-flux-turn-end.test.ts +83 -0
- package/src/live-voice/__tests__/protocol.test.ts +77 -0
- package/src/live-voice/__tests__/share-targets.test.ts +163 -0
- package/src/live-voice/live-voice-session.ts +23 -6
- package/src/live-voice/protocol.ts +22 -0
- package/src/live-voice/share-targets.ts +225 -0
- package/src/mcp/__tests__/mcp-oauth-client-registration.test.ts +144 -3
- package/src/mcp/__tests__/mcp-oauth-provider.test.ts +2 -0
- package/src/mcp/client.ts +1 -19
- package/src/mcp/mcp-oauth-provider.ts +66 -49
- package/src/media/openrouter-image-service.ts +39 -22
- package/src/monitoring/__tests__/oom-inheritance-sweep.test.ts +86 -0
- package/src/monitoring/__tests__/oom-kill-reporter.test.ts +374 -0
- package/src/monitoring/control.ts +2 -1
- package/src/monitoring/oom-inheritance-sweep.ts +96 -0
- package/src/monitoring/oom-kill-reporter.ts +469 -0
- package/src/monitoring/resource-sampler.ts +30 -0
- package/src/notifications/README.md +53 -4
- package/src/notifications/__tests__/assistant-initiated-thread.test.ts +254 -0
- package/src/notifications/__tests__/broadcaster.test.ts +558 -6
- package/src/notifications/__tests__/completion-policy.test.ts +37 -0
- package/src/notifications/__tests__/from-me-alert-integration.test.ts +192 -0
- package/src/notifications/adapters/platform.ts +14 -0
- package/src/notifications/assistant-initiated-thread.ts +101 -0
- package/src/notifications/broadcaster.ts +94 -16
- package/src/notifications/chat-reply-policy.ts +11 -0
- package/src/notifications/completion-policy.ts +12 -2
- package/src/notifications/conversation-pairing.ts +15 -70
- package/src/notifications/emit-signal.ts +34 -3
- package/src/notifications/types.ts +2 -0
- package/src/oauth/connect-orchestrator.ts +1 -2
- package/src/persistence/__tests__/conversation-auto-archive.test.ts +330 -0
- package/src/persistence/conversation-auto-archive.ts +170 -0
- package/src/persistence/conversation-crud.ts +2 -1
- package/src/persistence/conversation-queries.ts +15 -8
- package/src/persistence/embeddings/embed.test.ts +96 -0
- package/src/persistence/embeddings/embed.ts +20 -2
- package/src/persistence/embeddings/embedding-gemini.test.ts +39 -3
- package/src/persistence/embeddings/embedding-gemini.ts +39 -1
- package/src/persistence/embeddings/embedding-local.ts +6 -0
- package/src/persistence/embeddings/qdrant-manager.ts +5 -0
- package/src/persistence/job-utils.ts +10 -0
- package/src/persistence/migrations/384-add-document-revision.test.ts +83 -0
- package/src/persistence/migrations/384-add-document-revision.ts +21 -0
- package/src/persistence/migrations/385-create-document-revisions.test.ts +67 -0
- package/src/persistence/migrations/385-create-document-revisions.ts +28 -0
- package/src/persistence/migrations/386-conversations-last-reopened-at.ts +10 -0
- package/src/persistence/migrations/__tests__/386-conversations-last-reopened-at.test.ts +32 -0
- package/src/persistence/schema/conversations.ts +1 -0
- package/src/persistence/schema/documents.ts +20 -0
- package/src/persistence/steps.ts +6 -0
- package/src/platform/managed-speech.ts +42 -46
- package/src/plugin-api/vision-support.test.ts +21 -21
- package/src/plugins/defaults/empty-response/hooks/post-model-call.ts +11 -6
- package/src/providers/__tests__/dispatch-connection-routing.test.ts +1 -1
- package/src/providers/__tests__/search-provider-catalog.test.ts +8 -0
- package/src/providers/__tests__/unparseable-tool-args.test.ts +25 -0
- package/src/providers/anthropic/client.ts +1 -1
- package/src/providers/fireworks/client.test.ts +168 -0
- package/src/providers/fireworks/client.ts +24 -0
- package/src/providers/inference/adapter-factory.ts +6 -0
- package/src/providers/ionet/client.test.ts +143 -0
- package/src/providers/ionet/client.ts +37 -0
- package/src/providers/model-catalog.ts +141 -84
- package/src/providers/model-intents.ts +4 -4
- package/src/providers/openai/chat-completions-provider.ts +33 -2
- package/src/providers/retry.ts +56 -4
- package/src/providers/search-provider-catalog.ts +10 -0
- package/src/providers/types.ts +10 -2
- package/src/providers/unparseable-tool-args.ts +29 -5
- package/src/providers/usage-tracking.ts +5 -3
- package/src/runtime/assistant-event-hub.ts +23 -0
- package/src/runtime/auth/__tests__/guard-tests.test.ts +0 -7
- package/src/runtime/auth/__tests__/route-policy.test.ts +1 -1
- package/src/runtime/channel-reply-delivery.ts +2 -0
- package/src/runtime/channel-retry-sweep.ts +1 -2
- package/src/runtime/http-types.ts +3 -0
- package/src/runtime/migrations/__tests__/vbundle-streaming-validator.test.ts +35 -10
- package/src/runtime/migrations/__tests__/vbundle-validator-v1-schema.test.ts +48 -0
- package/src/runtime/migrations/vbundle-builder.ts +19 -3
- package/src/runtime/migrations/vbundle-streaming-importer.ts +14 -19
- package/src/runtime/migrations/vbundle-streaming-validator.ts +14 -7
- package/src/runtime/migrations/vbundle-validator.ts +54 -1
- package/src/runtime/pending-interactions.ts +20 -2
- package/src/runtime/routes/__tests__/contact-routes.test.ts +5 -3
- package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +36 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +375 -2
- package/src/runtime/routes/__tests__/inference-send-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +12 -0
- package/src/runtime/routes/contact-routes.ts +2 -2
- package/src/runtime/routes/conversation-list-routes.ts +10 -2
- package/src/runtime/routes/conversation-query-routes.ts +48 -10
- package/src/runtime/routes/conversation-routes.ts +7 -0
- package/src/runtime/routes/documents-routes.ts +185 -47
- package/src/runtime/routes/events-routes.ts +1 -3
- package/src/runtime/routes/host-app-control-routes.ts +1 -0
- package/src/runtime/routes/host-bash-routes.ts +1 -0
- package/src/runtime/routes/host-browser-routes.ts +15 -0
- package/src/runtime/routes/host-cu-routes.ts +4 -1
- package/src/runtime/routes/host-file-routes.ts +1 -0
- package/src/runtime/routes/host-proxy-result-binding.test.ts +83 -0
- package/src/runtime/routes/host-proxy-result-binding.ts +25 -0
- package/src/runtime/routes/host-transfer-routes.ts +1 -0
- package/src/runtime/routes/identity-routes.ts +3 -3
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +4 -0
- package/src/runtime/routes/integrations/a2a.ts +4 -1
- package/src/runtime/routes/migration-routes.ts +3 -1
- package/src/runtime/routes/notification-routes.ts +1 -2
- package/src/runtime/routes/ui-snapshot-routes.test.ts +1 -0
- package/src/runtime/routes/user-route-dispatcher.ts +5 -1
- package/src/runtime/verification-templates.ts +1 -2
- package/src/sequence/analytics.ts +0 -4
- package/src/sequence/engine.ts +1 -11
- package/src/sequence/guardrails.ts +1 -6
- package/src/sequence/importer.ts +0 -8
- package/src/sequence/store.ts +0 -10
- package/src/signals/user-message.ts +0 -6
- package/src/skills/catalog-install.ts +52 -23
- package/src/tools/browser/browser-execution.ts +20 -23
- package/src/tools/browser/cdp-client/cdp-inspect/ws-transport.ts +1 -3
- package/src/tools/document/document-tool.ts +13 -11
- package/src/tools/executor.ts +1 -2
- package/src/tools/host-terminal/host-shell.ts +4 -2
- package/src/tools/network/__tests__/web-search.test.ts +105 -0
- package/src/tools/network/web-fetch.ts +2 -4
- package/src/tools/network/web-search.ts +239 -12
- package/src/tools/shared/filesystem/format-diff.ts +1 -2
- package/src/tools/shared/shell-output.test.ts +42 -0
- package/src/tools/shared/shell-output.ts +27 -4
- package/src/tools/skills/execute.ts +10 -7
- package/src/tools/terminal/shell.ts +3 -3
- package/src/tools/tool-approval-handler.ts +9 -1
- package/src/tools/ui-surface/visual-validation.ts +1 -2
- package/src/usage/attribution.ts +18 -1
- package/src/util/__tests__/oom-priority.test.ts +55 -0
- package/src/util/__tests__/worker-memory.test.ts +16 -0
- package/src/util/debounce.ts +0 -4
- package/src/util/oom-priority.ts +79 -0
- package/src/util/platform.ts +0 -2
- package/src/util/plugin-manifest.ts +5 -7
- package/src/util/pricing.ts +25 -0
- package/src/util/worker-memory.ts +9 -3
- package/src/util/worker-process.ts +8 -0
- package/src/workspace/byok-default-profile-ensure.ts +11 -0
- package/src/workspace/hatched-date.ts +11 -4
- package/src/workspace/migrations/160-repair-retired-fireworks-model-ids.ts +209 -0
- package/src/workspace/migrations/161-repair-retired-fireworks-glm-5p2-model-id.ts +197 -0
- package/src/workspace/migrations/registry.ts +4 -0
|
@@ -9,8 +9,10 @@
|
|
|
9
9
|
* inbound mu-law audio is decoded to 16 kHz PCM16 and fed to a
|
|
10
10
|
* {@link StreamingTranscriber} resolved for the configured
|
|
11
11
|
* `services.stt` provider. Replies trigger on the provider's
|
|
12
|
-
* utterance-boundary `final` events;
|
|
13
|
-
*
|
|
12
|
+
* utterance-boundary `final` events; interim `partial` events are
|
|
13
|
+
* forwarded as the freshest words spoken since that final but start no
|
|
14
|
+
* turn of their own, and barge-in (`onSpeechStart`) always fires from
|
|
15
|
+
* the local energy VAD, never from transcriber partials.
|
|
14
16
|
* Frames arriving while the provider session is still starting are
|
|
15
17
|
* held in a bounded buffer and flushed on start (overflow drops the
|
|
16
18
|
* oldest frames and is counted + logged at teardown).
|
|
@@ -20,10 +22,11 @@
|
|
|
20
22
|
* transcriber is available for the configured provider, or the
|
|
21
23
|
* provider closed the streaming session unexpectedly mid-call.
|
|
22
24
|
*
|
|
23
|
-
* This module is **transport-neutral
|
|
24
|
-
* (`onSpeechStart`, `onTranscriptFinal`, `
|
|
25
|
-
* driving any call flow itself;
|
|
26
|
-
* connects it to the
|
|
25
|
+
* This module is **transport-neutral**: it exposes callback hooks
|
|
26
|
+
* (`onSpeechStart`, `onTranscriptFinal`, `onTranscriptPartial`, `onDtmf`,
|
|
27
|
+
* `onStop`) rather than driving any call flow itself;
|
|
28
|
+
* `media-stream-server.ts` instantiates and connects it to the
|
|
29
|
+
* media-stream WebSocket ingress.
|
|
27
30
|
*
|
|
28
31
|
* Error handling:
|
|
29
32
|
* - When the telephony resolver returns a non-supported status, the
|
|
@@ -142,6 +145,19 @@ export interface MediaStreamSttSessionCallbacks {
|
|
|
142
145
|
*/
|
|
143
146
|
onTranscriptFinal?: (text: string, durationMs: number) => void;
|
|
144
147
|
|
|
148
|
+
/**
|
|
149
|
+
* Called for each interim transcript the streaming provider revises on
|
|
150
|
+
* its way to the next final. The text is the provider's running guess at
|
|
151
|
+
* the words spoken since that final, so each call supersedes the last and
|
|
152
|
+
* the run resets when {@link onTranscriptFinal} commits it. Empty
|
|
153
|
+
* partials are suppressed, and batch mode never emits one.
|
|
154
|
+
*
|
|
155
|
+
* Turn taking still runs off the finals: this is the freshest text a
|
|
156
|
+
* boundary the front door held mid-thought can be re-judged on, not a
|
|
157
|
+
* boundary of its own.
|
|
158
|
+
*/
|
|
159
|
+
onTranscriptPartial?: (text: string) => void;
|
|
160
|
+
|
|
145
161
|
/**
|
|
146
162
|
* Called when a DTMF digit is received from Twilio.
|
|
147
163
|
*/
|
|
@@ -542,8 +558,8 @@ export class MediaStreamSttSession {
|
|
|
542
558
|
/**
|
|
543
559
|
* Map streaming transcriber events onto the session callbacks.
|
|
544
560
|
*
|
|
545
|
-
* Partials are
|
|
546
|
-
* provider's utterance-boundary finals, and barge-in comes from the
|
|
561
|
+
* Partials are forwarded but own no turn taking: replies trigger only on
|
|
562
|
+
* the provider's utterance-boundary finals, and barge-in comes from the
|
|
547
563
|
* local VAD.
|
|
548
564
|
*/
|
|
549
565
|
private handleStreamingEvent(event: SttStreamServerEvent): void {
|
|
@@ -552,8 +568,13 @@ export class MediaStreamSttSession {
|
|
|
552
568
|
}
|
|
553
569
|
|
|
554
570
|
switch (event.type) {
|
|
555
|
-
case "partial":
|
|
571
|
+
case "partial": {
|
|
572
|
+
const text = event.text.trim();
|
|
573
|
+
if (text.length > 0) {
|
|
574
|
+
this.callbacks.onTranscriptPartial?.(text);
|
|
575
|
+
}
|
|
556
576
|
return;
|
|
577
|
+
}
|
|
557
578
|
case "finalized":
|
|
558
579
|
return;
|
|
559
580
|
case "turn-start":
|
|
@@ -23,27 +23,10 @@ const log = getLogger("twilio-provider");
|
|
|
23
23
|
export class TwilioVoiceProvider implements VoiceProvider {
|
|
24
24
|
readonly name = "twilio";
|
|
25
25
|
|
|
26
|
-
// ── Credential helpers ──────────────────────────────────────────────
|
|
27
|
-
|
|
28
|
-
private async getCredentials(): Promise<{
|
|
29
|
-
accountSid: string;
|
|
30
|
-
authToken: string;
|
|
31
|
-
}> {
|
|
32
|
-
return await getTwilioCredentials();
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
private authHeader(accountSid: string, authToken: string): string {
|
|
36
|
-
return twilioAuthHeader(accountSid, authToken);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
private baseUrl(accountSid: string): string {
|
|
40
|
-
return twilioBaseUrl(accountSid);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
26
|
// ── VoiceProvider interface ─────────────────────────────────────────
|
|
44
27
|
|
|
45
28
|
async initiateCall(opts: InitiateCallOptions): Promise<{ callSid: string }> {
|
|
46
|
-
const { accountSid, authToken } = await
|
|
29
|
+
const { accountSid, authToken } = await getTwilioCredentials();
|
|
47
30
|
|
|
48
31
|
const body = new URLSearchParams({
|
|
49
32
|
From: opts.from,
|
|
@@ -80,10 +63,10 @@ export class TwilioVoiceProvider implements VoiceProvider {
|
|
|
80
63
|
|
|
81
64
|
log.info({ from: opts.from, to: opts.to }, "Initiating Twilio call");
|
|
82
65
|
|
|
83
|
-
const res = await fetch(`${
|
|
66
|
+
const res = await fetch(`${twilioBaseUrl(accountSid)}/Calls.json`, {
|
|
84
67
|
method: "POST",
|
|
85
68
|
headers: {
|
|
86
|
-
Authorization:
|
|
69
|
+
Authorization: twilioAuthHeader(accountSid, authToken),
|
|
87
70
|
"Content-Type": "application/x-www-form-urlencoded",
|
|
88
71
|
},
|
|
89
72
|
body: body.toString(),
|
|
@@ -108,18 +91,18 @@ export class TwilioVoiceProvider implements VoiceProvider {
|
|
|
108
91
|
}
|
|
109
92
|
|
|
110
93
|
async endCall(callSid: string): Promise<void> {
|
|
111
|
-
const { accountSid, authToken } = await
|
|
94
|
+
const { accountSid, authToken } = await getTwilioCredentials();
|
|
112
95
|
|
|
113
96
|
log.info({ callSid }, "Ending Twilio call");
|
|
114
97
|
|
|
115
98
|
const body = new URLSearchParams({ Status: "completed" });
|
|
116
99
|
|
|
117
100
|
const res = await fetch(
|
|
118
|
-
`${
|
|
101
|
+
`${twilioBaseUrl(accountSid)}/Calls/${callSid}.json`,
|
|
119
102
|
{
|
|
120
103
|
method: "POST",
|
|
121
104
|
headers: {
|
|
122
|
-
Authorization:
|
|
105
|
+
Authorization: twilioAuthHeader(accountSid, authToken),
|
|
123
106
|
"Content-Type": "application/x-www-form-urlencoded",
|
|
124
107
|
},
|
|
125
108
|
body: body.toString(),
|
|
@@ -143,14 +126,14 @@ export class TwilioVoiceProvider implements VoiceProvider {
|
|
|
143
126
|
}
|
|
144
127
|
|
|
145
128
|
async getCallStatus(callSid: string): Promise<string> {
|
|
146
|
-
const { accountSid, authToken } = await
|
|
129
|
+
const { accountSid, authToken } = await getTwilioCredentials();
|
|
147
130
|
|
|
148
131
|
const res = await fetch(
|
|
149
|
-
`${
|
|
132
|
+
`${twilioBaseUrl(accountSid)}/Calls/${callSid}.json`,
|
|
150
133
|
{
|
|
151
134
|
method: "GET",
|
|
152
135
|
headers: {
|
|
153
|
-
Authorization:
|
|
136
|
+
Authorization: twilioAuthHeader(accountSid, authToken),
|
|
154
137
|
},
|
|
155
138
|
},
|
|
156
139
|
);
|
|
@@ -183,7 +166,7 @@ export class TwilioVoiceProvider implements VoiceProvider {
|
|
|
183
166
|
async checkCallerIdEligibility(
|
|
184
167
|
phoneNumber: string,
|
|
185
168
|
): Promise<{ eligible: boolean; reason?: string }> {
|
|
186
|
-
const { accountSid, authToken } = await
|
|
169
|
+
const { accountSid, authToken } = await getTwilioCredentials();
|
|
187
170
|
const encodedNumber = encodeURIComponent(phoneNumber);
|
|
188
171
|
|
|
189
172
|
let incomingOk = false;
|
|
@@ -191,13 +174,13 @@ export class TwilioVoiceProvider implements VoiceProvider {
|
|
|
191
174
|
|
|
192
175
|
// Check incoming phone numbers (owned by this account)
|
|
193
176
|
const incomingRes = await fetch(
|
|
194
|
-
`${
|
|
177
|
+
`${twilioBaseUrl(
|
|
195
178
|
accountSid,
|
|
196
179
|
)}/IncomingPhoneNumbers.json?PhoneNumber=${encodedNumber}`,
|
|
197
180
|
{
|
|
198
181
|
method: "GET",
|
|
199
182
|
headers: {
|
|
200
|
-
Authorization:
|
|
183
|
+
Authorization: twilioAuthHeader(accountSid, authToken),
|
|
201
184
|
},
|
|
202
185
|
},
|
|
203
186
|
);
|
|
@@ -223,13 +206,13 @@ export class TwilioVoiceProvider implements VoiceProvider {
|
|
|
223
206
|
|
|
224
207
|
// Check outgoing caller IDs (verified with this account)
|
|
225
208
|
const outgoingRes = await fetch(
|
|
226
|
-
`${
|
|
209
|
+
`${twilioBaseUrl(
|
|
227
210
|
accountSid,
|
|
228
211
|
)}/OutgoingCallerIds.json?PhoneNumber=${encodedNumber}`,
|
|
229
212
|
{
|
|
230
213
|
method: "GET",
|
|
231
214
|
headers: {
|
|
232
|
-
Authorization:
|
|
215
|
+
Authorization: twilioAuthHeader(accountSid, authToken),
|
|
233
216
|
},
|
|
234
217
|
},
|
|
235
218
|
);
|
|
@@ -41,6 +41,10 @@ import {
|
|
|
41
41
|
pendingStandaloneImagePersist,
|
|
42
42
|
SIGHT_FRAME_TURN_HOLD_MS,
|
|
43
43
|
} from "../live-voice/live-voice-photo.js";
|
|
44
|
+
import {
|
|
45
|
+
formatShareTargetsForPrompt,
|
|
46
|
+
type ShareTargetSnapshot,
|
|
47
|
+
} from "../live-voice/share-targets.js";
|
|
44
48
|
import { resolveAttachmentsForPersist } from "../persistence/attachments-store.js";
|
|
45
49
|
import {
|
|
46
50
|
deleteMessageById,
|
|
@@ -462,6 +466,13 @@ export interface VoiceTurnOptions {
|
|
|
462
466
|
macosDesktopSession?: boolean;
|
|
463
467
|
/** The desktop client currently shares a surface with this voice session. */
|
|
464
468
|
screenSharing?: boolean;
|
|
469
|
+
/**
|
|
470
|
+
* The controls the shared surface offers to be pointed at, from the
|
|
471
|
+
* client's newest read of its accessibility tree. Offered alongside the
|
|
472
|
+
* screen-annotation instructions so the first `screen_point_at` names a
|
|
473
|
+
* control the lookup can resolve.
|
|
474
|
+
*/
|
|
475
|
+
shareTargets?: ShareTargetSnapshot;
|
|
465
476
|
/** Front-door verdict: this action needs only the shared screen and current context. */
|
|
466
477
|
screenAction?: boolean;
|
|
467
478
|
/** Whether this is an inbound call (no outbound task). */
|
|
@@ -2145,8 +2156,14 @@ export async function startVoiceTurn(
|
|
|
2145
2156
|
signal: opts.signal,
|
|
2146
2157
|
});
|
|
2147
2158
|
if (annotation !== null) {
|
|
2159
|
+
const controls =
|
|
2160
|
+
opts.shareTargets !== undefined
|
|
2161
|
+
? formatShareTargetsForPrompt(opts.shareTargets)
|
|
2162
|
+
: null;
|
|
2148
2163
|
conversation.setVoiceCallControlPrompt(
|
|
2149
|
-
[voiceCallControlPrompt, annotation]
|
|
2164
|
+
[voiceCallControlPrompt, annotation, controls]
|
|
2165
|
+
.filter(Boolean)
|
|
2166
|
+
.join("\n\n"),
|
|
2150
2167
|
);
|
|
2151
2168
|
}
|
|
2152
2169
|
}
|
|
@@ -214,8 +214,7 @@ function attachOperationAction(
|
|
|
214
214
|
opts.output &&
|
|
215
215
|
result.screenshots?.length
|
|
216
216
|
) {
|
|
217
|
-
const
|
|
218
|
-
const buffer = Buffer.from(screenshot.data, "base64");
|
|
217
|
+
const buffer = Buffer.from(result.screenshots[0].data, "base64");
|
|
219
218
|
try {
|
|
220
219
|
writeFileSync(String(opts.output), buffer);
|
|
221
220
|
} catch (err) {
|
|
@@ -123,6 +123,23 @@ describe("parsePluginArtifact", () => {
|
|
|
123
123
|
});
|
|
124
124
|
});
|
|
125
125
|
|
|
126
|
+
test("ignores a malformed icon beside a valid artifact", () => {
|
|
127
|
+
expect(
|
|
128
|
+
parsePluginArtifact({
|
|
129
|
+
vellum: {
|
|
130
|
+
artifact: {
|
|
131
|
+
url: "https://example.com/App.dmg",
|
|
132
|
+
sha256: VALID_SHA,
|
|
133
|
+
},
|
|
134
|
+
icon: 42,
|
|
135
|
+
},
|
|
136
|
+
}),
|
|
137
|
+
).toEqual({
|
|
138
|
+
url: "https://example.com/App.dmg",
|
|
139
|
+
sha256: VALID_SHA,
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
|
|
126
143
|
test.each([
|
|
127
144
|
["no vellum block", { name: "x" }],
|
|
128
145
|
["no artifact block", { vellum: {} }],
|
|
@@ -202,6 +219,12 @@ describe("parsePluginIcon", () => {
|
|
|
202
219
|
expect(parsePluginIcon({ vellum: { icon: sixteen } })).toBe(sixteen);
|
|
203
220
|
});
|
|
204
221
|
|
|
222
|
+
test("ignores a malformed artifact beside a valid icon", () => {
|
|
223
|
+
expect(
|
|
224
|
+
parsePluginIcon({ vellum: { artifact: "App.dmg", icon: "🚀" } }),
|
|
225
|
+
).toBe("🚀");
|
|
226
|
+
});
|
|
227
|
+
|
|
205
228
|
test.each([
|
|
206
229
|
["no vellum block", { name: "x" }],
|
|
207
230
|
["no icon field", { vellum: {} }],
|
|
@@ -33,6 +33,7 @@ describe("filterPluginCatalogByFeatureFlags", () => {
|
|
|
33
33
|
"ramp",
|
|
34
34
|
"semrush",
|
|
35
35
|
"typeform",
|
|
36
|
+
"upwork",
|
|
36
37
|
"notion",
|
|
37
38
|
]);
|
|
38
39
|
|
|
@@ -56,6 +57,7 @@ describe("filterPluginCatalogByFeatureFlags", () => {
|
|
|
56
57
|
"ramp",
|
|
57
58
|
"semrush",
|
|
58
59
|
"typeform",
|
|
60
|
+
"upwork",
|
|
59
61
|
]);
|
|
60
62
|
|
|
61
63
|
const result = filterPluginCatalogByFeatureFlags(input, () => true);
|
|
@@ -309,6 +309,22 @@ describe("getPluginDetails (bundled catalog, offline)", () => {
|
|
|
309
309
|
expect(details.icon).toBeNull();
|
|
310
310
|
});
|
|
311
311
|
|
|
312
|
+
test("reads a legacy object-form license from an installed package.json", async () => {
|
|
313
|
+
const target = join(workspace, "caveman");
|
|
314
|
+
mkdirSync(target, { recursive: true });
|
|
315
|
+
writeFileSync(
|
|
316
|
+
join(target, "package.json"),
|
|
317
|
+
JSON.stringify({ version: "2.0.0", license: { type: "MIT" } }),
|
|
318
|
+
);
|
|
319
|
+
|
|
320
|
+
const details = await getPluginDetails(
|
|
321
|
+
{ name: "caveman" },
|
|
322
|
+
{ fetch: makeFetch({}), workspacePluginsDir: workspace },
|
|
323
|
+
);
|
|
324
|
+
|
|
325
|
+
expect(details.license).toBe("MIT");
|
|
326
|
+
});
|
|
327
|
+
|
|
312
328
|
test("reads metadata from an installed standard-only plugin.json", async () => {
|
|
313
329
|
const target = join(workspace, "caveman");
|
|
314
330
|
mkdirSync(target, { recursive: true });
|
|
@@ -135,6 +135,32 @@
|
|
|
135
135
|
"homepage": "https://github.com/topoteretes/cognee-integrations/tree/main/integrations/vellum-assistant",
|
|
136
136
|
"icon": "\ud83d\udd78\ufe0f"
|
|
137
137
|
},
|
|
138
|
+
{
|
|
139
|
+
"name": "contractor-missed-call-dispatcher",
|
|
140
|
+
"description": "Captures missed trade contractor inbound calls, fires an instant 5-second SMS qualification text-back, triages emergencies, and stages draft jobs in Jobber or ServiceTitan.",
|
|
141
|
+
"category": "commerce",
|
|
142
|
+
"icon": "📲",
|
|
143
|
+
"license": "MIT",
|
|
144
|
+
"homepage": "https://github.com/ZeebBoyBlue/contractor-missed-call-dispatcher",
|
|
145
|
+
"source": {
|
|
146
|
+
"source": "github",
|
|
147
|
+
"repo": "ZeebBoyBlue/contractor-missed-call-dispatcher",
|
|
148
|
+
"ref": "2d293cdcb68af03d446b61a9915a0ee3dc842d4e"
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
"name": "contractor-photo-to-quote",
|
|
153
|
+
"description": "Turns job-site and equipment photos into standardized trade estimates and quotes in Jobber or QuickBooks with multi-tier Good/Better/Best options in 5 minutes.",
|
|
154
|
+
"category": "commerce",
|
|
155
|
+
"icon": "📸",
|
|
156
|
+
"license": "MIT",
|
|
157
|
+
"homepage": "https://github.com/ZeebBoyBlue/contractor-photo-to-quote",
|
|
158
|
+
"source": {
|
|
159
|
+
"source": "github",
|
|
160
|
+
"repo": "ZeebBoyBlue/contractor-photo-to-quote",
|
|
161
|
+
"ref": "6c136186faaf03bcf125d202f0b1f51f97d94544"
|
|
162
|
+
}
|
|
163
|
+
},
|
|
138
164
|
{
|
|
139
165
|
"name": "contractor-voice-invoicer",
|
|
140
166
|
"description": "Converts 20-second truck voice memos and job photos into itemized client invoices with custom labor rates, material markups, and one-click accounting staging.",
|
|
@@ -461,6 +487,19 @@
|
|
|
461
487
|
"license": "MIT",
|
|
462
488
|
"icon": "\ud83d\udcac"
|
|
463
489
|
},
|
|
490
|
+
{
|
|
491
|
+
"name": "quickbooks-expense-categorizer",
|
|
492
|
+
"description": "Scans uncategorized QuickBooks Online expenses and bank feeds, matches receipts, and stages categorized entries with confidence scoring for 1-click batch review.",
|
|
493
|
+
"category": "commerce",
|
|
494
|
+
"icon": "🧾",
|
|
495
|
+
"license": "MIT",
|
|
496
|
+
"homepage": "https://github.com/ZeebBoyBlue/quickbooks-expense-categorizer",
|
|
497
|
+
"source": {
|
|
498
|
+
"source": "github",
|
|
499
|
+
"repo": "ZeebBoyBlue/quickbooks-expense-categorizer",
|
|
500
|
+
"ref": "120776619698cc3e9490caf5c756465f5caaa6c2"
|
|
501
|
+
}
|
|
502
|
+
},
|
|
464
503
|
{
|
|
465
504
|
"name": "reading-pal",
|
|
466
505
|
"source": {
|
|
@@ -487,6 +526,19 @@
|
|
|
487
526
|
"ref": "f1905e639941e98172c5032b6d722f668882d75d"
|
|
488
527
|
}
|
|
489
528
|
},
|
|
529
|
+
{
|
|
530
|
+
"name": "shopify-payout-reconciler",
|
|
531
|
+
"description": "Unbundles lump-sum Shopify merchant bank deposits into gross sales, processor fees, customer refunds, shipping labels, and sales tax for penny-accurate QuickBooks reconciliation.",
|
|
532
|
+
"category": "commerce",
|
|
533
|
+
"icon": "🛍️",
|
|
534
|
+
"license": "MIT",
|
|
535
|
+
"homepage": "https://github.com/ZeebBoyBlue/shopify-payout-reconciler",
|
|
536
|
+
"source": {
|
|
537
|
+
"source": "github",
|
|
538
|
+
"repo": "ZeebBoyBlue/shopify-payout-reconciler",
|
|
539
|
+
"ref": "079fe0d86629686243a588cf3c4867d38d6ab8b8"
|
|
540
|
+
}
|
|
541
|
+
},
|
|
490
542
|
{
|
|
491
543
|
"name": "simple-memory",
|
|
492
544
|
"source": {
|
|
@@ -1368,9 +1420,9 @@
|
|
|
1368
1420
|
"source": {
|
|
1369
1421
|
"source": "github",
|
|
1370
1422
|
"repo": "vellum-ai/runway-plugin",
|
|
1371
|
-
"ref": "
|
|
1423
|
+
"ref": "bb017a1e018c7be140c02c8463273c2d85f3b9d9"
|
|
1372
1424
|
},
|
|
1373
|
-
"description": "
|
|
1425
|
+
"description": "Securely set up, collaboratively direct, approve, create, track, cancel, and download Runway Gen-4.5 videos.",
|
|
1374
1426
|
"category": "other",
|
|
1375
1427
|
"submittedBy": "marina"
|
|
1376
1428
|
},
|
|
@@ -16,6 +16,8 @@
|
|
|
16
16
|
* is a thin wrapper that supplies production deps and formats the result.
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
+
import { z } from "zod";
|
|
20
|
+
|
|
19
21
|
import { PRESERVED_ENTRIES } from "../../plugins/plugin-tree-walk.js";
|
|
20
22
|
import type { FetchLike } from "./fetch-like.js";
|
|
21
23
|
import {
|
|
@@ -50,6 +52,14 @@ import type { PluginCatalog, PluginSearchMatch } from "./search-plugins.js";
|
|
|
50
52
|
/** Full commit SHA (40 hex SHA-1 or 64 hex SHA-256). */
|
|
51
53
|
const FULL_SHA_RE = /^(?:[0-9a-f]{40}|[0-9a-f]{64})$/i;
|
|
52
54
|
|
|
55
|
+
const GitHubCommitResponseSchema = z.object({
|
|
56
|
+
commit: z.object({
|
|
57
|
+
committer: z.object({
|
|
58
|
+
date: z.string(),
|
|
59
|
+
}),
|
|
60
|
+
}),
|
|
61
|
+
});
|
|
62
|
+
|
|
53
63
|
/**
|
|
54
64
|
* Drift classification between the installed copy and the marketplace pin.
|
|
55
65
|
*
|
|
@@ -294,23 +304,13 @@ export async function fetchCommitDate(
|
|
|
294
304
|
if (!res.ok) {
|
|
295
305
|
return null;
|
|
296
306
|
}
|
|
297
|
-
const
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
const commit = (json as Record<string, unknown>).commit;
|
|
302
|
-
if (typeof commit !== "object" || commit === null) {
|
|
303
|
-
return null;
|
|
304
|
-
}
|
|
305
|
-
const committer = (commit as Record<string, unknown>).committer;
|
|
306
|
-
if (typeof committer !== "object" || committer === null) {
|
|
307
|
-
return null;
|
|
308
|
-
}
|
|
309
|
-
const date = (committer as Record<string, unknown>).date;
|
|
310
|
-
if (typeof date !== "string") {
|
|
307
|
+
const parsed = GitHubCommitResponseSchema.safeParse(
|
|
308
|
+
JSON.parse(await res.text()),
|
|
309
|
+
);
|
|
310
|
+
if (!parsed.success) {
|
|
311
311
|
return null;
|
|
312
312
|
}
|
|
313
|
-
const ms = Date.parse(date);
|
|
313
|
+
const ms = Date.parse(parsed.data.commit.committer.date);
|
|
314
314
|
return Number.isFinite(ms) ? new Date(ms).toISOString() : null;
|
|
315
315
|
} catch {
|
|
316
316
|
return null;
|
|
@@ -16,12 +16,14 @@ import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
|
16
16
|
import { dirname, join } from "node:path";
|
|
17
17
|
import { fileURLToPath } from "node:url";
|
|
18
18
|
|
|
19
|
+
import { z } from "zod";
|
|
20
|
+
|
|
19
21
|
import { getWorkspacePluginsDir } from "../../util/platform.js";
|
|
20
22
|
import {
|
|
21
23
|
LEGACY_PLUGIN_MANIFEST,
|
|
22
24
|
readPluginManifest,
|
|
23
25
|
} from "../../util/plugin-manifest.js";
|
|
24
|
-
import {
|
|
26
|
+
import { PluginPackageJsonSchema } from "./plugin-artifact.js";
|
|
25
27
|
import { readValidatedPluginIcon } from "./plugin-icon-file.js";
|
|
26
28
|
|
|
27
29
|
/**
|
|
@@ -39,6 +41,15 @@ const DEFAULT_PLUGINS_DIR = join(
|
|
|
39
41
|
"defaults",
|
|
40
42
|
);
|
|
41
43
|
|
|
44
|
+
const DefaultPluginManifestSchema = z.object({
|
|
45
|
+
name: z.string(),
|
|
46
|
+
version: z.string().optional().catch(undefined),
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
const InstallMetadataSchema = z.object({
|
|
50
|
+
installedAt: z.string(),
|
|
51
|
+
});
|
|
52
|
+
|
|
42
53
|
/** Minimal manifest fields surfaced to the CLI. */
|
|
43
54
|
export interface PluginPackageMetadata {
|
|
44
55
|
readonly name?: string;
|
|
@@ -163,23 +174,17 @@ function readPluginEntry(
|
|
|
163
174
|
issues.push(err instanceof Error ? err.message : String(err));
|
|
164
175
|
return { name, target, packageJson: null, issues, ...iconFields };
|
|
165
176
|
}
|
|
166
|
-
const
|
|
167
|
-
const
|
|
168
|
-
manifest.source === LEGACY_PLUGIN_MANIFEST
|
|
169
|
-
?
|
|
170
|
-
:
|
|
177
|
+
const parsedPackageJson = PluginPackageJsonSchema.safeParse(manifest.raw);
|
|
178
|
+
const legacyMetadata =
|
|
179
|
+
manifest.source === LEGACY_PLUGIN_MANIFEST && parsedPackageJson.success
|
|
180
|
+
? parsedPackageJson.data
|
|
181
|
+
: null;
|
|
171
182
|
const packageJson: PluginPackageMetadata = {
|
|
172
183
|
name: manifest.name,
|
|
173
184
|
version: manifest.version,
|
|
174
185
|
description: manifest.description,
|
|
175
|
-
peerDependencies:
|
|
176
|
-
|
|
177
|
-
typeof meta.peerDependencies === "object" &&
|
|
178
|
-
meta.peerDependencies !== null &&
|
|
179
|
-
!Array.isArray(meta.peerDependencies)
|
|
180
|
-
? (meta.peerDependencies as Record<string, string>)
|
|
181
|
-
: undefined,
|
|
182
|
-
...(icon ? { icon } : {}),
|
|
186
|
+
peerDependencies: legacyMetadata?.peerDependencies,
|
|
187
|
+
...(legacyMetadata?.icon ? { icon: legacyMetadata.icon } : {}),
|
|
183
188
|
};
|
|
184
189
|
|
|
185
190
|
return { name, target, packageJson, issues, ...iconFields };
|
|
@@ -319,14 +324,11 @@ function readDefaultPluginManifests(): readonly DefaultPluginManifest[] {
|
|
|
319
324
|
continue;
|
|
320
325
|
}
|
|
321
326
|
try {
|
|
322
|
-
const
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
version:
|
|
328
|
-
typeof parsed.version === "string" ? parsed.version : undefined,
|
|
329
|
-
});
|
|
327
|
+
const parsed = DefaultPluginManifestSchema.safeParse(
|
|
328
|
+
JSON.parse(readFileSync(pkgJsonPath, "utf8")),
|
|
329
|
+
);
|
|
330
|
+
if (parsed.success) {
|
|
331
|
+
manifests.push(parsed.data);
|
|
330
332
|
}
|
|
331
333
|
} catch {
|
|
332
334
|
// Skip malformed entries — lenient like listInstalledPlugins.
|
|
@@ -345,12 +347,11 @@ function getPluginInstallDate(plugin: AllPluginInfo): number {
|
|
|
345
347
|
const metaPath = join(plugin.target, "install-meta.json");
|
|
346
348
|
try {
|
|
347
349
|
if (existsSync(metaPath)) {
|
|
348
|
-
const
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
const ms = Date.parse(raw.installedAt);
|
|
350
|
+
const parsed = InstallMetadataSchema.safeParse(
|
|
351
|
+
JSON.parse(readFileSync(metaPath, "utf8")),
|
|
352
|
+
);
|
|
353
|
+
if (parsed.success) {
|
|
354
|
+
const ms = Date.parse(parsed.data.installedAt);
|
|
354
355
|
if (Number.isFinite(ms)) {
|
|
355
356
|
return ms;
|
|
356
357
|
}
|