@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.
Files changed (67) hide show
  1. package/dist/agent-repo.d.ts +14 -2
  2. package/dist/agent-repo.js +17 -4
  3. package/dist/agent-state-kind.js +14 -63
  4. package/dist/asset-service.js +14 -10
  5. package/dist/credential-push.d.ts +48 -4
  6. package/dist/credential-push.js +138 -6
  7. package/dist/event-collector-registry.d.ts +2 -1
  8. package/dist/event-collector-registry.js +38 -9
  9. package/dist/event-collector.d.ts +11 -1
  10. package/dist/event-collector.js +36 -3
  11. package/dist/hub-session-lookups.d.ts +1 -1
  12. package/dist/hub-session-lookups.js +68 -72
  13. package/dist/hub-session-orchestrator.d.ts +2 -3
  14. package/dist/hub-session-orchestrator.js +13 -12
  15. package/dist/index.d.ts +7 -6
  16. package/dist/index.js +7 -6
  17. package/dist/reconciliation-scheduler.d.ts +14 -0
  18. package/dist/reconciliation-scheduler.js +55 -0
  19. package/dist/repo-store/index.d.ts +1 -0
  20. package/dist/repo-store/index.js +1 -0
  21. package/dist/repo-store/user-principal-gate.d.ts +26 -0
  22. package/dist/repo-store/user-principal-gate.js +78 -0
  23. package/dist/session-service.d.ts +66 -121
  24. package/dist/session-service.js +444 -411
  25. package/dist/sidecar-allocation/capability-policy.d.ts +27 -0
  26. package/dist/sidecar-allocation/capability-policy.js +124 -0
  27. package/dist/sidecar-allocation/contracts.d.ts +29 -6
  28. package/dist/sidecar-allocation/contracts.js +7 -2
  29. package/dist/sidecar-allocation/index.d.ts +4 -3
  30. package/dist/sidecar-allocation/index.js +3 -2
  31. package/dist/sidecar-allocation/operation.d.ts +10 -0
  32. package/dist/sidecar-allocation/operation.js +54 -0
  33. package/dist/sidecar-allocation/plugin-registry.d.ts +16 -3
  34. package/dist/sidecar-allocation/plugin-registry.js +36 -12
  35. package/dist/sidecar-allocation/reconciler.d.ts +16 -4
  36. package/dist/sidecar-allocation/reconciler.js +486 -92
  37. package/dist/skill-kind.js +8 -62
  38. package/dist/substrate.d.ts +1 -1
  39. package/dist/substrate.js +1 -1
  40. package/dist/workflow-allocation-service.d.ts +21 -15
  41. package/dist/workflow-allocation-service.js +440 -125
  42. package/dist/workflow-dispatch-service.d.ts +4 -2
  43. package/dist/workflow-dispatch-service.js +89 -26
  44. package/dist/workflow-kind.d.ts +12 -0
  45. package/dist/workflow-kind.js +17 -60
  46. package/dist/workflow-probe-gate.d.ts +99 -27
  47. package/dist/workflow-probe-gate.js +196 -21
  48. package/dist/workflow-run-kind.d.ts +112 -19
  49. package/dist/workflow-run-kind.js +626 -210
  50. package/dist/workflow-run-restore.d.ts +1 -0
  51. package/dist/workflow-run-restore.js +5 -1
  52. package/dist/workflow-source-pins.d.ts +8 -0
  53. package/dist/workflow-source-pins.js +14 -0
  54. package/dist/ws/index.d.ts +1 -1
  55. package/dist/ws/index.js +1 -1
  56. package/dist/ws/pending-tracker.d.ts +93 -0
  57. package/dist/ws/pending-tracker.js +132 -0
  58. package/dist/ws/sidecar-events.d.ts +43 -29
  59. package/dist/ws/sidecar-events.js +0 -2
  60. package/dist/ws/sidecar-handler.d.ts +122 -85
  61. package/dist/ws/sidecar-handler.js +925 -878
  62. package/dist/ws/sidecar-handler.test-helpers.d.ts +38 -0
  63. package/dist/ws/sidecar-handler.test-helpers.js +95 -0
  64. package/dist/ws/sidecar-token-authenticator.js +37 -23
  65. package/package.json +13 -13
  66. package/dist/sidecar-allocation/placement-policy.d.ts +0 -11
  67. package/dist/sidecar-allocation/placement-policy.js +0 -21
@@ -12,4 +12,5 @@ export declare function restoreWorkflowRunToAllocation(args: {
12
12
  allocationRouter: Pick<SidecarAllocationRouter, "sendWorkflowRunPackToAllocation">;
13
13
  allocationTarget: AllocatedSidecarTarget;
14
14
  agentAddress: string;
15
+ signal?: AbortSignal;
15
16
  }): Promise<void>;
@@ -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
- await allocationRouter.sendWorkflowRunPackToAllocation(allocationTarget, agentAddress, pack.pack, pack.ref, pack.commitSha);
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
+ }
@@ -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 -- challenged session
54
- * addresses and hub-minted workflow-substrate deployment addresses
55
- * alike -- so lifecycle teardown covers both. */
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 an exclusive worker, `allocated` identifies the exact
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) {