@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,232 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* ConditionParser — tiny boolean expression engine for workflow step
|
|
4
|
+
* conditions.
|
|
5
|
+
*
|
|
6
|
+
* Story 10.3 — OpenLife v1.4.
|
|
7
|
+
*
|
|
8
|
+
* Replaces the v1.2/v1.3 literal-only `conditionMet()` in WorkflowEngine.
|
|
9
|
+
*
|
|
10
|
+
* Grammar (precedence-climbing, lowest to highest):
|
|
11
|
+
* expr := orExpr
|
|
12
|
+
* orExpr := andExpr ( "OR" andExpr )*
|
|
13
|
+
* andExpr := notExpr ( "AND" notExpr )*
|
|
14
|
+
* notExpr := "NOT" notExpr | primary
|
|
15
|
+
* primary := "(" expr ")" | comparison | identifier
|
|
16
|
+
* comparison := identifier ( "==" | "!=" ) literal
|
|
17
|
+
* identifier := [A-Za-z_][A-Za-z0-9_.]*
|
|
18
|
+
* literal := number | "true" | "false" | "null" | quoted-string
|
|
19
|
+
*
|
|
20
|
+
* Backward-compat:
|
|
21
|
+
* A bare identifier `foo` evaluates to `ctx.foo === true` (same as v1.2).
|
|
22
|
+
*
|
|
23
|
+
* Never throws. On parse failure, evaluate() returns false and the
|
|
24
|
+
* `parse()` result carries the error.
|
|
25
|
+
*/
|
|
26
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
27
|
+
exports.parseCondition = parseCondition;
|
|
28
|
+
exports.evaluate = evaluate;
|
|
29
|
+
exports.evaluateCondition = evaluateCondition;
|
|
30
|
+
function tokenize(input) {
|
|
31
|
+
const tokens = [];
|
|
32
|
+
let i = 0;
|
|
33
|
+
while (i < input.length) {
|
|
34
|
+
const c = input[i];
|
|
35
|
+
if (/\s/.test(c)) {
|
|
36
|
+
i++;
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
if (c === '(' || c === ')') {
|
|
40
|
+
tokens.push({ type: 'op', value: c });
|
|
41
|
+
i++;
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
if (c === '=' && input[i + 1] === '=') {
|
|
45
|
+
tokens.push({ type: 'op', value: '==' });
|
|
46
|
+
i += 2;
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
if (c === '!' && input[i + 1] === '=') {
|
|
50
|
+
tokens.push({ type: 'op', value: '!=' });
|
|
51
|
+
i += 2;
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
if (c === '"' || c === "'") {
|
|
55
|
+
const end = input.indexOf(c, i + 1);
|
|
56
|
+
if (end === -1)
|
|
57
|
+
return null; // unterminated
|
|
58
|
+
tokens.push({ type: 'string', value: input.slice(i + 1, end) });
|
|
59
|
+
i = end + 1;
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
if (/[0-9-]/.test(c)) {
|
|
63
|
+
const m = input.slice(i).match(/^-?\d+(?:\.\d+)?/);
|
|
64
|
+
if (m) {
|
|
65
|
+
tokens.push({ type: 'number', value: Number(m[0]) });
|
|
66
|
+
i += m[0].length;
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
if (/[A-Za-z_]/.test(c)) {
|
|
71
|
+
const m = input.slice(i).match(/^[A-Za-z_][A-Za-z0-9_.]*/);
|
|
72
|
+
if (m) {
|
|
73
|
+
const word = m[0];
|
|
74
|
+
if (word === 'AND')
|
|
75
|
+
tokens.push({ type: 'op', value: 'AND' });
|
|
76
|
+
else if (word === 'OR')
|
|
77
|
+
tokens.push({ type: 'op', value: 'OR' });
|
|
78
|
+
else if (word === 'NOT')
|
|
79
|
+
tokens.push({ type: 'op', value: 'NOT' });
|
|
80
|
+
else if (word === 'true')
|
|
81
|
+
tokens.push({ type: 'bool', value: true });
|
|
82
|
+
else if (word === 'false')
|
|
83
|
+
tokens.push({ type: 'bool', value: false });
|
|
84
|
+
else if (word === 'null')
|
|
85
|
+
tokens.push({ type: 'null' });
|
|
86
|
+
else
|
|
87
|
+
tokens.push({ type: 'ident', value: word });
|
|
88
|
+
i += word.length;
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return null; // unknown char
|
|
93
|
+
}
|
|
94
|
+
return tokens;
|
|
95
|
+
}
|
|
96
|
+
// ─── Parser (recursive-descent, precedence-climbing) ────────
|
|
97
|
+
class Parser {
|
|
98
|
+
tokens;
|
|
99
|
+
pos = 0;
|
|
100
|
+
constructor(tokens) {
|
|
101
|
+
this.tokens = tokens;
|
|
102
|
+
}
|
|
103
|
+
parseTopLevel() {
|
|
104
|
+
const node = this.parseOr();
|
|
105
|
+
if (this.pos < this.tokens.length)
|
|
106
|
+
return null; // leftover tokens
|
|
107
|
+
return node;
|
|
108
|
+
}
|
|
109
|
+
peek() { return this.tokens[this.pos]; }
|
|
110
|
+
consume() { return this.tokens[this.pos++]; }
|
|
111
|
+
parseOr() {
|
|
112
|
+
let left = this.parseAnd();
|
|
113
|
+
if (!left)
|
|
114
|
+
return null;
|
|
115
|
+
while (this.peek()?.type === 'op' && this.peek().value === 'OR') {
|
|
116
|
+
this.consume();
|
|
117
|
+
const right = this.parseAnd();
|
|
118
|
+
if (!right)
|
|
119
|
+
return null;
|
|
120
|
+
left = { kind: 'or', left, right };
|
|
121
|
+
}
|
|
122
|
+
return left;
|
|
123
|
+
}
|
|
124
|
+
parseAnd() {
|
|
125
|
+
let left = this.parseNot();
|
|
126
|
+
if (!left)
|
|
127
|
+
return null;
|
|
128
|
+
while (this.peek()?.type === 'op' && this.peek().value === 'AND') {
|
|
129
|
+
this.consume();
|
|
130
|
+
const right = this.parseNot();
|
|
131
|
+
if (!right)
|
|
132
|
+
return null;
|
|
133
|
+
left = { kind: 'and', left, right };
|
|
134
|
+
}
|
|
135
|
+
return left;
|
|
136
|
+
}
|
|
137
|
+
parseNot() {
|
|
138
|
+
if (this.peek()?.type === 'op' && this.peek().value === 'NOT') {
|
|
139
|
+
this.consume();
|
|
140
|
+
const inner = this.parseNot();
|
|
141
|
+
if (!inner)
|
|
142
|
+
return null;
|
|
143
|
+
return { kind: 'not', inner };
|
|
144
|
+
}
|
|
145
|
+
return this.parsePrimary();
|
|
146
|
+
}
|
|
147
|
+
parsePrimary() {
|
|
148
|
+
const tok = this.peek();
|
|
149
|
+
if (!tok)
|
|
150
|
+
return null;
|
|
151
|
+
if (tok.type === 'op' && tok.value === '(') {
|
|
152
|
+
this.consume();
|
|
153
|
+
const inner = this.parseOr();
|
|
154
|
+
const close = this.consume();
|
|
155
|
+
if (!close || close.type !== 'op' || close.value !== ')')
|
|
156
|
+
return null;
|
|
157
|
+
return inner;
|
|
158
|
+
}
|
|
159
|
+
if (tok.type === 'ident') {
|
|
160
|
+
this.consume();
|
|
161
|
+
// Check for comparison operator
|
|
162
|
+
const next = this.peek();
|
|
163
|
+
if (next?.type === 'op' && (next.value === '==' || next.value === '!=')) {
|
|
164
|
+
const opTok = this.consume();
|
|
165
|
+
const litTok = this.consume();
|
|
166
|
+
if (!litTok)
|
|
167
|
+
return null;
|
|
168
|
+
let lit;
|
|
169
|
+
if (litTok.type === 'string')
|
|
170
|
+
lit = litTok.value;
|
|
171
|
+
else if (litTok.type === 'number')
|
|
172
|
+
lit = litTok.value;
|
|
173
|
+
else if (litTok.type === 'bool')
|
|
174
|
+
lit = litTok.value;
|
|
175
|
+
else if (litTok.type === 'null')
|
|
176
|
+
lit = null;
|
|
177
|
+
else
|
|
178
|
+
return null;
|
|
179
|
+
return { kind: opTok.value === '==' ? 'eq' : 'neq', ident: tok.value, literal: lit };
|
|
180
|
+
}
|
|
181
|
+
return { kind: 'truthy', ident: tok.value };
|
|
182
|
+
}
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
// ─── Public API ────────────────────────────────────────────
|
|
187
|
+
function parseCondition(input) {
|
|
188
|
+
const tokens = tokenize(input);
|
|
189
|
+
if (!tokens)
|
|
190
|
+
return { ok: false, error: 'tokenize_failed' };
|
|
191
|
+
if (tokens.length === 0)
|
|
192
|
+
return { ok: false, error: 'empty_expression' };
|
|
193
|
+
const ast = new Parser(tokens).parseTopLevel();
|
|
194
|
+
if (!ast)
|
|
195
|
+
return { ok: false, error: 'parse_failed' };
|
|
196
|
+
return { ok: true, ast };
|
|
197
|
+
}
|
|
198
|
+
/** Resolve `a.b.c` against a context object. Returns undefined on miss. */
|
|
199
|
+
function resolveIdent(ident, ctx) {
|
|
200
|
+
const parts = ident.split('.');
|
|
201
|
+
let cur = ctx;
|
|
202
|
+
for (const p of parts) {
|
|
203
|
+
if (cur === null || cur === undefined || typeof cur !== 'object')
|
|
204
|
+
return undefined;
|
|
205
|
+
cur = cur[p];
|
|
206
|
+
}
|
|
207
|
+
return cur;
|
|
208
|
+
}
|
|
209
|
+
function evaluate(ast, ctx) {
|
|
210
|
+
switch (ast.kind) {
|
|
211
|
+
case 'and': return evaluate(ast.left, ctx) && evaluate(ast.right, ctx);
|
|
212
|
+
case 'or': return evaluate(ast.left, ctx) || evaluate(ast.right, ctx);
|
|
213
|
+
case 'not': return !evaluate(ast.inner, ctx);
|
|
214
|
+
case 'truthy': return resolveIdent(ast.ident, ctx) === true;
|
|
215
|
+
case 'eq': return resolveIdent(ast.ident, ctx) === ast.literal;
|
|
216
|
+
case 'neq': return resolveIdent(ast.ident, ctx) !== ast.literal;
|
|
217
|
+
default: return false;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* One-shot helper used by WorkflowEngine. Parses + evaluates in one call.
|
|
222
|
+
* Backwards-compatible: empty string returns true (no condition = pass).
|
|
223
|
+
* Parse failures return false (refuse to run if expression is broken).
|
|
224
|
+
*/
|
|
225
|
+
function evaluateCondition(expr, ctx) {
|
|
226
|
+
if (!expr || expr.trim() === '')
|
|
227
|
+
return true;
|
|
228
|
+
const r = parseCondition(expr);
|
|
229
|
+
if (!r.ok || !r.ast)
|
|
230
|
+
return false;
|
|
231
|
+
return evaluate(r.ast, ctx);
|
|
232
|
+
}
|
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* WorkflowEngine — execute, checkpoint, resume, and abort workflow runs.
|
|
4
|
+
*
|
|
5
|
+
* Story 4.2 — OpenLife v1.2 Royal Stack.
|
|
6
|
+
*
|
|
7
|
+
* The engine is the maestro. It iterates the workflow `sequence`,
|
|
8
|
+
* dispatches each executable step through an injected
|
|
9
|
+
* `WorkflowStepExecutor` (default = Gatekeeper-backed), records
|
|
10
|
+
* progress to `.openlife/workflows/<runId>.json` via AtomicWriter,
|
|
11
|
+
* appends a step-level event log to `.openlife/workflows/events/
|
|
12
|
+
* <runId>.jsonl`, and saves a MissionCheckpoint after each successful
|
|
13
|
+
* step.
|
|
14
|
+
*
|
|
15
|
+
* Crash safety: every state transition is durable. If the daemon dies
|
|
16
|
+
* mid-step, calling `resume(runId)` reads the last checkpoint and
|
|
17
|
+
* continues from the next pending step (idempotent steps may re-execute
|
|
18
|
+
* once; non-idempotent are skipped to avoid double-side-effects).
|
|
19
|
+
*
|
|
20
|
+
* Elicit support: a step with `elicit: true` causes the engine to set
|
|
21
|
+
* run status to `awaiting_input` and return control. The caller must
|
|
22
|
+
* later invoke `provideInput(runId, stepId, payload)` to resume.
|
|
23
|
+
*/
|
|
24
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
25
|
+
if (k2 === undefined) k2 = k;
|
|
26
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
27
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
28
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
29
|
+
}
|
|
30
|
+
Object.defineProperty(o, k2, desc);
|
|
31
|
+
}) : (function(o, m, k, k2) {
|
|
32
|
+
if (k2 === undefined) k2 = k;
|
|
33
|
+
o[k2] = m[k];
|
|
34
|
+
}));
|
|
35
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
36
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
37
|
+
}) : function(o, v) {
|
|
38
|
+
o["default"] = v;
|
|
39
|
+
});
|
|
40
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
41
|
+
var ownKeys = function(o) {
|
|
42
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
43
|
+
var ar = [];
|
|
44
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
45
|
+
return ar;
|
|
46
|
+
};
|
|
47
|
+
return ownKeys(o);
|
|
48
|
+
};
|
|
49
|
+
return function (mod) {
|
|
50
|
+
if (mod && mod.__esModule) return mod;
|
|
51
|
+
var result = {};
|
|
52
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
53
|
+
__setModuleDefault(result, mod);
|
|
54
|
+
return result;
|
|
55
|
+
};
|
|
56
|
+
})();
|
|
57
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
58
|
+
exports.WorkflowEngine = exports.SimulatedStepExecutor = void 0;
|
|
59
|
+
const crypto = __importStar(require("crypto"));
|
|
60
|
+
const fs = __importStar(require("fs"));
|
|
61
|
+
const path = __importStar(require("path"));
|
|
62
|
+
const ToolsetGuard_1 = require("../toolset/ToolsetGuard");
|
|
63
|
+
const WorkflowSchema_1 = require("./WorkflowSchema");
|
|
64
|
+
const AtomicWriter_1 = require("../util/AtomicWriter");
|
|
65
|
+
const MissionState_1 = require("../MissionState");
|
|
66
|
+
// ─────────────────────────────────────────────────────────────
|
|
67
|
+
// Default executor — simulated mode. Reports each step as completed
|
|
68
|
+
// without invoking real LLMs. Story 4.2 ships this as the v1.2
|
|
69
|
+
// minimum; deeper Gatekeeper integration is wired in Epic 5 once the
|
|
70
|
+
// AIOBuilder CLI commands materialize.
|
|
71
|
+
// ─────────────────────────────────────────────────────────────
|
|
72
|
+
class SimulatedStepExecutor {
|
|
73
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
74
|
+
async execute(inv) {
|
|
75
|
+
if (inv.step.elicit) {
|
|
76
|
+
return { status: 'awaiting_input' };
|
|
77
|
+
}
|
|
78
|
+
const artifacts = [];
|
|
79
|
+
if (Array.isArray(inv.step.creates))
|
|
80
|
+
artifacts.push(...inv.step.creates);
|
|
81
|
+
if (Array.isArray(inv.step.updates))
|
|
82
|
+
artifacts.push(...inv.step.updates);
|
|
83
|
+
return {
|
|
84
|
+
status: 'completed',
|
|
85
|
+
artifacts,
|
|
86
|
+
result: JSON.stringify({
|
|
87
|
+
simulated: true,
|
|
88
|
+
stepId: inv.step.id,
|
|
89
|
+
agent: inv.step.agent,
|
|
90
|
+
squad: inv.step.squad,
|
|
91
|
+
action: inv.step.action,
|
|
92
|
+
}),
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
exports.SimulatedStepExecutor = SimulatedStepExecutor;
|
|
97
|
+
class WorkflowEngine {
|
|
98
|
+
stateDir;
|
|
99
|
+
executor;
|
|
100
|
+
checkpointStore;
|
|
101
|
+
constructor(opts = {}) {
|
|
102
|
+
this.stateDir = opts.stateDir
|
|
103
|
+
|| process.env.OPENLIFE_STATE_DIR
|
|
104
|
+
|| path.join(process.cwd(), '.openlife');
|
|
105
|
+
this.executor = opts.executor || new SimulatedStepExecutor();
|
|
106
|
+
this.checkpointStore = opts.checkpointStore || new MissionState_1.MissionCheckpointStore();
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Execute a workflow from scratch. Returns the final WorkflowRunState
|
|
110
|
+
* (the caller is responsible for inspecting `status` — terminal states
|
|
111
|
+
* are `completed`, `failed`, `aborted`, or `awaiting_input`).
|
|
112
|
+
*/
|
|
113
|
+
async run(workflow, context = {}, opts = {}) {
|
|
114
|
+
(0, ToolsetGuard_1.assertToolsetAllowed)('workflows', 'WorkflowEngine.run');
|
|
115
|
+
// v1.3 — service mode requires explicit opt-in (Story 2.3).
|
|
116
|
+
// If the workflow declares mode: 'service', the caller MUST pass
|
|
117
|
+
// { allowServiceMode: true }. Silent task → service promotion refused.
|
|
118
|
+
const declaredMode = workflow.mode || 'task';
|
|
119
|
+
if (declaredMode === 'service' && !opts.allowServiceMode) {
|
|
120
|
+
const err = new Error(`workflow '${workflow.id}' declares mode='service' but caller did not pass allowServiceMode=true; service mode is explicit-only`);
|
|
121
|
+
err.code = 'service_mode_requires_explicit_opt_in';
|
|
122
|
+
throw err;
|
|
123
|
+
}
|
|
124
|
+
const runId = this.newRunId(workflow.id);
|
|
125
|
+
const state = {
|
|
126
|
+
runId,
|
|
127
|
+
workflowId: workflow.id,
|
|
128
|
+
status: 'pending',
|
|
129
|
+
startedAt: new Date().toISOString(),
|
|
130
|
+
completedSteps: [],
|
|
131
|
+
steps: {},
|
|
132
|
+
artifacts: [],
|
|
133
|
+
context: { ...context, __mode: declaredMode },
|
|
134
|
+
};
|
|
135
|
+
this.persistState(state);
|
|
136
|
+
this.emitEvent({
|
|
137
|
+
ts: new Date().toISOString(),
|
|
138
|
+
runId,
|
|
139
|
+
type: 'run.started',
|
|
140
|
+
detail: { workflowId: workflow.id, mode: declaredMode },
|
|
141
|
+
});
|
|
142
|
+
state.status = 'running';
|
|
143
|
+
this.persistState(state);
|
|
144
|
+
return await this.executeFrom(workflow, state, 0);
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Read the persisted state for a run without modifying it.
|
|
148
|
+
*/
|
|
149
|
+
status(runId) {
|
|
150
|
+
const filePath = this.stateFilePath(runId);
|
|
151
|
+
if (!fs.existsSync(filePath))
|
|
152
|
+
return null;
|
|
153
|
+
try {
|
|
154
|
+
return JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Resume a previously-paused or crashed run. Requires the original
|
|
162
|
+
* Workflow (the engine does not persist workflow definitions — only
|
|
163
|
+
* runs of them, identified by `workflowId` in state).
|
|
164
|
+
*/
|
|
165
|
+
async resume(workflow, runId) {
|
|
166
|
+
const state = this.status(runId);
|
|
167
|
+
if (!state) {
|
|
168
|
+
throw new Error(`workflow run not found: ${runId}`);
|
|
169
|
+
}
|
|
170
|
+
if (state.status === 'completed' || state.status === 'failed' || state.status === 'aborted') {
|
|
171
|
+
return state; // terminal — no-op
|
|
172
|
+
}
|
|
173
|
+
if (state.workflowId !== workflow.id) {
|
|
174
|
+
throw new Error(`workflowId mismatch: state has '${state.workflowId}', supplied '${workflow.id}'`);
|
|
175
|
+
}
|
|
176
|
+
this.emitEvent({ ts: new Date().toISOString(), runId, type: 'run.resumed', detail: { fromStatus: state.status } });
|
|
177
|
+
// Find the index to resume from: first non-completed executable step
|
|
178
|
+
const resumeIdx = this.findResumeIndex(workflow.sequence, state);
|
|
179
|
+
state.status = 'running';
|
|
180
|
+
this.persistState(state);
|
|
181
|
+
return await this.executeFrom(workflow, state, resumeIdx);
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Mark a run as aborted. Idempotent.
|
|
185
|
+
*/
|
|
186
|
+
abort(runId, reason = 'manual') {
|
|
187
|
+
const state = this.status(runId);
|
|
188
|
+
if (!state)
|
|
189
|
+
return null;
|
|
190
|
+
if (state.status === 'completed' || state.status === 'failed' || state.status === 'aborted') {
|
|
191
|
+
return state;
|
|
192
|
+
}
|
|
193
|
+
state.status = 'aborted';
|
|
194
|
+
state.failureReason = reason;
|
|
195
|
+
state.completedAt = new Date().toISOString();
|
|
196
|
+
this.persistState(state);
|
|
197
|
+
this.emitEvent({ ts: new Date().toISOString(), runId, type: 'run.aborted', detail: { reason } });
|
|
198
|
+
this.checkpointStore.clear(runId);
|
|
199
|
+
return state;
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Provide user input for a step that paused with elicit. Re-runs that
|
|
203
|
+
* step with the input merged into context.
|
|
204
|
+
*/
|
|
205
|
+
async provideInput(workflow, runId, stepId, payload) {
|
|
206
|
+
const state = this.status(runId);
|
|
207
|
+
if (!state)
|
|
208
|
+
throw new Error(`workflow run not found: ${runId}`);
|
|
209
|
+
if (state.status !== 'awaiting_input' || state.currentStep !== stepId) {
|
|
210
|
+
throw new Error(`run ${runId} is not awaiting input on step ${stepId} (status=${state.status}, currentStep=${state.currentStep ?? 'none'})`);
|
|
211
|
+
}
|
|
212
|
+
state.context = { ...state.context, [`elicit:${stepId}`]: payload };
|
|
213
|
+
state.status = 'running';
|
|
214
|
+
this.persistState(state);
|
|
215
|
+
// Mark this step's elicit gate as satisfied so the executor can proceed.
|
|
216
|
+
// The simplest approach: re-run from this step's index, but clear its
|
|
217
|
+
// elicit flag in-memory for this run only.
|
|
218
|
+
const idx = workflow.sequence.findIndex((e) => (0, WorkflowSchema_1.isExecutableStep)(e) && e.id === stepId);
|
|
219
|
+
if (idx < 0)
|
|
220
|
+
throw new Error(`step ${stepId} not in workflow ${workflow.id}`);
|
|
221
|
+
// Clone the workflow with elicit=false on this specific step so the
|
|
222
|
+
// resumed run advances past it.
|
|
223
|
+
const patched = {
|
|
224
|
+
...workflow,
|
|
225
|
+
sequence: workflow.sequence.map((e) => {
|
|
226
|
+
if ((0, WorkflowSchema_1.isExecutableStep)(e) && e.id === stepId)
|
|
227
|
+
return { ...e, elicit: false };
|
|
228
|
+
return e;
|
|
229
|
+
}),
|
|
230
|
+
};
|
|
231
|
+
return await this.executeFrom(patched, state, idx);
|
|
232
|
+
}
|
|
233
|
+
// ─────────────────────────────────────────────────────────
|
|
234
|
+
// Internals
|
|
235
|
+
// ─────────────────────────────────────────────────────────
|
|
236
|
+
async executeFrom(workflow, state, startIdx) {
|
|
237
|
+
for (let i = startIdx; i < workflow.sequence.length; i++) {
|
|
238
|
+
const entry = workflow.sequence[i];
|
|
239
|
+
if (!(0, WorkflowSchema_1.isExecutableStep)(entry)) {
|
|
240
|
+
// Phase marker — narrative only, no execution
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
const step = entry;
|
|
244
|
+
// Skip already-completed steps (resume path)
|
|
245
|
+
if (state.completedSteps.includes(step.id)) {
|
|
246
|
+
continue;
|
|
247
|
+
}
|
|
248
|
+
// Optional step + condition not met → skip cleanly
|
|
249
|
+
if (step.optional && !this.conditionMet(step, state)) {
|
|
250
|
+
state.steps[step.id] = {
|
|
251
|
+
stepId: step.id,
|
|
252
|
+
status: 'skipped',
|
|
253
|
+
completedAt: new Date().toISOString(),
|
|
254
|
+
};
|
|
255
|
+
state.completedSteps.push(step.id);
|
|
256
|
+
this.persistState(state);
|
|
257
|
+
this.emitEvent({ ts: new Date().toISOString(), runId: state.runId, type: 'step.skipped', stepId: step.id, detail: { reason: 'optional_condition_not_met' } });
|
|
258
|
+
continue;
|
|
259
|
+
}
|
|
260
|
+
// Begin step
|
|
261
|
+
const stepRec = {
|
|
262
|
+
stepId: step.id,
|
|
263
|
+
status: 'running',
|
|
264
|
+
startedAt: new Date().toISOString(),
|
|
265
|
+
};
|
|
266
|
+
state.steps[step.id] = stepRec;
|
|
267
|
+
state.currentStep = step.id;
|
|
268
|
+
this.persistState(state);
|
|
269
|
+
this.emitEvent({ ts: new Date().toISOString(), runId: state.runId, type: 'step.started', stepId: step.id });
|
|
270
|
+
let result;
|
|
271
|
+
try {
|
|
272
|
+
result = await this.executor.execute({ runId: state.runId, step, context: state.context || {} });
|
|
273
|
+
}
|
|
274
|
+
catch (err) {
|
|
275
|
+
stepRec.status = 'failed';
|
|
276
|
+
stepRec.error = err instanceof Error ? err.message : String(err);
|
|
277
|
+
stepRec.completedAt = new Date().toISOString();
|
|
278
|
+
state.status = 'failed';
|
|
279
|
+
state.failureReason = `step ${step.id} threw: ${stepRec.error}`;
|
|
280
|
+
state.completedAt = new Date().toISOString();
|
|
281
|
+
this.persistState(state);
|
|
282
|
+
this.emitEvent({ ts: new Date().toISOString(), runId: state.runId, type: 'step.failed', stepId: step.id, detail: { error: stepRec.error } });
|
|
283
|
+
this.emitEvent({ ts: new Date().toISOString(), runId: state.runId, type: 'run.failed', detail: { failedStep: step.id } });
|
|
284
|
+
return state;
|
|
285
|
+
}
|
|
286
|
+
if (result.status === 'awaiting_input') {
|
|
287
|
+
state.status = 'awaiting_input';
|
|
288
|
+
stepRec.status = 'running'; // stays running until input arrives
|
|
289
|
+
this.persistState(state);
|
|
290
|
+
this.emitEvent({ ts: new Date().toISOString(), runId: state.runId, type: 'run.awaiting_input', stepId: step.id });
|
|
291
|
+
return state;
|
|
292
|
+
}
|
|
293
|
+
if (result.status === 'failed') {
|
|
294
|
+
stepRec.status = 'failed';
|
|
295
|
+
stepRec.error = result.error || 'unknown failure';
|
|
296
|
+
stepRec.completedAt = new Date().toISOString();
|
|
297
|
+
state.status = 'failed';
|
|
298
|
+
state.failureReason = `step ${step.id} failed: ${stepRec.error}`;
|
|
299
|
+
state.completedAt = new Date().toISOString();
|
|
300
|
+
this.persistState(state);
|
|
301
|
+
this.emitEvent({ ts: new Date().toISOString(), runId: state.runId, type: 'step.failed', stepId: step.id, detail: { error: stepRec.error } });
|
|
302
|
+
this.emitEvent({ ts: new Date().toISOString(), runId: state.runId, type: 'run.failed', detail: { failedStep: step.id } });
|
|
303
|
+
return state;
|
|
304
|
+
}
|
|
305
|
+
// completed or skipped — both advance the run
|
|
306
|
+
stepRec.status = result.status;
|
|
307
|
+
stepRec.completedAt = new Date().toISOString();
|
|
308
|
+
stepRec.artifacts = result.artifacts;
|
|
309
|
+
stepRec.result = result.result;
|
|
310
|
+
state.completedSteps.push(step.id);
|
|
311
|
+
if (result.artifacts)
|
|
312
|
+
state.artifacts.push(...result.artifacts);
|
|
313
|
+
state.currentStep = undefined;
|
|
314
|
+
state.lastCheckpointAt = new Date().toISOString();
|
|
315
|
+
this.persistState(state);
|
|
316
|
+
this.checkpointStore.saveCheckpoint(state.runId, step.id, {
|
|
317
|
+
workflowId: workflow.id,
|
|
318
|
+
completedSteps: state.completedSteps,
|
|
319
|
+
artifacts: state.artifacts,
|
|
320
|
+
});
|
|
321
|
+
this.emitEvent({
|
|
322
|
+
ts: new Date().toISOString(),
|
|
323
|
+
runId: state.runId,
|
|
324
|
+
type: result.status === 'completed' ? 'step.completed' : 'step.skipped',
|
|
325
|
+
stepId: step.id,
|
|
326
|
+
detail: { artifacts: result.artifacts || [] },
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
// All steps processed → mark run completed
|
|
330
|
+
state.status = 'completed';
|
|
331
|
+
state.completedAt = new Date().toISOString();
|
|
332
|
+
this.persistState(state);
|
|
333
|
+
this.emitEvent({ ts: new Date().toISOString(), runId: state.runId, type: 'run.completed', detail: { totalSteps: state.completedSteps.length } });
|
|
334
|
+
this.checkpointStore.clear(state.runId);
|
|
335
|
+
return state;
|
|
336
|
+
}
|
|
337
|
+
conditionMet(step, state) {
|
|
338
|
+
if (!step.condition)
|
|
339
|
+
return true;
|
|
340
|
+
// v1.4 (Story 10.3) — full expression engine: AND / OR / NOT / == / !=
|
|
341
|
+
// plus parentheses. Backward-compat: a bare identifier `foo` still
|
|
342
|
+
// evaluates to `ctx.foo === true`.
|
|
343
|
+
const { evaluateCondition } = require('./ConditionParser');
|
|
344
|
+
const ctx = (state.context || {});
|
|
345
|
+
return evaluateCondition(step.condition, ctx);
|
|
346
|
+
}
|
|
347
|
+
findResumeIndex(sequence, state) {
|
|
348
|
+
for (let i = 0; i < sequence.length; i++) {
|
|
349
|
+
const e = sequence[i];
|
|
350
|
+
if (!(0, WorkflowSchema_1.isExecutableStep)(e))
|
|
351
|
+
continue;
|
|
352
|
+
if (!state.completedSteps.includes(e.id))
|
|
353
|
+
return i;
|
|
354
|
+
}
|
|
355
|
+
return sequence.length;
|
|
356
|
+
}
|
|
357
|
+
persistState(state) {
|
|
358
|
+
(0, AtomicWriter_1.writeJsonAtomic)(this.stateFilePath(state.runId), state);
|
|
359
|
+
}
|
|
360
|
+
emitEvent(event) {
|
|
361
|
+
(0, AtomicWriter_1.appendJsonlAtomic)(this.eventLogPath(event.runId), event);
|
|
362
|
+
}
|
|
363
|
+
stateFilePath(runId) {
|
|
364
|
+
return path.join(this.stateDir, 'workflows', `${runId}.json`);
|
|
365
|
+
}
|
|
366
|
+
eventLogPath(runId) {
|
|
367
|
+
return path.join(this.stateDir, 'workflows', 'events', `${runId}.jsonl`);
|
|
368
|
+
}
|
|
369
|
+
newRunId(workflowId) {
|
|
370
|
+
const ts = Date.now().toString(36);
|
|
371
|
+
const rnd = crypto.randomBytes(4).toString('hex');
|
|
372
|
+
return `${workflowId}-${ts}-${rnd}`;
|
|
373
|
+
}
|
|
374
|
+
/** Exposed for tests so they can assert resolved storage location. */
|
|
375
|
+
_stateDir() {
|
|
376
|
+
return this.stateDir;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
exports.WorkflowEngine = WorkflowEngine;
|