@useorgx/wizard 0.1.38 → 0.1.40
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/cli.js +2073 -80
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
// src/cli.ts
|
|
4
4
|
import * as clack from "@clack/prompts";
|
|
5
5
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
6
|
-
import { readFileSync as
|
|
6
|
+
import { readFileSync as readFileSync7 } from "fs";
|
|
7
7
|
import { hostname } from "os";
|
|
8
|
-
import { resolve } from "path";
|
|
8
|
+
import { resolve as resolve2 } from "path";
|
|
9
9
|
import { Command } from "commander";
|
|
10
10
|
import pc3 from "picocolors";
|
|
11
11
|
|
|
@@ -795,7 +795,7 @@ function parsePairingPollResult(value) {
|
|
|
795
795
|
};
|
|
796
796
|
}
|
|
797
797
|
function sleep(ms) {
|
|
798
|
-
return new Promise((
|
|
798
|
+
return new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
799
799
|
}
|
|
800
800
|
async function startBrowserPairing(options, fetchImpl) {
|
|
801
801
|
const data = await fetchJson({
|
|
@@ -908,12 +908,12 @@ h1{font-size:1.5rem;color:#b91c1c}p{color:#555;margin-top:.5rem}</style>
|
|
|
908
908
|
<p>Return to your terminal and try again.</p>
|
|
909
909
|
</div></body></html>`;
|
|
910
910
|
function tryListen(port, hostname2) {
|
|
911
|
-
return new Promise((
|
|
911
|
+
return new Promise((resolve3, reject) => {
|
|
912
912
|
const server = createServer();
|
|
913
913
|
server.once("error", reject);
|
|
914
914
|
server.listen(port, hostname2, () => {
|
|
915
915
|
server.removeListener("error", reject);
|
|
916
|
-
|
|
916
|
+
resolve3(server);
|
|
917
917
|
});
|
|
918
918
|
});
|
|
919
919
|
}
|
|
@@ -942,7 +942,7 @@ async function startLocalAuthServer(options) {
|
|
|
942
942
|
const successHtml = options.successHtml ?? DEFAULT_SUCCESS_HTML;
|
|
943
943
|
const errorHtml = options.errorHtml ?? DEFAULT_ERROR_HTML;
|
|
944
944
|
const { server, port } = await bindServer(options.preferredPort, hostname2);
|
|
945
|
-
const result = new Promise((
|
|
945
|
+
const result = new Promise((resolve3, reject) => {
|
|
946
946
|
const timer = setTimeout(() => {
|
|
947
947
|
server.close();
|
|
948
948
|
reject(new Error("Timed out waiting for browser authorization."));
|
|
@@ -985,7 +985,7 @@ async function startLocalAuthServer(options) {
|
|
|
985
985
|
res.writeHead(200, { "Content-Type": "text/html" }).end(successHtml);
|
|
986
986
|
clearTimeout(timer);
|
|
987
987
|
server.close();
|
|
988
|
-
|
|
988
|
+
resolve3({ code, state });
|
|
989
989
|
});
|
|
990
990
|
});
|
|
991
991
|
return { port, result };
|
|
@@ -3215,8 +3215,8 @@ function encodeRepoPath2(value) {
|
|
|
3215
3215
|
return value.split("/").filter((segment) => segment.length > 0).map((segment) => encodeURIComponent(segment)).join("/");
|
|
3216
3216
|
}
|
|
3217
3217
|
function isLikelyRepoFilePath(path) {
|
|
3218
|
-
const
|
|
3219
|
-
return
|
|
3218
|
+
const basename4 = path.split("/").pop() ?? path;
|
|
3219
|
+
return basename4.includes(".") && !/^\.[^./]+$/.test(basename4);
|
|
3220
3220
|
}
|
|
3221
3221
|
function buildContentsUrl2(spec, path) {
|
|
3222
3222
|
const encodedPath = encodeRepoPath2(path);
|
|
@@ -3550,7 +3550,7 @@ function formatCommandFailure(command, args, result) {
|
|
|
3550
3550
|
return `${command} ${args.join(" ")} failed${result.exitCode >= 0 ? ` with exit code ${result.exitCode}` : ""}${detail ? `: ${detail}` : "."}`;
|
|
3551
3551
|
}
|
|
3552
3552
|
async function defaultCommandRunner(command, args) {
|
|
3553
|
-
return await new Promise((
|
|
3553
|
+
return await new Promise((resolve3) => {
|
|
3554
3554
|
const child = spawn(command, [...args], {
|
|
3555
3555
|
env: process.env,
|
|
3556
3556
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -3565,7 +3565,7 @@ async function defaultCommandRunner(command, args) {
|
|
|
3565
3565
|
});
|
|
3566
3566
|
child.on("error", (error) => {
|
|
3567
3567
|
const errorCode = typeof error === "object" && error && "code" in error ? String(error.code) : void 0;
|
|
3568
|
-
|
|
3568
|
+
resolve3({
|
|
3569
3569
|
exitCode: -1,
|
|
3570
3570
|
stdout,
|
|
3571
3571
|
stderr,
|
|
@@ -3573,7 +3573,7 @@ async function defaultCommandRunner(command, args) {
|
|
|
3573
3573
|
});
|
|
3574
3574
|
});
|
|
3575
3575
|
child.on("close", (code) => {
|
|
3576
|
-
|
|
3576
|
+
resolve3({
|
|
3577
3577
|
exitCode: code ?? -1,
|
|
3578
3578
|
stdout,
|
|
3579
3579
|
stderr
|
|
@@ -6500,8 +6500,634 @@ function loadAiSessionImports(options) {
|
|
|
6500
6500
|
};
|
|
6501
6501
|
}
|
|
6502
6502
|
|
|
6503
|
-
// src/lib/
|
|
6503
|
+
// src/lib/work-graph-source-adapters.ts
|
|
6504
6504
|
import { createHash as createHash3 } from "crypto";
|
|
6505
|
+
import { existsSync as existsSync6, readdirSync as readdirSync4, readFileSync as readFileSync4, statSync as statSync4 } from "fs";
|
|
6506
|
+
import { homedir as homedir2 } from "os";
|
|
6507
|
+
import { basename as basename3, join as join5, resolve } from "path";
|
|
6508
|
+
var CLIENT_SOURCES = ["claude_code", "codex", "opencode", "goose", "cursor"];
|
|
6509
|
+
var DEFAULT_LIMIT_PER_SOURCE2 = 8;
|
|
6510
|
+
var DEFAULT_SINCE_DAYS2 = 45;
|
|
6511
|
+
var DEFAULT_MAX_BYTES_PER_FILE2 = 2e6;
|
|
6512
|
+
var WORK_SIGNAL_PATTERN = /\b(decision|decided|tradeoff|artifact|implemented|created|changed|edited|shipped|verified|test|proof|commit|blocked|blocker|failed|fails|error|timeout|permission|owner|handoff|mcp__|orgx_|tool call|schema|zod|next action|follow[- ]?up|initiative|agent|skill|recipe|workflow|customer|business|launch|deploy)\b/i;
|
|
6513
|
+
function parseInvestigationSourceList(value) {
|
|
6514
|
+
if (!value?.trim() || value.trim().toLowerCase() === "manual") return [];
|
|
6515
|
+
const requested = value.split(",").map((item) => item.trim().toLowerCase().replace(/-/g, "_")).filter(Boolean);
|
|
6516
|
+
const expanded = requested.includes("all") ? [...CLIENT_SOURCES] : requested.map((source) => source === "claude" ? "claude_code" : source);
|
|
6517
|
+
const deduped = [...new Set(expanded)];
|
|
6518
|
+
const invalid = deduped.filter((source) => !CLIENT_SOURCES.includes(source));
|
|
6519
|
+
if (invalid.length > 0) {
|
|
6520
|
+
throw new Error(
|
|
6521
|
+
`Unsupported investigation source: ${invalid.join(", ")}. Use claude, codex, opencode, goose, cursor, or all.`
|
|
6522
|
+
);
|
|
6523
|
+
}
|
|
6524
|
+
return deduped;
|
|
6525
|
+
}
|
|
6526
|
+
function hash(value, length = 24) {
|
|
6527
|
+
return createHash3("sha256").update(JSON.stringify(value)).digest("hex").slice(0, length);
|
|
6528
|
+
}
|
|
6529
|
+
function expandPath(path, env) {
|
|
6530
|
+
return path.replace(/^~(?=\/|$)/, env.home).replace(/\$CWD/g, env.cwd).replace(/\$HOME/g, env.home);
|
|
6531
|
+
}
|
|
6532
|
+
function safeStat(path) {
|
|
6533
|
+
try {
|
|
6534
|
+
return statSync4(path);
|
|
6535
|
+
} catch {
|
|
6536
|
+
return null;
|
|
6537
|
+
}
|
|
6538
|
+
}
|
|
6539
|
+
function walkFiles(root, predicate, maxFiles = 500) {
|
|
6540
|
+
if (!existsSync6(root)) return [];
|
|
6541
|
+
const files = [];
|
|
6542
|
+
const stack = [root];
|
|
6543
|
+
const ignored = /* @__PURE__ */ new Set(["node_modules", ".git", ".next", "dist", "build", ".turbo"]);
|
|
6544
|
+
while (stack.length > 0 && files.length < maxFiles) {
|
|
6545
|
+
const current = stack.pop();
|
|
6546
|
+
if (!current) continue;
|
|
6547
|
+
let entries;
|
|
6548
|
+
try {
|
|
6549
|
+
entries = readdirSync4(current);
|
|
6550
|
+
} catch {
|
|
6551
|
+
continue;
|
|
6552
|
+
}
|
|
6553
|
+
for (const entry of entries) {
|
|
6554
|
+
if (ignored.has(entry)) continue;
|
|
6555
|
+
const path = join5(current, entry);
|
|
6556
|
+
const stats = safeStat(path);
|
|
6557
|
+
if (!stats) continue;
|
|
6558
|
+
if (stats.isDirectory()) {
|
|
6559
|
+
stack.push(path);
|
|
6560
|
+
} else if (stats.isFile() && predicate(path)) {
|
|
6561
|
+
files.push(path);
|
|
6562
|
+
}
|
|
6563
|
+
}
|
|
6564
|
+
}
|
|
6565
|
+
return files;
|
|
6566
|
+
}
|
|
6567
|
+
function providerForClient(client) {
|
|
6568
|
+
if (client === "claude_code") return "anthropic";
|
|
6569
|
+
if (client === "codex") return "openai";
|
|
6570
|
+
return "unknown";
|
|
6571
|
+
}
|
|
6572
|
+
function workGraphSourceClient(client) {
|
|
6573
|
+
if (client === "claude_code") return "claude-code";
|
|
6574
|
+
return client;
|
|
6575
|
+
}
|
|
6576
|
+
function clientLabel(client) {
|
|
6577
|
+
switch (client) {
|
|
6578
|
+
case "claude_code":
|
|
6579
|
+
return "Claude Code sessions";
|
|
6580
|
+
case "codex":
|
|
6581
|
+
return "Codex sessions";
|
|
6582
|
+
case "opencode":
|
|
6583
|
+
return "OpenCode sessions";
|
|
6584
|
+
case "goose":
|
|
6585
|
+
return "goose sessions";
|
|
6586
|
+
case "cursor":
|
|
6587
|
+
return "Cursor workspace evidence";
|
|
6588
|
+
}
|
|
6589
|
+
}
|
|
6590
|
+
function redactedText(value, limit = 900) {
|
|
6591
|
+
return value.replace(/\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/gi, "[redacted-email]").replace(/\b(?:sk|oxk|ghp|gho|xox[baprs])_[A-Za-z0-9_-]{12,}\b/g, "[redacted-token]").replace(/\s+/g, " ").trim().slice(0, limit);
|
|
6592
|
+
}
|
|
6593
|
+
function nowWindow(timestamp, now) {
|
|
6594
|
+
const occurred = Date.parse(timestamp);
|
|
6595
|
+
if (!Number.isFinite(occurred)) return "older";
|
|
6596
|
+
const delta = now.getTime() - occurred;
|
|
6597
|
+
if (delta < 5 * 60 * 1e3) return "live";
|
|
6598
|
+
if (delta <= 24 * 60 * 60 * 1e3) return "recent_24h";
|
|
6599
|
+
if (delta <= 7 * 24 * 60 * 60 * 1e3) return "recent_7d";
|
|
6600
|
+
if (delta <= 30 * 24 * 60 * 60 * 1e3) return "recent_30d";
|
|
6601
|
+
return "older";
|
|
6602
|
+
}
|
|
6603
|
+
function makeRawEvent(input) {
|
|
6604
|
+
const contentHash = hash(input.payload, 64);
|
|
6605
|
+
const uri = `${input.client}:${input.sessionId}:${input.uriSuffix}`;
|
|
6606
|
+
return {
|
|
6607
|
+
event_id: `evt_${hash([uri, contentHash], 24)}`,
|
|
6608
|
+
source_ref: {
|
|
6609
|
+
source_id: input.client,
|
|
6610
|
+
uri,
|
|
6611
|
+
cite_url: null,
|
|
6612
|
+
resolves: true
|
|
6613
|
+
},
|
|
6614
|
+
source_id: input.client,
|
|
6615
|
+
provider: providerForClient(input.client),
|
|
6616
|
+
session_id: input.sessionId,
|
|
6617
|
+
project_id: null,
|
|
6618
|
+
timestamp: input.timestamp,
|
|
6619
|
+
occurred_in_window: nowWindow(input.timestamp, input.now),
|
|
6620
|
+
kind: input.kind,
|
|
6621
|
+
role: input.role,
|
|
6622
|
+
payload: input.payload,
|
|
6623
|
+
raw_byte_offset: input.rawByteOffset ?? null,
|
|
6624
|
+
raw_row_id: input.rawRowId ?? null,
|
|
6625
|
+
content_hash: contentHash,
|
|
6626
|
+
redaction_applied: true
|
|
6627
|
+
};
|
|
6628
|
+
}
|
|
6629
|
+
function parseJsonLine2(line) {
|
|
6630
|
+
try {
|
|
6631
|
+
return JSON.parse(line);
|
|
6632
|
+
} catch {
|
|
6633
|
+
return null;
|
|
6634
|
+
}
|
|
6635
|
+
}
|
|
6636
|
+
function asText2(value) {
|
|
6637
|
+
if (typeof value === "string") return value;
|
|
6638
|
+
if (Array.isArray(value)) {
|
|
6639
|
+
return value.map((item) => {
|
|
6640
|
+
if (typeof item === "string") return item;
|
|
6641
|
+
if (!isRecord(item)) return "";
|
|
6642
|
+
if (typeof item.text === "string") return item.text;
|
|
6643
|
+
if (typeof item.content === "string") return item.content;
|
|
6644
|
+
return "";
|
|
6645
|
+
}).filter(Boolean).join("\n");
|
|
6646
|
+
}
|
|
6647
|
+
if (isRecord(value)) {
|
|
6648
|
+
if (typeof value.text === "string") return value.text;
|
|
6649
|
+
if (typeof value.content === "string") return value.content;
|
|
6650
|
+
}
|
|
6651
|
+
return "";
|
|
6652
|
+
}
|
|
6653
|
+
function stringField(record, ...keys) {
|
|
6654
|
+
for (const key of keys) {
|
|
6655
|
+
const value = record[key];
|
|
6656
|
+
if (typeof value === "string" && value.trim()) return value.trim();
|
|
6657
|
+
}
|
|
6658
|
+
return null;
|
|
6659
|
+
}
|
|
6660
|
+
function timestampFor(record, statsMtime) {
|
|
6661
|
+
if (isRecord(record)) {
|
|
6662
|
+
const timestamp = stringField(record, "timestamp", "created_at", "createdAt", "created", "time");
|
|
6663
|
+
if (timestamp && Number.isFinite(Date.parse(timestamp))) return new Date(timestamp).toISOString();
|
|
6664
|
+
const numeric = record.timestamp ?? record.created_at ?? record.created;
|
|
6665
|
+
if (typeof numeric === "number" && Number.isFinite(numeric)) {
|
|
6666
|
+
return new Date(numeric > 1e10 ? numeric : numeric * 1e3).toISOString();
|
|
6667
|
+
}
|
|
6668
|
+
}
|
|
6669
|
+
return new Date(statsMtime).toISOString();
|
|
6670
|
+
}
|
|
6671
|
+
function mapCodexRecord(record, fallbackTimestamp) {
|
|
6672
|
+
if (!isRecord(record)) return [];
|
|
6673
|
+
const timestamp = timestampFor(record, Date.parse(fallbackTimestamp));
|
|
6674
|
+
if (record.type === "response_item" && isRecord(record.payload)) {
|
|
6675
|
+
const payload = record.payload;
|
|
6676
|
+
if (payload.type === "message" && (payload.role === "user" || payload.role === "assistant")) {
|
|
6677
|
+
return [{
|
|
6678
|
+
kind: payload.role === "user" ? "user_prompt" : "assistant_text",
|
|
6679
|
+
role: payload.role,
|
|
6680
|
+
text: asText2(payload.content),
|
|
6681
|
+
timestamp
|
|
6682
|
+
}];
|
|
6683
|
+
}
|
|
6684
|
+
if (payload.type === "function_call") {
|
|
6685
|
+
const toolName = stringField(payload, "name", "tool_name") ?? "unknown_tool";
|
|
6686
|
+
return [{
|
|
6687
|
+
kind: toolName.startsWith("mcp__") ? "mcp_tool_call" : "tool_call_start",
|
|
6688
|
+
role: "tool",
|
|
6689
|
+
text: redactedText(JSON.stringify(payload.arguments ?? payload)),
|
|
6690
|
+
toolName,
|
|
6691
|
+
timestamp
|
|
6692
|
+
}];
|
|
6693
|
+
}
|
|
6694
|
+
if (payload.type === "function_call_output") {
|
|
6695
|
+
const text2 = asText2(payload.output) || redactedText(JSON.stringify(payload.output ?? payload));
|
|
6696
|
+
return [{
|
|
6697
|
+
kind: /\berror|failed|exception\b/i.test(text2) ? "tool_call_error" : "tool_call_result",
|
|
6698
|
+
role: "tool",
|
|
6699
|
+
text: text2,
|
|
6700
|
+
timestamp
|
|
6701
|
+
}];
|
|
6702
|
+
}
|
|
6703
|
+
if (payload.type === "reasoning") {
|
|
6704
|
+
return [{ kind: "assistant_reasoning", role: "assistant", text: asText2(payload.content), timestamp }];
|
|
6705
|
+
}
|
|
6706
|
+
}
|
|
6707
|
+
if (record.type === "event_msg" && isRecord(record.payload) && typeof record.payload.message === "string") {
|
|
6708
|
+
return [{ kind: "assistant_text", role: "meta", text: record.payload.message, timestamp }];
|
|
6709
|
+
}
|
|
6710
|
+
return [];
|
|
6711
|
+
}
|
|
6712
|
+
function mapClaudeRecord(record, fallbackTimestamp) {
|
|
6713
|
+
if (!isRecord(record)) return [];
|
|
6714
|
+
const timestamp = timestampFor(record, Date.parse(fallbackTimestamp));
|
|
6715
|
+
const message = isRecord(record.message) ? record.message : record;
|
|
6716
|
+
const role = message.role === "user" || message.role === "assistant" ? message.role : "meta";
|
|
6717
|
+
const content = message.content;
|
|
6718
|
+
const out = [];
|
|
6719
|
+
if (Array.isArray(content)) {
|
|
6720
|
+
for (const [index, part] of content.entries()) {
|
|
6721
|
+
if (!isRecord(part)) continue;
|
|
6722
|
+
if (part.type === "text" || typeof part.text === "string") {
|
|
6723
|
+
out.push({ kind: role === "user" ? "user_prompt" : "assistant_text", role, text: asText2(part), timestamp });
|
|
6724
|
+
} else if (part.type === "thinking") {
|
|
6725
|
+
out.push({ kind: "assistant_reasoning", role: "assistant", text: asText2(part), timestamp });
|
|
6726
|
+
} else if (part.type === "tool_use") {
|
|
6727
|
+
const toolName = stringField(part, "name") ?? `tool_${index}`;
|
|
6728
|
+
out.push({
|
|
6729
|
+
kind: toolName.startsWith("mcp__") ? "mcp_tool_call" : "tool_call_start",
|
|
6730
|
+
role: "tool",
|
|
6731
|
+
text: redactedText(JSON.stringify(part.input ?? part)),
|
|
6732
|
+
toolName,
|
|
6733
|
+
timestamp
|
|
6734
|
+
});
|
|
6735
|
+
} else if (part.type === "tool_result") {
|
|
6736
|
+
const text3 = asText2(part.content) || redactedText(JSON.stringify(part.content ?? part));
|
|
6737
|
+
out.push({ kind: part.is_error === true ? "tool_call_error" : "tool_call_result", role: "tool", text: text3, timestamp });
|
|
6738
|
+
}
|
|
6739
|
+
}
|
|
6740
|
+
return out;
|
|
6741
|
+
}
|
|
6742
|
+
const text2 = asText2(content);
|
|
6743
|
+
if (text2) out.push({ kind: role === "user" ? "user_prompt" : "assistant_text", role, text: text2, timestamp });
|
|
6744
|
+
return out;
|
|
6745
|
+
}
|
|
6746
|
+
function readJsonlCandidate(candidate, options) {
|
|
6747
|
+
const stats = safeStat(candidate.path);
|
|
6748
|
+
if (!stats || stats.size > options.maxBytesPerFile) {
|
|
6749
|
+
return {
|
|
6750
|
+
events: [],
|
|
6751
|
+
filesRead: 0,
|
|
6752
|
+
filesSkipped: [{ path: candidate.path, reason: stats ? "schema_version_unsupported" : "permission_denied" }],
|
|
6753
|
+
notes: [],
|
|
6754
|
+
searchedSessions: 0
|
|
6755
|
+
};
|
|
6756
|
+
}
|
|
6757
|
+
const lines = readFileSync4(candidate.path, "utf8").split(/\r?\n/);
|
|
6758
|
+
const fallbackTimestamp = new Date(stats.mtimeMs).toISOString();
|
|
6759
|
+
const sessionId = basename3(candidate.path).replace(/\.[^.]+$/, "");
|
|
6760
|
+
const events = [];
|
|
6761
|
+
for (const [index, line] of lines.entries()) {
|
|
6762
|
+
if (!line.trim()) continue;
|
|
6763
|
+
const record = parseJsonLine2(line);
|
|
6764
|
+
const mapped = candidate.source === "codex" ? mapCodexRecord(record, fallbackTimestamp) : candidate.source === "claude_code" ? mapClaudeRecord(record, fallbackTimestamp) : mapGenericJsonRecord(record, fallbackTimestamp);
|
|
6765
|
+
for (const [partIndex, item] of mapped.entries()) {
|
|
6766
|
+
if (!item.text && !item.toolName) continue;
|
|
6767
|
+
events.push(makeRawEvent({
|
|
6768
|
+
client: candidate.source,
|
|
6769
|
+
kind: item.kind,
|
|
6770
|
+
now: options.now,
|
|
6771
|
+
path: candidate.path,
|
|
6772
|
+
payload: {
|
|
6773
|
+
extraction_mode: candidate.extractionMode,
|
|
6774
|
+
relative_source_path: candidate.path,
|
|
6775
|
+
text_summary: redactedText(item.text),
|
|
6776
|
+
tool_name: item.toolName
|
|
6777
|
+
},
|
|
6778
|
+
rawByteOffset: index,
|
|
6779
|
+
role: item.role,
|
|
6780
|
+
sessionId,
|
|
6781
|
+
timestamp: item.timestamp,
|
|
6782
|
+
uriSuffix: `line:${index}:part:${partIndex}`
|
|
6783
|
+
}));
|
|
6784
|
+
}
|
|
6785
|
+
}
|
|
6786
|
+
return {
|
|
6787
|
+
events,
|
|
6788
|
+
filesRead: 1,
|
|
6789
|
+
filesSkipped: [],
|
|
6790
|
+
notes: [],
|
|
6791
|
+
searchedSessions: 1
|
|
6792
|
+
};
|
|
6793
|
+
}
|
|
6794
|
+
function mapGenericJsonRecord(record, fallbackTimestamp) {
|
|
6795
|
+
if (!isRecord(record)) return [];
|
|
6796
|
+
const timestamp = timestampFor(record, Date.parse(fallbackTimestamp));
|
|
6797
|
+
const roleValue = record.role === "user" || record.role === "assistant" ? record.role : "meta";
|
|
6798
|
+
const toolName = stringField(record, "tool_name", "toolName", "name");
|
|
6799
|
+
if (toolName) {
|
|
6800
|
+
const text3 = asText2(record.response) || asText2(record.output) || redactedText(JSON.stringify(record));
|
|
6801
|
+
return [{
|
|
6802
|
+
kind: toolName.startsWith("mcp__") ? "mcp_tool_call" : record.success === false ? "tool_call_error" : "tool_call_start",
|
|
6803
|
+
role: "tool",
|
|
6804
|
+
text: text3,
|
|
6805
|
+
toolName,
|
|
6806
|
+
timestamp
|
|
6807
|
+
}];
|
|
6808
|
+
}
|
|
6809
|
+
const text2 = asText2(record.content) || asText2(record.message) || asText2(record.text);
|
|
6810
|
+
if (!text2) return [];
|
|
6811
|
+
return [{
|
|
6812
|
+
kind: roleValue === "user" ? "user_prompt" : "assistant_text",
|
|
6813
|
+
role: roleValue,
|
|
6814
|
+
text: text2,
|
|
6815
|
+
timestamp
|
|
6816
|
+
}];
|
|
6817
|
+
}
|
|
6818
|
+
function readJsonCandidate(candidate, options) {
|
|
6819
|
+
const stats = safeStat(candidate.path);
|
|
6820
|
+
if (!stats || stats.size > options.maxBytesPerFile) {
|
|
6821
|
+
return {
|
|
6822
|
+
events: [],
|
|
6823
|
+
filesRead: 0,
|
|
6824
|
+
filesSkipped: [{ path: candidate.path, reason: stats ? "schema_version_unsupported" : "permission_denied" }],
|
|
6825
|
+
notes: [],
|
|
6826
|
+
searchedSessions: 0
|
|
6827
|
+
};
|
|
6828
|
+
}
|
|
6829
|
+
let parsed;
|
|
6830
|
+
try {
|
|
6831
|
+
parsed = JSON.parse(readFileSync4(candidate.path, "utf8"));
|
|
6832
|
+
} catch {
|
|
6833
|
+
return {
|
|
6834
|
+
events: [],
|
|
6835
|
+
filesRead: 0,
|
|
6836
|
+
filesSkipped: [{ path: candidate.path, reason: "binary_or_corrupt" }],
|
|
6837
|
+
notes: [],
|
|
6838
|
+
searchedSessions: 0
|
|
6839
|
+
};
|
|
6840
|
+
}
|
|
6841
|
+
const records = flattenPotentialMessages(parsed).slice(0, 800);
|
|
6842
|
+
const fallbackTimestamp = new Date(stats.mtimeMs).toISOString();
|
|
6843
|
+
const sessionId = basename3(candidate.path).replace(/\.[^.]+$/, "");
|
|
6844
|
+
const events = records.flatMap(
|
|
6845
|
+
(record, index) => mapGenericJsonRecord(record, fallbackTimestamp).map(
|
|
6846
|
+
(item, partIndex) => makeRawEvent({
|
|
6847
|
+
client: candidate.source,
|
|
6848
|
+
kind: item.kind,
|
|
6849
|
+
now: options.now,
|
|
6850
|
+
path: candidate.path,
|
|
6851
|
+
payload: {
|
|
6852
|
+
extraction_mode: candidate.extractionMode,
|
|
6853
|
+
relative_source_path: candidate.path,
|
|
6854
|
+
text_summary: redactedText(item.text),
|
|
6855
|
+
tool_name: item.toolName
|
|
6856
|
+
},
|
|
6857
|
+
rawRowId: String(index),
|
|
6858
|
+
role: item.role,
|
|
6859
|
+
sessionId,
|
|
6860
|
+
timestamp: item.timestamp,
|
|
6861
|
+
uriSuffix: `json:${index}:part:${partIndex}`
|
|
6862
|
+
})
|
|
6863
|
+
)
|
|
6864
|
+
);
|
|
6865
|
+
return {
|
|
6866
|
+
events,
|
|
6867
|
+
filesRead: 1,
|
|
6868
|
+
filesSkipped: [],
|
|
6869
|
+
notes: [],
|
|
6870
|
+
searchedSessions: 1
|
|
6871
|
+
};
|
|
6872
|
+
}
|
|
6873
|
+
function flattenPotentialMessages(value) {
|
|
6874
|
+
const out = [];
|
|
6875
|
+
const visit = (item, depth) => {
|
|
6876
|
+
if (depth > 8 || out.length >= 1e3) return;
|
|
6877
|
+
if (Array.isArray(item)) {
|
|
6878
|
+
for (const child of item) visit(child, depth + 1);
|
|
6879
|
+
return;
|
|
6880
|
+
}
|
|
6881
|
+
if (!isRecord(item)) return;
|
|
6882
|
+
if (typeof item.role === "string" || typeof item.content === "string" || typeof item.text === "string" || typeof item.tool_name === "string" || typeof item.toolName === "string") {
|
|
6883
|
+
out.push(item);
|
|
6884
|
+
}
|
|
6885
|
+
for (const value2 of Object.values(item)) {
|
|
6886
|
+
if (Array.isArray(value2) || isRecord(value2)) visit(value2, depth + 1);
|
|
6887
|
+
}
|
|
6888
|
+
};
|
|
6889
|
+
visit(value, 0);
|
|
6890
|
+
return out;
|
|
6891
|
+
}
|
|
6892
|
+
function readMarkdownCandidate(candidate, options) {
|
|
6893
|
+
const stats = safeStat(candidate.path);
|
|
6894
|
+
if (!stats || stats.size > options.maxBytesPerFile) {
|
|
6895
|
+
return {
|
|
6896
|
+
events: [],
|
|
6897
|
+
filesRead: 0,
|
|
6898
|
+
filesSkipped: [{ path: candidate.path, reason: stats ? "schema_version_unsupported" : "permission_denied" }],
|
|
6899
|
+
notes: [],
|
|
6900
|
+
searchedSessions: 0
|
|
6901
|
+
};
|
|
6902
|
+
}
|
|
6903
|
+
const text2 = readFileSync4(candidate.path, "utf8");
|
|
6904
|
+
const sessionId = basename3(candidate.path).replace(/\.[^.]+$/, "");
|
|
6905
|
+
const timestamp = new Date(stats.mtimeMs).toISOString();
|
|
6906
|
+
const event = makeRawEvent({
|
|
6907
|
+
client: candidate.source,
|
|
6908
|
+
kind: candidate.path.includes(".cursor/rules") || candidate.path.endsWith(".cursorrules") ? "session_start" : "assistant_text",
|
|
6909
|
+
now: options.now,
|
|
6910
|
+
path: candidate.path,
|
|
6911
|
+
payload: {
|
|
6912
|
+
extraction_mode: candidate.extractionMode,
|
|
6913
|
+
relative_source_path: candidate.path,
|
|
6914
|
+
text_summary: redactedText(text2, 1200),
|
|
6915
|
+
tool_usage_state: candidate.path.includes("mcp.json") ? "configured" : void 0
|
|
6916
|
+
},
|
|
6917
|
+
role: "meta",
|
|
6918
|
+
sessionId,
|
|
6919
|
+
timestamp,
|
|
6920
|
+
uriSuffix: "markdown:0"
|
|
6921
|
+
});
|
|
6922
|
+
return {
|
|
6923
|
+
events: [event],
|
|
6924
|
+
filesRead: 1,
|
|
6925
|
+
filesSkipped: [],
|
|
6926
|
+
notes: candidate.extractionMode === "specstory" ? ["SpecStory evidence is repo-grounded and version-stable."] : [],
|
|
6927
|
+
searchedSessions: 1
|
|
6928
|
+
};
|
|
6929
|
+
}
|
|
6930
|
+
function discoverCandidates(source, env, sinceMs) {
|
|
6931
|
+
const candidates = [];
|
|
6932
|
+
const addFiles = (paths, extractionMode, predicate) => {
|
|
6933
|
+
for (const rootPattern of paths) {
|
|
6934
|
+
const expanded = expandPath(rootPattern, env);
|
|
6935
|
+
const root = expanded.includes("*") ? expanded.slice(0, expanded.indexOf("*")).replace(/\/$/, "") : expanded;
|
|
6936
|
+
const exactStats = safeStat(expanded);
|
|
6937
|
+
const found = exactStats?.isFile() ? [expanded] : walkFiles(root, predicate);
|
|
6938
|
+
for (const path of found) {
|
|
6939
|
+
const stats = safeStat(path);
|
|
6940
|
+
if (!stats || stats.mtimeMs < sinceMs) continue;
|
|
6941
|
+
candidates.push({ extractionMode, mtimeMs: stats.mtimeMs, path, source });
|
|
6942
|
+
}
|
|
6943
|
+
}
|
|
6944
|
+
};
|
|
6945
|
+
if (source === "codex") {
|
|
6946
|
+
addFiles([CODEX_SESSIONS_DIR], "jsonl", (path) => path.endsWith(".jsonl"));
|
|
6947
|
+
} else if (source === "claude_code") {
|
|
6948
|
+
addFiles([CLAUDE_PROJECTS_DIR], "jsonl", (path) => path.endsWith(".jsonl"));
|
|
6949
|
+
} else if (source === "opencode") {
|
|
6950
|
+
addFiles([
|
|
6951
|
+
"~/.local/share/opencode/storage/session",
|
|
6952
|
+
"~/Library/Application Support/opencode/storage/session"
|
|
6953
|
+
], "json", (path) => path.endsWith(".json"));
|
|
6954
|
+
addFiles([
|
|
6955
|
+
"~/.local/share/opencode/storage/db.sqlite",
|
|
6956
|
+
"~/Library/Application Support/opencode/storage/db.sqlite"
|
|
6957
|
+
], "sqlite", (path) => path.endsWith(".sqlite") || path.endsWith(".db"));
|
|
6958
|
+
} else if (source === "goose") {
|
|
6959
|
+
addFiles(["~/.local/share/goose/sessions"], "jsonl", (path) => path.endsWith(".jsonl"));
|
|
6960
|
+
addFiles([
|
|
6961
|
+
"~/.local/share/goose/logs",
|
|
6962
|
+
"~/.local/share/goose/data/sessions/sessions.db"
|
|
6963
|
+
], "sqlite", (path) => path.endsWith(".db") || /llm_request\.\d+\.jsonl$/.test(path));
|
|
6964
|
+
} else if (source === "cursor") {
|
|
6965
|
+
addFiles(
|
|
6966
|
+
[
|
|
6967
|
+
"$CWD/.specstory/history",
|
|
6968
|
+
"$CWD/.cursor/rules",
|
|
6969
|
+
"$CWD/.cursorrules",
|
|
6970
|
+
"$CWD/.cursor/mcp.json"
|
|
6971
|
+
],
|
|
6972
|
+
"specstory",
|
|
6973
|
+
(path) => path.endsWith(".md") || path.endsWith(".mdc") || path.endsWith(".json") || path.endsWith(".cursorrules")
|
|
6974
|
+
);
|
|
6975
|
+
addFiles([
|
|
6976
|
+
"~/Library/Application Support/Cursor/User/workspaceStorage",
|
|
6977
|
+
"~/.config/Cursor/User/workspaceStorage"
|
|
6978
|
+
], "sqlite", (path) => path.endsWith("state.vscdb"));
|
|
6979
|
+
}
|
|
6980
|
+
return candidates.sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
6981
|
+
}
|
|
6982
|
+
function discoverOverrideCandidates(source, root, sinceMs) {
|
|
6983
|
+
const extractionMode = root.endsWith(".json") ? "json" : "jsonl";
|
|
6984
|
+
const files = walkFiles(root, (path) => path.endsWith(".jsonl") || path.endsWith(".json"));
|
|
6985
|
+
const candidates = [];
|
|
6986
|
+
for (const path of files) {
|
|
6987
|
+
const stats = safeStat(path);
|
|
6988
|
+
if (!stats || stats.mtimeMs < sinceMs) continue;
|
|
6989
|
+
candidates.push({ extractionMode, mtimeMs: stats.mtimeMs, path, source });
|
|
6990
|
+
}
|
|
6991
|
+
return candidates.sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
6992
|
+
}
|
|
6993
|
+
function findingTypeFromEvent(event) {
|
|
6994
|
+
const text2 = `${event.payload.text_summary ?? ""}
|
|
6995
|
+
${event.payload.tool_name ?? ""}`;
|
|
6996
|
+
if (!WORK_SIGNAL_PATTERN.test(text2)) return null;
|
|
6997
|
+
if (/\b(decision|decided|tradeoff|architecture|approval)\b/i.test(text2)) return "decision";
|
|
6998
|
+
if (/\b(blocked|blocker|failed|fails|error|timeout|permission|schema|zod|invalid)\b/i.test(text2)) return "blocker";
|
|
6999
|
+
if (/\b(artifact|implemented|created|changed|edited|commit|pr|file|shipped|deploy)\b/i.test(text2)) return "artifact";
|
|
7000
|
+
if (/\b(test|verified|proof|outcome|result)\b/i.test(text2)) return "outcome";
|
|
7001
|
+
if (/\b(customer|business|revenue|deal|buyer)\b/i.test(text2)) return "business";
|
|
7002
|
+
if (/\b(agent|skill|recipe|tool|mcp__|orgx_)\b/i.test(text2)) return "missed_orchestration_opportunity";
|
|
7003
|
+
return "action";
|
|
7004
|
+
}
|
|
7005
|
+
function titleFromEvent(event) {
|
|
7006
|
+
const text2 = redactedText(String(event.payload.text_summary ?? event.payload.tool_name ?? "AI-client work signal"), 240);
|
|
7007
|
+
const first = text2.replace(/^(decision|artifact|blocker|proof|next action|outcome)\s*:\s*/i, "").split(/[.!?]\s/)[0]?.trim();
|
|
7008
|
+
return (first || `${event.source_id} work signal`).slice(0, 110);
|
|
7009
|
+
}
|
|
7010
|
+
function extractionFromEvents(input) {
|
|
7011
|
+
const findings = [];
|
|
7012
|
+
const seen = /* @__PURE__ */ new Set();
|
|
7013
|
+
for (const event of input.events) {
|
|
7014
|
+
const type = findingTypeFromEvent(event);
|
|
7015
|
+
if (!type) continue;
|
|
7016
|
+
const dedupe = `${type}:${titleFromEvent(event).toLowerCase()}`;
|
|
7017
|
+
if (seen.has(dedupe)) continue;
|
|
7018
|
+
seen.add(dedupe);
|
|
7019
|
+
findings.push({
|
|
7020
|
+
type,
|
|
7021
|
+
title: titleFromEvent(event),
|
|
7022
|
+
summary: type === "missed_orchestration_opportunity" && Number(event.payload.tool_name ? 1 : 0) === 0 ? "The client evidence mentions agent/tool workflow but does not prove an invocation event." : redactedText(String(event.payload.text_summary ?? "Client work signal"), 260),
|
|
7023
|
+
source_client: workGraphSourceClient(input.client),
|
|
7024
|
+
source_id: event.session_id,
|
|
7025
|
+
source_label: clientLabel(input.client),
|
|
7026
|
+
evidence_ref: event.source_ref.uri,
|
|
7027
|
+
confidence: event.kind === "mcp_tool_call" || event.kind === "tool_call_start" ? 0.82 : 0.68,
|
|
7028
|
+
occurred_at: event.timestamp,
|
|
7029
|
+
redacted_verbatim: redactedText(String(event.payload.text_summary ?? ""), 360),
|
|
7030
|
+
privacy_state: "redacted",
|
|
7031
|
+
metadata: {
|
|
7032
|
+
event_id: event.event_id,
|
|
7033
|
+
event_kind: event.kind,
|
|
7034
|
+
extraction_mode: event.payload.extraction_mode,
|
|
7035
|
+
provider: event.provider,
|
|
7036
|
+
tool_name: event.payload.tool_name
|
|
7037
|
+
}
|
|
7038
|
+
});
|
|
7039
|
+
if (findings.length >= 50) break;
|
|
7040
|
+
}
|
|
7041
|
+
return {
|
|
7042
|
+
schema_version: "2.0.0.investigation",
|
|
7043
|
+
extraction_id: `${input.client}:investigation:${hash([input.client, input.events.map((event) => event.event_id)], 12)}`,
|
|
7044
|
+
source_client: workGraphSourceClient(input.client),
|
|
7045
|
+
source_label: clientLabel(input.client),
|
|
7046
|
+
searched_sources: [...new Set(input.events.map((event) => String(event.payload.relative_source_path ?? event.session_id)))].slice(0, 30),
|
|
7047
|
+
search_queries: [
|
|
7048
|
+
{
|
|
7049
|
+
id: "full_session_inventory",
|
|
7050
|
+
lens: "Full-session deterministic inventory",
|
|
7051
|
+
query: "Read messages, tool calls, commands, configs, and repo-grounded session artifacts before deriving loop candidates.",
|
|
7052
|
+
result_count: input.events.length
|
|
7053
|
+
}
|
|
7054
|
+
],
|
|
7055
|
+
extraction_quality: {
|
|
7056
|
+
confidence: findings.length > 0 ? 0.74 : input.filesRead > 0 ? 0.32 : 0.18,
|
|
7057
|
+
searched_session_count: Math.max(input.filesRead, new Set(input.events.map((event) => event.session_id)).size),
|
|
7058
|
+
skipped_session_count: input.filesSkipped.length,
|
|
7059
|
+
notes: [
|
|
7060
|
+
...input.notes,
|
|
7061
|
+
`${input.events.length} normalized raw events; ${findings.length} public-safe loop candidates.`,
|
|
7062
|
+
...input.filesSkipped.map((file) => `Skipped ${file.path}: ${file.reason}`)
|
|
7063
|
+
]
|
|
7064
|
+
},
|
|
7065
|
+
findings
|
|
7066
|
+
};
|
|
7067
|
+
}
|
|
7068
|
+
function loadWorkGraphInvestigationSourceData(options) {
|
|
7069
|
+
const now = options.now ?? /* @__PURE__ */ new Date();
|
|
7070
|
+
const env = {
|
|
7071
|
+
cwd: options.cwd ? resolve(options.cwd) : process.cwd(),
|
|
7072
|
+
home: options.home ?? homedir2()
|
|
7073
|
+
};
|
|
7074
|
+
const sinceMs = now.getTime() - (options.sinceDays ?? DEFAULT_SINCE_DAYS2) * 24 * 60 * 60 * 1e3;
|
|
7075
|
+
const limit = Math.max(1, options.limitPerSource ?? DEFAULT_LIMIT_PER_SOURCE2);
|
|
7076
|
+
const maxBytesPerFile = Math.max(1, options.maxBytesPerFile ?? DEFAULT_MAX_BYTES_PER_FILE2);
|
|
7077
|
+
const rawEvents = [];
|
|
7078
|
+
const clientExtractions = [];
|
|
7079
|
+
const connectedSources = [];
|
|
7080
|
+
const missingSources = [];
|
|
7081
|
+
const sourceOverrides = /* @__PURE__ */ new Map();
|
|
7082
|
+
if (options.codexSessionsDir) sourceOverrides.set("codex", resolve(options.codexSessionsDir));
|
|
7083
|
+
if (options.claudeProjectsDir) sourceOverrides.set("claude_code", resolve(options.claudeProjectsDir));
|
|
7084
|
+
for (const source of options.sources) {
|
|
7085
|
+
const overrideRoot = sourceOverrides.get(source);
|
|
7086
|
+
const candidates = (overrideRoot ? discoverOverrideCandidates(source, overrideRoot, sinceMs) : discoverCandidates(source, env, sinceMs)).slice(0, limit);
|
|
7087
|
+
if (candidates.length === 0) {
|
|
7088
|
+
missingSources.push(`${clientLabel(source)} store`);
|
|
7089
|
+
continue;
|
|
7090
|
+
}
|
|
7091
|
+
const sourceEvents = [];
|
|
7092
|
+
const filesSkipped = [];
|
|
7093
|
+
let filesRead = 0;
|
|
7094
|
+
const notes = [];
|
|
7095
|
+
for (const candidate of candidates) {
|
|
7096
|
+
if (candidate.extractionMode === "sqlite") {
|
|
7097
|
+
filesSkipped.push({ path: candidate.path, reason: "schema_version_unsupported" });
|
|
7098
|
+
notes.push(`${candidate.path} detected; SQLite extraction is reserved for the cloud/runtime adapter reader.`);
|
|
7099
|
+
continue;
|
|
7100
|
+
}
|
|
7101
|
+
const result = candidate.path.endsWith(".jsonl") ? readJsonlCandidate(candidate, { maxBytesPerFile, now }) : candidate.path.endsWith(".json") && !candidate.path.endsWith("mcp.json") ? readJsonCandidate(candidate, { maxBytesPerFile, now }) : readMarkdownCandidate(candidate, { maxBytesPerFile, now });
|
|
7102
|
+
sourceEvents.push(...result.events);
|
|
7103
|
+
filesRead += result.filesRead;
|
|
7104
|
+
filesSkipped.push(...result.filesSkipped);
|
|
7105
|
+
notes.push(...result.notes);
|
|
7106
|
+
}
|
|
7107
|
+
rawEvents.push(...sourceEvents);
|
|
7108
|
+
clientExtractions.push(extractionFromEvents({
|
|
7109
|
+
client: source,
|
|
7110
|
+
events: sourceEvents,
|
|
7111
|
+
filesRead,
|
|
7112
|
+
filesSkipped,
|
|
7113
|
+
notes
|
|
7114
|
+
}));
|
|
7115
|
+
if (sourceEvents.length > 0 || filesRead > 0) {
|
|
7116
|
+
connectedSources.push(clientLabel(source));
|
|
7117
|
+
} else {
|
|
7118
|
+
missingSources.push(`${clientLabel(source)} readable events`);
|
|
7119
|
+
}
|
|
7120
|
+
}
|
|
7121
|
+
return {
|
|
7122
|
+
clientExtractions,
|
|
7123
|
+
connectedSources: [...new Set(connectedSources)],
|
|
7124
|
+
missingSources: [...new Set(missingSources)],
|
|
7125
|
+
rawEvents
|
|
7126
|
+
};
|
|
7127
|
+
}
|
|
7128
|
+
|
|
7129
|
+
// src/lib/self-audit.ts
|
|
7130
|
+
import { createHash as createHash4 } from "crypto";
|
|
6505
7131
|
var SELF_AUDIT_SCHEMA_VERSION = "2026-04-27";
|
|
6506
7132
|
var AUDIT_DIMENSIONS = [
|
|
6507
7133
|
"queryability",
|
|
@@ -6668,7 +7294,7 @@ function buildSelfCritique(scores) {
|
|
|
6668
7294
|
});
|
|
6669
7295
|
}
|
|
6670
7296
|
function hashPlanPayload(payload) {
|
|
6671
|
-
return
|
|
7297
|
+
return createHash4("sha256").update(JSON.stringify(payload)).digest("hex");
|
|
6672
7298
|
}
|
|
6673
7299
|
function buildSelfAuditPlan(input) {
|
|
6674
7300
|
if (input.imports.length === 0) {
|
|
@@ -6998,10 +7624,1162 @@ Rollback: ${plan.recommended_follow_up.rollback}`,
|
|
|
6998
7624
|
}
|
|
6999
7625
|
|
|
7000
7626
|
// src/lib/work-graph.ts
|
|
7001
|
-
import { createHash as
|
|
7002
|
-
|
|
7627
|
+
import { createHash as createHash6 } from "crypto";
|
|
7628
|
+
|
|
7629
|
+
// src/lib/work-graph-investigation.ts
|
|
7630
|
+
import { createHash as createHash5 } from "crypto";
|
|
7631
|
+
var WORK_GRAPH_INVESTIGATION_SCHEMA_VERSION = "2.0.0";
|
|
7632
|
+
var WORK_GRAPH_INVESTIGATION_CLIENTS = [
|
|
7633
|
+
"claude_code",
|
|
7634
|
+
"codex",
|
|
7635
|
+
"opencode",
|
|
7636
|
+
"goose",
|
|
7637
|
+
"cursor",
|
|
7638
|
+
"github",
|
|
7639
|
+
"slack",
|
|
7640
|
+
"orgx_runtime_hook"
|
|
7641
|
+
];
|
|
7642
|
+
var BRANDS = {
|
|
7643
|
+
claude_code: {
|
|
7644
|
+
name: "Claude Code",
|
|
7645
|
+
simple_icons: "anthropic",
|
|
7646
|
+
color: "#D97757",
|
|
7647
|
+
logo_kind: "wordmark+symbol"
|
|
7648
|
+
},
|
|
7649
|
+
codex: {
|
|
7650
|
+
name: "Codex",
|
|
7651
|
+
simple_icons: "openai",
|
|
7652
|
+
color: "#10A37F",
|
|
7653
|
+
logo_kind: "wordmark+symbol"
|
|
7654
|
+
},
|
|
7655
|
+
opencode: {
|
|
7656
|
+
name: "OpenCode",
|
|
7657
|
+
simple_icons: null,
|
|
7658
|
+
color: "#1A1A1A",
|
|
7659
|
+
logo_kind: "wordmark",
|
|
7660
|
+
asset_path: "/brands/opencode.svg"
|
|
7661
|
+
},
|
|
7662
|
+
goose: {
|
|
7663
|
+
name: "goose",
|
|
7664
|
+
simple_icons: null,
|
|
7665
|
+
color: "#FCBF49",
|
|
7666
|
+
logo_kind: "wordmark+symbol",
|
|
7667
|
+
asset_path: "/brands/goose.svg"
|
|
7668
|
+
},
|
|
7669
|
+
cursor: {
|
|
7670
|
+
name: "Cursor",
|
|
7671
|
+
simple_icons: null,
|
|
7672
|
+
color: "#FFFFFF",
|
|
7673
|
+
logo_kind: "wordmark+symbol",
|
|
7674
|
+
asset_path: "/brands/cursor.svg"
|
|
7675
|
+
},
|
|
7676
|
+
github: {
|
|
7677
|
+
name: "GitHub",
|
|
7678
|
+
simple_icons: "github",
|
|
7679
|
+
color: "#181717",
|
|
7680
|
+
logo_kind: "symbol"
|
|
7681
|
+
},
|
|
7682
|
+
slack: {
|
|
7683
|
+
name: "Slack",
|
|
7684
|
+
simple_icons: "slack",
|
|
7685
|
+
color: "#4A154B",
|
|
7686
|
+
logo_kind: "wordmark+symbol"
|
|
7687
|
+
},
|
|
7688
|
+
orgx_runtime_hook: {
|
|
7689
|
+
name: "OrgX Runtime",
|
|
7690
|
+
simple_icons: null,
|
|
7691
|
+
color: "#00C9A7",
|
|
7692
|
+
logo_kind: "wordmark+symbol",
|
|
7693
|
+
asset_path: "/brands/orgx.svg"
|
|
7694
|
+
}
|
|
7695
|
+
};
|
|
7696
|
+
var CAPABILITY_CEILINGS = {
|
|
7697
|
+
claude_code: {
|
|
7698
|
+
chronology: "high",
|
|
7699
|
+
ownership: "none",
|
|
7700
|
+
verification: "low",
|
|
7701
|
+
handoff: "medium",
|
|
7702
|
+
customer_context: "none",
|
|
7703
|
+
decision_lineage: "medium"
|
|
7704
|
+
},
|
|
7705
|
+
codex: {
|
|
7706
|
+
chronology: "high",
|
|
7707
|
+
ownership: "none",
|
|
7708
|
+
verification: "low",
|
|
7709
|
+
handoff: "medium",
|
|
7710
|
+
customer_context: "none",
|
|
7711
|
+
decision_lineage: "medium"
|
|
7712
|
+
},
|
|
7713
|
+
opencode: {
|
|
7714
|
+
chronology: "high",
|
|
7715
|
+
ownership: "none",
|
|
7716
|
+
verification: "medium",
|
|
7717
|
+
handoff: "medium",
|
|
7718
|
+
customer_context: "none",
|
|
7719
|
+
decision_lineage: "high"
|
|
7720
|
+
},
|
|
7721
|
+
goose: {
|
|
7722
|
+
chronology: "high",
|
|
7723
|
+
ownership: "low",
|
|
7724
|
+
verification: "medium",
|
|
7725
|
+
handoff: "low",
|
|
7726
|
+
customer_context: "low",
|
|
7727
|
+
decision_lineage: "high"
|
|
7728
|
+
},
|
|
7729
|
+
cursor: {
|
|
7730
|
+
chronology: "medium",
|
|
7731
|
+
ownership: "none",
|
|
7732
|
+
verification: "low",
|
|
7733
|
+
handoff: "low",
|
|
7734
|
+
customer_context: "none",
|
|
7735
|
+
decision_lineage: "medium"
|
|
7736
|
+
},
|
|
7737
|
+
github: {
|
|
7738
|
+
chronology: "high",
|
|
7739
|
+
ownership: "high",
|
|
7740
|
+
verification: "high",
|
|
7741
|
+
handoff: "none",
|
|
7742
|
+
customer_context: "low",
|
|
7743
|
+
decision_lineage: "medium"
|
|
7744
|
+
},
|
|
7745
|
+
slack: {
|
|
7746
|
+
chronology: "medium",
|
|
7747
|
+
ownership: "high",
|
|
7748
|
+
verification: "none",
|
|
7749
|
+
handoff: "high",
|
|
7750
|
+
customer_context: "high",
|
|
7751
|
+
decision_lineage: "low"
|
|
7752
|
+
},
|
|
7753
|
+
orgx_runtime_hook: {
|
|
7754
|
+
chronology: "high",
|
|
7755
|
+
ownership: "medium",
|
|
7756
|
+
verification: "high",
|
|
7757
|
+
handoff: "high",
|
|
7758
|
+
customer_context: "none",
|
|
7759
|
+
decision_lineage: "high"
|
|
7760
|
+
}
|
|
7761
|
+
};
|
|
7762
|
+
function hash2(value, length = 16) {
|
|
7763
|
+
return createHash5("sha256").update(JSON.stringify(value)).digest("hex").slice(0, length);
|
|
7764
|
+
}
|
|
7765
|
+
function clamp(value, min = 0, max = 1) {
|
|
7766
|
+
return Math.max(min, Math.min(max, value));
|
|
7767
|
+
}
|
|
7768
|
+
function compactText(value) {
|
|
7769
|
+
return value.replace(/\s+/g, " ").trim();
|
|
7770
|
+
}
|
|
7771
|
+
function stripSourceSyntax(value) {
|
|
7772
|
+
return compactText(
|
|
7773
|
+
value.replace(/[`*_#>]/g, "").replace(/\b[A-Z]+:\s*\[[^\]]+\]/g, "").replace(/\[[^\]]*(?:brief|placeholder|todo|impact|summary)[^\]]*\]/gi, "").replace(/\b(?:Impact|Summary|Result|TODO)\s*:\s*$/i, "")
|
|
7774
|
+
);
|
|
7775
|
+
}
|
|
7776
|
+
function isPlaceholderIntent(value) {
|
|
7777
|
+
const text2 = value.trim();
|
|
7778
|
+
return /\[(?:brief|placeholder|todo|impact|summary)[^\]]*\]/i.test(text2) || /^(?:impact|summary|result|todo)\s*:\s*(?:$|\[[^\]]+\])/i.test(text2);
|
|
7779
|
+
}
|
|
7780
|
+
function isCodeFragmentIntent(value) {
|
|
7781
|
+
const text2 = value.trim();
|
|
7782
|
+
if (/^[{[\]}(),:;'"`|\\/-]+/.test(text2)) return true;
|
|
7783
|
+
if (/[{};]/.test(text2) && text2.length < 180) return true;
|
|
7784
|
+
if (/^[a-zA-Z_$][\w$]*\s*[:,]/.test(text2) && /[,{}]/.test(text2)) return true;
|
|
7785
|
+
if (/^(?:true|false|null|undefined|\d+)$/.test(text2)) return true;
|
|
7786
|
+
return false;
|
|
7787
|
+
}
|
|
7788
|
+
function isLowQualityIntent(value) {
|
|
7789
|
+
const text2 = value.trim();
|
|
7790
|
+
return text2.length < 18 || isPlaceholderIntent(text2) || isCodeFragmentIntent(text2) || /^[\W_]+$/.test(text2);
|
|
7791
|
+
}
|
|
7792
|
+
function cleanLoopIntent(value, fallback = "Unclassified AI work loop") {
|
|
7793
|
+
const cleaned = stripSourceSyntax(value).replace(/\bmcporgx\b/gi, "OrgX MCP").replace(/\bmcp__orgx__([a-z0-9_]+)/gi, "OrgX MCP $1").replace(/_/g, " ");
|
|
7794
|
+
if (!cleaned || isLowQualityIntent(cleaned)) return fallback;
|
|
7795
|
+
return cleaned.slice(0, 160);
|
|
7796
|
+
}
|
|
7797
|
+
function loopTopicKey(loop) {
|
|
7798
|
+
const intent = cleanLoopIntent(loop.origin.intent, "");
|
|
7799
|
+
const text2 = `${intent} ${loop.path.tools_used.join(" ")} ${loop.bottleneck.class ?? ""}`.toLowerCase();
|
|
7800
|
+
const topicRules = [
|
|
7801
|
+
[/\blist entities\b|\blist_entities\b|\bschema validation\b|\bzod\b/, "orgx-list-entities-schema"],
|
|
7802
|
+
[/\bscaffold\b|\binitiative\b|\boperation qa loop\b/, "scaffold-initiative-loop"],
|
|
7803
|
+
[/\bship batch\b|\bship_batch\b|\bartifacturl\b|\bexternalurl\b/, "ship-batch-artifact-contract"],
|
|
7804
|
+
[/\bruntime hook\b|\bwriteback\b|\brecord outcome\b|\bsubmit learning\b/, "runtime-writeback"],
|
|
7805
|
+
[/\b64\b.*\btools\b|\bmcp tools\b|\btool catalog\b/, "mcp-tool-catalog"],
|
|
7806
|
+
[/\banthropic\b|\bapi credits\b|\bbilling\b|\bquota\b/, "provider-auth-billing"],
|
|
7807
|
+
[/\bgithub\b|\bcommit\b|\bpr\b|\bproof\b/, "github-proof"],
|
|
7808
|
+
[/\bpublic profile\b|\bwork graph\b|\bmirror\b|\bclaim\b/, "work-graph-profile"],
|
|
7809
|
+
[/\bcursor\b/, "cursor-client"],
|
|
7810
|
+
[/\bcodex\b/, "codex-client"],
|
|
7811
|
+
[/\bclaude\b/, "claude-code-client"]
|
|
7812
|
+
];
|
|
7813
|
+
for (const [pattern, topic] of topicRules) {
|
|
7814
|
+
if (pattern.test(text2)) return topic;
|
|
7815
|
+
}
|
|
7816
|
+
if (!intent) return `unclassified:${hash2(loop.loop_id, 10)}`;
|
|
7817
|
+
return `topic:${hash2(intent.toLowerCase(), 10)}`;
|
|
7818
|
+
}
|
|
7819
|
+
function bestFamilyCentroid(group) {
|
|
7820
|
+
const candidates = group.map((loop) => cleanLoopIntent(loop.origin.intent, "")).filter(Boolean).map((intent) => {
|
|
7821
|
+
let score = 0;
|
|
7822
|
+
if (!isLowQualityIntent(intent)) score += 20;
|
|
7823
|
+
if (/\borgx|mcp|codex|claude|cursor|github|slack|runtime|hook|scaffold|profile|wizard/i.test(intent)) score += 8;
|
|
7824
|
+
if (intent.length >= 32 && intent.length <= 120) score += 4;
|
|
7825
|
+
if (/[{}\[\];]/.test(intent)) score -= 20;
|
|
7826
|
+
if (/^(?:impact|summary|result)\s*:/i.test(intent)) score -= 16;
|
|
7827
|
+
return { intent, score };
|
|
7828
|
+
}).sort((left, right) => right.score - left.score);
|
|
7829
|
+
return candidates[0]?.intent || "A repeated AI work loop needs durable writeback";
|
|
7830
|
+
}
|
|
7831
|
+
function articleFor(value) {
|
|
7832
|
+
return /^[aeiou]/i.test(value.trim()) ? "an" : "a";
|
|
7833
|
+
}
|
|
7834
|
+
function countBy(values) {
|
|
7835
|
+
const counts = /* @__PURE__ */ new Map();
|
|
7836
|
+
for (const value of values) counts.set(value, (counts.get(value) ?? 0) + 1);
|
|
7837
|
+
return [...counts.entries()].map(([key, count]) => ({ key, count }));
|
|
7838
|
+
}
|
|
7839
|
+
function normalizeClient(value) {
|
|
7840
|
+
const lower = value.toLowerCase().replace(/-/g, "_");
|
|
7841
|
+
if (lower.includes("claude")) return "claude_code";
|
|
7842
|
+
if (lower.includes("codex") || lower.includes("openai")) return "codex";
|
|
7843
|
+
if (lower.includes("cursor")) return "cursor";
|
|
7844
|
+
if (lower.includes("opencode")) return "opencode";
|
|
7845
|
+
if (lower.includes("goose")) return "goose";
|
|
7846
|
+
if (lower.includes("github") || lower === "git") return "github";
|
|
7847
|
+
if (lower.includes("slack")) return "slack";
|
|
7848
|
+
if (lower.includes("hook") || lower.includes("runtime") || lower.includes("mcp")) return "orgx_runtime_hook";
|
|
7849
|
+
return "codex";
|
|
7850
|
+
}
|
|
7851
|
+
function providerForClient2(client) {
|
|
7852
|
+
if (client === "claude_code") return "anthropic";
|
|
7853
|
+
if (client === "codex") return "openai";
|
|
7854
|
+
return "unknown";
|
|
7855
|
+
}
|
|
7856
|
+
function eventKindForFinding(finding) {
|
|
7857
|
+
if (finding.type === "decision") return "assistant_reasoning";
|
|
7858
|
+
if (finding.type === "artifact") return "file_edit";
|
|
7859
|
+
if (finding.type === "blocker") return "tool_call_error";
|
|
7860
|
+
if (finding.type === "outcome") return "test_run";
|
|
7861
|
+
if (finding.type === "missed_orchestration_opportunity") return "mcp_tool_call";
|
|
7862
|
+
return "assistant_text";
|
|
7863
|
+
}
|
|
7864
|
+
function windowFor(timestamp, now) {
|
|
7865
|
+
const occurred = Date.parse(timestamp);
|
|
7866
|
+
const current = Date.parse(now);
|
|
7867
|
+
if (!Number.isFinite(occurred) || !Number.isFinite(current)) return "older";
|
|
7868
|
+
const delta = current - occurred;
|
|
7869
|
+
if (delta < 5 * 60 * 1e3) return "live";
|
|
7870
|
+
if (delta <= 24 * 60 * 60 * 1e3) return "recent_24h";
|
|
7871
|
+
if (delta <= 7 * 24 * 60 * 60 * 1e3) return "recent_7d";
|
|
7872
|
+
if (delta <= 30 * 24 * 60 * 60 * 1e3) return "recent_30d";
|
|
7873
|
+
return "older";
|
|
7874
|
+
}
|
|
7875
|
+
function rawEventFromFinding(finding, index, generatedAt) {
|
|
7876
|
+
const sourceClient = normalizeClient(finding.source_client);
|
|
7877
|
+
const timestamp = typeof finding.metadata.occurred_at === "string" ? finding.metadata.occurred_at : generatedAt;
|
|
7878
|
+
const eventId = `evt_${hash2([finding.evidence_ref, finding.title, index], 24)}`;
|
|
7879
|
+
const payload = {
|
|
7880
|
+
title: finding.title,
|
|
7881
|
+
summary: finding.summary,
|
|
7882
|
+
finding_type: finding.type,
|
|
7883
|
+
source_id: finding.source_id,
|
|
7884
|
+
confidence: finding.confidence,
|
|
7885
|
+
redacted_verbatim: typeof finding.metadata.redacted_verbatim === "string" ? finding.metadata.redacted_verbatim : finding.summary.slice(0, 420)
|
|
7886
|
+
};
|
|
7887
|
+
return {
|
|
7888
|
+
event_id: eventId,
|
|
7889
|
+
source_ref: {
|
|
7890
|
+
source_id: sourceClient,
|
|
7891
|
+
uri: finding.evidence_ref,
|
|
7892
|
+
cite_url: null,
|
|
7893
|
+
resolves: true
|
|
7894
|
+
},
|
|
7895
|
+
source_id: sourceClient,
|
|
7896
|
+
provider: providerForClient2(sourceClient),
|
|
7897
|
+
session_id: finding.source_id,
|
|
7898
|
+
project_id: null,
|
|
7899
|
+
timestamp,
|
|
7900
|
+
occurred_in_window: windowFor(timestamp, generatedAt),
|
|
7901
|
+
kind: eventKindForFinding(finding),
|
|
7902
|
+
role: finding.type === "blocker" ? "tool" : "meta",
|
|
7903
|
+
payload,
|
|
7904
|
+
raw_byte_offset: null,
|
|
7905
|
+
raw_row_id: null,
|
|
7906
|
+
content_hash: hash2(payload, 64),
|
|
7907
|
+
redaction_applied: true
|
|
7908
|
+
};
|
|
7909
|
+
}
|
|
7910
|
+
function rawEventFromSourceEvent(event, index, generatedAt) {
|
|
7911
|
+
const sourceClient = normalizeClient(event.source_client);
|
|
7912
|
+
const payload = {
|
|
7913
|
+
source_label: event.source_label,
|
|
7914
|
+
event_type: event.event_type,
|
|
7915
|
+
text_summary: event.text.slice(0, 420)
|
|
7916
|
+
};
|
|
7917
|
+
return {
|
|
7918
|
+
event_id: `evt_${hash2([event.evidence_ref, index], 24)}`,
|
|
7919
|
+
source_ref: {
|
|
7920
|
+
source_id: sourceClient,
|
|
7921
|
+
uri: event.evidence_ref,
|
|
7922
|
+
cite_url: null,
|
|
7923
|
+
resolves: true
|
|
7924
|
+
},
|
|
7925
|
+
source_id: sourceClient,
|
|
7926
|
+
provider: providerForClient2(sourceClient),
|
|
7927
|
+
session_id: event.source_id,
|
|
7928
|
+
project_id: null,
|
|
7929
|
+
timestamp: generatedAt,
|
|
7930
|
+
occurred_in_window: "live",
|
|
7931
|
+
kind: event.event_type === "tool_signal" ? "tool_call_start" : "assistant_text",
|
|
7932
|
+
role: "meta",
|
|
7933
|
+
payload,
|
|
7934
|
+
raw_byte_offset: null,
|
|
7935
|
+
raw_row_id: null,
|
|
7936
|
+
content_hash: hash2(payload, 64),
|
|
7937
|
+
redaction_applied: true
|
|
7938
|
+
};
|
|
7939
|
+
}
|
|
7940
|
+
function buildRawEvents(input) {
|
|
7941
|
+
const sourceEvents = input.events.map(
|
|
7942
|
+
(event, index) => rawEventFromSourceEvent(event, index, input.generatedAt)
|
|
7943
|
+
);
|
|
7944
|
+
const findingEvents = input.findings.map(
|
|
7945
|
+
(finding, index) => rawEventFromFinding(finding, index, input.generatedAt)
|
|
7946
|
+
);
|
|
7947
|
+
const byUri = /* @__PURE__ */ new Map();
|
|
7948
|
+
for (const event of [...input.rawEvents ?? [], ...sourceEvents, ...findingEvents]) {
|
|
7949
|
+
byUri.set(event.event_id || `${event.source_ref.uri}:${event.kind}`, event);
|
|
7950
|
+
}
|
|
7951
|
+
return [...byUri.values()].sort(
|
|
7952
|
+
(left, right) => left.timestamp.localeCompare(right.timestamp)
|
|
7953
|
+
);
|
|
7954
|
+
}
|
|
7955
|
+
function eventsForRefs(events, refs) {
|
|
7956
|
+
const refSet = new Set(refs);
|
|
7957
|
+
return events.filter((event) => refSet.has(event.source_ref.uri));
|
|
7958
|
+
}
|
|
7959
|
+
function intentClassForFinding(finding) {
|
|
7960
|
+
const text2 = `${finding.title}
|
|
7961
|
+
${finding.summary}`;
|
|
7962
|
+
if (finding.type === "decision") return "architecture_decision";
|
|
7963
|
+
if (finding.type === "artifact" || /\b(implemented|built|created|shipped)\b/i.test(text2)) {
|
|
7964
|
+
return "feature_implementation";
|
|
7965
|
+
}
|
|
7966
|
+
if (/\b(test|qa|verified|proof)\b/i.test(text2)) return "verification_or_testing";
|
|
7967
|
+
if (/\b(auth|config|mcp|source|connect|integration|hook)\b/i.test(text2)) {
|
|
7968
|
+
return "integration_or_connection";
|
|
7969
|
+
}
|
|
7970
|
+
if (/\b(refactor|cleanup|rename|remove)\b/i.test(text2)) return "cleanup_or_refactor";
|
|
7971
|
+
if (/\b(doc|readme|methodology|runbook)\b/i.test(text2)) return "documentation";
|
|
7972
|
+
if (finding.type === "blocker" && /\bbug|error|fail|zod|schema\b/i.test(text2)) return "bug_fix";
|
|
7973
|
+
if (finding.type === "blocker") return "investigation";
|
|
7974
|
+
return "unclear";
|
|
7975
|
+
}
|
|
7976
|
+
function bottleneckClassForText(text2) {
|
|
7977
|
+
if (/\bzod\b|schema|validation|expected .* received/i.test(text2)) return "schema_validation";
|
|
7978
|
+
if (/permission|approval|denied|unauthorized|401|403/i.test(text2)) return "permission_denied";
|
|
7979
|
+
if (/missing arg|wrong type|tool.*misconfigured|invalid/i.test(text2)) return "tool_misconfigured";
|
|
7980
|
+
if (/context overflow|compaction|too large/i.test(text2)) return "context_overflow";
|
|
7981
|
+
if (/auth|billing|quota|402|api key|token/i.test(text2)) return "auth_or_billing";
|
|
7982
|
+
if (/dispatch|race|not dispatched|ready stream/i.test(text2)) return "race_or_dispatch";
|
|
7983
|
+
if (/cross.repo|repo boundary|vendored|mirror/i.test(text2)) return "cross_repo_dependency";
|
|
7984
|
+
if (/external|api failed|timeout|timed out/i.test(text2)) return "external_api_failure";
|
|
7985
|
+
if (/owner|handoff|dri|approval/i.test(text2)) return "human_handoff_missing";
|
|
7986
|
+
return null;
|
|
7987
|
+
}
|
|
7988
|
+
function terminalForTrail(trail) {
|
|
7989
|
+
if (trail.state === "verified" || trail.valence === "healthy") {
|
|
7990
|
+
return {
|
|
7991
|
+
state: "shipped",
|
|
7992
|
+
proof: trail.evidence_refs[0] ? { kind: "inferred_only", ref: trail.evidence_refs[0] } : { kind: "inferred_only" },
|
|
7993
|
+
classified_by: "deterministic",
|
|
7994
|
+
classification_basis: ["verified_or_healthy_trail_state"]
|
|
7995
|
+
};
|
|
7996
|
+
}
|
|
7997
|
+
if (trail.state === "blocked" || trail.valence === "risk" || trail.valence === "escalating") {
|
|
7998
|
+
return {
|
|
7999
|
+
state: "blocked",
|
|
8000
|
+
proof: { kind: "absent", expected_kind: "owner_visible_resolution" },
|
|
8001
|
+
classified_by: "deterministic",
|
|
8002
|
+
classification_basis: ["blocked_or_escalating_trail_state"]
|
|
8003
|
+
};
|
|
8004
|
+
}
|
|
8005
|
+
if (trail.state === "stale" || trail.state === "decaying") {
|
|
8006
|
+
return {
|
|
8007
|
+
state: "abandoned",
|
|
8008
|
+
proof: { kind: "absent", expected_kind: "later_resolution_event" },
|
|
8009
|
+
classified_by: "deterministic",
|
|
8010
|
+
classification_basis: ["stale_or_decaying_trail_state"]
|
|
8011
|
+
};
|
|
8012
|
+
}
|
|
8013
|
+
return {
|
|
8014
|
+
state: "ongoing",
|
|
8015
|
+
proof: trail.evidence_refs[0] ? { kind: "inferred_only", ref: trail.evidence_refs[0] } : { kind: "inferred_only" },
|
|
8016
|
+
classified_by: "hybrid",
|
|
8017
|
+
classification_basis: ["no_deterministic_terminal_event"]
|
|
8018
|
+
};
|
|
8019
|
+
}
|
|
8020
|
+
function buildWorkLoops(input) {
|
|
8021
|
+
const findingsByRef = new Map(input.findings.map((finding) => [finding.evidence_ref, finding]));
|
|
8022
|
+
return input.trails.map((trail, index) => {
|
|
8023
|
+
const matchedEvents = eventsForRefs(input.events, trail.evidence_refs);
|
|
8024
|
+
const matchedFindings = trail.evidence_refs.map((ref) => findingsByRef.get(ref)).filter((finding) => Boolean(finding));
|
|
8025
|
+
const firstFinding = matchedFindings[0];
|
|
8026
|
+
const eventIds = matchedEvents.map((event) => event.event_id);
|
|
8027
|
+
const intent = cleanLoopIntent(
|
|
8028
|
+
trail.title,
|
|
8029
|
+
`${trail.subject_entity_type.replace(/_/g, " ")} work loop needs review`
|
|
8030
|
+
);
|
|
8031
|
+
const intentQualityOk = !isLowQualityIntent(trail.title) && !isLowQualityIntent(intent);
|
|
8032
|
+
const text2 = [
|
|
8033
|
+
trail.title,
|
|
8034
|
+
trail.summary,
|
|
8035
|
+
...matchedFindings.map((finding) => finding.summary)
|
|
8036
|
+
].join("\n");
|
|
8037
|
+
const bottleneck = bottleneckClassForText(text2);
|
|
8038
|
+
const terminal = terminalForTrail(trail);
|
|
8039
|
+
const confidence = clamp(
|
|
8040
|
+
trail.confidence || matchedFindings.reduce((total, finding) => total + finding.confidence, 0) / Math.max(1, matchedFindings.length)
|
|
8041
|
+
);
|
|
8042
|
+
return {
|
|
8043
|
+
loop_id: `loop_${hash2([trail.id, index], 18)}`,
|
|
8044
|
+
cites: eventIds,
|
|
8045
|
+
origin: {
|
|
8046
|
+
event_id: eventIds[0] ?? `evt_missing_${index}`,
|
|
8047
|
+
timestamp: matchedEvents[0]?.timestamp ?? trail.created_at,
|
|
8048
|
+
intent,
|
|
8049
|
+
intent_class: firstFinding ? intentClassForFinding(firstFinding) : "unclear"
|
|
8050
|
+
},
|
|
8051
|
+
path: {
|
|
8052
|
+
event_ids: eventIds,
|
|
8053
|
+
actions_taken: matchedEvents.map((event) => ({
|
|
8054
|
+
kind: event.kind,
|
|
8055
|
+
label: String(event.payload.title ?? event.kind).slice(0, 120),
|
|
8056
|
+
event_id: event.event_id
|
|
8057
|
+
})),
|
|
8058
|
+
sessions_spanned: [...new Set(matchedEvents.map((event) => event.session_id))],
|
|
8059
|
+
tools_used: matchedEvents.filter((event) => event.kind === "tool_call_start" || event.kind === "mcp_tool_call").map((event) => String(event.payload.tool_name ?? event.payload.title ?? "unknown_tool")),
|
|
8060
|
+
span_minutes: Math.max(1, matchedEvents.length * 12)
|
|
8061
|
+
},
|
|
8062
|
+
terminal,
|
|
8063
|
+
bottleneck: {
|
|
8064
|
+
class: bottleneck,
|
|
8065
|
+
evidence_event_ids: bottleneck ? eventIds : []
|
|
8066
|
+
},
|
|
8067
|
+
recurrence: {
|
|
8068
|
+
family_id: null,
|
|
8069
|
+
appearances_in_family: 1,
|
|
8070
|
+
is_first_appearance: true,
|
|
8071
|
+
decay_half_life_days: terminal.state === "blocked" ? 3 : null
|
|
8072
|
+
},
|
|
8073
|
+
confidence,
|
|
8074
|
+
survived_critic: confidence >= 0.6 && eventIds.length > 0,
|
|
8075
|
+
public_surface: confidence >= 0.6 && eventIds.length > 0 && intentQualityOk
|
|
8076
|
+
};
|
|
8077
|
+
});
|
|
8078
|
+
}
|
|
8079
|
+
function loopFamilyKey(loop) {
|
|
8080
|
+
return [
|
|
8081
|
+
loop.origin.intent_class,
|
|
8082
|
+
loop.terminal.state,
|
|
8083
|
+
loop.bottleneck.class ?? "none",
|
|
8084
|
+
loopTopicKey(loop)
|
|
8085
|
+
].join(":");
|
|
8086
|
+
}
|
|
8087
|
+
function shapeForFamily(loops) {
|
|
8088
|
+
if (loops.length <= 1) return "single_signal";
|
|
8089
|
+
if (loops.some((loop) => loop.terminal.state === "shipped")) return "decaying_resolved";
|
|
8090
|
+
if (loops.some((loop) => loop.bottleneck.class)) return loops.length >= 3 ? "accelerating" : "dense_recent_cluster";
|
|
8091
|
+
return loops.length >= 4 ? "chronic_evenly_spaced" : "dense_recent_cluster";
|
|
8092
|
+
}
|
|
8093
|
+
function repairForFamily(familyLoops, impact) {
|
|
8094
|
+
const top = familyLoops[0];
|
|
8095
|
+
const title = top?.origin.intent ?? "Investigate work loop";
|
|
8096
|
+
const bottleneck = top?.bottleneck.class;
|
|
8097
|
+
const capability = bottleneck === "human_handoff_missing" ? "mcp__orgx__assign_owner" : bottleneck ? "mcp__orgx__create_blocker" : top?.origin.intent_class === "architecture_decision" ? "mcp__orgx__create_decision" : "mcp__orgx__scaffold_initiative";
|
|
8098
|
+
return {
|
|
8099
|
+
named_orgx_capability: capability,
|
|
8100
|
+
expected_tool_call_args: {
|
|
8101
|
+
title,
|
|
8102
|
+
source: "work_graph_investigation",
|
|
8103
|
+
evidence_event_ids: familyLoops.flatMap((loop) => loop.cites).slice(0, 12),
|
|
8104
|
+
terminal_state: top?.terminal.state ?? "ongoing",
|
|
8105
|
+
bottleneck_class: bottleneck ?? null
|
|
8106
|
+
},
|
|
8107
|
+
expected_outcome: "OrgX would create an owner-visible record tied to the source evidence, then suppress duplicate rediscovery until it is reopened.",
|
|
8108
|
+
expected_score_lift: [
|
|
8109
|
+
{ dimension: "chronology_depth", delta: Math.min(18, familyLoops.length * 3) },
|
|
8110
|
+
{ dimension: "repair_actionability", delta: capability.includes("scaffold") ? 12 : 16 }
|
|
8111
|
+
],
|
|
8112
|
+
expected_hours_recovered: Number(Math.min(impact.time_saved_hours_per_week, familyLoops.length * 0.75).toFixed(1)),
|
|
8113
|
+
expected_dollars_recovered: Math.round(Math.min(impact.estimated_monthly_value_usd, familyLoops.length * 0.75 * 4.33 * 200)),
|
|
8114
|
+
preconditions: ["claim_profile", "confirm_or_correct_evidence"]
|
|
8115
|
+
};
|
|
8116
|
+
}
|
|
8117
|
+
function buildLoopFamilies(loops, events, impact) {
|
|
8118
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
8119
|
+
for (const loop of loops) {
|
|
8120
|
+
const key = loopFamilyKey(loop);
|
|
8121
|
+
grouped.set(key, [...grouped.get(key) ?? [], loop]);
|
|
8122
|
+
}
|
|
8123
|
+
const families = [...grouped.entries()].map(([key, group]) => {
|
|
8124
|
+
const sourceIds = new Set(
|
|
8125
|
+
group.flatMap(
|
|
8126
|
+
(loop) => loop.cites.map((cite) => events.find((event) => event.event_id === cite)?.source_id).filter(Boolean)
|
|
8127
|
+
)
|
|
8128
|
+
);
|
|
8129
|
+
const familyId = `family_${hash2(key, 16)}`;
|
|
8130
|
+
const timestamps = group.map((loop) => Date.parse(loop.origin.timestamp)).filter(Number.isFinite);
|
|
8131
|
+
const spanDays = timestamps.length > 1 ? Math.max(1, Math.ceil((Math.max(...timestamps) - Math.min(...timestamps)) / 864e5)) : 0;
|
|
8132
|
+
const publicLoopCount = group.filter((loop) => loop.public_surface).length;
|
|
8133
|
+
const semanticCentroid = bestFamilyCentroid(group);
|
|
8134
|
+
const divergenceScore = group.length >= 3 && publicLoopCount >= 3 ? 0.32 : 0.5;
|
|
8135
|
+
return {
|
|
8136
|
+
family_id: familyId,
|
|
8137
|
+
shared_intent_class: group[0]?.origin.intent_class ?? "unclear",
|
|
8138
|
+
shared_terminal_state: group[0]?.terminal.state ?? "ongoing",
|
|
8139
|
+
shared_bottleneck_class: group[0]?.bottleneck.class ?? null,
|
|
8140
|
+
loop_ids: group.map((loop) => loop.loop_id),
|
|
8141
|
+
appearances: group.length,
|
|
8142
|
+
span_days: spanDays,
|
|
8143
|
+
shape: shapeForFamily(group),
|
|
8144
|
+
semantic_centroid: semanticCentroid,
|
|
8145
|
+
divergence_score: divergenceScore,
|
|
8146
|
+
cross_source_confluence: sourceIds.size >= 2,
|
|
8147
|
+
orgx_repair: repairForFamily(group, impact),
|
|
8148
|
+
alternative_explanations_considered: [
|
|
8149
|
+
"The loops may be related only by vocabulary.",
|
|
8150
|
+
"The apparent recurrence may be a planned multi-step implementation rather than unresolved work."
|
|
8151
|
+
],
|
|
8152
|
+
alternative_rejected_because: group.length >= 3 ? ["Shared intent class, terminal state, and bottleneck class recur across multiple evidence events."] : ["Kept as a non-public single/weak family until more chronology exists."],
|
|
8153
|
+
survived_critic: group.length >= 3 && publicLoopCount >= 3 && divergenceScore < 0.5,
|
|
8154
|
+
public_surface: group.length >= 3 && publicLoopCount >= 3 && divergenceScore < 0.5
|
|
8155
|
+
};
|
|
8156
|
+
});
|
|
8157
|
+
const familyMap = new Map(families.map((family) => [family.family_id, family]));
|
|
8158
|
+
for (const family of families) {
|
|
8159
|
+
for (const loopId of family.loop_ids) {
|
|
8160
|
+
const loop = loops.find((candidate) => candidate.loop_id === loopId);
|
|
8161
|
+
if (!loop) continue;
|
|
8162
|
+
loop.recurrence.family_id = family.family_id;
|
|
8163
|
+
loop.recurrence.appearances_in_family = family.appearances;
|
|
8164
|
+
loop.recurrence.is_first_appearance = family.loop_ids[0] === loop.loop_id;
|
|
8165
|
+
}
|
|
8166
|
+
}
|
|
8167
|
+
return [...familyMap.values()].sort((left, right) => right.appearances - left.appearances);
|
|
8168
|
+
}
|
|
8169
|
+
function buildUsageCatalogue(events, findings) {
|
|
8170
|
+
const toolEvents = events.filter((event) => event.kind === "tool_call_start" || event.kind === "mcp_tool_call");
|
|
8171
|
+
const mcpToolNames = /* @__PURE__ */ new Map();
|
|
8172
|
+
for (const event of toolEvents) {
|
|
8173
|
+
const text2 = `${event.payload.title ?? ""}
|
|
8174
|
+
${event.payload.summary ?? ""}
|
|
8175
|
+
${event.payload.text_summary ?? ""}`;
|
|
8176
|
+
const match = text2.match(/\b(?:mcp__[\w-]+__[\w-]+|orgx_[\w-]+|complete_with_proof|ship_batch|record_outcome|scaffold_initiative)\b/i);
|
|
8177
|
+
const name = match?.[0] ?? "unknown_tool";
|
|
8178
|
+
mcpToolNames.set(name, [...mcpToolNames.get(name) ?? [], event]);
|
|
8179
|
+
}
|
|
8180
|
+
const mentionedTools = /* @__PURE__ */ new Set();
|
|
8181
|
+
for (const finding of findings) {
|
|
8182
|
+
const matches = `${finding.title}
|
|
8183
|
+
${finding.summary}`.matchAll(/\b(?:mcp__[\w-]+__[\w-]+|orgx_[\w-]+|complete_with_proof|ship_batch|record_outcome|scaffold_initiative)\b/gi);
|
|
8184
|
+
for (const match of matches) mentionedTools.add(match[0]);
|
|
8185
|
+
}
|
|
8186
|
+
const configuredOrgxEvents = events.filter(
|
|
8187
|
+
(event) => event.payload.tool_usage_state === "configured" && /\borgx\b|mcp/i.test(JSON.stringify(event.payload))
|
|
8188
|
+
);
|
|
8189
|
+
const shouldHaveInvoked = findings.filter(
|
|
8190
|
+
(finding) => finding.type === "missed_orchestration_opportunity" || /\bshould have|available but not called|not invoked|writeback\b/i.test(`${finding.title}
|
|
8191
|
+
${finding.summary}`)
|
|
8192
|
+
);
|
|
8193
|
+
const mcp_tools = [
|
|
8194
|
+
...[...mcpToolNames.entries()].map(([name, matchingEvents]) => ({
|
|
8195
|
+
name,
|
|
8196
|
+
namespace: name.startsWith("mcp__") ? name.split("__")[1] ?? "unknown" : "orgx",
|
|
8197
|
+
state: matchingEvents.length > 0 ? "invoked" : "mentioned",
|
|
8198
|
+
invocations: matchingEvents.length,
|
|
8199
|
+
success: matchingEvents.filter((event) => event.kind === "tool_call_result").length,
|
|
8200
|
+
error: matchingEvents.filter((event) => event.kind === "tool_call_error").length,
|
|
8201
|
+
error_classes: [],
|
|
8202
|
+
by_source: countBy(matchingEvents.map((event) => event.source_id)).map(({ key, count }) => ({
|
|
8203
|
+
source_id: key,
|
|
8204
|
+
count
|
|
8205
|
+
})),
|
|
8206
|
+
event_refs: matchingEvents.map((event) => event.event_id)
|
|
8207
|
+
})),
|
|
8208
|
+
...[...mentionedTools].filter((name) => !mcpToolNames.has(name)).map((name) => ({
|
|
8209
|
+
name,
|
|
8210
|
+
namespace: name.startsWith("mcp__") ? name.split("__")[1] ?? "unknown" : "orgx",
|
|
8211
|
+
state: "mentioned",
|
|
8212
|
+
invocations: 0,
|
|
8213
|
+
success: 0,
|
|
8214
|
+
error: 0,
|
|
8215
|
+
error_classes: [],
|
|
8216
|
+
by_source: [],
|
|
8217
|
+
event_refs: []
|
|
8218
|
+
})),
|
|
8219
|
+
...configuredOrgxEvents.length > 0 && !mcpToolNames.has("mcp__orgx__configured") ? [{
|
|
8220
|
+
name: "mcp__orgx__configured",
|
|
8221
|
+
namespace: "orgx",
|
|
8222
|
+
state: "configured",
|
|
8223
|
+
invocations: 0,
|
|
8224
|
+
success: 0,
|
|
8225
|
+
error: 0,
|
|
8226
|
+
error_classes: [],
|
|
8227
|
+
by_source: countBy(configuredOrgxEvents.map((event) => event.source_id)).map(({ key, count }) => ({
|
|
8228
|
+
source_id: key,
|
|
8229
|
+
count
|
|
8230
|
+
})),
|
|
8231
|
+
event_refs: configuredOrgxEvents.map((event) => event.event_id)
|
|
8232
|
+
}] : [],
|
|
8233
|
+
...shouldHaveInvoked.length > 0 && !mcpToolNames.has("mcp__orgx__create_decision") ? [{
|
|
8234
|
+
name: "mcp__orgx__create_decision",
|
|
8235
|
+
namespace: "orgx",
|
|
8236
|
+
state: "should_have_invoked",
|
|
8237
|
+
invocations: 0,
|
|
8238
|
+
success: 0,
|
|
8239
|
+
error: 0,
|
|
8240
|
+
error_classes: [],
|
|
8241
|
+
by_source: [],
|
|
8242
|
+
event_refs: []
|
|
8243
|
+
}] : []
|
|
8244
|
+
];
|
|
8245
|
+
const ai_clients = WORK_GRAPH_INVESTIGATION_CLIENTS.slice(0, 5).map((sourceId) => {
|
|
8246
|
+
const sourceEvents = events.filter((event) => event.source_id === sourceId);
|
|
8247
|
+
return {
|
|
8248
|
+
source_id: sourceId,
|
|
8249
|
+
sessions: new Set(sourceEvents.map((event) => event.session_id)).size,
|
|
8250
|
+
turns: sourceEvents.filter((event) => event.kind === "user_prompt" || event.kind === "assistant_text").length
|
|
8251
|
+
};
|
|
8252
|
+
});
|
|
8253
|
+
return {
|
|
8254
|
+
ai_clients,
|
|
8255
|
+
mcp_tools,
|
|
8256
|
+
mcp_namespaces_seen: [...new Set(mcp_tools.map((tool) => tool.namespace))],
|
|
8257
|
+
shell_commands: [],
|
|
8258
|
+
recipes_or_skills: findings.filter((finding) => /\bskill|recipe|agent\b/i.test(`${finding.title}
|
|
8259
|
+
${finding.summary}`)).slice(0, 20).map((finding) => ({
|
|
8260
|
+
source_id: normalizeClient(finding.source_client),
|
|
8261
|
+
name: finding.title.slice(0, 80),
|
|
8262
|
+
invocations: 0,
|
|
8263
|
+
last_used: typeof finding.metadata.occurred_at === "string" ? finding.metadata.occurred_at : (/* @__PURE__ */ new Date()).toISOString()
|
|
8264
|
+
})),
|
|
8265
|
+
agents_observed: ai_clients.filter((client) => client.sessions > 0).map((client) => ({
|
|
8266
|
+
source_id: client.source_id,
|
|
8267
|
+
name: BRANDS[client.source_id].name,
|
|
8268
|
+
spawn_count: client.sessions,
|
|
8269
|
+
success_count: 0
|
|
8270
|
+
})),
|
|
8271
|
+
repo_paths_touched: [],
|
|
8272
|
+
external_sources_called: []
|
|
8273
|
+
};
|
|
8274
|
+
}
|
|
8275
|
+
function capabilityCellsFor(input) {
|
|
8276
|
+
const ceilings = CAPABILITY_CEILINGS[input.sourceId];
|
|
8277
|
+
const sourceEvents = input.events.filter((event) => event.source_id === input.sourceId);
|
|
8278
|
+
const achievedBase = input.status === "connected" ? 0.68 : input.status === "partial" ? 0.42 : input.status === "detected_not_read" ? 0.16 : 0;
|
|
8279
|
+
const hasToolState = sourceEvents.some((event) => event.kind.includes("tool"));
|
|
8280
|
+
const hasVerification = sourceEvents.some((event) => event.kind === "test_run" || event.kind === "commit");
|
|
8281
|
+
const hasHandoff = sourceEvents.some((event) => /handoff|owner|approval/i.test(JSON.stringify(event.payload)));
|
|
8282
|
+
return Object.fromEntries(
|
|
8283
|
+
Object.keys(ceilings).map((dimension) => {
|
|
8284
|
+
const ceiling = ceilings[dimension];
|
|
8285
|
+
const boost = dimension === "decision_lineage" && hasToolState || dimension === "verification" && hasVerification || dimension === "handoff" && hasHandoff ? 0.18 : 0;
|
|
8286
|
+
const achieved = ceiling === "none" ? 0 : clamp(achievedBase + boost);
|
|
8287
|
+
return [
|
|
8288
|
+
dimension,
|
|
8289
|
+
{
|
|
8290
|
+
ceiling,
|
|
8291
|
+
achieved,
|
|
8292
|
+
basis: sourceEvents.length > 0 ? `${sourceEvents.length} normalized raw event${sourceEvents.length === 1 ? "" : "s"}` : input.status === "missing" ? "Source supported but not connected for this audit" : "Source detected without enough readable evidence",
|
|
8293
|
+
blockers_to_full: achieved >= 0.8 ? [] : [
|
|
8294
|
+
dimension === "verification" ? "connect_github_or_runtime_hook" : dimension === "ownership" || dimension === "handoff" ? "connect_slack_or_claim_profile" : "deepen_native_session_events"
|
|
8295
|
+
]
|
|
8296
|
+
}
|
|
8297
|
+
];
|
|
8298
|
+
})
|
|
8299
|
+
);
|
|
8300
|
+
}
|
|
8301
|
+
function buildCorpusManifest(input) {
|
|
8302
|
+
const manifests = WORK_GRAPH_INVESTIGATION_CLIENTS.map((sourceId) => {
|
|
8303
|
+
const sourceEvents = input.events.filter((event) => event.source_id === sourceId);
|
|
8304
|
+
const matchingExtraction = input.clientExtractions.find(
|
|
8305
|
+
(extraction) => normalizeClient(extraction.source_client) === sourceId
|
|
8306
|
+
);
|
|
8307
|
+
const coverageManifest = input.coverage.manifests?.find(
|
|
8308
|
+
(manifest) => normalizeClient(manifest.source_client) === sourceId
|
|
8309
|
+
);
|
|
8310
|
+
const status = sourceEvents.length > 0 || matchingExtraction && matchingExtraction.finding_count > 0 || coverageManifest?.status === "connected" ? "connected" : matchingExtraction ? "detected_not_read" : coverageManifest?.status === "partial" ? "partial" : input.coverage.missing.some((source) => normalizeClient(source) === sourceId) ? "missing" : "supported_not_connected";
|
|
8311
|
+
const skipped = coverageManifest?.skipped_session_count ? [{ reason: "no_entity_references", count: coverageManifest.skipped_session_count }] : [];
|
|
8312
|
+
const cells = capabilityCellsFor({ sourceId, status, events: input.events });
|
|
8313
|
+
const sourceSessionCount = new Set(sourceEvents.map((event) => event.session_id)).size;
|
|
8314
|
+
return {
|
|
8315
|
+
source_id: sourceId,
|
|
8316
|
+
brand: BRANDS[sourceId],
|
|
8317
|
+
client: sourceId,
|
|
8318
|
+
provider: providerForClient2(sourceId),
|
|
8319
|
+
status,
|
|
8320
|
+
extraction_mode: sourceId === "opencode" || sourceId === "goose" ? "sqlite" : sourceId === "cursor" ? "specstory" : sourceId === "orgx_runtime_hook" ? "orgx_runtime_hook" : "jsonl",
|
|
8321
|
+
storage_paths_resolved: coverageManifest?.searched_sources ?? [],
|
|
8322
|
+
storage_version_detected: null,
|
|
8323
|
+
files_seen: coverageManifest?.searched_session_count ?? matchingExtraction?.searched_session_count ?? sourceSessionCount,
|
|
8324
|
+
files_read: sourceEvents.length > 0 ? Math.max(1, sourceSessionCount) : 0,
|
|
8325
|
+
files_skipped: skipped,
|
|
8326
|
+
sessions: sourceSessionCount || matchingExtraction?.searched_session_count || coverageManifest?.searched_session_count || 0,
|
|
8327
|
+
message_turns: sourceEvents.filter((event) => event.kind === "user_prompt" || event.kind === "assistant_text").length || (sourceId === "codex" || sourceId === "claude_code" ? input.auditMethod.searched_message_count : 0),
|
|
8328
|
+
tool_calls: sourceEvents.filter((event) => event.kind === "tool_call_start").length,
|
|
8329
|
+
mcp_tool_calls: sourceEvents.filter((event) => event.kind === "mcp_tool_call").length,
|
|
8330
|
+
capability_cells: cells
|
|
8331
|
+
};
|
|
8332
|
+
});
|
|
8333
|
+
const timestamps = input.events.map((event) => event.timestamp).sort();
|
|
8334
|
+
const filesSeen = manifests.reduce((total, source) => total + source.files_seen, 0);
|
|
8335
|
+
const filesRead = manifests.reduce((total, source) => total + source.files_read, 0);
|
|
8336
|
+
const filesSkipped = manifests.reduce(
|
|
8337
|
+
(total, source) => total + source.files_skipped.reduce((sum, skipped) => sum + skipped.count, 0),
|
|
8338
|
+
0
|
|
8339
|
+
);
|
|
8340
|
+
const sessions = manifests.reduce((total, source) => total + source.sessions, 0);
|
|
8341
|
+
return {
|
|
8342
|
+
audit_id: input.auditId,
|
|
8343
|
+
ran_at: input.generatedAt,
|
|
8344
|
+
sources: manifests,
|
|
8345
|
+
totals: {
|
|
8346
|
+
files_seen: Math.max(filesSeen, input.auditMethod.searched_session_files),
|
|
8347
|
+
files_read: Math.max(filesRead, input.auditMethod.searched_session_files - input.auditMethod.skipped_session_files),
|
|
8348
|
+
files_skipped: Math.max(filesSkipped, input.auditMethod.skipped_session_files),
|
|
8349
|
+
bytes_read: 0,
|
|
8350
|
+
sessions: Math.max(sessions, input.auditMethod.searched_session_files),
|
|
8351
|
+
sessions_skipped: input.auditMethod.skipped_session_files ? [{ reason: "no_entity_references", count: input.auditMethod.skipped_session_files }] : [],
|
|
8352
|
+
message_turns: Math.max(
|
|
8353
|
+
input.auditMethod.searched_message_count,
|
|
8354
|
+
input.events.filter((event) => event.kind === "user_prompt" || event.kind === "assistant_text").length
|
|
8355
|
+
),
|
|
8356
|
+
user_prompts: input.events.filter((event) => event.kind === "user_prompt").length,
|
|
8357
|
+
assistant_turns: input.events.filter((event) => event.kind === "assistant_text").length,
|
|
8358
|
+
assistant_reasoning_turns: input.events.filter((event) => event.kind === "assistant_reasoning").length,
|
|
8359
|
+
tool_calls: input.events.filter((event) => event.kind === "tool_call_start").length,
|
|
8360
|
+
mcp_tool_calls: input.events.filter((event) => event.kind === "mcp_tool_call").length,
|
|
8361
|
+
tool_call_errors: input.events.filter((event) => event.kind === "tool_call_error").length,
|
|
8362
|
+
file_edits: input.events.filter((event) => event.kind === "file_edit").length,
|
|
8363
|
+
file_reads: input.events.filter((event) => event.kind === "file_read").length,
|
|
8364
|
+
shell_commands: input.events.filter((event) => event.kind === "shell_command").length,
|
|
8365
|
+
test_runs: input.events.filter((event) => event.kind === "test_run").length,
|
|
8366
|
+
errors_emitted: input.events.filter((event) => event.kind === "error_emitted").length,
|
|
8367
|
+
permission_requests: input.events.filter((event) => event.kind === "permission_request").length,
|
|
8368
|
+
forks: input.events.filter((event) => event.kind === "session_fork").length,
|
|
8369
|
+
compactions: input.events.filter((event) => event.kind === "compaction").length,
|
|
8370
|
+
earliest_event: timestamps[0] ?? null,
|
|
8371
|
+
latest_event: timestamps[timestamps.length - 1] ?? null
|
|
8372
|
+
}
|
|
8373
|
+
};
|
|
8374
|
+
}
|
|
8375
|
+
function buildSourceConfidence(corpusManifest) {
|
|
8376
|
+
return {
|
|
8377
|
+
rows: [
|
|
8378
|
+
"chronology",
|
|
8379
|
+
"ownership",
|
|
8380
|
+
"verification",
|
|
8381
|
+
"handoff",
|
|
8382
|
+
"customer_context",
|
|
8383
|
+
"decision_lineage"
|
|
8384
|
+
],
|
|
8385
|
+
columns: corpusManifest.sources
|
|
8386
|
+
};
|
|
8387
|
+
}
|
|
8388
|
+
function verifyItems(items, events) {
|
|
8389
|
+
const eventIds = new Set(events.map((event) => event.event_id));
|
|
8390
|
+
return items.map((item) => {
|
|
8391
|
+
const failed = item.cites.filter((cite) => !eventIds.has(cite)).map((cite) => ({ event_id_ref: cite, reason: "missing_event" }));
|
|
8392
|
+
return {
|
|
8393
|
+
finding_id: item.id,
|
|
8394
|
+
citations_total: item.cites.length,
|
|
8395
|
+
citations_resolved: item.cites.length - failed.length,
|
|
8396
|
+
citations_failed: failed,
|
|
8397
|
+
content_hashes_match: true,
|
|
8398
|
+
passed: item.cites.length > 0 && failed.length === 0
|
|
8399
|
+
};
|
|
8400
|
+
});
|
|
8401
|
+
}
|
|
8402
|
+
function criticForLoop(loop) {
|
|
8403
|
+
const survival = clamp(loop.confidence - (loop.cites.length <= 1 ? 0.08 : 0));
|
|
8404
|
+
return {
|
|
8405
|
+
finding_id: loop.loop_id,
|
|
8406
|
+
attack_vectors_attempted: [
|
|
8407
|
+
"is_this_actually_recurring",
|
|
8408
|
+
"is_this_actually_blocked",
|
|
8409
|
+
"is_the_intent_actually_what_we_say",
|
|
8410
|
+
"are_these_events_actually_related",
|
|
8411
|
+
"is_the_dollar_estimate_defensible",
|
|
8412
|
+
"would_orgx_actually_have_helped",
|
|
8413
|
+
"is_the_owner_actually_ambiguous",
|
|
8414
|
+
"is_the_recurrence_just_text_overlap"
|
|
8415
|
+
],
|
|
8416
|
+
weakest_claim: loop.cites.length <= 1 ? "Only one resolved citation supports this loop; chronology depth is limited." : "The loop is supported, but source coverage may still miss owner or verification context.",
|
|
8417
|
+
most_plausible_alternative: loop.cites.length <= 1 ? "This may be a single work signal rather than a recurring bottleneck." : "This could be normal staged execution rather than unresolved rework.",
|
|
8418
|
+
survival_score: Number(survival.toFixed(2)),
|
|
8419
|
+
survived: survival >= 0.6,
|
|
8420
|
+
demoted_to: survival >= 0.6 ? "public" : "weak_signal"
|
|
8421
|
+
};
|
|
8422
|
+
}
|
|
8423
|
+
function counterfactualForLoop(loop, family) {
|
|
8424
|
+
if (!loop.survived_critic || loop.cites.length === 0) return null;
|
|
8425
|
+
const repair = family?.orgx_repair ?? repairForFamily([loop], {
|
|
8426
|
+
time_saved_hours_per_week: 1,
|
|
8427
|
+
acceleration_percent: 10,
|
|
8428
|
+
estimated_monthly_value_usd: 800,
|
|
8429
|
+
confidence: loop.confidence,
|
|
8430
|
+
basis: [],
|
|
8431
|
+
assumptions: []
|
|
8432
|
+
});
|
|
8433
|
+
const realism = clamp(loop.confidence + (loop.terminal.state === "blocked" ? 0.06 : 0), 0, 0.92);
|
|
8434
|
+
if (realism < 0.7) return null;
|
|
8435
|
+
const entityKind = repair.named_orgx_capability.includes("decision") ? "decision" : repair.named_orgx_capability.includes("blocker") ? "task" : repair.named_orgx_capability.includes("artifact") ? "artifact" : "initiative";
|
|
8436
|
+
return {
|
|
8437
|
+
triggered_by_event_id: loop.origin.event_id,
|
|
8438
|
+
triggered_at: loop.origin.timestamp,
|
|
8439
|
+
actual_user_event_summary: `You worked on "${loop.origin.intent}" without a durable OrgX repair record tied to the source event.`,
|
|
8440
|
+
actual_outcome: loop.terminal.state === "blocked" ? "The loop stayed blocked or returned as new work." : "The work stayed visible in session evidence, but not as durable operating memory.",
|
|
8441
|
+
orgx_capability: {
|
|
8442
|
+
tool: repair.named_orgx_capability,
|
|
8443
|
+
args: repair.expected_tool_call_args,
|
|
8444
|
+
args_basis: ["loop.origin.intent", "loop.cites", "loop.terminal.state", "loop.bottleneck.class"]
|
|
8445
|
+
},
|
|
8446
|
+
orgx_outcome: repair.expected_outcome,
|
|
8447
|
+
resulting_entity: {
|
|
8448
|
+
kind: entityKind,
|
|
8449
|
+
inferred_id: `orgx_${entityKind}_${hash2(loop.loop_id, 12)}`,
|
|
8450
|
+
would_link_to: loop.cites
|
|
8451
|
+
},
|
|
8452
|
+
realism_score: Number(realism.toFixed(2)),
|
|
8453
|
+
realism_basis: [
|
|
8454
|
+
"The triggering event resolves to the normalized corpus.",
|
|
8455
|
+
"The proposed OrgX capability maps directly to the loop terminal state and bottleneck.",
|
|
8456
|
+
"The output is deterministic: create or link an owner-visible entity rather than asserting improvement."
|
|
8457
|
+
],
|
|
8458
|
+
could_have_failed_because: [
|
|
8459
|
+
"The user may not have granted write permission.",
|
|
8460
|
+
"The inferred title or owner might need human correction after claim.",
|
|
8461
|
+
"The source may lack enough downstream verification until GitHub or runtime hooks are connected."
|
|
8462
|
+
],
|
|
8463
|
+
generated_by_prompt_version: "orgx-investigation-counterfactual-v1",
|
|
8464
|
+
survived_critic: true
|
|
8465
|
+
};
|
|
8466
|
+
}
|
|
8467
|
+
function buildWhyNot100(input) {
|
|
8468
|
+
const entries = [];
|
|
8469
|
+
const singleSignal = input.loops.filter((loop) => loop.cites.length <= 1).length;
|
|
8470
|
+
const confluenceFamilies = input.families.filter((family) => family.cross_source_confluence).length;
|
|
8471
|
+
const fileEdits = input.corpus.totals.file_edits;
|
|
8472
|
+
if (confluenceFamilies === 0) {
|
|
8473
|
+
entries.push({
|
|
8474
|
+
dimension: "cross_source_confluence",
|
|
8475
|
+
current_score: 0,
|
|
8476
|
+
precise_gap: `0 cross-source loop families were proven; ${input.loops.length} loops still need a second source such as GitHub, Slack, or runtime hooks.`,
|
|
8477
|
+
user_action: {
|
|
8478
|
+
kind: "connect_source",
|
|
8479
|
+
target: "GitHub or Slack",
|
|
8480
|
+
ux_route: "/integrations",
|
|
8481
|
+
copy_for_button: "Connect proof source"
|
|
8482
|
+
},
|
|
8483
|
+
orgx_action: {
|
|
8484
|
+
kind: "auto_correlate",
|
|
8485
|
+
description: "OrgX will join session events to commits, approvals, and runtime writeback events.",
|
|
8486
|
+
triggers_when: "on_next_audit_run"
|
|
8487
|
+
},
|
|
8488
|
+
expected_score_lift: 22,
|
|
8489
|
+
expected_findings_unlocked: Math.max(1, Math.ceil(input.loops.length * 0.35)),
|
|
8490
|
+
preview_findings: [
|
|
8491
|
+
{
|
|
8492
|
+
title_redacted: "Session decision linked to commit proof",
|
|
8493
|
+
evidence_count_anticipated: Math.max(2, fileEdits || 2),
|
|
8494
|
+
confidence_anticipated: 0.82,
|
|
8495
|
+
derivation_basis: "extrapolated_from_existing"
|
|
8496
|
+
}
|
|
8497
|
+
],
|
|
8498
|
+
preconditions: ["connect_github_or_slack"],
|
|
8499
|
+
estimated_seconds_to_complete: 90
|
|
8500
|
+
});
|
|
8501
|
+
}
|
|
8502
|
+
if (singleSignal > 0) {
|
|
8503
|
+
entries.push({
|
|
8504
|
+
dimension: "chronology_depth",
|
|
8505
|
+
current_score: Math.max(1, 10 - Math.min(9, singleSignal)),
|
|
8506
|
+
precise_gap: `${singleSignal} work loop${singleSignal === 1 ? "" : "s"} have only one resolved event; OrgX can identify them but cannot yet prove before/after motion.`,
|
|
8507
|
+
user_action: {
|
|
8508
|
+
kind: "install_hook",
|
|
8509
|
+
target: "OrgX runtime hooks",
|
|
8510
|
+
ux_route: "/settings/integrations/orgx-runtime",
|
|
8511
|
+
copy_for_button: "Install runtime hooks"
|
|
8512
|
+
},
|
|
8513
|
+
orgx_action: {
|
|
8514
|
+
kind: "auto_writeback",
|
|
8515
|
+
description: "OrgX will append session-start, tool-use, stop, and outcome events to the same loop.",
|
|
8516
|
+
triggers_when: "on_user_action_completion"
|
|
8517
|
+
},
|
|
8518
|
+
expected_score_lift: Math.min(24, singleSignal * 3),
|
|
8519
|
+
expected_findings_unlocked: singleSignal,
|
|
8520
|
+
preview_findings: [
|
|
8521
|
+
{
|
|
8522
|
+
title_redacted: "Blocked loop gains start, retry, and resolution events",
|
|
8523
|
+
evidence_count_anticipated: 3,
|
|
8524
|
+
confidence_anticipated: 0.78,
|
|
8525
|
+
derivation_basis: "rule_based"
|
|
8526
|
+
}
|
|
8527
|
+
],
|
|
8528
|
+
preconditions: ["install_runtime_hook"],
|
|
8529
|
+
estimated_seconds_to_complete: 120
|
|
8530
|
+
});
|
|
8531
|
+
}
|
|
8532
|
+
if (input.coverage.missing.length > 0) {
|
|
8533
|
+
entries.push({
|
|
8534
|
+
dimension: "source_coverage",
|
|
8535
|
+
current_score: Math.max(0, 10 - input.coverage.missing.length * 2),
|
|
8536
|
+
precise_gap: `${input.coverage.missing.length} source gap${input.coverage.missing.length === 1 ? "" : "s"} remain: ${input.coverage.missing.join(", ")}.`,
|
|
8537
|
+
user_action: {
|
|
8538
|
+
kind: "connect_source",
|
|
8539
|
+
target: input.coverage.missing[0] ?? "missing source",
|
|
8540
|
+
ux_route: "/integrations",
|
|
8541
|
+
copy_for_button: "Connect missing source"
|
|
8542
|
+
},
|
|
8543
|
+
orgx_action: {
|
|
8544
|
+
kind: "auto_classify",
|
|
8545
|
+
description: "OrgX will classify ownership, handoff, and verification evidence from the newly connected source.",
|
|
8546
|
+
triggers_when: "on_next_audit_run"
|
|
8547
|
+
},
|
|
8548
|
+
expected_score_lift: Math.min(18, input.coverage.missing.length * 4),
|
|
8549
|
+
expected_findings_unlocked: Math.max(1, input.coverage.missing.length * 2),
|
|
8550
|
+
preview_findings: [
|
|
8551
|
+
{
|
|
8552
|
+
title_redacted: "Owner-visible handoff replaces inferred blocker state",
|
|
8553
|
+
evidence_count_anticipated: 2,
|
|
8554
|
+
confidence_anticipated: 0.8,
|
|
8555
|
+
derivation_basis: "rule_based"
|
|
8556
|
+
}
|
|
8557
|
+
],
|
|
8558
|
+
preconditions: ["authorize_source"],
|
|
8559
|
+
estimated_seconds_to_complete: 75
|
|
8560
|
+
});
|
|
8561
|
+
}
|
|
8562
|
+
return entries.slice(0, 6);
|
|
8563
|
+
}
|
|
8564
|
+
function buildRepairPlan(input) {
|
|
8565
|
+
const firstFamily = input.families.find((family) => family.public_surface) ?? input.families[0];
|
|
8566
|
+
const firstCounterfactual = input.counterfactuals[0];
|
|
8567
|
+
const actions = [];
|
|
8568
|
+
if (firstFamily || firstCounterfactual) {
|
|
8569
|
+
const closes = firstFamily?.loop_ids ?? (firstCounterfactual ? [firstCounterfactual.triggered_by_event_id] : []);
|
|
8570
|
+
actions.push({
|
|
8571
|
+
id: "repair:promote-loop",
|
|
8572
|
+
type: firstFamily?.orgx_repair.named_orgx_capability.includes("assign") ? "assign_owner" : "promote_decision",
|
|
8573
|
+
title: "Repair the top recurring work loop",
|
|
8574
|
+
closes_loop_ids: closes,
|
|
8575
|
+
expected_gain: "execution continuity",
|
|
8576
|
+
expected_hours_recovered: firstFamily?.orgx_repair.expected_hours_recovered ?? 0.8,
|
|
8577
|
+
expected_dollars_recovered: firstFamily?.orgx_repair.expected_dollars_recovered ?? 650,
|
|
8578
|
+
required_inputs: ["confirmed evidence", "owner or fallback owner"],
|
|
8579
|
+
confidence: firstCounterfactual?.realism_score ?? 0.72,
|
|
8580
|
+
time_to_value: "10 minutes after claim",
|
|
8581
|
+
preview_tree: [
|
|
8582
|
+
{ kind: "workstream", title: "Repair recurring AI work loop" },
|
|
8583
|
+
{ kind: "milestone", title: "Promote decision or blocker to durable record" },
|
|
8584
|
+
{ kind: "task", title: "Attach source evidence and owner-visible next step" }
|
|
8585
|
+
]
|
|
8586
|
+
});
|
|
8587
|
+
}
|
|
8588
|
+
const sourceGap = input.whyNot100.find((entry) => entry.dimension === "source_coverage" || entry.dimension === "cross_source_confluence");
|
|
8589
|
+
if (sourceGap) {
|
|
8590
|
+
actions.push({
|
|
8591
|
+
id: "repair:connect-source",
|
|
8592
|
+
type: "connect_source",
|
|
8593
|
+
title: sourceGap.user_action?.copy_for_button ?? "Connect missing proof source",
|
|
8594
|
+
closes_loop_ids: [],
|
|
8595
|
+
expected_gain: "source confidence",
|
|
8596
|
+
expected_hours_recovered: 0,
|
|
8597
|
+
expected_dollars_recovered: 0,
|
|
8598
|
+
required_inputs: [sourceGap.user_action?.target ?? "source authorization"],
|
|
8599
|
+
confidence: 0.78,
|
|
8600
|
+
time_to_value: `${Math.ceil(sourceGap.estimated_seconds_to_complete / 60)} minutes`,
|
|
8601
|
+
preview_tree: [
|
|
8602
|
+
{ kind: "workstream", title: "Connect source evidence" },
|
|
8603
|
+
{ kind: "task", title: "Correlate session events to proof, owner, and handoff signals" }
|
|
8604
|
+
]
|
|
8605
|
+
});
|
|
8606
|
+
}
|
|
8607
|
+
actions.push({
|
|
8608
|
+
id: "repair:launch-profile",
|
|
8609
|
+
type: "convert_to_initiative",
|
|
8610
|
+
title: "Launch from this investigation",
|
|
8611
|
+
closes_loop_ids: firstFamily?.loop_ids ?? [],
|
|
8612
|
+
expected_gain: "initiative readiness",
|
|
8613
|
+
expected_hours_recovered: firstFamily?.orgx_repair.expected_hours_recovered ?? 0,
|
|
8614
|
+
expected_dollars_recovered: firstFamily?.orgx_repair.expected_dollars_recovered ?? 0,
|
|
8615
|
+
required_inputs: ["claim profile", "approve repair plan"],
|
|
8616
|
+
confidence: 0.7,
|
|
8617
|
+
time_to_value: "1 click after claim",
|
|
8618
|
+
preview_tree: [
|
|
8619
|
+
{ kind: "workstream", title: "Investigation to durable OrgX initiative" },
|
|
8620
|
+
{ kind: "milestone", title: "Convert loop family into assigned work" },
|
|
8621
|
+
{ kind: "task", title: "Close the highest-confidence counterfactual gap" }
|
|
8622
|
+
]
|
|
8623
|
+
});
|
|
8624
|
+
return actions.slice(0, 3);
|
|
8625
|
+
}
|
|
8626
|
+
function buildMirror(input) {
|
|
8627
|
+
const topFamily = input.families.find((family) => family.public_surface);
|
|
8628
|
+
const topLoop = input.loops.find((loop) => loop.public_surface) ?? input.loops[0];
|
|
8629
|
+
const sourceCount = input.corpus.sources.filter((source) => source.status === "connected" || source.status === "partial").length;
|
|
8630
|
+
const dropped = input.loops.filter((loop) => !loop.survived_critic).length;
|
|
8631
|
+
const terminal = topFamily?.shared_terminal_state ?? "ongoing";
|
|
8632
|
+
const terminalPhrase = `${articleFor(terminal)} ${terminal}`;
|
|
8633
|
+
const text2 = [
|
|
8634
|
+
`You have AI-assisted work spread across ${sourceCount} connected or partial source${sourceCount === 1 ? "" : "s"}, but the execution record is still incomplete.`,
|
|
8635
|
+
topFamily ? `The clearest repeated loop is ${topFamily.semantic_centroid}: ${topFamily.appearances} appearance${topFamily.appearances === 1 ? "" : "s"} with ${terminalPhrase} state.` : topLoop ? `The clearest work loop is "${topLoop.origin.intent}", but it still needs more chronology before OrgX should call it recurring.` : "The corpus did not produce a verified work loop yet.",
|
|
8636
|
+
input.counterfactuals[0] ? `OrgX can point to the exact event where it would have called ${input.counterfactuals[0].orgx_capability.tool} and what entity it would have created.` : "Counterfactual repair is waiting on a higher-confidence loop with resolved citations.",
|
|
8637
|
+
`The current estimate is ${input.impact.time_saved_hours_per_week} recoverable hours/week, grounded in ${input.impact.basis[0] ?? "resolved work-loop evidence"}.`,
|
|
8638
|
+
dropped > 0 ? `${dropped} weak signal${dropped === 1 ? " was" : "s were"} kept out of the public readout because the citations or critic score did not clear the bar.` : "Every surfaced loop cleared citation verification and the critic floor."
|
|
8639
|
+
].join(" ");
|
|
8640
|
+
return {
|
|
8641
|
+
text: text2,
|
|
8642
|
+
evidence_pills: [
|
|
8643
|
+
{
|
|
8644
|
+
phrase: `${input.loops.length} work loop${input.loops.length === 1 ? "" : "s"}`,
|
|
8645
|
+
cites: input.loops.flatMap((loop) => loop.cites).slice(0, 12)
|
|
8646
|
+
},
|
|
8647
|
+
{
|
|
8648
|
+
phrase: `${sourceCount} source${sourceCount === 1 ? "" : "s"}`,
|
|
8649
|
+
cites: []
|
|
8650
|
+
},
|
|
8651
|
+
{
|
|
8652
|
+
phrase: `${input.counterfactuals.length} earned counterfactual${input.counterfactuals.length === 1 ? "" : "s"}`,
|
|
8653
|
+
cites: input.counterfactuals.map((item) => item.triggered_by_event_id)
|
|
8654
|
+
}
|
|
8655
|
+
]
|
|
8656
|
+
};
|
|
8657
|
+
}
|
|
8658
|
+
function buildWorkGraphInvestigation(input) {
|
|
8659
|
+
const auditId = `wgi_${hash2([input.fingerprint, input.generatedAt], 24)}`;
|
|
8660
|
+
const rawEvents = buildRawEvents(input);
|
|
8661
|
+
const corpus = buildCorpusManifest({
|
|
8662
|
+
auditId,
|
|
8663
|
+
auditMethod: input.auditMethod,
|
|
8664
|
+
clientExtractions: input.clientExtractions,
|
|
8665
|
+
coverage: input.coverage,
|
|
8666
|
+
events: rawEvents,
|
|
8667
|
+
generatedAt: input.generatedAt
|
|
8668
|
+
});
|
|
8669
|
+
const loops = buildWorkLoops({ events: rawEvents, findings: input.findings, trails: input.trails });
|
|
8670
|
+
const families = buildLoopFamilies(loops, rawEvents, input.impact);
|
|
8671
|
+
const usage = buildUsageCatalogue(rawEvents, input.findings);
|
|
8672
|
+
const sourceConfidence = buildSourceConfidence(corpus);
|
|
8673
|
+
const loopCritics = loops.map(criticForLoop);
|
|
8674
|
+
const familyByLoop = /* @__PURE__ */ new Map();
|
|
8675
|
+
for (const family of families) {
|
|
8676
|
+
for (const loopId of family.loop_ids) familyByLoop.set(loopId, family);
|
|
8677
|
+
}
|
|
8678
|
+
const verifiedLoops = loops.filter((loop) => {
|
|
8679
|
+
const critic = loopCritics.find((entry) => entry.finding_id === loop.loop_id);
|
|
8680
|
+
return loop.public_surface && critic?.survived;
|
|
8681
|
+
});
|
|
8682
|
+
const counterfactuals = verifiedLoops.map((loop) => counterfactualForLoop(loop, familyByLoop.get(loop.loop_id))).filter((item) => Boolean(item)).slice(0, 8);
|
|
8683
|
+
const verificationItems = [
|
|
8684
|
+
...loops.map((loop) => ({ id: loop.loop_id, cites: loop.cites })),
|
|
8685
|
+
...counterfactuals.map((item) => ({
|
|
8686
|
+
id: `counterfactual:${item.resulting_entity.inferred_id}`,
|
|
8687
|
+
cites: [item.triggered_by_event_id]
|
|
8688
|
+
}))
|
|
8689
|
+
];
|
|
8690
|
+
const verification = verifyItems(verificationItems, rawEvents);
|
|
8691
|
+
const whyNot100 = buildWhyNot100({
|
|
8692
|
+
corpus,
|
|
8693
|
+
coverage: input.coverage,
|
|
8694
|
+
loops,
|
|
8695
|
+
families,
|
|
8696
|
+
impact: input.impact
|
|
8697
|
+
});
|
|
8698
|
+
const repairPlan = buildRepairPlan({ counterfactuals, families, whyNot100 });
|
|
8699
|
+
const mirror = buildMirror({
|
|
8700
|
+
loops,
|
|
8701
|
+
families,
|
|
8702
|
+
counterfactuals,
|
|
8703
|
+
corpus,
|
|
8704
|
+
impact: input.impact
|
|
8705
|
+
});
|
|
8706
|
+
const eventBySource = countBy(rawEvents.map((event) => event.source_id)).map(({ key, count }) => ({
|
|
8707
|
+
source_id: key,
|
|
8708
|
+
count
|
|
8709
|
+
}));
|
|
8710
|
+
const eventByKind = countBy(rawEvents.map((event) => event.kind)).map(({ key, count }) => ({
|
|
8711
|
+
kind: key,
|
|
8712
|
+
count
|
|
8713
|
+
}));
|
|
8714
|
+
return {
|
|
8715
|
+
schema_version: WORK_GRAPH_INVESTIGATION_SCHEMA_VERSION,
|
|
8716
|
+
audit_id: auditId,
|
|
8717
|
+
fingerprint: input.fingerprint,
|
|
8718
|
+
generated_at: input.generatedAt,
|
|
8719
|
+
corpus_manifest: corpus,
|
|
8720
|
+
raw_events_summary: {
|
|
8721
|
+
count: rawEvents.length,
|
|
8722
|
+
by_source: eventBySource,
|
|
8723
|
+
by_kind: eventByKind,
|
|
8724
|
+
earliest: corpus.totals.earliest_event,
|
|
8725
|
+
latest: corpus.totals.latest_event
|
|
8726
|
+
},
|
|
8727
|
+
raw_events: rawEvents.slice(0, 500),
|
|
8728
|
+
work_loops: loops,
|
|
8729
|
+
loop_families: families,
|
|
8730
|
+
usage_catalogue: usage,
|
|
8731
|
+
source_confidence: sourceConfidence,
|
|
8732
|
+
why_not_100: whyNot100,
|
|
8733
|
+
counterfactuals,
|
|
8734
|
+
critic_log: [
|
|
8735
|
+
...loopCritics,
|
|
8736
|
+
...families.map((family) => ({
|
|
8737
|
+
finding_id: family.family_id,
|
|
8738
|
+
attack_vectors_attempted: [
|
|
8739
|
+
"is_this_actually_recurring",
|
|
8740
|
+
"is_the_recurrence_just_text_overlap",
|
|
8741
|
+
"are_these_events_actually_related"
|
|
8742
|
+
],
|
|
8743
|
+
weakest_claim: family.appearances < 3 ? "The family has fewer than 3 appearances and should not be public as recurrence." : "The family still depends on source normalization and may need human correction.",
|
|
8744
|
+
most_plausible_alternative: family.appearances < 3 ? "This is a single signal or thin pair, not a real recurring loop." : "The loops may be related staged work rather than repeated unresolved work.",
|
|
8745
|
+
survival_score: family.survived_critic ? 0.72 : 0.52,
|
|
8746
|
+
survived: family.survived_critic,
|
|
8747
|
+
demoted_to: family.public_surface ? "public" : "weak_signal"
|
|
8748
|
+
}))
|
|
8749
|
+
],
|
|
8750
|
+
verification_log: verification,
|
|
8751
|
+
mirror_paragraph: mirror,
|
|
8752
|
+
repair_plan: repairPlan,
|
|
8753
|
+
impact_projection: {
|
|
8754
|
+
hours_recoverable_per_week: {
|
|
8755
|
+
value: input.impact.time_saved_hours_per_week,
|
|
8756
|
+
basis: input.impact.basis.join(" "),
|
|
8757
|
+
confidence: input.impact.confidence
|
|
8758
|
+
},
|
|
8759
|
+
dollars_recoverable_per_month: {
|
|
8760
|
+
value: input.impact.estimated_monthly_value_usd,
|
|
8761
|
+
basis: "hours_recoverable_per_week * 4.33 weeks * blended operator hourly value",
|
|
8762
|
+
confidence: input.impact.confidence
|
|
8763
|
+
},
|
|
8764
|
+
acceleration_pct: {
|
|
8765
|
+
value: input.impact.acceleration_percent,
|
|
8766
|
+
basis: "directional execution lift from repairable loops, source coverage, and automation potential",
|
|
8767
|
+
confidence: input.impact.confidence
|
|
8768
|
+
}
|
|
8769
|
+
},
|
|
8770
|
+
redaction_log: [
|
|
8771
|
+
{ kind: "raw_transcripts_excluded", count: 1 },
|
|
8772
|
+
{ kind: "redacted_event_payloads", count: rawEvents.length }
|
|
8773
|
+
],
|
|
8774
|
+
raw_transcripts_excluded: true,
|
|
8775
|
+
claimable: loops.some((loop) => loop.public_surface)
|
|
8776
|
+
};
|
|
8777
|
+
}
|
|
8778
|
+
|
|
8779
|
+
// src/lib/work-graph.ts
|
|
8780
|
+
var WORK_GRAPH_SCHEMA_VERSION = "2.0.0";
|
|
7003
8781
|
var WORK_GRAPH_FINGERPRINT_VERSION = "wgf_v1";
|
|
7004
|
-
var WORK_GRAPH_EXTRACTION_SCHEMA_VERSION = "
|
|
8782
|
+
var WORK_GRAPH_EXTRACTION_SCHEMA_VERSION = "2.0.0.investigation";
|
|
7005
8783
|
var WORK_GRAPH_FINDING_TYPES = [
|
|
7006
8784
|
"action",
|
|
7007
8785
|
"decision",
|
|
@@ -7019,7 +8797,7 @@ function clampScore2(value) {
|
|
|
7019
8797
|
return Math.max(0, Math.min(100, Math.round(value)));
|
|
7020
8798
|
}
|
|
7021
8799
|
function hashJson(value) {
|
|
7022
|
-
return
|
|
8800
|
+
return createHash6("sha256").update(JSON.stringify(value)).digest("hex");
|
|
7023
8801
|
}
|
|
7024
8802
|
function normalizeFingerprintText(value) {
|
|
7025
8803
|
return value.toLowerCase().replace(/https?:\/\/\S+/g, "url").replace(/[0-9a-f]{12,}/g, "hash").replace(/\b[0-9a-f]{8}-[0-9a-f-]{27,}\b/g, "uuid").replace(/\s+/g, " ").trim().slice(0, 600);
|
|
@@ -7040,6 +8818,10 @@ function sourceClientForImport(source) {
|
|
|
7040
8818
|
if (raw.includes("claude-code")) return "claude-code";
|
|
7041
8819
|
if (raw.includes("claude")) return "claude";
|
|
7042
8820
|
if (raw.includes("cursor")) return "cursor";
|
|
8821
|
+
if (raw.includes("opencode")) return "opencode";
|
|
8822
|
+
if (raw.includes("goose")) return "goose";
|
|
8823
|
+
if (raw.includes("opencode")) return "opencode";
|
|
8824
|
+
if (raw.includes("goose")) return "goose";
|
|
7043
8825
|
if (raw.includes("openclaw")) return "openclaw";
|
|
7044
8826
|
if (raw.includes("slack")) return "slack";
|
|
7045
8827
|
if (raw.includes("github")) return "github";
|
|
@@ -7056,12 +8838,14 @@ function sourceClientForImport(source) {
|
|
|
7056
8838
|
function normalizeSourceClient(value) {
|
|
7057
8839
|
if (typeof value !== "string") return "unknown";
|
|
7058
8840
|
const normalized = value.trim().toLowerCase();
|
|
7059
|
-
if (normalized === "codex" || normalized === "claude" || normalized === "claude-code" || normalized === "cursor" || normalized === "openclaw" || normalized === "slack" || normalized === "mcp" || normalized === "github" || normalized === "linear" || normalized === "gmail" || normalized === "calendar" || normalized === "notion" || normalized === "docs" || normalized === "manual" || normalized === "wizard" || normalized === "api") {
|
|
8841
|
+
if (normalized === "codex" || normalized === "claude" || normalized === "claude-code" || normalized === "cursor" || normalized === "opencode" || normalized === "goose" || normalized === "openclaw" || normalized === "slack" || normalized === "mcp" || normalized === "orgx_runtime_hook" || normalized === "github" || normalized === "linear" || normalized === "gmail" || normalized === "calendar" || normalized === "notion" || normalized === "docs" || normalized === "manual" || normalized === "wizard" || normalized === "api") {
|
|
7060
8842
|
return normalized;
|
|
7061
8843
|
}
|
|
7062
8844
|
if (normalized.includes("claude")) return "claude";
|
|
7063
8845
|
if (normalized.includes("codex")) return "codex";
|
|
7064
8846
|
if (normalized.includes("cursor")) return "cursor";
|
|
8847
|
+
if (normalized.includes("opencode")) return "opencode";
|
|
8848
|
+
if (normalized.includes("goose")) return "goose";
|
|
7065
8849
|
if (normalized.includes("slack")) return "slack";
|
|
7066
8850
|
if (normalized.includes("github")) return "github";
|
|
7067
8851
|
if (normalized.includes("linear")) return "linear";
|
|
@@ -7070,6 +8854,7 @@ function normalizeSourceClient(value) {
|
|
|
7070
8854
|
if (normalized.includes("notion")) return "notion";
|
|
7071
8855
|
if (normalized.includes("doc")) return "docs";
|
|
7072
8856
|
if (normalized.includes("mcp")) return "mcp";
|
|
8857
|
+
if (normalized.includes("runtime") || normalized.includes("hook")) return "orgx_runtime_hook";
|
|
7073
8858
|
return "unknown";
|
|
7074
8859
|
}
|
|
7075
8860
|
function sourceClientFromText(value) {
|
|
@@ -7077,6 +8862,8 @@ function sourceClientFromText(value) {
|
|
|
7077
8862
|
if (/\bclaude(?:[- ]code)?\b|\.claude\/projects|claude:/.test(normalized)) return "claude";
|
|
7078
8863
|
if (/\bcodex\b|\.codex\/sessions|rollout-/.test(normalized)) return "codex";
|
|
7079
8864
|
if (/\bcursor\b/.test(normalized)) return "cursor";
|
|
8865
|
+
if (/\bopencode\b/.test(normalized)) return "opencode";
|
|
8866
|
+
if (/\bgoose\b/.test(normalized)) return "goose";
|
|
7080
8867
|
if (/\bopenclaw\b/.test(normalized)) return "openclaw";
|
|
7081
8868
|
if (/\bslack\b/.test(normalized)) return "slack";
|
|
7082
8869
|
if (/\bgithub\b|\bgit:|pull request|commit\b/.test(normalized)) return "github";
|
|
@@ -7219,7 +9006,7 @@ function buildWorkGraphExtractionProtocol() {
|
|
|
7219
9006
|
required_output: {
|
|
7220
9007
|
schema_version: WORK_GRAPH_EXTRACTION_SCHEMA_VERSION,
|
|
7221
9008
|
extraction_id: "stable id for this extraction run",
|
|
7222
|
-
source_client: "codex | claude | claude-code | cursor | openclaw | slack | mcp | github | linear | gmail | calendar | notion | docs | manual | api | unknown",
|
|
9009
|
+
source_client: "codex | claude | claude-code | cursor | opencode | goose | openclaw | slack | mcp | github | linear | gmail | calendar | notion | docs | manual | api | unknown",
|
|
7223
9010
|
source_label: "human-readable source label",
|
|
7224
9011
|
searched_sources: ["session/log/source group names searched"],
|
|
7225
9012
|
search_queries: [
|
|
@@ -7348,8 +9135,12 @@ ${extractionText}`.toLowerCase();
|
|
|
7348
9135
|
...normalizedConnectedSources,
|
|
7349
9136
|
...sourceClients.includes("codex") ? ["Codex sessions"] : [],
|
|
7350
9137
|
...sourceClients.includes("claude") || sourceClients.includes("claude-code") ? ["Claude Code sessions"] : [],
|
|
9138
|
+
...sourceClients.includes("cursor") ? ["Cursor workspace evidence"] : [],
|
|
9139
|
+
...sourceClients.includes("opencode") ? ["OpenCode sessions"] : [],
|
|
9140
|
+
...sourceClients.includes("goose") ? ["goose sessions"] : [],
|
|
7351
9141
|
...sourceClients.includes("github") ? ["Git/GitHub proof"] : [],
|
|
7352
9142
|
...sourceClients.includes("mcp") ? ["MCP tool telemetry"] : [],
|
|
9143
|
+
...sourceClients.includes("orgx_runtime_hook") ? ["OrgX runtime hook replay"] : [],
|
|
7353
9144
|
...sourceClients.includes("slack") ? ["Slack coordination"] : []
|
|
7354
9145
|
];
|
|
7355
9146
|
const inferredMissing = [
|
|
@@ -7369,7 +9160,7 @@ ${extractionText}`.toLowerCase();
|
|
|
7369
9160
|
});
|
|
7370
9161
|
const partialCount = manifests.filter((manifest) => manifest.status === "partial").length;
|
|
7371
9162
|
const coverageScore = clampScore2(
|
|
7372
|
-
28 + Math.min(34, connected.length * 7) + Math.min(18, findings.length * 2) + (sourceClients.includes("codex") ? 6 : 0) + (sourceClients.includes("claude") || sourceClients.includes("claude-code") ? 6 : 0) - missing.length * 7 - partialCount * 3
|
|
9163
|
+
28 + Math.min(34, connected.length * 7) + Math.min(18, findings.length * 2) + (sourceClients.includes("codex") ? 6 : 0) + (sourceClients.includes("claude") || sourceClients.includes("claude-code") ? 6 : 0) + (sourceClients.includes("cursor") ? 4 : 0) + (sourceClients.includes("opencode") ? 4 : 0) + (sourceClients.includes("goose") ? 4 : 0) - missing.length * 7 - partialCount * 3
|
|
7373
9164
|
);
|
|
7374
9165
|
return {
|
|
7375
9166
|
connected,
|
|
@@ -7466,7 +9257,7 @@ function buildSourceCoverageManifests(input) {
|
|
|
7466
9257
|
});
|
|
7467
9258
|
}
|
|
7468
9259
|
function canonicalSourceManifestLabel(manifest) {
|
|
7469
|
-
if (manifest.source_client === "codex" || manifest.source_client === "claude" || manifest.source_client === "claude-code") {
|
|
9260
|
+
if (manifest.source_client === "codex" || manifest.source_client === "claude" || manifest.source_client === "claude-code" || manifest.source_client === "cursor" || manifest.source_client === "opencode" || manifest.source_client === "goose") {
|
|
7470
9261
|
return labelForSourceClient(manifest.source_client);
|
|
7471
9262
|
}
|
|
7472
9263
|
if (manifest.source_client === "github" || manifest.source_client === "slack" || manifest.source_client === "mcp") {
|
|
@@ -7481,8 +9272,16 @@ function labelForSourceClient(sourceClient) {
|
|
|
7481
9272
|
case "claude":
|
|
7482
9273
|
case "claude-code":
|
|
7483
9274
|
return "Claude Code sessions";
|
|
9275
|
+
case "cursor":
|
|
9276
|
+
return "Cursor workspace evidence";
|
|
9277
|
+
case "opencode":
|
|
9278
|
+
return "OpenCode sessions";
|
|
9279
|
+
case "goose":
|
|
9280
|
+
return "goose sessions";
|
|
7484
9281
|
case "mcp":
|
|
7485
9282
|
return "MCP tool telemetry";
|
|
9283
|
+
case "orgx_runtime_hook":
|
|
9284
|
+
return "OrgX runtime hook replay";
|
|
7486
9285
|
case "github":
|
|
7487
9286
|
return "Git/GitHub proof";
|
|
7488
9287
|
case "slack":
|
|
@@ -7548,7 +9347,9 @@ function numberFromImportMetadata(source, key) {
|
|
|
7548
9347
|
}
|
|
7549
9348
|
function buildAuditMethod(input) {
|
|
7550
9349
|
const extractionSummaries = summarizeClientExtractions(input.clientExtractions);
|
|
7551
|
-
const nativePacks = extractionSummaries.filter(
|
|
9350
|
+
const nativePacks = extractionSummaries.filter(
|
|
9351
|
+
(summary) => summary.source_client === "codex" || summary.source_client === "claude" || summary.source_client === "claude-code" || summary.source_client === "cursor" || summary.source_client === "opencode" || summary.source_client === "goose"
|
|
9352
|
+
).map((summary) => ({
|
|
7552
9353
|
source_client: summary.source_client,
|
|
7553
9354
|
source_label: summary.source_label,
|
|
7554
9355
|
searched_session_count: summary.searched_session_count,
|
|
@@ -8932,7 +10733,7 @@ function buildAgentNodes(findings) {
|
|
|
8932
10733
|
);
|
|
8933
10734
|
const sourceAgents = sortedUnique(
|
|
8934
10735
|
findings.map(
|
|
8935
|
-
(finding) => ["codex", "claude", "claude-code", "cursor", "openclaw"].includes(finding.source_client) ? finding.source_client : ""
|
|
10736
|
+
(finding) => ["codex", "claude", "claude-code", "cursor", "opencode", "goose", "openclaw"].includes(finding.source_client) ? finding.source_client : ""
|
|
8936
10737
|
).filter(Boolean)
|
|
8937
10738
|
);
|
|
8938
10739
|
return [...actorIds, ...sourceAgents].slice(0, 20).map((agent) => ({
|
|
@@ -9250,6 +11051,20 @@ function buildSessionReconciliationReport(input) {
|
|
|
9250
11051
|
};
|
|
9251
11052
|
const reportHash = hashJson(reportSeed);
|
|
9252
11053
|
const sessionId = input.sessionId ?? `work-graph-${reportHash.slice(0, 16)}`;
|
|
11054
|
+
const investigation = buildWorkGraphInvestigation({
|
|
11055
|
+
auditMethod,
|
|
11056
|
+
clientExtractions: clientExtractionSummaries,
|
|
11057
|
+
coverage,
|
|
11058
|
+
events,
|
|
11059
|
+
findings: allFindings,
|
|
11060
|
+
fingerprint: fingerprint.fingerprint,
|
|
11061
|
+
generatedAt,
|
|
11062
|
+
impact: impactProjection,
|
|
11063
|
+
patterns: recurringPatterns,
|
|
11064
|
+
...input.investigationRawEvents ? { rawEvents: input.investigationRawEvents } : {},
|
|
11065
|
+
recommendations,
|
|
11066
|
+
trails
|
|
11067
|
+
});
|
|
9253
11068
|
return {
|
|
9254
11069
|
schema_version: WORK_GRAPH_SCHEMA_VERSION,
|
|
9255
11070
|
report_id: reportHash.slice(0, 24),
|
|
@@ -9281,6 +11096,7 @@ function buildSessionReconciliationReport(input) {
|
|
|
9281
11096
|
opportunity_score: opportunityScore,
|
|
9282
11097
|
execution_quality: executionQuality,
|
|
9283
11098
|
impact_projection: impactProjection,
|
|
11099
|
+
investigation,
|
|
9284
11100
|
initiative_kickoffs: initiativeKickoffs,
|
|
9285
11101
|
redaction_level: "summary_only",
|
|
9286
11102
|
raw_transcripts_sent: false
|
|
@@ -9328,7 +11144,7 @@ function renderWorkGraphExtractionProtocolMarkdown(protocol = buildWorkGraphExtr
|
|
|
9328
11144
|
}
|
|
9329
11145
|
function renderWorkGraphMarkdown(report) {
|
|
9330
11146
|
const lines = [];
|
|
9331
|
-
lines.push("# OrgX
|
|
11147
|
+
lines.push("# OrgX Investigation Engine");
|
|
9332
11148
|
lines.push("");
|
|
9333
11149
|
lines.push(`Generated: ${report.generated_at}`);
|
|
9334
11150
|
lines.push(`Workspace: ${report.workspace.name} (${report.workspace.id})`);
|
|
@@ -9359,6 +11175,44 @@ function renderWorkGraphMarkdown(report) {
|
|
|
9359
11175
|
lines.push(`- ${note}`);
|
|
9360
11176
|
}
|
|
9361
11177
|
lines.push("");
|
|
11178
|
+
lines.push("## Investigation v2");
|
|
11179
|
+
lines.push("");
|
|
11180
|
+
lines.push(`Schema: ${report.investigation.schema_version}`);
|
|
11181
|
+
lines.push(`Audit ID: ${report.investigation.audit_id}`);
|
|
11182
|
+
lines.push(`Raw events normalized: ${report.investigation.raw_events_summary.count}`);
|
|
11183
|
+
lines.push(`Work loops: ${report.investigation.work_loops.length}`);
|
|
11184
|
+
lines.push(`Recurring loop families: ${report.investigation.loop_families.filter((family) => family.public_surface).length}`);
|
|
11185
|
+
const passedVerification = report.investigation.verification_log.filter((entry) => entry.passed).length;
|
|
11186
|
+
const droppedVerification = report.investigation.verification_log.length - passedVerification;
|
|
11187
|
+
lines.push(`Verification: ${passedVerification} passed, ${droppedVerification} dropped as unverifiable`);
|
|
11188
|
+
lines.push(`Earned counterfactuals: ${report.investigation.counterfactuals.length}`);
|
|
11189
|
+
lines.push("");
|
|
11190
|
+
lines.push("Mirror:");
|
|
11191
|
+
lines.push(report.investigation.mirror_paragraph.text);
|
|
11192
|
+
lines.push("");
|
|
11193
|
+
if (report.investigation.counterfactuals[0]) {
|
|
11194
|
+
const counterfactual = report.investigation.counterfactuals[0];
|
|
11195
|
+
lines.push("Top earned counterfactual:");
|
|
11196
|
+
lines.push(`- Trigger: ${counterfactual.triggered_by_event_id} at ${counterfactual.triggered_at}`);
|
|
11197
|
+
lines.push(`- Actual: ${counterfactual.actual_outcome}`);
|
|
11198
|
+
lines.push(`- OrgX would call: ${counterfactual.orgx_capability.tool}`);
|
|
11199
|
+
lines.push(`- Outcome: ${counterfactual.orgx_outcome}`);
|
|
11200
|
+
lines.push("");
|
|
11201
|
+
}
|
|
11202
|
+
if (report.investigation.why_not_100.length > 0) {
|
|
11203
|
+
lines.push("What improves it:");
|
|
11204
|
+
for (const gap of report.investigation.why_not_100) {
|
|
11205
|
+
lines.push(`- ${gap.precise_gap} ${gap.user_action ? `Action: ${gap.user_action.copy_for_button} (${gap.user_action.ux_route}).` : ""}`);
|
|
11206
|
+
}
|
|
11207
|
+
lines.push("");
|
|
11208
|
+
}
|
|
11209
|
+
lines.push("Source capability matrix:");
|
|
11210
|
+
for (const source of report.investigation.source_confidence.columns) {
|
|
11211
|
+
const chronology = source.capability_cells.chronology;
|
|
11212
|
+
const verification = source.capability_cells.verification;
|
|
11213
|
+
lines.push(`- ${source.brand.name}: ${source.status}, chronology ${Math.round(chronology.achieved * 100)}%/${chronology.ceiling}, verification ${Math.round(verification.achieved * 100)}%/${verification.ceiling}`);
|
|
11214
|
+
}
|
|
11215
|
+
lines.push("");
|
|
9362
11216
|
lines.push("## Client Extractions");
|
|
9363
11217
|
lines.push("");
|
|
9364
11218
|
if (report.client_extractions.length === 0) {
|
|
@@ -9538,8 +11392,46 @@ function renderWorkGraphMarkdown(report) {
|
|
|
9538
11392
|
}
|
|
9539
11393
|
|
|
9540
11394
|
// src/lib/work-graph-publish.ts
|
|
11395
|
+
import { createHash as createHash7, randomUUID as randomUUID2 } from "crypto";
|
|
9541
11396
|
import { gzipSync } from "zlib";
|
|
9542
|
-
var WORK_GRAPH_REPORT_GZIP_THRESHOLD_BYTES =
|
|
11397
|
+
var WORK_GRAPH_REPORT_GZIP_THRESHOLD_BYTES = 4e6;
|
|
11398
|
+
var WORK_GRAPH_REPORT_CHUNK_UPLOAD_THRESHOLD_BYTES = 5e5;
|
|
11399
|
+
var WORK_GRAPH_REPORT_CHUNK_CHARS = 15e4;
|
|
11400
|
+
function hashText(value) {
|
|
11401
|
+
return createHash7("sha256").update(value).digest("hex");
|
|
11402
|
+
}
|
|
11403
|
+
function buildWorkGraphReportPostPayload(report, options = {}) {
|
|
11404
|
+
return {
|
|
11405
|
+
report,
|
|
11406
|
+
...options.workspaceId ? { workspace_id: options.workspaceId } : {},
|
|
11407
|
+
...options.initiativeId ? { initiative_id: options.initiativeId } : {},
|
|
11408
|
+
...options.entityType ? { entity_type: options.entityType } : {},
|
|
11409
|
+
...options.entityId ? { entity_id: options.entityId } : {},
|
|
11410
|
+
...options.artifactUrl ? { artifact_url: options.artifactUrl } : {},
|
|
11411
|
+
attach_artifact: Boolean(options.attachArtifact),
|
|
11412
|
+
public_share: Boolean(options.publicShare)
|
|
11413
|
+
};
|
|
11414
|
+
}
|
|
11415
|
+
function createWorkGraphReportUploadChunks(payload, options = {}) {
|
|
11416
|
+
const chunkChars = options.chunkChars ?? WORK_GRAPH_REPORT_CHUNK_CHARS;
|
|
11417
|
+
const json = JSON.stringify(payload);
|
|
11418
|
+
const chunks = [];
|
|
11419
|
+
for (let offset = 0; offset < json.length; offset += chunkChars) {
|
|
11420
|
+
const chunkText = json.slice(offset, offset + chunkChars);
|
|
11421
|
+
chunks.push({
|
|
11422
|
+
chunkIndex: chunks.length,
|
|
11423
|
+
chunkText,
|
|
11424
|
+
chunkSha256: hashText(chunkText)
|
|
11425
|
+
});
|
|
11426
|
+
}
|
|
11427
|
+
return {
|
|
11428
|
+
uploadId: options.uploadId ?? `wgrup_${randomUUID2()}`,
|
|
11429
|
+
reportSha256: hashText(json),
|
|
11430
|
+
totalBytes: Buffer.byteLength(json),
|
|
11431
|
+
json,
|
|
11432
|
+
chunks
|
|
11433
|
+
};
|
|
11434
|
+
}
|
|
9543
11435
|
function encodeWorkGraphReportPostBody(payload, thresholdBytes = WORK_GRAPH_REPORT_GZIP_THRESHOLD_BYTES) {
|
|
9544
11436
|
const json = JSON.stringify(payload);
|
|
9545
11437
|
const byteLength = Buffer.byteLength(json);
|
|
@@ -9570,22 +11462,106 @@ async function parseResponse(response) {
|
|
|
9570
11462
|
return text2;
|
|
9571
11463
|
}
|
|
9572
11464
|
}
|
|
11465
|
+
async function postWorkGraphReportJson({
|
|
11466
|
+
auth,
|
|
11467
|
+
body,
|
|
11468
|
+
signal,
|
|
11469
|
+
url
|
|
11470
|
+
}) {
|
|
11471
|
+
const requestInit = {
|
|
11472
|
+
method: "POST",
|
|
11473
|
+
headers: {
|
|
11474
|
+
Authorization: `Bearer ${auth.apiKey}`,
|
|
11475
|
+
"Content-Type": "application/json"
|
|
11476
|
+
},
|
|
11477
|
+
body: JSON.stringify(body)
|
|
11478
|
+
};
|
|
11479
|
+
if (signal) requestInit.signal = signal;
|
|
11480
|
+
return fetch(url, requestInit);
|
|
11481
|
+
}
|
|
11482
|
+
async function publishWorkGraphReportInChunks({
|
|
11483
|
+
auth,
|
|
11484
|
+
payload,
|
|
11485
|
+
signal,
|
|
11486
|
+
url
|
|
11487
|
+
}) {
|
|
11488
|
+
const upload = createWorkGraphReportUploadChunks(payload);
|
|
11489
|
+
const uploadSignal = signal ?? AbortSignal.timeout(6e4);
|
|
11490
|
+
const startResponse = await postWorkGraphReportJson({
|
|
11491
|
+
auth,
|
|
11492
|
+
body: {
|
|
11493
|
+
action: "start",
|
|
11494
|
+
upload_id: upload.uploadId,
|
|
11495
|
+
chunk_count: upload.chunks.length,
|
|
11496
|
+
total_bytes: upload.totalBytes,
|
|
11497
|
+
report_sha256: upload.reportSha256
|
|
11498
|
+
},
|
|
11499
|
+
signal: uploadSignal,
|
|
11500
|
+
url
|
|
11501
|
+
});
|
|
11502
|
+
if (!startResponse.ok) {
|
|
11503
|
+
return {
|
|
11504
|
+
ok: false,
|
|
11505
|
+
status: startResponse.status,
|
|
11506
|
+
url,
|
|
11507
|
+
data: await parseResponse(startResponse)
|
|
11508
|
+
};
|
|
11509
|
+
}
|
|
11510
|
+
for (const chunk of upload.chunks) {
|
|
11511
|
+
const chunkResponse = await postWorkGraphReportJson({
|
|
11512
|
+
auth,
|
|
11513
|
+
body: {
|
|
11514
|
+
action: "chunk",
|
|
11515
|
+
upload_id: upload.uploadId,
|
|
11516
|
+
chunk_index: chunk.chunkIndex,
|
|
11517
|
+
chunk_text: chunk.chunkText,
|
|
11518
|
+
chunk_sha256: chunk.chunkSha256
|
|
11519
|
+
},
|
|
11520
|
+
signal: uploadSignal,
|
|
11521
|
+
url
|
|
11522
|
+
});
|
|
11523
|
+
if (!chunkResponse.ok) {
|
|
11524
|
+
return {
|
|
11525
|
+
ok: false,
|
|
11526
|
+
status: chunkResponse.status,
|
|
11527
|
+
url,
|
|
11528
|
+
data: await parseResponse(chunkResponse)
|
|
11529
|
+
};
|
|
11530
|
+
}
|
|
11531
|
+
}
|
|
11532
|
+
const completeResponse = await postWorkGraphReportJson({
|
|
11533
|
+
auth,
|
|
11534
|
+
body: {
|
|
11535
|
+
action: "complete",
|
|
11536
|
+
upload_id: upload.uploadId
|
|
11537
|
+
},
|
|
11538
|
+
signal: uploadSignal,
|
|
11539
|
+
url
|
|
11540
|
+
});
|
|
11541
|
+
return {
|
|
11542
|
+
ok: completeResponse.ok,
|
|
11543
|
+
status: completeResponse.status,
|
|
11544
|
+
url,
|
|
11545
|
+
data: await parseResponse(completeResponse)
|
|
11546
|
+
};
|
|
11547
|
+
}
|
|
9573
11548
|
async function publishWorkGraphReport(report, options = {}) {
|
|
9574
11549
|
const auth = await resolveOrgxAuth();
|
|
9575
11550
|
if (!auth) {
|
|
9576
11551
|
throw new Error("OrgX auth is required to publish a Work Graph. Run `orgx-wizard auth login` or set ORGX_API_KEY.");
|
|
9577
11552
|
}
|
|
9578
11553
|
const url = buildOrgxApiUrl("/client/work-graph/reports", auth.baseUrl);
|
|
9579
|
-
const
|
|
9580
|
-
|
|
9581
|
-
|
|
9582
|
-
|
|
9583
|
-
|
|
9584
|
-
|
|
9585
|
-
|
|
9586
|
-
|
|
9587
|
-
|
|
9588
|
-
}
|
|
11554
|
+
const payload = buildWorkGraphReportPostPayload(report, options);
|
|
11555
|
+
const payloadBytes = Buffer.byteLength(JSON.stringify(payload));
|
|
11556
|
+
if (payloadBytes >= WORK_GRAPH_REPORT_CHUNK_UPLOAD_THRESHOLD_BYTES) {
|
|
11557
|
+
return publishWorkGraphReportInChunks({
|
|
11558
|
+
auth,
|
|
11559
|
+
payload,
|
|
11560
|
+
url,
|
|
11561
|
+
...options.signal ? { signal: options.signal } : {}
|
|
11562
|
+
});
|
|
11563
|
+
}
|
|
11564
|
+
const encoded = encodeWorkGraphReportPostBody(payload);
|
|
9589
11565
|
const response = await fetch(url, {
|
|
9590
11566
|
method: "POST",
|
|
9591
11567
|
headers: {
|
|
@@ -9629,8 +11605,8 @@ async function publishWorkGraphEvents(fingerprint, patch, options = {}) {
|
|
|
9629
11605
|
}
|
|
9630
11606
|
|
|
9631
11607
|
// src/lib/work-graph-hook-events.ts
|
|
9632
|
-
import { createHash as
|
|
9633
|
-
import { existsSync as
|
|
11608
|
+
import { createHash as createHash8 } from "crypto";
|
|
11609
|
+
import { existsSync as existsSync7, readFileSync as readFileSync5 } from "fs";
|
|
9634
11610
|
var SOURCE_CLIENTS = [
|
|
9635
11611
|
"codex",
|
|
9636
11612
|
"claude",
|
|
@@ -9664,7 +11640,7 @@ function asStringArray(value) {
|
|
|
9664
11640
|
return value.filter((item) => typeof item === "string" && item.trim().length > 0);
|
|
9665
11641
|
}
|
|
9666
11642
|
function stableHash(value) {
|
|
9667
|
-
return
|
|
11643
|
+
return createHash8("sha256").update(value).digest("hex").slice(0, 20);
|
|
9668
11644
|
}
|
|
9669
11645
|
function normalizeSourceClient2(value) {
|
|
9670
11646
|
const raw = asString2(value)?.toLowerCase();
|
|
@@ -9731,8 +11707,8 @@ function readHookRecord(line) {
|
|
|
9731
11707
|
}
|
|
9732
11708
|
}
|
|
9733
11709
|
function readRuntimeHookOutbox(path, limit = 200) {
|
|
9734
|
-
if (!
|
|
9735
|
-
const lines =
|
|
11710
|
+
if (!existsSync7(path)) return { path, records: [], skipped: 0 };
|
|
11711
|
+
const lines = readFileSync5(path, "utf8").split(/\r?\n/).filter((line) => line.trim().length > 0);
|
|
9736
11712
|
const selected = lines.slice(Math.max(0, lines.length - Math.max(1, limit)));
|
|
9737
11713
|
const records = [];
|
|
9738
11714
|
let skipped = Math.max(0, lines.length - selected.length);
|
|
@@ -9868,20 +11844,20 @@ function buildWorkGraphHookReplayPatch(readResult) {
|
|
|
9868
11844
|
}
|
|
9869
11845
|
|
|
9870
11846
|
// src/lib/runtime-hooks.ts
|
|
9871
|
-
import { copyFileSync, existsSync as
|
|
9872
|
-
import { homedir as
|
|
9873
|
-
import { dirname as dirname4, join as
|
|
11847
|
+
import { copyFileSync, existsSync as existsSync8, mkdirSync as mkdirSync3 } from "fs";
|
|
11848
|
+
import { homedir as homedir3 } from "os";
|
|
11849
|
+
import { dirname as dirname4, join as join6 } from "path";
|
|
9874
11850
|
var HOOK_MARKER = "orgx-session-hook.mjs";
|
|
9875
11851
|
var HOOK_EVENTS = ["SessionStart", "UserPromptSubmit", "PreToolUse", "PostToolUse", "PermissionRequest", "Stop"];
|
|
9876
11852
|
var CLAUDE_HOOK_EVENTS = ["SessionStart", "UserPromptSubmit", "PreToolUse", "PostToolUse", "SubagentStop", "Stop", "SessionEnd"];
|
|
9877
11853
|
function defaultPaths(options = {}) {
|
|
9878
|
-
const hookDir =
|
|
11854
|
+
const hookDir = join6(ORGX_WIZARD_CONFIG_HOME, "hooks");
|
|
9879
11855
|
return {
|
|
9880
|
-
claudeSettingsPath: options.claudeSettingsPath ??
|
|
9881
|
-
codexConfigPath: options.codexConfigPath ?? CODEX_CONFIG_PATH ??
|
|
9882
|
-
codexHooksPath: options.codexHooksPath ??
|
|
9883
|
-
hookScriptPath: options.hookScriptPath ??
|
|
9884
|
-
outboxPath: options.outboxPath ??
|
|
11856
|
+
claudeSettingsPath: options.claudeSettingsPath ?? join6(CLAUDE_DIR, "settings.json"),
|
|
11857
|
+
codexConfigPath: options.codexConfigPath ?? CODEX_CONFIG_PATH ?? join6(CODEX_DIR, "config.toml"),
|
|
11858
|
+
codexHooksPath: options.codexHooksPath ?? join6(CODEX_DIR, "hooks.json"),
|
|
11859
|
+
hookScriptPath: options.hookScriptPath ?? join6(hookDir, HOOK_MARKER),
|
|
11860
|
+
outboxPath: options.outboxPath ?? join6(hookDir, "events.jsonl")
|
|
9885
11861
|
};
|
|
9886
11862
|
}
|
|
9887
11863
|
function countJsonlLines(path) {
|
|
@@ -9894,7 +11870,7 @@ function backupPath(path, now) {
|
|
|
9894
11870
|
return `${path}.bak.${timestamp}`;
|
|
9895
11871
|
}
|
|
9896
11872
|
function backupExisting(path, now) {
|
|
9897
|
-
if (!
|
|
11873
|
+
if (!existsSync8(path)) return null;
|
|
9898
11874
|
const backup = backupPath(path, now);
|
|
9899
11875
|
copyFileSync(path, backup);
|
|
9900
11876
|
return backup;
|
|
@@ -10092,7 +12068,7 @@ function inspectRuntimeHooks(options = {}) {
|
|
|
10092
12068
|
installed: {
|
|
10093
12069
|
claudeCode: hasOrgxHook(claudeSettingsRaw),
|
|
10094
12070
|
codex: hasOrgxHook(codexHooksRaw),
|
|
10095
|
-
hookScript:
|
|
12071
|
+
hookScript: existsSync8(paths.hookScriptPath)
|
|
10096
12072
|
},
|
|
10097
12073
|
codex: {
|
|
10098
12074
|
configExists: Boolean(codexConfigRaw),
|
|
@@ -10322,7 +12298,7 @@ async function runHookReplayCommand(options) {
|
|
|
10322
12298
|
}
|
|
10323
12299
|
const interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
10324
12300
|
const paths = inspectRuntimeHooks().paths;
|
|
10325
|
-
const outboxPath =
|
|
12301
|
+
const outboxPath = resolve2(options.outbox?.trim() || paths.outboxPath);
|
|
10326
12302
|
const readResult = readRuntimeHookOutbox(outboxPath, parsePositiveInt(options.limit, 200));
|
|
10327
12303
|
const replay = buildWorkGraphHookReplayPatch(readResult);
|
|
10328
12304
|
if (replay.records === 0) {
|
|
@@ -10354,10 +12330,10 @@ async function runHookReplayCommand(options) {
|
|
|
10354
12330
|
}
|
|
10355
12331
|
function readAuditInput(options, interactive) {
|
|
10356
12332
|
if (options.input?.trim()) {
|
|
10357
|
-
return
|
|
12333
|
+
return readFileSync7(resolve2(options.input.trim()), "utf8");
|
|
10358
12334
|
}
|
|
10359
12335
|
if (!process.stdin.isTTY) {
|
|
10360
|
-
return
|
|
12336
|
+
return readFileSync7(0, "utf8");
|
|
10361
12337
|
}
|
|
10362
12338
|
if (!interactive) {
|
|
10363
12339
|
throw new Error("Audit input is required. Pass --input <file> or pipe text into wizard audit.");
|
|
@@ -10389,8 +12365,8 @@ function collectPathOption(value, previous = []) {
|
|
|
10389
12365
|
];
|
|
10390
12366
|
}
|
|
10391
12367
|
function parseClientExtractionFile(path) {
|
|
10392
|
-
const resolvedPath =
|
|
10393
|
-
const parsed = JSON.parse(
|
|
12368
|
+
const resolvedPath = resolve2(path);
|
|
12369
|
+
const parsed = JSON.parse(readFileSync7(resolvedPath, "utf8"));
|
|
10394
12370
|
if (!isRecord(parsed)) {
|
|
10395
12371
|
throw new Error(`AI-client extraction must be a JSON object: ${resolvedPath}`);
|
|
10396
12372
|
}
|
|
@@ -10412,8 +12388,8 @@ async function readAuditImports(options, interactive) {
|
|
|
10412
12388
|
const missingSources = [];
|
|
10413
12389
|
if (sources.length > 0) {
|
|
10414
12390
|
const imported = loadAiSessionImports({
|
|
10415
|
-
...options.claudeProjectsDir?.trim() ? { claudeProjectsDir:
|
|
10416
|
-
...options.codexSessionsDir?.trim() ? { codexSessionsDir:
|
|
12391
|
+
...options.claudeProjectsDir?.trim() ? { claudeProjectsDir: resolve2(options.claudeProjectsDir.trim()) } : {},
|
|
12392
|
+
...options.codexSessionsDir?.trim() ? { codexSessionsDir: resolve2(options.codexSessionsDir.trim()) } : {},
|
|
10417
12393
|
limitPerSource: parsePositiveInteger(options.sessionLimit, 3, "--session-limit"),
|
|
10418
12394
|
sinceDays: parsePositiveInteger(options.sessionDays, 30, "--session-days"),
|
|
10419
12395
|
sources
|
|
@@ -10441,7 +12417,7 @@ async function readAuditImports(options, interactive) {
|
|
|
10441
12417
|
}
|
|
10442
12418
|
if (imports.length === 0) {
|
|
10443
12419
|
const sourceHint = sources.length > 0 ? ` No audit-relevant lines were found in ${sources.join(", ")} sessions.` : "";
|
|
10444
|
-
throw new Error(`Audit input is required.${sourceHint} Pass --input <file>, pipe text, or use --from codex
|
|
12420
|
+
throw new Error(`Audit input is required.${sourceHint} Pass --input <file>, pipe text, or use --from claude,codex,opencode,goose,cursor,all.`);
|
|
10445
12421
|
}
|
|
10446
12422
|
return {
|
|
10447
12423
|
connectedSources,
|
|
@@ -10451,13 +12427,28 @@ async function readAuditImports(options, interactive) {
|
|
|
10451
12427
|
}
|
|
10452
12428
|
async function readWorkGraphInputs(options, interactive) {
|
|
10453
12429
|
const clientExtractions = readClientExtractions(options);
|
|
12430
|
+
const investigationSources = parseInvestigationSourceList(options.from);
|
|
12431
|
+
const investigationSourceData = investigationSources.length > 0 ? loadWorkGraphInvestigationSourceData({
|
|
12432
|
+
...options.claudeProjectsDir?.trim() ? { claudeProjectsDir: resolve2(options.claudeProjectsDir.trim()) } : {},
|
|
12433
|
+
...options.codexSessionsDir?.trim() ? { codexSessionsDir: resolve2(options.codexSessionsDir.trim()) } : {},
|
|
12434
|
+
cwd: process.cwd(),
|
|
12435
|
+
limitPerSource: parsePositiveInteger(options.sessionLimit, 8, "--session-limit"),
|
|
12436
|
+
sinceDays: parsePositiveInteger(options.sessionDays, 45, "--session-days"),
|
|
12437
|
+
sources: investigationSources
|
|
12438
|
+
}) : {
|
|
12439
|
+
clientExtractions: [],
|
|
12440
|
+
connectedSources: [],
|
|
12441
|
+
missingSources: [],
|
|
12442
|
+
rawEvents: []
|
|
12443
|
+
};
|
|
10454
12444
|
const shouldReadSessionImports = Boolean(
|
|
10455
|
-
options.from?.trim() || options.input?.trim() || clientExtractions.length === 0 || !process.stdin.isTTY && clientExtractions.length === 0
|
|
12445
|
+
options.from?.trim() || options.input?.trim() || clientExtractions.length === 0 && investigationSourceData.clientExtractions.length === 0 || !process.stdin.isTTY && clientExtractions.length === 0 && investigationSourceData.clientExtractions.length === 0
|
|
10456
12446
|
);
|
|
10457
12447
|
if (!shouldReadSessionImports) {
|
|
10458
12448
|
return {
|
|
10459
|
-
clientExtractions,
|
|
12449
|
+
clientExtractions: [...clientExtractions, ...investigationSourceData.clientExtractions],
|
|
10460
12450
|
connectedSources: [],
|
|
12451
|
+
investigationRawEvents: investigationSourceData.rawEvents,
|
|
10461
12452
|
imports: [],
|
|
10462
12453
|
missingSources: []
|
|
10463
12454
|
};
|
|
@@ -10466,7 +12457,7 @@ async function readWorkGraphInputs(options, interactive) {
|
|
|
10466
12457
|
try {
|
|
10467
12458
|
auditImports = await readAuditImports(options, interactive);
|
|
10468
12459
|
} catch (error) {
|
|
10469
|
-
if (clientExtractions.length === 0) throw error;
|
|
12460
|
+
if (clientExtractions.length === 0 && investigationSourceData.clientExtractions.length === 0) throw error;
|
|
10470
12461
|
const message = error instanceof Error ? error.message : String(error);
|
|
10471
12462
|
auditImports = {
|
|
10472
12463
|
connectedSources: [],
|
|
@@ -10475,10 +12466,11 @@ async function readWorkGraphInputs(options, interactive) {
|
|
|
10475
12466
|
};
|
|
10476
12467
|
}
|
|
10477
12468
|
return {
|
|
10478
|
-
clientExtractions,
|
|
10479
|
-
connectedSources: auditImports.connectedSources,
|
|
12469
|
+
clientExtractions: [...clientExtractions, ...investigationSourceData.clientExtractions],
|
|
12470
|
+
connectedSources: [...auditImports.connectedSources, ...investigationSourceData.connectedSources],
|
|
12471
|
+
investigationRawEvents: investigationSourceData.rawEvents,
|
|
10480
12472
|
imports: auditImports.imports,
|
|
10481
|
-
missingSources: auditImports.missingSources
|
|
12473
|
+
missingSources: [...auditImports.missingSources, ...investigationSourceData.missingSources]
|
|
10482
12474
|
};
|
|
10483
12475
|
}
|
|
10484
12476
|
function requireWriteApproval(options, interactive) {
|
|
@@ -10555,10 +12547,10 @@ async function runAuditCommand(options) {
|
|
|
10555
12547
|
workspace
|
|
10556
12548
|
});
|
|
10557
12549
|
const markdown = renderSelfAuditMarkdown(plan);
|
|
10558
|
-
const outputDir =
|
|
12550
|
+
const outputDir = resolve2(options.outputDir?.trim() || ".orgx/audits");
|
|
10559
12551
|
const timestamp = plan.generated_at.replace(/[:.]/g, "-");
|
|
10560
|
-
const jsonPath =
|
|
10561
|
-
const markdownPath =
|
|
12552
|
+
const jsonPath = resolve2(outputDir, `ai-native-self-audit-${timestamp}.json`);
|
|
12553
|
+
const markdownPath = resolve2(outputDir, `ai-native-self-audit-${timestamp}.md`);
|
|
10562
12554
|
writeJsonFile(jsonPath, plan);
|
|
10563
12555
|
writeTextFile(markdownPath, markdown);
|
|
10564
12556
|
if (options.json) {
|
|
@@ -10609,7 +12601,7 @@ async function runAuditCommand(options) {
|
|
|
10609
12601
|
}
|
|
10610
12602
|
function runWorkGraphExtractionSchemaCommand(options) {
|
|
10611
12603
|
const protocol = buildWorkGraphExtractionProtocol();
|
|
10612
|
-
const outputPath = options.output?.trim() ?
|
|
12604
|
+
const outputPath = options.output?.trim() ? resolve2(options.output.trim()) : "";
|
|
10613
12605
|
if (outputPath) {
|
|
10614
12606
|
if (options.json) {
|
|
10615
12607
|
writeJsonFile(outputPath, protocol);
|
|
@@ -10648,6 +12640,7 @@ async function runWorkGraphCommand(options, defaults = {}) {
|
|
|
10648
12640
|
...workspace.id === "local-workspace" ? [] : ["OrgX workspace"]
|
|
10649
12641
|
],
|
|
10650
12642
|
imports: auditInputs.imports,
|
|
12643
|
+
investigationRawEvents: auditInputs.investigationRawEvents,
|
|
10651
12644
|
missingSources: [
|
|
10652
12645
|
...workspace.id === "local-workspace" ? ["OrgX workspace auth"] : [],
|
|
10653
12646
|
...auditInputs.missingSources
|
|
@@ -10656,10 +12649,10 @@ async function runWorkGraphCommand(options, defaults = {}) {
|
|
|
10656
12649
|
workspace
|
|
10657
12650
|
});
|
|
10658
12651
|
const markdown = renderWorkGraphMarkdown(report);
|
|
10659
|
-
const outputDir =
|
|
12652
|
+
const outputDir = resolve2(commandOptions.outputDir?.trim() || ".orgx/work-graph");
|
|
10660
12653
|
const timestamp = report.generated_at.replace(/[:.]/g, "-");
|
|
10661
|
-
const jsonPath =
|
|
10662
|
-
const markdownPath =
|
|
12654
|
+
const jsonPath = resolve2(outputDir, `work-graph-report-${timestamp}.json`);
|
|
12655
|
+
const markdownPath = resolve2(outputDir, `work-graph-report-${timestamp}.md`);
|
|
10663
12656
|
writeJsonFile(jsonPath, report);
|
|
10664
12657
|
writeTextFile(markdownPath, markdown);
|
|
10665
12658
|
let published = null;
|
|
@@ -11041,14 +13034,14 @@ async function readSingleKey() {
|
|
|
11041
13034
|
const stdin = process.stdin;
|
|
11042
13035
|
if (!stdin.isTTY) return null;
|
|
11043
13036
|
const previousRawMode = stdin.isRaw === true;
|
|
11044
|
-
return await new Promise((
|
|
13037
|
+
return await new Promise((resolve3) => {
|
|
11045
13038
|
const cleanup = (result) => {
|
|
11046
13039
|
stdin.off("data", onData);
|
|
11047
13040
|
if (stdin.isTTY) {
|
|
11048
13041
|
stdin.setRawMode(previousRawMode);
|
|
11049
13042
|
}
|
|
11050
13043
|
stdin.pause();
|
|
11051
|
-
|
|
13044
|
+
resolve3(result);
|
|
11052
13045
|
};
|
|
11053
13046
|
const onData = (chunk) => {
|
|
11054
13047
|
const text2 = chunk.toString("utf8");
|
|
@@ -11701,7 +13694,7 @@ function printDoctorReport(report, assessment) {
|
|
|
11701
13694
|
async function main() {
|
|
11702
13695
|
const program = new Command();
|
|
11703
13696
|
program.name("orgx-wizard").description("Add OrgX MCP configs, skills/rules, and companion plugins to your local AI tools.").showHelpAfterError();
|
|
11704
|
-
const pkgVersion = true ? "0.1.
|
|
13697
|
+
const pkgVersion = true ? "0.1.40" : void 0;
|
|
11705
13698
|
program.version(pkgVersion ?? "unknown", "-V, --version");
|
|
11706
13699
|
program.hook("preAction", (_thisCommand, actionCommand) => {
|
|
11707
13700
|
if (Boolean(actionCommand.optsWithGlobals().json)) return;
|
|
@@ -12391,7 +14384,7 @@ async function main() {
|
|
|
12391
14384
|
jsonOutput: Boolean(options.json)
|
|
12392
14385
|
});
|
|
12393
14386
|
});
|
|
12394
|
-
program.command("audit").description("Run the AI-native Founder Loop self-audit from pasted or file-based AI-session context.").option("--input <path>", "AI-session transcript or summary file; stdin is used when piped").option("--from <sources>", "auto-import recent local AI sessions: codex,
|
|
14387
|
+
program.command("audit").description("Run the AI-native Founder Loop self-audit from pasted or file-based AI-session context.").option("--input <path>", "AI-session transcript or summary file; stdin is used when piped").option("--from <sources>", "auto-import recent local AI sessions: claude, codex, opencode, goose, cursor, or all").option("--session-limit <count>", "max recent sessions to import per selected source", "3").option("--session-days <days>", "lookback window for local AI-session imports", "30").option("--codex-sessions-dir <path>", "override Codex sessions directory for audit import").option("--claude-projects-dir <path>", "override Claude projects directory for audit import").option("--source-label <label>", "label for the imported AI-session source").option("--workspace-id <id>", "workspace id override; defaults to the current OrgX workspace when authenticated").option("--workspace-name <name>", "workspace name override for local-only audits").option("--output-dir <path>", "directory for generated audit JSON and Markdown", ".orgx/audits").option("--attach-to-initiative <id>", "attach the generated audit artifact to an existing OrgX initiative").option("--create-initiative", "create an OrgX initiative from the generated audit plan").option("--write-follow-up", "create one approval-gated OrgX follow-up task from the audit recommendation").option("--workstream-id <id>", "optional workstream id for the generated follow-up task").option("--milestone-id <id>", "optional milestone id for the generated follow-up task").option("--dry-run", "exercise OrgX write paths without sending writes").option("--yes", "approve OrgX write flags in non-interactive mode").option("--json", "emit a JSON command summary").action(async (options) => {
|
|
12395
14388
|
await safeTrackWizardTelemetry("audit_started", {
|
|
12396
14389
|
attach_to_initiative: Boolean(options.attachToInitiative),
|
|
12397
14390
|
command: "audit",
|
|
@@ -12409,14 +14402,14 @@ async function main() {
|
|
|
12409
14402
|
});
|
|
12410
14403
|
runWorkGraphExtractionSchemaCommand(options);
|
|
12411
14404
|
});
|
|
12412
|
-
workGraph.command("preview").description("Preview the OrgX Profile evidence findings without writing to OrgX.").option("--input <path>", "source transcript or summary file; stdin is used when piped").option("--extraction <path>", "structured AI-client extraction JSON; repeat or comma-separate for multiple clients", collectPathOption, []).option("--from <sources>", "auto-import recent local AI sessions: codex,
|
|
14405
|
+
workGraph.command("preview").description("Preview the OrgX Profile evidence findings without writing to OrgX.").option("--input <path>", "source transcript or summary file; stdin is used when piped").option("--extraction <path>", "structured AI-client extraction JSON; repeat or comma-separate for multiple clients", collectPathOption, []).option("--from <sources>", "auto-import recent local AI sessions: claude, codex, opencode, goose, cursor, or all").option("--session-limit <count>", "max recent sessions to import per selected source", "15").option("--session-days <days>", "lookback window for local AI-session imports", "60").option("--codex-sessions-dir <path>", "override Codex sessions directory for import").option("--claude-projects-dir <path>", "override Claude projects directory for import").option("--source-label <label>", "label for the imported manual source").option("--workspace-id <id>", "workspace id override; defaults to the current OrgX workspace when authenticated").option("--workspace-name <name>", "workspace name override for local-only previews").option("--output-dir <path>", "directory for generated Work Graph JSON and Markdown", ".orgx/work-graph").option("--json", "emit a JSON command summary").action(async (options) => {
|
|
12413
14406
|
await safeTrackWizardTelemetry("work_graph_preview_started", {
|
|
12414
14407
|
command: "work-graph preview",
|
|
12415
14408
|
from: options.from ?? "manual"
|
|
12416
14409
|
});
|
|
12417
14410
|
await runWorkGraphCommand(options);
|
|
12418
14411
|
});
|
|
12419
|
-
workGraph.command("profile").description("Build a local OrgX Profile with evidence findings, domain coverage, source confidence, and repair recommendations.").option("--input <path>", "source transcript or summary file; stdin is used when piped").option("--extraction <path>", "structured AI-client extraction JSON; repeat or comma-separate for multiple clients", collectPathOption, []).option("--from <sources>", "auto-import recent local AI sessions: codex,
|
|
14412
|
+
workGraph.command("profile").description("Build a local OrgX Profile with evidence findings, domain coverage, source confidence, and repair recommendations.").option("--input <path>", "source transcript or summary file; stdin is used when piped").option("--extraction <path>", "structured AI-client extraction JSON; repeat or comma-separate for multiple clients", collectPathOption, []).option("--from <sources>", "auto-import recent local AI sessions: claude, codex, opencode, goose, cursor, or all").option("--session-limit <count>", "max recent sessions to import per selected source", "25").option("--session-days <days>", "lookback window for local AI-session imports", "60").option("--codex-sessions-dir <path>", "override Codex sessions directory for import").option("--claude-projects-dir <path>", "override Claude projects directory for import").option("--source-label <label>", "label for the imported manual source").option("--workspace-id <id>", "workspace id override; defaults to the current OrgX workspace when authenticated").option("--workspace-name <name>", "workspace name override for local-only profiles").option("--output-dir <path>", "directory for generated Work Graph JSON and Markdown", ".orgx/work-graph").option("--publish", "publish the generated OrgX Profile to OrgX and return a shareable URL").option("--public-share", "create a public redacted /work-graph/<token> share when publishing").option("--attach-artifact", "attach the report as an OrgX artifact when an initiative/entity is provided").option("--attach-to-initiative <id>", "attach the report to an existing OrgX initiative").option("--entity-type <type>", "entity type for artifact attachment: project, initiative, milestone, task, decision").option("--entity-id <id>", "entity id for artifact attachment").option("--artifact-url <url>", "override the artifact URL stored in OrgX").option("--yes", "approve publish/write prompts in non-interactive mode").option("--json", "emit a JSON command summary").action(async (options) => {
|
|
12420
14413
|
await safeTrackWizardTelemetry("work_graph_profile_started", {
|
|
12421
14414
|
command: "work-graph profile",
|
|
12422
14415
|
from: options.from ?? "manual"
|
|
@@ -12424,7 +14417,7 @@ async function main() {
|
|
|
12424
14417
|
await runWorkGraphCommand(options);
|
|
12425
14418
|
});
|
|
12426
14419
|
const sessions = program.command("sessions").description("Inspect and reconcile local AI sessions into OrgX-ready Work Graph reports.");
|
|
12427
|
-
sessions.command("reconcile").description("Backfill recent Codex and
|
|
14420
|
+
sessions.command("reconcile").description("Backfill recent Claude Code, Codex, OpenCode, goose, and Cursor sessions into a redacted OrgX Profile report.").option("--extraction <path>", "structured AI-client extraction JSON; repeat or comma-separate for multiple clients", collectPathOption, []).option("--from <sources>", "auto-import recent local AI sessions: claude, codex, opencode, goose, cursor, or all", "all").option("--session-limit <count>", "max recent sessions to import per selected source", "25").option("--session-days <days>", "lookback window for local AI-session imports", "30").option("--codex-sessions-dir <path>", "override Codex sessions directory for import").option("--claude-projects-dir <path>", "override Claude projects directory for import").option("--workspace-id <id>", "workspace id override; defaults to the current OrgX workspace when authenticated").option("--workspace-name <name>", "workspace name override for local-only reconciliation").option("--output-dir <path>", "directory for generated Work Graph JSON and Markdown", ".orgx/work-graph").option("--publish", "publish the generated OrgX Profile to OrgX and return a shareable URL").option("--public-share", "create a public redacted /work-graph/<token> share when publishing").option("--attach-artifact", "attach the report as an OrgX artifact when an initiative/entity is provided").option("--attach-to-initiative <id>", "attach the report to an existing OrgX initiative").option("--entity-type <type>", "entity type for artifact attachment: project, initiative, milestone, task, decision").option("--entity-id <id>", "entity id for artifact attachment").option("--artifact-url <url>", "override the artifact URL stored in OrgX").option("--yes", "approve publish/write prompts in non-interactive mode").option("--json", "emit a JSON command summary").action(async (options) => {
|
|
12428
14421
|
await safeTrackWizardTelemetry("sessions_reconcile_started", {
|
|
12429
14422
|
command: "sessions reconcile",
|
|
12430
14423
|
from: options.from ?? "all"
|