@axiom-lattice/gateway 4.6.1 → 4.8.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/dist/OrphanRecoveryRuntime-BEFILJMU.mjs +8 -0
- package/dist/RecoveryCallerDeps-6SJUYJ5S.mjs +8 -0
- package/dist/RecoveryCallerDeps-6SJUYJ5S.mjs.map +1 -0
- package/dist/{a2a-standard-PIOKEHPS.mjs → a2a-standard-PBYPNHOA.mjs} +59 -21
- package/dist/a2a-standard-PBYPNHOA.mjs.map +1 -0
- package/dist/{chunk-2POW27EI.mjs → chunk-7CG57LEF.mjs} +2 -2
- package/dist/{chunk-WKNZ7GBC.mjs → chunk-EXPERMZR.mjs} +1 -1
- package/dist/chunk-EXPERMZR.mjs.map +1 -0
- package/dist/{chunk-52SUWMOX.mjs → chunk-JUA3VJE7.mjs} +3 -2
- package/dist/chunk-JUA3VJE7.mjs.map +1 -0
- package/dist/chunk-NOXGKYUV.mjs +37 -0
- package/dist/chunk-NOXGKYUV.mjs.map +1 -0
- package/dist/{chunk-526UYVNQ.mjs → chunk-NPHK4SZ6.mjs} +4 -4
- package/dist/{chunk-TJAUXMYJ.mjs → chunk-URXZPI2F.mjs} +8 -1
- package/dist/chunk-URXZPI2F.mjs.map +1 -0
- package/dist/chunk-ZBTG3T7K.mjs +324 -0
- package/dist/chunk-ZBTG3T7K.mjs.map +1 -0
- package/dist/embed/{embed-page-AgentDetailSideApp-7NYLPLAX.js → embed-page-AgentDetailSideApp-NTLAFO5C.js} +1 -1
- package/dist/embed/{embed-page-AgentGovernanceWorkbench-LNGNSI7X.js → embed-page-AgentGovernanceWorkbench-QPVBOMLH.js} +1 -1
- package/dist/embed/{embed-page-MessageGraph-E6DZDT65.js → embed-page-MessageGraph-JAJGMYKH.js} +1 -1
- package/dist/embed/{embed-page-ReactChart-3VE7F5VJ.js → embed-page-ReactChart-WUG6TEAZ.js} +1 -1
- package/dist/embed/{embed-page-SkillFlow-XMMHEHQL.js → embed-page-SkillFlow-TSBZ4V2P.js} +1 -1
- package/dist/embed/{embed-page-TasksSideApp-QM5TZ7EG.js → embed-page-TasksSideApp-TJI22YGD.js} +1 -1
- package/dist/embed/{embed-page-TeamWorkspace-E7RFY3ZY.js → embed-page-TeamWorkspace-OJJLN5VI.js} +1 -1
- package/dist/embed/{embed-page-TopologyRuntimeView-DLPDUB7Z.js → embed-page-TopologyRuntimeView-ZFU4N6TV.js} +1 -1
- package/dist/embed/{embed-page-WorkflowAutomationView-FKSVOHHB.js → embed-page-WorkflowAutomationView-ALXV7VQQ.js} +1 -1
- package/dist/embed/{embed-page-chunk-I7PWCKAG.js → embed-page-chunk-DZQBVJ7Z.js} +1 -1
- package/dist/embed/{embed-page-chunk-WWTU55RX.js → embed-page-chunk-IPIJ2SG6.js} +1 -1
- package/dist/embed/{embed-page-chunk-KN7D66KL.js → embed-page-chunk-N22EGQAJ.js} +1 -1
- package/dist/embed/{embed-page-chunk-EXCJ42IB.js → embed-page-chunk-O72XBKQH.js} +2 -2
- package/dist/embed/{embed-page-chunk-U276WBDI.js → embed-page-chunk-QMVIL4BJ.js} +190 -185
- package/dist/embed/{embed-page-chunk-H6TK3FAR.js → embed-page-chunk-UT3WYBZH.js} +1 -1
- package/dist/embed/embed-page-chunk-YPNSOZYW.js +8 -0
- package/dist/embed/embed-page.js +1 -1
- package/dist/index.d.mts +362 -10
- package/dist/index.d.ts +362 -10
- package/dist/index.js +1693 -450
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +714 -154
- package/dist/index.mjs.map +1 -1
- package/dist/{open-AMEMJOFJ.mjs → open-UVKMONVE.mjs} +10 -9
- package/dist/open-UVKMONVE.mjs.map +1 -0
- package/dist/{open-exposure-I44Q4M3T.mjs → open-exposure-VU227IO3.mjs} +5 -5
- package/dist/{open-files-EJ6O2SGX.mjs → open-files-X3HXVEEM.mjs} +6 -6
- package/dist/{open-session-LBTCU3AE.mjs → open-session-2Y7XJJW2.mjs} +6 -6
- package/dist/{resources-DXPXAXEZ.mjs → resources-2L3KSUUU.mjs} +2 -2
- package/dist/sandboxOpenCredentialProvider-USIU7Z7H.mjs +275 -0
- package/dist/sandboxOpenCredentialProvider-USIU7Z7H.mjs.map +1 -0
- package/dist/{sandboxProvider-I3P66VXT.mjs → sandboxProvider-DUYWHYJN.mjs} +2 -2
- package/dist/sandboxProvider-DUYWHYJN.mjs.map +1 -0
- package/package.json +14 -8
- package/dist/a2a-standard-PIOKEHPS.mjs.map +0 -1
- package/dist/chunk-52SUWMOX.mjs.map +0 -1
- package/dist/chunk-TJAUXMYJ.mjs.map +0 -1
- package/dist/chunk-WKNZ7GBC.mjs.map +0 -1
- package/dist/embed/embed-page-chunk-HEMNMC4X.js +0 -7
- package/dist/open-AMEMJOFJ.mjs.map +0 -1
- /package/dist/{sandboxProvider-I3P66VXT.mjs.map → OrphanRecoveryRuntime-BEFILJMU.mjs.map} +0 -0
- /package/dist/{chunk-2POW27EI.mjs.map → chunk-7CG57LEF.mjs.map} +0 -0
- /package/dist/{chunk-526UYVNQ.mjs.map → chunk-NPHK4SZ6.mjs.map} +0 -0
- /package/dist/{open-exposure-I44Q4M3T.mjs.map → open-exposure-VU227IO3.mjs.map} +0 -0
- /package/dist/{open-files-EJ6O2SGX.mjs.map → open-files-X3HXVEEM.mjs.map} +0 -0
- /package/dist/{open-session-LBTCU3AE.mjs.map → open-session-2Y7XJJW2.mjs.map} +0 -0
- /package/dist/{resources-DXPXAXEZ.mjs.map → resources-2L3KSUUU.mjs.map} +0 -0
package/dist/embed/embed-page.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{b as f}from"./embed-page-chunk-
|
|
1
|
+
import{b as f}from"./embed-page-chunk-O72XBKQH.js";import"./embed-page-chunk-QMVIL4BJ.js";import"./embed-page-chunk-YPNSOZYW.js";import{a as b}from"./embed-page-chunk-MB65DZAX.js";import"./embed-page-chunk-VW3NF5NT.js";import"./embed-page-chunk-5JDQ456X.js";import"./embed-page-chunk-Z7USDLHI.js";import{a as g}from"./embed-page-chunk-O4QK5LWZ.js";import"./embed-page-chunk-4BKXE5ON.js";import"./embed-page-chunk-IX4WLWCY.js";import"./embed-page-chunk-EUOOO2LS.js";import{b as U,e as s}from"./embed-page-chunk-6Y3UYJ6Z.js";var h=U(d=>{"use strict";var E=g();d.createRoot=E.createRoot,d.hydrateRoot=E.hydrateRoot;var ee});var T=s(h()),k=s(g());var y="axiom-web-app-embed";var x=["axiom:hello","axiom:identity","axiom:send","axiom:config"];function w(e,t){return{source:y,version:1,type:e,payload:t}}var N=6,z=40,B=500;function H(e){if(typeof e!="object"||e===null||Array.isArray(e))return!1;let t=e,n=Object.keys(t);return n.length!==2||!n.includes("label")||!n.includes("text")?!1:typeof t.label=="string"&&t.label.length>=1&&t.label.length<=z&&typeof t.text=="string"&&t.text.length>=1&&t.text.length<=B}function Q(e){if(!Array.isArray(e))return[];let t=[];for(let n of e){if(t.length>=N)break;H(n)&&t.push({label:n.label,text:n.text})}return t}function _(e){if(typeof e!="object"||e===null)return null;let t=e,n={};if(typeof t.welcome=="string"&&t.welcome.length>0&&(n.welcome=t.welcome),Array.isArray(t.quicks)){let r=Q(t.quicks);(r.length>0||t.quicks.length===0)&&(n.quicks=r)}return n.welcome===void 0&&n.quicks===void 0?null:n}function R(e,t){if(typeof e!="object"||e===null)return null;let n=e;return n.source!==y||n.version!==1||typeof n.type!="string"||!t.includes(n.type)?null:{type:n.type,payload:n.payload}}var p=s(b()),P=/^[A-Za-z0-9._:@-]{1,128}$/,q="Open this chat from the host page embed to start chatting.",v="axiom-embed-root",C="axiom-embed-style",A=32768,G=`
|
|
2
2
|
html, body, #${v} { height: 100%; margin: 0; }
|
|
3
3
|
.axiom-embed-fill { height: 100% !important; min-height: 0 !important; }
|
|
4
4
|
.axiom-embed-card { display: flex; align-items: center; justify-content: center; height: 100%; margin: 0; font-family: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; font-size: 14px; color: #64748b; padding: 24px; box-sizing: border-box; text-align: center; }
|
package/dist/index.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as http from 'http';
|
|
2
|
-
import { ProjectRoomMessage, ProjectBotMembership, ProjectMembership, TaskItem, ChannelAdapter, InboundMessage, Binding, BindingRegistry, ChannelInstallationStore, DispatchResult, ProjectRoomEventScope, ProjectRoomBusinessEventDraft, ProjectRoomScopedBusinessEvent, ProjectRoomBusinessEvent, ProjectRoomEventSubscription, TaskStore, ProjectRoomStore, ProjectBotMembershipStore, ThreadStore, ProjectRoom, Thread, ProjectStore, LoggerType, PinoFileOptions } from '@axiom-lattice/protocols';
|
|
3
|
-
import { TrustedAgentMessageDispatcher, TrustedMessageOptions, TaskLifecycleListener, TaskLifecycleService, IMessageQueueStore, Agent } from '@axiom-lattice/core';
|
|
2
|
+
import { ProjectRoomMessage, ProjectBotMembership, ProjectMembership, TaskItem, ChannelAdapter, InboundMessage, Binding, BindingRegistry, ChannelInstallationStore, DispatchResult, ProjectRoomEventScope, ProjectRoomBusinessEventDraft, ProjectRoomScopedBusinessEvent, ProjectRoomBusinessEvent, ProjectRoomEventSubscription, TaskStore, ProjectRoomStore, ProjectBotMembershipStore, ThreadStore, ProjectRoom, Thread, ProjectStore, TaskWorkItemStore, TenantStore, OutboxStore, LoggerType, PinoFileOptions } from '@axiom-lattice/protocols';
|
|
3
|
+
import { TrustedAgentMessageDispatcher, TrustedMessageOptions, TaskLifecycleListener, TaskLifecycleService, IMessageQueueStore, Agent, TaskRunRegistry } from '@axiom-lattice/core';
|
|
4
4
|
import * as fastify from 'fastify';
|
|
5
5
|
|
|
6
6
|
/** Public publisher surface for committed Project Room business facts. */
|
|
@@ -177,6 +177,7 @@ interface ProjectRoomEventBrokerOptions {
|
|
|
177
177
|
maxGlobalEvents?: number;
|
|
178
178
|
maxGlobalBytes?: number;
|
|
179
179
|
initialSequence?: number;
|
|
180
|
+
sourceKeyLimit?: number;
|
|
180
181
|
log?: {
|
|
181
182
|
warn(message: string, context: Record<string, unknown>): void;
|
|
182
183
|
};
|
|
@@ -195,22 +196,53 @@ declare class ProjectRoomEventBroker {
|
|
|
195
196
|
private readonly maxRooms;
|
|
196
197
|
private readonly maxGlobalEvents;
|
|
197
198
|
private readonly maxGlobalBytes;
|
|
199
|
+
private readonly sourceKeyLimit;
|
|
198
200
|
private readonly log;
|
|
199
201
|
private readonly rooms;
|
|
200
202
|
private readonly sequenceScopes;
|
|
203
|
+
private readonly seenSourceKeys;
|
|
201
204
|
private sequence;
|
|
202
205
|
private eventCount;
|
|
203
206
|
private byteCount;
|
|
204
207
|
private subscriberCount;
|
|
205
208
|
private usageClock;
|
|
206
209
|
private closed;
|
|
210
|
+
private broadcast;
|
|
207
211
|
constructor(options?: ProjectRoomEventBrokerOptions);
|
|
208
212
|
/** Number of currently registered subscriptions across all rooms. */
|
|
209
213
|
get activeSubscriberCount(): number;
|
|
210
214
|
/** Exact UTF-8 byte count of all retained serialized scoped events. */
|
|
211
215
|
get retainedBytes(): number;
|
|
212
|
-
/**
|
|
213
|
-
|
|
216
|
+
/**
|
|
217
|
+
* Installs (or clears) a hook invoked after a locally published event is stored
|
|
218
|
+
* and queued to local subscribers. Used to mirror events to other replicas via
|
|
219
|
+
* a shared transport. Best-effort: a throwing hook never affects `publish`.
|
|
220
|
+
*/
|
|
221
|
+
setBroadcastHook(hook: ((scope: ProjectRoomEventScope, serializedEvent: string, sourceKey?: string) => void) | undefined): void;
|
|
222
|
+
/**
|
|
223
|
+
* Atomically stores an immutable event before queueing detached listener callbacks.
|
|
224
|
+
*
|
|
225
|
+
* @param sourceKey Optional caller-owned fact key; when present it is retained
|
|
226
|
+
* for bounded cross-replica duplicate suppression so a later foreign copy of the
|
|
227
|
+
* same fact (or a local publish) is dropped by {@link ingestForeignEvent}.
|
|
228
|
+
*/
|
|
229
|
+
publish(scope: ProjectRoomEventScope, draft: ProjectRoomBusinessEventDraft, sourceKey?: string): ProjectRoomScopedBusinessEvent;
|
|
230
|
+
/**
|
|
231
|
+
* Delivers one event published by ANOTHER replica to this process's local
|
|
232
|
+
* subscribers for its room, without adding it to the local replay ring or
|
|
233
|
+
* consuming a local sequence (its id carries the origin replica's epoch).
|
|
234
|
+
*
|
|
235
|
+
* @param serializedEvent A serialized scoped business event produced by a peer.
|
|
236
|
+
* @param sourceKey Optional caller-owned fact key shared by every replica's copy
|
|
237
|
+
* of the same committed fact. When it was already seen (locally published or
|
|
238
|
+
* ingested from a peer) the event is dropped to avoid N× duplicate delivery.
|
|
239
|
+
* @returns True when it was queued to at least one local subscription.
|
|
240
|
+
* @remarks Source-key deduplication is process-local and bounded, so it caps
|
|
241
|
+
* duplicate fan-out within the retention window without replacing REST, which
|
|
242
|
+
* remains the authority. A malformed payload or a room with no local
|
|
243
|
+
* subscribers is ignored.
|
|
244
|
+
*/
|
|
245
|
+
ingestForeignEvent(serializedEvent: string, sourceKey?: string): boolean;
|
|
214
246
|
/** Atomically resolves replay and registers a distinct subscription for live events. */
|
|
215
247
|
subscribe(scope: ProjectRoomEventScope, afterEventId: string | undefined, listener: Listener): ProjectRoomEventSubscription;
|
|
216
248
|
/** Clears retained state, marks every subscription closed, and permanently closes the broker. */
|
|
@@ -232,6 +264,7 @@ declare class ProjectRoomEventBroker {
|
|
|
232
264
|
private assertOpen;
|
|
233
265
|
private createRoom;
|
|
234
266
|
private touch;
|
|
267
|
+
private rememberSourceKey;
|
|
235
268
|
}
|
|
236
269
|
|
|
237
270
|
/**
|
|
@@ -307,11 +340,18 @@ interface ProjectTaskMachineScope {
|
|
|
307
340
|
/**
|
|
308
341
|
* Serializes Project task machine operations by tenant, Assistant, and Project.
|
|
309
342
|
*
|
|
310
|
-
*
|
|
311
|
-
*
|
|
312
|
-
*
|
|
313
|
-
*
|
|
314
|
-
*
|
|
343
|
+
* The local FIFO lock is process-local: callers must share one instance.
|
|
344
|
+
* Acquisition is FIFO and intentionally non-reentrant; an operation must not
|
|
345
|
+
* await another acquisition of its own machine key. When an operation also needs
|
|
346
|
+
* the Project mutation mutex, it must acquire this machine mutex first.
|
|
347
|
+
*
|
|
348
|
+
* A coordination lease store may be installed process-wide
|
|
349
|
+
* ({@link configureCoordinationLeaseStore}). When present, each critical section
|
|
350
|
+
* additionally holds a bounded distributed lease
|
|
351
|
+
* (`project-task-machine:<tenant>:<assistant>:<project>`) so operations on the
|
|
352
|
+
* same machine serialize across replicas, not just within this process. The local
|
|
353
|
+
* FIFO lock is always retained; the lease only adds cross-replica exclusion. When
|
|
354
|
+
* no store is configured (single process, tests), behavior is unchanged.
|
|
315
355
|
*/
|
|
316
356
|
declare class ProjectTaskExecutionMutex {
|
|
317
357
|
private readonly queues;
|
|
@@ -397,6 +437,11 @@ type ProjectTaskRunOutcome = {
|
|
|
397
437
|
task: TaskItem;
|
|
398
438
|
thread: Thread;
|
|
399
439
|
messageId: string;
|
|
440
|
+
} | {
|
|
441
|
+
kind: "deferred";
|
|
442
|
+
task: TaskItem;
|
|
443
|
+
thread: Thread;
|
|
444
|
+
messageId: string;
|
|
400
445
|
};
|
|
401
446
|
/** Stable classification of the queue fact used to recover an in-progress Project Task. */
|
|
402
447
|
type ProjectTaskRecoveryClassification = {
|
|
@@ -764,6 +809,313 @@ declare class AgentTaskConsumer {
|
|
|
764
809
|
trigger_agent_task(taskRequest: AgentTaskRequest): void;
|
|
765
810
|
}
|
|
766
811
|
|
|
812
|
+
/** Cross-replica liveness probe: resolves true when a peer still owns the task's run. */
|
|
813
|
+
type TaskLivenessProbe = (task: TaskItem) => Promise<boolean>;
|
|
814
|
+
/**
|
|
815
|
+
* Builds a cross-replica task-liveness probe from the registered `"lease"` store.
|
|
816
|
+
*
|
|
817
|
+
* @returns A {@link TaskLivenessProbe}, or `undefined` only when no `lease`
|
|
818
|
+
* store is registered (a test-only path); with the default in-memory lease
|
|
819
|
+
* store a probe is always returned (single-process safe because the in-memory
|
|
820
|
+
* store has no peer).
|
|
821
|
+
* @remarks
|
|
822
|
+
* A task is considered live when EITHER lease is currently held:
|
|
823
|
+
* - `task-run:<taskId>` — the immediate executor's run-lifetime lease, or
|
|
824
|
+
* - `thread-run:<tenantId>:<ownerId>:<thread_id>` — the queue processor
|
|
825
|
+
* executing the task's run thread (dispatched workers run through this).
|
|
826
|
+
*
|
|
827
|
+
* Fail-safe: any lease-store error resolves `true`, so an unknown liveness
|
|
828
|
+
* state never causes a live run to be converged as an orphan.
|
|
829
|
+
*
|
|
830
|
+
* The probe is advisory: there is a benign race between the probe read and
|
|
831
|
+
* `failTask` (a run could acquire its lease in that window), and no
|
|
832
|
+
* fencing/heartbeat-expiry detection is provided yet — consistent with the
|
|
833
|
+
* framework's current no-lease posture.
|
|
834
|
+
*
|
|
835
|
+
* @example
|
|
836
|
+
* ```ts
|
|
837
|
+
* const probe = createTaskRunLiveness();
|
|
838
|
+
* if (probe && await probe(task)) return; // a peer replica owns it
|
|
839
|
+
* ```
|
|
840
|
+
*/
|
|
841
|
+
declare function createTaskRunLiveness(): TaskLivenessProbe | undefined;
|
|
842
|
+
|
|
843
|
+
/** Minimal caller-graph client surface used by recovery. */
|
|
844
|
+
interface RecoveryCallerClient {
|
|
845
|
+
getState(config: unknown): Promise<unknown>;
|
|
846
|
+
updateState(config: unknown, values: unknown, asNode?: string): Promise<unknown>;
|
|
847
|
+
invoke(input: unknown, config: unknown): Promise<unknown>;
|
|
848
|
+
}
|
|
849
|
+
interface TopCallerRecoveryDeps {
|
|
850
|
+
/**
|
|
851
|
+
* Resolve a caller's agent client + config from the resolved top caller.
|
|
852
|
+
*
|
|
853
|
+
* @param top The resolved top-level caller thread + assistant.
|
|
854
|
+
* @param tenantId The orphan task's tenant, used to scope the caller client.
|
|
855
|
+
*/
|
|
856
|
+
callerFor(top: {
|
|
857
|
+
threadId: string;
|
|
858
|
+
assistantId?: string;
|
|
859
|
+
}, tenantId: string): Promise<{
|
|
860
|
+
client: RecoveryCallerClient;
|
|
861
|
+
config: unknown;
|
|
862
|
+
agent?: unknown;
|
|
863
|
+
} | undefined>;
|
|
864
|
+
/** Live-run registry (defaults to the shared Core singleton). */
|
|
865
|
+
registry?: {
|
|
866
|
+
isLive(taskId: string): boolean;
|
|
867
|
+
};
|
|
868
|
+
/**
|
|
869
|
+
* Optional cross-replica liveness probe consulted by Guard B, so a run held
|
|
870
|
+
* by a peer replica is not converged/backfilled. Mutually additive with the
|
|
871
|
+
* process-local `registry`.
|
|
872
|
+
*/
|
|
873
|
+
isTaskLive?: TaskLivenessProbe;
|
|
874
|
+
/** Resume the continuation (tests can call client.invoke(null, config)). */
|
|
875
|
+
resume(client: RecoveryCallerClient, config: unknown, agent?: unknown): Promise<void>;
|
|
876
|
+
}
|
|
877
|
+
/** Dependencies for an on-demand single-task recovery ({@link recoverTask}). */
|
|
878
|
+
interface RecoverTaskDeps {
|
|
879
|
+
/** Lifecycle used to converge the orphan to `failed(no_executor)`. */
|
|
880
|
+
lifecycle: Pick<TaskLifecycleService, "failTask">;
|
|
881
|
+
/** Live-run registry (defaults to the shared Core singleton). */
|
|
882
|
+
registry?: TaskRunRegistry;
|
|
883
|
+
/**
|
|
884
|
+
* The tenant's other orphan candidates (normally every `in_progress` task),
|
|
885
|
+
* used to walk the caller chain up to the true top-level caller.
|
|
886
|
+
*
|
|
887
|
+
* @remarks
|
|
888
|
+
* Without this set {@link resolveTopCaller} can only see the target itself,
|
|
889
|
+
* so a nested middle run would be mistaken for a direct child of its
|
|
890
|
+
* immediate (frozen) caller. Defaults to `[converged]` when omitted.
|
|
891
|
+
*/
|
|
892
|
+
orphans?: TaskItem[];
|
|
893
|
+
/**
|
|
894
|
+
* Optional cross-replica liveness probe consulted by Guard B, so a run held
|
|
895
|
+
* by a peer replica is not converged. Forwarded to
|
|
896
|
+
* {@link reconcileStaleTaskRuns} and to {@link recoverTopCaller}.
|
|
897
|
+
*/
|
|
898
|
+
isTaskLive?: TaskLivenessProbe;
|
|
899
|
+
/** Resolve a caller's agent client + config from the resolved top caller. */
|
|
900
|
+
callerFor: TopCallerRecoveryDeps["callerFor"];
|
|
901
|
+
/** Resume the continuation (tests can call client.invoke(null, config)). */
|
|
902
|
+
resume: TopCallerRecoveryDeps["resume"];
|
|
903
|
+
}
|
|
904
|
+
/**
|
|
905
|
+
* Outcome of an on-demand single-task recovery attempt.
|
|
906
|
+
*
|
|
907
|
+
* @remarks
|
|
908
|
+
* - `recovered`: the task was converged and its top-level caller was resumed.
|
|
909
|
+
* - `skipped`: nothing was changed; `reason` names the guard (for example
|
|
910
|
+
* `not_in_progress`, `live`, `middle`, `project_task`, `no_top_caller`,
|
|
911
|
+
* `not_found`).
|
|
912
|
+
* - `converged_no_call`: the task was converged to `failed`, but the caller had
|
|
913
|
+
* no pending `task` call left to resolve, so no continuation was resumed.
|
|
914
|
+
*/
|
|
915
|
+
type RecoverTaskResult = {
|
|
916
|
+
kind: "recovered";
|
|
917
|
+
} | {
|
|
918
|
+
kind: "skipped";
|
|
919
|
+
reason: string;
|
|
920
|
+
} | {
|
|
921
|
+
kind: "converged_no_call";
|
|
922
|
+
};
|
|
923
|
+
/**
|
|
924
|
+
* Backfills one converged direct-child task's top-level caller.
|
|
925
|
+
*
|
|
926
|
+
* @param task The converged orphan task (must carry `context.caller_thread_id`).
|
|
927
|
+
* @param top The resolved top-level caller.
|
|
928
|
+
* @param deps Caller resolver, live-run registry, and resume hook.
|
|
929
|
+
* @returns `"recovered"` or a `"skipped:<reason>"` string.
|
|
930
|
+
*/
|
|
931
|
+
declare function recoverTopCaller(task: TaskItem, top: {
|
|
932
|
+
threadId: string;
|
|
933
|
+
assistantId?: string;
|
|
934
|
+
}, deps: TopCallerRecoveryDeps): Promise<string>;
|
|
935
|
+
/**
|
|
936
|
+
* Recovers a single orphan task on demand, reusing the collapse logic.
|
|
937
|
+
*
|
|
938
|
+
* @param task The candidate task (any status).
|
|
939
|
+
* @param deps Lifecycle, live-run registry, orphan candidate set (for the
|
|
940
|
+
* caller-chain walk), caller resolver, and resume hook.
|
|
941
|
+
* @returns A discriminated outcome: `recovered`, `skipped:<reason>`, or
|
|
942
|
+
* `converged_no_call` when the task was converged but the caller had no
|
|
943
|
+
* pending `task` call to resolve.
|
|
944
|
+
* @remarks
|
|
945
|
+
* Mirrors {@link runOrphanRecovery} for exactly one task: anything not
|
|
946
|
+
* `in_progress`, or still held by a live run, is skipped; the rest is converged
|
|
947
|
+
* to `failed` (`no_executor`, ADR-143) and its **top-level** caller is
|
|
948
|
+
* backfilled. `deps.orphans` must carry the tenant's other `in_progress`
|
|
949
|
+
* candidates so a nested middle run is correctly skipped (`middle`) instead of
|
|
950
|
+
* being mistaken for a direct child of its frozen immediate caller.
|
|
951
|
+
*
|
|
952
|
+
* This is an **offline/maintenance** entry point (the `task:recover` CLI): run
|
|
953
|
+
* it when the Gateway is stopped, or from within the Gateway process. Guard B
|
|
954
|
+
* reads the process-local {@link taskRunRegistry}; when the optional
|
|
955
|
+
* `isTaskLive` cross-replica probe is supplied (the gateway's lease-backed
|
|
956
|
+
* probe), it also detects a run held by a peer replica.
|
|
957
|
+
*
|
|
958
|
+
* The convergence actor is `system:on-demand-recovery` (distinct from the
|
|
959
|
+
* startup sweep's `system:orphan-recovery`), so on-demand convergences are
|
|
960
|
+
* auditable in the task work-item trail. No HTTP route is added and recovery
|
|
961
|
+
* semantics are unchanged.
|
|
962
|
+
*/
|
|
963
|
+
declare function recoverTask(task: TaskItem, deps: RecoverTaskDeps): Promise<RecoverTaskResult>;
|
|
964
|
+
/** Walks caller links up to the thread that is not any orphan's run thread. */
|
|
965
|
+
declare function resolveTopCaller(task: TaskItem, orphans: TaskItem[]): {
|
|
966
|
+
threadId: string;
|
|
967
|
+
assistantId?: string;
|
|
968
|
+
} | undefined;
|
|
969
|
+
/**
|
|
970
|
+
* Runs the whole collapse recovery once: reconcile orphan tasks, then backfill
|
|
971
|
+
* the top-level caller of every direct-child orphan, grouped per top caller.
|
|
972
|
+
* Best-effort; a failing group is contained and never aborts the others.
|
|
973
|
+
*
|
|
974
|
+
* @param deps Reconcile source, caller resolution, resume, and logger.
|
|
975
|
+
* @returns The recovered and skipped `taskId:reason` entries.
|
|
976
|
+
*/
|
|
977
|
+
declare function runOrphanRecovery(deps: {
|
|
978
|
+
reconcile: () => Promise<{
|
|
979
|
+
convergedTasks: TaskItem[];
|
|
980
|
+
}>;
|
|
981
|
+
callerFor: TopCallerRecoveryDeps["callerFor"];
|
|
982
|
+
resume: TopCallerRecoveryDeps["resume"];
|
|
983
|
+
isTaskLive?: TaskLivenessProbe;
|
|
984
|
+
log: {
|
|
985
|
+
info(msg: string, ctx: Record<string, unknown>): void;
|
|
986
|
+
warn(msg: string, ctx: Record<string, unknown>): void;
|
|
987
|
+
};
|
|
988
|
+
}): Promise<{
|
|
989
|
+
recovered: string[];
|
|
990
|
+
skipped: string[];
|
|
991
|
+
}>;
|
|
992
|
+
|
|
993
|
+
/**
|
|
994
|
+
* Shared orphan-recovery runtime: the startup sweep and the on-demand
|
|
995
|
+
* single-task entry point share one dependency shape and one lifecycle.
|
|
996
|
+
*
|
|
997
|
+
* @remarks
|
|
998
|
+
* `recoverAll()` is the behavior-preserving move of the gateway startup block
|
|
999
|
+
* (reconcile every tenant's orphaned ordinary tasks, then backfill the
|
|
1000
|
+
* top-level caller). `recoverTask()` is the A-lite on-demand entry: no HTTP
|
|
1001
|
+
* route, just a programmatic call used by the `task:recover` CLI/script.
|
|
1002
|
+
*/
|
|
1003
|
+
|
|
1004
|
+
/** Dependencies shared by the startup sweep and the single-task entry. */
|
|
1005
|
+
interface OrphanRecoveryRuntimeDeps {
|
|
1006
|
+
/** Task store used to enumerate tenants' orphans and read one task by id. */
|
|
1007
|
+
taskStore: TaskStore;
|
|
1008
|
+
/** Work-item store used to build the lifecycle when `lifecycle` is omitted. */
|
|
1009
|
+
workItemStore: TaskWorkItemStore;
|
|
1010
|
+
/** Tenant store supplying `getAllTenants()` for the startup sweep. */
|
|
1011
|
+
tenantStore: Pick<TenantStore, "getAllTenants">;
|
|
1012
|
+
/** Lifecycle used to converge orphans (built from the stores when omitted). */
|
|
1013
|
+
lifecycle?: Pick<TaskLifecycleService, "failTask">;
|
|
1014
|
+
/** Resolve a caller's agent client + config from the resolved top caller. */
|
|
1015
|
+
callerFor: TopCallerRecoveryDeps["callerFor"];
|
|
1016
|
+
/** Resume the continuation (tests can call client.invoke(null, config)). */
|
|
1017
|
+
resume: TopCallerRecoveryDeps["resume"];
|
|
1018
|
+
/**
|
|
1019
|
+
* Cross-replica liveness probe; defaults to the shared cross-replica probe
|
|
1020
|
+
* ({@link createTaskRunLiveness()}); inherit it unless a test injects one.
|
|
1021
|
+
*/
|
|
1022
|
+
isTaskLive?: TaskLivenessProbe;
|
|
1023
|
+
/** Structured logger. */
|
|
1024
|
+
log: {
|
|
1025
|
+
info(msg: string, ctx: Record<string, unknown>): void;
|
|
1026
|
+
warn(msg: string, ctx: Record<string, unknown>): void;
|
|
1027
|
+
};
|
|
1028
|
+
}
|
|
1029
|
+
/** Entry points returned by {@link createOrphanRecoveryRuntime}. */
|
|
1030
|
+
interface OrphanRecoveryRuntime {
|
|
1031
|
+
/** Startup/interval sweep over every tenant's orphaned ordinary tasks. */
|
|
1032
|
+
recoverAll(): Promise<{
|
|
1033
|
+
recovered: string[];
|
|
1034
|
+
skipped: string[];
|
|
1035
|
+
}>;
|
|
1036
|
+
/**
|
|
1037
|
+
* On-demand recovery of exactly one task within one tenant.
|
|
1038
|
+
*
|
|
1039
|
+
* @remarks Offline/maintenance: run when the Gateway is stopped, or from the
|
|
1040
|
+
* Gateway process. The `live` guard reads the process-local task registry and,
|
|
1041
|
+
* when a lease store is registered, also consults the cross-replica liveness
|
|
1042
|
+
* probe so a run held by another process is not converged. Governed Project
|
|
1043
|
+
* tasks are refused (`skipped:project_task`) so this never bypasses their
|
|
1044
|
+
* managed runtime.
|
|
1045
|
+
*/
|
|
1046
|
+
recoverTask(input: {
|
|
1047
|
+
tenantId: string;
|
|
1048
|
+
taskId: string;
|
|
1049
|
+
}): Promise<RecoverTaskResult>;
|
|
1050
|
+
}
|
|
1051
|
+
/**
|
|
1052
|
+
* Builds the shared orphan-recovery runtime.
|
|
1053
|
+
*
|
|
1054
|
+
* @param deps Task/work-item/tenant stores, optional lifecycle, caller
|
|
1055
|
+
* resolver, resume hook, and logger.
|
|
1056
|
+
* @returns `recoverAll()` (startup sweep) and `recoverTask()` (on-demand).
|
|
1057
|
+
* @remarks
|
|
1058
|
+
* Governed Project tasks are excluded from `recoverAll()` (they have their own
|
|
1059
|
+
* observer-based reconciliation) and from `recoverTask()`
|
|
1060
|
+
* (`skipped:project_task`), so the on-demand entry can never bypass the managed
|
|
1061
|
+
* Project runtime. `recoverTask()` delegates to {@link recoverSingleTask} with
|
|
1062
|
+
* the shared lifecycle, the process-local live-run registry, and the
|
|
1063
|
+
* cross-replica liveness probe (built from the shared `"lease"` store unless
|
|
1064
|
+
* `isTaskLive` is supplied), so it does not converge a task a live run still
|
|
1065
|
+
* holds — in this process or a peer replica. The liveness probe is advisory: a
|
|
1066
|
+
* benign race exists between the probe read and `failTask` (no
|
|
1067
|
+
* fencing/heartbeat support yet).
|
|
1068
|
+
*
|
|
1069
|
+
* @example
|
|
1070
|
+
* ```ts
|
|
1071
|
+
* const runtime = createOrphanRecoveryRuntime({ taskStore, workItemStore, tenantStore, callerFor, resume, log });
|
|
1072
|
+
* await runtime.recoverAll();
|
|
1073
|
+
* const outcome = await runtime.recoverTask({ tenantId, taskId });
|
|
1074
|
+
* ```
|
|
1075
|
+
*/
|
|
1076
|
+
declare function createOrphanRecoveryRuntime(deps: OrphanRecoveryRuntimeDeps): OrphanRecoveryRuntime;
|
|
1077
|
+
|
|
1078
|
+
/**
|
|
1079
|
+
* Builds the caller-resolution and resume hooks used by orphan recovery.
|
|
1080
|
+
*
|
|
1081
|
+
* @returns `callerFor` (resolves a top caller's agent client + config, omitting
|
|
1082
|
+
* the in-memory `agent` when it is absent) and `resume` (prefers
|
|
1083
|
+
* `agent.resumeTask()`, otherwise a raw checkpoint continuation via
|
|
1084
|
+
* `client.invoke(null, config)`).
|
|
1085
|
+
* @remarks
|
|
1086
|
+
* `callerFor` uses `getAgentClientAsync` (falls back to `getAgentClient`) so a
|
|
1087
|
+
* DB-backed assistant that is not resident in memory can still be loaded.
|
|
1088
|
+
*/
|
|
1089
|
+
declare function createRecoveryCallerDeps(): Pick<TopCallerRecoveryDeps, "callerFor" | "resume">;
|
|
1090
|
+
|
|
1091
|
+
/** Outbox topic carrying cross-replica Project Room realtime events. */
|
|
1092
|
+
declare const ROOM_EVENT_TOPIC = "room-event";
|
|
1093
|
+
/**
|
|
1094
|
+
* Mirrors one locally published Project Room event to the shared outbox.
|
|
1095
|
+
*
|
|
1096
|
+
* @param outbox Shared append-only outbox log.
|
|
1097
|
+
* @param owner Origin replica id, so its own poller skips the row.
|
|
1098
|
+
* @param scope Room scope the event was published for.
|
|
1099
|
+
* @param serialized Canonical serialized scoped event.
|
|
1100
|
+
* @param sourceKey Optional caller-owned fact key carried to peer replicas for
|
|
1101
|
+
* bounded duplicate suppression at their broker.
|
|
1102
|
+
* @returns A promise that settles once the append attempt completes.
|
|
1103
|
+
* @remarks Best-effort: an append failure is swallowed because REST remains the
|
|
1104
|
+
* authority and clients refetch on reconnect.
|
|
1105
|
+
*/
|
|
1106
|
+
declare function publishRoomEvent(outbox: OutboxStore, owner: string, scope: ProjectRoomEventScope, serialized: string, sourceKey?: string): Promise<void>;
|
|
1107
|
+
/**
|
|
1108
|
+
* Delivers one foreign outbox payload to the local broker's subscribers.
|
|
1109
|
+
*
|
|
1110
|
+
* @param payload Raw outbox payload; expected `{ serialized: string, sourceKey?: string }`.
|
|
1111
|
+
* @param broker The process-local Project Room event broker.
|
|
1112
|
+
* @returns True when the broker queued the event to at least one local subscription.
|
|
1113
|
+
* @remarks Malformed payloads are ignored. The broker drops events whose
|
|
1114
|
+
* source key it already saw; the poller additionally skips rows authored by this
|
|
1115
|
+
* replica.
|
|
1116
|
+
*/
|
|
1117
|
+
declare function ingestRoomEvent(payload: unknown, broker: ProjectRoomEventBroker): boolean;
|
|
1118
|
+
|
|
767
1119
|
/**
|
|
768
1120
|
* Logger configuration for gateway
|
|
769
1121
|
*/
|
|
@@ -794,4 +1146,4 @@ declare const LatticeGateway: {
|
|
|
794
1146
|
AgentTaskConsumer: typeof AgentTaskConsumer;
|
|
795
1147
|
};
|
|
796
1148
|
|
|
797
|
-
export { type GatewayLoggerConfig, LatticeGateway, type LatticeGatewayConfig };
|
|
1149
|
+
export { type GatewayLoggerConfig, LatticeGateway, type LatticeGatewayConfig, type OrphanRecoveryRuntime, type OrphanRecoveryRuntimeDeps, ProjectRoomEventBroker, ROOM_EVENT_TOPIC, type RecoverTaskDeps, type RecoverTaskResult, type TaskLivenessProbe, createOrphanRecoveryRuntime, createRecoveryCallerDeps, createTaskRunLiveness, ingestRoomEvent, publishRoomEvent, recoverTask, recoverTopCaller, resolveTopCaller, runOrphanRecovery };
|