@vellumai/assistant 0.5.16 → 0.6.1
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/AGENTS.md +4 -0
- package/ARCHITECTURE.md +69 -16
- package/Dockerfile +2 -5
- package/bun.lock +6 -2
- package/docker-entrypoint.sh +32 -1
- package/docs/architecture/integrations.md +1 -1
- package/docs/architecture/memory.md +21 -24
- package/knip.json +2 -1
- package/openapi.yaml +1198 -83
- package/package.json +5 -1
- package/src/__tests__/actor-token-service.test.ts +68 -0
- package/src/__tests__/agent-loop.test.ts +0 -32
- package/src/__tests__/always-loaded-tools-guard.test.ts +2 -2
- package/src/__tests__/anthropic-provider.test.ts +217 -98
- package/src/__tests__/app-compiler.test.ts +120 -0
- package/src/__tests__/app-dir-path-guard.test.ts +1 -0
- package/src/__tests__/app-executors.test.ts +47 -1
- package/src/__tests__/app-source-watcher.test.ts +159 -0
- package/src/__tests__/assistant-feature-flags-integration.test.ts +2 -2
- package/src/__tests__/call-conversation-messages.test.ts +2 -6
- package/src/__tests__/call-domain.test.ts +2 -6
- package/src/__tests__/call-pointer-messages.test.ts +2 -14
- package/src/__tests__/call-recovery.test.ts +2 -6
- package/src/__tests__/call-routes-http.test.ts +2 -6
- package/src/__tests__/call-store.test.ts +2 -6
- package/src/__tests__/cancel-resolves-conversation-key.test.ts +2 -6
- package/src/__tests__/canonical-guardian-store.test.ts +2 -6
- package/src/__tests__/channel-delivery-store.test.ts +2 -6
- package/src/__tests__/channel-retry-sweep.test.ts +2 -6
- package/src/__tests__/checker.test.ts +63 -9
- package/src/__tests__/clawhub.test.ts +54 -24
- package/src/__tests__/cli-command-risk-guard.test.ts +14 -0
- package/src/__tests__/config-schema.test.ts +6 -1
- package/src/__tests__/config-set-platform-guard.test.ts +302 -0
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +2 -6
- package/src/__tests__/contacts-tools.test.ts +31 -0
- package/src/__tests__/context-overflow-reducer.test.ts +86 -0
- package/src/__tests__/context-token-estimator.test.ts +175 -10
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +13 -6
- package/src/__tests__/conversation-agent-loop.test.ts +13 -51
- package/src/__tests__/conversation-attachments.test.ts +2 -6
- package/src/__tests__/conversation-attention-store.test.ts +2 -6
- package/src/__tests__/conversation-clear-safety.test.ts +2 -6
- package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +4 -10
- package/src/__tests__/conversation-disk-view-integration.test.ts +2 -6
- package/src/__tests__/conversation-disk-view.test.ts +2 -6
- package/src/__tests__/conversation-error.test.ts +33 -2
- package/src/__tests__/conversation-fork-crud.test.ts +2 -6
- package/src/__tests__/conversation-history-web-search.test.ts +6 -1
- package/src/__tests__/conversation-load-history-repair.test.ts +5 -1
- package/src/__tests__/conversation-media-retry.test.ts +91 -0
- package/src/__tests__/conversation-runtime-assembly.test.ts +653 -832
- package/src/__tests__/conversation-runtime-workspace.test.ts +1 -93
- package/src/__tests__/conversation-starter-routes.test.ts +20 -11
- package/src/__tests__/conversation-store.test.ts +2 -6
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +17 -4
- package/src/__tests__/conversation-usage.test.ts +2 -6
- package/src/__tests__/conversation-wipe.test.ts +13 -414
- package/src/__tests__/conversation-workspace-cache-state.test.ts +6 -12
- package/src/__tests__/conversation-workspace-injection.test.ts +25 -26
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +1 -1
- package/src/__tests__/copy-composer-tc-templates.test.ts +335 -0
- package/src/__tests__/credential-execution-feature-gates.test.ts +3 -3
- package/src/__tests__/credential-execution-shell-lockdown.test.ts +2 -2
- package/src/__tests__/credential-security-e2e.test.ts +2 -0
- package/src/__tests__/date-context.test.ts +76 -210
- package/src/__tests__/db-schedule-syntax-migration.test.ts +16 -1
- package/src/__tests__/file-list-tool.test.ts +219 -0
- package/src/__tests__/first-greeting.test.ts +1 -1
- package/src/__tests__/followup-tools.test.ts +2 -6
- package/src/__tests__/graph-extraction-event-date.test.ts +186 -0
- package/src/__tests__/guardian-action-conversation-turn.test.ts +2 -6
- package/src/__tests__/guardian-action-followup-executor.test.ts +2 -6
- package/src/__tests__/guardian-action-followup-store.test.ts +2 -6
- package/src/__tests__/guardian-action-grant-mint-consume.test.ts +2 -6
- package/src/__tests__/guardian-action-late-reply.test.ts +2 -6
- package/src/__tests__/guardian-action-store.test.ts +2 -6
- package/src/__tests__/guardian-binding-drift-heal.test.ts +2 -6
- package/src/__tests__/guardian-decision-primitive-canonical.test.ts +8 -8
- package/src/__tests__/guardian-dispatch.test.ts +2 -6
- package/src/__tests__/guardian-grant-minting.test.ts +2 -14
- package/src/__tests__/guardian-principal-id-roundtrip.test.ts +2 -6
- package/src/__tests__/guardian-routing-invariants.test.ts +192 -6
- package/src/__tests__/guardian-routing-state.test.ts +2 -6
- package/src/__tests__/guardian-verification-voice-binding.test.ts +2 -6
- package/src/__tests__/heartbeat-service.test.ts +180 -3
- package/src/__tests__/identity-routes.test.ts +328 -0
- package/src/__tests__/inbound-invite-redemption.test.ts +2 -6
- package/src/__tests__/injection-block.test.ts +178 -0
- package/src/__tests__/install-meta.test.ts +506 -0
- package/src/__tests__/install-skill-routing.test.ts +293 -0
- package/src/__tests__/invite-redemption-service.test.ts +2 -6
- package/src/__tests__/invite-routes-http.test.ts +2 -6
- package/src/__tests__/jobs-store-qdrant-breaker.test.ts +17 -28
- package/src/__tests__/list-messages-attachments.test.ts +2 -6
- package/src/__tests__/list-messages-tool-merge.test.ts +300 -0
- package/src/__tests__/llm-context-normalization.test.ts +18 -18
- package/src/__tests__/llm-context-route-provider.test.ts +103 -6
- package/src/__tests__/llm-request-log-turn-query.test.ts +164 -6
- package/src/__tests__/llm-usage-store.test.ts +2 -6
- package/src/__tests__/log-export-workspace.test.ts +74 -111
- package/src/__tests__/managed-store.test.ts +38 -11
- package/src/__tests__/mcp-abort-signal.test.ts +5 -0
- package/src/__tests__/mcp-client-auth.test.ts +5 -0
- package/src/__tests__/memory-jobs-worker-backoff.test.ts +2 -8
- package/src/__tests__/memory-recall-log-store.test.ts +134 -6
- package/src/__tests__/memory-upsert-concurrency.test.ts +4 -112
- package/src/__tests__/migration-export-streaming.test.ts +304 -0
- package/src/__tests__/migration-import-commit-http.test.ts +11 -10
- package/src/__tests__/mock-fetch.ts +87 -0
- package/src/__tests__/non-member-access-request.test.ts +2 -6
- package/src/__tests__/notification-decision-recipient-context.test.ts +282 -0
- package/src/__tests__/notification-guardian-path.test.ts +2 -6
- package/src/__tests__/oauth-cli.test.ts +364 -2
- package/src/__tests__/oauth2-gateway-transport.test.ts +18 -3
- package/src/__tests__/onboarding-template-contract.test.ts +62 -14
- package/src/__tests__/outlook-attachments.test.ts +301 -0
- package/src/__tests__/outlook-automation-tools.test.ts +425 -0
- package/src/__tests__/outlook-categories.test.ts +212 -0
- package/src/__tests__/outlook-client-automation.test.ts +246 -0
- package/src/__tests__/outlook-compose-tools.test.ts +325 -0
- package/src/__tests__/outlook-declutter-tools.test.ts +585 -0
- package/src/__tests__/outlook-email-watcher.test.ts +322 -0
- package/src/__tests__/outlook-follow-up.test.ts +196 -0
- package/src/__tests__/outlook-messaging-provider.test.ts +498 -3
- package/src/__tests__/outlook-trash.test.ts +77 -0
- package/src/__tests__/outlook-unsubscribe.test.ts +250 -0
- package/src/__tests__/parser.test.ts +32 -0
- package/src/__tests__/permission-checker-host-gate.test.ts +452 -0
- package/src/__tests__/permission-controls-v2-flag.test.ts +55 -0
- package/src/__tests__/permission-mode-sse.test.ts +418 -0
- package/src/__tests__/permission-mode-store.test.ts +277 -0
- package/src/__tests__/permission-mode.test.ts +101 -0
- package/src/__tests__/platform-bash-auto-approve.test.ts +359 -0
- package/src/__tests__/platform-callback-registration.test.ts +4 -4
- package/src/__tests__/playbook-execution.test.ts +76 -80
- package/src/__tests__/playbook-tools.test.ts +5 -7
- package/src/__tests__/profiler-routes.test.ts +502 -0
- package/src/__tests__/profiler-run-store.test.ts +441 -0
- package/src/__tests__/provider-error-scenarios.test.ts +21 -0
- package/src/__tests__/proxy-approval-callback.test.ts +4 -75
- package/src/__tests__/rebuild-index-graph-nodes.test.ts +273 -0
- package/src/__tests__/registry.test.ts +3 -3
- package/src/__tests__/require-fresh-approval.test.ts +64 -2
- package/src/__tests__/runtime-events-sse-parity.test.ts +2 -6
- package/src/__tests__/runtime-events-sse.test.ts +2 -6
- package/src/__tests__/sandbox-host-parity.test.ts +5 -4
- package/src/__tests__/schedule-store.test.ts +2 -6
- package/src/__tests__/schedule-tools.test.ts +2 -6
- package/src/__tests__/scheduler-recurrence.test.ts +1 -5
- package/src/__tests__/scheduler-reuse-conversation.test.ts +368 -0
- package/src/__tests__/scoped-approval-grants.test.ts +2 -6
- package/src/__tests__/scoped-grant-security-matrix.test.ts +2 -6
- package/src/__tests__/scrub-corrupted-image-attachments.test.ts +278 -0
- package/src/__tests__/search-skills-unified.test.ts +422 -0
- package/src/__tests__/secret-onetime-send.test.ts +2 -0
- package/src/__tests__/send-endpoint-busy.test.ts +44 -9
- package/src/__tests__/sequence-store.test.ts +2 -6
- package/src/__tests__/server-history-render.test.ts +2 -6
- package/src/__tests__/set-permission-mode.test.ts +274 -0
- package/src/__tests__/skill-feature-flags-integration.test.ts +38 -31
- package/src/__tests__/skill-feature-flags.test.ts +6 -6
- package/src/__tests__/skill-load-feature-flag.test.ts +23 -11
- package/src/__tests__/skill-memory.test.ts +2 -741
- package/src/__tests__/skills-uninstall.test.ts +2 -2
- package/src/__tests__/skills.test.ts +1 -1
- package/src/__tests__/slack-inbound-verification.test.ts +2 -6
- package/src/__tests__/strip-memory-injections.test.ts +187 -0
- package/src/__tests__/subagent-detail.test.ts +84 -0
- package/src/__tests__/subagent-disposal.test.ts +308 -0
- package/src/__tests__/subagent-manager-notify.test.ts +19 -10
- package/src/__tests__/subagent-notify-parent.test.ts +390 -0
- package/src/__tests__/subagent-role-registry.test.ts +108 -0
- package/src/__tests__/subagent-tool-filtering.test.ts +71 -0
- package/src/__tests__/subagent-tools.test.ts +464 -4
- package/src/__tests__/system-prompt-ask-mode.test.ts +139 -0
- package/src/__tests__/task-compiler.test.ts +2 -6
- package/src/__tests__/task-management-tools.test.ts +2 -6
- package/src/__tests__/task-memory-cleanup.test.ts +185 -241
- package/src/__tests__/task-runner.test.ts +2 -6
- package/src/__tests__/task-scheduler.test.ts +2 -6
- package/src/__tests__/terminal-tools.test.ts +17 -27
- package/src/__tests__/test-preload.ts +7 -0
- package/src/__tests__/tool-approval-handler.test.ts +2 -6
- package/src/__tests__/tool-executor.test.ts +4 -26
- package/src/__tests__/tool-grant-request-escalation.test.ts +2 -6
- package/src/__tests__/tool-side-effects-slack-dm.test.ts +277 -0
- package/src/__tests__/top-level-renderer.test.ts +10 -13
- package/src/__tests__/trust-store.test.ts +1 -1
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +2 -6
- package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +118 -8
- package/src/__tests__/trusted-contact-multichannel.test.ts +2 -6
- package/src/__tests__/trusted-contact-verification.test.ts +2 -6
- package/src/__tests__/turn-boundary-resolution.test.ts +2 -6
- package/src/__tests__/usage-cache-backfill-migration.test.ts +1 -6
- package/src/__tests__/usage-routes.test.ts +2 -6
- package/src/__tests__/verification-control-plane-policy.test.ts +0 -2
- package/src/__tests__/voice-invite-redemption.test.ts +2 -6
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +2 -6
- package/src/__tests__/voice-session-bridge.test.ts +2 -6
- package/src/__tests__/volume-security-guard.test.ts +2 -0
- package/src/__tests__/workspace-lifecycle.test.ts +29 -1
- package/src/__tests__/workspace-migration-009-backfill-conversation-disk-view.test.ts +2 -6
- package/src/__tests__/workspace-migration-013-repair-conversation-disk-view.test.ts +2 -6
- package/src/__tests__/workspace-migration-026-backfill-install-meta.test.ts +558 -0
- package/src/__tests__/workspace-migration-028-recover-conversations-from-disk-view.test.ts +387 -0
- package/src/__tests__/workspace-policy.test.ts +1 -1
- package/src/agent/attachments.ts +7 -2
- package/src/agent/image-optimize.ts +165 -0
- package/src/agent/loop.ts +7 -15
- package/src/approvals/guardian-request-resolvers.ts +24 -0
- package/src/avatar/traits-png-sync.ts +3 -3
- package/src/bundler/app-compiler.ts +179 -2
- package/src/bundler/package-resolver.ts +3 -5
- package/src/cli/__tests__/notifications.test.ts +1 -2
- package/src/cli/__tests__/run-assistant-command.ts +29 -0
- package/src/cli/commands/__tests__/email-download.test.ts +245 -0
- package/src/cli/commands/__tests__/email-list.test.ts +192 -0
- package/src/cli/commands/__tests__/email-register.test.ts +186 -0
- package/src/cli/commands/__tests__/email-send.test.ts +291 -0
- package/src/cli/commands/__tests__/email-status.test.ts +181 -0
- package/src/cli/commands/__tests__/email-unregister.test.ts +139 -0
- package/src/cli/commands/__tests__/routes.test.ts +562 -0
- package/src/cli/commands/avatar.ts +3 -3
- package/src/cli/commands/config.ts +26 -13
- package/src/cli/commands/conversations.ts +1 -8
- package/src/cli/commands/doctor.ts +2 -2
- package/src/cli/commands/email.ts +584 -835
- package/src/cli/commands/memory.ts +37 -84
- package/src/cli/commands/notifications.ts +7 -2
- package/src/cli/commands/oauth/__tests__/connect.test.ts +2 -2
- package/src/cli/commands/oauth/__tests__/disconnect.test.ts +2 -2
- package/src/cli/commands/oauth/__tests__/mode.test.ts +8 -1
- package/src/cli/commands/oauth/__tests__/status.test.ts +2 -2
- package/src/cli/commands/oauth/connect.ts +25 -11
- package/src/cli/commands/oauth/mode.ts +7 -0
- package/src/cli/commands/oauth/shared.ts +39 -3
- package/src/cli/commands/platform/__tests__/connect.test.ts +1 -1
- package/src/cli/commands/platform/__tests__/disconnect.test.ts +1 -1
- package/src/cli/commands/platform/__tests__/status.test.ts +5 -5
- package/src/cli/commands/platform/index.ts +16 -16
- package/src/cli/commands/routes.ts +396 -0
- package/src/cli/commands/skills.ts +218 -36
- package/src/cli/commands/trust.ts +2 -2
- package/src/cli/lib/daemon-credential-client.ts +2 -3
- package/src/cli/program.ts +2 -0
- package/src/cli.ts +1 -120
- package/src/config/bundled-skills/acp/TOOLS.json +1 -1
- package/src/config/bundled-skills/app-builder/SKILL.md +4 -1
- package/src/config/bundled-skills/contacts/SKILL.md +0 -1
- package/src/config/bundled-skills/contacts/TOOLS.json +0 -8
- package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +0 -4
- package/src/config/bundled-skills/gmail/SKILL.md +4 -12
- package/src/config/bundled-skills/google-calendar/SKILL.md +1 -9
- package/src/config/bundled-skills/messaging/SKILL.md +17 -18
- package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +40 -33
- package/src/config/bundled-skills/outlook/SKILL.md +189 -0
- package/src/config/bundled-skills/outlook/TOOLS.json +530 -0
- package/src/config/bundled-skills/outlook/tools/outlook-attachments.ts +85 -0
- package/src/config/bundled-skills/outlook/tools/outlook-categories.ts +77 -0
- package/src/config/bundled-skills/outlook/tools/outlook-draft.ts +84 -0
- package/src/config/bundled-skills/outlook/tools/outlook-follow-up.ts +94 -0
- package/src/config/bundled-skills/outlook/tools/outlook-forward.ts +49 -0
- package/src/config/bundled-skills/outlook/tools/outlook-outreach-scan.ts +237 -0
- package/src/config/bundled-skills/outlook/tools/outlook-rules.ts +161 -0
- package/src/config/bundled-skills/outlook/tools/outlook-send-draft.ts +32 -0
- package/src/config/bundled-skills/outlook/tools/outlook-sender-digest.ts +272 -0
- package/src/config/bundled-skills/outlook/tools/outlook-trash.ts +29 -0
- package/src/config/bundled-skills/outlook/tools/outlook-unsubscribe.ts +129 -0
- package/src/config/bundled-skills/outlook/tools/outlook-vacation.ts +87 -0
- package/src/config/bundled-skills/outlook/tools/shared.ts +20 -0
- package/src/config/bundled-skills/outlook-calendar/SKILL.md +51 -0
- package/src/config/bundled-skills/outlook-calendar/TOOLS.json +221 -0
- package/src/config/bundled-skills/outlook-calendar/calendar-client.ts +252 -0
- package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-check-availability.ts +53 -0
- package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-create-event.ts +74 -0
- package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-get-event.ts +18 -0
- package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-list-events.ts +46 -0
- package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-rsvp.ts +36 -0
- package/src/config/bundled-skills/outlook-calendar/tools/shared.ts +17 -0
- package/src/config/bundled-skills/outlook-calendar/types.ts +120 -0
- package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +47 -40
- package/src/config/bundled-skills/playbooks/tools/playbook-delete.ts +16 -29
- package/src/config/bundled-skills/playbooks/tools/playbook-list.ts +16 -18
- package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +39 -47
- package/src/config/bundled-skills/schedule/SKILL.md +22 -2
- package/src/config/bundled-skills/schedule/TOOLS.json +8 -0
- package/src/config/bundled-skills/settings/tools/avatar-get.ts +3 -13
- package/src/config/bundled-skills/settings/tools/avatar-remove.ts +2 -4
- package/src/config/bundled-skills/settings/tools/avatar-update.ts +5 -2
- package/src/config/bundled-skills/slack/SKILL.md +3 -7
- package/src/config/bundled-skills/subagent/SKILL.md +43 -3
- package/src/config/bundled-skills/subagent/TOOLS.json +29 -4
- package/src/config/bundled-tool-registry.ts +56 -4
- package/src/config/env-registry.ts +78 -8
- package/src/config/feature-flag-registry.json +38 -125
- package/src/config/schema.ts +8 -0
- package/src/config/schemas/filing.ts +51 -0
- package/src/config/schemas/heartbeat.ts +15 -12
- package/src/config/schemas/memory-lifecycle.ts +12 -0
- package/src/config/schemas/platform.ts +8 -0
- package/src/config/schemas/security.ts +14 -0
- package/src/config/schemas/timeouts.ts +1 -1
- package/src/config/skills.ts +18 -7
- package/src/context/token-estimator.ts +25 -18
- package/src/context/window-manager.ts +6 -2
- package/src/credential-execution/process-manager.ts +3 -1
- package/src/daemon/app-source-watcher.ts +93 -0
- package/src/daemon/config-watcher.ts +79 -1
- package/src/daemon/context-overflow-reducer.ts +46 -2
- package/src/daemon/conversation-agent-loop-handlers.ts +143 -82
- package/src/daemon/conversation-agent-loop.ts +236 -108
- package/src/daemon/conversation-error.ts +31 -8
- package/src/daemon/conversation-history.ts +4 -19
- package/src/daemon/conversation-lifecycle.ts +36 -9
- package/src/daemon/conversation-media-retry.ts +85 -7
- package/src/daemon/conversation-notifiers.ts +4 -1
- package/src/daemon/conversation-process.ts +13 -7
- package/src/daemon/conversation-runtime-assembly.ts +305 -306
- package/src/daemon/conversation-tool-setup.ts +44 -14
- package/src/daemon/conversation-workspace.ts +1 -2
- package/src/daemon/conversation.ts +59 -2
- package/src/daemon/daemon-control.ts +8 -2
- package/src/daemon/date-context.ts +26 -53
- package/src/daemon/first-greeting.ts +1 -1
- package/src/daemon/handlers/conversations.ts +4 -7
- package/src/daemon/handlers/shared.test.ts +143 -0
- package/src/daemon/handlers/shared.ts +85 -17
- package/src/daemon/handlers/skills.ts +416 -209
- package/src/daemon/lifecycle.ts +212 -131
- package/src/daemon/main.ts +5 -1
- package/src/daemon/message-types/conversations.ts +29 -7
- package/src/daemon/message-types/messages.ts +12 -2
- package/src/daemon/message-types/schedules.ts +1 -0
- package/src/daemon/message-types/settings.ts +6 -0
- package/src/daemon/message-types/skills.ts +97 -36
- package/src/daemon/profiler-run-store.ts +557 -0
- package/src/daemon/providers-setup.ts +5 -0
- package/src/daemon/server.ts +100 -11
- package/src/daemon/shutdown-handlers.ts +5 -0
- package/src/daemon/tool-side-effects.ts +50 -8
- package/src/export/transcript-formatter.ts +148 -0
- package/src/filing/filing-service.ts +228 -0
- package/src/heartbeat/heartbeat-service.ts +97 -7
- package/src/hooks/cli.ts +2 -2
- package/src/hooks/runner.ts +15 -38
- package/src/inbound/platform-callback-registration.ts +14 -14
- package/src/mcp/client.ts +6 -0
- package/src/mcp/mcp-oauth-provider.ts +149 -27
- package/src/memory/admin.ts +42 -75
- package/src/memory/app-store.ts +69 -0
- package/src/memory/conversation-bootstrap.ts +3 -1
- package/src/memory/conversation-crud.ts +211 -288
- package/src/memory/conversation-group-migration.ts +157 -0
- package/src/memory/conversation-queries.ts +61 -13
- package/src/memory/conversation-title-service.ts +1 -0
- package/src/memory/db-init.ts +194 -361
- package/src/memory/embed.ts +73 -0
- package/src/memory/embedding-backend.ts +8 -14
- package/src/memory/embedding-runtime-manager.ts +12 -114
- package/src/memory/fingerprint.ts +2 -2
- package/src/memory/graph/bootstrap.ts +521 -0
- package/src/memory/graph/capability-seed.ts +449 -0
- package/src/memory/graph/consolidation.ts +725 -0
- package/src/memory/graph/conversation-graph-memory.ts +659 -0
- package/src/memory/graph/decay.test.ts +208 -0
- package/src/memory/graph/decay.ts +195 -0
- package/src/memory/graph/extraction-job.ts +74 -0
- package/src/memory/graph/extraction.test.ts +936 -0
- package/src/memory/graph/extraction.ts +1297 -0
- package/src/memory/graph/graph-memory-state-store.ts +37 -0
- package/src/memory/graph/graph-search.ts +280 -0
- package/src/memory/graph/image-ref-utils.ts +29 -0
- package/src/memory/graph/injection.test.ts +513 -0
- package/src/memory/graph/injection.ts +469 -0
- package/src/memory/graph/inspect.ts +543 -0
- package/src/memory/graph/narrative.ts +267 -0
- package/src/memory/graph/pattern-scan.ts +269 -0
- package/src/memory/graph/retriever.ts +1111 -0
- package/src/memory/graph/scoring.test.ts +548 -0
- package/src/memory/graph/scoring.ts +232 -0
- package/src/memory/graph/serendipity.ts +65 -0
- package/src/memory/graph/store.test.ts +1098 -0
- package/src/memory/graph/store.ts +838 -0
- package/src/memory/graph/tool-handlers.ts +301 -0
- package/src/memory/graph/tools.ts +97 -0
- package/src/memory/graph/triggers.test.ts +487 -0
- package/src/memory/graph/triggers.ts +223 -0
- package/src/memory/graph/types.ts +295 -0
- package/src/memory/group-crud.ts +191 -0
- package/src/memory/indexer.ts +37 -19
- package/src/memory/job-handlers/cleanup.ts +32 -42
- package/src/memory/job-handlers/conversation-starters.ts +91 -53
- package/src/memory/job-handlers/embedding.ts +5 -31
- package/src/memory/job-handlers/index-maintenance.ts +23 -11
- package/src/memory/job-handlers/summarization.ts +32 -17
- package/src/memory/job-utils.ts +1 -1
- package/src/memory/jobs-store.ts +21 -31
- package/src/memory/jobs-worker.ts +180 -129
- package/src/memory/llm-request-log-store.ts +96 -12
- package/src/memory/memory-recall-log-store.ts +49 -5
- package/src/memory/message-content.ts +1 -0
- package/src/memory/migrations/202-memory-graph-tables.ts +130 -0
- package/src/memory/migrations/203-drop-memory-items-tables.ts +55 -0
- package/src/memory/migrations/204-rename-memory-graph-type-values.ts +46 -0
- package/src/memory/migrations/205-memory-graph-image-refs.ts +11 -0
- package/src/memory/migrations/206-memory-graph-node-edits.ts +19 -0
- package/src/memory/migrations/206-scrub-corrupted-image-attachments.ts +131 -0
- package/src/memory/migrations/207-conversation-graph-memory-state.ts +20 -0
- package/src/memory/migrations/208-conversations-last-message-at.ts +35 -0
- package/src/memory/migrations/209-strip-thinking-from-consolidated.ts +85 -0
- package/src/memory/migrations/210-schedule-reuse-conversation.ts +13 -0
- package/src/memory/migrations/211-memory-recall-logs-query-context.ts +21 -0
- package/src/memory/migrations/212-llm-request-logs-created-at-index.ts +19 -0
- package/src/memory/migrations/index.ts +12 -0
- package/src/memory/migrations/registry.ts +16 -0
- package/src/memory/qdrant-client.ts +44 -17
- package/src/memory/schema/conversations.ts +14 -0
- package/src/memory/schema/index.ts +1 -0
- package/src/memory/schema/infrastructure.ts +8 -1
- package/src/memory/schema/memory-core.ts +0 -51
- package/src/memory/schema/memory-graph.ts +154 -0
- package/src/memory/search/semantic.ts +47 -91
- package/src/memory/task-memory-cleanup.ts +58 -61
- package/src/messaging/providers/outlook/adapter.ts +8 -1
- package/src/messaging/providers/outlook/client.ts +299 -0
- package/src/messaging/providers/outlook/types.ts +118 -0
- package/src/notifications/adapters/macos.ts +1 -0
- package/src/notifications/copy-composer.ts +95 -0
- package/src/notifications/decision-engine.ts +35 -0
- package/src/notifications/signal.ts +16 -0
- package/src/oauth/seed-providers.ts +2 -1
- package/src/permissions/checker.ts +36 -4
- package/src/permissions/defaults.ts +4 -4
- package/src/permissions/permission-mode-store.ts +180 -0
- package/src/permissions/permission-mode.ts +31 -0
- package/src/permissions/workspace-policy.ts +10 -1
- package/src/playbooks/playbook-compiler.ts +19 -18
- package/src/playbooks/types.ts +4 -3
- package/src/prompts/system-prompt.ts +62 -36
- package/src/prompts/templates/BOOTSTRAP-REFERENCE.md +100 -0
- package/src/prompts/templates/BOOTSTRAP.md +70 -165
- package/src/prompts/templates/HEARTBEAT.md +3 -1
- package/src/prompts/templates/SOUL.md +25 -4
- package/src/prompts/templates/UPDATES.md +8 -0
- package/src/providers/anthropic/client.ts +136 -220
- package/src/providers/gemini/client.ts +1 -1
- package/src/providers/openai/client.ts +1 -1
- package/src/providers/registry.ts +1 -1
- package/src/providers/retry.ts +19 -3
- package/src/runtime/actor-trust-resolver.ts +5 -1
- package/src/runtime/auth/route-policy.ts +30 -0
- package/src/runtime/guardian-reply-router.ts +5 -1
- package/src/runtime/http-server.ts +55 -5
- package/src/runtime/http-types.ts +12 -1
- package/src/runtime/middleware/auth.ts +20 -0
- package/src/runtime/migrations/vbundle-builder.ts +389 -3
- package/src/runtime/migrations/vbundle-importer.ts +8 -6
- package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +378 -0
- package/src/runtime/routes/app-management-routes.ts +1 -11
- package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +26 -0
- package/src/runtime/routes/archive-utils.ts +29 -0
- package/src/runtime/routes/attachment-routes.test.ts +106 -0
- package/src/runtime/routes/attachment-routes.ts +106 -16
- package/src/runtime/routes/avatar-routes.ts +2 -9
- package/src/runtime/routes/brain-graph-routes.ts +21 -22
- package/src/runtime/routes/btw-routes.ts +22 -1
- package/src/runtime/routes/conversation-analysis-routes.ts +173 -0
- package/src/runtime/routes/conversation-management-routes.ts +3 -14
- package/src/runtime/routes/conversation-query-routes.ts +49 -3
- package/src/runtime/routes/conversation-routes.ts +264 -44
- package/src/runtime/routes/conversation-starter-routes.ts +2 -2
- package/src/runtime/routes/debug-routes.ts +1 -1
- package/src/runtime/routes/global-search-routes.ts +21 -19
- package/src/runtime/routes/group-routes.ts +207 -0
- package/src/runtime/routes/guardian-action-routes.ts +21 -10
- package/src/runtime/routes/guardian-bootstrap-routes.ts +23 -19
- package/src/runtime/routes/heartbeat-routes.ts +4 -10
- package/src/runtime/routes/identity-routes.ts +53 -18
- package/src/runtime/routes/inbound-message-handler.ts +19 -0
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.test.ts +292 -0
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +207 -0
- package/src/runtime/routes/llm-context-normalization.ts +14 -10
- package/src/runtime/routes/log-export-routes.ts +23 -275
- package/src/runtime/routes/memory-item-routes.test.ts +170 -247
- package/src/runtime/routes/memory-item-routes.ts +341 -388
- package/src/runtime/routes/migration-routes.ts +18 -7
- package/src/runtime/routes/profiler-routes.ts +350 -0
- package/src/runtime/routes/schedule-routes.ts +28 -11
- package/src/runtime/routes/settings-routes.ts +95 -8
- package/src/runtime/routes/skills-routes.ts +103 -37
- package/src/runtime/routes/subagents-routes.ts +28 -7
- package/src/runtime/routes/user-route-dispatcher.ts +223 -0
- package/src/runtime/routes/user-routes.ts +41 -0
- package/src/runtime/routes/work-items-routes.test.ts +2 -6
- package/src/runtime/routes/workspace-routes.ts +0 -1
- package/src/schedule/schedule-store.ts +30 -0
- package/src/schedule/scheduler.ts +52 -18
- package/src/security/oauth2.ts +1 -1
- package/src/security/secure-keys.ts +4 -8
- package/src/shared/provider-env-vars.ts +19 -0
- package/src/skills/catalog-cache.ts +5 -0
- package/src/skills/catalog-install.ts +25 -16
- package/src/skills/clawhub.ts +134 -154
- package/src/skills/install-meta.ts +208 -0
- package/src/skills/managed-store.ts +29 -18
- package/src/skills/skill-memory.ts +12 -229
- package/src/skills/skillssh-registry.ts +19 -17
- package/src/subagent/index.ts +13 -3
- package/src/subagent/manager.ts +308 -29
- package/src/subagent/types.ts +68 -0
- package/src/tasks/task-runner.ts +7 -5
- package/src/telemetry/usage-telemetry-reporter.test.ts +3 -5
- package/src/tools/apps/executors.ts +29 -4
- package/src/tools/browser/runtime-check.ts +3 -1
- package/src/tools/filesystem/list.ts +93 -0
- package/src/tools/memory/register.ts +63 -46
- package/src/tools/permission-checker.ts +85 -1
- package/src/tools/registry.ts +4 -0
- package/src/tools/schedule/create.ts +3 -0
- package/src/tools/schedule/list.ts +1 -0
- package/src/tools/schedule/update.ts +6 -0
- package/src/tools/shared/filesystem/errors.ts +5 -0
- package/src/tools/shared/filesystem/file-ops-service.ts +90 -2
- package/src/tools/shared/filesystem/image-read.ts +22 -85
- package/src/tools/shared/filesystem/types.ts +17 -0
- package/src/tools/shared/shell-output.ts +31 -2
- package/src/tools/subagent/abort.ts +12 -2
- package/src/tools/subagent/message.ts +9 -2
- package/src/tools/subagent/notify-parent.ts +79 -0
- package/src/tools/subagent/read.ts +29 -8
- package/src/tools/subagent/resolve.ts +21 -0
- package/src/tools/subagent/spawn.ts +2 -0
- package/src/tools/subagent/status.ts +11 -1
- package/src/tools/system/avatar-generator.ts +3 -3
- package/src/tools/system/register.ts +23 -0
- package/src/tools/system/set-permission-mode.ts +103 -0
- package/src/tools/terminal/parser.ts +30 -5
- package/src/tools/terminal/safe-env.ts +17 -1
- package/src/tools/tool-manifest.ts +9 -3
- package/src/tools/types.ts +2 -0
- package/src/util/browser.ts +25 -10
- package/src/util/bun-runtime.ts +172 -0
- package/src/util/logger.ts +1 -1
- package/src/util/platform.ts +50 -17
- package/src/watcher/providers/outlook-calendar.ts +343 -0
- package/src/watcher/providers/outlook.ts +198 -0
- package/src/workspace/migrations/023-move-config-files-to-workspace.ts +2 -2
- package/src/workspace/migrations/024-move-runtime-files-to-workspace.ts +2 -2
- package/src/workspace/migrations/025-remove-oauth-app-setup-skills.ts +76 -0
- package/src/workspace/migrations/026-backfill-install-meta.ts +325 -0
- package/src/workspace/migrations/027-remove-orphaned-optimized-images-cache.ts +42 -0
- package/src/workspace/migrations/028-recover-conversations-from-disk-view.ts +270 -0
- package/src/workspace/migrations/029-seed-pkb.ts +84 -0
- package/src/workspace/migrations/registry.ts +10 -0
- package/src/workspace/top-level-renderer.ts +5 -9
- package/src/__tests__/cli-memory.test.ts +0 -372
- package/src/__tests__/clipboard.test.ts +0 -88
- package/src/__tests__/context-memory-e2e.test.ts +0 -415
- package/src/__tests__/journal-context.test.ts +0 -268
- package/src/__tests__/memory-context-benchmark.benchmark.test.ts +0 -297
- package/src/__tests__/memory-lifecycle-e2e.test.ts +0 -459
- package/src/__tests__/memory-query-builder.test.ts +0 -59
- package/src/__tests__/memory-recall-quality.test.ts +0 -1046
- package/src/__tests__/memory-regressions.experimental.test.ts +0 -629
- package/src/__tests__/memory-regressions.test.ts +0 -3696
- package/src/__tests__/memory-retrieval.benchmark.test.ts +0 -295
- package/src/cli/cli-memory.ts +0 -176
- package/src/daemon/conversation-memory.ts +0 -207
- package/src/memory/conversation-starters-cadence.ts +0 -74
- package/src/memory/items-extractor.ts +0 -860
- package/src/memory/job-handlers/batch-extraction.ts +0 -753
- package/src/memory/job-handlers/extraction.ts +0 -40
- package/src/memory/job-handlers/journal-carry-forward.test.ts +0 -355
- package/src/memory/job-handlers/journal-carry-forward.ts +0 -255
- package/src/memory/journal-memory.ts +0 -224
- package/src/memory/query-builder.ts +0 -47
- package/src/memory/query-expansion.ts +0 -83
- package/src/memory/retriever.test.ts +0 -1592
- package/src/memory/retriever.ts +0 -1331
- package/src/memory/search/formatting.test.ts +0 -140
- package/src/memory/search/formatting.ts +0 -262
- package/src/memory/search/mmr.ts +0 -139
- package/src/memory/search/ranking.ts +0 -15
- package/src/memory/search/staleness.ts +0 -40
- package/src/memory/search/tier-classifier.ts +0 -18
- package/src/memory/search/types.ts +0 -121
- package/src/prompts/journal-context.ts +0 -154
- package/src/tools/memory/definitions.ts +0 -69
- package/src/tools/memory/handlers.test.ts +0 -562
- package/src/tools/memory/handlers.ts +0 -434
- package/src/util/clipboard.ts +0 -34
|
@@ -76,33 +76,50 @@ export interface SkillsCreateRequest {
|
|
|
76
76
|
|
|
77
77
|
// === Server → Client ===
|
|
78
78
|
|
|
79
|
+
/** Fields shared by all skill origins. */
|
|
80
|
+
interface SlimSkillBase {
|
|
81
|
+
id: string;
|
|
82
|
+
name: string;
|
|
83
|
+
description: string;
|
|
84
|
+
emoji?: string;
|
|
85
|
+
kind: "bundled" | "installed" | "catalog";
|
|
86
|
+
status: "enabled" | "disabled" | "available";
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
interface VellumSlimSkill extends SlimSkillBase {
|
|
90
|
+
origin: "vellum";
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
interface ClawhubSlimSkill extends SlimSkillBase {
|
|
94
|
+
origin: "clawhub";
|
|
95
|
+
slug: string;
|
|
96
|
+
author: string;
|
|
97
|
+
stars: number;
|
|
98
|
+
installs: number;
|
|
99
|
+
reports: number;
|
|
100
|
+
publishedAt?: string;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
interface SkillsshSlimSkill extends SlimSkillBase {
|
|
104
|
+
origin: "skillssh";
|
|
105
|
+
slug: string;
|
|
106
|
+
sourceRepo: string;
|
|
107
|
+
installs: number;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
interface CustomSlimSkill extends SlimSkillBase {
|
|
111
|
+
origin: "custom";
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export type SlimSkillResponse =
|
|
115
|
+
| VellumSlimSkill
|
|
116
|
+
| ClawhubSlimSkill
|
|
117
|
+
| SkillsshSlimSkill
|
|
118
|
+
| CustomSlimSkill;
|
|
119
|
+
|
|
79
120
|
export interface SkillsListResponse {
|
|
80
121
|
type: "skills_list_response";
|
|
81
|
-
skills:
|
|
82
|
-
id: string;
|
|
83
|
-
name: string;
|
|
84
|
-
description: string;
|
|
85
|
-
emoji?: string;
|
|
86
|
-
homepage?: string;
|
|
87
|
-
source: "bundled" | "managed" | "workspace" | "clawhub" | "extra";
|
|
88
|
-
state: "enabled" | "disabled";
|
|
89
|
-
installedVersion?: string;
|
|
90
|
-
latestVersion?: string;
|
|
91
|
-
updateAvailable: boolean;
|
|
92
|
-
clawhub?: {
|
|
93
|
-
author: string;
|
|
94
|
-
stars: number;
|
|
95
|
-
installs: number;
|
|
96
|
-
reports: number;
|
|
97
|
-
publishedAt: string;
|
|
98
|
-
};
|
|
99
|
-
provenance?: {
|
|
100
|
-
kind: "first-party" | "third-party" | "local";
|
|
101
|
-
provider?: string;
|
|
102
|
-
originId?: string;
|
|
103
|
-
sourceUrl?: string;
|
|
104
|
-
};
|
|
105
|
-
}>;
|
|
122
|
+
skills: SlimSkillResponse[];
|
|
106
123
|
}
|
|
107
124
|
|
|
108
125
|
export interface SkillStateChanged {
|
|
@@ -111,15 +128,7 @@ export interface SkillStateChanged {
|
|
|
111
128
|
state: "enabled" | "disabled" | "installed" | "uninstalled";
|
|
112
129
|
}
|
|
113
130
|
|
|
114
|
-
export interface
|
|
115
|
-
type: "skills_operation_response";
|
|
116
|
-
operation: string;
|
|
117
|
-
success: boolean;
|
|
118
|
-
error?: string;
|
|
119
|
-
data?: unknown;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
export interface SkillDetailResponse {
|
|
131
|
+
export interface SkillBodyResponse {
|
|
123
132
|
type: "skill_detail_response";
|
|
124
133
|
skillId: string;
|
|
125
134
|
body: string;
|
|
@@ -127,6 +136,59 @@ export interface SkillDetailResponse {
|
|
|
127
136
|
error?: string;
|
|
128
137
|
}
|
|
129
138
|
|
|
139
|
+
// ─── Detail endpoint response (HTTP API) ──────────────────────────────────
|
|
140
|
+
|
|
141
|
+
interface SkillDetailBase {
|
|
142
|
+
id: string;
|
|
143
|
+
name: string;
|
|
144
|
+
description: string;
|
|
145
|
+
emoji?: string;
|
|
146
|
+
kind: "bundled" | "installed" | "catalog";
|
|
147
|
+
status: "enabled" | "disabled" | "available";
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
interface VellumSkillDetail extends SkillDetailBase {
|
|
151
|
+
origin: "vellum";
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
interface ClawhubSkillDetail extends SkillDetailBase {
|
|
155
|
+
origin: "clawhub";
|
|
156
|
+
slug: string;
|
|
157
|
+
author: string;
|
|
158
|
+
stars: number;
|
|
159
|
+
installs: number;
|
|
160
|
+
reports: number;
|
|
161
|
+
publishedAt?: string;
|
|
162
|
+
// Enrichment fields (from clawhubInspect):
|
|
163
|
+
owner?: { handle: string; displayName: string; image?: string } | null;
|
|
164
|
+
stats?: {
|
|
165
|
+
stars: number;
|
|
166
|
+
installs: number;
|
|
167
|
+
downloads: number;
|
|
168
|
+
versions: number;
|
|
169
|
+
} | null;
|
|
170
|
+
latestVersion?: { version: string; changelog?: string } | null;
|
|
171
|
+
createdAt?: number | null;
|
|
172
|
+
updatedAt?: number | null;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
interface SkillsshSkillDetail extends SkillDetailBase {
|
|
176
|
+
origin: "skillssh";
|
|
177
|
+
slug: string;
|
|
178
|
+
sourceRepo: string;
|
|
179
|
+
installs: number;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
interface CustomSkillDetail extends SkillDetailBase {
|
|
183
|
+
origin: "custom";
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export type SkillDetailResponse =
|
|
187
|
+
| VellumSkillDetail
|
|
188
|
+
| ClawhubSkillDetail
|
|
189
|
+
| SkillsshSkillDetail
|
|
190
|
+
| CustomSkillDetail;
|
|
191
|
+
|
|
130
192
|
export interface SkillsDraftResponse {
|
|
131
193
|
type: "skills_draft_response";
|
|
132
194
|
success: boolean;
|
|
@@ -181,8 +243,7 @@ export type _SkillsClientMessages =
|
|
|
181
243
|
|
|
182
244
|
export type _SkillsServerMessages =
|
|
183
245
|
| SkillsListResponse
|
|
184
|
-
|
|
|
246
|
+
| SkillBodyResponse
|
|
185
247
|
| SkillStateChanged
|
|
186
|
-
| SkillsOperationResponse
|
|
187
248
|
| SkillsInspectResponse
|
|
188
249
|
| SkillsDraftResponse;
|
|
@@ -0,0 +1,557 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Profiler run store — manages profiler run directories, manifest state,
|
|
3
|
+
* and retention budget enforcement.
|
|
4
|
+
*
|
|
5
|
+
* Each profiler run lives in its own sub-directory under the profiler runs
|
|
6
|
+
* directory (<workspace>/data/profiler/runs/<runId>/). A small manifest.json
|
|
7
|
+
* in each run directory records metadata (status, timestamps, byte count).
|
|
8
|
+
*
|
|
9
|
+
* The startup sweep enumerates all run directories, recomputes sizes,
|
|
10
|
+
* updates manifests, and prunes completed runs oldest-first until the
|
|
11
|
+
* configured byte-count, run-count, and free-space budgets are satisfied.
|
|
12
|
+
* The active run (identified by VELLUM_PROFILER_RUN_ID) is never deleted.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import {
|
|
16
|
+
existsSync,
|
|
17
|
+
lstatSync,
|
|
18
|
+
mkdirSync,
|
|
19
|
+
readdirSync,
|
|
20
|
+
readFileSync,
|
|
21
|
+
rmSync,
|
|
22
|
+
statfsSync,
|
|
23
|
+
statSync,
|
|
24
|
+
writeFileSync,
|
|
25
|
+
} from "node:fs";
|
|
26
|
+
import { join } from "node:path";
|
|
27
|
+
|
|
28
|
+
import {
|
|
29
|
+
getProfilerMaxBytes,
|
|
30
|
+
getProfilerMaxRuns,
|
|
31
|
+
getProfilerMinFreeMb,
|
|
32
|
+
getProfilerMode,
|
|
33
|
+
getProfilerRunId,
|
|
34
|
+
} from "../config/env-registry.js";
|
|
35
|
+
import { getLogger } from "../util/logger.js";
|
|
36
|
+
import {
|
|
37
|
+
getProfilerRootDir,
|
|
38
|
+
getProfilerRunDir,
|
|
39
|
+
getProfilerRunsDir,
|
|
40
|
+
} from "../util/platform.js";
|
|
41
|
+
|
|
42
|
+
const log = getLogger("profiler-run-store");
|
|
43
|
+
|
|
44
|
+
// ── Manifest schema ─────────────────────────────────────────────────────
|
|
45
|
+
|
|
46
|
+
export interface ProfilerRunManifest {
|
|
47
|
+
/** Unique run identifier (matches the directory name). */
|
|
48
|
+
runId: string;
|
|
49
|
+
/** "active" while profiling is in progress; "completed" once finished. */
|
|
50
|
+
status: "active" | "completed";
|
|
51
|
+
/** ISO-8601 timestamp when the run was first observed. */
|
|
52
|
+
createdAt: string;
|
|
53
|
+
/** ISO-8601 timestamp of the last manifest update. */
|
|
54
|
+
updatedAt: string;
|
|
55
|
+
/** Total bytes consumed by all files in the run directory. */
|
|
56
|
+
totalBytes: number;
|
|
57
|
+
/** ISO-8601 timestamp when the run transitioned to "completed". */
|
|
58
|
+
completedAt?: string;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const MANIFEST_FILENAME = "manifest.json";
|
|
62
|
+
|
|
63
|
+
// ── Default budgets ─────────────────────────────────────────────────────
|
|
64
|
+
|
|
65
|
+
/** Default max total bytes across all runs (including active): 500 MB */
|
|
66
|
+
const DEFAULT_MAX_BYTES = 500 * 1024 * 1024;
|
|
67
|
+
|
|
68
|
+
/** Default max number of completed runs retained: 10 */
|
|
69
|
+
const DEFAULT_MAX_RUNS = 10;
|
|
70
|
+
|
|
71
|
+
/** Default minimum free disk space: 200 MB */
|
|
72
|
+
const DEFAULT_MIN_FREE_MB = 200;
|
|
73
|
+
|
|
74
|
+
// ── Result type ─────────────────────────────────────────────────────────
|
|
75
|
+
|
|
76
|
+
export interface ProfilerSweepResult {
|
|
77
|
+
/** Number of completed runs pruned during this sweep. */
|
|
78
|
+
prunedCount: number;
|
|
79
|
+
/** Total bytes freed by pruning. */
|
|
80
|
+
freedBytes: number;
|
|
81
|
+
/** When true, the active run alone exceeds the byte budget. */
|
|
82
|
+
activeRunOverBudget: boolean;
|
|
83
|
+
/** Number of runs remaining after the sweep (including active). */
|
|
84
|
+
remainingRuns: number;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// ── Helpers ─────────────────────────────────────────────────────────────
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Recursively compute the total byte size of all files in a directory.
|
|
91
|
+
* Uses lstatSync to avoid following symlinks (prevents infinite loops
|
|
92
|
+
* from symlink cycles and avoids counting out-of-tree data).
|
|
93
|
+
*/
|
|
94
|
+
function computeDirBytes(dirPath: string): number {
|
|
95
|
+
let total = 0;
|
|
96
|
+
if (!existsSync(dirPath)) return 0;
|
|
97
|
+
|
|
98
|
+
const names = readdirSync(dirPath);
|
|
99
|
+
for (const name of names) {
|
|
100
|
+
const entryPath = join(dirPath, name);
|
|
101
|
+
try {
|
|
102
|
+
const stat = lstatSync(entryPath);
|
|
103
|
+
if (stat.isSymbolicLink()) continue;
|
|
104
|
+
if (stat.isDirectory()) {
|
|
105
|
+
total += computeDirBytes(entryPath);
|
|
106
|
+
} else if (stat.isFile()) {
|
|
107
|
+
total += stat.size;
|
|
108
|
+
}
|
|
109
|
+
} catch {
|
|
110
|
+
// Entry may have been removed between readdir and stat
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return total;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Read a manifest.json from a run directory, returning null if missing or
|
|
118
|
+
* unparseable.
|
|
119
|
+
*/
|
|
120
|
+
function readManifest(runDir: string): ProfilerRunManifest | null {
|
|
121
|
+
const manifestPath = join(runDir, MANIFEST_FILENAME);
|
|
122
|
+
try {
|
|
123
|
+
const raw = readFileSync(manifestPath, "utf-8");
|
|
124
|
+
return JSON.parse(raw) as ProfilerRunManifest;
|
|
125
|
+
} catch {
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Write a manifest.json into a run directory, creating the directory if
|
|
132
|
+
* needed.
|
|
133
|
+
*/
|
|
134
|
+
function writeManifest(runDir: string, manifest: ProfilerRunManifest): void {
|
|
135
|
+
if (!existsSync(runDir)) {
|
|
136
|
+
mkdirSync(runDir, { recursive: true });
|
|
137
|
+
}
|
|
138
|
+
writeFileSync(
|
|
139
|
+
join(runDir, MANIFEST_FILENAME),
|
|
140
|
+
JSON.stringify(manifest, null, 2) + "\n",
|
|
141
|
+
"utf-8",
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Get the available free bytes on the filesystem containing the given path.
|
|
147
|
+
*/
|
|
148
|
+
function getFreeDiskBytes(path: string): number {
|
|
149
|
+
try {
|
|
150
|
+
const stats = statfsSync(path);
|
|
151
|
+
return stats.bavail * stats.bsize;
|
|
152
|
+
} catch {
|
|
153
|
+
// If statfs fails (e.g. unsupported FS), return a large value so the
|
|
154
|
+
// free-space budget doesn't spuriously trigger pruning.
|
|
155
|
+
return Number.MAX_SAFE_INTEGER;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// ── Core operations ─────────────────────────────────────────────────────
|
|
160
|
+
|
|
161
|
+
/** Options for {@link rescanRuns}. */
|
|
162
|
+
export interface RescanRunsOptions {
|
|
163
|
+
/**
|
|
164
|
+
* When true, skip all `writeManifest()` calls — just read existing
|
|
165
|
+
* manifests and recompute sizes without mutating the filesystem.
|
|
166
|
+
* Used by health endpoints to avoid write side-effects on every poll.
|
|
167
|
+
*/
|
|
168
|
+
readOnly?: boolean;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Enumerate all profiler run directories, recompute sizes, and return
|
|
173
|
+
* up-to-date manifests. By default also writes updated manifests back
|
|
174
|
+
* to disk; pass `{ readOnly: true }` to suppress writes (e.g. from
|
|
175
|
+
* health-check callers).
|
|
176
|
+
*/
|
|
177
|
+
export function rescanRuns(options?: RescanRunsOptions): ProfilerRunManifest[] {
|
|
178
|
+
const readOnly = options?.readOnly ?? false;
|
|
179
|
+
const runsDir = getProfilerRunsDir();
|
|
180
|
+
if (!existsSync(runsDir)) return [];
|
|
181
|
+
|
|
182
|
+
const activeRunId = getProfilerRunId();
|
|
183
|
+
const manifests: ProfilerRunManifest[] = [];
|
|
184
|
+
const now = new Date().toISOString();
|
|
185
|
+
|
|
186
|
+
let names: string[];
|
|
187
|
+
try {
|
|
188
|
+
names = readdirSync(runsDir);
|
|
189
|
+
} catch {
|
|
190
|
+
return [];
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
for (const runId of names) {
|
|
194
|
+
const runDir = getProfilerRunDir(runId);
|
|
195
|
+
try {
|
|
196
|
+
if (!statSync(runDir).isDirectory()) continue;
|
|
197
|
+
} catch {
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
const totalBytes = computeDirBytes(runDir);
|
|
201
|
+
|
|
202
|
+
const existing = readManifest(runDir);
|
|
203
|
+
const isActive = runId === activeRunId;
|
|
204
|
+
|
|
205
|
+
// For first-time manifests (no existing manifest.json), seed createdAt
|
|
206
|
+
// from the directory's mtime so legacy runs preserve their actual age
|
|
207
|
+
// for oldest-first pruning order.
|
|
208
|
+
let createdAt = existing?.createdAt ?? now;
|
|
209
|
+
if (!existing) {
|
|
210
|
+
try {
|
|
211
|
+
createdAt = statSync(runDir).mtime.toISOString();
|
|
212
|
+
} catch {
|
|
213
|
+
// Fall back to current time if stat fails
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const newStatus: "active" | "completed" = isActive ? "active" : "completed";
|
|
218
|
+
|
|
219
|
+
// Determine completedAt: set it when a run transitions to completed
|
|
220
|
+
// for the first time, otherwise preserve the existing value.
|
|
221
|
+
let completedAt = existing?.completedAt;
|
|
222
|
+
if (
|
|
223
|
+
newStatus === "completed" &&
|
|
224
|
+
!completedAt &&
|
|
225
|
+
(existing?.status === "active" || !existing)
|
|
226
|
+
) {
|
|
227
|
+
completedAt = now;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Only bump updatedAt when something meaningful changed compared to
|
|
231
|
+
// the on-disk manifest (status, totalBytes, or first creation).
|
|
232
|
+
const somethingChanged =
|
|
233
|
+
!existing ||
|
|
234
|
+
existing.status !== newStatus ||
|
|
235
|
+
existing.totalBytes !== totalBytes;
|
|
236
|
+
const updatedAt = somethingChanged ? now : existing.updatedAt;
|
|
237
|
+
|
|
238
|
+
const manifest: ProfilerRunManifest = {
|
|
239
|
+
runId,
|
|
240
|
+
status: newStatus,
|
|
241
|
+
createdAt,
|
|
242
|
+
updatedAt,
|
|
243
|
+
totalBytes,
|
|
244
|
+
...(completedAt ? { completedAt } : {}),
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
if (!readOnly) {
|
|
248
|
+
writeManifest(runDir, manifest);
|
|
249
|
+
}
|
|
250
|
+
manifests.push(manifest);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
return manifests;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Run the profiler retention sweep. This is the primary entry point,
|
|
258
|
+
* called on daemon startup and after explicit cleanup operations.
|
|
259
|
+
*
|
|
260
|
+
* The sweep:
|
|
261
|
+
* 1. Rescans all run directories and updates manifests.
|
|
262
|
+
* 2. Separates completed runs from the active run.
|
|
263
|
+
* 3. Sorts completed runs oldest-first by createdAt.
|
|
264
|
+
* 4. Prunes completed runs until all budgets are satisfied:
|
|
265
|
+
* - Total bytes across all runs <= maxBytes
|
|
266
|
+
* - Total completed run count <= maxRuns
|
|
267
|
+
* - Free disk space >= minFreeMb
|
|
268
|
+
*
|
|
269
|
+
* If the active run alone exceeds the byte budget, no runs are pruned
|
|
270
|
+
* (you can't prune the active run), and the over-budget condition is
|
|
271
|
+
* reported.
|
|
272
|
+
*/
|
|
273
|
+
export function runProfilerSweep(): ProfilerSweepResult {
|
|
274
|
+
const runsDir = getProfilerRunsDir();
|
|
275
|
+
|
|
276
|
+
// Ensure the runs directory exists for clean first-boot
|
|
277
|
+
if (!existsSync(runsDir)) {
|
|
278
|
+
const rootDir = getProfilerRootDir();
|
|
279
|
+
mkdirSync(rootDir, { recursive: true });
|
|
280
|
+
mkdirSync(runsDir, { recursive: true });
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
const manifests = rescanRuns();
|
|
284
|
+
const activeRunId = getProfilerRunId();
|
|
285
|
+
|
|
286
|
+
// Budget configuration
|
|
287
|
+
const maxBytes = getProfilerMaxBytes() ?? DEFAULT_MAX_BYTES;
|
|
288
|
+
const maxRuns = getProfilerMaxRuns() ?? DEFAULT_MAX_RUNS;
|
|
289
|
+
const minFreeMb = getProfilerMinFreeMb() ?? DEFAULT_MIN_FREE_MB;
|
|
290
|
+
const minFreeBytes = minFreeMb * 1024 * 1024;
|
|
291
|
+
|
|
292
|
+
// Separate active vs completed
|
|
293
|
+
const activeManifest = manifests.find(
|
|
294
|
+
(m) => m.runId === activeRunId && m.status === "active",
|
|
295
|
+
);
|
|
296
|
+
const completedRuns = manifests
|
|
297
|
+
.filter((m) => m.status === "completed")
|
|
298
|
+
.sort(
|
|
299
|
+
(a, b) =>
|
|
300
|
+
new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime(),
|
|
301
|
+
);
|
|
302
|
+
|
|
303
|
+
let totalBytes = manifests.reduce((sum, m) => sum + m.totalBytes, 0);
|
|
304
|
+
let prunedCount = 0;
|
|
305
|
+
let freedBytes = 0;
|
|
306
|
+
|
|
307
|
+
// Prune completed runs oldest-first until all budgets are met
|
|
308
|
+
while (completedRuns.length > 0) {
|
|
309
|
+
const overBytesBudget = totalBytes > maxBytes;
|
|
310
|
+
const overRunCount = completedRuns.length > maxRuns;
|
|
311
|
+
const overFreeSpace = getFreeDiskBytes(runsDir) < minFreeBytes;
|
|
312
|
+
|
|
313
|
+
if (!overBytesBudget && !overRunCount && !overFreeSpace) break;
|
|
314
|
+
|
|
315
|
+
const oldest = completedRuns[0]!;
|
|
316
|
+
const runDir = getProfilerRunDir(oldest.runId);
|
|
317
|
+
|
|
318
|
+
log.info(
|
|
319
|
+
{
|
|
320
|
+
runId: oldest.runId,
|
|
321
|
+
bytes: oldest.totalBytes,
|
|
322
|
+
reason: overBytesBudget
|
|
323
|
+
? "byte_budget"
|
|
324
|
+
: overRunCount
|
|
325
|
+
? "run_count"
|
|
326
|
+
: "free_space",
|
|
327
|
+
},
|
|
328
|
+
`Pruning completed profiler run`,
|
|
329
|
+
);
|
|
330
|
+
|
|
331
|
+
try {
|
|
332
|
+
rmSync(runDir, { recursive: true, force: true });
|
|
333
|
+
completedRuns.shift();
|
|
334
|
+
totalBytes -= oldest.totalBytes;
|
|
335
|
+
freedBytes += oldest.totalBytes;
|
|
336
|
+
prunedCount++;
|
|
337
|
+
} catch (err) {
|
|
338
|
+
log.warn(
|
|
339
|
+
{ runId: oldest.runId, err },
|
|
340
|
+
"Failed to remove profiler run directory",
|
|
341
|
+
);
|
|
342
|
+
// The run still exists on disk — leave it in completedRuns so
|
|
343
|
+
// remainingRuns stays accurate. Break to avoid an infinite retry
|
|
344
|
+
// loop on the same un-deletable run.
|
|
345
|
+
break;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// Check if the active run alone exceeds the byte budget
|
|
350
|
+
const activeRunOverBudget =
|
|
351
|
+
activeManifest !== undefined && activeManifest.totalBytes > maxBytes;
|
|
352
|
+
|
|
353
|
+
if (activeRunOverBudget) {
|
|
354
|
+
log.warn(
|
|
355
|
+
{
|
|
356
|
+
runId: activeManifest.runId,
|
|
357
|
+
activeBytes: activeManifest.totalBytes,
|
|
358
|
+
maxBytes,
|
|
359
|
+
},
|
|
360
|
+
"Active profiler run exceeds byte budget — cannot prune live artifacts",
|
|
361
|
+
);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
const remainingRuns =
|
|
365
|
+
completedRuns.length + (activeManifest !== undefined ? 1 : 0);
|
|
366
|
+
|
|
367
|
+
if (prunedCount > 0) {
|
|
368
|
+
log.info(
|
|
369
|
+
{ prunedCount, freedBytes, remainingRuns },
|
|
370
|
+
"Profiler retention sweep complete",
|
|
371
|
+
);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
return {
|
|
375
|
+
prunedCount,
|
|
376
|
+
freedBytes,
|
|
377
|
+
activeRunOverBudget,
|
|
378
|
+
remainingRuns,
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// ── Runtime status helpers ──────────────────────────────────────────────
|
|
383
|
+
// These derive the current profiler state from env vars + filesystem for
|
|
384
|
+
// health-endpoint reporting and control-plane polling.
|
|
385
|
+
|
|
386
|
+
/** Budget state for the active profiler run. */
|
|
387
|
+
export interface ProfilerBudgetStatus {
|
|
388
|
+
/** Configured maximum bytes across all runs. */
|
|
389
|
+
maxBytes: number;
|
|
390
|
+
/** Bytes remaining before the byte-count budget is exceeded. */
|
|
391
|
+
remainingBytes: number;
|
|
392
|
+
/** Configured minimum free disk space in MB. */
|
|
393
|
+
minFreeMb: number;
|
|
394
|
+
/** Current free disk space in MB. */
|
|
395
|
+
freeMb: number;
|
|
396
|
+
/** True when any budget constraint is currently violated. */
|
|
397
|
+
overBudget: boolean;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/** Summary of the most recently completed profiler run. */
|
|
401
|
+
export interface ProfilerLastCompletedRun {
|
|
402
|
+
runId: string;
|
|
403
|
+
totalBytes: number;
|
|
404
|
+
artifactCount: number;
|
|
405
|
+
hasSummaries: boolean;
|
|
406
|
+
completedAt: string;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/** Full runtime status snapshot for health-endpoint embedding. */
|
|
410
|
+
export interface ProfilerRuntimeStatus {
|
|
411
|
+
/** Whether profiling is enabled (env vars present). */
|
|
412
|
+
enabled: boolean;
|
|
413
|
+
/** The profiling mode ("cpu", "heap", "cpu+heap"), or null when disabled. */
|
|
414
|
+
mode: string | null;
|
|
415
|
+
/** The active run ID, or null when disabled. */
|
|
416
|
+
runId: string | null;
|
|
417
|
+
/** Path to the active run directory, or null when disabled. */
|
|
418
|
+
runDir: string | null;
|
|
419
|
+
/** Total bytes consumed by the active run, or 0 when no active run. */
|
|
420
|
+
totalBytes: number;
|
|
421
|
+
/** Number of profiler artifact files in the active run directory. */
|
|
422
|
+
artifactCount: number;
|
|
423
|
+
/** Budget headroom for the active run. Null when profiling is disabled. */
|
|
424
|
+
budget: ProfilerBudgetStatus | null;
|
|
425
|
+
/** Summary of the most recently completed run, or null when none exist. */
|
|
426
|
+
lastCompletedRun: ProfilerLastCompletedRun | null;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
/** File extensions that Bun profiler writes as raw artifacts. */
|
|
430
|
+
const PROFILER_ARTIFACT_EXTENSIONS = [".cpuprofile", ".heapsnapshot"];
|
|
431
|
+
|
|
432
|
+
/** File extensions for Bun-generated markdown summaries. */
|
|
433
|
+
const PROFILER_SUMMARY_EXTENSIONS = [".md"];
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* Count profiler artifact files (raw profiles) in a run directory.
|
|
437
|
+
*/
|
|
438
|
+
function countArtifacts(runDir: string): number {
|
|
439
|
+
if (!existsSync(runDir)) return 0;
|
|
440
|
+
try {
|
|
441
|
+
return readdirSync(runDir).filter((name) =>
|
|
442
|
+
PROFILER_ARTIFACT_EXTENSIONS.some((ext) => name.endsWith(ext)),
|
|
443
|
+
).length;
|
|
444
|
+
} catch {
|
|
445
|
+
return 0;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
/**
|
|
450
|
+
* Check whether any Bun-generated markdown summary files exist in a run
|
|
451
|
+
* directory.
|
|
452
|
+
*/
|
|
453
|
+
function hasSummaryFiles(runDir: string): boolean {
|
|
454
|
+
if (!existsSync(runDir)) return false;
|
|
455
|
+
try {
|
|
456
|
+
return readdirSync(runDir).some((name) =>
|
|
457
|
+
PROFILER_SUMMARY_EXTENSIONS.some((ext) => name.endsWith(ext)),
|
|
458
|
+
);
|
|
459
|
+
} catch {
|
|
460
|
+
return false;
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
/**
|
|
465
|
+
* Derive the full profiler runtime status from environment variables and
|
|
466
|
+
* the filesystem. This is the main entry point for health-endpoint
|
|
467
|
+
* integration — it never throws.
|
|
468
|
+
*/
|
|
469
|
+
export function getProfilerRuntimeStatus(): ProfilerRuntimeStatus {
|
|
470
|
+
const runId = getProfilerRunId() ?? null;
|
|
471
|
+
const mode = getProfilerMode() ?? null;
|
|
472
|
+
const enabled = runId !== null && mode !== null;
|
|
473
|
+
|
|
474
|
+
if (!enabled) {
|
|
475
|
+
return {
|
|
476
|
+
enabled: false,
|
|
477
|
+
mode: null,
|
|
478
|
+
runId: null,
|
|
479
|
+
runDir: null,
|
|
480
|
+
totalBytes: 0,
|
|
481
|
+
artifactCount: 0,
|
|
482
|
+
budget: null,
|
|
483
|
+
lastCompletedRun: null,
|
|
484
|
+
};
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
const runDir = getProfilerRunDir(runId!);
|
|
488
|
+
const runsDir = getProfilerRunsDir();
|
|
489
|
+
|
|
490
|
+
// Rescan to get up-to-date manifests — read-only so health checks
|
|
491
|
+
// don't write to disk on every poll.
|
|
492
|
+
let manifests: ProfilerRunManifest[];
|
|
493
|
+
try {
|
|
494
|
+
manifests = rescanRuns({ readOnly: true });
|
|
495
|
+
} catch {
|
|
496
|
+
manifests = [];
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
const activeManifest = manifests.find(
|
|
500
|
+
(m) => m.runId === runId && m.status === "active",
|
|
501
|
+
);
|
|
502
|
+
const totalBytes = activeManifest?.totalBytes ?? 0;
|
|
503
|
+
const artifactCount = countArtifacts(runDir);
|
|
504
|
+
|
|
505
|
+
// Compute budget state
|
|
506
|
+
const maxBytes = getProfilerMaxBytes() ?? DEFAULT_MAX_BYTES;
|
|
507
|
+
const minFreeMb = getProfilerMinFreeMb() ?? DEFAULT_MIN_FREE_MB;
|
|
508
|
+
const allRunBytes = manifests.reduce((sum, m) => sum + m.totalBytes, 0);
|
|
509
|
+
const remainingBytes = Math.max(0, maxBytes - allRunBytes);
|
|
510
|
+
const freeDiskBytes = getFreeDiskBytes(
|
|
511
|
+
existsSync(runsDir) ? runsDir : runDir,
|
|
512
|
+
);
|
|
513
|
+
const freeMb = Math.round((freeDiskBytes / (1024 * 1024)) * 100) / 100;
|
|
514
|
+
const overBudget =
|
|
515
|
+
allRunBytes > maxBytes || freeDiskBytes < minFreeMb * 1024 * 1024;
|
|
516
|
+
|
|
517
|
+
const budget: ProfilerBudgetStatus = {
|
|
518
|
+
maxBytes,
|
|
519
|
+
remainingBytes,
|
|
520
|
+
minFreeMb,
|
|
521
|
+
freeMb,
|
|
522
|
+
overBudget,
|
|
523
|
+
};
|
|
524
|
+
|
|
525
|
+
// Find most recent completed run for lastCompletedRun summary
|
|
526
|
+
const completedRuns = manifests
|
|
527
|
+
.filter((m) => m.status === "completed")
|
|
528
|
+
.sort(
|
|
529
|
+
(a, b) =>
|
|
530
|
+
new Date(b.completedAt ?? b.createdAt).getTime() -
|
|
531
|
+
new Date(a.completedAt ?? a.createdAt).getTime(),
|
|
532
|
+
);
|
|
533
|
+
|
|
534
|
+
let lastCompletedRun: ProfilerLastCompletedRun | null = null;
|
|
535
|
+
if (completedRuns.length > 0) {
|
|
536
|
+
const latest = completedRuns[0]!;
|
|
537
|
+
const latestDir = getProfilerRunDir(latest.runId);
|
|
538
|
+
lastCompletedRun = {
|
|
539
|
+
runId: latest.runId,
|
|
540
|
+
totalBytes: latest.totalBytes,
|
|
541
|
+
artifactCount: countArtifacts(latestDir),
|
|
542
|
+
hasSummaries: hasSummaryFiles(latestDir),
|
|
543
|
+
completedAt: latest.completedAt ?? latest.updatedAt,
|
|
544
|
+
};
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
return {
|
|
548
|
+
enabled,
|
|
549
|
+
mode,
|
|
550
|
+
runId,
|
|
551
|
+
runDir,
|
|
552
|
+
totalBytes,
|
|
553
|
+
artifactCount,
|
|
554
|
+
budget,
|
|
555
|
+
lastCompletedRun,
|
|
556
|
+
};
|
|
557
|
+
}
|