instar 1.3.1649 → 1.3.1650
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/dashboard/console/accounts.js +2 -1
- package/dashboard/console/agents-model.js +3 -3
- package/dashboard/console/agents.js +7 -4
- package/dashboard/console/console.css +1 -0
- package/dashboard/console/console.js +35 -4
- package/dashboard/console/costs-model.js +12 -0
- package/dashboard/console/costs.js +6 -3
- package/dashboard/console/development-model.js +9 -3
- package/dashboard/console/development.js +7 -4
- package/dashboard/console/home-model.js +30 -14
- package/dashboard/console/mandates.js +3 -3
- package/dashboard/console/needs-model.js +105 -0
- package/dashboard/console/plans.js +2 -1
- package/dashboard/console/secrets.js +2 -2
- package/dashboard/console/ticket-model.js +36 -0
- package/dashboard/console/ui.js +3 -0
- package/dist/cli.js +8 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/install.d.ts +9 -7
- package/dist/commands/install.d.ts.map +1 -1
- package/dist/commands/install.js +2 -19
- package/dist/commands/install.js.map +1 -1
- package/dist/commands/reflect.d.ts.map +1 -1
- package/dist/commands/reflect.js +2 -0
- package/dist/commands/reflect.js.map +1 -1
- package/dist/commands/route.d.ts.map +1 -1
- package/dist/commands/route.js +2 -0
- package/dist/commands/route.js.map +1 -1
- package/dist/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +315 -14
- package/dist/commands/server.js.map +1 -1
- package/dist/commands/setClaudeKey.d.ts +8 -0
- package/dist/commands/setClaudeKey.d.ts.map +1 -0
- package/dist/commands/setClaudeKey.js +35 -0
- package/dist/commands/setClaudeKey.js.map +1 -0
- package/dist/core/AccountFollowMeOrchestrator.d.ts.map +1 -1
- package/dist/core/AccountFollowMeOrchestrator.js +4 -0
- package/dist/core/AccountFollowMeOrchestrator.js.map +1 -1
- package/dist/core/AppMediaStore.d.ts.map +1 -1
- package/dist/core/AppMediaStore.js +3 -0
- package/dist/core/AppMediaStore.js.map +1 -1
- package/dist/core/BackupManager.d.ts.map +1 -1
- package/dist/core/BackupManager.js +2 -0
- package/dist/core/BackupManager.js.map +1 -1
- package/dist/core/ClaudeCliIntelligenceProvider.d.ts.map +1 -1
- package/dist/core/ClaudeCliIntelligenceProvider.js +11 -3
- package/dist/core/ClaudeCliIntelligenceProvider.js.map +1 -1
- package/dist/core/ClaudeKeyHealth.d.ts +141 -0
- package/dist/core/ClaudeKeyHealth.d.ts.map +1 -0
- package/dist/core/ClaudeKeyHealth.js +467 -0
- package/dist/core/ClaudeKeyHealth.js.map +1 -0
- package/dist/core/CodexTaskContinuationStore.d.ts.map +1 -1
- package/dist/core/CodexTaskContinuationStore.js +7 -0
- package/dist/core/CodexTaskContinuationStore.js.map +1 -1
- package/dist/core/CoherenceJournal.d.ts +1 -1
- package/dist/core/CoherenceJournal.d.ts.map +1 -1
- package/dist/core/CoherenceJournal.js +11 -2
- package/dist/core/CoherenceJournal.js.map +1 -1
- package/dist/core/Config.d.ts +8 -0
- package/dist/core/Config.d.ts.map +1 -1
- package/dist/core/Config.js +26 -0
- package/dist/core/Config.js.map +1 -1
- package/dist/core/CredentialSwapExecutor.d.ts.map +1 -1
- package/dist/core/CredentialSwapExecutor.js +10 -0
- package/dist/core/CredentialSwapExecutor.js.map +1 -1
- package/dist/core/DecisionQualityRecorderImpl.d.ts.map +1 -1
- package/dist/core/DecisionQualityRecorderImpl.js +6 -0
- package/dist/core/DecisionQualityRecorderImpl.js.map +1 -1
- package/dist/core/DiscoveryEvaluator.d.ts.map +1 -1
- package/dist/core/DiscoveryEvaluator.js +4 -0
- package/dist/core/DiscoveryEvaluator.js.map +1 -1
- package/dist/core/IdentityReannounceClaimant.d.ts.map +1 -1
- package/dist/core/IdentityReannounceClaimant.js +4 -0
- package/dist/core/IdentityReannounceClaimant.js.map +1 -1
- package/dist/core/IntelligenceRouter.d.ts.map +1 -1
- package/dist/core/IntelligenceRouter.js +9 -0
- package/dist/core/IntelligenceRouter.js.map +1 -1
- package/dist/core/JournalSyncApplier.d.ts +1 -0
- package/dist/core/JournalSyncApplier.d.ts.map +1 -1
- package/dist/core/JournalSyncApplier.js +48 -0
- package/dist/core/JournalSyncApplier.js.map +1 -1
- package/dist/core/LeaseCoordinator.d.ts +4 -0
- package/dist/core/LeaseCoordinator.d.ts.map +1 -1
- package/dist/core/LeaseCoordinator.js +64 -1
- package/dist/core/LeaseCoordinator.js.map +1 -1
- package/dist/core/MessagingToneGate.d.ts +11 -7
- package/dist/core/MessagingToneGate.d.ts.map +1 -1
- package/dist/core/MessagingToneGate.js +15 -1
- package/dist/core/MessagingToneGate.js.map +1 -1
- package/dist/core/MobileDeviceKeys.d.ts +10 -0
- package/dist/core/MobileDeviceKeys.d.ts.map +1 -1
- package/dist/core/MobileDeviceKeys.js +17 -0
- package/dist/core/MobileDeviceKeys.js.map +1 -1
- package/dist/core/MultiMachineCoordinator.d.ts +12 -0
- package/dist/core/MultiMachineCoordinator.d.ts.map +1 -1
- package/dist/core/MultiMachineCoordinator.js +115 -3
- package/dist/core/MultiMachineCoordinator.js.map +1 -1
- package/dist/core/MutualSshRuntime.d.ts +1 -0
- package/dist/core/MutualSshRuntime.d.ts.map +1 -1
- package/dist/core/MutualSshRuntime.js +17 -1
- package/dist/core/MutualSshRuntime.js.map +1 -1
- package/dist/core/ObservedEndpointTracker.d.ts +1 -0
- package/dist/core/ObservedEndpointTracker.d.ts.map +1 -1
- package/dist/core/ObservedEndpointTracker.js +17 -0
- package/dist/core/ObservedEndpointTracker.js.map +1 -1
- package/dist/core/OverloadShedPlanner.d.ts.map +1 -1
- package/dist/core/OverloadShedPlanner.js +9 -0
- package/dist/core/OverloadShedPlanner.js.map +1 -1
- package/dist/core/OwnershipApplier.d.ts.map +1 -1
- package/dist/core/OwnershipApplier.js +7 -0
- package/dist/core/OwnershipApplier.js.map +1 -1
- package/dist/core/OwnershipReconciler.d.ts.map +1 -1
- package/dist/core/OwnershipReconciler.js +5 -0
- package/dist/core/OwnershipReconciler.js.map +1 -1
- package/dist/core/PostUpdateMigrator.d.ts +8 -0
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +76 -2
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/core/PrHandLease.d.ts.map +1 -1
- package/dist/core/PrHandLease.js +2 -0
- package/dist/core/PrHandLease.js.map +1 -1
- package/dist/core/ProactiveSwapMonitor.d.ts.map +1 -1
- package/dist/core/ProactiveSwapMonitor.js +7 -0
- package/dist/core/ProactiveSwapMonitor.js.map +1 -1
- package/dist/core/ProjectRoundExecution.d.ts.map +1 -1
- package/dist/core/ProjectRoundExecution.js +3 -2
- package/dist/core/ProjectRoundExecution.js.map +1 -1
- package/dist/core/QueueDrainLoop.d.ts +7 -0
- package/dist/core/QueueDrainLoop.d.ts.map +1 -1
- package/dist/core/QueueDrainLoop.js +37 -1
- package/dist/core/QueueDrainLoop.js.map +1 -1
- package/dist/core/RemoteAckStore.d.ts.map +1 -1
- package/dist/core/RemoteAckStore.js +4 -0
- package/dist/core/RemoteAckStore.js.map +1 -1
- package/dist/core/ReplicatedStoreReconcilerWiring.d.ts.map +1 -1
- package/dist/core/ReplicatedStoreReconcilerWiring.js +1 -0
- package/dist/core/ReplicatedStoreReconcilerWiring.js.map +1 -1
- package/dist/core/SecretSync.d.ts +1 -0
- package/dist/core/SecretSync.d.ts.map +1 -1
- package/dist/core/SecretSync.js +16 -0
- package/dist/core/SecretSync.js.map +1 -1
- package/dist/core/SessionManager.d.ts +11 -0
- package/dist/core/SessionManager.d.ts.map +1 -1
- package/dist/core/SessionManager.js +125 -14
- package/dist/core/SessionManager.js.map +1 -1
- package/dist/core/SessionPoolFailoverRunner.d.ts.map +1 -1
- package/dist/core/SessionPoolFailoverRunner.js +4 -0
- package/dist/core/SessionPoolFailoverRunner.js.map +1 -1
- package/dist/core/SleepWakeDetector.d.ts.map +1 -1
- package/dist/core/SleepWakeDetector.js +10 -0
- package/dist/core/SleepWakeDetector.js.map +1 -1
- package/dist/core/SubscriptionReloginStore.d.ts.map +1 -1
- package/dist/core/SubscriptionReloginStore.js +10 -1
- package/dist/core/SubscriptionReloginStore.js.map +1 -1
- package/dist/core/TopicPinMutation.d.ts.map +1 -1
- package/dist/core/TopicPinMutation.js +5 -0
- package/dist/core/TopicPinMutation.js.map +1 -1
- package/dist/core/UnjustifiedStopGate.d.ts.map +1 -1
- package/dist/core/UnjustifiedStopGate.js +22 -0
- package/dist/core/UnjustifiedStopGate.js.map +1 -1
- package/dist/core/WindowRunCadenceExecutor.d.ts.map +1 -1
- package/dist/core/WindowRunCadenceExecutor.js +20 -0
- package/dist/core/WindowRunCadenceExecutor.js.map +1 -1
- package/dist/core/claudeChildAuth.d.ts +68 -0
- package/dist/core/claudeChildAuth.d.ts.map +1 -0
- package/dist/core/claudeChildAuth.js +173 -0
- package/dist/core/claudeChildAuth.js.map +1 -0
- package/dist/core/claudeKeyProbe.d.ts +23 -0
- package/dist/core/claudeKeyProbe.d.ts.map +1 -0
- package/dist/core/claudeKeyProbe.js +68 -0
- package/dist/core/claudeKeyProbe.js.map +1 -0
- package/dist/core/codexDeviceLimitWiring.d.ts.map +1 -1
- package/dist/core/codexDeviceLimitWiring.js +12 -0
- package/dist/core/codexDeviceLimitWiring.js.map +1 -1
- package/dist/core/deliverToConversation.d.ts.map +1 -1
- package/dist/core/deliverToConversation.js +11 -0
- package/dist/core/deliverToConversation.js.map +1 -1
- package/dist/core/meteredCallEntry.d.ts.map +1 -1
- package/dist/core/meteredCallEntry.js +21 -0
- package/dist/core/meteredCallEntry.js.map +1 -1
- package/dist/core/nobodyPollingActuator.d.ts.map +1 -1
- package/dist/core/nobodyPollingActuator.js +5 -0
- package/dist/core/nobodyPollingActuator.js.map +1 -1
- package/dist/core/operatorSurfaceReferral.d.ts +18 -0
- package/dist/core/operatorSurfaceReferral.d.ts.map +1 -0
- package/dist/core/operatorSurfaceReferral.js +32 -0
- package/dist/core/operatorSurfaceReferral.js.map +1 -0
- package/dist/core/ownershipFollowsLiveWork.d.ts.map +1 -1
- package/dist/core/ownershipFollowsLiveWork.js +10 -1
- package/dist/core/ownershipFollowsLiveWork.js.map +1 -1
- package/dist/core/types.d.ts +15 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/core/worktreeDirtyCheck.d.ts.map +1 -1
- package/dist/core/worktreeDirtyCheck.js +2 -0
- package/dist/core/worktreeDirtyCheck.js.map +1 -1
- package/dist/data/stageBCertifiedSet.d.ts +9 -0
- package/dist/data/stageBCertifiedSet.d.ts.map +1 -1
- package/dist/data/stageBCertifiedSet.js +12 -0
- package/dist/data/stageBCertifiedSet.js.map +1 -1
- package/dist/data/standards-guard-index.json +1 -1
- package/dist/data/standards-guard-index.meta.json +2 -2
- package/dist/data/standards-registry.meta.json +1 -1
- package/dist/lifeline/TelegramLifeline.d.ts.map +1 -1
- package/dist/lifeline/TelegramLifeline.js +17 -3
- package/dist/lifeline/TelegramLifeline.js.map +1 -1
- package/dist/messaging/attentionHubPolicy.d.ts.map +1 -1
- package/dist/messaging/attentionHubPolicy.js +1 -0
- package/dist/messaging/attentionHubPolicy.js.map +1 -1
- package/dist/monitoring/AgentWorktreeReaper.d.ts.map +1 -1
- package/dist/monitoring/AgentWorktreeReaper.js +15 -0
- package/dist/monitoring/AgentWorktreeReaper.js.map +1 -1
- package/dist/monitoring/ApprenticeshipCycleSlaMonitor.d.ts.map +1 -1
- package/dist/monitoring/ApprenticeshipCycleSlaMonitor.js +18 -9
- package/dist/monitoring/ApprenticeshipCycleSlaMonitor.js.map +1 -1
- package/dist/monitoring/CodexArmSessionSweeper.d.ts.map +1 -1
- package/dist/monitoring/CodexArmSessionSweeper.js +11 -0
- package/dist/monitoring/CodexArmSessionSweeper.js.map +1 -1
- package/dist/monitoring/CrashLoopPauser.d.ts.map +1 -1
- package/dist/monitoring/CrashLoopPauser.js +8 -0
- package/dist/monitoring/CrashLoopPauser.js.map +1 -1
- package/dist/monitoring/DegradationReporter.d.ts.map +1 -1
- package/dist/monitoring/DegradationReporter.js +7 -1
- package/dist/monitoring/DegradationReporter.js.map +1 -1
- package/dist/monitoring/DegradedTmuxGuard.d.ts +2 -0
- package/dist/monitoring/DegradedTmuxGuard.d.ts.map +1 -1
- package/dist/monitoring/DegradedTmuxGuard.js +14 -0
- package/dist/monitoring/DegradedTmuxGuard.js.map +1 -1
- package/dist/monitoring/DurableOutputScrubber.d.ts.map +1 -1
- package/dist/monitoring/DurableOutputScrubber.js +7 -0
- package/dist/monitoring/DurableOutputScrubber.js.map +1 -1
- package/dist/monitoring/DurableVaultSession.d.ts.map +1 -1
- package/dist/monitoring/DurableVaultSession.js +15 -0
- package/dist/monitoring/DurableVaultSession.js.map +1 -1
- package/dist/monitoring/ExternalHogSentinel.d.ts +2 -0
- package/dist/monitoring/ExternalHogSentinel.d.ts.map +1 -1
- package/dist/monitoring/ExternalHogSentinel.js +27 -1
- package/dist/monitoring/ExternalHogSentinel.js.map +1 -1
- package/dist/monitoring/FailureLedger.d.ts.map +1 -1
- package/dist/monitoring/FailureLedger.js +38 -1
- package/dist/monitoring/FailureLedger.js.map +1 -1
- package/dist/monitoring/FeatureEvidenceLog.d.ts +5 -0
- package/dist/monitoring/FeatureEvidenceLog.d.ts.map +1 -1
- package/dist/monitoring/FeatureEvidenceLog.js +25 -0
- package/dist/monitoring/FeatureEvidenceLog.js.map +1 -1
- package/dist/monitoring/GeminiCapacityEscalationMonitor.d.ts.map +1 -1
- package/dist/monitoring/GeminiCapacityEscalationMonitor.js +20 -2
- package/dist/monitoring/GeminiCapacityEscalationMonitor.js.map +1 -1
- package/dist/monitoring/GeminiLoopRunner.d.ts.map +1 -1
- package/dist/monitoring/GeminiLoopRunner.js +11 -1
- package/dist/monitoring/GeminiLoopRunner.js.map +1 -1
- package/dist/monitoring/GreenPrAutoMerger.d.ts.map +1 -1
- package/dist/monitoring/GreenPrAutoMerger.js +11 -0
- package/dist/monitoring/GreenPrAutoMerger.js.map +1 -1
- package/dist/monitoring/GrowthDigestPublisher.d.ts.map +1 -1
- package/dist/monitoring/GrowthDigestPublisher.js +16 -0
- package/dist/monitoring/GrowthDigestPublisher.js.map +1 -1
- package/dist/monitoring/GrowthMilestoneAnalyst.d.ts +3 -0
- package/dist/monitoring/GrowthMilestoneAnalyst.d.ts.map +1 -1
- package/dist/monitoring/GrowthMilestoneAnalyst.js +45 -1
- package/dist/monitoring/GrowthMilestoneAnalyst.js.map +1 -1
- package/dist/monitoring/McpProcessReaper.d.ts +2 -0
- package/dist/monitoring/McpProcessReaper.d.ts.map +1 -1
- package/dist/monitoring/McpProcessReaper.js +32 -0
- package/dist/monitoring/McpProcessReaper.js.map +1 -1
- package/dist/monitoring/MissingLoginSessionDetector.d.ts +2 -1
- package/dist/monitoring/MissingLoginSessionDetector.d.ts.map +1 -1
- package/dist/monitoring/MissingLoginSessionDetector.js +23 -0
- package/dist/monitoring/MissingLoginSessionDetector.js.map +1 -1
- package/dist/monitoring/OrchestratorPoller.d.ts +1 -0
- package/dist/monitoring/OrchestratorPoller.d.ts.map +1 -1
- package/dist/monitoring/OrchestratorPoller.js +13 -0
- package/dist/monitoring/OrchestratorPoller.js.map +1 -1
- package/dist/monitoring/OrphanedWorkSentinel.d.ts.map +1 -1
- package/dist/monitoring/OrphanedWorkSentinel.js +7 -0
- package/dist/monitoring/OrphanedWorkSentinel.js.map +1 -1
- package/dist/monitoring/PresenceProxy.d.ts.map +1 -1
- package/dist/monitoring/PresenceProxy.js +9 -1
- package/dist/monitoring/PresenceProxy.js.map +1 -1
- package/dist/monitoring/ProcessFootprintMonitor.d.ts.map +1 -1
- package/dist/monitoring/ProcessFootprintMonitor.js +20 -2
- package/dist/monitoring/ProcessFootprintMonitor.js.map +1 -1
- package/dist/monitoring/PromiseBeacon.d.ts.map +1 -1
- package/dist/monitoring/PromiseBeacon.js +20 -0
- package/dist/monitoring/PromiseBeacon.js.map +1 -1
- package/dist/monitoring/ReleaseReadinessSentinel.d.ts +2 -0
- package/dist/monitoring/ReleaseReadinessSentinel.d.ts.map +1 -1
- package/dist/monitoring/ReleaseReadinessSentinel.js +26 -0
- package/dist/monitoring/ReleaseReadinessSentinel.js.map +1 -1
- package/dist/monitoring/RopeHealthMonitor.d.ts.map +1 -1
- package/dist/monitoring/RopeHealthMonitor.js +35 -1
- package/dist/monitoring/RopeHealthMonitor.js.map +1 -1
- package/dist/monitoring/SentinelNotifier.d.ts.map +1 -1
- package/dist/monitoring/SentinelNotifier.js +16 -0
- package/dist/monitoring/SentinelNotifier.js.map +1 -1
- package/dist/monitoring/SessionReaper.d.ts.map +1 -1
- package/dist/monitoring/SessionReaper.js +11 -2
- package/dist/monitoring/SessionReaper.js.map +1 -1
- package/dist/monitoring/SingleMachineFailoverGapDetector.d.ts +2 -0
- package/dist/monitoring/SingleMachineFailoverGapDetector.d.ts.map +1 -1
- package/dist/monitoring/SingleMachineFailoverGapDetector.js +20 -0
- package/dist/monitoring/SingleMachineFailoverGapDetector.js.map +1 -1
- package/dist/monitoring/SpeakerElection.d.ts.map +1 -1
- package/dist/monitoring/SpeakerElection.js +8 -0
- package/dist/monitoring/SpeakerElection.js.map +1 -1
- package/dist/monitoring/StrandedTopicSentinel.d.ts +3 -0
- package/dist/monitoring/StrandedTopicSentinel.d.ts.map +1 -1
- package/dist/monitoring/StrandedTopicSentinel.js +33 -0
- package/dist/monitoring/StrandedTopicSentinel.js.map +1 -1
- package/dist/native/InstarWedgeCanary.app/Contents/MacOS/InstarWedgeCanary +0 -0
- package/dist/native/instar-desktop-host +0 -0
- package/dist/providers/adapters/anthropic-headless/transport/oneShotCompletion.d.ts.map +1 -1
- package/dist/providers/adapters/anthropic-headless/transport/oneShotCompletion.js +5 -2
- package/dist/providers/adapters/anthropic-headless/transport/oneShotCompletion.js.map +1 -1
- package/dist/recommended/RecommendedServicesLedger.d.ts +91 -0
- package/dist/recommended/RecommendedServicesLedger.d.ts.map +1 -0
- package/dist/recommended/RecommendedServicesLedger.js +193 -0
- package/dist/recommended/RecommendedServicesLedger.js.map +1 -0
- package/dist/recommended/RecommendedServicesReplicatedStore.d.ts +14 -0
- package/dist/recommended/RecommendedServicesReplicatedStore.d.ts.map +1 -0
- package/dist/recommended/RecommendedServicesReplicatedStore.js +34 -0
- package/dist/recommended/RecommendedServicesReplicatedStore.js.map +1 -0
- package/dist/recommended/legacyDeclines.d.ts +11 -0
- package/dist/recommended/legacyDeclines.d.ts.map +1 -0
- package/dist/recommended/legacyDeclines.js +17 -0
- package/dist/recommended/legacyDeclines.js.map +1 -0
- package/dist/recommended/serviceIds.d.ts +3 -0
- package/dist/recommended/serviceIds.d.ts.map +1 -0
- package/dist/recommended/serviceIds.js +6 -0
- package/dist/recommended/serviceIds.js.map +1 -0
- package/dist/scaffold/templates.d.ts +2 -0
- package/dist/scaffold/templates.d.ts.map +1 -1
- package/dist/scaffold/templates.js +8 -0
- package/dist/scaffold/templates.js.map +1 -1
- package/dist/scheduler/JobResultTriage.d.ts.map +1 -1
- package/dist/scheduler/JobResultTriage.js +10 -0
- package/dist/scheduler/JobResultTriage.js.map +1 -1
- package/dist/scheduler/JobScheduler.d.ts.map +1 -1
- package/dist/scheduler/JobScheduler.js +12 -0
- package/dist/scheduler/JobScheduler.js.map +1 -1
- package/dist/scheduler/MentorOnboardingRunner.d.ts +3 -0
- package/dist/scheduler/MentorOnboardingRunner.d.ts.map +1 -1
- package/dist/scheduler/MentorOnboardingRunner.js +24 -2
- package/dist/scheduler/MentorOnboardingRunner.js.map +1 -1
- package/dist/server/AgentServer.d.ts +2 -0
- package/dist/server/AgentServer.d.ts.map +1 -1
- package/dist/server/AgentServer.js +76 -15
- package/dist/server/AgentServer.js.map +1 -1
- package/dist/server/BootHealthBeacon.d.ts +3 -0
- package/dist/server/BootHealthBeacon.d.ts.map +1 -1
- package/dist/server/BootHealthBeacon.js +9 -0
- package/dist/server/BootHealthBeacon.js.map +1 -1
- package/dist/server/CapabilityIndex.d.ts.map +1 -1
- package/dist/server/CapabilityIndex.js +10 -1
- package/dist/server/CapabilityIndex.js.map +1 -1
- package/dist/server/LifelineIngressGuard.d.ts.map +1 -1
- package/dist/server/LifelineIngressGuard.js +11 -0
- package/dist/server/LifelineIngressGuard.js.map +1 -1
- package/dist/server/PoolPollCache.d.ts +4 -0
- package/dist/server/PoolPollCache.d.ts.map +1 -1
- package/dist/server/PoolPollCache.js +33 -0
- package/dist/server/PoolPollCache.js.map +1 -1
- package/dist/server/machineRoutes.d.ts.map +1 -1
- package/dist/server/machineRoutes.js +6 -0
- package/dist/server/machineRoutes.js.map +1 -1
- package/dist/server/middleware.d.ts.map +1 -1
- package/dist/server/middleware.js +6 -0
- package/dist/server/middleware.js.map +1 -1
- package/dist/server/mobileConsoleTicket.d.ts +113 -0
- package/dist/server/mobileConsoleTicket.d.ts.map +1 -0
- package/dist/server/mobileConsoleTicket.js +405 -0
- package/dist/server/mobileConsoleTicket.js.map +1 -0
- package/dist/server/mobileConversationRoutes.d.ts.map +1 -1
- package/dist/server/mobileConversationRoutes.js +20 -1
- package/dist/server/mobileConversationRoutes.js.map +1 -1
- package/dist/server/mobileMediaRoutes.d.ts.map +1 -1
- package/dist/server/mobileMediaRoutes.js +3 -0
- package/dist/server/mobileMediaRoutes.js.map +1 -1
- package/dist/server/mobilePairingRoutes.d.ts +8 -2
- package/dist/server/mobilePairingRoutes.d.ts.map +1 -1
- package/dist/server/mobilePairingRoutes.js +67 -6
- package/dist/server/mobilePairingRoutes.js.map +1 -1
- package/dist/server/mobileSessionTerminalRoutes.d.ts +128 -0
- package/dist/server/mobileSessionTerminalRoutes.d.ts.map +1 -0
- package/dist/server/mobileSessionTerminalRoutes.js +389 -0
- package/dist/server/mobileSessionTerminalRoutes.js.map +1 -0
- package/dist/server/recommendedServiceRoutes.d.ts +13 -0
- package/dist/server/recommendedServiceRoutes.d.ts.map +1 -0
- package/dist/server/recommendedServiceRoutes.js +49 -0
- package/dist/server/recommendedServiceRoutes.js.map +1 -0
- package/dist/server/routes.d.ts +2 -0
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +170 -4
- package/dist/server/routes.js.map +1 -1
- package/dist/testing/selfActionRegistry.d.ts.map +1 -1
- package/dist/testing/selfActionRegistry.js +36 -0
- package/dist/testing/selfActionRegistry.js.map +1 -1
- package/dist/threadline/A2ACheckInProxy.d.ts.map +1 -1
- package/dist/threadline/A2ACheckInProxy.js +26 -15
- package/dist/threadline/A2ACheckInProxy.js.map +1 -1
- package/dist/threadline/AgentTrustManager.d.ts.map +1 -1
- package/dist/threadline/AgentTrustManager.js +3 -0
- package/dist/threadline/AgentTrustManager.js.map +1 -1
- package/dist/threadline/InboundIdLedger.d.ts.map +1 -1
- package/dist/threadline/InboundIdLedger.js +18 -1
- package/dist/threadline/InboundIdLedger.js.map +1 -1
- package/dist/threadline/NegotiatorGate.d.ts.map +1 -1
- package/dist/threadline/NegotiatorGate.js +11 -0
- package/dist/threadline/NegotiatorGate.js.map +1 -1
- package/dist/threadline/PipeSessionSpawner.d.ts.map +1 -1
- package/dist/threadline/PipeSessionSpawner.js +8 -0
- package/dist/threadline/PipeSessionSpawner.js.map +1 -1
- package/dist/threadline/ThreadlineRouter.d.ts.map +1 -1
- package/dist/threadline/ThreadlineRouter.js +13 -0
- package/dist/threadline/ThreadlineRouter.js.map +1 -1
- package/dist/threadline/pairingReplicationWiring.d.ts.map +1 -1
- package/dist/threadline/pairingReplicationWiring.js +9 -0
- package/dist/threadline/pairingReplicationWiring.js.map +1 -1
- package/dist/threadline/relayForward.d.ts.map +1 -1
- package/dist/threadline/relayForward.js +13 -2
- package/dist/threadline/relayForward.js.map +1 -1
- package/dist/threadline/relayUnknownSenderTrust.d.ts +0 -9
- package/dist/threadline/relayUnknownSenderTrust.d.ts.map +1 -1
- package/dist/threadline/relayUnknownSenderTrust.js +16 -0
- package/dist/threadline/relayUnknownSenderTrust.js.map +1 -1
- package/package.json +1 -1
- package/scripts/check-constitution-pairing.mjs +273 -0
- package/scripts/safe-merge.mjs +9 -0
- package/src/data/builtin-manifest.json +103 -103
- package/src/data/feature-ladders.json +113 -113
- package/src/data/stageBCertifiedSet.ts +12 -0
- package/src/data/standards-guard-index.json +1 -1
- package/src/data/standards-guard-index.meta.json +2 -2
- package/src/data/standards-registry.meta.json +1 -1
- package/src/data/state-coherence-registry.json +18 -0
- package/src/scaffold/templates.ts +9 -0
- package/upgrades/1.3.1650.md +105 -0
- package/upgrades/mobile-console-device-signin.eli16.md +5 -0
- package/upgrades/side-effects/cloud-key-internal-calls.md +116 -0
- package/upgrades/side-effects/constitution-alignment.eli16.md +11 -0
- package/upgrades/side-effects/constitution-alignment.md +69 -0
- package/upgrades/side-effects/feature-evidence-recording.eli16.md +3 -0
- package/upgrades/side-effects/feature-evidence-recording.md +66 -0
- package/upgrades/side-effects/feedback-reroute-admission-parity.md +77 -0
- package/upgrades/side-effects/mobile-console-device-signin.md +102 -0
- package/upgrades/side-effects/mobile-native-session-screen.md +88 -0
- package/upgrades/side-effects/operator-needs-first-screen.md +87 -0
- package/upgrades/side-effects/recommended-services-choices.md +50 -0
- package/upgrades/side-effects/rerouted-lifetime-active-process.md +63 -0
- package/upgrades/side-effects/undated-resurfacer-e2e-wait.eli16.md +5 -0
- package/upgrades/side-effects/undated-resurfacer-e2e-wait.md +9 -0
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
// repair / retry / cancel POST /subscription-relogin/repair-cell operator session
|
|
8
8
|
|
|
9
9
|
import { buildAccountsPage, frameworkName } from './accounts-model.js';
|
|
10
|
-
import { el, setKids, pageHead, sheet, closeSheet, story, details, note, cantTell, askPin, forgetOperatorSession, firstPaint, arrivalScroll } from './ui.js';
|
|
10
|
+
import { el, setKids, pageHead, sheet, closeSheet, story, details, note, cantTell, askPin, forgetOperatorSession, firstPaint, arrivalScroll, markNeed } from './ui.js';
|
|
11
11
|
import { legacyHref } from './nav-model.js';
|
|
12
12
|
import { verifiedDisplayName } from './display-identity.js';
|
|
13
13
|
|
|
@@ -60,6 +60,7 @@ export async function renderAccounts(main, ctx) {
|
|
|
60
60
|
return row;
|
|
61
61
|
})))));
|
|
62
62
|
|
|
63
|
+
markNeed(needs, 'accounts');
|
|
63
64
|
setKids(main,
|
|
64
65
|
pageHead({ eyebrow: 'Capacity · Accounts', headline: page.headline, sub: page.sub, tone: page.tone, facts: page.facts }),
|
|
65
66
|
page.partial ? note(page.partial) : null,
|
|
@@ -315,7 +315,7 @@ export function waitingCards(pairing, now = Date.now()) {
|
|
|
315
315
|
for (const n of pairing.restartNotices) cards.push({ kind: 'restart', id: n.id, title: 'An invite was cleared by a restart', body: `An invite from ${claimed(n.claimedAgent, n.claimedOwner)} was waiting for your approval when I restarted, and it was cleared (its secret lived only in memory). Paste it again, or forward it to my Telegram, before it expires at ${localTime(n.exp, now)}.`, actions: ['dismiss'] });
|
|
316
316
|
for (const a of pairing.alarms) {
|
|
317
317
|
const secondName = verifiedDisplayName(a.secondAccept && a.secondAccept.agent);
|
|
318
|
-
cards.push({ kind: 'alarm', iid: a.iid, title: 'Warning: your invite got out', body: `Two agents used the same invite, so I paused the first one: it cannot receive anything secret. The second ${secondName ? `calls itself ${secondName}` : 'has no verified name'}. Make a new invite and send it privately.`, actions: [] });
|
|
318
|
+
cards.push({ kind: 'alarm', iid: a.iid, title: 'Warning: your invite got out', body: `Two agents used the same invite, so I paused the first one: it cannot receive anything secret. The second ${secondName ? `calls itself ${secondName}` : 'has no verified name'}. Make a new invite and send it privately.`, actions: ['new-invite'] });
|
|
319
319
|
}
|
|
320
320
|
for (const p of pairing.pendingAccepts) cards.push({ kind: 'accept', iid: p.iid, name: verifiedDisplayName(p.claimedAgent) || 'An unnamed agent', title: `Pair with ${claimed(p.claimedAgent, p.claimedOwner)}?`, body: `Only approve if you know who sent you this invite. Check code (optional, to read to them): ${p.checkCode || '—'}. It expires at ${localTime(p.exp, now)}.`, actions: ['approve', 'decline'] });
|
|
321
321
|
for (const i of pairing.finishPrompts) cards.push({ kind: 'finish', iid: i.iid, name: verifiedDisplayName(i.acceptor && i.acceptor.agent) || 'An unnamed agent', title: `${verifiedDisplayName(i.acceptor && i.acceptor.agent) || 'An unnamed agent'} accepted your invite.`, body: `${claimed(i.acceptor && i.acceptor.agent, i.acceptor && i.acceptor.owner).replace(/^the/, 'The')} accepted it. Finish only if that is who you sent it to.`, actions: ['finish', 'not-them'] });
|
|
@@ -341,7 +341,7 @@ export function buildAgentsPage({ health, pairingRes, invitesRes, peersRes, conv
|
|
|
341
341
|
return {
|
|
342
342
|
mode: 'off', certain: relayOff, relay, pairing, agents, waiting, conversations, facts: [], topicNames,
|
|
343
343
|
headline: relayOff ? 'The agent network is off on this agent.' : 'The agent network looks off on this agent.',
|
|
344
|
-
sub: `${relayOff ? '' : `${relay.word}. `}Pairing is turned off too. To turn it on, ask me to “connect to the agent network”, or tap Turn on pairing
|
|
344
|
+
sub: `${relayOff ? '' : `${relay.word}. `}Pairing is turned off too. To turn it on, ask me to “connect to the agent network”, or tap Turn on pairing; that restarts my server, and this page reconnects by itself.`,
|
|
345
345
|
tone: 'muted', notes: [],
|
|
346
346
|
};
|
|
347
347
|
}
|
|
@@ -378,7 +378,7 @@ export function buildAgentsPage({ health, pairingRes, invitesRes, peersRes, conv
|
|
|
378
378
|
const subParts = [];
|
|
379
379
|
if (waiting.length && !relay.connected && relay.state !== 'cant-tell' && !relayOff) subParts.push(`${relay.word}.`);
|
|
380
380
|
if (pairing.state === 'elsewhere') subParts.push(pairing.holder ? `Pairing runs on ${pairing.holder.name}; open the console there to make or approve invites.` : 'Pairing runs on another of my machines.');
|
|
381
|
-
if (pairing.state === 'off' && agents.length) subParts.push(paired.length ? 'Existing agents are listed
|
|
381
|
+
if (pairing.state === 'off' && agents.length) subParts.push(paired.length ? 'Existing agents are still listed.' : agents.some(a => a.historyOnly) ? 'Past conversations are still saved.' : 'No agents are paired here.');
|
|
382
382
|
if (pairing.state === 'cant-tell') subParts.push(pairing.reason);
|
|
383
383
|
if (!agents.length && pairing.state === 'ok' && !waiting.length) subParts.push('Connect another agent to start.');
|
|
384
384
|
if (relay.identity && relay.identity.tone === 'bad') subParts.push(relay.identity.word + '.');
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
import {
|
|
15
15
|
buildAgentsPage, threadModel, inviteResultWords, pinResultWords, bearerResultWords, enableWords, bindRequest, bindResultWords, localTime,
|
|
16
16
|
} from './agents-model.js';
|
|
17
|
-
import { el, setKids, pageHead, section, sheet, closeSheet, story, details, note, cantTell, askPin, firstPaint } from './ui.js';
|
|
17
|
+
import { el, setKids, pageHead, section, sheet, closeSheet, story, details, note, cantTell, askPin, firstPaint, markNeed } from './ui.js';
|
|
18
18
|
import { legacyHref } from './nav-model.js';
|
|
19
19
|
import { verifiedDisplayName, uniqueNamedChoices } from './display-identity.js';
|
|
20
20
|
import { topicNameMap } from './topic-names.js';
|
|
@@ -70,16 +70,18 @@ export async function renderAgents(main, ctx) {
|
|
|
70
70
|
const connect = el('button', { class: 'btn-primary btn-inline connect-btn', type: 'button', text: '+ Connect another agent', onclick: () => openConnect(deps) });
|
|
71
71
|
setKids(main, el('div', { class: 'agents-page' },
|
|
72
72
|
pageHead({ eyebrow: EYEBROW, headline: page.headline, sub: page.sub, tone: page.tone, facts: page.facts }),
|
|
73
|
-
|
|
73
|
+
// Pairing being off is the thing to fix here, so its switch leads rather than following the lists.
|
|
74
|
+
page.pairing.state === 'off' ? turnOnCard(deps) : null,
|
|
75
|
+
page.waiting.map((w) => markNeed(waitingCard(w, deps), 'agents')),
|
|
74
76
|
page.relay.connected || page.relay.state === 'cant-tell' ? null : relayLine(page.relay),
|
|
75
77
|
page.relay.state === 'cant-tell' ? note(page.relay.word + '.') : null,
|
|
76
78
|
page.notes.map((n) => note(n)),
|
|
77
79
|
el('div', { class: 'page-sections' },
|
|
78
|
-
agentRows.length || page.pairing.state === 'ok' ? section({ id: 'agents', title: 'Agents', rows: agentRows, empty: 'No other agents yet. Connect one
|
|
80
|
+
agentRows.length || page.pairing.state === 'ok' ? section({ id: 'agents', title: 'Agents', rows: agentRows, empty: 'No other agents yet. Use Connect another agent to add one.', onRow: (r) => openAgent(r.agent, deps) }) : null,
|
|
79
81
|
pastRows.length ? section({ id: 'past-agents', title: 'Past conversations', collapsed: true, rows: pastRows, onRow: (r) => openAgent(r.agent, deps) }) : null,
|
|
80
82
|
page.pairing.openInvites.length ? section({ id: 'open-invites', title: 'Invites not used yet', collapsed: true, rows: page.pairing.openInvites.map((i) => ({ key: `inv:${i.iid}`, main: 'Unused invitation', sub: `works once · expires at ${localTime(i.exp)}`, invite: i })), onRow: (r) => openInvite(r.invite, deps) }) : null,
|
|
81
83
|
),
|
|
82
|
-
page.pairing.state === '
|
|
84
|
+
page.pairing.state === 'elsewhere' ? note(page.pairing.holder ? `Pairing runs on ${page.pairing.holder.name}; open the console there to connect another agent.` : 'Pairing runs on another of my machines; connect another agent from there.') : el('div', { class: 'page-primary' }, connect),
|
|
83
85
|
footLink(),
|
|
84
86
|
));
|
|
85
87
|
}
|
|
@@ -158,6 +160,7 @@ function waitingCard(w, deps) {
|
|
|
158
160
|
b.addEventListener('click', () => pinned('Finish pairing', `/threadline/pairing/invites/${encodeURIComponent(w.iid)}/finish`, `Finish pairing with ${w.name}: I pin it as verified.`, 'Finishing. Checking we can talk…', b));
|
|
159
161
|
acts.append(b);
|
|
160
162
|
}
|
|
163
|
+
if (w.actions.includes('new-invite')) acts.append(el('button', { class: 'btn-primary btn-inline', type: 'button', text: 'Make a new invite', onclick: () => openConnect(deps) }));
|
|
161
164
|
if (w.actions.includes('not-them')) {
|
|
162
165
|
const b = el('button', { class: 'btn-quiet btn-danger-outline', type: 'button', text: 'Not them' });
|
|
163
166
|
b.addEventListener('click', () => {
|
|
@@ -772,6 +772,7 @@ button.run-mark:focus-visible { outline: 2px solid var(--accent); outline-offset
|
|
|
772
772
|
/* ── Development (under-the-hood.md: Shipping + Feedback + Pace) ─────────── */
|
|
773
773
|
.tiles-dev { margin-top: var(--s-3); }
|
|
774
774
|
.dev-page .need-card { margin-bottom: var(--s-3); }
|
|
775
|
+
.dev-page .need-line { display: block; margin-bottom: var(--s-3); }
|
|
775
776
|
.ideas-page .need-card { margin: var(--s-3) 0; }
|
|
776
777
|
.ideas-page .need-sub { margin: 0; }
|
|
777
778
|
.ideas-page .need-sub:empty { display: none; }
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
} from './home-model.js';
|
|
11
11
|
import { GROUPS, legacyHref, icon, tabHref, LEGACY_DASHBOARD } from './nav-model.js';
|
|
12
12
|
import { fillPartial } from './home-model.js';
|
|
13
|
+
import { NEED_SOURCES, surfacedNeeds } from './needs-model.js';
|
|
13
14
|
import { renderPromises, topicLink } from './promises.js';
|
|
14
15
|
import { renderMachines } from './machines.js';
|
|
15
16
|
import { renderAccounts } from './accounts.js';
|
|
@@ -30,6 +31,7 @@ import { renderSessions } from './sessions.js';
|
|
|
30
31
|
import { renderMaturation, maturationTile } from './maturation-board.js';
|
|
31
32
|
import { renderIdeas, ideasTile } from './ideas.js';
|
|
32
33
|
import { paceCard } from './pace.js';
|
|
34
|
+
import { takeTicket, signInAtBoot } from './ticket-model.js';
|
|
33
35
|
import { sheetIsOpen, closeSheet, unlockCheck, machineIssue, MACHINES_PAGE, newArrival, refreshHeld } from './ui.js';
|
|
34
36
|
|
|
35
37
|
const TOKEN_KEY = 'instar_token'; // shared with the legacy dashboard
|
|
@@ -91,11 +93,12 @@ function toggleTheme() {
|
|
|
91
93
|
// told "can't tell". Cross-machine reads in particular can be slow for a moment.
|
|
92
94
|
const MIN_TIMEOUT_MS = 20000;
|
|
93
95
|
const RETRY_DELAY_MS = 0; // retry at once: a timeout already waited MIN_TIMEOUT_MS
|
|
94
|
-
async function getOnce(path, timeoutMs) {
|
|
96
|
+
async function getOnce(path, timeoutMs, soft403 = false) {
|
|
95
97
|
const ctl = new AbortController();
|
|
96
98
|
const t = setTimeout(() => ctl.abort(), timeoutMs);
|
|
97
99
|
try {
|
|
98
100
|
const r = await fetch(path, { headers: { Authorization: `Bearer ${token}` }, signal: ctl.signal, cache: 'no-store' });
|
|
101
|
+
if (r.status === 403 && soft403) return failure('the server answered 403');
|
|
99
102
|
if (r.status === 401 || r.status === 403) { signOut(); return failure('not signed in'); }
|
|
100
103
|
if (r.status === 503) return failure('switched off on this agent');
|
|
101
104
|
if (!r.ok) return { ...failure(`the server answered ${r.status}`), retryable: r.status >= 500 };
|
|
@@ -468,7 +471,8 @@ function loadHome() {
|
|
|
468
471
|
const { models, raw, results } = run;
|
|
469
472
|
const paint = (id) => {
|
|
470
473
|
// "What needs you" is the one answer: queue items plus promises waiting on the operator.
|
|
471
|
-
const model = id === 'needs' ? combineNeeds(raw.needs, raw.promised) : raw[id];
|
|
474
|
+
const model = id === 'needs' ? combineNeeds(raw.needs, raw.promised, surfacedNeeds(run.sources)) : raw[id];
|
|
475
|
+
if (!model) return; // nothing read yet for this card
|
|
472
476
|
const shown = paintCard(CARDS.find((c) => c.id === id), model) || model;
|
|
473
477
|
models[id] = shown;
|
|
474
478
|
// The action queue must use the same last-known promise reading as its
|
|
@@ -527,6 +531,24 @@ function loadHome() {
|
|
|
527
531
|
};
|
|
528
532
|
attempt();
|
|
529
533
|
};
|
|
534
|
+
// What needs the operator on other pages (needs-model.js): one in-flight read per source, no
|
|
535
|
+
// retry timer of its own (the next tick is the retry), and a 403 here never signs anyone out.
|
|
536
|
+
run.tick = (run.tick || 0) + 1;
|
|
537
|
+
for (const source of NEED_SOURCES) {
|
|
538
|
+
if (run.sourceBusy[source.id]) continue;
|
|
539
|
+
if (source.when && !source.when(run.sources)) continue;
|
|
540
|
+
if (source.everyTicks && run.sourceAt[source.id] && run.tick - run.sourceAt[source.id] < source.everyTicks) continue;
|
|
541
|
+
run.sourceAt[source.id] = run.tick;
|
|
542
|
+
run.sourceBusy[source.id] = true;
|
|
543
|
+
const keys = Object.keys(source.reads);
|
|
544
|
+
Promise.all(keys.map((k) => getOnce(source.reads[k], Math.max(TIMEOUT_MS, MIN_TIMEOUT_MS), true))).then((reads) => {
|
|
545
|
+
run.sourceBusy[source.id] = false;
|
|
546
|
+
if (!run.active || run.document !== document) return;
|
|
547
|
+
run.sources[source.id] = { ...(run.sources[source.id] || {}), results: Object.fromEntries(keys.map((k, i) => [k, reads[i]])) };
|
|
548
|
+
paint('needs');
|
|
549
|
+
paintHeadline(models);
|
|
550
|
+
});
|
|
551
|
+
}
|
|
530
552
|
// Every source has one in-flight request and its own bounded retry timer.
|
|
531
553
|
// A slow source cannot block another card or start overlapping refreshes.
|
|
532
554
|
read('pool', '/pool', POOL_TIMEOUT_MS);
|
|
@@ -542,7 +564,7 @@ function loadHome() {
|
|
|
542
564
|
}
|
|
543
565
|
|
|
544
566
|
function renderHome(main) {
|
|
545
|
-
homeRun = { active: true, document, models: Object.fromEntries(CARDS.map((c) => [c.id, { state: 'loading' }])), raw: {}, results: {}, pending: {}, failures: {} };
|
|
567
|
+
homeRun = { active: true, document, models: Object.fromEntries(CARDS.map((c) => [c.id, { state: 'loading' }])), raw: {}, results: {}, pending: {}, failures: {}, sources: {}, sourceBusy: {}, sourceAt: {} };
|
|
546
568
|
main.replaceChildren(
|
|
547
569
|
el('section', { class: 'hero' },
|
|
548
570
|
el('div', { class: 'eyebrow', id: 'heroTime', text: 'Checking in' }),
|
|
@@ -696,5 +718,14 @@ function previewBanner() {
|
|
|
696
718
|
applyTheme(readTheme());
|
|
697
719
|
await loadPreviewInfo();
|
|
698
720
|
try { token = localStorage.getItem(TOKEN_KEY) || ''; } catch { token = ''; }
|
|
699
|
-
|
|
721
|
+
// An owner's paired phone opens signed in without the PIN (mobile-console-device-signin §3.5).
|
|
722
|
+
let ticket = '';
|
|
723
|
+
try { ticket = takeTicket(sessionStorage); } catch { ticket = ''; }
|
|
724
|
+
const signIn = await signInAtBoot({ storedToken: token, validToken, ticket, fetchFn: (p, o) => fetch(p, o) });
|
|
725
|
+
if (signIn.via === 'ticket') {
|
|
726
|
+
// Stored exactly as the PIN unlock stores it.
|
|
727
|
+
try { localStorage.setItem(TOKEN_KEY, signIn.token); } catch { /* storage blocked */ }
|
|
728
|
+
}
|
|
729
|
+
token = signIn.token;
|
|
730
|
+
if (signIn.via === 'pin') renderUnlock(); else start();
|
|
700
731
|
})();
|
|
@@ -387,6 +387,18 @@ export function buildRouting(res) {
|
|
|
387
387
|
* Build the whole page from the reads: { spend, caps, status, metrics, insights, tokens, claude, codex, orphans, chains, names }.
|
|
388
388
|
* Returns { headline, sub, tone, facts, window, recorded, limits, checks, conversations, routing }.
|
|
389
389
|
*/
|
|
390
|
+
/**
|
|
391
|
+
* Spending limits the operator turned on that are not enforcing: they believe money is capped
|
|
392
|
+
* and it is not. Only this counts: a route they froze is their own choice, and a pending
|
|
393
|
+
* restart is the agent's job. The only control is in the legacy Spend tab, so that is the action.
|
|
394
|
+
*/
|
|
395
|
+
export function spendingNeed(statusRes) {
|
|
396
|
+
if (!statusRes || !statusRes.ok) return null;
|
|
397
|
+
const st = (statusRes.data || {}).lifecycleState;
|
|
398
|
+
if (st !== 'probe-failed' && st !== 'construction-failed') return null;
|
|
399
|
+
return { title: 'Spending limits are on but not enforcing', why: 'Paid use is not capped right now, even though limits are turned on.' };
|
|
400
|
+
}
|
|
401
|
+
|
|
390
402
|
export function buildCostsPage(res, now = Date.now(), { window = DEFAULT_WINDOW } = {}) {
|
|
391
403
|
const r = res || {};
|
|
392
404
|
const w = windowParams(window, now);
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
// switch stay in the legacy Spend tab until their native sheets ship. Every value
|
|
4
4
|
// reaches the DOM through textContent; the model (costs-model.js) decides everything.
|
|
5
5
|
|
|
6
|
-
import { buildCostsPage, WINDOWS, DEFAULT_WINDOW, GRAINS, windowParams, spendQuery, committedLine, basisWord, fmtTokens, fmtUsd, modelWord, doorWord } from './costs-model.js';
|
|
7
|
-
import { el, setKids, pageHead, section, sheet, story, details, note, cantTell, firstPaint } from './ui.js';
|
|
6
|
+
import { buildCostsPage, WINDOWS, DEFAULT_WINDOW, GRAINS, windowParams, spendQuery, committedLine, basisWord, fmtTokens, fmtUsd, modelWord, doorWord, spendingNeed } from './costs-model.js';
|
|
7
|
+
import { el, setKids, pageHead, section, sheet, story, details, note, cantTell, firstPaint, markNeed } from './ui.js';
|
|
8
8
|
import { legacyHref } from './nav-model.js';
|
|
9
9
|
import { sessionLabel } from './home-model.js';
|
|
10
10
|
|
|
@@ -43,8 +43,11 @@ export async function renderCosts(main, ctx) {
|
|
|
43
43
|
timeControl.classList.add('time-control');
|
|
44
44
|
hero.insertBefore(timeControl, hero.querySelector('.facts'));
|
|
45
45
|
|
|
46
|
+
const spending = spendingNeed(status);
|
|
46
47
|
setKids(main, el('div', { class: 'costs-page' },
|
|
47
48
|
hero,
|
|
49
|
+
spending ? markNeed(el('div', { class: 'need-card' }, el('span', { class: 'need-title', text: spending.title }), el('span', { class: 'need-sub', text: spending.why }),
|
|
50
|
+
el('a', { class: 'btn-primary btn-inline', href: legacyHref('spend'), text: 'Open the spending controls' })), 'spending') : null,
|
|
48
51
|
el('div', { class: 'page-controls' }, routingLink),
|
|
49
52
|
el('div', { class: 'page-sections' },
|
|
50
53
|
recordedSection(page, repaint),
|
|
@@ -75,7 +78,7 @@ function recordedSection(page, repaint) {
|
|
|
75
78
|
const more = el('details', { class: 'raw more-detail' }, el('summary', { text: 'More detail' }),
|
|
76
79
|
el('p', { class: 'card-line', text: 'How the ledger is grouped. By hour and by day follow the time window above; by month and all time cover every retained day.' }), grainChips,
|
|
77
80
|
r.horizonNote ? el('p', { class: 'card-note', text: r.horizonNote }) : null,
|
|
78
|
-
el('p', { class: 'card-note', text: `Source: ${r.source || 'the feature-metrics ledger'}${r.lastReconcileAt ? `, reconciled ${new Date(r.lastReconcileAt).toLocaleString([], { month: 'short', day: 'numeric', hour: 'numeric', minute: '2-digit' })}` : ''}${r.rollupMaintained ? '' : ' · rollup not maintained'}. Reporting figures are recomputed at current prices; the committed figures
|
|
81
|
+
el('p', { class: 'card-note', text: `Source: ${r.source || 'the feature-metrics ledger'}${r.lastReconcileAt ? `, reconciled ${new Date(r.lastReconcileAt).toLocaleString([], { month: 'short', day: 'numeric', hour: 'numeric', minute: '2-digit' })}` : ''}${r.rollupMaintained ? '' : ' · rollup not maintained'}. Reporting figures are recomputed at current prices; the committed figures are what a limit enforces.` }));
|
|
79
82
|
wrap.append(more);
|
|
80
83
|
}
|
|
81
84
|
// Paid routes: committed limits and committed spend, visibly apart from the reporting rows.
|
|
@@ -74,14 +74,20 @@ function shippingRow(group, pr, sub, end, tone, record, repo, stage) {
|
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
/** The watcher's own health line (slow vs stuck vs silently failed are separate states). */
|
|
77
|
+
/** A stop only the agent's own records hold: it releases this itself, so nothing is asked of the operator yet. */
|
|
78
|
+
export function agentOwnedStop(w) {
|
|
79
|
+
const stops = w && w.gate && Array.isArray(w.gate.rollbackStops) ? w.gate.rollbackStops : [];
|
|
80
|
+
return stops.length > 0 && stops.every((s) => s && s.class === 'agent');
|
|
81
|
+
}
|
|
82
|
+
|
|
77
83
|
export function watcherLine(w, now = Date.now()) {
|
|
78
84
|
if (!w) return { text: 'Merge watcher: no reading.', tone: 'unknown' };
|
|
79
85
|
if (w.invariantOk === false) return { text: 'Merge watcher: its safety invariant failed; it will not arm anything until that clears.', tone: 'bad' };
|
|
80
86
|
if (w.breaker && w.breaker.open) return { text: 'Merge watcher paused itself after repeated failures.', tone: 'bad' };
|
|
81
87
|
const gate = w.gate || {};
|
|
82
88
|
const stops = Array.isArray(gate.rollbackStops) ? gate.rollbackStops : [];
|
|
83
|
-
if (stops.length && stops.every((s) => s && s.class === 'agent')) return { text: 'Auto-merging is stopped by the agent for its own reason. It releases this itself when its condition is met; you can also turn it back on
|
|
84
|
-
if (Array.isArray(gate.rollback) && gate.rollback.length) return { text: 'Auto-merging is stopped (you or the agent rolled it back).
|
|
89
|
+
if (stops.length && stops.every((s) => s && s.class === 'agent')) return { text: 'Auto-merging is stopped by the agent for its own reason. It releases this itself when its condition is met; you can also turn it back on here.', tone: 'attn', stopped: true };
|
|
90
|
+
if (Array.isArray(gate.rollback) && gate.rollback.length) return { text: 'Auto-merging is stopped (you or the agent rolled it back).', tone: 'attn', stopped: true };
|
|
85
91
|
if (!w.lastTickAt) return { text: 'Merge watcher has not checked yet.', tone: 'unknown' };
|
|
86
92
|
const age = now - w.lastTickAt;
|
|
87
93
|
if (age > 2 * WATCHER_CADENCE_MS) return { text: `Merge watcher last checked ${ago(w.lastTickAt, now)}, usually every ${Math.round(WATCHER_CADENCE_MS / 60000)} min.`, tone: 'attn' };
|
|
@@ -464,7 +470,7 @@ export function buildFeedback(res = {}, now = Date.now()) {
|
|
|
464
470
|
state = 'cant-tell';
|
|
465
471
|
const k = failed.find((x) => todoFeeds.includes(x));
|
|
466
472
|
headline = `I can’t tell whether feedback needs you: ${names[k]} didn’t answer (${reasonOf(res[k])}).`; tone = 'muted';
|
|
467
|
-
if (todos.length) sub = `${plural(todos.length, 'thing')} that ${todos.length === 1 ? 'does' : 'do'} need you ${todos.length === 1 ? 'is' : 'are'}
|
|
473
|
+
if (todos.length) sub = `${plural(todos.length, 'thing')} that ${todos.length === 1 ? 'does' : 'do'} need you ${todos.length === 1 ? 'is' : 'are'} shown, from the reads that answered.`;
|
|
468
474
|
} else if (todos.length) {
|
|
469
475
|
headline = todos[0].sentence; tone = 'attn';
|
|
470
476
|
const more = todos.length - 1;
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
buildShipping, buildPace, buildFeedback, developmentHeadline, stoppedLead, foldFromHash, maintainerMode, authorityCard, holdWords, verifyWords, rollbackWords,
|
|
14
14
|
readinessAuthorityRequest, triageAuthorityRequest, commitRequest, authorityResultWords, commitResultWords, newDecisionRef, PACE_METRICS, DEV_TOOLS_KEY,
|
|
15
15
|
} from './development-model.js';
|
|
16
|
-
import { el, setKids, pageHead, section, sheet, closeSheet, story, details, note, cantTell, askPin, firstPaint, arrivalScroll } from './ui.js';
|
|
16
|
+
import { el, setKids, pageHead, section, sheet, closeSheet, story, details, note, cantTell, askPin, firstPaint, arrivalScroll, markNeed } from './ui.js';
|
|
17
17
|
import { legacyHref } from './nav-model.js';
|
|
18
18
|
|
|
19
19
|
const EYEBROW = 'Work · Development';
|
|
@@ -120,7 +120,10 @@ function paint() {
|
|
|
120
120
|
// The one thing that needs the operator comes first; the rest wait as a counted line.
|
|
121
121
|
const todos = feedback.todos;
|
|
122
122
|
const first = todos[0] || null;
|
|
123
|
-
const lead = first ? todoCard(first, deps, true) : null;
|
|
123
|
+
const lead = first ? markNeed(todoCard(first, deps, true), 'feedback') : null;
|
|
124
|
+
const approvals = shipping.rows ? shipping.rows.filter((r) => r.group === 'needs').length : 0;
|
|
125
|
+
// Approvals live in the Shipping fold, which is often closed: the lead line opens it and goes there.
|
|
126
|
+
const approvalsLine = approvals ? markNeed(el('button', { class: 'btn-primary btn-inline need-line', type: 'button', text: `${approvals === 1 ? 'A pull request needs' : `${approvals} pull requests need`} your approval: show ${approvals === 1 ? 'it' : 'them'}`, onclick: () => { view.open = 'shipping'; paint(); arrivalScroll(main.querySelector('#sec-shipping'), { key: `tap:${Date.now()}` }); } }), 'shipping-approvals') : null;
|
|
124
127
|
const stopped = stoppedCard(shipping, deps);
|
|
125
128
|
const moreLine = todos.length > 1 ? el('button', { class: 'link-btn older-line', type: 'button', text: `${todos.length - 1} more ${todos.length === 2 ? 'thing needs' : 'things need'} you ›`, onclick: () => openMore(todos.slice(1), deps) }) : null;
|
|
126
129
|
|
|
@@ -128,7 +131,7 @@ function paint() {
|
|
|
128
131
|
if (!view.open) view.open = feedback.tone === 'attn' || feedback.state === 'cant-tell' ? 'feedback' : 'shipping';
|
|
129
132
|
setKids(main, el('div', { class: 'dev-page' },
|
|
130
133
|
pageHead({ eyebrow: EYEBROW, headline: h.text, sub: h.sub, tone: h.tone }),
|
|
131
|
-
stopped, lead, moreLine,
|
|
134
|
+
stopped, lead, approvalsLine, moreLine,
|
|
132
135
|
el('div', { class: 'page-sections' },
|
|
133
136
|
fold({ id: 'shipping', title: 'Shipping', line: shipping.line, tone: shipping.tone, body: () => shippingBody(shipping, deps) }),
|
|
134
137
|
fold({ id: 'feedback', title: 'Feedback', line: feedback.line, tone: feedback.tone, body: () => feedbackBody(feedback, deps) }),
|
|
@@ -176,7 +179,7 @@ function backOnButton(s, deps, primary = false) {
|
|
|
176
179
|
function stoppedCard(s, deps) {
|
|
177
180
|
const c = stoppedLead(s);
|
|
178
181
|
if (!c) return null;
|
|
179
|
-
return el('div', { class: 'need-card stopped-lead' }, el('span', { class: 'need-title', text: c.title }), el('span', { class: 'need-sub', text: c.detail }), el('span', { class: 'need-sub', text: c.why }), backOnButton(s, deps, true));
|
|
182
|
+
return el('div', { class: 'need-card stopped-lead', 'data-operator-need': 'merging' }, el('span', { class: 'need-title', text: c.title }), el('span', { class: 'need-sub', text: c.detail }), el('span', { class: 'need-sub', text: c.why }), backOnButton(s, deps, true));
|
|
180
183
|
}
|
|
181
184
|
|
|
182
185
|
// ── to-dos (Feedback approvals and links) ────────────────────────────────────
|
|
@@ -466,29 +466,45 @@ export function pullRequestRefs(row) {
|
|
|
466
466
|
return [...out];
|
|
467
467
|
}
|
|
468
468
|
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
469
|
+
/**
|
|
470
|
+
* The one count. Surfaced rows (needs-model.js) merge in even while the queue or the promises
|
|
471
|
+
* are unread, so a slow read cannot hide a known need. A row naming the same thing as a queue
|
|
472
|
+
* item (its refs appear in that item) is dropped by identity: the queue item carries the action.
|
|
473
|
+
*/
|
|
474
|
+
export function combineNeeds(needs, promised, surfaced) {
|
|
475
|
+
// Nothing surfaced was asked for: the queue alone, exactly as before.
|
|
476
|
+
if (!surfaced && (!needs || !Number.isFinite(needs.count) || !promised || promised.state === 'loading')) return needs;
|
|
477
|
+
const known = surfaced && Array.isArray(surfaced.rows) ? surfaced : { rows: [], pending: [], failed: [] };
|
|
478
|
+
const pending = Array.isArray(known.pending) && known.pending.length > 0;
|
|
479
|
+
const failed = Array.isArray(known.failed) && known.failed.length > 0;
|
|
480
|
+
if (!needs || !Number.isFinite(needs.count)) {
|
|
481
|
+
if (!known.rows.length) return needs ? { ...needs, surfacedPending: pending } : needs; // loading or can't tell: say so as-is
|
|
482
|
+
needs = { ...(needs || {}), count: 0, all: [], rows: [], line: '', unreadQueue: true };
|
|
483
|
+
}
|
|
473
484
|
const queue = needs.all || needs.rows || [];
|
|
485
|
+
const words = (r) => `${r.main || ''} ${r.sub || ''}`.toLowerCase();
|
|
486
|
+
const hasToken = (text, ref) => { const at = text.indexOf(ref); return at >= 0 && !/[\w.@-]/.test(text[at + ref.length] || ' ') && !/[\w.@-]/.test(text[at - 1] || ' '); };
|
|
487
|
+
const rows = known.rows.filter((r) => !(r.refs || []).some((ref) => queue.some((q) => hasToken(words(q), String(ref).toLowerCase()))));
|
|
474
488
|
// A promise that waits on the same pull request as a queue item is the same
|
|
475
489
|
// ask twice (the approval itself, and my promise to merge after it): show the
|
|
476
490
|
// queue item, which carries the action, and drop the promise row.
|
|
477
491
|
const queueRefs = new Set(queue.flatMap(pullRequestRefs));
|
|
478
|
-
const
|
|
492
|
+
const promisesRead = promised && promised.state !== 'loading';
|
|
493
|
+
const pCount = promisesRead && Number.isFinite(promised.count) ? promised.count : 0;
|
|
494
|
+
const waitingAll = promisesRead ? (promised.all || []).filter((r) => r.tone === 'attn').slice(0, pCount) : [];
|
|
479
495
|
const waiting = waitingAll.filter((r) => !pullRequestRefs(r).some((ref) => queueRefs.has(ref)))
|
|
480
496
|
.map((r) => ({ ...r, sub: ['a promise', r.sub].filter(Boolean).join(' · ') }));
|
|
481
|
-
const promisesKnown = Number.isFinite(promised.count);
|
|
482
|
-
const count = needs.count + waiting.length;
|
|
483
|
-
// Urgent queue items lead (the headline names the first row), then promises, then the rest.
|
|
484
|
-
const all = [...queue.filter((r) => r.tone === 'bad'), ...waiting, ...queue.filter((r) => r.tone !== 'bad')];
|
|
497
|
+
const promisesKnown = !promisesRead || Number.isFinite(promised.count);
|
|
498
|
+
const count = needs.count + rows.length + waiting.length;
|
|
499
|
+
// Urgent queue items lead (the headline names the first row), then what is surfaced, then promises, then the rest.
|
|
500
|
+
const all = [...queue.filter((r) => r.tone === 'bad'), ...rows, ...waiting, ...queue.filter((r) => r.tone !== 'bad')];
|
|
485
501
|
const notes = !promisesKnown ? "Couldn't check for promises waiting on you."
|
|
486
|
-
: promised.state === 'stale' ? 'Promise information is from the last successful check.' : '';
|
|
502
|
+
: promisesRead && promised.state === 'stale' ? 'Promise information is from the last successful check.' : '';
|
|
487
503
|
const queueLine = needs.line === 'The queue is clear.' || needs.line === 'Waiting on you.' ? null : needs.line;
|
|
488
504
|
const line = count ? '' : [notes, queueLine].filter(Boolean).join(' ') || 'Nothing is waiting on you.';
|
|
489
|
-
const partial = needs.state === 'partial' || !promisesKnown || promised.state === 'stale' || !!promised.coverage;
|
|
505
|
+
const partial = needs.state === 'partial' || !promisesKnown || needs.unreadQueue || failed || (promisesRead && (promised.state === 'stale' || !!promised.coverage));
|
|
490
506
|
return {
|
|
491
|
-
...needs, combined: true, count, all, rows: all.slice(0, MAX_ROWS), line,
|
|
507
|
+
...needs, combined: true, count, all, rows: all.slice(0, MAX_ROWS), line, surfacedPending: pending, surfacedCount: rows.length,
|
|
492
508
|
note: count ? null : needs.note,
|
|
493
509
|
statusNote: count ? [needs.statusNote, notes, queueLine].filter(Boolean).join(' ') : needs.statusNote,
|
|
494
510
|
coverage: partial ? 'partial view' : needs.coverage,
|
|
@@ -520,7 +536,7 @@ export function headline(allCards) {
|
|
|
520
536
|
const otherAttn = attention.length;
|
|
521
537
|
const loading = list.filter((c) => c.state === 'loading').length;
|
|
522
538
|
// Never announce a count, or "all quiet", before both counting cards answered.
|
|
523
|
-
if (loading === list.length || (cards.needs && cards.needs.state === 'loading') || (cards.promised && cards.promised.state === 'loading')) return { text: 'Checking in…', tone: 'muted' };
|
|
539
|
+
if (loading === list.length || (cards.needs && cards.needs.state === 'loading' && !Number.isFinite(cards.needs.count)) || (cards.promised && cards.promised.state === 'loading' && !(cards.needs && cards.needs.surfacedCount > 0)) || (cards.needs && cards.needs.surfacedPending && !(cards.needs.count > 0))) return { text: 'Checking in…', tone: 'muted' };
|
|
524
540
|
// A known action survives incomplete coverage. Coverage belongs under agent
|
|
525
541
|
// status, not in the sentence describing the action.
|
|
526
542
|
const count = cards.needs && cards.needs.combined ? known(cards.needs) : known(cards.needs) + known(cards.promised);
|
|
@@ -537,7 +553,7 @@ export function headline(allCards) {
|
|
|
537
553
|
const context = withContext ? `${withContext[2].replace(/,\s*/g, ' · ')}.` : null;
|
|
538
554
|
return { text: title || 'Open what needs your attention', tone: 'attn',
|
|
539
555
|
sub: [context, first?.action ? 'Tap to review and respond.' : null,
|
|
540
|
-
count > 1 ? `${count - 1 === 1 ? 'One more thing needs' : `${count - 1} more things need`} you
|
|
556
|
+
count > 1 ? `${count - 1 === 1 ? 'One more thing needs' : `${count - 1} more things need`} you.` : null].filter(Boolean).join(' '),
|
|
541
557
|
action: first?.action || null };
|
|
542
558
|
}
|
|
543
559
|
// Only what the operator can act on takes the headline. My own areas
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
issueRequest, revokeRequest, grantRequest, approveRequest, denyRequest, resultWords,
|
|
10
10
|
FLOOR_ACTIONS, GRANT_DURATIONS, AUTHORITIES_TEMPLATE, humanAction, defaultExpiryLocal,
|
|
11
11
|
} from './mandates-model.js';
|
|
12
|
-
import { el, setKids, pageHead, section, sheet, closeSheet, story, details, note, cantTell, askPin, firstPaint } from './ui.js';
|
|
12
|
+
import { el, setKids, pageHead, section, sheet, closeSheet, story, details, note, cantTell, askPin, firstPaint, markNeed } from './ui.js';
|
|
13
13
|
import { legacyHref } from './nav-model.js';
|
|
14
14
|
import { verifiedDisplayName, uniqueNamedChoices } from './display-identity.js';
|
|
15
15
|
|
|
@@ -50,7 +50,7 @@ export async function renderMandates(main, ctx) {
|
|
|
50
50
|
|
|
51
51
|
const parts = [pageHead({ eyebrow: 'People · Permission slips', headline: h.text, tone: h.tone, facts: mandatesFacts({ slips, approvals, audit }) })];
|
|
52
52
|
// A broken chain is an incident: it leads the page, above everything else.
|
|
53
|
-
if (audit.ok && audit.chain.state === 'broken') parts.push(el('section', { class: 'incident', role: 'alert' }, el('span', { class: 'incident-title', text: 'Treat as an incident' }), el('p', { class: 'incident-text', text: `${audit.chain.words} Every decision
|
|
53
|
+
if (audit.ok && audit.chain.state === 'broken') parts.push(el('section', { class: 'incident', role: 'alert' }, el('span', { class: 'incident-title', text: 'Treat as an incident' }), el('p', { class: 'incident-text', text: `${audit.chain.words} Every decision on this page is suspect until the record is checked.` })));
|
|
54
54
|
const sections = [];
|
|
55
55
|
if (!approvals.hidden) sections.push(approvalsBlock(approvals, deps));
|
|
56
56
|
if (!slips.ok) sections.push(el('section', { class: 'list-section', id: 'sec-active' }, el('div', { class: 'list-head' }, el('h2', { class: 'eyebrow', text: 'Active slips' })), cantTell(slips.reason)));
|
|
@@ -70,7 +70,7 @@ function approvalsBlock(approvals, deps) {
|
|
|
70
70
|
if (!approvals.ok) { wrap.append(el('p', { class: 'card-note', text: approvals.reason })); return wrap; }
|
|
71
71
|
if (!approvals.rows.length) { wrap.append(el('p', { class: 'empty-line', text: 'Nothing is waiting for your approval.' })); return wrap; }
|
|
72
72
|
for (const r of approvals.rows) wrap.append(approvalCard(r, deps));
|
|
73
|
-
return wrap;
|
|
73
|
+
return markNeed(wrap, 'approvals');
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
function approvalCard(r, deps) {
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
// The one list of states that need the operator (docs/specs/operator-needs-first-screen.md).
|
|
2
|
+
// Home's count and headline come from it, and every state's own page leads with it, so
|
|
3
|
+
// neither can miss one. Pure: it reads nothing itself and touches no DOM. Rows are built with
|
|
4
|
+
// each page's own model, so Home and the page cannot disagree.
|
|
5
|
+
|
|
6
|
+
import { buildShipping, stoppedLead, operatorTodos, sourceState, agentOwnedStop } from './development-model.js';
|
|
7
|
+
import { buildAccountsPage } from './accounts-model.js';
|
|
8
|
+
import { pairingModel, waitingCards } from './agents-model.js';
|
|
9
|
+
import { buildApprovals } from './mandates-model.js';
|
|
10
|
+
import { buildPlansPage } from './plans-model.js';
|
|
11
|
+
import { buildRequests } from './secrets-model.js';
|
|
12
|
+
import { spendingNeed } from './costs-model.js';
|
|
13
|
+
|
|
14
|
+
const row = (key, main, sub, href, refs = []) => ({ key: `need:${key}`, main, sub: sub || '', tone: 'attn', refs, action: { kind: 'href', href } });
|
|
15
|
+
|
|
16
|
+
/** A 200 that does not have the expected shape: it told us nothing, so never read it as "nothing waiting". */
|
|
17
|
+
const UNREADABLE = null;
|
|
18
|
+
|
|
19
|
+
export const NEED_SOURCES = [
|
|
20
|
+
{ id: 'merging', page: 'work/development', reads: { watcher: '/green-pr-automerge' },
|
|
21
|
+
rows: ({ watcher }, now) => {
|
|
22
|
+
if (agentOwnedStop(watcher.data)) return []; // the agent releases this itself; the page still shows it
|
|
23
|
+
const lead = stoppedLead(buildShipping({ watcher }, now, {}));
|
|
24
|
+
return lead ? [row('merging', lead.title, lead.detail, '#/work/development')] : [];
|
|
25
|
+
} },
|
|
26
|
+
{ id: 'shipping-approvals', page: 'work/development', reads: { watcher: '/green-pr-automerge' },
|
|
27
|
+
rows: ({ watcher }, now) => buildShipping({ watcher }, now, {}).rows.filter((r) => r.group === 'needs')
|
|
28
|
+
.map((r) => row(`pr:${r.pr}`, `Pull request #${r.pr} needs your approval on GitHub`, '', '#/work/development?s=shipping', [`#${r.pr}`, `PR ${r.pr}`])) },
|
|
29
|
+
// Four reads, on a slower beat, and only where the merge watcher says this is a maintainer's agent.
|
|
30
|
+
{ id: 'feedback', page: 'work/development', everyTicks: 4, when: (known) => sourceState(known.merging && known.merging.results && known.merging.results.watcher) === 'ok',
|
|
31
|
+
reads: { readiness: '/feedback-factory/readiness-authorities/proposal', triageProposal: '/feedback-factory/triage/authority/proposal', execute: '/feedback-factory/execute/status', queue: '/feedback-factory/triage/queue' },
|
|
32
|
+
rows: (r) => operatorTodos(r).map((t, i) => row(`feedback:${t.action || t.kind}:${i}`, t.sentence, t.detail, '#/work/development?s=feedback')) },
|
|
33
|
+
{ id: 'accounts', page: 'capacity/accounts', reads: { pool: '/subscription-pool?scope=pool', relogin: '/subscription-relogin?scope=pool' },
|
|
34
|
+
rows: ({ pool, relogin }, now) => {
|
|
35
|
+
const episodes = relogin.ok && Array.isArray(relogin.data && relogin.data.episodes) ? relogin.data.episodes : [];
|
|
36
|
+
const page = buildAccountsPage(pool, now, { episodes });
|
|
37
|
+
if (!page.ok) return pool.data && pool.data.enabled === false ? [] : UNREADABLE;
|
|
38
|
+
return page.needs.map((n) => row(`account:${n.key}`, n.title, n.sub, '#/capacity/accounts?s=needs', [String(n.label || '').split(' · ').pop()].filter(Boolean)));
|
|
39
|
+
} },
|
|
40
|
+
{ id: 'agents', page: 'people/agents', reads: { pairing: '/threadline/pairing', invites: '/threadline/pairing/invites' },
|
|
41
|
+
rows: ({ pairing, invites }, now) => waitingCards(pairingModel(pairing, invites, undefined, now), now)
|
|
42
|
+
.map((c, i) => row(`agent:${c.kind}:${c.iid || c.id || i}`, c.title, '', '#/people/agents')) },
|
|
43
|
+
{ id: 'approvals', page: 'people/mandates', reads: { approvals: '/authorization-requests?status=pending' },
|
|
44
|
+
rows: ({ approvals }) => { const a = buildApprovals(approvals); return a.ok ? a.rows.map((r) => row(r.key, r.headline, r.reason, '#/people/mandates?s=waiting')) : UNREADABLE; } },
|
|
45
|
+
{ id: 'plans', page: 'work/plans', reads: { initiatives: '/initiatives', blockers: '/blockers' },
|
|
46
|
+
rows: ({ initiatives, blockers }, now) => {
|
|
47
|
+
const page = buildPlansPage({ initiatives, blockers }, now);
|
|
48
|
+
if (!page.ok) return UNREADABLE;
|
|
49
|
+
return page.sections.needsYou.map((r) => row(`plan:${r.key}`, r.main, r.needs, '#/work/plans?s=needsYou'));
|
|
50
|
+
} },
|
|
51
|
+
{ id: 'secrets', page: 'people/secrets', reads: { pending: '/secrets/pending' },
|
|
52
|
+
// Never the card's own key or link: both carry the one-time drop token.
|
|
53
|
+
rows: ({ pending }, now) => {
|
|
54
|
+
const reqs = buildRequests(pending, { now, names: {}, origin: '' });
|
|
55
|
+
if (!reqs.ok) return UNREADABLE;
|
|
56
|
+
return reqs.waiting.map((c, i) => row(`secret:${i}`, `Waiting for you to supply: ${c.label}`, c.leftWords, '#/people/secrets?s=waiting'));
|
|
57
|
+
} },
|
|
58
|
+
{ id: 'spending', page: 'capacity/costs', reads: { status: '/routing-spend/enable-status' },
|
|
59
|
+
rows: ({ status }) => { const n = spendingNeed(status); return n ? [row('spending', n.title, n.why, '#/capacity/costs')] : []; } },
|
|
60
|
+
];
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Where a console file says a state needs the operator and it is NOT one of the sources.
|
|
64
|
+
* Exact phrases, each with its reason, so a real need added to one of these pages still
|
|
65
|
+
* fails the floor. `file` + `phrase` must both match the line.
|
|
66
|
+
*/
|
|
67
|
+
export const NEED_EXEMPT = [
|
|
68
|
+
{ file: 'home-model.js', phrase: '', why: 'Home itself: this is the card the sources feed.' },
|
|
69
|
+
{ file: 'console.js', phrase: 'What needs you', why: 'Home itself: the card title.' },
|
|
70
|
+
{ file: 'nav-model.js', phrase: 'waiting', why: 'A navigation description of the page, not a state.' },
|
|
71
|
+
{ file: 'promises-model.js', phrase: '', why: 'Already on Home; the operator decided 2026-11-02 that only promises due within 24 hours count.' },
|
|
72
|
+
{ file: 'promises.js', phrase: '', why: 'As promises-model.js.' },
|
|
73
|
+
{ file: 'allowed-model.js', phrase: 'will wait for your approval', why: 'Describes what a safety profile means, not something waiting now.' },
|
|
74
|
+
{ file: 'pace-model.js', phrase: 'waiting on you', why: 'A verdict on queue pace; the items themselves appear in their own sources.' },
|
|
75
|
+
{ file: 'sessions-model.js', phrase: 'Waiting on you', why: 'A comment-like note that sessions do not report this; nothing waits.' },
|
|
76
|
+
];
|
|
77
|
+
|
|
78
|
+
const failedRead = (res) => sourceState(res) === 'failed';
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* known: id → { results, rows?, rowsAt? }, Home's reads so far. Returns the rows that need the
|
|
82
|
+
* operator, plus which sources are unread (say "checking", never "quiet") or failed (say
|
|
83
|
+
* partial, never "quiet"). A failed read keeps its last good rows for graceMs. 503 means off.
|
|
84
|
+
*/
|
|
85
|
+
export function surfacedNeeds(known, now = Date.now(), graceMs = 120000) {
|
|
86
|
+
const out = { rows: [], pending: [], failed: [] };
|
|
87
|
+
for (const s of NEED_SOURCES) {
|
|
88
|
+
const k = known && known[s.id];
|
|
89
|
+
if (s.when && !s.when(known || {})) continue;
|
|
90
|
+
if (!k || !k.results) { if (!s.when) out.pending.push(s.id); continue; } // a conditional source is read on a later tick and never holds the headline
|
|
91
|
+
const reads = Object.values(k.results);
|
|
92
|
+
if (reads.every((r) => sourceState(r) === 'off')) continue; // switched off here; a page's builder handles one optional read being off
|
|
93
|
+
if (reads.some(failedRead)) {
|
|
94
|
+
out.failed.push(s.id);
|
|
95
|
+
if (k.rows && now - (k.rowsAt || 0) <= graceMs) out.rows.push(...k.rows);
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
const rows = s.rows(k.results, now);
|
|
99
|
+
if (rows === UNREADABLE) { out.failed.push(s.id); if (k.rows && now - (k.rowsAt || 0) <= graceMs) out.rows.push(...k.rows); continue; }
|
|
100
|
+
k.rows = rows;
|
|
101
|
+
k.rowsAt = now;
|
|
102
|
+
out.rows.push(...rows);
|
|
103
|
+
}
|
|
104
|
+
return out;
|
|
105
|
+
}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import {
|
|
8
8
|
buildPlansPage, roundsTrack, projectActions, resultWords, refusalWords, driftSentence, linkChips, stepList, blockerStory, reasonWord, statusWord, PROJECT_DETAIL_CAP, OPEN_STATUSES,
|
|
9
9
|
} from './plans-model.js';
|
|
10
|
-
import { el, setKids, pageHead, section, sheet, closeSheet, sheetIsOpen, story, details, note, cantTell, firstPaint, arrivalScroll } from './ui.js';
|
|
10
|
+
import { el, setKids, pageHead, section, sheet, closeSheet, sheetIsOpen, story, details, note, cantTell, firstPaint, arrivalScroll, markNeed } from './ui.js';
|
|
11
11
|
import { legacyHref } from './nav-model.js';
|
|
12
12
|
import { topicNameMap } from './topic-names.js';
|
|
13
13
|
import { topicLink } from './promises.js';
|
|
@@ -49,6 +49,7 @@ function paint() {
|
|
|
49
49
|
|
|
50
50
|
// Needs you: fresh items as rows; older ones waiting on you are a counted line, never dropped.
|
|
51
51
|
const needs = section({ id: 'needsYou', title: 'Needs you', tone: 'attn', rows: s.needsYou, empty: 'Nothing is waiting on you.', onRow: open });
|
|
52
|
+
if (s.needsYou.length) markNeed(needs, 'plans');
|
|
52
53
|
if (n.olderNeedsYou) needs.querySelector('.list-rows').append(el('a', { class: 'link-btn older-line', href: '#/work/plans?s=older', text: `+${n.olderNeedsYou} older, still waiting on you ›` }));
|
|
53
54
|
|
|
54
55
|
// In the way, above In flight. Off and failed are one dashed line each, not a page.
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
topicNameMap, conversationChoices, requestCard, requestBody, testRequestBody, resultWords, shareWords,
|
|
10
10
|
leftWords, TTL_CHOICES,
|
|
11
11
|
} from './secrets-model.js';
|
|
12
|
-
import { el, setKids, pageHead, section, sheet, closeSheet, story, details, note, cantTell, firstPaint } from './ui.js';
|
|
12
|
+
import { el, setKids, pageHead, section, sheet, closeSheet, story, details, note, cantTell, firstPaint, markNeed } from './ui.js';
|
|
13
13
|
import { legacyHref } from './nav-model.js';
|
|
14
14
|
|
|
15
15
|
const RECORD_KEYS = ['token', 'topicId', 'createdAt', 'expiresAt'];
|
|
@@ -90,7 +90,7 @@ function waitingBlock(requests, deps, multi) {
|
|
|
90
90
|
const wrap = el('section', { class: 'list-section', id: 'sec-waiting' }, el('div', { class: 'list-head' }, el('h2', { class: 'eyebrow', text: 'Waiting' }), requests.ok ? el('span', { class: 'list-count', text: String(requests.waiting.length) }) : null));
|
|
91
91
|
if (!requests.ok) { wrap.append(cantTell(requests.reason)); return wrap; }
|
|
92
92
|
if (!requests.waiting.length) wrap.append(el('p', { class: 'empty-line', text: 'Nothing is waiting. When the agent needs a password or key, it asks here with a one-time link.' }));
|
|
93
|
-
else wrap.append(el('div', { class: 'secret-cards' }, requests.waiting.map((c) => waitingCard(c, deps))));
|
|
93
|
+
else { markNeed(wrap, 'secrets'); wrap.append(el('div', { class: 'secret-cards' }, requests.waiting.map((c) => waitingCard(c, deps)))); }
|
|
94
94
|
wrap.append(note('Once someone submits, it moves to the agent; it doesn’t stay listed here.'));
|
|
95
95
|
if (multi) wrap.append(note('These are this machine’s requests. Each machine keeps its own.'));
|
|
96
96
|
return wrap;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// Console sign-in from an owner's paired phone (docs/specs/mobile-console-device-signin.md §3.5).
|
|
2
|
+
// The Instar app puts a one-minute, single-use ticket in session storage; the console trades it
|
|
3
|
+
// once for the same token a PIN unlock returns. Any failure falls back to the PIN box.
|
|
4
|
+
|
|
5
|
+
export const TICKET_KEY = 'instar_console_ticket';
|
|
6
|
+
export const REDEEM_PATH = '/dashboard/console-ticket';
|
|
7
|
+
|
|
8
|
+
/** Reads the ticket and removes it at once, before any request, on every outcome. */
|
|
9
|
+
export function takeTicket(storage) {
|
|
10
|
+
let t = '';
|
|
11
|
+
try { t = storage.getItem(TICKET_KEY) || ''; } catch { t = ''; }
|
|
12
|
+
try { storage.removeItem(TICKET_KEY); } catch { /* storage blocked */ }
|
|
13
|
+
return typeof t === 'string' && t.length > 0 && t.length <= 128 ? t : '';
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** POSTs the ticket; resolves to the token, or '' on any failure. */
|
|
17
|
+
export async function redeemTicket(fetchFn, ticket) {
|
|
18
|
+
if (!ticket) return '';
|
|
19
|
+
try {
|
|
20
|
+
const r = await fetchFn(REDEEM_PATH, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ ticket }), cache: 'no-store' });
|
|
21
|
+
if (!r || !r.ok) return '';
|
|
22
|
+
const data = await r.json().catch(() => ({}));
|
|
23
|
+
return data && typeof data.token === 'string' && data.token.length > 0 ? data.token : '';
|
|
24
|
+
} catch { return ''; }
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* The boot decision: a valid stored token wins; else a ticket is redeemed; else the PIN box.
|
|
29
|
+
* Returns { token, via: 'stored' | 'ticket' | 'pin' }.
|
|
30
|
+
*/
|
|
31
|
+
export async function signInAtBoot({ storedToken, validToken, ticket, fetchFn }) {
|
|
32
|
+
if (storedToken && await validToken(storedToken)) return { token: storedToken, via: 'stored' };
|
|
33
|
+
const t = await redeemTicket(fetchFn, ticket);
|
|
34
|
+
if (t) return { token: t, via: 'ticket' };
|
|
35
|
+
return { token: '', via: 'pin' };
|
|
36
|
+
}
|