blob: e2fff784d9d8b2e7c299e11cb7e8a463d5e3316d [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { EXECUTION_LOG_LEDGERS, type ExecutionLogCoverage } from './execution-log-coverage.js';
import { SESSION_STATUSES, type SessionHeader } from './session.js';
export const AGENT_RUN_INSPECT_DOCUMENT_VERSION = 'maka.agent_run_inspect.v1' as const;
export const SESSION_INSPECT_DOCUMENT_VERSION = 'maka.session_inspect.v1' as const;
export type ExecutionInspectSeverity = 'error' | 'warning' | 'info';
export interface ExecutionInspectDiagnostic {
severity: ExecutionInspectSeverity;
code: string;
message: string;
sessionId: string;
agentRunId?: string;
turnId?: string;
eventId?: string;
}
const AGENT_RUN_INSPECT_STATUSES = ['running', 'completed', 'failed', 'cancelled'] as const;
export interface AgentRunInspectIdentity {
sessionId: string;
agentRunId: string;
invocationId: string;
turnId: string;
parentRunId?: string;
resumedFromRunId?: string;
retriedFromRunId?: string;
parentTurnId?: string;
agentId?: string;
/** Derived from the terminal RuntimeEvent; `running` means there is none yet. */
status: (typeof AGENT_RUN_INSPECT_STATUSES)[number];
openedAt: number;
endedAt?: number;
failureClass?: string;
abortSource?: string;
}
export interface AgentRunInspectToolFact {
toolCallId: string;
toolName: string;
eventId: string;
}
export interface AgentRunInspectToolSummary {
callCount: number;
responseCount: number;
errorResponseCount: number;
callsWithoutResponse: AgentRunInspectToolFact[];
responsesWithoutCall: AgentRunInspectToolFact[];
}
export interface AgentRunInspectCompactionCheckpoint {
eventId: string;
/** `superseded`: well-formed, but minted under an older source policy. */
validation: 'shape_valid' | 'invalid' | 'superseded';
checkpointId?: string;
policyVersion?: string;
sourceCoverage?: ExecutionLogCoverage;
}
export interface AgentRunInspectSourceHealth {
runtimeLedger: 'present' | 'missing' | 'read_failed';
runtimeTerminalPresent: boolean;
}
export interface AgentRunInspectDocument {
schemaVersion: typeof AGENT_RUN_INSPECT_DOCUMENT_VERSION;
kind: 'agent_run';
agentRun: AgentRunInspectIdentity;
sources: {
operationalEventCount: number;
runtimeEventCount: number;
runtimeCoverage?: ExecutionLogCoverage;
health: AgentRunInspectSourceHealth;
};
tools: AgentRunInspectToolSummary;
compactionCheckpoints: AgentRunInspectCompactionCheckpoint[];
diagnostics: ExecutionInspectDiagnostic[];
}
export interface SessionInspectSummary {
sessionId: string;
name: string;
status: SessionHeader['status'];
createdAt: number;
lastMessageAt?: number;
isArchived: boolean;
parentSessionId?: string;
branchOfTurnId?: string;
revisionRootSessionId?: string;
revisionParentSessionId?: string;
revisionOfTurnId?: string;
revisionIndex?: number;
revisionState?: 'preparing' | 'committed';
}
export interface SessionInspectDocument {
schemaVersion: typeof SESSION_INSPECT_DOCUMENT_VERSION;
kind: 'session';
session: SessionInspectSummary;
agentRuns: AgentRunInspectDocument[];
diagnostics: ExecutionInspectDiagnostic[];
}
export function isAgentRunInspectDocument(value: unknown): value is AgentRunInspectDocument {
if (
!hasShape(
value,
[
'schemaVersion',
'kind',
'agentRun',
'sources',
'tools',
'compactionCheckpoints',
'diagnostics',
],
[],
) ||
value.schemaVersion !== AGENT_RUN_INSPECT_DOCUMENT_VERSION ||
value.kind !== 'agent_run' ||
!isAgentRunIdentity(value.agentRun) ||
!isAgentRunSources(value.sources) ||
!isToolSummary(value.tools) ||
!Array.isArray(value.compactionCheckpoints) ||
!value.compactionCheckpoints.every(isCompactionCheckpoint) ||
!Array.isArray(value.diagnostics)
) {
return false;
}
const { sessionId, agentRunId, turnId } = value.agentRun;
return value.diagnostics.every(
(item) =>
isDiagnostic(item) &&
item.sessionId === sessionId &&
item.agentRunId === agentRunId &&
item.turnId === turnId,
);
}
export function isSessionInspectDocument(value: unknown): value is SessionInspectDocument {
if (
!hasShape(value, ['schemaVersion', 'kind', 'session', 'agentRuns', 'diagnostics'], []) ||
value.schemaVersion !== SESSION_INSPECT_DOCUMENT_VERSION ||
value.kind !== 'session' ||
!isSessionSummary(value.session) ||
!Array.isArray(value.agentRuns) ||
!Array.isArray(value.diagnostics)
) {
return false;
}
const { sessionId } = value.session;
if (
!value.agentRuns.every(
(item) => isAgentRunInspectDocument(item) && item.agentRun.sessionId === sessionId,
)
) {
return false;
}
const runTurns = new Map(
value.agentRuns.map((item) => [item.agentRun.agentRunId, item.agentRun.turnId]),
);
return value.diagnostics.every(
(item) =>
isDiagnostic(item) &&
item.sessionId === sessionId &&
item.agentRunId !== undefined &&
item.turnId === runTurns.get(item.agentRunId),
);
}
function isAgentRunIdentity(value: unknown): value is AgentRunInspectIdentity {
return (
hasShape(
value,
['sessionId', 'agentRunId', 'invocationId', 'turnId', 'status', 'openedAt'],
[
'parentRunId',
'resumedFromRunId',
'retriedFromRunId',
'parentTurnId',
'agentId',
'endedAt',
'failureClass',
'abortSource',
],
) &&
isString(value.sessionId) &&
isString(value.agentRunId) &&
isString(value.invocationId) &&
isString(value.turnId) &&
AGENT_RUN_INSPECT_STATUSES.includes(
value.status as (typeof AGENT_RUN_INSPECT_STATUSES)[number],
) &&
isCount(value.openedAt) &&
isOptionalCount(value.endedAt) &&
[
value.parentRunId,
value.resumedFromRunId,
value.retriedFromRunId,
value.parentTurnId,
value.agentId,
value.failureClass,
value.abortSource,
].every(isOptionalString)
);
}
function isAgentRunSources(value: unknown): boolean {
return (
hasShape(
value,
['operationalEventCount', 'runtimeEventCount', 'health'],
['runtimeCoverage'],
) &&
isCount(value.operationalEventCount) &&
isCount(value.runtimeEventCount) &&
(value.runtimeCoverage === undefined || isCoverage(value.runtimeCoverage)) &&
hasShape(value.health, ['runtimeLedger', 'runtimeTerminalPresent'], []) &&
(value.health.runtimeLedger === 'present' ||
value.health.runtimeLedger === 'missing' ||
value.health.runtimeLedger === 'read_failed') &&
typeof value.health.runtimeTerminalPresent === 'boolean'
);
}
function isToolSummary(value: unknown): boolean {
return (
hasShape(
value,
[
'callCount',
'responseCount',
'errorResponseCount',
'callsWithoutResponse',
'responsesWithoutCall',
],
[],
) &&
isCount(value.callCount) &&
isCount(value.responseCount) &&
isCount(value.errorResponseCount) &&
Array.isArray(value.callsWithoutResponse) &&
value.callsWithoutResponse.every(isToolFact) &&
Array.isArray(value.responsesWithoutCall) &&
value.responsesWithoutCall.every(isToolFact)
);
}
function isToolFact(value: unknown): boolean {
return (
hasShape(value, ['toolCallId', 'toolName', 'eventId'], []) &&
typeof value.toolCallId === 'string' &&
typeof value.toolName === 'string' &&
typeof value.eventId === 'string'
);
}
function isCompactionCheckpoint(value: unknown): boolean {
return (
hasShape(
value,
['eventId', 'validation'],
['checkpointId', 'policyVersion', 'sourceCoverage'],
) &&
typeof value.eventId === 'string' &&
(value.validation === 'shape_valid' ||
value.validation === 'invalid' ||
value.validation === 'superseded') &&
isOptionalString(value.checkpointId) &&
isOptionalString(value.policyVersion) &&
(value.sourceCoverage === undefined || isCoverage(value.sourceCoverage))
);
}
function isDiagnostic(value: unknown): value is ExecutionInspectDiagnostic {
return (
hasShape(
value,
['severity', 'code', 'message', 'sessionId'],
['agentRunId', 'turnId', 'eventId'],
) &&
(value.severity === 'error' || value.severity === 'warning' || value.severity === 'info') &&
isString(value.code) &&
isString(value.message) &&
isString(value.sessionId) &&
isOptionalString(value.agentRunId) &&
isOptionalString(value.turnId) &&
isOptionalString(value.eventId)
);
}
function isSessionSummary(value: unknown): value is SessionInspectSummary {
return (
hasShape(
value,
['sessionId', 'name', 'status', 'createdAt', 'isArchived'],
[
'lastMessageAt',
'parentSessionId',
'branchOfTurnId',
'revisionRootSessionId',
'revisionParentSessionId',
'revisionOfTurnId',
'revisionIndex',
'revisionState',
],
) &&
isString(value.sessionId) &&
typeof value.name === 'string' &&
SESSION_STATUSES.includes(value.status as (typeof SESSION_STATUSES)[number]) &&
isCount(value.createdAt) &&
isOptionalCount(value.lastMessageAt) &&
typeof value.isArchived === 'boolean' &&
[
value.parentSessionId,
value.branchOfTurnId,
value.revisionRootSessionId,
value.revisionParentSessionId,
value.revisionOfTurnId,
].every(isOptionalString) &&
isOptionalCount(value.revisionIndex) &&
(value.revisionState === undefined ||
value.revisionState === 'preparing' ||
value.revisionState === 'committed')
);
}
function isCoverage(value: unknown): boolean {
return (
hasShape(value, ['highWater'], ['lowWater', 'eventCount']) &&
isCursor(value.highWater) &&
(value.lowWater === undefined || isCursor(value.lowWater)) &&
isOptionalCount(value.eventCount)
);
}
function isCursor(value: unknown): boolean {
return (
hasShape(value, ['ledger', 'streamId', 'sequence'], ['eventId']) &&
EXECUTION_LOG_LEDGERS.includes(value.ledger as (typeof EXECUTION_LOG_LEDGERS)[number]) &&
isString(value.streamId) &&
isCount(value.sequence) &&
isOptionalString(value.eventId)
);
}
function hasShape<Required extends string, Optional extends string>(
value: unknown,
required: readonly Required[],
optional: readonly Optional[],
): value is Record<Required, unknown> & Partial<Record<Optional, unknown>> {
if (!value || typeof value !== 'object' || Array.isArray(value)) return false;
const record = value as Record<string, unknown>;
const allowed = new Set<string>([...required, ...optional]);
return (
required.every((key) => Object.hasOwn(record, key)) &&
Object.keys(record).every((key) => allowed.has(key))
);
}
function isString(value: unknown): value is string {
return typeof value === 'string' && value.length > 0;
}
function isOptionalString(value: unknown): boolean {
return value === undefined || typeof value === 'string';
}
function isCount(value: unknown): value is number {
return Number.isSafeInteger(value) && (value as number) >= 0;
}
function isOptionalCount(value: unknown): boolean {
return value === undefined || isCount(value);
}