UNPKG

@hotmeshio/hotmesh

Version:

Permanent-Memory Workflows & AI Agents

165 lines (164 loc) 6.76 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Hook = void 0; const errors_1 = require("../../modules/errors"); const collator_1 = require("../collator"); const pipe_1 = require("../pipe"); const task_1 = require("../task"); const telemetry_1 = require("../telemetry"); const stream_1 = require("../../types/stream"); const activity_1 = require("./activity"); /** * Supports `signal hook`, `time hook`, and `cycle hook` patterns */ class Hook extends activity_1.Activity { constructor(config, data, metadata, hook, engine, context) { super(config, data, metadata, hook, engine, context); } //******** INITIAL ENTRY POINT (A) ********// async process() { this.logger.debug('hook-process', { jid: this.context.metadata.jid, gid: this.context.metadata.gid, aid: this.metadata.aid, }); let telemetry; try { await this.verifyEntry(); telemetry = new telemetry_1.TelemetryService(this.engine.appId, this.config, this.metadata, this.context); telemetry.startActivitySpan(this.leg); if (this.doesHook()) { //sleep and wait to awaken upon a signal await this.doHook(telemetry); } else { //end the activity and transition to its children await this.doPassThrough(telemetry); } return this.context.metadata.aid; } catch (error) { if (error instanceof errors_1.InactiveJobError) { this.logger.error('hook-inactive-job-error', { error }); return; } else if (error instanceof errors_1.GenerationalError) { this.logger.info('process-event-generational-job-error', { error }); return; } else if (error instanceof errors_1.GetStateError) { this.logger.error('hook-get-state-error', { error }); return; } else if (error instanceof errors_1.CollationError) { if (error.fault === 'duplicate') { this.logger.info('hook-collation-overage', { job_id: this.context.metadata.jid, guid: this.context.metadata.guid, }); return; } //unknown collation error this.logger.error('hook-collation-error', { error }); } else { this.logger.error('hook-process-error', { error }); } telemetry?.setActivityError(error.message); throw error; } finally { telemetry?.endActivitySpan(); this.logger.debug('hook-process-end', { jid: this.context.metadata.jid, gid: this.context.metadata.gid, aid: this.metadata.aid, }); } } /** * does this activity use a time-hook or web-hook */ doesHook() { if (this.config.sleep) { const duration = pipe_1.Pipe.resolve(this.config.sleep, this.context); return !isNaN(duration) && Number(duration) > 0; } return !!this.config.hook?.topic; } async doHook(telemetry) { const transaction = this.store.transact(); await this.registerHook(transaction); this.mapOutputData(); this.mapJobData(); await this.setState(transaction); await collator_1.CollatorService.authorizeReentry(this, transaction); await this.setStatus(0, transaction); await transaction.exec(); telemetry.mapActivityAttributes(); } async doPassThrough(telemetry) { const transaction = this.store.transact(); let multiResponse; this.adjacencyList = await this.filterAdjacent(); this.mapOutputData(); this.mapJobData(); await this.setState(transaction); await collator_1.CollatorService.notarizeEarlyCompletion(this, transaction); await this.setStatus(this.adjacencyList.length - 1, transaction); multiResponse = (await transaction.exec()); telemetry.mapActivityAttributes(); const jobStatus = this.resolveStatus(multiResponse); const attrs = { 'app.job.jss': jobStatus }; const messageIds = await this.transition(this.adjacencyList, jobStatus); if (messageIds.length) { attrs['app.activity.mids'] = messageIds.join(','); } telemetry.setActivityAttributes(attrs); } async getHookRule(topic) { const rules = await this.store.getHookRules(); return rules?.[topic]?.[0]; } async registerHook(transaction) { if (this.config.hook?.topic) { return await this.engine.taskService.registerWebHook(this.config.hook.topic, this.context, this.resolveDad(), this.context.metadata.expire, transaction); } else if (this.config.sleep) { const duration = pipe_1.Pipe.resolve(this.config.sleep, this.context); await this.engine.taskService.registerTimeHook(this.context.metadata.jid, this.context.metadata.gid, `${this.metadata.aid}${this.metadata.dad || ''}`, 'sleep', duration, this.metadata.dad || ''); return this.context.metadata.jid; } } //******** SIGNAL RE-ENTRY POINT ********// async processWebHookEvent(status = stream_1.StreamStatus.SUCCESS, code = 200) { this.logger.debug('hook-process-web-hook-event', { topic: this.config.hook.topic, aid: this.metadata.aid, status, code, }); const taskService = new task_1.TaskService(this.store, this.logger); const data = { ...this.data }; const signal = await taskService.processWebHookSignal(this.config.hook.topic, data); if (signal) { const [jobId, _aid, dad, gId] = signal; this.context.metadata.jid = jobId; this.context.metadata.gid = gId; this.context.metadata.dad = dad; await this.processEvent(status, code, 'hook'); if (code === 200) { await taskService.deleteWebHookSignal(this.config.hook.topic, data); } //else => 202/keep alive } //else => already resolved } async processTimeHookEvent(jobId) { this.logger.debug('hook-process-time-hook-event', { jid: jobId, gid: this.context.metadata.gid, aid: this.metadata.aid, }); await this.processEvent(stream_1.StreamStatus.SUCCESS, 200, 'hook'); } } exports.Hook = Hook;