@drisp/cli 0.5.11 → 0.5.13
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/athena-gateway.js +248 -181
- package/dist/{chunk-2QMUBFZZ.js → chunk-BUNMENOT.js} +557 -446
- package/dist/cli.js +2 -2
- package/dist/dashboard-daemon.js +1 -1
- package/dist/hook-forwarder.js +5 -5
- package/package.json +1 -1
package/dist/athena-gateway.js
CHANGED
|
@@ -2039,6 +2039,39 @@ function instantiateAdapter(sidecar) {
|
|
|
2039
2039
|
return { ok: true, adapter: module.create(parsed.config, sidecar.instanceId) };
|
|
2040
2040
|
}
|
|
2041
2041
|
|
|
2042
|
+
// src/gateway/channelReconcilePlan.ts
|
|
2043
|
+
function planChannelReconciliation(input) {
|
|
2044
|
+
const actions = [];
|
|
2045
|
+
for (const error2 of input.loadErrors) {
|
|
2046
|
+
actions.push({
|
|
2047
|
+
kind: "load-error",
|
|
2048
|
+
id: pathIdFromSidecarPath(error2.path),
|
|
2049
|
+
path: error2.path,
|
|
2050
|
+
reason: error2.reason
|
|
2051
|
+
});
|
|
2052
|
+
}
|
|
2053
|
+
if (input.unregisterStale) {
|
|
2054
|
+
const desiredIds = new Set(
|
|
2055
|
+
input.desired.map((sidecar) => sidecar.instanceId)
|
|
2056
|
+
);
|
|
2057
|
+
for (const id of input.currentChannelIds) {
|
|
2058
|
+
if (desiredIds.has(id)) continue;
|
|
2059
|
+
actions.push({ kind: "unregister-stale", id });
|
|
2060
|
+
}
|
|
2061
|
+
}
|
|
2062
|
+
const currentIds = new Set(input.currentChannelIds);
|
|
2063
|
+
for (const sidecar of input.desired) {
|
|
2064
|
+
actions.push(
|
|
2065
|
+
currentIds.has(sidecar.instanceId) ? { kind: "replace", sidecar } : { kind: "register", sidecar }
|
|
2066
|
+
);
|
|
2067
|
+
}
|
|
2068
|
+
return { actions };
|
|
2069
|
+
}
|
|
2070
|
+
function pathIdFromSidecarPath(filePath) {
|
|
2071
|
+
const base = filePath.split(/[\\/]/).pop() ?? filePath;
|
|
2072
|
+
return base.endsWith(".json") ? base.slice(0, -".json".length) : base;
|
|
2073
|
+
}
|
|
2074
|
+
|
|
2042
2075
|
// src/gateway/channelSidecarReconciler.ts
|
|
2043
2076
|
var ChannelSidecarReconciler = class {
|
|
2044
2077
|
channelManager;
|
|
@@ -2056,124 +2089,120 @@ var ChannelSidecarReconciler = class {
|
|
|
2056
2089
|
this.stderr = opts.stderr ?? ((message) => process.stderr.write(message));
|
|
2057
2090
|
}
|
|
2058
2091
|
async reconcile(opts) {
|
|
2059
|
-
const results = [];
|
|
2060
2092
|
const { sidecars, errors } = this.loadSidecars(this.home);
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
const
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
const reason = errorReason(err);
|
|
2087
|
-
results.push({
|
|
2088
|
-
id: channel.id,
|
|
2093
|
+
const plan = planChannelReconciliation({
|
|
2094
|
+
desired: sidecars,
|
|
2095
|
+
currentChannelIds: this.channelManager.listChannels().map((channel) => channel.id),
|
|
2096
|
+
loadErrors: errors,
|
|
2097
|
+
unregisterStale: opts.unregisterStale
|
|
2098
|
+
});
|
|
2099
|
+
const outcomes = [];
|
|
2100
|
+
for (const action of plan.actions) {
|
|
2101
|
+
outcomes.push(await this.executeAction(action));
|
|
2102
|
+
}
|
|
2103
|
+
for (const outcome of outcomes) {
|
|
2104
|
+
if (!outcome.log) continue;
|
|
2105
|
+
const enabled = outcome.log.stream === "err" ? opts.logFailures : opts.logRegistrations;
|
|
2106
|
+
if (!enabled) continue;
|
|
2107
|
+
const write = outcome.log.stream === "err" ? this.stderr : this.stdout;
|
|
2108
|
+
write(outcome.log.message);
|
|
2109
|
+
}
|
|
2110
|
+
return { results: outcomes.map((outcome) => outcome.result) };
|
|
2111
|
+
}
|
|
2112
|
+
async executeAction(action) {
|
|
2113
|
+
switch (action.kind) {
|
|
2114
|
+
case "load-error":
|
|
2115
|
+
return {
|
|
2116
|
+
result: {
|
|
2117
|
+
id: action.id,
|
|
2089
2118
|
ok: false,
|
|
2090
2119
|
action: "failed",
|
|
2091
|
-
reason
|
|
2092
|
-
}
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2120
|
+
reason: action.reason
|
|
2121
|
+
},
|
|
2122
|
+
log: {
|
|
2123
|
+
stream: "err",
|
|
2124
|
+
message: `athena-gateway: skipping ${action.path}: ${action.reason}
|
|
2096
2125
|
`
|
|
2097
|
-
);
|
|
2098
2126
|
}
|
|
2127
|
+
};
|
|
2128
|
+
case "unregister-stale":
|
|
2129
|
+
return this.executeUnregisterStale(action.id);
|
|
2130
|
+
case "replace":
|
|
2131
|
+
return this.executeApply(action.sidecar, true);
|
|
2132
|
+
case "register":
|
|
2133
|
+
return this.executeApply(action.sidecar, false);
|
|
2134
|
+
}
|
|
2135
|
+
}
|
|
2136
|
+
async executeUnregisterStale(id) {
|
|
2137
|
+
try {
|
|
2138
|
+
await this.channelManager.unregister(id, "shutdown");
|
|
2139
|
+
return { result: { id, ok: true, action: "unregistered" } };
|
|
2140
|
+
} catch (err) {
|
|
2141
|
+
const reason = errorReason(err);
|
|
2142
|
+
return {
|
|
2143
|
+
result: { id, ok: false, action: "failed", reason },
|
|
2144
|
+
log: {
|
|
2145
|
+
stream: "err",
|
|
2146
|
+
message: `athena-gateway: unregister ${id} failed: ${reason}
|
|
2147
|
+
`
|
|
2099
2148
|
}
|
|
2100
|
-
}
|
|
2149
|
+
};
|
|
2101
2150
|
}
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
if (opts.logFailures) {
|
|
2116
|
-
this.stderr(
|
|
2117
|
-
`athena-gateway: unregister ${sidecar.instanceId} failed: ${reason}
|
|
2151
|
+
}
|
|
2152
|
+
async executeApply(sidecar, existed) {
|
|
2153
|
+
const id = sidecar.instanceId;
|
|
2154
|
+
if (existed) {
|
|
2155
|
+
try {
|
|
2156
|
+
await this.channelManager.unregister(id, "shutdown");
|
|
2157
|
+
} catch (err) {
|
|
2158
|
+
const reason = errorReason(err);
|
|
2159
|
+
return {
|
|
2160
|
+
result: { id, ok: false, action: "failed", reason },
|
|
2161
|
+
log: {
|
|
2162
|
+
stream: "err",
|
|
2163
|
+
message: `athena-gateway: unregister ${id} failed: ${reason}
|
|
2118
2164
|
`
|
|
2119
|
-
);
|
|
2120
2165
|
}
|
|
2121
|
-
|
|
2122
|
-
}
|
|
2166
|
+
};
|
|
2123
2167
|
}
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
if (opts.logFailures) {
|
|
2133
|
-
this.stderr(
|
|
2134
|
-
`athena-gateway: ${sidecar.instanceId}: ${built.reason}
|
|
2168
|
+
}
|
|
2169
|
+
const built = this.instantiateAdapter(sidecar);
|
|
2170
|
+
if (!built.ok) {
|
|
2171
|
+
return {
|
|
2172
|
+
result: { id, ok: false, action: "failed", reason: built.reason },
|
|
2173
|
+
log: {
|
|
2174
|
+
stream: "err",
|
|
2175
|
+
message: `athena-gateway: ${id}: ${built.reason}
|
|
2135
2176
|
`
|
|
2136
|
-
);
|
|
2137
2177
|
}
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
this.stdout(`athena-gateway: registered ${sidecar.instanceId}
|
|
2152
|
-
`);
|
|
2178
|
+
};
|
|
2179
|
+
}
|
|
2180
|
+
try {
|
|
2181
|
+
await this.channelManager.register(
|
|
2182
|
+
built.adapter,
|
|
2183
|
+
sidecar.attachmentId !== void 0 ? { attachmentId: sidecar.attachmentId } : {}
|
|
2184
|
+
);
|
|
2185
|
+
return {
|
|
2186
|
+
result: { id, ok: true, action: existed ? "replaced" : "registered" },
|
|
2187
|
+
log: {
|
|
2188
|
+
stream: "out",
|
|
2189
|
+
message: `athena-gateway: registered ${id}
|
|
2190
|
+
`
|
|
2153
2191
|
}
|
|
2154
|
-
}
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
if (opts.logFailures) {
|
|
2163
|
-
this.stderr(
|
|
2164
|
-
`athena-gateway: register ${sidecar.instanceId} failed: ${reason}
|
|
2192
|
+
};
|
|
2193
|
+
} catch (err) {
|
|
2194
|
+
const reason = errorReason(err);
|
|
2195
|
+
return {
|
|
2196
|
+
result: { id, ok: false, action: "failed", reason },
|
|
2197
|
+
log: {
|
|
2198
|
+
stream: "err",
|
|
2199
|
+
message: `athena-gateway: register ${id} failed: ${reason}
|
|
2165
2200
|
`
|
|
2166
|
-
);
|
|
2167
2201
|
}
|
|
2168
|
-
}
|
|
2202
|
+
};
|
|
2169
2203
|
}
|
|
2170
|
-
return { results };
|
|
2171
2204
|
}
|
|
2172
2205
|
};
|
|
2173
|
-
function pathIdFromSidecarPath(filePath) {
|
|
2174
|
-
const base = filePath.split(/[\\/]/).pop() ?? filePath;
|
|
2175
|
-
return base.endsWith(".json") ? base.slice(0, -".json".length) : base;
|
|
2176
|
-
}
|
|
2177
2206
|
function errorReason(err) {
|
|
2178
2207
|
return err instanceof Error ? err.message : String(err);
|
|
2179
2208
|
}
|
|
@@ -2248,7 +2277,14 @@ var RuntimeBindingStore = class {
|
|
|
2248
2277
|
epoch,
|
|
2249
2278
|
...maybeLastRebindAt(lastRebindAt)
|
|
2250
2279
|
};
|
|
2251
|
-
const
|
|
2280
|
+
const push = input.push ?? existing?.push ?? null;
|
|
2281
|
+
const slot = existing ? { ...existing, runtime, binding: newBinding, push } : {
|
|
2282
|
+
runtime,
|
|
2283
|
+
binding: newBinding,
|
|
2284
|
+
push,
|
|
2285
|
+
staleTimer: null,
|
|
2286
|
+
staleSince: null
|
|
2287
|
+
};
|
|
2252
2288
|
this.clearStaleTimerForSlot(slot);
|
|
2253
2289
|
this.slots.set(key, slot);
|
|
2254
2290
|
if (wasStale && staleSince !== null) {
|
|
@@ -2271,9 +2307,10 @@ var RuntimeBindingStore = class {
|
|
|
2271
2307
|
this.observers.onRuntimeConnectionLost?.({ runtimeId, graceful: true });
|
|
2272
2308
|
}
|
|
2273
2309
|
/**
|
|
2274
|
-
* Called when the transport connection closes.
|
|
2275
|
-
*
|
|
2276
|
-
*
|
|
2310
|
+
* Called when the transport connection closes. Drops the slot's push handle
|
|
2311
|
+
* (immediately deleting the slot when no grace period is configured, otherwise
|
|
2312
|
+
* marking it stale). Returns the runtimeId if the close matched a current
|
|
2313
|
+
* binding; returns null if the connectionId was not recognised.
|
|
2277
2314
|
*/
|
|
2278
2315
|
notifyConnectionClosed(connectionId) {
|
|
2279
2316
|
const entry = this.findSlotByConnectionId(connectionId);
|
|
@@ -2294,6 +2331,7 @@ var RuntimeBindingStore = class {
|
|
|
2294
2331
|
this.observers.onRuntimeConnectionLost?.({ runtimeId, graceful: false });
|
|
2295
2332
|
return runtimeId;
|
|
2296
2333
|
}
|
|
2334
|
+
slot.push = null;
|
|
2297
2335
|
slot.staleSince = now;
|
|
2298
2336
|
slot.staleTimer = setTimeout(() => {
|
|
2299
2337
|
this.expireStaleBinding(key, runtimeId);
|
|
@@ -2328,6 +2366,28 @@ var RuntimeBindingStore = class {
|
|
|
2328
2366
|
const entry = this.findSlotByConnectionId(connectionId);
|
|
2329
2367
|
return entry ? entry.slot.runtime.runtimeId : null;
|
|
2330
2368
|
}
|
|
2369
|
+
/**
|
|
2370
|
+
* Deliver a control-push envelope to the runtime occupying `key`. Returns
|
|
2371
|
+
* false (and pushes nothing) when the slot is empty or its connection has
|
|
2372
|
+
* already been lost.
|
|
2373
|
+
*/
|
|
2374
|
+
pushTo(key, env) {
|
|
2375
|
+
const slot = this.slots.get(key);
|
|
2376
|
+
if (!slot || !slot.push) return false;
|
|
2377
|
+
slot.push(env);
|
|
2378
|
+
return true;
|
|
2379
|
+
}
|
|
2380
|
+
/**
|
|
2381
|
+
* Reportable state for every slot — both attachment-keyed and the legacy
|
|
2382
|
+
* fallback — so status reporting sees all registered runtimes.
|
|
2383
|
+
*/
|
|
2384
|
+
snapshot() {
|
|
2385
|
+
const entries = [];
|
|
2386
|
+
for (const [key, slot] of this.slots) {
|
|
2387
|
+
entries.push({ key, runtime: slot.runtime, binding: slot.binding });
|
|
2388
|
+
}
|
|
2389
|
+
return entries;
|
|
2390
|
+
}
|
|
2331
2391
|
/**
|
|
2332
2392
|
* Returns the attachment slot key (or `undefined` for the legacy slot) that
|
|
2333
2393
|
* holds the given runtime, or `null` if no slot does. Lets callers
|
|
@@ -2380,7 +2440,7 @@ var cachedVersion = null;
|
|
|
2380
2440
|
function readVersion() {
|
|
2381
2441
|
if (cachedVersion !== null) return cachedVersion;
|
|
2382
2442
|
try {
|
|
2383
|
-
const injected = "0.5.
|
|
2443
|
+
const injected = "0.5.13";
|
|
2384
2444
|
if (typeof injected === "string" && injected.length > 0) {
|
|
2385
2445
|
cachedVersion = injected;
|
|
2386
2446
|
return cachedVersion;
|
|
@@ -2655,29 +2715,25 @@ function createDispatcher(deps) {
|
|
|
2655
2715
|
return handle;
|
|
2656
2716
|
}
|
|
2657
2717
|
function runtimeStatusEntries(pipeline) {
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
} : { state: "none" },
|
|
2678
|
-
pendingDispatchCount: pipeline.pendingDispatchCount()
|
|
2679
|
-
}
|
|
2680
|
-
];
|
|
2718
|
+
if (!pipeline) return [];
|
|
2719
|
+
return pipeline.snapshotRuntimes().map(({ runtime, binding }) => ({
|
|
2720
|
+
runtimeId: runtime.runtimeId,
|
|
2721
|
+
defaultAgentId: runtime.defaultAgentId,
|
|
2722
|
+
pid: runtime.pid,
|
|
2723
|
+
registeredAt: runtime.registeredAt,
|
|
2724
|
+
binding: binding?.state === "active" ? {
|
|
2725
|
+
state: "active",
|
|
2726
|
+
boundAt: binding.boundAt,
|
|
2727
|
+
epoch: binding.epoch,
|
|
2728
|
+
...maybeLastRebindAt(binding.lastRebindAt)
|
|
2729
|
+
} : binding?.state === "stale" ? {
|
|
2730
|
+
state: "stale",
|
|
2731
|
+
staleSince: binding.staleSince,
|
|
2732
|
+
epoch: binding.epoch,
|
|
2733
|
+
...maybeLastRebindAt(binding.lastRebindAt)
|
|
2734
|
+
} : { state: "none" },
|
|
2735
|
+
pendingDispatchCount: pipeline.pendingDispatchCountFor(runtime.runtimeId)
|
|
2736
|
+
}));
|
|
2681
2737
|
}
|
|
2682
2738
|
function ok(envelope, ts, payload) {
|
|
2683
2739
|
return { request_id: envelope.request_id, ts, ok: true, payload };
|
|
@@ -2959,13 +3015,6 @@ function deriveSessionKey(loc) {
|
|
|
2959
3015
|
|
|
2960
3016
|
// src/gateway/sessionRegistry.ts
|
|
2961
3017
|
import { randomUUID } from "crypto";
|
|
2962
|
-
var UnknownDispatchError = class extends Error {
|
|
2963
|
-
code = "unknown_dispatch";
|
|
2964
|
-
constructor(id) {
|
|
2965
|
-
super(`unknown dispatchId: ${id}`);
|
|
2966
|
-
this.name = "UnknownDispatchError";
|
|
2967
|
-
}
|
|
2968
|
-
};
|
|
2969
3018
|
var SessionRegistry = class {
|
|
2970
3019
|
dispatches = /* @__PURE__ */ new Map();
|
|
2971
3020
|
idFactory;
|
|
@@ -2980,25 +3029,54 @@ var SessionRegistry = class {
|
|
|
2980
3029
|
dispatchId,
|
|
2981
3030
|
sessionKey: input.sessionKey,
|
|
2982
3031
|
agentId: input.agentId,
|
|
3032
|
+
runtimeId: input.runtimeId,
|
|
3033
|
+
...input.attachmentKey !== void 0 ? { attachmentKey: input.attachmentKey } : {},
|
|
2983
3034
|
location: input.location,
|
|
2984
3035
|
createdAt: this.now()
|
|
2985
3036
|
};
|
|
2986
3037
|
this.dispatches.set(dispatchId, entry);
|
|
2987
3038
|
return entry;
|
|
2988
3039
|
}
|
|
2989
|
-
|
|
3040
|
+
/**
|
|
3041
|
+
* Resolve a parked turn for the runtime claiming to complete it. The entry is
|
|
3042
|
+
* consumed only when the claiming runtime matches the one the turn was
|
|
3043
|
+
* dispatched to — a mismatched runtime cannot cancel or steal another
|
|
3044
|
+
* runtime's turn, and an unknown id is reported rather than thrown.
|
|
3045
|
+
*/
|
|
3046
|
+
completeDispatch(dispatchId, by) {
|
|
2990
3047
|
const entry = this.dispatches.get(dispatchId);
|
|
2991
3048
|
if (!entry) {
|
|
2992
|
-
|
|
3049
|
+
return { kind: "unknown" };
|
|
3050
|
+
}
|
|
3051
|
+
if (entry.runtimeId !== by.runtimeId) {
|
|
3052
|
+
return { kind: "runtime_mismatch", entry };
|
|
2993
3053
|
}
|
|
2994
3054
|
this.dispatches.delete(dispatchId);
|
|
2995
|
-
return entry;
|
|
3055
|
+
return { kind: "completed", entry };
|
|
2996
3056
|
}
|
|
2997
3057
|
pendingDispatchCount() {
|
|
2998
3058
|
return this.dispatches.size;
|
|
2999
3059
|
}
|
|
3000
|
-
|
|
3001
|
-
|
|
3060
|
+
/** Number of parked turns owned by the given runtime. */
|
|
3061
|
+
pendingDispatchCountFor(runtimeId) {
|
|
3062
|
+
let count = 0;
|
|
3063
|
+
for (const entry of this.dispatches.values()) {
|
|
3064
|
+
if (entry.runtimeId === runtimeId) count += 1;
|
|
3065
|
+
}
|
|
3066
|
+
return count;
|
|
3067
|
+
}
|
|
3068
|
+
/**
|
|
3069
|
+
* Remove only the parked turns owned by the given runtime, leaving every other
|
|
3070
|
+
* runtime's in-flight dispatches intact. Used when a single Registered runtime
|
|
3071
|
+
* unregisters or its connection is lost — clearing the correct slot's turns
|
|
3072
|
+
* without a global wipe.
|
|
3073
|
+
*/
|
|
3074
|
+
clearDispatchesFor(runtimeId) {
|
|
3075
|
+
for (const [dispatchId, entry] of this.dispatches) {
|
|
3076
|
+
if (entry.runtimeId === runtimeId) {
|
|
3077
|
+
this.dispatches.delete(dispatchId);
|
|
3078
|
+
}
|
|
3079
|
+
}
|
|
3002
3080
|
}
|
|
3003
3081
|
};
|
|
3004
3082
|
|
|
@@ -3125,8 +3203,6 @@ var DispatchPipeline = class {
|
|
|
3125
3203
|
log;
|
|
3126
3204
|
now;
|
|
3127
3205
|
idFactory;
|
|
3128
|
-
pushes = /* @__PURE__ */ new Map();
|
|
3129
|
-
connectionToKey = /* @__PURE__ */ new Map();
|
|
3130
3206
|
constructor(opts) {
|
|
3131
3207
|
this.bindingStore = new RuntimeBindingStore({
|
|
3132
3208
|
gracePeriodMs: opts.gracePeriodMs,
|
|
@@ -3207,6 +3283,8 @@ var DispatchPipeline = class {
|
|
|
3207
3283
|
const entry = this.registry.beginDispatch({
|
|
3208
3284
|
sessionKey,
|
|
3209
3285
|
agentId,
|
|
3286
|
+
runtimeId: current.runtimeId,
|
|
3287
|
+
...key !== void 0 ? { attachmentKey: key } : {},
|
|
3210
3288
|
location: inbound.location
|
|
3211
3289
|
});
|
|
3212
3290
|
this.pushDispatch(key, {
|
|
@@ -3218,9 +3296,7 @@ var DispatchPipeline = class {
|
|
|
3218
3296
|
return { kind: "dispatched", dispatchId: entry.dispatchId, sessionKey };
|
|
3219
3297
|
}
|
|
3220
3298
|
pushDispatch(key, payload) {
|
|
3221
|
-
|
|
3222
|
-
if (!handle) return;
|
|
3223
|
-
handle.push({
|
|
3299
|
+
this.bindingStore.pushTo(key, {
|
|
3224
3300
|
push_id: this.idFactory(),
|
|
3225
3301
|
ts: this.now(),
|
|
3226
3302
|
kind: "session.dispatch.turn",
|
|
@@ -3235,17 +3311,9 @@ var DispatchPipeline = class {
|
|
|
3235
3311
|
defaultAgentId: input.defaultAgentId,
|
|
3236
3312
|
pid: input.pid,
|
|
3237
3313
|
connectionId: input.connectionId,
|
|
3314
|
+
push: input.push,
|
|
3238
3315
|
...input.attachmentId !== void 0 ? { attachmentId: input.attachmentId } : {}
|
|
3239
3316
|
});
|
|
3240
|
-
const previous = this.pushes.get(key);
|
|
3241
|
-
if (previous && previous.connectionId !== input.connectionId) {
|
|
3242
|
-
this.connectionToKey.delete(previous.connectionId);
|
|
3243
|
-
}
|
|
3244
|
-
this.pushes.set(key, {
|
|
3245
|
-
connectionId: input.connectionId,
|
|
3246
|
-
push: input.push
|
|
3247
|
-
});
|
|
3248
|
-
this.connectionToKey.set(input.connectionId, key);
|
|
3249
3317
|
writeGatewayTrace(
|
|
3250
3318
|
`pipeline registered runtime runtimeId=${input.runtimeId} connectionId=${input.connectionId}`
|
|
3251
3319
|
);
|
|
@@ -3253,27 +3321,16 @@ var DispatchPipeline = class {
|
|
|
3253
3321
|
return { registeredAt: result.registeredAt };
|
|
3254
3322
|
}
|
|
3255
3323
|
unregisterRuntime(runtimeId) {
|
|
3256
|
-
const slot = this.findSlotByRuntimeId(runtimeId);
|
|
3257
3324
|
this.bindingStore.unbind(runtimeId);
|
|
3258
|
-
this.registry.
|
|
3259
|
-
if (slot) {
|
|
3260
|
-
const handle = this.pushes.get(slot.key);
|
|
3261
|
-
this.pushes.delete(slot.key);
|
|
3262
|
-
if (handle) this.connectionToKey.delete(handle.connectionId);
|
|
3263
|
-
}
|
|
3325
|
+
this.registry.clearDispatchesFor(runtimeId);
|
|
3264
3326
|
writeGatewayTrace(`pipeline unregistered runtime runtimeId=${runtimeId}`);
|
|
3265
3327
|
}
|
|
3266
3328
|
notifyConnectionClosed(connectionId) {
|
|
3267
|
-
const key = this.connectionToKey.get(connectionId);
|
|
3268
3329
|
const runtimeId = this.bindingStore.notifyConnectionClosed(connectionId);
|
|
3269
3330
|
if (runtimeId === null) return;
|
|
3270
3331
|
writeGatewayTrace(
|
|
3271
3332
|
`pipeline runtime connection closed runtimeId=${runtimeId} connectionId=${connectionId}`
|
|
3272
3333
|
);
|
|
3273
|
-
if (key !== void 0 || this.pushes.has(key)) {
|
|
3274
|
-
this.pushes.delete(key);
|
|
3275
|
-
this.connectionToKey.delete(connectionId);
|
|
3276
|
-
}
|
|
3277
3334
|
}
|
|
3278
3335
|
/**
|
|
3279
3336
|
* Streaming run-event from a registered runtime to its outbound channel
|
|
@@ -3313,32 +3370,35 @@ var DispatchPipeline = class {
|
|
|
3313
3370
|
if (!slot) {
|
|
3314
3371
|
throw new Error("runtime mismatch on session.turn.complete");
|
|
3315
3372
|
}
|
|
3316
|
-
|
|
3317
|
-
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
|
|
3326
|
-
|
|
3373
|
+
const completion = this.registry.completeDispatch(payload.dispatchId, {
|
|
3374
|
+
runtimeId: payload.runtimeId
|
|
3375
|
+
});
|
|
3376
|
+
if (completion.kind === "unknown") {
|
|
3377
|
+
writeGatewayTrace(
|
|
3378
|
+
`pipeline turn.complete unknown dispatchId=${payload.dispatchId}`
|
|
3379
|
+
);
|
|
3380
|
+
return { delivered: false };
|
|
3381
|
+
}
|
|
3382
|
+
if (completion.kind === "runtime_mismatch") {
|
|
3383
|
+
writeGatewayTrace(
|
|
3384
|
+
`pipeline turn.complete runtime mismatch dispatchId=${payload.dispatchId} authorized=${completion.entry.runtimeId} claimed=${payload.runtimeId}`
|
|
3385
|
+
);
|
|
3386
|
+
return { delivered: false };
|
|
3327
3387
|
}
|
|
3328
|
-
const result = await this.sendOutbound(entry.location, payload);
|
|
3388
|
+
const result = await this.sendOutbound(completion.entry.location, payload);
|
|
3329
3389
|
writeGatewayTrace(
|
|
3330
3390
|
`pipeline sendOutbound delivered dispatchId=${payload.dispatchId} providerMessageId=${result.providerMessageId}`
|
|
3331
3391
|
);
|
|
3332
3392
|
return { delivered: true, providerMessageId: result.providerMessageId };
|
|
3333
3393
|
}
|
|
3334
|
-
async sendOutbound(
|
|
3394
|
+
async sendOutbound(parkedLocation, payload) {
|
|
3335
3395
|
const out = {
|
|
3336
|
-
location:
|
|
3396
|
+
location: parkedLocation,
|
|
3337
3397
|
text: payload.text,
|
|
3338
3398
|
idempotencyKey: payload.idempotencyKey
|
|
3339
3399
|
};
|
|
3340
3400
|
const result = await this.outboundDispatcher.dispatch(
|
|
3341
|
-
|
|
3401
|
+
parkedLocation.channelId,
|
|
3342
3402
|
out
|
|
3343
3403
|
);
|
|
3344
3404
|
if (result.kind === "sent") return result.result;
|
|
@@ -3373,6 +3433,10 @@ var DispatchPipeline = class {
|
|
|
3373
3433
|
getCurrentRuntime() {
|
|
3374
3434
|
return this.bindingStore.getCurrent();
|
|
3375
3435
|
}
|
|
3436
|
+
/** Reportable state for every registered-runtime slot (legacy + attachment-keyed). */
|
|
3437
|
+
snapshotRuntimes() {
|
|
3438
|
+
return this.bindingStore.snapshot();
|
|
3439
|
+
}
|
|
3376
3440
|
getCurrentRuntimeByAttachment(attachmentId) {
|
|
3377
3441
|
return this.bindingStore.getCurrentByAttachment(attachmentId);
|
|
3378
3442
|
}
|
|
@@ -3388,6 +3452,9 @@ var DispatchPipeline = class {
|
|
|
3388
3452
|
pendingDispatchCount() {
|
|
3389
3453
|
return this.registry.pendingDispatchCount();
|
|
3390
3454
|
}
|
|
3455
|
+
pendingDispatchCountFor(runtimeId) {
|
|
3456
|
+
return this.registry.pendingDispatchCountFor(runtimeId);
|
|
3457
|
+
}
|
|
3391
3458
|
pendingInboundCount() {
|
|
3392
3459
|
return this.inboundQueue.size();
|
|
3393
3460
|
}
|