@kbediako/codex-orchestrator 0.1.38 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/plugins/marketplace.json +20 -0
- package/README.md +46 -317
- package/bin/codex-orchestrator.js +161 -0
- package/codex.orchestrator.json +149 -13
- package/dist/bin/codex-orchestrator.js +797 -1154
- package/dist/orchestrator/src/cli/adapters/CommandBuilder.js +50 -0
- package/dist/orchestrator/src/cli/adapters/CommandPlanner.js +22 -4
- package/dist/orchestrator/src/cli/adapters/CommandReviewer.js +3 -3
- package/dist/orchestrator/src/cli/adapters/CommandTester.js +2 -2
- package/dist/orchestrator/src/cli/adapters/cloudFailureDiagnostics.js +295 -11
- package/dist/orchestrator/src/cli/coStatusAttachCliShell.js +402 -0
- package/dist/orchestrator/src/cli/coStatusCliShell.js +451 -0
- package/dist/orchestrator/src/cli/coStatusOperatorAutopilotCliShell.js +120 -0
- package/dist/orchestrator/src/cli/codexCliShell.js +119 -0
- package/dist/orchestrator/src/cli/codexDefaultsSetup.js +265 -36
- package/dist/orchestrator/src/cli/config/delegationConfig.js +317 -5
- package/dist/orchestrator/src/cli/config/repoConfigPolicy.js +2 -3
- package/dist/orchestrator/src/cli/config/userConfig.js +28 -13
- package/dist/orchestrator/src/cli/control/authenticatedControlRouteGate.js +69 -0
- package/dist/orchestrator/src/cli/control/authenticatedRouteComposition.js +267 -0
- package/dist/orchestrator/src/cli/control/authenticatedRouteController.js +5 -0
- package/dist/orchestrator/src/cli/control/authenticatedRouteDispatcher.js +41 -0
- package/dist/orchestrator/src/cli/control/compatibilityIssuePresenter.js +1035 -0
- package/dist/orchestrator/src/cli/control/confirmationApproveController.js +62 -0
- package/dist/orchestrator/src/cli/control/confirmationCreateController.js +69 -0
- package/dist/orchestrator/src/cli/control/confirmationIssueConsumeController.js +43 -0
- package/dist/orchestrator/src/cli/control/confirmationListController.js +22 -0
- package/dist/orchestrator/src/cli/control/confirmationValidateController.js +58 -0
- package/dist/orchestrator/src/cli/control/confirmations.js +25 -3
- package/dist/orchestrator/src/cli/control/controlActionCancelConfirmation.js +65 -0
- package/dist/orchestrator/src/cli/control/controlActionController.js +77 -0
- package/dist/orchestrator/src/cli/control/controlActionControllerSequencing.js +161 -0
- package/dist/orchestrator/src/cli/control/controlActionExecution.js +142 -0
- package/dist/orchestrator/src/cli/control/controlActionFinalization.js +43 -0
- package/dist/orchestrator/src/cli/control/controlActionOutcome.js +60 -0
- package/dist/orchestrator/src/cli/control/controlActionPreflight.js +476 -0
- package/dist/orchestrator/src/cli/control/controlAuthenticatedRouteHandoff.js +57 -0
- package/dist/orchestrator/src/cli/control/controlBootstrapAssembly.js +39 -0
- package/dist/orchestrator/src/cli/control/controlBootstrapMetadataPersistence.js +16 -0
- package/dist/orchestrator/src/cli/control/controlEventTransport.js +49 -0
- package/dist/orchestrator/src/cli/control/controlExpiryLifecycle.js +102 -0
- package/dist/orchestrator/src/cli/control/controlHostOwnership.js +480 -0
- package/dist/orchestrator/src/cli/control/controlHostSupervision.js +630 -0
- package/dist/orchestrator/src/cli/control/controlOversightFacade.js +8 -0
- package/dist/orchestrator/src/cli/control/controlOversightReadContract.js +1 -0
- package/dist/orchestrator/src/cli/control/controlOversightReadService.js +16 -0
- package/dist/orchestrator/src/cli/control/controlOversightUpdateContract.js +1 -0
- package/dist/orchestrator/src/cli/control/controlPersistenceFiles.js +6 -0
- package/dist/orchestrator/src/cli/control/controlQuestionChildResolution.js +18 -0
- package/dist/orchestrator/src/cli/control/controlRequestContext.js +42 -0
- package/dist/orchestrator/src/cli/control/controlRequestController.js +9 -0
- package/dist/orchestrator/src/cli/control/controlRequestPredispatch.js +17 -0
- package/dist/orchestrator/src/cli/control/controlRequestRouteDispatch.js +44 -0
- package/dist/orchestrator/src/cli/control/controlRuntime.js +1003 -0
- package/dist/orchestrator/src/cli/control/controlServer.js +23 -1456
- package/dist/orchestrator/src/cli/control/controlServerAuditAndErrorHelpers.js +115 -0
- package/dist/orchestrator/src/cli/control/controlServerAuthenticatedRouteBranch.js +29 -0
- package/dist/orchestrator/src/cli/control/controlServerBootstrapLifecycle.js +30 -0
- package/dist/orchestrator/src/cli/control/controlServerBootstrapStartSequence.js +21 -0
- package/dist/orchestrator/src/cli/control/controlServerOwnedRuntimeLifecycle.js +67 -0
- package/dist/orchestrator/src/cli/control/controlServerPublicLifecycle.js +756 -0
- package/dist/orchestrator/src/cli/control/controlServerPublicRouteHelpers.js +86 -0
- package/dist/orchestrator/src/cli/control/controlServerReadyInstanceLifecycle.js +25 -0
- package/dist/orchestrator/src/cli/control/controlServerReadyInstanceStartup.js +18 -0
- package/dist/orchestrator/src/cli/control/controlServerRequestBodyHelpers.js +37 -0
- package/dist/orchestrator/src/cli/control/controlServerRequestShell.js +40 -0
- package/dist/orchestrator/src/cli/control/controlServerRequestShellBinding.js +17 -0
- package/dist/orchestrator/src/cli/control/controlServerSeedLoading.js +27 -0
- package/dist/orchestrator/src/cli/control/controlServerSeededRuntimeAssembly.js +186 -0
- package/dist/orchestrator/src/cli/control/controlServerStartupInputPreparation.js +31 -0
- package/dist/orchestrator/src/cli/control/controlServerStartupSequence.js +49 -0
- package/dist/orchestrator/src/cli/control/controlState.js +233 -2
- package/dist/orchestrator/src/cli/control/controlStatusDashboard.js +1904 -0
- package/dist/orchestrator/src/cli/control/controlTelegramBridgeBootstrapLifecycle.js +22 -0
- package/dist/orchestrator/src/cli/control/controlTelegramBridgeLifecycle.js +67 -0
- package/dist/orchestrator/src/cli/control/controlTelegramBridgeOversightFacadeFactory.js +8 -0
- package/dist/orchestrator/src/cli/control/controlTelegramCommandController.js +49 -0
- package/dist/orchestrator/src/cli/control/controlTelegramDispatchRead.js +40 -0
- package/dist/orchestrator/src/cli/control/controlTelegramPollingController.js +89 -0
- package/dist/orchestrator/src/cli/control/controlTelegramProjectionNotificationController.js +29 -0
- package/dist/orchestrator/src/cli/control/controlTelegramPushState.js +63 -0
- package/dist/orchestrator/src/cli/control/controlTelegramQuestionRead.js +13 -0
- package/dist/orchestrator/src/cli/control/controlTelegramReadController.js +216 -0
- package/dist/orchestrator/src/cli/control/controlTelegramUpdateHandler.js +63 -0
- package/dist/orchestrator/src/cli/control/controlWatcher.js +73 -5
- package/dist/orchestrator/src/cli/control/delegationRegisterController.js +35 -0
- package/dist/orchestrator/src/cli/control/dynamicToolBridgePolicy.js +139 -0
- package/dist/orchestrator/src/cli/control/eventsSseController.js +12 -0
- package/dist/orchestrator/src/cli/control/linearBudgetState.js +1789 -0
- package/dist/orchestrator/src/cli/control/linearDispatchSource.js +1137 -0
- package/dist/orchestrator/src/cli/control/linearGraphqlClient.js +150 -0
- package/dist/orchestrator/src/cli/control/linearRateLimit.js +102 -0
- package/dist/orchestrator/src/cli/control/linearWebhookController.js +499 -0
- package/dist/orchestrator/src/cli/control/liveLinearAdvisoryRuntime.js +70 -0
- package/dist/orchestrator/src/cli/control/observabilityApiController.js +173 -0
- package/dist/orchestrator/src/cli/control/observabilityReadModel.js +500 -0
- package/dist/orchestrator/src/cli/control/observabilitySurface.js +284 -0
- package/dist/orchestrator/src/cli/control/observabilityUpdateNotifier.js +22 -0
- package/dist/orchestrator/src/cli/control/operatorDashboardPresenter.js +252 -0
- package/dist/orchestrator/src/cli/control/providerAgentCapacity.js +70 -0
- package/dist/orchestrator/src/cli/control/providerControlHostFreshnessGauge.js +1068 -0
- package/dist/orchestrator/src/cli/control/providerIntakeState.js +473 -0
- package/dist/orchestrator/src/cli/control/providerIssueHandoff.js +6811 -0
- package/dist/orchestrator/src/cli/control/providerIssueObservability.js +1348 -0
- package/dist/orchestrator/src/cli/control/providerIssueRetryQueue.js +84 -0
- package/dist/orchestrator/src/cli/control/providerLinearRuntimeProof.js +588 -0
- package/dist/orchestrator/src/cli/control/providerLinearScreenshotProof.js +473 -0
- package/dist/orchestrator/src/cli/control/providerLinearWorkerTruth.js +383 -0
- package/dist/orchestrator/src/cli/control/providerLinearWorkflowAudit.js +254 -0
- package/dist/orchestrator/src/cli/control/providerLinearWorkflowFacade.js +5573 -0
- package/dist/orchestrator/src/cli/control/providerLinearWorkflowStates.js +115 -0
- package/dist/orchestrator/src/cli/control/providerMergeCloseout.js +1868 -0
- package/dist/orchestrator/src/cli/control/providerOperatorAutopilot.js +1580 -0
- package/dist/orchestrator/src/cli/control/providerOperatorAutopilotLifecycle.js +154 -0
- package/dist/orchestrator/src/cli/control/providerOperatorAutopilotLocalRolloutExecution.js +1006 -0
- package/dist/orchestrator/src/cli/control/providerPollingHealth.js +435 -0
- package/dist/orchestrator/src/cli/control/providerTerminalCleanup.js +516 -0
- package/dist/orchestrator/src/cli/control/providerWorkerHosts.js +191 -0
- package/dist/orchestrator/src/cli/control/providerWorkflowConfigStore.js +515 -0
- package/dist/orchestrator/src/cli/control/questionChildResolutionAdapter.js +361 -0
- package/dist/orchestrator/src/cli/control/questionQueueController.js +181 -0
- package/dist/orchestrator/src/cli/control/questionReadRetryDeduplication.js +9 -0
- package/dist/orchestrator/src/cli/control/questionReadSequence.js +10 -0
- package/dist/orchestrator/src/cli/control/securityViolationController.js +27 -0
- package/dist/orchestrator/src/cli/control/selectedRunProjection.js +1885 -0
- package/dist/orchestrator/src/cli/control/telegramOversightApiClient.js +48 -0
- package/dist/orchestrator/src/cli/control/telegramOversightBridge.js +180 -0
- package/dist/orchestrator/src/cli/control/telegramOversightBridgeProjectionDeliveryQueue.js +25 -0
- package/dist/orchestrator/src/cli/control/telegramOversightBridgeRuntimeLifecycle.js +45 -0
- package/dist/orchestrator/src/cli/control/telegramOversightBridgeStateStore.js +77 -0
- package/dist/orchestrator/src/cli/control/telegramOversightControlActionApiClient.js +45 -0
- package/dist/orchestrator/src/cli/control/trackerDispatchPilot.js +439 -0
- package/dist/orchestrator/src/cli/control/uiDataController.js +34 -0
- package/dist/orchestrator/src/cli/control/uiSessionController.js +100 -0
- package/dist/orchestrator/src/cli/controlHostCliShell.js +860 -0
- package/dist/orchestrator/src/cli/controlHostFreshnessGaugeCliShell.js +129 -0
- package/dist/orchestrator/src/cli/controlHostSupervisionCliShell.js +2127 -0
- package/dist/orchestrator/src/cli/delegationCliShell.js +62 -0
- package/dist/orchestrator/src/cli/delegationServer.js +567 -678
- package/dist/orchestrator/src/cli/delegationServerCliShell.js +52 -0
- package/dist/orchestrator/src/cli/delegationServerQuestionFlowShell.js +228 -0
- package/dist/orchestrator/src/cli/delegationServerToolDispatchShell.js +411 -0
- package/dist/orchestrator/src/cli/delegationServerTransport.js +274 -0
- package/dist/orchestrator/src/cli/delegationSetup.js +51 -171
- package/dist/orchestrator/src/cli/devtoolsCliShell.js +34 -0
- package/dist/orchestrator/src/cli/doctor.js +678 -164
- package/dist/orchestrator/src/cli/doctorCliRequestShell.js +72 -0
- package/dist/orchestrator/src/cli/doctorCliShell.js +138 -0
- package/dist/orchestrator/src/cli/doctorUsage.js +119 -15
- package/dist/orchestrator/src/cli/exec/experience.js +16 -2
- package/dist/orchestrator/src/cli/exec/summary.js +3 -0
- package/dist/orchestrator/src/cli/execCliShell.js +51 -0
- package/dist/orchestrator/src/cli/flowCliRequestShell.js +44 -0
- package/dist/orchestrator/src/cli/flowCliShell.js +239 -0
- package/dist/orchestrator/src/cli/frontendTestCliRequestShell.js +80 -0
- package/dist/orchestrator/src/cli/frontendTestCliShell.js +41 -0
- package/dist/orchestrator/src/cli/init.js +95 -1
- package/dist/orchestrator/src/cli/initCliShell.js +50 -0
- package/dist/orchestrator/src/cli/linearCliShell.js +1200 -0
- package/dist/orchestrator/src/cli/mcpEnableCliShell.js +132 -0
- package/dist/orchestrator/src/cli/metrics/metricsAggregator.js +3 -2
- package/dist/orchestrator/src/cli/metrics/metricsRecorder.js +56 -0
- package/dist/orchestrator/src/cli/orchestrator.js +66 -1376
- package/dist/orchestrator/src/cli/planCliShell.js +19 -0
- package/dist/orchestrator/src/cli/prCliShell.js +41 -0
- package/dist/orchestrator/src/cli/providerLinearChildLanePhaseContract.js +204 -0
- package/dist/orchestrator/src/cli/providerLinearChildLaneRunner.js +1835 -0
- package/dist/orchestrator/src/cli/providerLinearChildLaneShell.js +2420 -0
- package/dist/orchestrator/src/cli/providerLinearChildStreamShell.js +385 -0
- package/dist/orchestrator/src/cli/providerLinearWorkerRunner.js +6834 -0
- package/dist/orchestrator/src/cli/resumeCliShell.js +14 -0
- package/dist/orchestrator/src/cli/reviewCliLaunchShell.js +72 -0
- package/dist/orchestrator/src/cli/rlm/alignment.js +3 -3
- package/dist/orchestrator/src/cli/rlm/context.js +94 -7
- package/dist/orchestrator/src/cli/rlm/rlmCodexRuntimeShell.js +546 -0
- package/dist/orchestrator/src/cli/rlm/symbolic.js +4 -2
- package/dist/orchestrator/src/cli/rlmCliRequestShell.js +42 -0
- package/dist/orchestrator/src/cli/rlmCompletionCliShell.js +46 -0
- package/dist/orchestrator/src/cli/rlmLaunchCliShell.js +51 -0
- package/dist/orchestrator/src/cli/rlmRunner.js +83 -523
- package/dist/orchestrator/src/cli/run/blockMemory.js +500 -0
- package/dist/orchestrator/src/cli/run/manifest.js +410 -73
- package/dist/orchestrator/src/cli/run/manifestPersister.js +45 -14
- package/dist/orchestrator/src/cli/run/runMemoryController.js +216 -0
- package/dist/orchestrator/src/cli/run/source0.js +690 -0
- package/dist/orchestrator/src/cli/run/workspacePath.js +101 -0
- package/dist/orchestrator/src/cli/runtime/mode.js +2 -1
- package/dist/orchestrator/src/cli/runtime/provider.js +39 -2
- package/dist/orchestrator/src/cli/selfCheckCliShell.js +12 -0
- package/dist/orchestrator/src/cli/services/commandRunner.js +698 -18
- package/dist/orchestrator/src/cli/services/execRuntime.js +66 -1
- package/dist/orchestrator/src/cli/services/orchestratorAutoScoutEvidenceRecorder.js +71 -0
- package/dist/orchestrator/src/cli/services/orchestratorCloudBranchResolution.js +8 -0
- package/dist/orchestrator/src/cli/services/orchestratorCloudEnvironmentResolution.js +22 -0
- package/dist/orchestrator/src/cli/services/orchestratorCloudExecutionLifecycleShell.js +39 -0
- package/dist/orchestrator/src/cli/services/orchestratorCloudPromptBuilder.js +37 -0
- package/dist/orchestrator/src/cli/services/orchestratorCloudRouteFallbackContract.js +45 -0
- package/dist/orchestrator/src/cli/services/orchestratorCloudRouteShell.js +36 -0
- package/dist/orchestrator/src/cli/services/orchestratorCloudTargetExecutor.js +277 -0
- package/dist/orchestrator/src/cli/services/orchestratorControlPlaneLifecycle.js +98 -0
- package/dist/orchestrator/src/cli/services/orchestratorControlPlaneLifecycleShell.js +54 -0
- package/dist/orchestrator/src/cli/services/orchestratorExecutionLifecycle.js +112 -0
- package/dist/orchestrator/src/cli/services/orchestratorExecutionModePolicy.js +27 -0
- package/dist/orchestrator/src/cli/services/orchestratorExecutionRouteAdapterShell.js +59 -0
- package/dist/orchestrator/src/cli/services/orchestratorExecutionRouteDecisionShell.js +57 -0
- package/dist/orchestrator/src/cli/services/orchestratorExecutionRouteState.js +21 -0
- package/dist/orchestrator/src/cli/services/orchestratorExecutionRouter.js +2 -0
- package/dist/orchestrator/src/cli/services/orchestratorLocalPipelineExecutor.js +149 -0
- package/dist/orchestrator/src/cli/services/orchestratorLocalRouteShell.js +63 -0
- package/dist/orchestrator/src/cli/services/orchestratorPlanShell.js +54 -0
- package/dist/orchestrator/src/cli/services/orchestratorPlanTargetTracker.js +16 -0
- package/dist/orchestrator/src/cli/services/orchestratorResumePreparationShell.js +84 -0
- package/dist/orchestrator/src/cli/services/orchestratorResumeTokenValidation.js +15 -0
- package/dist/orchestrator/src/cli/services/orchestratorRunLifecycleCompletion.js +31 -0
- package/dist/orchestrator/src/cli/services/orchestratorRunLifecycleExecutionRegistration.js +37 -0
- package/dist/orchestrator/src/cli/services/orchestratorRunLifecycleOrchestrationShell.js +83 -0
- package/dist/orchestrator/src/cli/services/orchestratorRunLifecycleTaskManagerShell.js +37 -0
- package/dist/orchestrator/src/cli/services/orchestratorRuntimeManifestMutation.js +20 -0
- package/dist/orchestrator/src/cli/services/orchestratorStartPreparationShell.js +56 -0
- package/dist/orchestrator/src/cli/services/orchestratorStatusShell.js +70 -0
- package/dist/orchestrator/src/cli/services/pipelineResolver.js +7 -3
- package/dist/orchestrator/src/cli/services/plannerMemory.js +119 -0
- package/dist/orchestrator/src/cli/services/runPreparation.js +7 -3
- package/dist/orchestrator/src/cli/services/runSummaryWriter.js +9 -0
- package/dist/orchestrator/src/cli/setupBootstrapShell.js +114 -0
- package/dist/orchestrator/src/cli/setupCliShell.js +51 -0
- package/dist/orchestrator/src/cli/skillsCliShell.js +56 -0
- package/dist/orchestrator/src/cli/startCliRequestShell.js +53 -0
- package/dist/orchestrator/src/cli/startCliShell.js +68 -0
- package/dist/orchestrator/src/cli/statusCliShell.js +22 -0
- package/dist/orchestrator/src/cli/utils/authProvenanceFingerprint.js +27 -0
- package/dist/orchestrator/src/cli/utils/cloudPreflight.js +285 -7
- package/dist/orchestrator/src/cli/utils/codexFeatures.js +60 -0
- package/dist/orchestrator/src/cli/utils/delegationConfigParser.js +250 -0
- package/dist/orchestrator/src/cli/utils/delegationMcpHealth.js +1382 -0
- package/dist/orchestrator/src/cli/utils/devtools.js +2 -54
- package/dist/orchestrator/src/cli/utils/mcpServerEntry.js +53 -0
- package/dist/orchestrator/src/cli/utils/packageProgramResolver.js +151 -0
- package/dist/orchestrator/src/cli/utils/providerOverrideEnv.js +71 -0
- package/dist/orchestrator/src/cli/utils/trailingJsonObject.js +59 -0
- package/dist/orchestrator/src/learning/crystalizer.js +2 -2
- package/dist/orchestrator/src/manager.js +74 -4
- package/dist/orchestrator/src/persistence/ExperienceStore.js +233 -49
- package/dist/orchestrator/src/persistence/TaskStateStore.js +6 -6
- package/dist/orchestrator/src/persistence/lockFile.js +70 -4
- package/dist/orchestrator/src/persistence/sanitizeIdentifier.js +39 -0
- package/dist/orchestrator/src/sync/createCloudSyncWorker.js +3 -2
- package/dist/orchestrator/src/utils/atomicWrite.js +17 -2
- package/dist/packages/orchestrator/src/exec/unified-exec.js +99 -6
- package/dist/packages/orchestrator/src/instructions/promptPacks.js +150 -19
- package/dist/packages/sdk-node/src/orchestrator.js +137 -13
- package/dist/packages/shared/config/designConfig.js +8 -1
- package/dist/packages/shared/streams/stdio.js +1 -1
- package/dist/scripts/design/pipeline/permit.js +15 -0
- package/dist/scripts/lib/docs-catalog.js +399 -0
- package/dist/scripts/lib/docs-helpers.js +87 -5
- package/dist/scripts/lib/pr-watch-merge.js +1088 -80
- package/dist/scripts/lib/provider-run-contract.js +26 -0
- package/dist/scripts/lib/review-command-intent-classification.js +532 -0
- package/dist/scripts/lib/review-command-probe-classification.js +385 -0
- package/dist/scripts/lib/review-execution-boundary-preflight.js +279 -0
- package/dist/scripts/lib/review-execution-runtime.js +753 -0
- package/dist/scripts/lib/review-execution-state.js +1144 -0
- package/dist/scripts/lib/review-execution-telemetry.js +215 -0
- package/dist/scripts/lib/review-inspection-target-parsing.js +78 -0
- package/dist/scripts/lib/review-launch-attempt.js +601 -0
- package/dist/scripts/lib/review-meta-surface-boundary-analysis.js +300 -0
- package/dist/scripts/lib/review-meta-surface-normalization.js +746 -0
- package/dist/scripts/lib/review-non-interactive-handoff.js +61 -0
- package/dist/scripts/lib/review-prompt-context.js +376 -0
- package/dist/scripts/lib/review-scope-advisory.js +286 -0
- package/dist/scripts/lib/review-scope-paths.js +123 -0
- package/dist/scripts/lib/review-shell-command-parser.js +389 -0
- package/dist/scripts/lib/review-shell-env-interpreter.js +340 -0
- package/dist/scripts/lib/run-manifests.js +192 -36
- package/dist/scripts/lib/spark-policy-classifier.js +593 -0
- package/dist/scripts/run-review.js +507 -1777
- package/docs/README.md +43 -20
- package/docs/book/README.md +19 -0
- package/docs/book/codex-cli-0124-adoption.md +68 -0
- package/docs/book/local-hook-impact.md +73 -0
- package/docs/book/operations.md +60 -0
- package/docs/book/public-posture.md +34 -0
- package/docs/book/setup.md +91 -0
- package/docs/book/skills.md +11 -0
- package/docs/guides/codex-version-policy.md +104 -0
- package/docs/public/downstream-setup.md +113 -0
- package/docs/public/provider-onboarding.md +173 -0
- package/package.json +23 -10
- package/plugins/codex-orchestrator/.codex-plugin/plugin.json +30 -0
- package/plugins/codex-orchestrator/.mcp.json +13 -0
- package/plugins/codex-orchestrator/launcher.mjs +361 -0
- package/schemas/manifest.json +411 -0
- package/skills/README.md +26 -0
- package/skills/collab-subagents-first/SKILL.md +1 -1
- package/skills/delegation-usage/DELEGATION_GUIDE.md +30 -12
- package/skills/delegation-usage/SKILL.md +25 -14
- package/skills/land/SKILL.md +77 -0
- package/skills/linear/SKILL.md +255 -0
- package/skills/release/SKILL.md +47 -3
- package/skills/standalone-review/SKILL.md +6 -1
- package/templates/README.md +4 -2
- package/templates/codex/.codex/agents/awaiter-high.toml +2 -2
- package/templates/codex/.codex/agents/worker-complex.toml +1 -1
- package/templates/codex/.codex/config.toml +3 -4
- package/templates/codex/.codex/providers/README.md +13 -0
- package/templates/codex/.codex/providers/control.example.json +18 -0
- package/templates/codex/.codex/providers/provider.env.example +15 -0
- package/templates/codex/AGENTS.md +15 -8
- package/templates/codex/mcp-client.json +5 -1
- package/docs/assets/setup.gif +0 -0
|
@@ -0,0 +1,1904 @@
|
|
|
1
|
+
import { mkdir, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import process from 'node:process';
|
|
4
|
+
import { logger } from '../../logger.js';
|
|
5
|
+
import { evaluateInteractiveGate } from '../utils/interactive.js';
|
|
6
|
+
import { readUiDataset } from './operatorDashboardPresenter.js';
|
|
7
|
+
const graphemeSegmenter = typeof Intl !== 'undefined' && typeof Intl.Segmenter === 'function'
|
|
8
|
+
? new Intl.Segmenter(undefined, { granularity: 'grapheme' })
|
|
9
|
+
: null;
|
|
10
|
+
const combiningMarkPattern = /^\p{Mark}$/u;
|
|
11
|
+
const extendedPictographicPattern = /\p{Extended_Pictographic}/u;
|
|
12
|
+
const keycapPattern = /^[0-9#*]\uFE0F?\u20E3$/u;
|
|
13
|
+
const ANSI_CLEAR_HOME = '\u001b[H\u001b[2J';
|
|
14
|
+
const ANSI_CLEAR_DOWN = '\u001b[J';
|
|
15
|
+
const ANSI_ENTER_ALT_SCREEN = '\u001b[?1049h';
|
|
16
|
+
const ANSI_EXIT_ALT_SCREEN = '\u001b[?1049l';
|
|
17
|
+
const ANSI_RESET = '\u001b[0m';
|
|
18
|
+
const ANSI_BOLD = '\u001b[1m';
|
|
19
|
+
const ANSI_BLUE = '\u001b[34m';
|
|
20
|
+
const ANSI_CYAN = '\u001b[36m';
|
|
21
|
+
const ANSI_DIM = '\u001b[2m';
|
|
22
|
+
const ANSI_GREEN = '\u001b[32m';
|
|
23
|
+
const ANSI_MAGENTA = '\u001b[35m';
|
|
24
|
+
const ANSI_RED = '\u001b[31m';
|
|
25
|
+
const ANSI_YELLOW = '\u001b[33m';
|
|
26
|
+
const ANSI_GRAY = '\u001b[90m';
|
|
27
|
+
const ANSI_CLEAR_CONTROL = '\u001b';
|
|
28
|
+
const ANSI_CLEAR_BELL = '\u0007';
|
|
29
|
+
const CONTROL_CHARACTER_CLASS = `${String.fromCharCode(0x00)}-${String.fromCharCode(0x1f)}${String.fromCharCode(0x7f)}-${String.fromCharCode(0x9f)}`;
|
|
30
|
+
const ANSI_CONTROL_SEQUENCE_PATTERN = new RegExp(`${ANSI_CLEAR_CONTROL}(?:\\[[0-?]*[ -/]*[@-~]|\\][^${ANSI_CLEAR_BELL}${ANSI_CLEAR_CONTROL}]*(?:${ANSI_CLEAR_BELL}|${ANSI_CLEAR_CONTROL}\\\\)|[@-Z\\\\-_])`, 'g');
|
|
31
|
+
const CONTROL_CHARACTER_PATTERN = new RegExp(`[${CONTROL_CHARACTER_CLASS}]`, 'g');
|
|
32
|
+
const DEFAULT_REFRESH_INTERVAL_MS = 1_000;
|
|
33
|
+
const DEFAULT_TERMINAL_COLUMNS = 115;
|
|
34
|
+
const THROUGHPUT_WINDOW_MS = 5_000;
|
|
35
|
+
const DEFAULT_OUTPUT = process.stdout;
|
|
36
|
+
const DEFAULT_INPUT = process.stdin;
|
|
37
|
+
const NUMBER_FORMAT = new Intl.NumberFormat('en-US');
|
|
38
|
+
const DASHBOARD_SNAPSHOT_DIRNAME = 'co-status-snapshots';
|
|
39
|
+
const DEFAULT_DEPENDENCIES = {
|
|
40
|
+
readDataset: async (runtime) => await readUiDataset({
|
|
41
|
+
readCompatibilityProjection: async () => await runtime.snapshot().readCompatibilityProjection()
|
|
42
|
+
}),
|
|
43
|
+
setTimeout,
|
|
44
|
+
clearTimeout,
|
|
45
|
+
now: () => new Date()
|
|
46
|
+
};
|
|
47
|
+
export function shouldEnableControlStatusDashboard(input) {
|
|
48
|
+
return evaluateInteractiveGate({
|
|
49
|
+
requested: true,
|
|
50
|
+
format: input.format,
|
|
51
|
+
stdoutIsTTY: input.stdoutIsTTY,
|
|
52
|
+
stderrIsTTY: input.stderrIsTTY,
|
|
53
|
+
term: input.term,
|
|
54
|
+
// CO STATUS is a real terminal surface, so inherited CI markers should
|
|
55
|
+
// not suppress it when both output streams are interactive.
|
|
56
|
+
env: input.env === undefined
|
|
57
|
+
? undefined
|
|
58
|
+
: {
|
|
59
|
+
...input.env,
|
|
60
|
+
CI: undefined
|
|
61
|
+
}
|
|
62
|
+
}).enabled;
|
|
63
|
+
}
|
|
64
|
+
export function startControlStatusDashboard(options, overrides = {}) {
|
|
65
|
+
const deps = { ...DEFAULT_DEPENDENCIES, ...overrides };
|
|
66
|
+
const liveOutput = options.output ?? DEFAULT_OUTPUT;
|
|
67
|
+
return startControlStatusViewer({
|
|
68
|
+
readDataset: async () => await deps.readDataset(options.runtime),
|
|
69
|
+
requestRefresh: async () => {
|
|
70
|
+
await options.runtime.requestRefresh();
|
|
71
|
+
},
|
|
72
|
+
subscribe: (listener) => options.runtime.subscribe(listener),
|
|
73
|
+
baseUrl: options.baseUrl,
|
|
74
|
+
taskId: options.taskId,
|
|
75
|
+
runId: options.runId,
|
|
76
|
+
runDir: options.runDir,
|
|
77
|
+
startPipelineId: options.startPipelineId,
|
|
78
|
+
refreshIntervalMs: options.refreshIntervalMs,
|
|
79
|
+
output: options.output,
|
|
80
|
+
input: options.input,
|
|
81
|
+
liveSurfaceMode: liveOutput.isTTY === true ? 'alternate' : 'primary',
|
|
82
|
+
showDashboardLine: false
|
|
83
|
+
}, deps);
|
|
84
|
+
}
|
|
85
|
+
export function startAttachedControlStatusDashboard(options, overrides = {}) {
|
|
86
|
+
const deps = { ...DEFAULT_DEPENDENCIES, ...overrides };
|
|
87
|
+
return startControlStatusViewer({
|
|
88
|
+
readDataset: options.readDataset,
|
|
89
|
+
requestRefresh: null,
|
|
90
|
+
subscribe: null,
|
|
91
|
+
baseUrl: options.baseUrl,
|
|
92
|
+
taskId: options.taskId,
|
|
93
|
+
runId: options.runId,
|
|
94
|
+
runDir: options.runDir,
|
|
95
|
+
startPipelineId: options.startPipelineId,
|
|
96
|
+
refreshIntervalMs: options.refreshIntervalMs,
|
|
97
|
+
output: options.output,
|
|
98
|
+
input: options.input,
|
|
99
|
+
liveSurfaceMode: 'primary',
|
|
100
|
+
showDashboardLine: false
|
|
101
|
+
}, deps);
|
|
102
|
+
}
|
|
103
|
+
function startControlStatusViewer(options, deps) {
|
|
104
|
+
const refreshIntervalMs = Math.max(250, options.refreshIntervalMs ?? DEFAULT_REFRESH_INTERVAL_MS);
|
|
105
|
+
const output = options.output ?? DEFAULT_OUTPUT;
|
|
106
|
+
const input = options.input ?? DEFAULT_INPUT;
|
|
107
|
+
let stopped = false;
|
|
108
|
+
let timer = null;
|
|
109
|
+
let activeRender = null;
|
|
110
|
+
let activeReadController = null;
|
|
111
|
+
let queuedRender = false;
|
|
112
|
+
let queuedForceRefresh = false;
|
|
113
|
+
let queuedReadDataset = false;
|
|
114
|
+
let queuedUseCachedFrame = false;
|
|
115
|
+
let tokenSamples = [];
|
|
116
|
+
let renderedState = null;
|
|
117
|
+
let escapeSequenceState = 'idle';
|
|
118
|
+
const liveSurfaceMode = options.liveSurfaceMode ?? 'primary';
|
|
119
|
+
const enablePinnedPrimaryLiveRegion = liveSurfaceMode === 'primary' && output.isTTY === true;
|
|
120
|
+
let activeSurfaceMode = 'primary';
|
|
121
|
+
let activePrimaryFrame = null;
|
|
122
|
+
let primarySurfacePromptNeedsNewline = false;
|
|
123
|
+
let frameState = {
|
|
124
|
+
paused: false,
|
|
125
|
+
pausedLiveReferenceTime: null,
|
|
126
|
+
viewMode: 'full',
|
|
127
|
+
surfaceMode: liveSurfaceMode,
|
|
128
|
+
pendingUpdate: false,
|
|
129
|
+
snapshotStatus: 'idle',
|
|
130
|
+
snapshotPath: null,
|
|
131
|
+
snapshotMessage: null
|
|
132
|
+
};
|
|
133
|
+
let snapshotWrite = null;
|
|
134
|
+
const queueRender = (forceRefresh, useCachedFrame) => {
|
|
135
|
+
if (stopped) {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
queuedForceRefresh = queuedForceRefresh || forceRefresh;
|
|
139
|
+
queuedReadDataset = queuedReadDataset || forceRefresh || !useCachedFrame;
|
|
140
|
+
queuedUseCachedFrame = queuedUseCachedFrame || useCachedFrame;
|
|
141
|
+
if (activeRender) {
|
|
142
|
+
queuedRender = true;
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
startQueuedRender();
|
|
146
|
+
};
|
|
147
|
+
const requestRender = (forceRefresh) => {
|
|
148
|
+
if (stopped) {
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
if (frameState.paused) {
|
|
152
|
+
frameState = {
|
|
153
|
+
...frameState,
|
|
154
|
+
pendingUpdate: true
|
|
155
|
+
};
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
queueRender(forceRefresh, false);
|
|
159
|
+
};
|
|
160
|
+
const requestCachedFrameRender = () => {
|
|
161
|
+
if (stopped) {
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
queueRender(false, true);
|
|
165
|
+
};
|
|
166
|
+
const clearQueuedRenderState = () => {
|
|
167
|
+
const hadQueuedRender = queuedRender || queuedForceRefresh || queuedReadDataset || queuedUseCachedFrame;
|
|
168
|
+
queuedRender = false;
|
|
169
|
+
queuedForceRefresh = false;
|
|
170
|
+
queuedReadDataset = false;
|
|
171
|
+
queuedUseCachedFrame = false;
|
|
172
|
+
return hadQueuedRender;
|
|
173
|
+
};
|
|
174
|
+
const startQueuedRender = () => {
|
|
175
|
+
const forceRefresh = queuedForceRefresh;
|
|
176
|
+
const useCachedFrame = queuedUseCachedFrame && !queuedReadDataset && !forceRefresh;
|
|
177
|
+
queuedRender = false;
|
|
178
|
+
queuedForceRefresh = false;
|
|
179
|
+
queuedReadDataset = false;
|
|
180
|
+
queuedUseCachedFrame = false;
|
|
181
|
+
activeRender = renderFrame(forceRefresh, useCachedFrame).finally(() => {
|
|
182
|
+
activeRender = null;
|
|
183
|
+
if (stopped || !queuedRender) {
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
startQueuedRender();
|
|
187
|
+
});
|
|
188
|
+
};
|
|
189
|
+
const scheduleTick = () => {
|
|
190
|
+
if (stopped) {
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
timer = deps.setTimeout(() => {
|
|
194
|
+
timer = null;
|
|
195
|
+
if (stopped) {
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
requestRender(true);
|
|
199
|
+
if (stopped) {
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
scheduleTick();
|
|
203
|
+
}, refreshIntervalMs);
|
|
204
|
+
timer.unref?.();
|
|
205
|
+
};
|
|
206
|
+
const unsubscribe = options.subscribe?.(() => {
|
|
207
|
+
requestRender(false);
|
|
208
|
+
}) ?? (() => undefined);
|
|
209
|
+
const detachInput = attachInteractiveInput();
|
|
210
|
+
requestRender(true);
|
|
211
|
+
scheduleTick();
|
|
212
|
+
return {
|
|
213
|
+
stop() {
|
|
214
|
+
stopped = true;
|
|
215
|
+
queuedRender = false;
|
|
216
|
+
queuedForceRefresh = false;
|
|
217
|
+
activeReadController?.abort();
|
|
218
|
+
if (timer) {
|
|
219
|
+
deps.clearTimeout(timer);
|
|
220
|
+
timer = null;
|
|
221
|
+
}
|
|
222
|
+
detachInput();
|
|
223
|
+
unsubscribe();
|
|
224
|
+
if (activeSurfaceMode === 'alternate') {
|
|
225
|
+
output.write(`${ANSI_EXIT_ALT_SCREEN}${primarySurfacePromptNeedsNewline ? '\n' : ''}`);
|
|
226
|
+
activeSurfaceMode = 'primary';
|
|
227
|
+
}
|
|
228
|
+
else if (primarySurfacePromptNeedsNewline || activePrimaryFrame !== null) {
|
|
229
|
+
output.write('\n');
|
|
230
|
+
}
|
|
231
|
+
activePrimaryFrame = null;
|
|
232
|
+
primarySurfacePromptNeedsNewline = false;
|
|
233
|
+
},
|
|
234
|
+
async flush() {
|
|
235
|
+
await activeRender;
|
|
236
|
+
await snapshotWrite;
|
|
237
|
+
await activeRender;
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
function attachInteractiveInput() {
|
|
241
|
+
if (input.isTTY !== true || typeof input.on !== 'function' || typeof input.off !== 'function') {
|
|
242
|
+
return () => undefined;
|
|
243
|
+
}
|
|
244
|
+
let rawModeEnabled = false;
|
|
245
|
+
const onData = (chunk) => {
|
|
246
|
+
void handleInputChunk(chunk);
|
|
247
|
+
};
|
|
248
|
+
try {
|
|
249
|
+
input.setRawMode?.(true);
|
|
250
|
+
rawModeEnabled = true;
|
|
251
|
+
}
|
|
252
|
+
catch {
|
|
253
|
+
return () => undefined;
|
|
254
|
+
}
|
|
255
|
+
input.resume?.();
|
|
256
|
+
input.on('data', onData);
|
|
257
|
+
return () => {
|
|
258
|
+
input.off('data', onData);
|
|
259
|
+
if (rawModeEnabled) {
|
|
260
|
+
try {
|
|
261
|
+
input.setRawMode?.(false);
|
|
262
|
+
}
|
|
263
|
+
catch {
|
|
264
|
+
// Ignore TTY restore failures during shutdown.
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
input.pause?.();
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
async function handleInputChunk(chunk) {
|
|
271
|
+
const text = Buffer.isBuffer(chunk) ? chunk.toString('utf8') : String(chunk);
|
|
272
|
+
for (const character of text) {
|
|
273
|
+
if (escapeSequenceState === 'escape') {
|
|
274
|
+
if (character === '[' || character === 'O') {
|
|
275
|
+
escapeSequenceState = 'control';
|
|
276
|
+
continue;
|
|
277
|
+
}
|
|
278
|
+
escapeSequenceState = 'idle';
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
281
|
+
if (escapeSequenceState === 'control') {
|
|
282
|
+
if (character >= '@' && character <= '~') {
|
|
283
|
+
escapeSequenceState = 'idle';
|
|
284
|
+
}
|
|
285
|
+
continue;
|
|
286
|
+
}
|
|
287
|
+
if (character === '\u001b') {
|
|
288
|
+
escapeSequenceState = 'escape';
|
|
289
|
+
continue;
|
|
290
|
+
}
|
|
291
|
+
if (character === '\u0003') {
|
|
292
|
+
process.kill(process.pid, 'SIGINT');
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
if (isControlCharacter(character)) {
|
|
296
|
+
continue;
|
|
297
|
+
}
|
|
298
|
+
const key = character.toLowerCase();
|
|
299
|
+
if (key === 'p') {
|
|
300
|
+
const enteringPaused = !frameState.paused;
|
|
301
|
+
frameState = {
|
|
302
|
+
...frameState,
|
|
303
|
+
paused: enteringPaused,
|
|
304
|
+
pausedLiveReferenceTime: enteringPaused && renderedState ? deriveLiveReferenceTime(renderedState, deps.now()) : null,
|
|
305
|
+
surfaceMode: enteringPaused ? 'primary' : liveSurfaceMode
|
|
306
|
+
};
|
|
307
|
+
if (frameState.paused) {
|
|
308
|
+
if (activeRender) {
|
|
309
|
+
const hadQueuedRender = clearQueuedRenderState();
|
|
310
|
+
frameState = {
|
|
311
|
+
...frameState,
|
|
312
|
+
pendingUpdate: frameState.pendingUpdate || hadQueuedRender
|
|
313
|
+
};
|
|
314
|
+
if (renderedState) {
|
|
315
|
+
queuedRender = true;
|
|
316
|
+
queuedUseCachedFrame = true;
|
|
317
|
+
}
|
|
318
|
+
continue;
|
|
319
|
+
}
|
|
320
|
+
requestCachedFrameRender();
|
|
321
|
+
}
|
|
322
|
+
else {
|
|
323
|
+
const shouldForceRefresh = frameState.pendingUpdate;
|
|
324
|
+
frameState = {
|
|
325
|
+
...frameState,
|
|
326
|
+
pendingUpdate: false
|
|
327
|
+
};
|
|
328
|
+
queueRender(shouldForceRefresh, false);
|
|
329
|
+
}
|
|
330
|
+
continue;
|
|
331
|
+
}
|
|
332
|
+
if (key === 'c') {
|
|
333
|
+
frameState = {
|
|
334
|
+
...frameState,
|
|
335
|
+
viewMode: frameState.viewMode === 'full' ? 'compact' : 'full'
|
|
336
|
+
};
|
|
337
|
+
requestCachedFrameRender();
|
|
338
|
+
continue;
|
|
339
|
+
}
|
|
340
|
+
if (key === 's') {
|
|
341
|
+
if (!snapshotWrite) {
|
|
342
|
+
snapshotWrite = exportSnapshot().finally(() => {
|
|
343
|
+
snapshotWrite = null;
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
async function exportSnapshot() {
|
|
350
|
+
if (!renderedState) {
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
const timestamp = formatSnapshotTimestamp(deps.now());
|
|
354
|
+
const snapshotDir = join(options.runDir, DASHBOARD_SNAPSHOT_DIRNAME);
|
|
355
|
+
const snapshotPath = join(snapshotDir, `co-status-${timestamp}.txt`);
|
|
356
|
+
try {
|
|
357
|
+
await mkdir(snapshotDir, { recursive: true });
|
|
358
|
+
const frame = renderControlStatusFrame({
|
|
359
|
+
dataset: renderedState.dataset,
|
|
360
|
+
baseUrl: options.baseUrl,
|
|
361
|
+
taskId: options.taskId,
|
|
362
|
+
runId: options.runId,
|
|
363
|
+
runDir: options.runDir,
|
|
364
|
+
startPipelineId: options.startPipelineId,
|
|
365
|
+
showDashboardLine: options.showDashboardLine,
|
|
366
|
+
terminalColumns: output.columns ?? null,
|
|
367
|
+
terminalRows: output.rows ?? null,
|
|
368
|
+
throughputTps: renderedState.throughputTps,
|
|
369
|
+
referenceTime: renderedState.referenceTime,
|
|
370
|
+
liveReferenceTime: resolveDisplayedLiveReferenceTime(renderedState, frameState, deps.now()),
|
|
371
|
+
paused: frameState.paused,
|
|
372
|
+
viewMode: frameState.viewMode,
|
|
373
|
+
surfaceMode: frameState.surfaceMode,
|
|
374
|
+
pendingUpdate: frameState.pendingUpdate,
|
|
375
|
+
snapshotStatus: 'saved',
|
|
376
|
+
snapshotPath,
|
|
377
|
+
snapshotMessage: 'saved'
|
|
378
|
+
});
|
|
379
|
+
await writeFile(snapshotPath, `${stripAnsiSequences(frame)}\n`, 'utf8');
|
|
380
|
+
frameState = {
|
|
381
|
+
...frameState,
|
|
382
|
+
snapshotStatus: 'saved',
|
|
383
|
+
snapshotPath,
|
|
384
|
+
snapshotMessage: 'saved'
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
catch (error) {
|
|
388
|
+
frameState = {
|
|
389
|
+
...frameState,
|
|
390
|
+
snapshotStatus: 'failed',
|
|
391
|
+
snapshotMessage: sanitizeDisplayValue(error?.message ?? String(error))
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
requestCachedFrameRender();
|
|
395
|
+
}
|
|
396
|
+
async function renderFrame(forceRefresh, useCachedFrame) {
|
|
397
|
+
try {
|
|
398
|
+
if (useCachedFrame && renderedState && !forceRefresh) {
|
|
399
|
+
const frame = renderControlStatusFrame({
|
|
400
|
+
dataset: renderedState.dataset,
|
|
401
|
+
baseUrl: options.baseUrl,
|
|
402
|
+
taskId: options.taskId,
|
|
403
|
+
runId: options.runId,
|
|
404
|
+
runDir: options.runDir,
|
|
405
|
+
startPipelineId: options.startPipelineId,
|
|
406
|
+
showDashboardLine: options.showDashboardLine,
|
|
407
|
+
terminalColumns: output.columns ?? null,
|
|
408
|
+
terminalRows: output.rows ?? null,
|
|
409
|
+
throughputTps: renderedState.throughputTps,
|
|
410
|
+
referenceTime: renderedState.referenceTime,
|
|
411
|
+
liveReferenceTime: resolveDisplayedLiveReferenceTime(renderedState, frameState, deps.now()),
|
|
412
|
+
paused: frameState.paused,
|
|
413
|
+
viewMode: frameState.viewMode,
|
|
414
|
+
surfaceMode: frameState.surfaceMode,
|
|
415
|
+
pendingUpdate: frameState.pendingUpdate,
|
|
416
|
+
snapshotStatus: frameState.snapshotStatus,
|
|
417
|
+
snapshotPath: frameState.snapshotPath,
|
|
418
|
+
snapshotMessage: frameState.snapshotMessage
|
|
419
|
+
});
|
|
420
|
+
writeFrame(frame);
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
if (forceRefresh) {
|
|
424
|
+
await options.requestRefresh?.();
|
|
425
|
+
}
|
|
426
|
+
if (stopped) {
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
const readController = new AbortController();
|
|
430
|
+
activeReadController = readController;
|
|
431
|
+
const dataset = await options.readDataset(readController.signal).finally(() => {
|
|
432
|
+
if (activeReadController === readController) {
|
|
433
|
+
activeReadController = null;
|
|
434
|
+
}
|
|
435
|
+
});
|
|
436
|
+
if (stopped) {
|
|
437
|
+
return;
|
|
438
|
+
}
|
|
439
|
+
const now = deps.now();
|
|
440
|
+
const referenceTime = resolveReferenceTime(undefined, dataset.generated_at, now);
|
|
441
|
+
const liveClockStartedAt = renderedState && renderedState.dataset.generated_at === dataset.generated_at
|
|
442
|
+
? renderedState.liveClockStartedAt
|
|
443
|
+
: now;
|
|
444
|
+
tokenSamples = appendTokenSample(tokenSamples, now.getTime(), dataset.totals.total_tokens);
|
|
445
|
+
const throughputTps = rollingTokensPerSecond(tokenSamples);
|
|
446
|
+
const nextRenderedState = {
|
|
447
|
+
dataset,
|
|
448
|
+
referenceTime,
|
|
449
|
+
liveClockStartedAt,
|
|
450
|
+
throughputTps
|
|
451
|
+
};
|
|
452
|
+
if (frameState.paused && renderedState) {
|
|
453
|
+
frameState = {
|
|
454
|
+
...frameState,
|
|
455
|
+
pendingUpdate: true
|
|
456
|
+
};
|
|
457
|
+
return;
|
|
458
|
+
}
|
|
459
|
+
const liveReferenceTime = frameState.paused && frameState.pausedLiveReferenceTime === null
|
|
460
|
+
? deriveLiveReferenceTime(nextRenderedState, now)
|
|
461
|
+
: resolveDisplayedLiveReferenceTime(nextRenderedState, frameState, now);
|
|
462
|
+
renderedState = nextRenderedState;
|
|
463
|
+
frameState = {
|
|
464
|
+
...frameState,
|
|
465
|
+
pausedLiveReferenceTime: frameState.paused && frameState.pausedLiveReferenceTime === null
|
|
466
|
+
? liveReferenceTime
|
|
467
|
+
: frameState.pausedLiveReferenceTime,
|
|
468
|
+
pendingUpdate: frameState.paused ? frameState.pendingUpdate : false
|
|
469
|
+
};
|
|
470
|
+
const frame = renderControlStatusFrame({
|
|
471
|
+
dataset,
|
|
472
|
+
baseUrl: options.baseUrl,
|
|
473
|
+
taskId: options.taskId,
|
|
474
|
+
runId: options.runId,
|
|
475
|
+
runDir: options.runDir,
|
|
476
|
+
startPipelineId: options.startPipelineId,
|
|
477
|
+
showDashboardLine: options.showDashboardLine,
|
|
478
|
+
terminalColumns: output.columns ?? null,
|
|
479
|
+
terminalRows: output.rows ?? null,
|
|
480
|
+
throughputTps,
|
|
481
|
+
referenceTime,
|
|
482
|
+
liveReferenceTime,
|
|
483
|
+
paused: frameState.paused,
|
|
484
|
+
viewMode: frameState.viewMode,
|
|
485
|
+
surfaceMode: frameState.surfaceMode,
|
|
486
|
+
pendingUpdate: frameState.pendingUpdate,
|
|
487
|
+
snapshotStatus: frameState.snapshotStatus,
|
|
488
|
+
snapshotPath: frameState.snapshotPath,
|
|
489
|
+
snapshotMessage: frameState.snapshotMessage
|
|
490
|
+
});
|
|
491
|
+
writeFrame(frame);
|
|
492
|
+
}
|
|
493
|
+
catch (error) {
|
|
494
|
+
const message = error?.message ?? String(error);
|
|
495
|
+
if (stopped) {
|
|
496
|
+
return;
|
|
497
|
+
}
|
|
498
|
+
logger.warn(`Failed rendering CO STATUS dashboard frame: ${message}`);
|
|
499
|
+
writeFrame(renderControlStatusErrorFrame(options, deps.now(), message, output.columns ?? null));
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
function writeFrame(frame) {
|
|
503
|
+
if (frameState.surfaceMode === 'alternate') {
|
|
504
|
+
activePrimaryFrame = null;
|
|
505
|
+
if (activeSurfaceMode !== 'alternate') {
|
|
506
|
+
activeSurfaceMode = 'alternate';
|
|
507
|
+
output.write(`${ANSI_ENTER_ALT_SCREEN}${ANSI_CLEAR_HOME}${frame}`);
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
510
|
+
output.write(`${ANSI_CLEAR_HOME}${frame}`);
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
if (activeSurfaceMode === 'alternate') {
|
|
514
|
+
activeSurfaceMode = 'primary';
|
|
515
|
+
activePrimaryFrame = null;
|
|
516
|
+
primarySurfacePromptNeedsNewline = false;
|
|
517
|
+
output.write(`${ANSI_EXIT_ALT_SCREEN}${ANSI_CLEAR_HOME}${frame}\n`);
|
|
518
|
+
return;
|
|
519
|
+
}
|
|
520
|
+
primarySurfacePromptNeedsNewline = true;
|
|
521
|
+
if (enablePinnedPrimaryLiveRegion) {
|
|
522
|
+
if (activePrimaryFrame !== null) {
|
|
523
|
+
const viewportRows = resolveTerminalRows(output.rows ?? null);
|
|
524
|
+
const previousRowCount = countFrameRows(activePrimaryFrame, output.columns ?? null);
|
|
525
|
+
const currentRowCount = countFrameRows(frame, output.columns ?? null);
|
|
526
|
+
if (previousRowCount > viewportRows || currentRowCount > viewportRows) {
|
|
527
|
+
output.write(`${ANSI_CLEAR_HOME}${frame}`);
|
|
528
|
+
}
|
|
529
|
+
else {
|
|
530
|
+
output.write(rewritePrimaryFrame(frame, previousRowCount));
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
else {
|
|
534
|
+
output.write(frame);
|
|
535
|
+
}
|
|
536
|
+
activePrimaryFrame = frame;
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
539
|
+
activePrimaryFrame = null;
|
|
540
|
+
output.write(`${ANSI_CLEAR_HOME}${frame}`);
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
function countFrameRows(frame, terminalColumns) {
|
|
544
|
+
if (frame.length === 0) {
|
|
545
|
+
return 0;
|
|
546
|
+
}
|
|
547
|
+
const columns = resolveTerminalColumns(terminalColumns);
|
|
548
|
+
return frame
|
|
549
|
+
.split('\n')
|
|
550
|
+
.reduce((rowCount, line) => rowCount + countWrappedTerminalRows(stripAnsiSequences(line), columns), 0);
|
|
551
|
+
}
|
|
552
|
+
function countWrappedTerminalRows(line, terminalColumns) {
|
|
553
|
+
if (terminalColumns <= 0) {
|
|
554
|
+
return 1;
|
|
555
|
+
}
|
|
556
|
+
return Math.max(1, Math.ceil(measureTerminalDisplayWidth(line) / terminalColumns));
|
|
557
|
+
}
|
|
558
|
+
function measureTerminalDisplayWidth(line) {
|
|
559
|
+
if (line.length === 0) {
|
|
560
|
+
return 0;
|
|
561
|
+
}
|
|
562
|
+
if (graphemeSegmenter === null) {
|
|
563
|
+
return Array.from(line).reduce((width, grapheme) => width + measureTerminalGraphemeWidth(grapheme), 0);
|
|
564
|
+
}
|
|
565
|
+
let width = 0;
|
|
566
|
+
for (const { segment } of graphemeSegmenter.segment(line)) {
|
|
567
|
+
width += measureTerminalGraphemeWidth(segment);
|
|
568
|
+
}
|
|
569
|
+
return width;
|
|
570
|
+
}
|
|
571
|
+
function measureTerminalGraphemeWidth(grapheme) {
|
|
572
|
+
if (grapheme.length === 0) {
|
|
573
|
+
return 0;
|
|
574
|
+
}
|
|
575
|
+
if (containsExtendedPictographic(grapheme) || isRegionalIndicatorCluster(grapheme) || isKeycapCluster(grapheme)) {
|
|
576
|
+
return 2;
|
|
577
|
+
}
|
|
578
|
+
let width = 0;
|
|
579
|
+
for (const char of grapheme) {
|
|
580
|
+
width += measureTerminalCodePointWidth(char);
|
|
581
|
+
}
|
|
582
|
+
return width;
|
|
583
|
+
}
|
|
584
|
+
function measureTerminalCodePointWidth(char) {
|
|
585
|
+
const codePoint = char.codePointAt(0);
|
|
586
|
+
if (codePoint === undefined || isZeroWidthCodePoint(codePoint) || combiningMarkPattern.test(char)) {
|
|
587
|
+
return 0;
|
|
588
|
+
}
|
|
589
|
+
return isFullwidthCodePoint(codePoint) ? 2 : 1;
|
|
590
|
+
}
|
|
591
|
+
function containsExtendedPictographic(value) {
|
|
592
|
+
return extendedPictographicPattern.test(value);
|
|
593
|
+
}
|
|
594
|
+
function isRegionalIndicatorCluster(value) {
|
|
595
|
+
const codePoints = Array.from(value, (char) => char.codePointAt(0) ?? 0);
|
|
596
|
+
return codePoints.length > 0 && codePoints.every((codePoint) => codePoint >= 0x1f1e6 && codePoint <= 0x1f1ff);
|
|
597
|
+
}
|
|
598
|
+
function isKeycapCluster(value) {
|
|
599
|
+
return keycapPattern.test(value);
|
|
600
|
+
}
|
|
601
|
+
function isZeroWidthCodePoint(codePoint) {
|
|
602
|
+
return (codePoint < 0x20 ||
|
|
603
|
+
(codePoint >= 0x7f && codePoint < 0xa0) ||
|
|
604
|
+
codePoint === 0x200b ||
|
|
605
|
+
codePoint === 0x200c ||
|
|
606
|
+
codePoint === 0x200d ||
|
|
607
|
+
codePoint === 0x2060 ||
|
|
608
|
+
(codePoint >= 0xfe00 && codePoint <= 0xfe0f) ||
|
|
609
|
+
(codePoint >= 0xe0100 && codePoint <= 0xe01ef));
|
|
610
|
+
}
|
|
611
|
+
function isFullwidthCodePoint(codePoint) {
|
|
612
|
+
return (codePoint >= 0x1100 &&
|
|
613
|
+
(codePoint <= 0x115f ||
|
|
614
|
+
codePoint === 0x2329 ||
|
|
615
|
+
codePoint === 0x232a ||
|
|
616
|
+
(codePoint >= 0x2e80 && codePoint <= 0x3247 && codePoint !== 0x303f) ||
|
|
617
|
+
(codePoint >= 0x3250 && codePoint <= 0x4dbf) ||
|
|
618
|
+
(codePoint >= 0x4e00 && codePoint <= 0xa4c6) ||
|
|
619
|
+
(codePoint >= 0xa960 && codePoint <= 0xa97c) ||
|
|
620
|
+
(codePoint >= 0xac00 && codePoint <= 0xd7a3) ||
|
|
621
|
+
(codePoint >= 0xf900 && codePoint <= 0xfaff) ||
|
|
622
|
+
(codePoint >= 0xfe10 && codePoint <= 0xfe19) ||
|
|
623
|
+
(codePoint >= 0xfe30 && codePoint <= 0xfe6b) ||
|
|
624
|
+
(codePoint >= 0xff01 && codePoint <= 0xff60) ||
|
|
625
|
+
(codePoint >= 0xffe0 && codePoint <= 0xffe6) ||
|
|
626
|
+
(codePoint >= 0x1b000 && codePoint <= 0x1b001) ||
|
|
627
|
+
(codePoint >= 0x1f200 && codePoint <= 0x1f251) ||
|
|
628
|
+
(codePoint >= 0x20000 && codePoint <= 0x3fffd)));
|
|
629
|
+
}
|
|
630
|
+
function rewritePrimaryFrame(frame, previousRowCount) {
|
|
631
|
+
let prefix = '\r';
|
|
632
|
+
if (previousRowCount > 1) {
|
|
633
|
+
prefix += `\u001b[${previousRowCount - 1}A`;
|
|
634
|
+
}
|
|
635
|
+
return `${prefix}${ANSI_CLEAR_DOWN}${frame}`;
|
|
636
|
+
}
|
|
637
|
+
export function renderControlStatusFrame(input) {
|
|
638
|
+
const referenceTime = resolveReferenceTime(input.referenceTime, input.dataset.generated_at);
|
|
639
|
+
const liveReferenceTime = resolveLiveReferenceTime(input.liveReferenceTime, referenceTime);
|
|
640
|
+
const terminalColumns = resolveTerminalColumns(input.terminalColumns);
|
|
641
|
+
const terminalRows = resolveTerminalRows(input.terminalRows);
|
|
642
|
+
const frameState = {
|
|
643
|
+
paused: input.paused === true,
|
|
644
|
+
pausedLiveReferenceTime: null,
|
|
645
|
+
viewMode: input.viewMode ?? 'full',
|
|
646
|
+
surfaceMode: input.surfaceMode ?? (input.paused === true ? 'primary' : 'alternate'),
|
|
647
|
+
pendingUpdate: input.pendingUpdate === true,
|
|
648
|
+
snapshotStatus: input.snapshotStatus ?? 'idle',
|
|
649
|
+
snapshotPath: input.snapshotPath ?? null,
|
|
650
|
+
snapshotMessage: input.snapshotMessage ?? null
|
|
651
|
+
};
|
|
652
|
+
if (frameState.viewMode === 'compact') {
|
|
653
|
+
return renderCompactControlStatusFrame(input, referenceTime, liveReferenceTime, terminalColumns, terminalRows, frameState);
|
|
654
|
+
}
|
|
655
|
+
const runningColumns = selectRunningColumns(terminalColumns);
|
|
656
|
+
const lines = [
|
|
657
|
+
colorize('╭─ CO STATUS', ANSI_BOLD),
|
|
658
|
+
renderAgentsLine(input.dataset, terminalColumns),
|
|
659
|
+
renderThroughputLine(input.throughputTps ?? 0, terminalColumns),
|
|
660
|
+
renderRuntimeLine(input.dataset, referenceTime, liveReferenceTime, terminalColumns),
|
|
661
|
+
renderTokensLine(input.dataset, terminalColumns),
|
|
662
|
+
renderRateLimitsLine(input.dataset, referenceTime, terminalColumns),
|
|
663
|
+
renderProjectLine(input.dataset, terminalColumns),
|
|
664
|
+
renderNextRefreshLine(input.dataset, liveReferenceTime, terminalColumns),
|
|
665
|
+
colorize('├─ Running', ANSI_BOLD),
|
|
666
|
+
'│',
|
|
667
|
+
renderRunningHeaderRow(runningColumns),
|
|
668
|
+
renderRunningSeparatorRow(runningColumns)
|
|
669
|
+
];
|
|
670
|
+
lines.push(...renderRunningRows(input.dataset.running, runningColumns, liveReferenceTime));
|
|
671
|
+
lines.push('│');
|
|
672
|
+
lines.push(colorize('├─ Backoff queue', ANSI_BOLD));
|
|
673
|
+
lines.push('│');
|
|
674
|
+
lines.push(...renderRetryRows(input.dataset.retrying, referenceTime, terminalColumns));
|
|
675
|
+
lines.push('│');
|
|
676
|
+
lines.push(colorize('├─ Status controls', ANSI_BOLD));
|
|
677
|
+
lines.push(renderControlsLine(terminalColumns, frameState));
|
|
678
|
+
lines.push(renderInspectLine(terminalColumns, frameState, linesWillExceedTerminalHeight(terminalRows, lines.length + 3) && frameState.viewMode === 'full'));
|
|
679
|
+
lines.push(renderSnapshotLine(terminalColumns, frameState));
|
|
680
|
+
lines.push('╰─');
|
|
681
|
+
return lines.join('\n');
|
|
682
|
+
}
|
|
683
|
+
function renderCompactControlStatusFrame(input, referenceTime, liveReferenceTime, terminalColumns, terminalRows, frameState) {
|
|
684
|
+
const lines = [
|
|
685
|
+
colorize('╭─ CO STATUS', ANSI_BOLD),
|
|
686
|
+
renderCompactStatusLine(input.dataset, referenceTime, liveReferenceTime, terminalColumns),
|
|
687
|
+
renderTokensLine(input.dataset, terminalColumns),
|
|
688
|
+
renderRateLimitsLine(input.dataset, referenceTime, terminalColumns),
|
|
689
|
+
renderCompactRunningLine(input.dataset.running, liveReferenceTime, terminalColumns),
|
|
690
|
+
renderCompactRetryLine(input.dataset.retrying, referenceTime, terminalColumns),
|
|
691
|
+
renderControlsLine(terminalColumns, frameState),
|
|
692
|
+
renderInspectLine(terminalColumns, frameState, linesWillExceedTerminalHeight(terminalRows, 10)),
|
|
693
|
+
renderSnapshotLine(terminalColumns, frameState),
|
|
694
|
+
'╰─'
|
|
695
|
+
];
|
|
696
|
+
return lines.join('\n');
|
|
697
|
+
}
|
|
698
|
+
function renderControlStatusErrorFrame(input, now, message, terminalColumns) {
|
|
699
|
+
const safe = (value) => sanitizeDisplayValue(value);
|
|
700
|
+
const columns = resolveTerminalColumns(terminalColumns);
|
|
701
|
+
const lines = [
|
|
702
|
+
colorize('╭─ CO STATUS', ANSI_BOLD),
|
|
703
|
+
renderSummaryLine('Generated', [{ text: now.toISOString(), color: ANSI_CYAN }], columns),
|
|
704
|
+
renderSummaryLine('Task', [
|
|
705
|
+
{ text: safe(input.taskId), color: ANSI_CYAN },
|
|
706
|
+
{ text: ' | ', color: ANSI_GRAY },
|
|
707
|
+
{ text: 'Run: ', color: ANSI_BOLD },
|
|
708
|
+
{ text: safe(input.runId), color: ANSI_CYAN }
|
|
709
|
+
], columns),
|
|
710
|
+
renderSummaryLine('Pipeline', [
|
|
711
|
+
{ text: safe(input.startPipelineId), color: ANSI_CYAN },
|
|
712
|
+
{ text: ' | ', color: ANSI_GRAY },
|
|
713
|
+
{ text: 'Run dir: ', color: ANSI_BOLD },
|
|
714
|
+
{ text: safe(input.runDir), color: ANSI_CYAN, truncateMode: 'middle' }
|
|
715
|
+
], columns),
|
|
716
|
+
renderSummaryLine('Dashboard error', [{ text: safe(message), color: ANSI_RED }], columns),
|
|
717
|
+
'╰─'
|
|
718
|
+
];
|
|
719
|
+
return lines.join('\n');
|
|
720
|
+
}
|
|
721
|
+
function renderAgentsLine(dataset, terminalColumns) {
|
|
722
|
+
const maxAllowed = resolveMaxAllowedAgents(dataset);
|
|
723
|
+
return renderSummaryLine('Agents', [
|
|
724
|
+
{ text: formatCount(dataset.counts.running), color: ANSI_GREEN },
|
|
725
|
+
{ text: '/', color: ANSI_GRAY },
|
|
726
|
+
{ text: `${formatOptionalCount(maxAllowed)} max allowed`, color: ANSI_GRAY }
|
|
727
|
+
], terminalColumns);
|
|
728
|
+
}
|
|
729
|
+
function renderThroughputLine(throughputTps, terminalColumns) {
|
|
730
|
+
return renderSummaryLine('Throughput', [{ text: `${formatTps(throughputTps)} tps`, color: ANSI_CYAN }], terminalColumns);
|
|
731
|
+
}
|
|
732
|
+
function renderRuntimeLine(dataset, referenceTime, liveReferenceTime, terminalColumns) {
|
|
733
|
+
return renderSummaryLine('Runtime', [{ text: formatLiveRuntimeSeconds(dataset, referenceTime, liveReferenceTime), color: ANSI_MAGENTA }], terminalColumns);
|
|
734
|
+
}
|
|
735
|
+
function renderTokensLine(dataset, terminalColumns) {
|
|
736
|
+
return renderSummaryLine('Tokens', [
|
|
737
|
+
{ text: `in ${formatOptionalCount(dataset.totals.input_tokens)}`, color: ANSI_YELLOW },
|
|
738
|
+
{ text: ' | ', color: ANSI_GRAY },
|
|
739
|
+
{ text: `out ${formatOptionalCount(dataset.totals.output_tokens)}`, color: ANSI_YELLOW },
|
|
740
|
+
{ text: ' | ', color: ANSI_GRAY },
|
|
741
|
+
{ text: `total ${formatOptionalCount(dataset.totals.total_tokens)}`, color: ANSI_YELLOW },
|
|
742
|
+
{ text: ' | ', color: ANSI_GRAY },
|
|
743
|
+
{
|
|
744
|
+
text: `reasoning ${formatOptionalCount(dataset.totals.reasoning_output_tokens)}`,
|
|
745
|
+
color: ANSI_YELLOW
|
|
746
|
+
}
|
|
747
|
+
], terminalColumns);
|
|
748
|
+
}
|
|
749
|
+
function renderRateLimitsLine(dataset, referenceTime, terminalColumns) {
|
|
750
|
+
return renderSummaryLine('Rate Limits', formatRateLimitSegments(dataset.rate_limits, referenceTime), terminalColumns);
|
|
751
|
+
}
|
|
752
|
+
function renderProjectLine(dataset, terminalColumns) {
|
|
753
|
+
const project = resolveProjectLabel(dataset);
|
|
754
|
+
return renderSummaryLine('Project', [{ text: project, color: project === 'n/a' ? ANSI_GRAY : ANSI_CYAN }], terminalColumns);
|
|
755
|
+
}
|
|
756
|
+
function renderNextRefreshLine(dataset, liveReferenceTime, terminalColumns) {
|
|
757
|
+
const nextRefreshText = resolveNextRefreshSummaryText(dataset.polling);
|
|
758
|
+
const freshnessSegments = buildPollingFreshnessSegments(dataset.polling, liveReferenceTime);
|
|
759
|
+
if (nextRefreshText === 'checking now...') {
|
|
760
|
+
return renderSummaryLine('Next refresh', [{ text: 'checking now...', color: ANSI_CYAN }, ...freshnessSegments], terminalColumns);
|
|
761
|
+
}
|
|
762
|
+
if (nextRefreshText) {
|
|
763
|
+
return renderSummaryLine('Next refresh', [{ text: nextRefreshText, color: ANSI_CYAN }, ...freshnessSegments], terminalColumns);
|
|
764
|
+
}
|
|
765
|
+
return renderSummaryLine('Next refresh', [{ text: 'n/a', color: ANSI_GRAY }, ...freshnessSegments], terminalColumns);
|
|
766
|
+
}
|
|
767
|
+
function renderCompactStatusLine(dataset, referenceTime, liveReferenceTime, terminalColumns) {
|
|
768
|
+
const maxAllowed = resolveMaxAllowedAgents(dataset);
|
|
769
|
+
const nextRefreshText = resolveNextRefreshSummaryText(dataset.polling);
|
|
770
|
+
const sourceFreshnessText = resolvePollingSourceFreshnessText(dataset.polling, liveReferenceTime);
|
|
771
|
+
const refreshText = nextRefreshText === null
|
|
772
|
+
? 'next n/a'
|
|
773
|
+
: nextRefreshText === 'checking now...'
|
|
774
|
+
? nextRefreshText
|
|
775
|
+
: `next ${nextRefreshText}`;
|
|
776
|
+
const freshnessColor = resolvePollingSourceFreshnessColor(dataset.polling, liveReferenceTime);
|
|
777
|
+
return renderSummaryLine('Status', [
|
|
778
|
+
{
|
|
779
|
+
text: `${formatCount(dataset.counts.running)}/${formatOptionalCount(maxAllowed)} max allowed`,
|
|
780
|
+
color: ANSI_GREEN
|
|
781
|
+
},
|
|
782
|
+
{ text: ' | ', color: ANSI_GRAY },
|
|
783
|
+
{ text: formatLiveRuntimeSeconds(dataset, referenceTime, liveReferenceTime), color: ANSI_MAGENTA },
|
|
784
|
+
{ text: ' | ', color: ANSI_GRAY },
|
|
785
|
+
{ text: refreshText, color: ANSI_CYAN },
|
|
786
|
+
...(sourceFreshnessText
|
|
787
|
+
? [
|
|
788
|
+
{ text: ' | ', color: ANSI_GRAY },
|
|
789
|
+
{ text: sourceFreshnessText, color: freshnessColor }
|
|
790
|
+
]
|
|
791
|
+
: [])
|
|
792
|
+
], terminalColumns);
|
|
793
|
+
}
|
|
794
|
+
function resolveNextRefreshSummaryText(polling) {
|
|
795
|
+
const hasProjectedState = polling?.next_refresh_state !== undefined && polling?.next_refresh_state !== null;
|
|
796
|
+
const projectedState = polling?.next_refresh_state === 'cooldown' ||
|
|
797
|
+
polling?.next_refresh_state === 'checking' ||
|
|
798
|
+
polling?.next_refresh_state === 'scheduled' ||
|
|
799
|
+
polling?.next_refresh_state === 'unknown'
|
|
800
|
+
? polling.next_refresh_state
|
|
801
|
+
: null;
|
|
802
|
+
const projectedCountdown = typeof polling?.next_refresh_in_ms === 'number' &&
|
|
803
|
+
Number.isFinite(polling.next_refresh_in_ms) &&
|
|
804
|
+
polling.next_refresh_in_ms >= 0
|
|
805
|
+
? polling.next_refresh_in_ms
|
|
806
|
+
: null;
|
|
807
|
+
if (projectedState === 'checking') {
|
|
808
|
+
return 'checking now...';
|
|
809
|
+
}
|
|
810
|
+
if (projectedState === 'unknown') {
|
|
811
|
+
return null;
|
|
812
|
+
}
|
|
813
|
+
if ((projectedState === 'cooldown' || projectedState === 'scheduled') &&
|
|
814
|
+
projectedCountdown !== null) {
|
|
815
|
+
return formatCountdownMs(projectedCountdown);
|
|
816
|
+
}
|
|
817
|
+
if (hasProjectedState) {
|
|
818
|
+
return null;
|
|
819
|
+
}
|
|
820
|
+
if (polling?.checking) {
|
|
821
|
+
return 'checking now...';
|
|
822
|
+
}
|
|
823
|
+
if (typeof polling?.next_poll_in_ms === 'number' &&
|
|
824
|
+
Number.isFinite(polling.next_poll_in_ms) &&
|
|
825
|
+
polling.next_poll_in_ms >= 0) {
|
|
826
|
+
return formatCountdownMs(polling.next_poll_in_ms);
|
|
827
|
+
}
|
|
828
|
+
return null;
|
|
829
|
+
}
|
|
830
|
+
function buildPollingFreshnessSegments(polling, liveReferenceTime) {
|
|
831
|
+
const freshnessText = resolvePollingSourceFreshnessText(polling, liveReferenceTime);
|
|
832
|
+
if (!freshnessText) {
|
|
833
|
+
return [];
|
|
834
|
+
}
|
|
835
|
+
return [
|
|
836
|
+
{ text: ' | ', color: ANSI_GRAY },
|
|
837
|
+
{
|
|
838
|
+
text: freshnessText,
|
|
839
|
+
color: resolvePollingSourceFreshnessColor(polling, liveReferenceTime)
|
|
840
|
+
}
|
|
841
|
+
];
|
|
842
|
+
}
|
|
843
|
+
function resolvePollingSourceFreshnessText(polling, liveReferenceTime) {
|
|
844
|
+
const freshnessAge = formatRelativePast(polling?.source_updated_at ?? null, liveReferenceTime);
|
|
845
|
+
return freshnessAge === null ? null : `source ${freshnessAge} old`;
|
|
846
|
+
}
|
|
847
|
+
function resolvePollingSourceFreshnessColor(polling, liveReferenceTime) {
|
|
848
|
+
const sourceUpdatedAtMs = parseTimestamp(polling?.source_updated_at ?? null);
|
|
849
|
+
const intervalMs = typeof polling?.interval_ms === 'number' && Number.isFinite(polling.interval_ms) && polling.interval_ms > 0
|
|
850
|
+
? polling.interval_ms
|
|
851
|
+
: null;
|
|
852
|
+
const referenceMs = liveReferenceTime.getTime();
|
|
853
|
+
if (sourceUpdatedAtMs === null || intervalMs === null || !Number.isFinite(referenceMs)) {
|
|
854
|
+
return ANSI_GRAY;
|
|
855
|
+
}
|
|
856
|
+
return referenceMs - sourceUpdatedAtMs > intervalMs ? ANSI_YELLOW : ANSI_GRAY;
|
|
857
|
+
}
|
|
858
|
+
function renderCompactRunningLine(entries, referenceTime, terminalColumns) {
|
|
859
|
+
if (entries.length === 0) {
|
|
860
|
+
return renderSummaryLine('Running', [{ text: 'No active agents', color: ANSI_GRAY }], terminalColumns);
|
|
861
|
+
}
|
|
862
|
+
const [entry] = [...entries].sort((left, right) => left.issue_identifier.localeCompare(right.issue_identifier));
|
|
863
|
+
return renderSummaryLine('Running', [
|
|
864
|
+
{ text: sanitizeDisplayValue(entry.issue_identifier), color: ANSI_CYAN },
|
|
865
|
+
{ text: ' | ', color: ANSI_GRAY },
|
|
866
|
+
{ text: sanitizeDisplayValue(entry.display_state), color: resolveRunningAccent(entry) },
|
|
867
|
+
{ text: ' | ', color: ANSI_GRAY },
|
|
868
|
+
{ text: summarizeRunningEvent(entry, referenceTime), color: ANSI_GRAY, truncateMode: 'end' }
|
|
869
|
+
], terminalColumns);
|
|
870
|
+
}
|
|
871
|
+
function renderCompactRetryLine(entries, referenceTime, terminalColumns) {
|
|
872
|
+
if (entries.length === 0) {
|
|
873
|
+
return renderSummaryLine('Retry', [{ text: 'No queued retries', color: ANSI_GRAY }], terminalColumns);
|
|
874
|
+
}
|
|
875
|
+
const [entry] = [...entries].sort((left, right) => compareDueAt(left.due_at, right.due_at));
|
|
876
|
+
return renderSummaryLine('Retry', [
|
|
877
|
+
{ text: sanitizeDisplayValue(entry.issue_identifier), color: ANSI_RED },
|
|
878
|
+
{ text: ' | ', color: ANSI_GRAY },
|
|
879
|
+
{ text: `${summarizeRetryHeadline(entry)} in ${formatRelativeDue(entry.due_at, referenceTime)}`, color: ANSI_CYAN },
|
|
880
|
+
{ text: ' | ', color: ANSI_GRAY },
|
|
881
|
+
{ text: summarizeRetryDetail(entry), color: ANSI_GRAY, truncateMode: 'end' }
|
|
882
|
+
], terminalColumns);
|
|
883
|
+
}
|
|
884
|
+
function renderControlsLine(terminalColumns, frameState) {
|
|
885
|
+
return renderSummaryLine('Controls', [
|
|
886
|
+
{ text: `p ${frameState.paused ? 'resume live redraw' : 'freeze live redraw'}`, color: ANSI_CYAN },
|
|
887
|
+
{ text: ' | ', color: ANSI_GRAY },
|
|
888
|
+
{ text: `c ${frameState.viewMode === 'full' ? 'compact inspect' : 'full frame'}`, color: ANSI_CYAN },
|
|
889
|
+
{ text: ' | ', color: ANSI_GRAY },
|
|
890
|
+
{ text: 's snapshot export', color: ANSI_CYAN }
|
|
891
|
+
], terminalColumns);
|
|
892
|
+
}
|
|
893
|
+
function renderInspectLine(terminalColumns, frameState, frameExceedsTerminalHeight) {
|
|
894
|
+
const surfaceLabel = frameState.surfaceMode === 'alternate'
|
|
895
|
+
? 'alternate screen'
|
|
896
|
+
: frameState.paused
|
|
897
|
+
? 'primary snapshot'
|
|
898
|
+
: 'primary scrollback';
|
|
899
|
+
const segments = [
|
|
900
|
+
{ text: frameState.paused ? 'paused' : 'live', color: frameState.paused ? ANSI_YELLOW : ANSI_GREEN },
|
|
901
|
+
{ text: ' | ', color: ANSI_GRAY },
|
|
902
|
+
{ text: surfaceLabel, color: frameState.surfaceMode === 'alternate' ? ANSI_BLUE : ANSI_MAGENTA },
|
|
903
|
+
{ text: ' | ', color: ANSI_GRAY },
|
|
904
|
+
{ text: frameState.viewMode === 'compact' ? 'compact inspect' : 'full frame', color: ANSI_CYAN }
|
|
905
|
+
];
|
|
906
|
+
if (frameState.pendingUpdate) {
|
|
907
|
+
segments.push({ text: ' | ', color: ANSI_GRAY });
|
|
908
|
+
segments.push({ text: 'updates waiting', color: ANSI_YELLOW });
|
|
909
|
+
}
|
|
910
|
+
if (frameExceedsTerminalHeight && frameState.viewMode === 'full') {
|
|
911
|
+
segments.push({ text: ' | ', color: ANSI_GRAY });
|
|
912
|
+
segments.push({ text: 'short terminal: pause then compact', color: ANSI_RED });
|
|
913
|
+
}
|
|
914
|
+
return renderSummaryLine('Inspect', segments, terminalColumns);
|
|
915
|
+
}
|
|
916
|
+
function renderSnapshotLine(terminalColumns, frameState) {
|
|
917
|
+
if (frameState.snapshotStatus === 'saved' && frameState.snapshotPath) {
|
|
918
|
+
return renderSummaryLine('Snapshot', [
|
|
919
|
+
{ text: 'saved', color: ANSI_GREEN },
|
|
920
|
+
{ text: ' | ', color: ANSI_GRAY },
|
|
921
|
+
{ text: frameState.snapshotPath, color: ANSI_CYAN, truncateMode: 'middle' }
|
|
922
|
+
], terminalColumns);
|
|
923
|
+
}
|
|
924
|
+
if (frameState.snapshotStatus === 'failed') {
|
|
925
|
+
return renderSummaryLine('Snapshot', [
|
|
926
|
+
{ text: 'save failed', color: ANSI_RED },
|
|
927
|
+
{ text: ' | ', color: ANSI_GRAY },
|
|
928
|
+
{ text: frameState.snapshotMessage ?? 'unknown error', color: ANSI_GRAY, truncateMode: 'end' }
|
|
929
|
+
], terminalColumns);
|
|
930
|
+
}
|
|
931
|
+
return renderSummaryLine('Snapshot', [{ text: 'press s to export a stable frame under run dir', color: ANSI_GRAY, truncateMode: 'end' }], terminalColumns);
|
|
932
|
+
}
|
|
933
|
+
function renderRunningHeaderRow(columns) {
|
|
934
|
+
const labels = columns.map((column) => formatCell(column.label, column.width));
|
|
935
|
+
return `│ ${colorize(labels.join(' '), ANSI_GRAY)}`;
|
|
936
|
+
}
|
|
937
|
+
function renderRunningSeparatorRow(columns) {
|
|
938
|
+
const separatorWidth = columns.reduce((sum, column) => sum + column.width, 0) + Math.max(0, columns.length - 1);
|
|
939
|
+
return `│ ${colorize('─'.repeat(separatorWidth), ANSI_GRAY)}`;
|
|
940
|
+
}
|
|
941
|
+
function renderRunningRows(entries, columns, referenceTime) {
|
|
942
|
+
if (entries.length === 0) {
|
|
943
|
+
return [`│ ${colorize('No active agents', ANSI_GRAY)}`];
|
|
944
|
+
}
|
|
945
|
+
return [...entries]
|
|
946
|
+
.sort((left, right) => left.issue_identifier.localeCompare(right.issue_identifier))
|
|
947
|
+
.map((entry) => renderRunningRow(entry, columns, referenceTime));
|
|
948
|
+
}
|
|
949
|
+
function renderRunningRow(entry, columns, referenceTime) {
|
|
950
|
+
const accent = resolveRunningAccent(entry);
|
|
951
|
+
const cells = columns.map((column) => {
|
|
952
|
+
const value = formatRunningColumnValue(entry, column.key, referenceTime);
|
|
953
|
+
const formatted = formatCell(value, column.width, column.align);
|
|
954
|
+
switch (column.key) {
|
|
955
|
+
case 'id':
|
|
956
|
+
return colorize(formatted, ANSI_CYAN);
|
|
957
|
+
case 'age':
|
|
958
|
+
return colorize(formatted, ANSI_MAGENTA);
|
|
959
|
+
case 'tokens':
|
|
960
|
+
return colorize(formatted, ANSI_YELLOW);
|
|
961
|
+
case 'pid':
|
|
962
|
+
return colorize(formatted, ANSI_YELLOW);
|
|
963
|
+
case 'session':
|
|
964
|
+
return colorize(formatted, ANSI_CYAN);
|
|
965
|
+
case 'stage':
|
|
966
|
+
case 'event':
|
|
967
|
+
return colorize(formatted, accent);
|
|
968
|
+
default:
|
|
969
|
+
return formatted;
|
|
970
|
+
}
|
|
971
|
+
});
|
|
972
|
+
return `│ ${colorize('●', accent)} ${cells.join(' ')}`;
|
|
973
|
+
}
|
|
974
|
+
function renderRetryRows(entries, referenceTime, terminalColumns) {
|
|
975
|
+
if (entries.length === 0) {
|
|
976
|
+
return [`│ ${colorize('No queued retries', ANSI_GRAY)}`];
|
|
977
|
+
}
|
|
978
|
+
return [...entries]
|
|
979
|
+
.sort((left, right) => compareDueAt(left.due_at, right.due_at))
|
|
980
|
+
.map((entry) => renderRetryRow(entry, referenceTime, terminalColumns));
|
|
981
|
+
}
|
|
982
|
+
function renderRetryRow(entry, referenceTime, terminalColumns) {
|
|
983
|
+
const issueIdentifier = sanitizeDisplayValue(entry.issue_identifier);
|
|
984
|
+
const attempt = formatNullable(entry.attempt);
|
|
985
|
+
const relativeDue = formatRelativeDue(entry.due_at, referenceTime);
|
|
986
|
+
const plainPrefix = `│ ↻ ${issueIdentifier} `;
|
|
987
|
+
const coloredPrefix = `│ ${colorize('↻', ANSI_YELLOW)} ${colorize(issueIdentifier, ANSI_RED)} `;
|
|
988
|
+
const detail = summarizeRetryDetail(entry);
|
|
989
|
+
const segments = [
|
|
990
|
+
{ text: summarizeRetryHeadline(entry), color: ANSI_YELLOW },
|
|
991
|
+
{ text: ' in ', color: ANSI_DIM },
|
|
992
|
+
{ text: relativeDue, color: ANSI_CYAN }
|
|
993
|
+
];
|
|
994
|
+
if (attempt !== '-') {
|
|
995
|
+
segments.push({ text: ' | ', color: ANSI_GRAY });
|
|
996
|
+
segments.push({ text: `attempt ${attempt}`, color: ANSI_YELLOW });
|
|
997
|
+
}
|
|
998
|
+
if (detail !== 'n/a') {
|
|
999
|
+
segments.push({ text: ' | ', color: ANSI_GRAY });
|
|
1000
|
+
segments.push({ text: detail, color: ANSI_GRAY, truncateMode: 'end' });
|
|
1001
|
+
}
|
|
1002
|
+
return coloredPrefix + colorizeSummarySegments(segments, Math.max(0, terminalColumns - plainPrefix.length));
|
|
1003
|
+
}
|
|
1004
|
+
function resolveRunningAccent(entry) {
|
|
1005
|
+
const lastEvent = normalizeRunningEventKey(entry.last_event) ?? '';
|
|
1006
|
+
const displayState = sanitizeDisplayValue(entry.display_state).toLowerCase();
|
|
1007
|
+
if (lastEvent.includes('turn_completed')) {
|
|
1008
|
+
return ANSI_MAGENTA;
|
|
1009
|
+
}
|
|
1010
|
+
if (lastEvent.includes('task_started') || lastEvent.includes('turn_started')) {
|
|
1011
|
+
return ANSI_GREEN;
|
|
1012
|
+
}
|
|
1013
|
+
if (lastEvent.includes('token')) {
|
|
1014
|
+
return ANSI_YELLOW;
|
|
1015
|
+
}
|
|
1016
|
+
if (displayState.includes('retry')) {
|
|
1017
|
+
return ANSI_BLUE;
|
|
1018
|
+
}
|
|
1019
|
+
if (displayState.includes('fail') || displayState.includes('error')) {
|
|
1020
|
+
return ANSI_RED;
|
|
1021
|
+
}
|
|
1022
|
+
return ANSI_BLUE;
|
|
1023
|
+
}
|
|
1024
|
+
function formatRunningColumnValue(entry, key, referenceTime) {
|
|
1025
|
+
switch (key) {
|
|
1026
|
+
case 'id':
|
|
1027
|
+
return sanitizeDisplayValue(entry.issue_identifier);
|
|
1028
|
+
case 'stage':
|
|
1029
|
+
return sanitizeDisplayValue(entry.display_state);
|
|
1030
|
+
case 'pid':
|
|
1031
|
+
return formatPid(entry.pid);
|
|
1032
|
+
case 'age':
|
|
1033
|
+
return formatRuntimeAndTurns(entry.started_at, referenceTime, entry.turn_count);
|
|
1034
|
+
case 'tokens':
|
|
1035
|
+
return formatOptionalCount(entry.tokens.total_tokens);
|
|
1036
|
+
case 'session':
|
|
1037
|
+
return compactSessionId(entry.session_id);
|
|
1038
|
+
case 'event':
|
|
1039
|
+
return summarizeRunningEvent(entry, referenceTime);
|
|
1040
|
+
default:
|
|
1041
|
+
return 'n/a';
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
function summarizeRunningEvent(entry, referenceTime) {
|
|
1045
|
+
const displayEvent = sanitizeDisplayValue(entry.display_event);
|
|
1046
|
+
if (displayEvent !== '-') {
|
|
1047
|
+
return appendWorkerHostSummary(displayEvent, entry.worker_host);
|
|
1048
|
+
}
|
|
1049
|
+
const displayState = sanitizeDisplayValue(entry.display_state).toLowerCase();
|
|
1050
|
+
const lastMessage = sanitizeDisplayValue(entry.last_message);
|
|
1051
|
+
if (isHighSignalStatusText(lastMessage, displayState)) {
|
|
1052
|
+
return appendWorkerHostSummary(lastMessage, entry.worker_host);
|
|
1053
|
+
}
|
|
1054
|
+
const summary = sanitizeDisplayValue(entry.summary);
|
|
1055
|
+
if (isHighSignalStatusText(summary, displayState) && !sameStatusText(summary, lastMessage)) {
|
|
1056
|
+
return appendWorkerHostSummary(summary, entry.worker_host);
|
|
1057
|
+
}
|
|
1058
|
+
const humanizedEvent = humanizeRunningEvent(entry.last_event);
|
|
1059
|
+
const eventAge = formatRelativePast(entry.last_event_at, referenceTime);
|
|
1060
|
+
if (isHighSignalStatusText(humanizedEvent, displayState)) {
|
|
1061
|
+
return appendWorkerHostSummary(humanizedEvent, entry.worker_host);
|
|
1062
|
+
}
|
|
1063
|
+
const statusReason = humanizeRunningEvent(entry.status_reason);
|
|
1064
|
+
if (isHighSignalStatusText(statusReason, displayState)) {
|
|
1065
|
+
return appendWorkerHostSummary(statusReason, entry.worker_host);
|
|
1066
|
+
}
|
|
1067
|
+
if (humanizedEvent !== 'n/a' && eventAge !== null) {
|
|
1068
|
+
return appendWorkerHostSummary(`${humanizedEvent} (${eventAge} ago)`, entry.worker_host);
|
|
1069
|
+
}
|
|
1070
|
+
if (summary !== '-') {
|
|
1071
|
+
return appendWorkerHostSummary(summary, entry.worker_host);
|
|
1072
|
+
}
|
|
1073
|
+
if (lastMessage !== '-') {
|
|
1074
|
+
return appendWorkerHostSummary(eventAge === null ? lastMessage : `${lastMessage} (${eventAge} ago)`, entry.worker_host);
|
|
1075
|
+
}
|
|
1076
|
+
return appendWorkerHostSummary('n/a', entry.worker_host);
|
|
1077
|
+
}
|
|
1078
|
+
function summarizeRetryHeadline(entry) {
|
|
1079
|
+
const lastEvent = humanizeRunningEvent(entry.last_event);
|
|
1080
|
+
if (lastEvent !== 'n/a') {
|
|
1081
|
+
return lastEvent;
|
|
1082
|
+
}
|
|
1083
|
+
const statusReason = humanizeRunningEvent(entry.status_reason);
|
|
1084
|
+
if (statusReason !== 'n/a') {
|
|
1085
|
+
return statusReason;
|
|
1086
|
+
}
|
|
1087
|
+
const displayState = sanitizeDisplayValue(entry.display_state);
|
|
1088
|
+
return displayState === '-' ? 'retrying' : displayState;
|
|
1089
|
+
}
|
|
1090
|
+
function summarizeRetryDetail(entry) {
|
|
1091
|
+
const headline = summarizeRetryHeadline(entry).toLowerCase();
|
|
1092
|
+
const displayState = sanitizeDisplayValue(entry.display_state).toLowerCase();
|
|
1093
|
+
const error = sanitizeDisplayValue(entry.error);
|
|
1094
|
+
if (isHighSignalStatusText(error, displayState) && !sameStatusText(error, headline)) {
|
|
1095
|
+
return appendWorkerHostSummary(error, entry.worker_host);
|
|
1096
|
+
}
|
|
1097
|
+
const summary = sanitizeDisplayValue(entry.summary);
|
|
1098
|
+
if (isHighSignalStatusText(summary, displayState) && !sameStatusText(summary, headline)) {
|
|
1099
|
+
return appendWorkerHostSummary(summary, entry.worker_host);
|
|
1100
|
+
}
|
|
1101
|
+
const lastMessage = sanitizeDisplayValue(entry.last_message);
|
|
1102
|
+
if (isHighSignalStatusText(lastMessage, displayState) && !sameStatusText(lastMessage, headline)) {
|
|
1103
|
+
return appendWorkerHostSummary(lastMessage, entry.worker_host);
|
|
1104
|
+
}
|
|
1105
|
+
const statusReason = humanizeRunningEvent(entry.status_reason);
|
|
1106
|
+
if (isHighSignalStatusText(statusReason, displayState) && !sameStatusText(statusReason, headline)) {
|
|
1107
|
+
return appendWorkerHostSummary(statusReason, entry.worker_host);
|
|
1108
|
+
}
|
|
1109
|
+
return appendWorkerHostSummary('n/a', entry.worker_host);
|
|
1110
|
+
}
|
|
1111
|
+
function appendWorkerHostSummary(summary, workerHost) {
|
|
1112
|
+
const displayWorkerHost = sanitizeDisplayValue(workerHost);
|
|
1113
|
+
if (displayWorkerHost === '-') {
|
|
1114
|
+
return summary;
|
|
1115
|
+
}
|
|
1116
|
+
if (summary === 'n/a') {
|
|
1117
|
+
return `worker ${displayWorkerHost}`;
|
|
1118
|
+
}
|
|
1119
|
+
return `${displayWorkerHost} | ${summary}`;
|
|
1120
|
+
}
|
|
1121
|
+
function selectRunningColumns(terminalColumns) {
|
|
1122
|
+
const baseColumns = terminalColumns >= 120
|
|
1123
|
+
? [
|
|
1124
|
+
{ key: 'id', label: 'ID', width: 10 },
|
|
1125
|
+
{ key: 'stage', label: 'STAGE', width: 12 },
|
|
1126
|
+
{ key: 'pid', label: 'PID', width: 8 },
|
|
1127
|
+
{ key: 'age', label: 'AGE / TURN', width: 12 },
|
|
1128
|
+
{ key: 'tokens', label: 'TOKENS', width: 10, align: 'right' },
|
|
1129
|
+
{ key: 'session', label: 'SESSION', width: 14 },
|
|
1130
|
+
{ key: 'event', label: 'EVENT', width: 0 }
|
|
1131
|
+
]
|
|
1132
|
+
: terminalColumns >= 96
|
|
1133
|
+
? [
|
|
1134
|
+
{ key: 'id', label: 'ID', width: 9 },
|
|
1135
|
+
{ key: 'stage', label: 'STAGE', width: 10 },
|
|
1136
|
+
{ key: 'pid', label: 'PID', width: 7 },
|
|
1137
|
+
{ key: 'age', label: 'AGE / TURN', width: 12 },
|
|
1138
|
+
{ key: 'tokens', label: 'TOKENS', width: 9, align: 'right' },
|
|
1139
|
+
{ key: 'session', label: 'SESSION', width: 12 },
|
|
1140
|
+
{ key: 'event', label: 'EVENT', width: 0 }
|
|
1141
|
+
]
|
|
1142
|
+
: terminalColumns >= 78
|
|
1143
|
+
? [
|
|
1144
|
+
{ key: 'id', label: 'ID', width: 9 },
|
|
1145
|
+
{ key: 'stage', label: 'STAGE', width: 10 },
|
|
1146
|
+
{ key: 'pid', label: 'PID', width: 7 },
|
|
1147
|
+
{ key: 'tokens', label: 'TOKENS', width: 9, align: 'right' },
|
|
1148
|
+
{ key: 'event', label: 'EVENT', width: 0 }
|
|
1149
|
+
]
|
|
1150
|
+
: [
|
|
1151
|
+
{ key: 'id', label: 'ID', width: 8 },
|
|
1152
|
+
{ key: 'stage', label: 'STAGE', width: 8 },
|
|
1153
|
+
{ key: 'event', label: 'EVENT', width: 0 }
|
|
1154
|
+
];
|
|
1155
|
+
const fixedWidth = baseColumns
|
|
1156
|
+
.filter((column) => column.key !== 'event')
|
|
1157
|
+
.reduce((sum, column) => sum + column.width, 0);
|
|
1158
|
+
const gapWidth = Math.max(0, baseColumns.length - 1);
|
|
1159
|
+
const minimumEventWidth = terminalColumns >= 96 ? 18 : 12;
|
|
1160
|
+
const eventWidth = Math.max(minimumEventWidth, terminalColumns - 4 - fixedWidth - gapWidth);
|
|
1161
|
+
return baseColumns.map((column) => column.key === 'event' ? { ...column, width: eventWidth } : column);
|
|
1162
|
+
}
|
|
1163
|
+
function resolveProjectLabel(dataset) {
|
|
1164
|
+
const projectName = dataset.tracked?.linear?.project_name ??
|
|
1165
|
+
dataset.selected?.tracked?.linear?.project_name ??
|
|
1166
|
+
dataset.issues
|
|
1167
|
+
.map((issue) => issue.tracked?.linear?.project_name ?? null)
|
|
1168
|
+
.find((value) => typeof value === 'string' && value.trim().length > 0) ??
|
|
1169
|
+
null;
|
|
1170
|
+
if (!projectName) {
|
|
1171
|
+
return 'n/a';
|
|
1172
|
+
}
|
|
1173
|
+
return sanitizeDisplayValue(projectName);
|
|
1174
|
+
}
|
|
1175
|
+
function appendTokenSample(samples, timestampMs, totalTokens) {
|
|
1176
|
+
const normalizedTotal = normalizeFiniteNumber(totalTokens);
|
|
1177
|
+
if (totalTokens === null || totalTokens === undefined || !Number.isFinite(totalTokens)) {
|
|
1178
|
+
return samples.filter((sample) => sample.timestampMs >= timestampMs - THROUGHPUT_WINDOW_MS);
|
|
1179
|
+
}
|
|
1180
|
+
const baselineSamples = samples.length > 0 && normalizedTotal < samples[0].totalTokens ? [] : samples;
|
|
1181
|
+
return [{ timestampMs, totalTokens: normalizedTotal }, ...baselineSamples].filter((sample) => sample.timestampMs >= timestampMs - THROUGHPUT_WINDOW_MS);
|
|
1182
|
+
}
|
|
1183
|
+
function rollingTokensPerSecond(samples) {
|
|
1184
|
+
if (samples.length < 2) {
|
|
1185
|
+
return 0;
|
|
1186
|
+
}
|
|
1187
|
+
const newest = samples[0];
|
|
1188
|
+
const oldest = samples[samples.length - 1];
|
|
1189
|
+
const elapsedMs = newest.timestampMs - oldest.timestampMs;
|
|
1190
|
+
const deltaTokens = Math.max(0, newest.totalTokens - oldest.totalTokens);
|
|
1191
|
+
if (elapsedMs <= 0) {
|
|
1192
|
+
return 0;
|
|
1193
|
+
}
|
|
1194
|
+
return deltaTokens / (elapsedMs / 1000);
|
|
1195
|
+
}
|
|
1196
|
+
function compareDueAt(left, right) {
|
|
1197
|
+
const leftMs = parseTimestamp(left);
|
|
1198
|
+
const rightMs = parseTimestamp(right);
|
|
1199
|
+
if (leftMs === null && rightMs === null) {
|
|
1200
|
+
return 0;
|
|
1201
|
+
}
|
|
1202
|
+
if (leftMs === null) {
|
|
1203
|
+
return 1;
|
|
1204
|
+
}
|
|
1205
|
+
if (rightMs === null) {
|
|
1206
|
+
return -1;
|
|
1207
|
+
}
|
|
1208
|
+
return leftMs - rightMs;
|
|
1209
|
+
}
|
|
1210
|
+
function formatRelativeDue(dueAt, referenceTime) {
|
|
1211
|
+
const dueTimestamp = parseTimestamp(dueAt);
|
|
1212
|
+
if (dueTimestamp === null) {
|
|
1213
|
+
return 'n/a';
|
|
1214
|
+
}
|
|
1215
|
+
const remainingMs = Math.max(0, dueTimestamp - referenceTime.getTime());
|
|
1216
|
+
return formatCountdownMs(remainingMs);
|
|
1217
|
+
}
|
|
1218
|
+
function formatRelativePast(timestamp, referenceTime) {
|
|
1219
|
+
const parsedTimestamp = parseTimestamp(timestamp);
|
|
1220
|
+
if (parsedTimestamp === null) {
|
|
1221
|
+
return null;
|
|
1222
|
+
}
|
|
1223
|
+
const elapsedSeconds = Math.max(0, Math.floor((referenceTime.getTime() - parsedTimestamp) / 1000));
|
|
1224
|
+
return formatHumanDurationShort(elapsedSeconds);
|
|
1225
|
+
}
|
|
1226
|
+
function formatRuntimeAndTurns(startedAt, referenceTime, turnCount) {
|
|
1227
|
+
const startedTimestamp = parseTimestamp(startedAt);
|
|
1228
|
+
const runtime = startedTimestamp === null
|
|
1229
|
+
? 'n/a'
|
|
1230
|
+
: formatRuntimeSeconds(Math.max(0, (referenceTime.getTime() - startedTimestamp) / 1000));
|
|
1231
|
+
const turns = typeof turnCount === 'number' && Number.isFinite(turnCount) && turnCount >= 0
|
|
1232
|
+
? String(Math.floor(turnCount))
|
|
1233
|
+
: 'n/a';
|
|
1234
|
+
return `${runtime} / ${turns}`;
|
|
1235
|
+
}
|
|
1236
|
+
function formatRuntimeSeconds(value) {
|
|
1237
|
+
return formatHumanDurationShort(Math.max(0, Math.floor(normalizeFiniteNumber(value))));
|
|
1238
|
+
}
|
|
1239
|
+
function formatLiveRuntimeSeconds(dataset, referenceTime, liveReferenceTime) {
|
|
1240
|
+
const liveDeltaSeconds = Math.max(0, (liveReferenceTime.getTime() - referenceTime.getTime()) / 1000);
|
|
1241
|
+
return formatRuntimeSeconds(normalizeFiniteNumber(dataset.totals.seconds_running) + liveDeltaSeconds);
|
|
1242
|
+
}
|
|
1243
|
+
function formatTps(value) {
|
|
1244
|
+
return formatCount(Math.max(0, Math.floor(normalizeFiniteNumber(value))));
|
|
1245
|
+
}
|
|
1246
|
+
function formatCount(value) {
|
|
1247
|
+
if (typeof value === 'string') {
|
|
1248
|
+
const trimmed = value.trim();
|
|
1249
|
+
if (trimmed.length === 0) {
|
|
1250
|
+
return '0';
|
|
1251
|
+
}
|
|
1252
|
+
const parsed = Number(trimmed);
|
|
1253
|
+
if (Number.isFinite(parsed)) {
|
|
1254
|
+
return NUMBER_FORMAT.format(Math.trunc(parsed));
|
|
1255
|
+
}
|
|
1256
|
+
return sanitizeDisplayValue(trimmed);
|
|
1257
|
+
}
|
|
1258
|
+
if (typeof value === 'number' && Number.isFinite(value)) {
|
|
1259
|
+
return NUMBER_FORMAT.format(Math.trunc(value));
|
|
1260
|
+
}
|
|
1261
|
+
return '0';
|
|
1262
|
+
}
|
|
1263
|
+
function formatOptionalCount(value) {
|
|
1264
|
+
if (value === null || value === undefined) {
|
|
1265
|
+
return 'n/a';
|
|
1266
|
+
}
|
|
1267
|
+
if (typeof value === 'string') {
|
|
1268
|
+
const trimmed = value.trim();
|
|
1269
|
+
if (trimmed.length === 0) {
|
|
1270
|
+
return 'n/a';
|
|
1271
|
+
}
|
|
1272
|
+
const parsed = Number(trimmed);
|
|
1273
|
+
if (Number.isFinite(parsed)) {
|
|
1274
|
+
return NUMBER_FORMAT.format(Math.trunc(parsed));
|
|
1275
|
+
}
|
|
1276
|
+
return sanitizeDisplayValue(trimmed);
|
|
1277
|
+
}
|
|
1278
|
+
if (typeof value === 'number' && Number.isFinite(value)) {
|
|
1279
|
+
return NUMBER_FORMAT.format(Math.trunc(value));
|
|
1280
|
+
}
|
|
1281
|
+
return 'n/a';
|
|
1282
|
+
}
|
|
1283
|
+
function formatRateLimitSegments(value, referenceTime) {
|
|
1284
|
+
if (!value || Object.keys(value).length === 0) {
|
|
1285
|
+
return [{ text: 'unavailable', color: ANSI_GRAY }];
|
|
1286
|
+
}
|
|
1287
|
+
const combined = formatCombinedRateLimitSegments(value, referenceTime);
|
|
1288
|
+
if (combined) {
|
|
1289
|
+
return combined;
|
|
1290
|
+
}
|
|
1291
|
+
const codexRateLimits = formatCodexRateLimitSegments(value, referenceTime);
|
|
1292
|
+
if (codexRateLimits) {
|
|
1293
|
+
return codexRateLimits;
|
|
1294
|
+
}
|
|
1295
|
+
const linearBudget = formatLinearBudgetSegments(value, referenceTime);
|
|
1296
|
+
if (linearBudget) {
|
|
1297
|
+
return linearBudget;
|
|
1298
|
+
}
|
|
1299
|
+
return [{ text: formatRecord(value), color: ANSI_GRAY }];
|
|
1300
|
+
}
|
|
1301
|
+
function formatCombinedRateLimitSegments(value, referenceTime) {
|
|
1302
|
+
const codex = asRecord(value.codex);
|
|
1303
|
+
const linearBudget = asRecord(value.linear_budget) ?? asRecord(value.linearBudget);
|
|
1304
|
+
if (!codex || !linearBudget) {
|
|
1305
|
+
return null;
|
|
1306
|
+
}
|
|
1307
|
+
const pieces = [];
|
|
1308
|
+
const codexPieces = formatCompactCodexRateLimitSegments(codex, referenceTime, {
|
|
1309
|
+
allowLegacyMetadata: true
|
|
1310
|
+
});
|
|
1311
|
+
if (codexPieces) {
|
|
1312
|
+
pieces.push(...codexPieces);
|
|
1313
|
+
}
|
|
1314
|
+
const linearPieces = formatCompactLinearBudgetSegments(linearBudget, referenceTime);
|
|
1315
|
+
if (linearPieces) {
|
|
1316
|
+
if (pieces.length > 0) {
|
|
1317
|
+
pieces.push({ text: ' || ', color: ANSI_GRAY });
|
|
1318
|
+
}
|
|
1319
|
+
pieces.push(...linearPieces);
|
|
1320
|
+
}
|
|
1321
|
+
return pieces.length > 0 ? pieces : null;
|
|
1322
|
+
}
|
|
1323
|
+
function formatCompactCodexRateLimitSegments(value, referenceTime, options = {}) {
|
|
1324
|
+
return formatOperatorCodexRateLimitSegments(value, referenceTime, {
|
|
1325
|
+
allowLegacyMetadata: options.allowLegacyMetadata,
|
|
1326
|
+
formatBucket: formatCompactRateLimitBucket,
|
|
1327
|
+
formatCredits: formatCompactRateLimitCredits
|
|
1328
|
+
});
|
|
1329
|
+
}
|
|
1330
|
+
function formatCodexRateLimitSegments(value, referenceTime, options = {}) {
|
|
1331
|
+
return formatOperatorCodexRateLimitSegments(value, referenceTime, {
|
|
1332
|
+
allowLegacyMetadata: options.allowLegacyMetadata,
|
|
1333
|
+
formatBucket: formatRateLimitBucket,
|
|
1334
|
+
formatCredits: formatRateLimitCredits
|
|
1335
|
+
});
|
|
1336
|
+
}
|
|
1337
|
+
function formatOperatorCodexRateLimitSegments(value, referenceTime, options) {
|
|
1338
|
+
const limitId = readRecordString(value, ['limit_id', 'limitId', 'limit_name', 'limitName']);
|
|
1339
|
+
const primary = asRecord(value.primary);
|
|
1340
|
+
const secondary = asRecord(value.secondary);
|
|
1341
|
+
const credits = asRecord(value.credits);
|
|
1342
|
+
const requests = asRecord(value.requests);
|
|
1343
|
+
const endpointRequests = asRecord(value.endpoint_requests);
|
|
1344
|
+
const observedAt = readRecordString(value, ['observed_at', 'observedAt']);
|
|
1345
|
+
const suppression = readRecordString(value, ['suppression']);
|
|
1346
|
+
const retryAfterSeconds = readRecordNumber(value, ['retry_after_seconds', 'retryAfterSeconds']);
|
|
1347
|
+
if (!limitId && !primary && !secondary && !credits) {
|
|
1348
|
+
if (!requests && !endpointRequests) {
|
|
1349
|
+
return null;
|
|
1350
|
+
}
|
|
1351
|
+
if (options.allowLegacyMetadata !== true &&
|
|
1352
|
+
(observedAt !== null || suppression !== null || retryAfterSeconds !== null)) {
|
|
1353
|
+
return null;
|
|
1354
|
+
}
|
|
1355
|
+
const pieces = [{ text: 'Codex', color: ANSI_YELLOW }];
|
|
1356
|
+
const displayRequests = selectOperatorVisibleBudgetBucket(requests, endpointRequests, referenceTime);
|
|
1357
|
+
if (displayRequests) {
|
|
1358
|
+
appendOperatorRateLimitSegment(pieces, `requests ${options.formatBucket(displayRequests, referenceTime)}`, ANSI_CYAN);
|
|
1359
|
+
}
|
|
1360
|
+
return pieces;
|
|
1361
|
+
}
|
|
1362
|
+
const pieces = [{ text: 'Codex', color: ANSI_YELLOW }];
|
|
1363
|
+
if (primary) {
|
|
1364
|
+
appendOperatorRateLimitSegment(pieces, `${resolveCodexRateLimitBucketLabel(primary) ?? 'primary'} ${options.formatBucket(primary, referenceTime)}`, ANSI_CYAN);
|
|
1365
|
+
}
|
|
1366
|
+
if (secondary) {
|
|
1367
|
+
appendOperatorRateLimitSegment(pieces, `${resolveCodexRateLimitBucketLabel(secondary) ?? 'secondary'} ${options.formatBucket(secondary, referenceTime)}`, ANSI_CYAN);
|
|
1368
|
+
}
|
|
1369
|
+
if (credits) {
|
|
1370
|
+
appendOperatorRateLimitSegment(pieces, options.formatCredits(credits), ANSI_GREEN);
|
|
1371
|
+
}
|
|
1372
|
+
return pieces;
|
|
1373
|
+
}
|
|
1374
|
+
function resolveCodexRateLimitBucketLabel(bucket) {
|
|
1375
|
+
const windowDurationMins = readRecordNumber(bucket, [
|
|
1376
|
+
'windowDurationMins',
|
|
1377
|
+
'window_duration_mins',
|
|
1378
|
+
'window_minutes'
|
|
1379
|
+
]);
|
|
1380
|
+
const normalizedWindowMinutes = windowDurationMins !== null && Number.isFinite(windowDurationMins)
|
|
1381
|
+
? Math.max(0, Math.trunc(windowDurationMins))
|
|
1382
|
+
: null;
|
|
1383
|
+
if (normalizedWindowMinutes === 300) {
|
|
1384
|
+
return '5-hour';
|
|
1385
|
+
}
|
|
1386
|
+
if (normalizedWindowMinutes === 10_080) {
|
|
1387
|
+
return 'weekly';
|
|
1388
|
+
}
|
|
1389
|
+
return null;
|
|
1390
|
+
}
|
|
1391
|
+
function formatLinearBudgetSegments(value, referenceTime) {
|
|
1392
|
+
return formatOperatorLinearBudgetSegments(value, referenceTime, formatRateLimitBucket);
|
|
1393
|
+
}
|
|
1394
|
+
function formatCompactLinearBudgetSegments(value, referenceTime) {
|
|
1395
|
+
return formatOperatorLinearBudgetSegments(value, referenceTime, formatCompactRateLimitBucket);
|
|
1396
|
+
}
|
|
1397
|
+
function formatOperatorLinearBudgetSegments(value, referenceTime, formatBucket) {
|
|
1398
|
+
const requests = asRecord(value.requests);
|
|
1399
|
+
const complexity = asRecord(value.complexity);
|
|
1400
|
+
const endpointRequests = asRecord(value.endpoint_requests);
|
|
1401
|
+
const endpointComplexity = asRecord(value.endpoint_complexity);
|
|
1402
|
+
const observedAt = readRecordString(value, ['observed_at', 'observedAt']);
|
|
1403
|
+
const source = readRecordString(value, ['source']);
|
|
1404
|
+
const suppression = readRecordString(value, ['suppression']);
|
|
1405
|
+
const retryAfterSeconds = readRecordNumber(value, ['retry_after_seconds', 'retryAfterSeconds']);
|
|
1406
|
+
const looksLikeLinearBudget = observedAt !== null ||
|
|
1407
|
+
suppression !== null ||
|
|
1408
|
+
retryAfterSeconds !== null ||
|
|
1409
|
+
source?.toLowerCase().startsWith('linear') === true;
|
|
1410
|
+
if (!looksLikeLinearBudget) {
|
|
1411
|
+
return null;
|
|
1412
|
+
}
|
|
1413
|
+
const pieces = [{ text: 'Linear', color: ANSI_YELLOW }];
|
|
1414
|
+
const displayRequests = selectOperatorVisibleBudgetBucket(requests, endpointRequests, referenceTime);
|
|
1415
|
+
if (displayRequests) {
|
|
1416
|
+
appendOperatorRateLimitSegment(pieces, `requests ${formatBucket(displayRequests, referenceTime)}`, ANSI_CYAN);
|
|
1417
|
+
}
|
|
1418
|
+
const displayComplexity = selectOperatorVisibleBudgetBucket(complexity, endpointComplexity, referenceTime);
|
|
1419
|
+
if (displayComplexity) {
|
|
1420
|
+
appendOperatorRateLimitSegment(pieces, `complexity ${formatBucket(displayComplexity, referenceTime)}`, ANSI_CYAN);
|
|
1421
|
+
}
|
|
1422
|
+
return pieces;
|
|
1423
|
+
}
|
|
1424
|
+
function selectOperatorVisibleBudgetBucket(primary, endpoint, referenceTime) {
|
|
1425
|
+
if (!primary) {
|
|
1426
|
+
return endpoint;
|
|
1427
|
+
}
|
|
1428
|
+
if (!endpoint) {
|
|
1429
|
+
return primary;
|
|
1430
|
+
}
|
|
1431
|
+
const primaryExhausted = isOperatorRateLimitBucketExhausted(primary);
|
|
1432
|
+
const endpointExhausted = isOperatorRateLimitBucketExhausted(endpoint);
|
|
1433
|
+
if (endpointExhausted && !primaryExhausted) {
|
|
1434
|
+
return endpoint;
|
|
1435
|
+
}
|
|
1436
|
+
if (primaryExhausted && !endpointExhausted) {
|
|
1437
|
+
return primary;
|
|
1438
|
+
}
|
|
1439
|
+
if (primaryExhausted && endpointExhausted) {
|
|
1440
|
+
return compareOperatorRateLimitBucketResetMs(primary, endpoint, referenceTime) >= 0
|
|
1441
|
+
? primary
|
|
1442
|
+
: endpoint;
|
|
1443
|
+
}
|
|
1444
|
+
return primary;
|
|
1445
|
+
}
|
|
1446
|
+
function formatRateLimitBucket(bucket, referenceTime) {
|
|
1447
|
+
return formatOperatorRateLimitBucket(bucket, referenceTime);
|
|
1448
|
+
}
|
|
1449
|
+
function formatCompactRateLimitBucket(bucket, referenceTime) {
|
|
1450
|
+
return formatOperatorRateLimitBucket(bucket, referenceTime);
|
|
1451
|
+
}
|
|
1452
|
+
function formatRateLimitCredits(credits) {
|
|
1453
|
+
if (readRecordBoolean(credits, ['unlimited']) === true) {
|
|
1454
|
+
return 'credits unlimited';
|
|
1455
|
+
}
|
|
1456
|
+
if (readRecordBoolean(credits, ['has_credits', 'hasCredits']) === false) {
|
|
1457
|
+
return 'credits none';
|
|
1458
|
+
}
|
|
1459
|
+
const balance = readRecordNumber(credits, ['balance']);
|
|
1460
|
+
if (balance !== null) {
|
|
1461
|
+
return `credits ${balance.toFixed(2)}`;
|
|
1462
|
+
}
|
|
1463
|
+
return 'credits available';
|
|
1464
|
+
}
|
|
1465
|
+
function isOperatorRateLimitBucketExhausted(bucket) {
|
|
1466
|
+
const remaining = readRecordNumber(bucket, ['remaining']);
|
|
1467
|
+
if (remaining !== null) {
|
|
1468
|
+
return remaining <= 0;
|
|
1469
|
+
}
|
|
1470
|
+
const usedPercent = readRecordNumber(bucket, ['usedPercent', 'used_percent']);
|
|
1471
|
+
if (usedPercent !== null) {
|
|
1472
|
+
return usedPercent >= 100;
|
|
1473
|
+
}
|
|
1474
|
+
return false;
|
|
1475
|
+
}
|
|
1476
|
+
function compareOperatorRateLimitBucketResetMs(left, right, referenceTime) {
|
|
1477
|
+
const leftMs = resolveOperatorRateLimitBucketResetMs(left, referenceTime);
|
|
1478
|
+
const rightMs = resolveOperatorRateLimitBucketResetMs(right, referenceTime);
|
|
1479
|
+
if (leftMs === null && rightMs === null) {
|
|
1480
|
+
return 0;
|
|
1481
|
+
}
|
|
1482
|
+
if (leftMs === null) {
|
|
1483
|
+
return -1;
|
|
1484
|
+
}
|
|
1485
|
+
if (rightMs === null) {
|
|
1486
|
+
return 1;
|
|
1487
|
+
}
|
|
1488
|
+
return leftMs - rightMs;
|
|
1489
|
+
}
|
|
1490
|
+
function resolveOperatorRateLimitBucketResetMs(bucket, referenceTime) {
|
|
1491
|
+
const resetAt = readRecordString(bucket, ['reset_at', 'resetAt', 'resets_at', 'resetsAt']);
|
|
1492
|
+
if (resetAt) {
|
|
1493
|
+
const parsed = Date.parse(resetAt);
|
|
1494
|
+
if (Number.isFinite(parsed)) {
|
|
1495
|
+
return parsed;
|
|
1496
|
+
}
|
|
1497
|
+
}
|
|
1498
|
+
const resetInSeconds = readRecordNumber(bucket, ['reset_in_seconds', 'resetInSeconds']);
|
|
1499
|
+
if (resetInSeconds !== null) {
|
|
1500
|
+
return referenceTime.getTime() + Math.max(0, resetInSeconds) * 1000;
|
|
1501
|
+
}
|
|
1502
|
+
return null;
|
|
1503
|
+
}
|
|
1504
|
+
function formatCompactRateLimitCredits(credits) {
|
|
1505
|
+
return formatRateLimitCredits(credits);
|
|
1506
|
+
}
|
|
1507
|
+
function appendOperatorRateLimitSegment(pieces, text, color) {
|
|
1508
|
+
pieces.push({ text: pieces.length === 1 ? ' ' : ' | ', color: ANSI_GRAY });
|
|
1509
|
+
pieces.push({ text, color });
|
|
1510
|
+
}
|
|
1511
|
+
function formatOperatorRateLimitBucket(bucket, referenceTime) {
|
|
1512
|
+
if (isRateLimitBucketExhausted(bucket)) {
|
|
1513
|
+
const resetSeconds = resolveRateLimitBucketResetSeconds(bucket, referenceTime);
|
|
1514
|
+
if (resetSeconds !== null) {
|
|
1515
|
+
return `resets ${formatHumanDurationShort(resetSeconds)}`;
|
|
1516
|
+
}
|
|
1517
|
+
}
|
|
1518
|
+
const remainingPercent = resolveRateLimitBucketRemainingPercent(bucket);
|
|
1519
|
+
if (remainingPercent !== null) {
|
|
1520
|
+
return formatPercent(remainingPercent);
|
|
1521
|
+
}
|
|
1522
|
+
const remaining = readRecordNumber(bucket, ['remaining']);
|
|
1523
|
+
const limit = readRecordNumber(bucket, ['limit']);
|
|
1524
|
+
if (remaining !== null && limit !== null) {
|
|
1525
|
+
return `${formatCount(remaining)}/${formatCount(limit)}`;
|
|
1526
|
+
}
|
|
1527
|
+
if (remaining !== null) {
|
|
1528
|
+
return `remaining ${formatCount(remaining)}`;
|
|
1529
|
+
}
|
|
1530
|
+
if (limit !== null) {
|
|
1531
|
+
return `limit ${formatCount(limit)}`;
|
|
1532
|
+
}
|
|
1533
|
+
return 'n/a';
|
|
1534
|
+
}
|
|
1535
|
+
function resolveRateLimitBucketRemainingPercent(bucket) {
|
|
1536
|
+
const usedPercent = readRecordNumber(bucket, ['usedPercent', 'used_percent']);
|
|
1537
|
+
if (usedPercent !== null) {
|
|
1538
|
+
return Math.min(100, Math.max(0, 100 - usedPercent));
|
|
1539
|
+
}
|
|
1540
|
+
const remaining = readRecordNumber(bucket, ['remaining']);
|
|
1541
|
+
const limit = readRecordNumber(bucket, ['limit']);
|
|
1542
|
+
if (remaining !== null && limit !== null && limit > 0) {
|
|
1543
|
+
return Math.min(100, Math.max(0, (remaining / limit) * 100));
|
|
1544
|
+
}
|
|
1545
|
+
return null;
|
|
1546
|
+
}
|
|
1547
|
+
function isRateLimitBucketExhausted(bucket) {
|
|
1548
|
+
const remaining = readRecordNumber(bucket, ['remaining']);
|
|
1549
|
+
if (remaining !== null) {
|
|
1550
|
+
return remaining <= 0;
|
|
1551
|
+
}
|
|
1552
|
+
const usedPercent = readRecordNumber(bucket, ['usedPercent', 'used_percent']);
|
|
1553
|
+
return usedPercent !== null ? usedPercent >= 100 : false;
|
|
1554
|
+
}
|
|
1555
|
+
function resolveRateLimitBucketResetSeconds(bucket, referenceTime) {
|
|
1556
|
+
return (readRecordNumber(bucket, ['reset_in_seconds', 'resetInSeconds']) ??
|
|
1557
|
+
secondsUntilTimestamp(readRecordString(bucket, ['reset_at', 'resetAt', 'resets_at', 'resetsAt']), referenceTime));
|
|
1558
|
+
}
|
|
1559
|
+
function formatPercent(value) {
|
|
1560
|
+
const rounded = Math.round(value * 10) / 10;
|
|
1561
|
+
return Number.isInteger(rounded) ? `${rounded.toFixed(0)}%` : `${rounded.toFixed(1).replace(/\.0$/, '')}%`;
|
|
1562
|
+
}
|
|
1563
|
+
function formatRecord(value) {
|
|
1564
|
+
return truncate(Object.entries(value)
|
|
1565
|
+
.map(([key, entry]) => `${sanitizeDisplayValue(key)}=${formatRecordValue(entry)}`)
|
|
1566
|
+
.join(' | '), 140);
|
|
1567
|
+
}
|
|
1568
|
+
function formatRecordValue(value) {
|
|
1569
|
+
if (value === null || value === undefined) {
|
|
1570
|
+
return '-';
|
|
1571
|
+
}
|
|
1572
|
+
if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
|
|
1573
|
+
return sanitizeDisplayValue(value);
|
|
1574
|
+
}
|
|
1575
|
+
try {
|
|
1576
|
+
return sanitizeDisplayValue(JSON.stringify(value));
|
|
1577
|
+
}
|
|
1578
|
+
catch {
|
|
1579
|
+
return sanitizeDisplayValue(String(value));
|
|
1580
|
+
}
|
|
1581
|
+
}
|
|
1582
|
+
function compactSessionId(sessionId) {
|
|
1583
|
+
const sanitized = sanitizeDisplayValue(sessionId);
|
|
1584
|
+
if (sanitized === '-') {
|
|
1585
|
+
return 'n/a';
|
|
1586
|
+
}
|
|
1587
|
+
if (sanitized.length <= 10) {
|
|
1588
|
+
return sanitized;
|
|
1589
|
+
}
|
|
1590
|
+
return `${sanitized.slice(0, 4)}...${sanitized.slice(-6)}`;
|
|
1591
|
+
}
|
|
1592
|
+
function formatPid(pid) {
|
|
1593
|
+
const sanitized = sanitizeDisplayValue(pid);
|
|
1594
|
+
return sanitized === '-' ? 'n/a' : sanitized;
|
|
1595
|
+
}
|
|
1596
|
+
function renderSummaryLine(label, segments, terminalColumns) {
|
|
1597
|
+
const prefix = `│ ${label}: `;
|
|
1598
|
+
const maxWidth = Math.max(0, terminalColumns - prefix.length);
|
|
1599
|
+
return colorize(prefix, ANSI_BOLD) + colorizeSummarySegments(segments, maxWidth);
|
|
1600
|
+
}
|
|
1601
|
+
function colorizeSummarySegments(segments, maxWidth) {
|
|
1602
|
+
if (maxWidth <= 0 || segments.length === 0) {
|
|
1603
|
+
return '';
|
|
1604
|
+
}
|
|
1605
|
+
if (segments.length === 1 && segments[0]?.truncateMode === 'middle') {
|
|
1606
|
+
return colorize(truncateMiddle(segments[0].text, maxWidth), segments[0].color);
|
|
1607
|
+
}
|
|
1608
|
+
const totalWidth = segments.reduce((sum, segment) => sum + segment.text.length, 0);
|
|
1609
|
+
if (totalWidth <= maxWidth) {
|
|
1610
|
+
return segments.map((segment) => colorize(segment.text, segment.color)).join('');
|
|
1611
|
+
}
|
|
1612
|
+
if (maxWidth <= 3) {
|
|
1613
|
+
return colorize(segments
|
|
1614
|
+
.map((segment) => segment.text)
|
|
1615
|
+
.join('')
|
|
1616
|
+
.slice(0, maxWidth), segments[0]?.color ?? ANSI_GRAY);
|
|
1617
|
+
}
|
|
1618
|
+
let remaining = maxWidth - 3;
|
|
1619
|
+
let ellipsisColor = segments[0]?.color ?? ANSI_GRAY;
|
|
1620
|
+
const rendered = [];
|
|
1621
|
+
for (const segment of segments) {
|
|
1622
|
+
if (remaining <= 0) {
|
|
1623
|
+
ellipsisColor = segment.color;
|
|
1624
|
+
break;
|
|
1625
|
+
}
|
|
1626
|
+
if (segment.text.length <= remaining) {
|
|
1627
|
+
rendered.push(colorize(segment.text, segment.color));
|
|
1628
|
+
remaining -= segment.text.length;
|
|
1629
|
+
ellipsisColor = segment.color;
|
|
1630
|
+
continue;
|
|
1631
|
+
}
|
|
1632
|
+
rendered.push(colorize(segment.text.slice(0, remaining), segment.color));
|
|
1633
|
+
ellipsisColor = segment.color;
|
|
1634
|
+
remaining = 0;
|
|
1635
|
+
break;
|
|
1636
|
+
}
|
|
1637
|
+
rendered.push(colorize('...', ellipsisColor));
|
|
1638
|
+
return rendered.join('');
|
|
1639
|
+
}
|
|
1640
|
+
function formatCell(value, width, align = 'left') {
|
|
1641
|
+
const sanitized = truncatePlain(sanitizeDisplayValue(value), width);
|
|
1642
|
+
return align === 'right' ? sanitized.padStart(width) : sanitized.padEnd(width);
|
|
1643
|
+
}
|
|
1644
|
+
function truncatePlain(value, maxLength) {
|
|
1645
|
+
if (value.length <= maxLength) {
|
|
1646
|
+
return value;
|
|
1647
|
+
}
|
|
1648
|
+
if (maxLength <= 3) {
|
|
1649
|
+
return value.slice(0, maxLength);
|
|
1650
|
+
}
|
|
1651
|
+
return `${value.slice(0, maxLength - 3)}...`;
|
|
1652
|
+
}
|
|
1653
|
+
function sanitizeDisplayValue(value) {
|
|
1654
|
+
if (value === null || value === undefined) {
|
|
1655
|
+
return '-';
|
|
1656
|
+
}
|
|
1657
|
+
const sanitized = sanitizeTerminalText(String(value));
|
|
1658
|
+
return sanitized.length === 0 ? '-' : sanitized;
|
|
1659
|
+
}
|
|
1660
|
+
function humanizeRunningEvent(event) {
|
|
1661
|
+
const normalized = normalizeRunningEventKey(event);
|
|
1662
|
+
if (!normalized) {
|
|
1663
|
+
return 'n/a';
|
|
1664
|
+
}
|
|
1665
|
+
switch (normalized) {
|
|
1666
|
+
case 'agent_message':
|
|
1667
|
+
return 'agent message';
|
|
1668
|
+
case 'task_started':
|
|
1669
|
+
return 'session started';
|
|
1670
|
+
case 'task_complete':
|
|
1671
|
+
case 'turn_completed':
|
|
1672
|
+
return 'turn completed';
|
|
1673
|
+
case 'turn_started':
|
|
1674
|
+
return 'turn started';
|
|
1675
|
+
case 'turn_failed':
|
|
1676
|
+
return 'turn failed';
|
|
1677
|
+
case 'turn_cancelled':
|
|
1678
|
+
return 'turn cancelled';
|
|
1679
|
+
case 'thread_tokenusage_updated':
|
|
1680
|
+
return 'token usage updated';
|
|
1681
|
+
case 'account_ratelimits_updated':
|
|
1682
|
+
return 'rate limits updated';
|
|
1683
|
+
case 'queued_questions':
|
|
1684
|
+
return 'queued questions';
|
|
1685
|
+
case 'notification':
|
|
1686
|
+
return 'notification';
|
|
1687
|
+
case 'item_started':
|
|
1688
|
+
return 'item started';
|
|
1689
|
+
case 'item_completed':
|
|
1690
|
+
return 'item completed';
|
|
1691
|
+
case 'item_updated':
|
|
1692
|
+
return 'item updated';
|
|
1693
|
+
case 'retry_scheduled':
|
|
1694
|
+
return 'retry scheduled';
|
|
1695
|
+
default:
|
|
1696
|
+
return normalized.replace(/_/g, ' ');
|
|
1697
|
+
}
|
|
1698
|
+
}
|
|
1699
|
+
function isHighSignalStatusText(value, displayState) {
|
|
1700
|
+
if (value === '-' || value === 'n/a' || value.toLowerCase() === displayState) {
|
|
1701
|
+
return false;
|
|
1702
|
+
}
|
|
1703
|
+
const normalized = value.toLowerCase();
|
|
1704
|
+
if (normalized === 'retry queued' ||
|
|
1705
|
+
normalized === 'turn running' ||
|
|
1706
|
+
normalized === 'worker turn active' ||
|
|
1707
|
+
normalized === 'provider worker turn is active.' ||
|
|
1708
|
+
normalized === 'provider worker turn is active' ||
|
|
1709
|
+
normalized === 'turn active') {
|
|
1710
|
+
return false;
|
|
1711
|
+
}
|
|
1712
|
+
return true;
|
|
1713
|
+
}
|
|
1714
|
+
function sameStatusText(left, right) {
|
|
1715
|
+
return left.toLowerCase() === right.toLowerCase();
|
|
1716
|
+
}
|
|
1717
|
+
function normalizeRunningEventKey(value) {
|
|
1718
|
+
const sanitized = sanitizeDisplayValue(value);
|
|
1719
|
+
if (sanitized === '-') {
|
|
1720
|
+
return null;
|
|
1721
|
+
}
|
|
1722
|
+
return sanitized
|
|
1723
|
+
.toLowerCase()
|
|
1724
|
+
.replace(/^codex\/event\//u, '')
|
|
1725
|
+
.replace(/[./]+/gu, '_')
|
|
1726
|
+
.replace(/[^a-z0-9_]+/gu, '_')
|
|
1727
|
+
.replace(/_+/gu, '_')
|
|
1728
|
+
.replace(/^_|_$/gu, '');
|
|
1729
|
+
}
|
|
1730
|
+
function formatHumanDurationShort(valueSeconds) {
|
|
1731
|
+
const totalSeconds = Math.max(0, Math.floor(normalizeFiniteNumber(valueSeconds)));
|
|
1732
|
+
const days = Math.floor(totalSeconds / 86_400);
|
|
1733
|
+
const hours = Math.floor(totalSeconds / 3600);
|
|
1734
|
+
const minutes = Math.floor((totalSeconds % 3600) / 60);
|
|
1735
|
+
const seconds = totalSeconds % 60;
|
|
1736
|
+
if (days > 0) {
|
|
1737
|
+
return hours % 24 > 0 ? `${days}d ${hours % 24}h` : `${days}d`;
|
|
1738
|
+
}
|
|
1739
|
+
if (hours > 0) {
|
|
1740
|
+
return minutes > 0 ? `${hours}h ${minutes}m` : `${hours}h`;
|
|
1741
|
+
}
|
|
1742
|
+
if (minutes > 0) {
|
|
1743
|
+
return seconds > 0 ? `${minutes}m ${seconds}s` : `${minutes}m`;
|
|
1744
|
+
}
|
|
1745
|
+
return `${seconds}s`;
|
|
1746
|
+
}
|
|
1747
|
+
function formatCountdownMs(valueMs) {
|
|
1748
|
+
if (!Number.isFinite(valueMs)) {
|
|
1749
|
+
return 'n/a';
|
|
1750
|
+
}
|
|
1751
|
+
if (valueMs <= 0) {
|
|
1752
|
+
return 'now';
|
|
1753
|
+
}
|
|
1754
|
+
return formatHumanDurationShort(Math.ceil(valueMs / 1000));
|
|
1755
|
+
}
|
|
1756
|
+
function truncate(value, maxLength) {
|
|
1757
|
+
const sanitized = sanitizeDisplayValue(value);
|
|
1758
|
+
if (sanitized.length <= maxLength) {
|
|
1759
|
+
return sanitized;
|
|
1760
|
+
}
|
|
1761
|
+
if (maxLength <= 3) {
|
|
1762
|
+
return sanitized.slice(0, maxLength);
|
|
1763
|
+
}
|
|
1764
|
+
return `${sanitized.slice(0, maxLength - 3)}...`;
|
|
1765
|
+
}
|
|
1766
|
+
function truncateMiddle(value, maxLength) {
|
|
1767
|
+
const sanitized = sanitizeDisplayValue(value);
|
|
1768
|
+
if (sanitized.length <= maxLength) {
|
|
1769
|
+
return sanitized;
|
|
1770
|
+
}
|
|
1771
|
+
const headLength = Math.max(1, Math.floor((maxLength - 3) / 2));
|
|
1772
|
+
const tailLength = Math.max(1, maxLength - 3 - headLength);
|
|
1773
|
+
return `${sanitized.slice(0, headLength)}...${sanitized.slice(-tailLength)}`;
|
|
1774
|
+
}
|
|
1775
|
+
function sanitizeTerminalText(value) {
|
|
1776
|
+
return value
|
|
1777
|
+
.replace(/\r\n|\n|\r/g, ' ')
|
|
1778
|
+
.replace(ANSI_CONTROL_SEQUENCE_PATTERN, ' ')
|
|
1779
|
+
.replace(CONTROL_CHARACTER_PATTERN, ' ')
|
|
1780
|
+
.replace(/\s+/g, ' ')
|
|
1781
|
+
.trim();
|
|
1782
|
+
}
|
|
1783
|
+
function resolveReferenceTime(referenceTime, generatedAt, fallbackTime = new Date(0)) {
|
|
1784
|
+
if (referenceTime instanceof Date && Number.isFinite(referenceTime.getTime())) {
|
|
1785
|
+
return referenceTime;
|
|
1786
|
+
}
|
|
1787
|
+
const parsed = parseTimestamp(generatedAt);
|
|
1788
|
+
return parsed === null ? fallbackTime : new Date(parsed);
|
|
1789
|
+
}
|
|
1790
|
+
function resolveLiveReferenceTime(liveReferenceTime, fallbackTime) {
|
|
1791
|
+
if (liveReferenceTime instanceof Date && Number.isFinite(liveReferenceTime.getTime())) {
|
|
1792
|
+
return liveReferenceTime;
|
|
1793
|
+
}
|
|
1794
|
+
return fallbackTime;
|
|
1795
|
+
}
|
|
1796
|
+
function deriveLiveReferenceTime(renderedState, now) {
|
|
1797
|
+
const elapsedMs = Math.max(0, now.getTime() - renderedState.liveClockStartedAt.getTime());
|
|
1798
|
+
return new Date(renderedState.referenceTime.getTime() + elapsedMs);
|
|
1799
|
+
}
|
|
1800
|
+
function resolveDisplayedLiveReferenceTime(renderedState, frameState, now) {
|
|
1801
|
+
if (frameState.paused && frameState.pausedLiveReferenceTime) {
|
|
1802
|
+
return frameState.pausedLiveReferenceTime;
|
|
1803
|
+
}
|
|
1804
|
+
return deriveLiveReferenceTime(renderedState, now);
|
|
1805
|
+
}
|
|
1806
|
+
function resolveMaxAllowedAgents(dataset) {
|
|
1807
|
+
const value = dataset.counts.max_allowed;
|
|
1808
|
+
if (typeof value === 'number' && Number.isFinite(value) && value > 0) {
|
|
1809
|
+
return Math.floor(value);
|
|
1810
|
+
}
|
|
1811
|
+
return null;
|
|
1812
|
+
}
|
|
1813
|
+
function resolveTerminalColumns(value) {
|
|
1814
|
+
if (typeof value === 'number' && Number.isFinite(value) && value > 0) {
|
|
1815
|
+
return Math.floor(value);
|
|
1816
|
+
}
|
|
1817
|
+
return DEFAULT_TERMINAL_COLUMNS;
|
|
1818
|
+
}
|
|
1819
|
+
function resolveTerminalRows(value) {
|
|
1820
|
+
if (typeof value === 'number' && Number.isFinite(value) && value > 0) {
|
|
1821
|
+
return Math.floor(value);
|
|
1822
|
+
}
|
|
1823
|
+
return Number.POSITIVE_INFINITY;
|
|
1824
|
+
}
|
|
1825
|
+
function linesWillExceedTerminalHeight(terminalRows, lineCount) {
|
|
1826
|
+
return Number.isFinite(terminalRows) && lineCount > terminalRows;
|
|
1827
|
+
}
|
|
1828
|
+
function isControlCharacter(value) {
|
|
1829
|
+
if (value.length === 0) {
|
|
1830
|
+
return false;
|
|
1831
|
+
}
|
|
1832
|
+
const codePoint = value.charCodeAt(0);
|
|
1833
|
+
return (codePoint >= 0x00 && codePoint <= 0x1f) || (codePoint >= 0x7f && codePoint <= 0x9f);
|
|
1834
|
+
}
|
|
1835
|
+
function parseTimestamp(value) {
|
|
1836
|
+
if (typeof value !== 'string' || value.trim().length === 0) {
|
|
1837
|
+
return null;
|
|
1838
|
+
}
|
|
1839
|
+
const parsed = Date.parse(value);
|
|
1840
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
1841
|
+
}
|
|
1842
|
+
function secondsUntilTimestamp(value, referenceTime) {
|
|
1843
|
+
const timestamp = parseTimestamp(value);
|
|
1844
|
+
if (timestamp === null) {
|
|
1845
|
+
return null;
|
|
1846
|
+
}
|
|
1847
|
+
return Math.max(0, Math.floor((timestamp - referenceTime.getTime()) / 1000));
|
|
1848
|
+
}
|
|
1849
|
+
function normalizeFiniteNumber(value) {
|
|
1850
|
+
return typeof value === 'number' && Number.isFinite(value) ? value : 0;
|
|
1851
|
+
}
|
|
1852
|
+
function formatNullable(value) {
|
|
1853
|
+
if (value === null || value === undefined) {
|
|
1854
|
+
return '-';
|
|
1855
|
+
}
|
|
1856
|
+
return sanitizeDisplayValue(value);
|
|
1857
|
+
}
|
|
1858
|
+
function readRecordString(record, keys) {
|
|
1859
|
+
for (const key of keys) {
|
|
1860
|
+
const value = record[key];
|
|
1861
|
+
if (typeof value === 'string' && value.trim().length > 0) {
|
|
1862
|
+
return value.trim();
|
|
1863
|
+
}
|
|
1864
|
+
}
|
|
1865
|
+
return null;
|
|
1866
|
+
}
|
|
1867
|
+
function readRecordNumber(record, keys) {
|
|
1868
|
+
for (const key of keys) {
|
|
1869
|
+
const value = record[key];
|
|
1870
|
+
if (typeof value === 'number' && Number.isFinite(value)) {
|
|
1871
|
+
return value;
|
|
1872
|
+
}
|
|
1873
|
+
if (typeof value === 'string' && value.trim().length > 0) {
|
|
1874
|
+
const parsed = Number(value.trim());
|
|
1875
|
+
if (Number.isFinite(parsed)) {
|
|
1876
|
+
return parsed;
|
|
1877
|
+
}
|
|
1878
|
+
}
|
|
1879
|
+
}
|
|
1880
|
+
return null;
|
|
1881
|
+
}
|
|
1882
|
+
function readRecordBoolean(record, keys) {
|
|
1883
|
+
for (const key of keys) {
|
|
1884
|
+
const value = record[key];
|
|
1885
|
+
if (typeof value === 'boolean') {
|
|
1886
|
+
return value;
|
|
1887
|
+
}
|
|
1888
|
+
}
|
|
1889
|
+
return null;
|
|
1890
|
+
}
|
|
1891
|
+
function asRecord(value) {
|
|
1892
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value)
|
|
1893
|
+
? value
|
|
1894
|
+
: null;
|
|
1895
|
+
}
|
|
1896
|
+
function colorize(value, ansiCode) {
|
|
1897
|
+
return `${ansiCode}${value}${ANSI_RESET}`;
|
|
1898
|
+
}
|
|
1899
|
+
function stripAnsiSequences(value) {
|
|
1900
|
+
return value.replace(ANSI_CONTROL_SEQUENCE_PATTERN, '');
|
|
1901
|
+
}
|
|
1902
|
+
function formatSnapshotTimestamp(value) {
|
|
1903
|
+
return value.toISOString().replace(/[-:.]/g, '');
|
|
1904
|
+
}
|