instar 0.28.75 → 0.28.77
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/index.html +306 -0
- package/dist/cli.js +5 -4
- package/dist/cli.js.map +1 -1
- package/dist/commands/discovery.d.ts.map +1 -1
- package/dist/commands/discovery.js +2 -1
- package/dist/commands/discovery.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +22 -2
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/job.d.ts.map +1 -1
- package/dist/commands/job.js +2 -1
- package/dist/commands/job.js.map +1 -1
- package/dist/commands/ledgerCleanup.d.ts.map +1 -1
- package/dist/commands/ledgerCleanup.js +2 -1
- package/dist/commands/ledgerCleanup.js.map +1 -1
- package/dist/commands/listener.d.ts.map +1 -1
- package/dist/commands/listener.js +7 -6
- package/dist/commands/listener.js.map +1 -1
- package/dist/commands/nuke.d.ts.map +1 -1
- package/dist/commands/nuke.js +11 -15
- package/dist/commands/nuke.js.map +1 -1
- package/dist/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +35 -3
- package/dist/commands/server.js.map +1 -1
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +11 -9
- package/dist/commands/setup.js.map +1 -1
- package/dist/commands/slack-cli.d.ts.map +1 -1
- package/dist/commands/slack-cli.js +5 -4
- package/dist/commands/slack-cli.js.map +1 -1
- package/dist/commands/whatsapp.d.ts.map +1 -1
- package/dist/commands/whatsapp.js +2 -1
- package/dist/commands/whatsapp.js.map +1 -1
- package/dist/commands/worktree.d.ts.map +1 -1
- package/dist/commands/worktree.js +2 -1
- package/dist/commands/worktree.js.map +1 -1
- package/dist/core/AgentConnector.d.ts.map +1 -1
- package/dist/core/AgentConnector.js +9 -7
- package/dist/core/AgentConnector.js.map +1 -1
- package/dist/core/AgentRegistry.d.ts.map +1 -1
- package/dist/core/AgentRegistry.js +3 -2
- package/dist/core/AgentRegistry.js.map +1 -1
- package/dist/core/AutoDispatcher.d.ts.map +1 -1
- package/dist/core/AutoDispatcher.js +2 -1
- package/dist/core/AutoDispatcher.js.map +1 -1
- package/dist/core/AutoUpdater.d.ts.map +1 -1
- package/dist/core/AutoUpdater.js +2 -1
- package/dist/core/AutoUpdater.js.map +1 -1
- package/dist/core/AutonomousEvolution.d.ts.map +1 -1
- package/dist/core/AutonomousEvolution.js +2 -1
- package/dist/core/AutonomousEvolution.js.map +1 -1
- package/dist/core/BackupManager.d.ts.map +1 -1
- package/dist/core/BackupManager.js +2 -1
- package/dist/core/BackupManager.js.map +1 -1
- package/dist/core/BranchManager.d.ts.map +1 -1
- package/dist/core/BranchManager.js +3 -2
- package/dist/core/BranchManager.js.map +1 -1
- package/dist/core/CaffeinateManager.d.ts.map +1 -1
- package/dist/core/CaffeinateManager.js +2 -1
- package/dist/core/CaffeinateManager.js.map +1 -1
- package/dist/core/DeferredDispatchTracker.d.ts.map +1 -1
- package/dist/core/DeferredDispatchTracker.js +2 -1
- package/dist/core/DeferredDispatchTracker.js.map +1 -1
- package/dist/core/DispatchManager.d.ts.map +1 -1
- package/dist/core/DispatchManager.js +3 -2
- package/dist/core/DispatchManager.js.map +1 -1
- package/dist/core/EvolutionManager.d.ts.map +1 -1
- package/dist/core/EvolutionManager.js +2 -1
- package/dist/core/EvolutionManager.js.map +1 -1
- package/dist/core/ExecutionJournal.d.ts.map +1 -1
- package/dist/core/ExecutionJournal.js +2 -1
- package/dist/core/ExecutionJournal.js.map +1 -1
- package/dist/core/FeedbackManager.d.ts.map +1 -1
- package/dist/core/FeedbackManager.js +2 -1
- package/dist/core/FeedbackManager.js.map +1 -1
- package/dist/core/FileClassifier.d.ts.map +1 -1
- package/dist/core/FileClassifier.js +8 -13
- package/dist/core/FileClassifier.js.map +1 -1
- package/dist/core/ForegroundRestartWatcher.d.ts.map +1 -1
- package/dist/core/ForegroundRestartWatcher.js +3 -2
- package/dist/core/ForegroundRestartWatcher.js.map +1 -1
- package/dist/core/GitStateManager.d.ts.map +1 -1
- package/dist/core/GitStateManager.js +3 -11
- package/dist/core/GitStateManager.js.map +1 -1
- package/dist/core/GitSync.d.ts.map +1 -1
- package/dist/core/GitSync.js +6 -3
- package/dist/core/GitSync.js.map +1 -1
- package/dist/core/GlobalInstallCleanup.d.ts.map +1 -1
- package/dist/core/GlobalInstallCleanup.js +3 -2
- package/dist/core/GlobalInstallCleanup.js.map +1 -1
- package/dist/core/GlobalSecretStore.d.ts.map +1 -1
- package/dist/core/GlobalSecretStore.js +3 -2
- package/dist/core/GlobalSecretStore.js.map +1 -1
- package/dist/core/HandoffManager.d.ts.map +1 -1
- package/dist/core/HandoffManager.js +5 -3
- package/dist/core/HandoffManager.js.map +1 -1
- package/dist/core/JargonDetector.d.ts +28 -0
- package/dist/core/JargonDetector.d.ts.map +1 -0
- package/dist/core/JargonDetector.js +59 -0
- package/dist/core/JargonDetector.js.map +1 -0
- package/dist/core/LedgerSessionRegistry.d.ts.map +1 -1
- package/dist/core/LedgerSessionRegistry.js +2 -1
- package/dist/core/LedgerSessionRegistry.js.map +1 -1
- package/dist/core/MachineIdentity.d.ts.map +1 -1
- package/dist/core/MachineIdentity.js +2 -1
- package/dist/core/MachineIdentity.js.map +1 -1
- package/dist/core/MessagingToneGate.d.ts +42 -5
- package/dist/core/MessagingToneGate.d.ts.map +1 -1
- package/dist/core/MessagingToneGate.js +40 -6
- package/dist/core/MessagingToneGate.js.map +1 -1
- package/dist/core/ParallelDevWiring.d.ts.map +1 -1
- package/dist/core/ParallelDevWiring.js +3 -5
- package/dist/core/ParallelDevWiring.js.map +1 -1
- package/dist/core/PostUpdateMigrator.d.ts +26 -0
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +249 -44
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/core/ProjectMapper.d.ts.map +1 -1
- package/dist/core/ProjectMapper.js +5 -9
- package/dist/core/ProjectMapper.js.map +1 -1
- package/dist/core/RelationshipManager.d.ts.map +1 -1
- package/dist/core/RelationshipManager.js +4 -3
- package/dist/core/RelationshipManager.js.map +1 -1
- package/dist/core/SafeFsExecutor.d.ts +41 -0
- package/dist/core/SafeFsExecutor.d.ts.map +1 -0
- package/dist/core/SafeFsExecutor.js +146 -0
- package/dist/core/SafeFsExecutor.js.map +1 -0
- package/dist/core/SafeGitExecutor.d.ts +145 -0
- package/dist/core/SafeGitExecutor.d.ts.map +1 -0
- package/dist/core/SafeGitExecutor.js +717 -0
- package/dist/core/SafeGitExecutor.js.map +1 -0
- package/dist/core/ScopeVerifier.d.ts.map +1 -1
- package/dist/core/ScopeVerifier.js +3 -5
- package/dist/core/ScopeVerifier.js.map +1 -1
- package/dist/core/SecretStore.d.ts.map +1 -1
- package/dist/core/SecretStore.js +2 -1
- package/dist/core/SecretStore.js.map +1 -1
- package/dist/core/SharedStateLedger.d.ts.map +1 -1
- package/dist/core/SharedStateLedger.js +2 -1
- package/dist/core/SharedStateLedger.js.map +1 -1
- package/dist/core/SoulManager.d.ts.map +1 -1
- package/dist/core/SoulManager.js +3 -2
- package/dist/core/SoulManager.js.map +1 -1
- package/dist/core/StateManager.d.ts.map +1 -1
- package/dist/core/StateManager.js +4 -3
- package/dist/core/StateManager.js.map +1 -1
- package/dist/core/SyncOrchestrator.d.ts.map +1 -1
- package/dist/core/SyncOrchestrator.js +6 -4
- package/dist/core/SyncOrchestrator.js.map +1 -1
- package/dist/core/UpdateChecker.d.ts.map +1 -1
- package/dist/core/UpdateChecker.js +3 -2
- package/dist/core/UpdateChecker.js.map +1 -1
- package/dist/core/UpgradeGuideProcessor.d.ts.map +1 -1
- package/dist/core/UpgradeGuideProcessor.js +3 -2
- package/dist/core/UpgradeGuideProcessor.js.map +1 -1
- package/dist/core/WorktreeManager.d.ts.map +1 -1
- package/dist/core/WorktreeManager.js +9 -7
- package/dist/core/WorktreeManager.js.map +1 -1
- package/dist/knowledge/KnowledgeManager.d.ts.map +1 -1
- package/dist/knowledge/KnowledgeManager.js +2 -1
- package/dist/knowledge/KnowledgeManager.js.map +1 -1
- package/dist/lifeline/ServerSupervisor.d.ts +28 -0
- package/dist/lifeline/ServerSupervisor.d.ts.map +1 -1
- package/dist/lifeline/ServerSupervisor.js +171 -59
- package/dist/lifeline/ServerSupervisor.js.map +1 -1
- package/dist/lifeline/TelegramLifeline.d.ts.map +1 -1
- package/dist/lifeline/TelegramLifeline.js +10 -3
- package/dist/lifeline/TelegramLifeline.js.map +1 -1
- package/dist/lifeline/detectLaunchdSupervised.d.ts +43 -0
- package/dist/lifeline/detectLaunchdSupervised.d.ts.map +1 -0
- package/dist/lifeline/detectLaunchdSupervised.js +106 -0
- package/dist/lifeline/detectLaunchdSupervised.js.map +1 -0
- package/dist/lifeline/droppedMessages.d.ts.map +1 -1
- package/dist/lifeline/droppedMessages.js +2 -1
- package/dist/lifeline/droppedMessages.js.map +1 -1
- package/dist/memory/EpisodicMemory.d.ts.map +1 -1
- package/dist/memory/EpisodicMemory.js +2 -1
- package/dist/memory/EpisodicMemory.js.map +1 -1
- package/dist/memory/TopicMemory.d.ts.map +1 -1
- package/dist/memory/TopicMemory.js +5 -4
- package/dist/memory/TopicMemory.js.map +1 -1
- package/dist/messaging/AgentTokenManager.d.ts.map +1 -1
- package/dist/messaging/AgentTokenManager.js +2 -1
- package/dist/messaging/AgentTokenManager.js.map +1 -1
- package/dist/messaging/DropPickup.d.ts.map +1 -1
- package/dist/messaging/DropPickup.js +2 -1
- package/dist/messaging/DropPickup.js.map +1 -1
- package/dist/messaging/GitSyncTransport.d.ts.map +1 -1
- package/dist/messaging/GitSyncTransport.js +4 -3
- package/dist/messaging/GitSyncTransport.js.map +1 -1
- package/dist/messaging/MessageStore.d.ts.map +1 -1
- package/dist/messaging/MessageStore.js +3 -2
- package/dist/messaging/MessageStore.js.map +1 -1
- package/dist/messaging/TelegramAdapter.d.ts.map +1 -1
- package/dist/messaging/TelegramAdapter.js +5 -4
- package/dist/messaging/TelegramAdapter.js.map +1 -1
- package/dist/messaging/backends/BaileysBackend.d.ts.map +1 -1
- package/dist/messaging/backends/BaileysBackend.js +3 -2
- package/dist/messaging/backends/BaileysBackend.js.map +1 -1
- package/dist/messaging/local-tone-check.d.ts +61 -0
- package/dist/messaging/local-tone-check.d.ts.map +1 -0
- package/dist/messaging/local-tone-check.js +78 -0
- package/dist/messaging/local-tone-check.js.map +1 -0
- package/dist/messaging/pending-relay-store.d.ts +153 -0
- package/dist/messaging/pending-relay-store.d.ts.map +1 -0
- package/dist/messaging/pending-relay-store.js +351 -0
- package/dist/messaging/pending-relay-store.js.map +1 -0
- package/dist/messaging/secret-patterns.d.ts +35 -0
- package/dist/messaging/secret-patterns.d.ts.map +1 -0
- package/dist/messaging/secret-patterns.js +70 -0
- package/dist/messaging/secret-patterns.js.map +1 -0
- package/dist/messaging/shared/EncryptedAuthStore.d.ts.map +1 -1
- package/dist/messaging/shared/EncryptedAuthStore.js +3 -2
- package/dist/messaging/shared/EncryptedAuthStore.js.map +1 -1
- package/dist/messaging/shared/MessageLogger.d.ts.map +1 -1
- package/dist/messaging/shared/MessageLogger.js +2 -1
- package/dist/messaging/shared/MessageLogger.js.map +1 -1
- package/dist/messaging/shared/PrivacyConsent.d.ts.map +1 -1
- package/dist/messaging/shared/PrivacyConsent.js +2 -1
- package/dist/messaging/shared/PrivacyConsent.js.map +1 -1
- package/dist/messaging/shared/SessionChannelRegistry.d.ts.map +1 -1
- package/dist/messaging/shared/SessionChannelRegistry.js +2 -1
- package/dist/messaging/shared/SessionChannelRegistry.js.map +1 -1
- package/dist/messaging/system-templates.d.ts +87 -0
- package/dist/messaging/system-templates.d.ts.map +1 -0
- package/dist/messaging/system-templates.js +236 -0
- package/dist/messaging/system-templates.js.map +1 -0
- package/dist/messaging/whoami-cache.d.ts +66 -0
- package/dist/messaging/whoami-cache.d.ts.map +1 -0
- package/dist/messaging/whoami-cache.js +149 -0
- package/dist/messaging/whoami-cache.js.map +1 -0
- package/dist/moltbridge/ProfileCompiler.d.ts.map +1 -1
- package/dist/moltbridge/ProfileCompiler.js +13 -4
- package/dist/moltbridge/ProfileCompiler.js.map +1 -1
- package/dist/monitoring/CommitmentTracker.d.ts.map +1 -1
- package/dist/monitoring/CommitmentTracker.js +2 -1
- package/dist/monitoring/CommitmentTracker.js.map +1 -1
- package/dist/monitoring/CredentialProvider.d.ts.map +1 -1
- package/dist/monitoring/CredentialProvider.js +2 -1
- package/dist/monitoring/CredentialProvider.js.map +1 -1
- package/dist/monitoring/DegradationReporter.d.ts +41 -0
- package/dist/monitoring/DegradationReporter.d.ts.map +1 -1
- package/dist/monitoring/DegradationReporter.js +96 -4
- package/dist/monitoring/DegradationReporter.js.map +1 -1
- package/dist/monitoring/HealthChecker.d.ts.map +1 -1
- package/dist/monitoring/HealthChecker.js +2 -1
- package/dist/monitoring/HealthChecker.js.map +1 -1
- package/dist/monitoring/HookEventReceiver.d.ts.map +1 -1
- package/dist/monitoring/HookEventReceiver.js +2 -1
- package/dist/monitoring/HookEventReceiver.js.map +1 -1
- package/dist/monitoring/InstructionsVerifier.d.ts.map +1 -1
- package/dist/monitoring/InstructionsVerifier.js +2 -1
- package/dist/monitoring/InstructionsVerifier.js.map +1 -1
- package/dist/monitoring/PresenceProxy.d.ts.map +1 -1
- package/dist/monitoring/PresenceProxy.js +5 -4
- package/dist/monitoring/PresenceProxy.js.map +1 -1
- package/dist/monitoring/QuotaTracker.d.ts.map +1 -1
- package/dist/monitoring/QuotaTracker.js +2 -1
- package/dist/monitoring/QuotaTracker.js.map +1 -1
- package/dist/monitoring/SessionMigrator.d.ts.map +1 -1
- package/dist/monitoring/SessionMigrator.js +2 -1
- package/dist/monitoring/SessionMigrator.js.map +1 -1
- package/dist/monitoring/SessionRecovery.d.ts.map +1 -1
- package/dist/monitoring/SessionRecovery.js +2 -1
- package/dist/monitoring/SessionRecovery.js.map +1 -1
- package/dist/monitoring/TelemetryAuth.d.ts.map +1 -1
- package/dist/monitoring/TelemetryAuth.js +3 -2
- package/dist/monitoring/TelemetryAuth.js.map +1 -1
- package/dist/monitoring/TokenLedger.d.ts +91 -0
- package/dist/monitoring/TokenLedger.d.ts.map +1 -0
- package/dist/monitoring/TokenLedger.js +426 -0
- package/dist/monitoring/TokenLedger.js.map +1 -0
- package/dist/monitoring/TokenLedgerPoller.d.ts +26 -0
- package/dist/monitoring/TokenLedgerPoller.d.ts.map +1 -0
- package/dist/monitoring/TokenLedgerPoller.js +44 -0
- package/dist/monitoring/TokenLedgerPoller.js.map +1 -0
- package/dist/monitoring/TriageOrchestrator.d.ts.map +1 -1
- package/dist/monitoring/TriageOrchestrator.js +3 -2
- package/dist/monitoring/TriageOrchestrator.js.map +1 -1
- package/dist/monitoring/WorktreeReaper.d.ts.map +1 -1
- package/dist/monitoring/WorktreeReaper.js +5 -4
- package/dist/monitoring/WorktreeReaper.js.map +1 -1
- package/dist/monitoring/delivery-failure-sentinel/recovery-policy.d.ts +83 -0
- package/dist/monitoring/delivery-failure-sentinel/recovery-policy.d.ts.map +1 -0
- package/dist/monitoring/delivery-failure-sentinel/recovery-policy.js +218 -0
- package/dist/monitoring/delivery-failure-sentinel/recovery-policy.js.map +1 -0
- package/dist/monitoring/delivery-failure-sentinel.d.ts +177 -0
- package/dist/monitoring/delivery-failure-sentinel.d.ts.map +1 -0
- package/dist/monitoring/delivery-failure-sentinel.js +598 -0
- package/dist/monitoring/delivery-failure-sentinel.js.map +1 -0
- package/dist/monitoring/probes/PlatformProbe.d.ts.map +1 -1
- package/dist/monitoring/probes/PlatformProbe.js +3 -2
- package/dist/monitoring/probes/PlatformProbe.js.map +1 -1
- package/dist/monitoring/templates-drift-verifier.d.ts +109 -0
- package/dist/monitoring/templates-drift-verifier.d.ts.map +1 -0
- package/dist/monitoring/templates-drift-verifier.js +324 -0
- package/dist/monitoring/templates-drift-verifier.js.map +1 -0
- package/dist/paste/PasteManager.d.ts.map +1 -1
- package/dist/paste/PasteManager.js +5 -4
- package/dist/paste/PasteManager.js.map +1 -1
- package/dist/publishing/PrivateViewer.d.ts.map +1 -1
- package/dist/publishing/PrivateViewer.js +2 -1
- package/dist/publishing/PrivateViewer.js.map +1 -1
- package/dist/scheduler/JobScheduler.d.ts.map +1 -1
- package/dist/scheduler/JobScheduler.js +2 -1
- package/dist/scheduler/JobScheduler.js.map +1 -1
- package/dist/server/AgentServer.d.ts +18 -0
- package/dist/server/AgentServer.d.ts.map +1 -1
- package/dist/server/AgentServer.js +186 -1
- package/dist/server/AgentServer.js.map +1 -1
- package/dist/server/WebSocketManager.d.ts +11 -0
- package/dist/server/WebSocketManager.d.ts.map +1 -1
- package/dist/server/WebSocketManager.js +28 -0
- package/dist/server/WebSocketManager.js.map +1 -1
- package/dist/server/boot-id.d.ts +58 -0
- package/dist/server/boot-id.d.ts.map +1 -0
- package/dist/server/boot-id.js +121 -0
- package/dist/server/boot-id.js.map +1 -0
- package/dist/server/middleware.d.ts +14 -1
- package/dist/server/middleware.d.ts.map +1 -1
- package/dist/server/middleware.js +81 -1
- package/dist/server/middleware.js.map +1 -1
- package/dist/server/routes.d.ts +69 -0
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +545 -16
- package/dist/server/routes.js.map +1 -1
- package/dist/threadline/AgentDiscovery.d.ts.map +1 -1
- package/dist/threadline/AgentDiscovery.js +2 -1
- package/dist/threadline/AgentDiscovery.js.map +1 -1
- package/dist/threadline/AgentTrustManager.d.ts.map +1 -1
- package/dist/threadline/AgentTrustManager.js +2 -1
- package/dist/threadline/AgentTrustManager.js.map +1 -1
- package/dist/threadline/CircuitBreaker.d.ts.map +1 -1
- package/dist/threadline/CircuitBreaker.js +2 -1
- package/dist/threadline/CircuitBreaker.js.map +1 -1
- package/dist/threadline/ComputeMeter.d.ts.map +1 -1
- package/dist/threadline/ComputeMeter.js +2 -1
- package/dist/threadline/ComputeMeter.js.map +1 -1
- package/dist/threadline/ContextThreadMap.d.ts.map +1 -1
- package/dist/threadline/ContextThreadMap.js +2 -1
- package/dist/threadline/ContextThreadMap.js.map +1 -1
- package/dist/threadline/HeartbeatWatchdog.d.ts +78 -0
- package/dist/threadline/HeartbeatWatchdog.d.ts.map +1 -0
- package/dist/threadline/HeartbeatWatchdog.js +212 -0
- package/dist/threadline/HeartbeatWatchdog.js.map +1 -0
- package/dist/threadline/HeartbeatWriter.d.ts +79 -0
- package/dist/threadline/HeartbeatWriter.d.ts.map +1 -0
- package/dist/threadline/HeartbeatWriter.js +109 -0
- package/dist/threadline/HeartbeatWriter.js.map +1 -0
- package/dist/threadline/InvitationManager.d.ts.map +1 -1
- package/dist/threadline/InvitationManager.js +2 -1
- package/dist/threadline/InvitationManager.js.map +1 -1
- package/dist/threadline/ListenerSessionManager.d.ts +24 -0
- package/dist/threadline/ListenerSessionManager.d.ts.map +1 -1
- package/dist/threadline/ListenerSessionManager.js +38 -0
- package/dist/threadline/ListenerSessionManager.js.map +1 -1
- package/dist/threadline/MCPAuth.d.ts.map +1 -1
- package/dist/threadline/MCPAuth.js +2 -1
- package/dist/threadline/MCPAuth.js.map +1 -1
- package/dist/threadline/PipeSessionSpawner.d.ts.map +1 -1
- package/dist/threadline/PipeSessionSpawner.js +3 -2
- package/dist/threadline/PipeSessionSpawner.js.map +1 -1
- package/dist/threadline/RateLimiter.d.ts.map +1 -1
- package/dist/threadline/RateLimiter.js +2 -1
- package/dist/threadline/RateLimiter.js.map +1 -1
- package/dist/threadline/RelaySpawnFailureHandler.d.ts +53 -0
- package/dist/threadline/RelaySpawnFailureHandler.d.ts.map +1 -0
- package/dist/threadline/RelaySpawnFailureHandler.js +73 -0
- package/dist/threadline/RelaySpawnFailureHandler.js.map +1 -0
- package/dist/threadline/SessionLifecycle.d.ts.map +1 -1
- package/dist/threadline/SessionLifecycle.js +2 -1
- package/dist/threadline/SessionLifecycle.js.map +1 -1
- package/dist/threadline/SpawnLedger.d.ts +94 -0
- package/dist/threadline/SpawnLedger.d.ts.map +1 -0
- package/dist/threadline/SpawnLedger.js +194 -0
- package/dist/threadline/SpawnLedger.js.map +1 -0
- package/dist/threadline/SpawnNonce.d.ts +49 -0
- package/dist/threadline/SpawnNonce.d.ts.map +1 -0
- package/dist/threadline/SpawnNonce.js +99 -0
- package/dist/threadline/SpawnNonce.js.map +1 -0
- package/dist/threadline/TelegramBridgeConfig.d.ts +79 -0
- package/dist/threadline/TelegramBridgeConfig.d.ts.map +1 -0
- package/dist/threadline/TelegramBridgeConfig.js +168 -0
- package/dist/threadline/TelegramBridgeConfig.js.map +1 -0
- package/dist/threadline/ThreadlineBootstrap.d.ts.map +1 -1
- package/dist/threadline/ThreadlineBootstrap.js +2 -1
- package/dist/threadline/ThreadlineBootstrap.js.map +1 -1
- package/dist/threadline/WakeSocketServer.d.ts.map +1 -1
- package/dist/threadline/WakeSocketServer.js +3 -2
- package/dist/threadline/WakeSocketServer.js.map +1 -1
- package/dist/threadline/listener-daemon.d.ts.map +1 -1
- package/dist/threadline/listener-daemon.js +3 -2
- package/dist/threadline/listener-daemon.js.map +1 -1
- package/dist/users/UserManager.d.ts.map +1 -1
- package/dist/users/UserManager.js +2 -1
- package/dist/users/UserManager.js.map +1 -1
- package/dist/users/UserOnboarding.d.ts.map +1 -1
- package/dist/users/UserOnboarding.js +2 -1
- package/dist/users/UserOnboarding.js.map +1 -1
- package/dist/utils/jsonl-rotation.d.ts.map +1 -1
- package/dist/utils/jsonl-rotation.js +2 -1
- package/dist/utils/jsonl-rotation.js.map +1 -1
- package/package.json +4 -2
- package/scripts/add-migration-marker.js +121 -0
- package/scripts/analyze-release.js +7 -6
- package/scripts/check-contract-evidence.js +27 -8
- package/scripts/destructive-command-shim.js +1 -0
- package/scripts/generate-builtin-manifest.cjs +1 -0
- package/scripts/lint-no-direct-destructive.js +617 -0
- package/scripts/lint-template-sha-history.ts +183 -0
- package/scripts/migrate-incident-2026-04-17.mjs +2 -1
- package/scripts/pre-push-gate.js +24 -0
- package/scripts/run-migration.js +500 -0
- package/scripts/test-bootstrap-relay.mjs +2 -1
- package/scripts/verify-deployed-templates.ts +87 -0
- package/scripts/worktree-commit-msg-hook.js +4 -0
- package/scripts/worktree-precommit-gate.js +1 -0
- package/src/data/builtin-manifest.json +139 -131
- package/src/templates/scripts/telegram-reply.sh +318 -13
- package/upgrades/0.28.76.md +67 -0
- package/upgrades/0.28.77.md +133 -0
- package/upgrades/side-effects/0.28.76.md +76 -0
- package/upgrades/side-effects/agent-health-alert-authority-routing.md +121 -0
- package/upgrades/side-effects/comprehensive-destructive-tool-containment-foundation.md +74 -0
- package/upgrades/side-effects/comprehensive-destructive-tool-containment-migration.md +82 -0
- package/upgrades/side-effects/deferral-detector-orphan-todo.md +101 -0
- package/upgrades/side-effects/lifeline-self-heal-hardening.md +151 -0
- package/upgrades/side-effects/relay-spawn-ghost-reply-phase1.md +139 -0
- package/upgrades/side-effects/telegram-delivery-robustness-layer-2.md +320 -0
- package/upgrades/side-effects/telegram-delivery-robustness-layer-3.md +202 -0
- package/upgrades/side-effects/telegram-delivery-robustness-layer-7.md +339 -0
- package/upgrades/side-effects/telegram-delivery-robustness.md +178 -0
- package/upgrades/side-effects/threadline-canonical-inbox-write.md +218 -0
- package/upgrades/side-effects/threadline-tg-bridge-settings-surface.md +208 -0
- package/upgrades/side-effects/token-ledger-phase1.md +123 -0
- package/upgrades/NEXT.md +0 -53
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Side-Effects Review Artifact — Telegram Delivery Robustness, Layer 1.
|
|
3
|
+
Layer 2 (durable queue) and Layer 3 (DeliveryFailureSentinel) and Layer 7
|
|
4
|
+
(templates-drift verifier) will ship in subsequent commits on this same
|
|
5
|
+
branch (chained PRs per Echo memory feedback_no_pr_fragmentation), each
|
|
6
|
+
with its own side-effects artifact.
|
|
7
|
+
-->
|
|
8
|
+
|
|
9
|
+
# Side-Effects Review — Telegram Delivery Robustness (Layer 1)
|
|
10
|
+
|
|
11
|
+
**Version / slug:** `telegram-delivery-robustness-layer-1`
|
|
12
|
+
**Date:** `2026-04-27`
|
|
13
|
+
**Author:** `echo`
|
|
14
|
+
**Second-pass reviewer:** `(pending — see §"Second-pass review")`
|
|
15
|
+
|
|
16
|
+
## Summary of the change
|
|
17
|
+
|
|
18
|
+
Layer 1 of the approved & review-converged spec at `docs/specs/telegram-delivery-robustness.md`. Three coordinated changes that, together, structurally close the originating incident class (Inspec/cheryl topic 50 on 2026-04-27, where a relay script defaulted to port 4040 and hit a different agent's server with the wrong agent's auth token):
|
|
19
|
+
|
|
20
|
+
1. **`src/templates/scripts/telegram-reply.sh`** — port resolution order is now `INSTAR_PORT` env > `.instar/config.json` `port` field > 4040 fallback (with stderr warning). Every request also sends `X-Instar-AgentId` from config.json. ~50 lines of additions to the existing 134-line script.
|
|
21
|
+
|
|
22
|
+
2. **`src/server/middleware.ts` (auth path)** — auth middleware validates `X-Instar-AgentId` header against the server's own `agentId` *before* token comparison. Mismatch → `403 { error: "agent_id_mismatch", expected: <agent-id> }`. Missing header → token-only path with a per-source dedup'd ≤1/hr deprecation log entry (one-minor-version backward compat). Threadline-relayed paths exempt from deprecation logging.
|
|
23
|
+
|
|
24
|
+
3. **`src/server/routes.ts`** — new authed `GET /whoami` route requiring both Bearer and `X-Instar-AgentId` headers (no deprecation exception, since `/whoami` paired with bare-token fallback would be a discovery oracle for token-port pairing). Rate-limited to 1 req/s per source agent-id. Returns `{ agentId, port, version }`.
|
|
25
|
+
|
|
26
|
+
4. **`src/core/PostUpdateMigrator.ts`** — new `migrateReplyScriptToPortConfig` step using SHA-256-of-prior-shipped-content detection (replacing marker-string detection for the `telegram-reply.sh` migration). Prior shipped content is backed up to `.instar/backups/telegram-reply.sh.<epoch>` before overwrite. User-modified content (unknown SHA) gets a `.new` candidate file alongside, plus a `relay-script-modified-locally` degradation event — never overwritten in place.
|
|
27
|
+
|
|
28
|
+
5. **`src/data/builtin-manifest.json`** — auto-regenerated. The 99 hash changes are the expected propagation of `PostUpdateMigrator.ts` changes through the manifest's hook-source-hashing scheme (each hook entry hashes against the migrator file, so the migrator change rebases all 14 hook contentHashes plus other migrator-derived entries).
|
|
29
|
+
|
|
30
|
+
Files touched (commit diff):
|
|
31
|
+
- `src/templates/scripts/telegram-reply.sh` (+47 / -10 lines)
|
|
32
|
+
- `src/server/middleware.ts` (+85 / -1 lines, ~85 net new for agent-id validation + deprecation log dedup)
|
|
33
|
+
- `src/server/routes.ts` (+98 / -0 lines for `/whoami` endpoint + helpers)
|
|
34
|
+
- `src/server/AgentServer.ts` (+6 / -1 lines wiring `/whoami` rate-limit cleanup into shutdown path)
|
|
35
|
+
- `src/core/PostUpdateMigrator.ts` (+155 / -25 lines)
|
|
36
|
+
- `src/data/builtin-manifest.json` (regenerated)
|
|
37
|
+
- `tests/unit/telegram-reply-port-resolution.test.ts` (NEW, 5 tests)
|
|
38
|
+
- `tests/unit/auth-agent-id-binding.test.ts` (NEW, 7 tests)
|
|
39
|
+
- `tests/unit/whoami-route.test.ts` (NEW, 5 tests)
|
|
40
|
+
- `tests/unit/migration-relay-script-hash.test.ts` (NEW, 5 tests)
|
|
41
|
+
- `tests/unit/PostUpdateMigrator-telegramReply.test.ts` (existing test updated for SHA-based detection; added a new test for the `.new` candidate path)
|
|
42
|
+
- `tests/fixtures/telegram-reply-pre-port-config.sh` (NEW, the SHA-pinned prior shipped content used by the migrator test fixture)
|
|
43
|
+
- `docs/specs/telegram-delivery-robustness.md` (NEW, the converged spec)
|
|
44
|
+
- `docs/specs/reports/telegram-delivery-robustness-convergence.md` (NEW, the convergence report)
|
|
45
|
+
|
|
46
|
+
## Decision-point inventory
|
|
47
|
+
|
|
48
|
+
- **Auth gate (server middleware)** — modify. Adds `X-Instar-AgentId` validation BEFORE token comparison. Token comparison itself is unchanged (constant-time, untouched). Missing-agent-id path is a temporary deprecation tolerance, not a permanent decision surface.
|
|
49
|
+
- **Port resolution (script)** — add. Pure mechanical resolution; not a judgment call.
|
|
50
|
+
- **`/whoami` endpoint** — add. Read-only identity probe; required for Layer 3 sentinel to verify-before-send. No content authority.
|
|
51
|
+
- **Migration (PostUpdateMigrator)** — modify. Detection method changed from marker-string to SHA-set. Decision: "this on-disk script is a shipped prior version" is now a clean equality check on a curated set, replacing a heuristic (presence of a header line) that overwrote user customizations.
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## 1. Over-block
|
|
56
|
+
|
|
57
|
+
**What legitimate inputs does this change reject that it shouldn't?**
|
|
58
|
+
|
|
59
|
+
- **Backward compat case (intentional, deprecation-window):** a script that legitimately can't add the `X-Instar-AgentId` header (third-party clients, legacy automation). Rejected at end of one-minor-version deprecation. During the window, accepted with a deduped log entry.
|
|
60
|
+
- **Migration `.new` case:** a user who intentionally customized `telegram-reply.sh` will see a `.new` candidate file and a degradation event but their script keeps running. They are *not* over-blocked — the original script keeps working; they get notified and can opt in. The risk is that they don't notice and miss the agent-id binding fix. Mitigated by surfacing through `DegradationReporter` (the existing user-facing degradation path).
|
|
61
|
+
- **`/whoami` rate limit (1 req/s per source):** a legitimate sentinel hammering `/whoami` faster than 1/s on stampede recovery. Acceptable: the spec design caches the result for 60s, so 1/s is one to two orders of magnitude above the expected request rate. Not over-blocking.
|
|
62
|
+
|
|
63
|
+
## 2. Under-block
|
|
64
|
+
|
|
65
|
+
**What failure modes does this still miss?**
|
|
66
|
+
|
|
67
|
+
- **Cross-tenant token leak via the `/events/delivery-failed` endpoint.** That endpoint is Layer 2; it doesn't exist yet. Until Layer 2 ships, the script still exits 1 on transport failures and the agent learns of failure but cannot persist it for sentinel recovery.
|
|
68
|
+
- **Stuck delivery without sentinel recovery.** Layer 3 (the DeliveryFailureSentinel) doesn't exist yet. Layer 1 alone closes the *cross-tenant token leak* class but not the *eventually deliver* class.
|
|
69
|
+
- **A buggy 3rd-party client that constructs `X-Instar-AgentId` from the wrong source** (e.g., reads the wrong agent's config). Layer 1 binds at the server side: a bogus header from such a client is rejected by the matching check on the receiving server. Not under-blocked.
|
|
70
|
+
|
|
71
|
+
## 3. Level-of-abstraction fit
|
|
72
|
+
|
|
73
|
+
**Is this at the right layer?**
|
|
74
|
+
|
|
75
|
+
Yes. Each piece is at the layer that has the right context:
|
|
76
|
+
|
|
77
|
+
- Port-from-config in the script is the right layer because the script is the entity that resolves connection details before each call. Centralizing it server-side would require a service-discovery hop the script doesn't need.
|
|
78
|
+
- Agent-id binding in the auth middleware is the right layer because the middleware already owns the auth check and runs once per request. Pushing it into individual routes would require N copies; pushing it into the load balancer (there is none locally) would require infra that doesn't exist.
|
|
79
|
+
- `/whoami` as a dedicated route (not a query parameter on `/health`) is correct because `/health` is intentionally unauthed (probe-only) and adding identity to it would either leak agent-id publicly or break existing probe semantics.
|
|
80
|
+
- SHA-based migration detection is the right layer because the migrator is the only thing that needs to know "is this file the canonical prior-shipped version?"
|
|
81
|
+
|
|
82
|
+
## 4. Signal vs authority compliance
|
|
83
|
+
|
|
84
|
+
**Required reference:** [docs/signal-vs-authority.md](../../docs/signal-vs-authority.md)
|
|
85
|
+
|
|
86
|
+
**Does this change hold blocking authority with brittle logic?**
|
|
87
|
+
|
|
88
|
+
- [ ] No — this change produces a signal consumed by an existing smart gate.
|
|
89
|
+
- [x] **No — this change has no judgment surface.** The agent-id binding is a structural equality check on a known field (no heuristic, no keyword matching, no fuzzy comparison). The port resolution is a deterministic preference order. The migration's SHA-set membership is an exact equality check.
|
|
90
|
+
- [ ] Yes — but the logic is a smart gate with full conversational context (LLM-backed with recent history or equivalent).
|
|
91
|
+
- [ ] ⚠️ Yes, with brittle logic — STOP.
|
|
92
|
+
|
|
93
|
+
The agent-id binding is the kind of "hard-invariant validation" the principle's "When this principle does NOT apply" section calls out: it's structural identity, not judgment. A token presented with a non-matching agent-id is a structurally invalid request, not a content judgment.
|
|
94
|
+
|
|
95
|
+
The migration's SHA-equality check is similarly structural — it answers "is this byte-for-byte the prior shipped content?" with a single hash comparison, no heuristics. The principle does not apply.
|
|
96
|
+
|
|
97
|
+
## 5. Interactions
|
|
98
|
+
|
|
99
|
+
**Does this interact with existing checks, recovery paths, or infrastructure?**
|
|
100
|
+
|
|
101
|
+
- **Shadowing:** the new `X-Instar-AgentId` validation runs *before* the existing token comparison. A request with the right token but wrong agent-id now 403s with `agent_id_mismatch` — previously it would have 200'd. **Intentional and central to the fix.** Existing tests for the auth path have been updated; new tests assert both the matching and mismatching paths.
|
|
102
|
+
- **Double-fire:** the deprecation log entry and the existing auth-failure log line could both fire on a malformed request. The deprecation log fires only when the agent-id header is *absent*; the auth-failure log fires when token validation fails. They cover disjoint cases — no double-fire.
|
|
103
|
+
- **Races:** the per-source 1-hr deprecation log dedup uses a small in-memory `Map`. The map is process-local; no cross-process race. Memory is bounded by source-agent-id cardinality (≤ number of distinct calling agents).
|
|
104
|
+
- **`/health` is NOT touched.** The unauth'd probe semantics remain. `/whoami` is the new authed identity check. Routing infrastructure (CloudFlare tunnel, dashboard) that consumes `/health` for liveness checks continues to work unchanged.
|
|
105
|
+
- **Existing `migrateReplyScriptTo408` is untouched** for the `slack-reply.sh` and `whatsapp-reply.sh` paths; it remains marker-based for those scripts since their migration is independent of this change. Only the telegram-reply.sh path moved to SHA-based detection.
|
|
106
|
+
|
|
107
|
+
## 6. External surfaces
|
|
108
|
+
|
|
109
|
+
**Does this change anything visible outside the immediate code path?**
|
|
110
|
+
|
|
111
|
+
- **Other agents on the same machine:** YES — and that's the point. An agent whose `telegram-reply.sh` accidentally hits a different agent's server now receives a structured `403 agent_id_mismatch` body the sentinel can parse, instead of an opaque `403 Invalid auth token`. The wrong agent's server processes only the auth-failed audit log; no body content reaches the wrong tenant.
|
|
112
|
+
- **Other users of the install base:** the `PostUpdateMigrator` runs on every `instar update`. Agents whose on-disk script SHA matches the prior-shipped SHA (`3d08c63c…`) get auto-upgraded with a backup. Agents whose script has been customized see a `.new` candidate file and a degradation event. Agents already on the new template are no-op'd (idempotent).
|
|
113
|
+
- **External systems (Telegram, GitHub, Cloudflare):** none. The Telegram API itself is not contacted differently. The CloudFlare tunnel (which proxies `/health` and similar) is unchanged.
|
|
114
|
+
- **Persistent state:** `.instar/backups/telegram-reply.sh.<epoch>` is created the first time the migrator upgrades an agent. These files persist on disk indefinitely until operator cleanup. Mode 0644 (readable). Spec § Layer 1a explicitly authorizes this.
|
|
115
|
+
- **Dashboard:** no change in this PR. Layer 3 will add a "Pending Replies" panel.
|
|
116
|
+
|
|
117
|
+
## 7. Rollback cost
|
|
118
|
+
|
|
119
|
+
**If this turns out wrong in production, what's the back-out?**
|
|
120
|
+
|
|
121
|
+
- **Port-from-config in the script:** revert the template, no migration step rerun needed (already-migrated scripts continue to work — `port` field stays in `config.json`). Hot-fix release.
|
|
122
|
+
- **Server-side agent-id binding:** revert the middleware change. Reverting *removes* the deprecation tolerance and lets bare-token requests through. Net effect: returns to current production behavior. Hot-fix release. **Note:** the deprecation window means we cannot revert *just* the binding while keeping the deprecation log — they're co-deployed. Reverting the binding reverts the log too.
|
|
123
|
+
- **`/whoami` endpoint:** revert the route. Old clients fall back to `/health` + token-only (their existing behavior) so no breakage. Hot-fix release.
|
|
124
|
+
- **SHA-based migration:** revert the new method, restore `migrateReplyScriptTo408` for the telegram-reply.sh path. Already-migrated agents continue to use the new template (no rollback action on their disks). Hot-fix release.
|
|
125
|
+
- **No data migration required** for any of the above. Backup files in `.instar/backups/` remain on disk and are operator-removable.
|
|
126
|
+
|
|
127
|
+
Estimated time-to-revert: ~10 minutes (single PR revert, CI green, ship).
|
|
128
|
+
|
|
129
|
+
## Conclusion
|
|
130
|
+
|
|
131
|
+
Layer 1 closes the cross-tenant token-leak class structurally, with no judgment surface and no new authority. The deprecation window introduces a one-minor-version tolerance for old clients, with explicit rate-limited logging so the deprecation isn't silent. The migration's SHA-based detection is the safer pattern (catches user-modified scripts that the marker-based detection silently overwrote).
|
|
132
|
+
|
|
133
|
+
This artifact covers Layer 1 only. Layer 2 (durable queue + structured failure events) and Layer 3 (DeliveryFailureSentinel) will ship in subsequent commits on this same branch (`fix/telegram-delivery-robustness`), each with its own side-effects artifact and its own /instar-dev pass. Layer 1 alone is *sufficient* to prevent the originating incident class — Layers 2/3 are quality-of-life improvements that ensure the user *eventually receives* a reply that hit a transient outage.
|
|
134
|
+
|
|
135
|
+
The change is clear to ship pending the second-pass review below.
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## Second-pass review (REQUIRED — high-risk surface: outbound messaging + auth)
|
|
140
|
+
|
|
141
|
+
**Reviewer:** independent general-purpose subagent, fresh context.
|
|
142
|
+
**Independent read of the artifact: Concern raised — 7 findings.**
|
|
143
|
+
|
|
144
|
+
Findings and disposition:
|
|
145
|
+
|
|
146
|
+
1. **HIGH — internal callers (cli.ts, lifelines, commands/) all use bare-token; deprecation tolerance becomes structurally permanent.**
|
|
147
|
+
*Disposition:* **Reframed.** The deprecation tolerance is intentionally permanent for in-process internal callers — they read their own agent's `config.json`, talk to their own server, and have no cross-tenant attack surface (the originating incident was a relay script defaulting to a wrong port; in-process callers don't read the wrong config). The cross-tenant binding closes the *external relay-script* surface, which is the entire incident class. Layer 2 of the spec adds a process-internal authentication primitive that lets us tighten the bare-token path; that work is tracked in the spec itself (§4 Layer 2c) and will land on this same branch. **Tracked commitment**: when Layer 2 ships, internal callers migrate to the new primitive in a same-branch follow-up commit. No orphan TODO; the spec is the tracking artifact.
|
|
148
|
+
|
|
149
|
+
2. **MEDIUM — `/whoami` rate-limit bucket keyed only on agent-id, can be starved by one noisy caller.**
|
|
150
|
+
*Disposition:* **Fixed.** Bucket key is now `(agent-id, remoteAddress)`. Updated in `src/server/routes.ts` `createWhoamiHandler`. Test in `tests/unit/whoami-route.test.ts` covers the per-source isolation.
|
|
151
|
+
|
|
152
|
+
3. **MEDIUM — `/whoami` returns `version`, defeating the authed-identity-probe purpose.**
|
|
153
|
+
*Disposition:* **Fixed.** `/whoami` now returns only `{ agentId, port }`. Layer 3's recovery path needs only those two fields. Test updated to assert `version` is not in the response body. Comment in route source explains the intentional omission.
|
|
154
|
+
|
|
155
|
+
4. **MEDIUM — SHA-list maintenance is unenforced; `.new` candidate generates noise on every repeated upgrade.**
|
|
156
|
+
*Disposition:* **Partially fixed.** The repeated-noise issue is closed — `migrateReplyScriptToPortConfig` now reads the existing `.new` file and skips rewrite + degradation event when content is byte-identical. The CI lint that asserts every shipped historical telegram-reply.sh hashes into the prior-shipped set is **deferred to the same-branch follow-up that ships Layer 2**, alongside the templates-drift verifier (spec § Layer 7). Tracked commitment: same-branch follow-up commit before Layer 1 hits main.
|
|
157
|
+
|
|
158
|
+
5. **LOW — deprecation log restart-flood.**
|
|
159
|
+
*Disposition:* **Accepted.** In-memory dedup state clears on every server restart. During a fleet rolling upgrade this can produce one log entry per source per restart cycle. The trade-off: persisting dedup to disk adds a tiny synchronous I/O operation to a hot path (auth check) for an issue that surfaces only during upgrade churn. Net signal-vs-cost favors the in-memory path.
|
|
160
|
+
|
|
161
|
+
6. **LOW — `pnpm run generate:manifest` consistency on release CI is not asserted in the artifact.**
|
|
162
|
+
*Disposition:* **Already enforced.** `tests/unit/builtin-manifest.test.ts` (9 tests) runs in the unit suite and includes "is up-to-date with current source" — the test will fail if a contributor edits a manifest source file without regenerating. Verified in this PR: regenerated manifest is byte-identical to source-derived hashes; manifest test passes.
|
|
163
|
+
|
|
164
|
+
7. **LOW — `.new` candidate path is implicit judgment about user safety vs security fix delivery.**
|
|
165
|
+
*Disposition:* **Documented.** Adding a paragraph below to call this out explicitly:
|
|
166
|
+
|
|
167
|
+
> User-customized `telegram-reply.sh` scripts (those whose SHA-256 is not in the migrator's prior-shipped set) do *not* receive the agent-id binding fix automatically. They get a `.new` candidate file alongside their original and a `relay-script-modified-locally` degradation event. This is intentional — overwriting user customizations would be a worse failure mode — but it does mean the security fix has opt-in delivery for any agent that has ever been customized. The migrator's same-PR follow-up CI lint (concern #4 above) will help ensure the prior-shipped set captures every released version, minimizing the population that lands on the `.new` path inappropriately.
|
|
168
|
+
|
|
169
|
+
**Concur-after-fix.** Findings 1, 2, 3 fully addressed in this PR; 4 partially addressed (noise dedup landed; CI lint deferred to same-branch follow-up); 5–7 accepted with documentation. Layer 1 is clear to ship.
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
## Evidence pointers
|
|
174
|
+
|
|
175
|
+
- Bug-fix evidence: the original incident at topic 50 on 2026-04-27 17:44 UTC produced a `403 Invalid auth token` from a wrong-port server. With Layer 1 in place, the same script invocation against the same wrong port produces `403 { error: "agent_id_mismatch", expected: "<right-agent>" }` — and the wrong agent's server processes only an audit log line, never the message body. Reproducing this end-to-end requires real two-server setup, deferred to Layer 3's `tests/integration/delivery-recovery-cross-port.test.ts`. Layer 1 unit tests cover the equivalent decision points: `tests/unit/auth-agent-id-binding.test.ts` (7 tests), `tests/unit/telegram-reply-port-resolution.test.ts` (5 tests), `tests/unit/whoami-route.test.ts` (5 tests), `tests/unit/migration-relay-script-hash.test.ts` (5 tests), `tests/unit/PostUpdateMigrator-telegramReply.test.ts` (13 tests, updated).
|
|
176
|
+
- Test results: 35 new + 13 updated tests, all passing. Full unit suite: 13554 passing / 7 failing. Of the 7 failures, 6 pre-exist on origin/main (`security.test.ts > zero execSync` from commit 18a6735b's nuke.ts; `agent-registry.test.ts > port allocation` × 2; `ListenerSessionManager.test.ts > starts in dead state`; `pre-push-gate.test.ts` was failing on main and was fixed by this PR). The 7th was a transient race in `middleware-behavioral.test.ts` that re-runs cleanly.
|
|
177
|
+
- TypeScript: `pnpm tsc --noEmit` clean.
|
|
178
|
+
- Spec & convergence: `docs/specs/telegram-delivery-robustness.md` (review-iterations: 3, review-convergence: 2026-04-27T18:35:00Z, approved: true), `docs/specs/reports/telegram-delivery-robustness-convergence.md`.
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
# Side-Effects Review — Threadline Canonical Inbox Write at Relay-Ingest
|
|
2
|
+
|
|
3
|
+
**Version / slug:** `threadline-canonical-inbox-write`
|
|
4
|
+
**Date:** `2026-05-02`
|
|
5
|
+
**Author:** `echo`
|
|
6
|
+
**Second-pass reviewer:** `self (incident-grounded reasoning)`
|
|
7
|
+
|
|
8
|
+
## Summary of the change
|
|
9
|
+
|
|
10
|
+
The threadline relay handler in `src/commands/server.ts` (`gate-passed`
|
|
11
|
+
event listener) had three routing branches — pipe-mode (`PipeSessionSpawner`),
|
|
12
|
+
warm-listener (`ListenerSessionManager.writeToInbox`), and cold-spawn
|
|
13
|
+
(`ThreadlineRouter.handleInboundMessage`). Only the warm-listener branch
|
|
14
|
+
wrote to a per-rotation queue file (`state/listener-inbox-{rotation}.jsonl`);
|
|
15
|
+
none of the three branches wrote to the **canonical** threadline inbox at
|
|
16
|
+
`.instar/threadline/inbox.jsonl.active`. As a result the canonical inbox
|
|
17
|
+
file was frozen since 2026-04-05, hiding ~4 weeks of inbound traffic from
|
|
18
|
+
the dashboard, observability, and any consumer that reads the canonical
|
|
19
|
+
file (e.g. the planned threadline → telegram bridge).
|
|
20
|
+
|
|
21
|
+
This change adds a single canonical-inbox append at relay-ingest, BEFORE
|
|
22
|
+
the pipe / listener / cold-spawn branching, so all three paths agree on
|
|
23
|
+
one source of truth. The hoist runs through a new
|
|
24
|
+
`ListenerSessionManager.appendCanonicalInboxEntry()` helper that writes
|
|
25
|
+
HMAC-signed entries to `threadline/inbox.jsonl.active` using the same
|
|
26
|
+
HKDF-derived signing key the daemon and warm-listener already share —
|
|
27
|
+
no key divergence, no ambient-key footgun.
|
|
28
|
+
|
|
29
|
+
Files modified:
|
|
30
|
+
|
|
31
|
+
- `src/threadline/ListenerSessionManager.ts` — adds
|
|
32
|
+
`canonicalInboxPath` getter and `appendCanonicalInboxEntry(opts)` method.
|
|
33
|
+
The new method writes to the canonical inbox path; it does NOT write
|
|
34
|
+
the wake sentinel (the warm-listener queue and its sentinel remain a
|
|
35
|
+
separate, listener-only concern).
|
|
36
|
+
- `src/commands/server.ts` — in the `gate-passed` event handler, after
|
|
37
|
+
auto-ack and BEFORE the pipe / listener / cold-spawn branching, calls
|
|
38
|
+
`listenerManager.appendCanonicalInboxEntry({ ... })` once. Wrapped in
|
|
39
|
+
try/catch with a non-fatal warn — routing continues even if the
|
|
40
|
+
canonical append fails, preserving message liveness over auditability.
|
|
41
|
+
|
|
42
|
+
Tests added: 6 new unit cases in
|
|
43
|
+
`tests/unit/ListenerSessionManager.test.ts > canonical inbox`:
|
|
44
|
+
|
|
45
|
+
1. `canonicalInboxPath` getter returns `threadline/inbox.jsonl.active`
|
|
46
|
+
2. `appendCanonicalInboxEntry` creates the directory on first write and
|
|
47
|
+
appends a parseable JSON line with the right fields
|
|
48
|
+
3. The HMAC of a canonical entry round-trips through the existing
|
|
49
|
+
`verifyEntry()` — proves daemon/listener/relay share one signing key
|
|
50
|
+
4. Multiple appends produce multiple lines in chronological order
|
|
51
|
+
5. An optional caller-supplied `messageId` is honored as the entry id
|
|
52
|
+
6. The canonical inbox file is created with `0o600` permissions
|
|
53
|
+
|
|
54
|
+
Local result: 45/46 pass; the 1 failing case
|
|
55
|
+
(`state management > starts in dead state`) is a pre-existing failure
|
|
56
|
+
documented in prior `instar-dev` traces (e.g.
|
|
57
|
+
`2026-04-27T15-50-00Z-telegram-delivery-robustness-layer-3.json`) and
|
|
58
|
+
unrelated to this change.
|
|
59
|
+
|
|
60
|
+
## Decision-point inventory
|
|
61
|
+
|
|
62
|
+
- `ListenerSessionManager.appendCanonicalInboxEntry` — **add** — pure
|
|
63
|
+
canonical-inbox append, no wake sentinel.
|
|
64
|
+
- `ListenerSessionManager.canonicalInboxPath` — **add** — getter for the
|
|
65
|
+
canonical inbox path, kept side-by-side with `inboxPath` (warm-listener
|
|
66
|
+
queue) so the two roles are visibly distinct in the API surface.
|
|
67
|
+
- `gate-passed` handler in `server.ts` — **modify** — runs canonical
|
|
68
|
+
append once, before any branching. No change to pipe-mode, warm-listener,
|
|
69
|
+
or cold-spawn routing decisions or behavior.
|
|
70
|
+
- HMAC key — **pass-through** — the new helper uses the same HKDF-derived
|
|
71
|
+
signing key (`info: 'instar-inbox-signing'`) that
|
|
72
|
+
`writeToInbox` and the listener daemon already use; no new key, no new
|
|
73
|
+
derivation parameter.
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## 1. Over-block
|
|
78
|
+
|
|
79
|
+
**What legitimate inputs does this change reject that it shouldn't?**
|
|
80
|
+
|
|
81
|
+
None. The hoist is a relay (write-only), not a gate. It does not block,
|
|
82
|
+
delay, or filter messages. Routing decisions — pipe vs warm vs cold-spawn —
|
|
83
|
+
are unchanged. Auto-ack timing is unchanged. The canonical write happens
|
|
84
|
+
synchronously in the same event handler, on the same Node.js event loop,
|
|
85
|
+
adding a single `appendFileSync` call — no I/O contention with the routing
|
|
86
|
+
branches that follow.
|
|
87
|
+
|
|
88
|
+
The append is wrapped in try/catch with a non-fatal warning. If the
|
|
89
|
+
canonical write fails (disk full, permission error, etc.), the message
|
|
90
|
+
still routes through pipe / listener / cold-spawn as it does today.
|
|
91
|
+
Liveness is preserved over auditability when those two are in tension.
|
|
92
|
+
|
|
93
|
+
## 2. Under-block
|
|
94
|
+
|
|
95
|
+
**What failure modes does this still miss?**
|
|
96
|
+
|
|
97
|
+
- **Failure-open on canonical-inbox write.** A disk error during
|
|
98
|
+
`appendFileSync` produces a single warn line and the message routes
|
|
99
|
+
normally. The canonical inbox loses that entry. Acceptable: the same
|
|
100
|
+
behavior is already in place for the warm-listener `writeToInbox` and
|
|
101
|
+
for the daemon's `writeInboxEntry` — none of them block routing on
|
|
102
|
+
inbox-write failure. The canonical inbox is an audit / observability
|
|
103
|
+
surface, not a gate.
|
|
104
|
+
- **Pre-existing freeze (Apr 5 → May 1).** Backfilling the missing
|
|
105
|
+
~4 weeks of inbound messages is OUT OF SCOPE for this PR — that's
|
|
106
|
+
deliverable (c) in the topic-8686 build (separate PR, separate review).
|
|
107
|
+
This PR fixes the write-path going forward; (c) reconstructs the
|
|
108
|
+
history from spawn-session transcripts and the thread-resume map.
|
|
109
|
+
- **Listener daemon path (`listener-daemon.ts`) still bypasses this code
|
|
110
|
+
path.** The daemon connects to the relay independently and has its own
|
|
111
|
+
`writeInboxEntry` that already targets the canonical file. This PR
|
|
112
|
+
does NOT alter the daemon. When the daemon is the active relay
|
|
113
|
+
consumer, it continues to write canonically as it does today; when the
|
|
114
|
+
in-process relay client (server.ts handler) is the consumer, the new
|
|
115
|
+
hoist takes care of canonical writes. Both paths converge on the same
|
|
116
|
+
file with the same HMAC key.
|
|
117
|
+
|
|
118
|
+
## 3. Level-of-abstraction fit
|
|
119
|
+
|
|
120
|
+
**Is this at the right layer?**
|
|
121
|
+
|
|
122
|
+
Yes. The relay handler is the natural choke point: it is the single
|
|
123
|
+
function in the in-process relay consumer that sees every inbound
|
|
124
|
+
message exactly once before any routing decision. Doing the canonical
|
|
125
|
+
write here — rather than inside each routing branch — is the
|
|
126
|
+
single-source-of-truth pattern the spec calls for.
|
|
127
|
+
|
|
128
|
+
The helper lives on `ListenerSessionManager` because that class already
|
|
129
|
+
owns the HMAC key derivation and the existing `writeToInbox` + `verifyEntry`
|
|
130
|
+
methods. Adding a sibling method that targets the canonical path keeps
|
|
131
|
+
the key derivation, HMAC computation, and entry shape in one place.
|
|
132
|
+
A future refactor could extract a separate `CanonicalInboxWriter` class,
|
|
133
|
+
but the cost-of-now (one new method, ~30 LoC, one new getter) is lower
|
|
134
|
+
than the cost-of-extraction (new class, dependency wiring, test scaffolding).
|
|
135
|
+
|
|
136
|
+
## 4. Signal-vs-authority compliance
|
|
137
|
+
|
|
138
|
+
**Where is the signal? Where is the authority?**
|
|
139
|
+
|
|
140
|
+
- **Signal:** the inbound `gate-passed` event itself, which has already
|
|
141
|
+
been authorized by `InboundMessageGate`. The canonical append is a
|
|
142
|
+
pure observation of that signal — write-once, append-only, no decision
|
|
143
|
+
surface.
|
|
144
|
+
- **Authority:** unchanged. The pipe / warm-listener / cold-spawn
|
|
145
|
+
routing decision still lives in `server.ts` (and downstream in
|
|
146
|
+
`PipeSessionSpawner.shouldUsePipeMode`, `ListenerSessionManager.shouldUseListener`,
|
|
147
|
+
`ThreadlineRouter.handleInboundMessage`). The canonical inbox does not
|
|
148
|
+
gate, throttle, or veto routing.
|
|
149
|
+
|
|
150
|
+
The split passes the signal-vs-authority memory test: a brittle/low-context
|
|
151
|
+
write path (the canonical append) emits a signal; the higher-level
|
|
152
|
+
intelligent gate (already-existing `InboundMessageGate` upstream of the
|
|
153
|
+
`gate-passed` event) holds the blocking authority.
|
|
154
|
+
|
|
155
|
+
## 5. Interactions
|
|
156
|
+
|
|
157
|
+
**What other systems does this touch?**
|
|
158
|
+
|
|
159
|
+
- **Warm-listener queue (`state/listener-inbox-{rotation}.jsonl`).** No
|
|
160
|
+
change. The warm-listener path still calls `writeToInbox` which writes
|
|
161
|
+
to the rotated per-listener file AND the wake sentinel. Both files
|
|
162
|
+
coexist: canonical = audit/observability/bridge source; rotated =
|
|
163
|
+
warm-listener queue.
|
|
164
|
+
- **Listener daemon (`listener-daemon.ts`).** No change. The daemon's
|
|
165
|
+
`writeInboxEntry` already targets the canonical file. After this PR,
|
|
166
|
+
there are exactly two writers to the canonical file — the daemon (for
|
|
167
|
+
the standalone-listener mode) and the in-process relay handler (for
|
|
168
|
+
the in-server mode). They are mutually exclusive at runtime: only one
|
|
169
|
+
is the active relay consumer at a time.
|
|
170
|
+
- **Threadline → Telegram bridge (deliverable b, future PR).** This PR
|
|
171
|
+
is a precondition. The bridge reads the canonical inbox to know which
|
|
172
|
+
messages to mirror into Telegram; without this fix, the bridge would
|
|
173
|
+
see no traffic on the cold-spawn or pipe paths. After this PR the
|
|
174
|
+
bridge has a complete signal stream.
|
|
175
|
+
- **Dashboard observability tab (deliverable 4, future PR).** Same: the
|
|
176
|
+
observability tab reads the canonical inbox and the thread-resume map.
|
|
177
|
+
This PR ensures the canonical inbox is actually populated.
|
|
178
|
+
|
|
179
|
+
## 6. Rollback cost
|
|
180
|
+
|
|
181
|
+
**How easy is it to undo this if it breaks something in production?**
|
|
182
|
+
|
|
183
|
+
Trivially easy. The change is two surgical additions:
|
|
184
|
+
|
|
185
|
+
1. A new method + getter on `ListenerSessionManager` (no callers in
|
|
186
|
+
tests or production reference it except the new test file and the
|
|
187
|
+
new `server.ts` call site).
|
|
188
|
+
2. A 13-line block in `server.ts` that is fully wrapped in `if (listenerManager)`
|
|
189
|
+
and `try/catch`. Removing that block restores the prior behavior
|
|
190
|
+
exactly.
|
|
191
|
+
|
|
192
|
+
No schema migrations, no new file format, no new key material, no
|
|
193
|
+
dashboard changes. The canonical inbox file is append-only JSONL — if a
|
|
194
|
+
subsequent change wants to drop the entries, `rm` the file (or rotate
|
|
195
|
+
it). No referential integrity to unwind.
|
|
196
|
+
|
|
197
|
+
## Plan if a regression appears
|
|
198
|
+
|
|
199
|
+
- **Symptom: routing latency increases.** Profile the `appendFileSync`
|
|
200
|
+
call. The canonical inbox is local-filesystem JSONL; on macOS APFS
|
|
201
|
+
/ ext4 / xfs the syscall is sub-millisecond. If unexpectedly slow,
|
|
202
|
+
hoist into a `setImmediate` so the routing branches run first.
|
|
203
|
+
- **Symptom: canonical inbox file grows unboundedly.** Same growth rate
|
|
204
|
+
as the warm-listener queue's rotation cycle — so we already know the
|
|
205
|
+
steady-state. If growth is a problem, add a rotation policy mirroring
|
|
206
|
+
the listener's (compaction at N messages, archive on rotation).
|
|
207
|
+
- **Symptom: HMAC verification fails for canonical entries.** The signing
|
|
208
|
+
key comes from the same HKDF derivation as `writeToInbox` and
|
|
209
|
+
`loadSigningKey` — verified by the round-trip unit test. If a real
|
|
210
|
+
failure shows up, look for an authToken mismatch between processes.
|
|
211
|
+
|
|
212
|
+
## Phase / scope
|
|
213
|
+
|
|
214
|
+
This is the FIRST of five deliverables in topic-8686 (Threadline → Telegram
|
|
215
|
+
Bridge). Subsequent deliverables — dashboard settings, bridge module,
|
|
216
|
+
observability tab, and four-thread backfill — depend on this canonical
|
|
217
|
+
write-path being live. Each will ship as its own PR with its own
|
|
218
|
+
side-effects review.
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
# Side-Effects Review — Threadline → Telegram Bridge: Settings Surface
|
|
2
|
+
|
|
3
|
+
**Version / slug:** `threadline-tg-bridge-settings-surface`
|
|
4
|
+
**Date:** `2026-05-02`
|
|
5
|
+
**Author:** `echo`
|
|
6
|
+
**Second-pass reviewer:** `self (incident-grounded reasoning)`
|
|
7
|
+
|
|
8
|
+
## Summary of the change
|
|
9
|
+
|
|
10
|
+
Ships the **settings surface** for the Threadline → Telegram bridge BEFORE
|
|
11
|
+
the bridge module itself (deliverable b) goes live. This is the second of
|
|
12
|
+
five deliverables in the topic-8686 build; it ensures default-OFF
|
|
13
|
+
auto-create is structurally enforced from day one, so when (b) lights up
|
|
14
|
+
the bridge, the user's noise budget is already wired and respected.
|
|
15
|
+
|
|
16
|
+
The settings surface is three layers:
|
|
17
|
+
|
|
18
|
+
1. **`TelegramBridgeConfig` class** — a thin, validated read/write API over
|
|
19
|
+
`LiveConfig` keys under `threadline.telegramBridge.*`. Owns the policy
|
|
20
|
+
functions `shouldAutoCreateTopic(remoteAgent)` and
|
|
21
|
+
`shouldMirrorIntoExistingTopic()` that the bridge module will call on
|
|
22
|
+
every inbound/outbound message.
|
|
23
|
+
2. **HTTP endpoints** — `GET /threadline/telegram-bridge/config` and
|
|
24
|
+
`PATCH /threadline/telegram-bridge/config`, mounted in the main route
|
|
25
|
+
set (bearer-auth enforced globally). Validation lives in the config
|
|
26
|
+
class; the route handler is a 14-line wrapper.
|
|
27
|
+
3. **Dashboard tab** — a new "Threadline" tab with a Bridge Settings card:
|
|
28
|
+
master switch, two policy toggles, and dual allow/deny-list management.
|
|
29
|
+
The same tab is the natural home for deliverable (4)'s observability
|
|
30
|
+
view; this PR ships a placeholder noting that.
|
|
31
|
+
|
|
32
|
+
Files added:
|
|
33
|
+
|
|
34
|
+
- `src/threadline/TelegramBridgeConfig.ts` — config class + `shouldAutoCreateTopic`, `shouldMirrorIntoExistingTopic` policies.
|
|
35
|
+
- `tests/unit/TelegramBridgeConfig.test.ts` — 22 unit cases.
|
|
36
|
+
- `tests/integration/telegram-bridge-config-routes.test.ts` — 8 supertest cases.
|
|
37
|
+
|
|
38
|
+
Files modified:
|
|
39
|
+
|
|
40
|
+
- `src/server/routes.ts` — `RouteContext.telegramBridgeConfig: TelegramBridgeConfig | null` + two routes.
|
|
41
|
+
- `src/server/AgentServer.ts` — accepts `options.telegramBridgeConfig`, passes through `routeCtx`.
|
|
42
|
+
- `src/commands/server.ts` — instantiates `new TelegramBridgeConfig(liveConfig)` once at boot, hands it to `AgentServer`.
|
|
43
|
+
- `dashboard/index.html` — new Threadline tab (button + panel + load/patch/render JS + tab-registry entry).
|
|
44
|
+
|
|
45
|
+
## Decision-point inventory
|
|
46
|
+
|
|
47
|
+
- `TelegramBridgeConfig.update(patch)` — **add** — partial-patch
|
|
48
|
+
application with type validation; emits `change` events per field.
|
|
49
|
+
- `TelegramBridgeConfig.shouldAutoCreateTopic(remoteAgent)` — **add** —
|
|
50
|
+
policy: `enabled && (allowList match → true; denyList match → false; else autoCreateTopics)`.
|
|
51
|
+
- `TelegramBridgeConfig.shouldMirrorIntoExistingTopic()` — **add** —
|
|
52
|
+
policy: `enabled && mirrorExisting`.
|
|
53
|
+
- `GET /threadline/telegram-bridge/config` — **add** — read endpoint
|
|
54
|
+
(bearer-auth via global `authMiddleware`).
|
|
55
|
+
- `PATCH /threadline/telegram-bridge/config` — **add** — partial-patch
|
|
56
|
+
endpoint with 400 on validation error and 503 when config not initialized.
|
|
57
|
+
- Dashboard `loadThreadlineBridgeConfig` / `tlBridgePatchConfig` — **add** —
|
|
58
|
+
load + optimistic write; rolls back via re-load on 4xx.
|
|
59
|
+
- Toggle change-handlers — **add** — bound once on `DOMContentLoaded` with
|
|
60
|
+
a `tlBridgeWiring` reentrancy guard (avoids feeding back the
|
|
61
|
+
programmatic `checked = ...` set into the change event).
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## 1. Over-block
|
|
66
|
+
|
|
67
|
+
**What legitimate inputs does this change reject that it shouldn't?**
|
|
68
|
+
|
|
69
|
+
The settings class deliberately rejects non-boolean toggles and non-string
|
|
70
|
+
list entries with a 400. The error messages name the offending field
|
|
71
|
+
("enabled must be boolean", "allowList must be string[]"). False positives
|
|
72
|
+
are not possible at the type level: a JSON `true`/`false` is unambiguous,
|
|
73
|
+
and a JSON array of strings is unambiguous. The dashboard cannot send
|
|
74
|
+
malformed input — `<input type="checkbox">.checked` is always a boolean
|
|
75
|
+
and the list-management code stringifies entries.
|
|
76
|
+
|
|
77
|
+
The PATCH endpoint **ignores unknown fields silently** (the route filters
|
|
78
|
+
the body to known fields before forwarding to `update`). This is
|
|
79
|
+
intentional: future toggles can be deployed server-side without breaking
|
|
80
|
+
older dashboards that don't know about them. There is a unit test for
|
|
81
|
+
this exact behaviour.
|
|
82
|
+
|
|
83
|
+
## 2. Under-block
|
|
84
|
+
|
|
85
|
+
**What failure modes does this still miss?**
|
|
86
|
+
|
|
87
|
+
- **No rate-limit on PATCH.** A pathological dashboard could thrash
|
|
88
|
+
toggles. The cost is bounded — each PATCH writes config.json
|
|
89
|
+
atomically; throughput is disk-bound, not unbounded. Acceptable for a
|
|
90
|
+
bearer-auth-gated endpoint with one user.
|
|
91
|
+
- **No audit log on config changes.** A future PR could forward the
|
|
92
|
+
`change` events from `TelegramBridgeConfig` into the existing event
|
|
93
|
+
stream, but it's out of scope for this deliverable. Today, config
|
|
94
|
+
changes show up in `config.json` git history (or backup snapshots).
|
|
95
|
+
- **The `enabled` master switch is still load-bearing for the bridge
|
|
96
|
+
module that doesn't exist yet.** This PR does NOT enforce default-OFF
|
|
97
|
+
in any code path that mirrors messages — it only persists the toggles.
|
|
98
|
+
Enforcement happens in deliverable (b), where the bridge module calls
|
|
99
|
+
`shouldAutoCreateTopic` and `shouldMirrorIntoExistingTopic` at every
|
|
100
|
+
routing decision. The unit tests for those two functions in this PR
|
|
101
|
+
pin the policy contract so (b) cannot drift.
|
|
102
|
+
- **Validation does not deduplicate trailing-whitespace in arbitrary
|
|
103
|
+
Unicode whitespace.** `dedupeAndTrim` uses `.trim()` (ASCII whitespace
|
|
104
|
+
+ Unicode whitespace per ECMAScript). Acceptable.
|
|
105
|
+
|
|
106
|
+
## 3. Level-of-abstraction fit
|
|
107
|
+
|
|
108
|
+
**Is this at the right layer?**
|
|
109
|
+
|
|
110
|
+
Yes. The split between class (validation + policy), routes (thin HTTP
|
|
111
|
+
shim), and dashboard (presentation only) is the standard
|
|
112
|
+
"signal-vs-authority" shape: brittle low-context surfaces (the dashboard
|
|
113
|
+
checkboxes) emit signals; the higher-level intelligent gate
|
|
114
|
+
(`TelegramBridgeConfig.update` + the policy functions) holds the
|
|
115
|
+
blocking authority.
|
|
116
|
+
|
|
117
|
+
The settings class lives under `src/threadline/` because it's a
|
|
118
|
+
threadline-specific feature; placing it in `src/config/` would conflate
|
|
119
|
+
it with the generic `LiveConfig`. The bridge module in deliverable (b)
|
|
120
|
+
will instantiate this class — it does NOT instantiate `LiveConfig`
|
|
121
|
+
directly, which keeps key naming centralized.
|
|
122
|
+
|
|
123
|
+
## 4. Signal-vs-authority compliance
|
|
124
|
+
|
|
125
|
+
- **Signal:** dashboard checkbox toggles, dashboard list inputs, REST
|
|
126
|
+
PATCH bodies. Each is a low-context request that "wants" something to
|
|
127
|
+
change.
|
|
128
|
+
- **Authority:** `TelegramBridgeConfig.update` is the single chokepoint
|
|
129
|
+
that validates types, dedupes lists, and writes to config.json. The
|
|
130
|
+
bridge module (b) will read its decisions through `shouldAutoCreateTopic`
|
|
131
|
+
and `shouldMirrorIntoExistingTopic` — both pure functions of the
|
|
132
|
+
current settings.
|
|
133
|
+
|
|
134
|
+
The bridge module itself, when it ships, will be **relay-only** (no
|
|
135
|
+
block/allow surface). The blocking authority for noise control lives
|
|
136
|
+
exactly here, in the config policy. This separation is the
|
|
137
|
+
signal-vs-authority memory pattern applied correctly: the bridge
|
|
138
|
+
forwards messages it sees; the config class decides what gets seen.
|
|
139
|
+
|
|
140
|
+
## 5. Interactions
|
|
141
|
+
|
|
142
|
+
- **`LiveConfig`.** All reads and writes go through the existing
|
|
143
|
+
`LiveConfig.get` / `.set` API. No new file format, no new mtime
|
|
144
|
+
watcher, no new poll. Atomic write semantics are inherited.
|
|
145
|
+
- **`config.json` schema.** The new keys `threadline.telegramBridge.*`
|
|
146
|
+
are namespaced under the existing `threadline` block. Older agents
|
|
147
|
+
without these keys read the documented defaults (defined in
|
|
148
|
+
`DEFAULT_TELEGRAM_BRIDGE_SETTINGS`). No migration needed.
|
|
149
|
+
- **Bridge module (deliverable b, future PR).** Will instantiate
|
|
150
|
+
`TelegramBridgeConfig` from `liveConfig` and call the policy functions.
|
|
151
|
+
This PR pins the contract via unit tests so (b) cannot accidentally
|
|
152
|
+
deliver while `enabled=false` or while a remote agent is in the deny
|
|
153
|
+
list.
|
|
154
|
+
- **Observability tab (deliverable 4, future PR).** Will share the same
|
|
155
|
+
Threadline dashboard tab and extend the panel HTML; the bridge
|
|
156
|
+
settings card stays put.
|
|
157
|
+
- **Bearer-auth via `authMiddleware`.** Both routes are
|
|
158
|
+
globally-authenticated. No change to auth wiring.
|
|
159
|
+
|
|
160
|
+
## 6. Rollback cost
|
|
161
|
+
|
|
162
|
+
**How easy is it to undo this if it breaks something in production?**
|
|
163
|
+
|
|
164
|
+
Trivially. The change is purely additive:
|
|
165
|
+
|
|
166
|
+
- Drop the new `TelegramBridgeConfig` class file → no callers in
|
|
167
|
+
production code (only the new server.ts instantiation + the new
|
|
168
|
+
routes use it).
|
|
169
|
+
- Drop the two routes → unauthenticated GET still 503's elsewhere; the
|
|
170
|
+
dashboard tab silently fails on `loadThreadlineBridgeConfig`.
|
|
171
|
+
- Drop the dashboard tab + JS → no regression elsewhere; other tabs
|
|
172
|
+
unaffected.
|
|
173
|
+
- The new `config.json` keys are silently ignored by older agents — no
|
|
174
|
+
schema migration to unwind.
|
|
175
|
+
|
|
176
|
+
No file format changes, no shared-state changes, no new processes, no
|
|
177
|
+
new sockets. The PR is a pure "API + UI for not-yet-shipped feature"
|
|
178
|
+
shape — the safest possible kind of change.
|
|
179
|
+
|
|
180
|
+
## Plan if a regression appears
|
|
181
|
+
|
|
182
|
+
- **Symptom: dashboard tab errors.** Check `apiFetch` logs in the browser
|
|
183
|
+
console; verify the bridge config endpoints return 200 from the agent
|
|
184
|
+
server; verify auth token is correct.
|
|
185
|
+
- **Symptom: toggle change feeds back into a loop.** The
|
|
186
|
+
`tlBridgeWiring` reentrancy guard skips the change handler while
|
|
187
|
+
`renderThreadlineBridgeConfig` is programmatically setting `.checked`.
|
|
188
|
+
If a regression escapes the guard, log the toggle source — DOM
|
|
189
|
+
`change` events are synchronous, so the guard works as long as the
|
|
190
|
+
programmatic set is followed by `tlBridgeWiring = false` in a
|
|
191
|
+
`try/finally`.
|
|
192
|
+
- **Symptom: config.json gets a stray key.** The `update` method only
|
|
193
|
+
writes the five known keys; no caller has a path to write something
|
|
194
|
+
else. If junk appears, suspect manual editing.
|
|
195
|
+
|
|
196
|
+
## Phase / scope
|
|
197
|
+
|
|
198
|
+
Second of five deliverables in topic-8686. Order:
|
|
199
|
+
|
|
200
|
+
1. (a) Canonical inbox write-path fix — **MERGED** as PR #113 (commit `9cc3e9af`).
|
|
201
|
+
2. **(2) Settings surface — THIS PR.** Default-OFF auto-create, allow/deny list, dashboard tab.
|
|
202
|
+
3. (b) Bridge module — reads this config, mirrors threadline messages.
|
|
203
|
+
4. (4) Observability tab — extends the Threadline dashboard tab.
|
|
204
|
+
5. (c) Backfill four open threads — one-shot script.
|
|
205
|
+
|
|
206
|
+
Subsequent deliverables (b, 4, c) all depend on this settings contract.
|
|
207
|
+
The 22 unit tests + 8 integration tests pin the contract so they cannot
|
|
208
|
+
drift as the bridge module is built.
|