@merittdev/horus 0.21.6 → 0.21.8
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/horus_source.whl +0 -0
- package/dist/index.cjs +1027 -25
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -3057,7 +3057,7 @@ var init_version = __esm({
|
|
|
3057
3057
|
"../../packages/core/src/version.ts"() {
|
|
3058
3058
|
"use strict";
|
|
3059
3059
|
init_cjs_shims();
|
|
3060
|
-
HORUS_VERSION = true ? "0.21.
|
|
3060
|
+
HORUS_VERSION = true ? "0.21.8" : "dev";
|
|
3061
3061
|
PINNED_SOURCE_VERSION = HORUS_VERSION;
|
|
3062
3062
|
SOURCE_PIN_ENFORCED = HORUS_VERSION !== "dev";
|
|
3063
3063
|
EXPECTED_INDEX_CAPABILITY = 2;
|
|
@@ -29518,7 +29518,7 @@ var require_lib = __commonJS({
|
|
|
29518
29518
|
}
|
|
29519
29519
|
return new ErrorType(`${options.context ? options.context : "Value"} ${message}.`);
|
|
29520
29520
|
}
|
|
29521
|
-
function
|
|
29521
|
+
function toNumber3(value, options) {
|
|
29522
29522
|
if (typeof value === "bigint") {
|
|
29523
29523
|
throw makeException(TypeError, "is a BigInt which cannot be converted to a number", options);
|
|
29524
29524
|
}
|
|
@@ -29561,7 +29561,7 @@ var require_lib = __commonJS({
|
|
|
29561
29561
|
const twoToTheBitLength = 2 ** bitLength;
|
|
29562
29562
|
const twoToOneLessThanTheBitLength = 2 ** (bitLength - 1);
|
|
29563
29563
|
return (value, options = {}) => {
|
|
29564
|
-
let x4 =
|
|
29564
|
+
let x4 = toNumber3(value, options);
|
|
29565
29565
|
x4 = censorNegativeZero(x4);
|
|
29566
29566
|
if (options.enforceRange) {
|
|
29567
29567
|
if (!Number.isFinite(x4)) {
|
|
@@ -29601,7 +29601,7 @@ var require_lib = __commonJS({
|
|
|
29601
29601
|
const lowerBound = unsigned ? 0 : Number.MIN_SAFE_INTEGER;
|
|
29602
29602
|
const asBigIntN = unsigned ? BigInt.asUintN : BigInt.asIntN;
|
|
29603
29603
|
return (value, options = {}) => {
|
|
29604
|
-
let x4 =
|
|
29604
|
+
let x4 = toNumber3(value, options);
|
|
29605
29605
|
x4 = censorNegativeZero(x4);
|
|
29606
29606
|
if (options.enforceRange) {
|
|
29607
29607
|
if (!Number.isFinite(x4)) {
|
|
@@ -29648,18 +29648,18 @@ var require_lib = __commonJS({
|
|
|
29648
29648
|
exports2["long long"] = createLongLongConversion(64, { unsigned: false });
|
|
29649
29649
|
exports2["unsigned long long"] = createLongLongConversion(64, { unsigned: true });
|
|
29650
29650
|
exports2.double = (value, options = {}) => {
|
|
29651
|
-
const x4 =
|
|
29651
|
+
const x4 = toNumber3(value, options);
|
|
29652
29652
|
if (!Number.isFinite(x4)) {
|
|
29653
29653
|
throw makeException(TypeError, "is not a finite floating-point value", options);
|
|
29654
29654
|
}
|
|
29655
29655
|
return x4;
|
|
29656
29656
|
};
|
|
29657
29657
|
exports2["unrestricted double"] = (value, options = {}) => {
|
|
29658
|
-
const x4 =
|
|
29658
|
+
const x4 = toNumber3(value, options);
|
|
29659
29659
|
return x4;
|
|
29660
29660
|
};
|
|
29661
29661
|
exports2.float = (value, options = {}) => {
|
|
29662
|
-
const x4 =
|
|
29662
|
+
const x4 = toNumber3(value, options);
|
|
29663
29663
|
if (!Number.isFinite(x4)) {
|
|
29664
29664
|
throw makeException(TypeError, "is not a finite floating-point value", options);
|
|
29665
29665
|
}
|
|
@@ -29673,7 +29673,7 @@ var require_lib = __commonJS({
|
|
|
29673
29673
|
return y5;
|
|
29674
29674
|
};
|
|
29675
29675
|
exports2["unrestricted float"] = (value, options = {}) => {
|
|
29676
|
-
const x4 =
|
|
29676
|
+
const x4 = toNumber3(value, options);
|
|
29677
29677
|
if (isNaN(x4)) {
|
|
29678
29678
|
return x4;
|
|
29679
29679
|
}
|
|
@@ -75376,6 +75376,501 @@ function shortTs2(iso) {
|
|
|
75376
75376
|
return `${iso.slice(5, 10)} ${iso.slice(11, 16)}`;
|
|
75377
75377
|
}
|
|
75378
75378
|
|
|
75379
|
+
// ../../packages/connectors/src/lens/client.ts
|
|
75380
|
+
init_cjs_shims();
|
|
75381
|
+
init_src();
|
|
75382
|
+
var LensCloudClient = class {
|
|
75383
|
+
apiBaseUrl;
|
|
75384
|
+
token;
|
|
75385
|
+
workspaceId;
|
|
75386
|
+
http;
|
|
75387
|
+
constructor(opts) {
|
|
75388
|
+
this.apiBaseUrl = opts.apiBaseUrl.replace(/\/$/, "");
|
|
75389
|
+
this.token = opts.token;
|
|
75390
|
+
this.workspaceId = opts.workspaceId;
|
|
75391
|
+
this.http = opts.http ?? {};
|
|
75392
|
+
}
|
|
75393
|
+
async request(path) {
|
|
75394
|
+
const url = `${this.apiBaseUrl}${path}`;
|
|
75395
|
+
const res = await fetchWithRetry(
|
|
75396
|
+
url,
|
|
75397
|
+
{
|
|
75398
|
+
method: "GET",
|
|
75399
|
+
headers: {
|
|
75400
|
+
Authorization: `Bearer ${this.token}`,
|
|
75401
|
+
"Content-Type": "application/json"
|
|
75402
|
+
}
|
|
75403
|
+
},
|
|
75404
|
+
this.http
|
|
75405
|
+
);
|
|
75406
|
+
if (!res.ok) {
|
|
75407
|
+
const text2 = await res.text().catch(() => "");
|
|
75408
|
+
throw new Error(`Lens GET ${path} -> ${res.status}: ${redactUpstreamBody(text2)}`);
|
|
75409
|
+
}
|
|
75410
|
+
return res.json();
|
|
75411
|
+
}
|
|
75412
|
+
get base() {
|
|
75413
|
+
return `/v1/workspaces/${encodeURIComponent(this.workspaceId)}/lens`;
|
|
75414
|
+
}
|
|
75415
|
+
/**
|
|
75416
|
+
* List the Lens sites the workspace owns. Reports are site-scoped, so this is the
|
|
75417
|
+
* discovery step before `listReports`. Transport/auth failures PROPAGATE (like Sentry's
|
|
75418
|
+
* `listIssues`) so an outage reads as a gap, not as "no sites".
|
|
75419
|
+
*/
|
|
75420
|
+
async listSites() {
|
|
75421
|
+
const raw = await this.request(`${this.base}/sites`);
|
|
75422
|
+
const sites = Array.isArray(raw) ? raw : raw?.["sites"];
|
|
75423
|
+
if (!Array.isArray(sites)) return [];
|
|
75424
|
+
return sites.map((s5) => ({
|
|
75425
|
+
id: typeof s5["id"] === "string" ? s5["id"] : "",
|
|
75426
|
+
name: typeof s5["name"] === "string" && s5["name"] !== "" ? s5["name"] : typeof s5["id"] === "string" ? s5["id"] : ""
|
|
75427
|
+
})).filter((s5) => s5.id !== "");
|
|
75428
|
+
}
|
|
75429
|
+
/** Build the site-reports query path with the incident-window + filter params. */
|
|
75430
|
+
reportsPath(siteId, opts = {}) {
|
|
75431
|
+
const params = new URLSearchParams();
|
|
75432
|
+
if (opts.from !== void 0) params.set("from", opts.from);
|
|
75433
|
+
if (opts.to !== void 0) params.set("to", opts.to);
|
|
75434
|
+
if (opts.q !== void 0 && opts.q !== "") params.set("q", opts.q);
|
|
75435
|
+
if (opts.status !== void 0) params.set("status", opts.status);
|
|
75436
|
+
params.set("limit", String(Math.max(1, Math.min(opts.limit ?? 25, 200))));
|
|
75437
|
+
return `${this.base}/sites/${encodeURIComponent(siteId)}/reports?${params.toString()}`;
|
|
75438
|
+
}
|
|
75439
|
+
/**
|
|
75440
|
+
* List reports for a site within an optional incident window. `from`/`to` are inclusive
|
|
75441
|
+
* `createdAt` ISO-8601 bounds; `q` is a single case-insensitive substring over the
|
|
75442
|
+
* comment + env URL (NOT hint fan-out — the provider relevance-ranks client-side).
|
|
75443
|
+
* Returns the first page; transport/auth failures PROPAGATE.
|
|
75444
|
+
*/
|
|
75445
|
+
async listReports(siteId, opts = {}) {
|
|
75446
|
+
const raw = await this.request(this.reportsPath(siteId, opts));
|
|
75447
|
+
const reports = raw?.["reports"];
|
|
75448
|
+
if (!Array.isArray(reports)) return [];
|
|
75449
|
+
return reports.map((r) => parseSummary(r, siteId));
|
|
75450
|
+
}
|
|
75451
|
+
/**
|
|
75452
|
+
* Fetch one full report (with metadata) by id, scoped to its site. Transport/auth
|
|
75453
|
+
* failures PROPAGATE; the provider wraps each call so one bad report never aborts a
|
|
75454
|
+
* collection.
|
|
75455
|
+
*/
|
|
75456
|
+
async getReport(siteId, reportId) {
|
|
75457
|
+
const raw = await this.request(
|
|
75458
|
+
`${this.base}/sites/${encodeURIComponent(siteId)}/reports/${encodeURIComponent(reportId)}`
|
|
75459
|
+
);
|
|
75460
|
+
return parseReport(raw);
|
|
75461
|
+
}
|
|
75462
|
+
/** Cheap reachability probe: `listSites` succeeds → ok. Never throws. */
|
|
75463
|
+
async health() {
|
|
75464
|
+
try {
|
|
75465
|
+
const sites = await this.listSites();
|
|
75466
|
+
return { ok: true, detail: `lens reachable (workspace ${this.workspaceId}, ${sites.length} site(s))` };
|
|
75467
|
+
} catch (err2) {
|
|
75468
|
+
return { ok: false, detail: redactErrorMessage(err2) };
|
|
75469
|
+
}
|
|
75470
|
+
}
|
|
75471
|
+
};
|
|
75472
|
+
function toNumber2(v4) {
|
|
75473
|
+
if (typeof v4 === "number") return Number.isFinite(v4) ? v4 : 0;
|
|
75474
|
+
if (typeof v4 === "string") {
|
|
75475
|
+
const n4 = Number(v4);
|
|
75476
|
+
return Number.isFinite(n4) ? n4 : 0;
|
|
75477
|
+
}
|
|
75478
|
+
return 0;
|
|
75479
|
+
}
|
|
75480
|
+
function parseSummary(raw, siteId) {
|
|
75481
|
+
const summary = {
|
|
75482
|
+
id: String(raw["id"] ?? ""),
|
|
75483
|
+
lensSiteId: typeof raw["lensSiteId"] === "string" ? raw["lensSiteId"] : siteId,
|
|
75484
|
+
status: typeof raw["status"] === "string" ? raw["status"] : "",
|
|
75485
|
+
createdAt: typeof raw["createdAt"] === "string" ? raw["createdAt"] : "",
|
|
75486
|
+
submittedAt: typeof raw["submittedAt"] === "string" ? raw["submittedAt"] : null,
|
|
75487
|
+
externalIssue: raw["externalIssue"] ?? null,
|
|
75488
|
+
lensIssueId: typeof raw["lensIssueId"] === "string" ? raw["lensIssueId"] : null,
|
|
75489
|
+
issueOccurrenceCount: typeof raw["issueOccurrenceCount"] === "number" ? raw["issueOccurrenceCount"] : null,
|
|
75490
|
+
errorCount: toNumber2(raw["errorCount"]),
|
|
75491
|
+
hasReplay: raw["hasReplay"] === true
|
|
75492
|
+
};
|
|
75493
|
+
if (typeof raw["comment"] === "string") summary.comment = raw["comment"];
|
|
75494
|
+
if (typeof raw["url"] === "string") summary.url = raw["url"];
|
|
75495
|
+
return summary;
|
|
75496
|
+
}
|
|
75497
|
+
function parseReport(raw) {
|
|
75498
|
+
const o5 = raw ?? {};
|
|
75499
|
+
return {
|
|
75500
|
+
id: String(o5["id"] ?? ""),
|
|
75501
|
+
status: typeof o5["status"] === "string" ? o5["status"] : "",
|
|
75502
|
+
createdAt: typeof o5["createdAt"] === "string" ? o5["createdAt"] : "",
|
|
75503
|
+
submittedAt: typeof o5["submittedAt"] === "string" ? o5["submittedAt"] : null,
|
|
75504
|
+
metadata: parseMetadata(o5["metadata"])
|
|
75505
|
+
};
|
|
75506
|
+
}
|
|
75507
|
+
function parseMetadata(raw) {
|
|
75508
|
+
if (raw === null || raw === void 0 || typeof raw !== "object") return null;
|
|
75509
|
+
const m5 = raw;
|
|
75510
|
+
const meta = {
|
|
75511
|
+
errors: parseErrors(m5["errors"]),
|
|
75512
|
+
networkTail: parseNetworkTail(m5["networkTail"])
|
|
75513
|
+
};
|
|
75514
|
+
if (typeof m5["comment"] === "string") meta.comment = m5["comment"];
|
|
75515
|
+
if (typeof m5["trigger"] === "string") meta.trigger = m5["trigger"];
|
|
75516
|
+
const target = m5["target"];
|
|
75517
|
+
if (target && typeof target === "object") {
|
|
75518
|
+
const t2 = target;
|
|
75519
|
+
const parsed = {};
|
|
75520
|
+
if (typeof t2["domPath"] === "string") parsed.domPath = t2["domPath"];
|
|
75521
|
+
if (typeof t2["tagName"] === "string") parsed.tagName = t2["tagName"];
|
|
75522
|
+
if (typeof t2["text"] === "string") parsed.text = t2["text"];
|
|
75523
|
+
if (Array.isArray(t2["reactComponents"])) {
|
|
75524
|
+
parsed.reactComponents = t2["reactComponents"].filter(
|
|
75525
|
+
(c4) => typeof c4 === "string"
|
|
75526
|
+
);
|
|
75527
|
+
}
|
|
75528
|
+
meta.target = parsed;
|
|
75529
|
+
}
|
|
75530
|
+
const env = m5["env"];
|
|
75531
|
+
if (env && typeof env === "object") {
|
|
75532
|
+
const e = env;
|
|
75533
|
+
const parsed = { url: typeof e["url"] === "string" ? e["url"] : "" };
|
|
75534
|
+
if (typeof e["route"] === "string") parsed.route = e["route"];
|
|
75535
|
+
if (typeof e["userAgent"] === "string") parsed.userAgent = e["userAgent"];
|
|
75536
|
+
if (typeof e["browser"] === "string") parsed.browser = e["browser"];
|
|
75537
|
+
if (typeof e["os"] === "string") parsed.os = e["os"];
|
|
75538
|
+
meta.env = parsed;
|
|
75539
|
+
}
|
|
75540
|
+
const app = m5["app"];
|
|
75541
|
+
if (app && typeof app === "object") {
|
|
75542
|
+
const a2 = app;
|
|
75543
|
+
const parsed = {};
|
|
75544
|
+
if (typeof a2["release"] === "string") parsed.release = a2["release"];
|
|
75545
|
+
if (typeof a2["gitSha"] === "string") parsed.gitSha = a2["gitSha"];
|
|
75546
|
+
if (typeof a2["environment"] === "string") parsed.environment = a2["environment"];
|
|
75547
|
+
if (a2["featureFlags"] && typeof a2["featureFlags"] === "object") {
|
|
75548
|
+
parsed.featureFlags = a2["featureFlags"];
|
|
75549
|
+
}
|
|
75550
|
+
meta.app = parsed;
|
|
75551
|
+
}
|
|
75552
|
+
return meta;
|
|
75553
|
+
}
|
|
75554
|
+
function parseErrors(raw) {
|
|
75555
|
+
if (!Array.isArray(raw)) return [];
|
|
75556
|
+
return raw.filter((e) => typeof e["message"] === "string").map((e) => {
|
|
75557
|
+
const err2 = {
|
|
75558
|
+
message: e["message"],
|
|
75559
|
+
source: typeof e["source"] === "string" ? e["source"] : "",
|
|
75560
|
+
timestamp: toNumber2(e["timestamp"])
|
|
75561
|
+
};
|
|
75562
|
+
if (typeof e["stack"] === "string") err2.stack = e["stack"];
|
|
75563
|
+
return err2;
|
|
75564
|
+
});
|
|
75565
|
+
}
|
|
75566
|
+
function parseNetworkTail(raw) {
|
|
75567
|
+
if (!Array.isArray(raw)) return [];
|
|
75568
|
+
return raw.map((n4) => {
|
|
75569
|
+
const entry = {
|
|
75570
|
+
method: typeof n4["method"] === "string" ? n4["method"] : "",
|
|
75571
|
+
url: typeof n4["url"] === "string" ? n4["url"] : "",
|
|
75572
|
+
status: typeof n4["status"] === "number" ? n4["status"] : null,
|
|
75573
|
+
ok: typeof n4["ok"] === "boolean" ? n4["ok"] : null,
|
|
75574
|
+
durationMs: typeof n4["durationMs"] === "number" ? n4["durationMs"] : null,
|
|
75575
|
+
requestType: typeof n4["requestType"] === "string" ? n4["requestType"] : "",
|
|
75576
|
+
startedAt: toNumber2(n4["startedAt"])
|
|
75577
|
+
};
|
|
75578
|
+
if (typeof n4["error"] === "string") entry.error = n4["error"];
|
|
75579
|
+
return entry;
|
|
75580
|
+
});
|
|
75581
|
+
}
|
|
75582
|
+
|
|
75583
|
+
// ../../packages/connectors/src/lens/provider.ts
|
|
75584
|
+
init_cjs_shims();
|
|
75585
|
+
init_src();
|
|
75586
|
+
var LensProvider = class {
|
|
75587
|
+
constructor(client, opts = {}) {
|
|
75588
|
+
this.client = client;
|
|
75589
|
+
this.opts = opts;
|
|
75590
|
+
}
|
|
75591
|
+
client;
|
|
75592
|
+
opts;
|
|
75593
|
+
id = "lens";
|
|
75594
|
+
kind = "logs";
|
|
75595
|
+
sitesCache = null;
|
|
75596
|
+
get limit() {
|
|
75597
|
+
return this.opts.limit ?? 25;
|
|
75598
|
+
}
|
|
75599
|
+
get statusFilter() {
|
|
75600
|
+
return this.opts.statusFilter ?? "submitted";
|
|
75601
|
+
}
|
|
75602
|
+
/**
|
|
75603
|
+
* Collect recent reports across every site within the window and resolve each one's
|
|
75604
|
+
* top frame + failing requests. Reports are low-volume, so we fetch the full metadata
|
|
75605
|
+
* for up to `limit` (relevance-ranked) reports. A failing site LIST or reports LIST
|
|
75606
|
+
* PROPAGATES so the engine records a gap; per-report metadata fetches are best-effort
|
|
75607
|
+
* (a report still becomes a signal from its summary alone). Use queryEvidence() for the
|
|
75608
|
+
* degrade-to-[] contract.
|
|
75609
|
+
*/
|
|
75610
|
+
async collect(opts = {}) {
|
|
75611
|
+
const siteIds = await this.sites();
|
|
75612
|
+
const hintTerms = opts.hintTerms ?? [];
|
|
75613
|
+
const summaries = [];
|
|
75614
|
+
for (const siteId of siteIds) {
|
|
75615
|
+
const page = await this.client.listReports(siteId, {
|
|
75616
|
+
...opts.from !== void 0 ? { from: opts.from } : {},
|
|
75617
|
+
...opts.to !== void 0 ? { to: opts.to } : {},
|
|
75618
|
+
status: this.statusFilter,
|
|
75619
|
+
limit: this.limit
|
|
75620
|
+
});
|
|
75621
|
+
summaries.push(...page);
|
|
75622
|
+
}
|
|
75623
|
+
const windowed = summaries.filter((s5) => inWindow(s5.createdAt, opts.from, opts.to));
|
|
75624
|
+
const ranked = rankSummaries(windowed, hintTerms).slice(0, this.limit);
|
|
75625
|
+
const reports = await Promise.all(
|
|
75626
|
+
ranked.map(
|
|
75627
|
+
(s5) => this.client.getReport(s5.lensSiteId, s5.id).catch(() => null)
|
|
75628
|
+
)
|
|
75629
|
+
);
|
|
75630
|
+
return ranked.map((summary, i3) => buildSignal(summary, reports[i3] ?? null));
|
|
75631
|
+
}
|
|
75632
|
+
/**
|
|
75633
|
+
* Synthesize Evidence from Lens reports. One `kind: 'log'` Evidence per report, with the
|
|
75634
|
+
* comment as the human signature, the captured error + failing requests + app context in
|
|
75635
|
+
* the payload, and the top frame surfaced as `filePath`/`symbolName`/`lineStart` (payload)
|
|
75636
|
+
* and `file`/`line` (links) so the engine can seed directly on it.
|
|
75637
|
+
*/
|
|
75638
|
+
toEvidence(signals, hintTerms, collectedAt) {
|
|
75639
|
+
const query = `lens reports (status:${this.statusFilter}, up to ${this.limit})`;
|
|
75640
|
+
return signals.map((sig, i3) => signalToEvidence(sig, hintTerms, query, collectedAt, i3));
|
|
75641
|
+
}
|
|
75642
|
+
/**
|
|
75643
|
+
* One-shot evidence query: collect reports, convert to redacted Evidence. Preferred entry
|
|
75644
|
+
* point for the investigation engine. Degrades to [] on any failure so a flaky Cloud never
|
|
75645
|
+
* aborts an investigation.
|
|
75646
|
+
*/
|
|
75647
|
+
async queryEvidence(opts = {}) {
|
|
75648
|
+
try {
|
|
75649
|
+
const hintTerms = opts.hintTerms ?? [];
|
|
75650
|
+
const signals = await this.collect({
|
|
75651
|
+
...opts.from !== void 0 ? { from: opts.from } : {},
|
|
75652
|
+
...opts.to !== void 0 ? { to: opts.to } : {},
|
|
75653
|
+
hintTerms
|
|
75654
|
+
});
|
|
75655
|
+
return this.toEvidence(signals, hintTerms, opts.collectedAt ?? (/* @__PURE__ */ new Date()).toISOString());
|
|
75656
|
+
} catch {
|
|
75657
|
+
return [];
|
|
75658
|
+
}
|
|
75659
|
+
}
|
|
75660
|
+
async health() {
|
|
75661
|
+
return this.client.health();
|
|
75662
|
+
}
|
|
75663
|
+
/** In-instance site-id cache — reports are site-scoped and the site list is stable. */
|
|
75664
|
+
async sites() {
|
|
75665
|
+
if (this.sitesCache !== null) return this.sitesCache;
|
|
75666
|
+
const sites = await this.client.listSites();
|
|
75667
|
+
this.sitesCache = sites.map((s5) => s5.id);
|
|
75668
|
+
return this.sitesCache;
|
|
75669
|
+
}
|
|
75670
|
+
};
|
|
75671
|
+
function buildSignal(summary, report) {
|
|
75672
|
+
const meta = report?.metadata ?? null;
|
|
75673
|
+
const errors2 = meta?.errors ?? [];
|
|
75674
|
+
const { frame, message } = topFrameFromErrors(errors2);
|
|
75675
|
+
const signal = {
|
|
75676
|
+
id: summary.id,
|
|
75677
|
+
lensSiteId: summary.lensSiteId,
|
|
75678
|
+
status: summary.status,
|
|
75679
|
+
createdAt: summary.createdAt,
|
|
75680
|
+
submittedAt: summary.submittedAt,
|
|
75681
|
+
issueOccurrenceCount: summary.issueOccurrenceCount,
|
|
75682
|
+
errorCount: summary.errorCount || errors2.length,
|
|
75683
|
+
hasReplay: summary.hasReplay,
|
|
75684
|
+
topFrame: frame,
|
|
75685
|
+
failingRequests: failingRequestsFrom(meta?.networkTail ?? [])
|
|
75686
|
+
};
|
|
75687
|
+
const comment = meta?.comment ?? summary.comment;
|
|
75688
|
+
if (comment !== void 0) signal.comment = comment;
|
|
75689
|
+
const route2 = meta?.env?.route;
|
|
75690
|
+
if (route2 !== void 0) signal.route = route2;
|
|
75691
|
+
const url = meta?.env?.url && meta.env.url !== "" ? meta.env.url : summary.url;
|
|
75692
|
+
if (url !== void 0) signal.url = url;
|
|
75693
|
+
if (meta?.app?.release !== void 0) signal.release = meta.app.release;
|
|
75694
|
+
if (meta?.app?.gitSha !== void 0) signal.gitSha = meta.app.gitSha;
|
|
75695
|
+
if (meta?.app?.environment !== void 0) signal.environment = meta.app.environment;
|
|
75696
|
+
if (meta?.app?.featureFlags !== void 0) signal.featureFlags = meta.app.featureFlags;
|
|
75697
|
+
if (meta?.trigger !== void 0) signal.trigger = meta.trigger;
|
|
75698
|
+
if (message !== void 0) signal.topErrorMessage = message;
|
|
75699
|
+
const react = meta?.target?.reactComponents;
|
|
75700
|
+
if (react !== void 0 && react.length > 0) signal.reactComponents = react;
|
|
75701
|
+
return signal;
|
|
75702
|
+
}
|
|
75703
|
+
function failingRequestsFrom(tail) {
|
|
75704
|
+
return tail.filter((n4) => n4.ok === false || typeof n4.status === "number" && n4.status >= 500).slice(0, 5).map((n4) => ({ method: n4.method, url: n4.url, status: n4.status }));
|
|
75705
|
+
}
|
|
75706
|
+
function topFrameFromErrors(errors2) {
|
|
75707
|
+
let firstMessage;
|
|
75708
|
+
for (const err2 of errors2) {
|
|
75709
|
+
if (firstMessage === void 0 && err2.message) firstMessage = err2.message;
|
|
75710
|
+
if (!err2.stack) continue;
|
|
75711
|
+
const frame = parseStackTopFrame(err2.stack);
|
|
75712
|
+
if (frame) return { frame, message: err2.message || firstMessage };
|
|
75713
|
+
}
|
|
75714
|
+
return firstMessage !== void 0 ? { frame: null, message: firstMessage } : { frame: null };
|
|
75715
|
+
}
|
|
75716
|
+
function isSkippableLocation(location2) {
|
|
75717
|
+
if (location2 === "") return true;
|
|
75718
|
+
if (location2.includes("node_modules")) return true;
|
|
75719
|
+
if (/^[a-z-]+-extension:\/\//i.test(location2)) return true;
|
|
75720
|
+
if (location2.includes("[native code]") || location2.includes("<anonymous>")) return true;
|
|
75721
|
+
return false;
|
|
75722
|
+
}
|
|
75723
|
+
function parseStackTopFrame(stack) {
|
|
75724
|
+
const lines = stack.split("\n");
|
|
75725
|
+
for (const raw of lines) {
|
|
75726
|
+
const line2 = raw.trim();
|
|
75727
|
+
if (line2 === "") continue;
|
|
75728
|
+
let fnName;
|
|
75729
|
+
let location2;
|
|
75730
|
+
if (line2.startsWith("at ")) {
|
|
75731
|
+
const body2 = line2.slice(3).trim();
|
|
75732
|
+
const parenMatch = /^(.*?)\s*\((.+)\)\s*$/.exec(body2);
|
|
75733
|
+
if (parenMatch) {
|
|
75734
|
+
fnName = parenMatch[1]?.trim() || void 0;
|
|
75735
|
+
location2 = parenMatch[2].trim();
|
|
75736
|
+
} else {
|
|
75737
|
+
location2 = body2;
|
|
75738
|
+
}
|
|
75739
|
+
} else if (line2.includes("@")) {
|
|
75740
|
+
const at4 = line2.lastIndexOf("@");
|
|
75741
|
+
fnName = line2.slice(0, at4).trim() || void 0;
|
|
75742
|
+
location2 = line2.slice(at4 + 1).trim();
|
|
75743
|
+
} else {
|
|
75744
|
+
continue;
|
|
75745
|
+
}
|
|
75746
|
+
if (location2 === void 0 || isSkippableLocation(location2)) continue;
|
|
75747
|
+
const parsed = parseLocation(location2);
|
|
75748
|
+
if (!parsed) continue;
|
|
75749
|
+
if (isSkippableLocation(parsed.filename)) continue;
|
|
75750
|
+
const frame = { filename: parsed.filename };
|
|
75751
|
+
if (fnName !== void 0 && fnName !== "") frame.function = fnName;
|
|
75752
|
+
if (parsed.lineno !== void 0) frame.lineno = parsed.lineno;
|
|
75753
|
+
return frame;
|
|
75754
|
+
}
|
|
75755
|
+
return null;
|
|
75756
|
+
}
|
|
75757
|
+
function parseLocation(location2) {
|
|
75758
|
+
const m5 = /^(.*?):(\d+)(?::(\d+))?$/.exec(location2);
|
|
75759
|
+
const rawUrl = m5 ? m5[1] : location2;
|
|
75760
|
+
const lineno = m5 ? Number(m5[2]) : void 0;
|
|
75761
|
+
let filename = rawUrl;
|
|
75762
|
+
try {
|
|
75763
|
+
if (/^[a-z]+:\/\//i.test(rawUrl)) filename = new URL(rawUrl).pathname;
|
|
75764
|
+
} catch {
|
|
75765
|
+
filename = rawUrl;
|
|
75766
|
+
}
|
|
75767
|
+
if (filename === "") return null;
|
|
75768
|
+
return lineno !== void 0 && Number.isFinite(lineno) ? { filename, lineno } : { filename };
|
|
75769
|
+
}
|
|
75770
|
+
function signalToEvidence(sig, hintTerms, query, collectedAt, index2) {
|
|
75771
|
+
const frame = sig.topFrame;
|
|
75772
|
+
const title = buildTitle2(sig);
|
|
75773
|
+
const relevance = computeRelevance2(sig, hintTerms);
|
|
75774
|
+
const payload = {
|
|
75775
|
+
source: "lens",
|
|
75776
|
+
reportId: sig.id,
|
|
75777
|
+
...sig.comment !== void 0 ? { comment: redactSecrets(sig.comment) } : {},
|
|
75778
|
+
...sig.route !== void 0 ? { route: sig.route } : {},
|
|
75779
|
+
...sig.url !== void 0 ? { url: sig.url } : {},
|
|
75780
|
+
...sig.release !== void 0 ? { release: sig.release } : {},
|
|
75781
|
+
...sig.gitSha !== void 0 ? { gitSha: sig.gitSha } : {},
|
|
75782
|
+
...sig.trigger !== void 0 ? { trigger: sig.trigger } : {},
|
|
75783
|
+
errorCount: sig.errorCount,
|
|
75784
|
+
...sig.topErrorMessage !== void 0 ? { topError: redactSecrets(sig.topErrorMessage) } : {},
|
|
75785
|
+
...sig.failingRequests.length > 0 ? { failingRequests: sig.failingRequests } : {},
|
|
75786
|
+
...sig.reactComponents !== void 0 ? { reactComponents: sig.reactComponents } : {},
|
|
75787
|
+
...sig.featureFlags !== void 0 ? { featureFlags: sig.featureFlags } : {}
|
|
75788
|
+
};
|
|
75789
|
+
if (frame?.filename !== void 0) payload["filePath"] = frame.filename;
|
|
75790
|
+
if (frame?.function !== void 0) payload["symbolName"] = frame.function;
|
|
75791
|
+
if (frame?.lineno !== void 0) payload["lineStart"] = frame.lineno;
|
|
75792
|
+
const links = {};
|
|
75793
|
+
if (frame?.filename !== void 0) links.file = frame.filename;
|
|
75794
|
+
if (frame?.lineno !== void 0) links.line = frame.lineno;
|
|
75795
|
+
const ev = {
|
|
75796
|
+
id: `ev_lens_${index2}`,
|
|
75797
|
+
source: "logs",
|
|
75798
|
+
kind: "log",
|
|
75799
|
+
title,
|
|
75800
|
+
relevance,
|
|
75801
|
+
payload,
|
|
75802
|
+
links,
|
|
75803
|
+
provenance: { query, collectedAt }
|
|
75804
|
+
};
|
|
75805
|
+
const ts = sig.submittedAt ?? sig.createdAt;
|
|
75806
|
+
if (ts) ev.timestamp = ts;
|
|
75807
|
+
return ev;
|
|
75808
|
+
}
|
|
75809
|
+
function buildTitle2(sig) {
|
|
75810
|
+
const rawComment = sig.comment ? redactSecrets(sig.comment) : "";
|
|
75811
|
+
const comment = rawComment !== "" ? rawComment.slice(0, 80) : "(no comment)";
|
|
75812
|
+
const errs = `${sig.errorCount} error(s)`;
|
|
75813
|
+
const where = sig.route ?? sig.url ?? "";
|
|
75814
|
+
const whereStr = where !== "" ? ` \xB7 ${where}` : "";
|
|
75815
|
+
const when = sig.createdAt ? ` \xB7 ${shortTs3(sig.createdAt)}` : "";
|
|
75816
|
+
return `Lens report: "${comment}" \xB7 ${errs}${whereStr}${when}`.slice(0, 220);
|
|
75817
|
+
}
|
|
75818
|
+
function computeRelevance2(sig, hintTerms, now = Date.now()) {
|
|
75819
|
+
let score = 0.6;
|
|
75820
|
+
const hay = [
|
|
75821
|
+
sig.comment ?? "",
|
|
75822
|
+
sig.route ?? "",
|
|
75823
|
+
sig.url ?? "",
|
|
75824
|
+
sig.topErrorMessage ?? "",
|
|
75825
|
+
sig.topFrame?.filename ?? "",
|
|
75826
|
+
sig.topFrame?.function ?? ""
|
|
75827
|
+
].join(" ").toLowerCase();
|
|
75828
|
+
const domainTerms = hintTerms.filter((t2) => t2.length > 2);
|
|
75829
|
+
if (domainTerms.some((t2) => hay.includes(t2.toLowerCase()))) score += 0.2;
|
|
75830
|
+
const ts = sig.submittedAt ?? sig.createdAt;
|
|
75831
|
+
if (ts) {
|
|
75832
|
+
const ageMs = now - Date.parse(ts);
|
|
75833
|
+
if (Number.isFinite(ageMs)) {
|
|
75834
|
+
if (ageMs <= 864e5) score += 0.1;
|
|
75835
|
+
else if (ageMs <= 7 * 864e5) score += 0.05;
|
|
75836
|
+
}
|
|
75837
|
+
}
|
|
75838
|
+
if ((sig.issueOccurrenceCount ?? 0) >= 3) score += 0.05;
|
|
75839
|
+
return Math.min(0.95, Math.max(0.5, score));
|
|
75840
|
+
}
|
|
75841
|
+
function rankSummaries(summaries, hintTerms, now = Date.now()) {
|
|
75842
|
+
const domainTerms = hintTerms.filter((t2) => t2.length > 2);
|
|
75843
|
+
const score = (s5) => {
|
|
75844
|
+
let v4 = 0;
|
|
75845
|
+
const hay = `${s5.comment ?? ""} ${s5.url ?? ""}`.toLowerCase();
|
|
75846
|
+
if (domainTerms.some((t2) => hay.includes(t2.toLowerCase()))) v4 += 2;
|
|
75847
|
+
if (s5.createdAt) {
|
|
75848
|
+
const ageMs = now - Date.parse(s5.createdAt);
|
|
75849
|
+
if (Number.isFinite(ageMs) && ageMs <= 864e5) v4 += 1;
|
|
75850
|
+
}
|
|
75851
|
+
if ((s5.issueOccurrenceCount ?? 0) >= 3) v4 += 0.5;
|
|
75852
|
+
return v4;
|
|
75853
|
+
};
|
|
75854
|
+
return [...summaries].sort((a2, b5) => {
|
|
75855
|
+
const d3 = score(b5) - score(a2);
|
|
75856
|
+
if (d3 !== 0) return d3;
|
|
75857
|
+
return (Date.parse(b5.createdAt) || 0) - (Date.parse(a2.createdAt) || 0);
|
|
75858
|
+
});
|
|
75859
|
+
}
|
|
75860
|
+
function inWindow(createdAt, from, to) {
|
|
75861
|
+
const t2 = Date.parse(createdAt);
|
|
75862
|
+
if (!Number.isFinite(t2)) return true;
|
|
75863
|
+
const lo = from !== void 0 ? Date.parse(from) : NaN;
|
|
75864
|
+
const hi = to !== void 0 ? Date.parse(to) : NaN;
|
|
75865
|
+
if (Number.isFinite(lo) && t2 < lo) return false;
|
|
75866
|
+
if (Number.isFinite(hi) && t2 > hi) return false;
|
|
75867
|
+
return true;
|
|
75868
|
+
}
|
|
75869
|
+
function shortTs3(iso) {
|
|
75870
|
+
if (!iso || iso.length < 16) return iso || "\u2014";
|
|
75871
|
+
return `${iso.slice(5, 10)} ${iso.slice(11, 16)}`;
|
|
75872
|
+
}
|
|
75873
|
+
|
|
75379
75874
|
// ../../packages/connectors/src/axiom/client.ts
|
|
75380
75875
|
init_cjs_shims();
|
|
75381
75876
|
init_src();
|
|
@@ -75629,8 +76124,8 @@ var AxiomProvider = class {
|
|
|
75629
76124
|
return this.client.health();
|
|
75630
76125
|
}
|
|
75631
76126
|
recordToEvidence(rec, hintTerms, query, collectedAt, index2) {
|
|
75632
|
-
const title =
|
|
75633
|
-
const relevance =
|
|
76127
|
+
const title = buildTitle3(rec, this.opts.dataset);
|
|
76128
|
+
const relevance = computeRelevance3(rec, hintTerms);
|
|
75634
76129
|
const payload = {
|
|
75635
76130
|
source: "axiom",
|
|
75636
76131
|
dataset: this.opts.dataset,
|
|
@@ -75660,7 +76155,7 @@ function pickField2(fields, candidates) {
|
|
|
75660
76155
|
}
|
|
75661
76156
|
return void 0;
|
|
75662
76157
|
}
|
|
75663
|
-
function
|
|
76158
|
+
function buildTitle3(rec, dataset) {
|
|
75664
76159
|
const msg = pickField2(rec.fields, MESSAGE_FIELDS);
|
|
75665
76160
|
const level = pickField2(rec.fields, LEVEL_FIELDS);
|
|
75666
76161
|
const lvl = level ? `[${level}] ` : "";
|
|
@@ -75668,13 +76163,13 @@ function buildTitle2(rec, dataset) {
|
|
|
75668
76163
|
const rawCount = rec.fields["count"];
|
|
75669
76164
|
const count = typeof rawCount === "number" && Number.isFinite(rawCount) && rawCount > 1 ? rawCount : void 0;
|
|
75670
76165
|
if (count !== void 0) {
|
|
75671
|
-
const latest = rec.timestamp ? ` (latest ${
|
|
76166
|
+
const latest = rec.timestamp ? ` (latest ${shortTs4(rec.timestamp)})` : "";
|
|
75672
76167
|
return `Axiom ${dataset}: ${lvl}${body2} \xD7${count.toLocaleString("en-US")}${latest}`.slice(0, 220);
|
|
75673
76168
|
}
|
|
75674
|
-
const ts = rec.timestamp ? ` \xB7 ${
|
|
76169
|
+
const ts = rec.timestamp ? ` \xB7 ${shortTs4(rec.timestamp)}` : "";
|
|
75675
76170
|
return `Axiom ${dataset}: ${lvl}${body2}${ts}`.slice(0, 220);
|
|
75676
76171
|
}
|
|
75677
|
-
function
|
|
76172
|
+
function computeRelevance3(rec, hintTerms, now = Date.now()) {
|
|
75678
76173
|
let score = 0.6;
|
|
75679
76174
|
const hay = JSON.stringify(rec.fields).toLowerCase();
|
|
75680
76175
|
const domainTerms = hintTerms.filter((t2) => t2.length > 2);
|
|
@@ -75693,7 +76188,7 @@ function computeRelevance2(rec, hintTerms, now = Date.now()) {
|
|
|
75693
76188
|
if (typeof count === "number" && count >= 10) score += 0.1;
|
|
75694
76189
|
return Math.min(0.95, Math.max(0.5, score));
|
|
75695
76190
|
}
|
|
75696
|
-
function
|
|
76191
|
+
function shortTs4(iso) {
|
|
75697
76192
|
if (!iso || iso.length < 16) return iso || "\u2014";
|
|
75698
76193
|
return `${iso.slice(5, 10)} ${iso.slice(11, 16)}`;
|
|
75699
76194
|
}
|
|
@@ -75942,8 +76437,8 @@ var ShopifyProvider = class {
|
|
|
75942
76437
|
id: `ev_shopify_${index2}`,
|
|
75943
76438
|
source: KIND_TO_SOURCE[rec.kind],
|
|
75944
76439
|
kind: rec.kind,
|
|
75945
|
-
title:
|
|
75946
|
-
relevance:
|
|
76440
|
+
title: buildTitle4(rec, this.opts.store),
|
|
76441
|
+
relevance: computeRelevance4(rec, hintTerms),
|
|
75947
76442
|
payload,
|
|
75948
76443
|
links: {},
|
|
75949
76444
|
provenance: { query: `shopify ${this.opts.store} ${rec.name}`, collectedAt }
|
|
@@ -75960,8 +76455,8 @@ function bindWindowVars(q2, from, to) {
|
|
|
75960
76455
|
if (to !== void 0) window2["to"] = to;
|
|
75961
76456
|
return { ...base2, ...window2 };
|
|
75962
76457
|
}
|
|
75963
|
-
function
|
|
75964
|
-
const ts = rec.timestamp ? ` \xB7 ${
|
|
76458
|
+
function buildTitle4(rec, store) {
|
|
76459
|
+
const ts = rec.timestamp ? ` \xB7 ${shortTs5(rec.timestamp)}` : "";
|
|
75965
76460
|
return redactSecrets(`Shopify ${store} ${rec.name}: ${summarize2(rec)}${ts}`).slice(0, 220);
|
|
75966
76461
|
}
|
|
75967
76462
|
function summarize2(rec) {
|
|
@@ -75988,14 +76483,14 @@ function countResults(data) {
|
|
|
75988
76483
|
}
|
|
75989
76484
|
return found ? total : null;
|
|
75990
76485
|
}
|
|
75991
|
-
function
|
|
76486
|
+
function computeRelevance4(rec, hintTerms) {
|
|
75992
76487
|
let score = rec.relevance ?? 0.6;
|
|
75993
76488
|
const hay = JSON.stringify(rec.data ?? {}).toLowerCase();
|
|
75994
76489
|
const domainTerms = hintTerms.filter((t2) => t2.length > 2);
|
|
75995
76490
|
if (domainTerms.some((t2) => hay.includes(t2.toLowerCase()))) score += 0.2;
|
|
75996
76491
|
return Math.min(0.95, Math.max(0.5, score));
|
|
75997
76492
|
}
|
|
75998
|
-
function
|
|
76493
|
+
function shortTs5(iso) {
|
|
75999
76494
|
if (!iso || iso.length < 16) return iso || "\u2014";
|
|
76000
76495
|
return `${iso.slice(5, 10)} ${iso.slice(11, 16)}`;
|
|
76001
76496
|
}
|
|
@@ -76714,6 +77209,15 @@ function shopifyForEnv(renv) {
|
|
|
76714
77209
|
}
|
|
76715
77210
|
);
|
|
76716
77211
|
}
|
|
77212
|
+
function lensForCloud(opts) {
|
|
77213
|
+
return new LensProvider(
|
|
77214
|
+
new LensCloudClient({
|
|
77215
|
+
apiBaseUrl: opts.apiBaseUrl,
|
|
77216
|
+
token: opts.token,
|
|
77217
|
+
workspaceId: opts.workspaceId
|
|
77218
|
+
})
|
|
77219
|
+
);
|
|
77220
|
+
}
|
|
76717
77221
|
function codeForRepo(config2, repoName) {
|
|
76718
77222
|
const renv = resolveEnvironment(config2, { project: repoName });
|
|
76719
77223
|
const c4 = codeForEnv(renv);
|
|
@@ -77096,6 +77600,9 @@ async function redisServerStatus(renv) {
|
|
|
77096
77600
|
// ../../packages/connectors/src/shopify/index.ts
|
|
77097
77601
|
init_cjs_shims();
|
|
77098
77602
|
|
|
77603
|
+
// ../../packages/connectors/src/lens/index.ts
|
|
77604
|
+
init_cjs_shims();
|
|
77605
|
+
|
|
77099
77606
|
// ../../packages/cli/src/lib/bundled-backend.ts
|
|
77100
77607
|
init_cjs_shims();
|
|
77101
77608
|
var import_node_fs5 = require("fs");
|
|
@@ -77378,6 +77885,35 @@ var CloudClient = class {
|
|
|
77378
77885
|
`/v1/projects/${projectId}/memory-items${qs ? `?${qs}` : ""}`
|
|
77379
77886
|
);
|
|
77380
77887
|
}
|
|
77888
|
+
// ── Team memory (HOR-464 / M4) ────────────────────────────────────────────
|
|
77889
|
+
/**
|
|
77890
|
+
* One-way PROMOTE local items into the workspace's shared team-memory store. Server-wins &
|
|
77891
|
+
* idempotent (`ON CONFLICT (workspace_id, origin_client_id) DO NOTHING`); it clamps/skips
|
|
77892
|
+
* confirmed-outcome (privacy). The body is a positive allowlist — vectors NEVER cross the wire.
|
|
77893
|
+
*/
|
|
77894
|
+
promoteTeamMemory(projectId, body2) {
|
|
77895
|
+
return this.request(
|
|
77896
|
+
"POST",
|
|
77897
|
+
`/v1/projects/${projectId}/team-memory/promote`,
|
|
77898
|
+
body2
|
|
77899
|
+
);
|
|
77900
|
+
}
|
|
77901
|
+
/**
|
|
77902
|
+
* Read the workspace's team-memory items with `seq > since`, ORDER BY seq ASC. The v1 client pulls
|
|
77903
|
+
* `since:'0'` and paginates on `nextCursor` until `hasMore` is false, then reconciles the local
|
|
77904
|
+
* cache. `includeDeleted` surfaces tombstones (deletedAt) so the cache can drop removed rows.
|
|
77905
|
+
*/
|
|
77906
|
+
listTeamMemorySince(projectId, q2) {
|
|
77907
|
+
const params = new URLSearchParams();
|
|
77908
|
+
params.set("since", q2.since);
|
|
77909
|
+
if (q2.limit !== void 0) params.set("limit", String(q2.limit));
|
|
77910
|
+
if (q2.includeDeleted) params.set("includeDeleted", "true");
|
|
77911
|
+
if (q2.search) params.set("search", q2.search);
|
|
77912
|
+
return this.request(
|
|
77913
|
+
"GET",
|
|
77914
|
+
`/v1/projects/${projectId}/team-memory?${params.toString()}`
|
|
77915
|
+
);
|
|
77916
|
+
}
|
|
77381
77917
|
};
|
|
77382
77918
|
|
|
77383
77919
|
// ../../packages/cli/src/lib/cloud/auth-store.ts
|
|
@@ -84445,6 +84981,21 @@ var memoryItem = pgTable(
|
|
|
84445
84981
|
userId: text("user_id"),
|
|
84446
84982
|
visibility: text("visibility").notNull().default("private"),
|
|
84447
84983
|
// private|team
|
|
84984
|
+
// HOR-464 (M4): team-memory cache provenance. `origin` distinguishes a locally-authored row
|
|
84985
|
+
// (`local`) from a disposable read-cache row pulled from the cloud team store (`cloud`). The
|
|
84986
|
+
// cloud-owned copy is server-authoritative — `origin='cloud'` rows are readonly locally (any
|
|
84987
|
+
// local mutation throws `MemoryCacheReadonlyError`; re-promote to change) and are NEVER re-pushed
|
|
84988
|
+
// by `memory sync` (which pushes `origin='local'` only). `cloudId` is the server team-memory id,
|
|
84989
|
+
// `authorName` the promoting teammate (DISPLAY-ONLY — never feeds rank), `pulledAt` the last
|
|
84990
|
+
// refresh time (staleness).
|
|
84991
|
+
origin: text("origin").notNull().default("local"),
|
|
84992
|
+
// local|cloud
|
|
84993
|
+
cloudId: text("cloud_id"),
|
|
84994
|
+
// server team_memory id (provenance/join)
|
|
84995
|
+
authorName: text("author_name"),
|
|
84996
|
+
// promoting teammate's display name (attribution, display-only)
|
|
84997
|
+
pulledAt: timestamp("pulled_at", { withTimezone: true }),
|
|
84998
|
+
// last cache-refresh time
|
|
84448
84999
|
payload: jsonb("payload"),
|
|
84449
85000
|
// forward-compat, zero-migration extension
|
|
84450
85001
|
// Incident-family recall keys, populated at WRITE for incident-derived kinds
|
|
@@ -84461,7 +85012,9 @@ var memoryItem = pgTable(
|
|
|
84461
85012
|
index("memory_item_scope_idx").on(t2.scope),
|
|
84462
85013
|
index("memory_item_status_idx").on(t2.status),
|
|
84463
85014
|
index("memory_item_tenancy_idx").on(t2.orgId, t2.workspaceId, t2.repo, t2.userId),
|
|
84464
|
-
index("memory_item_signature_idx").on(t2.signature)
|
|
85015
|
+
index("memory_item_signature_idx").on(t2.signature),
|
|
85016
|
+
// HOR-464: fast repo-scoped split of local-authored vs cloud-cached rows (pull reconcile + sync filter).
|
|
85017
|
+
index("memory_item_origin_idx").on(t2.repo, t2.origin)
|
|
84465
85018
|
]
|
|
84466
85019
|
);
|
|
84467
85020
|
var memoryLink = pgTable(
|
|
@@ -92771,6 +93324,16 @@ WHERE im.investigation_id IS NOT NULL AND im.project IS NULL;`,
|
|
|
92771
93324
|
WHERE "project" IS NULL AND incident_input->>'repo' IS NOT NULL;`,
|
|
92772
93325
|
'CREATE INDEX "investigations_project_created_idx" ON "investigations" ("project","created_at");'
|
|
92773
93326
|
]
|
|
93327
|
+
},
|
|
93328
|
+
{
|
|
93329
|
+
"tag": "0012_memory_item_origin",
|
|
93330
|
+
"statements": [
|
|
93331
|
+
`ALTER TABLE "memory_item" ADD COLUMN "origin" text DEFAULT 'local' NOT NULL;`,
|
|
93332
|
+
'ALTER TABLE "memory_item" ADD COLUMN "cloud_id" text;',
|
|
93333
|
+
'ALTER TABLE "memory_item" ADD COLUMN "author_name" text;',
|
|
93334
|
+
'ALTER TABLE "memory_item" ADD COLUMN "pulled_at" timestamp with time zone;',
|
|
93335
|
+
'CREATE INDEX IF NOT EXISTS "memory_item_origin_idx" ON "memory_item" ("repo","origin");'
|
|
93336
|
+
]
|
|
92774
93337
|
}
|
|
92775
93338
|
];
|
|
92776
93339
|
|
|
@@ -94177,6 +94740,7 @@ init_cjs_shims();
|
|
|
94177
94740
|
|
|
94178
94741
|
// ../../packages/engine/src/engine.ts
|
|
94179
94742
|
init_cjs_shims();
|
|
94743
|
+
init_src();
|
|
94180
94744
|
|
|
94181
94745
|
// ../../packages/engine/src/render.ts
|
|
94182
94746
|
init_cjs_shims();
|
|
@@ -96403,6 +96967,16 @@ var MemorySecretError = class extends Error {
|
|
|
96403
96967
|
this.kind = kind;
|
|
96404
96968
|
}
|
|
96405
96969
|
};
|
|
96970
|
+
var MemoryCacheReadonlyError = class extends Error {
|
|
96971
|
+
id;
|
|
96972
|
+
constructor(id) {
|
|
96973
|
+
super(
|
|
96974
|
+
`refusing to mutate a cloud-owned cached memory item (${id}); the team store owns it \u2014 re-promote to change it`
|
|
96975
|
+
);
|
|
96976
|
+
this.name = "MemoryCacheReadonlyError";
|
|
96977
|
+
this.id = id;
|
|
96978
|
+
}
|
|
96979
|
+
};
|
|
96406
96980
|
var CLAIM_SECRET_PATTERNS = [
|
|
96407
96981
|
{ label: "private-key", re: /-----BEGIN (?:[A-Z ]+ )?PRIVATE KEY-----/ },
|
|
96408
96982
|
{ label: "jwt", re: /\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/ },
|
|
@@ -96562,6 +97136,7 @@ function createLocalMemoryStore(db) {
|
|
|
96562
97136
|
async update(id, patch, opts) {
|
|
96563
97137
|
const current = await getById(id);
|
|
96564
97138
|
if (current === null) throw new Error(`memory item not found: ${id}`);
|
|
97139
|
+
if (current.origin === "cloud") throw new MemoryCacheReadonlyError(id);
|
|
96565
97140
|
const set = {};
|
|
96566
97141
|
if (patch.claim !== void 0) {
|
|
96567
97142
|
assertClaimClean(patch.claim);
|
|
@@ -96594,6 +97169,7 @@ function createLocalMemoryStore(db) {
|
|
|
96594
97169
|
if (repo === "") return [];
|
|
96595
97170
|
const conds = [eq(memoryItem.repo, repo)];
|
|
96596
97171
|
if (q2.scope !== void 0) conds.push(eq(memoryItem.scope, q2.scope));
|
|
97172
|
+
if (q2.origin !== void 0) conds.push(eq(memoryItem.origin, q2.origin));
|
|
96597
97173
|
if (q2.visibility !== void 0) conds.push(eq(memoryItem.visibility, q2.visibility));
|
|
96598
97174
|
if (q2.orgId !== void 0) conds.push(eq(memoryItem.orgId, q2.orgId));
|
|
96599
97175
|
if (q2.workspaceId !== void 0) conds.push(eq(memoryItem.workspaceId, q2.workspaceId));
|
|
@@ -96614,6 +97190,7 @@ function createLocalMemoryStore(db) {
|
|
|
96614
97190
|
async setStatus(id, status, audit) {
|
|
96615
97191
|
const current = await getById(id);
|
|
96616
97192
|
if (current === null) throw new Error(`memory item not found: ${id}`);
|
|
97193
|
+
if (current.origin === "cloud") throw new MemoryCacheReadonlyError(id);
|
|
96617
97194
|
await db.update(memoryItem).set({ status }).where(eq(memoryItem.id, id));
|
|
96618
97195
|
await appendAudit({
|
|
96619
97196
|
memoryId: id,
|
|
@@ -96627,6 +97204,7 @@ function createLocalMemoryStore(db) {
|
|
|
96627
97204
|
async setVisibility(id, v4, audit) {
|
|
96628
97205
|
const current = await getById(id);
|
|
96629
97206
|
if (current === null) throw new Error(`memory item not found: ${id}`);
|
|
97207
|
+
if (current.origin === "cloud") throw new MemoryCacheReadonlyError(id);
|
|
96630
97208
|
await db.update(memoryItem).set({ visibility: v4 }).where(eq(memoryItem.id, id));
|
|
96631
97209
|
await appendAudit({
|
|
96632
97210
|
memoryId: id,
|
|
@@ -96771,6 +97349,96 @@ function createLocalMemoryStore(db) {
|
|
|
96771
97349
|
},
|
|
96772
97350
|
async history(id) {
|
|
96773
97351
|
return db.select().from(memoryAudit).where(eq(memoryAudit.memoryId, id)).orderBy(desc(memoryAudit.at), desc(memoryAudit.id));
|
|
97352
|
+
},
|
|
97353
|
+
// ---- HOR-464 / M4: disposable team-memory read-cache + one-way promote ----
|
|
97354
|
+
async upsertCached(item, audit) {
|
|
97355
|
+
assertClaimClean(item.claim);
|
|
97356
|
+
const repo = (item.repo ?? "").trim();
|
|
97357
|
+
if (repo === "") throw new Error("memory_item requires a repo (HOR-46 fail-closed)");
|
|
97358
|
+
const id = (item.id ?? "").trim();
|
|
97359
|
+
if (id === "") throw new Error("upsertCached requires a stable item id (the origin client id)");
|
|
97360
|
+
const status = item.status ?? "fresh";
|
|
97361
|
+
const now = /* @__PURE__ */ new Date();
|
|
97362
|
+
const values2 = {
|
|
97363
|
+
...item,
|
|
97364
|
+
id,
|
|
97365
|
+
repo,
|
|
97366
|
+
status,
|
|
97367
|
+
origin: "cloud",
|
|
97368
|
+
pulledAt: now,
|
|
97369
|
+
signature: null,
|
|
97370
|
+
tags: null
|
|
97371
|
+
};
|
|
97372
|
+
await db.insert(memoryItem).values(values2).onConflictDoUpdate({
|
|
97373
|
+
target: memoryItem.id,
|
|
97374
|
+
set: {
|
|
97375
|
+
kind: values2.kind,
|
|
97376
|
+
claim: values2.claim,
|
|
97377
|
+
scope: values2.scope,
|
|
97378
|
+
source: values2.source,
|
|
97379
|
+
status,
|
|
97380
|
+
confidence: values2.confidence,
|
|
97381
|
+
visibility: values2.visibility ?? "team",
|
|
97382
|
+
evidence: values2.evidence,
|
|
97383
|
+
cloudId: values2.cloudId ?? null,
|
|
97384
|
+
authorName: values2.authorName ?? null,
|
|
97385
|
+
lastVerifiedAt: values2.lastVerifiedAt ?? null,
|
|
97386
|
+
lastVerifiedHash: values2.lastVerifiedHash ?? null,
|
|
97387
|
+
origin: "cloud",
|
|
97388
|
+
// Re-scope to the repo we're pulling into. The cache is disposable and
|
|
97389
|
+
// project-isolated (HOR-46): a team item's id is the promoter's local id and is
|
|
97390
|
+
// stable across a workspace, so pulling it from a different repo on the same store
|
|
97391
|
+
// must move the cache row to the current repo — otherwise "pull here" silently
|
|
97392
|
+
// leaves it invisible here, stuck in whichever repo first pulled it.
|
|
97393
|
+
repo: values2.repo,
|
|
97394
|
+
pulledAt: now
|
|
97395
|
+
}
|
|
97396
|
+
});
|
|
97397
|
+
const row = await getById(id);
|
|
97398
|
+
if (row === null) throw new Error("memory_item upsertCached returned no row");
|
|
97399
|
+
await appendAudit({
|
|
97400
|
+
memoryId: id,
|
|
97401
|
+
action: "pulled",
|
|
97402
|
+
actor: audit.actor,
|
|
97403
|
+
fromStatus: null,
|
|
97404
|
+
toStatus: status,
|
|
97405
|
+
note: audit.note ?? null,
|
|
97406
|
+
detail: { origin: "cloud", ...values2.cloudId ? { cloudId: values2.cloudId } : {} }
|
|
97407
|
+
});
|
|
97408
|
+
return row;
|
|
97409
|
+
},
|
|
97410
|
+
async reconcileCache(repo, keepIds) {
|
|
97411
|
+
const r = repo.trim();
|
|
97412
|
+
if (r === "") return 0;
|
|
97413
|
+
const conds = keepIds.length > 0 ? and(
|
|
97414
|
+
eq(memoryItem.repo, r),
|
|
97415
|
+
eq(memoryItem.origin, "cloud"),
|
|
97416
|
+
notInArray(memoryItem.id, keepIds)
|
|
97417
|
+
) : and(eq(memoryItem.repo, r), eq(memoryItem.origin, "cloud"));
|
|
97418
|
+
const deleted = await db.delete(memoryItem).where(conds).returning();
|
|
97419
|
+
return deleted.length;
|
|
97420
|
+
},
|
|
97421
|
+
async markPromoted(id, provenance, audit) {
|
|
97422
|
+
const current = await getById(id);
|
|
97423
|
+
if (current === null) throw new Error(`memory item not found: ${id}`);
|
|
97424
|
+
await db.update(memoryItem).set({
|
|
97425
|
+
visibility: "team",
|
|
97426
|
+
origin: "cloud",
|
|
97427
|
+
cloudId: provenance.cloudId,
|
|
97428
|
+
authorName: provenance.authorName ?? current.authorName ?? null
|
|
97429
|
+
}).where(eq(memoryItem.id, id));
|
|
97430
|
+
await appendAudit({
|
|
97431
|
+
memoryId: id,
|
|
97432
|
+
action: "promote",
|
|
97433
|
+
actor: audit.actor,
|
|
97434
|
+
fromStatus: null,
|
|
97435
|
+
toStatus: null,
|
|
97436
|
+
note: audit.note ?? `promoted to team (cloud id ${provenance.cloudId})`,
|
|
97437
|
+
detail: { origin: "cloud", visibility: "team", cloudId: provenance.cloudId }
|
|
97438
|
+
});
|
|
97439
|
+
const updated = await getById(id);
|
|
97440
|
+
if (updated === null) throw new Error(`memory item vanished during promote: ${id}`);
|
|
97441
|
+
return updated;
|
|
96774
97442
|
}
|
|
96775
97443
|
};
|
|
96776
97444
|
}
|
|
@@ -97168,7 +97836,7 @@ async function captureInvestigationMemory(store, investigationId, report, report
|
|
|
97168
97836
|
init_cjs_shims();
|
|
97169
97837
|
function hasAnyRuntimeConnector(c4) {
|
|
97170
97838
|
return Boolean(
|
|
97171
|
-
c4.elasticsearch || c4.sentry || c4.axiom || c4.grafana || c4.mongodb || c4.postgres || c4.redis || c4.queue || c4.shopify
|
|
97839
|
+
c4.elasticsearch || c4.sentry || c4.lens || c4.axiom || c4.grafana || c4.mongodb || c4.postgres || c4.redis || c4.queue || c4.shopify
|
|
97172
97840
|
);
|
|
97173
97841
|
}
|
|
97174
97842
|
function isUnavailable(c4, kind) {
|
|
@@ -99895,6 +100563,88 @@ async function investigate(input, deps) {
|
|
|
99895
100563
|
sentryFailureReason = connectorFailureReason(sentryErr);
|
|
99896
100564
|
}
|
|
99897
100565
|
}
|
|
100566
|
+
let lensCollected = false;
|
|
100567
|
+
let lensFailureReason;
|
|
100568
|
+
let lensReportCount = 0;
|
|
100569
|
+
if (deps.lens) {
|
|
100570
|
+
try {
|
|
100571
|
+
const from = logWindowFrom(input.logsSince ?? input.since);
|
|
100572
|
+
const to = (/* @__PURE__ */ new Date()).toISOString();
|
|
100573
|
+
const lensSeedBase = top ? top.filePath.split("/").pop() ?? "" : "";
|
|
100574
|
+
const lensTerms = top ? [.../* @__PURE__ */ new Set([...tokenize(hint), ...tokenize(top.name), ...tokenize(lensSeedBase)])] : [...new Set(tokenize(hint))];
|
|
100575
|
+
const seedFileBase = top ? (top.filePath.split("/").pop() ?? "").toLowerCase() : "";
|
|
100576
|
+
const signals = await deps.lens.collect({ from, to, hintTerms: lensTerms });
|
|
100577
|
+
lensCollected = true;
|
|
100578
|
+
lensReportCount = signals.length;
|
|
100579
|
+
for (const sig of signals.slice(0, 10)) {
|
|
100580
|
+
const frame = sig.topFrame;
|
|
100581
|
+
const sigKey = [sig.comment ?? "", sig.topErrorMessage ?? "", frame?.function ?? "", frame?.filename ?? ""].filter(Boolean).join(" ");
|
|
100582
|
+
const frameFileBase = (frame?.filename ?? "").split("/").pop()?.toLowerCase() ?? "";
|
|
100583
|
+
const frameMatchesSeed = seedFileBase.length > 0 && frameFileBase.length > 0 && (frameFileBase === seedFileBase || frameFileBase.includes(seedFileBase) || seedFileBase.includes(frameFileBase));
|
|
100584
|
+
const { relevanceClass: classified, relevanceReason } = classifyLogRelevance(
|
|
100585
|
+
sigKey,
|
|
100586
|
+
frame?.filename ? [frame.filename] : [],
|
|
100587
|
+
lensTerms,
|
|
100588
|
+
input.service
|
|
100589
|
+
);
|
|
100590
|
+
const relevanceClass = frameMatchesSeed ? "direct" : classified;
|
|
100591
|
+
const reason = frameMatchesSeed ? `Lens report frame at seed file ${frame?.filename ?? ""}` : relevanceReason;
|
|
100592
|
+
const commentText = sig.comment ? redactSecrets(sig.comment).slice(0, 80) : "(no comment)";
|
|
100593
|
+
const where = sig.route ?? sig.url ?? "";
|
|
100594
|
+
const whereStr = where !== "" ? ` \xB7 ${where}` : "";
|
|
100595
|
+
const frameLoc = frame?.filename ? ` @ ${frame.filename}${frame.lineno !== void 0 ? `:${frame.lineno}` : ""}` : "";
|
|
100596
|
+
const tag = relevanceClass === "ambient" ? " [ambient]" : "";
|
|
100597
|
+
const title = `Lens report: "${commentText}" \xB7 ${sig.errorCount} error(s)${whereStr} (${shortTs(sig.createdAt)})${frameLoc}${tag}`.slice(
|
|
100598
|
+
0,
|
|
100599
|
+
220
|
|
100600
|
+
);
|
|
100601
|
+
const links = {};
|
|
100602
|
+
if (frame?.filename !== void 0) links.file = frame.filename;
|
|
100603
|
+
if (frame?.lineno !== void 0) links.line = frame.lineno;
|
|
100604
|
+
const ev = mkEv(
|
|
100605
|
+
"log",
|
|
100606
|
+
title,
|
|
100607
|
+
{
|
|
100608
|
+
source: "lens",
|
|
100609
|
+
reportId: sig.id,
|
|
100610
|
+
comment: sig.comment ? redactSecrets(sig.comment) : null,
|
|
100611
|
+
route: sig.route ?? null,
|
|
100612
|
+
url: sig.url ?? null,
|
|
100613
|
+
release: sig.release ?? null,
|
|
100614
|
+
gitSha: sig.gitSha ?? null,
|
|
100615
|
+
trigger: sig.trigger ?? null,
|
|
100616
|
+
errorCount: sig.errorCount,
|
|
100617
|
+
topError: sig.topErrorMessage ? redactSecrets(sig.topErrorMessage) : null,
|
|
100618
|
+
// Direct code seed — same fields the engine reads off a code symbol.
|
|
100619
|
+
filePath: frame?.filename ?? null,
|
|
100620
|
+
symbolName: frame?.function ?? null,
|
|
100621
|
+
lineStart: frame?.lineno ?? null,
|
|
100622
|
+
failingRequests: sig.failingRequests,
|
|
100623
|
+
reactComponents: sig.reactComponents ?? null,
|
|
100624
|
+
featureFlags: sig.featureFlags ?? null,
|
|
100625
|
+
relevanceClass,
|
|
100626
|
+
relevanceReason: reason
|
|
100627
|
+
},
|
|
100628
|
+
links,
|
|
100629
|
+
sig.submittedAt ?? sig.createdAt ?? void 0,
|
|
100630
|
+
// Direct: full report weight, boosted when the raise-site frame is resolved.
|
|
100631
|
+
// Ambient: demoted so unrelated reports don't inflate confidence.
|
|
100632
|
+
relevanceClass === "direct" ? frame?.filename ? 0.95 : 0.9 : 0.45
|
|
100633
|
+
);
|
|
100634
|
+
logEvIds.push(ev.id);
|
|
100635
|
+
if (relevanceClass === "direct") {
|
|
100636
|
+
directLogEvIds.push(ev.id);
|
|
100637
|
+
const sigLabel = sig.comment && sig.comment.trim() !== "" ? redactSecrets(sig.comment).slice(0, 120) : sig.topErrorMessage ? redactSecrets(sig.topErrorMessage) : `Lens report ${sig.id}`;
|
|
100638
|
+
directSignatures.push({ key: sigLabel, count: Math.max(1, sig.errorCount), message: sigLabel });
|
|
100639
|
+
} else {
|
|
100640
|
+
ambientLogEvIds.push(ev.id);
|
|
100641
|
+
}
|
|
100642
|
+
}
|
|
100643
|
+
} catch (lensErr) {
|
|
100644
|
+
lensCollected = false;
|
|
100645
|
+
lensFailureReason = connectorFailureReason(lensErr);
|
|
100646
|
+
}
|
|
100647
|
+
}
|
|
99898
100648
|
let axiomCollected = false;
|
|
99899
100649
|
let axiomFailureReason;
|
|
99900
100650
|
if (deps.axiom) {
|
|
@@ -100920,6 +101670,7 @@ async function investigate(input, deps) {
|
|
|
100920
101670
|
["mongodb", deps.connectors.mongodb, deps.mongo],
|
|
100921
101671
|
["postgres", deps.connectors.postgres, deps.postgres],
|
|
100922
101672
|
["sentry", deps.connectors.sentry, deps.sentry],
|
|
101673
|
+
["lens", deps.connectors.lens, deps.lens],
|
|
100923
101674
|
["axiom", deps.connectors.axiom, deps.axiom],
|
|
100924
101675
|
["shopify", deps.connectors.shopify, deps.shopify],
|
|
100925
101676
|
["redis", deps.connectors.redis, deps.redisState]
|
|
@@ -100935,6 +101686,8 @@ async function investigate(input, deps) {
|
|
|
100935
101686
|
// `unavailable` instead of silently flipping to "not configured".
|
|
100936
101687
|
sentryCollected,
|
|
100937
101688
|
sentryFailureReason,
|
|
101689
|
+
lensCollected,
|
|
101690
|
+
lensFailureReason,
|
|
100938
101691
|
axiomCollected,
|
|
100939
101692
|
axiomFailureReason,
|
|
100940
101693
|
shopifyCollected,
|
|
@@ -100957,6 +101710,9 @@ async function investigate(input, deps) {
|
|
|
100957
101710
|
sentry: deps.sentry != null,
|
|
100958
101711
|
sentryCollected,
|
|
100959
101712
|
sentryFailureReason,
|
|
101713
|
+
lens: deps.lens != null,
|
|
101714
|
+
lensCollected,
|
|
101715
|
+
lensFailureReason,
|
|
100960
101716
|
axiom: deps.axiom != null,
|
|
100961
101717
|
axiomCollected,
|
|
100962
101718
|
axiomFailureReason,
|
|
@@ -104291,8 +105047,8 @@ function buildLowerPriority(report, seedLowConfidence, effectiveTopLower) {
|
|
|
104291
105047
|
});
|
|
104292
105048
|
const noEvidence = !c4.implicated && !hasRuntimeEvidence;
|
|
104293
105049
|
if (!noEvidence) continue;
|
|
104294
|
-
const
|
|
104295
|
-
if (
|
|
105050
|
+
const inWindow2 = [...c4.files].some((f5) => changedFiles.has(f5));
|
|
105051
|
+
if (inWindow2) continue;
|
|
104296
105052
|
out2.push({
|
|
104297
105053
|
area: c4.area,
|
|
104298
105054
|
reasons: [
|
|
@@ -119879,6 +120635,9 @@ async function buildInvestigationContext(renv, opts) {
|
|
|
119879
120635
|
const mongo = mongoForEnv(renv);
|
|
119880
120636
|
const postgres2 = postgresForEnv(renv);
|
|
119881
120637
|
const sentry = sentryForEnv(renv);
|
|
120638
|
+
const auth = readAuth();
|
|
120639
|
+
const cloudCfg = readCloudConfig(renv.path);
|
|
120640
|
+
const lens = auth && cloudCfg?.workspace?.id ? lensForCloud({ apiBaseUrl: auth.apiBaseUrl, token: auth.token, workspaceId: cloudCfg.workspace.id }) : null;
|
|
119882
120641
|
const axiom = axiomForEnv(renv);
|
|
119883
120642
|
const shopify = shopifyForEnv(renv);
|
|
119884
120643
|
const queue = queueForEnv(renv);
|
|
@@ -119897,6 +120656,7 @@ async function buildInvestigationContext(renv, opts) {
|
|
|
119897
120656
|
mongo,
|
|
119898
120657
|
postgres: postgres2,
|
|
119899
120658
|
sentry,
|
|
120659
|
+
lens,
|
|
119900
120660
|
axiom,
|
|
119901
120661
|
shopify,
|
|
119902
120662
|
queue,
|
|
@@ -119949,6 +120709,7 @@ async function runOneInvestigation(input, ctx, opts = {}) {
|
|
|
119949
120709
|
mongo: ctx.mongo,
|
|
119950
120710
|
postgres: ctx.postgres,
|
|
119951
120711
|
sentry: ctx.sentry,
|
|
120712
|
+
lens: ctx.lens,
|
|
119952
120713
|
axiom: ctx.axiom,
|
|
119953
120714
|
shopify: ctx.shopify,
|
|
119954
120715
|
queue: ctx.queue,
|
|
@@ -119965,6 +120726,9 @@ async function runOneInvestigation(input, ctx, opts = {}) {
|
|
|
119965
120726
|
mongodb: !!renv.connectors.mongodb,
|
|
119966
120727
|
postgres: !!renv.connectors.postgres,
|
|
119967
120728
|
sentry: !!renv.connectors.sentry,
|
|
120729
|
+
// Lens is NATIVE (no config stanza) — "configured" == provider present, mirroring how
|
|
120730
|
+
// `queue` is derived from `ctx.queue`. Absent when not logged in / repo not cloud-linked.
|
|
120731
|
+
lens: ctx.lens != null,
|
|
119968
120732
|
axiom: !!renv.connectors.axiom,
|
|
119969
120733
|
shopify: !!renv.connectors.shopify,
|
|
119970
120734
|
redis: !!renv.connectors.redis,
|
|
@@ -122314,6 +123078,12 @@ function fromWire(r, repo) {
|
|
|
122314
123078
|
repo,
|
|
122315
123079
|
userId: r.createdByUserId ?? null,
|
|
122316
123080
|
visibility: r.visibility,
|
|
123081
|
+
// This is the write-only private mirror path (dualWrite reads local): a round-tripped record is
|
|
123082
|
+
// the device's OWN authored item, so it carries no team-cache provenance (HOR-464).
|
|
123083
|
+
origin: "local",
|
|
123084
|
+
cloudId: null,
|
|
123085
|
+
authorName: null,
|
|
123086
|
+
pulledAt: null,
|
|
122317
123087
|
payload: null,
|
|
122318
123088
|
// Incident-family recall keys are LOCAL-ONLY context (like vectors): they never enter the
|
|
122319
123089
|
// cloud-sync path, so a cloud-read row carries neither.
|
|
@@ -122375,6 +123145,10 @@ function createCloudMemoryStore(client, cfg) {
|
|
|
122375
123145
|
repo: item.repo ?? repoFallback,
|
|
122376
123146
|
userId: item.userId ?? null,
|
|
122377
123147
|
visibility: input.visibility ?? "private",
|
|
123148
|
+
origin: "local",
|
|
123149
|
+
cloudId: null,
|
|
123150
|
+
authorName: null,
|
|
123151
|
+
pulledAt: null,
|
|
122378
123152
|
payload: null,
|
|
122379
123153
|
// Incident-family recall keys stay LOCAL-ONLY — never synced, so the cloud echo carries neither.
|
|
122380
123154
|
signature: null,
|
|
@@ -122409,6 +123183,10 @@ function createCloudMemoryStore(client, cfg) {
|
|
|
122409
123183
|
repo: repoFallback,
|
|
122410
123184
|
userId: null,
|
|
122411
123185
|
visibility: "private",
|
|
123186
|
+
origin: "local",
|
|
123187
|
+
cloudId: null,
|
|
123188
|
+
authorName: null,
|
|
123189
|
+
pulledAt: null,
|
|
122412
123190
|
payload: null,
|
|
122413
123191
|
signature: null,
|
|
122414
123192
|
tags: null
|
|
@@ -122667,6 +123445,10 @@ function recalledToJSON(r) {
|
|
|
122667
123445
|
effectiveStatus: r.freshness.status,
|
|
122668
123446
|
confidence: r.item.confidence,
|
|
122669
123447
|
visibility: r.item.visibility,
|
|
123448
|
+
// HOR-464: provenance + teammate attribution. `origin='cloud'` marks a pulled team item; the
|
|
123449
|
+
// `authorName` is DISPLAY-ONLY (it is never read into rank/order — the honesty invariant).
|
|
123450
|
+
origin: r.item.origin,
|
|
123451
|
+
authorName: r.item.authorName ?? null,
|
|
122670
123452
|
evidence: r.item.evidence,
|
|
122671
123453
|
freshness: {
|
|
122672
123454
|
label: r.freshness.label,
|
|
@@ -122693,6 +123475,10 @@ function renderStoredItems(items) {
|
|
|
122693
123475
|
lines.push(
|
|
122694
123476
|
` - ${r.item.kind} \xB7 scope: ${r.item.scope} \xB7 ${r.freshness.label}${drift} \xB7 confidence: ${conf}`
|
|
122695
123477
|
);
|
|
123478
|
+
if (r.item.origin === "cloud") {
|
|
123479
|
+
const who = r.item.authorName ? `teammate ${r.item.authorName}` : "a teammate";
|
|
123480
|
+
lines.push(` - ${import_picocolors36.default.dim(`shared by ${who} (team)`)}`);
|
|
123481
|
+
}
|
|
122696
123482
|
}
|
|
122697
123483
|
return lines.join("\n");
|
|
122698
123484
|
}
|
|
@@ -123171,6 +123957,7 @@ async function runMemorySync(opts) {
|
|
|
123171
123957
|
const store = createLocalMemoryStore(db);
|
|
123172
123958
|
items = await store.query({
|
|
123173
123959
|
repo: project,
|
|
123960
|
+
origin: "local",
|
|
123174
123961
|
status: SYNCABLE_STATUSES,
|
|
123175
123962
|
limit: opts.limit ?? 5e3
|
|
123176
123963
|
});
|
|
@@ -123305,6 +124092,215 @@ async function runMemorySync(opts) {
|
|
|
123305
124092
|
}
|
|
123306
124093
|
return failed > 0 ? 1 : 0;
|
|
123307
124094
|
}
|
|
124095
|
+
function toPromoteInput(item) {
|
|
124096
|
+
return {
|
|
124097
|
+
originClientId: item.id,
|
|
124098
|
+
kind: item.kind,
|
|
124099
|
+
claim: item.claim,
|
|
124100
|
+
scope: item.scope,
|
|
124101
|
+
source: item.source,
|
|
124102
|
+
status: item.status,
|
|
124103
|
+
confidence: item.confidence,
|
|
124104
|
+
contentHash: item.lastVerifiedHash ?? null,
|
|
124105
|
+
provenance: {}
|
|
124106
|
+
};
|
|
124107
|
+
}
|
|
124108
|
+
async function runMemoryPromote(id, opts) {
|
|
124109
|
+
const json2 = opts.json === true;
|
|
124110
|
+
const fail = (msg) => {
|
|
124111
|
+
if (json2) console.log(JSON.stringify({ ok: false, error: msg }, null, 2));
|
|
124112
|
+
else console.error(import_picocolors36.default.red(msg));
|
|
124113
|
+
return 1;
|
|
124114
|
+
};
|
|
124115
|
+
const memId = (id ?? "").trim();
|
|
124116
|
+
if (memId === "") return fail("A memory item id is required.");
|
|
124117
|
+
const root = repoRootOrCwd(opts.cwd);
|
|
124118
|
+
const cfg = readCloudConfig(root);
|
|
124119
|
+
if (!isCloudActive(cfg) || !cfg.project) {
|
|
124120
|
+
return fail("This repo isn't linked to a cloud project. Run `horus cloud link` first.");
|
|
124121
|
+
}
|
|
124122
|
+
const session = authedClient();
|
|
124123
|
+
if (!session) return fail("Not logged in. Run `horus login` first.");
|
|
124124
|
+
let config2;
|
|
124125
|
+
try {
|
|
124126
|
+
config2 = await loadConfig(opts.config);
|
|
124127
|
+
} catch (err2) {
|
|
124128
|
+
return fail(err2.message);
|
|
124129
|
+
}
|
|
124130
|
+
const project = resolveProject(config2, opts.repo);
|
|
124131
|
+
if (!project) return 1;
|
|
124132
|
+
const { db, sql: sql2 } = await openDb(config2.database.url);
|
|
124133
|
+
try {
|
|
124134
|
+
const store = createLocalMemoryStore(db);
|
|
124135
|
+
const row = await store.get(memId);
|
|
124136
|
+
if (row === null) return fail(`Memory item not found: ${memId}`);
|
|
124137
|
+
if (row.kind === "confirmed-outcome" || row.source === "confirmed-outcome") {
|
|
124138
|
+
return fail("Refusing to promote a confirmed-outcome item \u2014 it stays private (spec \xA75.2).");
|
|
124139
|
+
}
|
|
124140
|
+
if (row.origin === "cloud") {
|
|
124141
|
+
if (json2) console.log(JSON.stringify({ ok: true, id: memId, alreadyTeam: true }, null, 2));
|
|
124142
|
+
else console.log(import_picocolors36.default.dim(`${memId} is already a team item \u2014 nothing to promote.`));
|
|
124143
|
+
return 0;
|
|
124144
|
+
}
|
|
124145
|
+
const res = await session.client.promoteTeamMemory(cfg.project.id, {
|
|
124146
|
+
items: [toPromoteInput(row)]
|
|
124147
|
+
});
|
|
124148
|
+
const promoted = res.items.find((it2) => it2.originClientId === memId);
|
|
124149
|
+
if (!promoted) {
|
|
124150
|
+
return fail(
|
|
124151
|
+
`The server did not promote ${memId} (skipped ${res.skipped}/${res.total}). Local state unchanged.`
|
|
124152
|
+
);
|
|
124153
|
+
}
|
|
124154
|
+
const updated = await store.markPromoted(
|
|
124155
|
+
memId,
|
|
124156
|
+
{ cloudId: promoted.id, authorName: promoted.authorName ?? null },
|
|
124157
|
+
{ actor: { kind: "user" }, note: `promoted to team ${cfg.project.slug}` }
|
|
124158
|
+
);
|
|
124159
|
+
if (json2) {
|
|
124160
|
+
console.log(
|
|
124161
|
+
JSON.stringify(
|
|
124162
|
+
{
|
|
124163
|
+
ok: true,
|
|
124164
|
+
id: memId,
|
|
124165
|
+
cloudId: promoted.id,
|
|
124166
|
+
visibility: updated.visibility,
|
|
124167
|
+
origin: updated.origin
|
|
124168
|
+
},
|
|
124169
|
+
null,
|
|
124170
|
+
2
|
|
124171
|
+
)
|
|
124172
|
+
);
|
|
124173
|
+
} else {
|
|
124174
|
+
console.log(
|
|
124175
|
+
import_picocolors36.default.green(`Promoted ${memId} to team memory (${cfg.project.slug}) \u2014 now visible to teammates.`)
|
|
124176
|
+
);
|
|
124177
|
+
}
|
|
124178
|
+
return 0;
|
|
124179
|
+
} catch (err2) {
|
|
124180
|
+
return fail(err2.message);
|
|
124181
|
+
} finally {
|
|
124182
|
+
await sql2.end();
|
|
124183
|
+
}
|
|
124184
|
+
}
|
|
124185
|
+
var TEAM_PULL_PAGE_LIMIT = 200;
|
|
124186
|
+
var TEAM_PULL_MAX_PAGES = 1e4;
|
|
124187
|
+
function formatAge3(ms) {
|
|
124188
|
+
const mins = Math.floor(ms / 6e4);
|
|
124189
|
+
if (mins < 1) return "just now";
|
|
124190
|
+
if (mins < 60) return `${mins}m ago`;
|
|
124191
|
+
const hrs = Math.floor(mins / 60);
|
|
124192
|
+
if (hrs < 24) return `${hrs}h ago`;
|
|
124193
|
+
return `${Math.floor(hrs / 24)}d ago`;
|
|
124194
|
+
}
|
|
124195
|
+
async function runMemoryPull(opts) {
|
|
124196
|
+
const json2 = opts.json === true;
|
|
124197
|
+
const fail = (msg) => {
|
|
124198
|
+
if (json2) console.log(JSON.stringify({ ok: false, error: msg }, null, 2));
|
|
124199
|
+
else console.error(import_picocolors36.default.red(msg));
|
|
124200
|
+
return 1;
|
|
124201
|
+
};
|
|
124202
|
+
const root = repoRootOrCwd(opts.cwd);
|
|
124203
|
+
const cfg = readCloudConfig(root);
|
|
124204
|
+
if (!isCloudActive(cfg) || !cfg.project) {
|
|
124205
|
+
return fail("This repo isn't linked to a cloud project. Run `horus cloud link` first.");
|
|
124206
|
+
}
|
|
124207
|
+
const session = authedClient();
|
|
124208
|
+
if (!session) return fail("Not logged in. Run `horus login` first.");
|
|
124209
|
+
let config2;
|
|
124210
|
+
try {
|
|
124211
|
+
config2 = await loadConfig(opts.config);
|
|
124212
|
+
} catch (err2) {
|
|
124213
|
+
return fail(err2.message);
|
|
124214
|
+
}
|
|
124215
|
+
const project = resolveProject(config2, opts.repo);
|
|
124216
|
+
if (!project) return 1;
|
|
124217
|
+
const { db, sql: sql2 } = await openDb(config2.database.url);
|
|
124218
|
+
try {
|
|
124219
|
+
const store = createLocalMemoryStore(db);
|
|
124220
|
+
const vectorIndex = buildVectorIndex(config2, project);
|
|
124221
|
+
const items = [];
|
|
124222
|
+
let since = "0";
|
|
124223
|
+
try {
|
|
124224
|
+
for (let page = 0; page < TEAM_PULL_MAX_PAGES; page++) {
|
|
124225
|
+
const res = await session.client.listTeamMemorySince(cfg.project.id, {
|
|
124226
|
+
since,
|
|
124227
|
+
limit: TEAM_PULL_PAGE_LIMIT,
|
|
124228
|
+
includeDeleted: true
|
|
124229
|
+
});
|
|
124230
|
+
items.push(...res.items);
|
|
124231
|
+
if (!res.hasMore || !res.nextCursor) break;
|
|
124232
|
+
since = res.nextCursor;
|
|
124233
|
+
}
|
|
124234
|
+
} catch (err2) {
|
|
124235
|
+
if (err2 instanceof CloudOfflineError) {
|
|
124236
|
+
const cached2 = await store.query({ repo: project, origin: "cloud" });
|
|
124237
|
+
const newest = cached2.map((c4) => c4.pulledAt ? new Date(c4.pulledAt).getTime() : 0).reduce((a2, b5) => Math.max(a2, b5), 0);
|
|
124238
|
+
const note = newest > 0 ? `stale \u2014 last pulled ${formatAge3(Date.now() - newest)}` : "stale \u2014 never pulled";
|
|
124239
|
+
if (json2) {
|
|
124240
|
+
console.log(
|
|
124241
|
+
JSON.stringify({ ok: true, offline: true, stale: true, cached: cached2.length, note }, null, 2)
|
|
124242
|
+
);
|
|
124243
|
+
} else {
|
|
124244
|
+
console.log(import_picocolors36.default.yellow(`Offline \u2014 serving ${cached2.length} cached team item(s) (${note}).`));
|
|
124245
|
+
}
|
|
124246
|
+
return 0;
|
|
124247
|
+
}
|
|
124248
|
+
throw err2;
|
|
124249
|
+
}
|
|
124250
|
+
const keepIds = [];
|
|
124251
|
+
let upserted = 0;
|
|
124252
|
+
let removed = 0;
|
|
124253
|
+
for (const it2 of items) {
|
|
124254
|
+
if (it2.deletedAt) {
|
|
124255
|
+
await bestEffortRemove(vectorIndex, it2.originClientId);
|
|
124256
|
+
continue;
|
|
124257
|
+
}
|
|
124258
|
+
const cacheRow = {
|
|
124259
|
+
id: it2.originClientId,
|
|
124260
|
+
// stable cross-device id (the promoting device's local id)
|
|
124261
|
+
kind: it2.kind,
|
|
124262
|
+
claim: it2.claim,
|
|
124263
|
+
scope: it2.scope,
|
|
124264
|
+
source: it2.source,
|
|
124265
|
+
status: it2.status ?? "fresh",
|
|
124266
|
+
// A null server confidence stores as 0 — honest (unknown confidence ranks low, never inflated).
|
|
124267
|
+
confidence: it2.confidence ?? 0,
|
|
124268
|
+
evidence: [],
|
|
124269
|
+
repo: project,
|
|
124270
|
+
visibility: "team",
|
|
124271
|
+
cloudId: it2.id,
|
|
124272
|
+
authorName: it2.authorName
|
|
124273
|
+
};
|
|
124274
|
+
await store.upsertCached(cacheRow, { actor: { kind: "system" }, note: "memory pull" });
|
|
124275
|
+
await bestEffortUpsert(vectorIndex, {
|
|
124276
|
+
memoryId: it2.originClientId,
|
|
124277
|
+
claim: it2.claim,
|
|
124278
|
+
repo: project,
|
|
124279
|
+
scope: it2.scope
|
|
124280
|
+
});
|
|
124281
|
+
keepIds.push(it2.originClientId);
|
|
124282
|
+
upserted += 1;
|
|
124283
|
+
}
|
|
124284
|
+
removed = await store.reconcileCache(project, keepIds);
|
|
124285
|
+
if (json2) {
|
|
124286
|
+
console.log(
|
|
124287
|
+
JSON.stringify({ ok: true, project, pulled: upserted, removed, total: items.length }, null, 2)
|
|
124288
|
+
);
|
|
124289
|
+
} else {
|
|
124290
|
+
console.log(
|
|
124291
|
+
import_picocolors36.default.green(
|
|
124292
|
+
`Pulled ${upserted} team memory item(s)` + (removed ? `, dropped ${removed} stale` : "") + "."
|
|
124293
|
+
)
|
|
124294
|
+
);
|
|
124295
|
+
console.log(import_picocolors36.default.dim("Team items are read-only locally \u2014 re-promote to change one. Vectors stay local."));
|
|
124296
|
+
}
|
|
124297
|
+
return 0;
|
|
124298
|
+
} catch (err2) {
|
|
124299
|
+
return fail(err2.message);
|
|
124300
|
+
} finally {
|
|
124301
|
+
await sql2.end();
|
|
124302
|
+
}
|
|
124303
|
+
}
|
|
123308
124304
|
function outcomeLabelToJSON(l2) {
|
|
123309
124305
|
return {
|
|
123310
124306
|
id: l2.id,
|
|
@@ -129302,6 +130298,12 @@ Examples:
|
|
|
129302
130298
|
process.exitCode = await runMemorySync(opts);
|
|
129303
130299
|
}
|
|
129304
130300
|
);
|
|
130301
|
+
memory.command("promote <id>").description("Promote a local memory item into the shared team memory (one-way; refuses confirmed-outcome)").option("-c, --config <path>", "path to horus.config.ts").option("--repo <name>", "repository/project WITHIN the loaded config (default: inferred from cwd) \u2014 cross-repo targeting is --config or cd").option("--json", "output JSON").action(async (id, opts) => {
|
|
130302
|
+
process.exitCode = await runMemoryPromote(id, opts);
|
|
130303
|
+
});
|
|
130304
|
+
memory.command("pull").description("Refresh the local cache of shared team memory (full pull; offline serves stale cache)").option("-c, --config <path>", "path to horus.config.ts").option("--repo <name>", "repository/project WITHIN the loaded config (default: inferred from cwd) \u2014 cross-repo targeting is --config or cd").option("--force", "force a full refresh even if the cache looks current").option("--json", "output JSON").action(async (opts) => {
|
|
130305
|
+
process.exitCode = await runMemoryPull(opts);
|
|
130306
|
+
});
|
|
129305
130307
|
const evalCmd = program2.command("eval").description("Read-only accuracy harness over Horus's outcome-label eval set (never writes labels)");
|
|
129306
130308
|
evalCmd.command("build").description("Emit a byte-stable corpus-<version>.jsonl + manifest from the eval set (read-only)").option("-c, --config <path>", "path to horus.config.ts").option("--repo <name>", "repository/project WITHIN the loaded config (default: inferred from cwd) \u2014 cross-repo targeting is --config or cd").option("--source <source>", "filter to one signal source: feedback | confirm").option("--days <n>", "only include labels from the last N days", (v4) => Number(v4)).option("--limit <n>", "max labels to scan", (v4) => Number(v4)).option("--out <dir>", "output directory for the corpus + manifest (default: ./.horus/eval)").option("--version <v>", "corpus schema version tag (default: v1)").option("--json", "output JSON").action(
|
|
129307
130309
|
async (opts) => {
|