P0-P8: Full V1.0.0 Alpha implementation + audit reports

Implements 123 tasks across 9 phases (T-001..T-809) totaling 146 source files.

Monorepo (P0):
- 7-package Bun + Turborepo + TypeScript monorepo
- dependency-cruiser enforcing 7 forbidden edges + 5 deep-import rules

Contracts (P0):
- 16 type files (ids/error/event/runtime/ipc/task/worker-result/tool/artifact/evidence/project/provider/permission/ui/capability/platform)

Storage & Events (P1):
- DatabaseManager + MigrationRunner (19 tables, 22 indexes, 5 schema_meta seeds)
- 16 repositories (Repository<T,I,U> pattern, INV-1 status columns via EventStore.project only)
- EventSchemaRegistry (54 durable + 7 ephemeral), EventStore, EventBus, EventIngestor
- Project/Session/Artifact/Evidence stores + 8-step Recovery

Tools & Permission (P2):
- PathClassifier (8 categories), CommandRiskAnalyzer (10 categories), SecretRedactor
- PermissionEngine 6-layer evaluation (capability→profile→task_scope→risk→credential→user_prompt)
- ToolRegistry with 20+ tools across fs/shell/git/project/artifact/context/permission/doctor
- CapabilityManifestValidator + CapabilityRegistry

LLM & Context (P3):
- ModelConfigLoader, CapabilityMatrix, AnthropicCanonicalConverter
- AnthropicAdapter + OpenAICompatibleAdapter
- ProviderManager facade
- PromptLayerLoader (L0/L1/L3/L5), CompactionPolicy, ContextAssembler

Worker IPC & Scheduler (P4):
- WorkerProtocol (NDJSON), WorkerProcess (exit codes 0-5), WorkerManager (spawn/handshake)
- WorkerRuntime (INV-3: IPC only, no direct fs/shell/SQLite)
- 5 worker roles (Executor/Reviewer/Debugger/Compactor/ExperienceMiner)
- TaskGraph, WavePlanner, RetryPlanner, AgentMonitor, WorkspaceManager
- Scheduler (state machine), 8-step Recovery

C++ Toolchain (P5):
- DiagnosticParser, CppProjectDetector, CMakeConfigurator, CppBuilder
- CppTestRunner, CppcheckRunner, ClangdClient
- CppToolRegistrar + capability manifest

Projection & TUI (P6):
- ProjectionStore (hydrate/apply/snapshot/subscribe)
- TuiApp + 8 components (Session/Task/Agent/Tool/Diff/Evidence/Permission/Blocker/Hud)
- ProjectionClient in-process ref

Agents & Knowledge (P7):
- MainAgent, ArchitectureDesigner
- DebugKnowledgeStore + LearnedMemoryStore (single-writer, outbox model)
- Role integration wiring

CLI & Doctor & Release (P8):
- Logger + DeveloperLogEncryptor (AES-256-GCM)
- DoctorService (self_bootstrap first)
- RuntimeApp + ServiceRegistry
- 11 CLI commands: run/init/doctor/provider/resume/compact/history/session/restore/e2e/release
- CliEntrypoint + air<TODO>

Audit (in AirPlan/docs/):
- Deepseek开发阶段审计.md (97 findings)
- Opus开发阶段审计.md (140+ findings, 18 P0 blockers)
- MiniMaxM3开发阶段审计.md (18 P0 blockers, focuses on executability)
- AirPlan/TODO.md (technical debt + 42 TODOs by phase)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
AirCoding
2026-06-02 19:19:55 +08:00
parent 071283df8f
commit a773bac28c
179 changed files with 21855 additions and 0 deletions

View File

@@ -0,0 +1,173 @@
/**
* CapabilityManifestValidator - Validates capability manifests
*
* Implements contracts §18; DD §9.5.
* Validates schema_version=1, tool schemas, permissions.
*
* @module packages/runtime/src/capabilities/CapabilityManifestValidator
*/
import type { ToolDefinition } from '@aircoding/contracts'
export interface CapabilityManifest {
schema_version: number
name: string
version: string
description?: string
tools: CapabilityTool[]
dependencies?: string[]
trust_level?: 'core' | 'trusted' | 'untrusted'
}
export interface CapabilityTool {
name: string
category?: string
permissions?: {
read?: boolean
write?: boolean
network?: boolean
}
input_schema?: Record<string, unknown>
}
export interface ValidationResult {
valid: boolean
errors: ValidationError[]
warnings: ValidationWarning[]
}
export interface ValidationError {
field: string
message: string
code: string
}
export interface ValidationWarning {
field: string
message: string
}
export class CapabilityManifestValidator {
private static readonly SUPPORTED_SCHEMA_VERSION = 1
private static readonly REQUIRED_FIELDS = ['schema_version', 'name', 'version', 'tools']
private static readonly TRUST_LEVELS = ['core', 'trusted', 'untrusted'] as const
/**
* Validate a capability manifest.
*/
validate(manifest: unknown): ValidationResult {
const errors: ValidationError[] = []
const warnings: ValidationWarning[] = []
if (!manifest || typeof manifest !== 'object') {
errors.push({ field: 'manifest', message: 'Manifest must be an object', code: 'INVALID_TYPE' })
return { valid: false, errors, warnings }
}
const obj = manifest as Record<string, unknown>
// Check required fields
for (const field of CapabilityManifestValidator.REQUIRED_FIELDS) {
if (!(field in obj)) {
errors.push({ field, message: `Required field missing: ${field}`, code: 'MISSING_FIELD' })
}
}
// Validate schema_version
if ('schema_version' in obj) {
const schema_version = obj.schema_version
if (typeof schema_version !== 'number') {
errors.push({ field: 'schema_version', message: 'schema_version must be a number', code: 'INVALID_TYPE' })
} else if (schema_version !== CapabilityManifestValidator.SUPPORTED_SCHEMA_VERSION) {
errors.push({
field: 'schema_version',
message: `Unsupported schema_version: ${schema_version}. Supported: ${CapabilityManifestValidator.SUPPORTED_SCHEMA_VERSION}`,
code: 'UNSUPPORTED_VERSION'
})
}
}
// Validate name
if ('name' in obj && typeof obj.name !== 'string') {
errors.push({ field: 'name', message: 'name must be a string', code: 'INVALID_TYPE' })
} else if ('name' in obj && obj.name) {
const name = obj.name as string
if (!/^[a-z][a-z0-9_-]*$/.test(name)) {
errors.push({ field: 'name', message: 'name must be lowercase alphanumeric with dashes/underscores', code: 'INVALID_FORMAT' })
}
}
// Validate version
if ('version' in obj && typeof obj.version !== 'string') {
errors.push({ field: 'version', message: 'version must be a string', code: 'INVALID_TYPE' })
} else if ('version' in obj && obj.version) {
const version = obj.version as string
if (!/^\d+\.\d+\.\d+(-[a-zA-Z0-9.]+)?$/.test(version)) {
warnings.push({ field: 'version', message: 'version should follow semver format (e.g., 1.0.0)' })
}
}
// Validate tools array
if ('tools' in obj) {
if (!Array.isArray(obj.tools)) {
errors.push({ field: 'tools', message: 'tools must be an array', code: 'INVALID_TYPE' })
} else {
this.validate_tools(obj.tools as unknown[], errors, warnings)
}
}
// Validate trust_level
if ('trust_level' in obj) {
const trust_level = obj.trust_level
if (typeof trust_level !== 'string') {
errors.push({ field: 'trust_level', message: 'trust_level must be a string', code: 'INVALID_TYPE' })
} else if (!CapabilityManifestValidator.TRUST_LEVELS.includes(trust_level as typeof CapabilityManifestValidator.TRUST_LEVELS[number])) {
errors.push({
field: 'trust_level',
message: `Invalid trust_level: ${trust_level}. Must be one of: ${CapabilityManifestValidator.TRUST_LEVELS.join(', ')}`,
code: 'INVALID_VALUE'
})
}
}
return { valid: errors.length === 0, errors, warnings }
}
private validate_tools(tools: unknown[], errors: ValidationError[], warnings: ValidationWarning[]): void {
tools.forEach((tool, index) => {
if (!tool || typeof tool !== 'object') {
errors.push({ field: `tools[${index}]`, message: 'Tool must be an object', code: 'INVALID_TYPE' })
return
}
const t = tool as Record<string, unknown>
// Validate tool name
if (!('name' in t) || typeof t.name !== 'string') {
errors.push({ field: `tools[${index}].name`, message: 'Tool name is required and must be a string', code: 'MISSING_FIELD' })
}
// Validate permissions object if present
if ('permissions' in t && t.permissions) {
if (typeof t.permissions !== 'object') {
errors.push({ field: `tools[${index}].permissions`, message: 'permissions must be an object', code: 'INVALID_TYPE' })
} else {
const perms = t.permissions as Record<string, unknown>
const valid_perms = ['read', 'write', 'network']
for (const key of Object.keys(perms)) {
if (!valid_perms.includes(key)) {
warnings.push({ field: `tools[${index}].permissions.${key}`, message: `Unknown permission: ${key}` })
}
if (typeof perms[key] !== 'boolean') {
errors.push({ field: `tools[${index}].permissions.${key}`, message: 'Permission value must be boolean', code: 'INVALID_TYPE' })
}
}
}
}
})
}
}
export function createCapabilityManifestValidator(): CapabilityManifestValidator {
return new CapabilityManifestValidator()
}

View File

@@ -0,0 +1,232 @@
/**
* CapabilityRegistry - Lifecycle management for capabilities
*
* Implements contracts §18; DD §9.5.
* Lifecycle: discovered → validated → doctor_checked → enabled → registered → active.
* Trust levels affect default posture, never bypass ToolRegistry/PermissionEngine.
*
* @module packages/runtime/src/capabilities/CapabilityRegistry
*/
import type { ToolDefinition } from '@aircoding/contracts'
import { CapabilityManifestValidator, type CapabilityManifest, type ValidationResult } from './CapabilityManifestValidator.js'
export type CapabilityState = 'discovered' | 'validated' | 'doctor_checked' | 'enabled' | 'registered' | 'active' | 'disabled' | 'failed'
export interface CapabilityEntry {
manifest: CapabilityManifest
state: CapabilityState
tool_definitions: ToolDefinition[]
enabled_at?: string
error?: string
}
/**
* CapabilityRegistry manages the lifecycle of capabilities.
* INV-4: Dependency installs go only through Doctor (no direct install).
*/
export class CapabilityRegistry {
private capabilities: Map<string, CapabilityEntry> = new Map()
private validator: CapabilityManifestValidator
private tool_registry: ToolRegistry | null = null
constructor() {
this.validator = createCapabilityManifestValidator()
}
/**
* Set the tool registry for registering tools.
*/
set_tool_registry(registry: ToolRegistry): void {
this.tool_registry = registry
}
/**
* Discover a capability manifest.
*/
discover(manifest: CapabilityManifest): { ok: boolean; capability_id?: string; error?: string } {
const capability_id = `${manifest.name}@${manifest.version}`
if (this.capabilities.has(capability_id)) {
return { ok: false, capability_id, error: 'Capability already discovered' }
}
const entry: CapabilityEntry = {
manifest,
state: 'discovered',
tool_definitions: []
}
this.capabilities.set(capability_id, entry)
return { ok: true, capability_id }
}
/**
* Validate a discovered capability.
*/
validate(capability_id: string): ValidationResult {
const entry = this.capabilities.get(capability_id)
if (!entry) {
return { valid: false, errors: [{ field: 'capability_id', message: 'Capability not found', code: 'NOT_FOUND' }], warnings: [] }
}
const result = this.validator.validate(entry.manifest)
if (result.valid) {
entry.state = 'validated'
// Convert capability tools to ToolDefinitions
entry.tool_definitions = this.convert_to_tool_definitions(entry.manifest)
} else {
entry.state = 'failed'
entry.error = result.errors.map(e => e.message).join('; ')
}
return result
}
/**
* Doctor check - verify the capability is safe to enable.
* This is a placeholder - actual implementation would integrate with DoctorService.
*/
async doctor_check(capability_id: string): Promise<{ ok: boolean; error?: string }> {
const entry = this.capabilities.get(capability_id)
if (!entry) {
return { ok: false, error: 'Capability not found' }
}
if (entry.state !== 'validated') {
return { ok: false, error: `Capability must be validated first, current state: ${entry.state}` }
}
// Stub: would run doctor checks
entry.state = 'doctor_checked'
return { ok: true }
}
/**
* Enable a capability after all checks pass.
*/
enable(capability_id: string): { ok: boolean; error?: string } {
const entry = this.capabilities.get(capability_id)
if (!entry) {
return { ok: false, error: 'Capability not found' }
}
// Must pass doctor_check before enabling
if (entry.state !== 'doctor_checked') {
return { ok: false, error: `Capability must pass doctor_check first, current state: ${entry.state}` }
}
entry.state = 'enabled'
entry.enabled_at = new Date().toISOString()
return { ok: true }
}
/**
* Register tools from an enabled capability into ToolRegistry.
*/
register_tools(capability_id: string): { ok: boolean; registered_count: number; error?: string } {
const entry = this.capabilities.get(capability_id)
if (!entry) {
return { ok: false, registered_count: 0, error: 'Capability not found' }
}
if (entry.state !== 'enabled') {
return { ok: false, registered_count: 0, error: `Capability must be enabled first, current state: ${entry.state}` }
}
if (!this.tool_registry) {
return { ok: false, registered_count: 0, error: 'Tool registry not set' }
}
// Register all tools
let registered_count = 0
for (const tool_def of entry.tool_definitions) {
// Create a stub executor for each tool
const executor = create_stub_executor(tool_def.name)
this.tool_registry.register(tool_def.name, tool_def, executor)
registered_count++
}
entry.state = 'active'
return { ok: true, registered_count }
}
/**
* Disable a capability and remove its tools.
*/
disable(capability_id: string): { ok: boolean; error?: string } {
const entry = this.capabilities.get(capability_id)
if (!entry) {
return { ok: false, error: 'Capability not found' }
}
// Remove tools from registry if active
if (entry.state === 'active' && this.tool_registry) {
for (const tool_def of entry.tool_definitions) {
this.tool_registry.unregister(tool_def.name)
}
}
entry.state = 'disabled'
return { ok: true }
}
/**
* List all capabilities.
*/
list(): Array<{ id: string; name: string; version: string; state: CapabilityState }> {
return Array.from(this.capabilities.entries()).map(([id, entry]) => ({
id,
name: entry.manifest.name,
version: entry.manifest.version,
state: entry.state
}))
}
/**
* Get a capability by ID.
*/
get(capability_id: string): CapabilityEntry | undefined {
return this.capabilities.get(capability_id)
}
/**
* Convert capability tools to ToolDefinition format.
*/
private convert_to_tool_definitions(manifest: CapabilityManifest): ToolDefinition[] {
return manifest.tools.map(tool => ({
name: tool.name,
category: tool.category || 'custom',
description: `${manifest.name} tool: ${tool.name}`,
input_schema: tool.input_schema || { type: 'object', properties: {} },
permissions: {
read: tool.permissions?.read ?? false,
write: tool.permissions?.write ?? false,
network: tool.permissions?.network ?? false
},
streaming: false
}))
}
}
function create_stub_executor(tool_name: string): (call: any) => Promise<any> {
return async (call: any) => ({
call_id: call.id,
tool_name,
type: 'text' as const,
content: { message: `Tool ${tool_name} executed (capability stub)` },
metadata: { timestamp: new Date().toISOString() }
})
}
export function createCapabilityRegistry(): CapabilityRegistry {
return new CapabilityRegistry()
}
// Placeholder for ToolRegistry type (would be imported in real implementation)
interface ToolRegistry {
register(name: string, definition: ToolDefinition, executor: (call: any) => Promise<any>): void
unregister(name: string): void
}

View File

@@ -0,0 +1,10 @@
/**
* Capabilities module exports
* @module packages/runtime/src/capabilities
*/
export { CapabilityManifestValidator, createCapabilityManifestValidator } from './CapabilityManifestValidator.js'
export type { CapabilityManifest, CapabilityTool, ValidationResult, ValidationError, ValidationWarning } from './CapabilityManifestValidator.js'
export { CapabilityRegistry, createCapabilityRegistry } from './CapabilityRegistry.js'
export type { CapabilityState, CapabilityEntry } from './CapabilityRegistry.js'