@intx/hub-sessions 0.2.2 → 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/README.md +3 -5
- package/dist/agent-repo.d.ts +23 -7
- package/dist/agent-repo.js +19 -6
- package/dist/agent-state-kind.js +18 -63
- package/dist/asset-service.d.ts +1 -20
- package/dist/asset-service.js +19 -97
- package/dist/committed-source-tree.d.ts +10 -0
- package/dist/committed-source-tree.js +35 -0
- package/dist/credential-push.d.ts +52 -7
- package/dist/credential-push.js +178 -22
- package/dist/event-collector-registry.d.ts +3 -2
- package/dist/event-collector-registry.js +42 -13
- package/dist/event-collector.d.ts +12 -2
- package/dist/event-collector.js +45 -4
- package/dist/hub-session-lookups.d.ts +125 -7
- package/dist/hub-session-lookups.js +541 -86
- package/dist/hub-session-orchestrator.d.ts +2 -3
- package/dist/hub-session-orchestrator.js +22 -56
- package/dist/index.d.ts +19 -9
- package/dist/index.js +16 -7
- package/dist/reconciliation-scheduler.d.ts +14 -0
- package/dist/reconciliation-scheduler.js +55 -0
- package/dist/repo-store/index.d.ts +2 -1
- package/dist/repo-store/index.js +1 -0
- package/dist/repo-store/store.d.ts +1 -1
- package/dist/repo-store/store.js +138 -1
- package/dist/repo-store/subscribe-kind.d.ts +6 -3
- package/dist/repo-store/subscribe-kind.js +42 -77
- package/dist/repo-store/types.d.ts +94 -6
- 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 +252 -126
- package/dist/session-service.js +851 -624
- 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 +101 -0
- package/dist/sidecar-allocation/contracts.js +26 -0
- package/dist/sidecar-allocation/index.d.ts +5 -0
- package/dist/sidecar-allocation/index.js +4 -0
- 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 +24 -0
- package/dist/sidecar-allocation/plugin-registry.js +61 -0
- package/dist/sidecar-allocation/reconciler.d.ts +54 -0
- package/dist/sidecar-allocation/reconciler.js +825 -0
- package/dist/skill-kind.js +12 -62
- package/dist/substrate.d.ts +3 -3
- package/dist/substrate.js +1 -1
- package/dist/workflow-allocation-service.d.ts +64 -0
- package/dist/workflow-allocation-service.js +554 -0
- package/dist/workflow-closure-resolution.d.ts +106 -0
- package/dist/workflow-closure-resolution.js +123 -0
- package/dist/workflow-definition-ensure.d.ts +24 -0
- package/dist/workflow-definition-ensure.js +75 -0
- package/dist/workflow-dispatch-service.d.ts +42 -0
- package/dist/workflow-dispatch-service.js +209 -0
- package/dist/workflow-dispatch-settlement.d.ts +29 -0
- package/dist/workflow-dispatch-settlement.js +140 -0
- package/dist/workflow-kind.d.ts +29 -1
- package/dist/workflow-kind.js +143 -139
- package/dist/workflow-probe-gate.d.ts +286 -0
- package/dist/workflow-probe-gate.js +382 -0
- package/dist/workflow-run-kind.d.ts +239 -32
- package/dist/workflow-run-kind.js +874 -188
- package/dist/workflow-run-reader.d.ts +1 -1
- package/dist/workflow-run-reader.js +3 -7
- package/dist/workflow-run-restore.d.ts +16 -0
- package/dist/workflow-run-restore.js +30 -0
- package/dist/workflow-source-closure.d.ts +35 -0
- package/dist/workflow-source-closure.js +342 -0
- package/dist/workflow-source-pins.d.ts +8 -0
- package/dist/workflow-source-pins.js +14 -0
- package/dist/ws/index.d.ts +3 -3
- package/dist/ws/index.js +2 -2
- package/dist/ws/pending-tracker.d.ts +93 -0
- package/dist/ws/pending-tracker.js +132 -0
- package/dist/ws/sidecar-events.d.ts +139 -37
- package/dist/ws/sidecar-events.js +2 -2
- package/dist/ws/sidecar-handler.d.ts +218 -60
- package/dist/ws/sidecar-handler.js +1713 -732
- 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.d.ts +3 -1
- package/dist/ws/sidecar-token-authenticator.js +78 -7
- package/package.json +14 -13
- package/dist/available-skills-stanza.d.ts +0 -21
- package/dist/available-skills-stanza.js +0 -32
|
@@ -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
|
+
}
|
|
@@ -1,15 +1,41 @@
|
|
|
1
|
-
import type { PackRejectReason, RepoId } from "@intx/types/sidecar";
|
|
2
|
-
import type { ConnectorThreadState } from "@intx/types/runtime";
|
|
1
|
+
import type { PackRejectReason, RepoId, RunGrantsFrame } from "@intx/types/sidecar";
|
|
2
|
+
import type { ApprovalSnapshot, ConnectorThreadState } from "@intx/types/runtime";
|
|
3
|
+
import type { SignalKind } from "@intx/types";
|
|
3
4
|
export type SidecarMailPersistedRow = {
|
|
4
5
|
id: string;
|
|
5
6
|
createdAt: Date;
|
|
6
7
|
direction: "inbound" | "outbound";
|
|
7
|
-
|
|
8
|
+
runId: string | null;
|
|
8
9
|
address: string;
|
|
9
10
|
};
|
|
10
11
|
export type SidecarMailPersistedPayload = SidecarMailPersistedRow & {
|
|
11
12
|
raw: Uint8Array;
|
|
12
13
|
};
|
|
14
|
+
/** Authenticated connection scope attached to a workflow-run pack. */
|
|
15
|
+
export type WorkflowRunPackSource = {
|
|
16
|
+
readonly kind: "allocated";
|
|
17
|
+
readonly agentAddress: string;
|
|
18
|
+
readonly allocationId: string;
|
|
19
|
+
readonly anchorRunId: string;
|
|
20
|
+
readonly generation: number;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Outcome of reserving a mail-triggered run's grants. `skip` means the
|
|
24
|
+
* recipient names no workflow deployment; `rejected` means the deployment's
|
|
25
|
+
* stable run is terminal or its requirements cannot be authorized;
|
|
26
|
+
* `materialized` carries the canonical persisted wire rows.
|
|
27
|
+
*/
|
|
28
|
+
export type MailTriggeredRunGrantsResult = {
|
|
29
|
+
outcome: "skip";
|
|
30
|
+
} | {
|
|
31
|
+
outcome: "rejected";
|
|
32
|
+
status: 403 | 409;
|
|
33
|
+
code: string;
|
|
34
|
+
message: string;
|
|
35
|
+
} | {
|
|
36
|
+
outcome: "materialized";
|
|
37
|
+
stepGrants: RunGrantsFrame["stepGrants"];
|
|
38
|
+
};
|
|
13
39
|
export type SidecarEventMap = {
|
|
14
40
|
/** Notification. Emitted for every agent.event frame the wire layer
|
|
15
41
|
* decodes. The wire layer also forwards the event to in-process agent
|
|
@@ -21,11 +47,21 @@ export type SidecarEventMap = {
|
|
|
21
47
|
event: unknown;
|
|
22
48
|
};
|
|
23
49
|
/** Notification. Emitted once when a sidecar's connection closes,
|
|
24
|
-
* carrying every address the connection owned --
|
|
25
|
-
*
|
|
26
|
-
*
|
|
50
|
+
* carrying every address the connection owned -- session addresses
|
|
51
|
+
* and hub-minted workflow-substrate deployment addresses alike --
|
|
52
|
+
* so lifecycle teardown covers both. */
|
|
27
53
|
"sidecar.disconnect": {
|
|
28
54
|
ownedAddresses: string[];
|
|
55
|
+
/** Present only when the closing socket was the current allocated owner. */
|
|
56
|
+
allocated?: {
|
|
57
|
+
allocationId: string;
|
|
58
|
+
generation: number;
|
|
59
|
+
};
|
|
60
|
+
};
|
|
61
|
+
/** Notification after the exact authenticated allocation generation registers. */
|
|
62
|
+
"sidecar.allocated.connected": {
|
|
63
|
+
allocationId: string;
|
|
64
|
+
generation: number;
|
|
29
65
|
};
|
|
30
66
|
/** Notification. Emitted when a mail.outbound frame from a sidecar
|
|
31
67
|
* names recipients that the wire layer could not deliver locally and
|
|
@@ -40,11 +76,30 @@ export type SidecarEventMap = {
|
|
|
40
76
|
* the rows; this event fires for each so subscribers can react
|
|
41
77
|
* per-row (e.g. dispatch a delivered event). */
|
|
42
78
|
"mail.persisted": SidecarMailPersistedPayload;
|
|
79
|
+
/** Notification after a sidecar confirms a mail trigger is in its durable
|
|
80
|
+
* local inbox. For a provisioned worker, `allocated` identifies the exact
|
|
81
|
+
* generation that acknowledged the message. This is not workflow
|
|
82
|
+
* settlement; the Hub retains the payload until the Git claim-check records
|
|
83
|
+
* consumption. */
|
|
84
|
+
"mail.inbound.acknowledged": {
|
|
85
|
+
agentAddress: string;
|
|
86
|
+
messageId: string;
|
|
87
|
+
allocated?: {
|
|
88
|
+
allocationId: string;
|
|
89
|
+
anchorRunId: string;
|
|
90
|
+
generation: number;
|
|
91
|
+
};
|
|
92
|
+
};
|
|
43
93
|
/** Awaited. Emitted when an agent.deploy.ack frame arrives. Rejection
|
|
44
94
|
* fails the pending deploy with the listener's error. */
|
|
45
95
|
"agent.deploy.ack": {
|
|
46
96
|
agentAddress: string;
|
|
47
97
|
publicKey: string;
|
|
98
|
+
allocated?: {
|
|
99
|
+
allocationId: string;
|
|
100
|
+
anchorRunId: string;
|
|
101
|
+
generation: number;
|
|
102
|
+
};
|
|
48
103
|
};
|
|
49
104
|
/** Notification. Emitted when the sidecar reports a change to an
|
|
50
105
|
* agent's connector-thread state. The wire layer caches the state
|
|
@@ -56,24 +111,6 @@ export type SidecarEventMap = {
|
|
|
56
111
|
agentAddress: string;
|
|
57
112
|
connectorState: ConnectorThreadState | null;
|
|
58
113
|
};
|
|
59
|
-
/** Awaited. Emitted per address after challenge verification
|
|
60
|
-
* succeeds and before the disconnect queue is flushed. Rejection
|
|
61
|
-
* rolls that address back from the routing table; earlier listeners
|
|
62
|
-
* in registration order have already executed and their side effects
|
|
63
|
-
* are not undone. A subsequent reconnect arriving mid-flight may
|
|
64
|
-
* supersede this one, so listeners must be idempotent. */
|
|
65
|
-
"agent.reconnected": {
|
|
66
|
-
agentAddress: string;
|
|
67
|
-
};
|
|
68
|
-
/** Awaited. Emitted per address after the wire layer has confirmed
|
|
69
|
-
* the sidecar's deploy ref is stale relative to the hub's current
|
|
70
|
-
* ref. The listener's job is to push a fresh deploy pack. The wire
|
|
71
|
-
* layer fires this only when staleness is confirmed; subscribing
|
|
72
|
-
* without a `lookupDeployRef` configured on the router will never
|
|
73
|
-
* deliver. */
|
|
74
|
-
"deploy.ref.stale": {
|
|
75
|
-
agentAddress: string;
|
|
76
|
-
};
|
|
77
114
|
};
|
|
78
115
|
export type SidecarEventType = keyof SidecarEventMap;
|
|
79
116
|
export type SidecarEventListener<T extends SidecarEventType> = (payload: SidecarEventMap[T]) => void | Promise<void>;
|
|
@@ -88,15 +125,19 @@ export type SidecarEventEmitter = {
|
|
|
88
125
|
};
|
|
89
126
|
export declare function createSidecarEmitter(): SidecarEventEmitter;
|
|
90
127
|
export type SidecarLookups = {
|
|
91
|
-
/** Returns the hex-encoded Ed25519 public key stored for the address,
|
|
92
|
-
* or `null` if the address is unknown. Used during the reconnect
|
|
93
|
-
* challenge to verify the sidecar's signature. */
|
|
94
|
-
lookupPublicKey?: (agentAddress: string) => Promise<string | null>;
|
|
95
128
|
/** Returns the hub's current deploy ref for the address, or `null` if
|
|
96
129
|
* no deploy state is tracked. The wire layer compares this against
|
|
97
130
|
* the sidecar's reported ref during reconnect and emits
|
|
98
131
|
* `deploy.ref.stale` only on mismatch. */
|
|
99
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;
|
|
100
141
|
/** Persists a delivered outbound mail frame. Returns one row per
|
|
101
142
|
* persisted record; the wire layer attaches `raw` to each row and
|
|
102
143
|
* emits a `mail.persisted` event. */
|
|
@@ -105,9 +146,73 @@ export type SidecarLookups = {
|
|
|
105
146
|
recipients: string[];
|
|
106
147
|
raw: Uint8Array;
|
|
107
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>;
|
|
180
|
+
/** Co-writes the `signal_correlation` routing row and the `approval` row
|
|
181
|
+
* for a suspending workflow agent step, in one transaction. Called from
|
|
182
|
+
* the `signal.correlation.register` frame handler after the wire layer has
|
|
183
|
+
* confirmed the sending sidecar owns `agentAddress`. Idempotent: a
|
|
184
|
+
* redelivered frame (reconnect, workflow-log replay, supervisor restart
|
|
185
|
+
* re-emitting) is a no-op, not an error. The wire layer does not carry
|
|
186
|
+
* `signalName`; the host derives it from `correlationId`. Resolves the
|
|
187
|
+
* tenancy from the workflow deployment the address names. */
|
|
188
|
+
registerSignalCorrelation?: (args: {
|
|
189
|
+
correlationId: string;
|
|
190
|
+
runId: string;
|
|
191
|
+
anchorRunId: string;
|
|
192
|
+
agentAddress: string;
|
|
193
|
+
kind: SignalKind;
|
|
194
|
+
approvalSnapshot: ApprovalSnapshot;
|
|
195
|
+
}) => Promise<void>;
|
|
196
|
+
/** Reserves a mail-triggered workflow run's grants from the receiving
|
|
197
|
+
* deployment's definition, returning a discriminated result the
|
|
198
|
+
* `mail.outbound` handler orders against delivery. Called for each recipient
|
|
199
|
+
* that is a workflow deployment. The `runId` is the deployment's stable
|
|
200
|
+
* address-derived run id.
|
|
201
|
+
*
|
|
202
|
+
* On `materialized`, `stepGrants` are already persisted and the caller sends
|
|
203
|
+
* them ahead of the inbound mail. Reservation is idempotent on the runId, so
|
|
204
|
+
* a redelivered inbound mail neither double-mints nor throws. On `skip` the
|
|
205
|
+
* address names no deployed workflow deployment, so no grants are sent and
|
|
206
|
+
* the mail still forwards. On `rejected` the stable run is terminal or a
|
|
207
|
+
* declared requirement's authority is insufficient; the caller fails the
|
|
208
|
+
* mail closed for that recipient. */
|
|
209
|
+
materializeMailTriggeredRunGrants?: (args: {
|
|
210
|
+
agentAddress: string;
|
|
211
|
+
runId: string;
|
|
212
|
+
}) => Promise<MailTriggeredRunGrantsResult>;
|
|
108
213
|
/** Ingests a received agent-state pack and returns whether the wire
|
|
109
214
|
* layer should ack or reject the pack to the sidecar. `repoId.kind`
|
|
110
|
-
* is `"agent-state"` and `repoId.id` is the
|
|
215
|
+
* is `"agent-state"` and `repoId.id` is the run address. The wire
|
|
111
216
|
* layer dispatches on `repoId.kind` against the receive lookups
|
|
112
217
|
* before calling either; this lookup must reject any pack whose
|
|
113
218
|
* `repoId.kind` is not `"agent-state"`. */
|
|
@@ -118,14 +223,11 @@ export type SidecarLookups = {
|
|
|
118
223
|
reason: PackRejectReason;
|
|
119
224
|
}>;
|
|
120
225
|
/** Ingests a received workflow-run pack and returns whether the wire
|
|
121
|
-
* layer should ack or reject the pack to the sidecar. `
|
|
122
|
-
*
|
|
123
|
-
*
|
|
124
|
-
*
|
|
125
|
-
|
|
126
|
-
* this lookup must reject any pack whose `repoId.kind` is not
|
|
127
|
-
* `"workflow-run"`. */
|
|
128
|
-
receiveWorkflowRunPack?: (repoId: RepoId, pack: Uint8Array, ref: string, commitSha: string) => Promise<{
|
|
226
|
+
* layer should ack or reject the pack to the sidecar. `source` is derived
|
|
227
|
+
* from the authenticated socket, never from the frame. The lookup must
|
|
228
|
+
* revalidate that source against durable deployment/allocation ownership
|
|
229
|
+
* before advancing the Git ref. */
|
|
230
|
+
receiveWorkflowRunPack?: (repoId: RepoId, pack: Uint8Array, ref: string, commitSha: string, source: WorkflowRunPackSource) => Promise<{
|
|
129
231
|
accepted: true;
|
|
130
232
|
} | {
|
|
131
233
|
accepted: false;
|
|
@@ -24,11 +24,11 @@ export function createSidecarEmitter() {
|
|
|
24
24
|
const listeners = {
|
|
25
25
|
"agent.event": new Set(),
|
|
26
26
|
"sidecar.disconnect": new Set(),
|
|
27
|
+
"sidecar.allocated.connected": new Set(),
|
|
27
28
|
"mail.outbound.undelivered": new Set(),
|
|
28
29
|
"mail.persisted": new Set(),
|
|
30
|
+
"mail.inbound.acknowledged": new Set(),
|
|
29
31
|
"agent.deploy.ack": new Set(),
|
|
30
|
-
"agent.reconnected": new Set(),
|
|
31
|
-
"deploy.ref.stale": new Set(),
|
|
32
32
|
"connector.state.changed": new Set(),
|
|
33
33
|
};
|
|
34
34
|
function on(type, listener) {
|