@moxt-ai/mobius 0.0.25 → 0.0.27
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-catalog/publish-agent-catalog.js +3 -3
- package/dist/agent-catalog/publish-agent-catalog.js.map +1 -1
- package/dist/cli.js +1194 -205
- package/dist/cli.js.map +4 -4
- package/dist/index.js +1186 -204
- package/dist/index.js.map +4 -4
- package/dist/network/environment-proxy.d.ts +1 -0
- package/dist/network/environment-proxy.d.ts.map +1 -1
- package/dist/network/environment-proxy.js +47 -0
- package/dist/network/environment-proxy.js.map +1 -1
- package/dist/prompt-artifact-resolver.d.ts.map +1 -1
- package/dist/prompt-artifact-resolver.js +7 -4
- package/dist/prompt-artifact-resolver.js.map +1 -1
- package/dist/relay-connection/connect.d.ts.map +1 -1
- package/dist/relay-connection/connect.js +19 -14
- package/dist/relay-connection/connect.js.map +1 -1
- package/dist/relay-connection/diagnostics.d.ts +11 -0
- package/dist/relay-connection/diagnostics.d.ts.map +1 -0
- package/dist/relay-connection/diagnostics.js +122 -0
- package/dist/relay-connection/diagnostics.js.map +1 -0
- package/dist/relay-connection/liveness.d.ts +1 -1
- package/dist/relay-connection/liveness.d.ts.map +1 -1
- package/dist/relay-connection/liveness.js +2 -1
- package/dist/relay-connection/liveness.js.map +1 -1
- package/dist/relay-connection/runtime-authentication.d.ts +3 -0
- package/dist/relay-connection/runtime-authentication.d.ts.map +1 -0
- package/dist/relay-connection/runtime-authentication.js +14 -0
- package/dist/relay-connection/runtime-authentication.js.map +1 -0
- package/dist/relay-connection/session-event-outbox.d.ts +34 -3
- package/dist/relay-connection/session-event-outbox.d.ts.map +1 -1
- package/dist/relay-connection/session-event-outbox.js +404 -13
- package/dist/relay-connection/session-event-outbox.js.map +1 -1
- package/dist/run-daemon.d.ts.map +1 -1
- package/dist/run-daemon.js +437 -151
- package/dist/run-daemon.js.map +1 -1
- package/dist/runtime.js +1186 -204
- package/dist/runtime.js.map +4 -4
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -11,7 +11,7 @@ import { mkdir as mkdir5 } from "node:fs/promises";
|
|
|
11
11
|
// package.json
|
|
12
12
|
var package_default = {
|
|
13
13
|
name: "@moxt-ai/mobius",
|
|
14
|
-
version: "0.0.
|
|
14
|
+
version: "0.0.27",
|
|
15
15
|
description: "Mobius CLI for pairing and running local AI agent services.",
|
|
16
16
|
type: "module",
|
|
17
17
|
bin: {
|
|
@@ -1578,7 +1578,53 @@ import { fetch } from "undici";
|
|
|
1578
1578
|
|
|
1579
1579
|
// src/network/environment-proxy.ts
|
|
1580
1580
|
import { EnvHttpProxyAgent } from "undici";
|
|
1581
|
+
var configuredHttpProxy = process.env["http_proxy"] ?? process.env["HTTP_PROXY"] ?? "";
|
|
1582
|
+
var configuredHttpsProxy = process.env["https_proxy"] ?? process.env["HTTPS_PROXY"] ?? configuredHttpProxy;
|
|
1581
1583
|
var environmentProxyDispatcher = new EnvHttpProxyAgent();
|
|
1584
|
+
function bypassesProxy(target) {
|
|
1585
|
+
const noProxy = process.env["no_proxy"] ?? process.env["NO_PROXY"] ?? "";
|
|
1586
|
+
if (noProxy === "*") {
|
|
1587
|
+
return true;
|
|
1588
|
+
}
|
|
1589
|
+
const hostname4 = target.hostname.replace(/^\[|\]$/gu, "").toLowerCase();
|
|
1590
|
+
const defaultPort = target.protocol === "wss:" || target.protocol === "https:" ? 443 : 80;
|
|
1591
|
+
const port = Number.parseInt(target.port, 10) || defaultPort;
|
|
1592
|
+
for (const rawEntry of noProxy.split(/[,\s]/u)) {
|
|
1593
|
+
if (rawEntry.length === 0) {
|
|
1594
|
+
continue;
|
|
1595
|
+
}
|
|
1596
|
+
const parsed = rawEntry.match(/^(.+):(\d+)$/u);
|
|
1597
|
+
const entryHostname = (parsed?.[1] ?? rawEntry).replace(/^\*?\./u, "").toLowerCase();
|
|
1598
|
+
const entryPort = parsed?.[2] === void 0 ? 0 : Number.parseInt(parsed[2], 10);
|
|
1599
|
+
if (entryPort !== 0 && entryPort !== port) {
|
|
1600
|
+
continue;
|
|
1601
|
+
}
|
|
1602
|
+
if (hostname4 === entryHostname || hostname4.endsWith(`.${entryHostname}`)) {
|
|
1603
|
+
return true;
|
|
1604
|
+
}
|
|
1605
|
+
}
|
|
1606
|
+
return false;
|
|
1607
|
+
}
|
|
1608
|
+
function sanitizedProxyOrigin(proxyUrl) {
|
|
1609
|
+
try {
|
|
1610
|
+
return new URL(proxyUrl).origin;
|
|
1611
|
+
} catch {
|
|
1612
|
+
return "configured-invalid-url";
|
|
1613
|
+
}
|
|
1614
|
+
}
|
|
1615
|
+
function describeEnvironmentProxyRoute(target) {
|
|
1616
|
+
if (bypassesProxy(target)) {
|
|
1617
|
+
return { proxyRoute: "direct" };
|
|
1618
|
+
}
|
|
1619
|
+
const proxyUrl = target.protocol === "wss:" || target.protocol === "https:" ? configuredHttpsProxy : configuredHttpProxy;
|
|
1620
|
+
if (proxyUrl.length === 0) {
|
|
1621
|
+
return { proxyRoute: "direct" };
|
|
1622
|
+
}
|
|
1623
|
+
return {
|
|
1624
|
+
proxyOrigin: sanitizedProxyOrigin(proxyUrl),
|
|
1625
|
+
proxyRoute: "proxy"
|
|
1626
|
+
};
|
|
1627
|
+
}
|
|
1582
1628
|
|
|
1583
1629
|
// src/agent-installation/managed-agent-archive.ts
|
|
1584
1630
|
var ARCHIVE_DOWNLOAD_LIMIT_BYTES = 128 * 1024 * 1024;
|
|
@@ -22048,6 +22094,9 @@ var SESSION_CANCEL_KIND = "session.cancel";
|
|
|
22048
22094
|
var RUNTIME_LIVENESS_READY_KIND = "runtime.liveness.ready";
|
|
22049
22095
|
var RUNTIME_HEARTBEAT_KIND = "runtime.heartbeat";
|
|
22050
22096
|
var RUNTIME_HEARTBEAT_ACK_KIND = "runtime.heartbeat.ack";
|
|
22097
|
+
var RUNTIME_DELIVERY_KIND = "runtime.delivery";
|
|
22098
|
+
var RUNTIME_DELIVERY_RECEIVED_KIND = "runtime.delivery.received";
|
|
22099
|
+
var RUNTIME_DELIVERY_COMMITTED_KIND = "runtime.delivery.committed";
|
|
22051
22100
|
var SESSION_STARTED_KIND = "session.started";
|
|
22052
22101
|
var AGENT_MESSAGE_DELTA_KIND = "agent.message.delta";
|
|
22053
22102
|
var AGENT_ACTIVITY_KIND = "agent.activity";
|
|
@@ -22070,6 +22119,7 @@ var DIRECTORY_IDENTIFIER_PATTERN2 = /^[a-zA-Z0-9][a-zA-Z0-9_-]{0,2047}$/;
|
|
|
22070
22119
|
var MAX_PROMPT_LENGTH = 1e4;
|
|
22071
22120
|
var MAX_PROMPT_ATTACHMENT_LENGTH = 6e6;
|
|
22072
22121
|
var MAX_EVENT_TEXT_LENGTH = 1e5;
|
|
22122
|
+
var MAX_RUNTIME_DELIVERY_PAYLOAD_LENGTH = 8 * 1024 * 1024;
|
|
22073
22123
|
var ACTIVITY_TYPES = /* @__PURE__ */ new Set(["plan", "progress", "reasoning", "tool"]);
|
|
22074
22124
|
var ACTIVITY_STATUSES = /* @__PURE__ */ new Set(["pending", "running", "completed", "failed"]);
|
|
22075
22125
|
var TOOL_KINDS = /* @__PURE__ */ new Set([
|
|
@@ -22348,6 +22398,37 @@ function decodeRuntimeHeartbeatAckEvent(encoded) {
|
|
|
22348
22398
|
kind: RUNTIME_HEARTBEAT_ACK_KIND
|
|
22349
22399
|
};
|
|
22350
22400
|
}
|
|
22401
|
+
function decodeRuntimeDelivery(encoded) {
|
|
22402
|
+
const value = parseJson2(encoded);
|
|
22403
|
+
assertExactKeys(value, ["deliveryId", "kind", "payload", "sessionId"]);
|
|
22404
|
+
assertKind(value, RUNTIME_DELIVERY_KIND);
|
|
22405
|
+
return {
|
|
22406
|
+
deliveryId: readIdentifier(value, "deliveryId"),
|
|
22407
|
+
kind: RUNTIME_DELIVERY_KIND,
|
|
22408
|
+
payload: readString(value, "payload", MAX_RUNTIME_DELIVERY_PAYLOAD_LENGTH),
|
|
22409
|
+
sessionId: readIdentifier(value, "sessionId")
|
|
22410
|
+
};
|
|
22411
|
+
}
|
|
22412
|
+
function decodeRuntimeDeliveryReceived(encoded) {
|
|
22413
|
+
const value = parseJson2(encoded);
|
|
22414
|
+
assertExactKeys(value, ["deliveryId", "kind", "sessionId"]);
|
|
22415
|
+
assertKind(value, RUNTIME_DELIVERY_RECEIVED_KIND);
|
|
22416
|
+
return {
|
|
22417
|
+
deliveryId: readIdentifier(value, "deliveryId"),
|
|
22418
|
+
kind: RUNTIME_DELIVERY_RECEIVED_KIND,
|
|
22419
|
+
sessionId: readIdentifier(value, "sessionId")
|
|
22420
|
+
};
|
|
22421
|
+
}
|
|
22422
|
+
function decodeRuntimeDeliveryCommitted(encoded) {
|
|
22423
|
+
const value = parseJson2(encoded);
|
|
22424
|
+
assertExactKeys(value, ["deliveryId", "kind", "sessionId"]);
|
|
22425
|
+
assertKind(value, RUNTIME_DELIVERY_COMMITTED_KIND);
|
|
22426
|
+
return {
|
|
22427
|
+
deliveryId: readIdentifier(value, "deliveryId"),
|
|
22428
|
+
kind: RUNTIME_DELIVERY_COMMITTED_KIND,
|
|
22429
|
+
sessionId: readIdentifier(value, "sessionId")
|
|
22430
|
+
};
|
|
22431
|
+
}
|
|
22351
22432
|
function decodeSessionStartedEvent(encoded) {
|
|
22352
22433
|
const value = parseJson2(encoded);
|
|
22353
22434
|
assertExactKeys(value, ["commandId", "kind", "attemptId", "sessionId"]);
|
|
@@ -22435,6 +22516,9 @@ function decodeSessionFailedEvent(encoded) {
|
|
|
22435
22516
|
function encodeRuntimeHeartbeatEvent(message) {
|
|
22436
22517
|
return JSON.stringify(decodeRuntimeHeartbeatEvent(JSON.stringify(message)));
|
|
22437
22518
|
}
|
|
22519
|
+
function encodeRuntimeDeliveryReceived(message) {
|
|
22520
|
+
return JSON.stringify(decodeRuntimeDeliveryReceived(JSON.stringify(message)));
|
|
22521
|
+
}
|
|
22438
22522
|
function encodeSessionStartedEvent(message) {
|
|
22439
22523
|
return JSON.stringify(decodeSessionStartedEvent(JSON.stringify(message)));
|
|
22440
22524
|
}
|
|
@@ -23133,7 +23217,8 @@ var SESSION_EVENT_COMMITTED_KIND = "session.event.committed";
|
|
|
23133
23217
|
var SESSION_EVENT_REJECTED_KIND = "session.event.rejected";
|
|
23134
23218
|
var SessionEventRejectionCode = {
|
|
23135
23219
|
SessionDeleted: "session_deleted",
|
|
23136
|
-
SessionNotFound: "session_not_found"
|
|
23220
|
+
SessionNotFound: "session_not_found",
|
|
23221
|
+
SessionRejected: "session_rejected"
|
|
23137
23222
|
};
|
|
23138
23223
|
var IDENTIFIER_PATTERN4 = /^[a-zA-Z0-9][a-zA-Z0-9._:-]{0,127}$/;
|
|
23139
23224
|
var CURSOR_PATTERN = /^[A-Za-z0-9_-]{1,512}$/;
|
|
@@ -23205,6 +23290,7 @@ var encode4 = (message, schema) => {
|
|
|
23205
23290
|
}
|
|
23206
23291
|
return JSON.stringify(decoded.data);
|
|
23207
23292
|
};
|
|
23293
|
+
var decodeSessionEventEnvelope = (encoded) => decode5(encoded, eventEnvelopeSchema);
|
|
23208
23294
|
var encodeSessionEventEnvelope = (message) => encode4(message, eventEnvelopeSchema);
|
|
23209
23295
|
var decodeSessionEventAcknowledgement = (encoded) => decode5(encoded, eventAcknowledgementSchema);
|
|
23210
23296
|
var decodeSessionEventRejection = (encoded) => decode5(encoded, eventRejectionSchema);
|
|
@@ -23390,12 +23476,12 @@ function decodePairingResponse(value, credentialField, expectedKeys) {
|
|
|
23390
23476
|
throw new PairingClientError("invalid_relay_response", "The relay returned an invalid pairing response");
|
|
23391
23477
|
}
|
|
23392
23478
|
const channelId = value["channelId"];
|
|
23393
|
-
const
|
|
23479
|
+
const runtimeCredential2 = value[credentialField];
|
|
23394
23480
|
const runtimeId = value["runtimeId"];
|
|
23395
|
-
if (typeof channelId !== "string" || !IDENTIFIER_PATTERN5.test(channelId) || typeof
|
|
23481
|
+
if (typeof channelId !== "string" || !IDENTIFIER_PATTERN5.test(channelId) || typeof runtimeCredential2 !== "string" || !TOKEN_PATTERN2.test(runtimeCredential2) || typeof runtimeId !== "string" || !IDENTIFIER_PATTERN5.test(runtimeId)) {
|
|
23396
23482
|
throw new PairingClientError("invalid_relay_response", "The relay returned an invalid pairing response");
|
|
23397
23483
|
}
|
|
23398
|
-
return { channelId, runtimeCredential, runtimeId };
|
|
23484
|
+
return { channelId, runtimeCredential: runtimeCredential2, runtimeId };
|
|
23399
23485
|
}
|
|
23400
23486
|
async function claimPairing(options, defaultWorkspace, localAgents, installableAgents, signal) {
|
|
23401
23487
|
const agents = localAgents.map((agent) => ({
|
|
@@ -23430,7 +23516,7 @@ async function claimPairing(options, defaultWorkspace, localAgents, installableA
|
|
|
23430
23516
|
}
|
|
23431
23517
|
return options.pairingId.length > 0 ? decodePairingResponse(value, "runtimeCredential", REDEEM_RESPONSE_KEYS) : decodePairingResponse(value, "daemonToken", CLAIM_RESPONSE_KEYS);
|
|
23432
23518
|
}
|
|
23433
|
-
async function persistConnection(channelId, relayOrigin,
|
|
23519
|
+
async function persistConnection(channelId, relayOrigin, runtimeCredential2, defaultWorkspace, environment) {
|
|
23434
23520
|
const connectionsDirectory = join14(readStateDirectory(environment), "connections");
|
|
23435
23521
|
await mkdir3(connectionsDirectory, { mode: 448, recursive: true });
|
|
23436
23522
|
const connectionPath = join14(connectionsDirectory, `${channelId}.json`);
|
|
@@ -23442,7 +23528,7 @@ async function persistConnection(channelId, relayOrigin, runtimeCredential, defa
|
|
|
23442
23528
|
defaultWorkspace,
|
|
23443
23529
|
channelId,
|
|
23444
23530
|
relayOrigin: relayOrigin.toString(),
|
|
23445
|
-
runtimeCredential
|
|
23531
|
+
runtimeCredential: runtimeCredential2
|
|
23446
23532
|
},
|
|
23447
23533
|
null,
|
|
23448
23534
|
2
|
|
@@ -23459,16 +23545,16 @@ function decodeSavedConnection(value, expectedChannelId) {
|
|
|
23459
23545
|
}
|
|
23460
23546
|
const defaultWorkspace = value["defaultWorkspace"];
|
|
23461
23547
|
const channelId = value["channelId"];
|
|
23462
|
-
const
|
|
23548
|
+
const runtimeCredential2 = value["runtimeCredential"] ?? value["daemonToken"];
|
|
23463
23549
|
const relayOrigin = value["relayOrigin"];
|
|
23464
|
-
if (typeof defaultWorkspace !== "string" || typeof channelId !== "string" || channelId !== expectedChannelId || !IDENTIFIER_PATTERN5.test(channelId) || typeof
|
|
23550
|
+
if (typeof defaultWorkspace !== "string" || typeof channelId !== "string" || channelId !== expectedChannelId || !IDENTIFIER_PATTERN5.test(channelId) || typeof runtimeCredential2 !== "string" || !TOKEN_PATTERN2.test(runtimeCredential2) || typeof relayOrigin !== "string") {
|
|
23465
23551
|
throw new PairingClientError("invalid_connection", "The saved connection is invalid");
|
|
23466
23552
|
}
|
|
23467
23553
|
return {
|
|
23468
23554
|
defaultWorkspace,
|
|
23469
23555
|
channelId,
|
|
23470
23556
|
relayOrigin: parseRelayOrigin(relayOrigin),
|
|
23471
|
-
runtimeCredential
|
|
23557
|
+
runtimeCredential: runtimeCredential2
|
|
23472
23558
|
};
|
|
23473
23559
|
}
|
|
23474
23560
|
async function readSavedConnection(channelId, environment) {
|
|
@@ -27001,6 +27087,23 @@ var AcpSessionManager = class {
|
|
|
27001
27087
|
|
|
27002
27088
|
// src/agent-catalog/publish-agent-catalog.ts
|
|
27003
27089
|
import { fetch as fetch3 } from "undici";
|
|
27090
|
+
|
|
27091
|
+
// src/relay-connection/runtime-authentication.ts
|
|
27092
|
+
function runtimeCredential(relayUrl) {
|
|
27093
|
+
return relayUrl.searchParams.get("token") ?? "";
|
|
27094
|
+
}
|
|
27095
|
+
function runtimeAuthorization(relayUrl) {
|
|
27096
|
+
const credential = runtimeCredential(relayUrl);
|
|
27097
|
+
return credential.length === 0 ? {} : { authorization: `Bearer ${credential}` };
|
|
27098
|
+
}
|
|
27099
|
+
function relayHttpEndpoint(relayUrl) {
|
|
27100
|
+
const endpoint = new URL(relayUrl);
|
|
27101
|
+
endpoint.protocol = endpoint.protocol === "wss:" ? "https:" : "http:";
|
|
27102
|
+
endpoint.search = "";
|
|
27103
|
+
return endpoint;
|
|
27104
|
+
}
|
|
27105
|
+
|
|
27106
|
+
// src/agent-catalog/publish-agent-catalog.ts
|
|
27004
27107
|
var CATALOG_UPDATE_TIMEOUT_MILLISECONDS = 2e4;
|
|
27005
27108
|
var AgentCatalogPublishError = class extends Error {
|
|
27006
27109
|
code = "agent_catalog_update_failed";
|
|
@@ -27010,8 +27113,7 @@ var AgentCatalogPublishError = class extends Error {
|
|
|
27010
27113
|
}
|
|
27011
27114
|
};
|
|
27012
27115
|
function catalogEndpoint(config2) {
|
|
27013
|
-
const endpoint =
|
|
27014
|
-
endpoint.protocol = endpoint.protocol === "wss:" ? "https:" : "http:";
|
|
27116
|
+
const endpoint = relayHttpEndpoint(config2.relayUrl);
|
|
27015
27117
|
if (endpoint.pathname.endsWith("/daemon")) {
|
|
27016
27118
|
endpoint.pathname = `${endpoint.pathname.slice(0, -"/daemon".length)}/catalog`;
|
|
27017
27119
|
return endpoint;
|
|
@@ -27042,7 +27144,7 @@ async function publishAgentCatalog(config2, signal) {
|
|
|
27042
27144
|
installableAgents: catalog.installableAgents
|
|
27043
27145
|
}),
|
|
27044
27146
|
dispatcher: environmentProxyDispatcher,
|
|
27045
|
-
headers: { "content-type": "application/json" },
|
|
27147
|
+
headers: { ...runtimeAuthorization(config2.relayUrl), "content-type": "application/json" },
|
|
27046
27148
|
method: "PUT",
|
|
27047
27149
|
signal: requestSignal
|
|
27048
27150
|
});
|
|
@@ -29084,8 +29186,7 @@ function parseReference(resource) {
|
|
|
29084
29186
|
return new PromptArtifactReference(artifactId, byteLength, sha256, resource);
|
|
29085
29187
|
}
|
|
29086
29188
|
function downloadUrl(config2, command, reference) {
|
|
29087
|
-
const url2 =
|
|
29088
|
-
url2.protocol = url2.protocol === "wss:" ? "https:" : "http:";
|
|
29189
|
+
const url2 = relayHttpEndpoint(config2.relayUrl);
|
|
29089
29190
|
let channelPath = "";
|
|
29090
29191
|
if (url2.pathname.endsWith("/daemon")) {
|
|
29091
29192
|
channelPath = url2.pathname.slice(0, -"/daemon".length);
|
|
@@ -29097,7 +29198,6 @@ function downloadUrl(config2, command, reference) {
|
|
|
29097
29198
|
channelPath = `${url2.pathname.slice(0, -runtimePath.length)}/channels/${encodeURIComponent(config2.channelId)}`;
|
|
29098
29199
|
}
|
|
29099
29200
|
url2.pathname = `${channelPath}/sessions/${encodeURIComponent(command.sessionId)}/commands/${encodeURIComponent(command.commandId)}/artifacts/${encodeURIComponent(reference.artifactId)}/content`;
|
|
29100
|
-
url2.searchParams.delete("liveness");
|
|
29101
29201
|
return url2;
|
|
29102
29202
|
}
|
|
29103
29203
|
async function responseBytes(response, expectedByteLength, signal) {
|
|
@@ -29148,7 +29248,11 @@ async function download(config2, command, reference, signal) {
|
|
|
29148
29248
|
try {
|
|
29149
29249
|
response = await fetch5(downloadUrl(config2, command, reference), {
|
|
29150
29250
|
dispatcher: environmentProxyDispatcher,
|
|
29151
|
-
headers: {
|
|
29251
|
+
headers: {
|
|
29252
|
+
accept: reference.resource.mimeType,
|
|
29253
|
+
"accept-encoding": "identity",
|
|
29254
|
+
...runtimeAuthorization(config2.relayUrl)
|
|
29255
|
+
},
|
|
29152
29256
|
signal: requestSignal
|
|
29153
29257
|
});
|
|
29154
29258
|
if (!response.ok) {
|
|
@@ -29246,9 +29350,11 @@ import { WebSocket } from "undici";
|
|
|
29246
29350
|
var CONNECTION_TIMEOUT_MILLISECONDS = 15e3;
|
|
29247
29351
|
var RelayConnectionError = class extends Error {
|
|
29248
29352
|
code;
|
|
29249
|
-
|
|
29353
|
+
diagnosticDetails;
|
|
29354
|
+
constructor(code, message, diagnosticDetails = {}, options = {}) {
|
|
29250
29355
|
super(message, options);
|
|
29251
29356
|
this.code = code;
|
|
29357
|
+
this.diagnosticDetails = diagnosticDetails;
|
|
29252
29358
|
this.name = "RelayConnectionError";
|
|
29253
29359
|
}
|
|
29254
29360
|
};
|
|
@@ -29256,12 +29362,8 @@ function connectRelay(url2, signal) {
|
|
|
29256
29362
|
signal.throwIfAborted();
|
|
29257
29363
|
return new Promise((resolve5, reject) => {
|
|
29258
29364
|
const endpoint = new URL(url2);
|
|
29259
|
-
const headers =
|
|
29260
|
-
|
|
29261
|
-
const runtimeCredential = endpoint.searchParams.get("token") ?? "";
|
|
29262
|
-
endpoint.searchParams.delete("token");
|
|
29263
|
-
headers["authorization"] = `Bearer ${runtimeCredential}`;
|
|
29264
|
-
}
|
|
29365
|
+
const headers = runtimeAuthorization(endpoint);
|
|
29366
|
+
endpoint.searchParams.delete("token");
|
|
29265
29367
|
const socket = new WebSocket(endpoint, {
|
|
29266
29368
|
dispatcher: environmentProxyDispatcher,
|
|
29267
29369
|
headers
|
|
@@ -29286,16 +29388,34 @@ function connectRelay(url2, signal) {
|
|
|
29286
29388
|
};
|
|
29287
29389
|
const onAbort = () => {
|
|
29288
29390
|
fail(
|
|
29289
|
-
new RelayConnectionError(
|
|
29290
|
-
|
|
29291
|
-
|
|
29391
|
+
new RelayConnectionError(
|
|
29392
|
+
"relay_connection_cancelled",
|
|
29393
|
+
"Relay connection was cancelled",
|
|
29394
|
+
{
|
|
29395
|
+
cancellation: "requested"
|
|
29396
|
+
},
|
|
29397
|
+
{ cause: signal.reason }
|
|
29398
|
+
)
|
|
29292
29399
|
);
|
|
29293
29400
|
};
|
|
29294
|
-
const onClose = () => {
|
|
29295
|
-
fail(
|
|
29401
|
+
const onClose = (event) => {
|
|
29402
|
+
fail(
|
|
29403
|
+
new RelayConnectionError("relay_connection_closed", "Relay connection closed before it was ready", {
|
|
29404
|
+
closeCode: event.code.toString(),
|
|
29405
|
+
closeReason: event.reason
|
|
29406
|
+
})
|
|
29407
|
+
);
|
|
29296
29408
|
};
|
|
29297
|
-
const onError = () => {
|
|
29298
|
-
|
|
29409
|
+
const onError = (event) => {
|
|
29410
|
+
const cause = Reflect.get(event, "error");
|
|
29411
|
+
fail(
|
|
29412
|
+
new RelayConnectionError(
|
|
29413
|
+
"relay_connection_failed",
|
|
29414
|
+
"Relay connection failed",
|
|
29415
|
+
{},
|
|
29416
|
+
{ cause: cause ?? event }
|
|
29417
|
+
)
|
|
29418
|
+
);
|
|
29299
29419
|
};
|
|
29300
29420
|
const onOpen = () => {
|
|
29301
29421
|
if (finished) {
|
|
@@ -29306,7 +29426,11 @@ function connectRelay(url2, signal) {
|
|
|
29306
29426
|
resolve5(socket);
|
|
29307
29427
|
};
|
|
29308
29428
|
function onTimeout() {
|
|
29309
|
-
fail(
|
|
29429
|
+
fail(
|
|
29430
|
+
new RelayConnectionError("relay_connection_timeout", "Relay connection timed out", {
|
|
29431
|
+
timeoutMilliseconds: CONNECTION_TIMEOUT_MILLISECONDS.toString()
|
|
29432
|
+
})
|
|
29433
|
+
);
|
|
29310
29434
|
}
|
|
29311
29435
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
29312
29436
|
socket.addEventListener("close", onClose, { once: true });
|
|
@@ -29318,6 +29442,142 @@ function connectRelay(url2, signal) {
|
|
|
29318
29442
|
});
|
|
29319
29443
|
}
|
|
29320
29444
|
|
|
29445
|
+
// src/relay-connection/diagnostics.ts
|
|
29446
|
+
var MAX_DIAGNOSTIC_TEXT_LENGTH = 256;
|
|
29447
|
+
function sanitizeDiagnosticText(value) {
|
|
29448
|
+
const printable = Array.from(value, (character) => {
|
|
29449
|
+
const code = character.charCodeAt(0);
|
|
29450
|
+
return code < 32 || code === 127 ? " " : character;
|
|
29451
|
+
}).join("");
|
|
29452
|
+
const withoutSecrets = printable.replace(/([?&](?:access_token|authorization|credential|key|secret|token)=)[^&\s]*/giu, "$1[redacted]").replace(/(?:https?|wss?):\/\/[^\s)]+/giu, (encodedUrl) => {
|
|
29453
|
+
try {
|
|
29454
|
+
return new URL(encodedUrl).origin;
|
|
29455
|
+
} catch {
|
|
29456
|
+
return "[redacted-url]";
|
|
29457
|
+
}
|
|
29458
|
+
});
|
|
29459
|
+
return withoutSecrets.slice(0, MAX_DIAGNOSTIC_TEXT_LENGTH);
|
|
29460
|
+
}
|
|
29461
|
+
function addFailureDetails(details, failure) {
|
|
29462
|
+
let current = failure;
|
|
29463
|
+
for (let depth = 0; depth < 3 && current instanceof Error; depth += 1) {
|
|
29464
|
+
const prefix = depth === 0 ? "failure" : `cause${depth.toString()}`;
|
|
29465
|
+
details[`${prefix}Name`] = current.name;
|
|
29466
|
+
details[`${prefix}Message`] = sanitizeDiagnosticText(current.message);
|
|
29467
|
+
const code = Reflect.get(current, "code");
|
|
29468
|
+
if (typeof code === "string" || typeof code === "number") {
|
|
29469
|
+
details[`${prefix}Code`] = String(code);
|
|
29470
|
+
}
|
|
29471
|
+
const statusCode = Reflect.get(current, "statusCode");
|
|
29472
|
+
if (typeof statusCode === "string" || typeof statusCode === "number") {
|
|
29473
|
+
details[`${prefix}StatusCode`] = String(statusCode);
|
|
29474
|
+
}
|
|
29475
|
+
const diagnosticDetails = Reflect.get(current, "diagnosticDetails");
|
|
29476
|
+
if (typeof diagnosticDetails === "object" && diagnosticDetails !== null) {
|
|
29477
|
+
for (const [key, value] of Object.entries(diagnosticDetails)) {
|
|
29478
|
+
if (typeof value === "string") {
|
|
29479
|
+
details[key] = sanitizeDiagnosticText(value);
|
|
29480
|
+
}
|
|
29481
|
+
}
|
|
29482
|
+
}
|
|
29483
|
+
current = current.cause;
|
|
29484
|
+
}
|
|
29485
|
+
}
|
|
29486
|
+
var RelayConnectionDiagnostics = class {
|
|
29487
|
+
#audit;
|
|
29488
|
+
#channelId;
|
|
29489
|
+
#lastClosedEpoch = 0;
|
|
29490
|
+
#lastFailedEpoch = 0;
|
|
29491
|
+
#relayOrigin;
|
|
29492
|
+
#route;
|
|
29493
|
+
constructor(audit, channelId, relayUrl) {
|
|
29494
|
+
this.#audit = audit;
|
|
29495
|
+
this.#channelId = channelId;
|
|
29496
|
+
this.#relayOrigin = relayUrl.origin;
|
|
29497
|
+
this.#route = describeEnvironmentProxyRoute(relayUrl);
|
|
29498
|
+
}
|
|
29499
|
+
catalogFailed = (failure) => {
|
|
29500
|
+
const details = { ...this.#route, relayOrigin: this.#relayOrigin };
|
|
29501
|
+
addFailureDetails(details, failure);
|
|
29502
|
+
this.#record("agent_catalog_publish_failed", "catalog", details, true);
|
|
29503
|
+
};
|
|
29504
|
+
connectionClosed = (connectionEpoch, code, reason, source) => {
|
|
29505
|
+
if (this.#lastClosedEpoch === connectionEpoch) {
|
|
29506
|
+
return;
|
|
29507
|
+
}
|
|
29508
|
+
this.#lastClosedEpoch = connectionEpoch;
|
|
29509
|
+
this.#record(
|
|
29510
|
+
"relay_connection_closed",
|
|
29511
|
+
connectionEpoch.toString(),
|
|
29512
|
+
{
|
|
29513
|
+
closeCode: code.toString(),
|
|
29514
|
+
closeReason: sanitizeDiagnosticText(reason),
|
|
29515
|
+
connectionEpoch: connectionEpoch.toString(),
|
|
29516
|
+
relayOrigin: this.#relayOrigin,
|
|
29517
|
+
source,
|
|
29518
|
+
...this.#route
|
|
29519
|
+
},
|
|
29520
|
+
true
|
|
29521
|
+
);
|
|
29522
|
+
};
|
|
29523
|
+
connectionFailed = (connectionEpoch, failure, source) => {
|
|
29524
|
+
if (this.#lastFailedEpoch === connectionEpoch) {
|
|
29525
|
+
return;
|
|
29526
|
+
}
|
|
29527
|
+
this.#lastFailedEpoch = connectionEpoch;
|
|
29528
|
+
const details = {
|
|
29529
|
+
connectionEpoch: connectionEpoch.toString(),
|
|
29530
|
+
relayOrigin: this.#relayOrigin,
|
|
29531
|
+
source,
|
|
29532
|
+
...this.#route
|
|
29533
|
+
};
|
|
29534
|
+
addFailureDetails(details, failure);
|
|
29535
|
+
this.#record("relay_connection_failed", connectionEpoch.toString(), details, true);
|
|
29536
|
+
};
|
|
29537
|
+
connectionOpened = (connectionEpoch) => {
|
|
29538
|
+
this.#record(
|
|
29539
|
+
"relay_connection_opened",
|
|
29540
|
+
connectionEpoch.toString(),
|
|
29541
|
+
{
|
|
29542
|
+
connectionEpoch: connectionEpoch.toString(),
|
|
29543
|
+
relayOrigin: this.#relayOrigin,
|
|
29544
|
+
...this.#route
|
|
29545
|
+
},
|
|
29546
|
+
false
|
|
29547
|
+
);
|
|
29548
|
+
};
|
|
29549
|
+
heartbeatTimedOut = (connectionEpoch, heartbeatId, timeoutMilliseconds) => {
|
|
29550
|
+
this.#record(
|
|
29551
|
+
"relay_heartbeat_timed_out",
|
|
29552
|
+
connectionEpoch.toString(),
|
|
29553
|
+
{
|
|
29554
|
+
connectionEpoch: connectionEpoch.toString(),
|
|
29555
|
+
heartbeatId,
|
|
29556
|
+
relayOrigin: this.#relayOrigin,
|
|
29557
|
+
timeoutMilliseconds: timeoutMilliseconds.toString(),
|
|
29558
|
+
...this.#route
|
|
29559
|
+
},
|
|
29560
|
+
true
|
|
29561
|
+
);
|
|
29562
|
+
};
|
|
29563
|
+
#record = (event, commandId, details, warning) => {
|
|
29564
|
+
this.#audit.record({
|
|
29565
|
+
commandId,
|
|
29566
|
+
details,
|
|
29567
|
+
event,
|
|
29568
|
+
sessionId: this.#channelId
|
|
29569
|
+
});
|
|
29570
|
+
const encoded = JSON.stringify({ channelId: this.#channelId, event, ...details });
|
|
29571
|
+
if (warning) {
|
|
29572
|
+
process.stderr.write(`Mobius relay diagnostic ${encoded}
|
|
29573
|
+
`);
|
|
29574
|
+
return;
|
|
29575
|
+
}
|
|
29576
|
+
process.stdout.write(`Mobius relay diagnostic ${encoded}
|
|
29577
|
+
`);
|
|
29578
|
+
};
|
|
29579
|
+
};
|
|
29580
|
+
|
|
29321
29581
|
// src/relay-connection/liveness.ts
|
|
29322
29582
|
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
29323
29583
|
var HEARTBEAT_ACK_TIMEOUT_MILLISECONDS = 1e4;
|
|
@@ -29394,9 +29654,10 @@ var RelayConnectionLiveness = class {
|
|
|
29394
29654
|
if (!(this.#state instanceof AwaitingHeartbeatAckState)) {
|
|
29395
29655
|
throw new Error("Heartbeat timeout occurred in an invalid state");
|
|
29396
29656
|
}
|
|
29657
|
+
const heartbeatId = this.#state.heartbeatId;
|
|
29397
29658
|
this.#state.timeout.cancel();
|
|
29398
29659
|
this.#state = new FinishedRelayLivenessState();
|
|
29399
|
-
this.#transport.unresponsive();
|
|
29660
|
+
this.#transport.unresponsive(heartbeatId, HEARTBEAT_ACK_TIMEOUT_MILLISECONDS);
|
|
29400
29661
|
};
|
|
29401
29662
|
#sendHeartbeat = () => {
|
|
29402
29663
|
if (this.#state instanceof WaitingForHeartbeatState) {
|
|
@@ -29425,6 +29686,42 @@ var RelayConnectionLiveness = class {
|
|
|
29425
29686
|
import { mkdirSync as mkdirSync3 } from "node:fs";
|
|
29426
29687
|
import { dirname as dirname7 } from "node:path";
|
|
29427
29688
|
import { DatabaseSync as DatabaseSync4 } from "node:sqlite";
|
|
29689
|
+
var DEFAULT_OUTBOX_LIMITS = {
|
|
29690
|
+
criticalEventBytes: 136 * 1024 * 1024,
|
|
29691
|
+
criticalEventCount: 10128,
|
|
29692
|
+
runtimeDeliveryBytes: 64 * 1024 * 1024,
|
|
29693
|
+
runtimeDeliveryCount: 64,
|
|
29694
|
+
standardEventBytes: 128 * 1024 * 1024,
|
|
29695
|
+
standardEventCount: 1e4
|
|
29696
|
+
};
|
|
29697
|
+
var SessionEventOutboxCapacityError = class extends Error {
|
|
29698
|
+
code = "session_event_outbox_full";
|
|
29699
|
+
constructor() {
|
|
29700
|
+
super("The durable Session event outbox is full");
|
|
29701
|
+
this.name = "SessionEventOutboxCapacityError";
|
|
29702
|
+
}
|
|
29703
|
+
};
|
|
29704
|
+
var RuntimeDeliveryInboxCapacityError = class extends Error {
|
|
29705
|
+
code = "runtime_delivery_inbox_full";
|
|
29706
|
+
constructor() {
|
|
29707
|
+
super("The durable Runtime delivery inbox is full");
|
|
29708
|
+
this.name = "RuntimeDeliveryInboxCapacityError";
|
|
29709
|
+
}
|
|
29710
|
+
};
|
|
29711
|
+
var StoredRuntimeDelivery = class {
|
|
29712
|
+
deliveryId;
|
|
29713
|
+
duplicate;
|
|
29714
|
+
payload;
|
|
29715
|
+
sessionId;
|
|
29716
|
+
state;
|
|
29717
|
+
constructor(deliveryId, sessionId, payload, state, duplicate) {
|
|
29718
|
+
this.deliveryId = deliveryId;
|
|
29719
|
+
this.duplicate = duplicate;
|
|
29720
|
+
this.payload = payload;
|
|
29721
|
+
this.sessionId = sessionId;
|
|
29722
|
+
this.state = state;
|
|
29723
|
+
}
|
|
29724
|
+
};
|
|
29428
29725
|
function readPendingEnvelope(row) {
|
|
29429
29726
|
if (typeof row !== "object" || row === null || Array.isArray(row)) {
|
|
29430
29727
|
throw new Error("SQLite returned an invalid session event outbox row");
|
|
@@ -29435,9 +29732,26 @@ function readPendingEnvelope(row) {
|
|
|
29435
29732
|
}
|
|
29436
29733
|
return envelope2;
|
|
29437
29734
|
}
|
|
29735
|
+
function rowNumber(row, field) {
|
|
29736
|
+
const value = typeof row === "object" && row !== null ? Reflect.get(row, field) : void 0;
|
|
29737
|
+
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) {
|
|
29738
|
+
throw new Error(`SQLite returned an invalid ${field} value`);
|
|
29739
|
+
}
|
|
29740
|
+
return value;
|
|
29741
|
+
}
|
|
29742
|
+
function rowString2(row, field) {
|
|
29743
|
+
const value = typeof row === "object" && row !== null ? Reflect.get(row, field) : void 0;
|
|
29744
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
29745
|
+
throw new Error(`SQLite returned an invalid ${field} value`);
|
|
29746
|
+
}
|
|
29747
|
+
return value;
|
|
29748
|
+
}
|
|
29438
29749
|
var SessionEventOutbox = class {
|
|
29439
29750
|
#database;
|
|
29440
|
-
|
|
29751
|
+
#limits;
|
|
29752
|
+
constructor(path2, limits = DEFAULT_OUTBOX_LIMITS) {
|
|
29753
|
+
validateLimits(limits);
|
|
29754
|
+
this.#limits = limits;
|
|
29441
29755
|
mkdirSync3(dirname7(path2), { recursive: true });
|
|
29442
29756
|
this.#database = new DatabaseSync4(path2);
|
|
29443
29757
|
this.#database.exec(`
|
|
@@ -29445,6 +29759,8 @@ var SessionEventOutbox = class {
|
|
|
29445
29759
|
ordinal INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
29446
29760
|
event_id TEXT NOT NULL UNIQUE,
|
|
29447
29761
|
session_id TEXT NOT NULL,
|
|
29762
|
+
source_key TEXT NOT NULL,
|
|
29763
|
+
byte_length INTEGER NOT NULL,
|
|
29448
29764
|
envelope_json TEXT NOT NULL
|
|
29449
29765
|
) STRICT;
|
|
29450
29766
|
CREATE INDEX IF NOT EXISTS relay_session_event_outbox_order
|
|
@@ -29455,7 +29771,7 @@ var SessionEventOutbox = class {
|
|
|
29455
29771
|
session_id TEXT NOT NULL,
|
|
29456
29772
|
envelope_json TEXT NOT NULL,
|
|
29457
29773
|
rejection_code TEXT NOT NULL CHECK (
|
|
29458
|
-
rejection_code IN ('session_deleted', 'session_not_found')
|
|
29774
|
+
rejection_code IN ('session_deleted', 'session_not_found', 'session_rejected')
|
|
29459
29775
|
)
|
|
29460
29776
|
) STRICT;
|
|
29461
29777
|
CREATE TABLE IF NOT EXISTS relay_session_event_sources (
|
|
@@ -29471,6 +29787,92 @@ var SessionEventOutbox = class {
|
|
|
29471
29787
|
state IN ('dispatching', 'running', 'succeeded', 'failed', 'cancelled')
|
|
29472
29788
|
)
|
|
29473
29789
|
) STRICT;
|
|
29790
|
+
CREATE TABLE IF NOT EXISTS relay_session_event_outbox_state (
|
|
29791
|
+
singleton INTEGER PRIMARY KEY CHECK (singleton = 1),
|
|
29792
|
+
event_count INTEGER NOT NULL,
|
|
29793
|
+
total_bytes INTEGER NOT NULL
|
|
29794
|
+
) STRICT;
|
|
29795
|
+
INSERT OR IGNORE INTO relay_session_event_outbox_state (
|
|
29796
|
+
singleton, event_count, total_bytes
|
|
29797
|
+
) VALUES (1, 0, 0);
|
|
29798
|
+
CREATE TABLE IF NOT EXISTS relay_runtime_deliveries (
|
|
29799
|
+
delivery_id TEXT PRIMARY KEY,
|
|
29800
|
+
session_id TEXT NOT NULL,
|
|
29801
|
+
payload_json TEXT NOT NULL,
|
|
29802
|
+
payload_bytes INTEGER NOT NULL,
|
|
29803
|
+
state TEXT NOT NULL CHECK (
|
|
29804
|
+
state IN ('received', 'relay_committed', 'processed')
|
|
29805
|
+
),
|
|
29806
|
+
received_at INTEGER NOT NULL
|
|
29807
|
+
) STRICT;
|
|
29808
|
+
`);
|
|
29809
|
+
const deadLetterTable = this.#database.prepare(
|
|
29810
|
+
`SELECT sql FROM sqlite_master
|
|
29811
|
+
WHERE type = 'table' AND name = 'relay_session_event_dead_letters'`
|
|
29812
|
+
).get();
|
|
29813
|
+
const deadLetterSql = typeof deadLetterTable === "object" && deadLetterTable !== null ? Reflect.get(deadLetterTable, "sql") : void 0;
|
|
29814
|
+
if (typeof deadLetterSql !== "string" || !deadLetterSql.includes("'session_rejected'")) {
|
|
29815
|
+
this.#database.exec(`
|
|
29816
|
+
BEGIN IMMEDIATE;
|
|
29817
|
+
ALTER TABLE relay_session_event_dead_letters
|
|
29818
|
+
RENAME TO relay_session_event_dead_letters_legacy;
|
|
29819
|
+
CREATE TABLE relay_session_event_dead_letters (
|
|
29820
|
+
ordinal INTEGER PRIMARY KEY,
|
|
29821
|
+
event_id TEXT NOT NULL UNIQUE,
|
|
29822
|
+
session_id TEXT NOT NULL,
|
|
29823
|
+
envelope_json TEXT NOT NULL,
|
|
29824
|
+
rejection_code TEXT NOT NULL CHECK (
|
|
29825
|
+
rejection_code IN ('session_deleted', 'session_not_found', 'session_rejected')
|
|
29826
|
+
)
|
|
29827
|
+
) STRICT;
|
|
29828
|
+
INSERT INTO relay_session_event_dead_letters (
|
|
29829
|
+
ordinal, event_id, session_id, envelope_json, rejection_code
|
|
29830
|
+
)
|
|
29831
|
+
SELECT ordinal, event_id, session_id, envelope_json, rejection_code
|
|
29832
|
+
FROM relay_session_event_dead_letters_legacy;
|
|
29833
|
+
DROP TABLE relay_session_event_dead_letters_legacy;
|
|
29834
|
+
COMMIT;
|
|
29835
|
+
`);
|
|
29836
|
+
}
|
|
29837
|
+
const outboxColumns = this.#database.prepare("PRAGMA table_info(relay_session_event_outbox)").all();
|
|
29838
|
+
const hasSourceKey = outboxColumns.some(
|
|
29839
|
+
(column) => typeof column === "object" && column !== null && Reflect.get(column, "name") === "source_key"
|
|
29840
|
+
);
|
|
29841
|
+
if (!hasSourceKey) {
|
|
29842
|
+
this.#database.exec(`
|
|
29843
|
+
ALTER TABLE relay_session_event_outbox
|
|
29844
|
+
ADD COLUMN source_key TEXT NOT NULL DEFAULT '';
|
|
29845
|
+
`);
|
|
29846
|
+
}
|
|
29847
|
+
const hasByteLength = outboxColumns.some(
|
|
29848
|
+
(column) => typeof column === "object" && column !== null && Reflect.get(column, "name") === "byte_length"
|
|
29849
|
+
);
|
|
29850
|
+
if (!hasByteLength) {
|
|
29851
|
+
this.#database.exec(`
|
|
29852
|
+
ALTER TABLE relay_session_event_outbox
|
|
29853
|
+
ADD COLUMN byte_length INTEGER NOT NULL DEFAULT 0;
|
|
29854
|
+
`);
|
|
29855
|
+
}
|
|
29856
|
+
this.#database.exec(`
|
|
29857
|
+
UPDATE relay_session_event_outbox
|
|
29858
|
+
SET source_key = CASE json_extract(envelope_json, '$.source.kind')
|
|
29859
|
+
WHEN 'attempt' THEN
|
|
29860
|
+
'attempt:' || session_id || ':' || json_extract(envelope_json, '$.source.attemptId')
|
|
29861
|
+
ELSE 'runtime:' || session_id
|
|
29862
|
+
END
|
|
29863
|
+
WHERE source_key = '';
|
|
29864
|
+
UPDATE relay_session_event_outbox
|
|
29865
|
+
SET byte_length = length(CAST(envelope_json AS BLOB))
|
|
29866
|
+
WHERE byte_length = 0;
|
|
29867
|
+
UPDATE relay_session_event_outbox_state
|
|
29868
|
+
SET event_count = (SELECT COUNT(*) FROM relay_session_event_outbox),
|
|
29869
|
+
total_bytes = (
|
|
29870
|
+
SELECT COALESCE(SUM(byte_length), 0)
|
|
29871
|
+
FROM relay_session_event_outbox
|
|
29872
|
+
)
|
|
29873
|
+
WHERE singleton = 1;
|
|
29874
|
+
CREATE INDEX IF NOT EXISTS relay_session_event_outbox_source_order
|
|
29875
|
+
ON relay_session_event_outbox(source_key, ordinal);
|
|
29474
29876
|
`);
|
|
29475
29877
|
}
|
|
29476
29878
|
claimExecution = (commandId, sessionId, commandJson) => {
|
|
@@ -29529,15 +29931,19 @@ var SessionEventOutbox = class {
|
|
|
29529
29931
|
const state = typeof row === "object" && row !== null ? Reflect.get(row, "state") : void 0;
|
|
29530
29932
|
return state === "succeeded" || state === "failed" || state === "cancelled";
|
|
29531
29933
|
};
|
|
29532
|
-
acknowledge = (acknowledgement) =>
|
|
29533
|
-
this.#database.prepare(
|
|
29534
|
-
`DELETE FROM relay_session_event_outbox
|
|
29535
|
-
WHERE event_id = ? AND session_id = ?`
|
|
29536
|
-
).run(acknowledgement.eventId, acknowledgement.sessionId);
|
|
29537
|
-
};
|
|
29934
|
+
acknowledge = (acknowledgement) => this.#removeAcknowledgedEvent(acknowledgement.eventId, acknowledgement.sessionId);
|
|
29538
29935
|
quarantine = (rejection) => {
|
|
29539
29936
|
this.#database.exec("BEGIN IMMEDIATE");
|
|
29540
29937
|
try {
|
|
29938
|
+
const stored = this.#database.prepare(
|
|
29939
|
+
`SELECT byte_length FROM relay_session_event_outbox
|
|
29940
|
+
WHERE event_id = ? AND session_id = ?`
|
|
29941
|
+
).get(rejection.eventId, rejection.sessionId);
|
|
29942
|
+
if (stored === void 0) {
|
|
29943
|
+
this.#database.exec("COMMIT");
|
|
29944
|
+
return false;
|
|
29945
|
+
}
|
|
29946
|
+
const byteLength = rowNumber(stored, "byte_length");
|
|
29541
29947
|
const insertion = this.#database.prepare(
|
|
29542
29948
|
`INSERT INTO relay_session_event_dead_letters (
|
|
29543
29949
|
ordinal, event_id, session_id, envelope_json, rejection_code
|
|
@@ -29557,6 +29963,7 @@ var SessionEventOutbox = class {
|
|
|
29557
29963
|
if (deletion.changes !== 1) {
|
|
29558
29964
|
throw new Error("The rejected session event could not be removed from the durable outbox");
|
|
29559
29965
|
}
|
|
29966
|
+
this.#decrementEventState(byteLength);
|
|
29560
29967
|
this.#database.exec("COMMIT");
|
|
29561
29968
|
return true;
|
|
29562
29969
|
} catch (error61) {
|
|
@@ -29568,7 +29975,129 @@ var SessionEventOutbox = class {
|
|
|
29568
29975
|
this.#database.close();
|
|
29569
29976
|
};
|
|
29570
29977
|
enqueue = (sessionId, payload, attemptId = null) => {
|
|
29571
|
-
|
|
29978
|
+
return this.#enqueueBounded(
|
|
29979
|
+
sessionId,
|
|
29980
|
+
payload,
|
|
29981
|
+
attemptId ?? "",
|
|
29982
|
+
this.#limits.standardEventCount,
|
|
29983
|
+
this.#limits.standardEventBytes
|
|
29984
|
+
);
|
|
29985
|
+
};
|
|
29986
|
+
enqueueCritical = (sessionId, payload, attemptId = "") => this.#enqueueBounded(
|
|
29987
|
+
sessionId,
|
|
29988
|
+
payload,
|
|
29989
|
+
attemptId,
|
|
29990
|
+
this.#limits.criticalEventCount,
|
|
29991
|
+
this.#limits.criticalEventBytes
|
|
29992
|
+
);
|
|
29993
|
+
claimRuntimeDelivery = (delivery) => {
|
|
29994
|
+
const payloadBytes = new TextEncoder().encode(delivery.payload).byteLength;
|
|
29995
|
+
this.#database.exec("BEGIN IMMEDIATE");
|
|
29996
|
+
try {
|
|
29997
|
+
const existing = this.#database.prepare(
|
|
29998
|
+
`SELECT session_id, payload_json, state
|
|
29999
|
+
FROM relay_runtime_deliveries
|
|
30000
|
+
WHERE delivery_id = ?`
|
|
30001
|
+
).get(delivery.deliveryId);
|
|
30002
|
+
if (existing !== void 0) {
|
|
30003
|
+
const sessionId = rowString2(existing, "session_id");
|
|
30004
|
+
const payload = rowString2(existing, "payload_json");
|
|
30005
|
+
const state = runtimeDeliveryState(existing);
|
|
30006
|
+
if (sessionId !== delivery.sessionId || payload !== delivery.payload) {
|
|
30007
|
+
throw new Error("The Runtime delivery identity conflicts with its durable receipt");
|
|
30008
|
+
}
|
|
30009
|
+
this.#database.exec("COMMIT");
|
|
30010
|
+
return new StoredRuntimeDelivery(delivery.deliveryId, sessionId, payload, state, true);
|
|
30011
|
+
}
|
|
30012
|
+
const usage = this.#database.prepare(
|
|
30013
|
+
`SELECT COUNT(*) AS delivery_count,
|
|
30014
|
+
COALESCE(SUM(payload_bytes), 0) AS delivery_bytes
|
|
30015
|
+
FROM relay_runtime_deliveries`
|
|
30016
|
+
).get();
|
|
30017
|
+
if (rowNumber(usage, "delivery_count") + 1 > this.#limits.runtimeDeliveryCount || rowNumber(usage, "delivery_bytes") + payloadBytes > this.#limits.runtimeDeliveryBytes) {
|
|
30018
|
+
throw new RuntimeDeliveryInboxCapacityError();
|
|
30019
|
+
}
|
|
30020
|
+
this.#database.prepare(
|
|
30021
|
+
`INSERT INTO relay_runtime_deliveries (
|
|
30022
|
+
delivery_id, session_id, payload_json, payload_bytes, state, received_at
|
|
30023
|
+
) VALUES (?, ?, ?, ?, 'received', ?)`
|
|
30024
|
+
).run(delivery.deliveryId, delivery.sessionId, delivery.payload, payloadBytes, Date.now());
|
|
30025
|
+
this.#database.exec("COMMIT");
|
|
30026
|
+
return new StoredRuntimeDelivery(
|
|
30027
|
+
delivery.deliveryId,
|
|
30028
|
+
delivery.sessionId,
|
|
30029
|
+
delivery.payload,
|
|
30030
|
+
"received",
|
|
30031
|
+
false
|
|
30032
|
+
);
|
|
30033
|
+
} catch (error61) {
|
|
30034
|
+
this.#database.exec("ROLLBACK");
|
|
30035
|
+
throw error61;
|
|
30036
|
+
}
|
|
30037
|
+
};
|
|
30038
|
+
markRuntimeDeliveryCommitted = (deliveryId, sessionId) => {
|
|
30039
|
+
this.#database.exec("BEGIN IMMEDIATE");
|
|
30040
|
+
try {
|
|
30041
|
+
const row = this.#database.prepare(
|
|
30042
|
+
`SELECT state FROM relay_runtime_deliveries
|
|
30043
|
+
WHERE delivery_id = ? AND session_id = ?`
|
|
30044
|
+
).get(deliveryId, sessionId);
|
|
30045
|
+
if (row === void 0) {
|
|
30046
|
+
this.#database.exec("COMMIT");
|
|
30047
|
+
return false;
|
|
30048
|
+
}
|
|
30049
|
+
const state = runtimeDeliveryState(row);
|
|
30050
|
+
if (state === "processed") {
|
|
30051
|
+
this.#database.prepare(`DELETE FROM relay_runtime_deliveries WHERE delivery_id = ?`).run(deliveryId);
|
|
30052
|
+
} else if (state === "received") {
|
|
30053
|
+
this.#database.prepare(`UPDATE relay_runtime_deliveries SET state = 'relay_committed' WHERE delivery_id = ?`).run(deliveryId);
|
|
30054
|
+
}
|
|
30055
|
+
this.#database.exec("COMMIT");
|
|
30056
|
+
return true;
|
|
30057
|
+
} catch (error61) {
|
|
30058
|
+
this.#database.exec("ROLLBACK");
|
|
30059
|
+
throw error61;
|
|
30060
|
+
}
|
|
30061
|
+
};
|
|
30062
|
+
markRuntimeDeliveryProcessed = (deliveryId, sessionId) => {
|
|
30063
|
+
this.#database.exec("BEGIN IMMEDIATE");
|
|
30064
|
+
try {
|
|
30065
|
+
const row = this.#database.prepare(
|
|
30066
|
+
`SELECT state FROM relay_runtime_deliveries
|
|
30067
|
+
WHERE delivery_id = ? AND session_id = ?`
|
|
30068
|
+
).get(deliveryId, sessionId);
|
|
30069
|
+
if (row === void 0) {
|
|
30070
|
+
this.#database.exec("COMMIT");
|
|
30071
|
+
return false;
|
|
30072
|
+
}
|
|
30073
|
+
const state = runtimeDeliveryState(row);
|
|
30074
|
+
if (state === "relay_committed") {
|
|
30075
|
+
this.#database.prepare(`DELETE FROM relay_runtime_deliveries WHERE delivery_id = ?`).run(deliveryId);
|
|
30076
|
+
} else if (state === "received") {
|
|
30077
|
+
this.#database.prepare(`UPDATE relay_runtime_deliveries SET state = 'processed' WHERE delivery_id = ?`).run(deliveryId);
|
|
30078
|
+
}
|
|
30079
|
+
this.#database.exec("COMMIT");
|
|
30080
|
+
return true;
|
|
30081
|
+
} catch (error61) {
|
|
30082
|
+
this.#database.exec("ROLLBACK");
|
|
30083
|
+
throw error61;
|
|
30084
|
+
}
|
|
30085
|
+
};
|
|
30086
|
+
runtimeDeliveries = () => this.#database.prepare(
|
|
30087
|
+
`SELECT delivery_id, session_id, payload_json, state
|
|
30088
|
+
FROM relay_runtime_deliveries
|
|
30089
|
+
ORDER BY received_at, delivery_id`
|
|
30090
|
+
).all().map(
|
|
30091
|
+
(row) => new StoredRuntimeDelivery(
|
|
30092
|
+
rowString2(row, "delivery_id"),
|
|
30093
|
+
rowString2(row, "session_id"),
|
|
30094
|
+
rowString2(row, "payload_json"),
|
|
30095
|
+
runtimeDeliveryState(row),
|
|
30096
|
+
true
|
|
30097
|
+
)
|
|
30098
|
+
);
|
|
30099
|
+
#enqueueBounded = (sessionId, payload, attemptId, maximumEventCount, maximumEventBytes) => {
|
|
30100
|
+
const sourceKey = attemptId.length === 0 ? `runtime:${sessionId}` : `attempt:${sessionId}:${attemptId}`;
|
|
29572
30101
|
this.#database.exec("BEGIN IMMEDIATE");
|
|
29573
30102
|
try {
|
|
29574
30103
|
const row = this.#database.prepare(
|
|
@@ -29585,13 +30114,22 @@ var SessionEventOutbox = class {
|
|
|
29585
30114
|
kind: SESSION_EVENT_KIND,
|
|
29586
30115
|
payload,
|
|
29587
30116
|
sessionId,
|
|
29588
|
-
source: attemptId ===
|
|
30117
|
+
source: attemptId.length === 0 ? { kind: "runtime", sourceSeq: nextSourceSeq.toString() } : {
|
|
29589
30118
|
attemptId,
|
|
29590
30119
|
kind: "attempt",
|
|
29591
30120
|
sourceSeq: nextSourceSeq.toString()
|
|
29592
30121
|
}
|
|
29593
30122
|
};
|
|
29594
30123
|
const encoded = encodeSessionEventEnvelope(envelope2);
|
|
30124
|
+
const byteLength = new TextEncoder().encode(encoded).byteLength;
|
|
30125
|
+
const state = this.#database.prepare(
|
|
30126
|
+
`SELECT event_count, total_bytes
|
|
30127
|
+
FROM relay_session_event_outbox_state
|
|
30128
|
+
WHERE singleton = 1`
|
|
30129
|
+
).get();
|
|
30130
|
+
if (rowNumber(state, "event_count") + 1 > maximumEventCount || rowNumber(state, "total_bytes") + byteLength > maximumEventBytes) {
|
|
30131
|
+
throw new SessionEventOutboxCapacityError();
|
|
30132
|
+
}
|
|
29595
30133
|
this.#database.prepare(
|
|
29596
30134
|
`INSERT INTO relay_session_event_sources (
|
|
29597
30135
|
source_key, next_source_seq
|
|
@@ -29601,9 +30139,15 @@ var SessionEventOutbox = class {
|
|
|
29601
30139
|
).run(sourceKey, nextSourceSeq + 1);
|
|
29602
30140
|
this.#database.prepare(
|
|
29603
30141
|
`INSERT INTO relay_session_event_outbox (
|
|
29604
|
-
event_id, session_id, envelope_json
|
|
29605
|
-
) VALUES (?, ?, ?)`
|
|
29606
|
-
).run(envelope2.eventId, envelope2.sessionId, encoded);
|
|
30142
|
+
event_id, session_id, source_key, byte_length, envelope_json
|
|
30143
|
+
) VALUES (?, ?, ?, ?, ?)`
|
|
30144
|
+
).run(envelope2.eventId, envelope2.sessionId, sourceKey, byteLength, encoded);
|
|
30145
|
+
this.#database.prepare(
|
|
30146
|
+
`UPDATE relay_session_event_outbox_state
|
|
30147
|
+
SET event_count = event_count + 1,
|
|
30148
|
+
total_bytes = total_bytes + ?
|
|
30149
|
+
WHERE singleton = 1`
|
|
30150
|
+
).run(byteLength);
|
|
29607
30151
|
this.#database.exec("COMMIT");
|
|
29608
30152
|
return encoded;
|
|
29609
30153
|
} catch (error61) {
|
|
@@ -29611,12 +30155,132 @@ var SessionEventOutbox = class {
|
|
|
29611
30155
|
throw error61;
|
|
29612
30156
|
}
|
|
29613
30157
|
};
|
|
30158
|
+
#removeAcknowledgedEvent = (eventId, sessionId) => {
|
|
30159
|
+
this.#database.exec("BEGIN IMMEDIATE");
|
|
30160
|
+
try {
|
|
30161
|
+
const stored = this.#database.prepare(
|
|
30162
|
+
`SELECT byte_length FROM relay_session_event_outbox
|
|
30163
|
+
WHERE event_id = ? AND session_id = ?`
|
|
30164
|
+
).get(eventId, sessionId);
|
|
30165
|
+
if (stored === void 0) {
|
|
30166
|
+
this.#database.exec("COMMIT");
|
|
30167
|
+
return false;
|
|
30168
|
+
}
|
|
30169
|
+
const byteLength = rowNumber(stored, "byte_length");
|
|
30170
|
+
const deletion = this.#database.prepare(
|
|
30171
|
+
`DELETE FROM relay_session_event_outbox
|
|
30172
|
+
WHERE event_id = ? AND session_id = ?`
|
|
30173
|
+
).run(eventId, sessionId);
|
|
30174
|
+
if (deletion.changes !== 1) {
|
|
30175
|
+
throw new Error("The acknowledged session event could not be removed from the durable outbox");
|
|
30176
|
+
}
|
|
30177
|
+
this.#decrementEventState(byteLength);
|
|
30178
|
+
this.#database.exec("COMMIT");
|
|
30179
|
+
return true;
|
|
30180
|
+
} catch (error61) {
|
|
30181
|
+
this.#database.exec("ROLLBACK");
|
|
30182
|
+
throw error61;
|
|
30183
|
+
}
|
|
30184
|
+
};
|
|
30185
|
+
#decrementEventState = (byteLength) => {
|
|
30186
|
+
const result = this.#database.prepare(
|
|
30187
|
+
`UPDATE relay_session_event_outbox_state
|
|
30188
|
+
SET event_count = event_count - 1,
|
|
30189
|
+
total_bytes = total_bytes - ?
|
|
30190
|
+
WHERE singleton = 1 AND event_count > 0 AND total_bytes >= ?`
|
|
30191
|
+
).run(byteLength, byteLength);
|
|
30192
|
+
if (result.changes !== 1) {
|
|
30193
|
+
throw new Error("The Session event outbox capacity state is inconsistent");
|
|
30194
|
+
}
|
|
30195
|
+
};
|
|
29614
30196
|
pending = () => this.#database.prepare(
|
|
29615
30197
|
`SELECT envelope_json
|
|
29616
30198
|
FROM relay_session_event_outbox
|
|
29617
30199
|
ORDER BY ordinal`
|
|
29618
30200
|
).all().map(readPendingEnvelope);
|
|
29619
|
-
|
|
30201
|
+
pendingBatch = (limit) => {
|
|
30202
|
+
if (!Number.isSafeInteger(limit) || limit < 1) {
|
|
30203
|
+
throw new Error("Session event replay batch limit must be a positive integer");
|
|
30204
|
+
}
|
|
30205
|
+
return this.#database.prepare(
|
|
30206
|
+
`WITH ranked_events AS (
|
|
30207
|
+
SELECT ordinal,
|
|
30208
|
+
ROW_NUMBER() OVER (
|
|
30209
|
+
PARTITION BY source_key
|
|
30210
|
+
ORDER BY ordinal
|
|
30211
|
+
) AS source_position
|
|
30212
|
+
FROM relay_session_event_outbox
|
|
30213
|
+
)
|
|
30214
|
+
SELECT outbox.envelope_json
|
|
30215
|
+
FROM ranked_events AS ranked
|
|
30216
|
+
INNER JOIN relay_session_event_outbox AS outbox
|
|
30217
|
+
ON outbox.ordinal = ranked.ordinal
|
|
30218
|
+
ORDER BY ranked.source_position, outbox.ordinal
|
|
30219
|
+
LIMIT ?`
|
|
30220
|
+
).all(limit).map(readPendingEnvelope);
|
|
30221
|
+
};
|
|
30222
|
+
pendingSourceHeads = (limit, excludedEventIds, excludedSourceKeys) => {
|
|
30223
|
+
if (!Number.isSafeInteger(limit) || limit < 1) {
|
|
30224
|
+
throw new Error("Session event replay batch limit must be a positive integer");
|
|
30225
|
+
}
|
|
30226
|
+
const exclusions = [];
|
|
30227
|
+
const parameters = [];
|
|
30228
|
+
if (excludedEventIds.length > 0) {
|
|
30229
|
+
exclusions.push(`event_id NOT IN (${excludedEventIds.map(() => "?").join(", ")})`);
|
|
30230
|
+
parameters.push(...excludedEventIds);
|
|
30231
|
+
}
|
|
30232
|
+
if (excludedSourceKeys.length > 0) {
|
|
30233
|
+
exclusions.push(`source_key NOT IN (${excludedSourceKeys.map(() => "?").join(", ")})`);
|
|
30234
|
+
parameters.push(...excludedSourceKeys);
|
|
30235
|
+
}
|
|
30236
|
+
parameters.push(limit.toString());
|
|
30237
|
+
const where = exclusions.length === 0 ? "" : `WHERE ${exclusions.join(" AND ")}`;
|
|
30238
|
+
return this.#database.prepare(
|
|
30239
|
+
`WITH available_events AS (
|
|
30240
|
+
SELECT ordinal, source_key
|
|
30241
|
+
FROM relay_session_event_outbox
|
|
30242
|
+
${where}
|
|
30243
|
+
), ranked_events AS (
|
|
30244
|
+
SELECT ordinal,
|
|
30245
|
+
ROW_NUMBER() OVER (
|
|
30246
|
+
PARTITION BY source_key
|
|
30247
|
+
ORDER BY ordinal
|
|
30248
|
+
) AS source_position
|
|
30249
|
+
FROM available_events
|
|
30250
|
+
)
|
|
30251
|
+
SELECT outbox.envelope_json
|
|
30252
|
+
FROM ranked_events AS ranked
|
|
30253
|
+
INNER JOIN relay_session_event_outbox AS outbox
|
|
30254
|
+
ON outbox.ordinal = ranked.ordinal
|
|
30255
|
+
WHERE ranked.source_position = 1
|
|
30256
|
+
ORDER BY outbox.ordinal
|
|
30257
|
+
LIMIT ?`
|
|
30258
|
+
).all(...parameters).map(readPendingEnvelope);
|
|
30259
|
+
};
|
|
30260
|
+
};
|
|
30261
|
+
function validateLimits(limits) {
|
|
30262
|
+
const values = [
|
|
30263
|
+
limits.criticalEventBytes,
|
|
30264
|
+
limits.criticalEventCount,
|
|
30265
|
+
limits.runtimeDeliveryBytes,
|
|
30266
|
+
limits.runtimeDeliveryCount,
|
|
30267
|
+
limits.standardEventBytes,
|
|
30268
|
+
limits.standardEventCount
|
|
30269
|
+
];
|
|
30270
|
+
if (values.some((value) => !Number.isSafeInteger(value) || value < 1)) {
|
|
30271
|
+
throw new Error("Session event outbox limits must be positive integers");
|
|
30272
|
+
}
|
|
30273
|
+
if (limits.criticalEventBytes < limits.standardEventBytes || limits.criticalEventCount < limits.standardEventCount) {
|
|
30274
|
+
throw new Error("Critical Session event capacity must include the standard capacity");
|
|
30275
|
+
}
|
|
30276
|
+
}
|
|
30277
|
+
function runtimeDeliveryState(row) {
|
|
30278
|
+
const state = rowString2(row, "state");
|
|
30279
|
+
if (state !== "received" && state !== "relay_committed" && state !== "processed") {
|
|
30280
|
+
throw new Error("SQLite returned an invalid Runtime delivery state");
|
|
30281
|
+
}
|
|
30282
|
+
return state;
|
|
30283
|
+
}
|
|
29620
30284
|
function isDaemonExecutionState(value) {
|
|
29621
30285
|
return value === "cancelled" || value === "dispatching" || value === "failed" || value === "running" || value === "succeeded";
|
|
29622
30286
|
}
|
|
@@ -29632,7 +30296,7 @@ var systemDaemonTimer = new SystemDaemonTimer();
|
|
|
29632
30296
|
|
|
29633
30297
|
// src/run-daemon.ts
|
|
29634
30298
|
var RELAY_RECONNECT_DELAY_MILLISECONDS = 500;
|
|
29635
|
-
var
|
|
30299
|
+
var AGENT_ACTIVITY_COALESCING_MILLISECONDS = 100;
|
|
29636
30300
|
var ACTIVE_TURN_RECONCILIATION_TIMEOUT_MILLISECONDS = 2e4;
|
|
29637
30301
|
var ACTIVE_TURN_RECONCILIATION_CLOSE_CODE = 4009;
|
|
29638
30302
|
function botIpcSocketPath() {
|
|
@@ -29641,54 +30305,6 @@ function botIpcSocketPath() {
|
|
|
29641
30305
|
}
|
|
29642
30306
|
return join16("/tmp", `mobius-bot-${randomUUID6()}.sock`);
|
|
29643
30307
|
}
|
|
29644
|
-
var RelayConnectionRefreshState = class {
|
|
29645
|
-
};
|
|
29646
|
-
var ScheduledRelayConnectionRefreshState = class extends RelayConnectionRefreshState {
|
|
29647
|
-
};
|
|
29648
|
-
var DueRelayConnectionRefreshState = class extends RelayConnectionRefreshState {
|
|
29649
|
-
};
|
|
29650
|
-
var FinishedRelayConnectionRefreshState = class extends RelayConnectionRefreshState {
|
|
29651
|
-
};
|
|
29652
|
-
var RelayConnectionRefresh = class {
|
|
29653
|
-
#isSafe;
|
|
29654
|
-
#refresh;
|
|
29655
|
-
#scheduledTask;
|
|
29656
|
-
#state = new ScheduledRelayConnectionRefreshState();
|
|
29657
|
-
constructor(timer, isSafe, refresh) {
|
|
29658
|
-
this.#isSafe = isSafe;
|
|
29659
|
-
this.#refresh = refresh;
|
|
29660
|
-
this.#scheduledTask = timer.schedule(this.#request, RELAY_CONNECTION_REFRESH_MILLISECONDS);
|
|
29661
|
-
}
|
|
29662
|
-
afterTurnFinished = () => {
|
|
29663
|
-
if (this.#state instanceof DueRelayConnectionRefreshState) {
|
|
29664
|
-
this.#request();
|
|
29665
|
-
return;
|
|
29666
|
-
}
|
|
29667
|
-
if (this.#state instanceof ScheduledRelayConnectionRefreshState || this.#state instanceof FinishedRelayConnectionRefreshState) {
|
|
29668
|
-
return;
|
|
29669
|
-
}
|
|
29670
|
-
throw new Error("Unknown relay connection refresh state");
|
|
29671
|
-
};
|
|
29672
|
-
close = () => {
|
|
29673
|
-
this.#scheduledTask.cancel();
|
|
29674
|
-
this.#state = new FinishedRelayConnectionRefreshState();
|
|
29675
|
-
};
|
|
29676
|
-
#request = () => {
|
|
29677
|
-
if (this.#state instanceof FinishedRelayConnectionRefreshState) {
|
|
29678
|
-
return;
|
|
29679
|
-
}
|
|
29680
|
-
if (!(this.#state instanceof ScheduledRelayConnectionRefreshState) && !(this.#state instanceof DueRelayConnectionRefreshState)) {
|
|
29681
|
-
throw new Error("Unknown relay connection refresh state");
|
|
29682
|
-
}
|
|
29683
|
-
if (!this.#isSafe()) {
|
|
29684
|
-
this.#state = new DueRelayConnectionRefreshState();
|
|
29685
|
-
return;
|
|
29686
|
-
}
|
|
29687
|
-
this.#state = new FinishedRelayConnectionRefreshState();
|
|
29688
|
-
this.#scheduledTask.cancel();
|
|
29689
|
-
this.#refresh();
|
|
29690
|
-
};
|
|
29691
|
-
};
|
|
29692
30308
|
var DaemonAcpObserver = class {
|
|
29693
30309
|
#record;
|
|
29694
30310
|
constructor(record2) {
|
|
@@ -29883,8 +30499,7 @@ var ActiveTurnReconciliationError = class extends Error {
|
|
|
29883
30499
|
}
|
|
29884
30500
|
};
|
|
29885
30501
|
function activeTurnReconciliationEndpoint(config2, turn) {
|
|
29886
|
-
const endpoint =
|
|
29887
|
-
endpoint.protocol = endpoint.protocol === "wss:" ? "https:" : "http:";
|
|
30502
|
+
const endpoint = relayHttpEndpoint(config2.relayUrl);
|
|
29888
30503
|
let channelPath = "";
|
|
29889
30504
|
if (endpoint.pathname.endsWith("/daemon")) {
|
|
29890
30505
|
channelPath = endpoint.pathname.slice(0, -"/daemon".length);
|
|
@@ -29896,7 +30511,6 @@ function activeTurnReconciliationEndpoint(config2, turn) {
|
|
|
29896
30511
|
channelPath = `${endpoint.pathname.slice(0, -runtimePath.length)}/channels/${encodeURIComponent(config2.channelId)}`;
|
|
29897
30512
|
}
|
|
29898
30513
|
endpoint.pathname = `${channelPath}/sessions/${encodeURIComponent(turn.command.sessionId)}/attempts/${encodeURIComponent(turn.attemptId)}/reconciliation`;
|
|
29899
|
-
endpoint.searchParams.delete("liveness");
|
|
29900
30514
|
return endpoint;
|
|
29901
30515
|
}
|
|
29902
30516
|
async function reconcileActiveTurn(config2, turn, signal) {
|
|
@@ -29908,7 +30522,7 @@ async function reconcileActiveTurn(config2, turn, signal) {
|
|
|
29908
30522
|
response = await fetch6(activeTurnReconciliationEndpoint(config2, turn), {
|
|
29909
30523
|
body: JSON.stringify({ outcome: { kind: "running" } }),
|
|
29910
30524
|
dispatcher: environmentProxyDispatcher,
|
|
29911
|
-
headers: { "content-type": "application/json" },
|
|
30525
|
+
headers: { ...runtimeAuthorization(config2.relayUrl), "content-type": "application/json" },
|
|
29912
30526
|
method: "POST",
|
|
29913
30527
|
signal: requestSignal
|
|
29914
30528
|
});
|
|
@@ -29953,7 +30567,8 @@ var DaemonSessionEventConnectionState = class {
|
|
|
29953
30567
|
};
|
|
29954
30568
|
var DisconnectedDaemonSessionEventState = class extends DaemonSessionEventConnectionState {
|
|
29955
30569
|
disconnect = () => this;
|
|
29956
|
-
|
|
30570
|
+
matches = () => false;
|
|
30571
|
+
send = () => false;
|
|
29957
30572
|
};
|
|
29958
30573
|
var ConnectedDaemonSessionEventState = class extends DaemonSessionEventConnectionState {
|
|
29959
30574
|
#socket;
|
|
@@ -29962,38 +30577,210 @@ var ConnectedDaemonSessionEventState = class extends DaemonSessionEventConnectio
|
|
|
29962
30577
|
this.#socket = socket;
|
|
29963
30578
|
}
|
|
29964
30579
|
disconnect = (socket) => socket === this.#socket ? new DisconnectedDaemonSessionEventState() : this;
|
|
30580
|
+
matches = (socket) => socket === this.#socket;
|
|
29965
30581
|
send = (message) => {
|
|
29966
|
-
if (this.#socket.readyState
|
|
29967
|
-
|
|
29968
|
-
|
|
29969
|
-
|
|
29970
|
-
|
|
30582
|
+
if (this.#socket.readyState !== WebSocket2.OPEN) {
|
|
30583
|
+
return false;
|
|
30584
|
+
}
|
|
30585
|
+
try {
|
|
30586
|
+
this.#socket.send(message);
|
|
30587
|
+
return true;
|
|
30588
|
+
} catch {
|
|
30589
|
+
return false;
|
|
29971
30590
|
}
|
|
29972
30591
|
};
|
|
29973
30592
|
};
|
|
30593
|
+
var SESSION_EVENT_REPLAY_WINDOW = 16;
|
|
30594
|
+
var SESSION_EVENT_SOURCE_REPLAY_WINDOW = 4;
|
|
30595
|
+
var SESSION_EVENT_REPLAY_BYTE_LIMIT = 32 * 1024 * 1024;
|
|
30596
|
+
var InFlightSessionEvent = class {
|
|
30597
|
+
byteLength;
|
|
30598
|
+
sourceKey;
|
|
30599
|
+
constructor(sourceKey, byteLength) {
|
|
30600
|
+
this.byteLength = byteLength;
|
|
30601
|
+
this.sourceKey = sourceKey;
|
|
30602
|
+
}
|
|
30603
|
+
};
|
|
30604
|
+
function sessionEventSourceKey(envelope2) {
|
|
30605
|
+
return envelope2.source.kind === "attempt" ? `attempt:${envelope2.sessionId}:${envelope2.source.attemptId}` : `runtime:${envelope2.sessionId}`;
|
|
30606
|
+
}
|
|
29974
30607
|
var DaemonSessionEventPublisher = class {
|
|
29975
30608
|
#outbox;
|
|
30609
|
+
#inFlightEvents = /* @__PURE__ */ new Map();
|
|
30610
|
+
#inFlightBytes = 0;
|
|
29976
30611
|
#connection = new DisconnectedDaemonSessionEventState();
|
|
29977
30612
|
constructor(outbox) {
|
|
29978
30613
|
this.#outbox = outbox;
|
|
29979
30614
|
}
|
|
29980
30615
|
connect = (socket) => {
|
|
29981
|
-
|
|
29982
|
-
this.#
|
|
29983
|
-
|
|
29984
|
-
connection.send(pending);
|
|
29985
|
-
}
|
|
30616
|
+
this.#connection = new ConnectedDaemonSessionEventState(socket);
|
|
30617
|
+
this.#clearInFlight();
|
|
30618
|
+
this.#publishNextBatch();
|
|
29986
30619
|
};
|
|
29987
30620
|
disconnect = (socket) => {
|
|
30621
|
+
if (!this.#connection.matches(socket)) {
|
|
30622
|
+
return;
|
|
30623
|
+
}
|
|
29988
30624
|
this.#connection = this.#connection.disconnect(socket);
|
|
30625
|
+
this.#clearInFlight();
|
|
29989
30626
|
};
|
|
29990
30627
|
recordAttempt = (sessionId, payload, attemptId) => {
|
|
29991
|
-
|
|
30628
|
+
const kind = decodeMessageKind(payload);
|
|
30629
|
+
if (kind === SESSION_COMPLETED_KIND || kind === SESSION_FAILED_KIND) {
|
|
30630
|
+
this.#outbox.enqueueCritical(sessionId, payload, attemptId);
|
|
30631
|
+
} else {
|
|
30632
|
+
this.#outbox.enqueue(sessionId, payload, attemptId);
|
|
30633
|
+
}
|
|
30634
|
+
this.#publishNextBatch();
|
|
29992
30635
|
};
|
|
29993
30636
|
recordRuntime = (sessionId, payload) => {
|
|
29994
|
-
this.#
|
|
30637
|
+
this.#outbox.enqueue(sessionId, payload);
|
|
30638
|
+
this.#publishNextBatch();
|
|
30639
|
+
};
|
|
30640
|
+
acknowledge = (acknowledgement) => {
|
|
30641
|
+
if (!this.#outbox.acknowledge(acknowledgement)) {
|
|
30642
|
+
return;
|
|
30643
|
+
}
|
|
30644
|
+
this.#removeInFlight(acknowledgement.eventId);
|
|
30645
|
+
this.#publishNextBatch();
|
|
30646
|
+
};
|
|
30647
|
+
quarantine = (rejection) => {
|
|
30648
|
+
const quarantined = this.#outbox.quarantine(rejection);
|
|
30649
|
+
if (!quarantined) {
|
|
30650
|
+
return false;
|
|
30651
|
+
}
|
|
30652
|
+
this.#removeInFlight(rejection.eventId);
|
|
30653
|
+
this.#publishNextBatch();
|
|
30654
|
+
return true;
|
|
30655
|
+
};
|
|
30656
|
+
#publishNextBatch = () => {
|
|
30657
|
+
while (this.#inFlightEvents.size < SESSION_EVENT_REPLAY_WINDOW) {
|
|
30658
|
+
const sourceCounts = /* @__PURE__ */ new Map();
|
|
30659
|
+
for (const inFlight of this.#inFlightEvents.values()) {
|
|
30660
|
+
sourceCounts.set(inFlight.sourceKey, (sourceCounts.get(inFlight.sourceKey) ?? 0) + 1);
|
|
30661
|
+
}
|
|
30662
|
+
const saturatedSources = [...sourceCounts.entries()].filter(([, count]) => count >= SESSION_EVENT_SOURCE_REPLAY_WINDOW).map(([sourceKey]) => sourceKey);
|
|
30663
|
+
const batch = this.#outbox.pendingSourceHeads(
|
|
30664
|
+
SESSION_EVENT_REPLAY_WINDOW - this.#inFlightEvents.size,
|
|
30665
|
+
[...this.#inFlightEvents.keys()],
|
|
30666
|
+
saturatedSources
|
|
30667
|
+
);
|
|
30668
|
+
let sent = 0;
|
|
30669
|
+
for (const encoded of batch) {
|
|
30670
|
+
const envelope2 = decodeSessionEventEnvelope(encoded);
|
|
30671
|
+
const sourceKey = sessionEventSourceKey(envelope2);
|
|
30672
|
+
const sourceCount = sourceCounts.get(sourceKey) ?? 0;
|
|
30673
|
+
if (sourceCount >= SESSION_EVENT_SOURCE_REPLAY_WINDOW) {
|
|
30674
|
+
continue;
|
|
30675
|
+
}
|
|
30676
|
+
const byteLength = new TextEncoder().encode(encoded).byteLength;
|
|
30677
|
+
if (this.#inFlightBytes + byteLength > SESSION_EVENT_REPLAY_BYTE_LIMIT) {
|
|
30678
|
+
continue;
|
|
30679
|
+
}
|
|
30680
|
+
if (!this.#connection.send(encoded)) {
|
|
30681
|
+
this.#clearInFlight();
|
|
30682
|
+
return;
|
|
30683
|
+
}
|
|
30684
|
+
this.#inFlightEvents.set(envelope2.eventId, new InFlightSessionEvent(sourceKey, byteLength));
|
|
30685
|
+
this.#inFlightBytes += byteLength;
|
|
30686
|
+
sourceCounts.set(sourceKey, sourceCount + 1);
|
|
30687
|
+
sent += 1;
|
|
30688
|
+
}
|
|
30689
|
+
if (sent === 0) {
|
|
30690
|
+
return;
|
|
30691
|
+
}
|
|
30692
|
+
}
|
|
30693
|
+
};
|
|
30694
|
+
#removeInFlight = (eventId) => {
|
|
30695
|
+
const inFlight = this.#inFlightEvents.get(eventId);
|
|
30696
|
+
if (inFlight === void 0) {
|
|
30697
|
+
return;
|
|
30698
|
+
}
|
|
30699
|
+
this.#inFlightEvents.delete(eventId);
|
|
30700
|
+
this.#inFlightBytes -= inFlight.byteLength;
|
|
30701
|
+
};
|
|
30702
|
+
#clearInFlight = () => {
|
|
30703
|
+
this.#inFlightEvents.clear();
|
|
30704
|
+
this.#inFlightBytes = 0;
|
|
29995
30705
|
};
|
|
29996
30706
|
};
|
|
30707
|
+
var CoalescingDaemonActivityPublisher = class {
|
|
30708
|
+
#pending = /* @__PURE__ */ new Map();
|
|
30709
|
+
#publish;
|
|
30710
|
+
#publishedActivityIds = /* @__PURE__ */ new Set();
|
|
30711
|
+
#scheduled = /* @__PURE__ */ new Map();
|
|
30712
|
+
#timer;
|
|
30713
|
+
constructor(timer, publish) {
|
|
30714
|
+
this.#publish = publish;
|
|
30715
|
+
this.#timer = timer;
|
|
30716
|
+
}
|
|
30717
|
+
flush = () => {
|
|
30718
|
+
const activities = [...this.#pending.values()].sort((left, right) => left.sequence - right.sequence);
|
|
30719
|
+
for (const task of this.#scheduled.values()) {
|
|
30720
|
+
task.cancel();
|
|
30721
|
+
}
|
|
30722
|
+
this.#pending.clear();
|
|
30723
|
+
this.#scheduled.clear();
|
|
30724
|
+
for (const activity of activities) {
|
|
30725
|
+
this.#publish(activity);
|
|
30726
|
+
}
|
|
30727
|
+
};
|
|
30728
|
+
record = (activity) => {
|
|
30729
|
+
if (activity.status !== "pending" && activity.status !== "running") {
|
|
30730
|
+
this.#discardPending(activity.activityId);
|
|
30731
|
+
this.#publishedActivityIds.add(activity.activityId);
|
|
30732
|
+
this.#publish(activity);
|
|
30733
|
+
return Promise.resolve();
|
|
30734
|
+
}
|
|
30735
|
+
if (!this.#publishedActivityIds.has(activity.activityId)) {
|
|
30736
|
+
this.#publishedActivityIds.add(activity.activityId);
|
|
30737
|
+
this.#publish(activity);
|
|
30738
|
+
return Promise.resolve();
|
|
30739
|
+
}
|
|
30740
|
+
this.#pending.set(activity.activityId, activity);
|
|
30741
|
+
if (!this.#scheduled.has(activity.activityId)) {
|
|
30742
|
+
this.#scheduled.set(
|
|
30743
|
+
activity.activityId,
|
|
30744
|
+
this.#timer.schedule(
|
|
30745
|
+
() => this.#flushActivity(activity.activityId),
|
|
30746
|
+
AGENT_ACTIVITY_COALESCING_MILLISECONDS
|
|
30747
|
+
)
|
|
30748
|
+
);
|
|
30749
|
+
}
|
|
30750
|
+
return Promise.resolve();
|
|
30751
|
+
};
|
|
30752
|
+
#discardPending = (activityId) => {
|
|
30753
|
+
this.#scheduled.get(activityId)?.cancel();
|
|
30754
|
+
this.#scheduled.delete(activityId);
|
|
30755
|
+
this.#pending.delete(activityId);
|
|
30756
|
+
};
|
|
30757
|
+
#flushActivity = (activityId) => {
|
|
30758
|
+
const activity = this.#pending.get(activityId);
|
|
30759
|
+
this.#discardPending(activityId);
|
|
30760
|
+
if (activity !== void 0) {
|
|
30761
|
+
this.#publish(activity);
|
|
30762
|
+
}
|
|
30763
|
+
};
|
|
30764
|
+
};
|
|
30765
|
+
function encodeDaemonActivity(command, activity) {
|
|
30766
|
+
return encodeAgentActivityEvent({
|
|
30767
|
+
activityId: activity.activityId,
|
|
30768
|
+
activityType: activity.activityType,
|
|
30769
|
+
commandId: command.commandId,
|
|
30770
|
+
content: activity.content,
|
|
30771
|
+
detail: activity.detail,
|
|
30772
|
+
input: activity.input,
|
|
30773
|
+
kind: AGENT_ACTIVITY_KIND,
|
|
30774
|
+
locations: activity.locations,
|
|
30775
|
+
name: activity.name,
|
|
30776
|
+
output: activity.output,
|
|
30777
|
+
sequence: activity.sequence,
|
|
30778
|
+
sessionId: command.sessionId,
|
|
30779
|
+
status: activity.status,
|
|
30780
|
+
title: activity.title,
|
|
30781
|
+
toolKind: activity.toolKind
|
|
30782
|
+
});
|
|
30783
|
+
}
|
|
29997
30784
|
function reportFailure(publisher, command, code, message, attemptId) {
|
|
29998
30785
|
publisher.recordAttempt(
|
|
29999
30786
|
command.sessionId,
|
|
@@ -30167,10 +30954,16 @@ async function executeSessionConfigure(publisher, socket, command, config2, dire
|
|
|
30167
30954
|
})
|
|
30168
30955
|
);
|
|
30169
30956
|
} catch (error61) {
|
|
30957
|
+
if (signal.aborted) {
|
|
30958
|
+
signal.throwIfAborted();
|
|
30959
|
+
}
|
|
30170
30960
|
reportOperationFailure(socket, command.requestId, command.sessionId, error61);
|
|
30171
30961
|
}
|
|
30172
30962
|
}
|
|
30173
|
-
async function executePrompt(outbox, publisher, command, config2, directories, audit, sessions, attemptId, signal) {
|
|
30963
|
+
async function executePrompt(outbox, publisher, command, config2, directories, audit, sessions, attemptId, timer, signal) {
|
|
30964
|
+
const activities = new CoalescingDaemonActivityPublisher(timer, (activity) => {
|
|
30965
|
+
publisher.recordAttempt(command.sessionId, encodeDaemonActivity(command, activity), attemptId);
|
|
30966
|
+
});
|
|
30174
30967
|
try {
|
|
30175
30968
|
const resolved = await resolveSessionDirectories(
|
|
30176
30969
|
directories,
|
|
@@ -30227,30 +31020,7 @@ async function executePrompt(outbox, publisher, command, config2, directories, a
|
|
|
30227
31020
|
cwd: resolved.cwd,
|
|
30228
31021
|
disposition: command.disposition,
|
|
30229
31022
|
images: attachments.images,
|
|
30230
|
-
onActivity:
|
|
30231
|
-
publisher.recordAttempt(
|
|
30232
|
-
command.sessionId,
|
|
30233
|
-
encodeAgentActivityEvent({
|
|
30234
|
-
activityId: activity.activityId,
|
|
30235
|
-
activityType: activity.activityType,
|
|
30236
|
-
commandId: command.commandId,
|
|
30237
|
-
content: activity.content,
|
|
30238
|
-
detail: activity.detail,
|
|
30239
|
-
input: activity.input,
|
|
30240
|
-
kind: AGENT_ACTIVITY_KIND,
|
|
30241
|
-
locations: activity.locations,
|
|
30242
|
-
name: activity.name,
|
|
30243
|
-
output: activity.output,
|
|
30244
|
-
sequence: activity.sequence,
|
|
30245
|
-
sessionId: command.sessionId,
|
|
30246
|
-
status: activity.status,
|
|
30247
|
-
title: activity.title,
|
|
30248
|
-
toolKind: activity.toolKind
|
|
30249
|
-
}),
|
|
30250
|
-
attemptId
|
|
30251
|
-
);
|
|
30252
|
-
return Promise.resolve();
|
|
30253
|
-
},
|
|
31023
|
+
onActivity: activities.record,
|
|
30254
31024
|
onText: (text, messageId) => {
|
|
30255
31025
|
publisher.recordAttempt(
|
|
30256
31026
|
command.sessionId,
|
|
@@ -30273,6 +31043,7 @@ async function executePrompt(outbox, publisher, command, config2, directories, a
|
|
|
30273
31043
|
});
|
|
30274
31044
|
stopReason = result.stopReason;
|
|
30275
31045
|
}
|
|
31046
|
+
activities.flush();
|
|
30276
31047
|
publisher.recordAttempt(
|
|
30277
31048
|
command.sessionId,
|
|
30278
31049
|
encodeSessionCompletedEvent({
|
|
@@ -30290,6 +31061,7 @@ async function executePrompt(outbox, publisher, command, config2, directories, a
|
|
|
30290
31061
|
sessionId: command.sessionId
|
|
30291
31062
|
});
|
|
30292
31063
|
} catch (error61) {
|
|
31064
|
+
activities.flush();
|
|
30293
31065
|
audit.record({
|
|
30294
31066
|
commandId: command.commandId,
|
|
30295
31067
|
event: "session_failed",
|
|
@@ -30328,7 +31100,10 @@ function botExecutionAgent(agent, contextHandle, socketPath) {
|
|
|
30328
31100
|
agent.capabilities
|
|
30329
31101
|
);
|
|
30330
31102
|
}
|
|
30331
|
-
async function executeBotPrompt(outbox, publisher, command, config2, directories, audit, sessions, grants, contexts, applicator, ipc, attemptId, signal) {
|
|
31103
|
+
async function executeBotPrompt(outbox, publisher, command, config2, directories, audit, sessions, grants, contexts, applicator, ipc, attemptId, timer, signal) {
|
|
31104
|
+
const activities = new CoalescingDaemonActivityPublisher(timer, (activity) => {
|
|
31105
|
+
publisher.recordAttempt(command.sessionId, encodeDaemonActivity(command, activity), attemptId);
|
|
31106
|
+
});
|
|
30332
31107
|
try {
|
|
30333
31108
|
const deadline = Date.parse(command.executionDeadlineAt);
|
|
30334
31109
|
const remaining = deadline - Date.now();
|
|
@@ -30412,30 +31187,7 @@ async function executeBotPrompt(outbox, publisher, command, config2, directories
|
|
|
30412
31187
|
commandId: command.commandId,
|
|
30413
31188
|
cwd: resolved.cwd,
|
|
30414
31189
|
images: attachments.images,
|
|
30415
|
-
onActivity:
|
|
30416
|
-
publisher.recordAttempt(
|
|
30417
|
-
command.sessionId,
|
|
30418
|
-
encodeAgentActivityEvent({
|
|
30419
|
-
activityId: activity.activityId,
|
|
30420
|
-
activityType: activity.activityType,
|
|
30421
|
-
commandId: command.commandId,
|
|
30422
|
-
content: activity.content,
|
|
30423
|
-
detail: activity.detail,
|
|
30424
|
-
input: activity.input,
|
|
30425
|
-
kind: AGENT_ACTIVITY_KIND,
|
|
30426
|
-
locations: activity.locations,
|
|
30427
|
-
name: activity.name,
|
|
30428
|
-
output: activity.output,
|
|
30429
|
-
sequence: activity.sequence,
|
|
30430
|
-
sessionId: command.sessionId,
|
|
30431
|
-
status: activity.status,
|
|
30432
|
-
title: activity.title,
|
|
30433
|
-
toolKind: activity.toolKind
|
|
30434
|
-
}),
|
|
30435
|
-
attemptId
|
|
30436
|
-
);
|
|
30437
|
-
return Promise.resolve();
|
|
30438
|
-
},
|
|
31190
|
+
onActivity: activities.record,
|
|
30439
31191
|
onText: (text, messageId) => {
|
|
30440
31192
|
publisher.recordAttempt(
|
|
30441
31193
|
command.sessionId,
|
|
@@ -30455,6 +31207,7 @@ async function executeBotPrompt(outbox, publisher, command, config2, directories
|
|
|
30455
31207
|
sessionId: command.sessionId,
|
|
30456
31208
|
signal: executionSignal
|
|
30457
31209
|
});
|
|
31210
|
+
activities.flush();
|
|
30458
31211
|
publisher.recordAttempt(
|
|
30459
31212
|
command.sessionId,
|
|
30460
31213
|
encodeSessionCompletedEvent({
|
|
@@ -30479,6 +31232,7 @@ async function executeBotPrompt(outbox, publisher, command, config2, directories
|
|
|
30479
31232
|
lease.release();
|
|
30480
31233
|
}
|
|
30481
31234
|
} catch (error61) {
|
|
31235
|
+
activities.flush();
|
|
30482
31236
|
const failure = Date.parse(command.executionDeadlineAt) <= Date.now() ? new LocalBotAuthorizationError("turn_expired", "The Bot execution deadline has expired", {
|
|
30483
31237
|
cause: error61
|
|
30484
31238
|
}) : error61;
|
|
@@ -30518,15 +31272,15 @@ function waitForRelayRetry(signal, timer) {
|
|
|
30518
31272
|
signal.addEventListener("abort", finish, { once: true });
|
|
30519
31273
|
});
|
|
30520
31274
|
}
|
|
30521
|
-
async function publishAgentCatalogUntilAvailable(config2, signal, timer) {
|
|
31275
|
+
async function publishAgentCatalogUntilAvailable(config2, diagnostics, signal, timer) {
|
|
30522
31276
|
try {
|
|
30523
31277
|
await publishAgentCatalog(config2, signal);
|
|
30524
31278
|
return;
|
|
30525
|
-
} catch {
|
|
31279
|
+
} catch (error61) {
|
|
30526
31280
|
if (signal.aborted) {
|
|
30527
31281
|
return;
|
|
30528
31282
|
}
|
|
30529
|
-
|
|
31283
|
+
diagnostics.catalogFailed(error61);
|
|
30530
31284
|
}
|
|
30531
31285
|
while (!signal.aborted) {
|
|
30532
31286
|
await waitForRelayRetry(signal, timer);
|
|
@@ -30540,11 +31294,13 @@ async function publishAgentCatalogUntilAvailable(config2, signal, timer) {
|
|
|
30540
31294
|
}
|
|
30541
31295
|
}
|
|
30542
31296
|
}
|
|
30543
|
-
async function runRelayConnection(config2, directories, audit, sessions, installations, installationObserver, nativeSessions, outbox, publisher, grants, contexts, applicator, ipc, activeTurns, activeAttemptIds, onConnected, signal, timer) {
|
|
31297
|
+
async function runRelayConnection(config2, directories, audit, sessions, installations, installationObserver, nativeSessions, outbox, publisher, grants, contexts, applicator, ipc, activeTurns, activeAttemptIds, connectionEpoch, diagnostics, onConnected, signal, timer) {
|
|
30544
31298
|
const relayUrl = new URL(config2.relayUrl);
|
|
31299
|
+
relayUrl.searchParams.set("deliveryReceipts", "1");
|
|
30545
31300
|
relayUrl.searchParams.set("liveness", "1");
|
|
30546
31301
|
const socket = await connectRelay(relayUrl, signal);
|
|
30547
31302
|
const reconnectingTurns = [...activeTurns.values()];
|
|
31303
|
+
diagnostics.connectionOpened(connectionEpoch);
|
|
30548
31304
|
onConnected();
|
|
30549
31305
|
const connectionController = new AbortController();
|
|
30550
31306
|
const nativeRequests = /* @__PURE__ */ new Map();
|
|
@@ -30553,7 +31309,6 @@ async function runRelayConnection(config2, directories, audit, sessions, install
|
|
|
30553
31309
|
await new Promise((resolve5, reject) => {
|
|
30554
31310
|
let finished = false;
|
|
30555
31311
|
const cleanup = () => {
|
|
30556
|
-
connectionRefresh.close();
|
|
30557
31312
|
connectionLiveness.close();
|
|
30558
31313
|
signal.removeEventListener("abort", stop);
|
|
30559
31314
|
socket.removeEventListener("close", handleClose);
|
|
@@ -30571,61 +31326,282 @@ async function runRelayConnection(config2, directories, audit, sessions, install
|
|
|
30571
31326
|
cleanup();
|
|
30572
31327
|
resolve5();
|
|
30573
31328
|
};
|
|
30574
|
-
const rejectConnection = () => {
|
|
31329
|
+
const rejectConnection = (failure) => {
|
|
30575
31330
|
if (finished) {
|
|
30576
31331
|
return;
|
|
30577
31332
|
}
|
|
30578
31333
|
finished = true;
|
|
30579
31334
|
cleanup();
|
|
30580
|
-
reject(
|
|
31335
|
+
reject(failure);
|
|
30581
31336
|
};
|
|
30582
31337
|
const stop = () => {
|
|
31338
|
+
diagnostics.connectionClosed(connectionEpoch, 1e3, "Daemon stopped", "daemon_stop");
|
|
30583
31339
|
closeRelaySocket(socket, 1e3, "Daemon stopped");
|
|
30584
31340
|
resolveConnection();
|
|
30585
31341
|
};
|
|
30586
|
-
const
|
|
30587
|
-
|
|
30588
|
-
|
|
31342
|
+
const failAndClose = (failure, code, reason, source) => {
|
|
31343
|
+
diagnostics.connectionFailed(connectionEpoch, failure, source);
|
|
31344
|
+
diagnostics.connectionClosed(connectionEpoch, code, reason, source);
|
|
31345
|
+
closeRelaySocket(socket, code, reason);
|
|
31346
|
+
rejectConnection(failure);
|
|
31347
|
+
};
|
|
31348
|
+
const processingDeliveryIds = /* @__PURE__ */ new Set();
|
|
31349
|
+
const processDeliveredPrompt = async (delivery) => {
|
|
31350
|
+
const command = decodeSessionPromptCommand(delivery.payload);
|
|
31351
|
+
if (command.sessionId !== delivery.sessionId) {
|
|
31352
|
+
throw new Error("Runtime delivery Session identity does not match its payload");
|
|
31353
|
+
}
|
|
31354
|
+
const active = activeTurns.get(command.commandId);
|
|
31355
|
+
if (active !== void 0) {
|
|
31356
|
+
await active.completion;
|
|
31357
|
+
return;
|
|
31358
|
+
}
|
|
31359
|
+
const receipt = outbox.claimExecution(command.commandId, command.sessionId, delivery.payload);
|
|
31360
|
+
if (receipt.kind === "duplicate") {
|
|
31361
|
+
if (receipt.state === "running") {
|
|
31362
|
+
reportFailure(
|
|
31363
|
+
publisher,
|
|
31364
|
+
command,
|
|
31365
|
+
"agent_execution_interrupted",
|
|
31366
|
+
"The local Agent execution was interrupted before it completed",
|
|
31367
|
+
receipt.attemptId
|
|
31368
|
+
);
|
|
31369
|
+
outbox.transitionExecution(command.commandId, "failed");
|
|
31370
|
+
return;
|
|
31371
|
+
}
|
|
31372
|
+
if (receipt.state !== "dispatching") {
|
|
31373
|
+
return;
|
|
31374
|
+
}
|
|
31375
|
+
}
|
|
31376
|
+
const turn = new ActiveDaemonTurn(command, receipt.attemptId);
|
|
31377
|
+
activeTurns.set(command.commandId, turn);
|
|
31378
|
+
if (command.disposition !== SessionPromptDisposition.Steer) {
|
|
31379
|
+
activeAttemptIds.set(command.sessionId, receipt.attemptId);
|
|
31380
|
+
}
|
|
31381
|
+
try {
|
|
31382
|
+
await executePrompt(
|
|
31383
|
+
outbox,
|
|
31384
|
+
publisher,
|
|
31385
|
+
command,
|
|
31386
|
+
config2,
|
|
31387
|
+
directories,
|
|
31388
|
+
audit,
|
|
31389
|
+
sessions,
|
|
31390
|
+
receipt.attemptId,
|
|
31391
|
+
timer,
|
|
31392
|
+
turn.signal
|
|
31393
|
+
);
|
|
31394
|
+
} finally {
|
|
31395
|
+
activeTurns.delete(command.commandId);
|
|
31396
|
+
if (activeAttemptIds.get(command.sessionId) === receipt.attemptId) {
|
|
31397
|
+
activeAttemptIds.delete(command.sessionId);
|
|
31398
|
+
}
|
|
31399
|
+
turn.finish();
|
|
31400
|
+
}
|
|
31401
|
+
};
|
|
31402
|
+
const processDeliveredPayload = async (delivery) => {
|
|
31403
|
+
const kind = decodeMessageKind(delivery.payload);
|
|
31404
|
+
if (kind === SESSION_CANCEL_KIND) {
|
|
31405
|
+
const cancellation = decodeSessionCancelCommand(delivery.payload);
|
|
31406
|
+
if (cancellation.sessionId !== delivery.sessionId) {
|
|
31407
|
+
throw new Error("Runtime delivery Session identity does not match its payload");
|
|
31408
|
+
}
|
|
31409
|
+
const turn = activeTurns.get(cancellation.commandId);
|
|
31410
|
+
if (turn === void 0 || !turn.matches(cancellation) || !turn.requestCancellation()) {
|
|
31411
|
+
return;
|
|
31412
|
+
}
|
|
31413
|
+
audit.record({
|
|
31414
|
+
commandId: cancellation.commandId,
|
|
31415
|
+
event: "session_cancellation_requested",
|
|
31416
|
+
sessionId: cancellation.sessionId
|
|
31417
|
+
});
|
|
31418
|
+
await sessions.cancelPrompt(cancellation.sessionId);
|
|
31419
|
+
return;
|
|
31420
|
+
}
|
|
31421
|
+
if (kind === SESSION_CONFIGURE_KIND) {
|
|
31422
|
+
const command = decodeSessionConfigureCommand(delivery.payload);
|
|
31423
|
+
if (command.sessionId !== delivery.sessionId) {
|
|
31424
|
+
throw new Error("Runtime delivery Session identity does not match its payload");
|
|
31425
|
+
}
|
|
31426
|
+
await executeSessionConfigure(
|
|
31427
|
+
publisher,
|
|
31428
|
+
socket,
|
|
31429
|
+
command,
|
|
31430
|
+
config2,
|
|
31431
|
+
directories,
|
|
31432
|
+
audit,
|
|
31433
|
+
sessions,
|
|
31434
|
+
connectionController.signal
|
|
31435
|
+
);
|
|
31436
|
+
return;
|
|
31437
|
+
}
|
|
31438
|
+
if (kind === SESSION_PERMISSION_RESPOND_KIND) {
|
|
31439
|
+
const command = decodeSessionPermissionRespondCommand(delivery.payload);
|
|
31440
|
+
if (command.sessionId !== delivery.sessionId) {
|
|
31441
|
+
throw new Error("Runtime delivery Session identity does not match its payload");
|
|
31442
|
+
}
|
|
31443
|
+
try {
|
|
31444
|
+
await sessions.respondPermission(command);
|
|
31445
|
+
audit.record({
|
|
31446
|
+
commandId: command.interactionId,
|
|
31447
|
+
event: "session_permission_resolved",
|
|
31448
|
+
sessionId: command.sessionId
|
|
31449
|
+
});
|
|
31450
|
+
} catch (error61) {
|
|
31451
|
+
reportOperationFailure(socket, command.interactionId, command.sessionId, error61);
|
|
31452
|
+
}
|
|
31453
|
+
return;
|
|
31454
|
+
}
|
|
31455
|
+
if (kind === SESSION_ELICITATION_RESPOND_KIND) {
|
|
31456
|
+
const command = decodeSessionElicitationRespondCommand(delivery.payload);
|
|
31457
|
+
if (command.sessionId !== delivery.sessionId) {
|
|
31458
|
+
throw new Error("Runtime delivery Session identity does not match its payload");
|
|
31459
|
+
}
|
|
31460
|
+
try {
|
|
31461
|
+
await sessions.respondElicitation(command);
|
|
31462
|
+
audit.record({
|
|
31463
|
+
commandId: command.interactionId,
|
|
31464
|
+
event: "session_elicitation_resolved",
|
|
31465
|
+
sessionId: command.sessionId
|
|
31466
|
+
});
|
|
31467
|
+
} catch (error61) {
|
|
31468
|
+
reportOperationFailure(socket, command.interactionId, command.sessionId, error61);
|
|
31469
|
+
}
|
|
31470
|
+
return;
|
|
31471
|
+
}
|
|
31472
|
+
if (kind === SESSION_PROMPT_KIND) {
|
|
31473
|
+
await processDeliveredPrompt(delivery);
|
|
31474
|
+
return;
|
|
31475
|
+
}
|
|
31476
|
+
throw new Error("Runtime delivery contains an unsupported protocol message");
|
|
31477
|
+
};
|
|
31478
|
+
const processRuntimeDelivery = (delivery, state) => {
|
|
31479
|
+
if (state === "processed" || processingDeliveryIds.has(delivery.deliveryId)) {
|
|
31480
|
+
return;
|
|
31481
|
+
}
|
|
31482
|
+
processingDeliveryIds.add(delivery.deliveryId);
|
|
31483
|
+
void processDeliveredPayload(delivery).then(() => {
|
|
31484
|
+
outbox.markRuntimeDeliveryProcessed(delivery.deliveryId, delivery.sessionId);
|
|
31485
|
+
}).catch((error61) => {
|
|
31486
|
+
if (connectionController.signal.aborted) {
|
|
31487
|
+
return;
|
|
31488
|
+
}
|
|
31489
|
+
console.error("Mobius daemon could not process a durable Runtime delivery", error61);
|
|
31490
|
+
failAndClose(
|
|
31491
|
+
new Error("Runtime delivery processing failed", { cause: error61 }),
|
|
31492
|
+
4008,
|
|
31493
|
+
"Invalid Runtime delivery",
|
|
31494
|
+
"delivery"
|
|
31495
|
+
);
|
|
31496
|
+
}).finally(() => processingDeliveryIds.delete(delivery.deliveryId));
|
|
31497
|
+
};
|
|
31498
|
+
const receiveRuntimeDelivery = (delivery) => {
|
|
31499
|
+
const payloadKind = decodeMessageKind(delivery.payload);
|
|
31500
|
+
let payloadSessionId = "";
|
|
31501
|
+
if (payloadKind === SESSION_CANCEL_KIND) {
|
|
31502
|
+
payloadSessionId = decodeSessionCancelCommand(delivery.payload).sessionId;
|
|
31503
|
+
} else if (payloadKind === SESSION_CONFIGURE_KIND) {
|
|
31504
|
+
payloadSessionId = decodeSessionConfigureCommand(delivery.payload).sessionId;
|
|
31505
|
+
} else if (payloadKind === SESSION_PERMISSION_RESPOND_KIND) {
|
|
31506
|
+
payloadSessionId = decodeSessionPermissionRespondCommand(delivery.payload).sessionId;
|
|
31507
|
+
} else if (payloadKind === SESSION_ELICITATION_RESPOND_KIND) {
|
|
31508
|
+
payloadSessionId = decodeSessionElicitationRespondCommand(delivery.payload).sessionId;
|
|
31509
|
+
} else if (payloadKind === SESSION_PROMPT_KIND) {
|
|
31510
|
+
payloadSessionId = decodeSessionPromptCommand(delivery.payload).sessionId;
|
|
31511
|
+
} else {
|
|
31512
|
+
throw new Error("Runtime delivery contains an unsupported protocol message");
|
|
31513
|
+
}
|
|
31514
|
+
if (payloadSessionId !== delivery.sessionId) {
|
|
31515
|
+
throw new Error("Runtime delivery Session identity does not match its payload");
|
|
31516
|
+
}
|
|
31517
|
+
const stored = outbox.claimRuntimeDelivery(delivery);
|
|
31518
|
+
send(
|
|
31519
|
+
socket,
|
|
31520
|
+
encodeRuntimeDeliveryReceived({
|
|
31521
|
+
deliveryId: delivery.deliveryId,
|
|
31522
|
+
kind: RUNTIME_DELIVERY_RECEIVED_KIND,
|
|
31523
|
+
sessionId: delivery.sessionId
|
|
31524
|
+
})
|
|
31525
|
+
);
|
|
31526
|
+
processRuntimeDelivery(delivery, stored.state);
|
|
31527
|
+
};
|
|
31528
|
+
const resumeRuntimeDeliveries = () => {
|
|
31529
|
+
for (const stored of outbox.runtimeDeliveries()) {
|
|
31530
|
+
const delivery = {
|
|
31531
|
+
deliveryId: stored.deliveryId,
|
|
31532
|
+
kind: RUNTIME_DELIVERY_KIND,
|
|
31533
|
+
payload: stored.payload,
|
|
31534
|
+
sessionId: stored.sessionId
|
|
31535
|
+
};
|
|
31536
|
+
if (stored.state !== "relay_committed") {
|
|
31537
|
+
send(
|
|
31538
|
+
socket,
|
|
31539
|
+
encodeRuntimeDeliveryReceived({
|
|
31540
|
+
deliveryId: stored.deliveryId,
|
|
31541
|
+
kind: RUNTIME_DELIVERY_RECEIVED_KIND,
|
|
31542
|
+
sessionId: stored.sessionId
|
|
31543
|
+
})
|
|
31544
|
+
);
|
|
31545
|
+
}
|
|
31546
|
+
processRuntimeDelivery(delivery, stored.state);
|
|
31547
|
+
}
|
|
30589
31548
|
};
|
|
30590
31549
|
const connectionLiveness = new RelayConnectionLiveness(timer, {
|
|
30591
31550
|
send: (message) => send(socket, message),
|
|
30592
|
-
unresponsive: () => {
|
|
31551
|
+
unresponsive: (heartbeatId, timeoutMilliseconds) => {
|
|
31552
|
+
diagnostics.heartbeatTimedOut(connectionEpoch, heartbeatId, timeoutMilliseconds);
|
|
31553
|
+
diagnostics.connectionClosed(
|
|
31554
|
+
connectionEpoch,
|
|
31555
|
+
4001,
|
|
31556
|
+
"Relay liveness check timed out",
|
|
31557
|
+
"heartbeat_timeout"
|
|
31558
|
+
);
|
|
30593
31559
|
closeRelaySocket(socket, 4001, "Relay liveness check timed out");
|
|
30594
31560
|
resolveConnection();
|
|
30595
31561
|
}
|
|
30596
31562
|
});
|
|
30597
|
-
const connectionRefresh = new RelayConnectionRefresh(
|
|
30598
|
-
timer,
|
|
30599
|
-
() => activeTurns.size === 0,
|
|
30600
|
-
refreshConnection
|
|
30601
|
-
);
|
|
30602
31563
|
signal.addEventListener("abort", stop, { once: true });
|
|
30603
|
-
const handleClose = () => {
|
|
31564
|
+
const handleClose = (event) => {
|
|
31565
|
+
diagnostics.connectionClosed(connectionEpoch, event.code, event.reason, "relay");
|
|
30604
31566
|
resolveConnection();
|
|
30605
31567
|
};
|
|
30606
|
-
const handleError = () => {
|
|
31568
|
+
const handleError = (event) => {
|
|
30607
31569
|
if (signal.aborted) {
|
|
30608
31570
|
resolveConnection();
|
|
30609
31571
|
return;
|
|
30610
31572
|
}
|
|
30611
|
-
|
|
31573
|
+
const cause = Reflect.get(event, "error");
|
|
31574
|
+
const failure = new Error("Relay WebSocket emitted an error", { cause: cause ?? event });
|
|
31575
|
+
diagnostics.connectionFailed(connectionEpoch, failure, "socket_error");
|
|
31576
|
+
diagnostics.connectionClosed(connectionEpoch, 1006, "WebSocket error", "socket_error");
|
|
31577
|
+
rejectConnection(failure);
|
|
30612
31578
|
};
|
|
30613
31579
|
const handleMessage = (event) => {
|
|
30614
31580
|
const encoded = event.data;
|
|
30615
31581
|
if (typeof encoded !== "string") {
|
|
30616
|
-
|
|
30617
|
-
rejectConnection();
|
|
31582
|
+
failAndClose(new Error("Relay sent a non-text message"), 4003, "Text message required", "protocol");
|
|
30618
31583
|
return;
|
|
30619
31584
|
}
|
|
30620
31585
|
try {
|
|
30621
31586
|
const kind = decodeMessageKind(encoded);
|
|
31587
|
+
if (kind === RUNTIME_DELIVERY_COMMITTED_KIND) {
|
|
31588
|
+
const committed = decodeRuntimeDeliveryCommitted(encoded);
|
|
31589
|
+
if (!outbox.markRuntimeDeliveryCommitted(committed.deliveryId, committed.sessionId)) {
|
|
31590
|
+
console.warn("Mobius daemon ignored confirmation for an unknown Runtime delivery");
|
|
31591
|
+
}
|
|
31592
|
+
return;
|
|
31593
|
+
}
|
|
31594
|
+
if (kind === RUNTIME_DELIVERY_KIND) {
|
|
31595
|
+
receiveRuntimeDelivery(decodeRuntimeDelivery(encoded));
|
|
31596
|
+
return;
|
|
31597
|
+
}
|
|
30622
31598
|
if (kind === SESSION_EVENT_ACK_KIND) {
|
|
30623
|
-
|
|
31599
|
+
publisher.acknowledge(decodeSessionEventAcknowledgement(encoded));
|
|
30624
31600
|
return;
|
|
30625
31601
|
}
|
|
30626
31602
|
if (kind === SESSION_EVENT_REJECTED_KIND) {
|
|
30627
31603
|
const rejection = decodeSessionEventRejection(encoded);
|
|
30628
|
-
if (
|
|
31604
|
+
if (publisher.quarantine(rejection)) {
|
|
30629
31605
|
audit.record({
|
|
30630
31606
|
commandId: rejection.eventId,
|
|
30631
31607
|
details: { code: rejection.code },
|
|
@@ -31004,6 +31980,7 @@ async function runRelayConnection(config2, directories, audit, sessions, install
|
|
|
31004
31980
|
applicator,
|
|
31005
31981
|
ipc,
|
|
31006
31982
|
receipt2.attemptId,
|
|
31983
|
+
timer,
|
|
31007
31984
|
turn2.signal
|
|
31008
31985
|
).catch((error61) => {
|
|
31009
31986
|
console.error("Mobius daemon Bot turn failed", error61);
|
|
@@ -31013,7 +31990,6 @@ async function runRelayConnection(config2, directories, audit, sessions, install
|
|
|
31013
31990
|
activeAttemptIds.delete(command2.sessionId);
|
|
31014
31991
|
}
|
|
31015
31992
|
turn2.finish();
|
|
31016
|
-
connectionRefresh.afterTurnFinished();
|
|
31017
31993
|
});
|
|
31018
31994
|
return;
|
|
31019
31995
|
}
|
|
@@ -31042,6 +32018,7 @@ async function runRelayConnection(config2, directories, audit, sessions, install
|
|
|
31042
32018
|
audit,
|
|
31043
32019
|
sessions,
|
|
31044
32020
|
receipt.attemptId,
|
|
32021
|
+
timer,
|
|
31045
32022
|
turn.signal
|
|
31046
32023
|
).catch((error61) => {
|
|
31047
32024
|
console.error("Mobius daemon turn failed", error61);
|
|
@@ -31051,12 +32028,19 @@ async function runRelayConnection(config2, directories, audit, sessions, install
|
|
|
31051
32028
|
activeAttemptIds.delete(command.sessionId);
|
|
31052
32029
|
}
|
|
31053
32030
|
turn.finish();
|
|
31054
|
-
connectionRefresh.afterTurnFinished();
|
|
31055
32031
|
});
|
|
31056
32032
|
} catch (error61) {
|
|
31057
32033
|
console.error("Mobius daemon rejected a message", error61);
|
|
31058
|
-
|
|
31059
|
-
|
|
32034
|
+
if (error61 instanceof RuntimeDeliveryInboxCapacityError) {
|
|
32035
|
+
failAndClose(error61, 4013, "Runtime delivery queue full", "delivery_backpressure");
|
|
32036
|
+
return;
|
|
32037
|
+
}
|
|
32038
|
+
failAndClose(
|
|
32039
|
+
new Error("Relay sent an invalid protocol message", { cause: error61 }),
|
|
32040
|
+
4008,
|
|
32041
|
+
"Invalid protocol message",
|
|
32042
|
+
"protocol"
|
|
32043
|
+
);
|
|
31060
32044
|
}
|
|
31061
32045
|
};
|
|
31062
32046
|
socket.addEventListener("close", handleClose);
|
|
@@ -31068,21 +32052,19 @@ async function runRelayConnection(config2, directories, audit, sessions, install
|
|
|
31068
32052
|
return;
|
|
31069
32053
|
}
|
|
31070
32054
|
publisher.connect(socket);
|
|
31071
|
-
|
|
31072
|
-
connectionRefresh.afterTurnFinished
|
|
31073
|
-
);
|
|
32055
|
+
resumeRuntimeDeliveries();
|
|
31074
32056
|
},
|
|
31075
32057
|
(error61) => {
|
|
31076
32058
|
if (finished) {
|
|
31077
32059
|
return;
|
|
31078
32060
|
}
|
|
31079
32061
|
console.error("Mobius daemon could not reconcile active Agent turns", error61);
|
|
31080
|
-
|
|
31081
|
-
|
|
32062
|
+
failAndClose(
|
|
32063
|
+
new Error("Active Agent turn reconciliation failed", { cause: error61 }),
|
|
31082
32064
|
ACTIVE_TURN_RECONCILIATION_CLOSE_CODE,
|
|
31083
|
-
"Active Agent turn reconciliation failed"
|
|
32065
|
+
"Active Agent turn reconciliation failed",
|
|
32066
|
+
"reconciliation"
|
|
31084
32067
|
);
|
|
31085
|
-
rejectConnection();
|
|
31086
32068
|
}
|
|
31087
32069
|
);
|
|
31088
32070
|
if (signal.aborted) {
|
|
@@ -31126,6 +32108,7 @@ async function runDaemon(config2, audit, signal, timer = systemDaemonTimer) {
|
|
|
31126
32108
|
const sessions = new AcpSessionManager(new DaemonAcpObserver(recordSessionEvent), sessionStore);
|
|
31127
32109
|
const directories = new WorkspaceDirectoryService(config2.bindingId, config2.defaultWorkspace);
|
|
31128
32110
|
const installationObserver = new DaemonInstallationObserver();
|
|
32111
|
+
const diagnostics = new RelayConnectionDiagnostics(audit, config2.channelId, config2.relayUrl);
|
|
31129
32112
|
const nativeSessions = new NativeSessionService(config2, directories, sessions, audit);
|
|
31130
32113
|
const installations = new AgentInstallationManager(
|
|
31131
32114
|
specifications,
|
|
@@ -31133,7 +32116,7 @@ async function runDaemon(config2, audit, signal, timer = systemDaemonTimer) {
|
|
|
31133
32116
|
installationObserver,
|
|
31134
32117
|
installationAudit
|
|
31135
32118
|
);
|
|
31136
|
-
let
|
|
32119
|
+
let connectionEpoch = 0;
|
|
31137
32120
|
let connectionAnnounced = false;
|
|
31138
32121
|
const announceConnection = () => {
|
|
31139
32122
|
if (connectionAnnounced) {
|
|
@@ -31149,10 +32132,11 @@ async function runDaemon(config2, audit, signal, timer = systemDaemonTimer) {
|
|
|
31149
32132
|
`
|
|
31150
32133
|
);
|
|
31151
32134
|
};
|
|
31152
|
-
const catalogPublication = publishAgentCatalogUntilAvailable(config2, signal, timer);
|
|
32135
|
+
const catalogPublication = publishAgentCatalogUntilAvailable(config2, diagnostics, signal, timer);
|
|
31153
32136
|
try {
|
|
31154
32137
|
await ipc.listen(signal);
|
|
31155
32138
|
while (!signal.aborted) {
|
|
32139
|
+
connectionEpoch += 1;
|
|
31156
32140
|
try {
|
|
31157
32141
|
await runRelayConnection(
|
|
31158
32142
|
config2,
|
|
@@ -31170,19 +32154,17 @@ async function runDaemon(config2, audit, signal, timer = systemDaemonTimer) {
|
|
|
31170
32154
|
ipc,
|
|
31171
32155
|
activeTurns,
|
|
31172
32156
|
activeAttemptIds,
|
|
32157
|
+
connectionEpoch,
|
|
32158
|
+
diagnostics,
|
|
31173
32159
|
announceConnection,
|
|
31174
32160
|
signal,
|
|
31175
32161
|
timer
|
|
31176
32162
|
);
|
|
31177
|
-
|
|
31178
|
-
} catch {
|
|
32163
|
+
} catch (error61) {
|
|
31179
32164
|
if (signal.aborted) {
|
|
31180
32165
|
return;
|
|
31181
32166
|
}
|
|
31182
|
-
|
|
31183
|
-
console.warn("Mobius daemon is waiting for the relay");
|
|
31184
|
-
retryAnnounced = true;
|
|
31185
|
-
}
|
|
32167
|
+
diagnostics.connectionFailed(connectionEpoch, error61, "connection");
|
|
31186
32168
|
}
|
|
31187
32169
|
await waitForRelayRetry(signal, timer);
|
|
31188
32170
|
}
|
|
@@ -31290,12 +32272,14 @@ process.once("SIGTERM", () => shutdown.abort());
|
|
|
31290
32272
|
var USAGE = `Mobius CLI
|
|
31291
32273
|
|
|
31292
32274
|
Usage:
|
|
32275
|
+
mobius --version
|
|
31293
32276
|
mobius pair --url <relay-url> --pairing <runtime-pairing-id> --code <pairing-code> [--workspace <directory>]
|
|
31294
32277
|
mobius start --channel <channel-id>
|
|
31295
32278
|
mobius agents [--json]
|
|
31296
32279
|
mobius bot message send --target <current|alias> --text <message>
|
|
31297
32280
|
|
|
31298
32281
|
Commands:
|
|
32282
|
+
--version Print the installed Mobius CLI version
|
|
31299
32283
|
pair Pair this machine and start its local daemon
|
|
31300
32284
|
start Start the local daemon for a saved channel
|
|
31301
32285
|
agents Show detected, missing, and integration-ready Agents
|
|
@@ -31310,6 +32294,11 @@ async function main() {
|
|
|
31310
32294
|
process.stdout.write(USAGE);
|
|
31311
32295
|
return;
|
|
31312
32296
|
}
|
|
32297
|
+
if (command === "--version" || command === "-v" || command === "version") {
|
|
32298
|
+
process.stdout.write(`${package_default.version}
|
|
32299
|
+
`);
|
|
32300
|
+
return;
|
|
32301
|
+
}
|
|
31313
32302
|
if (command === "agents") {
|
|
31314
32303
|
const arguments_ = process.argv.slice(3);
|
|
31315
32304
|
if (arguments_.length > 1 || arguments_.length === 1 && arguments_[0] !== "--json") {
|