chore: push all design docs, V2 plan specs, and current working state
Includes AirPlan design documents, AircOding-alpha1-plan, AirPlanV2, AirPlan-ParaV2, AirPlan-Para V1 reference docs, and all working code changes across packages. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -14,7 +14,8 @@
|
||||
"clean": "rm -rf dist tsconfig.tsbuildinfo"
|
||||
},
|
||||
"dependencies": {
|
||||
"@aircoding/contracts": "workspace:*"
|
||||
"@aircoding/contracts": "workspace:*",
|
||||
"@aircoding/runtime": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^25.9.1",
|
||||
|
||||
@@ -64,12 +64,12 @@ async function main(): Promise<void> {
|
||||
// Handle exit
|
||||
process.on('SIGTERM', () => {
|
||||
clearInterval(heartbeat_interval)
|
||||
process.exit(0)
|
||||
process.exit(4) // Parent cancelled
|
||||
})
|
||||
|
||||
process.on('SIGINT', () => {
|
||||
clearInterval(heartbeat_interval)
|
||||
process.exit(0)
|
||||
process.exit(4) // Parent cancelled
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
*/
|
||||
|
||||
import { WorkerRuntime } from '../WorkerRuntime.js'
|
||||
import { CompressionValidator } from '@aircoding/runtime'
|
||||
import type { ValidationResult } from '@aircoding/runtime'
|
||||
|
||||
const SUMMARY_PREFIX = `This is a compacted summary of earlier context. Treat it as reference only.
|
||||
The latest user message and any newer runtime events after this summary are the source of truth.
|
||||
@@ -67,7 +69,41 @@ export class CompactorRole {
|
||||
const target_after = Math.max(1, Math.floor(threshold * 0.6))
|
||||
const source_content = compact_spec.source_content || `Current token estimate: ${token_estimate_before}; target budget: ${threshold}.`
|
||||
|
||||
const summary = await this.build_summary(source_content, token_estimate_before, threshold)
|
||||
const first_summary = await this.build_summary(source_content, token_estimate_before, threshold)
|
||||
|
||||
// FR-014 / V2 §3.3.1: Validate compaction quality
|
||||
const validator = new CompressionValidator()
|
||||
const validation = validator.validate(source_content, first_summary)
|
||||
let summary = first_summary
|
||||
|
||||
if (!validation.ok) {
|
||||
this.runtime.checkpoint('compaction_validation_failed', {
|
||||
task_id,
|
||||
missing_patterns: validation.missing.map((m: ValidationResult['missing'][number]) => `${m.pattern}: lost ${m.lost_count}/${m.original_count}`),
|
||||
})
|
||||
// Retry once with explicit preservation instruction
|
||||
const retry_content = `CRITICAL: The previous summary lost critical references. You MUST preserve:\n${validation.missing.map((m: ValidationResult['missing'][number]) => `- ${m.pattern}: ${m.lost.join(', ')}`).join('\n')}\n\nOriginal context:\n${source_content}`
|
||||
const retry_summary = await this.build_summary(retry_content, token_estimate_before, threshold)
|
||||
const retry_validation = validator.validate(source_content, retry_summary)
|
||||
if (!retry_validation.ok) {
|
||||
// Both attempts failed — preserve original context, mark as skipped
|
||||
result.summary_content = source_content.slice(0, threshold * 4) // raw text, char estimate
|
||||
result.status = 'skipped'
|
||||
this.runtime.emit('context.compaction.validation_failed', {
|
||||
event_id: `evt_compaction_fail_${crypto.randomUUID()}`,
|
||||
task_id,
|
||||
agent_id: process.env.AIRCODING_AGENT_ID || 'compactor',
|
||||
range_start_message_id,
|
||||
range_end_message_id,
|
||||
error: { message: `Compaction validation failed after retry: ${retry_validation.missing.map((m: ValidationResult['missing'][number]) => m.pattern).join(', ')}` },
|
||||
evidence_refs: [],
|
||||
metadata: {},
|
||||
})
|
||||
return result
|
||||
}
|
||||
// Retry succeeded — use retry summary
|
||||
summary = retry_summary
|
||||
}
|
||||
const summary_id = `summary_${crypto.randomUUID()}`
|
||||
const token_estimate_after = Math.min(target_after, Math.max(1, Math.floor(summary.length / 4)))
|
||||
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
import { describe, expect, test } from 'bun:test'
|
||||
import { ExecutorRole } from '../src/roles/ExecutorRole.js'
|
||||
|
||||
class NativeToolRuntime {
|
||||
turns = 0
|
||||
calls: Array<{ name: string; args: Record<string, unknown> }> = []
|
||||
emit() {}
|
||||
heartbeat() {}
|
||||
checkpoint() {}
|
||||
async call_llm() {
|
||||
this.turns++
|
||||
if (this.turns === 1) {
|
||||
return { content: '', tool_calls: [{ id: 'tu_1', name: 'fs.write', arguments: { path: 'a.txt', content: 'X' } }] }
|
||||
}
|
||||
return { content: 'DONE' }
|
||||
}
|
||||
async call_tool(name: string, args: Record<string, unknown>) {
|
||||
this.calls.push({ name, args })
|
||||
return { call_id: 'c', type: 'text' as const, content: { ok: true } }
|
||||
}
|
||||
}
|
||||
|
||||
class VerificationFailRuntime {
|
||||
turns = 0
|
||||
checkpointed = false
|
||||
emit() {}
|
||||
heartbeat() {}
|
||||
checkpoint() { this.checkpointed = true }
|
||||
async call_llm() {
|
||||
this.turns++
|
||||
if (this.turns === 1) {
|
||||
return { content: '', tool_calls: [{ id: 'tu_main', name: 'fs.write', arguments: { path: 'main.cpp', content: 'int main(){return 0;}' } }] }
|
||||
}
|
||||
return { content: 'DONE' }
|
||||
}
|
||||
async call_tool(name: string, _args: Record<string, unknown>) {
|
||||
if (name === 'fs.write') return { call_id: 'w', type: 'text' as const, content: { ok: true } }
|
||||
if (name === 'shell.run') return { call_id: 's', type: 'error' as const, content: { exit_code: 1, stderr: 'compile failed' } }
|
||||
return { call_id: 'x', type: 'error' as const, content: { message: 'unexpected tool' } }
|
||||
}
|
||||
}
|
||||
|
||||
describe('ExecutorRole structured tool execution', () => {
|
||||
test('executes native tool_calls instead of regex text parsing', async () => {
|
||||
const runtime = new NativeToolRuntime()
|
||||
const result = await new ExecutorRole(runtime as any).run({
|
||||
id: 't1',
|
||||
title: 'create file',
|
||||
description: 'create a file',
|
||||
acceptance_criteria: ['file exists'],
|
||||
})
|
||||
|
||||
expect(result.status).toBe('completed')
|
||||
expect(runtime.calls).toEqual([{ name: 'fs.write', args: { path: 'a.txt', content: 'X' } }])
|
||||
})
|
||||
|
||||
test('does not complete executable tasks when verification fails', async () => {
|
||||
const runtime = new VerificationFailRuntime()
|
||||
const result = await new ExecutorRole(runtime as any).run({
|
||||
id: 't2',
|
||||
title: 'compile cpp',
|
||||
description: 'write and compile C++',
|
||||
acceptance_criteria: ['must compile'],
|
||||
})
|
||||
|
||||
expect(result.status).not.toBe('completed')
|
||||
expect(runtime.checkpointed).toBe(false)
|
||||
expect(result.changes).toEqual([{ file: 'main.cpp', type: 'create' }])
|
||||
})
|
||||
})
|
||||
@@ -1,11 +1,8 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src"
|
||||
"skipLibCheck": true,
|
||||
"outDir": "./dist"
|
||||
},
|
||||
"include": ["src"],
|
||||
"references": [
|
||||
{ "path": "../contracts" }
|
||||
]
|
||||
"include": ["src/**/*"]
|
||||
}
|
||||
Reference in New Issue
Block a user