"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.toolResult = toolResult;
exports.toolSuccess = toolSuccess;
exports.toolFailure = toolFailure;
exports.toolPartialFailure = toolPartialFailure;
exports.reminderEchoBackEnabled = reminderEchoBackEnabled;
exports.reminderIsVirtual = reminderIsVirtual;
exports.reminderIsListed = reminderIsListed;
exports.generateReminderId = generateReminderId;
exports.materializeReminder = materializeReminder;
exports.cloneReminder = cloneReminder;
exports.computeReminderRenderRevision = computeReminderRenderRevision;
exports.compareReminderDisplayOrder = compareReminderDisplayOrder;
exports.getReminderOwnerName = getReminderOwnerName;
exports.reminderOwnedBy = reminderOwnedBy;
exports.validateArgs = validateArgs;
exports.formatToolArgumentsForContext = formatToolArgumentsForContext;
exports.prepareRawToolArguments = prepareRawToolArguments;
exports.resolveFuncToolInvocationArguments = resolveFuncToolInvocationArguments;
const id_1 = require("@longrun-ai/kernel/utils/id");
const time_1 = require("@longrun-ai/kernel/utils/time");
const crypto_1 = require("crypto");
function toolResult(content, outcome, contentItems) {
    return {
        content,
        outcome,
        contentItems,
    };
}
function toolSuccess(content, contentItems) {
    return toolResult(content, 'success', contentItems);
}
function toolFailure(content, contentItems) {
    return toolResult(content, 'failure', contentItems);
}
function toolPartialFailure(content, contentItems) {
    return toolResult(content, 'partial_failure', contentItems);
}
const FUNC_TOOL_ARG_ALIASES = {
    readonly_shell: {
        timeout: 'timeout_ms',
    },
};
function reminderEchoBackEnabled(reminder) {
    return reminder.echoback !== false;
}
function reminderIsVirtual(reminder) {
    return !reminderEchoBackEnabled(reminder);
}
function reminderIsListed(reminder) {
    return reminderEchoBackEnabled(reminder);
}
function generateReminderId() {
    let id = '';
    while (id.length < 8) {
        id += (0, id_1.generateShortId)();
    }
    return id.slice(0, 8);
}
function ensureReminderId(value) {
    const trimmed = value?.trim();
    return trimmed && trimmed.length > 0 ? trimmed : generateReminderId();
}
function materializeReminder(input) {
    return {
        id: ensureReminderId(input.id),
        content: input.content,
        owner: input.owner,
        meta: input.meta,
        echoback: input.echoback,
        scope: input.scope ?? 'dialog',
        createdAt: input.createdAt,
        priority: input.priority,
        renderMode: input.renderMode ?? 'markdown',
    };
}
function cloneReminder(reminder) {
    return materializeReminder({
        id: reminder.id,
        content: reminder.content,
        owner: reminder.owner,
        meta: reminder.meta,
        echoback: reminder.echoback,
        scope: reminder.scope,
        createdAt: reminder.createdAt,
        priority: reminder.priority,
        renderMode: reminder.renderMode,
    });
}
function computeReminderRenderRevision(reminder) {
    const payload = JSON.stringify({
        id: reminder.id,
        content: reminder.content,
        meta: reminder.meta ?? null,
        echoback: reminder.echoback ?? true,
        scope: reminder.scope ?? 'dialog',
        renderMode: reminder.renderMode ?? 'markdown',
    });
    return `sha256:${(0, crypto_1.createHash)('sha256').update(payload, 'utf8').digest('hex')}`;
}
function extractReminderDisplayTimestamp(reminder) {
    let displayTs = reminder.createdAt ?? '';
    let displayTsMs = parseReminderSortTimestamp(displayTs);
    const meta = reminder.meta;
    if (typeof meta !== 'object' || meta === null || Array.isArray(meta)) {
        return { raw: displayTs, ms: displayTsMs };
    }
    const updatedAt = meta['updatedAt'];
    if (typeof updatedAt === 'string') {
        const updatedAtMs = parseReminderSortTimestamp(updatedAt);
        if (updatedAtMs !== null && (displayTsMs === null || updatedAtMs > displayTsMs)) {
            displayTs = updatedAt;
            displayTsMs = updatedAtMs;
        }
    }
    const lastUpdated = meta['lastUpdated'];
    if (typeof lastUpdated === 'string') {
        const lastUpdatedMs = parseReminderSortTimestamp(lastUpdated);
        if (lastUpdatedMs !== null && (displayTsMs === null || lastUpdatedMs > displayTsMs)) {
            displayTs = lastUpdated;
            displayTsMs = lastUpdatedMs;
        }
    }
    return { raw: displayTs, ms: displayTsMs };
}
function parseReminderSortTimestamp(value) {
    return (0, time_1.parseUnifiedTimestampMs)(value);
}
// Reminder presentation order is a framework-level concern distinct from owner semantics.
// Keep it centralized so dialog-local reminders and runtime reminders stay in the same
// newest-first order everywhere they are merged, rendered, or injected.
function compareReminderDisplayOrder(a, b) {
    const aDisplayTs = extractReminderDisplayTimestamp(a);
    const bDisplayTs = extractReminderDisplayTimestamp(b);
    if (aDisplayTs.ms !== null || bDisplayTs.ms !== null) {
        if (aDisplayTs.ms === null)
            return 1;
        if (bDisplayTs.ms === null)
            return -1;
        if (aDisplayTs.ms !== bDisplayTs.ms) {
            return bDisplayTs.ms - aDisplayTs.ms;
        }
    }
    if (aDisplayTs.raw !== bDisplayTs.raw) {
        return bDisplayTs.raw.localeCompare(aDisplayTs.raw);
    }
    return b.id.localeCompare(a.id);
}
function getReminderOwnerName(reminder) {
    return reminder.owner?.name;
}
function reminderOwnedBy(reminder, owner) {
    const ownerName = typeof owner === 'string' ? owner : owner.name;
    return getReminderOwnerName(reminder) === ownerName;
}
function validateArgs(schema, args) {
    if (!isRecord(args)) {
        return { ok: false, error: 'Arguments must be an object' };
    }
    if (Array.isArray(args)) {
        return { ok: false, error: 'Arguments must be an object' };
    }
    const schemaType = isRecord(schema) && 'type' in schema ? schema.type : undefined;
    if (schemaType !== undefined && schemaType !== 'object') {
        return { ok: false, error: 'Schema root must be an object' };
    }
    const propertiesValue = isRecord(schema) && 'properties' in schema ? schema.properties : undefined;
    const properties = isRecord(propertiesValue) ? propertiesValue : {};
    const requiredValue = isRecord(schema) && 'required' in schema ? schema.required : undefined;
    const required = Array.isArray(requiredValue) && requiredValue.every((v) => typeof v === 'string')
        ? new Set(requiredValue)
        : new Set();
    const additionalPropertiesValue = isRecord(schema) && 'additionalProperties' in schema ? schema.additionalProperties : undefined;
    const allowAdditional = additionalPropertiesValue === false
        ? false
        : additionalPropertiesValue === true || additionalPropertiesValue === undefined
            ? true
            : true; // schema additionalProperties is allowed (not deeply validated)
    // required fields
    for (const key of required) {
        if (!(key in args))
            return { ok: false, error: `Missing required field: ${key}` };
    }
    // validate each provided field
    for (const [key, value] of Object.entries(args)) {
        const propSchemaUnknown = properties[key];
        if (propSchemaUnknown === undefined) {
            if (!allowAdditional) {
                return { ok: false, error: `Unexpected field: ${key}` };
            }
            continue;
        }
        const res = validateValue(propSchemaUnknown, value, key);
        if (!res.ok)
            return res;
    }
    return { ok: true };
}
function isPlainObject(value) {
    return typeof value === 'object' && value !== null && !Array.isArray(value);
}
function normalizeFuncToolArguments(toolName, args) {
    const aliases = FUNC_TOOL_ARG_ALIASES[toolName];
    if (!aliases)
        return args;
    let normalized;
    for (const [aliasKey, canonicalKey] of Object.entries(aliases)) {
        if (!(aliasKey in args))
            continue;
        if (!normalized)
            normalized = { ...args };
        if (!(canonicalKey in normalized)) {
            normalized[canonicalKey] = normalized[aliasKey];
        }
        delete normalized[aliasKey];
    }
    return (normalized ?? args);
}
function formatToolArgumentsForContext(arguments_) {
    return JSON.stringify(arguments_ ?? {});
}
function prepareRawToolArguments(argumentsStr) {
    if (argumentsStr.trim() === '') {
        return {
            ok: true,
            raw: {},
            contextArguments: '{}',
            persistedArguments: {},
        };
    }
    let parsed;
    try {
        parsed = JSON.parse(argumentsStr);
    }
    catch (err) {
        return {
            ok: false,
            error: `Arguments must be valid JSON: ${err instanceof Error ? err.message : String(err)}`,
            contextArguments: '{}',
            persistedArguments: {},
        };
    }
    if (!isPlainObject(parsed)) {
        return {
            ok: false,
            error: 'Arguments must be an object',
            contextArguments: '{}',
            persistedArguments: {},
        };
    }
    return {
        ok: true,
        raw: parsed,
        contextArguments: argumentsStr,
        persistedArguments: parsed,
    };
}
function resolveFuncToolInvocationArguments(tool, argumentsStr) {
    const prepared = prepareRawToolArguments(argumentsStr);
    if (!prepared.ok) {
        return prepared;
    }
    const normalizedArgs = normalizeFuncToolArguments(tool.name, prepared.raw);
    if (tool.argsValidation === 'passthrough') {
        return {
            ok: true,
            args: normalizedArgs,
            contextArguments: formatToolArgumentsForContext(normalizedArgs),
            persistedArguments: normalizedArgs,
        };
    }
    const validation = validateArgs(tool.parameters, normalizedArgs);
    if (!validation.ok) {
        return {
            ok: false,
            error: validation.error,
            contextArguments: formatToolArgumentsForContext(normalizedArgs),
            persistedArguments: normalizedArgs,
        };
    }
    return {
        ok: true,
        args: normalizedArgs,
        contextArguments: formatToolArgumentsForContext(normalizedArgs),
        persistedArguments: normalizedArgs,
    };
}
function isJsonPrimitiveValue(value) {
    return (typeof value === 'string' ||
        typeof value === 'number' ||
        typeof value === 'boolean' ||
        value === null);
}
function formatJsonPrimitiveValue(value) {
    return JSON.stringify(value);
}
function describeShortValue(value) {
    if (isJsonPrimitiveValue(value))
        return formatJsonPrimitiveValue(value);
    if (Array.isArray(value))
        return '[array]';
    if (typeof value === 'object' && value !== null)
        return '[object]';
    return JSON.stringify(value);
}
function formatEnumAllowedList(values, maxShown) {
    const shown = values.slice(0, Math.max(0, Math.floor(maxShown)));
    const shownText = shown.map((v) => formatJsonPrimitiveValue(v)).join(', ');
    const remaining = values.length - shown.length;
    if (remaining <= 0)
        return shownText;
    return shownText.length > 0
        ? `${shownText}, ... (+${remaining} more)`
        : `... (+${remaining} more)`;
}
function validateValue(schema, value, path) {
    if (!isRecord(schema)) {
        // Unknown schema shape: don't block execution.
        return { ok: true };
    }
    // Best-effort const validation (only supports primitive const).
    // For complex/object const, keep permissive behavior.
    if ('const' in schema && isJsonPrimitiveValue(schema.const)) {
        const expected = schema.const;
        if (!isJsonPrimitiveValue(value) || value !== expected) {
            return {
                ok: false,
                error: `Field ${path} must be ${formatJsonPrimitiveValue(expected)}; got ${describeShortValue(value)}`,
            };
        }
    }
    // Best-effort enum validation (only supports primitive enums).
    // For complex/object enums, keep permissive behavior.
    if ('enum' in schema && Array.isArray(schema.enum)) {
        const enumValues = schema.enum;
        const allPrimitive = enumValues.every((v) => isJsonPrimitiveValue(v));
        if (allPrimitive) {
            const allowedValues = enumValues;
            const allowed = isJsonPrimitiveValue(value) && allowedValues.some((v) => v === value);
            if (!allowed) {
                const allowedText = formatEnumAllowedList(allowedValues, 10);
                return {
                    ok: false,
                    error: `Field ${path} must be one of ${allowedText}; got ${describeShortValue(value)}`,
                };
            }
        }
    }
    const typeValue = 'type' in schema ? schema.type : undefined;
    const schemaType = typeof typeValue === 'string'
        ? typeValue
        : Array.isArray(typeValue) && typeValue.every((t) => typeof t === 'string')
            ? typeValue
            : undefined;
    if (schemaType === undefined) {
        // Unknown/omitted type: permissive.
        return { ok: true };
    }
    const acceptsType = (t) => {
        switch (t) {
            case 'string':
                return typeof value === 'string';
            case 'number':
                return typeof value === 'number';
            case 'integer':
                return typeof value === 'number' && Number.isInteger(value);
            case 'boolean':
                return typeof value === 'boolean';
            case 'null':
                return value === null;
            case 'array': {
                if (!Array.isArray(value))
                    return false;
                if (!('items' in schema))
                    return true;
                const itemsSchema = schema.items;
                if (itemsSchema === undefined)
                    return true;
                if (Array.isArray(itemsSchema)) {
                    // Tuple validation: validate corresponding indices when schema exists.
                    for (let i = 0; i < Math.min(itemsSchema.length, value.length); i++) {
                        const r = validateValue(itemsSchema[i], value[i], `${path}[${i}]`);
                        if (!r.ok)
                            return false;
                    }
                    return true;
                }
                for (let i = 0; i < value.length; i++) {
                    const r = validateValue(itemsSchema, value[i], `${path}[${i}]`);
                    if (!r.ok)
                        return false;
                }
                return true;
            }
            case 'object': {
                if (!isRecord(value) || Array.isArray(value))
                    return false;
                const propsValue = 'properties' in schema ? schema.properties : undefined;
                const props = isRecord(propsValue) ? propsValue : {};
                const requiredValue = 'required' in schema ? schema.required : undefined;
                const required = Array.isArray(requiredValue) && requiredValue.every((v) => typeof v === 'string')
                    ? new Set(requiredValue)
                    : new Set();
                const additionalPropertiesValue = 'additionalProperties' in schema ? schema.additionalProperties : undefined;
                const allowAdditional = additionalPropertiesValue === false
                    ? false
                    : additionalPropertiesValue === true || additionalPropertiesValue === undefined
                        ? true
                        : true;
                for (const key of required) {
                    if (!(key in value)) {
                        return false;
                    }
                }
                for (const [k, v] of Object.entries(value)) {
                    const subSchema = props[k];
                    if (subSchema === undefined) {
                        if (!allowAdditional)
                            return false;
                        continue;
                    }
                    const r = validateValue(subSchema, v, `${path}.${k}`);
                    if (!r.ok)
                        return false;
                }
                return true;
            }
            default:
                return true;
        }
    };
    if (Array.isArray(schemaType)) {
        for (const t of schemaType) {
            if (acceptsType(t)) {
                return { ok: true };
            }
        }
        return { ok: false, error: `Field ${path} does not match expected type` };
    }
    if (!acceptsType(schemaType)) {
        return { ok: false, error: `Field ${path} does not match expected type` };
    }
    return { ok: true };
}
function isRecord(value) {
    return typeof value === 'object' && value !== null;
}
