polymath-society 0.2.18 → 0.2.20
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 +503 -167
- package/dist/index.js +418 -140
- package/dist/pipeline/FOCUS-EXAMPLES.md +127 -0
- package/dist/pipeline/FOCUS-RUBRIC.md +418 -0
- package/dist/pipeline/coding-agglomerate.js +126 -2
- package/dist/pipeline/coding-aggregate.js +136 -0
- package/dist/pipeline/coding-build.js +118 -4
- package/dist/pipeline/coding-coaching.js +173 -14
- package/dist/pipeline/coding-day-digest.js +109 -0
- package/dist/pipeline/coding-delegation.js +189 -17
- package/dist/pipeline/coding-expertise.js +126 -2
- package/dist/pipeline/coding-flow.js +136 -0
- package/dist/pipeline/coding-focus.js +136 -0
- package/dist/pipeline/coding-frontier-detail.js +126 -2
- package/dist/pipeline/coding-gap-dist.js +136 -0
- package/dist/pipeline/coding-gap.js +182 -16
- package/dist/pipeline/coding-grade.js +132 -2
- package/dist/pipeline/coding-nutshell.js +159 -150
- package/dist/pipeline/coding-walkthrough.js +144 -0
- package/dist/web/app.js +59 -19
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -33,9 +33,17 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
33
33
|
));
|
|
34
34
|
|
|
35
35
|
// src/dataHome.ts
|
|
36
|
-
import { existsSync } from "fs";
|
|
36
|
+
import { existsSync, readFileSync } from "fs";
|
|
37
37
|
import os from "os";
|
|
38
38
|
import path from "path";
|
|
39
|
+
function hasCompletedAnalysis(root) {
|
|
40
|
+
try {
|
|
41
|
+
const a = JSON.parse(readFileSync(path.join(root, "coding", "aggregate.json"), "utf-8"));
|
|
42
|
+
return !!String(a.bigPicture ?? "").trim();
|
|
43
|
+
} catch {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
39
47
|
function electronAppData() {
|
|
40
48
|
const candidates = [
|
|
41
49
|
"/Applications/Polymath.app/Contents/Resources/standalone/.data",
|
|
@@ -44,14 +52,18 @@ function electronAppData() {
|
|
|
44
52
|
for (const c of candidates) if (existsSync(path.join(c, "coding"))) return c;
|
|
45
53
|
return null;
|
|
46
54
|
}
|
|
47
|
-
function resolveDataRoot(env = process.env, cwd = process.cwd(), appData = electronAppData) {
|
|
55
|
+
function resolveDataRoot(env = process.env, cwd = process.cwd(), appData = electronAppData, home = os.homedir()) {
|
|
48
56
|
const fromEnv = env.POLYMATH_DATA_DIR;
|
|
49
57
|
if (fromEnv && fromEnv.trim()) return path.resolve(fromEnv);
|
|
50
58
|
const local = path.join(cwd, ".data");
|
|
51
|
-
|
|
59
|
+
const dflt = path.join(home, ".polymath-society", "data");
|
|
60
|
+
if (existsSync(local) && hasCompletedAnalysis(local)) return local;
|
|
52
61
|
const fromApp = appData();
|
|
62
|
+
if (fromApp && hasCompletedAnalysis(fromApp)) return fromApp;
|
|
63
|
+
if (hasCompletedAnalysis(dflt)) return dflt;
|
|
64
|
+
if (existsSync(local)) return local;
|
|
53
65
|
if (fromApp) return fromApp;
|
|
54
|
-
return
|
|
66
|
+
return dflt;
|
|
55
67
|
}
|
|
56
68
|
var init_dataHome = __esm({
|
|
57
69
|
"src/dataHome.ts"() {
|
|
@@ -98,6 +110,103 @@ var init_injected = __esm({
|
|
|
98
110
|
}
|
|
99
111
|
});
|
|
100
112
|
|
|
113
|
+
// ../coding-core/dist/codex.js
|
|
114
|
+
function isCodexInjectedUserText(text2) {
|
|
115
|
+
return CODEX_INJECTED_TEXT_RE.test(text2);
|
|
116
|
+
}
|
|
117
|
+
function codexContentText(content) {
|
|
118
|
+
if (typeof content === "string")
|
|
119
|
+
return content;
|
|
120
|
+
if (!Array.isArray(content))
|
|
121
|
+
return "";
|
|
122
|
+
const parts = [];
|
|
123
|
+
for (const item of content) {
|
|
124
|
+
if (typeof item === "string") {
|
|
125
|
+
parts.push(item);
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
if (item && typeof item === "object") {
|
|
129
|
+
const it = item;
|
|
130
|
+
if (it.type === "input_text" || it.type === "output_text" || it.type === "text")
|
|
131
|
+
parts.push(it.text || "");
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return parts.join("\n");
|
|
135
|
+
}
|
|
136
|
+
function sniffCodexRaw(raw) {
|
|
137
|
+
let checked = 0;
|
|
138
|
+
for (const lineRaw of raw.split("\n")) {
|
|
139
|
+
const line = lineRaw.trim();
|
|
140
|
+
if (!line)
|
|
141
|
+
continue;
|
|
142
|
+
if (checked++ >= 5)
|
|
143
|
+
break;
|
|
144
|
+
try {
|
|
145
|
+
const e = JSON.parse(line);
|
|
146
|
+
if (e.type === "session_meta" || e.type === "response_item" || e.type === "turn_context")
|
|
147
|
+
return true;
|
|
148
|
+
if (e.type === "user" || e.type === "assistant" || e.type === "summary" || e.type === "queue-operation")
|
|
149
|
+
return false;
|
|
150
|
+
} catch {
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
function extractCodexEvents(raw) {
|
|
156
|
+
const out = [];
|
|
157
|
+
for (const lineRaw of raw.split("\n")) {
|
|
158
|
+
const line = lineRaw.trim();
|
|
159
|
+
if (!line)
|
|
160
|
+
continue;
|
|
161
|
+
let e;
|
|
162
|
+
try {
|
|
163
|
+
e = JSON.parse(line);
|
|
164
|
+
} catch {
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
if (e.type !== "response_item")
|
|
168
|
+
continue;
|
|
169
|
+
const p = e.payload ?? {};
|
|
170
|
+
const t = typeof e.timestamp === "string" ? Date.parse(e.timestamp) : NaN;
|
|
171
|
+
const ptype = p.type;
|
|
172
|
+
if (ptype === "message") {
|
|
173
|
+
const role = p.role;
|
|
174
|
+
const text2 = codexContentText(p.content).replace(SYSTEM_REMINDER_RE2, "").trim();
|
|
175
|
+
if (!text2)
|
|
176
|
+
continue;
|
|
177
|
+
if (role === "user") {
|
|
178
|
+
if (isCodexInjectedUserText(text2))
|
|
179
|
+
continue;
|
|
180
|
+
out.push({ kind: "user", t, text: text2 });
|
|
181
|
+
} else if (role === "assistant") {
|
|
182
|
+
out.push({ kind: "assistant", t, text: text2 });
|
|
183
|
+
}
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
if (ptype === "function_call" || ptype === "custom_tool_call" || ptype === "local_shell_call") {
|
|
187
|
+
const name17 = typeof p.name === "string" && p.name ? p.name : ptype === "local_shell_call" ? "shell" : String(ptype);
|
|
188
|
+
const input = typeof p.arguments === "string" ? p.arguments : typeof p.input === "string" ? p.input : "";
|
|
189
|
+
out.push({ kind: "tool", t, name: name17, input });
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
if (ptype === "function_call_output" || ptype === "custom_tool_call_output") {
|
|
193
|
+
const output = typeof p.output === "string" ? p.output : "";
|
|
194
|
+
const m = EXIT_CODE_RE.exec(output.slice(0, 200));
|
|
195
|
+
out.push({ kind: "tool_output", t, output, isError: !!m && m[1] !== "0" });
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
return out;
|
|
199
|
+
}
|
|
200
|
+
var SYSTEM_REMINDER_RE2, CODEX_INJECTED_TEXT_RE, EXIT_CODE_RE;
|
|
201
|
+
var init_codex = __esm({
|
|
202
|
+
"../coding-core/dist/codex.js"() {
|
|
203
|
+
"use strict";
|
|
204
|
+
SYSTEM_REMINDER_RE2 = /<system-reminder>[\s\S]*?<\/system-reminder>/g;
|
|
205
|
+
CODEX_INJECTED_TEXT_RE = /^\s*(<(environment_context|user_instructions|ENVIRONMENT_CONTEXT|turn_aborted)\b|#\s*AGENTS\.md instructions\b)/;
|
|
206
|
+
EXIT_CODE_RE = /(?:Process exited with code|Exit code:)\s+(\d+)/;
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
|
|
101
210
|
// ../coding-core/dist/raw.js
|
|
102
211
|
import { promises as fs } from "fs";
|
|
103
212
|
import path2 from "path";
|
|
@@ -370,7 +479,7 @@ function parseCodex(raw, file2) {
|
|
|
370
479
|
const role = p.role;
|
|
371
480
|
const text2 = codexText(p.content).replace(SYSTEM_REMINDER_RE, "").trim();
|
|
372
481
|
if (role === "user") {
|
|
373
|
-
if (text2 && !
|
|
482
|
+
if (text2 && !isCodexInjectedUserText(text2)) {
|
|
374
483
|
s.humanTurns++;
|
|
375
484
|
s.humanChars += text2.length;
|
|
376
485
|
if (!s.firstHuman)
|
|
@@ -762,11 +871,12 @@ async function readAllSessions(opts = {}) {
|
|
|
762
871
|
demoteTemplateFanouts(all);
|
|
763
872
|
return all;
|
|
764
873
|
}
|
|
765
|
-
var SOURCE_ROOT_ENV_VARS, anyRootOverridden, claudeProjectsRoot, codexSessionsRoot, cursorWorkspaceRoot, cursorProjectsRoot, SKIP_DIR_RE, AGENT_CWD_RE, PERSONA_RE, OUTPUT_CONTRACT_RES, CAPS_HEADER_RE, PING_RE, ms,
|
|
874
|
+
var SOURCE_ROOT_ENV_VARS, anyRootOverridden, claudeProjectsRoot, codexSessionsRoot, cursorWorkspaceRoot, cursorProjectsRoot, SKIP_DIR_RE, AGENT_CWD_RE, PERSONA_RE, OUTPUT_CONTRACT_RES, CAPS_HEADER_RE, PING_RE, ms, CURSOR_USER_QUERY_RE, NODE_SQLITE, FANOUT_PREFIX, FANOUT_MIN_SESSIONS;
|
|
766
875
|
var init_raw = __esm({
|
|
767
876
|
"../coding-core/dist/raw.js"() {
|
|
768
877
|
"use strict";
|
|
769
878
|
init_injected();
|
|
879
|
+
init_codex();
|
|
770
880
|
SOURCE_ROOT_ENV_VARS = [
|
|
771
881
|
"CLAUDE_PROJECTS_DIR",
|
|
772
882
|
"CODEX_SESSIONS_DIR",
|
|
@@ -795,7 +905,6 @@ var init_raw = __esm({
|
|
|
795
905
|
const n = Date.parse(ts);
|
|
796
906
|
return Number.isFinite(n) ? n : null;
|
|
797
907
|
};
|
|
798
|
-
CODEX_INJECTED_RE = /^\s*<(environment_context|user_instructions)>/;
|
|
799
908
|
CURSOR_USER_QUERY_RE = /<user_query>\s*([\s\S]*?)\s*<\/user_query>/;
|
|
800
909
|
NODE_SQLITE = "node:sqlite";
|
|
801
910
|
FANOUT_PREFIX = 80;
|
|
@@ -1474,6 +1583,11 @@ async function readManifest(dataDir) {
|
|
|
1474
1583
|
exports: { included: arr(ex.included), excluded: arr(ex.excluded) }
|
|
1475
1584
|
};
|
|
1476
1585
|
}
|
|
1586
|
+
function agentFlagsFromManifest(m) {
|
|
1587
|
+
if (!m?.agents) return { claudeCode: true, codex: true };
|
|
1588
|
+
const on = (id) => m.agents.included.includes(id);
|
|
1589
|
+
return { claudeCode: on("claude-code"), codex: on("codex") };
|
|
1590
|
+
}
|
|
1477
1591
|
async function writeManifest(dataDir, m) {
|
|
1478
1592
|
await fs22.mkdir(dataDir, { recursive: true });
|
|
1479
1593
|
await fs22.writeFile(manifestPath(dataDir), JSON.stringify(m, null, 2));
|
|
@@ -2042,7 +2156,7 @@ __export(plan_exports, {
|
|
|
2042
2156
|
tierFromStrings: () => tierFromStrings
|
|
2043
2157
|
});
|
|
2044
2158
|
import { promises as fs34 } from "fs";
|
|
2045
|
-
import
|
|
2159
|
+
import os10 from "os";
|
|
2046
2160
|
import path39 from "path";
|
|
2047
2161
|
function tierFromStrings(...hints) {
|
|
2048
2162
|
for (const h of hints) {
|
|
@@ -2063,7 +2177,7 @@ function tierFromChatgptPlan(planType) {
|
|
|
2063
2177
|
if (s === "free") return "gpt_free";
|
|
2064
2178
|
return "unknown";
|
|
2065
2179
|
}
|
|
2066
|
-
async function detectCodexPlan(home =
|
|
2180
|
+
async function detectCodexPlan(home = os10.homedir()) {
|
|
2067
2181
|
try {
|
|
2068
2182
|
const raw = JSON.parse(await fs34.readFile(path39.join(home, ".codex", "auth.json"), "utf8"));
|
|
2069
2183
|
for (const tok of [raw.tokens?.id_token, raw.tokens?.access_token]) {
|
|
@@ -2079,7 +2193,7 @@ async function detectCodexPlan(home = os9.homedir()) {
|
|
|
2079
2193
|
}
|
|
2080
2194
|
return "unknown";
|
|
2081
2195
|
}
|
|
2082
|
-
async function detectPlan(home =
|
|
2196
|
+
async function detectPlan(home = os10.homedir()) {
|
|
2083
2197
|
let tier = "unknown";
|
|
2084
2198
|
try {
|
|
2085
2199
|
const raw = JSON.parse(await fs34.readFile(path39.join(home, ".claude.json"), "utf8"));
|
|
@@ -2170,17 +2284,18 @@ var init_plan = __esm({
|
|
|
2170
2284
|
var wizard_exports = {};
|
|
2171
2285
|
__export(wizard_exports, {
|
|
2172
2286
|
runWizard: () => runWizard,
|
|
2173
|
-
sayExportLinks: () => sayExportLinks
|
|
2287
|
+
sayExportLinks: () => sayExportLinks,
|
|
2288
|
+
scanJsonlDir: () => scanJsonlDir
|
|
2174
2289
|
});
|
|
2175
2290
|
import { promises as fs35 } from "fs";
|
|
2176
|
-
import
|
|
2291
|
+
import os11 from "os";
|
|
2177
2292
|
import path40 from "path";
|
|
2178
2293
|
import * as readline from "node:readline/promises";
|
|
2179
|
-
async function scanJsonlDir(label, dir) {
|
|
2294
|
+
async function scanJsonlDir(label, dir, maxDepth = 1) {
|
|
2180
2295
|
let files = 0;
|
|
2181
2296
|
let newest = 0;
|
|
2182
2297
|
async function walk(d, depth) {
|
|
2183
|
-
if (depth >
|
|
2298
|
+
if (depth > maxDepth) return;
|
|
2184
2299
|
let names = [];
|
|
2185
2300
|
try {
|
|
2186
2301
|
names = await fs35.readdir(d);
|
|
@@ -2305,15 +2420,15 @@ async function runWizard() {
|
|
|
2305
2420
|
});
|
|
2306
2421
|
say(bold(" Step 1 \xB7 Your coding agents"));
|
|
2307
2422
|
const roots = sourceRoots();
|
|
2308
|
-
const scanRoot = (label, dir) => dir ? scanJsonlDir(label, dir) : Promise.resolve({ label, dir: "(disabled \u2014 log roots redirected via env)", files: 0, newest: null });
|
|
2309
|
-
const [cc, cx] = await Promise.all([scanRoot("Claude Code", roots.claudeProjects), scanRoot("Codex", roots.codexSessions)]);
|
|
2423
|
+
const scanRoot = (label, dir, maxDepth) => dir ? scanJsonlDir(label, dir, maxDepth) : Promise.resolve({ label, dir: "(disabled \u2014 log roots redirected via env)", files: 0, newest: null });
|
|
2424
|
+
const [cc, cx] = await Promise.all([scanRoot("Claude Code", roots.claudeProjects, 1), scanRoot("Codex", roots.codexSessions, 3)]);
|
|
2310
2425
|
const agentIds = ["claude-code", "codex"];
|
|
2311
2426
|
const agentScans = [cc, cx];
|
|
2312
2427
|
const present = agentIds.filter((_, i) => agentScans[i].files > 0);
|
|
2313
2428
|
for (let i = 0; i < agentScans.length; i++) {
|
|
2314
2429
|
const s = agentScans[i];
|
|
2315
2430
|
const idx = present.indexOf(agentIds[i]);
|
|
2316
|
-
say(s.files > 0 ? ` ${idx + 1}. ${green("\u2713")} ${bold(s.label)} \u2014 ${s.files.toLocaleString()} log files \xB7 newest ${s.newest} ${dim(s.dir.replace(
|
|
2431
|
+
say(s.files > 0 ? ` ${idx + 1}. ${green("\u2713")} ${bold(s.label)} \u2014 ${s.files.toLocaleString()} log files \xB7 newest ${s.newest} ${dim(s.dir.replace(os11.homedir(), "~"))}` : ` ${dim("\xB7")} ${dim(`${s.label} \u2014 none found`)} ${dim(s.dir.replace(os11.homedir(), "~"))}`);
|
|
2317
2432
|
}
|
|
2318
2433
|
say(dim(" (log files include forks and agent runs \u2014 the report separates your real sessions out)"));
|
|
2319
2434
|
if (present.length === 0) {
|
|
@@ -2376,17 +2491,20 @@ async function runWizard() {
|
|
|
2376
2491
|
}
|
|
2377
2492
|
};
|
|
2378
2493
|
say(dim(" Scanning for known export formats (Spotlight + the usual folders, a few seconds)..."));
|
|
2379
|
-
for (const c of await deterministicDiscover(
|
|
2380
|
-
for (const f of await scanForExports(path40.join(
|
|
2381
|
-
for (const f of await scanForObsidianVaults(
|
|
2494
|
+
for (const c of await deterministicDiscover(os11.homedir())) add(await identifyPath(c));
|
|
2495
|
+
for (const f of await scanForExports(path40.join(os11.homedir(), "Downloads"))) add(f);
|
|
2496
|
+
for (const f of await scanForObsidianVaults(os11.homedir())) add(f);
|
|
2382
2497
|
for (const f of [...previous?.exports.included ?? [], ...previous?.exports.excluded ?? []]) add(f);
|
|
2498
|
+
let linksShown = false;
|
|
2383
2499
|
if (!found.length) {
|
|
2384
2500
|
say(dim(" Nothing found in the usual places."));
|
|
2501
|
+
await sayExportLinks(say, /* @__PURE__ */ new Set());
|
|
2502
|
+
linksShown = true;
|
|
2385
2503
|
if (await yes(` Search deeper with your own local agent? ${dim("(capped at ~90s)")}`, false)) {
|
|
2386
2504
|
try {
|
|
2387
2505
|
const { invokeAgent: invokeAgent3 } = await Promise.resolve().then(() => (init_agent(), agent_exports));
|
|
2388
2506
|
say(dim(" Searching (your agent writes its own filesystem searches; nothing leaves this machine; 90s max)..."));
|
|
2389
|
-
const candidates = await agentDiscoverCandidates((inv) => invokeAgent3(inv),
|
|
2507
|
+
const candidates = await agentDiscoverCandidates((inv) => invokeAgent3(inv), os11.homedir(), []);
|
|
2390
2508
|
for (const c of candidates) add(await identifyPath(c));
|
|
2391
2509
|
} catch (e) {
|
|
2392
2510
|
say(dim(` Agent search unavailable (${e instanceof Error ? e.message.slice(0, 80) : "failed"}).`));
|
|
@@ -2394,7 +2512,7 @@ async function runWizard() {
|
|
|
2394
2512
|
}
|
|
2395
2513
|
if (!found.length) {
|
|
2396
2514
|
const folder = await ask(` Got them in a specific folder? ${dim("(path, or Enter to skip)")}: `);
|
|
2397
|
-
if (folder) for (const f of await scanForExports(folder.replace(/^~(?=$|\/)/,
|
|
2515
|
+
if (folder) for (const f of await scanForExports(folder.replace(/^~(?=$|\/)/, os11.homedir()))) add(f);
|
|
2398
2516
|
}
|
|
2399
2517
|
}
|
|
2400
2518
|
if (found.length) {
|
|
@@ -2406,7 +2524,7 @@ async function runWizard() {
|
|
|
2406
2524
|
const defaults = found.map(() => true);
|
|
2407
2525
|
say(` Found ${dim("(identified locally from the files themselves)")}:`);
|
|
2408
2526
|
found.forEach((f, i) => say(describeExport(f, i, defaults[i], hadPrevious)));
|
|
2409
|
-
await sayExportLinks(say, new Set(found.map((f) => f.kind)));
|
|
2527
|
+
if (!linksShown) await sayExportLinks(say, new Set(found.map((f) => f.kind)));
|
|
2410
2528
|
const include = await pick2(defaults, hadPrevious);
|
|
2411
2529
|
const manifest = {
|
|
2412
2530
|
approvedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -2419,14 +2537,14 @@ async function runWizard() {
|
|
|
2419
2537
|
approvedExports = manifest.exports.included;
|
|
2420
2538
|
await writeManifest(dataDir, manifest);
|
|
2421
2539
|
const nIn = manifest.exports.included.length;
|
|
2422
|
-
say(` ${green("\u2713")} Saved to ${path40.join(dataDir, "exports-manifest.json").replace(
|
|
2540
|
+
say(` ${green("\u2713")} Saved to ${path40.join(dataDir, "exports-manifest.json").replace(os11.homedir(), "~")} \u2014 ${nIn} source${nIn === 1 ? "" : "s"} approved, ${manifest.exports.excluded.length} excluded (re-run anytime to change).`);
|
|
2423
2541
|
for (const f of manifest.exports.excluded) say(` ${dim(`\u2717 ${path40.basename(f.path)} \u2014 excluded, never read past identification.`)}`);
|
|
2424
2542
|
if (manifest.exports.included.some((f) => f.kind !== "unknown")) {
|
|
2425
2543
|
say(` ${dim("These are ingested and analyzed locally when you pick the full analysis below.")}`);
|
|
2426
2544
|
}
|
|
2427
2545
|
} else {
|
|
2428
2546
|
await writeManifest(dataDir, { approvedAt: (/* @__PURE__ */ new Date()).toISOString(), agents, exports: { included: [], excluded: [] } });
|
|
2429
|
-
await sayExportLinks(say, /* @__PURE__ */ new Set());
|
|
2547
|
+
if (!linksShown) await sayExportLinks(say, /* @__PURE__ */ new Set());
|
|
2430
2548
|
}
|
|
2431
2549
|
{
|
|
2432
2550
|
const { EXPORT_LINKS: EXPORT_LINKS2 } = await Promise.resolve().then(() => (init_exportLinks(), exportLinks_exports));
|
|
@@ -2941,6 +3059,7 @@ init_gate2();
|
|
|
2941
3059
|
|
|
2942
3060
|
// ../coding-core/dist/messages.js
|
|
2943
3061
|
init_injected();
|
|
3062
|
+
init_codex();
|
|
2944
3063
|
import { promises as fs2 } from "fs";
|
|
2945
3064
|
function extractText2(content) {
|
|
2946
3065
|
if (typeof content === "string")
|
|
@@ -2952,6 +3071,22 @@ function extractText2(content) {
|
|
|
2952
3071
|
function isToolResultOnly(content) {
|
|
2953
3072
|
return Array.isArray(content) && content.length > 0 && content.every((c) => c && typeof c === "object" && c.type === "tool_result");
|
|
2954
3073
|
}
|
|
3074
|
+
function extractUserMessagesCodex(raw) {
|
|
3075
|
+
const out = [];
|
|
3076
|
+
let lastTs = null;
|
|
3077
|
+
for (const ev of extractCodexEvents(raw)) {
|
|
3078
|
+
const t = ev.t;
|
|
3079
|
+
const gapMs = Number.isFinite(t) && lastTs != null ? t - lastTs : NaN;
|
|
3080
|
+
if (Number.isFinite(t))
|
|
3081
|
+
lastTs = t;
|
|
3082
|
+
if (ev.kind !== "user")
|
|
3083
|
+
continue;
|
|
3084
|
+
const text2 = humanTypedText(ev.text);
|
|
3085
|
+
if (text2 && Number.isFinite(t))
|
|
3086
|
+
out.push({ t, text: text2, gapMs });
|
|
3087
|
+
}
|
|
3088
|
+
return out.sort((a, b) => a.t - b.t);
|
|
3089
|
+
}
|
|
2955
3090
|
async function extractUserMessages(file2) {
|
|
2956
3091
|
let raw;
|
|
2957
3092
|
try {
|
|
@@ -2959,6 +3094,8 @@ async function extractUserMessages(file2) {
|
|
|
2959
3094
|
} catch {
|
|
2960
3095
|
return [];
|
|
2961
3096
|
}
|
|
3097
|
+
if (sniffCodexRaw(raw))
|
|
3098
|
+
return extractUserMessagesCodex(raw);
|
|
2962
3099
|
const out = [];
|
|
2963
3100
|
const userTexts = /* @__PURE__ */ new Set();
|
|
2964
3101
|
const seenUuids = /* @__PURE__ */ new Set();
|
|
@@ -3013,6 +3150,49 @@ function createMessageDeduper() {
|
|
|
3013
3150
|
return true;
|
|
3014
3151
|
};
|
|
3015
3152
|
}
|
|
3153
|
+
function extractMessagesCodex(raw) {
|
|
3154
|
+
const out = [];
|
|
3155
|
+
let aiProse = [];
|
|
3156
|
+
let aiTools = {};
|
|
3157
|
+
let aiStart = 0;
|
|
3158
|
+
const flushAI = () => {
|
|
3159
|
+
if (!aiProse.length && Object.keys(aiTools).length === 0)
|
|
3160
|
+
return;
|
|
3161
|
+
const toolStr = Object.entries(aiTools).map(([n, c]) => c > 1 ? `${n}\xD7${c}` : n).join(", ");
|
|
3162
|
+
let prose = aiProse.join(" ").replace(/\s+/g, " ").trim();
|
|
3163
|
+
if (prose.length > 2200)
|
|
3164
|
+
prose = prose.slice(0, 2200) + "\u2026";
|
|
3165
|
+
let text2 = prose;
|
|
3166
|
+
if (toolStr)
|
|
3167
|
+
text2 = (text2 ? text2 + " " : "") + `[ran ${toolStr}]`;
|
|
3168
|
+
if (text2)
|
|
3169
|
+
out.push({ t: aiStart, role: "ai", text: text2 });
|
|
3170
|
+
aiProse = [];
|
|
3171
|
+
aiTools = {};
|
|
3172
|
+
};
|
|
3173
|
+
for (const ev of extractCodexEvents(raw)) {
|
|
3174
|
+
if (!Number.isFinite(ev.t))
|
|
3175
|
+
continue;
|
|
3176
|
+
if (ev.kind === "user") {
|
|
3177
|
+
const text2 = humanTypedText(ev.text);
|
|
3178
|
+
if (text2) {
|
|
3179
|
+
flushAI();
|
|
3180
|
+
out.push({ t: ev.t, role: "user", text: text2 });
|
|
3181
|
+
}
|
|
3182
|
+
} else if (ev.kind === "assistant") {
|
|
3183
|
+
if (!aiProse.length && Object.keys(aiTools).length === 0)
|
|
3184
|
+
aiStart = ev.t;
|
|
3185
|
+
if (ev.text)
|
|
3186
|
+
aiProse.push(ev.text);
|
|
3187
|
+
} else if (ev.kind === "tool") {
|
|
3188
|
+
if (!aiProse.length && Object.keys(aiTools).length === 0)
|
|
3189
|
+
aiStart = ev.t;
|
|
3190
|
+
aiTools[ev.name] = (aiTools[ev.name] ?? 0) + 1;
|
|
3191
|
+
}
|
|
3192
|
+
}
|
|
3193
|
+
flushAI();
|
|
3194
|
+
return out.sort((a, b) => a.t - b.t);
|
|
3195
|
+
}
|
|
3016
3196
|
async function extractMessages(file2) {
|
|
3017
3197
|
let raw;
|
|
3018
3198
|
try {
|
|
@@ -3020,6 +3200,8 @@ async function extractMessages(file2) {
|
|
|
3020
3200
|
} catch {
|
|
3021
3201
|
return [];
|
|
3022
3202
|
}
|
|
3203
|
+
if (sniffCodexRaw(raw))
|
|
3204
|
+
return extractMessagesCodex(raw);
|
|
3023
3205
|
const out = [];
|
|
3024
3206
|
const userTexts = /* @__PURE__ */ new Set();
|
|
3025
3207
|
const seenUuids = /* @__PURE__ */ new Set();
|
|
@@ -3755,7 +3937,7 @@ var LABEL_BY_UIKEY = Object.fromEntries(GRADED_CRITERIA2.map((c) => [c.uiKey, c.
|
|
|
3755
3937
|
// src/grade/materialize.ts
|
|
3756
3938
|
init_injected();
|
|
3757
3939
|
import { promises as fs3 } from "fs";
|
|
3758
|
-
var
|
|
3940
|
+
var SYSTEM_REMINDER_RE3 = /<system-reminder>[\s\S]*?<\/system-reminder>/g;
|
|
3759
3941
|
var INJECT_GIST = 120;
|
|
3760
3942
|
var INJECTED = [
|
|
3761
3943
|
{ re: /<task-notification>[\s\S]*?<\/task-notification>/g, label: "bg task", gistRe: /<summary>([\s\S]*?)<\/summary>/ },
|
|
@@ -3775,7 +3957,7 @@ function collapseInjected(input) {
|
|
|
3775
3957
|
return "";
|
|
3776
3958
|
});
|
|
3777
3959
|
}
|
|
3778
|
-
return { text: text2.replace(
|
|
3960
|
+
return { text: text2.replace(SYSTEM_REMINDER_RE3, "").trim(), markers };
|
|
3779
3961
|
}
|
|
3780
3962
|
function extractText3(content) {
|
|
3781
3963
|
if (typeof content === "string") return content;
|
|
@@ -4447,7 +4629,7 @@ import { promises as fs14 } from "fs";
|
|
|
4447
4629
|
import path19 from "path";
|
|
4448
4630
|
|
|
4449
4631
|
// ../../lib/agents/shared/agent.ts
|
|
4450
|
-
import { readFileSync } from "fs";
|
|
4632
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
4451
4633
|
import path13 from "path";
|
|
4452
4634
|
|
|
4453
4635
|
// ../../lib/agents/shared/cliAdapter.ts
|
|
@@ -19895,7 +20077,7 @@ var ADAPTERS2 = {
|
|
|
19895
20077
|
};
|
|
19896
20078
|
function preferredEngine() {
|
|
19897
20079
|
try {
|
|
19898
|
-
const p = JSON.parse(
|
|
20080
|
+
const p = JSON.parse(readFileSync2(path13.join(process.cwd(), ".data", "profile.json"), "utf-8"));
|
|
19899
20081
|
const e = p?.preferredEngine;
|
|
19900
20082
|
return e === "cli" || e === "codex" || e === "cursor" || e === "api" ? e : null;
|
|
19901
20083
|
} catch {
|
|
@@ -21247,8 +21429,9 @@ import path17 from "node:path";
|
|
|
21247
21429
|
|
|
21248
21430
|
// ../../lib/agents/coding/materialize.ts
|
|
21249
21431
|
init_injected();
|
|
21432
|
+
init_codex();
|
|
21250
21433
|
import { promises as fs11 } from "fs";
|
|
21251
|
-
var
|
|
21434
|
+
var SYSTEM_REMINDER_RE4 = /<system-reminder>[\s\S]*?<\/system-reminder>/g;
|
|
21252
21435
|
var INJECT_GIST2 = 120;
|
|
21253
21436
|
var INJECTED2 = [
|
|
21254
21437
|
{ re: /<task-notification>[\s\S]*?<\/task-notification>/g, label: "bg task", gistRe: /<summary>([\s\S]*?)<\/summary>/ },
|
|
@@ -21268,7 +21451,7 @@ function collapseInjected2(input) {
|
|
|
21268
21451
|
return "";
|
|
21269
21452
|
});
|
|
21270
21453
|
}
|
|
21271
|
-
return { text: text2.replace(
|
|
21454
|
+
return { text: text2.replace(SYSTEM_REMINDER_RE4, "").trim(), markers };
|
|
21272
21455
|
}
|
|
21273
21456
|
function extractText4(content) {
|
|
21274
21457
|
if (typeof content === "string") return content;
|
|
@@ -21285,6 +21468,38 @@ function isToolResultOnly3(content) {
|
|
|
21285
21468
|
return Array.isArray(content) && content.length > 0 && content.every((c) => c && typeof c === "object" && c.type === "tool_result");
|
|
21286
21469
|
}
|
|
21287
21470
|
var AI_GIST2 = 160;
|
|
21471
|
+
function materializeCodex(raw) {
|
|
21472
|
+
const lines = [];
|
|
21473
|
+
let humanTurns = 0, humanChars = 0;
|
|
21474
|
+
let aiLastProse = "";
|
|
21475
|
+
const aiTools = /* @__PURE__ */ new Map();
|
|
21476
|
+
const flushAI = () => {
|
|
21477
|
+
if (!aiLastProse && aiTools.size === 0) return;
|
|
21478
|
+
const tools = [...aiTools.entries()].map(([n, c]) => c > 1 ? `${n}\xD7${c}` : n).join(", ");
|
|
21479
|
+
const gist = aiLastProse ? aiLastProse.length > AI_GIST2 ? aiLastProse.slice(0, AI_GIST2) + "\u2026" : aiLastProse : "";
|
|
21480
|
+
const bits = [];
|
|
21481
|
+
if (gist) bits.push(gist);
|
|
21482
|
+
if (tools) bits.push(`ran ${tools}`);
|
|
21483
|
+
lines.push(`[AI: ${bits.join(" \xB7 ")}]`);
|
|
21484
|
+
aiLastProse = "";
|
|
21485
|
+
aiTools.clear();
|
|
21486
|
+
};
|
|
21487
|
+
for (const ev of extractCodexEvents(raw)) {
|
|
21488
|
+
if (ev.kind === "user") {
|
|
21489
|
+
flushAI();
|
|
21490
|
+
humanTurns++;
|
|
21491
|
+
humanChars += ev.text.length;
|
|
21492
|
+
lines.push(`>> ${ev.text}`);
|
|
21493
|
+
} else if (ev.kind === "assistant") {
|
|
21494
|
+
const prose = ev.text.replace(/\s+/g, " ").trim();
|
|
21495
|
+
if (prose) aiLastProse = prose;
|
|
21496
|
+
} else if (ev.kind === "tool") {
|
|
21497
|
+
aiTools.set(ev.name, (aiTools.get(ev.name) ?? 0) + 1);
|
|
21498
|
+
}
|
|
21499
|
+
}
|
|
21500
|
+
flushAI();
|
|
21501
|
+
return { text: lines.join("\n"), humanTurns, humanChars };
|
|
21502
|
+
}
|
|
21288
21503
|
async function materializeSession2(file2) {
|
|
21289
21504
|
let raw;
|
|
21290
21505
|
try {
|
|
@@ -21292,6 +21507,7 @@ async function materializeSession2(file2) {
|
|
|
21292
21507
|
} catch {
|
|
21293
21508
|
return null;
|
|
21294
21509
|
}
|
|
21510
|
+
if (sniffCodexRaw(raw)) return materializeCodex(raw);
|
|
21295
21511
|
const lines = [];
|
|
21296
21512
|
let humanTurns = 0, humanChars = 0;
|
|
21297
21513
|
const pendingQueued = [];
|
|
@@ -21827,124 +22043,6 @@ function handleDiagnostic(method, body, key2) {
|
|
|
21827
22043
|
// src/codingChat.ts
|
|
21828
22044
|
init_agent();
|
|
21829
22045
|
|
|
21830
|
-
// ../../lib/agents/coding/chat-context.ts
|
|
21831
|
-
var RUNGS_BY_KEY = new Map(CODING_CRITERIA.map((c) => [c.key, c.rungs ?? []]));
|
|
21832
|
-
var clip = (s, n) => {
|
|
21833
|
-
const t = (s || "").replace(/\s+/g, " ").trim();
|
|
21834
|
-
return t.length > n ? t.slice(0, n - 1).replace(/\s+\S*$/, "") + "\u2026" : t;
|
|
21835
|
-
};
|
|
21836
|
-
function criterionBlock(c) {
|
|
21837
|
-
if (!c.graded || c.dist.n <= 0) return null;
|
|
21838
|
-
const lines = [];
|
|
21839
|
-
const mean2 = c.dist.mean != null ? `${c.dist.mean}` : "\u2014";
|
|
21840
|
-
lines.push(`### ${c.label} (your mean ${mean2} across ${c.dist.n} graded session${c.dist.n === 1 ? "" : "s"})`);
|
|
21841
|
-
lines.push(`What it measures: ${clip(c.definition, 400)}`);
|
|
21842
|
-
const rungs = RUNGS_BY_KEY.get(c.key) ?? [];
|
|
21843
|
-
if (rungs.length) {
|
|
21844
|
-
lines.push(`The ladder I grade against:`);
|
|
21845
|
-
for (const r of rungs) lines.push(` L${r.level} \u2014 ${r.marker}`);
|
|
21846
|
-
}
|
|
21847
|
-
const takes = (c.takes || []).slice(0, 5);
|
|
21848
|
-
if (takes.length) {
|
|
21849
|
-
lines.push(`How I scored your sessions here:`);
|
|
21850
|
-
for (const t of takes) {
|
|
21851
|
-
const sc = t.score != null ? `${t.score}` : t.best != null ? `~${t.best}` : "\u2014";
|
|
21852
|
-
const parts = [` \u2022 "${clip(t.title, 80)}" (${(t.date || "").slice(0, 10)}) \u2014 scored ${sc}${t.confidence ? ` (${t.confidence})` : ""}.`];
|
|
21853
|
-
if (t.instance) parts.push(`What showed it: ${clip(t.instance, 240)}`);
|
|
21854
|
-
if (t.why) parts.push(`Why that score: ${clip(t.why, 320)}`);
|
|
21855
|
-
const qs = (t.quotes || []).filter(Boolean).slice(0, 2).map((q) => `"${clip(q, 160)}"`);
|
|
21856
|
-
if (qs.length) parts.push(`You said: ${qs.join(" / ")}`);
|
|
21857
|
-
lines.push(parts.join(" "));
|
|
21858
|
-
}
|
|
21859
|
-
}
|
|
21860
|
-
return lines.join("\n");
|
|
21861
|
-
}
|
|
21862
|
-
function buildCodingContext(profile) {
|
|
21863
|
-
const out = [];
|
|
21864
|
-
const t = profile.throughput;
|
|
21865
|
-
const p = profile.parallelism;
|
|
21866
|
-
const meta = [];
|
|
21867
|
-
if (t?.sessions != null) meta.push(`${t.sessions} interactive coding sessions`);
|
|
21868
|
-
if (profile.gradedSessions != null) meta.push(`${profile.gradedSessions} graded`);
|
|
21869
|
-
if (p?.maxConcurrency != null) meta.push(`peak ${p.maxConcurrency} sessions in parallel`);
|
|
21870
|
-
if (meta.length) out.push(`HOW THEY WORK: ${meta.join(" \xB7 ")}.`);
|
|
21871
|
-
const blocks = (profile.criteria || []).map(criterionBlock).filter((b) => !!b);
|
|
21872
|
-
if (blocks.length) {
|
|
21873
|
-
out.push(`
|
|
21874
|
-
YOUR GRADED CRITERIA \u2014 the rubric ladder and how I scored you on each, with your own words as the receipts:`);
|
|
21875
|
-
out.push(blocks.join("\n\n"));
|
|
21876
|
-
} else {
|
|
21877
|
-
out.push(`
|
|
21878
|
-
(No criteria have been graded yet, so I can only speak to how you work, not the qualitative scores.)`);
|
|
21879
|
-
}
|
|
21880
|
-
return out.join("\n");
|
|
21881
|
-
}
|
|
21882
|
-
var CODING_CHAT_SYSTEM = `You are the analyst who graded this person's coding-agent sessions (their Claude Code / Codex logs) against a FIXED rubric, and you are now talking WITH them about their report. Below you are given that rubric \u2014 the definition and the level ladder for each criterion \u2014 AND your own per-session grades: the exact moment that keyed each score, WHY you placed it on that rung, and their verbatim quotes. Everything you claim comes from that material.
|
|
21883
|
-
|
|
21884
|
-
HOW TO ANSWER:
|
|
21885
|
-
- Ground every claim in the receipts you were given. When they ask "why did I get that score on X", name the specific session, the rung you landed them on, what that rung requires, and quote their actual words. Never invent a session, a score, a rung, or a criterion that isn't below.
|
|
21886
|
-
- The rubric is the authority. If they push on a score, explain the ladder honestly: what rung they hit and what the NEXT rung would concretely have required of them. Don't inflate a score to be nice and don't apologize for a low one \u2014 being read accurately is the point. If they're genuinely strong, say so plainly.
|
|
21887
|
-
- Be honest about the instrument's limits. These grades come only from their coding logs, which UNDER-observe real skill (a lot of thinking never reaches the transcript). When it's relevant, say what you can and can't actually see from the logs rather than overclaiming.
|
|
21888
|
-
- Voice: warm, plain, declarative, second person. Specific, never a horoscope, never flattery. Numbers are calibration, not a verdict \u2014 don't recite them at the person.
|
|
21889
|
-
- Keep replies tight and human unless they ask you to go deep. Output ONLY your reply \u2014 no preamble, no meta.
|
|
21890
|
-
|
|
21891
|
-
Do NOT use em dashes. Use a comma or a period instead.`;
|
|
21892
|
-
function stripSlop(raw) {
|
|
21893
|
-
return (raw || "").trim().replace(/\s*—\s*/g, ", ").replace(/\s+–\s+/g, ", ").replace(/\s+,/g, ",");
|
|
21894
|
-
}
|
|
21895
|
-
function buildCodingChatPrompt(profile, messages) {
|
|
21896
|
-
const context = buildCodingContext(profile);
|
|
21897
|
-
const history = messages.map((m) => `${m.role === "user" ? "Them" : "You"}: ${m.content}`).join("\n\n");
|
|
21898
|
-
return `YOUR READ OF THIS PERSON (the rubric + your grades + their receipts):
|
|
21899
|
-
${context}
|
|
21900
|
-
|
|
21901
|
-
--- the conversation so far ---
|
|
21902
|
-
${history}
|
|
21903
|
-
|
|
21904
|
-
Write your next reply to them now.`;
|
|
21905
|
-
}
|
|
21906
|
-
|
|
21907
|
-
// src/codingChat.ts
|
|
21908
|
-
async function handleCodingChat(profile, dataDir, body, res) {
|
|
21909
|
-
const raw = Array.isArray(body.messages) ? body.messages : [];
|
|
21910
|
-
const messages = raw.filter((m) => (m.role === "user" || m.role === "assistant") && typeof m.content === "string").slice(-20);
|
|
21911
|
-
if (!messages.length) {
|
|
21912
|
-
res.writeHead(400, { "Content-Type": "application/json; charset=utf-8" });
|
|
21913
|
-
res.end(JSON.stringify({ error: "no messages" }));
|
|
21914
|
-
return;
|
|
21915
|
-
}
|
|
21916
|
-
const prompt = buildCodingChatPrompt(profile, messages);
|
|
21917
|
-
res.writeHead(200, {
|
|
21918
|
-
"Content-Type": "application/x-ndjson; charset=utf-8",
|
|
21919
|
-
"Cache-Control": "no-store, no-transform",
|
|
21920
|
-
"X-Accel-Buffering": "no"
|
|
21921
|
-
});
|
|
21922
|
-
const send = (o) => {
|
|
21923
|
-
try {
|
|
21924
|
-
res.write(JSON.stringify(o) + "\n");
|
|
21925
|
-
} catch {
|
|
21926
|
-
}
|
|
21927
|
-
};
|
|
21928
|
-
try {
|
|
21929
|
-
const run4 = await invokeAgent({
|
|
21930
|
-
prompt,
|
|
21931
|
-
systemPrompt: CODING_CHAT_SYSTEM,
|
|
21932
|
-
// Everything it needs is in the prompt; dataDir is a harmless read-only
|
|
21933
|
-
// sandbox. maxTurns 2 → answer directly, never a tool-loop.
|
|
21934
|
-
addDir: dataDir,
|
|
21935
|
-
allowedTools: ["Read"],
|
|
21936
|
-
maxTurns: 2,
|
|
21937
|
-
model: "sonnet",
|
|
21938
|
-
onText: (delta) => send({ t: "delta", v: delta })
|
|
21939
|
-
});
|
|
21940
|
-
send({ t: "done", reply: stripSlop(run4.raw || "") || "(no response)" });
|
|
21941
|
-
} catch {
|
|
21942
|
-
send({ t: "error", message: "Couldn't reach your local Claude \u2014 is Claude Code (or Codex) installed and logged in?" });
|
|
21943
|
-
} finally {
|
|
21944
|
-
res.end();
|
|
21945
|
-
}
|
|
21946
|
-
}
|
|
21947
|
-
|
|
21948
22046
|
// src/personApi.ts
|
|
21949
22047
|
init_agent();
|
|
21950
22048
|
import { promises as fs18 } from "fs";
|
|
@@ -22507,6 +22605,137 @@ ${dataMap}`,
|
|
|
22507
22605
|
}
|
|
22508
22606
|
}
|
|
22509
22607
|
|
|
22608
|
+
// ../../lib/agents/coding/chat-context.ts
|
|
22609
|
+
var RUNGS_BY_KEY = new Map(CODING_CRITERIA.map((c) => [c.key, c.rungs ?? []]));
|
|
22610
|
+
var clip = (s, n) => {
|
|
22611
|
+
const t = (s || "").replace(/\s+/g, " ").trim();
|
|
22612
|
+
return t.length > n ? t.slice(0, n - 1).replace(/\s+\S*$/, "") + "\u2026" : t;
|
|
22613
|
+
};
|
|
22614
|
+
function criterionBlock(c) {
|
|
22615
|
+
if (!c.graded || c.dist.n <= 0) return null;
|
|
22616
|
+
const lines = [];
|
|
22617
|
+
const mean2 = c.dist.mean != null ? `${c.dist.mean}` : "\u2014";
|
|
22618
|
+
lines.push(`### ${c.label} (your mean ${mean2} across ${c.dist.n} graded session${c.dist.n === 1 ? "" : "s"})`);
|
|
22619
|
+
lines.push(`What it measures: ${clip(c.definition, 400)}`);
|
|
22620
|
+
const rungs = RUNGS_BY_KEY.get(c.key) ?? [];
|
|
22621
|
+
if (rungs.length) {
|
|
22622
|
+
lines.push(`The ladder I grade against:`);
|
|
22623
|
+
for (const r of rungs) lines.push(` L${r.level} \u2014 ${r.marker}`);
|
|
22624
|
+
}
|
|
22625
|
+
const takes = (c.takes || []).slice(0, 5);
|
|
22626
|
+
if (takes.length) {
|
|
22627
|
+
lines.push(`How I scored your sessions here:`);
|
|
22628
|
+
for (const t of takes) {
|
|
22629
|
+
const sc = t.score != null ? `${t.score}` : t.best != null ? `~${t.best}` : "\u2014";
|
|
22630
|
+
const parts = [` \u2022 "${clip(t.title, 80)}" (${(t.date || "").slice(0, 10)}) \u2014 scored ${sc}${t.confidence ? ` (${t.confidence})` : ""}.`];
|
|
22631
|
+
if (t.instance) parts.push(`What showed it: ${clip(t.instance, 240)}`);
|
|
22632
|
+
if (t.why) parts.push(`Why that score: ${clip(t.why, 320)}`);
|
|
22633
|
+
const qs = (t.quotes || []).filter(Boolean).slice(0, 2).map((q) => `"${clip(q, 160)}"`);
|
|
22634
|
+
if (qs.length) parts.push(`You said: ${qs.join(" / ")}`);
|
|
22635
|
+
lines.push(parts.join(" "));
|
|
22636
|
+
}
|
|
22637
|
+
}
|
|
22638
|
+
return lines.join("\n");
|
|
22639
|
+
}
|
|
22640
|
+
var CODING_DATA_HOME_MAP = `THE DATA HOME YOU CAN READ (tools: Read, Grep, Glob; every path is relative to the extra read-only directory you were given):
|
|
22641
|
+
- coding/sessions.json \u2014 every session: title, times, activeMin, humanTurns, tool counts, klass (only "interactive" sessions are the person; the rest are machine runs), duplicateOf (fork copies \u2014 skip them or you double-count).
|
|
22642
|
+
- coding/focus.json \u2014 the flow analysis: per-day half-hour cells, steering runs, gaps, false starts, workstream features, causal findings. coding/flow.json \u2014 the older looser metric; the report calls it "engaged time", never flow. Do not conflate the two.
|
|
22643
|
+
- coding/day-digest.json and coding/day-chart.json \u2014 per-day narratives and per-conversation active intervals.
|
|
22644
|
+
- coding/projects.json \u2014 per-repo attribution. coding/delegation/ \u2014 delegation pattern reads.
|
|
22645
|
+
- coding/grades/*.json \u2014 per-session rubric grades (score on the 1-11 ladder, keyed instance, why, verbatim quotes). coding/walkthroughs/ \u2014 per-session walkthroughs.
|
|
22646
|
+
- coding/aggregate.json (ability ceilings + the bigPicture nutshell), coding/expertise.json, coding/gap.json (ranked technique recommendations), coding/coaching.json, coding/frontier.json, coding/frontier-detail.json, coding/concurrency.json \u2014 each self-describing; read the file before citing it.
|
|
22647
|
+
Trust the filesystem over this list: Glob/ls a directory before assuming its contents, and a file may simply not exist yet on a fresh run \u2014 say so instead of guessing. Budget 2-4 lookups per question, then answer.`;
|
|
22648
|
+
function buildCodingContext(profile) {
|
|
22649
|
+
const out = [];
|
|
22650
|
+
const t = profile.throughput;
|
|
22651
|
+
const p = profile.parallelism;
|
|
22652
|
+
const meta = [];
|
|
22653
|
+
if (t?.sessions != null) meta.push(`${t.sessions} interactive coding sessions`);
|
|
22654
|
+
if (profile.gradedSessions != null) meta.push(`${profile.gradedSessions} graded`);
|
|
22655
|
+
if (p?.maxConcurrency != null) meta.push(`peak ${p.maxConcurrency} sessions in parallel`);
|
|
22656
|
+
if (meta.length) out.push(`HOW THEY WORK: ${meta.join(" \xB7 ")}.`);
|
|
22657
|
+
const blocks = (profile.criteria || []).map(criterionBlock).filter((b) => !!b);
|
|
22658
|
+
if (blocks.length) {
|
|
22659
|
+
out.push(`
|
|
22660
|
+
YOUR GRADED CRITERIA \u2014 the rubric ladder and how I scored you on each, with your own words as the receipts:`);
|
|
22661
|
+
out.push(blocks.join("\n\n"));
|
|
22662
|
+
} else {
|
|
22663
|
+
out.push(`
|
|
22664
|
+
NO QUALITATIVE GRADES YET: the rubric grading pass has not scored any sessions, so there are no criterion scores, rungs, or graded receipts to cite. If they ask about grades or scores, say that plainly and never fabricate one. Everything measurable is still answerable: read the deterministic artifacts in the data home (sessions, focus/flow, projects, day digests) and give them real numbers with dates instead of shrugging.`);
|
|
22665
|
+
}
|
|
22666
|
+
out.push(`
|
|
22667
|
+
${CODING_DATA_HOME_MAP}`);
|
|
22668
|
+
return out.join("\n");
|
|
22669
|
+
}
|
|
22670
|
+
var CODING_CHAT_SYSTEM = `You are the analyst who graded this person's coding-agent sessions (their Claude Code / Codex logs) against a FIXED rubric, and you are now talking WITH them about their report. Below you are given that rubric \u2014 the definition and the level ladder for each criterion \u2014 AND your own per-session grades: the exact moment that keyed each score, WHY you placed it on that rung, and their verbatim quotes. Everything you claim comes from that material or from the served data-home artifacts you can read with your tools (the grounding block maps them) \u2014 never invent a number, score, session, or quote that no file backs. The person's raw session logs are in bounds only through the artifacts present in the data home; do not hunt outside the directory you were given.
|
|
22671
|
+
|
|
22672
|
+
HOW TO ANSWER:
|
|
22673
|
+
- Ground every claim in the receipts you were given. When they ask "why did I get that score on X", name the specific session, the rung you landed them on, what that rung requires, and quote their actual words. Never invent a session, a score, a rung, or a criterion that isn't below.
|
|
22674
|
+
- The rubric is the authority. If they push on a score, explain the ladder honestly: what rung they hit and what the NEXT rung would concretely have required of them. Don't inflate a score to be nice and don't apologize for a low one \u2014 being read accurately is the point. If they're genuinely strong, say so plainly.
|
|
22675
|
+
- Be honest about the instrument's limits. These grades come only from their coding logs, which UNDER-observe real skill (a lot of thinking never reaches the transcript). When it's relevant, say what you can and can't actually see from the logs rather than overclaiming.
|
|
22676
|
+
- Voice: warm, plain, declarative, second person. Specific, never a horoscope, never flattery. Numbers are calibration, not a verdict \u2014 don't recite them at the person.
|
|
22677
|
+
- Keep replies tight and human unless they ask you to go deep. Output ONLY your reply \u2014 no preamble, no meta.
|
|
22678
|
+
|
|
22679
|
+
Do NOT use em dashes. Use a comma or a period instead.`;
|
|
22680
|
+
function stripSlop(raw) {
|
|
22681
|
+
return (raw || "").trim().replace(/\s*—\s*/g, ", ").replace(/\s+–\s+/g, ", ").replace(/\s+,/g, ",");
|
|
22682
|
+
}
|
|
22683
|
+
function buildCodingChatPrompt(profile, messages) {
|
|
22684
|
+
const context = buildCodingContext(profile);
|
|
22685
|
+
const history = messages.map((m) => `${m.role === "user" ? "Them" : "You"}: ${m.content}`).join("\n\n");
|
|
22686
|
+
return `YOUR READ OF THIS PERSON (the rubric + your grades + their receipts):
|
|
22687
|
+
${context}
|
|
22688
|
+
|
|
22689
|
+
--- the conversation so far ---
|
|
22690
|
+
${history}
|
|
22691
|
+
|
|
22692
|
+
Write your next reply to them now.`;
|
|
22693
|
+
}
|
|
22694
|
+
|
|
22695
|
+
// src/codingChat.ts
|
|
22696
|
+
async function handleCodingChat(profile, dataDir, body, res) {
|
|
22697
|
+
const raw = Array.isArray(body.messages) ? body.messages : [];
|
|
22698
|
+
const messages = raw.filter((m) => (m.role === "user" || m.role === "assistant") && typeof m.content === "string").slice(-20);
|
|
22699
|
+
if (!messages.length) {
|
|
22700
|
+
res.writeHead(400, { "Content-Type": "application/json; charset=utf-8" });
|
|
22701
|
+
res.end(JSON.stringify({ error: "no messages" }));
|
|
22702
|
+
return;
|
|
22703
|
+
}
|
|
22704
|
+
const prompt = buildCodingChatPrompt(profile, messages);
|
|
22705
|
+
res.writeHead(200, {
|
|
22706
|
+
"Content-Type": "application/x-ndjson; charset=utf-8",
|
|
22707
|
+
"Cache-Control": "no-store, no-transform",
|
|
22708
|
+
"X-Accel-Buffering": "no"
|
|
22709
|
+
});
|
|
22710
|
+
const send = (o) => {
|
|
22711
|
+
try {
|
|
22712
|
+
res.write(JSON.stringify(o) + "\n");
|
|
22713
|
+
} catch {
|
|
22714
|
+
}
|
|
22715
|
+
};
|
|
22716
|
+
try {
|
|
22717
|
+
const dataMap = await readDataMap();
|
|
22718
|
+
const run4 = await invokeAgent({
|
|
22719
|
+
prompt,
|
|
22720
|
+
systemPrompt: `${CODING_CHAT_SYSTEM}
|
|
22721
|
+
|
|
22722
|
+
AGENT MODE: you also have read-only tools (Read/Grep/Glob) over the person's local record. Use them whenever the pre-baked context can't answer precisely \u2014 the map below tells you what lives where. Traverse first, then answer with receipts. Never claim you lack access to something the map covers.
|
|
22723
|
+
|
|
22724
|
+
${dataMap}`,
|
|
22725
|
+
addDir: dataDir,
|
|
22726
|
+
allowedTools: ["Read", "Grep", "Glob"],
|
|
22727
|
+
maxTurns: 16,
|
|
22728
|
+
model: "sonnet",
|
|
22729
|
+
onText: (delta) => send({ t: "delta", v: delta })
|
|
22730
|
+
});
|
|
22731
|
+
send({ t: "done", reply: stripSlop(run4.raw || "") || "(no response)" });
|
|
22732
|
+
} catch {
|
|
22733
|
+
send({ t: "error", message: "Couldn't reach your local Claude \u2014 is Claude Code (or Codex) installed and logged in?" });
|
|
22734
|
+
} finally {
|
|
22735
|
+
res.end();
|
|
22736
|
+
}
|
|
22737
|
+
}
|
|
22738
|
+
|
|
22510
22739
|
// src/publicReportApi.ts
|
|
22511
22740
|
init_agent();
|
|
22512
22741
|
|
|
@@ -23066,6 +23295,8 @@ init_exportLinks();
|
|
|
23066
23295
|
// src/publicCodingPageApi.ts
|
|
23067
23296
|
import { promises as fs28 } from "fs";
|
|
23068
23297
|
import path32 from "path";
|
|
23298
|
+
import os7 from "os";
|
|
23299
|
+
import crypto3 from "crypto";
|
|
23069
23300
|
|
|
23070
23301
|
// ../../lib/agents/coding/publicPage.ts
|
|
23071
23302
|
import { promises as fs27 } from "fs";
|
|
@@ -23541,6 +23772,18 @@ function distOf3(takes) {
|
|
|
23541
23772
|
const hist = [...h.keys()].sort((a, b) => b - a).map((level) => ({ level, count: h.get(level) }));
|
|
23542
23773
|
return { n, observed, mean: mean2, median: median2, hist };
|
|
23543
23774
|
}
|
|
23775
|
+
var phi = (z) => {
|
|
23776
|
+
const t = 1 / (1 + 0.2316419 * Math.abs(z));
|
|
23777
|
+
const d = 0.3989423 * Math.exp(-z * z / 2);
|
|
23778
|
+
let pr = d * t * (0.3193815 + t * (-0.3565638 + t * (1.781478 + t * (-1.821256 + t * 1.330274))));
|
|
23779
|
+
if (z > 0) pr = 1 - pr;
|
|
23780
|
+
return pr;
|
|
23781
|
+
};
|
|
23782
|
+
var lognPct = (x, c) => x == null || !c || x <= 0 ? null : Math.min(99.9, Math.max(0.1, Math.round(phi(Math.log(x / c.median) / c.sigma) * 1e3) / 10));
|
|
23783
|
+
var NO_GRADES_GAP = "no sessions graded for this yet \u2014 this fills in once grading has run";
|
|
23784
|
+
function estimateRowParts(score, nTakes, evidenceGap) {
|
|
23785
|
+
return nTakes === 0 && score == null ? { score: null, gap: NO_GRADES_GAP } : { score, gap: evidenceGap };
|
|
23786
|
+
}
|
|
23544
23787
|
async function compileCodingProfile() {
|
|
23545
23788
|
const allRecords = await readJson2(path30.join(CODING_DIR2, "sessions.json"), []);
|
|
23546
23789
|
const dupIds = new Set(allRecords.filter((s) => s.duplicateOf).map((s) => s.sessionId));
|
|
@@ -23795,15 +24038,11 @@ async function compileCodingProfile() {
|
|
|
23795
24038
|
const unb = ceilOf("unblocking");
|
|
23796
24039
|
const agency = out2 != null && unb != null ? Math.round((out2 + unb) / 2) : out2 ?? unb;
|
|
23797
24040
|
const sRow = (label, score, gap2) => ({ label, score, percentile: pctOf(score), gap: gap2 });
|
|
23798
|
-
const
|
|
23799
|
-
|
|
23800
|
-
|
|
23801
|
-
const d = 0.3989423 * Math.exp(-z * z / 2);
|
|
23802
|
-
let pr = d * t * (0.3193815 + t * (-0.3565638 + t * (1.781478 + t * (-1.821256 + t * 1.330274))));
|
|
23803
|
-
if (z > 0) pr = 1 - pr;
|
|
23804
|
-
return pr;
|
|
24041
|
+
const est = (label, score, key2, evidenceGap) => {
|
|
24042
|
+
const e = estimateRowParts(score, nTk(key2), evidenceGap);
|
|
24043
|
+
return sRow(label, e.score, e.gap);
|
|
23805
24044
|
};
|
|
23806
|
-
const
|
|
24045
|
+
const tiers = DEFAULT_CALIBRATION.codingTiers;
|
|
23807
24046
|
const avgWords = aggregate?.throughput?.avgWordsPerDay ?? null;
|
|
23808
24047
|
const pushPctl = lognPct(avgWords, tiers?.push);
|
|
23809
24048
|
const focusPctl = lognPct(flowPctDay, tiers?.focus);
|
|
@@ -23822,10 +24061,16 @@ async function compileCodingProfile() {
|
|
|
23822
24061
|
null,
|
|
23823
24062
|
workDayHrs != null ? `~${workDayHrs}h of active building on a typical work day \u2014 engaged builders average ~3h, and sustaining 7h+ day after day, week after week, is top-1% territory` : "not enough dated activity yet to measure a typical work day"
|
|
23824
24063
|
),
|
|
24064
|
+
// The copy FOLLOWS the person's actual orchestration band (orchPctl above) —
|
|
24065
|
+
// a hardcoded "single biggest speed-up still on the table" once shipped to a
|
|
24066
|
+
// user at orchPctl 99.5 (18 agents peak, 48% of hours at 2+), flatly
|
|
24067
|
+
// contradicting the gap section's "already strong" on the same page
|
|
24068
|
+
// (2026-07-17). The still-on-the-table claim is reserved for genuinely low
|
|
24069
|
+
// orchestration.
|
|
23825
24070
|
sRow(
|
|
23826
24071
|
"Your AI parallelism",
|
|
23827
24072
|
ceilOf("delegation"),
|
|
23828
|
-
soloPct != null ? `~${soloPct}% of your hours you drive a single agent (${Math.round(soloMin / 60)}h solo vs ${Math.round(parMin / 60)}h with 2+ live) \u2014 the Claude Code lead keeps 10\u201315 going at once, so this is the single biggest speed-up still on the table` : "you brief the AI sharply, but mostly drive one agent at a time \u2014 the top run 10\u201315 in parallel, a several-fold speed-up"
|
|
24073
|
+
soloPct != null ? orchPctl >= 97 ? `you already run parallel at the frontier \u2014 ${Math.round(parMin / 60)}h of your hours with 2+ agents live vs ${Math.round(soloMin / 60)}h solo; the Claude Code lead keeps 10\u201315 going at once, and you work the same way` : `~${soloPct}% of your hours you drive a single agent (${Math.round(soloMin / 60)}h solo vs ${Math.round(parMin / 60)}h with 2+ live) \u2014 the Claude Code lead keeps 10\u201315 going at once, so this is the single biggest speed-up still on the table` : "you brief the AI sharply, but mostly drive one agent at a time \u2014 the top run 10\u201315 in parallel, a several-fold speed-up"
|
|
23829
24074
|
),
|
|
23830
24075
|
// The evidence line cites the RANKING metric (flow as a share of the work
|
|
23831
24076
|
// day), never absolute hours — a friend with MORE flow hours over a longer
|
|
@@ -23841,22 +24086,25 @@ async function compileCodingProfile() {
|
|
|
23841
24086
|
// The four estimates LEAD with a concrete moment from their own sessions, then an
|
|
23842
24087
|
// HONEST caveat — how many graded sessions back the read. No invented weakness, no
|
|
23843
24088
|
// claim about what they "didn't" do (false specifics churn a real user); it's an estimate.
|
|
23844
|
-
|
|
24089
|
+
est(
|
|
23845
24090
|
"Agency",
|
|
23846
24091
|
agency,
|
|
24092
|
+
"outcomes",
|
|
23847
24093
|
`${proud("outcomes", "you ship working systems and unblock yourself")} \u2014 one of ${nTk("outcomes")} sessions behind this read; a strong, consistent signal, still an estimate from your logs`
|
|
23848
24094
|
),
|
|
23849
24095
|
// Judgement/Taste use the SAME numbers their ability cards prove (aggregate
|
|
23850
24096
|
// peak-with-consistency over near-peak instances) — a chip saying "top 1%"
|
|
23851
24097
|
// above a card proving 10/Top 0.1% with ten receipts reads as a bug.
|
|
23852
|
-
|
|
24098
|
+
est(
|
|
23853
24099
|
"Judgement",
|
|
23854
24100
|
aggregate?.ability?.abstraction?.score ?? ceilOf("generative"),
|
|
24101
|
+
"generative",
|
|
23855
24102
|
`${proud("generative", "you make sharp, non-obvious calls on what actually matters")} \u2014 one of ${nTk("generative")} sessions we've graded for this; clear so far, not yet a final verdict`
|
|
23856
24103
|
),
|
|
23857
|
-
|
|
24104
|
+
est(
|
|
23858
24105
|
"Taste",
|
|
23859
24106
|
aggregate?.ability?.taste?.score ?? ceilOf("taste"),
|
|
24107
|
+
"taste",
|
|
23860
24108
|
`${proud("taste", "you hold a high, opinionated quality bar")} \u2014 one of ${nTk("taste")} sessions behind this read; a real signal, still an estimate`
|
|
23861
24109
|
)
|
|
23862
24110
|
// Expertise: NO chip — the scored criterion is deprecated; the real judge is
|
|
@@ -24465,6 +24713,49 @@ async function handleAnalysisShare(dataDir, kind, full) {
|
|
|
24465
24713
|
return { status: 502, json: { error: e.message.slice(0, 200) } };
|
|
24466
24714
|
}
|
|
24467
24715
|
}
|
|
24716
|
+
function computeMachineUnion(machines) {
|
|
24717
|
+
const counts = /* @__PURE__ */ new Map();
|
|
24718
|
+
for (const m of Object.values(machines)) for (const s of m.sessions ?? []) counts.set(s.id, (counts.get(s.id) ?? 0) + 1);
|
|
24719
|
+
return {
|
|
24720
|
+
machines: Object.entries(machines).map(([id, m]) => ({ id, label: m.label ?? id, sessions: (m.sessions ?? []).length, syncedAt: m.syncedAt ?? null })),
|
|
24721
|
+
uniqueSessions: counts.size,
|
|
24722
|
+
overlappingSessions: [...counts.values()].filter((n) => n > 1).length
|
|
24723
|
+
};
|
|
24724
|
+
}
|
|
24725
|
+
async function handleAnalysisSync(dataDir) {
|
|
24726
|
+
const cfg = centralConfig();
|
|
24727
|
+
if (!cfg.configured) return { status: 503, json: { error: "central services are disabled in this build" } };
|
|
24728
|
+
const auth = await getAccessToken(dataDir);
|
|
24729
|
+
if (!auth) return { status: 401, json: { error: "sign in with Google (top right) first" } };
|
|
24730
|
+
const mFile = path32.join(dataDir, "machine.json");
|
|
24731
|
+
let machine = await readJson4(mFile, null);
|
|
24732
|
+
if (!machine?.id) {
|
|
24733
|
+
machine = { id: crypto3.randomBytes(8).toString("hex"), label: os7.hostname().replace(/\.local$/, "") };
|
|
24734
|
+
await fs28.writeFile(mFile, JSON.stringify(machine, null, 2));
|
|
24735
|
+
}
|
|
24736
|
+
const rows = await readJson4(
|
|
24737
|
+
path32.join(dataDir, "coding", "sessions.json"),
|
|
24738
|
+
[]
|
|
24739
|
+
);
|
|
24740
|
+
const sessions = rows.filter((r) => r.klass === "interactive" && !r.duplicateOf && r.sessionId).map((r) => ({ id: r.sessionId, start: r.start?.slice(0, 10) ?? null, chars: r.humanChars ?? 0, project: r.project ?? null, source: r.source ?? null }));
|
|
24741
|
+
if (!sessions.length) return { status: 409, json: { error: "nothing to sync yet \u2014 run the analysis first" } };
|
|
24742
|
+
const prior = await readOwnReportContent(cfg, auth.token, auth.identity.userId);
|
|
24743
|
+
try {
|
|
24744
|
+
const machines = prior.machines && typeof prior.machines === "object" ? prior.machines : {};
|
|
24745
|
+
machines[machine.id] = { label: machine.label, syncedAt: (/* @__PURE__ */ new Date()).toISOString(), sessions };
|
|
24746
|
+
const combined = computeMachineUnion(machines);
|
|
24747
|
+
const merged = { format: "coding-pr1", ...prior, machines, combined };
|
|
24748
|
+
const up = await fetch(`${cfg.supabaseUrl}/rest/v1/rpc/submit_report`, {
|
|
24749
|
+
method: "POST",
|
|
24750
|
+
headers: { apikey: cfg.supabaseAnonKey, authorization: `Bearer ${auth.token}`, "content-type": "application/json" },
|
|
24751
|
+
body: JSON.stringify({ p_session_id: null, p_content: JSON.stringify(merged) })
|
|
24752
|
+
});
|
|
24753
|
+
if (!up.ok) return { status: 502, json: { error: `sync failed (HTTP ${up.status})` } };
|
|
24754
|
+
return { status: 200, json: { ok: true, machine: machine.label, ...combined } };
|
|
24755
|
+
} catch (e) {
|
|
24756
|
+
return { status: 502, json: { error: e.message.slice(0, 200) } };
|
|
24757
|
+
}
|
|
24758
|
+
}
|
|
24468
24759
|
|
|
24469
24760
|
// src/server.ts
|
|
24470
24761
|
var __dirname = path33.dirname(fileURLToPath2(import.meta.url));
|
|
@@ -24792,6 +25083,11 @@ function startServer(opts) {
|
|
|
24792
25083
|
json(res, out.status, out.json);
|
|
24793
25084
|
return;
|
|
24794
25085
|
}
|
|
25086
|
+
if (req.method === "POST" && route === "/api/analysis/sync") {
|
|
25087
|
+
const out = await handleAnalysisSync(dataDir);
|
|
25088
|
+
json(res, out.status, out.json);
|
|
25089
|
+
return;
|
|
25090
|
+
}
|
|
24795
25091
|
if (req.method === "POST" && route === "/api/analysis/share") {
|
|
24796
25092
|
const body = JSON.parse(await readBody(req) || "{}");
|
|
24797
25093
|
const out = await handleAnalysisShare(dataDir, body.kind === "chat" ? "chat" : "coding", {
|
|
@@ -24855,7 +25151,7 @@ function startServer(opts) {
|
|
|
24855
25151
|
|
|
24856
25152
|
// src/pipeline.ts
|
|
24857
25153
|
import { spawn as spawn7 } from "child_process";
|
|
24858
|
-
import
|
|
25154
|
+
import os9 from "os";
|
|
24859
25155
|
import path36 from "path";
|
|
24860
25156
|
import { existsSync as existsSync5, promises as fs31 } from "fs";
|
|
24861
25157
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
@@ -24867,10 +25163,10 @@ import { promises as fs30 } from "fs";
|
|
|
24867
25163
|
import path35 from "path";
|
|
24868
25164
|
|
|
24869
25165
|
// ../../lib/agents/shared/paths.ts
|
|
24870
|
-
import
|
|
25166
|
+
import os8 from "os";
|
|
24871
25167
|
import path34 from "path";
|
|
24872
25168
|
function polymathHome() {
|
|
24873
|
-
return process.env.POLYMATH_HOME || path34.join(
|
|
25169
|
+
return process.env.POLYMATH_HOME || path34.join(os8.homedir(), ".polymath-society");
|
|
24874
25170
|
}
|
|
24875
25171
|
function agentDir(agent) {
|
|
24876
25172
|
return path34.join(polymathHome(), agent);
|
|
@@ -25029,7 +25325,7 @@ async function startRunLog(clientInfo = {}, opts = {}) {
|
|
|
25029
25325
|
|
|
25030
25326
|
// src/pipeline.ts
|
|
25031
25327
|
var MODULE_DIR2 = path36.dirname(fileURLToPath3(import.meta.url));
|
|
25032
|
-
function machineBudget(totalMemBytes =
|
|
25328
|
+
function machineBudget(totalMemBytes = os9.totalmem(), cpus = os9.availableParallelism?.() ?? os9.cpus().length, envOverride = process.env.POLYMATH_CONC) {
|
|
25033
25329
|
const envB = Number(envOverride);
|
|
25034
25330
|
if (Number.isFinite(envB) && envB >= 1) return Math.min(32, Math.floor(envB));
|
|
25035
25331
|
const byMem = Math.floor(totalMemBytes / 2 ** 30 * 0.35 / 0.45);
|
|
@@ -25050,6 +25346,7 @@ function stages(o) {
|
|
|
25050
25346
|
const conc = String(o.conc ?? h.grade);
|
|
25051
25347
|
const buildArgs = [];
|
|
25052
25348
|
if (o.codex === false) buildArgs.push("--no-codex");
|
|
25349
|
+
if (o.claudeCode === false) buildArgs.push("--no-claude");
|
|
25053
25350
|
if (o.idleGapMin != null) buildArgs.push("--idle", String(o.idleGapMin));
|
|
25054
25351
|
const gradeArgs = ["--all", "--model", gradeModel, "--conc", conc];
|
|
25055
25352
|
if (o.regrade) gradeArgs.push("--regrade");
|
|
@@ -25279,6 +25576,7 @@ async function runPipeline(o) {
|
|
|
25279
25576
|
`\u2713 report is up to date \u2014 ${gate2.pending} new session(s) since the last full run (below the ${threshold}-session threshold). Skipping the AI stages; free metrics still refresh. They'll be analyzed once ${threshold}+ accumulate (POLYMATH_MIN_NEW_SESSIONS=0 or --regrade to force now).`
|
|
25280
25577
|
);
|
|
25281
25578
|
} else if (gate2.hasPriorRun) {
|
|
25579
|
+
o.onNewWork?.(gate2.pending);
|
|
25282
25580
|
o.onLine?.(
|
|
25283
25581
|
`\u2713 your finished report from the last run is safe and already serving at the link below \u2014 nothing restarts from scratch. ${gate2.pending >= 0 ? `${gate2.pending} pending session(s)` : "New work"} plus any stages added by an update run on top of it, and sections refresh in place as they finish.`
|
|
25284
25582
|
);
|
|
@@ -25480,6 +25778,8 @@ async function runChatPipeline(o) {
|
|
|
25480
25778
|
upToDate = true;
|
|
25481
25779
|
o.onUpToDate?.();
|
|
25482
25780
|
o.onLine?.(`\u2713 chat report is up to date \u2014 ${newDocs} new doc(s) since the last full run (below the ${minNewDocs}-doc threshold), so the AI stages are skipped and the existing report is served as-is. POLYMATH_MIN_NEW_DOCS=0 forces a full run.`);
|
|
25781
|
+
} else {
|
|
25782
|
+
o.onNewWork?.(newDocs);
|
|
25483
25783
|
}
|
|
25484
25784
|
}
|
|
25485
25785
|
}
|
|
@@ -25719,6 +26019,11 @@ Graded ${detail.gradedSessions} sessions \u2192 ${gradeOut}/graded.json`);
|
|
|
25719
26019
|
}
|
|
25720
26020
|
if (opts.cmd === "serve") {
|
|
25721
26021
|
let earlyHandle = null;
|
|
26022
|
+
const consentManifest = await readManifest(DATA_ROOT);
|
|
26023
|
+
const consent = agentFlagsFromManifest(consentManifest);
|
|
26024
|
+
const consentWhy = (id) => consentManifest?.agents?.excluded.includes(id) ? "you excluded it during setup" : "setup never asked about it";
|
|
26025
|
+
if (!consent.claudeCode) console.error(` \u26D4 Claude Code logs will NOT be analyzed \u2014 ${consentWhy("claude-code")}. POLYMATH_WIZARD=1 polymath-society to change.`);
|
|
26026
|
+
if (!consent.codex) console.error(` \u26D4 Codex logs will NOT be analyzed \u2014 ${consentWhy("codex")}. POLYMATH_WIZARD=1 polymath-society to change.`);
|
|
25722
26027
|
const runMarker = path41.join(DATA_ROOT, "coding", ".grade-run.json");
|
|
25723
26028
|
if (!opts.grade) {
|
|
25724
26029
|
try {
|
|
@@ -25796,12 +26101,19 @@ Running the full analysis on ${backend.name === "cli" ? "Claude Code" : "Codex"}
|
|
|
25796
26101
|
mbar.setLink(earlyHandle.url);
|
|
25797
26102
|
const codingLane = mbar.lane("coding");
|
|
25798
26103
|
let codingPinned = false;
|
|
26104
|
+
if (await readPriorAnalysis()) {
|
|
26105
|
+
codingPinned = true;
|
|
26106
|
+
progress.coding = { i: 2, total: 1, label: "up to date \u2014 checking for anything new", done: false };
|
|
26107
|
+
earlyHandle.setProgress(progress);
|
|
26108
|
+
codingLane.finish("up to date \u2014 checking for anything new");
|
|
26109
|
+
}
|
|
25799
26110
|
const codingDone = runPipeline({
|
|
25800
26111
|
repoRoot: REPO_ROOT,
|
|
25801
26112
|
model: opts.model,
|
|
25802
26113
|
conc: opts.conc,
|
|
25803
26114
|
overnight: opts.overnight,
|
|
25804
|
-
codex: opts.codex,
|
|
26115
|
+
codex: opts.codex && consent.codex,
|
|
26116
|
+
claudeCode: consent.claudeCode,
|
|
25805
26117
|
idleGapMin: opts.idleGapMin,
|
|
25806
26118
|
regrade: opts.regrade,
|
|
25807
26119
|
onUpToDate: () => {
|
|
@@ -25810,14 +26122,22 @@ Running the full analysis on ${backend.name === "cli" ? "Claude Code" : "Codex"}
|
|
|
25810
26122
|
earlyHandle.setProgress(progress);
|
|
25811
26123
|
codingLane.finish("everything up to date");
|
|
25812
26124
|
},
|
|
26125
|
+
onNewWork: (pending2) => {
|
|
26126
|
+
codingPinned = false;
|
|
26127
|
+
codingLane.log(` ${pending2} new session(s) to analyze \u2014 updating your report in place.`);
|
|
26128
|
+
},
|
|
25813
26129
|
onStage: (i, total, label, llm) => {
|
|
25814
26130
|
if (codingPinned) return;
|
|
25815
26131
|
progress.coding = { ...progress.coding, i, total, label, done: false };
|
|
25816
26132
|
earlyHandle.setProgress(progress);
|
|
25817
26133
|
codingLane.stage(i, total, `${label}${llm ? " (AI)" : ""}`);
|
|
25818
26134
|
},
|
|
26135
|
+
// While pinned, the scrolling stage output stays quiet ("prints hella
|
|
26136
|
+
// things when everything is cached", owner 2026-07-17) — warnings and
|
|
26137
|
+
// failures always pass through.
|
|
25819
26138
|
onLine: (line) => {
|
|
25820
26139
|
harvestWithin("coding", line);
|
|
26140
|
+
if (codingPinned && !/⚠|error|fail/i.test(line)) return;
|
|
25821
26141
|
codingLane.log(` ${line}`);
|
|
25822
26142
|
}
|
|
25823
26143
|
}).then(async (codingRes2) => {
|
|
@@ -25850,6 +26170,16 @@ Running the full analysis on ${backend.name === "cli" ? "Claude Code" : "Codex"}
|
|
|
25850
26170
|
}
|
|
25851
26171
|
chatLane.log(` chat analysis starting over your ${manifest.exports.included.length} approved source(s) \u2014 same engine as the hosted app.`);
|
|
25852
26172
|
let chatPinned = false;
|
|
26173
|
+
const chatComplete = await fs36.readFile(path41.join(DATA_ROOT, "engine-pilot", "public-report.json"), "utf8").then((s) => {
|
|
26174
|
+
const r = JSON.parse(s);
|
|
26175
|
+
return !!(r.headline && (r.spikes?.length ?? 0) > 0);
|
|
26176
|
+
}).catch(() => false);
|
|
26177
|
+
if (chatComplete) {
|
|
26178
|
+
chatPinned = true;
|
|
26179
|
+
progress.chat = { i: 2, total: 1, label: "up to date \u2014 checking for anything new", done: false };
|
|
26180
|
+
earlyHandle.setProgress(progress);
|
|
26181
|
+
chatLane.finish("up to date \u2014 checking for anything new");
|
|
26182
|
+
}
|
|
25853
26183
|
const chat2 = await runChatPipeline({
|
|
25854
26184
|
repoRoot: REPO_ROOT,
|
|
25855
26185
|
model: opts.model,
|
|
@@ -25860,6 +26190,10 @@ Running the full analysis on ${backend.name === "cli" ? "Claude Code" : "Codex"}
|
|
|
25860
26190
|
earlyHandle.setProgress(progress);
|
|
25861
26191
|
chatLane.finish("everything up to date");
|
|
25862
26192
|
},
|
|
26193
|
+
onNewWork: (pending2) => {
|
|
26194
|
+
chatPinned = false;
|
|
26195
|
+
chatLane.log(` ${pending2} new doc(s) to analyze \u2014 updating your chat report in place.`);
|
|
26196
|
+
},
|
|
25863
26197
|
onStage: (i, total, label, llm) => {
|
|
25864
26198
|
if (chatPinned) return;
|
|
25865
26199
|
progress.chat = { ...progress.chat, i, total, label, done: false };
|
|
@@ -25868,6 +26202,7 @@ Running the full analysis on ${backend.name === "cli" ? "Claude Code" : "Codex"}
|
|
|
25868
26202
|
},
|
|
25869
26203
|
onLine: (line) => {
|
|
25870
26204
|
harvestWithin("chat", line);
|
|
26205
|
+
if (chatPinned && !/⚠|error|fail/i.test(line)) return;
|
|
25871
26206
|
chatLane.log(` ${line}`);
|
|
25872
26207
|
}
|
|
25873
26208
|
});
|
|
@@ -25923,7 +26258,8 @@ No analysis here yet \u2014 computing the instant deterministic metrics now (fre
|
|
|
25923
26258
|
const det = await runPipeline({
|
|
25924
26259
|
repoRoot: REPO_ROOT,
|
|
25925
26260
|
deterministicOnly: true,
|
|
25926
|
-
codex: opts.codex,
|
|
26261
|
+
codex: opts.codex && consent.codex,
|
|
26262
|
+
claudeCode: consent.claudeCode,
|
|
25927
26263
|
idleGapMin: opts.idleGapMin,
|
|
25928
26264
|
onStage: (i, total, label) => dbar.stage(i, total, label),
|
|
25929
26265
|
onLine: (line) => dbar.log(` ${line}`)
|