@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,240 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* test_workflow_e2e — Story 4.5 regression suite.
|
|
4
|
+
*
|
|
5
|
+
* Spawns `node bin/openlife.js workflow ...` as a real child process
|
|
6
|
+
* against fresh temp project roots. Asserts the CLI surface introduced
|
|
7
|
+
* by Story 4.3 + the 4 ported workflows from Story 4.4 work end-to-end:
|
|
8
|
+
*
|
|
9
|
+
* 1. `workflow list` — JSON envelope, count=4
|
|
10
|
+
* 2. `workflow validate <each ported file>` — ok:true for all 4
|
|
11
|
+
* 3. `workflow run <file>` against the simulated executor — exits 0,
|
|
12
|
+
* state file created under <tmp>/.openlife/workflows/<runId>.json
|
|
13
|
+
* 4. `workflow status <runId>` — reads back the persisted state
|
|
14
|
+
* 5. `workflow abort <runId>` — idempotent, exits 0 (or matches state)
|
|
15
|
+
* 6. `workflow validate <invalid-yaml-fixture>` — exits 1 with error
|
|
16
|
+
* envelope listing workflow_invalid_yaml or similar code
|
|
17
|
+
*
|
|
18
|
+
* Prints TEST_WORKFLOW_E2E_OK on full pass.
|
|
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 child_process_1 = require("child_process");
|
|
55
|
+
const fs = __importStar(require("fs"));
|
|
56
|
+
const os = __importStar(require("os"));
|
|
57
|
+
const path = __importStar(require("path"));
|
|
58
|
+
const REPO_ROOT = path.resolve(__dirname, '..');
|
|
59
|
+
const CLI = path.join(REPO_ROOT, 'bin', 'openlife.js');
|
|
60
|
+
const TIMEOUT_MS = 60_000;
|
|
61
|
+
function assert(cond, msg) {
|
|
62
|
+
if (!cond)
|
|
63
|
+
throw new Error(`assertion failed: ${msg}`);
|
|
64
|
+
}
|
|
65
|
+
function mkTmp() {
|
|
66
|
+
return fs.mkdtempSync(path.join(os.tmpdir(), 'wf-e2e-'));
|
|
67
|
+
}
|
|
68
|
+
function rmTmp(dir) {
|
|
69
|
+
try {
|
|
70
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
71
|
+
}
|
|
72
|
+
catch { /* ignore */ }
|
|
73
|
+
}
|
|
74
|
+
function run(args, cwd) {
|
|
75
|
+
return (0, child_process_1.spawnSync)(process.execPath, [CLI, ...args], {
|
|
76
|
+
cwd,
|
|
77
|
+
encoding: 'utf-8',
|
|
78
|
+
timeout: TIMEOUT_MS,
|
|
79
|
+
env: { ...process.env, OPENLIFE_STATE_DIR: path.join(cwd, '.openlife') },
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
function extractJson(stdout) {
|
|
83
|
+
// Prefer the largest balanced top-level JSON block in stdout (handles
|
|
84
|
+
// logs/warnings preceding the envelope).
|
|
85
|
+
let best = null;
|
|
86
|
+
let depth = 0;
|
|
87
|
+
let start = -1;
|
|
88
|
+
for (let i = 0; i < stdout.length; i++) {
|
|
89
|
+
const ch = stdout[i];
|
|
90
|
+
if (ch === '{') {
|
|
91
|
+
if (depth === 0)
|
|
92
|
+
start = i;
|
|
93
|
+
depth += 1;
|
|
94
|
+
}
|
|
95
|
+
else if (ch === '}') {
|
|
96
|
+
depth -= 1;
|
|
97
|
+
if (depth === 0 && start >= 0) {
|
|
98
|
+
const block = { start, end: i + 1 };
|
|
99
|
+
if (!best || (block.end - block.start) > (best.end - best.start))
|
|
100
|
+
best = block;
|
|
101
|
+
start = -1;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
if (!best)
|
|
106
|
+
throw new Error(`no JSON object found in stdout: ${stdout.slice(0, 200)}`);
|
|
107
|
+
return JSON.parse(stdout.slice(best.start, best.end));
|
|
108
|
+
}
|
|
109
|
+
// ─────────────────────────────────────────────────────────────
|
|
110
|
+
// Scenario 1 — `workflow list`
|
|
111
|
+
// ─────────────────────────────────────────────────────────────
|
|
112
|
+
function scenarioList() {
|
|
113
|
+
const tmp = mkTmp();
|
|
114
|
+
try {
|
|
115
|
+
const r = run(['workflow', 'list'], tmp);
|
|
116
|
+
assert(r.status === 0, `list exit code: ${r.status}, stderr: ${r.stderr}`);
|
|
117
|
+
const obj = extractJson(r.stdout);
|
|
118
|
+
assert(obj.ok === true, 'list must return ok:true');
|
|
119
|
+
assert(obj.count === 4, `expected 4 workflows, got ${obj.count}`);
|
|
120
|
+
const ids = obj.workflows.map((w) => w.id).sort();
|
|
121
|
+
assert(JSON.stringify(ids) === JSON.stringify(['brownfield-discovery', 'greenfield-fullstack', 'qa-loop', 'story-development-cycle']), `unexpected workflow ids: ${ids.join(', ')}`);
|
|
122
|
+
console.log('✅ scenario 1: workflow list (4 workflows)');
|
|
123
|
+
}
|
|
124
|
+
finally {
|
|
125
|
+
rmTmp(tmp);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
// ─────────────────────────────────────────────────────────────
|
|
129
|
+
// Scenario 2 — `workflow validate <each>`
|
|
130
|
+
// ─────────────────────────────────────────────────────────────
|
|
131
|
+
function scenarioValidateEach() {
|
|
132
|
+
const tmp = mkTmp();
|
|
133
|
+
const files = [
|
|
134
|
+
'dist-templates/workflows/story-development-cycle.yaml',
|
|
135
|
+
'dist-templates/workflows/greenfield-fullstack.yaml',
|
|
136
|
+
'dist-templates/workflows/brownfield-discovery.yaml',
|
|
137
|
+
'dist-templates/workflows/qa-loop.yaml',
|
|
138
|
+
];
|
|
139
|
+
try {
|
|
140
|
+
for (const f of files) {
|
|
141
|
+
const full = path.join(REPO_ROOT, f);
|
|
142
|
+
const r = run(['workflow', 'validate', full], tmp);
|
|
143
|
+
assert(r.status === 0, `validate ${f} exit: ${r.status}, stderr: ${r.stderr}`);
|
|
144
|
+
const obj = extractJson(r.stdout);
|
|
145
|
+
assert(obj.ok === true, `validate ${f} must return ok:true`);
|
|
146
|
+
assert(typeof obj.workflowId === 'string' && obj.workflowId.length > 0, `validate ${f} must include workflowId`);
|
|
147
|
+
}
|
|
148
|
+
console.log('✅ scenario 2: validate all 4 ported workflows');
|
|
149
|
+
}
|
|
150
|
+
finally {
|
|
151
|
+
rmTmp(tmp);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
// ─────────────────────────────────────────────────────────────
|
|
155
|
+
// Scenario 3 — `workflow run <file>` (simulated)
|
|
156
|
+
// ─────────────────────────────────────────────────────────────
|
|
157
|
+
function scenarioRun() {
|
|
158
|
+
const tmp = mkTmp();
|
|
159
|
+
const file = path.join(REPO_ROOT, 'dist-templates/workflows/story-development-cycle.yaml');
|
|
160
|
+
const r = run(['workflow', 'run', file], tmp);
|
|
161
|
+
assert(r.status === 0, `run exit: ${r.status}, stderr: ${r.stderr}`);
|
|
162
|
+
const obj = extractJson(r.stdout);
|
|
163
|
+
assert(obj.ok === true, 'run must return ok:true (status completed)');
|
|
164
|
+
assert(obj.state.status === 'completed', `expected status=completed, got ${obj.state.status}`);
|
|
165
|
+
assert(obj.state.completedSteps.length === 4, `expected 4 completed steps, got ${obj.state.completedSteps.length}`);
|
|
166
|
+
// state file on disk
|
|
167
|
+
const stateFile = path.join(tmp, '.openlife', 'workflows', `${obj.state.runId}.json`);
|
|
168
|
+
assert(fs.existsSync(stateFile), `state file must exist at ${stateFile}`);
|
|
169
|
+
console.log('✅ scenario 3: workflow run (simulated executor, 4 steps completed)');
|
|
170
|
+
return { runId: obj.state.runId, tmpRoot: tmp };
|
|
171
|
+
}
|
|
172
|
+
// ─────────────────────────────────────────────────────────────
|
|
173
|
+
// Scenario 4 — `workflow status <runId>`
|
|
174
|
+
// ─────────────────────────────────────────────────────────────
|
|
175
|
+
function scenarioStatus(runId, tmpRoot) {
|
|
176
|
+
const r = run(['workflow', 'status', runId], tmpRoot);
|
|
177
|
+
assert(r.status === 0, `status exit: ${r.status}, stderr: ${r.stderr}`);
|
|
178
|
+
const obj = extractJson(r.stdout);
|
|
179
|
+
assert(obj.ok === true, 'status must return ok:true for known runId');
|
|
180
|
+
assert(obj.state.runId === runId, 'runId round-trip');
|
|
181
|
+
assert(obj.state.status === 'completed', 'persisted state must reflect completion');
|
|
182
|
+
console.log('✅ scenario 4: workflow status (persisted state round-trip)');
|
|
183
|
+
}
|
|
184
|
+
// ─────────────────────────────────────────────────────────────
|
|
185
|
+
// Scenario 5 — `workflow abort <runId>` against completed (no-op)
|
|
186
|
+
// ─────────────────────────────────────────────────────────────
|
|
187
|
+
function scenarioAbortIdempotent(runId, tmpRoot) {
|
|
188
|
+
const r = run(['workflow', 'abort', runId], tmpRoot);
|
|
189
|
+
assert(r.status === 0, `abort exit: ${r.status}, stderr: ${r.stderr}`);
|
|
190
|
+
const obj = extractJson(r.stdout);
|
|
191
|
+
assert(obj.ok === true, 'abort must return ok:true');
|
|
192
|
+
// Already terminal (completed) — abort leaves it as completed (no transition)
|
|
193
|
+
assert(obj.state.status === 'completed', `abort on terminal must keep status, got ${obj.state.status}`);
|
|
194
|
+
console.log('✅ scenario 5: workflow abort idempotent on terminal run');
|
|
195
|
+
}
|
|
196
|
+
// ─────────────────────────────────────────────────────────────
|
|
197
|
+
// Scenario 6 — validate invalid YAML fixture
|
|
198
|
+
// ─────────────────────────────────────────────────────────────
|
|
199
|
+
function scenarioInvalidValidate() {
|
|
200
|
+
const tmp = mkTmp();
|
|
201
|
+
try {
|
|
202
|
+
const bad = path.join(tmp, 'bad.yaml');
|
|
203
|
+
fs.writeFileSync(bad, 'just: a string\nnot: a workflow\n', 'utf-8');
|
|
204
|
+
const r = run(['workflow', 'validate', bad], tmp);
|
|
205
|
+
assert(r.status === 1, `invalid validate must exit 1, got ${r.status}`);
|
|
206
|
+
const obj = extractJson(r.stdout);
|
|
207
|
+
assert(obj.ok === false, 'invalid validate must return ok:false');
|
|
208
|
+
assert(obj.errors.some((e) => e.code === 'workflow_missing_root'), `expected workflow_missing_root error, got ${obj.errors.map((e) => e.code).join(',')}`);
|
|
209
|
+
console.log('✅ scenario 6: validate invalid YAML (missing `workflow:` root)');
|
|
210
|
+
}
|
|
211
|
+
finally {
|
|
212
|
+
rmTmp(tmp);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
// ─────────────────────────────────────────────────────────────
|
|
216
|
+
// Driver
|
|
217
|
+
// ─────────────────────────────────────────────────────────────
|
|
218
|
+
function main() {
|
|
219
|
+
console.log('🧪 test_workflow_e2e — Story 4.5 regression suite');
|
|
220
|
+
scenarioList();
|
|
221
|
+
scenarioValidateEach();
|
|
222
|
+
const { runId, tmpRoot } = scenarioRun();
|
|
223
|
+
try {
|
|
224
|
+
scenarioStatus(runId, tmpRoot);
|
|
225
|
+
scenarioAbortIdempotent(runId, tmpRoot);
|
|
226
|
+
}
|
|
227
|
+
finally {
|
|
228
|
+
rmTmp(tmpRoot);
|
|
229
|
+
}
|
|
230
|
+
scenarioInvalidValidate();
|
|
231
|
+
console.log('');
|
|
232
|
+
console.log('TEST_WORKFLOW_E2E_OK');
|
|
233
|
+
}
|
|
234
|
+
try {
|
|
235
|
+
main();
|
|
236
|
+
}
|
|
237
|
+
catch (err) {
|
|
238
|
+
console.error('❌ test_workflow_e2e FAILED:', err instanceof Error ? err.message : err);
|
|
239
|
+
process.exit(1);
|
|
240
|
+
}
|
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* test_workflow_engine — Story 4.2 regression suite.
|
|
4
|
+
*
|
|
5
|
+
* Asserts the WorkflowEngine contract via 6 scenarios:
|
|
6
|
+
* 1. Happy path — workflow with 4 sequential executable steps + phase
|
|
7
|
+
* markers runs to `completed` with all steps marked, artifacts
|
|
8
|
+
* accumulated, checkpoint cleared at the end.
|
|
9
|
+
* 2. Elicit pause — step with elicit:true sets run status to
|
|
10
|
+
* `awaiting_input`; provideInput() resumes the run to completion.
|
|
11
|
+
* 3. Resume after simulated crash — run halted by injected executor
|
|
12
|
+
* failure on step 3 is resumable; subsequent resume() with a
|
|
13
|
+
* passing executor finishes all remaining steps.
|
|
14
|
+
* 4. Abort during run — abort() sets status=aborted and clears the
|
|
15
|
+
* checkpoint; idempotent on re-abort.
|
|
16
|
+
* 5. Optional + condition — optional step whose condition is not met
|
|
17
|
+
* in context is `skipped`, not `completed`; runs around it succeed.
|
|
18
|
+
* 6. Status / state file durability — status(runId) returns null for
|
|
19
|
+
* unknown ids; state file lives under <stateDir>/workflows/.
|
|
20
|
+
*
|
|
21
|
+
* Prints TEST_WORKFLOW_ENGINE_OK on full pass.
|
|
22
|
+
*/
|
|
23
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
24
|
+
if (k2 === undefined) k2 = k;
|
|
25
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
26
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
27
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
28
|
+
}
|
|
29
|
+
Object.defineProperty(o, k2, desc);
|
|
30
|
+
}) : (function(o, m, k, k2) {
|
|
31
|
+
if (k2 === undefined) k2 = k;
|
|
32
|
+
o[k2] = m[k];
|
|
33
|
+
}));
|
|
34
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
35
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
36
|
+
}) : function(o, v) {
|
|
37
|
+
o["default"] = v;
|
|
38
|
+
});
|
|
39
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
40
|
+
var ownKeys = function(o) {
|
|
41
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
42
|
+
var ar = [];
|
|
43
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
44
|
+
return ar;
|
|
45
|
+
};
|
|
46
|
+
return ownKeys(o);
|
|
47
|
+
};
|
|
48
|
+
return function (mod) {
|
|
49
|
+
if (mod && mod.__esModule) return mod;
|
|
50
|
+
var result = {};
|
|
51
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
52
|
+
__setModuleDefault(result, mod);
|
|
53
|
+
return result;
|
|
54
|
+
};
|
|
55
|
+
})();
|
|
56
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
57
|
+
const fs = __importStar(require("fs"));
|
|
58
|
+
const os = __importStar(require("os"));
|
|
59
|
+
const path = __importStar(require("path"));
|
|
60
|
+
const WorkflowEngine_1 = require("./orchestrator/workflow/WorkflowEngine");
|
|
61
|
+
function assert(cond, msg) {
|
|
62
|
+
if (!cond)
|
|
63
|
+
throw new Error(`assertion failed: ${msg}`);
|
|
64
|
+
}
|
|
65
|
+
function mkTmp() {
|
|
66
|
+
return fs.mkdtempSync(path.join(os.tmpdir(), 'wf-engine-test-'));
|
|
67
|
+
}
|
|
68
|
+
function rmTmp(dir) {
|
|
69
|
+
try {
|
|
70
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
71
|
+
}
|
|
72
|
+
catch { /* ignore */ }
|
|
73
|
+
}
|
|
74
|
+
function makeWorkflow(extra = {}) {
|
|
75
|
+
return {
|
|
76
|
+
id: 'test-wf',
|
|
77
|
+
name: 'Test',
|
|
78
|
+
type: 'custom',
|
|
79
|
+
version: '1.0',
|
|
80
|
+
sequence: [
|
|
81
|
+
{ phase: 0, name: 'Setup' },
|
|
82
|
+
{ id: 's1', agent: 'dev', action: 'do-a', creates: ['a.md'] },
|
|
83
|
+
{ id: 's2', agent: 'dev', action: 'do-b', creates: ['b.md'], requires: 's1' },
|
|
84
|
+
{ phase: 1, name: 'Cleanup' },
|
|
85
|
+
{ id: 's3', agent: 'dev', action: 'do-c', creates: ['c.md'], requires: 's2' },
|
|
86
|
+
{ id: 's4', agent: 'dev', action: 'do-d', updates: ['c.md'], requires: 's3' },
|
|
87
|
+
],
|
|
88
|
+
...extra,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
class ScriptedExecutor {
|
|
92
|
+
rules;
|
|
93
|
+
constructor(rules) {
|
|
94
|
+
this.rules = rules;
|
|
95
|
+
}
|
|
96
|
+
async execute(inv) {
|
|
97
|
+
const r = this.rules[inv.step.id];
|
|
98
|
+
if (!r) {
|
|
99
|
+
const artifacts = [];
|
|
100
|
+
if (Array.isArray(inv.step.creates))
|
|
101
|
+
artifacts.push(...inv.step.creates);
|
|
102
|
+
if (Array.isArray(inv.step.updates))
|
|
103
|
+
artifacts.push(...inv.step.updates);
|
|
104
|
+
return { status: 'completed', artifacts, result: 'default' };
|
|
105
|
+
}
|
|
106
|
+
return r;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
// ─────────────────────────────────────────────────────────────
|
|
110
|
+
// Scenario 1 — happy path
|
|
111
|
+
// ─────────────────────────────────────────────────────────────
|
|
112
|
+
async function scenarioHappyPath() {
|
|
113
|
+
const tmp = mkTmp();
|
|
114
|
+
const prev = process.env.OPENLIFE_STATE_DIR;
|
|
115
|
+
try {
|
|
116
|
+
process.env.OPENLIFE_STATE_DIR = path.join(tmp, '.openlife');
|
|
117
|
+
const engine = new WorkflowEngine_1.WorkflowEngine();
|
|
118
|
+
const wf = makeWorkflow();
|
|
119
|
+
const state = await engine.run(wf);
|
|
120
|
+
assert(state.status === 'completed', `expected completed, got ${state.status}`);
|
|
121
|
+
assert(state.completedSteps.length === 4, `expected 4 completed, got ${state.completedSteps.length}`);
|
|
122
|
+
assert(state.artifacts.includes('a.md') && state.artifacts.includes('c.md'), 'artifacts must accumulate');
|
|
123
|
+
// state file written
|
|
124
|
+
const stateFile = path.join(engine._stateDir(), 'workflows', `${state.runId}.json`);
|
|
125
|
+
assert(fs.existsSync(stateFile), 'state file must exist');
|
|
126
|
+
// events log written
|
|
127
|
+
const eventsFile = path.join(engine._stateDir(), 'workflows', 'events', `${state.runId}.jsonl`);
|
|
128
|
+
assert(fs.existsSync(eventsFile), 'events log must exist');
|
|
129
|
+
const events = fs.readFileSync(eventsFile, 'utf-8').split('\n').filter((l) => l.length > 0);
|
|
130
|
+
assert(events.length >= 6, `expected at least 6 events (run.started + 4 step + run.completed), got ${events.length}`);
|
|
131
|
+
console.log('✅ scenario 1: happy path');
|
|
132
|
+
}
|
|
133
|
+
finally {
|
|
134
|
+
if (prev === undefined)
|
|
135
|
+
delete process.env.OPENLIFE_STATE_DIR;
|
|
136
|
+
else
|
|
137
|
+
process.env.OPENLIFE_STATE_DIR = prev;
|
|
138
|
+
rmTmp(tmp);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
// ─────────────────────────────────────────────────────────────
|
|
142
|
+
// Scenario 2 — elicit pause + provideInput resumes
|
|
143
|
+
// ─────────────────────────────────────────────────────────────
|
|
144
|
+
async function scenarioElicit() {
|
|
145
|
+
const tmp = mkTmp();
|
|
146
|
+
const prev = process.env.OPENLIFE_STATE_DIR;
|
|
147
|
+
try {
|
|
148
|
+
process.env.OPENLIFE_STATE_DIR = path.join(tmp, '.openlife');
|
|
149
|
+
const engine = new WorkflowEngine_1.WorkflowEngine();
|
|
150
|
+
const wf = {
|
|
151
|
+
id: 'elicit-wf',
|
|
152
|
+
name: 'Elicit',
|
|
153
|
+
type: 'custom',
|
|
154
|
+
version: '1.0',
|
|
155
|
+
sequence: [
|
|
156
|
+
{ id: 'a', agent: 'dev' },
|
|
157
|
+
{ id: 'gate', agent: 'pm', elicit: true },
|
|
158
|
+
{ id: 'b', agent: 'dev' },
|
|
159
|
+
],
|
|
160
|
+
};
|
|
161
|
+
const state = await engine.run(wf);
|
|
162
|
+
assert(state.status === 'awaiting_input', `expected awaiting_input, got ${state.status}`);
|
|
163
|
+
assert(state.currentStep === 'gate', `expected currentStep=gate, got ${state.currentStep ?? 'none'}`);
|
|
164
|
+
assert(state.completedSteps.length === 1, 'only `a` should be done before elicit');
|
|
165
|
+
const resumed = await engine.provideInput(wf, state.runId, 'gate', { answer: 'yes' });
|
|
166
|
+
assert(resumed.status === 'completed', `expected completed after provideInput, got ${resumed.status}`);
|
|
167
|
+
assert(resumed.completedSteps.length === 3, `expected 3 completed, got ${resumed.completedSteps.length}`);
|
|
168
|
+
console.log('✅ scenario 2: elicit pause + provideInput');
|
|
169
|
+
}
|
|
170
|
+
finally {
|
|
171
|
+
if (prev === undefined)
|
|
172
|
+
delete process.env.OPENLIFE_STATE_DIR;
|
|
173
|
+
else
|
|
174
|
+
process.env.OPENLIFE_STATE_DIR = prev;
|
|
175
|
+
rmTmp(tmp);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
// ─────────────────────────────────────────────────────────────
|
|
179
|
+
// Scenario 3 — resume after injected step failure
|
|
180
|
+
// ─────────────────────────────────────────────────────────────
|
|
181
|
+
async function scenarioResume() {
|
|
182
|
+
const tmp = mkTmp();
|
|
183
|
+
const prev = process.env.OPENLIFE_STATE_DIR;
|
|
184
|
+
try {
|
|
185
|
+
process.env.OPENLIFE_STATE_DIR = path.join(tmp, '.openlife');
|
|
186
|
+
// First run: executor fails on step 3
|
|
187
|
+
const failing = new WorkflowEngine_1.WorkflowEngine({
|
|
188
|
+
executor: new ScriptedExecutor({
|
|
189
|
+
s3: { status: 'failed', error: 'simulated network drop' },
|
|
190
|
+
}),
|
|
191
|
+
});
|
|
192
|
+
const wf = makeWorkflow();
|
|
193
|
+
const first = await failing.run(wf);
|
|
194
|
+
assert(first.status === 'failed', `expected failed, got ${first.status}`);
|
|
195
|
+
assert(first.completedSteps.length === 2, `expected 2 done before failure, got ${first.completedSteps.length}`);
|
|
196
|
+
// Mark as recoverable by flipping status back to running (in real life
|
|
197
|
+
// a resume would happen after process restart; we simulate by setting
|
|
198
|
+
// status to running and re-invoking resume with a passing executor).
|
|
199
|
+
// resume() expects non-terminal status; we mutate state for the test.
|
|
200
|
+
const stateFile = path.join(failing._stateDir(), 'workflows', `${first.runId}.json`);
|
|
201
|
+
const raw = JSON.parse(fs.readFileSync(stateFile, 'utf-8'));
|
|
202
|
+
raw.status = 'running';
|
|
203
|
+
raw.failureReason = undefined;
|
|
204
|
+
raw.completedAt = undefined;
|
|
205
|
+
fs.writeFileSync(stateFile, JSON.stringify(raw, null, 2));
|
|
206
|
+
// Resume with passing executor
|
|
207
|
+
const passing = new WorkflowEngine_1.WorkflowEngine({ executor: new ScriptedExecutor({}) });
|
|
208
|
+
const resumed = await passing.resume(wf, first.runId);
|
|
209
|
+
assert(resumed.status === 'completed', `expected completed on resume, got ${resumed.status}`);
|
|
210
|
+
assert(resumed.completedSteps.length === 4, `expected 4 done after resume, got ${resumed.completedSteps.length}`);
|
|
211
|
+
assert(resumed.completedSteps.includes('s3'), 'resumed run must complete the previously-failed step');
|
|
212
|
+
console.log('✅ scenario 3: resume after injected failure');
|
|
213
|
+
}
|
|
214
|
+
finally {
|
|
215
|
+
if (prev === undefined)
|
|
216
|
+
delete process.env.OPENLIFE_STATE_DIR;
|
|
217
|
+
else
|
|
218
|
+
process.env.OPENLIFE_STATE_DIR = prev;
|
|
219
|
+
rmTmp(tmp);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
// ─────────────────────────────────────────────────────────────
|
|
223
|
+
// Scenario 4 — abort
|
|
224
|
+
// ─────────────────────────────────────────────────────────────
|
|
225
|
+
async function scenarioAbort() {
|
|
226
|
+
const tmp = mkTmp();
|
|
227
|
+
const prev = process.env.OPENLIFE_STATE_DIR;
|
|
228
|
+
try {
|
|
229
|
+
process.env.OPENLIFE_STATE_DIR = path.join(tmp, '.openlife');
|
|
230
|
+
const engine = new WorkflowEngine_1.WorkflowEngine();
|
|
231
|
+
const wf = {
|
|
232
|
+
id: 'abort-wf',
|
|
233
|
+
name: 'Abort',
|
|
234
|
+
type: 'custom',
|
|
235
|
+
version: '1.0',
|
|
236
|
+
sequence: [
|
|
237
|
+
{ id: 'a', agent: 'dev', elicit: true }, // pauses immediately
|
|
238
|
+
],
|
|
239
|
+
};
|
|
240
|
+
const state = await engine.run(wf);
|
|
241
|
+
assert(state.status === 'awaiting_input', 'must pause on elicit');
|
|
242
|
+
const aborted = engine.abort(state.runId, 'test reason');
|
|
243
|
+
assert(aborted !== null && aborted.status === 'aborted', 'must mark as aborted');
|
|
244
|
+
assert(aborted.failureReason === 'test reason', 'reason must be recorded');
|
|
245
|
+
// Idempotent on second call
|
|
246
|
+
const again = engine.abort(state.runId, 'another reason');
|
|
247
|
+
assert(again?.status === 'aborted' && again.failureReason === 'test reason', 'abort must be idempotent (preserves first reason)');
|
|
248
|
+
console.log('✅ scenario 4: abort + idempotency');
|
|
249
|
+
}
|
|
250
|
+
finally {
|
|
251
|
+
if (prev === undefined)
|
|
252
|
+
delete process.env.OPENLIFE_STATE_DIR;
|
|
253
|
+
else
|
|
254
|
+
process.env.OPENLIFE_STATE_DIR = prev;
|
|
255
|
+
rmTmp(tmp);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
// ─────────────────────────────────────────────────────────────
|
|
259
|
+
// Scenario 5 — optional + condition not met → skipped
|
|
260
|
+
// ─────────────────────────────────────────────────────────────
|
|
261
|
+
async function scenarioOptionalCondition() {
|
|
262
|
+
const tmp = mkTmp();
|
|
263
|
+
const prev = process.env.OPENLIFE_STATE_DIR;
|
|
264
|
+
try {
|
|
265
|
+
process.env.OPENLIFE_STATE_DIR = path.join(tmp, '.openlife');
|
|
266
|
+
const engine = new WorkflowEngine_1.WorkflowEngine();
|
|
267
|
+
const wf = {
|
|
268
|
+
id: 'cond-wf',
|
|
269
|
+
name: 'Cond',
|
|
270
|
+
type: 'custom',
|
|
271
|
+
version: '1.0',
|
|
272
|
+
sequence: [
|
|
273
|
+
{ id: 'always', agent: 'dev' },
|
|
274
|
+
{ id: 'maybe', agent: 'dev', optional: true, condition: 'feature_enabled' },
|
|
275
|
+
{ id: 'always-2', agent: 'dev' },
|
|
276
|
+
],
|
|
277
|
+
};
|
|
278
|
+
const state = await engine.run(wf, { other_flag: true });
|
|
279
|
+
assert(state.status === 'completed', `expected completed, got ${state.status}`);
|
|
280
|
+
assert(state.steps['maybe'].status === 'skipped', 'maybe must be skipped when condition unmet');
|
|
281
|
+
assert(state.steps['always'].status === 'completed', 'always must complete');
|
|
282
|
+
assert(state.steps['always-2'].status === 'completed', 'always-2 must complete');
|
|
283
|
+
console.log('✅ scenario 5: optional + condition (skipped when unmet)');
|
|
284
|
+
}
|
|
285
|
+
finally {
|
|
286
|
+
if (prev === undefined)
|
|
287
|
+
delete process.env.OPENLIFE_STATE_DIR;
|
|
288
|
+
else
|
|
289
|
+
process.env.OPENLIFE_STATE_DIR = prev;
|
|
290
|
+
rmTmp(tmp);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
// ─────────────────────────────────────────────────────────────
|
|
294
|
+
// Scenario 6 — status() returns null for unknown id
|
|
295
|
+
// ─────────────────────────────────────────────────────────────
|
|
296
|
+
function scenarioStatusUnknown() {
|
|
297
|
+
const tmp = mkTmp();
|
|
298
|
+
const prev = process.env.OPENLIFE_STATE_DIR;
|
|
299
|
+
try {
|
|
300
|
+
process.env.OPENLIFE_STATE_DIR = path.join(tmp, '.openlife');
|
|
301
|
+
const engine = new WorkflowEngine_1.WorkflowEngine();
|
|
302
|
+
assert(engine.status('does-not-exist') === null, 'status must return null for unknown id');
|
|
303
|
+
console.log('✅ scenario 6: status(unknown) returns null');
|
|
304
|
+
}
|
|
305
|
+
finally {
|
|
306
|
+
if (prev === undefined)
|
|
307
|
+
delete process.env.OPENLIFE_STATE_DIR;
|
|
308
|
+
else
|
|
309
|
+
process.env.OPENLIFE_STATE_DIR = prev;
|
|
310
|
+
rmTmp(tmp);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
// ─────────────────────────────────────────────────────────────
|
|
314
|
+
// Driver
|
|
315
|
+
// ─────────────────────────────────────────────────────────────
|
|
316
|
+
async function main() {
|
|
317
|
+
console.log('🧪 test_workflow_engine — Story 4.2 regression suite');
|
|
318
|
+
await scenarioHappyPath();
|
|
319
|
+
await scenarioElicit();
|
|
320
|
+
await scenarioResume();
|
|
321
|
+
await scenarioAbort();
|
|
322
|
+
await scenarioOptionalCondition();
|
|
323
|
+
scenarioStatusUnknown();
|
|
324
|
+
console.log('');
|
|
325
|
+
console.log('TEST_WORKFLOW_ENGINE_OK');
|
|
326
|
+
}
|
|
327
|
+
main().catch((err) => {
|
|
328
|
+
console.error('❌ test_workflow_engine FAILED:', err instanceof Error ? err.message : err);
|
|
329
|
+
process.exit(1);
|
|
330
|
+
});
|