@vellumai/assistant 0.5.0 → 0.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +54 -54
- package/docs/architecture/integrations.md +62 -67
- package/docs/credential-execution-service.md +3 -3
- package/package.json +1 -1
- package/src/__tests__/agent-loop.test.ts +111 -0
- package/src/__tests__/always-loaded-tools-guard.test.ts +3 -4
- package/src/__tests__/app-builder-tool-scripts.test.ts +13 -151
- package/src/__tests__/app-dir-path-guard.test.ts +78 -0
- package/src/__tests__/app-executors.test.ts +1 -291
- package/src/__tests__/app-git-history.test.ts +4 -4
- package/src/__tests__/app-routes-csp.test.ts +1 -0
- package/src/__tests__/app-store-dir-names.test.ts +426 -0
- package/src/__tests__/assistant-feature-flags-integration.test.ts +7 -9
- package/src/__tests__/attachments-store.test.ts +169 -21
- package/src/__tests__/attachments.test.ts +115 -1
- package/src/__tests__/btw-routes.test.ts +1 -0
- package/src/__tests__/canonical-guardian-store.test.ts +38 -0
- package/src/__tests__/channel-reply-delivery.test.ts +55 -0
- package/src/__tests__/checker.test.ts +54 -0
- package/src/__tests__/claude-code-skill-regression.test.ts +2 -0
- package/src/__tests__/claude-code-tool-profiles.test.ts +2 -0
- package/src/__tests__/compaction.benchmark.test.ts +2 -1
- package/src/__tests__/config-schema-cmd.test.ts +68 -21
- package/src/__tests__/config-schema.test.ts +1 -1
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +149 -5
- package/src/__tests__/conversation-agent-loop.test.ts +290 -2
- package/src/__tests__/conversation-attachments.test.ts +17 -19
- package/src/__tests__/conversation-disk-view-integration.test.ts +277 -0
- package/src/__tests__/conversation-disk-view.test.ts +810 -0
- package/src/__tests__/conversation-error.test.ts +1 -1
- package/src/__tests__/conversation-fork-crud.test.ts +551 -0
- package/src/__tests__/conversation-fork-route.test.ts +386 -0
- package/src/__tests__/conversation-history-web-search.test.ts +1 -1
- package/src/__tests__/conversation-key-store-disk-view.test.ts +130 -0
- package/src/__tests__/conversation-media-retry.test.ts +8 -2
- package/src/__tests__/conversation-queue.test.ts +36 -1
- package/src/__tests__/conversation-routes-disk-view.test.ts +439 -0
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +2 -2
- package/src/__tests__/conversation-routes-slash-commands.test.ts +2 -7
- package/src/__tests__/conversation-runtime-assembly.test.ts +17 -2
- package/src/__tests__/conversation-skill-tools.test.ts +4 -9
- package/src/__tests__/conversation-slash-commands.test.ts +149 -0
- package/src/__tests__/conversation-store.test.ts +24 -21
- package/src/__tests__/conversation-surfaces-state-update.test.ts +246 -0
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +1 -0
- package/src/__tests__/conversation-title-service.test.ts +137 -0
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +25 -315
- package/src/__tests__/conversation-tool-setup-memory-scope.test.ts +1 -0
- package/src/__tests__/conversation-tool-setup-side-effect-flag.test.ts +1 -0
- package/src/__tests__/conversation-workspace-cache-state.test.ts +44 -2
- package/src/__tests__/conversation-workspace-injection.test.ts +11 -0
- package/src/__tests__/credential-execution-feature-gates.test.ts +3 -3
- package/src/__tests__/credential-security-invariants.test.ts +3 -0
- package/src/__tests__/credential-vault-unit.test.ts +5 -10
- package/src/__tests__/cu-unified-flow.test.ts +1 -0
- package/src/__tests__/db-conversation-fork-lineage-migration.test.ts +241 -0
- package/src/__tests__/db-llm-request-log-provider-migration.test.ts +214 -0
- package/src/__tests__/diagnostics-export.test.ts +70 -1
- package/src/__tests__/filesystem-tools.test.ts +4 -2
- package/src/__tests__/first-greeting.test.ts +80 -0
- package/src/__tests__/gateway-only-guard.test.ts +1 -0
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +3 -7
- package/src/__tests__/history-repair.test.ts +103 -10
- package/src/__tests__/http-conversation-lineage.test.ts +251 -0
- package/src/__tests__/image-source-path-reinject.test.ts +136 -0
- package/src/__tests__/llm-context-normalization.test.ts +1116 -0
- package/src/__tests__/llm-context-route-provider.test.ts +217 -0
- package/src/__tests__/llm-request-log-turn-query.test.ts +270 -0
- package/src/__tests__/media-generate-image.test.ts +47 -94
- package/src/__tests__/memory-lifecycle-e2e.test.ts +3 -1
- package/src/__tests__/memory-recall-quality.test.ts +5 -5
- package/src/__tests__/migration-cross-version-compatibility.test.ts +4 -1
- package/src/__tests__/migration-export-http.test.ts +3 -1
- package/src/__tests__/migration-import-commit-http.test.ts +18 -4
- package/src/__tests__/migration-import-preflight-http.test.ts +1 -3
- package/src/__tests__/mime-builder.test.ts +3 -2
- package/src/__tests__/non-member-access-request.test.ts +12 -1
- package/src/__tests__/notification-decision-identity.test.ts +52 -0
- package/src/__tests__/oauth-apps-routes.test.ts +103 -0
- package/src/__tests__/oauth-store.test.ts +115 -0
- package/src/__tests__/provider-error-scenarios.test.ts +1 -3
- package/src/__tests__/provider-failover-actual-provider.test.ts +66 -0
- package/src/__tests__/recording-handler.test.ts +17 -0
- package/src/__tests__/registry.test.ts +3 -8
- package/src/__tests__/relay-server.test.ts +1 -1
- package/src/__tests__/runtime-attachment-metadata.test.ts +7 -3
- package/src/__tests__/schema-transforms.test.ts +165 -5
- package/src/__tests__/server-history-render.test.ts +2 -2
- package/src/__tests__/skill-feature-flags-integration.test.ts +18 -17
- package/src/__tests__/skill-feature-flags.test.ts +13 -13
- package/src/__tests__/skill-load-feature-flag.test.ts +4 -4
- package/src/__tests__/slack-app-setup-skill-regression.test.ts +3 -1
- package/src/__tests__/slack-inbound-verification.test.ts +2 -2
- package/src/__tests__/starter-task-flow.test.ts +1 -0
- package/src/__tests__/suggestion-routes.test.ts +443 -0
- package/src/__tests__/swarm-conversation-integration.test.ts +1 -0
- package/src/__tests__/swarm-recursion.test.ts +1 -0
- package/src/__tests__/swarm-tool.test.ts +1 -0
- package/src/__tests__/system-prompt.test.ts +8 -0
- package/src/__tests__/tool-execution-abort-cleanup.test.ts +1 -0
- package/src/__tests__/tool-preview-lifecycle.test.ts +32 -5
- package/src/__tests__/top-level-renderer.test.ts +22 -0
- package/src/__tests__/turn-boundary-resolution.test.ts +243 -0
- package/src/__tests__/web-fetch.test.ts +6 -2
- package/src/__tests__/workspace-migration-006-services-config.test.ts +335 -0
- package/src/__tests__/workspace-migration-007-web-search-provider-rename.test.ts +312 -0
- package/src/__tests__/workspace-migration-009-backfill-conversation-disk-view.test.ts +278 -0
- package/src/__tests__/workspace-migration-010-app-dir-rename.test.ts +275 -0
- package/src/__tests__/workspace-migration-012-rename-conversation-disk-view-dirs.test.ts +77 -0
- package/src/__tests__/workspace-migration-013-repair-conversation-disk-view.test.ts +401 -0
- package/src/__tests__/workspace-migration-backfill-installation-id.test.ts +328 -0
- package/src/__tests__/workspace-migration-seed-device-id.test.ts +6 -10
- package/src/agent/attachments.ts +27 -1
- package/src/agent/loop.ts +29 -1
- package/src/avatar/traits-png-sync.ts +80 -25
- package/src/bundler/app-bundler.ts +4 -4
- package/src/calls/call-domain.ts +1 -0
- package/src/calls/voice-session-bridge.ts +1 -0
- package/src/cli/commands/auth.ts +92 -0
- package/src/cli/commands/avatar.ts +7 -6
- package/src/cli/commands/config.ts +2 -0
- package/src/cli/commands/oauth/providers.ts +29 -0
- package/src/cli/program.ts +12 -0
- package/src/cli.ts +15 -48
- package/src/config/bundled-skills/app-builder/SKILL.md +103 -28
- package/src/config/bundled-skills/app-builder/TOOLS.json +5 -199
- package/src/config/bundled-skills/app-builder/tools/{app-query.ts → app-refresh.ts} +2 -2
- package/src/config/bundled-skills/contacts/tools/google-contacts.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +6 -9
- package/src/config/bundled-skills/gmail/tools/gmail-attachments.ts +4 -6
- package/src/config/bundled-skills/gmail/tools/gmail-draft.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-filters.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-follow-up.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-forward.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-label.ts +4 -6
- package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-send-draft.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-trash.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-vacation.ts +2 -3
- package/src/config/bundled-skills/google-calendar/tools/shared.ts +1 -1
- package/src/config/bundled-skills/image-studio/SKILL.md +2 -2
- package/src/config/bundled-skills/image-studio/TOOLS.json +2 -2
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +45 -72
- package/src/config/bundled-skills/media-processing/tools/extract-keyframes.ts +2 -2
- package/src/config/bundled-skills/messaging/tools/shared.ts +1 -1
- package/src/config/bundled-skills/settings/tools/voice-config-update.ts +19 -3
- package/src/config/bundled-skills/skill-management/TOOLS.json +2 -2
- package/src/config/bundled-skills/slack/tools/shared.ts +19 -4
- package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +2 -3
- package/src/config/bundled-skills/transcribe/SKILL.md +1 -1
- package/src/config/bundled-skills/transcribe/TOOLS.json +2 -6
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +19 -83
- package/src/config/bundled-tool-registry.ts +2 -14
- package/src/config/feature-flag-registry.json +16 -0
- package/src/config/loader.ts +64 -0
- package/src/config/raw-config-utils.ts +30 -0
- package/src/config/schema-utils.ts +28 -7
- package/src/config/schema.ts +8 -0
- package/src/config/schemas/elevenlabs.ts +18 -0
- package/src/config/schemas/memory-lifecycle.ts +4 -2
- package/src/config/schemas/memory-storage.ts +1 -1
- package/src/config/schemas/services.ts +8 -6
- package/src/contacts/contact-store.ts +13 -6
- package/src/contacts/contacts-write.ts +0 -1
- package/src/context/window-manager.ts +13 -2
- package/src/daemon/conversation-agent-loop-handlers.ts +46 -42
- package/src/daemon/conversation-agent-loop.ts +56 -19
- package/src/daemon/conversation-attachments.ts +18 -36
- package/src/daemon/conversation-error.ts +2 -1
- package/src/daemon/conversation-history.ts +18 -4
- package/src/daemon/conversation-lifecycle.ts +39 -15
- package/src/daemon/conversation-messaging.ts +70 -26
- package/src/daemon/conversation-process.ts +58 -34
- package/src/daemon/conversation-runtime-assembly.ts +21 -38
- package/src/daemon/conversation-slash.ts +121 -256
- package/src/daemon/conversation-surfaces.ts +143 -20
- package/src/daemon/conversation-tool-setup.ts +0 -6
- package/src/daemon/conversation-workspace.ts +21 -1
- package/src/daemon/conversation.ts +51 -29
- package/src/daemon/first-greeting.ts +35 -0
- package/src/daemon/handlers/config-embeddings.ts +148 -0
- package/src/daemon/handlers/config-model.ts +71 -26
- package/src/daemon/handlers/conversations.ts +0 -23
- package/src/daemon/handlers/recording.ts +26 -21
- package/src/daemon/history-repair.ts +28 -8
- package/src/daemon/host-cu-proxy.ts +2 -2
- package/src/daemon/lifecycle.ts +106 -64
- package/src/daemon/message-protocol.ts +3 -0
- package/src/daemon/message-types/conversations.ts +19 -0
- package/src/daemon/message-types/messages.ts +1 -0
- package/src/daemon/message-types/shared.ts +2 -0
- package/src/daemon/message-types/surfaces.ts +2 -0
- package/src/daemon/message-types/upgrades.ts +23 -0
- package/src/daemon/server.ts +83 -12
- package/src/daemon/shutdown-handlers.ts +8 -5
- package/src/daemon/startup-error.ts +9 -0
- package/src/daemon/tool-side-effects.ts +11 -28
- package/src/events/tool-permission-telemetry-listener.ts +1 -3
- package/src/instrument.ts +0 -4
- package/src/media/app-icon-generator.ts +2 -2
- package/src/memory/app-git-service.ts +28 -16
- package/src/memory/app-store.ts +230 -41
- package/src/memory/attachments-store.ts +558 -130
- package/src/memory/conversation-attention-store.ts +70 -0
- package/src/memory/conversation-crud.ts +442 -3
- package/src/memory/conversation-directories.ts +125 -0
- package/src/memory/conversation-disk-view.ts +390 -0
- package/src/memory/conversation-key-store.ts +17 -5
- package/src/memory/conversation-queries.ts +5 -1
- package/src/memory/conversation-title-service.ts +21 -49
- package/src/memory/db-init.ts +28 -0
- package/src/memory/embedding-backend.ts +42 -53
- package/src/memory/embedding-gemini.test.ts +4 -4
- package/src/memory/embedding-local.ts +1 -3
- package/src/memory/embedding-ollama.ts +1 -3
- package/src/memory/embedding-openai.ts +1 -3
- package/src/memory/indexer.ts +9 -7
- package/src/memory/items-extractor.ts +42 -13
- package/src/memory/job-handlers/conversation-starters.ts +6 -1
- package/src/memory/job-handlers/embedding.test.ts +1 -4
- package/src/memory/llm-request-log-store.ts +100 -1
- package/src/memory/migrations/102-alter-table-columns.ts +5 -0
- package/src/memory/migrations/146-schedule-oneshot-routing.ts +3 -3
- package/src/memory/migrations/147-migrate-reminders-to-schedules.ts +66 -70
- package/src/memory/migrations/148-drop-reminders-table.ts +5 -9
- package/src/memory/migrations/160-drop-loopback-port-column.ts +1 -3
- package/src/memory/migrations/174-rename-thread-starters-table.ts +0 -7
- package/src/memory/migrations/178-oauth-providers-managed-service-config-key.ts +15 -0
- package/src/memory/migrations/179-llm-request-log-message-id.ts +16 -0
- package/src/memory/migrations/180-backfill-inline-attachments-to-disk.ts +66 -0
- package/src/memory/migrations/181-rename-thread-starters-checkpoints.ts +46 -0
- package/src/memory/migrations/182-oauth-providers-display-metadata.ts +20 -0
- package/src/memory/migrations/183-add-conversation-fork-lineage.ts +22 -0
- package/src/memory/migrations/184-llm-request-log-provider.ts +12 -0
- package/src/memory/migrations/index.ts +7 -0
- package/src/memory/migrations/registry.ts +13 -0
- package/src/memory/retriever.test.ts +601 -2
- package/src/memory/retriever.ts +85 -9
- package/src/memory/schema/conversations.ts +6 -0
- package/src/memory/schema/infrastructure.ts +13 -7
- package/src/memory/schema/oauth.ts +6 -0
- package/src/messaging/providers/gmail/mime-builder.ts +3 -1
- package/src/notifications/copy-composer.ts +26 -0
- package/src/notifications/decision-engine.ts +14 -1
- package/src/notifications/emit-signal.ts +1 -1
- package/src/notifications/signal.ts +36 -0
- package/src/oauth/byo-connection.test.ts +1 -45
- package/src/oauth/byo-connection.ts +2 -8
- package/src/oauth/connect-orchestrator.ts +15 -11
- package/src/oauth/connection-resolver.test.ts +191 -0
- package/src/oauth/connection-resolver.ts +66 -38
- package/src/oauth/connection.ts +0 -1
- package/src/oauth/oauth-store.ts +97 -47
- package/src/oauth/platform-connection.test.ts +0 -1
- package/src/oauth/platform-connection.ts +11 -3
- package/src/oauth/seed-providers.ts +78 -3
- package/src/oauth/token-persistence.ts +16 -10
- package/src/permissions/checker.ts +62 -19
- package/src/prompts/system-prompt.ts +2 -0
- package/src/prompts/templates/BOOTSTRAP.md +2 -0
- package/src/providers/anthropic/client.ts +8 -1
- package/src/providers/failover.ts +4 -1
- package/src/providers/gemini/client.ts +50 -0
- package/src/providers/model-catalog.ts +92 -0
- package/src/providers/model-intents.ts +29 -20
- package/src/providers/openai/client.ts +49 -0
- package/src/providers/types.ts +2 -0
- package/src/runtime/access-request-helper.ts +16 -7
- package/src/runtime/auth/credential-service.ts +3 -1
- package/src/runtime/auth/route-policy.ts +14 -1
- package/src/runtime/btw-sidechain.ts +101 -0
- package/src/runtime/channel-reply-delivery.ts +17 -1
- package/src/runtime/http-router.ts +3 -1
- package/src/runtime/http-server.ts +196 -141
- package/src/runtime/http-types.ts +1 -0
- package/src/runtime/migrations/vbundle-builder.ts +5 -1
- package/src/runtime/routes/access-request-decision.ts +41 -0
- package/src/runtime/routes/app-management-routes.ts +6 -3
- package/src/runtime/routes/app-routes.ts +7 -3
- package/src/runtime/routes/approval-routes.ts +1 -0
- package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +34 -2
- package/src/runtime/routes/attachment-routes.ts +45 -15
- package/src/runtime/routes/btw-routes.ts +21 -61
- package/src/runtime/routes/conversation-management-routes.ts +68 -0
- package/src/runtime/routes/conversation-query-routes.ts +180 -10
- package/src/runtime/routes/conversation-routes.ts +222 -28
- package/src/runtime/routes/conversation-starter-routes.ts +9 -11
- package/src/runtime/routes/diagnostics-routes.ts +1 -0
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +2 -2
- package/src/runtime/routes/llm-context-normalization.ts +1199 -0
- package/src/runtime/routes/log-export-routes.ts +3 -0
- package/src/runtime/routes/memory-item-routes.test.ts +34 -0
- package/src/runtime/routes/memory-item-routes.ts +4 -0
- package/src/runtime/routes/migration-routes.ts +4 -1
- package/src/runtime/routes/oauth-apps.ts +291 -0
- package/src/runtime/routes/secret-routes.ts +28 -1
- package/src/runtime/routes/settings-routes.ts +14 -0
- package/src/runtime/routes/trace-event-routes.ts +4 -1
- package/src/schedule/schedule-store.ts +9 -21
- package/src/security/secure-keys.ts +21 -0
- package/src/signals/bash.ts +1 -1
- package/src/swarm/backend-claude-code.ts +3 -6
- package/src/telemetry/usage-telemetry-reporter.test.ts +3 -2
- package/src/telemetry/usage-telemetry-reporter.ts +3 -1
- package/src/tools/AGENTS.md +6 -10
- package/src/tools/apps/executors.ts +17 -232
- package/src/tools/claude-code/claude-code.ts +2 -3
- package/src/tools/credentials/vault.ts +7 -12
- package/src/tools/host-filesystem/read.ts +13 -10
- package/src/tools/network/__tests__/web-search.test.ts +4 -2
- package/src/tools/schedule/list.ts +2 -7
- package/src/tools/schema-transforms.ts +5 -0
- package/src/tools/shared/filesystem/format-diff.ts +4 -21
- package/src/tools/skills/execute.ts +1 -1
- package/src/tools/tool-manifest.ts +0 -6
- package/src/tools/ui-surface/definitions.ts +2 -2
- package/src/util/device-id.ts +28 -5
- package/src/util/platform.ts +6 -0
- package/src/util/pricing.ts +1 -0
- package/src/util/retry.ts +1 -3
- package/src/workspace/migrations/002-backfill-installation-id.ts +23 -12
- package/src/workspace/migrations/003-seed-device-id.ts +3 -4
- package/src/workspace/migrations/006-services-config.ts +5 -0
- package/src/workspace/migrations/008-voice-timeout-and-max-steps.ts +12 -0
- package/src/workspace/migrations/009-backfill-conversation-disk-view.ts +10 -0
- package/src/workspace/migrations/010-app-dir-rename.ts +223 -0
- package/src/workspace/migrations/012-rename-conversation-disk-view-dirs.ts +64 -0
- package/src/workspace/migrations/013-repair-conversation-disk-view.ts +11 -0
- package/src/workspace/migrations/rebuild-conversation-disk-view.ts +186 -0
- package/src/workspace/migrations/registry.ts +10 -0
- package/src/workspace/top-level-renderer.ts +12 -0
- package/src/__tests__/asset-materialize-tool.test.ts +0 -523
- package/src/__tests__/asset-search-tool.test.ts +0 -536
- package/src/__tests__/fixtures/media-reuse-fixtures.ts +0 -56
- package/src/__tests__/media-reuse-story.e2e.test.ts +0 -762
- package/src/__tests__/media-visibility-policy.test.ts +0 -190
- package/src/config/bundled-skills/app-builder/tools/app-file-edit.ts +0 -14
- package/src/config/bundled-skills/app-builder/tools/app-file-list.ts +0 -13
- package/src/config/bundled-skills/app-builder/tools/app-file-read.ts +0 -21
- package/src/config/bundled-skills/app-builder/tools/app-file-write.ts +0 -14
- package/src/config/bundled-skills/app-builder/tools/app-list.ts +0 -13
- package/src/config/bundled-skills/app-builder/tools/app-update.ts +0 -23
- package/src/daemon/media-visibility-policy.ts +0 -59
- package/src/tools/assets/materialize.ts +0 -248
- package/src/tools/assets/search.ts +0 -400
package/src/tools/AGENTS.md
CHANGED
|
@@ -1,18 +1,14 @@
|
|
|
1
1
|
# Tools - Agent Instructions
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## New Non-Skill Tools Are Strongly Discouraged
|
|
4
4
|
|
|
5
|
-
**
|
|
5
|
+
**Prefer skills over new non-skill tool registrations.** Non-skill tools require approval from Team Jarvis.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Skills are the preferred approach for adding new capabilities — they are progressively disclosed into context, more portable, and can be iterated on independently. New non-skill tool registrations (`class ... implements Tool` + `registerTool()`) carry additional costs:
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
1. **Context overhead** — Each registered tool adds to the system prompt and increases token usage for every conversation.
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
2. **Context overhead** - Each registered tool adds to the system prompt and increases token usage for every conversation.
|
|
14
|
-
|
|
15
|
-
3. **Maintenance burden** - Tools require ongoing maintenance, testing, and security review. Skills can be iterated on independently.
|
|
11
|
+
2. **Maintenance burden** — Tools require ongoing maintenance, testing, and security review.
|
|
16
12
|
|
|
17
13
|
## What To Do Instead
|
|
18
14
|
|
|
@@ -26,7 +22,7 @@ Instead of creating a new tool, consider:
|
|
|
26
22
|
|
|
27
23
|
## Approved Exception: Credential Execution Service (CES) Tools
|
|
28
24
|
|
|
29
|
-
The following three CES tools are
|
|
25
|
+
The following three CES tools are approved exceptions that justify tool registrations over skills:
|
|
30
26
|
|
|
31
27
|
| Tool | Purpose |
|
|
32
28
|
| ---------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
@@ -10,11 +10,8 @@
|
|
|
10
10
|
|
|
11
11
|
import { compileApp } from "../../bundler/app-compiler.js";
|
|
12
12
|
import { generateAppIcon } from "../../media/app-icon-generator.js";
|
|
13
|
-
import type {
|
|
14
|
-
|
|
15
|
-
EditEngineResult,
|
|
16
|
-
} from "../../memory/app-store.js";
|
|
17
|
-
import { getAppsDir, isMultifileApp } from "../../memory/app-store.js";
|
|
13
|
+
import type { AppDefinition } from "../../memory/app-store.js";
|
|
14
|
+
import { getAppDirPath } from "../../memory/app-store.js";
|
|
18
15
|
|
|
19
16
|
// ---------------------------------------------------------------------------
|
|
20
17
|
// Shared result type
|
|
@@ -35,9 +32,6 @@ export interface ExecutorResult {
|
|
|
35
32
|
export interface AppStoreReader {
|
|
36
33
|
getApp(id: string): AppDefinition | null;
|
|
37
34
|
listApps(): AppDefinition[];
|
|
38
|
-
queryAppRecords(appId: string): unknown[];
|
|
39
|
-
listAppFiles(appId: string): string[];
|
|
40
|
-
readAppFile(appId: string, path: string): string;
|
|
41
35
|
}
|
|
42
36
|
|
|
43
37
|
export interface AppStoreWriter {
|
|
@@ -61,13 +55,6 @@ export interface AppStoreWriter {
|
|
|
61
55
|
): AppDefinition;
|
|
62
56
|
deleteApp(id: string): void;
|
|
63
57
|
writeAppFile(appId: string, path: string, content: string): void;
|
|
64
|
-
editAppFile(
|
|
65
|
-
appId: string,
|
|
66
|
-
path: string,
|
|
67
|
-
oldString: string,
|
|
68
|
-
newString: string,
|
|
69
|
-
replaceAll?: boolean,
|
|
70
|
-
): EditEngineResult;
|
|
71
58
|
}
|
|
72
59
|
|
|
73
60
|
export type AppStore = AppStoreReader & AppStoreWriter;
|
|
@@ -81,28 +68,6 @@ export type ProxyResolver = (
|
|
|
81
68
|
input: Record<string, unknown>,
|
|
82
69
|
) => Promise<ExecutorResult>;
|
|
83
70
|
|
|
84
|
-
// ---------------------------------------------------------------------------
|
|
85
|
-
// Path resolution - multifile apps default to src/ for file operations
|
|
86
|
-
// ---------------------------------------------------------------------------
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* For multifile (formatVersion 2) apps, prepend `src/` to paths that don't
|
|
90
|
-
* already target a known top-level directory (src/, dist/, records/).
|
|
91
|
-
* Legacy apps pass through unchanged.
|
|
92
|
-
*/
|
|
93
|
-
export function resolveAppFilePath(app: AppDefinition, path: string): string {
|
|
94
|
-
if (!isMultifileApp(app)) return path;
|
|
95
|
-
const normalized = path.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
96
|
-
if (
|
|
97
|
-
normalized.startsWith("src/") ||
|
|
98
|
-
normalized.startsWith("dist/") ||
|
|
99
|
-
normalized.startsWith("records/")
|
|
100
|
-
) {
|
|
101
|
-
return normalized;
|
|
102
|
-
}
|
|
103
|
-
return `src/${normalized}`;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
71
|
// ---------------------------------------------------------------------------
|
|
107
72
|
// app_create
|
|
108
73
|
// ---------------------------------------------------------------------------
|
|
@@ -222,8 +187,7 @@ render(<App />, document.getElementById('app')!);
|
|
|
222
187
|
store.writeAppFile(app.id, "src/main.tsx", mainTsx);
|
|
223
188
|
|
|
224
189
|
// Compile src/ → dist/
|
|
225
|
-
const
|
|
226
|
-
const appDir = join(getAppsDir(), app.id);
|
|
190
|
+
const appDir = getAppDirPath(app.id);
|
|
227
191
|
const compileResult = await compileApp(appDir);
|
|
228
192
|
if (!compileResult.ok) {
|
|
229
193
|
return {
|
|
@@ -286,72 +250,6 @@ render(<App />, document.getElementById('app')!);
|
|
|
286
250
|
return { content: JSON.stringify(app), isError: false };
|
|
287
251
|
}
|
|
288
252
|
|
|
289
|
-
// ---------------------------------------------------------------------------
|
|
290
|
-
// app_list
|
|
291
|
-
// ---------------------------------------------------------------------------
|
|
292
|
-
|
|
293
|
-
export function executeAppList(store: AppStoreReader): ExecutorResult {
|
|
294
|
-
const apps = store.listApps().map((a) => ({
|
|
295
|
-
id: a.id,
|
|
296
|
-
name: a.name,
|
|
297
|
-
description: a.description,
|
|
298
|
-
updatedAt: a.updatedAt,
|
|
299
|
-
}));
|
|
300
|
-
return { content: JSON.stringify(apps), isError: false };
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
// ---------------------------------------------------------------------------
|
|
304
|
-
// app_query
|
|
305
|
-
// ---------------------------------------------------------------------------
|
|
306
|
-
|
|
307
|
-
export interface AppQueryInput {
|
|
308
|
-
app_id: string;
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
export function executeAppQuery(
|
|
312
|
-
input: AppQueryInput,
|
|
313
|
-
store: AppStoreReader,
|
|
314
|
-
): ExecutorResult {
|
|
315
|
-
const records = store.queryAppRecords(input.app_id);
|
|
316
|
-
return { content: JSON.stringify(records), isError: false };
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
// ---------------------------------------------------------------------------
|
|
320
|
-
// app_update
|
|
321
|
-
// ---------------------------------------------------------------------------
|
|
322
|
-
|
|
323
|
-
export interface AppUpdateInput {
|
|
324
|
-
app_id: string;
|
|
325
|
-
name?: string;
|
|
326
|
-
description?: string;
|
|
327
|
-
schema_json?: string;
|
|
328
|
-
html?: string;
|
|
329
|
-
pages?: Record<string, string>;
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
export function executeAppUpdate(
|
|
333
|
-
input: AppUpdateInput,
|
|
334
|
-
store: AppStore,
|
|
335
|
-
): ExecutorResult {
|
|
336
|
-
const updates: Partial<
|
|
337
|
-
Pick<
|
|
338
|
-
AppDefinition,
|
|
339
|
-
"name" | "description" | "schemaJson" | "htmlDefinition" | "pages"
|
|
340
|
-
>
|
|
341
|
-
> = {};
|
|
342
|
-
if (typeof input.name === "string") updates.name = input.name;
|
|
343
|
-
if (typeof input.description === "string")
|
|
344
|
-
updates.description = input.description;
|
|
345
|
-
if (typeof input.schema_json === "string")
|
|
346
|
-
updates.schemaJson = input.schema_json;
|
|
347
|
-
if (typeof input.html === "string") updates.htmlDefinition = input.html;
|
|
348
|
-
if (input.pages && typeof input.pages === "object")
|
|
349
|
-
updates.pages = input.pages;
|
|
350
|
-
|
|
351
|
-
const app = store.updateApp(input.app_id, updates);
|
|
352
|
-
return { content: JSON.stringify(app), isError: false };
|
|
353
|
-
}
|
|
354
|
-
|
|
355
253
|
// ---------------------------------------------------------------------------
|
|
356
254
|
// app_delete
|
|
357
255
|
// ---------------------------------------------------------------------------
|
|
@@ -372,131 +270,15 @@ export function executeAppDelete(
|
|
|
372
270
|
}
|
|
373
271
|
|
|
374
272
|
// ---------------------------------------------------------------------------
|
|
375
|
-
//
|
|
376
|
-
// ---------------------------------------------------------------------------
|
|
377
|
-
|
|
378
|
-
export interface AppFileListInput {
|
|
379
|
-
app_id: string;
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
export function executeAppFileList(
|
|
383
|
-
input: AppFileListInput,
|
|
384
|
-
store: AppStoreReader,
|
|
385
|
-
): ExecutorResult {
|
|
386
|
-
const files = store.listAppFiles(input.app_id);
|
|
387
|
-
const app = store.getApp(input.app_id);
|
|
388
|
-
|
|
389
|
-
if (app && isMultifileApp(app)) {
|
|
390
|
-
// Separate build output paths from source paths without mutating the
|
|
391
|
-
// file path strings - consumers need clean paths for subsequent tool calls.
|
|
392
|
-
const buildOutputPaths = files.filter((f) =>
|
|
393
|
-
f.replace(/\\/g, "/").startsWith("dist/"),
|
|
394
|
-
);
|
|
395
|
-
return {
|
|
396
|
-
content: JSON.stringify({
|
|
397
|
-
files,
|
|
398
|
-
buildOutput: buildOutputPaths,
|
|
399
|
-
}),
|
|
400
|
-
isError: false,
|
|
401
|
-
};
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
return { content: JSON.stringify(files), isError: false };
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
// ---------------------------------------------------------------------------
|
|
408
|
-
// app_file_read
|
|
409
|
-
// ---------------------------------------------------------------------------
|
|
410
|
-
|
|
411
|
-
export interface AppFileReadInput {
|
|
412
|
-
app_id: string;
|
|
413
|
-
path: string;
|
|
414
|
-
offset?: number;
|
|
415
|
-
limit?: number;
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
export function executeAppFileRead(
|
|
419
|
-
input: AppFileReadInput,
|
|
420
|
-
store: AppStoreReader,
|
|
421
|
-
): ExecutorResult {
|
|
422
|
-
const offset = input.offset ?? 1;
|
|
423
|
-
const limit = input.limit;
|
|
424
|
-
|
|
425
|
-
const app = store.getApp(input.app_id);
|
|
426
|
-
const resolvedPath = app ? resolveAppFilePath(app, input.path) : input.path;
|
|
427
|
-
const raw = store.readAppFile(input.app_id, resolvedPath);
|
|
428
|
-
const allLines = raw.split("\n");
|
|
429
|
-
const startIndex = Math.max(0, offset - 1);
|
|
430
|
-
const sliced =
|
|
431
|
-
limit != null
|
|
432
|
-
? allLines.slice(startIndex, startIndex + limit)
|
|
433
|
-
: allLines.slice(startIndex);
|
|
434
|
-
|
|
435
|
-
const formatted = sliced
|
|
436
|
-
.map((line, i) => {
|
|
437
|
-
const lineNum = startIndex + i + 1;
|
|
438
|
-
return `${String(lineNum).padStart(6)}\t${line}`;
|
|
439
|
-
})
|
|
440
|
-
.join("\n");
|
|
441
|
-
|
|
442
|
-
return { content: formatted, isError: false };
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
// ---------------------------------------------------------------------------
|
|
446
|
-
// app_file_edit
|
|
447
|
-
// ---------------------------------------------------------------------------
|
|
448
|
-
|
|
449
|
-
export interface AppFileEditInput {
|
|
450
|
-
app_id: string;
|
|
451
|
-
path: string;
|
|
452
|
-
old_string: string;
|
|
453
|
-
new_string: string;
|
|
454
|
-
replace_all?: boolean;
|
|
455
|
-
status?: string;
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
export function executeAppFileEdit(
|
|
459
|
-
input: AppFileEditInput,
|
|
460
|
-
store: AppStore,
|
|
461
|
-
): ExecutorResult {
|
|
462
|
-
if (!input.old_string) {
|
|
463
|
-
return {
|
|
464
|
-
content: JSON.stringify({ error: "old_string must not be empty" }),
|
|
465
|
-
isError: true,
|
|
466
|
-
};
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
const app = store.getApp(input.app_id);
|
|
470
|
-
const resolvedPath = app ? resolveAppFilePath(app, input.path) : input.path;
|
|
471
|
-
|
|
472
|
-
const replaceAll = input.replace_all ?? false;
|
|
473
|
-
const result = store.editAppFile(
|
|
474
|
-
input.app_id,
|
|
475
|
-
resolvedPath,
|
|
476
|
-
input.old_string,
|
|
477
|
-
input.new_string,
|
|
478
|
-
replaceAll,
|
|
479
|
-
);
|
|
480
|
-
return {
|
|
481
|
-
content: JSON.stringify(result),
|
|
482
|
-
isError: false,
|
|
483
|
-
status: input.status,
|
|
484
|
-
};
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
// ---------------------------------------------------------------------------
|
|
488
|
-
// app_file_write
|
|
273
|
+
// app_refresh
|
|
489
274
|
// ---------------------------------------------------------------------------
|
|
490
275
|
|
|
491
|
-
export interface
|
|
276
|
+
export interface AppRefreshInput {
|
|
492
277
|
app_id: string;
|
|
493
|
-
path: string;
|
|
494
|
-
content: string;
|
|
495
|
-
status?: string;
|
|
496
278
|
}
|
|
497
279
|
|
|
498
|
-
export function
|
|
499
|
-
input:
|
|
280
|
+
export function executeAppRefresh(
|
|
281
|
+
input: AppRefreshInput,
|
|
500
282
|
store: AppStore,
|
|
501
283
|
): ExecutorResult {
|
|
502
284
|
const app = store.getApp(input.app_id);
|
|
@@ -507,12 +289,16 @@ export function executeAppFileWrite(
|
|
|
507
289
|
};
|
|
508
290
|
}
|
|
509
291
|
|
|
510
|
-
|
|
511
|
-
|
|
292
|
+
// Empty update bumps updatedAt timestamp, triggering recompilation and
|
|
293
|
+
// surface refresh on the client side.
|
|
294
|
+
const updated = store.updateApp(input.app_id, {});
|
|
512
295
|
return {
|
|
513
|
-
content: JSON.stringify({
|
|
296
|
+
content: JSON.stringify({
|
|
297
|
+
refreshed: true,
|
|
298
|
+
appId: updated.id,
|
|
299
|
+
name: updated.name,
|
|
300
|
+
}),
|
|
514
301
|
isError: false,
|
|
515
|
-
status: input.status,
|
|
516
302
|
};
|
|
517
303
|
}
|
|
518
304
|
|
|
@@ -541,9 +327,8 @@ export async function executeAppGenerateIcon(
|
|
|
541
327
|
// destroying an existing icon if generation fails.
|
|
542
328
|
const { existsSync, renameSync, unlinkSync } = await import("node:fs");
|
|
543
329
|
const { join } = await import("node:path");
|
|
544
|
-
const
|
|
545
|
-
const
|
|
546
|
-
const tempPath = join(getAppsDir(), input.app_id, "icon.tmp.png");
|
|
330
|
+
const iconPath = join(getAppDirPath(input.app_id), "icon.png");
|
|
331
|
+
const tempPath = join(getAppDirPath(input.app_id), "icon.tmp.png");
|
|
547
332
|
|
|
548
333
|
// Temporarily move existing icon aside so generateAppIcon doesn't skip
|
|
549
334
|
if (existsSync(iconPath)) {
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
} from "../../commands/cc-command-registry.js";
|
|
5
5
|
import { RiskLevel } from "../../permissions/types.js";
|
|
6
6
|
import type { ToolDefinition } from "../../providers/types.js";
|
|
7
|
-
import {
|
|
7
|
+
import { getProviderKeyAsync } from "../../security/secure-keys.js";
|
|
8
8
|
import type { WorkerProfile } from "../../swarm/worker-backend.js";
|
|
9
9
|
import { getProfilePolicy } from "../../swarm/worker-backend.js";
|
|
10
10
|
import { getLogger } from "../../util/logger.js";
|
|
@@ -203,8 +203,7 @@ export const claudeCodeTool: Tool = {
|
|
|
203
203
|
const profilePolicy = getProfilePolicy(profileName);
|
|
204
204
|
|
|
205
205
|
// Validate API key
|
|
206
|
-
const apiKey =
|
|
207
|
-
(await getSecureKeyAsync("anthropic")) ?? process.env.ANTHROPIC_API_KEY;
|
|
206
|
+
const apiKey = await getProviderKeyAsync("anthropic");
|
|
208
207
|
if (!apiKey) {
|
|
209
208
|
return {
|
|
210
209
|
content:
|
|
@@ -7,8 +7,8 @@ import { orchestrateOAuthConnect } from "../../oauth/connect-orchestrator.js";
|
|
|
7
7
|
import { syncManualTokenConnection } from "../../oauth/manual-token-connection.js";
|
|
8
8
|
import {
|
|
9
9
|
disconnectOAuthProvider,
|
|
10
|
+
getActiveConnection,
|
|
10
11
|
getAppByProviderAndClientId,
|
|
11
|
-
getConnectionByProviderAndAccount,
|
|
12
12
|
getMostRecentAppByProvider,
|
|
13
13
|
getProvider,
|
|
14
14
|
} from "../../oauth/oauth-store.js";
|
|
@@ -66,9 +66,7 @@ async function storeSlackChannelCredential(
|
|
|
66
66
|
: setSlackChannelConfig(undefined, value);
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
function formatSlackChannelStatus(
|
|
70
|
-
result: SlackChannelConfigResult,
|
|
71
|
-
): string {
|
|
69
|
+
function formatSlackChannelStatus(result: SlackChannelConfigResult): string {
|
|
72
70
|
if (result.connected) {
|
|
73
71
|
const teamLabel = result.teamName ?? "Slack";
|
|
74
72
|
const botLabel = result.botUsername ? ` (@${result.botUsername})` : "";
|
|
@@ -366,8 +364,7 @@ class CredentialStoreTool implements Tool {
|
|
|
366
364
|
if (!slackChannelResult.success) {
|
|
367
365
|
return {
|
|
368
366
|
content: `Error: ${
|
|
369
|
-
slackChannelResult.error ??
|
|
370
|
-
"failed to configure Slack channel"
|
|
367
|
+
slackChannelResult.error ?? "failed to configure Slack channel"
|
|
371
368
|
}`,
|
|
372
369
|
isError: true,
|
|
373
370
|
};
|
|
@@ -523,10 +520,9 @@ class CredentialStoreTool implements Tool {
|
|
|
523
520
|
const accountHint = input.account as string | undefined;
|
|
524
521
|
let oauthResult: "disconnected" | "not-found" | "error";
|
|
525
522
|
if (accountHint) {
|
|
526
|
-
const targetConn =
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
);
|
|
523
|
+
const targetConn = getActiveConnection(service, {
|
|
524
|
+
account: accountHint,
|
|
525
|
+
});
|
|
530
526
|
oauthResult = targetConn
|
|
531
527
|
? await disconnectOAuthProvider(service, undefined, targetConn.id)
|
|
532
528
|
: "not-found";
|
|
@@ -778,8 +774,7 @@ class CredentialStoreTool implements Tool {
|
|
|
778
774
|
if (!slackChannelResult.success) {
|
|
779
775
|
return {
|
|
780
776
|
content: `Error: ${
|
|
781
|
-
slackChannelResult.error ??
|
|
782
|
-
"failed to configure Slack channel"
|
|
777
|
+
slackChannelResult.error ?? "failed to configure Slack channel"
|
|
783
778
|
}`,
|
|
784
779
|
isError: true,
|
|
785
780
|
};
|
|
@@ -54,6 +54,19 @@ class HostFileReadTool implements Tool {
|
|
|
54
54
|
};
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
+
// Image files must be handled locally — the host-file proxy protocol
|
|
58
|
+
// only carries {content, isError} and cannot transport contentBlocks
|
|
59
|
+
// (base64 image data). Check for image extensions before the proxy
|
|
60
|
+
// short-circuit so image reads work in managed/macOS+iOS sessions.
|
|
61
|
+
const ext = extname(rawPath).toLowerCase();
|
|
62
|
+
if (IMAGE_EXTENSIONS.has(ext)) {
|
|
63
|
+
const pathCheck = hostPolicy(rawPath);
|
|
64
|
+
if (!pathCheck.ok) {
|
|
65
|
+
return { content: `Error: ${pathCheck.error}`, isError: true };
|
|
66
|
+
}
|
|
67
|
+
return readImageFile(pathCheck.resolved);
|
|
68
|
+
}
|
|
69
|
+
|
|
57
70
|
// Proxy to connected client for execution on the user's machine
|
|
58
71
|
// when a capable client is available (managed/cloud-hosted mode).
|
|
59
72
|
if (context.hostFileProxy?.isAvailable()) {
|
|
@@ -69,16 +82,6 @@ class HostFileReadTool implements Tool {
|
|
|
69
82
|
);
|
|
70
83
|
}
|
|
71
84
|
|
|
72
|
-
// For image files, delegate to the shared image reader.
|
|
73
|
-
const ext = extname(rawPath).toLowerCase();
|
|
74
|
-
if (IMAGE_EXTENSIONS.has(ext)) {
|
|
75
|
-
const pathCheck = hostPolicy(rawPath);
|
|
76
|
-
if (!pathCheck.ok) {
|
|
77
|
-
return { content: `Error: ${pathCheck.error}`, isError: true };
|
|
78
|
-
}
|
|
79
|
-
return readImageFile(pathCheck.resolved);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
85
|
const ops = new FileSystemOps(hostPolicy);
|
|
83
86
|
|
|
84
87
|
const result = ops.readFileSafe({
|
|
@@ -16,12 +16,14 @@ mock.module("../../registry.js", () => ({
|
|
|
16
16
|
|
|
17
17
|
mock.module("../../../config/loader.js", () => ({
|
|
18
18
|
getConfig: () => ({
|
|
19
|
-
|
|
19
|
+
services: {
|
|
20
|
+
"web-search": { provider: mockWebSearchProvider },
|
|
21
|
+
},
|
|
20
22
|
}),
|
|
21
23
|
}));
|
|
22
24
|
|
|
23
25
|
mock.module("../../../security/secure-keys.js", () => ({
|
|
24
|
-
|
|
26
|
+
getProviderKeyAsync: async (provider: string) => {
|
|
25
27
|
if (provider === "brave") return mockBraveSecureKey;
|
|
26
28
|
if (provider === "perplexity") return mockPerplexitySecureKey;
|
|
27
29
|
return undefined;
|
|
@@ -62,15 +62,10 @@ export async function executeScheduleList(
|
|
|
62
62
|
);
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
lines.push(
|
|
66
|
-
` Enabled: ${job.enabled}`,
|
|
67
|
-
` Message: ${job.message}`,
|
|
68
|
-
);
|
|
65
|
+
lines.push(` Enabled: ${job.enabled}`, ` Message: ${job.message}`);
|
|
69
66
|
|
|
70
67
|
if (!oneShot) {
|
|
71
|
-
lines.push(
|
|
72
|
-
` Next run: ${formatLocalDate(job.nextRunAt)}`,
|
|
73
|
-
);
|
|
68
|
+
lines.push(` Next run: ${formatLocalDate(job.nextRunAt)}`);
|
|
74
69
|
}
|
|
75
70
|
|
|
76
71
|
lines.push(
|
|
@@ -29,7 +29,12 @@ export function injectActivityField(
|
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
const properties = schema.properties as Record<string, unknown>;
|
|
32
|
+
|
|
32
33
|
if (schemaDefinesProperty(schema, "activity")) {
|
|
34
|
+
// Activity is already defined somewhere in the schema (top-level properties
|
|
35
|
+
// or composite sub-schemas). Don't modify schemas we don't own — MCP tools
|
|
36
|
+
// may define activity as intentionally optional or with server-specific
|
|
37
|
+
// semantics.
|
|
33
38
|
return def;
|
|
34
39
|
}
|
|
35
40
|
|
|
@@ -1,22 +1,12 @@
|
|
|
1
|
-
const MAX_DIFF_LINES = 8;
|
|
2
|
-
|
|
3
1
|
/**
|
|
4
|
-
* Build
|
|
5
|
-
* Lines are prefixed with - /
|
|
2
|
+
* Build an inline diff from an old→new string replacement.
|
|
3
|
+
* Lines are prefixed with - / +.
|
|
6
4
|
*/
|
|
7
5
|
export function formatEditDiff(oldString: string, newString: string): string {
|
|
8
6
|
const removed =
|
|
9
|
-
oldString.length > 0
|
|
10
|
-
? truncateLines(oldString.split("\n"), MAX_DIFF_LINES).map(
|
|
11
|
-
(l) => `- ${l}`,
|
|
12
|
-
)
|
|
13
|
-
: [];
|
|
7
|
+
oldString.length > 0 ? oldString.split("\n").map((l) => `- ${l}`) : [];
|
|
14
8
|
const added =
|
|
15
|
-
newString.length > 0
|
|
16
|
-
? truncateLines(newString.split("\n"), MAX_DIFF_LINES).map(
|
|
17
|
-
(l) => `+ ${l}`,
|
|
18
|
-
)
|
|
19
|
-
: [];
|
|
9
|
+
newString.length > 0 ? newString.split("\n").map((l) => `+ ${l}`) : [];
|
|
20
10
|
|
|
21
11
|
return [...removed, ...added].join("\n");
|
|
22
12
|
}
|
|
@@ -36,10 +26,3 @@ export function formatWriteSummary(
|
|
|
36
26
|
const oldLineCount = oldContent.split("\n").length;
|
|
37
27
|
return `(${oldLineCount} → ${newLineCount} lines)`;
|
|
38
28
|
}
|
|
39
|
-
|
|
40
|
-
function truncateLines(lines: string[], max: number): string[] {
|
|
41
|
-
if (lines.length <= max) return lines;
|
|
42
|
-
const kept = lines.slice(0, max);
|
|
43
|
-
kept.push(`... (${lines.length - max} more lines)`);
|
|
44
|
-
return kept;
|
|
45
|
-
}
|
|
@@ -11,8 +11,6 @@ import {
|
|
|
11
11
|
isCesSecureInstallEnabled,
|
|
12
12
|
isCesToolsEnabled,
|
|
13
13
|
} from "../credential-execution/feature-gates.js";
|
|
14
|
-
import { assetMaterializeTool } from "./assets/materialize.js";
|
|
15
|
-
import { assetSearchTool } from "./assets/search.js";
|
|
16
14
|
import { makeAuthenticatedRequestTool } from "./credential-execution/make-authenticated-request.js";
|
|
17
15
|
import { manageSecureCommandTool } from "./credential-execution/manage-secure-command-tool.js";
|
|
18
16
|
import { runAuthenticatedCommandTool } from "./credential-execution/run-authenticated-command.js";
|
|
@@ -63,8 +61,6 @@ export const eagerModuleToolNames: string[] = [
|
|
|
63
61
|
"skill_execute",
|
|
64
62
|
"skill_load",
|
|
65
63
|
"request_system_permission",
|
|
66
|
-
"asset_search",
|
|
67
|
-
"asset_materialize",
|
|
68
64
|
];
|
|
69
65
|
|
|
70
66
|
// ── Explicit tool instances ─────────────────────────────────────────
|
|
@@ -85,8 +81,6 @@ export const explicitTools: Tool[] = [
|
|
|
85
81
|
skillExecuteTool,
|
|
86
82
|
skillLoadTool,
|
|
87
83
|
requestSystemPermissionTool,
|
|
88
|
-
assetSearchTool,
|
|
89
|
-
assetMaterializeTool,
|
|
90
84
|
// Always-explicit tools
|
|
91
85
|
memoryManageTool,
|
|
92
86
|
memoryRecallTool,
|
|
@@ -30,7 +30,7 @@ export const uiShowTool: Tool = {
|
|
|
30
30
|
description:
|
|
31
31
|
"Show structured data or UI to the user. For long-form writing use the document skill; for interactive apps use the app-builder skill.\n\n" +
|
|
32
32
|
"Surface types (data shapes):\n" +
|
|
33
|
-
|
|
33
|
+
'- card: { title, subtitle?, body, metadata?: [{ label, value }], template?, templateData? }. Templates: "weather_forecast" (native weather widget), "task_progress" (live step tracker - update via ui_update on data.templateData; shape: { title, status: "in_progress"|"completed"|"failed", steps: [{ label, status: "pending"|"in_progress"|"completed"|"failed", detail? }] })\n' +
|
|
34
34
|
'- table: { columns: [{ id, label, width? }], rows: [{ id, cells: Record<id, string | { text, icon?, iconColor?: "success"|"warning"|"error"|"muted" }>, selectable?, selected? }], selectionMode?: "none"|"single"|"multiple", caption? }\n' +
|
|
35
35
|
'- form: { description?, fields: [{ id, type: "text"|"textarea"|"select"|"toggle"|"number"|"password", label, placeholder?, required?, defaultValue?, options?: [{ label, value }] }], submitLabel? }. Multi-page: { pages: [{ id, title, description?, fields }], pageLabels?: { next?, back?, submit? }, submitLabel? }\n' +
|
|
36
36
|
'- list: { items: [{ id, title, subtitle?, icon?, selected? }], selectionMode: "single"|"multiple"|"none" }\n' +
|
|
@@ -91,7 +91,7 @@ export const uiShowTool: Tool = {
|
|
|
91
91
|
type: "string",
|
|
92
92
|
enum: ["inline", "panel"],
|
|
93
93
|
description:
|
|
94
|
-
'Where to render the surface. "inline" embeds it in the chat message. "panel" shows a floating window. Defaults to "inline".',
|
|
94
|
+
'Where to render the surface. "inline" embeds it in the chat message. "panel" shows a floating window. Defaults to "inline". Prefer inline — only use panel when the user explicitly asks for a separate window.',
|
|
95
95
|
},
|
|
96
96
|
await_action: {
|
|
97
97
|
type: "boolean",
|
package/src/util/device-id.ts
CHANGED
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Device ID resolver.
|
|
3
3
|
*
|
|
4
|
-
* Reads or creates a stable per-device UUID stored in
|
|
5
|
-
* The file is a JSON object (`{ "deviceId": "<uuid>" }`)
|
|
6
|
-
* future per-device metadata.
|
|
4
|
+
* Reads or creates a stable per-device UUID stored in device.json under the
|
|
5
|
+
* Vellum config directory. The file is a JSON object (`{ "deviceId": "<uuid>" }`)
|
|
6
|
+
* extensible for future per-device metadata.
|
|
7
|
+
*
|
|
8
|
+
* Path resolution:
|
|
9
|
+
* - Containerized (IS_CONTAINERIZED=true): uses BASE_DATA_DIR, which maps to a
|
|
10
|
+
* persistent volume. Each container is effectively its own "device."
|
|
11
|
+
* - Local (single or multi-instance): uses homedir() so all instances on the
|
|
12
|
+
* same machine share a single device ID, even when BASE_DATA_DIR is set to
|
|
13
|
+
* an instance-scoped directory.
|
|
7
14
|
*
|
|
8
15
|
* The value is cached in memory after the first successful read/write.
|
|
9
16
|
* Falls back to a generated UUID if the file cannot be read or written.
|
|
@@ -14,18 +21,34 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
|
14
21
|
import { homedir } from "node:os";
|
|
15
22
|
import { join } from "node:path";
|
|
16
23
|
|
|
24
|
+
import { getBaseDataDir, getIsContainerized } from "../config/env-registry.js";
|
|
17
25
|
import { getLogger } from "./logger.js";
|
|
18
26
|
|
|
19
27
|
const log = getLogger("device-id");
|
|
20
28
|
|
|
21
29
|
let cached: string | undefined;
|
|
22
30
|
|
|
31
|
+
/**
|
|
32
|
+
* Resolve the base directory for device.json.
|
|
33
|
+
*
|
|
34
|
+
* In containerized environments, BASE_DATA_DIR points to a persistent volume
|
|
35
|
+
* and homedir() is ephemeral, so we must use BASE_DATA_DIR.
|
|
36
|
+
* In local environments (including multi-instance), homedir() is stable and
|
|
37
|
+
* shared across instances, giving a true per-machine device ID.
|
|
38
|
+
*/
|
|
39
|
+
export function getDeviceIdBaseDir(): string {
|
|
40
|
+
if (getIsContainerized()) {
|
|
41
|
+
return getBaseDataDir() || homedir();
|
|
42
|
+
}
|
|
43
|
+
return homedir();
|
|
44
|
+
}
|
|
45
|
+
|
|
23
46
|
/**
|
|
24
47
|
* Get the stable device ID for this machine.
|
|
25
48
|
*
|
|
26
49
|
* Resolution order:
|
|
27
50
|
* 1. Cached in-memory value (populated on first call)
|
|
28
|
-
* 2. `deviceId` field from
|
|
51
|
+
* 2. `deviceId` field from device.json
|
|
29
52
|
* 3. Generate a new UUID, persist it to device.json, and return it
|
|
30
53
|
*
|
|
31
54
|
* On any read/write error the generated UUID is still cached so the
|
|
@@ -36,7 +59,7 @@ export function getDeviceId(): string {
|
|
|
36
59
|
return cached;
|
|
37
60
|
}
|
|
38
61
|
|
|
39
|
-
const vellumDir = join(
|
|
62
|
+
const vellumDir = join(getDeviceIdBaseDir(), ".vellum");
|
|
40
63
|
const filePath = join(vellumDir, "device.json");
|
|
41
64
|
const generated = randomUUID();
|
|
42
65
|
|