@loompad/cli 0.1.0 → 1.1.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/ARCHITECTURE.md +96 -11
- package/CHANGELOG.md +1042 -0
- package/README.md +1473 -44
- package/THIRD_PARTY_NOTICES.md +275 -0
- package/dist/adapters/antigravity-cli.d.ts +62 -0
- package/dist/adapters/antigravity-cli.js +258 -0
- package/dist/adapters/antigravity-cli.js.map +1 -0
- package/dist/adapters/base.d.ts +61 -9
- package/dist/adapters/base.js +266 -15
- package/dist/adapters/base.js.map +1 -1
- package/dist/adapters/bridges/antigravity.d.ts +9 -12
- package/dist/adapters/bridges/antigravity.js +9 -12
- package/dist/adapters/bridges/antigravity.js.map +1 -1
- package/dist/adapters/echo.d.ts +1 -0
- package/dist/adapters/echo.js +59 -3
- package/dist/adapters/echo.js.map +1 -1
- package/dist/adapters/grok.d.ts +7 -0
- package/dist/adapters/grok.js +20 -10
- package/dist/adapters/grok.js.map +1 -1
- package/dist/adapters/index.d.ts +5 -1
- package/dist/adapters/index.js +53 -7
- package/dist/adapters/index.js.map +1 -1
- package/dist/adapters/model.d.ts +137 -0
- package/dist/adapters/model.js +527 -0
- package/dist/adapters/model.js.map +1 -0
- package/dist/adapters/opencode.d.ts +101 -1
- package/dist/adapters/opencode.js +874 -98
- package/dist/adapters/opencode.js.map +1 -1
- package/dist/cli/doctor.d.ts +32 -0
- package/dist/cli/doctor.js +89 -4
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/index.js +2651 -87
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/tui-model.js +3 -1
- package/dist/cli/tui-model.js.map +1 -1
- package/dist/cli/tui.js +44 -1
- package/dist/cli/tui.js.map +1 -1
- package/dist/cli/ui.d.ts +2 -0
- package/dist/cli/ui.js +13 -0
- package/dist/cli/ui.js.map +1 -1
- package/dist/core/ades.d.ts +32 -16
- package/dist/core/ades.js +57 -20
- package/dist/core/ades.js.map +1 -1
- package/dist/core/agent-state.d.ts +14 -0
- package/dist/core/agent-state.js +32 -0
- package/dist/core/agent-state.js.map +1 -0
- package/dist/core/approvals.d.ts +33 -0
- package/dist/core/approvals.js +29 -0
- package/dist/core/approvals.js.map +1 -0
- package/dist/core/backup.d.ts +19 -0
- package/dist/core/backup.js +86 -0
- package/dist/core/backup.js.map +1 -0
- package/dist/core/baton.d.ts +2 -2
- package/dist/core/baton.js.map +1 -1
- package/dist/core/brain-extract.d.ts +17 -0
- package/dist/core/brain-extract.js +28 -2
- package/dist/core/brain-extract.js.map +1 -1
- package/dist/core/brain-index.d.ts +71 -0
- package/dist/core/brain-index.js +204 -5
- package/dist/core/brain-index.js.map +1 -1
- package/dist/core/brain.d.ts +58 -2
- package/dist/core/brain.js +79 -1
- package/dist/core/brain.js.map +1 -1
- package/dist/core/chat-import.d.ts +71 -0
- package/dist/core/chat-import.js +419 -0
- package/dist/core/chat-import.js.map +1 -0
- package/dist/core/checkpoint.d.ts +107 -0
- package/dist/core/checkpoint.js +259 -0
- package/dist/core/checkpoint.js.map +1 -0
- package/dist/core/claude-cli.js +1 -1
- package/dist/core/claude-cli.js.map +1 -1
- package/dist/core/continuity/artifacts.d.ts +14 -0
- package/dist/core/continuity/artifacts.js +118 -0
- package/dist/core/continuity/artifacts.js.map +1 -0
- package/dist/core/continuity/capabilities.d.ts +65 -0
- package/dist/core/continuity/capabilities.js +129 -0
- package/dist/core/continuity/capabilities.js.map +1 -0
- package/dist/core/continuity/contracts.d.ts +352 -0
- package/dist/core/continuity/contracts.js +117 -0
- package/dist/core/continuity/contracts.js.map +1 -0
- package/dist/core/continuity/engine.d.ts +55 -0
- package/dist/core/continuity/engine.js +504 -0
- package/dist/core/continuity/engine.js.map +1 -0
- package/dist/core/continuity/store.d.ts +73 -0
- package/dist/core/continuity/store.js +438 -0
- package/dist/core/continuity/store.js.map +1 -0
- package/dist/core/conversations.d.ts +19 -0
- package/dist/core/conversations.js +87 -0
- package/dist/core/conversations.js.map +1 -0
- package/dist/core/crew-coordination.d.ts +6 -0
- package/dist/core/crew-coordination.js +24 -0
- package/dist/core/crew-coordination.js.map +1 -0
- package/dist/core/crew-policy.d.ts +18 -0
- package/dist/core/crew-policy.js +40 -0
- package/dist/core/crew-policy.js.map +1 -0
- package/dist/core/crew-protocol.d.ts +64 -0
- package/dist/core/crew-protocol.js +146 -0
- package/dist/core/crew-protocol.js.map +1 -0
- package/dist/core/crew-requests.d.ts +14 -0
- package/dist/core/crew-requests.js +22 -0
- package/dist/core/crew-requests.js.map +1 -0
- package/dist/core/crew.d.ts +395 -0
- package/dist/core/crew.js +1857 -0
- package/dist/core/crew.js.map +1 -0
- package/dist/core/digest.d.ts +40 -0
- package/dist/core/digest.js +94 -0
- package/dist/core/digest.js.map +1 -0
- package/dist/core/distill.js +3 -5
- package/dist/core/distill.js.map +1 -1
- package/dist/core/eventlog.d.ts +27 -25
- package/dist/core/eventlog.js +97 -150
- package/dist/core/eventlog.js.map +1 -1
- package/dist/core/events/contracts.d.ts +51 -0
- package/dist/core/events/contracts.js +2 -0
- package/dist/core/events/contracts.js.map +1 -0
- package/dist/core/events/jsonl-store.d.ts +23 -0
- package/dist/core/events/jsonl-store.js +90 -0
- package/dist/core/events/jsonl-store.js.map +1 -0
- package/dist/core/events/sqlite-store.d.ts +37 -0
- package/dist/core/events/sqlite-store.js +215 -0
- package/dist/core/events/sqlite-store.js.map +1 -0
- package/dist/core/forecast.d.ts +44 -0
- package/dist/core/forecast.js +46 -0
- package/dist/core/forecast.js.map +1 -0
- package/dist/core/free-pool.d.ts +65 -0
- package/dist/core/free-pool.js +171 -0
- package/dist/core/free-pool.js.map +1 -0
- package/dist/core/git.d.ts +30 -0
- package/dist/core/git.js +50 -2
- package/dist/core/git.js.map +1 -1
- package/dist/core/github-events.d.ts +53 -0
- package/dist/core/github-events.js +227 -0
- package/dist/core/github-events.js.map +1 -0
- package/dist/core/goal-lanes.d.ts +53 -0
- package/dist/core/goal-lanes.js +99 -0
- package/dist/core/goal-lanes.js.map +1 -0
- package/dist/core/hosted.d.ts +27 -0
- package/dist/core/hosted.js +50 -0
- package/dist/core/hosted.js.map +1 -0
- package/dist/core/http-server.d.ts +5 -0
- package/dist/core/http-server.js +8 -0
- package/dist/core/http-server.js.map +1 -0
- package/dist/core/invite-link.d.ts +31 -0
- package/dist/core/invite-link.js +45 -0
- package/dist/core/invite-link.js.map +1 -0
- package/dist/core/loops.d.ts +37 -0
- package/dist/core/loops.js +81 -0
- package/dist/core/loops.js.map +1 -0
- package/dist/core/mcp-catalog.d.ts +182 -0
- package/dist/core/mcp-catalog.js +431 -0
- package/dist/core/mcp-catalog.js.map +1 -0
- package/dist/core/mcp.d.ts +87 -0
- package/dist/core/mcp.js +181 -0
- package/dist/core/mcp.js.map +1 -0
- package/dist/core/memory.d.ts +1 -0
- package/dist/core/memory.js +5 -2
- package/dist/core/memory.js.map +1 -1
- package/dist/core/model-tools.d.ts +97 -0
- package/dist/core/model-tools.js +388 -0
- package/dist/core/model-tools.js.map +1 -0
- package/dist/core/notify.js +19 -1
- package/dist/core/notify.js.map +1 -1
- package/dist/core/orchestra.d.ts +635 -0
- package/dist/core/orchestra.js +2120 -0
- package/dist/core/orchestra.js.map +1 -0
- package/dist/core/permissions.d.ts +56 -0
- package/dist/core/permissions.js +122 -0
- package/dist/core/permissions.js.map +1 -0
- package/dist/core/preview-proxy.d.ts +70 -0
- package/dist/core/preview-proxy.js +563 -0
- package/dist/core/preview-proxy.js.map +1 -0
- package/dist/core/preview-shot.d.ts +46 -0
- package/dist/core/preview-shot.js +109 -0
- package/dist/core/preview-shot.js.map +1 -0
- package/dist/core/process.d.ts +36 -0
- package/dist/core/process.js +168 -0
- package/dist/core/process.js.map +1 -0
- package/dist/core/prompt-queue.d.ts +136 -0
- package/dist/core/prompt-queue.js +269 -0
- package/dist/core/prompt-queue.js.map +1 -0
- package/dist/core/prompts.d.ts +53 -0
- package/dist/core/prompts.js +127 -0
- package/dist/core/prompts.js.map +1 -0
- package/dist/core/proof.d.ts +82 -0
- package/dist/core/proof.js +216 -0
- package/dist/core/proof.js.map +1 -0
- package/dist/core/provider-text.d.ts +23 -0
- package/dist/core/provider-text.js +66 -0
- package/dist/core/provider-text.js.map +1 -0
- package/dist/core/providers.d.ts +152 -0
- package/dist/core/providers.js +343 -0
- package/dist/core/providers.js.map +1 -0
- package/dist/core/quota.d.ts +33 -0
- package/dist/core/quota.js +74 -0
- package/dist/core/quota.js.map +1 -0
- package/dist/core/recipes.d.ts +50 -0
- package/dist/core/recipes.js +99 -0
- package/dist/core/recipes.js.map +1 -0
- package/dist/core/registry.d.ts +54 -0
- package/dist/core/registry.js +62 -5
- package/dist/core/registry.js.map +1 -1
- package/dist/core/relay-client.d.ts +67 -0
- package/dist/core/relay-client.js +184 -0
- package/dist/core/relay-client.js.map +1 -0
- package/dist/core/relay-protocol.d.ts +140 -0
- package/dist/core/relay-protocol.js +189 -0
- package/dist/core/relay-protocol.js.map +1 -0
- package/dist/core/replay.d.ts +18 -0
- package/dist/core/replay.js +191 -0
- package/dist/core/replay.js.map +1 -0
- package/dist/core/review-triage.d.ts +41 -0
- package/dist/core/review-triage.js +124 -0
- package/dist/core/review-triage.js.map +1 -0
- package/dist/core/router.js +2 -2
- package/dist/core/router.js.map +1 -1
- package/dist/core/routes.d.ts +39 -3
- package/dist/core/routes.js +137 -4
- package/dist/core/routes.js.map +1 -1
- package/dist/core/run-command.d.ts +14 -0
- package/dist/core/run-command.js +45 -0
- package/dist/core/run-command.js.map +1 -0
- package/dist/core/runner-setup.d.ts +36 -0
- package/dist/core/runner-setup.js +114 -0
- package/dist/core/runner-setup.js.map +1 -0
- package/dist/core/search.js +0 -0
- package/dist/core/search.js.map +1 -1
- package/dist/core/semantic.d.ts +92 -0
- package/dist/core/semantic.js +227 -0
- package/dist/core/semantic.js.map +1 -0
- package/dist/core/servers.d.ts +115 -0
- package/dist/core/servers.js +305 -0
- package/dist/core/servers.js.map +1 -0
- package/dist/core/setup.js +64 -6
- package/dist/core/setup.js.map +1 -1
- package/dist/core/shield.d.ts +17 -0
- package/dist/core/shield.js +81 -0
- package/dist/core/shield.js.map +1 -0
- package/dist/core/shift.d.ts +69 -0
- package/dist/core/shift.js +120 -0
- package/dist/core/shift.js.map +1 -0
- package/dist/core/skill-install.d.ts +125 -0
- package/dist/core/skill-install.js +328 -0
- package/dist/core/skill-install.js.map +1 -0
- package/dist/core/skills.d.ts +116 -0
- package/dist/core/skills.js +223 -0
- package/dist/core/skills.js.map +1 -0
- package/dist/core/step-conditions.d.ts +65 -0
- package/dist/core/step-conditions.js +102 -0
- package/dist/core/step-conditions.js.map +1 -0
- package/dist/core/subagents.d.ts +45 -0
- package/dist/core/subagents.js +90 -0
- package/dist/core/subagents.js.map +1 -0
- package/dist/core/team-canon.d.ts +42 -0
- package/dist/core/team-canon.js +121 -0
- package/dist/core/team-canon.js.map +1 -0
- package/dist/core/team-crypto.d.ts +97 -0
- package/dist/core/team-crypto.js +175 -0
- package/dist/core/team-crypto.js.map +1 -0
- package/dist/core/team-hub.d.ts +514 -0
- package/dist/core/team-hub.js +964 -0
- package/dist/core/team-hub.js.map +1 -0
- package/dist/core/team-landing.d.ts +239 -0
- package/dist/core/team-landing.js +504 -0
- package/dist/core/team-landing.js.map +1 -0
- package/dist/core/team-leases.d.ts +44 -0
- package/dist/core/team-leases.js +145 -0
- package/dist/core/team-leases.js.map +1 -0
- package/dist/core/team-memory.d.ts +42 -0
- package/dist/core/team-memory.js +84 -0
- package/dist/core/team-memory.js.map +1 -0
- package/dist/core/team-policy.d.ts +85 -0
- package/dist/core/team-policy.js +191 -0
- package/dist/core/team-policy.js.map +1 -0
- package/dist/core/thread-md.d.ts +6 -0
- package/dist/core/thread-md.js +42 -0
- package/dist/core/thread-md.js.map +1 -0
- package/dist/core/turn-stats.d.ts +61 -0
- package/dist/core/turn-stats.js +124 -0
- package/dist/core/turn-stats.js.map +1 -0
- package/dist/core/updater.d.ts +73 -0
- package/dist/core/updater.js +153 -0
- package/dist/core/updater.js.map +1 -0
- package/dist/core/usage.d.ts +75 -0
- package/dist/core/usage.js +163 -0
- package/dist/core/usage.js.map +1 -0
- package/dist/core/windows-jobs.d.ts +15 -0
- package/dist/core/windows-jobs.js +170 -0
- package/dist/core/windows-jobs.js.map +1 -0
- package/dist/core/worktree-merge.d.ts +57 -0
- package/dist/core/worktree-merge.js +123 -0
- package/dist/core/worktree-merge.js.map +1 -0
- package/dist/core/worktree.d.ts +5 -0
- package/dist/core/worktree.js +45 -5
- package/dist/core/worktree.js.map +1 -1
- package/dist/core/worktrees.d.ts +6 -0
- package/dist/core/worktrees.js +21 -0
- package/dist/core/worktrees.js.map +1 -0
- package/dist/daemon/app-page.d.ts +0 -17
- package/dist/daemon/app-page.js +10 -6346
- package/dist/daemon/app-page.js.map +1 -1
- package/dist/daemon/auth.d.ts +24 -3
- package/dist/daemon/auth.js +66 -7
- package/dist/daemon/auth.js.map +1 -1
- package/dist/daemon/board.d.ts +7 -0
- package/dist/daemon/board.js +12 -1
- package/dist/daemon/board.js.map +1 -1
- package/dist/daemon/brand-icons.d.ts +6 -1
- package/dist/daemon/brand-icons.js +11 -3
- package/dist/daemon/brand-icons.js.map +1 -1
- package/dist/daemon/client.d.ts +665 -1
- package/dist/daemon/client.js +381 -3
- package/dist/daemon/client.js.map +1 -1
- package/dist/daemon/delivery.d.ts +34 -0
- package/dist/daemon/delivery.js +63 -0
- package/dist/daemon/delivery.js.map +1 -0
- package/dist/daemon/deploys.d.ts +62 -0
- package/dist/daemon/deploys.js +159 -0
- package/dist/daemon/deploys.js.map +1 -0
- package/dist/daemon/landing.d.ts +192 -0
- package/dist/daemon/landing.js +992 -0
- package/dist/daemon/landing.js.map +1 -0
- package/dist/daemon/onboard.d.ts +150 -0
- package/dist/daemon/onboard.js +390 -0
- package/dist/daemon/onboard.js.map +1 -0
- package/dist/daemon/proof.d.ts +70 -0
- package/dist/daemon/proof.js +172 -0
- package/dist/daemon/proof.js.map +1 -0
- package/dist/daemon/push.d.ts +19 -1
- package/dist/daemon/push.js +90 -3
- package/dist/daemon/push.js.map +1 -1
- package/dist/daemon/relay.d.ts +82 -0
- package/dist/daemon/relay.js +351 -0
- package/dist/daemon/relay.js.map +1 -0
- package/dist/daemon/routes/agent-discovery.d.ts +4 -0
- package/dist/daemon/routes/agent-discovery.js +29 -0
- package/dist/daemon/routes/agent-discovery.js.map +1 -0
- package/dist/daemon/routes/agent-settings.d.ts +4 -0
- package/dist/daemon/routes/agent-settings.js +94 -0
- package/dist/daemon/routes/agent-settings.js.map +1 -0
- package/dist/daemon/routes/agents.d.ts +4 -0
- package/dist/daemon/routes/agents.js +105 -0
- package/dist/daemon/routes/agents.js.map +1 -0
- package/dist/daemon/routes/alerts.d.ts +4 -0
- package/dist/daemon/routes/alerts.js +137 -0
- package/dist/daemon/routes/alerts.js.map +1 -0
- package/dist/daemon/routes/approvals.d.ts +4 -0
- package/dist/daemon/routes/approvals.js +40 -0
- package/dist/daemon/routes/approvals.js.map +1 -0
- package/dist/daemon/routes/assets.d.ts +3 -0
- package/dist/daemon/routes/assets.js +64 -0
- package/dist/daemon/routes/assets.js.map +1 -0
- package/dist/daemon/routes/board.d.ts +4 -0
- package/dist/daemon/routes/board.js +147 -0
- package/dist/daemon/routes/board.js.map +1 -0
- package/dist/daemon/routes/brain.d.ts +4 -0
- package/dist/daemon/routes/brain.js +270 -0
- package/dist/daemon/routes/brain.js.map +1 -0
- package/dist/daemon/routes/chats.d.ts +4 -0
- package/dist/daemon/routes/chats.js +249 -0
- package/dist/daemon/routes/chats.js.map +1 -0
- package/dist/daemon/routes/clients.d.ts +4 -0
- package/dist/daemon/routes/clients.js +49 -0
- package/dist/daemon/routes/clients.js.map +1 -0
- package/dist/daemon/routes/cloud.d.ts +4 -0
- package/dist/daemon/routes/cloud.js +46 -0
- package/dist/daemon/routes/cloud.js.map +1 -0
- package/dist/daemon/routes/config.d.ts +4 -0
- package/dist/daemon/routes/config.js +35 -0
- package/dist/daemon/routes/config.js.map +1 -0
- package/dist/daemon/routes/context.d.ts +51 -0
- package/dist/daemon/routes/context.js +2 -0
- package/dist/daemon/routes/context.js.map +1 -0
- package/dist/daemon/routes/crews.d.ts +4 -0
- package/dist/daemon/routes/crews.js +149 -0
- package/dist/daemon/routes/crews.js.map +1 -0
- package/dist/daemon/routes/files.d.ts +3 -0
- package/dist/daemon/routes/files.js +253 -0
- package/dist/daemon/routes/files.js.map +1 -0
- package/dist/daemon/routes/git.d.ts +4 -0
- package/dist/daemon/routes/git.js +96 -0
- package/dist/daemon/routes/git.js.map +1 -0
- package/dist/daemon/routes/history.d.ts +4 -0
- package/dist/daemon/routes/history.js +101 -0
- package/dist/daemon/routes/history.js.map +1 -0
- package/dist/daemon/routes/integrations-project.d.ts +3 -0
- package/dist/daemon/routes/integrations-project.js +103 -0
- package/dist/daemon/routes/integrations-project.js.map +1 -0
- package/dist/daemon/routes/integrations.d.ts +3 -0
- package/dist/daemon/routes/integrations.js +111 -0
- package/dist/daemon/routes/integrations.js.map +1 -0
- package/dist/daemon/routes/logs.d.ts +3 -0
- package/dist/daemon/routes/logs.js +40 -0
- package/dist/daemon/routes/logs.js.map +1 -0
- package/dist/daemon/routes/messages.d.ts +4 -0
- package/dist/daemon/routes/messages.js +60 -0
- package/dist/daemon/routes/messages.js.map +1 -0
- package/dist/daemon/routes/observability.d.ts +4 -0
- package/dist/daemon/routes/observability.js +247 -0
- package/dist/daemon/routes/observability.js.map +1 -0
- package/dist/daemon/routes/onboard.d.ts +12 -0
- package/dist/daemon/routes/onboard.js +67 -0
- package/dist/daemon/routes/onboard.js.map +1 -0
- package/dist/daemon/routes/orchestra.d.ts +4 -0
- package/dist/daemon/routes/orchestra.js +110 -0
- package/dist/daemon/routes/orchestra.js.map +1 -0
- package/dist/daemon/routes/pairing.d.ts +4 -0
- package/dist/daemon/routes/pairing.js +155 -0
- package/dist/daemon/routes/pairing.js.map +1 -0
- package/dist/daemon/routes/preview.d.ts +24 -0
- package/dist/daemon/routes/preview.js +122 -0
- package/dist/daemon/routes/preview.js.map +1 -0
- package/dist/daemon/routes/project-actions.d.ts +4 -0
- package/dist/daemon/routes/project-actions.js +53 -0
- package/dist/daemon/routes/project-actions.js.map +1 -0
- package/dist/daemon/routes/project-team.d.ts +4 -0
- package/dist/daemon/routes/project-team.js +201 -0
- package/dist/daemon/routes/project-team.js.map +1 -0
- package/dist/daemon/routes/projects.d.ts +14 -0
- package/dist/daemon/routes/projects.js +138 -0
- package/dist/daemon/routes/projects.js.map +1 -0
- package/dist/daemon/routes/prompts.d.ts +4 -0
- package/dist/daemon/routes/prompts.js +44 -0
- package/dist/daemon/routes/prompts.js.map +1 -0
- package/dist/daemon/routes/proof.d.ts +4 -0
- package/dist/daemon/routes/proof.js +22 -0
- package/dist/daemon/routes/proof.js.map +1 -0
- package/dist/daemon/routes/providers.d.ts +3 -0
- package/dist/daemon/routes/providers.js +60 -0
- package/dist/daemon/routes/providers.js.map +1 -0
- package/dist/daemon/routes/public.d.ts +4 -0
- package/dist/daemon/routes/public.js +64 -0
- package/dist/daemon/routes/public.js.map +1 -0
- package/dist/daemon/routes/queue.d.ts +4 -0
- package/dist/daemon/routes/queue.js +140 -0
- package/dist/daemon/routes/queue.js.map +1 -0
- package/dist/daemon/routes/routing.d.ts +4 -0
- package/dist/daemon/routes/routing.js +61 -0
- package/dist/daemon/routes/routing.js.map +1 -0
- package/dist/daemon/routes/runtime-handler.d.ts +2 -0
- package/dist/daemon/routes/runtime-handler.js +63 -0
- package/dist/daemon/routes/runtime-handler.js.map +1 -0
- package/dist/daemon/routes/search.d.ts +4 -0
- package/dist/daemon/routes/search.js +17 -0
- package/dist/daemon/routes/search.js.map +1 -0
- package/dist/daemon/routes/servers.d.ts +4 -0
- package/dist/daemon/routes/servers.js +133 -0
- package/dist/daemon/routes/servers.js.map +1 -0
- package/dist/daemon/routes/setup.d.ts +3 -0
- package/dist/daemon/routes/setup.js +50 -0
- package/dist/daemon/routes/setup.js.map +1 -0
- package/dist/daemon/routes/shift.d.ts +4 -0
- package/dist/daemon/routes/shift.js +30 -0
- package/dist/daemon/routes/shift.js.map +1 -0
- package/dist/daemon/routes/specs.d.ts +4 -0
- package/dist/daemon/routes/specs.js +57 -0
- package/dist/daemon/routes/specs.js.map +1 -0
- package/dist/daemon/routes/task-delivery.d.ts +4 -0
- package/dist/daemon/routes/task-delivery.js +38 -0
- package/dist/daemon/routes/task-delivery.js.map +1 -0
- package/dist/daemon/routes/team.d.ts +4 -0
- package/dist/daemon/routes/team.js +193 -0
- package/dist/daemon/routes/team.js.map +1 -0
- package/dist/daemon/routes/terminals.d.ts +4 -0
- package/dist/daemon/routes/terminals.js +87 -0
- package/dist/daemon/routes/terminals.js.map +1 -0
- package/dist/daemon/routes/tools.d.ts +4 -0
- package/dist/daemon/routes/tools.js +180 -0
- package/dist/daemon/routes/tools.js.map +1 -0
- package/dist/daemon/routes/updates.d.ts +4 -0
- package/dist/daemon/routes/updates.js +89 -0
- package/dist/daemon/routes/updates.js.map +1 -0
- package/dist/daemon/routes/voice.d.ts +4 -0
- package/dist/daemon/routes/voice.js +53 -0
- package/dist/daemon/routes/voice.js.map +1 -0
- package/dist/daemon/runner.d.ts +186 -0
- package/dist/daemon/runner.js +612 -0
- package/dist/daemon/runner.js.map +1 -0
- package/dist/daemon/runtime/accounting.d.ts +130 -0
- package/dist/daemon/runtime/accounting.js +303 -0
- package/dist/daemon/runtime/accounting.js.map +1 -0
- package/dist/daemon/runtime/agents.d.ts +16 -0
- package/dist/daemon/runtime/agents.js +71 -0
- package/dist/daemon/runtime/agents.js.map +1 -0
- package/dist/daemon/runtime/briefings.d.ts +134 -0
- package/dist/daemon/runtime/briefings.js +329 -0
- package/dist/daemon/runtime/briefings.js.map +1 -0
- package/dist/daemon/runtime/native-usage.d.ts +21 -0
- package/dist/daemon/runtime/native-usage.js +71 -0
- package/dist/daemon/runtime/native-usage.js.map +1 -0
- package/dist/daemon/runtime/queue.d.ts +94 -0
- package/dist/daemon/runtime/queue.js +222 -0
- package/dist/daemon/runtime/queue.js.map +1 -0
- package/dist/daemon/runtime/relay.d.ts +93 -0
- package/dist/daemon/runtime/relay.js +240 -0
- package/dist/daemon/runtime/relay.js.map +1 -0
- package/dist/daemon/runtime/turns.d.ts +202 -0
- package/dist/daemon/runtime/turns.js +663 -0
- package/dist/daemon/runtime/turns.js.map +1 -0
- package/dist/daemon/runtime-support.d.ts +76 -0
- package/dist/daemon/runtime-support.js +134 -0
- package/dist/daemon/runtime-support.js.map +1 -0
- package/dist/daemon/runtime.d.ts +739 -70
- package/dist/daemon/runtime.js +2751 -470
- package/dist/daemon/runtime.js.map +1 -1
- package/dist/daemon/server.d.ts +82 -55
- package/dist/daemon/server.js +674 -1435
- package/dist/daemon/server.js.map +1 -1
- package/dist/daemon/shift.d.ts +70 -0
- package/dist/daemon/shift.js +170 -0
- package/dist/daemon/shift.js.map +1 -0
- package/dist/daemon/specs.d.ts +92 -0
- package/dist/daemon/specs.js +285 -0
- package/dist/daemon/specs.js.map +1 -0
- package/dist/daemon/system.d.ts +208 -0
- package/dist/daemon/system.js +557 -0
- package/dist/daemon/system.js.map +1 -0
- package/dist/daemon/tasks.js +1 -1
- package/dist/daemon/team-brain.d.ts +136 -0
- package/dist/daemon/team-brain.js +659 -0
- package/dist/daemon/team-brain.js.map +1 -0
- package/dist/daemon/team-coordinator.d.ts +78 -0
- package/dist/daemon/team-coordinator.js +408 -0
- package/dist/daemon/team-coordinator.js.map +1 -0
- package/dist/daemon/team.d.ts +339 -0
- package/dist/daemon/team.js +1433 -0
- package/dist/daemon/team.js.map +1 -0
- package/dist/daemon/terminals.d.ts +22 -3
- package/dist/daemon/terminals.js +236 -16
- package/dist/daemon/terminals.js.map +1 -1
- package/dist/hub/client.d.ts +120 -0
- package/dist/hub/client.js +228 -0
- package/dist/hub/client.js.map +1 -0
- package/dist/hub/server.d.ts +32 -0
- package/dist/hub/server.js +180 -0
- package/dist/hub/server.js.map +1 -0
- package/dist/hub/supabase-client.d.ts +220 -0
- package/dist/hub/supabase-client.js +843 -0
- package/dist/hub/supabase-client.js.map +1 -0
- package/dist/observability/ask.d.ts +91 -0
- package/dist/observability/ask.js +179 -0
- package/dist/observability/ask.js.map +1 -0
- package/dist/observability/clickhouse.d.ts +33 -0
- package/dist/observability/clickhouse.js +48 -0
- package/dist/observability/clickhouse.js.map +1 -0
- package/dist/observability/decisions.d.ts +176 -0
- package/dist/observability/decisions.js +377 -0
- package/dist/observability/decisions.js.map +1 -0
- package/dist/observability/index.d.ts +89 -0
- package/dist/observability/index.js +348 -0
- package/dist/observability/index.js.map +1 -0
- package/dist/observability/insights.d.ts +151 -0
- package/dist/observability/insights.js +319 -0
- package/dist/observability/insights.js.map +1 -0
- package/dist/observability/logs-query.d.ts +82 -0
- package/dist/observability/logs-query.js +102 -0
- package/dist/observability/logs-query.js.map +1 -0
- package/dist/observability/logs.d.ts +72 -0
- package/dist/observability/logs.js +295 -0
- package/dist/observability/logs.js.map +1 -0
- package/dist/observability/metrics.d.ts +86 -0
- package/dist/observability/metrics.js +303 -0
- package/dist/observability/metrics.js.map +1 -0
- package/dist/observability/otlp.d.ts +127 -0
- package/dist/observability/otlp.js +242 -0
- package/dist/observability/otlp.js.map +1 -0
- package/dist/observability/snapshots.d.ts +43 -0
- package/dist/observability/snapshots.js +129 -0
- package/dist/observability/snapshots.js.map +1 -0
- package/dist/observability/triage.d.ts +49 -0
- package/dist/observability/triage.js +284 -0
- package/dist/observability/triage.js.map +1 -0
- package/dist/providers/adapter.d.ts +50 -0
- package/dist/providers/adapter.js +38 -0
- package/dist/providers/adapter.js.map +1 -0
- package/dist/providers/agent.d.ts +110 -0
- package/dist/providers/agent.js +487 -0
- package/dist/providers/agent.js.map +1 -0
- package/dist/providers/approvals.d.ts +24 -0
- package/dist/providers/approvals.js +78 -0
- package/dist/providers/approvals.js.map +1 -0
- package/dist/providers/attachments.d.ts +20 -0
- package/dist/providers/attachments.js +51 -0
- package/dist/providers/attachments.js.map +1 -0
- package/dist/providers/claude/adapter.d.ts +99 -0
- package/dist/providers/claude/adapter.js +790 -0
- package/dist/providers/claude/adapter.js.map +1 -0
- package/dist/providers/codex/adapter.d.ts +113 -0
- package/dist/providers/codex/adapter.js +881 -0
- package/dist/providers/codex/adapter.js.map +1 -0
- package/dist/providers/codex/instructions.d.ts +11 -0
- package/dist/providers/codex/instructions.js +154 -0
- package/dist/providers/codex/instructions.js.map +1 -0
- package/dist/providers/codex/rpc.d.ts +36 -0
- package/dist/providers/codex/rpc.js +104 -0
- package/dist/providers/codex/rpc.js.map +1 -0
- package/dist/providers/contracts.d.ts +339 -0
- package/dist/providers/contracts.js +19 -0
- package/dist/providers/contracts.js.map +1 -0
- package/dist/providers/directory.d.ts +70 -0
- package/dist/providers/directory.js +95 -0
- package/dist/providers/directory.js.map +1 -0
- package/dist/providers/errors.d.ts +47 -0
- package/dist/providers/errors.js +24 -0
- package/dist/providers/errors.js.map +1 -0
- package/dist/providers/index.d.ts +18 -0
- package/dist/providers/index.js +19 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/providers/ingestion.d.ts +80 -0
- package/dist/providers/ingestion.js +330 -0
- package/dist/providers/ingestion.js.map +1 -0
- package/dist/providers/live.d.ts +22 -0
- package/dist/providers/live.js +56 -0
- package/dist/providers/live.js.map +1 -0
- package/dist/providers/process.d.ts +29 -0
- package/dist/providers/process.js +71 -0
- package/dist/providers/process.js.map +1 -0
- package/dist/providers/reaper.d.ts +33 -0
- package/dist/providers/reaper.js +65 -0
- package/dist/providers/reaper.js.map +1 -0
- package/dist/providers/service.d.ts +87 -0
- package/dist/providers/service.js +256 -0
- package/dist/providers/service.js.map +1 -0
- package/dist/sdk.d.ts +1 -1
- package/dist/sdk.js +1 -1
- package/dist/sdk.js.map +1 -1
- package/dist/types.d.ts +385 -6
- package/dist/types.js +1 -0
- package/dist/types.js.map +1 -1
- package/dist/version.d.ts +7 -0
- package/dist/version.js +16 -0
- package/dist/version.js.map +1 -0
- package/dist/web/app.css +4643 -0
- package/dist/web/app.js +22640 -0
- package/dist/web/app.js.map +7 -0
- package/dist/web/shell.html +39 -0
- package/docs/README.md +21 -0
- package/docs/SETUP.md +10 -0
- package/docs/adapters.md +21 -8
- package/docs/brain-continuity.md +186 -0
- package/docs/cloud.md +108 -0
- package/docs/crews.md +62 -0
- package/docs/design-system.md +31 -2
- package/docs/integration-notes.md +128 -20
- package/docs/loompad.md +105 -0
- package/docs/observability.md +220 -0
- package/docs/play-store.md +123 -0
- package/docs/proposals/agent-teams.md +249 -0
- package/docs/proposals/brain-continuity-implementation.md +468 -0
- package/docs/proposals/brain-session-continuity.md +623 -0
- package/docs/proposals/t3code-port.md +81 -0
- package/docs/proposals/t3code-reference.md +95 -0
- package/docs/refactoring/AGENT-TEAMS-PROGRESS.md +88 -0
- package/docs/refactoring/BOUNDARIES.md +120 -0
- package/docs/refactoring/BRAIN-AUDIT.md +176 -0
- package/docs/refactoring/BRAIN-NOTES.md +390 -0
- package/docs/refactoring/BRAIN-TODO.md +214 -0
- package/docs/refactoring/NOTES.md +129 -0
- package/docs/refactoring/T3-PORT-NOTES.md +165 -0
- package/docs/refactoring/T3-PORT-TODO.md +123 -0
- package/docs/refactoring/TODO.md +46 -0
- package/docs/refactoring/brain-benchmark-2026-09-28.json +48 -0
- package/docs/refactoring/brain-benchmark-audit-2026-09-28.json +48 -0
- package/docs/release-notes/v0.2.0.md +35 -0
- package/docs/release-notes/v0.2.1.md +59 -0
- package/docs/release-notes/v0.2.3.md +77 -0
- package/docs/release-notes/v0.2.4.md +128 -0
- package/docs/release-notes/v0.2.5.md +153 -0
- package/docs/release-notes/v0.2.6.md +62 -0
- package/docs/release-notes/v1.0.0.md +120 -0
- package/docs/release-notes/v1.0.1.md +77 -0
- package/docs/release-notes/v1.1.0.md +71 -0
- package/docs/release-notes/v1.1.1.md +38 -0
- package/docs/teams-architecture.md +697 -0
- package/docs/teams.md +171 -0
- package/docs/windows.md +85 -0
- package/package.json +22 -9
- package/dist/adapters/claude-code.d.ts +0 -26
- package/dist/adapters/claude-code.js +0 -209
- package/dist/adapters/claude-code.js.map +0 -1
- package/dist/adapters/codex.d.ts +0 -51
- package/dist/adapters/codex.js +0 -291
- package/dist/adapters/codex.js.map +0 -1
- package/docs/img/board-linear.png +0 -0
- package/docs/img/board-projects.png +0 -0
- package/docs/img/brain.png +0 -0
- package/docs/img/settings.png +0 -0
- package/docs/img/workspace.png +0 -0
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Observatory insights, read back from the backend's ClickHouse store: the spans
|
|
3
|
+
* behind the Span Replay viewer and the Trace Waterfall, the per-agent burn-rate
|
|
4
|
+
* series, and a deterministic 0–100 Agent Health Score. All of it is derived from
|
|
5
|
+
* the same real gen_ai spans Loom exports — nothing here is synthetic.
|
|
6
|
+
*
|
|
7
|
+
* The scoring (`healthScore`) is a pure function so it is unit-tested without a
|
|
8
|
+
* live ClickHouse, and it is the same math the UI badges render.
|
|
9
|
+
*/
|
|
10
|
+
import { SERVICE_NAME, SPAN_TABLE, chLiteral, chQuery } from "./clickhouse.js";
|
|
11
|
+
const SELECT = `
|
|
12
|
+
toString(trace_id) AS traceId, toString(span_id) AS spanId,
|
|
13
|
+
toUnixTimestamp64Milli(timestamp) AS ts, name,
|
|
14
|
+
round(duration_nano / 1e6) AS ms, status_code AS code, status_message AS msg,
|
|
15
|
+
attributes_string['gen_ai.agent.id'] AS agent,
|
|
16
|
+
attributes_string['gen_ai.system'] AS ade,
|
|
17
|
+
attributes_string['gen_ai.request.model'] AS model,
|
|
18
|
+
attributes_string['loom.handoff.from'] AS handoffFrom,
|
|
19
|
+
attributes_string['loom.handoff.to'] AS handoffTo,
|
|
20
|
+
toFloat64(attributes_number['gen_ai.usage.cost_usd']) AS cost,
|
|
21
|
+
toUInt32(attributes_number['gen_ai.usage.input_tokens']) AS tin,
|
|
22
|
+
toUInt32(attributes_number['gen_ai.usage.output_tokens']) AS tout`;
|
|
23
|
+
const SERVICE = `\`resource_string_service$$name\` = '${chLiteral(SERVICE_NAME)}'`;
|
|
24
|
+
function rowToSpan(r) {
|
|
25
|
+
return {
|
|
26
|
+
traceId: String(r.traceId ?? ""),
|
|
27
|
+
spanId: String(r.spanId ?? ""),
|
|
28
|
+
ts: Number(r.ts ?? 0),
|
|
29
|
+
name: String(r.name ?? ""),
|
|
30
|
+
ms: Number(r.ms ?? 0),
|
|
31
|
+
code: Number(r.code ?? 0),
|
|
32
|
+
msg: String(r.msg ?? ""),
|
|
33
|
+
agent: String(r.agent ?? ""),
|
|
34
|
+
ade: String(r.ade ?? ""),
|
|
35
|
+
model: String(r.model ?? ""),
|
|
36
|
+
tin: Number(r.tin ?? 0),
|
|
37
|
+
tout: Number(r.tout ?? 0),
|
|
38
|
+
cost: Number(r.cost ?? 0),
|
|
39
|
+
handoffFrom: String(r.handoffFrom ?? ""),
|
|
40
|
+
handoffTo: String(r.handoffTo ?? ""),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/** Recent spans for a project (optionally one agent), newest first. */
|
|
44
|
+
export async function fetchSpans(project, opts = {}) {
|
|
45
|
+
const proj = chLiteral(project);
|
|
46
|
+
const limit = Math.min(500, Math.max(1, opts.limit ?? 200));
|
|
47
|
+
const agentFilter = opts.agent ? `AND attributes_string['gen_ai.agent.id'] = '${chLiteral(opts.agent)}'` : "";
|
|
48
|
+
const sql = `SELECT ${SELECT} FROM ${SPAN_TABLE}
|
|
49
|
+
WHERE ${SERVICE} AND attributes_string['loom.project'] = '${proj}' ${agentFilter}
|
|
50
|
+
ORDER BY timestamp DESC LIMIT ${limit}`;
|
|
51
|
+
return (await chQuery(sql)).map(rowToSpan);
|
|
52
|
+
}
|
|
53
|
+
/** How many error spans an agent has emitted since `sinceMs` — the self-heal recheck signal. */
|
|
54
|
+
export async function recentAgentErrors(project, agent, sinceMs) {
|
|
55
|
+
const proj = chLiteral(project);
|
|
56
|
+
const a = chLiteral(agent);
|
|
57
|
+
const sinceSec = Math.floor(Math.max(0, sinceMs) / 1000);
|
|
58
|
+
const sql = `SELECT count() AS n FROM ${SPAN_TABLE}
|
|
59
|
+
WHERE ${SERVICE} AND attributes_string['loom.project'] = '${proj}'
|
|
60
|
+
AND attributes_string['gen_ai.agent.id'] = '${a}'
|
|
61
|
+
AND status_code = 2
|
|
62
|
+
AND timestamp >= toDateTime(${sinceSec})`;
|
|
63
|
+
const rows = await chQuery(sql);
|
|
64
|
+
return Number(rows[0]?.n ?? 0);
|
|
65
|
+
}
|
|
66
|
+
/** Every span in one trace, oldest first — the waterfall's rows. */
|
|
67
|
+
export async function traceSpans(traceId) {
|
|
68
|
+
const t = chLiteral(traceId);
|
|
69
|
+
if (!t)
|
|
70
|
+
return [];
|
|
71
|
+
const sql = `SELECT ${SELECT} FROM ${SPAN_TABLE}
|
|
72
|
+
WHERE ${SERVICE} AND trace_id = '${t}' ORDER BY timestamp ASC LIMIT 300`;
|
|
73
|
+
return (await chQuery(sql)).map(rowToSpan);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Fallback: derive InsightSpans from the local event log — the same data the
|
|
77
|
+
* backend gets — so the Observatory's replay/health/waterfall still work when
|
|
78
|
+
* ClickHouse is down or the agent hasn't exported anything yet.
|
|
79
|
+
*/
|
|
80
|
+
export function insightSpansFromLog(events, agent) {
|
|
81
|
+
const out = [];
|
|
82
|
+
for (const e of events) {
|
|
83
|
+
const p = (e.payload ?? {});
|
|
84
|
+
const mine = e.agentId === agent || p.to === agent || p.from === agent;
|
|
85
|
+
if (agent && !mine)
|
|
86
|
+
continue;
|
|
87
|
+
const common = { traceId: "", spanId: String(e.id), ts: e.ts, ade: "", model: String(p.model ?? ""), handoffFrom: "", handoffTo: "" };
|
|
88
|
+
if (e.kind === "run_complete") {
|
|
89
|
+
out.push({ ...common, name: "gen_ai.agent.turn", ms: Number(p.durationMs ?? 0), code: p.error ? 2 : 1, msg: String(p.error ?? ""),
|
|
90
|
+
agent: e.agentId ?? "", tin: Number(p.inputTokens ?? 0), tout: Number(p.outputTokens ?? 0), cost: Number(p.costUsd ?? 0) });
|
|
91
|
+
}
|
|
92
|
+
else if (e.kind === "error") {
|
|
93
|
+
out.push({ ...common, name: "loom.error", ms: 0, code: 2, msg: String(p.message ?? "error"), agent: e.agentId ?? "", tin: 0, tout: 0, cost: 0 });
|
|
94
|
+
}
|
|
95
|
+
else if (e.kind === "handoff") {
|
|
96
|
+
out.push({ ...common, name: "loom.baton.handoff", ms: 0, code: 1, msg: `${p.from ?? "?"} -> ${p.to ?? "?"}`,
|
|
97
|
+
agent: e.agentId ?? "", tin: 0, tout: 0, cost: 0, handoffFrom: String(p.from ?? ""), handoffTo: String(p.to ?? "") });
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return out.sort((a, b) => b.ts - a.ts);
|
|
101
|
+
}
|
|
102
|
+
/** Per-agent cost over time, bucketed — the burn-rate sparkline's data + a linear projection. */
|
|
103
|
+
export async function burnSeries(project, opts = {}) {
|
|
104
|
+
const proj = chLiteral(project);
|
|
105
|
+
const hours = Math.min(720, Math.max(1, opts.hours ?? 24));
|
|
106
|
+
const nBuckets = Math.min(60, Math.max(2, opts.buckets ?? 12));
|
|
107
|
+
const stepSec = Math.max(60, Math.round((hours * 3600) / nBuckets));
|
|
108
|
+
const sql = `SELECT
|
|
109
|
+
toUnixTimestamp(toStartOfInterval(timestamp, INTERVAL ${stepSec} SECOND)) AS bucket,
|
|
110
|
+
attributes_string['gen_ai.agent.id'] AS agent,
|
|
111
|
+
sum(toFloat64(attributes_number['gen_ai.usage.cost_usd'])) AS cost
|
|
112
|
+
FROM ${SPAN_TABLE}
|
|
113
|
+
WHERE ${SERVICE} AND attributes_string['loom.project'] = '${proj}'
|
|
114
|
+
AND name = 'gen_ai.agent.turn'
|
|
115
|
+
AND timestamp >= now() - INTERVAL ${hours} HOUR
|
|
116
|
+
GROUP BY bucket, agent ORDER BY bucket ASC`;
|
|
117
|
+
const rows = await chQuery(sql);
|
|
118
|
+
const map = new Map();
|
|
119
|
+
for (const r of rows) {
|
|
120
|
+
const t = Number(r.bucket ?? 0) * 1000;
|
|
121
|
+
const agent = String(r.agent ?? "unknown");
|
|
122
|
+
const cost = Number(r.cost ?? 0);
|
|
123
|
+
const b = map.get(t) ?? { t, byAgent: {}, total: 0 };
|
|
124
|
+
b.byAgent[agent] = (b.byAgent[agent] ?? 0) + cost;
|
|
125
|
+
b.total += cost;
|
|
126
|
+
map.set(t, b);
|
|
127
|
+
}
|
|
128
|
+
const buckets = [...map.values()].sort((a, b) => a.t - b.t);
|
|
129
|
+
const totalUsd = buckets.reduce((a, b) => a + b.total, 0);
|
|
130
|
+
const ratePerHour = totalUsd / hours;
|
|
131
|
+
return { hours, buckets, totalUsd, ratePerHour, projected24h: ratePerHour * 24 };
|
|
132
|
+
}
|
|
133
|
+
/* ------------------------------------------------------------------------- *
|
|
134
|
+
* Metrics read-back
|
|
135
|
+
*
|
|
136
|
+
* Loom exports loom.turns, loom.handoffs, loom.cost.usd, loom.agents.active,
|
|
137
|
+
* gen_ai.client.token.usage and a gen_ai.client.operation.duration histogram
|
|
138
|
+
* (observability/metrics.ts). Reading them back closes the loop: the same
|
|
139
|
+
* numbers the Observatory shows can be checked against what the backend stored.
|
|
140
|
+
*
|
|
141
|
+
* The backend splits a metric across two tables, confirmed by DESCRIBE on the
|
|
142
|
+
* live stack:
|
|
143
|
+
* distributed_time_series_v4 — one row per (fingerprint, hour) carrying the
|
|
144
|
+
* metadata and label maps: metric_name, type, unit, temporality, attrs
|
|
145
|
+
* (metric attributes), resource_attrs, labels (a JSON string).
|
|
146
|
+
* distributed_samples_v4 — the actual points: metric_name, fingerprint,
|
|
147
|
+
* unix_milli, value.
|
|
148
|
+
* `fingerprint` is the join key: it identifies one label set, i.e. one series.
|
|
149
|
+
* ------------------------------------------------------------------------- */
|
|
150
|
+
/**
|
|
151
|
+
* The metric tables the collector writes into. Defaults are that layout;
|
|
152
|
+
* both are overridable for a backend that stores its metrics elsewhere.
|
|
153
|
+
*/
|
|
154
|
+
export const METRIC_SAMPLES_TABLE = process.env.LOOM_METRIC_SAMPLES_TABLE || "signoz_metrics.distributed_samples_v4";
|
|
155
|
+
export const METRIC_SERIES_TABLE = process.env.LOOM_METRIC_SERIES_TABLE || "signoz_metrics.distributed_time_series_v4";
|
|
156
|
+
/**
|
|
157
|
+
* A histogram does not exist under its own name in ClickHouse — the OTLP
|
|
158
|
+
* exporter fans it out into suffixed sums/gauges. Asking for
|
|
159
|
+
* "gen_ai.client.operation.duration" therefore has to expand to the real names,
|
|
160
|
+
* or the query silently returns nothing.
|
|
161
|
+
*
|
|
162
|
+
* `.bucket` is deliberately excluded: it is one series per histogram boundary
|
|
163
|
+
* (le="0.005", le="0.01", …), which is a dozen lines of noise on a chart and
|
|
164
|
+
* never what a caller asking for "the duration metric" means. A caller that
|
|
165
|
+
* genuinely wants buckets can name `...duration.bucket` explicitly, which the
|
|
166
|
+
* exact-name path below still honours.
|
|
167
|
+
*/
|
|
168
|
+
const HISTOGRAM_SUFFIXES = ["sum", "count", "min", "max"];
|
|
169
|
+
/** Expand each requested name to itself plus its histogram parts, deduped. */
|
|
170
|
+
export function expandMetricNames(names) {
|
|
171
|
+
const out = new Set();
|
|
172
|
+
for (const raw of names) {
|
|
173
|
+
const n = chLiteral(String(raw ?? "").trim());
|
|
174
|
+
if (!n)
|
|
175
|
+
continue;
|
|
176
|
+
out.add(n);
|
|
177
|
+
// Suffixing a non-histogram name is harmless — nothing matches it.
|
|
178
|
+
if (!HISTOGRAM_SUFFIXES.some((s) => n.endsWith(`.${s}`)) && !n.endsWith(".bucket")) {
|
|
179
|
+
for (const s of HISTOGRAM_SUFFIXES)
|
|
180
|
+
out.add(`${n}.${s}`);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return [...out];
|
|
184
|
+
}
|
|
185
|
+
/** Drop the backend's internal label keys (`__name__`, `__temporality__`, …). */
|
|
186
|
+
function cleanLabels(v) {
|
|
187
|
+
const out = {};
|
|
188
|
+
if (v && typeof v === "object") {
|
|
189
|
+
for (const [k, val] of Object.entries(v)) {
|
|
190
|
+
if (k.startsWith("__"))
|
|
191
|
+
continue;
|
|
192
|
+
out[k] = String(val ?? "");
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return out;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Build the SQL for a metric series query — split out so the join, the project
|
|
199
|
+
* filter and the bucketing are testable without a live ClickHouse.
|
|
200
|
+
*
|
|
201
|
+
* Note what the inner subquery does *not* do: filter time_series_v4 by
|
|
202
|
+
* unix_milli. That column is floored to the hour there, so a naive
|
|
203
|
+
* `>= sinceMs` would drop the metadata row for a series whose hour started
|
|
204
|
+
* before the window and take the whole series with it. The metric_name +
|
|
205
|
+
* project filters already narrow it to a handful of fingerprints, so the time
|
|
206
|
+
* filter belongs on the samples side only, where it is exact.
|
|
207
|
+
*
|
|
208
|
+
* The GROUP BY inside the subquery collapses the one-row-per-hour duplication;
|
|
209
|
+
* without it the join would multiply every sample by the number of hours the
|
|
210
|
+
* series has existed.
|
|
211
|
+
*/
|
|
212
|
+
export function buildMetricQuery(names, opts) {
|
|
213
|
+
const proj = chLiteral(opts.project ?? "");
|
|
214
|
+
const step = Math.max(1000, Math.round(opts.stepMs ?? 60_000));
|
|
215
|
+
const since = Math.max(0, Math.round(opts.sinceMs ?? Date.now() - 6 * 3600_000));
|
|
216
|
+
const list = expandMetricNames(names)
|
|
217
|
+
.map((n) => `'${n}'`)
|
|
218
|
+
.join(", ");
|
|
219
|
+
return `SELECT
|
|
220
|
+
any(s.s_metric) AS metric, any(s.s_type) AS type, any(s.s_unit) AS unit,
|
|
221
|
+
any(s.s_temporality) AS temporality, any(s.s_attrs) AS labels,
|
|
222
|
+
toString(sm.fingerprint) AS fingerprint,
|
|
223
|
+
intDiv(sm.unix_milli, ${step}) * ${step} AS t,
|
|
224
|
+
sum(sm.value) AS vsum, avg(sm.value) AS vavg, max(sm.value) AS vmax, count() AS n
|
|
225
|
+
FROM ${METRIC_SAMPLES_TABLE} AS sm
|
|
226
|
+
INNER JOIN (
|
|
227
|
+
SELECT fingerprint,
|
|
228
|
+
any(metric_name) AS s_metric, any(type) AS s_type, any(unit) AS s_unit,
|
|
229
|
+
any(temporality) AS s_temporality, any(attrs) AS s_attrs
|
|
230
|
+
FROM ${METRIC_SERIES_TABLE}
|
|
231
|
+
WHERE metric_name IN (${list})
|
|
232
|
+
AND resource_attrs['service.name'] = '${chLiteral(SERVICE_NAME)}'
|
|
233
|
+
AND attrs['loom.project'] = '${proj}'
|
|
234
|
+
GROUP BY fingerprint
|
|
235
|
+
) AS s ON s.fingerprint = sm.fingerprint
|
|
236
|
+
WHERE sm.metric_name IN (${list}) AND sm.unix_milli >= ${since}
|
|
237
|
+
GROUP BY fingerprint, t
|
|
238
|
+
ORDER BY metric ASC, fingerprint ASC, t ASC`;
|
|
239
|
+
}
|
|
240
|
+
/** Fold the flat (series, bucket) rows into one entry per series, points ordered. */
|
|
241
|
+
export function rowsToSeries(rows) {
|
|
242
|
+
const byFp = new Map();
|
|
243
|
+
for (const r of rows) {
|
|
244
|
+
const fp = String(r.fingerprint ?? "");
|
|
245
|
+
const type = String(r.type ?? "");
|
|
246
|
+
let s = byFp.get(fp);
|
|
247
|
+
if (!s) {
|
|
248
|
+
s = {
|
|
249
|
+
metric: String(r.metric ?? ""),
|
|
250
|
+
type,
|
|
251
|
+
unit: String(r.unit ?? ""),
|
|
252
|
+
temporality: String(r.temporality ?? ""),
|
|
253
|
+
labels: cleanLabels(r.labels),
|
|
254
|
+
fingerprint: fp,
|
|
255
|
+
prefer: type === "Gauge" ? "avg" : "sum",
|
|
256
|
+
points: [],
|
|
257
|
+
};
|
|
258
|
+
byFp.set(fp, s);
|
|
259
|
+
}
|
|
260
|
+
s.points.push({
|
|
261
|
+
t: Number(r.t ?? 0),
|
|
262
|
+
sum: Number(r.vsum ?? 0),
|
|
263
|
+
avg: Number(r.vavg ?? 0),
|
|
264
|
+
max: Number(r.vmax ?? 0),
|
|
265
|
+
n: Number(r.n ?? 0),
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
for (const s of byFp.values())
|
|
269
|
+
s.points.sort((a, b) => a.t - b.t);
|
|
270
|
+
return [...byFp.values()].sort((a, b) => a.metric.localeCompare(b.metric) || a.fingerprint.localeCompare(b.fingerprint));
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Read named metrics back for one project, one entry per distinct label set.
|
|
274
|
+
* Throws if ClickHouse is unreachable — like logs, there is no local substitute
|
|
275
|
+
* for a metric the backend never received, and the route reports that honestly.
|
|
276
|
+
*/
|
|
277
|
+
export async function fetchMetricSeries(names, opts) {
|
|
278
|
+
if (!expandMetricNames(names).length)
|
|
279
|
+
return [];
|
|
280
|
+
return rowsToSeries(await chQuery(buildMetricQuery(names, opts)));
|
|
281
|
+
}
|
|
282
|
+
/** The metrics Loom actually emits — the default set when a caller names none. */
|
|
283
|
+
export const LOOM_METRIC_NAMES = [
|
|
284
|
+
"loom.turns",
|
|
285
|
+
"loom.handoffs",
|
|
286
|
+
"loom.cost.usd",
|
|
287
|
+
"loom.agents.active",
|
|
288
|
+
"gen_ai.client.token.usage",
|
|
289
|
+
"gen_ai.client.operation.duration",
|
|
290
|
+
];
|
|
291
|
+
const isErr = (s) => s.code === 2 || s.name === "loom.error" || s.name === "loom.route.failed";
|
|
292
|
+
/**
|
|
293
|
+
* A deterministic 0–100 health score from an agent's own spans. Four penalty
|
|
294
|
+
* buckets subtract from 100:
|
|
295
|
+
* errorRate (≤40) — share of spans that errored
|
|
296
|
+
* latency (≤25) — share of turns slower than 30s
|
|
297
|
+
* tokenBloat(≤20) — average input tokens above an 80k comfort line
|
|
298
|
+
* recency (≤15) — how recently the last error fired (fades over 30 min)
|
|
299
|
+
* Pure: same input, same score — this is what the UI badge shows.
|
|
300
|
+
*/
|
|
301
|
+
export function healthScore(spans, now = Date.now()) {
|
|
302
|
+
const turns = spans.filter((s) => s.name === "gen_ai.agent.turn");
|
|
303
|
+
const errs = spans.filter(isErr).sort((a, b) => b.ts - a.ts);
|
|
304
|
+
const nTurns = turns.length;
|
|
305
|
+
const errorRate = spans.length ? Math.round((errs.length / spans.length) * 40) : 0;
|
|
306
|
+
const slow = turns.filter((t) => t.ms > 30_000).length;
|
|
307
|
+
const latency = nTurns ? Math.round(Math.min(1, slow / nTurns) * 25) : 0;
|
|
308
|
+
const avgIn = nTurns ? turns.reduce((a, t) => a + t.tin, 0) / nTurns : 0;
|
|
309
|
+
const tokenBloat = Math.round(Math.min(1, Math.max(0, (avgIn - 80_000) / 120_000)) * 20);
|
|
310
|
+
let recency = 0;
|
|
311
|
+
if (errs[0]) {
|
|
312
|
+
const ageMin = (now - errs[0].ts) / 60_000;
|
|
313
|
+
recency = Math.round(Math.max(0, 1 - ageMin / 30) * 15);
|
|
314
|
+
}
|
|
315
|
+
const score = Math.max(0, Math.min(100, 100 - errorRate - latency - tokenBloat - recency));
|
|
316
|
+
const grade = score >= 80 ? "healthy" : score >= 50 ? "degraded" : "unhealthy";
|
|
317
|
+
return { score, grade, turns: nTurns, errorCount: errs.length, buckets: { errorRate, latency, tokenBloat, recency } };
|
|
318
|
+
}
|
|
319
|
+
//# sourceMappingURL=insights.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"insights.js","sourceRoot":"","sources":["../../src/observability/insights.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reading Loom's own logs back out of the telemetry backend.
|
|
3
|
+
*
|
|
4
|
+
* Loom already *emits* structured logs over OTLP (see observability/logs.ts).
|
|
5
|
+
* This is the other half: the query that lets the Observatory show an agent what
|
|
6
|
+
* it actually said, correlated to the span it said it in.
|
|
7
|
+
*
|
|
8
|
+
* Two things make this different from the span read-back in insights.ts:
|
|
9
|
+
*
|
|
10
|
+
* 1. There is NO local fallback. Spans have one — `insightSpansFromLog` derives
|
|
11
|
+
* equivalent spans from the event log, because a span is a summary of an
|
|
12
|
+
* event Loom already has on disk. A log line is not: severity, body and the
|
|
13
|
+
* trace/span correlation only exist once the log record has been built and
|
|
14
|
+
* shipped. Reconstructing something log-shaped from the event log would be
|
|
15
|
+
* inventing data, so when ClickHouse is unreachable the caller reports
|
|
16
|
+
* `from: "unavailable"` and the UI says so plainly.
|
|
17
|
+
*
|
|
18
|
+
* 2. The identity filter is doubled up. `resources_string['service.name']`
|
|
19
|
+
* pins us to Loom (rather than any other service shipping to the same
|
|
20
|
+
* backend), and `attributes_string['loom.project']` pins us to one project.
|
|
21
|
+
* Confirmed against the live table: Loom sets loom.project as a *record*
|
|
22
|
+
* attribute, not a resource attribute, so it lands in attributes_string —
|
|
23
|
+
* one daemon's logs would otherwise leak into another project's view.
|
|
24
|
+
*/
|
|
25
|
+
/**
|
|
26
|
+
* The log store. Columns confirmed with DESCRIBE against a live 25.12 instance.
|
|
27
|
+
* As with the span table, this is the layout the collector writes into and is
|
|
28
|
+
* overridable for a backend that stores its logs elsewhere.
|
|
29
|
+
*/
|
|
30
|
+
export declare const LOG_TABLE: string;
|
|
31
|
+
/**
|
|
32
|
+
* A log line, normalised for the UI.
|
|
33
|
+
*
|
|
34
|
+
* `ts` is milliseconds (the column is UInt64 *nanoseconds*, hence the intDiv).
|
|
35
|
+
* `traceId`/`spanId` are already lowercase hex Strings in v2 — no unhex/hex
|
|
36
|
+
* dance like the trace tables need — and are empty strings when the record was
|
|
37
|
+
* emitted outside a span, which is normal for status lines.
|
|
38
|
+
*/
|
|
39
|
+
export type InsightLog = {
|
|
40
|
+
ts: number;
|
|
41
|
+
severity: string;
|
|
42
|
+
/** OTel severity_number (1–24). Kept so the UI can sort/threshold without parsing text. */
|
|
43
|
+
severityNumber: number;
|
|
44
|
+
body: string;
|
|
45
|
+
traceId: string;
|
|
46
|
+
spanId: string;
|
|
47
|
+
agent: string;
|
|
48
|
+
/** loom.event.kind — which event produced the line (run_complete, status, message, ...). */
|
|
49
|
+
kind: string;
|
|
50
|
+
/** loom.chat — the conversation the line belongs to. */
|
|
51
|
+
chat: string;
|
|
52
|
+
};
|
|
53
|
+
export type FetchLogsOpts = {
|
|
54
|
+
project: string;
|
|
55
|
+
agent?: string;
|
|
56
|
+
/** One or more severity_text values, comma-separated and case-insensitive ("error,warn"). */
|
|
57
|
+
severity?: string;
|
|
58
|
+
/** Pin to a single trace — the log side of the Trace Waterfall. */
|
|
59
|
+
traceId?: string;
|
|
60
|
+
/** Case-insensitive substring match on the body. */
|
|
61
|
+
search?: string;
|
|
62
|
+
limit?: number;
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* Build the SQL for a log query. Split out from the fetch so the WHERE clause —
|
|
66
|
+
* where a missing project filter would be a real leak — is unit-testable without
|
|
67
|
+
* a live ClickHouse.
|
|
68
|
+
*
|
|
69
|
+
* Every interpolated value goes through `chLiteral`, which strips anything
|
|
70
|
+
* outside `[\w.\-:/ ]`. That is deliberately blunt: it also removes `%` and `_`,
|
|
71
|
+
* so a search term can never turn into a LIKE wildcard, and quotes can never
|
|
72
|
+
* escape the literal. The cost is that punctuation in a search term is dropped
|
|
73
|
+
* rather than matched, which for substring search over log bodies is a fine
|
|
74
|
+
* trade.
|
|
75
|
+
*/
|
|
76
|
+
export declare function buildLogsQuery(opts: FetchLogsOpts): string;
|
|
77
|
+
/**
|
|
78
|
+
* Recent log lines for one project, newest first. Throws if ClickHouse is
|
|
79
|
+
* unreachable — the caller turns that into `from: "unavailable"` rather than
|
|
80
|
+
* substituting anything made up.
|
|
81
|
+
*/
|
|
82
|
+
export declare function fetchLogs(opts: FetchLogsOpts): Promise<InsightLog[]>;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reading Loom's own logs back out of the telemetry backend.
|
|
3
|
+
*
|
|
4
|
+
* Loom already *emits* structured logs over OTLP (see observability/logs.ts).
|
|
5
|
+
* This is the other half: the query that lets the Observatory show an agent what
|
|
6
|
+
* it actually said, correlated to the span it said it in.
|
|
7
|
+
*
|
|
8
|
+
* Two things make this different from the span read-back in insights.ts:
|
|
9
|
+
*
|
|
10
|
+
* 1. There is NO local fallback. Spans have one — `insightSpansFromLog` derives
|
|
11
|
+
* equivalent spans from the event log, because a span is a summary of an
|
|
12
|
+
* event Loom already has on disk. A log line is not: severity, body and the
|
|
13
|
+
* trace/span correlation only exist once the log record has been built and
|
|
14
|
+
* shipped. Reconstructing something log-shaped from the event log would be
|
|
15
|
+
* inventing data, so when ClickHouse is unreachable the caller reports
|
|
16
|
+
* `from: "unavailable"` and the UI says so plainly.
|
|
17
|
+
*
|
|
18
|
+
* 2. The identity filter is doubled up. `resources_string['service.name']`
|
|
19
|
+
* pins us to Loom (rather than any other service shipping to the same
|
|
20
|
+
* backend), and `attributes_string['loom.project']` pins us to one project.
|
|
21
|
+
* Confirmed against the live table: Loom sets loom.project as a *record*
|
|
22
|
+
* attribute, not a resource attribute, so it lands in attributes_string —
|
|
23
|
+
* one daemon's logs would otherwise leak into another project's view.
|
|
24
|
+
*/
|
|
25
|
+
import { SERVICE_NAME, chLiteral, chQuery } from "./clickhouse.js";
|
|
26
|
+
/**
|
|
27
|
+
* The log store. Columns confirmed with DESCRIBE against a live 25.12 instance.
|
|
28
|
+
* As with the span table, this is the layout the collector writes into and is
|
|
29
|
+
* overridable for a backend that stores its logs elsewhere.
|
|
30
|
+
*/
|
|
31
|
+
export const LOG_TABLE = process.env.LOOM_LOG_TABLE || "signoz_logs.distributed_logs_v2";
|
|
32
|
+
const SELECT = `
|
|
33
|
+
intDiv(timestamp, 1000000) AS ts,
|
|
34
|
+
severity_text AS severity,
|
|
35
|
+
toUInt16(severity_number) AS severityNumber,
|
|
36
|
+
body,
|
|
37
|
+
trace_id AS traceId,
|
|
38
|
+
span_id AS spanId,
|
|
39
|
+
attributes_string['gen_ai.agent.id'] AS agent,
|
|
40
|
+
attributes_string['loom.event.kind'] AS kind,
|
|
41
|
+
attributes_string['loom.chat'] AS chat`;
|
|
42
|
+
/** Same shape as insights.ts's SERVICE guard, but logs keep resource attrs in a Map. */
|
|
43
|
+
const SERVICE = `resources_string['service.name'] = '${chLiteral(SERVICE_NAME)}'`;
|
|
44
|
+
function rowToLog(r) {
|
|
45
|
+
return {
|
|
46
|
+
ts: Number(r.ts ?? 0),
|
|
47
|
+
severity: String(r.severity ?? ""),
|
|
48
|
+
severityNumber: Number(r.severityNumber ?? 0),
|
|
49
|
+
body: String(r.body ?? ""),
|
|
50
|
+
traceId: String(r.traceId ?? ""),
|
|
51
|
+
spanId: String(r.spanId ?? ""),
|
|
52
|
+
agent: String(r.agent ?? ""),
|
|
53
|
+
kind: String(r.kind ?? ""),
|
|
54
|
+
chat: String(r.chat ?? ""),
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Build the SQL for a log query. Split out from the fetch so the WHERE clause —
|
|
59
|
+
* where a missing project filter would be a real leak — is unit-testable without
|
|
60
|
+
* a live ClickHouse.
|
|
61
|
+
*
|
|
62
|
+
* Every interpolated value goes through `chLiteral`, which strips anything
|
|
63
|
+
* outside `[\w.\-:/ ]`. That is deliberately blunt: it also removes `%` and `_`,
|
|
64
|
+
* so a search term can never turn into a LIKE wildcard, and quotes can never
|
|
65
|
+
* escape the literal. The cost is that punctuation in a search term is dropped
|
|
66
|
+
* rather than matched, which for substring search over log bodies is a fine
|
|
67
|
+
* trade.
|
|
68
|
+
*/
|
|
69
|
+
export function buildLogsQuery(opts) {
|
|
70
|
+
const proj = chLiteral(opts.project ?? "");
|
|
71
|
+
const limit = Math.min(1000, Math.max(1, opts.limit ?? 200));
|
|
72
|
+
const where = [SERVICE, `attributes_string['loom.project'] = '${proj}'`];
|
|
73
|
+
if (opts.agent)
|
|
74
|
+
where.push(`attributes_string['gen_ai.agent.id'] = '${chLiteral(opts.agent)}'`);
|
|
75
|
+
if (opts.traceId)
|
|
76
|
+
where.push(`trace_id = '${chLiteral(opts.traceId)}'`);
|
|
77
|
+
// severity_text is LowCardinality(String) and Loom writes it upper-case, but
|
|
78
|
+
// callers type "error". Compare upper-cased on both sides so either works.
|
|
79
|
+
const sevs = (opts.severity ?? "")
|
|
80
|
+
.split(",")
|
|
81
|
+
.map((s) => chLiteral(s.trim()).toUpperCase())
|
|
82
|
+
.filter(Boolean);
|
|
83
|
+
if (sevs.length)
|
|
84
|
+
where.push(`upper(severity_text) IN (${sevs.map((s) => `'${s}'`).join(", ")})`);
|
|
85
|
+
// positionCaseInsensitive rather than ILIKE: no wildcard semantics to smuggle
|
|
86
|
+
// in, and it reads as "contains" which is what the search box promises.
|
|
87
|
+
const q = chLiteral((opts.search ?? "").trim());
|
|
88
|
+
if (q)
|
|
89
|
+
where.push(`positionCaseInsensitive(body, '${q}') > 0`);
|
|
90
|
+
return `SELECT ${SELECT} FROM ${LOG_TABLE}
|
|
91
|
+
WHERE ${where.join("\n AND ")}
|
|
92
|
+
ORDER BY timestamp DESC LIMIT ${limit}`;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Recent log lines for one project, newest first. Throws if ClickHouse is
|
|
96
|
+
* unreachable — the caller turns that into `from: "unavailable"` rather than
|
|
97
|
+
* substituting anything made up.
|
|
98
|
+
*/
|
|
99
|
+
export async function fetchLogs(opts) {
|
|
100
|
+
return (await chQuery(buildLogsQuery(opts))).map(rowToLog);
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=logs-query.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logs-query.js","sourceRoot":"","sources":["../../src/observability/logs-query.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Loom → OpenTelemetry logs (OTLP/HTTP JSON, `/v1/logs`).
|
|
3
|
+
*
|
|
4
|
+
* Loom already has a log: the LoomEvent stream is the source of truth for the
|
|
5
|
+
* whole product. This ships it. Every agent message, tool call, file edit,
|
|
6
|
+
* decision, route step, budget pause and error becomes one OTLP log record with
|
|
7
|
+
* the same `service.name` as the spans, and — when the event happened inside a
|
|
8
|
+
* turn — the same `traceId`/`spanId`.
|
|
9
|
+
*
|
|
10
|
+
* That last part is the whole point. Without trace correlation this is just a
|
|
11
|
+
* second copy of the event log in a different database. With it, a slow turn
|
|
12
|
+
* in the trace view gets a "related logs" tab showing what the agent actually
|
|
13
|
+
* said and did while it was slow, and a log line has a link back to the span
|
|
14
|
+
* that produced it. Traces tell you a turn took 40s; logs tell you why.
|
|
15
|
+
*
|
|
16
|
+
* Same contract as the other two exporters: buffered, best-effort, never throws
|
|
17
|
+
* into the agent loop. Off with LOOM_OTEL_LOGS=0 (or any consent opt-out).
|
|
18
|
+
*/
|
|
19
|
+
import type { LoomEvent } from "../types.js";
|
|
20
|
+
import { type AttrValue, type TelemetryConfig } from "./otlp.js";
|
|
21
|
+
/**
|
|
22
|
+
* OTLP SeverityNumber. The spec's named levels; we use four of them.
|
|
23
|
+
*
|
|
24
|
+
* Mapped honestly, which mostly means resisting the urge to inflate. An agent
|
|
25
|
+
* saying something is INFO, not DEBUG — it is the primary content of the
|
|
26
|
+
* product. A turn that failed is ERROR. Something a human needs to look at but
|
|
27
|
+
* that isn't a failure (blocked on input, over budget, a suggested handoff) is
|
|
28
|
+
* WARN. Adapter lifecycle chatter is DEBUG. Nothing is FATAL: Loom survives
|
|
29
|
+
* every event it logs, and a severity nobody can act on is noise with a red
|
|
30
|
+
* icon.
|
|
31
|
+
*/
|
|
32
|
+
export declare const SEVERITY: {
|
|
33
|
+
readonly DEBUG: 5;
|
|
34
|
+
readonly INFO: 9;
|
|
35
|
+
readonly WARN: 13;
|
|
36
|
+
readonly ERROR: 17;
|
|
37
|
+
};
|
|
38
|
+
export type LogRecordInput = {
|
|
39
|
+
timeUnixNano: bigint;
|
|
40
|
+
severityNumber: number;
|
|
41
|
+
body: string;
|
|
42
|
+
attributes: Record<string, AttrValue>;
|
|
43
|
+
traceId?: string;
|
|
44
|
+
spanId?: string;
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* A LoomEvent → the log record it should produce, or null for kinds that carry
|
|
48
|
+
* nothing a human would read.
|
|
49
|
+
*
|
|
50
|
+
* Pure, and exported so the mapping (and especially the severity mapping) is
|
|
51
|
+
* testable without a collector — same arrangement as `eventToSpan`.
|
|
52
|
+
*/
|
|
53
|
+
export declare function eventToLogRecord(event: LoomEvent, ctx?: {
|
|
54
|
+
project?: string;
|
|
55
|
+
}): LogRecordInput | null;
|
|
56
|
+
/** Cut an over-long body and say by how much, rather than lying about length. */
|
|
57
|
+
export declare function truncate(body: string, max?: number): string;
|
|
58
|
+
/** Buffered OTLP log exporter. Mirrors LoomTelemetry's batching exactly. */
|
|
59
|
+
export declare class LoomLogs {
|
|
60
|
+
private readonly cfg;
|
|
61
|
+
private buffer;
|
|
62
|
+
private timer;
|
|
63
|
+
private readonly logsUrl;
|
|
64
|
+
private readonly resource;
|
|
65
|
+
constructor(cfg: TelemetryConfig);
|
|
66
|
+
get enabled(): boolean;
|
|
67
|
+
/** Buffer one record. No-op when logs are disabled. */
|
|
68
|
+
log(input: LogRecordInput): void;
|
|
69
|
+
private ensureTimer;
|
|
70
|
+
private drain;
|
|
71
|
+
flush(): void;
|
|
72
|
+
}
|