feat: phase 3-5 — workflow engine, background subagents, multi-modal, cache shape, inline diff
Phase 3: Dynamic Workflow Engine - VM sandbox (node:vm) with agent/parallel/pipeline API, Claude Code compatible - Workflow file discovery (.boocode/workflows/*.js + ~/.boocode/workflows/*.js) - Workflow manager with session/chat creation and inference dispatch - Built-in catalog: deep-research, review-code, find-issues - Resumability cache: SHA-256 hash of agent spec, in-memory Map Phase 4: Background Subagents - background-task.ts service: spawn/poll/cancel lifecycle - spawn_subagent, subagent_status, subagent_result tools in ALL_TOOLS Phase 5: Multi-modal + Cache Shape - Multi-modal stub with type defs and hook point in payload.ts - CacheShapeBadge component in trace viewer (colored bar + %)
This commit is contained in:
@@ -4,6 +4,8 @@ import { randomBytes } from 'node:crypto';
|
||||
import type { Sql } from '../db.js';
|
||||
import { resolveWritePath } from './write_guard.js';
|
||||
import { locateMatch } from './fuzzy-match.js';
|
||||
import { conflictIndex } from './conflict-index.js';
|
||||
import { findConflicts } from './collision-detector.js';
|
||||
|
||||
/**
|
||||
* Write a file atomically: stage to a sibling temp file, then rename over the
|
||||
@@ -170,6 +172,10 @@ export async function queueEdit(
|
||||
VALUES (${sessionId}, ${taskId}, ${resolved}, 'edit', ${diff}, ${agent})
|
||||
RETURNING *
|
||||
`;
|
||||
|
||||
// Register in the conflict index so concurrent worktrees see this edit.
|
||||
conflictIndex.registerChange(resolved, sessionId, agent ?? 'unknown');
|
||||
|
||||
return row!;
|
||||
}
|
||||
|
||||
@@ -216,6 +222,9 @@ export async function queueCreate(
|
||||
VALUES (${sessionId}, ${taskId}, ${resolved}, 'create', ${content}, ${agent})
|
||||
RETURNING *
|
||||
`;
|
||||
|
||||
conflictIndex.registerChange(resolved, sessionId, agent ?? 'unknown');
|
||||
|
||||
return row!;
|
||||
}
|
||||
|
||||
@@ -238,6 +247,9 @@ export async function queueDelete(
|
||||
VALUES (${sessionId}, ${taskId}, ${resolved}, 'delete', '', ${agent})
|
||||
RETURNING *
|
||||
`;
|
||||
|
||||
conflictIndex.registerChange(resolved, sessionId, agent ?? 'unknown');
|
||||
|
||||
return row!;
|
||||
}
|
||||
|
||||
@@ -260,6 +272,23 @@ export async function applyOne(
|
||||
// Re-validate path in case projectRoot has shifted
|
||||
resolveWritePath(projectRoot, change.file_path);
|
||||
|
||||
// Advisory collision check: log a warning if another worktree has pending
|
||||
// edits to this file. Does NOT block the write — same non-blocking pattern
|
||||
// as the edit guards (validateEditResult, checkDroppedImports).
|
||||
{
|
||||
const conflicts = conflictIndex.query(
|
||||
[change.file_path],
|
||||
change.session_id, // sessionId doubles as worktree identifier
|
||||
new Map(),
|
||||
);
|
||||
for (const v of conflicts) {
|
||||
console.log(
|
||||
`[collision] ${v.filePath} — conflict with worktrees [${v.worktrees.join(', ')}] ` +
|
||||
`agents [${v.agents.join(', ')}] severity=${v.severity}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
switch (change.operation) {
|
||||
case 'create': {
|
||||
await mkdir(dirname(change.file_path), { recursive: true });
|
||||
|
||||
Reference in New Issue
Block a user