blob: 208d820a6bc5176e307f5833a8ecb287d19b49ee [file]
import { resolve } from 'node:path';
import type { AgentRunHeader, SessionHeader } from '@maka/core';
import {
inspectTaskRun,
openHeadlessStorageForRead,
renderTaskRunInspectTree,
type TaskRunInspectDocument,
type TaskRunReader,
} from '@maka/headless';
import {
inspectAgentRunDocument,
inspectSessionDocument,
renderAgentRunInspectTree,
renderSessionInspectTree,
type RuntimeEventInspectReader,
type SessionAgentRunInspectReader,
type AgentRunInspectDocument,
type SessionInspectDocument,
} from '@maka/runtime';
import {
openInteractiveExecutionStoresForRead,
type ExecutionStoresReader,
} from '@maka/storage/execution-stores';
import {
discoverMarkedStorageRoot,
StorageRootAuthorityError,
tryAcquireInteractiveRootReader,
} from '@maka/storage/root-authority';
import { resolveMakaWorkspaceRoot } from './workspace-root.js';
export const INSPECT_RESOLUTION_SCHEMA_VERSION = 'maka.inspect_resolution.v1' as const;
const isStorageRootAuthorityError = (error: unknown): error is StorageRootAuthorityError =>
error instanceof StorageRootAuthorityError;
export type InspectEntityKind = 'session' | 'agent-run' | 'task-run';
export interface InspectCandidateDescriptor {
kind: InspectEntityKind;
id: string;
sessionId?: string;
}
export interface InspectResolutionDocument {
schemaVersion: typeof INSPECT_RESOLUTION_SCHEMA_VERSION;
kind: 'inspect_resolution';
query: {
id: string;
requestedKind?: InspectEntityKind;
sessionId?: string;
};
status: 'not_found' | 'ambiguous';
candidates: InspectCandidateDescriptor[];
}
export type InspectDocument =
| SessionInspectDocument
| AgentRunInspectDocument
| TaskRunInspectDocument;
interface InspectSessionReader {
list(): Promise<readonly { id: string }[]>;
readHeader(id: string): Promise<SessionHeader>;
}
export interface InspectCommandStores {
sessionStore: InspectSessionReader;
agentRunStore: SessionAgentRunInspectReader;
runtimeEventStore: RuntimeEventInspectReader;
taskRunStore?: TaskRunReader;
}
interface SessionCandidate extends InspectCandidateDescriptor {
kind: 'session';
header: SessionHeader;
}
interface AgentRunCandidate extends InspectCandidateDescriptor {
kind: 'agent-run';
sessionId: string;
header: AgentRunHeader;
}
interface TaskRunCandidate extends InspectCandidateDescriptor {
kind: 'task-run';
}
type InspectCandidate = SessionCandidate | AgentRunCandidate | TaskRunCandidate;
export type InspectResolution =
| { status: 'resolved'; candidate: InspectCandidate }
| { status: 'not_found' | 'ambiguous'; document: InspectResolutionDocument };
export async function resolveInspectTarget(
stores: InspectCommandStores,
query: { id: string; requestedKind?: InspectEntityKind; sessionId?: string },
): Promise<InspectResolution> {
let candidates: InspectCandidate[];
if (query.requestedKind === 'session') {
candidates = await findSessionCandidates(stores.sessionStore, query.id);
} else if (query.requestedKind === 'task-run') {
candidates = await findTaskRunCandidates(stores.taskRunStore, query.id);
} else if (query.requestedKind === 'agent-run') {
candidates = await findAgentRunCandidates(stores, query.id, query.sessionId);
} else {
const [sessions, agentRuns, taskRuns] = await Promise.all([
findSessionCandidates(stores.sessionStore, query.id),
findAgentRunCandidates(stores, query.id),
findTaskRunCandidates(stores.taskRunStore, query.id),
]);
candidates = [...sessions, ...agentRuns, ...taskRuns];
}
candidates.sort(compareCandidates);
if (candidates.length === 1) return { status: 'resolved', candidate: candidates[0]! };
const status = candidates.length === 0 ? 'not_found' : 'ambiguous';
return {
status,
document: {
schemaVersion: INSPECT_RESOLUTION_SCHEMA_VERSION,
kind: 'inspect_resolution',
query: {
id: query.id,
...(query.requestedKind ? { requestedKind: query.requestedKind } : {}),
...(query.sessionId ? { sessionId: query.sessionId } : {}),
},
status,
candidates: candidates.map(({ kind, id, sessionId }) => ({
kind,
id,
...(sessionId ? { sessionId } : {}),
})),
},
};
}
export async function inspectResolvedTarget(
stores: InspectCommandStores,
candidate: InspectCandidate,
): Promise<InspectDocument> {
if (candidate.kind === 'task-run') {
if (!stores.taskRunStore) {
throw new StorageRootAuthorityError(
'root_kind_mismatch',
'TaskRun inspection requires a headless storage root',
);
}
return inspectTaskRun(
{
taskRunStore: stores.taskRunStore,
agentRunStore: stores.agentRunStore,
runtimeEventStore: stores.runtimeEventStore,
},
candidate.id,
);
}
if (candidate.kind === 'agent-run') {
return inspectAgentRunDocument(stores.agentRunStore, stores.runtimeEventStore, {
sessionId: candidate.sessionId,
agentRunId: candidate.id,
header: candidate.header,
isFatalReadError: isStorageRootAuthorityError,
});
}
return inspectSessionDocument(
{ readHeader: (id) => stores.sessionStore.readHeader(id) },
stores.agentRunStore,
stores.runtimeEventStore,
candidate.id,
{
header: candidate.header,
isFatalReadError: isStorageRootAuthorityError,
},
);
}
export interface InspectCommandIo {
stdout: { write(value: string): unknown };
stderr: { write(value: string): unknown };
}
export async function runMakaInspectCli(
args: string[],
io: InspectCommandIo = process,
): Promise<number> {
let parsed: ParsedInspectArgs;
try {
parsed = parseInspectArgs(args);
} catch (error) {
io.stderr.write(`${errorMessage(error)}\n\n${inspectUsage()}\n`);
return 2;
}
if (parsed.help) {
io.stdout.write(`${inspectUsage()}\n`);
return 0;
}
if (!parsed.id) {
io.stderr.write(`${inspectUsage()}\n`);
return 2;
}
const storageRoot = parsed.store ? resolve(parsed.store) : resolveMakaWorkspaceRoot();
const result = await withInspectCommandStores(storageRoot, async (stores) => {
const resolution = await resolveInspectTarget(stores, {
id: parsed.id!,
...(parsed.kind ? { requestedKind: parsed.kind } : {}),
...(parsed.sessionId ? { sessionId: parsed.sessionId } : {}),
});
if (resolution.status !== 'resolved') return resolution;
return {
status: 'inspected' as const,
document: await inspectResolvedTarget(stores, resolution.candidate),
};
}).catch((error: unknown) => {
if (!isStorageRootAuthorityError(error)) throw error;
io.stderr.write(
`maka inspect: ${formatInspectAuthorityError(error, parsed.store === undefined)}\n`,
);
return undefined;
});
if (result === undefined) return 1;
if (result.status !== 'inspected') {
if (parsed.json) {
io.stdout.write(`${JSON.stringify(result.document, null, 2)}\n`);
} else {
io.stderr.write(`${renderResolutionFailure(result.document)}\n`);
}
return result.status === 'ambiguous' ? 2 : 1;
}
const document = result.document;
if (parsed.json) {
io.stdout.write(`${JSON.stringify(document, null, 2)}\n`);
} else if (document.kind === 'task_run') {
io.stdout.write(renderTaskRunInspectTree(document));
} else if (document.kind === 'agent_run') {
io.stdout.write(renderAgentRunInspectTree(document));
} else {
io.stdout.write(renderSessionInspectTree(document));
}
return 0;
}
export async function withInspectCommandStores<T>(
storageRoot: string,
operation: (stores: InspectCommandStores) => Promise<T>,
): Promise<T> {
const capability = await discoverMarkedStorageRoot({ path: storageRoot });
if (capability.kind === 'headless') {
const storage = await openHeadlessStorageForRead(capability);
try {
return await operation(
inspectStoresFromExecutionReader(storage.executionStores, storage.taskRunStore),
);
} finally {
await storage.executionStores.sessionStore.close?.();
}
}
const reader = await tryAcquireInteractiveRootReader(capability);
if (!reader) {
throw new StorageRootAuthorityError(
'lock_failed',
`Interactive storage root is exclusively locked: ${capability.canonicalPath}`,
);
}
try {
const executionStores = await openInteractiveExecutionStoresForRead(reader.lease);
try {
return await operation(inspectStoresFromExecutionReader(executionStores));
} finally {
await executionStores.sessionStore.close?.();
}
} finally {
await reader.close();
}
}
function inspectStoresFromExecutionReader(
stores: ExecutionStoresReader<'headless' | 'interactive'>,
taskRunStore?: TaskRunReader,
): InspectCommandStores {
return {
sessionStore: stores.sessionStore,
agentRunStore: stores.agentRunStore,
runtimeEventStore: stores.runtimeEventStore,
...(taskRunStore ? { taskRunStore } : {}),
};
}
interface ParsedInspectArgs {
id?: string;
store?: string;
kind?: InspectEntityKind;
sessionId?: string;
json: boolean;
help: boolean;
}
function parseInspectArgs(args: readonly string[]): ParsedInspectArgs {
const positional: string[] = [];
let store: string | undefined;
let kind: InspectEntityKind | undefined;
let sessionId: string | undefined;
let json = false;
let help = false;
for (let index = 0; index < args.length; index += 1) {
const arg = args[index]!;
if (arg === '--json') {
json = true;
} else if (arg === '--help' || arg === '-h') {
help = true;
} else if (arg === '--store' || arg === '--kind' || arg === '--session') {
const value = args[index + 1];
if (!value || value.startsWith('--')) throw new Error(`${arg} requires a value`);
index += 1;
if (arg === '--store') store = value;
else if (arg === '--session') sessionId = value;
else if (isInspectEntityKind(value)) kind = value;
else throw new Error(`invalid --kind: ${value}`);
} else if (arg.startsWith('-')) {
throw new Error(`unknown option: ${arg}`);
} else {
positional.push(arg);
}
}
if (positional.length > 1) throw new Error(`unexpected argument: ${positional[1]}`);
if (sessionId && kind !== 'agent-run') {
throw new Error('--session requires --kind agent-run');
}
return {
...(positional[0] ? { id: positional[0] } : {}),
...(store ? { store } : {}),
...(kind ? { kind } : {}),
...(sessionId ? { sessionId } : {}),
json,
help,
};
}
function isInspectEntityKind(value: string): value is InspectEntityKind {
return value === 'session' || value === 'agent-run' || value === 'task-run';
}
async function findSessionCandidates(
store: InspectSessionReader,
id: string,
): Promise<SessionCandidate[]> {
const summaries = await store.list();
if (!summaries.some((session) => session.id === id)) return [];
return [{ kind: 'session', id, header: await store.readHeader(id) }];
}
async function findTaskRunCandidates(
store: TaskRunReader | undefined,
id: string,
): Promise<TaskRunCandidate[]> {
if (!store) return [];
const records = await store.readEventRecords(id);
return records.length > 0 ? [{ kind: 'task-run', id }] : [];
}
async function findAgentRunCandidates(
stores: Pick<InspectCommandStores, 'sessionStore' | 'agentRunStore'>,
id: string,
sessionId?: string,
): Promise<AgentRunCandidate[]> {
if (sessionId) {
try {
const header = await stores.agentRunStore.readRun(sessionId, id);
return [{ kind: 'agent-run', id, sessionId, header }];
} catch (error) {
if (isNotFound(error)) return [];
throw error;
}
}
const sessions = await stores.sessionStore.list();
const runLists = await Promise.all(
sessions.map((session) => stores.agentRunStore.listSessionRuns(session.id)),
);
const candidates: AgentRunCandidate[] = [];
runLists.forEach((runs) => {
for (const header of runs) {
if (header.runId === id) {
candidates.push({ kind: 'agent-run', id, sessionId: header.sessionId, header });
}
}
});
return candidates;
}
function compareCandidates(a: InspectCandidate, b: InspectCandidate): number {
return a.kind.localeCompare(b.kind) || (a.sessionId ?? '').localeCompare(b.sessionId ?? '');
}
function renderResolutionFailure(document: InspectResolutionDocument): string {
if (document.status === 'not_found') {
return `No Session, AgentRun, or TaskRun found for ${document.query.id}.`;
}
const candidates = document.candidates.map((candidate) =>
candidate.kind === 'agent-run'
? ` - agent-run ${candidate.id} in session ${candidate.sessionId}`
: ` - ${candidate.kind} ${candidate.id}`,
);
return [
`Inspect target ${document.query.id} is ambiguous:`,
...candidates,
'Choose one with --kind session|agent-run|task-run; add --session <id> for a duplicate AgentRun id.',
].join('\n');
}
function inspectUsage(): string {
return [
'Usage: maka inspect <id> [--store <root>] [--kind session|agent-run|task-run] [--session <sessionId>] [--json]',
'',
'Inspects Session, AgentRun, or TaskRun evidence without copying raw model or tool payloads.',
'The default store is the current Maka desktop workspace.',
].join('\n');
}
function isNotFound(error: unknown): boolean {
return (error as NodeJS.ErrnoException)?.code === 'ENOENT';
}
function errorMessage(error: unknown): string {
return error instanceof Error ? error.message : String(error);
}
function formatInspectAuthorityError(
error: StorageRootAuthorityError,
usesDefaultWorkspace: boolean,
): string {
if (error.code !== 'root_unmarked') return error.message;
return usesDefaultWorkspace
? `${error.message}. Open Maka Desktop or the TUI once to initialize this workspace, then retry`
: `${error.message}. Initialize it through its owning Maka write command, or pass the correct --store root`;
}