@integrity-labs/agt-cli 0.28.441 → 0.28.443
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/agt.js +4 -4
- package/dist/{chunk-NQP2W2RD.js → chunk-IQX7TMYZ.js} +178 -11
- package/dist/chunk-IQX7TMYZ.js.map +1 -0
- package/dist/{chunk-6KTN6GTT.js → chunk-X2LHWP6E.js} +3 -3
- package/dist/{claude-pair-runtime-YU6KOA6R.js → claude-pair-runtime-J3BEIXWL.js} +2 -2
- package/dist/lib/manager-worker.js +35 -49
- package/dist/lib/manager-worker.js.map +1 -1
- package/dist/mcp/direct-chat-channel.js +463 -34
- package/dist/mcp/origami.js +60 -2
- package/dist/mcp/slack-channel.js +60 -2
- package/dist/mcp/telegram-channel.js +60 -2
- package/dist/{persistent-session-PWLMLDVX.js → persistent-session-6SCIPYQI.js} +2 -2
- package/dist/{responsiveness-probe-ZKUDFRIU.js → responsiveness-probe-WHDWK62P.js} +2 -2
- package/package.json +1 -1
- package/dist/chunk-NQP2W2RD.js.map +0 -1
- /package/dist/{chunk-6KTN6GTT.js.map → chunk-X2LHWP6E.js.map} +0 -0
- /package/dist/{claude-pair-runtime-YU6KOA6R.js.map → claude-pair-runtime-J3BEIXWL.js.map} +0 -0
- /package/dist/{persistent-session-PWLMLDVX.js.map → persistent-session-6SCIPYQI.js.map} +0 -0
- /package/dist/{responsiveness-probe-ZKUDFRIU.js.map → responsiveness-probe-WHDWK62P.js.map} +0 -0
|
@@ -18867,7 +18867,7 @@ var require_filters = __commonJS({
|
|
|
18867
18867
|
return r.copySafeness(str, res);
|
|
18868
18868
|
}
|
|
18869
18869
|
_exports.indent = indent;
|
|
18870
|
-
function
|
|
18870
|
+
function join14(arr, del, attr) {
|
|
18871
18871
|
del = del || "";
|
|
18872
18872
|
if (attr) {
|
|
18873
18873
|
arr = lib.map(arr, function(v) {
|
|
@@ -18876,7 +18876,7 @@ var require_filters = __commonJS({
|
|
|
18876
18876
|
}
|
|
18877
18877
|
return arr.join(del);
|
|
18878
18878
|
}
|
|
18879
|
-
_exports.join =
|
|
18879
|
+
_exports.join = join14;
|
|
18880
18880
|
function last(arr) {
|
|
18881
18881
|
return arr[arr.length - 1];
|
|
18882
18882
|
}
|
|
@@ -30087,6 +30087,40 @@ function resolveHostBooleanFlag(opts) {
|
|
|
30087
30087
|
if (cached2 !== void 0) return cached2;
|
|
30088
30088
|
return opts.defaultValue;
|
|
30089
30089
|
}
|
|
30090
|
+
var USAGE_LIMIT_REACTIVE_FLAG_KEY = "usage-limit-reactive-notice";
|
|
30091
|
+
function parseUsageLimitReactiveMode(raw) {
|
|
30092
|
+
if (raw === void 0) return void 0;
|
|
30093
|
+
const v = raw.trim().toLowerCase();
|
|
30094
|
+
if (v === "") return void 0;
|
|
30095
|
+
if (v === "off" || v === "shadow" || v === "enforce") return v;
|
|
30096
|
+
const b = envBoolean(raw);
|
|
30097
|
+
if (b === true) return "enforce";
|
|
30098
|
+
if (b === false) return "off";
|
|
30099
|
+
return void 0;
|
|
30100
|
+
}
|
|
30101
|
+
function cachedUsageLimitReactiveMode(path) {
|
|
30102
|
+
try {
|
|
30103
|
+
if (!existsSync4(path)) return void 0;
|
|
30104
|
+
const parsed = JSON.parse(readFileSync6(path, "utf8"));
|
|
30105
|
+
if (!parsed || typeof parsed !== "object") return void 0;
|
|
30106
|
+
const flags = parsed.flags;
|
|
30107
|
+
if (!flags || typeof flags !== "object") return void 0;
|
|
30108
|
+
const value = flags[USAGE_LIMIT_REACTIVE_FLAG_KEY];
|
|
30109
|
+
if (typeof value === "string") return parseUsageLimitReactiveMode(value);
|
|
30110
|
+
if (typeof value === "boolean") return value ? "enforce" : "off";
|
|
30111
|
+
return void 0;
|
|
30112
|
+
} catch {
|
|
30113
|
+
return void 0;
|
|
30114
|
+
}
|
|
30115
|
+
}
|
|
30116
|
+
function resolveUsageLimitReactiveMode(opts) {
|
|
30117
|
+
const env2 = opts?.env ?? process.env;
|
|
30118
|
+
const modeOverride = parseUsageLimitReactiveMode(env2["AGT_USAGE_LIMIT_REACTIVE_MODE"]);
|
|
30119
|
+
if (modeOverride !== void 0) return modeOverride;
|
|
30120
|
+
const cached2 = cachedUsageLimitReactiveMode(opts?.cachePath ?? defaultFlagsCachePath());
|
|
30121
|
+
if (cached2 !== void 0) return cached2;
|
|
30122
|
+
return "off";
|
|
30123
|
+
}
|
|
30090
30124
|
|
|
30091
30125
|
// src/ack-reaction.ts
|
|
30092
30126
|
var REPLY_WEDGED_THRESHOLD_MS = 5 * 60 * 1e3;
|
|
@@ -30399,19 +30433,19 @@ function readLockHolder(path) {
|
|
|
30399
30433
|
}
|
|
30400
30434
|
|
|
30401
30435
|
// src/direct-chat-channel.ts
|
|
30402
|
-
import { homedir as
|
|
30403
|
-
import { join as
|
|
30436
|
+
import { homedir as homedir5 } from "os";
|
|
30437
|
+
import { join as join13 } from "path";
|
|
30404
30438
|
import { randomUUID } from "crypto";
|
|
30405
30439
|
import {
|
|
30406
30440
|
watch,
|
|
30407
30441
|
mkdirSync as mkdirSync5,
|
|
30408
30442
|
writeFileSync as writeFileSync6,
|
|
30409
|
-
readFileSync as
|
|
30410
|
-
readdirSync as
|
|
30443
|
+
readFileSync as readFileSync12,
|
|
30444
|
+
readdirSync as readdirSync5,
|
|
30411
30445
|
existsSync as existsSync6,
|
|
30412
30446
|
renameSync as renameSync5,
|
|
30413
30447
|
unlinkSync as unlinkSync4,
|
|
30414
|
-
statSync as
|
|
30448
|
+
statSync as statSync3,
|
|
30415
30449
|
createWriteStream
|
|
30416
30450
|
} from "fs";
|
|
30417
30451
|
|
|
@@ -31989,8 +32023,31 @@ var DEFAULT_SCOPES = [
|
|
|
31989
32023
|
// links an inbound Slack sender to their organization_people record (inbound
|
|
31990
32024
|
// identity reconcile). Existing installs without it degrade gracefully - the
|
|
31991
32025
|
// reconcile no-ops until the bot is re-authorised with the wider scope set.
|
|
31992
|
-
"users:read.email"
|
|
31993
|
-
|
|
32026
|
+
"users:read.email"
|
|
32027
|
+
// ENG-8203: 'users.profile:write' is deliberately NOT here.
|
|
32028
|
+
//
|
|
32029
|
+
// It is registered with `token_type: 'user'` (ENG-4812 — Slack rejects it
|
|
32030
|
+
// under oauth_config.scopes.bot with `illegal_bot_scopes`). The MANIFEST
|
|
32031
|
+
// generator partitions on that flag; the OAuth install-URL builder does not —
|
|
32032
|
+
// `buildSlackAuthorizeUrl` puts every stored scope into `scope=` — so a
|
|
32033
|
+
// default-scoped OAuth install sent a user-only scope as a bot scope and
|
|
32034
|
+
// Slack rejected the whole authorization with "Invalid permissions
|
|
32035
|
+
// requested". That blocked EVERY new install on the default set.
|
|
32036
|
+
//
|
|
32037
|
+
// Removing it rather than routing it to `user_scope=` is deliberate: the
|
|
32038
|
+
// callback destructures `authed_user?: { id?: string }` and persists only the
|
|
32039
|
+
// BOT token (oauth-callback/route.ts:217,246). `authed_user.access_token` is
|
|
32040
|
+
// never read, so asking for the scope would put an extra consent screen in
|
|
32041
|
+
// front of the installing admin and then discard the token it returned.
|
|
32042
|
+
//
|
|
32043
|
+
// Nothing is lost. `users.profile.set` needs an `xoxp-` user token, which no
|
|
32044
|
+
// OAuth-installed agent has ever had — the bot-status/live-presence indicator
|
|
32045
|
+
// only ever worked where an operator pasted a user token into the wizard by
|
|
32046
|
+
// hand, and that path is untouched. The registry definition stays so existing
|
|
32047
|
+
// configs still render and the `token_type: 'user'` knowledge is preserved.
|
|
32048
|
+
//
|
|
32049
|
+
// Wiring the indicator for OAuth installs means persisting
|
|
32050
|
+
// `authed_user.access_token` first, THEN adding `user_scope=`. Separate work.
|
|
31994
32051
|
];
|
|
31995
32052
|
var SLACK_SCOPE_PRESETS = {
|
|
31996
32053
|
minimal: [
|
|
@@ -34142,7 +34199,113 @@ var BANNER_PATTERNS = [
|
|
|
34142
34199
|
// pct=100.
|
|
34143
34200
|
new RegExp(`${SUBJECT}hit\\s+your\\s+(?:[a-z0-9-]+\\s+)?limit${SEP}resets\\s+(${RESET_DATE})`, "i")
|
|
34144
34201
|
];
|
|
34202
|
+
function parseUsageBanner(text, now = /* @__PURE__ */ new Date()) {
|
|
34203
|
+
let bestIndex = -1;
|
|
34204
|
+
let best = null;
|
|
34205
|
+
for (let i = 0; i < BANNER_PATTERNS.length; i++) {
|
|
34206
|
+
const pattern = new RegExp(BANNER_PATTERNS[i].source, "gi");
|
|
34207
|
+
let match;
|
|
34208
|
+
while ((match = pattern.exec(text)) !== null) {
|
|
34209
|
+
if (match.index === pattern.lastIndex)
|
|
34210
|
+
pattern.lastIndex++;
|
|
34211
|
+
let pct;
|
|
34212
|
+
let resetStr;
|
|
34213
|
+
if (i === 0) {
|
|
34214
|
+
pct = Number.parseInt(match[1], 10);
|
|
34215
|
+
resetStr = match[2];
|
|
34216
|
+
} else {
|
|
34217
|
+
pct = 100;
|
|
34218
|
+
resetStr = match[1];
|
|
34219
|
+
}
|
|
34220
|
+
if (!Number.isFinite(pct) || pct < 0 || pct > 100)
|
|
34221
|
+
continue;
|
|
34222
|
+
const nextChar = text[match.index + match[0].length];
|
|
34223
|
+
if (nextChar !== void 0 && /[A-Za-z0-9]/.test(nextChar))
|
|
34224
|
+
continue;
|
|
34225
|
+
const weekResetsAt = parseResetDateTime(resetStr, now);
|
|
34226
|
+
if (!weekResetsAt)
|
|
34227
|
+
continue;
|
|
34228
|
+
if (match.index >= bestIndex) {
|
|
34229
|
+
bestIndex = match.index;
|
|
34230
|
+
best = { pct, weekResetsAt };
|
|
34231
|
+
}
|
|
34232
|
+
}
|
|
34233
|
+
}
|
|
34234
|
+
return best;
|
|
34235
|
+
}
|
|
34236
|
+
var MONTHS = [
|
|
34237
|
+
"jan",
|
|
34238
|
+
"feb",
|
|
34239
|
+
"mar",
|
|
34240
|
+
"apr",
|
|
34241
|
+
"may",
|
|
34242
|
+
"jun",
|
|
34243
|
+
"jul",
|
|
34244
|
+
"aug",
|
|
34245
|
+
"sep",
|
|
34246
|
+
"oct",
|
|
34247
|
+
"nov",
|
|
34248
|
+
"dec"
|
|
34249
|
+
];
|
|
34250
|
+
var TIME_TAIL = /,\s*(\d{1,2})(?::(\d{2}))?\s*(am|pm)(?:\s*\(?UTC\)?)?\s*$/i;
|
|
34251
|
+
var TIME_ONLY = /^(\d{1,2})(?::(\d{2}))?\s*(am|pm)(?:\s*\(?UTC\)?)?$/i;
|
|
34145
34252
|
var MS_PER_DAY = 24 * 60 * 60 * 1e3;
|
|
34253
|
+
function parseAmPm(hourStr, minStr, ampm) {
|
|
34254
|
+
const rawHour = Number.parseInt(hourStr, 10);
|
|
34255
|
+
if (!Number.isFinite(rawHour) || rawHour < 1 || rawHour > 12)
|
|
34256
|
+
return null;
|
|
34257
|
+
let minute = 0;
|
|
34258
|
+
if (minStr) {
|
|
34259
|
+
minute = Number.parseInt(minStr, 10);
|
|
34260
|
+
if (!Number.isFinite(minute) || minute < 0 || minute > 59)
|
|
34261
|
+
return null;
|
|
34262
|
+
}
|
|
34263
|
+
const isPm = ampm.toLowerCase() === "pm";
|
|
34264
|
+
return { hour: rawHour % 12 + (isPm ? 12 : 0), minute };
|
|
34265
|
+
}
|
|
34266
|
+
function parseResetDateTime(humanDate, now) {
|
|
34267
|
+
const trimmed = humanDate.trim();
|
|
34268
|
+
const timeOnly = trimmed.match(TIME_ONLY);
|
|
34269
|
+
if (timeOnly) {
|
|
34270
|
+
const hm = parseAmPm(timeOnly[1], timeOnly[2], timeOnly[3]);
|
|
34271
|
+
if (!hm)
|
|
34272
|
+
return null;
|
|
34273
|
+
const todayAt = Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate(), hm.hour, hm.minute);
|
|
34274
|
+
return new Date(todayAt <= now.getTime() ? todayAt + MS_PER_DAY : todayAt);
|
|
34275
|
+
}
|
|
34276
|
+
const timeMatch = trimmed.match(TIME_TAIL);
|
|
34277
|
+
const dateOnly = timeMatch ? trimmed.slice(0, timeMatch.index).trim() : trimmed;
|
|
34278
|
+
const parts = dateOnly.split(/\s+/);
|
|
34279
|
+
if (parts.length !== 2)
|
|
34280
|
+
return null;
|
|
34281
|
+
const month = MONTHS.indexOf(parts[0].slice(0, 3).toLowerCase());
|
|
34282
|
+
if (month < 0)
|
|
34283
|
+
return null;
|
|
34284
|
+
const day = Number.parseInt(parts[1], 10);
|
|
34285
|
+
if (!Number.isFinite(day) || day < 1 || day > 31)
|
|
34286
|
+
return null;
|
|
34287
|
+
let hour = 0;
|
|
34288
|
+
let minute = 0;
|
|
34289
|
+
if (timeMatch) {
|
|
34290
|
+
const hm = parseAmPm(timeMatch[1], timeMatch[2], timeMatch[3]);
|
|
34291
|
+
if (!hm)
|
|
34292
|
+
return null;
|
|
34293
|
+
hour = hm.hour;
|
|
34294
|
+
minute = hm.minute;
|
|
34295
|
+
}
|
|
34296
|
+
const baseYear = now.getUTCFullYear();
|
|
34297
|
+
let resolved = null;
|
|
34298
|
+
let bestDelta = Number.POSITIVE_INFINITY;
|
|
34299
|
+
for (const y of [baseYear - 1, baseYear, baseYear + 1]) {
|
|
34300
|
+
const candidate = new Date(Date.UTC(y, month, day, hour, minute));
|
|
34301
|
+
const delta = Math.abs(candidate.getTime() - now.getTime());
|
|
34302
|
+
if (delta < bestDelta) {
|
|
34303
|
+
bestDelta = delta;
|
|
34304
|
+
resolved = candidate;
|
|
34305
|
+
}
|
|
34306
|
+
}
|
|
34307
|
+
return resolved;
|
|
34308
|
+
}
|
|
34146
34309
|
|
|
34147
34310
|
// ../core/dist/claude-code-usage/usage-limit-marker.js
|
|
34148
34311
|
var USAGE_LIMIT_MARKER_FILENAME = "claude-usage-limit.json";
|
|
@@ -34180,6 +34343,109 @@ function buildUsageLimitReplyText(limitedUntil) {
|
|
|
34180
34343
|
return `Your agent has hit its Claude Code usage limit until ${formatUtcClock(limitedUntil)}. It'll pick back up once the limit resets \u2014 please try again after then.`;
|
|
34181
34344
|
}
|
|
34182
34345
|
|
|
34346
|
+
// ../core/dist/claude-code-usage/rate-limit-classifier.js
|
|
34347
|
+
var UNKNOWN_RATE_LIMIT = Object.freeze({
|
|
34348
|
+
verdict: "unknown",
|
|
34349
|
+
atMs: null,
|
|
34350
|
+
resetsAt: null,
|
|
34351
|
+
text: null
|
|
34352
|
+
});
|
|
34353
|
+
function contentText(record2) {
|
|
34354
|
+
const candidates = [record2.content];
|
|
34355
|
+
const message = record2.message;
|
|
34356
|
+
if (typeof message === "object" && message !== null) {
|
|
34357
|
+
candidates.push(message.content);
|
|
34358
|
+
}
|
|
34359
|
+
const parts = [];
|
|
34360
|
+
for (const candidate of candidates) {
|
|
34361
|
+
if (typeof candidate === "string") {
|
|
34362
|
+
if (candidate)
|
|
34363
|
+
parts.push(candidate);
|
|
34364
|
+
continue;
|
|
34365
|
+
}
|
|
34366
|
+
if (!Array.isArray(candidate))
|
|
34367
|
+
continue;
|
|
34368
|
+
for (const block of candidate) {
|
|
34369
|
+
if (typeof block === "string") {
|
|
34370
|
+
if (block)
|
|
34371
|
+
parts.push(block);
|
|
34372
|
+
continue;
|
|
34373
|
+
}
|
|
34374
|
+
if (typeof block !== "object" || block === null)
|
|
34375
|
+
continue;
|
|
34376
|
+
const text = block.text;
|
|
34377
|
+
if (typeof text === "string" && text)
|
|
34378
|
+
parts.push(text);
|
|
34379
|
+
}
|
|
34380
|
+
}
|
|
34381
|
+
const joined = parts.join("\n").trim();
|
|
34382
|
+
return joined ? joined : null;
|
|
34383
|
+
}
|
|
34384
|
+
function classifyTranscriptLine(line, startMs, endMs, now) {
|
|
34385
|
+
const trimmed = line.trim();
|
|
34386
|
+
if (!trimmed)
|
|
34387
|
+
return null;
|
|
34388
|
+
let obj;
|
|
34389
|
+
try {
|
|
34390
|
+
obj = JSON.parse(trimmed);
|
|
34391
|
+
} catch {
|
|
34392
|
+
return null;
|
|
34393
|
+
}
|
|
34394
|
+
if (typeof obj !== "object" || obj === null)
|
|
34395
|
+
return null;
|
|
34396
|
+
const record2 = obj;
|
|
34397
|
+
if (record2.type !== "assistant")
|
|
34398
|
+
return null;
|
|
34399
|
+
const ts = record2.timestamp;
|
|
34400
|
+
if (typeof ts !== "string" || !ts)
|
|
34401
|
+
return null;
|
|
34402
|
+
const tsMs = new Date(ts).getTime();
|
|
34403
|
+
if (!Number.isFinite(tsMs) || tsMs < startMs || tsMs > endMs)
|
|
34404
|
+
return null;
|
|
34405
|
+
if (record2.error === "rate_limit" || record2.apiErrorStatus === 429) {
|
|
34406
|
+
const text = contentText(record2);
|
|
34407
|
+
const observation = text ? parseUsageBanner(text, now ?? new Date(endMs)) : null;
|
|
34408
|
+
return { verdict: "capped", atMs: tsMs, resetsAt: observation?.weekResetsAt ?? null, text };
|
|
34409
|
+
}
|
|
34410
|
+
if (record2.isApiErrorMessage === true)
|
|
34411
|
+
return null;
|
|
34412
|
+
const message = record2.message;
|
|
34413
|
+
if (typeof message !== "object" || message === null)
|
|
34414
|
+
return null;
|
|
34415
|
+
const msg = message;
|
|
34416
|
+
if (msg.model === "<synthetic>")
|
|
34417
|
+
return null;
|
|
34418
|
+
const usage = msg.usage;
|
|
34419
|
+
if (typeof usage !== "object" || usage === null)
|
|
34420
|
+
return null;
|
|
34421
|
+
const u = usage;
|
|
34422
|
+
const spent = Number(u.input_tokens ?? 0) + Number(u.output_tokens ?? 0) + Number(u.cache_creation_input_tokens ?? 0) + Number(u.cache_read_input_tokens ?? 0);
|
|
34423
|
+
if (!Number.isFinite(spent) || spent <= 0)
|
|
34424
|
+
return null;
|
|
34425
|
+
return { verdict: "serving", atMs: tsMs, resetsAt: null, text: null };
|
|
34426
|
+
}
|
|
34427
|
+
function pickNewerClassification(current, next) {
|
|
34428
|
+
if (next.verdict === "unknown")
|
|
34429
|
+
return current;
|
|
34430
|
+
if (current.verdict === "unknown")
|
|
34431
|
+
return next;
|
|
34432
|
+
return next.atMs >= current.atMs ? next : current;
|
|
34433
|
+
}
|
|
34434
|
+
function classifyTranscriptRateLimit(jsonl, startMs, endMs, now) {
|
|
34435
|
+
let newest = UNKNOWN_RATE_LIMIT;
|
|
34436
|
+
for (const line of jsonl.split("\n")) {
|
|
34437
|
+
const classified = classifyTranscriptLine(line, startMs, endMs, now);
|
|
34438
|
+
if (classified)
|
|
34439
|
+
newest = pickNewerClassification(newest, classified);
|
|
34440
|
+
}
|
|
34441
|
+
return newest;
|
|
34442
|
+
}
|
|
34443
|
+
|
|
34444
|
+
// ../core/dist/claude-code-usage/transcript-location.js
|
|
34445
|
+
function encodeClaudeProjectPath(projectDir) {
|
|
34446
|
+
return "-" + projectDir.replace(/^\//, "").replace(/[/.]/g, "-");
|
|
34447
|
+
}
|
|
34448
|
+
|
|
34183
34449
|
// ../core/dist/account-enforcement/marker.js
|
|
34184
34450
|
var ACCOUNT_ENFORCEMENT_MARKER_FILENAME = "account-enforcement.json";
|
|
34185
34451
|
var ACCOUNT_ENFORCEMENT_MARKER_VERSION = 1;
|
|
@@ -34824,6 +35090,33 @@ var FLAG_REGISTRY = [
|
|
|
34824
35090
|
// projectDefinition, so setting it does not roll FLAGS_SCHEMA_VERSION.
|
|
34825
35091
|
since: "0.28.421"
|
|
34826
35092
|
},
|
|
35093
|
+
{
|
|
35094
|
+
key: "usage-limit-reactive-notice",
|
|
35095
|
+
description: "Report a Claude Code usage cap REACTIVELY instead of predicting it (ENG-8201). Today the manager guesses from the agent transcript that the next turn will be refused, writes a marker, and every channel MCP refuses to dispatch on the strength of that guess - so a wrong guess is a swallowed message, and because the notice is throttled per (channel, sender) while the DROP is not, the usual symptom is total silence rather than a wrong reply. A refused turn actually costs nothing (rejected in under a second, zero tokens) and Claude Code records it with the reset time in it, so there is no need to guess: dispatch, and report the refusal if one comes back. off = today behaviour (pre-dispatch marker gate, no watcher). shadow = still gate on the marker, but ALSO watch dispatched messages and log the refusal that would have been reported - measures the true-positive rate with no user-visible change. enforce = stop reading the marker before dispatch; every admitted human message reaches the agent and a refusal is answered in-thread with the reset time from the error itself. Read live from the heartbeat flags-cache (or the env override).",
|
|
35096
|
+
flagType: "enum",
|
|
35097
|
+
allowedValues: ["off", "shadow", "enforce"],
|
|
35098
|
+
// Ships dark: off preserves today's gate byte-for-byte. shadow is a free
|
|
35099
|
+
// local log line (the watch is a transcript read, no model spend), so it is a
|
|
35100
|
+
// cheap soak; enforce changes what reaches the agent, so it is the audited
|
|
35101
|
+
// per-org flip.
|
|
35102
|
+
defaultValue: "off",
|
|
35103
|
+
// Enum override AGT_USAGE_LIMIT_REACTIVE_MODE (off|shadow|enforce), resolved
|
|
35104
|
+
// by resolveUsageLimitReactiveMode in the channel-server bundle.
|
|
35105
|
+
envVar: "AGT_USAGE_LIMIT_REACTIVE_MODE",
|
|
35106
|
+
// enforce sends a message to an agent the host currently believes is capped -
|
|
35107
|
+
// a deliberate availability trade (the refusal is free, but it is still a
|
|
35108
|
+
// dispatch the operator previously suppressed), so flipping toward it is an
|
|
35109
|
+
// audited change (ADR-0022 sensitive-flag confirm).
|
|
35110
|
+
sensitive: true
|
|
35111
|
+
// ENG-8149: `since` is the agt-cli version that first carries
|
|
35112
|
+
// resolveUsageLimitReactiveMode + the MCP watcher. It cannot be known before
|
|
35113
|
+
// this lands (the auto-publish patch-bumps on merge), and an undefined
|
|
35114
|
+
// `since` makes the flip-reach modal claim FULL reach - true for flags that
|
|
35115
|
+
// predate the reach work, wrong for a NEW host-read flag whose reader older
|
|
35116
|
+
// hosts simply do not have. Backfilled by a follow-up commit once the
|
|
35117
|
+
// publishing run reports the version. Excluded from projectDefinition, so
|
|
35118
|
+
// setting it does not roll FLAGS_SCHEMA_VERSION.
|
|
35119
|
+
},
|
|
34827
35120
|
{
|
|
34828
35121
|
key: "slack-hot-thread-guard",
|
|
34829
35122
|
description: "Server-side hot-thread guard on the slack.reply surface (ENG-7462). Prevents an agent posting a NEW top-level Slack message when it meant to reply inside the thread it is already working in - a prompt/memory rule proved insufficient (the agent had the rule and still slipped). When a reply would otherwise post to channel ROOT (no thread_ts / message_ts / inbound_id, no active kanban card) and the agent has a recent active thread in that channel (its last bot-posted thread, from the persisted trackedThreads cache, within a freshness window), the reply is redirected into that thread. proactive:true no longer implies channel root; posting at root becomes a deliberate action (the to_channel_root flag, or the thread_ts:null sentinel). off = guard never runs, replies with no coords root exactly as today (ships dark). shadow = compute + log the would-redirect but STILL post to root (measure the fire rate before acting). enforce = apply the redirect (a soft-block: redirect + inform, never a hard rejection). Read live from the heartbeat flags-cache (or the env override); enforce is a deliberate per-org flip after a shadow soak.",
|
|
@@ -35547,6 +35840,75 @@ function shouldSendAccountWarn(opts) {
|
|
|
35547
35840
|
}).reply;
|
|
35548
35841
|
}
|
|
35549
35842
|
|
|
35843
|
+
// src/rate-limit-watch.ts
|
|
35844
|
+
import { readFileSync as readFileSync11, readdirSync as readdirSync4, statSync as statSync2 } from "fs";
|
|
35845
|
+
import { homedir as homedir4 } from "os";
|
|
35846
|
+
import { join as join12 } from "path";
|
|
35847
|
+
var DEFAULT_WATCH_MS = 5e3;
|
|
35848
|
+
var DEFAULT_POLL_MS = 400;
|
|
35849
|
+
function agentTranscriptDir(opts) {
|
|
35850
|
+
const cwd = opts?.cwd ?? process.cwd();
|
|
35851
|
+
const home = opts?.home ?? homedir4();
|
|
35852
|
+
return join12(home, ".claude", "projects", encodeClaudeProjectPath(cwd));
|
|
35853
|
+
}
|
|
35854
|
+
function classifyTranscriptSince(opts) {
|
|
35855
|
+
const dir = opts.transcriptDir ?? agentTranscriptDir({ cwd: opts.cwd, home: opts.home });
|
|
35856
|
+
let entries;
|
|
35857
|
+
try {
|
|
35858
|
+
entries = readdirSync4(dir);
|
|
35859
|
+
} catch {
|
|
35860
|
+
return UNKNOWN_RATE_LIMIT;
|
|
35861
|
+
}
|
|
35862
|
+
let newest = UNKNOWN_RATE_LIMIT;
|
|
35863
|
+
for (const name of entries) {
|
|
35864
|
+
if (!name.endsWith(".jsonl")) continue;
|
|
35865
|
+
const path = join12(dir, name);
|
|
35866
|
+
try {
|
|
35867
|
+
const st = statSync2(path);
|
|
35868
|
+
if (!st.isFile() || st.mtimeMs < opts.sinceMs) continue;
|
|
35869
|
+
} catch {
|
|
35870
|
+
continue;
|
|
35871
|
+
}
|
|
35872
|
+
let content;
|
|
35873
|
+
try {
|
|
35874
|
+
content = readFileSync11(path, "utf-8");
|
|
35875
|
+
} catch {
|
|
35876
|
+
continue;
|
|
35877
|
+
}
|
|
35878
|
+
newest = pickNewerClassification(
|
|
35879
|
+
newest,
|
|
35880
|
+
classifyTranscriptRateLimit(content, opts.sinceMs, opts.nowMs, new Date(opts.nowMs))
|
|
35881
|
+
);
|
|
35882
|
+
}
|
|
35883
|
+
return newest;
|
|
35884
|
+
}
|
|
35885
|
+
var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
35886
|
+
async function watchForRateLimitRefusal(opts) {
|
|
35887
|
+
const now = opts.now ?? (() => Date.now());
|
|
35888
|
+
const wait = opts.wait ?? sleep;
|
|
35889
|
+
const timeoutMs = opts.timeoutMs ?? DEFAULT_WATCH_MS;
|
|
35890
|
+
const pollMs = opts.pollMs ?? DEFAULT_POLL_MS;
|
|
35891
|
+
const deadline = now() + timeoutMs;
|
|
35892
|
+
for (; ; ) {
|
|
35893
|
+
let result;
|
|
35894
|
+
try {
|
|
35895
|
+
result = classifyTranscriptSince({
|
|
35896
|
+
sinceMs: opts.sinceMs,
|
|
35897
|
+
nowMs: now(),
|
|
35898
|
+
transcriptDir: opts.transcriptDir,
|
|
35899
|
+
cwd: opts.cwd,
|
|
35900
|
+
home: opts.home
|
|
35901
|
+
});
|
|
35902
|
+
} catch {
|
|
35903
|
+
return null;
|
|
35904
|
+
}
|
|
35905
|
+
if (result.verdict === "capped") return result;
|
|
35906
|
+
if (result.verdict === "serving") return null;
|
|
35907
|
+
if (now() >= deadline) return null;
|
|
35908
|
+
await wait(pollMs);
|
|
35909
|
+
}
|
|
35910
|
+
}
|
|
35911
|
+
|
|
35550
35912
|
// src/direct-chat-channel.ts
|
|
35551
35913
|
var DIRECT_CHAT_MAINTENANCE_CACHE = /* @__PURE__ */ new Map();
|
|
35552
35914
|
var DIRECT_CHAT_MAINTENANCE_COOLDOWN_MS = (() => {
|
|
@@ -35565,21 +35927,21 @@ var DIRECT_CHAT_ACCOUNT_WARN_COOLDOWN_MS = (() => {
|
|
|
35565
35927
|
var AGT_HOST = process.env.AGT_HOST;
|
|
35566
35928
|
var AGT_API_KEY = process.env.AGT_API_KEY;
|
|
35567
35929
|
var AGT_AGENT_ID = process.env.AGT_AGENT_ID;
|
|
35568
|
-
var DIRECT_CHAT_AGENT_DIR = AGT_AGENT_ID ?
|
|
35930
|
+
var DIRECT_CHAT_AGENT_DIR = AGT_AGENT_ID ? join13(homedir5(), ".augmented", AGT_AGENT_ID) : null;
|
|
35569
35931
|
var INBOUND_ATTACHMENTS_DIR = resolveInboundAttachmentsDir({
|
|
35570
35932
|
codeName: process.env.AGT_AGENT_CODE_NAME,
|
|
35571
35933
|
turnInitiatorFile: process.env.AGT_TURN_INITIATOR_FILE,
|
|
35572
35934
|
agentId: AGT_AGENT_ID,
|
|
35573
|
-
homeDir:
|
|
35935
|
+
homeDir: homedir5()
|
|
35574
35936
|
});
|
|
35575
35937
|
var AGT_AGENT_CODE_NAME = process.env.AGT_AGENT_CODE_NAME;
|
|
35576
35938
|
var directChatStderrLogStream = null;
|
|
35577
35939
|
if (AGT_AGENT_CODE_NAME) {
|
|
35578
35940
|
try {
|
|
35579
|
-
const logDir =
|
|
35941
|
+
const logDir = join13(homedir5(), ".augmented", AGT_AGENT_CODE_NAME);
|
|
35580
35942
|
mkdirSync5(logDir, { recursive: true });
|
|
35581
35943
|
directChatStderrLogStream = createWriteStream(
|
|
35582
|
-
|
|
35944
|
+
join13(logDir, "direct-chat-channel-stderr.log"),
|
|
35583
35945
|
{ flags: "a", mode: 384 }
|
|
35584
35946
|
);
|
|
35585
35947
|
directChatStderrLogStream.on("error", () => {
|
|
@@ -35598,10 +35960,10 @@ if (AGT_AGENT_CODE_NAME) {
|
|
|
35598
35960
|
} catch {
|
|
35599
35961
|
}
|
|
35600
35962
|
}
|
|
35601
|
-
var PROGRESS_HEARTBEAT_PATH = AGT_AGENT_CODE_NAME ?
|
|
35602
|
-
var DIRECT_CHAT_PENDING_INBOUND_DIR = AGT_AGENT_CODE_NAME ?
|
|
35603
|
-
var DIRECT_CHAT_DELIVERY_LEDGER_DIR = AGT_AGENT_CODE_NAME ?
|
|
35604
|
-
var DIRECT_CHAT_CODE_NAME_AGENT_DIR = AGT_AGENT_CODE_NAME ?
|
|
35963
|
+
var PROGRESS_HEARTBEAT_PATH = AGT_AGENT_CODE_NAME ? join13(homedir5(), ".augmented", AGT_AGENT_CODE_NAME, "channel-progress-heartbeat.json") : null;
|
|
35964
|
+
var DIRECT_CHAT_PENDING_INBOUND_DIR = AGT_AGENT_CODE_NAME ? join13(homedir5(), ".augmented", AGT_AGENT_CODE_NAME, "direct-chat-pending-inbound") : null;
|
|
35965
|
+
var DIRECT_CHAT_DELIVERY_LEDGER_DIR = AGT_AGENT_CODE_NAME ? join13(homedir5(), ".augmented", AGT_AGENT_CODE_NAME, ".agt-inbound-delivery-ledger") : null;
|
|
35966
|
+
var DIRECT_CHAT_CODE_NAME_AGENT_DIR = AGT_AGENT_CODE_NAME ? join13(homedir5(), ".augmented", AGT_AGENT_CODE_NAME) : null;
|
|
35605
35967
|
var DIRECT_CHAT_STALE_MARKER_MS = 24 * 60 * 60 * 1e3;
|
|
35606
35968
|
function recordDirectChatDelivery(sessionId, messageIds) {
|
|
35607
35969
|
if (!sessionId) return;
|
|
@@ -35612,8 +35974,8 @@ function recordDirectChatDelivery(sessionId, messageIds) {
|
|
|
35612
35974
|
delivered_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
35613
35975
|
});
|
|
35614
35976
|
}
|
|
35615
|
-
var DIRECT_CHAT_RECOVERY_OUTBOX_DIR = AGT_AGENT_CODE_NAME ?
|
|
35616
|
-
var DIRECT_CHAT_RECOVERY_LEDGER_DIR = AGT_AGENT_CODE_NAME ?
|
|
35977
|
+
var DIRECT_CHAT_RECOVERY_OUTBOX_DIR = AGT_AGENT_CODE_NAME ? join13(homedir5(), ".augmented", AGT_AGENT_CODE_NAME, "direct-chat-recovery-outbox") : null;
|
|
35978
|
+
var DIRECT_CHAT_RECOVERY_LEDGER_DIR = AGT_AGENT_CODE_NAME ? join13(homedir5(), ".augmented", AGT_AGENT_CODE_NAME, ".agt-direct-chat-recovery-ledger") : null;
|
|
35617
35979
|
var progressReceivedAt = /* @__PURE__ */ new Map();
|
|
35618
35980
|
var directChatProgressState = { tracked: null };
|
|
35619
35981
|
var directChatProgressTickRunning = false;
|
|
@@ -35632,7 +35994,7 @@ var directChatKanbanCardClient = createKanbanCardActiveClient({
|
|
|
35632
35994
|
function readProgressHeartbeat() {
|
|
35633
35995
|
if (!PROGRESS_HEARTBEAT_PATH || !existsSync6(PROGRESS_HEARTBEAT_PATH)) return null;
|
|
35634
35996
|
try {
|
|
35635
|
-
return parseProgressHeartbeat(
|
|
35997
|
+
return parseProgressHeartbeat(readFileSync12(PROGRESS_HEARTBEAT_PATH, "utf-8"));
|
|
35636
35998
|
} catch {
|
|
35637
35999
|
return null;
|
|
35638
36000
|
}
|
|
@@ -35641,7 +36003,7 @@ function seedProgressHeartbeat() {
|
|
|
35641
36003
|
if (!PROGRESS_HEARTBEAT_PATH) return;
|
|
35642
36004
|
const tmp = `${PROGRESS_HEARTBEAT_PATH}.${process.pid}.tmp`;
|
|
35643
36005
|
try {
|
|
35644
|
-
mkdirSync5(
|
|
36006
|
+
mkdirSync5(join13(homedir5(), ".augmented", AGT_AGENT_CODE_NAME), { recursive: true });
|
|
35645
36007
|
writeFileSync6(tmp, serializeProgressHeartbeat(SEED_PROGRESS_STEP, Date.now()), { mode: 384 });
|
|
35646
36008
|
renameSync5(tmp, PROGRESS_HEARTBEAT_PATH);
|
|
35647
36009
|
} catch {
|
|
@@ -35762,7 +36124,7 @@ var STREAM_REPLY_FLUSH_MS = (() => {
|
|
|
35762
36124
|
const n = raw ? Number(raw) : NaN;
|
|
35763
36125
|
return Number.isFinite(n) && n >= 0 ? n : 60;
|
|
35764
36126
|
})();
|
|
35765
|
-
var
|
|
36127
|
+
var sleep2 = (ms) => ms > 0 ? new Promise((resolve) => setTimeout(resolve, ms)) : Promise.resolve();
|
|
35766
36128
|
async function apiPost(path, body) {
|
|
35767
36129
|
const token = await getAuthToken();
|
|
35768
36130
|
const res = await fetchWithTimeout(`${AGT_HOST}${path}`, {
|
|
@@ -35833,7 +36195,7 @@ var inboundAttachmentDeps = {
|
|
|
35833
36195
|
},
|
|
35834
36196
|
ensureDir: (dir) => mkdirSync5(dir, { recursive: true }),
|
|
35835
36197
|
writeFile: (path, bytes) => writeFileSync6(path, bytes, { mode: 384 }),
|
|
35836
|
-
joinPath: (...parts) =>
|
|
36198
|
+
joinPath: (...parts) => join13(...parts),
|
|
35837
36199
|
warn: (msg) => process.stderr.write(`${msg}
|
|
35838
36200
|
`)
|
|
35839
36201
|
};
|
|
@@ -36012,11 +36374,11 @@ mcp.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
36012
36374
|
recordDirectChatDelivery(session_id, message_ids);
|
|
36013
36375
|
const messageId = data.message_id;
|
|
36014
36376
|
for (let i = 1; i < slices.length; i++) {
|
|
36015
|
-
await
|
|
36377
|
+
await sleep2(STREAM_REPLY_FLUSH_MS);
|
|
36016
36378
|
const isFinal = i === slices.length - 1;
|
|
36017
36379
|
let ok = await postDirectChatUpdate(messageId, session_id, slices[i]);
|
|
36018
36380
|
if (!ok && isFinal) {
|
|
36019
|
-
await
|
|
36381
|
+
await sleep2(STREAM_REPLY_FLUSH_MS);
|
|
36020
36382
|
ok = await postDirectChatUpdate(messageId, session_id, slices[i]);
|
|
36021
36383
|
if (!ok) {
|
|
36022
36384
|
return {
|
|
@@ -36165,7 +36527,7 @@ function scheduleDirectChatBusyAck(sessionId, messageId, arrivedWhileBusy) {
|
|
|
36165
36527
|
let paneLogFreshAgeMs = null;
|
|
36166
36528
|
if (DIRECT_CHAT_CODE_NAME_AGENT_DIR) {
|
|
36167
36529
|
try {
|
|
36168
|
-
const paneMtimeMs =
|
|
36530
|
+
const paneMtimeMs = statSync3(join13(DIRECT_CHAT_CODE_NAME_AGENT_DIR, "pane.log")).mtimeMs;
|
|
36169
36531
|
paneLogFreshAgeMs = Math.max(0, Date.now() - paneMtimeMs);
|
|
36170
36532
|
} catch {
|
|
36171
36533
|
}
|
|
@@ -36213,6 +36575,63 @@ function scheduleDirectChatBusyAck(sessionId, messageId, arrivedWhileBusy) {
|
|
|
36213
36575
|
}, thresholdMs);
|
|
36214
36576
|
timer.unref();
|
|
36215
36577
|
}
|
|
36578
|
+
function armUsageLimitWatch(args) {
|
|
36579
|
+
void (async () => {
|
|
36580
|
+
try {
|
|
36581
|
+
const refusal = await watchForRateLimitRefusal({ sinceMs: args.sinceMs });
|
|
36582
|
+
if (!refusal) return;
|
|
36583
|
+
if (!refusal.resetsAt) {
|
|
36584
|
+
process.stderr.write(
|
|
36585
|
+
`direct-chat-channel: [usage-limit-reactive] mode=${args.mode} refusal detected for session=${args.sessionId} but no reset time in it (${refusal.text ?? "no text"}) - no notice
|
|
36586
|
+
`
|
|
36587
|
+
);
|
|
36588
|
+
return;
|
|
36589
|
+
}
|
|
36590
|
+
if (args.mode === "shadow") {
|
|
36591
|
+
process.stderr.write(
|
|
36592
|
+
`direct-chat-channel: [usage-limit-reactive] mode=shadow WOULD notify session=${args.sessionId} message=${args.messageId} resets=${refusal.resetsAt.toISOString()}
|
|
36593
|
+
`
|
|
36594
|
+
);
|
|
36595
|
+
return;
|
|
36596
|
+
}
|
|
36597
|
+
const throttleKey = `${args.sessionId}|usage_limit`;
|
|
36598
|
+
const throttle = decideDeclineReply({
|
|
36599
|
+
cache: DIRECT_CHAT_USAGE_LIMIT_CACHE,
|
|
36600
|
+
key: throttleKey,
|
|
36601
|
+
cooldownMs: DIRECT_CHAT_MAINTENANCE_COOLDOWN_MS,
|
|
36602
|
+
now: Date.now()
|
|
36603
|
+
});
|
|
36604
|
+
const route = throttle.reply ? "/host/direct-chat/reply" : "/host/direct-chat/consume";
|
|
36605
|
+
const body = throttle.reply ? {
|
|
36606
|
+
agent_id: AGT_AGENT_ID,
|
|
36607
|
+
session_id: args.sessionId,
|
|
36608
|
+
content: buildUsageLimitReplyText(refusal.resetsAt),
|
|
36609
|
+
message_ids: [args.messageId]
|
|
36610
|
+
} : { agent_id: AGT_AGENT_ID, session_id: args.sessionId, message_ids: [args.messageId] };
|
|
36611
|
+
const res = await apiPost(route, body);
|
|
36612
|
+
const data = await res.json().catch(() => ({}));
|
|
36613
|
+
if (res.ok && data.error == null) {
|
|
36614
|
+
claimTracker.clear(args.sessionId, [args.messageId]);
|
|
36615
|
+
clearDirectChatPendingMarkersForSession(DIRECT_CHAT_PENDING_INBOUND_DIR, args.sessionId);
|
|
36616
|
+
process.stderr.write(
|
|
36617
|
+
`direct-chat-channel: [usage-limit-reactive] ${throttle.reply ? "reported" : "consumed (notice throttled)"} refusal for session=${args.sessionId} message=${args.messageId} resets=${refusal.resetsAt.toISOString()}
|
|
36618
|
+
`
|
|
36619
|
+
);
|
|
36620
|
+
} else {
|
|
36621
|
+
if (throttle.reply) DIRECT_CHAT_USAGE_LIMIT_CACHE.delete(throttleKey);
|
|
36622
|
+
process.stderr.write(
|
|
36623
|
+
`direct-chat-channel: [usage-limit-reactive] ${throttle.reply ? "notice" : "consume"} failed for session=${args.sessionId}: ${data.error ?? `HTTP ${res.status}`}
|
|
36624
|
+
`
|
|
36625
|
+
);
|
|
36626
|
+
}
|
|
36627
|
+
} catch (err) {
|
|
36628
|
+
process.stderr.write(
|
|
36629
|
+
`direct-chat-channel: [usage-limit-reactive] watch failed for session=${args.sessionId}: ${err.message}
|
|
36630
|
+
`
|
|
36631
|
+
);
|
|
36632
|
+
}
|
|
36633
|
+
})();
|
|
36634
|
+
}
|
|
36216
36635
|
async function pollForMessages(sinceMs) {
|
|
36217
36636
|
try {
|
|
36218
36637
|
const res = await apiPost("/host/direct-chat/poll", {
|
|
@@ -36239,7 +36658,8 @@ async function pollForMessages(sinceMs) {
|
|
|
36239
36658
|
const isNotice = msg.kind === "notice";
|
|
36240
36659
|
const maintenanceActive = !isNotice && isMaintenanceModeActive();
|
|
36241
36660
|
const accountMuted = !isNotice && !maintenanceActive ? readAccountEnforcementLevel({ codeName: AGT_AGENT_CODE_NAME ?? null }) === "mute" : false;
|
|
36242
|
-
const
|
|
36661
|
+
const reactiveMode = resolveUsageLimitReactiveMode();
|
|
36662
|
+
const usageLimitUntil = !isNotice && !maintenanceActive && !accountMuted && reactiveMode !== "enforce" ? readUsageLimitUntil({ codeName: AGT_AGENT_CODE_NAME ?? null }) : null;
|
|
36243
36663
|
if (!isNotice && !maintenanceActive && !accountMuted && !usageLimitUntil) {
|
|
36244
36664
|
markProcessed(msg.id);
|
|
36245
36665
|
claimTracker.track(msg.session_id, msg.id);
|
|
@@ -36357,6 +36777,7 @@ async function pollForMessages(sinceMs) {
|
|
|
36357
36777
|
if (!isNotice && msg.sender_id) {
|
|
36358
36778
|
clearScheduledTurnMarker(DIRECT_CHAT_CODE_NAME_AGENT_DIR);
|
|
36359
36779
|
}
|
|
36780
|
+
const dispatchedAtMs = Date.now();
|
|
36360
36781
|
await mcp.notification({
|
|
36361
36782
|
method: "notifications/claude/channel",
|
|
36362
36783
|
params: {
|
|
@@ -36417,6 +36838,14 @@ async function pollForMessages(sinceMs) {
|
|
|
36417
36838
|
`direct-chat-channel: Injected message ${msg.id} (session=${msg.session_id})
|
|
36418
36839
|
`
|
|
36419
36840
|
);
|
|
36841
|
+
if (!isNotice && reactiveMode !== "off") {
|
|
36842
|
+
armUsageLimitWatch({
|
|
36843
|
+
mode: reactiveMode,
|
|
36844
|
+
sessionId: msg.session_id,
|
|
36845
|
+
messageId: msg.id,
|
|
36846
|
+
sinceMs: dispatchedAtMs
|
|
36847
|
+
});
|
|
36848
|
+
}
|
|
36420
36849
|
}
|
|
36421
36850
|
} catch (err) {
|
|
36422
36851
|
process.stderr.write(
|
|
@@ -36522,8 +36951,8 @@ function sweepAgedDirectChatMarkersNow(thresholdMs) {
|
|
|
36522
36951
|
if (!DIRECT_CHAT_PENDING_INBOUND_DIR) return;
|
|
36523
36952
|
const now = Date.now();
|
|
36524
36953
|
const res = sweepAgedDirectChatMarkers(DIRECT_CHAT_PENDING_INBOUND_DIR, {
|
|
36525
|
-
readdir: (dir) =>
|
|
36526
|
-
readFile: (p2) =>
|
|
36954
|
+
readdir: (dir) => readdirSync5(dir),
|
|
36955
|
+
readFile: (p2) => readFileSync12(p2, "utf8"),
|
|
36527
36956
|
unlink: (p2) => {
|
|
36528
36957
|
if (existsSync6(p2)) unlinkSync4(p2);
|
|
36529
36958
|
},
|
|
@@ -36559,7 +36988,7 @@ function sanitizeRecoveryText(text) {
|
|
|
36559
36988
|
}
|
|
36560
36989
|
function directChatRecoveryDeps() {
|
|
36561
36990
|
return {
|
|
36562
|
-
readFile: (p2) =>
|
|
36991
|
+
readFile: (p2) => readFileSync12(p2, "utf-8"),
|
|
36563
36992
|
renameFile: (from, to) => renameSync5(from, to),
|
|
36564
36993
|
unlinkFile: (p2) => {
|
|
36565
36994
|
if (existsSync6(p2)) unlinkSync4(p2);
|
|
@@ -36572,7 +37001,7 @@ function directChatRecoveryDeps() {
|
|
|
36572
37001
|
if (!DIRECT_CHAT_RECOVERY_LEDGER_DIR) return;
|
|
36573
37002
|
if (markerName.includes("/") || markerName.includes("\\") || markerName.includes("..")) return;
|
|
36574
37003
|
try {
|
|
36575
|
-
const p2 =
|
|
37004
|
+
const p2 = join13(DIRECT_CHAT_RECOVERY_LEDGER_DIR, markerName);
|
|
36576
37005
|
if (existsSync6(p2)) unlinkSync4(p2);
|
|
36577
37006
|
} catch {
|
|
36578
37007
|
}
|
|
@@ -36608,7 +37037,7 @@ async function processDirectChatRecoveryOutboxFile(filename) {
|
|
|
36608
37037
|
if (!enabled) return;
|
|
36609
37038
|
directChatRecoveryInFlight.add(filename);
|
|
36610
37039
|
try {
|
|
36611
|
-
const fullPath =
|
|
37040
|
+
const fullPath = join13(DIRECT_CHAT_RECOVERY_OUTBOX_DIR, filename);
|
|
36612
37041
|
await consumeDirectChatRecoveryFile(fullPath, filename, directChatRecoveryDeps());
|
|
36613
37042
|
} catch (err) {
|
|
36614
37043
|
process.stderr.write(
|
|
@@ -36626,7 +37055,7 @@ if (DIRECT_CHAT_RECOVERY_OUTBOX_DIR) {
|
|
|
36626
37055
|
} catch {
|
|
36627
37056
|
}
|
|
36628
37057
|
try {
|
|
36629
|
-
for (const f of
|
|
37058
|
+
for (const f of readdirSync5(DIRECT_CHAT_RECOVERY_OUTBOX_DIR)) {
|
|
36630
37059
|
if (f.endsWith(".json")) void processDirectChatRecoveryOutboxFile(f);
|
|
36631
37060
|
}
|
|
36632
37061
|
} catch {
|
|
@@ -36636,7 +37065,7 @@ if (DIRECT_CHAT_RECOVERY_OUTBOX_DIR) {
|
|
|
36636
37065
|
if (!filename) return;
|
|
36637
37066
|
const name = filename.toString();
|
|
36638
37067
|
if (!name.endsWith(".json")) return;
|
|
36639
|
-
if (existsSync6(
|
|
37068
|
+
if (existsSync6(join13(DIRECT_CHAT_RECOVERY_OUTBOX_DIR, name))) {
|
|
36640
37069
|
void processDirectChatRecoveryOutboxFile(name);
|
|
36641
37070
|
}
|
|
36642
37071
|
});
|