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:
2026-06-08 03:11:39 +00:00
parent 74da084521
commit 45a1140fd3
23 changed files with 2938 additions and 33 deletions

View File

@@ -76,6 +76,8 @@ export const MessageStartedFrame = z.object({
message_id: Uuid,
chat_id: Uuid.optional(),
role: MessageRoleValue,
// v2.8-compare: groups messages belonging to the same compare operation.
compare_group_id: z.string().uuid().optional(),
});
export const DeltaFrame = z.object({
@@ -83,6 +85,7 @@ export const DeltaFrame = z.object({
message_id: Uuid,
chat_id: Uuid.optional(),
content: z.string(),
compare_group_id: z.string().uuid().optional(),
});
export const ReasoningDeltaFrame = z.object({
@@ -107,6 +110,10 @@ export const ToolResultFrame = z.object({
output: z.unknown(),
truncated: z.boolean(),
error: z.string().optional(),
// v2.8: unified diff for write tools (edit_file, create_file, etc.).
// Published alongside successful tool results so the frontend can render
// a compact diff snippet inline. Absent for read-only tools or failures.
diff: z.string().optional(),
});
export const MessageCompleteFrame = z.object({
@@ -132,6 +139,7 @@ export const MessageCompleteFrame = z.object({
// web reducer can render a muted "Stopped" / failed state without a new frame
// type. Optional → fail-closed publishFrame must keep, not strip, it.
status: z.enum(['complete', 'cancelled', 'failed']).optional(),
compare_group_id: z.string().uuid().optional(),
});
export const UsageFrame = z.object({
@@ -168,6 +176,7 @@ export const ErrorFrame = z.object({
chat_id: Uuid.optional(),
error: z.string(),
reason: ErrorReasonValue.optional(),
compare_group_id: z.string().uuid().optional(),
});
// ---- per-user channel frames (sidebar refresh) -----------------------------
@@ -472,6 +481,7 @@ const TextChannelPayload = z.object({
message_id: Uuid,
chat_id: Uuid.optional(),
content: z.string(),
compare_group_id: z.string().uuid().optional(),
});
const ToolCallChannelPayload = z.object({
@@ -487,6 +497,7 @@ const ToolResultChannelPayload = z.object({
output: z.unknown(),
truncated: z.boolean(),
error: z.string().optional(),
diff: z.string().optional(),
});
const StatusChannelPayload = z.object({
@@ -534,6 +545,7 @@ export const ChannelDeltaFrame = z.object({
tool_call_id: ToolCallId.optional(),
output: z.unknown().optional(),
truncated: z.boolean().optional(),
diff: z.string().optional(),
});
// ---- discriminated union ---------------------------------------------------