@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,303 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Loom → OpenTelemetry metrics (OTLP/HTTP JSON, `/v1/metrics`).
|
|
3
|
+
*
|
|
4
|
+
* Same shape as the trace exporter next door: aggregate in memory, flush on a
|
|
5
|
+
* short timer, POST, never throw. What differs is that a metric is a fold, not
|
|
6
|
+
* an event — several turns collapse into one datapoint — so this holds a small
|
|
7
|
+
* aggregation table keyed by metric name + attribute set instead of a list.
|
|
8
|
+
*
|
|
9
|
+
* Every number here is derived from a LoomEvent that actually happened. There
|
|
10
|
+
* is no periodic "emit 0 so the chart has a line" path, because a flat zero and
|
|
11
|
+
* "nothing happened" look identical on a graph and only one of them is true.
|
|
12
|
+
*
|
|
13
|
+
* ── Naming ──────────────────────────────────────────────────────────────────
|
|
14
|
+
* `gen_ai.*` names and attributes follow the OpenTelemetry GenAI semantic
|
|
15
|
+
* conventions (semantic-conventions-genai, the GenAI metrics doc split out of
|
|
16
|
+
* the main semconv repo): `gen_ai.client.token.usage` with a
|
|
17
|
+
* `gen_ai.token.type` of `input`/`output`, `gen_ai.client.operation.duration`
|
|
18
|
+
* in seconds, dimensioned by `gen_ai.operation.name`, `gen_ai.system` and
|
|
19
|
+
* `gen_ai.request.model`. `loom.*` names are ours — nothing in the convention
|
|
20
|
+
* covers batons, routes or a per-agent daily spend cap — and follow its style.
|
|
21
|
+
*
|
|
22
|
+
* One deliberate deviation: the convention models `gen_ai.client.token.usage`
|
|
23
|
+
* as a *histogram* (a distribution of tokens-per-request). Loom emits it as a
|
|
24
|
+
* monotonic sum. A histogram earns its keep when you want percentiles over many
|
|
25
|
+
* requests; Loom reports exactly one input/output pair per turn, and what
|
|
26
|
+
* anyone asks this data is "how many tokens did this agent burn today" — a sum
|
|
27
|
+
* answers that exactly, and a histogram's `sum` field only approximates it once
|
|
28
|
+
* you start dropping buckets. `gen_ai.client.operation.duration` stays a
|
|
29
|
+
* histogram, as specified, because turn latency genuinely is a distribution.
|
|
30
|
+
*
|
|
31
|
+
* ── Temporality ─────────────────────────────────────────────────────────────
|
|
32
|
+
* Delta, everywhere (AGGREGATION_TEMPORALITY_DELTA). Cumulative asks the
|
|
33
|
+
* *client* to remember every counter's running total for the life of the
|
|
34
|
+
* process and expects the backend to detect resets. Loom's daemon restarts
|
|
35
|
+
* whenever you edit a config or upgrade the CLI, so cumulative would mean a
|
|
36
|
+
* counter reset every few minutes — the exact case cumulative handles worst.
|
|
37
|
+
* Delta makes each export self-contained: what happened since the last flush,
|
|
38
|
+
* nothing remembered, nothing to reset. Collectors accept both.
|
|
39
|
+
*/
|
|
40
|
+
import { encodeAttributes, postOtlp, resourceAttributes, } from "./otlp.js";
|
|
41
|
+
/** OTLP AggregationTemporality. 1 = CUMULATIVE, 2 = DELTA. See the header. */
|
|
42
|
+
const TEMPORALITY_DELTA = 2;
|
|
43
|
+
/** Flush window, matched to the trace exporter's so signals land together. */
|
|
44
|
+
const FLUSH_MS = 1500;
|
|
45
|
+
/** Hard cap on distinct series held between flushes; see `guardCardinality`. */
|
|
46
|
+
const MAX_SERIES = 512;
|
|
47
|
+
/**
|
|
48
|
+
* Bucket boundaries for `gen_ai.client.operation.duration`, in seconds, as
|
|
49
|
+
* specified by the GenAI metrics convention. Kept verbatim rather than tuned:
|
|
50
|
+
* matching the convention is what lets a GenAI dashboard built for any
|
|
51
|
+
* other instrumented service read Loom's turns too.
|
|
52
|
+
*/
|
|
53
|
+
const DURATION_BUCKETS_S = [
|
|
54
|
+
0.01, 0.02, 0.04, 0.08, 0.16, 0.32, 0.64, 1.28, 2.56, 5.12, 10.24, 20.48, 40.96, 81.92,
|
|
55
|
+
];
|
|
56
|
+
const META = {
|
|
57
|
+
"gen_ai.client.token.usage": { unit: "{token}", description: "Tokens consumed by an agent turn, by token type." },
|
|
58
|
+
"gen_ai.client.operation.duration": { unit: "s", description: "Duration of a completed agent turn." },
|
|
59
|
+
"loom.turns": { unit: "{turn}", description: "Agent turns that reached completion, by outcome." },
|
|
60
|
+
"loom.cost.usd": { unit: "USD", description: "Money an adapter reported spending on a turn." },
|
|
61
|
+
"loom.agents.active": { unit: "{agent}", description: "Agents currently executing a turn." },
|
|
62
|
+
"loom.handoffs": { unit: "{handoff}", description: "Baton handoffs between agents." },
|
|
63
|
+
};
|
|
64
|
+
export class LoomMetrics {
|
|
65
|
+
cfg;
|
|
66
|
+
series = new Map();
|
|
67
|
+
timer = null;
|
|
68
|
+
metricsUrl;
|
|
69
|
+
resource;
|
|
70
|
+
/**
|
|
71
|
+
* Start of the current delta window. A delta datapoint's
|
|
72
|
+
* `startTimeUnixNano`..`timeUnixNano` must bracket exactly the interval the
|
|
73
|
+
* value accumulated over, and consecutive windows must not overlap — so this
|
|
74
|
+
* advances to the previous flush's end rather than being re-read from the
|
|
75
|
+
* clock each time.
|
|
76
|
+
*/
|
|
77
|
+
windowStartNs;
|
|
78
|
+
constructor(cfg) {
|
|
79
|
+
this.cfg = cfg;
|
|
80
|
+
this.metricsUrl = `${cfg.endpoint}/v1/metrics`;
|
|
81
|
+
this.resource = resourceAttributes(cfg);
|
|
82
|
+
this.windowStartNs = nowNs();
|
|
83
|
+
}
|
|
84
|
+
get enabled() {
|
|
85
|
+
return this.cfg.metricsEnabled && typeof globalThis.fetch === "function";
|
|
86
|
+
}
|
|
87
|
+
/** Add to a monotonic counter. Negative or non-finite deltas are ignored. */
|
|
88
|
+
addCount(name, value, attributes, isInt = true) {
|
|
89
|
+
if (!this.enabled || !Number.isFinite(value) || value < 0)
|
|
90
|
+
return;
|
|
91
|
+
const bag = this.bag(name);
|
|
92
|
+
const attrs = encodeAttributes(attributes);
|
|
93
|
+
const key = seriesKey(attrs);
|
|
94
|
+
const existing = bag.get(key);
|
|
95
|
+
if (existing?.kind === "sum")
|
|
96
|
+
existing.value += value;
|
|
97
|
+
else if (this.guardCardinality(bag))
|
|
98
|
+
bag.set(key, { kind: "sum", attributes: attrs, value, isInt });
|
|
99
|
+
this.ensureTimer();
|
|
100
|
+
}
|
|
101
|
+
/** Record one observation into a histogram. */
|
|
102
|
+
record(name, value, attributes, bounds = DURATION_BUCKETS_S) {
|
|
103
|
+
if (!this.enabled || !Number.isFinite(value) || value < 0)
|
|
104
|
+
return;
|
|
105
|
+
const bag = this.bag(name);
|
|
106
|
+
const attrs = encodeAttributes(attributes);
|
|
107
|
+
const key = seriesKey(attrs);
|
|
108
|
+
let s = bag.get(key);
|
|
109
|
+
if (s?.kind !== "histogram") {
|
|
110
|
+
if (!this.guardCardinality(bag))
|
|
111
|
+
return;
|
|
112
|
+
s = {
|
|
113
|
+
kind: "histogram",
|
|
114
|
+
attributes: attrs,
|
|
115
|
+
bounds,
|
|
116
|
+
// One bucket per boundary plus the +Inf overflow bucket — OTLP requires
|
|
117
|
+
// bucketCounts.length === explicitBounds.length + 1.
|
|
118
|
+
counts: new Array(bounds.length + 1).fill(0),
|
|
119
|
+
count: 0,
|
|
120
|
+
sum: 0,
|
|
121
|
+
min: value,
|
|
122
|
+
max: value,
|
|
123
|
+
};
|
|
124
|
+
bag.set(key, s);
|
|
125
|
+
}
|
|
126
|
+
let i = 0;
|
|
127
|
+
while (i < s.bounds.length && value > s.bounds[i])
|
|
128
|
+
i++;
|
|
129
|
+
s.counts[i] += 1;
|
|
130
|
+
s.count += 1;
|
|
131
|
+
s.sum += value;
|
|
132
|
+
s.min = Math.min(s.min, value);
|
|
133
|
+
s.max = Math.max(s.max, value);
|
|
134
|
+
this.ensureTimer();
|
|
135
|
+
}
|
|
136
|
+
/** Observe a gauge's current value, as of now. */
|
|
137
|
+
setGauge(name, value, attributes = {}) {
|
|
138
|
+
if (!this.enabled || !Number.isFinite(value))
|
|
139
|
+
return;
|
|
140
|
+
const bag = this.bag(name);
|
|
141
|
+
const attrs = encodeAttributes(attributes);
|
|
142
|
+
const key = seriesKey(attrs);
|
|
143
|
+
if (!bag.has(key) && !this.guardCardinality(bag))
|
|
144
|
+
return;
|
|
145
|
+
bag.set(key, { kind: "gauge", attributes: attrs, value, observedNs: nowNs() });
|
|
146
|
+
this.ensureTimer();
|
|
147
|
+
}
|
|
148
|
+
flush() {
|
|
149
|
+
if (this.timer) {
|
|
150
|
+
clearTimeout(this.timer);
|
|
151
|
+
this.timer = null;
|
|
152
|
+
}
|
|
153
|
+
this.drain();
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Build the OTLP payload for everything accumulated so far and clear the
|
|
157
|
+
* window. Exported for tests: asserting on the JSON we would send beats
|
|
158
|
+
* asserting on a mocked fetch, and the payload shape is the part a collector
|
|
159
|
+
* actually rejects.
|
|
160
|
+
*/
|
|
161
|
+
collect(nowOverrideNs) {
|
|
162
|
+
if (this.series.size === 0)
|
|
163
|
+
return null;
|
|
164
|
+
const endNs = nowOverrideNs ?? nowNs();
|
|
165
|
+
const startNs = this.windowStartNs;
|
|
166
|
+
const metrics = [];
|
|
167
|
+
for (const [name, bag] of this.series) {
|
|
168
|
+
if (bag.size === 0)
|
|
169
|
+
continue;
|
|
170
|
+
const meta = META[name] ?? { unit: "1", description: "" };
|
|
171
|
+
metrics.push({
|
|
172
|
+
name,
|
|
173
|
+
unit: meta.unit,
|
|
174
|
+
description: meta.description,
|
|
175
|
+
...body([...bag.values()], startNs, endNs),
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
this.series.clear();
|
|
179
|
+
this.windowStartNs = endNs;
|
|
180
|
+
if (metrics.length === 0)
|
|
181
|
+
return null;
|
|
182
|
+
return {
|
|
183
|
+
resourceMetrics: [
|
|
184
|
+
{
|
|
185
|
+
resource: { attributes: this.resource },
|
|
186
|
+
scopeMetrics: [{ scope: { name: "loom" }, metrics }],
|
|
187
|
+
},
|
|
188
|
+
],
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
bag(name) {
|
|
192
|
+
let b = this.series.get(name);
|
|
193
|
+
if (!b)
|
|
194
|
+
this.series.set(name, (b = new Map()));
|
|
195
|
+
return b;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Refuse to open a new series past the cap.
|
|
199
|
+
*
|
|
200
|
+
* Metric attributes come from agent ids and model names, which are bounded in
|
|
201
|
+
* practice — but a misbehaving adapter reporting a unique model string per
|
|
202
|
+
* turn would otherwise turn this map into an unbounded leak and the backend's
|
|
203
|
+
* index into a mess. Dropping the *new* series keeps the ones already being
|
|
204
|
+
* measured intact and correct; the alternative (evicting) would silently
|
|
205
|
+
* discard counts that were already recorded.
|
|
206
|
+
*/
|
|
207
|
+
guardCardinality(bag) {
|
|
208
|
+
return bag.size < MAX_SERIES;
|
|
209
|
+
}
|
|
210
|
+
ensureTimer() {
|
|
211
|
+
if (this.timer)
|
|
212
|
+
return;
|
|
213
|
+
this.timer = setTimeout(() => {
|
|
214
|
+
this.timer = null;
|
|
215
|
+
this.drain();
|
|
216
|
+
}, FLUSH_MS);
|
|
217
|
+
if (typeof this.timer.unref === "function")
|
|
218
|
+
this.timer.unref();
|
|
219
|
+
}
|
|
220
|
+
drain() {
|
|
221
|
+
const payload = this.collect();
|
|
222
|
+
if (!payload)
|
|
223
|
+
return;
|
|
224
|
+
postOtlp(this.metricsUrl, this.cfg.headers, payload);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
/** The `sum` / `histogram` / `gauge` body for one metric's worth of series. */
|
|
228
|
+
function body(all, startNs, endNs) {
|
|
229
|
+
const start = startNs.toString();
|
|
230
|
+
const time = endNs.toString();
|
|
231
|
+
const first = all[0];
|
|
232
|
+
if (first.kind === "gauge") {
|
|
233
|
+
// No temporality and no start time on a gauge: it is a reading, not an
|
|
234
|
+
// interval. The timestamp is when we actually observed it.
|
|
235
|
+
return {
|
|
236
|
+
gauge: {
|
|
237
|
+
dataPoints: all
|
|
238
|
+
.filter((s) => s.kind === "gauge")
|
|
239
|
+
.map((s) => ({
|
|
240
|
+
attributes: s.attributes,
|
|
241
|
+
timeUnixNano: s.observedNs.toString(),
|
|
242
|
+
...numeric(s.value, true),
|
|
243
|
+
})),
|
|
244
|
+
},
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
if (first.kind === "histogram") {
|
|
248
|
+
return {
|
|
249
|
+
histogram: {
|
|
250
|
+
aggregationTemporality: TEMPORALITY_DELTA,
|
|
251
|
+
dataPoints: all
|
|
252
|
+
.filter((s) => s.kind === "histogram")
|
|
253
|
+
.map((s) => ({
|
|
254
|
+
attributes: s.attributes,
|
|
255
|
+
startTimeUnixNano: start,
|
|
256
|
+
timeUnixNano: time,
|
|
257
|
+
count: String(s.count),
|
|
258
|
+
sum: s.sum,
|
|
259
|
+
bucketCounts: s.counts.map(String),
|
|
260
|
+
explicitBounds: s.bounds,
|
|
261
|
+
min: s.min,
|
|
262
|
+
max: s.max,
|
|
263
|
+
})),
|
|
264
|
+
},
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
return {
|
|
268
|
+
sum: {
|
|
269
|
+
aggregationTemporality: TEMPORALITY_DELTA,
|
|
270
|
+
// Every counter Loom emits only ever goes up within a window — turns
|
|
271
|
+
// completed, tokens spent, dollars burned, batons passed. None of them
|
|
272
|
+
// can be undone, so isMonotonic is the truth and lets a backend offer rate().
|
|
273
|
+
isMonotonic: true,
|
|
274
|
+
dataPoints: all
|
|
275
|
+
.filter((s) => s.kind === "sum")
|
|
276
|
+
.map((s) => ({
|
|
277
|
+
attributes: s.attributes,
|
|
278
|
+
startTimeUnixNano: start,
|
|
279
|
+
timeUnixNano: time,
|
|
280
|
+
...numeric(s.value, s.isInt),
|
|
281
|
+
})),
|
|
282
|
+
},
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* `asInt` (int64, string-encoded per the proto3 JSON mapping) or `asDouble`.
|
|
287
|
+
* Counts are integers; money and seconds are not, and rounding either to an int
|
|
288
|
+
* would report $0 for a real three-cent turn.
|
|
289
|
+
*/
|
|
290
|
+
function numeric(value, isInt) {
|
|
291
|
+
return isInt ? { asInt: String(Math.round(value)) } : { asDouble: value };
|
|
292
|
+
}
|
|
293
|
+
/** Stable identity for an attribute set, so repeat observations coalesce. */
|
|
294
|
+
function seriesKey(attrs) {
|
|
295
|
+
return attrs
|
|
296
|
+
.map((a) => `${a.key}=${Object.values(a.value)[0]}`)
|
|
297
|
+
.sort()
|
|
298
|
+
.join("");
|
|
299
|
+
}
|
|
300
|
+
function nowNs() {
|
|
301
|
+
return BigInt(Date.now()) * 1000000n;
|
|
302
|
+
}
|
|
303
|
+
//# sourceMappingURL=metrics.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metrics.js","sourceRoot":"","sources":["../../src/observability/metrics.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Loom → OpenTelemetry.
|
|
3
|
+
*
|
|
4
|
+
* A self-contained OTLP/HTTP (JSON) trace exporter — no OpenTelemetry SDK
|
|
5
|
+
* dependency, just `fetch`. Loom's agent lifecycle is already a stream of
|
|
6
|
+
* LoomEvents (turns, routes, baton handoffs, memory folds); this maps the
|
|
7
|
+
* notable ones to spans using the OpenTelemetry GenAI semantic conventions
|
|
8
|
+
* (`gen_ai.*`) so they land in the LLM/GenAI views every OTLP backend builds
|
|
9
|
+
* on those conventions.
|
|
10
|
+
*
|
|
11
|
+
* Backend-agnostic on purpose: this speaks OTLP/HTTP and nothing else, so it
|
|
12
|
+
* works against a plain OpenTelemetry Collector, Grafana/Tempo, Jaeger, SigNoz,
|
|
13
|
+
* Honeycomb — anything with a `/v1/traces` endpoint. Point
|
|
14
|
+
* OTEL_EXPORTER_OTLP_ENDPOINT at yours.
|
|
15
|
+
*
|
|
16
|
+
* This module owns traces plus the pieces every signal shares: the resolved
|
|
17
|
+
* config, the resource attributes, and the OTLP attribute encoding. The
|
|
18
|
+
* metrics (`./metrics.ts`) and logs (`./logs.ts`) exporters import those rather
|
|
19
|
+
* than growing their own copy, so a service.name change lands on all three at
|
|
20
|
+
* once and a span, a datapoint and a log line always agree about who emitted
|
|
21
|
+
* them.
|
|
22
|
+
*
|
|
23
|
+
* Egress is best-effort and consent-gated: if no collector is reachable the
|
|
24
|
+
* POST fails silently and never surfaces in the instrumented operation. Turn
|
|
25
|
+
* it off with DO_NOT_TRACK=1 or LOOM_TELEMETRY_DISABLED=1.
|
|
26
|
+
*/
|
|
27
|
+
export type KeyValue = {
|
|
28
|
+
key: string;
|
|
29
|
+
value: {
|
|
30
|
+
stringValue: string;
|
|
31
|
+
} | {
|
|
32
|
+
intValue: string;
|
|
33
|
+
} | {
|
|
34
|
+
doubleValue: number;
|
|
35
|
+
} | {
|
|
36
|
+
boolValue: boolean;
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
/** Anything we know how to put on an OTLP attribute. */
|
|
40
|
+
export type AttrValue = string | number | boolean | undefined | null;
|
|
41
|
+
export type TelemetryConfig = {
|
|
42
|
+
/** Collector base URL, e.g. http://localhost:4318. */
|
|
43
|
+
endpoint: string;
|
|
44
|
+
/** Logical service name reported to the backend. */
|
|
45
|
+
serviceName: string;
|
|
46
|
+
/** Outgoing headers, including any ingestion key the backend wants. */
|
|
47
|
+
headers: Record<string, string>;
|
|
48
|
+
enabled: boolean;
|
|
49
|
+
/**
|
|
50
|
+
* Per-signal switches, ANDed with `enabled`.
|
|
51
|
+
*
|
|
52
|
+
* The consent opt-outs (DO_NOT_TRACK, LOOM_TELEMETRY_DISABLED, LOOM_OTEL=0)
|
|
53
|
+
* kill everything — consent is not per-signal. These two are a different
|
|
54
|
+
* thing: volume control for someone who wants traces but not the log firehose
|
|
55
|
+
* (Loom ships every agent message as a log record, which is the point, and
|
|
56
|
+
* also a lot of bytes). Off means off for that signal only.
|
|
57
|
+
*/
|
|
58
|
+
metricsEnabled: boolean;
|
|
59
|
+
logsEnabled: boolean;
|
|
60
|
+
};
|
|
61
|
+
/** Resolve config from the environment; disabled under standard opt-outs. */
|
|
62
|
+
export declare function resolveTelemetryConfig(env?: NodeJS.ProcessEnv): TelemetryConfig;
|
|
63
|
+
/**
|
|
64
|
+
* The `resource` block every signal carries. Identical across traces, metrics
|
|
65
|
+
* and logs on purpose: a backend keys a service off `service.name`, so a
|
|
66
|
+
* mismatch here is what makes a trace and its own logs look like two different
|
|
67
|
+
* apps.
|
|
68
|
+
*/
|
|
69
|
+
export declare function resourceAttributes(cfg: TelemetryConfig): KeyValue[];
|
|
70
|
+
/**
|
|
71
|
+
* Encode an attribute bag, dropping empties.
|
|
72
|
+
*
|
|
73
|
+
* Absent is not the same as zero or "": a datapoint tagged
|
|
74
|
+
* `gen_ai.request.model=""` claims we know the model and it is the empty
|
|
75
|
+
* string. Leaving the key off says we don't know, which is the truth for
|
|
76
|
+
* adapters that never report one.
|
|
77
|
+
*/
|
|
78
|
+
export declare function encodeAttributes(attributes: Record<string, AttrValue>): KeyValue[];
|
|
79
|
+
/** 16-byte trace id / 8-byte span id as lowercase hex (no crypto dep needed). */
|
|
80
|
+
export declare function hexId(bytes: number): string;
|
|
81
|
+
export type SpanInput = {
|
|
82
|
+
name: string;
|
|
83
|
+
startNs: bigint;
|
|
84
|
+
endNs: bigint;
|
|
85
|
+
attributes: Record<string, string | number | boolean | undefined | null>;
|
|
86
|
+
error?: string;
|
|
87
|
+
/** Correlate related spans (a turn + its tool calls) under one trace. */
|
|
88
|
+
traceId?: string;
|
|
89
|
+
};
|
|
90
|
+
/** A fresh 16-byte trace id — used to group a turn's spans into one trace. */
|
|
91
|
+
export declare function newTraceId(): string;
|
|
92
|
+
/**
|
|
93
|
+
* The exporter. Batches spans in a short window and POSTs OTLP/HTTP JSON.
|
|
94
|
+
* Failures are swallowed by design so tracing never breaks the app.
|
|
95
|
+
*/
|
|
96
|
+
export declare class LoomTelemetry {
|
|
97
|
+
private readonly cfg;
|
|
98
|
+
private buffer;
|
|
99
|
+
private timer;
|
|
100
|
+
private readonly tracesUrl;
|
|
101
|
+
private readonly resourceAttributes;
|
|
102
|
+
constructor(cfg: TelemetryConfig);
|
|
103
|
+
get enabled(): boolean;
|
|
104
|
+
/**
|
|
105
|
+
* Emit one span, returning the span id it was given — or undefined when
|
|
106
|
+
* telemetry is off and no span exists.
|
|
107
|
+
*
|
|
108
|
+
* The id goes back to the caller so a log record covering the same event can
|
|
109
|
+
* carry the same `spanId` and be clickable from that exact span, rather than
|
|
110
|
+
* only from the trace as a whole. Undefined rather than "" for the same
|
|
111
|
+
* reason turnTraceId() is: an empty span id is a link to nothing.
|
|
112
|
+
*/
|
|
113
|
+
span(input: SpanInput): string | undefined;
|
|
114
|
+
private ensureTimer;
|
|
115
|
+
private drain;
|
|
116
|
+
flush(): void;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Fire one OTLP/HTTP JSON payload at the collector and forget about it.
|
|
120
|
+
*
|
|
121
|
+
* Deliberately not awaited and deliberately swallowing everything: telemetry is
|
|
122
|
+
* a side channel, and a collector that is down, slow or returning 400 must not
|
|
123
|
+
* become an unhandled rejection in an agent turn. Shared by all three signals
|
|
124
|
+
* so they fail the same silent way.
|
|
125
|
+
*/
|
|
126
|
+
export declare function postOtlp(url: string, headers: Record<string, string>, payload: unknown): void;
|
|
127
|
+
export declare function kv(key: string, v: string | number | boolean): KeyValue;
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Loom → OpenTelemetry.
|
|
3
|
+
*
|
|
4
|
+
* A self-contained OTLP/HTTP (JSON) trace exporter — no OpenTelemetry SDK
|
|
5
|
+
* dependency, just `fetch`. Loom's agent lifecycle is already a stream of
|
|
6
|
+
* LoomEvents (turns, routes, baton handoffs, memory folds); this maps the
|
|
7
|
+
* notable ones to spans using the OpenTelemetry GenAI semantic conventions
|
|
8
|
+
* (`gen_ai.*`) so they land in the LLM/GenAI views every OTLP backend builds
|
|
9
|
+
* on those conventions.
|
|
10
|
+
*
|
|
11
|
+
* Backend-agnostic on purpose: this speaks OTLP/HTTP and nothing else, so it
|
|
12
|
+
* works against a plain OpenTelemetry Collector, Grafana/Tempo, Jaeger, SigNoz,
|
|
13
|
+
* Honeycomb — anything with a `/v1/traces` endpoint. Point
|
|
14
|
+
* OTEL_EXPORTER_OTLP_ENDPOINT at yours.
|
|
15
|
+
*
|
|
16
|
+
* This module owns traces plus the pieces every signal shares: the resolved
|
|
17
|
+
* config, the resource attributes, and the OTLP attribute encoding. The
|
|
18
|
+
* metrics (`./metrics.ts`) and logs (`./logs.ts`) exporters import those rather
|
|
19
|
+
* than growing their own copy, so a service.name change lands on all three at
|
|
20
|
+
* once and a span, a datapoint and a log line always agree about who emitted
|
|
21
|
+
* them.
|
|
22
|
+
*
|
|
23
|
+
* Egress is best-effort and consent-gated: if no collector is reachable the
|
|
24
|
+
* POST fails silently and never surfaces in the instrumented operation. Turn
|
|
25
|
+
* it off with DO_NOT_TRACK=1 or LOOM_TELEMETRY_DISABLED=1.
|
|
26
|
+
*/
|
|
27
|
+
const SPAN_KIND_INTERNAL = 1;
|
|
28
|
+
const STATUS_OK = 1;
|
|
29
|
+
const STATUS_ERROR = 2;
|
|
30
|
+
/**
|
|
31
|
+
* Parse OTEL_EXPORTER_OTLP_HEADERS — the standard `k=v,k2=v2` form.
|
|
32
|
+
*
|
|
33
|
+
* This is how a backend's ingestion key reaches us without Loom knowing which
|
|
34
|
+
* backend it is. Hardcoding one vendor's header name would have meant a code
|
|
35
|
+
* change per destination.
|
|
36
|
+
*/
|
|
37
|
+
function parseOtlpHeaders(raw) {
|
|
38
|
+
const out = {};
|
|
39
|
+
if (!raw)
|
|
40
|
+
return out;
|
|
41
|
+
for (const pair of raw.split(",")) {
|
|
42
|
+
const eq = pair.indexOf("=");
|
|
43
|
+
if (eq <= 0)
|
|
44
|
+
continue;
|
|
45
|
+
const k = pair.slice(0, eq).trim();
|
|
46
|
+
const v = pair.slice(eq + 1).trim();
|
|
47
|
+
if (k && v)
|
|
48
|
+
out[k.toLowerCase()] = decodeURIComponent(v);
|
|
49
|
+
}
|
|
50
|
+
return out;
|
|
51
|
+
}
|
|
52
|
+
/** Resolve config from the environment; disabled under standard opt-outs. */
|
|
53
|
+
export function resolveTelemetryConfig(env = process.env) {
|
|
54
|
+
const optedOut = truthy(env.DO_NOT_TRACK) ||
|
|
55
|
+
truthy(env.LOOM_TELEMETRY_DISABLED) ||
|
|
56
|
+
env.LOOM_OTEL === "0";
|
|
57
|
+
const endpoint = (env.LOOM_OTEL_ENDPOINT ||
|
|
58
|
+
env.OTEL_EXPORTER_OTLP_ENDPOINT ||
|
|
59
|
+
"http://localhost:4318").replace(/\/+$/, "");
|
|
60
|
+
const headers = {
|
|
61
|
+
"content-type": "application/json",
|
|
62
|
+
...parseOtlpHeaders(env.OTEL_EXPORTER_OTLP_HEADERS),
|
|
63
|
+
};
|
|
64
|
+
const enabled = !optedOut;
|
|
65
|
+
return {
|
|
66
|
+
endpoint,
|
|
67
|
+
serviceName: env.LOOM_SERVICE_NAME || env.OTEL_SERVICE_NAME || "loom",
|
|
68
|
+
headers,
|
|
69
|
+
enabled,
|
|
70
|
+
metricsEnabled: enabled && env.LOOM_OTEL_METRICS !== "0",
|
|
71
|
+
logsEnabled: enabled && env.LOOM_OTEL_LOGS !== "0",
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* The `resource` block every signal carries. Identical across traces, metrics
|
|
76
|
+
* and logs on purpose: a backend keys a service off `service.name`, so a
|
|
77
|
+
* mismatch here is what makes a trace and its own logs look like two different
|
|
78
|
+
* apps.
|
|
79
|
+
*/
|
|
80
|
+
export function resourceAttributes(cfg) {
|
|
81
|
+
return [
|
|
82
|
+
kv("service.name", cfg.serviceName),
|
|
83
|
+
kv("service.namespace", "loom"),
|
|
84
|
+
kv("telemetry.sdk.name", "loom-otlp"),
|
|
85
|
+
kv("telemetry.sdk.language", "nodejs"),
|
|
86
|
+
];
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Encode an attribute bag, dropping empties.
|
|
90
|
+
*
|
|
91
|
+
* Absent is not the same as zero or "": a datapoint tagged
|
|
92
|
+
* `gen_ai.request.model=""` claims we know the model and it is the empty
|
|
93
|
+
* string. Leaving the key off says we don't know, which is the truth for
|
|
94
|
+
* adapters that never report one.
|
|
95
|
+
*/
|
|
96
|
+
export function encodeAttributes(attributes) {
|
|
97
|
+
const out = [];
|
|
98
|
+
for (const [k, v] of Object.entries(attributes)) {
|
|
99
|
+
if (v === undefined || v === null || v === "")
|
|
100
|
+
continue;
|
|
101
|
+
out.push(kv(k, v));
|
|
102
|
+
}
|
|
103
|
+
return out;
|
|
104
|
+
}
|
|
105
|
+
function truthy(v) {
|
|
106
|
+
return v === "1" || v === "true" || v === "yes";
|
|
107
|
+
}
|
|
108
|
+
/** 16-byte trace id / 8-byte span id as lowercase hex (no crypto dep needed). */
|
|
109
|
+
export function hexId(bytes) {
|
|
110
|
+
let s = "";
|
|
111
|
+
for (let i = 0; i < bytes; i++)
|
|
112
|
+
s += ((Math.random() * 256) | 0).toString(16).padStart(2, "0");
|
|
113
|
+
return s;
|
|
114
|
+
}
|
|
115
|
+
function str(v) {
|
|
116
|
+
return typeof v === "string" ? v : v == null ? "" : JSON.stringify(v);
|
|
117
|
+
}
|
|
118
|
+
/** A fresh 16-byte trace id — used to group a turn's spans into one trace. */
|
|
119
|
+
export function newTraceId() {
|
|
120
|
+
return hexId(16);
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* The exporter. Batches spans in a short window and POSTs OTLP/HTTP JSON.
|
|
124
|
+
* Failures are swallowed by design so tracing never breaks the app.
|
|
125
|
+
*/
|
|
126
|
+
export class LoomTelemetry {
|
|
127
|
+
cfg;
|
|
128
|
+
buffer = [];
|
|
129
|
+
timer = null;
|
|
130
|
+
tracesUrl;
|
|
131
|
+
resourceAttributes;
|
|
132
|
+
constructor(cfg) {
|
|
133
|
+
this.cfg = cfg;
|
|
134
|
+
this.tracesUrl = `${cfg.endpoint}/v1/traces`;
|
|
135
|
+
this.resourceAttributes = resourceAttributes(cfg);
|
|
136
|
+
}
|
|
137
|
+
get enabled() {
|
|
138
|
+
return this.cfg.enabled && typeof globalThis.fetch === "function";
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Emit one span, returning the span id it was given — or undefined when
|
|
142
|
+
* telemetry is off and no span exists.
|
|
143
|
+
*
|
|
144
|
+
* The id goes back to the caller so a log record covering the same event can
|
|
145
|
+
* carry the same `spanId` and be clickable from that exact span, rather than
|
|
146
|
+
* only from the trace as a whole. Undefined rather than "" for the same
|
|
147
|
+
* reason turnTraceId() is: an empty span id is a link to nothing.
|
|
148
|
+
*/
|
|
149
|
+
span(input) {
|
|
150
|
+
if (!this.enabled)
|
|
151
|
+
return undefined;
|
|
152
|
+
const spanId = hexId(8);
|
|
153
|
+
this.buffer.push({
|
|
154
|
+
traceId: input.traceId ?? hexId(16),
|
|
155
|
+
spanId,
|
|
156
|
+
name: input.name,
|
|
157
|
+
kind: SPAN_KIND_INTERNAL,
|
|
158
|
+
startTimeUnixNano: input.startNs.toString(),
|
|
159
|
+
endTimeUnixNano: input.endNs.toString(),
|
|
160
|
+
attributes: encodeAttributes(input.attributes),
|
|
161
|
+
status: input.error
|
|
162
|
+
? { code: STATUS_ERROR, message: input.error }
|
|
163
|
+
: { code: STATUS_OK },
|
|
164
|
+
});
|
|
165
|
+
if (this.buffer.length >= 64)
|
|
166
|
+
this.drain();
|
|
167
|
+
else
|
|
168
|
+
this.ensureTimer();
|
|
169
|
+
return spanId;
|
|
170
|
+
}
|
|
171
|
+
ensureTimer() {
|
|
172
|
+
if (this.timer)
|
|
173
|
+
return;
|
|
174
|
+
this.timer = setTimeout(() => {
|
|
175
|
+
this.timer = null;
|
|
176
|
+
this.drain();
|
|
177
|
+
}, 1500);
|
|
178
|
+
if (typeof this.timer.unref === "function")
|
|
179
|
+
this.timer.unref();
|
|
180
|
+
}
|
|
181
|
+
drain() {
|
|
182
|
+
if (this.buffer.length === 0)
|
|
183
|
+
return;
|
|
184
|
+
const spans = this.buffer;
|
|
185
|
+
this.buffer = [];
|
|
186
|
+
const payload = {
|
|
187
|
+
resourceSpans: [
|
|
188
|
+
{
|
|
189
|
+
resource: { attributes: this.resourceAttributes },
|
|
190
|
+
scopeSpans: [{ scope: { name: "loom" }, spans }],
|
|
191
|
+
},
|
|
192
|
+
],
|
|
193
|
+
};
|
|
194
|
+
postOtlp(this.tracesUrl, this.cfg.headers, payload);
|
|
195
|
+
}
|
|
196
|
+
flush() {
|
|
197
|
+
if (this.timer) {
|
|
198
|
+
clearTimeout(this.timer);
|
|
199
|
+
this.timer = null;
|
|
200
|
+
}
|
|
201
|
+
this.drain();
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Fire one OTLP/HTTP JSON payload at the collector and forget about it.
|
|
206
|
+
*
|
|
207
|
+
* Deliberately not awaited and deliberately swallowing everything: telemetry is
|
|
208
|
+
* a side channel, and a collector that is down, slow or returning 400 must not
|
|
209
|
+
* become an unhandled rejection in an agent turn. Shared by all three signals
|
|
210
|
+
* so they fail the same silent way.
|
|
211
|
+
*/
|
|
212
|
+
export function postOtlp(url, headers, payload) {
|
|
213
|
+
void Promise.resolve()
|
|
214
|
+
.then(async () => {
|
|
215
|
+
const res = await globalThis.fetch(url, {
|
|
216
|
+
method: "POST",
|
|
217
|
+
headers,
|
|
218
|
+
body: JSON.stringify(payload),
|
|
219
|
+
// a collector that accepts and stalls would hold every payload for minutes
|
|
220
|
+
signal: AbortSignal.timeout(10_000),
|
|
221
|
+
});
|
|
222
|
+
// Drain the response even though we don't care what it says. undici keeps
|
|
223
|
+
// a pooled socket checked out until its body is consumed, so a fire-and-
|
|
224
|
+
// forget POST that never reads the reply holds a connection open until
|
|
225
|
+
// GC. One signal got away with that; three signals on a busy event stream
|
|
226
|
+
// exhaust the pool and start queueing — which shows up as the *agent*
|
|
227
|
+
// getting slower, i.e. exactly the thing telemetry must never do.
|
|
228
|
+
await res.arrayBuffer().catch(() => undefined);
|
|
229
|
+
})
|
|
230
|
+
.catch(() => {
|
|
231
|
+
/* best-effort: an unreachable collector must never surface */
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
export function kv(key, v) {
|
|
235
|
+
if (typeof v === "number") {
|
|
236
|
+
return Number.isInteger(v) ? { key, value: { intValue: String(v) } } : { key, value: { doubleValue: v } };
|
|
237
|
+
}
|
|
238
|
+
if (typeof v === "boolean")
|
|
239
|
+
return { key, value: { boolValue: v } };
|
|
240
|
+
return { key, value: { stringValue: str(v) } };
|
|
241
|
+
}
|
|
242
|
+
//# sourceMappingURL=otlp.js.map
|