@vellumai/assistant 0.7.2 → 0.7.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 +16 -1
- package/docs/architecture/memory.md +5 -2
- package/node_modules/@vellumai/gateway-client/src/ipc-client.ts +13 -4
- package/node_modules/@vellumai/skill-host-contracts/src/assistant-event.ts +0 -9
- package/node_modules/@vellumai/slack-text/src/index.test.ts +18 -35
- package/node_modules/@vellumai/slack-text/src/index.ts +2 -48
- package/openapi.yaml +449 -22
- package/package.json +1 -1
- package/src/__tests__/app-control-flow.test.ts +21 -11
- package/src/__tests__/assistant-event-hub.test.ts +48 -0
- package/src/__tests__/assistant-event.test.ts +0 -10
- package/src/__tests__/assistant-events-sse-hardening.test.ts +2 -7
- package/src/__tests__/assistant-feature-flags-integration.test.ts +18 -0
- package/src/__tests__/auto-analysis-end-to-end.test.ts +62 -1
- package/src/__tests__/background-workers-disk-pressure.test.ts +268 -0
- package/src/__tests__/call-conversation-messages.test.ts +8 -2
- package/src/__tests__/channel-inbound-disk-pressure.test.ts +537 -0
- package/src/__tests__/channel-readiness-service.test.ts +4 -2
- package/src/__tests__/config-loader-backfill.test.ts +379 -0
- package/src/__tests__/config-schema.test.ts +1 -0
- package/src/__tests__/config-watcher-cleanup-throttle.test.ts +18 -9
- package/src/__tests__/config-watcher.test.ts +140 -69
- package/src/__tests__/context-search-agent-runner.test.ts +61 -3
- package/src/__tests__/context-search-conversations-source.test.ts +0 -24
- package/src/__tests__/context-search-fanout.test.ts +0 -1
- package/src/__tests__/context-search-memory-source.test.ts +3 -7
- package/src/__tests__/context-search-memory-v2-source.test.ts +0 -2
- package/src/__tests__/context-search-pkb-source.test.ts +0 -1
- package/src/__tests__/context-search-workspace-source.test.ts +0 -1
- package/src/__tests__/conversation-abort-tool-results.test.ts +6 -0
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +223 -0
- package/src/__tests__/conversation-agent-loop.test.ts +454 -5
- package/src/__tests__/conversation-error.test.ts +150 -3
- package/src/__tests__/conversation-process-callsite.test.ts +43 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +6 -0
- package/src/__tests__/conversation-runtime-assembly.test.ts +65 -0
- package/src/__tests__/conversation-slash-unknown.test.ts +6 -0
- package/src/__tests__/conversation-speed-override.test.ts +0 -3
- package/src/__tests__/conversation-store.test.ts +0 -18
- package/src/__tests__/conversation-surfaces-app-control.test.ts +15 -4
- package/src/__tests__/conversation-surfaces-data-persist.test.ts +404 -0
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +2 -5
- package/src/__tests__/conversation-workspace-injection.test.ts +6 -0
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +6 -0
- package/src/__tests__/credentials-cli.test.ts +7 -0
- package/src/__tests__/cu-unified-flow.test.ts +176 -10
- package/src/__tests__/date-context.test.ts +164 -2
- package/src/__tests__/disk-pressure-guard.test.ts +262 -0
- package/src/__tests__/disk-pressure-lifecycle.test.ts +168 -0
- package/src/__tests__/disk-pressure-policy.test.ts +241 -0
- package/src/__tests__/disk-pressure-routes.test.ts +379 -0
- package/src/__tests__/disk-pressure-tools.test.ts +277 -0
- package/src/__tests__/disk-usage.test.ts +150 -0
- package/src/__tests__/events-client-registration.test.ts +52 -0
- package/src/__tests__/events-dev-bypass-actor.test.ts +162 -0
- package/src/__tests__/file-write-tool.test.ts +4 -10
- package/src/__tests__/filing-service.test.ts +3 -4
- package/src/__tests__/heartbeat-disk-pressure.test.ts +183 -0
- package/src/__tests__/heartbeat-service.test.ts +260 -11
- package/src/__tests__/host-app-control-proxy.test.ts +195 -25
- package/src/__tests__/host-bash-proxy.test.ts +227 -34
- package/src/__tests__/host-bash-routes.test.ts +178 -13
- package/src/__tests__/host-cu-proxy.test.ts +210 -3
- package/src/__tests__/host-cu-routes-targeted.test.ts +141 -12
- package/src/__tests__/host-file-proxy-targeted.test.ts +48 -9
- package/src/__tests__/host-file-proxy.test.ts +268 -6
- package/src/__tests__/host-file-routes-targeted.test.ts +175 -17
- package/src/__tests__/host-transfer-proxy-targeted.test.ts +408 -59
- package/src/__tests__/host-transfer-routes-targeted.test.ts +232 -17
- package/src/__tests__/http-user-message-parity.test.ts +107 -1
- package/src/__tests__/injector-chain.test.ts +18 -6
- package/src/__tests__/injector-disk-pressure.test.ts +224 -0
- package/src/__tests__/managed-profile-guard.test.ts +18 -0
- package/src/__tests__/mcp-abort-signal.test.ts +130 -0
- package/src/__tests__/memory-admin-recall.test.ts +3 -11
- package/src/__tests__/memory-retrieval-pipeline.test.ts +22 -1
- package/src/__tests__/normalize-onboarding.test.ts +180 -0
- package/src/__tests__/oauth-connect-routes.test.ts +316 -0
- package/src/__tests__/oauth-provider-seed-logos.test.ts +24 -2
- package/src/__tests__/onboarding-persona-write.test.ts +308 -0
- package/src/__tests__/openai-provider.test.ts +45 -8
- package/src/__tests__/persist-onboarding-artifacts.test.ts +44 -64
- package/src/__tests__/platform-callback-registration.test.ts +21 -4
- package/src/__tests__/platform.test.ts +2 -1
- package/src/__tests__/playbook-execution.test.ts +0 -43
- package/src/__tests__/plugin-tool-contribution.test.ts +47 -0
- package/src/__tests__/prechat-onboarding-contract.test.ts +214 -27
- package/src/__tests__/provider-tool-name.test.ts +23 -0
- package/src/__tests__/relay-server.test.ts +15 -4
- package/src/__tests__/runtime-events-sse.test.ts +4 -8
- package/src/__tests__/scheduler-disk-pressure.test.ts +148 -0
- package/src/__tests__/secret-ingress-http.test.ts +0 -1
- package/src/__tests__/suggestion-routes.test.ts +46 -0
- package/src/__tests__/twilio-validation.test.ts +2 -2
- package/src/__tests__/workspace-migration-065-bump-stale-heartbeat-interval.test.ts +122 -0
- package/src/__tests__/workspace-migration-066-seed-heartbeat-callsite-cost-default.test.ts +285 -0
- package/src/__tests__/workspace-migration-068-release-notes-local-timezone.test.ts +90 -0
- package/src/__tests__/workspace-migration-safe-storage-limits-release.test.ts +90 -0
- package/src/approvals/guardian-decision-primitive.ts +13 -0
- package/src/approvals/guardian-request-resolvers.ts +16 -17
- package/src/backup/snapshot-lock.ts +2 -27
- package/src/bundler/compiler-tools.ts +3 -2
- package/src/calls/call-conversation-messages.ts +46 -10
- package/src/cli/commands/__tests__/webhooks.test.ts +0 -4
- package/src/cli/commands/bash.ts +35 -108
- package/src/cli/commands/contacts.ts +64 -25
- package/src/cli/commands/credentials.ts +56 -0
- package/src/cli/commands/memory-v2.ts +7 -6
- package/src/cli/commands/oauth/__tests__/connect.test.ts +437 -1
- package/src/cli/commands/oauth/connect.ts +127 -1
- package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +0 -3
- package/src/cli/commands/platform/__tests__/connect.test.ts +7 -1
- package/src/cli/commands/platform/__tests__/disconnect.test.ts +7 -1
- package/src/cli/commands/platform/__tests__/status.test.ts +103 -6
- package/src/cli/commands/platform/index.ts +16 -7
- package/src/cli/commands/status.ts +57 -0
- package/src/cli/program.ts +4 -2
- package/src/config/assistant-feature-flags.ts +13 -3
- package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +4 -3
- package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +13 -7
- package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +2 -2
- package/src/config/bundled-skills/playbooks/tools/playbook-delete.ts +2 -2
- package/src/config/bundled-skills/playbooks/tools/playbook-list.ts +2 -2
- package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +2 -2
- package/src/config/env.ts +0 -8
- package/src/config/feature-flag-registry.json +27 -3
- package/src/config/loader.ts +127 -8
- package/src/config/schemas/__tests__/memory-v2.test.ts +10 -5
- package/src/config/schemas/call-site-catalog.ts +14 -0
- package/src/config/schemas/channels.ts +0 -5
- package/src/config/schemas/heartbeat.ts +1 -1
- package/src/config/schemas/llm.ts +2 -0
- package/src/config/schemas/memory-lifecycle.ts +13 -0
- package/src/config/schemas/memory-v2.ts +75 -11
- package/src/config/schemas/platform.ts +43 -3
- package/src/config/schemas/services.ts +28 -0
- package/src/config/seed-inference-profiles.ts +230 -33
- package/src/contacts/contact-store.ts +0 -25
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +86 -25
- package/src/daemon/assistant-attachments.ts +4 -4
- package/src/daemon/config-watcher.ts +85 -57
- package/src/daemon/conversation-agent-loop-handlers.ts +6 -0
- package/src/daemon/conversation-agent-loop.ts +170 -33
- package/src/daemon/conversation-error.ts +87 -15
- package/src/daemon/conversation-lifecycle.ts +1 -3
- package/src/daemon/conversation-process.ts +8 -0
- package/src/daemon/conversation-runtime-assembly.ts +26 -0
- package/src/daemon/conversation-store.ts +2 -2
- package/src/daemon/conversation-surfaces.ts +195 -15
- package/src/daemon/conversation-tool-setup.ts +57 -14
- package/src/daemon/conversation.ts +17 -22
- package/src/daemon/date-context.ts +71 -22
- package/src/daemon/disk-pressure-background-gate.ts +73 -0
- package/src/daemon/disk-pressure-guard.ts +343 -0
- package/src/daemon/disk-pressure-policy.ts +163 -0
- package/src/daemon/handlers/shared.ts +0 -1
- package/src/daemon/handlers/skills.ts +3 -4
- package/src/daemon/host-app-control-proxy.ts +137 -41
- package/src/daemon/host-bash-proxy.ts +46 -21
- package/src/daemon/host-cu-proxy.ts +49 -3
- package/src/daemon/host-file-proxy.ts +43 -7
- package/src/daemon/host-transfer-proxy.ts +95 -4
- package/src/daemon/lifecycle.ts +79 -28
- package/src/daemon/meet-host-supervisor.ts +4 -4
- package/src/daemon/meet-manifest-loader.ts +0 -1
- package/src/daemon/memory-v2-startup.ts +14 -4
- package/src/daemon/message-protocol.ts +3 -0
- package/src/daemon/message-types/conversations.ts +4 -0
- package/src/daemon/message-types/disk-pressure.ts +9 -0
- package/src/daemon/message-types/messages.ts +3 -0
- package/src/daemon/profiler-run-store.ts +5 -5
- package/src/daemon/tool-setup-types.ts +2 -2
- package/src/documents/document-store.ts +85 -0
- package/src/filing/filing-service.ts +30 -5
- package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +9 -16
- package/src/heartbeat/__tests__/heartbeat-run-store.test.ts +36 -0
- package/src/heartbeat/heartbeat-run-store.ts +13 -0
- package/src/heartbeat/heartbeat-service.ts +205 -31
- package/src/home/feed-scheduler.ts +18 -0
- package/src/inbound/platform-callback-registration.ts +8 -15
- package/src/ipc/__tests__/clients-list-ipc.test.ts +169 -0
- package/src/ipc/assistant-server.ts +56 -2
- package/src/ipc/gateway-client.ts +37 -3
- package/src/live-voice/live-voice-archive.ts +4 -4
- package/src/live-voice/protocol.ts +5 -7
- package/src/media/image-service.ts +1 -7
- package/src/memory/__tests__/fixtures/memory-v2-activation-fixtures.ts +21 -13
- package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +52 -22
- package/src/memory/__tests__/memory-v2-activation-log-store.test.ts +0 -6
- package/src/memory/__tests__/memory-v2-concept-frequency.test.ts +272 -0
- package/src/memory/admin.ts +5 -9
- package/src/memory/context-search/agent-runner.ts +19 -2
- package/src/memory/context-search/sources/conversations.ts +2 -11
- package/src/memory/context-search/sources/memory-v2.ts +5 -4
- package/src/memory/context-search/sources/memory.ts +0 -1
- package/src/memory/context-search/types.ts +0 -1
- package/src/memory/conversation-crud.ts +4 -12
- package/src/memory/db-init.ts +2 -0
- package/src/memory/embedding-runtime-manager.ts +119 -5
- package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +32 -21
- package/src/memory/graph/conversation-graph-memory.ts +42 -54
- package/src/memory/graph/extraction.ts +1 -3
- package/src/memory/graph/graph-search.test.ts +10 -67
- package/src/memory/graph/graph-search.ts +1 -20
- package/src/memory/graph/retriever.test.ts +6 -0
- package/src/memory/graph/retriever.ts +6 -10
- package/src/memory/indexer.ts +54 -45
- package/src/memory/job-handlers/backfill.ts +2 -11
- package/src/memory/job-handlers/cleanup.ts +43 -0
- package/src/memory/job-handlers/embedding.ts +6 -8
- package/src/memory/job-handlers/summarization.ts +2 -7
- package/src/memory/jobs-store.ts +48 -0
- package/src/memory/jobs-worker.ts +81 -43
- package/src/memory/memory-v2-activation-log-store.ts +32 -14
- package/src/memory/memory-v2-concept-frequency.ts +169 -0
- package/src/memory/migrations/239-trace-events-created-at-index.ts +18 -0
- package/src/memory/migrations/index.ts +1 -0
- package/src/memory/pkb/pkb-search.test.ts +6 -0
- package/src/memory/qdrant-client.ts +0 -13
- package/src/memory/rerank-local.ts +374 -0
- package/src/memory/search/semantic.ts +6 -67
- package/src/memory/trace-event-store.ts +1 -17
- package/src/memory/v2/__tests__/activation.test.ts +311 -250
- package/src/memory/v2/__tests__/consolidation-job.test.ts +40 -8
- package/src/memory/v2/__tests__/injection.test.ts +157 -167
- package/src/memory/v2/__tests__/prompts-consolidation.test.ts +61 -2
- package/src/memory/v2/__tests__/qdrant.test.ts +16 -0
- package/src/memory/v2/__tests__/reranker.test.ts +338 -0
- package/src/memory/v2/__tests__/sim.test.ts +5 -199
- package/src/memory/v2/__tests__/skill-store.test.ts +71 -65
- package/src/memory/v2/__tests__/static-context.test.ts +76 -1
- package/src/memory/v2/activation.ts +149 -156
- package/src/memory/v2/consolidation-job.ts +62 -12
- package/src/memory/v2/injection.ts +47 -60
- package/src/memory/v2/prompts/consolidation.ts +36 -1
- package/src/memory/v2/qdrant.ts +99 -0
- package/src/memory/v2/reranker.ts +177 -0
- package/src/memory/v2/sim.ts +10 -84
- package/src/memory/v2/skill-content.ts +4 -3
- package/src/memory/v2/skill-store.ts +82 -59
- package/src/memory/v2/static-context.ts +22 -0
- package/src/memory/v2/types.ts +10 -10
- package/src/notifications/copy-composer.ts +13 -0
- package/src/notifications/signal.ts +4 -0
- package/src/oauth/AGENTS.md +3 -1
- package/src/oauth/__tests__/oauth-connect-state.test.ts +137 -0
- package/src/oauth/connect-orchestrator.ts +2 -0
- package/src/oauth/connection-resolver.test.ts +66 -1
- package/src/oauth/connection-resolver.ts +55 -1
- package/src/oauth/oauth-connect-state.ts +77 -0
- package/src/oauth/seed-providers.ts +58 -1
- package/src/plugins/defaults/injectors.ts +35 -2
- package/src/plugins/defaults/memory-retrieval.ts +5 -6
- package/src/plugins/types.ts +7 -0
- package/src/proactive-artifact/aux-message-injector.ts +74 -0
- package/src/proactive-artifact/decision.test.ts +226 -0
- package/src/proactive-artifact/decision.ts +165 -0
- package/src/proactive-artifact/index.ts +7 -0
- package/src/proactive-artifact/job.test.ts +867 -0
- package/src/proactive-artifact/job.ts +352 -0
- package/src/proactive-artifact/message-copy.ts +41 -0
- package/src/proactive-artifact/trigger-state.test.ts +277 -0
- package/src/proactive-artifact/trigger-state.ts +119 -0
- package/src/prompts/normalize-onboarding.ts +80 -0
- package/src/prompts/persona-resolver.ts +101 -9
- package/src/prompts/system-prompt.ts +21 -7
- package/src/prompts/templates/BOOTSTRAP.md +13 -5
- package/src/providers/__tests__/retry-callsite.test.ts +222 -1
- package/src/providers/model-intents.ts +7 -0
- package/src/providers/openrouter/client.ts +8 -0
- package/src/providers/retry.ts +50 -0
- package/src/providers/types.ts +1 -0
- package/src/runtime/__tests__/agent-wake.test.ts +456 -3
- package/src/runtime/agent-wake.ts +238 -100
- package/src/runtime/assistant-event-hub.ts +36 -6
- package/src/runtime/assistant-event.ts +0 -1
- package/src/runtime/auth/__tests__/route-policy.test.ts +64 -0
- package/src/runtime/auth/route-policy.ts +14 -1
- package/src/runtime/auth/same-actor.ts +216 -0
- package/src/runtime/channel-retry-sweep.ts +65 -1
- package/src/runtime/guardian-reply-router.ts +10 -0
- package/src/runtime/local-actor-identity.ts +52 -11
- package/src/runtime/pending-interactions.ts +8 -0
- package/src/runtime/routes/__tests__/client-routes.test.ts +155 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +0 -5
- package/src/runtime/routes/__tests__/heartbeat-routes.test.ts +1 -1
- package/src/runtime/routes/client-routes.ts +20 -2
- package/src/runtime/routes/contact-routes.ts +0 -25
- package/src/runtime/routes/conversation-routes.ts +35 -26
- package/src/runtime/routes/debug-bash-routes.ts +163 -0
- package/src/runtime/routes/disk-pressure-routes.ts +121 -0
- package/src/runtime/routes/document-pdf-renderer.ts +6 -2
- package/src/runtime/routes/documents-routes.ts +2 -75
- package/src/runtime/routes/events-routes.ts +41 -9
- package/src/runtime/routes/host-bash-routes.ts +23 -3
- package/src/runtime/routes/host-cu-routes.ts +33 -6
- package/src/runtime/routes/host-file-routes.ts +32 -6
- package/src/runtime/routes/host-transfer-routes.ts +79 -16
- package/src/runtime/routes/identity-routes.ts +7 -138
- package/src/runtime/routes/inbound-message-handler.ts +77 -12
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +3 -0
- package/src/runtime/routes/index.ts +6 -0
- package/src/runtime/routes/memory-item-routes.test.ts +41 -15
- package/src/runtime/routes/memory-v2-routes.ts +33 -0
- package/src/runtime/routes/oauth-connect-routes.ts +153 -0
- package/src/runtime/verification-outbound-actions.ts +4 -4
- package/src/schedule/run-script.ts +37 -5
- package/src/schedule/scheduler.ts +20 -1
- package/src/security/encrypted-store.ts +2 -0
- package/src/security/secure-keys.ts +55 -0
- package/src/skills/remote-skill-policy.ts +4 -10
- package/src/subagent/index.ts +1 -7
- package/src/subagent/manager.ts +1 -15
- package/src/tasks/task-runner.ts +0 -1
- package/src/tasks/task-store.ts +0 -3
- package/src/tools/background-tool-registry.ts +17 -3
- package/src/tools/host-filesystem/edit.test.ts +151 -0
- package/src/tools/host-filesystem/edit.ts +43 -1
- package/src/tools/host-filesystem/read.test.ts +129 -0
- package/src/tools/host-filesystem/read.ts +43 -1
- package/src/tools/host-filesystem/transfer.test.ts +127 -2
- package/src/tools/host-filesystem/transfer.ts +56 -11
- package/src/tools/host-filesystem/write.test.ts +134 -0
- package/src/tools/host-filesystem/write.ts +43 -1
- package/src/tools/host-terminal/host-shell.ts +13 -6
- package/src/tools/mcp/mcp-tool-factory.ts +2 -1
- package/src/tools/memory/register.test.ts +12 -9
- package/src/tools/memory/register.ts +1 -2
- package/src/tools/provider-tool-name.ts +28 -0
- package/src/tools/registry.ts +30 -9
- package/src/tools/terminal/shell.ts +9 -1
- package/src/tools/tool-approval-handler.ts +31 -6
- package/src/tools/types.ts +24 -2
- package/src/tts/provider-catalog.ts +3 -5
- package/src/util/disk-usage.ts +138 -0
- package/src/util/platform.ts +21 -11
- package/src/util/process-liveness.ts +26 -0
- package/src/workspace/heartbeat-service.ts +19 -0
- package/src/workspace/migrations/065-bump-stale-heartbeat-interval.ts +60 -0
- package/src/workspace/migrations/066-seed-heartbeat-callsite-cost-default.ts +146 -0
- package/src/workspace/migrations/067-release-notes-safe-storage-limits.ts +72 -0
- package/src/workspace/migrations/068-release-notes-local-timezone.ts +65 -0
- package/src/workspace/migrations/registry.ts +8 -0
- package/src/__tests__/conversation-tool-setup-memory-scope.test.ts +0 -167
- package/src/memory/v2/__tests__/skill-qdrant.test.ts +0 -657
- package/src/memory/v2/skill-qdrant.ts +0 -404
- package/src/signals/bash.ts +0 -198
|
@@ -15,6 +15,8 @@ import {
|
|
|
15
15
|
createApprovalConversationGenerator,
|
|
16
16
|
createApprovalCopyGenerator,
|
|
17
17
|
} from "../../daemon/approval-generators.js";
|
|
18
|
+
import { getDiskPressureStatus } from "../../daemon/disk-pressure-guard.js";
|
|
19
|
+
import { classifyDiskPressureTurnPolicy } from "../../daemon/disk-pressure-policy.js";
|
|
18
20
|
import { processMessage } from "../../daemon/process-message.js";
|
|
19
21
|
import type { TrustContext } from "../../daemon/trust-context.js";
|
|
20
22
|
import { HeartbeatService } from "../../heartbeat/heartbeat-service.js";
|
|
@@ -35,6 +37,7 @@ import {
|
|
|
35
37
|
getPendingVerificationReply,
|
|
36
38
|
} from "../../memory/delivery-channels.js";
|
|
37
39
|
import {
|
|
40
|
+
clearPayload,
|
|
38
41
|
findMessageBySourceId,
|
|
39
42
|
linkMessage,
|
|
40
43
|
recordInbound,
|
|
@@ -75,6 +78,8 @@ import { tryTranscribeAudioAttachments } from "./inbound-stages/transcribe-audio
|
|
|
75
78
|
import type { RouteHandlerArgs } from "./types.js";
|
|
76
79
|
|
|
77
80
|
const log = getLogger("runtime-http");
|
|
81
|
+
const DISK_PRESSURE_REMOTE_BLOCK_REPLY =
|
|
82
|
+
"Storage is critically low, so remote messages are ignored until the guardian frees enough space. Please try again later.";
|
|
78
83
|
|
|
79
84
|
// Delete-lookup retry configuration. Delete webhooks can race ahead of
|
|
80
85
|
// the inbound handler's `linkMessage` call when the original message's
|
|
@@ -524,6 +529,78 @@ export async function handleChannelInbound({
|
|
|
524
529
|
});
|
|
525
530
|
}
|
|
526
531
|
|
|
532
|
+
// ── Actor role resolution ──
|
|
533
|
+
// Uses shared channel-agnostic resolution so all ingress paths classify
|
|
534
|
+
// guardian vs non-guardian actors the same way.
|
|
535
|
+
const trustCtx: TrustContext = resolveTrustContext({
|
|
536
|
+
assistantId: canonicalAssistantId,
|
|
537
|
+
sourceChannel,
|
|
538
|
+
conversationExternalId,
|
|
539
|
+
actorExternalId: rawSenderId,
|
|
540
|
+
actorUsername: body.actorUsername,
|
|
541
|
+
actorDisplayName: body.actorDisplayName,
|
|
542
|
+
});
|
|
543
|
+
|
|
544
|
+
const diskPressureDecision = classifyDiskPressureTurnPolicy(
|
|
545
|
+
getDiskPressureStatus(),
|
|
546
|
+
{
|
|
547
|
+
sourceChannel,
|
|
548
|
+
sourceInterface,
|
|
549
|
+
trustContext: {
|
|
550
|
+
sourceChannel: trustCtx.sourceChannel,
|
|
551
|
+
trustClass: trustCtx.trustClass,
|
|
552
|
+
},
|
|
553
|
+
},
|
|
554
|
+
);
|
|
555
|
+
if (diskPressureDecision.action === "block") {
|
|
556
|
+
if (!result.duplicate) {
|
|
557
|
+
clearPayload(result.eventId);
|
|
558
|
+
markProcessed(result.eventId);
|
|
559
|
+
}
|
|
560
|
+
log.info(
|
|
561
|
+
{
|
|
562
|
+
conversationId: result.conversationId,
|
|
563
|
+
eventId: result.eventId,
|
|
564
|
+
duplicate: result.duplicate,
|
|
565
|
+
reason: diskPressureDecision.reason,
|
|
566
|
+
trustClass: trustCtx.trustClass,
|
|
567
|
+
},
|
|
568
|
+
"Channel inbound blocked during disk pressure cleanup mode",
|
|
569
|
+
);
|
|
570
|
+
|
|
571
|
+
if (replyCallbackUrl && !result.duplicate) {
|
|
572
|
+
const replyPayload: Parameters<typeof deliverChannelReply>[1] = {
|
|
573
|
+
chatId: conversationExternalId,
|
|
574
|
+
text: DISK_PRESSURE_REMOTE_BLOCK_REPLY,
|
|
575
|
+
assistantId: canonicalAssistantId,
|
|
576
|
+
};
|
|
577
|
+
if (sourceChannel === "slack" && (canonicalSenderId ?? rawSenderId)) {
|
|
578
|
+
replyPayload.ephemeral = true;
|
|
579
|
+
replyPayload.user = (canonicalSenderId ?? rawSenderId)!;
|
|
580
|
+
}
|
|
581
|
+
try {
|
|
582
|
+
await deliverChannelReply(replyCallbackUrl, replyPayload);
|
|
583
|
+
} catch (err) {
|
|
584
|
+
log.warn(
|
|
585
|
+
{
|
|
586
|
+
err,
|
|
587
|
+
conversationId: result.conversationId,
|
|
588
|
+
eventId: result.eventId,
|
|
589
|
+
},
|
|
590
|
+
"Failed to deliver disk pressure block reply",
|
|
591
|
+
);
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
return {
|
|
596
|
+
accepted: true,
|
|
597
|
+
duplicate: result.duplicate,
|
|
598
|
+
eventId: result.eventId,
|
|
599
|
+
diskPressure: "blocked",
|
|
600
|
+
reason: diskPressureDecision.reason,
|
|
601
|
+
};
|
|
602
|
+
}
|
|
603
|
+
|
|
527
604
|
// ── Slack reaction handling ──
|
|
528
605
|
// Reactions arrive as regular `SlackInboundEvent`s with `callbackData`
|
|
529
606
|
// prefixed `reaction:` (added) or `reaction_removed:` (removed).
|
|
@@ -735,18 +812,6 @@ export async function handleChannelInbound({
|
|
|
735
812
|
// which handles request code matching, callback parsing, and NL classification
|
|
736
813
|
// against canonical_guardian_requests.
|
|
737
814
|
|
|
738
|
-
// ── Actor role resolution ──
|
|
739
|
-
// Uses shared channel-agnostic resolution so all ingress paths classify
|
|
740
|
-
// guardian vs non-guardian actors the same way.
|
|
741
|
-
const trustCtx: TrustContext = resolveTrustContext({
|
|
742
|
-
assistantId: canonicalAssistantId,
|
|
743
|
-
sourceChannel,
|
|
744
|
-
conversationExternalId,
|
|
745
|
-
actorExternalId: rawSenderId,
|
|
746
|
-
actorUsername: body.actorUsername,
|
|
747
|
-
actorDisplayName: body.actorDisplayName,
|
|
748
|
-
});
|
|
749
|
-
|
|
750
815
|
// ── Canonical guardian reply router ──
|
|
751
816
|
const guardianReplyResult = await handleGuardianReplyIntercept({
|
|
752
817
|
isDuplicate: result.duplicate,
|
|
@@ -174,6 +174,9 @@ export async function handleGuardianReplyIntercept(
|
|
|
174
174
|
eventId,
|
|
175
175
|
canonicalRouter: routerResult.type,
|
|
176
176
|
requestId: routerResult.requestId,
|
|
177
|
+
...(routerResult.activatedContact
|
|
178
|
+
? { activatedContact: routerResult.activatedContact }
|
|
179
|
+
: {}),
|
|
177
180
|
}),
|
|
178
181
|
skipApprovalInterception: false,
|
|
179
182
|
};
|
|
@@ -37,9 +37,11 @@ import { ROUTES as CONVERSATION_QUERY_ROUTES } from "./conversation-query-routes
|
|
|
37
37
|
import { ROUTES as CONVERSATION_MESSAGE_ROUTES } from "./conversation-routes.js";
|
|
38
38
|
import { ROUTES as CONVERSATION_STARTER_ROUTES } from "./conversation-starter-routes.js";
|
|
39
39
|
import { ROUTES as CREDENTIAL_PROMPT_ROUTES } from "./credential-prompt-routes.js";
|
|
40
|
+
import { ROUTES as DEBUG_BASH_ROUTES } from "./debug-bash-routes.js";
|
|
40
41
|
import { ROUTES as DEBUG_ROUTES } from "./debug-routes.js";
|
|
41
42
|
import { ROUTES as DEFER_ROUTES } from "./defer-routes.js";
|
|
42
43
|
import { ROUTES as DIAGNOSTICS_ROUTES } from "./diagnostics-routes.js";
|
|
44
|
+
import { ROUTES as DISK_PRESSURE_ROUTES } from "./disk-pressure-routes.js";
|
|
43
45
|
import { ROUTES as DOCUMENT_ROUTES } from "./documents-routes.js";
|
|
44
46
|
import { ROUTES as EVENTS_ROUTES } from "./events-routes.js";
|
|
45
47
|
import { ROUTES as FILING_ROUTES } from "./filing-routes.js";
|
|
@@ -74,6 +76,7 @@ import { ROUTES as MIGRATION_ROLLBACK_ROUTES } from "./migration-rollback-routes
|
|
|
74
76
|
import { ROUTES as MIGRATION_ROUTES } from "./migration-routes.js";
|
|
75
77
|
import { ROUTES as NOTIFICATION_ROUTES } from "./notification-routes.js";
|
|
76
78
|
import { ROUTES as OAUTH_APPS_ROUTES } from "./oauth-apps.js";
|
|
79
|
+
import { ROUTES as OAUTH_CONNECT_ROUTES } from "./oauth-connect-routes.js";
|
|
77
80
|
import { ROUTES as OAUTH_PROVIDERS_ROUTES } from "./oauth-providers.js";
|
|
78
81
|
import { ROUTES as PLAYGROUND_ROUTES } from "./playground/index.js";
|
|
79
82
|
import { ROUTES as PROFILER_ROUTES } from "./profiler-routes.js";
|
|
@@ -137,8 +140,10 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
137
140
|
...DEFER_ROUTES,
|
|
138
141
|
...CONVERSATION_QUERY_ROUTES,
|
|
139
142
|
...CONVERSATION_STARTER_ROUTES,
|
|
143
|
+
...DEBUG_BASH_ROUTES,
|
|
140
144
|
...DEBUG_ROUTES,
|
|
141
145
|
...DIAGNOSTICS_ROUTES,
|
|
146
|
+
...DISK_PRESSURE_ROUTES,
|
|
142
147
|
...DOCUMENT_ROUTES,
|
|
143
148
|
...EVENTS_ROUTES,
|
|
144
149
|
...FILING_ROUTES,
|
|
@@ -159,6 +164,7 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
159
164
|
...INTERFACE_ROUTES,
|
|
160
165
|
...INTERNAL_OAUTH_ROUTES,
|
|
161
166
|
...MCP_AUTH_ROUTES,
|
|
167
|
+
...OAUTH_CONNECT_ROUTES,
|
|
162
168
|
...INTERNAL_TWILIO_ROUTES,
|
|
163
169
|
...LOG_EXPORT_ROUTES,
|
|
164
170
|
...LLM_CALL_SITES_ROUTES,
|
|
@@ -6,6 +6,13 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { beforeAll, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
8
8
|
|
|
9
|
+
import { _setOverridesForTesting } from "../../config/assistant-feature-flags.js";
|
|
10
|
+
|
|
11
|
+
// This test exercises v1 memory CRUD routes. The `memory-v2-enabled` flag
|
|
12
|
+
// (registry default `true`) flips memory routing to v2 — disable it here so
|
|
13
|
+
// the v1 paths under test stay active.
|
|
14
|
+
_setOverridesForTesting({ "memory-v2-enabled": false });
|
|
15
|
+
|
|
9
16
|
mock.module("../../util/logger.js", () => ({
|
|
10
17
|
getLogger: () =>
|
|
11
18
|
new Proxy({} as Record<string, unknown>, {
|
|
@@ -68,11 +75,7 @@ import { eq } from "drizzle-orm";
|
|
|
68
75
|
import { getDb } from "../../memory/db-connection.js";
|
|
69
76
|
import { initializeDb } from "../../memory/db-init.js";
|
|
70
77
|
import { memoryGraphNodes, memoryJobs } from "../../memory/schema.js";
|
|
71
|
-
import {
|
|
72
|
-
BadRequestError,
|
|
73
|
-
ConflictError,
|
|
74
|
-
NotFoundError,
|
|
75
|
-
} from "./errors.js";
|
|
78
|
+
import { BadRequestError, ConflictError, NotFoundError } from "./errors.js";
|
|
76
79
|
import { ROUTES } from "./memory-item-routes.js";
|
|
77
80
|
import type { RouteDefinition } from "./types.js";
|
|
78
81
|
|
|
@@ -269,7 +272,9 @@ describe("Memory Item Routes", () => {
|
|
|
269
272
|
content: "s2\nst2",
|
|
270
273
|
});
|
|
271
274
|
|
|
272
|
-
const res = await callHandler(route, {
|
|
275
|
+
const res = await callHandler(route, {
|
|
276
|
+
queryParams: { kind: "semantic" },
|
|
277
|
+
});
|
|
273
278
|
const body = (await res.json()) as {
|
|
274
279
|
items: Array<{ id: string }>;
|
|
275
280
|
total: number;
|
|
@@ -319,7 +324,9 @@ describe("Memory Item Routes", () => {
|
|
|
319
324
|
lastAccessed: 3000,
|
|
320
325
|
});
|
|
321
326
|
|
|
322
|
-
const res = await callHandler(route, {
|
|
327
|
+
const res = await callHandler(route, {
|
|
328
|
+
queryParams: { limit: "1", offset: "1" },
|
|
329
|
+
});
|
|
323
330
|
const body = (await res.json()) as {
|
|
324
331
|
items: Array<{ id: string }>;
|
|
325
332
|
total: number;
|
|
@@ -344,7 +351,9 @@ describe("Memory Item Routes", () => {
|
|
|
344
351
|
created: 1000,
|
|
345
352
|
});
|
|
346
353
|
|
|
347
|
-
const res = await callHandler(route, {
|
|
354
|
+
const res = await callHandler(route, {
|
|
355
|
+
queryParams: { sort: "firstSeenAt", order: "asc" },
|
|
356
|
+
});
|
|
348
357
|
const body = (await res.json()) as {
|
|
349
358
|
items: Array<{ id: string }>;
|
|
350
359
|
};
|
|
@@ -366,7 +375,9 @@ describe("Memory Item Routes", () => {
|
|
|
366
375
|
significance: 0.9,
|
|
367
376
|
});
|
|
368
377
|
|
|
369
|
-
const res = await callHandler(route, {
|
|
378
|
+
const res = await callHandler(route, {
|
|
379
|
+
queryParams: { sort: "importance", order: "desc" },
|
|
380
|
+
});
|
|
370
381
|
const body = (await res.json()) as {
|
|
371
382
|
items: Array<{ id: string }>;
|
|
372
383
|
};
|
|
@@ -418,7 +429,9 @@ describe("Memory Item Routes", () => {
|
|
|
418
429
|
},
|
|
419
430
|
];
|
|
420
431
|
|
|
421
|
-
const res = await callHandler(route, {
|
|
432
|
+
const res = await callHandler(route, {
|
|
433
|
+
queryParams: { search: "alice" },
|
|
434
|
+
});
|
|
422
435
|
const body = (await res.json()) as {
|
|
423
436
|
items: Array<{ id: string }>;
|
|
424
437
|
total: number;
|
|
@@ -503,7 +516,9 @@ describe("Memory Item Routes", () => {
|
|
|
503
516
|
];
|
|
504
517
|
|
|
505
518
|
// Request page 2 (offset=1, limit=1)
|
|
506
|
-
const res = await callHandler(route, {
|
|
519
|
+
const res = await callHandler(route, {
|
|
520
|
+
queryParams: { search: "item", limit: "1", offset: "1" },
|
|
521
|
+
});
|
|
507
522
|
const body = (await res.json()) as {
|
|
508
523
|
items: Array<{ id: string }>;
|
|
509
524
|
total: number;
|
|
@@ -563,7 +578,10 @@ describe("Memory Item Routes", () => {
|
|
|
563
578
|
content: "dark mode\nPrefers dark mode",
|
|
564
579
|
});
|
|
565
580
|
|
|
566
|
-
const res = await callHandler(route, {
|
|
581
|
+
const res = await callHandler(route, {
|
|
582
|
+
queryParams: {},
|
|
583
|
+
pathParams: { id: "i1" },
|
|
584
|
+
});
|
|
567
585
|
expect(res.status).toBe(200);
|
|
568
586
|
const body = (await res.json()) as {
|
|
569
587
|
item: { id: string; subject: string };
|
|
@@ -573,7 +591,10 @@ describe("Memory Item Routes", () => {
|
|
|
573
591
|
});
|
|
574
592
|
|
|
575
593
|
test("returns 404 for non-existent item", async () => {
|
|
576
|
-
const res = await callHandler(route, {
|
|
594
|
+
const res = await callHandler(route, {
|
|
595
|
+
queryParams: {},
|
|
596
|
+
pathParams: { id: "nonexistent" },
|
|
597
|
+
});
|
|
577
598
|
expect(res.status).toBe(404);
|
|
578
599
|
});
|
|
579
600
|
|
|
@@ -584,7 +605,10 @@ describe("Memory Item Routes", () => {
|
|
|
584
605
|
content: "some content\nsome statement",
|
|
585
606
|
});
|
|
586
607
|
|
|
587
|
-
const res = await callHandler(route, {
|
|
608
|
+
const res = await callHandler(route, {
|
|
609
|
+
queryParams: {},
|
|
610
|
+
pathParams: { id: "i1" },
|
|
611
|
+
});
|
|
588
612
|
const body = (await res.json()) as {
|
|
589
613
|
item: { supersedes: unknown; supersededBy: unknown };
|
|
590
614
|
};
|
|
@@ -862,7 +886,9 @@ describe("Memory Item Routes", () => {
|
|
|
862
886
|
});
|
|
863
887
|
|
|
864
888
|
test("returns 404 for non-existent item", async () => {
|
|
865
|
-
const res = await callHandler(route, {
|
|
889
|
+
const res = await callHandler(route, {
|
|
890
|
+
pathParams: { id: "nonexistent" },
|
|
891
|
+
});
|
|
866
892
|
expect(res.status).toBe(404);
|
|
867
893
|
});
|
|
868
894
|
|
|
@@ -22,6 +22,10 @@ import {
|
|
|
22
22
|
enqueueMemoryJob,
|
|
23
23
|
type MemoryJobType,
|
|
24
24
|
} from "../../memory/jobs-store.js";
|
|
25
|
+
import {
|
|
26
|
+
type ConceptFrequencyResponse,
|
|
27
|
+
getConceptFrequencySummary,
|
|
28
|
+
} from "../../memory/memory-v2-concept-frequency.js";
|
|
25
29
|
import {
|
|
26
30
|
getEdgeIndex,
|
|
27
31
|
totalEdgeCount,
|
|
@@ -459,6 +463,24 @@ async function handleExplainSimilarity({
|
|
|
459
463
|
};
|
|
460
464
|
}
|
|
461
465
|
|
|
466
|
+
// ── Concept injection frequency (debug-only) ────────────────────────────
|
|
467
|
+
|
|
468
|
+
const MemoryV2ConceptFrequencyParams = z
|
|
469
|
+
.object({
|
|
470
|
+
conversationId: z.string().min(1).optional(),
|
|
471
|
+
sinceMs: z.number().int().nonnegative().optional(),
|
|
472
|
+
})
|
|
473
|
+
.strict();
|
|
474
|
+
|
|
475
|
+
async function handleConceptFrequency({
|
|
476
|
+
body = {},
|
|
477
|
+
}: RouteHandlerArgs): Promise<ConceptFrequencyResponse> {
|
|
478
|
+
const { conversationId, sinceMs } =
|
|
479
|
+
MemoryV2ConceptFrequencyParams.parse(body);
|
|
480
|
+
const workspaceDir = getWorkspaceDir();
|
|
481
|
+
return getConceptFrequencySummary(workspaceDir, { conversationId, sinceMs });
|
|
482
|
+
}
|
|
483
|
+
|
|
462
484
|
// ── Fit anisotropy calibration ──────────────────────────────────────────
|
|
463
485
|
|
|
464
486
|
const MemoryV2FitAnisotropyParams = z
|
|
@@ -614,6 +636,17 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
614
636
|
tags: ["memory"],
|
|
615
637
|
requestBody: MemoryV2RebuildCorpusStatsParams,
|
|
616
638
|
},
|
|
639
|
+
{
|
|
640
|
+
operationId: "memory_v2_concept_frequency",
|
|
641
|
+
method: "POST",
|
|
642
|
+
endpoint: "memory/v2/concept-frequency",
|
|
643
|
+
handler: handleConceptFrequency,
|
|
644
|
+
summary: "Aggregate per-concept injection frequency from activation logs",
|
|
645
|
+
description:
|
|
646
|
+
"Debug-only. Aggregates the existing memory_v2_activation_logs table by (slug, status) and cross-references on-disk concept pages so an operator can see which concepts get injected often, which get scored but rejected, and which on-disk pages never even surface as candidates. Optional filters: conversationId narrows to a single conversation; sinceMs restricts to logs created at-or-after the given epoch ms timestamp.",
|
|
647
|
+
tags: ["memory"],
|
|
648
|
+
requestBody: MemoryV2ConceptFrequencyParams,
|
|
649
|
+
},
|
|
617
650
|
{
|
|
618
651
|
operationId: "memory_v2_fit_anisotropy",
|
|
619
652
|
method: "POST",
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal routes for daemon-owned OAuth connect flows (CLI gateway transport fix).
|
|
3
|
+
*
|
|
4
|
+
* POST internal/oauth/connect/start — starts the flow in the daemon, returns auth URL
|
|
5
|
+
* GET internal/oauth/connect/status/:state — polls current flow status
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { z } from "zod";
|
|
9
|
+
|
|
10
|
+
import { orchestrateOAuthConnect } from "../../oauth/connect-orchestrator.js";
|
|
11
|
+
import {
|
|
12
|
+
getOAuthConnectState,
|
|
13
|
+
setOAuthConnectComplete,
|
|
14
|
+
setOAuthConnectError,
|
|
15
|
+
setOAuthConnectPending,
|
|
16
|
+
} from "../../oauth/oauth-connect-state.js";
|
|
17
|
+
import { getLogger } from "../../util/logger.js";
|
|
18
|
+
import { BadRequestError, InternalError, NotFoundError } from "./errors.js";
|
|
19
|
+
import type { RouteDefinition } from "./types.js";
|
|
20
|
+
|
|
21
|
+
const log = getLogger("oauth-connect-routes");
|
|
22
|
+
|
|
23
|
+
async function handleOAuthConnectStart({
|
|
24
|
+
body,
|
|
25
|
+
}: {
|
|
26
|
+
body?: Record<string, unknown>;
|
|
27
|
+
}): Promise<{ auth_url: string; state: string }> {
|
|
28
|
+
const {
|
|
29
|
+
service,
|
|
30
|
+
clientId,
|
|
31
|
+
clientSecret,
|
|
32
|
+
callbackTransport,
|
|
33
|
+
requestedScopes,
|
|
34
|
+
} = (body ?? {}) as {
|
|
35
|
+
service: string;
|
|
36
|
+
clientId: string;
|
|
37
|
+
clientSecret?: string;
|
|
38
|
+
callbackTransport?: string;
|
|
39
|
+
requestedScopes?: string[];
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
if (!service) throw new BadRequestError("service is required");
|
|
43
|
+
if (!clientId) throw new BadRequestError("clientId is required");
|
|
44
|
+
if (callbackTransport !== "loopback" && callbackTransport !== "gateway") {
|
|
45
|
+
throw new BadRequestError(
|
|
46
|
+
'callbackTransport must be "loopback" or "gateway"',
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Capture resolvedState separately so the onDeferredComplete closure can
|
|
51
|
+
// reference it without a reference-before-assignment risk (the fire-and-forget
|
|
52
|
+
// tail only fires after the await resolves, by which point resolvedState is set).
|
|
53
|
+
// eslint-disable-next-line prefer-const -- intentional forward-declared binding
|
|
54
|
+
let resolvedState: string | undefined;
|
|
55
|
+
|
|
56
|
+
let result: Awaited<ReturnType<typeof orchestrateOAuthConnect>>;
|
|
57
|
+
try {
|
|
58
|
+
result = await orchestrateOAuthConnect({
|
|
59
|
+
service,
|
|
60
|
+
clientId,
|
|
61
|
+
clientSecret,
|
|
62
|
+
callbackTransport,
|
|
63
|
+
...(requestedScopes ? { requestedScopes } : {}),
|
|
64
|
+
isInteractive: false,
|
|
65
|
+
onDeferredComplete: (r) => {
|
|
66
|
+
if (!resolvedState) return;
|
|
67
|
+
if (r.success) {
|
|
68
|
+
setOAuthConnectComplete(resolvedState, r.service, r.accountInfo, r.grantedScopes);
|
|
69
|
+
} else {
|
|
70
|
+
setOAuthConnectError(resolvedState, r.service, r.error ?? "OAuth connect failed");
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
} catch (err) {
|
|
75
|
+
throw new InternalError(err instanceof Error ? err.message : String(err));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (!result.success) {
|
|
79
|
+
throw new InternalError(result.error);
|
|
80
|
+
}
|
|
81
|
+
if (!result.deferred) {
|
|
82
|
+
throw new InternalError("Orchestrator returned non-deferred result");
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
resolvedState = result.state;
|
|
86
|
+
setOAuthConnectPending(result.state, service);
|
|
87
|
+
log.info({ state: result.state, service }, "oauth connect flow started");
|
|
88
|
+
return { auth_url: result.authorizeUrl, state: result.state };
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function handleOAuthConnectStatus({
|
|
92
|
+
pathParams,
|
|
93
|
+
}: {
|
|
94
|
+
pathParams?: Record<string, string>;
|
|
95
|
+
}): {
|
|
96
|
+
status: "pending" | "complete" | "error";
|
|
97
|
+
service: string;
|
|
98
|
+
account_info?: string;
|
|
99
|
+
granted_scopes?: string[];
|
|
100
|
+
error?: string;
|
|
101
|
+
} {
|
|
102
|
+
const { state } = pathParams as { state: string };
|
|
103
|
+
const flowState = getOAuthConnectState(state);
|
|
104
|
+
|
|
105
|
+
if (flowState === null) {
|
|
106
|
+
throw new NotFoundError(`No active OAuth connect flow for state "${state}"`);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (flowState.status === "pending") return { status: "pending", service: flowState.service };
|
|
110
|
+
if (flowState.status === "complete") {
|
|
111
|
+
return {
|
|
112
|
+
status: "complete",
|
|
113
|
+
service: flowState.service,
|
|
114
|
+
...(flowState.accountInfo ? { account_info: flowState.accountInfo } : {}),
|
|
115
|
+
...(flowState.grantedScopes ? { granted_scopes: flowState.grantedScopes } : {}),
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
return { status: "error", service: flowState.service, error: flowState.error };
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export const ROUTES: RouteDefinition[] = [
|
|
122
|
+
{
|
|
123
|
+
operationId: "internal_oauth_connect_start",
|
|
124
|
+
endpoint: "internal/oauth/connect/start",
|
|
125
|
+
method: "POST",
|
|
126
|
+
summary: "Start daemon-owned OAuth connect flow",
|
|
127
|
+
description:
|
|
128
|
+
"Starts an OAuth connect flow in the daemon and returns the authorization URL for the CLI to open in the browser.",
|
|
129
|
+
tags: ["internal"],
|
|
130
|
+
requestBody: z.object({
|
|
131
|
+
service: z.string(),
|
|
132
|
+
clientId: z.string(),
|
|
133
|
+
clientSecret: z.string().optional(),
|
|
134
|
+
callbackTransport: z.enum(["loopback", "gateway"]),
|
|
135
|
+
requestedScopes: z.array(z.string()).optional(),
|
|
136
|
+
}),
|
|
137
|
+
handler: handleOAuthConnectStart,
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
operationId: "internal_oauth_connect_status",
|
|
141
|
+
endpoint: "internal/oauth/connect/status/:state",
|
|
142
|
+
method: "GET",
|
|
143
|
+
summary: "Poll daemon OAuth connect flow status",
|
|
144
|
+
description:
|
|
145
|
+
"Returns the current status of an in-flight daemon-owned OAuth connect flow (pending/complete/error).",
|
|
146
|
+
tags: ["internal"],
|
|
147
|
+
pathParams: [{ name: "state" }],
|
|
148
|
+
additionalResponses: {
|
|
149
|
+
"404": { description: "No active OAuth connect flow for the given state token" },
|
|
150
|
+
},
|
|
151
|
+
handler: handleOAuthConnectStatus,
|
|
152
|
+
},
|
|
153
|
+
];
|
|
@@ -79,7 +79,7 @@ export function normalizeTelegramDestination(destination: string): string {
|
|
|
79
79
|
// Input / output types
|
|
80
80
|
// ---------------------------------------------------------------------------
|
|
81
81
|
|
|
82
|
-
|
|
82
|
+
interface StartOutboundParams {
|
|
83
83
|
channel: ChannelId;
|
|
84
84
|
destination?: string;
|
|
85
85
|
rebind?: boolean;
|
|
@@ -87,13 +87,13 @@ export interface StartOutboundParams {
|
|
|
87
87
|
originConversationId?: string;
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
-
|
|
90
|
+
interface ResendOutboundParams {
|
|
91
91
|
channel: ChannelId;
|
|
92
92
|
/** Origin conversation ID so completion/failure pointers can route back on resend. */
|
|
93
93
|
originConversationId?: string;
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
-
|
|
96
|
+
interface CancelOutboundParams {
|
|
97
97
|
channel: ChannelId;
|
|
98
98
|
}
|
|
99
99
|
|
|
@@ -102,7 +102,7 @@ export interface CancelOutboundParams {
|
|
|
102
102
|
* Maps 1:1 with the fields in ChannelVerificationSessionResponse minus the
|
|
103
103
|
* `type` discriminant.
|
|
104
104
|
*/
|
|
105
|
-
|
|
105
|
+
interface OutboundActionResult {
|
|
106
106
|
success: boolean;
|
|
107
107
|
error?: string;
|
|
108
108
|
message?: string;
|
|
@@ -38,21 +38,53 @@ export async function runScript(
|
|
|
38
38
|
env: buildSanitizedEnv(),
|
|
39
39
|
});
|
|
40
40
|
|
|
41
|
-
//
|
|
41
|
+
// Start consuming streams immediately so buffered output is available even on timeout.
|
|
42
|
+
// When the process is killed the pipe fds close and these promises resolve on their own.
|
|
43
|
+
const stdoutPromise = new Response(proc.stdout).text();
|
|
44
|
+
const stderrPromise = new Response(proc.stderr).text();
|
|
45
|
+
|
|
46
|
+
let timedOut = false;
|
|
47
|
+
|
|
42
48
|
const timeoutPromise = new Promise<never>((_, reject) => {
|
|
43
49
|
const timer = setTimeout(() => {
|
|
50
|
+
timedOut = true;
|
|
44
51
|
proc.kill("SIGKILL");
|
|
45
52
|
reject(new Error(`Script timed out after ${timeoutMs}ms`));
|
|
46
53
|
}, timeoutMs);
|
|
47
54
|
timer.unref();
|
|
48
|
-
// Clean up timer if process finishes first
|
|
49
55
|
proc.exited.then(() => clearTimeout(timer));
|
|
50
56
|
});
|
|
51
57
|
|
|
52
|
-
|
|
58
|
+
/** How long to wait for pipes to drain after SIGKILL before giving up. */
|
|
59
|
+
const DRAIN_TIMEOUT_MS = 5_000;
|
|
60
|
+
|
|
61
|
+
let exitCode: number;
|
|
62
|
+
try {
|
|
63
|
+
exitCode = await Promise.race([proc.exited, timeoutPromise]);
|
|
64
|
+
} catch (err) {
|
|
65
|
+
if (!timedOut) throw err;
|
|
66
|
+
// Collect whatever the process wrote before it was killed.
|
|
67
|
+
// Race each stream against a short drain window — if a background child
|
|
68
|
+
// process inherited the pipe fd, the stream would otherwise never reach
|
|
69
|
+
// EOF and block the scheduler tick indefinitely.
|
|
70
|
+
const empty = (ms: number): Promise<string> =>
|
|
71
|
+
new Promise((resolve) => setTimeout(() => resolve(""), ms));
|
|
72
|
+
const [stdoutStr, stderrStr] = await Promise.all([
|
|
73
|
+
Promise.race([stdoutPromise, empty(DRAIN_TIMEOUT_MS)]),
|
|
74
|
+
Promise.race([stderrPromise, empty(DRAIN_TIMEOUT_MS)]),
|
|
75
|
+
]);
|
|
76
|
+
const stdout = truncate(stdoutStr);
|
|
77
|
+
const timeoutMsg = `Script timed out after ${timeoutMs}ms`;
|
|
78
|
+
const stderr = truncate(stderrStr ? `${timeoutMsg}\n${stderrStr}` : timeoutMsg);
|
|
79
|
+
log.info(
|
|
80
|
+
{ command, timedOut: true, stdoutLen: stdout.length },
|
|
81
|
+
"Script timed out",
|
|
82
|
+
);
|
|
83
|
+
return { exitCode: 124, stdout, stderr };
|
|
84
|
+
}
|
|
53
85
|
|
|
54
|
-
const stdout = truncate(await
|
|
55
|
-
const stderr = truncate(await
|
|
86
|
+
const stdout = truncate(await stdoutPromise);
|
|
87
|
+
const stderr = truncate(await stderrPromise);
|
|
56
88
|
|
|
57
89
|
log.info(
|
|
58
90
|
{ command, exitCode, stdoutLen: stdout.length, stderrLen: stderr.length },
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
import {
|
|
2
|
+
checkDiskPressureBackgroundGate,
|
|
3
|
+
diskPressureBackgroundSkipLogFields,
|
|
4
|
+
shouldLogDiskPressureBackgroundSkip,
|
|
5
|
+
} from "../daemon/disk-pressure-background-gate.js";
|
|
1
6
|
import { emitFeedEvent } from "../home/emit-feed-event.js";
|
|
2
7
|
import { bootstrapConversation } from "../memory/conversation-bootstrap.js";
|
|
3
8
|
import { getConversation } from "../memory/conversation-crud.js";
|
|
@@ -129,7 +134,7 @@ export function startScheduler(
|
|
|
129
134
|
};
|
|
130
135
|
}
|
|
131
136
|
|
|
132
|
-
async function runScheduleOnce(
|
|
137
|
+
export async function runScheduleOnce(
|
|
133
138
|
processMessage: ScheduleMessageProcessor,
|
|
134
139
|
notifyScheduleOneShot: ScheduleNotifyModeNotifier,
|
|
135
140
|
watcherNotifier?: WatcherNotifier,
|
|
@@ -139,6 +144,20 @@ async function runScheduleOnce(
|
|
|
139
144
|
const now = Date.now();
|
|
140
145
|
let processed = 0;
|
|
141
146
|
|
|
147
|
+
const diskPressureGate = checkDiskPressureBackgroundGate("background-work");
|
|
148
|
+
if (diskPressureGate.action === "skip") {
|
|
149
|
+
if (shouldLogDiskPressureBackgroundSkip("scheduler")) {
|
|
150
|
+
log.warn(
|
|
151
|
+
{
|
|
152
|
+
source: "schedule",
|
|
153
|
+
...diskPressureBackgroundSkipLogFields(diskPressureGate),
|
|
154
|
+
},
|
|
155
|
+
"Schedule tick skipped during disk pressure cleanup mode",
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
return 0;
|
|
159
|
+
}
|
|
160
|
+
|
|
142
161
|
// ── Schedules (recurring cron/RRULE + one-shot) ─────────────────────
|
|
143
162
|
const jobs = claimDueSchedules(now);
|
|
144
163
|
for (const job of jobs) {
|