@vellumai/assistant 0.8.7 → 0.8.8-dev.202606052332.17fc8ea
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/Dockerfile +20 -4
- package/bun.lock +2 -2
- package/docker-entrypoint.sh +4 -2
- package/docker-init-apt-root.sh +3 -1
- package/docker-kata-apt-env.sh +3 -1
- package/docker-kata-runtime-family.sh +12 -0
- package/docs/architecture/memory.md +1 -1
- package/examples/plugins/echo/README.md +61 -66
- package/examples/plugins/echo/hooks/post-tool-use.ts +18 -0
- package/examples/plugins/echo/hooks/stop.ts +16 -0
- package/examples/plugins/echo/hooks/user-prompt-submit.ts +18 -0
- package/examples/plugins/echo/package.json +1 -2
- package/examples/plugins/echo/src/emit.ts +19 -0
- package/node_modules/@vellumai/skill-host-contracts/src/server-message.ts +3 -3
- package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +7 -6
- package/openapi.yaml +3378 -335
- package/package.json +2 -2
- package/scripts/generate-openapi.ts +68 -41
- package/src/__tests__/agent-loop-exit-reason.test.ts +35 -93
- package/src/__tests__/agent-loop-provider-error-recording.test.ts +1 -1
- package/src/__tests__/agent-loop.test.ts +37 -87
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +2 -0
- package/src/__tests__/annotate-activity-metadata.test.ts +262 -0
- package/src/__tests__/annotate-risk-options.test.ts +2 -3
- package/src/__tests__/anthropic-provider.test.ts +95 -2
- package/src/__tests__/app-control-flow.test.ts +1 -1
- package/src/__tests__/app-dir-path-guard.test.ts +1 -0
- package/src/__tests__/approval-routes-http.test.ts +4 -1
- package/src/__tests__/assistant-event-hub.test.ts +25 -0
- package/src/__tests__/assistant-events-sse-shed.test.ts +8 -0
- package/src/__tests__/{conversation-stream-state.test.ts → assistant-stream-state.test.ts} +252 -91
- package/src/__tests__/auth-fallback-events-store.test.ts +116 -0
- package/src/__tests__/background-workers-disk-pressure.test.ts +6 -0
- package/src/__tests__/btw-routes.test.ts +62 -3
- package/src/__tests__/build-persisted-content.test.ts +184 -0
- package/src/__tests__/catalog-files.test.ts +1 -1
- package/src/__tests__/channel-approval-routes.test.ts +1 -1
- package/src/__tests__/channel-approvals.test.ts +1 -1
- package/src/__tests__/clawhub-files.test.ts +1 -1
- package/src/__tests__/compaction-circuit.test.ts +258 -0
- package/src/__tests__/compaction-direct.test.ts +132 -0
- package/src/__tests__/compaction.benchmark.test.ts +0 -30
- package/src/__tests__/config-watcher.test.ts +1 -1
- package/src/__tests__/conversation-abort-tool-results.test.ts +57 -19
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +6 -5
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +10 -7
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +316 -1143
- package/src/__tests__/conversation-agent-loop.test.ts +638 -1655
- package/src/__tests__/conversation-analysis-routes.test.ts +6 -0
- package/src/__tests__/conversation-clean-command.test.ts +5 -2
- package/src/__tests__/conversation-history-web-search.test.ts +11 -1
- package/src/__tests__/conversation-pairing.test.ts +4 -31
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +6 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +30 -10
- package/src/__tests__/conversation-queue.test.ts +2 -0
- package/src/__tests__/conversation-routes-disk-view.test.ts +3 -0
- package/src/__tests__/conversation-routes-slash-commands.test.ts +6 -5
- package/src/__tests__/conversation-runtime-assembly.test.ts +310 -300
- package/src/__tests__/conversation-runtime-workspace.test.ts +105 -45
- package/src/__tests__/conversation-slash-commands.test.ts +8 -42
- package/src/__tests__/conversation-slash-queue.test.ts +6 -1
- package/src/__tests__/conversation-starter-routes.test.ts +14 -6
- package/src/__tests__/conversation-surfaces-action-delivery.test.ts +84 -0
- package/src/__tests__/conversation-sync-tags.test.ts +27 -15
- package/src/__tests__/conversation-title-service.test.ts +135 -2
- package/src/__tests__/conversation-workspace-cache-state.test.ts +17 -16
- package/src/__tests__/conversation-workspace-injection.test.ts +67 -2
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +7 -6
- package/src/__tests__/conversations-import-system-filter.test.ts +101 -0
- package/src/__tests__/cross-provider-web-search.test.ts +214 -1
- package/src/__tests__/db-acp-history.test.ts +101 -0
- package/src/__tests__/db-schedule-syntax-migration.test.ts +5 -0
- package/src/__tests__/dm-persistence.test.ts +5 -1
- package/src/__tests__/dynamic-page-surface.test.ts +31 -0
- package/src/__tests__/empty-response-hook.test.ts +304 -0
- package/src/__tests__/feature-flag-test-helpers.ts +2 -2
- package/src/__tests__/file-write-tool.test.ts +63 -0
- package/src/__tests__/gateway-only-guard.test.ts +12 -2
- package/src/__tests__/gemini-image-service.test.ts +13 -0
- package/src/__tests__/guardian-grant-minting.test.ts +1 -1
- package/src/__tests__/guardian-routing-invariants.test.ts +2 -4
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +1 -1
- package/src/__tests__/heartbeat-disk-pressure.test.ts +1 -0
- package/src/__tests__/heartbeat-service.test.ts +1 -0
- package/src/__tests__/helpers/mock-provider.ts +110 -0
- package/src/__tests__/helpers/native-web-search-harness.ts +129 -0
- package/src/__tests__/history-repair-hook.test.ts +1 -0
- package/src/__tests__/host-app-control-routes.test.ts +1 -1
- package/src/__tests__/host-cu-routes-targeted.test.ts +3 -3
- package/src/__tests__/identity-intro-cache.test.ts +12 -100
- package/src/__tests__/identity-routes.test.ts +248 -7
- package/src/__tests__/inbound-slack-persistence.test.ts +5 -1
- package/src/__tests__/injector-background-turn.test.ts +3 -9
- package/src/__tests__/injector-chain.test.ts +139 -275
- package/src/__tests__/injector-disk-pressure.test.ts +75 -41
- package/src/__tests__/injector-document-comments.test.ts +3 -3
- package/src/__tests__/injector-pkb-v2-silenced.test.ts +30 -22
- package/src/__tests__/injector-v3-suppression.test.ts +31 -37
- package/src/__tests__/internal-telemetry-routes.test.ts +109 -0
- package/src/__tests__/list-messages-hidden-metadata.test.ts +38 -0
- package/src/__tests__/list-messages-page-latest.test.ts +60 -0
- package/src/__tests__/list-messages-tool-merge.test.ts +20 -0
- package/src/__tests__/llm-usage-store.test.ts +223 -1
- package/src/__tests__/memory-retrieval-hook.test.ts +297 -0
- package/src/__tests__/memory-v2-static-injector.test.ts +103 -35
- package/src/__tests__/native-web-search.test.ts +191 -0
- package/src/__tests__/onboarding-template-contract.test.ts +2 -0
- package/src/__tests__/openai-image-service.test.ts +17 -0
- package/src/__tests__/openai-provider.test.ts +31 -1
- package/src/__tests__/{overflow-reduce-pipeline.test.ts → overflow-reduction-loop.test.ts} +64 -284
- package/src/__tests__/persist-unsendable-image.test.ts +215 -0
- package/src/__tests__/persistence-secret-redaction.test.ts +1 -0
- package/src/__tests__/pkb-autoinject.test.ts +2 -5
- package/src/__tests__/plugin-api-shim.test.ts +3 -6
- package/src/__tests__/plugin-bootstrap.test.ts +14 -40
- package/src/__tests__/plugin-registry.test.ts +3 -76
- package/src/__tests__/plugin-types.test.ts +0 -193
- package/src/__tests__/process-message-display-content.test.ts +6 -2
- package/src/__tests__/reaction-persistence.test.ts +1 -1
- package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +5 -1
- package/src/__tests__/resolve-trust-class.test.ts +4 -4
- package/src/__tests__/runtime-events-sse-reconnect.test.ts +60 -23
- package/src/__tests__/schedule-routes.test.ts +603 -2
- package/src/__tests__/schedule-store.test.ts +41 -0
- package/src/__tests__/schedule-tools.test.ts +35 -0
- package/src/__tests__/send-endpoint-busy.test.ts +4 -1
- package/src/__tests__/server-history-render.test.ts +314 -1
- package/src/__tests__/skill-feature-flags-integration.test.ts +33 -0
- package/src/__tests__/skillssh-files.test.ts +1 -1
- package/src/__tests__/subagent-call-site-routing.test.ts +1 -1
- package/src/__tests__/subagent-fork-notifications.test.ts +1 -3
- package/src/__tests__/subagent-fork-spawn.test.ts +1 -1
- package/src/__tests__/subagent-manager-notify.test.ts +1 -3
- package/src/__tests__/subagent-notify-parent.test.ts +1 -3
- package/src/__tests__/subagent-spawn-tool-fork.test.ts +1 -1
- package/src/__tests__/system-prompt.test.ts +20 -0
- package/src/__tests__/task-scheduler.test.ts +162 -1
- package/src/__tests__/terminal-tools.test.ts +6 -1
- package/src/__tests__/title-generate-hook.test.ts +319 -0
- package/src/__tests__/tool-error-hook.test.ts +278 -0
- package/src/__tests__/tool-preview-lifecycle.test.ts +468 -5
- package/src/__tests__/tool-result-metadata-plumbing.test.ts +1 -0
- package/src/__tests__/tool-result-truncate-hook.test.ts +127 -0
- package/src/__tests__/tool-result-truncation.test.ts +0 -2
- package/src/__tests__/ui-choice-copy-surfaces.test.ts +254 -0
- package/src/__tests__/ui-work-result-surface.test.ts +159 -0
- package/src/__tests__/usage-routes.test.ts +285 -1
- package/src/__tests__/user-plugin-loader.test.ts +54 -286
- package/src/__tests__/voice-session-bridge.test.ts +6 -3
- package/src/__tests__/web-search-backend-failure.test.ts +166 -0
- package/src/acp/__tests__/agent-process.test.ts +161 -0
- package/src/acp/__tests__/client-handler.test.ts +40 -0
- package/src/acp/__tests__/helpers/acp-history-db.ts +82 -0
- package/src/acp/__tests__/helpers/exec-file-stub.ts +101 -0
- package/src/acp/__tests__/prepare-agent-env.test.ts +137 -0
- package/src/acp/__tests__/session-manager-persistence.test.ts +95 -28
- package/src/acp/__tests__/session-manager-resume.test.ts +736 -0
- package/src/acp/agent-process.ts +61 -1
- package/src/acp/auto-install.test.ts +196 -0
- package/src/acp/auto-install.ts +177 -0
- package/src/acp/client-handler.ts +31 -0
- package/src/acp/feature-gate.test.ts +48 -0
- package/src/acp/feature-gate.ts +34 -0
- package/src/acp/prepare-agent-env.ts +83 -29
- package/src/acp/resolve-agent.test.ts +320 -7
- package/src/acp/resolve-agent.ts +182 -18
- package/src/acp/resume-hint.ts +25 -0
- package/src/acp/session-manager.ts +495 -73
- package/src/acp/types.ts +8 -0
- package/src/agent/compaction-circuit.ts +60 -102
- package/src/agent/loop.ts +362 -485
- package/src/api/events/assistant-thinking-delta.ts +33 -0
- package/src/api/events/tool-output-chunk.ts +45 -0
- package/src/api/events/tool-use-preview-start.ts +32 -0
- package/src/api/events/trace-event.ts +69 -0
- package/src/api/index.ts +48 -13
- package/src/api/responses/conversation-message.ts +374 -0
- package/src/approvals/guardian-request-resolvers.ts +1 -1
- package/src/avatar/__tests__/avatar-store.test.ts +34 -29
- package/src/background-wake/next-wake.ts +1 -0
- package/src/cli/commands/__tests__/notifications.test.ts +58 -14
- package/src/cli/commands/notifications.ts +112 -60
- package/src/config/__tests__/feature-flag-registry-guard.test.ts +2 -2
- package/src/config/acp-defaults.test.ts +10 -0
- package/src/config/acp-defaults.ts +6 -0
- package/src/config/assistant-feature-flags.ts +22 -11
- package/src/config/bundled-skills/acp/SKILL.md +83 -31
- package/src/config/bundled-skills/acp/TOOLS.json +4 -4
- package/src/config/bundled-skills/app-builder/SKILL.md +224 -398
- package/src/config/bundled-skills/app-builder/TOOLS.json +29 -0
- package/src/config/bundled-skills/app-builder/references/DESIGN_SYSTEM.md +48 -0
- package/src/config/bundled-skills/app-builder/references/RESPONSIVE.md +57 -0
- package/src/config/bundled-skills/app-builder/references/SLIDES.md +38 -0
- package/src/config/bundled-skills/app-builder/references/examples/README.md +17 -0
- package/src/config/bundled-skills/app-builder/references/examples/expense-tracker.md +515 -0
- package/src/config/bundled-skills/app-builder/references/examples/focus-timer.md +342 -0
- package/src/config/bundled-skills/app-builder/references/examples/habit-tracker.md +490 -0
- package/src/config/bundled-skills/app-builder/tools/app-list.ts +62 -0
- package/src/config/bundled-skills/document-editor/SKILL.md +28 -23
- package/src/config/bundled-skills/document-editor/TOOLS.json +1 -1
- package/src/config/bundled-skills/messaging/SKILL.md +0 -7
- package/src/config/bundled-tool-registry.ts +2 -0
- package/src/config/feature-flag-cache.ts +3 -3
- package/src/config/feature-flag-registry.json +48 -7
- package/src/config/schemas/__tests__/memory-v2.test.ts +1 -0
- package/src/config/schemas/__tests__/memory-v3.test.ts +25 -0
- package/src/config/schemas/heartbeat.ts +9 -0
- package/src/config/schemas/llm.ts +1 -0
- package/src/config/schemas/memory-v2.ts +8 -0
- package/src/config/schemas/memory-v3.ts +8 -0
- package/src/config/schemas/platform.ts +8 -0
- package/src/config/seed-inference-profiles.ts +2 -2
- package/src/config/skills.ts +13 -0
- package/src/context/compactor.ts +1 -1
- package/src/context/strip-injections.ts +128 -0
- package/src/context/token-estimator.ts +23 -0
- package/src/context/tool-result-truncation.ts +0 -23
- package/src/context/window-manager.ts +5 -7
- package/src/credential-execution/executable-discovery.ts +16 -0
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +6 -0
- package/src/daemon/__tests__/inference-profile-notification.test.ts +153 -0
- package/src/daemon/__tests__/native-web-search-metadata.test.ts +10 -8
- package/src/daemon/assistant-attachments.ts +1 -1
- package/src/daemon/config-watcher.ts +2 -2
- package/src/daemon/context-overflow-reducer.ts +0 -1
- package/src/daemon/conversation-agent-loop-handlers.ts +594 -153
- package/src/daemon/conversation-agent-loop.ts +301 -997
- package/src/daemon/conversation-history.ts +5 -4
- package/src/daemon/conversation-lifecycle.ts +3 -4
- package/src/daemon/conversation-messaging.ts +7 -6
- package/src/daemon/conversation-process.ts +11 -16
- package/src/daemon/conversation-registry.ts +159 -0
- package/src/daemon/conversation-runtime-assembly.ts +218 -398
- package/src/daemon/conversation-slash.ts +6 -25
- package/src/daemon/conversation-store.ts +9 -90
- package/src/daemon/conversation-surfaces.ts +222 -4
- package/src/daemon/conversation-tool-setup.ts +2 -29
- package/src/daemon/conversation-workspace.ts +17 -0
- package/src/daemon/conversation.ts +32 -20
- package/src/daemon/external-plugins-bootstrap.ts +17 -18
- package/src/daemon/handlers/config-a2a.ts +51 -36
- package/src/daemon/handlers/config-slack-channel.ts +20 -14
- package/src/daemon/handlers/config-telegram.ts +16 -2
- package/src/daemon/handlers/conversations.ts +3 -1
- package/src/daemon/handlers/shared.ts +156 -84
- package/src/daemon/handlers/skills.ts +42 -10
- package/src/daemon/lifecycle.ts +25 -0
- package/src/daemon/message-types/apps.ts +1 -29
- package/src/daemon/message-types/messages.ts +9 -57
- package/src/daemon/message-types/skills.ts +2 -0
- package/src/daemon/message-types/surfaces.ts +136 -3
- package/src/daemon/now-scratchpad.ts +21 -0
- package/src/daemon/orphan-reaper.test.ts +210 -0
- package/src/daemon/orphan-reaper.ts +240 -0
- package/src/daemon/overflow-reduction-loop.ts +230 -0
- package/src/daemon/persist-unsendable-image.ts +117 -0
- package/src/daemon/process-message.ts +1 -3
- package/src/daemon/server.ts +2 -0
- package/src/daemon/trace-emitter.ts +6 -4
- package/src/daemon/trust-context.ts +19 -0
- package/src/daemon/wake-target-adapter.ts +3 -1
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +3 -0
- package/src/heartbeat/heartbeat-run-store.ts +23 -1
- package/src/heartbeat/heartbeat-service.ts +26 -0
- package/src/home/home-greeting-cache.ts +24 -1
- package/src/ipc/__tests__/browser-ipc.test.ts +1 -1
- package/src/ipc/__tests__/ui-request-route.test.ts +3 -3
- package/src/ipc/gateway-client.test.ts +2 -2
- package/src/ipc/gateway-client.ts +3 -3
- package/src/ipc/skill-routes/__tests__/memory.test.ts +15 -0
- package/src/ipc/skill-routes/memory.ts +4 -2
- package/src/media/gemini-image-service.ts +15 -0
- package/src/media/openai-image-service.ts +14 -0
- package/src/media/types.ts +34 -0
- package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +56 -0
- package/src/memory/auth-fallback-events-store.ts +94 -0
- package/src/memory/conversation-starter-checkpoints.ts +1 -0
- package/src/memory/conversation-title-service.ts +65 -41
- package/src/memory/db-init.ts +6 -0
- package/src/memory/graph/__tests__/conversation-graph-memory-registry.test.ts +119 -0
- package/src/memory/graph/conversation-graph-memory.ts +65 -0
- package/src/memory/job-handlers/conversation-starters.ts +13 -2
- package/src/memory/jobs-store.ts +33 -0
- package/src/memory/jobs-worker.ts +32 -5
- package/src/memory/llm-usage-store.ts +224 -50
- package/src/memory/migrations/222-strip-placeholder-sentinels-from-messages.ts +6 -5
- package/src/memory/migrations/270-schedule-source-conversation.ts +13 -0
- package/src/memory/migrations/271-create-auth-fallback-events.ts +21 -0
- package/src/memory/migrations/272-acp-session-history-cwd.ts +36 -0
- package/src/memory/migrations/index.ts +3 -0
- package/src/memory/pkb/autoinject.ts +61 -0
- package/src/memory/pkb/context.ts +50 -0
- package/src/memory/pkb/types.ts +14 -0
- package/src/memory/schedule-attribution-sql.ts +104 -0
- package/src/memory/schema/acp.ts +4 -0
- package/src/memory/schema/infrastructure.ts +16 -0
- package/src/memory/usage-grouped-buckets.ts +6 -1
- package/src/memory/v2/__tests__/consolidation-job.test.ts +4 -4
- package/src/memory/v2/consolidation-job.ts +14 -5
- package/src/notifications/conversation-pairing.ts +8 -15
- package/src/notifications/decision-engine.ts +6 -3
- package/src/notifications/home-feed-side-effect.ts +12 -1
- package/src/permissions/prompter.ts +4 -0
- package/src/plugin-api/constants.ts +4 -0
- package/src/plugin-api/index.ts +7 -5
- package/src/plugin-api/types.ts +151 -1
- package/src/plugins/defaults/compaction/compact.ts +59 -0
- package/src/plugins/defaults/compaction/package.json +1 -1
- package/src/plugins/defaults/compaction/register.ts +8 -19
- package/src/plugins/defaults/empty-response/hooks/stop.ts +126 -0
- package/src/plugins/defaults/empty-response/register.ts +8 -13
- package/src/plugins/defaults/index.ts +2 -18
- package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +95 -0
- package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit-temp.ts +216 -0
- package/src/plugins/defaults/memory-retrieval/injector-chain.ts +35 -0
- package/src/plugins/defaults/{injectors/register.ts → memory-retrieval/injectors.ts} +288 -81
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/assign.test.ts +4 -4
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/health.test.ts +16 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/live-integration.test.ts +4 -4
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/maintain-job.test.ts +5 -5
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/orchestrate.test.ts +48 -12
- package/src/plugins/defaults/memory-v3-shadow/__tests__/provider-blocks.test.ts +13 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/reconcile.test.ts +2 -2
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/render-injection.test.ts +1 -1
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/router.test.ts +104 -32
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/selection-log-store.test.ts +8 -8
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/selector.test.ts +96 -30
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/shadow-plugin.test.ts +34 -16
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/assign.ts +5 -5
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/capabilities.ts +2 -2
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/health.ts +0 -0
- package/src/plugins/defaults/memory-v3-shadow/hooks/post-compact.ts +14 -0
- package/src/plugins/defaults/memory-v3-shadow/hooks/user-prompt-submit.ts +19 -0
- package/src/plugins/defaults/memory-v3-shadow/injector.ts +75 -0
- package/src/plugins/defaults/memory-v3-shadow/llm-retry.ts +32 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/maintain-job.ts +8 -8
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/orchestrate.ts +26 -14
- package/src/plugins/defaults/{llm-call → memory-v3-shadow}/package.json +2 -2
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/page-content.ts +2 -2
- package/src/plugins/defaults/memory-v3-shadow/provider-blocks.ts +26 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/reconcile.ts +3 -3
- package/src/plugins/defaults/memory-v3-shadow/register.ts +26 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/render-injection.ts +1 -1
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/router.ts +51 -45
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/selection-log-store.ts +4 -4
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/selector.ts +61 -46
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/shadow-plugin.ts +69 -99
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/tree.ts +1 -1
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/types.ts +8 -0
- package/src/plugins/defaults/title-generate/hooks/stop.ts +75 -0
- package/src/plugins/defaults/title-generate/hooks/user-prompt-submit.ts +35 -0
- package/src/plugins/defaults/title-generate/package.json +1 -1
- package/src/plugins/defaults/title-generate/register.ts +18 -18
- package/src/plugins/defaults/tool-error/hooks/post-tool-use.ts +118 -0
- package/src/plugins/defaults/tool-error/package.json +1 -1
- package/src/plugins/defaults/tool-error/register.ts +9 -21
- package/src/plugins/defaults/tool-result-truncate/hooks/post-tool-use.ts +32 -0
- package/src/plugins/defaults/tool-result-truncate/register.ts +10 -21
- package/src/plugins/defaults/tool-result-truncate/terminal.ts +37 -18
- package/src/plugins/external-api.ts +2 -2
- package/src/plugins/pipeline.ts +6 -305
- package/src/plugins/registry.ts +10 -55
- package/src/plugins/types.ts +62 -797
- package/src/plugins/user-loader.ts +30 -127
- package/src/proactive-artifact/aux-message-injector.ts +4 -4
- package/src/proactive-artifact/job.test.ts +8 -13
- package/src/prompts/__tests__/system-prompt.test.ts +42 -0
- package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +64 -0
- package/src/prompts/templates/BOOTSTRAP.md +2 -2
- package/src/prompts/templates/system-sections.ts +15 -0
- package/src/providers/anthropic/client.ts +37 -29
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +112 -0
- package/src/providers/openai/chat-completions-provider.ts +44 -0
- package/src/providers/openrouter/client.ts +1 -0
- package/src/providers/placeholder-sentinels.ts +35 -0
- package/src/runtime/__tests__/agent-wake.test.ts +10 -6
- package/src/runtime/__tests__/interactive-ui.test.ts +1 -1
- package/src/runtime/agent-wake.ts +2 -5
- package/src/runtime/assistant-event-hub.ts +37 -7
- package/src/runtime/{conversation-stream-state.ts → assistant-stream-state.ts} +132 -58
- package/src/runtime/channel-approvals.ts +1 -1
- package/src/runtime/http-router.ts +16 -21
- package/src/runtime/http-types.ts +16 -70
- package/src/runtime/interactive-ui.ts +1 -1
- package/src/runtime/pending-interactions.ts +1 -0
- package/src/runtime/routes/__tests__/acp-routes.test.ts +283 -55
- package/src/runtime/routes/__tests__/consolidation-routes.test.ts +265 -2
- package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +31 -1
- package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +6 -2
- package/src/runtime/routes/__tests__/surface-action-routes.test.ts +5 -4
- package/src/runtime/routes/__tests__/surface-content-routes.test.ts +4 -1
- package/src/runtime/routes/__tests__/tts-routes.test.ts +6 -2
- package/src/runtime/routes/acp-routes.test.ts +89 -25
- package/src/runtime/routes/acp-routes.ts +81 -29
- package/src/runtime/routes/app-management-routes.ts +6 -117
- package/src/runtime/routes/app-routes.ts +13 -15
- package/src/runtime/routes/approval-routes.ts +1 -1
- package/src/runtime/routes/attachment-routes.ts +26 -15
- package/src/runtime/routes/avatar-routes.ts +26 -0
- package/src/runtime/routes/browser-routes.ts +1 -1
- package/src/runtime/routes/browser-tabs-routes.ts +6 -10
- package/src/runtime/routes/btw-routes.ts +29 -23
- package/src/runtime/routes/consolidation-routes.ts +120 -20
- package/src/runtime/routes/conversation-cli-routes.ts +1 -1
- package/src/runtime/routes/conversation-list-routes.ts +1 -1
- package/src/runtime/routes/conversation-query-routes.ts +3 -1
- package/src/runtime/routes/conversation-routes.ts +372 -185
- package/src/runtime/routes/conversation-starter-routes.ts +13 -7
- package/src/runtime/routes/conversations-import-routes.ts +24 -7
- package/src/runtime/routes/documents-routes.ts +4 -0
- package/src/runtime/routes/domain-routes.ts +51 -37
- package/src/runtime/routes/epoch-millis-range.ts +34 -0
- package/src/runtime/routes/events-routes.ts +28 -34
- package/src/runtime/routes/gateway-log-routes.ts +26 -4
- package/src/runtime/routes/heartbeat-routes.ts +32 -12
- package/src/runtime/routes/host-app-control-routes.ts +1 -1
- package/src/runtime/routes/host-cu-routes.ts +1 -1
- package/src/runtime/routes/identity-intro-cache.ts +11 -34
- package/src/runtime/routes/identity-routes.ts +224 -18
- package/src/runtime/routes/image-generation-routes.ts +40 -2
- package/src/runtime/routes/inbound-message-handler.ts +1 -1
- package/src/runtime/routes/index.ts +2 -0
- package/src/runtime/routes/integrations/a2a.ts +12 -10
- package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +16 -0
- package/src/runtime/routes/integrations/slack/channel.ts +4 -0
- package/src/runtime/routes/integrations/slack/share.ts +27 -6
- package/src/runtime/routes/integrations/telegram.ts +6 -0
- package/src/runtime/routes/integrations/twilio.ts +42 -0
- package/src/runtime/routes/internal-telemetry-routes.ts +88 -0
- package/src/runtime/routes/log-export-routes.ts +8 -0
- package/src/runtime/routes/memory-v2-routes.ts +15 -8
- package/src/runtime/routes/memory-v3-routes.ts +66 -34
- package/src/runtime/routes/oauth-apps.ts +66 -12
- package/src/runtime/routes/oauth-providers.ts +44 -5
- package/src/runtime/routes/platform-routes.ts +81 -5
- package/src/runtime/routes/playground/__tests__/force-compact.test.ts +6 -4
- package/src/runtime/routes/playground/force-compact.ts +1 -1
- package/src/runtime/routes/playground/helpers.ts +1 -1
- package/src/runtime/routes/rename-conversation-routes.ts +5 -0
- package/src/runtime/routes/schedule-routes.ts +152 -42
- package/src/runtime/routes/secret-routes.ts +14 -2
- package/src/runtime/routes/skills-routes.ts +43 -14
- package/src/runtime/routes/surface-conversation-resolver.ts +4 -3
- package/src/runtime/routes/tool-call-confirmation-enrichment.test.ts +161 -0
- package/src/runtime/routes/tool-call-confirmation-enrichment.ts +107 -0
- package/src/runtime/routes/trust-rules-routes.ts +26 -2
- package/src/runtime/routes/tts-routes.ts +35 -0
- package/src/runtime/routes/types.ts +66 -8
- package/src/runtime/routes/usage-routes.ts +47 -39
- package/src/runtime/routes/webhook-routes.ts +41 -2
- package/src/runtime/routes/work-items-routes.ts +2 -4
- package/src/runtime/routes/workspace-routes.ts +4 -0
- package/src/runtime/services/__tests__/analyze-conversation.test.ts +6 -0
- package/src/runtime/services/analyze-conversation.ts +2 -2
- package/src/runtime/services/conversation-serializer.ts +1 -1
- package/src/schedule/schedule-store.ts +20 -1
- package/src/schedule/schedule-usage-store.ts +83 -0
- package/src/schedule/scheduler.ts +12 -5
- package/src/signals/cancel.ts +2 -4
- package/src/skills/catalog-files.ts +2 -2
- package/src/skills/catalog-install.ts +3 -0
- package/src/skills/categories-cache.ts +118 -0
- package/src/skills/clawhub-files.ts +1 -2
- package/src/skills/skillssh-files.ts +1 -2
- package/src/subagent/manager.ts +17 -5
- package/src/telemetry/types.ts +29 -1
- package/src/telemetry/usage-telemetry-reporter.test.ts +112 -3
- package/src/telemetry/usage-telemetry-reporter.ts +57 -2
- package/src/tools/acp/context.ts +20 -0
- package/src/tools/acp/list-agents.test.ts +7 -1
- package/src/tools/acp/spawn.test.ts +158 -55
- package/src/tools/acp/spawn.ts +47 -72
- package/src/tools/acp/steer.test.ts +105 -8
- package/src/tools/acp/steer.ts +48 -17
- package/src/tools/apps/executors.ts +13 -8
- package/src/tools/executor.ts +1 -53
- package/src/tools/filesystem/write.ts +34 -0
- package/src/tools/network/__tests__/web-search-metadata.test.ts +7 -1
- package/src/tools/network/__tests__/web-search.test.ts +11 -3
- package/src/tools/network/web-search-error.test.ts +248 -0
- package/src/tools/network/web-search-error.ts +267 -0
- package/src/tools/network/web-search.ts +207 -48
- package/src/tools/schedule/create.ts +2 -0
- package/src/tools/subagent/spawn.ts +2 -4
- package/src/tools/terminal/safe-env.ts +10 -1
- package/src/tools/ui-surface/definitions.ts +34 -5
- package/src/tts/__tests__/provider-catalog-consistency.test.ts +85 -1
- package/src/tts/provider-catalog.ts +76 -1
- package/src/util/mutex.ts +47 -0
- package/src/workspace/git-service.ts +1 -42
- package/src/workspace/migrations/051-seed-conversation-summarization-callsite.ts +4 -5
- package/src/workspace/migrations/095-bump-heartbeat-interval-30m-to-60m.ts +51 -0
- package/src/workspace/migrations/096-reduce-quality-profile-effort.ts +72 -0
- package/src/workspace/migrations/097-enable-adaptive-thinking-managed-profiles.ts +117 -0
- package/src/workspace/migrations/registry.ts +6 -0
- package/docs/plugins.md +0 -836
- package/examples/plugins/echo/register.ts +0 -184
- package/src/__tests__/bootstrap-turn-cleanup.test.ts +0 -44
- package/src/__tests__/circuit-breaker-pipeline.test.ts +0 -405
- package/src/__tests__/compaction-pipeline.test.ts +0 -210
- package/src/__tests__/compaction-timeout-recovery.test.ts +0 -251
- package/src/__tests__/empty-response-pipeline.test.ts +0 -423
- package/src/__tests__/llm-call-pipeline.test.ts +0 -287
- package/src/__tests__/memory-retrieval-pipeline.test.ts +0 -418
- package/src/__tests__/persistence-pipeline.test.ts +0 -503
- package/src/__tests__/pipeline-runner.test.ts +0 -564
- package/src/__tests__/title-generate-pipeline.test.ts +0 -211
- package/src/__tests__/token-estimate-pipeline.test.ts +0 -479
- package/src/__tests__/tool-error-pipeline.test.ts +0 -241
- package/src/__tests__/tool-execute-pipeline.test.ts +0 -417
- package/src/__tests__/tool-result-truncate-pipeline.test.ts +0 -341
- package/src/daemon/bootstrap-turn-cleanup.ts +0 -45
- package/src/gallery/default-gallery.ts +0 -1359
- package/src/gallery/gallery-manifest.ts +0 -28
- package/src/home/feature-gate.ts +0 -22
- package/src/memory/v3/provider-blocks.ts +0 -16
- package/src/plugins/defaults/circuit-breaker/middlewares/circuitBreaker.ts +0 -93
- package/src/plugins/defaults/circuit-breaker/package.json +0 -15
- package/src/plugins/defaults/circuit-breaker/register.ts +0 -39
- package/src/plugins/defaults/compaction/middlewares/compaction.ts +0 -25
- package/src/plugins/defaults/compaction/terminal.ts +0 -73
- package/src/plugins/defaults/empty-response/middlewares/emptyResponse.ts +0 -22
- package/src/plugins/defaults/empty-response/terminal.ts +0 -106
- package/src/plugins/defaults/injectors/package.json +0 -15
- package/src/plugins/defaults/llm-call/middlewares/llmCall.ts +0 -17
- package/src/plugins/defaults/llm-call/register.ts +0 -45
- package/src/plugins/defaults/memory-retrieval/middlewares/memoryRetrieval.ts +0 -17
- package/src/plugins/defaults/memory-retrieval/package.json +0 -15
- package/src/plugins/defaults/memory-retrieval/register.ts +0 -181
- package/src/plugins/defaults/overflow-reduce/middlewares/overflowReduce.ts +0 -126
- package/src/plugins/defaults/overflow-reduce/package.json +0 -15
- package/src/plugins/defaults/overflow-reduce/register.ts +0 -42
- package/src/plugins/defaults/persistence/middlewares/persistence.ts +0 -19
- package/src/plugins/defaults/persistence/package.json +0 -15
- package/src/plugins/defaults/persistence/register.ts +0 -38
- package/src/plugins/defaults/persistence/terminal.ts +0 -83
- package/src/plugins/defaults/title-generate/terminal.ts +0 -31
- package/src/plugins/defaults/token-estimate/middlewares/tokenEstimate.ts +0 -23
- package/src/plugins/defaults/token-estimate/package.json +0 -15
- package/src/plugins/defaults/token-estimate/register.ts +0 -34
- package/src/plugins/defaults/token-estimate/terminal.ts +0 -40
- package/src/plugins/defaults/tool-error/middlewares/toolError.ts +0 -21
- package/src/plugins/defaults/tool-error/terminal.ts +0 -47
- package/src/plugins/defaults/tool-execute/middlewares/toolExecute.ts +0 -23
- package/src/plugins/defaults/tool-execute/package.json +0 -15
- package/src/plugins/defaults/tool-execute/register.ts +0 -49
- package/src/plugins/defaults/tool-result-truncate/middlewares/toolResultTruncate.ts +0 -23
- package/src/plugins/defaults/tool-result-truncate/types.ts +0 -22
- package/src/skills/category-inference.ts +0 -111
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/capabilities.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/core.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/fixtures/eval-turns.json +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/fixtures/live-turns.json +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/needle.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/snapshot.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/tree.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/types.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/working-set-eviction.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/working-set-skeleton.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/core.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/README.md +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/assignments.json +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/core.json +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-a/topic-x.md +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-a/topic-y.md +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-b/topic-z.md +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/needle.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/snapshot.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/working-set.ts +0 -0
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Periodic reaper for orphaned subprocesses that reparent to the daemon.
|
|
3
|
+
*
|
|
4
|
+
* Tools run commands in their own process group (`detached: true`) and, on
|
|
5
|
+
* timeout/abort, group-kill with `process.kill(-pgid, SIGKILL)` (the bash and
|
|
6
|
+
* host_bash tools, the skill sandbox runner, and the debug-bash route). The
|
|
7
|
+
* immediate child is reaped by Bun/libuv, but its descendants — e.g. git's
|
|
8
|
+
* transport helpers or a skill runner's `bun` process — were never spawned by
|
|
9
|
+
* the daemon, so when the group dies they reparent to PID 1. When the daemon
|
|
10
|
+
* runs as PID 1 in a container, Bun is not an init: it never calls `waitpid()`
|
|
11
|
+
* on those reparented orphans, so they accumulate as `<defunct>` entries that
|
|
12
|
+
* consume PID slots until the container is recycled.
|
|
13
|
+
*
|
|
14
|
+
* This reaper scans `/proc` for zombie children of the daemon and reaps each
|
|
15
|
+
* by **specific PID** with `WNOHANG`. It deliberately does NOT use
|
|
16
|
+
* `waitpid(-1)`: libuv reaps the children it spawned by specific PID on
|
|
17
|
+
* `SIGCHLD`, and a blanket `waitpid(-1)` would race libuv and could swallow a
|
|
18
|
+
* tracked child's exit status — libuv's own source handles the lost race by
|
|
19
|
+
* dropping the exit callback ("someone else stole the waitpid from us. Handle
|
|
20
|
+
* this by not handling it at all."). To stay clear of that race we only reap a
|
|
21
|
+
* zombie after it has survived at least one scan interval: libuv reaps its own
|
|
22
|
+
* within milliseconds of `SIGCHLD`, so anything still defunct a full interval
|
|
23
|
+
* later is a genuine orphan libuv is not tracking.
|
|
24
|
+
*
|
|
25
|
+
* The reaper is a no-op unless the daemon is PID 1 on Linux. Off PID 1 (local
|
|
26
|
+
* macOS dev, or if an init such as tini is ever placed above the daemon),
|
|
27
|
+
* orphans reparent to that init and are reaped there, so there is nothing for
|
|
28
|
+
* this to do. Because the daemon is PID 1, orphans already reparent to it and
|
|
29
|
+
* `PR_SET_CHILD_SUBREAPER` is unnecessary. It is additionally gated behind the
|
|
30
|
+
* `daemon.reapOrphanedSubprocesses` config flag (default off) so the behavior
|
|
31
|
+
* can be enabled per workspace for validation before becoming the default.
|
|
32
|
+
*
|
|
33
|
+
* References:
|
|
34
|
+
* - libuv reaps its own children by pid on SIGCHLD (`uv__wait_children`):
|
|
35
|
+
* https://github.com/nodejs/node/blob/main/deps/uv/src/unix/process.c
|
|
36
|
+
* - Subreaper reaping pattern for runtimes embedding libuv (specific-pid +
|
|
37
|
+
* WNOHANG, never `waitpid(-1)`, grace window for libuv co-existence):
|
|
38
|
+
* https://github.com/coopergwrenn/prctl-subreaper
|
|
39
|
+
* - waitpid(2): https://man7.org/linux/man-pages/man2/waitpid.2.html
|
|
40
|
+
*/
|
|
41
|
+
|
|
42
|
+
import { readdirSync, readFileSync } from "node:fs";
|
|
43
|
+
import { dlopen, FFIType, ptr } from "bun:ffi";
|
|
44
|
+
|
|
45
|
+
import { getConfigReadOnly } from "../config/loader.js";
|
|
46
|
+
import { getLogger } from "../util/logger.js";
|
|
47
|
+
|
|
48
|
+
const log = getLogger("orphan-reaper");
|
|
49
|
+
|
|
50
|
+
/** Linux `WNOHANG` — return immediately if no child has changed state. */
|
|
51
|
+
const WNOHANG = 1;
|
|
52
|
+
|
|
53
|
+
const SCAN_INTERVAL_MS = 60_000;
|
|
54
|
+
|
|
55
|
+
let scanTimer: ReturnType<typeof setInterval> | null = null;
|
|
56
|
+
|
|
57
|
+
/** Zombie child PIDs observed on the previous scan (the grace set). */
|
|
58
|
+
let seenLastScan: Set<number> = new Set();
|
|
59
|
+
|
|
60
|
+
type WaitpidFn = (pid: number, statusPtr: unknown, options: number) => number;
|
|
61
|
+
|
|
62
|
+
let waitpid: WaitpidFn | null = null;
|
|
63
|
+
// Held at module scope so the backing buffer is not GC'd while `waitStatusPtr`
|
|
64
|
+
// keeps only a raw pointer into it (waitpid writes the exit status here).
|
|
65
|
+
let waitStatusBuf: Int32Array | null = null;
|
|
66
|
+
let waitStatusPtr: unknown = null;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Bind libc `waitpid` via FFI. Returns false (and disables the reaper) if FFI
|
|
70
|
+
* is unavailable so daemon startup never fails on this subsystem.
|
|
71
|
+
*/
|
|
72
|
+
function initWaitpid(): boolean {
|
|
73
|
+
if (waitpid) return true;
|
|
74
|
+
try {
|
|
75
|
+
const lib = dlopen("libc.so.6", {
|
|
76
|
+
waitpid: {
|
|
77
|
+
args: [FFIType.i32, FFIType.ptr, FFIType.i32],
|
|
78
|
+
returns: FFIType.i32,
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
// Reusable out-param buffer for the wstatus we don't inspect.
|
|
82
|
+
waitStatusBuf = new Int32Array(1);
|
|
83
|
+
waitStatusPtr = ptr(waitStatusBuf);
|
|
84
|
+
waitpid = lib.symbols.waitpid as unknown as WaitpidFn;
|
|
85
|
+
return true;
|
|
86
|
+
} catch (err) {
|
|
87
|
+
log.warn(
|
|
88
|
+
{ err },
|
|
89
|
+
"Orphan reaper unavailable: failed to bind libc waitpid via FFI",
|
|
90
|
+
);
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export interface ZombieChild {
|
|
96
|
+
pid: number;
|
|
97
|
+
comm: string;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Parse a `/proc/<pid>/stat` line into its leading fields. `comm` (the
|
|
102
|
+
* executable name) may itself contain spaces and parentheses, so the fixed
|
|
103
|
+
* fields are read relative to the final `)` rather than by naive splitting.
|
|
104
|
+
* Returns null if the line is malformed.
|
|
105
|
+
*/
|
|
106
|
+
export function parseProcStat(
|
|
107
|
+
content: string,
|
|
108
|
+
): { comm: string; state: string; ppid: number } | null {
|
|
109
|
+
const lparen = content.indexOf("(");
|
|
110
|
+
const rparen = content.lastIndexOf(")");
|
|
111
|
+
if (lparen === -1 || rparen === -1 || rparen < lparen) return null;
|
|
112
|
+
const comm = content.slice(lparen + 1, rparen);
|
|
113
|
+
const rest = content.slice(rparen + 2).split(" ");
|
|
114
|
+
const state = rest[0];
|
|
115
|
+
const ppid = Number(rest[1]);
|
|
116
|
+
if (!state || !Number.isInteger(ppid)) return null;
|
|
117
|
+
return { comm, state, ppid };
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Given the zombie child PIDs seen this scan and those seen on the previous
|
|
122
|
+
* scan, decide which to reap now. A zombie is only reaped once it has
|
|
123
|
+
* survived a full interval (present in `seenLast`), leaving newly-defunct
|
|
124
|
+
* children for libuv to reap first. Returns the PIDs to reap and the set to
|
|
125
|
+
* carry into the next scan.
|
|
126
|
+
*/
|
|
127
|
+
export function selectReapable(
|
|
128
|
+
current: number[],
|
|
129
|
+
seenLast: Set<number>,
|
|
130
|
+
): { reap: number[]; nextSeen: Set<number> } {
|
|
131
|
+
const reap = current.filter((pid) => seenLast.has(pid));
|
|
132
|
+
return { reap, nextSeen: new Set(current) };
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Scan `/proc` for zombie (`Z`) processes whose parent is this daemon.
|
|
137
|
+
* Reparented orphans keep their original process group but their parent
|
|
138
|
+
* becomes PID 1 (the daemon), so they appear here once defunct.
|
|
139
|
+
*/
|
|
140
|
+
function findZombieChildren(): ZombieChild[] {
|
|
141
|
+
const selfPid = process.pid;
|
|
142
|
+
const zombies: ZombieChild[] = [];
|
|
143
|
+
let entries: string[];
|
|
144
|
+
try {
|
|
145
|
+
entries = readdirSync("/proc");
|
|
146
|
+
} catch {
|
|
147
|
+
return zombies;
|
|
148
|
+
}
|
|
149
|
+
for (const entry of entries) {
|
|
150
|
+
const pid = Number(entry);
|
|
151
|
+
if (!Number.isInteger(pid) || pid <= 1) continue;
|
|
152
|
+
let stat: string;
|
|
153
|
+
try {
|
|
154
|
+
stat = readFileSync(`/proc/${pid}/stat`, "utf8");
|
|
155
|
+
} catch {
|
|
156
|
+
// Process exited between readdir and read — skip.
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
const parsed = parseProcStat(stat);
|
|
160
|
+
if (parsed && parsed.state === "Z" && parsed.ppid === selfPid) {
|
|
161
|
+
zombies.push({ pid, comm: parsed.comm });
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return zombies;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Reap zombie children that have persisted for at least one scan interval,
|
|
169
|
+
* leaving newly-defunct children for libuv to reap first.
|
|
170
|
+
*/
|
|
171
|
+
function reapScan(): void {
|
|
172
|
+
if (!waitpid) return;
|
|
173
|
+
const zombies = findZombieChildren();
|
|
174
|
+
const byPid = new Map(zombies.map((z) => [z.pid, z]));
|
|
175
|
+
const { reap, nextSeen } = selectReapable([...byPid.keys()], seenLastScan);
|
|
176
|
+
const reaped: ZombieChild[] = [];
|
|
177
|
+
for (const pid of reap) {
|
|
178
|
+
const rc = waitpid(pid, waitStatusPtr, WNOHANG);
|
|
179
|
+
// rc > 0: reaped. rc <= 0 (0 = not yet, -1 = ECHILD/raced): leave it.
|
|
180
|
+
if (rc > 0) reaped.push(byPid.get(pid)!);
|
|
181
|
+
}
|
|
182
|
+
seenLastScan = nextSeen;
|
|
183
|
+
if (reaped.length > 0) {
|
|
184
|
+
log.info(
|
|
185
|
+
{
|
|
186
|
+
count: reaped.length,
|
|
187
|
+
pids: reaped.map((z) => z.pid),
|
|
188
|
+
comms: reaped.map((z) => z.comm),
|
|
189
|
+
},
|
|
190
|
+
"Reaped orphaned subprocesses reparented to the daemon (PID 1)",
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Read the opt-in gate from workspace config (`daemon.reapOrphanedSubprocesses`),
|
|
197
|
+
* tolerating a missing or malformed config so startup never fails on it.
|
|
198
|
+
*/
|
|
199
|
+
function isReaperEnabled(): boolean {
|
|
200
|
+
try {
|
|
201
|
+
return getConfigReadOnly().daemon.reapOrphanedSubprocesses;
|
|
202
|
+
} catch {
|
|
203
|
+
return false;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Start the periodic orphan reaper. No-op unless the daemon is PID 1 on Linux
|
|
209
|
+
* (otherwise reparented orphans are reaped by the real init) and the
|
|
210
|
+
* `daemon.reapOrphanedSubprocesses` config gate is enabled.
|
|
211
|
+
*/
|
|
212
|
+
export function startOrphanReaper(): void {
|
|
213
|
+
if (scanTimer) return;
|
|
214
|
+
if (process.platform !== "linux" || process.pid !== 1) {
|
|
215
|
+
log.info(
|
|
216
|
+
{ platform: process.platform, pid: process.pid },
|
|
217
|
+
"Orphan reaper not started: daemon is not PID 1 on Linux",
|
|
218
|
+
);
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
if (!isReaperEnabled()) {
|
|
222
|
+
log.info(
|
|
223
|
+
"Orphan reaper not started: daemon.reapOrphanedSubprocesses is disabled",
|
|
224
|
+
);
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
if (!initWaitpid()) return;
|
|
228
|
+
seenLastScan = new Set();
|
|
229
|
+
scanTimer = setInterval(reapScan, SCAN_INTERVAL_MS);
|
|
230
|
+
scanTimer.unref?.();
|
|
231
|
+
log.info({ intervalMs: SCAN_INTERVAL_MS }, "Orphan reaper started");
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
export function stopOrphanReaper(): void {
|
|
235
|
+
if (scanTimer) {
|
|
236
|
+
clearInterval(scanTimer);
|
|
237
|
+
scanTimer = null;
|
|
238
|
+
}
|
|
239
|
+
seenLastScan = new Set();
|
|
240
|
+
}
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import type { ContextWindowConfig } from "../config/schemas/inference.js";
|
|
2
|
+
import type {
|
|
3
|
+
ContextWindowCompactOptions,
|
|
4
|
+
ContextWindowResult,
|
|
5
|
+
} from "../context/window-manager.js";
|
|
6
|
+
import type { Message } from "../providers/types.js";
|
|
7
|
+
import {
|
|
8
|
+
createInitialReducerState,
|
|
9
|
+
reduceContextOverflow,
|
|
10
|
+
type ReducerState,
|
|
11
|
+
} from "./context-overflow-reducer.js";
|
|
12
|
+
import type { InjectionMode } from "./conversation-runtime-assembly.js";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Input to the overflow-reduction loop. Captures everything the reducer
|
|
16
|
+
* tier loop needs, including the message history, reducer configuration,
|
|
17
|
+
* and side-effect callbacks that bridge the loop back to the orchestrator's
|
|
18
|
+
* mutable per-turn state (context-window manager, activity emitter, runtime
|
|
19
|
+
* injection reassembly, memory reinjection).
|
|
20
|
+
*
|
|
21
|
+
* The callbacks are supplied by the orchestrator because the reducer loop
|
|
22
|
+
* needs to coordinate with state that lives on the `AgentLoopConversationContext`
|
|
23
|
+
* (message mutation, compaction event emission, circuit breaker tracking,
|
|
24
|
+
* injection block reassembly). Keeping them as explicit callbacks keeps the
|
|
25
|
+
* loop free of any dependency on the agent-loop context object.
|
|
26
|
+
*/
|
|
27
|
+
export interface OverflowReduceArgs {
|
|
28
|
+
/** Bare persisted message history (the reducer applies results to a copy
|
|
29
|
+
* of this array). */
|
|
30
|
+
readonly messages: Message[];
|
|
31
|
+
/** Current run-time message array with runtime injections applied. */
|
|
32
|
+
readonly runMessages: Message[];
|
|
33
|
+
/** System prompt used for post-step token estimation. */
|
|
34
|
+
readonly systemPrompt: string;
|
|
35
|
+
/** Provider name used for token estimation (calibration provider key). */
|
|
36
|
+
readonly providerName: string;
|
|
37
|
+
/** Context window config (drives compaction behavior). */
|
|
38
|
+
readonly contextWindow: ContextWindowConfig;
|
|
39
|
+
/** Token budget the reducer must get below (preflight budget). */
|
|
40
|
+
readonly preflightBudget: number;
|
|
41
|
+
/** Tool-token overhead included in every estimation call. */
|
|
42
|
+
readonly toolTokenBudget?: number;
|
|
43
|
+
/** Maximum reducer iterations before the loop exits unconditionally. */
|
|
44
|
+
readonly maxAttempts: number;
|
|
45
|
+
/** Abort signal threaded through compaction calls. */
|
|
46
|
+
readonly abortSignal?: AbortSignal;
|
|
47
|
+
/**
|
|
48
|
+
* Compaction callback — the loop never owns the ContextWindowManager
|
|
49
|
+
* instance. The orchestrator supplies this closure so the loop can
|
|
50
|
+
* delegate the forced-compaction tier without crossing the infra
|
|
51
|
+
* boundary on its own.
|
|
52
|
+
*/
|
|
53
|
+
readonly compactFn: (
|
|
54
|
+
messages: Message[],
|
|
55
|
+
signal: AbortSignal | undefined,
|
|
56
|
+
options: unknown,
|
|
57
|
+
) => Promise<ContextWindowResult>;
|
|
58
|
+
/**
|
|
59
|
+
* Invoked before each reducer iteration to emit the `context_compacting`
|
|
60
|
+
* activity state. The orchestrator owns activity emission because the
|
|
61
|
+
* signal is trust/channel aware.
|
|
62
|
+
*/
|
|
63
|
+
readonly emitActivityState: () => void;
|
|
64
|
+
/**
|
|
65
|
+
* Invoked after each reducer step that produced a successful compaction.
|
|
66
|
+
* Handles circuit-breaker tracking, event emission, and context mutation.
|
|
67
|
+
*/
|
|
68
|
+
readonly onCompactionResult: (
|
|
69
|
+
result: ContextWindowResult,
|
|
70
|
+
compactedBasis?: Message[],
|
|
71
|
+
) => void | Promise<void>;
|
|
72
|
+
/**
|
|
73
|
+
* Invoked after each step to rebuild `runMessages` from the step's
|
|
74
|
+
* reduced history with the requested injection mode. The orchestrator
|
|
75
|
+
* owns this helper so the full per-turn injection options object doesn't
|
|
76
|
+
* leak into the loop. The current reduced messages array is passed
|
|
77
|
+
* explicitly so the orchestrator doesn't need to read mutable shared
|
|
78
|
+
* state. Returns the new `runMessages`.
|
|
79
|
+
*
|
|
80
|
+
* Two distinct "did compact" signals are passed so the orchestrator can
|
|
81
|
+
* apply the correct per-iteration vs sticky gating:
|
|
82
|
+
* - `stepCompacted` — whether THIS iteration's reducer step produced a
|
|
83
|
+
* fresh compaction. Gates PKB / NOW re-injection: compaction strips the
|
|
84
|
+
* existing blocks, so only iterations that just compacted need the
|
|
85
|
+
* content re-threaded. Iterations that only truncated tool results or
|
|
86
|
+
* downgraded injections must NOT force a re-injection or the token
|
|
87
|
+
* count grows each round.
|
|
88
|
+
* - `accumulatedCompacted` — whether ANY iteration in this invocation has
|
|
89
|
+
* compacted. Gates `slackChronologicalMessages` suppression: once
|
|
90
|
+
* compaction has run, the captured Slack transcript snapshot would
|
|
91
|
+
* overwrite the compacted history, so it must stay suppressed for every
|
|
92
|
+
* subsequent iteration even if that iteration didn't re-compact.
|
|
93
|
+
*/
|
|
94
|
+
readonly reinjectForMode: (
|
|
95
|
+
messages: Message[],
|
|
96
|
+
mode: InjectionMode,
|
|
97
|
+
stepCompacted: boolean,
|
|
98
|
+
accumulatedCompacted: boolean,
|
|
99
|
+
) => Promise<Message[]>;
|
|
100
|
+
/**
|
|
101
|
+
* Invoked after each step to post-estimate the rebuilt `runMessages`.
|
|
102
|
+
* Pulled out so the orchestrator controls how estimation is performed
|
|
103
|
+
* (and which fields feed it) without the loop reimplementing it.
|
|
104
|
+
*/
|
|
105
|
+
readonly estimatePostInjection: (runMessages: Message[]) => number;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** Output of the overflow-reduction loop. */
|
|
109
|
+
export interface OverflowReduceResult {
|
|
110
|
+
/** Final reduced `ctx.messages` value. */
|
|
111
|
+
readonly messages: Message[];
|
|
112
|
+
/** Final `runMessages` with re-applied runtime injections. */
|
|
113
|
+
readonly runMessages: Message[];
|
|
114
|
+
/** Final injection mode (may be `"minimal"` if the downgrade tier fired). */
|
|
115
|
+
readonly injectionMode: InjectionMode;
|
|
116
|
+
/** Accumulated reducer state at exit. */
|
|
117
|
+
readonly reducerState: ReducerState;
|
|
118
|
+
/** True if any step successfully compacted history. */
|
|
119
|
+
readonly reducerCompacted: boolean;
|
|
120
|
+
/** How many iterations of the tier loop executed. */
|
|
121
|
+
readonly attempts: number;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Run the context-overflow reducer tier loop — forced compaction, tool-result
|
|
126
|
+
* truncation, media stubbing, injection downgrade — plus the post-step
|
|
127
|
+
* re-injection / re-estimation convergence check.
|
|
128
|
+
*
|
|
129
|
+
* The forced-compaction tier is delegated through `args.compactFn`; the other
|
|
130
|
+
* tiers mutate the message array directly. After each step the orchestrator
|
|
131
|
+
* rebuilds `runMessages` via `args.reinjectForMode` and the loop re-estimates
|
|
132
|
+
* the post-injection token count, exiting once it fits the preflight budget,
|
|
133
|
+
* the reducer is exhausted, or `maxAttempts` is reached.
|
|
134
|
+
*/
|
|
135
|
+
export async function runOverflowReductionLoop(
|
|
136
|
+
args: OverflowReduceArgs,
|
|
137
|
+
): Promise<OverflowReduceResult> {
|
|
138
|
+
let messages = args.messages;
|
|
139
|
+
let runMessages = args.runMessages;
|
|
140
|
+
let injectionMode: "full" | "minimal" = "full";
|
|
141
|
+
let reducerState: ReducerState = createInitialReducerState();
|
|
142
|
+
let reducerCompacted = false;
|
|
143
|
+
let attempts = 0;
|
|
144
|
+
|
|
145
|
+
while (attempts < args.maxAttempts && !reducerState.exhausted) {
|
|
146
|
+
// Abort check at the top of every iteration. When the caller aborts
|
|
147
|
+
// externally, this check lets us bail out BETWEEN iterations rather
|
|
148
|
+
// than letting another round of compaction / re-injection mutate
|
|
149
|
+
// `ctx.messages` after the turn has already failed. Individual
|
|
150
|
+
// `reduceContextOverflow` calls also honor the signal, but without this
|
|
151
|
+
// gate a fresh iteration could still start after the signal fires,
|
|
152
|
+
// since the previous one returned normally before the abort propagated.
|
|
153
|
+
args.abortSignal?.throwIfAborted();
|
|
154
|
+
|
|
155
|
+
attempts++;
|
|
156
|
+
args.emitActivityState();
|
|
157
|
+
|
|
158
|
+
const basisMessages = messages;
|
|
159
|
+
const step = await reduceContextOverflow(
|
|
160
|
+
basisMessages,
|
|
161
|
+
{
|
|
162
|
+
providerName: args.providerName,
|
|
163
|
+
systemPrompt: args.systemPrompt,
|
|
164
|
+
contextWindow: args.contextWindow,
|
|
165
|
+
targetTokens: args.preflightBudget,
|
|
166
|
+
toolTokenBudget: args.toolTokenBudget,
|
|
167
|
+
},
|
|
168
|
+
reducerState,
|
|
169
|
+
(msgs, signal, opts: ContextWindowCompactOptions) =>
|
|
170
|
+
args.compactFn(msgs, signal, opts),
|
|
171
|
+
args.abortSignal,
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
reducerState = step.state;
|
|
175
|
+
messages = step.messages;
|
|
176
|
+
injectionMode = step.state.injectionMode;
|
|
177
|
+
|
|
178
|
+
// Per-iteration compaction flag: whether THIS step just produced a
|
|
179
|
+
// fresh compaction. PKB / NOW re-injection is gated on this — see the
|
|
180
|
+
// reinjectForMode JSDoc for why the two signals differ.
|
|
181
|
+
const stepCompacted = step.compactionResult?.compacted === true;
|
|
182
|
+
|
|
183
|
+
// Let the orchestrator apply compaction side effects (circuit-breaker
|
|
184
|
+
// tracking, event emission, ctx mutation) before we re-inject.
|
|
185
|
+
if (step.compactionResult) {
|
|
186
|
+
await args.onCompactionResult(step.compactionResult, basisMessages);
|
|
187
|
+
if (stepCompacted) {
|
|
188
|
+
reducerCompacted = true;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Second abort gate — if the side effects or the step itself took us
|
|
193
|
+
// past the deadline, don't rebuild runMessages or iterate again.
|
|
194
|
+
args.abortSignal?.throwIfAborted();
|
|
195
|
+
|
|
196
|
+
// Rebuild runMessages via the orchestrator-supplied helper (which
|
|
197
|
+
// re-runs `applyRuntimeInjections` with potentially downgraded mode
|
|
198
|
+
// and freshly re-hydrated PKB/NOW blocks after compaction). We pass
|
|
199
|
+
// the current reduced `messages` explicitly so the orchestrator never
|
|
200
|
+
// has to read from mutable shared state to rebuild runMessages — a
|
|
201
|
+
// tier that doesn't trigger compaction (tool-result truncation, media
|
|
202
|
+
// stubbing) won't update `ctx.messages` on its own.
|
|
203
|
+
//
|
|
204
|
+
// `stepCompacted` and `reducerCompacted` are both passed so the
|
|
205
|
+
// orchestrator can gate PKB / NOW re-injection per-iteration while
|
|
206
|
+
// keeping `slackChronologicalMessages` suppressed once any iteration
|
|
207
|
+
// has compacted.
|
|
208
|
+
runMessages = await args.reinjectForMode(
|
|
209
|
+
messages,
|
|
210
|
+
injectionMode,
|
|
211
|
+
stepCompacted,
|
|
212
|
+
reducerCompacted,
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
// Re-estimate with injections included — `step.estimatedTokens` was
|
|
216
|
+
// computed on bare history and doesn't account for tokens added by
|
|
217
|
+
// runtime injections.
|
|
218
|
+
const postInjectionTokens = args.estimatePostInjection(runMessages);
|
|
219
|
+
if (postInjectionTokens <= args.preflightBudget) break;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return {
|
|
223
|
+
messages,
|
|
224
|
+
runMessages,
|
|
225
|
+
injectionMode,
|
|
226
|
+
reducerState,
|
|
227
|
+
reducerCompacted,
|
|
228
|
+
attempts,
|
|
229
|
+
};
|
|
230
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Persistence for the image-too-large recovery path.
|
|
3
|
+
*
|
|
4
|
+
* When the provider rejects a turn because an attached image exceeds its
|
|
5
|
+
* limits, the agent loop downgrades the offending image blocks in memory and
|
|
6
|
+
* retries. That transformation is transient — the stored message row keeps the
|
|
7
|
+
* full-size image block, so the rejected image is rehydrated on every later
|
|
8
|
+
* turn and keeps re-entering the model's context. This module makes the
|
|
9
|
+
* downgrade durable for images that can *never* be transmitted, so a rejected
|
|
10
|
+
* upload cannot resurface after the user re-uploads a smaller version.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { optimizeImageForTransport } from "../agent/image-optimize.js";
|
|
14
|
+
import { parseImageDimensions } from "../context/image-dimensions.js";
|
|
15
|
+
import {
|
|
16
|
+
getMessages,
|
|
17
|
+
updateMessageContent,
|
|
18
|
+
} from "../memory/conversation-crud.js";
|
|
19
|
+
import type { ContentBlock } from "../providers/types.js";
|
|
20
|
+
import { getLogger } from "../util/logger.js";
|
|
21
|
+
|
|
22
|
+
const log = getLogger("persist-unsendable-image");
|
|
23
|
+
|
|
24
|
+
// Anthropic rejects any image whose longest side exceeds this many pixels,
|
|
25
|
+
// regardless of payload size. Mirrors the user-facing message surfaced by
|
|
26
|
+
// `classifyConversationError` for the IMAGE_TOO_LARGE code.
|
|
27
|
+
// https://docs.anthropic.com/en/docs/build-with-claude/vision#image-size
|
|
28
|
+
const PROVIDER_MAX_IMAGE_DIMENSION = 8000;
|
|
29
|
+
|
|
30
|
+
// Anthropic rejects any single image whose base64 payload exceeds 5 MB.
|
|
31
|
+
// https://docs.anthropic.com/en/docs/build-with-claude/vision#image-size
|
|
32
|
+
const PROVIDER_MAX_IMAGE_PAYLOAD_BYTES = 5 * 1024 * 1024;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Note left in place of an image that cannot be sent to the provider. Shared
|
|
36
|
+
* with the in-memory recovery path so the persisted history matches what the
|
|
37
|
+
* model saw on the turn the image was rejected.
|
|
38
|
+
*/
|
|
39
|
+
export const UNSENDABLE_IMAGE_NOTE =
|
|
40
|
+
"(An image was attached but could not be sent — its dimensions exceed the provider limit and automatic resize was not available. Please resize the image and try again.)";
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* True when a stored image block can never be transmitted to the provider: it
|
|
44
|
+
* violates a provider hard limit (per-side pixel cap or payload size) and
|
|
45
|
+
* cannot be shrunk to fit (re-optimization is a no-op).
|
|
46
|
+
*
|
|
47
|
+
* Stored blocks are already post-optimization, so a block that is still
|
|
48
|
+
* oversized here only stays oversized because resizing is unavailable on this
|
|
49
|
+
* host (e.g. `sips` is absent off macOS, or the format is unsupported). A
|
|
50
|
+
* downscalable image would have been reduced at upload time and would not reach
|
|
51
|
+
* this predicate, so it is left untouched.
|
|
52
|
+
*/
|
|
53
|
+
function isImagePermanentlyUnsendable(
|
|
54
|
+
block: Extract<ContentBlock, { type: "image" }>,
|
|
55
|
+
): boolean {
|
|
56
|
+
const payloadBytes = block.source.data.length;
|
|
57
|
+
const dims = parseImageDimensions(block.source.data, block.source.media_type);
|
|
58
|
+
const exceedsDimensionCap =
|
|
59
|
+
dims != null &&
|
|
60
|
+
(dims.width > PROVIDER_MAX_IMAGE_DIMENSION ||
|
|
61
|
+
dims.height > PROVIDER_MAX_IMAGE_DIMENSION);
|
|
62
|
+
const exceedsPayloadCap = payloadBytes > PROVIDER_MAX_IMAGE_PAYLOAD_BYTES;
|
|
63
|
+
if (!exceedsDimensionCap && !exceedsPayloadCap) return false;
|
|
64
|
+
|
|
65
|
+
const optimized = optimizeImageForTransport(
|
|
66
|
+
block.source.data,
|
|
67
|
+
block.source.media_type,
|
|
68
|
+
);
|
|
69
|
+
return optimized.data === block.source.data;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Rewrite every stored message in a conversation that holds a permanently
|
|
74
|
+
* unsendable image, replacing those image blocks with {@link
|
|
75
|
+
* UNSENDABLE_IMAGE_NOTE}. Reads stored content directly (not the in-memory,
|
|
76
|
+
* injection-enriched copy) so injected prefixes and hydrated source paths are
|
|
77
|
+
* never written back.
|
|
78
|
+
*
|
|
79
|
+
* Idempotent: once an image is replaced by the note there is no image block
|
|
80
|
+
* left to match, so re-running is a no-op. Returns the number of rewritten
|
|
81
|
+
* messages.
|
|
82
|
+
*/
|
|
83
|
+
export function persistUnsendableImageDowngrades(
|
|
84
|
+
conversationId: string,
|
|
85
|
+
): number {
|
|
86
|
+
let rewritten = 0;
|
|
87
|
+
for (const row of getMessages(conversationId)) {
|
|
88
|
+
// Cheap prefilter — JSON.stringify emits no spaces, so an image block
|
|
89
|
+
// always serializes with this exact substring.
|
|
90
|
+
if (!row.content.includes('"type":"image"')) continue;
|
|
91
|
+
|
|
92
|
+
let parsed: unknown;
|
|
93
|
+
try {
|
|
94
|
+
parsed = JSON.parse(row.content);
|
|
95
|
+
} catch {
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
if (!Array.isArray(parsed)) continue;
|
|
99
|
+
|
|
100
|
+
let changed = false;
|
|
101
|
+
const next = (parsed as ContentBlock[]).map((block): ContentBlock => {
|
|
102
|
+
if (block.type !== "image") return block;
|
|
103
|
+
if (!isImagePermanentlyUnsendable(block)) return block;
|
|
104
|
+
changed = true;
|
|
105
|
+
return { type: "text", text: UNSENDABLE_IMAGE_NOTE };
|
|
106
|
+
});
|
|
107
|
+
if (!changed) continue;
|
|
108
|
+
|
|
109
|
+
updateMessageContent(row.id, JSON.stringify(next));
|
|
110
|
+
rewritten++;
|
|
111
|
+
log.info(
|
|
112
|
+
{ conversationId, messageId: row.id },
|
|
113
|
+
"Persisted unsendable-image downgrade so it cannot resurface on later turns",
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
return rewritten;
|
|
117
|
+
}
|
|
@@ -437,9 +437,7 @@ export async function processMessage(
|
|
|
437
437
|
conversation.getMessages().push(cleanMsg);
|
|
438
438
|
|
|
439
439
|
conversation.emitActivityState("thinking", "context_compacting");
|
|
440
|
-
const result = await conversation.forceCompact(
|
|
441
|
-
targetInputTokensOverride: slashResult.targetInputTokensOverride,
|
|
442
|
-
});
|
|
440
|
+
const result = await conversation.forceCompact();
|
|
443
441
|
const responseText = formatCompactResult(result);
|
|
444
442
|
const assistantMsg = createAssistantMessage(responseText);
|
|
445
443
|
const persistedAssistant = await addMessage(
|
package/src/daemon/server.ts
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import { v4 as uuid } from "uuid";
|
|
2
2
|
|
|
3
|
+
import type {
|
|
4
|
+
TraceEvent,
|
|
5
|
+
TraceEventKind,
|
|
6
|
+
TraceEventStatus,
|
|
7
|
+
} from "../api/events/trace-event.js";
|
|
3
8
|
import {
|
|
4
9
|
getMaxSequence,
|
|
5
10
|
persistTraceEvent,
|
|
6
11
|
} from "../memory/trace-event-store.js";
|
|
7
12
|
import { getLogger } from "../util/logger.js";
|
|
8
|
-
import type { ServerMessage
|
|
9
|
-
import type { TraceEvent } from "./message-types/messages.js";
|
|
10
|
-
|
|
11
|
-
export type TraceEventStatus = "info" | "success" | "warning" | "error";
|
|
13
|
+
import type { ServerMessage } from "./message-protocol.js";
|
|
12
14
|
|
|
13
15
|
const log = getLogger("trace-emitter");
|
|
14
16
|
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
* imports (memory/conversation-crud → daemon/conversation-runtime-assembly).
|
|
6
6
|
*/
|
|
7
7
|
import type { ChannelId } from "../channels/types.js";
|
|
8
|
+
import { isHttpAuthDisabled } from "../config/env.js";
|
|
9
|
+
import type { TrustClass } from "../runtime/actor-trust-resolver.js";
|
|
8
10
|
|
|
9
11
|
export interface TrustContext {
|
|
10
12
|
/** Channel through which the inbound message arrived. */
|
|
@@ -62,3 +64,20 @@ export const FALLBACK_TURN_TRUST: TrustContext = {
|
|
|
62
64
|
sourceChannel: "vellum",
|
|
63
65
|
trustClass: "unknown",
|
|
64
66
|
};
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Resolve the effective trust class for an actor.
|
|
70
|
+
*
|
|
71
|
+
* When HTTP auth is disabled (dev bypass), always returns `'guardian'`
|
|
72
|
+
* so that control-plane gates don't block local development.
|
|
73
|
+
*
|
|
74
|
+
* When no trust context is available (e.g. desktop-only conversations that
|
|
75
|
+
* don't go through channel trust resolution), defaults to `'unknown'`
|
|
76
|
+
* to fail-closed.
|
|
77
|
+
*/
|
|
78
|
+
export function resolveTrustClass(
|
|
79
|
+
trustContext: TrustContext | undefined,
|
|
80
|
+
): TrustClass {
|
|
81
|
+
if (isHttpAuthDisabled()) return "guardian";
|
|
82
|
+
return trustContext?.trustClass ?? "unknown";
|
|
83
|
+
}
|
|
@@ -140,6 +140,8 @@ function translateAgentEventToServerMessage(
|
|
|
140
140
|
case "context_compacting":
|
|
141
141
|
case "compaction_circuit_open":
|
|
142
142
|
case "compaction_circuit_closed":
|
|
143
|
+
case "compaction_completed":
|
|
144
|
+
case "history_stripped":
|
|
143
145
|
case "agent_loop_exit":
|
|
144
146
|
return null;
|
|
145
147
|
case "llm_call_started":
|
|
@@ -192,7 +194,7 @@ export function conversationToWakeTarget(
|
|
|
192
194
|
},
|
|
193
195
|
isProcessing: () => conversation.isProcessing(),
|
|
194
196
|
markProcessing: (on) => {
|
|
195
|
-
conversation.
|
|
197
|
+
conversation.setProcessing(on);
|
|
196
198
|
},
|
|
197
199
|
setTrustContext: (ctx) => conversation.setTrustContext(ctx),
|
|
198
200
|
setWakeAllowedTools: (tools) => {
|