@useorgx/wizard 0.1.32 → 0.1.36
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 +1414 -60
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
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 readFileSync6 } from "fs";
|
|
7
7
|
import { hostname } from "os";
|
|
8
8
|
import { resolve } from "path";
|
|
9
9
|
import { Command } from "commander";
|
|
@@ -6990,6 +6990,7 @@ var WORK_GRAPH_FINDING_TYPES = [
|
|
|
6990
6990
|
"business",
|
|
6991
6991
|
"product_surface",
|
|
6992
6992
|
"goal",
|
|
6993
|
+
"outcome",
|
|
6993
6994
|
"initiative_candidate",
|
|
6994
6995
|
"missed_orchestration_opportunity"
|
|
6995
6996
|
];
|
|
@@ -7022,6 +7023,10 @@ function sourceClientForImport(source) {
|
|
|
7022
7023
|
if (raw.includes("slack")) return "slack";
|
|
7023
7024
|
if (raw.includes("github")) return "github";
|
|
7024
7025
|
if (raw.includes("linear")) return "linear";
|
|
7026
|
+
if (raw.includes("gmail") || raw.includes("email")) return "gmail";
|
|
7027
|
+
if (raw.includes("calendar") || raw.includes("meeting")) return "calendar";
|
|
7028
|
+
if (raw.includes("notion")) return "notion";
|
|
7029
|
+
if (raw.includes("doc")) return "docs";
|
|
7025
7030
|
if (raw.includes("mcp")) return "mcp";
|
|
7026
7031
|
if (raw.includes("api")) return "api";
|
|
7027
7032
|
if (raw.includes("manual") || raw.includes("wizard-audit-input")) return "manual";
|
|
@@ -7030,7 +7035,7 @@ function sourceClientForImport(source) {
|
|
|
7030
7035
|
function normalizeSourceClient(value) {
|
|
7031
7036
|
if (typeof value !== "string") return "unknown";
|
|
7032
7037
|
const normalized = value.trim().toLowerCase();
|
|
7033
|
-
if (normalized === "codex" || normalized === "claude" || normalized === "claude-code" || normalized === "cursor" || normalized === "openclaw" || normalized === "slack" || normalized === "mcp" || normalized === "github" || normalized === "linear" || normalized === "manual" || normalized === "wizard" || normalized === "api") {
|
|
7038
|
+
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") {
|
|
7034
7039
|
return normalized;
|
|
7035
7040
|
}
|
|
7036
7041
|
if (normalized.includes("claude")) return "claude";
|
|
@@ -7039,9 +7044,46 @@ function normalizeSourceClient(value) {
|
|
|
7039
7044
|
if (normalized.includes("slack")) return "slack";
|
|
7040
7045
|
if (normalized.includes("github")) return "github";
|
|
7041
7046
|
if (normalized.includes("linear")) return "linear";
|
|
7047
|
+
if (normalized.includes("gmail") || normalized.includes("email")) return "gmail";
|
|
7048
|
+
if (normalized.includes("calendar") || normalized.includes("meeting")) return "calendar";
|
|
7049
|
+
if (normalized.includes("notion")) return "notion";
|
|
7050
|
+
if (normalized.includes("doc")) return "docs";
|
|
7042
7051
|
if (normalized.includes("mcp")) return "mcp";
|
|
7043
7052
|
return "unknown";
|
|
7044
7053
|
}
|
|
7054
|
+
function sourceClientFromText(value) {
|
|
7055
|
+
const normalized = value.toLowerCase();
|
|
7056
|
+
if (/\bclaude-code\b|\.claude\/projects|claude:/.test(normalized)) return "claude";
|
|
7057
|
+
if (/\bcodex\b|\.codex\/sessions|rollout-/.test(normalized)) return "codex";
|
|
7058
|
+
if (/\bcursor\b/.test(normalized)) return "cursor";
|
|
7059
|
+
if (/\bopenclaw\b/.test(normalized)) return "openclaw";
|
|
7060
|
+
if (/\bslack\b/.test(normalized)) return "slack";
|
|
7061
|
+
if (/\bgithub\b|\bgit:|pull request|commit\b/.test(normalized)) return "github";
|
|
7062
|
+
if (/\blinear\b/.test(normalized)) return "linear";
|
|
7063
|
+
if (/\bgmail\b|\bemail\b/.test(normalized)) return "gmail";
|
|
7064
|
+
if (/\bcalendar\b|\bmeeting\b/.test(normalized)) return "calendar";
|
|
7065
|
+
if (/\bnotion\b/.test(normalized)) return "notion";
|
|
7066
|
+
if (/\bdocs?\b|google drive/.test(normalized)) return "docs";
|
|
7067
|
+
if (/\bmcp\b|mcp__|orgx_emit|scaffold_initiative|ship_batch/.test(normalized)) return "mcp";
|
|
7068
|
+
if (/\borgx api\b|\bapi\b/.test(normalized)) return "api";
|
|
7069
|
+
return "unknown";
|
|
7070
|
+
}
|
|
7071
|
+
function sourceClientForExtractionFinding(extraction, finding) {
|
|
7072
|
+
const explicit = normalizeSourceClient(finding.source_client);
|
|
7073
|
+
if (explicit !== "unknown") return explicit;
|
|
7074
|
+
const inferred = sourceClientFromText([
|
|
7075
|
+
finding.source_id ?? "",
|
|
7076
|
+
finding.source_label ?? "",
|
|
7077
|
+
finding.evidence_ref ?? "",
|
|
7078
|
+
finding.summary ?? ""
|
|
7079
|
+
].join("\n"));
|
|
7080
|
+
if (inferred !== "unknown") return inferred;
|
|
7081
|
+
return normalizeSourceClient(extraction.source_client);
|
|
7082
|
+
}
|
|
7083
|
+
function arrayOfStrings(value) {
|
|
7084
|
+
if (!Array.isArray(value)) return [];
|
|
7085
|
+
return value.filter((item) => typeof item === "string" && item.trim().length > 0);
|
|
7086
|
+
}
|
|
7045
7087
|
function normalizeFindingType(value) {
|
|
7046
7088
|
if (typeof value !== "string") return null;
|
|
7047
7089
|
const normalized = value.trim().toLowerCase();
|
|
@@ -7052,9 +7094,23 @@ function normalizeConfidence(value, fallback = 0.72) {
|
|
|
7052
7094
|
return Math.max(0.1, Math.min(0.99, Number(value.toFixed(2))));
|
|
7053
7095
|
}
|
|
7054
7096
|
function titleFromText(text2, fallback) {
|
|
7055
|
-
const
|
|
7097
|
+
const lower = text2.toLowerCase();
|
|
7098
|
+
if (lower.includes("mcp__orgx__list_entities") && lower.includes("zod")) {
|
|
7099
|
+
return "OrgX MCP list_entities is failing schema validation";
|
|
7100
|
+
}
|
|
7101
|
+
if (lower.includes("scaffold_initiative") && lower.includes("auto_continue") && lower.includes("dispatch")) {
|
|
7102
|
+
return "scaffold_initiative creates ready streams without dispatching agent runs";
|
|
7103
|
+
}
|
|
7104
|
+
if (lower.includes("operation qa loop") && lower.includes("0/") && lower.includes("entities")) {
|
|
7105
|
+
return "OrgX scaffold fails to create Operation QA Loop entities";
|
|
7106
|
+
}
|
|
7107
|
+
if (lower.includes("production-only redirect") || lower.includes("livedemopageclient")) {
|
|
7108
|
+
return "Production route behavior changed without durable approval";
|
|
7109
|
+
}
|
|
7110
|
+
const normalized = text2.replace(/[`*_>#]/g, "").replace(/^\s*[-*\d.)]+\s*/, "").replace(/^\s*(decision|artifact|commitment|next action|follow[- ]?up|outcome|roi|economics|open loop|gap|blocker|risk|goal|summary)\s*:\s*/i, "").replace(/^\s*(fix|fixing|debug|investigate|continue|todo)\s+(the\s+)?(bug|issue|problem)\s*[:.-]?\s*/i, "").replace(/^\s*one\s+bug\s+surfaced\s*:\s*/i, "").replace(/\s+/g, " ").trim();
|
|
7056
7111
|
const firstSentence = normalized.split(/[.!?]\s/)[0]?.trim() || normalized;
|
|
7057
|
-
|
|
7112
|
+
const firstClause = firstSentence.split(/\s[-:;]\s/)[0]?.trim() || firstSentence;
|
|
7113
|
+
return (firstClause || fallback).slice(0, 110);
|
|
7058
7114
|
}
|
|
7059
7115
|
function normalizeClientExtractionId(extraction, index) {
|
|
7060
7116
|
return extraction.extraction_id?.trim() || `${extraction.source_client || "unknown"}:client-extraction:${index + 1}`;
|
|
@@ -7093,9 +7149,15 @@ function buildWorkGraphExtractionProtocol() {
|
|
|
7093
7149
|
{
|
|
7094
7150
|
id: "people_businesses",
|
|
7095
7151
|
lens: "People and business trails",
|
|
7096
|
-
query: "Find users, customers, buyers, stakeholders, reviewers, teams, businesses, accounts, and market signals connected to work or decisions.",
|
|
7152
|
+
query: "Find users, customers, buyers, stakeholders, reviewers, teams, businesses, accounts, deal context, support signals, and market signals connected to work or decisions.",
|
|
7097
7153
|
return_when: "A person or business changes priority, ownership, revenue potential, customer pain, or follow-up urgency."
|
|
7098
7154
|
},
|
|
7155
|
+
{
|
|
7156
|
+
id: "coordination_sources",
|
|
7157
|
+
lens: "Coordination and calendar/email trails",
|
|
7158
|
+
query: "Find Slack threads, emails, meetings, docs, handoffs, approvals, owner changes, follow-ups, and coordination gaps that explain how work moved or stalled outside the AI client.",
|
|
7159
|
+
return_when: "The coordination source proves ownership, urgency, approval, customer context, or a missing source needed for attribution."
|
|
7160
|
+
},
|
|
7099
7161
|
{
|
|
7100
7162
|
id: "product_surfaces",
|
|
7101
7163
|
lens: "Product surface trails",
|
|
@@ -7124,7 +7186,7 @@ function buildWorkGraphExtractionProtocol() {
|
|
|
7124
7186
|
required_output: {
|
|
7125
7187
|
schema_version: WORK_GRAPH_EXTRACTION_SCHEMA_VERSION,
|
|
7126
7188
|
extraction_id: "stable id for this extraction run",
|
|
7127
|
-
source_client: "codex | claude | claude-code | cursor | openclaw | slack | mcp | github | linear | manual | api | unknown",
|
|
7189
|
+
source_client: "codex | claude | claude-code | cursor | openclaw | slack | mcp | github | linear | gmail | calendar | notion | docs | manual | api | unknown",
|
|
7128
7190
|
source_label: "human-readable source label",
|
|
7129
7191
|
searched_sources: ["session/log/source group names searched"],
|
|
7130
7192
|
search_queries: [
|
|
@@ -7143,9 +7205,10 @@ function buildWorkGraphExtractionProtocol() {
|
|
|
7143
7205
|
},
|
|
7144
7206
|
findings: [
|
|
7145
7207
|
{
|
|
7146
|
-
type: "decision | artifact | blocker | person | business | product_surface | goal | action | initiative_candidate | missed_orchestration_opportunity",
|
|
7208
|
+
type: "decision | artifact | blocker | person | business | product_surface | goal | outcome | action | initiative_candidate | missed_orchestration_opportunity",
|
|
7147
7209
|
title: "short durable title, not a raw line",
|
|
7148
7210
|
summary: "one sentence explaining why this matters",
|
|
7211
|
+
source_client: "optional override when a blended extractor found evidence from another client",
|
|
7149
7212
|
source_id: "session/tool/source id",
|
|
7150
7213
|
source_label: "source label",
|
|
7151
7214
|
evidence_ref: "stable evidence pointer",
|
|
@@ -7219,6 +7282,12 @@ function includesAny2(text2, patterns) {
|
|
|
7219
7282
|
return patterns.some((pattern) => pattern.test(text2));
|
|
7220
7283
|
}
|
|
7221
7284
|
function buildCoverage(imports, connectedSources, missingSources, clientExtractions = []) {
|
|
7285
|
+
const findings = buildClientExtractionFindings(clientExtractions);
|
|
7286
|
+
const sourceClients = sortedUnique([
|
|
7287
|
+
...imports.map(sourceClientForImport),
|
|
7288
|
+
...findings.map((finding) => finding.source_client),
|
|
7289
|
+
...clientExtractions.map((extraction) => normalizeSourceClient(extraction.source_client))
|
|
7290
|
+
].filter((source) => source !== "unknown"));
|
|
7222
7291
|
const extractionText = clientExtractions.flatMap((extraction) => [
|
|
7223
7292
|
extraction.source_client,
|
|
7224
7293
|
extraction.source_label ?? "",
|
|
@@ -7235,14 +7304,150 @@ ${extractionText}`.toLowerCase();
|
|
|
7235
7304
|
const mcpObserved = /\bmcp\b|mcp__|tool call|tools\/call|call_tool|orgx_emit_activity/i.test(allText);
|
|
7236
7305
|
const orgxMcpCalled = /mcp__orgx__|orgx_emit_activity|orgx_apply_changeset|complete_with_proof|scaffold_initiative/i.test(allText);
|
|
7237
7306
|
const skillOnlySignal = orgxObserved && !orgxMcpCalled && /\bskill|instructions|agent|workflow\b/i.test(allText);
|
|
7307
|
+
const inferredConnected = [
|
|
7308
|
+
...connectedSources,
|
|
7309
|
+
...sourceClients.includes("codex") ? ["Codex sessions"] : [],
|
|
7310
|
+
...sourceClients.includes("claude") || sourceClients.includes("claude-code") ? ["Claude Code sessions"] : [],
|
|
7311
|
+
...sourceClients.includes("github") ? ["Git/GitHub proof"] : [],
|
|
7312
|
+
...sourceClients.includes("mcp") ? ["MCP tool telemetry"] : [],
|
|
7313
|
+
...sourceClients.includes("slack") ? ["Slack coordination"] : []
|
|
7314
|
+
];
|
|
7315
|
+
const inferredMissing = [
|
|
7316
|
+
...missingSources,
|
|
7317
|
+
...sourceClients.includes("slack") ? [] : ["Slack coordination"],
|
|
7318
|
+
...sourceClients.includes("github") ? [] : ["GitHub PR/commit proof"],
|
|
7319
|
+
...allText.includes("hook") || allText.includes("outbox") ? [] : ["Runtime hook outbox replay"]
|
|
7320
|
+
];
|
|
7321
|
+
const connected = sortedUnique(inferredConnected.filter(Boolean));
|
|
7322
|
+
const missing = sortedUnique(inferredMissing.filter((source) => !connected.includes(source)));
|
|
7323
|
+
const manifests = buildSourceCoverageManifests({
|
|
7324
|
+
clientExtractions,
|
|
7325
|
+
connectedSources: connected,
|
|
7326
|
+
findings,
|
|
7327
|
+
imports,
|
|
7328
|
+
missingSources: missing
|
|
7329
|
+
});
|
|
7330
|
+
const partialCount = manifests.filter((manifest) => manifest.status === "partial").length;
|
|
7331
|
+
const coverageScore = clampScore2(
|
|
7332
|
+
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
|
|
7333
|
+
);
|
|
7238
7334
|
return {
|
|
7239
|
-
connected
|
|
7240
|
-
missing
|
|
7335
|
+
connected,
|
|
7336
|
+
missing,
|
|
7241
7337
|
mcpObserved,
|
|
7242
7338
|
orgxObserved,
|
|
7243
7339
|
orgxMcpCalled,
|
|
7244
|
-
skillOnlySignal
|
|
7340
|
+
skillOnlySignal,
|
|
7341
|
+
coverage_score: coverageScore,
|
|
7342
|
+
manifests,
|
|
7343
|
+
notes: [
|
|
7344
|
+
"OrgX/MCP writeback is one coverage signal; it is not required for the audit to find useful work.",
|
|
7345
|
+
missing.length > 0 ? `Coverage is limited by missing sources: ${missing.join(", ")}.` : "Connected sources are sufficient for a first-pass operating profile, but still require user review."
|
|
7346
|
+
]
|
|
7347
|
+
};
|
|
7348
|
+
}
|
|
7349
|
+
function buildSourceCoverageManifests(input) {
|
|
7350
|
+
const manifests = /* @__PURE__ */ new Map();
|
|
7351
|
+
const addManifest = (manifest) => {
|
|
7352
|
+
const key = `${manifest.source_client}:${manifest.source_label}`;
|
|
7353
|
+
const previous = manifests.get(key);
|
|
7354
|
+
if (!previous) {
|
|
7355
|
+
manifests.set(key, manifest);
|
|
7356
|
+
return;
|
|
7357
|
+
}
|
|
7358
|
+
manifests.set(key, {
|
|
7359
|
+
...previous,
|
|
7360
|
+
status: previous.status === "connected" || manifest.status === "connected" ? "connected" : previous.status === "partial" || manifest.status === "partial" ? "partial" : "missing",
|
|
7361
|
+
searched_sources: sortedUnique([...previous.searched_sources, ...manifest.searched_sources]),
|
|
7362
|
+
searched_session_count: previous.searched_session_count + manifest.searched_session_count,
|
|
7363
|
+
skipped_session_count: previous.skipped_session_count + manifest.skipped_session_count,
|
|
7364
|
+
query_count: previous.query_count + manifest.query_count,
|
|
7365
|
+
finding_count: previous.finding_count + manifest.finding_count,
|
|
7366
|
+
confidence: Math.max(previous.confidence, manifest.confidence),
|
|
7367
|
+
notes: sortedUnique([...previous.notes, ...manifest.notes])
|
|
7368
|
+
});
|
|
7245
7369
|
};
|
|
7370
|
+
for (const extraction of input.clientExtractions) {
|
|
7371
|
+
const extractionFindings = buildClientExtractionFindings([extraction]);
|
|
7372
|
+
const byClient = /* @__PURE__ */ new Map();
|
|
7373
|
+
for (const finding of extractionFindings) {
|
|
7374
|
+
byClient.set(finding.source_client, [...byClient.get(finding.source_client) ?? [], finding]);
|
|
7375
|
+
}
|
|
7376
|
+
for (const [sourceClient, findings] of byClient) {
|
|
7377
|
+
const searchedSources = extraction.searched_sources ?? [];
|
|
7378
|
+
addManifest({
|
|
7379
|
+
source_client: sourceClient,
|
|
7380
|
+
source_label: labelForSourceClient(sourceClient),
|
|
7381
|
+
status: findings.length > 0 ? "connected" : "partial",
|
|
7382
|
+
searched_sources: searchedSources,
|
|
7383
|
+
searched_session_count: extraction.extraction_quality?.searched_session_count ?? 0,
|
|
7384
|
+
skipped_session_count: extraction.extraction_quality?.skipped_session_count ?? 0,
|
|
7385
|
+
query_count: extraction.search_queries?.length ?? 0,
|
|
7386
|
+
finding_count: findings.length,
|
|
7387
|
+
confidence: normalizeConfidence(extraction.extraction_quality?.confidence, 0.74),
|
|
7388
|
+
notes: extraction.extraction_quality?.notes ?? []
|
|
7389
|
+
});
|
|
7390
|
+
}
|
|
7391
|
+
}
|
|
7392
|
+
for (const source of input.imports) {
|
|
7393
|
+
const sourceClient = sourceClientForImport(source);
|
|
7394
|
+
addManifest({
|
|
7395
|
+
source_client: sourceClient,
|
|
7396
|
+
source_label: source.sourceLabel,
|
|
7397
|
+
status: "connected",
|
|
7398
|
+
searched_sources: [source.sourceId],
|
|
7399
|
+
searched_session_count: 1,
|
|
7400
|
+
skipped_session_count: 0,
|
|
7401
|
+
query_count: 0,
|
|
7402
|
+
finding_count: input.findings.filter((finding) => finding.source_id === source.sourceId).length,
|
|
7403
|
+
confidence: 0.62,
|
|
7404
|
+
notes: ["Fallback line-level import; lower confidence than client-native extraction."]
|
|
7405
|
+
});
|
|
7406
|
+
}
|
|
7407
|
+
for (const source of input.missingSources) {
|
|
7408
|
+
addManifest({
|
|
7409
|
+
source_client: sourceClientFromText(source),
|
|
7410
|
+
source_label: source,
|
|
7411
|
+
status: "missing",
|
|
7412
|
+
searched_sources: [],
|
|
7413
|
+
searched_session_count: 0,
|
|
7414
|
+
skipped_session_count: 0,
|
|
7415
|
+
query_count: 0,
|
|
7416
|
+
finding_count: 0,
|
|
7417
|
+
confidence: 0,
|
|
7418
|
+
notes: ["Not connected or not searched in this audit pass."]
|
|
7419
|
+
});
|
|
7420
|
+
}
|
|
7421
|
+
return [...manifests.values()].sort((left, right) => {
|
|
7422
|
+
const rank = { connected: 0, partial: 1, missing: 2 };
|
|
7423
|
+
return rank[left.status] - rank[right.status] || right.finding_count - left.finding_count;
|
|
7424
|
+
});
|
|
7425
|
+
}
|
|
7426
|
+
function labelForSourceClient(sourceClient) {
|
|
7427
|
+
switch (sourceClient) {
|
|
7428
|
+
case "codex":
|
|
7429
|
+
return "Codex sessions";
|
|
7430
|
+
case "claude":
|
|
7431
|
+
case "claude-code":
|
|
7432
|
+
return "Claude Code sessions";
|
|
7433
|
+
case "mcp":
|
|
7434
|
+
return "MCP tool telemetry";
|
|
7435
|
+
case "github":
|
|
7436
|
+
return "Git/GitHub proof";
|
|
7437
|
+
case "slack":
|
|
7438
|
+
return "Slack coordination";
|
|
7439
|
+
case "linear":
|
|
7440
|
+
return "Linear issues";
|
|
7441
|
+
case "gmail":
|
|
7442
|
+
return "Email coordination";
|
|
7443
|
+
case "calendar":
|
|
7444
|
+
return "Calendar/meeting context";
|
|
7445
|
+
case "notion":
|
|
7446
|
+
case "docs":
|
|
7447
|
+
return "Docs";
|
|
7448
|
+
default:
|
|
7449
|
+
return sourceClient.replace(/[-_]/g, " ").replace(/\b\w/g, (char) => char.toUpperCase());
|
|
7450
|
+
}
|
|
7246
7451
|
}
|
|
7247
7452
|
function summarizeClientExtractions(clientExtractions) {
|
|
7248
7453
|
return clientExtractions.map((extraction, index) => {
|
|
@@ -7252,9 +7457,12 @@ function summarizeClientExtractions(clientExtractions) {
|
|
|
7252
7457
|
source_client: normalizeSourceClient(extraction.source_client),
|
|
7253
7458
|
source_label: extraction.source_label?.trim() || `${extraction.source_client || "Unknown"} AI-client extraction`,
|
|
7254
7459
|
searched_source_count: extraction.searched_sources?.length ?? 0,
|
|
7460
|
+
searched_session_count: extraction.extraction_quality?.searched_session_count ?? 0,
|
|
7461
|
+
skipped_session_count: extraction.extraction_quality?.skipped_session_count ?? 0,
|
|
7255
7462
|
query_count: extraction.search_queries?.length ?? 0,
|
|
7256
7463
|
finding_count: extraction.findings.length,
|
|
7257
|
-
confidence: normalizeConfidence(extraction.extraction_quality?.confidence, 0.74)
|
|
7464
|
+
confidence: normalizeConfidence(extraction.extraction_quality?.confidence, 0.74),
|
|
7465
|
+
notes: extraction.extraction_quality?.notes ?? []
|
|
7258
7466
|
};
|
|
7259
7467
|
});
|
|
7260
7468
|
}
|
|
@@ -7275,6 +7483,8 @@ function buildClientExtractionEvents(clientExtractions) {
|
|
|
7275
7483
|
finding_count: summary.finding_count,
|
|
7276
7484
|
query_count: summary.query_count,
|
|
7277
7485
|
searched_source_count: summary.searched_source_count,
|
|
7486
|
+
searched_session_count: summary.searched_session_count,
|
|
7487
|
+
skipped_session_count: summary.skipped_session_count,
|
|
7278
7488
|
raw_transcript_sent: false
|
|
7279
7489
|
}
|
|
7280
7490
|
}));
|
|
@@ -7283,13 +7493,14 @@ function buildClientExtractionFindings(clientExtractions) {
|
|
|
7283
7493
|
const findings = [];
|
|
7284
7494
|
clientExtractions.forEach((extraction, extractionIndex) => {
|
|
7285
7495
|
const extractionId = normalizeClientExtractionId(extraction, extractionIndex);
|
|
7286
|
-
const
|
|
7287
|
-
const
|
|
7496
|
+
const extractionSourceClient = normalizeSourceClient(extraction.source_client);
|
|
7497
|
+
const sourceLabel2 = extraction.source_label?.trim() || `${extractionSourceClient} AI-client extraction`;
|
|
7288
7498
|
extraction.findings.forEach((finding, findingIndex) => {
|
|
7289
7499
|
const type = normalizeFindingType(finding.type);
|
|
7290
7500
|
const title = finding.title?.trim();
|
|
7291
7501
|
const summary = finding.summary?.trim();
|
|
7292
7502
|
if (!type || !title || !summary) return;
|
|
7503
|
+
const sourceClient = sourceClientForExtractionFinding(extraction, finding);
|
|
7293
7504
|
findings.push({
|
|
7294
7505
|
type,
|
|
7295
7506
|
title: title.slice(0, 180),
|
|
@@ -7299,7 +7510,7 @@ function buildClientExtractionFindings(clientExtractions) {
|
|
|
7299
7510
|
evidence_ref: finding.evidence_ref?.trim() || `${extractionId}:F${findingIndex + 1}`,
|
|
7300
7511
|
confidence: normalizeConfidence(finding.confidence, normalizeConfidence(extraction.extraction_quality?.confidence, 0.76)),
|
|
7301
7512
|
metadata: {
|
|
7302
|
-
source_label: finding.source_label?.trim() ||
|
|
7513
|
+
source_label: finding.source_label?.trim() || sourceLabel2,
|
|
7303
7514
|
extraction_id: extractionId,
|
|
7304
7515
|
schema_version: extraction.schema_version ?? null,
|
|
7305
7516
|
occurred_at: finding.occurred_at ?? null,
|
|
@@ -7313,6 +7524,124 @@ function buildClientExtractionFindings(clientExtractions) {
|
|
|
7313
7524
|
});
|
|
7314
7525
|
return findings;
|
|
7315
7526
|
}
|
|
7527
|
+
function nativeExtractionFindingType(line) {
|
|
7528
|
+
const lower = line.toLowerCase();
|
|
7529
|
+
if (/^\s*(?:decision|decided|choice|approved|rejected)\s*:/i.test(line)) return "decision";
|
|
7530
|
+
if (/^\s*(?:artifact|receipt|proof)\s*:/i.test(line)) return "artifact";
|
|
7531
|
+
if (/^\s*(?:outcome|result|impact)\s*:/i.test(line)) return "outcome";
|
|
7532
|
+
if (/^\s*(?:roi|economics)\s*:/i.test(line)) return "business";
|
|
7533
|
+
if (/^\s*(?:next action|follow[- ]?up|commitment)\s*:/i.test(line)) return "action";
|
|
7534
|
+
if (/^\s*(?:blocker|risk|gap|open loop|rollback)\s*:/i.test(line)) return lower.includes("source") ? "missed_orchestration_opportunity" : "blocker";
|
|
7535
|
+
if (/\b(?:failed|fails|error|invalid|unavailable|timeout|timed out|not called|not dispatched|missing|broken)\b/i.test(line)) {
|
|
7536
|
+
if (/\b(?:tool|mcp|orgx|writeback|source|hook|outbox)\b/i.test(line)) return "missed_orchestration_opportunity";
|
|
7537
|
+
return "blocker";
|
|
7538
|
+
}
|
|
7539
|
+
if (/\b(?:created|generated|implemented|shipped|verified|passed)\b/i.test(line)) return "artifact";
|
|
7540
|
+
if (/\b(?:owner|dri|assigned)\b/i.test(line)) return "person";
|
|
7541
|
+
if (/\b(?:initiative|workstream|milestone|launch|goal)\b/i.test(line)) return "goal";
|
|
7542
|
+
return null;
|
|
7543
|
+
}
|
|
7544
|
+
function nativeEpisodeType(line) {
|
|
7545
|
+
if (/\b(?:plan|decide|decision|choice|tradeoff|approve|reject)\b/i.test(line)) return "planning";
|
|
7546
|
+
if (/\b(?:implemented|created|generated|edited|file|diff|commit|artifact)\b/i.test(line)) return "implementation";
|
|
7547
|
+
if (/\b(?:failed|fails|error|timeout|invalid|debug|retry|blocked|blocker)\b/i.test(line)) return "debugging";
|
|
7548
|
+
if (/\b(?:verified|passed|test|browser|qa|proof|outcome)\b/i.test(line)) return "verification";
|
|
7549
|
+
return "handoff";
|
|
7550
|
+
}
|
|
7551
|
+
function nativeFindingConfidence(type, line) {
|
|
7552
|
+
let confidence = type === "decision" || type === "artifact" || type === "outcome" ? 0.78 : 0.72;
|
|
7553
|
+
if (/^\s*(?:decision|artifact|blocker|outcome|proof|result|next action|open loop|risk|gap)\s*:/i.test(line)) confidence += 0.08;
|
|
7554
|
+
if (/\b(?:mcp__orgx__|scaffold_initiative|orgx_emit_activity|complete_with_proof|github|codex|claude)\b/i.test(line)) confidence += 0.05;
|
|
7555
|
+
if (line.length < 32) confidence -= 0.1;
|
|
7556
|
+
return normalizeConfidence(confidence, 0.72);
|
|
7557
|
+
}
|
|
7558
|
+
function nativeRelatedEntityNames(line) {
|
|
7559
|
+
const candidates = [
|
|
7560
|
+
...Array.from(line.matchAll(/`([^`]{3,80})`/g)).map((match) => match[1] ?? ""),
|
|
7561
|
+
...Array.from(line.matchAll(/\b(?:OrgX|MCP|Codex|Claude|Slack|GitHub|Cursor|ChatGPT|Work Graph|Profile|Scaffold Initiative|Operation QA Loop|Runtime Hook|Eject Bay|Trail Inspector)\b/gi)).map((match) => match[0] ?? "")
|
|
7562
|
+
];
|
|
7563
|
+
return sortedUnique(
|
|
7564
|
+
candidates.map((candidate) => candidate.trim()).filter((candidate) => candidate.length >= 3).slice(0, 8)
|
|
7565
|
+
);
|
|
7566
|
+
}
|
|
7567
|
+
function supportsNativeExtraction(sourceClient) {
|
|
7568
|
+
return sourceClient === "codex" || sourceClient === "claude" || sourceClient === "claude-code";
|
|
7569
|
+
}
|
|
7570
|
+
function buildNativeAiClientExtractions(imports, generatedAt = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
7571
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
7572
|
+
for (const source of imports) {
|
|
7573
|
+
const sourceClient = sourceClientForImport(source);
|
|
7574
|
+
if (!supportsNativeExtraction(sourceClient)) continue;
|
|
7575
|
+
const bucket = grouped.get(sourceClient) ?? {
|
|
7576
|
+
findings: [],
|
|
7577
|
+
searchedSources: [],
|
|
7578
|
+
searchedSessionCount: 0,
|
|
7579
|
+
skippedSessionCount: 0
|
|
7580
|
+
};
|
|
7581
|
+
bucket.searchedSources.push(source.sourceId);
|
|
7582
|
+
bucket.searchedSessionCount += 1;
|
|
7583
|
+
source.text.split(/\r?\n/).forEach((line, lineIndex) => {
|
|
7584
|
+
const trimmed = line.trim();
|
|
7585
|
+
if (!trimmed) return;
|
|
7586
|
+
const type = nativeExtractionFindingType(trimmed);
|
|
7587
|
+
if (!type) {
|
|
7588
|
+
bucket.skippedSessionCount += 1;
|
|
7589
|
+
return;
|
|
7590
|
+
}
|
|
7591
|
+
bucket.findings.push({
|
|
7592
|
+
type,
|
|
7593
|
+
title: titleFromText(trimmed, type.replace(/_/g, " ")),
|
|
7594
|
+
summary: trimmed,
|
|
7595
|
+
source_client: sourceClient,
|
|
7596
|
+
source_id: source.sourceId,
|
|
7597
|
+
source_label: source.sourceLabel,
|
|
7598
|
+
evidence_ref: `${source.sourceId}:native:${lineIndex + 1}`,
|
|
7599
|
+
confidence: nativeFindingConfidence(type, trimmed),
|
|
7600
|
+
occurred_at: generatedAt,
|
|
7601
|
+
redacted_verbatim: trimmed.slice(0, 420),
|
|
7602
|
+
privacy_state: "redacted",
|
|
7603
|
+
metadata: {
|
|
7604
|
+
episode_type: nativeEpisodeType(trimmed),
|
|
7605
|
+
related_entity_names: nativeRelatedEntityNames(trimmed),
|
|
7606
|
+
related_source_ids: [source.sourceId],
|
|
7607
|
+
state: type === "blocker" || type === "missed_orchestration_opportunity" ? "blocked" : type === "outcome" || type === "artifact" ? "verified" : "observed"
|
|
7608
|
+
}
|
|
7609
|
+
});
|
|
7610
|
+
});
|
|
7611
|
+
grouped.set(sourceClient, bucket);
|
|
7612
|
+
}
|
|
7613
|
+
return [...grouped.entries()].filter(([, bucket]) => bucket.findings.length > 0).map(([sourceClient, bucket]) => ({
|
|
7614
|
+
schema_version: WORK_GRAPH_EXTRACTION_SCHEMA_VERSION,
|
|
7615
|
+
extraction_id: `${sourceClient}:native-extraction:${shortHash({
|
|
7616
|
+
sources: bucket.searchedSources,
|
|
7617
|
+
findings: bucket.findings.map((finding) => finding.evidence_ref)
|
|
7618
|
+
}, 12)}`,
|
|
7619
|
+
source_client: sourceClient,
|
|
7620
|
+
source_label: `${labelForSourceClient(sourceClient)} native extraction`,
|
|
7621
|
+
searched_sources: sortedUnique(bucket.searchedSources),
|
|
7622
|
+
search_queries: [
|
|
7623
|
+
{
|
|
7624
|
+
id: "native_session_episodes",
|
|
7625
|
+
lens: `${labelForSourceClient(sourceClient)} episode extraction`,
|
|
7626
|
+
query: "Extract decisions, artifacts, blockers, outcomes, handoffs, and missed OrgX writeback from local AI-client session lines.",
|
|
7627
|
+
result_count: bucket.findings.length
|
|
7628
|
+
}
|
|
7629
|
+
],
|
|
7630
|
+
extraction_quality: {
|
|
7631
|
+
confidence: normalizeConfidence(
|
|
7632
|
+
bucket.findings.reduce((total, finding) => total + (finding.confidence ?? 0.72), 0) / bucket.findings.length,
|
|
7633
|
+
0.74
|
|
7634
|
+
),
|
|
7635
|
+
searched_session_count: bucket.searchedSessionCount,
|
|
7636
|
+
skipped_session_count: bucket.skippedSessionCount,
|
|
7637
|
+
notes: [
|
|
7638
|
+
"Auto-generated by orgx-wizard from local AI-client session imports.",
|
|
7639
|
+
"Raw transcripts are excluded; only redacted evidence snippets and source refs are retained."
|
|
7640
|
+
]
|
|
7641
|
+
},
|
|
7642
|
+
findings: bucket.findings
|
|
7643
|
+
}));
|
|
7644
|
+
}
|
|
7316
7645
|
function buildDerivedFindings(imports) {
|
|
7317
7646
|
const findings = [];
|
|
7318
7647
|
let derivedIndex = 0;
|
|
@@ -7466,9 +7795,84 @@ function scoreOpportunity(coverage, findings) {
|
|
|
7466
7795
|
orgx_fit: orgxFit
|
|
7467
7796
|
};
|
|
7468
7797
|
}
|
|
7798
|
+
function estimateImpactProjection(input) {
|
|
7799
|
+
const { coverage, findings, opportunityScore, patterns, trails } = input;
|
|
7800
|
+
const blockerEvents = trails.filter((trail) => trail.subject_entity_type === "blocker").reduce((total, trail) => total + Math.max(1, trail.events.length), 0);
|
|
7801
|
+
const decisionEvents = trails.filter((trail) => trail.subject_entity_type === "decision").reduce((total, trail) => total + Math.max(1, trail.events.length), 0);
|
|
7802
|
+
const artifactEvents = trails.filter((trail) => trail.subject_entity_type === "artifact").reduce((total, trail) => total + Math.max(1, trail.events.length), 0);
|
|
7803
|
+
const missedCount = findings.filter((finding) => finding.type === "missed_orchestration_opportunity").length;
|
|
7804
|
+
const repeatedEvents = trails.filter((trail) => trail.events.length > 1).reduce((total, trail) => total + trail.events.length, 0);
|
|
7805
|
+
const rawHours = blockerEvents * 0.75 + decisionEvents * 0.45 + artifactEvents * 0.25 + missedCount * 0.65 + repeatedEvents * 0.35 + coverage.missing.length * 0.4;
|
|
7806
|
+
const hours = Number(Math.max(0.5, Math.min(40, rawHours)).toFixed(1));
|
|
7807
|
+
const acceleration = clampScore2(
|
|
7808
|
+
6 + Math.round(opportunityScore.automation_potential * 0.22) + patterns.filter((pattern) => pattern.severity === "critical" || pattern.severity === "high").length * 5 + (coverage.manifests?.filter((manifest) => manifest.status === "connected").length ?? 0) * 2
|
|
7809
|
+
);
|
|
7810
|
+
const confidenceInputs = [
|
|
7811
|
+
...findings.map((finding) => finding.confidence),
|
|
7812
|
+
...patterns.map((pattern) => pattern.confidence),
|
|
7813
|
+
coverage.coverage_score ? coverage.coverage_score / 100 : 0.55
|
|
7814
|
+
];
|
|
7815
|
+
const confidence = Number((confidenceInputs.reduce((total, value) => total + value, 0) / Math.max(1, confidenceInputs.length)).toFixed(2));
|
|
7816
|
+
return {
|
|
7817
|
+
time_saved_hours_per_week: hours,
|
|
7818
|
+
acceleration_percent: Math.min(70, acceleration),
|
|
7819
|
+
estimated_monthly_value_usd: Math.round(hours * 4.33 * 200),
|
|
7820
|
+
confidence,
|
|
7821
|
+
basis: [
|
|
7822
|
+
`${blockerEvents} blocker event${blockerEvents === 1 ? "" : "s"} at 45 minutes of reconstruction/coordination each.`,
|
|
7823
|
+
`${decisionEvents} decision event${decisionEvents === 1 ? "" : "s"} at 27 minutes of rediscovery/promotion each.`,
|
|
7824
|
+
`${missedCount} missed orchestration signal${missedCount === 1 ? "" : "s"} at 39 minutes of manual writeback each.`,
|
|
7825
|
+
`${coverage.missing.length} missing source${coverage.missing.length === 1 ? "" : "s"} reducing attribution confidence.`
|
|
7826
|
+
],
|
|
7827
|
+
assumptions: [
|
|
7828
|
+
"Uses a conservative $200/hour blended founder/operator cost for public-safe value estimates.",
|
|
7829
|
+
"Acceleration estimates the execution lift from turning repeated trails into automated attribution, decisions, and source connections.",
|
|
7830
|
+
"Impact is directional until the user claims the fingerprint and confirms or corrects trail evidence."
|
|
7831
|
+
]
|
|
7832
|
+
};
|
|
7833
|
+
}
|
|
7834
|
+
function scoreExecutionQuality(input) {
|
|
7835
|
+
const { coverage, findings, impact, patterns, recommendations, trails } = input;
|
|
7836
|
+
const uniqueClients = sortedUnique(findings.map((finding) => finding.source_client));
|
|
7837
|
+
const unknownCount = findings.filter((finding) => finding.source_client === "unknown").length;
|
|
7838
|
+
const multiEventTrails = trails.filter((trail) => trail.events.length > 1).length;
|
|
7839
|
+
const evidenceCoverage = clampScore2(
|
|
7840
|
+
(coverage.coverage_score ?? 50) + Math.min(18, coverage.connected.length * 3) - coverage.missing.length * 4
|
|
7841
|
+
);
|
|
7842
|
+
const sourceAttribution = clampScore2(
|
|
7843
|
+
34 + uniqueClients.filter((client) => client !== "unknown").length * 10 + (coverage.manifests?.filter((manifest) => manifest.status === "connected").length ?? 0) * 6 - unknownCount * 8
|
|
7844
|
+
);
|
|
7845
|
+
const trailDepth = clampScore2(
|
|
7846
|
+
25 + (trails.length > 0 ? Math.round(multiEventTrails / trails.length * 45) : 0) + Math.min(20, trails.reduce((total, trail) => total + trail.events.length, 0) * 2)
|
|
7847
|
+
);
|
|
7848
|
+
const insightDepth = clampScore2(
|
|
7849
|
+
30 + Math.min(28, patterns.length * 8) + Math.min(18, findings.filter((finding) => ["blocker", "decision", "outcome", "missed_orchestration_opportunity"].includes(finding.type)).length * 3)
|
|
7850
|
+
);
|
|
7851
|
+
const actionability = clampScore2(
|
|
7852
|
+
30 + Math.min(35, recommendations.length * 10) + Math.min(20, recommendations.filter((recommendation) => recommendation.priority === "p0").length * 10)
|
|
7853
|
+
);
|
|
7854
|
+
const impactConfidence = clampScore2(impact.confidence * 100);
|
|
7855
|
+
const overall = clampScore2(
|
|
7856
|
+
evidenceCoverage * 0.22 + sourceAttribution * 0.18 + trailDepth * 0.18 + insightDepth * 0.18 + actionability * 0.14 + impactConfidence * 0.1
|
|
7857
|
+
);
|
|
7858
|
+
return {
|
|
7859
|
+
overall,
|
|
7860
|
+
evidence_coverage: evidenceCoverage,
|
|
7861
|
+
source_attribution: sourceAttribution,
|
|
7862
|
+
trail_depth: trailDepth,
|
|
7863
|
+
insight_depth: insightDepth,
|
|
7864
|
+
actionability,
|
|
7865
|
+
impact_confidence: impactConfidence,
|
|
7866
|
+
notes: [
|
|
7867
|
+
"10/10 requires client-native extraction per source, multi-event trails, source-specific evidence, and action recommendations tied to impact.",
|
|
7868
|
+
multiEventTrails === trails.length ? "Every trail has multiple evidence events." : `${trails.length - multiEventTrails} trail${trails.length - multiEventTrails === 1 ? "" : "s"} still need more chronology before they should be called recurring.`,
|
|
7869
|
+
coverage.missing.length > 0 ? `Missing source coverage: ${coverage.missing.join(", ")}.` : "No required source gaps were declared for this run."
|
|
7870
|
+
]
|
|
7871
|
+
};
|
|
7872
|
+
}
|
|
7469
7873
|
function inferFinalState(findings) {
|
|
7470
7874
|
if (findings.some((finding) => finding.type === "blocker")) return "blocked";
|
|
7471
|
-
if (findings.some((finding) => finding.type === "artifact" || /\b(shipped|completed|done|verified)\b/i.test(finding.summary))) {
|
|
7875
|
+
if (findings.some((finding) => finding.type === "artifact" || finding.type === "outcome" || /\b(shipped|completed|done|verified)\b/i.test(finding.summary))) {
|
|
7472
7876
|
return "completed";
|
|
7473
7877
|
}
|
|
7474
7878
|
if (findings.some((finding) => finding.type === "action" || finding.type === "decision")) return "in_progress";
|
|
@@ -7534,6 +7938,8 @@ function entityTypeForFinding(finding) {
|
|
|
7534
7938
|
case "goal":
|
|
7535
7939
|
case "initiative_candidate":
|
|
7536
7940
|
return "initiative";
|
|
7941
|
+
case "outcome":
|
|
7942
|
+
return "outcome";
|
|
7537
7943
|
case "action":
|
|
7538
7944
|
return /\b(outcome|shipped|completed|verified)\b/i.test(finding.summary) ? "outcome" : "task";
|
|
7539
7945
|
case "missed_orchestration_opportunity":
|
|
@@ -7586,6 +7992,8 @@ function eventTypeForFinding(finding) {
|
|
|
7586
7992
|
case "goal":
|
|
7587
7993
|
case "initiative_candidate":
|
|
7588
7994
|
return "initiative_created";
|
|
7995
|
+
case "outcome":
|
|
7996
|
+
return "outcome_recorded";
|
|
7589
7997
|
case "action":
|
|
7590
7998
|
return /\b(outcome|result|impact|roi)\b/i.test(finding.summary) ? "outcome_recorded" : "recommendation_generated";
|
|
7591
7999
|
case "missed_orchestration_opportunity":
|
|
@@ -7596,7 +8004,7 @@ function trailStateForFindings(findings) {
|
|
|
7596
8004
|
if (findings.some((finding) => finding.type === "blocker")) return "blocked";
|
|
7597
8005
|
if (findings.some((finding) => finding.type === "missed_orchestration_opportunity")) return "missing_evidence";
|
|
7598
8006
|
if (findings.some((finding) => /\b(contradict|conflict)\b/i.test(finding.summary))) return "contradicted";
|
|
7599
|
-
if (findings.some((finding) => /\b(verified|proof|passed|complete_with_proof)\b/i.test(finding.summary))) return "verified";
|
|
8007
|
+
if (findings.some((finding) => finding.type === "outcome" || /\b(verified|proof|passed|complete_with_proof)\b/i.test(finding.summary))) return "verified";
|
|
7600
8008
|
if (findings.some((finding) => finding.type === "decision")) return "inferred";
|
|
7601
8009
|
return "observed";
|
|
7602
8010
|
}
|
|
@@ -7604,6 +8012,7 @@ function trailValenceForFindings(findings, recurrence) {
|
|
|
7604
8012
|
if (findings.some((finding) => finding.type === "blocker")) return recurrence > 1 ? "escalating" : "risk";
|
|
7605
8013
|
if (findings.some((finding) => finding.type === "missed_orchestration_opportunity")) return "leak";
|
|
7606
8014
|
if (findings.some((finding) => finding.type === "decision") && recurrence > 1) return "wasteful_recurrence";
|
|
8015
|
+
if (findings.some((finding) => finding.type === "outcome")) return "healthy";
|
|
7607
8016
|
if (findings.some((finding) => finding.type === "business")) return "opportunity";
|
|
7608
8017
|
if (findings.some((finding) => finding.type === "artifact")) return "healthy";
|
|
7609
8018
|
return recurrence > 1 ? "useful_recurrence" : "opportunity";
|
|
@@ -7611,35 +8020,93 @@ function trailValenceForFindings(findings, recurrence) {
|
|
|
7611
8020
|
function trailShapeForFindings(findings, recurrence) {
|
|
7612
8021
|
const decisions = findings.filter((finding) => finding.type === "decision").length;
|
|
7613
8022
|
const artifacts = findings.filter((finding) => finding.type === "artifact").length;
|
|
8023
|
+
if (recurrence <= 1) return "single_signal";
|
|
7614
8024
|
if (decisions > artifacts && decisions > 0) return "decision_heavy_artifact_light";
|
|
7615
8025
|
if (artifacts > decisions && artifacts > 0 && decisions === 0) return "artifact_heavy_decision_light";
|
|
7616
|
-
if (findings.some((finding) => finding.type === "blocker")) return "accelerating_issue";
|
|
8026
|
+
if (findings.some((finding) => finding.type === "blocker")) return recurrence >= 3 ? "accelerating_issue" : "dense_recent_cluster";
|
|
7617
8027
|
if (recurrence >= 3) return "chronic_recurrence";
|
|
7618
8028
|
if (findings.some((finding) => /\b(resurface|again|back|revived|reappeared)\b/i.test(finding.summary))) return "zombie_revival";
|
|
7619
|
-
if (findings.some((finding) => /\b(verified|passed|shipped|completed)\b/i.test(finding.summary))) return "healthy_execution";
|
|
8029
|
+
if (findings.some((finding) => finding.type === "outcome" || /\b(verified|passed|shipped|completed)\b/i.test(finding.summary))) return "healthy_execution";
|
|
7620
8030
|
return "dense_recent_cluster";
|
|
7621
8031
|
}
|
|
8032
|
+
function findingTimestamp(finding, fallback) {
|
|
8033
|
+
const occurredAt = finding.metadata.occurred_at;
|
|
8034
|
+
return isIsoTimestamp(occurredAt) ? occurredAt : fallback;
|
|
8035
|
+
}
|
|
8036
|
+
function canonicalRelatedToken(finding) {
|
|
8037
|
+
const candidates = [
|
|
8038
|
+
...arrayOfStrings(finding.metadata.related_source_ids),
|
|
8039
|
+
...arrayOfStrings(finding.metadata.related_entity_names),
|
|
8040
|
+
finding.source_id,
|
|
8041
|
+
finding.title
|
|
8042
|
+
];
|
|
8043
|
+
const scored = candidates.map((candidate) => candidate.trim()).filter(Boolean).map((candidate) => {
|
|
8044
|
+
const normalized = candidate.toLowerCase();
|
|
8045
|
+
let score = 0;
|
|
8046
|
+
if (/_/.test(candidate)) score += 6;
|
|
8047
|
+
if (/orgx|mcp|wizard|plugin|runtime|hook|work graph|profile|trace|scaffold|ship|spawn|record|submit|list/i.test(candidate)) score += 5;
|
|
8048
|
+
if (/session|jsonl|rollout|line|current-release/i.test(normalized)) score -= 4;
|
|
8049
|
+
if (candidate.length > 80) score -= 2;
|
|
8050
|
+
return { candidate, score };
|
|
8051
|
+
}).filter((item) => item.score > 0).sort((left, right) => right.score - left.score);
|
|
8052
|
+
return scored[0]?.candidate ?? null;
|
|
8053
|
+
}
|
|
8054
|
+
function groupingKeyForFinding(finding) {
|
|
8055
|
+
const entityType = entityTypeForFinding(finding);
|
|
8056
|
+
const token = canonicalRelatedToken(finding);
|
|
8057
|
+
if (token && ["blocker", "source", "surface", "initiative", "outcome", "task"].includes(entityType)) {
|
|
8058
|
+
return `${entityType}:${slugPart(token)}`;
|
|
8059
|
+
}
|
|
8060
|
+
return `${entityType}:${slugPart(finding.title)}`;
|
|
8061
|
+
}
|
|
8062
|
+
function trailTitleForGroup(entityType, group) {
|
|
8063
|
+
const token = canonicalRelatedToken(group[0]);
|
|
8064
|
+
if (group.length > 1 && token) {
|
|
8065
|
+
const label = token.replace(/[-_]/g, " ").replace(/\b\w/g, (char) => char.toUpperCase());
|
|
8066
|
+
if (entityType === "blocker") return `${label} is recurring as an execution blocker`;
|
|
8067
|
+
if (entityType === "source") return `${label} is an attribution coverage gap`;
|
|
8068
|
+
if (entityType === "surface") return `${label} is becoming a product surface trail`;
|
|
8069
|
+
if (entityType === "outcome") return `${label} is becoming outcome evidence`;
|
|
8070
|
+
return `${label} is becoming durable work`;
|
|
8071
|
+
}
|
|
8072
|
+
return group[0].title;
|
|
8073
|
+
}
|
|
8074
|
+
function trailSummaryForGroup(group) {
|
|
8075
|
+
if (group.length === 1) return group[0].summary;
|
|
8076
|
+
const sources = sortedUnique(group.map((finding) => labelForSourceClient(finding.source_client)));
|
|
8077
|
+
const states = sortedUnique(
|
|
8078
|
+
group.map((finding) => typeof finding.metadata.state === "string" ? finding.metadata.state : "").filter(Boolean)
|
|
8079
|
+
);
|
|
8080
|
+
return [
|
|
8081
|
+
`${group.length} evidence events connect this trail across ${sources.join(", ")}.`,
|
|
8082
|
+
states.length > 0 ? `Observed states: ${states.join(", ")}.` : "",
|
|
8083
|
+
group[0]?.summary ?? ""
|
|
8084
|
+
].filter(Boolean).join(" ");
|
|
8085
|
+
}
|
|
7622
8086
|
function buildWorkGraphTrails(findings, generatedAt) {
|
|
7623
8087
|
const grouped = /* @__PURE__ */ new Map();
|
|
7624
8088
|
for (const finding of findings) {
|
|
7625
|
-
const
|
|
7626
|
-
const key = `${entityType}:${slugPart(finding.title)}`;
|
|
8089
|
+
const key = groupingKeyForFinding(finding);
|
|
7627
8090
|
grouped.set(key, [...grouped.get(key) ?? [], finding]);
|
|
7628
8091
|
}
|
|
7629
8092
|
return [...grouped.entries()].map(([key, group], index) => {
|
|
7630
|
-
const
|
|
8093
|
+
const sortedGroup = [...group].sort(
|
|
8094
|
+
(left, right) => findingTimestamp(left, generatedAt).localeCompare(findingTimestamp(right, generatedAt))
|
|
8095
|
+
);
|
|
8096
|
+
const first = sortedGroup[0];
|
|
7631
8097
|
const entityType = entityTypeForFinding(first);
|
|
7632
8098
|
const entityId = `${entityType}:${shortHash(key, 12)}`;
|
|
7633
|
-
const trailId = `trail:${shortHash({ key, evidence:
|
|
7634
|
-
const recurrence =
|
|
7635
|
-
const evidenceRefs = sortedUnique(
|
|
7636
|
-
const events =
|
|
8099
|
+
const trailId = `trail:${shortHash({ key, evidence: sortedGroup.map((finding) => finding.evidence_ref) }, 14)}`;
|
|
8100
|
+
const recurrence = sortedGroup.length;
|
|
8101
|
+
const evidenceRefs = sortedUnique(sortedGroup.map((finding) => finding.evidence_ref));
|
|
8102
|
+
const events = sortedGroup.map((finding, eventIndex) => ({
|
|
7637
8103
|
id: `${trailId}:event:${eventIndex + 1}`,
|
|
7638
8104
|
trail_id: trailId,
|
|
7639
8105
|
event_type: eventTypeForFinding(finding),
|
|
7640
8106
|
entity_id: entityId,
|
|
7641
8107
|
entity_type: entityType,
|
|
7642
|
-
timestamp: generatedAt,
|
|
8108
|
+
timestamp: findingTimestamp(finding, generatedAt),
|
|
8109
|
+
...typeof finding.metadata.actor_id === "string" ? { actor_id: finding.metadata.actor_id } : {},
|
|
7643
8110
|
source_id: finding.source_id,
|
|
7644
8111
|
source_type: finding.source_client,
|
|
7645
8112
|
redacted_verbatim: finding.summary.slice(0, 320),
|
|
@@ -7656,28 +8123,30 @@ function buildWorkGraphTrails(findings, generatedAt) {
|
|
|
7656
8123
|
confidence: Math.min(0.92, Math.max(0.58, event.confidence - 0.04)),
|
|
7657
8124
|
evidence_refs: event.evidence_refs
|
|
7658
8125
|
}));
|
|
7659
|
-
const confidence =
|
|
8126
|
+
const confidence = sortedGroup.reduce((total, finding) => total + finding.confidence, 0) / sortedGroup.length;
|
|
8127
|
+
const createdAt = events[0]?.timestamp ?? generatedAt;
|
|
8128
|
+
const updatedAt = events[events.length - 1]?.timestamp ?? generatedAt;
|
|
7660
8129
|
return {
|
|
7661
8130
|
id: trailId,
|
|
7662
8131
|
kind: trailKindForEntity(entityType),
|
|
7663
|
-
title:
|
|
7664
|
-
summary:
|
|
8132
|
+
title: trailTitleForGroup(entityType, sortedGroup),
|
|
8133
|
+
summary: trailSummaryForGroup(sortedGroup),
|
|
7665
8134
|
subject_entity_id: entityId,
|
|
7666
8135
|
subject_entity_type: entityType,
|
|
7667
|
-
state: trailStateForFindings(
|
|
7668
|
-
valence: trailValenceForFindings(
|
|
8136
|
+
state: trailStateForFindings(sortedGroup),
|
|
8137
|
+
valence: trailValenceForFindings(sortedGroup, recurrence),
|
|
7669
8138
|
confidence: Number(confidence.toFixed(2)),
|
|
7670
|
-
recurrence_score: Math.min(100, recurrence *
|
|
8139
|
+
recurrence_score: recurrence <= 1 ? Math.min(24, evidenceRefs.length * 8 + 10) : Math.min(100, recurrence * 24 + evidenceRefs.length * 7),
|
|
7671
8140
|
impact_score: Math.min(100, Math.round(first.confidence * 70) + recurrence * 8 + (index < 4 ? 10 : 0)),
|
|
7672
8141
|
privacy_state: "redacted",
|
|
7673
8142
|
events,
|
|
7674
8143
|
edges,
|
|
7675
8144
|
evidence_refs: evidenceRefs,
|
|
7676
|
-
blocker_ids:
|
|
8145
|
+
blocker_ids: sortedGroup.filter((finding) => finding.type === "blocker").map((finding) => finding.evidence_ref),
|
|
7677
8146
|
recommendation_ids: [],
|
|
7678
|
-
created_at:
|
|
7679
|
-
updated_at:
|
|
7680
|
-
shape: trailShapeForFindings(
|
|
8147
|
+
created_at: createdAt,
|
|
8148
|
+
updated_at: updatedAt,
|
|
8149
|
+
shape: trailShapeForFindings(sortedGroup, recurrence)
|
|
7681
8150
|
};
|
|
7682
8151
|
});
|
|
7683
8152
|
}
|
|
@@ -7796,9 +8265,12 @@ function buildRecurringPatterns(coverage, findings, trails) {
|
|
|
7796
8265
|
});
|
|
7797
8266
|
}
|
|
7798
8267
|
if (blockerTrails.length > 0) {
|
|
8268
|
+
const topBlockerTrail = [...blockerTrails].sort(
|
|
8269
|
+
(left, right) => right.events.length - left.events.length || right.impact_score - left.impact_score
|
|
8270
|
+
)[0];
|
|
7799
8271
|
patterns.push({
|
|
7800
8272
|
id: "pattern:handoff-friction",
|
|
7801
|
-
title: "Blockers are becoming handoff friction",
|
|
8273
|
+
title: topBlockerTrail && topBlockerTrail.events.length > 1 ? `${topBlockerTrail.title} and needs owner-visible resolution` : "Blockers are becoming handoff friction",
|
|
7802
8274
|
description: `${blockerTrails.length} blocker trail${blockerTrails.length === 1 ? "" : "s"} need owner-visible resolution.`,
|
|
7803
8275
|
pattern_type: "handoff_friction",
|
|
7804
8276
|
affected_trail_ids: blockerTrails.map((trail) => trail.id),
|
|
@@ -7872,6 +8344,18 @@ function buildTrailRecommendations(patterns, trails) {
|
|
|
7872
8344
|
expected_lift: "+continuous attribution",
|
|
7873
8345
|
confidence: pattern.confidence
|
|
7874
8346
|
});
|
|
8347
|
+
} else if (pattern.pattern_type === "handoff_friction") {
|
|
8348
|
+
add({
|
|
8349
|
+
id: "recommendation:resolve-blocker-handoff",
|
|
8350
|
+
title: "Resolve the blocker handoff trail",
|
|
8351
|
+
summary: "Promote the recurring blocker path into assigned work with proof requirements and a clear owner.",
|
|
8352
|
+
action_type: "assign_owner",
|
|
8353
|
+
trail_ids: pattern.affected_trail_ids,
|
|
8354
|
+
evidence_refs: evidenceRefs,
|
|
8355
|
+
priority: pattern.severity === "critical" || pattern.severity === "high" ? "p0" : "p1",
|
|
8356
|
+
expected_lift: "+execution continuity",
|
|
8357
|
+
confidence: pattern.confidence
|
|
8358
|
+
});
|
|
7875
8359
|
} else if (pattern.pattern_type === "business_signal_unclaimed" || pattern.pattern_type === "repeated_work") {
|
|
7876
8360
|
add({
|
|
7877
8361
|
id: "recommendation:launch-initiative",
|
|
@@ -7900,17 +8384,24 @@ function buildTrailRecommendations(patterns, trails) {
|
|
|
7900
8384
|
confidence: topTrail.confidence
|
|
7901
8385
|
});
|
|
7902
8386
|
}
|
|
7903
|
-
|
|
8387
|
+
const priorityRank = { p0: 0, p1: 1, p2: 2 };
|
|
8388
|
+
return recommendations.sort(
|
|
8389
|
+
(left, right) => priorityRank[left.priority] - priorityRank[right.priority] || right.confidence - left.confidence
|
|
8390
|
+
).slice(0, 5);
|
|
7904
8391
|
}
|
|
7905
8392
|
function buildWorkGraphMirror(input) {
|
|
7906
|
-
const { coverage, generatedAt, patterns, recommendations, trails } = input;
|
|
8393
|
+
const { coverage, generatedAt, impact, patterns, recommendations, trails } = input;
|
|
7907
8394
|
const topTrail = [...trails].sort((a, b) => b.impact_score - a.impact_score)[0];
|
|
7908
8395
|
const topPattern = [...patterns].sort((a, b) => b.recurrence_count - a.recurrence_count)[0];
|
|
7909
8396
|
const decisionCount = trailsForType(trails, "decision").length;
|
|
7910
8397
|
const artifactCount = trailsForType(trails, "artifact").length;
|
|
7911
8398
|
const blockerCount = trailsForType(trails, "blocker").length;
|
|
7912
8399
|
const sourceGapCount = coverage.missing.length;
|
|
7913
|
-
const
|
|
8400
|
+
const sourceClients = sortedUnique(
|
|
8401
|
+
trails.flatMap((trail) => trail.events.map((event) => event.source_type))
|
|
8402
|
+
);
|
|
8403
|
+
const sourcePhrase = sourceClients.length > 0 ? sourceClients.map(labelForSourceClient).join(", ") : coverage.connected.join(", ") || "local sources";
|
|
8404
|
+
const headline = blockerCount > 0 ? "Your AI work is shipping, but the execution trail is breaking" : sourceGapCount > 0 ? "Your work leaves trails, but the proof chain is still incomplete" : topPattern ? "Your work has a recurring operating pattern worth preserving" : topTrail ? "Your work is becoming an operating profile" : "Your work is leaving an operating trail";
|
|
7914
8405
|
const primaryClaimRefs = topTrail?.evidence_refs ?? [];
|
|
7915
8406
|
const claims = [
|
|
7916
8407
|
{
|
|
@@ -7927,17 +8418,25 @@ function buildWorkGraphMirror(input) {
|
|
|
7927
8418
|
},
|
|
7928
8419
|
{
|
|
7929
8420
|
id: "mirror:missing-sources",
|
|
7930
|
-
text: sourceGapCount > 0 ? `${sourceGapCount} source gap${sourceGapCount === 1 ? "" : "s"} still limit attribution depth.` : "The connected sources are enough for a first operating profile.",
|
|
8421
|
+
text: sourceGapCount > 0 ? `${sourceGapCount} source gap${sourceGapCount === 1 ? "" : "s"} still limit attribution depth.` : "The connected sources are enough for a first operating profile, pending human review.",
|
|
7931
8422
|
evidence_refs: trailsForType(trails, "source").flatMap((trail) => trail.evidence_refs).slice(0, 4),
|
|
7932
8423
|
confidence: sourceGapCount > 0 ? 0.76 : 0.66
|
|
7933
|
-
}
|
|
8424
|
+
},
|
|
8425
|
+
...impact ? [{
|
|
8426
|
+
id: "mirror:impact",
|
|
8427
|
+
text: `${impact.time_saved_hours_per_week} hours/week and +${impact.acceleration_percent}% execution acceleration are recoverable if the top trails become durable.`,
|
|
8428
|
+
evidence_refs: trails.flatMap((trail) => trail.evidence_refs).slice(0, 6),
|
|
8429
|
+
confidence: impact.confidence
|
|
8430
|
+
}] : []
|
|
7934
8431
|
];
|
|
7935
8432
|
const body = [
|
|
7936
|
-
`OrgX found ${trails.length} evidence trail${trails.length === 1 ? "" : "s"}
|
|
7937
|
-
`The strongest
|
|
7938
|
-
blockerCount > 0 ? `${blockerCount} blocker trail${blockerCount === 1 ? "" : "s"}
|
|
7939
|
-
|
|
7940
|
-
|
|
8433
|
+
`You have real work moving across ${sourcePhrase}: OrgX found ${trails.length} evidence trail${trails.length === 1 ? "" : "s"} without requiring OrgX tool calls to be present.`,
|
|
8434
|
+
topPattern ? `The strongest recurring pattern is ${topPattern.title.toLowerCase()}, with ${topPattern.recurrence_count} appearance${topPattern.recurrence_count === 1 ? "" : "s"} still needing durable writeback.` : topTrail ? `The clearest trail is ${topTrail.title.toLowerCase()}, but it still needs stronger chronology before it becomes durable operating memory.` : "The first profile is forming from sparse evidence and should be reviewed before promotion.",
|
|
8435
|
+
blockerCount > 0 ? `${blockerCount} blocker trail${blockerCount === 1 ? "" : "s"} keep work returning as new work instead of becoming owner-visible resolution.` : `${decisionCount} decision trail${decisionCount === 1 ? "" : "s"} and ${artifactCount} artifact trail${artifactCount === 1 ? "" : "s"} already show where work can be preserved.`,
|
|
8436
|
+
sourceGapCount > 0 ? `${sourceGapCount} source gap${sourceGapCount === 1 ? "" : "s"} still limit trust, especially ownership, handoffs, and later verification.` : "The connected sources are enough for a first public-safe profile, pending human review.",
|
|
8437
|
+
impact ? `Left unresolved, the profile estimates ${impact.time_saved_hours_per_week} recoverable hours/week, +${impact.acceleration_percent}% acceleration, and about $${impact.estimated_monthly_value_usd.toLocaleString("en-US")}/month in operator leverage.` : "",
|
|
8438
|
+
recommendations[0] ? `The next repair is concrete: ${recommendations[0].title}.` : "The next move is to inspect the highest-confidence trail before publishing it."
|
|
8439
|
+
].filter(Boolean).join(" ");
|
|
7941
8440
|
return {
|
|
7942
8441
|
headline,
|
|
7943
8442
|
body,
|
|
@@ -7948,13 +8447,39 @@ function buildWorkGraphMirror(input) {
|
|
|
7948
8447
|
};
|
|
7949
8448
|
}
|
|
7950
8449
|
function buildTensionMetrics(input) {
|
|
7951
|
-
const { coverage, patterns, trails } = input;
|
|
8450
|
+
const { coverage, impact, patterns, quality, trails } = input;
|
|
7952
8451
|
const decisionTrails = trailsForType(trails, "decision");
|
|
7953
8452
|
const blockerTrails = trailsForType(trails, "blocker");
|
|
7954
8453
|
const artifactTrails = trailsForType(trails, "artifact");
|
|
7955
8454
|
const missingSourceTrails = trailsForType(trails, "source");
|
|
7956
8455
|
const topReady = trails.filter((trail) => trail.impact_score >= 70 && trail.confidence >= 0.75);
|
|
7957
8456
|
return [
|
|
8457
|
+
...quality ? [{
|
|
8458
|
+
id: "tension:quality-score",
|
|
8459
|
+
label: "quality score",
|
|
8460
|
+
value: `${quality.overall}/100`,
|
|
8461
|
+
tone: quality.overall >= 82 ? "good" : quality.overall >= 65 ? "warning" : "danger",
|
|
8462
|
+
trail_ids: trails.slice(0, 8).map((trail) => trail.id),
|
|
8463
|
+
evidence_refs: trails.flatMap((trail) => trail.evidence_refs).slice(0, 8),
|
|
8464
|
+
explanation: "Weighted audit quality across evidence coverage, attribution, trail depth, insight depth, actionability, and impact confidence."
|
|
8465
|
+
}] : [],
|
|
8466
|
+
...impact ? [{
|
|
8467
|
+
id: "tension:time-saved",
|
|
8468
|
+
label: "hours recoverable",
|
|
8469
|
+
value: `${impact.time_saved_hours_per_week}h/wk`,
|
|
8470
|
+
tone: impact.time_saved_hours_per_week >= 4 ? "warning" : "muted",
|
|
8471
|
+
trail_ids: patterns.flatMap((pattern) => pattern.affected_trail_ids).slice(0, 8),
|
|
8472
|
+
evidence_refs: trails.flatMap((trail) => trail.evidence_refs).slice(0, 8),
|
|
8473
|
+
explanation: "Estimated time recovered by promoting repeated decisions, blockers, artifacts, and writeback gaps into durable operating memory."
|
|
8474
|
+
}, {
|
|
8475
|
+
id: "tension:acceleration",
|
|
8476
|
+
label: "acceleration",
|
|
8477
|
+
value: `+${impact.acceleration_percent}%`,
|
|
8478
|
+
tone: impact.acceleration_percent >= 30 ? "good" : "muted",
|
|
8479
|
+
trail_ids: patterns.flatMap((pattern) => pattern.affected_trail_ids).slice(0, 8),
|
|
8480
|
+
evidence_refs: trails.flatMap((trail) => trail.evidence_refs).slice(0, 8),
|
|
8481
|
+
explanation: "Directional execution lift from connecting source evidence, writeback, owners, and action recommendations."
|
|
8482
|
+
}] : [],
|
|
7958
8483
|
{
|
|
7959
8484
|
id: "tension:work-leaks",
|
|
7960
8485
|
label: "work leaks",
|
|
@@ -8011,6 +8536,317 @@ function countFindingsByType(findings) {
|
|
|
8011
8536
|
Object.entries(counts).sort(([left], [right]) => left.localeCompare(right))
|
|
8012
8537
|
);
|
|
8013
8538
|
}
|
|
8539
|
+
function isIsoTimestamp(value) {
|
|
8540
|
+
return typeof value === "string" && !Number.isNaN(Date.parse(value));
|
|
8541
|
+
}
|
|
8542
|
+
function redactionStateForFinding(finding) {
|
|
8543
|
+
const privacy = typeof finding.metadata.privacy_state === "string" ? finding.metadata.privacy_state : "";
|
|
8544
|
+
if (privacy === "private") return "private";
|
|
8545
|
+
if (privacy === "redacted") return "redacted";
|
|
8546
|
+
return "public_summary";
|
|
8547
|
+
}
|
|
8548
|
+
function attributionKindForFinding(type) {
|
|
8549
|
+
switch (type) {
|
|
8550
|
+
case "action":
|
|
8551
|
+
case "blocker":
|
|
8552
|
+
case "outcome":
|
|
8553
|
+
return "action";
|
|
8554
|
+
case "decision":
|
|
8555
|
+
case "artifact":
|
|
8556
|
+
case "person":
|
|
8557
|
+
case "business":
|
|
8558
|
+
case "product_surface":
|
|
8559
|
+
case "goal":
|
|
8560
|
+
return type;
|
|
8561
|
+
case "initiative_candidate":
|
|
8562
|
+
return "goal";
|
|
8563
|
+
case "missed_orchestration_opportunity":
|
|
8564
|
+
return "source";
|
|
8565
|
+
default:
|
|
8566
|
+
return null;
|
|
8567
|
+
}
|
|
8568
|
+
}
|
|
8569
|
+
function attributionEventTypeForFinding(finding) {
|
|
8570
|
+
if (finding.source_client === "slack") return "slack_message";
|
|
8571
|
+
if (finding.source_client === "github") return "github_event";
|
|
8572
|
+
if (finding.source_client === "linear") return "linear_event";
|
|
8573
|
+
if (finding.source_client === "gmail") return "gmail_message";
|
|
8574
|
+
if (finding.source_client === "calendar") return "calendar_event";
|
|
8575
|
+
if (finding.source_client === "notion" || finding.source_client === "docs") return "doc_signal";
|
|
8576
|
+
if (finding.source_client === "mcp") return "mcp_call";
|
|
8577
|
+
if (finding.type === "artifact") return "repo_artifact";
|
|
8578
|
+
if (finding.type === "missed_orchestration_opportunity") return "source_coverage";
|
|
8579
|
+
if (/\b(mcp|tool|hook|runtime)\b/i.test(`${finding.title} ${finding.summary}`)) return "tool_signal";
|
|
8580
|
+
return "client_extraction";
|
|
8581
|
+
}
|
|
8582
|
+
function nodeBucketForKind(kind) {
|
|
8583
|
+
switch (kind) {
|
|
8584
|
+
case "decision":
|
|
8585
|
+
return "decisions";
|
|
8586
|
+
case "artifact":
|
|
8587
|
+
return "artifacts";
|
|
8588
|
+
case "person":
|
|
8589
|
+
return "people";
|
|
8590
|
+
case "agent":
|
|
8591
|
+
return "agents";
|
|
8592
|
+
case "tool":
|
|
8593
|
+
return "tools";
|
|
8594
|
+
case "business":
|
|
8595
|
+
return "businesses";
|
|
8596
|
+
case "product_surface":
|
|
8597
|
+
return "product_surfaces";
|
|
8598
|
+
case "goal":
|
|
8599
|
+
return "goals";
|
|
8600
|
+
case "source":
|
|
8601
|
+
return "sources";
|
|
8602
|
+
case "action":
|
|
8603
|
+
default:
|
|
8604
|
+
return "actions";
|
|
8605
|
+
}
|
|
8606
|
+
}
|
|
8607
|
+
function nodeIdForFinding(kind, finding) {
|
|
8608
|
+
const dedupeSource = finding.metadata.dedupe_key ?? finding.evidence_ref ?? finding.title;
|
|
8609
|
+
return `${kind}:${slugPart(finding.title)}:${shortHash(dedupeSource, 8)}`;
|
|
8610
|
+
}
|
|
8611
|
+
function buildFindingNode(finding, kind) {
|
|
8612
|
+
const id = nodeIdForFinding(kind, finding);
|
|
8613
|
+
return {
|
|
8614
|
+
id,
|
|
8615
|
+
kind,
|
|
8616
|
+
label: finding.title,
|
|
8617
|
+
summary: finding.summary,
|
|
8618
|
+
source_client: finding.source_client,
|
|
8619
|
+
evidence_refs: [finding.evidence_ref],
|
|
8620
|
+
linked_node_ids: [],
|
|
8621
|
+
confidence: finding.confidence,
|
|
8622
|
+
weight: clampScore2(finding.confidence * 100),
|
|
8623
|
+
review_state: "unreviewed",
|
|
8624
|
+
dedupe_key: `${kind}:${slugPart(finding.title)}`,
|
|
8625
|
+
metadata: {
|
|
8626
|
+
source_id: finding.source_id,
|
|
8627
|
+
finding_type: finding.type,
|
|
8628
|
+
...finding.metadata
|
|
8629
|
+
}
|
|
8630
|
+
};
|
|
8631
|
+
}
|
|
8632
|
+
function mergeNodes(nodes) {
|
|
8633
|
+
const merged = /* @__PURE__ */ new Map();
|
|
8634
|
+
for (const node of nodes) {
|
|
8635
|
+
const key = node.dedupe_key ?? node.id;
|
|
8636
|
+
const previous = merged.get(key);
|
|
8637
|
+
if (!previous) {
|
|
8638
|
+
merged.set(key, node);
|
|
8639
|
+
continue;
|
|
8640
|
+
}
|
|
8641
|
+
merged.set(key, {
|
|
8642
|
+
...previous,
|
|
8643
|
+
confidence: Math.max(previous.confidence, node.confidence),
|
|
8644
|
+
evidence_refs: sortedUnique([...previous.evidence_refs, ...node.evidence_refs]),
|
|
8645
|
+
linked_node_ids: sortedUnique([...previous.linked_node_ids, ...node.linked_node_ids]),
|
|
8646
|
+
weight: Math.max(previous.weight, node.weight)
|
|
8647
|
+
});
|
|
8648
|
+
}
|
|
8649
|
+
return [...merged.values()].sort((left, right) => right.weight - left.weight);
|
|
8650
|
+
}
|
|
8651
|
+
function sourceNode(source, connected) {
|
|
8652
|
+
const label = source.trim() || "Unknown source";
|
|
8653
|
+
return {
|
|
8654
|
+
id: `source:${slugPart(label)}:${connected ? "connected" : "missing"}`,
|
|
8655
|
+
kind: "source",
|
|
8656
|
+
label,
|
|
8657
|
+
summary: connected ? `${label} contributed evidence to this Work Graph.` : `${label} was not connected, so related attribution remains incomplete.`,
|
|
8658
|
+
evidence_refs: [],
|
|
8659
|
+
linked_node_ids: [],
|
|
8660
|
+
confidence: connected ? 0.8 : 0.52,
|
|
8661
|
+
weight: connected ? 70 : 62,
|
|
8662
|
+
review_state: connected ? "unreviewed" : "important",
|
|
8663
|
+
dedupe_key: `source:${slugPart(label)}`,
|
|
8664
|
+
metadata: { connected }
|
|
8665
|
+
};
|
|
8666
|
+
}
|
|
8667
|
+
function buildToolNodes(coverage, findings) {
|
|
8668
|
+
const mentionsOrgxTool = coverage.mcpObserved || findings.some((finding) => /\b(orgx[_-]?mcp|orgx_emit|mcp__orgx|tool call)\b/i.test(`${finding.title} ${finding.summary}`));
|
|
8669
|
+
if (!mentionsOrgxTool) return [];
|
|
8670
|
+
return [
|
|
8671
|
+
{
|
|
8672
|
+
id: "tool:orgx-mcp",
|
|
8673
|
+
kind: "tool",
|
|
8674
|
+
label: "OrgX MCP",
|
|
8675
|
+
summary: coverage.orgxMcpCalled ? "OrgX MCP was observed as a durable writeback surface in the audited work." : "OrgX MCP was available or mentioned, but the audit found incomplete durable writeback.",
|
|
8676
|
+
source_client: "mcp",
|
|
8677
|
+
evidence_refs: findings.filter((finding) => /\b(orgx|mcp|tool)\b/i.test(`${finding.title} ${finding.summary}`)).map((finding) => finding.evidence_ref).slice(0, 12),
|
|
8678
|
+
linked_node_ids: [],
|
|
8679
|
+
confidence: coverage.orgxMcpCalled ? 0.86 : 0.72,
|
|
8680
|
+
weight: coverage.orgxMcpCalled ? 88 : 76,
|
|
8681
|
+
review_state: "unreviewed",
|
|
8682
|
+
dedupe_key: "tool:orgx-mcp",
|
|
8683
|
+
metadata: {
|
|
8684
|
+
mcp_observed: coverage.mcpObserved,
|
|
8685
|
+
orgx_observed: coverage.orgxObserved,
|
|
8686
|
+
orgx_mcp_called: coverage.orgxMcpCalled,
|
|
8687
|
+
skill_only_signal: coverage.skillOnlySignal
|
|
8688
|
+
}
|
|
8689
|
+
}
|
|
8690
|
+
];
|
|
8691
|
+
}
|
|
8692
|
+
function buildAgentNodes(findings) {
|
|
8693
|
+
const actorIds = sortedUnique(
|
|
8694
|
+
findings.map((finding) => typeof finding.metadata.actor_id === "string" ? finding.metadata.actor_id.trim() : "").filter(Boolean)
|
|
8695
|
+
);
|
|
8696
|
+
const sourceAgents = sortedUnique(
|
|
8697
|
+
findings.map(
|
|
8698
|
+
(finding) => ["codex", "claude", "claude-code", "cursor", "openclaw"].includes(finding.source_client) ? finding.source_client : ""
|
|
8699
|
+
).filter(Boolean)
|
|
8700
|
+
);
|
|
8701
|
+
return [...actorIds, ...sourceAgents].slice(0, 20).map((agent) => ({
|
|
8702
|
+
id: `agent:${slugPart(agent)}`,
|
|
8703
|
+
kind: "agent",
|
|
8704
|
+
label: agent,
|
|
8705
|
+
summary: `${agent} contributed evidence to the Work Graph audit.`,
|
|
8706
|
+
source_client: sourceAgents.includes(agent) ? agent : "manual",
|
|
8707
|
+
evidence_refs: findings.filter((finding) => finding.metadata.actor_id === agent || finding.source_client === agent).map((finding) => finding.evidence_ref).slice(0, 12),
|
|
8708
|
+
linked_node_ids: [],
|
|
8709
|
+
confidence: 0.72,
|
|
8710
|
+
weight: 64,
|
|
8711
|
+
review_state: "unreviewed",
|
|
8712
|
+
dedupe_key: `agent:${slugPart(agent)}`,
|
|
8713
|
+
metadata: {}
|
|
8714
|
+
}));
|
|
8715
|
+
}
|
|
8716
|
+
function buildWorkGraphAttributionSpine(input) {
|
|
8717
|
+
const evidence_refs = input.findings.map((finding) => ({
|
|
8718
|
+
id: finding.evidence_ref,
|
|
8719
|
+
source_client: finding.source_client,
|
|
8720
|
+
source_id: finding.source_id,
|
|
8721
|
+
label: finding.title,
|
|
8722
|
+
summary: finding.summary.slice(0, 1200),
|
|
8723
|
+
...isIsoTimestamp(finding.metadata.occurred_at) ? { occurred_at: finding.metadata.occurred_at } : {},
|
|
8724
|
+
confidence: finding.confidence,
|
|
8725
|
+
redaction_state: redactionStateForFinding(finding),
|
|
8726
|
+
metadata: {
|
|
8727
|
+
raw_transcript_sent: false,
|
|
8728
|
+
source_label: finding.metadata.source_label ?? null
|
|
8729
|
+
}
|
|
8730
|
+
}));
|
|
8731
|
+
const source_events = [
|
|
8732
|
+
...input.events.map((event) => ({
|
|
8733
|
+
source_client: event.source_client,
|
|
8734
|
+
source_id: event.source_id,
|
|
8735
|
+
source_label: event.source_label,
|
|
8736
|
+
event_type: event.event_type === "client_extraction" ? "client_extraction" : event.event_type,
|
|
8737
|
+
evidence_ref: event.evidence_ref,
|
|
8738
|
+
confidence: 0.7,
|
|
8739
|
+
metadata: {
|
|
8740
|
+
...event.metadata,
|
|
8741
|
+
raw_transcript_sent: false
|
|
8742
|
+
}
|
|
8743
|
+
})),
|
|
8744
|
+
...input.findings.map((finding) => ({
|
|
8745
|
+
source_client: finding.source_client,
|
|
8746
|
+
source_id: finding.source_id,
|
|
8747
|
+
source_label: typeof finding.metadata.source_label === "string" ? finding.metadata.source_label : finding.source_id,
|
|
8748
|
+
event_type: attributionEventTypeForFinding(finding),
|
|
8749
|
+
...isIsoTimestamp(finding.metadata.occurred_at) ? { occurred_at: finding.metadata.occurred_at } : {},
|
|
8750
|
+
evidence_ref: finding.evidence_ref,
|
|
8751
|
+
...typeof finding.metadata.actor_id === "string" ? { actor_ref: finding.metadata.actor_id } : {},
|
|
8752
|
+
confidence: finding.confidence,
|
|
8753
|
+
metadata: {
|
|
8754
|
+
finding_type: finding.type,
|
|
8755
|
+
raw_transcript_sent: false
|
|
8756
|
+
}
|
|
8757
|
+
}))
|
|
8758
|
+
];
|
|
8759
|
+
const buckets = {
|
|
8760
|
+
actions: [],
|
|
8761
|
+
decisions: [],
|
|
8762
|
+
artifacts: [],
|
|
8763
|
+
people: [],
|
|
8764
|
+
agents: buildAgentNodes(input.findings),
|
|
8765
|
+
tools: buildToolNodes(input.coverage, input.findings),
|
|
8766
|
+
businesses: [],
|
|
8767
|
+
product_surfaces: [],
|
|
8768
|
+
goals: [],
|
|
8769
|
+
sources: [
|
|
8770
|
+
...input.coverage.connected.map((source) => sourceNode(source, true)),
|
|
8771
|
+
...input.coverage.missing.map((source) => sourceNode(source, false))
|
|
8772
|
+
],
|
|
8773
|
+
initiative_candidates: []
|
|
8774
|
+
};
|
|
8775
|
+
for (const finding of input.findings) {
|
|
8776
|
+
const kind = attributionKindForFinding(finding.type);
|
|
8777
|
+
if (!kind) continue;
|
|
8778
|
+
const node = buildFindingNode(finding, kind);
|
|
8779
|
+
const bucket = finding.type === "initiative_candidate" ? "initiative_candidates" : nodeBucketForKind(kind);
|
|
8780
|
+
buckets[bucket].push(node);
|
|
8781
|
+
}
|
|
8782
|
+
const normalized = {
|
|
8783
|
+
source_events: source_events.filter(
|
|
8784
|
+
(event, index, all) => all.findIndex(
|
|
8785
|
+
(candidate) => [
|
|
8786
|
+
candidate.source_client,
|
|
8787
|
+
candidate.source_id,
|
|
8788
|
+
candidate.event_type,
|
|
8789
|
+
candidate.evidence_ref
|
|
8790
|
+
].join(":") === [
|
|
8791
|
+
event.source_client,
|
|
8792
|
+
event.source_id,
|
|
8793
|
+
event.event_type,
|
|
8794
|
+
event.evidence_ref
|
|
8795
|
+
].join(":")
|
|
8796
|
+
) === index
|
|
8797
|
+
).slice(0, 1e3),
|
|
8798
|
+
actions: mergeNodes(buckets.actions).slice(0, 500),
|
|
8799
|
+
decisions: mergeNodes(buckets.decisions).slice(0, 500),
|
|
8800
|
+
artifacts: mergeNodes(buckets.artifacts).slice(0, 500),
|
|
8801
|
+
people: mergeNodes(buckets.people).slice(0, 500),
|
|
8802
|
+
agents: mergeNodes(buckets.agents).slice(0, 200),
|
|
8803
|
+
tools: mergeNodes(buckets.tools).slice(0, 300),
|
|
8804
|
+
businesses: mergeNodes(buckets.businesses).slice(0, 200),
|
|
8805
|
+
product_surfaces: mergeNodes(buckets.product_surfaces).slice(0, 500),
|
|
8806
|
+
goals: mergeNodes(buckets.goals).slice(0, 500),
|
|
8807
|
+
sources: mergeNodes(buckets.sources).slice(0, 300),
|
|
8808
|
+
initiative_candidates: mergeNodes(buckets.initiative_candidates).slice(0, 50),
|
|
8809
|
+
evidence_refs: mergeByEvidenceId(evidence_refs).slice(0, 1e3)
|
|
8810
|
+
};
|
|
8811
|
+
const nodeCount = normalized.actions.length + normalized.decisions.length + normalized.artifacts.length + normalized.people.length + normalized.agents.length + normalized.tools.length + normalized.businesses.length + normalized.product_surfaces.length + normalized.goals.length + normalized.sources.length + normalized.initiative_candidates.length;
|
|
8812
|
+
const confidenceInputs = [
|
|
8813
|
+
...normalized.evidence_refs.map((evidence) => evidence.confidence),
|
|
8814
|
+
...normalized.source_events.map((event) => event.confidence)
|
|
8815
|
+
];
|
|
8816
|
+
const confidence = confidenceInputs.length ? Number((confidenceInputs.reduce((total, value) => total + value, 0) / confidenceInputs.length).toFixed(2)) : 0;
|
|
8817
|
+
return {
|
|
8818
|
+
...normalized,
|
|
8819
|
+
confidence,
|
|
8820
|
+
dedupe_keys: sortedUnique([
|
|
8821
|
+
...Object.values(normalized).flat().map((item) => typeof item === "object" && item && "dedupe_key" in item ? String(item.dedupe_key ?? "") : "").filter(Boolean)
|
|
8822
|
+
]).slice(0, 1e3),
|
|
8823
|
+
privacy: {
|
|
8824
|
+
redaction_state: "public_summary",
|
|
8825
|
+
raw_transcripts_included: false,
|
|
8826
|
+
public_summary_only: true
|
|
8827
|
+
},
|
|
8828
|
+
review: {
|
|
8829
|
+
pending_count: nodeCount,
|
|
8830
|
+
correction_affordances: [
|
|
8831
|
+
"confirm",
|
|
8832
|
+
"merge",
|
|
8833
|
+
"hide",
|
|
8834
|
+
"mark_important",
|
|
8835
|
+
"launch_or_dismiss"
|
|
8836
|
+
]
|
|
8837
|
+
}
|
|
8838
|
+
};
|
|
8839
|
+
}
|
|
8840
|
+
function mergeByEvidenceId(evidenceRefs) {
|
|
8841
|
+
const merged = /* @__PURE__ */ new Map();
|
|
8842
|
+
for (const evidence of evidenceRefs) {
|
|
8843
|
+
const previous = merged.get(evidence.id);
|
|
8844
|
+
if (!previous || evidence.confidence > previous.confidence) {
|
|
8845
|
+
merged.set(evidence.id, evidence);
|
|
8846
|
+
}
|
|
8847
|
+
}
|
|
8848
|
+
return [...merged.values()].sort((left, right) => right.confidence - left.confidence);
|
|
8849
|
+
}
|
|
8014
8850
|
function buildWorkGraphFingerprint(input) {
|
|
8015
8851
|
const sourceClients = sortedUnique(input.findings.map((finding) => finding.source_client));
|
|
8016
8852
|
const patternHashes = input.findings.map(
|
|
@@ -8108,18 +8944,41 @@ function buildSessionReconciliationReport(input) {
|
|
|
8108
8944
|
const opportunityScore = scoreOpportunity(coverage, allFindings);
|
|
8109
8945
|
const initiativeKickoffs = buildKickoffs(allFindings, missed, opportunityScore);
|
|
8110
8946
|
const recommendations = buildTrailRecommendations(recurringPatterns, trails);
|
|
8947
|
+
const impactProjection = estimateImpactProjection({
|
|
8948
|
+
coverage,
|
|
8949
|
+
findings: allFindings,
|
|
8950
|
+
opportunityScore,
|
|
8951
|
+
patterns: recurringPatterns,
|
|
8952
|
+
trails
|
|
8953
|
+
});
|
|
8954
|
+
const executionQuality = scoreExecutionQuality({
|
|
8955
|
+
coverage,
|
|
8956
|
+
findings: allFindings,
|
|
8957
|
+
impact: impactProjection,
|
|
8958
|
+
patterns: recurringPatterns,
|
|
8959
|
+
recommendations,
|
|
8960
|
+
trails
|
|
8961
|
+
});
|
|
8111
8962
|
const mirror = buildWorkGraphMirror({
|
|
8112
8963
|
coverage,
|
|
8113
8964
|
generatedAt,
|
|
8965
|
+
impact: impactProjection,
|
|
8114
8966
|
patterns: recurringPatterns,
|
|
8115
8967
|
recommendations,
|
|
8116
8968
|
trails
|
|
8117
8969
|
});
|
|
8118
8970
|
const tensionMetrics = buildTensionMetrics({
|
|
8119
8971
|
coverage,
|
|
8972
|
+
impact: impactProjection,
|
|
8120
8973
|
patterns: recurringPatterns,
|
|
8974
|
+
quality: executionQuality,
|
|
8121
8975
|
trails
|
|
8122
8976
|
});
|
|
8977
|
+
const attributionSpine = buildWorkGraphAttributionSpine({
|
|
8978
|
+
coverage,
|
|
8979
|
+
events,
|
|
8980
|
+
findings: allFindings
|
|
8981
|
+
});
|
|
8123
8982
|
const fingerprint = buildWorkGraphFingerprint({
|
|
8124
8983
|
connectedSources,
|
|
8125
8984
|
findings: allFindings,
|
|
@@ -8167,7 +9026,10 @@ function buildSessionReconciliationReport(input) {
|
|
|
8167
9026
|
recommendations,
|
|
8168
9027
|
mirror,
|
|
8169
9028
|
tension_metrics: tensionMetrics,
|
|
9029
|
+
attribution_spine: attributionSpine,
|
|
8170
9030
|
opportunity_score: opportunityScore,
|
|
9031
|
+
execution_quality: executionQuality,
|
|
9032
|
+
impact_projection: impactProjection,
|
|
8171
9033
|
initiative_kickoffs: initiativeKickoffs,
|
|
8172
9034
|
redaction_level: "summary_only",
|
|
8173
9035
|
raw_transcripts_sent: false
|
|
@@ -8254,13 +9116,44 @@ function renderWorkGraphMarkdown(report) {
|
|
|
8254
9116
|
lines.push(`Automation potential: ${report.opportunity_score.automation_potential}/100`);
|
|
8255
9117
|
lines.push(`OrgX fit: ${report.opportunity_score.orgx_fit}/100`);
|
|
8256
9118
|
lines.push("");
|
|
9119
|
+
lines.push("## Execution Quality");
|
|
9120
|
+
lines.push("");
|
|
9121
|
+
lines.push(`Overall: ${report.execution_quality.overall}/100`);
|
|
9122
|
+
lines.push(`Evidence coverage: ${report.execution_quality.evidence_coverage}/100`);
|
|
9123
|
+
lines.push(`Source attribution: ${report.execution_quality.source_attribution}/100`);
|
|
9124
|
+
lines.push(`Trail depth: ${report.execution_quality.trail_depth}/100`);
|
|
9125
|
+
lines.push(`Insight depth: ${report.execution_quality.insight_depth}/100`);
|
|
9126
|
+
lines.push(`Actionability: ${report.execution_quality.actionability}/100`);
|
|
9127
|
+
lines.push(`Impact confidence: ${report.execution_quality.impact_confidence}/100`);
|
|
9128
|
+
for (const note of report.execution_quality.notes) {
|
|
9129
|
+
lines.push(`- ${note}`);
|
|
9130
|
+
}
|
|
9131
|
+
lines.push("");
|
|
9132
|
+
lines.push("## Impact Projection");
|
|
9133
|
+
lines.push("");
|
|
9134
|
+
lines.push(`Time saved: ${report.impact_projection.time_saved_hours_per_week}h/week`);
|
|
9135
|
+
lines.push(`Acceleration: +${report.impact_projection.acceleration_percent}%`);
|
|
9136
|
+
lines.push(`Estimated monthly value: $${report.impact_projection.estimated_monthly_value_usd.toLocaleString("en-US")}`);
|
|
9137
|
+
lines.push(`Confidence: ${report.impact_projection.confidence}`);
|
|
9138
|
+
for (const item of report.impact_projection.basis) {
|
|
9139
|
+
lines.push(`- ${item}`);
|
|
9140
|
+
}
|
|
9141
|
+
lines.push("");
|
|
8257
9142
|
lines.push("## Source Coverage");
|
|
8258
9143
|
lines.push("");
|
|
8259
9144
|
lines.push(`Connected: ${report.source_coverage.connected.join(", ") || "none"}`);
|
|
8260
9145
|
lines.push(`Missing: ${report.source_coverage.missing.join(", ") || "none"}`);
|
|
9146
|
+
lines.push(`Coverage score: ${report.source_coverage.coverage_score ?? 0}/100`);
|
|
8261
9147
|
lines.push(`MCP observed: ${report.source_coverage.mcpObserved ? "yes" : "no"}`);
|
|
8262
9148
|
lines.push(`OrgX observed: ${report.source_coverage.orgxObserved ? "yes" : "no"}`);
|
|
8263
9149
|
lines.push(`OrgX MCP called: ${report.source_coverage.orgxMcpCalled ? "yes" : "no"}`);
|
|
9150
|
+
if (report.source_coverage.manifests?.length) {
|
|
9151
|
+
lines.push("");
|
|
9152
|
+
lines.push("Source manifests:");
|
|
9153
|
+
for (const manifest of report.source_coverage.manifests) {
|
|
9154
|
+
lines.push(`- [${manifest.status}] ${manifest.source_label}: ${manifest.finding_count} findings, ${manifest.searched_session_count} searched / ${manifest.skipped_session_count} skipped sessions, confidence ${manifest.confidence}`);
|
|
9155
|
+
}
|
|
9156
|
+
}
|
|
8264
9157
|
lines.push("");
|
|
8265
9158
|
lines.push("## Mirror");
|
|
8266
9159
|
lines.push("");
|
|
@@ -8322,6 +9215,30 @@ function renderWorkGraphMarkdown(report) {
|
|
|
8322
9215
|
lines.push(`- [${recommendation.priority}] ${recommendation.title}: ${recommendation.summary}`);
|
|
8323
9216
|
}
|
|
8324
9217
|
lines.push("");
|
|
9218
|
+
lines.push("## Attribution Spine");
|
|
9219
|
+
lines.push("");
|
|
9220
|
+
lines.push(`Source events: ${report.attribution_spine.source_events.length}`);
|
|
9221
|
+
lines.push(`Evidence refs: ${report.attribution_spine.evidence_refs.length}`);
|
|
9222
|
+
lines.push(`Review nodes: ${report.attribution_spine.review.pending_count}`);
|
|
9223
|
+
lines.push(`Confidence: ${report.attribution_spine.confidence}`);
|
|
9224
|
+
lines.push("");
|
|
9225
|
+
const attributionGroups = [
|
|
9226
|
+
["Actions", report.attribution_spine.actions],
|
|
9227
|
+
["Decisions", report.attribution_spine.decisions],
|
|
9228
|
+
["Artifacts", report.attribution_spine.artifacts],
|
|
9229
|
+
["People", report.attribution_spine.people],
|
|
9230
|
+
["Businesses", report.attribution_spine.businesses],
|
|
9231
|
+
["Surfaces", report.attribution_spine.product_surfaces],
|
|
9232
|
+
["Sources", report.attribution_spine.sources]
|
|
9233
|
+
];
|
|
9234
|
+
for (const [label, nodes] of attributionGroups) {
|
|
9235
|
+
if (nodes.length === 0) continue;
|
|
9236
|
+
lines.push(`${label}:`);
|
|
9237
|
+
for (const node of nodes.slice(0, 5)) {
|
|
9238
|
+
lines.push(`- ${node.label} (${node.evidence_refs.join(", ") || "source coverage"})`);
|
|
9239
|
+
}
|
|
9240
|
+
lines.push("");
|
|
9241
|
+
}
|
|
8325
9242
|
lines.push("## Signup Hydration");
|
|
8326
9243
|
lines.push("");
|
|
8327
9244
|
lines.push(`- Strategy: ${report.signup_hydration.strategy}`);
|
|
@@ -8337,8 +9254,314 @@ function renderWorkGraphMarkdown(report) {
|
|
|
8337
9254
|
return lines.join("\n");
|
|
8338
9255
|
}
|
|
8339
9256
|
|
|
9257
|
+
// src/lib/work-graph-publish.ts
|
|
9258
|
+
async function parseResponse(response) {
|
|
9259
|
+
const text2 = await response.text();
|
|
9260
|
+
if (!text2) return null;
|
|
9261
|
+
try {
|
|
9262
|
+
return JSON.parse(text2);
|
|
9263
|
+
} catch {
|
|
9264
|
+
return text2;
|
|
9265
|
+
}
|
|
9266
|
+
}
|
|
9267
|
+
async function publishWorkGraphReport(report, options = {}) {
|
|
9268
|
+
const auth = await resolveOrgxAuth();
|
|
9269
|
+
if (!auth) {
|
|
9270
|
+
throw new Error("OrgX auth is required to publish a Work Graph. Run `orgx-wizard auth login` or set ORGX_API_KEY.");
|
|
9271
|
+
}
|
|
9272
|
+
const url = buildOrgxApiUrl("/client/work-graph/reports", auth.baseUrl);
|
|
9273
|
+
const response = await fetch(url, {
|
|
9274
|
+
method: "POST",
|
|
9275
|
+
headers: {
|
|
9276
|
+
Authorization: `Bearer ${auth.apiKey}`,
|
|
9277
|
+
"Content-Type": "application/json"
|
|
9278
|
+
},
|
|
9279
|
+
body: JSON.stringify({
|
|
9280
|
+
report,
|
|
9281
|
+
...options.workspaceId ? { workspace_id: options.workspaceId } : {},
|
|
9282
|
+
...options.initiativeId ? { initiative_id: options.initiativeId } : {},
|
|
9283
|
+
...options.entityType ? { entity_type: options.entityType } : {},
|
|
9284
|
+
...options.entityId ? { entity_id: options.entityId } : {},
|
|
9285
|
+
...options.artifactUrl ? { artifact_url: options.artifactUrl } : {},
|
|
9286
|
+
attach_artifact: Boolean(options.attachArtifact),
|
|
9287
|
+
public_share: Boolean(options.publicShare)
|
|
9288
|
+
}),
|
|
9289
|
+
signal: options.signal ?? AbortSignal.timeout(15e3)
|
|
9290
|
+
});
|
|
9291
|
+
return {
|
|
9292
|
+
ok: response.ok,
|
|
9293
|
+
status: response.status,
|
|
9294
|
+
url,
|
|
9295
|
+
data: await parseResponse(response)
|
|
9296
|
+
};
|
|
9297
|
+
}
|
|
9298
|
+
async function publishWorkGraphEvents(fingerprint, patch, options = {}) {
|
|
9299
|
+
const auth = await resolveOrgxAuth();
|
|
9300
|
+
if (!auth) {
|
|
9301
|
+
throw new Error("OrgX auth is required to replay Work Graph hook events. Run `orgx-wizard auth login` or set ORGX_API_KEY.");
|
|
9302
|
+
}
|
|
9303
|
+
const url = buildOrgxApiUrl("/client/work-graph/events", auth.baseUrl);
|
|
9304
|
+
const response = await fetch(url, {
|
|
9305
|
+
method: "POST",
|
|
9306
|
+
headers: {
|
|
9307
|
+
Authorization: `Bearer ${auth.apiKey}`,
|
|
9308
|
+
"Content-Type": "application/json"
|
|
9309
|
+
},
|
|
9310
|
+
body: JSON.stringify({
|
|
9311
|
+
work_graph_fingerprint: fingerprint,
|
|
9312
|
+
patch
|
|
9313
|
+
}),
|
|
9314
|
+
signal: options.signal ?? AbortSignal.timeout(15e3)
|
|
9315
|
+
});
|
|
9316
|
+
return {
|
|
9317
|
+
ok: response.ok,
|
|
9318
|
+
status: response.status,
|
|
9319
|
+
url,
|
|
9320
|
+
data: await parseResponse(response)
|
|
9321
|
+
};
|
|
9322
|
+
}
|
|
9323
|
+
|
|
9324
|
+
// src/lib/work-graph-hook-events.ts
|
|
9325
|
+
import { createHash as createHash5 } from "crypto";
|
|
9326
|
+
import { existsSync as existsSync6, readFileSync as readFileSync4 } from "fs";
|
|
9327
|
+
var SOURCE_CLIENTS = [
|
|
9328
|
+
"codex",
|
|
9329
|
+
"claude",
|
|
9330
|
+
"claude-code",
|
|
9331
|
+
"cursor",
|
|
9332
|
+
"openclaw",
|
|
9333
|
+
"slack",
|
|
9334
|
+
"mcp",
|
|
9335
|
+
"github",
|
|
9336
|
+
"linear",
|
|
9337
|
+
"gmail",
|
|
9338
|
+
"calendar",
|
|
9339
|
+
"notion",
|
|
9340
|
+
"docs",
|
|
9341
|
+
"manual",
|
|
9342
|
+
"wizard",
|
|
9343
|
+
"api",
|
|
9344
|
+
"unknown"
|
|
9345
|
+
];
|
|
9346
|
+
function isRecord2(value) {
|
|
9347
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
9348
|
+
}
|
|
9349
|
+
function asString2(value) {
|
|
9350
|
+
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
9351
|
+
}
|
|
9352
|
+
function asNumber(value) {
|
|
9353
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
9354
|
+
}
|
|
9355
|
+
function asStringArray(value) {
|
|
9356
|
+
if (!Array.isArray(value)) return void 0;
|
|
9357
|
+
return value.filter((item) => typeof item === "string" && item.trim().length > 0);
|
|
9358
|
+
}
|
|
9359
|
+
function stableHash(value) {
|
|
9360
|
+
return createHash5("sha256").update(value).digest("hex").slice(0, 20);
|
|
9361
|
+
}
|
|
9362
|
+
function normalizeSourceClient2(value) {
|
|
9363
|
+
const raw = asString2(value)?.toLowerCase();
|
|
9364
|
+
if (!raw) return "unknown";
|
|
9365
|
+
if (SOURCE_CLIENTS.includes(raw)) return raw;
|
|
9366
|
+
if (raw === "claude_code") return "claude-code";
|
|
9367
|
+
return "unknown";
|
|
9368
|
+
}
|
|
9369
|
+
function safeTimestamp(value) {
|
|
9370
|
+
const raw = asString2(value);
|
|
9371
|
+
if (raw && !Number.isNaN(Date.parse(raw))) return new Date(raw).toISOString();
|
|
9372
|
+
return (/* @__PURE__ */ new Date(0)).toISOString();
|
|
9373
|
+
}
|
|
9374
|
+
function sourceLabel(sourceClient) {
|
|
9375
|
+
switch (sourceClient) {
|
|
9376
|
+
case "claude-code":
|
|
9377
|
+
return "Claude Code";
|
|
9378
|
+
case "codex":
|
|
9379
|
+
return "Codex";
|
|
9380
|
+
case "openclaw":
|
|
9381
|
+
return "OpenClaw";
|
|
9382
|
+
case "cursor":
|
|
9383
|
+
return "Cursor";
|
|
9384
|
+
default:
|
|
9385
|
+
return sourceClient.replace(/[-_]/g, " ").replace(/\b\w/g, (char) => char.toUpperCase());
|
|
9386
|
+
}
|
|
9387
|
+
}
|
|
9388
|
+
function readHookRecord(line) {
|
|
9389
|
+
try {
|
|
9390
|
+
const parsed = JSON.parse(line);
|
|
9391
|
+
if (!isRecord2(parsed)) return null;
|
|
9392
|
+
const record = {};
|
|
9393
|
+
const schemaVersion = asString2(parsed.schema_version);
|
|
9394
|
+
const source = asString2(parsed.source);
|
|
9395
|
+
const sourceClient = asString2(parsed.source_client);
|
|
9396
|
+
const event = asString2(parsed.event);
|
|
9397
|
+
const sessionId = asString2(parsed.session_id);
|
|
9398
|
+
const turnId = asString2(parsed.turn_id);
|
|
9399
|
+
const cwd = asString2(parsed.cwd);
|
|
9400
|
+
const transcriptPath = asString2(parsed.transcript_path);
|
|
9401
|
+
const timestamp = asString2(parsed.timestamp);
|
|
9402
|
+
if (schemaVersion) record.schema_version = schemaVersion;
|
|
9403
|
+
if (source) record.source = source;
|
|
9404
|
+
if (sourceClient) record.source_client = sourceClient;
|
|
9405
|
+
if (event) record.event = event;
|
|
9406
|
+
if (sessionId) record.session_id = sessionId;
|
|
9407
|
+
if (turnId) record.turn_id = turnId;
|
|
9408
|
+
if (cwd) record.cwd = cwd;
|
|
9409
|
+
if (transcriptPath) record.transcript_path = transcriptPath;
|
|
9410
|
+
if (timestamp) record.timestamp = timestamp;
|
|
9411
|
+
if (isRecord2(parsed.summary)) {
|
|
9412
|
+
const summary = {};
|
|
9413
|
+
const toolName = asString2(parsed.summary.tool_name);
|
|
9414
|
+
const promptChars = asNumber(parsed.summary.prompt_chars);
|
|
9415
|
+
const payloadKeys = asStringArray(parsed.summary.payload_keys);
|
|
9416
|
+
if (toolName) summary.tool_name = toolName;
|
|
9417
|
+
if (promptChars !== void 0) summary.prompt_chars = promptChars;
|
|
9418
|
+
if (payloadKeys) summary.payload_keys = payloadKeys;
|
|
9419
|
+
record.summary = summary;
|
|
9420
|
+
}
|
|
9421
|
+
return record;
|
|
9422
|
+
} catch {
|
|
9423
|
+
return null;
|
|
9424
|
+
}
|
|
9425
|
+
}
|
|
9426
|
+
function readRuntimeHookOutbox(path, limit = 200) {
|
|
9427
|
+
if (!existsSync6(path)) return { path, records: [], skipped: 0 };
|
|
9428
|
+
const lines = readFileSync4(path, "utf8").split(/\r?\n/).filter((line) => line.trim().length > 0);
|
|
9429
|
+
const selected = lines.slice(Math.max(0, lines.length - Math.max(1, limit)));
|
|
9430
|
+
const records = [];
|
|
9431
|
+
let skipped = Math.max(0, lines.length - selected.length);
|
|
9432
|
+
for (const line of selected) {
|
|
9433
|
+
const record = readHookRecord(line);
|
|
9434
|
+
if (record) {
|
|
9435
|
+
records.push(record);
|
|
9436
|
+
} else {
|
|
9437
|
+
skipped += 1;
|
|
9438
|
+
}
|
|
9439
|
+
}
|
|
9440
|
+
return { path, records, skipped };
|
|
9441
|
+
}
|
|
9442
|
+
function buildWorkGraphHookReplayPatch(readResult) {
|
|
9443
|
+
const sourceEvents = [];
|
|
9444
|
+
const evidenceRefs = [];
|
|
9445
|
+
const sources = /* @__PURE__ */ new Map();
|
|
9446
|
+
const agents = /* @__PURE__ */ new Map();
|
|
9447
|
+
const tools = /* @__PURE__ */ new Map();
|
|
9448
|
+
const dedupeKeys = /* @__PURE__ */ new Set();
|
|
9449
|
+
readResult.records.forEach((record, index) => {
|
|
9450
|
+
const sourceClient = normalizeSourceClient2(record.source_client);
|
|
9451
|
+
const event = record.event ?? "runtime_hook";
|
|
9452
|
+
const timestamp = safeTimestamp(record.timestamp);
|
|
9453
|
+
const sourceId = record.session_id ?? record.transcript_path ?? record.cwd ?? `${sourceClient}:hook:${index}`;
|
|
9454
|
+
const evidenceId = `hook:${stableHash(`${sourceClient}:${sourceId}:${event}:${timestamp}:${index}`)}`;
|
|
9455
|
+
const toolName = record.summary?.tool_name;
|
|
9456
|
+
const hasMcpTool = Boolean(toolName && /(mcp|orgx)/i.test(toolName));
|
|
9457
|
+
const label = `${sourceLabel(sourceClient)} ${event}`;
|
|
9458
|
+
const summary = hasMcpTool ? `${sourceLabel(sourceClient)} hook observed tool activity through ${toolName}.` : `${sourceLabel(sourceClient)} hook observed ${event} in the local runtime.`;
|
|
9459
|
+
evidenceRefs.push({
|
|
9460
|
+
id: evidenceId,
|
|
9461
|
+
source_client: sourceClient,
|
|
9462
|
+
source_id: sourceId,
|
|
9463
|
+
label,
|
|
9464
|
+
summary,
|
|
9465
|
+
occurred_at: timestamp,
|
|
9466
|
+
confidence: hasMcpTool ? 0.82 : 0.68,
|
|
9467
|
+
redaction_state: "public_summary",
|
|
9468
|
+
metadata: {
|
|
9469
|
+
raw_transcript_sent: false,
|
|
9470
|
+
hook_source: record.source ?? "runtime_hook",
|
|
9471
|
+
cwd: record.cwd ?? null,
|
|
9472
|
+
transcript_path: record.transcript_path ?? null
|
|
9473
|
+
}
|
|
9474
|
+
});
|
|
9475
|
+
sourceEvents.push({
|
|
9476
|
+
source_client: sourceClient,
|
|
9477
|
+
source_id: sourceId,
|
|
9478
|
+
source_label: sourceLabel(sourceClient),
|
|
9479
|
+
event_type: hasMcpTool ? "mcp_call" : "runtime_hook",
|
|
9480
|
+
occurred_at: timestamp,
|
|
9481
|
+
evidence_ref: evidenceId,
|
|
9482
|
+
confidence: hasMcpTool ? 0.82 : 0.68,
|
|
9483
|
+
metadata: {
|
|
9484
|
+
hook_event: event,
|
|
9485
|
+
raw_transcript_sent: false,
|
|
9486
|
+
prompt_chars: record.summary?.prompt_chars ?? null,
|
|
9487
|
+
payload_keys: record.summary?.payload_keys ?? []
|
|
9488
|
+
}
|
|
9489
|
+
});
|
|
9490
|
+
const sourceEvidence = sources.get(sourceClient) ?? [];
|
|
9491
|
+
sourceEvidence.push(evidenceId);
|
|
9492
|
+
sources.set(sourceClient, sourceEvidence.slice(0, 30));
|
|
9493
|
+
dedupeKeys.add(`hook:${sourceClient}:${sourceId}:${event}`);
|
|
9494
|
+
if (sourceClient !== "unknown") {
|
|
9495
|
+
const agentId = `agent:${sourceClient}`;
|
|
9496
|
+
agents.set(agentId, {
|
|
9497
|
+
id: agentId,
|
|
9498
|
+
kind: "agent",
|
|
9499
|
+
label: sourceLabel(sourceClient),
|
|
9500
|
+
summary: `${sourceLabel(sourceClient)} is emitting passive runtime hook evidence into this Work Graph.`,
|
|
9501
|
+
source_client: sourceClient,
|
|
9502
|
+
evidence_refs: sourceEvidence.slice(0, 30),
|
|
9503
|
+
linked_node_ids: [`source:${sourceClient}`],
|
|
9504
|
+
confidence: 0.7,
|
|
9505
|
+
weight: 62,
|
|
9506
|
+
review_state: "unreviewed",
|
|
9507
|
+
dedupe_key: agentId,
|
|
9508
|
+
metadata: { runtime_hook_replay: true }
|
|
9509
|
+
});
|
|
9510
|
+
}
|
|
9511
|
+
if (toolName) {
|
|
9512
|
+
const toolId = `tool:${stableHash(toolName.toLowerCase())}`;
|
|
9513
|
+
const previous = tools.get(toolId);
|
|
9514
|
+
const previousEvidence = Array.isArray(previous?.evidence_refs) ? previous.evidence_refs.filter((item) => typeof item === "string") : [];
|
|
9515
|
+
tools.set(toolId, {
|
|
9516
|
+
id: toolId,
|
|
9517
|
+
kind: "tool",
|
|
9518
|
+
label: toolName,
|
|
9519
|
+
summary: hasMcpTool ? `Runtime hook evidence shows ${toolName} participating in OrgX/MCP work.` : `Runtime hook evidence shows ${toolName} participating in local work.`,
|
|
9520
|
+
source_client: sourceClient,
|
|
9521
|
+
evidence_refs: [...previousEvidence, evidenceId].slice(0, 30),
|
|
9522
|
+
linked_node_ids: [`source:${sourceClient}`],
|
|
9523
|
+
confidence: hasMcpTool ? 0.82 : 0.66,
|
|
9524
|
+
weight: hasMcpTool ? 78 : 54,
|
|
9525
|
+
review_state: "unreviewed",
|
|
9526
|
+
dedupe_key: toolId,
|
|
9527
|
+
metadata: { runtime_hook_replay: true }
|
|
9528
|
+
});
|
|
9529
|
+
}
|
|
9530
|
+
});
|
|
9531
|
+
const sourceNodes = [...sources.entries()].map(([sourceClient, refs]) => ({
|
|
9532
|
+
id: `source:${sourceClient}`,
|
|
9533
|
+
kind: "source",
|
|
9534
|
+
label: sourceLabel(sourceClient),
|
|
9535
|
+
summary: `${sourceLabel(sourceClient)} hook events have been replayed into this Work Graph profile.`,
|
|
9536
|
+
source_client: sourceClient,
|
|
9537
|
+
evidence_refs: refs.slice(0, 30),
|
|
9538
|
+
linked_node_ids: [],
|
|
9539
|
+
confidence: 0.7,
|
|
9540
|
+
weight: 65,
|
|
9541
|
+
review_state: "unreviewed",
|
|
9542
|
+
dedupe_key: `source:${sourceClient}`,
|
|
9543
|
+
metadata: { runtime_hook_replay: true }
|
|
9544
|
+
}));
|
|
9545
|
+
const patch = {
|
|
9546
|
+
source_events: sourceEvents,
|
|
9547
|
+
evidence_refs: evidenceRefs,
|
|
9548
|
+
sources: sourceNodes,
|
|
9549
|
+
agents: [...agents.values()],
|
|
9550
|
+
tools: [...tools.values()],
|
|
9551
|
+
dedupe_keys: [...dedupeKeys].slice(0, 200),
|
|
9552
|
+
confidence: evidenceRefs.length > 0 ? 0.7 : 0
|
|
9553
|
+
};
|
|
9554
|
+
return {
|
|
9555
|
+
patch,
|
|
9556
|
+
records: readResult.records.length,
|
|
9557
|
+
skipped: readResult.skipped,
|
|
9558
|
+
sources: [...sources.keys()],
|
|
9559
|
+
evidenceRefs: evidenceRefs.length
|
|
9560
|
+
};
|
|
9561
|
+
}
|
|
9562
|
+
|
|
8340
9563
|
// src/lib/runtime-hooks.ts
|
|
8341
|
-
import { copyFileSync, existsSync as
|
|
9564
|
+
import { copyFileSync, existsSync as existsSync7, mkdirSync as mkdirSync3 } from "fs";
|
|
8342
9565
|
import { homedir as homedir2 } from "os";
|
|
8343
9566
|
import { dirname as dirname4, join as join5 } from "path";
|
|
8344
9567
|
var HOOK_MARKER = "orgx-session-hook.mjs";
|
|
@@ -8364,7 +9587,7 @@ function backupPath(path, now) {
|
|
|
8364
9587
|
return `${path}.bak.${timestamp}`;
|
|
8365
9588
|
}
|
|
8366
9589
|
function backupExisting(path, now) {
|
|
8367
|
-
if (!
|
|
9590
|
+
if (!existsSync7(path)) return null;
|
|
8368
9591
|
const backup = backupPath(path, now);
|
|
8369
9592
|
copyFileSync(path, backup);
|
|
8370
9593
|
return backup;
|
|
@@ -8562,7 +9785,7 @@ function inspectRuntimeHooks(options = {}) {
|
|
|
8562
9785
|
installed: {
|
|
8563
9786
|
claudeCode: hasOrgxHook(claudeSettingsRaw),
|
|
8564
9787
|
codex: hasOrgxHook(codexHooksRaw),
|
|
8565
|
-
hookScript:
|
|
9788
|
+
hookScript: existsSync7(paths.hookScriptPath)
|
|
8566
9789
|
},
|
|
8567
9790
|
codex: {
|
|
8568
9791
|
configExists: Boolean(codexConfigRaw),
|
|
@@ -8762,12 +9985,72 @@ function printRuntimeHookInspection(report) {
|
|
|
8762
9985
|
console.log(` ${report.installed.claudeCode ? ICON.ok : ICON.warn} ${pc3.bold("Claude Code ")} ${report.installed.claudeCode ? pc3.green("installed") : pc3.yellow("missing")} ${pc3.dim(report.paths.claudeSettingsPath)}`);
|
|
8763
9986
|
console.log(` ${ICON.skip} ${pc3.bold("outbox ")} ${pc3.dim(`${report.outboxEvents} event${report.outboxEvents === 1 ? "" : "s"} at ${report.paths.outboxPath}`)}`);
|
|
8764
9987
|
}
|
|
9988
|
+
function requireHookReplayApproval(options, interactive) {
|
|
9989
|
+
if (options.yes) return true;
|
|
9990
|
+
if (!interactive) {
|
|
9991
|
+
throw new Error("Replaying Work Graph hook events requires --yes in non-interactive mode.");
|
|
9992
|
+
}
|
|
9993
|
+
return clack.confirm({
|
|
9994
|
+
message: "Replay passive hook events into this claimed Work Graph profile?"
|
|
9995
|
+
}).then((value) => {
|
|
9996
|
+
if (clack.isCancel(value) || value !== true) {
|
|
9997
|
+
clack.cancel("Hook replay cancelled.");
|
|
9998
|
+
return false;
|
|
9999
|
+
}
|
|
10000
|
+
return true;
|
|
10001
|
+
});
|
|
10002
|
+
}
|
|
10003
|
+
function parsePositiveInt(value, fallback) {
|
|
10004
|
+
if (!value?.trim()) return fallback;
|
|
10005
|
+
const parsed = Number.parseInt(value.trim(), 10);
|
|
10006
|
+
if (!Number.isFinite(parsed) || parsed < 1) {
|
|
10007
|
+
throw new Error(`Expected a positive integer, got ${value}`);
|
|
10008
|
+
}
|
|
10009
|
+
return parsed;
|
|
10010
|
+
}
|
|
10011
|
+
async function runHookReplayCommand(options) {
|
|
10012
|
+
const fingerprint = options.fingerprint?.trim();
|
|
10013
|
+
if (!fingerprint) {
|
|
10014
|
+
throw new Error("Missing --fingerprint <wgf_...> for hook event replay.");
|
|
10015
|
+
}
|
|
10016
|
+
const interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
10017
|
+
const paths = inspectRuntimeHooks().paths;
|
|
10018
|
+
const outboxPath = resolve(options.outbox?.trim() || paths.outboxPath);
|
|
10019
|
+
const readResult = readRuntimeHookOutbox(outboxPath, parsePositiveInt(options.limit, 200));
|
|
10020
|
+
const replay = buildWorkGraphHookReplayPatch(readResult);
|
|
10021
|
+
if (replay.records === 0) {
|
|
10022
|
+
if (options.json) {
|
|
10023
|
+
console.log(JSON.stringify({ ok: true, published: false, reason: "empty_outbox", ...replay }, null, 2));
|
|
10024
|
+
return;
|
|
10025
|
+
}
|
|
10026
|
+
console.log(` ${ICON.skip} ${pc3.bold("outbox ")} ${pc3.dim(`no hook events found at ${outboxPath}`)}`);
|
|
10027
|
+
return;
|
|
10028
|
+
}
|
|
10029
|
+
const approved = await requireHookReplayApproval(options, interactive);
|
|
10030
|
+
if (!approved) return;
|
|
10031
|
+
const result = await publishWorkGraphEvents(fingerprint, replay.patch);
|
|
10032
|
+
if (!result.ok) process.exitCode = 1;
|
|
10033
|
+
await safeTrackWizardTelemetry("hooks_replay_ran", {
|
|
10034
|
+
command: "hooks replay",
|
|
10035
|
+
fingerprint,
|
|
10036
|
+
records: String(replay.records),
|
|
10037
|
+
sources: replay.sources.join(","),
|
|
10038
|
+
status: String(result.status)
|
|
10039
|
+
});
|
|
10040
|
+
if (options.json) {
|
|
10041
|
+
console.log(JSON.stringify({ ...replay, published: result }, null, 2));
|
|
10042
|
+
return;
|
|
10043
|
+
}
|
|
10044
|
+
console.log(` ${result.ok ? ICON.ok : ICON.warn} ${pc3.bold("replayed ")} ${replay.records} hook event${replay.records === 1 ? "" : "s"} ${pc3.dim(`status ${result.status}`)}`);
|
|
10045
|
+
console.log(` ${ICON.skip} ${pc3.bold("sources ")} ${pc3.dim(replay.sources.join(", ") || "none")}`);
|
|
10046
|
+
console.log(` ${ICON.skip} ${pc3.bold("evidence ")} ${pc3.dim(`${replay.evidenceRefs} public-summary ref${replay.evidenceRefs === 1 ? "" : "s"}`)}`);
|
|
10047
|
+
}
|
|
8765
10048
|
function readAuditInput(options, interactive) {
|
|
8766
10049
|
if (options.input?.trim()) {
|
|
8767
|
-
return
|
|
10050
|
+
return readFileSync6(resolve(options.input.trim()), "utf8");
|
|
8768
10051
|
}
|
|
8769
10052
|
if (!process.stdin.isTTY) {
|
|
8770
|
-
return
|
|
10053
|
+
return readFileSync6(0, "utf8");
|
|
8771
10054
|
}
|
|
8772
10055
|
if (!interactive) {
|
|
8773
10056
|
throw new Error("Audit input is required. Pass --input <file> or pipe text into wizard audit.");
|
|
@@ -8800,7 +10083,7 @@ function collectPathOption(value, previous = []) {
|
|
|
8800
10083
|
}
|
|
8801
10084
|
function parseClientExtractionFile(path) {
|
|
8802
10085
|
const resolvedPath = resolve(path);
|
|
8803
|
-
const parsed = JSON.parse(
|
|
10086
|
+
const parsed = JSON.parse(readFileSync6(resolvedPath, "utf8"));
|
|
8804
10087
|
if (!isRecord(parsed)) {
|
|
8805
10088
|
throw new Error(`AI-client extraction must be a JSON object: ${resolvedPath}`);
|
|
8806
10089
|
}
|
|
@@ -8902,6 +10185,22 @@ function requireWriteApproval(options, interactive) {
|
|
|
8902
10185
|
return true;
|
|
8903
10186
|
});
|
|
8904
10187
|
}
|
|
10188
|
+
function requireWorkGraphPublishApproval(options, interactive) {
|
|
10189
|
+
const wantsPublish = Boolean(options.publish || options.publicShare);
|
|
10190
|
+
if (!wantsPublish || options.yes || options.dryRun) return true;
|
|
10191
|
+
if (!interactive) {
|
|
10192
|
+
throw new Error("Publishing a Work Graph requires --yes in non-interactive mode.");
|
|
10193
|
+
}
|
|
10194
|
+
return clack.confirm({
|
|
10195
|
+
message: "Publish this Work Graph to OrgX and create a shareable profile?"
|
|
10196
|
+
}).then((value) => {
|
|
10197
|
+
if (clack.isCancel(value) || value !== true) {
|
|
10198
|
+
clack.cancel("Work Graph publish cancelled.");
|
|
10199
|
+
return false;
|
|
10200
|
+
}
|
|
10201
|
+
return true;
|
|
10202
|
+
});
|
|
10203
|
+
}
|
|
8905
10204
|
async function resolveAuditWorkspace(options) {
|
|
8906
10205
|
const explicitId = options.workspaceId?.trim();
|
|
8907
10206
|
const explicitName = options.workspaceName?.trim();
|
|
@@ -9021,18 +10320,27 @@ async function runWorkGraphCommand(options, defaults = {}) {
|
|
|
9021
10320
|
};
|
|
9022
10321
|
const auditInputs = await readWorkGraphInputs(commandOptions, interactive);
|
|
9023
10322
|
const workspace = await resolveAuditWorkspace(commandOptions);
|
|
10323
|
+
const generatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
10324
|
+
const nativeClientExtractions = buildNativeAiClientExtractions(
|
|
10325
|
+
auditInputs.imports,
|
|
10326
|
+
generatedAt
|
|
10327
|
+
);
|
|
10328
|
+
const clientExtractions = [
|
|
10329
|
+
...auditInputs.clientExtractions,
|
|
10330
|
+
...nativeClientExtractions
|
|
10331
|
+
];
|
|
9024
10332
|
const report = buildSessionReconciliationReport({
|
|
9025
|
-
clientExtractions
|
|
10333
|
+
clientExtractions,
|
|
9026
10334
|
connectedSources: [
|
|
9027
10335
|
...auditInputs.connectedSources,
|
|
9028
10336
|
...workspace.id === "local-workspace" ? [] : ["OrgX workspace"]
|
|
9029
10337
|
],
|
|
9030
|
-
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
9031
10338
|
imports: auditInputs.imports,
|
|
9032
10339
|
missingSources: [
|
|
9033
10340
|
...workspace.id === "local-workspace" ? ["OrgX workspace auth"] : [],
|
|
9034
10341
|
...auditInputs.missingSources
|
|
9035
10342
|
],
|
|
10343
|
+
generatedAt,
|
|
9036
10344
|
workspace
|
|
9037
10345
|
});
|
|
9038
10346
|
const markdown = renderWorkGraphMarkdown(report);
|
|
@@ -9042,6 +10350,39 @@ async function runWorkGraphCommand(options, defaults = {}) {
|
|
|
9042
10350
|
const markdownPath = resolve(outputDir, `work-graph-report-${timestamp}.md`);
|
|
9043
10351
|
writeJsonFile(jsonPath, report);
|
|
9044
10352
|
writeTextFile(markdownPath, markdown);
|
|
10353
|
+
let published = null;
|
|
10354
|
+
const shouldPublish = Boolean(commandOptions.publish || commandOptions.publicShare);
|
|
10355
|
+
if (shouldPublish) {
|
|
10356
|
+
const approved = await requireWorkGraphPublishApproval(commandOptions, interactive);
|
|
10357
|
+
if (approved) {
|
|
10358
|
+
if (commandOptions.dryRun) {
|
|
10359
|
+
published = { ok: true, status: 0 };
|
|
10360
|
+
} else {
|
|
10361
|
+
const publishResult = await publishWorkGraphReport(report, {
|
|
10362
|
+
...commandOptions.artifactUrl?.trim() ? { artifactUrl: commandOptions.artifactUrl.trim() } : {},
|
|
10363
|
+
attachArtifact: Boolean(commandOptions.attachArtifact || commandOptions.attachToInitiative),
|
|
10364
|
+
...commandOptions.entityId?.trim() ? { entityId: commandOptions.entityId.trim() } : {},
|
|
10365
|
+
...commandOptions.entityType ? { entityType: commandOptions.entityType } : {},
|
|
10366
|
+
...commandOptions.attachToInitiative?.trim() ? { initiativeId: commandOptions.attachToInitiative.trim() } : {},
|
|
10367
|
+
publicShare: Boolean(commandOptions.publicShare || commandOptions.publish),
|
|
10368
|
+
...workspace.id !== "local-workspace" ? { workspaceId: workspace.id } : {}
|
|
10369
|
+
});
|
|
10370
|
+
if (!publishResult.ok) {
|
|
10371
|
+
throw new Error(`Work Graph publish failed with HTTP ${publishResult.status}: ${JSON.stringify(publishResult.data)}`);
|
|
10372
|
+
}
|
|
10373
|
+
const body = isRecord(publishResult.data) ? publishResult.data : {};
|
|
10374
|
+
const publicShare = isRecord(body.public_share) ? body.public_share : null;
|
|
10375
|
+
const publicUrl = typeof publicShare?.url === "string" ? `${normalizeOrgxBaseUrl(process.env.ORGX_BASE_URL || DEFAULT_ORGX_BASE_URL)}${publicShare.url}` : void 0;
|
|
10376
|
+
const readout = isRecord(body.public_readout) ? body.public_readout : null;
|
|
10377
|
+
published = {
|
|
10378
|
+
ok: true,
|
|
10379
|
+
status: publishResult.status,
|
|
10380
|
+
...publicUrl ? { publicUrl } : {},
|
|
10381
|
+
...typeof readout?.review_url === "string" ? { reviewUrl: readout.review_url } : {}
|
|
10382
|
+
};
|
|
10383
|
+
}
|
|
10384
|
+
}
|
|
10385
|
+
}
|
|
9045
10386
|
if (commandOptions.json) {
|
|
9046
10387
|
console.log(JSON.stringify({
|
|
9047
10388
|
jsonPath,
|
|
@@ -9051,13 +10392,17 @@ async function runWorkGraphCommand(options, defaults = {}) {
|
|
|
9051
10392
|
hydrationKey: report.signup_hydration.hydration_key,
|
|
9052
10393
|
finalState: report.final_state,
|
|
9053
10394
|
opportunityScore: report.opportunity_score,
|
|
10395
|
+
executionQuality: report.execution_quality,
|
|
10396
|
+
impactProjection: report.impact_projection,
|
|
9054
10397
|
missedOrchestration: report.missed_orchestration_opportunities.length,
|
|
9055
10398
|
clientExtractionCount: report.client_extractions.length,
|
|
9056
10399
|
kickoffCount: report.initiative_kickoffs.length,
|
|
9057
10400
|
trailCount: report.trails.length,
|
|
9058
10401
|
recurringPatternCount: report.recurring_patterns.length,
|
|
10402
|
+
attributionCount: report.attribution_spine.review.pending_count,
|
|
9059
10403
|
topTrail: report.mirror.primary_trail_id ?? null,
|
|
9060
|
-
mirrorHeadline: report.mirror.headline
|
|
10404
|
+
mirrorHeadline: report.mirror.headline,
|
|
10405
|
+
published
|
|
9061
10406
|
}, null, 2));
|
|
9062
10407
|
return;
|
|
9063
10408
|
}
|
|
@@ -9067,6 +10412,8 @@ async function runWorkGraphCommand(options, defaults = {}) {
|
|
|
9067
10412
|
console.log(` ${ICON.ok} ${pc3.green("state ")} ${pc3.dim(report.final_state)}`);
|
|
9068
10413
|
console.log(` ${ICON.ok} ${pc3.green("extractions ")} ${pc3.dim(String(report.client_extractions.length))}`);
|
|
9069
10414
|
console.log(` ${ICON.ok} ${pc3.green("score ")} ${pc3.dim(formatWorkGraphScoreLine(report.opportunity_score))}`);
|
|
10415
|
+
console.log(` ${ICON.ok} ${pc3.green("quality ")} ${pc3.dim(`${report.execution_quality.overall}/100 \xB7 coverage ${report.source_coverage.coverage_score ?? 0}/100`)}`);
|
|
10416
|
+
console.log(` ${ICON.ok} ${pc3.green("impact ")} ${pc3.dim(`${report.impact_projection.time_saved_hours_per_week}h/week \xB7 +${report.impact_projection.acceleration_percent}% acceleration \xB7 ~$${report.impact_projection.estimated_monthly_value_usd.toLocaleString("en-US")}/month`)}`);
|
|
9070
10417
|
const missed = report.missed_orchestration_opportunities.length;
|
|
9071
10418
|
const missedColor = missed > 0 ? pc3.yellow : pc3.green;
|
|
9072
10419
|
console.log(` ${missed > 0 ? ICON.warn : ICON.ok} ${missedColor("missed ")} ${pc3.dim(`${missed} orchestration opportunit${missed === 1 ? "y" : "ies"}`)}`);
|
|
@@ -9079,6 +10426,10 @@ async function runWorkGraphCommand(options, defaults = {}) {
|
|
|
9079
10426
|
for (const kickoff of report.initiative_kickoffs) {
|
|
9080
10427
|
console.log(` ${ICON.skip} ${pc3.bold(kickoff.priority.padEnd(3))} ${kickoff.title}`);
|
|
9081
10428
|
}
|
|
10429
|
+
console.log(` ${ICON.ok} ${pc3.green("attribution ")} ${pc3.dim(`${report.attribution_spine.review.pending_count} nodes \xB7 ${report.attribution_spine.source_events.length} source events`)}`);
|
|
10430
|
+
if (published?.publicUrl) {
|
|
10431
|
+
console.log(` ${ICON.ok} ${pc3.green("profile ")} ${pc3.bold(published.publicUrl)}`);
|
|
10432
|
+
}
|
|
9082
10433
|
}
|
|
9083
10434
|
async function checkPluginStatusesCompact() {
|
|
9084
10435
|
const spinner = createOrgxSpinner("Checking OrgX companion plugin status");
|
|
@@ -10038,7 +11389,7 @@ function printDoctorReport(report, assessment) {
|
|
|
10038
11389
|
async function main() {
|
|
10039
11390
|
const program = new Command();
|
|
10040
11391
|
program.name("orgx-wizard").description("Add OrgX MCP configs, skills/rules, and companion plugins to your local AI tools.").showHelpAfterError();
|
|
10041
|
-
const pkgVersion = true ? "0.1.
|
|
11392
|
+
const pkgVersion = true ? "0.1.36" : void 0;
|
|
10042
11393
|
program.version(pkgVersion ?? "unknown", "-V, --version");
|
|
10043
11394
|
program.hook("preAction", (_thisCommand, actionCommand) => {
|
|
10044
11395
|
if (Boolean(actionCommand.optsWithGlobals().json)) return;
|
|
@@ -10753,7 +12104,7 @@ async function main() {
|
|
|
10753
12104
|
});
|
|
10754
12105
|
await runWorkGraphCommand(options);
|
|
10755
12106
|
});
|
|
10756
|
-
workGraph.command("profile").description("Build a local OrgX Profile with Work Graph Trails, Mirror, tensions, and launch 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, claude, or all").option("--session-limit <count>", "max recent sessions to import per selected source", "5").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("--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("--json", "emit a JSON command summary").action(async (options) => {
|
|
12107
|
+
workGraph.command("profile").description("Build a local OrgX Profile with Work Graph Trails, Mirror, tensions, and launch 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, claude, or all").option("--session-limit <count>", "max recent sessions to import per selected source", "5").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("--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) => {
|
|
10757
12108
|
await safeTrackWizardTelemetry("work_graph_profile_started", {
|
|
10758
12109
|
command: "work-graph profile",
|
|
10759
12110
|
from: options.from ?? "manual"
|
|
@@ -10761,7 +12112,7 @@ async function main() {
|
|
|
10761
12112
|
await runWorkGraphCommand(options);
|
|
10762
12113
|
});
|
|
10763
12114
|
const sessions = program.command("sessions").description("Inspect and reconcile local AI sessions into OrgX-ready Work Graph reports.");
|
|
10764
|
-
sessions.command("reconcile").description("Backfill recent Codex and Claude sessions into a redacted Work Graph 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: codex, claude, or all", "all").option("--session-limit <count>", "max recent sessions to import per selected source", "5").option("--session-days <days>", "lookback window for local AI-session imports", "7").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("--json", "emit a JSON command summary").action(async (options) => {
|
|
12115
|
+
sessions.command("reconcile").description("Backfill recent Codex and Claude sessions into a redacted Work Graph 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: codex, claude, or all", "all").option("--session-limit <count>", "max recent sessions to import per selected source", "5").option("--session-days <days>", "lookback window for local AI-session imports", "7").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) => {
|
|
10765
12116
|
await safeTrackWizardTelemetry("sessions_reconcile_started", {
|
|
10766
12117
|
command: "sessions reconcile",
|
|
10767
12118
|
from: options.from ?? "all"
|
|
@@ -10805,6 +12156,9 @@ async function main() {
|
|
|
10805
12156
|
}
|
|
10806
12157
|
}
|
|
10807
12158
|
});
|
|
12159
|
+
hooks.command("replay").description("Replay passive hook outbox events into a claimed Work Graph profile.").requiredOption("--fingerprint <fingerprint>", "target Work Graph fingerprint, for example wgf_0123...").option("--outbox <path>", "runtime hook JSONL outbox path").option("--limit <count>", "maximum recent hook events to replay", "200").option("--yes", "approve publishing hook evidence without prompting").option("--json", "emit a JSON command summary").action(async (options) => {
|
|
12160
|
+
await runHookReplayCommand(options);
|
|
12161
|
+
});
|
|
10808
12162
|
program.command("doctor").description("Verify local OrgX surface config and optional remote setup status.").action(async () => {
|
|
10809
12163
|
const spinner = createOrgxSpinner("Running OrgX health check");
|
|
10810
12164
|
spinner.start();
|