{"version":3,"file":"task-graph.mjs","names":[],"sources":["../../../../../../src/extensions/(backend)/channel/_lib/task-graph.ts"],"sourcesContent":["import type { DoomHubChannelSource, DoomHubSessionScope, DoomHubChannel } from '@agimon-ai/doompi-core/hub-channel';\n\nimport type { TaskDocument } from '../../../../models/task';\nimport { resolveStorePath } from '../../../../services/paths';\nimport { TaskStore } from '../../../../services/taskStore';\nimport { TASKS_CHANNEL_TYPE, type WebTask, type WebTasksPayload } from '../../../../types/webTasks';\n\ninterface TaskStoreSource {\n  readonly snapshot: TaskDocument;\n  read(): TaskDocument;\n  dispose(): void;\n}\n\nfunction isTaskDocument(value: unknown): value is TaskDocument {\n  return (\n    typeof value === 'object' &&\n    value !== null &&\n    typeof (value as TaskDocument).rev === 'number' &&\n    Array.isArray((value as TaskDocument).tasks)\n  );\n}\n\nexport interface TasksChannelOptions {\n  /** Injectable for tests. The default opens the session tree's durable task store. */\n  storeFor?: (scope: DoomHubSessionScope) => TaskStoreSource;\n}\n\nfunction present(document: TaskDocument): WebTasksPayload {\n  const tasks: WebTask[] = document.tasks.flatMap((task) => {\n    if (task.status === 'deleted') return [];\n    const view: WebTask = {\n      id: task.id,\n      subject: task.subject,\n      status: task.status,\n      blockedBy: task.blockedBy ?? [],\n    };\n    if (task.description) view.description = task.description;\n    if (task.activeForm) view.activeForm = task.activeForm;\n    if (task.owner) view.owner = task.owner;\n    if (task.updatedAt) view.updatedAt = task.updatedAt;\n    if (task.delegation) {\n      view.delegation = {\n        ...(task.delegation.agent === undefined ? {} : { agent: task.delegation.agent }),\n        ...(task.delegation.state === undefined ? {} : { state: task.delegation.state }),\n      };\n    }\n    return [view];\n  });\n  return { tasks, rev: document.rev };\n}\n\n/** Publishes each managed session's durable task graph and follows direct session commits. */\nexport function createTasksChannel(options: TasksChannelOptions = {}): DoomHubChannel {\n  return {\n    frameType: TASKS_CHANNEL_TYPE,\n    start(host) {\n      const stores = new Map<string, { store: TaskStoreSource; unsubscribe: () => void; latest?: TaskDocument }>();\n      const open = (scope: DoomHubSessionScope): void => {\n        if (stores.has(scope.sessionId)) return;\n        let store = options.storeFor?.(scope);\n        if (store === undefined) {\n          if (scope.environment === undefined)\n            throw new Error(`Task channel requires an admitted environment for session '${scope.sessionId}'.`);\n          store = new TaskStore({\n            cwd: scope.cwd,\n            env: scope.environment,\n            storePath: resolveStorePath(scope.cwd, scope.environment, scope.sessionId),\n          });\n        }\n        store.read();\n        const source = { store, unsubscribe: () => undefined as void };\n        stores.set(scope.sessionId, source);\n        source.unsubscribe = host.directEvents.subscribe(\n          TASKS_CHANNEL_TYPE,\n          scope.sessionId,\n          (payload) => {\n            if (!isTaskDocument(payload)) return;\n            const current = stores.get(scope.sessionId);\n            if (current === undefined) return;\n            current.latest = payload;\n            host.publish(scope.sessionId, present(payload));\n          },\n          { replayLatest: true },\n        );\n      };\n      const close = (sessionId: string): void => {\n        const source = stores.get(sessionId);\n        if (!source) return;\n        source.unsubscribe();\n        source.store.dispose();\n        stores.delete(sessionId);\n      };\n      for (const scope of host.sessions()) open(scope);\n\n      const source: DoomHubChannelSource = {\n        payloadFor(scope) {\n          open(scope);\n          const source = stores.get(scope.sessionId);\n          const document = source?.latest ?? source?.store.snapshot;\n          return document === undefined ? undefined : present(document);\n        },\n        sessionAdded: open,\n        sessionRemoved: close,\n        close() {\n          for (const sessionId of stores.keys()) close(sessionId);\n        },\n      };\n      return source;\n    },\n  };\n}\n\nexport default createTasksChannel;\n"],"mappings":";;;;AAaA,SAAS,eAAe,OAAuC;CAC7D,OACE,OAAO,UAAU,YACjB,UAAU,QACV,OAAQ,MAAuB,QAAQ,YACvC,MAAM,QAAS,MAAuB,KAAK;AAE/C;AAOA,SAAS,QAAQ,UAAyC;CAqBxD,OAAO;EAAE,OApBgB,SAAS,MAAM,SAAS,SAAS;GACxD,IAAI,KAAK,WAAW,WAAW,OAAO,CAAC;GACvC,MAAM,OAAgB;IACpB,IAAI,KAAK;IACT,SAAS,KAAK;IACd,QAAQ,KAAK;IACb,WAAW,KAAK,aAAa,CAAC;GAChC;GACA,IAAI,KAAK,aAAa,KAAK,cAAc,KAAK;GAC9C,IAAI,KAAK,YAAY,KAAK,aAAa,KAAK;GAC5C,IAAI,KAAK,OAAO,KAAK,QAAQ,KAAK;GAClC,IAAI,KAAK,WAAW,KAAK,YAAY,KAAK;GAC1C,IAAI,KAAK,YACP,KAAK,aAAa;IAChB,GAAI,KAAK,WAAW,UAAU,KAAA,IAAY,CAAC,IAAI,EAAE,OAAO,KAAK,WAAW,MAAM;IAC9E,GAAI,KAAK,WAAW,UAAU,KAAA,IAAY,CAAC,IAAI,EAAE,OAAO,KAAK,WAAW,MAAM;GAChF;GAEF,OAAO,CAAC,IAAI;EACd,CACa;EAAG,KAAK,SAAS;CAAI;AACpC;;AAGA,SAAgB,mBAAmB,UAA+B,CAAC,GAAmB;CACpF,OAAO;EACL,WAAW;EACX,MAAM,MAAM;GACV,MAAM,yBAAS,IAAI,IAAwF;GAC3G,MAAM,QAAQ,UAAqC;IACjD,IAAI,OAAO,IAAI,MAAM,SAAS,GAAG;IACjC,IAAI,QAAQ,QAAQ,WAAW,KAAK;IACpC,IAAI,UAAU,KAAA,GAAW;KACvB,IAAI,MAAM,gBAAgB,KAAA,GACxB,MAAM,IAAI,MAAM,8DAA8D,MAAM,UAAU,GAAG;KACnG,QAAQ,IAAI,UAAU;MACpB,KAAK,MAAM;MACX,KAAK,MAAM;MACX,WAAW,iBAAiB,MAAM,KAAK,MAAM,aAAa,MAAM,SAAS;KAC3E,CAAC;IACH;IACA,MAAM,KAAK;IACX,MAAM,SAAS;KAAE;KAAO,mBAAmB,KAAA;IAAkB;IAC7D,OAAO,IAAI,MAAM,WAAW,MAAM;IAClC,OAAO,cAAc,KAAK,aAAa,UACrC,oBACA,MAAM,YACL,YAAY;KACX,IAAI,CAAC,eAAe,OAAO,GAAG;KAC9B,MAAM,UAAU,OAAO,IAAI,MAAM,SAAS;KAC1C,IAAI,YAAY,KAAA,GAAW;KAC3B,QAAQ,SAAS;KACjB,KAAK,QAAQ,MAAM,WAAW,QAAQ,OAAO,CAAC;IAChD,GACA,EAAE,cAAc,KAAK,CACvB;GACF;GACA,MAAM,SAAS,cAA4B;IACzC,MAAM,SAAS,OAAO,IAAI,SAAS;IACnC,IAAI,CAAC,QAAQ;IACb,OAAO,YAAY;IACnB,OAAO,MAAM,QAAQ;IACrB,OAAO,OAAO,SAAS;GACzB;GACA,KAAK,MAAM,SAAS,KAAK,SAAS,GAAG,KAAK,KAAK;GAe/C,OAAO;IAZL,WAAW,OAAO;KAChB,KAAK,KAAK;KACV,MAAM,SAAS,OAAO,IAAI,MAAM,SAAS;KACzC,MAAM,WAAW,QAAQ,UAAU,QAAQ,MAAM;KACjD,OAAO,aAAa,KAAA,IAAY,KAAA,IAAY,QAAQ,QAAQ;IAC9D;IACA,cAAc;IACd,gBAAgB;IAChB,QAAQ;KACN,KAAK,MAAM,aAAa,OAAO,KAAK,GAAG,MAAM,SAAS;IACxD;GAEU;EACd;CACF;AACF"}