@vellumai/assistant 0.9.1-staging.1 → 0.10.0-staging.2
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/docs/activation-funnel-telemetry.md +24 -18
- package/node_modules/@vellumai/gateway-client/src/admission-policy-contract.ts +97 -0
- package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +10 -0
- package/node_modules/@vellumai/gateway-client/src/index.ts +15 -0
- package/openapi.yaml +852 -15
- package/package.json +1 -1
- package/src/__tests__/access-request-card-view.test.ts +98 -0
- package/src/__tests__/access-request-seed-content-blocks.test.ts +2 -4
- package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +59 -7
- package/src/__tests__/agent-loop-compaction-strip.test.ts +17 -16
- package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +16 -13
- package/src/__tests__/app-compiler.test.ts +15 -1
- package/src/__tests__/auth-fallback-events-store.test.ts +6 -14
- package/src/__tests__/call-pointer-messages.test.ts +28 -0
- package/src/__tests__/cancel-clears-processing.test.ts +89 -0
- package/src/__tests__/channel-approval-routes.test.ts +0 -4
- package/src/__tests__/channel-inbound-disk-pressure.test.ts +5 -15
- package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +3 -4
- package/src/__tests__/compactor-image-manifest-trust.test.ts +21 -1
- package/src/__tests__/compactor-summary-call-truncation.test.ts +223 -0
- package/src/__tests__/config-loader-backfill.test.ts +174 -30
- package/src/__tests__/config-schema.test.ts +35 -0
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +2 -2
- package/src/__tests__/contact-store-user-file.test.ts +0 -6
- package/src/__tests__/contacts-tools.test.ts +29 -0
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -0
- package/src/__tests__/conversation-agent-loop.test.ts +58 -0
- package/src/__tests__/conversation-attention-telegram.test.ts +0 -1
- package/src/__tests__/conversation-lifecycle.test.ts +7 -9
- package/src/__tests__/conversation-load-history-repair.test.ts +101 -0
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +15 -12
- package/src/__tests__/conversation-surfaces-activation-emit.test.ts +6 -3
- package/src/__tests__/conversation-title-service.test.ts +62 -0
- package/src/__tests__/credential-execution-shell-lockdown.test.ts +18 -11
- package/src/__tests__/credential-prompt-route.test.ts +1 -0
- package/src/__tests__/credential-security-invariants.test.ts +2 -0
- package/src/__tests__/disk-pressure-policy.test.ts +12 -0
- package/src/__tests__/disk-usage.test.ts +65 -0
- package/src/__tests__/dynamic-page-surface.test.ts +51 -0
- package/src/__tests__/gateway-flag-listener.test.ts +110 -1
- package/src/__tests__/guardian-binding-drift-heal.test.ts +1 -1
- package/src/__tests__/guardian-card-withdrawal.test.ts +403 -0
- package/src/__tests__/guardian-decision-primitive-canonical.test.ts +5 -3
- package/src/__tests__/guardian-grant-minting.test.ts +3 -35
- package/src/__tests__/guardian-routing-invariants.test.ts +64 -26
- package/src/__tests__/guardian-routing-state.test.ts +0 -1
- package/src/__tests__/headless-browser-mode.test.ts +10 -0
- package/src/__tests__/headless-browser-navigate.test.ts +8 -3
- package/src/__tests__/helpers/create-guardian-binding.ts +0 -1
- package/src/__tests__/host-browser-proxy.test.ts +87 -0
- package/src/__tests__/injector-v3-suppression.test.ts +27 -20
- package/src/__tests__/internal-telemetry-routes.test.ts +6 -14
- package/src/__tests__/invite-redemption-service.test.ts +0 -3
- package/src/__tests__/llm-catalog-parity.test.ts +30 -1
- package/src/__tests__/llm-resolver.test.ts +21 -0
- package/src/__tests__/llm-schema.test.ts +1 -0
- package/src/__tests__/managed-profile-guard.test.ts +163 -4
- package/src/__tests__/mcp-health-check.test.ts +6 -7
- package/src/__tests__/media-stream-server-integration.test.ts +317 -13
- package/src/__tests__/path-policy.test.ts +34 -0
- package/src/__tests__/persona-resolver.test.ts +38 -0
- package/src/__tests__/plugin-api-provider.test.ts +24 -0
- package/src/__tests__/plugin-tool-contribution.test.ts +6 -3
- package/src/__tests__/post-compaction-reinjection-idempotency.test.ts +214 -0
- package/src/__tests__/provider-send-message-override-profile.test.ts +76 -0
- package/src/__tests__/reaction-persistence.test.ts +150 -29
- package/src/__tests__/relay-server.test.ts +285 -0
- package/src/__tests__/runtime-attachment-metadata.test.ts +0 -1
- package/src/__tests__/scheduler-reuse-conversation.test.ts +8 -5
- package/src/__tests__/skill-execute-input.test.ts +5 -0
- package/src/__tests__/skills.test.ts +51 -0
- package/src/__tests__/slack-notification-approval-card.test.ts +176 -0
- package/src/__tests__/slack-reaction-canonical-approval.test.ts +285 -0
- package/src/__tests__/subagent-tools.test.ts +150 -0
- package/src/__tests__/task-progress-nudge-hook.test.ts +1 -1
- package/src/__tests__/title-generate-hook.test.ts +100 -3
- package/src/__tests__/tool-approval-seed-content-blocks.test.ts +1 -1
- package/src/__tests__/tool-audit-listener.test.ts +7 -7
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +6 -3
- package/src/__tests__/trusted-contact-approval-notifier.test.ts +4 -2
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +220 -3
- package/src/__tests__/trusted-contact-verification.test.ts +2 -4
- package/src/__tests__/twilio-routes.test.ts +81 -1
- package/src/__tests__/voice-invite-redemption.test.ts +0 -1
- package/src/__tests__/weak-open-model.test.ts +30 -0
- package/src/__tests__/workspace-migration-105-enable-memory-v3-live-for-new-workspaces.test.ts +149 -0
- package/src/__tests__/workspace-migration-108-drop-balanced-economy-profile.test.ts +285 -0
- package/src/__tests__/workspace-migration-add-send-diagnostics.test.ts +1 -1
- package/src/__tests__/workspace-migration-drop-collect-usage-data.test.ts +118 -0
- package/src/__tests__/workspace-migration-drop-send-diagnostics.test.ts +118 -0
- package/src/a2a/__tests__/e2e-a2a-channel.test.ts +0 -4
- package/src/agent/loop.ts +33 -33
- package/src/api/events/tool-result.ts +6 -0
- package/src/api/events/workflow-completed.ts +53 -0
- package/src/api/events/workflow-leaf-finished.ts +38 -0
- package/src/api/events/workflow-leaf-started.ts +35 -0
- package/src/api/events/workflow-progress.ts +32 -0
- package/src/api/events/workflow-started.ts +31 -0
- package/src/api/index.ts +40 -0
- package/src/api/responses/conversation-message.ts +26 -0
- package/src/api/responses/home.ts +26 -0
- package/src/api/responses/workflow-journal.ts +53 -0
- package/src/approvals/guardian-card-withdrawal.ts +145 -0
- package/src/approvals/guardian-decision-primitive.ts +26 -3
- package/src/approvals/guardian-request-resolvers.ts +181 -78
- package/src/calls/__tests__/channel-admission-reader.test.ts +132 -0
- package/src/calls/__tests__/relay-setup-router.test.ts +350 -0
- package/src/calls/call-pointer-messages.ts +10 -4
- package/src/calls/channel-admission-reader.ts +104 -0
- package/src/calls/guardian-dispatch.ts +17 -45
- package/src/calls/media-stream-server.ts +84 -2
- package/src/calls/relay-server.ts +66 -0
- package/src/calls/relay-setup-router.ts +82 -1
- package/src/calls/twilio-routes.ts +17 -8
- package/src/cli/commands/clients.ts +3 -0
- package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v2-compare-render.test.ts +1 -1
- package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v2.test.ts +8 -7
- package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v3.test.ts +5 -4
- package/src/cli/commands/memory/index.ts +30 -0
- package/src/cli/commands/{memory-v2-compare-render.ts → memory/memory-v2-compare-render.ts} +1 -1
- package/src/cli/commands/{memory-v2.ts → memory/memory-v2.ts} +6 -15
- package/src/cli/commands/{memory-v3.ts → memory/memory-v3.ts} +97 -11
- package/src/cli/commands/oauth/status.test.ts +36 -0
- package/src/cli/commands/oauth/status.ts +23 -3
- package/src/cli/commands/plugins.ts +57 -5
- package/src/cli/lib/__tests__/inspect-plugin.test.ts +54 -0
- package/src/cli/lib/__tests__/merge-plugin-tree.test.ts +134 -4
- package/src/cli/lib/__tests__/plugin-surfaces.test.ts +111 -0
- package/src/cli/lib/__tests__/upgrade-plugin.test.ts +53 -11
- package/src/cli/lib/inspect-plugin.ts +12 -1
- package/src/cli/lib/merge-plugin-tree.ts +149 -49
- package/src/cli/lib/plugin-surfaces.ts +104 -0
- package/src/cli/lib/upgrade-plugin.ts +64 -36
- package/src/cli/program.ts +2 -4
- package/src/config/__tests__/sync-gated-profiles.test.ts +368 -0
- package/src/config/assistant-feature-flags.ts +22 -7
- package/src/config/bundled-skills/contacts/tools/contact-search.ts +0 -1
- package/src/config/bundled-skills/messaging/SKILL.md +6 -4
- package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +9 -8
- package/src/config/bundled-skills/workflows/SKILL.md +14 -7
- package/src/config/call-site-defaults.ts +3 -0
- package/src/config/feature-flag-registry.json +49 -18
- package/src/config/llm-resolver.ts +3 -0
- package/src/config/memory-v3-gate.ts +11 -0
- package/src/config/schema.ts +8 -6
- package/src/config/schemas/__tests__/memory-v3.test.ts +1 -0
- package/src/config/schemas/call-site-catalog.ts +7 -0
- package/src/config/schemas/channels.ts +11 -0
- package/src/config/schemas/llm.ts +31 -0
- package/src/config/schemas/memory-lifecycle.ts +3 -7
- package/src/config/schemas/memory-v3.ts +6 -0
- package/src/config/schemas/services.ts +18 -0
- package/src/config/seed-inference-profiles.ts +94 -34
- package/src/config/skills.ts +21 -0
- package/src/config/sync-gated-profiles.ts +220 -0
- package/src/contacts/contact-store.ts +2 -10
- package/src/contacts/contacts-write.ts +1 -2
- package/src/contacts/types.ts +0 -1
- package/src/context/compactor.ts +86 -52
- package/src/context/strip-injections.ts +58 -10
- package/src/context/token-estimator.ts +1 -1
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +3 -2
- package/src/daemon/conversation-agent-loop-handlers.ts +2 -0
- package/src/daemon/conversation-agent-loop.ts +100 -19
- package/src/daemon/conversation-history.ts +1 -1
- package/src/daemon/conversation-lifecycle.ts +3 -5
- package/src/daemon/conversation-process.ts +13 -5
- package/src/daemon/conversation-runtime-assembly.ts +13 -15
- package/src/daemon/conversation-surfaces.ts +26 -0
- package/src/daemon/conversation-tool-setup.ts +16 -11
- package/src/daemon/conversation.ts +64 -14
- package/src/daemon/disk-pressure-policy.ts +5 -3
- package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +0 -1
- package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +0 -1
- package/src/daemon/handlers/config-a2a.ts +0 -2
- package/src/daemon/handlers/config-channels.ts +5 -10
- package/src/daemon/handlers/config-slack-channel.ts +20 -0
- package/src/daemon/handlers/conversations.ts +107 -0
- package/src/daemon/host-browser-proxy.ts +41 -0
- package/src/daemon/lifecycle.ts +55 -20
- package/src/daemon/message-provenance.ts +2 -0
- package/src/daemon/message-types/contacts.ts +0 -1
- package/src/daemon/message-types/web-activity.ts +7 -1
- package/src/daemon/message-types/workflows.ts +83 -1
- package/src/daemon/tool-setup-types.ts +4 -0
- package/src/daemon/trust-context.ts +1 -1
- package/src/events/tool-audit-listener.ts +2 -2
- package/src/home/feed-source-enrichment.test.ts +151 -0
- package/src/home/feed-source-enrichment.ts +176 -0
- package/src/instrument.ts +18 -6
- package/src/ipc/__tests__/binary-result-ipc.test.ts +81 -0
- package/src/ipc/__tests__/clients-list-ipc.test.ts +20 -0
- package/src/ipc/assistant-server.ts +37 -4
- package/src/ipc/gateway-flag-listener.ts +18 -2
- package/src/memory/__tests__/auto-analysis-enqueue.test.ts +5 -16
- package/src/memory/__tests__/jobs-store-enqueue-gate.test.ts +7 -11
- package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +37 -7
- package/src/memory/__tests__/memory-retrospective-job.test.ts +34 -0
- package/src/memory/__tests__/onboarding-events-store.test.ts +7 -7
- package/src/memory/auth-fallback-events-store.ts +2 -2
- package/src/memory/auto-analysis-enqueue.ts +3 -5
- package/src/memory/canonical-guardian-store.ts +39 -1
- package/src/memory/conversation-crud.ts +9 -4
- package/src/memory/conversation-key-store.ts +17 -2
- package/src/memory/conversation-title-service.ts +64 -7
- package/src/memory/db-init.ts +10 -0
- package/src/memory/embedding-backend.ts +15 -1
- package/src/memory/jobs-worker.ts +2 -1
- package/src/memory/lifecycle-events-store.ts +2 -2
- package/src/memory/memory-retrospective-enqueue.ts +31 -6
- package/src/memory/memory-retrospective-job.ts +9 -0
- package/src/memory/migrations/129-contact-channels-access-fields.ts +18 -9
- package/src/memory/migrations/131-drop-legacy-member-guardian-tables.ts +14 -2
- package/src/memory/migrations/289-contact-channels-unique-ext-user.ts +10 -0
- package/src/memory/migrations/291-contact-channels-renormalize-addresses.ts +10 -0
- package/src/memory/migrations/292-schedule-default-no-reuse-conversation.test.ts +67 -0
- package/src/memory/migrations/292-schedule-default-no-reuse-conversation.ts +25 -0
- package/src/memory/migrations/293-workflow-journal-leaf-tokens.ts +32 -0
- package/src/memory/migrations/294-drop-external-user-id.ts +31 -0
- package/src/memory/migrations/295-drop-approval-prompt-ts-tracker.ts +20 -0
- package/src/memory/migrations/296-rewrite-balanced-economy-profile-pins.test.ts +110 -0
- package/src/memory/migrations/296-rewrite-balanced-economy-profile-pins.ts +68 -0
- package/src/memory/migrations/__tests__/131-drop-legacy-member-guardian-tables.test.ts +154 -0
- package/src/memory/migrations/__tests__/289-contact-channels-unique-ext-user.test.ts +31 -0
- package/src/memory/migrations/__tests__/291-contact-channels-renormalize-addresses.test.ts +30 -0
- package/src/memory/migrations/index.ts +5 -0
- package/src/memory/onboarding-events-store.ts +3 -3
- package/src/memory/schema/contacts.ts +0 -1
- package/src/memory/skill-loaded-events-store.test.ts +7 -15
- package/src/memory/skill-loaded-events-store.ts +2 -2
- package/src/memory/tool-executed-events-store.test.ts +7 -7
- package/src/memory/turn-trace-store.test.ts +736 -0
- package/src/memory/turn-trace-store.ts +364 -0
- package/src/memory/v2/__tests__/consolidation-job.test.ts +8 -0
- package/src/memory/v2/__tests__/skill-content.test.ts +30 -0
- package/src/memory/v2/consolidation-job.ts +2 -2
- package/src/memory/v2/skill-content.ts +25 -7
- package/src/memory/v2/skill-store.ts +7 -1
- package/src/memory/v3-eval/__tests__/eval-packets.test.ts +248 -0
- package/src/memory/v3-eval/eval-packets.ts +546 -0
- package/src/messaging/providers/slack/api.ts +31 -0
- package/src/messaging/providers/slack/send.test.ts +114 -2
- package/src/messaging/providers/slack/send.ts +30 -7
- package/src/messaging/providers/slack/withdraw.test.ts +200 -0
- package/src/messaging/providers/slack/withdraw.ts +161 -0
- package/src/notifications/AGENTS.md +2 -0
- package/src/notifications/access-request-copy.ts +72 -59
- package/src/notifications/adapters/slack.ts +55 -73
- package/src/notifications/approval-card-data.ts +333 -0
- package/src/notifications/broadcaster.ts +6 -2
- package/src/notifications/canonical-delivery-recorder.ts +139 -0
- package/src/notifications/copy-composer.ts +3 -3
- package/src/notifications/decision-engine.ts +4 -2
- package/src/notifications/destination-resolver.ts +4 -6
- package/src/notifications/guardian-question-mode.ts +10 -0
- package/src/notifications/home-feed-side-effect.ts +3 -13
- package/src/notifications/notification-utils.ts +2 -1
- package/src/notifications/signal.ts +79 -43
- package/src/notifications/types.ts +98 -128
- package/src/permissions/checker.test.ts +51 -0
- package/src/permissions/checker.ts +185 -26
- package/src/permissions/ipc-risk-types.ts +24 -0
- package/src/permissions/question-prompter.test.ts +27 -0
- package/src/permissions/question-prompter.ts +4 -0
- package/src/platform/client.test.ts +119 -0
- package/src/platform/client.ts +66 -0
- package/src/platform/consent-cache.test.ts +267 -0
- package/src/platform/consent-cache.ts +174 -0
- package/src/plugin-api/index.ts +27 -0
- package/src/plugins/defaults/advisor/__tests__/advisor-gate.test.ts +56 -0
- package/src/plugins/defaults/advisor/__tests__/advisor-state-store.test.ts +43 -0
- package/src/plugins/defaults/advisor/__tests__/agent-loop-integration.test.ts +137 -0
- package/src/plugins/defaults/advisor/__tests__/consult.test.ts +153 -0
- package/src/plugins/defaults/advisor/__tests__/hooks.test.ts +138 -0
- package/src/plugins/defaults/advisor/__tests__/transcript.test.ts +147 -0
- package/src/plugins/defaults/advisor/advisor-gate.ts +29 -0
- package/src/plugins/defaults/advisor/advisor-state-store.ts +94 -0
- package/src/plugins/defaults/advisor/config.ts +21 -0
- package/src/plugins/defaults/advisor/consult.ts +93 -0
- package/src/plugins/defaults/advisor/hooks/post-model-call.ts +34 -0
- package/src/plugins/defaults/advisor/hooks/pre-model-call.ts +30 -0
- package/src/plugins/defaults/advisor/hooks/user-prompt-submit.ts +19 -0
- package/src/plugins/defaults/advisor/package.json +14 -0
- package/src/plugins/defaults/advisor/steering.ts +67 -0
- package/src/plugins/defaults/advisor/tools/advisor.ts +65 -0
- package/src/plugins/defaults/advisor/transcript.ts +76 -0
- package/src/plugins/defaults/index.ts +35 -0
- package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +22 -9
- package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +2 -2
- package/src/plugins/defaults/memory-retrieval/tail-reinjection-strip.ts +64 -0
- package/src/plugins/defaults/memory-retrieval/unified-turn-context.ts +29 -21
- package/src/plugins/defaults/memory-v3-shadow/__tests__/carry-integration.test.ts +1 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +1 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/maintain-job.test.ts +75 -7
- package/src/plugins/defaults/memory-v3-shadow/__tests__/orchestrate.test.ts +31 -4
- package/src/plugins/defaults/memory-v3-shadow/__tests__/selection-log-store.test.ts +77 -2
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +1 -0
- package/src/plugins/defaults/memory-v3-shadow/injector.ts +7 -10
- package/src/plugins/defaults/memory-v3-shadow/maintain-job.ts +37 -4
- package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +32 -20
- package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +56 -3
- package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +23 -2
- package/src/plugins/defaults/task-progress-nudge/hooks/post-tool-use.ts +2 -12
- package/src/plugins/defaults/title-generate/hooks/stop.ts +56 -21
- package/src/prompts/persona-resolver.ts +12 -2
- package/src/prompts/templates/system-sections.ts +7 -2
- package/src/providers/__tests__/provider-env-vars.test.ts +6 -0
- package/src/providers/__tests__/provider-secret-catalog.test.ts +1 -0
- package/src/providers/__tests__/retry-callsite.test.ts +176 -0
- package/src/providers/atlascloud/client.ts +85 -0
- package/src/providers/fetch-provider-catalog.ts +85 -0
- package/src/providers/inference/adapter-factory.ts +3 -0
- package/src/providers/model-catalog.ts +58 -0
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +33 -0
- package/src/providers/openai/chat-completions-provider.ts +7 -0
- package/src/providers/openai/responses-provider.ts +10 -0
- package/src/providers/provider-send-message.ts +11 -3
- package/src/providers/retry.ts +53 -12
- package/src/providers/search-provider-catalog.ts +10 -0
- package/src/providers/weak-open-model.ts +22 -0
- package/src/runtime/__tests__/agent-wake.test.ts +181 -0
- package/src/runtime/__tests__/client-health.test.ts +44 -0
- package/src/runtime/access-request-helper.ts +21 -53
- package/src/runtime/actor-trust-resolver.ts +49 -21
- package/src/runtime/agent-wake.ts +52 -0
- package/src/runtime/assistant-event-hub.ts +18 -4
- package/src/runtime/auth/__tests__/route-policy.test.ts +12 -0
- package/src/runtime/auth/require-bound-guardian.ts +1 -4
- package/src/runtime/capabilities.test.ts +120 -0
- package/src/runtime/capabilities.ts +197 -0
- package/src/runtime/channel-approval-types.ts +5 -1
- package/src/runtime/channel-retry-sweep.ts +1 -0
- package/src/runtime/channel-verification-service.ts +1 -2
- package/src/runtime/client-health.ts +26 -0
- package/src/runtime/confirmation-request-guardian-bridge.ts +38 -29
- package/src/runtime/effective-capabilities.test.ts +128 -0
- package/src/runtime/effective-capabilities.ts +84 -0
- package/src/runtime/guardian-reply-router.ts +106 -21
- package/src/runtime/invite-redemption-service.ts +6 -22
- package/src/runtime/migrations/__tests__/vbundle-builder-fd-leak.test.ts +123 -0
- package/src/runtime/migrations/vbundle-builder.ts +49 -20
- package/src/runtime/pending-interactions.ts +15 -0
- package/src/runtime/routes/__tests__/client-routes.test.ts +13 -0
- package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +67 -0
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +35 -13
- package/src/runtime/routes/browser-tabs-routes.ts +9 -0
- package/src/runtime/routes/canonical-guardian-expiry-sweep.ts +17 -8
- package/src/runtime/routes/client-routes.ts +10 -0
- package/src/runtime/routes/contact-routes.ts +31 -8
- package/src/runtime/routes/conversation-management-routes.ts +80 -1
- package/src/runtime/routes/conversation-query-routes.ts +68 -22
- package/src/runtime/routes/conversation-routes.ts +37 -12
- package/src/runtime/routes/events-routes.ts +1 -3
- package/src/runtime/routes/guardian-approval-interception.ts +14 -73
- package/src/runtime/routes/guardian-approval-prompt.ts +22 -4
- package/src/runtime/routes/home-feed-routes.ts +8 -3
- package/src/runtime/routes/inbound-message-handler.ts +214 -228
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +88 -6
- package/src/runtime/routes/inbound-stages/admission-policy.test.ts +154 -0
- package/src/runtime/routes/inbound-stages/admission-policy.ts +140 -0
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +3 -3
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +11 -6
- package/src/runtime/routes/inbound-stages/escalation-intercept.ts +1 -2
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +1 -2
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +7 -7
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +47 -28
- package/src/runtime/routes/inbound-stages/reaction-intercept.ts +358 -0
- package/src/runtime/routes/index.ts +2 -0
- package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +8 -0
- package/src/runtime/routes/integrations/slack/channel.ts +36 -0
- package/src/runtime/routes/internal-telemetry-routes.ts +1 -1
- package/src/runtime/routes/mcp-auth-routes.ts +233 -41
- package/src/runtime/routes/memory-eval-routes.ts +87 -0
- package/src/runtime/routes/notification-routes.ts +122 -133
- package/src/runtime/routes/platform-routes.ts +2 -2
- package/src/runtime/routes/plugins-routes.ts +40 -7
- package/src/runtime/routes/secret-routes.ts +10 -0
- package/src/runtime/routes/surface-action-routes.ts +2 -1
- package/src/runtime/routes/tool-call-question-enrichment.test.ts +146 -0
- package/src/runtime/routes/tool-call-question-enrichment.ts +66 -0
- package/src/runtime/routes/workflow-routes.test.ts +225 -1
- package/src/runtime/routes/workflow-routes.ts +131 -1
- package/src/runtime/tool-grant-request-helper.ts +18 -16
- package/src/runtime/trust-context-resolver.ts +8 -5
- package/src/schedule/schedule-store.ts +1 -1
- package/src/schedule/scheduler-types.ts +5 -1
- package/src/security/__tests__/provider-key-env-fallback.test.ts +6 -0
- package/src/security/secret-patterns.ts +3 -0
- package/src/subagent/manager.ts +11 -4
- package/src/telemetry/trace-collection-policy.test.ts +28 -0
- package/src/telemetry/trace-collection-policy.ts +30 -0
- package/src/telemetry/types.ts +89 -0
- package/src/telemetry/usage-telemetry-reporter.test.ts +586 -36
- package/src/telemetry/usage-telemetry-reporter.ts +148 -41
- package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +31 -0
- package/src/tools/browser/browser-execution.ts +29 -18
- package/src/tools/document/document-tool.ts +2 -3
- package/src/tools/executor.ts +5 -3
- package/src/tools/host-terminal/host-shell.ts +5 -4
- package/src/tools/memory/register.ts +2 -2
- package/src/tools/network/__tests__/web-fetch-firecrawl.test.ts +360 -0
- package/src/tools/network/__tests__/web-search.test.ts +143 -0
- package/src/tools/network/web-fetch.ts +372 -1
- package/src/tools/network/web-search.ts +213 -10
- package/src/tools/permission-checker.ts +3 -2
- package/src/tools/registry.ts +20 -0
- package/src/tools/schedule/create.ts +4 -3
- package/src/tools/schedule/update.ts +2 -1
- package/src/tools/shared/filesystem/path-policy.ts +39 -13
- package/src/tools/skills/execute.ts +1 -2
- package/src/tools/subagent/spawn.ts +37 -13
- package/src/tools/terminal/shell.ts +10 -4
- package/src/tools/tool-approval-handler.ts +17 -10
- package/src/tools/types.ts +9 -0
- package/src/tools/ui-surface/definitions.ts +25 -2
- package/src/tools/verification-control-plane-policy.ts +3 -1
- package/src/tools/workflows/run-workflow.ts +1 -0
- package/src/util/disk-usage.ts +78 -23
- package/src/util/platform.ts +8 -1
- package/src/watcher/telemetry.ts +2 -2
- package/src/workflows/engine.test.ts +175 -1
- package/src/workflows/engine.ts +82 -0
- package/src/workflows/journal-store.test.ts +70 -0
- package/src/workflows/journal-store.ts +18 -3
- package/src/workflows/run-manager.test.ts +171 -3
- package/src/workflows/run-manager.ts +64 -0
- package/src/workspace/migrations/105-enable-memory-v3-live-for-new-workspaces.ts +63 -0
- package/src/workspace/migrations/106-drop-collect-usage-data.ts +47 -0
- package/src/workspace/migrations/107-drop-send-diagnostics.ts +47 -0
- package/src/workspace/migrations/108-drop-balanced-economy-profile.ts +129 -0
- package/src/workspace/migrations/registry.ts +8 -0
- package/src/notifications/tool-approval-copy.ts +0 -142
- package/src/runtime/routes/approval-prompt-ts-tracker.ts +0 -78
|
@@ -18,8 +18,8 @@
|
|
|
18
18
|
|
|
19
19
|
import type { Command } from "commander";
|
|
20
20
|
|
|
21
|
-
import { cliIpcCall } from "
|
|
22
|
-
import type { ComparisonReport } from "
|
|
21
|
+
import { cliIpcCall } from "../../../ipc/cli-client.js";
|
|
22
|
+
import type { ComparisonReport } from "../../../memory/v2/harness/runner.js";
|
|
23
23
|
import type {
|
|
24
24
|
MemoryV2BackfillOp,
|
|
25
25
|
MemoryV2BackfillResult,
|
|
@@ -27,9 +27,9 @@ import type {
|
|
|
27
27
|
MemoryV2ReembedSkillsResult,
|
|
28
28
|
MemoryV2SimulateRouterResult,
|
|
29
29
|
MemoryV2ValidateResult,
|
|
30
|
-
} from "
|
|
31
|
-
import { registerCommand } from "
|
|
32
|
-
import { log } from "
|
|
30
|
+
} from "../../../runtime/routes/memory-v2-routes.js";
|
|
31
|
+
import { registerCommand } from "../../lib/register-command.js";
|
|
32
|
+
import { log } from "../../logger.js";
|
|
33
33
|
import {
|
|
34
34
|
renderComparisonReport,
|
|
35
35
|
renderTurnTrace,
|
|
@@ -63,16 +63,7 @@ async function runBackfillOp(op: MemoryV2BackfillOp): Promise<void> {
|
|
|
63
63
|
// Registration
|
|
64
64
|
// ---------------------------------------------------------------------------
|
|
65
65
|
|
|
66
|
-
export function registerMemoryV2Command(
|
|
67
|
-
// Reuse an existing `memory` parent if some other registrar attached to it
|
|
68
|
-
// first; otherwise create one. This keeps the registration order between
|
|
69
|
-
// sibling memory registrars unconstrained.
|
|
70
|
-
const memory =
|
|
71
|
-
program.commands.find((c) => c.name() === "memory") ??
|
|
72
|
-
program
|
|
73
|
-
.command("memory")
|
|
74
|
-
.description("Manage the v2 memory subsystem (concept-page model)");
|
|
75
|
-
|
|
66
|
+
export function registerMemoryV2Command(memory: Command): void {
|
|
76
67
|
registerCommand(memory, {
|
|
77
68
|
name: "v2",
|
|
78
69
|
transport: "ipc",
|
|
@@ -17,13 +17,14 @@
|
|
|
17
17
|
|
|
18
18
|
import type { Command } from "commander";
|
|
19
19
|
|
|
20
|
-
import { cliIpcCall } from "
|
|
20
|
+
import { cliIpcCall } from "../../../ipc/cli-client.js";
|
|
21
|
+
import type { MemoryEvalRunResult } from "../../../runtime/routes/memory-eval-routes.js";
|
|
21
22
|
import type {
|
|
22
23
|
MemoryV3BackfillSectionsResult,
|
|
23
24
|
MemoryV3RebuildIndexResult,
|
|
24
|
-
} from "
|
|
25
|
-
import { registerCommand } from "
|
|
26
|
-
import { log } from "
|
|
25
|
+
} from "../../../runtime/routes/memory-v3-routes.js";
|
|
26
|
+
import { registerCommand } from "../../lib/register-command.js";
|
|
27
|
+
import { log } from "../../logger.js";
|
|
27
28
|
|
|
28
29
|
/**
|
|
29
30
|
* IPC timeout for `backfill-sections`. The one-time full-corpus section embed
|
|
@@ -34,14 +35,14 @@ import { log } from "../logger.js";
|
|
|
34
35
|
*/
|
|
35
36
|
const BACKFILL_IPC_TIMEOUT_MS = 30 * 60 * 1000;
|
|
36
37
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
program.command("memory").description("Manage the memory subsystem");
|
|
38
|
+
/**
|
|
39
|
+
* IPC timeout for `eval`. Embedding both corpora's sections runs through the
|
|
40
|
+
* embedder and easily outlasts the default 60s, so allow the same generous
|
|
41
|
+
* ceiling as the backfill.
|
|
42
|
+
*/
|
|
43
|
+
const EVAL_IPC_TIMEOUT_MS = 30 * 60 * 1000;
|
|
44
44
|
|
|
45
|
+
export function registerMemoryV3Command(memory: Command): void {
|
|
45
46
|
registerCommand(memory, {
|
|
46
47
|
name: "v3",
|
|
47
48
|
transport: "ipc",
|
|
@@ -134,6 +135,91 @@ Examples:
|
|
|
134
135
|
: "."),
|
|
135
136
|
);
|
|
136
137
|
});
|
|
138
|
+
|
|
139
|
+
// ── eval ──────────────────────────────────────────────────────────────
|
|
140
|
+
|
|
141
|
+
v3.command("eval")
|
|
142
|
+
.description(
|
|
143
|
+
"Build blinded A/B retrieval-eval packets (snapshot corpus vs staged wiki)",
|
|
144
|
+
)
|
|
145
|
+
.requiredOption(
|
|
146
|
+
"--staging <dir>",
|
|
147
|
+
"Staged v3 wiki dir (relative to the workspace, or absolute)",
|
|
148
|
+
)
|
|
149
|
+
.requiredOption(
|
|
150
|
+
"--snapshot <dir>",
|
|
151
|
+
"Read-only v2 snapshot dir (relative to the workspace, or absolute)",
|
|
152
|
+
)
|
|
153
|
+
.requiredOption("--out <dir>", "Output dir for packets.json + key.json")
|
|
154
|
+
.option("--turns <n>", "Number of recent turns to mine", "30")
|
|
155
|
+
.option("--k <n>", "Pages per memory set", "8")
|
|
156
|
+
.option(
|
|
157
|
+
"--seed <n>",
|
|
158
|
+
"Blinding seed (reproducible A/B assignment)",
|
|
159
|
+
"1",
|
|
160
|
+
)
|
|
161
|
+
.option(
|
|
162
|
+
"--no-dense",
|
|
163
|
+
"Needle-only: skip section embedding (fast, cheaper, lower fidelity)",
|
|
164
|
+
)
|
|
165
|
+
.option("--json", "Emit raw JSON instead of a formatted summary")
|
|
166
|
+
.addHelpText(
|
|
167
|
+
"after",
|
|
168
|
+
`
|
|
169
|
+
Mines recent user turns, retrieves the top pages from each corpus per turn, and
|
|
170
|
+
writes blinded A/B packets (plus a separate unblinding key) for a blind-judge
|
|
171
|
+
workflow. Both corpora are read in memory — nothing in the live lanes or Qdrant
|
|
172
|
+
is touched. With the dense lane on (default) it embeds every section of both
|
|
173
|
+
corpora, which can take a while on a large corpus; use --no-dense for a fast
|
|
174
|
+
lexical-only pass.
|
|
175
|
+
|
|
176
|
+
Examples:
|
|
177
|
+
$ assistant memory v3 eval --snapshot .mv3/snapshot/concepts --staging .mv3/staging --out .mv3/eval
|
|
178
|
+
$ assistant memory v3 eval --snapshot .mv3/snapshot/concepts --staging .mv3/staging --out .mv3/eval --turns 50 --no-dense`,
|
|
179
|
+
)
|
|
180
|
+
.action(
|
|
181
|
+
async (opts: {
|
|
182
|
+
staging: string;
|
|
183
|
+
snapshot: string;
|
|
184
|
+
out: string;
|
|
185
|
+
turns: string;
|
|
186
|
+
k: string;
|
|
187
|
+
seed: string;
|
|
188
|
+
dense: boolean;
|
|
189
|
+
json?: boolean;
|
|
190
|
+
}) => {
|
|
191
|
+
const result = await cliIpcCall<MemoryEvalRunResult>(
|
|
192
|
+
"memory_eval_run",
|
|
193
|
+
{
|
|
194
|
+
body: {
|
|
195
|
+
stagingDir: opts.staging,
|
|
196
|
+
snapshotDir: opts.snapshot,
|
|
197
|
+
outDir: opts.out,
|
|
198
|
+
turns: Number(opts.turns),
|
|
199
|
+
k: Number(opts.k),
|
|
200
|
+
seed: Number(opts.seed),
|
|
201
|
+
dense: opts.dense,
|
|
202
|
+
},
|
|
203
|
+
},
|
|
204
|
+
{ timeoutMs: EVAL_IPC_TIMEOUT_MS },
|
|
205
|
+
);
|
|
206
|
+
if (!result.ok) {
|
|
207
|
+
log.error(result.error ?? "Failed to build eval packets");
|
|
208
|
+
process.exitCode = 1;
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
const payload = result.result!;
|
|
212
|
+
if (opts.json === true) {
|
|
213
|
+
log.info(JSON.stringify(payload, null, 2));
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
log.info(
|
|
217
|
+
`Wrote ${payload.packetsWritten} packets from ${payload.turnsMined} turns ` +
|
|
218
|
+
`(snapshot ${payload.snapshotPages} pages vs staged ${payload.stagingPages} pages, ` +
|
|
219
|
+
`dense=${payload.dense}).\n packets: ${payload.packetsPath}\n key: ${payload.keyPath}`,
|
|
220
|
+
);
|
|
221
|
+
},
|
|
222
|
+
);
|
|
137
223
|
},
|
|
138
224
|
});
|
|
139
225
|
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { afterEach, describe, expect, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import { noConnectionsMessage } from "./status.js";
|
|
4
|
+
|
|
5
|
+
const original = process.env.__RESOLVED_MODEL;
|
|
6
|
+
|
|
7
|
+
afterEach(() => {
|
|
8
|
+
if (original === undefined) delete process.env.__RESOLVED_MODEL;
|
|
9
|
+
else process.env.__RESOLVED_MODEL = original;
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
describe("noConnectionsMessage", () => {
|
|
13
|
+
test("capable models get the terse default", () => {
|
|
14
|
+
process.env.__RESOLVED_MODEL = "claude-opus-4-8";
|
|
15
|
+
const msg = noConnectionsMessage("google");
|
|
16
|
+
expect(msg).toBe(
|
|
17
|
+
"No active connections for google.\n" +
|
|
18
|
+
"Connect with `assistant oauth connect google`.\n",
|
|
19
|
+
);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
test("no resolved model falls back to the terse default", () => {
|
|
23
|
+
delete process.env.__RESOLVED_MODEL;
|
|
24
|
+
expect(noConnectionsMessage("google")).toContain(
|
|
25
|
+
"Connect with `assistant oauth connect google`.",
|
|
26
|
+
);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test("weak open models are steered to the oauth_connect surface", () => {
|
|
30
|
+
process.env.__RESOLVED_MODEL = "accounts/fireworks/models/minimax-m3";
|
|
31
|
+
const msg = noConnectionsMessage("google");
|
|
32
|
+
expect(msg).toContain('surface_type "oauth_connect"');
|
|
33
|
+
expect(msg).toContain('data.providerKey "google"');
|
|
34
|
+
expect(msg).not.toContain("assistant oauth connect google");
|
|
35
|
+
});
|
|
36
|
+
});
|
|
@@ -1,8 +1,30 @@
|
|
|
1
1
|
import type { Command } from "commander";
|
|
2
2
|
|
|
3
3
|
import { cliIpcCall, exitFromIpcResult } from "../../../ipc/cli-client.js";
|
|
4
|
+
import { isWeakOpenModel } from "../../../providers/weak-open-model.js";
|
|
4
5
|
import { shouldOutputJson, writeOutput } from "../../output.js";
|
|
5
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Message shown when a provider has no active connections. Weak open models
|
|
9
|
+
* loop through redundant discovery commands (channel checks, `oauth providers
|
|
10
|
+
* get`, loading the OAuth setup skill) before rendering the connect button, so
|
|
11
|
+
* they get an explicit single next action: render the core `oauth_connect`
|
|
12
|
+
* surface directly. Capable models keep the terse default.
|
|
13
|
+
*/
|
|
14
|
+
export function noConnectionsMessage(provider: string): string {
|
|
15
|
+
const base = `No active connections for ${provider}.`;
|
|
16
|
+
if (isWeakOpenModel(process.env.__RESOLVED_MODEL)) {
|
|
17
|
+
return (
|
|
18
|
+
`${base}\nTo let the user connect, render the connect button: call ` +
|
|
19
|
+
`\`ui_show\` with surface_type "oauth_connect" and ` +
|
|
20
|
+
`data.providerKey "${provider}". That surface is always available — do ` +
|
|
21
|
+
`not run further \`oauth\`/\`channels\` commands or load a setup skill ` +
|
|
22
|
+
`just to display it.\n`
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
return `${base}\nConnect with \`assistant oauth connect ${provider}\`.\n`;
|
|
26
|
+
}
|
|
27
|
+
|
|
6
28
|
// ---------------------------------------------------------------------------
|
|
7
29
|
// Types
|
|
8
30
|
// ---------------------------------------------------------------------------
|
|
@@ -91,9 +113,7 @@ Examples:
|
|
|
91
113
|
|
|
92
114
|
// Text output
|
|
93
115
|
if (connections.length === 0) {
|
|
94
|
-
process.stdout.write(
|
|
95
|
-
`No active connections for ${provider}.\nConnect with \`assistant oauth connect ${provider}\`.\n`,
|
|
96
|
-
);
|
|
116
|
+
process.stdout.write(noConnectionsMessage(provider));
|
|
97
117
|
return;
|
|
98
118
|
}
|
|
99
119
|
|
|
@@ -46,7 +46,6 @@ import {
|
|
|
46
46
|
PluginNotUpgradableError,
|
|
47
47
|
type PluginUpgradeResult,
|
|
48
48
|
type PluginUpgradeStrategy,
|
|
49
|
-
PluginUpgradeStrategyUnsupportedError,
|
|
50
49
|
upgradePlugin,
|
|
51
50
|
} from "../lib/upgrade-plugin.js";
|
|
52
51
|
import { getCliLogger } from "../logger.js";
|
|
@@ -76,6 +75,7 @@ Examples:
|
|
|
76
75
|
$ assistant plugins upgrade example --dry-run
|
|
77
76
|
$ assistant plugins upgrade example --strategy ours
|
|
78
77
|
$ assistant plugins upgrade example --strategy theirs
|
|
78
|
+
$ assistant plugins upgrade example --strategy assistant
|
|
79
79
|
$ assistant plugins search example
|
|
80
80
|
$ assistant plugins search "^example"
|
|
81
81
|
$ assistant plugins search example --json
|
|
@@ -180,7 +180,7 @@ Examples:
|
|
|
180
180
|
plugins
|
|
181
181
|
.command("inspect <name>")
|
|
182
182
|
.description(
|
|
183
|
-
"Show a plugin's local install metadata
|
|
183
|
+
"Show a plugin's local install metadata, the marketplace pin, whether an update is available, and the surfaces (skills, hooks, tools) it contributes",
|
|
184
184
|
)
|
|
185
185
|
.option("--json", "Emit machine-readable JSON instead of a summary")
|
|
186
186
|
.action(async (name: string, opts: { json?: boolean }) => {
|
|
@@ -465,7 +465,6 @@ Examples:
|
|
|
465
465
|
if (
|
|
466
466
|
err instanceof PluginNotInstalledError ||
|
|
467
467
|
err instanceof PluginNotUpgradableError ||
|
|
468
|
-
err instanceof PluginUpgradeStrategyUnsupportedError ||
|
|
469
468
|
err instanceof PluginMergeBaselineError ||
|
|
470
469
|
err instanceof InvalidPluginNameError
|
|
471
470
|
) {
|
|
@@ -550,12 +549,20 @@ function remoteLocation(remote: PluginRemoteInfo): string {
|
|
|
550
549
|
* `location`, with a `drift` line under the installed copy.
|
|
551
550
|
*/
|
|
552
551
|
function formatInspection(inspection: PluginInspection): string[] {
|
|
553
|
-
const { name, status, local, remote, remoteError } = inspection;
|
|
552
|
+
const { name, status, local, remote, remoteError, surfaces } = inspection;
|
|
554
553
|
const lines: string[] = [name, "─".repeat(44)];
|
|
555
554
|
const topRow = (label: string, value: string) =>
|
|
556
555
|
lines.push(`${label.padEnd(11)} ${value}`);
|
|
557
556
|
const blockRow = (label: string, value: string) =>
|
|
558
557
|
lines.push(` ${label.padEnd(9)} ${value}`);
|
|
558
|
+
// A surface block: the surface type as a heading, then its items indented
|
|
559
|
+
// under it. Omitted entirely when the plugin contributes none of that type,
|
|
560
|
+
// so the listing only ever shows what the plugin actually contributes.
|
|
561
|
+
const surfaceBlock = (label: string, items: readonly string[]) => {
|
|
562
|
+
if (items.length === 0) return;
|
|
563
|
+
lines.push(label);
|
|
564
|
+
for (const item of items) lines.push(` ${item}`);
|
|
565
|
+
};
|
|
559
566
|
|
|
560
567
|
topRow("status", statusLine(status));
|
|
561
568
|
|
|
@@ -590,6 +597,12 @@ function formatInspection(inspection: PluginInspection): string[] {
|
|
|
590
597
|
const description = remote?.description ?? local?.description ?? null;
|
|
591
598
|
if (description) topRow("description", description);
|
|
592
599
|
|
|
600
|
+
if (surfaces) {
|
|
601
|
+
surfaceBlock("skills", surfaces.skills);
|
|
602
|
+
surfaceBlock("hooks", surfaces.hooks);
|
|
603
|
+
surfaceBlock("tools", surfaces.tools);
|
|
604
|
+
}
|
|
605
|
+
|
|
593
606
|
for (const issue of local?.issues ?? []) topRow("issue", issue);
|
|
594
607
|
|
|
595
608
|
return lines;
|
|
@@ -628,10 +641,49 @@ function formatUpgrade(result: PluginUpgradeResult): string[] {
|
|
|
628
641
|
result.fileCount === null
|
|
629
642
|
? ""
|
|
630
643
|
: `(${result.fileCount} file${result.fileCount === 1 ? "" : "s"}) `;
|
|
644
|
+
const hasConflicts =
|
|
645
|
+
result.conflicts.length > 0 || result.binaryConflicts.length > 0;
|
|
646
|
+
|
|
647
|
+
// Under `assistant`, an unresolved merge leaves conflicts in the tree —
|
|
648
|
+
// the plugin would fail to load while any remain, so the usual "restart
|
|
649
|
+
// now" guidance is replaced with resolution instructions. Most conflicts
|
|
650
|
+
// carry git markers, but a modify/delete divergence (a file edited on one
|
|
651
|
+
// side and deleted on the other) keeps the surviving content with no
|
|
652
|
+
// markers, so the guidance covers both rather than assuming markers.
|
|
653
|
+
if (result.strategy === "assistant" && hasConflicts) {
|
|
654
|
+
const lines = [
|
|
655
|
+
`Merged "${name}" ${move} with conflicts`,
|
|
656
|
+
"",
|
|
657
|
+
`${count}→ ${result.target}`,
|
|
658
|
+
];
|
|
659
|
+
if (result.conflicts.length > 0) {
|
|
660
|
+
lines.push(
|
|
661
|
+
"",
|
|
662
|
+
`Resolve ${result.conflicts.length} conflicted file${result.conflicts.length === 1 ? "" : "s"} (open each: resolve its git conflict markers, or — if a modify/delete divergence kept the file with none — decide whether to keep or remove it):`,
|
|
663
|
+
...result.conflicts.map((p) => ` ${p}`),
|
|
664
|
+
);
|
|
665
|
+
}
|
|
666
|
+
if (result.binaryConflicts.length > 0) {
|
|
667
|
+
lines.push(
|
|
668
|
+
"",
|
|
669
|
+
`Binary conflict${result.binaryConflicts.length === 1 ? "" : "s"} (kept the local copy — choose a version manually):`,
|
|
670
|
+
...result.binaryConflicts.map((p) => ` ${p}`),
|
|
671
|
+
);
|
|
672
|
+
}
|
|
673
|
+
lines.push(
|
|
674
|
+
"",
|
|
675
|
+
"Resolve the conflicts, then restart the assistant to pick up the upgrade.",
|
|
676
|
+
);
|
|
677
|
+
if (provenanceNote) lines.push(provenanceNote);
|
|
678
|
+
return lines;
|
|
679
|
+
}
|
|
680
|
+
|
|
631
681
|
const mergeNote =
|
|
632
682
|
result.strategy === "ours" || result.strategy === "theirs"
|
|
633
683
|
? `Local edits were merged (--strategy ${result.strategy}).`
|
|
634
|
-
:
|
|
684
|
+
: result.strategy === "assistant"
|
|
685
|
+
? "Local edits were merged with no conflicts (--strategy assistant)."
|
|
686
|
+
: null;
|
|
635
687
|
const lines = [
|
|
636
688
|
`Upgraded "${name}" ${move}`,
|
|
637
689
|
"",
|
|
@@ -90,6 +90,8 @@ function installPlugin(
|
|
|
90
90
|
} | null;
|
|
91
91
|
/** Embed a content fingerprint of the materialized tree in the sidecar. */
|
|
92
92
|
fingerprint?: boolean;
|
|
93
|
+
/** Materialize surface directories (`hooks/<name>.ts`, `skills/<id>/SKILL.md`). */
|
|
94
|
+
surfaces?: { hooks?: string[]; tools?: string[]; skills?: string[] };
|
|
93
95
|
} = {},
|
|
94
96
|
): void {
|
|
95
97
|
const dir = join(workspace, name);
|
|
@@ -102,6 +104,18 @@ function installPlugin(
|
|
|
102
104
|
description: "Installed copy.",
|
|
103
105
|
}),
|
|
104
106
|
);
|
|
107
|
+
for (const hook of opts.surfaces?.hooks ?? []) {
|
|
108
|
+
mkdirSync(join(dir, "hooks"), { recursive: true });
|
|
109
|
+
writeFileSync(join(dir, "hooks", `${hook}.ts`), "export default () => {};");
|
|
110
|
+
}
|
|
111
|
+
for (const tool of opts.surfaces?.tools ?? []) {
|
|
112
|
+
mkdirSync(join(dir, "tools"), { recursive: true });
|
|
113
|
+
writeFileSync(join(dir, "tools", `${tool}.ts`), "export default {};");
|
|
114
|
+
}
|
|
115
|
+
for (const skill of opts.surfaces?.skills ?? []) {
|
|
116
|
+
mkdirSync(join(dir, "skills", skill), { recursive: true });
|
|
117
|
+
writeFileSync(join(dir, "skills", skill, "SKILL.md"), `# ${skill}`);
|
|
118
|
+
}
|
|
105
119
|
if (opts.sidecar !== null) {
|
|
106
120
|
const sidecar = opts.sidecar ?? { commit: SHA_A };
|
|
107
121
|
// Mirror install: fingerprint the tree before the sidecar is written so it
|
|
@@ -367,6 +381,46 @@ describe("inspectPlugin", () => {
|
|
|
367
381
|
expect(result.local?.localChanges).toBeNull();
|
|
368
382
|
});
|
|
369
383
|
|
|
384
|
+
test("reports the surfaces an installed copy contributes", async () => {
|
|
385
|
+
// GIVEN an installed plugin shipping hooks and skills but no tools
|
|
386
|
+
installPlugin(workspace, "level-up", {
|
|
387
|
+
sidecar: { commit: SHA_A },
|
|
388
|
+
surfaces: {
|
|
389
|
+
hooks: ["post-model-call", "init"],
|
|
390
|
+
skills: ["second-skill", "first-skill"],
|
|
391
|
+
},
|
|
392
|
+
});
|
|
393
|
+
const fetch = makeFetch({ marketplace: manifestWith("level-up", SHA_A) });
|
|
394
|
+
|
|
395
|
+
// WHEN it is inspected
|
|
396
|
+
const result = await inspectPlugin(
|
|
397
|
+
{ name: "level-up" },
|
|
398
|
+
{ fetch, workspacePluginsDir: workspace },
|
|
399
|
+
);
|
|
400
|
+
|
|
401
|
+
// THEN the contributed surfaces are listed (sorted) and tools is empty
|
|
402
|
+
expect(result.surfaces).toEqual({
|
|
403
|
+
skills: ["first-skill", "second-skill"],
|
|
404
|
+
hooks: ["init", "post-model-call"],
|
|
405
|
+
tools: [],
|
|
406
|
+
});
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
test("leaves surfaces null when the plugin is not installed", async () => {
|
|
410
|
+
// GIVEN a marketplace entry with no local copy to walk
|
|
411
|
+
const fetch = makeFetch({ marketplace: manifestWith("level-up", SHA_B) });
|
|
412
|
+
|
|
413
|
+
// WHEN it is inspected
|
|
414
|
+
const result = await inspectPlugin(
|
|
415
|
+
{ name: "level-up" },
|
|
416
|
+
{ fetch, workspacePluginsDir: workspace },
|
|
417
|
+
);
|
|
418
|
+
|
|
419
|
+
// THEN there is no tree to read surfaces from
|
|
420
|
+
expect(result.installed).toBe(false);
|
|
421
|
+
expect(result.surfaces).toBeNull();
|
|
422
|
+
});
|
|
423
|
+
|
|
370
424
|
test("throws when the plugin is neither installed nor in the marketplace", async () => {
|
|
371
425
|
// GIVEN no local copy and an empty catalog
|
|
372
426
|
const fetch = makeFetch({ marketplace: undefined });
|
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
* The merge is exercised against three real temp directories (base/ours/theirs)
|
|
5
5
|
* and the merged tree is read back from a fourth (dest). `git merge-file` runs
|
|
6
6
|
* for real — these tests assert the line-level merge behavior (non-conflicting
|
|
7
|
-
* hunks from both sides survive; conflicts resolve toward
|
|
8
|
-
* file-level add/delete
|
|
7
|
+
* hunks from both sides survive; conflicts resolve toward `ours`/`theirs` or
|
|
8
|
+
* get conflict markers under `assistant`), the file-level add/delete
|
|
9
|
+
* resolution, and binary whole-file handling.
|
|
9
10
|
*/
|
|
10
11
|
|
|
11
12
|
import {
|
|
@@ -97,7 +98,9 @@ describe("mergePluginTree", () => {
|
|
|
97
98
|
expect(read("local-only.txt")).toBe("local\n");
|
|
98
99
|
expect(read("remote-only.txt")).toBe("remote\n");
|
|
99
100
|
expect(read("conflict.txt")).toBe("ours\n");
|
|
100
|
-
expect(count).toBe(4);
|
|
101
|
+
expect(count.fileCount).toBe(4);
|
|
102
|
+
expect(count.conflicts).toEqual([]);
|
|
103
|
+
expect(count.binaryConflicts).toEqual([]);
|
|
101
104
|
});
|
|
102
105
|
|
|
103
106
|
test("resolves conflicts toward the pin under the theirs strategy", async () => {
|
|
@@ -289,7 +292,7 @@ describe("mergePluginTree", () => {
|
|
|
289
292
|
|
|
290
293
|
// THEN the sidecar is never carried through (the caller rewrites it)
|
|
291
294
|
expect(read("install-meta.json")).toBeNull();
|
|
292
|
-
expect(count).toBe(1);
|
|
295
|
+
expect(count.fileCount).toBe(1);
|
|
293
296
|
});
|
|
294
297
|
|
|
295
298
|
test("merges files in nested directories", async () => {
|
|
@@ -310,4 +313,131 @@ describe("mergePluginTree", () => {
|
|
|
310
313
|
// THEN the nested path is preserved and resolved
|
|
311
314
|
expect(read("src/a/b.txt")).toBe("ours\n");
|
|
312
315
|
});
|
|
316
|
+
|
|
317
|
+
test("writes git conflict markers and reports the path under the assistant strategy", async () => {
|
|
318
|
+
// GIVEN a file edited on disjoint lines by each side plus one true conflict
|
|
319
|
+
writeTree(baseDir, { "common.txt": "a\nb\nc\n", "conflict.txt": "base\n" });
|
|
320
|
+
writeTree(oursDir, { "common.txt": "A\nb\nc\n", "conflict.txt": "ours\n" });
|
|
321
|
+
writeTree(theirsDir, {
|
|
322
|
+
"common.txt": "a\nb\nC\n",
|
|
323
|
+
"conflict.txt": "theirs\n",
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
// WHEN merged with the `assistant` strategy
|
|
327
|
+
const result = await mergePluginTree({
|
|
328
|
+
baseDir,
|
|
329
|
+
oursDir,
|
|
330
|
+
theirsDir,
|
|
331
|
+
destDir,
|
|
332
|
+
strategy: "assistant",
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
// THEN the non-conflicting edits still auto-merge and only the true
|
|
336
|
+
// conflict carries markers naming both sides, and its path is reported
|
|
337
|
+
expect(read("common.txt")).toBe("A\nb\nC\n");
|
|
338
|
+
const conflict = read("conflict.txt") ?? "";
|
|
339
|
+
expect(conflict).toContain("<<<<<<<");
|
|
340
|
+
expect(conflict).toContain("=======");
|
|
341
|
+
expect(conflict).toContain(">>>>>>>");
|
|
342
|
+
expect(conflict).toContain("ours\n");
|
|
343
|
+
expect(conflict).toContain("theirs\n");
|
|
344
|
+
expect(result.conflicts).toEqual(["conflict.txt"]);
|
|
345
|
+
expect(result.binaryConflicts).toEqual([]);
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
test("uses the supplied conflict labels in the markers", async () => {
|
|
349
|
+
// GIVEN a true conflict and custom marker labels
|
|
350
|
+
writeTree(baseDir, { "f.txt": "base\n" });
|
|
351
|
+
writeTree(oursDir, { "f.txt": "ours\n" });
|
|
352
|
+
writeTree(theirsDir, { "f.txt": "theirs\n" });
|
|
353
|
+
|
|
354
|
+
// WHEN merged with the `assistant` strategy and explicit labels
|
|
355
|
+
await mergePluginTree({
|
|
356
|
+
baseDir,
|
|
357
|
+
oursDir,
|
|
358
|
+
theirsDir,
|
|
359
|
+
destDir,
|
|
360
|
+
strategy: "assistant",
|
|
361
|
+
conflictLabels: {
|
|
362
|
+
ours: "local edits (was abc1234)",
|
|
363
|
+
base: "install baseline abc1234",
|
|
364
|
+
theirs: "upgrade pin def5678",
|
|
365
|
+
},
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
// THEN the marker lines carry those labels
|
|
369
|
+
const merged = read("f.txt") ?? "";
|
|
370
|
+
expect(merged).toContain("<<<<<<< local edits (was abc1234)");
|
|
371
|
+
expect(merged).toContain(">>>>>>> upgrade pin def5678");
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
test("auto-merges with no markers when the assistant strategy hits no true conflict", async () => {
|
|
375
|
+
// GIVEN edits on disjoint lines of the same file (no overlapping hunk)
|
|
376
|
+
writeTree(baseDir, { "f.txt": "a\nb\nc\n" });
|
|
377
|
+
writeTree(oursDir, { "f.txt": "A\nb\nc\n" });
|
|
378
|
+
writeTree(theirsDir, { "f.txt": "a\nb\nC\n" });
|
|
379
|
+
|
|
380
|
+
// WHEN merged with the `assistant` strategy
|
|
381
|
+
const result = await mergePluginTree({
|
|
382
|
+
baseDir,
|
|
383
|
+
oursDir,
|
|
384
|
+
theirsDir,
|
|
385
|
+
destDir,
|
|
386
|
+
strategy: "assistant",
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
// THEN both edits merge cleanly with no markers and nothing is reported
|
|
390
|
+
const merged = read("f.txt") ?? "";
|
|
391
|
+
expect(merged).toBe("A\nb\nC\n");
|
|
392
|
+
expect(merged).not.toContain("<<<<<<<");
|
|
393
|
+
expect(result.conflicts).toEqual([]);
|
|
394
|
+
expect(result.binaryConflicts).toEqual([]);
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
test("keeps the local copy and reports a binary conflict under the assistant strategy", async () => {
|
|
398
|
+
// GIVEN a binary file (NUL bytes) diverged on both sides
|
|
399
|
+
const base = Buffer.from([0x00, 0x01, 0x02]);
|
|
400
|
+
const ours = Buffer.from([0x00, 0xaa, 0xbb]);
|
|
401
|
+
const theirs = Buffer.from([0x00, 0xcc, 0xdd]);
|
|
402
|
+
writeTree(baseDir, { "img.bin": base });
|
|
403
|
+
writeTree(oursDir, { "img.bin": ours });
|
|
404
|
+
writeTree(theirsDir, { "img.bin": theirs });
|
|
405
|
+
|
|
406
|
+
// WHEN merged with the `assistant` strategy
|
|
407
|
+
const result = await mergePluginTree({
|
|
408
|
+
baseDir,
|
|
409
|
+
oursDir,
|
|
410
|
+
theirsDir,
|
|
411
|
+
destDir,
|
|
412
|
+
strategy: "assistant",
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
// THEN the local copy is kept byte-for-byte (markers are impossible) and
|
|
416
|
+
// the path is surfaced as a binary conflict, not a marker conflict
|
|
417
|
+
expect(readFileSync(join(destDir, "img.bin")).equals(ours)).toBe(true);
|
|
418
|
+
expect(result.binaryConflicts).toEqual(["img.bin"]);
|
|
419
|
+
expect(result.conflicts).toEqual([]);
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
test("keeps the surviving content and reports a modify/delete conflict under the assistant strategy", async () => {
|
|
423
|
+
// GIVEN a file edited locally but deleted at the pin
|
|
424
|
+
writeTree(baseDir, { "f.txt": "base\n" });
|
|
425
|
+
writeTree(oursDir, { "f.txt": "local edit\n" });
|
|
426
|
+
writeTree(theirsDir, {});
|
|
427
|
+
|
|
428
|
+
// WHEN merged with the `assistant` strategy
|
|
429
|
+
const result = await mergePluginTree({
|
|
430
|
+
baseDir,
|
|
431
|
+
oursDir,
|
|
432
|
+
theirsDir,
|
|
433
|
+
destDir,
|
|
434
|
+
strategy: "assistant",
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
// THEN the local edit is preserved (never silently dropped) and the
|
|
438
|
+
// modify/delete divergence is reported for resolution
|
|
439
|
+
expect(read("f.txt")).toBe("local edit\n");
|
|
440
|
+
expect(result.conflicts).toEqual(["f.txt"]);
|
|
441
|
+
expect(result.binaryConflicts).toEqual([]);
|
|
442
|
+
});
|
|
313
443
|
});
|