@vellumai/assistant 0.4.48 → 0.4.50
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/ARCHITECTURE.md +26 -35
- package/README.md +5 -26
- package/docs/architecture/integrations.md +45 -41
- package/docs/architecture/keychain-broker.md +3 -3
- package/docs/architecture/memory.md +180 -119
- package/docs/runbook-trusted-contacts.md +3 -8
- package/hook-templates/debug-prompt-logger/hook.json +1 -1
- package/hook-templates/debug-prompt-logger/run.sh +1 -3
- package/package.json +2 -2
- package/src/__tests__/actor-token-service.test.ts +0 -1
- package/src/__tests__/agent-loop.test.ts +3 -1
- package/src/__tests__/anthropic-provider.test.ts +249 -2
- package/src/__tests__/approval-cascade.test.ts +796 -0
- package/src/__tests__/approval-primitive.test.ts +0 -1
- package/src/__tests__/approval-routes-http.test.ts +4 -0
- package/src/__tests__/assistant-attachments.test.ts +12 -34
- package/src/__tests__/assistant-feature-flag-guard.test.ts +0 -23
- package/src/__tests__/assistant-feature-flag-guardrails.test.ts +76 -0
- package/src/__tests__/assistant-feature-flags-integration.test.ts +0 -1
- package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +2 -2
- package/src/__tests__/canonical-guardian-store.test.ts +95 -0
- package/src/__tests__/channel-guardian.test.ts +0 -2
- package/src/__tests__/channel-readiness-routes.test.ts +15 -6
- package/src/__tests__/channel-readiness-service.test.ts +10 -9
- package/src/__tests__/checker.test.ts +13 -20
- package/src/__tests__/computer-use-skill-manifest-regression.test.ts +1 -1
- package/src/__tests__/computer-use-tools.test.ts +2 -19
- package/src/__tests__/config-schema.test.ts +1 -68
- package/src/__tests__/config-watcher.test.ts +0 -1
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +0 -1
- package/src/__tests__/context-image-dimensions.test.ts +332 -0
- package/src/__tests__/context-memory-e2e.test.ts +11 -100
- package/src/__tests__/context-token-estimator.test.ts +196 -13
- package/src/__tests__/conversation-attention-store.test.ts +0 -1
- package/src/__tests__/conversation-attention-telegram.test.ts +0 -1
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +152 -0
- package/src/__tests__/conversation-routes-slash-commands.test.ts +2 -0
- package/src/__tests__/credential-metadata-store.test.ts +64 -73
- package/src/__tests__/credential-security-e2e.test.ts +1 -0
- package/src/__tests__/credential-security-invariants.test.ts +13 -7
- package/src/__tests__/credential-vault-unit.test.ts +284 -49
- package/src/__tests__/credential-vault.test.ts +150 -16
- package/src/__tests__/credentials-cli.test.ts +71 -0
- package/src/__tests__/cu-unified-flow.test.ts +532 -0
- package/src/__tests__/date-context.test.ts +93 -77
- package/src/__tests__/deterministic-verification-control-plane.test.ts +64 -0
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +0 -1
- package/src/__tests__/ephemeral-permissions.test.ts +3 -3
- package/src/__tests__/gateway-only-guard.test.ts +0 -1
- package/src/__tests__/guardian-action-grant-mint-consume.test.ts +0 -1
- package/src/__tests__/guardian-decision-primitive-canonical.test.ts +0 -1
- package/src/__tests__/guardian-routing-invariants.test.ts +93 -1
- package/src/__tests__/guardian-verification-voice-binding.test.ts +0 -1
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +0 -39
- package/src/__tests__/heartbeat-service.test.ts +0 -1
- package/src/__tests__/history-repair.test.ts +245 -0
- package/src/__tests__/host-cu-proxy.test.ts +791 -0
- package/src/__tests__/host-shell-tool.test.ts +27 -15
- package/src/__tests__/http-user-message-parity.test.ts +2 -0
- package/src/__tests__/ingress-url-consistency.test.ts +14 -21
- package/src/__tests__/integration-status.test.ts +32 -51
- package/src/__tests__/intent-routing.test.ts +0 -1
- package/src/__tests__/invite-redemption-service.test.ts +65 -1
- package/src/__tests__/invite-routes-http.test.ts +10 -9
- package/src/__tests__/keychain-broker-client.test.ts +14 -46
- package/src/__tests__/memory-context-benchmark.benchmark.test.ts +56 -18
- package/src/__tests__/memory-lifecycle-e2e.test.ts +244 -387
- package/src/__tests__/memory-recall-quality.test.ts +244 -407
- package/src/__tests__/memory-regressions.experimental.test.ts +126 -101
- package/src/__tests__/memory-regressions.test.ts +477 -2841
- package/src/__tests__/memory-retrieval.benchmark.test.ts +33 -150
- package/src/__tests__/memory-upsert-concurrency.test.ts +5 -244
- package/src/__tests__/mime-builder.test.ts +28 -0
- package/src/__tests__/native-web-search.test.ts +1 -0
- package/src/__tests__/notification-routing-intent.test.ts +0 -1
- package/src/__tests__/oauth-cli.test.ts +941 -15
- package/src/__tests__/oauth-provider-profiles.test.ts +9 -9
- package/src/__tests__/oauth-scope-policy.test.ts +4 -6
- package/src/__tests__/oauth-store.test.ts +870 -0
- package/src/__tests__/onboarding-starter-tasks.test.ts +0 -1
- package/src/__tests__/provider-error-scenarios.test.ts +0 -1
- package/src/__tests__/provider-streaming.benchmark.test.ts +0 -1
- package/src/__tests__/public-ingress-urls.test.ts +15 -21
- package/src/__tests__/qdrant-collection-migration.test.ts +53 -8
- package/src/__tests__/recording-handler.test.ts +3 -4
- package/src/__tests__/registry.test.ts +2 -3
- package/src/__tests__/relay-server.test.ts +46 -1
- package/src/__tests__/runtime-events-sse.test.ts +55 -7
- package/src/__tests__/schedule-store.test.ts +0 -1
- package/src/__tests__/schedule-tools.test.ts +32 -0
- package/src/__tests__/scheduler-recurrence.test.ts +0 -1
- package/src/__tests__/scoped-approval-grants.test.ts +0 -1
- package/src/__tests__/scoped-grant-security-matrix.test.ts +0 -1
- package/src/__tests__/script-proxy-certs.test.ts +1 -1
- package/src/__tests__/secret-ingress-handler.test.ts +0 -1
- package/src/__tests__/secret-onetime-send.test.ts +1 -0
- package/src/__tests__/secure-keys.test.ts +7 -2
- package/src/__tests__/send-endpoint-busy.test.ts +24 -6
- package/src/__tests__/sequence-store.test.ts +0 -1
- package/src/__tests__/session-abort-tool-results.test.ts +1 -14
- package/src/__tests__/session-agent-loop-overflow.test.ts +1583 -0
- package/src/__tests__/session-agent-loop.test.ts +19 -15
- package/src/__tests__/session-confirmation-signals.test.ts +1 -15
- package/src/__tests__/session-error.test.ts +124 -2
- package/src/__tests__/session-history-web-search.test.ts +918 -0
- package/src/__tests__/session-init.benchmark.test.ts +4 -5
- package/src/__tests__/session-pre-run-repair.test.ts +1 -14
- package/src/__tests__/session-provider-retry-repair.test.ts +25 -28
- package/src/__tests__/session-queue.test.ts +37 -27
- package/src/__tests__/session-runtime-assembly.test.ts +54 -0
- package/src/__tests__/session-slash-known.test.ts +1 -15
- package/src/__tests__/session-slash-queue.test.ts +1 -15
- package/src/__tests__/session-slash-unknown.test.ts +1 -15
- package/src/__tests__/session-workspace-cache-state.test.ts +3 -33
- package/src/__tests__/session-workspace-injection.test.ts +3 -37
- package/src/__tests__/session-workspace-tool-tracking.test.ts +3 -37
- package/src/__tests__/skill-include-graph.test.ts +66 -0
- package/src/__tests__/skill-load-feature-flag.test.ts +0 -1
- package/src/__tests__/skill-load-tool.test.ts +149 -1
- package/src/__tests__/skill-projection-feature-flag.test.ts +0 -1
- package/src/__tests__/skills-install-extract.test.ts +93 -0
- package/src/__tests__/skills-uninstall.test.ts +1 -1
- package/src/__tests__/skills.test.ts +3 -3
- package/src/__tests__/skillssh-registry.test.ts +451 -0
- package/src/__tests__/slack-channel-config.test.ts +67 -3
- package/src/__tests__/slack-share-routes.test.ts +17 -19
- package/src/__tests__/system-prompt.test.ts +0 -1
- package/src/__tests__/telegram-invite-adapter.test.ts +18 -22
- package/src/__tests__/terminal-tools.test.ts +4 -3
- package/src/__tests__/test-support/computer-use-skill-harness.ts +3 -2
- package/src/__tests__/tool-approval-handler.test.ts +0 -1
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +0 -1
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +0 -1
- package/src/__tests__/tool-executor-shell-integration.test.ts +0 -1
- package/src/__tests__/tool-executor.test.ts +0 -1
- package/src/__tests__/tool-grant-request-escalation.test.ts +0 -1
- package/src/__tests__/trust-store-pattern-matches.test.ts +29 -0
- package/src/__tests__/trust-store.test.ts +7 -13
- package/src/__tests__/trusted-contact-approval-notifier.test.ts +0 -1
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +0 -1
- package/src/__tests__/twilio-routes.test.ts +0 -16
- package/src/__tests__/verification-control-plane-policy.test.ts +0 -1
- package/src/__tests__/voice-invite-redemption.test.ts +32 -1
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +0 -1
- package/src/agent/ax-tree-compaction.test.ts +286 -0
- package/src/agent/loop.ts +104 -131
- package/src/approvals/AGENTS.md +1 -1
- package/src/approvals/guardian-request-resolvers.ts +14 -2
- package/src/bundler/compiler-tools.ts +66 -2
- package/src/calls/call-domain.ts +133 -6
- package/src/calls/call-store.ts +6 -0
- package/src/calls/relay-server.ts +52 -18
- package/src/calls/relay-setup-router.ts +17 -1
- package/src/calls/twilio-config.ts +3 -8
- package/src/calls/twilio-routes.ts +1 -2
- package/src/calls/types.ts +3 -1
- package/src/calls/voice-ingress-preflight.ts +1 -1
- package/src/cli/commands/browser-relay.ts +18 -12
- package/src/cli/commands/completions.ts +0 -3
- package/src/cli/commands/credentials.ts +101 -15
- package/src/cli/commands/doctor.ts +4 -3
- package/src/cli/commands/mcp.ts +46 -59
- package/src/cli/commands/memory.ts +16 -165
- package/src/cli/commands/oauth/apps.ts +284 -0
- package/src/cli/commands/oauth/connections.ts +633 -0
- package/src/cli/commands/oauth/index.ts +52 -0
- package/src/cli/commands/oauth/providers.ts +256 -0
- package/src/cli/commands/sessions.ts +5 -2
- package/src/cli/commands/skills.ts +177 -339
- package/src/cli/http-client.ts +0 -20
- package/src/cli/main-screen.tsx +2 -2
- package/src/cli/program.ts +6 -11
- package/src/cli/reference.ts +1 -3
- package/src/cli.ts +4 -10
- package/src/config/assistant-feature-flags.ts +0 -3
- package/src/config/bundled-skills/_shared/CLI_RETRIEVAL_PATTERN.md +1 -1
- package/src/config/bundled-skills/computer-use/SKILL.md +3 -6
- package/src/config/bundled-skills/computer-use/TOOLS.json +23 -5
- package/src/config/bundled-skills/computer-use/tools/{computer-use-request-control.ts → computer-use-observe.ts} +1 -5
- package/src/config/bundled-skills/google-calendar/calendar-client.ts +21 -16
- package/src/config/bundled-skills/messaging/tools/shared.ts +1 -4
- package/src/config/bundled-skills/settings/SKILL.md +1 -1
- package/src/config/bundled-skills/settings/TOOLS.json +2 -8
- package/src/config/bundled-skills/settings/tools/voice-config-update.ts +5 -33
- package/src/config/bundled-tool-registry.ts +2 -5
- package/src/config/env-registry.ts +14 -83
- package/src/config/env.ts +11 -50
- package/src/config/feature-flag-registry.json +16 -16
- package/src/config/loader.ts +0 -6
- package/src/config/schema.ts +4 -13
- package/src/config/schemas/memory-lifecycle.ts +0 -9
- package/src/config/schemas/memory-processing.ts +0 -180
- package/src/config/schemas/memory-retrieval.ts +32 -104
- package/src/config/schemas/memory.ts +0 -10
- package/src/config/skills.ts +21 -2
- package/src/config/types.ts +0 -4
- package/src/context/image-dimensions.ts +229 -0
- package/src/context/token-estimator.ts +75 -12
- package/src/context/window-manager.ts +53 -11
- package/src/daemon/assistant-attachments.ts +1 -13
- package/src/daemon/config-watcher.ts +61 -3
- package/src/daemon/daemon-control.ts +1 -1
- package/src/daemon/date-context.ts +114 -31
- package/src/daemon/handlers/config-ingress.ts +8 -33
- package/src/daemon/handlers/config-slack-channel.ts +49 -46
- package/src/daemon/handlers/config-telegram.ts +32 -16
- package/src/daemon/handlers/sessions.ts +27 -36
- package/src/daemon/handlers/shared.ts +0 -130
- package/src/daemon/handlers/skills.ts +20 -1
- package/src/daemon/history-repair.ts +72 -8
- package/src/daemon/host-cu-proxy.ts +430 -0
- package/src/daemon/lifecycle.ts +67 -71
- package/src/daemon/mcp-reload-service.ts +2 -2
- package/src/daemon/message-protocol.ts +3 -0
- package/src/daemon/message-types/computer-use.ts +1 -129
- package/src/daemon/message-types/host-cu.ts +19 -0
- package/src/daemon/message-types/memory.ts +4 -16
- package/src/daemon/message-types/messages.ts +4 -0
- package/src/daemon/message-types/sessions.ts +4 -0
- package/src/daemon/server.ts +25 -21
- package/src/daemon/session-agent-loop-handlers.ts +40 -0
- package/src/daemon/session-agent-loop.ts +334 -48
- package/src/daemon/session-attachments.ts +1 -2
- package/src/daemon/session-error.ts +89 -6
- package/src/daemon/session-history.ts +17 -7
- package/src/daemon/session-media-retry.ts +6 -2
- package/src/daemon/session-memory.ts +69 -149
- package/src/daemon/session-process.ts +10 -1
- package/src/daemon/session-runtime-assembly.ts +49 -19
- package/src/daemon/session-slash.ts +1 -1
- package/src/daemon/session-surfaces.ts +43 -28
- package/src/daemon/session-tool-setup.ts +9 -10
- package/src/daemon/session.ts +150 -17
- package/src/daemon/tool-side-effects.ts +2 -8
- package/src/daemon/watch-handler.ts +2 -2
- package/src/events/tool-metrics-listener.ts +2 -2
- package/src/hooks/manager.ts +1 -4
- package/src/inbound/public-ingress-urls.ts +7 -7
- package/src/instrument.ts +61 -1
- package/src/logfire.ts +16 -5
- package/src/memory/admin.ts +2 -191
- package/src/memory/canonical-guardian-store.ts +38 -2
- package/src/memory/conversation-crud.ts +0 -33
- package/src/memory/conversation-key-store.ts +21 -0
- package/src/memory/conversation-queries.ts +22 -3
- package/src/memory/db-init.ts +32 -0
- package/src/memory/embedding-backend.ts +84 -8
- package/src/memory/embedding-types.ts +9 -1
- package/src/memory/indexer.ts +7 -46
- package/src/memory/items-extractor.ts +274 -76
- package/src/memory/job-handlers/backfill.ts +2 -127
- package/src/memory/job-handlers/cleanup.ts +2 -16
- package/src/memory/job-handlers/extraction.ts +2 -138
- package/src/memory/job-handlers/index-maintenance.ts +1 -6
- package/src/memory/job-handlers/summarization.ts +3 -148
- package/src/memory/job-utils.ts +21 -59
- package/src/memory/jobs-store.ts +1 -159
- package/src/memory/jobs-worker.ts +9 -52
- package/src/memory/migrations/104-core-indexes.ts +3 -3
- package/src/memory/migrations/149-oauth-tables.ts +62 -0
- package/src/memory/migrations/150-oauth-apps-client-secret-path.ts +98 -0
- package/src/memory/migrations/151-oauth-providers-ping-url.ts +11 -0
- package/src/memory/migrations/152-memory-item-supersession.ts +44 -0
- package/src/memory/migrations/153-drop-entity-tables.ts +15 -0
- package/src/memory/migrations/154-drop-fts.ts +20 -0
- package/src/memory/migrations/155-drop-conflicts.ts +7 -0
- package/src/memory/migrations/156-call-session-invite-metadata.ts +24 -0
- package/src/memory/migrations/index.ts +8 -0
- package/src/memory/qdrant-client.ts +148 -51
- package/src/memory/raw-query.ts +1 -1
- package/src/memory/retriever.test.ts +294 -273
- package/src/memory/retriever.ts +421 -645
- package/src/memory/schema/calls.ts +2 -0
- package/src/memory/schema/index.ts +1 -0
- package/src/memory/schema/memory-core.ts +3 -48
- package/src/memory/schema/oauth.ts +67 -0
- package/src/memory/search/formatting.ts +263 -176
- package/src/memory/search/lexical.ts +1 -254
- package/src/memory/search/ranking.ts +0 -455
- package/src/memory/search/semantic.ts +100 -14
- package/src/memory/search/staleness.ts +47 -0
- package/src/memory/search/tier-classifier.ts +21 -0
- package/src/memory/search/types.ts +15 -77
- package/src/memory/task-memory-cleanup.ts +4 -6
- package/src/messaging/provider.ts +4 -4
- package/src/messaging/providers/gmail/client.ts +82 -2
- package/src/messaging/providers/gmail/mime-builder.ts +17 -7
- package/src/messaging/providers/gmail/people-client.ts +10 -10
- package/src/messaging/providers/telegram-bot/adapter.ts +17 -17
- package/src/messaging/providers/whatsapp/adapter.ts +11 -8
- package/src/messaging/registry.ts +2 -32
- package/src/notifications/copy-composer.ts +0 -5
- package/src/notifications/signal.ts +4 -5
- package/src/oauth/byo-connection.test.ts +133 -25
- package/src/oauth/byo-connection.ts +22 -6
- package/src/oauth/connect-orchestrator.ts +113 -57
- package/src/oauth/connect-types.ts +17 -23
- package/src/oauth/connection-resolver.ts +35 -11
- package/src/oauth/connection.ts +1 -1
- package/src/oauth/manual-token-connection.ts +104 -0
- package/src/oauth/oauth-store.ts +582 -0
- package/src/oauth/platform-connection.test.ts +29 -0
- package/src/oauth/platform-connection.ts +6 -5
- package/src/oauth/provider-behaviors.ts +124 -0
- package/src/oauth/scope-policy.ts +9 -2
- package/src/oauth/seed-providers.ts +167 -0
- package/src/oauth/token-persistence.ts +81 -77
- package/src/permissions/checker.ts +3 -3
- package/src/permissions/defaults.ts +1 -1
- package/src/permissions/prompter.ts +10 -1
- package/src/permissions/trust-store.ts +36 -1
- package/src/playbooks/playbook-compiler.ts +1 -1
- package/src/prompts/__tests__/build-cli-reference-section.test.ts +3 -1
- package/src/prompts/system-prompt.ts +46 -42
- package/src/providers/anthropic/client.ts +59 -20
- package/src/providers/retry.ts +1 -27
- package/src/providers/types.ts +7 -1
- package/src/runtime/AGENTS.md +9 -0
- package/src/runtime/auth/route-policy.ts +6 -6
- package/src/runtime/channel-reply-delivery.ts +0 -40
- package/src/runtime/gateway-client.ts +0 -7
- package/src/runtime/guardian-reply-router.ts +24 -22
- package/src/runtime/http-server.ts +10 -8
- package/src/runtime/http-types.ts +2 -2
- package/src/runtime/invite-redemption-service.ts +19 -1
- package/src/runtime/invite-service.ts +25 -0
- package/src/runtime/middleware/twilio-validation.ts +1 -11
- package/src/runtime/pending-interactions.ts +14 -12
- package/src/runtime/routes/brain-graph-routes.ts +10 -90
- package/src/runtime/routes/channel-delivery-routes.ts +0 -1
- package/src/runtime/routes/conversation-routes.ts +81 -19
- package/src/runtime/routes/events-routes.ts +21 -11
- package/src/runtime/routes/host-cu-routes.ts +97 -0
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +21 -12
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +12 -111
- package/src/runtime/routes/integrations/slack/share.ts +6 -7
- package/src/runtime/routes/log-export-routes.ts +126 -8
- package/src/runtime/routes/memory-item-routes.test.ts +754 -0
- package/src/runtime/routes/memory-item-routes.ts +503 -0
- package/src/runtime/routes/session-management-routes.ts +3 -3
- package/src/runtime/routes/settings-routes.ts +55 -48
- package/src/runtime/routes/surface-action-routes.ts +1 -1
- package/src/runtime/routes/trust-rules-routes.ts +14 -0
- package/src/runtime/routes/watch-routes.ts +128 -0
- package/src/runtime/routes/workspace-routes.ts +2 -1
- package/src/schedule/integration-status.ts +10 -9
- package/src/security/credential-key.ts +0 -156
- package/src/security/keychain-broker-client.ts +22 -10
- package/src/security/oauth2.ts +1 -1
- package/src/security/secure-keys.ts +25 -3
- package/src/security/token-manager.ts +137 -64
- package/src/skills/catalog-install.ts +414 -0
- package/src/skills/include-graph.ts +32 -0
- package/src/skills/skillssh-registry.ts +503 -0
- package/src/telegram/bot-username.ts +2 -3
- package/src/tools/assets/search.ts +5 -1
- package/src/tools/browser/network-recorder.ts +1 -1
- package/src/tools/browser/network-recording-types.ts +1 -1
- package/src/tools/computer-use/definitions.ts +36 -11
- package/src/tools/computer-use/registry.ts +5 -6
- package/src/tools/credentials/broker.ts +1 -2
- package/src/tools/credentials/metadata-store.ts +17 -121
- package/src/tools/credentials/vault.ts +92 -167
- package/src/tools/memory/definitions.ts +4 -13
- package/src/tools/memory/handlers.test.ts +83 -103
- package/src/tools/memory/handlers.ts +50 -85
- package/src/tools/registry.ts +2 -7
- package/src/tools/schedule/create.ts +8 -1
- package/src/tools/schedule/update.ts +8 -1
- package/src/tools/skills/load.ts +85 -3
- package/src/tools/watch/watch-state.ts +0 -12
- package/src/util/logger.ts +7 -41
- package/src/util/platform.ts +9 -28
- package/src/watcher/providers/google-calendar.ts +2 -1
- package/src/__tests__/clarification-resolver.test.ts +0 -193
- package/src/__tests__/computer-use-session-compaction.test.ts +0 -143
- package/src/__tests__/computer-use-session-lifecycle.test.ts +0 -322
- package/src/__tests__/computer-use-session-working-dir.test.ts +0 -166
- package/src/__tests__/computer-use-skill-baseline.test.ts +0 -78
- package/src/__tests__/computer-use-skill-endstate.test.ts +0 -105
- package/src/__tests__/computer-use-skill-lifecycle-cleanup.test.ts +0 -249
- package/src/__tests__/conflict-intent-tokenization.test.ts +0 -160
- package/src/__tests__/conflict-policy.test.ts +0 -269
- package/src/__tests__/conflict-store.test.ts +0 -372
- package/src/__tests__/contradiction-checker.test.ts +0 -361
- package/src/__tests__/entity-extractor.test.ts +0 -211
- package/src/__tests__/entity-search.test.ts +0 -1117
- package/src/__tests__/profile-compiler.test.ts +0 -392
- package/src/__tests__/ride-shotgun-handler.test.ts +0 -452
- package/src/__tests__/session-conflict-gate.test.ts +0 -1228
- package/src/__tests__/session-profile-injection.test.ts +0 -557
- package/src/cli/commands/dev.ts +0 -129
- package/src/cli/commands/map.ts +0 -391
- package/src/cli/commands/oauth.ts +0 -77
- package/src/config/bundled-skills/knowledge-graph/SKILL.md +0 -25
- package/src/config/bundled-skills/knowledge-graph/TOOLS.json +0 -66
- package/src/config/bundled-skills/knowledge-graph/tools/graph-query.ts +0 -211
- package/src/daemon/computer-use-session.ts +0 -1026
- package/src/daemon/ride-shotgun-handler.ts +0 -569
- package/src/daemon/session-conflict-gate.ts +0 -167
- package/src/daemon/session-dynamic-profile.ts +0 -77
- package/src/memory/clarification-resolver.ts +0 -417
- package/src/memory/conflict-intent.ts +0 -205
- package/src/memory/conflict-policy.ts +0 -127
- package/src/memory/conflict-store.ts +0 -410
- package/src/memory/contradiction-checker.ts +0 -508
- package/src/memory/entity-extractor.ts +0 -535
- package/src/memory/format-recall.ts +0 -47
- package/src/memory/fts-reconciler.ts +0 -165
- package/src/memory/job-handlers/conflict.ts +0 -200
- package/src/memory/profile-compiler.ts +0 -195
- package/src/memory/recall-cache.ts +0 -117
- package/src/memory/search/entity.ts +0 -535
- package/src/memory/search/query-expansion.test.ts +0 -70
- package/src/memory/search/query-expansion.ts +0 -118
- package/src/oauth/provider-base-urls.ts +0 -21
- package/src/oauth/provider-profiles.ts +0 -192
- package/src/prompts/computer-use-prompt.ts +0 -98
- package/src/runtime/routes/computer-use-routes.ts +0 -641
- package/src/runtime/routes/mcp-routes.ts +0 -20
- package/src/runtime/telegram-streaming-delivery.test.ts +0 -729
- package/src/runtime/telegram-streaming-delivery.ts +0 -393
- package/src/tools/computer-use/request-computer-control.ts +0 -56
|
@@ -0,0 +1,503 @@
|
|
|
1
|
+
import { execSync } from "node:child_process";
|
|
2
|
+
import { existsSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { dirname, join, resolve, sep } from "node:path";
|
|
5
|
+
|
|
6
|
+
import { getWorkspaceSkillsDir } from "../util/platform.js";
|
|
7
|
+
import { upsertSkillsIndex } from "./catalog-install.js";
|
|
8
|
+
|
|
9
|
+
// ─── Types ───────────────────────────────────────────────────────────────────
|
|
10
|
+
|
|
11
|
+
export interface SkillsShSearchResult {
|
|
12
|
+
id: string; // e.g. "vercel-labs/agent-skills/vercel-react-best-practices"
|
|
13
|
+
skillId: string; // e.g. "vercel-react-best-practices"
|
|
14
|
+
name: string;
|
|
15
|
+
installs: number;
|
|
16
|
+
source: string; // e.g. "vercel-labs/agent-skills"
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export type RiskLevel =
|
|
20
|
+
| "safe"
|
|
21
|
+
| "low"
|
|
22
|
+
| "medium"
|
|
23
|
+
| "high"
|
|
24
|
+
| "critical"
|
|
25
|
+
| "unknown";
|
|
26
|
+
|
|
27
|
+
export interface PartnerAudit {
|
|
28
|
+
risk: RiskLevel;
|
|
29
|
+
alerts?: number;
|
|
30
|
+
score?: number;
|
|
31
|
+
analyzedAt: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Map from audit provider name (e.g. "ath", "socket", "snyk") to audit data */
|
|
35
|
+
export type SkillAuditData = Record<string, PartnerAudit>;
|
|
36
|
+
|
|
37
|
+
/** Map from skill slug to per-provider audit data */
|
|
38
|
+
export type AuditResponse = Record<string, SkillAuditData>;
|
|
39
|
+
|
|
40
|
+
export interface ResolvedSkillSource {
|
|
41
|
+
owner: string;
|
|
42
|
+
repo: string;
|
|
43
|
+
skillSlug: string;
|
|
44
|
+
ref?: string;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Map of relative file paths to their string contents */
|
|
48
|
+
export type SkillFiles = Record<string, string>;
|
|
49
|
+
|
|
50
|
+
// ─── Display helpers ─────────────────────────────────────────────────────────
|
|
51
|
+
|
|
52
|
+
const RISK_DISPLAY: Record<RiskLevel, string> = {
|
|
53
|
+
safe: "PASS",
|
|
54
|
+
low: "PASS",
|
|
55
|
+
medium: "WARN",
|
|
56
|
+
high: "FAIL",
|
|
57
|
+
critical: "FAIL",
|
|
58
|
+
unknown: "?",
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const PROVIDER_DISPLAY: Record<string, string> = {
|
|
62
|
+
ath: "ATH",
|
|
63
|
+
socket: "Socket",
|
|
64
|
+
snyk: "Snyk",
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
export function riskToDisplay(risk: RiskLevel): string {
|
|
68
|
+
return RISK_DISPLAY[risk] ?? "?";
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function providerDisplayName(provider: string): string {
|
|
72
|
+
return PROVIDER_DISPLAY[provider] ?? provider;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function formatAuditBadges(auditData: SkillAuditData): string {
|
|
76
|
+
const providers = Object.keys(auditData);
|
|
77
|
+
if (providers.length === 0) return "Security: no audit data";
|
|
78
|
+
|
|
79
|
+
const badges = providers.map((provider) => {
|
|
80
|
+
const audit = auditData[provider]!;
|
|
81
|
+
const display = riskToDisplay(audit.risk);
|
|
82
|
+
const name = providerDisplayName(provider);
|
|
83
|
+
return `[${name}:${display}]`;
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
return `Security: ${badges.join(" ")}`;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// ─── API clients ─────────────────────────────────────────────────────────────
|
|
90
|
+
|
|
91
|
+
export async function searchSkillsRegistry(
|
|
92
|
+
query: string,
|
|
93
|
+
limit?: number,
|
|
94
|
+
): Promise<SkillsShSearchResult[]> {
|
|
95
|
+
const params = new URLSearchParams({ q: query });
|
|
96
|
+
if (limit != null) {
|
|
97
|
+
params.set("limit", String(limit));
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const url = `https://skills.sh/api/search?${params.toString()}`;
|
|
101
|
+
const response = await fetch(url, {
|
|
102
|
+
signal: AbortSignal.timeout(10_000),
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
if (!response.ok) {
|
|
106
|
+
throw new Error(
|
|
107
|
+
`skills.sh search failed: HTTP ${response.status} ${response.statusText}`,
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const data = (await response.json()) as { skills: SkillsShSearchResult[] };
|
|
112
|
+
return data.skills ?? [];
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export async function fetchSkillAudits(
|
|
116
|
+
source: string,
|
|
117
|
+
skillSlugs: string[],
|
|
118
|
+
): Promise<AuditResponse> {
|
|
119
|
+
if (skillSlugs.length === 0) return {};
|
|
120
|
+
|
|
121
|
+
const params = new URLSearchParams({
|
|
122
|
+
source,
|
|
123
|
+
skills: skillSlugs.join(","),
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
const url = `https://add-skill.vercel.sh/audit?${params.toString()}`;
|
|
127
|
+
const response = await fetch(url, {
|
|
128
|
+
signal: AbortSignal.timeout(10_000),
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
if (!response.ok) {
|
|
132
|
+
throw new Error(
|
|
133
|
+
`Audit fetch failed: HTTP ${response.status} ${response.statusText}`,
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return (await response.json()) as AuditResponse;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// ─── Source resolution ──────────────────────────────────────────────────────
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Parse a skill source string into owner, repo, and skill slug.
|
|
144
|
+
*
|
|
145
|
+
* Supported formats:
|
|
146
|
+
* - `owner/repo@skill-name`
|
|
147
|
+
* - `owner/repo/skill-name`
|
|
148
|
+
* - `https://github.com/owner/repo/tree/<branch>/skills/skill-name`
|
|
149
|
+
*/
|
|
150
|
+
export function resolveSkillSource(source: string): ResolvedSkillSource {
|
|
151
|
+
// Full GitHub URL — capture the branch for ref passthrough
|
|
152
|
+
// Branch capture uses non-greedy `.+?` to handle branch names with slashes (e.g. feature/new-flow)
|
|
153
|
+
const urlMatch = source.match(
|
|
154
|
+
/^https?:\/\/github\.com\/([^/]+)\/([^/]+)\/tree\/(.+?)\/skills\/([a-z0-9][a-z0-9._-]*)\/?$/,
|
|
155
|
+
);
|
|
156
|
+
if (urlMatch) {
|
|
157
|
+
return {
|
|
158
|
+
owner: urlMatch[1]!,
|
|
159
|
+
repo: urlMatch[2]!,
|
|
160
|
+
skillSlug: urlMatch[4]!,
|
|
161
|
+
ref: urlMatch[3]!,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// owner/repo@skill-name — restrict slug to safe characters
|
|
166
|
+
const atMatch = source.match(/^([^/]+)\/([^/@]+)@([a-z0-9][a-z0-9._-]*)$/);
|
|
167
|
+
if (atMatch) {
|
|
168
|
+
return { owner: atMatch[1]!, repo: atMatch[2]!, skillSlug: atMatch[3]! };
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// owner/repo/skill-name (exactly 3 segments) — restrict slug to safe characters
|
|
172
|
+
const slashMatch = source.match(/^([^/]+)\/([^/]+)\/([a-z0-9][a-z0-9._-]*)$/);
|
|
173
|
+
if (slashMatch) {
|
|
174
|
+
return {
|
|
175
|
+
owner: slashMatch[1]!,
|
|
176
|
+
repo: slashMatch[2]!,
|
|
177
|
+
skillSlug: slashMatch[3]!,
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
throw new Error(
|
|
182
|
+
`Invalid skill source "${source}". Expected one of:\n` +
|
|
183
|
+
` owner/repo@skill-name\n` +
|
|
184
|
+
` owner/repo/skill-name\n` +
|
|
185
|
+
` https://github.com/owner/repo/tree/<branch>/skills/skill-name`,
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// ─── GitHub fetch ───────────────────────────────────────────────────────────
|
|
190
|
+
|
|
191
|
+
interface GitHubContentsEntry {
|
|
192
|
+
name: string;
|
|
193
|
+
type: "file" | "dir";
|
|
194
|
+
download_url: string | null;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/** Build common headers for GitHub API requests (User-Agent + optional auth). */
|
|
198
|
+
function githubHeaders(): Record<string, string> {
|
|
199
|
+
const headers: Record<string, string> = {
|
|
200
|
+
Accept: "application/vnd.github.v3+json",
|
|
201
|
+
"User-Agent": "vellum-assistant",
|
|
202
|
+
};
|
|
203
|
+
const token = process.env.GITHUB_TOKEN;
|
|
204
|
+
if (token) {
|
|
205
|
+
headers["Authorization"] = `token ${token}`;
|
|
206
|
+
}
|
|
207
|
+
return headers;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
interface GitHubTreeEntry {
|
|
211
|
+
path: string;
|
|
212
|
+
type: "blob" | "tree";
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Search the repo tree for a directory containing `<slug>/SKILL.md`.
|
|
217
|
+
* Returns the directory path (e.g. "examples/skills-tool/skills/csv") or null.
|
|
218
|
+
*/
|
|
219
|
+
async function findSkillDirInTree(
|
|
220
|
+
owner: string,
|
|
221
|
+
repo: string,
|
|
222
|
+
skillSlug: string,
|
|
223
|
+
ref: string,
|
|
224
|
+
headers: Record<string, string>,
|
|
225
|
+
): Promise<string | null> {
|
|
226
|
+
const treeUrl = `https://api.github.com/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/git/trees/${encodeURIComponent(ref)}?recursive=1`;
|
|
227
|
+
const response = await fetch(treeUrl, {
|
|
228
|
+
headers,
|
|
229
|
+
signal: AbortSignal.timeout(15_000),
|
|
230
|
+
});
|
|
231
|
+
if (!response.ok) return null;
|
|
232
|
+
|
|
233
|
+
const data = (await response.json()) as { tree: GitHubTreeEntry[] };
|
|
234
|
+
const suffix = `${skillSlug}/SKILL.md`;
|
|
235
|
+
const match = data.tree.find(
|
|
236
|
+
(entry) =>
|
|
237
|
+
entry.type === "blob" &&
|
|
238
|
+
(entry.path === suffix || entry.path.endsWith(`/${suffix}`)),
|
|
239
|
+
);
|
|
240
|
+
if (!match) return null;
|
|
241
|
+
|
|
242
|
+
// Return the directory containing SKILL.md (strip the trailing /SKILL.md)
|
|
243
|
+
return match.path.slice(0, -"/SKILL.md".length);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Fetch SKILL.md and supporting files from a GitHub-hosted skills directory.
|
|
248
|
+
*
|
|
249
|
+
* First tries the conventional `skills/<slug>/` path. If that returns a 404,
|
|
250
|
+
* falls back to searching the full repo tree for `<slug>/SKILL.md` at any
|
|
251
|
+
* depth (handles repos like `vercel-labs/bash-tool` where skills live at
|
|
252
|
+
* non-standard paths like `examples/skills-tool/skills/csv/`).
|
|
253
|
+
*
|
|
254
|
+
* Uses the GitHub Contents API for directory listing and file downloads.
|
|
255
|
+
* Recursively fetches subdirectories (e.g. scripts/, references/).
|
|
256
|
+
*/
|
|
257
|
+
export async function fetchSkillFromGitHub(
|
|
258
|
+
owner: string,
|
|
259
|
+
repo: string,
|
|
260
|
+
skillSlug: string,
|
|
261
|
+
ref?: string,
|
|
262
|
+
): Promise<SkillFiles> {
|
|
263
|
+
const headers = githubHeaders();
|
|
264
|
+
|
|
265
|
+
async function fetchDir(
|
|
266
|
+
subpath: string,
|
|
267
|
+
prefix: string,
|
|
268
|
+
): Promise<SkillFiles> {
|
|
269
|
+
let apiUrl = `https://api.github.com/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/contents/${subpath}`;
|
|
270
|
+
if (ref) {
|
|
271
|
+
apiUrl += `?ref=${encodeURIComponent(ref)}`;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
const response = await fetch(apiUrl, {
|
|
275
|
+
headers,
|
|
276
|
+
signal: AbortSignal.timeout(15_000),
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
if (!response.ok) {
|
|
280
|
+
throw new Error(
|
|
281
|
+
`GitHub API error: HTTP ${response.status} ${response.statusText}`,
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
const entries = (await response.json()) as GitHubContentsEntry[];
|
|
286
|
+
if (!Array.isArray(entries)) {
|
|
287
|
+
throw new Error(
|
|
288
|
+
`Expected a directory listing for ${subpath}/ but got a single file`,
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
const files: SkillFiles = {};
|
|
293
|
+
for (const entry of entries) {
|
|
294
|
+
const relativePath = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
295
|
+
|
|
296
|
+
if (entry.type === "dir") {
|
|
297
|
+
// Recursively fetch subdirectory contents
|
|
298
|
+
const subFiles = await fetchDir(
|
|
299
|
+
`${subpath}/${entry.name}`,
|
|
300
|
+
relativePath,
|
|
301
|
+
);
|
|
302
|
+
Object.assign(files, subFiles);
|
|
303
|
+
continue;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
if (entry.type !== "file" || !entry.download_url) continue;
|
|
307
|
+
const fileResponse = await fetch(entry.download_url, {
|
|
308
|
+
headers,
|
|
309
|
+
signal: AbortSignal.timeout(10_000),
|
|
310
|
+
});
|
|
311
|
+
if (!fileResponse.ok) {
|
|
312
|
+
throw new Error(
|
|
313
|
+
`Failed to download ${relativePath}: HTTP ${fileResponse.status}`,
|
|
314
|
+
);
|
|
315
|
+
}
|
|
316
|
+
files[relativePath] = await fileResponse.text();
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
return files;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// Try the conventional skills/<slug>/ path first
|
|
323
|
+
const conventionalPath = `skills/${encodeURIComponent(skillSlug)}`;
|
|
324
|
+
let skillDirPath = conventionalPath;
|
|
325
|
+
|
|
326
|
+
const probeUrl = `https://api.github.com/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/contents/${conventionalPath}${ref ? `?ref=${encodeURIComponent(ref)}` : ""}`;
|
|
327
|
+
const probeResponse = await fetch(probeUrl, {
|
|
328
|
+
headers,
|
|
329
|
+
signal: AbortSignal.timeout(15_000),
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
if (probeResponse.status === 404) {
|
|
333
|
+
// Fall back to searching the repo tree for <slug>/SKILL.md at any path
|
|
334
|
+
const treeRef = ref ?? "HEAD";
|
|
335
|
+
const foundPath = await findSkillDirInTree(
|
|
336
|
+
owner,
|
|
337
|
+
repo,
|
|
338
|
+
skillSlug,
|
|
339
|
+
treeRef,
|
|
340
|
+
headers,
|
|
341
|
+
);
|
|
342
|
+
if (!foundPath) {
|
|
343
|
+
throw new Error(
|
|
344
|
+
`Skill "${skillSlug}" not found in ${owner}/${repo}. ` +
|
|
345
|
+
`Searched skills/${skillSlug}/ and the full repo tree.`,
|
|
346
|
+
);
|
|
347
|
+
}
|
|
348
|
+
skillDirPath = foundPath;
|
|
349
|
+
} else if (!probeResponse.ok) {
|
|
350
|
+
throw new Error(
|
|
351
|
+
`GitHub API error: HTTP ${probeResponse.status} ${probeResponse.statusText}`,
|
|
352
|
+
);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// If we already have the probe response for the conventional path and it was
|
|
356
|
+
// successful, we can use it directly instead of re-fetching.
|
|
357
|
+
let files: SkillFiles;
|
|
358
|
+
if (skillDirPath === conventionalPath && probeResponse.ok) {
|
|
359
|
+
const entries = (await probeResponse.json()) as GitHubContentsEntry[];
|
|
360
|
+
if (!Array.isArray(entries)) {
|
|
361
|
+
throw new Error(
|
|
362
|
+
`Expected a directory listing for ${conventionalPath}/ but got a single file`,
|
|
363
|
+
);
|
|
364
|
+
}
|
|
365
|
+
// Fetch the directory contents from the already-parsed probe response
|
|
366
|
+
const result: SkillFiles = {};
|
|
367
|
+
for (const entry of entries) {
|
|
368
|
+
if (entry.type === "dir") {
|
|
369
|
+
const subFiles = await fetchDir(
|
|
370
|
+
`${conventionalPath}/${entry.name}`,
|
|
371
|
+
entry.name,
|
|
372
|
+
);
|
|
373
|
+
Object.assign(result, subFiles);
|
|
374
|
+
continue;
|
|
375
|
+
}
|
|
376
|
+
if (entry.type !== "file" || !entry.download_url) continue;
|
|
377
|
+
const fileResponse = await fetch(entry.download_url, {
|
|
378
|
+
headers,
|
|
379
|
+
signal: AbortSignal.timeout(10_000),
|
|
380
|
+
});
|
|
381
|
+
if (!fileResponse.ok) {
|
|
382
|
+
throw new Error(
|
|
383
|
+
`Failed to download ${entry.name}: HTTP ${fileResponse.status}`,
|
|
384
|
+
);
|
|
385
|
+
}
|
|
386
|
+
result[entry.name] = await fileResponse.text();
|
|
387
|
+
}
|
|
388
|
+
files = result;
|
|
389
|
+
} else {
|
|
390
|
+
files = await fetchDir(skillDirPath, "");
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
if (!files["SKILL.md"]) {
|
|
394
|
+
throw new Error(`SKILL.md not found in ${owner}/${repo}/${skillDirPath}/`);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
return files;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// ─── External skill installation ────────────────────────────────────────────
|
|
401
|
+
|
|
402
|
+
// ─── Slug validation ────────────────────────────────────────────────────────
|
|
403
|
+
|
|
404
|
+
const VALID_SKILL_SLUG = /^[a-z0-9][a-z0-9._-]*$/;
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* Validate that a skill slug is safe for use in filesystem paths.
|
|
408
|
+
* Follows the same pattern as `validateManagedSkillId` in managed-store.ts.
|
|
409
|
+
*/
|
|
410
|
+
export function validateSkillSlug(slug: string): void {
|
|
411
|
+
if (!slug || typeof slug !== "string") {
|
|
412
|
+
throw new Error("Skill slug is required");
|
|
413
|
+
}
|
|
414
|
+
if (slug.includes("..") || slug.includes("/") || slug.includes("\\")) {
|
|
415
|
+
throw new Error(
|
|
416
|
+
`Invalid skill slug "${slug}": must not contain path traversal characters`,
|
|
417
|
+
);
|
|
418
|
+
}
|
|
419
|
+
if (!VALID_SKILL_SLUG.test(slug)) {
|
|
420
|
+
throw new Error(
|
|
421
|
+
`Invalid skill slug "${slug}": must start with a lowercase letter or digit and contain only lowercase letters, digits, dots, hyphens, and underscores`,
|
|
422
|
+
);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* Install a community skill from a GitHub-hosted skills.sh registry repo.
|
|
428
|
+
*
|
|
429
|
+
* 1. Validates the skill slug for path safety
|
|
430
|
+
* 2. Fetches all files from `skills/<skillSlug>/` in the source repo
|
|
431
|
+
* 3. Writes them to `<workspace>/skills/<skillSlug>/` with path traversal protection
|
|
432
|
+
* 4. Writes `version.json` with origin metadata
|
|
433
|
+
* 5. Runs `bun install` if a `package.json` is present
|
|
434
|
+
* 6. Registers the skill in SKILLS.md only after all steps succeed
|
|
435
|
+
*/
|
|
436
|
+
export async function installExternalSkill(
|
|
437
|
+
owner: string,
|
|
438
|
+
repo: string,
|
|
439
|
+
skillSlug: string,
|
|
440
|
+
overwrite: boolean,
|
|
441
|
+
ref?: string,
|
|
442
|
+
): Promise<void> {
|
|
443
|
+
// Validate slug before using in filesystem paths
|
|
444
|
+
validateSkillSlug(skillSlug);
|
|
445
|
+
|
|
446
|
+
const skillDir = join(getWorkspaceSkillsDir(), skillSlug);
|
|
447
|
+
const skillFilePath = join(skillDir, "SKILL.md");
|
|
448
|
+
|
|
449
|
+
if (existsSync(skillFilePath) && !overwrite) {
|
|
450
|
+
throw new Error(
|
|
451
|
+
`Skill "${skillSlug}" is already installed. Use --overwrite to replace it.`,
|
|
452
|
+
);
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
const files = await fetchSkillFromGitHub(owner, repo, skillSlug, ref);
|
|
456
|
+
|
|
457
|
+
// Clear existing directory on overwrite to remove stale files
|
|
458
|
+
if (overwrite && existsSync(skillDir)) {
|
|
459
|
+
rmSync(skillDir, { recursive: true, force: true });
|
|
460
|
+
}
|
|
461
|
+
mkdirSync(skillDir, { recursive: true });
|
|
462
|
+
|
|
463
|
+
// Write files with path traversal protection (follows extractTarToDir pattern)
|
|
464
|
+
for (const [filename, content] of Object.entries(files)) {
|
|
465
|
+
const normalized = filename.replace(/\\/g, "/").replace(/^\.\/+/g, "");
|
|
466
|
+
if (!normalized || normalized.includes("..") || normalized.startsWith("/"))
|
|
467
|
+
continue;
|
|
468
|
+
const destPath = resolve(skillDir, normalized);
|
|
469
|
+
if (
|
|
470
|
+
!destPath.startsWith(resolve(skillDir) + sep) &&
|
|
471
|
+
destPath !== resolve(skillDir)
|
|
472
|
+
)
|
|
473
|
+
continue;
|
|
474
|
+
mkdirSync(dirname(destPath), { recursive: true });
|
|
475
|
+
writeFileSync(destPath, content, "utf-8");
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
// Write origin metadata
|
|
479
|
+
const meta = {
|
|
480
|
+
origin: "skills.sh",
|
|
481
|
+
source: `${owner}/${repo}`,
|
|
482
|
+
skillSlug,
|
|
483
|
+
installedAt: new Date().toISOString(),
|
|
484
|
+
};
|
|
485
|
+
writeFileSync(
|
|
486
|
+
join(skillDir, "version.json"),
|
|
487
|
+
JSON.stringify(meta, null, 2) + "\n",
|
|
488
|
+
"utf-8",
|
|
489
|
+
);
|
|
490
|
+
|
|
491
|
+
// Install npm dependencies if the skill ships a package.json
|
|
492
|
+
if (existsSync(join(skillDir, "package.json"))) {
|
|
493
|
+
const bunPath = `${homedir()}/.bun/bin`;
|
|
494
|
+
execSync("bun install", {
|
|
495
|
+
cwd: skillDir,
|
|
496
|
+
stdio: "inherit",
|
|
497
|
+
env: { ...process.env, PATH: `${bunPath}:${process.env.PATH}` },
|
|
498
|
+
});
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
// Register in SKILLS.md only after files are written and deps installed
|
|
502
|
+
upsertSkillsIndex(skillSlug);
|
|
503
|
+
}
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import { getConfig } from "../config/loader.js";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Read the Telegram bot username from config
|
|
5
|
-
* TELEGRAM_BOT_USERNAME env var.
|
|
4
|
+
* Read the Telegram bot username from config.
|
|
6
5
|
*/
|
|
7
6
|
export function getTelegramBotUsername(): string | undefined {
|
|
8
7
|
const value = getConfig().telegram.botUsername;
|
|
9
8
|
if (value.trim().length > 0) {
|
|
10
9
|
return value.trim();
|
|
11
10
|
}
|
|
12
|
-
return
|
|
11
|
+
return undefined;
|
|
13
12
|
}
|
|
@@ -22,8 +22,12 @@ import {
|
|
|
22
22
|
messageAttachments,
|
|
23
23
|
messages,
|
|
24
24
|
} from "../../memory/schema.js";
|
|
25
|
-
import { escapeLikeWildcards } from "../../memory/search/lexical.js";
|
|
26
25
|
import { RiskLevel } from "../../permissions/types.js";
|
|
26
|
+
|
|
27
|
+
/** Escape SQL LIKE wildcard characters so a literal substring match is used. */
|
|
28
|
+
function escapeLikeWildcards(s: string): string {
|
|
29
|
+
return s.replace(/%/g, "").replace(/_/g, "");
|
|
30
|
+
}
|
|
27
31
|
import type { ToolDefinition } from "../../providers/types.js";
|
|
28
32
|
import { registerTool } from "../registry.js";
|
|
29
33
|
import type { Tool, ToolContext, ToolExecutionResult } from "../types.js";
|
|
@@ -34,7 +34,7 @@ const reasonProperty = {
|
|
|
34
34
|
export const computerUseClickTool: Tool = {
|
|
35
35
|
name: "computer_use_click",
|
|
36
36
|
description:
|
|
37
|
-
"Click
|
|
37
|
+
"Click an element on screen. Prefer element_id (from the accessibility tree) over x/y coordinates.",
|
|
38
38
|
category: "computer-use",
|
|
39
39
|
defaultRiskLevel: RiskLevel.Low,
|
|
40
40
|
executionMode: "proxy",
|
|
@@ -86,7 +86,7 @@ export const computerUseClickTool: Tool = {
|
|
|
86
86
|
export const computerUseTypeTextTool: Tool = {
|
|
87
87
|
name: "computer_use_type_text",
|
|
88
88
|
description:
|
|
89
|
-
"Type text at the current cursor position.
|
|
89
|
+
"Type text at the current cursor position. First click a text field (by element_id) to focus it, then call this tool. If a field shows 'FOCUSED', skip the click.",
|
|
90
90
|
category: "computer-use",
|
|
91
91
|
defaultRiskLevel: RiskLevel.Low,
|
|
92
92
|
executionMode: "proxy",
|
|
@@ -352,13 +352,7 @@ export const computerUseOpenAppTool: Tool = {
|
|
|
352
352
|
export const computerUseRunAppleScriptTool: Tool = {
|
|
353
353
|
name: "computer_use_run_applescript",
|
|
354
354
|
description:
|
|
355
|
-
"
|
|
356
|
-
"Use this for operations that are more reliable through scripting than UI interaction: " +
|
|
357
|
-
"setting a browser URL directly, navigating Finder to a path, querying app state " +
|
|
358
|
-
"(tab count, window titles, document status), or clicking deeply nested menu items. " +
|
|
359
|
-
"The script's return value (if any) will be reported back. " +
|
|
360
|
-
'NEVER use "do shell script" — it is blocked for security. ' +
|
|
361
|
-
"Keep scripts short and targeted to a single operation.",
|
|
355
|
+
"Run an AppleScript command. Prefer this over click/type when possible — it doesn't move the cursor or interrupt the user. Never use 'do shell script' inside AppleScript (blocked for security).",
|
|
362
356
|
category: "computer-use",
|
|
363
357
|
defaultRiskLevel: RiskLevel.Low,
|
|
364
358
|
executionMode: "proxy",
|
|
@@ -395,7 +389,8 @@ export const computerUseRunAppleScriptTool: Tool = {
|
|
|
395
389
|
|
|
396
390
|
export const computerUseDoneTool: Tool = {
|
|
397
391
|
name: "computer_use_done",
|
|
398
|
-
description:
|
|
392
|
+
description:
|
|
393
|
+
"Signal that the computer use task is complete. Provide a summary of what was accomplished. This ends the computer use session.",
|
|
399
394
|
category: "computer-use",
|
|
400
395
|
defaultRiskLevel: RiskLevel.Low,
|
|
401
396
|
executionMode: "proxy",
|
|
@@ -428,7 +423,7 @@ export const computerUseDoneTool: Tool = {
|
|
|
428
423
|
export const computerUseRespondTool: Tool = {
|
|
429
424
|
name: "computer_use_respond",
|
|
430
425
|
description:
|
|
431
|
-
"Respond
|
|
426
|
+
"Respond to the user with a text answer instead of performing computer actions. Use this when you can answer directly without interacting with the screen.",
|
|
432
427
|
category: "computer-use",
|
|
433
428
|
defaultRiskLevel: RiskLevel.Low,
|
|
434
429
|
executionMode: "proxy",
|
|
@@ -458,11 +453,41 @@ export const computerUseRespondTool: Tool = {
|
|
|
458
453
|
execute: proxyExecute,
|
|
459
454
|
};
|
|
460
455
|
|
|
456
|
+
// ---------------------------------------------------------------------------
|
|
457
|
+
// observe
|
|
458
|
+
// ---------------------------------------------------------------------------
|
|
459
|
+
|
|
460
|
+
export const computerUseObserveTool: Tool = {
|
|
461
|
+
name: "computer_use_observe",
|
|
462
|
+
description:
|
|
463
|
+
"Capture the current screen state. Returns the accessibility tree with [ID] element references and optionally a screenshot.\n\nThe accessibility tree shows interactive elements like [3] AXButton 'Save' or [17] AXTextField 'Search'. Use element_id to target these elements in subsequent actions — this is much more reliable than pixel coordinates.\n\nCall this before your first computer use action, or to check screen state without acting.",
|
|
464
|
+
category: "computer-use",
|
|
465
|
+
defaultRiskLevel: RiskLevel.Low,
|
|
466
|
+
executionMode: "proxy",
|
|
467
|
+
|
|
468
|
+
getDefinition(): ToolDefinition {
|
|
469
|
+
return {
|
|
470
|
+
name: this.name,
|
|
471
|
+
description: this.description,
|
|
472
|
+
input_schema: {
|
|
473
|
+
type: "object",
|
|
474
|
+
properties: {
|
|
475
|
+
reason: reasonProperty,
|
|
476
|
+
},
|
|
477
|
+
required: ["reason"],
|
|
478
|
+
},
|
|
479
|
+
};
|
|
480
|
+
},
|
|
481
|
+
|
|
482
|
+
execute: proxyExecute,
|
|
483
|
+
};
|
|
484
|
+
|
|
461
485
|
// ---------------------------------------------------------------------------
|
|
462
486
|
// All tools exported as array for convenience
|
|
463
487
|
// ---------------------------------------------------------------------------
|
|
464
488
|
|
|
465
489
|
export const allComputerUseTools: Tool[] = [
|
|
490
|
+
computerUseObserveTool,
|
|
466
491
|
computerUseClickTool,
|
|
467
492
|
computerUseTypeTextTool,
|
|
468
493
|
computerUseKeyTool,
|
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Registers computer-use tools with the daemon's tool registry.
|
|
3
3
|
*
|
|
4
|
-
* The
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* normal startup.
|
|
4
|
+
* The computer_use_* action tools are now provided by the bundled
|
|
5
|
+
* computer-use skill. This module retains registerComputerUseActionTools()
|
|
6
|
+
* for backward compatibility (used by tests), but it is no longer called
|
|
7
|
+
* during normal startup.
|
|
9
8
|
*/
|
|
10
9
|
|
|
11
10
|
import { registerTool } from "../registry.js";
|
|
12
11
|
import { allComputerUseTools } from "./definitions.js";
|
|
13
12
|
|
|
14
13
|
/**
|
|
15
|
-
* Register the
|
|
14
|
+
* Register the 11 `computer_use_*` action proxy tools.
|
|
16
15
|
* After cutover these are provided by the bundled computer-use skill instead.
|
|
17
16
|
*/
|
|
18
17
|
export function registerComputerUseActionTools(): void {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { v4 as uuid } from "uuid";
|
|
2
2
|
|
|
3
|
-
import { credentialKey
|
|
3
|
+
import { credentialKey } from "../../security/credential-key.js";
|
|
4
4
|
import { getSecureKey } from "../../security/secure-keys.js";
|
|
5
5
|
import { getLogger } from "../../util/logger.js";
|
|
6
6
|
import type {
|
|
@@ -60,7 +60,6 @@ export class CredentialBroker {
|
|
|
60
60
|
* Returns a single-use token on success, or a denial reason on failure.
|
|
61
61
|
*/
|
|
62
62
|
authorize(request: AuthorizeRequest): AuthorizeResult {
|
|
63
|
-
migrateKeys();
|
|
64
63
|
const metadata = getCredentialMetadata(request.service, request.field);
|
|
65
64
|
if (!metadata) {
|
|
66
65
|
return {
|