commonswarm 0.1.45 → 0.1.46
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/cswarm.cjs +1668 -216
- package/package.json +1 -1
package/cswarm.cjs
CHANGED
|
@@ -13511,6 +13511,7 @@ __export(cli_exports, {
|
|
|
13511
13511
|
listenerFailureMessage: () => listenerFailureMessage,
|
|
13512
13512
|
listenerHostLimits: () => listenerHostLimits,
|
|
13513
13513
|
listenerPermissionMode: () => listenerPermissionMode,
|
|
13514
|
+
listenerProviderInstallEvidence: () => listenerProviderInstallEvidence,
|
|
13514
13515
|
listenerRouteConfiguration: () => listenerRouteConfiguration,
|
|
13515
13516
|
listenerStatusJson: () => listenerStatusJson,
|
|
13516
13517
|
renderListenerStatus: () => renderListenerStatus,
|
|
@@ -13521,7 +13522,7 @@ __export(cli_exports, {
|
|
|
13521
13522
|
resolveTurnBudgetOrDefer: () => resolveTurnBudgetOrDefer
|
|
13522
13523
|
});
|
|
13523
13524
|
module.exports = __toCommonJS(cli_exports);
|
|
13524
|
-
var
|
|
13525
|
+
var import_node_crypto21 = require("node:crypto");
|
|
13525
13526
|
var import_node_child_process9 = require("node:child_process");
|
|
13526
13527
|
var import_node_fs7 = require("node:fs");
|
|
13527
13528
|
var import_promises12 = require("node:fs/promises");
|
|
@@ -23848,8 +23849,7 @@ function parseStoredCurrentTarget(raw) {
|
|
|
23848
23849
|
throw new Error("stored current target is malformed");
|
|
23849
23850
|
}
|
|
23850
23851
|
const record = value;
|
|
23851
|
-
|
|
23852
|
-
if (keys.length !== 3 || keys[0] !== "anonKey" || keys[1] !== "url" || keys[2] !== "version" || record.version !== 1 || typeof record.url !== "string" || typeof record.anonKey !== "string") {
|
|
23852
|
+
if (record.version !== 1 || typeof record.url !== "string" || typeof record.anonKey !== "string") {
|
|
23853
23853
|
throw new Error("stored current target is malformed");
|
|
23854
23854
|
}
|
|
23855
23855
|
try {
|
|
@@ -28750,10 +28750,19 @@ var SIGNAL_BODY_DISPLAY_MAX = 8e3;
|
|
|
28750
28750
|
var SIGNAL_ABOUT_DISPLAY_MAX = 500;
|
|
28751
28751
|
var SIGNAL_READ_TIMEOUT_MS = 3e4;
|
|
28752
28752
|
var SignalReadTimeoutError = class extends Error {
|
|
28753
|
-
constructor(message = "signal read timed out") {
|
|
28753
|
+
constructor(message = "signal read timed out", phase = "response") {
|
|
28754
28754
|
super(message);
|
|
28755
|
+
this.phase = phase;
|
|
28755
28756
|
this.name = "SignalReadTimeoutError";
|
|
28756
28757
|
}
|
|
28758
|
+
phase;
|
|
28759
|
+
};
|
|
28760
|
+
var SignalHostPortsExhaustedError = class extends Error {
|
|
28761
|
+
code = "EADDRNOTAVAIL";
|
|
28762
|
+
constructor() {
|
|
28763
|
+
super("the host could not allocate an outbound source port");
|
|
28764
|
+
this.name = "SignalHostPortsExhaustedError";
|
|
28765
|
+
}
|
|
28757
28766
|
};
|
|
28758
28767
|
var SIGNAL_WAIT_MIN_SECONDS = 1;
|
|
28759
28768
|
var SIGNAL_WAIT_MAX_SECONDS = 300;
|
|
@@ -28792,9 +28801,17 @@ var plainHttpRetryAfterMs = /* @__PURE__ */ new WeakMap();
|
|
|
28792
28801
|
var plainHttpStatus = /* @__PURE__ */ new WeakMap();
|
|
28793
28802
|
var plainHttpEnvelope = /* @__PURE__ */ new WeakMap();
|
|
28794
28803
|
var plainTransportErrors = /* @__PURE__ */ new WeakSet();
|
|
28795
|
-
|
|
28804
|
+
var plainTransportFailureCodes = /* @__PURE__ */ new WeakMap();
|
|
28805
|
+
var plainMalformedErrors = /* @__PURE__ */ new WeakSet();
|
|
28806
|
+
function plainTransportError(failureCode2 = "no_response") {
|
|
28796
28807
|
const error = new Error("signal read could not reach the cloud service");
|
|
28797
28808
|
plainTransportErrors.add(error);
|
|
28809
|
+
plainTransportFailureCodes.set(error, failureCode2);
|
|
28810
|
+
return error;
|
|
28811
|
+
}
|
|
28812
|
+
function plainMalformedError(message) {
|
|
28813
|
+
const error = new Error(message);
|
|
28814
|
+
plainMalformedErrors.add(error);
|
|
28798
28815
|
return error;
|
|
28799
28816
|
}
|
|
28800
28817
|
function checkedUuid2(value, field) {
|
|
@@ -28999,7 +29016,62 @@ function followErrorEnvelope(error) {
|
|
|
28999
29016
|
return EMPTY_SERVER_ERROR_ENVELOPE;
|
|
29000
29017
|
}
|
|
29001
29018
|
function isTransportFollowMessage(error) {
|
|
29002
|
-
return error instanceof SignalTransportError || error instanceof Error && plainTransportErrors.has(error);
|
|
29019
|
+
return error instanceof SignalTransportError || error instanceof SignalHostPortsExhaustedError || error instanceof Error && plainTransportErrors.has(error);
|
|
29020
|
+
}
|
|
29021
|
+
function safeConstructorName(error) {
|
|
29022
|
+
if (error === null || typeof error !== "object") return typeof error;
|
|
29023
|
+
const name = error.constructor?.name;
|
|
29024
|
+
if (typeof name !== "string" || name.length === 0) return "Unknown";
|
|
29025
|
+
return name.replace(/[^A-Za-z0-9_$-]+/g, "_").slice(0, 96) || "Unknown";
|
|
29026
|
+
}
|
|
29027
|
+
function classifySignalReadFailure(error) {
|
|
29028
|
+
if (error instanceof SignalHostPortsExhaustedError || error !== null && typeof error === "object" && error.code === "EADDRNOTAVAIL") {
|
|
29029
|
+
return {
|
|
29030
|
+
code: "host_ports_exhausted",
|
|
29031
|
+
httpStatus: null,
|
|
29032
|
+
errorConstructor: null
|
|
29033
|
+
};
|
|
29034
|
+
}
|
|
29035
|
+
const http = followHttpDetails(error);
|
|
29036
|
+
if (http !== null) {
|
|
29037
|
+
return {
|
|
29038
|
+
code: "http_status",
|
|
29039
|
+
httpStatus: http.status,
|
|
29040
|
+
errorConstructor: null
|
|
29041
|
+
};
|
|
29042
|
+
}
|
|
29043
|
+
if (error instanceof SignalReadTimeoutError) {
|
|
29044
|
+
return {
|
|
29045
|
+
code: error.phase === "body" ? "body_timeout" : "no_response",
|
|
29046
|
+
httpStatus: null,
|
|
29047
|
+
errorConstructor: null
|
|
29048
|
+
};
|
|
29049
|
+
}
|
|
29050
|
+
if (error instanceof Error && plainTransportErrors.has(error)) {
|
|
29051
|
+
return {
|
|
29052
|
+
code: plainTransportFailureCodes.get(error) ?? "no_response",
|
|
29053
|
+
httpStatus: null,
|
|
29054
|
+
errorConstructor: null
|
|
29055
|
+
};
|
|
29056
|
+
}
|
|
29057
|
+
if (error instanceof SignalTransportError) {
|
|
29058
|
+
return { code: "no_response", httpStatus: null, errorConstructor: null };
|
|
29059
|
+
}
|
|
29060
|
+
if (error instanceof SignalMalformedError || error instanceof Error && plainMalformedErrors.has(error)) {
|
|
29061
|
+
return {
|
|
29062
|
+
code: "malformed_response",
|
|
29063
|
+
httpStatus: null,
|
|
29064
|
+
errorConstructor: null
|
|
29065
|
+
};
|
|
29066
|
+
}
|
|
29067
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
29068
|
+
return { code: "aborted", httpStatus: null, errorConstructor: null };
|
|
29069
|
+
}
|
|
29070
|
+
return {
|
|
29071
|
+
code: "unclassified",
|
|
29072
|
+
httpStatus: null,
|
|
29073
|
+
errorConstructor: safeConstructorName(error)
|
|
29074
|
+
};
|
|
29003
29075
|
}
|
|
29004
29076
|
function isRestartableReadError(error) {
|
|
29005
29077
|
if (error instanceof SignalReadTimeoutError) return true;
|
|
@@ -29051,6 +29123,7 @@ async function fetchSignalRead(fetcher, input, init, timeoutMs = SIGNAL_READ_TIM
|
|
|
29051
29123
|
}
|
|
29052
29124
|
const deadlineController = new AbortController();
|
|
29053
29125
|
let timedOut = false;
|
|
29126
|
+
let responseReceived = false;
|
|
29054
29127
|
const signal = init.signal ? AbortSignal.any([init.signal, deadlineController.signal]) : deadlineController.signal;
|
|
29055
29128
|
let onAbort = () => {
|
|
29056
29129
|
};
|
|
@@ -29078,22 +29151,31 @@ async function fetchSignalRead(fetcher, input, init, timeoutMs = SIGNAL_READ_TIM
|
|
|
29078
29151
|
signal
|
|
29079
29152
|
});
|
|
29080
29153
|
} catch (error) {
|
|
29081
|
-
if (signal.aborted || timedOut
|
|
29154
|
+
if (signal.aborted || timedOut) {
|
|
29082
29155
|
return "timeout";
|
|
29083
29156
|
}
|
|
29157
|
+
if (error instanceof Error && error.name === "AbortError") throw error;
|
|
29158
|
+
if (error !== null && typeof error === "object" && error.code === "EADDRNOTAVAIL") {
|
|
29159
|
+
throw new SignalHostPortsExhaustedError();
|
|
29160
|
+
}
|
|
29084
29161
|
return null;
|
|
29085
29162
|
}
|
|
29163
|
+
responseReceived = true;
|
|
29086
29164
|
if (signal.aborted || timedOut) return "timeout";
|
|
29087
29165
|
try {
|
|
29088
29166
|
return { response, body: await response.json() };
|
|
29089
|
-
} catch {
|
|
29167
|
+
} catch (error) {
|
|
29090
29168
|
if (signal.aborted || timedOut) return "timeout";
|
|
29169
|
+
if (error instanceof Error && error.name === "AbortError") throw error;
|
|
29091
29170
|
return { response, body: null };
|
|
29092
29171
|
}
|
|
29093
29172
|
})();
|
|
29094
29173
|
const raced = await Promise.race([read, aborted]);
|
|
29095
29174
|
if (raced === "timeout") {
|
|
29096
|
-
throw new SignalReadTimeoutError(
|
|
29175
|
+
throw new SignalReadTimeoutError(
|
|
29176
|
+
"signal read timed out",
|
|
29177
|
+
responseReceived ? "body" : "response"
|
|
29178
|
+
);
|
|
29097
29179
|
}
|
|
29098
29180
|
return raced;
|
|
29099
29181
|
} finally {
|
|
@@ -29122,7 +29204,9 @@ async function fetchSignalReadRetrying(fetcher, input, init, timeoutMs = SIGNAL_
|
|
|
29122
29204
|
function mapReadFailure(error, waitBound) {
|
|
29123
29205
|
if (error instanceof SignalReadTimeoutError) {
|
|
29124
29206
|
if (waitBound) throw error;
|
|
29125
|
-
throw plainTransportError(
|
|
29207
|
+
throw plainTransportError(
|
|
29208
|
+
error.phase === "body" ? "body_timeout" : "no_response"
|
|
29209
|
+
);
|
|
29126
29210
|
}
|
|
29127
29211
|
throw error;
|
|
29128
29212
|
}
|
|
@@ -29178,7 +29262,7 @@ async function humanSignals(target2, credential, query, options) {
|
|
|
29178
29262
|
throwSignalHttp(response, body);
|
|
29179
29263
|
}
|
|
29180
29264
|
if (!Array.isArray(body)) {
|
|
29181
|
-
throw
|
|
29265
|
+
throw plainMalformedError("signal read returned malformed JSON");
|
|
29182
29266
|
}
|
|
29183
29267
|
const parsed = body.map((value) => parseSignalRecord(value));
|
|
29184
29268
|
return sortSignals(rowsAfterCursor(parsed, query.after), ascending);
|
|
@@ -29246,7 +29330,7 @@ async function agentSignalPage(target2, credential, query, options, allowLegacyC
|
|
|
29246
29330
|
const { response, body } = result;
|
|
29247
29331
|
if (!response.ok) throwSignalHttp(response, body);
|
|
29248
29332
|
if (!body || typeof body !== "object" || Array.isArray(body) || !Array.isArray(body.signals)) {
|
|
29249
|
-
throw
|
|
29333
|
+
throw plainMalformedError("signal read returned malformed JSON");
|
|
29250
29334
|
}
|
|
29251
29335
|
const capabilities = signalReadCapabilities(
|
|
29252
29336
|
body.capabilities
|
|
@@ -29736,6 +29820,7 @@ function resolveRefusalToleranceMs(raw, warn = () => {
|
|
|
29736
29820
|
}
|
|
29737
29821
|
function isRetryableFollowError(error) {
|
|
29738
29822
|
if (serverRefusedRetry(followErrorEnvelope(error))) return false;
|
|
29823
|
+
if (error instanceof SignalHostPortsExhaustedError) return true;
|
|
29739
29824
|
if (error instanceof SignalReadTimeoutError) return true;
|
|
29740
29825
|
if (isTransportFollowMessage(error)) return true;
|
|
29741
29826
|
const http = followHttpDetails(error);
|
|
@@ -30026,9 +30111,8 @@ function parseCursor(raw, workspaceId2, principalId) {
|
|
|
30026
30111
|
throw new Error("stored arrival cursor is malformed");
|
|
30027
30112
|
}
|
|
30028
30113
|
const row = value;
|
|
30029
|
-
const keys = Object.keys(row).sort();
|
|
30030
30114
|
const cursor = row.cursor;
|
|
30031
|
-
if (
|
|
30115
|
+
if (row.version !== 1 || row.workspace_id !== workspaceId2.toLowerCase() || row.principal_id !== principalId.toLowerCase() || !(cursor === null || typeof cursor === "object" && !Array.isArray(cursor) && typeof cursor.created_at === "string" && Number.isFinite(Date.parse(cursor.created_at)) && typeof cursor.id === "string" && UUID_RE10.test(cursor.id))) {
|
|
30032
30116
|
throw new Error("stored arrival cursor is malformed");
|
|
30033
30117
|
}
|
|
30034
30118
|
if (cursor === null) return null;
|
|
@@ -30755,11 +30839,7 @@ function signalReceiptJsonPayload(report, nowMs = Date.now()) {
|
|
|
30755
30839
|
var import_node_child_process3 = require("node:child_process");
|
|
30756
30840
|
var import_node_crypto12 = require("node:crypto");
|
|
30757
30841
|
|
|
30758
|
-
// src/host/
|
|
30759
|
-
var RING_CAPACITY_BYTES = 4096;
|
|
30760
|
-
var TAIL_MAX_CHARS = 2048;
|
|
30761
|
-
var STDERR_EXIT_GRACE_MS = 100;
|
|
30762
|
-
var STDERR_READABLE_END_GRACE_MS = STDERR_EXIT_GRACE_MS + 50;
|
|
30842
|
+
// src/host/credential-redaction.ts
|
|
30763
30843
|
var EXOTIC_SEPARATORS = "\\u00a0\\u1680\\u2000-\\u200d\\u2028\\u2029\\u202a-\\u202e\\u2060\\u2066-\\u2069\\u202f\\u205f\\u3000\\ufeff";
|
|
30764
30844
|
var SEPARATOR_CLASS_SOURCE = "\\t\\n\\x0b\\f\\r " + EXOTIC_SEPARATORS;
|
|
30765
30845
|
var ANSI_ESCAPE_GLOBAL_RE2 = new RegExp("\\u001b\\[[0-?]*[ -\\/]*[@-~]", "g");
|
|
@@ -30771,8 +30851,17 @@ var CREDENTIAL_PREFIX_RE = new RegExp(
|
|
|
30771
30851
|
`swm_(?:agt|inv|cap)_[^${SEPARATOR_CLASS_SOURCE}]*`,
|
|
30772
30852
|
"gi"
|
|
30773
30853
|
);
|
|
30854
|
+
function redactCredentialText(value) {
|
|
30855
|
+
return value.replace(ANSI_ESCAPE_GLOBAL_RE2, "").replace(CONTROL_AND_SEPARATOR_STRIP_RE, "").replace(CREDENTIAL_PREFIX_RE, "[redacted-credential]");
|
|
30856
|
+
}
|
|
30857
|
+
|
|
30858
|
+
// src/host/stderr-tail.ts
|
|
30859
|
+
var RING_CAPACITY_BYTES = 4096;
|
|
30860
|
+
var TAIL_MAX_CHARS = 2048;
|
|
30861
|
+
var STDERR_EXIT_GRACE_MS = 100;
|
|
30862
|
+
var STDERR_READABLE_END_GRACE_MS = STDERR_EXIT_GRACE_MS + 50;
|
|
30774
30863
|
function sanitizeStderrTail(raw) {
|
|
30775
|
-
return raw
|
|
30864
|
+
return redactCredentialText(raw).slice(-TAIL_MAX_CHARS).trim();
|
|
30776
30865
|
}
|
|
30777
30866
|
function attachStderrTailRing(stderr) {
|
|
30778
30867
|
const chunks = [];
|
|
@@ -30984,7 +31073,7 @@ function permissionDecisionToResult(decision) {
|
|
|
30984
31073
|
var SECRET_VALUE_RE = /(?:(?:api[_-]?key|token|secret|password|authorization|bearer)\s*[:=]\s*)(["']?)([^\s"'\\]{8,})\1/gi;
|
|
30985
31074
|
var JWT_RE = /\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b/g;
|
|
30986
31075
|
function redactString(value) {
|
|
30987
|
-
return value.replace(SECRET_VALUE_RE, (_m, q) => `redacted=${q}***${q}`).replace(JWT_RE, "[redacted-jwt]");
|
|
31076
|
+
return redactCredentialText(value).replace(SECRET_VALUE_RE, (_m, q) => `redacted=${q}***${q}`).replace(JWT_RE, "[redacted-jwt]");
|
|
30988
31077
|
}
|
|
30989
31078
|
function redactUnknown(value, depth = 0) {
|
|
30990
31079
|
if (depth > 6) return "[truncated]";
|
|
@@ -31100,12 +31189,14 @@ var AcpVersionBelowFloorError = class extends AcpVersionError {
|
|
|
31100
31189
|
actual;
|
|
31101
31190
|
};
|
|
31102
31191
|
var AcpPermissionCanaryError = class extends AcpHostError {
|
|
31103
|
-
constructor(message, reasonCode = null) {
|
|
31192
|
+
constructor(message, reasonCode = null, minimumRequiredVersion = null) {
|
|
31104
31193
|
super("permission_canary_failed", message);
|
|
31105
31194
|
this.reasonCode = reasonCode;
|
|
31195
|
+
this.minimumRequiredVersion = minimumRequiredVersion;
|
|
31106
31196
|
this.name = "AcpPermissionCanaryError";
|
|
31107
31197
|
}
|
|
31108
31198
|
reasonCode;
|
|
31199
|
+
minimumRequiredVersion;
|
|
31109
31200
|
};
|
|
31110
31201
|
var AcpPromptsBlockedError = class extends AcpHostError {
|
|
31111
31202
|
constructor() {
|
|
@@ -32698,6 +32789,7 @@ async function openOpenCodeAcpSession(options) {
|
|
|
32698
32789
|
|
|
32699
32790
|
// src/host/claude.ts
|
|
32700
32791
|
var import_node_child_process4 = require("node:child_process");
|
|
32792
|
+
var import_node_module = require("node:module");
|
|
32701
32793
|
var import_node_fs4 = require("node:fs");
|
|
32702
32794
|
var import_node_path7 = require("node:path");
|
|
32703
32795
|
var CHILD_EXIT_WAIT_MS2 = 3e3;
|
|
@@ -32808,6 +32900,51 @@ function parseClaudeVersionOutput(stdout) {
|
|
|
32808
32900
|
function parseClaudeCodeVersionOutput(stdout) {
|
|
32809
32901
|
return parseProviderVersionOutput(stdout, /\bClaude Code\b/i, false);
|
|
32810
32902
|
}
|
|
32903
|
+
function semanticVersion(value) {
|
|
32904
|
+
if (typeof value !== "string") return null;
|
|
32905
|
+
return parseProviderVersionOutput(`${value}
|
|
32906
|
+
`, /\bnever-a-product-name\b/i);
|
|
32907
|
+
}
|
|
32908
|
+
function readPackageAtOrAbove(entrypoint, expectedName) {
|
|
32909
|
+
let directory = (0, import_node_path7.dirname)(entrypoint);
|
|
32910
|
+
for (let depth = 0; depth < 5; depth += 1) {
|
|
32911
|
+
const path = (0, import_node_path7.join)(directory, "package.json");
|
|
32912
|
+
try {
|
|
32913
|
+
const row = JSON.parse((0, import_node_fs4.readFileSync)(path, "utf8"));
|
|
32914
|
+
if (row && typeof row === "object" && !Array.isArray(row) && row.name === expectedName) {
|
|
32915
|
+
return { path, row };
|
|
32916
|
+
}
|
|
32917
|
+
} catch {
|
|
32918
|
+
}
|
|
32919
|
+
const parent = (0, import_node_path7.dirname)(directory);
|
|
32920
|
+
if (parent === directory) break;
|
|
32921
|
+
directory = parent;
|
|
32922
|
+
}
|
|
32923
|
+
return null;
|
|
32924
|
+
}
|
|
32925
|
+
function measureClaudeBundleVersions(executable) {
|
|
32926
|
+
const adapter = readPackageAtOrAbove(
|
|
32927
|
+
executable,
|
|
32928
|
+
"@agentclientprotocol/claude-agent-acp"
|
|
32929
|
+
);
|
|
32930
|
+
if (!adapter) return { agentSdkVersion: null, claudeCodeVersion: null };
|
|
32931
|
+
try {
|
|
32932
|
+
const sdkEntrypoint = (0, import_node_module.createRequire)(adapter.path).resolve(
|
|
32933
|
+
"@anthropic-ai/claude-agent-sdk"
|
|
32934
|
+
);
|
|
32935
|
+
const sdk = readPackageAtOrAbove(
|
|
32936
|
+
sdkEntrypoint,
|
|
32937
|
+
"@anthropic-ai/claude-agent-sdk"
|
|
32938
|
+
);
|
|
32939
|
+
if (!sdk) return { agentSdkVersion: null, claudeCodeVersion: null };
|
|
32940
|
+
return {
|
|
32941
|
+
agentSdkVersion: semanticVersion(sdk.row.version),
|
|
32942
|
+
claudeCodeVersion: semanticVersion(sdk.row.claudeCodeVersion)
|
|
32943
|
+
};
|
|
32944
|
+
} catch {
|
|
32945
|
+
return { agentSdkVersion: null, claudeCodeVersion: null };
|
|
32946
|
+
}
|
|
32947
|
+
}
|
|
32811
32948
|
async function readClaudeVersionOutput(executable, options) {
|
|
32812
32949
|
const timeoutMs = options?.timeoutMs ?? ACP_VERSION_CHECK_TIMEOUT_MS;
|
|
32813
32950
|
const env = options?.env ?? sanitizeChildEnv(process.env);
|
|
@@ -32831,24 +32968,21 @@ async function readClaudeVersionOutput(executable, options) {
|
|
|
32831
32968
|
);
|
|
32832
32969
|
});
|
|
32833
32970
|
}
|
|
32834
|
-
async function
|
|
32835
|
-
const
|
|
32836
|
-
|
|
32837
|
-
|
|
32838
|
-
|
|
32839
|
-
|
|
32840
|
-
|
|
32841
|
-
|
|
32842
|
-
|
|
32843
|
-
|
|
32844
|
-
|
|
32845
|
-
|
|
32846
|
-
|
|
32847
|
-
|
|
32848
|
-
|
|
32849
|
-
...options?.onNewerVersion ? { onNewerVersion: options.onNewerVersion } : {}
|
|
32850
|
-
});
|
|
32851
|
-
return version3;
|
|
32971
|
+
async function inspectClaudeBridgeExecutable(executable = "claude-agent-acp", options) {
|
|
32972
|
+
const resolved = resolveClaudeExecutable(
|
|
32973
|
+
executable,
|
|
32974
|
+
options?.pathEnv,
|
|
32975
|
+
options?.platform
|
|
32976
|
+
);
|
|
32977
|
+
const output = await readClaudeVersionOutput(resolved, options);
|
|
32978
|
+
const bundle = measureClaudeBundleVersions(resolved);
|
|
32979
|
+
return {
|
|
32980
|
+
executable: resolved,
|
|
32981
|
+
providerVersion: parseClaudeVersionOutput(output),
|
|
32982
|
+
lastMeasuredVersion: CLAUDE_ACP_LAST_MEASURED_VERSION,
|
|
32983
|
+
bundledAgentSdkVersion: bundle.agentSdkVersion,
|
|
32984
|
+
bundledClaudeCodeVersion: bundle.claudeCodeVersion
|
|
32985
|
+
};
|
|
32852
32986
|
}
|
|
32853
32987
|
function buildClaudeAcpArgs() {
|
|
32854
32988
|
return [];
|
|
@@ -32913,29 +33047,57 @@ async function openClaudeAcpSession(options) {
|
|
|
32913
33047
|
let executable;
|
|
32914
33048
|
let env = baseEnv;
|
|
32915
33049
|
let claudeCodeExecutable;
|
|
33050
|
+
let providerVersion;
|
|
33051
|
+
let bundleVersions = {
|
|
33052
|
+
agentSdkVersion: null,
|
|
33053
|
+
claudeCodeVersion: null
|
|
33054
|
+
};
|
|
33055
|
+
const reportRuntime = (resolved, version3) => {
|
|
33056
|
+
bundleVersions = measureClaudeBundleVersions(resolved);
|
|
33057
|
+
options.onRuntimeNotice?.({
|
|
33058
|
+
executable: resolved,
|
|
33059
|
+
providerVersion: version3,
|
|
33060
|
+
lastMeasuredVersion: CLAUDE_ACP_LAST_MEASURED_VERSION,
|
|
33061
|
+
bundledAgentSdkVersion: bundleVersions.agentSdkVersion,
|
|
33062
|
+
bundledClaudeCodeVersion: bundleVersions.claudeCodeVersion
|
|
33063
|
+
});
|
|
33064
|
+
};
|
|
33065
|
+
const admitBridgeVersion = (resolved, version3) => {
|
|
33066
|
+
providerVersion = version3;
|
|
33067
|
+
reportRuntime(resolved, version3);
|
|
33068
|
+
assertProviderVersionFloor({
|
|
33069
|
+
provider: "claude-agent-acp",
|
|
33070
|
+
version: version3,
|
|
33071
|
+
minimumVersion: CLAUDE_ACP_MIN_VERSION,
|
|
33072
|
+
lastMeasuredVersion: CLAUDE_ACP_LAST_MEASURED_VERSION,
|
|
33073
|
+
...options.onVersionNotice ? { onNewerVersion: options.onVersionNotice } : {}
|
|
33074
|
+
});
|
|
33075
|
+
};
|
|
32916
33076
|
if (requestedExecutable) {
|
|
32917
33077
|
const output = await readClaudeVersionOutput(requestedExecutable, {
|
|
32918
33078
|
env: baseEnv
|
|
32919
33079
|
});
|
|
32920
33080
|
const bridgeVersion = parseClaudeVersionOutput(output);
|
|
32921
33081
|
if (bridgeVersion) {
|
|
32922
|
-
assertProviderVersionFloor({
|
|
32923
|
-
provider: "claude-agent-acp",
|
|
32924
|
-
version: bridgeVersion,
|
|
32925
|
-
minimumVersion: CLAUDE_ACP_MIN_VERSION,
|
|
32926
|
-
lastMeasuredVersion: CLAUDE_ACP_LAST_MEASURED_VERSION,
|
|
32927
|
-
...options.onVersionNotice ? { onNewerVersion: options.onVersionNotice } : {}
|
|
32928
|
-
});
|
|
32929
33082
|
executable = requestedExecutable;
|
|
33083
|
+
admitBridgeVersion(executable, bridgeVersion);
|
|
32930
33084
|
} else if (parseClaudeCodeVersionOutput(output)) {
|
|
32931
33085
|
claudeCodeExecutable = requestedExecutable;
|
|
32932
33086
|
executable = resolvePackagedClaudeBridge(resolvedPathEnv);
|
|
32933
33087
|
env = buildClaudeChildEnv(parentEnv, claudeCodeExecutable);
|
|
32934
|
-
await
|
|
32935
|
-
env
|
|
32936
|
-
...options.onVersionNotice ? { onNewerVersion: options.onVersionNotice } : {}
|
|
33088
|
+
const bridgeOutput = await readClaudeVersionOutput(executable, {
|
|
33089
|
+
env
|
|
32937
33090
|
});
|
|
33091
|
+
const packagedVersion = parseClaudeVersionOutput(bridgeOutput);
|
|
33092
|
+
if (!packagedVersion) {
|
|
33093
|
+
reportRuntime(executable, null);
|
|
33094
|
+
throw new AcpVersionParseError(
|
|
33095
|
+
`could not parse claude-agent-acp version from: ${bridgeOutput.trim().slice(0, 200)}`
|
|
33096
|
+
);
|
|
33097
|
+
}
|
|
33098
|
+
admitBridgeVersion(executable, packagedVersion);
|
|
32938
33099
|
} else {
|
|
33100
|
+
reportRuntime(requestedExecutable, null);
|
|
32939
33101
|
throw new AcpVersionError(
|
|
32940
33102
|
`could not identify Claude executable from: ${output.trim().slice(0, 200)}`
|
|
32941
33103
|
);
|
|
@@ -32946,10 +33108,19 @@ async function openClaudeAcpSession(options) {
|
|
|
32946
33108
|
resolvedPathEnv
|
|
32947
33109
|
);
|
|
32948
33110
|
if (!options.skipVersionCheck) {
|
|
32949
|
-
await
|
|
32950
|
-
env: baseEnv
|
|
32951
|
-
...options.onVersionNotice ? { onNewerVersion: options.onVersionNotice } : {}
|
|
33111
|
+
const output = await readClaudeVersionOutput(executable, {
|
|
33112
|
+
env: baseEnv
|
|
32952
33113
|
});
|
|
33114
|
+
const version3 = parseClaudeVersionOutput(output);
|
|
33115
|
+
if (!version3) {
|
|
33116
|
+
reportRuntime(executable, null);
|
|
33117
|
+
throw new AcpVersionParseError(
|
|
33118
|
+
`could not parse claude-agent-acp version from: ${output.trim().slice(0, 200)}`
|
|
33119
|
+
);
|
|
33120
|
+
}
|
|
33121
|
+
admitBridgeVersion(executable, version3);
|
|
33122
|
+
} else {
|
|
33123
|
+
reportRuntime(executable, null);
|
|
32953
33124
|
}
|
|
32954
33125
|
}
|
|
32955
33126
|
if (options.signal?.aborted) {
|
|
@@ -33041,7 +33212,17 @@ async function openClaudeAcpSession(options) {
|
|
|
33041
33212
|
})();
|
|
33042
33213
|
return closePromise;
|
|
33043
33214
|
};
|
|
33044
|
-
return {
|
|
33215
|
+
return {
|
|
33216
|
+
session,
|
|
33217
|
+
child,
|
|
33218
|
+
executable,
|
|
33219
|
+
args,
|
|
33220
|
+
env,
|
|
33221
|
+
...providerVersion ? { providerVersion } : {},
|
|
33222
|
+
...bundleVersions.agentSdkVersion ? { bundledAgentSdkVersion: bundleVersions.agentSdkVersion } : {},
|
|
33223
|
+
...bundleVersions.claudeCodeVersion ? { bundledClaudeCodeVersion: bundleVersions.claudeCodeVersion } : {},
|
|
33224
|
+
close
|
|
33225
|
+
};
|
|
33045
33226
|
} catch (error) {
|
|
33046
33227
|
removeAbortListener();
|
|
33047
33228
|
transport.close();
|
|
@@ -33893,6 +34074,18 @@ var V1_EFFECT_KEYS = /* @__PURE__ */ new Set([
|
|
|
33893
34074
|
"updatedAt"
|
|
33894
34075
|
]);
|
|
33895
34076
|
var V2_EFFECT_KEYS = /* @__PURE__ */ new Set([...V1_EFFECT_KEYS, "signalKind"]);
|
|
34077
|
+
var EFFECT_SENSITIVE_KEYS = /* @__PURE__ */ new Set([
|
|
34078
|
+
"lease_id",
|
|
34079
|
+
"leaseId",
|
|
34080
|
+
"listenerBearer",
|
|
34081
|
+
"bearer",
|
|
34082
|
+
"token",
|
|
34083
|
+
"prompt",
|
|
34084
|
+
"ackCommandId",
|
|
34085
|
+
"ack_command_id",
|
|
34086
|
+
"claimCommandId",
|
|
34087
|
+
"claim_command_id"
|
|
34088
|
+
]);
|
|
33896
34089
|
function defaultListenerStateDirectory() {
|
|
33897
34090
|
return process.env.XDG_STATE_HOME ? (0, import_node_path9.join)(process.env.XDG_STATE_HOME, "cswarm", "listeners") : (0, import_node_path9.join)((0, import_node_os6.homedir)(), ".cswarm", "listeners");
|
|
33898
34091
|
}
|
|
@@ -33911,9 +34104,9 @@ function integer(value) {
|
|
|
33911
34104
|
function nullableString2(value, max) {
|
|
33912
34105
|
return value === null || typeof value === "string" && value.length <= max;
|
|
33913
34106
|
}
|
|
33914
|
-
function
|
|
34107
|
+
function rejectSensitiveKeys(row) {
|
|
33915
34108
|
for (const key2 of Object.keys(row)) {
|
|
33916
|
-
if (
|
|
34109
|
+
if (EFFECT_SENSITIVE_KEYS.has(key2)) {
|
|
33917
34110
|
throw new Error("stored listener effect is malformed");
|
|
33918
34111
|
}
|
|
33919
34112
|
}
|
|
@@ -33929,17 +34122,16 @@ function parseListenerEffectRecord(raw, expectedId) {
|
|
|
33929
34122
|
throw new Error("stored listener effect is malformed");
|
|
33930
34123
|
}
|
|
33931
34124
|
const row = value;
|
|
34125
|
+
rejectSensitiveKeys(row);
|
|
33932
34126
|
if (typeof row.version !== "number" || row.version !== 1 && row.version !== 2 || typeof row.signalId !== "string" || row.signalId.toLowerCase() !== expectedId || !UUID_RE13.test(row.signalId)) {
|
|
33933
34127
|
throw new Error("stored listener effect is malformed");
|
|
33934
34128
|
}
|
|
33935
34129
|
if (row.version === 1) {
|
|
33936
|
-
rejectUnknownKeys(row, V1_EFFECT_KEYS);
|
|
33937
34130
|
if ("signalKind" in row || row.state === "observed" || row.state === "routed_main") {
|
|
33938
34131
|
throw new Error("stored listener effect is malformed");
|
|
33939
34132
|
}
|
|
33940
34133
|
return upcastV1Ask(row);
|
|
33941
34134
|
}
|
|
33942
|
-
rejectUnknownKeys(row, V2_EFFECT_KEYS);
|
|
33943
34135
|
return parseV2Record(row);
|
|
33944
34136
|
}
|
|
33945
34137
|
function upcastV1Ask(row) {
|
|
@@ -34392,6 +34584,7 @@ var GrokListenerModel = class {
|
|
|
34392
34584
|
...this.options.effort ? { effort: this.options.effort } : {},
|
|
34393
34585
|
...this.options.env ? { env: this.options.env } : {},
|
|
34394
34586
|
...this.options.onVersionNotice ? { onVersionNotice: this.options.onVersionNotice } : {},
|
|
34587
|
+
...this.options.events ? { events: this.options.events } : {},
|
|
34395
34588
|
...this.options.onWorkerStderrTail ? { onStderrTail: this.options.onWorkerStderrTail } : {},
|
|
34396
34589
|
clientName: "cswarm-listener"
|
|
34397
34590
|
});
|
|
@@ -34832,6 +35025,7 @@ var OpenCodeListenerModel = class {
|
|
|
34832
35025
|
...this.options.model ? { model: this.options.model } : {},
|
|
34833
35026
|
...this.options.env ? { env: this.options.env } : {},
|
|
34834
35027
|
...this.options.onVersionNotice ? { onVersionNotice: this.options.onVersionNotice } : {},
|
|
35028
|
+
...this.options.events ? { events: this.options.events } : {},
|
|
34835
35029
|
...this.options.allowMissingAuth === true ? { allowMissingAuth: true } : {},
|
|
34836
35030
|
...this.options.onWorkerStderrTail ? { onStderrTail: this.options.onWorkerStderrTail } : {},
|
|
34837
35031
|
clientName: "cswarm-listener"
|
|
@@ -34935,6 +35129,35 @@ var import_node_crypto16 = require("node:crypto");
|
|
|
34935
35129
|
var import_promises7 = require("node:fs/promises");
|
|
34936
35130
|
var import_node_os8 = require("node:os");
|
|
34937
35131
|
var import_node_path13 = require("node:path");
|
|
35132
|
+
var CLAUDE_CODE_VERSION_REQUIRED_RE = /\bClaude Code (\d+\.\d+\.\d+) does not support this model; version (\d+\.\d+\.\d+) or newer is required\b/;
|
|
35133
|
+
var CLAUDE_AUTH_FAILURE_RE = /\b(?:authentication failed|authentication required|not authenticated|OAuth (?:sign-in|login|token)|keychain\/OAuth|please (?:log|sign) in)\b/i;
|
|
35134
|
+
var CLAUDE_CANARY_TIMEOUT_RE = /^ACP request timed out: session\/prompt(?: \(failed \d+ attempts\))?$/;
|
|
35135
|
+
function classifyClaudeCanaryFailure(detail, typedReasonCode) {
|
|
35136
|
+
const recorded = detail?.trim() ?? "";
|
|
35137
|
+
const demanded = CLAUDE_CODE_VERSION_REQUIRED_RE.exec(recorded);
|
|
35138
|
+
if (demanded?.[2]) {
|
|
35139
|
+
return {
|
|
35140
|
+
code: "claude_bridge_version_required",
|
|
35141
|
+
minimumRequiredVersion: demanded[2]
|
|
35142
|
+
};
|
|
35143
|
+
}
|
|
35144
|
+
if (typedReasonCode === "claude_canary_timeout" || typedReasonCode === "timeout" || (typedReasonCode === null || typedReasonCode === void 0) && CLAUDE_CANARY_TIMEOUT_RE.test(recorded)) {
|
|
35145
|
+
return { code: "claude_canary_timeout", minimumRequiredVersion: null };
|
|
35146
|
+
}
|
|
35147
|
+
if (typedReasonCode === "claude_canary_auth_failed") {
|
|
35148
|
+
return { code: "claude_canary_auth_failed", minimumRequiredVersion: null };
|
|
35149
|
+
}
|
|
35150
|
+
if (typedReasonCode === "claude_bridge_version_required") {
|
|
35151
|
+
return {
|
|
35152
|
+
code: "claude_bridge_version_required",
|
|
35153
|
+
minimumRequiredVersion: demanded?.[2] ?? null
|
|
35154
|
+
};
|
|
35155
|
+
}
|
|
35156
|
+
if ((typedReasonCode === "rpc_error" || typedReasonCode === null || typedReasonCode === void 0) && CLAUDE_AUTH_FAILURE_RE.test(recorded)) {
|
|
35157
|
+
return { code: "claude_canary_auth_failed", minimumRequiredVersion: null };
|
|
35158
|
+
}
|
|
35159
|
+
return { code: "claude_canary_unknown", minimumRequiredVersion: null };
|
|
35160
|
+
}
|
|
34938
35161
|
var ClaudeListenerClosedDuringOpen = class extends Error {
|
|
34939
35162
|
constructor() {
|
|
34940
35163
|
super("listener model closed while the Claude worker was opening");
|
|
@@ -35048,6 +35271,8 @@ var ClaudeListenerModel = class {
|
|
|
35048
35271
|
...this.options.executable ? { executable: this.options.executable } : {},
|
|
35049
35272
|
...this.options.env ? { env: this.options.env } : {},
|
|
35050
35273
|
...this.options.onVersionNotice ? { onVersionNotice: this.options.onVersionNotice } : {},
|
|
35274
|
+
...this.options.onRuntimeNotice ? { onRuntimeNotice: this.options.onRuntimeNotice } : {},
|
|
35275
|
+
...this.options.events ? { events: this.options.events } : {},
|
|
35051
35276
|
signal: controller.signal,
|
|
35052
35277
|
...this.options.onWorkerStderrTail ? { onStderrTail: this.options.onWorkerStderrTail } : {},
|
|
35053
35278
|
clientName: "cswarm-listener"
|
|
@@ -35076,6 +35301,7 @@ var ClaudeListenerModel = class {
|
|
|
35076
35301
|
(0, import_node_os8.tmpdir)(),
|
|
35077
35302
|
`cswarm-claude-permission-canary-${process.pid}-${(0, import_node_crypto16.randomUUID)()}`
|
|
35078
35303
|
);
|
|
35304
|
+
let canaryError;
|
|
35079
35305
|
let sentinelCreated = false;
|
|
35080
35306
|
try {
|
|
35081
35307
|
await handle.session.enablePromptsAfterCanary({
|
|
@@ -35083,6 +35309,8 @@ var ClaudeListenerModel = class {
|
|
|
35083
35309
|
probeText: `Create the file ${sentinelPath} using the Write tool with content CSWARM_CANARY_NOOP. You must use the Write tool. Do nothing else.`,
|
|
35084
35310
|
...this.options.onCanaryAttempt ? { onAttempt: this.options.onCanaryAttempt } : {}
|
|
35085
35311
|
});
|
|
35312
|
+
} catch (error) {
|
|
35313
|
+
canaryError = error;
|
|
35086
35314
|
} finally {
|
|
35087
35315
|
try {
|
|
35088
35316
|
await (0, import_promises7.lstat)(sentinelPath);
|
|
@@ -35094,9 +35322,22 @@ var ClaudeListenerModel = class {
|
|
|
35094
35322
|
}
|
|
35095
35323
|
if (sentinelCreated) {
|
|
35096
35324
|
throw new AcpPermissionCanaryError(
|
|
35097
|
-
"Claude bridge wrote the permission canary sentinel before denial"
|
|
35325
|
+
"Claude bridge wrote the permission canary sentinel before denial",
|
|
35326
|
+
"claude_canary_write_not_blocked"
|
|
35098
35327
|
);
|
|
35099
35328
|
}
|
|
35329
|
+
if (canaryError instanceof AcpPermissionCanaryError) {
|
|
35330
|
+
const shape = classifyClaudeCanaryFailure(
|
|
35331
|
+
canaryError.message,
|
|
35332
|
+
canaryError.reasonCode
|
|
35333
|
+
);
|
|
35334
|
+
throw new AcpPermissionCanaryError(
|
|
35335
|
+
canaryError.message,
|
|
35336
|
+
shape.code,
|
|
35337
|
+
shape.minimumRequiredVersion
|
|
35338
|
+
);
|
|
35339
|
+
}
|
|
35340
|
+
if (canaryError !== void 0) throw canaryError;
|
|
35100
35341
|
}
|
|
35101
35342
|
};
|
|
35102
35343
|
|
|
@@ -35222,6 +35463,7 @@ var CodexListenerModel = class {
|
|
|
35222
35463
|
...this.options.executable ? { executable: this.options.executable } : {},
|
|
35223
35464
|
...this.options.env ? { env: this.options.env } : {},
|
|
35224
35465
|
...this.options.onVersionNotice ? { onVersionNotice: this.options.onVersionNotice } : {},
|
|
35466
|
+
...this.options.events ? { events: this.options.events } : {},
|
|
35225
35467
|
signal: controller.signal,
|
|
35226
35468
|
...this.options.onWorkerStderrTail ? { onStderrTail: this.options.onWorkerStderrTail } : {},
|
|
35227
35469
|
clientName: "cswarm-listener"
|
|
@@ -35909,6 +36151,21 @@ var UUID_RE15 = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-
|
|
|
35909
36151
|
var MAX_QUEUE_BYTES = 1024 * 1024;
|
|
35910
36152
|
var QUEUE_FILE = "pending-for-main.json";
|
|
35911
36153
|
var QUEUE_LOCK = "pending-for-main";
|
|
36154
|
+
var QUEUE_KEYS = /* @__PURE__ */ new Set(["version", "entries", "droppedCount"]);
|
|
36155
|
+
var ENTRY_KEYS = /* @__PURE__ */ new Set([
|
|
36156
|
+
"signalId",
|
|
36157
|
+
"workspaceId",
|
|
36158
|
+
"principalId",
|
|
36159
|
+
"fromId",
|
|
36160
|
+
"fromKind",
|
|
36161
|
+
"kind",
|
|
36162
|
+
"senderName",
|
|
36163
|
+
"body",
|
|
36164
|
+
"attachmentCount",
|
|
36165
|
+
"createdAt",
|
|
36166
|
+
"queuedAt",
|
|
36167
|
+
"observationPending"
|
|
36168
|
+
]);
|
|
35912
36169
|
var LISTENER_MAIN_QUEUE_MAX = 200;
|
|
35913
36170
|
var LISTENER_DEFER_OVER_MIN = 1;
|
|
35914
36171
|
var LISTENER_DEFER_OVER_MAX = 1e4;
|
|
@@ -35939,26 +36196,12 @@ function decideListenerRoute(route, threshold, bodyLength) {
|
|
|
35939
36196
|
function checkedTimestamp2(value) {
|
|
35940
36197
|
return typeof value === "string" && Number.isFinite(Date.parse(value));
|
|
35941
36198
|
}
|
|
35942
|
-
function parseEntry(value) {
|
|
36199
|
+
function parseEntry(value, rejectUnknownKeys) {
|
|
35943
36200
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
35944
36201
|
throw new Error("stored pending-for-main entry is malformed");
|
|
35945
36202
|
}
|
|
35946
36203
|
const row = value;
|
|
35947
|
-
|
|
35948
|
-
"signalId",
|
|
35949
|
-
"workspaceId",
|
|
35950
|
-
"principalId",
|
|
35951
|
-
"fromId",
|
|
35952
|
-
"fromKind",
|
|
35953
|
-
"kind",
|
|
35954
|
-
"senderName",
|
|
35955
|
-
"body",
|
|
35956
|
-
"attachmentCount",
|
|
35957
|
-
"createdAt",
|
|
35958
|
-
"queuedAt",
|
|
35959
|
-
"observationPending"
|
|
35960
|
-
]);
|
|
35961
|
-
if (Object.keys(row).some((key2) => !allowed.has(key2))) {
|
|
36204
|
+
if (rejectUnknownKeys && Object.keys(row).some((key2) => !ENTRY_KEYS.has(key2))) {
|
|
35962
36205
|
throw new Error("stored pending-for-main entry is malformed");
|
|
35963
36206
|
}
|
|
35964
36207
|
if (typeof row.signalId !== "string" || !UUID_RE15.test(row.signalId) || typeof row.workspaceId !== "string" || !UUID_RE15.test(row.workspaceId) || typeof row.principalId !== "string" || !UUID_RE15.test(row.principalId) || typeof row.fromId !== "string" || !UUID_RE15.test(row.fromId) || row.fromKind !== "user" && row.fromKind !== "agent" || !(row.kind === void 0 || row.kind === "ask" || row.kind === "note") || !(row.senderName === null || typeof row.senderName === "string" && row.senderName.length <= 200) || typeof row.body !== "string" || row.body.length < 1 || !(row.attachmentCount === void 0 || typeof row.attachmentCount === "number" && Number.isSafeInteger(row.attachmentCount) && row.attachmentCount >= 1 && row.attachmentCount <= 8) || !checkedTimestamp2(row.createdAt) || !checkedTimestamp2(row.queuedAt) || !(row.observationPending === void 0 || row.observationPending === true)) {
|
|
@@ -35979,7 +36222,7 @@ function parseEntry(value) {
|
|
|
35979
36222
|
...row.observationPending === true ? { observationPending: true } : {}
|
|
35980
36223
|
};
|
|
35981
36224
|
}
|
|
35982
|
-
function parseFile(raw) {
|
|
36225
|
+
function parseFile(raw, rejectUnknownKeys = false) {
|
|
35983
36226
|
let value;
|
|
35984
36227
|
try {
|
|
35985
36228
|
value = JSON.parse(raw);
|
|
@@ -35990,12 +36233,10 @@ function parseFile(raw) {
|
|
|
35990
36233
|
throw new Error("stored pending-for-main queue is malformed");
|
|
35991
36234
|
}
|
|
35992
36235
|
const row = value;
|
|
35993
|
-
if (Object.keys(row).some(
|
|
35994
|
-
(key2) => key2 !== "version" && key2 !== "entries" && key2 !== "droppedCount"
|
|
35995
|
-
) || row.version !== 1 || !Array.isArray(row.entries) || row.entries.length > LISTENER_MAIN_QUEUE_MAX || !(row.droppedCount === void 0 || typeof row.droppedCount === "number" && Number.isSafeInteger(row.droppedCount) && row.droppedCount >= 0)) {
|
|
36236
|
+
if (rejectUnknownKeys && Object.keys(row).some((key2) => !QUEUE_KEYS.has(key2)) || row.version !== 1 || !Array.isArray(row.entries) || row.entries.length > LISTENER_MAIN_QUEUE_MAX || !(row.droppedCount === void 0 || typeof row.droppedCount === "number" && Number.isSafeInteger(row.droppedCount) && row.droppedCount >= 0)) {
|
|
35996
36237
|
throw new Error("stored pending-for-main queue is malformed");
|
|
35997
36238
|
}
|
|
35998
|
-
const entries = row.entries.map(parseEntry);
|
|
36239
|
+
const entries = row.entries.map((entry) => parseEntry(entry, rejectUnknownKeys));
|
|
35999
36240
|
if (new Set(entries.map((entry) => entry.signalId)).size !== entries.length) {
|
|
36000
36241
|
throw new Error("stored pending-for-main queue repeats a signal");
|
|
36001
36242
|
}
|
|
@@ -36016,7 +36257,7 @@ var FilePendingMainQueue = class {
|
|
|
36016
36257
|
return raw === null ? { version: 1, entries: [], droppedCount: 0 } : parseFile(raw);
|
|
36017
36258
|
}
|
|
36018
36259
|
async writeUnlocked(file) {
|
|
36019
|
-
const canonical = parseFile(JSON.stringify(file));
|
|
36260
|
+
const canonical = parseFile(JSON.stringify(file), true);
|
|
36020
36261
|
await writeSecureJsonFile(this.path, JSON.stringify(canonical));
|
|
36021
36262
|
}
|
|
36022
36263
|
async read() {
|
|
@@ -36030,7 +36271,7 @@ var FilePendingMainQueue = class {
|
|
|
36030
36271
|
return { count: file.entries.length, droppedCount: file.droppedCount };
|
|
36031
36272
|
}
|
|
36032
36273
|
async enqueue(entry) {
|
|
36033
|
-
const checked = parseEntry(entry);
|
|
36274
|
+
const checked = parseEntry(entry, true);
|
|
36034
36275
|
return await withFileLock(this.directory, QUEUE_LOCK, async () => {
|
|
36035
36276
|
const file = await this.readUnlocked();
|
|
36036
36277
|
if (file.entries.some((item) => item.signalId === checked.signalId)) {
|
|
@@ -36089,7 +36330,7 @@ function pendingMainEntry(signal, principalId, provenance, now, options = {}) {
|
|
|
36089
36330
|
createdAt: signal.created_at,
|
|
36090
36331
|
queuedAt: new Date(now).toISOString(),
|
|
36091
36332
|
...options.observationPending ? { observationPending: true } : {}
|
|
36092
|
-
});
|
|
36333
|
+
}, true);
|
|
36093
36334
|
}
|
|
36094
36335
|
|
|
36095
36336
|
// src/listener/runtime.ts
|
|
@@ -36102,6 +36343,7 @@ var LISTENER_REPLY_ONLY_MINIMUM_MS = SIGNAL_REQUEST_TIMEOUT_MS + LISTENER_ACK_ON
|
|
|
36102
36343
|
var LISTENER_PROMPT_START_MINIMUM_MS = SIGNAL_READ_TIMEOUT_MS + ACP_DEFAULT_REQUEST_TIMEOUT_MS + LISTENER_REPLY_ONLY_MINIMUM_MS;
|
|
36103
36344
|
var LISTENER_DELIVERY_RETRY_INITIAL_MS = 500;
|
|
36104
36345
|
var LISTENER_DELIVERY_RETRY_MAX_MS = 3e4;
|
|
36346
|
+
var LISTENER_HOST_PORTS_PROBE_MS = 6e4;
|
|
36105
36347
|
var UUID_RE16 = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
36106
36348
|
var ListenerCapabilityError = class extends Error {
|
|
36107
36349
|
code;
|
|
@@ -36536,6 +36778,8 @@ async function runListenerRuntime(options) {
|
|
|
36536
36778
|
let ready = false;
|
|
36537
36779
|
let deliveryMode = null;
|
|
36538
36780
|
let readAttempt = 0;
|
|
36781
|
+
let readEpisodeStartedAtMs = null;
|
|
36782
|
+
let readEpisodeAttempts = 0;
|
|
36539
36783
|
const onAbort = () => {
|
|
36540
36784
|
options.model.cancel();
|
|
36541
36785
|
};
|
|
@@ -36633,6 +36877,18 @@ async function runListenerRuntime(options) {
|
|
|
36633
36877
|
}
|
|
36634
36878
|
});
|
|
36635
36879
|
requireCapabilities(page);
|
|
36880
|
+
if (ready && readEpisodeStartedAtMs !== null) {
|
|
36881
|
+
const recoveredAtMs = now();
|
|
36882
|
+
options.onEvent?.({
|
|
36883
|
+
type: "read_recovered",
|
|
36884
|
+
attempts: readEpisodeAttempts,
|
|
36885
|
+
durationMs: Math.max(0, recoveredAtMs - readEpisodeStartedAtMs),
|
|
36886
|
+
startedAt: new Date(readEpisodeStartedAtMs).toISOString(),
|
|
36887
|
+
ts: new Date(recoveredAtMs).toISOString()
|
|
36888
|
+
});
|
|
36889
|
+
readEpisodeStartedAtMs = null;
|
|
36890
|
+
readEpisodeAttempts = 0;
|
|
36891
|
+
}
|
|
36636
36892
|
const nextMode = classifyDeliveryMode(page, durableConfigured);
|
|
36637
36893
|
if (nextMode !== deliveryMode) {
|
|
36638
36894
|
deliveryMode = nextMode;
|
|
@@ -36653,19 +36909,25 @@ async function runListenerRuntime(options) {
|
|
|
36653
36909
|
stop = { reason: "credential", error: asError2(error) };
|
|
36654
36910
|
break;
|
|
36655
36911
|
}
|
|
36656
|
-
|
|
36657
|
-
|
|
36658
|
-
break;
|
|
36659
|
-
}
|
|
36660
|
-
if (isRetryableFollowError(error)) {
|
|
36912
|
+
const failure = classifySignalReadFailure(error);
|
|
36913
|
+
if (isRetryableFollowError(error) || failure.code === "aborted" || failure.code === "host_ports_exhausted") {
|
|
36661
36914
|
readAttempt += 1;
|
|
36662
|
-
const delayMs = nextFollowBackoffMs(readAttempt, null, random);
|
|
36915
|
+
const delayMs = failure.code === "host_ports_exhausted" ? LISTENER_HOST_PORTS_PROBE_MS : nextFollowBackoffMs(readAttempt, null, random);
|
|
36663
36916
|
if (ready) {
|
|
36917
|
+
const failedAtMs = now();
|
|
36918
|
+
if (readEpisodeStartedAtMs === null) {
|
|
36919
|
+
readEpisodeStartedAtMs = failedAtMs;
|
|
36920
|
+
readEpisodeAttempts = 0;
|
|
36921
|
+
}
|
|
36922
|
+
readEpisodeAttempts += 1;
|
|
36664
36923
|
options.onEvent?.({
|
|
36665
36924
|
type: "read_retry",
|
|
36666
36925
|
attempt: readAttempt,
|
|
36926
|
+
episodeAttempt: readEpisodeAttempts,
|
|
36927
|
+
episodeStartedAt: new Date(readEpisodeStartedAtMs).toISOString(),
|
|
36928
|
+
failure,
|
|
36667
36929
|
delayMs,
|
|
36668
|
-
ts:
|
|
36930
|
+
ts: new Date(failedAtMs).toISOString()
|
|
36669
36931
|
});
|
|
36670
36932
|
}
|
|
36671
36933
|
await sleep2(delayMs, abort);
|
|
@@ -36688,6 +36950,7 @@ async function runListenerRuntime(options) {
|
|
|
36688
36950
|
type: "ready",
|
|
36689
36951
|
workspaceId: options.workspaceId,
|
|
36690
36952
|
principalId: options.principalId,
|
|
36953
|
+
cadenceMs: pollMs,
|
|
36691
36954
|
ts: eventTime(now)
|
|
36692
36955
|
});
|
|
36693
36956
|
if (options.declareModel !== void 0) {
|
|
@@ -37217,13 +37480,261 @@ async function runListenerRuntime(options) {
|
|
|
37217
37480
|
return stop ?? { reason: "cancelled" };
|
|
37218
37481
|
}
|
|
37219
37482
|
|
|
37483
|
+
// src/listener/read-health.ts
|
|
37484
|
+
var HOUR_MS = 60 * 6e4;
|
|
37485
|
+
var MINUTE_MS = 6e4;
|
|
37486
|
+
var HEALTH_WINDOW_MS = 24 * HOUR_MS;
|
|
37487
|
+
var LISTENER_READ_RETRY_HOUR_CAP = 25;
|
|
37488
|
+
var LISTENER_READ_RETRY_MINUTE_CAP = 61;
|
|
37489
|
+
var LISTENER_CLAIM_HOUR_CAP = 25;
|
|
37490
|
+
var LISTENER_THROUGHPUT_LAPSE_RATIO = 0.5;
|
|
37491
|
+
var FAILURE_CODES = /* @__PURE__ */ new Set([
|
|
37492
|
+
"http_status",
|
|
37493
|
+
"no_response",
|
|
37494
|
+
"body_timeout",
|
|
37495
|
+
"malformed_response",
|
|
37496
|
+
"aborted",
|
|
37497
|
+
"host_ports_exhausted",
|
|
37498
|
+
"unclassified"
|
|
37499
|
+
]);
|
|
37500
|
+
function emptyListenerReadHealth() {
|
|
37501
|
+
return {
|
|
37502
|
+
currentEpisodeStartedAt: null,
|
|
37503
|
+
currentEpisodeAttempts: 0,
|
|
37504
|
+
currentReasonCode: null,
|
|
37505
|
+
currentHttpStatus: null,
|
|
37506
|
+
currentErrorConstructor: null,
|
|
37507
|
+
retryHours: [],
|
|
37508
|
+
retryMinutes: [],
|
|
37509
|
+
claimCadenceMs: null,
|
|
37510
|
+
claimHours: []
|
|
37511
|
+
};
|
|
37512
|
+
}
|
|
37513
|
+
function bucketStart(ts, sizeMs) {
|
|
37514
|
+
const time = Date.parse(ts);
|
|
37515
|
+
return new Date(Math.floor(time / sizeMs) * sizeMs).toISOString();
|
|
37516
|
+
}
|
|
37517
|
+
function trimNewest(rows3, cap) {
|
|
37518
|
+
return rows3.sort((left, right) => Date.parse(left.hourStart) - Date.parse(right.hourStart)).slice(-cap);
|
|
37519
|
+
}
|
|
37520
|
+
function recordRetryHour(rows3, ts, episodeStarted) {
|
|
37521
|
+
const hourStart = bucketStart(ts, HOUR_MS);
|
|
37522
|
+
const next = rows3.map((row) => ({ ...row }));
|
|
37523
|
+
const existing = next.find((row) => row.hourStart === hourStart);
|
|
37524
|
+
if (existing) {
|
|
37525
|
+
existing.retries += 1;
|
|
37526
|
+
if (episodeStarted) existing.episodes += 1;
|
|
37527
|
+
} else {
|
|
37528
|
+
next.push({
|
|
37529
|
+
hourStart,
|
|
37530
|
+
retries: 1,
|
|
37531
|
+
episodes: episodeStarted ? 1 : 0,
|
|
37532
|
+
longestEpisodeAttempts: 0,
|
|
37533
|
+
longestEpisodeDurationMs: 0
|
|
37534
|
+
});
|
|
37535
|
+
}
|
|
37536
|
+
return trimNewest(next, LISTENER_READ_RETRY_HOUR_CAP);
|
|
37537
|
+
}
|
|
37538
|
+
function recordRetryMinute(rows3, ts) {
|
|
37539
|
+
const minuteStart = bucketStart(ts, MINUTE_MS);
|
|
37540
|
+
const next = rows3.map((row) => ({ ...row }));
|
|
37541
|
+
const existing = next.find((row) => row.minuteStart === minuteStart);
|
|
37542
|
+
if (existing) {
|
|
37543
|
+
existing.retries += 1;
|
|
37544
|
+
} else {
|
|
37545
|
+
next.push({ minuteStart, retries: 1 });
|
|
37546
|
+
}
|
|
37547
|
+
return next.sort(
|
|
37548
|
+
(left, right) => Date.parse(left.minuteStart) - Date.parse(right.minuteStart)
|
|
37549
|
+
).slice(-LISTENER_READ_RETRY_MINUTE_CAP);
|
|
37550
|
+
}
|
|
37551
|
+
function recordListenerReadRetry(health, input) {
|
|
37552
|
+
return {
|
|
37553
|
+
...health,
|
|
37554
|
+
currentEpisodeStartedAt: input.episodeStartedAt,
|
|
37555
|
+
currentEpisodeAttempts: input.episodeAttempt,
|
|
37556
|
+
currentReasonCode: input.failure.code,
|
|
37557
|
+
currentHttpStatus: input.failure.httpStatus,
|
|
37558
|
+
currentErrorConstructor: input.failure.errorConstructor,
|
|
37559
|
+
retryHours: recordRetryHour(
|
|
37560
|
+
health.retryHours,
|
|
37561
|
+
input.ts,
|
|
37562
|
+
input.episodeAttempt === 1
|
|
37563
|
+
),
|
|
37564
|
+
retryMinutes: recordRetryMinute(health.retryMinutes, input.ts)
|
|
37565
|
+
};
|
|
37566
|
+
}
|
|
37567
|
+
function recordListenerReadRecovery(health, input) {
|
|
37568
|
+
const hourStart = bucketStart(input.startedAt, HOUR_MS);
|
|
37569
|
+
const retryHours = health.retryHours.map((row) => ({ ...row }));
|
|
37570
|
+
const hour = retryHours.find((row) => row.hourStart === hourStart);
|
|
37571
|
+
if (hour) {
|
|
37572
|
+
if (input.durationMs > hour.longestEpisodeDurationMs || input.durationMs === hour.longestEpisodeDurationMs && input.attempts > hour.longestEpisodeAttempts) {
|
|
37573
|
+
hour.longestEpisodeAttempts = input.attempts;
|
|
37574
|
+
hour.longestEpisodeDurationMs = input.durationMs;
|
|
37575
|
+
}
|
|
37576
|
+
}
|
|
37577
|
+
return {
|
|
37578
|
+
...health,
|
|
37579
|
+
currentEpisodeStartedAt: null,
|
|
37580
|
+
currentEpisodeAttempts: 0,
|
|
37581
|
+
currentReasonCode: null,
|
|
37582
|
+
currentHttpStatus: null,
|
|
37583
|
+
currentErrorConstructor: null,
|
|
37584
|
+
retryHours: trimNewest(retryHours, LISTENER_READ_RETRY_HOUR_CAP)
|
|
37585
|
+
};
|
|
37586
|
+
}
|
|
37587
|
+
function recordListenerClaimCadence(health, cadenceMs) {
|
|
37588
|
+
return { ...health, claimCadenceMs: cadenceMs };
|
|
37589
|
+
}
|
|
37590
|
+
function recordListenerClaim(health, ts) {
|
|
37591
|
+
const hourStart = bucketStart(ts, HOUR_MS);
|
|
37592
|
+
const claimHours = health.claimHours.map((row) => ({ ...row }));
|
|
37593
|
+
const hour = claimHours.find((row) => row.hourStart === hourStart);
|
|
37594
|
+
if (hour) hour.claims += 1;
|
|
37595
|
+
else claimHours.push({ hourStart, claims: 1 });
|
|
37596
|
+
return {
|
|
37597
|
+
...health,
|
|
37598
|
+
claimHours: trimNewest(claimHours, LISTENER_CLAIM_HOUR_CAP)
|
|
37599
|
+
};
|
|
37600
|
+
}
|
|
37601
|
+
function hasExpectedKeys(value, expected, rejectUnknownKeys) {
|
|
37602
|
+
const actual = Object.keys(value);
|
|
37603
|
+
const allowed = new Set(expected);
|
|
37604
|
+
return expected.every((key2) => actual.includes(key2)) && (!rejectUnknownKeys || actual.every((key2) => allowed.has(key2)));
|
|
37605
|
+
}
|
|
37606
|
+
function validTimestamp(value) {
|
|
37607
|
+
return typeof value === "string" && Number.isFinite(Date.parse(value));
|
|
37608
|
+
}
|
|
37609
|
+
function validCount(value) {
|
|
37610
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
37611
|
+
}
|
|
37612
|
+
function parseListenerReadHealth(value, rejectUnknownKeys = false) {
|
|
37613
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
37614
|
+
const row = value;
|
|
37615
|
+
if (!hasExpectedKeys(row, [
|
|
37616
|
+
"currentEpisodeStartedAt",
|
|
37617
|
+
"currentEpisodeAttempts",
|
|
37618
|
+
"currentReasonCode",
|
|
37619
|
+
"currentHttpStatus",
|
|
37620
|
+
"currentErrorConstructor",
|
|
37621
|
+
"retryHours",
|
|
37622
|
+
"retryMinutes",
|
|
37623
|
+
"claimCadenceMs",
|
|
37624
|
+
"claimHours"
|
|
37625
|
+
], rejectUnknownKeys)) return null;
|
|
37626
|
+
if (!(row.currentEpisodeStartedAt === null || validTimestamp(row.currentEpisodeStartedAt)) || !validCount(row.currentEpisodeAttempts) || !(row.currentReasonCode === null || typeof row.currentReasonCode === "string" && FAILURE_CODES.has(row.currentReasonCode)) || !(row.currentHttpStatus === null || typeof row.currentHttpStatus === "number" && Number.isSafeInteger(row.currentHttpStatus) && row.currentHttpStatus >= 100 && row.currentHttpStatus <= 599) || !(row.currentErrorConstructor === null || typeof row.currentErrorConstructor === "string" && /^[A-Za-z0-9_$-]{1,96}$/.test(row.currentErrorConstructor)) || !(row.claimCadenceMs === null || typeof row.claimCadenceMs === "number" && Number.isSafeInteger(row.claimCadenceMs) && row.claimCadenceMs >= 1) || !Array.isArray(row.retryHours) || row.retryHours.length > LISTENER_READ_RETRY_HOUR_CAP || !Array.isArray(row.retryMinutes) || row.retryMinutes.length > LISTENER_READ_RETRY_MINUTE_CAP || !Array.isArray(row.claimHours) || row.claimHours.length > LISTENER_CLAIM_HOUR_CAP) return null;
|
|
37627
|
+
if (row.currentEpisodeStartedAt === null !== (row.currentEpisodeAttempts === 0) || row.currentEpisodeStartedAt === null !== (row.currentReasonCode === null) || row.currentReasonCode === "http_status" !== (row.currentHttpStatus !== null) || row.currentReasonCode === "unclassified" !== (row.currentErrorConstructor !== null)) return null;
|
|
37628
|
+
for (const value2 of row.retryHours) {
|
|
37629
|
+
if (!value2 || typeof value2 !== "object" || Array.isArray(value2)) return null;
|
|
37630
|
+
const hour = value2;
|
|
37631
|
+
if (!hasExpectedKeys(hour, [
|
|
37632
|
+
"hourStart",
|
|
37633
|
+
"retries",
|
|
37634
|
+
"episodes",
|
|
37635
|
+
"longestEpisodeAttempts",
|
|
37636
|
+
"longestEpisodeDurationMs"
|
|
37637
|
+
], rejectUnknownKeys) || !validTimestamp(hour.hourStart) || !validCount(hour.retries) || !validCount(hour.episodes) || !validCount(hour.longestEpisodeAttempts) || !validCount(hour.longestEpisodeDurationMs)) return null;
|
|
37638
|
+
}
|
|
37639
|
+
for (const value2 of row.retryMinutes) {
|
|
37640
|
+
if (!value2 || typeof value2 !== "object" || Array.isArray(value2)) return null;
|
|
37641
|
+
const minute = value2;
|
|
37642
|
+
if (!hasExpectedKeys(minute, ["minuteStart", "retries"], rejectUnknownKeys) || !validTimestamp(minute.minuteStart) || !validCount(minute.retries)) return null;
|
|
37643
|
+
}
|
|
37644
|
+
for (const value2 of row.claimHours) {
|
|
37645
|
+
if (!value2 || typeof value2 !== "object" || Array.isArray(value2)) return null;
|
|
37646
|
+
const hour = value2;
|
|
37647
|
+
if (!hasExpectedKeys(hour, ["hourStart", "claims"], rejectUnknownKeys) || !validTimestamp(hour.hourStart) || !validCount(hour.claims)) return null;
|
|
37648
|
+
}
|
|
37649
|
+
return {
|
|
37650
|
+
currentEpisodeStartedAt: row.currentEpisodeStartedAt,
|
|
37651
|
+
currentEpisodeAttempts: row.currentEpisodeAttempts,
|
|
37652
|
+
currentReasonCode: row.currentReasonCode,
|
|
37653
|
+
currentHttpStatus: row.currentHttpStatus,
|
|
37654
|
+
currentErrorConstructor: row.currentErrorConstructor,
|
|
37655
|
+
retryHours: row.retryHours.map((hour) => ({
|
|
37656
|
+
hourStart: hour.hourStart,
|
|
37657
|
+
retries: hour.retries,
|
|
37658
|
+
episodes: hour.episodes,
|
|
37659
|
+
longestEpisodeAttempts: hour.longestEpisodeAttempts,
|
|
37660
|
+
longestEpisodeDurationMs: hour.longestEpisodeDurationMs
|
|
37661
|
+
})),
|
|
37662
|
+
retryMinutes: row.retryMinutes.map((minute) => ({
|
|
37663
|
+
minuteStart: minute.minuteStart,
|
|
37664
|
+
retries: minute.retries
|
|
37665
|
+
})),
|
|
37666
|
+
claimCadenceMs: row.claimCadenceMs,
|
|
37667
|
+
claimHours: row.claimHours.map((hour) => ({
|
|
37668
|
+
hourStart: hour.hourStart,
|
|
37669
|
+
claims: hour.claims
|
|
37670
|
+
}))
|
|
37671
|
+
};
|
|
37672
|
+
}
|
|
37673
|
+
function summarizeListenerReadHealth(health, readyAt, nowMs) {
|
|
37674
|
+
const windowStart = nowMs - HEALTH_WINDOW_MS;
|
|
37675
|
+
const retryHours = health.retryHours.filter(
|
|
37676
|
+
(row) => Date.parse(row.hourStart) + HOUR_MS > windowStart && Date.parse(row.hourStart) <= nowMs
|
|
37677
|
+
);
|
|
37678
|
+
let episodesLast24h = retryHours.reduce((sum, row) => sum + row.episodes, 0);
|
|
37679
|
+
let longestEpisodeAttemptsLast24h = 0;
|
|
37680
|
+
let longestEpisodeDurationMsLast24h = 0;
|
|
37681
|
+
for (const row of retryHours) {
|
|
37682
|
+
if (row.longestEpisodeDurationMs > longestEpisodeDurationMsLast24h || row.longestEpisodeDurationMs === longestEpisodeDurationMsLast24h && row.longestEpisodeAttempts > longestEpisodeAttemptsLast24h) {
|
|
37683
|
+
longestEpisodeAttemptsLast24h = row.longestEpisodeAttempts;
|
|
37684
|
+
longestEpisodeDurationMsLast24h = row.longestEpisodeDurationMs;
|
|
37685
|
+
}
|
|
37686
|
+
}
|
|
37687
|
+
const currentStartedMs = health.currentEpisodeStartedAt === null ? null : Date.parse(health.currentEpisodeStartedAt);
|
|
37688
|
+
const currentEpisodeDurationMs = currentStartedMs === null ? null : Math.max(0, nowMs - currentStartedMs);
|
|
37689
|
+
if (currentStartedMs !== null && currentStartedMs >= windowStart && currentEpisodeDurationMs !== null && (currentEpisodeDurationMs > longestEpisodeDurationMsLast24h || currentEpisodeDurationMs === longestEpisodeDurationMsLast24h && health.currentEpisodeAttempts > longestEpisodeAttemptsLast24h)) {
|
|
37690
|
+
longestEpisodeAttemptsLast24h = health.currentEpisodeAttempts;
|
|
37691
|
+
longestEpisodeDurationMsLast24h = currentEpisodeDurationMs;
|
|
37692
|
+
}
|
|
37693
|
+
const rollingMinuteStart = Math.floor((nowMs - HOUR_MS) / MINUTE_MS) * MINUTE_MS;
|
|
37694
|
+
const retriesLastHour = health.retryMinutes.reduce((sum, row) => Date.parse(row.minuteStart) >= rollingMinuteStart && Date.parse(row.minuteStart) <= nowMs ? sum + row.retries : sum, 0);
|
|
37695
|
+
const claimThroughputHours = [];
|
|
37696
|
+
if (health.claimCadenceMs !== null && readyAt !== null) {
|
|
37697
|
+
const readyMs = Date.parse(readyAt);
|
|
37698
|
+
const firstFullHour = Math.ceil(readyMs / HOUR_MS) * HOUR_MS;
|
|
37699
|
+
const currentHour = Math.floor(nowMs / HOUR_MS) * HOUR_MS;
|
|
37700
|
+
const first = Math.max(firstFullHour, currentHour - HEALTH_WINDOW_MS);
|
|
37701
|
+
const claimsByHour = new Map(
|
|
37702
|
+
health.claimHours.map((row) => [row.hourStart, row.claims])
|
|
37703
|
+
);
|
|
37704
|
+
const expectedClaims = HOUR_MS / health.claimCadenceMs;
|
|
37705
|
+
for (let hour = first; hour < currentHour; hour += HOUR_MS) {
|
|
37706
|
+
const hourStart = new Date(hour).toISOString();
|
|
37707
|
+
const claims = claimsByHour.get(hourStart) ?? 0;
|
|
37708
|
+
claimThroughputHours.push({
|
|
37709
|
+
hourStart,
|
|
37710
|
+
claims,
|
|
37711
|
+
expectedClaims,
|
|
37712
|
+
ratio: claims / expectedClaims
|
|
37713
|
+
});
|
|
37714
|
+
}
|
|
37715
|
+
}
|
|
37716
|
+
const throughputLapseHours = claimThroughputHours.filter(
|
|
37717
|
+
(hour) => hour.ratio < LISTENER_THROUGHPUT_LAPSE_RATIO
|
|
37718
|
+
);
|
|
37719
|
+
return {
|
|
37720
|
+
currentEpisodeDurationMs,
|
|
37721
|
+
episodesLast24h,
|
|
37722
|
+
longestEpisodeAttemptsLast24h,
|
|
37723
|
+
longestEpisodeDurationMsLast24h,
|
|
37724
|
+
retriesLastHour,
|
|
37725
|
+
retryHours,
|
|
37726
|
+
claimThroughputHours,
|
|
37727
|
+
throughputLapseHours
|
|
37728
|
+
};
|
|
37729
|
+
}
|
|
37730
|
+
|
|
37220
37731
|
// src/listener/control.ts
|
|
37221
37732
|
var import_node_net = require("node:net");
|
|
37222
37733
|
var import_promises9 = require("node:fs/promises");
|
|
37223
37734
|
var import_node_path16 = require("node:path");
|
|
37224
37735
|
var UUID_RE17 = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
37225
37736
|
var SEMVER_RE2 = /^(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)(?:-(?:0|[1-9]\d*|[A-Za-z-][0-9A-Za-z-]*)(?:\.(?:0|[1-9]\d*|[A-Za-z-][0-9A-Za-z-]*))*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
|
|
37226
|
-
var MAX_STATUS_BYTES =
|
|
37737
|
+
var MAX_STATUS_BYTES = 32 * 1024;
|
|
37227
37738
|
var MAX_CONTROL_BYTES = 8 * 1024;
|
|
37228
37739
|
var CONTROL_TIMEOUT_MS = 2e3;
|
|
37229
37740
|
var START_LOCK_WAIT_MS = 2e3;
|
|
@@ -37270,8 +37781,13 @@ var STATUS_ALLOWED_KEYS = /* @__PURE__ */ new Set([
|
|
|
37270
37781
|
"lastSignalId",
|
|
37271
37782
|
"lastErrorCode",
|
|
37272
37783
|
"lastErrorDetail",
|
|
37784
|
+
"lastErrorReasonCode",
|
|
37785
|
+
"providerExecutable",
|
|
37273
37786
|
"providerVersion",
|
|
37274
37787
|
"providerLastMeasuredVersion",
|
|
37788
|
+
"providerBundledAgentSdkVersion",
|
|
37789
|
+
"providerBundledClaudeCodeVersion",
|
|
37790
|
+
"providerMinimumRequiredVersion",
|
|
37275
37791
|
"cswarmVersion",
|
|
37276
37792
|
"lastWorkerStderrTail",
|
|
37277
37793
|
"logPath",
|
|
@@ -37284,7 +37800,10 @@ var STATUS_ALLOWED_KEYS = /* @__PURE__ */ new Set([
|
|
|
37284
37800
|
"routeMode",
|
|
37285
37801
|
"deferOverChars",
|
|
37286
37802
|
"pendingForMainCount",
|
|
37287
|
-
"droppedForMainCount"
|
|
37803
|
+
"droppedForMainCount",
|
|
37804
|
+
"readHealth",
|
|
37805
|
+
"connectionsOpened",
|
|
37806
|
+
"connectionReuseRatio"
|
|
37288
37807
|
]);
|
|
37289
37808
|
var STATUS_SENSITIVE_KEYS = /* @__PURE__ */ new Set([
|
|
37290
37809
|
"leaseId",
|
|
@@ -37310,7 +37829,7 @@ var STATUS_DELIVERY_KEYS = [
|
|
|
37310
37829
|
"lastClaimAt",
|
|
37311
37830
|
"lastAckAt"
|
|
37312
37831
|
];
|
|
37313
|
-
function parseStatus(raw) {
|
|
37832
|
+
function parseStatus(raw, rejectUnknownKeys = false) {
|
|
37314
37833
|
let value;
|
|
37315
37834
|
try {
|
|
37316
37835
|
value = JSON.parse(raw);
|
|
@@ -37325,14 +37844,15 @@ function parseStatus(raw) {
|
|
|
37325
37844
|
if (STATUS_SENSITIVE_KEYS.has(key2)) {
|
|
37326
37845
|
throw new Error("stored listener status contains a forbidden field");
|
|
37327
37846
|
}
|
|
37328
|
-
if (!STATUS_ALLOWED_KEYS.has(key2)) {
|
|
37847
|
+
if (rejectUnknownKeys && !STATUS_ALLOWED_KEYS.has(key2)) {
|
|
37329
37848
|
throw new Error("stored listener status is malformed");
|
|
37330
37849
|
}
|
|
37331
37850
|
}
|
|
37332
37851
|
const nullableUuid3 = (candidate) => candidate === null || typeof candidate === "string" && UUID_RE17.test(candidate);
|
|
37333
37852
|
const nullableCount = (candidate) => candidate === null || typeof candidate === "number" && Number.isSafeInteger(candidate) && candidate >= 0;
|
|
37334
37853
|
const nullableTimestamp3 = (candidate) => candidate === null || typeof candidate === "string" && Number.isFinite(Date.parse(candidate));
|
|
37335
|
-
|
|
37854
|
+
const readHealth = row.readHealth === void 0 ? void 0 : parseListenerReadHealth(row.readHealth, rejectUnknownKeys);
|
|
37855
|
+
if (row.version !== 1 || typeof row.instanceId !== "string" || !UUID_RE17.test(row.instanceId) || row.provider !== "grok" && row.provider !== "opencode" && row.provider !== "claude" && row.provider !== "codex" || typeof row.profileId !== "string" || typeof row.workspaceId !== "string" || !UUID_RE17.test(row.workspaceId) || typeof row.principalId !== "string" || !UUID_RE17.test(row.principalId) || !Number.isSafeInteger(row.pid) || row.pid < 1 || typeof row.state !== "string" || !["starting", "ready", "stopping", "stopped", "failed"].includes(row.state) || typeof row.startedAt !== "string" || !Number.isFinite(Date.parse(row.startedAt)) || !(row.readyAt === null || typeof row.readyAt === "string" && Number.isFinite(Date.parse(row.readyAt))) || typeof row.updatedAt !== "string" || !Number.isFinite(Date.parse(row.updatedAt)) || !(row.stoppedAt === null || typeof row.stoppedAt === "string" && Number.isFinite(Date.parse(row.stoppedAt))) || !nullableUuid3(row.lastSignalId) || !(row.lastErrorCode === null || typeof row.lastErrorCode === "string" && /^[a-z0-9_-]{1,96}$/.test(row.lastErrorCode)) || !(row.lastErrorDetail === void 0 || row.lastErrorDetail === null || typeof row.lastErrorDetail === "string" && row.lastErrorDetail.length > 0 && row.lastErrorDetail.length <= 2048 && !/swm_(?:agt|inv|cap)_/i.test(row.lastErrorDetail)) || !(row.lastErrorReasonCode === void 0 || row.lastErrorReasonCode === null || typeof row.lastErrorReasonCode === "string" && /^[a-z0-9_-]{1,96}$/.test(row.lastErrorReasonCode)) || !(row.providerExecutable === void 0 || row.providerExecutable === null || typeof row.providerExecutable === "string" && (0, import_node_path16.isAbsolute)(row.providerExecutable)) || !(row.providerVersion === void 0 || row.providerVersion === null || typeof row.providerVersion === "string" && SEMVER_RE2.test(row.providerVersion)) || !(row.providerLastMeasuredVersion === void 0 || row.providerLastMeasuredVersion === null || typeof row.providerLastMeasuredVersion === "string" && SEMVER_RE2.test(row.providerLastMeasuredVersion)) || !(row.providerBundledAgentSdkVersion === void 0 || row.providerBundledAgentSdkVersion === null || typeof row.providerBundledAgentSdkVersion === "string" && SEMVER_RE2.test(row.providerBundledAgentSdkVersion)) || !(row.providerBundledClaudeCodeVersion === void 0 || row.providerBundledClaudeCodeVersion === null || typeof row.providerBundledClaudeCodeVersion === "string" && SEMVER_RE2.test(row.providerBundledClaudeCodeVersion)) || !(row.providerMinimumRequiredVersion === void 0 || row.providerMinimumRequiredVersion === null || typeof row.providerMinimumRequiredVersion === "string" && SEMVER_RE2.test(row.providerMinimumRequiredVersion)) || !(row.cswarmVersion === void 0 || row.cswarmVersion === null || typeof row.cswarmVersion === "string" && SEMVER_RE2.test(row.cswarmVersion)) || (row.providerVersion === null || row.providerVersion === void 0) !== (row.providerLastMeasuredVersion === null || row.providerLastMeasuredVersion === void 0) || !(row.lastWorkerStderrTail === void 0 || row.lastWorkerStderrTail === null || typeof row.lastWorkerStderrTail === "string" && row.lastWorkerStderrTail.length > 0 && row.lastWorkerStderrTail.length <= 2048 && !/swm_(?:agt|inv|cap)_/i.test(row.lastWorkerStderrTail)) || typeof row.logPath !== "string" || !(0, import_node_path16.isAbsolute)(row.logPath) || !(row.deliveryMode === void 0 || row.deliveryMode === null || typeof row.deliveryMode === "string" && STATUS_DELIVERY_MODES.has(row.deliveryMode)) || !(row.pendingDeliveryCount === void 0 || nullableCount(row.pendingDeliveryCount)) || !(row.lastTerminalDeliveryFailureCount === void 0 || nullableCount(row.lastTerminalDeliveryFailureCount)) || !(row.lastTerminalDeliveryFailureAt === void 0 || nullableTimestamp3(row.lastTerminalDeliveryFailureAt)) || !(row.lastClaimAt === void 0 || nullableTimestamp3(row.lastClaimAt)) || !(row.lastAckAt === void 0 || nullableTimestamp3(row.lastAckAt)) || !(row.routeMode === void 0 || row.routeMode === "worker" || row.routeMode === "main" || row.routeMode === "split") || !(row.deferOverChars === void 0 || row.deferOverChars === null || typeof row.deferOverChars === "number" && Number.isSafeInteger(row.deferOverChars) && row.deferOverChars >= 1 && row.deferOverChars <= 1e4) || !(row.pendingForMainCount === void 0 || typeof row.pendingForMainCount === "number" && Number.isSafeInteger(row.pendingForMainCount) && row.pendingForMainCount >= 0) || !(row.droppedForMainCount === void 0 || typeof row.droppedForMainCount === "number" && Number.isSafeInteger(row.droppedForMainCount) && row.droppedForMainCount >= 0) || readHealth === null || !(row.connectionsOpened === void 0 || typeof row.connectionsOpened === "number" && Number.isSafeInteger(row.connectionsOpened) && row.connectionsOpened >= 0) || !(row.connectionReuseRatio === void 0 || typeof row.connectionReuseRatio === "number" && Number.isFinite(row.connectionReuseRatio) && row.connectionReuseRatio >= 0)) {
|
|
37336
37856
|
throw new Error("stored listener status is malformed");
|
|
37337
37857
|
}
|
|
37338
37858
|
const routeMode = row.routeMode ?? "worker";
|
|
@@ -37340,8 +37860,11 @@ function parseStatus(raw) {
|
|
|
37340
37860
|
if (routeMode === "split" && deferOverChars === null || routeMode !== "split" && deferOverChars !== null) {
|
|
37341
37861
|
throw new Error("stored listener status routing fields are malformed");
|
|
37342
37862
|
}
|
|
37863
|
+
const knownRow = Object.fromEntries(
|
|
37864
|
+
Object.entries(row).filter(([key2]) => STATUS_ALLOWED_KEYS.has(key2))
|
|
37865
|
+
);
|
|
37343
37866
|
return {
|
|
37344
|
-
...
|
|
37867
|
+
...knownRow,
|
|
37345
37868
|
deliveryMode: row.deliveryMode ?? null,
|
|
37346
37869
|
pendingDeliveryCount: row.pendingDeliveryCount ?? null,
|
|
37347
37870
|
lastTerminalDeliveryFailureCount: row.lastTerminalDeliveryFailureCount ?? null,
|
|
@@ -37356,11 +37879,15 @@ function parseStatus(raw) {
|
|
|
37356
37879
|
routeMode,
|
|
37357
37880
|
deferOverChars,
|
|
37358
37881
|
pendingForMainCount: row.pendingForMainCount ?? 0,
|
|
37359
|
-
droppedForMainCount: row.droppedForMainCount ?? 0
|
|
37882
|
+
droppedForMainCount: row.droppedForMainCount ?? 0,
|
|
37883
|
+
...readHealth === void 0 ? {} : { readHealth }
|
|
37360
37884
|
};
|
|
37361
37885
|
}
|
|
37362
37886
|
async function writeListenerStatus(paths, status) {
|
|
37363
37887
|
const serialized = JSON.stringify(status);
|
|
37888
|
+
if (Buffer.byteLength(serialized, "utf8") > MAX_STATUS_BYTES) {
|
|
37889
|
+
throw new Error("listener status is too large");
|
|
37890
|
+
}
|
|
37364
37891
|
const parsed = JSON.parse(serialized);
|
|
37365
37892
|
for (const key2 of STATUS_DELIVERY_KEYS) {
|
|
37366
37893
|
if (!(key2 in parsed)) {
|
|
@@ -37370,7 +37897,7 @@ async function writeListenerStatus(paths, status) {
|
|
|
37370
37897
|
if (!("lastErrorDetail" in parsed)) {
|
|
37371
37898
|
throw new Error("listener status is missing local error detail metadata");
|
|
37372
37899
|
}
|
|
37373
|
-
parseStatus(serialized);
|
|
37900
|
+
parseStatus(serialized, true);
|
|
37374
37901
|
await writeSecureJsonFile(paths.statusPath, serialized);
|
|
37375
37902
|
}
|
|
37376
37903
|
async function readListenerStatus(paths) {
|
|
@@ -37395,6 +37922,12 @@ async function appendListenerEvent(paths, event) {
|
|
|
37395
37922
|
"passed",
|
|
37396
37923
|
"reason",
|
|
37397
37924
|
"delay_ms",
|
|
37925
|
+
"reason_code",
|
|
37926
|
+
"http_status",
|
|
37927
|
+
"error_constructor",
|
|
37928
|
+
"episode_attempt",
|
|
37929
|
+
"attempts",
|
|
37930
|
+
"duration_ms",
|
|
37398
37931
|
"index",
|
|
37399
37932
|
"delivery_mode",
|
|
37400
37933
|
"pending_delivery_count",
|
|
@@ -37433,6 +37966,29 @@ async function appendListenerEvent(paths, event) {
|
|
|
37433
37966
|
if ((key2 === "attempt" || key2 === "total") && !(typeof value === "number" && Number.isSafeInteger(value) && value >= 1)) {
|
|
37434
37967
|
throw new Error("listener event attempt count is not allowed");
|
|
37435
37968
|
}
|
|
37969
|
+
if ((key2 === "episode_attempt" || key2 === "attempts") && !(typeof value === "number" && Number.isSafeInteger(value) && value >= 1)) {
|
|
37970
|
+
throw new Error("listener event episode count is not allowed");
|
|
37971
|
+
}
|
|
37972
|
+
if (key2 === "duration_ms" && !(typeof value === "number" && Number.isSafeInteger(value) && value >= 0)) {
|
|
37973
|
+
throw new Error("listener event episode duration is not allowed");
|
|
37974
|
+
}
|
|
37975
|
+
if (key2 === "http_status" && !(typeof value === "number" && Number.isSafeInteger(value) && value >= 100 && value <= 599)) {
|
|
37976
|
+
throw new Error("listener event HTTP status is not allowed");
|
|
37977
|
+
}
|
|
37978
|
+
if (key2 === "reason_code" && !(typeof value === "string" && [
|
|
37979
|
+
"http_status",
|
|
37980
|
+
"no_response",
|
|
37981
|
+
"body_timeout",
|
|
37982
|
+
"malformed_response",
|
|
37983
|
+
"aborted",
|
|
37984
|
+
"host_ports_exhausted",
|
|
37985
|
+
"unclassified"
|
|
37986
|
+
].includes(value))) {
|
|
37987
|
+
throw new Error("listener event read reason code is not allowed");
|
|
37988
|
+
}
|
|
37989
|
+
if (key2 === "error_constructor" && !(typeof value === "string" && /^[A-Za-z0-9_$-]{1,96}$/.test(value))) {
|
|
37990
|
+
throw new Error("listener event error constructor is not allowed");
|
|
37991
|
+
}
|
|
37436
37992
|
if (key2 === "passed" && typeof value !== "boolean") {
|
|
37437
37993
|
throw new Error("listener event canary result is not allowed");
|
|
37438
37994
|
}
|
|
@@ -37476,6 +38032,12 @@ async function appendListenerEvent(paths, event) {
|
|
|
37476
38032
|
if (event.event === "listener_canary_attempt" && (typeof event.attempt !== "number" || typeof event.total !== "number" || event.attempt > event.total || typeof event.passed !== "boolean" || !(event.reason === null || typeof event.reason === "string"))) {
|
|
37477
38033
|
throw new Error("listener canary attempt event is incomplete");
|
|
37478
38034
|
}
|
|
38035
|
+
if (event.event === "listener_read_retry" && (typeof event.reason_code !== "string" || typeof event.episode_attempt !== "number" || event.reason_code === "http_status" !== (typeof event.http_status === "number") || event.reason_code === "unclassified" !== (typeof event.error_constructor === "string"))) {
|
|
38036
|
+
throw new Error("listener read retry event is incomplete");
|
|
38037
|
+
}
|
|
38038
|
+
if (event.event === "listener_read_recovered" && (typeof event.attempts !== "number" || typeof event.duration_ms !== "number")) {
|
|
38039
|
+
throw new Error("listener read recovery event is incomplete");
|
|
38040
|
+
}
|
|
37479
38041
|
await ensureSecureStateDirectory(paths.instanceDirectory);
|
|
37480
38042
|
const serialized = `${JSON.stringify(event)}
|
|
37481
38043
|
`;
|
|
@@ -37760,6 +38322,28 @@ function localDiagnostic(message, maxChars) {
|
|
|
37760
38322
|
function safeErrorDetail(error) {
|
|
37761
38323
|
return localDiagnostic(error.message, 2048);
|
|
37762
38324
|
}
|
|
38325
|
+
function providerStatusFields(notice) {
|
|
38326
|
+
if (!notice) return {};
|
|
38327
|
+
return {
|
|
38328
|
+
providerExecutable: notice.executable ?? null,
|
|
38329
|
+
providerVersion: notice.runningVersion,
|
|
38330
|
+
providerLastMeasuredVersion: notice.runningVersion === null ? null : notice.lastMeasuredVersion,
|
|
38331
|
+
providerBundledAgentSdkVersion: notice.bundledAgentSdkVersion ?? null,
|
|
38332
|
+
providerBundledClaudeCodeVersion: notice.bundledClaudeCodeVersion ?? null
|
|
38333
|
+
};
|
|
38334
|
+
}
|
|
38335
|
+
function providerFailureFields(error) {
|
|
38336
|
+
if (!(error instanceof AcpPermissionCanaryError)) {
|
|
38337
|
+
return {
|
|
38338
|
+
lastErrorReasonCode: null,
|
|
38339
|
+
providerMinimumRequiredVersion: null
|
|
38340
|
+
};
|
|
38341
|
+
}
|
|
38342
|
+
return {
|
|
38343
|
+
lastErrorReasonCode: error.reasonCode,
|
|
38344
|
+
providerMinimumRequiredVersion: error.minimumRequiredVersion
|
|
38345
|
+
};
|
|
38346
|
+
}
|
|
37763
38347
|
var TAIL_SERIALIZED_BUDGET_BYTES = 3e3;
|
|
37764
38348
|
function fitWorkerStderrTailForLog(tail) {
|
|
37765
38349
|
let fitted = tail.trim();
|
|
@@ -37800,8 +38384,13 @@ async function runListenerSupervisor(options) {
|
|
|
37800
38384
|
lastSignalId: null,
|
|
37801
38385
|
lastErrorCode: null,
|
|
37802
38386
|
lastErrorDetail: null,
|
|
38387
|
+
lastErrorReasonCode: null,
|
|
38388
|
+
providerExecutable: null,
|
|
37803
38389
|
providerVersion: null,
|
|
37804
38390
|
providerLastMeasuredVersion: null,
|
|
38391
|
+
providerBundledAgentSdkVersion: null,
|
|
38392
|
+
providerBundledClaudeCodeVersion: null,
|
|
38393
|
+
providerMinimumRequiredVersion: null,
|
|
37805
38394
|
lastWorkerStderrTail: null,
|
|
37806
38395
|
deliveryMode: null,
|
|
37807
38396
|
pendingDeliveryCount: null,
|
|
@@ -37813,14 +38402,27 @@ async function runListenerSupervisor(options) {
|
|
|
37813
38402
|
deferOverChars: options.deferOverChars ?? null,
|
|
37814
38403
|
pendingForMainCount: 0,
|
|
37815
38404
|
droppedForMainCount: 0,
|
|
38405
|
+
readHealth: emptyListenerReadHealth(),
|
|
38406
|
+
connectionsOpened: 0,
|
|
38407
|
+
connectionReuseRatio: 0,
|
|
37816
38408
|
logPath: options.paths.logPath
|
|
37817
38409
|
};
|
|
37818
38410
|
let writes = Promise.resolve();
|
|
37819
38411
|
const chain = (work) => {
|
|
37820
38412
|
writes = writes.then(work).catch(() => void 0);
|
|
37821
38413
|
};
|
|
38414
|
+
const statusSnapshot = () => {
|
|
38415
|
+
const metrics = options.getConnectionMetrics?.();
|
|
38416
|
+
return {
|
|
38417
|
+
...structuredClone(status),
|
|
38418
|
+
...metrics ? {
|
|
38419
|
+
connectionsOpened: metrics.connectionsOpened,
|
|
38420
|
+
connectionReuseRatio: metrics.connectionReuseRatio
|
|
38421
|
+
} : {}
|
|
38422
|
+
};
|
|
38423
|
+
};
|
|
37822
38424
|
const persist = () => {
|
|
37823
|
-
const snapshot =
|
|
38425
|
+
const snapshot = statusSnapshot();
|
|
37824
38426
|
chain(() => writeListenerStatus(options.paths, snapshot));
|
|
37825
38427
|
};
|
|
37826
38428
|
const log = (event) => {
|
|
@@ -37838,7 +38440,7 @@ async function runListenerSupervisor(options) {
|
|
|
37838
38440
|
const prepare = options.prepare;
|
|
37839
38441
|
const control = await startListenerControlServer({
|
|
37840
38442
|
paths: options.paths,
|
|
37841
|
-
status:
|
|
38443
|
+
status: statusSnapshot,
|
|
37842
38444
|
stop: () => {
|
|
37843
38445
|
if (status.state === "stopped" || status.state === "failed") return;
|
|
37844
38446
|
transition("stopping");
|
|
@@ -37876,9 +38478,16 @@ async function runListenerSupervisor(options) {
|
|
|
37876
38478
|
readyAt: event.ts,
|
|
37877
38479
|
lastErrorCode: null,
|
|
37878
38480
|
lastErrorDetail: null,
|
|
38481
|
+
lastErrorReasonCode: null,
|
|
37879
38482
|
lastWorkerStderrTail: null,
|
|
37880
|
-
|
|
37881
|
-
|
|
38483
|
+
providerMinimumRequiredVersion: null,
|
|
38484
|
+
...providerStatusFields(versionNotice),
|
|
38485
|
+
...event.cadenceMs === void 0 ? {} : {
|
|
38486
|
+
readHealth: recordListenerClaimCadence(
|
|
38487
|
+
status.readHealth ?? emptyListenerReadHealth(),
|
|
38488
|
+
event.cadenceMs
|
|
38489
|
+
)
|
|
38490
|
+
}
|
|
37882
38491
|
});
|
|
37883
38492
|
log({ ts: event.ts, event: "listener_ready" });
|
|
37884
38493
|
return;
|
|
@@ -37920,14 +38529,54 @@ async function runListenerSupervisor(options) {
|
|
|
37920
38529
|
return;
|
|
37921
38530
|
}
|
|
37922
38531
|
if (event.type === "read_retry") {
|
|
38532
|
+
status = {
|
|
38533
|
+
...status,
|
|
38534
|
+
readHealth: recordListenerReadRetry(
|
|
38535
|
+
status.readHealth ?? emptyListenerReadHealth(),
|
|
38536
|
+
{
|
|
38537
|
+
ts: event.ts,
|
|
38538
|
+
episodeStartedAt: event.episodeStartedAt,
|
|
38539
|
+
episodeAttempt: event.episodeAttempt,
|
|
38540
|
+
failure: event.failure
|
|
38541
|
+
}
|
|
38542
|
+
),
|
|
38543
|
+
updatedAt: event.ts
|
|
38544
|
+
};
|
|
38545
|
+
persist();
|
|
37923
38546
|
log({
|
|
37924
38547
|
ts: event.ts,
|
|
37925
38548
|
event: "listener_read_retry",
|
|
37926
38549
|
attempt: event.attempt,
|
|
38550
|
+
episode_attempt: event.episodeAttempt,
|
|
38551
|
+
reason_code: event.failure.code,
|
|
38552
|
+
...event.failure.httpStatus === null ? {} : { http_status: event.failure.httpStatus },
|
|
38553
|
+
...event.failure.errorConstructor === null ? {} : { error_constructor: event.failure.errorConstructor },
|
|
37927
38554
|
delay_ms: event.delayMs
|
|
37928
38555
|
});
|
|
37929
38556
|
return;
|
|
37930
38557
|
}
|
|
38558
|
+
if (event.type === "read_recovered") {
|
|
38559
|
+
status = {
|
|
38560
|
+
...status,
|
|
38561
|
+
readHealth: recordListenerReadRecovery(
|
|
38562
|
+
status.readHealth ?? emptyListenerReadHealth(),
|
|
38563
|
+
{
|
|
38564
|
+
startedAt: event.startedAt,
|
|
38565
|
+
attempts: event.attempts,
|
|
38566
|
+
durationMs: event.durationMs
|
|
38567
|
+
}
|
|
38568
|
+
),
|
|
38569
|
+
updatedAt: event.ts
|
|
38570
|
+
};
|
|
38571
|
+
persist();
|
|
38572
|
+
log({
|
|
38573
|
+
ts: event.ts,
|
|
38574
|
+
event: "listener_read_recovered",
|
|
38575
|
+
attempts: event.attempts,
|
|
38576
|
+
duration_ms: event.durationMs
|
|
38577
|
+
});
|
|
38578
|
+
return;
|
|
38579
|
+
}
|
|
37931
38580
|
if (event.type === "malformed_row") {
|
|
37932
38581
|
log({
|
|
37933
38582
|
ts: event.ts,
|
|
@@ -37964,6 +38613,10 @@ async function runListenerSupervisor(options) {
|
|
|
37964
38613
|
if (event.type === "delivery_claim") {
|
|
37965
38614
|
status = {
|
|
37966
38615
|
...status,
|
|
38616
|
+
readHealth: recordListenerClaim(
|
|
38617
|
+
status.readHealth ?? emptyListenerReadHealth(),
|
|
38618
|
+
event.ts
|
|
38619
|
+
),
|
|
37967
38620
|
pendingDeliveryCount: event.pendingDeliveryCount,
|
|
37968
38621
|
lastClaimAt: event.ts,
|
|
37969
38622
|
updatedAt: event.ts
|
|
@@ -38088,9 +38741,9 @@ async function runListenerSupervisor(options) {
|
|
|
38088
38741
|
readyAt: null,
|
|
38089
38742
|
lastErrorCode: restartCode,
|
|
38090
38743
|
lastErrorDetail: safeErrorDetail(stop.error),
|
|
38744
|
+
...providerFailureFields(stop.error),
|
|
38091
38745
|
lastWorkerStderrTail: restartStderrTail,
|
|
38092
|
-
|
|
38093
|
-
providerLastMeasuredVersion: null
|
|
38746
|
+
...providerStatusFields(options.getProviderVersionNotice?.() ?? null)
|
|
38094
38747
|
});
|
|
38095
38748
|
await restartSleep(delayMs, controller.signal);
|
|
38096
38749
|
if (controller.signal.aborted) {
|
|
@@ -38104,7 +38757,9 @@ async function runListenerSupervisor(options) {
|
|
|
38104
38757
|
stoppedAt,
|
|
38105
38758
|
lastErrorCode: null,
|
|
38106
38759
|
lastErrorDetail: null,
|
|
38107
|
-
|
|
38760
|
+
lastErrorReasonCode: null,
|
|
38761
|
+
lastWorkerStderrTail: null,
|
|
38762
|
+
providerMinimumRequiredVersion: null
|
|
38108
38763
|
});
|
|
38109
38764
|
log({ ts: stoppedAt, event: "listener_stopped" });
|
|
38110
38765
|
} else {
|
|
@@ -38114,6 +38769,8 @@ async function runListenerSupervisor(options) {
|
|
|
38114
38769
|
stoppedAt,
|
|
38115
38770
|
lastErrorCode: code,
|
|
38116
38771
|
lastErrorDetail: safeErrorDetail(stop.error),
|
|
38772
|
+
...providerFailureFields(stop.error),
|
|
38773
|
+
...providerStatusFields(options.getProviderVersionNotice?.() ?? null),
|
|
38117
38774
|
lastWorkerStderrTail: failedStderrTail
|
|
38118
38775
|
});
|
|
38119
38776
|
log({
|
|
@@ -38138,6 +38795,10 @@ async function runListenerSupervisor(options) {
|
|
|
38138
38795
|
lastErrorDetail: safeErrorDetail(
|
|
38139
38796
|
error instanceof Error ? error : new Error(String(error))
|
|
38140
38797
|
),
|
|
38798
|
+
...providerFailureFields(
|
|
38799
|
+
error instanceof Error ? error : new Error(String(error))
|
|
38800
|
+
),
|
|
38801
|
+
...providerStatusFields(options.getProviderVersionNotice?.() ?? null),
|
|
38141
38802
|
lastWorkerStderrTail: failedStderrTail
|
|
38142
38803
|
});
|
|
38143
38804
|
log({
|
|
@@ -38150,7 +38811,7 @@ async function runListenerSupervisor(options) {
|
|
|
38150
38811
|
await writes.catch(() => void 0);
|
|
38151
38812
|
await control.close().catch(() => void 0);
|
|
38152
38813
|
}
|
|
38153
|
-
return
|
|
38814
|
+
return statusSnapshot();
|
|
38154
38815
|
}
|
|
38155
38816
|
async function effectiveListenerStatus(paths) {
|
|
38156
38817
|
try {
|
|
@@ -38383,7 +39044,7 @@ function assertPlainObject(input, allowedKeys, requiredKeys, errMessage = "deliv
|
|
|
38383
39044
|
throw new Error(errMessage);
|
|
38384
39045
|
}
|
|
38385
39046
|
}
|
|
38386
|
-
function parseJournalRecord(raw, expectedWorkspaceId, expectedPrincipalId) {
|
|
39047
|
+
function parseJournalRecord(raw, expectedWorkspaceId, expectedPrincipalId, rejectUnknownKeys = false) {
|
|
38387
39048
|
if (Buffer.byteLength(raw, "utf8") > MAX_JOURNAL_BYTES) {
|
|
38388
39049
|
throw new Error("stored delivery journal is malformed");
|
|
38389
39050
|
}
|
|
@@ -38404,7 +39065,7 @@ function parseJournalRecord(raw, expectedWorkspaceId, expectedPrincipalId) {
|
|
|
38404
39065
|
throw new Error("stored delivery journal is malformed");
|
|
38405
39066
|
}
|
|
38406
39067
|
const ownProps = Object.getOwnPropertyNames(value);
|
|
38407
|
-
if (
|
|
39068
|
+
if ([...ALLOWED_TOP_KEYS].some((key2) => !ownProps.includes(key2)) || rejectUnknownKeys && !ownProps.every((key2) => ALLOWED_TOP_KEYS.has(key2))) {
|
|
38408
39069
|
throw new Error("stored delivery journal is malformed");
|
|
38409
39070
|
}
|
|
38410
39071
|
for (const prop of ownProps) {
|
|
@@ -38438,8 +39099,16 @@ function parseJournalRecord(raw, expectedWorkspaceId, expectedPrincipalId) {
|
|
|
38438
39099
|
if (!isValidIsoTimestamp(row.updatedAt)) {
|
|
38439
39100
|
throw new Error("stored delivery journal is malformed");
|
|
38440
39101
|
}
|
|
39102
|
+
const base = {
|
|
39103
|
+
version: 1,
|
|
39104
|
+
workspaceId: row.workspaceId,
|
|
39105
|
+
principalId: row.principalId,
|
|
39106
|
+
listenerInstanceId: row.listenerInstanceId,
|
|
39107
|
+
nextClaimOrdinal: row.nextClaimOrdinal,
|
|
39108
|
+
updatedAt: row.updatedAt
|
|
39109
|
+
};
|
|
38441
39110
|
if (row.active === null) {
|
|
38442
|
-
return
|
|
39111
|
+
return { ...base, active: null };
|
|
38443
39112
|
}
|
|
38444
39113
|
if (typeof row.active !== "object" || row.active === null || Array.isArray(row.active)) {
|
|
38445
39114
|
throw new Error("stored delivery journal is malformed");
|
|
@@ -38452,8 +39121,10 @@ function parseJournalRecord(raw, expectedWorkspaceId, expectedPrincipalId) {
|
|
|
38452
39121
|
throw new Error("stored delivery journal is malformed");
|
|
38453
39122
|
}
|
|
38454
39123
|
const activeProps = Object.getOwnPropertyNames(row.active);
|
|
38455
|
-
const
|
|
38456
|
-
|
|
39124
|
+
const requiredActiveKeys = [...ALLOWED_ACTIVE_KEYS].filter(
|
|
39125
|
+
(key2) => key2 !== "signalFingerprint"
|
|
39126
|
+
);
|
|
39127
|
+
if (requiredActiveKeys.some((key2) => !activeProps.includes(key2)) || rejectUnknownKeys && !activeProps.every((key2) => ALLOWED_ACTIVE_KEYS.has(key2))) {
|
|
38457
39128
|
throw new Error("stored delivery journal is malformed");
|
|
38458
39129
|
}
|
|
38459
39130
|
for (const prop of activeProps) {
|
|
@@ -38521,7 +39192,7 @@ function parseJournalRecord(raw, expectedWorkspaceId, expectedPrincipalId) {
|
|
|
38521
39192
|
throw new Error("stored delivery journal is malformed");
|
|
38522
39193
|
}
|
|
38523
39194
|
const ackProps = Object.getOwnPropertyNames(active.ack);
|
|
38524
|
-
if (
|
|
39195
|
+
if ([...ALLOWED_ACK_KEYS].some((key2) => !ackProps.includes(key2)) || rejectUnknownKeys && !ackProps.every((key2) => ALLOWED_ACK_KEYS.has(key2))) {
|
|
38525
39196
|
throw new Error("stored delivery journal is malformed");
|
|
38526
39197
|
}
|
|
38527
39198
|
for (const prop of ackProps) {
|
|
@@ -38530,32 +39201,52 @@ function parseJournalRecord(raw, expectedWorkspaceId, expectedPrincipalId) {
|
|
|
38530
39201
|
throw new Error("stored delivery journal is malformed");
|
|
38531
39202
|
}
|
|
38532
39203
|
}
|
|
38533
|
-
const
|
|
38534
|
-
if (typeof
|
|
39204
|
+
const ack2 = active.ack;
|
|
39205
|
+
if (typeof ack2.commandId !== "string" || !COMMAND_ID_RE3.test(ack2.commandId)) {
|
|
38535
39206
|
throw new Error("stored delivery journal is malformed");
|
|
38536
39207
|
}
|
|
38537
39208
|
const expectedAckCmdId = ackCommandId(active.leaseId);
|
|
38538
|
-
if (
|
|
39209
|
+
if (ack2.commandId !== expectedAckCmdId) {
|
|
38539
39210
|
throw new Error("stored delivery journal is malformed");
|
|
38540
39211
|
}
|
|
38541
|
-
if (typeof
|
|
39212
|
+
if (typeof ack2.outcome !== "string" || !ALLOWED_OUTCOMES.has(ack2.outcome)) {
|
|
38542
39213
|
throw new Error("stored delivery journal is malformed");
|
|
38543
39214
|
}
|
|
38544
|
-
if (
|
|
38545
|
-
if (typeof
|
|
39215
|
+
if (ack2.outcome === "failed_terminal") {
|
|
39216
|
+
if (typeof ack2.lastErrorCode !== "string" || !ALLOWED_ERROR_CODES.has(ack2.lastErrorCode)) {
|
|
38546
39217
|
throw new Error("stored delivery journal is malformed");
|
|
38547
39218
|
}
|
|
38548
39219
|
} else {
|
|
38549
|
-
if (
|
|
39220
|
+
if (ack2.lastErrorCode !== null) {
|
|
38550
39221
|
throw new Error("stored delivery journal is malformed");
|
|
38551
39222
|
}
|
|
38552
39223
|
}
|
|
38553
|
-
if (!isValidIsoTimestamp(
|
|
39224
|
+
if (!isValidIsoTimestamp(ack2.preparedAt)) {
|
|
38554
39225
|
throw new Error("stored delivery journal is malformed");
|
|
38555
39226
|
}
|
|
38556
39227
|
}
|
|
38557
39228
|
}
|
|
38558
|
-
|
|
39229
|
+
const ack = active.ack;
|
|
39230
|
+
return {
|
|
39231
|
+
...base,
|
|
39232
|
+
active: {
|
|
39233
|
+
phase: active.phase,
|
|
39234
|
+
claimOrdinal: active.claimOrdinal,
|
|
39235
|
+
claimCommandId: active.claimCommandId,
|
|
39236
|
+
claimCreatedAt: active.claimCreatedAt,
|
|
39237
|
+
claimLastAttemptAt: active.claimLastAttemptAt,
|
|
39238
|
+
signalId: active.signalId,
|
|
39239
|
+
leaseId: active.leaseId,
|
|
39240
|
+
leasedUntil: active.leasedUntil,
|
|
39241
|
+
...active.signalFingerprint === void 0 ? {} : { signalFingerprint: active.signalFingerprint },
|
|
39242
|
+
ack: ack === null ? null : {
|
|
39243
|
+
commandId: ack.commandId,
|
|
39244
|
+
outcome: ack.outcome,
|
|
39245
|
+
lastErrorCode: ack.lastErrorCode,
|
|
39246
|
+
preparedAt: ack.preparedAt
|
|
39247
|
+
}
|
|
39248
|
+
}
|
|
39249
|
+
};
|
|
38559
39250
|
}
|
|
38560
39251
|
var FileListenerDeliveryJournal = class {
|
|
38561
39252
|
instanceDirectory;
|
|
@@ -38619,7 +39310,8 @@ var FileListenerDeliveryJournal = class {
|
|
|
38619
39310
|
parseJournalRecord(
|
|
38620
39311
|
serialized,
|
|
38621
39312
|
this.options.workspaceId,
|
|
38622
|
-
this.options.principalId
|
|
39313
|
+
this.options.principalId,
|
|
39314
|
+
true
|
|
38623
39315
|
);
|
|
38624
39316
|
await writeSecureJsonFile(this.journalPath, serialized);
|
|
38625
39317
|
}
|
|
@@ -38869,7 +39561,8 @@ async function openListenerDeliveryJournal(options) {
|
|
|
38869
39561
|
parseJournalRecord(
|
|
38870
39562
|
serialized2,
|
|
38871
39563
|
workspaceIdSnapshot,
|
|
38872
|
-
principalIdSnapshot
|
|
39564
|
+
principalIdSnapshot,
|
|
39565
|
+
true
|
|
38873
39566
|
);
|
|
38874
39567
|
await writeSecureJsonFile(journal.journalPath, serialized2);
|
|
38875
39568
|
return {
|
|
@@ -38903,7 +39596,8 @@ async function openListenerDeliveryJournal(options) {
|
|
|
38903
39596
|
parseJournalRecord(
|
|
38904
39597
|
serialized,
|
|
38905
39598
|
workspaceIdSnapshot,
|
|
38906
|
-
principalIdSnapshot
|
|
39599
|
+
principalIdSnapshot,
|
|
39600
|
+
true
|
|
38907
39601
|
);
|
|
38908
39602
|
await writeSecureJsonFile(journal.journalPath, serialized);
|
|
38909
39603
|
return {
|
|
@@ -39059,7 +39753,7 @@ function parseState(raw) {
|
|
|
39059
39753
|
}
|
|
39060
39754
|
const row = value;
|
|
39061
39755
|
const topicVersions = row.topicVersions;
|
|
39062
|
-
if (
|
|
39756
|
+
if (row.version !== 1 || typeof row.principalId !== "string" || !UUID_RE20.test(row.principalId) || !topicVersions || typeof topicVersions !== "object" || Array.isArray(topicVersions) || Object.keys(topicVersions).length > MAX_BRAIN_DIGEST_TOPICS) {
|
|
39063
39757
|
throw new Error("stored brain digest state is malformed");
|
|
39064
39758
|
}
|
|
39065
39759
|
for (const [topic, version3] of Object.entries(topicVersions)) {
|
|
@@ -39171,7 +39865,26 @@ function exactKeys2(row, keys) {
|
|
|
39171
39865
|
const expected = new Set(keys);
|
|
39172
39866
|
return Object.keys(row).length === expected.size && Object.keys(row).every((key2) => expected.has(key2));
|
|
39173
39867
|
}
|
|
39174
|
-
function
|
|
39868
|
+
function hasRequiredKeys(row, keys) {
|
|
39869
|
+
return keys.every((key2) => Object.hasOwn(row, key2));
|
|
39870
|
+
}
|
|
39871
|
+
var LISTENER_CREDENTIAL_KEYS = [
|
|
39872
|
+
"version",
|
|
39873
|
+
"profileId",
|
|
39874
|
+
"targetUrl",
|
|
39875
|
+
"anonKey",
|
|
39876
|
+
"workspaceId",
|
|
39877
|
+
"principalId",
|
|
39878
|
+
"credential",
|
|
39879
|
+
"updatedAt"
|
|
39880
|
+
];
|
|
39881
|
+
var HOOK_SURFACE_KEYS = /* @__PURE__ */ new Set([
|
|
39882
|
+
"version",
|
|
39883
|
+
"surfacedSignalIds",
|
|
39884
|
+
"reportedDroppedCount",
|
|
39885
|
+
"credentialFailureReported"
|
|
39886
|
+
]);
|
|
39887
|
+
function parseListenerCredential(raw, rejectUnknownKeys = false) {
|
|
39175
39888
|
let value;
|
|
39176
39889
|
try {
|
|
39177
39890
|
value = JSON.parse(raw);
|
|
@@ -39182,16 +39895,7 @@ function parseListenerCredential(raw) {
|
|
|
39182
39895
|
throw new Error("stored listener hook credential is malformed");
|
|
39183
39896
|
}
|
|
39184
39897
|
const row = value;
|
|
39185
|
-
if (!exactKeys2(row, [
|
|
39186
|
-
"version",
|
|
39187
|
-
"profileId",
|
|
39188
|
-
"targetUrl",
|
|
39189
|
-
"anonKey",
|
|
39190
|
-
"workspaceId",
|
|
39191
|
-
"principalId",
|
|
39192
|
-
"credential",
|
|
39193
|
-
"updatedAt"
|
|
39194
|
-
]) || row.version !== 1 || typeof row.profileId !== "string" || !/^[0-9a-f]{24}$/.test(row.profileId) || typeof row.targetUrl !== "string" || typeof row.anonKey !== "string" || row.anonKey.length < 1 || row.anonKey.length > 4096 || typeof row.workspaceId !== "string" || !UUID_RE21.test(row.workspaceId) || typeof row.principalId !== "string" || !UUID_RE21.test(row.principalId) || typeof row.credential !== "string" || !TOKEN_RE.test(row.credential) || typeof row.updatedAt !== "string" || !Number.isFinite(Date.parse(row.updatedAt))) {
|
|
39898
|
+
if (!(rejectUnknownKeys ? exactKeys2(row, LISTENER_CREDENTIAL_KEYS) : hasRequiredKeys(row, LISTENER_CREDENTIAL_KEYS)) || row.version !== 1 || typeof row.profileId !== "string" || !/^[0-9a-f]{24}$/.test(row.profileId) || typeof row.targetUrl !== "string" || typeof row.anonKey !== "string" || row.anonKey.length < 1 || row.anonKey.length > 4096 || typeof row.workspaceId !== "string" || !UUID_RE21.test(row.workspaceId) || typeof row.principalId !== "string" || !UUID_RE21.test(row.principalId) || typeof row.credential !== "string" || !TOKEN_RE.test(row.credential) || typeof row.updatedAt !== "string" || !Number.isFinite(Date.parse(row.updatedAt))) {
|
|
39195
39899
|
throw new Error("stored listener hook credential is malformed");
|
|
39196
39900
|
}
|
|
39197
39901
|
const target2 = cloudTarget(row.targetUrl, row.anonKey);
|
|
@@ -39222,7 +39926,7 @@ async function writeListenerCredentialState(instanceDirectory, input) {
|
|
|
39222
39926
|
principalId: input.principalId,
|
|
39223
39927
|
credential: input.credential,
|
|
39224
39928
|
updatedAt: new Date(input.now ?? Date.now()).toISOString()
|
|
39225
|
-
}));
|
|
39929
|
+
}), true);
|
|
39226
39930
|
await writeSecureJsonFile(
|
|
39227
39931
|
(0, import_node_path20.join)(instanceDirectory, LISTENER_CREDENTIAL_FILE),
|
|
39228
39932
|
JSON.stringify(record)
|
|
@@ -39238,7 +39942,7 @@ async function readListenerCredentialState(instanceDirectory) {
|
|
|
39238
39942
|
);
|
|
39239
39943
|
return raw === null ? null : parseListenerCredential(raw);
|
|
39240
39944
|
}
|
|
39241
|
-
function parseSurface(raw) {
|
|
39945
|
+
function parseSurface(raw, rejectUnknownKeys = false) {
|
|
39242
39946
|
let value;
|
|
39243
39947
|
try {
|
|
39244
39948
|
value = JSON.parse(raw);
|
|
@@ -39249,9 +39953,7 @@ function parseSurface(raw) {
|
|
|
39249
39953
|
throw new Error("stored listener hook surface state is malformed");
|
|
39250
39954
|
}
|
|
39251
39955
|
const row = value;
|
|
39252
|
-
if (Object.keys(row).some(
|
|
39253
|
-
(key2) => key2 !== "version" && key2 !== "surfacedSignalIds" && key2 !== "reportedDroppedCount" && key2 !== "credentialFailureReported"
|
|
39254
|
-
) || row.version !== 1 || !Array.isArray(row.surfacedSignalIds) || row.surfacedSignalIds.length > HOOK_SURFACED_IDS_MAX || row.surfacedSignalIds.some((id) => typeof id !== "string" || !UUID_RE21.test(id)) || !(row.reportedDroppedCount === void 0 || typeof row.reportedDroppedCount === "number" && Number.isSafeInteger(row.reportedDroppedCount) && row.reportedDroppedCount >= 0) || !(row.credentialFailureReported === void 0 || typeof row.credentialFailureReported === "boolean")) {
|
|
39956
|
+
if (rejectUnknownKeys && Object.keys(row).some((key2) => !HOOK_SURFACE_KEYS.has(key2)) || row.version !== 1 || !Array.isArray(row.surfacedSignalIds) || row.surfacedSignalIds.length > HOOK_SURFACED_IDS_MAX || row.surfacedSignalIds.some((id) => typeof id !== "string" || !UUID_RE21.test(id)) || !(row.reportedDroppedCount === void 0 || typeof row.reportedDroppedCount === "number" && Number.isSafeInteger(row.reportedDroppedCount) && row.reportedDroppedCount >= 0) || !(row.credentialFailureReported === void 0 || typeof row.credentialFailureReported === "boolean")) {
|
|
39255
39957
|
throw new Error("stored listener hook surface state is malformed");
|
|
39256
39958
|
}
|
|
39257
39959
|
const ids = row.surfacedSignalIds.map((id) => String(id).toLowerCase());
|
|
@@ -39345,7 +40047,7 @@ var FileHookSurfaceStore = class {
|
|
|
39345
40047
|
surfacedSignalIds: [...seen].slice(-HOOK_SURFACED_IDS_MAX),
|
|
39346
40048
|
reportedDroppedCount: options.droppedCount ?? state.reportedDroppedCount,
|
|
39347
40049
|
credentialFailureReported: options.credentialFailureReported ?? state.credentialFailureReported
|
|
39348
|
-
})))
|
|
40050
|
+
}), true))
|
|
39349
40051
|
);
|
|
39350
40052
|
}, { timeoutMs: HOOK_LOCK_TIMEOUT_MS });
|
|
39351
40053
|
}
|
|
@@ -39368,7 +40070,7 @@ function parseGlobalState(raw) {
|
|
|
39368
40070
|
throw new Error("stored hook cooldown state is malformed");
|
|
39369
40071
|
}
|
|
39370
40072
|
const row = value;
|
|
39371
|
-
if (!
|
|
40073
|
+
if (!hasRequiredKeys(row, ["version", "lastCheckAt"]) || row.version !== 1 || typeof row.lastCheckAt !== "number" || !Number.isSafeInteger(row.lastCheckAt) || row.lastCheckAt < 0) {
|
|
39372
40074
|
throw new Error("stored hook cooldown state is malformed");
|
|
39373
40075
|
}
|
|
39374
40076
|
return { version: 1, lastCheckAt: row.lastCheckAt };
|
|
@@ -39995,6 +40697,470 @@ function renderListenerAttendanceCanary(result, workspaceId2, principalId) {
|
|
|
39995
40697
|
return lines.join("\n");
|
|
39996
40698
|
}
|
|
39997
40699
|
|
|
40700
|
+
// src/listener/activity.ts
|
|
40701
|
+
var import_node_crypto20 = require("node:crypto");
|
|
40702
|
+
var ACTIVITY_FRAME_INTERVAL_MS = 750;
|
|
40703
|
+
var ACTIVITY_HEARTBEAT_MS = 15e3;
|
|
40704
|
+
var ACTIVITY_TOOL_TITLE_MAX = 160;
|
|
40705
|
+
var ACTIVITY_REQUEST_TIMEOUT_MS = 5e3;
|
|
40706
|
+
var SYSTEM_CLOCK = {
|
|
40707
|
+
now: Date.now,
|
|
40708
|
+
setTimer: (callback, delayMs) => setTimeout(callback, delayMs),
|
|
40709
|
+
clearTimer: (timer2) => clearTimeout(timer2)
|
|
40710
|
+
};
|
|
40711
|
+
var TERMINAL_TOOL_STATUSES = /* @__PURE__ */ new Set([
|
|
40712
|
+
"cancelled",
|
|
40713
|
+
"completed",
|
|
40714
|
+
"done",
|
|
40715
|
+
"failed",
|
|
40716
|
+
"rejected"
|
|
40717
|
+
]);
|
|
40718
|
+
var AgentActivityEndpointTransport = class {
|
|
40719
|
+
constructor(target2, credentialSession, fetcher) {
|
|
40720
|
+
this.target = target2;
|
|
40721
|
+
this.credentialSession = credentialSession;
|
|
40722
|
+
this.fetcher = fetcher ?? fetch;
|
|
40723
|
+
}
|
|
40724
|
+
target;
|
|
40725
|
+
credentialSession;
|
|
40726
|
+
fetcher;
|
|
40727
|
+
async publish(frame) {
|
|
40728
|
+
const credential = await this.credentialSession.bearer();
|
|
40729
|
+
const response = await this.fetcher(
|
|
40730
|
+
`${this.target.url}/functions/v1/activity`,
|
|
40731
|
+
{
|
|
40732
|
+
method: "POST",
|
|
40733
|
+
headers: {
|
|
40734
|
+
authorization: `Bearer ${credential}`,
|
|
40735
|
+
apikey: this.target.anonKey,
|
|
40736
|
+
"content-type": "application/json"
|
|
40737
|
+
},
|
|
40738
|
+
body: JSON.stringify({
|
|
40739
|
+
version: frame.version,
|
|
40740
|
+
workspace_id: frame.workspaceId,
|
|
40741
|
+
stream_id: frame.streamId,
|
|
40742
|
+
sequence: frame.sequence,
|
|
40743
|
+
phase: frame.phase,
|
|
40744
|
+
signal_id: frame.signalId,
|
|
40745
|
+
tool_title: frame.toolTitle,
|
|
40746
|
+
elapsed_ms: frame.elapsedMs
|
|
40747
|
+
}),
|
|
40748
|
+
signal: AbortSignal.timeout(ACTIVITY_REQUEST_TIMEOUT_MS)
|
|
40749
|
+
}
|
|
40750
|
+
);
|
|
40751
|
+
await response.body?.cancel();
|
|
40752
|
+
if (!response.ok) {
|
|
40753
|
+
throw new Error(`activity publish failed (${response.status})`);
|
|
40754
|
+
}
|
|
40755
|
+
}
|
|
40756
|
+
};
|
|
40757
|
+
var ListenerActivityController = class {
|
|
40758
|
+
constructor(options) {
|
|
40759
|
+
this.options = options;
|
|
40760
|
+
this.clock = options.clock ?? SYSTEM_CLOCK;
|
|
40761
|
+
this.streamId = options.streamId ?? (0, import_node_crypto20.randomUUID)();
|
|
40762
|
+
this.events = { update: (update) => this.onSessionUpdate(update) };
|
|
40763
|
+
}
|
|
40764
|
+
options;
|
|
40765
|
+
events;
|
|
40766
|
+
clock;
|
|
40767
|
+
streamId;
|
|
40768
|
+
sequence = 0;
|
|
40769
|
+
phase = "idle";
|
|
40770
|
+
signalId = null;
|
|
40771
|
+
signalStartedAt = null;
|
|
40772
|
+
runningTools = /* @__PURE__ */ new Map();
|
|
40773
|
+
latestToolId = null;
|
|
40774
|
+
dirty = false;
|
|
40775
|
+
sending = false;
|
|
40776
|
+
closed = false;
|
|
40777
|
+
lastSentAt = Number.NEGATIVE_INFINITY;
|
|
40778
|
+
timer = null;
|
|
40779
|
+
heartbeatTimer = null;
|
|
40780
|
+
/** Observe the listener state machine without changing its durable effect path. */
|
|
40781
|
+
onRuntimeEvent(event) {
|
|
40782
|
+
if (event.type === "ready") {
|
|
40783
|
+
this.setIdle();
|
|
40784
|
+
} else if (event.type === "delivery_claim" && event.signalId !== null) {
|
|
40785
|
+
this.beginSignal(event.signalId);
|
|
40786
|
+
} else if (event.type === "routing_decision") {
|
|
40787
|
+
this.beginSignal(event.signalId);
|
|
40788
|
+
} else if (event.type === "effect") {
|
|
40789
|
+
this.setIdle();
|
|
40790
|
+
}
|
|
40791
|
+
}
|
|
40792
|
+
/** Wrap one provider model so prompt and reply phases are visible. */
|
|
40793
|
+
instrumentModel(model) {
|
|
40794
|
+
return {
|
|
40795
|
+
start: async () => await model.start(),
|
|
40796
|
+
prompt: async (signal, mode3, prompt, attempt) => {
|
|
40797
|
+
this.beginSignal(signal.id);
|
|
40798
|
+
this.setPhase("prompting");
|
|
40799
|
+
const result = await model.prompt(signal, mode3, prompt, attempt);
|
|
40800
|
+
this.runningTools.clear();
|
|
40801
|
+
this.latestToolId = null;
|
|
40802
|
+
this.setPhase("replying");
|
|
40803
|
+
return result;
|
|
40804
|
+
},
|
|
40805
|
+
cancel: () => model.cancel(),
|
|
40806
|
+
close: async () => await model.close()
|
|
40807
|
+
};
|
|
40808
|
+
}
|
|
40809
|
+
/** Stop pending local timers; a missing next frame becomes stale in the panel. */
|
|
40810
|
+
close() {
|
|
40811
|
+
this.closed = true;
|
|
40812
|
+
if (this.timer !== null) this.clock.clearTimer(this.timer);
|
|
40813
|
+
if (this.heartbeatTimer !== null) this.clock.clearTimer(this.heartbeatTimer);
|
|
40814
|
+
this.timer = null;
|
|
40815
|
+
this.heartbeatTimer = null;
|
|
40816
|
+
}
|
|
40817
|
+
onSessionUpdate(update) {
|
|
40818
|
+
if (this.signalId === null) return;
|
|
40819
|
+
if (update.kind !== "tool_call" && update.kind !== "tool_call_update") return;
|
|
40820
|
+
const id = update.toolCallId;
|
|
40821
|
+
if (!id) return;
|
|
40822
|
+
const status = update.status?.toLowerCase();
|
|
40823
|
+
if (status && TERMINAL_TOOL_STATUSES.has(status)) {
|
|
40824
|
+
this.runningTools.delete(id);
|
|
40825
|
+
if (this.latestToolId === id) {
|
|
40826
|
+
this.latestToolId = [...this.runningTools.keys()].at(-1) ?? null;
|
|
40827
|
+
}
|
|
40828
|
+
this.setPhase(this.runningTools.size > 0 ? "tool-running" : "prompting");
|
|
40829
|
+
return;
|
|
40830
|
+
}
|
|
40831
|
+
const sanitizedTitle = update.title === void 0 ? this.runningTools.get(id) : sanitizeText(update.title).slice(0, ACTIVITY_TOOL_TITLE_MAX);
|
|
40832
|
+
const title = sanitizedTitle && sanitizedTitle.length > 0 ? sanitizedTitle : "Tool";
|
|
40833
|
+
this.runningTools.set(id, title);
|
|
40834
|
+
this.latestToolId = id;
|
|
40835
|
+
this.setPhase("tool-running");
|
|
40836
|
+
}
|
|
40837
|
+
beginSignal(signalId) {
|
|
40838
|
+
if (this.signalId === signalId) return;
|
|
40839
|
+
this.signalId = signalId;
|
|
40840
|
+
this.signalStartedAt = this.clock.now();
|
|
40841
|
+
this.runningTools.clear();
|
|
40842
|
+
this.latestToolId = null;
|
|
40843
|
+
this.setPhase("claimed");
|
|
40844
|
+
}
|
|
40845
|
+
setIdle() {
|
|
40846
|
+
this.signalId = null;
|
|
40847
|
+
this.signalStartedAt = null;
|
|
40848
|
+
this.runningTools.clear();
|
|
40849
|
+
this.latestToolId = null;
|
|
40850
|
+
this.setPhase("idle");
|
|
40851
|
+
}
|
|
40852
|
+
setPhase(phase) {
|
|
40853
|
+
this.phase = phase;
|
|
40854
|
+
if (this.heartbeatTimer !== null) {
|
|
40855
|
+
this.clock.clearTimer(this.heartbeatTimer);
|
|
40856
|
+
this.heartbeatTimer = null;
|
|
40857
|
+
}
|
|
40858
|
+
this.dirty = true;
|
|
40859
|
+
this.schedule();
|
|
40860
|
+
}
|
|
40861
|
+
armHeartbeat() {
|
|
40862
|
+
if (this.closed || this.heartbeatTimer !== null) return;
|
|
40863
|
+
this.heartbeatTimer = this.clock.setTimer(() => {
|
|
40864
|
+
this.heartbeatTimer = null;
|
|
40865
|
+
this.dirty = true;
|
|
40866
|
+
this.schedule();
|
|
40867
|
+
}, ACTIVITY_HEARTBEAT_MS);
|
|
40868
|
+
}
|
|
40869
|
+
schedule() {
|
|
40870
|
+
if (this.closed || this.timer !== null || this.sending) return;
|
|
40871
|
+
const delay2 = Math.max(
|
|
40872
|
+
0,
|
|
40873
|
+
this.lastSentAt + ACTIVITY_FRAME_INTERVAL_MS - this.clock.now()
|
|
40874
|
+
);
|
|
40875
|
+
if (delay2 === 0) {
|
|
40876
|
+
void this.flush();
|
|
40877
|
+
return;
|
|
40878
|
+
}
|
|
40879
|
+
this.timer = this.clock.setTimer(() => {
|
|
40880
|
+
this.timer = null;
|
|
40881
|
+
void this.flush();
|
|
40882
|
+
}, delay2);
|
|
40883
|
+
}
|
|
40884
|
+
async flush() {
|
|
40885
|
+
if (this.closed || this.sending || !this.dirty) return;
|
|
40886
|
+
this.dirty = false;
|
|
40887
|
+
this.sending = true;
|
|
40888
|
+
this.lastSentAt = this.clock.now();
|
|
40889
|
+
const toolTitle = this.latestToolId === null ? null : this.runningTools.get(this.latestToolId) ?? null;
|
|
40890
|
+
const frame = {
|
|
40891
|
+
version: 1,
|
|
40892
|
+
workspaceId: this.options.workspaceId,
|
|
40893
|
+
streamId: this.streamId,
|
|
40894
|
+
sequence: ++this.sequence,
|
|
40895
|
+
phase: this.phase,
|
|
40896
|
+
signalId: this.signalId,
|
|
40897
|
+
toolTitle,
|
|
40898
|
+
elapsedMs: this.signalStartedAt === null ? 0 : Math.max(0, Math.round(this.clock.now() - this.signalStartedAt))
|
|
40899
|
+
};
|
|
40900
|
+
try {
|
|
40901
|
+
await this.options.transport.publish(frame);
|
|
40902
|
+
} catch {
|
|
40903
|
+
} finally {
|
|
40904
|
+
this.sending = false;
|
|
40905
|
+
if (this.dirty) this.schedule();
|
|
40906
|
+
else this.armHeartbeat();
|
|
40907
|
+
}
|
|
40908
|
+
}
|
|
40909
|
+
};
|
|
40910
|
+
|
|
40911
|
+
// src/listener/http-client.ts
|
|
40912
|
+
var import_node_http2 = require("node:http");
|
|
40913
|
+
var import_node_https = require("node:https");
|
|
40914
|
+
var import_node_zlib = require("node:zlib");
|
|
40915
|
+
var LISTENER_HTTP_IDLE_TIMEOUT_MS = 6e4;
|
|
40916
|
+
function responseHeaders(message) {
|
|
40917
|
+
const headers = new Headers();
|
|
40918
|
+
for (let index = 0; index < message.rawHeaders.length; index += 2) {
|
|
40919
|
+
const name = message.rawHeaders[index];
|
|
40920
|
+
const value = message.rawHeaders[index + 1];
|
|
40921
|
+
if (name !== void 0 && value !== void 0) headers.append(name, value);
|
|
40922
|
+
}
|
|
40923
|
+
return headers;
|
|
40924
|
+
}
|
|
40925
|
+
function decodeResponseBody(bytes, headers) {
|
|
40926
|
+
const codings = (headers.get("content-encoding") ?? "").split(",").map((coding) => coding.trim().toLowerCase()).filter((coding) => coding !== "" && coding !== "identity");
|
|
40927
|
+
if (!codings.every((coding) => ["br", "deflate", "gzip", "x-gzip"].includes(coding))) {
|
|
40928
|
+
return Uint8Array.from(bytes).buffer;
|
|
40929
|
+
}
|
|
40930
|
+
let decoded = bytes;
|
|
40931
|
+
for (const coding of codings.reverse()) {
|
|
40932
|
+
if (coding === "br") decoded = (0, import_node_zlib.brotliDecompressSync)(decoded);
|
|
40933
|
+
else if (coding === "deflate") decoded = (0, import_node_zlib.inflateSync)(decoded);
|
|
40934
|
+
else decoded = (0, import_node_zlib.gunzipSync)(decoded);
|
|
40935
|
+
}
|
|
40936
|
+
return Uint8Array.from(decoded).buffer;
|
|
40937
|
+
}
|
|
40938
|
+
var ListenerHttpClient = class {
|
|
40939
|
+
fetch;
|
|
40940
|
+
httpAgent = new import_node_http2.Agent({
|
|
40941
|
+
keepAlive: true,
|
|
40942
|
+
maxSockets: 1,
|
|
40943
|
+
maxFreeSockets: 1,
|
|
40944
|
+
scheduling: "fifo"
|
|
40945
|
+
});
|
|
40946
|
+
httpsAgent = new import_node_https.Agent({
|
|
40947
|
+
keepAlive: true,
|
|
40948
|
+
maxSockets: 1,
|
|
40949
|
+
maxFreeSockets: 1,
|
|
40950
|
+
scheduling: "fifo"
|
|
40951
|
+
});
|
|
40952
|
+
idleTimeoutMs;
|
|
40953
|
+
sockets = /* @__PURE__ */ new WeakSet();
|
|
40954
|
+
requestCount = 0;
|
|
40955
|
+
openedCount = 0;
|
|
40956
|
+
activeRequests = 0;
|
|
40957
|
+
idleTimer = null;
|
|
40958
|
+
closed = false;
|
|
40959
|
+
constructor(options = {}) {
|
|
40960
|
+
const idleTimeoutMs = options.idleTimeoutMs ?? LISTENER_HTTP_IDLE_TIMEOUT_MS;
|
|
40961
|
+
if (!Number.isSafeInteger(idleTimeoutMs) || idleTimeoutMs <= 0) {
|
|
40962
|
+
throw new Error("listener HTTP idle timeout must be a positive integer");
|
|
40963
|
+
}
|
|
40964
|
+
this.idleTimeoutMs = idleTimeoutMs;
|
|
40965
|
+
this.fetch = this.request.bind(this);
|
|
40966
|
+
}
|
|
40967
|
+
/** Snapshot process-local counts without exposing the agents themselves. */
|
|
40968
|
+
metrics() {
|
|
40969
|
+
return {
|
|
40970
|
+
requests: this.requestCount,
|
|
40971
|
+
connectionsOpened: this.openedCount,
|
|
40972
|
+
connectionReuseRatio: this.openedCount === 0 ? 0 : this.requestCount / this.openedCount
|
|
40973
|
+
};
|
|
40974
|
+
}
|
|
40975
|
+
/** Close every idle or active socket when the listener process stops. */
|
|
40976
|
+
close() {
|
|
40977
|
+
if (this.closed) return;
|
|
40978
|
+
this.closed = true;
|
|
40979
|
+
this.clearIdleTimer();
|
|
40980
|
+
this.destroyAgents();
|
|
40981
|
+
}
|
|
40982
|
+
clearIdleTimer() {
|
|
40983
|
+
if (this.idleTimer === null) return;
|
|
40984
|
+
clearTimeout(this.idleTimer);
|
|
40985
|
+
this.idleTimer = null;
|
|
40986
|
+
}
|
|
40987
|
+
destroyAgents() {
|
|
40988
|
+
this.httpAgent.destroy();
|
|
40989
|
+
this.httpsAgent.destroy();
|
|
40990
|
+
}
|
|
40991
|
+
beginRequest() {
|
|
40992
|
+
if (this.closed) throw new Error("listener HTTP client is closed");
|
|
40993
|
+
this.clearIdleTimer();
|
|
40994
|
+
this.requestCount += 1;
|
|
40995
|
+
this.activeRequests += 1;
|
|
40996
|
+
}
|
|
40997
|
+
finishRequest() {
|
|
40998
|
+
this.activeRequests -= 1;
|
|
40999
|
+
if (this.activeRequests !== 0 || this.closed) return;
|
|
41000
|
+
this.idleTimer = setTimeout(() => {
|
|
41001
|
+
this.idleTimer = null;
|
|
41002
|
+
if (this.activeRequests === 0 && !this.closed) this.destroyAgents();
|
|
41003
|
+
}, this.idleTimeoutMs);
|
|
41004
|
+
this.idleTimer.unref?.();
|
|
41005
|
+
}
|
|
41006
|
+
trackSocket(request) {
|
|
41007
|
+
request.once("socket", (socket) => {
|
|
41008
|
+
if (this.sockets.has(socket)) return;
|
|
41009
|
+
this.sockets.add(socket);
|
|
41010
|
+
const opened = () => {
|
|
41011
|
+
this.openedCount += 1;
|
|
41012
|
+
};
|
|
41013
|
+
if (socket.connecting) socket.once("connect", opened);
|
|
41014
|
+
else opened();
|
|
41015
|
+
});
|
|
41016
|
+
}
|
|
41017
|
+
async request(input, init) {
|
|
41018
|
+
const webRequest = new Request(input, init);
|
|
41019
|
+
const body = webRequest.method === "GET" || webRequest.method === "HEAD" ? null : Buffer.from(await webRequest.arrayBuffer());
|
|
41020
|
+
let url = new URL(webRequest.url);
|
|
41021
|
+
let method = webRequest.method;
|
|
41022
|
+
let headers = new Headers(webRequest.headers);
|
|
41023
|
+
let redirected = false;
|
|
41024
|
+
for (let redirects = 0; ; redirects += 1) {
|
|
41025
|
+
const response = await this.sendOnce({
|
|
41026
|
+
url,
|
|
41027
|
+
method,
|
|
41028
|
+
headers,
|
|
41029
|
+
body: method === "GET" || method === "HEAD" ? null : body,
|
|
41030
|
+
signal: webRequest.signal,
|
|
41031
|
+
redirected
|
|
41032
|
+
});
|
|
41033
|
+
const location2 = response.headers.get("location");
|
|
41034
|
+
if (location2 === null || ![301, 302, 303, 307, 308].includes(response.status) || webRequest.redirect === "manual") {
|
|
41035
|
+
return response;
|
|
41036
|
+
}
|
|
41037
|
+
if (webRequest.redirect === "error" || redirects >= 20) {
|
|
41038
|
+
throw new TypeError("fetch failed while following a redirect");
|
|
41039
|
+
}
|
|
41040
|
+
const nextUrl = new URL(location2, url);
|
|
41041
|
+
if (nextUrl.protocol !== "http:" && nextUrl.protocol !== "https:") {
|
|
41042
|
+
throw new TypeError("listener HTTP client accepts only http and https URLs");
|
|
41043
|
+
}
|
|
41044
|
+
const rewriteToGet = response.status === 303 && method !== "HEAD" || (response.status === 301 || response.status === 302) && method === "POST";
|
|
41045
|
+
if (rewriteToGet) {
|
|
41046
|
+
method = "GET";
|
|
41047
|
+
for (const name of [
|
|
41048
|
+
"content-encoding",
|
|
41049
|
+
"content-language",
|
|
41050
|
+
"content-length",
|
|
41051
|
+
"content-location",
|
|
41052
|
+
"content-type",
|
|
41053
|
+
"transfer-encoding"
|
|
41054
|
+
]) {
|
|
41055
|
+
headers.delete(name);
|
|
41056
|
+
}
|
|
41057
|
+
}
|
|
41058
|
+
if (nextUrl.origin !== url.origin) {
|
|
41059
|
+
for (const name of [
|
|
41060
|
+
"apikey",
|
|
41061
|
+
"authorization",
|
|
41062
|
+
"cookie",
|
|
41063
|
+
"host",
|
|
41064
|
+
"proxy-authorization"
|
|
41065
|
+
]) {
|
|
41066
|
+
headers.delete(name);
|
|
41067
|
+
}
|
|
41068
|
+
}
|
|
41069
|
+
url = nextUrl;
|
|
41070
|
+
redirected = true;
|
|
41071
|
+
}
|
|
41072
|
+
}
|
|
41073
|
+
async sendOnce(options) {
|
|
41074
|
+
if (options.url.protocol !== "http:" && options.url.protocol !== "https:") {
|
|
41075
|
+
throw new TypeError("listener HTTP client accepts only http and https URLs");
|
|
41076
|
+
}
|
|
41077
|
+
const headers = Object.fromEntries(options.headers.entries());
|
|
41078
|
+
if (!("accept-encoding" in headers)) headers["accept-encoding"] = "gzip, deflate";
|
|
41079
|
+
if (options.body !== null && !("content-length" in headers) && !("transfer-encoding" in headers)) {
|
|
41080
|
+
headers["content-length"] = String(options.body.byteLength);
|
|
41081
|
+
}
|
|
41082
|
+
this.beginRequest();
|
|
41083
|
+
let finished = false;
|
|
41084
|
+
const finish = () => {
|
|
41085
|
+
if (finished) return;
|
|
41086
|
+
finished = true;
|
|
41087
|
+
this.finishRequest();
|
|
41088
|
+
};
|
|
41089
|
+
return await new Promise((resolve2, reject) => {
|
|
41090
|
+
const send = options.url.protocol === "https:" ? import_node_https.request : import_node_http2.request;
|
|
41091
|
+
const agent = options.url.protocol === "https:" ? this.httpsAgent : this.httpAgent;
|
|
41092
|
+
let request;
|
|
41093
|
+
try {
|
|
41094
|
+
request = send(options.url, {
|
|
41095
|
+
agent,
|
|
41096
|
+
method: options.method,
|
|
41097
|
+
headers,
|
|
41098
|
+
signal: options.signal
|
|
41099
|
+
});
|
|
41100
|
+
} catch (error) {
|
|
41101
|
+
finish();
|
|
41102
|
+
reject(error);
|
|
41103
|
+
return;
|
|
41104
|
+
}
|
|
41105
|
+
this.trackSocket(request);
|
|
41106
|
+
request.once("error", (error) => {
|
|
41107
|
+
finish();
|
|
41108
|
+
reject(error);
|
|
41109
|
+
});
|
|
41110
|
+
request.once("response", (message) => {
|
|
41111
|
+
const chunks = [];
|
|
41112
|
+
let settled = false;
|
|
41113
|
+
const rejectResponse = (error) => {
|
|
41114
|
+
if (settled) return;
|
|
41115
|
+
settled = true;
|
|
41116
|
+
finish();
|
|
41117
|
+
reject(error);
|
|
41118
|
+
};
|
|
41119
|
+
message.on("data", (chunk) => {
|
|
41120
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
41121
|
+
});
|
|
41122
|
+
message.once("aborted", () => {
|
|
41123
|
+
rejectResponse(new TypeError("response body was aborted"));
|
|
41124
|
+
});
|
|
41125
|
+
message.once("error", rejectResponse);
|
|
41126
|
+
message.once("end", () => {
|
|
41127
|
+
if (settled) return;
|
|
41128
|
+
settled = true;
|
|
41129
|
+
finish();
|
|
41130
|
+
const status = message.statusCode ?? 0;
|
|
41131
|
+
const bytes = Buffer.concat(chunks);
|
|
41132
|
+
const webHeaders = responseHeaders(message);
|
|
41133
|
+
const responseBody2 = status === 204 || status === 205 || status === 304 ? null : decodeResponseBody(bytes, webHeaders);
|
|
41134
|
+
try {
|
|
41135
|
+
const response = new Response(responseBody2, {
|
|
41136
|
+
status,
|
|
41137
|
+
statusText: message.statusMessage,
|
|
41138
|
+
headers: webHeaders
|
|
41139
|
+
});
|
|
41140
|
+
Object.defineProperties(response, {
|
|
41141
|
+
redirected: { value: options.redirected },
|
|
41142
|
+
url: { value: options.url.href }
|
|
41143
|
+
});
|
|
41144
|
+
resolve2(response);
|
|
41145
|
+
} catch (error) {
|
|
41146
|
+
reject(error);
|
|
41147
|
+
}
|
|
41148
|
+
});
|
|
41149
|
+
});
|
|
41150
|
+
try {
|
|
41151
|
+
if (options.body !== null && options.body.byteLength > 0) {
|
|
41152
|
+
request.write(options.body);
|
|
41153
|
+
}
|
|
41154
|
+
request.end();
|
|
41155
|
+
} catch (error) {
|
|
41156
|
+
request.destroy();
|
|
41157
|
+
finish();
|
|
41158
|
+
reject(error);
|
|
41159
|
+
}
|
|
41160
|
+
});
|
|
41161
|
+
}
|
|
41162
|
+
};
|
|
41163
|
+
|
|
39998
41164
|
// src/resume.ts
|
|
39999
41165
|
var import_node_child_process8 = require("node:child_process");
|
|
40000
41166
|
function execFileText(file, args) {
|
|
@@ -40410,8 +41576,8 @@ var ACCEPTED_AGENT_CREDENTIAL_MESSAGES = [
|
|
|
40410
41576
|
AGENT_CREDENTIAL_MESSAGE_D088
|
|
40411
41577
|
];
|
|
40412
41578
|
function packageVersion() {
|
|
40413
|
-
if ("0.1.
|
|
40414
|
-
return "0.1.
|
|
41579
|
+
if ("0.1.46".length > 0) {
|
|
41580
|
+
return "0.1.46";
|
|
40415
41581
|
}
|
|
40416
41582
|
try {
|
|
40417
41583
|
const value = JSON.parse(
|
|
@@ -41035,7 +42201,7 @@ async function runNew(args) {
|
|
|
41035
42201
|
assertWorkspaceName(name);
|
|
41036
42202
|
const cloud = await target(args);
|
|
41037
42203
|
const human = await humanCredential(args, cloud);
|
|
41038
|
-
const proposedId = (0,
|
|
42204
|
+
const proposedId = (0, import_node_crypto21.randomUUID)();
|
|
41039
42205
|
let result;
|
|
41040
42206
|
try {
|
|
41041
42207
|
result = await new ThinCommandClient(cloud).sendConnect({
|
|
@@ -42058,7 +43224,7 @@ function accepted(label, result) {
|
|
|
42058
43224
|
);
|
|
42059
43225
|
}
|
|
42060
43226
|
}
|
|
42061
|
-
async function agentSession(cloud, workspaceId2, agent) {
|
|
43227
|
+
async function agentSession(cloud, workspaceId2, agent, fetcher) {
|
|
42062
43228
|
let store2 = null;
|
|
42063
43229
|
try {
|
|
42064
43230
|
const candidate = await agentCredentialStore({
|
|
@@ -42084,7 +43250,8 @@ async function agentSession(cloud, workspaceId2, agent) {
|
|
|
42084
43250
|
runId: agent.runId,
|
|
42085
43251
|
expiresAt: agent.expiresAt
|
|
42086
43252
|
},
|
|
42087
|
-
store: store2
|
|
43253
|
+
store: store2,
|
|
43254
|
+
...fetcher ? { fetcher } : {}
|
|
42088
43255
|
});
|
|
42089
43256
|
}
|
|
42090
43257
|
async function commandWorkspaceAndCredential(args, cloud, options = {}) {
|
|
@@ -42366,8 +43533,8 @@ function prepareSignalAttachments(localPaths) {
|
|
|
42366
43533
|
name,
|
|
42367
43534
|
bytes,
|
|
42368
43535
|
contentType,
|
|
42369
|
-
fileId: (0,
|
|
42370
|
-
versionId: (0,
|
|
43536
|
+
fileId: (0, import_node_crypto21.randomUUID)(),
|
|
43537
|
+
versionId: (0, import_node_crypto21.randomUUID)(),
|
|
42371
43538
|
createCommandId: newCommandId(),
|
|
42372
43539
|
commitCommandId: newCommandId()
|
|
42373
43540
|
};
|
|
@@ -43052,6 +44219,7 @@ async function runInboxNotifyCommand(args) {
|
|
|
43052
44219
|
);
|
|
43053
44220
|
}
|
|
43054
44221
|
const controller = new AbortController();
|
|
44222
|
+
const httpClient = new ListenerHttpClient();
|
|
43055
44223
|
const stop = () => controller.abort();
|
|
43056
44224
|
process.on("SIGINT", stop);
|
|
43057
44225
|
process.on("SIGTERM", stop);
|
|
@@ -43082,7 +44250,7 @@ async function runInboxNotifyCommand(args) {
|
|
|
43082
44250
|
...after === null ? {} : { after }
|
|
43083
44251
|
}
|
|
43084
44252
|
},
|
|
43085
|
-
{ signal: controller.signal }
|
|
44253
|
+
{ signal: controller.signal, fetcher: httpClient.fetch }
|
|
43086
44254
|
);
|
|
43087
44255
|
},
|
|
43088
44256
|
emit: async (signal) => {
|
|
@@ -43116,6 +44284,7 @@ async function runInboxNotifyCommand(args) {
|
|
|
43116
44284
|
} finally {
|
|
43117
44285
|
process.off("SIGINT", stop);
|
|
43118
44286
|
process.off("SIGTERM", stop);
|
|
44287
|
+
httpClient.close();
|
|
43119
44288
|
}
|
|
43120
44289
|
}
|
|
43121
44290
|
async function runReceipt(args) {
|
|
@@ -43188,6 +44357,7 @@ async function runInboxFollowCommand(args) {
|
|
|
43188
44357
|
includeStale: args.has("include-stale")
|
|
43189
44358
|
};
|
|
43190
44359
|
const controller = new AbortController();
|
|
44360
|
+
const httpClient = new ListenerHttpClient();
|
|
43191
44361
|
let legacyCursorWarned = false;
|
|
43192
44362
|
let malformedRowWarnings = 0;
|
|
43193
44363
|
const onAbortSignal = () => controller.abort();
|
|
@@ -43219,7 +44389,7 @@ async function runInboxFollowCommand(args) {
|
|
|
43219
44389
|
cloud,
|
|
43220
44390
|
credential,
|
|
43221
44391
|
query,
|
|
43222
|
-
{ signal: controller.signal },
|
|
44392
|
+
{ signal: controller.signal, fetcher: httpClient.fetch },
|
|
43223
44393
|
{
|
|
43224
44394
|
allowLegacyCursorFallback: true,
|
|
43225
44395
|
tolerateMalformedRows: true,
|
|
@@ -43251,7 +44421,7 @@ async function runInboxFollowCommand(args) {
|
|
|
43251
44421
|
cloud,
|
|
43252
44422
|
credential,
|
|
43253
44423
|
query,
|
|
43254
|
-
{ signal: controller.signal }
|
|
44424
|
+
{ signal: controller.signal, fetcher: httpClient.fetch }
|
|
43255
44425
|
);
|
|
43256
44426
|
},
|
|
43257
44427
|
emit: (frame) => {
|
|
@@ -43269,6 +44439,7 @@ async function runInboxFollowCommand(args) {
|
|
|
43269
44439
|
} finally {
|
|
43270
44440
|
process.off("SIGINT", onAbortSignal);
|
|
43271
44441
|
process.off("SIGTERM", onAbortSignal);
|
|
44442
|
+
httpClient.close();
|
|
43272
44443
|
}
|
|
43273
44444
|
}
|
|
43274
44445
|
function listenerUuid(value, flag) {
|
|
@@ -43453,16 +44624,107 @@ function listenerAttendanceState(status, evidence) {
|
|
|
43453
44624
|
function listenerAttendanceRemedy(principalId) {
|
|
43454
44625
|
return `cswarm hook install claude --principal-id ${principalId} --write, then start a fresh session. Or restart the listener with --route worker.`;
|
|
43455
44626
|
}
|
|
44627
|
+
function listenerReadHealthSummary(status, nowMs) {
|
|
44628
|
+
return summarizeListenerReadHealth(
|
|
44629
|
+
status.readHealth ?? emptyListenerReadHealth(),
|
|
44630
|
+
status.readyAt,
|
|
44631
|
+
nowMs
|
|
44632
|
+
);
|
|
44633
|
+
}
|
|
44634
|
+
function listenerLapseNotices(status, summary) {
|
|
44635
|
+
const health = status.readHealth ?? emptyListenerReadHealth();
|
|
44636
|
+
const notices = [];
|
|
44637
|
+
if (health.currentReasonCode === "host_ports_exhausted") {
|
|
44638
|
+
notices.push({
|
|
44639
|
+
code: "listener_host_ports_exhausted",
|
|
44640
|
+
message: "This host has run out of outbound ports. The listener is probing only once per minute so it does not amplify the outage.",
|
|
44641
|
+
nextStep: "Find the consumer: lsof -nP -iTCP | awk '{print $1}' | sort | uniq -c | sort -rn"
|
|
44642
|
+
});
|
|
44643
|
+
} else if (
|
|
44644
|
+
// Reuse arrival-watch.ts's 60s loud-lapse transition. The listener keeps
|
|
44645
|
+
// the episode in durable status instead of the monitor's process-local machine.
|
|
44646
|
+
summary.currentEpisodeDurationMs !== null && summary.currentEpisodeDurationMs >= ARRIVAL_RETRY_NOTICE_THRESHOLD_MS
|
|
44647
|
+
) {
|
|
44648
|
+
notices.push({
|
|
44649
|
+
code: "listener_read_retry_persisting",
|
|
44650
|
+
message: `Listener reads have failed continuously for ${Math.floor(summary.currentEpisodeDurationMs / 1e3)}s. This is still in progress.`,
|
|
44651
|
+
nextStep: "Check cswarm status and the CommonSwarm service. If both are healthy, restart the listener."
|
|
44652
|
+
});
|
|
44653
|
+
}
|
|
44654
|
+
if (summary.throughputLapseHours.length > 0) {
|
|
44655
|
+
const latest = summary.throughputLapseHours.at(-1);
|
|
44656
|
+
notices.push({
|
|
44657
|
+
code: "listener_claim_throughput_lapse",
|
|
44658
|
+
message: `Claim throughput fell below 0.50 for the full hour at ${latest.hourStart}: ${latest.claims}/${Math.round(latest.expectedClaims)} expected (${latest.ratio.toFixed(3)}).`,
|
|
44659
|
+
nextStep: "This host is starving the listener \u2014 check load/memory pressure (sysctl kern.memorystatus_vm_pressure_level), or move the listener."
|
|
44660
|
+
});
|
|
44661
|
+
}
|
|
44662
|
+
return notices;
|
|
44663
|
+
}
|
|
44664
|
+
async function listenerProviderInstallEvidence(status) {
|
|
44665
|
+
if (status.provider !== "claude") return null;
|
|
44666
|
+
try {
|
|
44667
|
+
const notice = await inspectClaudeBridgeExecutable(
|
|
44668
|
+
status.providerExecutable ?? "claude-agent-acp",
|
|
44669
|
+
{ pathEnv: process.env.PATH, env: process.env }
|
|
44670
|
+
);
|
|
44671
|
+
return {
|
|
44672
|
+
executable: notice.executable,
|
|
44673
|
+
providerVersion: notice.providerVersion,
|
|
44674
|
+
bundledAgentSdkVersion: notice.bundledAgentSdkVersion,
|
|
44675
|
+
bundledClaudeCodeVersion: notice.bundledClaudeCodeVersion
|
|
44676
|
+
};
|
|
44677
|
+
} catch {
|
|
44678
|
+
return {
|
|
44679
|
+
executable: null,
|
|
44680
|
+
providerVersion: null,
|
|
44681
|
+
bundledAgentSdkVersion: null,
|
|
44682
|
+
bundledClaudeCodeVersion: null
|
|
44683
|
+
};
|
|
44684
|
+
}
|
|
44685
|
+
}
|
|
44686
|
+
function versionIsBelow(left, right) {
|
|
44687
|
+
if (!left || !right) return false;
|
|
44688
|
+
try {
|
|
44689
|
+
return compareSemVer(left, right) < 0;
|
|
44690
|
+
} catch {
|
|
44691
|
+
return false;
|
|
44692
|
+
}
|
|
44693
|
+
}
|
|
44694
|
+
function providerRestartRequired(status, installed) {
|
|
44695
|
+
if (!installed) return false;
|
|
44696
|
+
return status.providerVersion !== null && status.providerVersion !== void 0 && installed.providerVersion !== null && status.providerVersion !== installed.providerVersion || status.providerBundledClaudeCodeVersion !== null && status.providerBundledClaudeCodeVersion !== void 0 && installed.bundledClaudeCodeVersion !== null && status.providerBundledClaudeCodeVersion !== installed.bundledClaudeCodeVersion;
|
|
44697
|
+
}
|
|
43456
44698
|
function listenerStatusJson(status, permissionMode, evidence = {
|
|
43457
44699
|
pendingForMainOldestAt: null,
|
|
43458
44700
|
hookSurfaceExists: false,
|
|
43459
44701
|
hookSurfaceAdvanced: false
|
|
43460
|
-
}, nowMs = Date.now()) {
|
|
44702
|
+
}, nowMs = Date.now(), installed = null) {
|
|
43461
44703
|
const mode3 = permissionMode ?? status.permissionMode;
|
|
43462
44704
|
const attendance = listenerAttendanceState(status, evidence);
|
|
43463
44705
|
const pending = status.pendingForMainCount ?? 0;
|
|
44706
|
+
const readHealth = status.readHealth ?? emptyListenerReadHealth();
|
|
44707
|
+
const readSummary = listenerReadHealthSummary(status, nowMs);
|
|
44708
|
+
const lapseNotices = listenerLapseNotices(status, readSummary);
|
|
43464
44709
|
return {
|
|
43465
44710
|
...status,
|
|
44711
|
+
providerExecutable: status.providerExecutable ?? null,
|
|
44712
|
+
providerExecutableMeasured: typeof status.providerExecutable === "string",
|
|
44713
|
+
providerVersion: status.providerVersion ?? null,
|
|
44714
|
+
providerVersionMeasured: typeof status.providerVersion === "string",
|
|
44715
|
+
providerBundledAgentSdkVersion: status.providerBundledAgentSdkVersion ?? null,
|
|
44716
|
+
providerBundledClaudeCodeVersion: status.providerBundledClaudeCodeVersion ?? null,
|
|
44717
|
+
providerMinimumRequiredVersion: status.providerMinimumRequiredVersion ?? null,
|
|
44718
|
+
lastErrorReasonCode: status.lastErrorReasonCode ?? null,
|
|
44719
|
+
providerBelowDemandedMinimum: versionIsBelow(
|
|
44720
|
+
status.providerBundledClaudeCodeVersion,
|
|
44721
|
+
status.providerMinimumRequiredVersion
|
|
44722
|
+
),
|
|
44723
|
+
providerOnDiskExecutable: installed?.executable ?? null,
|
|
44724
|
+
providerOnDiskVersion: installed?.providerVersion ?? null,
|
|
44725
|
+
providerOnDiskBundledAgentSdkVersion: installed?.bundledAgentSdkVersion ?? null,
|
|
44726
|
+
providerOnDiskBundledClaudeCodeVersion: installed?.bundledClaudeCodeVersion ?? null,
|
|
44727
|
+
providerRestartRequired: providerRestartRequired(status, installed),
|
|
43466
44728
|
...attendance,
|
|
43467
44729
|
hookSurfaceExists: evidence.hookSurfaceExists,
|
|
43468
44730
|
hookSurfaceAdvanced: evidence.hookSurfaceAdvanced,
|
|
@@ -43470,6 +44732,22 @@ function listenerStatusJson(status, permissionMode, evidence = {
|
|
|
43470
44732
|
pendingForMainOldestAgeMs: evidence.pendingForMainOldestAt === null ? null : Math.max(0, nowMs - Date.parse(evidence.pendingForMainOldestAt)),
|
|
43471
44733
|
attendanceWarningCode: pending > 0 ? "listener_unattended_main_queue" : null,
|
|
43472
44734
|
attendanceNextStep: pending > 0 ? listenerAttendanceRemedy(status.principalId) : null,
|
|
44735
|
+
readRetryCurrentEpisodeStartedAt: readHealth.currentEpisodeStartedAt,
|
|
44736
|
+
readRetryCurrentEpisodeAttempts: readHealth.currentEpisodeAttempts,
|
|
44737
|
+
readRetryCurrentReasonCode: readHealth.currentReasonCode,
|
|
44738
|
+
readRetryCurrentHttpStatus: readHealth.currentHttpStatus,
|
|
44739
|
+
readRetryCurrentErrorConstructor: readHealth.currentErrorConstructor,
|
|
44740
|
+
readRetryCurrentEpisodeDurationMs: readSummary.currentEpisodeDurationMs,
|
|
44741
|
+
readRetryEpisodesLast24h: readSummary.episodesLast24h,
|
|
44742
|
+
readRetryLongestEpisodeAttemptsLast24h: readSummary.longestEpisodeAttemptsLast24h,
|
|
44743
|
+
readRetryLongestEpisodeDurationMsLast24h: readSummary.longestEpisodeDurationMsLast24h,
|
|
44744
|
+
readRetriesLastHour: readSummary.retriesLastHour,
|
|
44745
|
+
readRetryHours: readSummary.retryHours,
|
|
44746
|
+
claimCadenceMs: readHealth.claimCadenceMs,
|
|
44747
|
+
claimThroughputHours: readSummary.claimThroughputHours,
|
|
44748
|
+
listenerLapse: lapseNotices.length > 0,
|
|
44749
|
+
listenerLapseCodes: lapseNotices.map((notice) => notice.code),
|
|
44750
|
+
listenerLapseNextSteps: lapseNotices.map((notice) => notice.nextStep),
|
|
43473
44751
|
deliveryMode: status.deliveryMode ?? null,
|
|
43474
44752
|
pendingDeliveryCount: status.pendingDeliveryCount ?? null,
|
|
43475
44753
|
lastTerminalDeliveryFailureCount: status.lastTerminalDeliveryFailureCount ?? null,
|
|
@@ -43480,6 +44758,8 @@ function listenerStatusJson(status, permissionMode, evidence = {
|
|
|
43480
44758
|
deferOverChars: status.deferOverChars ?? null,
|
|
43481
44759
|
pendingForMainCount: status.pendingForMainCount ?? 0,
|
|
43482
44760
|
droppedForMainCount: status.droppedForMainCount ?? 0,
|
|
44761
|
+
connectionsOpened: status.connectionsOpened ?? null,
|
|
44762
|
+
connectionReuseRatio: status.connectionReuseRatio ?? null,
|
|
43483
44763
|
...mode3 ? {
|
|
43484
44764
|
permission_mode: mode3,
|
|
43485
44765
|
/* "allowed once" alone overstates it: allowOnceOrDeny selects allow_once only when the
|
|
@@ -43495,27 +44775,56 @@ function renderListenerStatus(status, evidence = {
|
|
|
43495
44775
|
pendingForMainOldestAt: null,
|
|
43496
44776
|
hookSurfaceExists: false,
|
|
43497
44777
|
hookSurfaceAdvanced: false
|
|
43498
|
-
}, nowMs = Date.now()) {
|
|
44778
|
+
}, nowMs = Date.now(), installed = null) {
|
|
43499
44779
|
const routeMode = status.routeMode ?? "worker";
|
|
43500
44780
|
const pendingForMainCount = status.pendingForMainCount ?? 0;
|
|
43501
44781
|
const droppedForMainCount = status.droppedForMainCount ?? 0;
|
|
43502
44782
|
const unattendedCount = `${pendingForMainCount} ${pendingForMainCount === 1 ? "message is" : "messages are"} unattended`;
|
|
43503
44783
|
const attendance = listenerAttendanceState(status, evidence);
|
|
44784
|
+
const readHealth = status.readHealth ?? emptyListenerReadHealth();
|
|
44785
|
+
const readSummary = listenerReadHealthSummary(status, nowMs);
|
|
44786
|
+
const lapseNotices = listenerLapseNotices(status, readSummary);
|
|
43504
44787
|
const lines = [
|
|
43505
|
-
pendingForMainCount > 0 ? `Listener WARNING for agent ${status.principalId}: ${unattendedCount}.` : `Listener ${status.state} for agent ${status.principalId}.`,
|
|
44788
|
+
lapseNotices.length > 0 ? `Listener LAPSE for agent ${status.principalId}: ${lapseNotices.map((notice) => notice.code).join(", ")}.` : pendingForMainCount > 0 ? `Listener WARNING for agent ${status.principalId}: ${unattendedCount}.` : `Listener ${status.state} for agent ${status.principalId}.`,
|
|
43506
44789
|
`CONNECTED: ${attendance.connected ? "yes" : "no"}. Transport state is ${status.state}.`,
|
|
43507
44790
|
`ATTENDED: ${attendance.attendanceState === "attended" ? "yes. The session hook has surfaced messages on this host" : attendance.attendanceState === "unattended" ? "no. The main-session queue is not draining" : attendance.attendanceState === "not_required" ? "not required for the worker route" : "not yet proven on this host"}.`,
|
|
43508
44791
|
`HANDLED: ${attendance.handledState === "handled" ? "yes. A delivery acknowledgement is recorded" : attendance.handledState === "not_handled" ? "no. Queued messages have not reached the session hook" : "not yet measured"}.`,
|
|
43509
44792
|
`Provider: ${status.provider}; process: ${status.pid}; started: ${status.startedAt}.`,
|
|
44793
|
+
`Provider executable: ${status.providerExecutable ?? "not measured"}.`,
|
|
44794
|
+
`Connections opened: ${status.connectionsOpened ?? "not measured"}.`,
|
|
44795
|
+
`Connection reuse ratio: ${status.connectionReuseRatio ?? "not measured"}.`,
|
|
43510
44796
|
status.readyAt ? `Ready since: ${status.readyAt}.` : "Not ready yet.",
|
|
43511
44797
|
status.lastSignalId ? pendingForMainCount > 0 ? `Last claimed and queued signal: ${status.lastSignalId}. It is not handled yet.` : routeMode === "worker" ? `Last handled signal: ${status.lastSignalId}.` : `Last listener signal: ${status.lastSignalId}. Local status does not prove its final observed receipt.` : "No signal has been handled yet.",
|
|
43512
|
-
status.lastErrorCode ? `Last status code: ${status.lastErrorCode}.` : "No listener process error is recorded."
|
|
44798
|
+
status.lastErrorCode ? `Last status code: ${status.lastErrorCode}.` : "No listener process error is recorded.",
|
|
44799
|
+
readHealth.currentEpisodeStartedAt === null ? "Current read retry episode: none." : `Current read retry episode: ${readHealth.currentEpisodeAttempts} attempt${readHealth.currentEpisodeAttempts === 1 ? "" : "s"} since ${readHealth.currentEpisodeStartedAt}; reason ${readHealth.currentReasonCode}${readHealth.currentHttpStatus === null ? "" : ` (HTTP ${readHealth.currentHttpStatus})`}${readHealth.currentErrorConstructor === null ? "" : ` (${readHealth.currentErrorConstructor})`}.`,
|
|
44800
|
+
`Read retry episodes in the last 24h: ${readSummary.episodesLast24h}; retries in the rolling hour: ${readSummary.retriesLastHour}.`,
|
|
44801
|
+
readSummary.longestEpisodeAttemptsLast24h === 0 ? "Longest read retry episode in the last 24h: none recorded." : `Longest read retry episode in the last 24h: ${readSummary.longestEpisodeAttemptsLast24h} attempts over ${Math.floor(readSummary.longestEpisodeDurationMsLast24h / 1e3)}s.`,
|
|
44802
|
+
readSummary.retryHours.length === 0 ? "Read retries by hour in the last 24h: none." : `Read retries by hour in the last 24h: ${readSummary.retryHours.map((hour) => `${hour.hourStart}=${hour.retries}`).join("; ")}.`,
|
|
44803
|
+
readSummary.claimThroughputHours.length === 0 ? "Claim throughput by full hour: no complete listener hour is available yet." : `Claim throughput by full hour: ${readSummary.claimThroughputHours.map((hour) => `${hour.hourStart} ${hour.claims}/${Math.round(hour.expectedClaims)} (${hour.ratio.toFixed(3)})`).join("; ")}.`
|
|
43513
44804
|
];
|
|
44805
|
+
for (const notice of lapseNotices) {
|
|
44806
|
+
lines.push(`WARNING [${notice.code}]: ${notice.message}`);
|
|
44807
|
+
lines.push(`Next: ${notice.nextStep}`);
|
|
44808
|
+
}
|
|
43514
44809
|
if (status.lastErrorDetail) {
|
|
43515
44810
|
const [first, ...rest] = status.lastErrorDetail.split("\n");
|
|
43516
44811
|
lines.push(`Last error detail (local only): ${first}`);
|
|
43517
44812
|
for (const line of rest) lines.push(` ${line}`);
|
|
43518
44813
|
}
|
|
44814
|
+
if (status.lastErrorReasonCode) {
|
|
44815
|
+
lines.push(`Last provider reason code: ${status.lastErrorReasonCode}.`);
|
|
44816
|
+
}
|
|
44817
|
+
if (status.provider === "claude" && status.lastErrorCode === "permission_canary_failed") {
|
|
44818
|
+
lines.push(
|
|
44819
|
+
`Canary diagnosis: ${listenerFailureMessage(
|
|
44820
|
+
status.lastErrorCode,
|
|
44821
|
+
status.provider,
|
|
44822
|
+
status.lastErrorDetail,
|
|
44823
|
+
status.lastErrorReasonCode,
|
|
44824
|
+
status.providerMinimumRequiredVersion
|
|
44825
|
+
)}.`
|
|
44826
|
+
);
|
|
44827
|
+
}
|
|
43519
44828
|
if (status.lastWorkerStderrTail) {
|
|
43520
44829
|
const tailLines = status.lastWorkerStderrTail.split("\n").filter((line) => line.trim().length > 0);
|
|
43521
44830
|
lines.push("Worker stderr (local log only):");
|
|
@@ -43525,8 +44834,34 @@ function renderListenerStatus(status, evidence = {
|
|
|
43525
44834
|
}
|
|
43526
44835
|
if (status.providerVersion && status.providerLastMeasuredVersion) {
|
|
43527
44836
|
lines.push(
|
|
43528
|
-
status.providerVersion === status.providerLastMeasuredVersion ? `Provider version: ${status.providerVersion} (last measured: ${status.providerLastMeasuredVersion}).` : `Provider version ${status.providerVersion} is newer than the last measured version ${status.providerLastMeasuredVersion}. It is unverified but allowed because the startup permission canary passed. Next: verify this provider release with CommonSwarm and update the last-measured version.`
|
|
44837
|
+
status.providerVersion === status.providerLastMeasuredVersion ? `Provider version: ${status.providerVersion} (last measured: ${status.providerLastMeasuredVersion}).` : status.state === "ready" ? `Provider version ${status.providerVersion} is newer than the last measured version ${status.providerLastMeasuredVersion}. It is unverified but allowed because the startup permission canary passed. Next: verify this provider release with CommonSwarm and update the last-measured version.` : `Provider version ${status.providerVersion} is newer than the last measured version ${status.providerLastMeasuredVersion}. It was measured before startup failed; compatibility was not established. Next: resolve the startup failure before verifying this provider release.`
|
|
43529
44838
|
);
|
|
44839
|
+
} else {
|
|
44840
|
+
lines.push("Provider version: not measured.");
|
|
44841
|
+
}
|
|
44842
|
+
if (status.provider === "claude") {
|
|
44843
|
+
lines.push(
|
|
44844
|
+
`Bundled Claude Code version: ${status.providerBundledClaudeCodeVersion ?? "not measured"}.`,
|
|
44845
|
+
`Bundled Claude agent SDK version: ${status.providerBundledAgentSdkVersion ?? "not measured"}.`
|
|
44846
|
+
);
|
|
44847
|
+
if (status.providerMinimumRequiredVersion) {
|
|
44848
|
+
lines.push(
|
|
44849
|
+
`Last API minimum demanded: Claude Code ${status.providerMinimumRequiredVersion}.`
|
|
44850
|
+
);
|
|
44851
|
+
}
|
|
44852
|
+
if (versionIsBelow(
|
|
44853
|
+
status.providerBundledClaudeCodeVersion,
|
|
44854
|
+
status.providerMinimumRequiredVersion
|
|
44855
|
+
)) {
|
|
44856
|
+
lines.push(
|
|
44857
|
+
`WARNING [claude_bridge_below_api_minimum]: this listener has bundled Claude Code ${status.providerBundledClaudeCodeVersion}, below the API minimum ${status.providerMinimumRequiredVersion}. Install the current bridge (npm i -g @agentclientprotocol/claude-agent-acp@latest), restart the listener, then run cswarm listen status and confirm that the bundled Claude Code version meets the API minimum ${status.providerMinimumRequiredVersion}.`
|
|
44858
|
+
);
|
|
44859
|
+
}
|
|
44860
|
+
if (providerRestartRequired(status, installed)) {
|
|
44861
|
+
lines.push(
|
|
44862
|
+
`A different Claude bridge is on disk: ${installed?.providerVersion ?? "version not measured"}${installed?.bundledClaudeCodeVersion ? ` (bundled Claude Code ${installed.bundledClaudeCodeVersion})` : ""}. Restart to pick up ${installed?.providerVersion ?? "the on-disk bridge"}.`
|
|
44863
|
+
);
|
|
44864
|
+
}
|
|
43530
44865
|
}
|
|
43531
44866
|
if (status.deliveryMode === "durable_claim") {
|
|
43532
44867
|
lines.push("Delivery mode: durable claim and acknowledgement.");
|
|
@@ -43603,7 +44938,26 @@ async function unsurfacedPendingMainStats(instanceDirectory, fallback) {
|
|
|
43603
44938
|
};
|
|
43604
44939
|
}
|
|
43605
44940
|
}
|
|
43606
|
-
function
|
|
44941
|
+
function quotedListenerFailureDetail(detail) {
|
|
44942
|
+
const recorded = detail?.trim();
|
|
44943
|
+
if (!recorded) return "not recorded";
|
|
44944
|
+
const bounded = recorded.length > 600 ? `${recorded.slice(0, 599)}\u2026` : recorded;
|
|
44945
|
+
return JSON.stringify(bounded);
|
|
44946
|
+
}
|
|
44947
|
+
function listenerProviderIdentitySummary(status) {
|
|
44948
|
+
const parts = [
|
|
44949
|
+
`Provider executable: ${status.providerExecutable ?? "not measured"}`,
|
|
44950
|
+
`provider version: ${status.providerVersion ?? "not measured"}`
|
|
44951
|
+
];
|
|
44952
|
+
if (status.provider === "claude") {
|
|
44953
|
+
parts.push(
|
|
44954
|
+
`bundled Claude Code: ${status.providerBundledClaudeCodeVersion ?? "not measured"}`,
|
|
44955
|
+
`bundled Claude agent SDK: ${status.providerBundledAgentSdkVersion ?? "not measured"}`
|
|
44956
|
+
);
|
|
44957
|
+
}
|
|
44958
|
+
return parts.join("; ");
|
|
44959
|
+
}
|
|
44960
|
+
function listenerFailureMessage(code, provider, detail, reasonCode, minimumRequiredVersion) {
|
|
43607
44961
|
if (code === "version_below_floor") {
|
|
43608
44962
|
if (provider === "codex") {
|
|
43609
44963
|
return "the Codex listener requires codex-acp 1.1.9 or newer; update the bridge, then retry";
|
|
@@ -43663,11 +45017,25 @@ function listenerFailureMessage(code, provider, detail) {
|
|
|
43663
45017
|
}
|
|
43664
45018
|
if (code === "permission_canary_failed") {
|
|
43665
45019
|
if (provider === "claude") {
|
|
43666
|
-
|
|
45020
|
+
const shape = classifyClaudeCanaryFailure(detail, reasonCode);
|
|
45021
|
+
const ran = "the Claude ACP permission canary ran, but no workspace signal prompt was delivered";
|
|
45022
|
+
const response = `bridge response [${shape.code}]: ${quotedListenerFailureDetail(detail)}`;
|
|
45023
|
+
if (shape.code === "claude_bridge_version_required") {
|
|
45024
|
+
const minimum = minimumRequiredVersion ?? shape.minimumRequiredVersion;
|
|
45025
|
+
return `${ran}. ${response}. Next: install the current bridge (npm i -g @agentclientprotocol/claude-agent-acp@latest), restart the listener, then run cswarm listen status and confirm that the bundled Claude Code version meets the API minimum ${minimum ?? "reported there"}`;
|
|
45026
|
+
}
|
|
45027
|
+
if (shape.code === "claude_canary_timeout") {
|
|
45028
|
+
return `${ran}. ${response}. Next: run claude -p and check for session-limit text, check host load, then retry`;
|
|
45029
|
+
}
|
|
45030
|
+
if (shape.code === "claude_canary_auth_failed") {
|
|
45031
|
+
return `${ran}. ${response}. Next: confirm Claude Code keychain/OAuth sign-in, then retry`;
|
|
45032
|
+
}
|
|
45033
|
+
return `${ran}. ${response}. The cause was not determined. Next: inspect the quoted bridge response and local worker stderr, then retry only after the cause is known or the failure appears transient`;
|
|
43667
45034
|
}
|
|
43668
45035
|
if (provider === "codex") {
|
|
43669
45036
|
const recorded = detail?.trim();
|
|
43670
|
-
|
|
45037
|
+
const gate = "the Codex listener did not pass the read-only ACP permission safety gate; no workspace signal prompt was delivered";
|
|
45038
|
+
return recorded ? `${gate}. Recorded reason: ${JSON.stringify(recorded)}` : `${gate}. The recorded reason was unavailable. Next: run cswarm listen status, then retry`;
|
|
43671
45039
|
}
|
|
43672
45040
|
if (provider === "grok") {
|
|
43673
45041
|
return "the Grok bridge did not complete the ACP permission canary; no workspace signal prompt was delivered. The local cswarm listen status output includes the final error detail; read it, then retry";
|
|
@@ -43750,11 +45118,19 @@ async function runConfiguredListener(options) {
|
|
|
43750
45118
|
principalId: options.principalId,
|
|
43751
45119
|
...options.stateDirectory ? { stateDirectory: options.stateDirectory } : {}
|
|
43752
45120
|
});
|
|
43753
|
-
const
|
|
43754
|
-
|
|
43755
|
-
|
|
43756
|
-
|
|
43757
|
-
|
|
45121
|
+
const httpClient = new ListenerHttpClient();
|
|
45122
|
+
let liveCredentialSession;
|
|
45123
|
+
try {
|
|
45124
|
+
liveCredentialSession = await agentSession(
|
|
45125
|
+
options.cloud,
|
|
45126
|
+
options.workspaceId,
|
|
45127
|
+
options.agent,
|
|
45128
|
+
httpClient.fetch
|
|
45129
|
+
);
|
|
45130
|
+
} catch (error) {
|
|
45131
|
+
httpClient.close();
|
|
45132
|
+
throw error;
|
|
45133
|
+
}
|
|
43758
45134
|
let storedCredential = null;
|
|
43759
45135
|
const credentialSession = {
|
|
43760
45136
|
bearer: async () => {
|
|
@@ -43782,7 +45158,7 @@ async function runConfiguredListener(options) {
|
|
|
43782
45158
|
options.cloud,
|
|
43783
45159
|
credential,
|
|
43784
45160
|
options.workspaceId,
|
|
43785
|
-
context
|
|
45161
|
+
{ ...context, fetcher: httpClient.fetch }
|
|
43786
45162
|
);
|
|
43787
45163
|
const provenance = listenerSenderProvenance(signal, senderDirectory);
|
|
43788
45164
|
if (context.includeBrainDigest !== true) return provenance;
|
|
@@ -43793,7 +45169,8 @@ async function runConfiguredListener(options) {
|
|
|
43793
45169
|
options.workspaceId,
|
|
43794
45170
|
{
|
|
43795
45171
|
...context.signal ? { signal: context.signal } : {},
|
|
43796
|
-
deadlineMs: context.deadlineMs
|
|
45172
|
+
deadlineMs: context.deadlineMs,
|
|
45173
|
+
fetcher: httpClient.fetch
|
|
43797
45174
|
}
|
|
43798
45175
|
);
|
|
43799
45176
|
const brainDigest = await new FileBrainDigestStore(
|
|
@@ -43833,7 +45210,19 @@ async function runConfiguredListener(options) {
|
|
|
43833
45210
|
let workerStderrGeneration = 0;
|
|
43834
45211
|
let providerVersionNotice = null;
|
|
43835
45212
|
const onVersionNotice = (notice) => {
|
|
43836
|
-
providerVersionNotice =
|
|
45213
|
+
providerVersionNotice = {
|
|
45214
|
+
runningVersion: notice.runningVersion,
|
|
45215
|
+
lastMeasuredVersion: notice.lastMeasuredVersion
|
|
45216
|
+
};
|
|
45217
|
+
};
|
|
45218
|
+
const onClaudeRuntimeNotice = (notice) => {
|
|
45219
|
+
providerVersionNotice = {
|
|
45220
|
+
runningVersion: notice.providerVersion,
|
|
45221
|
+
lastMeasuredVersion: notice.lastMeasuredVersion,
|
|
45222
|
+
executable: notice.executable,
|
|
45223
|
+
bundledAgentSdkVersion: notice.bundledAgentSdkVersion,
|
|
45224
|
+
bundledClaudeCodeVersion: notice.bundledClaudeCodeVersion
|
|
45225
|
+
};
|
|
43837
45226
|
};
|
|
43838
45227
|
const newWorkerStderrTailSink = () => {
|
|
43839
45228
|
const generation = ++workerStderrGeneration;
|
|
@@ -43843,7 +45232,7 @@ async function runConfiguredListener(options) {
|
|
|
43843
45232
|
lastWorkerStderrTail = tail.length > 0 ? tail : null;
|
|
43844
45233
|
};
|
|
43845
45234
|
};
|
|
43846
|
-
const newModel = (onCanaryAttempt) => {
|
|
45235
|
+
const newModel = (onCanaryAttempt, events) => {
|
|
43847
45236
|
providerVersionNotice = null;
|
|
43848
45237
|
return options.provider === "opencode" ? new OpenCodeListenerModel({
|
|
43849
45238
|
cwd: options.cwd,
|
|
@@ -43852,6 +45241,7 @@ async function runConfiguredListener(options) {
|
|
|
43852
45241
|
onWorkerStderrTail: newWorkerStderrTailSink(),
|
|
43853
45242
|
onCanaryAttempt,
|
|
43854
45243
|
onVersionNotice,
|
|
45244
|
+
events,
|
|
43855
45245
|
...options.model ? { model: options.model } : {},
|
|
43856
45246
|
...options.opencodeExecutable ? { executable: options.opencodeExecutable } : options.executable ? { executable: options.executable } : {}
|
|
43857
45247
|
}) : options.provider === "claude" ? new ClaudeListenerModel({
|
|
@@ -43860,7 +45250,9 @@ async function runConfiguredListener(options) {
|
|
|
43860
45250
|
promptTimeoutMs: resolveTurnBudgetMs,
|
|
43861
45251
|
onWorkerStderrTail: newWorkerStderrTailSink(),
|
|
43862
45252
|
onCanaryAttempt,
|
|
45253
|
+
onRuntimeNotice: onClaudeRuntimeNotice,
|
|
43863
45254
|
onVersionNotice,
|
|
45255
|
+
events,
|
|
43864
45256
|
...options.claudeExecutable ? { executable: options.claudeExecutable } : options.executable ? { executable: options.executable } : {}
|
|
43865
45257
|
}) : options.provider === "codex" ? new CodexListenerModel({
|
|
43866
45258
|
cwd: options.cwd,
|
|
@@ -43869,6 +45261,7 @@ async function runConfiguredListener(options) {
|
|
|
43869
45261
|
onWorkerStderrTail: newWorkerStderrTailSink(),
|
|
43870
45262
|
onCanaryAttempt,
|
|
43871
45263
|
onVersionNotice,
|
|
45264
|
+
events,
|
|
43872
45265
|
...options.codexExecutable ? { executable: options.codexExecutable } : options.executable ? { executable: options.executable } : {}
|
|
43873
45266
|
}) : new GrokListenerModel({
|
|
43874
45267
|
cwd: options.cwd,
|
|
@@ -43877,6 +45270,7 @@ async function runConfiguredListener(options) {
|
|
|
43877
45270
|
onWorkerStderrTail: newWorkerStderrTailSink(),
|
|
43878
45271
|
onCanaryAttempt,
|
|
43879
45272
|
onVersionNotice,
|
|
45273
|
+
events,
|
|
43880
45274
|
...options.model ? { model: options.model } : {},
|
|
43881
45275
|
...options.effort ? { effort: options.effort } : {},
|
|
43882
45276
|
...options.executable ? { executable: options.executable } : {}
|
|
@@ -43907,6 +45301,7 @@ async function runConfiguredListener(options) {
|
|
|
43907
45301
|
// one has run, else the configured cap.
|
|
43908
45302
|
getTurnBudgetMs: () => lastAppliedTurnBudgetMs ?? turnBudgetMs,
|
|
43909
45303
|
getProviderVersionNotice: () => providerVersionNotice,
|
|
45304
|
+
getConnectionMetrics: () => httpClient.metrics(),
|
|
43910
45305
|
takeWorkerStderrTail: () => {
|
|
43911
45306
|
const tail = lastWorkerStderrTail;
|
|
43912
45307
|
lastWorkerStderrTail = null;
|
|
@@ -43940,28 +45335,48 @@ async function runConfiguredListener(options) {
|
|
|
43940
45335
|
ts: (/* @__PURE__ */ new Date()).toISOString()
|
|
43941
45336
|
});
|
|
43942
45337
|
};
|
|
43943
|
-
|
|
43944
|
-
target: options.cloud,
|
|
45338
|
+
const activity = new ListenerActivityController({
|
|
43945
45339
|
workspaceId: options.workspaceId,
|
|
43946
|
-
|
|
43947
|
-
|
|
43948
|
-
|
|
43949
|
-
|
|
43950
|
-
|
|
43951
|
-
onEvent,
|
|
43952
|
-
declareModel: listenerModelLabel(options.provider),
|
|
43953
|
-
listenerInstanceId,
|
|
43954
|
-
deliveryJournal: selectedJournal,
|
|
43955
|
-
resolveSenderProvenance,
|
|
43956
|
-
routeMode,
|
|
43957
|
-
deferOverChars,
|
|
43958
|
-
pendingMainQueue
|
|
45340
|
+
transport: new AgentActivityEndpointTransport(
|
|
45341
|
+
options.cloud,
|
|
45342
|
+
credentialSession,
|
|
45343
|
+
httpClient.fetch
|
|
45344
|
+
)
|
|
43959
45345
|
});
|
|
45346
|
+
const instrumentedModel = activity.instrumentModel(
|
|
45347
|
+
newModel(onCanaryAttempt, activity.events)
|
|
45348
|
+
);
|
|
45349
|
+
try {
|
|
45350
|
+
return await runListenerRuntime({
|
|
45351
|
+
target: options.cloud,
|
|
45352
|
+
workspaceId: options.workspaceId,
|
|
45353
|
+
principalId: options.principalId,
|
|
45354
|
+
credentialSession,
|
|
45355
|
+
store: effectStore,
|
|
45356
|
+
model: instrumentedModel,
|
|
45357
|
+
signal,
|
|
45358
|
+
onEvent: (event) => {
|
|
45359
|
+
activity.onRuntimeEvent(event);
|
|
45360
|
+
onEvent(event);
|
|
45361
|
+
},
|
|
45362
|
+
declareModel: listenerModelLabel(options.provider),
|
|
45363
|
+
listenerInstanceId,
|
|
45364
|
+
deliveryJournal: selectedJournal,
|
|
45365
|
+
resolveSenderProvenance,
|
|
45366
|
+
routeMode,
|
|
45367
|
+
deferOverChars,
|
|
45368
|
+
pendingMainQueue,
|
|
45369
|
+
fetcher: httpClient.fetch
|
|
45370
|
+
});
|
|
45371
|
+
} finally {
|
|
45372
|
+
activity.close();
|
|
45373
|
+
}
|
|
43960
45374
|
}
|
|
43961
45375
|
});
|
|
43962
45376
|
} finally {
|
|
43963
45377
|
process.off("SIGINT", onProcessSignal);
|
|
43964
45378
|
process.off("SIGTERM", onProcessSignal);
|
|
45379
|
+
httpClient.close();
|
|
43965
45380
|
}
|
|
43966
45381
|
}
|
|
43967
45382
|
async function runListenStart(args) {
|
|
@@ -44111,18 +45526,30 @@ async function runListenStart(args) {
|
|
|
44111
45526
|
if (error instanceof ListenerStartupError) {
|
|
44112
45527
|
const failedStatus = await effectiveListenerStatus(paths).catch(() => null);
|
|
44113
45528
|
const detail = failedStatus?.lastErrorCode === error.code ? failedStatus.lastErrorDetail : null;
|
|
44114
|
-
|
|
45529
|
+
const reasonCode = failedStatus?.lastErrorCode === error.code ? failedStatus.lastErrorReasonCode : null;
|
|
45530
|
+
const message = listenerFailureMessage(
|
|
45531
|
+
error.code,
|
|
45532
|
+
provider,
|
|
45533
|
+
detail,
|
|
45534
|
+
reasonCode,
|
|
45535
|
+
failedStatus?.providerMinimumRequiredVersion
|
|
45536
|
+
);
|
|
45537
|
+
throw new Error(
|
|
45538
|
+
failedStatus === null ? message : `${message}. ${listenerProviderIdentitySummary(failedStatus)}`
|
|
45539
|
+
);
|
|
44115
45540
|
}
|
|
44116
45541
|
throw error;
|
|
44117
45542
|
}
|
|
44118
45543
|
}
|
|
44119
45544
|
if (status.state === "failed") {
|
|
44120
45545
|
throw new Error(
|
|
44121
|
-
listenerFailureMessage(
|
|
45546
|
+
`${listenerFailureMessage(
|
|
44122
45547
|
status.lastErrorCode ?? "unknown_error",
|
|
44123
45548
|
provider,
|
|
44124
|
-
status.lastErrorDetail
|
|
44125
|
-
|
|
45549
|
+
status.lastErrorDetail,
|
|
45550
|
+
status.lastErrorReasonCode,
|
|
45551
|
+
status.providerMinimumRequiredVersion
|
|
45552
|
+
)}. ${listenerProviderIdentitySummary(status)}`
|
|
44126
45553
|
);
|
|
44127
45554
|
}
|
|
44128
45555
|
let attendanceEvidence = {
|
|
@@ -44304,11 +45731,22 @@ async function runListenStatusOrStop(args, command2) {
|
|
|
44304
45731
|
hookSurfaceAdvanced: queueStats.hookSurfaceAdvanced
|
|
44305
45732
|
};
|
|
44306
45733
|
}
|
|
45734
|
+
const installed = command2 === "status" ? await listenerProviderInstallEvidence(status) : null;
|
|
44307
45735
|
if (args.has("json")) {
|
|
44308
|
-
printJson(
|
|
45736
|
+
printJson(
|
|
45737
|
+
listenerStatusJson(
|
|
45738
|
+
status,
|
|
45739
|
+
void 0,
|
|
45740
|
+
attendanceEvidence,
|
|
45741
|
+
Date.now(),
|
|
45742
|
+
installed
|
|
45743
|
+
)
|
|
45744
|
+
);
|
|
44309
45745
|
} else {
|
|
44310
|
-
process.stdout.write(
|
|
44311
|
-
|
|
45746
|
+
process.stdout.write(
|
|
45747
|
+
`${renderListenerStatus(status, attendanceEvidence, Date.now(), installed)}
|
|
45748
|
+
`
|
|
45749
|
+
);
|
|
44312
45750
|
}
|
|
44313
45751
|
}
|
|
44314
45752
|
async function runListenCanary(args) {
|
|
@@ -44345,16 +45783,23 @@ async function runListenCanary(args) {
|
|
|
44345
45783
|
...stateDirectory2 ? { stateDirectory: stateDirectory2 } : {}
|
|
44346
45784
|
});
|
|
44347
45785
|
const waitMs = parseWaitSeconds(args.optional("wait") ?? "10") * 1e3;
|
|
44348
|
-
const
|
|
44349
|
-
|
|
44350
|
-
|
|
44351
|
-
|
|
44352
|
-
|
|
44353
|
-
|
|
44354
|
-
|
|
44355
|
-
|
|
44356
|
-
|
|
44357
|
-
|
|
45786
|
+
const httpClient = new ListenerHttpClient();
|
|
45787
|
+
let result;
|
|
45788
|
+
try {
|
|
45789
|
+
result = await runListenerAttendanceCanary({
|
|
45790
|
+
target: cloud,
|
|
45791
|
+
workspaceId: workspaceId2,
|
|
45792
|
+
principalId,
|
|
45793
|
+
paths,
|
|
45794
|
+
waitMs,
|
|
45795
|
+
fetcher: httpClient.fetch,
|
|
45796
|
+
// Canary must remain read-only apart from its one self-note. It therefore
|
|
45797
|
+
// uses the presented token and never enters the renewal/mint path.
|
|
45798
|
+
credential: async () => agent.token
|
|
45799
|
+
});
|
|
45800
|
+
} finally {
|
|
45801
|
+
httpClient.close();
|
|
45802
|
+
}
|
|
44358
45803
|
if (args.has("json")) {
|
|
44359
45804
|
printJson({
|
|
44360
45805
|
workspaceId: workspaceId2,
|
|
@@ -44620,19 +46065,25 @@ async function runHook(args) {
|
|
|
44620
46065
|
process.exit(0);
|
|
44621
46066
|
}, 3e3);
|
|
44622
46067
|
hardExit.unref();
|
|
44623
|
-
|
|
44624
|
-
|
|
44625
|
-
|
|
44626
|
-
|
|
44627
|
-
|
|
44628
|
-
|
|
46068
|
+
const httpClient = new ListenerHttpClient();
|
|
46069
|
+
try {
|
|
46070
|
+
await runListenerHookCheck({
|
|
46071
|
+
...cooldownSeconds === void 0 ? {} : { cooldownSeconds },
|
|
46072
|
+
...principalIds.length === 0 ? {} : { principalIds },
|
|
46073
|
+
fetcher: httpClient.fetch,
|
|
46074
|
+
write: async (output) => {
|
|
46075
|
+
await new Promise((resolve2, reject) => {
|
|
46076
|
+
process.stdout.write(`${output}
|
|
44629
46077
|
`, (error) => {
|
|
44630
|
-
|
|
44631
|
-
|
|
46078
|
+
if (error) reject(error);
|
|
46079
|
+
else resolve2();
|
|
46080
|
+
});
|
|
44632
46081
|
});
|
|
44633
|
-
}
|
|
44634
|
-
}
|
|
44635
|
-
}
|
|
46082
|
+
}
|
|
46083
|
+
});
|
|
46084
|
+
} finally {
|
|
46085
|
+
httpClient.close();
|
|
46086
|
+
}
|
|
44636
46087
|
return;
|
|
44637
46088
|
}
|
|
44638
46089
|
if (command2 !== "install" && command2 !== "uninstall") {
|
|
@@ -44758,8 +46209,8 @@ async function uploadNamedFile(context, name, bytes) {
|
|
|
44758
46209
|
workspaceId: context.selected.selectedWorkspace,
|
|
44759
46210
|
credential: context.selected.bearer
|
|
44760
46211
|
};
|
|
44761
|
-
const fileId = (0,
|
|
44762
|
-
const versionId = (0,
|
|
46212
|
+
const fileId = (0, import_node_crypto21.randomUUID)();
|
|
46213
|
+
const versionId = (0, import_node_crypto21.randomUUID)();
|
|
44763
46214
|
const createCommandId = newCommandId();
|
|
44764
46215
|
const commitCommandId = newCommandId();
|
|
44765
46216
|
const created = await onceRetried(
|
|
@@ -45170,7 +46621,7 @@ async function runDogfood(args) {
|
|
|
45170
46621
|
const { selectedWorkspace, bearer } = await commandWorkspaceAndCredential(args, cloud);
|
|
45171
46622
|
const client = new ThinCommandClient(cloud);
|
|
45172
46623
|
const route = stream(args);
|
|
45173
|
-
const taskId = args.optional("task-id") ?? (0,
|
|
46624
|
+
const taskId = args.optional("task-id") ?? (0, import_node_crypto21.randomUUID)();
|
|
45174
46625
|
const ttl = Number(args.optional("ttl-ms") ?? "3600000");
|
|
45175
46626
|
if (!Number.isSafeInteger(ttl) || ttl <= 0 || ttl > 144e5) {
|
|
45176
46627
|
throw new Error("--ttl-ms must be an integer in 1..14400000");
|
|
@@ -45543,6 +46994,7 @@ ${usage()}
|
|
|
45543
46994
|
listenerFailureMessage,
|
|
45544
46995
|
listenerHostLimits,
|
|
45545
46996
|
listenerPermissionMode,
|
|
46997
|
+
listenerProviderInstallEvidence,
|
|
45546
46998
|
listenerRouteConfiguration,
|
|
45547
46999
|
listenerStatusJson,
|
|
45548
47000
|
renderListenerStatus,
|