UNPKG

@sema-agent/core

Version:

Stateless, task-oriented AI agent core

98 lines (97 loc) • 5.66 kB
import type { Message } from "../../internal/llm.js"; /** * design/152 S4b D-2 — READ-ONLY adapter over the Claude Code subagent transcript sidecars * (`~/.claude/projects/<proj>/<sid>/subagents/agent-<id>.jsonl` + companion * `agent-<id>.meta.json`), so a sema engine taking over a CC-created team (D-3: sequential * handover) can enumerate the teammates' transcript sidecars and project them into sema message * shapes for resume context. * * D-2 ruling (design/152 §3): the sidecar is CC→sema ONE-WAY. sema's transcript truth source * is the SessionStore — this module never writes, and nothing here mints a second truth source * (the dual-truth-source pathology design/151 just eliminated stays eliminated). Dual-write is a * separate ruling gated on a real user need. * * Disk shape (cc-teams-diskform README): * - flat sidecars: `subagents/agent-<id>.jsonl` * - nested children: `subagents/<parentSeg>/agent-<id>.jsonl` (parent-segment subdirectories) * - workflow runs: `subagents/workflows/<runId>/agent-<t>.jsonl` * - remote agents: `subagents/remote-agents/...` (CC's own migration walks the three siblings; * we walk EVERY subdirectory — symlinks are not followed) * - companion meta: `agent-<id>.meta.json` = `{agentType, description, name, toolUseId, * spawnDepth}` (revival lookup keys; same family as BackgroundAgentRecord's meta-sidecar parity * fields). Absent meta is tolerated (fail-soft) — the jsonl alone still enumerates. * * Line container = the CC session-entry envelope (same container as the main-session transcript): * `{type:"user"|"assistant", message:{...}, uuid, parentUuid, timestamp, ...}` interleaved with * attachment/marker lines that carry no `message`. Projection keeps ONLY the message-bearing * user/assistant lines; everything else is counted, never silently vanished. * * Locking: none. CC appends whole lines; reads tolerate a torn trailing line as one malformed line * (counted). D-3 promises sequential handover only — a concurrently-writing CC process is outside * the supported envelope, and read-side locks would not widen it. */ /** Companion `agent-<id>.meta.json` shape (loose — unknown keys ride through verbatim). */ export interface CcSidecarMeta extends Record<string, unknown> { agentType?: string; description?: string; name?: string; toolUseId?: string; spawnDepth?: number; } /** One enumerated sidecar transcript (jsonl + optional companion meta). */ export interface CcSidecarHandle { /** The `<id>` from the `agent-<id>.jsonl` filename. */ agentId: string; /** Absolute path to the transcript jsonl. */ jsonlPath: string; /** Directory segments under `subagents/` (nesting/workflows/remote-agents); flat sidecar = []. */ segments: string[]; /** Parsed companion meta; `undefined` when the meta file is absent or unparseable (fail-soft). */ meta?: CcSidecarMeta; } /** * Enumerate every transcript sidecar under a CC `subagents/` directory, walking nested * parent-segment subdirectories (symlinks not followed). Missing directory ⇒ `[]` — the CC tree is * a PARTIAL tree (headless runs write sidecars but a REPL-only subtree may be absent entirely), so * absence is a normal state, never a throw. * Order is deterministic: lexicographic by (segments join, agentId). */ export declare function listCcSidecarAgents(subagentsDir: string): CcSidecarHandle[]; /** One parsed jsonl line, verbatim (the raw CC session-entry envelope). */ export interface CcSidecarLine { raw: Record<string, unknown>; } /** A parsed sidecar transcript: verbatim lines + an honest malformed-line count. */ export interface CcSidecarTranscript { lines: CcSidecarLine[]; /** Lines that failed JSON.parse or were not objects (e.g. a torn trailing append). */ malformedLines: number; } export declare function readCcSidecarTranscript(jsonlPath: string): CcSidecarTranscript; /** Projection result: sema-shaped messages + honest counts for what was NOT projected. */ export interface CcSidecarMessages { messages: Message[]; /** Envelope lines without a projectable user/assistant message (attachments, markers, …). */ skippedLines: number; } /** * Project parsed sidecar lines into sema message shapes for resume context. * * Mapping (CC session-entry → sema): * - `type:"user"`, string content → {@link UserMessage} verbatim. * - `type:"user"`, array content → contiguous `text`/`image` runs fold into * {@link UserMessage}s and `tool_result` blocks become {@link ToolResultMessage} rows, in DISK * BLOCK ORDER (toolName recovered from the preceding assistant `tool_use` id — "unknown" when * the pairing is outside the file; base64 image sources map to sema ImageContent). * - `type:"assistant"` → {@link AssistantMessage}; `text`/`thinking`/ * `redacted_thinking`/`tool_use` blocks map to their sema block shapes; `api:"anthropic-messages"`, * `provider:"claude-code"` (an honest origin label — this transcript was produced by CC, not by * a sema brain), model/usage/stop_reason carried over (`cost` unpriced-zero). * - anything else (attachment lines, marker lines, unknown block types) → counted in * `skippedLines`, never a throw and never a silent drop. */ export declare function ccSidecarToMessages(lines: CcSidecarLine[]): CcSidecarMessages; /** Convenience: enumerate + parse + project one sidecar in a single read-only call. */ export declare function readCcSidecarMessages(handle: CcSidecarHandle): CcSidecarMessages & { malformedLines: number; };