@vellumai/assistant 0.8.10 → 0.8.11-staging.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bun.lock +62 -1
- package/docs/workspace-tools.md +196 -0
- package/examples/plugins/echo/README.md +3 -3
- package/knip.json +1 -0
- package/openapi.yaml +460 -128
- package/package.json +2 -1
- package/scripts/build-plugin-api.ts +299 -0
- package/src/__tests__/agent-loop-callsite-precedence.test.ts +7 -0
- package/src/__tests__/agent-loop-compaction-events.test.ts +197 -0
- package/src/__tests__/agent-loop-exit-reason.test.ts +93 -96
- package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +2 -0
- package/src/__tests__/agent-loop-output-hooks.test.ts +274 -1
- package/src/__tests__/agent-loop-override-profile.test.ts +3 -0
- package/src/__tests__/agent-loop-provider-error-recording.test.ts +4 -0
- package/src/__tests__/agent-loop-thinking.test.ts +4 -0
- package/src/__tests__/agent-loop.test.ts +578 -5
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +0 -1
- package/src/__tests__/approval-cascade.test.ts +1 -0
- package/src/__tests__/background-workers-disk-pressure.test.ts +0 -2
- package/src/__tests__/btw-routes.test.ts +0 -1
- package/src/__tests__/build-persisted-content.test.ts +75 -1
- package/src/__tests__/catalog-install-normalize.test.ts +141 -0
- package/src/__tests__/ces-startup-timeout.test.ts +60 -0
- package/src/__tests__/compaction-events.test.ts +1 -0
- package/src/__tests__/config-managed-gemini-defaults.test.ts +2 -46
- package/src/__tests__/context-overflow-reducer.test.ts +264 -124
- package/src/__tests__/context-window-manager-overflow-rung.test.ts +351 -0
- package/src/__tests__/conversation-abort-tool-results.test.ts +1 -1
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +13 -5
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +284 -455
- package/src/__tests__/conversation-agent-loop.test.ts +131 -551
- package/src/__tests__/conversation-app-control-instantiation.test.ts +13 -0
- package/src/__tests__/conversation-confirmation-signals.test.ts +1 -0
- package/src/__tests__/conversation-fork-crud.test.ts +259 -0
- package/src/__tests__/conversation-history-web-search.test.ts +1 -1
- package/src/__tests__/conversation-lifecycle.test.ts +257 -1
- package/src/__tests__/conversation-process-callsite.test.ts +1 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +38 -377
- package/src/__tests__/conversation-queue.test.ts +1 -39
- package/src/__tests__/conversation-runtime-assembly.test.ts +119 -8
- package/src/__tests__/conversation-skill-tools.test.ts +491 -5
- package/src/__tests__/conversation-slash-queue.test.ts +1 -1
- package/src/__tests__/conversation-slash-unknown.test.ts +1 -0
- package/src/__tests__/conversation-speed-override.test.ts +1 -0
- package/src/__tests__/conversation-store.test.ts +74 -0
- package/src/__tests__/conversation-surfaces-app-control.test.ts +4 -1
- package/src/__tests__/conversation-tool-setup-attribution.test.ts +323 -0
- package/src/__tests__/conversation-tool-setup-tools-disabled.test.ts +34 -0
- package/src/__tests__/conversation-workspace-cache-state.test.ts +1 -0
- package/src/__tests__/conversation-workspace-injection.test.ts +1 -1
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +1 -0
- package/src/__tests__/corrected-target.test.ts +93 -0
- package/src/__tests__/credential-execution-feature-gates.test.ts +3 -5
- package/src/__tests__/credential-execution-tools.test.ts +23 -11
- package/src/__tests__/credential-security-invariants.test.ts +6 -1
- package/src/__tests__/db-schedule-syntax-migration.test.ts +80 -0
- package/src/__tests__/device-id.test.ts +70 -1
- package/src/__tests__/embedding-managed-proxy-selection.test.ts +6 -40
- package/src/__tests__/empty-response-hook.test.ts +242 -66
- package/src/__tests__/external-plugin-loader.test.ts +0 -31
- package/src/__tests__/get-skill-detail-audit.test.ts +43 -1
- package/src/__tests__/guardian-routing-invariants.test.ts +91 -0
- package/src/__tests__/history-repair-hook.test.ts +228 -3
- package/src/__tests__/host-app-control-proxy.test.ts +45 -0
- package/src/__tests__/host-browser-proxy.test.ts +254 -9
- package/src/__tests__/identity-routes.test.ts +1 -0
- package/src/__tests__/image-recovery-hook.test.ts +387 -0
- package/src/__tests__/injector-chain.test.ts +5 -4
- package/src/__tests__/injector-v3-suppression.test.ts +373 -47
- package/src/__tests__/intent-routing.test.ts +7 -0
- package/src/__tests__/memory-retrieval-hook.test.ts +117 -15
- package/src/__tests__/notification-decision-strategy.test.ts +3 -3
- package/src/__tests__/oauth-store.test.ts +0 -85
- package/src/__tests__/{context-overflow-policy.test.ts → overflow-policy.test.ts} +1 -1
- package/src/__tests__/parallel-tool.benchmark.test.ts +4 -0
- package/src/__tests__/persist-unsendable-image-downscale.test.ts +29 -9
- package/src/__tests__/persist-unsendable-image.test.ts +4 -4
- package/src/__tests__/persistence-secret-redaction.test.ts +78 -0
- package/src/__tests__/plugin-bootstrap.test.ts +82 -73
- package/src/__tests__/plugin-tool-contribution.test.ts +7 -4
- package/src/__tests__/plugin-types.test.ts +0 -8
- package/src/__tests__/provider-catalog-visibility.test.ts +1 -9
- package/src/__tests__/prune-old-conversations-job.test.ts +99 -0
- package/src/__tests__/registry.test.ts +240 -1
- package/src/__tests__/require-fresh-approval.test.ts +3 -0
- package/src/__tests__/schedule-routes.test.ts +116 -1
- package/src/__tests__/schedule-store.test.ts +28 -0
- package/src/__tests__/schedule-tools.test.ts +94 -1
- package/src/__tests__/server-history-render.test.ts +39 -0
- package/src/__tests__/skill-projection-feature-flag.test.ts +13 -0
- package/src/__tests__/skill-projection.benchmark.test.ts +25 -7
- package/src/__tests__/skills.test.ts +202 -0
- package/src/__tests__/slim-skill-category.test.ts +195 -0
- package/src/__tests__/strip-memory-injections.test.ts +33 -39
- package/src/__tests__/test-support/tool-invocation-seed.ts +79 -0
- package/src/__tests__/title-generate-hook.test.ts +9 -7
- package/src/__tests__/tool-audit-listener.test.ts +264 -1
- package/src/__tests__/tool-error-hook.test.ts +4 -3
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +1 -0
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +273 -0
- package/src/__tests__/tool-result-truncate-hook.test.ts +1 -0
- package/src/__tests__/tool-start-timestamp.test.ts +218 -0
- package/src/__tests__/tools-get-route.test.ts +202 -0
- package/src/__tests__/workspace-tool-loader.test.ts +319 -0
- package/src/__tests__/workspace-tools-watcher-flag.test.ts +70 -0
- package/src/agent/loop.ts +569 -319
- package/src/api/events/tool-result.ts +9 -0
- package/src/api/events/tool-use-start.ts +7 -0
- package/src/api/index.ts +10 -0
- package/src/api/responses/conversation-message.ts +135 -27
- package/src/api/responses/memory-v3-selection-log.ts +4 -4
- package/src/approvals/guardian-request-resolvers.ts +26 -0
- package/src/browser-session/backends/host-bridge.ts +29 -0
- package/src/browser-session/index.ts +1 -0
- package/src/browser-session/types.ts +5 -1
- package/src/cli/commands/__tests__/schedules.test.ts +62 -4
- package/src/cli/commands/__tests__/skills.test.ts +53 -0
- package/src/cli/commands/channel-verification-sessions.ts +6 -6
- package/src/cli/commands/inference-providers.ts +0 -8
- package/src/cli/commands/plugins.ts +2 -2
- package/src/cli/commands/schedules.ts +27 -4
- package/src/cli/commands/skills.ts +187 -146
- package/src/cli/commands/tools.ts +106 -0
- package/src/cli/lib/__tests__/install-from-github.test.ts +256 -328
- package/src/cli/lib/__tests__/plugin-catalog-cache.test.ts +6 -2
- package/src/cli/lib/__tests__/plugin-details.test.ts +10 -16
- package/src/cli/lib/__tests__/plugin-marketplace.test.ts +2 -2
- package/src/cli/lib/__tests__/search-plugins.test.ts +145 -240
- package/src/cli/lib/install-from-github.ts +187 -117
- package/src/cli/lib/plugin-catalog-cache.ts +9 -9
- package/src/cli/lib/plugin-details.ts +38 -68
- package/src/cli/lib/plugin-marketplace.ts +42 -14
- package/src/cli/lib/search-plugins.ts +29 -129
- package/src/cli/program.ts +2 -0
- package/src/config/bundled-skills/acp/SKILL.md +1 -0
- package/src/config/bundled-skills/app-builder/SKILL.md +1 -0
- package/src/config/bundled-skills/app-control/SKILL.md +1 -0
- package/src/config/bundled-skills/computer-use/SKILL.md +1 -0
- package/src/config/bundled-skills/contacts/SKILL.md +1 -0
- package/src/config/bundled-skills/document-editor/SKILL.md +1 -0
- package/src/config/bundled-skills/followups/SKILL.md +1 -0
- package/src/config/bundled-skills/image-studio/SKILL.md +1 -0
- package/src/config/bundled-skills/media-processing/SKILL.md +1 -0
- package/src/config/bundled-skills/messaging/SKILL.md +1 -0
- package/src/config/bundled-skills/phone-calls/SKILL.md +1 -0
- package/src/config/bundled-skills/playbooks/SKILL.md +1 -0
- package/src/config/bundled-skills/schedule/SKILL.md +1 -0
- package/src/config/bundled-skills/schedule/TOOLS.json +11 -3
- package/src/config/bundled-skills/sequences/SKILL.md +1 -0
- package/src/config/bundled-skills/settings/SKILL.md +1 -0
- package/src/config/bundled-skills/skill-management/SKILL.md +101 -1
- package/src/config/bundled-skills/subagent/SKILL.md +1 -0
- package/src/config/bundled-skills/transcribe/SKILL.md +1 -0
- package/src/config/env-registry.ts +23 -0
- package/src/config/feature-flag-registry.json +17 -81
- package/src/config/loader.ts +5 -22
- package/src/config/schema.ts +2 -0
- package/src/config/schemas/__tests__/compaction-logs.test.ts +56 -0
- package/src/config/schemas/__tests__/memory-v2.test.ts +0 -1
- package/src/config/schemas/__tests__/memory-v3.test.ts +61 -1
- package/src/config/schemas/compaction-logs.ts +79 -0
- package/src/config/schemas/memory-v2.ts +0 -8
- package/src/config/schemas/memory-v3.ts +104 -33
- package/src/config/seed-inference-profiles.ts +1 -1
- package/src/config/skills.ts +117 -47
- package/src/context/compactor.ts +11 -0
- package/src/context/strip-injections.ts +38 -4
- package/src/credential-execution/feature-gates.ts +0 -21
- package/src/credential-execution/startup-timeout.ts +32 -4
- package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +18 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +140 -91
- package/src/daemon/conversation-agent-loop.ts +123 -658
- package/src/daemon/conversation-error.ts +6 -33
- package/src/daemon/conversation-lifecycle.ts +1 -1
- package/src/daemon/conversation-runtime-assembly.ts +183 -22
- package/src/daemon/conversation-skill-tools.ts +137 -8
- package/src/daemon/conversation-slash.ts +0 -14
- package/src/daemon/conversation-store.ts +2 -19
- package/src/daemon/conversation-tool-setup.ts +87 -1
- package/src/daemon/conversation.ts +119 -50
- package/src/daemon/external-plugins-bootstrap.ts +36 -95
- package/src/daemon/handlers/config-channels.ts +11 -2
- package/src/daemon/handlers/shared.ts +9 -1
- package/src/daemon/handlers/skills.ts +10 -4
- package/src/daemon/host-app-control-proxy.ts +72 -57
- package/src/daemon/host-browser-proxy.ts +117 -22
- package/src/daemon/lifecycle.ts +34 -5
- package/src/daemon/message-protocol.ts +0 -7
- package/src/daemon/message-types/schedules.ts +1 -0
- package/src/daemon/message-types/skills.ts +17 -0
- package/src/daemon/providers-setup.ts +3 -0
- package/src/daemon/server.ts +3 -3
- package/src/daemon/tool-setup-types.ts +9 -3
- package/src/daemon/trust-context.ts +23 -0
- package/src/daemon/workspace-tools-watcher.ts +324 -0
- package/src/events/tool-audit-listener.ts +78 -16
- package/src/events/tool-metrics-listener.ts +2 -5
- package/src/memory/__tests__/compaction-log-writer-clickhouse.test.ts +227 -0
- package/src/memory/__tests__/conversation-queries.test.ts +176 -0
- package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +20 -32
- package/src/memory/compaction-log-writer-clickhouse.ts +418 -0
- package/src/memory/conversation-crud.ts +134 -3
- package/src/memory/conversation-queries.ts +64 -7
- package/src/memory/db-init.ts +14 -0
- package/src/memory/embedding-backend.test.ts +130 -1
- package/src/memory/embedding-backend.ts +79 -106
- package/src/memory/embedding-gemini.ts +5 -0
- package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +12 -0
- package/src/memory/graph/__tests__/handle-remember-v2.test.ts +19 -0
- package/src/memory/graph/conversation-graph-memory.ts +36 -25
- package/src/memory/graph/tool-handlers.ts +3 -0
- package/src/memory/job-handlers/cleanup.ts +3 -1
- package/src/memory/jobs-store.ts +0 -28
- package/src/memory/jobs-worker.ts +10 -22
- package/src/memory/memory-marker.ts +29 -0
- package/src/memory/memory-retrospective-startup-cleanup.ts +1 -1
- package/src/memory/migrations/268-add-memory-v3-selections.ts +6 -0
- package/src/memory/migrations/270-schedule-description.ts +36 -0
- package/src/memory/migrations/275-tool-invocations-add-skill-id.test.ts +81 -0
- package/src/memory/migrations/275-tool-invocations-add-skill-id.ts +20 -0
- package/src/memory/migrations/276-tool-invocations-created-at-id-index.test.ts +68 -0
- package/src/memory/migrations/276-tool-invocations-created-at-id-index.ts +20 -0
- package/src/memory/migrations/277-add-memory-v3-ever-injected.ts +29 -0
- package/src/memory/migrations/278-tool-invocations-telemetry-columns.test.ts +96 -0
- package/src/memory/migrations/278-tool-invocations-telemetry-columns.ts +39 -0
- package/src/memory/migrations/279-create-skill-loaded-events.test.ts +84 -0
- package/src/memory/migrations/279-create-skill-loaded-events.ts +26 -0
- package/src/memory/migrations/280-conversations-surfaced-at.test.ts +88 -0
- package/src/memory/migrations/280-conversations-surfaced-at.ts +24 -0
- package/src/memory/migrations/index.ts +10 -0
- package/src/memory/migrations/registry.ts +8 -0
- package/src/memory/schema/conversations.ts +16 -0
- package/src/memory/schema/infrastructure.ts +26 -0
- package/src/memory/skill-loaded-events-store.test.ts +160 -0
- package/src/memory/skill-loaded-events-store.ts +95 -0
- package/src/memory/tool-executed-events-store.test.ts +219 -0
- package/src/memory/tool-executed-events-store.ts +102 -0
- package/src/memory/tool-usage-store.ts +15 -3
- package/src/memory/v2/__tests__/consolidation-job.test.ts +117 -12
- package/src/memory/v2/__tests__/consolidation-prompt-flag-gating-guard.test.ts +189 -0
- package/src/memory/v2/__tests__/injected-block-slugs.test.ts +90 -0
- package/src/memory/v2/__tests__/page-store.test.ts +33 -0
- package/src/memory/v2/__tests__/prompts-consolidation.test.ts +88 -15
- package/src/memory/v2/activation-store.ts +50 -1
- package/src/memory/v2/consolidation-job.ts +113 -29
- package/src/memory/v2/injected-block-slugs.ts +79 -0
- package/src/memory/v2/injection.ts +6 -1
- package/src/memory/v2/prompts/consolidation.ts +414 -13
- package/src/memory/v2/router.ts +2 -28
- package/src/memory/v2/static-context.ts +1 -1
- package/src/memory/v2/types.ts +16 -0
- package/src/notifications/__tests__/copy-composer.test.ts +244 -0
- package/src/notifications/access-request-copy.ts +298 -0
- package/src/notifications/adapters/slack.ts +3 -3
- package/src/notifications/adapters/telegram.ts +2 -1
- package/src/notifications/copy-composer.ts +49 -267
- package/src/notifications/decision-engine.ts +16 -35
- package/src/notifications/home-feed-side-effect.ts +1 -6
- package/src/oauth/oauth-store.ts +0 -9
- package/src/permissions/checker.test.ts +83 -1
- package/src/permissions/checker.ts +25 -2
- package/src/permissions/gateway-threshold-reader.test.ts +182 -0
- package/src/permissions/gateway-threshold-reader.ts +80 -0
- package/src/platform/client.ts +1 -3
- package/src/platform/feature-gate.ts +3 -12
- package/src/plugin-api/constants.ts +4 -2
- package/src/plugin-api/index.ts +63 -11
- package/src/plugin-api/types.ts +236 -71
- package/src/plugins/defaults/compaction/compact.ts +66 -2
- package/src/plugins/defaults/compaction/context-overflow-reducer.ts +240 -32
- package/src/plugins/defaults/compaction/corrected-target.ts +53 -0
- package/src/{daemon/context-overflow-policy.ts → plugins/defaults/compaction/overflow-policy.ts} +1 -1
- package/src/plugins/defaults/compaction/window-manager.ts +303 -1
- package/src/plugins/defaults/empty-response/hooks/post-model-call.ts +173 -0
- package/src/plugins/defaults/empty-response/hooks/stop.ts +11 -115
- package/src/plugins/defaults/empty-response/nudge-state-store.ts +46 -0
- package/src/plugins/defaults/history-repair/hooks/post-model-call.ts +50 -0
- package/src/plugins/defaults/history-repair/hooks/stop.ts +22 -0
- package/src/plugins/defaults/history-repair/repair-state-store.ts +51 -0
- package/src/plugins/defaults/history-repair/terminal.ts +39 -2
- package/src/plugins/defaults/image-recovery/detect.ts +25 -0
- package/src/plugins/defaults/image-recovery/hooks/post-model-call.ts +73 -0
- package/src/plugins/defaults/image-recovery/hooks/stop.ts +22 -0
- package/src/plugins/defaults/image-recovery/image-recovery-state-store.ts +48 -0
- package/src/plugins/defaults/image-recovery/package.json +14 -0
- package/src/{daemon/persist-unsendable-image.ts → plugins/defaults/image-recovery/recover.ts} +67 -14
- package/src/plugins/defaults/index.ts +71 -5
- package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +76 -112
- package/src/plugins/defaults/memory-retrieval/hooks/{user-prompt-submit-temp.ts → user-prompt-submit.ts} +83 -74
- package/src/plugins/defaults/memory-retrieval/injector-chain.ts +14 -8
- package/src/plugins/defaults/memory-retrieval/injectors.ts +2 -18
- package/src/plugins/defaults/memory-retrieval/package.json +14 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/carry-integration.test.ts +1157 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +683 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/live-integration.test.ts +161 -140
- package/src/plugins/defaults/memory-v3-shadow/__tests__/maintain-job.test.ts +160 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/orchestrate.test.ts +335 -316
- package/src/plugins/defaults/memory-v3-shadow/__tests__/pool-select.test.ts +145 -53
- package/src/plugins/defaults/memory-v3-shadow/__tests__/render-injection.test.ts +38 -1
- package/src/plugins/defaults/memory-v3-shadow/__tests__/selection-log-store.test.ts +18 -8
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-integration.test.ts +112 -71
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +192 -51
- package/src/plugins/defaults/memory-v3-shadow/__tests__/types.test.ts +4 -16
- package/src/plugins/defaults/memory-v3-shadow/card.test.ts +173 -0
- package/src/plugins/defaults/memory-v3-shadow/card.ts +116 -0
- package/src/plugins/defaults/memory-v3-shadow/core-set.test.ts +104 -0
- package/src/plugins/defaults/memory-v3-shadow/core-set.ts +59 -0
- package/src/plugins/defaults/memory-v3-shadow/ever-injected-store.test.ts +305 -0
- package/src/plugins/defaults/memory-v3-shadow/ever-injected-store.ts +278 -0
- package/src/plugins/defaults/memory-v3-shadow/hot-set.test.ts +138 -0
- package/src/plugins/defaults/memory-v3-shadow/hot-set.ts +85 -0
- package/src/plugins/defaults/memory-v3-shadow/injector.ts +331 -24
- package/src/plugins/defaults/memory-v3-shadow/maintain-job.ts +119 -13
- package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +169 -114
- package/src/plugins/defaults/memory-v3-shadow/page-content.ts +47 -16
- package/src/plugins/defaults/memory-v3-shadow/pool-select.ts +144 -66
- package/src/plugins/defaults/memory-v3-shadow/prune.test.ts +758 -0
- package/src/plugins/defaults/memory-v3-shadow/prune.ts +471 -0
- package/src/plugins/defaults/memory-v3-shadow/render-injection.ts +68 -16
- package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +19 -12
- package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +96 -43
- package/src/plugins/defaults/memory-v3-shadow/types.ts +34 -17
- package/src/plugins/defaults/title-generate/hooks/stop.ts +9 -11
- package/src/plugins/pipeline.ts +8 -5
- package/src/plugins/types.ts +5 -51
- package/src/providers/cache-control.ts +26 -0
- package/src/providers/inference/__tests__/base-url-route-validation.test.ts +1 -2
- package/src/providers/model-catalog.ts +13 -1
- package/src/providers/openai/__tests__/tool-choice-mapping.test.ts +147 -0
- package/src/providers/openai/chat-completions-provider.ts +46 -0
- package/src/providers/openai/responses-provider.ts +45 -0
- package/src/providers/registry.ts +0 -8
- package/src/runtime/__tests__/agent-wake.test.ts +0 -1
- package/src/runtime/agent-wake.ts +13 -0
- package/src/runtime/routes/__tests__/acp-routes.test.ts +151 -0
- package/src/runtime/routes/__tests__/consolidation-routes.test.ts +12 -50
- package/src/runtime/routes/__tests__/conversation-surface-routes.test.ts +322 -0
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +0 -62
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +31 -11
- package/src/runtime/routes/acp-routes.test.ts +106 -0
- package/src/runtime/routes/acp-routes.ts +248 -2
- package/src/runtime/routes/browser-tabs-routes.ts +1 -1
- package/src/runtime/routes/channel-verification-routes.ts +14 -5
- package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +0 -14
- package/src/runtime/routes/consolidation-routes.ts +6 -82
- package/src/runtime/routes/conversation-list-routes.ts +6 -0
- package/src/runtime/routes/conversation-management-routes.ts +71 -0
- package/src/runtime/routes/identity-routes.ts +8 -0
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +33 -34
- package/src/runtime/routes/inference-provider-connection-routes.ts +0 -45
- package/src/runtime/routes/plugins-routes.ts +34 -45
- package/src/runtime/routes/schedule-routes.ts +43 -5
- package/src/runtime/routes/settings-routes.ts +140 -15
- package/src/runtime/routes/skills-routes.ts +18 -6
- package/src/runtime/services/__tests__/conversation-serializer.test.ts +140 -0
- package/src/runtime/services/conversation-serializer.ts +38 -1
- package/src/runtime/verification-outbound-actions.ts +147 -2
- package/src/runtime/verification-templates.ts +29 -3
- package/src/schedule/schedule-store.ts +19 -0
- package/src/skills/catalog-install.ts +77 -13
- package/src/tasks/task-scheduler.ts +1 -0
- package/src/telemetry/types.ts +66 -1
- package/src/telemetry/usage-telemetry-reporter.test.ts +542 -13
- package/src/telemetry/usage-telemetry-reporter.ts +213 -20
- package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +49 -2
- package/src/tools/browser/__tests__/browser-status.test.ts +29 -5
- package/src/tools/browser/browser-execution.ts +27 -9
- package/src/tools/browser/cdp-client/__tests__/factory.test.ts +380 -4
- package/src/tools/browser/cdp-client/__tests__/host-bridge-cdp-client.test.ts +107 -0
- package/src/tools/browser/cdp-client/__tests__/types.test.ts +6 -1
- package/src/tools/browser/cdp-client/factory.ts +217 -17
- package/src/tools/browser/cdp-client/host-bridge-cdp-client.ts +67 -0
- package/src/tools/browser/cdp-client/types.ts +22 -2
- package/src/tools/credential-execution/make-authenticated-request.ts +2 -1
- package/src/tools/credential-execution/manage-secure-command-tool.ts +169 -164
- package/src/tools/credential-execution/run-authenticated-command.ts +2 -1
- package/src/tools/executor.ts +39 -7
- package/src/tools/registry.ts +387 -5
- package/src/tools/schedule/create.ts +16 -0
- package/src/tools/schedule/list.ts +12 -4
- package/src/tools/schedule/update.ts +12 -0
- package/src/tools/skills/load.ts +11 -6
- package/src/tools/terminal/safe-env.ts +2 -0
- package/src/tools/types.ts +65 -9
- package/src/tools/workspace-tools/loader.ts +673 -0
- package/src/usage/attribution.ts +28 -0
- package/src/util/device-id.ts +17 -3
- package/src/util/platform.ts +16 -0
- package/tsconfig.plugin-api.json +13 -0
- package/src/__tests__/plugin-external-api.test.ts +0 -68
- package/src/__tests__/plugin-skill-contribution.test.ts +0 -355
- package/src/daemon/message-types/browser.ts +0 -10
- package/src/notifications/__tests__/emit-signal-home-feed.test.ts +0 -187
- package/src/plugins/defaults/memory-v3-shadow/__tests__/fixtures/eval-turns.json +0 -36
- package/src/plugins/defaults/memory-v3-shadow/__tests__/fixtures/live-turns.json +0 -37
- package/src/plugins/defaults/memory-v3-shadow/__tests__/working-set-eviction.test.ts +0 -106
- package/src/plugins/defaults/memory-v3-shadow/__tests__/working-set-skeleton.test.ts +0 -44
- package/src/plugins/defaults/memory-v3-shadow/working-set.ts +0 -91
- package/src/plugins/external-api.ts +0 -114
- package/src/plugins/plugin-skill-contributions.ts +0 -292
|
@@ -1,33 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
|
|
3
|
-
/**
|
|
4
|
-
* Memory v3 (section-grain lane retrieval) working-set configuration.
|
|
5
|
-
*
|
|
6
|
-
* The working set is the per-conversation set of concept pages carried
|
|
7
|
-
* forward across turns. Eviction keeps it bounded: pages unseen for longer
|
|
8
|
-
* than `evictWindow` turns are dropped, and the set is capped at `maxPages`.
|
|
9
|
-
*/
|
|
10
|
-
export const MemoryV3WorkingSetSchema = z
|
|
11
|
-
.object({
|
|
12
|
-
maxPages: z
|
|
13
|
-
.number({ error: "memory.v3.workingSet.maxPages must be a number" })
|
|
14
|
-
.int("memory.v3.workingSet.maxPages must be an integer")
|
|
15
|
-
.positive("memory.v3.workingSet.maxPages must be a positive integer")
|
|
16
|
-
.default(150)
|
|
17
|
-
.describe(
|
|
18
|
-
"Upper bound on the number of pages retained in the working set. Once exceeded, the least-salient non-pinned pages are evicted until the set fits.",
|
|
19
|
-
),
|
|
20
|
-
evictWindow: z
|
|
21
|
-
.number({ error: "memory.v3.workingSet.evictWindow must be a number" })
|
|
22
|
-
.int("memory.v3.workingSet.evictWindow must be an integer")
|
|
23
|
-
.positive("memory.v3.workingSet.evictWindow must be a positive integer")
|
|
24
|
-
.default(5)
|
|
25
|
-
.describe(
|
|
26
|
-
"Number of turns a non-pinned page may go unseen before it is evicted from the working set.",
|
|
27
|
-
),
|
|
28
|
-
})
|
|
29
|
-
.describe("Memory v3 working-set retention/eviction tuning.");
|
|
30
|
-
|
|
31
3
|
/**
|
|
32
4
|
* Edge-lane tuning for the link-graph expansion that folds a turn's lexical and
|
|
33
5
|
* dense seeds outward to their first-class neighbours.
|
|
@@ -67,10 +39,111 @@ export const MemoryV3EdgeSchema = z
|
|
|
67
39
|
})
|
|
68
40
|
.describe("Memory v3 edge-lane (link-graph expansion) tuning.");
|
|
69
41
|
|
|
42
|
+
/**
|
|
43
|
+
* Hot-set lane tuning: the top-K pages by exponentially-decayed selection
|
|
44
|
+
* frequency (frecency) folded into the candidate pool as a stable lane.
|
|
45
|
+
*/
|
|
46
|
+
export const MemoryV3HotSetSchema = z
|
|
47
|
+
.object({
|
|
48
|
+
k: z
|
|
49
|
+
.number({ error: "memory.v3.hotSet.k must be a number" })
|
|
50
|
+
.int("memory.v3.hotSet.k must be an integer")
|
|
51
|
+
.positive("memory.v3.hotSet.k must be a positive integer")
|
|
52
|
+
.default(40)
|
|
53
|
+
.describe(
|
|
54
|
+
"Number of top frecency-scored pages included in the hot-set lane.",
|
|
55
|
+
),
|
|
56
|
+
halfLifeDays: z
|
|
57
|
+
.number({ error: "memory.v3.hotSet.halfLifeDays must be a number" })
|
|
58
|
+
.positive("memory.v3.hotSet.halfLifeDays must be a positive number")
|
|
59
|
+
.default(14)
|
|
60
|
+
.describe(
|
|
61
|
+
"Frecency decay half-life in days: a selection this old contributes half the weight of one made now.",
|
|
62
|
+
),
|
|
63
|
+
})
|
|
64
|
+
.describe("Memory v3 hot-set lane (decayed selection frequency) tuning.");
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Ephemeral section-spotlight tuning: how many of the current turn's selected
|
|
68
|
+
* finder hits render their matched section into the `<memory_spotlight>`
|
|
69
|
+
* block, and how many previous turns' spotlight entries are carried along
|
|
70
|
+
* before they age out. The block is strip-and-replaced every turn, so its
|
|
71
|
+
* size is bounded by `n × (windowTurns + 1)` entries.
|
|
72
|
+
*/
|
|
73
|
+
export const MemoryV3SpotlightSchema = z
|
|
74
|
+
.object({
|
|
75
|
+
n: z
|
|
76
|
+
.number({ error: "memory.v3.spotlight.n must be a number" })
|
|
77
|
+
.int("memory.v3.spotlight.n must be an integer")
|
|
78
|
+
.positive("memory.v3.spotlight.n must be a positive integer")
|
|
79
|
+
.default(6)
|
|
80
|
+
.describe(
|
|
81
|
+
"Number of the current turn's selected finder hits whose matched sections render into the spotlight block.",
|
|
82
|
+
),
|
|
83
|
+
windowTurns: z
|
|
84
|
+
.number({ error: "memory.v3.spotlight.windowTurns must be a number" })
|
|
85
|
+
.int("memory.v3.spotlight.windowTurns must be an integer")
|
|
86
|
+
.nonnegative(
|
|
87
|
+
"memory.v3.spotlight.windowTurns must be a non-negative integer",
|
|
88
|
+
)
|
|
89
|
+
.default(2)
|
|
90
|
+
.describe(
|
|
91
|
+
"Number of previous turns whose spotlight entries are carried into the current block before aging out (0 = current turn only).",
|
|
92
|
+
),
|
|
93
|
+
})
|
|
94
|
+
.describe("Memory v3 ephemeral section-spotlight tuning.");
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Prune-valve bounds on the resident (non-pruned) frozen-card footprint.
|
|
98
|
+
*
|
|
99
|
+
* Frozen cards accumulate in history with no per-turn bound, so the valve is
|
|
100
|
+
* the structural backstop: once resident card bytes exceed
|
|
101
|
+
* `maxResidentBytes`, the least-recently-selected non-core/non-hot cards are
|
|
102
|
+
* pruned until the footprint is back at `targetResidentBytes`.
|
|
103
|
+
*
|
|
104
|
+
* Defaults rationale: production v2 ran 303KB of accumulated memory unpruned
|
|
105
|
+
* on the widest observed conversation — 384KB max / 256KB target make the
|
|
106
|
+
* valve insurance for growth beyond that, not routine behavior.
|
|
107
|
+
*/
|
|
108
|
+
export const MemoryV3PruneSchema = z
|
|
109
|
+
.object({
|
|
110
|
+
maxResidentBytes: z
|
|
111
|
+
.number({ error: "memory.v3.prune.maxResidentBytes must be a number" })
|
|
112
|
+
.int("memory.v3.prune.maxResidentBytes must be an integer")
|
|
113
|
+
.positive("memory.v3.prune.maxResidentBytes must be a positive integer")
|
|
114
|
+
.default(393216 /* 384KB */)
|
|
115
|
+
.describe(
|
|
116
|
+
"Resident (non-pruned) card bytes above which the prune valve fires.",
|
|
117
|
+
),
|
|
118
|
+
targetResidentBytes: z
|
|
119
|
+
.number({
|
|
120
|
+
error: "memory.v3.prune.targetResidentBytes must be a number",
|
|
121
|
+
})
|
|
122
|
+
.int("memory.v3.prune.targetResidentBytes must be an integer")
|
|
123
|
+
.positive(
|
|
124
|
+
"memory.v3.prune.targetResidentBytes must be a positive integer",
|
|
125
|
+
)
|
|
126
|
+
.default(262144 /* 256KB */)
|
|
127
|
+
.describe(
|
|
128
|
+
"Resident card bytes a fired prune reduces the footprint to (must be below maxResidentBytes).",
|
|
129
|
+
),
|
|
130
|
+
})
|
|
131
|
+
.refine((value) => value.targetResidentBytes < value.maxResidentBytes, {
|
|
132
|
+
error:
|
|
133
|
+
"memory.v3.prune.targetResidentBytes must be less than memory.v3.prune.maxResidentBytes",
|
|
134
|
+
})
|
|
135
|
+
.describe("Memory v3 prune-valve (resident card footprint) bounds.");
|
|
136
|
+
|
|
137
|
+
// NOTE: a retired `workingSet` sub-config (maxPages/evictWindow for the old
|
|
138
|
+
// per-turn carry set) used to live here. Existing user config files may still
|
|
139
|
+
// contain the key; zod default unknown-key stripping accepts and ignores it,
|
|
140
|
+
// so legacy configs keep parsing. Do not make this object `.strict()`.
|
|
70
141
|
export const MemoryV3ConfigSchema = z
|
|
71
142
|
.object({
|
|
72
|
-
|
|
73
|
-
|
|
143
|
+
prune: MemoryV3PruneSchema.default(MemoryV3PruneSchema.parse({})),
|
|
144
|
+
hotSet: MemoryV3HotSetSchema.default(MemoryV3HotSetSchema.parse({})),
|
|
145
|
+
spotlight: MemoryV3SpotlightSchema.default(
|
|
146
|
+
MemoryV3SpotlightSchema.parse({}),
|
|
74
147
|
),
|
|
75
148
|
needleK: z
|
|
76
149
|
.number({ error: "memory.v3.needleK must be a number" })
|
|
@@ -90,8 +163,6 @@ export const MemoryV3ConfigSchema = z
|
|
|
90
163
|
),
|
|
91
164
|
edge: MemoryV3EdgeSchema.default(MemoryV3EdgeSchema.parse({})),
|
|
92
165
|
})
|
|
93
|
-
.describe(
|
|
94
|
-
"Memory v3 — section-grain lane retrieval with a carry-forward working set",
|
|
95
|
-
);
|
|
166
|
+
.describe("Memory v3 — section-grain lane retrieval");
|
|
96
167
|
|
|
97
168
|
export type MemoryV3Config = z.infer<typeof MemoryV3ConfigSchema>;
|
|
@@ -229,7 +229,7 @@ export function seedInferenceProfiles(
|
|
|
229
229
|
// profiles without duplicating; we have to honor those edits across
|
|
230
230
|
// reseeds or they'd silently revert on every boot. Carry by
|
|
231
231
|
// key-presence rather than truthiness so an explicit `null` (user
|
|
232
|
-
// cleared the label) survives too.
|
|
232
|
+
// cleared the label) survives too.
|
|
233
233
|
//
|
|
234
234
|
// BYOK seed defaults (off-platform only):
|
|
235
235
|
// • label: " (Managed)" suffix disambiguates managed profile labels
|
package/src/config/skills.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
type Dirent,
|
|
2
3
|
existsSync,
|
|
3
4
|
lstatSync,
|
|
4
5
|
readdirSync,
|
|
@@ -17,18 +18,16 @@ import {
|
|
|
17
18
|
|
|
18
19
|
import { z } from "zod";
|
|
19
20
|
|
|
20
|
-
import {
|
|
21
|
-
getPluginContributedSkillDefinition,
|
|
22
|
-
getPluginContributedSkillSummaries,
|
|
23
|
-
} from "../plugins/plugin-skill-contributions.js";
|
|
24
21
|
import { parseFrontmatterFields } from "../skills/frontmatter.js";
|
|
25
22
|
import type { InlineCommandExpansion } from "../skills/inline-command-expansions.js";
|
|
26
23
|
import { parseInlineCommandExpansions } from "../skills/inline-command-expansions.js";
|
|
27
24
|
import { parseToolManifestFile } from "../skills/tool-manifest.js";
|
|
28
25
|
import { computeSkillVersionHash } from "../skills/version-hash.js";
|
|
26
|
+
import type { OwnerInfo } from "../tools/types.js";
|
|
29
27
|
import { getLogger } from "../util/logger.js";
|
|
30
28
|
import {
|
|
31
29
|
getWorkspaceDirDisplay,
|
|
30
|
+
getWorkspacePluginsDir,
|
|
32
31
|
getWorkspaceSkillsDir,
|
|
33
32
|
} from "../util/platform.js";
|
|
34
33
|
import { stripCommentLines } from "../util/strip-comment-lines.js";
|
|
@@ -47,6 +46,7 @@ const VellumMetadataSchema = z
|
|
|
47
46
|
"feature-flag": z.string().optional(),
|
|
48
47
|
"activation-hints": z.array(z.string()).optional(),
|
|
49
48
|
"avoid-when": z.array(z.string()).optional(),
|
|
49
|
+
category: z.string().optional(),
|
|
50
50
|
})
|
|
51
51
|
.passthrough();
|
|
52
52
|
|
|
@@ -66,9 +66,9 @@ const SkillMetadataSchema = z
|
|
|
66
66
|
* - `workspace`: user-authored skill living in a conversation's working dir.
|
|
67
67
|
* - `extra`: third-party directory roots passed via `loadSkillCatalog`'s
|
|
68
68
|
* `extraDirs` argument (primarily for tests).
|
|
69
|
-
* - `plugin`:
|
|
70
|
-
*
|
|
71
|
-
*
|
|
69
|
+
* - `plugin`: shipped on disk inside an installed plugin at
|
|
70
|
+
* `<workspaceDir>/plugins/<name>/skills/<id>/SKILL.md`, attributed back to
|
|
71
|
+
* the owning plugin via its `owner` descriptor.
|
|
72
72
|
*/
|
|
73
73
|
export type SkillSource =
|
|
74
74
|
| "bundled"
|
|
@@ -90,6 +90,13 @@ export interface SkillSummary {
|
|
|
90
90
|
icon?: string;
|
|
91
91
|
emoji?: string;
|
|
92
92
|
source: SkillSource;
|
|
93
|
+
/**
|
|
94
|
+
* Ownership descriptor identifying the extension that ships this skill,
|
|
95
|
+
* reusing the same {@link OwnerInfo} model the tool registry uses. Set only
|
|
96
|
+
* for `source: "plugin"` skills — `{ kind: "plugin", id: <plugin dir name> }`
|
|
97
|
+
* — attributing them to the installed plugin under `<workspaceDir>/plugins/`.
|
|
98
|
+
*/
|
|
99
|
+
owner?: OwnerInfo;
|
|
93
100
|
/** Parsed tool manifest metadata, if the skill has a valid TOOLS.json. */
|
|
94
101
|
toolManifest?: SkillToolManifestMeta;
|
|
95
102
|
/** IDs of child skills that this skill includes (metadata-only, not auto-activated). */
|
|
@@ -100,6 +107,8 @@ export interface SkillSummary {
|
|
|
100
107
|
activationHints?: string[];
|
|
101
108
|
/** Conditions under which this skill should NOT be loaded. */
|
|
102
109
|
avoidWhen?: string[];
|
|
110
|
+
/** Category slug declared in frontmatter, used as a fallback when the skill is not in the Vellum catalog. */
|
|
111
|
+
category?: string;
|
|
103
112
|
/** Parsed inline command expansion descriptors (`!\`command\``) found in the skill body. */
|
|
104
113
|
inlineCommandExpansions?: InlineCommandExpansion[];
|
|
105
114
|
}
|
|
@@ -217,6 +226,7 @@ interface ParsedFrontmatter {
|
|
|
217
226
|
featureFlag?: string;
|
|
218
227
|
activationHints?: string[];
|
|
219
228
|
avoidWhen?: string[];
|
|
229
|
+
category?: string;
|
|
220
230
|
inlineCommandExpansions?: InlineCommandExpansion[];
|
|
221
231
|
}
|
|
222
232
|
|
|
@@ -327,6 +337,11 @@ function parseFrontmatter(
|
|
|
327
337
|
const activationHints = normalizeStringArray(vellum?.["activation-hints"]);
|
|
328
338
|
const avoidWhen = normalizeStringArray(vellum?.["avoid-when"]);
|
|
329
339
|
|
|
340
|
+
const category =
|
|
341
|
+
typeof vellum?.category === "string" && vellum.category.trim().length > 0
|
|
342
|
+
? vellum.category.trim()
|
|
343
|
+
: undefined;
|
|
344
|
+
|
|
330
345
|
const strippedBody = stripCommentLines(body);
|
|
331
346
|
|
|
332
347
|
// Parse inline command expansions from the body (after frontmatter/comment stripping)
|
|
@@ -350,6 +365,7 @@ function parseFrontmatter(
|
|
|
350
365
|
featureFlag,
|
|
351
366
|
activationHints,
|
|
352
367
|
avoidWhen,
|
|
368
|
+
category,
|
|
353
369
|
inlineCommandExpansions,
|
|
354
370
|
};
|
|
355
371
|
}
|
|
@@ -506,6 +522,7 @@ function readSkillFromDirectory(
|
|
|
506
522
|
featureFlag: parsed.featureFlag,
|
|
507
523
|
activationHints: parsed.activationHints,
|
|
508
524
|
avoidWhen: parsed.avoidWhen,
|
|
525
|
+
category: parsed.category,
|
|
509
526
|
inlineCommandExpansions: parsed.inlineCommandExpansions,
|
|
510
527
|
};
|
|
511
528
|
} catch (err) {
|
|
@@ -558,6 +575,7 @@ function readBundledSkillFromDirectory(
|
|
|
558
575
|
featureFlag: parsed.featureFlag,
|
|
559
576
|
activationHints: parsed.activationHints,
|
|
560
577
|
avoidWhen: parsed.avoidWhen,
|
|
578
|
+
category: parsed.category,
|
|
561
579
|
inlineCommandExpansions: parsed.inlineCommandExpansions,
|
|
562
580
|
};
|
|
563
581
|
} catch (err) {
|
|
@@ -618,6 +636,7 @@ function loadBundledSkills(): SkillSummary[] {
|
|
|
618
636
|
featureFlag: skill.featureFlag,
|
|
619
637
|
activationHints: skill.activationHints,
|
|
620
638
|
avoidWhen: skill.avoidWhen,
|
|
639
|
+
category: skill.category,
|
|
621
640
|
inlineCommandExpansions: skill.inlineCommandExpansions,
|
|
622
641
|
});
|
|
623
642
|
}
|
|
@@ -646,6 +665,82 @@ function discoverSkillDirectories(skillsDir: string): string[] {
|
|
|
646
665
|
return dirs.sort((a, b) => a.localeCompare(b));
|
|
647
666
|
}
|
|
648
667
|
|
|
668
|
+
/**
|
|
669
|
+
* Whether `pluginDir` is a recognized installed plugin: it must carry a
|
|
670
|
+
* parseable `package.json` whose `name` equals the directory name. This
|
|
671
|
+
* mirrors the external plugin loader's recognition gate, which skips any
|
|
672
|
+
* directory whose `manifest.name` does not match its directory name.
|
|
673
|
+
*/
|
|
674
|
+
function isRecognizedPluginDir(pluginDir: string, dirName: string): boolean {
|
|
675
|
+
const manifestPath = join(pluginDir, "package.json");
|
|
676
|
+
if (!existsSync(manifestPath)) return false;
|
|
677
|
+
try {
|
|
678
|
+
const parsed: unknown = JSON.parse(readFileSync(manifestPath, "utf-8"));
|
|
679
|
+
return (
|
|
680
|
+
typeof parsed === "object" &&
|
|
681
|
+
parsed !== null &&
|
|
682
|
+
"name" in parsed &&
|
|
683
|
+
(parsed as { name: unknown }).name === dirName
|
|
684
|
+
);
|
|
685
|
+
} catch (err) {
|
|
686
|
+
log.warn(
|
|
687
|
+
{ err, manifestPath },
|
|
688
|
+
"Skipping plugin dir with unparseable package.json for resident skills",
|
|
689
|
+
);
|
|
690
|
+
return false;
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
/**
|
|
695
|
+
* Discover skills shipped on disk inside installed plugins. Each installed
|
|
696
|
+
* plugin — a directory under `<workspaceDir>/plugins/` recognized by
|
|
697
|
+
* {@link isRecognizedPluginDir} — may ship skills at `skills/<id>/SKILL.md`.
|
|
698
|
+
* Returned summaries are attributed to the owning plugin via their `owner`
|
|
699
|
+
* descriptor (`{ kind: "plugin", id: <plugin dir name> }`).
|
|
700
|
+
*/
|
|
701
|
+
function discoverPluginResidentSkills(): SkillSummary[] {
|
|
702
|
+
const pluginsDir = getWorkspacePluginsDir();
|
|
703
|
+
if (!existsSync(pluginsDir)) return [];
|
|
704
|
+
|
|
705
|
+
let entries: Dirent[];
|
|
706
|
+
try {
|
|
707
|
+
entries = readdirSync(pluginsDir, { withFileTypes: true });
|
|
708
|
+
} catch (err) {
|
|
709
|
+
log.warn(
|
|
710
|
+
{ err, pluginsDir },
|
|
711
|
+
"Failed to read plugins directory for resident skills",
|
|
712
|
+
);
|
|
713
|
+
return [];
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
const summaries: SkillSummary[] = [];
|
|
717
|
+
for (const entry of entries) {
|
|
718
|
+
if (!entry.isDirectory() && !entry.isSymbolicLink()) continue;
|
|
719
|
+
const pluginDir = join(pluginsDir, entry.name);
|
|
720
|
+
// Mirror the plugin loader's recognition gate: a directory is a real
|
|
721
|
+
// installed plugin only if it carries a parseable `package.json` whose
|
|
722
|
+
// `name` matches the directory. This rejects staging dirs, stray files,
|
|
723
|
+
// and malformed/mismatched clones (e.g. an un-adapted `caveman-installer`)
|
|
724
|
+
// that the loader itself would skip, so the catalog never surfaces skills
|
|
725
|
+
// from a directory the runtime would refuse to load.
|
|
726
|
+
if (!isRecognizedPluginDir(pluginDir, entry.name)) continue;
|
|
727
|
+
|
|
728
|
+
const skillsDir = join(pluginDir, "skills");
|
|
729
|
+
if (!existsSync(skillsDir)) continue;
|
|
730
|
+
|
|
731
|
+
for (const directory of discoverSkillDirectories(skillsDir)) {
|
|
732
|
+
const skill = readSkillFromDirectory(directory, skillsDir, "plugin");
|
|
733
|
+
if (!skill) continue;
|
|
734
|
+
summaries.push({
|
|
735
|
+
...skillSummaryFromDefinition(skill, "plugin"),
|
|
736
|
+
owner: { kind: "plugin", id: entry.name },
|
|
737
|
+
});
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
return summaries;
|
|
742
|
+
}
|
|
743
|
+
|
|
649
744
|
// ─── Catalog loading ─────────────────────────────────────────────────────────
|
|
650
745
|
|
|
651
746
|
function skillSummaryFromDefinition(
|
|
@@ -668,6 +763,7 @@ function skillSummaryFromDefinition(
|
|
|
668
763
|
featureFlag: skill.featureFlag,
|
|
669
764
|
activationHints: skill.activationHints,
|
|
670
765
|
avoidWhen: skill.avoidWhen,
|
|
766
|
+
category: skill.category,
|
|
671
767
|
inlineCommandExpansions: skill.inlineCommandExpansions,
|
|
672
768
|
};
|
|
673
769
|
}
|
|
@@ -721,6 +817,7 @@ export function loadSkillCatalog(
|
|
|
721
817
|
featureFlag: parsed.featureFlag,
|
|
722
818
|
activationHints: parsed.activationHints,
|
|
723
819
|
avoidWhen: parsed.avoidWhen,
|
|
820
|
+
category: parsed.category,
|
|
724
821
|
inlineCommandExpansions: parsed.inlineCommandExpansions,
|
|
725
822
|
});
|
|
726
823
|
} catch (err) {
|
|
@@ -754,12 +851,11 @@ export function loadSkillCatalog(
|
|
|
754
851
|
catalog.push(skill);
|
|
755
852
|
}
|
|
756
853
|
|
|
757
|
-
//
|
|
758
|
-
// managed and workspace so
|
|
759
|
-
// override a plugin-provided skill by declaring the
|
|
760
|
-
//
|
|
761
|
-
|
|
762
|
-
const pluginSkills = getPluginContributedSkillSummaries();
|
|
854
|
+
// Discover skills shipped on disk inside installed plugins. They sit above
|
|
855
|
+
// bundled/extra but below managed and workspace so a user-authored
|
|
856
|
+
// filesystem skill can override a plugin-provided skill by declaring the
|
|
857
|
+
// same id under `$VELLUM_WORKSPACE_DIR/skills/`.
|
|
858
|
+
const pluginSkills = discoverPluginResidentSkills();
|
|
763
859
|
for (const skill of pluginSkills) {
|
|
764
860
|
if (seenIds.has(skill.id)) {
|
|
765
861
|
const existingIndex = catalog.findIndex((s) => s.id === skill.id);
|
|
@@ -769,14 +865,14 @@ export function loadSkillCatalog(
|
|
|
769
865
|
catalog[existingIndex].source === "extra")
|
|
770
866
|
) {
|
|
771
867
|
log.info(
|
|
772
|
-
{ id: skill.id,
|
|
868
|
+
{ id: skill.id, pluginName: skill.owner?.id },
|
|
773
869
|
"Plugin skill overrides bundled/extra skill",
|
|
774
870
|
);
|
|
775
871
|
catalog[existingIndex] = skill;
|
|
776
872
|
continue;
|
|
777
873
|
}
|
|
778
874
|
log.warn(
|
|
779
|
-
{ id: skill.id },
|
|
875
|
+
{ id: skill.id, pluginName: skill.owner?.id },
|
|
780
876
|
"Skipping duplicate plugin skill id (already present in catalog)",
|
|
781
877
|
);
|
|
782
878
|
continue;
|
|
@@ -857,6 +953,7 @@ export function loadSkillCatalog(
|
|
|
857
953
|
featureFlag: parsed.featureFlag,
|
|
858
954
|
activationHints: parsed.activationHints,
|
|
859
955
|
avoidWhen: parsed.avoidWhen,
|
|
956
|
+
category: parsed.category,
|
|
860
957
|
inlineCommandExpansions: parsed.inlineCommandExpansions,
|
|
861
958
|
};
|
|
862
959
|
|
|
@@ -992,41 +1089,13 @@ export function listReferenceFiles(directoryPath: string): string | null {
|
|
|
992
1089
|
}
|
|
993
1090
|
|
|
994
1091
|
function loadSkillDefinition(skill: SkillSummary): SkillLookupResult {
|
|
995
|
-
// Plugin-contributed skills live in-memory, not on disk. The registry
|
|
996
|
-
// (see `plugins/plugin-skill-contributions.ts`) stores a pre-built
|
|
997
|
-
// SkillDefinition we can return directly — no file read, no frontmatter
|
|
998
|
-
// parse. We still run placeholder / feature-gate substitution on the
|
|
999
|
-
// body so plugin skills behave the same as filesystem skills from the
|
|
1000
|
-
// model's perspective.
|
|
1001
|
-
if (skill.source === "plugin") {
|
|
1002
|
-
const pluginDef = getPluginContributedSkillDefinition(skill.id);
|
|
1003
|
-
if (!pluginDef) {
|
|
1004
|
-
// The skill was in the catalog when the selector ran but has since
|
|
1005
|
-
// been unregistered (plugin hot-reload, shutdown race). Fail loudly
|
|
1006
|
-
// rather than returning a mystery undefined.
|
|
1007
|
-
return {
|
|
1008
|
-
error: `Plugin-contributed skill "${skill.id}" is no longer registered`,
|
|
1009
|
-
};
|
|
1010
|
-
}
|
|
1011
|
-
// Shallow-clone so mutating the returned `body` below does not touch
|
|
1012
|
-
// the registry's stored copy — the registry must stay the source of
|
|
1013
|
-
// truth across repeated `skill_load` calls.
|
|
1014
|
-
const loaded: SkillDefinition = { ...pluginDef };
|
|
1015
|
-
loaded.body = loaded.body.replaceAll("{baseDir}", loaded.directoryPath);
|
|
1016
|
-
loaded.body = loaded.body.replaceAll(
|
|
1017
|
-
"{workspaceDir}",
|
|
1018
|
-
getWorkspaceDirDisplay(),
|
|
1019
|
-
);
|
|
1020
|
-
loaded.body = applyFeatureGatedSections(loaded.body);
|
|
1021
|
-
return { skill: loaded };
|
|
1022
|
-
}
|
|
1023
|
-
|
|
1024
1092
|
let loaded: SkillDefinition | null;
|
|
1025
1093
|
if (skill.bundled) {
|
|
1026
1094
|
loaded = readBundledSkillFromDirectory(skill.directoryPath);
|
|
1027
|
-
} else if (skill.source === "workspace") {
|
|
1028
|
-
// Workspace skills live outside
|
|
1029
|
-
//
|
|
1095
|
+
} else if (skill.source === "workspace" || skill.source === "plugin") {
|
|
1096
|
+
// Workspace and plugin-resident skills live outside
|
|
1097
|
+
// $VELLUM_WORKSPACE_DIR/skills, so use the skill's parent directory as the
|
|
1098
|
+
// root to avoid the isOutsideSkillsRoot rejection.
|
|
1030
1099
|
loaded = readSkillFromDirectory(
|
|
1031
1100
|
skill.directoryPath,
|
|
1032
1101
|
dirname(skill.directoryPath),
|
|
@@ -1042,6 +1111,7 @@ function loadSkillDefinition(skill: SkillSummary): SkillLookupResult {
|
|
|
1042
1111
|
if (!loaded) {
|
|
1043
1112
|
return { error: `Failed to load SKILL.md for "${skill.id}"` };
|
|
1044
1113
|
}
|
|
1114
|
+
loaded.owner = skill.owner;
|
|
1045
1115
|
// Replace {baseDir} placeholders with the actual skill directory path
|
|
1046
1116
|
loaded.body = loaded.body.replaceAll("{baseDir}", loaded.directoryPath);
|
|
1047
1117
|
// Replace {workspaceDir} placeholders with the runtime workspace display path
|
package/src/context/compactor.ts
CHANGED
|
@@ -743,12 +743,19 @@ export async function runAssistantDrivenCompaction(
|
|
|
743
743
|
let response: ProviderResponse;
|
|
744
744
|
try {
|
|
745
745
|
response = await args.provider.sendMessage(requestMessages, {
|
|
746
|
+
// Tools are passed so the cached prefix (system prompt + tools +
|
|
747
|
+
// history) matches the agent's main-turn cache key. Force
|
|
748
|
+
// `tool_choice: "none"` so the model can only answer with the
|
|
749
|
+
// `<compaction_result>` text and never burns the turn on a tool call
|
|
750
|
+
// it cannot complete — an empty text response yields no parseable
|
|
751
|
+
// summary, stalls compaction, and exhausts the agent loop budget.
|
|
746
752
|
tools: args.tools,
|
|
747
753
|
systemPrompt: args.systemPrompt,
|
|
748
754
|
signal: args.signal,
|
|
749
755
|
config: {
|
|
750
756
|
callSite: COMPACTION_CALL_SITE,
|
|
751
757
|
usageTracking: "manual",
|
|
758
|
+
tool_choice: { type: "none" },
|
|
752
759
|
...(args.overrideProfile
|
|
753
760
|
? { overrideProfile: args.overrideProfile }
|
|
754
761
|
: {}),
|
|
@@ -1113,12 +1120,16 @@ export async function runEmergencyCompaction(
|
|
|
1113
1120
|
let response: ProviderResponse;
|
|
1114
1121
|
try {
|
|
1115
1122
|
response = await args.provider.sendMessage(requestMessages, {
|
|
1123
|
+
// See the assistant-driven path: tools keep the prefix cache warm, but
|
|
1124
|
+
// `tool_choice: "none"` forces a text-only `<compaction_result>` so the
|
|
1125
|
+
// model can't stall compaction by emitting an uncompletable tool call.
|
|
1116
1126
|
tools: args.tools,
|
|
1117
1127
|
systemPrompt: args.systemPrompt,
|
|
1118
1128
|
signal: args.signal,
|
|
1119
1129
|
config: {
|
|
1120
1130
|
callSite: COMPACTION_CALL_SITE,
|
|
1121
1131
|
usageTracking: "manual",
|
|
1132
|
+
tool_choice: { type: "none" },
|
|
1122
1133
|
...(args.overrideProfile
|
|
1123
1134
|
? { overrideProfile: args.overrideProfile }
|
|
1124
1135
|
: {}),
|
|
@@ -11,6 +11,10 @@
|
|
|
11
11
|
* loop (which drives the compaction pipeline) and the compactor can call it
|
|
12
12
|
* without reaching up into the daemon orchestrator.
|
|
13
13
|
*/
|
|
14
|
+
import {
|
|
15
|
+
MEMORY_SPOTLIGHT_PREFIX,
|
|
16
|
+
MEMORY_SPOTLIGHT_SUFFIX,
|
|
17
|
+
} from "../memory/memory-marker.js";
|
|
14
18
|
import type { Message } from "../providers/types.js";
|
|
15
19
|
|
|
16
20
|
/**
|
|
@@ -56,6 +60,34 @@ export function stripUserTextBlocksByPrefix(
|
|
|
56
60
|
);
|
|
57
61
|
}
|
|
58
62
|
|
|
63
|
+
/**
|
|
64
|
+
* Full-wrapper matcher for the memory-v3 ephemeral `<memory_spotlight>` block.
|
|
65
|
+
* Shared by the per-turn scoped strip ({@link stripSpotlightInjections}) and
|
|
66
|
+
* the compaction pipeline below so both recognize exactly the same wrapper.
|
|
67
|
+
*/
|
|
68
|
+
const MEMORY_SPOTLIGHT_MATCHER: InjectionMatcher = {
|
|
69
|
+
prefix: MEMORY_SPOTLIGHT_PREFIX,
|
|
70
|
+
suffix: MEMORY_SPOTLIGHT_SUFFIX,
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Remove memory-v3 `<memory_spotlight>` blocks from every user message — and
|
|
75
|
+
* ONLY those blocks. The spotlight is ephemeral by contract: runtime assembly
|
|
76
|
+
* strip-and-replaces it each turn (the previous turn's spotlight is stale),
|
|
77
|
+
* while the frozen `<memory>` card blocks stay byte-identical in history for
|
|
78
|
+
* prompt caching. This is deliberately a scoped, single-id strip — the old
|
|
79
|
+
* whole-layer `stripAllMemoryInjections` replace is gone.
|
|
80
|
+
*/
|
|
81
|
+
export function stripSpotlightInjections(messages: Message[]): Message[] {
|
|
82
|
+
return stripUserTextBlocksByPrefix(messages, [MEMORY_SPOTLIGHT_MATCHER]);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** `<NOW.md>` scratchpad prefixes (current tag, pre-line-limit variant, legacy `<now_scratchpad>`) — shared with `stripNowScratchpad` so the two strip paths can't drift. */
|
|
86
|
+
export const NOW_SCRATCHPAD_STRIP_PREFIXES: InjectionMatcher[] = [
|
|
87
|
+
"<NOW.md Always keep this up to date",
|
|
88
|
+
"<now_scratchpad>",
|
|
89
|
+
];
|
|
90
|
+
|
|
59
91
|
/** Matchers stripped by the pipeline (order doesn't matter — single pass). */
|
|
60
92
|
const RUNTIME_INJECTION_PREFIXES: InjectionMatcher[] = [
|
|
61
93
|
"<channel_capabilities>",
|
|
@@ -85,6 +117,11 @@ const RUNTIME_INJECTION_PREFIXES: InjectionMatcher[] = [
|
|
|
85
117
|
// matches the full-wrapper requirement in `countMemoryPrefixBlocks`.
|
|
86
118
|
{ prefix: "<memory>\n", suffix: "\n</memory>" },
|
|
87
119
|
{ prefix: "<info>\n", suffix: "\n</info>" },
|
|
120
|
+
// The memory-v3 ephemeral spotlight block. Normally strip-and-replaced every
|
|
121
|
+
// turn by `stripSpotlightInjections`, but registered here too so compaction
|
|
122
|
+
// and overflow recovery remove a stale spotlight along with the rest of the
|
|
123
|
+
// runtime injections. Full-wrapper shape for the same reason as `<memory>`.
|
|
124
|
+
MEMORY_SPOTLIGHT_MATCHER,
|
|
88
125
|
"<voice_call_control>",
|
|
89
126
|
"<workspace_top_level>", // backward-compat: strip legacy workspace blocks
|
|
90
127
|
// The `<workspace>` top-level block is stripped so each compaction re-injects
|
|
@@ -100,10 +137,7 @@ const RUNTIME_INJECTION_PREFIXES: InjectionMatcher[] = [
|
|
|
100
137
|
"<active_workspace>",
|
|
101
138
|
"<active_dynamic_page>",
|
|
102
139
|
"<non_interactive_context>",
|
|
103
|
-
|
|
104
|
-
// variant that may linger in in-flight histories during a rolling deploy.
|
|
105
|
-
"<NOW.md Always keep this up to date",
|
|
106
|
-
"<now_scratchpad>", // backward-compat: strip legacy blocks from pre-rename history
|
|
140
|
+
...NOW_SCRATCHPAD_STRIP_PREFIXES,
|
|
107
141
|
"<knowledge_base>",
|
|
108
142
|
"<pkb>", // backward-compat: strip legacy tag from pre-rename history
|
|
109
143
|
"<system_reminder>",
|
|
@@ -28,12 +28,6 @@ export const CES_SECURE_INSTALL_FLAG_KEY = "ces-secure-install" as const;
|
|
|
28
28
|
/** Gate for credential grant and audit inspection surfaces. */
|
|
29
29
|
export const CES_GRANT_AUDIT_FLAG_KEY = "ces-grant-audit" as const;
|
|
30
30
|
|
|
31
|
-
/** Gate for routing credential reads/writes through the CES process. */
|
|
32
|
-
const CES_CREDENTIAL_BACKEND_FLAG_KEY = "ces-credential-backend" as const;
|
|
33
|
-
|
|
34
|
-
/** Gate for managed sidecar transport in containerized environments. */
|
|
35
|
-
export const CES_MANAGED_SIDECAR_FLAG_KEY = "ces-managed-sidecar" as const;
|
|
36
|
-
|
|
37
31
|
// ---------------------------------------------------------------------------
|
|
38
32
|
// Public API — predicate functions
|
|
39
33
|
// ---------------------------------------------------------------------------
|
|
@@ -66,18 +60,3 @@ export function isCesGrantAuditEnabled(config: AssistantConfig): boolean {
|
|
|
66
60
|
return isAssistantFeatureFlagEnabled(CES_GRANT_AUDIT_FLAG_KEY, config);
|
|
67
61
|
}
|
|
68
62
|
|
|
69
|
-
/**
|
|
70
|
-
* Whether credential reads and writes should be routed through the CES process.
|
|
71
|
-
*/
|
|
72
|
-
export function isCesCredentialBackendEnabled(
|
|
73
|
-
config: AssistantConfig,
|
|
74
|
-
): boolean {
|
|
75
|
-
return isAssistantFeatureFlagEnabled(CES_CREDENTIAL_BACKEND_FLAG_KEY, config);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Whether managed sidecar transport should be used for CES communication.
|
|
80
|
-
*/
|
|
81
|
-
export function isCesManagedSidecarEnabled(config: AssistantConfig): boolean {
|
|
82
|
-
return isAssistantFeatureFlagEnabled(CES_MANAGED_SIDECAR_FLAG_KEY, config);
|
|
83
|
-
}
|
|
@@ -11,10 +11,8 @@ export async function awaitCesClientWithTimeout(
|
|
|
11
11
|
clientPromise: Promise<CesClient | undefined>,
|
|
12
12
|
options: AwaitCesClientWithTimeoutOptions = {},
|
|
13
13
|
): Promise<CesClient | undefined> {
|
|
14
|
-
const {
|
|
15
|
-
|
|
16
|
-
onTimeout = () => {},
|
|
17
|
-
} = options;
|
|
14
|
+
const { timeoutMs = DEFAULT_CES_STARTUP_TIMEOUT_MS, onTimeout = () => {} } =
|
|
15
|
+
options;
|
|
18
16
|
|
|
19
17
|
let timeoutId: ReturnType<typeof setTimeout> | undefined;
|
|
20
18
|
|
|
@@ -34,3 +32,33 @@ export async function awaitCesClientWithTimeout(
|
|
|
34
32
|
}
|
|
35
33
|
}
|
|
36
34
|
}
|
|
35
|
+
|
|
36
|
+
export interface CesClientResolver {
|
|
37
|
+
getCesClient: () => CesClient | undefined;
|
|
38
|
+
setCesClient: (client: CesClient) => void;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Inject the CES client into the credential resolver once the startup
|
|
43
|
+
* handshake resolves.
|
|
44
|
+
*
|
|
45
|
+
* {@link awaitCesClientWithTimeout} only gates provider initialization: when
|
|
46
|
+
* the handshake loses the race it returns before the client exists, so the
|
|
47
|
+
* caller cannot inject it synchronously. This bridges that gap by injecting
|
|
48
|
+
* the client whenever the handshake eventually completes. The resolver is left
|
|
49
|
+
* untouched if it already holds a client (e.g. one installed by a reconnection
|
|
50
|
+
* while the late handshake was still in flight) so the older startup client
|
|
51
|
+
* never clobbers a newer one, and a failed or aborted handshake is a no-op.
|
|
52
|
+
*/
|
|
53
|
+
export function injectCesClientWhenReady(
|
|
54
|
+
clientPromise: Promise<CesClient | undefined>,
|
|
55
|
+
resolver: CesClientResolver,
|
|
56
|
+
): void {
|
|
57
|
+
void clientPromise
|
|
58
|
+
.then((client) => {
|
|
59
|
+
if (client && resolver.getCesClient() === undefined) {
|
|
60
|
+
resolver.setCesClient(client);
|
|
61
|
+
}
|
|
62
|
+
})
|
|
63
|
+
.catch(() => undefined);
|
|
64
|
+
}
|