@openlife/cli 1.7.3
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/INSTALL.md +266 -0
- package/LICENSE +21 -0
- package/README.md +142 -0
- package/bin/openlife.js +3 -0
- package/dist/admin_panel_server.js +66 -0
- package/dist/cli/AgentManager.js +109 -0
- package/dist/cli/AutonomousInstaller.js +134 -0
- package/dist/cli/DreamOrganizer.js +88 -0
- package/dist/cli/HostInstaller.js +426 -0
- package/dist/cli/InstallBanner.js +16 -0
- package/dist/cli/InstallFlow.js +256 -0
- package/dist/cli/InstallHeadless.js +47 -0
- package/dist/cli/InstallModules.js +148 -0
- package/dist/cli/InstallStateStore.js +75 -0
- package/dist/cli/InstallWizard.js +364 -0
- package/dist/cli/ProfileManager.js +163 -0
- package/dist/cli/SystemInstaller.js +89 -0
- package/dist/cli/WorldClassCommands.js +208 -0
- package/dist/design/DesignMdImporter.js +82 -0
- package/dist/design/DesignMdMode.js +93 -0
- package/dist/design/DesignMdRegistry.js +67 -0
- package/dist/index.js +2575 -0
- package/dist/memory/ConversationMemory.js +33 -0
- package/dist/memory/LocalMemoryProvider.js +86 -0
- package/dist/memory/Mem0Provider.js +16 -0
- package/dist/memory/MemoryNamespacePolicy.js +27 -0
- package/dist/memory/MemoryOrchestrator.js +65 -0
- package/dist/memory/MemoryPromotionFlow.js +32 -0
- package/dist/memory/MemoryProvider.js +2 -0
- package/dist/memory/MemoryProviderRegistry.js +27 -0
- package/dist/memory/MemoryRetentionPolicy.js +60 -0
- package/dist/memory/MempalaceProvider.js +72 -0
- package/dist/memory/OmniMemory.js +106 -0
- package/dist/memory/RedisAgentMemoryProvider.js +16 -0
- package/dist/memory/SessionManager.js +86 -0
- package/dist/memory/ZepGraphitiProvider.js +16 -0
- package/dist/orchestrator/AgentRegistry.js +56 -0
- package/dist/orchestrator/AgentScoring.js +82 -0
- package/dist/orchestrator/AgentTeam.js +22 -0
- package/dist/orchestrator/ArbitrationAgent.js +43 -0
- package/dist/orchestrator/ArbitrationScorecard.js +17 -0
- package/dist/orchestrator/AssetPromotionEngine.js +65 -0
- package/dist/orchestrator/AssetReuseRouter.js +63 -0
- package/dist/orchestrator/BenchmarkEngine.js +75 -0
- package/dist/orchestrator/Brain.js +298 -0
- package/dist/orchestrator/CadenceEngine.js +76 -0
- package/dist/orchestrator/CapabilityRouter.js +36 -0
- package/dist/orchestrator/CommandLanguage.js +27 -0
- package/dist/orchestrator/CommandRouter.js +70 -0
- package/dist/orchestrator/ConsequenceForecaster.js +286 -0
- package/dist/orchestrator/CronManager.js +286 -0
- package/dist/orchestrator/DynamicAgentBuilder.js +48 -0
- package/dist/orchestrator/DynamicAgentExecutor.js +15 -0
- package/dist/orchestrator/EnterpriseAgenticCore.js +276 -0
- package/dist/orchestrator/ExecutionBoard.js +86 -0
- package/dist/orchestrator/ExecutionIntent.js +13 -0
- package/dist/orchestrator/ExecutionModePolicy.js +48 -0
- package/dist/orchestrator/ExecutionRouter.js +9 -0
- package/dist/orchestrator/ExecutionState.js +20 -0
- package/dist/orchestrator/ExecutorHealth.js +86 -0
- package/dist/orchestrator/ExternalCatalogRegistry.js +83 -0
- package/dist/orchestrator/Gatekeeper.js +414 -0
- package/dist/orchestrator/Gateway.js +508 -0
- package/dist/orchestrator/GovernanceConsentStore.js +66 -0
- package/dist/orchestrator/GovernanceLayer.js +179 -0
- package/dist/orchestrator/GovernancePolicyStore.js +53 -0
- package/dist/orchestrator/GovernanceScopeLedger.js +134 -0
- package/dist/orchestrator/GovernanceScopePolicy.js +67 -0
- package/dist/orchestrator/IntentClassifier.js +45 -0
- package/dist/orchestrator/JobLifecycle.js +91 -0
- package/dist/orchestrator/LearningRouter.js +24 -0
- package/dist/orchestrator/MediaManager.js +92 -0
- package/dist/orchestrator/MemoryCuratorAgent.js +41 -0
- package/dist/orchestrator/MissionState.js +155 -0
- package/dist/orchestrator/ModelManager.js +84 -0
- package/dist/orchestrator/OperatingSystem.js +71 -0
- package/dist/orchestrator/OperationalMemoryStore.js +94 -0
- package/dist/orchestrator/OptimizationLoop.js +72 -0
- package/dist/orchestrator/OrchestrationLoop.js +905 -0
- package/dist/orchestrator/OrgStructure.js +88 -0
- package/dist/orchestrator/OutcomeSimulator.js +46 -0
- package/dist/orchestrator/ParallelOrchestrationLoop.js +36 -0
- package/dist/orchestrator/PerformanceScorecard.js +105 -0
- package/dist/orchestrator/PlannerAgent.js +46 -0
- package/dist/orchestrator/ProcessSandbox.js +129 -0
- package/dist/orchestrator/PromotionPipeline.js +74 -0
- package/dist/orchestrator/PromotionReviewGate.js +11 -0
- package/dist/orchestrator/QueueScheduler.js +260 -0
- package/dist/orchestrator/ReleaseGate.js +36 -0
- package/dist/orchestrator/ReleaseWorkflow.js +68 -0
- package/dist/orchestrator/RemotePublisher.js +139 -0
- package/dist/orchestrator/ReuseEngine.js +89 -0
- package/dist/orchestrator/ReviewerAgent.js +49 -0
- package/dist/orchestrator/RoleHandoff.js +65 -0
- package/dist/orchestrator/RuntimeHealthMonitor.js +143 -0
- package/dist/orchestrator/RuntimePolicy.js +105 -0
- package/dist/orchestrator/RuntimeProbe.js +97 -0
- package/dist/orchestrator/RuntimeRegistry.js +73 -0
- package/dist/orchestrator/SandboxPolicy.js +22 -0
- package/dist/orchestrator/SecurityDownloadGuard.js +169 -0
- package/dist/orchestrator/SecurityEventStore.js +58 -0
- package/dist/orchestrator/ServiceCompletionPolicy.js +36 -0
- package/dist/orchestrator/ServiceState.js +195 -0
- package/dist/orchestrator/SkillCreator.js +404 -0
- package/dist/orchestrator/SkillLearningLoop.js +57 -0
- package/dist/orchestrator/SkillManager.js +75 -0
- package/dist/orchestrator/SkillNetwork.js +29 -0
- package/dist/orchestrator/SkillRegistryV2.js +28 -0
- package/dist/orchestrator/SkillScoring.js +70 -0
- package/dist/orchestrator/SquadAutoCreator.js +64 -0
- package/dist/orchestrator/SquadCreator.js +727 -0
- package/dist/orchestrator/SquadRegistry.js +28 -0
- package/dist/orchestrator/SquadRouter.js +33 -0
- package/dist/orchestrator/SquadScoring.js +70 -0
- package/dist/orchestrator/SubagentLifecycle.js +90 -0
- package/dist/orchestrator/SynthesizerAgent.js +48 -0
- package/dist/orchestrator/SystemDoctor.js +224 -0
- package/dist/orchestrator/TaskExecutor.js +422 -0
- package/dist/orchestrator/TeammateBoard.js +61 -0
- package/dist/orchestrator/TestHarness.js +184 -0
- package/dist/orchestrator/VoiceManager.js +203 -0
- package/dist/orchestrator/VoiceRouter.js +89 -0
- package/dist/orchestrator/capability/CapabilityGenesisEngine.js +278 -0
- package/dist/orchestrator/capability/CapabilityPackParser.js +223 -0
- package/dist/orchestrator/capability/CapabilityPackSchema.js +62 -0
- package/dist/orchestrator/capability/CapabilityPackState.js +163 -0
- package/dist/orchestrator/providers/AgentProvider.js +2 -0
- package/dist/orchestrator/providers/CapabilityProvider.js +12 -0
- package/dist/orchestrator/providers/CloudAgentProvider.js +55 -0
- package/dist/orchestrator/providers/CloudSkillProvider.js +55 -0
- package/dist/orchestrator/providers/CloudSquadProvider.js +55 -0
- package/dist/orchestrator/providers/CompositeAgentProvider.js +16 -0
- package/dist/orchestrator/providers/CompositeCapabilityProvider.js +25 -0
- package/dist/orchestrator/providers/CompositeSkillProvider.js +16 -0
- package/dist/orchestrator/providers/CompositeSquadProvider.js +16 -0
- package/dist/orchestrator/providers/CompositeWorkflowProvider.js +46 -0
- package/dist/orchestrator/providers/FileAgentProvider.js +105 -0
- package/dist/orchestrator/providers/FileCapabilityProvider.js +106 -0
- package/dist/orchestrator/providers/FileSkillProvider.js +65 -0
- package/dist/orchestrator/providers/FileSquadProvider.js +69 -0
- package/dist/orchestrator/providers/FileWorkflowProvider.js +103 -0
- package/dist/orchestrator/providers/SkillProvider.js +2 -0
- package/dist/orchestrator/providers/SquadProvider.js +2 -0
- package/dist/orchestrator/toolset/ToolsetGuard.js +69 -0
- package/dist/orchestrator/toolset/ToolsetRegistry.js +65 -0
- package/dist/orchestrator/toolset/ToolsetSchema.js +21 -0
- package/dist/orchestrator/util/AtomicWriter.js +204 -0
- package/dist/orchestrator/util/DistributedLock.js +232 -0
- package/dist/orchestrator/util/TemplateRenderer.js +87 -0
- package/dist/orchestrator/util/WatchdogHeartbeat.js +116 -0
- package/dist/orchestrator/workflow/ConditionParser.js +232 -0
- package/dist/orchestrator/workflow/WorkflowEngine.js +379 -0
- package/dist/orchestrator/workflow/WorkflowParser.js +368 -0
- package/dist/orchestrator/workflow/WorkflowSchema.js +65 -0
- package/dist/orchestrator/workflow/WorkflowState.js +11 -0
- package/dist/reversa/ReversaAgent.js +134 -0
- package/dist/reversa/ReversaContracts.js +62 -0
- package/dist/reversa/ReversaExecutors.js +65 -0
- package/dist/skills/SkillRegistry.js +71 -0
- package/dist/squads/SquadManager.js +87 -0
- package/dist/test_admin_teams_networks.js +54 -0
- package/dist/test_agent_team_skill_network.js +15 -0
- package/dist/test_aiobuilder_cli_parity.js +169 -0
- package/dist/test_ask_exit.js +73 -0
- package/dist/test_atomic_writer.js +209 -0
- package/dist/test_autonomous_soak.js +141 -0
- package/dist/test_benchmark_engine.js +41 -0
- package/dist/test_brain_error_diagnostics.js +51 -0
- package/dist/test_brain_fallback_chain.js +93 -0
- package/dist/test_capability_genesis_engine.js +225 -0
- package/dist/test_capability_pack_schema.js +214 -0
- package/dist/test_catalog_quality.js +150 -0
- package/dist/test_cli_crud_roundtrip.js +154 -0
- package/dist/test_cli_diagnostics.js +131 -0
- package/dist/test_cli_doc_parity.js +126 -0
- package/dist/test_cli_help_surface.js +106 -0
- package/dist/test_cli_service_commands.js +83 -0
- package/dist/test_consequence_forecast_brain.js +165 -0
- package/dist/test_consequence_forecaster.js +24 -0
- package/dist/test_conversation_memory.js +36 -0
- package/dist/test_create_entities.js +54 -0
- package/dist/test_creator_placeholders_completed.js +177 -0
- package/dist/test_cron_manager.js +123 -0
- package/dist/test_daemon_sigterm.js +72 -0
- package/dist/test_deep_research_capability.js +87 -0
- package/dist/test_designmd_import_registry.js +16 -0
- package/dist/test_designmd_mode.js +50 -0
- package/dist/test_designmd_mode_workspace.js +13 -0
- package/dist/test_dist_templates_layout.js +135 -0
- package/dist/test_distributed_lock.js +201 -0
- package/dist/test_distribution_installability.js +67 -0
- package/dist/test_doctor_sandbox_check.js +44 -0
- package/dist/test_dream_organizer.js +25 -0
- package/dist/test_dual_mode.js +15 -0
- package/dist/test_enterprise_agentic_core.js +128 -0
- package/dist/test_forecast_brain_wiring.js +87 -0
- package/dist/test_gateway_telegram_guardrails.js +52 -0
- package/dist/test_governance.js +34 -0
- package/dist/test_governance_advanced.js +75 -0
- package/dist/test_governance_scope_ledger.js +147 -0
- package/dist/test_governance_v13_policies.js +44 -0
- package/dist/test_guided_creator_cli.js +100 -0
- package/dist/test_host_install_e2e.js +324 -0
- package/dist/test_host_installer.js +259 -0
- package/dist/test_host_installers_gemini_codex.js +95 -0
- package/dist/test_host_uninstaller.js +295 -0
- package/dist/test_install_flow.js +70 -0
- package/dist/test_install_flow_host_validation.js +143 -0
- package/dist/test_install_wizard.js +272 -0
- package/dist/test_integration_gemini_live.js +95 -0
- package/dist/test_integration_http_trigger_live.js +154 -0
- package/dist/test_integration_telegram_live.js +102 -0
- package/dist/test_job_lifecycle.js +16 -0
- package/dist/test_memory_orchestrator.js +33 -0
- package/dist/test_memory_promotion.js +36 -0
- package/dist/test_memory_retention.js +37 -0
- package/dist/test_mission_checkpoint.js +204 -0
- package/dist/test_multi_host_docs_parity.js +125 -0
- package/dist/test_openlife_auto_creator_routing.js +69 -0
- package/dist/test_openlife_evolution_surface.js +77 -0
- package/dist/test_openlife_gatekeeper_routing.js +15 -0
- package/dist/test_openlife_routing_surface.js +27 -0
- package/dist/test_openlife_runtime_source_truth.js +25 -0
- package/dist/test_operating_system.js +45 -0
- package/dist/test_optimization_loop.js +38 -0
- package/dist/test_orchestration_assets_lifecycle.js +78 -0
- package/dist/test_outcome_simulator.js +38 -0
- package/dist/test_performance_latency.js +215 -0
- package/dist/test_performance_scorecard.js +38 -0
- package/dist/test_phase1_check_exit.js +103 -0
- package/dist/test_phase6_board.js +31 -0
- package/dist/test_phase6_cadence.js +29 -0
- package/dist/test_phase6_ops.js +37 -0
- package/dist/test_post_mission_evaluation.js +190 -0
- package/dist/test_process_sandbox.js +88 -0
- package/dist/test_profile_toolset_mcp.js +125 -0
- package/dist/test_queue_scheduler.js +239 -0
- package/dist/test_release_gate.js +23 -0
- package/dist/test_remote_publish.js +193 -0
- package/dist/test_reversa_contracts_e2e.js +48 -0
- package/dist/test_reversa_export_and_strict.js +51 -0
- package/dist/test_reversa_full_execution.js +12 -0
- package/dist/test_reversa_lite.js +9 -0
- package/dist/test_royal_stack_golden.js +179 -0
- package/dist/test_runtime_health_backoff.js +154 -0
- package/dist/test_runtime_policy.js +26 -0
- package/dist/test_runtime_probe.js +19 -0
- package/dist/test_runtime_profile_oauth_only.js +262 -0
- package/dist/test_runtime_registry.js +11 -0
- package/dist/test_security_download_and_scan.js +103 -0
- package/dist/test_security_download_guard.js +14 -0
- package/dist/test_service_command_surface.js +12 -0
- package/dist/test_service_completion_policy.js +32 -0
- package/dist/test_service_guardrails_delete.js +12 -0
- package/dist/test_service_mode_explicit_only.js +174 -0
- package/dist/test_sources_import_ref.js +46 -0
- package/dist/test_sources_scaffold.js +43 -0
- package/dist/test_squad_skill_creator.js +305 -0
- package/dist/test_squad_skill_design_llm.js +176 -0
- package/dist/test_subsystems_org_state.js +271 -0
- package/dist/test_subsystems_promotion_memory_assets.js +343 -0
- package/dist/test_subsystems_routing_governance.js +234 -0
- package/dist/test_task_executor_sandbox_optin.js +127 -0
- package/dist/test_teammate_learning.js +15 -0
- package/dist/test_telegram_delete_guardrail.js +21 -0
- package/dist/test_toolset_enforcement.js +188 -0
- package/dist/test_trigger_basic_auth.js +112 -0
- package/dist/test_util/doc_parity.js +120 -0
- package/dist/test_v15_e2e_integration.js +207 -0
- package/dist/test_watchdog_heartbeat.js +152 -0
- package/dist/test_workflow_condition_parser.js +63 -0
- package/dist/test_workflow_e2e.js +240 -0
- package/dist/test_workflow_engine.js +330 -0
- package/dist/test_workflow_parser.js +245 -0
- package/dist/test_workflow_schema_backward_compat.js +197 -0
- package/dist-templates/README.md +91 -0
- package/dist-templates/claude-code/agents/openlife-atlas.md +52 -0
- package/dist-templates/claude-code/agents/openlife-forge.md +42 -0
- package/dist-templates/claude-code/agents/openlife-genesis.md +59 -0
- package/dist-templates/claude-code/agents/openlife-lyra.md +40 -0
- package/dist-templates/claude-code/agents/openlife-maestro.md +45 -0
- package/dist-templates/claude-code/commands/openlife/ask.md +14 -0
- package/dist-templates/claude-code/commands/openlife/doctor.md +19 -0
- package/dist-templates/claude-code/commands/openlife/dream.md +20 -0
- package/dist-templates/claude-code/commands/openlife/status.md +14 -0
- package/dist-templates/claude-code/mcp/openlife-orchestrator.json +46 -0
- package/dist-templates/codex/README.md +7 -0
- package/dist-templates/codex/agents/openlife-atlas.md +52 -0
- package/dist-templates/codex/agents/openlife-forge.md +42 -0
- package/dist-templates/codex/agents/openlife-genesis.md +59 -0
- package/dist-templates/codex/agents/openlife-lyra.md +40 -0
- package/dist-templates/codex/agents/openlife-maestro.md +45 -0
- package/dist-templates/codex/commands/openlife/ask.md +14 -0
- package/dist-templates/codex/commands/openlife/doctor.md +19 -0
- package/dist-templates/codex/commands/openlife/dream.md +20 -0
- package/dist-templates/codex/commands/openlife/status.md +14 -0
- package/dist-templates/codex/mcp/openlife-orchestrator.json +46 -0
- package/dist-templates/gemini-cli/README.md +8 -0
- package/dist-templates/gemini-cli/agents/openlife-atlas.md +52 -0
- package/dist-templates/gemini-cli/agents/openlife-forge.md +42 -0
- package/dist-templates/gemini-cli/agents/openlife-genesis.md +59 -0
- package/dist-templates/gemini-cli/agents/openlife-lyra.md +40 -0
- package/dist-templates/gemini-cli/agents/openlife-maestro.md +45 -0
- package/dist-templates/gemini-cli/commands/openlife/ask.md +14 -0
- package/dist-templates/gemini-cli/commands/openlife/doctor.md +19 -0
- package/dist-templates/gemini-cli/commands/openlife/dream.md +20 -0
- package/dist-templates/gemini-cli/commands/openlife/status.md +14 -0
- package/dist-templates/gemini-cli/mcp/openlife-orchestrator.json +46 -0
- package/dist-templates/skill-template/README.md +34 -0
- package/dist-templates/skill-template/SKILL.md.template +59 -0
- package/dist-templates/squad-template/README.md +82 -0
- package/dist-templates/squad-template/SQUAD.md.template +51 -0
- package/dist-templates/squad-template/agent-template.md +51 -0
- package/dist-templates/squad-template/checklist-template.md +25 -0
- package/dist-templates/squad-template/task-template.md +36 -0
- package/dist-templates/workflows/PORTED_WORKFLOWS.md +60 -0
- package/dist-templates/workflows/brownfield-discovery.yaml +137 -0
- package/dist-templates/workflows/greenfield-fullstack.yaml +132 -0
- package/dist-templates/workflows/qa-loop.yaml +125 -0
- package/dist-templates/workflows/story-development-cycle.yaml +80 -0
- package/docs/CHANGELOG_FEATURE_ROLLOUT_DESIGNMD.md +43 -0
- package/docs/EXTERNAL_SOURCES_AND_SECURITY_GUARD.md +33 -0
- package/docs/OPENLIFE_AUDIT_2026-05-06.md +170 -0
- package/docs/OPENLIFE_CONSOLIDATED_PLAN_2026-05-06.md +299 -0
- package/docs/OPENLIFE_DUAL_MODE_IMPLEMENTATION_PLAN.md +205 -0
- package/docs/OPENLIFE_EVOLUTION_SURFACE_2026-05-07.md +53 -0
- package/docs/OPENLIFE_SKILLS_IMPORT_2026-05-07.json +223 -0
- package/docs/OPENLIFE_SQUADS_IMPORT_2026-05-07.json +184 -0
- package/docs/PAPERCLIP_OPENLIFE_INVESTIGATION.md +85 -0
- package/docs/README.md +28 -0
- package/docs/RELEASE_ORGANIZATION_PLAN.md +164 -0
- package/docs/audit/CLI-EXECUTION-RESULTS.md +113 -0
- package/docs/audit/CLI-MATRIX.md +556 -0
- package/docs/audit/DOC-PARITY-GAPS.md +351 -0
- package/docs/audit/ORCHESTRATOR-MATRIX.md +136 -0
- package/docs/audit/TEST-COVERAGE-GAPS.md +334 -0
- package/docs/audit/integrations/SKIPPED.md +101 -0
- package/docs/autonomous-install.md +79 -0
- package/docs/capability-genesis.md +137 -0
- package/docs/capability-pack-schema.md +157 -0
- package/docs/commands.md +82 -0
- package/docs/deep-research-capability.md +114 -0
- package/docs/development/typescript-conventions.md +95 -0
- package/docs/host-installers.md +68 -0
- package/docs/install/aiobuilder.md +70 -0
- package/docs/install/claude-code.md +83 -0
- package/docs/install/codex.md +64 -0
- package/docs/install/gemini-cli.md +64 -0
- package/docs/install/runtime-profiles.md +83 -0
- package/docs/openlife-agent-os-blueprint.md +114 -0
- package/docs/openlife-install-backlog.md +115 -0
- package/docs/openlife-install-spec.md +306 -0
- package/docs/operations/CLOUD_CUTOVER_AUDIT.md +37 -0
- package/docs/operations/PHASE_PROGRESS_CONTINUATION.md +24 -0
- package/docs/performance-benchmarks.md +83 -0
- package/docs/planning/v1.3-capability-genesis.md +157 -0
- package/docs/plans/2026-05-05-admin-interface-professional-dark-premium-plan.md +84 -0
- package/docs/plans/2026-05-05-openlife-autonomous-domain-marketplace-masterplan.md +122 -0
- package/docs/quickstart.md +60 -0
- package/docs/release-process.md +236 -0
- package/docs/roadmap/OPENLIFE_MASTER_PLAN_CLOUD_V3.md +97 -0
- package/docs/sandboxing-research.md +117 -0
- package/docs/stories/epic-feature-audit/1.1.story.md +84 -0
- package/docs/stories/epic-feature-audit/1.2.story.md +102 -0
- package/docs/stories/epic-feature-audit/1.3.story.md +93 -0
- package/docs/stories/epic-feature-audit/1.5.story.md +121 -0
- package/docs/stories/epic-feature-audit/1.6.story.md +80 -0
- package/docs/stories/epic-feature-completeness/2.1.story.md +70 -0
- package/docs/stories/epic-feature-completeness/2.2.story.md +49 -0
- package/docs/stories/epic-feature-completeness/2.3.story.md +74 -0
- package/docs/stories/epic-feature-completeness/2.4.story.md +71 -0
- package/docs/stories/epic-feature-completeness/3.1.story.md +56 -0
- package/docs/stories/epic-feature-completeness/3.2.story.md +80 -0
- package/docs/stories/epic-feature-completeness/3.3.story.md +68 -0
- package/docs/stories/epic-feature-completeness/3.4.story.md +71 -0
- package/docs/stories/epic-feature-completeness/3.5.story.md +72 -0
- package/docs/stories/epic-feature-completeness/3.6.story.md +69 -0
- package/docs/stories/epic-feature-completeness/3.7.story.md +68 -0
- package/docs/stories/epic-feature-completeness/3.8.story.md +57 -0
- package/docs/toolset-enforcement.md +122 -0
- package/docs/v1.4-changelog.md +159 -0
- package/docs/v1.5-changelog.md +106 -0
- package/docs/v1.5-roadmap.md +121 -0
- package/docs/v1.6-changelog.md +67 -0
- package/docs/v1.6-roadmap.md +89 -0
- package/docs/v1.7-changelog.md +98 -0
- package/docs/workflow-schema.md +177 -0
- package/package.json +177 -0
- package/scripts/clean-test-pollution.js +61 -0
- package/scripts/openlife-agent-start.sh +6 -0
- package/scripts/openlife-agent.service.example +13 -0
- package/scripts/openlife-agent.supervisord.conf.example +8 -0
- package/scripts/openlife-autonomous-install.sh +29 -0
- package/scripts/postinstall-check.sh +37 -0
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* test_atomic_writer — Story 6.1 regression suite.
|
|
4
|
+
*
|
|
5
|
+
* Asserts the AtomicWriter contract:
|
|
6
|
+
* 1. Happy path round-trip
|
|
7
|
+
* 2. Crash safety (rename failure leaves no corrupted final file)
|
|
8
|
+
* 3. OPENLIFE_ATOMIC_WRITES=off fallback to plain writeFileSync
|
|
9
|
+
* 4. JSONL append atomicity
|
|
10
|
+
*
|
|
11
|
+
* Run via: `npm run test:atomic-writer` (compiles + executes dist/).
|
|
12
|
+
*
|
|
13
|
+
* Pattern: throws or process.exit(1) on failure; prints
|
|
14
|
+
* TEST_ATOMIC_WRITER_OK on full pass.
|
|
15
|
+
*/
|
|
16
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
17
|
+
if (k2 === undefined) k2 = k;
|
|
18
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
19
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
20
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
21
|
+
}
|
|
22
|
+
Object.defineProperty(o, k2, desc);
|
|
23
|
+
}) : (function(o, m, k, k2) {
|
|
24
|
+
if (k2 === undefined) k2 = k;
|
|
25
|
+
o[k2] = m[k];
|
|
26
|
+
}));
|
|
27
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
28
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
29
|
+
}) : function(o, v) {
|
|
30
|
+
o["default"] = v;
|
|
31
|
+
});
|
|
32
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
33
|
+
var ownKeys = function(o) {
|
|
34
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
35
|
+
var ar = [];
|
|
36
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
37
|
+
return ar;
|
|
38
|
+
};
|
|
39
|
+
return ownKeys(o);
|
|
40
|
+
};
|
|
41
|
+
return function (mod) {
|
|
42
|
+
if (mod && mod.__esModule) return mod;
|
|
43
|
+
var result = {};
|
|
44
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
45
|
+
__setModuleDefault(result, mod);
|
|
46
|
+
return result;
|
|
47
|
+
};
|
|
48
|
+
})();
|
|
49
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
50
|
+
const fs = __importStar(require("fs"));
|
|
51
|
+
const os = __importStar(require("os"));
|
|
52
|
+
const path = __importStar(require("path"));
|
|
53
|
+
const AtomicWriter_1 = require("./orchestrator/util/AtomicWriter");
|
|
54
|
+
function assert(cond, msg) {
|
|
55
|
+
if (!cond)
|
|
56
|
+
throw new Error(`assertion failed: ${msg}`);
|
|
57
|
+
}
|
|
58
|
+
function mkTmp() {
|
|
59
|
+
return fs.mkdtempSync(path.join(os.tmpdir(), 'atomic-test-'));
|
|
60
|
+
}
|
|
61
|
+
function rmTmp(dir) {
|
|
62
|
+
try {
|
|
63
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
64
|
+
}
|
|
65
|
+
catch { /* ignore */ }
|
|
66
|
+
}
|
|
67
|
+
function listTempOrphans(dir) {
|
|
68
|
+
if (!fs.existsSync(dir))
|
|
69
|
+
return [];
|
|
70
|
+
return fs.readdirSync(dir).filter((f) => /\.tmp\.\d+\.\d+\.[a-z0-9]+$/i.test(f));
|
|
71
|
+
}
|
|
72
|
+
// ─────────────────────────────────────────────────────────────
|
|
73
|
+
// Scenario 1: Happy path round-trip
|
|
74
|
+
// ─────────────────────────────────────────────────────────────
|
|
75
|
+
function scenarioHappyPath() {
|
|
76
|
+
const tmpRoot = mkTmp();
|
|
77
|
+
try {
|
|
78
|
+
const target = path.join(tmpRoot, 'state.json');
|
|
79
|
+
const payload = { hello: 'world', count: 42, nested: { ok: true } };
|
|
80
|
+
(0, AtomicWriter_1.writeJsonAtomic)(target, payload);
|
|
81
|
+
assert(fs.existsSync(target), 'final file must exist after writeJsonAtomic');
|
|
82
|
+
const read = JSON.parse(fs.readFileSync(target, 'utf-8'));
|
|
83
|
+
assert(JSON.stringify(read) === JSON.stringify(payload), 'round-trip mismatch');
|
|
84
|
+
assert(listTempOrphans(tmpRoot).length === 0, 'no temp file should remain after success');
|
|
85
|
+
console.log('✅ scenario 1: happy path round-trip');
|
|
86
|
+
}
|
|
87
|
+
finally {
|
|
88
|
+
rmTmp(tmpRoot);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
// ─────────────────────────────────────────────────────────────
|
|
92
|
+
// Scenario 2: Crash safety — rename fails, no corrupted file
|
|
93
|
+
// ─────────────────────────────────────────────────────────────
|
|
94
|
+
function scenarioCrashSafety() {
|
|
95
|
+
const tmpRoot = mkTmp();
|
|
96
|
+
try {
|
|
97
|
+
// Pre-existing file in valid parent that we expect to remain unchanged
|
|
98
|
+
const target = path.join(tmpRoot, 'state.json');
|
|
99
|
+
fs.writeFileSync(target, JSON.stringify({ before: 'unchanged' }), 'utf-8');
|
|
100
|
+
// Simulate "crash mid-write" by aiming at a target whose parent
|
|
101
|
+
// exists at writeJsonAtomic call time but is deleted before the rename
|
|
102
|
+
// could succeed. We can't reliably race that, so instead we trigger a
|
|
103
|
+
// hard error path: pre-create the final path as a *directory*, which
|
|
104
|
+
// makes renameSync throw with EISDIR / EEXIST.
|
|
105
|
+
//
|
|
106
|
+
// This exercises the same code path: write tmp → attempt rename → throw
|
|
107
|
+
// → ensure pre-existing data on `target` is untouched + temp orphan
|
|
108
|
+
// cleaned up.
|
|
109
|
+
const collidingDir = path.join(tmpRoot, 'collision');
|
|
110
|
+
fs.mkdirSync(collidingDir, { recursive: true });
|
|
111
|
+
// Place a file inside so the dir is non-empty — renameSync onto a
|
|
112
|
+
// non-empty dir is guaranteed to fail across POSIX + Windows.
|
|
113
|
+
fs.writeFileSync(path.join(collidingDir, 'sentinel'), 'x', 'utf-8');
|
|
114
|
+
let threw = false;
|
|
115
|
+
try {
|
|
116
|
+
(0, AtomicWriter_1.writeJsonAtomic)(collidingDir, { after: 'never-arrives' });
|
|
117
|
+
}
|
|
118
|
+
catch (err) {
|
|
119
|
+
threw = err instanceof Error;
|
|
120
|
+
}
|
|
121
|
+
assert(threw, 'writeJsonAtomic must throw when the rename target is a non-empty directory');
|
|
122
|
+
// The colliding directory should still contain its sentinel — no corruption
|
|
123
|
+
assert(fs.existsSync(path.join(collidingDir, 'sentinel')), 'colliding dir contents must not be wiped');
|
|
124
|
+
// The original target should be untouched (we never wrote to it on this attempt)
|
|
125
|
+
const stillThere = JSON.parse(fs.readFileSync(target, 'utf-8'));
|
|
126
|
+
assert(stillThere.before === 'unchanged', 'pre-existing target must not be overwritten by failed call');
|
|
127
|
+
// purgeStaleTempFiles should be invocable + non-error (cleanup helper)
|
|
128
|
+
const removed = (0, AtomicWriter_1.purgeStaleTempFiles)(tmpRoot, 0);
|
|
129
|
+
assert(typeof removed === 'number' && removed >= 0, 'purgeStaleTempFiles must return a non-negative count');
|
|
130
|
+
console.log('✅ scenario 2: crash safety (rename failure does not corrupt existing data)');
|
|
131
|
+
}
|
|
132
|
+
finally {
|
|
133
|
+
rmTmp(tmpRoot);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
// ─────────────────────────────────────────────────────────────
|
|
137
|
+
// Scenario 3: OPENLIFE_ATOMIC_WRITES=off fallback
|
|
138
|
+
// ─────────────────────────────────────────────────────────────
|
|
139
|
+
function scenarioFallback() {
|
|
140
|
+
const tmpRoot = mkTmp();
|
|
141
|
+
const prev = process.env.OPENLIFE_ATOMIC_WRITES;
|
|
142
|
+
try {
|
|
143
|
+
process.env.OPENLIFE_ATOMIC_WRITES = 'off';
|
|
144
|
+
assert((0, AtomicWriter_1._isAtomicEnabled)() === false, 'flag=off must disable atomic mode');
|
|
145
|
+
const target = path.join(tmpRoot, 'state.json');
|
|
146
|
+
(0, AtomicWriter_1.writeJsonAtomic)(target, { mode: 'fallback' });
|
|
147
|
+
assert(fs.existsSync(target), 'fallback mode must still write file');
|
|
148
|
+
const read = JSON.parse(fs.readFileSync(target, 'utf-8'));
|
|
149
|
+
assert(read.mode === 'fallback', 'fallback content must be intact');
|
|
150
|
+
assert(listTempOrphans(tmpRoot).length === 0, 'fallback mode must not create temp files');
|
|
151
|
+
// Restore — re-check enabled
|
|
152
|
+
delete process.env.OPENLIFE_ATOMIC_WRITES;
|
|
153
|
+
assert((0, AtomicWriter_1._isAtomicEnabled)() === true, 'default (no env) must be atomic enabled');
|
|
154
|
+
console.log('✅ scenario 3: OPENLIFE_ATOMIC_WRITES=off fallback');
|
|
155
|
+
}
|
|
156
|
+
finally {
|
|
157
|
+
if (prev === undefined)
|
|
158
|
+
delete process.env.OPENLIFE_ATOMIC_WRITES;
|
|
159
|
+
else
|
|
160
|
+
process.env.OPENLIFE_ATOMIC_WRITES = prev;
|
|
161
|
+
rmTmp(tmpRoot);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
// ─────────────────────────────────────────────────────────────
|
|
165
|
+
// Scenario 4: JSONL append atomicity
|
|
166
|
+
// ─────────────────────────────────────────────────────────────
|
|
167
|
+
function scenarioJsonlAppend() {
|
|
168
|
+
const tmpRoot = mkTmp();
|
|
169
|
+
try {
|
|
170
|
+
const target = path.join(tmpRoot, 'events.jsonl');
|
|
171
|
+
(0, AtomicWriter_1.appendJsonlAtomic)(target, { type: 'a', n: 1 });
|
|
172
|
+
(0, AtomicWriter_1.appendJsonlAtomic)(target, { type: 'b', n: 2 });
|
|
173
|
+
(0, AtomicWriter_1.appendJsonlAtomic)(target, { type: 'c', n: 3, nested: { ok: true } });
|
|
174
|
+
const lines = fs.readFileSync(target, 'utf-8').split('\n').filter((l) => l.length > 0);
|
|
175
|
+
assert(lines.length === 3, `expected 3 JSONL lines, got ${lines.length}`);
|
|
176
|
+
const parsed = lines.map((l) => JSON.parse(l));
|
|
177
|
+
assert(parsed[0].type === 'a' && parsed[0].n === 1, 'line 1 mismatch');
|
|
178
|
+
assert(parsed[1].type === 'b' && parsed[1].n === 2, 'line 2 mismatch');
|
|
179
|
+
assert(parsed[2].type === 'c' && parsed[2].n === 3, 'line 3 mismatch');
|
|
180
|
+
assert(listTempOrphans(tmpRoot).length === 0, 'no temp orphans after JSONL appends');
|
|
181
|
+
// Also write to non-existing parent dir to verify auto-mkdir
|
|
182
|
+
const nested = path.join(tmpRoot, 'deeper', 'log.jsonl');
|
|
183
|
+
(0, AtomicWriter_1.appendJsonlAtomic)(nested, { type: 'nested', n: 99 });
|
|
184
|
+
assert(fs.existsSync(nested), 'auto-mkdir for nested JSONL append');
|
|
185
|
+
console.log('✅ scenario 4: JSONL append atomicity');
|
|
186
|
+
}
|
|
187
|
+
finally {
|
|
188
|
+
rmTmp(tmpRoot);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
// ─────────────────────────────────────────────────────────────
|
|
192
|
+
// Driver
|
|
193
|
+
// ─────────────────────────────────────────────────────────────
|
|
194
|
+
function main() {
|
|
195
|
+
console.log('🧪 test_atomic_writer — Story 6.1 regression suite');
|
|
196
|
+
scenarioHappyPath();
|
|
197
|
+
scenarioCrashSafety();
|
|
198
|
+
scenarioFallback();
|
|
199
|
+
scenarioJsonlAppend();
|
|
200
|
+
console.log('');
|
|
201
|
+
console.log('TEST_ATOMIC_WRITER_OK');
|
|
202
|
+
}
|
|
203
|
+
try {
|
|
204
|
+
main();
|
|
205
|
+
}
|
|
206
|
+
catch (err) {
|
|
207
|
+
console.error('❌ test_atomic_writer FAILED:', err instanceof Error ? err.message : err);
|
|
208
|
+
process.exit(1);
|
|
209
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* test_autonomous_soak.ts — Story 6.7
|
|
4
|
+
*
|
|
5
|
+
* Multi-minute soak test for the autonomous runtime. Intentionally out of
|
|
6
|
+
* `test:all` (kept under `npm run test:soak`) so the canonical suite stays
|
|
7
|
+
* fast.
|
|
8
|
+
*
|
|
9
|
+
* What it proves:
|
|
10
|
+
* 1. QueueScheduler picks up enqueued missions and processes them.
|
|
11
|
+
* 2. After a hard kill mid-run, missions left in `running` state are
|
|
12
|
+
* reconciled back to `pending` on next start (Story 6.3 contract).
|
|
13
|
+
* 3. WatchdogHeartbeat stays fresh (< 60s) while the loop is alive
|
|
14
|
+
* (Story 6.6 contract).
|
|
15
|
+
* 4. AtomicWriter prevents state corruption under repeated crashes
|
|
16
|
+
* (Story 6.1 contract).
|
|
17
|
+
*
|
|
18
|
+
* Default budget: 3 minutes. Tunable via `OPENLIFE_SOAK_DURATION_MS`.
|
|
19
|
+
*/
|
|
20
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
21
|
+
if (k2 === undefined) k2 = k;
|
|
22
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
23
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
24
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
25
|
+
}
|
|
26
|
+
Object.defineProperty(o, k2, desc);
|
|
27
|
+
}) : (function(o, m, k, k2) {
|
|
28
|
+
if (k2 === undefined) k2 = k;
|
|
29
|
+
o[k2] = m[k];
|
|
30
|
+
}));
|
|
31
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
32
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
33
|
+
}) : function(o, v) {
|
|
34
|
+
o["default"] = v;
|
|
35
|
+
});
|
|
36
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
37
|
+
var ownKeys = function(o) {
|
|
38
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
39
|
+
var ar = [];
|
|
40
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
41
|
+
return ar;
|
|
42
|
+
};
|
|
43
|
+
return ownKeys(o);
|
|
44
|
+
};
|
|
45
|
+
return function (mod) {
|
|
46
|
+
if (mod && mod.__esModule) return mod;
|
|
47
|
+
var result = {};
|
|
48
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
49
|
+
__setModuleDefault(result, mod);
|
|
50
|
+
return result;
|
|
51
|
+
};
|
|
52
|
+
})();
|
|
53
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
54
|
+
const fs = __importStar(require("fs"));
|
|
55
|
+
const path = __importStar(require("path"));
|
|
56
|
+
const os = __importStar(require("os"));
|
|
57
|
+
const DURATION_MS = Number(process.env.OPENLIFE_SOAK_DURATION_MS || '180000');
|
|
58
|
+
const MISSION_COUNT = Number(process.env.OPENLIFE_SOAK_MISSIONS || '12');
|
|
59
|
+
const CRASH_INTERVAL_MS = Number(process.env.OPENLIFE_SOAK_CRASH_INTERVAL_MS || '60000');
|
|
60
|
+
function assertTrue(cond, label) {
|
|
61
|
+
if (!cond)
|
|
62
|
+
throw new Error(`SOAK_ASSERT_FAILED[${label}]`);
|
|
63
|
+
}
|
|
64
|
+
async function sleep(ms) {
|
|
65
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
66
|
+
}
|
|
67
|
+
async function main() {
|
|
68
|
+
const sandbox = fs.mkdtempSync(path.join(os.tmpdir(), 'openlife-soak-'));
|
|
69
|
+
const stateDir = path.join(sandbox, '.openlife');
|
|
70
|
+
fs.mkdirSync(stateDir, { recursive: true });
|
|
71
|
+
console.log(`[soak] sandbox: ${sandbox}`);
|
|
72
|
+
console.log(`[soak] duration ${DURATION_MS}ms, ${MISSION_COUNT} missions, crash every ${CRASH_INTERVAL_MS}ms`);
|
|
73
|
+
const { QueueScheduler } = require(path.join(process.cwd(), 'dist', 'orchestrator', 'QueueScheduler.js'));
|
|
74
|
+
// Cycle: spawn scheduler, let it work for CRASH_INTERVAL_MS, hard-stop, restart, repeat.
|
|
75
|
+
const startedAt = Date.now();
|
|
76
|
+
let cycle = 0;
|
|
77
|
+
let totalCompleted = 0;
|
|
78
|
+
while (Date.now() - startedAt < DURATION_MS) {
|
|
79
|
+
cycle += 1;
|
|
80
|
+
console.log(`[soak] cycle ${cycle} — fresh scheduler`);
|
|
81
|
+
const scheduler = new QueueScheduler({
|
|
82
|
+
queueFile: path.join(stateDir, 'agent-queue.json'),
|
|
83
|
+
maxConcurrent: 3,
|
|
84
|
+
pollIntervalMs: 250,
|
|
85
|
+
executor: {
|
|
86
|
+
async execute() {
|
|
87
|
+
await sleep(50);
|
|
88
|
+
return { status: 'completed' };
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
});
|
|
92
|
+
// On cycle 1, prime the queue.
|
|
93
|
+
if (cycle === 1) {
|
|
94
|
+
for (let i = 0; i < MISSION_COUNT; i++) {
|
|
95
|
+
scheduler.enqueue({ id: `soak-mission-${i}`, intent: 'soak', payload: { i } });
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
// Reconcile orphans left by prior cycle's hard-stop.
|
|
100
|
+
const reconciled = scheduler.reconcileOrphans();
|
|
101
|
+
console.log(`[soak] cycle ${cycle} reconciled ${reconciled} orphans back to pending`);
|
|
102
|
+
}
|
|
103
|
+
scheduler.start();
|
|
104
|
+
await sleep(Math.min(CRASH_INTERVAL_MS, DURATION_MS - (Date.now() - startedAt)));
|
|
105
|
+
// Hard "kill" simulation — fire-and-forget stop() so any in-flight mission
|
|
106
|
+
// may remain in `running` state in the queue file. Reconcile must rescue it
|
|
107
|
+
// on the next cycle.
|
|
108
|
+
void scheduler.stop();
|
|
109
|
+
// Snapshot queue health.
|
|
110
|
+
const queue = scheduler.list();
|
|
111
|
+
totalCompleted = queue.filter((m) => m.status === 'completed').length;
|
|
112
|
+
const running = queue.filter((m) => m.status === 'running').length;
|
|
113
|
+
console.log(`[soak] cycle ${cycle} ended — completed=${totalCompleted} running=${running} total=${queue.length}`);
|
|
114
|
+
}
|
|
115
|
+
// Final reconciliation pass.
|
|
116
|
+
const final = new QueueScheduler({
|
|
117
|
+
queueFile: path.join(stateDir, 'agent-queue.json'),
|
|
118
|
+
maxConcurrent: 3,
|
|
119
|
+
pollIntervalMs: 250,
|
|
120
|
+
});
|
|
121
|
+
final.reconcileOrphans();
|
|
122
|
+
const finalQueue = final.list();
|
|
123
|
+
const finalCompleted = finalQueue.filter((m) => m.status === 'completed').length;
|
|
124
|
+
const finalPending = finalQueue.filter((m) => m.status === 'pending').length;
|
|
125
|
+
const finalRunning = finalQueue.filter((m) => m.status === 'running').length;
|
|
126
|
+
console.log(`[soak] final — total=${finalQueue.length} completed=${finalCompleted} pending=${finalPending} running=${finalRunning}`);
|
|
127
|
+
assertTrue(finalQueue.length === MISSION_COUNT, `total missions preserved (got ${finalQueue.length}, expected ${MISSION_COUNT})`);
|
|
128
|
+
assertTrue(finalRunning === 0, `no missions left orphaned in running (got ${finalRunning})`);
|
|
129
|
+
// Queue must still parse — proves AtomicWriter survived crashes.
|
|
130
|
+
const queueFile = path.join(stateDir, 'agent-queue.json');
|
|
131
|
+
assertTrue(fs.existsSync(queueFile), 'queue file present');
|
|
132
|
+
const parsed = JSON.parse(fs.readFileSync(queueFile, 'utf-8'));
|
|
133
|
+
assertTrue(Array.isArray(parsed) || (parsed && typeof parsed === 'object'), 'queue file parses');
|
|
134
|
+
fs.rmSync(sandbox, { recursive: true, force: true });
|
|
135
|
+
console.log(`[soak] cycles=${cycle}, missions=${MISSION_COUNT}, completed=${finalCompleted}, pending=${finalPending}`);
|
|
136
|
+
console.log('TEST_AUTONOMOUS_SOAK_OK');
|
|
137
|
+
}
|
|
138
|
+
main().catch((err) => {
|
|
139
|
+
console.error('[soak] FAILED:', err instanceof Error ? err.message : err);
|
|
140
|
+
process.exit(1);
|
|
141
|
+
});
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const BenchmarkEngine_1 = require("./orchestrator/BenchmarkEngine");
|
|
4
|
+
function assert(condition, message) {
|
|
5
|
+
if (!condition)
|
|
6
|
+
throw new Error(message);
|
|
7
|
+
}
|
|
8
|
+
async function main() {
|
|
9
|
+
const benchmark = new BenchmarkEngine_1.BenchmarkEngine();
|
|
10
|
+
const benchmarkId = `bench-${Date.now()}`;
|
|
11
|
+
benchmark.record({
|
|
12
|
+
benchmarkId,
|
|
13
|
+
label: 'agent:a',
|
|
14
|
+
entityId: 'agent-a',
|
|
15
|
+
entityType: 'agent',
|
|
16
|
+
score: 7.2,
|
|
17
|
+
reviewScore: 7.2,
|
|
18
|
+
durationMs: 9000,
|
|
19
|
+
fallbackUsed: false,
|
|
20
|
+
timestamp: new Date().toISOString()
|
|
21
|
+
});
|
|
22
|
+
benchmark.record({
|
|
23
|
+
benchmarkId,
|
|
24
|
+
label: 'agent:b',
|
|
25
|
+
entityId: 'agent-b',
|
|
26
|
+
entityType: 'agent',
|
|
27
|
+
score: 8.1,
|
|
28
|
+
reviewScore: 8.1,
|
|
29
|
+
durationMs: 11000,
|
|
30
|
+
fallbackUsed: false,
|
|
31
|
+
timestamp: new Date().toISOString()
|
|
32
|
+
});
|
|
33
|
+
const result = benchmark.compare(benchmarkId);
|
|
34
|
+
assert(result.samples.length === 2, 'benchmark should retain both samples');
|
|
35
|
+
assert(result.winner?.entityId === 'agent-b', 'benchmark should identify highest-scoring sample');
|
|
36
|
+
console.log('TEST_BENCHMARK_ENGINE_OK');
|
|
37
|
+
}
|
|
38
|
+
main().catch((error) => {
|
|
39
|
+
console.error(error.message || error);
|
|
40
|
+
process.exit(1);
|
|
41
|
+
});
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const Brain_1 = require("./orchestrator/Brain");
|
|
4
|
+
const brain = new Brain_1.Brain();
|
|
5
|
+
function assertContains(haystack, needle, label) {
|
|
6
|
+
if (!haystack.includes(needle))
|
|
7
|
+
throw new Error(`MISSING_FIELD[${label}]: "${needle}" not in: ${haystack}`);
|
|
8
|
+
}
|
|
9
|
+
const openaiErr = brain.formatProviderError('openai-api', 'gpt-5.4-mini', {
|
|
10
|
+
status: 401,
|
|
11
|
+
error: { message: 'Incorrect API key provided' }
|
|
12
|
+
}, { keyEnvVar: 'OPENAI_API_KEY_TEST_NOT_SET', expectedKeyPrefix: 'sk-' });
|
|
13
|
+
assertContains(openaiErr.message, 'openai-api', 'provider-tag');
|
|
14
|
+
assertContains(openaiErr.message, 'HTTP 401', 'http-status');
|
|
15
|
+
assertContains(openaiErr.message, 'Incorrect API key provided', 'api-message');
|
|
16
|
+
process.env.OPENAI_API_KEY_TEST_WRONG_PREFIX = 'gqwen-fake-key-1234';
|
|
17
|
+
const wrongPrefix = brain.formatProviderError('openai-api', 'gpt-test', {
|
|
18
|
+
status: 401,
|
|
19
|
+
error: { message: 'bad key' }
|
|
20
|
+
}, { keyEnvVar: 'OPENAI_API_KEY_TEST_WRONG_PREFIX', expectedKeyPrefix: 'sk-' });
|
|
21
|
+
assertContains(wrongPrefix.message, 'key prefix:', 'key-prefix-warning');
|
|
22
|
+
assertContains(wrongPrefix.message, 'gqwen-fa', 'key-prefix-value');
|
|
23
|
+
delete process.env.OPENAI_API_KEY_TEST_WRONG_PREFIX;
|
|
24
|
+
process.env.OPENAI_API_KEY_TEST_OK_PREFIX = 'sk-proj-real-looking-key';
|
|
25
|
+
const okPrefix = brain.formatProviderError('openai-api', 'gpt-test', {
|
|
26
|
+
status: 401, error: { message: 'rate limited' }
|
|
27
|
+
}, { keyEnvVar: 'OPENAI_API_KEY_TEST_OK_PREFIX', expectedKeyPrefix: 'sk-' });
|
|
28
|
+
if (okPrefix.message.includes('key prefix:'))
|
|
29
|
+
throw new Error('SHOULD_NOT_WARN_ON_CORRECT_PREFIX');
|
|
30
|
+
delete process.env.OPENAI_API_KEY_TEST_OK_PREFIX;
|
|
31
|
+
const ollamaErr = brain.formatProviderError('ollama', 'llama3', {
|
|
32
|
+
status: 503,
|
|
33
|
+
message: 'Service Unavailable',
|
|
34
|
+
error: { message: 'model not loaded' }
|
|
35
|
+
});
|
|
36
|
+
assertContains(ollamaErr.message, 'HTTP 503', 'ollama-status');
|
|
37
|
+
assertContains(ollamaErr.message, 'model not loaded', 'ollama-body');
|
|
38
|
+
const cliErr = brain.formatProviderError('openai-cli', 'codex-default', {
|
|
39
|
+
stderr: 'codex: command not found\n',
|
|
40
|
+
message: 'spawn codex ENOENT'
|
|
41
|
+
});
|
|
42
|
+
assertContains(cliErr.message, 'stderr:', 'cli-stderr');
|
|
43
|
+
assertContains(cliErr.message, 'codex: command not found', 'cli-stderr-content');
|
|
44
|
+
const genericErr = brain.formatProviderError('gemini-api', 'gemini-3.1', { message: 'Connection error.' });
|
|
45
|
+
assertContains(genericErr.message, 'gemini-api', 'generic-provider');
|
|
46
|
+
assertContains(genericErr.message, 'Connection error.', 'generic-message');
|
|
47
|
+
const causedBy = brain.formatProviderError('openai-api', 'gpt-x', { status: 500, message: 'boom' });
|
|
48
|
+
const cause = causedBy.cause;
|
|
49
|
+
if (!cause || cause.status !== 500)
|
|
50
|
+
throw new Error('CAUSE_NOT_PRESERVED');
|
|
51
|
+
console.log('TEST_BRAIN_ERROR_DIAGNOSTICS_OK');
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const Brain_1 = require("./orchestrator/Brain");
|
|
4
|
+
function stubModelManager(brain, primary, fallbacks) {
|
|
5
|
+
brain.modelManager = {
|
|
6
|
+
getModelConfig: () => ({ primary, fallbacks })
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
async function testPrimaryFailsSecondaryWins() {
|
|
10
|
+
const brain = new Brain_1.Brain();
|
|
11
|
+
stubModelManager(brain, { raw: 'openai-api/gpt-test', provider: 'openai-api', name: 'gpt-test' }, [{ raw: 'gemini-api/gemini-test', provider: 'gemini-api', name: 'gemini-test' }]);
|
|
12
|
+
let primaryCalled = 0;
|
|
13
|
+
let secondaryCalled = 0;
|
|
14
|
+
brain.thinkWithOpenAIAPI = async () => {
|
|
15
|
+
primaryCalled++;
|
|
16
|
+
const err = new Error('forced primary failure');
|
|
17
|
+
err.status = 401;
|
|
18
|
+
err.error = { message: 'Incorrect API key provided' };
|
|
19
|
+
throw err;
|
|
20
|
+
};
|
|
21
|
+
brain.thinkWithGeminiAPI = async () => {
|
|
22
|
+
secondaryCalled++;
|
|
23
|
+
return 'response-from-fallback';
|
|
24
|
+
};
|
|
25
|
+
const out = await brain.think('sys', 'usr');
|
|
26
|
+
if (primaryCalled !== 1)
|
|
27
|
+
throw new Error(`PRIMARY_NOT_CALLED_ONCE: ${primaryCalled}`);
|
|
28
|
+
if (secondaryCalled !== 1)
|
|
29
|
+
throw new Error(`SECONDARY_NOT_CALLED_ONCE: ${secondaryCalled}`);
|
|
30
|
+
if (!out.includes('response-from-fallback'))
|
|
31
|
+
throw new Error(`FALLBACK_RESPONSE_NOT_RETURNED: ${out.slice(0, 150)}`);
|
|
32
|
+
}
|
|
33
|
+
async function testAllFailReturnsCriticalError() {
|
|
34
|
+
const brain = new Brain_1.Brain();
|
|
35
|
+
stubModelManager(brain, { raw: 'openai-api/x', provider: 'openai-api', name: 'x' }, [
|
|
36
|
+
{ raw: 'gemini-api/y', provider: 'gemini-api', name: 'y' },
|
|
37
|
+
{ raw: 'anthropic/z', provider: 'anthropic', name: 'z' }
|
|
38
|
+
]);
|
|
39
|
+
const calls = [];
|
|
40
|
+
brain.thinkWithOpenAIAPI = async () => { calls.push('openai-api'); throw new Error('openai-down'); };
|
|
41
|
+
brain.thinkWithGeminiAPI = async () => { calls.push('gemini-api'); throw new Error('gemini-down'); };
|
|
42
|
+
brain.thinkWithAnthropic = async () => { calls.push('anthropic'); throw new Error('anthropic-down'); };
|
|
43
|
+
const out = await brain.think('sys', 'usr');
|
|
44
|
+
if (calls.length !== 3)
|
|
45
|
+
throw new Error(`EXPECTED_3_CALLS_GOT_${calls.length}: ${calls.join(',')}`);
|
|
46
|
+
if (!out.includes('CRITICAL ERROR'))
|
|
47
|
+
throw new Error(`MISSING_CRITICAL_ERROR: ${out.slice(0, 200)}`);
|
|
48
|
+
if (!out.includes('openai-api/x') || !out.includes('gemini-api/y') || !out.includes('anthropic/z')) {
|
|
49
|
+
throw new Error(`CRITICAL_ERROR_MISSING_PROVIDER_FAILURES: ${out}`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
async function testOpenAIBaseURLApplied() {
|
|
53
|
+
process.env.OPENAI_API_KEY = 'sk-fake-for-construction-only';
|
|
54
|
+
process.env.OPENAI_BASE_URL = 'https://api.together.xyz/v1';
|
|
55
|
+
try {
|
|
56
|
+
const brain = new Brain_1.Brain();
|
|
57
|
+
const openaiClient = brain.openai;
|
|
58
|
+
if (!openaiClient)
|
|
59
|
+
throw new Error('OPENAI_CLIENT_NOT_CONSTRUCTED');
|
|
60
|
+
const inferredBase = openaiClient.baseURL || openaiClient._options?.baseURL;
|
|
61
|
+
if (typeof inferredBase === 'string' && !inferredBase.includes('together.xyz')) {
|
|
62
|
+
throw new Error(`BASE_URL_NOT_APPLIED: got ${inferredBase}`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
finally {
|
|
66
|
+
delete process.env.OPENAI_API_KEY;
|
|
67
|
+
delete process.env.OPENAI_BASE_URL;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
async function testOpenAIBaseURLDefaultUnset() {
|
|
71
|
+
process.env.OPENAI_API_KEY = 'sk-fake-default-base';
|
|
72
|
+
delete process.env.OPENAI_BASE_URL;
|
|
73
|
+
try {
|
|
74
|
+
const brain = new Brain_1.Brain();
|
|
75
|
+
const openaiClient = brain.openai;
|
|
76
|
+
if (!openaiClient)
|
|
77
|
+
throw new Error('OPENAI_CLIENT_NOT_CONSTRUCTED');
|
|
78
|
+
}
|
|
79
|
+
finally {
|
|
80
|
+
delete process.env.OPENAI_API_KEY;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
async function main() {
|
|
84
|
+
await testPrimaryFailsSecondaryWins();
|
|
85
|
+
await testAllFailReturnsCriticalError();
|
|
86
|
+
await testOpenAIBaseURLApplied();
|
|
87
|
+
await testOpenAIBaseURLDefaultUnset();
|
|
88
|
+
console.log('TEST_BRAIN_FALLBACK_CHAIN_OK');
|
|
89
|
+
}
|
|
90
|
+
main().catch((err) => {
|
|
91
|
+
console.error('TEST_BRAIN_FALLBACK_CHAIN_FAIL:', err?.message || err);
|
|
92
|
+
process.exit(1);
|
|
93
|
+
});
|