@vellumai/assistant 0.8.3 → 0.8.4
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/docker-entrypoint.sh +0 -1
- package/node_modules/@vellumai/gateway-client/src/types.ts +2 -0
- package/openapi.yaml +610 -16
- package/package.json +1 -1
- package/src/__tests__/agent-loop-exit-reason.test.ts +4 -5
- package/src/__tests__/agent-loop-override-profile.test.ts +1 -1
- package/src/__tests__/agent-loop.test.ts +88 -3
- package/src/__tests__/anthropic-provider.test.ts +272 -0
- package/src/__tests__/approval-cascade.test.ts +1 -1
- package/src/__tests__/background-workers-disk-pressure.test.ts +2 -1
- package/src/__tests__/channel-delivery-store.test.ts +193 -0
- package/src/__tests__/channel-reply-delivery.test.ts +284 -5
- package/src/__tests__/channel-retry-sweep.test.ts +274 -1
- package/src/__tests__/compaction-events.test.ts +1 -1
- package/src/__tests__/compactor-preserved-tail-count.test.ts +110 -0
- package/src/__tests__/config-watcher.test.ts +1 -1
- package/src/__tests__/context-token-estimator.test.ts +91 -1
- package/src/__tests__/conversation-abort-tool-results.test.ts +1 -1
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +54 -3
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +31 -6
- package/src/__tests__/conversation-agent-loop.test.ts +25 -7
- package/src/__tests__/conversation-app-control-lifecycle.test.ts +1 -1
- package/src/__tests__/conversation-clean-command.test.ts +137 -0
- package/src/__tests__/conversation-confirmation-signals.test.ts +1 -1
- package/src/__tests__/conversation-fork-crud.test.ts +161 -0
- package/src/__tests__/conversation-lifecycle.test.ts +1 -1
- package/src/__tests__/conversation-load-cleaned-at.test.ts +279 -0
- package/src/__tests__/conversation-load-history-repair.test.ts +1 -1
- package/src/__tests__/conversation-pairing.test.ts +2 -2
- package/src/__tests__/conversation-process-callsite.test.ts +1 -1
- package/src/__tests__/conversation-provider-retry-repair.test.ts +1 -1
- package/src/__tests__/conversation-queue.test.ts +1 -1
- package/src/__tests__/conversation-runtime-assembly.test.ts +264 -81
- package/src/__tests__/conversation-seed-composer.test.ts +66 -4
- package/src/__tests__/conversation-slash-commands.test.ts +36 -8
- package/src/__tests__/conversation-slash-queue.test.ts +1 -1
- package/src/__tests__/conversation-slash-unknown.test.ts +1 -1
- package/src/__tests__/conversation-speed-override.test.ts +1 -1
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +220 -0
- package/src/__tests__/conversation-workspace-cache-state.test.ts +1 -1
- 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 +6 -0
- package/src/__tests__/cu-unified-flow.test.ts +10 -1
- package/src/__tests__/dm-backfill.test.ts +64 -0
- package/src/__tests__/dm-persistence.test.ts +33 -0
- package/src/__tests__/document-find-replace.test.ts +501 -0
- package/src/__tests__/first-greeting.test.ts +23 -2
- package/src/__tests__/headless-browser-navigate.test.ts +172 -0
- package/src/__tests__/host-bash-proxy.test.ts +6 -0
- package/src/__tests__/host-browser-proxy.test.ts +10 -0
- package/src/__tests__/host-cu-proxy.test.ts +8 -1
- package/src/__tests__/host-file-proxy.test.ts +8 -1
- package/src/__tests__/host-transfer-proxy.test.ts +8 -1
- package/src/__tests__/identity-routes.test.ts +57 -0
- package/src/__tests__/inbound-slack-persistence.test.ts +3 -0
- package/src/__tests__/injector-chain.test.ts +2 -0
- package/src/__tests__/injector-document-comments.test.ts +378 -0
- package/src/__tests__/injector-pkb-v2-silenced.test.ts +4 -25
- package/src/__tests__/list-messages-attachments.test.ts +21 -17
- package/src/__tests__/list-messages-hidden-metadata.test.ts +217 -0
- package/src/__tests__/list-messages-page-latest.test.ts +130 -14
- package/src/__tests__/list-messages-tool-merge.test.ts +17 -16
- package/src/__tests__/llm-context-normalization.test.ts +0 -2
- package/src/__tests__/llm-resolver.test.ts +85 -1
- package/src/__tests__/log-export-routes.test.ts +99 -2
- package/src/__tests__/message-queue-steer.test.ts +114 -0
- package/src/__tests__/openai-provider.test.ts +105 -0
- package/src/__tests__/openai-responses-provider.test.ts +4 -4
- package/src/__tests__/outbound-slack-persistence.test.ts +187 -20
- package/src/__tests__/pending-interactions-resolved-event.test.ts +190 -0
- package/src/__tests__/platform.test.ts +0 -3
- package/src/__tests__/plugin-source-watcher.test.ts +302 -0
- package/src/__tests__/process-message-background-slack.test.ts +1 -51
- package/src/__tests__/process-message-display-content.test.ts +21 -16
- package/src/__tests__/server-history-render.test.ts +83 -4
- package/src/__tests__/steer-tool-repair.test.ts +249 -0
- package/src/__tests__/system-prompt.test.ts +51 -28
- package/src/__tests__/terminal-tools.test.ts +11 -1
- package/src/__tests__/thinking-block-replay.test.ts +113 -0
- package/src/__tests__/thread-backfill.test.ts +370 -22
- package/src/__tests__/tool-executor.test.ts +90 -1
- package/src/__tests__/tool-result-metadata-plumbing.test.ts +167 -0
- package/src/__tests__/twilio-routes.test.ts +1 -1
- package/src/__tests__/web-fetch.test.ts +2 -2
- package/src/__tests__/workspace-git-service.test.ts +88 -5
- package/src/__tests__/workspace-migration-088-deprecate-background-conversation-override.test.ts +158 -0
- package/src/agent/attachments.ts +1 -0
- package/src/agent/loop.ts +57 -20
- package/src/background-wake/next-wake.test.ts +289 -0
- package/src/background-wake/next-wake.ts +172 -0
- package/src/browser/operations.ts +15 -0
- package/src/cli/commands/__tests__/conversations-slack.test.ts +572 -0
- package/src/cli/commands/__tests__/memory-v2.test.ts +9 -12
- package/src/cli/commands/conversations.ts +128 -1
- package/src/cli/commands/inference-providers.ts +147 -1
- package/src/cli/commands/memory-v2.ts +308 -0
- package/src/cli/commands/notifications.ts +24 -2
- package/src/cli/utils/conversation-id.ts +17 -5
- package/src/config/bundled-skills/app-builder/SKILL.md +2 -2
- package/src/config/bundled-skills/document-editor/SKILL.md +115 -0
- package/src/config/bundled-skills/document-editor/TOOLS.json +240 -0
- package/src/config/bundled-skills/document-editor/tools/comment-list.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/comment-reply.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/comment-resolve.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/document-find.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/document-replace-text.ts +12 -0
- package/src/config/bundled-skills/media-processing/SKILL.md +8 -0
- package/src/config/bundled-skills/schedule/SKILL.md +8 -0
- package/src/config/bundled-tool-registry.ts +22 -12
- package/src/config/call-site-defaults.ts +19 -0
- package/src/config/feature-flag-registry.json +99 -3
- package/src/config/llm-resolver.ts +16 -2
- package/src/config/schemas/__tests__/memory-v2.test.ts +4 -0
- package/src/config/schemas/call-site-catalog.ts +21 -0
- package/src/config/schemas/llm.ts +3 -0
- package/src/config/schemas/memory-v2.ts +48 -1
- package/src/context/compactor.ts +8 -1
- package/src/context/token-estimator.ts +47 -4
- package/src/context/window-manager.ts +25 -0
- package/src/credential-health/credential-health-service.ts +34 -19
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +66 -6
- package/src/daemon/__tests__/native-web-search-metadata.test.ts +357 -0
- package/src/daemon/__tests__/web-search-status-text.test.ts +287 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +153 -23
- package/src/daemon/conversation-agent-loop.ts +223 -54
- package/src/daemon/conversation-lifecycle.ts +142 -116
- package/src/daemon/conversation-messaging.ts +3 -0
- package/src/daemon/conversation-process.ts +273 -0
- package/src/daemon/conversation-queue-manager.ts +14 -0
- package/src/daemon/conversation-runtime-assembly.ts +135 -75
- package/src/daemon/conversation-slash.ts +37 -5
- package/src/daemon/conversation-surfaces.ts +45 -2
- package/src/daemon/conversation-tool-setup.ts +7 -0
- package/src/daemon/conversation.ts +42 -5
- package/src/daemon/first-greeting.ts +10 -0
- package/src/daemon/handlers/__tests__/config-a2a-accept.test.ts +498 -0
- package/src/daemon/handlers/config-a2a.ts +160 -0
- package/src/daemon/handlers/config-model.test.ts +1 -0
- package/src/daemon/handlers/conversations.ts +79 -0
- package/src/daemon/handlers/shared.ts +92 -29
- package/src/daemon/host-bash-proxy.ts +1 -1
- package/src/daemon/host-cu-proxy.ts +1 -1
- package/src/daemon/host-file-proxy.ts +1 -1
- package/src/daemon/host-transfer-proxy.ts +1 -1
- package/src/daemon/lifecycle.ts +18 -4
- package/src/daemon/message-protocol.ts +4 -0
- package/src/daemon/message-types/conversations.ts +8 -0
- package/src/daemon/message-types/document-comments.ts +50 -0
- package/src/daemon/message-types/messages.ts +68 -1
- package/src/daemon/message-types/surfaces.ts +3 -1
- package/src/daemon/message-types/web-activity.ts +57 -0
- package/src/daemon/plugin-source-watcher.ts +135 -3
- package/src/daemon/process-message.ts +69 -12
- package/src/daemon/query-complexity-router.ts +75 -0
- package/src/daemon/trust-context.ts +6 -0
- package/src/documents/document-comments-store.test.ts +338 -0
- package/src/documents/document-comments-store.ts +237 -0
- package/src/documents/document-store.ts +202 -0
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +0 -1
- package/src/heartbeat/heartbeat-service.ts +1 -0
- package/src/home/__tests__/suggested-prompts.test.ts +33 -2
- package/src/home/feed-types.ts +6 -1
- package/src/home/home-content-refresh.ts +52 -0
- package/src/home/home-greeting-cache.ts +69 -0
- package/src/home/home-greeting.ts +94 -0
- package/src/home/suggested-prompts.ts +177 -9
- package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +135 -2
- package/src/memory/__tests__/memory-retrospective-job.test.ts +320 -6
- package/src/memory/conversation-crud.ts +133 -43
- package/src/memory/db-init.ts +16 -0
- package/src/memory/delivery-crud.ts +41 -0
- package/src/memory/delivery-status.ts +141 -15
- package/src/memory/external-conversation-store.ts +32 -1
- package/src/memory/jobs-worker.ts +21 -1
- package/src/memory/memory-retrospective-constants.ts +28 -0
- package/src/memory/memory-retrospective-enqueue.ts +3 -2
- package/src/memory/memory-retrospective-job.ts +408 -18
- package/src/memory/memory-retrospective-startup-cleanup.ts +3 -3
- package/src/memory/memory-v2-activation-log-store.ts +26 -8
- package/src/memory/migrations/100-core-tables.ts +1 -0
- package/src/memory/migrations/109-external-conversation-bindings.ts +1 -0
- package/src/memory/migrations/253-conversation-last-notified-profile.ts +15 -0
- package/src/memory/migrations/253-document-comments.ts +47 -0
- package/src/memory/migrations/254-external-conversation-binding-chat-name.ts +43 -0
- package/src/memory/migrations/255-channel-inbound-delivery-attempts.ts +24 -0
- package/src/memory/migrations/256-memory-v2-injection-events.ts +113 -0
- package/src/memory/migrations/257-strip-base-url-non-openai-compatible.ts +22 -0
- package/src/memory/migrations/258-onboarding-events-prior-assistants.ts +13 -0
- package/src/memory/migrations/259-conversation-cleaned-at.ts +33 -0
- package/src/memory/migrations/index.ts +17 -0
- package/src/memory/migrations/registry.ts +25 -0
- package/src/memory/onboarding-events-store.ts +7 -0
- package/src/memory/schema/calls.ts +1 -0
- package/src/memory/schema/conversations.ts +3 -0
- package/src/memory/schema/infrastructure.ts +1 -0
- package/src/memory/v2/__tests__/injection-events.test.ts +318 -0
- package/src/memory/v2/__tests__/injection.test.ts +31 -14
- package/src/memory/v2/__tests__/page-index.test.ts +365 -1
- package/src/memory/v2/__tests__/router.test.ts +489 -1
- package/src/memory/v2/consolidation-job.ts +14 -0
- package/src/memory/v2/injection-events.ts +101 -0
- package/src/memory/v2/injection.ts +21 -10
- package/src/memory/v2/page-index.ts +209 -7
- package/src/memory/v2/page-store.ts +18 -0
- package/src/memory/v2/router.ts +209 -55
- package/src/messaging/providers/index.ts +7 -1
- package/src/messaging/providers/slack/__tests__/adapter-mention-rendering.test.ts +329 -3
- package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +34 -1
- package/src/messaging/providers/slack/adapter.ts +178 -25
- package/src/messaging/providers/slack/api.test.ts +54 -0
- package/src/messaging/providers/slack/api.ts +119 -3
- package/src/messaging/providers/slack/client.ts +12 -0
- package/src/messaging/providers/slack/deep-link.ts +20 -1
- package/src/messaging/providers/slack/message-metadata.test.ts +48 -0
- package/src/messaging/providers/slack/message-metadata.ts +156 -0
- package/src/messaging/providers/slack/render-transcript.test.ts +107 -75
- package/src/messaging/providers/slack/render-transcript.ts +176 -49
- package/src/messaging/providers/slack/send.test.ts +77 -0
- package/src/messaging/providers/slack/send.ts +8 -2
- package/src/messaging/providers/slack/types.ts +14 -0
- package/src/notifications/__tests__/emit-signal-home-feed.test.ts +4 -1
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +116 -54
- package/src/notifications/conversation-seed-composer.ts +14 -2
- package/src/notifications/deferred-emit.ts +135 -0
- package/src/notifications/emit-signal.ts +9 -1
- package/src/notifications/home-feed-side-effect.ts +60 -30
- package/src/oauth/connect-orchestrator.ts +3 -0
- package/src/oauth/credential-token-resolver.ts +2 -0
- package/src/oauth/manual-token-connection.ts +19 -0
- package/src/oauth/oauth-store.ts +12 -0
- package/src/oauth/seed-providers.ts +22 -0
- package/src/permissions/prompter.ts +5 -2
- package/src/permissions/secret-prompter.ts +4 -1
- package/src/plugins/defaults/injectors.ts +82 -9
- package/src/prompts/__tests__/system-prompt.test.ts +46 -2
- package/src/prompts/normalize-onboarding.ts +40 -0
- package/src/prompts/sections.ts +32 -14
- package/src/prompts/system-prompt.ts +105 -68
- package/src/prompts/template-detection.ts +37 -0
- package/src/prompts/templates/BOOTSTRAP-CONTENT-AUTOMATION.md +141 -0
- package/src/prompts/templates/BOOTSTRAP.md +8 -0
- package/src/prompts/templates/VOICE.md +3 -0
- package/src/prompts/templates/system-sections.ts +53 -3
- package/src/providers/anthropic/client.ts +132 -5
- package/src/providers/fireworks/client.ts +20 -2
- package/src/providers/inference/__tests__/base-url-route-validation.test.ts +342 -0
- package/src/providers/inference/__tests__/base-url-security.test.ts +189 -0
- package/src/providers/inference/__tests__/codex-token-refresh.test.ts +254 -0
- package/src/providers/inference/adapter-factory.ts +15 -1
- package/src/providers/inference/auth.ts +3 -3
- package/src/providers/inference/codex-token-refresh.ts +128 -0
- package/src/providers/inference/resolve-auth.ts +49 -6
- package/src/providers/model-catalog.ts +48 -1
- package/src/providers/openai/chat-completions-provider.ts +57 -20
- package/src/providers/openai/responses-provider.ts +9 -3
- package/src/providers/openrouter/client.ts +5 -1
- package/src/providers/types.ts +25 -0
- package/src/runtime/__tests__/agent-wake.test.ts +214 -0
- package/src/runtime/__tests__/background-job-runner.test.ts +128 -0
- package/src/runtime/agent-wake.ts +151 -56
- package/src/runtime/auth/route-policy.ts +7 -3
- package/src/runtime/background-job-runner.ts +26 -0
- package/src/runtime/channel-reply-delivery.ts +182 -47
- package/src/runtime/channel-retry-sweep.ts +141 -16
- package/src/runtime/http-types.ts +7 -4
- package/src/runtime/pending-interactions.ts +51 -8
- package/src/runtime/routes/__tests__/content-source-routes.test.ts +162 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +55 -1
- package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +14 -0
- package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +271 -0
- package/src/runtime/routes/__tests__/sanity-routes.test.ts +280 -0
- package/src/runtime/routes/__tests__/slack-channel-routes.test.ts +266 -0
- package/src/runtime/routes/approval-routes.ts +4 -1
- package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +246 -0
- package/src/runtime/routes/content-source-routes.ts +78 -0
- package/src/runtime/routes/conversation-cli-routes.ts +146 -1
- package/src/runtime/routes/conversation-query-routes.ts +60 -1
- package/src/runtime/routes/conversation-routes.ts +281 -76
- package/src/runtime/routes/document-comments-routes.ts +287 -0
- package/src/runtime/routes/documents-routes.ts +33 -0
- package/src/runtime/routes/home-feed-routes.ts +6 -3
- package/src/runtime/routes/host-app-control-routes.ts +1 -1
- package/src/runtime/routes/host-browser-routes.ts +8 -1
- package/src/runtime/routes/identity-routes.ts +21 -0
- package/src/runtime/routes/inbound-message-handler.ts +288 -58
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +365 -6
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +283 -82
- package/src/runtime/routes/index.ts +12 -4
- package/src/runtime/routes/inference-provider-connection-routes.ts +63 -7
- package/src/runtime/routes/integrations/a2a.ts +60 -1
- package/src/runtime/routes/log-export-routes.ts +39 -0
- package/src/runtime/routes/memory-v2-routes.ts +217 -0
- package/src/runtime/routes/notification-routes.ts +19 -2
- package/src/runtime/routes/question-routes.ts +4 -1
- package/src/runtime/routes/sanity-routes.ts +159 -0
- package/src/runtime/routes/slack-channel-routes.ts +187 -0
- package/src/runtime/services/conversation-serializer.ts +30 -4
- package/src/schedule/integration-status.ts +3 -1
- package/src/security/__tests__/oauth2-device-code.test.ts +479 -0
- package/src/security/oauth2-device-code.ts +307 -0
- package/src/security/oauth2.ts +26 -9
- package/src/security/secure-keys.ts +5 -0
- package/src/skills/catalog-install.ts +6 -2
- package/src/tools/browser/__tests__/pinned-tabs.test.ts +80 -0
- package/src/tools/browser/browser-execution.ts +93 -0
- package/src/tools/browser/cdp-client/__tests__/factory.test.ts +28 -0
- package/src/tools/browser/cdp-client/__tests__/types.test.ts +1 -0
- package/src/tools/browser/cdp-client/cdp-inspect-client.ts +10 -0
- package/src/tools/browser/cdp-client/extension-cdp-client.ts +15 -1
- package/src/tools/browser/cdp-client/factory.ts +87 -3
- package/src/tools/browser/cdp-client/local-cdp-client.ts +9 -0
- package/src/tools/browser/cdp-client/types.ts +36 -0
- package/src/tools/browser/pinned-tabs.ts +90 -0
- package/src/tools/document/document-comment-tool.test.ts +379 -0
- package/src/tools/document/document-comment-tool.ts +156 -0
- package/src/tools/document/document-tool.ts +128 -2
- package/src/tools/network/__tests__/web-fetch-metadata.test.ts +229 -0
- package/src/tools/network/__tests__/web-search-metadata.test.ts +346 -0
- package/src/tools/network/domain-normalize.ts +17 -0
- package/src/tools/network/web-fetch.ts +213 -64
- package/src/tools/network/web-search.ts +191 -66
- package/src/tools/terminal/safe-env.ts +3 -2
- package/src/tools/tool-approval-handler.ts +19 -12
- package/src/tools/types.ts +4 -0
- package/src/tools/ui-surface/definitions.ts +3 -1
- package/src/types/onboarding-context.ts +4 -0
- package/src/util/__tests__/favicon.test.ts +84 -0
- package/src/util/favicon.ts +40 -0
- package/src/util/platform.ts +0 -5
- package/src/workspace/git-service.ts +75 -4
- package/src/workspace/migrations/088-deprecate-background-conversation-override.ts +103 -0
- package/src/workspace/migrations/registry.ts +2 -0
- package/src/config/bundled-skills/document/SKILL.md +0 -54
- package/src/config/bundled-skills/document/TOOLS.json +0 -106
- package/src/daemon/seed-files.ts +0 -18
- package/src/runtime/routes/interface-routes.ts +0 -43
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-create.ts +0 -0
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-delete.ts +0 -0
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-list.ts +0 -0
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-read.ts +0 -0
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-update.ts +0 -0
|
@@ -240,6 +240,99 @@ export function deleteDocument(surfaceId: string): boolean {
|
|
|
240
240
|
}
|
|
241
241
|
}
|
|
242
242
|
|
|
243
|
+
// ---------------------------------------------------------------------------
|
|
244
|
+
// In-document search (grep)
|
|
245
|
+
// ---------------------------------------------------------------------------
|
|
246
|
+
|
|
247
|
+
export interface FindMatch {
|
|
248
|
+
lineNumber: number;
|
|
249
|
+
lineContent: string;
|
|
250
|
+
matchStart: number;
|
|
251
|
+
matchEnd: number;
|
|
252
|
+
matchText: string;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
export interface FindResult {
|
|
256
|
+
surfaceId: string;
|
|
257
|
+
totalMatches: number;
|
|
258
|
+
matches: FindMatch[];
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const MAX_FIND_MATCHES = 50;
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Search for text or a regex pattern within a document's content.
|
|
265
|
+
* Returns matching lines with line numbers and match positions.
|
|
266
|
+
* Results are capped at {@link MAX_FIND_MATCHES} to avoid oversized responses.
|
|
267
|
+
*/
|
|
268
|
+
export function findInDocument(
|
|
269
|
+
surfaceId: string,
|
|
270
|
+
query: string,
|
|
271
|
+
options: { regex?: boolean; caseSensitive?: boolean } = {},
|
|
272
|
+
): FindResult | null {
|
|
273
|
+
try {
|
|
274
|
+
const row = rawGet<{ content: string }>(
|
|
275
|
+
/*sql*/ `SELECT content FROM documents WHERE surface_id = ?`,
|
|
276
|
+
surfaceId,
|
|
277
|
+
);
|
|
278
|
+
if (!row) return null;
|
|
279
|
+
|
|
280
|
+
const lines = row.content.split("\n");
|
|
281
|
+
const matches: FindMatch[] = [];
|
|
282
|
+
let uncappedTotal = 0;
|
|
283
|
+
|
|
284
|
+
if (options.regex) {
|
|
285
|
+
const flags = options.caseSensitive ? "g" : "gi";
|
|
286
|
+
const re = new RegExp(query, flags);
|
|
287
|
+
for (let i = 0; i < lines.length; i++) {
|
|
288
|
+
const line = lines[i];
|
|
289
|
+
re.lastIndex = 0;
|
|
290
|
+
let m: RegExpExecArray | null;
|
|
291
|
+
while ((m = re.exec(line)) !== null) {
|
|
292
|
+
uncappedTotal++;
|
|
293
|
+
if (matches.length < MAX_FIND_MATCHES) {
|
|
294
|
+
matches.push({
|
|
295
|
+
lineNumber: i + 1,
|
|
296
|
+
lineContent: line,
|
|
297
|
+
matchStart: m.index,
|
|
298
|
+
matchEnd: m.index + m[0].length,
|
|
299
|
+
matchText: m[0],
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
if (m[0].length === 0) re.lastIndex++;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
} else {
|
|
306
|
+
const needle = options.caseSensitive ? query : query.toLowerCase();
|
|
307
|
+
for (let i = 0; i < lines.length; i++) {
|
|
308
|
+
const line = lines[i];
|
|
309
|
+
const haystack = options.caseSensitive ? line : line.toLowerCase();
|
|
310
|
+
let startPos = 0;
|
|
311
|
+
while (startPos <= haystack.length) {
|
|
312
|
+
const idx = haystack.indexOf(needle, startPos);
|
|
313
|
+
if (idx === -1) break;
|
|
314
|
+
uncappedTotal++;
|
|
315
|
+
if (matches.length < MAX_FIND_MATCHES) {
|
|
316
|
+
matches.push({
|
|
317
|
+
lineNumber: i + 1,
|
|
318
|
+
lineContent: line,
|
|
319
|
+
matchStart: idx,
|
|
320
|
+
matchEnd: idx + needle.length,
|
|
321
|
+
matchText: line.slice(idx, idx + needle.length),
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
startPos = idx + Math.max(needle.length, 1);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
return { surfaceId, totalMatches: uncappedTotal, matches };
|
|
330
|
+
} catch (error) {
|
|
331
|
+
log.error({ err: error, surfaceId }, "Find-in-document error");
|
|
332
|
+
return null;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
243
336
|
// ---------------------------------------------------------------------------
|
|
244
337
|
// Document persistence
|
|
245
338
|
// ---------------------------------------------------------------------------
|
|
@@ -297,6 +390,115 @@ export function saveDocument(params: {
|
|
|
297
390
|
}
|
|
298
391
|
}
|
|
299
392
|
|
|
393
|
+
// ---------------------------------------------------------------------------
|
|
394
|
+
// Find-and-replace
|
|
395
|
+
// ---------------------------------------------------------------------------
|
|
396
|
+
|
|
397
|
+
export interface ReplaceInDocumentOptions {
|
|
398
|
+
regex?: boolean;
|
|
399
|
+
caseSensitive?: boolean;
|
|
400
|
+
maxReplacements?: number;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
export type ReplaceInDocumentResult =
|
|
404
|
+
| { success: true; replacements_made: number; content_changed: boolean }
|
|
405
|
+
| { success: false; error: string };
|
|
406
|
+
|
|
407
|
+
function escapeRegExpChars(str: string): string {
|
|
408
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* Find and replace text within a document — like sed.
|
|
413
|
+
* Supports literal text and regex patterns with optional backreferences.
|
|
414
|
+
*/
|
|
415
|
+
export function replaceInDocument(
|
|
416
|
+
surfaceId: string,
|
|
417
|
+
find: string,
|
|
418
|
+
replace: string,
|
|
419
|
+
options: ReplaceInDocumentOptions = {},
|
|
420
|
+
): ReplaceInDocumentResult {
|
|
421
|
+
try {
|
|
422
|
+
const row = rawGet<{ content: string }>(
|
|
423
|
+
/*sql*/ `SELECT content FROM documents WHERE surface_id = ?`,
|
|
424
|
+
surfaceId,
|
|
425
|
+
);
|
|
426
|
+
if (!row) {
|
|
427
|
+
return { success: false, error: "Document not found" };
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
const flags = "g" + (options.caseSensitive === true ? "" : "i");
|
|
431
|
+
const pattern = options.regex
|
|
432
|
+
? new RegExp(find, flags)
|
|
433
|
+
: new RegExp(escapeRegExpChars(find), flags);
|
|
434
|
+
|
|
435
|
+
const totalMatches = [...row.content.matchAll(pattern)].length;
|
|
436
|
+
if (
|
|
437
|
+
totalMatches === 0 ||
|
|
438
|
+
(options.maxReplacements != null && options.maxReplacements <= 0)
|
|
439
|
+
) {
|
|
440
|
+
return { success: true, replacements_made: 0, content_changed: false };
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
let newContent: string;
|
|
444
|
+
let replacementsMade: number;
|
|
445
|
+
|
|
446
|
+
if (
|
|
447
|
+
options.maxReplacements != null &&
|
|
448
|
+
options.maxReplacements < totalMatches
|
|
449
|
+
) {
|
|
450
|
+
// Iterative replacement up to maxReplacements using manual exec loop
|
|
451
|
+
// so backreferences in the replacement string work correctly.
|
|
452
|
+
const limit = options.maxReplacements;
|
|
453
|
+
let count = 0;
|
|
454
|
+
let result = "";
|
|
455
|
+
let lastIndex = 0;
|
|
456
|
+
let m: RegExpExecArray | null;
|
|
457
|
+
while ((m = pattern.exec(row.content)) !== null) {
|
|
458
|
+
if (count >= limit) break;
|
|
459
|
+
result += row.content.slice(lastIndex, m.index);
|
|
460
|
+
const singleMatchPattern = new RegExp(
|
|
461
|
+
pattern.source,
|
|
462
|
+
pattern.flags.replace("g", ""),
|
|
463
|
+
);
|
|
464
|
+
result += m[0].replace(singleMatchPattern, replace);
|
|
465
|
+
lastIndex = m.index + m[0].length;
|
|
466
|
+
count++;
|
|
467
|
+
if (m[0].length === 0) pattern.lastIndex++;
|
|
468
|
+
}
|
|
469
|
+
result += row.content.slice(lastIndex);
|
|
470
|
+
newContent = result;
|
|
471
|
+
replacementsMade = count;
|
|
472
|
+
} else {
|
|
473
|
+
newContent = row.content.replace(pattern, replace);
|
|
474
|
+
replacementsMade = totalMatches;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
const wordCount = newContent
|
|
478
|
+
.split(/\s+/)
|
|
479
|
+
.filter((w) => w.length > 0).length;
|
|
480
|
+
rawRun(
|
|
481
|
+
/*sql*/ `UPDATE documents SET content = ?, word_count = ?, updated_at = ? WHERE surface_id = ?`,
|
|
482
|
+
newContent,
|
|
483
|
+
wordCount,
|
|
484
|
+
Date.now(),
|
|
485
|
+
surfaceId,
|
|
486
|
+
);
|
|
487
|
+
log.info({ surfaceId, replacementsMade }, "Replaced text in document");
|
|
488
|
+
return {
|
|
489
|
+
success: true,
|
|
490
|
+
replacements_made: replacementsMade,
|
|
491
|
+
content_changed: true,
|
|
492
|
+
};
|
|
493
|
+
} catch (error) {
|
|
494
|
+
log.error({ err: error, surfaceId }, "Replace-in-document error");
|
|
495
|
+
return {
|
|
496
|
+
success: false,
|
|
497
|
+
error: error instanceof Error ? error.message : "Unknown error",
|
|
498
|
+
};
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
300
502
|
/** Update persisted document content (append or replace). */
|
|
301
503
|
export function updateDocumentContent(
|
|
302
504
|
surfaceId: string,
|
|
@@ -35,7 +35,6 @@ mock.module("../../util/platform.js", () => ({
|
|
|
35
35
|
getEmbeddingModelsDir: () => join(workspaceDir ?? fallbackDir, "models"),
|
|
36
36
|
getSandboxRootDir: () => join(workspaceDir ?? fallbackDir, "sandbox"),
|
|
37
37
|
getSandboxWorkingDir: () => join(workspaceDir ?? fallbackDir, "sandbox/work"),
|
|
38
|
-
getInterfacesDir: () => join(workspaceDir ?? fallbackDir, "interfaces"),
|
|
39
38
|
getSoundsDir: () => join(workspaceDir ?? fallbackDir, "sounds"),
|
|
40
39
|
getAvatarDir: () => join(workspaceDir ?? fallbackDir, "avatar"),
|
|
41
40
|
AVATAR_IMAGE_FILENAME: "avatar-image.png",
|
|
@@ -686,6 +686,7 @@ export class HeartbeatService {
|
|
|
686
686
|
callSite: "heartbeatAgent",
|
|
687
687
|
timeoutMs: HEARTBEAT_TIMEOUT_MS,
|
|
688
688
|
origin: "heartbeat",
|
|
689
|
+
deferNotifications: true,
|
|
689
690
|
onConversationCreated: (newConversationId) => {
|
|
690
691
|
conversationId = newConversationId;
|
|
691
692
|
this.deps.onConversationCreated?.({
|
|
@@ -5,8 +5,6 @@ import { describe, expect, mock, test } from "bun:test";
|
|
|
5
5
|
let mockConnectedProviders = new Set<string>();
|
|
6
6
|
|
|
7
7
|
mock.module("../../oauth/oauth-store.js", () => ({
|
|
8
|
-
isProviderConnected: async (provider: string) =>
|
|
9
|
-
mockConnectedProviders.has(provider),
|
|
10
8
|
listProviders: () => [
|
|
11
9
|
{ provider: "google" },
|
|
12
10
|
{ provider: "slack" },
|
|
@@ -16,6 +14,11 @@ mock.module("../../oauth/oauth-store.js", () => ({
|
|
|
16
14
|
],
|
|
17
15
|
}));
|
|
18
16
|
|
|
17
|
+
mock.module("../../schedule/integration-status.js", () => ({
|
|
18
|
+
isOAuthProviderConnected: async (provider: string) =>
|
|
19
|
+
mockConnectedProviders.has(provider),
|
|
20
|
+
}));
|
|
21
|
+
|
|
19
22
|
mock.module("../../util/logger.js", () => ({
|
|
20
23
|
getLogger: () =>
|
|
21
24
|
new Proxy({} as Record<string, unknown>, {
|
|
@@ -23,6 +26,34 @@ mock.module("../../util/logger.js", () => ({
|
|
|
23
26
|
}),
|
|
24
27
|
}));
|
|
25
28
|
|
|
29
|
+
mock.module("../../config/loader.js", () => ({
|
|
30
|
+
getConfig: () => ({ llm: {} }),
|
|
31
|
+
}));
|
|
32
|
+
|
|
33
|
+
mock.module("../../config/llm-resolver.js", () => ({
|
|
34
|
+
resolveCallSiteConfig: () => ({ provider: "mock" }),
|
|
35
|
+
}));
|
|
36
|
+
|
|
37
|
+
mock.module("../../providers/provider-send-message.js", () => ({
|
|
38
|
+
getConfiguredProvider: async () => ({}),
|
|
39
|
+
}));
|
|
40
|
+
|
|
41
|
+
mock.module("../../prompts/persona-resolver.js", () => ({
|
|
42
|
+
resolvePersonaContext: () => ({
|
|
43
|
+
userPersona: null,
|
|
44
|
+
userSlug: null,
|
|
45
|
+
channelPersona: null,
|
|
46
|
+
}),
|
|
47
|
+
}));
|
|
48
|
+
|
|
49
|
+
mock.module("../../prompts/system-prompt.js", () => ({
|
|
50
|
+
buildSystemPrompt: () => "mock system prompt",
|
|
51
|
+
}));
|
|
52
|
+
|
|
53
|
+
mock.module("../../runtime/btw-sidechain.js", () => ({
|
|
54
|
+
runBtwSidechain: async () => ({ text: "" }),
|
|
55
|
+
}));
|
|
56
|
+
|
|
26
57
|
const { getSuggestedPrompts } = await import("../suggested-prompts.js");
|
|
27
58
|
|
|
28
59
|
// ─── Tests ─────────────────────────────────────────────────────────────
|
package/src/home/feed-types.ts
CHANGED
|
@@ -106,7 +106,12 @@ export interface FeedItem {
|
|
|
106
106
|
actions?: FeedAction[];
|
|
107
107
|
/** Visual urgency treatment — controls badge color independently of sort priority. */
|
|
108
108
|
urgency?: FeedItemUrgency;
|
|
109
|
-
/**
|
|
109
|
+
/**
|
|
110
|
+
* Source conversation that emitted this notification, when known. Used by
|
|
111
|
+
* the "Go to Thread" affordance in the detail panel. Omitted when the
|
|
112
|
+
* source context is not a navigable conversation (scheduler job ids,
|
|
113
|
+
* watcher event ids, CLI tool-call ids).
|
|
114
|
+
*/
|
|
110
115
|
conversationId?: string;
|
|
111
116
|
/** Server-driven detail panel descriptor; when present, the client opens this panel kind. */
|
|
112
117
|
detailPanel?: FeedItemDetailPanel;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Background refresh timer for LLM-generated home page content.
|
|
3
|
+
*
|
|
4
|
+
* Keeps the personalized greeting and assistant-generated suggestion
|
|
5
|
+
* prompts warm in their caches so the GET handler never triggers LLM
|
|
6
|
+
* calls or database writes (see `src/runtime/AGENTS.md` — GET handler
|
|
7
|
+
* idempotency rule).
|
|
8
|
+
*
|
|
9
|
+
* Call `startHomeContentRefresh()` once during daemon startup; the
|
|
10
|
+
* timer handles periodic re-generation automatically.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { getLogger } from "../util/logger.js";
|
|
14
|
+
import { refreshPersonalizedGreeting } from "./home-greeting.js";
|
|
15
|
+
import { refreshAssistantSuggestedPrompts } from "./suggested-prompts.js";
|
|
16
|
+
|
|
17
|
+
const log = getLogger("home-content-refresh");
|
|
18
|
+
|
|
19
|
+
const REFRESH_INTERVAL_MS = 30 * 60 * 1000; // 30 minutes
|
|
20
|
+
|
|
21
|
+
let refreshTimer: ReturnType<typeof setInterval> | null = null;
|
|
22
|
+
|
|
23
|
+
async function refreshAll(): Promise<void> {
|
|
24
|
+
await Promise.all([
|
|
25
|
+
refreshPersonalizedGreeting(),
|
|
26
|
+
refreshAssistantSuggestedPrompts(),
|
|
27
|
+
]);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Start periodic background refresh of home page LLM content.
|
|
32
|
+
* Runs an initial generation immediately (fire-and-forget) and
|
|
33
|
+
* schedules re-generation every 30 minutes.
|
|
34
|
+
*/
|
|
35
|
+
export function startHomeContentRefresh(): void {
|
|
36
|
+
void refreshAll().catch((err) =>
|
|
37
|
+
log.warn({ err }, "Initial home content refresh failed"),
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
refreshTimer = setInterval(() => {
|
|
41
|
+
void refreshAll().catch((err) =>
|
|
42
|
+
log.warn({ err }, "Periodic home content refresh failed"),
|
|
43
|
+
);
|
|
44
|
+
}, REFRESH_INTERVAL_MS);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function stopHomeContentRefresh(): void {
|
|
48
|
+
if (refreshTimer) {
|
|
49
|
+
clearInterval(refreshTimer);
|
|
50
|
+
refreshTimer = null;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Caching layer for the LLM-generated personalized home greeting.
|
|
3
|
+
*
|
|
4
|
+
* The greeting (a short persona-flavored "here's what's been going on" line)
|
|
5
|
+
* is generated via `runBtwSidechain` and displayed at the top of the Home
|
|
6
|
+
* page. To avoid redundant LLM calls on every feed fetch, we cache the
|
|
7
|
+
* result for 4 hours with content-hash-based invalidation: when IDENTITY.md,
|
|
8
|
+
* SOUL.md, or the guardian persona change, the cache is busted.
|
|
9
|
+
*
|
|
10
|
+
* Storage uses the existing `memory_checkpoints` table (simple key-value store).
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import {
|
|
14
|
+
getMemoryCheckpoint,
|
|
15
|
+
setMemoryCheckpoint,
|
|
16
|
+
} from "../memory/checkpoints.js";
|
|
17
|
+
import { computeIdentityContentHash } from "../runtime/routes/identity-intro-cache.js";
|
|
18
|
+
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
// Constants
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
|
|
23
|
+
const CACHE_TTL_MS = 4 * 60 * 60 * 1000; // 4 hours
|
|
24
|
+
|
|
25
|
+
const CHECKPOINT_KEY_TEXT = "home:greeting:text";
|
|
26
|
+
const CHECKPOINT_KEY_HASH = "home:greeting:content_hash";
|
|
27
|
+
const CHECKPOINT_KEY_TIMESTAMP = "home:greeting:cached_at";
|
|
28
|
+
|
|
29
|
+
// ---------------------------------------------------------------------------
|
|
30
|
+
// Public API
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
32
|
+
|
|
33
|
+
export function getCachedHomeGreeting(): string | null {
|
|
34
|
+
try {
|
|
35
|
+
const text = getMemoryCheckpoint(CHECKPOINT_KEY_TEXT);
|
|
36
|
+
const hash = getMemoryCheckpoint(CHECKPOINT_KEY_HASH);
|
|
37
|
+
const timestampStr = getMemoryCheckpoint(CHECKPOINT_KEY_TIMESTAMP);
|
|
38
|
+
|
|
39
|
+
if (!text || !hash || !timestampStr) {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const cachedAt = Number(timestampStr);
|
|
44
|
+
if (isNaN(cachedAt) || Date.now() - cachedAt > CACHE_TTL_MS) {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const currentHash = computeIdentityContentHash();
|
|
49
|
+
if (currentHash !== hash) {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return text;
|
|
54
|
+
} catch {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function setCachedHomeGreeting(text: string): void {
|
|
60
|
+
try {
|
|
61
|
+
const hash = computeIdentityContentHash();
|
|
62
|
+
const now = String(Date.now());
|
|
63
|
+
setMemoryCheckpoint(CHECKPOINT_KEY_TEXT, text);
|
|
64
|
+
setMemoryCheckpoint(CHECKPOINT_KEY_HASH, hash);
|
|
65
|
+
setMemoryCheckpoint(CHECKPOINT_KEY_TIMESTAMP, now);
|
|
66
|
+
} catch {
|
|
67
|
+
// Cache write failure is non-fatal — next request will regenerate.
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Personalized home-page greeting generator.
|
|
3
|
+
*
|
|
4
|
+
* Produces a short greeting in the assistant's tone/persona for the
|
|
5
|
+
* Home page header. Uses the BTW side-chain for LLM generation and
|
|
6
|
+
* caches the result for 4 hours (busted when identity files change).
|
|
7
|
+
*
|
|
8
|
+
* The GET handler reads only from cache (`getPersonalizedGreeting`).
|
|
9
|
+
* Generation runs in the background via `refreshPersonalizedGreeting`,
|
|
10
|
+
* called at daemon startup and periodically by the home-content
|
|
11
|
+
* refresh timer.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { resolveCallSiteConfig } from "../config/llm-resolver.js";
|
|
15
|
+
import { getConfig } from "../config/loader.js";
|
|
16
|
+
import { resolvePersonaContext } from "../prompts/persona-resolver.js";
|
|
17
|
+
import { buildSystemPrompt } from "../prompts/system-prompt.js";
|
|
18
|
+
import { getConfiguredProvider } from "../providers/provider-send-message.js";
|
|
19
|
+
import { runBtwSidechain } from "../runtime/btw-sidechain.js";
|
|
20
|
+
import { getLogger } from "../util/logger.js";
|
|
21
|
+
import {
|
|
22
|
+
getCachedHomeGreeting,
|
|
23
|
+
setCachedHomeGreeting,
|
|
24
|
+
} from "./home-greeting-cache.js";
|
|
25
|
+
|
|
26
|
+
const log = getLogger("home-greeting");
|
|
27
|
+
|
|
28
|
+
const GENERATION_TIMEOUT_MS = 5_000;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Return the cached personalized greeting, or `null` when none is
|
|
32
|
+
* available yet. This is a synchronous cache read — safe for GET
|
|
33
|
+
* handlers.
|
|
34
|
+
*/
|
|
35
|
+
export function getPersonalizedGreeting(): string | null {
|
|
36
|
+
return getCachedHomeGreeting();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Generate a personalized greeting via LLM and write it to cache.
|
|
41
|
+
* No-ops when the cache is still fresh. Intended for background
|
|
42
|
+
* invocation (daemon startup / periodic refresh), not the GET path.
|
|
43
|
+
*/
|
|
44
|
+
export async function refreshPersonalizedGreeting(): Promise<void> {
|
|
45
|
+
const cached = getCachedHomeGreeting();
|
|
46
|
+
if (cached) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
try {
|
|
51
|
+
const config = getConfig();
|
|
52
|
+
const resolved = resolveCallSiteConfig("homeGreeting", config.llm);
|
|
53
|
+
|
|
54
|
+
const provider = await getConfiguredProvider("homeGreeting");
|
|
55
|
+
if (!provider) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const { userPersona, userSlug, channelPersona } = resolvePersonaContext(
|
|
60
|
+
undefined,
|
|
61
|
+
undefined,
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
const systemPrompt = buildSystemPrompt({
|
|
65
|
+
excludeBootstrap: true,
|
|
66
|
+
excludeCustomPrefix: true,
|
|
67
|
+
userPersona,
|
|
68
|
+
channelPersona,
|
|
69
|
+
userSlug,
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
const result = await runBtwSidechain({
|
|
73
|
+
content:
|
|
74
|
+
"Generate a short, casual greeting for the home page (max 10 words). " +
|
|
75
|
+
"It should convey the meaning of 'here's what's been going on' but in " +
|
|
76
|
+
"your unique tone and personality. Just output the greeting text, nothing else. " +
|
|
77
|
+
"No quotes, no preamble.",
|
|
78
|
+
provider,
|
|
79
|
+
systemPrompt,
|
|
80
|
+
messages: [],
|
|
81
|
+
tools: [],
|
|
82
|
+
callSite: "homeGreeting",
|
|
83
|
+
maxTokens: resolved.maxTokens,
|
|
84
|
+
timeoutMs: GENERATION_TIMEOUT_MS,
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
const text = result.text.trim();
|
|
88
|
+
if (text) {
|
|
89
|
+
setCachedHomeGreeting(text);
|
|
90
|
+
}
|
|
91
|
+
} catch (err) {
|
|
92
|
+
log.warn({ err }, "Failed to generate personalized home greeting");
|
|
93
|
+
}
|
|
94
|
+
}
|