@tonyclaw/agent-inspector 4.0.1 → 4.0.2
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/.output/backend/nitro.json +1 -1
- package/.output/cli.js +192 -109
- package/.output/server/_ssr/index.mjs +1 -1
- package/.output/server/_ssr/{router-CTHqmroT.mjs → router-BBY_zWRK.mjs} +91 -3
- package/.output/server/index.mjs +1 -1
- package/.output/ui/assets/{CompareDrawer-L3n4J-Or.js → CompareDrawer-rtNElN-B.js} +1 -1
- package/.output/ui/assets/{InspectorPet-D5o_3OZS.js → InspectorPet-QaAiuF3d.js} +1 -1
- package/.output/ui/assets/{ProxyViewerContainer-Ocagx75S.js → ProxyViewerContainer-6twNZHwl.js} +4 -4
- package/.output/ui/assets/{ReplayDialog-Bk7x1h-y.js → ReplayDialog-B7KlziO2.js} +1 -1
- package/.output/ui/assets/{RequestAnatomy-U4lzL7Sc.js → RequestAnatomy-2XVFBrW4.js} +1 -1
- package/.output/ui/assets/{ResponseView-LOWfvBLK.js → ResponseView-BULgzLVB.js} +1 -1
- package/.output/ui/assets/{StreamingChunkSequence-DPfsrrXQ.js → StreamingChunkSequence-DSkvkbns.js} +1 -1
- package/.output/ui/assets/{_sessionId-OdX2Jvt1.js → _sessionId-D2dmJi3G.js} +1 -1
- package/.output/ui/assets/{_sessionId-0MlWS-CO.js → _sessionId-DcS0s1Mr.js} +1 -1
- package/.output/ui/assets/{index-Du4qNOOt.js → index-B0TN1KKO.js} +2 -2
- package/.output/ui/assets/{index-gvFj_9K6.js → index-Bva1zHEs.js} +1 -1
- package/.output/ui/assets/{index-Ea-c8hPg.js → index-BzBExOxu.js} +1 -1
- package/.output/ui/assets/{index-zVVS53Hd.js → index-k6sRQz55.js} +1 -1
- package/.output/ui/assets/{json-viewer-C1WAMzg3.js → json-viewer-CWM4tY-j.js} +1 -1
- package/.output/ui/assets/{jszip.min-B8AKu2zm.js → jszip.min-BkWvRFlo.js} +1 -1
- package/.output/ui/index.html +1 -1
- package/package.json +5 -10
- package/src/assets/IDE/claudecode-color.svg +0 -1
- package/src/assets/IDE/codex-color.svg +0 -1
- package/src/assets/IDE/opencode.svg +0 -1
- package/src/assets/IDE/xiaomimimo.svg +0 -1
- package/src/assets/favicon.svg +0 -21
- package/src/assets/logos/alibaba.svg +0 -1
- package/src/assets/logos/anthropic.svg +0 -1
- package/src/assets/logos/claude-code.svg +0 -4
- package/src/assets/logos/deepseek.svg +0 -1
- package/src/assets/logos/minimax.jpeg +0 -0
- package/src/assets/logos/openai.svg +0 -1
- package/src/assets/logos/opencode.svg +0 -4
- package/src/assets/logos/qwen.webp +0 -0
- package/src/assets/logos/zhipuai.svg +0 -219
- package/src/backend/router.tsx +0 -36
- package/src/backend/routes/$.ts +0 -26
- package/src/backend/routes/__root.ts +0 -4
- package/src/backend/routes/api/-instances.ts +0 -7
- package/src/backend/routes/api/alerts.summary.ts +0 -28
- package/src/backend/routes/api/alerts.ts +0 -70
- package/src/backend/routes/api/config.paths.ts +0 -14
- package/src/backend/routes/api/config.ts +0 -73
- package/src/backend/routes/api/ecosystem.packages.$packageId.help.ts +0 -11
- package/src/backend/routes/api/ecosystem.packages.$packageId.install.ts +0 -11
- package/src/backend/routes/api/ecosystem.packages.$packageId.runner-presets.ts +0 -11
- package/src/backend/routes/api/ecosystem.packages.$packageId.upgrade.ts +0 -11
- package/src/backend/routes/api/ecosystem.packages.ts +0 -21
- package/src/backend/routes/api/ecosystem.recipes.$recipeId.run.ts +0 -11
- package/src/backend/routes/api/ecosystem.tasks.$taskId.ts +0 -16
- package/src/backend/routes/api/ecosystem.tasks.ts +0 -12
- package/src/backend/routes/api/groups.$groupId.evidence.ts +0 -55
- package/src/backend/routes/api/groups.$groupId.sessions.ts +0 -50
- package/src/backend/routes/api/groups.$groupId.ts +0 -93
- package/src/backend/routes/api/groups.ts +0 -79
- package/src/backend/routes/api/health.ts +0 -22
- package/src/backend/routes/api/instances.$name.connection.ts +0 -12
- package/src/backend/routes/api/instances.$name.restart.ts +0 -23
- package/src/backend/routes/api/instances.$name.start.ts +0 -18
- package/src/backend/routes/api/instances.$name.stop.ts +0 -23
- package/src/backend/routes/api/instances.$name.ts +0 -12
- package/src/backend/routes/api/instances.ts +0 -16
- package/src/backend/routes/api/knowledge.candidates.$candidateId.promote.ts +0 -32
- package/src/backend/routes/api/knowledge.candidates.$candidateId.ts +0 -50
- package/src/backend/routes/api/knowledge.candidates.ts +0 -10
- package/src/backend/routes/api/knowledge.project-context.ts +0 -29
- package/src/backend/routes/api/knowledge.search.ts +0 -62
- package/src/backend/routes/api/knowledge.sessions.$sessionId.candidates.ts +0 -26
- package/src/backend/routes/api/logs.$id.body.ts +0 -58
- package/src/backend/routes/api/logs.$id.chunks.ts +0 -37
- package/src/backend/routes/api/logs.$id.replay.ts +0 -297
- package/src/backend/routes/api/logs.$id.ts +0 -23
- package/src/backend/routes/api/logs.import.ts +0 -39
- package/src/backend/routes/api/logs.stream.ts +0 -174
- package/src/backend/routes/api/logs.ts +0 -214
- package/src/backend/routes/api/mcp.ts +0 -25
- package/src/backend/routes/api/models.ts +0 -10
- package/src/backend/routes/api/pi-agent.ts +0 -29
- package/src/backend/routes/api/providers.$providerId.model-metadata.ts +0 -173
- package/src/backend/routes/api/providers.$providerId.test.log.ts +0 -342
- package/src/backend/routes/api/providers.$providerId.ts +0 -111
- package/src/backend/routes/api/providers.export.ts +0 -44
- package/src/backend/routes/api/providers.import.ts +0 -81
- package/src/backend/routes/api/providers.scan.ts +0 -28
- package/src/backend/routes/api/providers.ts +0 -102
- package/src/backend/routes/api/runs.$runId.evidence.ts +0 -55
- package/src/backend/routes/api/runs.$runId.ts +0 -50
- package/src/backend/routes/api/runs.ts +0 -62
- package/src/backend/routes/api/runtime.ts +0 -46
- package/src/backend/routes/api/sessions.ts +0 -51
- package/src/backend/routes/api/storage.ts +0 -56
- package/src/backend/routes/livez.ts +0 -13
- package/src/backend/routes/metrics.ts +0 -14
- package/src/backend/routes/proxy/$.ts +0 -15
- package/src/backend/routes/readyz.ts +0 -18
- package/src/backend/start.ts +0 -10
- package/src/cli/alias.ts +0 -431
- package/src/cli/detect-tools.ts +0 -123
- package/src/cli/doctor.ts +0 -836
- package/src/cli/instance.ts +0 -1071
- package/src/cli/instanceApi.ts +0 -213
- package/src/cli/instanceArgs.ts +0 -301
- package/src/cli/instanceControl.ts +0 -191
- package/src/cli/instanceModel.ts +0 -276
- package/src/cli/instanceRegistry.ts +0 -731
- package/src/cli/networkHints.ts +0 -170
- package/src/cli/onboard.ts +0 -1668
- package/src/cli/onboardTarget.ts +0 -228
- package/src/cli/rustBackendPackage.ts +0 -458
- package/src/cli/startupOutput.ts +0 -16
- package/src/cli/templates/codex-skill-onboard.ts +0 -320
- package/src/cli/templates/command-onboard.ts +0 -25
- package/src/cli/templates/skill-onboard.ts +0 -625
- package/src/cli.ts +0 -1433
- package/src/components/OnboardingBanner.tsx +0 -115
- package/src/components/ProxyViewer.tsx +0 -2259
- package/src/components/ProxyViewerContainer.tsx +0 -1076
- package/src/components/alerts/AlertsDialog.tsx +0 -615
- package/src/components/clients/ClientLogo.tsx +0 -175
- package/src/components/ecosystem/AgentLabDialog.tsx +0 -947
- package/src/components/errors/SafeErrorBoundary.tsx +0 -201
- package/src/components/groups/GroupsDialog.tsx +0 -1304
- package/src/components/inspector-pet/InspectorPet.tsx +0 -398
- package/src/components/inspector-pet/InspectorPetCrab3D.tsx +0 -455
- package/src/components/inspector-pet/inspectorPetLogic.ts +0 -272
- package/src/components/pi-agent/PiAgentPanel.tsx +0 -446
- package/src/components/pi-agent/piAgentChatLogic.ts +0 -273
- package/src/components/providers/ImportWizardDialog.tsx +0 -281
- package/src/components/providers/ProviderCard.tsx +0 -347
- package/src/components/providers/ProviderForm.tsx +0 -1183
- package/src/components/providers/ProviderLogo.tsx +0 -122
- package/src/components/providers/ProviderTestResultsView.tsx +0 -734
- package/src/components/providers/ProvidersPanel.tsx +0 -714
- package/src/components/providers/SettingsDialog.tsx +0 -1042
- package/src/components/providers/providerFormCapabilities.ts +0 -36
- package/src/components/proxy-viewer/AgentTraceSummary.tsx +0 -902
- package/src/components/proxy-viewer/AnswerMarkdown.tsx +0 -51
- package/src/components/proxy-viewer/ApplicationBar.tsx +0 -123
- package/src/components/proxy-viewer/CompareDrawer.tsx +0 -984
- package/src/components/proxy-viewer/ConversationGroup.tsx +0 -208
- package/src/components/proxy-viewer/ConversationGroupList.tsx +0 -652
- package/src/components/proxy-viewer/ConversationHeader.tsx +0 -429
- package/src/components/proxy-viewer/LiveConnectionStatus.tsx +0 -186
- package/src/components/proxy-viewer/LogEntry.tsx +0 -1419
- package/src/components/proxy-viewer/LogEntryHeader.tsx +0 -596
- package/src/components/proxy-viewer/ProviderLogoStack.tsx +0 -57
- package/src/components/proxy-viewer/RawExportWarning.tsx +0 -77
- package/src/components/proxy-viewer/ReplayDialog.tsx +0 -636
- package/src/components/proxy-viewer/RequestToolsPanel.tsx +0 -299
- package/src/components/proxy-viewer/ResponseView.tsx +0 -202
- package/src/components/proxy-viewer/SessionLoadProgressBar.tsx +0 -61
- package/src/components/proxy-viewer/StreamingChunkSequence.tsx +0 -265
- package/src/components/proxy-viewer/ThreadConnector.tsx +0 -165
- package/src/components/proxy-viewer/ToolTraceEvents.tsx +0 -64
- package/src/components/proxy-viewer/TurnGroup.tsx +0 -383
- package/src/components/proxy-viewer/TurnGroupList.tsx +0 -156
- package/src/components/proxy-viewer/accessibility.ts +0 -8
- package/src/components/proxy-viewer/anatomy/RequestAnatomy.tsx +0 -887
- package/src/components/proxy-viewer/anatomy/SegmentBar.tsx +0 -222
- package/src/components/proxy-viewer/anatomy/contextIntelligence.ts +0 -737
- package/src/components/proxy-viewer/anatomy/sessionContextSummary.ts +0 -268
- package/src/components/proxy-viewer/anatomy/tokenEstimate.ts +0 -53
- package/src/components/proxy-viewer/anatomy/types.ts +0 -41
- package/src/components/proxy-viewer/anatomy/useAnatomyJump.ts +0 -114
- package/src/components/proxy-viewer/bodyHydration.ts +0 -123
- package/src/components/proxy-viewer/cacheTrend.ts +0 -50
- package/src/components/proxy-viewer/diff/DiffView.tsx +0 -321
- package/src/components/proxy-viewer/diff/computeDiff.ts +0 -178
- package/src/components/proxy-viewer/diff/index.ts +0 -3
- package/src/components/proxy-viewer/formats/anthropic/ContentBlocks.tsx +0 -194
- package/src/components/proxy-viewer/formats/anthropic/ResponseView.tsx +0 -81
- package/src/components/proxy-viewer/formats/anthropic/thinkingExtract.ts +0 -21
- package/src/components/proxy-viewer/formats/index.tsx +0 -47
- package/src/components/proxy-viewer/formats/openai/ResponseView.tsx +0 -442
- package/src/components/proxy-viewer/index.ts +0 -9
- package/src/components/proxy-viewer/lazy.ts +0 -41
- package/src/components/proxy-viewer/liveConnectionState.ts +0 -158
- package/src/components/proxy-viewer/log-formats/anthropic.ts +0 -191
- package/src/components/proxy-viewer/log-formats/index.ts +0 -23
- package/src/components/proxy-viewer/log-formats/openai.ts +0 -256
- package/src/components/proxy-viewer/log-formats/types.ts +0 -40
- package/src/components/proxy-viewer/log-formats/unknown.ts +0 -18
- package/src/components/proxy-viewer/logEntryVisibility.ts +0 -73
- package/src/components/proxy-viewer/logFocus.ts +0 -54
- package/src/components/proxy-viewer/proxyViewerContainerLogic.ts +0 -124
- package/src/components/proxy-viewer/proxyViewerLogic.ts +0 -488
- package/src/components/proxy-viewer/replayComparison.ts +0 -131
- package/src/components/proxy-viewer/requestDiff.ts +0 -277
- package/src/components/proxy-viewer/requestTools.ts +0 -220
- package/src/components/proxy-viewer/useCopyFeedback.ts +0 -38
- package/src/components/proxy-viewer/useKeyboardNavigation.ts +0 -232
- package/src/components/proxy-viewer/viewerState.ts +0 -176
- package/src/components/ui/badge.tsx +0 -47
- package/src/components/ui/button.tsx +0 -47
- package/src/components/ui/collapsible.tsx +0 -21
- package/src/components/ui/confirm-dialog.tsx +0 -51
- package/src/components/ui/crab-logo.tsx +0 -45
- package/src/components/ui/crab-variants.tsx +0 -467
- package/src/components/ui/dialog.tsx +0 -130
- package/src/components/ui/icon-trigger.ts +0 -2
- package/src/components/ui/json-viewer-bulk.ts +0 -132
- package/src/components/ui/json-viewer.tsx +0 -508
- package/src/components/ui/scroll-area.tsx +0 -54
- package/src/components/ui/select.tsx +0 -178
- package/src/components/ui/separator.tsx +0 -28
- package/src/components/ui/tabs.tsx +0 -88
- package/src/components/ui/tooltip.tsx +0 -51
- package/src/components/ui/transient-toast.tsx +0 -105
- package/src/contracts/anthropic.ts +0 -230
- package/src/contracts/index.ts +0 -59
- package/src/contracts/json.ts +0 -37
- package/src/contracts/log.ts +0 -139
- package/src/contracts/openai.ts +0 -267
- package/src/index.css +0 -11
- package/src/knowledge/candidateStore.ts +0 -94
- package/src/knowledge/distiller.ts +0 -98
- package/src/knowledge/openclawClient.ts +0 -161
- package/src/knowledge/openclawGatewayClient.ts +0 -249
- package/src/knowledge/openclawMarkdown.ts +0 -146
- package/src/knowledge/redactor.ts +0 -80
- package/src/knowledge/types.ts +0 -83
- package/src/lib/alertContract.ts +0 -76
- package/src/lib/apiClient.ts +0 -166
- package/src/lib/apiFormat.ts +0 -29
- package/src/lib/backendImplementation.ts +0 -16
- package/src/lib/basePath.ts +0 -107
- package/src/lib/browserRuntimeContract.ts +0 -124
- package/src/lib/browserRuntimeResolver.ts +0 -333
- package/src/lib/chunkLoadRecovery.ts +0 -83
- package/src/lib/claudeCodeStrip.ts +0 -98
- package/src/lib/clipboard.ts +0 -67
- package/src/lib/codingAgentEndpoints.ts +0 -54
- package/src/lib/ecosystemContract.ts +0 -130
- package/src/lib/export-logs.ts +0 -287
- package/src/lib/groupContract.ts +0 -166
- package/src/lib/instanceContract.ts +0 -239
- package/src/lib/jsonFile.ts +0 -46
- package/src/lib/logImportContract.ts +0 -12
- package/src/lib/managedInstance.ts +0 -112
- package/src/lib/mask.ts +0 -4
- package/src/lib/objectUtils.ts +0 -22
- package/src/lib/piAgentContract.ts +0 -102
- package/src/lib/providerContract.ts +0 -125
- package/src/lib/providerImportContract.ts +0 -27
- package/src/lib/providerModelMetadata.ts +0 -458
- package/src/lib/providerTestContract.ts +0 -170
- package/src/lib/providerTestPrompt.ts +0 -100
- package/src/lib/publicBasePathContext.ts +0 -137
- package/src/lib/resourceLimits.ts +0 -345
- package/src/lib/runContract.ts +0 -164
- package/src/lib/runtimeConfig.ts +0 -43
- package/src/lib/safeDiagnostic.ts +0 -38
- package/src/lib/safeFetch.ts +0 -508
- package/src/lib/serverPort.ts +0 -47
- package/src/lib/sessionInfoContract.ts +0 -78
- package/src/lib/sessionUrl.ts +0 -46
- package/src/lib/ssrfGuard.ts +0 -133
- package/src/lib/stopReason.ts +0 -130
- package/src/lib/themeMode.ts +0 -41
- package/src/lib/timeDisplay.ts +0 -22
- package/src/lib/toolTrace.ts +0 -290
- package/src/lib/uiScale.ts +0 -117
- package/src/lib/unknown.ts +0 -27
- package/src/lib/upstreamUrl.ts +0 -150
- package/src/lib/useAlerts.ts +0 -82
- package/src/lib/useGroupEvidence.ts +0 -37
- package/src/lib/useGroups.ts +0 -32
- package/src/lib/useOnboarding.ts +0 -93
- package/src/lib/useProviders.ts +0 -30
- package/src/lib/useStripConfig.ts +0 -154
- package/src/lib/useThemeMode.ts +0 -40
- package/src/lib/useUiScale.ts +0 -52
- package/src/lib/utils.ts +0 -78
- package/src/mcp/currentContext.ts +0 -65
- package/src/mcp/instanceHandlers.ts +0 -151
- package/src/mcp/loopback.ts +0 -88
- package/src/mcp/mode.ts +0 -75
- package/src/mcp/previewExtractor.ts +0 -233
- package/src/mcp/server.ts +0 -2274
- package/src/mcp/toolHandlers.ts +0 -1533
- package/src/proxy/alerts.ts +0 -664
- package/src/proxy/chunkStorage.ts +0 -123
- package/src/proxy/config.ts +0 -218
- package/src/proxy/constants.ts +0 -66
- package/src/proxy/dataDir.ts +0 -147
- package/src/proxy/ecosystemExecutionConfirmation.ts +0 -114
- package/src/proxy/ecosystemExecutionRoute.ts +0 -116
- package/src/proxy/ecosystemRegistry.ts +0 -296
- package/src/proxy/ecosystemTasks.ts +0 -415
- package/src/proxy/evidenceAnalysis.ts +0 -526
- package/src/proxy/evidenceExporter.ts +0 -235
- package/src/proxy/formats/anthropic/anthropicProvider.ts +0 -75
- package/src/proxy/formats/anthropic/handler.ts +0 -75
- package/src/proxy/formats/anthropic/index.ts +0 -13
- package/src/proxy/formats/anthropic/register.ts +0 -4
- package/src/proxy/formats/anthropic/schemas.ts +0 -6
- package/src/proxy/formats/anthropic/stream.ts +0 -205
- package/src/proxy/formats/handler.ts +0 -46
- package/src/proxy/formats/index.ts +0 -13
- package/src/proxy/formats/jsonSchema.ts +0 -2
- package/src/proxy/formats/openai/alibabaProvider.ts +0 -34
- package/src/proxy/formats/openai/codexMetadata.ts +0 -61
- package/src/proxy/formats/openai/handler.ts +0 -133
- package/src/proxy/formats/openai/index.ts +0 -33
- package/src/proxy/formats/openai/provider.ts +0 -50
- package/src/proxy/formats/openai/register.ts +0 -4
- package/src/proxy/formats/openai/schemas.ts +0 -25
- package/src/proxy/formats/openai/stream.ts +0 -600
- package/src/proxy/formats/openai/zhipuProvider.ts +0 -31
- package/src/proxy/formats/providerAdapter.ts +0 -50
- package/src/proxy/formats/providerRegistry.ts +0 -51
- package/src/proxy/formats/providers/index.ts +0 -3
- package/src/proxy/formats/registry.ts +0 -65
- package/src/proxy/groupEvidenceExporter.ts +0 -371
- package/src/proxy/groupStore.ts +0 -336
- package/src/proxy/handler.ts +0 -729
- package/src/proxy/identityProxy.ts +0 -1842
- package/src/proxy/jsonlRecovery.ts +0 -554
- package/src/proxy/logBodyChunks.ts +0 -91
- package/src/proxy/logFinalizer.ts +0 -777
- package/src/proxy/logFinalizer.worker.ts +0 -24
- package/src/proxy/logImportUpload.ts +0 -128
- package/src/proxy/logImporter.ts +0 -813
- package/src/proxy/logIndex.ts +0 -663
- package/src/proxy/logSearch.ts +0 -122
- package/src/proxy/logger.ts +0 -537
- package/src/proxy/openaiOrphanToolStrip.ts +0 -142
- package/src/proxy/platformCommands.ts +0 -258
- package/src/proxy/privateDataPath.ts +0 -419
- package/src/proxy/providerImporters.ts +0 -578
- package/src/proxy/providerScanStore.ts +0 -87
- package/src/proxy/providerSecretStore.ts +0 -216
- package/src/proxy/providers.ts +0 -998
- package/src/proxy/rawStreamCapture.ts +0 -342
- package/src/proxy/runFailures.ts +0 -94
- package/src/proxy/runStore.ts +0 -214
- package/src/proxy/runtimeAdmission.ts +0 -55
- package/src/proxy/runtimeHealth.ts +0 -221
- package/src/proxy/runtimeMetrics.ts +0 -149
- package/src/proxy/runtimeShutdown.ts +0 -157
- package/src/proxy/schemas.ts +0 -112
- package/src/proxy/sessionArchive.ts +0 -479
- package/src/proxy/sessionInfo.ts +0 -266
- package/src/proxy/sessionProcess.ts +0 -194
- package/src/proxy/sessionRuntime.ts +0 -121
- package/src/proxy/sessionSupervisor.ts +0 -305
- package/src/proxy/sessionWorkerEntry.ts +0 -26
- package/src/proxy/shutdownCoordinator.ts +0 -90
- package/src/proxy/socketTracker.ts +0 -639
- package/src/proxy/sqliteLogIndex.ts +0 -733
- package/src/proxy/storageLifecycle.ts +0 -432
- package/src/proxy/store.ts +0 -1773
- package/src/proxy/streamTiming.ts +0 -31
- package/src/proxy/toolSchemaWarnings.ts +0 -529
- package/src/proxy/uiAssetServer.ts +0 -106
- package/src/proxy/upstream.ts +0 -127
- package/src/services/alerts.ts +0 -12
- package/src/services/groups.ts +0 -119
- package/src/services/piAgent.ts +0 -421
- package/src/services/piAgentRetrieval.ts +0 -1180
- package/src/services/result.ts +0 -17
- package/src/services/runs.ts +0 -76
- package/src/ui/bootstrap.ts +0 -167
- package/src/ui/index.html +0 -38
- package/src/ui/logsStreamClient.ts +0 -20
- package/src/ui/main.tsx +0 -13
- package/src/ui/public/runtime-config.js +0 -2
- package/src/ui/router.tsx +0 -13
- package/src/ui/routes/__root.tsx +0 -69
- package/src/ui/routes/index.tsx +0 -35
- package/src/ui/routes/session/$sessionId.tsx +0 -50
- package/src/ui/runtimeDiscovery.ts +0 -50
|
@@ -1,342 +0,0 @@
|
|
|
1
|
-
import assert from "node:assert/strict";
|
|
2
|
-
import { Buffer } from "node:buffer";
|
|
3
|
-
import { randomUUID } from "node:crypto";
|
|
4
|
-
import { open, unlink, type FileHandle } from "node:fs/promises";
|
|
5
|
-
import { join } from "node:path";
|
|
6
|
-
import { getDataDir } from "./dataDir";
|
|
7
|
-
import {
|
|
8
|
-
DEFAULT_RUNTIME_BUDGET_POLICY,
|
|
9
|
-
type RuntimeBudgetPolicy,
|
|
10
|
-
runtimeBudgetPolicySnapshot,
|
|
11
|
-
} from "~/lib/resourceLimits";
|
|
12
|
-
import {
|
|
13
|
-
ensurePrivateDirectorySync,
|
|
14
|
-
PRIVATE_FILE_MODE,
|
|
15
|
-
securePrivateFileSync,
|
|
16
|
-
} from "./privateDataPath";
|
|
17
|
-
|
|
18
|
-
const DEFAULT_MEMORY_LIMIT_BYTES = 1024 * 1024;
|
|
19
|
-
const FILE_WRITE_BATCH_BYTES = 64 * 1024;
|
|
20
|
-
const DEFAULT_QUEUE_MAX_MESSAGES = 256;
|
|
21
|
-
const DEFAULT_QUEUE_MAX_BYTES = 8 * 1024 * 1024;
|
|
22
|
-
const DEFAULT_GLOBAL_QUEUE_MAX_MESSAGES = 4096;
|
|
23
|
-
const DEFAULT_GLOBAL_QUEUE_MAX_BYTES = 64 * 1024 * 1024;
|
|
24
|
-
const activeCaptures = new Set<RawStreamCapture>();
|
|
25
|
-
|
|
26
|
-
export type RawStreamCaptureGapReason = "capacity-exceeded" | "pipeline-closed" | "write-failed";
|
|
27
|
-
|
|
28
|
-
export type RawStreamCaptureStatus = {
|
|
29
|
-
incomplete: boolean;
|
|
30
|
-
droppedChunks: number;
|
|
31
|
-
droppedBytes: number;
|
|
32
|
-
reason: RawStreamCaptureGapReason | null;
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
export type RawStreamCaptureOptions = {
|
|
36
|
-
memoryLimitBytes?: number;
|
|
37
|
-
queueMaxMessages?: number;
|
|
38
|
-
queueMaxBytes?: number;
|
|
39
|
-
globalBudget?: RawStreamObservationBudget;
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
export function rawStreamCaptureOptionsFromBudgetPolicy(
|
|
43
|
-
policy: RuntimeBudgetPolicy = DEFAULT_RUNTIME_BUDGET_POLICY,
|
|
44
|
-
): Pick<RawStreamCaptureOptions, "queueMaxMessages" | "queueMaxBytes"> {
|
|
45
|
-
const snapshot = runtimeBudgetPolicySnapshot(policy);
|
|
46
|
-
return {
|
|
47
|
-
queueMaxMessages: snapshot.queues.rawStreamMessages,
|
|
48
|
-
queueMaxBytes: snapshot.queues.rawStreamBytes,
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export type RawStreamSource =
|
|
53
|
-
| {
|
|
54
|
-
type: "memory";
|
|
55
|
-
rawStream: string;
|
|
56
|
-
}
|
|
57
|
-
| {
|
|
58
|
-
type: "file";
|
|
59
|
-
path: string;
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
function resolveMemoryLimitBytes(): number {
|
|
63
|
-
const raw = process.env["AGENT_INSPECTOR_STREAM_MEMORY_LIMIT_BYTES"];
|
|
64
|
-
if (raw === undefined || raw === "") return DEFAULT_MEMORY_LIMIT_BYTES;
|
|
65
|
-
const parsed = Number(raw);
|
|
66
|
-
if (!Number.isInteger(parsed) || parsed < 0) return DEFAULT_MEMORY_LIMIT_BYTES;
|
|
67
|
-
return parsed;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function resolvePositiveIntegerEnv(name: string, fallback: number): number {
|
|
71
|
-
const raw = process.env[name];
|
|
72
|
-
if (raw === undefined || raw === "") return fallback;
|
|
73
|
-
const parsed = Number(raw);
|
|
74
|
-
if (!Number.isSafeInteger(parsed) || parsed < 1) return fallback;
|
|
75
|
-
return parsed;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
function defaultQueueMaxMessages(): number {
|
|
79
|
-
return resolvePositiveIntegerEnv(
|
|
80
|
-
"AGENT_INSPECTOR_OBSERVATION_QUEUE_MESSAGES",
|
|
81
|
-
DEFAULT_QUEUE_MAX_MESSAGES,
|
|
82
|
-
);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
function defaultQueueMaxBytes(): number {
|
|
86
|
-
return resolvePositiveIntegerEnv(
|
|
87
|
-
"AGENT_INSPECTOR_OBSERVATION_QUEUE_BYTES",
|
|
88
|
-
DEFAULT_QUEUE_MAX_BYTES,
|
|
89
|
-
);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
export class RawStreamObservationBudget {
|
|
93
|
-
private messages = 0;
|
|
94
|
-
private bytes = 0;
|
|
95
|
-
|
|
96
|
-
constructor(
|
|
97
|
-
private readonly maxMessages: number,
|
|
98
|
-
private readonly maxBytes: number,
|
|
99
|
-
) {}
|
|
100
|
-
|
|
101
|
-
tryAcquire(bytes: number): (() => void) | null {
|
|
102
|
-
if (this.messages + 1 > this.maxMessages || this.bytes + bytes > this.maxBytes) return null;
|
|
103
|
-
this.messages += 1;
|
|
104
|
-
this.bytes += bytes;
|
|
105
|
-
let released = false;
|
|
106
|
-
return () => {
|
|
107
|
-
if (released) return;
|
|
108
|
-
released = true;
|
|
109
|
-
this.messages -= 1;
|
|
110
|
-
this.bytes -= bytes;
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
const globalObservationBudget = new RawStreamObservationBudget(
|
|
116
|
-
resolvePositiveIntegerEnv(
|
|
117
|
-
"AGENT_INSPECTOR_OBSERVATION_GLOBAL_QUEUE_MESSAGES",
|
|
118
|
-
DEFAULT_GLOBAL_QUEUE_MAX_MESSAGES,
|
|
119
|
-
),
|
|
120
|
-
resolvePositiveIntegerEnv(
|
|
121
|
-
"AGENT_INSPECTOR_OBSERVATION_GLOBAL_QUEUE_BYTES",
|
|
122
|
-
DEFAULT_GLOBAL_QUEUE_MAX_BYTES,
|
|
123
|
-
),
|
|
124
|
-
);
|
|
125
|
-
|
|
126
|
-
export function getRawStreamDir(): string {
|
|
127
|
-
return join(getDataDir(), "raw-streams");
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
function createRawStreamPath(): string {
|
|
131
|
-
return join(getRawStreamDir(), `${Date.now()}-${randomUUID()}.stream.tmp`);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
export class RawStreamCapture {
|
|
135
|
-
private readonly memoryLimitBytes: number;
|
|
136
|
-
private readonly queueMaxMessages: number;
|
|
137
|
-
private readonly queueMaxBytes: number;
|
|
138
|
-
private readonly globalBudget: RawStreamObservationBudget;
|
|
139
|
-
private chunks: string[] = [];
|
|
140
|
-
private byteLength = 0;
|
|
141
|
-
private filePath: string | null = null;
|
|
142
|
-
private fileHandle: FileHandle | null = null;
|
|
143
|
-
private pendingFileChunks: string[] = [];
|
|
144
|
-
private pendingFileBytes = 0;
|
|
145
|
-
private operation = Promise.resolve();
|
|
146
|
-
private accepting = true;
|
|
147
|
-
private closed = false;
|
|
148
|
-
private sawChunk = false;
|
|
149
|
-
private queuedMessages = 0;
|
|
150
|
-
private queuedBytes = 0;
|
|
151
|
-
private incomplete = false;
|
|
152
|
-
private storageFailed = false;
|
|
153
|
-
private droppedChunks = 0;
|
|
154
|
-
private droppedBytes = 0;
|
|
155
|
-
private gapReason: RawStreamCaptureGapReason | null = null;
|
|
156
|
-
|
|
157
|
-
constructor(options: RawStreamCaptureOptions = {}) {
|
|
158
|
-
this.memoryLimitBytes = options.memoryLimitBytes ?? resolveMemoryLimitBytes();
|
|
159
|
-
this.queueMaxMessages = options.queueMaxMessages ?? defaultQueueMaxMessages();
|
|
160
|
-
this.queueMaxBytes = options.queueMaxBytes ?? defaultQueueMaxBytes();
|
|
161
|
-
this.globalBudget = options.globalBudget ?? globalObservationBudget;
|
|
162
|
-
activeCaptures.add(this);
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
get hasChunks(): boolean {
|
|
166
|
-
return this.sawChunk;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
get status(): RawStreamCaptureStatus {
|
|
170
|
-
return {
|
|
171
|
-
incomplete: this.incomplete,
|
|
172
|
-
droppedChunks: this.droppedChunks,
|
|
173
|
-
droppedBytes: this.droppedBytes,
|
|
174
|
-
reason: this.gapReason,
|
|
175
|
-
};
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
recordDroppedObservation(reason: RawStreamCaptureGapReason, observedBytes: number): void {
|
|
179
|
-
const safeObservedBytes =
|
|
180
|
-
Number.isSafeInteger(observedBytes) && observedBytes >= 0 ? observedBytes : 0;
|
|
181
|
-
this.recordGap(reason, safeObservedBytes, safeObservedBytes > 0);
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
async append(text: string): Promise<void> {
|
|
185
|
-
if (text === "") return;
|
|
186
|
-
const next = this.operation.then(() => this.appendInternal(text));
|
|
187
|
-
this.operation = next.catch(() => undefined);
|
|
188
|
-
return next;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
/**
|
|
192
|
-
* Submit an observation without waiting for file I/O. Once capacity is exceeded, capture remains
|
|
193
|
-
* incomplete and all later chunks are counted as dropped so the persisted value is an honest
|
|
194
|
-
* prefix rather than a misleading stream with an unmarked hole.
|
|
195
|
-
*/
|
|
196
|
-
tryAppend(text: string, observedBytes: number): boolean {
|
|
197
|
-
const safeObservedBytes =
|
|
198
|
-
Number.isSafeInteger(observedBytes) && observedBytes >= 0 ? observedBytes : 0;
|
|
199
|
-
const queuedBytes = Math.max(safeObservedBytes, Buffer.byteLength(text, "utf-8"));
|
|
200
|
-
|
|
201
|
-
if (!this.accepting || this.closed) {
|
|
202
|
-
this.recordGap("pipeline-closed", safeObservedBytes, safeObservedBytes > 0 || text !== "");
|
|
203
|
-
return false;
|
|
204
|
-
}
|
|
205
|
-
if (this.incomplete) {
|
|
206
|
-
this.recordGap("capacity-exceeded", safeObservedBytes, safeObservedBytes > 0 || text !== "");
|
|
207
|
-
return false;
|
|
208
|
-
}
|
|
209
|
-
if (text === "") return true;
|
|
210
|
-
if (
|
|
211
|
-
this.queuedMessages + 1 > this.queueMaxMessages ||
|
|
212
|
-
this.queuedBytes + queuedBytes > this.queueMaxBytes
|
|
213
|
-
) {
|
|
214
|
-
this.recordGap("capacity-exceeded", safeObservedBytes, true);
|
|
215
|
-
return false;
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
const releaseGlobal = this.globalBudget.tryAcquire(queuedBytes);
|
|
219
|
-
if (releaseGlobal === null) {
|
|
220
|
-
this.recordGap("capacity-exceeded", safeObservedBytes, true);
|
|
221
|
-
return false;
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
this.queuedMessages += 1;
|
|
225
|
-
this.queuedBytes += queuedBytes;
|
|
226
|
-
this.operation = this.operation
|
|
227
|
-
.then(async () => {
|
|
228
|
-
if (this.storageFailed) {
|
|
229
|
-
this.recordGap("write-failed", safeObservedBytes, true);
|
|
230
|
-
return;
|
|
231
|
-
}
|
|
232
|
-
await this.appendInternal(text);
|
|
233
|
-
})
|
|
234
|
-
.catch(() => {
|
|
235
|
-
this.storageFailed = true;
|
|
236
|
-
this.recordGap("write-failed", safeObservedBytes, true);
|
|
237
|
-
})
|
|
238
|
-
.finally(() => {
|
|
239
|
-
this.queuedMessages -= 1;
|
|
240
|
-
this.queuedBytes -= queuedBytes;
|
|
241
|
-
releaseGlobal();
|
|
242
|
-
});
|
|
243
|
-
return true;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
async finalize(): Promise<RawStreamSource> {
|
|
247
|
-
this.accepting = false;
|
|
248
|
-
const next = this.operation.then(() => this.finalizeInternal());
|
|
249
|
-
this.operation = next.then(
|
|
250
|
-
() => undefined,
|
|
251
|
-
() => undefined,
|
|
252
|
-
);
|
|
253
|
-
return next.finally(() => activeCaptures.delete(this));
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
private async appendInternal(text: string): Promise<void> {
|
|
257
|
-
if (this.closed) return;
|
|
258
|
-
this.sawChunk = true;
|
|
259
|
-
|
|
260
|
-
const nextBytes = Buffer.byteLength(text, "utf-8");
|
|
261
|
-
if (this.filePath === null && this.byteLength + nextBytes <= this.memoryLimitBytes) {
|
|
262
|
-
this.chunks.push(text);
|
|
263
|
-
this.byteLength += nextBytes;
|
|
264
|
-
return;
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
if (this.filePath === null) {
|
|
268
|
-
await this.spillToFile();
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
if (this.filePath !== null) {
|
|
272
|
-
this.pendingFileChunks.push(text);
|
|
273
|
-
this.pendingFileBytes += nextBytes;
|
|
274
|
-
this.byteLength += nextBytes;
|
|
275
|
-
if (this.pendingFileBytes >= FILE_WRITE_BATCH_BYTES) await this.flushFileBuffer();
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
private recordGap(
|
|
280
|
-
reason: RawStreamCaptureGapReason,
|
|
281
|
-
observedBytes: number,
|
|
282
|
-
countChunk: boolean,
|
|
283
|
-
): void {
|
|
284
|
-
this.incomplete = true;
|
|
285
|
-
this.gapReason ??= reason;
|
|
286
|
-
if (countChunk) this.droppedChunks += 1;
|
|
287
|
-
this.droppedBytes += observedBytes;
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
private async finalizeInternal(): Promise<RawStreamSource> {
|
|
291
|
-
if (!this.closed) {
|
|
292
|
-
this.closed = true;
|
|
293
|
-
await this.flushFileBuffer();
|
|
294
|
-
if (this.fileHandle !== null) {
|
|
295
|
-
const handle = this.fileHandle;
|
|
296
|
-
this.fileHandle = null;
|
|
297
|
-
try {
|
|
298
|
-
await handle.sync();
|
|
299
|
-
} finally {
|
|
300
|
-
await handle.close();
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
if (this.filePath !== null) {
|
|
305
|
-
return { type: "file", path: this.filePath };
|
|
306
|
-
}
|
|
307
|
-
return { type: "memory", rawStream: this.chunks.join("") };
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
private async spillToFile(): Promise<void> {
|
|
311
|
-
const path = createRawStreamPath();
|
|
312
|
-
assert(ensurePrivateDirectorySync(getRawStreamDir()), "Could not secure raw-stream directory");
|
|
313
|
-
const handle = await open(path, "wx", PRIVATE_FILE_MODE);
|
|
314
|
-
let committed = false;
|
|
315
|
-
try {
|
|
316
|
-
const memoryText = this.chunks.join("");
|
|
317
|
-
if (memoryText.length > 0) await handle.write(memoryText, undefined, "utf-8");
|
|
318
|
-
assert(securePrivateFileSync(path), "Could not secure raw-stream file");
|
|
319
|
-
this.filePath = path;
|
|
320
|
-
this.fileHandle = handle;
|
|
321
|
-
this.chunks = [];
|
|
322
|
-
committed = true;
|
|
323
|
-
} finally {
|
|
324
|
-
if (!committed) {
|
|
325
|
-
await handle.close().catch(() => undefined);
|
|
326
|
-
await unlink(path).catch(() => undefined);
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
private async flushFileBuffer(): Promise<void> {
|
|
332
|
-
if (this.fileHandle === null || this.pendingFileChunks.length === 0) return;
|
|
333
|
-
const text = this.pendingFileChunks.join("");
|
|
334
|
-
this.pendingFileChunks = [];
|
|
335
|
-
this.pendingFileBytes = 0;
|
|
336
|
-
await this.fileHandle.write(text, undefined, "utf-8");
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
export async function flushAllRawStreamCaptures(): Promise<void> {
|
|
341
|
-
await Promise.all([...activeCaptures].map((capture) => capture.finalize()));
|
|
342
|
-
}
|
package/src/proxy/runFailures.ts
DELETED
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
EvidenceExportResultSchema,
|
|
3
|
-
type EvidenceExportResult,
|
|
4
|
-
type FailureClassification,
|
|
5
|
-
type InspectorRun,
|
|
6
|
-
type InspectorRunEvidence,
|
|
7
|
-
type JenkinsReport,
|
|
8
|
-
type RecentFailure,
|
|
9
|
-
type RecentFailuresResponse,
|
|
10
|
-
} from "../lib/runContract";
|
|
11
|
-
import { readJsonFile } from "../lib/jsonFile";
|
|
12
|
-
import { listRuns } from "./runStore";
|
|
13
|
-
|
|
14
|
-
export const RECENT_FAILURES_DEFAULT_LIMIT = 5;
|
|
15
|
-
export const RECENT_FAILURES_MAX_LIMIT = 20;
|
|
16
|
-
|
|
17
|
-
export function clampRecentFailuresLimit(value: number): number {
|
|
18
|
-
if (!Number.isInteger(value) || value < 1) return RECENT_FAILURES_DEFAULT_LIMIT;
|
|
19
|
-
return Math.min(value, RECENT_FAILURES_MAX_LIMIT);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function readEvidenceDocument(run: InspectorRun): EvidenceExportResult | null {
|
|
23
|
-
const path = run.evidence?.jsonPath;
|
|
24
|
-
if (path === undefined) return null;
|
|
25
|
-
const result = readJsonFile(path, EvidenceExportResultSchema);
|
|
26
|
-
return result.ok ? result.data : null;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function syntheticFailedClassification(run: InspectorRun): FailureClassification {
|
|
30
|
-
return {
|
|
31
|
-
outcome: "failure",
|
|
32
|
-
severity: "high",
|
|
33
|
-
category: "unknown-failure",
|
|
34
|
-
summary: `Run ${run.id} is marked ${run.status}, but no exported evidence document was available.`,
|
|
35
|
-
evidenceLogIds: [],
|
|
36
|
-
hints: [
|
|
37
|
-
"Call inspector_export_evidence for this run to collect session metrics, failing log ids, and artifact paths.",
|
|
38
|
-
"Confirm the run's sessionId matches the coding agent traffic captured by Inspector.",
|
|
39
|
-
],
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function shouldIncludeRun(
|
|
44
|
-
run: InspectorRun,
|
|
45
|
-
document: EvidenceExportResult | null,
|
|
46
|
-
classification: FailureClassification,
|
|
47
|
-
): boolean {
|
|
48
|
-
if (run.status === "failed" || run.status === "cancelled") return true;
|
|
49
|
-
if (document === null) return false;
|
|
50
|
-
return classification.outcome !== "success";
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function evidenceFor(
|
|
54
|
-
run: InspectorRun,
|
|
55
|
-
document: EvidenceExportResult | null,
|
|
56
|
-
): InspectorRunEvidence | null {
|
|
57
|
-
return document?.evidence ?? run.evidence;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function jenkinsReportFor(document: EvidenceExportResult | null): JenkinsReport | null {
|
|
61
|
-
return document?.jenkinsReport ?? null;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
function failureFromRun(run: InspectorRun): RecentFailure | null {
|
|
65
|
-
const document = readEvidenceDocument(run);
|
|
66
|
-
const classification =
|
|
67
|
-
document === null ? syntheticFailedClassification(run) : document.classification;
|
|
68
|
-
if (!shouldIncludeRun(run, document, classification)) return null;
|
|
69
|
-
|
|
70
|
-
const evidence = evidenceFor(run, document);
|
|
71
|
-
return {
|
|
72
|
-
run,
|
|
73
|
-
classification,
|
|
74
|
-
evidence,
|
|
75
|
-
jenkinsReport: jenkinsReportFor(document),
|
|
76
|
-
sessionId: run.sessionId,
|
|
77
|
-
inspectorUrl: document?.session?.inspectorUrl ?? null,
|
|
78
|
-
evidenceMarkdownPath: evidence?.markdownPath ?? null,
|
|
79
|
-
updatedAt: evidence?.exportedAt ?? run.updatedAt,
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
export function listRecentFailures(limit: number): RecentFailuresResponse {
|
|
84
|
-
const failures = listRuns()
|
|
85
|
-
.map(failureFromRun)
|
|
86
|
-
.filter((failure): failure is RecentFailure => failure !== null)
|
|
87
|
-
.sort((left, right) => right.updatedAt.localeCompare(left.updatedAt));
|
|
88
|
-
const clampedLimit = clampRecentFailuresLimit(limit);
|
|
89
|
-
return {
|
|
90
|
-
failures: failures.slice(0, clampedLimit),
|
|
91
|
-
total: failures.length,
|
|
92
|
-
limit: clampedLimit,
|
|
93
|
-
};
|
|
94
|
-
}
|
package/src/proxy/runStore.ts
DELETED
|
@@ -1,214 +0,0 @@
|
|
|
1
|
-
import assert from "node:assert/strict";
|
|
2
|
-
import { writeFileSync } from "node:fs";
|
|
3
|
-
import { randomUUID } from "node:crypto";
|
|
4
|
-
import { dirname, join } from "node:path";
|
|
5
|
-
import { z } from "zod";
|
|
6
|
-
import {
|
|
7
|
-
CreateInspectorRunInputSchema,
|
|
8
|
-
InspectorRunSchema,
|
|
9
|
-
UpdateInspectorRunInputSchema,
|
|
10
|
-
type CreateInspectorRunInput,
|
|
11
|
-
type InspectorRun,
|
|
12
|
-
type InspectorRunEvidence,
|
|
13
|
-
type UpdateInspectorRunInput,
|
|
14
|
-
} from "../lib/runContract";
|
|
15
|
-
import { getDataDir } from "./dataDir";
|
|
16
|
-
import { addGroupSession, getGroup } from "./groupStore";
|
|
17
|
-
import { readJsonFile } from "../lib/jsonFile";
|
|
18
|
-
import {
|
|
19
|
-
ensurePrivateDirectorySync,
|
|
20
|
-
PRIVATE_FILE_MODE,
|
|
21
|
-
securePrivateFileSync,
|
|
22
|
-
} from "./privateDataPath";
|
|
23
|
-
|
|
24
|
-
const RunsFileSchema = z.object({
|
|
25
|
-
runs: z.array(InspectorRunSchema),
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
function runsFilePath(): string {
|
|
29
|
-
return join(getDataDir(), "runs.json");
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function nowIso(): string {
|
|
33
|
-
return new Date().toISOString();
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function stableId(input: string): string {
|
|
37
|
-
return input
|
|
38
|
-
.trim()
|
|
39
|
-
.replace(/[^A-Za-z0-9_.-]+/g, "-")
|
|
40
|
-
.replace(/^-+|-+$/g, "")
|
|
41
|
-
.slice(0, 80);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function createRunId(): string {
|
|
45
|
-
return `run_${Date.now().toString(36)}_${randomUUID().slice(0, 8)}`;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function normalizeRunId(input: string | undefined): string {
|
|
49
|
-
if (input === undefined) return createRunId();
|
|
50
|
-
const normalized = stableId(input);
|
|
51
|
-
return normalized.length > 0 ? normalized : createRunId();
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
function normalizeSessionId(input: string | undefined, runId: string): string {
|
|
55
|
-
if (input === undefined || input.trim().length === 0) return runId;
|
|
56
|
-
return input.trim();
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
function normalizeGroupId(input: string | null | undefined): string | null {
|
|
60
|
-
if (input === undefined || input === null) return null;
|
|
61
|
-
const normalized = stableId(input);
|
|
62
|
-
return normalized.length > 0 ? normalized : null;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
function metadataText(metadata: InspectorRun["metadata"], key: string): string | undefined {
|
|
66
|
-
const value = metadata[key];
|
|
67
|
-
if (typeof value !== "string") return undefined;
|
|
68
|
-
const trimmed = value.trim();
|
|
69
|
-
return trimmed.length > 0 ? trimmed : undefined;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function optionalText(value: string | null): string | undefined {
|
|
73
|
-
return value ?? undefined;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
function autoGroupMemberId(runId: string): string {
|
|
77
|
-
return stableId(`run-${runId}`);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
function runHasGroupMember(run: InspectorRun): boolean {
|
|
81
|
-
if (run.groupId === null) return false;
|
|
82
|
-
const group = getGroup(run.groupId);
|
|
83
|
-
if (group === null) return false;
|
|
84
|
-
return group.members.some((member) => member.id === autoGroupMemberId(run.id));
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
function syncRunGroupMember(run: InspectorRun, createIfMissing: boolean): void {
|
|
88
|
-
if (run.groupId === null) return;
|
|
89
|
-
if (!createIfMissing && !runHasGroupMember(run)) return;
|
|
90
|
-
|
|
91
|
-
addGroupSession(run.groupId, {
|
|
92
|
-
memberId: autoGroupMemberId(run.id),
|
|
93
|
-
sessionId: run.sessionId,
|
|
94
|
-
runId: run.id,
|
|
95
|
-
label: run.title,
|
|
96
|
-
agent: optionalText(run.agent),
|
|
97
|
-
provider: metadataText(run.metadata, "provider") ?? metadataText(run.metadata, "providerName"),
|
|
98
|
-
model: metadataText(run.metadata, "model") ?? metadataText(run.metadata, "modelName"),
|
|
99
|
-
status: run.status,
|
|
100
|
-
metadata: run.metadata,
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
function readRuns(): InspectorRun[] {
|
|
105
|
-
const result = readJsonFile(runsFilePath(), RunsFileSchema);
|
|
106
|
-
return result.ok ? result.data.runs : [];
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
function writeRuns(runs: readonly InspectorRun[]): void {
|
|
110
|
-
const filePath = runsFilePath();
|
|
111
|
-
assert(ensurePrivateDirectorySync(dirname(filePath)), "Could not secure runs directory");
|
|
112
|
-
writeFileSync(filePath, `${JSON.stringify({ runs }, null, 2)}\n`, {
|
|
113
|
-
encoding: "utf8",
|
|
114
|
-
mode: PRIVATE_FILE_MODE,
|
|
115
|
-
});
|
|
116
|
-
assert(securePrivateFileSync(filePath), "Could not secure runs file");
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
export function listRuns(): InspectorRun[] {
|
|
120
|
-
return readRuns().sort((left, right) => right.updatedAt.localeCompare(left.updatedAt));
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
export function getRun(runId: string): InspectorRun | null {
|
|
124
|
-
return readRuns().find((run) => run.id === runId) ?? null;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
export function createRun(input: CreateInspectorRunInput): InspectorRun {
|
|
128
|
-
const parsed = CreateInspectorRunInputSchema.parse(input);
|
|
129
|
-
const runId = normalizeRunId(parsed?.runId);
|
|
130
|
-
const existing = getRun(runId);
|
|
131
|
-
if (existing !== null) return existing;
|
|
132
|
-
|
|
133
|
-
const timestamp = nowIso();
|
|
134
|
-
const sessionId = normalizeSessionId(parsed?.sessionId, runId);
|
|
135
|
-
const groupId = normalizeGroupId(parsed?.groupId);
|
|
136
|
-
const run: InspectorRun = {
|
|
137
|
-
id: runId,
|
|
138
|
-
sessionId,
|
|
139
|
-
groupId,
|
|
140
|
-
title: parsed?.title ?? `Inspector Run ${runId}`,
|
|
141
|
-
task: parsed?.task ?? null,
|
|
142
|
-
project: parsed?.project ?? null,
|
|
143
|
-
agent: parsed?.agent ?? null,
|
|
144
|
-
status: parsed?.status ?? "created",
|
|
145
|
-
tags: parsed?.tags ?? [],
|
|
146
|
-
metadata: parsed?.metadata ?? {},
|
|
147
|
-
evidence: null,
|
|
148
|
-
createdAt: timestamp,
|
|
149
|
-
updatedAt: timestamp,
|
|
150
|
-
};
|
|
151
|
-
|
|
152
|
-
writeRuns([...readRuns(), run]);
|
|
153
|
-
syncRunGroupMember(run, parsed?.sessionId !== undefined);
|
|
154
|
-
return run;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
export function updateRunEvidence(
|
|
158
|
-
runId: string,
|
|
159
|
-
evidence: InspectorRunEvidence,
|
|
160
|
-
): InspectorRun | null {
|
|
161
|
-
const runs = readRuns();
|
|
162
|
-
let updatedRun: InspectorRun | null = null;
|
|
163
|
-
const updatedRuns = runs.map((run) => {
|
|
164
|
-
if (run.id !== runId) return run;
|
|
165
|
-
updatedRun = {
|
|
166
|
-
...run,
|
|
167
|
-
evidence,
|
|
168
|
-
updatedAt: evidence.exportedAt,
|
|
169
|
-
};
|
|
170
|
-
return updatedRun;
|
|
171
|
-
});
|
|
172
|
-
if (updatedRun === null) return null;
|
|
173
|
-
writeRuns(updatedRuns);
|
|
174
|
-
return updatedRun;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
export function updateRun(runId: string, input: UpdateInspectorRunInput): InspectorRun | null {
|
|
178
|
-
const parsed = UpdateInspectorRunInputSchema.parse(input);
|
|
179
|
-
const runs = readRuns();
|
|
180
|
-
const run = runs.find((candidate) => candidate.id === runId) ?? null;
|
|
181
|
-
const updatedAt = nowIso();
|
|
182
|
-
|
|
183
|
-
if (run === null) return null;
|
|
184
|
-
|
|
185
|
-
const updatedRun: InspectorRun = {
|
|
186
|
-
...run,
|
|
187
|
-
sessionId:
|
|
188
|
-
parsed.sessionId === undefined ? run.sessionId : normalizeSessionId(parsed.sessionId, run.id),
|
|
189
|
-
groupId: parsed.groupId === undefined ? run.groupId : normalizeGroupId(parsed.groupId),
|
|
190
|
-
title: parsed.title ?? run.title,
|
|
191
|
-
task: parsed.task === undefined ? run.task : parsed.task,
|
|
192
|
-
project: parsed.project === undefined ? run.project : parsed.project,
|
|
193
|
-
agent: parsed.agent === undefined ? run.agent : parsed.agent,
|
|
194
|
-
status: parsed.status ?? run.status,
|
|
195
|
-
tags: parsed.tags ?? run.tags,
|
|
196
|
-
metadata:
|
|
197
|
-
parsed.metadata === undefined ? run.metadata : { ...run.metadata, ...parsed.metadata },
|
|
198
|
-
updatedAt,
|
|
199
|
-
};
|
|
200
|
-
const updatedRuns = runs.map((candidate) => (candidate.id === runId ? updatedRun : candidate));
|
|
201
|
-
|
|
202
|
-
writeRuns(updatedRuns);
|
|
203
|
-
syncRunGroupMember(
|
|
204
|
-
updatedRun,
|
|
205
|
-
parsed.sessionId !== undefined ||
|
|
206
|
-
parsed.groupId !== undefined ||
|
|
207
|
-
updatedRun.sessionId !== updatedRun.id,
|
|
208
|
-
);
|
|
209
|
-
return updatedRun;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
export function _resetRunsForTests(): void {
|
|
213
|
-
writeRuns([]);
|
|
214
|
-
}
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import { MAX_ACTIVE_PROXY_REQUESTS } from "../lib/resourceLimits";
|
|
2
|
-
|
|
3
|
-
export type RuntimeRequestLease = {
|
|
4
|
-
release: () => void;
|
|
5
|
-
};
|
|
6
|
-
|
|
7
|
-
let acceptingRequests = true;
|
|
8
|
-
let activeRequests = 0;
|
|
9
|
-
const drainWaiters = new Set<() => void>();
|
|
10
|
-
|
|
11
|
-
function notifyDrained(): void {
|
|
12
|
-
if (activeRequests !== 0) return;
|
|
13
|
-
for (const resolve of drainWaiters) resolve();
|
|
14
|
-
drainWaiters.clear();
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export function acquireRuntimeRequest(): RuntimeRequestLease | null {
|
|
18
|
-
if (!acceptingRequests || activeRequests >= MAX_ACTIVE_PROXY_REQUESTS) return null;
|
|
19
|
-
activeRequests += 1;
|
|
20
|
-
let released = false;
|
|
21
|
-
return {
|
|
22
|
-
release(): void {
|
|
23
|
-
if (released) return;
|
|
24
|
-
released = true;
|
|
25
|
-
activeRequests = Math.max(0, activeRequests - 1);
|
|
26
|
-
notifyDrained();
|
|
27
|
-
},
|
|
28
|
-
};
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export function stopRuntimeAdmission(): void {
|
|
32
|
-
acceptingRequests = false;
|
|
33
|
-
notifyDrained();
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export async function waitForRuntimeTrafficDrain(): Promise<void> {
|
|
37
|
-
if (activeRequests === 0) return;
|
|
38
|
-
await new Promise<void>((resolve) => {
|
|
39
|
-
drainWaiters.add(resolve);
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export function getRuntimeAdmissionState(): {
|
|
44
|
-
acceptingRequests: boolean;
|
|
45
|
-
activeRequests: number;
|
|
46
|
-
capacity: number;
|
|
47
|
-
} {
|
|
48
|
-
return { acceptingRequests, activeRequests, capacity: MAX_ACTIVE_PROXY_REQUESTS };
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export function _resetRuntimeAdmissionForTests(): void {
|
|
52
|
-
acceptingRequests = true;
|
|
53
|
-
activeRequests = 0;
|
|
54
|
-
notifyDrained();
|
|
55
|
-
}
|