opencode-swarm 7.153.2 → 7.154.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/background/pending-delegations.d.ts +29 -0
- package/dist/background/pr-subscriptions.d.ts +258 -27
- package/dist/cli/{config-doctor-tv0ar88m.js → config-doctor-8p9y543t.js} +2 -2
- package/dist/cli/{core-kmsm50zn.js → core-1h6x6k59.js} +1 -1
- package/dist/cli/{curation-policy-twa6agf8.js → curation-policy-9r902jw4.js} +6 -6
- package/dist/cli/{curator-llm-factory-dxkhdhwg.js → curator-llm-factory-ypwf3a90.js} +29 -29
- package/dist/cli/{curator-wxe863k9.js → curator-yj00z1qz.js} +29 -29
- package/dist/cli/{evidence-summary-service-9akrdg9t.js → evidence-summary-service-npq04240.js} +12 -12
- package/dist/cli/{gate-evidence-bdzqbg2y.js → gate-evidence-4b6t0ycn.js} +6 -6
- package/dist/cli/{guardrail-explain-kx9rem51.js → guardrail-explain-dyxbhrdj.js} +30 -30
- package/dist/cli/{guardrail-log-zxzyx1de.js → guardrail-log-eyecsekk.js} +5 -5
- package/dist/cli/{guardrail-reset-1mc3st34.js → guardrail-reset-269jdwhd.js} +29 -29
- package/dist/cli/{hive-promoter-gncwtq0z.js → hive-promoter-5936am48.js} +29 -29
- package/dist/cli/{index-hhv2pvab.js → index-10zvxqra.js} +1 -1
- package/dist/cli/{index-j4f8sz67.js → index-1tdsddab.js} +192 -102
- package/dist/cli/{index-kenz5bja.js → index-2kzxkx50.js} +31 -31
- package/dist/cli/{index-3bh4h6ve.js → index-2mbs5q26.js} +7 -7
- package/dist/cli/{index-x3y6zhgj.js → index-3db7xza0.js} +1 -1
- package/dist/cli/{index-h3zhb18p.js → index-3hdnfmhp.js} +2 -2
- package/dist/cli/{index-84m4per1.js → index-3mhgv8d6.js} +2 -2
- package/dist/cli/{index-21pt4jwf.js → index-48m4841x.js} +1 -1
- package/dist/cli/{index-ebshrg4x.js → index-4g00jhbs.js} +3 -3
- package/dist/cli/{index-5zp4c36c.js → index-4y7g04py.js} +1 -1
- package/dist/cli/{index-vhbds89e.js → index-68w5jxv5.js} +4 -4
- package/dist/cli/{index-4pjkdcgc.js → index-6yc06dx9.js} +1 -1
- package/dist/cli/{index-2mqmvxac.js → index-7jnh1sgb.js} +7 -1
- package/dist/cli/{index-pk1gbqmq.js → index-c6dfyzdh.js} +1 -1
- package/dist/cli/{index-td0jz8xe.js → index-c809a69b.js} +2 -2
- package/dist/cli/{index-0azn976d.js → index-dwh02jmv.js} +7 -7
- package/dist/cli/{index-3cegckkq.js → index-garsang6.js} +1 -1
- package/dist/cli/{index-bw55r5bz.js → index-hk4wa0v1.js} +6 -6
- package/dist/cli/{index-5ad8xcv3.js → index-mddgkqg9.js} +2 -2
- package/dist/cli/{index-s6b8jgtn.js → index-nkr634n6.js} +1 -1
- package/dist/cli/{index-3fpqpyp6.js → index-p4y7jefb.js} +2 -2
- package/dist/cli/{index-kvk65qzz.js → index-pcj7jk4t.js} +3 -3
- package/dist/cli/{index-56ejcj07.js → index-qcz4xprd.js} +4 -4
- package/dist/cli/{index-zppxpc3y.js → index-s0a4ewkb.js} +2 -2
- package/dist/cli/{index-h06112sh.js → index-s70tmqn3.js} +5 -5
- package/dist/cli/{index-yvepnbyd.js → index-snnps1vm.js} +5 -5
- package/dist/cli/{index-a58nmnmz.js → index-tnm9bb8b.js} +5 -3
- package/dist/cli/{index-x8vw6ap1.js → index-ttqnhxxq.js} +59 -42
- package/dist/cli/{index-891g7hbf.js → index-vtdwkfbv.js} +1 -1
- package/dist/cli/index-wfxg3s7n.js +1158 -0
- package/dist/cli/{index-02rbfrab.js → index-wpstzk85.js} +4 -4
- package/dist/cli/{index-1gzdnjg9.js → index-y64jrbch.js} +2 -2
- package/dist/cli/{index-3ttktt56.js → index-yve4e8fx.js} +1 -1
- package/dist/cli/index.js +29 -29
- package/dist/cli/{knowledge-escalator-gp3mpbqe.js → knowledge-escalator-bxpzeth0.js} +11 -11
- package/dist/cli/{knowledge-events-nvkhtqvn.js → knowledge-events-jxdpg2hj.js} +9 -9
- package/dist/cli/{knowledge-link-ge0rddxw.js → knowledge-link-nwxykjya.js} +5 -5
- package/dist/cli/{knowledge-store-2ngh8xvv.js → knowledge-store-336hyzs8.js} +6 -6
- package/dist/cli/{knowledge-validator-4bphyrec.js → knowledge-validator-9wkvnsv0.js} +8 -8
- package/dist/cli/{pending-delegations-nk78wx7h.js → pending-delegations-0fmj1qy6.js} +3 -3
- package/dist/cli/{pr-subscriptions-dq1f61ye.js → pr-subscriptions-1ndwgtbw.js} +15 -4
- package/dist/cli/{runner-n50w1y4w.js → runner-06dvm53x.js} +6 -6
- package/dist/cli/{scan-cursor-c16xtygz.js → scan-cursor-3xxqtxwx.js} +7 -7
- package/dist/cli/{schema-83tbte2g.js → schema-jdx83m4m.js} +1 -1
- package/dist/cli/{scope-persistence-gdbw8xh8.js → scope-persistence-wgxn2kba.js} +8 -8
- package/dist/cli/{skill-generator-gr13xeha.js → skill-generator-vmxsc2fm.js} +13 -13
- package/dist/cli/{telemetry-hh1j99za.js → telemetry-q2km2zrz.js} +1 -1
- package/dist/cli/{worktree-collision-ownership-4drcjymy.js → worktree-collision-ownership-gsay5210.js} +5 -5
- package/dist/cli/{worktree-isolation-jjkfg2pg.js → worktree-isolation-peb4bkg3.js} +29 -29
- package/dist/commands/pr-monitor-status.d.ts +8 -1
- package/dist/config/schema.d.ts +8 -0
- package/dist/hooks/pr-review-resilience-circuit.d.ts +232 -0
- package/dist/hooks/pr-workflow-gate.d.ts +10 -11
- package/dist/index.js +298 -289
- package/dist/observability/catalog.d.ts +6 -3
- package/dist/telemetry.d.ts +21 -1
- package/package.json +1 -1
- package/dist/cli/index-meg33yyv.js +0 -247
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The event catalog (issue #2029).
|
|
3
3
|
*
|
|
4
|
-
* Exactly
|
|
4
|
+
* Exactly 51 entries, matching the `TelemetryEvent` union at
|
|
5
5
|
* `src/telemetry.ts:15-131`. Thirty-eight predate the #2029 contract; the 39th is
|
|
6
6
|
* `agent_conflict_detected` (previously emitted through a force-cast past the
|
|
7
7
|
* type system), the 40th is `close_archive_result` (issue #2030 — the
|
|
@@ -20,7 +20,10 @@
|
|
|
20
20
|
* `shell_audit_health` storage audit for the issue-#2040
|
|
21
21
|
* `.swarm/session/shell-audit.jsonl` security-audit store, and the 50th is
|
|
22
22
|
* the bounded `trajectory_health` storage audit for the issue-#2041
|
|
23
|
-
* `.swarm/trajectories/` PRM session store
|
|
23
|
+
* `.swarm/trajectories/` PRM session store, and the 51st is the bounded
|
|
24
|
+
* `pr_subscription_health` storage audit for the issue-#2042
|
|
25
|
+
* `.swarm/pr-monitor/` PR-monitor subscription checkpoint store. These late
|
|
26
|
+
* additions are instances
|
|
24
27
|
* of the defect class this contract exists to close: an event kind entering
|
|
25
28
|
* the stream with no registration.
|
|
26
29
|
*
|
|
@@ -75,7 +78,7 @@ export interface CatalogEntry {
|
|
|
75
78
|
/**
|
|
76
79
|
* Whether an event of this kind must carry `trace.parentSpanId`.
|
|
77
80
|
*
|
|
78
|
-
* `false` for all
|
|
81
|
+
* `false` for all 51 entries today, and that is a truthful statement about
|
|
79
82
|
* the current system rather than a placeholder: no producer supplies a
|
|
80
83
|
* parent span, so `createObservation` never sets one. Setting this to `true`
|
|
81
84
|
* for a kind whose producer cannot supply a parent would make every
|
package/dist/telemetry.d.ts
CHANGED
|
@@ -32,7 +32,7 @@ export type TelemetryEvent = 'session_started' | 'session_ended' | 'agent_activa
|
|
|
32
32
|
* `prm_hard_stop` TRIGGER emitted by `src/prm/escalation.ts`. A trigger with
|
|
33
33
|
* no matching delivery means the containment never reached the agent.
|
|
34
34
|
*/
|
|
35
|
-
| 'prm_hard_stop_delivered' | 'agent_conflict_detected' | 'close_archive_result' | 'knowledge_receipt_transition' | 'knowledge_maintenance' | 'residue_health' | 'context_telemetry_health' | 'skill_usage_health' | 'core_events_health' | 'shell_audit_health' | 'trajectory_health';
|
|
35
|
+
| 'prm_hard_stop_delivered' | 'agent_conflict_detected' | 'close_archive_result' | 'knowledge_receipt_transition' | 'knowledge_maintenance' | 'residue_health' | 'context_telemetry_health' | 'skill_usage_health' | 'core_events_health' | 'shell_audit_health' | 'trajectory_health' | 'pr_subscription_health';
|
|
36
36
|
/** Stable classification for how a reviewer-gate decision was established. */
|
|
37
37
|
export type ReviewerGateEvidenceKind = 'genuine' | 'fallback' | 'data_quality' | 'block';
|
|
38
38
|
/**
|
|
@@ -355,6 +355,26 @@ export declare const telemetry: {
|
|
|
355
355
|
bytes: number;
|
|
356
356
|
limit_bytes: number;
|
|
357
357
|
}): void;
|
|
358
|
+
/**
|
|
359
|
+
* PR-monitor subscription store health (issue #2042): emitted on terminal
|
|
360
|
+
* compaction, legacy migration completion, legacy archive, and
|
|
361
|
+
* foreign/corrupt checkpoint recovery for the bounded `.swarm/pr-monitor/`
|
|
362
|
+
* checkpoint store. Counts ONLY — no correlationIds, no filesystem paths,
|
|
363
|
+
* no repo identities — matching the observability contract's
|
|
364
|
+
* no-content-in-metrics rule. `corrupt_count` discloses legacy lines
|
|
365
|
+
* skipped by the bounded fold; `dropped_audit_count` discloses audit-tail
|
|
366
|
+
* transitions dropped by the high/low-water rewrite.
|
|
367
|
+
*/
|
|
368
|
+
prSubscriptionHealth(data: {
|
|
369
|
+
trigger: "compact" | "migrate-complete" | "archive" | "foreign-rebind" | "corrupt-quarantine";
|
|
370
|
+
active_count: number;
|
|
371
|
+
terminal_count: number;
|
|
372
|
+
compactions: number;
|
|
373
|
+
corrupt_count: number;
|
|
374
|
+
dropped_audit_count: number;
|
|
375
|
+
checkpoint_bytes: number;
|
|
376
|
+
limit_bytes: number;
|
|
377
|
+
}): void;
|
|
358
378
|
/**
|
|
359
379
|
* Issue #2063 C2 — the PRM hard-stop DENIAL was actually delivered to the
|
|
360
380
|
* agent (thrown by the guardrails `toolBefore` consumer). `prm_hard_stop`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-swarm",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.154.0",
|
|
4
4
|
"description": "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -1,247 +0,0 @@
|
|
|
1
|
-
// @bun
|
|
2
|
-
import {
|
|
3
|
-
validateSwarmPath
|
|
4
|
-
} from "./index-4cgee5vh.js";
|
|
5
|
-
import {
|
|
6
|
-
withEvidenceLock
|
|
7
|
-
} from "./index-891g7hbf.js";
|
|
8
|
-
import {
|
|
9
|
-
exports_external,
|
|
10
|
-
init_zod
|
|
11
|
-
} from "./index-bpmtbmy9.js";
|
|
12
|
-
import {
|
|
13
|
-
log
|
|
14
|
-
} from "./index-zgwm4ryv.js";
|
|
15
|
-
|
|
16
|
-
// src/background/pr-subscriptions.ts
|
|
17
|
-
init_zod();
|
|
18
|
-
import * as fs from "fs";
|
|
19
|
-
import * as path from "path";
|
|
20
|
-
var PR_SUBSCRIPTIONS_FILE = "pr-monitor/subscriptions.jsonl";
|
|
21
|
-
var STORE_LOCK_AGENT = "pr-monitor";
|
|
22
|
-
var STORE_LOCK_TASK = "pr-subscriptions";
|
|
23
|
-
var onSubscriptionCreated = null;
|
|
24
|
-
function setOnSubscriptionCreated(callback) {
|
|
25
|
-
onSubscriptionCreated = callback;
|
|
26
|
-
}
|
|
27
|
-
var RecordSchema = exports_external.object({
|
|
28
|
-
correlationId: exports_external.string().min(1),
|
|
29
|
-
sessionID: exports_external.string().min(1),
|
|
30
|
-
prNumber: exports_external.number().int().positive(),
|
|
31
|
-
repoFullName: exports_external.string().regex(/^[^/]+\/[^/]+$/, "Must be owner/repo format"),
|
|
32
|
-
prUrl: exports_external.string().min(1).regex(/^https:\/\/github\.com\/[^/]+\/[^/]+\/pull\/\d+$/, "Must be a valid GitHub PR URL"),
|
|
33
|
-
headRefOid: exports_external.string().optional(),
|
|
34
|
-
lastCheckedAt: exports_external.number(),
|
|
35
|
-
lastCommentId: exports_external.string().optional(),
|
|
36
|
-
lastCheckRunSet: exports_external.string().optional(),
|
|
37
|
-
mergeableState: exports_external.string().optional(),
|
|
38
|
-
mergeGroupRunStatus: exports_external.string().optional(),
|
|
39
|
-
mergeGroupRunConclusion: exports_external.string().optional(),
|
|
40
|
-
mergeGroupRunHtmlUrl: exports_external.string().optional(),
|
|
41
|
-
isWatching: exports_external.boolean(),
|
|
42
|
-
hasUnaddressedEvents: exports_external.boolean(),
|
|
43
|
-
status: exports_external.enum(["active", "removed", "expired"]),
|
|
44
|
-
createdAt: exports_external.number(),
|
|
45
|
-
updatedAt: exports_external.number(),
|
|
46
|
-
errorCount: exports_external.number().int().min(0),
|
|
47
|
-
customPollIntervalSeconds: exports_external.number().int().positive().optional(),
|
|
48
|
-
customFailureThreshold: exports_external.number().int().min(0).optional(),
|
|
49
|
-
customCooldownSeconds: exports_external.number().int().min(0).optional()
|
|
50
|
-
}).strict();
|
|
51
|
-
function storePath(directory) {
|
|
52
|
-
return validateSwarmPath(directory, PR_SUBSCRIPTIONS_FILE);
|
|
53
|
-
}
|
|
54
|
-
function ensureSwarmDir(directory) {
|
|
55
|
-
fs.mkdirSync(path.resolve(directory, ".swarm", "pr-monitor"), {
|
|
56
|
-
recursive: true
|
|
57
|
-
});
|
|
58
|
-
}
|
|
59
|
-
function buildCorrelationId(sessionID, repoFullName, prNumber) {
|
|
60
|
-
return `${sessionID}::${repoFullName}::${prNumber}`;
|
|
61
|
-
}
|
|
62
|
-
function readAllRecords(directory) {
|
|
63
|
-
let raw;
|
|
64
|
-
try {
|
|
65
|
-
raw = fs.readFileSync(storePath(directory), "utf-8");
|
|
66
|
-
} catch (err) {
|
|
67
|
-
if (err.code === "ENOENT")
|
|
68
|
-
return [];
|
|
69
|
-
log(`[pr-monitor] readAllRecords failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
70
|
-
return [];
|
|
71
|
-
}
|
|
72
|
-
const folded = new Map;
|
|
73
|
-
for (const line of raw.split(`
|
|
74
|
-
`)) {
|
|
75
|
-
const trimmed = line.trim();
|
|
76
|
-
if (trimmed.length === 0)
|
|
77
|
-
continue;
|
|
78
|
-
let parsedJson;
|
|
79
|
-
try {
|
|
80
|
-
parsedJson = JSON.parse(trimmed);
|
|
81
|
-
} catch {
|
|
82
|
-
continue;
|
|
83
|
-
}
|
|
84
|
-
const result = RecordSchema.safeParse(parsedJson);
|
|
85
|
-
if (!result.success)
|
|
86
|
-
continue;
|
|
87
|
-
folded.set(result.data.correlationId, result.data);
|
|
88
|
-
}
|
|
89
|
-
return [...folded.values()];
|
|
90
|
-
}
|
|
91
|
-
function appendRecord(directory, record) {
|
|
92
|
-
const result = RecordSchema.safeParse(record);
|
|
93
|
-
if (!result.success) {
|
|
94
|
-
throw new Error(`Invalid subscription record: ${result.error.message}`);
|
|
95
|
-
}
|
|
96
|
-
ensureSwarmDir(directory);
|
|
97
|
-
fs.appendFileSync(storePath(directory), `${JSON.stringify(record)}
|
|
98
|
-
`, "utf-8");
|
|
99
|
-
}
|
|
100
|
-
async function subscribe(directory, input) {
|
|
101
|
-
if (!directory || directory.trim() === "") {
|
|
102
|
-
throw new Error("directory is required");
|
|
103
|
-
}
|
|
104
|
-
if (!input.sessionID || input.sessionID.trim() === "") {
|
|
105
|
-
throw new Error("sessionID is required and must be non-empty");
|
|
106
|
-
}
|
|
107
|
-
if (!input.repoFullName || input.repoFullName.trim() === "") {
|
|
108
|
-
throw new Error("repoFullName is required and must be non-empty");
|
|
109
|
-
}
|
|
110
|
-
if (!input.prUrl || input.prUrl.trim() === "") {
|
|
111
|
-
throw new Error("prUrl is required and must be non-empty");
|
|
112
|
-
}
|
|
113
|
-
if (!input.prNumber || !Number.isInteger(input.prNumber) || input.prNumber <= 0) {
|
|
114
|
-
throw new Error("prNumber is required and must be a positive integer");
|
|
115
|
-
}
|
|
116
|
-
const correlationId = buildCorrelationId(input.sessionID, input.repoFullName, input.prNumber);
|
|
117
|
-
const now = Date.now();
|
|
118
|
-
return withEvidenceLock(directory, PR_SUBSCRIPTIONS_FILE, STORE_LOCK_AGENT, STORE_LOCK_TASK, async () => {
|
|
119
|
-
const existing = readAllRecords(directory);
|
|
120
|
-
const match = existing.find((r) => r.correlationId === correlationId && r.status === "active");
|
|
121
|
-
if (match) {
|
|
122
|
-
onSubscriptionCreated?.(directory, match);
|
|
123
|
-
return match;
|
|
124
|
-
}
|
|
125
|
-
if (input.maxSubscriptions !== undefined && input.maxSubscriptions > 0) {
|
|
126
|
-
const activeCount = existing.filter((r) => r.status === "active").length;
|
|
127
|
-
if (activeCount >= input.maxSubscriptions) {
|
|
128
|
-
throw new Error(`PR subscription limit reached: ${activeCount}/${input.maxSubscriptions}`);
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
const record = {
|
|
132
|
-
correlationId,
|
|
133
|
-
sessionID: input.sessionID,
|
|
134
|
-
prNumber: input.prNumber,
|
|
135
|
-
repoFullName: input.repoFullName,
|
|
136
|
-
prUrl: input.prUrl,
|
|
137
|
-
lastCheckedAt: now,
|
|
138
|
-
isWatching: true,
|
|
139
|
-
hasUnaddressedEvents: false,
|
|
140
|
-
status: "active",
|
|
141
|
-
createdAt: now,
|
|
142
|
-
updatedAt: now,
|
|
143
|
-
errorCount: 0
|
|
144
|
-
};
|
|
145
|
-
appendRecord(directory, record);
|
|
146
|
-
onSubscriptionCreated?.(directory, record);
|
|
147
|
-
return record;
|
|
148
|
-
});
|
|
149
|
-
}
|
|
150
|
-
async function unsubscribe(directory, correlationId) {
|
|
151
|
-
if (!correlationId)
|
|
152
|
-
return null;
|
|
153
|
-
return withEvidenceLock(directory, PR_SUBSCRIPTIONS_FILE, STORE_LOCK_AGENT, STORE_LOCK_TASK, async () => {
|
|
154
|
-
const existing = readAllRecords(directory);
|
|
155
|
-
const match = existing.find((r) => r.correlationId === correlationId && r.status === "active");
|
|
156
|
-
if (!match)
|
|
157
|
-
return null;
|
|
158
|
-
const now = Date.now();
|
|
159
|
-
const removed = {
|
|
160
|
-
...match,
|
|
161
|
-
status: "removed",
|
|
162
|
-
isWatching: false,
|
|
163
|
-
updatedAt: now
|
|
164
|
-
};
|
|
165
|
-
appendRecord(directory, removed);
|
|
166
|
-
return removed;
|
|
167
|
-
});
|
|
168
|
-
}
|
|
169
|
-
async function listActive(directory) {
|
|
170
|
-
return readAllRecords(directory).filter((r) => r.status === "active");
|
|
171
|
-
}
|
|
172
|
-
async function lookupByPr(directory, repoFullName, prNumber) {
|
|
173
|
-
for (const record of readAllRecords(directory)) {
|
|
174
|
-
if (record.status === "active" && record.repoFullName === repoFullName && record.prNumber === prNumber) {
|
|
175
|
-
return record;
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
return null;
|
|
179
|
-
}
|
|
180
|
-
async function updateSnapshot(directory, correlationId, updates) {
|
|
181
|
-
if (!correlationId)
|
|
182
|
-
return null;
|
|
183
|
-
return withEvidenceLock(directory, PR_SUBSCRIPTIONS_FILE, STORE_LOCK_AGENT, STORE_LOCK_TASK, async () => {
|
|
184
|
-
const existing = readAllRecords(directory);
|
|
185
|
-
const match = existing.find((r) => r.correlationId === correlationId && r.status === "active");
|
|
186
|
-
if (!match)
|
|
187
|
-
return null;
|
|
188
|
-
const updated = {
|
|
189
|
-
...match,
|
|
190
|
-
...updates,
|
|
191
|
-
correlationId,
|
|
192
|
-
sessionID: match.sessionID,
|
|
193
|
-
repoFullName: match.repoFullName,
|
|
194
|
-
prNumber: match.prNumber,
|
|
195
|
-
prUrl: match.prUrl,
|
|
196
|
-
createdAt: match.createdAt,
|
|
197
|
-
updatedAt: Date.now()
|
|
198
|
-
};
|
|
199
|
-
appendRecord(directory, updated);
|
|
200
|
-
return updated;
|
|
201
|
-
});
|
|
202
|
-
}
|
|
203
|
-
async function sweepStale(directory, ttlDays, mergedPrs) {
|
|
204
|
-
if (!ttlDays || ttlDays <= 0)
|
|
205
|
-
return 0;
|
|
206
|
-
const ttlMs = ttlDays * 86400000;
|
|
207
|
-
const now = Date.now();
|
|
208
|
-
try {
|
|
209
|
-
return await withEvidenceLock(directory, PR_SUBSCRIPTIONS_FILE, STORE_LOCK_AGENT, STORE_LOCK_TASK, async () => {
|
|
210
|
-
let swept = 0;
|
|
211
|
-
for (const record of readAllRecords(directory)) {
|
|
212
|
-
if (record.status !== "active")
|
|
213
|
-
continue;
|
|
214
|
-
const prKey = `${record.repoFullName}::${record.prNumber}`;
|
|
215
|
-
const isMerged = mergedPrs?.has(prKey) ?? false;
|
|
216
|
-
const isStale = now - record.updatedAt > ttlMs;
|
|
217
|
-
if (isMerged) {
|
|
218
|
-
appendRecord(directory, {
|
|
219
|
-
...record,
|
|
220
|
-
status: "expired",
|
|
221
|
-
isWatching: false,
|
|
222
|
-
updatedAt: now
|
|
223
|
-
});
|
|
224
|
-
swept += 1;
|
|
225
|
-
log(`[pr-monitor] Swept subscription: merged/closed: ${prKey}`);
|
|
226
|
-
continue;
|
|
227
|
-
}
|
|
228
|
-
if (isStale && !record.hasUnaddressedEvents) {
|
|
229
|
-
appendRecord(directory, {
|
|
230
|
-
...record,
|
|
231
|
-
status: "expired",
|
|
232
|
-
isWatching: false,
|
|
233
|
-
updatedAt: now
|
|
234
|
-
});
|
|
235
|
-
swept += 1;
|
|
236
|
-
log(`[pr-monitor] Swept subscription: stale (TTL ${ttlDays}d): ${prKey}`);
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
return swept;
|
|
240
|
-
});
|
|
241
|
-
} catch (err) {
|
|
242
|
-
log(`[pr-monitor] sweepStale failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
243
|
-
return 0;
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
export { PR_SUBSCRIPTIONS_FILE, setOnSubscriptionCreated, buildCorrelationId, subscribe, unsubscribe, listActive, lookupByPr, updateSnapshot, sweepStale };
|