@vellumai/assistant 0.8.1 → 0.8.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 +2 -7
- package/Dockerfile +75 -1
- package/bun.lock +11 -1
- package/docker-entrypoint.sh +5 -0
- package/docker-init-apt-root.sh +94 -0
- package/docker-kata-apt-env.sh +39 -0
- package/docs/plugins.md +88 -47
- package/docs/skills.md +9 -7
- package/examples/plugins/echo/README.md +27 -27
- package/examples/plugins/echo/package.json +3 -0
- package/examples/plugins/echo/register.ts +31 -31
- package/node_modules/@vellumai/slack-text/src/index.test.ts +114 -14
- package/node_modules/@vellumai/slack-text/src/index.ts +82 -18
- package/openapi.yaml +325 -3
- package/package.json +3 -1
- package/scripts/generate-openapi.ts +83 -10
- package/scripts/sync-llm-catalog.ts +2 -2
- package/scripts/sync-web-search-catalog.ts +47 -25
- package/src/__tests__/agent-image-optimize.test.ts +11 -3
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +131 -0
- package/src/__tests__/anthropic-provider.test.ts +45 -0
- package/src/__tests__/app-builder-tool-scripts.test.ts +9 -3
- package/src/__tests__/app-executors.test.ts +220 -4
- package/src/__tests__/auto-analysis-end-to-end.test.ts +35 -0
- package/src/__tests__/bundled-asset.test.ts +6 -6
- package/src/__tests__/channel-availability-routes.test.ts +206 -0
- package/src/__tests__/channel-delivery-store.test.ts +289 -1
- package/src/__tests__/circuit-breaker-pipeline.test.ts +0 -1
- package/src/__tests__/clawhub.test.ts +75 -16
- package/src/__tests__/compactor-tail-resolution.test.ts +41 -0
- package/src/__tests__/config-schema.test.ts +21 -0
- package/src/__tests__/config-set-route.test.ts +80 -0
- package/src/__tests__/config-sounds-sync.test.ts +97 -0
- package/src/__tests__/config-watcher-skill-reseed.test.ts +453 -0
- package/src/__tests__/context-search-conversations-source.test.ts +117 -2
- package/src/__tests__/context-search-memory-v2-source.test.ts +0 -1
- package/src/__tests__/context-search-workspace-source.test.ts +7 -0
- package/src/__tests__/context-token-estimator.test.ts +1 -0
- package/src/__tests__/conversation-abort-tool-results.test.ts +4 -1
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -0
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +92 -92
- package/src/__tests__/conversation-agent-loop.test.ts +2 -0
- package/src/__tests__/conversation-error.test.ts +42 -3
- package/src/__tests__/conversation-fork-crud.test.ts +82 -0
- package/src/__tests__/conversation-inference-profile-route.test.ts +40 -4
- package/src/__tests__/conversation-lifecycle.test.ts +173 -0
- package/src/__tests__/conversation-message-sync-tags.test.ts +97 -0
- package/src/__tests__/conversation-pairing.test.ts +54 -0
- package/src/__tests__/conversation-process-callsite.test.ts +4 -1
- package/src/__tests__/conversation-provider-retry-repair.test.ts +5 -1
- package/src/__tests__/conversation-queue.test.ts +4 -1
- package/src/__tests__/conversation-runtime-assembly.test.ts +76 -9
- package/src/__tests__/conversation-slash-queue.test.ts +59 -1
- package/src/__tests__/conversation-slash-unknown.test.ts +4 -1
- package/src/__tests__/conversation-surfaces-table-action.test.ts +360 -0
- package/src/__tests__/conversation-sync-tags.test.ts +235 -0
- package/src/__tests__/conversation-workspace-injection.test.ts +5 -1
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +5 -1
- package/src/__tests__/credential-security-invariants.test.ts +3 -2
- package/src/__tests__/db-slack-external-content-normalization.test.ts +301 -0
- package/src/__tests__/delete-managed-skill-tool.test.ts +55 -13
- package/src/__tests__/disk-pressure-tools.test.ts +1 -0
- package/src/__tests__/dm-backfill.test.ts +121 -10
- package/src/__tests__/document-tool-security.test.ts +258 -0
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +0 -1
- package/src/__tests__/edit-propagation.test.ts +33 -0
- package/src/__tests__/empty-response-pipeline.test.ts +0 -4
- package/src/__tests__/external-plugin-loader.test.ts +60 -36
- package/src/__tests__/filing-service.test.ts +140 -0
- package/src/__tests__/get-skill-detail-audit.test.ts +0 -4
- package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +43 -62
- package/src/__tests__/helpers/tar-fixtures.ts +39 -0
- package/src/__tests__/helpers/wait-for.ts +21 -0
- package/src/__tests__/history-repair-pipeline.test.ts +0 -3
- package/src/__tests__/history-repair.test.ts +73 -0
- package/src/__tests__/host-app-control-proxy.test.ts +266 -10
- package/src/__tests__/image-credentials.test.ts +1 -1
- package/src/__tests__/inbound-slack-persistence.test.ts +2 -0
- package/src/__tests__/inference-no-mode-boot-e2e.test.ts +1 -1
- package/src/__tests__/inference-profile-reaper.test.ts +4 -2
- package/src/__tests__/inference-profile-session-handler.test.ts +18 -6
- package/src/__tests__/inference-profile-session-ipc.test.ts +17 -5
- package/src/__tests__/injector-chain.test.ts +10 -8
- package/src/__tests__/install-skill-routing.test.ts +155 -37
- package/src/__tests__/lifecycle-memory-v2-seed.test.ts +92 -3
- package/src/__tests__/list-messages-page-latest.test.ts +55 -0
- package/src/__tests__/llm-call-pipeline.test.ts +0 -3
- package/src/__tests__/llm-catalog-parity.test.ts +55 -13
- package/src/__tests__/llm-request-log-source-clickhouse.test.ts +34 -0
- package/src/__tests__/llm-request-log-source-factory.test.ts +29 -53
- package/src/__tests__/llm-usage-store.test.ts +114 -0
- package/src/__tests__/managed-profile-guard.test.ts +31 -29
- package/src/__tests__/managed-skill-lifecycle.test.ts +109 -18
- package/src/__tests__/managed-store.test.ts +84 -192
- package/src/__tests__/media-generate-image.test.ts +1 -1
- package/src/__tests__/memory-retrieval-pipeline.test.ts +0 -2
- package/src/__tests__/messages-after-tiebreaker.test.ts +122 -0
- package/src/__tests__/oauth-commands-routes.test.ts +168 -16
- package/src/__tests__/oauth-provider-profiles.test.ts +9 -0
- package/src/__tests__/openai-provider.test.ts +24 -0
- package/src/__tests__/openai-responses-cutover-guard.test.ts +17 -9
- package/src/__tests__/overflow-reduce-pipeline.test.ts +0 -2
- package/src/__tests__/persistence-pipeline.test.ts +0 -2
- package/src/__tests__/{managed-proxy-context.test.ts → platform-proxy-context.test.ts} +1 -1
- package/src/__tests__/platform.test.ts +2 -0
- package/src/__tests__/plugin-api-shim.test.ts +125 -0
- package/src/__tests__/plugin-bootstrap.test.ts +10 -36
- package/src/__tests__/plugin-external-api.test.ts +68 -0
- package/src/__tests__/plugin-registry.test.ts +0 -77
- package/src/__tests__/plugin-route-contribution.test.ts +0 -1
- package/src/__tests__/plugin-skill-contribution.test.ts +0 -2
- package/src/__tests__/plugin-tool-contribution.test.ts +16 -15
- package/src/__tests__/plugin-types.test.ts +3 -13
- package/src/__tests__/process-message-background-slack.test.ts +8 -1
- package/src/__tests__/process-message-display-content.test.ts +421 -0
- package/src/__tests__/provider-catalog-visibility.test.ts +142 -0
- package/src/__tests__/provider-error-scenarios.test.ts +111 -0
- package/src/__tests__/{provider-managed-proxy-integration.test.ts → provider-platform-proxy-integration.test.ts} +8 -8
- package/src/__tests__/scaffold-managed-skill-tool.test.ts +65 -13
- package/src/__tests__/schedule-routes.test.ts +50 -3
- package/src/__tests__/schedule-store.test.ts +94 -0
- package/src/__tests__/scheduler-reuse-conversation.test.ts +54 -7
- package/src/__tests__/schema-transforms.test.ts +20 -0
- package/src/__tests__/search-skills-unified.test.ts +0 -5
- package/src/__tests__/server-history-render.test.ts +43 -0
- package/src/__tests__/skill-load-feature-flag.test.ts +0 -12
- package/src/__tests__/skill-load-tool.test.ts +27 -89
- package/src/__tests__/skill-memory.test.ts +23 -3
- package/src/__tests__/skills-file-content-endpoint.test.ts +9 -38
- package/src/__tests__/skills-files-catalog-fallback.test.ts +0 -3
- package/src/__tests__/skills-install-extract.test.ts +49 -38
- package/src/__tests__/skills-install-staging.test.ts +159 -0
- package/src/__tests__/skills-uninstall.test.ts +9 -41
- package/src/__tests__/skills.test.ts +51 -58
- package/src/__tests__/slack-channel-config.test.ts +9 -0
- package/src/__tests__/subagent-tool-filtering.test.ts +50 -0
- package/src/__tests__/system-prompt.test.ts +737 -63
- package/src/__tests__/terminal-tools.test.ts +28 -1
- package/src/__tests__/thread-backfill.test.ts +557 -27
- package/src/__tests__/title-generate-pipeline.test.ts +0 -13
- package/src/__tests__/token-estimate-pipeline.test.ts +0 -3
- package/src/__tests__/tool-error-pipeline.test.ts +0 -3
- package/src/__tests__/tool-execute-pipeline.test.ts +0 -5
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -1
- package/src/__tests__/tool-executor.test.ts +16 -4
- package/src/__tests__/tool-result-truncate-pipeline.test.ts +0 -12
- package/src/__tests__/turn-events-store.test.ts +256 -0
- package/src/__tests__/twilio-routes.test.ts +4 -0
- package/src/__tests__/user-plugin-loader.test.ts +0 -7
- package/src/__tests__/voice-session-bridge.test.ts +198 -0
- package/src/__tests__/web-search-catalog-parity.test.ts +32 -10
- package/src/__tests__/workspace-migration-057-repair-stale-gemini-model-ids.test.ts +115 -3
- package/src/__tests__/workspace-migration-072-seed-reply-suggestion-callsite.test.ts +50 -0
- package/src/__tests__/workspace-migration-073-repair-recall-callsite-empty-profile.test.ts +153 -0
- package/src/__tests__/workspace-migration-085-memory-v2-bm25-b-reembed-disabled-v2-pages.test.ts +220 -0
- package/src/__tests__/workspace-migration-086-revert-stale-gemini-mis-rewrites.test.ts +269 -0
- package/src/__tests__/workspace-migration-remove-legacy-skills-index.test.ts +309 -0
- package/src/__tests__/workspace-migrations-runner.test.ts +111 -3
- package/src/acp/resolve-agent.ts +1 -1
- package/src/agent/image-optimize.ts +13 -5
- package/src/calls/voice-session-bridge.ts +61 -42
- package/src/channels/types.ts +108 -0
- package/src/cli/__tests__/unknown-command.test.ts +24 -0
- package/src/cli/commands/__tests__/changelog.test.ts +304 -319
- package/src/cli/commands/__tests__/schedules.test.ts +491 -0
- package/src/cli/commands/changelog.ts +106 -42
- package/src/cli/commands/conversations.ts +102 -17
- package/src/cli/commands/default-action.ts +10 -53
- package/src/cli/commands/notifications.ts +329 -317
- package/src/cli/commands/plugins.ts +185 -0
- package/src/cli/commands/schedules.ts +391 -0
- package/src/cli/commands/telemetry.ts +40 -0
- package/src/cli/lib/__tests__/cli-colors.test.ts +48 -0
- package/src/cli/lib/__tests__/confirm-prompt.test.ts +159 -0
- package/src/cli/lib/__tests__/install-from-github.test.ts +355 -0
- package/src/cli/lib/__tests__/list-installed-plugins.test.ts +154 -0
- package/src/cli/lib/__tests__/uninstall-plugin.test.ts +124 -0
- package/src/cli/lib/__tests__/unknown-command.test.ts +106 -0
- package/src/cli/lib/cli-colors.ts +12 -0
- package/src/cli/lib/confirm-prompt.ts +79 -0
- package/src/cli/lib/install-from-github.ts +304 -0
- package/src/cli/lib/list-installed-plugins.ts +137 -0
- package/src/cli/lib/uninstall-plugin.ts +82 -0
- package/src/cli/lib/unknown-command.ts +111 -0
- package/src/cli/program.ts +38 -2
- package/src/config/bundled-skills/app-builder/SKILL.md +23 -21
- package/src/config/bundled-skills/app-builder/TOOLS.json +7 -0
- package/src/config/bundled-skills/computer-use/TOOLS.json +15 -52
- package/src/config/bundled-skills/document/SKILL.md +23 -3
- package/src/config/bundled-skills/document/TOOLS.json +53 -0
- package/src/config/bundled-skills/document/tools/document-delete.ts +12 -0
- package/src/config/bundled-skills/document/tools/document-list.ts +12 -0
- package/src/config/bundled-skills/document/tools/document-read.ts +12 -0
- package/src/config/bundled-skills/skill-management/SKILL.md +2 -2
- package/src/config/bundled-skills/skill-management/TOOLS.json +7 -7
- package/src/config/bundled-tool-registry.ts +6 -0
- package/src/config/feature-flag-registry.json +41 -1
- package/src/config/loader.ts +64 -38
- package/src/config/schema.ts +7 -10
- package/src/config/schemas/__tests__/llm-request-logs.test.ts +36 -0
- package/src/config/schemas/channels.ts +8 -0
- package/src/config/schemas/compaction.ts +28 -0
- package/src/config/schemas/heartbeat.ts +9 -0
- package/src/config/schemas/llm-request-logs.ts +31 -7
- package/src/config/schemas/llm.ts +3 -0
- package/src/config/schemas/memory-retrieval.ts +18 -0
- package/src/config/schemas/tools.ts +14 -0
- package/src/config/skills.ts +3 -96
- package/src/context/compactor.ts +1047 -0
- package/src/context/token-estimator.ts +2 -2
- package/src/context/window-manager.ts +197 -1520
- package/src/credential-execution/managed-catalog.ts +37 -0
- package/src/credential-health/credential-health-service.ts +280 -19
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +34 -0
- package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +138 -0
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +74 -0
- package/src/daemon/approval-generators.ts +8 -6
- package/src/daemon/config-watcher.ts +94 -31
- package/src/daemon/conversation-agent-loop.ts +169 -9
- package/src/daemon/conversation-error.ts +171 -37
- package/src/daemon/conversation-lifecycle.ts +53 -40
- package/src/daemon/conversation-messaging.ts +25 -6
- package/src/daemon/conversation-process.ts +49 -12
- package/src/daemon/conversation-runtime-assembly.ts +16 -1
- package/src/daemon/conversation-slash.ts +12 -5
- package/src/daemon/conversation-store.ts +11 -4
- package/src/daemon/conversation-tool-setup.ts +39 -7
- package/src/daemon/conversation.ts +33 -1
- package/src/daemon/external-plugins-bootstrap.ts +217 -181
- package/src/daemon/first-greeting.ts +22 -2
- package/src/daemon/handlers/config-model.ts +6 -5
- package/src/daemon/handlers/config-slack-channel.ts +15 -3
- package/src/daemon/handlers/shared.ts +14 -5
- package/src/daemon/handlers/skills.ts +111 -108
- package/src/daemon/history-repair.ts +28 -1
- package/src/daemon/host-app-control-proxy.ts +98 -23
- package/src/daemon/lifecycle.ts +45 -35
- package/src/daemon/meet-host-supervisor.ts +5 -4
- package/src/daemon/memory-v2-startup.ts +49 -0
- package/src/daemon/message-protocol.ts +1 -0
- package/src/daemon/message-types/conversations.ts +25 -0
- package/src/daemon/message-types/messages.ts +61 -0
- package/src/daemon/message-types/subagents.ts +1 -0
- package/src/daemon/message-types/sync.ts +1 -0
- package/src/daemon/pkb-reminder-builder.test.ts +1 -1
- package/src/daemon/pkb-reminder-builder.ts +1 -1
- package/src/daemon/plugin-source-watcher.ts +146 -0
- package/src/daemon/process-message.ts +21 -3
- package/src/daemon/server.ts +11 -2
- package/src/daemon/skill-memory-refresh.ts +29 -0
- package/src/documents/document-store.ts +221 -3
- package/src/embedded/plugin-api.ts +40 -0
- package/src/filing/filing-service.ts +39 -0
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +91 -6
- package/src/heartbeat/heartbeat-run-store.ts +2 -1
- package/src/heartbeat/heartbeat-service.ts +41 -0
- package/src/home/__tests__/feed-types.test.ts +40 -0
- package/src/home/feed-types.ts +22 -0
- package/src/home/post-connect-feed.ts +1 -0
- package/src/index.ts +18 -1
- package/src/live-voice/__tests__/live-voice-stt.test.ts +57 -0
- package/src/mcp/client.ts +20 -4
- package/src/media/image-credentials.ts +3 -3
- package/src/memory/__tests__/bookmark-crud.test.ts +33 -27
- package/src/memory/__tests__/conversation-queries.test.ts +263 -0
- package/src/memory/__tests__/jobs-worker-v2-graph-trigger-embed.test.ts +113 -0
- package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +119 -14
- package/src/memory/__tests__/message-content.test.ts +35 -0
- package/src/memory/bookmark-crud.ts +42 -10
- package/src/memory/context-search/sources/conversations.ts +62 -2
- package/src/memory/context-search/sources/workspace.ts +4 -0
- package/src/memory/conversation-crud.ts +63 -19
- package/src/memory/conversation-queries.ts +110 -10
- package/src/memory/db-init.ts +6 -0
- package/src/memory/delivery-crud.ts +152 -5
- package/src/memory/embedding-backend.ts +4 -4
- package/src/memory/external-conversation-store.ts +66 -5
- package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +66 -9
- package/src/memory/graph/conversation-graph-memory.ts +31 -15
- package/src/memory/graph/tools.ts +3 -3
- package/src/memory/indexer.ts +34 -29
- package/src/memory/jobs/__tests__/embed-concept-page.test.ts +73 -0
- package/src/memory/jobs/embed-concept-page.ts +20 -11
- package/src/memory/jobs-worker.ts +6 -1
- package/src/memory/llm-request-log-source-clickhouse.ts +17 -10
- package/src/memory/llm-request-log-source.ts +19 -52
- package/src/memory/llm-usage-store.ts +125 -5
- package/src/memory/memory-retrospective-startup-cleanup.ts +72 -5
- package/src/memory/message-content.ts +1 -1
- package/src/memory/migrations/109-external-conversation-bindings.ts +15 -4
- package/src/memory/migrations/229-delete-private-conversations.test.ts +38 -1
- package/src/memory/migrations/229-delete-private-conversations.ts +7 -0
- package/src/memory/migrations/247-external-conversation-binding-thread-id.ts +78 -0
- package/src/memory/migrations/248-create-onboarding-events.ts +21 -0
- package/src/memory/migrations/249-normalize-slack-external-content.ts +240 -0
- package/src/memory/migrations/index.ts +6 -0
- package/src/memory/migrations/registry.ts +8 -0
- package/src/memory/onboarding-events-store.ts +106 -0
- package/src/memory/schema/bookmarks.ts +0 -2
- package/src/memory/schema/calls.ts +1 -0
- package/src/memory/schema/inference.ts +1 -3
- package/src/memory/schema/infrastructure.ts +12 -0
- package/src/memory/turn-events-store.ts +127 -2
- package/src/memory/v2/__tests__/activation.test.ts +0 -8
- package/src/memory/v2/__tests__/injection.test.ts +98 -8
- package/src/memory/v2/__tests__/migration.test.ts +87 -0
- package/src/memory/v2/__tests__/page-index.test.ts +83 -0
- package/src/memory/v2/__tests__/prompts-router.test.ts +58 -6
- package/src/memory/v2/__tests__/qdrant.test.ts +66 -3
- package/src/memory/v2/__tests__/router.test.ts +15 -0
- package/src/memory/v2/__tests__/skill-store.test.ts +387 -8
- package/src/memory/v2/injection.ts +32 -6
- package/src/memory/v2/migration.ts +49 -19
- package/src/memory/v2/page-index.ts +35 -5
- package/src/memory/v2/prompts/router.ts +11 -8
- package/src/memory/v2/prompts/sweep.ts +2 -2
- package/src/memory/v2/qdrant.ts +135 -7
- package/src/memory/v2/router.ts +9 -8
- package/src/memory/v2/skill-store.ts +120 -35
- package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +45 -5
- package/src/messaging/providers/slack/__tests__/download.test.ts +231 -0
- package/src/messaging/providers/slack/adapter.ts +43 -5
- package/src/messaging/providers/slack/client.ts +27 -0
- package/src/messaging/providers/slack/deep-link.ts +65 -0
- package/src/messaging/providers/slack/download.ts +104 -0
- package/src/messaging/providers/slack/message-metadata.test.ts +32 -0
- package/src/messaging/providers/slack/message-metadata.ts +27 -0
- package/src/messaging/providers/slack/render-transcript.test.ts +134 -0
- package/src/messaging/providers/slack/render-transcript.ts +69 -5
- package/src/messaging/providers/slack/types.ts +20 -1
- package/src/notifications/conversation-pairing.ts +2 -1
- package/src/notifications/decision-engine.ts +2 -1
- package/src/notifications/emit-signal.ts +20 -1
- package/src/notifications/home-feed-side-effect.ts +54 -0
- package/src/notifications/signal.ts +3 -1
- package/src/oauth/connection-resolver.ts +8 -4
- package/src/oauth/platform-connection.ts +6 -2
- package/src/oauth/seed-providers.ts +10 -1
- package/src/permissions/checker.ts +2 -0
- package/src/permissions/ipc-risk-types.ts +1 -0
- package/src/permissions/question-prompter.test.ts +416 -0
- package/src/permissions/question-prompter.ts +294 -0
- package/src/platform/client.test.ts +1 -1
- package/src/platform/client.ts +1 -1
- package/src/plugin-api/constants.ts +26 -0
- package/src/plugin-api/index.ts +34 -1
- package/src/plugin-api/types.ts +104 -22
- package/src/plugins/defaults/circuit-breaker.ts +0 -5
- package/src/plugins/defaults/compaction.ts +0 -4
- package/src/plugins/defaults/empty-response.ts +0 -2
- package/src/plugins/defaults/history-repair.ts +0 -2
- package/src/plugins/defaults/injectors.ts +36 -3
- package/src/plugins/defaults/llm-call.ts +0 -2
- package/src/plugins/defaults/memory-retrieval.ts +0 -1
- package/src/plugins/defaults/overflow-reduce.ts +0 -1
- package/src/plugins/defaults/persistence.ts +0 -2
- package/src/plugins/defaults/title-generate.ts +0 -5
- package/src/plugins/defaults/token-estimate.ts +0 -2
- package/src/plugins/defaults/tool-error.ts +0 -7
- package/src/plugins/defaults/tool-execute.ts +0 -2
- package/src/plugins/defaults/tool-result-truncate.ts +0 -4
- package/src/plugins/ensure-plugin-api-shim.ts +96 -0
- package/src/plugins/external-api.ts +104 -0
- package/src/plugins/external-plugin-loader.ts +105 -32
- package/src/plugins/feature-gate.ts +22 -0
- package/src/plugins/pipeline.ts +37 -0
- package/src/plugins/registry.ts +48 -80
- package/src/plugins/types.ts +31 -26
- package/src/plugins/user-loader.ts +21 -2
- package/src/proactive-artifact/aux-message-injector.ts +11 -0
- package/src/proactive-artifact/job.test.ts +37 -5
- package/src/prompts/__tests__/system-prompt.test.ts +12 -0
- package/src/prompts/__tests__/task-progress-hint-section.test.ts +99 -0
- package/src/prompts/normalize-onboarding.ts +27 -0
- package/src/prompts/sections.ts +302 -0
- package/src/prompts/system-prompt.ts +63 -166
- package/src/prompts/templates/BOOTSTRAP.md +17 -1
- package/src/prompts/templates/system-sections.ts +173 -0
- package/src/providers/__tests__/inference.test.ts +22 -7
- package/src/providers/anthropic/client.ts +28 -28
- package/src/providers/connection-resolution.ts +7 -0
- package/src/providers/inference/adapter-factory.ts +41 -4
- package/src/providers/inference/connections.ts +74 -29
- package/src/providers/inference/resolve-auth.ts +12 -4
- package/src/providers/model-catalog.ts +294 -12
- package/src/providers/openai/chat-completions-provider.ts +10 -2
- package/src/providers/openrouter/client.ts +7 -0
- package/src/providers/{managed-proxy → platform-proxy}/constants.ts +4 -1
- package/src/providers/{managed-proxy → platform-proxy}/context.ts +3 -3
- package/src/providers/provider-availability.ts +17 -2
- package/src/providers/provider-catalog-visibility.ts +36 -0
- package/src/providers/registry.ts +22 -14
- package/src/providers/retry.ts +47 -1
- package/src/runtime/__tests__/agent-wake.test.ts +152 -0
- package/src/runtime/agent-wake.ts +42 -14
- package/src/runtime/auth/route-policy.ts +8 -1
- package/src/runtime/btw-sidechain.ts +2 -0
- package/src/runtime/http-types.ts +19 -0
- package/src/runtime/migrations/origin-mode.ts +1 -1
- package/src/runtime/pending-interactions.ts +1 -0
- package/src/runtime/routes/__tests__/bookmark-routes.test.ts +17 -0
- package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +5 -1
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +107 -20
- package/src/runtime/routes/__tests__/question-routes.test.ts +395 -0
- package/src/runtime/routes/__tests__/tts-routes.test.ts +64 -1
- package/src/runtime/routes/acp-routes-list.test.ts +143 -0
- package/src/runtime/routes/acp-routes.ts +5 -3
- package/src/runtime/routes/auth-routes.ts +1 -1
- package/src/runtime/routes/bookmark-routes.ts +5 -3
- package/src/runtime/routes/btw-routes.ts +5 -1
- package/src/runtime/routes/channel-availability-routes.ts +121 -0
- package/src/runtime/routes/conversation-cli-routes.ts +44 -3
- package/src/runtime/routes/conversation-list-routes.ts +3 -20
- package/src/runtime/routes/conversation-management-routes.ts +17 -42
- package/src/runtime/routes/conversation-query-routes.ts +40 -35
- package/src/runtime/routes/conversation-routes.ts +90 -11
- package/src/runtime/routes/documents-routes.ts +25 -86
- package/src/runtime/routes/group-routes.ts +5 -0
- package/src/runtime/routes/inbound-conversation.ts +28 -8
- package/src/runtime/routes/inbound-message-handler.ts +236 -41
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +111 -0
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +32 -1
- package/src/runtime/routes/inbound-stages/edit-intercept.ts +17 -4
- package/src/runtime/routes/index.ts +6 -0
- package/src/runtime/routes/inference-profile-session-handler.ts +17 -44
- package/src/runtime/routes/inference-profile-session-reaper.ts +7 -21
- package/src/runtime/routes/inference-provider-connection-routes.ts +65 -21
- package/src/runtime/routes/integrations/slack/share.ts +4 -52
- package/src/runtime/routes/integrations/slack/token.ts +43 -0
- package/src/runtime/routes/integrations/twilio.ts +6 -13
- package/src/runtime/routes/notification-routes.ts +1 -1
- package/src/runtime/routes/oauth-commands-routes.ts +105 -15
- package/src/runtime/routes/oauth-lifecycle-routes.ts +43 -0
- package/src/runtime/routes/question-routes.ts +259 -0
- package/src/runtime/routes/rename-conversation-routes.ts +2 -33
- package/src/runtime/routes/schedule-routes.ts +4 -7
- package/src/runtime/routes/subagents-routes.ts +57 -18
- package/src/runtime/routes/telemetry-routes.ts +27 -0
- package/src/runtime/routes/tts-routes.ts +27 -2
- package/src/runtime/routes/workspace-routes.test.ts +43 -0
- package/src/runtime/routes/workspace-routes.ts +28 -0
- package/src/runtime/services/conversation-serializer.ts +39 -7
- package/src/runtime/sync/resource-sync-events.ts +93 -1
- package/src/schedule/schedule-store.ts +27 -2
- package/src/schedule/scheduler.ts +9 -1
- package/src/security/__tests__/untrusted-content.test.ts +86 -0
- package/src/security/untrusted-content.ts +93 -8
- package/src/skills/catalog-files.ts +1 -1
- package/src/skills/catalog-install.ts +233 -116
- package/src/skills/clawhub.ts +70 -13
- package/src/skills/managed-store.ts +4 -119
- package/src/skills/skillssh-registry.ts +27 -48
- package/src/subagent/manager.ts +15 -7
- package/src/telemetry/types.ts +113 -1
- package/src/telemetry/usage-telemetry-reporter.test.ts +312 -5
- package/src/telemetry/usage-telemetry-reporter.ts +113 -7
- package/src/tools/apps/executors.ts +58 -7
- package/src/tools/ask-question/ask-question-tool.test.ts +509 -0
- package/src/tools/ask-question/ask-question-tool.ts +304 -0
- package/src/tools/browser/browser-execution.ts +15 -11
- package/src/tools/computer-use/definitions.ts +3 -3
- package/src/tools/credentials/vault.ts +1 -1
- package/src/tools/document/document-tool.ts +124 -1
- package/src/tools/filesystem/edit.ts +1 -1
- package/src/tools/filesystem/list.ts +1 -1
- package/src/tools/filesystem/read.ts +1 -1
- package/src/tools/filesystem/write.ts +5 -2
- package/src/tools/host-filesystem/transfer.ts +1 -1
- package/src/tools/host-terminal/host-shell.ts +1 -1
- package/src/tools/permission-checker.ts +1 -1
- package/src/tools/registry.ts +17 -7
- package/src/tools/schedule/create.ts +2 -2
- package/src/tools/schema-transforms.ts +7 -2
- package/src/tools/side-effects.ts +1 -0
- package/src/tools/skills/delete-managed.ts +4 -4
- package/src/tools/skills/execute.ts +1 -1
- package/src/tools/skills/scaffold-managed.ts +3 -2
- package/src/tools/subagent/notify-parent.ts +1 -1
- package/src/tools/system/request-permission.ts +2 -2
- package/src/tools/terminal/safe-env.ts +60 -1
- package/src/tools/tool-manifest.ts +2 -0
- package/src/tools/types.ts +72 -21
- package/src/tools/ui-surface/definitions.ts +6 -5
- package/src/tts/__tests__/provider-adapters.test.ts +76 -2
- package/src/tts/providers/elevenlabs-provider.ts +75 -1
- package/src/types/onboarding-context.ts +2 -0
- package/src/util/errors.ts +17 -0
- package/src/util/platform.ts +10 -0
- package/src/watcher/__tests__/engine.test.ts +22 -0
- package/src/watcher/engine.ts +6 -2
- package/src/workspace/migrations/057-repair-stale-gemini-model-ids.ts +80 -15
- package/src/workspace/migrations/072-seed-reply-suggestion-callsite.ts +35 -22
- package/src/workspace/migrations/073-repair-recall-callsite-empty-profile.ts +3 -1
- package/src/workspace/migrations/083-system-prompt-prefix-to-file.ts +191 -0
- package/src/workspace/migrations/084-remove-legacy-skills-index.ts +276 -0
- package/src/workspace/migrations/085-memory-v2-bm25-b-reembed-disabled-v2-pages.ts +137 -0
- package/src/workspace/migrations/086-revert-stale-gemini-mis-rewrites.ts +198 -0
- package/src/workspace/migrations/registry.ts +8 -0
- package/src/workspace/migrations/runner.ts +39 -9
- package/src/workspace/migrations/types.ts +4 -0
- package/examples/plugins/echo/bun.lock +0 -25
- package/src/__tests__/context-window-manager.test.ts +0 -2481
- package/src/context/__tests__/compact-prompt.test.ts +0 -63
- package/src/context/prompts/compact.md +0 -26
- package/src/prompts/__tests__/build-cli-reference-section.test.ts +0 -37
- /package/src/__tests__/{secret-routes-managed-proxy.test.ts → secret-routes-platform-proxy.test.ts} +0 -0
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
getMessages,
|
|
12
12
|
type MessageRow,
|
|
13
13
|
} from "../../memory/conversation-crud.js";
|
|
14
|
+
import { getConversationUsageTotals } from "../../memory/llm-usage-store.js";
|
|
14
15
|
import { getSubagentManager } from "../../subagent/index.js";
|
|
15
16
|
import { getLogger } from "../../util/logger.js";
|
|
16
17
|
import { BadRequestError, NotFoundError } from "./errors.js";
|
|
@@ -29,6 +30,7 @@ function isRecord(value: unknown): value is Record<string, unknown> {
|
|
|
29
30
|
export interface SubagentDetailResult {
|
|
30
31
|
subagentId: string;
|
|
31
32
|
objective?: string;
|
|
33
|
+
usage?: { inputTokens: number; outputTokens: number; estimatedCost: number };
|
|
32
34
|
events: Array<{
|
|
33
35
|
type: string;
|
|
34
36
|
content: string;
|
|
@@ -94,7 +96,11 @@ export function parseSubagentMessages(
|
|
|
94
96
|
typeof block.text === "string"
|
|
95
97
|
) {
|
|
96
98
|
events.push({ type: "text", content: block.text, messageId: m.id });
|
|
97
|
-
} else if (
|
|
99
|
+
} else if (
|
|
100
|
+
block.type === "tool_use" ||
|
|
101
|
+
block.type === "server_tool_use" ||
|
|
102
|
+
block.type === "mcp_tool_use"
|
|
103
|
+
) {
|
|
98
104
|
const name = typeof block.name === "string" ? block.name : "unknown";
|
|
99
105
|
const input = isRecord(block.input)
|
|
100
106
|
? (block.input as Record<string, unknown>)
|
|
@@ -106,7 +112,11 @@ export function parseSubagentMessages(
|
|
|
106
112
|
toolName: name,
|
|
107
113
|
});
|
|
108
114
|
if (id) pendingTools.set(id, name);
|
|
109
|
-
} else if (
|
|
115
|
+
} else if (
|
|
116
|
+
block.type === "tool_result" ||
|
|
117
|
+
block.type === "web_search_tool_result" ||
|
|
118
|
+
block.type === "mcp_tool_result"
|
|
119
|
+
) {
|
|
110
120
|
const toolUseId =
|
|
111
121
|
typeof block.tool_use_id === "string" ? block.tool_use_id : "";
|
|
112
122
|
const resultContent =
|
|
@@ -114,13 +124,18 @@ export function parseSubagentMessages(
|
|
|
114
124
|
? block.content
|
|
115
125
|
: Array.isArray(block.content)
|
|
116
126
|
? (block.content as unknown[])
|
|
117
|
-
.filter(
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
127
|
+
.filter((b): b is Record<string, unknown> => isRecord(b))
|
|
128
|
+
.map((b) => {
|
|
129
|
+
if (b.type === "text" && typeof b.text === "string")
|
|
130
|
+
return b.text;
|
|
131
|
+
if (
|
|
132
|
+
b.type === "web_search_result" &&
|
|
133
|
+
typeof b.title === "string"
|
|
134
|
+
)
|
|
135
|
+
return `${b.title}\n${typeof b.url === "string" ? b.url : ""}`;
|
|
136
|
+
return null;
|
|
137
|
+
})
|
|
138
|
+
.filter((s): s is string => s != null)
|
|
124
139
|
.join("\n")
|
|
125
140
|
: "";
|
|
126
141
|
const isError = block.is_error === true;
|
|
@@ -142,7 +157,30 @@ function getSubagentDetail(
|
|
|
142
157
|
subagentId: string,
|
|
143
158
|
conversationId: string,
|
|
144
159
|
): SubagentDetailResult {
|
|
145
|
-
|
|
160
|
+
const messages = getMessages(conversationId);
|
|
161
|
+
log.info(
|
|
162
|
+
{
|
|
163
|
+
subagentId,
|
|
164
|
+
conversationId,
|
|
165
|
+
messageCount: messages.length,
|
|
166
|
+
roles: messages.map((m) => m.role),
|
|
167
|
+
},
|
|
168
|
+
"getSubagentDetail: raw messages from DB",
|
|
169
|
+
);
|
|
170
|
+
const result = parseSubagentMessages(subagentId, messages);
|
|
171
|
+
log.info(
|
|
172
|
+
{
|
|
173
|
+
subagentId,
|
|
174
|
+
eventCount: result.events.length,
|
|
175
|
+
eventTypes: result.events.map((e) => `${e.type}:${e.toolName ?? ""}`),
|
|
176
|
+
},
|
|
177
|
+
"getSubagentDetail: parsed events",
|
|
178
|
+
);
|
|
179
|
+
const usage = getConversationUsageTotals(conversationId);
|
|
180
|
+
if (usage.inputTokens > 0 || usage.outputTokens > 0) {
|
|
181
|
+
result.usage = usage;
|
|
182
|
+
}
|
|
183
|
+
return result;
|
|
146
184
|
}
|
|
147
185
|
|
|
148
186
|
// ---------------------------------------------------------------------------
|
|
@@ -168,14 +206,19 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
168
206
|
responseBody: z.object({
|
|
169
207
|
subagentId: z.string(),
|
|
170
208
|
objective: z.string(),
|
|
209
|
+
usage: z
|
|
210
|
+
.object({
|
|
211
|
+
inputTokens: z.number(),
|
|
212
|
+
outputTokens: z.number(),
|
|
213
|
+
estimatedCost: z.number(),
|
|
214
|
+
})
|
|
215
|
+
.optional(),
|
|
171
216
|
events: z.array(z.unknown()).describe("Subagent event objects"),
|
|
172
217
|
}),
|
|
173
218
|
handler: ({ pathParams, queryParams }) => {
|
|
174
219
|
const conversationId = queryParams?.conversationId;
|
|
175
220
|
if (!conversationId) {
|
|
176
|
-
throw new BadRequestError(
|
|
177
|
-
"conversationId query parameter is required",
|
|
178
|
-
);
|
|
221
|
+
throw new BadRequestError("conversationId query parameter is required");
|
|
179
222
|
}
|
|
180
223
|
|
|
181
224
|
const manager = getSubagentManager();
|
|
@@ -209,11 +252,7 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
209
252
|
}
|
|
210
253
|
|
|
211
254
|
const manager = getSubagentManager();
|
|
212
|
-
const aborted = manager.abort(
|
|
213
|
-
pathParams!.id,
|
|
214
|
-
() => {},
|
|
215
|
-
conversationId,
|
|
216
|
-
);
|
|
255
|
+
const aborted = manager.abort(pathParams!.id, () => {}, conversationId);
|
|
217
256
|
|
|
218
257
|
if (!aborted) {
|
|
219
258
|
log.warn(
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import { z } from "zod";
|
|
8
8
|
|
|
9
9
|
import { recordLifecycleEvent } from "../../memory/lifecycle-events-store.js";
|
|
10
|
+
import { getUsageTelemetryReporter } from "../../telemetry/usage-telemetry-reporter.js";
|
|
10
11
|
import { getLogger } from "../../util/logger.js";
|
|
11
12
|
import { BadRequestError } from "./errors.js";
|
|
12
13
|
import type { RouteDefinition, RouteHandlerArgs } from "./types.js";
|
|
@@ -32,6 +33,15 @@ function handleRecordLifecycleEvent({ body }: RouteHandlerArgs) {
|
|
|
32
33
|
return { id: event.id, event_name: event.eventName };
|
|
33
34
|
}
|
|
34
35
|
|
|
36
|
+
async function handleTelemetryFlush() {
|
|
37
|
+
const reporter = getUsageTelemetryReporter();
|
|
38
|
+
if (!reporter) {
|
|
39
|
+
return { flushed: false, reason: "disabled" };
|
|
40
|
+
}
|
|
41
|
+
await reporter.flush();
|
|
42
|
+
return { flushed: true };
|
|
43
|
+
}
|
|
44
|
+
|
|
35
45
|
export const ROUTES: RouteDefinition[] = [
|
|
36
46
|
{
|
|
37
47
|
operationId: "telemetry_lifecycle",
|
|
@@ -58,4 +68,21 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
58
68
|
]),
|
|
59
69
|
handler: handleRecordLifecycleEvent,
|
|
60
70
|
},
|
|
71
|
+
{
|
|
72
|
+
operationId: "telemetry_flush",
|
|
73
|
+
endpoint: "telemetry/flush",
|
|
74
|
+
method: "POST",
|
|
75
|
+
summary: "Flush pending telemetry events",
|
|
76
|
+
description:
|
|
77
|
+
"Force-flush all pending usage, turn, and lifecycle telemetry events to the platform.",
|
|
78
|
+
tags: ["telemetry"],
|
|
79
|
+
responseBody: z.union([
|
|
80
|
+
z.object({ flushed: z.literal(true) }),
|
|
81
|
+
z.object({
|
|
82
|
+
flushed: z.literal(false),
|
|
83
|
+
reason: z.string(),
|
|
84
|
+
}),
|
|
85
|
+
]),
|
|
86
|
+
handler: handleTelemetryFlush,
|
|
87
|
+
},
|
|
61
88
|
];
|
|
@@ -94,10 +94,35 @@ async function doSynthesize(
|
|
|
94
94
|
throw new ServiceUnavailableError("TTS provider is not configured");
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
-
throw new BadGatewayError(
|
|
97
|
+
throw new BadGatewayError(formatTtsFailureMessage(err));
|
|
98
98
|
}
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
+
/**
|
|
102
|
+
* Build a user-facing error message for a failed TTS synthesis, embedding the
|
|
103
|
+
* upstream provider's message when available.
|
|
104
|
+
*
|
|
105
|
+
* The provider adapters surface a clean upstream message (e.g. "Free users
|
|
106
|
+
* cannot use library voices via the API…") and `synthesize-text` already
|
|
107
|
+
* prefixes those with `"TTS synthesis failed (provider: <id>): "`. We pass
|
|
108
|
+
* pre-prefixed messages through verbatim and only add the base prefix for
|
|
109
|
+
* raw provider errors, so users never see double- or triple-prefixed
|
|
110
|
+
* messages on the desktop / channels.
|
|
111
|
+
*
|
|
112
|
+
* Exported for unit testing.
|
|
113
|
+
*/
|
|
114
|
+
export function formatTtsFailureMessage(err: unknown): string {
|
|
115
|
+
const base = "TTS synthesis failed";
|
|
116
|
+
if (err instanceof Error && err.message && err.message.trim()) {
|
|
117
|
+
const trimmed = err.message.trim();
|
|
118
|
+
if (/^TTS synthesis failed\b/i.test(trimmed)) {
|
|
119
|
+
return trimmed;
|
|
120
|
+
}
|
|
121
|
+
return `${base}: ${trimmed}`;
|
|
122
|
+
}
|
|
123
|
+
return base;
|
|
124
|
+
}
|
|
125
|
+
|
|
101
126
|
// ---------------------------------------------------------------------------
|
|
102
127
|
// Response headers — shared by both routes
|
|
103
128
|
// ---------------------------------------------------------------------------
|
|
@@ -191,7 +216,7 @@ async function handleSynthesizeCliTts({ body }: RouteHandlerArgs) {
|
|
|
191
216
|
throw new ServiceUnavailableError("TTS provider is not configured");
|
|
192
217
|
}
|
|
193
218
|
|
|
194
|
-
throw new BadGatewayError(
|
|
219
|
+
throw new BadGatewayError(formatTtsFailureMessage(err));
|
|
195
220
|
}
|
|
196
221
|
}
|
|
197
222
|
|
|
@@ -9,6 +9,10 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
|
9
9
|
import { join } from "node:path";
|
|
10
10
|
import { beforeAll, describe, expect, test } from "bun:test";
|
|
11
11
|
|
|
12
|
+
import { SYNC_TAGS } from "../../daemon/message-types/sync.js";
|
|
13
|
+
import type { AssistantEvent } from "../assistant-event.js";
|
|
14
|
+
import { assistantEventHub } from "../assistant-event-hub.js";
|
|
15
|
+
|
|
12
16
|
// ---------------------------------------------------------------------------
|
|
13
17
|
// Create a temp workspace directory for isolation
|
|
14
18
|
// ---------------------------------------------------------------------------
|
|
@@ -59,6 +63,15 @@ function getRoute(operationId: string): RouteDefinition {
|
|
|
59
63
|
return route;
|
|
60
64
|
}
|
|
61
65
|
|
|
66
|
+
async function waitFor(predicate: () => boolean): Promise<void> {
|
|
67
|
+
const deadline = Date.now() + 500;
|
|
68
|
+
while (Date.now() < deadline) {
|
|
69
|
+
if (predicate()) return;
|
|
70
|
+
await new Promise((resolve) => setTimeout(resolve, 5));
|
|
71
|
+
}
|
|
72
|
+
throw new Error("Timed out waiting for workspace route event");
|
|
73
|
+
}
|
|
74
|
+
|
|
62
75
|
// ===========================================================================
|
|
63
76
|
// resolveWorkspacePath
|
|
64
77
|
// ===========================================================================
|
|
@@ -492,6 +505,36 @@ describe("POST /v1/workspace/write", () => {
|
|
|
492
505
|
handler({ body: { path: "subdir", content: "should fail" } }),
|
|
493
506
|
).toThrow(ConflictError);
|
|
494
507
|
});
|
|
508
|
+
|
|
509
|
+
test("publishes sounds sync events when writing sounds config", async () => {
|
|
510
|
+
const received: AssistantEvent[] = [];
|
|
511
|
+
const subscription = assistantEventHub.subscribe({
|
|
512
|
+
type: "process",
|
|
513
|
+
callback: (event) => {
|
|
514
|
+
received.push(event);
|
|
515
|
+
},
|
|
516
|
+
});
|
|
517
|
+
|
|
518
|
+
try {
|
|
519
|
+
handler({
|
|
520
|
+
body: {
|
|
521
|
+
path: "data/sounds/config.json",
|
|
522
|
+
content: "{}",
|
|
523
|
+
},
|
|
524
|
+
});
|
|
525
|
+
await waitFor(() => received.length === 2);
|
|
526
|
+
expect(received.map((event) => event.message.type)).toEqual([
|
|
527
|
+
"sounds_config_updated",
|
|
528
|
+
"sync_changed",
|
|
529
|
+
]);
|
|
530
|
+
expect(received[1]!.message).toEqual({
|
|
531
|
+
type: "sync_changed",
|
|
532
|
+
tags: [SYNC_TAGS.assistantSounds],
|
|
533
|
+
});
|
|
534
|
+
} finally {
|
|
535
|
+
subscription.dispose();
|
|
536
|
+
}
|
|
537
|
+
});
|
|
495
538
|
});
|
|
496
539
|
|
|
497
540
|
// ===========================================================================
|
|
@@ -18,6 +18,7 @@ import { basename, dirname, join } from "node:path";
|
|
|
18
18
|
import { z } from "zod";
|
|
19
19
|
|
|
20
20
|
import { getWorkspaceDir } from "../../util/platform.js";
|
|
21
|
+
import { publishSoundsConfigUpdated } from "../sync/resource-sync-events.js";
|
|
21
22
|
import {
|
|
22
23
|
BadRequestError,
|
|
23
24
|
ConflictError,
|
|
@@ -41,6 +42,29 @@ interface TreeEntry {
|
|
|
41
42
|
modifiedAt: string;
|
|
42
43
|
}
|
|
43
44
|
|
|
45
|
+
const SOUNDS_WORKSPACE_PATH = "data/sounds";
|
|
46
|
+
|
|
47
|
+
function normaliseWorkspacePathForSync(path: string): string {
|
|
48
|
+
return path
|
|
49
|
+
.split(/[\\/]+/)
|
|
50
|
+
.filter((part) => part.length > 0)
|
|
51
|
+
.join("/");
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function isSoundsWorkspacePath(path: string): boolean {
|
|
55
|
+
const normalized = normaliseWorkspacePathForSync(path);
|
|
56
|
+
return (
|
|
57
|
+
normalized === SOUNDS_WORKSPACE_PATH ||
|
|
58
|
+
normalized.startsWith(`${SOUNDS_WORKSPACE_PATH}/`)
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function publishSoundsConfigUpdatedForPaths(paths: string[]): void {
|
|
63
|
+
if (paths.some(isSoundsWorkspacePath)) {
|
|
64
|
+
publishSoundsConfigUpdated();
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
44
68
|
// ---------------------------------------------------------------------------
|
|
45
69
|
// GET /v1/workspace/tree — list directory contents
|
|
46
70
|
// ---------------------------------------------------------------------------
|
|
@@ -268,6 +292,7 @@ function handleWorkspaceWrite({ body }: RouteHandlerArgs) {
|
|
|
268
292
|
|
|
269
293
|
mkdirSync(dirname(resolved), { recursive: true });
|
|
270
294
|
writeFileSync(resolved, buffer);
|
|
295
|
+
publishSoundsConfigUpdatedForPaths([path]);
|
|
271
296
|
|
|
272
297
|
return { path, size: buffer.byteLength };
|
|
273
298
|
}
|
|
@@ -295,6 +320,7 @@ function handleWorkspaceMkdir({ body }: RouteHandlerArgs) {
|
|
|
295
320
|
}
|
|
296
321
|
|
|
297
322
|
mkdirSync(resolved, { recursive: true });
|
|
323
|
+
publishSoundsConfigUpdatedForPaths([path]);
|
|
298
324
|
return { path };
|
|
299
325
|
}
|
|
300
326
|
|
|
@@ -334,6 +360,7 @@ function handleWorkspaceRename({ body }: RouteHandlerArgs) {
|
|
|
334
360
|
|
|
335
361
|
mkdirSync(dirname(resolvedNew), { recursive: true });
|
|
336
362
|
renameSync(resolvedOld, resolvedNew);
|
|
363
|
+
publishSoundsConfigUpdatedForPaths([oldPath, newPath]);
|
|
337
364
|
return { oldPath, newPath };
|
|
338
365
|
}
|
|
339
366
|
|
|
@@ -361,6 +388,7 @@ function handleWorkspaceDelete({ body }: RouteHandlerArgs) {
|
|
|
361
388
|
}
|
|
362
389
|
|
|
363
390
|
rmSync(resolved, { recursive: true, force: true });
|
|
391
|
+
publishSoundsConfigUpdatedForPaths([path]);
|
|
364
392
|
return { success: true };
|
|
365
393
|
}
|
|
366
394
|
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { parseChannelId } from "../../channels/types.js";
|
|
11
|
+
import { getConfig } from "../../config/loader.js";
|
|
11
12
|
import { normalizeConversationType } from "../../daemon/message-types/shared.js";
|
|
12
13
|
import {
|
|
13
14
|
type AttentionState,
|
|
@@ -22,6 +23,7 @@ import {
|
|
|
22
23
|
} from "../../memory/conversation-crud.js";
|
|
23
24
|
import type { ExternalConversationBinding } from "../../memory/external-conversation-store.js";
|
|
24
25
|
import { getBindingsForConversations } from "../../memory/external-conversation-store.js";
|
|
26
|
+
import { buildSlackMessageDeepLinks } from "../../messaging/providers/slack/deep-link.js";
|
|
25
27
|
|
|
26
28
|
// ---------------------------------------------------------------------------
|
|
27
29
|
// Helpers
|
|
@@ -88,6 +90,42 @@ function buildForkParent(
|
|
|
88
90
|
};
|
|
89
91
|
}
|
|
90
92
|
|
|
93
|
+
function buildChannelBinding(binding: ExternalConversationBinding) {
|
|
94
|
+
const slackConfig =
|
|
95
|
+
binding.sourceChannel === "slack" && binding.externalThreadId
|
|
96
|
+
? getConfig().slack
|
|
97
|
+
: undefined;
|
|
98
|
+
const slackThreadLink =
|
|
99
|
+
slackConfig && binding.externalThreadId
|
|
100
|
+
? buildSlackMessageDeepLinks({
|
|
101
|
+
teamId: slackConfig.teamId,
|
|
102
|
+
teamUrl: slackConfig.teamUrl,
|
|
103
|
+
channelId: binding.externalChatId,
|
|
104
|
+
messageTs: binding.externalThreadId,
|
|
105
|
+
})
|
|
106
|
+
: undefined;
|
|
107
|
+
const slackThread =
|
|
108
|
+
binding.sourceChannel === "slack" && binding.externalThreadId
|
|
109
|
+
? {
|
|
110
|
+
channelId: binding.externalChatId,
|
|
111
|
+
threadTs: binding.externalThreadId,
|
|
112
|
+
...(slackThreadLink ? { link: slackThreadLink } : {}),
|
|
113
|
+
}
|
|
114
|
+
: undefined;
|
|
115
|
+
|
|
116
|
+
return {
|
|
117
|
+
sourceChannel: binding.sourceChannel,
|
|
118
|
+
externalChatId: binding.externalChatId,
|
|
119
|
+
...(binding.externalThreadId
|
|
120
|
+
? { externalThreadId: binding.externalThreadId }
|
|
121
|
+
: {}),
|
|
122
|
+
externalUserId: binding.externalUserId,
|
|
123
|
+
displayName: binding.displayName,
|
|
124
|
+
username: binding.username,
|
|
125
|
+
...(slackThread ? { slackThread } : {}),
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
91
129
|
export function serializeConversationSummary(params: {
|
|
92
130
|
conversation: ConversationRow;
|
|
93
131
|
binding?: ExternalConversationBinding | null;
|
|
@@ -118,13 +156,7 @@ export function serializeConversationSummary(params: {
|
|
|
118
156
|
: {}),
|
|
119
157
|
...(binding
|
|
120
158
|
? {
|
|
121
|
-
channelBinding:
|
|
122
|
-
sourceChannel: binding.sourceChannel,
|
|
123
|
-
externalChatId: binding.externalChatId,
|
|
124
|
-
externalUserId: binding.externalUserId,
|
|
125
|
-
displayName: binding.displayName,
|
|
126
|
-
username: binding.username,
|
|
127
|
-
},
|
|
159
|
+
channelBinding: buildChannelBinding(binding),
|
|
128
160
|
}
|
|
129
161
|
: {}),
|
|
130
162
|
...(originChannel ? { conversationOriginChannel: originChannel } : {}),
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import type { IdentityFields } from "../../daemon/handlers/identity.js";
|
|
2
|
-
import {
|
|
2
|
+
import type { ConversationListInvalidatedReason } from "../../daemon/message-types/conversations.js";
|
|
3
|
+
import {
|
|
4
|
+
conversationMessagesSyncTag,
|
|
5
|
+
conversationMetadataSyncTag,
|
|
6
|
+
SYNC_TAGS,
|
|
7
|
+
} from "../../daemon/message-types/sync.js";
|
|
3
8
|
import { getAvatarImagePath } from "../../util/platform.js";
|
|
4
9
|
import { broadcastMessage } from "../assistant-event-hub.js";
|
|
5
10
|
import { publishSyncInvalidation } from "./sync-publisher.js";
|
|
@@ -23,3 +28,90 @@ export function publishIdentityChanged(fields: IdentityFields): void {
|
|
|
23
28
|
});
|
|
24
29
|
void publishSyncInvalidation([SYNC_TAGS.assistantIdentity]);
|
|
25
30
|
}
|
|
31
|
+
|
|
32
|
+
export function publishConfigChanged(): void {
|
|
33
|
+
broadcastMessage({ type: "config_changed" });
|
|
34
|
+
void publishSyncInvalidation([SYNC_TAGS.assistantConfig]);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function publishSoundsConfigUpdated(): void {
|
|
38
|
+
broadcastMessage({ type: "sounds_config_updated" });
|
|
39
|
+
void publishSyncInvalidation([SYNC_TAGS.assistantSounds]);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function publishSchedulesChanged(): void {
|
|
43
|
+
void publishSyncInvalidation([SYNC_TAGS.assistantSchedules]);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function publishConversationListChanged(
|
|
47
|
+
reason: ConversationListInvalidatedReason,
|
|
48
|
+
): void {
|
|
49
|
+
broadcastMessage({
|
|
50
|
+
type: "conversation_list_invalidated",
|
|
51
|
+
reason,
|
|
52
|
+
});
|
|
53
|
+
void publishSyncInvalidation([SYNC_TAGS.conversationsList]);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function publishConversationMessagesChanged(
|
|
57
|
+
conversationId: string,
|
|
58
|
+
): void {
|
|
59
|
+
void publishSyncInvalidation([conversationMessagesSyncTag(conversationId)]);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function publishConversationListAndMetadataChanged(
|
|
63
|
+
reason: ConversationListInvalidatedReason,
|
|
64
|
+
conversationIds: string | string[],
|
|
65
|
+
): void {
|
|
66
|
+
const ids = Array.isArray(conversationIds)
|
|
67
|
+
? conversationIds
|
|
68
|
+
: [conversationIds];
|
|
69
|
+
broadcastMessage({
|
|
70
|
+
type: "conversation_list_invalidated",
|
|
71
|
+
reason,
|
|
72
|
+
});
|
|
73
|
+
void publishSyncInvalidation([
|
|
74
|
+
SYNC_TAGS.conversationsList,
|
|
75
|
+
...ids.map((conversationId) => conversationMetadataSyncTag(conversationId)),
|
|
76
|
+
]);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function publishConversationTitleChanged(
|
|
80
|
+
conversationId: string,
|
|
81
|
+
title: string,
|
|
82
|
+
): void {
|
|
83
|
+
broadcastMessage(
|
|
84
|
+
{
|
|
85
|
+
type: "conversation_title_updated",
|
|
86
|
+
conversationId,
|
|
87
|
+
title,
|
|
88
|
+
},
|
|
89
|
+
conversationId,
|
|
90
|
+
);
|
|
91
|
+
void publishSyncInvalidation([
|
|
92
|
+
SYNC_TAGS.conversationsList,
|
|
93
|
+
conversationMetadataSyncTag(conversationId),
|
|
94
|
+
]);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function publishConversationInferenceProfileChanged(params: {
|
|
98
|
+
conversationId: string;
|
|
99
|
+
profile: string | null;
|
|
100
|
+
sessionId?: string | null;
|
|
101
|
+
expiresAt?: number | null;
|
|
102
|
+
}): void {
|
|
103
|
+
broadcastMessage(
|
|
104
|
+
{
|
|
105
|
+
type: "conversation_inference_profile_updated",
|
|
106
|
+
conversationId: params.conversationId,
|
|
107
|
+
profile: params.profile,
|
|
108
|
+
sessionId: params.sessionId,
|
|
109
|
+
expiresAt: params.expiresAt,
|
|
110
|
+
},
|
|
111
|
+
params.conversationId,
|
|
112
|
+
);
|
|
113
|
+
void publishSyncInvalidation([
|
|
114
|
+
SYNC_TAGS.conversationsList,
|
|
115
|
+
conversationMetadataSyncTag(params.conversationId),
|
|
116
|
+
]);
|
|
117
|
+
}
|
|
@@ -5,6 +5,7 @@ import { v4 as uuid } from "uuid";
|
|
|
5
5
|
import { getDb } from "../memory/db-connection.js";
|
|
6
6
|
import { rawChanges } from "../memory/raw-query.js";
|
|
7
7
|
import { scheduleJobs, scheduleRuns } from "../memory/schema.js";
|
|
8
|
+
import { publishSchedulesChanged } from "../runtime/sync/resource-sync-events.js";
|
|
8
9
|
import { getLogger } from "../util/logger.js";
|
|
9
10
|
import {
|
|
10
11
|
computeNextRunAt as computeNextRunAtEngine,
|
|
@@ -14,6 +15,10 @@ import type { ScheduleSyntax } from "./recurrence-types.js";
|
|
|
14
15
|
|
|
15
16
|
const logger = getLogger("schedule-store");
|
|
16
17
|
|
|
18
|
+
function notifySchedulesChanged(): void {
|
|
19
|
+
publishSchedulesChanged();
|
|
20
|
+
}
|
|
21
|
+
|
|
17
22
|
export type ScheduleMode = "notify" | "execute" | "script" | "wake";
|
|
18
23
|
export type RoutingIntent = "single_channel" | "multi_channel" | "all_channels";
|
|
19
24
|
export type ScheduleStatus = "active" | "firing" | "fired" | "cancelled";
|
|
@@ -160,6 +165,7 @@ export function createSchedule(params: {
|
|
|
160
165
|
};
|
|
161
166
|
|
|
162
167
|
db.insert(scheduleJobs).values(row).run();
|
|
168
|
+
notifySchedulesChanged();
|
|
163
169
|
return parseJobRow(row);
|
|
164
170
|
}
|
|
165
171
|
|
|
@@ -328,6 +334,7 @@ export function updateSchedule(
|
|
|
328
334
|
}
|
|
329
335
|
|
|
330
336
|
db.update(scheduleJobs).set(set).where(eq(scheduleJobs.id, id)).run();
|
|
337
|
+
notifySchedulesChanged();
|
|
331
338
|
|
|
332
339
|
return getSchedule(id);
|
|
333
340
|
}
|
|
@@ -335,7 +342,9 @@ export function updateSchedule(
|
|
|
335
342
|
export function deleteSchedule(id: string): boolean {
|
|
336
343
|
const db = getDb();
|
|
337
344
|
db.delete(scheduleJobs).where(eq(scheduleJobs.id, id)).run();
|
|
338
|
-
|
|
345
|
+
const deleted = rawChanges() > 0;
|
|
346
|
+
if (deleted) notifySchedulesChanged();
|
|
347
|
+
return deleted;
|
|
339
348
|
}
|
|
340
349
|
|
|
341
350
|
/**
|
|
@@ -466,6 +475,7 @@ export function claimDueSchedules(now: number): ScheduleJob[] {
|
|
|
466
475
|
);
|
|
467
476
|
}
|
|
468
477
|
|
|
478
|
+
if (claimed.length > 0) notifySchedulesChanged();
|
|
469
479
|
return claimed;
|
|
470
480
|
}
|
|
471
481
|
|
|
@@ -484,6 +494,7 @@ export function completeOneShot(id: string): void {
|
|
|
484
494
|
})
|
|
485
495
|
.where(and(eq(scheduleJobs.id, id), eq(scheduleJobs.status, "firing")))
|
|
486
496
|
.run();
|
|
497
|
+
if (rawChanges() > 0) notifySchedulesChanged();
|
|
487
498
|
}
|
|
488
499
|
|
|
489
500
|
/**
|
|
@@ -500,6 +511,7 @@ export function failOneShot(id: string): void {
|
|
|
500
511
|
})
|
|
501
512
|
.where(and(eq(scheduleJobs.id, id), eq(scheduleJobs.status, "firing")))
|
|
502
513
|
.run();
|
|
514
|
+
if (rawChanges() > 0) notifySchedulesChanged();
|
|
503
515
|
}
|
|
504
516
|
|
|
505
517
|
/**
|
|
@@ -510,6 +522,7 @@ export function failOneShot(id: string): void {
|
|
|
510
522
|
export function retryOneShot(id: string): void {
|
|
511
523
|
const db = getDb();
|
|
512
524
|
const now = Date.now();
|
|
525
|
+
let changed = false;
|
|
513
526
|
db.update(scheduleJobs)
|
|
514
527
|
.set({
|
|
515
528
|
status: "active",
|
|
@@ -518,6 +531,8 @@ export function retryOneShot(id: string): void {
|
|
|
518
531
|
})
|
|
519
532
|
.where(and(eq(scheduleJobs.id, id), eq(scheduleJobs.status, "firing")))
|
|
520
533
|
.run();
|
|
534
|
+
changed = rawChanges() > 0;
|
|
535
|
+
if (changed) notifySchedulesChanged();
|
|
521
536
|
}
|
|
522
537
|
|
|
523
538
|
/**
|
|
@@ -537,6 +552,7 @@ export function failOneShotPermanently(id: string): void {
|
|
|
537
552
|
})
|
|
538
553
|
.where(and(eq(scheduleJobs.id, id), eq(scheduleJobs.status, "firing")))
|
|
539
554
|
.run();
|
|
555
|
+
if (rawChanges() > 0) notifySchedulesChanged();
|
|
540
556
|
}
|
|
541
557
|
|
|
542
558
|
/**
|
|
@@ -554,7 +570,9 @@ export function cancelSchedule(id: string): boolean {
|
|
|
554
570
|
})
|
|
555
571
|
.where(and(eq(scheduleJobs.id, id), eq(scheduleJobs.status, "active")))
|
|
556
572
|
.run();
|
|
557
|
-
|
|
573
|
+
const cancelled = rawChanges() > 0;
|
|
574
|
+
if (cancelled) notifySchedulesChanged();
|
|
575
|
+
return cancelled;
|
|
558
576
|
}
|
|
559
577
|
|
|
560
578
|
export function createScheduleRun(
|
|
@@ -625,12 +643,14 @@ export function completeScheduleRun(
|
|
|
625
643
|
})
|
|
626
644
|
.where(eq(scheduleJobs.id, run.jobId))
|
|
627
645
|
.run();
|
|
646
|
+
if (rawChanges() > 0) notifySchedulesChanged();
|
|
628
647
|
}
|
|
629
648
|
} else {
|
|
630
649
|
db.update(scheduleJobs)
|
|
631
650
|
.set({ lastStatus: "ok", retryCount: 0, updatedAt: now })
|
|
632
651
|
.where(eq(scheduleJobs.id, run.jobId))
|
|
633
652
|
.run();
|
|
653
|
+
if (rawChanges() > 0) notifySchedulesChanged();
|
|
634
654
|
}
|
|
635
655
|
}
|
|
636
656
|
|
|
@@ -839,16 +859,20 @@ export function describeCronExpression(expr: string | null): string {
|
|
|
839
859
|
export function scheduleRetry(id: string, nextRetryAt: number): void {
|
|
840
860
|
const db = getDb();
|
|
841
861
|
const now = Date.now();
|
|
862
|
+
let changed = false;
|
|
842
863
|
db.update(scheduleJobs)
|
|
843
864
|
.set({ nextRunAt: nextRetryAt, updatedAt: now })
|
|
844
865
|
.where(eq(scheduleJobs.id, id))
|
|
845
866
|
.run();
|
|
867
|
+
changed = rawChanges() > 0;
|
|
846
868
|
// Revert one-shot status from "firing" to "active" so the scheduler
|
|
847
869
|
// will claim it again when nextRetryAt arrives. No-op for recurring.
|
|
848
870
|
db.update(scheduleJobs)
|
|
849
871
|
.set({ status: "active", updatedAt: now })
|
|
850
872
|
.where(and(eq(scheduleJobs.id, id), eq(scheduleJobs.status, "firing")))
|
|
851
873
|
.run();
|
|
874
|
+
changed = rawChanges() > 0 || changed;
|
|
875
|
+
if (changed) notifySchedulesChanged();
|
|
852
876
|
}
|
|
853
877
|
|
|
854
878
|
/**
|
|
@@ -860,6 +884,7 @@ export function resetRetryCount(id: string): void {
|
|
|
860
884
|
.set({ retryCount: 0, updatedAt: Date.now() })
|
|
861
885
|
.where(eq(scheduleJobs.id, id))
|
|
862
886
|
.run();
|
|
887
|
+
if (rawChanges() > 0) notifySchedulesChanged();
|
|
863
888
|
}
|
|
864
889
|
|
|
865
890
|
/**
|
|
@@ -549,7 +549,15 @@ export async function runScheduleOnce(
|
|
|
549
549
|
});
|
|
550
550
|
},
|
|
551
551
|
});
|
|
552
|
-
conversationId
|
|
552
|
+
// Bootstrap-failure path returns `{ ok: false, conversationId: "" }`.
|
|
553
|
+
// Substitute a sentinel only for failures so the schedule-run DB row
|
|
554
|
+
// carries a recognizable marker. Successful skips (e.g.
|
|
555
|
+
// `pre_first_user_message`) also return `conversationId: ""` but with
|
|
556
|
+
// `ok: true` — keep the empty ID to preserve their skip contract.
|
|
557
|
+
conversationId =
|
|
558
|
+
!result.ok && result.conversationId === ""
|
|
559
|
+
? `bootstrap-error:${job.id}`
|
|
560
|
+
: result.conversationId;
|
|
553
561
|
ok = result.ok;
|
|
554
562
|
errorMsg = result.error?.message;
|
|
555
563
|
}
|