# WP2: packages/macp — MACP Protocol Package ## Context Port the MACP protocol layer from Python (in ~/src/mosaic-stack/tools/macp/) to TypeScript as `packages/macp` in this monorepo. This package provides the foundational protocol types, quality gate execution, credential resolution, and event system that `packages/coord` and `plugins/macp` depend on. **Board decisions that constrain this work:** - No Python in the new repo — everything rewrites to TypeScript - OpenBrain learning capture/recall is OUT OF SCOPE (deferred to future brief) - 85% test coverage on TS implementation files - Credential resolver behavior must be captured as test fixtures BEFORE rewrite - Clean index.ts exports, no internal path leakage ## Source Files (Python → TypeScript) ### 1. credential-resolver.ts **Source:** `~/src/mosaic-stack/tools/macp/dispatcher/credential_resolver.py` Resolution order (MUST preserve exactly): 1. Mosaic credential files (`~/.config/mosaic/credentials/{provider}.env`) 2. OpenClaw config (`~/.openclaw/openclaw.json`) — env block + models.providers.{provider}.apiKey 3. Ambient environment variables 4. CredentialError (failure) Key behaviors to preserve: - Provider registry: anthropic, openai, zai → env var names + credential file paths + OC config paths - Dotenv parser: handles single/double quotes, comments, blank lines - JSON5 stripping: placeholder-extraction approach (NOT naive regex) — protects URLs and timestamps inside string values - OC config permission check: warn on world-readable, skip if wrong owner - Redacted marker detection: `__OPENCLAW_REDACTED__` values skipped - Task-level override via `credentials.provider_key_env` ### 2. gate-runner.ts **Source:** `~/src/mosaic-stack/tools/macp/controller/gate_runner.py` Three gate types: - `mechanical`: shell command, pass = exit code 0 - `ai-review`: shell command producing JSON, parse findings, fail on blockers - `ci-pipeline`: placeholder (always passes for now) Key behaviors: - `normalize_gate()`: accepts string or dict, normalizes to gate entry - `run_gate()`: executes single gate, returns result with pass/fail - `run_gates()`: executes all gates, emits events, returns (all_passed, results) - AI review parsing: `_count_ai_findings()` reads stats.blockers or findings[].severity - `fail_on` modes: "blocker" (default) or "any" ### 3. event-emitter.ts **Source:** `~/src/mosaic-stack/tools/macp/controller/gate_runner.py` (emit_event, append_event functions) + `~/src/mosaic-stack/tools/macp/events/` - Append structured events to ndjson file - Event types: task.assigned, task.started, task.completed, task.failed, task.escalated, task.gated, task.retry.scheduled, rail.check.started, rail.check.passed, rail.check.failed - Each event: event_id (uuid), event_type, task_id, status, timestamp, source, message, metadata ### 4. types.ts **Source:** `~/src/mosaic-stack/tools/macp/protocol/task.schema.json` TypeScript types for: - Task (id, title, status, dispatch, runtime, depends_on, depends_on_policy, quality_gates, timeout_seconds, metadata, etc.) - Event (event_id, event_type, task_id, status, timestamp, source, message, metadata) - GateResult (command, exit_code, type, passed, output, findings, blockers) - TaskResult (task_id, status, completed_at, exit_code, gate_results, files_changed, etc.) - CredentialError, ProviderRegistry ### 5. schemas/ (copy) Copy `~/src/mosaic-stack/tools/macp/protocol/task.schema.json` as-is. ## Package Structure ``` packages/macp/ ├── src/ │ ├── index.ts │ ├── types.ts │ ├── credential-resolver.ts │ ├── gate-runner.ts │ ├── event-emitter.ts │ └── schemas/ │ └── task.schema.json ├── __tests__/ │ ├── credential-resolver.test.ts │ ├── gate-runner.test.ts │ └── event-emitter.test.ts ├── package.json ├── tsconfig.json └── vitest.config.ts ``` ## Package.json ```json { "name": "@mosaic/macp", "version": "0.0.1", "type": "module", "exports": { ".": "./src/index.ts" }, "dependencies": {}, "devDependencies": { "vitest": "workspace:*", "typescript": "workspace:*" } } ``` Zero external dependencies. Uses node:fs, node:path, node:child_process, node:crypto only. ## Test Requirements Port ALL existing Python tests as TypeScript equivalents: - `test_resolve_from_file` → credential file resolution - `test_resolve_from_ambient` → ambient env resolution - `test_resolve_from_oc_config_env_block` → OC config env block - `test_resolve_from_oc_config_provider_apikey` → OC config provider - `test_oc_config_precedence` → mosaic file wins over OC config - `test_oc_config_missing_file` → graceful fallback - `test_json5_strip` → structural transforms - `test_json5_strip_urls_and_timestamps` → URLs/timestamps survive - `test_redacted_values_skipped` → redacted marker detection - `test_oc_config_permission_warning` → file permission check - `test_resolve_missing_raises` → CredentialError thrown - Gate runner: mechanical pass/fail, AI review parsing, ci-pipeline placeholder - Event emitter: append to ndjson, event structure validation ## ESM Requirements - `"type": "module"` in package.json - NodeNext module resolution in tsconfig - `.js` extensions in all imports - No CommonJS (`require`, `module.exports`) ## Integration Points After this package is built: - `packages/coord` should import `@mosaic/macp` for event emission and gate types - `plugins/macp` should import `@mosaic/macp` for credential resolution and protocol types