@vellumai/assistant 0.5.1 → 0.5.3
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 +163 -54
- package/docs/architecture/integrations.md +62 -67
- package/docs/credential-execution-service.md +3 -3
- package/docs/skills.md +100 -0
- package/package.json +1 -1
- package/src/__tests__/agent-loop.test.ts +111 -0
- package/src/__tests__/always-loaded-tools-guard.test.ts +3 -4
- package/src/__tests__/app-builder-tool-scripts.test.ts +13 -151
- package/src/__tests__/app-dir-path-guard.test.ts +78 -0
- package/src/__tests__/app-executors.test.ts +1 -291
- package/src/__tests__/app-git-history.test.ts +4 -4
- package/src/__tests__/app-routes-csp.test.ts +1 -0
- package/src/__tests__/app-store-dir-names.test.ts +426 -0
- package/src/__tests__/attachments-store.test.ts +169 -21
- package/src/__tests__/attachments.test.ts +115 -1
- package/src/__tests__/btw-routes.test.ts +1 -0
- package/src/__tests__/canonical-guardian-store.test.ts +38 -0
- package/src/__tests__/channel-reply-delivery.test.ts +55 -0
- package/src/__tests__/checker.test.ts +54 -0
- package/src/__tests__/claude-code-skill-regression.test.ts +2 -0
- package/src/__tests__/claude-code-tool-profiles.test.ts +2 -0
- package/src/__tests__/compaction.benchmark.test.ts +2 -1
- package/src/__tests__/config-schema-cmd.test.ts +68 -21
- package/src/__tests__/config-schema.test.ts +1 -1
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +156 -5
- package/src/__tests__/conversation-agent-loop.test.ts +297 -2
- package/src/__tests__/conversation-attachments.test.ts +17 -19
- package/src/__tests__/conversation-disk-view-integration.test.ts +277 -0
- package/src/__tests__/conversation-disk-view.test.ts +810 -0
- package/src/__tests__/conversation-error.test.ts +1 -1
- package/src/__tests__/conversation-fork-crud.test.ts +551 -0
- package/src/__tests__/conversation-fork-route.test.ts +386 -0
- package/src/__tests__/conversation-history-web-search.test.ts +1 -1
- package/src/__tests__/conversation-key-store-disk-view.test.ts +130 -0
- package/src/__tests__/conversation-media-retry.test.ts +8 -2
- package/src/__tests__/conversation-memory-dirty-tail.test.ts +150 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +7 -0
- package/src/__tests__/conversation-queue.test.ts +36 -1
- package/src/__tests__/conversation-routes-disk-view.test.ts +439 -0
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +2 -2
- package/src/__tests__/conversation-routes-slash-commands.test.ts +2 -7
- package/src/__tests__/conversation-runtime-assembly.test.ts +17 -2
- package/src/__tests__/conversation-skill-tools.test.ts +4 -9
- package/src/__tests__/conversation-slash-commands.test.ts +149 -0
- package/src/__tests__/conversation-store.test.ts +24 -21
- package/src/__tests__/conversation-surfaces-state-update.test.ts +246 -0
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +1 -0
- package/src/__tests__/conversation-title-service.test.ts +137 -0
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +25 -315
- package/src/__tests__/conversation-tool-setup-memory-scope.test.ts +1 -0
- package/src/__tests__/conversation-tool-setup-side-effect-flag.test.ts +1 -0
- package/src/__tests__/conversation-wipe.test.ts +226 -0
- package/src/__tests__/conversation-workspace-cache-state.test.ts +44 -2
- package/src/__tests__/conversation-workspace-injection.test.ts +11 -0
- package/src/__tests__/credential-security-invariants.test.ts +3 -0
- package/src/__tests__/credential-vault-unit.test.ts +5 -10
- package/src/__tests__/cu-unified-flow.test.ts +1 -0
- package/src/__tests__/db-conversation-fork-lineage-migration.test.ts +241 -0
- package/src/__tests__/db-llm-request-log-provider-migration.test.ts +214 -0
- package/src/__tests__/db-memory-archive-migration.test.ts +372 -0
- package/src/__tests__/db-memory-brief-state-migration.test.ts +213 -0
- package/src/__tests__/db-memory-reducer-checkpoints.test.ts +273 -0
- package/src/__tests__/diagnostics-export.test.ts +70 -1
- package/src/__tests__/first-greeting.test.ts +80 -0
- package/src/__tests__/gateway-only-guard.test.ts +1 -0
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +3 -7
- package/src/__tests__/history-repair.test.ts +32 -10
- package/src/__tests__/http-conversation-lineage.test.ts +251 -0
- package/src/__tests__/image-source-path-reinject.test.ts +136 -0
- package/src/__tests__/inline-command-runner.test.ts +311 -0
- package/src/__tests__/inline-skill-authoring-guard.test.ts +220 -0
- package/src/__tests__/inline-skill-load-permissions.test.ts +435 -0
- package/src/__tests__/list-messages-attachments.test.ts +96 -0
- package/src/__tests__/llm-context-normalization.test.ts +1116 -0
- package/src/__tests__/llm-context-route-provider.test.ts +217 -0
- package/src/__tests__/llm-request-log-turn-query.test.ts +270 -0
- package/src/__tests__/media-generate-image.test.ts +47 -94
- package/src/__tests__/memory-brief-open-loops.test.ts +530 -0
- package/src/__tests__/memory-brief-time.test.ts +285 -0
- package/src/__tests__/memory-brief-wrapper.test.ts +311 -0
- package/src/__tests__/memory-chunk-archive.test.ts +400 -0
- package/src/__tests__/memory-chunk-dual-write.test.ts +453 -0
- package/src/__tests__/memory-episode-archive.test.ts +370 -0
- package/src/__tests__/memory-episode-dual-write.test.ts +626 -0
- package/src/__tests__/memory-lifecycle-e2e.test.ts +3 -1
- package/src/__tests__/memory-observation-archive.test.ts +375 -0
- package/src/__tests__/memory-observation-dual-write.test.ts +318 -0
- package/src/__tests__/memory-recall-quality.test.ts +7 -7
- package/src/__tests__/memory-reducer-store.test.ts +728 -0
- package/src/__tests__/memory-reducer-types.test.ts +699 -0
- package/src/__tests__/memory-reducer.test.ts +698 -0
- package/src/__tests__/memory-regressions.test.ts +6 -4
- package/src/__tests__/memory-simplified-config.test.ts +281 -0
- package/src/__tests__/migration-cross-version-compatibility.test.ts +4 -1
- package/src/__tests__/migration-export-http.test.ts +3 -1
- package/src/__tests__/migration-import-commit-http.test.ts +18 -4
- package/src/__tests__/migration-import-preflight-http.test.ts +1 -3
- package/src/__tests__/mime-builder.test.ts +3 -2
- package/src/__tests__/non-member-access-request.test.ts +12 -1
- package/src/__tests__/notification-decision-identity.test.ts +52 -0
- package/src/__tests__/oauth-apps-routes.test.ts +103 -0
- package/src/__tests__/oauth-store.test.ts +115 -0
- package/src/__tests__/parse-identity-fields.test.ts +129 -0
- package/src/__tests__/provider-error-scenarios.test.ts +1 -3
- package/src/__tests__/provider-failover-actual-provider.test.ts +66 -0
- package/src/__tests__/recording-handler.test.ts +17 -0
- package/src/__tests__/registry.test.ts +3 -8
- package/src/__tests__/relay-server.test.ts +1 -1
- package/src/__tests__/runtime-attachment-metadata.test.ts +7 -3
- package/src/__tests__/schema-transforms.test.ts +165 -5
- package/src/__tests__/server-history-render.test.ts +2 -2
- package/src/__tests__/skill-load-inline-command.test.ts +598 -0
- package/src/__tests__/skill-load-inline-includes.test.ts +644 -0
- package/src/__tests__/skills-inline-command-expansions.test.ts +301 -0
- package/src/__tests__/skills-transitive-hash.test.ts +333 -0
- package/src/__tests__/slack-app-setup-skill-regression.test.ts +3 -1
- package/src/__tests__/slack-inbound-verification.test.ts +2 -2
- package/src/__tests__/starter-task-flow.test.ts +1 -0
- package/src/__tests__/suggestion-routes.test.ts +443 -0
- package/src/__tests__/swarm-conversation-integration.test.ts +1 -0
- package/src/__tests__/swarm-recursion.test.ts +1 -0
- package/src/__tests__/swarm-tool.test.ts +1 -0
- package/src/__tests__/tool-execution-abort-cleanup.test.ts +1 -0
- package/src/__tests__/tool-preview-lifecycle.test.ts +32 -5
- package/src/__tests__/top-level-renderer.test.ts +22 -0
- package/src/__tests__/turn-boundary-resolution.test.ts +243 -0
- package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +320 -0
- package/src/__tests__/web-fetch.test.ts +6 -2
- package/src/__tests__/workspace-migration-006-services-config.test.ts +335 -0
- package/src/__tests__/workspace-migration-007-web-search-provider-rename.test.ts +312 -0
- package/src/__tests__/workspace-migration-009-backfill-conversation-disk-view.test.ts +278 -0
- package/src/__tests__/workspace-migration-010-app-dir-rename.test.ts +275 -0
- package/src/__tests__/workspace-migration-012-rename-conversation-disk-view-dirs.test.ts +77 -0
- package/src/__tests__/workspace-migration-013-repair-conversation-disk-view.test.ts +401 -0
- package/src/__tests__/workspace-migration-backfill-installation-id.test.ts +328 -0
- package/src/__tests__/workspace-migration-seed-device-id.test.ts +6 -10
- package/src/agent/attachments.ts +27 -1
- package/src/agent/loop.ts +29 -1
- package/src/avatar/traits-png-sync.ts +80 -25
- package/src/bundler/app-bundler.ts +4 -4
- package/src/calls/call-domain.ts +1 -0
- package/src/calls/voice-session-bridge.ts +1 -0
- package/src/cli/commands/auth.ts +92 -0
- package/src/cli/commands/avatar.ts +7 -6
- package/src/cli/commands/config.ts +2 -0
- package/src/cli/commands/oauth/providers.ts +29 -0
- package/src/cli/program.ts +12 -0
- package/src/cli.ts +15 -48
- package/src/config/bundled-skills/app-builder/SKILL.md +103 -28
- package/src/config/bundled-skills/app-builder/TOOLS.json +5 -199
- package/src/config/bundled-skills/app-builder/tools/{app-query.ts → app-refresh.ts} +2 -2
- package/src/config/bundled-skills/contacts/tools/google-contacts.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +6 -9
- package/src/config/bundled-skills/gmail/tools/gmail-attachments.ts +4 -6
- package/src/config/bundled-skills/gmail/tools/gmail-draft.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-filters.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-follow-up.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-forward.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-label.ts +4 -6
- package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-send-draft.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-trash.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-vacation.ts +2 -3
- package/src/config/bundled-skills/google-calendar/tools/shared.ts +1 -1
- package/src/config/bundled-skills/image-studio/SKILL.md +2 -2
- package/src/config/bundled-skills/image-studio/TOOLS.json +2 -2
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +45 -72
- package/src/config/bundled-skills/media-processing/tools/extract-keyframes.ts +2 -2
- package/src/config/bundled-skills/messaging/tools/shared.ts +1 -1
- package/src/config/bundled-skills/settings/tools/voice-config-update.ts +19 -3
- package/src/config/bundled-skills/skill-management/SKILL.md +1 -1
- package/src/config/bundled-skills/skill-management/TOOLS.json +2 -2
- package/src/config/bundled-skills/slack/tools/shared.ts +19 -4
- package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +2 -3
- package/src/config/bundled-skills/transcribe/SKILL.md +1 -1
- package/src/config/bundled-skills/transcribe/TOOLS.json +2 -6
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +19 -83
- package/src/config/bundled-tool-registry.ts +2 -14
- package/src/config/feature-flag-registry.json +24 -0
- package/src/config/loader.ts +65 -0
- package/src/config/raw-config-utils.ts +58 -0
- package/src/config/schema-utils.ts +28 -7
- package/src/config/schema.ts +20 -0
- package/src/config/schemas/elevenlabs.ts +18 -0
- package/src/config/schemas/memory-lifecycle.ts +4 -2
- package/src/config/schemas/memory-simplified.ts +101 -0
- package/src/config/schemas/memory-storage.ts +1 -1
- package/src/config/schemas/memory.ts +4 -0
- package/src/config/schemas/services.ts +8 -6
- package/src/config/skills.ts +50 -4
- package/src/contacts/contact-store.ts +13 -6
- package/src/contacts/contacts-write.ts +0 -1
- package/src/context/window-manager.ts +13 -2
- package/src/daemon/conversation-agent-loop-handlers.ts +54 -8
- package/src/daemon/conversation-agent-loop.ts +127 -20
- package/src/daemon/conversation-attachments.ts +18 -36
- package/src/daemon/conversation-error.ts +2 -1
- package/src/daemon/conversation-history.ts +18 -4
- package/src/daemon/conversation-lifecycle.ts +50 -16
- package/src/daemon/conversation-messaging.ts +70 -26
- package/src/daemon/conversation-process.ts +58 -34
- package/src/daemon/conversation-runtime-assembly.ts +22 -38
- package/src/daemon/conversation-slash.ts +121 -256
- package/src/daemon/conversation-surfaces.ts +170 -24
- package/src/daemon/conversation-tool-setup.ts +0 -6
- package/src/daemon/conversation-workspace.ts +21 -1
- package/src/daemon/conversation.ts +69 -30
- package/src/daemon/first-greeting.ts +35 -0
- package/src/daemon/handlers/config-embeddings.ts +156 -0
- package/src/daemon/handlers/config-model.ts +62 -26
- package/src/daemon/handlers/conversations.ts +0 -23
- package/src/daemon/handlers/identity.ts +12 -1
- package/src/daemon/handlers/recording.ts +26 -21
- package/src/daemon/host-cu-proxy.ts +2 -2
- package/src/daemon/lifecycle.ts +115 -65
- package/src/daemon/message-protocol.ts +3 -0
- package/src/daemon/message-types/conversations.ts +18 -0
- package/src/daemon/message-types/messages.ts +1 -0
- package/src/daemon/message-types/shared.ts +2 -0
- package/src/daemon/message-types/surfaces.ts +2 -0
- package/src/daemon/message-types/upgrades.ts +23 -0
- package/src/daemon/server.ts +83 -12
- package/src/daemon/shutdown-handlers.ts +8 -5
- package/src/daemon/startup-error.ts +9 -0
- package/src/daemon/tool-side-effects.ts +11 -28
- package/src/events/tool-permission-telemetry-listener.ts +1 -3
- package/src/followups/followup-store.ts +47 -1
- package/src/instrument.ts +0 -4
- package/src/media/app-icon-generator.ts +2 -2
- package/src/memory/app-git-service.ts +28 -16
- package/src/memory/app-store.ts +230 -41
- package/src/memory/archive-store.ts +400 -0
- package/src/memory/attachments-store.ts +558 -130
- package/src/memory/brief-formatting.ts +33 -0
- package/src/memory/brief-open-loops.ts +266 -0
- package/src/memory/brief-time.ts +161 -0
- package/src/memory/brief.ts +75 -0
- package/src/memory/conversation-attention-store.ts +70 -0
- package/src/memory/conversation-crud.ts +591 -8
- package/src/memory/conversation-directories.ts +125 -0
- package/src/memory/conversation-disk-view.ts +390 -0
- package/src/memory/conversation-key-store.ts +17 -5
- package/src/memory/conversation-queries.ts +5 -1
- package/src/memory/conversation-title-service.ts +21 -49
- package/src/memory/db-init.ts +40 -0
- package/src/memory/embedding-backend.ts +42 -53
- package/src/memory/embedding-gemini.test.ts +4 -4
- package/src/memory/embedding-local.ts +1 -3
- package/src/memory/embedding-ollama.ts +1 -3
- package/src/memory/embedding-openai.ts +1 -3
- package/src/memory/indexer.ts +114 -21
- package/src/memory/items-extractor.ts +42 -13
- package/src/memory/job-handlers/conversation-starters.ts +6 -1
- package/src/memory/job-handlers/embedding.test.ts +2 -4
- package/src/memory/job-handlers/embedding.ts +83 -0
- package/src/memory/job-utils.ts +1 -1
- package/src/memory/jobs-store.ts +6 -0
- package/src/memory/jobs-worker.ts +12 -0
- package/src/memory/llm-request-log-store.ts +100 -1
- package/src/memory/migrations/102-alter-table-columns.ts +5 -0
- package/src/memory/migrations/146-schedule-oneshot-routing.ts +3 -3
- package/src/memory/migrations/147-migrate-reminders-to-schedules.ts +66 -70
- package/src/memory/migrations/148-drop-reminders-table.ts +5 -9
- package/src/memory/migrations/160-drop-loopback-port-column.ts +1 -3
- package/src/memory/migrations/174-rename-thread-starters-table.ts +0 -7
- package/src/memory/migrations/178-oauth-providers-managed-service-config-key.ts +15 -0
- package/src/memory/migrations/179-llm-request-log-message-id.ts +16 -0
- package/src/memory/migrations/180-backfill-inline-attachments-to-disk.ts +66 -0
- package/src/memory/migrations/181-rename-thread-starters-checkpoints.ts +46 -0
- package/src/memory/migrations/182-oauth-providers-display-metadata.ts +20 -0
- package/src/memory/migrations/183-add-conversation-fork-lineage.ts +22 -0
- package/src/memory/migrations/184-llm-request-log-provider.ts +12 -0
- package/src/memory/migrations/185-memory-brief-state.ts +52 -0
- package/src/memory/migrations/186-memory-archive.ts +109 -0
- package/src/memory/migrations/187-memory-reducer-checkpoints.ts +19 -0
- package/src/memory/migrations/index.ts +10 -0
- package/src/memory/migrations/registry.ts +13 -0
- package/src/memory/qdrant-client.ts +23 -4
- package/src/memory/reducer-store.ts +271 -0
- package/src/memory/reducer-types.ts +99 -0
- package/src/memory/reducer.ts +453 -0
- package/src/memory/retriever.test.ts +601 -2
- package/src/memory/retriever.ts +85 -9
- package/src/memory/schema/conversations.ts +9 -0
- package/src/memory/schema/index.ts +2 -0
- package/src/memory/schema/infrastructure.ts +13 -7
- package/src/memory/schema/memory-archive.ts +121 -0
- package/src/memory/schema/memory-brief.ts +55 -0
- package/src/memory/schema/oauth.ts +6 -0
- package/src/memory/search/semantic.ts +17 -4
- package/src/messaging/providers/gmail/mime-builder.ts +3 -1
- package/src/notifications/copy-composer.ts +26 -0
- package/src/notifications/decision-engine.ts +14 -1
- package/src/notifications/emit-signal.ts +1 -1
- package/src/notifications/signal.ts +36 -0
- package/src/oauth/byo-connection.test.ts +1 -45
- package/src/oauth/byo-connection.ts +2 -8
- package/src/oauth/connect-orchestrator.ts +15 -11
- package/src/oauth/connection-resolver.test.ts +191 -0
- package/src/oauth/connection-resolver.ts +66 -38
- package/src/oauth/connection.ts +0 -1
- package/src/oauth/oauth-store.ts +99 -47
- package/src/oauth/platform-connection.test.ts +0 -1
- package/src/oauth/platform-connection.ts +11 -3
- package/src/oauth/seed-providers.ts +78 -3
- package/src/oauth/token-persistence.ts +16 -10
- package/src/permissions/checker.ts +160 -14
- package/src/permissions/defaults.ts +14 -0
- package/src/prompts/templates/BOOTSTRAP.md +2 -0
- package/src/providers/anthropic/client.ts +8 -1
- package/src/providers/failover.ts +4 -1
- package/src/providers/gemini/client.ts +50 -0
- package/src/providers/model-catalog.ts +92 -0
- package/src/providers/model-intents.ts +29 -20
- package/src/providers/openai/client.ts +49 -0
- package/src/providers/types.ts +2 -0
- package/src/runtime/access-request-helper.ts +16 -7
- package/src/runtime/auth/credential-service.ts +3 -1
- package/src/runtime/auth/route-policy.ts +14 -1
- package/src/runtime/btw-sidechain.ts +101 -0
- package/src/runtime/channel-reply-delivery.ts +17 -1
- package/src/runtime/http-router.ts +3 -1
- package/src/runtime/http-server.ts +196 -141
- package/src/runtime/http-types.ts +1 -0
- package/src/runtime/migrations/vbundle-builder.ts +5 -1
- package/src/runtime/routes/access-request-decision.ts +41 -0
- package/src/runtime/routes/app-management-routes.ts +6 -3
- package/src/runtime/routes/app-routes.ts +7 -3
- package/src/runtime/routes/approval-routes.ts +1 -0
- package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +34 -2
- package/src/runtime/routes/attachment-routes.ts +45 -15
- package/src/runtime/routes/btw-routes.ts +21 -61
- package/src/runtime/routes/conversation-management-routes.ts +74 -0
- package/src/runtime/routes/conversation-query-routes.ts +187 -10
- package/src/runtime/routes/conversation-routes.ts +269 -28
- package/src/runtime/routes/conversation-starter-routes.ts +9 -11
- package/src/runtime/routes/diagnostics-routes.ts +1 -0
- package/src/runtime/routes/identity-routes.ts +2 -35
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +2 -2
- package/src/runtime/routes/llm-context-normalization.ts +1212 -0
- package/src/runtime/routes/log-export-routes.ts +3 -0
- package/src/runtime/routes/memory-item-routes.test.ts +34 -0
- package/src/runtime/routes/memory-item-routes.ts +94 -5
- package/src/runtime/routes/migration-routes.ts +4 -1
- package/src/runtime/routes/oauth-apps.ts +291 -0
- package/src/runtime/routes/secret-routes.ts +30 -1
- package/src/runtime/routes/settings-routes.ts +14 -0
- package/src/runtime/routes/surface-action-routes.ts +68 -1
- package/src/runtime/routes/trace-event-routes.ts +4 -1
- package/src/schedule/schedule-store.ts +30 -21
- package/src/security/secure-keys.ts +21 -0
- package/src/signals/bash.ts +1 -1
- package/src/skills/inline-command-expansions.ts +204 -0
- package/src/skills/inline-command-render.ts +127 -0
- package/src/skills/inline-command-runner.ts +242 -0
- package/src/skills/transitive-version-hash.ts +88 -0
- package/src/swarm/backend-claude-code.ts +3 -6
- package/src/tasks/task-store.ts +43 -1
- package/src/telemetry/usage-telemetry-reporter.test.ts +3 -2
- package/src/telemetry/usage-telemetry-reporter.ts +3 -1
- package/src/tools/AGENTS.md +6 -10
- package/src/tools/apps/executors.ts +17 -232
- package/src/tools/claude-code/claude-code.ts +2 -3
- package/src/tools/credentials/vault.ts +7 -12
- package/src/tools/host-filesystem/read.ts +13 -10
- package/src/tools/network/__tests__/web-search.test.ts +4 -2
- package/src/tools/permission-checker.ts +8 -1
- package/src/tools/schedule/list.ts +2 -7
- package/src/tools/schema-transforms.ts +5 -0
- package/src/tools/shared/filesystem/format-diff.ts +2 -7
- package/src/tools/skills/execute.ts +1 -1
- package/src/tools/skills/load.ts +140 -6
- package/src/tools/tool-manifest.ts +0 -6
- package/src/tools/ui-surface/definitions.ts +2 -2
- package/src/util/device-id.ts +28 -5
- package/src/util/platform.ts +24 -0
- package/src/util/pricing.ts +1 -0
- package/src/util/retry.ts +1 -3
- package/src/workspace/migrations/003-seed-device-id.ts +3 -4
- package/src/workspace/migrations/006-services-config.ts +5 -0
- package/src/workspace/migrations/008-voice-timeout-and-max-steps.ts +12 -0
- package/src/workspace/migrations/009-backfill-conversation-disk-view.ts +10 -0
- package/src/workspace/migrations/010-app-dir-rename.ts +223 -0
- package/src/workspace/migrations/{002-backfill-installation-id.ts → 011-backfill-installation-id.ts} +24 -13
- package/src/workspace/migrations/012-rename-conversation-disk-view-dirs.ts +64 -0
- package/src/workspace/migrations/013-repair-conversation-disk-view.ts +11 -0
- package/src/workspace/migrations/rebuild-conversation-disk-view.ts +186 -0
- package/src/workspace/migrations/registry.ts +11 -1
- package/src/workspace/top-level-renderer.ts +12 -0
- package/src/__tests__/asset-materialize-tool.test.ts +0 -523
- package/src/__tests__/asset-search-tool.test.ts +0 -536
- package/src/__tests__/fixtures/media-reuse-fixtures.ts +0 -56
- package/src/__tests__/media-reuse-story.e2e.test.ts +0 -762
- package/src/__tests__/media-visibility-policy.test.ts +0 -190
- package/src/config/bundled-skills/app-builder/tools/app-file-edit.ts +0 -14
- package/src/config/bundled-skills/app-builder/tools/app-file-list.ts +0 -13
- package/src/config/bundled-skills/app-builder/tools/app-file-read.ts +0 -21
- package/src/config/bundled-skills/app-builder/tools/app-file-write.ts +0 -14
- package/src/config/bundled-skills/app-builder/tools/app-list.ts +0 -13
- package/src/config/bundled-skills/app-builder/tools/app-update.ts +0 -23
- package/src/daemon/media-visibility-policy.ts +0 -59
- package/src/tools/assets/materialize.ts +0 -248
- package/src/tools/assets/search.ts +0 -400
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import { existsSync, readdirSync, statSync } from "node:fs";
|
|
5
5
|
import { join, relative } from "node:path";
|
|
6
6
|
|
|
7
|
+
import { enrichMessageWithSourcePaths } from "../../agent/attachments.js";
|
|
7
8
|
import {
|
|
8
9
|
createAssistantMessage,
|
|
9
10
|
createUserMessage,
|
|
@@ -21,10 +22,13 @@ import {
|
|
|
21
22
|
isModelSlashCommand,
|
|
22
23
|
} from "../../daemon/conversation-process.js";
|
|
23
24
|
import {
|
|
24
|
-
isProviderShortcut,
|
|
25
25
|
resolveSlash,
|
|
26
26
|
type SlashContext,
|
|
27
27
|
} from "../../daemon/conversation-slash.js";
|
|
28
|
+
import {
|
|
29
|
+
getCannedFirstGreeting,
|
|
30
|
+
isWakeUpGreeting,
|
|
31
|
+
} from "../../daemon/first-greeting.js";
|
|
28
32
|
import { renderHistoryContent } from "../../daemon/handlers/shared.js";
|
|
29
33
|
import { HostBashProxy } from "../../daemon/host-bash-proxy.js";
|
|
30
34
|
import { HostCuProxy } from "../../daemon/host-cu-proxy.js";
|
|
@@ -34,6 +38,7 @@ import * as attachmentsStore from "../../memory/attachments-store.js";
|
|
|
34
38
|
import {
|
|
35
39
|
createCanonicalGuardianRequest,
|
|
36
40
|
generateCanonicalRequestCode,
|
|
41
|
+
listCanonicalGuardianRequests,
|
|
37
42
|
listPendingRequestsByConversationScope,
|
|
38
43
|
resolveCanonicalGuardianRequest,
|
|
39
44
|
} from "../../memory/canonical-guardian-store.js";
|
|
@@ -76,6 +81,9 @@ import {
|
|
|
76
81
|
|
|
77
82
|
const log = getLogger("conversation-routes");
|
|
78
83
|
|
|
84
|
+
/** Matches the `<no_response/>` sentinel used by channel delivery suppression. */
|
|
85
|
+
const NO_RESPONSE_INLINE_RE = /<no_response\s*\/?>/g;
|
|
86
|
+
|
|
79
87
|
const SUGGESTION_CACHE_MAX = 100;
|
|
80
88
|
|
|
81
89
|
function collectCanonicalGuardianRequestHintIds(
|
|
@@ -97,6 +105,43 @@ function collectCanonicalGuardianRequestHintIds(
|
|
|
97
105
|
.map((req) => req.id);
|
|
98
106
|
}
|
|
99
107
|
|
|
108
|
+
/**
|
|
109
|
+
* Expire orphaned canonical guardian requests for a conversation.
|
|
110
|
+
*
|
|
111
|
+
* After the in-memory auto-deny loop runs, there may still be "pending"
|
|
112
|
+
* canonical requests in the DB that have no corresponding in-memory
|
|
113
|
+
* pending interaction (e.g. the prompter timed out and resolved the
|
|
114
|
+
* confirmation directly without syncing canonical status). This sweep
|
|
115
|
+
* catches those stragglers so they don't get falsely matched by the
|
|
116
|
+
* guardian reply router on subsequent messages.
|
|
117
|
+
*
|
|
118
|
+
* Only expires requests *sourced from* (not merely delivered to) this
|
|
119
|
+
* conversation. Delivered requests may still have live pending interactions
|
|
120
|
+
* in their source conversation. Additionally skips requests that still
|
|
121
|
+
* have a live in-memory pending interaction.
|
|
122
|
+
*
|
|
123
|
+
* Uses `listCanonicalGuardianRequests` (not `listPendingRequestsByConversationScope`)
|
|
124
|
+
* so that time-expired requests (past their `expiresAt`) are also caught
|
|
125
|
+
* instead of being silently filtered out.
|
|
126
|
+
*/
|
|
127
|
+
function expireOrphanedCanonicalRequests(conversationId: string): void {
|
|
128
|
+
const sourceScoped = listCanonicalGuardianRequests({
|
|
129
|
+
conversationId,
|
|
130
|
+
status: "pending",
|
|
131
|
+
kind: "tool_approval",
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
for (const req of sourceScoped) {
|
|
135
|
+
// Skip requests that still have a live in-memory pending interaction —
|
|
136
|
+
// they are not orphaned.
|
|
137
|
+
if (pendingInteractions.get(req.id)) continue;
|
|
138
|
+
|
|
139
|
+
resolveCanonicalGuardianRequest(req.id, "pending", {
|
|
140
|
+
status: "expired",
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
100
145
|
async function tryConsumeCanonicalGuardianReply(params: {
|
|
101
146
|
conversationId: string;
|
|
102
147
|
sourceChannel: string;
|
|
@@ -107,6 +152,7 @@ async function tryConsumeCanonicalGuardianReply(params: {
|
|
|
107
152
|
filename: string;
|
|
108
153
|
mimeType: string;
|
|
109
154
|
data: string;
|
|
155
|
+
filePath?: string;
|
|
110
156
|
}>;
|
|
111
157
|
conversation: import("../../daemon/conversation.js").Conversation;
|
|
112
158
|
onEvent: (msg: ServerMessage) => void;
|
|
@@ -139,8 +185,12 @@ async function tryConsumeCanonicalGuardianReply(params: {
|
|
|
139
185
|
sourceChannel,
|
|
140
186
|
conversation,
|
|
141
187
|
);
|
|
142
|
-
|
|
143
|
-
|
|
188
|
+
// Always pass the hints array (even when empty) so
|
|
189
|
+
// findPendingCanonicalRequests respects the in-memory staleness filter
|
|
190
|
+
// applied by collectCanonicalGuardianRequestHintIds. Converting empty
|
|
191
|
+
// hints to `undefined` caused the router to fall through to raw DB
|
|
192
|
+
// queries that rediscovered stale canonical requests.
|
|
193
|
+
const pendingRequestIds = pendingRequestHintIds;
|
|
144
194
|
|
|
145
195
|
const routerResult = await routeGuardianReply({
|
|
146
196
|
messageText: trimmedContent,
|
|
@@ -183,19 +233,33 @@ async function tryConsumeCanonicalGuardianReply(params: {
|
|
|
183
233
|
// is not re-processed as a new user turn.
|
|
184
234
|
let messageId: string | undefined;
|
|
185
235
|
try {
|
|
236
|
+
const guardianImageSourcePaths: Record<string, string> = {};
|
|
237
|
+
for (let i = 0; i < attachments.length; i++) {
|
|
238
|
+
const a = attachments[i];
|
|
239
|
+
if (a.filePath && a.mimeType.toLowerCase().startsWith("image/")) {
|
|
240
|
+
guardianImageSourcePaths[`${i}:${a.filename}`] = a.filePath;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
186
243
|
const channelMeta = {
|
|
187
244
|
userMessageChannel: sourceChannel,
|
|
188
245
|
assistantMessageChannel: sourceChannel,
|
|
189
246
|
userMessageInterface: sourceInterface,
|
|
190
247
|
assistantMessageInterface: sourceInterface,
|
|
191
248
|
provenanceTrustClass: "guardian" as const,
|
|
249
|
+
...(Object.keys(guardianImageSourcePaths).length > 0
|
|
250
|
+
? { imageSourcePaths: guardianImageSourcePaths }
|
|
251
|
+
: {}),
|
|
192
252
|
};
|
|
193
253
|
|
|
194
|
-
const
|
|
254
|
+
const cleanUserMessage = createUserMessage(content, attachments);
|
|
255
|
+
const llmUserMessage = enrichMessageWithSourcePaths(
|
|
256
|
+
cleanUserMessage,
|
|
257
|
+
attachments,
|
|
258
|
+
);
|
|
195
259
|
const persistedUser = await addMessage(
|
|
196
260
|
conversationId,
|
|
197
261
|
"user",
|
|
198
|
-
JSON.stringify(
|
|
262
|
+
JSON.stringify(cleanUserMessage.content),
|
|
199
263
|
channelMeta,
|
|
200
264
|
);
|
|
201
265
|
messageId = persistedUser.id;
|
|
@@ -215,7 +279,7 @@ async function tryConsumeCanonicalGuardianReply(params: {
|
|
|
215
279
|
|
|
216
280
|
// Avoid mutating in-memory history / emitting stream deltas while a run is active.
|
|
217
281
|
if (!conversation.isProcessing()) {
|
|
218
|
-
conversation.getMessages().push(
|
|
282
|
+
conversation.getMessages().push(llmUserMessage, assistantMessage);
|
|
219
283
|
onEvent({
|
|
220
284
|
type: "assistant_text_delta",
|
|
221
285
|
text: replyText,
|
|
@@ -302,6 +366,48 @@ export function handleListMessages(
|
|
|
302
366
|
content = msg.content;
|
|
303
367
|
}
|
|
304
368
|
const rendered = renderHistoryContent(content);
|
|
369
|
+
|
|
370
|
+
// Strip <no_response/> markers from assistant messages so web/API
|
|
371
|
+
// clients never see the raw sentinel. Only assistant messages produce
|
|
372
|
+
// this marker; user messages are left untouched.
|
|
373
|
+
if (msg.role === "assistant") {
|
|
374
|
+
const originalSegments = rendered.textSegments;
|
|
375
|
+
const keepIndices: number[] = [];
|
|
376
|
+
const filteredSegments: string[] = [];
|
|
377
|
+
for (let i = 0; i < originalSegments.length; i++) {
|
|
378
|
+
const cleaned = originalSegments[i]
|
|
379
|
+
.replace(NO_RESPONSE_INLINE_RE, "")
|
|
380
|
+
.trim();
|
|
381
|
+
if (cleaned.length > 0) {
|
|
382
|
+
keepIndices.push(i);
|
|
383
|
+
filteredSegments.push(cleaned);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
// Remap contentOrder text:N indices to account for removed segments
|
|
387
|
+
const indexMap = new Map<number, number>();
|
|
388
|
+
keepIndices.forEach((oldIdx, newIdx) => indexMap.set(oldIdx, newIdx));
|
|
389
|
+
const filteredContentOrder = rendered.contentOrder
|
|
390
|
+
.map((entry) => {
|
|
391
|
+
const m = entry.match(/^text:(\d+)$/);
|
|
392
|
+
if (!m) return entry;
|
|
393
|
+
const newIdx = indexMap.get(Number(m[1]));
|
|
394
|
+
return newIdx !== undefined ? `text:${newIdx}` : undefined;
|
|
395
|
+
})
|
|
396
|
+
.filter((e): e is string => e !== undefined);
|
|
397
|
+
|
|
398
|
+
return {
|
|
399
|
+
role: msg.role,
|
|
400
|
+
text: rendered.text.replace(NO_RESPONSE_INLINE_RE, "").trim(),
|
|
401
|
+
timestamp: msg.createdAt,
|
|
402
|
+
toolCalls: rendered.toolCalls,
|
|
403
|
+
toolCallsBeforeText: rendered.toolCallsBeforeText,
|
|
404
|
+
textSegments: filteredSegments,
|
|
405
|
+
contentOrder: filteredContentOrder,
|
|
406
|
+
surfaces: rendered.surfaces,
|
|
407
|
+
id: msg.id,
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
|
|
305
411
|
return {
|
|
306
412
|
role: msg.role,
|
|
307
413
|
text: rendered.text,
|
|
@@ -327,15 +433,11 @@ export function handleListMessages(
|
|
|
327
433
|
// generate thumbnails for inline display on history restore.
|
|
328
434
|
const linked = attachmentsStore.getAttachmentMetadataForMessage(m.id);
|
|
329
435
|
if (linked.length > 0) {
|
|
330
|
-
// Batch-fetch file-backed status for all attachments in one query
|
|
331
|
-
// instead of issuing a separate query per attachment.
|
|
332
|
-
const fileBackedIds = attachmentsStore.getFileBackedAttachmentIds(
|
|
333
|
-
linked.map((a) => a.id),
|
|
334
|
-
);
|
|
335
436
|
msgAttachments = linked.map((a) => {
|
|
336
|
-
const isFileBacked = fileBackedIds.has(a.id);
|
|
337
437
|
if (a.mimeType.startsWith("image/")) {
|
|
338
|
-
const full = attachmentsStore.getAttachmentById(a.id
|
|
438
|
+
const full = attachmentsStore.getAttachmentById(a.id, {
|
|
439
|
+
hydrateFileData: true,
|
|
440
|
+
});
|
|
339
441
|
return {
|
|
340
442
|
id: a.id,
|
|
341
443
|
filename: a.originalFilename,
|
|
@@ -346,7 +448,7 @@ export function handleListMessages(
|
|
|
346
448
|
...(a.thumbnailBase64
|
|
347
449
|
? { thumbnailData: a.thumbnailBase64 }
|
|
348
450
|
: {}),
|
|
349
|
-
|
|
451
|
+
fileBacked: true,
|
|
350
452
|
};
|
|
351
453
|
}
|
|
352
454
|
return {
|
|
@@ -356,7 +458,7 @@ export function handleListMessages(
|
|
|
356
458
|
sizeBytes: a.sizeBytes,
|
|
357
459
|
kind: a.kind,
|
|
358
460
|
...(a.thumbnailBase64 ? { thumbnailData: a.thumbnailBase64 } : {}),
|
|
359
|
-
|
|
461
|
+
fileBacked: true,
|
|
360
462
|
};
|
|
361
463
|
});
|
|
362
464
|
}
|
|
@@ -748,6 +850,92 @@ export async function handleSendMessage(
|
|
|
748
850
|
skipProxySenderUpdate: preservingProxies,
|
|
749
851
|
});
|
|
750
852
|
|
|
853
|
+
// ── Canned first-greeting fast path ──
|
|
854
|
+
// On a completely fresh workspace, skip LLM inference for the macOS
|
|
855
|
+
// wake-up greeting and return a pre-written response. This eliminates
|
|
856
|
+
// 10-30s of inference latency on first boot.
|
|
857
|
+
if (isWakeUpGreeting(trimmedContent, conversation.getMessages().length)) {
|
|
858
|
+
const cannedGreeting = getCannedFirstGreeting();
|
|
859
|
+
if (cannedGreeting) {
|
|
860
|
+
conversation.processing = true;
|
|
861
|
+
let cleanupDeferred = false;
|
|
862
|
+
try {
|
|
863
|
+
const provenance = provenanceFromTrustContext(
|
|
864
|
+
conversation.trustContext,
|
|
865
|
+
);
|
|
866
|
+
const channelMeta = {
|
|
867
|
+
...provenance,
|
|
868
|
+
userMessageChannel: sourceChannel,
|
|
869
|
+
assistantMessageChannel: sourceChannel,
|
|
870
|
+
userMessageInterface: sourceInterface,
|
|
871
|
+
assistantMessageInterface: sourceInterface,
|
|
872
|
+
};
|
|
873
|
+
|
|
874
|
+
const rawContent = content ?? "";
|
|
875
|
+
const attachments = hasAttachments
|
|
876
|
+
? smDeps.resolveAttachments(attachmentIds)
|
|
877
|
+
: [];
|
|
878
|
+
const userMsg = createUserMessage(rawContent, attachments);
|
|
879
|
+
const persisted = await addMessage(
|
|
880
|
+
mapping.conversationId,
|
|
881
|
+
"user",
|
|
882
|
+
JSON.stringify(userMsg.content),
|
|
883
|
+
channelMeta,
|
|
884
|
+
);
|
|
885
|
+
conversation.getMessages().push(userMsg);
|
|
886
|
+
|
|
887
|
+
setConversationOriginChannelIfUnset(
|
|
888
|
+
mapping.conversationId,
|
|
889
|
+
sourceChannel,
|
|
890
|
+
);
|
|
891
|
+
setConversationOriginInterfaceIfUnset(
|
|
892
|
+
mapping.conversationId,
|
|
893
|
+
sourceInterface,
|
|
894
|
+
);
|
|
895
|
+
|
|
896
|
+
const assistantMsg = createAssistantMessage(cannedGreeting);
|
|
897
|
+
await addMessage(
|
|
898
|
+
mapping.conversationId,
|
|
899
|
+
"assistant",
|
|
900
|
+
JSON.stringify(assistantMsg.content),
|
|
901
|
+
channelMeta,
|
|
902
|
+
);
|
|
903
|
+
conversation.getMessages().push(assistantMsg);
|
|
904
|
+
|
|
905
|
+
const conversationId = mapping.conversationId;
|
|
906
|
+
const response = Response.json(
|
|
907
|
+
{ accepted: true, messageId: persisted.id, conversationId },
|
|
908
|
+
{ status: 202 },
|
|
909
|
+
);
|
|
910
|
+
|
|
911
|
+
// Defer event publishing to next tick (same pattern as unknown-slash
|
|
912
|
+
// fast path) so the HTTP response reaches the client before SSE
|
|
913
|
+
// events arrive.
|
|
914
|
+
setTimeout(() => {
|
|
915
|
+
onEvent({ type: "assistant_text_delta", text: cannedGreeting });
|
|
916
|
+
onEvent({ type: "message_complete", conversationId });
|
|
917
|
+
conversation.processing = false;
|
|
918
|
+
silentlyWithLog(
|
|
919
|
+
conversation.drainQueue(),
|
|
920
|
+
"canned-greeting queue drain",
|
|
921
|
+
);
|
|
922
|
+
}, 0);
|
|
923
|
+
|
|
924
|
+
log.info(
|
|
925
|
+
{ conversationId },
|
|
926
|
+
"Served canned first greeting — skipped LLM inference",
|
|
927
|
+
);
|
|
928
|
+
cleanupDeferred = true;
|
|
929
|
+
return response;
|
|
930
|
+
} finally {
|
|
931
|
+
if (!cleanupDeferred && conversation.processing) {
|
|
932
|
+
conversation.processing = false;
|
|
933
|
+
silentlyWithLog(conversation.drainQueue(), "error-path queue drain");
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
|
|
751
939
|
const attachments = hasAttachments
|
|
752
940
|
? smDeps.resolveAttachments(attachmentIds)
|
|
753
941
|
: [];
|
|
@@ -856,6 +1044,10 @@ export async function handleSendMessage(
|
|
|
856
1044
|
pendingInteractions.removeByConversation(conversation);
|
|
857
1045
|
}
|
|
858
1046
|
|
|
1047
|
+
// Expire any orphaned canonical requests that survived without a
|
|
1048
|
+
// matching in-memory pending interaction (e.g. prompter timeouts).
|
|
1049
|
+
expireOrphanedCanonicalRequests(mapping.conversationId);
|
|
1050
|
+
|
|
859
1051
|
return Response.json(
|
|
860
1052
|
{ accepted: true, queued: true, conversationId: mapping.conversationId },
|
|
861
1053
|
{ status: 202 },
|
|
@@ -892,6 +1084,10 @@ export async function handleSendMessage(
|
|
|
892
1084
|
pendingInteractions.removeByConversation(conversation);
|
|
893
1085
|
}
|
|
894
1086
|
|
|
1087
|
+
// Expire any orphaned canonical requests that survived without a
|
|
1088
|
+
// matching in-memory pending interaction (e.g. prompter timeouts).
|
|
1089
|
+
expireOrphanedCanonicalRequests(mapping.conversationId);
|
|
1090
|
+
|
|
895
1091
|
// Conversation is idle — persist and fire agent loop immediately
|
|
896
1092
|
conversation.setTurnChannelContext({
|
|
897
1093
|
userMessageChannel: sourceChannel,
|
|
@@ -915,6 +1111,7 @@ export async function handleSendMessage(
|
|
|
915
1111
|
model: config.services.inference.model,
|
|
916
1112
|
provider: config.services.inference.provider,
|
|
917
1113
|
estimatedCost: conversation.usageStats.estimatedCost,
|
|
1114
|
+
userMessageInterface: sourceInterface,
|
|
918
1115
|
};
|
|
919
1116
|
const slashResult = await resolveSlash(rawContent, slashContext);
|
|
920
1117
|
|
|
@@ -923,6 +1120,13 @@ export async function handleSendMessage(
|
|
|
923
1120
|
let cleanupDeferred = false;
|
|
924
1121
|
try {
|
|
925
1122
|
const provenance = provenanceFromTrustContext(conversation.trustContext);
|
|
1123
|
+
const imageSourcePaths: Record<string, string> = {};
|
|
1124
|
+
for (let i = 0; i < attachments.length; i++) {
|
|
1125
|
+
const a = attachments[i];
|
|
1126
|
+
if (a.filePath && a.mimeType.toLowerCase().startsWith("image/")) {
|
|
1127
|
+
imageSourcePaths[`${i}:${a.filename}`] = a.filePath;
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
926
1130
|
const channelMeta = {
|
|
927
1131
|
...provenance,
|
|
928
1132
|
userMessageChannel: sourceChannel,
|
|
@@ -930,15 +1134,19 @@ export async function handleSendMessage(
|
|
|
930
1134
|
userMessageInterface: sourceInterface,
|
|
931
1135
|
assistantMessageInterface: sourceInterface,
|
|
932
1136
|
...(body.automated === true ? { automated: true } : {}),
|
|
1137
|
+
...(Object.keys(imageSourcePaths).length > 0
|
|
1138
|
+
? { imageSourcePaths }
|
|
1139
|
+
: {}),
|
|
933
1140
|
};
|
|
934
|
-
const
|
|
1141
|
+
const cleanMsg = createUserMessage(rawContent, attachments);
|
|
1142
|
+
const llmMsg = enrichMessageWithSourcePaths(cleanMsg, attachments);
|
|
935
1143
|
const persisted = await addMessage(
|
|
936
1144
|
mapping.conversationId,
|
|
937
1145
|
"user",
|
|
938
|
-
JSON.stringify(
|
|
1146
|
+
JSON.stringify(cleanMsg.content),
|
|
939
1147
|
channelMeta,
|
|
940
1148
|
);
|
|
941
|
-
conversation.getMessages().push(
|
|
1149
|
+
conversation.getMessages().push(llmMsg);
|
|
942
1150
|
|
|
943
1151
|
const assistantMsg = createAssistantMessage(slashResult.message);
|
|
944
1152
|
await addMessage(
|
|
@@ -960,10 +1168,9 @@ export async function handleSendMessage(
|
|
|
960
1168
|
|
|
961
1169
|
// Snapshot model info now so the deferred callback cannot observe
|
|
962
1170
|
// a config change from a concurrent request.
|
|
963
|
-
const modelInfoEvent =
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
: null;
|
|
1171
|
+
const modelInfoEvent = isModelSlashCommand(rawContent)
|
|
1172
|
+
? await buildModelInfoEvent()
|
|
1173
|
+
: null;
|
|
967
1174
|
|
|
968
1175
|
const response = Response.json(
|
|
969
1176
|
{
|
|
@@ -1047,6 +1254,7 @@ async function generateLlmSuggestion(
|
|
|
1047
1254
|
provider: Provider,
|
|
1048
1255
|
assistantText: string,
|
|
1049
1256
|
): Promise<string | null> {
|
|
1257
|
+
const log = (await import("../../util/logger.js")).getLogger("runtime-http");
|
|
1050
1258
|
const truncated =
|
|
1051
1259
|
assistantText.length > 2000 ? assistantText.slice(-2000) : assistantText;
|
|
1052
1260
|
|
|
@@ -1055,18 +1263,43 @@ async function generateLlmSuggestion(
|
|
|
1055
1263
|
[{ role: "user", content: [{ type: "text", text: prompt }] }],
|
|
1056
1264
|
[], // no tools
|
|
1057
1265
|
undefined, // no system prompt
|
|
1058
|
-
{ config: { max_tokens:
|
|
1266
|
+
{ config: { max_tokens: 40, modelIntent: "latency-optimized" } },
|
|
1059
1267
|
);
|
|
1060
1268
|
|
|
1061
1269
|
const textBlock = response.content.find((b) => b.type === "text");
|
|
1062
1270
|
const raw = textBlock && "text" in textBlock ? textBlock.text.trim() : "";
|
|
1271
|
+
const stripped = raw.replace(/^["']+|["']+$/g, "");
|
|
1063
1272
|
|
|
1064
|
-
if (!
|
|
1273
|
+
if (!stripped) {
|
|
1274
|
+
log.debug("Suggestion rejected: empty LLM response");
|
|
1275
|
+
return null;
|
|
1276
|
+
}
|
|
1065
1277
|
|
|
1066
1278
|
// Take first line only, then enforce the length cap
|
|
1067
|
-
const firstLine =
|
|
1068
|
-
if (!firstLine
|
|
1069
|
-
|
|
1279
|
+
const firstLine = stripped.split("\n")[0].trim();
|
|
1280
|
+
if (!firstLine) {
|
|
1281
|
+
log.debug(
|
|
1282
|
+
{ rawLength: stripped.length },
|
|
1283
|
+
"Suggestion rejected: empty after first-line extraction",
|
|
1284
|
+
);
|
|
1285
|
+
return null;
|
|
1286
|
+
}
|
|
1287
|
+
if (firstLine.length <= 50) return firstLine;
|
|
1288
|
+
// Truncate at last word boundary within 50 chars.
|
|
1289
|
+
// Only strip the trailing partial word if the slice actually cut mid-word;
|
|
1290
|
+
// if the character right after the cut is whitespace, the slice is already clean.
|
|
1291
|
+
const sliced = firstLine.slice(0, 50);
|
|
1292
|
+
const wordTruncated = (
|
|
1293
|
+
/\s/.test(firstLine[50]) ? sliced : sliced.replace(/\s+\S*$/, "")
|
|
1294
|
+
).trim();
|
|
1295
|
+
if (wordTruncated.length < 15) {
|
|
1296
|
+
log.debug(
|
|
1297
|
+
{ rawLength: firstLine.length, truncatedLength: wordTruncated.length },
|
|
1298
|
+
"Suggestion rejected: too short after word-boundary truncation",
|
|
1299
|
+
);
|
|
1300
|
+
return null;
|
|
1301
|
+
}
|
|
1302
|
+
return wordTruncated;
|
|
1070
1303
|
}
|
|
1071
1304
|
|
|
1072
1305
|
export async function handleGetSuggestion(
|
|
@@ -1193,8 +1426,16 @@ export async function handleGetSuggestion(
|
|
|
1193
1426
|
}
|
|
1194
1427
|
} catch (err) {
|
|
1195
1428
|
suggestionInFlight.delete(msg.id);
|
|
1196
|
-
log.warn(
|
|
1429
|
+
log.warn(
|
|
1430
|
+
{ err, conversationKey, messageId: msg.id },
|
|
1431
|
+
"LLM suggestion failed",
|
|
1432
|
+
);
|
|
1197
1433
|
}
|
|
1434
|
+
} else {
|
|
1435
|
+
log.debug(
|
|
1436
|
+
{ conversationKey, messageId: msg.id },
|
|
1437
|
+
"Suggestion skipped: no provider available",
|
|
1438
|
+
);
|
|
1198
1439
|
}
|
|
1199
1440
|
|
|
1200
1441
|
return Response.json({
|
|
@@ -123,8 +123,10 @@ function handleListConversationStarters(url: URL): Response {
|
|
|
123
123
|
|
|
124
124
|
const db = getDb();
|
|
125
125
|
|
|
126
|
-
// Fetch chips (ranked by model, newest batch first)
|
|
127
|
-
|
|
126
|
+
// Fetch all chips (ranked by model, newest batch first), apply diversity
|
|
127
|
+
// reordering, then paginate. Reordering must happen before offset/limit so
|
|
128
|
+
// that paginated results are stable across pages.
|
|
129
|
+
const allItems = db
|
|
128
130
|
.select({
|
|
129
131
|
id: conversationStarters.id,
|
|
130
132
|
label: conversationStarters.label,
|
|
@@ -143,20 +145,16 @@ function handleListConversationStarters(url: URL): Response {
|
|
|
143
145
|
desc(conversationStarters.generationBatch),
|
|
144
146
|
desc(conversationStarters.createdAt),
|
|
145
147
|
)
|
|
146
|
-
.limit(limitParam)
|
|
147
|
-
.offset(offsetParam)
|
|
148
148
|
.all();
|
|
149
149
|
|
|
150
|
-
const
|
|
151
|
-
`SELECT COUNT(*) AS c FROM conversation_starters WHERE scope_id = ? AND card_type = 'chip'`,
|
|
152
|
-
scopeId,
|
|
153
|
-
);
|
|
154
|
-
const total = countRow?.c ?? 0;
|
|
150
|
+
const total = allItems.length;
|
|
155
151
|
|
|
156
|
-
// If starters exist,
|
|
152
|
+
// If starters exist, reorder for category diversity then paginate.
|
|
157
153
|
if (total > 0) {
|
|
154
|
+
const ordered = orderStrongestFirst(allItems);
|
|
155
|
+
const page = ordered.slice(offsetParam, offsetParam + limitParam);
|
|
158
156
|
return Response.json({
|
|
159
|
-
starters:
|
|
157
|
+
starters: page,
|
|
160
158
|
total,
|
|
161
159
|
status: "ready",
|
|
162
160
|
});
|
|
@@ -8,6 +8,7 @@ import { dirname, join } from "node:path";
|
|
|
8
8
|
import { fileURLToPath } from "node:url";
|
|
9
9
|
|
|
10
10
|
import { getBaseDataDir } from "../../config/env-registry.js";
|
|
11
|
+
import { parseIdentityFields } from "../../daemon/handlers/identity.js";
|
|
11
12
|
import { getWorkspacePromptPath, readLockfile } from "../../util/platform.js";
|
|
12
13
|
import { httpError } from "../http-errors.js";
|
|
13
14
|
import type { RouteDefinition } from "../http-router.js";
|
|
@@ -149,41 +150,7 @@ export function handleGetIdentity(): Response {
|
|
|
149
150
|
}
|
|
150
151
|
|
|
151
152
|
const content = readFileSync(identityPath, "utf-8");
|
|
152
|
-
const fields
|
|
153
|
-
for (const line of content.split("\n")) {
|
|
154
|
-
const trimmed = line.trim();
|
|
155
|
-
const lower = trimmed.toLowerCase();
|
|
156
|
-
const extract = (prefix: string): string | null => {
|
|
157
|
-
if (!lower.startsWith(prefix)) return null;
|
|
158
|
-
return trimmed.split(":**").pop()?.trim() ?? null;
|
|
159
|
-
};
|
|
160
|
-
|
|
161
|
-
const name = extract("- **name:**");
|
|
162
|
-
if (name) {
|
|
163
|
-
fields.name = name;
|
|
164
|
-
continue;
|
|
165
|
-
}
|
|
166
|
-
const role = extract("- **role:**");
|
|
167
|
-
if (role) {
|
|
168
|
-
fields.role = role;
|
|
169
|
-
continue;
|
|
170
|
-
}
|
|
171
|
-
const personality = extract("- **personality:**") ?? extract("- **vibe:**");
|
|
172
|
-
if (personality) {
|
|
173
|
-
fields.personality = personality;
|
|
174
|
-
continue;
|
|
175
|
-
}
|
|
176
|
-
const emoji = extract("- **emoji:**");
|
|
177
|
-
if (emoji) {
|
|
178
|
-
fields.emoji = emoji;
|
|
179
|
-
continue;
|
|
180
|
-
}
|
|
181
|
-
const home = extract("- **home:**");
|
|
182
|
-
if (home) {
|
|
183
|
-
fields.home = home;
|
|
184
|
-
continue;
|
|
185
|
-
}
|
|
186
|
-
}
|
|
153
|
+
const fields = parseIdentityFields(content);
|
|
187
154
|
|
|
188
155
|
const version = getPackageVersion();
|
|
189
156
|
|
|
@@ -334,7 +334,7 @@ export async function enforceIngressAcl(
|
|
|
334
334
|
dmCallbackUrl,
|
|
335
335
|
{
|
|
336
336
|
chatId: senderUserId,
|
|
337
|
-
text: "I've
|
|
337
|
+
text: "I don't recognize you yet! I've let my owner know you're trying to reach me. They'll need to share a 6-digit verification code with you — ask them directly if you know them. Once you have the code, reply here with it.",
|
|
338
338
|
assistantId,
|
|
339
339
|
},
|
|
340
340
|
mintBearerToken(),
|
|
@@ -588,7 +588,7 @@ export async function enforceIngressAcl(
|
|
|
588
588
|
dmCallbackUrl,
|
|
589
589
|
{
|
|
590
590
|
chatId: senderUserId,
|
|
591
|
-
text: "I've
|
|
591
|
+
text: "I don't recognize you yet! I've let my owner know you're trying to reach me. They'll need to share a 6-digit verification code with you — ask them directly if you know them. Once you have the code, reply here with it.",
|
|
592
592
|
assistantId,
|
|
593
593
|
},
|
|
594
594
|
mintBearerToken(),
|