@intx/hub-sessions 0.3.0 → 0.4.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/agent-repo.d.ts +14 -2
- package/dist/agent-repo.js +17 -4
- package/dist/agent-state-kind.js +14 -63
- package/dist/asset-service.js +14 -10
- package/dist/credential-push.d.ts +48 -4
- package/dist/credential-push.js +138 -6
- package/dist/event-collector-registry.d.ts +2 -1
- package/dist/event-collector-registry.js +38 -9
- package/dist/event-collector.d.ts +11 -1
- package/dist/event-collector.js +36 -3
- package/dist/hub-session-lookups.d.ts +1 -1
- package/dist/hub-session-lookups.js +68 -72
- package/dist/hub-session-orchestrator.d.ts +2 -3
- package/dist/hub-session-orchestrator.js +13 -12
- package/dist/index.d.ts +7 -6
- package/dist/index.js +7 -6
- package/dist/reconciliation-scheduler.d.ts +14 -0
- package/dist/reconciliation-scheduler.js +55 -0
- package/dist/repo-store/index.d.ts +1 -0
- package/dist/repo-store/index.js +1 -0
- package/dist/repo-store/user-principal-gate.d.ts +26 -0
- package/dist/repo-store/user-principal-gate.js +78 -0
- package/dist/session-service.d.ts +66 -121
- package/dist/session-service.js +444 -411
- package/dist/sidecar-allocation/capability-policy.d.ts +27 -0
- package/dist/sidecar-allocation/capability-policy.js +124 -0
- package/dist/sidecar-allocation/contracts.d.ts +29 -6
- package/dist/sidecar-allocation/contracts.js +7 -2
- package/dist/sidecar-allocation/index.d.ts +4 -3
- package/dist/sidecar-allocation/index.js +3 -2
- package/dist/sidecar-allocation/operation.d.ts +10 -0
- package/dist/sidecar-allocation/operation.js +54 -0
- package/dist/sidecar-allocation/plugin-registry.d.ts +16 -3
- package/dist/sidecar-allocation/plugin-registry.js +36 -12
- package/dist/sidecar-allocation/reconciler.d.ts +16 -4
- package/dist/sidecar-allocation/reconciler.js +486 -92
- package/dist/skill-kind.js +8 -62
- package/dist/substrate.d.ts +1 -1
- package/dist/substrate.js +1 -1
- package/dist/workflow-allocation-service.d.ts +21 -15
- package/dist/workflow-allocation-service.js +440 -125
- package/dist/workflow-dispatch-service.d.ts +4 -2
- package/dist/workflow-dispatch-service.js +89 -26
- package/dist/workflow-kind.d.ts +12 -0
- package/dist/workflow-kind.js +17 -60
- package/dist/workflow-probe-gate.d.ts +99 -27
- package/dist/workflow-probe-gate.js +196 -21
- package/dist/workflow-run-kind.d.ts +112 -19
- package/dist/workflow-run-kind.js +626 -210
- package/dist/workflow-run-restore.d.ts +1 -0
- package/dist/workflow-run-restore.js +5 -1
- package/dist/workflow-source-pins.d.ts +8 -0
- package/dist/workflow-source-pins.js +14 -0
- package/dist/ws/index.d.ts +1 -1
- package/dist/ws/index.js +1 -1
- package/dist/ws/pending-tracker.d.ts +93 -0
- package/dist/ws/pending-tracker.js +132 -0
- package/dist/ws/sidecar-events.d.ts +43 -29
- package/dist/ws/sidecar-events.js +0 -2
- package/dist/ws/sidecar-handler.d.ts +122 -85
- package/dist/ws/sidecar-handler.js +925 -878
- package/dist/ws/sidecar-handler.test-helpers.d.ts +38 -0
- package/dist/ws/sidecar-handler.test-helpers.js +95 -0
- package/dist/ws/sidecar-token-authenticator.js +37 -23
- package/package.json +13 -13
- package/dist/sidecar-allocation/placement-policy.d.ts +0 -11
- package/dist/sidecar-allocation/placement-policy.js +0 -21
|
@@ -17,10 +17,14 @@ export async function restoreWorkflowRunToAllocation(args) {
|
|
|
17
17
|
id: deriveWorkflowRunRepoId(agentAddress),
|
|
18
18
|
};
|
|
19
19
|
for (const ref of WORKFLOW_RUN_RESTORE_REFS) {
|
|
20
|
+
args.signal?.throwIfAborted();
|
|
20
21
|
const tip = await agentRepoStore.repoStore.resolveRef(principal, repoId, ref);
|
|
22
|
+
args.signal?.throwIfAborted();
|
|
21
23
|
if (tip === null)
|
|
22
24
|
continue;
|
|
23
25
|
const pack = await agentRepoStore.repoStore.createPack(principal, repoId, ref);
|
|
24
|
-
|
|
26
|
+
args.signal?.throwIfAborted();
|
|
27
|
+
await allocationRouter.sendWorkflowRunPackToAllocation(allocationTarget, agentAddress, pack.pack, pack.ref, pack.commitSha, args.signal);
|
|
25
28
|
}
|
|
29
|
+
args.signal?.throwIfAborted();
|
|
26
30
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { WorkflowProjectionWithSources } from "@intx/types/sidecar";
|
|
2
|
+
import type { HarnessConfig } from "@intx/types/runtime";
|
|
3
|
+
import { type ApprovalSet } from "@intx/workflow-deploy";
|
|
4
|
+
export declare function buildReferencedWorkflowSourcePins(args: {
|
|
5
|
+
projection: WorkflowProjectionWithSources["definition"];
|
|
6
|
+
config: HarnessConfig;
|
|
7
|
+
operatorApprovals: ApprovalSet;
|
|
8
|
+
}): Promise<readonly WorkflowProjectionWithSources[]>;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { computeWireDefinitionHash } from "@intx/types/wire-definition-hash";
|
|
2
|
+
import { buildInertBodyStepSources, enumerateInertBodies, } from "@intx/workflow-deploy";
|
|
3
|
+
export async function buildReferencedWorkflowSourcePins(args) {
|
|
4
|
+
return Promise.all(enumerateInertBodies(args.projection).map(async (body) => ({
|
|
5
|
+
definition: body.definition,
|
|
6
|
+
sources: buildInertBodyStepSources({
|
|
7
|
+
definition: body.definition,
|
|
8
|
+
workflowId: body.ref,
|
|
9
|
+
config: args.config,
|
|
10
|
+
operatorApprovals: args.operatorApprovals,
|
|
11
|
+
}),
|
|
12
|
+
approvedWireHash: await computeWireDefinitionHash(body.definition),
|
|
13
|
+
})));
|
|
14
|
+
}
|
package/dist/ws/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { createSidecarRouter, type SidecarRouter, type SidecarRouterConfig, type SidecarConnection, type SidecarAuthIdentity, type SidecarAuthenticator, type AllocatedSidecarTarget, type SidecarAllocationRouter, type SendPackOptions, type WsHandle, } from "./sidecar-handler.js";
|
|
1
|
+
export { createSidecarRouter, SidecarIdentityValidationError, type SidecarRouter, type SidecarRouterConfig, type SenderDeploySettledOutcome, type AllocatedSenderDeployAttempt, type SidecarConnection, type SidecarAuthIdentity, type SidecarAuthenticator, type AllocatedSidecarTarget, type SidecarAllocationRouter, type SendPackOptions, type WsHandle, } from "./sidecar-handler.js";
|
|
2
2
|
export { createSidecarCredentialResolver, createSidecarTokenAuthenticator, type CreateSidecarTokenAuthenticatorDeps, } from "./sidecar-token-authenticator.js";
|
|
3
3
|
export { createSidecarEmitter, type SidecarEventEmitter, type SidecarEventMap, type SidecarEventType, type SidecarEventListener, type SidecarLookups, type SidecarMailPersistedRow, type SidecarMailPersistedPayload, type MailTriggeredRunGrantsResult, type WorkflowRunPackSource, } from "./sidecar-events.js";
|
package/dist/ws/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { createSidecarRouter, } from "./sidecar-handler.js";
|
|
1
|
+
export { createSidecarRouter, SidecarIdentityValidationError, } from "./sidecar-handler.js";
|
|
2
2
|
export { createSidecarCredentialResolver, createSidecarTokenAuthenticator, } from "./sidecar-token-authenticator.js";
|
|
3
3
|
export { createSidecarEmitter, } from "./sidecar-events.js";
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
export type WsHandle = {
|
|
2
|
+
send(data: string): void;
|
|
3
|
+
close(): void;
|
|
4
|
+
};
|
|
5
|
+
/**
|
|
6
|
+
* Arms a one-shot timeout and returns the canceller for it.
|
|
7
|
+
*
|
|
8
|
+
* The tracker's timeouts are the only thing about it that depends on time
|
|
9
|
+
* passing, and a caller that wants them settled on demand rather than by
|
|
10
|
+
* waiting has nowhere to reach in: the global timer is not addressable from
|
|
11
|
+
* outside. Injecting the arming function makes the timeout observable and
|
|
12
|
+
* cancellable by whoever owns the tracker, so a test can fire a timeout, or
|
|
13
|
+
* assert one was disarmed, without a real duration elapsing.
|
|
14
|
+
*
|
|
15
|
+
* Returning the canceller rather than a handle keeps the timer's identity
|
|
16
|
+
* private to whichever implementation armed it.
|
|
17
|
+
*/
|
|
18
|
+
export type ScheduleTimeout = (handler: () => void, ms: number) => () => void;
|
|
19
|
+
export type PendingEntry<Key, Value, Meta> = {
|
|
20
|
+
key: Key;
|
|
21
|
+
ws: WsHandle;
|
|
22
|
+
/**
|
|
23
|
+
* Opaque per-entry payload for settle-time ownership checks. The pack
|
|
24
|
+
* tracker stores the send-site `{ agentAddress, repoId }` so an ack/reject
|
|
25
|
+
* is honored only when it comes from the connection that owns the transfer
|
|
26
|
+
* for the same repo; the other trackers pass `undefined`.
|
|
27
|
+
*/
|
|
28
|
+
meta: Meta;
|
|
29
|
+
resolve(value: Value): void;
|
|
30
|
+
reject(error: string): void;
|
|
31
|
+
/**
|
|
32
|
+
* Disarms this entry's timeout.
|
|
33
|
+
*
|
|
34
|
+
* Runs at most once per entry, which is why it need not be idempotent and
|
|
35
|
+
* `ScheduleTimeout` does not ask its implementations for that: each of the
|
|
36
|
+
* four settle paths cancels and drops the entry from the map in one
|
|
37
|
+
* synchronous block, and a timeout that fires drops the entry itself, so
|
|
38
|
+
* nothing can reach the entry to cancel it a second time.
|
|
39
|
+
*/
|
|
40
|
+
cancelTimeout(): void;
|
|
41
|
+
};
|
|
42
|
+
export declare class PendingTracker<Key, Value = void, Meta = undefined> {
|
|
43
|
+
private readonly schedule;
|
|
44
|
+
private readonly entries;
|
|
45
|
+
/**
|
|
46
|
+
* `schedule` defaults to the global timer, which is what every production
|
|
47
|
+
* caller wants; it is injectable so a caller can drive the timeouts.
|
|
48
|
+
*/
|
|
49
|
+
constructor(schedule?: ScheduleTimeout);
|
|
50
|
+
/**
|
|
51
|
+
* Register a pending round-trip and arm its timeout. The entry is stored
|
|
52
|
+
* before the caller sends its frame, so a synchronous reply (loopback
|
|
53
|
+
* transports, tests) settles it. When `timeoutMs` elapses the entry is
|
|
54
|
+
* dropped and `reject` is invoked with `timeoutMessage` — the same
|
|
55
|
+
* rejection path an error reply frame uses, so per-site cleanup (routing
|
|
56
|
+
* rollback, address bookkeeping) runs exactly once either way.
|
|
57
|
+
*
|
|
58
|
+
* `meta` is the opaque per-entry payload settle-time ownership checks read
|
|
59
|
+
* off `get`; pass `undefined` when the round-trip carries none.
|
|
60
|
+
*/
|
|
61
|
+
register(key: Key, ws: WsHandle, options: {
|
|
62
|
+
timeoutMs: number;
|
|
63
|
+
timeoutMessage: string;
|
|
64
|
+
resolve(value: Value): void;
|
|
65
|
+
reject(error: string): void;
|
|
66
|
+
}, meta: Meta): void;
|
|
67
|
+
has(key: Key): boolean;
|
|
68
|
+
get(key: Key): PendingEntry<Key, Value, Meta> | undefined;
|
|
69
|
+
/**
|
|
70
|
+
* Settle a pending entry as resolved. No-op when the entry is already
|
|
71
|
+
* gone (timed out or swept on disconnect).
|
|
72
|
+
*/
|
|
73
|
+
resolve(key: Key, value: Value): boolean;
|
|
74
|
+
/**
|
|
75
|
+
* Settle a pending entry as rejected. No-op when the entry is already
|
|
76
|
+
* gone (timed out or swept on disconnect).
|
|
77
|
+
*/
|
|
78
|
+
reject(key: Key, error: string): boolean;
|
|
79
|
+
/**
|
|
80
|
+
* Drop a pending entry without settling it. The caller rejects its own
|
|
81
|
+
* promise directly — used when a frame provably never reached the wire, so
|
|
82
|
+
* the failure must not take the normal rejection path (the deploy
|
|
83
|
+
* "failed to send" path, which must report `frameSent: false` and must not
|
|
84
|
+
* let the armed timer fire later and double-reject).
|
|
85
|
+
*/
|
|
86
|
+
delete(key: Key): void;
|
|
87
|
+
/**
|
|
88
|
+
* Reject every pending entry owned by `ws` — the disconnect sweep. Each
|
|
89
|
+
* entry's reject closure runs its own per-site cleanup (address
|
|
90
|
+
* bookkeeping), identical to a frame-error rejection.
|
|
91
|
+
*/
|
|
92
|
+
rejectAllForWs(ws: WsHandle, error: string): void;
|
|
93
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
// Generic pending-round-trip tracker for hub→sidecar requests.
|
|
2
|
+
//
|
|
3
|
+
// `sidecar-handler.ts` runs five request/response round-trips over the same
|
|
4
|
+
// websocket (session requests, agent deploys, pack transfers, undeploys,
|
|
5
|
+
// workflow probes). Each used to hand-roll the same lifecycle: register an
|
|
6
|
+
// entry in a Map keyed by request/transfer/address, arm a timeout that
|
|
7
|
+
// settles the entry, settle it early when the matching reply frame arrives,
|
|
8
|
+
// and sweep every entry owned by a connection when that connection drops.
|
|
9
|
+
// The sites differ only in the key, the resolved value, and the per-site
|
|
10
|
+
// cleanup their resolve/reject closures capture — the map + timer lifecycle
|
|
11
|
+
// is identical. `PendingTracker` owns that lifecycle so timeouts and
|
|
12
|
+
// disconnect sweeps behave uniformly and a fix to either lands in one place.
|
|
13
|
+
const scheduleOnGlobalTimer = (handler, ms) => {
|
|
14
|
+
const timer = setTimeout(handler, ms);
|
|
15
|
+
return () => {
|
|
16
|
+
clearTimeout(timer);
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
export class PendingTracker {
|
|
20
|
+
schedule;
|
|
21
|
+
entries = new Map();
|
|
22
|
+
/**
|
|
23
|
+
* `schedule` defaults to the global timer, which is what every production
|
|
24
|
+
* caller wants; it is injectable so a caller can drive the timeouts.
|
|
25
|
+
*/
|
|
26
|
+
constructor(schedule = scheduleOnGlobalTimer) {
|
|
27
|
+
this.schedule = schedule;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Register a pending round-trip and arm its timeout. The entry is stored
|
|
31
|
+
* before the caller sends its frame, so a synchronous reply (loopback
|
|
32
|
+
* transports, tests) settles it. When `timeoutMs` elapses the entry is
|
|
33
|
+
* dropped and `reject` is invoked with `timeoutMessage` — the same
|
|
34
|
+
* rejection path an error reply frame uses, so per-site cleanup (routing
|
|
35
|
+
* rollback, address bookkeeping) runs exactly once either way.
|
|
36
|
+
*
|
|
37
|
+
* `meta` is the opaque per-entry payload settle-time ownership checks read
|
|
38
|
+
* off `get`; pass `undefined` when the round-trip carries none.
|
|
39
|
+
*/
|
|
40
|
+
register(key, ws, options, meta) {
|
|
41
|
+
// Identifies the entry this call is about to register, so the handler
|
|
42
|
+
// below can recognize it. Allocated before `schedule`, because the
|
|
43
|
+
// handler closes over it and the entry itself does not exist yet.
|
|
44
|
+
const token = Symbol("pending-entry");
|
|
45
|
+
const cancelTimeout = this.schedule(() => {
|
|
46
|
+
// Only the entry this call registered may be timed out. The global
|
|
47
|
+
// timer never fires a cancelled timeout, but `schedule` is injectable
|
|
48
|
+
// now and a caller's implementation is not owed that guarantee -- so
|
|
49
|
+
// the handler checks rather than trusting cancellation to be perfect.
|
|
50
|
+
// The check compares `token` rather than asking whether the key is
|
|
51
|
+
// occupied: keys are reused (`pendingDeploys` is keyed by agent
|
|
52
|
+
// address), so a stale fire can find the key held by a live successor.
|
|
53
|
+
// Treating that as its own entry would drop the successor without
|
|
54
|
+
// settling it and run this already-settled entry's per-site cleanup a
|
|
55
|
+
// second time.
|
|
56
|
+
const current = this.entries.get(key);
|
|
57
|
+
if (current === undefined || current.token !== token)
|
|
58
|
+
return;
|
|
59
|
+
this.entries.delete(key);
|
|
60
|
+
options.reject(options.timeoutMessage);
|
|
61
|
+
}, options.timeoutMs);
|
|
62
|
+
this.entries.set(key, {
|
|
63
|
+
key,
|
|
64
|
+
ws,
|
|
65
|
+
meta,
|
|
66
|
+
resolve: options.resolve,
|
|
67
|
+
reject: options.reject,
|
|
68
|
+
cancelTimeout,
|
|
69
|
+
token,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
has(key) {
|
|
73
|
+
return this.entries.has(key);
|
|
74
|
+
}
|
|
75
|
+
get(key) {
|
|
76
|
+
return this.entries.get(key);
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Settle a pending entry as resolved. No-op when the entry is already
|
|
80
|
+
* gone (timed out or swept on disconnect).
|
|
81
|
+
*/
|
|
82
|
+
resolve(key, value) {
|
|
83
|
+
const entry = this.entries.get(key);
|
|
84
|
+
if (entry === undefined)
|
|
85
|
+
return false;
|
|
86
|
+
entry.cancelTimeout();
|
|
87
|
+
this.entries.delete(key);
|
|
88
|
+
entry.resolve(value);
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Settle a pending entry as rejected. No-op when the entry is already
|
|
93
|
+
* gone (timed out or swept on disconnect).
|
|
94
|
+
*/
|
|
95
|
+
reject(key, error) {
|
|
96
|
+
const entry = this.entries.get(key);
|
|
97
|
+
if (entry === undefined)
|
|
98
|
+
return false;
|
|
99
|
+
entry.cancelTimeout();
|
|
100
|
+
this.entries.delete(key);
|
|
101
|
+
entry.reject(error);
|
|
102
|
+
return true;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Drop a pending entry without settling it. The caller rejects its own
|
|
106
|
+
* promise directly — used when a frame provably never reached the wire, so
|
|
107
|
+
* the failure must not take the normal rejection path (the deploy
|
|
108
|
+
* "failed to send" path, which must report `frameSent: false` and must not
|
|
109
|
+
* let the armed timer fire later and double-reject).
|
|
110
|
+
*/
|
|
111
|
+
delete(key) {
|
|
112
|
+
const entry = this.entries.get(key);
|
|
113
|
+
if (entry === undefined)
|
|
114
|
+
return;
|
|
115
|
+
entry.cancelTimeout();
|
|
116
|
+
this.entries.delete(key);
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Reject every pending entry owned by `ws` — the disconnect sweep. Each
|
|
120
|
+
* entry's reject closure runs its own per-site cleanup (address
|
|
121
|
+
* bookkeeping), identical to a frame-error rejection.
|
|
122
|
+
*/
|
|
123
|
+
rejectAllForWs(ws, error) {
|
|
124
|
+
for (const [key, entry] of this.entries) {
|
|
125
|
+
if (entry.ws !== ws)
|
|
126
|
+
continue;
|
|
127
|
+
entry.cancelTimeout();
|
|
128
|
+
this.entries.delete(key);
|
|
129
|
+
entry.reject(error);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
@@ -13,9 +13,6 @@ export type SidecarMailPersistedPayload = SidecarMailPersistedRow & {
|
|
|
13
13
|
};
|
|
14
14
|
/** Authenticated connection scope attached to a workflow-run pack. */
|
|
15
15
|
export type WorkflowRunPackSource = {
|
|
16
|
-
readonly kind: "shared";
|
|
17
|
-
readonly agentAddress: string;
|
|
18
|
-
} | {
|
|
19
16
|
readonly kind: "allocated";
|
|
20
17
|
readonly agentAddress: string;
|
|
21
18
|
readonly allocationId: string;
|
|
@@ -50,9 +47,9 @@ export type SidecarEventMap = {
|
|
|
50
47
|
event: unknown;
|
|
51
48
|
};
|
|
52
49
|
/** Notification. Emitted once when a sidecar's connection closes,
|
|
53
|
-
* carrying every address the connection owned --
|
|
54
|
-
*
|
|
55
|
-
*
|
|
50
|
+
* carrying every address the connection owned -- session addresses
|
|
51
|
+
* and hub-minted workflow-substrate deployment addresses alike --
|
|
52
|
+
* so lifecycle teardown covers both. */
|
|
56
53
|
"sidecar.disconnect": {
|
|
57
54
|
ownedAddresses: string[];
|
|
58
55
|
/** Present only when the closing socket was the current allocated owner. */
|
|
@@ -80,7 +77,7 @@ export type SidecarEventMap = {
|
|
|
80
77
|
* per-row (e.g. dispatch a delivered event). */
|
|
81
78
|
"mail.persisted": SidecarMailPersistedPayload;
|
|
82
79
|
/** Notification after a sidecar confirms a mail trigger is in its durable
|
|
83
|
-
* local inbox. For
|
|
80
|
+
* local inbox. For a provisioned worker, `allocated` identifies the exact
|
|
84
81
|
* generation that acknowledged the message. This is not workflow
|
|
85
82
|
* settlement; the Hub retains the payload until the Git claim-check records
|
|
86
83
|
* consumption. */
|
|
@@ -114,24 +111,6 @@ export type SidecarEventMap = {
|
|
|
114
111
|
agentAddress: string;
|
|
115
112
|
connectorState: ConnectorThreadState | null;
|
|
116
113
|
};
|
|
117
|
-
/** Awaited. Emitted per address after challenge verification
|
|
118
|
-
* succeeds and before the disconnect queue is flushed. Rejection
|
|
119
|
-
* rolls that address back from the routing table; earlier listeners
|
|
120
|
-
* in registration order have already executed and their side effects
|
|
121
|
-
* are not undone. A subsequent reconnect arriving mid-flight may
|
|
122
|
-
* supersede this one, so listeners must be idempotent. */
|
|
123
|
-
"agent.reconnected": {
|
|
124
|
-
agentAddress: string;
|
|
125
|
-
};
|
|
126
|
-
/** Awaited. Emitted per address after the wire layer has confirmed
|
|
127
|
-
* the sidecar's deploy ref is stale relative to the hub's current
|
|
128
|
-
* ref. The listener's job is to push a fresh deploy pack. The wire
|
|
129
|
-
* layer fires this only when staleness is confirmed; subscribing
|
|
130
|
-
* without a `lookupDeployRef` configured on the router will never
|
|
131
|
-
* deliver. */
|
|
132
|
-
"deploy.ref.stale": {
|
|
133
|
-
agentAddress: string;
|
|
134
|
-
};
|
|
135
114
|
};
|
|
136
115
|
export type SidecarEventType = keyof SidecarEventMap;
|
|
137
116
|
export type SidecarEventListener<T extends SidecarEventType> = (payload: SidecarEventMap[T]) => void | Promise<void>;
|
|
@@ -146,15 +125,19 @@ export type SidecarEventEmitter = {
|
|
|
146
125
|
};
|
|
147
126
|
export declare function createSidecarEmitter(): SidecarEventEmitter;
|
|
148
127
|
export type SidecarLookups = {
|
|
149
|
-
/** Returns the hex-encoded Ed25519 public key stored for the address,
|
|
150
|
-
* or `null` if the address is unknown. Used during the reconnect
|
|
151
|
-
* challenge to verify the sidecar's signature. */
|
|
152
|
-
lookupPublicKey?: (agentAddress: string) => Promise<string | null>;
|
|
153
128
|
/** Returns the hub's current deploy ref for the address, or `null` if
|
|
154
129
|
* no deploy state is tracked. The wire layer compares this against
|
|
155
130
|
* the sidecar's reported ref during reconnect and emits
|
|
156
131
|
* `deploy.ref.stale` only on mismatch. */
|
|
157
132
|
lookupDeployRef?: (agentAddress: string) => Promise<string | null>;
|
|
133
|
+
/** Re-resolves a reconnecting deployment's current credential delivery and
|
|
134
|
+
* reconciles the child, so a credential revoked, deleted, or rotated while
|
|
135
|
+
* the sidecar was disconnected is applied on reconnect (closing the offline
|
|
136
|
+
* window). Keyed by the run address. Fire-and-forget: the wire layer does not
|
|
137
|
+
* await it and it never throws -- a failure is logged and dropped, like the
|
|
138
|
+
* best-effort source-update pushes. No-ops for a run that persisted no
|
|
139
|
+
* credential refs. */
|
|
140
|
+
resyncCredentials?: (agentAddress: string) => void;
|
|
158
141
|
/** Persists a delivered outbound mail frame. Returns one row per
|
|
159
142
|
* persisted record; the wire layer attaches `raw` to each row and
|
|
160
143
|
* emits a `mail.persisted` event. */
|
|
@@ -163,6 +146,37 @@ export type SidecarLookups = {
|
|
|
163
146
|
recipients: string[];
|
|
164
147
|
raw: Uint8Array;
|
|
165
148
|
}) => Promise<SidecarMailPersistedRow[]>;
|
|
149
|
+
/** Resolves the hub-held public key a sender address signs with, hex-encoded,
|
|
150
|
+
* or `null` when the sender has no resolvable key (a run whose deploy is not
|
|
151
|
+
* yet acked, an address matching no known principal). The `mail.inbound` frame
|
|
152
|
+
* carries the result so a recipient verifies the signature locally against the
|
|
153
|
+
* hub-resolved key rather than the message's own spoofable From. Returns only
|
|
154
|
+
* the key string, not its source, so the recipient cannot branch on sender
|
|
155
|
+
* kind.
|
|
156
|
+
*
|
|
157
|
+
* Best-effort: this NEVER throws. A resolution fault degrades to `null`
|
|
158
|
+
* (logged at ERROR by the resolver), so a key-resolution problem can never
|
|
159
|
+
* block mail delivery. The strict, throwing resolver is `resolveSenderKey`
|
|
160
|
+
* in `@intx/db`; the reconnect reconciliation consumes it through
|
|
161
|
+
* `resolveSenderKeyStrict` below, which needs to tell a fault from a genuine
|
|
162
|
+
* absence. */
|
|
163
|
+
resolveSenderKey?: (address: string) => Promise<string | null>;
|
|
164
|
+
/** Strict sibling of `resolveSenderKey` for the reconnect reconciliation. The
|
|
165
|
+
* register/reconnect handler re-resolves each reported cached sender and acts
|
|
166
|
+
* on the three-way outcome the best-effort resolver collapses:
|
|
167
|
+
* - returns the hex key when the sender resolves -> push `sender.key.refresh`;
|
|
168
|
+
* - returns `null` for a CONFIRMED absence (no matching principal = a deleted
|
|
169
|
+
* sender) -> push `sender.key.evict`;
|
|
170
|
+
* - THROWS on a fault (ambiguous address, a keyless-principal invariant
|
|
171
|
+
* break, a DB error) -> keep the stale cached key, evict nothing.
|
|
172
|
+
* Never evicting on a throw is load-bearing: dropping a live key on a
|
|
173
|
+
* transient DB fault would be worse than doing nothing. Wraps the strict
|
|
174
|
+
* `resolveSenderKey` in `@intx/db` (unwrapped to the key string). Note the
|
|
175
|
+
* naming inversion: in `@intx/db` the STRICT resolver is `resolveSenderKey`
|
|
176
|
+
* and the best-effort one is `resolveFrameSenderKey`, whereas on this lookup
|
|
177
|
+
* type the best-effort field is `resolveSenderKey` and this is the strict
|
|
178
|
+
* one. */
|
|
179
|
+
resolveSenderKeyStrict?: (address: string) => Promise<string | null>;
|
|
166
180
|
/** Co-writes the `signal_correlation` routing row and the `approval` row
|
|
167
181
|
* for a suspending workflow agent step, in one transaction. Called from
|
|
168
182
|
* the `signal.correlation.register` frame handler after the wire layer has
|
|
@@ -29,8 +29,6 @@ export function createSidecarEmitter() {
|
|
|
29
29
|
"mail.persisted": new Set(),
|
|
30
30
|
"mail.inbound.acknowledged": new Set(),
|
|
31
31
|
"agent.deploy.ack": new Set(),
|
|
32
|
-
"agent.reconnected": new Set(),
|
|
33
|
-
"deploy.ref.stale": new Set(),
|
|
34
32
|
"connector.state.changed": new Set(),
|
|
35
33
|
};
|
|
36
34
|
function on(type, listener) {
|