主线 A(事件驱动落库): - 统一 EventStore 模块单例:RuntimeApp 不再 new EventStore,改用 eventStore 并 setRepositories(14 个 domain repo),消除事件流向空 DB 的割裂 - Scheduler.create_tasks 改为 async,真正发出 task.created 事件 - run.ts dispatchTask 加 await - 主线 A 独立复审发现并修复关键假绿:四个 repo(Task/Agent/ToolRun/ TaskAttempt)的 *Update 类型 Omit<'status'> 且 update() 主动丢弃 status, 导致 EventStore.project() 的状态写入全部静默失效,DB 行内容 tasks.status 永远冻结在 pending,UI 显示的 completed 来自内存 graph。已修,DB 现 真实反映 task.status=completed - 补 agent.started/agent.completed/agent.failed 事件发出(之前 agents 表 恒空),修复后 agents 表有正确行+status 主线 B1(结构化工具调用块类型,N1): - 新增 content-block.ts 定义 Anthropic canonical content blocks (TextBlock/ThinkingBlock/ToolUseBlock/ToolResultBlock/CanonicalMessage) - provider.ts ProviderCompletionInput 去掉 unknown 逃生舱: messages: CanonicalMessage[], tools?: ToolDefinitionBlock[], tool_choice?: ToolChoice, system?: string | TextBlock[] 主线 B2(read-before-edit 代码层强制,FR-009): - fs/index.ts 新增 readFileState 机制(移植 claude-code FileEditTool), fs.edit 执行前检查:未读先改报 "File has not been read yet",外部修改 报 "File has been unexpectedly modified" - 修复 fs.edit 参数名不匹配:兼容 old_str/new_str (ExecutorRole) 和 find/replace (UI) 两种命名 - fs_edit 唯一性检查(非 global 模式下 old_str 出现多次报错) 真实验收: - TSC=0 - air run 后 DB:events=5(原 3,+agent.started/completed), tasks.status=completed(原 frozen pending),agents 1 行 status=completed - read-before-edit 行为测试:未读先改 status=error,读后再改 status=ok Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
298 lines
8.1 KiB
TypeScript
Executable File
298 lines
8.1 KiB
TypeScript
Executable File
/**
|
|
* AirCoding Provider Contracts
|
|
*
|
|
* Implements ProviderCapabilityMatrix, ModelRequirement, ProviderCompletionInput,
|
|
* ProviderStreamEvent, ProviderAdapter, and ProviderManager interfaces
|
|
* per interface-contracts-v1.md §15 and system-detailed-design.md §22.6.
|
|
*/
|
|
|
|
// Import IDs needed for these types
|
|
import type {
|
|
ProviderID,
|
|
ModelID,
|
|
JsonObject,
|
|
} from './ids'
|
|
|
|
// Import content block types for canonical message format
|
|
import type {
|
|
CanonicalMessage,
|
|
TextBlock,
|
|
ToolDefinitionBlock,
|
|
ToolChoice,
|
|
} from './content-block'
|
|
|
|
// =============================================================================
|
|
// §15 — Provider Contracts
|
|
// =============================================================================
|
|
|
|
/**
|
|
* Provider kind types supported by the system.
|
|
* Matches provider-capability-matrix-v1.md §2.
|
|
*/
|
|
export type ProviderKind =
|
|
| 'anthropic'
|
|
| 'openai'
|
|
| 'openrouter'
|
|
| 'ollama'
|
|
| 'anthropic_compatible'
|
|
| 'openai_compatible'
|
|
| 'custom'
|
|
|
|
/**
|
|
* Quality tier classification for models.
|
|
*/
|
|
export type QualityTier = 'frontier' | 'strong' | 'standard' | 'cheap' | 'local' | 'unknown'
|
|
|
|
/**
|
|
* Cost tier classification for models.
|
|
*/
|
|
export type CostTier = 'high' | 'medium' | 'low' | 'free' | 'unknown'
|
|
|
|
/**
|
|
* Provider identity - core provider metadata.
|
|
* Matches provider-capability-matrix-v1.md §2.
|
|
*/
|
|
export interface ProviderIdentity {
|
|
provider_id: ProviderID
|
|
provider_kind: ProviderKind
|
|
display_name: string
|
|
base_url?: string
|
|
auth_ref?: string
|
|
local: boolean
|
|
}
|
|
|
|
/**
|
|
* Capability matrix for a specific model on a provider.
|
|
* Matches interface-contracts-v1.md §15 and provider-capability-matrix-v1.md §3.
|
|
*/
|
|
export interface ProviderCapabilityMatrix {
|
|
provider_id: ProviderID
|
|
provider_kind: ProviderKind
|
|
model_id: ModelID
|
|
display_name?: string
|
|
enabled: boolean
|
|
quality_tier: QualityTier
|
|
cost_tier: CostTier
|
|
context_window_tokens?: number
|
|
max_output_tokens?: number
|
|
supports: ProviderSupports
|
|
conversion: ProviderConversion
|
|
limits?: ProviderLimits
|
|
default_use?: ProviderDefaultUse
|
|
notes?: string[]
|
|
}
|
|
|
|
/**
|
|
* Supported capabilities for a model.
|
|
* Per provider-capability-matrix-v1.md §3.
|
|
*/
|
|
export interface ProviderSupports {
|
|
text_input: boolean
|
|
text_output: boolean
|
|
streaming: boolean
|
|
tool_use: boolean
|
|
parallel_tool_use: boolean
|
|
structured_output: boolean
|
|
json_mode: boolean
|
|
thinking: boolean
|
|
prompt_cache: boolean
|
|
system_prompt: boolean
|
|
image_input: boolean
|
|
image_output: boolean
|
|
audio_input: boolean
|
|
audio_output: boolean
|
|
file_input: boolean
|
|
computer_use: boolean
|
|
long_context: boolean
|
|
batch: boolean
|
|
}
|
|
|
|
/**
|
|
* Conversion behavior for provider adapter.
|
|
* Per provider-capability-matrix-v1.md §3.
|
|
*/
|
|
export interface ProviderConversion {
|
|
from_anthropic_canonical: 'lossless' | 'lossy' | 'unsupported'
|
|
tool_schema: 'native' | 'converted' | 'emulated' | 'unsupported'
|
|
image_input: 'native' | 'artifact_link' | 'unsupported'
|
|
thinking: 'native' | 'stripped' | 'unsupported'
|
|
cache_control: 'native' | 'ignored' | 'unsupported'
|
|
}
|
|
|
|
/**
|
|
* Rate limits for a model.
|
|
* Per provider-capability-matrix-v1.md §3.
|
|
*/
|
|
export interface ProviderLimits {
|
|
requests_per_minute?: number
|
|
tokens_per_minute?: number
|
|
concurrent_requests?: number
|
|
max_tool_schema_bytes?: number
|
|
max_image_count?: number
|
|
max_file_bytes?: number
|
|
}
|
|
|
|
/**
|
|
* Default use cases for a model.
|
|
* Per provider-capability-matrix-v1.md §3.
|
|
*/
|
|
export interface ProviderDefaultUse {
|
|
main?: boolean
|
|
architecture?: boolean
|
|
execute?: boolean
|
|
review?: boolean
|
|
debug?: boolean
|
|
compact?: boolean
|
|
mine_experience?: boolean
|
|
}
|
|
|
|
/**
|
|
* Model requirement specification for task execution.
|
|
* Per interface-contracts-v1.md §15 and provider-capability-matrix-v1.md §5.
|
|
*/
|
|
export interface ModelRequirement {
|
|
required: Partial<ProviderSupports>
|
|
preferred?: Partial<ProviderSupports>
|
|
min_quality_tier?: 'frontier' | 'strong' | 'standard' | 'cheap' | 'local'
|
|
max_cost_tier?: 'high' | 'medium' | 'low' | 'free'
|
|
min_context_window_tokens?: number
|
|
allow_lossy_conversion?: boolean
|
|
}
|
|
|
|
/**
|
|
* Model assignment mode - how the model was selected.
|
|
* Per provider-capability-matrix-v1.md §6.
|
|
*/
|
|
export type ModelAssignmentMode = 'scheduler_forced' | 'agent_select'
|
|
|
|
/**
|
|
* Model assignment - the selected model for a task.
|
|
* Per interface-contracts-v1.md §9 and §15, and provider-capability-matrix-v1.md §6.
|
|
* Note: This is also defined in task.ts for scheduler use - this re-export ensures
|
|
* both modules have access to the same type definition.
|
|
*/
|
|
export interface ModelAssignment {
|
|
mode: ModelAssignmentMode
|
|
provider_id?: ProviderID
|
|
model_id?: ModelID
|
|
allowed_models?: Array<{ provider_id: ProviderID; model_id: ModelID }>
|
|
requirement: ModelRequirement
|
|
reason: string
|
|
}
|
|
|
|
/**
|
|
* Input for a provider completion request.
|
|
* Per interface-contracts-v1.md §15 and provider-capability-matrix-v1.md §7.
|
|
*/
|
|
export interface ProviderCompletionInput {
|
|
provider_id: ProviderID
|
|
model_id: ModelID
|
|
canonical_format: 'anthropic'
|
|
messages: CanonicalMessage[]
|
|
tools?: ToolDefinitionBlock[]
|
|
tool_choice?: ToolChoice
|
|
system?: string | TextBlock[]
|
|
max_output_tokens?: number
|
|
temperature?: number
|
|
metadata?: JsonObject
|
|
}
|
|
|
|
/**
|
|
* Stream event types from provider.
|
|
* Per interface-contracts-v1.md §15.
|
|
*/
|
|
export type ProviderStreamEventType =
|
|
| 'message_start'
|
|
| 'content_delta'
|
|
| 'tool_use'
|
|
| 'message_stop'
|
|
| 'error'
|
|
|
|
/**
|
|
* A stream event from the provider.
|
|
* Per interface-contracts-v1.md §15.
|
|
*/
|
|
export interface ProviderStreamEvent {
|
|
type: ProviderStreamEventType
|
|
payload: unknown
|
|
}
|
|
|
|
/**
|
|
* Conversion report for provider adaptation.
|
|
* Per provider-capability-matrix-v1.md §8.
|
|
*/
|
|
export interface ProviderConversionReport {
|
|
status: 'lossless' | 'lossy' | 'unsupported'
|
|
omissions: string[]
|
|
warnings: string[]
|
|
required_confirmation?: boolean
|
|
}
|
|
|
|
/**
|
|
* Provider adapter interface - the contract for all LLM provider implementations.
|
|
* Per interface-contracts-v1.md §15 and system-detailed-design.md §22.6.
|
|
*
|
|
* Adapter responsibilities (per provider-capability-matrix-v1.md §7):
|
|
* 1. Convert Anthropic canonical messages to provider format.
|
|
* 2. Convert provider output back to Anthropic canonical content blocks or RuntimeEvents.
|
|
* 3. Validate tool-call and structured-output compatibility.
|
|
* 4. Record conversion omissions/losses.
|
|
* 5. Never leak credentials into logs, events, artifacts, or model-visible messages.
|
|
*/
|
|
export interface ProviderAdapter {
|
|
/** Unique identifier for this adapter instance */
|
|
provider_id: ProviderID
|
|
|
|
/**
|
|
* List all available models for this provider.
|
|
* Returns capability matrix for each model.
|
|
*/
|
|
list_models(): Promise<ProviderCapabilityMatrix[]>
|
|
|
|
/**
|
|
* Validate and get capability matrix for a specific model.
|
|
* @throws Error if model is not available
|
|
*/
|
|
validate_model(model_id: ModelID): Promise<ProviderCapabilityMatrix>
|
|
|
|
/**
|
|
* Execute a completion request.
|
|
* Yields stream events as they arrive from the provider.
|
|
*/
|
|
complete(input: ProviderCompletionInput): AsyncIterable<ProviderStreamEvent>
|
|
|
|
/**
|
|
* Optional: Count tokens for a given input.
|
|
* Useful for context budgeting and cost estimation.
|
|
*/
|
|
count_tokens?(input: unknown): Promise<number>
|
|
}
|
|
|
|
/**
|
|
* Provider manager interface - the orchestration layer for model selection and completion.
|
|
* Per interface-contracts-v1.md §15 and system-detailed-design.md §12.1.
|
|
*
|
|
* The ProviderManager is the runtime facade that:
|
|
* - Loads and manages provider configuration
|
|
* - Selects appropriate models based on requirements
|
|
* - Routes completion requests to the appropriate adapter
|
|
*/
|
|
export interface ProviderManager {
|
|
/**
|
|
* Load provider configuration from global and project sources.
|
|
* Should be called at startup or when configuration changes.
|
|
*/
|
|
load_config(): Promise<void>
|
|
|
|
/**
|
|
* Select an appropriate model based on requirements.
|
|
* @returns ModelAssignment with the selected provider/model
|
|
*/
|
|
select_model(requirement: ModelRequirement): Promise<ModelAssignment>
|
|
|
|
/**
|
|
* Execute a completion request.
|
|
* Yields normalized stream events.
|
|
*/
|
|
complete(input: ProviderCompletionInput): AsyncIterable<ProviderStreamEvent>
|
|
} |