diff --git a/.codex-synaptic/project.json b/.codex-synaptic/project.json new file mode 100644 index 0000000..afc4730 --- /dev/null +++ b/.codex-synaptic/project.json @@ -0,0 +1,16 @@ +{ + "$schema": "./project.schema.json", + "projectId": "codex-synaptic", + "launchGate": { + "required": true, + "strictJsonCommand": "codex-synaptic launch --strict --json" + }, + "daemon": { + "stateDir": "~/.codex-synaptic", + "allowStateInsideRepo": false + }, + "optionalEngines": { + "ruflo": true, + "ruvFann": true + } +} diff --git a/.codex-synaptic/project.schema.json b/.codex-synaptic/project.schema.json new file mode 100644 index 0000000..87ccb4f --- /dev/null +++ b/.codex-synaptic/project.schema.json @@ -0,0 +1,33 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "required": ["projectId", "launchGate", "daemon"], + "properties": { + "projectId": { "type": "string", "minLength": 1 }, + "launchGate": { + "type": "object", + "required": ["required", "strictJsonCommand"], + "properties": { + "required": { "type": "boolean" }, + "strictJsonCommand": { "type": "string" } + } + }, + "daemon": { + "type": "object", + "required": ["stateDir", "allowStateInsideRepo"], + "properties": { + "stateDir": { "type": "string" }, + "allowStateInsideRepo": { "type": "boolean" } + } + }, + "optionalEngines": { + "type": "object", + "properties": { + "ruflo": { "type": "boolean" }, + "ruvFann": { "type": "boolean" } + }, + "additionalProperties": false + } + }, + "additionalProperties": false +} diff --git a/.codex/config.toml b/.codex/config.toml new file mode 100644 index 0000000..c79cc39 --- /dev/null +++ b/.codex/config.toml @@ -0,0 +1,20 @@ +[project] +root = "." +trusted = true +sandbox = "workspace-write" +approvals = "on-request" + +[paths] +restrict_to_project_root = true + +[mcp_servers.filesystem] +command = "npx" +args = ["-y", "@modelcontextprotocol/server-filesystem", "."] + +[mcp_servers.playwright] +command = "npx" +args = ["-y", "@playwright/mcp@latest"] + +[mcp_servers.desktop_commander] +command = "npx" +args = ["-y", "@modelcontextprotocol/server-desktop-commander"] diff --git a/AGENTS.md b/AGENTS.md index 02ad9b0..3bf128c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,497 +1,59 @@ -# AGENTS.md - Codex-Synaptic Agent System Architecture +# AGENTS.md — Codex-Synaptic Launch-Gated Operating Contract -## Overview +This repository is operated as a **global CLI + daemon appliance** for Codex (macOS app + VS Code extension). -The Codex-Synaptic system enhances OpenAI's Codex with advanced multi-agent capabilities, featuring MCP/A2A bridging, neural meshes, swarm coordination, topological constraints, and various consensus mechanisms. This document outlines the comprehensive agent architecture and deployment strategies. +## 1) Launch Gate is mandatory -## Operator Notes — 2025-11-05 - -- **Consensus gating:** ✅ **RESOLVED** — Hive-mind runs now automatically deploy 3+ voting agents (consensus_coordinator, review_worker, planning_worker) to ensure RAFT consensus quorum is reached. The quorum threshold has been adjusted to 2 votes minimum (40% quorum factor) for improved availability while maintaining consensus integrity. -- **Autoscaler behaviour:** With the background daemon disabled, idle worker retirement requests cannot execute. Expect scale-down warnings in logs and manually right-size replicas after experiments. See `docs/runbooks/autoscaler-daemon-coordination.md` for operational guidance. -- **Repository hygiene:** Active development is running from the local `codex-synaptic-clone` directory, but upstream pushes must target `github.com/clduab11/codex-synaptic`. Align the folder/remote names before release packaging so automation recipes resolve assets correctly. See `docs/runbooks/workspace-rename-guide.md` for the step-by-step procedure. - -## Startup Gate (Codex For macOS) - -- Run `codex-synaptic launch --json` before repository work whenever the user asks to launch or verify readiness first. -- Treat launch as a hard gate: if `ok` is `false` (or the command exits non-zero), stop and only return remediation commands. -- Proceed with repository changes only when launch returns `ok=true` and `nextAction="continue"`. -- Default launch gate profiles are `mcp-filesystem`, `mcp-playwright`, and `mcp-desktop-commander`. - -## Core Agent Types - -### 1. Worker Agents -Base execution units that perform specific computational tasks. - -#### CodeWorker -- **Purpose**: Code generation, analysis, and refactoring -- **Capabilities**: Language-specific code generation, syntax analysis, optimization -- **Interface**: Codex API integration, custom code processing pipelines -- **Deployment**: Single instance or clustered for parallel processing - -#### DataWorker -- **Purpose**: Data processing, transformation, and analysis -- **Capabilities**: ETL operations, statistical analysis, ML preprocessing -- **Interface**: Database connectors, file system access, streaming data -- **Deployment**: Distributed across data sources - -#### ValidationWorker -- **Purpose**: Code validation, testing, and quality assurance -- **Capabilities**: Static analysis, test generation, security scanning -- **Interface**: CI/CD integration, testing frameworks -- **Deployment**: Pipeline integration, on-demand validation - -#### ResearchWorker -- **Purpose**: Repository reconnaissance and intelligence gathering for Tree-of-Thought planning -- **Capabilities**: Contextual research, insight synthesis, knowledge gap detection -- **Interface**: Documentation corpus, memory system, telemetry feeds -- **Deployment**: Paired with CodeWorkers to keep implementation grounded in latest findings - -#### ArchitectWorker -- **Purpose**: Design resilient swarm architectures and rollout strategies -- **Capabilities**: Topology assessment, constraint modelling, phased rollout design -- **Interface**: Neural mesh topology, consensus records, swarm coordinator telemetry -- **Deployment**: Activated upfront for major platform upgrades and backlog governance loops - -#### KnowledgeWorker -- **Purpose**: Distil swarm artefacts into documentation and broadcast updates -- **Capabilities**: Knowledge synthesis, documentation drafting, communication packaging -- **Interface**: Persistent memory, docs/, operator channels -- **Deployment**: Runs after ReAcT/ToT cycles to keep humans and agents aligned - -#### GoalPlanner -- **Purpose**: Drive Goal-Oriented Action Planning (GOAP) workflows for complex, multi-step objectives -- **Capabilities**: A* search-based plan synthesis, precondition/effect tracking, adaptive replanning with OODA feedback loops -- **Interface**: GOAP action manifests under `user-projects/**/goap/`, reasoning planner APIs, consensus manager for approval gates -- **Deployment**: Activated when tasks demand structured decomposition, such as bounty hunts, compliance remediation, or multi-domain rollouts. See the [goal-planner brief](https://gist.github.com/ruvnet/d5d7686dd96d83fc7ef8968cb57d4b57) for the canonical response template. - -#### AnalystWorker -- **Purpose**: Generate heatmaps, scorecards, and analytical briefs to prioritise work -- **Capabilities**: Metric synthesis, risk diagnostics, hotspot reporting -- **Interface**: Telemetry streams, workflow artefacts -- **Deployment**: Activated before planning to sharpen backlog focus - -#### SecurityWorker -- **Purpose**: Safeguard the platform with static reviews and threat modelling -- **Capabilities**: Security diagnostics, mitigation planning, guardrail definition -- **Interface**: Code diffs, automation scripts, consensus history -- **Deployment**: Required for high-risk or external-facing changes - -#### OpsWorker -- **Purpose**: Produce operational runbooks and snapshots for sustained reliability -- **Capabilities**: Incident playbooks, escalation matrices, ops telemetry curation -- **Interface**: Mesh health metrics, consensus alerts, CLI automation -- **Deployment**: Ensures human operators have actionable guidance during swarms - -#### PerformanceWorker -- **Purpose**: Uncover performance hotspots and define optimisation benchmarks -- **Capabilities**: Profiling plans, benchmark design, hotspot recommendation -- **Interface**: Profilers, observability dashboards, automation traces -- **Deployment**: Run before scaling swarms or rolling out automation - -#### IntegrationWorker -- **Purpose**: Map Codex-Synaptic workflows to external systems and services -- **Capabilities**: Interface contracts, compatibility matrices, integration stages -- **Interface**: MCP bridge endpoints, external API specs -- **Deployment**: Used when wiring Codex-Synaptic into CI/CD, observability, or partner stacks - -#### SimulationWorker -- **Purpose**: Stress-test scenarios with what-if simulations prior to rollout -- **Capabilities**: Monte Carlo scenario modelling, risk envelope analysis -- **Interface**: Swarm coordinator, consensus manager, memory snapshots -- **Deployment**: Validates complex upgrades or automation changes before production - -#### MemoryWorker -- **Purpose**: Curate persistent knowledge and keep reasoning artefacts fresh -- **Capabilities**: Namespace audits, archival strategies, tagging and promotion -- **Interface**: Codex memory system, docs/, telemetry logs -- **Deployment**: Scheduled to keep `tot_runs` and related namespaces healthy - -#### PlanningWorker -- **Purpose**: Draft strategic roadmaps and translate insights into executable phases -- **Capabilities**: Phase planning, success metrics, roadmap refinement -- **Interface**: Tree-of-Thought outputs, analyst briefs, architecture blueprints -- **Deployment**: Aligns stakeholders before deep swarm engagements - -#### ReviewWorker -- **Purpose**: Summarise diffs and enforce quality gates prior to consensus -- **Capabilities**: Checklist generation, highlight extraction, approval preparation -- **Interface**: Code diffs, automation plans, compliance requirements -- **Deployment**: Supports automation worker and consensus coordinator hand-offs - -#### CommunicationWorker -- **Purpose**: Broadcast relevant updates to human operators and partner teams -- **Capabilities**: Message composition, digest creation, channel targeting -- **Interface**: Docs, memory artefacts, observability dashboards -- **Deployment**: Ensures transparency throughout multi-phase upgrades - -#### AutomationWorker -- **Purpose**: Design automation runbooks, guardrails, and rollout scripts -- **Capabilities**: Workflow scripting, safeguards, trigger definition -- **Interface**: CLI commands, task scheduler, consensus manager -- **Deployment**: Drives repeatable follow-up execution without sacrificing governance - -#### ObservabilityWorker -- **Purpose**: Keep telemetry coverage aligned with evolving automation -- **Capabilities**: Dashboard curation, alert tuning, instrumentation planning -- **Interface**: observability backends, swarm metrics, consensus telemetry -- **Deployment**: Run before and after major releases - -#### ComplianceWorker -- **Purpose**: Align swarm automation with policy and regulatory requirements -- **Capabilities**: Compliance gap analysis, policy drafting, retention auditing -- **Interface**: Memory retention policies, consensus audit logs, documentation -- **Deployment**: Mandatory for regulated workloads or enterprise rollouts - -#### ReliabilityWorker -- **Purpose**: Protect uptime and resilience across mesh, swarm, and consensus layers -- **Capabilities**: Reliability reviews, chaos experiment design, improvement recommendations -- **Interface**: Health monitor, auto-scaler, telemetry -- **Deployment**: Scheduled to keep SLAs on track and expose resilience gaps - - -### 4. Platform Capabilities - -- **Auto-scaling Manager** – monitors CPU/memory and triggers balanced worker scale-up/down, persisting events to `autoscaler_events`. -- **Self-Healing Mesh** – repairs topology gaps automatically and logs activity to `mesh_events`. -- **Vector Memory Service** – optional local/Qdrant/Redis vector store for knowledge retrieval; managed via `vector` config. -- **Cheat Code Catalog** – consult `docs/codex-synaptic-cheat-codes.md` for command combos. -- **Observability Toolkit** – see `docs/observability/README.md` for dashboard + metrics wiring. -- **Consensus Telemetry** – decisions are archived under `consensus_events` for audit trails. - -#### Consensus Voting Roles - -The Codex-Synaptic consensus system employs **distributed decision-making** where multiple agent types participate in voting to ensure decisions benefit from diverse domain expertise: - -**Voting Agent Types:** -- **ConsensusCoordinator** – Specialized in consensus protocols and quorum management -- **ReviewWorker** – Provides quality assurance and code review perspective -- **PlanningWorker** – Contributes strategic planning and roadmap considerations - -**Rationale:** -By enabling three distinct agent types to vote, the system achieves more balanced decision-making. Review and planning agents bring domain-specific context that complements pure consensus coordination, reducing the risk of overlooking quality or strategic concerns during approval gates. - -**Configuration:** -The quorum requirements are defined in `config/system.json` under the `consensus` section: -- `minVotes`: Minimum number of votes required (default: 2) -- `quorumFactor`: Fraction of available voting agents needed (default: 0.4 or 40%) -- With 3+ voting agents deployed by default, this ensures at least 2-of-3 quorum for reliability - -**Deployment:** -The system automatically deploys voting agents during bootstrap (`bootstrapDefaultAgents()`) and hive-mind spawns (`analyzePromptForAgents()`), ensuring consensus workflows never timeout due to insufficient quorum. - -### 2. Coordinator Agents -Higher-level agents that manage and orchestrate worker agents. - -#### SwarmCoordinator -- **Purpose**: Multi-agent task distribution and synchronization -- **Capabilities**: Load balancing, task scheduling, resource optimization -- **Interface**: Agent registry, task queue management -- **Deployment**: Central coordination nodes - -#### ConsensusCoordinator -- **Purpose**: Distributed decision making and agreement protocols -- **Capabilities**: Byzantine fault tolerance, RAFT consensus, voting mechanisms -- **Interface**: Peer-to-peer communication, state synchronization -- **Deployment**: Distributed consensus network - -#### TopologyCoordinator -- **Purpose**: Network structure management and optimization -- **Capabilities**: Dynamic topology adjustment, path optimization, load distribution -- **Interface**: Network graph management, routing protocols -- **Deployment**: Edge and core network positions - -### 3. Bridge Agents -Specialized agents for inter-system communication and protocol translation. - -#### MCPBridge (Model Control Protocol) -- **Purpose**: Seamless integration between different AI models and systems -- **Capabilities**: Protocol translation, model orchestration, API abstraction -- **Interface**: Multi-model API support, standardized communication protocols -- **Deployment**: Gateway and proxy configurations - -#### A2ABridge (Agent-to-Agent) -- **Purpose**: Direct agent communication and collaboration -- **Capabilities**: Secure messaging, capability discovery, task delegation -- **Interface**: Encrypted communication channels, identity verification -- **Deployment**: Mesh network topology - -## Neural Mesh Architecture - -### Mesh Topology -The neural mesh creates an interconnected network of agents that can: -- Share computational resources dynamically -- Distribute cognitive load across multiple nodes -- Enable emergent collective intelligence -- Provide fault tolerance through redundancy - -### Mesh Components - -#### NeuralNode -- Base unit of the neural mesh -- Encapsulates agent logic and state -- Provides standardized interfaces for mesh communication -- Supports hot-swapping and live updates - -#### MeshRouter -- Routes information and tasks through the mesh -- Optimizes paths based on node capabilities and load -- Maintains mesh topology and health monitoring -- Handles node discovery and registration - -#### SynapticConnection -- Direct communication links between nodes -- Supports different connection types (synchronous, asynchronous, streaming) -- Provides quality of service guarantees -- Enables bandwidth and latency optimization - -## Swarm Coordination Mechanisms - -### Coordination Patterns - -#### Hierarchical Coordination -- Tree-like command structure -- Clear authority and delegation chains -- Suitable for structured, predictable tasks -- Centralized decision making with distributed execution - -#### Emergent Coordination -- Self-organizing agent behaviors -- Decentralized decision making -- Adaptive to changing conditions -- Suitable for dynamic, unpredictable environments - -#### Hybrid Coordination -- Combines hierarchical and emergent patterns -- Flexible authority structures -- Context-aware coordination switching -- Optimizes for both efficiency and adaptability - -### Swarm Algorithms - -#### Particle Swarm Optimization (PSO) -- Optimizes agent positions and behaviors -- Balances exploration and exploitation -- Suitable for continuous optimization problems - -#### Ant Colony Optimization (ACO) -- Path-finding and resource allocation -- Pheromone-based communication -- Suitable for discrete optimization problems - -#### Flocking Algorithms -- Collective movement and coordination -- Separation, alignment, and cohesion rules -- Suitable for spatial coordination tasks - -## Consensus Mechanisms - -### Byzantine Fault Tolerant (BFT) Consensus -- Handles malicious or faulty agents -- Guarantees safety and liveness properties -- Suitable for critical decision-making processes -- Requires 3f+1 agents to tolerate f failures - -### RAFT Consensus -- Leader-based consensus for log replication -- Simpler than BFT, assumes non-malicious failures -- Suitable for state machine replication -- Provides strong consistency guarantees - -### Proof of Work (PoW) -- Computational puzzle-based consensus -- Suitable for open, permissionless networks -- Energy-intensive but highly secure -- Used for critical system updates - -### Proof of Stake (PoS) -- Stake-based voting mechanism -- More energy-efficient than PoW -- Suitable for resource allocation decisions -- Aligns incentives with system health - -## Topological Constraints - -### Network Constraints -- **Bandwidth limits**: Maximum data throughput between agents -- **Latency requirements**: Real-time communication needs -- **Connectivity constraints**: Physical or logical network limitations -- **Security boundaries**: Trust zones and access controls - -### Computational Constraints -- **Resource limits**: CPU, memory, and storage constraints -- **Processing priorities**: Critical vs. background tasks -- **Capability matching**: Ensuring agents can perform assigned tasks -- **Load balancing**: Preventing resource bottlenecks - -### Organizational Constraints -- **Authority hierarchies**: Permission and delegation structures -- **Information flow**: Data access and sharing policies -- **Compliance requirements**: Regulatory and policy constraints -- **Quality of service**: Performance and reliability guarantees - -## Deployment Architecture - -### CLI Deployment System -The command-line interface provides comprehensive deployment and management capabilities: +Run this before implementation work: ```bash -# Deploy a simple worker agent -codex-synaptic deploy worker --type code --replicas 3 - -# Create a neural mesh with specific topology -codex-synaptic mesh create --nodes 10 --topology ring --consensus raft - -# Start swarm coordination with PSO optimization -codex-synaptic swarm start --algorithm pso --agents worker:5,coordinator:2 - -# Configure MCP bridging between systems -codex-synaptic bridge mcp --source codex-api --target local-model --protocol grpc +codex-synaptic launch --strict --json ``` -### Configuration Management - -#### Agent Manifests -YAML/JSON configurations defining agent specifications: -- Resource requirements and limits -- Capability declarations -- Network and security policies -- Deployment and scaling rules - -#### Topology Definitions -Network topology specifications: -- Node connectivity patterns -- Communication protocols -- Routing and load balancing rules -- Fault tolerance configurations - -#### Consensus Configurations -Consensus mechanism parameters: -- Algorithm selection and tuning -- Voting thresholds and timeouts -- Leader election procedures -- State synchronization policies - -## Security Architecture - -### Identity and Authentication -- Public key cryptography for agent identity -- Certificate-based authentication -- Role-based access control (RBAC) -- Capability-based security model - -### Communication Security -- End-to-end encryption for all agent communications -- Perfect forward secrecy -- Message authentication and integrity -- Protection against replay attacks - -### System Integrity -- Code signing for agent deployments -- Secure boot and attestation -- Runtime integrity monitoring -- Anomaly detection and response - -## Monitoring and Observability - -### Metrics Collection -- Performance metrics (latency, throughput, resource utilization) -- Business metrics (task completion, success rates) -- System health (node availability, network connectivity) -- Security metrics (authentication events, anomalies) - -### Distributed Tracing -- End-to-end request tracing across agents -- Performance bottleneck identification -- Error propagation analysis -- System dependency mapping - -### Alerting and Response -- Automated anomaly detection -- Escalation procedures -- Self-healing capabilities -- Human operator integration - -## Future Extensions - -### Planned Enhancements -- Quantum-resistant cryptography -- Advanced ML model integration -- Cross-cloud deployment support -- Edge computing optimization -- Integration with blockchain networks -- Advanced visualization and debugging tools - -### Research Directions -- Emergent intelligence from agent interactions -- Adaptive consensus mechanisms -- Self-modifying agent architectures -- Advanced swarm intelligence algorithms -- Integration with neuromorphic computing - -## Agent Development Playbook - -This section provides operational guidance for working with agents inside Codex-Synaptic, including how to reason about the orchestration runtime and extend the system safely. Use it alongside the CLI help output and the module-level documentation in `src/`. - -### Runtime Overview - -The `CodexSynapticSystem` (see `src/core/system.ts`) coordinates several subsystems: - -- **Agent Registry (`src/agents/registry.ts`)** – Tracks lifecycle, status, capabilities, and resource usage for every agent instance. -- **Task Scheduler (`src/core/scheduler.ts`)** – Assigns queued work to available agents based on capabilities and priority. -- **Neural Mesh (`src/mesh`)** – Maintains graph-based connectivity, enforces topology constraints, and exposes metrics for telemetry. -- **Swarm Coordinator (`src/swarm`)** – Runs PSO/ACO/flocking optimisers for collaborative problem solving and hive-mind scenarios. -- **Consensus Manager (`src/consensus`)** – Manages proposals, votes, and quorum checks for distributed decisions. -- **Bridges (`src/bridging`, `src/mcp`)** – Provide controlled ingress/egress for MCP and A2A messaging. -- **Memory System (`src/memory/memory-system.ts`)** – SQLite-backed persistent storage for agent interactions and artefacts under `~/.codex-synaptic/memory.db`. -- **Telemetry & Health (`src/core/health.ts`, `src/core/resources.ts`)** – Surfaced via CLI commands and used to gate scaling/auto-healing decisions. - -### Built-in Agent Roles +Treat it as a hard gate: +- If `ok=false`, stop and apply remediations first. +- Only proceed when `ok=true`. -| Agent Type | Purpose | Key Implementation | -|------------|---------|--------------------| -| `code_worker` | Code generation, refactoring, and implementation tasks | `src/agents/code_worker.ts` | -| `data_worker` | Data preparation, analysis, and transformation | `src/agents/data_worker.ts` | -| `validation_worker` | Verification, linting, and quality gates | `src/agents/validation_worker.ts` | -| `swarm_coordinator` | Supervises swarm objectives and metrics | `src/agents/swarm_coordinator.ts` | -| `consensus_coordinator` | Runs vote aggregation and conflict resolution | `src/agents/consensus_coordinator.ts` | -| `topology_coordinator` | Adjusts mesh connectivity and constraints | `src/agents/topology_coordinator.ts` | -| `mcp_bridge` / `a2a_bridge` | Translate external requests into internal tasks | `src/agents/mcp_bridge_agent.ts`, `src/agents/a2a_bridge_agent.ts` | +## 2) Golden path -### Execution Flow +1. `launch --strict --json` +2. Apply safe remediations (`fixes[].safeUnderSandbox=true`) +3. Re-run `launch --strict --json` +4. Run bounded workflow +5. Produce report (diff summary + verification + follow-up) -1. **System start** – `codex-synaptic system start` boots the orchestrator, loads configuration, and initialises telemetry, GPU probes, and memory storage. -2. **Agent bootstrap** – Default agents are registered; additional replicas can be deployed via `codex-synaptic agent deploy`. -3. **Mesh configuration** – Mesh defaults can be tuned (`codex-synaptic mesh configure --nodes 8 --topology mesh`) before dispatching complex tasks. -4. **Swarm activation** – `codex-synaptic swarm start --algorithm pso` enables collaborative optimisation loops used by hive-mind workflows. -5. **Task dispatch** – Tasks submitted through the CLI or API enter the scheduler queue, receive capability-matched agents, and stream status events. -6. **Consensus checks** – Critical decisions (e.g., promotion of artefacts) can be gated behind proposals/votes via `codex-synaptic consensus ...` commands. -7. **Telemetry + persistence** – Health snapshots, resource metrics, and memory entries are persisted so the background daemon and CLI can resume seamlessly. +## 3) Command policy -### Agent Development Guidelines +Allowed by default under workspace-write: +- `npm install`, `npm run build`, `npm run test`, `npm exec tsc -- --noEmit` +- `codex-synaptic launch --strict --json` +- `codex-synaptic background status|start|attach|logs|stop` +- `codex-synaptic project attach|list` -- **Keep responsibilities focused.** New agent types should expose clear capabilities and register them through the agent registry utilities. -- **Instrument long running tasks.** Emit progress and heartbeat events so the scheduler keeps accurate status and the telemetry surface stays fresh. -- **Respect resource limits.** Read limits from `ResourceManager` and avoid allocating beyond configured CPU/memory bounds. -- **Integrate with memory.** Use `CodexMemorySystem` to store durable artefacts or contextual knowledge shared across runs. -- **Participate in consensus when required.** Agents that introduce risky changes should submit proposals or votes with enough context for auditability. +Needs explicit elevated allowlist (mark as unsafe in reports): +- Host-level installs (`brew install`, `npm install -g`) +- Docker credential operations (`codex-synaptic env docker-login ...`) +- Commands requiring access beyond repository/workspace scope -### CLI Workflow Tips +## 4) Worktree and daemon state -- Run `codex-synaptic system monitor` in a dedicated terminal when developing new agent logic. -- Use the `background` commands to keep long-running coordination alive between sessions. -- Combine `mesh` and `swarm` commands to stress-test new strategies before exposing them to production tasks. -- Query `codex-synaptic task recent` to audit how prompts flow through the system and to inspect generated artefacts. +- Assume automations run in dedicated worktrees. +- Never store daemon state in the repository or worktree. +- Default daemon state path: `~/.codex-synaptic`. +- Optional override: `CODEX_SYNAPTIC_STATE_DIR` must point outside repo/worktree. -### Extending the Platform +## 5) Project config model -1. Scaffold a new agent under `src/agents/` and register it with the agent registry. -2. Add capabilities and resource requirements so the scheduler can route work correctly. -3. Update CLI help (if you expose new coordination primitives) in `src/cli/index.ts`. -4. Document the new workflow in `docs/` or the README so operators understand how to invoke it. -5. Add corresponding Vitest coverage under `tests/` to lock in behaviour. +Per attached project: +- `AGENTS.md` → instruction policy +- `.codex/config.toml` → Codex runtime + MCP config +- `.codex-synaptic/project.json` → Synaptic project-local settings -Following these conventions keeps the Codex-Synaptic ecosystem consistent and makes it easier for the CLI, telemetry, and automation hooks to reason about every agent in the mesh. +Trust + config bootstrap: +1. Create `.codex/config.toml` with workspace-write defaults. +2. Keep MCP definitions minimal and local-safe. +3. Attach repo via `codex-synaptic project attach `. -## Conclusion +## 6) Optional engines -The Codex-Synaptic agent system provides a comprehensive framework for enhancing OpenAI's Codex with advanced distributed computing capabilities. Through careful orchestration of various agent types, consensus mechanisms, and coordination patterns, the system can tackle complex computational challenges that require distributed intelligence, fault tolerance, and scalable coordination. +`ruflo` and `ruv-FANN` are optional adapters. Missing engines must never block core launch readiness. diff --git a/README.md b/README.md index 03dd443..d951ab1 100644 --- a/README.md +++ b/README.md @@ -1,156 +1,58 @@ # codex-synaptic -[![Stars](https://img.shields.io/github/stars/clduab11/codex-synaptic?style=for-the-badge&logo=github)](https://github.com/clduab11/codex-synaptic/stargazers) -[![Forks](https://img.shields.io/github/forks/clduab11/codex-synaptic?style=for-the-badge&logo=github)](https://github.com/clduab11/codex-synaptic/network/members) -[![Open Issues](https://img.shields.io/github/issues/clduab11/codex-synaptic?style=for-the-badge&logo=github)](https://github.com/clduab11/codex-synaptic/issues) -[![Open PRs](https://img.shields.io/github/issues-pr/clduab11/codex-synaptic?style=for-the-badge&logo=github)](https://github.com/clduab11/codex-synaptic/pulls) -[![Commit Activity](https://img.shields.io/github/commit-activity/m/clduab11/codex-synaptic?style=for-the-badge&logo=git)](https://github.com/clduab11/codex-synaptic/commits/main) -[![License: AGPL-3.0](https://img.shields.io/badge/License-AGPL--3.0-blue?style=for-the-badge)](https://www.gnu.org/licenses/agpl-3.0.en.html) -[![Node](https://img.shields.io/badge/Node-20%2B-339933?style=for-the-badge&logo=node.js)](https://nodejs.org/) -[![TypeScript](https://img.shields.io/badge/TypeScript-5.x-3178C6?style=for-the-badge&logo=typescript)](https://www.typescriptlang.org/) - -Operator-grade orchestration for Codex workflows: daemon-backed runtime control, live terminal dashboards, and MCP-driven external bridges. - -## Why This Release - -Codex for macOS is now the default frontend path for this repo's operator workflow. This release tightens the "app + CLI + MCP" loop so teams can run Codex-Synaptic predictably in Local, Worktree, and Cloud-aligned flows. - -### Verified alignment with official OpenAI docs (February 2026) - -- Codex app setup is macOS (Apple Silicon) and recommended for Mac users. -- App feature model includes Local / Worktree / Cloud modes, built-in Git, integrated terminal, automations, and MCP support. -- Codex CLI supports interactive mode, `resume`, `cloud`, `exec`, and `mcp` operations. -- Security defaults recommend workspace-write + on-request approvals for version-controlled repos. - -Sources: -- [Codex Quickstart](https://developers.openai.com/codex/quickstart/) -- [Codex App](https://developers.openai.com/codex/app/) -- [Codex App Features](https://developers.openai.com/codex/app/features/) -- [Codex CLI Features](https://developers.openai.com/codex/cli/features/) -- [Codex Security](https://developers.openai.com/codex/security/) - -## Star Chart - -[![Star History Chart](https://api.star-history.com/svg?repos=clduab11/codex-synaptic&type=Date)](https://star-history.com/#clduab11/codex-synaptic&Date) - -## System Flow - -```mermaid -flowchart LR - A["Codex App (macOS)"] -->|"Local / Worktree / Cloud"| B["codex-synaptic CLI"] - B --> C["Runtime Authority Guard"] - C --> D["Detached Daemon"] - C --> E["Local Session"] - D --> F["Dashboard / TUI Attach"] - E --> F - B --> G["MCP Profiles"] - G --> G1["mcp-filesystem"] - G --> G2["mcp-playwright"] - G --> G3["mcp-desktop-commander"] - B --> H["Bridge Layer"] - H --> I["External Swarm / MCP Endpoints"] -``` - -## Operator Command Deck +Codex-Synaptic is an **appliance-grade global CLI + daemon** for Codex on macOS. Install once, attach to any repository, run a deterministic launch gate, and only then execute bounded workflows. -```bash -# build -npm install -npm run build - -# readiness -node dist/cli/index.js launch --json -node dist/cli/index.js launch --strict --json -node dist/cli/index.js doctor -node dist/cli/index.js doctor --strict --json - -# daemon lifecycle -node dist/cli/index.js background start -node dist/cli/index.js background status -node dist/cli/index.js background attach --watch --interval 2000 -node dist/cli/index.js background logs --tail 100 -node dist/cli/index.js background restart --timeout 10000 -node dist/cli/index.js background stop --timeout 10000 - -# live dashboard -node dist/cli/index.js tui --attach-daemon --interval 1000 -node dist/cli/index.js tui --local --interval 1000 - -# MCP profiles and registration -node dist/cli/index.js env plan mcp-filesystem mcp-playwright mcp-desktop-commander -node dist/cli/index.js env docker-login mcp-filesystem mcp-playwright mcp-desktop-commander -node dist/cli/index.js env up mcp-filesystem mcp-playwright mcp-desktop-commander -node dist/cli/index.js env status mcp-filesystem mcp-playwright mcp-desktop-commander -node dist/cli/index.js env codex-register mcp-filesystem mcp-playwright mcp-desktop-commander --replace -``` +## Product model -Non-interactive CLI commands run in one-shot mode by default (the process exits after command completion). Set `CODEX_CLI_AUTO_SHUTDOWN=0` only when you explicitly want to keep the in-process foreground session alive for debugging. +- **Codex is the cockpit**: app threads, VS Code extension, automations, approvals, and worktrees. +- **Synaptic is the engine room**: daemon lifecycle, readiness checks, MCP routing, telemetry, and release hygiene. +- **Launch Gate is the contract**: `codex-synaptic launch --strict --json` is the first command for every workflow. +- **Optional engines**: + - `ruflo` adapter (optional, capability upgrade only) + - `ruv-FANN` adapter (optional, capability upgrade only) -## Codex for macOS Workflow +## 5-minute first run ```bash -# Local mode -codex -C /absolute/path/to/codex-synaptic +# 1) Install globally +npm install -g codex-synaptic -# first-launch gate in this repo -codex-synaptic launch --json +# 2) Verify global CLI +codex-synaptic --help -# Worktree mode -git worktree add ../codex-synaptic-worktree -b codex/macos-ops -codex -C ../codex-synaptic-worktree +# 3) Attach current repository +codex-synaptic project attach . -# cloud task operations from CLI -codex cloud --help -``` +# 4) Run deterministic launch gate +codex-synaptic launch --strict --json -```mermaid -flowchart LR - L["Local Mode"] --> V["Fast Iteration"] - W["Worktree Mode"] --> S["Parallel, Isolated Changes"] - C["Cloud Mode"] --> R["Remote Task Delegation"] - V --> O["Unified Review + Merge"] - S --> O - R --> O +# 5) Apply safe remediations (from fixes[].safeUnderSandbox=true), rerun launch +codex-synaptic launch --strict --json ``` -## MCP Profiles Included - -| Profile | Purpose | Safety posture | -| --- | --- | --- | -| `mcp-filesystem` | Repo/document access for Codex tasks | defaults to safe mode; controlled write can be explicitly enabled | -| `mcp-playwright` | Browser automation and verification | command-scoped runtime with health checks | -| `mcp-desktop-commander` | Desktop-level command bridge for external orchestration workflows | explicit profile startup + diagnostics before use | - -## February 2026 Prompting Baseline +When `ok=true`, start your bounded workflow and produce a report artifact (diff summary, validation log, and follow-up backlog). -Use this structure for high-signal Codex tasks in this repo: - -1. Mission: one objective. -2. Constraints: boundaries, safety controls, non-goals. -3. Acceptance criteria: objective pass/fail list. -4. Verification: exact commands and expected outcomes. -5. Deliverables: changed files, test evidence, residual risks. - -## Security Posture (Codex-aligned) - -Recommended defaults for version-controlled repos: +## Attach a new repository ```bash -codex --sandbox workspace-write --ask-for-approval on-request +cd /path/to/target-repo +codex-synaptic project attach . +codex-synaptic launch --strict --json ``` -Use stricter read-only mode when auditing unfamiliar code: - -```bash -codex --sandbox read-only --ask-for-approval on-request -``` +Synaptic discovers and uses: -## Docs Index +- `AGENTS.md` for Codex instructions +- `.codex/config.toml` for Codex project config and MCP +- `.codex-synaptic/project.json` for Synaptic project-local behavior (optional) -- [macOS integration workflow](docs/guides/codex-macos-workflows.md) -- [MCP setup and profile catalog](docs/mcp/README.md) -- [Autoscaler/daemon runbook](docs/runbooks/autoscaler-daemon-coordination.md) +## Worktree + sandbox safety -## License +- Daemon state is global and **outside** repository/worktree paths (default: `~/.codex-synaptic`). +- Preferred sandbox for development and launch remediations: `workspace-write`. +- Launch report explicitly marks unsafe remediations with `safeUnderSandbox=false` and indicates minimal extra allowlist needs. -This project is licensed under **GNU Affero General Public License v3.0 (AGPL-3.0)**. See [LICENSE](LICENSE). +See: +- `docs/launch-gate.md` +- `docs/codex-usage.md` +- `docs/sandbox-and-rules.md` diff --git a/docs/codex-usage.md b/docs/codex-usage.md new file mode 100644 index 0000000..8bf6112 --- /dev/null +++ b/docs/codex-usage.md @@ -0,0 +1,50 @@ +# Codex usage (macOS app, threads, automations, VS Code) + +## Launch-first workflow + +1. Open repository in Codex for macOS or Codex VS Code extension. +2. Run `codex-synaptic launch --strict --json`. +3. Apply safe fixes from `fixes[]`. +4. Re-run launch until `ok=true`. +5. Execute bounded workflow and publish a report artifact. + +## Daily automation template + +**Name:** Daily Launch Gate + Brief Report + +```bash +codex-synaptic launch --strict --json > .codex-synaptic/reports/daily-launch.json +npm run build > .codex-synaptic/reports/daily-build.log 2>&1 +npm run test > .codex-synaptic/reports/daily-test.log 2>&1 +``` + +Output artifact: `.codex-synaptic/reports/daily-launch.json` + logs, committed or attached to thread. + +## Nightly automation template + +**Name:** Nightly Docs Drift / Friction Scan + +```bash +codex-synaptic launch --strict --json > .codex-synaptic/reports/nightly-launch.json +git diff -- README.md AGENTS.md docs/ > .codex-synaptic/reports/nightly-docs-drift.diff || true +``` + +Output artifact: diff-ready drift report for next working day triage. + +## Weekly automation template + +**Name:** Weekly Release Preflight + +```bash +codex-synaptic launch --strict --json > .codex-synaptic/reports/weekly-launch.json +npm run build > .codex-synaptic/reports/weekly-build.log 2>&1 +npm run test > .codex-synaptic/reports/weekly-test.log 2>&1 +``` + +Output artifact: preflight bundle for release thread/PR checklist. + +## Worktree behavior + +- Run automations in dedicated worktrees. +- Keep artifacts in project-local `.codex-synaptic/reports/`. +- Keep daemon runtime/state outside worktrees (`~/.codex-synaptic` by default). diff --git a/docs/launch-gate.md b/docs/launch-gate.md new file mode 100644 index 0000000..198e7f1 --- /dev/null +++ b/docs/launch-gate.md @@ -0,0 +1,44 @@ +# Launch Gate + +`codex-synaptic launch --strict --json` is the deterministic readiness contract for this appliance. + +## Strict JSON schema + +```json +{ + "ok": true, + "capabilities": ["..."], + "checks": [ + { "name": "...", "status": "pass|fail|warn", "detail": "..." } + ], + "fixes": [ + { "why": "...", "command": "...", "safeUnderSandbox": true } + ], + "nextActions": ["..."] +} +``` + +## Required checks + +The gate emits at least: +- `runtime.node` +- `runtime.npm` +- `repo.dependencies` +- `repo.build` +- `repo.typecheck` +- `repo.test` +- `daemon.health` +- `mcp.config` +- `worktree.state_location` +- optional capability checks (`optional.ruflo`, `optional.ruv-fann`) as non-blocking warnings + +## Remediation semantics + +- `safeUnderSandbox=true` means the fix is expected to run under workspace-write. +- `safeUnderSandbox=false` means the fix needs additional host allowlist or elevated rules. +- Apply safe fixes first, rerun launch, and only then proceed to workflow execution. + +## Failure handling contract + +- In strict mode, the command exits non-zero when `ok=false`. +- Optional engines can downgrade capabilities but must not fail core boot. diff --git a/docs/sandbox-and-rules.md b/docs/sandbox-and-rules.md new file mode 100644 index 0000000..bf2a777 --- /dev/null +++ b/docs/sandbox-and-rules.md @@ -0,0 +1,37 @@ +# Sandbox and rules + +## Default mode + +Use workspace-write by default: + +```bash +codex --sandbox workspace-write --ask-for-approval on-request +``` + +## Safe command set + +Safe under workspace-write for this repo: +- `codex-synaptic launch --strict --json` +- `codex-synaptic project attach ` +- `codex-synaptic background status|start|attach|logs|stop` +- `npm install` +- `npm run build` +- `npm run test` +- `npm exec tsc -- --noEmit` + +## Commands that need extra allowlist + +Mark as unsafe (`safeUnderSandbox=false`) and request minimal allowlist: +- `brew install ...` +- `npm install -g ...` +- Docker auth operations (`codex-synaptic env docker-login ...`) +- Any command requiring writes outside workspace scope + +## Minimal approval guidance + +When a command is unsafe, specify: +1. exact command +2. reason it needs elevated scope +3. smallest file/path/network allowance needed + +Never require blanket full-access defaults. diff --git a/src/adapters/optional-engines.ts b/src/adapters/optional-engines.ts new file mode 100644 index 0000000..618915d --- /dev/null +++ b/src/adapters/optional-engines.ts @@ -0,0 +1,52 @@ +import type { SpawnCommandResult } from '../cli/doctor.js'; + +export interface OptionalEngineStatus { + name: 'ruflo' | 'ruv-fann'; + available: boolean; + version: string; +} + +export type SpawnLike = ( + command: string, + args: string[], + options: { cwd: string; encoding: BufferEncoding } +) => Promise; + +async function detectVersion( + spawnCommand: SpawnLike, + cwd: string, + name: 'ruflo' | 'ruv-fann' +): Promise { + const result = await spawnCommand(name, ['--version'], { cwd, encoding: 'utf8' }); + return { + name, + available: result.status === 0, + version: (result.stdout || result.stderr || '').trim() + }; +} + +export async function detectOptionalEngines( + spawnCommand: SpawnLike, + cwd: string +): Promise { + return Promise.all([ + detectVersion(spawnCommand, cwd, 'ruflo'), + detectVersion(spawnCommand, cwd, 'ruv-fann') + ]); +} + +export async function invokeRufloAdapter( + spawnCommand: SpawnLike, + cwd: string, + args: string[] +): Promise { + return spawnCommand('ruflo', args, { cwd, encoding: 'utf8' }); +} + +export async function invokeRuvFannAdapter( + spawnCommand: SpawnLike, + cwd: string, + args: string[] +): Promise { + return spawnCommand('ruv-fann', args, { cwd, encoding: 'utf8' }); +} diff --git a/src/cli/index.ts b/src/cli/index.ts index 4291ed1..ed8275f 100644 --- a/src/cli/index.ts +++ b/src/cli/index.ts @@ -32,15 +32,14 @@ import type { CodexPromptEnvelope, ContextLogEntry } from '../types/codex-context.js'; -import { CliGateError, ErrorCode, RetryManager } from '../core/errors.js'; -import { RetryManager, DaemonConflictError } from '../core/errors.js'; +import { CliGateError, ErrorCode, RetryManager, DaemonConflictError } from '../core/errors.js'; import { HiveMindYamlFormatter } from '../utils/yaml-output.js'; import { parseFileContent, parseJsonInput, loadFileThroughFeedforward } from './feedforward.js'; import { InstructionParser } from '../instructions/index.js'; import { RoutingPolicyService, type RoutingRequest } from '../router/index.js'; import { readFileSync, existsSync } from 'fs'; import { join, resolve, relative } from 'path'; -import { spawnSync, execFile } from 'child_process'; +import { execFile } from 'child_process'; import { promisify } from 'util'; import { ToolOptimizer, type ToolCandidate } from '../tools/optimizer/index.js'; import { type ToolUsageRecord, type ReasoningRunRecord } from '../memory/memory-system.js'; @@ -82,129 +81,23 @@ import { parseProfileList, runDoctor } from './doctor.js'; -import { collectLaunchRemediations, runLaunch } from './launch.js'; +import { buildLaunchStrictJsonReport, collectLaunchRemediations, runLaunch } from './launch.js'; import { bootstrapCliEnv, buildCliEnvBootstrapMessages, shouldAutoLoadCliEnv, shouldShowCliEnvBanner } from './env-bootstrap.js'; - -let loadedEnvSources: string[] = []; +import { attachProject, listAttachedProjects } from './project-attach.js'; +import { executeGoapWorkflow, executeTaskWithConsensus, collectExecutionResults, renderExecutionSummary, setupWorkflowEventHandlers } from './hive-mind-helpers.js'; -import { - checkCliBuildArtifact, - checkCliExecution, - checkCodexAuth, - renderHealthCheckResults, - type HealthCheck -} from './doctor-helpers.js'; - -/** - * Loads environment variables from a file into process.env. - * - * @param filePath - Path to the environment file to load - * @returns true if any variables were loaded, false otherwise - * - * @remarks - * - Skips variables that are already defined in process.env - * - Handles quoted values and escape sequences (\n, \r, \t) - * - Ignores comments (lines starting with #) and empty lines - * - Returns false if file doesn't exist or can't be read - */ -function loadEnvFile(filePath: string): boolean { - if (!existsSync(filePath)) { - return false; - } - - try { - const content = readFileSync(filePath, 'utf8'); - const lines = content.split(/\r?\n/); - let applied = false; - - for (const rawLine of lines) { - const line = rawLine.trim(); - if (!line || line.startsWith('#')) { - continue; - } - - const separatorIndex = line.indexOf('='); - if (separatorIndex === -1) { - continue; - } - - const key = line.slice(0, separatorIndex).trim(); - if (!key) { - continue; - } - - let value = line.slice(separatorIndex + 1).trim(); - if (!value) { - value = ''; - } - - const startsWithQuote = value.startsWith('"') || value.startsWith("'"); - const endsWithQuote = value.endsWith('"') || value.endsWith("'"); - if (startsWithQuote && endsWithQuote && value.length >= 2) { - value = value.slice(1, -1); - } - - value = value.replace(/\\n/g, '\n').replace(/\\r/g, '\r').replace(/\\t/g, '\t'); - if (process.env[key] === undefined) { - process.env[key] = value; - applied = true; - } - } - - return applied; - } catch { - return false; - } -} - -/** - * Bootstraps CLI environment by loading environment files from standard locations. - * - * @returns Array of successfully loaded environment file sources - * - * @remarks - * Attempts to load environment variables from the following files in order: - * - .env in current directory - * - .env.local in current directory - * - ~/.codex-synaptic/.env - * - .env in package root directory - * Only loads variables that aren't already set in process.env - */ -function bootstrapCliEnv(): string[] { - const sources: string[] = []; - const cwd = process.cwd(); - const candidates = [ - resolve(cwd, '.env'), - resolve(cwd, '.env.local'), - resolve(cwd, 'src/cli/.env') - ]; - - const seen = new Set(); - for (const candidate of candidates) { - if (seen.has(candidate)) { - continue; - } - seen.add(candidate); - if (loadEnvFile(candidate)) { - sources.push(candidate); - } - } - - return sources; -} - -const loadedEnvSources = bootstrapCliEnv(); +let loadedEnvSources: string[] = []; /** * Resolves whether CLI should auto-shutdown after command execution by @@ -3172,6 +3065,68 @@ backgroundCmd selected.forEach((line) => console.log(line)); })); +const projectCmd = decorateCommandHelp( + program + .command('project') + .description('Attach Codex-Synaptic global daemon context to any repository path'), + { + title: 'Project Attach', + subtitle: 'Register arbitrary repositories so global daemon workflows can hydrate local Codex context.', + context: [ + 'Attachment keeps global daemon metadata outside worktrees while preserving project-specific instructions.', + 'Use this before launch gate when you switch repositories.' + ], + skills: [ + 'Discover AGENTS.md, Codex config, and Synaptic project config for each repository.', + 'Keep repository attachment deterministic for automation workflows.' + ], + actions: [ + { command: 'codex-synaptic project attach /path/to/repo', description: 'Attach a repository and discover AGENTS/Codex config files.' }, + { command: 'codex-synaptic project list --json', description: 'List attached repositories from global daemon state.' } + ] + } +); + +projectCmd + .command('attach') + .argument('', 'Repository path to attach') + .description('Attach a repository path for daemon-backed workflows') + .option('--json', 'Output attachment metadata as JSON') + .action(handleCommand('project.attach', async (path, options) => { + const record = attachProject(path); + if (options.json) { + console.log(JSON.stringify(record, null, 2)); + return; + } + + console.log(chalk.green(`✅ Attached project: ${record.path}`)); + console.log(chalk.gray(` AGENTS.md: ${record.agentsFile ?? 'missing'}`)); + console.log(chalk.gray(` .codex/config.toml: ${record.codexConfig ?? 'missing'}`)); + console.log(chalk.gray(` .codex-synaptic/project.json: ${record.synapticConfig ?? 'missing'}`)); + })); + +projectCmd + .command('list') + .description('List repositories attached to global daemon state') + .option('--json', 'Output list as JSON') + .action(handleCommand('project.list', async (options) => { + const projects = listAttachedProjects(); + if (options.json) { + console.log(JSON.stringify({ projects }, null, 2)); + return; + } + + if (!projects.length) { + console.log(chalk.gray('No attached projects yet. Use `codex-synaptic project attach `.')); + return; + } + + console.log(chalk.blue('Attached projects')); + for (const project of projects) { + console.log(`- ${project.path} (${project.attachedAt})`); + } + })); + // Instructions commands const instructionsCmd = decorateCommandHelp( program @@ -5278,14 +5233,30 @@ launchCmd const profileNames = parseProfileList(options.mcpProfiles, [...DEFAULT_MCP_PROFILES]); const report = await runLaunch({ cwd: process.cwd(), - strict, + strict: options.json ? false : strict, skipCodexAuth: Boolean(options.skipCodexAuth), mcpProfiles: profileNames, suppressInfoConsoleLogs: Boolean(options.json) }); if (options.json) { - console.log(JSON.stringify(report, null, 2)); + const strictJsonReport = await buildLaunchStrictJsonReport(report, { + cwd: process.cwd(), + strict, + skipCodexAuth: Boolean(options.skipCodexAuth), + mcpProfiles: profileNames, + suppressInfoConsoleLogs: true + }); + console.log(JSON.stringify(strictJsonReport, null, 2)); + + if (strict && !strictJsonReport.ok) { + throw new CliGateError( + ErrorCode.LAUNCH_GATE_FAILURE, + 'Launch failed one or more readiness gates.', + { strict, report: strictJsonReport } + ); + } + return; } else { console.log(chalk.blue('🚀 Codex-Synaptic Launch')); console.log(chalk.gray(` Status: ${report.ok ? 'ready' : 'blocked'}`)); @@ -5368,35 +5339,6 @@ doctorCmd .action(handleCommand('doctor', async (options) => { const profileNames = parseProfileList(options.mcpProfiles, [...DEFAULT_MCP_PROFILES]); const report = await runDoctor({ - const profileNames = String(options.mcpProfiles) - .split(',') - .map((item) => item.trim()) - .filter(Boolean); - - const checks: HealthCheck[] = []; - - // Check CLI build artifact - const buildCheck = checkCliBuildArtifact(); - checks.push(buildCheck); - - // Check CLI execution if build exists - if (buildCheck.ok) { - const distCliPath = join(process.cwd(), 'dist', 'cli', 'index.js'); - const execCheck = checkCliExecution(distCliPath); - if (execCheck) { - checks.push(execCheck); - } - } - - // Check Codex authentication - if (!options.skipCodexAuth) { - checks.push(checkCodexAuth()); - } - - // Check Codex MCP list - let codexMcpNames = new Set(); - const codexMcpList = spawnSync('codex', ['mcp', 'list', '--json'], { - cwd: process.cwd(), mcpProfiles: profileNames, skipCodexAuth: Boolean(options.skipCodexAuth) }); @@ -5424,66 +5366,6 @@ doctorCmd { summary: report.summary, report } ); } - if (codexMcpList.status === 0) { - try { - const parsed = JSON.parse(codexMcpList.stdout || '[]') as Array<{ name?: string }>; - codexMcpNames = new Set(parsed.map((entry) => String(entry.name)).filter(Boolean)); - checks.push({ - id: 'codex.mcp_list', - ok: true, - details: `Loaded ${codexMcpNames.size} Codex MCP registration(s).` - }); - } catch (error) { - checks.push({ - id: 'codex.mcp_list', - ok: false, - details: `Failed to parse codex mcp list output: ${(error as Error).message}`, - remediation: 'Run `codex mcp list --json` and inspect output.' - }); - } - } else { - checks.push({ - id: 'codex.mcp_list', - ok: false, - details: codexMcpList.stderr?.trim() || 'codex mcp list failed', - remediation: 'Verify Codex CLI install and MCP support (`codex mcp --help`).' - }); - } - - // Check MCP profiles using service manager - for (const profileName of profileNames) { - const status = await serviceManager.status(profileName); - const registration = serviceManager.codexRegistration(profileName); - const registered = registration ? codexMcpNames.has(registration.codexName) : true; - const healthy = status.healthy !== false; - const ok = status.running && healthy && registered; - - let details = `running=${status.running} healthy=${status.healthy === null ? 'n/a' : status.healthy} registered=${registered}`; - if (status.diagnostics.length) { - details += ` diagnostics=${status.diagnostics.join(' | ')}`; - } - - const remediationParts: string[] = []; - if (!status.running || !healthy) { - remediationParts.push(`codex-synaptic env up ${profileName}`); - } - if (registration && !registered) { - remediationParts.push(`codex-synaptic env codex-register ${profileName}`); - } - - checks.push({ - id: `mcp.${profileName}`, - ok, - details, - remediation: remediationParts.length ? remediationParts.join(' && ') : undefined, - metadata: { - codexName: registration?.codexName, - url: registration?.url - } - }); - } - - renderHealthCheckResults(checks, { json: options.json, strict: options.strict }); })); const memoryCmd = decorateCommandHelp( diff --git a/src/cli/launch.ts b/src/cli/launch.ts index 2eae7af..1f42195 100644 --- a/src/cli/launch.ts +++ b/src/cli/launch.ts @@ -1,7 +1,8 @@ import { spawn } from 'child_process'; -import { access } from 'fs/promises'; +import { access, readFile } from 'fs/promises'; import { join } from 'path'; import { + getDaemonPaths, getBackgroundStatus, startBackgroundSystem, type BackgroundStatus @@ -18,6 +19,7 @@ import { } from './doctor.js'; import { BridgeError, ErrorCode } from '../core/errors.js'; import { Logger, LogLevel } from '../core/logger.js'; +import { detectOptionalEngines } from '../adapters/optional-engines.js'; export interface LaunchStep { id: string; @@ -47,6 +49,26 @@ export interface LaunchOptions { suppressInfoConsoleLogs?: boolean; } +export interface LaunchGateCheck { + name: string; + status: 'pass' | 'fail' | 'warn'; + detail: string; +} + +export interface LaunchGateFix { + why: string; + command: string; + safeUnderSandbox: boolean; +} + +export interface LaunchStrictJsonReport { + ok: boolean; + capabilities: string[]; + checks: LaunchGateCheck[]; + fixes: LaunchGateFix[]; + nextActions: string[]; +} + export interface LaunchDependencies extends DoctorDependencies { startBackground?: () => Promise; getBackgroundStatus?: () => BackgroundStatus; @@ -75,7 +97,11 @@ function normalizeSpawn( ?? ((command, args, spawnOptions) => new Promise((resolve) => { const child = spawn(command, args, { cwd: spawnOptions.cwd, - stdio: ['ignore', 'pipe', 'pipe'] + stdio: ['ignore', 'pipe', 'pipe'], + env: { + ...process.env, + CODEX_AUTO_LINK: process.env.CODEX_AUTO_LINK ?? '0' + } }); if (child.stdout) { child.stdout.setEncoding(spawnOptions.encoding); @@ -126,6 +152,218 @@ function collectLaunchRemediationsFromStep(step: LaunchStep): string[] { .filter(Boolean); } +function splitRemediationCommands(raw?: string): string[] { + if (!raw) { + return []; + } + + return raw + .split('&&') + .map((item) => item.trim()) + .filter(Boolean); +} + +function remediationIsSafe(command: string): boolean { + return !/(docker-login|brew\s+install|npm\s+install(?!\s+--no-save))/i.test(command); +} + +async function readPackageScripts(cwd: string): Promise> { + try { + const packageJsonPath = join(cwd, 'package.json'); + const raw = await readFile(packageJsonPath, 'utf8'); + const parsed = JSON.parse(raw) as { scripts?: Record }; + return parsed.scripts ?? {}; + } catch { + return {}; + } +} + +async function runCheckCommand( + spawnCommand: ReturnType, + cwd: string, + name: string, + command: string, + args: string[], + remediation: string, + warnOnFailure = false +): Promise<{ check: LaunchGateCheck; fix?: LaunchGateFix }> { + const result = await spawnCommand(command, args, { cwd, encoding: 'utf8' }); + const status = result.status === 0 ? 'pass' : (warnOnFailure ? 'warn' : 'fail'); + const detail = result.status === 0 + ? `${command} ${args.join(' ')} succeeded.` + : (result.stderr?.trim() || result.stdout?.trim() || `${command} ${args.join(' ')} failed.`); + + return { + check: { name, status, detail }, + fix: result.status === 0 + ? undefined + : { + why: `${name} failed`, + command: remediation, + safeUnderSandbox: remediationIsSafe(remediation) + } + }; +} + +export async function buildLaunchStrictJsonReport( + report: LaunchReport, + options: LaunchOptions = {}, + deps: LaunchDependencies = {} +): Promise { + const cwd = options.cwd ?? process.cwd(); + const spawnCommand = normalizeSpawn(deps); + const checks: LaunchGateCheck[] = []; + const fixes: LaunchGateFix[] = []; + const capabilities = [ + 'launch-gate', + 'global-daemon', + 'project-attach', + 'codex-macos', + 'codex-vscode' + ]; + + const nodeCheck = await runCheckCommand( + spawnCommand, + cwd, + 'runtime.node', + 'node', + ['--version'], + 'Install Node.js 20+ and rerun `node --version`.' + ); + checks.push(nodeCheck.check); + if (nodeCheck.fix) fixes.push(nodeCheck.fix); + + const npmCheck = await runCheckCommand( + spawnCommand, + cwd, + 'runtime.npm', + 'npm', + ['--version'], + 'Install npm and rerun `npm --version`.' + ); + checks.push(npmCheck.check); + if (npmCheck.fix) fixes.push(npmCheck.fix); + + const depsCheck = await runCheckCommand( + spawnCommand, + cwd, + 'repo.dependencies', + 'npm', + ['ci', '--dry-run'], + 'Run `npm install` to install dependencies.' + ); + checks.push(depsCheck.check); + if (depsCheck.fix) fixes.push(depsCheck.fix); + + const scripts = await readPackageScripts(cwd); + + if (scripts.build) { + const buildCheck = await runCheckCommand(spawnCommand, cwd, 'repo.build', 'npm', ['run', 'build'], 'Run `npm run build`.'); + checks.push(buildCheck.check); + if (buildCheck.fix) fixes.push(buildCheck.fix); + } else { + checks.push({ name: 'repo.build', status: 'warn', detail: 'No build script declared in package.json.' }); + } + + if (scripts.typecheck) { + const typeCheck = await runCheckCommand(spawnCommand, cwd, 'repo.typecheck', 'npm', ['run', 'typecheck'], 'Run `npm run typecheck`.'); + checks.push(typeCheck.check); + if (typeCheck.fix) fixes.push(typeCheck.fix); + } else { + const tscCheck = await runCheckCommand( + spawnCommand, + cwd, + 'repo.typecheck', + 'npm', + ['exec', 'tsc', '--', '--noEmit'], + 'Run `npm exec tsc -- --noEmit`.' + ); + checks.push(tscCheck.check); + if (tscCheck.fix) fixes.push(tscCheck.fix); + } + + if (scripts.test) { + const testCheck = await runCheckCommand(spawnCommand, cwd, 'repo.test', 'npm', ['run', 'test'], 'Run `npm run test`.'); + checks.push(testCheck.check); + if (testCheck.fix) fixes.push(testCheck.fix); + } else { + checks.push({ name: 'repo.test', status: 'warn', detail: 'No test script declared in package.json.' }); + } + + const daemonStep = report.steps.find((step) => step.id === 'runtime.daemon'); + checks.push({ + name: 'daemon.health', + status: daemonStep?.ok ? 'pass' : 'fail', + detail: daemonStep?.details ?? 'Daemon health check was not executed.' + }); + if (daemonStep && !daemonStep.ok && daemonStep.remediation) { + for (const command of splitRemediationCommands(daemonStep.remediation)) { + fixes.push({ why: 'Daemon is not healthy', command, safeUnderSandbox: remediationIsSafe(command) }); + } + } + + const mcpConfigStatus = report.steps.find((step) => step.id === 'mcp.codex_register'); + checks.push({ + name: 'mcp.config', + status: mcpConfigStatus?.ok ? 'pass' : 'fail', + detail: mcpConfigStatus?.details ?? 'MCP configuration check did not run.' + }); + + const daemonPaths = getDaemonPaths(); + const cwdPrefix = `${cwd}/`; + const stateOutsideRepo = !daemonPaths.stateDir.startsWith(cwdPrefix) && daemonPaths.stateDir !== cwd; + checks.push({ + name: 'worktree.state_location', + status: stateOutsideRepo ? 'pass' : 'fail', + detail: stateOutsideRepo + ? `Daemon state directory resolves outside repository: ${daemonPaths.stateDir}` + : `Daemon state directory must be outside repository/worktree. Current: ${daemonPaths.stateDir}` + }); + if (!stateOutsideRepo) { + fixes.push({ + why: 'Daemon state location is worktree-unsafe', + command: 'Export CODEX_SYNAPTIC_STATE_DIR to a stable path outside the repository (for example ~/.codex-synaptic).', + safeUnderSandbox: false + }); + } + + const optionalEngines = await detectOptionalEngines(spawnCommand, cwd); + for (const engine of optionalEngines) { + checks.push({ + name: `optional.${engine.name}`, + status: engine.available ? 'pass' : 'warn', + detail: engine.available + ? `${engine.name} detected: ${engine.version || '--version returned success.'}` + : `${engine.name} not detected; continuing in core mode.` + }); + if (engine.available) { + capabilities.push(`${engine.name}-adapter`); + } + } + + for (const step of report.steps) { + if (step.ok || !step.remediation) { + continue; + } + for (const command of splitRemediationCommands(step.remediation)) { + fixes.push({ why: `${step.id} gate failed`, command, safeUnderSandbox: remediationIsSafe(command) }); + } + } + + const ok = checks.every((check) => check.status !== 'fail') && report.ok; + const nextActions = ok + ? ['Launch gate passed. Continue with bounded workflow execution and report generation.'] + : ['Apply safe remediations in order, then rerun `codex-synaptic launch --strict --json`.']; + + return { + ok, + capabilities, + checks, + fixes, + nextActions + }; +} + function buildMcpBootstrapRemediation(profileNames: string[]): string { const commands: string[] = []; diff --git a/src/cli/project-attach.ts b/src/cli/project-attach.ts new file mode 100644 index 0000000..5d9b17d --- /dev/null +++ b/src/cli/project-attach.ts @@ -0,0 +1,62 @@ +import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs'; +import { resolve, join } from 'path'; +import { getDaemonPaths } from './daemon-manager.js'; + +export interface AttachedProjectRecord { + path: string; + attachedAt: string; + agentsFile: string | null; + codexConfig: string | null; + synapticConfig: string | null; +} + +interface AttachmentStore { + projects: AttachedProjectRecord[]; +} + +function attachmentFilePath(): string { + return join(getDaemonPaths().stateDir, 'attached-projects.json'); +} + +function readStore(): AttachmentStore { + const file = attachmentFilePath(); + if (!existsSync(file)) { + return { projects: [] }; + } + + try { + return JSON.parse(readFileSync(file, 'utf8')) as AttachmentStore; + } catch { + return { projects: [] }; + } +} + +function writeStore(store: AttachmentStore): void { + const file = attachmentFilePath(); + mkdirSync(getDaemonPaths().stateDir, { recursive: true }); + writeFileSync(file, JSON.stringify(store, null, 2), 'utf8'); +} + +export function attachProject(targetPath: string): AttachedProjectRecord { + const absolutePath = resolve(targetPath); + const record: AttachedProjectRecord = { + path: absolutePath, + attachedAt: new Date().toISOString(), + agentsFile: existsSync(join(absolutePath, 'AGENTS.md')) ? join(absolutePath, 'AGENTS.md') : null, + codexConfig: existsSync(join(absolutePath, '.codex', 'config.toml')) ? join(absolutePath, '.codex', 'config.toml') : null, + synapticConfig: existsSync(join(absolutePath, '.codex-synaptic', 'project.json')) + ? join(absolutePath, '.codex-synaptic', 'project.json') + : null + }; + + const store = readStore(); + const filtered = store.projects.filter((project) => project.path !== absolutePath); + filtered.push(record); + writeStore({ projects: filtered }); + + return record; +} + +export function listAttachedProjects(): AttachedProjectRecord[] { + return readStore().projects.sort((a, b) => b.attachedAt.localeCompare(a.attachedAt)); +}