@agent-native/core 0.113.0 → 0.114.0
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/agent-native.eject.json +164 -0
- package/corpus/README.md +3 -3
- package/corpus/core/CHANGELOG.md +11 -0
- package/corpus/core/README.md +61 -174
- package/corpus/core/agent-native.eject.json +164 -0
- package/corpus/core/docs/content/agent-native-toolkit.mdx +38 -0
- package/corpus/core/docs/content/package-lifecycle.mdx +54 -4
- package/corpus/core/package.json +15 -2
- package/corpus/core/src/cli/eject.ts +1594 -0
- package/corpus/core/src/cli/index.ts +16 -0
- package/corpus/core/src/client/index.ts +4 -0
- package/corpus/core/src/client/resources/McpConnectionSuggestion.tsx +8 -2
- package/corpus/core/src/client/resources/McpIntegrationDialog.tsx +7 -2
- package/corpus/core/src/client/resources/ResourcesPanel.tsx +17 -3
- package/corpus/core/src/client/resources/index.ts +19 -0
- package/corpus/core/src/client/resources/mcp-integration-catalog.ts +9 -1
- package/corpus/core/src/client/resources/runtime.ts +1 -0
- package/corpus/core/src/client/setup-connections/catalog.ts +6 -0
- package/corpus/core/src/client/setup-connections/index.ts +1 -0
- package/corpus/core/src/client/setup-connections/runtime.ts +1 -0
- package/corpus/core/src/connections/catalog.ts +41 -24
- package/corpus/core/src/connections/index.ts +1 -0
- package/corpus/core/src/connections/runtime.ts +1 -0
- package/corpus/core/src/eject/provider-api-definitions.ts +193 -0
- package/corpus/core/src/eject/remote-mcp-presets.ts +137 -0
- package/corpus/core/src/eject/setup-readiness-ui.tsx +29 -0
- package/corpus/core/src/eject/workspace-connections.ts +92 -0
- package/corpus/core/src/index.ts +1 -0
- package/corpus/core/src/integrations/adapter-overrides.ts +12 -0
- package/corpus/core/src/integrations/eject/messaging-adapters.ts +85 -0
- package/corpus/core/src/integrations/index.ts +5 -1
- package/corpus/core/src/integrations/plugin.ts +34 -12
- package/corpus/core/src/integrations/runtime.ts +1 -0
- package/corpus/core/src/integrations/types.ts +3 -1
- package/corpus/core/src/package-lifecycle/eject-manifest.ts +345 -0
- package/corpus/core/src/package-lifecycle/index.ts +1 -0
- package/corpus/core/src/provider-api/index.ts +46 -12
- package/corpus/core/src/provider-api/runtime.ts +1 -0
- package/corpus/core/src/setup-connections/index.ts +1 -0
- package/corpus/core/src/shared/merge-by-id.ts +14 -0
- package/corpus/core/src/templates/default/.agents/skills/agent-native-docs/SKILL.md +15 -13
- package/corpus/core/src/templates/default/.agents/skills/customizing-agent-native/SKILL.md +63 -20
- package/corpus/core/src/templates/default/.agents/skills/self-modifying-code/SKILL.md +9 -6
- package/corpus/core/src/templates/default/AGENTS.md +6 -5
- package/corpus/core/src/templates/headless/.agents/skills/agent-native-docs/SKILL.md +15 -13
- package/corpus/core/src/templates/headless/.agents/skills/customizing-agent-native/SKILL.md +63 -20
- package/corpus/core/src/templates/headless/AGENTS.md +5 -4
- package/corpus/core/src/templates/workspace-core/.agents/skills/agent-native-docs/SKILL.md +15 -13
- package/corpus/core/src/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +3 -2
- package/corpus/core/src/templates/workspace-core/.agents/skills/customizing-agent-native/SKILL.md +63 -20
- package/corpus/core/src/templates/workspace-core/.agents/skills/self-modifying-code/SKILL.md +9 -6
- package/corpus/core/src/templates/workspace-core/AGENTS.md +4 -4
- package/corpus/core/src/templates/workspace-root/AGENTS.md +4 -4
- package/corpus/core/src/vite/client.ts +25 -2
- package/corpus/templates/analytics/.agents/skills/customizing-agent-native/SKILL.md +63 -20
- package/corpus/templates/analytics/.agents/skills/self-modifying-code/SKILL.md +9 -6
- package/corpus/templates/assets/.agents/skills/customizing-agent-native/SKILL.md +63 -20
- package/corpus/templates/assets/.agents/skills/self-modifying-code/SKILL.md +9 -6
- package/corpus/templates/brain/.agents/skills/customizing-agent-native/SKILL.md +63 -20
- package/corpus/templates/brain/.agents/skills/self-modifying-code/SKILL.md +9 -6
- package/corpus/templates/brain/actions/remediate-legacy-capture-audiences.ts +167 -0
- package/corpus/templates/brain/app/lib/brain-chat-readiness.ts +6 -0
- package/corpus/templates/brain/app/routes/_index.tsx +8 -0
- package/corpus/templates/brain/app/routes.ts +1 -0
- package/corpus/templates/brain/changelog/2026-07-19-ask-starts-immediately-when-builder-s-managed-ai-connection-.md +6 -0
- package/corpus/templates/brain/changelog/2026-07-19-slack-and-granola-backfills-now-process-captures-concurrentl.md +6 -0
- package/corpus/templates/brain/changelog/2026-07-19-source-review-settings-now-control-knowledge-approval.md +6 -0
- package/corpus/templates/brain/changelog/2026-07-19-the-manage-agent-page-now-opens-correctly-from-brain-navigat.md +6 -0
- package/corpus/templates/brain/server/db/schema.ts +3 -0
- package/corpus/templates/brain/server/lib/brain.ts +41 -2
- package/corpus/templates/brain/server/lib/connectors.ts +379 -113
- package/corpus/templates/brain/server/lib/meeting-audience.ts +38 -0
- package/corpus/templates/brain/server/plugins/db.ts +10 -0
- package/corpus/templates/brain/server/routes/api/_agent-native/brain/ingest.post.ts +6 -4
- package/corpus/templates/calendar/.agents/skills/customizing-agent-native/SKILL.md +63 -20
- package/corpus/templates/calendar/.agents/skills/self-modifying-code/SKILL.md +9 -6
- package/corpus/templates/chat/.agents/skills/agent-native-docs/SKILL.md +15 -13
- package/corpus/templates/chat/.agents/skills/customizing-agent-native/SKILL.md +63 -20
- package/corpus/templates/chat/.agents/skills/self-modifying-code/SKILL.md +9 -6
- package/corpus/templates/chat/AGENTS.md +3 -2
- package/corpus/templates/clips/.agents/skills/customizing-agent-native/SKILL.md +63 -20
- package/corpus/templates/clips/.agents/skills/self-modifying-code/SKILL.md +9 -6
- package/corpus/templates/content/.agents/skills/customizing-agent-native/SKILL.md +63 -20
- package/corpus/templates/content/.agents/skills/self-modifying-code/SKILL.md +9 -6
- package/corpus/templates/design/.agents/skills/customizing-agent-native/SKILL.md +63 -20
- package/corpus/templates/design/.agents/skills/self-modifying-code/SKILL.md +9 -6
- package/corpus/templates/dispatch/.agents/skills/customizing-agent-native/SKILL.md +63 -20
- package/corpus/templates/dispatch/.agents/skills/self-modifying-code/SKILL.md +9 -6
- package/corpus/templates/forms/.agents/skills/customizing-agent-native/SKILL.md +63 -20
- package/corpus/templates/forms/.agents/skills/self-modifying-code/SKILL.md +9 -6
- package/corpus/templates/macros/.agents/skills/customizing-agent-native/SKILL.md +63 -20
- package/corpus/templates/mail/.agents/skills/customizing-agent-native/SKILL.md +63 -20
- package/corpus/templates/mail/.agents/skills/self-modifying-code/SKILL.md +9 -6
- package/corpus/templates/plan/.agents/skills/customizing-agent-native/SKILL.md +63 -20
- package/corpus/templates/plan/.agents/skills/self-modifying-code/SKILL.md +9 -6
- package/corpus/templates/slides/.agents/skills/customizing-agent-native/SKILL.md +63 -20
- package/corpus/templates/slides/.agents/skills/self-modifying-code/SKILL.md +9 -6
- package/corpus/templates/tasks/.agents/skills/agent-native-docs/SKILL.md +15 -13
- package/corpus/templates/tasks/.agents/skills/customizing-agent-native/SKILL.md +63 -20
- package/corpus/templates/tasks/.agents/skills/self-modifying-code/SKILL.md +9 -6
- package/corpus/toolkit/CHANGELOG.md +6 -0
- package/corpus/toolkit/agent-native.eject.json +411 -0
- package/corpus/toolkit/package.json +5 -2
- package/dist/cli/eject.d.ts +21 -0
- package/dist/cli/eject.d.ts.map +1 -0
- package/dist/cli/eject.js +1145 -0
- package/dist/cli/eject.js.map +1 -0
- package/dist/cli/index.js +15 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +1 -1
- package/dist/client/index.js.map +1 -1
- package/dist/client/resources/McpConnectionSuggestion.d.ts +4 -3
- package/dist/client/resources/McpConnectionSuggestion.d.ts.map +1 -1
- package/dist/client/resources/McpConnectionSuggestion.js +3 -3
- package/dist/client/resources/McpConnectionSuggestion.js.map +1 -1
- package/dist/client/resources/McpIntegrationDialog.d.ts +4 -3
- package/dist/client/resources/McpIntegrationDialog.d.ts.map +1 -1
- package/dist/client/resources/McpIntegrationDialog.js +2 -2
- package/dist/client/resources/McpIntegrationDialog.js.map +1 -1
- package/dist/client/resources/ResourcesPanel.d.ts +4 -1
- package/dist/client/resources/ResourcesPanel.d.ts.map +1 -1
- package/dist/client/resources/ResourcesPanel.js +9 -7
- package/dist/client/resources/ResourcesPanel.js.map +1 -1
- package/dist/client/resources/index.d.ts +3 -0
- package/dist/client/resources/index.d.ts.map +1 -1
- package/dist/client/resources/index.js +3 -0
- package/dist/client/resources/index.js.map +1 -1
- package/dist/client/resources/mcp-integration-catalog.d.ts +2 -1
- package/dist/client/resources/mcp-integration-catalog.d.ts.map +1 -1
- package/dist/client/resources/mcp-integration-catalog.js +6 -2
- package/dist/client/resources/mcp-integration-catalog.js.map +1 -1
- package/dist/client/resources/runtime.d.ts +2 -0
- package/dist/client/resources/runtime.d.ts.map +1 -0
- package/dist/client/resources/runtime.js +2 -0
- package/dist/client/resources/runtime.js.map +1 -0
- package/dist/client/setup-connections/catalog.d.ts +2 -0
- package/dist/client/setup-connections/catalog.d.ts.map +1 -0
- package/dist/client/setup-connections/catalog.js +7 -0
- package/dist/client/setup-connections/catalog.js.map +1 -0
- package/dist/client/setup-connections/index.d.ts +1 -0
- package/dist/client/setup-connections/index.d.ts.map +1 -1
- package/dist/client/setup-connections/index.js +1 -0
- package/dist/client/setup-connections/index.js.map +1 -1
- package/dist/client/setup-connections/runtime.d.ts +2 -0
- package/dist/client/setup-connections/runtime.d.ts.map +1 -0
- package/dist/client/setup-connections/runtime.js +2 -0
- package/dist/client/setup-connections/runtime.js.map +1 -0
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/connections/catalog.d.ts +5 -3
- package/dist/connections/catalog.d.ts.map +1 -1
- package/dist/connections/catalog.js +16 -8
- package/dist/connections/catalog.js.map +1 -1
- package/dist/connections/index.d.ts +1 -1
- package/dist/connections/index.d.ts.map +1 -1
- package/dist/connections/index.js +1 -1
- package/dist/connections/index.js.map +1 -1
- package/dist/connections/runtime.d.ts +2 -0
- package/dist/connections/runtime.d.ts.map +1 -0
- package/dist/connections/runtime.js +2 -0
- package/dist/connections/runtime.js.map +1 -0
- package/dist/eject/provider-api-definitions.d.ts +34 -0
- package/dist/eject/provider-api-definitions.d.ts.map +1 -0
- package/dist/eject/provider-api-definitions.js +88 -0
- package/dist/eject/provider-api-definitions.js.map +1 -0
- package/dist/eject/remote-mcp-presets.d.ts +17 -0
- package/dist/eject/remote-mcp-presets.d.ts.map +1 -0
- package/dist/eject/remote-mcp-presets.js +81 -0
- package/dist/eject/remote-mcp-presets.js.map +1 -0
- package/dist/eject/setup-readiness-ui.d.ts +7 -0
- package/dist/eject/setup-readiness-ui.d.ts.map +1 -0
- package/dist/eject/setup-readiness-ui.js +13 -0
- package/dist/eject/setup-readiness-ui.js.map +1 -0
- package/dist/eject/workspace-connections.d.ts +11 -0
- package/dist/eject/workspace-connections.d.ts.map +1 -0
- package/dist/eject/workspace-connections.js +47 -0
- package/dist/eject/workspace-connections.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/integrations/adapter-overrides.d.ts +3 -0
- package/dist/integrations/adapter-overrides.d.ts.map +1 -0
- package/dist/integrations/adapter-overrides.js +7 -0
- package/dist/integrations/adapter-overrides.js.map +1 -0
- package/dist/integrations/eject/messaging-adapters.d.ts +12 -0
- package/dist/integrations/eject/messaging-adapters.d.ts.map +1 -0
- package/dist/integrations/eject/messaging-adapters.js +49 -0
- package/dist/integrations/eject/messaging-adapters.js.map +1 -0
- package/dist/integrations/index.d.ts +3 -2
- package/dist/integrations/index.d.ts.map +1 -1
- package/dist/integrations/index.js +2 -1
- package/dist/integrations/index.js.map +1 -1
- package/dist/integrations/plugin.d.ts +32 -1
- package/dist/integrations/plugin.d.ts.map +1 -1
- package/dist/integrations/plugin.js +19 -12
- package/dist/integrations/plugin.js.map +1 -1
- package/dist/integrations/runtime.d.ts +2 -0
- package/dist/integrations/runtime.d.ts.map +1 -0
- package/dist/integrations/runtime.js +2 -0
- package/dist/integrations/runtime.js.map +1 -0
- package/dist/integrations/types.d.ts +3 -1
- package/dist/integrations/types.d.ts.map +1 -1
- package/dist/integrations/types.js.map +1 -1
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/package-lifecycle/eject-manifest.d.ts +43 -0
- package/dist/package-lifecycle/eject-manifest.d.ts.map +1 -0
- package/dist/package-lifecycle/eject-manifest.js +219 -0
- package/dist/package-lifecycle/eject-manifest.js.map +1 -0
- package/dist/package-lifecycle/index.d.ts +1 -0
- package/dist/package-lifecycle/index.d.ts.map +1 -1
- package/dist/package-lifecycle/index.js +1 -0
- package/dist/package-lifecycle/index.js.map +1 -1
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +4 -4
- package/dist/provider-api/actions/provider-api.d.ts +8 -8
- package/dist/provider-api/corpus-jobs.d.ts +2 -2
- package/dist/provider-api/index.d.ts +6 -2
- package/dist/provider-api/index.d.ts.map +1 -1
- package/dist/provider-api/index.js +25 -12
- package/dist/provider-api/index.js.map +1 -1
- package/dist/provider-api/runtime.d.ts +2 -0
- package/dist/provider-api/runtime.d.ts.map +1 -0
- package/dist/provider-api/runtime.js +2 -0
- package/dist/provider-api/runtime.js.map +1 -0
- package/dist/secrets/routes.d.ts +9 -9
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/setup-connections/index.d.ts +1 -1
- package/dist/setup-connections/index.d.ts.map +1 -1
- package/dist/setup-connections/index.js +1 -1
- package/dist/setup-connections/index.js.map +1 -1
- package/dist/shared/merge-by-id.d.ts +5 -0
- package/dist/shared/merge-by-id.d.ts.map +1 -0
- package/dist/shared/merge-by-id.js +7 -0
- package/dist/shared/merge-by-id.js.map +1 -0
- package/dist/templates/chat/.agents/skills/agent-native-docs/SKILL.md +15 -13
- package/dist/templates/chat/.agents/skills/customizing-agent-native/SKILL.md +63 -20
- package/dist/templates/chat/.agents/skills/self-modifying-code/SKILL.md +9 -6
- package/dist/templates/chat/AGENTS.md +3 -2
- package/dist/templates/default/.agents/skills/agent-native-docs/SKILL.md +15 -13
- package/dist/templates/default/.agents/skills/customizing-agent-native/SKILL.md +63 -20
- package/dist/templates/default/.agents/skills/self-modifying-code/SKILL.md +9 -6
- package/dist/templates/default/AGENTS.md +6 -5
- package/dist/templates/headless/.agents/skills/agent-native-docs/SKILL.md +15 -13
- package/dist/templates/headless/.agents/skills/customizing-agent-native/SKILL.md +63 -20
- package/dist/templates/headless/AGENTS.md +5 -4
- package/dist/templates/workspace-core/.agents/skills/agent-native-docs/SKILL.md +15 -13
- package/dist/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +3 -2
- package/dist/templates/workspace-core/.agents/skills/customizing-agent-native/SKILL.md +63 -20
- package/dist/templates/workspace-core/.agents/skills/self-modifying-code/SKILL.md +9 -6
- package/dist/templates/workspace-core/AGENTS.md +4 -4
- package/dist/templates/workspace-root/AGENTS.md +4 -4
- package/dist/vite/client.d.ts.map +1 -1
- package/dist/vite/client.js +22 -2
- package/dist/vite/client.js.map +1 -1
- package/docs/content/agent-native-toolkit.mdx +38 -0
- package/docs/content/package-lifecycle.mdx +54 -4
- package/package.json +15 -2
- package/src/a2a/agent-card.ts +83 -0
- package/src/a2a/artifact-response.ts +1648 -0
- package/src/a2a/auth-policy.ts +66 -0
- package/src/a2a/caller-auth.ts +102 -0
- package/src/a2a/client.ts +876 -0
- package/src/a2a/correlation.ts +50 -0
- package/src/a2a/handlers.ts +1285 -0
- package/src/a2a/index.ts +50 -0
- package/src/a2a/invoke.ts +348 -0
- package/src/a2a/response-text.ts +53 -0
- package/src/a2a/server.ts +586 -0
- package/src/a2a/task-store.ts +917 -0
- package/src/a2a/types.ts +221 -0
- package/src/a2a-claims.ts +52 -0
- package/src/action-change-marker.ts +74 -0
- package/src/action-ui.ts +38 -0
- package/src/action.ts +1406 -0
- package/src/adapters/cli/index.ts +6 -0
- package/src/adapters/cli/registry.ts +73 -0
- package/src/adapters/cli/shell-adapter.ts +92 -0
- package/src/adapters/cli/types.ts +27 -0
- package/src/agent/action-continuation-guidance.ts +38 -0
- package/src/agent/app-model-defaults.ts +201 -0
- package/src/agent/context-xray/actions/context-evict.ts +56 -0
- package/src/agent/context-xray/actions/context-manifest-get.ts +75 -0
- package/src/agent/context-xray/actions/context-pin.ts +56 -0
- package/src/agent/context-xray/actions/context-preview-get.ts +134 -0
- package/src/agent/context-xray/actions/context-report.ts +75 -0
- package/src/agent/context-xray/actions/context-restore.ts +51 -0
- package/src/agent/context-xray/actions/errors.ts +40 -0
- package/src/agent/context-xray/apply-directives.ts +137 -0
- package/src/agent/context-xray/directives-store.ts +178 -0
- package/src/agent/context-xray/identity.ts +37 -0
- package/src/agent/context-xray/manifest.ts +284 -0
- package/src/agent/context-xray/migrations.ts +32 -0
- package/src/agent/context-xray/plugin.ts +23 -0
- package/src/agent/context-xray/schema.ts +17 -0
- package/src/agent/context-xray/segments.ts +194 -0
- package/src/agent/context-xray/tokenize.ts +93 -0
- package/src/agent/default-model.ts +5 -0
- package/src/agent/durable-background.ts +547 -0
- package/src/agent/engine/ai-sdk-engine.ts +565 -0
- package/src/agent/engine/anthropic-engine.ts +320 -0
- package/src/agent/engine/builder-engine.ts +1110 -0
- package/src/agent/engine/builder-gateway-headers.ts +44 -0
- package/src/agent/engine/builtin.ts +122 -0
- package/src/agent/engine/context-directives-transform.ts +73 -0
- package/src/agent/engine/continuation-dispatch-retry.ts +168 -0
- package/src/agent/engine/credential-errors.ts +69 -0
- package/src/agent/engine/index.ts +54 -0
- package/src/agent/engine/openai-compatible-endpoint.ts +25 -0
- package/src/agent/engine/output-tokens.ts +181 -0
- package/src/agent/engine/provider-env-vars.ts +69 -0
- package/src/agent/engine/registry.ts +853 -0
- package/src/agent/engine/tool-call-journal-seed.ts +85 -0
- package/src/agent/engine/translate-ai-sdk.ts +407 -0
- package/src/agent/engine/translate-anthropic.ts +611 -0
- package/src/agent/engine/types.ts +310 -0
- package/src/agent/harness/acp-adapter.ts +893 -0
- package/src/agent/harness/acp-builtin.ts +70 -0
- package/src/agent/harness/ai-sdk-adapter.ts +509 -0
- package/src/agent/harness/background.ts +365 -0
- package/src/agent/harness/builtin.ts +50 -0
- package/src/agent/harness/index.ts +86 -0
- package/src/agent/harness/lifecycle.ts +498 -0
- package/src/agent/harness/registry.ts +97 -0
- package/src/agent/harness/runner.ts +182 -0
- package/src/agent/harness/store.ts +466 -0
- package/src/agent/harness/translate.ts +130 -0
- package/src/agent/harness/types.ts +129 -0
- package/src/agent/index.ts +32 -0
- package/src/agent/loop-settings.ts +186 -0
- package/src/agent/model-config.ts +363 -0
- package/src/agent/observational-memory/compactor.ts +78 -0
- package/src/agent/observational-memory/config.ts +78 -0
- package/src/agent/observational-memory/index.ts +79 -0
- package/src/agent/observational-memory/internal-run.ts +89 -0
- package/src/agent/observational-memory/message-text.ts +49 -0
- package/src/agent/observational-memory/migrations.ts +44 -0
- package/src/agent/observational-memory/observer.ts +128 -0
- package/src/agent/observational-memory/plugin.ts +35 -0
- package/src/agent/observational-memory/prompts.ts +53 -0
- package/src/agent/observational-memory/read.ts +131 -0
- package/src/agent/observational-memory/reflector.ts +119 -0
- package/src/agent/observational-memory/schema.ts +48 -0
- package/src/agent/observational-memory/store.ts +275 -0
- package/src/agent/observational-memory/types.ts +65 -0
- package/src/agent/processors.ts +209 -0
- package/src/agent/production-agent.ts +8254 -0
- package/src/agent/run-loop-with-resume.ts +394 -0
- package/src/agent/run-manager.ts +1825 -0
- package/src/agent/run-ownership.ts +75 -0
- package/src/agent/run-store.ts +2601 -0
- package/src/agent/runtime-context.ts +169 -0
- package/src/agent/thread-data-builder.ts +1584 -0
- package/src/agent/tool-call-journal.ts +368 -0
- package/src/agent/tool-error-redaction.ts +52 -0
- package/src/agent/tool-result-images.ts +178 -0
- package/src/agent/tool-search.ts +376 -0
- package/src/agent/types.ts +372 -0
- package/src/agent-native/index.ts +198 -0
- package/src/agent-web/config.ts +253 -0
- package/src/agent-web/generator.ts +382 -0
- package/src/agent-web/index.ts +36 -0
- package/src/appearance/actions/change-appearance.ts +35 -0
- package/src/application-state/emitter.ts +81 -0
- package/src/application-state/handlers.ts +121 -0
- package/src/application-state/index.ts +44 -0
- package/src/application-state/script-helpers.ts +155 -0
- package/src/application-state/store.ts +274 -0
- package/src/assets/branding/agent-native.icon/Assets/background.svg +3 -0
- package/src/assets/branding/agent-native.icon/Assets/foreground.svg +12 -0
- package/src/assets/branding/agent-native.icon/icon.json +49 -0
- package/src/assets/branding/favicon.png +0 -0
- package/src/assets/branding/favicon.svg +1 -0
- package/src/assets/branding/icon-on-dark.svg +10 -0
- package/src/assets/branding/icon-on-light.svg +10 -0
- package/src/assets/branding/mac-app-icon.svg +1 -0
- package/src/assets/branding/slack-bot/agent-native-1024.png +0 -0
- package/src/assets/branding/slack-bot/agent-native-512.png +0 -0
- package/src/assets/branding/tray-icon.svg +6 -0
- package/src/assets/branding/wordmark-on-dark.svg +21 -0
- package/src/assets/branding/wordmark-on-light.svg +21 -0
- package/src/assets/fonts/LICENSE_LIBERATION +96 -0
- package/src/assets/fonts/LICENSE_NOTO_NASKH_ARABIC +93 -0
- package/src/assets/fonts/LiberationSans-Bold.ttf +0 -0
- package/src/assets/fonts/LiberationSans-Regular.ttf +0 -0
- package/src/assets/fonts/NotoNaskhArabic-Variable.ttf +0 -0
- package/src/audit/actions/export-audit-events.ts +162 -0
- package/src/audit/actions/get-audit-event.ts +24 -0
- package/src/audit/actions/list-audit-events.ts +66 -0
- package/src/audit/cleanup-job.ts +100 -0
- package/src/audit/config.ts +90 -0
- package/src/audit/index.ts +43 -0
- package/src/audit/record.ts +180 -0
- package/src/audit/redact.ts +109 -0
- package/src/audit/store.ts +346 -0
- package/src/audit/types.ts +141 -0
- package/src/automation/index.ts +802 -0
- package/src/brand-kit/brand-signals.ts +124 -0
- package/src/brand-kit/fig/index.ts +64 -0
- package/src/brand-kit/index.ts +72 -0
- package/src/brand-kit/types.ts +113 -0
- package/src/browser-sessions/actions.ts +302 -0
- package/src/browser-sessions/routes.ts +234 -0
- package/src/browser-sessions/store.ts +679 -0
- package/src/browser-sessions/types.ts +84 -0
- package/src/changelog/parse.ts +328 -0
- package/src/chat-threads/emitter.ts +22 -0
- package/src/chat-threads/schema.ts +43 -0
- package/src/chat-threads/store.ts +1311 -0
- package/src/checkpoints/index.ts +15 -0
- package/src/checkpoints/service.ts +142 -0
- package/src/checkpoints/store.ts +153 -0
- package/src/cli/add.ts +425 -0
- package/src/cli/agent.ts +424 -0
- package/src/cli/agents.ts +122 -0
- package/src/cli/app-skill.ts +1455 -0
- package/src/cli/atomic-json-file.ts +324 -0
- package/src/cli/audit-agent-web.ts +324 -0
- package/src/cli/changelog.ts +199 -0
- package/src/cli/claude-code-participant.ts +515 -0
- package/src/cli/code-agent-commands.ts +270 -0
- package/src/cli/code-agent-connector.ts +1109 -0
- package/src/cli/code-agent-executor.ts +1970 -0
- package/src/cli/code-agent-output-smoother.ts +140 -0
- package/src/cli/code-agent-runs.concurrent-worker.ts +33 -0
- package/src/cli/code-agent-runs.ts +548 -0
- package/src/cli/code.ts +1805 -0
- package/src/cli/codex-cli-participant.ts +496 -0
- package/src/cli/connect.ts +2714 -0
- package/src/cli/content-local.ts +442 -0
- package/src/cli/context-xray-local.ts +2193 -0
- package/src/cli/create-workspace.ts +30 -0
- package/src/cli/create.ts +2247 -0
- package/src/cli/deploy-build.ts +50 -0
- package/src/cli/design-connect.ts +3187 -0
- package/src/cli/doctor.ts +472 -0
- package/src/cli/eject.ts +1594 -0
- package/src/cli/eval.ts +134 -0
- package/src/cli/gateway-helpers.ts +70 -0
- package/src/cli/index.ts +1217 -0
- package/src/cli/info.ts +105 -0
- package/src/cli/invoke.ts +288 -0
- package/src/cli/mcp-config-writers.ts +857 -0
- package/src/cli/mcp.ts +594 -0
- package/src/cli/migrate.ts +1813 -0
- package/src/cli/migration-codemod.ts +646 -0
- package/src/cli/multi-frontier-runs.ts +1952 -0
- package/src/cli/package-lifecycle.ts +823 -0
- package/src/cli/plan-blocks.ts +143 -0
- package/src/cli/plan-local.ts +3189 -0
- package/src/cli/plan-publish-store.ts +138 -0
- package/src/cli/pr-visual-recap-workflow.ts +2 -0
- package/src/cli/recap.ts +5 -0
- package/src/cli/setup-agents.ts +120 -0
- package/src/cli/skills-content/assets-skill.ts +83 -0
- package/src/cli/skills-content/canvas.ts +153 -0
- package/src/cli/skills-content/connection.ts +52 -0
- package/src/cli/skills-content/content-skill.ts +120 -0
- package/src/cli/skills-content/design-exploration-skill.ts +118 -0
- package/src/cli/skills-content/design-visual-edit-skill.ts +228 -0
- package/src/cli/skills-content/document-quality.ts +196 -0
- package/src/cli/skills-content/exemplar.ts +69 -0
- package/src/cli/skills-content/help.ts +77 -0
- package/src/cli/skills-content/index.ts +22 -0
- package/src/cli/skills-content/local-files.ts +100 -0
- package/src/cli/skills-content/plan-setup-auth.ts +80 -0
- package/src/cli/skills-content/visual-plan-skill.ts +493 -0
- package/src/cli/skills-content/visual-recap-skill.ts +553 -0
- package/src/cli/skills-content/visualize-repo-skill.ts +93 -0
- package/src/cli/skills-content/wireframe.ts +349 -0
- package/src/cli/skills.ts +4340 -0
- package/src/cli/sync-builder-starter-manifest.ts +631 -0
- package/src/cli/telemetry.ts +136 -0
- package/src/cli/templates-meta.ts +269 -0
- package/src/cli/upgrade.ts +956 -0
- package/src/cli/visualize-repo.ts +715 -0
- package/src/cli/workspace-dev.ts +1486 -0
- package/src/cli/workspacify.ts +258 -0
- package/src/client/AgentAskPopover.tsx +89 -0
- package/src/client/AgentChatHome.tsx +66 -0
- package/src/client/AgentNative.tsx +161 -0
- package/src/client/AgentNativeEmbedded.tsx +288 -0
- package/src/client/AgentNativeFrame.tsx +151 -0
- package/src/client/AgentPanel.tsx +3437 -0
- package/src/client/AgentTaskCard.tsx +273 -0
- package/src/client/AppearancePicker.tsx +79 -0
- package/src/client/AssistantChat.tsx +5793 -0
- package/src/client/ClientOnly.tsx +22 -0
- package/src/client/CommandMenu.tsx +726 -0
- package/src/client/ConnectBuilderCard.tsx +390 -0
- package/src/client/DefaultSpinner.tsx +47 -0
- package/src/client/ErrorBoundary.tsx +359 -0
- package/src/client/ErrorReportActions.tsx +69 -0
- package/src/client/FeedbackButton.tsx +553 -0
- package/src/client/HighlightedCodeBlock.tsx +173 -0
- package/src/client/IframeEmbed.tsx +151 -0
- package/src/client/KeepTabOpenNotice.tsx +181 -0
- package/src/client/MultiTabAssistantChat.tsx +2576 -0
- package/src/client/NewWorkspaceAppFlow.tsx +525 -0
- package/src/client/PoweredByBadge.tsx +284 -0
- package/src/client/RunStuckBanner.tsx +431 -0
- package/src/client/StarfieldBackground.tsx +466 -0
- package/src/client/Turnstile.tsx +125 -0
- package/src/client/active-run-state.ts +139 -0
- package/src/client/agent-chat/index.ts +279 -0
- package/src/client/agent-chat-adapter.ts +3692 -0
- package/src/client/agent-chat.ts +1233 -0
- package/src/client/agent-engine-key.ts +122 -0
- package/src/client/agent-page/AgentContextTab.tsx +487 -0
- package/src/client/agent-page/AgentEmptyState.tsx +44 -0
- package/src/client/agent-page/AgentJobsTab.tsx +441 -0
- package/src/client/agent-page/AgentTabFrame.tsx +39 -0
- package/src/client/agent-page/AgentTabsPage.tsx +1096 -0
- package/src/client/agent-page/types.ts +18 -0
- package/src/client/agent-page/use-jobs.ts +150 -0
- package/src/client/agent-sidebar-state.ts +166 -0
- package/src/client/analytics/index.ts +40 -0
- package/src/client/analytics-session.ts +70 -0
- package/src/client/analytics.ts +1757 -0
- package/src/client/api-path.ts +124 -0
- package/src/client/app-providers.tsx +230 -0
- package/src/client/appearance.ts +130 -0
- package/src/client/application-state.ts +154 -0
- package/src/client/assistant-ui-recovery.tsx +205 -0
- package/src/client/automation/index.ts +4 -0
- package/src/client/automation.ts +22 -0
- package/src/client/blocks/AiEditableField.tsx +30 -0
- package/src/client/blocks/BlockView.tsx +145 -0
- package/src/client/blocks/RegistryBlockDataProvider.tsx +121 -0
- package/src/client/blocks/SchemaBlockEditor.tsx +457 -0
- package/src/client/blocks/agent.ts +95 -0
- package/src/client/blocks/code-block-direction.ts +21 -0
- package/src/client/blocks/index.ts +308 -0
- package/src/client/blocks/library/AnnotatedCodeBlock.tsx +710 -0
- package/src/client/blocks/library/ApiEndpointBlock.tsx +1120 -0
- package/src/client/blocks/library/DataModelBlock.tsx +775 -0
- package/src/client/blocks/library/DiffBlock.tsx +1854 -0
- package/src/client/blocks/library/FileTreeBlock.tsx +868 -0
- package/src/client/blocks/library/HighlightedCode.tsx +260 -0
- package/src/client/blocks/library/JsonExplorerBlock.tsx +608 -0
- package/src/client/blocks/library/MermaidBlock.tsx +362 -0
- package/src/client/blocks/library/OpenApiSpecBlock.tsx +998 -0
- package/src/client/blocks/library/annotated-code.config.ts +101 -0
- package/src/client/blocks/library/annotation-rail.tsx +1020 -0
- package/src/client/blocks/library/api-endpoint.config.ts +207 -0
- package/src/client/blocks/library/block-copy.ts +399 -0
- package/src/client/blocks/library/callout.config.ts +57 -0
- package/src/client/blocks/library/callout.tsx +149 -0
- package/src/client/blocks/library/checklist.config.ts +56 -0
- package/src/client/blocks/library/checklist.tsx +208 -0
- package/src/client/blocks/library/code-filename-label.tsx +59 -0
- package/src/client/blocks/library/code-highlight.tsx +178 -0
- package/src/client/blocks/library/code-tabs.config.ts +70 -0
- package/src/client/blocks/library/code-tabs.tsx +606 -0
- package/src/client/blocks/library/code.config.ts +69 -0
- package/src/client/blocks/library/code.tsx +440 -0
- package/src/client/blocks/library/columns.config.ts +85 -0
- package/src/client/blocks/library/columns.tsx +334 -0
- package/src/client/blocks/library/data-model.config.ts +152 -0
- package/src/client/blocks/library/dev-doc-ui.tsx +191 -0
- package/src/client/blocks/library/diagram.config.ts +222 -0
- package/src/client/blocks/library/diagram.tsx +958 -0
- package/src/client/blocks/library/diff.config.ts +115 -0
- package/src/client/blocks/library/file-tree.config.ts +92 -0
- package/src/client/blocks/library/html.config.ts +79 -0
- package/src/client/blocks/library/html.tsx +258 -0
- package/src/client/blocks/library/json-explorer.config.ts +74 -0
- package/src/client/blocks/library/mermaid.config.ts +49 -0
- package/src/client/blocks/library/narrow-container.tsx +37 -0
- package/src/client/blocks/library/openapi-spec.config.ts +66 -0
- package/src/client/blocks/library/question-form.config.ts +127 -0
- package/src/client/blocks/library/question-form.tsx +944 -0
- package/src/client/blocks/library/sanitize-html.ts +343 -0
- package/src/client/blocks/library/server-specs.ts +338 -0
- package/src/client/blocks/library/specs.tsx +308 -0
- package/src/client/blocks/library/table.config.ts +61 -0
- package/src/client/blocks/library/table.tsx +449 -0
- package/src/client/blocks/library/tabs.config.ts +96 -0
- package/src/client/blocks/library/tabs.tsx +665 -0
- package/src/client/blocks/library/wireframe-icons.ts +210 -0
- package/src/client/blocks/library/wireframe-kit.tsx +1850 -0
- package/src/client/blocks/library/wireframe.config.ts +539 -0
- package/src/client/blocks/library/wireframe.tsx +535 -0
- package/src/client/blocks/mdx.ts +364 -0
- package/src/client/blocks/provider.tsx +51 -0
- package/src/client/blocks/registry.ts +83 -0
- package/src/client/blocks/schema-form/introspect.ts +201 -0
- package/src/client/blocks/server.ts +170 -0
- package/src/client/blocks/types.ts +415 -0
- package/src/client/browser-session-bridge.ts +548 -0
- package/src/client/browser-tab-id.ts +36 -0
- package/src/client/builder-frame.ts +194 -0
- package/src/client/builder-mark.tsx +21 -0
- package/src/client/changelog/Changelog.tsx +333 -0
- package/src/client/changelog/index.ts +1 -0
- package/src/client/chat/ChatHistoryList.tsx +7 -0
- package/src/client/chat/attachment-adapters.ts +426 -0
- package/src/client/chat/connectors.ts +1579 -0
- package/src/client/chat/index.ts +160 -0
- package/src/client/chat/markdown-renderer.tsx +693 -0
- package/src/client/chat/message-components.tsx +1326 -0
- package/src/client/chat/repo-helpers.ts +281 -0
- package/src/client/chat/run-recovery.tsx +994 -0
- package/src/client/chat/runtime.ts +1810 -0
- package/src/client/chat/tool-call-display.tsx +1270 -0
- package/src/client/chat/tool-render-registry.tsx +144 -0
- package/src/client/chat/use-agent-chat-lifecycle-tracking.ts +74 -0
- package/src/client/chat/use-reconnect-reader-owner.ts +28 -0
- package/src/client/chat/widgets/DataChartRenderer.tsx +242 -0
- package/src/client/chat/widgets/DataChartWidget.tsx +51 -0
- package/src/client/chat/widgets/DataInsightsWidget.tsx +59 -0
- package/src/client/chat/widgets/DataTableWidget.tsx +240 -0
- package/src/client/chat/widgets/InlineExtensionWidget.tsx +60 -0
- package/src/client/chat/widgets/builtin-tool-renderers.tsx +213 -0
- package/src/client/chat/widgets/data-widget-types.ts +1 -0
- package/src/client/chat/widgets/inline-extension-result.ts +86 -0
- package/src/client/chat-model-groups.ts +171 -0
- package/src/client/chat-thread-url.ts +11 -0
- package/src/client/chat-view-transition.ts +213 -0
- package/src/client/client-action.ts +21 -0
- package/src/client/client-bootstrap.ts +7 -0
- package/src/client/client-surface.ts +41 -0
- package/src/client/clipboard.ts +89 -0
- package/src/client/code-agent-chat-adapter.ts +537 -0
- package/src/client/collab/index.ts +60 -0
- package/src/client/command-navigation/index.ts +15 -0
- package/src/client/comments-review/index.ts +1 -0
- package/src/client/components/AgentPresenceChip.tsx +4 -0
- package/src/client/components/ApiKeySettings.tsx +309 -0
- package/src/client/components/CodeAgentIndicator.tsx +59 -0
- package/src/client/components/CodeRequiredDialog.tsx +470 -0
- package/src/client/components/LiveCursorOverlay.tsx +5 -0
- package/src/client/components/MissingKeyCard.tsx +61 -0
- package/src/client/components/PresenceBar.tsx +4 -0
- package/src/client/components/RecentEditHighlights.tsx +4 -0
- package/src/client/components/RemoteSelectionRings.tsx +5 -0
- package/src/client/components/icons/AgentNativeIcon.tsx +46 -0
- package/src/client/components/ui/dialog.tsx +1 -0
- package/src/client/components/ui/dropdown-menu.tsx +1 -0
- package/src/client/components/ui/hover-card.tsx +1 -0
- package/src/client/components/ui/message-scroller.tsx +131 -0
- package/src/client/components/ui/popover.tsx +1 -0
- package/src/client/components/ui/sheet.tsx +1 -0
- package/src/client/components/ui/tooltip.tsx +1 -0
- package/src/client/composer/index.ts +11 -0
- package/src/client/composer/runtime-adapters.tsx +123 -0
- package/src/client/composer/use-mention-search.ts +7 -0
- package/src/client/composer/wired-components.tsx +53 -0
- package/src/client/context-xray/ContextMeter.tsx +124 -0
- package/src/client/context-xray/ContextSegmentRow.tsx +27 -0
- package/src/client/context-xray/ContextTreemap.tsx +24 -0
- package/src/client/context-xray/ContextXRayPanel.tsx +59 -0
- package/src/client/context-xray/SegmentProvenancePopover.tsx +18 -0
- package/src/client/context-xray/format.ts +27 -0
- package/src/client/conversation/AgentConversation.tsx +555 -0
- package/src/client/conversation/code-agent-transcript.ts +283 -0
- package/src/client/conversation/index.ts +22 -0
- package/src/client/conversation/types.ts +85 -0
- package/src/client/conversation/use-near-bottom-autoscroll.ts +278 -0
- package/src/client/create-query-client.ts +86 -0
- package/src/client/db-admin/DataGrid.tsx +551 -0
- package/src/client/db-admin/DbAdminPage.tsx +218 -0
- package/src/client/db-admin/DevDatabaseLink.tsx +16 -0
- package/src/client/db-admin/EditableCell.tsx +451 -0
- package/src/client/db-admin/FilterBar.tsx +203 -0
- package/src/client/db-admin/ResultsGrid.tsx +114 -0
- package/src/client/db-admin/RowSidePanel.tsx +337 -0
- package/src/client/db-admin/SqlEditor.tsx +840 -0
- package/src/client/db-admin/TableBrowser.tsx +212 -0
- package/src/client/db-admin/TableEditor.tsx +760 -0
- package/src/client/db-admin/cell-format.ts +246 -0
- package/src/client/db-admin/changeset.ts +296 -0
- package/src/client/db-admin/export-utils.ts +78 -0
- package/src/client/db-admin/index.ts +33 -0
- package/src/client/db-admin/sql-storage.ts +134 -0
- package/src/client/db-admin/storage.ts +68 -0
- package/src/client/db-admin/useAgentSync.ts +226 -0
- package/src/client/db-admin/useDbAdmin.ts +294 -0
- package/src/client/dev-overlay/DevOverlay.tsx +576 -0
- package/src/client/dev-overlay/builtins.ts +40 -0
- package/src/client/dev-overlay/index.ts +23 -0
- package/src/client/dev-overlay/registry.ts +67 -0
- package/src/client/dev-overlay/types.ts +64 -0
- package/src/client/dev-overlay/use-dev-option.ts +78 -0
- package/src/client/dev-overlay/use-dev-overlay-shortcut.ts +31 -0
- package/src/client/dynamic-suggestions.ts +455 -0
- package/src/client/embed-auth.ts +544 -0
- package/src/client/embed.ts +54 -0
- package/src/client/error-capture.ts +690 -0
- package/src/client/error-format.ts +211 -0
- package/src/client/error-reporting.ts +100 -0
- package/src/client/extensions/AgentNativeExtensionFrame.e2e-host.tsx +136 -0
- package/src/client/extensions/AgentNativeExtensionFrame.tsx +708 -0
- package/src/client/extensions/EmbeddedExtension.tsx +604 -0
- package/src/client/extensions/ExtensionEditor.tsx +450 -0
- package/src/client/extensions/ExtensionQueryErrorState.tsx +47 -0
- package/src/client/extensions/ExtensionSlot.tsx +345 -0
- package/src/client/extensions/ExtensionViewer.tsx +1502 -0
- package/src/client/extensions/ExtensionViewerPage.tsx +32 -0
- package/src/client/extensions/ExtensionsListPage.tsx +432 -0
- package/src/client/extensions/ExtensionsSidebarSection.tsx +1259 -0
- package/src/client/extensions/InlineExtensionFrame.tsx +660 -0
- package/src/client/extensions/agent-native-extension-runtime.ts +495 -0
- package/src/client/extensions/delete-extension.ts +76 -0
- package/src/client/extensions/extension-load-error.ts +26 -0
- package/src/client/extensions/extension-order.ts +49 -0
- package/src/client/extensions/extension-popularity.ts +61 -0
- package/src/client/extensions/iframe-bridge.ts +377 -0
- package/src/client/extensions/index.ts +84 -0
- package/src/client/extensions/portable-extension.ts +712 -0
- package/src/client/feature-flags/FeatureFlagsPanel.tsx +519 -0
- package/src/client/feature-flags/helpers.ts +77 -0
- package/src/client/feature-flags/index.ts +13 -0
- package/src/client/feature-flags/types.ts +29 -0
- package/src/client/feature-flags/use-feature-flag.ts +26 -0
- package/src/client/feedback-context.ts +90 -0
- package/src/client/frame-protocol.ts +168 -0
- package/src/client/frame.ts +329 -0
- package/src/client/guided-questions.tsx +1152 -0
- package/src/client/history/VersionHistoryPanel.tsx +175 -0
- package/src/client/history/index.ts +21 -0
- package/src/client/history/use-history.ts +114 -0
- package/src/client/hooks/index.ts +46 -0
- package/src/client/host/index.ts +170 -0
- package/src/client/host-bridge.ts +1272 -0
- package/src/client/host-tools.ts +190 -0
- package/src/client/i18n.tsx +798 -0
- package/src/client/index.ts +370 -0
- package/src/client/integrations/IntegrationCard.tsx +186 -0
- package/src/client/integrations/IntegrationsPanel.tsx +633 -0
- package/src/client/integrations/api.ts +295 -0
- package/src/client/integrations/index.ts +29 -0
- package/src/client/integrations/useIntegrationStatus.ts +52 -0
- package/src/client/mcp-app-host.ts +720 -0
- package/src/client/mcp-apps/McpAppRenderer.tsx +760 -0
- package/src/client/navigation/index.ts +54 -0
- package/src/client/notifications/NotificationsBell.tsx +383 -0
- package/src/client/notifications/index.ts +1 -0
- package/src/client/observability/ObservabilityDashboard.tsx +890 -0
- package/src/client/observability/ThumbsFeedback.tsx +160 -0
- package/src/client/observability/index.ts +20 -0
- package/src/client/observability/useObservability.ts +266 -0
- package/src/client/onboarding/OnboardingBanner.tsx +73 -0
- package/src/client/onboarding/OnboardingPanel.tsx +1036 -0
- package/src/client/onboarding/SetupButton.tsx +64 -0
- package/src/client/onboarding/index.ts +25 -0
- package/src/client/onboarding/use-onboarding.ts +164 -0
- package/src/client/onboarding/use-preview-mode.ts +40 -0
- package/src/client/org/InvitationBanner.tsx +148 -0
- package/src/client/org/OrgSwitcher.tsx +754 -0
- package/src/client/org/RequireActiveOrg.tsx +337 -0
- package/src/client/org/TeamPage.tsx +1432 -0
- package/src/client/org/hooks.ts +359 -0
- package/src/client/org/index.ts +70 -0
- package/src/client/org/workspace-app-links.ts +367 -0
- package/src/client/org-team/index.ts +1 -0
- package/src/client/progress/RunsTray.tsx +680 -0
- package/src/client/progress/index.ts +1 -0
- package/src/client/require-session.tsx +142 -0
- package/src/client/resources/BuiltinCapabilityDetail.tsx +258 -0
- package/src/client/resources/McpConnectionSuggestion.tsx +276 -0
- package/src/client/resources/McpIntegrationDialog.tsx +808 -0
- package/src/client/resources/McpServerDetail.tsx +230 -0
- package/src/client/resources/ResourceEditor.tsx +872 -0
- package/src/client/resources/ResourceTree.tsx +860 -0
- package/src/client/resources/ResourcesPanel.tsx +1960 -0
- package/src/client/resources/index.ts +25 -0
- package/src/client/resources/mcp-integration-catalog.ts +771 -0
- package/src/client/resources/mcp-integration-logos.ts +108 -0
- package/src/client/resources/runtime.ts +1 -0
- package/src/client/resources/use-builtin-capabilities.ts +113 -0
- package/src/client/resources/use-mcp-servers.ts +217 -0
- package/src/client/resources/use-resources.ts +402 -0
- package/src/client/review/ReviewCommentComposer.tsx +123 -0
- package/src/client/review/ReviewStatusBadge.tsx +39 -0
- package/src/client/review/ReviewThreadPanel.tsx +625 -0
- package/src/client/review/index.ts +38 -0
- package/src/client/review/use-review.ts +186 -0
- package/src/client/route-chunk-recovery/index.ts +1 -0
- package/src/client/route-chunk-recovery.ts +331 -0
- package/src/client/route-state.ts +455 -0
- package/src/client/route-warmup.tsx +543 -0
- package/src/client/session-replay-iframe.e2e-host.tsx +100 -0
- package/src/client/session-replay.ts +3417 -0
- package/src/client/settings/AgentsSection.tsx +441 -0
- package/src/client/settings/AutomationsSection.tsx +516 -0
- package/src/client/settings/BackgroundAgentSection.tsx +189 -0
- package/src/client/settings/BrowserSection.tsx +110 -0
- package/src/client/settings/DemoModeSection.tsx +48 -0
- package/src/client/settings/SecretsSection.tsx +875 -0
- package/src/client/settings/SettingsPanel.tsx +3162 -0
- package/src/client/settings/SettingsSection.tsx +283 -0
- package/src/client/settings/SettingsTabsPage.tsx +592 -0
- package/src/client/settings/UsageSection.tsx +401 -0
- package/src/client/settings/VoiceTranscriptionSection.tsx +960 -0
- package/src/client/settings/agent-settings-search.ts +220 -0
- package/src/client/settings/index.ts +27 -0
- package/src/client/settings/useBuilderStatus.ts +1055 -0
- package/src/client/setup-connections/BuilderConnectCard.tsx +89 -0
- package/src/client/setup-connections/ProviderReadinessBadge.tsx +73 -0
- package/src/client/setup-connections/SetupConnectionsPage.tsx +94 -0
- package/src/client/setup-connections/catalog.ts +6 -0
- package/src/client/setup-connections/index.ts +47 -0
- package/src/client/setup-connections/runtime.ts +1 -0
- package/src/client/sharing/ShareButton.tsx +1748 -0
- package/src/client/sharing/ShareDialog.tsx +846 -0
- package/src/client/sharing/VisibilityBadge.tsx +4 -0
- package/src/client/sharing/index.ts +2 -0
- package/src/client/sse-event-processor.ts +2002 -0
- package/src/client/terminal/AgentTerminal.tsx +488 -0
- package/src/client/terminal/index.ts +7 -0
- package/src/client/theme.ts +27 -0
- package/src/client/tombstone/agent-presence-chip.ts +17 -0
- package/src/client/tombstone/editor.ts +265 -0
- package/src/client/tombstone/live-cursor-overlay.ts +21 -0
- package/src/client/tombstone/presence-bar.ts +17 -0
- package/src/client/tombstone/recent-edit-highlights.ts +17 -0
- package/src/client/tombstone/remote-selection-rings.ts +21 -0
- package/src/client/tombstone/rich-markdown-editor.ts +265 -0
- package/src/client/tombstone/ui-dialog.ts +49 -0
- package/src/client/tombstone/ui-dropdown-menu.ts +69 -0
- package/src/client/tombstone/ui-hover-card.ts +21 -0
- package/src/client/tombstone/ui-popover.ts +25 -0
- package/src/client/tombstone/ui-sheet.ts +49 -0
- package/src/client/tombstone/ui-tooltip.ts +29 -0
- package/src/client/tombstone/visual-style-controls.ts +61 -0
- package/src/client/tool-cells/BashCell.tsx +164 -0
- package/src/client/tool-cells/EditCell.tsx +303 -0
- package/src/client/tool-cells/FilesChangedSummary.tsx +208 -0
- package/src/client/tool-cells/WriteCell.tsx +145 -0
- package/src/client/tool-cells/index.ts +7 -0
- package/src/client/tool-display.ts +41 -0
- package/src/client/track.ts +57 -0
- package/src/client/transcription/BuilderTranscriptionCta.tsx +120 -0
- package/src/client/transcription/use-live-transcription.ts +292 -0
- package/src/client/ui/index.ts +55 -0
- package/src/client/uploads/index.ts +6 -0
- package/src/client/uploads/upload-editor-image.ts +79 -0
- package/src/client/uploads/use-upload-resource.ts +24 -0
- package/src/client/url-scrub.ts +51 -0
- package/src/client/use-action.ts +766 -0
- package/src/client/use-agent-chat-context.ts +63 -0
- package/src/client/use-agent-chat-home-handoff.ts +207 -0
- package/src/client/use-agent-chat.ts +53 -0
- package/src/client/use-agent-engine-configured.ts +211 -0
- package/src/client/use-avatar.ts +127 -0
- package/src/client/use-change-version.ts +150 -0
- package/src/client/use-chat-models.ts +252 -0
- package/src/client/use-chat-threads.ts +1262 -0
- package/src/client/use-db-sync.ts +1112 -0
- package/src/client/use-demo-mode-status.ts +30 -0
- package/src/client/use-dev-mode.ts +177 -0
- package/src/client/use-external-value.ts +40 -0
- package/src/client/use-pausing-interval.ts +65 -0
- package/src/client/use-pinch-zoom.ts +202 -0
- package/src/client/use-run-stuck-detection.ts +427 -0
- package/src/client/use-send-to-agent-chat.ts +85 -0
- package/src/client/use-session.ts +114 -0
- package/src/client/utils.ts +7 -0
- package/src/client/visual-style-controls.tsx +1 -0
- package/src/client/vite-dev-recovery-script.ts +226 -0
- package/src/client/voice-provider-status.ts +44 -0
- package/src/client/widgets/index.ts +41 -0
- package/src/code-agents/background-controller.ts +398 -0
- package/src/code-agents/background-run.ts +156 -0
- package/src/code-agents/index.ts +95 -0
- package/src/code-agents/prompt-attachments.ts +39 -0
- package/src/code-agents/transcript-normalizer.ts +750 -0
- package/src/code-agents/transcript-order.ts +73 -0
- package/src/coding-tools/index.ts +742 -0
- package/src/coding-tools/run-code.ts +1662 -0
- package/src/coding-tools/sandbox/adapter.ts +82 -0
- package/src/coding-tools/sandbox/background.ts +406 -0
- package/src/coding-tools/sandbox/executions-store.ts +517 -0
- package/src/coding-tools/sandbox/index.ts +132 -0
- package/src/coding-tools/sandbox/local-child-process-adapter.ts +217 -0
- package/src/collab/agent-identity.ts +5 -0
- package/src/collab/agent-presence.ts +341 -0
- package/src/collab/awareness-store.ts +196 -0
- package/src/collab/awareness.ts +317 -0
- package/src/collab/client-struct.ts +366 -0
- package/src/collab/client.ts +1205 -0
- package/src/collab/emitter.ts +31 -0
- package/src/collab/follow-mode.ts +107 -0
- package/src/collab/index.ts +151 -0
- package/src/collab/json-to-yjs.ts +504 -0
- package/src/collab/presence.ts +207 -0
- package/src/collab/recent-edits.ts +215 -0
- package/src/collab/routes.ts +191 -0
- package/src/collab/storage.ts +228 -0
- package/src/collab/struct-routes.ts +133 -0
- package/src/collab/text-to-yjs.ts +79 -0
- package/src/collab/undo.ts +432 -0
- package/src/collab/xml-ops.ts +68 -0
- package/src/collab/ydoc-manager.ts +544 -0
- package/src/command-navigation/actions.ts +100 -0
- package/src/command-navigation/index.ts +12 -0
- package/src/comments-review/index.ts +1 -0
- package/src/connections/catalog.ts +451 -0
- package/src/connections/index.ts +48 -0
- package/src/connections/reader.ts +911 -0
- package/src/connections/runtime.ts +1 -0
- package/src/credentials/index.ts +187 -0
- package/src/data-programs/actions.ts +335 -0
- package/src/data-programs/contract.ts +284 -0
- package/src/data-programs/execute.ts +693 -0
- package/src/data-programs/index.ts +72 -0
- package/src/data-programs/schema.ts +148 -0
- package/src/data-programs/store.ts +755 -0
- package/src/data-widgets/index.ts +376 -0
- package/src/db/client.ts +1627 -0
- package/src/db/create-get-db.ts +579 -0
- package/src/db/ddl-guard.ts +302 -0
- package/src/db/drizzle-config.ts +190 -0
- package/src/db/ensure-additive-columns.ts +430 -0
- package/src/db/index.ts +57 -0
- package/src/db/migrations.ts +691 -0
- package/src/db/request-telemetry.ts +154 -0
- package/src/db/runtime-diagnostics.ts +331 -0
- package/src/db/schema.ts +121 -0
- package/src/db/widen-columns.ts +75 -0
- package/src/db-admin/agent-tools.ts +202 -0
- package/src/db-admin/index.ts +26 -0
- package/src/db-admin/operations.ts +803 -0
- package/src/db-admin/routes.ts +174 -0
- package/src/db-admin/types.ts +110 -0
- package/src/demo/browser-state.ts +47 -0
- package/src/demo/fetch-interceptor.ts +175 -0
- package/src/demo/redact.ts +575 -0
- package/src/deploy/build.ts +3935 -0
- package/src/deploy/immutable-assets.ts +65 -0
- package/src/deploy/route-discovery.ts +458 -0
- package/src/deploy/workspace-core.ts +282 -0
- package/src/deploy/workspace-deploy.ts +1488 -0
- package/src/eject/provider-api-definitions.ts +193 -0
- package/src/eject/remote-mcp-presets.ts +137 -0
- package/src/eject/setup-readiness-ui.tsx +29 -0
- package/src/eject/workspace-connections.ts +92 -0
- package/src/embedding/agent.ts +159 -0
- package/src/embedding/bridge.ts +297 -0
- package/src/embedding/index.ts +43 -0
- package/src/embedding/protocol.ts +185 -0
- package/src/embedding/react.tsx +319 -0
- package/src/embeddings/index.ts +233 -0
- package/src/eval/agent-runner.ts +210 -0
- package/src/eval/define-eval.ts +55 -0
- package/src/eval/index.ts +49 -0
- package/src/eval/report.ts +75 -0
- package/src/eval/runner.ts +369 -0
- package/src/eval/scorer.ts +244 -0
- package/src/eval/types.ts +188 -0
- package/src/event-bus/bus.ts +140 -0
- package/src/event-bus/index.ts +3 -0
- package/src/event-bus/registry.ts +79 -0
- package/src/event-bus/types.ts +29 -0
- package/src/extensions/actions.ts +1636 -0
- package/src/extensions/change-marker.ts +54 -0
- package/src/extensions/content-patch.ts +468 -0
- package/src/extensions/fetch-tool.ts +461 -0
- package/src/extensions/html-shell.ts +803 -0
- package/src/extensions/local.ts +462 -0
- package/src/extensions/path.ts +40 -0
- package/src/extensions/proxy-security.ts +171 -0
- package/src/extensions/routes.ts +1080 -0
- package/src/extensions/schema.ts +269 -0
- package/src/extensions/session-replay-iframe.ts +131 -0
- package/src/extensions/slots/routes.ts +126 -0
- package/src/extensions/slots/schema.ts +87 -0
- package/src/extensions/slots/store.ts +450 -0
- package/src/extensions/store.ts +1347 -0
- package/src/extensions/theme.ts +114 -0
- package/src/extensions/url-safety.ts +316 -0
- package/src/extensions/web-content.ts +663 -0
- package/src/extensions/web-search-tool.ts +437 -0
- package/src/feature-flags/a2a-action-route.ts +56 -0
- package/src/feature-flags/actions/get-feature-flags.ts +24 -0
- package/src/feature-flags/actions/list-feature-flags.ts +69 -0
- package/src/feature-flags/actions/set-feature-flag.ts +89 -0
- package/src/feature-flags/index.ts +21 -0
- package/src/feature-flags/permissions.ts +53 -0
- package/src/feature-flags/plugin.ts +44 -0
- package/src/feature-flags/registry.ts +96 -0
- package/src/feature-flags/store.ts +176 -0
- package/src/file-upload/actions/upload-image.ts +213 -0
- package/src/file-upload/builder.ts +431 -0
- package/src/file-upload/index.ts +24 -0
- package/src/file-upload/pre-upload-attachments.ts +305 -0
- package/src/file-upload/registry.ts +125 -0
- package/src/file-upload/types.ts +89 -0
- package/src/guards/db-tool-scoping.ts +129 -0
- package/src/guards/index.ts +23 -0
- package/src/guards/no-drizzle-push.ts +116 -0
- package/src/guards/no-env-credentials.ts +229 -0
- package/src/guards/no-env-mutation.ts +114 -0
- package/src/guards/no-localhost-fallback.ts +124 -0
- package/src/guards/no-unscoped-credentials.ts +227 -0
- package/src/guards/no-unscoped-queries.ts +834 -0
- package/src/guards/scan-utils.ts +105 -0
- package/src/guards/types.ts +30 -0
- package/src/history/actions/create-resource-version.ts +92 -0
- package/src/history/actions/get-resource-version.ts +60 -0
- package/src/history/actions/list-resource-history.ts +53 -0
- package/src/history/actions/list-resource-versions.ts +43 -0
- package/src/history/actions/restore-resource-version.ts +96 -0
- package/src/history/index.ts +26 -0
- package/src/history/registry.ts +111 -0
- package/src/history/store.ts +379 -0
- package/src/history/types.ts +69 -0
- package/src/index.browser.ts +155 -0
- package/src/index.ts +408 -0
- package/src/ingestion/docx.ts +158 -0
- package/src/ingestion/figma-node-to-html.ts +1860 -0
- package/src/ingestion/figma.ts +548 -0
- package/src/ingestion/index.ts +107 -0
- package/src/ingestion/media.ts +264 -0
- package/src/ingestion/notion.ts +121 -0
- package/src/ingestion/office.ts +213 -0
- package/src/ingestion/orchestration.ts +160 -0
- package/src/ingestion/pptx.ts +331 -0
- package/src/ingestion/selection.ts +81 -0
- package/src/ingestion/website.ts +151 -0
- package/src/integrations/a2a-continuation-marker.ts +2 -0
- package/src/integrations/a2a-continuation-processor.ts +888 -0
- package/src/integrations/a2a-continuations-store.ts +643 -0
- package/src/integrations/adapter-overrides.ts +12 -0
- package/src/integrations/adapters/discord.ts +364 -0
- package/src/integrations/adapters/email.ts +1164 -0
- package/src/integrations/adapters/google-docs.ts +371 -0
- package/src/integrations/adapters/index.ts +29 -0
- package/src/integrations/adapters/microsoft-teams.ts +413 -0
- package/src/integrations/adapters/slack.ts +1914 -0
- package/src/integrations/adapters/telegram.ts +386 -0
- package/src/integrations/adapters/whatsapp.ts +358 -0
- package/src/integrations/awaiting-input-store.ts +130 -0
- package/src/integrations/catalog.ts +571 -0
- package/src/integrations/computer-supervision-store.ts +423 -0
- package/src/integrations/computer-supervision.ts +213 -0
- package/src/integrations/config-store.ts +131 -0
- package/src/integrations/controls-store.ts +187 -0
- package/src/integrations/eject/messaging-adapters.ts +85 -0
- package/src/integrations/google-docs-poller.ts +671 -0
- package/src/integrations/identity-links-store.ts +210 -0
- package/src/integrations/identity.ts +196 -0
- package/src/integrations/index.ts +220 -0
- package/src/integrations/installations-store.ts +677 -0
- package/src/integrations/integration-memory.ts +187 -0
- package/src/integrations/internal-token.ts +106 -0
- package/src/integrations/pending-tasks-retry-job.ts +286 -0
- package/src/integrations/pending-tasks-store.ts +468 -0
- package/src/integrations/plugin.ts +3048 -0
- package/src/integrations/remote-commands-store.ts +690 -0
- package/src/integrations/remote-devices-store.ts +492 -0
- package/src/integrations/remote-json-safety.ts +127 -0
- package/src/integrations/remote-push-delivery-job.ts +48 -0
- package/src/integrations/remote-push-delivery.ts +370 -0
- package/src/integrations/remote-push-store.ts +664 -0
- package/src/integrations/remote-retry-job.ts +55 -0
- package/src/integrations/remote-run-events-store.ts +222 -0
- package/src/integrations/remote-types.ts +186 -0
- package/src/integrations/runtime.ts +1 -0
- package/src/integrations/scope-store.ts +498 -0
- package/src/integrations/slack-manifest.ts +78 -0
- package/src/integrations/slack-oauth.ts +344 -0
- package/src/integrations/task-queue-stats.ts +144 -0
- package/src/integrations/thread-mapping-store.ts +134 -0
- package/src/integrations/types.ts +485 -0
- package/src/integrations/usage-budget-store.ts +906 -0
- package/src/integrations/webhook-delivery.ts +55 -0
- package/src/integrations/webhook-handler.ts +1939 -0
- package/src/jobs/actions/list-recurring-jobs.ts +105 -0
- package/src/jobs/actions/manage-recurring-job.ts +85 -0
- package/src/jobs/cron.ts +126 -0
- package/src/jobs/index.ts +9 -0
- package/src/jobs/scheduler.ts +703 -0
- package/src/jobs/tools.ts +367 -0
- package/src/local-artifacts/index.ts +1372 -0
- package/src/localization/actions/get-localization-preference.ts +24 -0
- package/src/localization/actions/set-localization-preference.ts +32 -0
- package/src/localization/default-messages.ts +1009 -0
- package/src/localization/index.ts +2 -0
- package/src/localization/server.ts +131 -0
- package/src/localization/shared.ts +208 -0
- package/src/mcp/actions/call-mcp-tool.ts +19 -0
- package/src/mcp/actions/create-org-service-token.ts +87 -0
- package/src/mcp/actions/list-mcp-tools.ts +18 -0
- package/src/mcp/actions/list-org-service-tokens.ts +44 -0
- package/src/mcp/actions/revoke-org-service-token.ts +30 -0
- package/src/mcp/actions/service-token-access.ts +121 -0
- package/src/mcp/ask-app-inline-tasks.ts +125 -0
- package/src/mcp/build-server.ts +2356 -0
- package/src/mcp/builtin-tools.ts +1506 -0
- package/src/mcp/connect-route.ts +1460 -0
- package/src/mcp/connect-store.ts +680 -0
- package/src/mcp/embed-app.ts +2051 -0
- package/src/mcp/embed-route.ts +84 -0
- package/src/mcp/external-agent-policy.ts +18 -0
- package/src/mcp/index.ts +47 -0
- package/src/mcp/oauth-route.ts +1016 -0
- package/src/mcp/oauth-store.ts +581 -0
- package/src/mcp/oauth-token.ts +227 -0
- package/src/mcp/org-directory.ts +343 -0
- package/src/mcp/route-paths.ts +34 -0
- package/src/mcp/screen-memory-stdio.ts +290 -0
- package/src/mcp/server.ts +507 -0
- package/src/mcp/stdio.ts +328 -0
- package/src/mcp/workspace-resolve.ts +261 -0
- package/src/mcp-client/app-api.ts +136 -0
- package/src/mcp-client/app-result.ts +64 -0
- package/src/mcp-client/builtin-capabilities.ts +146 -0
- package/src/mcp-client/builtin-store.ts +93 -0
- package/src/mcp-client/config.ts +281 -0
- package/src/mcp-client/errors.ts +54 -0
- package/src/mcp-client/hub-client.ts +172 -0
- package/src/mcp-client/hub-routes.ts +198 -0
- package/src/mcp-client/index.ts +504 -0
- package/src/mcp-client/manager.ts +968 -0
- package/src/mcp-client/oauth-client.ts +462 -0
- package/src/mcp-client/oauth-routes.ts +307 -0
- package/src/mcp-client/remote-store.ts +674 -0
- package/src/mcp-client/remote-url.ts +97 -0
- package/src/mcp-client/routes.ts +1093 -0
- package/src/mcp-client/screen-memory-local.ts +461 -0
- package/src/mcp-client/tool-policy.ts +182 -0
- package/src/mcp-client/visibility.ts +56 -0
- package/src/mcp-client/workspace-servers.ts +379 -0
- package/src/navigation/index.ts +170 -0
- package/src/notifications/actions.ts +151 -0
- package/src/notifications/channels.ts +410 -0
- package/src/notifications/index.ts +22 -0
- package/src/notifications/registry.ts +216 -0
- package/src/notifications/routes.ts +106 -0
- package/src/notifications/store.ts +236 -0
- package/src/notifications/types.ts +52 -0
- package/src/oauth-tokens/google-refresh.ts +175 -0
- package/src/oauth-tokens/index.ts +15 -0
- package/src/oauth-tokens/store.ts +384 -0
- package/src/observability/cleanup-job.ts +124 -0
- package/src/observability/evals.ts +507 -0
- package/src/observability/experiments.ts +359 -0
- package/src/observability/feedback.ts +332 -0
- package/src/observability/hosted-model-experiment.ts +118 -0
- package/src/observability/index.ts +62 -0
- package/src/observability/plugin.ts +31 -0
- package/src/observability/routes.ts +431 -0
- package/src/observability/sentiment.ts +261 -0
- package/src/observability/store.ts +1347 -0
- package/src/observability/traces.ts +789 -0
- package/src/observability/tracing.ts +157 -0
- package/src/observability/tracking-identity.ts +55 -0
- package/src/observability/types.ts +211 -0
- package/src/onboarding/default-steps.ts +428 -0
- package/src/onboarding/index.ts +22 -0
- package/src/onboarding/plugin.ts +262 -0
- package/src/onboarding/registry.ts +47 -0
- package/src/onboarding/types.ts +96 -0
- package/src/org/accept-pending.ts +98 -0
- package/src/org/auto-join-domain.ts +113 -0
- package/src/org/context.ts +614 -0
- package/src/org/free-email-providers.ts +124 -0
- package/src/org/handlers.ts +1153 -0
- package/src/org/index.ts +63 -0
- package/src/org/migrations.ts +105 -0
- package/src/org/permissions.ts +30 -0
- package/src/org/plugin.ts +255 -0
- package/src/org/schema.ts +28 -0
- package/src/org/types.ts +57 -0
- package/src/org-team/index.ts +1 -0
- package/src/package-lifecycle/deprecated-imports.ts +179 -0
- package/src/package-lifecycle/eject-manifest.ts +345 -0
- package/src/package-lifecycle/index.ts +7 -0
- package/src/package-lifecycle/manifest.ts +120 -0
- package/src/package-lifecycle/migration-manifest.ts +159 -0
- package/src/package-lifecycle/migration-message.ts +6 -0
- package/src/package-lifecycle/tombstone.ts +58 -0
- package/src/package-lifecycle/upgrade-error.ts +37 -0
- package/src/private-blob/index.ts +18 -0
- package/src/private-blob/registry.ts +242 -0
- package/src/private-blob/types.ts +55 -0
- package/src/progress/actions.ts +179 -0
- package/src/progress/index.ts +16 -0
- package/src/progress/registry.ts +127 -0
- package/src/progress/routes.ts +89 -0
- package/src/progress/store.ts +353 -0
- package/src/progress/types.ts +59 -0
- package/src/provider-api/actions/custom-provider-registration.ts +197 -0
- package/src/provider-api/actions/delete-staged-dataset.ts +5 -0
- package/src/provider-api/actions/github-repo-files.ts +266 -0
- package/src/provider-api/actions/list-staged-datasets.ts +5 -0
- package/src/provider-api/actions/provider-api-audit.ts +88 -0
- package/src/provider-api/actions/provider-api.ts +460 -0
- package/src/provider-api/actions/query-staged-dataset.ts +5 -0
- package/src/provider-api/actions/staged-datasets.ts +359 -0
- package/src/provider-api/corpus-jobs-store.ts +522 -0
- package/src/provider-api/corpus-jobs.ts +1817 -0
- package/src/provider-api/custom-registry.ts +456 -0
- package/src/provider-api/github-repo.ts +675 -0
- package/src/provider-api/gong.ts +108 -0
- package/src/provider-api/index.ts +5444 -0
- package/src/provider-api/quota-governor.ts +562 -0
- package/src/provider-api/runtime.ts +1 -0
- package/src/provider-api/staged-datasets-aggregate.ts +304 -0
- package/src/provider-api/staged-datasets-store.ts +441 -0
- package/src/provider-api/staging.ts +483 -0
- package/src/resources/agents.ts +65 -0
- package/src/resources/emitter.ts +54 -0
- package/src/resources/handlers.ts +728 -0
- package/src/resources/index.ts +4 -0
- package/src/resources/metadata.ts +252 -0
- package/src/resources/script-helpers.ts +160 -0
- package/src/resources/store.ts +1618 -0
- package/src/review/actions/consume-review-feedback.ts +48 -0
- package/src/review/actions/create-review-comment.ts +108 -0
- package/src/review/actions/delete-review-comment.ts +67 -0
- package/src/review/actions/get-review-feedback.ts +49 -0
- package/src/review/actions/list-review-comments.ts +87 -0
- package/src/review/actions/reply-review-comment.ts +118 -0
- package/src/review/actions/resolve-review-thread.ts +94 -0
- package/src/review/actions/send-review-thread-to-agent.ts +62 -0
- package/src/review/actions/set-review-status.ts +58 -0
- package/src/review/identity.ts +101 -0
- package/src/review/index.ts +45 -0
- package/src/review/mentions.ts +44 -0
- package/src/review/registry.ts +111 -0
- package/src/review/store.ts +997 -0
- package/src/review/types.ts +101 -0
- package/src/router/index.ts +24 -0
- package/src/scripts/agent-engines/list-agent-engines.ts +130 -0
- package/src/scripts/agent-engines/manage-agent-engine.ts +197 -0
- package/src/scripts/agent-engines/set-agent-engine.ts +84 -0
- package/src/scripts/agent-engines/test-agent-engine.ts +174 -0
- package/src/scripts/call-agent.ts +792 -0
- package/src/scripts/chat/index.ts +8 -0
- package/src/scripts/chat/open-chat.ts +56 -0
- package/src/scripts/chat/search-chats.ts +121 -0
- package/src/scripts/core-scripts.ts +22 -0
- package/src/scripts/db/check-scoping.ts +212 -0
- package/src/scripts/db/exec.ts +791 -0
- package/src/scripts/db/index.ts +19 -0
- package/src/scripts/db/migrate-encrypt-credentials.ts +223 -0
- package/src/scripts/db/migrate-encrypt-oauth-tokens.ts +213 -0
- package/src/scripts/db/migrate-user-api-keys.ts +257 -0
- package/src/scripts/db/patch.ts +820 -0
- package/src/scripts/db/query.ts +299 -0
- package/src/scripts/db/reset-dev-owner.ts +285 -0
- package/src/scripts/db/safety.ts +286 -0
- package/src/scripts/db/schema.ts +364 -0
- package/src/scripts/db/scoping.ts +341 -0
- package/src/scripts/db/sqlite-client.ts +78 -0
- package/src/scripts/db/tool-mode.ts +23 -0
- package/src/scripts/db/tool-schemas.ts +31 -0
- package/src/scripts/db/wipe-leaked-builder-keys.ts +201 -0
- package/src/scripts/dev/index.ts +296 -0
- package/src/scripts/dev/list-files.ts +119 -0
- package/src/scripts/dev/read-file.ts +77 -0
- package/src/scripts/dev/search-files.ts +151 -0
- package/src/scripts/dev/shell.ts +111 -0
- package/src/scripts/dev/write-file.ts +56 -0
- package/src/scripts/dev-session.ts +96 -0
- package/src/scripts/docs/index.ts +8 -0
- package/src/scripts/docs/search.ts +274 -0
- package/src/scripts/docs/source-search.ts +363 -0
- package/src/scripts/index.ts +15 -0
- package/src/scripts/manage-agent-loop-settings.ts +84 -0
- package/src/scripts/parse-args.ts +46 -0
- package/src/scripts/resources/delete-memory.ts +67 -0
- package/src/scripts/resources/delete.ts +69 -0
- package/src/scripts/resources/effective.ts +71 -0
- package/src/scripts/resources/index.ts +18 -0
- package/src/scripts/resources/list.ts +103 -0
- package/src/scripts/resources/migrate-learnings.ts +40 -0
- package/src/scripts/resources/read.ts +107 -0
- package/src/scripts/resources/save-memory.ts +99 -0
- package/src/scripts/resources/write.ts +158 -0
- package/src/scripts/runner.ts +339 -0
- package/src/scripts/utils.ts +130 -0
- package/src/search/index.ts +413 -0
- package/src/search-utils/index.ts +112 -0
- package/src/secrets/crypto.ts +228 -0
- package/src/secrets/index.ts +65 -0
- package/src/secrets/onboarding.ts +101 -0
- package/src/secrets/register-framework-secrets.ts +228 -0
- package/src/secrets/register.ts +129 -0
- package/src/secrets/routes.ts +697 -0
- package/src/secrets/schema.ts +52 -0
- package/src/secrets/storage.ts +561 -0
- package/src/secrets/substitution.ts +305 -0
- package/src/server/__snapshots__/agent-chat-plugin.surface.spec.ts.snap +150 -0
- package/src/server/action-change.ts +66 -0
- package/src/server/action-discovery.ts +774 -0
- package/src/server/action-routes.ts +608 -0
- package/src/server/agent-access.ts +72 -0
- package/src/server/agent-chat/action-filters-a2a.ts +339 -0
- package/src/server/agent-chat/browser-team-tools.ts +510 -0
- package/src/server/agent-chat/context-tools.ts +599 -0
- package/src/server/agent-chat/framework-prompts.ts +467 -0
- package/src/server/agent-chat/lazy-fs.ts +11 -0
- package/src/server/agent-chat/mcp-glue.ts +91 -0
- package/src/server/agent-chat/plugin-options.ts +379 -0
- package/src/server/agent-chat/process-run-failure.ts +62 -0
- package/src/server/agent-chat/prompt-resources.ts +1004 -0
- package/src/server/agent-chat/recurring-jobs-runtime.ts +80 -0
- package/src/server/agent-chat/request-surface.ts +71 -0
- package/src/server/agent-chat/run-code-tools.ts +67 -0
- package/src/server/agent-chat/script-entries.ts +796 -0
- package/src/server/agent-chat/shared-thread.ts +301 -0
- package/src/server/agent-chat/skill-frontmatter.ts +41 -0
- package/src/server/agent-chat-plugin.ts +5930 -0
- package/src/server/agent-discovery.ts +706 -0
- package/src/server/agent-engine-api-key-route.ts +236 -0
- package/src/server/agent-run-context.ts +204 -0
- package/src/server/agent-teams-run-queue.ts +344 -0
- package/src/server/agent-teams.ts +2257 -0
- package/src/server/agents-bundle.ts +464 -0
- package/src/server/analytics.ts +105 -0
- package/src/server/app-base-path.ts +79 -0
- package/src/server/app-name.ts +53 -0
- package/src/server/app-url.ts +184 -0
- package/src/server/attachment-actions.ts +276 -0
- package/src/server/attribution.ts +214 -0
- package/src/server/auth-marketing.ts +260 -0
- package/src/server/auth-plugin.ts +27 -0
- package/src/server/auth.ts +3803 -0
- package/src/server/better-auth-instance.ts +1197 -0
- package/src/server/builder-browser.ts +1701 -0
- package/src/server/builder-design-systems.ts +668 -0
- package/src/server/builder-space.ts +176 -0
- package/src/server/captcha.ts +63 -0
- package/src/server/capture-error.ts +64 -0
- package/src/server/cli-capture.ts +141 -0
- package/src/server/collab-plugin.ts +391 -0
- package/src/server/complete-text.ts +231 -0
- package/src/server/cookie-namespace.ts +159 -0
- package/src/server/core-routes-plugin.ts +4122 -0
- package/src/server/cors-origins.ts +60 -0
- package/src/server/create-server.ts +268 -0
- package/src/server/credential-provider.ts +1202 -0
- package/src/server/csrf.ts +229 -0
- package/src/server/date-utils.ts +42 -0
- package/src/server/deep-link.ts +119 -0
- package/src/server/derived-secret.ts +32 -0
- package/src/server/design-token-utils.ts +1032 -0
- package/src/server/desktop-sso.ts +83 -0
- package/src/server/edge.ts +69 -0
- package/src/server/email-actions.ts +218 -0
- package/src/server/email-template.ts +192 -0
- package/src/server/email-templates.ts +148 -0
- package/src/server/email.ts +219 -0
- package/src/server/embed-route.ts +283 -0
- package/src/server/embed-session.ts +805 -0
- package/src/server/embedded.ts +252 -0
- package/src/server/entry-server.tsx +154 -0
- package/src/server/framework-request-handler.ts +848 -0
- package/src/server/fusion-app.ts +447 -0
- package/src/server/google-auth-mode.ts +34 -0
- package/src/server/google-auth-plugin.ts +496 -0
- package/src/server/google-oauth-credentials.ts +128 -0
- package/src/server/google-oauth.ts +972 -0
- package/src/server/google-realtime-session.ts +182 -0
- package/src/server/h3-helpers.ts +174 -0
- package/src/server/http-response-telemetry.ts +396 -0
- package/src/server/identity-sso-store.ts +304 -0
- package/src/server/identity-sso.ts +514 -0
- package/src/server/index.ts +593 -0
- package/src/server/missing-key.ts +36 -0
- package/src/server/oauth-helpers.ts +44 -0
- package/src/server/oauth-public-origin.ts +44 -0
- package/src/server/oauth-return-url.ts +120 -0
- package/src/server/og-fonts-data.ts +15 -0
- package/src/server/og-fonts.ts +75 -0
- package/src/server/onboarding-html.ts +4040 -0
- package/src/server/open-route.ts +311 -0
- package/src/server/org-admin.ts +28 -0
- package/src/server/poll-events.ts +88 -0
- package/src/server/poll.ts +1224 -0
- package/src/server/prompts/framework-core-compact.ts +107 -0
- package/src/server/prompts/framework-core.ts +143 -0
- package/src/server/prompts/index.ts +16 -0
- package/src/server/prompts/model-overlays.ts +54 -0
- package/src/server/prompts/shared-rules.ts +104 -0
- package/src/server/realtime-voice.ts +967 -0
- package/src/server/recap-image-route.ts +242 -0
- package/src/server/recap-image-store.ts +185 -0
- package/src/server/request-context.ts +416 -0
- package/src/server/request-origin.ts +67 -0
- package/src/server/resources-plugin.ts +119 -0
- package/src/server/schema-prompt.ts +429 -0
- package/src/server/scoped-key-storage.ts +223 -0
- package/src/server/security-headers.ts +170 -0
- package/src/server/self-dispatch.ts +224 -0
- package/src/server/sentry-config.ts +73 -0
- package/src/server/sentry-plugin.ts +138 -0
- package/src/server/sentry.ts +444 -0
- package/src/server/short-lived-token.ts +170 -0
- package/src/server/social-og-image.ts +454 -0
- package/src/server/sse.ts +102 -0
- package/src/server/ssr-handler.ts +466 -0
- package/src/server/transcribe-voice.ts +1076 -0
- package/src/server/voice-providers-status.ts +118 -0
- package/src/server/workspace-oauth.ts +31 -0
- package/src/server/workspace-provider-oauth.ts +970 -0
- package/src/session-replay-iframe-protocol.ts +58 -0
- package/src/settings/handlers.ts +44 -0
- package/src/settings/index.ts +34 -0
- package/src/settings/org-settings.ts +83 -0
- package/src/settings/script-helpers.ts +24 -0
- package/src/settings/store.ts +247 -0
- package/src/settings/user-settings.ts +47 -0
- package/src/setup-connections/index.ts +90 -0
- package/src/setup-connections/onboarding.ts +47 -0
- package/src/shared/agent-access.ts +112 -0
- package/src/shared/agent-chat.ts +124 -0
- package/src/shared/agent-env.ts +46 -0
- package/src/shared/agent-readable-resource.ts +111 -0
- package/src/shared/agent-sidebar-url.ts +39 -0
- package/src/shared/cache-control.ts +25 -0
- package/src/shared/cloudflare-globals.d.ts +34 -0
- package/src/shared/context-xray.ts +172 -0
- package/src/shared/embed-auth.ts +6 -0
- package/src/shared/index.ts +83 -0
- package/src/shared/llm-connection.ts +42 -0
- package/src/shared/markdown-block-split.ts +129 -0
- package/src/shared/mcp-connect-content.ts +130 -0
- package/src/shared/mcp-embed-headers.ts +175 -0
- package/src/shared/mcp-integration-config.ts +101 -0
- package/src/shared/merge-by-id.ts +14 -0
- package/src/shared/oauth-state.ts +39 -0
- package/src/shared/reasoning-effort.ts +228 -0
- package/src/shared/route-warmup-config.ts +128 -0
- package/src/shared/runtime.ts +31 -0
- package/src/shared/social-meta.ts +82 -0
- package/src/shared/streaming-text-smoothing.ts +184 -0
- package/src/shared/truncate.ts +13 -0
- package/src/shared/workspace-app-audience.ts +177 -0
- package/src/shared/workspace-app-id.ts +54 -0
- package/src/sharing/access.ts +585 -0
- package/src/sharing/actions/create-agent-resource-link.ts +91 -0
- package/src/sharing/actions/extension-change.ts +22 -0
- package/src/sharing/actions/list-resource-shares.ts +93 -0
- package/src/sharing/actions/set-resource-visibility.ts +91 -0
- package/src/sharing/actions/share-resource.ts +322 -0
- package/src/sharing/actions/unshare-resource.ts +72 -0
- package/src/sharing/index.ts +35 -0
- package/src/sharing/registry.ts +201 -0
- package/src/sharing/schema.ts +99 -0
- package/src/styles/agent-conversation.css +766 -0
- package/src/styles/agent-native.css +938 -0
- package/src/styles/blocks.css +1717 -0
- package/src/styles/chat-history-list.css +1 -0
- package/src/styles/rich-markdown-editor.css +1 -0
- package/src/tailwind.preset.ts +140 -0
- package/src/templates/chat/.agents/skills/agent-native-docs/SKILL.md +15 -13
- package/src/templates/chat/.agents/skills/customizing-agent-native/SKILL.md +63 -20
- package/src/templates/chat/.agents/skills/self-modifying-code/SKILL.md +9 -6
- package/src/templates/chat/AGENTS.md +3 -2
- package/src/templates/default/.agents/skills/agent-native-docs/SKILL.md +15 -13
- package/src/templates/default/.agents/skills/customizing-agent-native/SKILL.md +63 -20
- package/src/templates/default/.agents/skills/self-modifying-code/SKILL.md +9 -6
- package/src/templates/default/AGENTS.md +6 -5
- package/src/templates/headless/.agents/skills/agent-native-docs/SKILL.md +15 -13
- package/src/templates/headless/.agents/skills/customizing-agent-native/SKILL.md +63 -20
- package/src/templates/headless/AGENTS.md +5 -4
- package/src/templates/workspace-core/.agents/skills/agent-native-docs/SKILL.md +15 -13
- package/src/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +3 -2
- package/src/templates/workspace-core/.agents/skills/customizing-agent-native/SKILL.md +63 -20
- package/src/templates/workspace-core/.agents/skills/self-modifying-code/SKILL.md +9 -6
- package/src/templates/workspace-core/AGENTS.md +4 -4
- package/src/templates/workspace-root/AGENTS.md +4 -4
- package/src/terminal/cli-registry.ts +57 -0
- package/src/terminal/index.ts +22 -0
- package/src/terminal/pty-server.ts +389 -0
- package/src/terminal/terminal-plugin.ts +265 -0
- package/src/testing.ts +23 -0
- package/src/tracking/index.ts +10 -0
- package/src/tracking/providers.ts +422 -0
- package/src/tracking/registry.ts +102 -0
- package/src/tracking/route.ts +108 -0
- package/src/tracking/types.ts +16 -0
- package/src/transcription/builder-transcription.ts +118 -0
- package/src/triggers/actions/list-automations.ts +99 -0
- package/src/triggers/actions/manage-automation.ts +78 -0
- package/src/triggers/actions.ts +333 -0
- package/src/triggers/condition-evaluator.ts +163 -0
- package/src/triggers/dispatcher.ts +536 -0
- package/src/triggers/index.ts +13 -0
- package/src/triggers/routes.ts +322 -0
- package/src/triggers/types.ts +42 -0
- package/src/types/pglite.d.ts +26 -0
- package/src/usage/store.ts +698 -0
- package/src/vite/action-types-plugin.ts +481 -0
- package/src/vite/agent-web-plugin.ts +78 -0
- package/src/vite/agents-bundle-plugin.ts +185 -0
- package/src/vite/client.ts +3039 -0
- package/src/vite/index.ts +21 -0
- package/src/voice/index.ts +18 -0
- package/src/voice/voice-cleanup-prompt.ts +38 -0
- package/src/voice/voice-context.ts +217 -0
- package/src/voice/voice-replacements.ts +75 -0
- package/src/workspace-connections/credentials.ts +603 -0
- package/src/workspace-connections/index.ts +72 -0
- package/src/workspace-connections/lifecycle.ts +43 -0
- package/src/workspace-connections/store.ts +1943 -0
- package/src/workspace-files/index.ts +18 -0
- package/src/workspace-files/store.ts +339 -0
- package/src/workspace-files/tool.ts +266 -0
|
@@ -0,0 +1,2193 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
|
|
5
|
+
import type { ClientId } from "./mcp-config-writers.js";
|
|
6
|
+
|
|
7
|
+
export interface InstallLocalContextXrayOptions {
|
|
8
|
+
baseDir?: string;
|
|
9
|
+
clients: ClientId[];
|
|
10
|
+
scope: string;
|
|
11
|
+
dryRun?: boolean;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface InstallLocalContextXrayResult {
|
|
15
|
+
commands: string[];
|
|
16
|
+
scriptPath: string;
|
|
17
|
+
written: string[];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const CONTEXT_XRAY_EXECUTABLE = String.raw`#!/usr/bin/env node
|
|
21
|
+
"use strict";
|
|
22
|
+
|
|
23
|
+
const childProcess = require("node:child_process");
|
|
24
|
+
const fs = require("node:fs");
|
|
25
|
+
const os = require("node:os");
|
|
26
|
+
const path = require("node:path");
|
|
27
|
+
const { pathToFileURL } = require("node:url");
|
|
28
|
+
|
|
29
|
+
const HOME = os.homedir();
|
|
30
|
+
const CODEX_DIR = process.env.CODEX_HOME && process.env.CODEX_HOME.trim() ? process.env.CODEX_HOME.trim() : path.join(HOME, ".codex");
|
|
31
|
+
const CLAUDE_DIR = path.join(HOME, ".claude");
|
|
32
|
+
const OUT_DIR = path.join(CODEX_DIR, "context-xray");
|
|
33
|
+
const SESSION_ID_RE = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/i;
|
|
34
|
+
const CATEGORIES = ["user", "assistant", "tool_call", "tool_output", "reasoning", "instructions", "attachment", "metadata", "other"];
|
|
35
|
+
const LABELS = {
|
|
36
|
+
user: "User asks",
|
|
37
|
+
assistant: "Assistant text",
|
|
38
|
+
tool_call: "Tool calls",
|
|
39
|
+
tool_output: "Tool output",
|
|
40
|
+
reasoning: "Reasoning",
|
|
41
|
+
instructions: "Instructions/context",
|
|
42
|
+
attachment: "Attachments",
|
|
43
|
+
metadata: "Metadata",
|
|
44
|
+
other: "Other",
|
|
45
|
+
};
|
|
46
|
+
const COLORS = {
|
|
47
|
+
user: "#8ba8ff",
|
|
48
|
+
assistant: "#55b982",
|
|
49
|
+
tool_call: "#f0a85b",
|
|
50
|
+
tool_output: "#e06b73",
|
|
51
|
+
reasoning: "#a77be8",
|
|
52
|
+
instructions: "#6ac3d5",
|
|
53
|
+
attachment: "#d6a85a",
|
|
54
|
+
metadata: "#9aa3ad",
|
|
55
|
+
other: "#c3c8ce",
|
|
56
|
+
};
|
|
57
|
+
const MAX_EVENT_SAMPLES = 80;
|
|
58
|
+
const MAX_STEP_SAMPLES = 900;
|
|
59
|
+
const PRESSURE_WINDOW = 5;
|
|
60
|
+
const PRESSURE_TOKENS = 50000;
|
|
61
|
+
|
|
62
|
+
function parseArgs(argv) {
|
|
63
|
+
const out = {
|
|
64
|
+
mode: "current",
|
|
65
|
+
source: "both",
|
|
66
|
+
since: "7d",
|
|
67
|
+
last: 12,
|
|
68
|
+
scanLimit: 80,
|
|
69
|
+
project: process.cwd(),
|
|
70
|
+
allProjects: false,
|
|
71
|
+
sessionId: "",
|
|
72
|
+
format: "html",
|
|
73
|
+
out: "",
|
|
74
|
+
open: false,
|
|
75
|
+
port: 0,
|
|
76
|
+
};
|
|
77
|
+
for (let i = 0; i < argv.length; i++) {
|
|
78
|
+
const arg = argv[i];
|
|
79
|
+
const eat = (flag) => {
|
|
80
|
+
if (arg === flag) return argv[++i] || "";
|
|
81
|
+
if (arg.startsWith(flag + "=")) return arg.slice(flag.length + 1);
|
|
82
|
+
return undefined;
|
|
83
|
+
};
|
|
84
|
+
let value;
|
|
85
|
+
if (arg === "threads" || arg === "--threads") out.mode = "threads";
|
|
86
|
+
else if (arg === "trends" || arg === "--trends") out.mode = "trends";
|
|
87
|
+
else if (arg === "current" || arg === "--current") out.mode = "current";
|
|
88
|
+
else if ((value = eat("--source")) !== undefined) out.source = value;
|
|
89
|
+
else if ((value = eat("--since")) !== undefined) out.since = value;
|
|
90
|
+
else if ((value = eat("--last")) !== undefined) out.last = Number(value) || out.last;
|
|
91
|
+
else if ((value = eat("--scan-limit")) !== undefined) out.scanLimit = Number(value) || out.scanLimit;
|
|
92
|
+
else if ((value = eat("--project")) !== undefined) out.project = value;
|
|
93
|
+
else if ((value = eat("--session-id")) !== undefined) out.sessionId = value;
|
|
94
|
+
else if ((value = eat("--format")) !== undefined) out.format = value;
|
|
95
|
+
else if ((value = eat("--out")) !== undefined) out.out = value;
|
|
96
|
+
else if ((value = eat("--port")) !== undefined) out.port = Number(value) || 0;
|
|
97
|
+
else if (arg === "--all-projects") out.allProjects = true;
|
|
98
|
+
else if (arg === "--open") out.open = true;
|
|
99
|
+
else if (arg === "--json") out.format = "json";
|
|
100
|
+
else if (arg === "--help" || arg === "-h") out.help = true;
|
|
101
|
+
}
|
|
102
|
+
if (process.env.CLAUDE_CODE_SESSION_ID && !out.sessionId && out.mode === "current") {
|
|
103
|
+
out.sessionId = process.env.CLAUDE_CODE_SESSION_ID;
|
|
104
|
+
}
|
|
105
|
+
if (out.mode === "threads") {
|
|
106
|
+
out.allProjects = true;
|
|
107
|
+
out.last = Math.max(out.last, 30);
|
|
108
|
+
}
|
|
109
|
+
if (out.mode === "trends") {
|
|
110
|
+
out.allProjects = true;
|
|
111
|
+
out.last = Math.max(out.last, 60);
|
|
112
|
+
}
|
|
113
|
+
if (out.mode === "current") {
|
|
114
|
+
out.last = 1;
|
|
115
|
+
}
|
|
116
|
+
return out;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function help() {
|
|
120
|
+
console.log([
|
|
121
|
+
"Context X-Ray",
|
|
122
|
+
"",
|
|
123
|
+
"Usage:",
|
|
124
|
+
" context-xray --open Visualize the current/recent local thread",
|
|
125
|
+
" context-xray threads --open Pick from recent Codex/Claude sessions",
|
|
126
|
+
" context-xray trends --since 7d --open Show recent usage trends",
|
|
127
|
+
" context-xray --session-id <id> --open Analyze one exact session",
|
|
128
|
+
"",
|
|
129
|
+
"Options:",
|
|
130
|
+
" --source codex|claude|both",
|
|
131
|
+
" --since 24h|7d|2w|ISO",
|
|
132
|
+
" --last <n>",
|
|
133
|
+
" --all-projects",
|
|
134
|
+
" --format html|json",
|
|
135
|
+
" --out <path>",
|
|
136
|
+
].join("\n"));
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function mkdirp(dir) {
|
|
140
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function parseSince(value) {
|
|
144
|
+
const now = Date.now();
|
|
145
|
+
const match = String(value || "7d").trim().toLowerCase().match(/^(\d+)([hdw])$/);
|
|
146
|
+
if (match) {
|
|
147
|
+
const amount = Number(match[1]);
|
|
148
|
+
const unit = match[2];
|
|
149
|
+
const mult = unit === "h" ? 3600000 : unit === "d" ? 86400000 : 604800000;
|
|
150
|
+
return now - amount * mult;
|
|
151
|
+
}
|
|
152
|
+
const parsed = Date.parse(value);
|
|
153
|
+
return Number.isFinite(parsed) ? parsed : now - 7 * 86400000;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function readJsonl(file) {
|
|
157
|
+
try {
|
|
158
|
+
return fs.readFileSync(file, "utf8").split(/\r?\n/).filter(Boolean).map((line) => {
|
|
159
|
+
try {
|
|
160
|
+
return JSON.parse(line);
|
|
161
|
+
} catch {
|
|
162
|
+
return null;
|
|
163
|
+
}
|
|
164
|
+
}).filter(Boolean);
|
|
165
|
+
} catch {
|
|
166
|
+
return [];
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function compact(value) {
|
|
171
|
+
try {
|
|
172
|
+
const text = JSON.stringify(value);
|
|
173
|
+
return text.length > 250000 ? text.slice(0, 250000) : text;
|
|
174
|
+
} catch {
|
|
175
|
+
return String(value || "");
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function textFrom(value, depth) {
|
|
180
|
+
if (depth > 8 || value == null) return "";
|
|
181
|
+
if (typeof value === "string") return value.length > 250000 ? value.slice(0, 250000) : value;
|
|
182
|
+
if (typeof value !== "object") return "";
|
|
183
|
+
if (Array.isArray(value)) return value.map((item) => textFrom(item, depth + 1)).filter(Boolean).join("\n");
|
|
184
|
+
const skip = new Set(["encrypted_content", "id", "uuid", "call_id", "sessionId", "parentUuid"]);
|
|
185
|
+
const keys = new Set(["text", "message", "output", "result", "content", "summary", "arguments", "args", "input", "stdout", "stderr", "attachment"]);
|
|
186
|
+
const parts = [];
|
|
187
|
+
for (const key of Object.keys(value)) {
|
|
188
|
+
if (skip.has(key)) continue;
|
|
189
|
+
const item = value[key];
|
|
190
|
+
if (keys.has(key) || typeof item === "object") {
|
|
191
|
+
const text = textFrom(item, depth + 1);
|
|
192
|
+
if (text) parts.push(text);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return parts.join("\n");
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function addCounter(counter, key, amount) {
|
|
199
|
+
if (!key || !amount) return;
|
|
200
|
+
counter[key] = (counter[key] || 0) + amount;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function mergeCounter(into, from) {
|
|
204
|
+
for (const key of Object.keys(from || {})) addCounter(into, key, from[key]);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function estimateTokens(chars) {
|
|
208
|
+
return chars > 0 ? Math.max(1, Math.ceil(chars / 4)) : 0;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function fmtTokens(tokens) {
|
|
212
|
+
if (tokens >= 1000000) return (tokens / 1000000).toFixed(1) + "m";
|
|
213
|
+
if (tokens >= 1000) return (tokens / 1000).toFixed(1) + "k";
|
|
214
|
+
return String(tokens);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function pct(part, total) {
|
|
218
|
+
return total > 0 ? Math.max(0, Math.min(100, (part / total) * 100)) : 0;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function pathCounts(text) {
|
|
222
|
+
const out = {};
|
|
223
|
+
const matches = String(text || "").match(/(?:(?:\/[\w@.+,=-]+)+|(?:[\w.-]+\/)+[\w.+,=-]+)(?:\.[A-Za-z0-9_+-]+)?/g) || [];
|
|
224
|
+
for (const raw of matches) {
|
|
225
|
+
const value = raw.replace(/['",.)]+$/g, "");
|
|
226
|
+
if (value.length > 5 && !value.startsWith("http") && value.includes("/")) addCounter(out, value, 1);
|
|
227
|
+
}
|
|
228
|
+
return out;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function eventPreview(text) {
|
|
232
|
+
return cleanTitle(String(text || "")).slice(0, 240);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function inferMcpTool(toolName) {
|
|
236
|
+
const value = String(toolName || "");
|
|
237
|
+
if (value.startsWith("mcp__")) {
|
|
238
|
+
const rest = value.slice(5);
|
|
239
|
+
const split = rest.indexOf("__");
|
|
240
|
+
if (split !== -1) return { server: rest.slice(0, split), tool: rest.slice(split + 2) };
|
|
241
|
+
const dot = rest.indexOf(".");
|
|
242
|
+
if (dot !== -1) return { server: rest.slice(0, dot), tool: rest.slice(dot + 1) };
|
|
243
|
+
}
|
|
244
|
+
if (value.startsWith("mcp_")) {
|
|
245
|
+
const rest = value.slice(4);
|
|
246
|
+
const split = rest.indexOf("_");
|
|
247
|
+
if (split !== -1) return { server: rest.slice(0, split), tool: rest.slice(split + 1) };
|
|
248
|
+
}
|
|
249
|
+
return { server: "", tool: "" };
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function codexTitle(id) {
|
|
253
|
+
const index = path.join(CODEX_DIR, "session_index.jsonl");
|
|
254
|
+
for (const record of readJsonl(index)) {
|
|
255
|
+
if (record.id === id && record.thread_name) return String(record.thread_name);
|
|
256
|
+
}
|
|
257
|
+
return "";
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function observedCodexTokens(payload) {
|
|
261
|
+
if (!payload || payload.type !== "token_count" || !payload.info) return 0;
|
|
262
|
+
const total = payload.info.total_token_usage;
|
|
263
|
+
if (total && Number(total.total_tokens)) return Number(total.total_tokens);
|
|
264
|
+
if (total && typeof total === "object") {
|
|
265
|
+
return ["input_tokens", "cached_input_tokens", "output_tokens", "reasoning_output_tokens"].reduce((sum, key) => sum + (Number(total[key]) || 0), 0);
|
|
266
|
+
}
|
|
267
|
+
const last = payload.info.last_token_usage;
|
|
268
|
+
if (last && Number(last.total_tokens)) return Number(last.total_tokens);
|
|
269
|
+
return 0;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
function claudeUsageTokens(usage) {
|
|
273
|
+
if (!usage || typeof usage !== "object") return 0;
|
|
274
|
+
return (Number(usage.input_tokens) || 0) + (Number(usage.output_tokens) || 0) + (Number(usage.cache_creation_input_tokens) || 0) + (Number(usage.cache_read_input_tokens) || 0);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function emptyUsage() {
|
|
278
|
+
return {
|
|
279
|
+
inputTokens: 0,
|
|
280
|
+
outputTokens: 0,
|
|
281
|
+
cacheCreationInputTokens: 0,
|
|
282
|
+
cacheReadInputTokens: 0,
|
|
283
|
+
cachedInputTokens: 0,
|
|
284
|
+
reasoningOutputTokens: 0,
|
|
285
|
+
totalTokens: 0,
|
|
286
|
+
turnsWithUsage: 0,
|
|
287
|
+
peakTurnTokens: 0,
|
|
288
|
+
peakTurnLabel: "",
|
|
289
|
+
latestTurnTokens: 0,
|
|
290
|
+
latestInputTokens: 0,
|
|
291
|
+
series: [],
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
function usageTotal(usage) {
|
|
296
|
+
if (!usage) return 0;
|
|
297
|
+
if (Number(usage.totalTokens)) return Number(usage.totalTokens) || 0;
|
|
298
|
+
return (Number(usage.inputTokens) || 0) +
|
|
299
|
+
(Number(usage.outputTokens) || 0) +
|
|
300
|
+
(Number(usage.cacheCreationInputTokens) || 0) +
|
|
301
|
+
(Number(usage.cacheReadInputTokens) || 0) +
|
|
302
|
+
(Number(usage.reasoningOutputTokens) || 0);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
function normalizedUsage(raw) {
|
|
306
|
+
if (!raw || typeof raw !== "object") return null;
|
|
307
|
+
const usage = {
|
|
308
|
+
inputTokens: Number(raw.input_tokens) || Number(raw.inputTokens) || 0,
|
|
309
|
+
outputTokens: Number(raw.output_tokens) || Number(raw.outputTokens) || 0,
|
|
310
|
+
cacheCreationInputTokens: Number(raw.cache_creation_input_tokens) || Number(raw.cacheCreationInputTokens) || 0,
|
|
311
|
+
cacheReadInputTokens: Number(raw.cache_read_input_tokens) || Number(raw.cacheReadInputTokens) || Number(raw.cached_input_tokens) || Number(raw.cachedInputTokens) || 0,
|
|
312
|
+
cachedInputTokens: Number(raw.cached_input_tokens) || Number(raw.cachedInputTokens) || 0,
|
|
313
|
+
reasoningOutputTokens: Number(raw.reasoning_output_tokens) || Number(raw.reasoningOutputTokens) || 0,
|
|
314
|
+
totalTokens: Number(raw.total_tokens) || Number(raw.totalTokens) || 0,
|
|
315
|
+
};
|
|
316
|
+
if (!usage.totalTokens) usage.totalTokens = usageTotal(usage);
|
|
317
|
+
return usage.totalTokens || usage.inputTokens || usage.outputTokens || usage.cacheCreationInputTokens || usage.cacheReadInputTokens ? usage : null;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
function codexUsageFromPayload(payload) {
|
|
321
|
+
if (!payload || payload.type !== "token_count" || !payload.info) return null;
|
|
322
|
+
return normalizedUsage(payload.info.last_token_usage || payload.info.total_token_usage);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
function addUsage(summary, usage, timestamp, label) {
|
|
326
|
+
if (!usage) return;
|
|
327
|
+
const total = usageTotal(usage);
|
|
328
|
+
summary.usage.inputTokens += usage.inputTokens || 0;
|
|
329
|
+
summary.usage.outputTokens += usage.outputTokens || 0;
|
|
330
|
+
summary.usage.cacheCreationInputTokens += usage.cacheCreationInputTokens || 0;
|
|
331
|
+
summary.usage.cacheReadInputTokens += usage.cacheReadInputTokens || 0;
|
|
332
|
+
summary.usage.cachedInputTokens += usage.cachedInputTokens || 0;
|
|
333
|
+
summary.usage.reasoningOutputTokens += usage.reasoningOutputTokens || 0;
|
|
334
|
+
summary.usage.totalTokens += total;
|
|
335
|
+
summary.usage.turnsWithUsage += 1;
|
|
336
|
+
summary.usage.latestTurnTokens = total;
|
|
337
|
+
summary.usage.latestInputTokens = (usage.inputTokens || 0) + (usage.cacheCreationInputTokens || 0) + (usage.cacheReadInputTokens || 0);
|
|
338
|
+
if (total > summary.usage.peakTurnTokens) {
|
|
339
|
+
summary.usage.peakTurnTokens = total;
|
|
340
|
+
summary.usage.peakTurnLabel = label || timestamp || "turn " + summary.usage.turnsWithUsage;
|
|
341
|
+
}
|
|
342
|
+
if (summary.usage.series.length < 260) {
|
|
343
|
+
summary.usage.series.push({
|
|
344
|
+
timestamp: String(timestamp || ""),
|
|
345
|
+
label: label || "turn " + summary.usage.turnsWithUsage,
|
|
346
|
+
totalTokens: total,
|
|
347
|
+
inputTokens: usage.inputTokens || 0,
|
|
348
|
+
outputTokens: usage.outputTokens || 0,
|
|
349
|
+
cacheCreationInputTokens: usage.cacheCreationInputTokens || 0,
|
|
350
|
+
cacheReadInputTokens: usage.cacheReadInputTokens || 0,
|
|
351
|
+
cachedInputTokens: usage.cachedInputTokens || 0,
|
|
352
|
+
reasoningOutputTokens: usage.reasoningOutputTokens || 0,
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
function sessionIdFromPath(file) {
|
|
358
|
+
const match = path.basename(file).match(SESSION_ID_RE);
|
|
359
|
+
return match ? match[0] : path.basename(file, ".jsonl");
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
function safeJson(value) {
|
|
363
|
+
if (!value || typeof value !== "string") return null;
|
|
364
|
+
const text = value.trim();
|
|
365
|
+
if (!text || !/^[{[]/.test(text)) return null;
|
|
366
|
+
try {
|
|
367
|
+
return JSON.parse(text);
|
|
368
|
+
} catch {
|
|
369
|
+
return null;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
function normalizeToolInput(value) {
|
|
374
|
+
if (value == null) return {};
|
|
375
|
+
if (typeof value === "string") {
|
|
376
|
+
const parsed = safeJson(value);
|
|
377
|
+
return parsed && typeof parsed === "object" ? parsed : { text: value };
|
|
378
|
+
}
|
|
379
|
+
if (typeof value === "object") return value;
|
|
380
|
+
return { text: String(value) };
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
function codexToolInput(payload) {
|
|
384
|
+
if (!payload || typeof payload !== "object") return {};
|
|
385
|
+
if (Object.prototype.hasOwnProperty.call(payload, "input")) return normalizeToolInput(payload.input);
|
|
386
|
+
if (Object.prototype.hasOwnProperty.call(payload, "arguments")) return normalizeToolInput(payload.arguments);
|
|
387
|
+
if (Object.prototype.hasOwnProperty.call(payload, "args")) return normalizeToolInput(payload.args);
|
|
388
|
+
if (Object.prototype.hasOwnProperty.call(payload, "parameters")) return normalizeToolInput(payload.parameters);
|
|
389
|
+
return {};
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
function firstStringField(input, keys) {
|
|
393
|
+
if (!input || typeof input !== "object") return "";
|
|
394
|
+
for (const key of keys) {
|
|
395
|
+
const value = input[key];
|
|
396
|
+
if (typeof value === "string" && value.trim()) return value.trim();
|
|
397
|
+
}
|
|
398
|
+
return "";
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
function shortCommand(command) {
|
|
402
|
+
return cleanTitle(command).slice(0, 260);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
function stripAnsi(value) {
|
|
406
|
+
return String(value || "").replace(/\x1b\[[0-9;?]*[ -/]*[@-~]/g, "");
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
function normalizeCommand(command) {
|
|
410
|
+
return stripAnsi(command)
|
|
411
|
+
.replace(/\/Users\/[^\s"']+/g, "<abs-path>")
|
|
412
|
+
.replace(/\/tmp\/[^\s"']+/g, "<tmp-path>")
|
|
413
|
+
.replace(/:\d+(:\d+)?/g, ":<line>")
|
|
414
|
+
.replace(/\b[0-9a-f]{8}-[0-9a-f-]{27,36}\b/gi, "<id>")
|
|
415
|
+
.replace(/\s+/g, " ")
|
|
416
|
+
.trim()
|
|
417
|
+
.slice(0, 260)
|
|
418
|
+
.toLowerCase();
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
function toolFamily(toolName, input, inputText) {
|
|
422
|
+
const name = String(toolName || "").toLowerCase();
|
|
423
|
+
if (inferMcpTool(toolName).server) return "mcp";
|
|
424
|
+
if (/(^|_|\.)(task|agent|subagent|spawn_agent|delegate)/.test(name)) return "agent";
|
|
425
|
+
if (/(read|view|open_file|get_file|cat_file)/.test(name)) return "read";
|
|
426
|
+
if (/(grep|glob|search|find|ripgrep|rg|list|ls)/.test(name)) return "search";
|
|
427
|
+
if (/(edit|write|patch|apply_patch|replace|update_file|create_file|delete|rm)/.test(name)) return "write";
|
|
428
|
+
if (/(bash|shell|exec|terminal|command|run)/.test(name)) return "execute";
|
|
429
|
+
const text = String(inputText || "").toLowerCase();
|
|
430
|
+
if (/^\s*(rg|grep|find|ls)\b/.test(text)) return "search";
|
|
431
|
+
if (/^\s*(cat|sed|nl|head|tail)\b/.test(text)) return "read";
|
|
432
|
+
if (/^\s*(python|node|pnpm|npm|yarn|bun|cargo|go|git|make|pytest|vitest)\b/.test(text)) return "execute";
|
|
433
|
+
return "tool";
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
function toolTarget(toolName, input, inputText) {
|
|
437
|
+
const direct = firstStringField(input, ["file_path", "filePath", "path", "filename", "relative_path", "target", "cwd"]);
|
|
438
|
+
if (direct) return direct;
|
|
439
|
+
const command = firstStringField(input, ["command", "cmd", "shell", "script"]);
|
|
440
|
+
if (command) return shortCommand(command);
|
|
441
|
+
const paths = pathCounts(inputText || "");
|
|
442
|
+
const firstPath = Object.keys(paths)[0];
|
|
443
|
+
if (firstPath) return firstPath;
|
|
444
|
+
return shortCommand(inputText || toolName || "");
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
function toolCommand(toolName, input, inputText) {
|
|
448
|
+
const command = firstStringField(input, ["command", "cmd", "shell", "script"]);
|
|
449
|
+
if (command) return shortCommand(command);
|
|
450
|
+
const name = String(toolName || "").toLowerCase();
|
|
451
|
+
if (/(bash|shell|exec|terminal|command|run)/.test(name)) return shortCommand(inputText || "");
|
|
452
|
+
return "";
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
function outputLooksError(value, text) {
|
|
456
|
+
if (!value || typeof value !== "object") {
|
|
457
|
+
return /\b(exit code|status|error)\s*[:=]?\s*[1-9]\b/i.test(text || "") || /\bfailed\b|\btraceback\b|\bexception\b/i.test(text || "");
|
|
458
|
+
}
|
|
459
|
+
if (value.is_error === true || value.error === true || value.success === false) return true;
|
|
460
|
+
const code = Number(value.exit_code ?? value.exitCode ?? value.status ?? value.code);
|
|
461
|
+
if (Number.isFinite(code) && code !== 0) return true;
|
|
462
|
+
return /\b(exit code|status)\s*[:=]?\s*[1-9]\b/i.test(text || "") || /\btraceback\b|\bexception\b/i.test(text || "");
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
function initTraceFields(summary) {
|
|
466
|
+
summary.usage = emptyUsage();
|
|
467
|
+
summary.steps = [];
|
|
468
|
+
summary.stepCount = 0;
|
|
469
|
+
summary._callMap = {};
|
|
470
|
+
summary._lastToolStep = null;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
function recordToolStep(summary, options) {
|
|
474
|
+
const input = normalizeToolInput(options.input);
|
|
475
|
+
const inputText = textFrom(input, 0) || compact(input);
|
|
476
|
+
const preview = eventPreview(inputText || options.preview || options.tool || "");
|
|
477
|
+
const family = toolFamily(options.tool, input, inputText || preview);
|
|
478
|
+
const command = toolCommand(options.tool, input, inputText || preview);
|
|
479
|
+
const mcp = inferMcpTool(options.tool);
|
|
480
|
+
const step = {
|
|
481
|
+
index: summary.stepCount++,
|
|
482
|
+
source: summary.source,
|
|
483
|
+
sessionId: summary.sessionId,
|
|
484
|
+
timestamp: String(options.timestamp || ""),
|
|
485
|
+
type: "tool_call",
|
|
486
|
+
tool: String(options.tool || "tool_call"),
|
|
487
|
+
family,
|
|
488
|
+
target: toolTarget(options.tool, input, inputText || preview),
|
|
489
|
+
command,
|
|
490
|
+
normalizedCommand: command ? normalizeCommand(command) : "",
|
|
491
|
+
mcpServer: mcp.server,
|
|
492
|
+
mcpTool: mcp.tool,
|
|
493
|
+
tokens: estimateTokens((inputText || preview).length),
|
|
494
|
+
preview,
|
|
495
|
+
isError: false,
|
|
496
|
+
errorPreview: "",
|
|
497
|
+
};
|
|
498
|
+
if (summary.steps.length < MAX_STEP_SAMPLES) summary.steps.push(step);
|
|
499
|
+
for (const id of options.ids || []) {
|
|
500
|
+
if (id) summary._callMap[String(id)] = step;
|
|
501
|
+
}
|
|
502
|
+
summary._lastToolStep = step;
|
|
503
|
+
return step;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
function markToolResult(summary, ids, isError, preview) {
|
|
507
|
+
let step = null;
|
|
508
|
+
for (const id of ids || []) {
|
|
509
|
+
if (id && summary._callMap[String(id)]) {
|
|
510
|
+
step = summary._callMap[String(id)];
|
|
511
|
+
break;
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
if (!step) step = summary._lastToolStep;
|
|
515
|
+
if (!step) return;
|
|
516
|
+
if (isError) {
|
|
517
|
+
step.isError = true;
|
|
518
|
+
step.errorPreview = eventPreview(preview || step.errorPreview || "");
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
function contentBlocks(content) {
|
|
523
|
+
if (Array.isArray(content)) return content;
|
|
524
|
+
if (content == null) return [];
|
|
525
|
+
return [content];
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
function classifyCodex(record) {
|
|
529
|
+
const top = String(record.type || "");
|
|
530
|
+
const payload = record.payload && typeof record.payload === "object" ? record.payload : {};
|
|
531
|
+
const ptype = String(payload.type || "");
|
|
532
|
+
let category = "other";
|
|
533
|
+
const tools = {};
|
|
534
|
+
let toolName = "";
|
|
535
|
+
if (top === "session_meta") category = "metadata";
|
|
536
|
+
else if (top === "turn_context") category = "instructions";
|
|
537
|
+
else if (top === "event_msg") category = ptype === "user_message" ? "user" : "metadata";
|
|
538
|
+
else if (top === "response_item") {
|
|
539
|
+
if (["function_call", "custom_tool_call", "web_search_call", "tool_search_call", "tool_call"].includes(ptype) || payload.name && payload.call_id) {
|
|
540
|
+
category = "tool_call";
|
|
541
|
+
toolName = payload.name ? String(payload.name) : ptype || "tool_call";
|
|
542
|
+
if (payload.name) addCounter(tools, String(payload.name), 1);
|
|
543
|
+
} else if (["function_call_output", "custom_tool_call_output", "tool_search_output", "tool_result"].includes(ptype) || Object.prototype.hasOwnProperty.call(payload, "output")) {
|
|
544
|
+
category = "tool_output";
|
|
545
|
+
toolName = payload.name ? String(payload.name) : "tool output";
|
|
546
|
+
}
|
|
547
|
+
else if (ptype === "reasoning" || payload.summary) category = "reasoning";
|
|
548
|
+
else if (payload.role === "assistant") category = "assistant";
|
|
549
|
+
else if (payload.role === "user" || payload.role === "developer") category = "user";
|
|
550
|
+
}
|
|
551
|
+
const text = textFrom(record, 0) || (category === "metadata" ? compact(record) : "");
|
|
552
|
+
return {
|
|
553
|
+
category,
|
|
554
|
+
chars: text.length,
|
|
555
|
+
tools,
|
|
556
|
+
paths: pathCounts(text),
|
|
557
|
+
toolName,
|
|
558
|
+
mcp: inferMcpTool(toolName),
|
|
559
|
+
metadataType: top + (ptype ? ":" + ptype : ""),
|
|
560
|
+
preview: eventPreview(text),
|
|
561
|
+
};
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
function classifyClaude(record) {
|
|
565
|
+
let category = "other";
|
|
566
|
+
const tools = {};
|
|
567
|
+
let toolName = "";
|
|
568
|
+
const message = record.message && typeof record.message === "object" ? record.message : null;
|
|
569
|
+
let text = "";
|
|
570
|
+
if (message) {
|
|
571
|
+
if (message.role === "user") category = "user";
|
|
572
|
+
else if (message.role === "assistant") category = "assistant";
|
|
573
|
+
const content = Array.isArray(message.content) ? message.content : [message.content];
|
|
574
|
+
const parts = [];
|
|
575
|
+
for (const part of content) {
|
|
576
|
+
if (part && typeof part === "object") {
|
|
577
|
+
if (part.type === "tool_use") {
|
|
578
|
+
category = "tool_call";
|
|
579
|
+
toolName = part.name ? String(part.name) : "tool_use";
|
|
580
|
+
if (part.name) addCounter(tools, String(part.name), 1);
|
|
581
|
+
} else if (part.type === "tool_result") {
|
|
582
|
+
category = "tool_output";
|
|
583
|
+
toolName = "tool_result";
|
|
584
|
+
}
|
|
585
|
+
else if (part.type === "thinking") category = "reasoning";
|
|
586
|
+
}
|
|
587
|
+
parts.push(textFrom(part, 0));
|
|
588
|
+
}
|
|
589
|
+
text = parts.join("\n");
|
|
590
|
+
} else if (record.toolUseResult) {
|
|
591
|
+
category = "tool_output";
|
|
592
|
+
toolName = "toolUseResult";
|
|
593
|
+
text = textFrom(record.toolUseResult, 0);
|
|
594
|
+
} else if (record.attachment) {
|
|
595
|
+
category = "attachment";
|
|
596
|
+
text = textFrom(record.attachment, 0);
|
|
597
|
+
} else {
|
|
598
|
+
text = textFrom(record, 0);
|
|
599
|
+
}
|
|
600
|
+
return {
|
|
601
|
+
category,
|
|
602
|
+
chars: text.length,
|
|
603
|
+
tools,
|
|
604
|
+
paths: pathCounts(text),
|
|
605
|
+
toolName,
|
|
606
|
+
mcp: inferMcpTool(toolName),
|
|
607
|
+
metadataType: String(record.type || message && message.role || "record"),
|
|
608
|
+
preview: eventPreview(text),
|
|
609
|
+
};
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
function summarizeCodex(file) {
|
|
613
|
+
const stat = fs.statSync(file);
|
|
614
|
+
const summary = {
|
|
615
|
+
source: "codex",
|
|
616
|
+
path: file,
|
|
617
|
+
sessionId: sessionIdFromPath(file),
|
|
618
|
+
title: "",
|
|
619
|
+
cwd: "",
|
|
620
|
+
startedAt: "",
|
|
621
|
+
updatedAt: "",
|
|
622
|
+
categories: {},
|
|
623
|
+
tools: {},
|
|
624
|
+
toolTokens: {},
|
|
625
|
+
paths: {},
|
|
626
|
+
metadata: {},
|
|
627
|
+
mcpUsage: {},
|
|
628
|
+
mcpTools: {},
|
|
629
|
+
toolEvents: [],
|
|
630
|
+
metadataEvents: [],
|
|
631
|
+
observedTokens: 0,
|
|
632
|
+
bytes: stat.size,
|
|
633
|
+
mtime: stat.mtimeMs,
|
|
634
|
+
};
|
|
635
|
+
initTraceFields(summary);
|
|
636
|
+
const records = readJsonl(file);
|
|
637
|
+
for (const record of records) {
|
|
638
|
+
const payload = record.payload && typeof record.payload === "object" ? record.payload : {};
|
|
639
|
+
if (record.type === "session_meta") {
|
|
640
|
+
summary.sessionId = String(payload.id || summary.sessionId);
|
|
641
|
+
summary.cwd = String(payload.cwd || summary.cwd);
|
|
642
|
+
summary.startedAt = String(payload.timestamp || summary.startedAt);
|
|
643
|
+
}
|
|
644
|
+
if (payload.cwd && !summary.cwd) summary.cwd = String(payload.cwd);
|
|
645
|
+
if (record.timestamp) summary.updatedAt = String(record.timestamp);
|
|
646
|
+
summary.observedTokens = Math.max(summary.observedTokens, observedCodexTokens(payload));
|
|
647
|
+
addUsage(summary, codexUsageFromPayload(payload), String(record.timestamp || payload.timestamp || ""), "turn " + (summary.usage.turnsWithUsage + 1));
|
|
648
|
+
const stats = classifyCodex(record);
|
|
649
|
+
addCounter(summary.categories, stats.category, stats.chars);
|
|
650
|
+
mergeCounter(summary.tools, stats.tools);
|
|
651
|
+
if (stats.toolName && stats.category === "tool_call") {
|
|
652
|
+
recordToolStep(summary, {
|
|
653
|
+
tool: stats.toolName,
|
|
654
|
+
input: codexToolInput(payload),
|
|
655
|
+
ids: [payload.call_id, payload.id],
|
|
656
|
+
timestamp: record.timestamp || payload.timestamp,
|
|
657
|
+
preview: stats.preview,
|
|
658
|
+
});
|
|
659
|
+
addCounter(summary.toolTokens, stats.toolName, estimateTokens(stats.chars));
|
|
660
|
+
if (stats.mcp.server) {
|
|
661
|
+
addCounter(summary.mcpUsage, stats.mcp.server, 1);
|
|
662
|
+
addCounter(summary.mcpTools, stats.mcp.server + " / " + (stats.mcp.tool || stats.toolName), 1);
|
|
663
|
+
}
|
|
664
|
+
if (summary.toolEvents.length < MAX_EVENT_SAMPLES) {
|
|
665
|
+
summary.toolEvents.push({
|
|
666
|
+
source: "codex",
|
|
667
|
+
sessionId: summary.sessionId,
|
|
668
|
+
timestamp: String(record.timestamp || payload.timestamp || ""),
|
|
669
|
+
category: stats.category,
|
|
670
|
+
tool: stats.toolName,
|
|
671
|
+
mcpServer: stats.mcp.server,
|
|
672
|
+
mcpTool: stats.mcp.tool,
|
|
673
|
+
tokens: estimateTokens(stats.chars),
|
|
674
|
+
preview: stats.preview,
|
|
675
|
+
});
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
if (stats.category === "tool_output") {
|
|
679
|
+
const text = textFrom(payload, 0);
|
|
680
|
+
markToolResult(summary, [payload.call_id, payload.id], outputLooksError(payload, text), text);
|
|
681
|
+
}
|
|
682
|
+
if (stats.category === "metadata") {
|
|
683
|
+
addCounter(summary.metadata, stats.metadataType, stats.chars);
|
|
684
|
+
if (summary.metadataEvents.length < MAX_EVENT_SAMPLES) {
|
|
685
|
+
summary.metadataEvents.push({
|
|
686
|
+
source: "codex",
|
|
687
|
+
sessionId: summary.sessionId,
|
|
688
|
+
timestamp: String(record.timestamp || payload.timestamp || ""),
|
|
689
|
+
type: stats.metadataType,
|
|
690
|
+
tokens: estimateTokens(stats.chars),
|
|
691
|
+
preview: stats.preview,
|
|
692
|
+
});
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
mergeCounter(summary.paths, stats.paths);
|
|
696
|
+
}
|
|
697
|
+
summary.title = codexTitle(summary.sessionId) || summary.sessionId;
|
|
698
|
+
return finalizeSummary(summary);
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
function summarizeClaude(file) {
|
|
702
|
+
const stat = fs.statSync(file);
|
|
703
|
+
const summary = {
|
|
704
|
+
source: "claude",
|
|
705
|
+
path: file,
|
|
706
|
+
sessionId: sessionIdFromPath(file),
|
|
707
|
+
title: "",
|
|
708
|
+
cwd: "",
|
|
709
|
+
startedAt: "",
|
|
710
|
+
updatedAt: "",
|
|
711
|
+
categories: {},
|
|
712
|
+
tools: {},
|
|
713
|
+
toolTokens: {},
|
|
714
|
+
paths: {},
|
|
715
|
+
metadata: {},
|
|
716
|
+
mcpUsage: {},
|
|
717
|
+
mcpTools: {},
|
|
718
|
+
toolEvents: [],
|
|
719
|
+
metadataEvents: [],
|
|
720
|
+
observedTokens: 0,
|
|
721
|
+
bytes: stat.size,
|
|
722
|
+
mtime: stat.mtimeMs,
|
|
723
|
+
};
|
|
724
|
+
initTraceFields(summary);
|
|
725
|
+
const records = readJsonl(file);
|
|
726
|
+
for (const record of records) {
|
|
727
|
+
summary.sessionId = String(record.sessionId || summary.sessionId);
|
|
728
|
+
summary.cwd = String(record.cwd || summary.cwd);
|
|
729
|
+
if (record.timestamp) {
|
|
730
|
+
summary.updatedAt = String(record.timestamp);
|
|
731
|
+
if (!summary.startedAt) summary.startedAt = String(record.timestamp);
|
|
732
|
+
}
|
|
733
|
+
if (record.message && typeof record.message === "object") {
|
|
734
|
+
summary.observedTokens = Math.max(summary.observedTokens, claudeUsageTokens(record.message.usage));
|
|
735
|
+
addUsage(summary, normalizedUsage(record.message.usage), String(record.timestamp || ""), "turn " + (summary.usage.turnsWithUsage + 1));
|
|
736
|
+
if (!summary.title && record.message.role === "user") summary.title = cleanTitle(textFrom(record.message, 0)).slice(0, 90);
|
|
737
|
+
for (const part of contentBlocks(record.message.content)) {
|
|
738
|
+
if (!part || typeof part !== "object") continue;
|
|
739
|
+
if (part.type === "tool_use") {
|
|
740
|
+
recordToolStep(summary, {
|
|
741
|
+
tool: part.name || "tool_use",
|
|
742
|
+
input: part.input || {},
|
|
743
|
+
ids: [part.id, record.uuid],
|
|
744
|
+
timestamp: record.timestamp,
|
|
745
|
+
preview: textFrom(part.input || part, 0),
|
|
746
|
+
});
|
|
747
|
+
} else if (part.type === "tool_result") {
|
|
748
|
+
const text = textFrom(part, 0);
|
|
749
|
+
markToolResult(summary, [part.tool_use_id, record.sourceToolAssistantUUID], part.is_error === true || outputLooksError(part, text), text);
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
if (record.toolUseResult) {
|
|
754
|
+
const text = textFrom(record.toolUseResult, 0);
|
|
755
|
+
markToolResult(summary, [record.toolUseID, record.toolUseId, record.sourceToolAssistantUUID], outputLooksError(record.toolUseResult, text), text);
|
|
756
|
+
}
|
|
757
|
+
const stats = classifyClaude(record);
|
|
758
|
+
addCounter(summary.categories, stats.category, stats.chars);
|
|
759
|
+
mergeCounter(summary.tools, stats.tools);
|
|
760
|
+
if (stats.toolName && stats.category === "tool_call") {
|
|
761
|
+
addCounter(summary.toolTokens, stats.toolName, estimateTokens(stats.chars));
|
|
762
|
+
if (stats.mcp.server) {
|
|
763
|
+
addCounter(summary.mcpUsage, stats.mcp.server, 1);
|
|
764
|
+
addCounter(summary.mcpTools, stats.mcp.server + " / " + (stats.mcp.tool || stats.toolName), 1);
|
|
765
|
+
}
|
|
766
|
+
if (summary.toolEvents.length < MAX_EVENT_SAMPLES) {
|
|
767
|
+
summary.toolEvents.push({
|
|
768
|
+
source: "claude",
|
|
769
|
+
sessionId: summary.sessionId,
|
|
770
|
+
timestamp: String(record.timestamp || ""),
|
|
771
|
+
category: stats.category,
|
|
772
|
+
tool: stats.toolName,
|
|
773
|
+
mcpServer: stats.mcp.server,
|
|
774
|
+
mcpTool: stats.mcp.tool,
|
|
775
|
+
tokens: estimateTokens(stats.chars),
|
|
776
|
+
preview: stats.preview,
|
|
777
|
+
});
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
if (stats.category === "metadata") {
|
|
781
|
+
addCounter(summary.metadata, stats.metadataType, stats.chars);
|
|
782
|
+
if (summary.metadataEvents.length < MAX_EVENT_SAMPLES) {
|
|
783
|
+
summary.metadataEvents.push({
|
|
784
|
+
source: "claude",
|
|
785
|
+
sessionId: summary.sessionId,
|
|
786
|
+
timestamp: String(record.timestamp || ""),
|
|
787
|
+
type: stats.metadataType,
|
|
788
|
+
tokens: estimateTokens(stats.chars),
|
|
789
|
+
preview: stats.preview,
|
|
790
|
+
});
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
mergeCounter(summary.paths, stats.paths);
|
|
794
|
+
}
|
|
795
|
+
if (!summary.title) summary.title = summary.sessionId;
|
|
796
|
+
return finalizeSummary(summary);
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
function finalizeSummary(summary) {
|
|
800
|
+
const totalChars = Object.values(summary.categories).reduce((sum, value) => sum + value, 0);
|
|
801
|
+
summary.totalChars = totalChars;
|
|
802
|
+
summary.tokens = summary.observedTokens || estimateTokens(totalChars);
|
|
803
|
+
summary.tokenMethod = summary.observedTokens ? "observed" : "estimated";
|
|
804
|
+
summary.totalSteps = summary.stepCount || (summary.steps || []).length;
|
|
805
|
+
delete summary._callMap;
|
|
806
|
+
delete summary._lastToolStep;
|
|
807
|
+
return summary;
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
function walk(root) {
|
|
811
|
+
const out = [];
|
|
812
|
+
if (!fs.existsSync(root)) return out;
|
|
813
|
+
const visit = (dir) => {
|
|
814
|
+
let entries = [];
|
|
815
|
+
try {
|
|
816
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
817
|
+
} catch {
|
|
818
|
+
return;
|
|
819
|
+
}
|
|
820
|
+
for (const entry of entries) {
|
|
821
|
+
const file = path.join(dir, entry.name);
|
|
822
|
+
if (entry.isDirectory()) visit(file);
|
|
823
|
+
else if (entry.isFile() && entry.name.endsWith(".jsonl")) out.push(file);
|
|
824
|
+
}
|
|
825
|
+
};
|
|
826
|
+
visit(root);
|
|
827
|
+
return out;
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
function encodedProject(project) {
|
|
831
|
+
return path.resolve(project).replace(/\//g, "-");
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
function pathInsideOrEqual(value, parent) {
|
|
835
|
+
const relative = path.relative(parent, value);
|
|
836
|
+
return relative === "" || (!!relative && !relative.startsWith("..") && !path.isAbsolute(relative));
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
function candidateFiles(source, args) {
|
|
840
|
+
if (args.sessionId) {
|
|
841
|
+
const roots = source === "codex" ? [path.join(CODEX_DIR, "sessions"), path.join(CODEX_DIR, "archived_sessions")] : [path.join(CLAUDE_DIR, "projects")];
|
|
842
|
+
return roots.flatMap(walk).filter((file) => file.includes(args.sessionId));
|
|
843
|
+
}
|
|
844
|
+
const since = parseSince(args.since);
|
|
845
|
+
const roots = source === "codex" ? [path.join(CODEX_DIR, "sessions"), path.join(CODEX_DIR, "archived_sessions")] : [path.join(CLAUDE_DIR, "projects")];
|
|
846
|
+
const projectFragment = encodedProject(args.project);
|
|
847
|
+
const files = roots.flatMap(walk).filter((file) => {
|
|
848
|
+
let stat;
|
|
849
|
+
try {
|
|
850
|
+
stat = fs.statSync(file);
|
|
851
|
+
} catch {
|
|
852
|
+
return false;
|
|
853
|
+
}
|
|
854
|
+
if (stat.mtimeMs < since) return false;
|
|
855
|
+
if (args.allProjects || source === "codex") return true;
|
|
856
|
+
return file.includes(projectFragment) || file.includes(path.basename(args.project));
|
|
857
|
+
}).sort((a, b) => fs.statSync(b).mtimeMs - fs.statSync(a).mtimeMs);
|
|
858
|
+
return source === "codex" && !args.allProjects ? files : files.slice(0, args.scanLimit);
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
function projectMatches(session, args) {
|
|
862
|
+
if (args.allProjects || args.sessionId) return true;
|
|
863
|
+
const project = path.resolve(args.project);
|
|
864
|
+
if (session.cwd && pathInsideOrEqual(path.resolve(session.cwd), project)) return true;
|
|
865
|
+
return session.path.includes(encodedProject(project));
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
function collectSessions(args) {
|
|
869
|
+
const sources = args.source === "both" ? ["codex", "claude"] : [args.source];
|
|
870
|
+
const sessions = [];
|
|
871
|
+
for (const source of sources) {
|
|
872
|
+
for (const file of candidateFiles(source, args)) {
|
|
873
|
+
try {
|
|
874
|
+
const summary = source === "codex" ? summarizeCodex(file) : summarizeClaude(file);
|
|
875
|
+
if (projectMatches(summary, args)) sessions.push(summary);
|
|
876
|
+
} catch {}
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
if (args.mode === "current" || args.sessionId) sessions.sort((a, b) => b.mtime - a.mtime);
|
|
880
|
+
else sessions.sort((a, b) => b.tokens - a.tokens || b.mtime - a.mtime);
|
|
881
|
+
return sessions.slice(0, args.last);
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
function aggregate(sessions) {
|
|
885
|
+
const out = { categories: {}, tools: {}, toolTokens: {}, paths: {}, metadata: {}, mcpUsage: {}, mcpTools: {}, toolEvents: [], metadataEvents: [], steps: [], usage: emptyUsage() };
|
|
886
|
+
for (const session of sessions) {
|
|
887
|
+
mergeCounter(out.categories, session.categories);
|
|
888
|
+
mergeCounter(out.tools, session.tools);
|
|
889
|
+
mergeCounter(out.toolTokens, session.toolTokens);
|
|
890
|
+
mergeCounter(out.paths, session.paths);
|
|
891
|
+
mergeCounter(out.metadata, session.metadata);
|
|
892
|
+
mergeCounter(out.mcpUsage, session.mcpUsage);
|
|
893
|
+
mergeCounter(out.mcpTools, session.mcpTools);
|
|
894
|
+
out.usage.inputTokens += (session.usage && session.usage.inputTokens) || 0;
|
|
895
|
+
out.usage.outputTokens += (session.usage && session.usage.outputTokens) || 0;
|
|
896
|
+
out.usage.cacheCreationInputTokens += (session.usage && session.usage.cacheCreationInputTokens) || 0;
|
|
897
|
+
out.usage.cacheReadInputTokens += (session.usage && session.usage.cacheReadInputTokens) || 0;
|
|
898
|
+
out.usage.cachedInputTokens += (session.usage && session.usage.cachedInputTokens) || 0;
|
|
899
|
+
out.usage.reasoningOutputTokens += (session.usage && session.usage.reasoningOutputTokens) || 0;
|
|
900
|
+
out.usage.totalTokens += (session.usage && session.usage.totalTokens) || 0;
|
|
901
|
+
out.usage.turnsWithUsage += (session.usage && session.usage.turnsWithUsage) || 0;
|
|
902
|
+
if (session.usage && session.usage.peakTurnTokens > out.usage.peakTurnTokens) {
|
|
903
|
+
out.usage.peakTurnTokens = session.usage.peakTurnTokens;
|
|
904
|
+
out.usage.peakTurnLabel = (session.title || session.sessionId) + " · " + session.usage.peakTurnLabel;
|
|
905
|
+
}
|
|
906
|
+
for (const step of session.steps || []) {
|
|
907
|
+
if (out.steps.length < 2000) out.steps.push(Object.assign({ sessionTitle: session.title, cwd: session.cwd, sessionPath: session.path }, step));
|
|
908
|
+
}
|
|
909
|
+
for (const event of session.toolEvents || []) {
|
|
910
|
+
if (out.toolEvents.length < 500) out.toolEvents.push(Object.assign({ sessionTitle: session.title, cwd: session.cwd, sessionPath: session.path }, event));
|
|
911
|
+
}
|
|
912
|
+
for (const event of session.metadataEvents || []) {
|
|
913
|
+
if (out.metadataEvents.length < 500) out.metadataEvents.push(Object.assign({ sessionTitle: session.title, cwd: session.cwd, sessionPath: session.path }, event));
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
return out;
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
function sortedEntries(counter, limit) {
|
|
920
|
+
return Object.entries(counter || {}).sort((a, b) => b[1] - a[1]).slice(0, limit);
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
function normalizedTarget(value) {
|
|
924
|
+
return String(value || "").replace(/^file:\/\//, "").replace(/:\d+(:\d+)?$/g, "").replace(/\/+/g, "/").trim();
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
function severity(score) {
|
|
928
|
+
if (score >= 90) return "critical";
|
|
929
|
+
if (score >= 70) return "high";
|
|
930
|
+
if (score >= 40) return "medium";
|
|
931
|
+
if (score >= 15) return "low";
|
|
932
|
+
return "info";
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
function addFinding(findings, rule, score, category, title, summary, evidence, recommendation, action) {
|
|
936
|
+
findings.push({
|
|
937
|
+
rule,
|
|
938
|
+
severity: severity(score),
|
|
939
|
+
score: Math.round(score),
|
|
940
|
+
category,
|
|
941
|
+
title,
|
|
942
|
+
summary,
|
|
943
|
+
evidence: (evidence || []).filter(Boolean).slice(0, 5),
|
|
944
|
+
recommendation,
|
|
945
|
+
action: action || "prompt",
|
|
946
|
+
confidence: score >= 70 ? "high" : score >= 40 ? "medium" : "low",
|
|
947
|
+
});
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
function cacheStability(session) {
|
|
951
|
+
const series = session.usage && session.usage.series || [];
|
|
952
|
+
if (series.length < 5) {
|
|
953
|
+
return { classification: "stable", turnsAboveThreshold: 0, totalTurns: series.length, avgCacheCreationPct: 0, perTurnRatios: [] };
|
|
954
|
+
}
|
|
955
|
+
const ratios = series.map((turn) => {
|
|
956
|
+
const total = (turn.inputTokens || 0) + (turn.cacheCreationInputTokens || 0) + (turn.cacheReadInputTokens || 0);
|
|
957
|
+
return total ? (turn.cacheCreationInputTokens || 0) / total : 0;
|
|
958
|
+
});
|
|
959
|
+
const turnsAboveThreshold = ratios.filter((ratio) => ratio > 0.3).length;
|
|
960
|
+
const mid = Math.floor(ratios.length / 2);
|
|
961
|
+
const first = ratios.slice(0, mid);
|
|
962
|
+
const second = ratios.slice(mid);
|
|
963
|
+
const avg = (items) => items.length ? items.reduce((sum, item) => sum + item, 0) / items.length : 0;
|
|
964
|
+
const firstAvg = avg(first);
|
|
965
|
+
const secondAvg = avg(second);
|
|
966
|
+
const classification = secondAvg > firstAvg && secondAvg > 0.15 ? "degrading" : turnsAboveThreshold > 5 ? "churning" : "stable";
|
|
967
|
+
return {
|
|
968
|
+
classification,
|
|
969
|
+
turnsAboveThreshold,
|
|
970
|
+
totalTurns: series.length,
|
|
971
|
+
avgCacheCreationPct: avg(ratios) * 100,
|
|
972
|
+
perTurnRatios: ratios.map((ratio) => Math.round(ratio * 1000) / 10),
|
|
973
|
+
};
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
function contextGrowth(session) {
|
|
977
|
+
const series = session.usage && session.usage.series || [];
|
|
978
|
+
const perTurnInput = series.map((turn) => (turn.inputTokens || 0) + (turn.cacheCreationInputTokens || 0) + (turn.cacheReadInputTokens || 0));
|
|
979
|
+
let growthFactor = 0;
|
|
980
|
+
let flagged = false;
|
|
981
|
+
if (perTurnInput.length > 5 && perTurnInput[4] > 0) {
|
|
982
|
+
growthFactor = perTurnInput[perTurnInput.length - 1] / perTurnInput[4];
|
|
983
|
+
flagged = growthFactor > 2;
|
|
984
|
+
}
|
|
985
|
+
let pressureWindows = 0;
|
|
986
|
+
let peakWindowAvg = 0;
|
|
987
|
+
if (perTurnInput.length >= PRESSURE_WINDOW) {
|
|
988
|
+
for (let i = 0; i <= perTurnInput.length - PRESSURE_WINDOW; i++) {
|
|
989
|
+
const window = perTurnInput.slice(i, i + PRESSURE_WINDOW);
|
|
990
|
+
const avg = window.reduce((sum, value) => sum + value, 0) / PRESSURE_WINDOW;
|
|
991
|
+
if (avg > PRESSURE_TOKENS) pressureWindows += 1;
|
|
992
|
+
if (avg > peakWindowAvg) peakWindowAvg = avg;
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
return {
|
|
996
|
+
flagged,
|
|
997
|
+
growthFactor: Math.round(growthFactor * 10) / 10,
|
|
998
|
+
perTurnInput: perTurnInput.slice(0, 260),
|
|
999
|
+
pressureWindows,
|
|
1000
|
+
peakWindowAvg: Math.round(peakWindowAvg),
|
|
1001
|
+
};
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
function duplicateReadGroups(sessions) {
|
|
1005
|
+
const groups = {};
|
|
1006
|
+
let duplicateCount = 0;
|
|
1007
|
+
for (const session of sessions) {
|
|
1008
|
+
const seen = {};
|
|
1009
|
+
const steps = (session.steps || []).slice().sort((a, b) => a.index - b.index);
|
|
1010
|
+
for (const step of steps) {
|
|
1011
|
+
const target = normalizedTarget(step.target);
|
|
1012
|
+
if (!target || !target.includes("/")) continue;
|
|
1013
|
+
if (step.family === "write") {
|
|
1014
|
+
delete seen[target];
|
|
1015
|
+
continue;
|
|
1016
|
+
}
|
|
1017
|
+
if (step.family !== "read") continue;
|
|
1018
|
+
if (!seen[target]) {
|
|
1019
|
+
seen[target] = step;
|
|
1020
|
+
continue;
|
|
1021
|
+
}
|
|
1022
|
+
duplicateCount += 1;
|
|
1023
|
+
const key = session.sessionId + "|" + target;
|
|
1024
|
+
if (!groups[key]) {
|
|
1025
|
+
groups[key] = {
|
|
1026
|
+
path: target,
|
|
1027
|
+
sessionId: session.sessionId,
|
|
1028
|
+
sessionTitle: session.title,
|
|
1029
|
+
count: 1,
|
|
1030
|
+
firstIndex: seen[target].index,
|
|
1031
|
+
repeated: [],
|
|
1032
|
+
};
|
|
1033
|
+
}
|
|
1034
|
+
groups[key].count += 1;
|
|
1035
|
+
groups[key].repeated.push(step.index);
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
return { duplicateCount, groups: Object.values(groups).sort((a, b) => b.count - a.count) };
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
function commandRetryGroups(sessions) {
|
|
1042
|
+
const retries = [];
|
|
1043
|
+
for (const session of sessions) {
|
|
1044
|
+
const steps = (session.steps || []).slice().sort((a, b) => a.index - b.index);
|
|
1045
|
+
let current = null;
|
|
1046
|
+
let streak = [];
|
|
1047
|
+
const flush = () => {
|
|
1048
|
+
if (current && streak.length >= 3) {
|
|
1049
|
+
retries.push({
|
|
1050
|
+
command: current,
|
|
1051
|
+
sessionId: session.sessionId,
|
|
1052
|
+
sessionTitle: session.title,
|
|
1053
|
+
count: streak.length,
|
|
1054
|
+
steps: streak.map((step) => step.index),
|
|
1055
|
+
});
|
|
1056
|
+
}
|
|
1057
|
+
};
|
|
1058
|
+
for (const step of steps) {
|
|
1059
|
+
const command = step.family === "execute" ? step.normalizedCommand : "";
|
|
1060
|
+
if (command && command === current) {
|
|
1061
|
+
streak.push(step);
|
|
1062
|
+
} else {
|
|
1063
|
+
flush();
|
|
1064
|
+
current = command || null;
|
|
1065
|
+
streak = command ? [step] : [];
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
flush();
|
|
1069
|
+
}
|
|
1070
|
+
return retries.sort((a, b) => b.count - a.count);
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
function failedToolLoops(sessions) {
|
|
1074
|
+
const loops = [];
|
|
1075
|
+
for (const session of sessions) {
|
|
1076
|
+
const steps = (session.steps || []).slice().sort((a, b) => a.index - b.index);
|
|
1077
|
+
let currentTool = "";
|
|
1078
|
+
let streak = [];
|
|
1079
|
+
const flush = () => {
|
|
1080
|
+
if (currentTool && streak.length >= 3) {
|
|
1081
|
+
loops.push({
|
|
1082
|
+
tool: currentTool,
|
|
1083
|
+
sessionId: session.sessionId,
|
|
1084
|
+
sessionTitle: session.title,
|
|
1085
|
+
count: streak.length,
|
|
1086
|
+
steps: streak.map((step) => step.index),
|
|
1087
|
+
sample: streak[0] && (streak[0].errorPreview || streak[0].preview),
|
|
1088
|
+
});
|
|
1089
|
+
}
|
|
1090
|
+
};
|
|
1091
|
+
for (const step of steps) {
|
|
1092
|
+
if (step.isError && step.tool === currentTool) {
|
|
1093
|
+
streak.push(step);
|
|
1094
|
+
} else {
|
|
1095
|
+
flush();
|
|
1096
|
+
currentTool = step.isError ? step.tool : "";
|
|
1097
|
+
streak = step.isError ? [step] : [];
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
flush();
|
|
1101
|
+
}
|
|
1102
|
+
return loops.sort((a, b) => b.count - a.count);
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
function dayKey(value) {
|
|
1106
|
+
const date = new Date(value || Date.now());
|
|
1107
|
+
if (!Number.isFinite(date.getTime())) return "unknown";
|
|
1108
|
+
return date.toISOString().slice(0, 10);
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
function analyzeContext(sessions, args, mcpServers) {
|
|
1112
|
+
const agg = aggregate(sessions);
|
|
1113
|
+
const steps = agg.steps || [];
|
|
1114
|
+
const familyCounts = {};
|
|
1115
|
+
let failedTools = 0;
|
|
1116
|
+
for (const step of steps) {
|
|
1117
|
+
addCounter(familyCounts, step.family || "tool", 1);
|
|
1118
|
+
if (step.isError) failedTools += 1;
|
|
1119
|
+
}
|
|
1120
|
+
const reads = familyCounts.read || 0;
|
|
1121
|
+
const searches = familyCounts.search || 0;
|
|
1122
|
+
const writes = familyCounts.write || 0;
|
|
1123
|
+
const executes = familyCounts.execute || 0;
|
|
1124
|
+
const agents = familyCounts.agent || 0;
|
|
1125
|
+
const mcpCalls = familyCounts.mcp || 0;
|
|
1126
|
+
const readSearch = reads + searches;
|
|
1127
|
+
const explorationRatio = writes ? readSearch / writes : readSearch;
|
|
1128
|
+
const usage = agg.usage || emptyUsage();
|
|
1129
|
+
const cacheDenom = (usage.inputTokens || 0) + (usage.cacheCreationInputTokens || 0) + (usage.cacheReadInputTokens || 0);
|
|
1130
|
+
const cacheHitRatio = cacheDenom ? (usage.cacheReadInputTokens || 0) / cacheDenom : 0;
|
|
1131
|
+
const cacheCreationPct = cacheDenom ? (usage.cacheCreationInputTokens || 0) / cacheDenom : 0;
|
|
1132
|
+
const duplicateReads = duplicateReadGroups(sessions);
|
|
1133
|
+
const retries = commandRetryGroups(sessions);
|
|
1134
|
+
const failureLoops = failedToolLoops(sessions);
|
|
1135
|
+
const cacheSessions = sessions.map((session) => Object.assign({ sessionId: session.sessionId, sessionTitle: session.title }, cacheStability(session)));
|
|
1136
|
+
const growthSessions = sessions.map((session) => Object.assign({ sessionId: session.sessionId, sessionTitle: session.title }, contextGrowth(session)));
|
|
1137
|
+
const churning = cacheSessions.filter((item) => item.classification !== "stable");
|
|
1138
|
+
const growing = growthSessions.filter((item) => item.flagged);
|
|
1139
|
+
const pressure = growthSessions.filter((item) => item.pressureWindows > 0);
|
|
1140
|
+
const byDay = {};
|
|
1141
|
+
for (const session of sessions) {
|
|
1142
|
+
const key = dayKey(session.updatedAt || session.startedAt || session.mtime);
|
|
1143
|
+
if (!byDay[key]) byDay[key] = { day: key, sessions: 0, tokens: 0, observed: 0, codex: 0, claude: 0 };
|
|
1144
|
+
byDay[key].sessions += 1;
|
|
1145
|
+
byDay[key].tokens += session.tokens || 0;
|
|
1146
|
+
if (session.tokenMethod === "observed") byDay[key].observed += 1;
|
|
1147
|
+
byDay[key][session.source] = (byDay[key][session.source] || 0) + 1;
|
|
1148
|
+
}
|
|
1149
|
+
const categoryTotal = Object.values(agg.categories).reduce((sum, value) => sum + value, 0);
|
|
1150
|
+
const toolOutputPct = pct(agg.categories.tool_output || 0, categoryTotal);
|
|
1151
|
+
const metadataPct = pct(agg.categories.metadata || 0, categoryTotal);
|
|
1152
|
+
const assistantPct = pct(agg.categories.assistant || 0, categoryTotal);
|
|
1153
|
+
const maxSession = sessions.length ? sessions.reduce((a, b) => a.tokens > b.tokens ? a : b) : null;
|
|
1154
|
+
const failureRate = steps.length ? failedTools / steps.length : 0;
|
|
1155
|
+
const findings = [];
|
|
1156
|
+
if (duplicateReads.duplicateCount > 0) {
|
|
1157
|
+
addFinding(findings, "duplicate_read", Math.min(86, 35 + duplicateReads.duplicateCount * 6), "context", "Repeated file reads", duplicateReads.duplicateCount + " file reads repeated without an intervening edit/write.", duplicateReads.groups.slice(0, 4).map((group) => group.path + " read " + group.count + "x in " + group.sessionTitle), "Ask the agent to keep a short file-role note after the first read and reopen the file only when it needs exact line numbers.", "prompt");
|
|
1158
|
+
}
|
|
1159
|
+
if (retries.length) {
|
|
1160
|
+
addFinding(findings, "command_retry_loop", 78, "loop", "Repeated command loop", "One or more shell commands ran 3+ times in a row.", retries.slice(0, 4).map((retry) => retry.command + " · " + retry.count + "x in " + retry.sessionTitle), "Ask the agent to stop after two identical failures, summarize the error, and change strategy before rerunning the command.", "prompt");
|
|
1161
|
+
}
|
|
1162
|
+
if (failureLoops.length || (failedTools >= 3 && failureRate > 0.12)) {
|
|
1163
|
+
addFinding(findings, "failed_tool_loop", failureLoops.length ? 82 : 55, "loop", "Tool failures need a recovery plan", failedTools + " failed tool calls detected across " + steps.length + " normalized tool steps.", (failureLoops.length ? failureLoops : steps.filter((step) => step.isError).slice(0, 4)).map((item) => (item.tool || item.title || "tool") + " · " + (item.count || "failed") + " · " + (item.sessionTitle || item.sessionId || "")), "Tell the agent to diagnose the first failure, propose the next attempt, and avoid repeating the same tool call unchanged.", "prompt");
|
|
1164
|
+
}
|
|
1165
|
+
if (explorationRatio > 5 && readSearch >= 10) {
|
|
1166
|
+
addFinding(findings, "exploration_ratio", Math.min(80, 35 + explorationRatio * 5), "workflow", "Exploration outweighs edits", "Read/search calls are " + explorationRatio.toFixed(1) + "x edit/write calls.", ["Reads/searches: " + readSearch, "Writes/edits: " + writes, "Execute calls: " + executes], "Give the agent a concrete inspection budget, then ask for a short implementation plan before more reading.", "prompt");
|
|
1167
|
+
}
|
|
1168
|
+
if (agents > 3) {
|
|
1169
|
+
addFinding(findings, "subagent_sprawl", Math.min(72, 35 + agents * 6), "workflow", "Subagent usage is broad", agents + " agent/delegation tool calls were detected.", ["Agent-family calls: " + agents], "Ask for a coordination summary after subagents finish: decisions, files touched, and what should stay in the main thread.", "prompt");
|
|
1170
|
+
}
|
|
1171
|
+
if (churning.length) {
|
|
1172
|
+
addFinding(findings, "cache_churn", churning.some((item) => item.classification === "churning") ? 78 : 62, "cache", "Cache is churning or degrading", churning.length + " session(s) had sustained or rising cache creation.", churning.slice(0, 4).map((item) => item.sessionTitle + " · " + item.classification + " · avg create " + item.avgCacheCreationPct.toFixed(0) + "%"), "Move stable instructions into a skill/repo doc and start long follow-up work from a compact handoff so cache writes settle earlier.", "workflow");
|
|
1173
|
+
}
|
|
1174
|
+
if (growing.length) {
|
|
1175
|
+
addFinding(findings, "context_growth", 74, "context", "Context keeps growing late in the session", growing.length + " session(s) grew >2x from turn 5 to the final observed turn.", growing.slice(0, 4).map((item) => item.sessionTitle + " · " + item.growthFactor + "x growth"), "After a milestone, ask for a handoff summary and continue in a fresh thread rather than carrying the full transcript forward.", "workflow");
|
|
1176
|
+
}
|
|
1177
|
+
if (pressure.length) {
|
|
1178
|
+
addFinding(findings, "context_pressure", 70, "context", "High context pressure windows", pressure.length + " session(s) crossed a " + fmtTokens(PRESSURE_TOKENS) + " five-turn average input window.", pressure.slice(0, 4).map((item) => item.sessionTitle + " · peak avg " + fmtTokens(item.peakWindowAvg)), "Use a context reset before the next implementation phase and preserve only decisions, current files, and known failing commands.", "workflow");
|
|
1179
|
+
}
|
|
1180
|
+
if (toolOutputPct > 45) {
|
|
1181
|
+
addFinding(findings, "tool_output_heavy", Math.min(76, 35 + toolOutputPct), "context", "Tool output dominates the window", toolOutputPct.toFixed(0) + "% of transcript text came from tool output.", sortedEntries(agg.tools, 4).map((entry) => entry[0] + " x" + entry[1]), "Ask the agent to cap logs, request targeted excerpts, and summarize failing output unless exact lines are needed.", "prompt");
|
|
1182
|
+
}
|
|
1183
|
+
if (metadataPct > 18) {
|
|
1184
|
+
addFinding(findings, "metadata_pressure", Math.min(64, 25 + metadataPct), "context", "Metadata is a visible share", metadataPct.toFixed(0) + "% of local transcript text was metadata or protocol state.", sortedEntries(agg.metadata, 4).map((entry) => entry[0] + " · about " + fmtTokens(estimateTokens(entry[1]))), "Keep the report in drilldown mode: inspect metadata when diagnosing protocol overhead, but optimize prompts around user/tool/output buckets first.", "workflow");
|
|
1185
|
+
}
|
|
1186
|
+
if (assistantPct > 45) {
|
|
1187
|
+
addFinding(findings, "assistant_prose", Math.min(68, 25 + assistantPct), "workflow", "Assistant narration is heavy", assistantPct.toFixed(0) + "% of transcript text came from assistant prose.", ["Assistant bucket: about " + fmtTokens(estimateTokens(agg.categories.assistant || 0))], "Ask for concise progress notes and a final decision log, with detailed rationale moved into docs only when useful.", "prompt");
|
|
1188
|
+
}
|
|
1189
|
+
if (maxSession && maxSession.tokens > 80000) {
|
|
1190
|
+
addFinding(findings, "large_session", Math.min(82, 42 + maxSession.tokens / 5000), "context", "One session is carrying a lot", "Largest session is about " + fmtTokens(maxSession.tokens) + " " + maxSession.tokenMethod + " tokens.", [maxSession.title + " · " + maxSession.source + " · " + (maxSession.cwd || maxSession.path)], "Start the next large change with a compact handoff summary instead of continuing the whole thread.", "workflow");
|
|
1191
|
+
}
|
|
1192
|
+
if ((mcpServers || []).length > 12 && mcpCalls < 3) {
|
|
1193
|
+
addFinding(findings, "mcp_surface", 28, "mcp", "Configured MCP surface is broad", (mcpServers || []).length + " MCP servers are configured, but only " + mcpCalls + " MCP-style calls were detected.", (mcpServers || []).slice(0, 5).map((server) => server.source + " · " + server.name), "For focused CLI work, keep rarely used MCP servers disabled or project-scoped so tool lists stay easier to scan.", "configuration");
|
|
1194
|
+
}
|
|
1195
|
+
let score = 100;
|
|
1196
|
+
score -= Math.min(18, duplicateReads.duplicateCount * 2);
|
|
1197
|
+
score -= retries.length ? 14 : 0;
|
|
1198
|
+
score -= failureLoops.length ? 16 : Math.min(12, Math.round(failureRate * 60));
|
|
1199
|
+
score -= explorationRatio > 5 ? Math.min(15, Math.round((explorationRatio - 5) * 2)) : 0;
|
|
1200
|
+
score -= churning.length ? 14 : 0;
|
|
1201
|
+
score -= growing.length ? 12 : 0;
|
|
1202
|
+
score -= pressure.length ? 10 : 0;
|
|
1203
|
+
score -= toolOutputPct > 45 ? 8 : 0;
|
|
1204
|
+
score = Math.max(0, Math.min(100, Math.round(score)));
|
|
1205
|
+
findings.sort((a, b) => b.score - a.score);
|
|
1206
|
+
return {
|
|
1207
|
+
score,
|
|
1208
|
+
scoreLabel: score >= 85 ? "healthy" : score >= 70 ? "watch" : score >= 50 ? "strained" : "critical",
|
|
1209
|
+
metrics: {
|
|
1210
|
+
normalizedSteps: steps.length,
|
|
1211
|
+
failedTools,
|
|
1212
|
+
failureRate,
|
|
1213
|
+
successRate: steps.length ? 1 - failureRate : 1,
|
|
1214
|
+
familyCounts,
|
|
1215
|
+
reads,
|
|
1216
|
+
searches,
|
|
1217
|
+
writes,
|
|
1218
|
+
executes,
|
|
1219
|
+
agents,
|
|
1220
|
+
mcpCalls,
|
|
1221
|
+
explorationRatio: Math.round(explorationRatio * 10) / 10,
|
|
1222
|
+
duplicateReadCount: duplicateReads.duplicateCount,
|
|
1223
|
+
retryLoopCount: retries.length,
|
|
1224
|
+
failureLoopCount: failureLoops.length,
|
|
1225
|
+
cacheHitRatio,
|
|
1226
|
+
cacheCreationPct,
|
|
1227
|
+
tokenUsage: usage,
|
|
1228
|
+
observedSessionCoverage: sessions.length ? sessions.filter((session) => session.tokenMethod === "observed").length / sessions.length : 0,
|
|
1229
|
+
toolOutputPct,
|
|
1230
|
+
metadataPct,
|
|
1231
|
+
assistantPct,
|
|
1232
|
+
},
|
|
1233
|
+
findings,
|
|
1234
|
+
evidence: {
|
|
1235
|
+
duplicateReads: duplicateReads.groups.slice(0, 20),
|
|
1236
|
+
commandRetries: retries.slice(0, 20),
|
|
1237
|
+
failureLoops: failureLoops.slice(0, 20),
|
|
1238
|
+
cacheStability: cacheSessions,
|
|
1239
|
+
contextGrowth: growthSessions,
|
|
1240
|
+
},
|
|
1241
|
+
trends: {
|
|
1242
|
+
byDay: Object.values(byDay).sort((a, b) => a.day.localeCompare(b.day)),
|
|
1243
|
+
topTools: sortedEntries(agg.tools, 12),
|
|
1244
|
+
topPaths: sortedEntries(agg.paths, 12),
|
|
1245
|
+
topMetadata: sortedEntries(agg.metadata, 12),
|
|
1246
|
+
},
|
|
1247
|
+
sourceModel: {
|
|
1248
|
+
sourceProjectSession: sessions.map((session) => ({
|
|
1249
|
+
source: session.source,
|
|
1250
|
+
project: session.cwd || args.project,
|
|
1251
|
+
sessionId: session.sessionId,
|
|
1252
|
+
title: session.title,
|
|
1253
|
+
tokens: session.tokens,
|
|
1254
|
+
updatedAt: session.updatedAt,
|
|
1255
|
+
tokenMethod: session.tokenMethod,
|
|
1256
|
+
steps: session.totalSteps || 0,
|
|
1257
|
+
})),
|
|
1258
|
+
privacy: "Metadata-first: the report stores counters, tool names, paths, short previews, and line offsets where available; transcript bodies stay local.",
|
|
1259
|
+
},
|
|
1260
|
+
};
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
function recommendations(sessions, analysis) {
|
|
1264
|
+
if (!sessions.length) return ["No matching sessions found. Try context-xray threads --all-projects --since 2w --open."];
|
|
1265
|
+
if (analysis && analysis.findings && analysis.findings.length) {
|
|
1266
|
+
const seen = {};
|
|
1267
|
+
const fromFindings = [];
|
|
1268
|
+
for (const finding of analysis.findings) {
|
|
1269
|
+
if (!finding.recommendation || seen[finding.recommendation]) continue;
|
|
1270
|
+
seen[finding.recommendation] = true;
|
|
1271
|
+
fromFindings.push(finding.recommendation);
|
|
1272
|
+
}
|
|
1273
|
+
if (fromFindings.length) return fromFindings.slice(0, 6);
|
|
1274
|
+
}
|
|
1275
|
+
const agg = aggregate(sessions);
|
|
1276
|
+
const total = Object.values(agg.categories).reduce((sum, value) => sum + value, 0);
|
|
1277
|
+
const tips = [];
|
|
1278
|
+
const toolOutput = pct(agg.categories.tool_output || 0, total);
|
|
1279
|
+
const instructions = pct(agg.categories.instructions || 0, total);
|
|
1280
|
+
const assistant = pct(agg.categories.assistant || 0, total);
|
|
1281
|
+
const maxSession = sessions.reduce((a, b) => a.tokens > b.tokens ? a : b);
|
|
1282
|
+
if (toolOutput > 45) tips.push("Tool output dominates context. In your prompt, ask the agent to keep command output capped, summarize failures, and only expand logs when the exact lines matter.");
|
|
1283
|
+
if (instructions > 25) tips.push("Instructions are a large share. Move durable workflow preferences into a skill, AGENTS.md, or CLAUDE.md so each thread can start with a shorter task prompt.");
|
|
1284
|
+
if (assistant > 45) tips.push("Assistant prose is heavy. Ask for brief progress updates and a final decision log, instead of detailed narration during every loop.");
|
|
1285
|
+
if (maxSession.tokens > 80000) tips.push("The largest session is about " + fmtTokens(maxSession.tokens) + " " + maxSession.tokenMethod + " tokens. Ask for a handoff summary, then continue in a fresh thread before the next large implementation pass.");
|
|
1286
|
+
const topTool = sortedEntries(agg.tools, 1)[0];
|
|
1287
|
+
if (topTool && topTool[1] > 20) tips.push(topTool[0] + " appears " + topTool[1] + " times. Tell the agent to batch independent inspection and avoid rerunning diagnostics unless state changed.");
|
|
1288
|
+
const topPath = sortedEntries(agg.paths, 1)[0];
|
|
1289
|
+
if (topPath && topPath[1] > 12) tips.push(topPath[0] + " appears repeatedly (" + topPath[1] + " mentions). Ask the agent to keep a short file-role summary and reopen the file only when it needs exact lines.");
|
|
1290
|
+
return tips.length ? tips.slice(0, 6) : ["Recent sessions look balanced. Keep giving scoped tasks, ask for compaction after milestones, and preserve reusable decisions in a skill or repo doc."];
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1293
|
+
function escapeHtml(value) {
|
|
1294
|
+
return String(value || "").replace(/[&<>"']/g, (ch) => ({ "&": "&", "<": "<", ">": ">", "\"": """, "'": "'" }[ch]));
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1297
|
+
function cleanTitle(value) {
|
|
1298
|
+
return String(value || "").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
|
|
1299
|
+
}
|
|
1300
|
+
|
|
1301
|
+
function categoryBar(categories) {
|
|
1302
|
+
const total = Object.values(categories || {}).reduce((sum, value) => sum + value, 0);
|
|
1303
|
+
if (!total) return "<div class=\"bar\"></div>";
|
|
1304
|
+
return "<div class=\"bar\">" + CATEGORIES.map((cat) => {
|
|
1305
|
+
const value = categories[cat] || 0;
|
|
1306
|
+
if (!value) return "";
|
|
1307
|
+
const width = Math.max(1, pct(value, total));
|
|
1308
|
+
return "<span title=\"" + escapeHtml(LABELS[cat]) + "\" style=\"width:" + width.toFixed(2) + "%;background:" + COLORS[cat] + "\"></span>";
|
|
1309
|
+
}).join("") + "</div>";
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1312
|
+
function readJsonFile(file) {
|
|
1313
|
+
try {
|
|
1314
|
+
return JSON.parse(fs.readFileSync(file, "utf8"));
|
|
1315
|
+
} catch {
|
|
1316
|
+
return null;
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1320
|
+
function safeUrl(value) {
|
|
1321
|
+
try {
|
|
1322
|
+
const url = new URL(String(value || ""));
|
|
1323
|
+
return url.origin + url.pathname;
|
|
1324
|
+
} catch {
|
|
1325
|
+
return String(value || "").split(/[?#]/)[0].slice(0, 120);
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
function mcpTarget(definition) {
|
|
1330
|
+
if (!definition || typeof definition !== "object") return "configured";
|
|
1331
|
+
if (definition.url) return safeUrl(definition.url);
|
|
1332
|
+
if (definition.command) return String(definition.command);
|
|
1333
|
+
if (definition.transport) return String(definition.transport);
|
|
1334
|
+
return "configured";
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1337
|
+
function addMcpServer(out, name, source, definition) {
|
|
1338
|
+
if (!name) return;
|
|
1339
|
+
const key = source + ":" + name;
|
|
1340
|
+
if (out.some((server) => server.key === key)) return;
|
|
1341
|
+
out.push({
|
|
1342
|
+
key,
|
|
1343
|
+
name: String(name),
|
|
1344
|
+
source,
|
|
1345
|
+
target: mcpTarget(definition),
|
|
1346
|
+
});
|
|
1347
|
+
}
|
|
1348
|
+
|
|
1349
|
+
function readMcpJson(file, source, out) {
|
|
1350
|
+
const json = readJsonFile(file);
|
|
1351
|
+
if (!json || typeof json !== "object") return;
|
|
1352
|
+
const servers = json.mcpServers || json.mcp_servers || json.servers;
|
|
1353
|
+
if (!servers || typeof servers !== "object") return;
|
|
1354
|
+
for (const name of Object.keys(servers)) addMcpServer(out, name, source, servers[name]);
|
|
1355
|
+
}
|
|
1356
|
+
|
|
1357
|
+
function readCodexTomlServers(file, out) {
|
|
1358
|
+
let text = "";
|
|
1359
|
+
try {
|
|
1360
|
+
text = fs.readFileSync(file, "utf8");
|
|
1361
|
+
} catch {
|
|
1362
|
+
return;
|
|
1363
|
+
}
|
|
1364
|
+
const lines = text.split(/\r?\n/);
|
|
1365
|
+
for (let index = 0; index < lines.length; index++) {
|
|
1366
|
+
const match = lines[index].match(/^\s*\[mcp_servers\.(?:"([^"]+)"|([^\]]+))\]\s*$/);
|
|
1367
|
+
if (!match) continue;
|
|
1368
|
+
const name = match[1] || match[2] || "";
|
|
1369
|
+
const section = {};
|
|
1370
|
+
for (let cursor = index + 1; cursor < lines.length; cursor++) {
|
|
1371
|
+
if (/^\s*\[/.test(lines[cursor])) break;
|
|
1372
|
+
const pair = lines[cursor].match(/^\s*([A-Za-z0-9_-]+)\s*=\s*"?([^"]+)"?\s*$/);
|
|
1373
|
+
if (pair) section[pair[1]] = pair[2];
|
|
1374
|
+
}
|
|
1375
|
+
addMcpServer(out, name, "Codex config", section);
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
|
|
1379
|
+
function readMcpServers(project) {
|
|
1380
|
+
const out = [];
|
|
1381
|
+
readCodexTomlServers(path.join(CODEX_DIR, "config.toml"), out);
|
|
1382
|
+
readMcpJson(path.join(HOME, ".claude.json"), "Claude user config", out);
|
|
1383
|
+
readMcpJson(path.join(CLAUDE_DIR, "settings.json"), "Claude settings", out);
|
|
1384
|
+
readMcpJson(path.join(path.resolve(project), ".mcp.json"), "Project .mcp.json", out);
|
|
1385
|
+
readMcpJson(path.join(path.resolve(project), ".cursor", "mcp.json"), "Project Cursor MCP", out);
|
|
1386
|
+
readMcpJson(path.join(HOME, ".cowork", "mcp.json"), "Cowork MCP", out);
|
|
1387
|
+
return out.sort((a, b) => a.name.localeCompare(b.name) || a.source.localeCompare(b.source));
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1390
|
+
function sourceCounts(sessions) {
|
|
1391
|
+
return sessions.reduce((counts, session) => {
|
|
1392
|
+
counts[session.source] = (counts[session.source] || 0) + 1;
|
|
1393
|
+
return counts;
|
|
1394
|
+
}, {});
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1397
|
+
function buildReport(sessions, args) {
|
|
1398
|
+
const agg = aggregate(sessions);
|
|
1399
|
+
const mcpServers = readMcpServers(args.project);
|
|
1400
|
+
const analysis = analyzeContext(sessions, args, mcpServers);
|
|
1401
|
+
return {
|
|
1402
|
+
generatedAt: new Date().toISOString(),
|
|
1403
|
+
generatedLabel: new Date().toLocaleString(),
|
|
1404
|
+
mode: args.mode,
|
|
1405
|
+
source: args.source,
|
|
1406
|
+
since: args.since,
|
|
1407
|
+
project: path.resolve(args.project),
|
|
1408
|
+
sessionCount: sessions.length,
|
|
1409
|
+
sourceCounts: sourceCounts(sessions),
|
|
1410
|
+
totalTokens: sessions.reduce((sum, s) => sum + s.tokens, 0),
|
|
1411
|
+
categories: agg.categories,
|
|
1412
|
+
tools: sortedEntries(agg.tools, 50),
|
|
1413
|
+
toolTokens: sortedEntries(agg.toolTokens, 50),
|
|
1414
|
+
paths: sortedEntries(agg.paths, 50),
|
|
1415
|
+
metadata: sortedEntries(agg.metadata, 50),
|
|
1416
|
+
mcpUsage: sortedEntries(agg.mcpUsage, 50),
|
|
1417
|
+
mcpTools: sortedEntries(agg.mcpTools, 50),
|
|
1418
|
+
mcpServers,
|
|
1419
|
+
analysis,
|
|
1420
|
+
recommendations: recommendations(sessions, analysis),
|
|
1421
|
+
toolEvents: agg.toolEvents,
|
|
1422
|
+
metadataEvents: agg.metadataEvents,
|
|
1423
|
+
steps: agg.steps,
|
|
1424
|
+
sessions,
|
|
1425
|
+
};
|
|
1426
|
+
}
|
|
1427
|
+
|
|
1428
|
+
function jsonScript(value) {
|
|
1429
|
+
return JSON.stringify(value).replace(/</g, "\\u003c");
|
|
1430
|
+
}
|
|
1431
|
+
|
|
1432
|
+
function overviewEnhancementScript() {
|
|
1433
|
+
return "(" + function () {
|
|
1434
|
+
const dataEl = document.getElementById("xray-data");
|
|
1435
|
+
if (!dataEl) return;
|
|
1436
|
+
const report = JSON.parse(dataEl.textContent || "{}");
|
|
1437
|
+
const cats = ["user", "assistant", "tool_call", "tool_output", "reasoning", "instructions", "attachment", "metadata", "other"];
|
|
1438
|
+
const labels = {
|
|
1439
|
+
user: "User asks",
|
|
1440
|
+
assistant: "Assistant text",
|
|
1441
|
+
tool_call: "Tool calls",
|
|
1442
|
+
tool_output: "Tool output",
|
|
1443
|
+
reasoning: "Reasoning",
|
|
1444
|
+
instructions: "Instructions/context",
|
|
1445
|
+
attachment: "Attachments",
|
|
1446
|
+
metadata: "Metadata",
|
|
1447
|
+
other: "Other",
|
|
1448
|
+
};
|
|
1449
|
+
const colors = {
|
|
1450
|
+
user: "#8ba8ff",
|
|
1451
|
+
assistant: "#55b982",
|
|
1452
|
+
tool_call: "#f0a85b",
|
|
1453
|
+
tool_output: "#e06b73",
|
|
1454
|
+
reasoning: "#a77be8",
|
|
1455
|
+
instructions: "#6ac3d5",
|
|
1456
|
+
attachment: "#d6a85a",
|
|
1457
|
+
metadata: "#9aa3ad",
|
|
1458
|
+
other: "#c3c8ce",
|
|
1459
|
+
};
|
|
1460
|
+
|
|
1461
|
+
function esc(value) {
|
|
1462
|
+
return String(value || "").replace(/[&<>"']/g, function (ch) {
|
|
1463
|
+
return { "&": "&", "<": "<", ">": ">", "\"": """, "'": "'" }[ch];
|
|
1464
|
+
});
|
|
1465
|
+
}
|
|
1466
|
+
|
|
1467
|
+
function fmt(value) {
|
|
1468
|
+
const number = Number(value) || 0;
|
|
1469
|
+
if (number >= 1000000) return (number / 1000000).toFixed(1) + "m";
|
|
1470
|
+
if (number >= 1000) return (number / 1000).toFixed(1) + "k";
|
|
1471
|
+
return String(number);
|
|
1472
|
+
}
|
|
1473
|
+
|
|
1474
|
+
function tok(chars) {
|
|
1475
|
+
chars = Number(chars) || 0;
|
|
1476
|
+
return chars > 0 ? Math.max(1, Math.ceil(chars / 4)) : 0;
|
|
1477
|
+
}
|
|
1478
|
+
|
|
1479
|
+
function totalCounter(counter) {
|
|
1480
|
+
return Object.keys(counter || {}).reduce(function (sum, key) {
|
|
1481
|
+
return sum + (Number(counter[key]) || 0);
|
|
1482
|
+
}, 0);
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1485
|
+
function pct(part, total) {
|
|
1486
|
+
return total > 0 ? Math.max(0, Math.min(100, (part / total) * 100)) : 0;
|
|
1487
|
+
}
|
|
1488
|
+
|
|
1489
|
+
function actionRow(kind, value, title, subtitle, right, farRight, color) {
|
|
1490
|
+
return "<button class=\"row-button\" data-overview-kind=\"" + esc(kind) + "\" data-overview-value=\"" + esc(value) + "\"><span><span class=\"row-title\">" + (color ? "<span class=\"badge\"><span class=\"dot\" style=\"background:" + esc(color) + "\"></span>" + esc(title) + "</span>" : esc(title)) + "</span><span class=\"meta\">" + esc(subtitle) + "</span></span><span class=\"row-meta\">" + esc(right) + "</span><span class=\"row-meta\">" + esc(farRight || "") + "</span></button>";
|
|
1491
|
+
}
|
|
1492
|
+
|
|
1493
|
+
function bar(counter) {
|
|
1494
|
+
const total = totalCounter(counter);
|
|
1495
|
+
if (!total) return "<div class=\"bar\"></div>";
|
|
1496
|
+
return "<div class=\"bar\">" + cats.map(function (cat) {
|
|
1497
|
+
const value = (counter || {})[cat] || 0;
|
|
1498
|
+
if (!value) return "";
|
|
1499
|
+
return "<button type=\"button\" data-overview-kind=\"category\" data-overview-value=\"" + esc(cat) + "\" title=\"" + esc(labels[cat]) + "\" style=\"width:" + Math.max(1, pct(value, total)).toFixed(2) + "%;background:" + colors[cat] + ";border:0;padding:0;display:block;min-width:2px;cursor:pointer\"></button>";
|
|
1500
|
+
}).join("") + "</div>";
|
|
1501
|
+
}
|
|
1502
|
+
|
|
1503
|
+
function topTools(limit) {
|
|
1504
|
+
return (report.tools || []).slice(0, limit || 8);
|
|
1505
|
+
}
|
|
1506
|
+
|
|
1507
|
+
function toolTokens(name) {
|
|
1508
|
+
const found = (report.toolTokens || []).filter(function (entry) {
|
|
1509
|
+
return entry[0] === name;
|
|
1510
|
+
})[0];
|
|
1511
|
+
return found ? found[1] : 0;
|
|
1512
|
+
}
|
|
1513
|
+
|
|
1514
|
+
function sessionMatches(kind, value) {
|
|
1515
|
+
return (report.sessions || []).filter(function (session) {
|
|
1516
|
+
if (kind === "category") return !!((session.categories || {})[value]);
|
|
1517
|
+
if (kind === "path") return !!((session.paths || {})[value]);
|
|
1518
|
+
return false;
|
|
1519
|
+
}).sort(function (a, b) {
|
|
1520
|
+
const left = kind === "path" ? (a.paths || {})[value] || 0 : (a.categories || {})[value] || 0;
|
|
1521
|
+
const right = kind === "path" ? (b.paths || {})[value] || 0 : (b.categories || {})[value] || 0;
|
|
1522
|
+
return right - left;
|
|
1523
|
+
});
|
|
1524
|
+
}
|
|
1525
|
+
|
|
1526
|
+
function sampleCards(events) {
|
|
1527
|
+
events = (events || []).slice(0, 8);
|
|
1528
|
+
if (!events.length) return "<p class=\"empty\">No sampled records for this item.</p>";
|
|
1529
|
+
return "<div class=\"detail-list\">" + events.map(function (event) {
|
|
1530
|
+
return "<article class=\"sample\"><div class=\"sample-top\"><span>" + esc(event.source) + " · " + esc(event.sessionTitle || event.sessionId) + "</span><span>" + fmt(event.tokens || 0) + " tok</span></div><p>" + esc(event.preview || "No preview captured.") + "</p></article>";
|
|
1531
|
+
}).join("") + "</div>";
|
|
1532
|
+
}
|
|
1533
|
+
|
|
1534
|
+
function sessionCards(sessions, kind, value) {
|
|
1535
|
+
sessions = (sessions || []).slice(0, 8);
|
|
1536
|
+
if (!sessions.length) return "<p class=\"empty\">No matching sessions for this item.</p>";
|
|
1537
|
+
return "<div class=\"detail-list\">" + sessions.map(function (session) {
|
|
1538
|
+
const amount = kind === "path" ? (session.paths || {})[value] || 0 : tok((session.categories || {})[value] || 0);
|
|
1539
|
+
const unit = kind === "path" ? "mentions" : "tok";
|
|
1540
|
+
return "<article class=\"sample\"><div class=\"sample-top\"><span>" + esc(session.source) + " · " + esc(session.updatedAt || "unknown time") + "</span><span>" + esc(fmt(amount) + " " + unit) + "</span></div><p><strong>" + esc(session.title || session.sessionId) + "</strong></p><p class=\"meta\">" + esc(session.cwd || session.path) + "</p></article>";
|
|
1541
|
+
}).join("") + "</div>";
|
|
1542
|
+
}
|
|
1543
|
+
|
|
1544
|
+
function openDetailTab(tab, selector, value) {
|
|
1545
|
+
const tabButton = document.querySelector("[data-tab=\"" + tab + "\"]");
|
|
1546
|
+
if (tabButton) tabButton.click();
|
|
1547
|
+
if (!selector) return;
|
|
1548
|
+
window.setTimeout(function () {
|
|
1549
|
+
const rows = Array.prototype.slice.call(document.querySelectorAll(selector));
|
|
1550
|
+
const row = rows.filter(function (item) {
|
|
1551
|
+
return item.getAttribute(selector.slice(1, -1)) === value;
|
|
1552
|
+
})[0];
|
|
1553
|
+
if (row) row.click();
|
|
1554
|
+
}, 0);
|
|
1555
|
+
}
|
|
1556
|
+
|
|
1557
|
+
function jumpButton(tab, label, selector, value) {
|
|
1558
|
+
return "<button class=\"row-button\" data-overview-jump=\"" + esc(tab) + "\" data-overview-selector=\"" + esc(selector) + "\" data-overview-value=\"" + esc(value || "") + "\"><span><span class=\"row-title\">" + esc(label) + "</span><span class=\"meta\">Open the full detail tab</span></span><span class=\"row-meta\">open</span><span class=\"row-meta\"></span></button>";
|
|
1559
|
+
}
|
|
1560
|
+
|
|
1561
|
+
function setDetail(kind, value) {
|
|
1562
|
+
const el = document.getElementById("overview-drilldown");
|
|
1563
|
+
if (!el) return;
|
|
1564
|
+
if (kind === "tool") {
|
|
1565
|
+
const events = (report.toolEvents || []).filter(function (event) {
|
|
1566
|
+
return event.tool === value;
|
|
1567
|
+
});
|
|
1568
|
+
el.innerHTML = "<div class=\"section-head\"><div><h2>" + esc(value) + "</h2><p class=\"meta\">Sampled calls from the selected sessions.</p></div></div>" + sampleCards(events) + "<div class=\"mini-label\">More</div>" + jumpButton("tools", "Open Tool Calls", "[data-tool]", value);
|
|
1569
|
+
} else if (kind === "metadata") {
|
|
1570
|
+
const events = (report.metadataEvents || []).filter(function (event) {
|
|
1571
|
+
return event.type === value;
|
|
1572
|
+
});
|
|
1573
|
+
el.innerHTML = "<div class=\"section-head\"><div><h2>" + esc(value) + "</h2><p class=\"meta\">Sampled metadata records from transcripts.</p></div></div>" + sampleCards(events) + "<div class=\"mini-label\">More</div>" + jumpButton("metadata", "Open Metadata", "[data-meta]", value);
|
|
1574
|
+
} else if (kind === "path") {
|
|
1575
|
+
el.innerHTML = "<div class=\"section-head\"><div><h2>" + esc(value) + "</h2><p class=\"meta\">Sessions where this path repeats.</p></div></div>" + sessionCards(sessionMatches("path", value), "path", value) + "<div class=\"mini-label\">More</div>" + jumpButton("sessions", "Open Sessions", "", "");
|
|
1576
|
+
} else {
|
|
1577
|
+
const chars = (report.categories || {})[value] || 0;
|
|
1578
|
+
const sessions = sessionMatches("category", value);
|
|
1579
|
+
let extra = "";
|
|
1580
|
+
if (value === "tool_call") {
|
|
1581
|
+
extra = "<div class=\"mini-label\">Top tools</div>" + topTools(6).map(function (entry) {
|
|
1582
|
+
return actionRow("tool", entry[0], entry[0], "Click for sampled calls", "x" + entry[1], fmt(toolTokens(entry[0])) + " tok");
|
|
1583
|
+
}).join("");
|
|
1584
|
+
} else if (value === "metadata") {
|
|
1585
|
+
extra = "<div class=\"mini-label\">Metadata types</div>" + (report.metadata || []).slice(0, 6).map(function (entry) {
|
|
1586
|
+
return actionRow("metadata", entry[0], entry[0], "Click for sampled records", fmt(tok(entry[1])) + " tok", fmt(entry[1]) + " ch");
|
|
1587
|
+
}).join("");
|
|
1588
|
+
}
|
|
1589
|
+
el.innerHTML = "<div class=\"section-head\"><div><h2>" + esc(labels[value] || value) + "</h2><p class=\"meta\">" + esc(fmt(tok(chars)) + " estimated tokens across " + sessions.length + " session(s).") + "</p></div></div>" + (extra || sessionCards(sessions, "category", value));
|
|
1590
|
+
}
|
|
1591
|
+
bindOverview();
|
|
1592
|
+
}
|
|
1593
|
+
|
|
1594
|
+
function bindOverview() {
|
|
1595
|
+
Array.prototype.forEach.call(document.querySelectorAll("[data-overview-kind]"), function (button) {
|
|
1596
|
+
button.onclick = function () {
|
|
1597
|
+
setDetail(button.getAttribute("data-overview-kind"), button.getAttribute("data-overview-value"));
|
|
1598
|
+
};
|
|
1599
|
+
});
|
|
1600
|
+
Array.prototype.forEach.call(document.querySelectorAll("[data-overview-jump]"), function (button) {
|
|
1601
|
+
button.onclick = function () {
|
|
1602
|
+
const selector = button.getAttribute("data-overview-selector");
|
|
1603
|
+
const value = button.getAttribute("data-overview-value");
|
|
1604
|
+
openDetailTab(button.getAttribute("data-overview-jump"), selector, value);
|
|
1605
|
+
};
|
|
1606
|
+
});
|
|
1607
|
+
}
|
|
1608
|
+
|
|
1609
|
+
function renderOverview() {
|
|
1610
|
+
const panel = document.getElementById("panel-overview");
|
|
1611
|
+
if (!panel) return;
|
|
1612
|
+
const total = totalCounter(report.categories);
|
|
1613
|
+
const categoryRows = cats.map(function (cat) {
|
|
1614
|
+
const chars = (report.categories || {})[cat] || 0;
|
|
1615
|
+
if (!chars) return "";
|
|
1616
|
+
return actionRow("category", cat, labels[cat], "Click for sessions and hotspots", fmt(tok(chars)) + " tok", pct(chars, total).toFixed(0) + "%", colors[cat]);
|
|
1617
|
+
}).join("");
|
|
1618
|
+
const toolRows = topTools(8).map(function (entry) {
|
|
1619
|
+
return actionRow("tool", entry[0], entry[0], "Click for sampled calls", "x" + entry[1], fmt(toolTokens(entry[0])) + " tok");
|
|
1620
|
+
}).join("") || "<p class=\"empty\">No tool calls detected.</p>";
|
|
1621
|
+
const pathRows = (report.paths || []).slice(0, 8).map(function (entry) {
|
|
1622
|
+
return actionRow("path", entry[0], entry[0], "Click for matching sessions", "x" + entry[1], "");
|
|
1623
|
+
}).join("") || "<p class=\"empty\">No repeated paths detected.</p>";
|
|
1624
|
+
const metadataRows = (report.metadata || []).slice(0, 8).map(function (entry) {
|
|
1625
|
+
return actionRow("metadata", entry[0], entry[0], "Click for sampled records", fmt(tok(entry[1])) + " tok", fmt(entry[1]) + " ch");
|
|
1626
|
+
}).join("") || "<p class=\"empty\">No metadata-heavy records detected.</p>";
|
|
1627
|
+
panel.innerHTML = "<div class=\"grid\"><section class=\"card\"><div class=\"section-head\"><div><h2>Where The Context Is Going</h2><p class=\"meta\">Click a segment or row to inspect it.</p></div></div>" + bar(report.categories) + "<div>" + categoryRows + "</div></section><section class=\"card\" id=\"overview-drilldown\"><div class=\"section-head\"><div><h2>Warnings And Optimizations</h2><p class=\"meta\">Promptable changes you control.</p></div></div><ol class=\"tips\">" + (report.recommendations || []).map(function (tip) { return "<li>" + esc(tip) + "</li>"; }).join("") + "</ol></section></div><div class=\"grid equal\" style=\"margin-top:14px\"><section class=\"card\"><h2>Tool Hotspots</h2>" + toolRows + "</section><section class=\"card\"><h2>Top Paths</h2>" + pathRows + "</section></div><section class=\"card\" style=\"margin-top:14px\"><h2>Metadata Types</h2>" + metadataRows + "</section>";
|
|
1628
|
+
bindOverview();
|
|
1629
|
+
}
|
|
1630
|
+
|
|
1631
|
+
renderOverview();
|
|
1632
|
+
}.toString() + ")();";
|
|
1633
|
+
}
|
|
1634
|
+
|
|
1635
|
+
function insightsEnhancementScript() {
|
|
1636
|
+
return "(" + function () {
|
|
1637
|
+
const dataEl = document.getElementById("xray-data");
|
|
1638
|
+
if (!dataEl) return;
|
|
1639
|
+
const report = JSON.parse(dataEl.textContent || "{}");
|
|
1640
|
+
const analysis = report.analysis || {};
|
|
1641
|
+
const metrics = analysis.metrics || {};
|
|
1642
|
+
const findings = analysis.findings || [];
|
|
1643
|
+
const cats = ["user", "assistant", "tool_call", "tool_output", "reasoning", "instructions", "attachment", "metadata", "other"];
|
|
1644
|
+
const labels = {
|
|
1645
|
+
user: "User asks",
|
|
1646
|
+
assistant: "Assistant text",
|
|
1647
|
+
tool_call: "Tool calls",
|
|
1648
|
+
tool_output: "Tool output",
|
|
1649
|
+
reasoning: "Reasoning",
|
|
1650
|
+
instructions: "Instructions/context",
|
|
1651
|
+
attachment: "Attachments",
|
|
1652
|
+
metadata: "Metadata",
|
|
1653
|
+
other: "Other",
|
|
1654
|
+
};
|
|
1655
|
+
const colors = {
|
|
1656
|
+
user: "#2563eb",
|
|
1657
|
+
assistant: "#16a34a",
|
|
1658
|
+
tool_call: "#d97706",
|
|
1659
|
+
tool_output: "#dc2626",
|
|
1660
|
+
reasoning: "#7c3aed",
|
|
1661
|
+
instructions: "#0891b2",
|
|
1662
|
+
attachment: "#b45309",
|
|
1663
|
+
metadata: "#64748b",
|
|
1664
|
+
other: "#94a3b8",
|
|
1665
|
+
};
|
|
1666
|
+
|
|
1667
|
+
function esc(value) {
|
|
1668
|
+
return String(value || "").replace(/[&<>"']/g, function (ch) {
|
|
1669
|
+
return { "&": "&", "<": "<", ">": ">", "\"": """, "'": "'" }[ch];
|
|
1670
|
+
});
|
|
1671
|
+
}
|
|
1672
|
+
|
|
1673
|
+
function fmt(value) {
|
|
1674
|
+
const number = Number(value) || 0;
|
|
1675
|
+
if (Math.abs(number) >= 1000000) return (number / 1000000).toFixed(1) + "m";
|
|
1676
|
+
if (Math.abs(number) >= 1000) return (number / 1000).toFixed(1) + "k";
|
|
1677
|
+
return String(Math.round(number * 10) / 10);
|
|
1678
|
+
}
|
|
1679
|
+
|
|
1680
|
+
function pctText(value) {
|
|
1681
|
+
return Math.round((Number(value) || 0) * 100) + "%";
|
|
1682
|
+
}
|
|
1683
|
+
|
|
1684
|
+
function totalCounter(counter) {
|
|
1685
|
+
return Object.keys(counter || {}).reduce(function (sum, key) {
|
|
1686
|
+
return sum + (Number(counter[key]) || 0);
|
|
1687
|
+
}, 0);
|
|
1688
|
+
}
|
|
1689
|
+
|
|
1690
|
+
function tok(chars) {
|
|
1691
|
+
chars = Number(chars) || 0;
|
|
1692
|
+
return chars > 0 ? Math.max(1, Math.ceil(chars / 4)) : 0;
|
|
1693
|
+
}
|
|
1694
|
+
|
|
1695
|
+
function addStyle() {
|
|
1696
|
+
if (document.getElementById("xray-insights-style")) return;
|
|
1697
|
+
const style = document.createElement("style");
|
|
1698
|
+
style.id = "xray-insights-style";
|
|
1699
|
+
style.textContent = [
|
|
1700
|
+
".health-shell{display:grid;grid-template-columns:270px minmax(0,1fr);gap:14px;align-items:stretch;margin-bottom:14px}",
|
|
1701
|
+
".health-card{display:flex;gap:16px;align-items:center;background:hsl(var(--card));border:1px solid hsl(var(--border));border-radius:8px;padding:16px;box-shadow:var(--shadow)}",
|
|
1702
|
+
".health-dial{width:112px;height:112px;border-radius:999px;display:grid;place-items:center;background:conic-gradient(#16a34a calc(var(--score)*1%),hsl(var(--muted)) 0);position:relative;flex:0 0 auto}",
|
|
1703
|
+
".health-dial:after{content:\"\";position:absolute;inset:9px;border-radius:inherit;background:hsl(var(--card))}",
|
|
1704
|
+
".health-score{position:relative;z-index:1;font-size:30px;font-weight:750;letter-spacing:0}",
|
|
1705
|
+
".health-copy{min-width:0}.health-copy h2{font-size:18px}.health-copy p{margin-top:6px;color:hsl(var(--muted-foreground));font-size:13px}",
|
|
1706
|
+
".metric-grid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:10px}",
|
|
1707
|
+
".metric-tile{border:1px solid hsl(var(--border));border-radius:8px;padding:12px;background:hsl(var(--card));min-height:84px}",
|
|
1708
|
+
".metric-tile strong{display:block;font-size:22px;line-height:1.1;margin-top:7px}",
|
|
1709
|
+
".metric-tile span{font-size:11px;text-transform:uppercase;letter-spacing:.08em;font-weight:650;color:hsl(var(--muted-foreground))}",
|
|
1710
|
+
".finding-button{width:100%;border:1px solid hsl(var(--border));border-radius:8px;background:hsl(var(--card));padding:12px;text-align:left;display:grid;grid-template-columns:minmax(0,1fr) auto;gap:12px;cursor:pointer;margin-bottom:8px}",
|
|
1711
|
+
".finding-button:hover,.finding-button.active{background:hsl(var(--accent)/.45);border-color:hsl(var(--ring)/.45)}",
|
|
1712
|
+
".finding-title{font-size:14px;font-weight:650}.finding-summary{font-size:12px;color:hsl(var(--muted-foreground));margin-top:4px;overflow-wrap:anywhere}",
|
|
1713
|
+
".severity{display:inline-flex;align-items:center;border-radius:999px;border:1px solid hsl(var(--border));padding:3px 7px;font-size:11px;text-transform:uppercase;letter-spacing:.06em;font-weight:700;background:hsl(var(--background))}",
|
|
1714
|
+
".severity.critical,.severity.high{color:#dc2626;border-color:rgba(220,38,38,.28);background:rgba(220,38,38,.08)}",
|
|
1715
|
+
".severity.medium{color:#b45309;border-color:rgba(180,83,9,.28);background:rgba(180,83,9,.08)}",
|
|
1716
|
+
".severity.low,.severity.info{color:#0369a1;border-color:rgba(3,105,161,.26);background:rgba(3,105,161,.08)}",
|
|
1717
|
+
".insight-detail{position:sticky;top:82px}.evidence-list{display:grid;gap:7px;margin-top:10px}.evidence-item{border-left:3px solid hsl(var(--border));padding:7px 0 7px 10px;font-size:12px;overflow-wrap:anywhere}",
|
|
1718
|
+
".context-split{display:grid;grid-template-columns:minmax(0,1.1fr) minmax(300px,.9fr);gap:14px;align-items:start}.category-button{width:100%;display:grid;grid-template-columns:minmax(0,1fr) 74px 54px;gap:10px;border:0;background:transparent;border-radius:6px;padding:8px;text-align:left;cursor:pointer}.category-button:hover{background:hsl(var(--accent)/.4)}",
|
|
1719
|
+
".inline-bar{height:8px;border-radius:999px;background:hsl(var(--muted));overflow:hidden;margin-top:8px}.inline-bar span{display:block;height:100%}",
|
|
1720
|
+
".trend-bars{display:grid;gap:8px}.trend-row{display:grid;grid-template-columns:86px minmax(0,1fr) 80px;gap:10px;align-items:center;font-size:12px}.trend-track{height:9px;border-radius:999px;background:hsl(var(--muted));overflow:hidden}.trend-track span{display:block;height:100%;background:#2563eb}",
|
|
1721
|
+
".timeline-list{display:grid;gap:10px}.timeline-session{border:1px solid hsl(var(--border));border-radius:8px;background:hsl(var(--card));padding:12px}.timeline-top{display:flex;justify-content:space-between;gap:12px;margin-bottom:8px}.turn-strip{display:flex;gap:2px;align-items:end;height:42px}.turn-strip button{flex:1;min-width:3px;border:0;border-radius:3px 3px 0 0;background:#2563eb;cursor:pointer}.turn-strip button:hover{filter:brightness(1.15)}",
|
|
1722
|
+
".source-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px}.source-row{border:1px solid hsl(var(--border));border-radius:8px;background:hsl(var(--card));padding:11px;min-width:0}.source-row strong{display:block;font-size:13px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.source-row p{font-size:12px;color:hsl(var(--muted-foreground));overflow-wrap:anywhere}",
|
|
1723
|
+
".raw-note{border:1px dashed hsl(var(--border));border-radius:8px;padding:12px;background:hsl(var(--muted)/.2);font-size:12px;color:hsl(var(--muted-foreground))}",
|
|
1724
|
+
"@media(max-width:920px){.health-shell,.context-split{grid-template-columns:1fr}.metric-grid,.source-grid{grid-template-columns:repeat(2,minmax(0,1fr))}.insight-detail{position:static}}",
|
|
1725
|
+
"@media(max-width:620px){.metric-grid,.source-grid{grid-template-columns:1fr}.trend-row{grid-template-columns:74px minmax(0,1fr) 54px}.health-card{align-items:flex-start}.health-dial{width:88px;height:88px}.health-score{font-size:24px}}",
|
|
1726
|
+
].join("");
|
|
1727
|
+
document.head.appendChild(style);
|
|
1728
|
+
}
|
|
1729
|
+
|
|
1730
|
+
function ensureTab(name, label) {
|
|
1731
|
+
const nav = document.querySelector(".tabs");
|
|
1732
|
+
if (!nav || document.querySelector("[data-tab=\"" + name + "\"]")) return;
|
|
1733
|
+
const button = document.createElement("button");
|
|
1734
|
+
button.className = "tab";
|
|
1735
|
+
button.setAttribute("data-tab", name);
|
|
1736
|
+
button.type = "button";
|
|
1737
|
+
button.textContent = label;
|
|
1738
|
+
nav.appendChild(button);
|
|
1739
|
+
}
|
|
1740
|
+
|
|
1741
|
+
function ensurePanel(name) {
|
|
1742
|
+
if (document.getElementById("panel-" + name)) return;
|
|
1743
|
+
const footer = document.querySelector(".footer");
|
|
1744
|
+
const panel = document.createElement("section");
|
|
1745
|
+
panel.className = "panel";
|
|
1746
|
+
panel.id = "panel-" + name;
|
|
1747
|
+
if (footer && footer.parentNode) footer.parentNode.insertBefore(panel, footer);
|
|
1748
|
+
}
|
|
1749
|
+
|
|
1750
|
+
function activate(name) {
|
|
1751
|
+
Array.prototype.forEach.call(document.querySelectorAll(".tab"), function (tab) {
|
|
1752
|
+
tab.classList.toggle("active", tab.getAttribute("data-tab") === name);
|
|
1753
|
+
});
|
|
1754
|
+
Array.prototype.forEach.call(document.querySelectorAll(".panel"), function (panel) {
|
|
1755
|
+
panel.classList.toggle("active", panel.id === "panel-" + name);
|
|
1756
|
+
});
|
|
1757
|
+
if (location.hash !== "#" + name) history.replaceState(null, "", "#" + name);
|
|
1758
|
+
}
|
|
1759
|
+
|
|
1760
|
+
function wireTabs() {
|
|
1761
|
+
Array.prototype.forEach.call(document.querySelectorAll(".tab"), function (tab) {
|
|
1762
|
+
tab.onclick = function () {
|
|
1763
|
+
activate(tab.getAttribute("data-tab"));
|
|
1764
|
+
};
|
|
1765
|
+
});
|
|
1766
|
+
}
|
|
1767
|
+
|
|
1768
|
+
function metric(label, value, detail) {
|
|
1769
|
+
return "<div class=\"metric-tile\"><span>" + esc(label) + "</span><strong>" + esc(value) + "</strong><p class=\"meta\">" + esc(detail || "") + "</p></div>";
|
|
1770
|
+
}
|
|
1771
|
+
|
|
1772
|
+
function findingButton(finding, index) {
|
|
1773
|
+
return "<button class=\"finding-button\" data-finding=\"" + index + "\"><span><span class=\"finding-title\">" + esc(finding.title) + "</span><span class=\"finding-summary\">" + esc(finding.summary) + "</span></span><span class=\"severity " + esc(finding.severity) + "\">" + esc(finding.severity) + "</span></button>";
|
|
1774
|
+
}
|
|
1775
|
+
|
|
1776
|
+
function renderFindingDetail(index, targetId) {
|
|
1777
|
+
const finding = findings[index] || findings[0];
|
|
1778
|
+
const el = document.getElementById(targetId || "finding-detail");
|
|
1779
|
+
if (!el) return;
|
|
1780
|
+
if (!finding) {
|
|
1781
|
+
el.innerHTML = "<h2>No Findings</h2><p class=\"empty\">No deterministic warning crossed the reporting threshold.</p>";
|
|
1782
|
+
return;
|
|
1783
|
+
}
|
|
1784
|
+
const evidence = (finding.evidence || []).map(function (item) {
|
|
1785
|
+
return "<div class=\"evidence-item\">" + esc(item) + "</div>";
|
|
1786
|
+
}).join("") || "<p class=\"empty\">No evidence sample captured.</p>";
|
|
1787
|
+
el.innerHTML = "<div class=\"section-head\"><div><span class=\"severity " + esc(finding.severity) + "\">" + esc(finding.severity) + "</span><h2 style=\"margin-top:8px\">" + esc(finding.title) + "</h2><p class=\"meta\">" + esc(finding.rule) + " · confidence " + esc(finding.confidence) + " · " + esc(finding.action) + "</p></div><strong>" + esc(finding.score) + "</strong></div><p>" + esc(finding.summary) + "</p><div class=\"mini-label\">Evidence</div><div class=\"evidence-list\">" + evidence + "</div><div class=\"mini-label\">Recommended Move</div><p class=\"raw-note\">" + esc(finding.recommendation || "No recommendation captured.") + "</p>";
|
|
1788
|
+
}
|
|
1789
|
+
|
|
1790
|
+
function categoryRows() {
|
|
1791
|
+
const total = totalCounter(report.categories);
|
|
1792
|
+
return cats.map(function (cat) {
|
|
1793
|
+
const chars = (report.categories || {})[cat] || 0;
|
|
1794
|
+
if (!chars) return "";
|
|
1795
|
+
const percentage = total ? chars / total : 0;
|
|
1796
|
+
return "<button class=\"category-button\" data-category=\"" + esc(cat) + "\"><span><span class=\"badge\"><span class=\"dot\" style=\"background:" + colors[cat] + "\"></span>" + esc(labels[cat]) + "</span><span class=\"inline-bar\"><span style=\"width:" + Math.max(2, percentage * 100).toFixed(1) + "%;background:" + colors[cat] + "\"></span></span></span><span class=\"row-meta\">" + fmt(tok(chars)) + " tok</span><span class=\"row-meta\">" + Math.round(percentage * 100) + "%</span></button>";
|
|
1797
|
+
}).join("");
|
|
1798
|
+
}
|
|
1799
|
+
|
|
1800
|
+
function renderStats() {
|
|
1801
|
+
const stats = document.getElementById("stats");
|
|
1802
|
+
if (!stats) return;
|
|
1803
|
+
stats.innerHTML = metric("Health", (analysis.score || 0) + "/100", analysis.scoreLabel || "unknown") +
|
|
1804
|
+
metric("Findings", findings.length, findings.length ? findings[0].title : "no threshold crossed") +
|
|
1805
|
+
metric("Cache hit", pctText(metrics.cacheHitRatio || 0), "create " + pctText(metrics.cacheCreationPct || 0)) +
|
|
1806
|
+
metric("Tool success", pctText(metrics.successRate == null ? 1 : metrics.successRate), fmt(metrics.normalizedSteps || 0) + " normalized steps");
|
|
1807
|
+
}
|
|
1808
|
+
|
|
1809
|
+
function renderOverview() {
|
|
1810
|
+
const panel = document.getElementById("panel-overview");
|
|
1811
|
+
if (!panel) return;
|
|
1812
|
+
const topFindings = findings.slice(0, 4).map(findingButton).join("") || "<p class=\"empty\">No major issues found. Recent sessions look balanced.</p>";
|
|
1813
|
+
panel.innerHTML = "<div class=\"health-shell\"><section class=\"health-card\"><div class=\"health-dial\" style=\"--score:" + esc(analysis.score || 0) + "\"><span class=\"health-score\">" + esc(analysis.score || 0) + "</span></div><div class=\"health-copy\"><div class=\"eyebrow\">Context Health</div><h2>" + esc(analysis.scoreLabel || "unknown") + "</h2><p>Score combines cache behavior, context pressure, duplicate reads, retry loops, tool failures, and exploration-to-edit ratio.</p></div></section><section class=\"card\" id=\"overview-finding-detail\"></section></div><div class=\"context-split\"><section class=\"card\"><div class=\"section-head\"><div><h2>Top Findings</h2><p class=\"meta\">Click a finding to inspect evidence and the suggested move.</p></div></div>" + topFindings + "</section><section class=\"card\"><div class=\"section-head\"><div><h2>Where Context Is Going</h2><p class=\"meta\">Bucket rows are clickable and stay local to this report.</p></div></div>" + categoryRows() + "</section></div><div class=\"metric-grid\" style=\"margin-top:14px\">" + metric("Exploration ratio", (metrics.explorationRatio || 0) + "x", "read/search to edit/write") + metric("Duplicate reads", metrics.duplicateReadCount || 0, "suppressed after edits") + metric("Retry loops", metrics.retryLoopCount || 0, "3+ identical commands") + metric("MCP calls", metrics.mcpCalls || 0, (report.mcpServers || []).length + " configured") + "</div>";
|
|
1814
|
+
Array.prototype.forEach.call(panel.querySelectorAll("[data-finding]"), function (button) {
|
|
1815
|
+
button.onclick = function () {
|
|
1816
|
+
Array.prototype.forEach.call(panel.querySelectorAll("[data-finding]"), function (item) { item.classList.remove("active"); });
|
|
1817
|
+
button.classList.add("active");
|
|
1818
|
+
renderFindingDetail(Number(button.getAttribute("data-finding")), "overview-finding-detail");
|
|
1819
|
+
};
|
|
1820
|
+
});
|
|
1821
|
+
Array.prototype.forEach.call(panel.querySelectorAll("[data-category]"), function (button) {
|
|
1822
|
+
button.onclick = function () {
|
|
1823
|
+
const category = button.getAttribute("data-category");
|
|
1824
|
+
const sessions = (report.sessions || []).filter(function (session) { return (session.categories || {})[category]; }).slice(0, 4);
|
|
1825
|
+
const detail = document.getElementById("overview-finding-detail");
|
|
1826
|
+
if (!detail) return;
|
|
1827
|
+
detail.innerHTML = "<div class=\"section-head\"><div><h2>" + esc(labels[category] || category) + "</h2><p class=\"meta\">Sessions contributing to this bucket.</p></div></div><div class=\"evidence-list\">" + (sessions.map(function (session) { return "<div class=\"evidence-item\"><strong>" + esc(session.title || session.sessionId) + "</strong><br>" + esc(session.source + " · " + fmt(tok((session.categories || {})[category] || 0)) + " tok · " + (session.cwd || session.path)) + "</div>"; }).join("") || "<p class=\"empty\">No sessions matched.</p>") + "</div>";
|
|
1828
|
+
};
|
|
1829
|
+
});
|
|
1830
|
+
renderFindingDetail(0, "overview-finding-detail");
|
|
1831
|
+
const first = panel.querySelector("[data-finding]");
|
|
1832
|
+
if (first) first.classList.add("active");
|
|
1833
|
+
}
|
|
1834
|
+
|
|
1835
|
+
function renderFindings() {
|
|
1836
|
+
const panel = document.getElementById("panel-findings");
|
|
1837
|
+
if (!panel) return;
|
|
1838
|
+
panel.innerHTML = "<div class=\"grid\"><section class=\"card\"><div class=\"section-head\"><div><h2>Findings</h2><p class=\"meta\">Deterministic checks adapted from AgentSight, Argus, Cogpit, and usage dashboards.</p></div></div>" + (findings.map(findingButton).join("") || "<p class=\"empty\">No findings crossed threshold.</p>") + "</section><aside class=\"card detail insight-detail\" id=\"finding-detail\"></aside></div>";
|
|
1839
|
+
Array.prototype.forEach.call(panel.querySelectorAll("[data-finding]"), function (button) {
|
|
1840
|
+
button.onclick = function () {
|
|
1841
|
+
Array.prototype.forEach.call(panel.querySelectorAll("[data-finding]"), function (item) { item.classList.remove("active"); });
|
|
1842
|
+
button.classList.add("active");
|
|
1843
|
+
renderFindingDetail(Number(button.getAttribute("data-finding")), "finding-detail");
|
|
1844
|
+
};
|
|
1845
|
+
});
|
|
1846
|
+
renderFindingDetail(0, "finding-detail");
|
|
1847
|
+
const first = panel.querySelector("[data-finding]");
|
|
1848
|
+
if (first) first.classList.add("active");
|
|
1849
|
+
}
|
|
1850
|
+
|
|
1851
|
+
function renderTrends() {
|
|
1852
|
+
const panel = document.getElementById("panel-trends");
|
|
1853
|
+
if (!panel) return;
|
|
1854
|
+
const days = (analysis.trends && analysis.trends.byDay || []).slice(-14);
|
|
1855
|
+
const maxTokens = days.reduce(function (max, day) { return Math.max(max, day.tokens || 0); }, 1);
|
|
1856
|
+
const dayRows = days.map(function (day) {
|
|
1857
|
+
return "<div class=\"trend-row\"><span>" + esc(day.day) + "</span><span class=\"trend-track\"><span style=\"width:" + Math.max(2, ((day.tokens || 0) / maxTokens) * 100).toFixed(1) + "%\"></span></span><span class=\"row-meta\">" + fmt(day.tokens || 0) + " tok</span></div>";
|
|
1858
|
+
}).join("") || "<p class=\"empty\">No trend window found.</p>";
|
|
1859
|
+
const toolRows = (analysis.trends && analysis.trends.topTools || []).slice(0, 10).map(function (entry) {
|
|
1860
|
+
return "<tr><td>" + esc(entry[0]) + "</td><td>" + esc(entry[1]) + "</td></tr>";
|
|
1861
|
+
}).join("") || "<tr><td>No tools detected</td><td></td></tr>";
|
|
1862
|
+
const pathRows = (analysis.trends && analysis.trends.topPaths || []).slice(0, 10).map(function (entry) {
|
|
1863
|
+
return "<tr><td>" + esc(entry[0]) + "</td><td>" + esc(entry[1]) + "</td></tr>";
|
|
1864
|
+
}).join("") || "<tr><td>No paths detected</td><td></td></tr>";
|
|
1865
|
+
panel.innerHTML = "<div class=\"grid\"><section class=\"card\"><div class=\"section-head\"><div><h2>Daily Trend</h2><p class=\"meta\">Sessions and observed/estimated token load by day.</p></div></div><div class=\"trend-bars\">" + dayRows + "</div></section><section class=\"card\"><h2>Hotspots</h2><div class=\"grid equal\" style=\"margin-top:10px\"><div><div class=\"mini-label\">Tools</div><table class=\"table\"><tbody>" + toolRows + "</tbody></table></div><div><div class=\"mini-label\">Paths</div><table class=\"table\"><tbody>" + pathRows + "</tbody></table></div></div></section></div>";
|
|
1866
|
+
}
|
|
1867
|
+
|
|
1868
|
+
function turnHeight(turn, max) {
|
|
1869
|
+
const value = Number(turn.totalTokens || turn.inputTokens || 0);
|
|
1870
|
+
return Math.max(4, Math.round((value / Math.max(1, max)) * 40));
|
|
1871
|
+
}
|
|
1872
|
+
|
|
1873
|
+
function renderTimeline() {
|
|
1874
|
+
const panel = document.getElementById("panel-timeline");
|
|
1875
|
+
if (!panel) return;
|
|
1876
|
+
const sessions = (report.sessions || []).slice(0, 30);
|
|
1877
|
+
panel.innerHTML = "<div class=\"timeline-list\">" + (sessions.map(function (session) {
|
|
1878
|
+
const series = session.usage && session.usage.series || [];
|
|
1879
|
+
const maxTurn = series.reduce(function (max, turn) { return Math.max(max, turn.totalTokens || turn.inputTokens || 0); }, 1);
|
|
1880
|
+
const strip = series.length ? series.map(function (turn, index) {
|
|
1881
|
+
const cache = (turn.cacheReadInputTokens || 0) + (turn.cachedInputTokens || 0);
|
|
1882
|
+
const color = turn.cacheCreationInputTokens > turn.inputTokens ? "#b45309" : cache > turn.inputTokens ? "#16a34a" : "#2563eb";
|
|
1883
|
+
return "<button title=\"" + esc((turn.label || "turn " + (index + 1)) + " · " + fmt(turn.totalTokens || 0) + " tokens") + "\" style=\"height:" + turnHeight(turn, maxTurn) + "px;background:" + color + "\"></button>";
|
|
1884
|
+
}).join("") : "<p class=\"empty\">No observed per-turn token series in this session.</p>";
|
|
1885
|
+
const cache = cacheStabilityClient(session);
|
|
1886
|
+
const growth = contextGrowthClient(session);
|
|
1887
|
+
return "<article class=\"timeline-session\"><div class=\"timeline-top\"><div><div class=\"eyebrow\">" + esc(session.source + " · " + (session.updatedAt || "unknown time")) + "</div><h2>" + esc(session.title || session.sessionId) + "</h2><p class=\"meta\">" + esc(session.cwd || session.path) + "</p></div><div style=\"text-align:right\"><strong>" + fmt(session.tokens || 0) + "</strong><p class=\"meta\">" + esc(session.tokenMethod || "") + "</p></div></div><div class=\"turn-strip\">" + strip + "</div><div class=\"badge-list\" style=\"margin-top:10px\"><span class=\"badge\">cache " + esc(cache.classification) + "</span><span class=\"badge\">growth " + esc(growth.growthFactor || 0) + "x</span><span class=\"badge\">steps " + esc(session.totalSteps || 0) + "</span></div></article>";
|
|
1888
|
+
}).join("") || "<section class=\"card\"><p class=\"empty\">No sessions found.</p></section>") + "</div>";
|
|
1889
|
+
}
|
|
1890
|
+
|
|
1891
|
+
function cacheStabilityClient(session) {
|
|
1892
|
+
const match = (analysis.evidence && analysis.evidence.cacheStability || []).filter(function (item) { return item.sessionId === session.sessionId; })[0];
|
|
1893
|
+
return match || { classification: "unknown" };
|
|
1894
|
+
}
|
|
1895
|
+
|
|
1896
|
+
function contextGrowthClient(session) {
|
|
1897
|
+
const match = (analysis.evidence && analysis.evidence.contextGrowth || []).filter(function (item) { return item.sessionId === session.sessionId; })[0];
|
|
1898
|
+
return match || { growthFactor: 0 };
|
|
1899
|
+
}
|
|
1900
|
+
|
|
1901
|
+
function renderSources() {
|
|
1902
|
+
const panel = document.getElementById("panel-sources");
|
|
1903
|
+
if (!panel) return;
|
|
1904
|
+
const rows = (analysis.sourceModel && analysis.sourceModel.sourceProjectSession || []).slice(0, 60).map(function (item) {
|
|
1905
|
+
return "<article class=\"source-row\"><strong>" + esc(item.title || item.sessionId) + "</strong><p>" + esc(item.source + " · " + fmt(item.tokens || 0) + " tok · " + (item.project || "")) + "</p><p>" + esc(item.updatedAt || "") + "</p></article>";
|
|
1906
|
+
}).join("") || "<p class=\"empty\">No source records found.</p>";
|
|
1907
|
+
panel.innerHTML = "<section class=\"card\"><div class=\"section-head\"><div><h2>Source / Project / Session</h2><p class=\"meta\">Metadata-first browser model for local Codex and Claude Code sessions.</p></div></div><p class=\"raw-note\">" + esc(analysis.sourceModel && analysis.sourceModel.privacy || "Transcript content stays local.") + "</p><div class=\"source-grid\" style=\"margin-top:12px\">" + rows + "</div></section>";
|
|
1908
|
+
}
|
|
1909
|
+
|
|
1910
|
+
addStyle();
|
|
1911
|
+
ensureTab("findings", "Findings");
|
|
1912
|
+
ensureTab("timeline", "Timeline");
|
|
1913
|
+
ensureTab("trends", "Trends");
|
|
1914
|
+
ensureTab("sources", "Sources");
|
|
1915
|
+
ensurePanel("findings");
|
|
1916
|
+
ensurePanel("timeline");
|
|
1917
|
+
ensurePanel("trends");
|
|
1918
|
+
ensurePanel("sources");
|
|
1919
|
+
wireTabs();
|
|
1920
|
+
renderStats();
|
|
1921
|
+
renderOverview();
|
|
1922
|
+
renderFindings();
|
|
1923
|
+
renderTimeline();
|
|
1924
|
+
renderTrends();
|
|
1925
|
+
renderSources();
|
|
1926
|
+
const initial = (location.hash || "#overview").slice(1);
|
|
1927
|
+
if (document.getElementById("panel-" + initial)) activate(initial);
|
|
1928
|
+
}.toString() + ")();";
|
|
1929
|
+
}
|
|
1930
|
+
|
|
1931
|
+
function renderHtml(sessions, args) {
|
|
1932
|
+
const report = buildReport(sessions, args);
|
|
1933
|
+
return "<!doctype html><html><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"><title>Context X-Ray</title><style>" +
|
|
1934
|
+
"@import url(\"https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap\");" +
|
|
1935
|
+
":root{--background:0 0% 100%;--foreground:220 10% 10%;--card:0 0% 100%;--muted:220 10% 95%;--muted-foreground:220 5% 45%;--accent:220 10% 92%;--border:220 10% 90%;--ring:220 10% 55%;--destructive:0 84% 60%;--radius:.5rem;--shadow:0 1px 2px rgba(16,24,40,.06);}" +
|
|
1936
|
+
"@media(prefers-color-scheme:dark){:root{--background:220 10% 7%;--foreground:220 8% 92%;--card:220 9% 9%;--muted:220 8% 15%;--muted-foreground:220 6% 64%;--accent:220 8% 17%;--border:220 8% 18%;--ring:220 8% 54%;--destructive:0 72% 51%;--shadow:none;}}" +
|
|
1937
|
+
"*{box-sizing:border-box}html{background:hsl(var(--background))}body{margin:0;background:hsl(var(--background));color:hsl(var(--foreground));font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,\"Segoe UI\",sans-serif;font-feature-settings:\"cv02\",\"cv03\",\"cv04\",\"cv11\";line-height:1.45}button{font:inherit;color:inherit}main{max-width:1180px;margin:0 auto;padding:20px 18px 44px}.topbar{border-bottom:1px solid hsl(var(--border));background:hsl(var(--background));position:sticky;top:0;z-index:10}.topbar-inner{max-width:1180px;margin:0 auto;padding:14px 18px;display:flex;align-items:center;justify-content:space-between;gap:16px}.title-row{display:flex;align-items:center;gap:9px}.logo-dot{width:10px;height:10px;border-radius:999px;background:#38bdf8;box-shadow:0 0 0 4px rgba(56,189,248,.12)}h1{font-size:20px;line-height:1.1;margin:0;font-weight:650;letter-spacing:0}h2{font-size:15px;line-height:1.2;margin:0;font-weight:650}h3{font-size:14px;line-height:1.3;margin:0;font-weight:600}p{margin:0}.muted,.eyebrow,.meta,.empty{color:hsl(var(--muted-foreground))}.eyebrow{text-transform:uppercase;letter-spacing:.08em;font-size:11px;font-weight:650}.meta{font-size:12px}.tabs{display:inline-flex;align-items:center;gap:2px;border:1px solid hsl(var(--border));background:hsl(var(--muted)/.35);border-radius:8px;padding:2px}.tab{border:0;background:transparent;border-radius:6px;padding:6px 10px;font-size:12px;line-height:1.1;cursor:pointer}.tab:hover{background:hsl(var(--accent)/.5)}.tab.active{background:hsl(var(--background));box-shadow:var(--shadow)}.stats{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:10px;margin:18px 0}.card{background:hsl(var(--card));border:1px solid hsl(var(--border));border-radius:8px;padding:14px;box-shadow:var(--shadow)}.stat-value{display:block;font-size:28px;line-height:1.1;font-weight:700;margin-top:6px}.panel{display:none}.panel.active{display:block}.grid{display:grid;grid-template-columns:minmax(0,1.35fr) minmax(280px,.8fr);gap:14px;align-items:start}.grid.equal{grid-template-columns:repeat(2,minmax(0,1fr))}.section-head{display:flex;align-items:flex-start;justify-content:space-between;gap:10px;margin-bottom:12px}.bar{display:flex;overflow:hidden;height:10px;border-radius:999px;background:hsl(var(--muted));margin:10px 0 12px}.bar span{display:block;min-width:2px}.table{width:100%;border-collapse:collapse;font-size:12px}.table th{text-align:left;color:hsl(var(--muted-foreground));font-weight:600;border-bottom:1px solid hsl(var(--border));padding:7px 8px}.table td{border-bottom:1px solid hsl(var(--border));padding:8px;vertical-align:top}.table th:last-child,.table td:last-child{text-align:right}.row-button{width:100%;display:grid;grid-template-columns:minmax(0,1fr) 74px 70px;gap:10px;align-items:center;text-align:left;border:1px solid transparent;background:transparent;border-radius:6px;padding:8px;cursor:pointer}.row-button:hover,.row-button.active{border-color:hsl(var(--border));background:hsl(var(--accent)/.35)}.row-title{font-size:13px;font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.row-meta{font-size:12px;color:hsl(var(--muted-foreground));text-align:right}.badge-list{display:flex;flex-wrap:wrap;gap:6px}.badge{display:inline-flex;align-items:center;gap:5px;border:1px solid hsl(var(--border));border-radius:999px;padding:3px 7px;font-size:11px;background:hsl(var(--background));max-width:100%}.badge .dot{width:7px;height:7px;border-radius:999px;flex:0 0 auto}.tips{margin:0;padding-left:20px}.tips li{margin:0 0 8px}.detail{min-height:228px}.detail-list{display:grid;gap:8px;margin-top:10px}.sample{border:1px solid hsl(var(--border));border-radius:8px;padding:10px;background:hsl(var(--muted)/.22)}.sample-top{display:flex;justify-content:space-between;gap:10px;margin-bottom:5px;font-size:11px;color:hsl(var(--muted-foreground))}.sample p{font-size:12px;color:hsl(var(--foreground));overflow-wrap:anywhere}.session-card{border:1px solid hsl(var(--border));border-radius:8px;background:hsl(var(--card));margin-bottom:10px;overflow:hidden}.session-card summary{list-style:none;display:flex;justify-content:space-between;gap:14px;cursor:pointer;padding:13px 14px}.session-card summary::-webkit-details-marker{display:none}.session-body{border-top:1px solid hsl(var(--border));padding:12px 14px}.session-title{font-size:13px;font-weight:650;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.session-path{font-size:12px;color:hsl(var(--muted-foreground));margin-top:3px;overflow-wrap:anywhere}.token-big{font-size:20px;font-weight:700;white-space:nowrap}.mini-label{margin:10px 0 6px;font-size:11px;text-transform:uppercase;letter-spacing:.08em;font-weight:650;color:hsl(var(--muted-foreground))}.footer{margin-top:20px;color:hsl(var(--muted-foreground));font-size:12px}.hidden{display:none!important}@media(max-width:840px){.topbar-inner{align-items:flex-start;flex-direction:column}.stats,.grid,.grid.equal{grid-template-columns:1fr}.tabs{width:100%;overflow:auto}.tab{white-space:nowrap}.row-button{grid-template-columns:minmax(0,1fr) 64px 58px}.session-card summary{align-items:flex-start;flex-direction:column}}" +
|
|
1938
|
+
"</style></head><body><header class=\"topbar\"><div class=\"topbar-inner\"><div><div class=\"title-row\"><span class=\"logo-dot\"></span><h1>Context X-Ray</h1></div><p class=\"meta\">Generated " + escapeHtml(report.generatedLabel) + " · mode=" + escapeHtml(report.mode) + " · source=" + escapeHtml(report.source) + " · since=" + escapeHtml(report.since) + "</p></div><nav class=\"tabs\" aria-label=\"Report views\"><button class=\"tab active\" data-tab=\"overview\">Overview</button><button class=\"tab\" data-tab=\"tools\">Tool Calls</button><button class=\"tab\" data-tab=\"mcp\">MCP</button><button class=\"tab\" data-tab=\"metadata\">Metadata</button><button class=\"tab\" data-tab=\"sessions\">Sessions</button></nav></div></header><main><section class=\"stats\" id=\"stats\"></section><section class=\"panel active\" id=\"panel-overview\"></section><section class=\"panel\" id=\"panel-tools\"></section><section class=\"panel\" id=\"panel-mcp\"></section><section class=\"panel\" id=\"panel-metadata\"></section><section class=\"panel\" id=\"panel-sessions\"></section><p class=\"footer\">Reads local transcript and MCP config files only. No transcript content is uploaded.</p></main><script type=\"application/json\" id=\"xray-data\">" + jsonScript(report) + "</script><script>" +
|
|
1939
|
+
"(function(){var report=JSON.parse(document.getElementById('xray-data').textContent);var cats=['user','assistant','tool_call','tool_output','reasoning','instructions','attachment','metadata','other'];var labels={user:'User asks',assistant:'Assistant text',tool_call:'Tool calls',tool_output:'Tool output',reasoning:'Reasoning',instructions:'Instructions/context',attachment:'Attachments',metadata:'Metadata',other:'Other'};var colors={user:'#8ba8ff',assistant:'#55b982',tool_call:'#f0a85b',tool_output:'#e06b73',reasoning:'#a77be8',instructions:'#6ac3d5',attachment:'#d6a85a',metadata:'#9aa3ad',other:'#c3c8ce'};function esc(v){return String(v||'').replace(/[&<>\"']/g,function(ch){return {'&':'&','<':'<','>':'>','\"':'"',\"'\":'''}[ch];});}function fmt(n){n=Number(n)||0;if(n>=1000000)return(n/1000000).toFixed(1)+'m';if(n>=1000)return(n/1000).toFixed(1)+'k';return String(n);}function tok(chars){chars=Number(chars)||0;return chars>0?Math.max(1,Math.ceil(chars/4)):0;}function pc(part,total){return total>0?Math.max(0,Math.min(100,(part/total)*100)):0;}function totalCounter(counter){return Object.keys(counter||{}).reduce(function(sum,key){return sum+(Number(counter[key])||0);},0);}function bar(counter){var total=totalCounter(counter);if(!total)return'<div class=\"bar\"></div>';return'<div class=\"bar\">'+cats.map(function(cat){var value=counter[cat]||0;if(!value)return'';return'<span title=\"'+esc(labels[cat])+'\" style=\"width:'+Math.max(1,pc(value,total)).toFixed(2)+'%;background:'+colors[cat]+'\"></span>';}).join('')+'</div>';}function metric(label,value){return'<article class=\"card\"><div class=\"eyebrow\">'+esc(label)+'</div><span class=\"stat-value\">'+esc(value)+'</span></article>';}function tableRows(entries,kind){if(!entries||!entries.length)return'<tr><td>None detected</td><td></td></tr>';return entries.map(function(entry){var value=entry[1];var shown=kind==='chars'?fmt(tok(value)):(kind==='tokens'?fmt(value):String(value));return'<tr><td>'+esc(entry[0])+'</td><td>'+esc(shown)+'</td></tr>';}).join('');}function toolToken(name){var found=(report.toolTokens||[]).filter(function(entry){return entry[0]===name;})[0];return found?found[1]:0;}function renderStats(){var counts=report.sourceCounts||{};document.getElementById('stats').innerHTML=metric('Sessions',report.sessionCount)+metric('Observed/estimated tokens',fmt(report.totalTokens))+metric('Codex',counts.codex||0)+metric('Claude',counts.claude||0);}function renderOverview(){var categoryTotal=totalCounter(report.categories);var categoryRows=cats.map(function(cat){var chars=(report.categories||{})[cat]||0;if(!chars)return'';return'<tr><td><span class=\"badge\"><span class=\"dot\" style=\"background:'+colors[cat]+'\"></span>'+esc(labels[cat])+'</span></td><td>'+fmt(tok(chars))+'</td><td>'+pc(chars,categoryTotal).toFixed(0)+'%</td></tr>';}).join('');document.getElementById('panel-overview').innerHTML='<div class=\"grid\"><section class=\"card\"><div class=\"section-head\"><div><h2>Where The Context Is Going</h2><p class=\"meta\">Approximate contribution by transcript bucket.</p></div></div>'+bar(report.categories)+'<table class=\"table\"><tbody>'+categoryRows+'</tbody></table></section><section class=\"card\"><div class=\"section-head\"><div><h2>Warnings And Optimizations</h2><p class=\"meta\">Promptable changes you control.</p></div></div><ol class=\"tips\">'+(report.recommendations||[]).map(function(tip){return'<li>'+esc(tip)+'</li>';}).join('')+'</ol></section></div><div class=\"grid equal\" style=\"margin-top:14px\"><section class=\"card\"><h2>Top Paths</h2><table class=\"table\"><tbody>'+tableRows(report.paths,'count')+'</tbody></table></section><section class=\"card\"><h2>Metadata Types</h2><table class=\"table\"><tbody>'+tableRows(report.metadata,'chars')+'</tbody></table></section></div>';}function renderTools(){var rows=(report.tools||[]).map(function(entry,index){var name=entry[0];var count=entry[1];return'<button class=\"row-button'+(index===0?' active':'')+'\" data-tool=\"'+esc(name)+'\"><span><span class=\"row-title\">'+esc(name)+'</span><span class=\"meta\">Click to inspect sampled calls</span></span><span class=\"row-meta\">x'+count+'</span><span class=\"row-meta\">'+fmt(toolToken(name))+' tok</span></button>';}).join('')||'<div class=\"empty\">No tool calls detected in these sessions.</div>';document.getElementById('panel-tools').innerHTML='<div class=\"grid\"><section class=\"card\"><div class=\"section-head\"><div><h2>Tool Calls</h2><p class=\"meta\">Calls are clickable; samples are capped so the report stays light.</p></div></div><div>'+rows+'</div></section><aside class=\"card detail\" id=\"tool-detail\"></aside></div>';Array.prototype.forEach.call(document.querySelectorAll('[data-tool]'),function(btn){btn.addEventListener('click',function(){Array.prototype.forEach.call(document.querySelectorAll('[data-tool]'),function(item){item.classList.remove('active');});btn.classList.add('active');renderToolDetail(btn.getAttribute('data-tool'));});});if((report.tools||[]).length)renderToolDetail(report.tools[0][0]);else document.getElementById('tool-detail').innerHTML='<h2>Tool Detail</h2><p class=\"empty\">Nothing to inspect yet.</p>';}function renderToolDetail(name){var events=(report.toolEvents||[]).filter(function(event){return event.tool===name;}).slice(0,30);document.getElementById('tool-detail').innerHTML='<div class=\"section-head\"><div><h2>'+esc(name)+'</h2><p class=\"meta\">'+events.length+' sampled call'+(events.length===1?'':'s')+'</p></div></div><div class=\"detail-list\">'+(events.map(function(event){return'<article class=\"sample\"><div class=\"sample-top\"><span>'+esc(event.source)+' · '+esc(event.sessionTitle||event.sessionId)+'</span><span>'+fmt(event.tokens||0)+' tok</span></div><p>'+esc(event.preview||'No preview captured.')+'</p></article>';}).join('')||'<p class=\"empty\">No sampled call payloads for this tool.</p>')+'</div>';}function renderMcp(){var servers=report.mcpServers||[];var usage=report.mcpUsage||[];var configured=servers.map(function(server){return'<button class=\"row-button\" data-mcp=\"'+esc(server.name)+'\"><span><span class=\"row-title\">'+esc(server.name)+'</span><span class=\"meta\">'+esc(server.source)+' · '+esc(server.target)+'</span></span><span class=\"row-meta\">config</span><span class=\"row-meta\"></span></button>';}).join('')||'<div class=\"empty\">No MCP server config found in the common local config files.</div>';var detected=usage.map(function(entry,index){return'<button class=\"row-button'+(!servers.length&&index===0?' active':'')+'\" data-mcp=\"'+esc(entry[0])+'\"><span><span class=\"row-title\">'+esc(entry[0])+'</span><span class=\"meta\">Detected from mcp__server__tool style names</span></span><span class=\"row-meta\">x'+entry[1]+'</span><span class=\"row-meta\"></span></button>';}).join('')||'<div class=\"empty\">No MCP-prefixed tool calls detected in the selected sessions.</div>';document.getElementById('panel-mcp').innerHTML='<div class=\"grid\"><section class=\"card\"><div class=\"section-head\"><div><h2>MCP Servers</h2><p class=\"meta\">Configured locally plus inferred calls from transcripts.</p></div></div><div class=\"mini-label\">Configured</div>'+configured+'<div class=\"mini-label\">Detected usage</div>'+detected+'</section><aside class=\"card detail\" id=\"mcp-detail\"></aside></div>';Array.prototype.forEach.call(document.querySelectorAll('[data-mcp]'),function(btn){btn.addEventListener('click',function(){Array.prototype.forEach.call(document.querySelectorAll('[data-mcp]'),function(item){item.classList.remove('active');});btn.classList.add('active');renderMcpDetail(btn.getAttribute('data-mcp'));});});if(usage.length)renderMcpDetail(usage[0][0]);else if(servers.length)renderMcpDetail(servers[0].name);else document.getElementById('mcp-detail').innerHTML='<h2>MCP Detail</h2><p class=\"empty\">Install or run sessions with MCP tools to see usage here.</p>';}function renderMcpDetail(name){var server=(report.mcpServers||[]).filter(function(item){return item.name===name;})[0];var tools=(report.mcpTools||[]).filter(function(entry){return entry[0].indexOf(name+' / ')===0;});var events=(report.toolEvents||[]).filter(function(event){return event.mcpServer===name;}).slice(0,25);document.getElementById('mcp-detail').innerHTML='<div class=\"section-head\"><div><h2>'+esc(name)+'</h2><p class=\"meta\">'+(server?esc(server.source+' · '+server.target):'Detected from tool call names')+'</p></div></div><div class=\"mini-label\">Tools</div><table class=\"table\"><tbody>'+tableRows(tools,'count')+'</tbody></table><div class=\"mini-label\">Sample calls</div><div class=\"detail-list\">'+(events.map(function(event){return'<article class=\"sample\"><div class=\"sample-top\"><span>'+esc(event.mcpTool||event.tool)+'</span><span>'+fmt(event.tokens||0)+' tok</span></div><p>'+esc(event.preview||'No preview captured.')+'</p></article>';}).join('')||'<p class=\"empty\">No sampled MCP calls for this server in the selected sessions.</p>')+'</div>';}function renderMetadata(){var rows=(report.metadata||[]).map(function(entry,index){return'<button class=\"row-button'+(index===0?' active':'')+'\" data-meta=\"'+esc(entry[0])+'\"><span><span class=\"row-title\">'+esc(entry[0])+'</span><span class=\"meta\">Click to inspect sampled records</span></span><span class=\"row-meta\">'+fmt(tok(entry[1]))+' tok</span><span class=\"row-meta\">'+fmt(entry[1])+' ch</span></button>';}).join('')||'<div class=\"empty\">No metadata-heavy records detected.</div>';document.getElementById('panel-metadata').innerHTML='<div class=\"grid\"><section class=\"card\"><div class=\"section-head\"><div><h2>Metadata</h2><p class=\"meta\">Breakdown by transcript record type.</p></div></div>'+rows+'</section><aside class=\"card detail\" id=\"metadata-detail\"></aside></div>';Array.prototype.forEach.call(document.querySelectorAll('[data-meta]'),function(btn){btn.addEventListener('click',function(){Array.prototype.forEach.call(document.querySelectorAll('[data-meta]'),function(item){item.classList.remove('active');});btn.classList.add('active');renderMetadataDetail(btn.getAttribute('data-meta'));});});if((report.metadata||[]).length)renderMetadataDetail(report.metadata[0][0]);else document.getElementById('metadata-detail').innerHTML='<h2>Metadata Detail</h2><p class=\"empty\">Nothing to inspect yet.</p>';}function renderMetadataDetail(type){var events=(report.metadataEvents||[]).filter(function(event){return event.type===type;}).slice(0,30);document.getElementById('metadata-detail').innerHTML='<div class=\"section-head\"><div><h2>'+esc(type)+'</h2><p class=\"meta\">'+events.length+' sampled record'+(events.length===1?'':'s')+'</p></div></div><div class=\"detail-list\">'+(events.map(function(event){return'<article class=\"sample\"><div class=\"sample-top\"><span>'+esc(event.source)+' · '+esc(event.sessionTitle||event.sessionId)+'</span><span>'+fmt(event.tokens||0)+' tok</span></div><p>'+esc(event.preview||'No preview captured.')+'</p></article>';}).join('')||'<p class=\"empty\">No sampled records for this metadata type.</p>')+'</div>';}function renderSessions(){document.getElementById('panel-sessions').innerHTML=(report.sessions||[]).map(function(session,index){var catRows=Object.keys(session.categories||{}).sort(function(a,b){return session.categories[b]-session.categories[a];}).map(function(cat){var chars=session.categories[cat];return'<tr><td>'+esc(labels[cat]||cat)+'</td><td>'+fmt(tok(chars))+'</td><td>'+pc(chars,session.totalChars||0).toFixed(0)+'%</td></tr>';}).join('');var tools=(session.tools?Object.keys(session.tools):[]).sort(function(a,b){return session.tools[b]-session.tools[a];}).slice(0,8).map(function(name){return'<span class=\"badge\">'+esc(name)+' x'+session.tools[name]+'</span>';}).join('')||'<span class=\"empty\">none detected</span>';var paths=(session.paths?Object.keys(session.paths):[]).sort(function(a,b){return session.paths[b]-session.paths[a];}).slice(0,8).map(function(name){return'<span class=\"badge\">'+esc(name)+' x'+session.paths[name]+'</span>';}).join('')||'<span class=\"empty\">none detected</span>';return'<details class=\"session-card\"'+(index===0?' open':'')+'><summary><span><span class=\"eyebrow\">'+esc(session.source)+' · '+esc(session.updatedAt||'unknown time')+'</span><span class=\"session-title\">'+esc(session.title||session.sessionId)+'</span><span class=\"session-path\">'+esc(session.cwd||session.path)+'</span></span><span class=\"token-big\">'+fmt(session.tokens)+'</span></summary><div class=\"session-body\">'+bar(session.categories)+'<div class=\"grid equal\"><div><div class=\"mini-label\">Buckets</div><table class=\"table\"><tbody>'+catRows+'</tbody></table></div><div><div class=\"mini-label\">Frequent tools</div><div class=\"badge-list\">'+tools+'</div><div class=\"mini-label\">Repeated paths</div><div class=\"badge-list\">'+paths+'</div></div></div></div></details>';}).join('')||'<section class=\"card\"><p class=\"empty\">No matching sessions found.</p></section>';}function activate(name){Array.prototype.forEach.call(document.querySelectorAll('.tab'),function(tab){tab.classList.toggle('active',tab.getAttribute('data-tab')===name);});Array.prototype.forEach.call(document.querySelectorAll('.panel'),function(panel){panel.classList.toggle('active',panel.id==='panel-'+name);});if(location.hash!=='#'+name)history.replaceState(null,'','#'+name);}Array.prototype.forEach.call(document.querySelectorAll('.tab'),function(tab){tab.addEventListener('click',function(){activate(tab.getAttribute('data-tab'));});});renderStats();renderOverview();renderTools();renderMcp();renderMetadata();renderSessions();var initial=(location.hash||'#overview').slice(1);if(document.getElementById('panel-'+initial))activate(initial);})();" +
|
|
1940
|
+
"</script><script>" + overviewEnhancementScript() + "</script><script>" + insightsEnhancementScript() + "</script></body></html>";
|
|
1941
|
+
}
|
|
1942
|
+
|
|
1943
|
+
function writeJson(sessions, args, file) {
|
|
1944
|
+
fs.writeFileSync(file, JSON.stringify(buildReport(sessions, args), null, 2));
|
|
1945
|
+
}
|
|
1946
|
+
|
|
1947
|
+
function openUrl(url) {
|
|
1948
|
+
const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "cmd" : "xdg-open";
|
|
1949
|
+
const args = process.platform === "win32" ? ["/c", "start", "", url] : [url];
|
|
1950
|
+
try {
|
|
1951
|
+
childProcess.spawn(cmd, args, { detached: true, stdio: "ignore" }).unref();
|
|
1952
|
+
} catch {}
|
|
1953
|
+
}
|
|
1954
|
+
|
|
1955
|
+
function printSummary(sessions, args, file, url) {
|
|
1956
|
+
const total = sessions.reduce((sum, s) => sum + s.tokens, 0);
|
|
1957
|
+
const analysis = analyzeContext(sessions, args, readMcpServers(args.project));
|
|
1958
|
+
const topFinding = analysis.findings && analysis.findings[0];
|
|
1959
|
+
console.log("Context X-Ray: analyzed " + sessions.length + " session(s), about " + fmtTokens(total) + " observed/estimated tokens.");
|
|
1960
|
+
if (url) console.log("Open: " + url);
|
|
1961
|
+
else console.log("Report: " + file);
|
|
1962
|
+
console.log("Health: " + analysis.score + "/100 (" + analysis.scoreLabel + ")" + (topFinding ? " · " + topFinding.title : ""));
|
|
1963
|
+
if (topFinding && topFinding.evidence && topFinding.evidence.length) console.log("Evidence: " + topFinding.evidence[0]);
|
|
1964
|
+
console.log("");
|
|
1965
|
+
for (const tip of recommendations(sessions, analysis).slice(0, 4)) console.log("- " + tip);
|
|
1966
|
+
const tools = sortedEntries(aggregate(sessions).tools, 5);
|
|
1967
|
+
if (tools.length) console.log("- Frequent tools: " + tools.map((entry) => entry[0] + " x" + entry[1]).join(", "));
|
|
1968
|
+
}
|
|
1969
|
+
|
|
1970
|
+
function main() {
|
|
1971
|
+
const args = parseArgs(process.argv.slice(2));
|
|
1972
|
+
if (args.help) return help();
|
|
1973
|
+
const sessions = collectSessions(args);
|
|
1974
|
+
mkdirp(OUT_DIR);
|
|
1975
|
+
const suffix = args.format === "json" ? "json" : "html";
|
|
1976
|
+
const file = args.out || path.join(OUT_DIR, "context-xray-" + new Date().toISOString().replace(/[:.]/g, "-") + "." + suffix);
|
|
1977
|
+
mkdirp(path.dirname(file));
|
|
1978
|
+
if (args.format === "json") writeJson(sessions, args, file);
|
|
1979
|
+
else fs.writeFileSync(file, renderHtml(sessions, args));
|
|
1980
|
+
const url = args.open && args.format === "html" ? pathToFileURL(file).href : "";
|
|
1981
|
+
if (url) openUrl(url);
|
|
1982
|
+
printSummary(sessions, args, file, url);
|
|
1983
|
+
}
|
|
1984
|
+
|
|
1985
|
+
main();
|
|
1986
|
+
`;
|
|
1987
|
+
|
|
1988
|
+
export const CONTEXT_XRAY_SKILL_MD = `---
|
|
1989
|
+
name: context-xray
|
|
1990
|
+
description: >-
|
|
1991
|
+
Visualize local Codex and Claude Code context usage, open an inline/browser
|
|
1992
|
+
report, flag warnings, and suggest prompt/tooling optimizations. Use when the
|
|
1993
|
+
user types /context-xray, asks where context is going, wants recent local
|
|
1994
|
+
coding-agent trends, or wants to improve context efficiency.
|
|
1995
|
+
metadata:
|
|
1996
|
+
visibility: exported
|
|
1997
|
+
---
|
|
1998
|
+
|
|
1999
|
+
# Context X-Ray
|
|
2000
|
+
|
|
2001
|
+
Use the locally installed Context X-Ray command to visualize recent Codex and
|
|
2002
|
+
Claude Code context usage. It reads local transcript files only and does not
|
|
2003
|
+
upload transcript content.
|
|
2004
|
+
|
|
2005
|
+
Project-scoped installs write only project \`.agents\` skill and command
|
|
2006
|
+
artifacts; user-scoped installs write global Codex/Claude instructions.
|
|
2007
|
+
|
|
2008
|
+
## Run
|
|
2009
|
+
|
|
2010
|
+
Current or most recent local thread:
|
|
2011
|
+
|
|
2012
|
+
\`\`\`sh
|
|
2013
|
+
~/.agent-native/context-xray/context-xray --open
|
|
2014
|
+
\`\`\`
|
|
2015
|
+
|
|
2016
|
+
Thread picker / recent sessions:
|
|
2017
|
+
|
|
2018
|
+
\`\`\`sh
|
|
2019
|
+
~/.agent-native/context-xray/context-xray threads --open
|
|
2020
|
+
\`\`\`
|
|
2021
|
+
|
|
2022
|
+
Weekly trends:
|
|
2023
|
+
|
|
2024
|
+
\`\`\`sh
|
|
2025
|
+
~/.agent-native/context-xray/context-xray trends --since 7d --open
|
|
2026
|
+
\`\`\`
|
|
2027
|
+
|
|
2028
|
+
Exact session when the host exposes one:
|
|
2029
|
+
|
|
2030
|
+
\`\`\`sh
|
|
2031
|
+
~/.agent-native/context-xray/context-xray --session-id "$CLAUDE_CODE_SESSION_ID" --open
|
|
2032
|
+
\`\`\`
|
|
2033
|
+
|
|
2034
|
+
After running, report the link, the number of sessions analyzed, the largest
|
|
2035
|
+
context buckets, and 3-5 specific optimizations.
|
|
2036
|
+
\`--open\` opens the generated local HTML file directly and does not keep a
|
|
2037
|
+
background report server running.
|
|
2038
|
+
|
|
2039
|
+
## Interpret
|
|
2040
|
+
|
|
2041
|
+
- Treat the Overview score as a triage signal, then open Findings for evidence.
|
|
2042
|
+
- Repeated file reads: ask the agent to keep a short file-role note and reopen
|
|
2043
|
+
only when exact line numbers are needed.
|
|
2044
|
+
- Retry loops or failed tool loops: ask the agent to stop after two identical
|
|
2045
|
+
failures, summarize the error, and change strategy before rerunning.
|
|
2046
|
+
- Exploration heavy: give an inspection budget, then ask for a short
|
|
2047
|
+
implementation plan before more reading.
|
|
2048
|
+
- Cache churn or context growth: move stable instructions into skills or repo
|
|
2049
|
+
docs and continue large work from a compact handoff summary.
|
|
2050
|
+
- Tool output heavy: ask the agent to cap command output, summarize failures,
|
|
2051
|
+
and only expand logs when exact lines matter.
|
|
2052
|
+
- Metadata heavy: use Metadata/Sources drilldowns for protocol overhead, but
|
|
2053
|
+
prioritize prompt changes around user/tool/output buckets first.
|
|
2054
|
+
`;
|
|
2055
|
+
|
|
2056
|
+
export const CONTEXT_XRAY_COMMAND_MD = `---
|
|
2057
|
+
description: Visualize local Codex/Claude context usage and get optimization tips.
|
|
2058
|
+
argument-hint: [current|threads|trends|--since 7d]
|
|
2059
|
+
---
|
|
2060
|
+
|
|
2061
|
+
Run Context X-Ray locally and show the user the generated report link plus the
|
|
2062
|
+
top warnings.
|
|
2063
|
+
|
|
2064
|
+
Choose the command from the user's arguments:
|
|
2065
|
+
|
|
2066
|
+
- No arguments or \`current\`:
|
|
2067
|
+
\`~/.agent-native/context-xray/context-xray --open\`
|
|
2068
|
+
- \`threads\`:
|
|
2069
|
+
\`~/.agent-native/context-xray/context-xray threads --open\`
|
|
2070
|
+
- \`trends\`:
|
|
2071
|
+
\`~/.agent-native/context-xray/context-xray trends --since 7d --open\`
|
|
2072
|
+
|
|
2073
|
+
If \`$ARGUMENTS\` includes flags such as \`--since 24h\`, \`--last 20\`, or
|
|
2074
|
+
\`--all-projects\`, pass them through to the command. If the host exposes
|
|
2075
|
+
\`CLAUDE_CODE_SESSION_ID\`, prefer:
|
|
2076
|
+
|
|
2077
|
+
\`\`\`sh
|
|
2078
|
+
~/.agent-native/context-xray/context-xray --session-id "$CLAUDE_CODE_SESSION_ID" --open
|
|
2079
|
+
\`\`\`
|
|
2080
|
+
|
|
2081
|
+
\`--open\` opens a local HTML report file directly; there should not be a
|
|
2082
|
+
long-running server process to monitor.
|
|
2083
|
+
|
|
2084
|
+
After the command finishes, summarize:
|
|
2085
|
+
|
|
2086
|
+
- the report link
|
|
2087
|
+
- sessions analyzed
|
|
2088
|
+
- the health score and most important finding
|
|
2089
|
+
- one concrete evidence point
|
|
2090
|
+
- two or three promptable ways to improve this thread
|
|
2091
|
+
`;
|
|
2092
|
+
|
|
2093
|
+
function codexHome(): string {
|
|
2094
|
+
return process.env.CODEX_HOME?.trim() || path.join(os.homedir(), ".codex");
|
|
2095
|
+
}
|
|
2096
|
+
|
|
2097
|
+
function writeExecutable(file: string, content: string): void {
|
|
2098
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
2099
|
+
fs.writeFileSync(file, content, "utf-8");
|
|
2100
|
+
fs.chmodSync(file, 0o755);
|
|
2101
|
+
}
|
|
2102
|
+
|
|
2103
|
+
function writeFile(file: string, content: string, written: string[]): void {
|
|
2104
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
2105
|
+
fs.writeFileSync(file, content, "utf-8");
|
|
2106
|
+
written.push(file);
|
|
2107
|
+
}
|
|
2108
|
+
|
|
2109
|
+
function installProjectArtifacts(baseDir: string, written: string[]): void {
|
|
2110
|
+
writeFile(
|
|
2111
|
+
path.join(baseDir, ".agents", "skills", "context-xray", "SKILL.md"),
|
|
2112
|
+
CONTEXT_XRAY_SKILL_MD,
|
|
2113
|
+
written,
|
|
2114
|
+
);
|
|
2115
|
+
writeFile(
|
|
2116
|
+
path.join(baseDir, ".agents", "commands", "context-xray.md"),
|
|
2117
|
+
CONTEXT_XRAY_COMMAND_MD,
|
|
2118
|
+
written,
|
|
2119
|
+
);
|
|
2120
|
+
}
|
|
2121
|
+
|
|
2122
|
+
export function installLocalContextXray(
|
|
2123
|
+
options: InstallLocalContextXrayOptions,
|
|
2124
|
+
): InstallLocalContextXrayResult {
|
|
2125
|
+
const installDir = path.join(os.homedir(), ".agent-native", "context-xray");
|
|
2126
|
+
const scriptPath = path.join(installDir, "context-xray");
|
|
2127
|
+
const binPath = path.join(os.homedir(), ".local", "bin", "context-xray");
|
|
2128
|
+
const written: string[] = [];
|
|
2129
|
+
|
|
2130
|
+
if (options.dryRun) {
|
|
2131
|
+
return {
|
|
2132
|
+
commands: ["context-xray --open"],
|
|
2133
|
+
scriptPath,
|
|
2134
|
+
written,
|
|
2135
|
+
};
|
|
2136
|
+
}
|
|
2137
|
+
|
|
2138
|
+
writeExecutable(scriptPath, CONTEXT_XRAY_EXECUTABLE);
|
|
2139
|
+
written.push(scriptPath);
|
|
2140
|
+
if (process.platform === "win32") {
|
|
2141
|
+
const cmdPath = `${binPath}.cmd`;
|
|
2142
|
+
writeExecutable(
|
|
2143
|
+
cmdPath,
|
|
2144
|
+
`@echo off\r\nnode ${JSON.stringify(scriptPath)} %*\r\n`,
|
|
2145
|
+
);
|
|
2146
|
+
written.push(cmdPath);
|
|
2147
|
+
} else {
|
|
2148
|
+
writeExecutable(
|
|
2149
|
+
binPath,
|
|
2150
|
+
`#!/usr/bin/env sh\nexec ${JSON.stringify(scriptPath)} "$@"\n`,
|
|
2151
|
+
);
|
|
2152
|
+
written.push(binPath);
|
|
2153
|
+
}
|
|
2154
|
+
|
|
2155
|
+
const clientSet = new Set(options.clients);
|
|
2156
|
+
const wantsCodex = clientSet.has("codex");
|
|
2157
|
+
const wantsClaude =
|
|
2158
|
+
clientSet.has("claude-code") || clientSet.has("claude-code-cli");
|
|
2159
|
+
|
|
2160
|
+
if (options.scope === "project" && options.baseDir) {
|
|
2161
|
+
installProjectArtifacts(options.baseDir, written);
|
|
2162
|
+
} else if (wantsCodex) {
|
|
2163
|
+
writeFile(
|
|
2164
|
+
path.join(codexHome(), "skills", "context-xray", "SKILL.md"),
|
|
2165
|
+
CONTEXT_XRAY_SKILL_MD,
|
|
2166
|
+
written,
|
|
2167
|
+
);
|
|
2168
|
+
writeFile(
|
|
2169
|
+
path.join(codexHome(), "commands", "context-xray.md"),
|
|
2170
|
+
CONTEXT_XRAY_COMMAND_MD,
|
|
2171
|
+
written,
|
|
2172
|
+
);
|
|
2173
|
+
}
|
|
2174
|
+
|
|
2175
|
+
if (options.scope !== "project" && wantsClaude) {
|
|
2176
|
+
writeFile(
|
|
2177
|
+
path.join(os.homedir(), ".claude", "skills", "context-xray", "SKILL.md"),
|
|
2178
|
+
CONTEXT_XRAY_SKILL_MD,
|
|
2179
|
+
written,
|
|
2180
|
+
);
|
|
2181
|
+
writeFile(
|
|
2182
|
+
path.join(os.homedir(), ".claude", "commands", "context-xray.md"),
|
|
2183
|
+
CONTEXT_XRAY_COMMAND_MD,
|
|
2184
|
+
written,
|
|
2185
|
+
);
|
|
2186
|
+
}
|
|
2187
|
+
|
|
2188
|
+
return {
|
|
2189
|
+
commands: ["context-xray --open"],
|
|
2190
|
+
scriptPath,
|
|
2191
|
+
written,
|
|
2192
|
+
};
|
|
2193
|
+
}
|