@axiom-lattice/gateway 6.0.1 → 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.
- package/README.md +0 -1
- package/dist/a2a-standard-DP53K7RC.mjs +3416 -0
- package/dist/a2a-standard-DP53K7RC.mjs.map +1 -0
- package/dist/{chunk-YKTXW6F6.mjs → chunk-2SR4F3ZD.mjs} +65 -1
- package/dist/chunk-2SR4F3ZD.mjs.map +1 -0
- package/dist/{chunk-QUT2DS4R.mjs → chunk-J6B2VFJ7.mjs} +17 -4
- package/dist/chunk-J6B2VFJ7.mjs.map +1 -0
- package/dist/embed/embed-page-AgentDetailSideApp-KNVDO3FZ.js +1 -0
- package/dist/embed/embed-page-AgentGovernanceWorkbench-QSKWVADP.js +1 -0
- package/dist/embed/embed-page-MessageGraph-VGXX4OWU.js +6 -0
- package/dist/embed/embed-page-ReactChart-PIISVBLU.js +1 -0
- package/dist/embed/{embed-page-ReactInfographic-5P36JLSX.js → embed-page-ReactInfographic-677NVKOU.js} +1 -1
- package/dist/embed/embed-page-SkillFlow-JLI5MEZE.js +1 -0
- package/dist/embed/{embed-page-TasksSideApp-DA4LUTMD.js → embed-page-TasksSideApp-D7FFG2OC.js} +1 -1
- package/dist/embed/embed-page-ThreadMaintenancePanel-FSQVVIIG.js +1 -0
- package/dist/embed/{embed-page-TopologyRuntimeView-NFBHAICN.js → embed-page-TopologyRuntimeView-IS6HEO4F.js} +1 -1
- package/dist/embed/{embed-page-WorkflowAutomationView-43YGTXGF.js → embed-page-WorkflowAutomationView-IDVSQTJB.js} +1 -1
- package/dist/embed/embed-page-all-JEPF6WHD.js +1 -0
- package/dist/embed/{embed-page-architectureDiagram-VXUJARFQ-THUN2PFL.js → embed-page-architectureDiagram-VXUJARFQ-TYJ3NO2K.js} +1 -1
- package/dist/embed/embed-page-aspnet-GIV4ESY6.js +1 -0
- package/dist/embed/{embed-page-blockDiagram-VD42YOAC-K7OX6LIO.js → embed-page-blockDiagram-VD42YOAC-L4J3TZWR.js} +1 -1
- package/dist/embed/{embed-page-c4Diagram-YG6GDRKO-DJDAF5AO.js → embed-page-c4Diagram-YG6GDRKO-CP2IXU3Z.js} +1 -1
- package/dist/embed/{embed-page-chunk-FD2PRLMW.js → embed-page-chunk-2VJ7T35I.js} +1 -1
- package/dist/embed/embed-page-chunk-37LGEEJU.js +21 -0
- package/dist/embed/embed-page-chunk-436REIWY.js +1 -0
- package/dist/embed/embed-page-chunk-4W34QOXT.js +517 -0
- package/dist/embed/{embed-page-chunk-JUZPOR5Y.js → embed-page-chunk-5WWOKLDB.js} +1 -1
- package/dist/embed/{embed-page-chunk-HYPA2PHA.js → embed-page-chunk-6IIMW7OG.js} +1 -1
- package/dist/embed/{embed-page-chunk-XKGF5D6L.js → embed-page-chunk-7CJDHJEH.js} +1 -1
- package/dist/embed/{embed-page-chunk-J5AHS4PO.js → embed-page-chunk-7GLQRV3J.js} +1 -1
- package/dist/embed/{embed-page-chunk-SQ4GANYE.js → embed-page-chunk-AEDAOTEO.js} +1 -1
- package/dist/embed/embed-page-chunk-BANVRQT7.js +322 -0
- package/dist/embed/{embed-page-chunk-C4ZV4IXK.js → embed-page-chunk-BOEX26H7.js} +2 -2
- package/dist/embed/{embed-page-chunk-A2AHDN6K.js → embed-page-chunk-DQJVWZOB.js} +1 -1
- package/dist/embed/embed-page-chunk-EYYKNTX7.js +8 -0
- package/dist/embed/{embed-page-chunk-DTRFJLVZ.js → embed-page-chunk-F5QVZLK5.js} +1 -1
- package/dist/embed/{embed-page-chunk-GKHIPXZ2.js → embed-page-chunk-FEMHLB6I.js} +1 -1
- package/dist/embed/{embed-page-chunk-UHH575CK.js → embed-page-chunk-JFMBLJXY.js} +1 -1
- package/dist/embed/{embed-page-chunk-G2RO6IBN.js → embed-page-chunk-KXVXKWED.js} +1 -1
- package/dist/embed/embed-page-chunk-N66H4DNH.js +6657 -0
- package/dist/embed/{embed-page-chunk-WTOAQDQN.js → embed-page-chunk-NFMLKSPM.js} +1 -1
- package/dist/embed/{embed-page-chunk-PLNJRORR.js → embed-page-chunk-NVFNRWRN.js} +1 -1
- package/dist/embed/{embed-page-chunk-V2GBFWLG.js → embed-page-chunk-S2KVN3RB.js} +1 -1
- package/dist/embed/{embed-page-chunk-QULNM5O2.js → embed-page-chunk-SRMYHWL3.js} +1 -1
- package/dist/embed/{embed-page-chunk-D5GP7FHH.js → embed-page-chunk-TVVAPR7I.js} +1 -1
- package/dist/embed/{embed-page-chunk-WFUFTRWA.js → embed-page-chunk-WK77HIEN.js} +1 -1
- package/dist/embed/{embed-page-chunk-XSKNS3LY.js → embed-page-chunk-WWXRO4ZL.js} +1 -1
- package/dist/embed/{embed-page-chunk-BVSC3MSO.js → embed-page-chunk-XFV3N2LD.js} +1 -1
- package/dist/embed/{embed-page-chunk-VY6MAPHR.js → embed-page-chunk-XSKDYUNQ.js} +1 -1
- package/dist/embed/{embed-page-chunk-V7R2Y72J.js → embed-page-chunk-YJUCDIYK.js} +1 -1
- package/dist/embed/embed-page-chunk-ZQQFWEWO.js +1 -0
- package/dist/embed/embed-page-classDiagram-2ON5EDUG-MHNSVPAN.js +1 -0
- package/dist/embed/embed-page-classDiagram-v2-WZHVMYZB-QCMEVMYH.js +1 -0
- package/dist/embed/{embed-page-code-highlighter-IPU5ZNPJ.js → embed-page-code-highlighter-66CSPTFK.js} +1 -1
- package/dist/embed/embed-page-cshtml-RYMZMLKB.js +1 -0
- package/dist/embed/{embed-page-dagre-6UL2VRFP-UQFZMKYW.js → embed-page-dagre-6UL2VRFP-LTZ2DVVO.js} +1 -1
- package/dist/embed/{embed-page-diagram-PSM6KHXK-4IKXWNA4.js → embed-page-diagram-PSM6KHXK-JHYXSMCB.js} +1 -1
- package/dist/embed/{embed-page-diagram-QEK2KX5R-245T32UF.js → embed-page-diagram-QEK2KX5R-2YGDFFTU.js} +1 -1
- package/dist/embed/{embed-page-diagram-S2PKOQOG-BOA5MJA5.js → embed-page-diagram-S2PKOQOG-6EW6MDJW.js} +1 -1
- package/dist/embed/{embed-page-en-WB2EPS63.js → embed-page-en-2SWQHPAC.js} +1 -1
- package/dist/embed/embed-page-en-3Z4N5KXB.js +1 -0
- package/dist/embed/embed-page-en-7IHJFS63.js +1 -0
- package/dist/embed/embed-page-en-7NF4GCC2.js +1 -0
- package/dist/embed/embed-page-en-BLF5THT6.js +1 -0
- package/dist/embed/embed-page-en-ETUCVCSC.js +1 -0
- package/dist/embed/{embed-page-en-UWBIHR6I.js → embed-page-en-H4P63IAK.js} +1 -1
- package/dist/embed/embed-page-en-WUFULCUS.js +1 -0
- package/dist/embed/{embed-page-en-S4MXFPIU.js → embed-page-en-X6B6RVNL.js} +1 -1
- package/dist/embed/embed-page-en-XUMHP5CL.js +1 -0
- package/dist/embed/{embed-page-erDiagram-Q2GNP2WA-S4J4SQPR.js → embed-page-erDiagram-Q2GNP2WA-KSUS6MHW.js} +1 -1
- package/dist/embed/{embed-page-flowDiagram-NV44I4VS-ZFIJXKMW.js → embed-page-flowDiagram-NV44I4VS-AYXRDKAJ.js} +1 -1
- package/dist/embed/{embed-page-ganttDiagram-JELNMOA3-7MSIEWL4.js → embed-page-ganttDiagram-JELNMOA3-AV7N2N3T.js} +1 -1
- package/dist/embed/{embed-page-gitGraphDiagram-NY62KEGX-F6KE6PZK.js → embed-page-gitGraphDiagram-NY62KEGX-IBVOXVO5.js} +1 -1
- package/dist/embed/embed-page-infoDiagram-WHAUD3N6-ZULKOSOZ.js +2 -0
- package/dist/embed/{embed-page-journeyDiagram-XKPGCS4Q-6VTW6GQ3.js → embed-page-journeyDiagram-XKPGCS4Q-3GILI7Y4.js} +1 -1
- package/dist/embed/{embed-page-kanban-definition-3W4ZIXB7-7IZYCLQZ.js → embed-page-kanban-definition-3W4ZIXB7-UGO3J7IH.js} +1 -1
- package/dist/embed/{embed-page-mermaid-R7ZBUE3D.js → embed-page-mermaid-KEBBKHQH.js} +2 -2
- package/dist/embed/{embed-page-mindmap-definition-VGOIOE7T-GKTOT34A.js → embed-page-mindmap-definition-VGOIOE7T-VXXZJS3Z.js} +1 -1
- package/dist/embed/{embed-page-pieDiagram-ADFJNKIX-VDPALO42.js → embed-page-pieDiagram-ADFJNKIX-ZFAWYA2N.js} +1 -1
- package/dist/embed/{embed-page-requirementDiagram-UZGBJVZJ-LTZH6RTD.js → embed-page-requirementDiagram-UZGBJVZJ-2QBINXR7.js} +1 -1
- package/dist/embed/{embed-page-sequenceDiagram-WL72ISMW-LV6Q4SHW.js → embed-page-sequenceDiagram-WL72ISMW-3MGTESG4.js} +1 -1
- package/dist/embed/{embed-page-stateDiagram-FKZM4ZOC-U4NHQDNP.js → embed-page-stateDiagram-FKZM4ZOC-W6VPIMB5.js} +1 -1
- package/dist/embed/embed-page-stateDiagram-v2-4FDKWEC3-QYJ47QBY.js +1 -0
- package/dist/embed/{embed-page-tsx-PNW7N5T4.js → embed-page-tsx-LGDDERM2.js} +1 -1
- package/dist/embed/{embed-page-xychartDiagram-PRI3JC2R-VZSIXBFK.js → embed-page-xychartDiagram-PRI3JC2R-7XKT4R7V.js} +1 -1
- package/dist/embed/embed-page-zh-D3IC6BGA.js +1 -0
- package/dist/embed/{embed-page-zh-SSMSF5LE.js → embed-page-zh-EQ5X2M32.js} +1 -1
- package/dist/embed/embed-page-zh-G75LDQU4.js +1 -0
- package/dist/embed/embed-page-zh-GXE63PQ7.js +1 -0
- package/dist/embed/{embed-page-zh-ACZNJ4X4.js → embed-page-zh-IB3422D4.js} +1 -1
- package/dist/embed/embed-page-zh-KJXKOUIY.js +1 -0
- package/dist/embed/{embed-page-zh-NKHP3CFU.js → embed-page-zh-N2STRBUG.js} +1 -1
- package/dist/embed/{embed-page-zh-XEYNKCTC.js → embed-page-zh-PRMID3YQ.js} +1 -1
- package/dist/embed/embed-page-zh-RMGMUSDH.js +1 -0
- package/dist/embed/embed-page-zh-RMSS6PXD.js +1 -0
- package/dist/embed/embed-page.js +1 -1
- package/dist/index.d.mts +1513 -252
- package/dist/index.d.ts +1513 -252
- package/dist/index.js +17568 -5813
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +31824 -22070
- package/dist/index.mjs.map +1 -1
- package/dist/{open-MDC4VCR7.mjs → open-NTH7AGD3.mjs} +41 -78
- package/dist/open-NTH7AGD3.mjs.map +1 -0
- package/dist/{open-exposure-OX5NKWDG.mjs → open-exposure-AGNG4MC3.mjs} +13 -3
- package/dist/{open-exposure-OX5NKWDG.mjs.map → open-exposure-AGNG4MC3.mjs.map} +1 -1
- package/dist/{open-files-FS5AN6Q6.mjs → open-files-V3R2BFTV.mjs} +4 -4
- package/package.json +9 -8
- package/dist/a2a-standard-3OZFXDCW.mjs +0 -1413
- package/dist/a2a-standard-3OZFXDCW.mjs.map +0 -1
- package/dist/chunk-K32XEQLH.mjs +0 -28
- package/dist/chunk-K32XEQLH.mjs.map +0 -1
- package/dist/chunk-QUT2DS4R.mjs.map +0 -1
- package/dist/chunk-YKTXW6F6.mjs.map +0 -1
- package/dist/embed/embed-page-AgentDetailSideApp-JYVZKSUU.js +0 -1
- package/dist/embed/embed-page-AgentGovernanceWorkbench-SD2EHW2B.js +0 -1
- package/dist/embed/embed-page-MessageGraph-GLQXKAVC.js +0 -6
- package/dist/embed/embed-page-ReactChart-74MHCE2K.js +0 -1
- package/dist/embed/embed-page-SkillFlow-NDAK3GAD.js +0 -1
- package/dist/embed/embed-page-TeamWorkspace-DUB3L6V6.js +0 -748
- package/dist/embed/embed-page-TeamWorkspace-YQFWXDLI.css +0 -1
- package/dist/embed/embed-page-all-NEJVJ4G7.js +0 -1
- package/dist/embed/embed-page-aspnet-SRK67AH2.js +0 -1
- package/dist/embed/embed-page-chunk-2PFKLMTR.js +0 -21
- package/dist/embed/embed-page-chunk-DWIBLGBW.js +0 -2
- package/dist/embed/embed-page-chunk-ESQ5V77Q.js +0 -1
- package/dist/embed/embed-page-chunk-MHGVD4EV.js +0 -525
- package/dist/embed/embed-page-chunk-P477ZTUO.js +0 -7009
- package/dist/embed/embed-page-classDiagram-2ON5EDUG-VYHHMIKZ.js +0 -1
- package/dist/embed/embed-page-classDiagram-v2-WZHVMYZB-5AISLXUF.js +0 -1
- package/dist/embed/embed-page-cshtml-ZY7LJSTH.js +0 -1
- package/dist/embed/embed-page-en-2WLCQIB6.js +0 -1
- package/dist/embed/embed-page-en-7WXWAWGG.js +0 -1
- package/dist/embed/embed-page-en-ICVL5QAC.js +0 -1
- package/dist/embed/embed-page-en-OLZHSUC3.js +0 -1
- package/dist/embed/embed-page-infoDiagram-WHAUD3N6-FQMKXERB.js +0 -2
- package/dist/embed/embed-page-stateDiagram-v2-4FDKWEC3-G3EWQCOW.js +0 -1
- package/dist/embed/embed-page-zh-EYLHUD5G.js +0 -1
- package/dist/embed/embed-page-zh-FPYCTKQK.js +0 -1
- package/dist/embed/embed-page-zh-OPUVZZSQ.js +0 -1
- package/dist/mcpTransportRegistry-VXUVJKBC.mjs +0 -14
- package/dist/mcpTransportRegistry-VXUVJKBC.mjs.map +0 -1
- package/dist/open-MDC4VCR7.mjs.map +0 -1
- /package/dist/embed/{embed-page-chunk-34FVRFPW.js → embed-page-chunk-UH47IEAR.js} +0 -0
- /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,
|
|
3
|
-
import { TrustedAgentMessageDispatcher, TrustedMessageOptions, TaskLifecycleListener, TaskLifecycleService,
|
|
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,10 +167,31 @@ 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
|
+
/**
|
|
178
|
+
* 流式中途刷新的节流间隔与上限。
|
|
179
|
+
*
|
|
180
|
+
* 企业微信单会话限制 30 条/分钟(含占位、收尾和卡片)。2500ms 约 24 次
|
|
181
|
+
* 刷新/分钟,再加最多 3 条生命周期消息仍留有余量。
|
|
182
|
+
*/
|
|
162
183
|
private static readonly STREAM_CHUNK_INTERVAL_MS;
|
|
163
184
|
private static readonly MAX_STREAM_CHUNKS;
|
|
185
|
+
/** 流式回复的硬超时:企微要求 10 分钟内 `finish=true`,留 2 分钟余量。 */
|
|
186
|
+
private static readonly STREAM_TIMEOUT_MS;
|
|
187
|
+
/**
|
|
188
|
+
* `MESSAGE_COMPLETED` 后等待 `reply:ready` 的宽限。
|
|
189
|
+
*
|
|
190
|
+
* 正常完成通常由 `reply:ready` 用权威全文收尾;但最终 AI 消息为空时
|
|
191
|
+
* (例如模型只调用了 `wecom_card`、没有产出文字)不会触发 `reply:ready`,
|
|
192
|
+
* 这里兜底自行收尾,避免流挂住、卡片丢失。
|
|
193
|
+
*/
|
|
194
|
+
private static readonly COMPLETION_GRACE_MS;
|
|
164
195
|
/**
|
|
165
196
|
* 消费 agent 的逐块输出,把**中途**内容推给支持流式的渠道适配器。
|
|
166
197
|
*
|
|
@@ -211,6 +242,23 @@ interface ModelProviderServiceDeps {
|
|
|
211
242
|
has(tenantId: string, modelKey: string): boolean;
|
|
212
243
|
remove(tenantId: string, modelKey: string): boolean;
|
|
213
244
|
listRegistered(tenantId: string): RegisteredModel[];
|
|
245
|
+
/**
|
|
246
|
+
* Drop this replica's cached clients for one provider, plus the tenant's
|
|
247
|
+
* resolved default.
|
|
248
|
+
*
|
|
249
|
+
* The registry's positive entries have no TTL — once a key is resolved the
|
|
250
|
+
* store is never read again — so a changed `extra`, apiKey or baseURL is
|
|
251
|
+
* otherwise invisible until the process restarts. Evicting is enough:
|
|
252
|
+
* resolution re-materialises the row from the shared store, which is how a
|
|
253
|
+
* replica that never saw the change already works.
|
|
254
|
+
*
|
|
255
|
+
* Optional so existing constructions keep working, but a caller that omits
|
|
256
|
+
* it silently loses change propagation.
|
|
257
|
+
*
|
|
258
|
+
* @param providerId clears that `${providerId}/` prefix; omit to clear only
|
|
259
|
+
* the tenant default.
|
|
260
|
+
*/
|
|
261
|
+
evictProviderCache?(tenantId: string, providerId?: string): void;
|
|
214
262
|
/**
|
|
215
263
|
* Drop the registry's cached "this tenant has no default model" answer.
|
|
216
264
|
*
|
|
@@ -315,14 +363,17 @@ declare class ModelProviderService {
|
|
|
315
363
|
*/
|
|
316
364
|
setModels(userId: string | undefined, tenantId: string, id: string, selection: ModelSelection[]): Promise<ModelProviderPublicEntry>;
|
|
317
365
|
/**
|
|
318
|
-
* Give a tenant's sole model the default flag.
|
|
366
|
+
* Give a tenant's sole **chat** model the default flag.
|
|
367
|
+
*
|
|
368
|
+
* A tenant with exactly one runnable chat model has an obvious default, and
|
|
369
|
+
* saying so is what keeps the invariant the rest of the feature leans on: a
|
|
370
|
+
* tenant either has no chat model at all — and falls back to the deployment's
|
|
371
|
+
* default — or has a default, which cannot be deleted. Without this a tenant
|
|
372
|
+
* could sit with one model and no default, and deleting that model would
|
|
373
|
+
* leave nothing behind.
|
|
319
374
|
*
|
|
320
|
-
*
|
|
321
|
-
*
|
|
322
|
-
* either has no models at all — and falls back to the deployment's default —
|
|
323
|
-
* or has a default, which cannot be deleted. Without this a tenant could sit
|
|
324
|
-
* with one model and no default, and deleting that model would leave nothing
|
|
325
|
-
* behind.
|
|
375
|
+
* Chat only: a bare `"default"` key resolves to a chat client, so an
|
|
376
|
+
* embedding or ASR row must never be marked as one.
|
|
326
377
|
*
|
|
327
378
|
* Deliberately narrow: more than one model is the tenant's choice to make,
|
|
328
379
|
* and a disabled model is not a candidate for "the model this tenant runs".
|
|
@@ -690,6 +741,16 @@ declare class ProjectTaskThreadService {
|
|
|
690
741
|
private readonly deps;
|
|
691
742
|
/** @param deps Thread persistence and unique-ID generation dependencies. */
|
|
692
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>;
|
|
693
754
|
/**
|
|
694
755
|
* Ensures a canonical Task execution Thread for the requested ownership mode.
|
|
695
756
|
*
|
|
@@ -701,60 +762,470 @@ declare class ProjectTaskThreadService {
|
|
|
701
762
|
private assertThread;
|
|
702
763
|
}
|
|
703
764
|
|
|
704
|
-
/**
|
|
705
|
-
type
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
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 {
|
|
712
837
|
task: TaskItem;
|
|
713
838
|
thread: Thread;
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
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 {
|
|
717
892
|
task: TaskItem;
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
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. */
|
|
725
1014
|
task: TaskItem;
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
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. */
|
|
730
1030
|
task: TaskItem;
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
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 {
|
|
734
1221
|
kind: "deferred";
|
|
735
1222
|
task: TaskItem;
|
|
736
1223
|
thread: Thread;
|
|
737
1224
|
messageId: string;
|
|
738
|
-
|
|
739
|
-
/** Stable classification of the queue fact used to recover an in-progress Project Task. */
|
|
740
|
-
type ProjectTaskRecoveryClassification = {
|
|
741
|
-
kind: "pending" | "processing";
|
|
742
|
-
inputMessageId: string;
|
|
743
|
-
} | {
|
|
744
|
-
kind: "missing";
|
|
745
|
-
} | {
|
|
746
|
-
kind: "invalid";
|
|
747
|
-
reason: string;
|
|
748
|
-
};
|
|
749
|
-
/** Runtime bounds for Project Task settlement. */
|
|
750
|
-
interface ProjectTaskExecutionOptions {
|
|
751
|
-
/** Maximum time to retain Agent listeners while awaiting execution settlement. */
|
|
752
|
-
settlementTimeoutMs: number;
|
|
753
|
-
/** Maximum time to await lifecycle persistence after a failed or inconsistent execution. */
|
|
754
|
-
lifecycleSettlementTimeoutMs: number;
|
|
755
|
-
/** Queue message id factory; defaults to `randomUUID`. */
|
|
756
|
-
idFactory?: () => string;
|
|
1225
|
+
runId: string;
|
|
757
1226
|
}
|
|
1227
|
+
/** Durable run-queue port used by the Project Task dispatch path. */
|
|
1228
|
+
type ProjectTaskRunServicePort = Pick<ProjectTaskRunService, "start" | "resume" | "dispatch" | "dispatchDetached" | "getActiveRun" | "getLatestRun">;
|
|
758
1229
|
/**
|
|
759
1230
|
* Optional resume command threaded into the queued trusted run message.
|
|
760
1231
|
*
|
|
@@ -775,92 +1246,61 @@ interface ProjectTaskResumeOptions {
|
|
|
775
1246
|
*/
|
|
776
1247
|
expectedStatus?: TaskItem["status"];
|
|
777
1248
|
}
|
|
778
|
-
/** Collaborators required to
|
|
1249
|
+
/** Collaborators required to dispatch Project Task executions. */
|
|
779
1250
|
interface ProjectTaskExecutionDependencies {
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
threads: ProjectTaskThreadService;
|
|
785
|
-
lifecycle: TaskLifecycleService;
|
|
786
|
-
dispatcher: TrustedAgentMessageDispatcher;
|
|
787
|
-
queue: IMessageQueueStore;
|
|
788
|
-
agentFor: (task: TaskItem, thread: Thread) => Agent;
|
|
789
|
-
/** JIT-materializes a public-project task owner so it has a room anchor to post/reply. */
|
|
790
|
-
ensurePublicOwner?: (input: {
|
|
791
|
-
tenantId: string;
|
|
792
|
-
projectId: string;
|
|
793
|
-
assistantId: string;
|
|
794
|
-
}) => Promise<unknown>;
|
|
795
|
-
/**
|
|
796
|
-
* Optional gateway hook: a task thread paused with a durable interruption.
|
|
797
|
-
*
|
|
798
|
-
* @remarks Best-effort and fire-and-forget: it never blocks or alters
|
|
799
|
-
* settlement. It may fire more than once for the same interrupt (the
|
|
800
|
-
* `message:interrupted` event path and the recovery path), so consumers must
|
|
801
|
-
* dedupe using the correlation key derived from `messageId`.
|
|
802
|
-
*/
|
|
803
|
-
onTaskInterrupted?: (input: {
|
|
804
|
-
task: TaskItem;
|
|
805
|
-
thread: Thread;
|
|
806
|
-
messageId: string;
|
|
807
|
-
state: unknown;
|
|
808
|
-
}) => void | Promise<void>;
|
|
809
|
-
}
|
|
810
|
-
/** Input for recovering exactly one persisted Project Task queue execution. */
|
|
811
|
-
interface RecoverProjectTaskExecutionInput {
|
|
812
|
-
task: TaskItem;
|
|
813
|
-
thread: Thread;
|
|
814
|
-
inputMessageId: string;
|
|
815
|
-
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;
|
|
816
1255
|
}
|
|
817
1256
|
/**
|
|
818
|
-
*
|
|
1257
|
+
* Dispatches Project Tasks on the durable run queue.
|
|
819
1258
|
*
|
|
820
|
-
*
|
|
821
|
-
*
|
|
822
|
-
*
|
|
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.
|
|
823
1264
|
*/
|
|
824
1265
|
declare class ProjectTaskExecutionService {
|
|
825
1266
|
private readonly deps;
|
|
826
|
-
private readonly options;
|
|
827
|
-
private readonly runBridge;
|
|
828
1267
|
/**
|
|
829
|
-
* @param deps
|
|
830
|
-
* @param options Settlement watchdog bounds.
|
|
1268
|
+
* @param deps Canonical Thread reads and the durable run-queue port.
|
|
831
1269
|
*/
|
|
832
|
-
constructor(deps: ProjectTaskExecutionDependencies
|
|
833
|
-
/**
|
|
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. */
|
|
834
1281
|
start(input: {
|
|
835
1282
|
tenantId: string;
|
|
836
1283
|
taskId: string;
|
|
837
1284
|
actor?: string;
|
|
838
1285
|
}, signal?: AbortSignal): Promise<ProjectTaskRunOutcome>;
|
|
839
|
-
/**
|
|
840
|
-
private prepareStart;
|
|
841
|
-
/** Resumes an interrupted Project Task on its canonical existing Thread. */
|
|
1286
|
+
/** Resumes an interrupted Project Task and creates its continuation run. */
|
|
842
1287
|
resume(input: {
|
|
843
1288
|
tenantId: string;
|
|
844
1289
|
taskId: string;
|
|
845
1290
|
actor?: string;
|
|
846
1291
|
}, signal?: AbortSignal, options?: ProjectTaskResumeOptions): Promise<ProjectTaskRunOutcome>;
|
|
847
|
-
/** Clears the durable interruption and loads the exact resumed task. */
|
|
848
|
-
private prepareResume;
|
|
849
1292
|
/**
|
|
850
1293
|
* Dispatches a Project Task by selecting the correct pre-run transition for
|
|
851
|
-
* its current status, then
|
|
1294
|
+
* its current status, then committing one run.
|
|
852
1295
|
*
|
|
853
1296
|
* @param input Tenant, task identity, and optional command actor.
|
|
854
|
-
* @param signal Optional cancellation signal
|
|
855
|
-
* @param options Optional resume command carried into the queued run
|
|
856
|
-
* @returns The
|
|
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.
|
|
857
1300
|
* @remarks `start` semantics: make this task run. `pending` starts,
|
|
858
1301
|
* `interrupted` resumes, `failed` retries, and `completed`/`cancelled`
|
|
859
1302
|
* reopen, each followed by a trusted run dispatch. `in_progress` is a
|
|
860
|
-
* conflict.
|
|
861
|
-
* effect; the kernel only records the status change. `actor` attributes the
|
|
862
|
-
* start/resume/retry/reopen transition and defaults to the system actor.
|
|
863
|
-
* `options.command` is only carried on an interrupted-task resume.
|
|
1303
|
+
* conflict. `options.command` is only carried on an interrupted-task resume.
|
|
864
1304
|
*/
|
|
865
1305
|
dispatch(input: {
|
|
866
1306
|
tenantId: string;
|
|
@@ -868,17 +1308,12 @@ declare class ProjectTaskExecutionService {
|
|
|
868
1308
|
actor?: string;
|
|
869
1309
|
}, signal?: AbortSignal, options?: ProjectTaskResumeOptions): Promise<ProjectTaskRunOutcome>;
|
|
870
1310
|
/**
|
|
871
|
-
* Dispatches a Project Task without awaiting
|
|
1311
|
+
* Dispatches a Project Task without awaiting execution.
|
|
872
1312
|
*
|
|
873
1313
|
* @param input Tenant, task identity, and optional command actor.
|
|
874
|
-
* @param signal Optional cancellation signal
|
|
875
|
-
* @param options Optional
|
|
1314
|
+
* @param signal Optional cancellation signal checked before dispatch.
|
|
1315
|
+
* @param options Optional expected-status guard and HITL resume command.
|
|
876
1316
|
* @returns The `in_progress` task snapshot and whether the run was queued.
|
|
877
|
-
* @remarks Intended for command HTTP handlers that must release the Project
|
|
878
|
-
* mutation mutex immediately: the armed settlement keeps running in the
|
|
879
|
-
* background (completion, watchdog, and convergence) but the caller never
|
|
880
|
-
* awaits it. Enqueue failure converges the task exactly like the awaited
|
|
881
|
-
* path before throwing, so no phantom run is left behind.
|
|
882
1317
|
*/
|
|
883
1318
|
dispatchDetached(input: {
|
|
884
1319
|
tenantId: string;
|
|
@@ -888,79 +1323,9 @@ declare class ProjectTaskExecutionService {
|
|
|
888
1323
|
task: TaskItem;
|
|
889
1324
|
queued: boolean;
|
|
890
1325
|
}>;
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
/** Classifies one exact Task Thread queue fact with transition-safe repeated reads. */
|
|
894
|
-
classifyRecovery(task: TaskItem, thread: Thread): Promise<ProjectTaskRecoveryClassification>;
|
|
895
|
-
private requireTask;
|
|
896
|
-
private validateReadyTask;
|
|
897
|
-
private validateResumableTask;
|
|
898
|
-
/**
|
|
899
|
-
* Retries a failed task or reopens a terminal task, then runs it on its
|
|
900
|
-
* canonical existing Thread.
|
|
901
|
-
*
|
|
902
|
-
* @param task Task snapshot already routed to the restartable branch.
|
|
903
|
-
* @param actor Actor attributed to the retry/reopen transition.
|
|
904
|
-
* @param signal Optional cancellation signal for the armed settlement.
|
|
905
|
-
* @returns The settled run outcome.
|
|
906
|
-
*/
|
|
907
|
-
private dispatchRestartable;
|
|
908
|
-
/** Retries a failed task or reopens a terminal task, then loads the exact current task. */
|
|
909
|
-
private prepareRestartable;
|
|
910
|
-
private validateRestartableTask;
|
|
911
|
-
private validateReadyTaskSnapshot;
|
|
912
|
-
private requireRoom;
|
|
913
|
-
private requireMembership;
|
|
914
|
-
private requireCurrentThread;
|
|
915
|
-
private dispatchRun;
|
|
916
|
-
/**
|
|
917
|
-
* Arms the exact settlement and enqueues the trusted run without awaiting settlement.
|
|
918
|
-
*
|
|
919
|
-
* @returns The armed settlement plus the enqueue result. A declined or thrown
|
|
920
|
-
* enqueue yields `queued: false` with a stable reason; the caller decides
|
|
921
|
-
* whether to converge only (awaited path) or converge and throw (detached).
|
|
922
|
-
*/
|
|
923
|
-
private armAndEnqueue;
|
|
924
|
-
/**
|
|
925
|
-
* Arms and enqueues a run, then returns an `in_progress` snapshot immediately.
|
|
926
|
-
*
|
|
927
|
-
* @remarks The settlement continues in the background. On enqueue failure the
|
|
928
|
-
* task is converged exactly like the awaited path before throwing, so no
|
|
929
|
-
* phantom in-progress run is left behind.
|
|
930
|
-
*/
|
|
931
|
-
private enqueueDetached;
|
|
932
|
-
private claimDispatchFailure;
|
|
933
|
-
private armSettlement;
|
|
1326
|
+
private dispatchRunQueue;
|
|
1327
|
+
private toDetachedOptions;
|
|
934
1328
|
private throwIfCancelled;
|
|
935
|
-
private awaitSettlementCancellation;
|
|
936
|
-
private cancelSettlement;
|
|
937
|
-
private compensateCancellation;
|
|
938
|
-
private reloadTask;
|
|
939
|
-
private settleInterruptedEvent;
|
|
940
|
-
/**
|
|
941
|
-
* Fire-and-forget best-effort projection of a durable interruption.
|
|
942
|
-
*
|
|
943
|
-
* @remarks Never awaited and never allowed to affect settlement. The state is
|
|
944
|
-
* resolved lazily so a missing/undefined event state only triggers a thread
|
|
945
|
-
* read when the hook is actually wired.
|
|
946
|
-
*/
|
|
947
|
-
private emitInterrupted;
|
|
948
|
-
/** Reads the paused thread snapshot when no interrupt event payload is available (recovery). */
|
|
949
|
-
private readInterruptedThreadState;
|
|
950
|
-
/**
|
|
951
|
-
* Persists a durable `missing_input` interruption for a paused clarify question.
|
|
952
|
-
*
|
|
953
|
-
* @remarks The task must survive the pause so the room can answer it; the
|
|
954
|
-
* agent resumes the same run once the answer arrives.
|
|
955
|
-
*/
|
|
956
|
-
private interruptForClarify;
|
|
957
|
-
private settleEndedRecovery;
|
|
958
|
-
private terminalOutcome;
|
|
959
|
-
private matchesExecution;
|
|
960
|
-
private failSettledTaskAwaited;
|
|
961
|
-
private withLifecycleTimeout;
|
|
962
|
-
private failSettledTask;
|
|
963
|
-
private validateExistingExecution;
|
|
964
1329
|
}
|
|
965
1330
|
|
|
966
1331
|
/** Aggregate result of one bounded deterministic startup reconciliation. */
|
|
@@ -1096,64 +1461,85 @@ declare const defaultSwaggerUiConfig: {
|
|
|
1096
1461
|
transformStaticCSP: (header: string) => string;
|
|
1097
1462
|
};
|
|
1098
1463
|
|
|
1099
|
-
|
|
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
|
+
*/
|
|
1100
1498
|
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
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>;
|
|
1112
1517
|
}
|
|
1113
1518
|
/**
|
|
1114
|
-
*
|
|
1115
|
-
*
|
|
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.
|
|
1116
1523
|
*/
|
|
1117
|
-
declare class
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
private activeTasks;
|
|
1123
|
-
private processing;
|
|
1124
|
-
private immediateProcessingEnabled;
|
|
1125
|
-
gatewayPort: number;
|
|
1126
|
-
static agent_run_endpoint: string;
|
|
1127
|
-
constructor(gatewayPort: number, pollingIntervalMs?: number);
|
|
1128
|
-
/**
|
|
1129
|
-
* 初始化事件监听和队列轮询
|
|
1130
|
-
*/
|
|
1131
|
-
private initialize;
|
|
1132
|
-
/**
|
|
1133
|
-
* 启动队列轮询
|
|
1134
|
-
*/
|
|
1135
|
-
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";
|
|
1136
1529
|
/**
|
|
1137
|
-
*
|
|
1530
|
+
* @param cursor - Cursor of the offending stream entry
|
|
1531
|
+
* @param reason - Why the entry could not be decoded
|
|
1138
1532
|
*/
|
|
1139
|
-
|
|
1140
|
-
/**
|
|
1141
|
-
* 处理单个任务并在完成后立即尝试处理下一个
|
|
1142
|
-
*/
|
|
1143
|
-
processNextTask(): Promise<boolean>;
|
|
1144
|
-
/**
|
|
1145
|
-
* 检查队列中是否有任务并处理
|
|
1146
|
-
*/
|
|
1147
|
-
checkQueueForTasks(): Promise<void>;
|
|
1148
|
-
/**
|
|
1149
|
-
* 从队列中消费任务
|
|
1150
|
-
*/
|
|
1151
|
-
consumeFromQueue(): Promise<void>;
|
|
1152
|
-
/**
|
|
1153
|
-
* 处理通过事件触发的任务
|
|
1154
|
-
*/
|
|
1155
|
-
trigger_agent_task(taskRequest: AgentTaskRequest): void;
|
|
1533
|
+
constructor(cursor: string, reason: string);
|
|
1156
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>;
|
|
1157
1543
|
|
|
1158
1544
|
/**
|
|
1159
1545
|
* Builds a cross-replica task-liveness probe from the registered `"lease"` store.
|
|
@@ -1462,6 +1848,91 @@ declare function publishRoomEvent(outbox: OutboxStore, owner: string, scope: Pro
|
|
|
1462
1848
|
*/
|
|
1463
1849
|
declare function ingestRoomEvent(payload: unknown, broker: ProjectRoomEventBroker): boolean;
|
|
1464
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
|
+
|
|
1465
1936
|
/** Outbox topic carrying cross-replica A2A API-key invalidation. */
|
|
1466
1937
|
declare const A2A_KEY_CHANGED_TOPIC = "a2a-key-changed";
|
|
1467
1938
|
/**
|
|
@@ -1578,6 +2049,799 @@ declare class A2AAuthService {
|
|
|
1578
2049
|
private extractToken;
|
|
1579
2050
|
}
|
|
1580
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
|
+
|
|
1581
2845
|
/**
|
|
1582
2846
|
* Logger configuration for gateway
|
|
1583
2847
|
*/
|
|
@@ -1592,10 +2856,6 @@ interface GatewayLoggerConfig {
|
|
|
1592
2856
|
}
|
|
1593
2857
|
interface LatticeGatewayConfig {
|
|
1594
2858
|
port?: number;
|
|
1595
|
-
queueServiceConfig?: {
|
|
1596
|
-
type: QueueServiceType;
|
|
1597
|
-
defaultStartPollingQueue: boolean;
|
|
1598
|
-
};
|
|
1599
2859
|
loggerConfig?: Partial<GatewayLoggerConfig>;
|
|
1600
2860
|
/**
|
|
1601
2861
|
* Install SIGTERM/SIGINT graceful-shutdown handlers (default `true`). On a
|
|
@@ -1607,6 +2867,8 @@ interface LatticeGatewayConfig {
|
|
|
1607
2867
|
gracefulShutdown?: boolean;
|
|
1608
2868
|
/** Force-exit deadline for graceful shutdown in ms (default 10_000). */
|
|
1609
2869
|
gracefulShutdownTimeoutMs?: number;
|
|
2870
|
+
/** Optional host startup/shutdown hooks. */
|
|
2871
|
+
lifespans?: GatewayLifespan | GatewayLifespan[];
|
|
1610
2872
|
}
|
|
1611
2873
|
declare const LatticeGateway: {
|
|
1612
2874
|
startAsHttpEndpoint: (config?: LatticeGatewayConfig) => Promise<void>;
|
|
@@ -1615,7 +2877,6 @@ declare const LatticeGateway: {
|
|
|
1615
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>> & {
|
|
1616
2878
|
__linterBrands: "SafePromiseLike";
|
|
1617
2879
|
};
|
|
1618
|
-
AgentTaskConsumer: typeof AgentTaskConsumer;
|
|
1619
2880
|
};
|
|
1620
2881
|
|
|
1621
|
-
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 };
|