feat(KB-123): add agent role tracking and expanded logging to agent system
- Extend AgentLogEntry with agent field and new event types (thinking, tool_end) - Expand AgentLogger with thinking, tool_end callbacks and agent role support - Wire new logging callbacks in createKbAgent and all agent call-sites (executor, merger, reviewer, triage, pi) - Update AgentLogViewer with agent role badges and rendering for new entry types - Export AgentRole and AgentLogType from core package and add tests for new functionality
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
export { COLUMNS, COLUMN_LABELS, COLUMN_DESCRIPTIONS, VALID_TRANSITIONS, DEFAULT_SETTINGS, THINKING_LEVELS } from "./types.js";
|
||||
export type { Column, Task, TaskAttachment, TaskCreateInput, TaskDetail, AgentLogEntry, BoardConfig, MergeResult, Settings, TaskStep, StepStatus, TaskLogEntry, ThinkingLevel } from "./types.js";
|
||||
export type { Column, Task, TaskAttachment, TaskCreateInput, TaskDetail, AgentLogEntry, AgentLogType, AgentRole, BoardConfig, MergeResult, Settings, TaskStep, StepStatus, TaskLogEntry, ThinkingLevel } from "./types.js";
|
||||
export { TaskStore } from "./store.js";
|
||||
export { canTransition, getValidTransitions, resolveDependencyOrder } from "./board.js";
|
||||
|
||||
@@ -610,6 +610,52 @@ describe("TaskStore", () => {
|
||||
expect(logs).toHaveLength(5);
|
||||
expect(logs[4].text).toBe("chunk 4");
|
||||
});
|
||||
|
||||
it("appendAgentLog persists and reads back the agent field", async () => {
|
||||
const task = await createTestTask();
|
||||
|
||||
await store.appendAgentLog(task.id, "hello", "text", undefined, "executor");
|
||||
await store.appendAgentLog(task.id, "Read", "tool", "file.ts", "triage");
|
||||
|
||||
const logs = await store.getAgentLogs(task.id);
|
||||
expect(logs).toHaveLength(2);
|
||||
expect(logs[0].agent).toBe("executor");
|
||||
expect(logs[1].agent).toBe("triage");
|
||||
});
|
||||
|
||||
it("appendAgentLog omits agent field when not provided", async () => {
|
||||
const task = await createTestTask();
|
||||
|
||||
await store.appendAgentLog(task.id, "hello", "text");
|
||||
|
||||
const logs = await store.getAgentLogs(task.id);
|
||||
expect(logs).toHaveLength(1);
|
||||
expect(logs[0]).not.toHaveProperty("agent");
|
||||
});
|
||||
|
||||
it("new type values (thinking, tool_result, tool_error) round-trip correctly", async () => {
|
||||
const task = await createTestTask();
|
||||
|
||||
await store.appendAgentLog(task.id, "internal thought", "thinking", undefined, "executor");
|
||||
await store.appendAgentLog(task.id, "Bash", "tool_result", "output summary", "executor");
|
||||
await store.appendAgentLog(task.id, "Read", "tool_error", "file not found", "reviewer");
|
||||
|
||||
const logs = await store.getAgentLogs(task.id);
|
||||
expect(logs).toHaveLength(3);
|
||||
|
||||
expect(logs[0].type).toBe("thinking");
|
||||
expect(logs[0].text).toBe("internal thought");
|
||||
expect(logs[0].agent).toBe("executor");
|
||||
|
||||
expect(logs[1].type).toBe("tool_result");
|
||||
expect(logs[1].text).toBe("Bash");
|
||||
expect(logs[1].detail).toBe("output summary");
|
||||
|
||||
expect(logs[2].type).toBe("tool_error");
|
||||
expect(logs[2].text).toBe("Read");
|
||||
expect(logs[2].detail).toBe("file not found");
|
||||
expect(logs[2].agent).toBe("reviewer");
|
||||
});
|
||||
});
|
||||
|
||||
describe("columnMovedAt", () => {
|
||||
|
||||
@@ -882,17 +882,25 @@ export class TaskStore extends EventEmitter<TaskStoreEvents> {
|
||||
* Also emits an `agent:log` event for live streaming.
|
||||
*
|
||||
* @param taskId - The task ID (e.g. "KB-001")
|
||||
* @param text - The text content (delta for "text", tool name for "tool")
|
||||
* @param type - Whether this is a "text" delta or a "tool" invocation marker
|
||||
* @param detail - Optional human-readable summary of tool args (e.g. file path, command)
|
||||
* @param text - The text content (delta for "text"/"thinking", tool name for "tool"/"tool_result"/"tool_error")
|
||||
* @param type - The entry type discriminator
|
||||
* @param detail - Optional human-readable summary (tool args, result summary, or error message)
|
||||
* @param agent - Optional agent role that produced this entry
|
||||
*/
|
||||
async appendAgentLog(taskId: string, text: string, type: "text" | "tool", detail?: string): Promise<void> {
|
||||
async appendAgentLog(
|
||||
taskId: string,
|
||||
text: string,
|
||||
type: AgentLogEntry["type"],
|
||||
detail?: string,
|
||||
agent?: AgentLogEntry["agent"],
|
||||
): Promise<void> {
|
||||
const entry: AgentLogEntry = {
|
||||
timestamp: new Date().toISOString(),
|
||||
taskId,
|
||||
text,
|
||||
type,
|
||||
...(detail !== undefined && { detail }),
|
||||
...(agent !== undefined && { agent }),
|
||||
};
|
||||
const dir = this.taskDir(taskId);
|
||||
const logPath = join(dir, "agent.log");
|
||||
|
||||
@@ -18,18 +18,27 @@ export interface TaskLogEntry {
|
||||
outcome?: string;
|
||||
}
|
||||
|
||||
/** A single chunk of agent output (text delta or tool invocation) persisted to disk. */
|
||||
/** The set of agent roles that produce log entries. */
|
||||
export type AgentRole = "triage" | "executor" | "reviewer" | "merger";
|
||||
|
||||
/** The discriminator for agent log entry types. */
|
||||
export type AgentLogType = "text" | "tool" | "thinking" | "tool_result" | "tool_error";
|
||||
|
||||
/** A single chunk of agent output persisted to disk (JSONL in agent.log). */
|
||||
export interface AgentLogEntry {
|
||||
/** ISO-8601 timestamp of when the entry was recorded */
|
||||
/** ISO-8601 timestamp of when the entry was recorded. */
|
||||
timestamp: string;
|
||||
/** The task this log entry belongs to */
|
||||
/** The task this log entry belongs to. */
|
||||
taskId: string;
|
||||
/** The text content (delta for "text", tool name for "tool") */
|
||||
/** The text content (delta for "text"/"thinking", tool name for "tool"/"tool_result"/"tool_error"). */
|
||||
text: string;
|
||||
/** Whether this is a text delta or a tool invocation marker */
|
||||
type: "text" | "tool";
|
||||
/** For tool entries: human-readable summary of tool args (e.g. file path, command) */
|
||||
/** The kind of entry — text delta, tool invocation marker, thinking block, tool result, or tool error. */
|
||||
type: AgentLogType;
|
||||
/** For tool entries: human-readable summary of tool args (e.g. file path, command).
|
||||
* For tool_result/tool_error: summary of the result or error message. */
|
||||
detail?: string;
|
||||
/** Which agent produced this entry. Absent in logs written before this field was added. */
|
||||
agent?: AgentRole;
|
||||
}
|
||||
|
||||
export interface TaskAttachment {
|
||||
|
||||
Reference in New Issue
Block a user