"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.RegistryService = void 0;
const contracts_1 = require("@teachagenthub/contracts");
const common_1 = require("@nestjs/common");
class LocalSkillCatalogProvider {
    localSkills;
    source = 'local';
    constructor(localSkills) {
        this.localSkills = localSkills;
    }
    async loadCatalog(_tenantId) {
        return {
            source: this.source,
            version: 'local-static-v1',
            cursor: 0,
            skills: this.localSkills.slice()
        };
    }
}
class CloudSkillCatalogProvider {
    baseUrl;
    timeoutMs;
    source = 'cloud';
    constructor(baseUrl, timeoutMs) {
        this.baseUrl = baseUrl;
        this.timeoutMs = timeoutMs;
    }
    async loadCatalogSnapshot(ctx) {
        const controller = new AbortController();
        const timer = setTimeout(() => controller.abort(), this.timeoutMs);
        const url = this.composeUrl('/catalog/snapshot', ctx);
        try {
            const response = await fetch(url, {
                method: 'GET',
                headers: this.createHeaders(ctx),
                signal: controller.signal
            });
            const payload = (await response.json());
            if (!response.ok) {
                throw new Error(payload.message ?? `cloud catalog http ${response.status}`);
            }
            if (!Array.isArray(payload.skills)) {
                throw new Error('cloud catalog payload invalid: skills must be array');
            }
            return {
                source: this.source,
                version: payload.version ?? `cloud-${Date.now()}`,
                cursor: payload.cursor ?? 0,
                skills: payload.skills
            };
        }
        finally {
            clearTimeout(timer);
        }
    }
    async loadCatalogDelta(ctx, sinceCursor) {
        const controller = new AbortController();
        const timer = setTimeout(() => controller.abort(), this.timeoutMs);
        const url = this.composeUrl('/catalog/delta', ctx, { sinceCursor: String(sinceCursor) });
        try {
            const response = await fetch(url, {
                method: 'GET',
                headers: this.createHeaders(ctx),
                signal: controller.signal
            });
            const payload = (await response.json());
            if (!response.ok) {
                throw new Error(payload.message ?? `cloud catalog delta http ${response.status}`);
            }
            return payload;
        }
        finally {
            clearTimeout(timer);
        }
    }
    createHeaders(ctx) {
        const headers = {
            'Content-Type': 'application/json',
            'x-tenant-id': ctx.tenantId
        };
        if (ctx.userId) {
            headers['x-user-id'] = ctx.userId;
        }
        if (ctx.userRole) {
            headers['x-user-role'] = ctx.userRole;
        }
        if (ctx.visibility) {
            headers['x-visibility'] = ctx.visibility;
        }
        return headers;
    }
    composeUrl(path, ctx, extraParams) {
        const search = new URLSearchParams();
        search.set('tenantId', ctx.tenantId);
        if (ctx.visibility) {
            search.set('visibility', ctx.visibility);
        }
        if (extraParams) {
            for (const [key, value] of Object.entries(extraParams)) {
                search.set(key, value);
            }
        }
        return `${this.baseUrl}${path}?${search.toString()}`;
    }
}
let RegistryService = class RegistryService {
    logger = new common_1.Logger('RegistryService');
    defaultPolicy = {
        timeoutMs: 2000,
        retry: { maxAttempts: 2, backoffMs: 80 }
    };
    skills = [
        {
            skillId: 'gateway.connect-ipc',
            version: '1.0.0',
            name: 'Connect IPC Session',
            module: 'gateway',
            framework: 'hermes',
            description: 'Create/restore IPC session context for routing downstream execution.',
            dependencies: [],
            executionPolicy: this.defaultPolicy,
            supportedActions: ['navigate', 'pick', 'place', 'teach', 'run_program', 'io_write'],
            supportedTargets: ['agv', 'arm', 'both'],
            entrypoint: 'agent-gateway:/api/v1/ipc/connect',
            inputSchema: { ipcIp: 'string', name: 'string?' },
            outputSchema: { connectionId: 'string', status: 'connected|disconnected' },
            tags: ['session', 'routing', 'ipc'],
            enabled: true
        },
        {
            skillId: 'model.asr.transcribe-stream',
            version: '1.0.0',
            name: 'ASR Stream Transcription',
            module: 'model',
            framework: 'openclaw',
            description: 'Transcribe audio chunks and provide incremental transcript text.',
            dependencies: ['gateway.connect-ipc'],
            executionPolicy: { timeoutMs: 2500, retry: { maxAttempts: 1, backoffMs: 0 } },
            supportedActions: ['navigate', 'pick', 'place', 'teach', 'run_program', 'io_write'],
            supportedTargets: ['agv', 'arm', 'both'],
            entrypoint: 'agent-service:/internal/v1/model/asr/transcribe',
            inputSchema: { audioBase64: 'string', chunkSeq: 'number' },
            outputSchema: { text: 'string' },
            tags: ['speech', 'asr', 'streaming'],
            enabled: true
        },
        {
            skillId: 'model.intent.parse',
            version: '1.1.0',
            name: 'LLM Intent Parser',
            module: 'model',
            framework: 'hermes',
            description: 'Map user text into structured task command.',
            dependencies: ['gateway.connect-ipc'],
            executionPolicy: { timeoutMs: 2600, retry: { maxAttempts: 2, backoffMs: 120 } },
            supportedActions: ['navigate', 'pick', 'place', 'teach', 'run_program', 'io_write'],
            supportedTargets: ['agv', 'arm', 'both'],
            entrypoint: 'agent-service:/internal/v1/model/llm/parse-intent',
            inputSchema: { text: 'string', sessionId: 'string' },
            outputSchema: { action: 'TaskAction', target: 'string', params: 'record' },
            tags: ['llm', 'nlp', 'intent'],
            enabled: true
        },
        {
            skillId: 'planner.command.plan',
            version: '1.0.0',
            name: 'Skill-Driven Planner',
            module: 'planner',
            framework: 'openclaw',
            description: 'Construct execution plan from normalized command and selected skills.',
            dependencies: ['model.intent.parse'],
            executionPolicy: this.defaultPolicy,
            supportedActions: ['navigate', 'pick', 'place', 'teach', 'run_program', 'io_write'],
            supportedTargets: ['agv', 'arm', 'both'],
            entrypoint: 'agent-service:/internal/v1/planner/plan',
            inputSchema: { command: 'TaskCommand' },
            outputSchema: { planId: 'string', commands: 'TaskCommand[]' },
            tags: ['plan', 'orchestration'],
            enabled: true
        },
        {
            skillId: 'guard.command.validate',
            version: '1.2.0',
            name: 'Command Guard Validation',
            module: 'guard',
            framework: 'hermes',
            description: 'Validate command risk and required routing fields.',
            dependencies: ['model.intent.parse'],
            executionPolicy: { timeoutMs: 1200, retry: { maxAttempts: 1, backoffMs: 0 } },
            supportedActions: ['navigate', 'pick', 'place', 'teach', 'run_program', 'io_write'],
            supportedTargets: ['agv', 'arm', 'both'],
            entrypoint: 'agent-service:guard.validateCommand',
            inputSchema: { command: 'TaskCommand' },
            outputSchema: { valid: 'boolean', reason: 'string?' },
            tags: ['safety', 'risk'],
            enabled: true
        },
        {
            skillId: 'runtime.task.create',
            version: '1.0.0',
            name: 'Runtime Task Create',
            module: 'runtime',
            framework: 'openclaw',
            description: 'Create and track runtime task lifecycle.',
            dependencies: ['guard.command.validate', 'planner.command.plan'],
            executionPolicy: this.defaultPolicy,
            supportedActions: ['navigate', 'pick', 'place', 'teach', 'run_program', 'io_write'],
            supportedTargets: ['agv', 'arm', 'both'],
            entrypoint: 'agent-service:/internal/v1/runtime/tasks',
            inputSchema: { command: 'TaskCommand' },
            outputSchema: { taskId: 'string', status: 'created|running|failed' },
            tags: ['runtime', 'task'],
            enabled: true
        },
        {
            skillId: 'memory.session.append',
            version: '1.0.0',
            name: 'Session Memory Append',
            module: 'memory',
            framework: 'hermes',
            description: 'Append runtime trace into session memory buffer.',
            dependencies: ['runtime.task.create'],
            executionPolicy: { timeoutMs: 1000, retry: { maxAttempts: 1, backoffMs: 0 } },
            supportedActions: ['navigate', 'pick', 'place', 'teach', 'run_program', 'io_write'],
            supportedTargets: ['agv', 'arm', 'both'],
            entrypoint: 'agent-service:memory.append',
            inputSchema: { sessionId: 'string', text: 'string' },
            outputSchema: { accepted: 'boolean' },
            tags: ['memory', 'context'],
            enabled: true
        },
        {
            skillId: 'policy.risk.evaluate',
            version: '1.0.0',
            name: 'Policy Risk Check',
            module: 'policy',
            framework: 'openclaw',
            description: 'Evaluate runtime risk policy before command execution.',
            dependencies: ['guard.command.validate'],
            executionPolicy: { timeoutMs: 1100, retry: { maxAttempts: 1, backoffMs: 0 } },
            supportedActions: ['navigate', 'pick', 'place', 'teach', 'run_program', 'io_write'],
            supportedTargets: ['agv', 'arm', 'both'],
            entrypoint: 'agent-service:/internal/v1/policy/check',
            inputSchema: { command: 'TaskCommand' },
            outputSchema: { allow: 'boolean', reason: 'string?' },
            tags: ['policy', 'approval'],
            enabled: true
        },
        {
            skillId: 'executor.mcp.dispatch',
            version: '1.0.0',
            name: 'MCP Dispatch',
            module: 'executor',
            framework: 'hermes',
            description: 'Dispatch normalized command to target IPC execution endpoint.',
            dependencies: ['policy.risk.evaluate', 'runtime.task.create'],
            executionPolicy: { timeoutMs: 30000, retry: { maxAttempts: 2, backoffMs: 150 } },
            supportedActions: ['navigate', 'pick', 'place', 'teach', 'run_program', 'io_write'],
            supportedTargets: ['agv', 'arm', 'both'],
            entrypoint: 'aubo-mcp:jsonrpc/tools/call',
            inputSchema: { ipcIp: 'string', targetType: 'agv|arm|both', action: 'TaskAction' },
            outputSchema: { accepted: 'boolean', message: 'string' },
            tags: ['execution', 'mcp'],
            enabled: true
        },
        {
            skillId: 'realtime.event.push',
            version: '1.0.0',
            name: 'Realtime Event Push',
            module: 'realtime',
            framework: 'openclaw',
            description: 'Push runtime and ASR events to session websocket channel.',
            dependencies: ['executor.mcp.dispatch'],
            executionPolicy: { timeoutMs: 900, retry: { maxAttempts: 1, backoffMs: 0 } },
            supportedActions: ['navigate', 'pick', 'place', 'teach', 'run_program', 'io_write'],
            supportedTargets: ['agv', 'arm', 'both'],
            entrypoint: 'agent-gateway:/ws/runtime',
            inputSchema: { eventType: 'RuntimeEventType', payload: 'record' },
            outputSchema: { delivered: 'boolean' },
            tags: ['ws', 'realtime', 'event'],
            enabled: true
        },
        {
            skillId: 'audit.event.record',
            version: '1.0.0',
            name: 'Audit Event Recorder',
            module: 'audit',
            framework: 'hermes',
            description: 'Record execution and risk decision for audit trail.',
            dependencies: ['executor.mcp.dispatch'],
            executionPolicy: { timeoutMs: 900, retry: { maxAttempts: 1, backoffMs: 0 } },
            supportedActions: ['navigate', 'pick', 'place', 'teach', 'run_program', 'io_write'],
            supportedTargets: ['agv', 'arm', 'both'],
            entrypoint: 'agent-gateway:audit.append',
            inputSchema: { category: 'access|execution|risk|model', action: 'string' },
            outputSchema: { eventId: 'string' },
            tags: ['audit', 'compliance'],
            enabled: true
        },
        {
            skillId: 'gateway.session-recover',
            version: '1.0.0',
            name: 'Session Recover',
            module: 'gateway',
            framework: 'hermes',
            description: 'Recover session context and routing keys for resumed conversations.',
            dependencies: ['gateway.connect-ipc'],
            executionPolicy: { timeoutMs: 1200, retry: { maxAttempts: 1, backoffMs: 0 } },
            supportedActions: ['navigate', 'pick', 'place', 'teach', 'run_program', 'io_write'],
            supportedTargets: ['agv', 'arm', 'both'],
            entrypoint: 'agent-gateway:/api/v1/sessions/:sessionId',
            inputSchema: { sessionId: 'string' },
            outputSchema: { restored: 'boolean', sessionId: 'string' },
            tags: ['gateway', 'session', 'resume'],
            enabled: true
        },
        {
            skillId: 'realtime.subscription-manage',
            version: '1.0.0',
            name: 'Realtime Subscription Manage',
            module: 'realtime',
            framework: 'openclaw',
            description: 'Manage per-session websocket subscriptions and channel scopes.',
            dependencies: ['gateway.session-recover'],
            executionPolicy: { timeoutMs: 900, retry: { maxAttempts: 1, backoffMs: 0 } },
            supportedActions: ['navigate', 'pick', 'place', 'teach', 'run_program', 'io_write'],
            supportedTargets: ['agv', 'arm', 'both'],
            entrypoint: 'agent-gateway:/ws/runtime#subscribe',
            inputSchema: { sessionId: 'string', channels: 'string[]' },
            outputSchema: { accepted: 'boolean' },
            tags: ['realtime', 'ws', 'subscription'],
            enabled: false
        },
        {
            skillId: 'model.provider-select',
            version: '1.0.0',
            name: 'Model Provider Select',
            module: 'model',
            framework: 'hermes',
            description: 'Choose model provider by framework, latency budget and risk level.',
            dependencies: ['gateway.session-recover'],
            executionPolicy: { timeoutMs: 1400, retry: { maxAttempts: 2, backoffMs: 100 } },
            supportedActions: ['navigate', 'pick', 'place', 'teach', 'run_program', 'io_write'],
            supportedTargets: ['agv', 'arm', 'both'],
            entrypoint: 'agent-service:/internal/v1/model/router/invoke',
            inputSchema: { text: 'string', riskLevel: 'low|medium|high' },
            outputSchema: { provider: 'string', model: 'string' },
            tags: ['model', 'router', 'provider'],
            enabled: true
        },
        {
            skillId: 'model.llm-schema-validate',
            version: '1.0.0',
            name: 'LLM Schema Validate',
            module: 'model',
            framework: 'native',
            description: 'Validate parsed LLM output against TaskCommand schema.',
            dependencies: ['model.provider-select', 'model.intent.parse'],
            executionPolicy: { timeoutMs: 1000, retry: { maxAttempts: 1, backoffMs: 0 } },
            supportedActions: ['navigate', 'pick', 'place', 'teach', 'run_program', 'io_write'],
            supportedTargets: ['agv', 'arm', 'both'],
            entrypoint: 'agent-service:model.schemaValidate',
            inputSchema: { parsed: 'record' },
            outputSchema: { valid: 'boolean', reason: 'string?' },
            tags: ['model', 'schema', 'validation'],
            enabled: true
        },
        {
            skillId: 'planner.skill-bind',
            version: '1.0.0',
            name: 'Planner Skill Bind',
            module: 'planner',
            framework: 'openclaw',
            description: 'Bind normalized command to candidate skill chain and ordering.',
            dependencies: ['model.llm-schema-validate'],
            executionPolicy: { timeoutMs: 1800, retry: { maxAttempts: 2, backoffMs: 120 } },
            supportedActions: ['navigate', 'pick', 'place', 'teach', 'run_program', 'io_write'],
            supportedTargets: ['agv', 'arm', 'both'],
            entrypoint: 'agent-service:planner.bindSkills',
            inputSchema: { command: 'TaskCommand' },
            outputSchema: { skillIds: 'string[]' },
            tags: ['planner', 'binding', 'dag'],
            enabled: true
        },
        {
            skillId: 'guard.param-range-check',
            version: '1.0.0',
            name: 'Guard Param Range Check',
            module: 'guard',
            framework: 'hermes',
            description: 'Validate numeric and enum parameter ranges before execution.',
            dependencies: ['guard.command.validate'],
            executionPolicy: { timeoutMs: 900, retry: { maxAttempts: 1, backoffMs: 0 } },
            supportedActions: ['navigate', 'pick', 'place', 'teach', 'run_program', 'io_write'],
            supportedTargets: ['agv', 'arm', 'both'],
            entrypoint: 'agent-service:guard.rangeCheck',
            inputSchema: { params: 'record' },
            outputSchema: { valid: 'boolean', violations: 'string[]' },
            tags: ['guard', 'safety', 'range'],
            enabled: true
        },
        {
            skillId: 'policy.human-confirm-gate',
            version: '1.0.0',
            name: 'Policy Human Confirm Gate',
            module: 'policy',
            framework: 'openclaw',
            description: 'Require explicit confirmation for high-risk actions.',
            dependencies: ['policy.risk.evaluate', 'guard.param-range-check'],
            executionPolicy: { timeoutMs: 1500, retry: { maxAttempts: 1, backoffMs: 0 } },
            supportedActions: ['navigate', 'pick', 'place', 'teach', 'run_program', 'io_write'],
            supportedTargets: ['agv', 'arm', 'both'],
            entrypoint: 'agent-service:/internal/v1/policy/confirm',
            inputSchema: { taskId: 'string', riskLevel: 'low|medium|high' },
            outputSchema: { allow: 'boolean', source: 'auto|human' },
            tags: ['policy', 'approval', 'risk'],
            enabled: true
        },
        {
            skillId: 'runtime.dag-schedule',
            version: '1.0.0',
            name: 'Runtime DAG Schedule',
            module: 'runtime',
            framework: 'openclaw',
            description: 'Execute resolved skill DAG by topological levels in parallel.',
            dependencies: ['planner.skill-bind', 'policy.human-confirm-gate'],
            executionPolicy: { timeoutMs: 2200, retry: { maxAttempts: 2, backoffMs: 100 } },
            supportedActions: ['navigate', 'pick', 'place', 'teach', 'run_program', 'io_write'],
            supportedTargets: ['agv', 'arm', 'both'],
            entrypoint: 'agent-service:runtime.scheduleDag',
            inputSchema: { dag: 'SkillDagNode[]' },
            outputSchema: { scheduled: 'boolean', levels: 'number' },
            tags: ['runtime', 'dag', 'scheduler'],
            enabled: true
        },
        {
            skillId: 'runtime.retry-backoff-control',
            version: '1.0.0',
            name: 'Runtime Retry Backoff Control',
            module: 'runtime',
            framework: 'native',
            description: 'Apply retry and backoff policy per skill node execution.',
            dependencies: ['runtime.dag-schedule'],
            executionPolicy: { timeoutMs: 1100, retry: { maxAttempts: 1, backoffMs: 0 } },
            supportedActions: ['navigate', 'pick', 'place', 'teach', 'run_program', 'io_write'],
            supportedTargets: ['agv', 'arm', 'both'],
            entrypoint: 'agent-service:runtime.retryControl',
            inputSchema: { skillId: 'string', attempts: 'number' },
            outputSchema: { shouldRetry: 'boolean', waitMs: 'number' },
            tags: ['runtime', 'retry', 'backoff'],
            enabled: false
        },
        {
            skillId: 'executor.mcp-capability-query',
            version: '1.0.0',
            name: 'MCP Capability Query',
            module: 'executor',
            framework: 'hermes',
            description: 'Query target IPC capabilities before command dispatch.',
            dependencies: ['runtime.dag-schedule'],
            executionPolicy: { timeoutMs: 1400, retry: { maxAttempts: 2, backoffMs: 120 } },
            supportedActions: ['navigate', 'pick', 'place', 'teach', 'run_program', 'io_write'],
            supportedTargets: ['agv', 'arm', 'both'],
            entrypoint: 'aubo-mcp:jsonrpc/tools/list',
            inputSchema: { ipcIp: 'string', targetType: 'agv|arm|both' },
            outputSchema: { capabilities: 'string[]' },
            tags: ['executor', 'mcp', 'capability'],
            enabled: false
        },
        {
            skillId: 'memory.session-snapshot',
            version: '1.0.0',
            name: 'Memory Session Snapshot',
            module: 'memory',
            framework: 'native',
            description: 'Persist runtime checkpoint for rollback and replay.',
            dependencies: ['runtime.dag-schedule'],
            executionPolicy: { timeoutMs: 1000, retry: { maxAttempts: 1, backoffMs: 0 } },
            supportedActions: ['navigate', 'pick', 'place', 'teach', 'run_program', 'io_write'],
            supportedTargets: ['agv', 'arm', 'both'],
            entrypoint: 'agent-service:memory.snapshot',
            inputSchema: { sessionId: 'string', taskId: 'string' },
            outputSchema: { snapshotId: 'string' },
            tags: ['memory', 'snapshot', 'recovery'],
            enabled: false
        },
        {
            skillId: 'memory.world-state-aggregate',
            version: '1.0.0',
            name: 'World State Aggregate',
            module: 'memory',
            framework: 'openclaw',
            description: 'Aggregate latest device and business state as planning context.',
            dependencies: ['executor.mcp-capability-query'],
            executionPolicy: { timeoutMs: 1600, retry: { maxAttempts: 2, backoffMs: 100 } },
            supportedActions: ['navigate', 'pick', 'place', 'teach', 'run_program', 'io_write'],
            supportedTargets: ['agv', 'arm', 'both'],
            entrypoint: 'agent-service:world.aggregate',
            inputSchema: { sessionId: 'string', ipcIp: 'string' },
            outputSchema: { worldState: 'record' },
            tags: ['world', 'state', 'context'],
            enabled: false
        },
        {
            skillId: 'realtime.timeline-push',
            version: '1.0.0',
            name: 'Realtime Timeline Push',
            module: 'realtime',
            framework: 'openclaw',
            description: 'Push per-node lifecycle updates for gantt timeline rendering.',
            dependencies: ['runtime.dag-schedule', 'executor.mcp.dispatch'],
            executionPolicy: { timeoutMs: 900, retry: { maxAttempts: 1, backoffMs: 0 } },
            supportedActions: ['navigate', 'pick', 'place', 'teach', 'run_program', 'io_write'],
            supportedTargets: ['agv', 'arm', 'both'],
            entrypoint: 'agent-gateway:/api/v1/realtime/events/publish',
            inputSchema: { taskId: 'string', eventType: 'string', payload: 'record' },
            outputSchema: { delivered: 'boolean' },
            tags: ['realtime', 'timeline', 'trace'],
            enabled: true
        },
        {
            skillId: 'audit.trace-record',
            version: '1.0.0',
            name: 'Audit Trace Record',
            module: 'audit',
            framework: 'hermes',
            description: 'Persist skill execution trace bundle for compliance and replay.',
            dependencies: ['executor.mcp.dispatch'],
            executionPolicy: { timeoutMs: 1000, retry: { maxAttempts: 1, backoffMs: 0 } },
            supportedActions: ['navigate', 'pick', 'place', 'teach', 'run_program', 'io_write'],
            supportedTargets: ['agv', 'arm', 'both'],
            entrypoint: 'agent-gateway:audit.appendTrace',
            inputSchema: { taskId: 'string', traces: 'SkillExecutionTrace[]' },
            outputSchema: { eventId: 'string' },
            tags: ['audit', 'trace', 'compliance'],
            enabled: false
        }
    ];
    localProvider = new LocalSkillCatalogProvider(this.skills);
    cloudProvider = new CloudSkillCatalogProvider(this.getCloudBaseUrl(), this.getCloudTimeoutMs());
    cacheByTenant = new Map();
    async listSkills(filters) {
        const tenantId = this.resolveTenantId(filters?.tenantId);
        const catalog = await this.loadCatalogByMode({
            tenantId,
            userId: filters?.userId,
            userRole: filters?.userRole,
            visibility: filters?.visibility
        });
        return catalog.skills.filter((skill) => {
            if (!skill.enabled) {
                return false;
            }
            if (filters?.framework && skill.framework !== filters.framework) {
                return false;
            }
            if (filters?.module && skill.module !== filters.module) {
                return false;
            }
            const resolvedVisibility = skill.visibility ?? 'tenant';
            if (filters?.visibility && resolvedVisibility !== filters.visibility) {
                return false;
            }
            if (!this.canAccessSkill(skill, tenantId, filters?.userId, filters?.userRole ?? 'viewer')) {
                return false;
            }
            return true;
        });
    }
    async getSkill(skillId, tenantId, userId, userRole = 'viewer') {
        const resolvedTenantId = this.resolveTenantId(tenantId);
        const catalog = await this.loadCatalogByMode({ tenantId: resolvedTenantId, userId, userRole });
        return catalog.skills.find((skill) => skill.skillId === skillId && skill.enabled && this.canAccessSkill(skill, resolvedTenantId, userId, userRole));
    }
    async resolveSkills(req) {
        const preferredFramework = req.framework ?? this.getDefaultFramework();
        const tenantId = this.resolveTenantId(req.tenantId);
        const userRole = req.userRole ?? 'viewer';
        const catalog = await this.loadCatalogByMode({
            tenantId,
            userId: req.userId,
            userRole,
            visibility: req.visibility
        });
        const candidates = catalog.skills.filter((skill) => {
            if (!skill.enabled) {
                return false;
            }
            if (skill.framework !== preferredFramework && skill.framework !== 'native') {
                return false;
            }
            if (!skill.supportedActions.includes(req.command.action)) {
                return false;
            }
            if (!skill.supportedTargets.includes(req.command.targetType)) {
                return false;
            }
            if (req.visibility && (skill.visibility ?? 'tenant') !== req.visibility) {
                return false;
            }
            return this.canAccessSkill(skill, tenantId, req.userId, userRole);
        });
        const dag = this.buildDag(candidates);
        return {
            framework: preferredFramework,
            tenantId,
            catalogSource: catalog.source,
            command: {
                sessionId: req.command.sessionId,
                action: req.command.action,
                target: req.command.target,
                targetType: req.command.targetType
            },
            skills: candidates,
            dag
        };
    }
    async loadCatalogByMode(ctx) {
        const tenantId = ctx.tenantId;
        const mode = this.getCatalogMode();
        if (mode === 'local') {
            const local = await this.localProvider.loadCatalog(tenantId);
            return this.normalizeCatalog(local, tenantId);
        }
        if (mode === 'cloud') {
            const cloud = await this.loadCloudThenCache(ctx);
            if (cloud) {
                return cloud;
            }
            const cached = this.loadValidCachedCatalog(tenantId);
            if (cached) {
                return cached;
            }
            throw new Error(`cloud skill catalog unavailable for tenant=${tenantId}`);
        }
        const cloud = await this.loadCloudThenCache(ctx);
        if (cloud) {
            return cloud;
        }
        const cached = this.loadValidCachedCatalog(tenantId);
        if (cached) {
            return cached;
        }
        const local = await this.localProvider.loadCatalog(tenantId);
        return this.normalizeCatalog(local, tenantId);
    }
    async loadCloudThenCache(ctx) {
        const tenantId = ctx.tenantId;
        try {
            const cached = this.cacheByTenant.get(tenantId);
            if (!cached) {
                const snapshot = await this.cloudProvider.loadCatalogSnapshot(ctx);
                const normalizedSnapshot = this.normalizeCatalog(snapshot, tenantId);
                this.cacheByTenant.set(tenantId, {
                    skills: normalizedSnapshot.skills,
                    version: normalizedSnapshot.version,
                    cursor: normalizedSnapshot.cursor,
                    fetchedAt: Date.now(),
                    refreshedAt: Date.now()
                });
                return normalizedSnapshot;
            }
            const refreshIntervalMs = this.getRefreshIntervalMs();
            if (Date.now() - cached.refreshedAt < refreshIntervalMs) {
                return this.normalizeCatalog({
                    source: 'cache',
                    version: cached.version,
                    cursor: cached.cursor,
                    skills: cached.skills
                }, tenantId);
            }
            const delta = await this.cloudProvider.loadCatalogDelta(ctx, cached.cursor);
            if (delta.reset) {
                const snapshot = await this.cloudProvider.loadCatalogSnapshot(ctx);
                const normalizedSnapshot = this.normalizeCatalog(snapshot, tenantId);
                this.cacheByTenant.set(tenantId, {
                    skills: normalizedSnapshot.skills,
                    version: normalizedSnapshot.version,
                    cursor: normalizedSnapshot.cursor,
                    fetchedAt: Date.now(),
                    refreshedAt: Date.now()
                });
                return normalizedSnapshot;
            }
            const baseById = new Map(cached.skills.map((item) => [item.skillId, item]));
            for (const removedSkillId of delta.removals ?? []) {
                baseById.delete(removedSkillId);
            }
            for (const upsert of delta.upserts ?? []) {
                baseById.set(upsert.skillId, upsert);
            }
            const next = {
                source: 'cloud',
                version: delta.version ?? cached.version,
                cursor: delta.cursor ?? cached.cursor,
                skills: Array.from(baseById.values())
            };
            const normalized = this.normalizeCatalog(next, tenantId);
            this.cacheByTenant.set(tenantId, {
                skills: normalized.skills,
                version: normalized.version,
                cursor: normalized.cursor,
                fetchedAt: cached.fetchedAt,
                refreshedAt: Date.now()
            });
            return normalized;
        }
        catch (error) {
            const reason = error instanceof Error ? error.message : 'unknown error';
            this.logger.warn(`cloud catalog load failed tenant=${tenantId}: ${reason}`);
            return undefined;
        }
    }
    loadValidCachedCatalog(tenantId) {
        const cached = this.cacheByTenant.get(tenantId);
        if (!cached) {
            return undefined;
        }
        const ttlMs = this.getCacheTtlMs();
        if (Date.now() - cached.fetchedAt > ttlMs) {
            this.cacheByTenant.delete(tenantId);
            return undefined;
        }
        return this.normalizeCatalog({
            source: 'cache',
            version: cached.version,
            cursor: cached.cursor,
            skills: cached.skills
        }, tenantId);
    }
    normalizeCatalog(result, tenantId) {
        const normalizedSkills = result.skills.map((skill) => ({
            ...skill,
            source: skill.source ?? result.source,
            visibility: skill.visibility ?? 'tenant',
            tenantId: skill.tenantId ?? tenantId
        }));
        return {
            source: result.source,
            version: result.version,
            cursor: result.cursor,
            skills: normalizedSkills
        };
    }
    canAccessSkill(skill, tenantId, userId, userRole) {
        const visibility = skill.visibility ?? 'tenant';
        const ownerTenantId = skill.tenantId ?? tenantId;
        if (visibility === 'public') {
            return true;
        }
        if (visibility === 'tenant') {
            return ownerTenantId === tenantId;
        }
        if (userRole === 'admin') {
            return true;
        }
        if (!userId) {
            return false;
        }
        return ownerTenantId === tenantId && skill.ownerId === userId;
    }
    resolveTenantId(tenantId) {
        return tenantId?.trim() || contracts_1.APP_CONFIG.AGENT_DEFAULT_TENANT_ID || 'default';
    }
    getCatalogMode() {
        const mode = (contracts_1.APP_CONFIG.SKILL_CATALOG_MODE ?? 'local').toLowerCase();
        if (mode === 'hybrid' || mode === 'cloud') {
            return mode;
        }
        return 'local';
    }
    getCloudBaseUrl() {
        return (contracts_1.APP_CONFIG.SKILL_REPO_BASE_URL ?? 'http://localhost:3010/internal/v1/skills').replace(/\/$/, '');
    }
    getCloudTimeoutMs() {
        const parsed = Number.parseInt(contracts_1.APP_CONFIG.SKILL_REPO_TIMEOUT_MS ?? '2500', 10);
        if (Number.isNaN(parsed) || parsed <= 0) {
            return 2500;
        }
        return parsed;
    }
    getCacheTtlMs() {
        const parsed = Number.parseInt(contracts_1.APP_CONFIG.SKILL_CATALOG_CACHE_TTL_MS ?? '60000', 10);
        if (Number.isNaN(parsed) || parsed <= 0) {
            return 60000;
        }
        return parsed;
    }
    getRefreshIntervalMs() {
        const parsed = Number.parseInt(contracts_1.APP_CONFIG.SKILL_CATALOG_REFRESH_INTERVAL_MS ?? '5000', 10);
        if (Number.isNaN(parsed) || parsed <= 0) {
            return 5000;
        }
        return parsed;
    }
    buildDag(skills) {
        const byId = new Map(skills.map((skill) => [skill.skillId, skill]));
        const indegree = new Map();
        const edges = new Map();
        for (const skill of skills) {
            indegree.set(skill.skillId, 0);
            edges.set(skill.skillId, []);
        }
        for (const skill of skills) {
            const validDeps = skill.dependencies.filter((dep) => byId.has(dep));
            for (const dep of validDeps) {
                const outgoing = edges.get(dep) ?? [];
                outgoing.push(skill.skillId);
                edges.set(dep, outgoing);
                indegree.set(skill.skillId, (indegree.get(skill.skillId) ?? 0) + 1);
            }
        }
        const queue = skills
            .map((skill) => skill.skillId)
            .filter((id) => (indegree.get(id) ?? 0) === 0)
            .sort();
        const ordered = [];
        while (queue.length > 0) {
            const current = queue.shift();
            const node = byId.get(current);
            if (!node) {
                continue;
            }
            ordered.push(node);
            for (const next of edges.get(current) ?? []) {
                indegree.set(next, (indegree.get(next) ?? 0) - 1);
                if ((indegree.get(next) ?? 0) === 0) {
                    queue.push(next);
                    queue.sort();
                }
            }
        }
        if (ordered.length !== skills.length) {
            return skills
                .slice()
                .sort((a, b) => a.skillId.localeCompare(b.skillId))
                .map((skill) => ({
                skillId: skill.skillId,
                module: skill.module,
                version: skill.version,
                dependencies: skill.dependencies.filter((dep) => byId.has(dep)),
                timeoutMs: skill.executionPolicy.timeoutMs,
                maxAttempts: skill.executionPolicy.retry.maxAttempts
            }));
        }
        return ordered.map((skill) => ({
            skillId: skill.skillId,
            module: skill.module,
            version: skill.version,
            dependencies: skill.dependencies.filter((dep) => byId.has(dep)),
            timeoutMs: skill.executionPolicy.timeoutMs,
            maxAttempts: skill.executionPolicy.retry.maxAttempts
        }));
    }
    getDefaultFramework() {
        const raw = contracts_1.APP_CONFIG.AGENT_SKILL_FRAMEWORK;
        if (raw === 'openclaw' || raw === 'hermes' || raw === 'native') {
            return raw;
        }
        return 'hermes';
    }
};
exports.RegistryService = RegistryService;
exports.RegistryService = RegistryService = __decorate([
    (0, common_1.Injectable)()
], RegistryService);
//# sourceMappingURL=registry.service.js.map