trantor 0.18.82 → 0.18.86
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/.claude-plugin/plugin.json +1 -1
- package/bin/cli.mjs +8 -0
- package/bin/crew/cmux.mjs +11 -11
- package/bin/crew/core.mjs +3 -2
- package/bin/crew/herdr.mjs +8 -8
- package/bin/crew/state.mjs +30 -15
- package/bin/crew/swap.mjs +12 -12
- package/bin/crew/tmux.mjs +11 -11
- package/bin/crew-runner.mjs +176 -63
- package/bin/crew.mjs +22 -10
- package/bin/cut-snapshot.mjs +37 -0
- package/bin/gate.mjs +30 -0
- package/bin/keys.mjs +53 -0
- package/bin/turn-watchdog.mjs +14 -9
- package/bin/wake-nudge.mjs +21 -2
- package/hooks/file-claim.mjs +19 -7
- package/hooks/file-hold.mjs +49 -0
- package/hooks/heartbeat.mjs +2 -1
- package/hooks/hooks.json +10 -1
- package/hooks/lib/git-checkout.mjs +10 -0
- package/hooks/lib/overseer-level-cache.mjs +25 -0
- package/hooks/overseer-warn.mjs +1 -1
- package/hub/auth.mjs +7 -52
- package/hub/overseer.mjs +100 -34
- package/hub/routes/admin.mjs +58 -12
- package/hub/store.mjs +3 -2
- package/hub.mjs +1 -1
- package/lib/duty-nudges.mjs +12 -0
- package/lib/identity.mjs +181 -8
- package/lib/nudge-ledger.mjs +95 -0
- package/lib/overseer.mjs +82 -24
- package/lib/persona.mjs +43 -0
- package/lib/remote-overlap.mjs +193 -0
- package/lib/seat-why.mjs +6 -1
- package/lib/session-liveness.mjs +83 -0
- package/lib/state/gate.mjs +1 -1
- package/lib/store-pg.mjs +2 -0
- package/package.json +1 -1
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
// #11311: client-side open-PR overlap evidence. The session's OWN hook polls the repo it is
|
|
2
|
+
// actually editing (`git -C <gitRoot> remote get-url origin`) — never the hub, whose checkout and
|
|
3
|
+
// unauthenticated gh would match trantor's PRs to every project. Cached 5 min per repo in
|
|
4
|
+
// ~/.agent-bus/remote-overlap-<owner>-<name>.json; gh missing/unauthenticated = off, one log line.
|
|
5
|
+
import { execFile, spawnSync } from "node:child_process";
|
|
6
|
+
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
7
|
+
import { join } from "node:path";
|
|
8
|
+
import { homedir } from "node:os";
|
|
9
|
+
|
|
10
|
+
const POLL_TTL_MS = 5 * 60 * 1000;
|
|
11
|
+
const RATE_LIMIT_BACKOFF_MS = 10 * 60 * 1000;
|
|
12
|
+
const BLIP_BACKOFF_MS = 30 * 1000;
|
|
13
|
+
const GH_TIMEOUT_MS = Number(process.env.RELAY_REMOTE_OVERLAP_TIMEOUT_MS || 5000);
|
|
14
|
+
// Cap PRs per poll: each costs a /files call, and a busy repo must not eat the rate limit.
|
|
15
|
+
const PR_CAP = 25;
|
|
16
|
+
|
|
17
|
+
const GH_OFF = /gh auth login|not logged into|gh: auth|authentication/i;
|
|
18
|
+
const RATE_LIMIT = /rate limit/i;
|
|
19
|
+
|
|
20
|
+
// "owner/name" from every remote form in the wild — https, ssh, git://, with credentials or a
|
|
21
|
+
// .git suffix — or a bare owner/name that is already parsed. Anything else is not pollable.
|
|
22
|
+
export function parseRepoUrl(url) {
|
|
23
|
+
const raw = String(url ?? "").trim();
|
|
24
|
+
if (!raw) return null;
|
|
25
|
+
if (/^[\w.-]+\/[\w.-]+$/.test(raw)) return raw;
|
|
26
|
+
const m = raw.match(/^(?:(?:https?:\/\/|ssh:\/\/|git:\/\/)(?:[^@/]+@)?|git@)?[^/:@]+[:/](.+?)(?:\.git)?\/?$/i);
|
|
27
|
+
const parts = m ? m[1].replace(/\.git$/i, "").split("/").filter(Boolean) : [];
|
|
28
|
+
const owner = parts[parts.length - 2];
|
|
29
|
+
const name = parts[parts.length - 1];
|
|
30
|
+
return owner && name && /^[\w.-]+$/.test(owner) && /^[\w.-]+$/.test(name) ? `${owner}/${name}` : null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function defaultGitRun(args, { cwd } = {}) {
|
|
34
|
+
const r = spawnSync("git", args, { cwd, encoding: "utf8", timeout: 5000 });
|
|
35
|
+
return { status: r.status, stdout: r.stdout || "", stderr: r.stderr || "" };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function defaultGhRun(args) {
|
|
39
|
+
const r = spawnSync("gh", args, { encoding: "utf8", timeout: GH_TIMEOUT_MS, maxBuffer: 16 * 1024 * 1024 });
|
|
40
|
+
if (r.error && !r.error.message?.includes("exited with")) {
|
|
41
|
+
return { code: r.error.code ?? 1, stdout: "", stderr: String(r.stderr || r.error.message || "") };
|
|
42
|
+
}
|
|
43
|
+
return { code: r.status ?? 1, stdout: r.stdout || "", stderr: r.stderr || "" };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Repo detection: `git remote get-url origin` from the session's git root. Null when there is
|
|
47
|
+
// no origin or it is not a GitHub-ish remote — the poller stays quietly off.
|
|
48
|
+
export function detectRepo({ dir = process.cwd(), run = defaultGitRun } = {}) {
|
|
49
|
+
try {
|
|
50
|
+
const r = run(["remote", "get-url", "origin"], { cwd: dir });
|
|
51
|
+
return r.status === 0 ? parseRepoUrl(r.stdout) : null;
|
|
52
|
+
} catch {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const cleanPaths = (files) =>
|
|
58
|
+
[...new Set(Array.from(files ?? []).map((f) => String(f ?? "").trim()).filter(Boolean))].sort((a, b) => a.localeCompare(b));
|
|
59
|
+
|
|
60
|
+
// The poller. overlapsFor({repo, gitRoot, files}) is SYNC: cache file read first (the norm), a
|
|
61
|
+
// stale or cold repo triggers one gh poll (list + per-PR files) whose result rewrites the cache,
|
|
62
|
+
// so sibling processes (other claims, other seats) hit the warm file. rate-limit and gh-off state
|
|
63
|
+
// persist in the cache file too, so backoff survives the hook's short-lived processes.
|
|
64
|
+
export function createRemoteOverlap({
|
|
65
|
+
run = defaultGhRun,
|
|
66
|
+
gitRun = defaultGitRun,
|
|
67
|
+
log = (m) => console.error(`[remote-overlap] ${m}`),
|
|
68
|
+
now = Date.now,
|
|
69
|
+
ttlMs = POLL_TTL_MS,
|
|
70
|
+
backoffMs = RATE_LIMIT_BACKOFF_MS,
|
|
71
|
+
cacheDir,
|
|
72
|
+
} = {}) {
|
|
73
|
+
const dir = cacheDir || process.env.AGENT_BUS_DIR || join(homedir(), ".agent-bus");
|
|
74
|
+
let enabled = true;
|
|
75
|
+
let logged = "";
|
|
76
|
+
|
|
77
|
+
const say = (m) => {
|
|
78
|
+
if (!logged) {
|
|
79
|
+
logged = m;
|
|
80
|
+
log(m);
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const cachePath = (repo) => join(dir, `remote-overlap-${String(repo).replace(/\//g, "-")}.json`);
|
|
85
|
+
const readCache = (repo) => {
|
|
86
|
+
try {
|
|
87
|
+
return JSON.parse(readFileSync(cachePath(repo), "utf8"));
|
|
88
|
+
} catch {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
const writeCache = (repo, data) => {
|
|
93
|
+
try {
|
|
94
|
+
mkdirSync(dir, { recursive: true });
|
|
95
|
+
writeFileSync(cachePath(repo), JSON.stringify({ repo, at: now(), prs: [], off: "", blockedUntil: 0, ...data }));
|
|
96
|
+
} catch {}
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
function fail(repo, res) {
|
|
100
|
+
const stderr = String(res?.stderr || "");
|
|
101
|
+
if (res?.code === "ENOENT" || GH_OFF.test(stderr)) {
|
|
102
|
+
enabled = false;
|
|
103
|
+
say(`gh unusable (${res?.code === "ENOENT" ? "gh not found" : "not authenticated"}) — remote-overlap off.`);
|
|
104
|
+
writeCache(repo, { off: "gh missing or unauthenticated", prs: [] });
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
const prior = readCache(repo);
|
|
108
|
+
const blockedUntil = RATE_LIMIT.test(stderr) ? (say(`GitHub rate limit hit — polling paused ${Math.round(backoffMs / 60000)}m.`), now() + backoffMs) : now() + BLIP_BACKOFF_MS;
|
|
109
|
+
writeCache(repo, { at: prior?.at ?? 0, prs: prior?.prs ?? [], blockedUntil, off: "" });
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function poll(repo) {
|
|
114
|
+
const list = run(["api", `repos/${repo}/pulls?state=open`]);
|
|
115
|
+
if (list.code !== 0) return fail(repo, list);
|
|
116
|
+
let pulls;
|
|
117
|
+
try {
|
|
118
|
+
pulls = JSON.parse(list.stdout || "[]");
|
|
119
|
+
} catch {
|
|
120
|
+
return fail(repo, { code: list.code, stderr: "unparsable PR list" });
|
|
121
|
+
}
|
|
122
|
+
if (!Array.isArray(pulls)) return fail(repo, { code: list.code, stderr: "unexpected PR list shape" });
|
|
123
|
+
const prs = [];
|
|
124
|
+
for (const p of pulls.slice(0, PR_CAP)) {
|
|
125
|
+
const number = Number(p?.number);
|
|
126
|
+
if (!Number.isFinite(number)) continue;
|
|
127
|
+
const res = run(["api", `repos/${repo}/pulls/${number}/files`]);
|
|
128
|
+
let files = [];
|
|
129
|
+
if (res.code === 0) {
|
|
130
|
+
try {
|
|
131
|
+
files = cleanPaths(JSON.parse(res.stdout || "[]").map((f) => f?.filename));
|
|
132
|
+
} catch {
|
|
133
|
+
files = [];
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
prs.push({ number, author: String(p?.user?.login || ""), url: String(p?.html_url || ""), files });
|
|
137
|
+
}
|
|
138
|
+
const data = { repo, at: now(), prs, off: "", blockedUntil: 0 };
|
|
139
|
+
writeCache(repo, data);
|
|
140
|
+
return data;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function overlapsFor(input = {}) {
|
|
144
|
+
if (!enabled) return [];
|
|
145
|
+
const want = cleanPaths(input.files);
|
|
146
|
+
if (!want.length) return [];
|
|
147
|
+
const repo = String(input.repo || "").trim() || (input.gitRoot ? detectRepo({ dir: input.gitRoot, run: gitRun }) : null);
|
|
148
|
+
if (!repo) return [];
|
|
149
|
+
const t = now();
|
|
150
|
+
let data = readCache(repo);
|
|
151
|
+
if (!data || data.off) {
|
|
152
|
+
// a fresh off marker stays off without spawning gh; past the TTL the poll retries once
|
|
153
|
+
if (data?.off && t - (data.at ?? 0) <= ttlMs) return [];
|
|
154
|
+
poll(repo);
|
|
155
|
+
data = readCache(repo);
|
|
156
|
+
if (!data || data.off) return [];
|
|
157
|
+
} else if (t - (data.at ?? 0) > ttlMs && t >= (data.blockedUntil ?? 0)) {
|
|
158
|
+
poll(repo);
|
|
159
|
+
data = readCache(repo) ?? data;
|
|
160
|
+
}
|
|
161
|
+
const wantSet = new Set(want);
|
|
162
|
+
const out = [];
|
|
163
|
+
for (const p of data.prs ?? []) {
|
|
164
|
+
const hit = (p.files || []).filter((f) => wantSet.has(f));
|
|
165
|
+
if (hit.length) out.push({ repo, number: p.number, author: p.author, url: p.url, files: cleanPaths(hit) });
|
|
166
|
+
}
|
|
167
|
+
return out.sort((a, b) => a.number - b.number);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Force a poll, cache result, return it (or null on failure) — the test seam and warm-up path.
|
|
171
|
+
function refresh(repo) {
|
|
172
|
+
return poll(repo);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return {
|
|
176
|
+
overlapsFor,
|
|
177
|
+
refresh,
|
|
178
|
+
get enabled() {
|
|
179
|
+
return enabled;
|
|
180
|
+
},
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const defaultApi = createRemoteOverlap({});
|
|
185
|
+
|
|
186
|
+
// The hook-facing seam: never raises, whatever the input.
|
|
187
|
+
export function overlapsFor(input) {
|
|
188
|
+
try {
|
|
189
|
+
return defaultApi.overlapsFor(input);
|
|
190
|
+
} catch {
|
|
191
|
+
return [];
|
|
192
|
+
}
|
|
193
|
+
}
|
package/lib/seat-why.mjs
CHANGED
|
@@ -134,6 +134,7 @@ export function seatWhy(project, agent, opts = {}) {
|
|
|
134
134
|
const pids = opts.pidCheck ? opts.pidCheck(project, agent) : scanPids(project, agent);
|
|
135
135
|
const last = [...telemetry].reverse().find(r => asNumber(r.turn) !== null) || null;
|
|
136
136
|
const boots = telemetry.filter(r => r.boot).length;
|
|
137
|
+
const lifecycle = [...telemetry].reverse().find(r => r.boot || r.stopped);
|
|
137
138
|
const errTail = errText.trim().split("\n").filter(Boolean).slice(-2).join(" | ").slice(0, 240);
|
|
138
139
|
|
|
139
140
|
let state, why, advice;
|
|
@@ -184,7 +185,11 @@ export function seatWhy(project, agent, opts = {}) {
|
|
|
184
185
|
else if (boots) bits.push(`${boots} boot record(s), no completed turn`);
|
|
185
186
|
if (pane) bits.push(`pane ${pane.source}/${pane.pane} registered but no runner process`);
|
|
186
187
|
|
|
187
|
-
if (
|
|
188
|
+
if (lifecycle?.stopped) {
|
|
189
|
+
state = "stopped";
|
|
190
|
+
why = `no runner process; operator stopped ${agent}:${project} ${rel(lifecycle.ts)}.`;
|
|
191
|
+
advice = "seat was deliberately stopped — `trantor up <agent>` to start it again.";
|
|
192
|
+
} else if (last?.authFailed || AUTH_RE.test(errText)) {
|
|
188
193
|
state = "dead-auth";
|
|
189
194
|
why = `no runner process; ${bits.join("; ") || "auth markers in evidence"}.`;
|
|
190
195
|
advice = "provider credentials are rejected — fix the key (~/.agent-bus/.env or the seat's token-scrooge .env), then `trantor up <agent>`.";
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
// CLI-owned records let read-only turns and their sub-agents prove liveness (#10497).
|
|
2
|
+
import { spawnSync } from "node:child_process";
|
|
3
|
+
import { readFileSync, readdirSync, realpathSync, statSync } from "node:fs";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { createHash } from "node:crypto";
|
|
6
|
+
|
|
7
|
+
const canonical = p => { try { return realpathSync(p); } catch { return p; } };
|
|
8
|
+
const sql = s => `'${String(s).replaceAll("'", "''")}'`;
|
|
9
|
+
const entries = p => { try { return readdirSync(p, { withFileTypes: true }); } catch { return []; } };
|
|
10
|
+
const mtime = p => { try { return statSync(p).mtimeMs; } catch { return 0; } };
|
|
11
|
+
function logTime(dir, budget = { left: 20000 }) {
|
|
12
|
+
let best = 0;
|
|
13
|
+
for (const e of entries(dir)) {
|
|
14
|
+
if (--budget.left < 0) break;
|
|
15
|
+
const path = join(dir, e.name);
|
|
16
|
+
if (e.isDirectory()) best = Math.max(best, logTime(path, budget));
|
|
17
|
+
else if (e.name.endsWith(".jsonl")) best = Math.max(best, mtime(path));
|
|
18
|
+
}
|
|
19
|
+
return best;
|
|
20
|
+
}
|
|
21
|
+
function query(db, statement) {
|
|
22
|
+
try {
|
|
23
|
+
const r = spawnSync("sqlite3", ["-readonly", db, statement], {
|
|
24
|
+
encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], timeout: 2000,
|
|
25
|
+
});
|
|
26
|
+
return r.status === 0 ? r.stdout.trim() : null;
|
|
27
|
+
} catch { return null; }
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Pin the root once discovered. A fresh root must have been created during this turn;
|
|
31
|
+
// a resumed root is supplied by the runner. Never use global DB/WAL mtimes.
|
|
32
|
+
export function sessionActivityReader({ kind, sid = "", db, workDir, home, startedAt, transcriptDir }) {
|
|
33
|
+
let root = sid, logDir = "", lastDb = 0;
|
|
34
|
+
const cwd = canonical(workDir);
|
|
35
|
+
return () => {
|
|
36
|
+
if (kind === "opencode") {
|
|
37
|
+
if (!root) root = query(db, `SELECT id FROM session WHERE directory IN (${sql(workDir)},${sql(cwd)})`
|
|
38
|
+
+ ` AND parent_id IS NULL AND time_created>=${Math.floor(startedAt)} ORDER BY time_created DESC LIMIT 1;`);
|
|
39
|
+
if (!/^ses_[A-Za-z0-9]+$/.test(root)) return 0;
|
|
40
|
+
const value = query(db, `WITH RECURSIVE own(id) AS (SELECT id FROM session WHERE id=${sql(root)}`
|
|
41
|
+
+ ` UNION SELECT s.id FROM session s JOIN own ON s.parent_id=own.id)`
|
|
42
|
+
+ ` SELECT MAX(t) FROM (SELECT MAX(time_created,time_updated) t FROM message WHERE session_id IN (SELECT id FROM own)`
|
|
43
|
+
+ ` UNION ALL SELECT MAX(time_created,time_updated) t FROM part WHERE session_id IN (SELECT id FROM own));`);
|
|
44
|
+
// #9832: a locked or timed-out read is unknown, not silence; keep the last seen activity.
|
|
45
|
+
if (value === null) return lastDb;
|
|
46
|
+
return (lastDb = Math.max(lastDb, Number(value) || 0));
|
|
47
|
+
}
|
|
48
|
+
if (kind === "claude") {
|
|
49
|
+
if (!root) {
|
|
50
|
+
const newest = entries(transcriptDir).filter(e => e.isFile() && e.name.endsWith(".jsonl"))
|
|
51
|
+
.map(e => ({ name: e.name, ts: mtime(join(transcriptDir, e.name)) }))
|
|
52
|
+
.filter(e => e.ts >= startedAt).sort((a, b) => b.ts - a.ts)[0];
|
|
53
|
+
if (newest) root = newest.name.slice(0, -6);
|
|
54
|
+
}
|
|
55
|
+
if (!root || root.includes("/")) return 0;
|
|
56
|
+
return Math.max(mtime(join(transcriptDir, `${root}.jsonl`)), logTime(join(transcriptDir, root)));
|
|
57
|
+
}
|
|
58
|
+
if (kind !== "kimi") return 0;
|
|
59
|
+
if (!logDir) {
|
|
60
|
+
// kimi-code indexes the actual workdir and session directory; avoid guessing its hash.
|
|
61
|
+
try {
|
|
62
|
+
const rows = readFileSync(join(home, ".kimi-code", "session_index.jsonl"), "utf8").trim().split("\n");
|
|
63
|
+
for (const line of rows.reverse()) {
|
|
64
|
+
let row; try { row = JSON.parse(line); } catch { continue; }
|
|
65
|
+
if (canonical(row.workDir || "") !== cwd || !row.sessionDir || (root && row.sessionId !== root)) continue;
|
|
66
|
+
if (!root && mtime(row.sessionDir) < startedAt) continue;
|
|
67
|
+
root = row.sessionId; logDir = row.sessionDir; break;
|
|
68
|
+
}
|
|
69
|
+
} catch {}
|
|
70
|
+
// Python kimi-cli uses md5(canonical cwd)/session-id.
|
|
71
|
+
if (!logDir) {
|
|
72
|
+
const parent = join(home, ".kimi", "sessions", createHash("md5").update(cwd).digest("hex"));
|
|
73
|
+
if (root && !root.includes("/")) logDir = join(parent, root);
|
|
74
|
+
else {
|
|
75
|
+
const newest = entries(parent).filter(e => e.isDirectory()).map(e => ({ name: e.name, ts: mtime(join(parent, e.name)) }))
|
|
76
|
+
.filter(e => e.ts >= startedAt).sort((a, b) => b.ts - a.ts)[0];
|
|
77
|
+
if (newest) { root = newest.name; logDir = join(parent, root); }
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return logDir ? logTime(logDir) : 0;
|
|
82
|
+
};
|
|
83
|
+
}
|
package/lib/state/gate.mjs
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import { spawnSync } from "node:child_process";
|
|
4
4
|
import { existsSync, mkdirSync, readFileSync, mkdtempSync, readdirSync, writeFileSync } from "node:fs";
|
|
5
5
|
import { tmpdir } from "node:os";
|
|
6
|
-
import { join, resolve } from "node:path";
|
|
6
|
+
import { dirname, join, resolve } from "node:path";
|
|
7
7
|
import { CAPS } from "./schema.mjs";
|
|
8
8
|
|
|
9
9
|
const DEFAULT_MAX_MS = 300_000;
|
package/lib/store-pg.mjs
CHANGED
|
@@ -154,6 +154,7 @@ function kvFromState(state) {
|
|
|
154
154
|
proposals: state.proposals || [],
|
|
155
155
|
contractReap: state.contractReap || {},
|
|
156
156
|
meta: {
|
|
157
|
+
overseerState: state.overseerState || {},
|
|
157
158
|
taskSeq: Number(state.taskSeq || 0),
|
|
158
159
|
verifyGateSeq: Number(state.verifyGateSeq || 0),
|
|
159
160
|
proposalSeq: Number(state.proposalSeq || 0),
|
|
@@ -731,6 +732,7 @@ export class PgStore {
|
|
|
731
732
|
inviteTokens: meta.inviteTokens && typeof meta.inviteTokens === "object" ? meta.inviteTokens : {},
|
|
732
733
|
instances: meta.instances && typeof meta.instances === "object" ? meta.instances : {},
|
|
733
734
|
focus: kv.focus && typeof kv.focus === "object" ? kv.focus : {},
|
|
735
|
+
overseerState: meta.overseerState || {},
|
|
734
736
|
orgPolicy: kv.orgPolicy && typeof kv.orgPolicy === "object" ? kv.orgPolicy : {},
|
|
735
737
|
contractReap: kv.contractReap && typeof kv.contractReap === "object" ? kv.contractReap : {},
|
|
736
738
|
eventSeq: Math.max(Number(eventMaxRow?.rows?.[0]?.max_id || 0), Number(meta.eventSeq || 0)),
|