Files
makelore/shared/coding-conversation-subagent-protocol.ts

83 lines
3.2 KiB
TypeScript

import type { PublicUsage, SubagentDetailsV1 } from './coding-conversation-contracts';
const STATUSES = new Set(['queued', 'running', 'complete', 'error', 'aborted', 'skipped']);
function recordValue(value: unknown): Record<string, unknown> | null {
return value !== null && typeof value === 'object' && !Array.isArray(value)
? value as Record<string, unknown>
: null;
}
function publicUsage(value: unknown): PublicUsage | undefined {
const usage = recordValue(value);
if (!usage
|| typeof usage.inputTokens !== 'number'
|| typeof usage.outputTokens !== 'number'
|| !Number.isFinite(usage.inputTokens)
|| !Number.isFinite(usage.outputTokens)
|| usage.inputTokens < 0
|| usage.outputTokens < 0
|| (usage.cacheReadTokens !== undefined
&& (typeof usage.cacheReadTokens !== 'number'
|| !Number.isFinite(usage.cacheReadTokens)
|| usage.cacheReadTokens < 0))
|| (usage.cacheWriteTokens !== undefined
&& (typeof usage.cacheWriteTokens !== 'number'
|| !Number.isFinite(usage.cacheWriteTokens)
|| usage.cacheWriteTokens < 0))) {
return undefined;
}
return {
inputTokens: usage.inputTokens,
outputTokens: usage.outputTokens,
...(typeof usage.cacheReadTokens === 'number' ? { cacheReadTokens: usage.cacheReadTokens } : {}),
...(typeof usage.cacheWriteTokens === 'number' ? { cacheWriteTokens: usage.cacheWriteTokens } : {}),
};
}
export function projectSubagentDetailsV1(value: unknown): SubagentDetailsV1 | undefined {
const record = recordValue(value);
if (!record
|| record.schema !== 'subagent.v1'
|| typeof record.dispatchId !== 'string'
|| !record.dispatchId.trim()
|| !['single', 'parallel', 'chain'].includes(String(record.mode))
|| !Array.isArray(record.tasks)
|| record.tasks.length === 0
|| record.tasks.length > 8) return undefined;
const tasks: SubagentDetailsV1['tasks'] = [];
for (const value of record.tasks) {
const task = recordValue(value);
if (!task
|| typeof task.taskId !== 'string'
|| !task.taskId.trim()
|| typeof task.agentId !== 'string'
|| !task.agentId.trim()
|| (task.toolProfile !== 'read-only' && task.toolProfile !== 'coding')
|| !STATUSES.has(String(task.status))
|| (task.summary !== undefined && typeof task.summary !== 'string')
|| (task.errorCode !== undefined && typeof task.errorCode !== 'string')) return undefined;
const usage = task.usage === undefined ? undefined : publicUsage(task.usage);
if (task.usage !== undefined && !usage) return undefined;
tasks.push({
taskId: task.taskId,
agentId: task.agentId,
toolProfile: task.toolProfile,
status: task.status as SubagentDetailsV1['tasks'][number]['status'],
...(typeof task.summary === 'string' ? { summary: task.summary.slice(0, 4_000) } : {}),
...(typeof task.errorCode === 'string' ? { errorCode: task.errorCode.slice(0, 64) } : {}),
...(usage ? { usage } : {}),
});
}
return {
schema: 'subagent.v1',
dispatchId: record.dispatchId,
mode: record.mode as SubagentDetailsV1['mode'],
tasks,
};
}
export function subagentDetailsOfResult(value: unknown): SubagentDetailsV1 | undefined {
return projectSubagentDetailsV1(recordValue(value)?.details);
}