Files
AirCoding/packages/tui/src/ProjectionClient.ts
AirCoding ea7cf427dd fix: tsc 0 errors + depcruise 0 violations + all GA blockers closed
Changes (37 files, +1159/-587):
- tsconfig: moduleResolution bundler + paths alias for bun:sqlite
- bun-sqlite.ts: type shim replacing stale declare module .d.ts
- All 7 tool files: ToolDefinition alignment (version, output_schema,
  ToolPermissionSpec read_paths/write_paths, ToolCall.call_id)
- 2 adapters: ProviderAdapter implements + ProviderCapabilityMatrix shape
  (provider_kind, enabled, quality_tier, cost_tier, conversion)
- PathClassifier: 9 categories aligned (credential_store, project_air_*)
- CommandRiskAnalyzer: remove unused imports
- Recovery: Database field + scanOrphanReferences FK-off 8 invariants
- Scheduler: rebuild_from_db from session DB tasks
- ProjectionStore: 20+ event types, subscribe, rebuild from repos
- MigrationRunner: constructor accepts optional db_path
- e2e.ts: replaced hardcoded  with 14 real test/check gates
- wiring.ts: eventIngestor.ingest (durable path, INV-2)
- init.ts: ToolRegistry+PermissionEngine path (INV-3)
- TUI: local ProjectionClient (INV-4)
- MainAgent: classify_via_llm with real ProviderManager invocation
- WorkerMessage: kind/session_id/agent_id/correlation_id (contracts §10)
- WorkerProcess exit code 4 = parent_cancelled

Validation gates:
- tsc --noEmit: 0 errors
- depcruise: 0 violations (28 modules)
- tests: 169/169 pass

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 11:43:19 +08:00

73 lines
1.8 KiB
TypeScript
Executable File

/**
* ProjectionClient - Local TUI-side projection consumer
* TUI copies minimal projection types from contracts to avoid INV-4 violation
* (TUI must only depend on contracts; dd §13.2 / c4/code-view §2 rule 3).
*
* The runtime package provides the authoritative ProjectionClient in
* `runtime/projection/ProjectionClient.ts`. TUI defines its own local copy
* with the same surface so that subscriptions work in-process.
*
* @module packages/tui/src/ProjectionClient
*/
import type { SessionID, ProjectID, TaskID, AgentID, ISOTimeString } from '@aircoding/contracts'
export interface SessionProjection {
session_id: SessionID
project_id: ProjectID
status: string
title?: string
tasks: TaskProjection[]
agents: AgentProjection[]
}
export interface TaskProjection {
id: TaskID
type: string
status: string
title: string
retry_count: number
attempts: number
created_at: string
}
export interface AgentProjection {
id: AgentID
type: string
status: string
task_id?: TaskID
last_heartbeat?: string
}
export type ProjectionSubscriber = (projection: SessionProjection) => void
export class ProjectionClient {
private snapshot: SessionProjection | null = null
private subscribers: Set<ProjectionSubscriber> = new Set()
/**
* Receive and cache a projection snapshot.
*/
receive_snapshot(projection: SessionProjection): void {
this.snapshot = projection
for (const sub of this.subscribers) {
sub(projection)
}
}
/**
* Subscribe to projection updates.
*/
subscribe(subscriber: ProjectionSubscriber): () => void {
this.subscribers.add(subscriber)
return () => this.subscribers.delete(subscriber)
}
/**
* Get current snapshot.
*/
get_snapshot(): SessionProjection | null {
return this.snapshot
}
}