Files
united-workforce/packages/workflow-agent-builtin/src/detail.ts
T
xingyue 330db43b5f feat(builtin-agent): persist ReAct loop turns as session JSONL
Each turn (assistant response / tool result) is appended to a JSONL file
at ~/.uncaged/workflow/sessions/<sessionId>.jsonl during the loop.

On completion, the JSONL is read back, each turn is stored as a CAS node,
and the detail payload references them as a flat turns[] array in
chronological order. The session file is then deleted.

Benefits:
- Real-time observability: tail -f the JSONL to watch loop progress
- Crash recovery: partial JSONL survives process death
- Zero write contention: one file per session
- Detail stays a flat array for easy consumption by CLI/dashboard

Changes:
- New session.ts: initSessionDir, appendSessionTurn, readSessionTurns, removeSession
- loop.ts: append JSONL each turn instead of accumulating in-memory
- detail.ts: reads session JSONL → persists turns to CAS → stores detail
- agent.ts: passes storageRoot/sessionId to loop, cleans up session on completion
- types.ts: remove index from TurnPayload (order is implicit in JSONL/array)
- schemas.ts: sync with type changes

Ref: #433
2026-05-23 18:27:28 +08:00

50 lines
1.5 KiB
TypeScript

import { bootstrap, putSchema, type Store } from "@uncaged/json-cas";
import { BUILTIN_DETAIL_SCHEMA, BUILTIN_TURN_SCHEMA } from "./schemas.js";
import { readSessionTurns } from "./session.js";
import type { BuiltinDetailPayload } from "./types.js";
type BuiltinSchemaHashes = {
turn: string;
detail: string;
};
export async function registerBuiltinSchemas(store: Store): Promise<BuiltinSchemaHashes> {
await bootstrap(store);
const [turn, detail] = await Promise.all([
putSchema(store, BUILTIN_TURN_SCHEMA),
putSchema(store, BUILTIN_DETAIL_SCHEMA),
]);
return { turn, detail };
}
/** Read session jsonl, persist each turn to CAS, return detail hash. */
export async function storeBuiltinDetail(
store: Store,
storageRoot: string,
sessionId: string,
model: string,
startedAtMs: number,
nowMs: number = Date.now(),
): Promise<{ detailHash: string; turnCount: number }> {
const schemas = await registerBuiltinSchemas(store);
const turns = await readSessionTurns(storageRoot, sessionId);
const turnHashes: string[] = [];
for (const turn of turns) {
const hash = await store.put(schemas.turn, turn);
turnHashes.push(hash);
}
const duration = Math.max(0, nowMs - startedAtMs);
const detail: BuiltinDetailPayload = {
sessionId,
model,
duration,
turnCount: turnHashes.length,
turns: turnHashes,
};
const detailHash = await store.put(schemas.detail, detail);
return { detailHash, turnCount: turnHashes.length };
}