@axiom-lattice/gateway 6.1.0 → 7.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. package/README.md +0 -1
  2. package/dist/a2a-standard-DP53K7RC.mjs +3416 -0
  3. package/dist/a2a-standard-DP53K7RC.mjs.map +1 -0
  4. package/dist/{chunk-YKTXW6F6.mjs → chunk-2SR4F3ZD.mjs} +65 -1
  5. package/dist/chunk-2SR4F3ZD.mjs.map +1 -0
  6. package/dist/{chunk-QUT2DS4R.mjs → chunk-J6B2VFJ7.mjs} +17 -4
  7. package/dist/chunk-J6B2VFJ7.mjs.map +1 -0
  8. package/dist/embed/embed-page-AgentDetailSideApp-KNVDO3FZ.js +1 -0
  9. package/dist/embed/embed-page-AgentGovernanceWorkbench-QSKWVADP.js +1 -0
  10. package/dist/embed/embed-page-MessageGraph-VGXX4OWU.js +6 -0
  11. package/dist/embed/embed-page-ReactChart-PIISVBLU.js +1 -0
  12. package/dist/embed/{embed-page-ReactInfographic-5P36JLSX.js → embed-page-ReactInfographic-677NVKOU.js} +1 -1
  13. package/dist/embed/embed-page-SkillFlow-JLI5MEZE.js +1 -0
  14. package/dist/embed/{embed-page-TasksSideApp-BQMOLOUF.js → embed-page-TasksSideApp-D7FFG2OC.js} +1 -1
  15. package/dist/embed/embed-page-ThreadMaintenancePanel-FSQVVIIG.js +1 -0
  16. package/dist/embed/{embed-page-TopologyRuntimeView-OSV75P6Y.js → embed-page-TopologyRuntimeView-IS6HEO4F.js} +1 -1
  17. package/dist/embed/{embed-page-WorkflowAutomationView-KJ6HJ5UT.js → embed-page-WorkflowAutomationView-IDVSQTJB.js} +1 -1
  18. package/dist/embed/embed-page-all-JEPF6WHD.js +1 -0
  19. package/dist/embed/{embed-page-architectureDiagram-VXUJARFQ-THUN2PFL.js → embed-page-architectureDiagram-VXUJARFQ-TYJ3NO2K.js} +1 -1
  20. package/dist/embed/embed-page-aspnet-GIV4ESY6.js +1 -0
  21. package/dist/embed/{embed-page-blockDiagram-VD42YOAC-K7OX6LIO.js → embed-page-blockDiagram-VD42YOAC-L4J3TZWR.js} +1 -1
  22. package/dist/embed/{embed-page-c4Diagram-YG6GDRKO-DJDAF5AO.js → embed-page-c4Diagram-YG6GDRKO-CP2IXU3Z.js} +1 -1
  23. package/dist/embed/{embed-page-chunk-FD2PRLMW.js → embed-page-chunk-2VJ7T35I.js} +1 -1
  24. package/dist/embed/embed-page-chunk-37LGEEJU.js +21 -0
  25. package/dist/embed/embed-page-chunk-436REIWY.js +1 -0
  26. package/dist/embed/embed-page-chunk-4W34QOXT.js +517 -0
  27. package/dist/embed/{embed-page-chunk-JUZPOR5Y.js → embed-page-chunk-5WWOKLDB.js} +1 -1
  28. package/dist/embed/{embed-page-chunk-HYPA2PHA.js → embed-page-chunk-6IIMW7OG.js} +1 -1
  29. package/dist/embed/{embed-page-chunk-XKGF5D6L.js → embed-page-chunk-7CJDHJEH.js} +1 -1
  30. package/dist/embed/{embed-page-chunk-J5AHS4PO.js → embed-page-chunk-7GLQRV3J.js} +1 -1
  31. package/dist/embed/{embed-page-chunk-SQ4GANYE.js → embed-page-chunk-AEDAOTEO.js} +1 -1
  32. package/dist/embed/embed-page-chunk-BANVRQT7.js +322 -0
  33. package/dist/embed/{embed-page-chunk-E6F5YZBR.js → embed-page-chunk-BOEX26H7.js} +2 -2
  34. package/dist/embed/{embed-page-chunk-A2AHDN6K.js → embed-page-chunk-DQJVWZOB.js} +1 -1
  35. package/dist/embed/embed-page-chunk-EYYKNTX7.js +8 -0
  36. package/dist/embed/{embed-page-chunk-DTRFJLVZ.js → embed-page-chunk-F5QVZLK5.js} +1 -1
  37. package/dist/embed/{embed-page-chunk-IIEDLN7W.js → embed-page-chunk-FEMHLB6I.js} +1 -1
  38. package/dist/embed/{embed-page-chunk-DBUC6J4G.js → embed-page-chunk-JFMBLJXY.js} +1 -1
  39. package/dist/embed/{embed-page-chunk-G2RO6IBN.js → embed-page-chunk-KXVXKWED.js} +1 -1
  40. package/dist/embed/embed-page-chunk-N66H4DNH.js +6657 -0
  41. package/dist/embed/{embed-page-chunk-WTOAQDQN.js → embed-page-chunk-NFMLKSPM.js} +1 -1
  42. package/dist/embed/{embed-page-chunk-PLNJRORR.js → embed-page-chunk-NVFNRWRN.js} +1 -1
  43. package/dist/embed/{embed-page-chunk-V2GBFWLG.js → embed-page-chunk-S2KVN3RB.js} +1 -1
  44. package/dist/embed/{embed-page-chunk-QULNM5O2.js → embed-page-chunk-SRMYHWL3.js} +1 -1
  45. package/dist/embed/{embed-page-chunk-D5GP7FHH.js → embed-page-chunk-TVVAPR7I.js} +1 -1
  46. package/dist/embed/{embed-page-chunk-WFUFTRWA.js → embed-page-chunk-WK77HIEN.js} +1 -1
  47. package/dist/embed/{embed-page-chunk-XSKNS3LY.js → embed-page-chunk-WWXRO4ZL.js} +1 -1
  48. package/dist/embed/{embed-page-chunk-FGKHXQAJ.js → embed-page-chunk-XFV3N2LD.js} +1 -1
  49. package/dist/embed/{embed-page-chunk-VY6MAPHR.js → embed-page-chunk-XSKDYUNQ.js} +1 -1
  50. package/dist/embed/{embed-page-chunk-V7R2Y72J.js → embed-page-chunk-YJUCDIYK.js} +1 -1
  51. package/dist/embed/embed-page-chunk-ZQQFWEWO.js +1 -0
  52. package/dist/embed/embed-page-classDiagram-2ON5EDUG-MHNSVPAN.js +1 -0
  53. package/dist/embed/embed-page-classDiagram-v2-WZHVMYZB-QCMEVMYH.js +1 -0
  54. package/dist/embed/{embed-page-code-highlighter-IPU5ZNPJ.js → embed-page-code-highlighter-66CSPTFK.js} +1 -1
  55. package/dist/embed/embed-page-cshtml-RYMZMLKB.js +1 -0
  56. package/dist/embed/{embed-page-dagre-6UL2VRFP-UQFZMKYW.js → embed-page-dagre-6UL2VRFP-LTZ2DVVO.js} +1 -1
  57. package/dist/embed/{embed-page-diagram-PSM6KHXK-4IKXWNA4.js → embed-page-diagram-PSM6KHXK-JHYXSMCB.js} +1 -1
  58. package/dist/embed/{embed-page-diagram-QEK2KX5R-245T32UF.js → embed-page-diagram-QEK2KX5R-2YGDFFTU.js} +1 -1
  59. package/dist/embed/{embed-page-diagram-S2PKOQOG-BOA5MJA5.js → embed-page-diagram-S2PKOQOG-6EW6MDJW.js} +1 -1
  60. package/dist/embed/{embed-page-en-WB2EPS63.js → embed-page-en-2SWQHPAC.js} +1 -1
  61. package/dist/embed/embed-page-en-3Z4N5KXB.js +1 -0
  62. package/dist/embed/embed-page-en-7IHJFS63.js +1 -0
  63. package/dist/embed/embed-page-en-7NF4GCC2.js +1 -0
  64. package/dist/embed/embed-page-en-BLF5THT6.js +1 -0
  65. package/dist/embed/embed-page-en-ETUCVCSC.js +1 -0
  66. package/dist/embed/{embed-page-en-UWBIHR6I.js → embed-page-en-H4P63IAK.js} +1 -1
  67. package/dist/embed/embed-page-en-WUFULCUS.js +1 -0
  68. package/dist/embed/embed-page-en-XUMHP5CL.js +1 -0
  69. package/dist/embed/{embed-page-erDiagram-Q2GNP2WA-S4J4SQPR.js → embed-page-erDiagram-Q2GNP2WA-KSUS6MHW.js} +1 -1
  70. package/dist/embed/{embed-page-flowDiagram-NV44I4VS-ZFIJXKMW.js → embed-page-flowDiagram-NV44I4VS-AYXRDKAJ.js} +1 -1
  71. package/dist/embed/{embed-page-ganttDiagram-JELNMOA3-7MSIEWL4.js → embed-page-ganttDiagram-JELNMOA3-AV7N2N3T.js} +1 -1
  72. package/dist/embed/{embed-page-gitGraphDiagram-NY62KEGX-F6KE6PZK.js → embed-page-gitGraphDiagram-NY62KEGX-IBVOXVO5.js} +1 -1
  73. package/dist/embed/embed-page-infoDiagram-WHAUD3N6-ZULKOSOZ.js +2 -0
  74. package/dist/embed/{embed-page-journeyDiagram-XKPGCS4Q-6VTW6GQ3.js → embed-page-journeyDiagram-XKPGCS4Q-3GILI7Y4.js} +1 -1
  75. package/dist/embed/{embed-page-kanban-definition-3W4ZIXB7-7IZYCLQZ.js → embed-page-kanban-definition-3W4ZIXB7-UGO3J7IH.js} +1 -1
  76. package/dist/embed/{embed-page-mermaid-R7ZBUE3D.js → embed-page-mermaid-KEBBKHQH.js} +2 -2
  77. package/dist/embed/{embed-page-mindmap-definition-VGOIOE7T-GKTOT34A.js → embed-page-mindmap-definition-VGOIOE7T-VXXZJS3Z.js} +1 -1
  78. package/dist/embed/{embed-page-pieDiagram-ADFJNKIX-VDPALO42.js → embed-page-pieDiagram-ADFJNKIX-ZFAWYA2N.js} +1 -1
  79. package/dist/embed/{embed-page-requirementDiagram-UZGBJVZJ-LTZH6RTD.js → embed-page-requirementDiagram-UZGBJVZJ-2QBINXR7.js} +1 -1
  80. package/dist/embed/{embed-page-sequenceDiagram-WL72ISMW-LV6Q4SHW.js → embed-page-sequenceDiagram-WL72ISMW-3MGTESG4.js} +1 -1
  81. package/dist/embed/{embed-page-stateDiagram-FKZM4ZOC-U4NHQDNP.js → embed-page-stateDiagram-FKZM4ZOC-W6VPIMB5.js} +1 -1
  82. package/dist/embed/embed-page-stateDiagram-v2-4FDKWEC3-QYJ47QBY.js +1 -0
  83. package/dist/embed/{embed-page-tsx-PNW7N5T4.js → embed-page-tsx-LGDDERM2.js} +1 -1
  84. package/dist/embed/{embed-page-xychartDiagram-PRI3JC2R-VZSIXBFK.js → embed-page-xychartDiagram-PRI3JC2R-7XKT4R7V.js} +1 -1
  85. package/dist/embed/embed-page-zh-D3IC6BGA.js +1 -0
  86. package/dist/embed/embed-page-zh-G75LDQU4.js +1 -0
  87. package/dist/embed/embed-page-zh-GXE63PQ7.js +1 -0
  88. package/dist/embed/{embed-page-zh-ACZNJ4X4.js → embed-page-zh-IB3422D4.js} +1 -1
  89. package/dist/embed/embed-page-zh-KJXKOUIY.js +1 -0
  90. package/dist/embed/{embed-page-zh-NKHP3CFU.js → embed-page-zh-N2STRBUG.js} +1 -1
  91. package/dist/embed/{embed-page-zh-XEYNKCTC.js → embed-page-zh-PRMID3YQ.js} +1 -1
  92. package/dist/embed/embed-page-zh-RMGMUSDH.js +1 -0
  93. package/dist/embed/embed-page-zh-RMSS6PXD.js +1 -0
  94. package/dist/embed/embed-page.js +1 -1
  95. package/dist/index.d.mts +1470 -244
  96. package/dist/index.d.ts +1470 -244
  97. package/dist/index.js +16913 -5910
  98. package/dist/index.js.map +1 -1
  99. package/dist/index.mjs +33677 -24682
  100. package/dist/index.mjs.map +1 -1
  101. package/dist/{open-MDC4VCR7.mjs → open-NTH7AGD3.mjs} +41 -78
  102. package/dist/open-NTH7AGD3.mjs.map +1 -0
  103. package/dist/{open-exposure-OX5NKWDG.mjs → open-exposure-AGNG4MC3.mjs} +13 -3
  104. package/dist/{open-exposure-OX5NKWDG.mjs.map → open-exposure-AGNG4MC3.mjs.map} +1 -1
  105. package/dist/{open-files-FS5AN6Q6.mjs → open-files-V3R2BFTV.mjs} +4 -4
  106. package/package.json +9 -8
  107. package/dist/a2a-standard-3OZFXDCW.mjs +0 -1413
  108. package/dist/a2a-standard-3OZFXDCW.mjs.map +0 -1
  109. package/dist/chunk-K32XEQLH.mjs +0 -28
  110. package/dist/chunk-K32XEQLH.mjs.map +0 -1
  111. package/dist/chunk-QUT2DS4R.mjs.map +0 -1
  112. package/dist/chunk-YKTXW6F6.mjs.map +0 -1
  113. package/dist/embed/embed-page-AgentDetailSideApp-2UFH4RJG.js +0 -1
  114. package/dist/embed/embed-page-AgentGovernanceWorkbench-3JOSRW2V.js +0 -1
  115. package/dist/embed/embed-page-MessageGraph-SPB75EHC.js +0 -6
  116. package/dist/embed/embed-page-ReactChart-EE4A5WKS.js +0 -1
  117. package/dist/embed/embed-page-SkillFlow-H2ARZGEX.js +0 -1
  118. package/dist/embed/embed-page-TeamWorkspace-QXMRVXWU.js +0 -748
  119. package/dist/embed/embed-page-TeamWorkspace-YQFWXDLI.css +0 -1
  120. package/dist/embed/embed-page-all-NEJVJ4G7.js +0 -1
  121. package/dist/embed/embed-page-aspnet-SRK67AH2.js +0 -1
  122. package/dist/embed/embed-page-chunk-ESQ5V77Q.js +0 -1
  123. package/dist/embed/embed-page-chunk-GSCVATOA.js +0 -21
  124. package/dist/embed/embed-page-chunk-MHGVD4EV.js +0 -525
  125. package/dist/embed/embed-page-chunk-U4XSGC4Y.js +0 -7009
  126. package/dist/embed/embed-page-chunk-VXWRPT33.js +0 -2
  127. package/dist/embed/embed-page-classDiagram-2ON5EDUG-VYHHMIKZ.js +0 -1
  128. package/dist/embed/embed-page-classDiagram-v2-WZHVMYZB-5AISLXUF.js +0 -1
  129. package/dist/embed/embed-page-cshtml-ZY7LJSTH.js +0 -1
  130. package/dist/embed/embed-page-en-2WLCQIB6.js +0 -1
  131. package/dist/embed/embed-page-en-7WXWAWGG.js +0 -1
  132. package/dist/embed/embed-page-en-ICVL5QAC.js +0 -1
  133. package/dist/embed/embed-page-en-OLZHSUC3.js +0 -1
  134. package/dist/embed/embed-page-infoDiagram-WHAUD3N6-FQMKXERB.js +0 -2
  135. package/dist/embed/embed-page-stateDiagram-v2-4FDKWEC3-G3EWQCOW.js +0 -1
  136. package/dist/embed/embed-page-zh-EYLHUD5G.js +0 -1
  137. package/dist/embed/embed-page-zh-FPYCTKQK.js +0 -1
  138. package/dist/embed/embed-page-zh-OPUVZZSQ.js +0 -1
  139. package/dist/mcpTransportRegistry-VXUVJKBC.mjs +0 -14
  140. package/dist/mcpTransportRegistry-VXUVJKBC.mjs.map +0 -1
  141. package/dist/open-MDC4VCR7.mjs.map +0 -1
  142. /package/dist/embed/{embed-page-chunk-34FVRFPW.js → embed-page-chunk-UH47IEAR.js} +0 -0
  143. /package/dist/{open-files-FS5AN6Q6.mjs.map → open-files-V3R2BFTV.mjs.map} +0 -0
package/dist/index.d.mts CHANGED
@@ -1,9 +1,9 @@
1
1
  import * as http from 'http';
2
- import { ProjectRoomMessage, ProjectBotMembership, ProjectMembership, TaskItem, ChannelAdapter, InboundMessage, Binding, BindingRegistry, ChannelInstallationStore, DispatchResult, ModelProviderStore, UserTenantLinkStore, LLMConfig, ModelProviderPublicEntry, TenantModelCatalog, CreateModelProviderRequest, UpdateModelProviderRequest, ModelDiscoveryResult, ModelProviderProbeRequest, ModelProviderProbeResult, ModelSelection, ProjectRoomEventScope, ProjectRoomBusinessEventDraft, ProjectRoomScopedBusinessEvent, ProjectRoomBusinessEvent, ProjectRoomEventSubscription, TaskStore, ProjectRoomStore, ProjectBotMembershipStore, ThreadStore, ProjectRoom, Thread, ProjectStore, TaskWorkItemStore, TenantStore, OutboxStore, A2AApiKeyStore, AssistantStore, A2AApiKeyEntry, A2AAuthContext, Assistant, OpenGrant, A2AApiKeyRecord, LoggerType, PinoFileOptions } from '@axiom-lattice/protocols';
3
- import { TrustedAgentMessageDispatcher, TrustedMessageOptions, TaskLifecycleListener, TaskLifecycleService, IMessageQueueStore, Agent, TaskLivenessProbe, TaskRunRegistry } from '@axiom-lattice/core';
2
+ import { ProjectRoomMessage, ProjectBotMembership, ProjectMembership, TaskItem, ChannelAdapter, InboundMessage, Binding, BindingRegistry, ChannelInstallationStore, DispatchResult, ModelProviderStore, UserTenantLinkStore, LLMConfig, ModelProviderPublicEntry, TenantModelCatalog, CreateModelProviderRequest, UpdateModelProviderRequest, ModelDiscoveryResult, ModelProviderProbeRequest, ModelProviderProbeResult, ModelSelection, ProjectRoomEventScope, ProjectRoomBusinessEventDraft, ProjectRoomScopedBusinessEvent, ProjectRoomBusinessEvent, ProjectRoomEventSubscription, TaskStore, ProjectRoomStore, ProjectBotMembershipStore, ThreadStore, Thread, ProjectRoom, ProjectStore, Project, TaskWorkItemStore, TenantStore, OutboxStore, A2AApiKeyStore, AssistantStore, A2AApiKeyEntry, A2AAuthContext, Assistant, OpenGrant, A2AApiKeyRecord, LeaseStore, CheckpointMaintenance, LoggerType, PinoFileOptions } from '@axiom-lattice/protocols';
3
+ import { TrustedAgentMessageDispatcher, TrustedMessageOptions, TaskLifecycleListener, AgentRunJsonObject, TrustedRunContext, AgentRunRecord, TaskLifecycleService, IAgentRunLedger, AppendLog, TaskLivenessProbe, TaskRunRegistry, AgentLifecycleTapEvent, AgentLifecycleEventName, AgentThreadInterface, AgentCheckpointTip, ExecuteRunInput, ExecuteRunResult, AgentRunSource, AgentRunStatus, ThreadRunLeaseHandle } from '@axiom-lattice/core';
4
4
  export { TaskLivenessProbe } from '@axiom-lattice/core';
5
5
  import * as fastify from 'fastify';
6
- import { RouteGenericInterface, FastifyRequest, FastifyReply } from 'fastify';
6
+ import { RouteGenericInterface, FastifyRequest, FastifyReply, FastifyInstance } from 'fastify';
7
7
 
8
8
  /** Public publisher surface for committed Project Room business facts. */
9
9
  interface ProjectRoomRealtimePublisher {
@@ -93,6 +93,15 @@ interface MessageRouterConfig {
93
93
  adapterRegistry: ChannelAdapterRegistry;
94
94
  installationStore: ChannelInstallationStore;
95
95
  trustedDispatcher?: TrustedAgentMessageDispatcher;
96
+ /**
97
+ * Route non-Room public ingress through a durable pending run plus
98
+ * {@link ChannelReplyJob} instead of executing in-process.
99
+ *
100
+ * @remarks Default `false` so existing deployments/tests keep the legacy
101
+ * `agent.addMessage` + `reply:ready` path. Room and trusted dispatch are
102
+ * never affected.
103
+ */
104
+ durableChannelRuns?: boolean;
96
105
  }
97
106
  /** Host-trusted options for internal orchestration only. Public routes must not provide these. */
98
107
  interface InternalMessageDispatchOptions {
@@ -123,6 +132,7 @@ declare class MessageRouter {
123
132
  private adapterRegistry;
124
133
  private installationStore;
125
134
  private trustedDispatcher?;
135
+ private durableChannelRuns;
126
136
  private replySubscriptions;
127
137
  /** Recent answer-by-message resumes per presence thread (first answer wins). */
128
138
  private recentResumes;
@@ -157,6 +167,12 @@ declare class MessageRouter {
157
167
  */
158
168
  dispatch(message: InboundMessage, options?: InternalMessageDispatchOptions): Promise<DispatchResult>;
159
169
  private dispatchInternal;
170
+ /**
171
+ * Creates the durable pending run and its reply job for non-Room ingress.
172
+ *
173
+ * @returns The serialized queued result for {@link DispatchResult.result}.
174
+ */
175
+ private dispatchDurableChannelRun;
160
176
  private runMiddlewares;
161
177
  /**
162
178
  * 流式中途刷新的节流间隔与上限。
@@ -725,6 +741,16 @@ declare class ProjectTaskThreadService {
725
741
  private readonly deps;
726
742
  /** @param deps Thread persistence and unique-ID generation dependencies. */
727
743
  constructor(deps: ProjectTaskThreadServiceDependencies);
744
+ /**
745
+ * Reads the persisted Thread without creating or validating it.
746
+ *
747
+ * @param tenantId Tenant scope.
748
+ * @param threadId Thread identifier.
749
+ * @returns The stored Thread snapshot, or `undefined` when it is unreadable.
750
+ * @remarks Used by the run planner to rebuild trusted context from canonical
751
+ * rows; it never mutates Thread state.
752
+ */
753
+ getThreadById(tenantId: string, threadId: string): Promise<Thread | undefined>;
728
754
  /**
729
755
  * Ensures a canonical Task execution Thread for the requested ownership mode.
730
756
  *
@@ -736,60 +762,470 @@ declare class ProjectTaskThreadService {
736
762
  private assertThread;
737
763
  }
738
764
 
739
- /** Result of one Project Task Agent execution settlement. */
740
- type ProjectTaskRunOutcome = {
741
- kind: "completed";
742
- task: TaskItem;
743
- thread: Thread;
744
- messageId: string;
745
- } | {
746
- kind: "cancelled";
765
+ /** Execution mode of a Project Task run intent. */
766
+ type ProjectTaskRunMode = "start" | "resume";
767
+
768
+ /** HITL resume command carried into a Project Task run intent. */
769
+ type ProjectTaskRunCommand = AgentRunJsonObject;
770
+ /** Fields shared by both Project Task run intent variants. */
771
+ interface ProjectTaskRunIntentBase {
772
+ source: "project_task";
773
+ taskId: string;
774
+ tenantId: string;
775
+ workspaceId: string;
776
+ projectId: string;
777
+ assistantId: string;
778
+ threadId: string;
779
+ config: {
780
+ taskId: string;
781
+ execution: {
782
+ taskId: string;
783
+ };
784
+ };
785
+ metadata: {
786
+ taskId: string;
787
+ projectId: string;
788
+ workspaceId: string;
789
+ actor: string;
790
+ mode: ProjectTaskRunMode;
791
+ };
792
+ }
793
+ /**
794
+ * Durable Project Task run intent produced from canonical rows.
795
+ *
796
+ * @remarks A run carries exactly one of `input` (normal start/resume) or
797
+ * `command` (HITL resume), matching `CreatePendingAgentRunInput`.
798
+ */
799
+ type ProjectTaskRunIntent = (ProjectTaskRunIntentBase & {
800
+ input: {
801
+ id: string;
802
+ message: string;
803
+ };
804
+ command?: never;
805
+ }) | (ProjectTaskRunIntentBase & {
806
+ command: ProjectTaskRunCommand;
807
+ input?: never;
808
+ });
809
+ /**
810
+ * Validated plan for one Project Task execution attempt.
811
+ *
812
+ * @remarks The planner never persists the trusted context: callers pass it to
813
+ * the run worker, while `run` is the durable queue intent.
814
+ */
815
+ interface ProjectTaskRunPlan {
816
+ run: ProjectTaskRunIntent;
817
+ trustedContext: TrustedRunContext;
818
+ }
819
+ /** Collaborators required to validate Project Task runs. */
820
+ interface ProjectTaskRunPlannerDependencies {
821
+ tasks: Pick<TaskStore, "getById">;
822
+ projects: Pick<ProjectStore, "getProjectById">;
823
+ rooms: Pick<ProjectRoomStore, "getMainRoom">;
824
+ bots: Pick<ProjectBotMembershipStore, "findByAssistant">;
825
+ threads: Pick<ThreadStore, "getThreadById">;
826
+ /** JIT-materializes a public-project task owner so it has a room anchor to post/reply. */
827
+ ensurePublicOwner?: (input: {
828
+ tenantId: string;
829
+ projectId: string;
830
+ assistantId: string;
831
+ }) => Promise<unknown>;
832
+ /** Run input id factory; defaults to `randomUUID`. */
833
+ idFactory?: () => string;
834
+ }
835
+ /** Input for planning one Project Task run. */
836
+ interface PlanProjectTaskRunInput {
747
837
  task: TaskItem;
748
838
  thread: Thread;
749
- messageId: string;
750
- } | {
751
- kind: "failed";
839
+ mode: ProjectTaskRunMode;
840
+ actor?: string;
841
+ /** Optional HITL resume command passed through for Task 4 to attach. */
842
+ command?: ProjectTaskRunCommand;
843
+ }
844
+ /**
845
+ * Converts validated Project Task rows into a durable run intent and rebuilds
846
+ * trusted context from canonical rows when a worker executes the run.
847
+ *
848
+ * @remarks The planner performs no lifecycle or queue mutation and never trusts
849
+ * `run.config` for authority.
850
+ */
851
+ declare class ProjectTaskRunPlanner {
852
+ private readonly deps;
853
+ private readonly targets;
854
+ /** @param deps Canonical Task, Project, room, roster, and Thread reads. */
855
+ constructor(deps: ProjectTaskRunPlannerDependencies);
856
+ /**
857
+ * Validates canonical Project Task rows and returns a run intent.
858
+ *
859
+ * @param input Task snapshot, canonical Thread, dispatch mode, actor, and optional command.
860
+ * @returns The run intent plus trusted context for tests/diagnostics.
861
+ * @throws {@link ProjectTaskRunPlanError} when scope, status, roster, Thread, or dependencies drift.
862
+ */
863
+ plan(input: PlanProjectTaskRunInput): Promise<ProjectTaskRunPlan>;
864
+ /**
865
+ * Rebuilds trusted Project Task context for a claimed run.
866
+ *
867
+ * @param run Durable run record claimed by the worker.
868
+ * @returns The trusted context wrapper, or `undefined` for non-ProjectTask sources.
869
+ * @throws {@link ProjectTaskRunPlanError} when the canonical scope has drifted.
870
+ * @remarks `run.config` is never read for authority.
871
+ */
872
+ resolveTrustedRunContext(run: AgentRunRecord): Promise<TrustedRunContext | undefined>;
873
+ private requireTask;
874
+ private assertStatus;
875
+ }
876
+
877
+ /**
878
+ * Admission modes with their exact allowed Task statuses.
879
+ *
880
+ * @remarks `plan_*` modes accept the pre- and post-transition status so a
881
+ * planner can run on either side of the lifecycle claim.
882
+ */
883
+ type ProjectTaskAdmissionMode = "start" | "resume" | "restart" | "recovery" | "plan_start" | "plan_resume";
884
+ /** Exact task load input; `expectedTask` requires the caller snapshot to still match. */
885
+ interface RequireProjectTaskInput {
886
+ tenantId: string;
887
+ taskId: string;
888
+ expectedTask?: TaskItem;
889
+ }
890
+ /** Non-roster Project Task scope resolved before any public-owner materialization. */
891
+ interface ProjectTaskTargetContext {
752
892
  task: TaskItem;
753
- thread: Thread;
754
- messageId: string;
755
- error: string;
756
- cause: "agent_failed" | "queue_rejected" | "settlement_timeout" | "recovery_ended" | "interruption_not_durable" | "startup_cancelled";
757
- lifecycleHandled: boolean;
758
- } | {
759
- kind: "interrupted";
893
+ project: Project;
894
+ room: ProjectRoom;
895
+ rosterExempt: boolean;
896
+ }
897
+ /** Fully admitted Project Task target including the owner membership. */
898
+ interface ProjectTaskTarget extends ProjectTaskTargetContext {
899
+ membership: ProjectBotMembership;
900
+ }
901
+ /** Options for target context resolution. */
902
+ interface ResolveProjectTaskContextOptions {
903
+ /**
904
+ * Whether to require completed dependencies (default `true`).
905
+ *
906
+ * @remarks Recovery revalidation skips dependency reads because the task was
907
+ * already admitted when it started; planning/start always checks them.
908
+ */
909
+ dependencies?: boolean;
910
+ }
911
+ /** Collaborators required to resolve Project Task admission facts. */
912
+ interface ProjectTaskTargetResolverDependencies {
913
+ tasks: Pick<TaskStore, "getById">;
914
+ projects: Pick<ProjectStore, "getProjectById">;
915
+ rooms: Pick<ProjectRoomStore, "getMainRoom">;
916
+ bots: Pick<ProjectBotMembershipStore, "findByAssistant">;
917
+ /** JIT-materializes a public-project task owner so it has a room anchor to post/reply. */
918
+ ensurePublicOwner?: (input: {
919
+ tenantId: string;
920
+ projectId: string;
921
+ assistantId: string;
922
+ }) => Promise<unknown>;
923
+ }
924
+ /**
925
+ * Single admission authority for Project Task planning and execution.
926
+ *
927
+ * @remarks All reads are canonical store reads; the only write is the optional
928
+ * public-project owner materialization, which runs only after task scope,
929
+ * project, room, and dependency checks pass.
930
+ */
931
+ declare class ProjectTaskTargetResolver {
932
+ private readonly deps;
933
+ /** @param deps Canonical Task, Project, room, and roster reads plus public JIT. */
934
+ constructor(deps: ProjectTaskTargetResolverDependencies);
935
+ /**
936
+ * Loads an exact Project Task and optionally rejects caller-snapshot drift.
937
+ *
938
+ * @param input Tenant, task id, and optional caller snapshot.
939
+ * @returns The canonical Task.
940
+ * @throws {@link ProjectTaskTargetError} when the task is missing, out of scope, or drifted.
941
+ */
942
+ requireTask(input: RequireProjectTaskInput): Promise<TaskItem>;
943
+ /**
944
+ * Asserts that a Task status is admissible for a mode.
945
+ *
946
+ * @param task Canonical Project Task.
947
+ * @param mode Admission mode.
948
+ * @throws {@link ProjectTaskTargetError} when the status is not allowed.
949
+ */
950
+ assertModeStatus(task: TaskItem, mode: ProjectTaskAdmissionMode): void;
951
+ /**
952
+ * Resolves project, main room, and dependency admission.
953
+ *
954
+ * @param task Canonical Project Task.
955
+ * @param options Dependency toggle (recovery skips dependencies).
956
+ * @returns The non-roster scope.
957
+ * @throws {@link ProjectTaskTargetError} on scope or dependency drift.
958
+ */
959
+ resolveContext(task: TaskItem, options?: ResolveProjectTaskContextOptions): Promise<ProjectTaskTargetContext>;
960
+ /**
961
+ * Resolves the owner membership, materializing a public-project owner first.
962
+ *
963
+ * @param task Canonical Project Task.
964
+ * @param context Non-roster scope from {@link resolveContext}.
965
+ * @returns The fully admitted target.
966
+ * @throws {@link ProjectTaskTargetError} when roster authority is invalid.
967
+ */
968
+ resolveOwner(task: TaskItem, context: ProjectTaskTargetContext): Promise<ProjectTaskTarget>;
969
+ /**
970
+ * Resolves the full target (scope, dependencies, then owner membership).
971
+ *
972
+ * @param task Canonical Project Task.
973
+ * @param options Dependency toggle.
974
+ * @returns The fully admitted target.
975
+ */
976
+ resolveTarget(task: TaskItem, options?: ResolveProjectTaskContextOptions): Promise<ProjectTaskTarget>;
977
+ /**
978
+ * Validates canonical Task Thread metadata against stable scope.
979
+ *
980
+ * @param task Canonical Project Task.
981
+ * @param thread Candidate Task execution Thread.
982
+ * @param roomId Exact Project main room id.
983
+ * @param owner Optional admitted owner; when supplied, a non-exempt project
984
+ * additionally requires the metadata membership id to equal the roster id.
985
+ * @returns The canonical Thread snapshot.
986
+ * @remarks The metadata's `membershipId` is accepted as canonical for public
987
+ * projects: a synthetic owner membership and the materialized Thread's real
988
+ * roster membership id legitimately differ. Business projects must match.
989
+ * @throws {@link ProjectTaskTargetError} on any scope, membership, or parent-task mismatch.
990
+ */
991
+ requireThread(task: TaskItem, thread: Thread, roomId: string, owner?: Pick<ProjectTaskTarget, "membership" | "rosterExempt">): Thread;
992
+ private assertDependenciesReady;
993
+ }
994
+
995
+ /** Input identifying the Project Task to create a run for. */
996
+ interface ProjectTaskRunInput {
997
+ /** Tenant that owns the task. */
998
+ tenantId: string;
999
+ /** Persisted Project Task identifier. */
1000
+ taskId: string;
1001
+ /** Actor attributed to the lifecycle transition; defaults to the service actor. */
1002
+ actor?: string;
1003
+ }
1004
+ /** Resume input carrying the optional HITL command. */
1005
+ interface ProjectTaskResumeInput extends ProjectTaskRunInput {
1006
+ /** HITL resume command; when omitted the run carries a normal resume input. */
1007
+ command?: ProjectTaskRunCommand;
1008
+ }
1009
+ /** Successful dispatch result returned after the pending run row is committed. */
1010
+ interface ProjectTaskRunDispatchResult {
1011
+ /** Discriminator for callers switching on the result shape. */
1012
+ kind: "dispatched";
1013
+ /** Task snapshot after the lifecycle transition. */
760
1014
  task: TaskItem;
761
- thread: Thread;
762
- messageId: string;
763
- } | {
764
- kind: "aborted";
1015
+ /** Canonical Task execution Thread the run executes on. */
1016
+ threadId: string;
1017
+ /** Identifier of the committed pending run. */
1018
+ runId: string;
1019
+ }
1020
+ /** Compatibility options accepted by {@link ProjectTaskRunService.dispatchDetached}. */
1021
+ interface ProjectTaskDetachedOptions {
1022
+ /** HITL resume command; only attached when the task is genuinely interrupted. */
1023
+ command?: ProjectTaskRunCommand;
1024
+ /** Expected task status guard evaluated before routing; defaults to no guard. */
1025
+ expectedStatus?: TaskItem["status"];
1026
+ }
1027
+ /** Detached dispatch result matching the Project Task execution caller contract. */
1028
+ interface ProjectTaskRunDetachedResult {
1029
+ /** Task snapshot after the lifecycle transition. */
765
1030
  task: TaskItem;
766
- thread: Thread;
767
- messageId: string;
768
- } | {
1031
+ /** Canonical Task execution Thread the run executes on. */
1032
+ threadId: string;
1033
+ /** The pending run row was committed. */
1034
+ queued: true;
1035
+ /** Identifier of the committed pending run. */
1036
+ runId: string;
1037
+ }
1038
+ /** Structured warning sink used for observable convergence failures. */
1039
+ interface ProjectTaskRunLogger {
1040
+ /**
1041
+ * @param message Human-readable warning.
1042
+ * @param context Structured diagnostic fields (`taskId`, `code`, `error`, …).
1043
+ */
1044
+ warn(message: string, context: Record<string, unknown>): void;
1045
+ }
1046
+ /** Collaborators required to transition a Project Task and create its run. */
1047
+ interface ProjectTaskRunServiceDependencies {
1048
+ /** Canonical task reads used for admission. */
1049
+ tasks: Pick<TaskStore, "getById">;
1050
+ /** Project reads used for scope admission. */
1051
+ projects: Pick<ProjectStore, "getProjectById">;
1052
+ /** Main-room reads used for scope admission. */
1053
+ rooms: Pick<ProjectRoomStore, "getMainRoom">;
1054
+ /** Roster reads used to authorize the task owner. */
1055
+ bots: Pick<ProjectBotMembershipStore, "findByAssistant">;
1056
+ /** Canonical Project Task Thread resolution. */
1057
+ threads: ProjectTaskThreadService;
1058
+ /** Governed task lifecycle operations. */
1059
+ lifecycle: TaskLifecycleService;
1060
+ /** Durable run ledger that owns the pending queue row. */
1061
+ ledger: IAgentRunLedger;
1062
+ /** Best-effort wake after the pending row commits. */
1063
+ wake: () => Promise<void>;
1064
+ /** JIT-materializes a public-project task owner so it has a room anchor to post/reply. */
1065
+ ensurePublicOwner?: (input: {
1066
+ tenantId: string;
1067
+ projectId: string;
1068
+ assistantId: string;
1069
+ }) => Promise<unknown>;
1070
+ /** Run id factory; defaults to `randomUUID`. */
1071
+ idFactory?: () => string;
1072
+ /** Default actor when a dispatch omits one; defaults to `system`. */
1073
+ actor?: string;
1074
+ /** Optional structured warning sink for failed task convergence; defaults to no-op. */
1075
+ log?: ProjectTaskRunLogger;
1076
+ /** Injectable admission resolver for tests; defaults to a canonical resolver. */
1077
+ targets?: ProjectTaskTargetResolver;
1078
+ /** Injectable run planner for tests; defaults to a canonical planner. */
1079
+ planner?: ProjectTaskRunPlanner;
1080
+ }
1081
+ /**
1082
+ * Creates durable Project Task runs after applying the canonical lifecycle transition.
1083
+ *
1084
+ * @remarks This service replaces only the Project Task dispatch transport. It
1085
+ * performs admission through {@link ProjectTaskTargetResolver}, resolves the
1086
+ * canonical Thread through {@link ProjectTaskThreadService}, builds one
1087
+ * `source="project_task"` intent through {@link ProjectTaskRunPlanner}, commits
1088
+ * the pending {@link IAgentRunLedger} row, and wakes the run queue. It never
1089
+ * enqueues on the legacy thread message queue and never waits for execution
1090
+ * settlement; Task completion remains the worker's `manage_task` write.
1091
+ */
1092
+ declare class ProjectTaskRunService {
1093
+ private readonly deps;
1094
+ private readonly targets;
1095
+ private readonly planner;
1096
+ private readonly runBridge;
1097
+ private readonly idFactory;
1098
+ private readonly log;
1099
+ /** @param deps Canonical stores, lifecycle, ledger, and queue wake collaborators. */
1100
+ constructor(deps: ProjectTaskRunServiceDependencies);
1101
+ /**
1102
+ * Starts a pending Project Task and creates its first run.
1103
+ *
1104
+ * @param input Tenant, task id, and optional actor.
1105
+ * @returns The `in_progress` task snapshot and committed run id.
1106
+ * @throws {@link ProjectTaskRunDispatchError} when admission, the lifecycle
1107
+ * CAS, or run creation fails.
1108
+ */
1109
+ start(input: ProjectTaskRunInput): Promise<ProjectTaskRunDispatchResult>;
1110
+ /**
1111
+ * Resumes an interrupted Project Task and creates its continuation run.
1112
+ *
1113
+ * @param input Tenant, task id, optional actor, and optional HITL command.
1114
+ * @returns The `in_progress` task snapshot and committed run id.
1115
+ * @throws {@link ProjectTaskRunDispatchError} when admission, the resume
1116
+ * transition, or run creation fails.
1117
+ */
1118
+ resume(input: ProjectTaskResumeInput): Promise<ProjectTaskRunDispatchResult>;
1119
+ /**
1120
+ * Retries a failed or reopens a terminal Project Task, then creates an input run.
1121
+ *
1122
+ * @param input Tenant, task id, and optional actor.
1123
+ * @returns The `in_progress` task snapshot and committed run id.
1124
+ * @throws {@link ProjectTaskRunDispatchError} when admission, the retry/reopen
1125
+ * transition, or run creation fails.
1126
+ */
1127
+ restart(input: ProjectTaskRunInput): Promise<ProjectTaskRunDispatchResult>;
1128
+ /**
1129
+ * Routes a Project Task to the transition its current status requires.
1130
+ *
1131
+ * @param input Tenant, task id, optional actor, and optional resume command.
1132
+ * @returns The `in_progress` task snapshot and committed run id.
1133
+ * @throws {@link ProjectTaskRunDispatchError} when the task is already
1134
+ * `in_progress` or any transition/run creation fails.
1135
+ * @remarks `pending` starts, `interrupted` resumes, and `failed`/`completed`/
1136
+ * `cancelled` retry or reopen. A resume command is only attached to an
1137
+ * interrupted-task resume.
1138
+ */
1139
+ dispatch(input: ProjectTaskResumeInput): Promise<ProjectTaskRunDispatchResult>;
1140
+ /**
1141
+ * Creates the run without awaiting execution.
1142
+ *
1143
+ * @param input Tenant, task id, and optional actor.
1144
+ * @param _signal Retained for the existing caller shape; dispatch never waits.
1145
+ * @param options Optional expected-status guard and HITL resume command.
1146
+ * @returns The `in_progress` task snapshot, `queued: true`, and committed run id.
1147
+ * @throws {@link ProjectTaskRunDispatchError} with
1148
+ * `PROJECT_TASK_NOT_RESUMABLE` when `expectedStatus` no longer matches.
1149
+ * @remarks Run creation is already detached (this service never waits for
1150
+ * settlement). A resume command is only attached when the task is genuinely
1151
+ * interrupted, matching {@link dispatch}.
1152
+ */
1153
+ dispatchDetached(input: ProjectTaskRunInput, _signal?: AbortSignal, options?: ProjectTaskDetachedOptions): Promise<ProjectTaskRunDetachedResult>;
1154
+ /**
1155
+ * Reads the active (`pending`/`running`) run for a Project Task.
1156
+ *
1157
+ * @param input Tenant and persisted task id.
1158
+ * @returns The active run record, or `undefined` when no run is active.
1159
+ * @remarks Recovery uses this instead of queue-row classification; a missing
1160
+ * row means no executor owns the task, not that work is still queued.
1161
+ */
1162
+ getActiveRun(input: {
1163
+ tenantId: string;
1164
+ taskId: string;
1165
+ }): Promise<AgentRunRecord | undefined>;
1166
+ /**
1167
+ * Reads the newest durable run recorded for a Project Task Thread.
1168
+ *
1169
+ * @param input Tenant, task id, and canonical Task Thread id.
1170
+ * @returns The newest matching `source="project_task"` run for the task, or `undefined`.
1171
+ * @remarks Used by startup recovery to settle a lost terminal run when no
1172
+ * active run exists. Reads the run ledger directly; the legacy queue is never
1173
+ * consulted.
1174
+ */
1175
+ getLatestRun(input: {
1176
+ tenantId: string;
1177
+ taskId: string;
1178
+ threadId: string;
1179
+ }): Promise<AgentRunRecord | undefined>;
1180
+ private dispatchLoaded;
1181
+ private startValidated;
1182
+ private resumeValidated;
1183
+ private restartValidated;
1184
+ /**
1185
+ * Resolves an already-active run for the same task and Thread.
1186
+ *
1187
+ * @param task Canonical task snapshot.
1188
+ * @param thread Canonical execution Thread.
1189
+ * @param actor Actor attributed to convergence if the ledger lookup fails.
1190
+ * @param conflictCode Typed code used when the active run belongs to another
1191
+ * Thread or, when required, when no active run exists.
1192
+ * @param requireActive Whether a missing active run is a conflict.
1193
+ * @returns The idempotent dispatched result, or `undefined` when no active run
1194
+ * exists and `requireActive` is false.
1195
+ * @throws {@link ProjectTaskRunDispatchError} with
1196
+ * `PROJECT_TASK_RUN_LOOKUP_FAILED` after converging the task when the ledger
1197
+ * lookup itself fails.
1198
+ */
1199
+ private idempotentDispatch;
1200
+ /**
1201
+ * Plans the run intent and commits the pending row, converging on failure.
1202
+ *
1203
+ * @remarks A planner or ledger failure after the lifecycle transition calls
1204
+ * `bridge.settleRun(failed)` best-effort so the task never stays `in_progress`
1205
+ * without a durable run. An already-active run for this task makes the
1206
+ * dispatch idempotent instead of creating a second run.
1207
+ */
1208
+ private planAndCreate;
1209
+ private convergeFailure;
1210
+ private wakeSafely;
1211
+ private requireTask;
1212
+ private resolveTarget;
1213
+ private ensureThread;
1214
+ private assertAgentOwned;
1215
+ private assertStatus;
1216
+ private resolveActor;
1217
+ }
1218
+
1219
+ /** Result of one Project Task dispatch committed to the durable run queue. */
1220
+ interface ProjectTaskRunOutcome {
769
1221
  kind: "deferred";
770
1222
  task: TaskItem;
771
1223
  thread: Thread;
772
1224
  messageId: string;
773
- };
774
- /** Stable classification of the queue fact used to recover an in-progress Project Task. */
775
- type ProjectTaskRecoveryClassification = {
776
- kind: "pending" | "processing";
777
- inputMessageId: string;
778
- } | {
779
- kind: "missing";
780
- } | {
781
- kind: "invalid";
782
- reason: string;
783
- };
784
- /** Runtime bounds for Project Task settlement. */
785
- interface ProjectTaskExecutionOptions {
786
- /** Maximum time to retain Agent listeners while awaiting execution settlement. */
787
- settlementTimeoutMs: number;
788
- /** Maximum time to await lifecycle persistence after a failed or inconsistent execution. */
789
- lifecycleSettlementTimeoutMs: number;
790
- /** Queue message id factory; defaults to `randomUUID`. */
791
- idFactory?: () => string;
1225
+ runId: string;
792
1226
  }
1227
+ /** Durable run-queue port used by the Project Task dispatch path. */
1228
+ type ProjectTaskRunServicePort = Pick<ProjectTaskRunService, "start" | "resume" | "dispatch" | "dispatchDetached" | "getActiveRun" | "getLatestRun">;
793
1229
  /**
794
1230
  * Optional resume command threaded into the queued trusted run message.
795
1231
  *
@@ -810,92 +1246,61 @@ interface ProjectTaskResumeOptions {
810
1246
  */
811
1247
  expectedStatus?: TaskItem["status"];
812
1248
  }
813
- /** Collaborators required to validate, dispatch, recover, and settle Project Task executions. */
1249
+ /** Collaborators required to dispatch Project Task executions. */
814
1250
  interface ProjectTaskExecutionDependencies {
815
- tasks: TaskStore;
816
- projects: Pick<ProjectStore, "getProjectById">;
817
- rooms: Pick<ProjectRoomStore, "getMainRoom">;
818
- bots: Pick<ProjectBotMembershipStore, "findByAssistant">;
819
- threads: ProjectTaskThreadService;
820
- lifecycle: TaskLifecycleService;
821
- dispatcher: TrustedAgentMessageDispatcher;
822
- queue: IMessageQueueStore;
823
- agentFor: (task: TaskItem, thread: Thread) => Agent;
824
- /** JIT-materializes a public-project task owner so it has a room anchor to post/reply. */
825
- ensurePublicOwner?: (input: {
826
- tenantId: string;
827
- projectId: string;
828
- assistantId: string;
829
- }) => Promise<unknown>;
830
- /**
831
- * Optional gateway hook: a task thread paused with a durable interruption.
832
- *
833
- * @remarks Best-effort and fire-and-forget: it never blocks or alters
834
- * settlement. It may fire more than once for the same interrupt (the
835
- * `message:interrupted` event path and the recovery path), so consumers must
836
- * dedupe using the correlation key derived from `messageId`.
837
- */
838
- onTaskInterrupted?: (input: {
839
- task: TaskItem;
840
- thread: Thread;
841
- messageId: string;
842
- state: unknown;
843
- }) => void | Promise<void>;
844
- }
845
- /** Input for recovering exactly one persisted Project Task queue execution. */
846
- interface RecoverProjectTaskExecutionInput {
847
- task: TaskItem;
848
- thread: Thread;
849
- inputMessageId: string;
850
- mode: "pending" | "processing";
1251
+ /** Canonical Task Thread reads used to map a committed run to its thread. */
1252
+ threads: Pick<ProjectTaskThreadService, "getThreadById">;
1253
+ /** Durable run-queue dispatch and run-ledger read port. */
1254
+ runService: ProjectTaskRunServicePort;
851
1255
  }
852
1256
  /**
853
- * Executes and recovers Project Tasks on canonical Task Threads.
1257
+ * Dispatches Project Tasks on the durable run queue.
854
1258
  *
855
- * The service arms exact Agent listeners before enqueue/recovery and treats Task lifecycle
856
- * persistence as authoritative. Its caller owns machine serialization through the returned
857
- * settlement promise; a generic Agent completion never completes a Task by itself.
1259
+ * @remarks This service is a thin adapter over {@link ProjectTaskRunService}.
1260
+ * The run worker owns execution and settlement; dispatch only commits one
1261
+ * `source="project_task"` pending run and returns a deferred outcome. Recovery is
1262
+ * owned by {@link ProjectTaskObserver} through the active/latest run reads exposed
1263
+ * here.
858
1264
  */
859
1265
  declare class ProjectTaskExecutionService {
860
1266
  private readonly deps;
861
- private readonly options;
862
- private readonly runBridge;
863
1267
  /**
864
- * @param deps Persistence, lifecycle, dispatch, and Agent collaborators.
865
- * @param options Settlement watchdog bounds.
1268
+ * @param deps Canonical Thread reads and the durable run-queue port.
866
1269
  */
867
- constructor(deps: ProjectTaskExecutionDependencies, options: ProjectTaskExecutionOptions);
868
- /** Validates, atomically starts, dispatches, and settles a pending Project Task. */
1270
+ constructor(deps: ProjectTaskExecutionDependencies);
1271
+ /** Reads the active durable run for a Project Task. */
1272
+ getActiveProjectTaskRun(task: TaskItem): Promise<AgentRunRecord | undefined>;
1273
+ /**
1274
+ * Reads the newest durable run for an `in_progress` Project Task.
1275
+ *
1276
+ * @returns The newest matching run, or `undefined` when the task has no
1277
+ * recorded run or no canonical Thread.
1278
+ */
1279
+ getLatestProjectTaskRun(task: TaskItem): Promise<AgentRunRecord | undefined>;
1280
+ /** Validates, starts, and dispatches a pending Project Task run. */
869
1281
  start(input: {
870
1282
  tenantId: string;
871
1283
  taskId: string;
872
1284
  actor?: string;
873
1285
  }, signal?: AbortSignal): Promise<ProjectTaskRunOutcome>;
874
- /** Performs the pending→in_progress transition and loads the exact current task. */
875
- private prepareStart;
876
- /** Resumes an interrupted Project Task on its canonical existing Thread. */
1286
+ /** Resumes an interrupted Project Task and creates its continuation run. */
877
1287
  resume(input: {
878
1288
  tenantId: string;
879
1289
  taskId: string;
880
1290
  actor?: string;
881
1291
  }, signal?: AbortSignal, options?: ProjectTaskResumeOptions): Promise<ProjectTaskRunOutcome>;
882
- /** Clears the durable interruption and loads the exact resumed task. */
883
- private prepareResume;
884
1292
  /**
885
1293
  * Dispatches a Project Task by selecting the correct pre-run transition for
886
- * its current status, then running it.
1294
+ * its current status, then committing one run.
887
1295
  *
888
1296
  * @param input Tenant, task identity, and optional command actor.
889
- * @param signal Optional cancellation signal for the armed settlement.
890
- * @param options Optional resume command carried into the queued run message.
891
- * @returns The settled run outcome.
1297
+ * @param signal Optional cancellation signal checked before dispatch.
1298
+ * @param options Optional resume command carried into the queued run.
1299
+ * @returns The deferred run outcome.
892
1300
  * @remarks `start` semantics: make this task run. `pending` starts,
893
1301
  * `interrupted` resumes, `failed` retries, and `completed`/`cancelled`
894
1302
  * reopen, each followed by a trusted run dispatch. `in_progress` is a
895
- * conflict. This is a record transition plus an application-layer side
896
- * effect; the kernel only records the status change. `actor` attributes the
897
- * start/resume/retry/reopen transition and defaults to the system actor.
898
- * `options.command` is only carried on an interrupted-task resume.
1303
+ * conflict. `options.command` is only carried on an interrupted-task resume.
899
1304
  */
900
1305
  dispatch(input: {
901
1306
  tenantId: string;
@@ -903,17 +1308,12 @@ declare class ProjectTaskExecutionService {
903
1308
  actor?: string;
904
1309
  }, signal?: AbortSignal, options?: ProjectTaskResumeOptions): Promise<ProjectTaskRunOutcome>;
905
1310
  /**
906
- * Dispatches a Project Task without awaiting its settlement.
1311
+ * Dispatches a Project Task without awaiting execution.
907
1312
  *
908
1313
  * @param input Tenant, task identity, and optional command actor.
909
- * @param signal Optional cancellation signal for the pre-run transition.
910
- * @param options Optional resume command carried into the queued run message.
1314
+ * @param signal Optional cancellation signal checked before dispatch.
1315
+ * @param options Optional expected-status guard and HITL resume command.
911
1316
  * @returns The `in_progress` task snapshot and whether the run was queued.
912
- * @remarks Intended for command HTTP handlers that must release the Project
913
- * mutation mutex immediately: the armed settlement keeps running in the
914
- * background (completion, watchdog, and convergence) but the caller never
915
- * awaits it. Enqueue failure converges the task exactly like the awaited
916
- * path before throwing, so no phantom run is left behind.
917
1317
  */
918
1318
  dispatchDetached(input: {
919
1319
  tenantId: string;
@@ -923,79 +1323,9 @@ declare class ProjectTaskExecutionService {
923
1323
  task: TaskItem;
924
1324
  queued: boolean;
925
1325
  }>;
926
- /** Recovers one exact pending or processing queue row without dispatching a duplicate input. */
927
- recover(input: RecoverProjectTaskExecutionInput, signal?: AbortSignal): Promise<ProjectTaskRunOutcome>;
928
- /** Classifies one exact Task Thread queue fact with transition-safe repeated reads. */
929
- classifyRecovery(task: TaskItem, thread: Thread): Promise<ProjectTaskRecoveryClassification>;
930
- private requireTask;
931
- private validateReadyTask;
932
- private validateResumableTask;
933
- /**
934
- * Retries a failed task or reopens a terminal task, then runs it on its
935
- * canonical existing Thread.
936
- *
937
- * @param task Task snapshot already routed to the restartable branch.
938
- * @param actor Actor attributed to the retry/reopen transition.
939
- * @param signal Optional cancellation signal for the armed settlement.
940
- * @returns The settled run outcome.
941
- */
942
- private dispatchRestartable;
943
- /** Retries a failed task or reopens a terminal task, then loads the exact current task. */
944
- private prepareRestartable;
945
- private validateRestartableTask;
946
- private validateReadyTaskSnapshot;
947
- private requireRoom;
948
- private requireMembership;
949
- private requireCurrentThread;
950
- private dispatchRun;
951
- /**
952
- * Arms the exact settlement and enqueues the trusted run without awaiting settlement.
953
- *
954
- * @returns The armed settlement plus the enqueue result. A declined or thrown
955
- * enqueue yields `queued: false` with a stable reason; the caller decides
956
- * whether to converge only (awaited path) or converge and throw (detached).
957
- */
958
- private armAndEnqueue;
959
- /**
960
- * Arms and enqueues a run, then returns an `in_progress` snapshot immediately.
961
- *
962
- * @remarks The settlement continues in the background. On enqueue failure the
963
- * task is converged exactly like the awaited path before throwing, so no
964
- * phantom in-progress run is left behind.
965
- */
966
- private enqueueDetached;
967
- private claimDispatchFailure;
968
- private armSettlement;
1326
+ private dispatchRunQueue;
1327
+ private toDetachedOptions;
969
1328
  private throwIfCancelled;
970
- private awaitSettlementCancellation;
971
- private cancelSettlement;
972
- private compensateCancellation;
973
- private reloadTask;
974
- private settleInterruptedEvent;
975
- /**
976
- * Fire-and-forget best-effort projection of a durable interruption.
977
- *
978
- * @remarks Never awaited and never allowed to affect settlement. The state is
979
- * resolved lazily so a missing/undefined event state only triggers a thread
980
- * read when the hook is actually wired.
981
- */
982
- private emitInterrupted;
983
- /** Reads the paused thread snapshot when no interrupt event payload is available (recovery). */
984
- private readInterruptedThreadState;
985
- /**
986
- * Persists a durable `missing_input` interruption for a paused clarify question.
987
- *
988
- * @remarks The task must survive the pause so the room can answer it; the
989
- * agent resumes the same run once the answer arrives.
990
- */
991
- private interruptForClarify;
992
- private settleEndedRecovery;
993
- private terminalOutcome;
994
- private matchesExecution;
995
- private failSettledTaskAwaited;
996
- private withLifecycleTimeout;
997
- private failSettledTask;
998
- private validateExistingExecution;
999
1329
  }
1000
1330
 
1001
1331
  /** Aggregate result of one bounded deterministic startup reconciliation. */
@@ -1131,64 +1461,85 @@ declare const defaultSwaggerUiConfig: {
1131
1461
  transformStaticCSP: (header: string) => string;
1132
1462
  };
1133
1463
 
1134
- type QueueServiceType = "memory" | "redis";
1464
+ /** Optional startup/shutdown hooks for host-provided Gateway resources. */
1465
+ interface GatewayLifespan {
1466
+ /** Optional name used in shutdown error logs. */
1467
+ name?: string;
1468
+ /** Runs after the Fastify app is ready, before listen resolves. */
1469
+ onStartup?: (app: FastifyInstance) => void | Promise<void>;
1470
+ /** Runs when the Fastify app closes (signal or host close). */
1471
+ onShutdown?: (app: FastifyInstance) => void | Promise<void>;
1472
+ }
1473
+ /** Minimal logger seam for best-effort shutdown diagnostics. */
1474
+ interface LifespanLogger {
1475
+ /** Log a warning without failing shutdown. */
1476
+ warn(message: string, context?: Record<string, unknown>): void;
1477
+ }
1478
+ /**
1479
+ * Install ordered Gateway lifespan hooks exactly once per Fastify instance.
1480
+ *
1481
+ * Startup hooks run in array order and a failure rejects `app.ready()` /
1482
+ * `listen`. Shutdown hooks also run in order, but failures are logged and do
1483
+ * not prevent later hooks or app close from completing.
1484
+ *
1485
+ * @param app - Fastify instance.
1486
+ * @param lifespans - One hook or an ordered array.
1487
+ * @param log - Optional logger for shutdown failures.
1488
+ */
1489
+ declare function installLifespanHooks(app: FastifyInstance, lifespans?: GatewayLifespan | GatewayLifespan[], log?: LifespanLogger): void;
1490
+
1491
+ /**
1492
+ * Redis Streams implementation of the core {@link AppendLog} port.
1493
+ *
1494
+ * Each append is one `XADD` entry carrying the JSON payload in the `v` field.
1495
+ * Readers use a dedicated duplicated connection so a blocking `XREAD` never
1496
+ * stalls ordinary appends or ranges on the base connection.
1497
+ */
1135
1498
 
1136
- interface AgentTaskRequest {
1137
- assistant_id: string;
1138
- input: any;
1139
- thread_id: string;
1140
- "x-tenant-id": string;
1141
- command?: any;
1142
- callback_event?: string;
1143
- runConfig?: Record<string, any>;
1144
- main_thread_id?: string;
1145
- main_tenant_id?: string;
1146
- main_assistant_id?: string;
1499
+ /** Structured logger accepted by the Redis append-log adapter. */
1500
+ interface RedisAppendLogLogger {
1501
+ /** Report a connection or decoding problem with context. */
1502
+ warn(message: string, context: Record<string, unknown>): void;
1503
+ }
1504
+ /** Optional adapter configuration. */
1505
+ interface RedisAppendLogOptions {
1506
+ /** Logger for connection errors and corruption diagnostics. */
1507
+ logger?: RedisAppendLogLogger;
1508
+ /** Key prefix applied by the caller's buffer (documented for diagnostics). */
1509
+ keyPrefix?: string;
1510
+ }
1511
+ /** Handle owning a Redis-backed append log and its base connection. */
1512
+ interface RedisAppendLogHandle {
1513
+ /** Shared append log backed by Redis Streams. */
1514
+ log: AppendLog;
1515
+ /** Close the base connection (readers own their own connections). */
1516
+ close(): Promise<void>;
1147
1517
  }
1148
1518
  /**
1149
- * Agent任务消费者
1150
- * 负责监听和执行Agent任务事件,同时从队列中消费任务
1519
+ * Raised when a Redis Stream entry cannot be decoded.
1520
+ *
1521
+ * Corrupt entries fail loudly instead of being skipped: silently dropping a
1522
+ * trailing malformed record would make a tailing reader spin forever.
1151
1523
  */
1152
- declare class AgentTaskConsumer {
1153
- private isPolling;
1154
- private pollingInterval;
1155
- private pollingIntervalMs;
1156
- private maxConcurrentTasks;
1157
- private activeTasks;
1158
- private processing;
1159
- private immediateProcessingEnabled;
1160
- gatewayPort: number;
1161
- static agent_run_endpoint: string;
1162
- constructor(gatewayPort: number, pollingIntervalMs?: number);
1163
- /**
1164
- * 初始化事件监听和队列轮询
1165
- */
1166
- private initialize;
1167
- /**
1168
- * 启动队列轮询
1169
- */
1170
- startPollingQueue(): void;
1524
+ declare class ChunkLogCorruptionError extends Error {
1525
+ /** Cursor of the offending stream entry. */
1526
+ readonly cursor: string;
1527
+ /** Machine-readable error code. */
1528
+ readonly code = "CHUNK_LOG_CORRUPTION";
1171
1529
  /**
1172
- * 停止队列轮询
1530
+ * @param cursor - Cursor of the offending stream entry
1531
+ * @param reason - Why the entry could not be decoded
1173
1532
  */
1174
- stopPollingQueue(): void;
1175
- /**
1176
- * 处理单个任务并在完成后立即尝试处理下一个
1177
- */
1178
- processNextTask(): Promise<boolean>;
1179
- /**
1180
- * 检查队列中是否有任务并处理
1181
- */
1182
- checkQueueForTasks(): Promise<void>;
1183
- /**
1184
- * 从队列中消费任务
1185
- */
1186
- consumeFromQueue(): Promise<void>;
1187
- /**
1188
- * 处理通过事件触发的任务
1189
- */
1190
- trigger_agent_task(taskRequest: AgentTaskRequest): void;
1533
+ constructor(cursor: string, reason: string);
1191
1534
  }
1535
+ /**
1536
+ * Create a Redis Streams append log.
1537
+ *
1538
+ * @param url - Redis connection URL (for example `redis://localhost:6379`)
1539
+ * @param options - Optional logger/diagnostics configuration
1540
+ * @returns A handle exposing the log and a close function
1541
+ */
1542
+ declare function createRedisAppendLog(url: string, options?: RedisAppendLogOptions): Promise<RedisAppendLogHandle>;
1192
1543
 
1193
1544
  /**
1194
1545
  * Builds a cross-replica task-liveness probe from the registered `"lease"` store.
@@ -1497,6 +1848,91 @@ declare function publishRoomEvent(outbox: OutboxStore, owner: string, scope: Pro
1497
1848
  */
1498
1849
  declare function ingestRoomEvent(payload: unknown, broker: ProjectRoomEventBroker): boolean;
1499
1850
 
1851
+ /** Outbox topic carrying mirrored Agent lifecycle events across replicas. */
1852
+ declare const AGENT_LIFECYCLE_TOPIC = "agent-lifecycle";
1853
+ /**
1854
+ * Agent lifecycle events that are mirrored through the durable outbox.
1855
+ *
1856
+ * Only events consumed by cross-replica delivery paths are mirrored
1857
+ * (`MessageRouter` replies and task settlement); high-frequency execution
1858
+ * progress events stay process-local.
1859
+ */
1860
+ declare const MIRRORED_AGENT_EVENTS: readonly ["reply:ready", "message:completed", "message:failed", "message:interrupted"];
1861
+ /** Minimal local Agent surface needed to deliver a mirrored event. */
1862
+ interface AgentLifecycleTarget {
1863
+ /**
1864
+ * Publish an externally sourced lifecycle event to local subscribers.
1865
+ *
1866
+ * @param eventName Lifecycle event name.
1867
+ * @param data Event payload.
1868
+ */
1869
+ publishExternal(eventName: AgentLifecycleEventName, data: unknown): void;
1870
+ }
1871
+ /** Scope passed to the local Agent resolver. */
1872
+ interface AgentLifecycleTargetScope {
1873
+ /** Assistant owning the thread. */
1874
+ assistant_id: string;
1875
+ /** Thread that emitted the original event. */
1876
+ thread_id: string;
1877
+ /** Tenant owning the thread. */
1878
+ tenant_id: string;
1879
+ /** Workspace scope, when known. */
1880
+ workspace_id?: string;
1881
+ /** Project scope, when known. */
1882
+ project_id?: string;
1883
+ }
1884
+ /** Dependencies for {@link handleAgentLifecycle}. */
1885
+ interface AgentLifecycleHandlerDeps {
1886
+ /**
1887
+ * Resolves or creates the local Agent instance for a thread.
1888
+ *
1889
+ * Defaults to the process-wide `agentInstanceManager`; tests may inject a
1890
+ * fake to assert delivery without constructing a real Agent.
1891
+ */
1892
+ getAgent?: (scope: AgentLifecycleTargetScope) => AgentLifecycleTarget;
1893
+ }
1894
+ /**
1895
+ * Best-effort publish of one mirrored Agent lifecycle event to the durable
1896
+ * outbox.
1897
+ *
1898
+ * @param outbox Shared outbox store.
1899
+ * @param owner This replica id (recorded as the event origin).
1900
+ * @param event Event scope and payload to mirror.
1901
+ * @returns A promise that resolves once the append is attempted.
1902
+ *
1903
+ * @remarks Never throws: mirroring is best-effort / at-most-once, and a lost
1904
+ * event must not affect the local run.
1905
+ */
1906
+ declare function publishAgentLifecycle(outbox: OutboxStore, owner: string, event: AgentLifecycleTapEvent): Promise<void>;
1907
+ /**
1908
+ * Installs the process-wide Agent lifecycle tap that mirrors selected events
1909
+ * into the shared outbox.
1910
+ *
1911
+ * @param params Outbox plus this replica's id.
1912
+ *
1913
+ * @remarks Only {@link MIRRORED_AGENT_EVENTS} are mirrored. Calling this again
1914
+ * replaces the previous tap.
1915
+ */
1916
+ declare function installAgentLifecycleSync(params: {
1917
+ outbox: OutboxStore;
1918
+ owner: string;
1919
+ }): void;
1920
+ /**
1921
+ * Delivers one foreign outbox payload to the local Agent event bus.
1922
+ *
1923
+ * @param payload Outbox event payload, expected to carry `tenantId`,
1924
+ * `assistantId`, `threadId`, a mirrored `eventName`, and optional scope ids.
1925
+ * @param deps Optional resolver seam (defaults to `agentInstanceManager`).
1926
+ * @returns `true` when the event was delivered; `false` for malformed payloads
1927
+ * or non-mirrored event names.
1928
+ *
1929
+ * @remarks The local Agent is resolved with the mirror's scope so subscribers
1930
+ * attached to that thread receive the event; delivery goes through
1931
+ * {@link AgentLifecycleTarget.publishExternal} and is therefore never mirrored
1932
+ * again.
1933
+ */
1934
+ declare function handleAgentLifecycle(payload: unknown, deps?: AgentLifecycleHandlerDeps): boolean;
1935
+
1500
1936
  /** Outbox topic carrying cross-replica A2A API-key invalidation. */
1501
1937
  declare const A2A_KEY_CHANGED_TOPIC = "a2a-key-changed";
1502
1938
  /**
@@ -1613,6 +2049,799 @@ declare class A2AAuthService {
1613
2049
  private extractToken;
1614
2050
  }
1615
2051
 
2052
+ /** Default concurrent executing runs per worker (`N_JOBS_PER_WORKER`). */
2053
+ declare const DEFAULT_RUN_TASK_JOBS = 10;
2054
+ /** Default poll interval when no wake signal arrives. */
2055
+ declare const DEFAULT_RUN_TASK_SCAN_MS = 500;
2056
+ /** Default run (row) lease duration. */
2057
+ declare const DEFAULT_RUN_TASK_LEASE_TTL_MS = 60000;
2058
+ /** Default thread-run lease duration. */
2059
+ declare const DEFAULT_THREAD_RUN_LEASE_TTL_MS = 60000;
2060
+ /**
2061
+ * Shared execution-slot limiter for the scanning worker and recovery loop.
2062
+ *
2063
+ * @remarks Recovered executions consume the same slots as claimed pending
2064
+ * runs, so `N_JOBS_PER_WORKER` is a per-worker bound across both paths.
2065
+ */
2066
+ declare class RunTaskConcurrencyLimiter {
2067
+ readonly max: number;
2068
+ private active;
2069
+ /** @param max Maximum concurrently executing runs. */
2070
+ constructor(max: number);
2071
+ /** Slots currently in use. */
2072
+ get inUse(): number;
2073
+ /** Slots currently available. */
2074
+ get available(): number;
2075
+ /** Whether at least one slot is free. */
2076
+ hasCapacity(): boolean;
2077
+ /**
2078
+ * Reserves one slot.
2079
+ *
2080
+ * @returns `true` when a slot was reserved, `false` when the bound is full.
2081
+ */
2082
+ tryAcquire(): boolean;
2083
+ /** Releases one reserved/running slot (idempotent at zero). */
2084
+ release(): void;
2085
+ }
2086
+ /** Point-in-time worker posture exposed by {@link RunTaskWorker.stats}. */
2087
+ interface RunTaskWorkerStats {
2088
+ /** Whether the polling loop is running. */
2089
+ running: boolean;
2090
+ /** Executions currently tracked by the worker. */
2091
+ inFlight: number;
2092
+ /** Concurrency slots currently reserved. */
2093
+ activeSlots: number;
2094
+ /** Concurrency slots still available. */
2095
+ availableSlots: number;
2096
+ /** Configured maximum concurrent executions. */
2097
+ maxConcurrentJobs: number;
2098
+ /** Poll interval used when no wake is delivered. */
2099
+ scanIntervalMs: number;
2100
+ /** Run row lease TTL. */
2101
+ leaseTtlMs: number;
2102
+ }
2103
+ /** Structured logger accepted by the worker. */
2104
+ interface RunTaskLogger {
2105
+ info?(message: string, context?: Record<string, unknown>): void;
2106
+ warn(message: string, context?: Record<string, unknown>): void;
2107
+ }
2108
+ /** Minimal Agent surface required to execute one durable run. */
2109
+ interface RunTaskAgent {
2110
+ /** Current checkpoint id at the thread tip, when one exists. */
2111
+ getCurrentCheckpointId(): Promise<string | undefined>;
2112
+ /**
2113
+ * Current checkpoint tip plus the run that produced it.
2114
+ *
2115
+ * @remarks Optional for older fakes; without it the worker cannot prove
2116
+ * ownership and therefore treats the run as having no progress.
2117
+ */
2118
+ getCurrentCheckpointTip?(): Promise<AgentCheckpointTip>;
2119
+ /** Execute the run without entering the legacy message queue. */
2120
+ executeRun(input: ExecuteRunInput): Promise<ExecuteRunResult>;
2121
+ }
2122
+ /** Dependencies accepted by {@link RunTaskWorker}. */
2123
+ interface RunTaskWorkerDependencies {
2124
+ /** Durable run ledger/task queue. */
2125
+ ledger: IAgentRunLedger;
2126
+ /** Shared lease store that provides per-thread execution exclusion. */
2127
+ leaseStore: LeaseStore;
2128
+ /** Resolve the managed Agent for a run's thread identity. */
2129
+ getAgent(params: AgentThreadInterface): RunTaskAgent;
2130
+ /** Resolve the checkpoint rollback store, or `undefined` when unsupported. */
2131
+ resolveRollbackStore?(): CheckpointMaintenance | undefined;
2132
+ /** Stable worker identity; used for both the run row and thread-run lease. */
2133
+ owner: string;
2134
+ /** Maximum concurrent executing runs (default 10). */
2135
+ maxConcurrentJobs?: number;
2136
+ /**
2137
+ * Maximum `running` rows per tenant across the selected sources (default
2138
+ * unlimited).
2139
+ *
2140
+ * @remarks The global `maxConcurrentJobs` limiter still applies. This cap is
2141
+ * per tenant and counts only the worker's selected `sources`; it lets a scan
2142
+ * skip tenants already at their cap (best-effort fairness), not strict
2143
+ * round-robin scheduling.
2144
+ */
2145
+ maxRunningPerTenant?: number;
2146
+ /**
2147
+ * Sources this worker may scan and claim.
2148
+ *
2149
+ * @remarks Defaults to `["run_api"]`. Include `"project_task"` to execute
2150
+ * ProjectTask queue work without importing Project or Room modules here.
2151
+ *
2152
+ * @default ["run_api"]
2153
+ */
2154
+ sources?: readonly AgentRunSource[];
2155
+ /**
2156
+ * Rebuild trusted host-authenticated context for a claimed run.
2157
+ *
2158
+ * @remarks Called once before execution. Returning `undefined` executes
2159
+ * without trusted context. Rejecting settles the run through the normal error
2160
+ * path.
2161
+ */
2162
+ resolveTrustedRunContext?: (run: AgentRunRecord) => Promise<TrustedRunContext | undefined>;
2163
+ /**
2164
+ * Best-effort terminal observer for run-queue work.
2165
+ *
2166
+ * @remarks Invoked once after a run settles terminally, including recovered
2167
+ * runs. Failures are logged and never alter the persisted run status.
2168
+ */
2169
+ onRunTerminal?: (input: {
2170
+ run: AgentRunRecord;
2171
+ status: Exclude<AgentRunStatus, "pending" | "running">;
2172
+ error?: string;
2173
+ }) => Promise<void>;
2174
+ /** Shared limiter; when supplied, `maxConcurrentJobs` is ignored. */
2175
+ limiter?: RunTaskConcurrencyLimiter;
2176
+ /** Poll interval used when no wake is delivered (default 500ms). */
2177
+ scanIntervalMs?: number;
2178
+ /** Run row lease TTL (default 60s). */
2179
+ leaseTtlMs?: number;
2180
+ /** Run lease heartbeat interval (default `leaseTtlMs / 3`, min 100ms). */
2181
+ heartbeatMs?: number;
2182
+ /** Cancellation/rollback flag poll interval (default 1000ms). */
2183
+ cancelPollMs?: number;
2184
+ /** Maximum rollback failures before terminal convergence (default 3). */
2185
+ maxRollbackAttempts?: number;
2186
+ /** Thread-run lease heartbeat interval (default `leaseTtlMs / 3`, min 100ms). */
2187
+ threadHeartbeatMs?: number;
2188
+ /** Optional structured logger. */
2189
+ logger?: RunTaskLogger;
2190
+ /** Clock seam for backoff/tests (default `Date.now`). */
2191
+ now?: () => number;
2192
+ /** Bounded wait for in-flight runs on stop (default 30s). */
2193
+ shutdownTimeoutMs?: number;
2194
+ }
2195
+ type RunTaskAbortReason = "cancel" | "rollback" | "lease" | "shutdown";
2196
+ /** In-flight execution handle shared with the run-task recovery loop. */
2197
+ interface RunTaskExecutionState {
2198
+ readonly controller: AbortController;
2199
+ abortReason?: RunTaskAbortReason;
2200
+ promise: Promise<void>;
2201
+ }
2202
+ /**
2203
+ * Claims and executes durable run-queue pending runs for `sources`.
2204
+ *
2205
+ * @remarks
2206
+ * - The run row and the existing `thread-run` lease share one owner identity.
2207
+ * - A candidate is claimed only after the thread-run lease is acquired; a lost
2208
+ * claim releases the lease and leaves the row untouched.
2209
+ * - Pending work is discovered by a fair tenant-interleaved scan, so one
2210
+ * blocked thread or noisy tenant cannot head-of-line block unrelated runs.
2211
+ * - Redis wake is optional: {@link wake} is best-effort and polling is the
2212
+ * durable fallback.
2213
+ * - Legacy `source='legacy'` rows are never listed or executed here.
2214
+ * - ProjectTask behavior is injected through `resolveTrustedRunContext` and
2215
+ * `onRunTerminal`; this module never imports Project or Room code.
2216
+ */
2217
+ declare class RunTaskWorker {
2218
+ private readonly ledger;
2219
+ private readonly leaseStore;
2220
+ private readonly getAgent;
2221
+ private readonly resolveRollbackStore;
2222
+ private readonly owner;
2223
+ private readonly maxRunningPerTenant?;
2224
+ private readonly sources;
2225
+ private readonly resolveTrustedRunContext?;
2226
+ private readonly onRunTerminal?;
2227
+ private readonly limiter;
2228
+ private readonly scanIntervalMs;
2229
+ private readonly leaseTtlMs;
2230
+ private readonly heartbeatMs;
2231
+ private readonly cancelPollMs;
2232
+ private readonly maxRollbackAttempts;
2233
+ private readonly threadHeartbeatMs;
2234
+ private readonly logger?;
2235
+ private readonly now;
2236
+ private readonly shutdownTimeoutMs;
2237
+ private running;
2238
+ private scanLoop?;
2239
+ private scanAgain;
2240
+ private pollTimer?;
2241
+ private readonly executions;
2242
+ /** @param deps Ledger, leases, Agent resolver, and worker identity/limits. */
2243
+ constructor(deps: RunTaskWorkerDependencies);
2244
+ /**
2245
+ * Returns the worker's current execution posture.
2246
+ *
2247
+ * @returns Running state, in-flight count, and concurrency slots.
2248
+ */
2249
+ stats(): RunTaskWorkerStats;
2250
+ /** Starts the polling loop and an immediate scan. */
2251
+ start(): void;
2252
+ /**
2253
+ * Requests an immediate scan.
2254
+ *
2255
+ * @returns A promise that resolves when the requested scans finish claiming
2256
+ * eligible work. Executions themselves continue in the background.
2257
+ */
2258
+ wake(): Promise<void>;
2259
+ /**
2260
+ * Stops scanning and waits for in-flight runs to settle.
2261
+ *
2262
+ * @param timeoutMs Optional override for the bounded shutdown wait.
2263
+ * @remarks After the deadline the worker aborts remaining executions so they
2264
+ * settle `interrupted` and release their leases before a caller force-exits.
2265
+ */
2266
+ stop(timeoutMs?: number): Promise<void>;
2267
+ /**
2268
+ * Executes a run already claimed/recovered by another component.
2269
+ *
2270
+ * @param run Running record owned by this worker identity.
2271
+ * @param lease Held thread-run lease; released by this method when done.
2272
+ * @param execution Abort state owned by the caller (recovery tracks stop).
2273
+ * @remarks Recovery uses this to run an expired row without requeueing it.
2274
+ */
2275
+ executeRecoveredRun(run: AgentRunRecord, lease: ThreadRunLeaseHandle, execution: RunTaskExecutionState): Promise<void>;
2276
+ private scan;
2277
+ private claimAndStart;
2278
+ private executeClaimed;
2279
+ private buildExecutionRequest;
2280
+ private readCheckpointTip;
2281
+ private startHeartbeat;
2282
+ private heartbeat;
2283
+ private completeRollback;
2284
+ private failRollback;
2285
+ private persistRevision;
2286
+ private settle;
2287
+ private settleOrRollback;
2288
+ private notifyTerminal;
2289
+ private readRun;
2290
+ private warn;
2291
+ }
2292
+
2293
+ /** Default execution attempts before an expired run settles `timeout`. */
2294
+ declare const DEFAULT_RUN_TASK_MAX_ATTEMPTS = 3;
2295
+ /** Point-in-time recovery posture exposed by {@link RunTaskRecovery.stats}. */
2296
+ interface RunTaskRecoveryStats {
2297
+ /** Whether the periodic recovery loop is running. */
2298
+ running: boolean;
2299
+ /** Recovered executions currently in flight. */
2300
+ active: number;
2301
+ /** Configured maximum concurrent recovered executions. */
2302
+ maxConcurrentJobs: number;
2303
+ /** Recovery scan interval. */
2304
+ intervalMs: number;
2305
+ /** Thread-run lease TTL. */
2306
+ leaseTtlMs: number;
2307
+ }
2308
+ /**
2309
+ * Executes a run already reclaimed by recovery while the caller holds the
2310
+ * thread-run lease. The executor owns releasing that lease.
2311
+ */
2312
+ type RunTaskRecoveredExecutor = (run: AgentRunRecord, lease: ThreadRunLeaseHandle, execution: RunTaskExecutionState) => Promise<void>;
2313
+ /** Dependencies accepted by {@link RunTaskRecovery}. */
2314
+ interface RunTaskRecoveryDependencies {
2315
+ /** Durable run ledger/task queue. */
2316
+ ledger: IAgentRunLedger;
2317
+ /** Shared lease store used to serialize thread execution and rollback. */
2318
+ leaseStore: LeaseStore;
2319
+ /** Resolve the checkpoint rollback store, or `undefined` when unsupported. */
2320
+ resolveRollbackStore?(): CheckpointMaintenance | undefined;
2321
+ /**
2322
+ * Execute a recovered run through the normal run-task execution path.
2323
+ *
2324
+ * @remarks Absent on API-only replicas. Without it, recovery only finishes
2325
+ * cancel/rollback work and leaves other expired running rows untouched so
2326
+ * they keep blocking queued runs until a worker replica reclaims them.
2327
+ */
2328
+ executeRun?: RunTaskRecoveredExecutor;
2329
+ /**
2330
+ * Best-effort terminal observer for runs recovery settles directly.
2331
+ *
2332
+ * @remarks Covers cancel, attempts-exhausted timeout, lease-loss and rollback
2333
+ * failure settlements. Recovered executions report through the worker hook.
2334
+ * Failures are logged and never alter the recovery outcome.
2335
+ */
2336
+ onRunTerminal?: (input: {
2337
+ run: AgentRunRecord;
2338
+ status: Exclude<AgentRunStatus, "pending" | "running">;
2339
+ error?: string;
2340
+ }) => Promise<void>;
2341
+ /** Stable worker/recovery identity used for the run and thread-run leases. */
2342
+ owner: string;
2343
+ /**
2344
+ * Shared execution-slot limiter with the scanning worker.
2345
+ *
2346
+ * @remarks Recovered executions consume the same `N_JOBS_PER_WORKER` slots as
2347
+ * pending claims. When omitted, recovery creates its own limiter from
2348
+ * `maxConcurrentJobs`.
2349
+ */
2350
+ limiter?: RunTaskConcurrencyLimiter;
2351
+ /** Recovery-only limiter size when no shared limiter is supplied (default 10). */
2352
+ maxConcurrentJobs?: number;
2353
+ /**
2354
+ * Sources this recovery loop may reap.
2355
+ *
2356
+ * @remarks Defaults to `["run_api"]`. Add `"project_task"` to recover
2357
+ * ProjectTask queue work; executed runs flow through the worker's trusted
2358
+ * context and terminal hooks. This list only scopes {@link reapExpired};
2359
+ * pending rollback work remains `run_api`-only because rollback is not
2360
+ * supported for ProjectTask runs.
2361
+ *
2362
+ * @default ["run_api"]
2363
+ */
2364
+ sources?: readonly AgentRunSource[];
2365
+ /** Attempts allowed before a run settles `timeout` (default 3). */
2366
+ maxAttempts?: number;
2367
+ /** Bounded rollback attempts before terminal ROLLBACK_FAILED (default 3). */
2368
+ maxRollbackAttempts?: number;
2369
+ /** Base backoff between failed rollback attempts (default 1000ms). */
2370
+ rollbackBackoffBaseMs?: number;
2371
+ /** Maximum rollback backoff (default 60000ms). */
2372
+ rollbackBackoffMaxMs?: number;
2373
+ /** Scan interval in milliseconds (default 500). */
2374
+ intervalMs?: number;
2375
+ /** Thread-run lease TTL (default 60s). */
2376
+ leaseTtlMs?: number;
2377
+ /** Thread-run lease heartbeat interval (default `leaseTtlMs / 3`). */
2378
+ threadHeartbeatMs?: number;
2379
+ /** Bounded wait for in-flight recovered executions on stop (default 30s). */
2380
+ shutdownTimeoutMs?: number;
2381
+ /** Optional structured logger. */
2382
+ logger?: RunTaskLogger;
2383
+ /** Clock seam for tests (default `Date.now`). */
2384
+ now?: () => number;
2385
+ }
2386
+ /**
2387
+ * Recovers expired run-queue executions and unfinished rollbacks.
2388
+ *
2389
+ * @remarks
2390
+ * - Only rows whose source is in `sources` (default `["run_api"]`) are
2391
+ * observed; legacy ledger rows are left to the existing supervisor.
2392
+ * - An expired running row is never requeued. Recovery acquires the
2393
+ * `thread-run` lease, atomically reclaims the row with a fresh run lease, and
2394
+ * executes it in place. The row stays `running`, so queued runs on the same
2395
+ * thread remain blocked until it settles.
2396
+ * - Cancel/rollback requests are routed to `interrupted`. Rollback work then
2397
+ * acquires the thread-run lease and completes the checkpoint rewind and row
2398
+ * deletion idempotently.
2399
+ * - A failed checkpoint rewind leaves the run `interrupted` with
2400
+ * `rollbackRequested=true`, and {@link IAgentRunLedger.listRollbackRuns}
2401
+ * makes it retryable on later ticks.
2402
+ * - Exhausted attempts settle `timeout`.
2403
+ */
2404
+ declare class RunTaskRecovery {
2405
+ private readonly ledger;
2406
+ private readonly leaseStore;
2407
+ private readonly resolveRollbackStore;
2408
+ private readonly executeRun?;
2409
+ private readonly onRunTerminal?;
2410
+ private readonly sources;
2411
+ private readonly limiter;
2412
+ private readonly owner;
2413
+ private readonly maxAttempts;
2414
+ private readonly maxRollbackAttempts;
2415
+ private readonly rollbackBackoffBaseMs;
2416
+ private readonly rollbackBackoffMaxMs;
2417
+ private readonly intervalMs;
2418
+ private readonly leaseTtlMs;
2419
+ private readonly threadHeartbeatMs;
2420
+ private readonly shutdownTimeoutMs;
2421
+ private readonly logger?;
2422
+ private readonly now;
2423
+ private running;
2424
+ private timer?;
2425
+ private tickLoop?;
2426
+ private readonly active;
2427
+ /** @param deps Ledger, leases, recovery policy, and identity. */
2428
+ constructor(deps: RunTaskRecoveryDependencies);
2429
+ /**
2430
+ * Returns the recovery loop's current posture.
2431
+ *
2432
+ * @returns Running state, active recovered executions, and bounds.
2433
+ */
2434
+ stats(): RunTaskRecoveryStats;
2435
+ /** Starts periodic recovery scans and an immediate tick. */
2436
+ start(): void;
2437
+ /**
2438
+ * Stops periodic recovery and waits for in-flight executions to settle.
2439
+ *
2440
+ * @param timeoutMs Optional override for the bounded wait.
2441
+ */
2442
+ stop(timeoutMs?: number): Promise<void>;
2443
+ /**
2444
+ * Runs one recovery scan.
2445
+ *
2446
+ * @remarks Single-flight: concurrent calls share the in-progress scan.
2447
+ */
2448
+ tick(): Promise<void>;
2449
+ private runTick;
2450
+ private recoverAndExecute;
2451
+ private recoverRollback;
2452
+ private failRollbackRecovery;
2453
+ private rollbackBackoffFor;
2454
+ private settleExpired;
2455
+ private notifyTerminal;
2456
+ private warn;
2457
+ }
2458
+
2459
+ /**
2460
+ * Best-effort wake surface for the durable run task queue.
2461
+ *
2462
+ * @remarks A wake only nudges the local process; the DB pending scan remains
2463
+ * authoritative, so callers may ignore failures.
2464
+ */
2465
+ interface RunTaskWake {
2466
+ /** Requests an immediate local scan. */
2467
+ wake(): Promise<void>;
2468
+ }
2469
+ /** Point-in-time runtime posture exposed by {@link RunTaskRuntime.stats}. */
2470
+ interface RunTaskRuntimeStats {
2471
+ /** Whether {@link RunTaskRuntime.start} has run without a later stop. */
2472
+ started: boolean;
2473
+ /** Worker posture, absent on API-only replicas. */
2474
+ worker?: RunTaskWorkerStats;
2475
+ /** Recovery posture. */
2476
+ recovery: RunTaskRecoveryStats;
2477
+ }
2478
+ /** Dependencies accepted by {@link RunTaskRuntime}. */
2479
+ interface RunTaskRuntimeDependencies {
2480
+ /** Durable run ledger/task queue. */
2481
+ ledger: IAgentRunLedger;
2482
+ /** Shared lease store for thread-run exclusion. */
2483
+ leaseStore: LeaseStore;
2484
+ /** Resolve the managed Agent for a run's thread identity. */
2485
+ getAgent(params: AgentThreadInterface): RunTaskAgent;
2486
+ /**
2487
+ * Sources the worker and recovery may scan/reap.
2488
+ *
2489
+ * @remarks Defaults to `["run_api"]` in both loops. Include `"project_task"`
2490
+ * to execute ProjectTask queue work; Task 6 enables it at the composition
2491
+ * root after settlement/observer cutover.
2492
+ *
2493
+ * @default ["run_api"]
2494
+ */
2495
+ sources?: readonly AgentRunSource[];
2496
+ /**
2497
+ * Rebuild trusted host-authenticated context for a claimed run.
2498
+ *
2499
+ * @remarks Forwarded to the worker, including runs recovered by the recovery
2500
+ * loop (recovered executions run through the worker's executor).
2501
+ */
2502
+ resolveTrustedRunContext?: RunTaskWorkerDependencies["resolveTrustedRunContext"];
2503
+ /** Resolve the checkpoint rollback store, or `undefined` when unsupported. */
2504
+ resolveRollbackStore?(): CheckpointMaintenance | undefined;
2505
+ /** Stable worker/recovery identity. */
2506
+ owner: string;
2507
+ /**
2508
+ * Start the execution worker (default `true`).
2509
+ *
2510
+ * @remarks Set `false` on API-only replicas. Recovery still starts so
2511
+ * rollback requests left by another replica can be completed.
2512
+ */
2513
+ executeEnabled?: boolean;
2514
+ /** Maximum concurrent executing runs (default 10). */
2515
+ maxConcurrentJobs?: number;
2516
+ /**
2517
+ * Maximum `running` rows per tenant across the selected sources (default
2518
+ * unlimited).
2519
+ *
2520
+ * @remarks Global `maxConcurrentJobs` still bounds this worker. The cap is
2521
+ * per tenant and used by pending scans/claims for best-effort fairness; it is
2522
+ * not strict round-robin scheduling.
2523
+ */
2524
+ maxRunningPerTenant?: number;
2525
+ /**
2526
+ * Best-effort terminal observer shared by the worker and recovery.
2527
+ *
2528
+ * @remarks Invoked once per run that becomes terminal, whether claimed by the
2529
+ * worker or settled directly by recovery. Failures are logged and never alter
2530
+ * run status or recovery outcome.
2531
+ */
2532
+ onRunTerminal?: RunTaskWorkerDependencies["onRunTerminal"];
2533
+ /** Pending-run poll interval (default 500ms). */
2534
+ scanIntervalMs?: number;
2535
+ /** Recovery scan interval (default 500ms). */
2536
+ recoveryIntervalMs?: number;
2537
+ /** Run/thread lease TTL (default 60s). */
2538
+ leaseTtlMs?: number;
2539
+ /** Run lease heartbeat interval. */
2540
+ heartbeatMs?: number;
2541
+ /** Cancellation/rollback flag poll interval (default 1000ms). */
2542
+ cancelPollMs?: number;
2543
+ /** Bounded rollback attempts before terminal ROLLBACK_FAILED. */
2544
+ maxRollbackAttempts?: number;
2545
+ /** Base backoff between failed rollback attempts. */
2546
+ rollbackBackoffBaseMs?: number;
2547
+ /** Maximum rollback backoff. */
2548
+ rollbackBackoffMaxMs?: number;
2549
+ /** Thread-run lease heartbeat interval. */
2550
+ threadHeartbeatMs?: number;
2551
+ /** Execution attempts before an expired run settles `timeout`. */
2552
+ maxAttempts?: number;
2553
+ /** Bounded shutdown wait. */
2554
+ shutdownTimeoutMs?: number;
2555
+ /** Optional structured logger. */
2556
+ logger?: RunTaskLogger;
2557
+ /** Clock seam for tests. */
2558
+ now?: () => number;
2559
+ }
2560
+ /**
2561
+ * Process-level runtime for the durable run task queue.
2562
+ *
2563
+ * @remarks Starts the execution worker (unless this is an API-only replica) and
2564
+ * the recovery loop. Recovery always runs because durable rollback requests can
2565
+ * exist even when this replica never executes agent code.
2566
+ */
2567
+ declare class RunTaskRuntime implements RunTaskWake {
2568
+ /** Execution worker, absent when this replica is API-only. */
2569
+ readonly worker?: RunTaskWorker;
2570
+ /** Expired-run and rollback recovery loop. */
2571
+ readonly recovery: RunTaskRecovery;
2572
+ private started;
2573
+ /** @param deps Ledger, leases, worker/recovery policy, and identity. */
2574
+ constructor(deps: RunTaskRuntimeDependencies);
2575
+ /**
2576
+ * Returns the runtime, worker, and recovery posture.
2577
+ *
2578
+ * @returns Snapshot with worker stats omitted on API-only replicas.
2579
+ */
2580
+ stats(): RunTaskRuntimeStats;
2581
+ /** Starts the worker (when present) and recovery. */
2582
+ start(): void;
2583
+ /** Stops both loops and waits for in-flight work within their bounds. */
2584
+ stop(): Promise<void>;
2585
+ /**
2586
+ * Requests an immediate worker scan.
2587
+ *
2588
+ * @remarks No-op on API-only replicas.
2589
+ */
2590
+ wake(): Promise<void>;
2591
+ }
2592
+
2593
+ /** Single channel shared by every run-task wake publisher and worker. */
2594
+ declare const RUN_TASK_WAKE_CHANNEL = "run-task:wake";
2595
+ /** Small cross-replica wake frame; the durable pending scan remains authority. */
2596
+ interface RunTaskWakeFrame {
2597
+ /** Replica that published the wake, used to skip self-delivery. */
2598
+ source: string;
2599
+ /** Publication timestamp for diagnostics only. */
2600
+ at: string;
2601
+ }
2602
+ /** Payload handler registered on the subscribed Redis channel. */
2603
+ type RunTaskWakeRedisMessageHandler = (message: string, channel: string) => void | Promise<void>;
2604
+ /**
2605
+ * Minimal Redis pub/sub surface used by {@link RedisRunTaskWake}.
2606
+ *
2607
+ * @remarks Deliberately narrower than the `redis` package client so tests can
2608
+ * inject a deterministic fake without a server. The production adapter relies
2609
+ * on node-redis for reconnects and resubscribes; this interface only exposes
2610
+ * the lifecycle events needed for diagnostics and teardown.
2611
+ */
2612
+ interface RunTaskWakeRedisClient {
2613
+ /** Whether the client currently has an open connection. */
2614
+ readonly isOpen: boolean;
2615
+ /** Open the connection (no-op when already open). */
2616
+ connect(): Promise<void>;
2617
+ /** Close the connection; teardown is best-effort. */
2618
+ disconnect(): Promise<void>;
2619
+ /** Publish a payload and return the number of subscribers reached. */
2620
+ publish(channel: string, message: string): Promise<number>;
2621
+ /** Subscribe a handler to a channel. */
2622
+ subscribe(channel: string, listener: RunTaskWakeRedisMessageHandler): Promise<void>;
2623
+ /** Unsubscribe from a channel. */
2624
+ unsubscribe(channel: string): Promise<void>;
2625
+ /** Register a connection lifecycle listener. */
2626
+ on(event: "error" | "end" | "reconnecting" | "ready", listener: (...args: unknown[]) => void): void;
2627
+ /** Abort the socket immediately when supported (optional). */
2628
+ destroy?(): void;
2629
+ }
2630
+ /** Publish side registered with the process-local wake registry. */
2631
+ interface RunTaskWakePublisher {
2632
+ /**
2633
+ * Best-effort cross-replica wake.
2634
+ *
2635
+ * @returns Number of subscriber replicas reached; `0` on transport failure.
2636
+ * @remarks Implementations must never throw: the durable row is already
2637
+ * committed and polling is the fallback.
2638
+ */
2639
+ publish(): Promise<number>;
2640
+ }
2641
+ /** Point-in-time wake transport posture exposed by {@link RedisRunTaskWake.stats}. */
2642
+ interface RunTaskWakeStats {
2643
+ /** Whether the subscriber loop is running. */
2644
+ started: boolean;
2645
+ /** Whether the lazy publisher connection is currently open. */
2646
+ publisherConnected: boolean;
2647
+ /** Whether the subscriber connection is currently open. */
2648
+ subscriberConnected: boolean;
2649
+ }
2650
+ /** Structured logger used for best-effort transport diagnostics. */
2651
+ interface RunTaskWakeLogger {
2652
+ /** Report the first transport problem with context. */
2653
+ warn(message: string, context: Record<string, unknown>): void;
2654
+ /** Report connection recovery after a failure (optional). */
2655
+ info?(message: string, context: Record<string, unknown>): void;
2656
+ }
2657
+ /** Dependencies required by {@link RedisRunTaskWake}. */
2658
+ interface RedisRunTaskWakeDependencies {
2659
+ /** Stable identity of this replica; used to skip self-published frames. */
2660
+ owner: string;
2661
+ /** Wake the local worker; only called by the subscriber path. */
2662
+ wake: () => Promise<void> | void;
2663
+ /** Factory for independent pub/sub connections (publisher is lazy). */
2664
+ clientFactory: () => RunTaskWakeRedisClient;
2665
+ /** Optional structured logger. */
2666
+ logger?: RunTaskWakeLogger;
2667
+ /** Clock seam for the frame timestamp (default `new Date`). */
2668
+ now?: () => Date;
2669
+ /**
2670
+ * Bound for connect/subscribe/publish/teardown operations (default 2000ms).
2671
+ *
2672
+ * @remarks A hung Redis operation must never block gateway startup or
2673
+ * graceful shutdown; polling remains the durable fallback.
2674
+ */
2675
+ operationTimeoutMs?: number;
2676
+ /**
2677
+ * Backoff sequence between background subscription retries.
2678
+ *
2679
+ * @remarks Defaults to 500ms, 2s, 5s, then 10s for every later attempt. Tests
2680
+ * may inject a shorter sequence. Non-positive entries are ignored.
2681
+ */
2682
+ retryDelaysMs?: readonly number[];
2683
+ }
2684
+ /**
2685
+ * Redis pub/sub wake transport for the durable run task queue.
2686
+ *
2687
+ * @remarks
2688
+ * `publish` lazily owns a publisher connection; every worker replica calls
2689
+ * {@link start} to subscribe and wake its local runtime. API-only replicas may
2690
+ * publish without subscribing. {@link start} schedules the subscription in the
2691
+ * background and resolves immediately, so a hung Redis handshake cannot block
2692
+ * gateway startup. {@link stop} bounds every teardown operation. All Redis
2693
+ * failures are swallowed: the durable pending row and the PostgreSQL poll remain
2694
+ * authoritative, so a lost wake only delays work until the next scan.
2695
+ *
2696
+ * The subscriber ignores frames published by this replica. The registry always
2697
+ * wakes the local runtime directly after publishing, so self-delivery would
2698
+ * only duplicate the same coalesced scan.
2699
+ */
2700
+ declare class RedisRunTaskWake implements RunTaskWakePublisher {
2701
+ private readonly owner;
2702
+ private readonly wake;
2703
+ private readonly clientFactory;
2704
+ private readonly logger?;
2705
+ private readonly now;
2706
+ private readonly operationTimeoutMs;
2707
+ private readonly retryDelaysMs;
2708
+ private subscriber?;
2709
+ private subscriberEpoch;
2710
+ private retryAttempt;
2711
+ private retryTimer?;
2712
+ private publisher?;
2713
+ private publisherPromise?;
2714
+ private pendingPublisherClient?;
2715
+ private publisherEpoch;
2716
+ private readonly connectionErrorsReported;
2717
+ private running;
2718
+ /** @param dependencies Replica identity, local wake, and transport factory. */
2719
+ constructor(dependencies: RedisRunTaskWakeDependencies);
2720
+ /**
2721
+ * Returns the wake transport's current connection posture.
2722
+ *
2723
+ * @returns Started flag plus publisher/subscriber connection state.
2724
+ */
2725
+ stats(): RunTaskWakeStats;
2726
+ /**
2727
+ * Publish one wake frame for every worker replica.
2728
+ *
2729
+ * @returns Number of Redis subscribers reached, or `0` on failure.
2730
+ * @remarks Never throws. A failed publisher connection is reset so the next
2731
+ * create can reconnect.
2732
+ */
2733
+ publish(): Promise<number>;
2734
+ /**
2735
+ * Schedule subscription to the shared wake channel.
2736
+ *
2737
+ * @returns A promise that resolves immediately; connection work continues in
2738
+ * the background.
2739
+ * @remarks Idempotent while a subscription is active. A call during a retry
2740
+ * backoff cancels the timer and retries immediately. Connection failures are
2741
+ * retried automatically with capped exponential backoff; startup and request
2742
+ * paths never wait on Redis. Polling remains the durable fallback.
2743
+ */
2744
+ start(): Promise<void>;
2745
+ /**
2746
+ * Stop the subscriber and close the transport connections.
2747
+ *
2748
+ * @remarks Idempotent and bounded, so a hung Redis handshake cannot block
2749
+ * graceful shutdown. Pending clients are aborted/destroyed when supported and
2750
+ * every teardown operation has a timeout.
2751
+ */
2752
+ stop(): Promise<void>;
2753
+ private frame;
2754
+ private launchConnect;
2755
+ private connectSubscriber;
2756
+ private scheduleRetry;
2757
+ private clearRetryTimer;
2758
+ private bindSubscriberLifecycle;
2759
+ private getPublisher;
2760
+ private onMessage;
2761
+ private reportConnectionFailure;
2762
+ private reportConnectionRecovered;
2763
+ private closeClient;
2764
+ private warn;
2765
+ }
2766
+ /** Options for {@link createRedisRunTaskWake}. */
2767
+ interface CreateRedisRunTaskWakeOptions extends Omit<RedisRunTaskWakeDependencies, "clientFactory"> {
2768
+ /** Redis connection URL, for example `redis://localhost:6379`. */
2769
+ url: string;
2770
+ }
2771
+ /**
2772
+ * Create a wake transport backed by real `redis` pub/sub connections.
2773
+ *
2774
+ * @param options Redis URL, replica identity, and local wake callback.
2775
+ * @returns A transport ready for {@link RedisRunTaskWake.publish} / `start`.
2776
+ */
2777
+ declare function createRedisRunTaskWake(options: CreateRedisRunTaskWakeOptions): RedisRunTaskWake;
2778
+ /**
2779
+ * Create the transport only when a Redis URL is configured.
2780
+ *
2781
+ * @param options Same as {@link createRedisRunTaskWake}, with an optional URL.
2782
+ * @returns The transport, or `undefined` when Redis wake is not configured.
2783
+ * @remarks Without Redis the registry still wakes the local worker, and the
2784
+ * durable PostgreSQL scan remains the fallback for every other replica.
2785
+ */
2786
+ declare function createConfiguredRedisRunTaskWake(options: Omit<CreateRedisRunTaskWakeOptions, "url"> & {
2787
+ url?: string;
2788
+ }): RedisRunTaskWake | undefined;
2789
+
2790
+ /** Structural source of wake transport posture for process metrics. */
2791
+ interface RunTaskWakeStatsSource {
2792
+ /** Returns a point-in-time wake posture snapshot. */
2793
+ stats(): RunTaskWakeStats;
2794
+ }
2795
+ /**
2796
+ * Registers the process-local run task runtime.
2797
+ *
2798
+ * @param runtime The running runtime, or `undefined` on shutdown.
2799
+ * @remarks The registry is intentionally process-local: it can only wake the
2800
+ * worker in this process. Other replicas still discover pending runs through DB
2801
+ * polling, which remains authoritative.
2802
+ */
2803
+ declare function setRunTaskRuntime(runtime?: RunTaskRuntime): void;
2804
+ /**
2805
+ * Returns the process-local run task runtime, when this replica runs one.
2806
+ *
2807
+ * @returns The registered runtime, or `undefined`.
2808
+ */
2809
+ declare function getRunTaskRuntime(): RunTaskRuntime | undefined;
2810
+ /**
2811
+ * Registers the best-effort cross-replica wake publisher.
2812
+ *
2813
+ * @param next The Redis-backed publisher, or `undefined` on shutdown.
2814
+ * @remarks API-only replicas may register a publisher without subscribing. A
2815
+ * missing publisher leaves the process-local wake (and DB polling) intact.
2816
+ */
2817
+ declare function setRunTaskWakePublisher(next?: RunTaskWakePublisher): void;
2818
+ /**
2819
+ * Registers the process-local wake transport used for metrics.
2820
+ *
2821
+ * @param next The wake transport, or `undefined` on shutdown.
2822
+ * @remarks Kept separate from the publish registry so API-only replicas can
2823
+ * expose publisher connection posture even without a local worker.
2824
+ */
2825
+ declare function setRunTaskWakeStatsSource(next?: RunTaskWakeStatsSource): void;
2826
+ /**
2827
+ * Returns the process-local wake transport registered for metrics.
2828
+ *
2829
+ * @returns The wake stats source, or `undefined` when no Redis wake exists.
2830
+ */
2831
+ declare function getRunTaskWakeStatsSource(): RunTaskWakeStatsSource | undefined;
2832
+ /**
2833
+ * Best-effort wake for newly created pending runs.
2834
+ *
2835
+ * @returns A promise that resolves after the local wake attempt; the Redis
2836
+ * publish continues in the background.
2837
+ * @remarks Never authoritative and never throws. The local runtime is woken
2838
+ * first so a hung or failed publisher cannot suppress it. The cross-replica
2839
+ * publish is fire-and-forget and wakes other worker replicas; this replica does
2840
+ * not depend on round-tripping its own frame. A missing runtime, missing
2841
+ * publisher, or transport failure leaves the durable run to polling.
2842
+ */
2843
+ declare function wakeRunTaskQueue(): Promise<void>;
2844
+
1616
2845
  /**
1617
2846
  * Logger configuration for gateway
1618
2847
  */
@@ -1627,10 +2856,6 @@ interface GatewayLoggerConfig {
1627
2856
  }
1628
2857
  interface LatticeGatewayConfig {
1629
2858
  port?: number;
1630
- queueServiceConfig?: {
1631
- type: QueueServiceType;
1632
- defaultStartPollingQueue: boolean;
1633
- };
1634
2859
  loggerConfig?: Partial<GatewayLoggerConfig>;
1635
2860
  /**
1636
2861
  * Install SIGTERM/SIGINT graceful-shutdown handlers (default `true`). On a
@@ -1642,6 +2867,8 @@ interface LatticeGatewayConfig {
1642
2867
  gracefulShutdown?: boolean;
1643
2868
  /** Force-exit deadline for graceful shutdown in ms (default 10_000). */
1644
2869
  gracefulShutdownTimeoutMs?: number;
2870
+ /** Optional host startup/shutdown hooks. */
2871
+ lifespans?: GatewayLifespan | GatewayLifespan[];
1645
2872
  }
1646
2873
  declare const LatticeGateway: {
1647
2874
  startAsHttpEndpoint: (config?: LatticeGatewayConfig) => Promise<void>;
@@ -1650,7 +2877,6 @@ declare const LatticeGateway: {
1650
2877
  app: fastify.FastifyInstance<http.Server<typeof http.IncomingMessage, typeof http.ServerResponse>, http.IncomingMessage, http.ServerResponse<http.IncomingMessage>, fastify.FastifyBaseLogger, fastify.FastifyTypeProviderDefault> & PromiseLike<fastify.FastifyInstance<http.Server<typeof http.IncomingMessage, typeof http.ServerResponse>, http.IncomingMessage, http.ServerResponse<http.IncomingMessage>, fastify.FastifyBaseLogger, fastify.FastifyTypeProviderDefault>> & {
1651
2878
  __linterBrands: "SafePromiseLike";
1652
2879
  };
1653
- AgentTaskConsumer: typeof AgentTaskConsumer;
1654
2880
  };
1655
2881
 
1656
- export { A2AAuthService, A2A_KEY_CHANGED_TOPIC, type GatewayLoggerConfig, LatticeGateway, type LatticeGatewayConfig, MODEL_PROVIDER_CHANGED_TOPIC, ModelProviderService, type ModelProviderServiceDeps, type OrphanRecoveryRuntime, type OrphanRecoveryRuntimeDeps, ProjectRoomEventBroker, ROOM_EVENT_TOPIC, type RecoverTaskDeps, type RecoverTaskResult, createOrphanRecoveryRuntime, createRecoveryCallerDeps, createTaskRunLiveness, handleA2AKeyChanged, handleModelProviderChanged, ingestRoomEvent, parseEnvKeys, publishA2AKeyChanged, publishModelProviderChanged, publishRoomEvent, recoverTask, recoverTopCaller, resolveTopCaller, runOrphanRecovery };
2882
+ export { A2AAuthService, A2A_KEY_CHANGED_TOPIC, AGENT_LIFECYCLE_TOPIC, type AgentLifecycleHandlerDeps, type AgentLifecycleTarget, type AgentLifecycleTargetScope, ChunkLogCorruptionError, type CreateRedisRunTaskWakeOptions, DEFAULT_RUN_TASK_JOBS, DEFAULT_RUN_TASK_LEASE_TTL_MS, DEFAULT_RUN_TASK_MAX_ATTEMPTS, DEFAULT_RUN_TASK_SCAN_MS, DEFAULT_THREAD_RUN_LEASE_TTL_MS, type GatewayLifespan, type GatewayLoggerConfig, LatticeGateway, type LatticeGatewayConfig, type LifespanLogger, MIRRORED_AGENT_EVENTS, MODEL_PROVIDER_CHANGED_TOPIC, ModelProviderService, type ModelProviderServiceDeps, type OrphanRecoveryRuntime, type OrphanRecoveryRuntimeDeps, ProjectRoomEventBroker, ROOM_EVENT_TOPIC, RUN_TASK_WAKE_CHANNEL, type RecoverTaskDeps, type RecoverTaskResult, type RedisAppendLogHandle, type RedisAppendLogLogger, type RedisAppendLogOptions, RedisRunTaskWake, type RedisRunTaskWakeDependencies, type RunTaskAgent, RunTaskConcurrencyLimiter, type RunTaskLogger, RunTaskRecovery, type RunTaskRecoveryDependencies, type RunTaskRecoveryStats, RunTaskRuntime, type RunTaskRuntimeDependencies, type RunTaskRuntimeStats, type RunTaskWake, type RunTaskWakeFrame, type RunTaskWakeLogger, type RunTaskWakePublisher, type RunTaskWakeRedisClient, type RunTaskWakeRedisMessageHandler, type RunTaskWakeStats, type RunTaskWakeStatsSource, RunTaskWorker, type RunTaskWorkerDependencies, type RunTaskWorkerStats, createConfiguredRedisRunTaskWake, createOrphanRecoveryRuntime, createRecoveryCallerDeps, createRedisAppendLog, createRedisRunTaskWake, createTaskRunLiveness, getRunTaskRuntime, getRunTaskWakeStatsSource, handleA2AKeyChanged, handleAgentLifecycle, handleModelProviderChanged, ingestRoomEvent, installAgentLifecycleSync, installLifespanHooks, parseEnvKeys, publishA2AKeyChanged, publishAgentLifecycle, publishModelProviderChanged, publishRoomEvent, recoverTask, recoverTopCaller, resolveTopCaller, runOrphanRecovery, setRunTaskRuntime, setRunTaskWakePublisher, setRunTaskWakeStatsSource, wakeRunTaskQueue };