tldr-experts 0.9.2 → 0.11.0
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/CHANGELOG.md +286 -0
- package/README.md +6 -2
- package/dist/hooks/answer-capture.js +297 -25
- package/dist/hooks/budget-gate.js +7 -10
- package/dist/hooks/{chunk-hg43zexg.js → chunk-1c43349s.js} +65 -51
- package/dist/hooks/{chunk-x3zy7zrn.js → chunk-6c84kjd7.js} +1 -1
- package/dist/hooks/{chunk-2y3kwfrx.js → chunk-6ws778wq.js} +1 -1
- package/dist/hooks/{chunk-fv66tdsm.js → chunk-9v45j71e.js} +23 -8
- package/dist/hooks/chunk-bk7rkdzw.js +103 -0
- package/dist/hooks/{chunk-4w03q2kj.js → chunk-d20x2aew.js} +27 -19
- package/dist/hooks/{chunk-rk1nm39z.js → chunk-ecwes7kd.js} +1 -1
- package/dist/hooks/{chunk-f6484ykg.js → chunk-kqvbzyc0.js} +24 -10
- package/dist/hooks/{chunk-y3cxqvnx.js → chunk-r1efatxk.js} +411 -10
- package/dist/hooks/{chunk-cf4az0j5.js → chunk-stw0dxna.js} +1 -1
- package/dist/hooks/{chunk-8awygmqw.js → chunk-tg7ppsxr.js} +4 -1
- package/dist/hooks/{chunk-vyvjbbmb.js → chunk-wzzywf6c.js} +32 -2
- package/dist/hooks/claim-sources.js +5 -5
- package/dist/hooks/dod-gate.js +8 -99
- package/dist/hooks/no-reask.js +8 -9
- package/dist/hooks/session-start.js +38 -123
- package/dist/hooks/statusline.js +6 -9
- package/dist/tldrx.js +3503 -1648
- package/package.json +1 -1
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/stages/build/stage.yml +14 -0
- package/stages/how/stage.yml +6 -0
- package/stages/plan/stage.yml +6 -0
- package/stages/watch/stage.yml +6 -0
- package/stages/what/stage.yml +6 -0
- package/templates/experts/stack/dotnet.md +4 -3
- package/templates/experts/stack/javascript.md +4 -3
- package/templates/experts/stack/python.md +4 -3
- package/templates/experts/stack/typescript.md +4 -3
- package/templates/workspace.yml +6 -0
- package/workflows/bugfix.yml +7 -0
- package/workflows/docs.yml +7 -0
- package/workflows/feature.yml +7 -0
- package/workflows/hotfix.yml +7 -0
- package/workflows/integration.yml +7 -0
- package/workflows/migration.yml +7 -0
- package/workflows/performance.yml +7 -0
- package/workflows/prototype.yml +7 -0
- package/workflows/refactor.yml +7 -0
- package/workflows/retro.yml +7 -0
- package/workflows/security-patch.yml +7 -0
- package/workflows/spike.yml +7 -0
- package/workflows/upgrade.yml +7 -0
- package/dist/hooks/chunk-39zh2e44.js +0 -97
- package/dist/hooks/chunk-5w5eg00k.js +0 -136
- package/dist/hooks/chunk-rrkdfk7s.js +0 -30
|
@@ -1,37 +1,29 @@
|
|
|
1
|
-
import {
|
|
2
|
-
EVIDENCE_FILE,
|
|
3
|
-
gateEvidenceRelPath
|
|
4
|
-
} from "./chunk-fv66tdsm.js";
|
|
5
1
|
import {
|
|
6
2
|
MAX_PLAN_STORIES,
|
|
7
3
|
MAX_STORIES_PER_WAVE,
|
|
8
4
|
MAX_WAVES,
|
|
5
|
+
PROJECT_FRAMEWORK_DIR,
|
|
6
|
+
SRC_PATTERNS,
|
|
9
7
|
STORY_ID_RE,
|
|
10
8
|
WAVE_ID_RE,
|
|
11
|
-
requirePattern,
|
|
12
|
-
requireStringList,
|
|
13
|
-
requireVersion1
|
|
14
|
-
} from "./chunk-5w5eg00k.js";
|
|
15
|
-
import {
|
|
16
9
|
asDocument,
|
|
10
|
+
classifySrc,
|
|
17
11
|
isRecord,
|
|
12
|
+
listRunDirs,
|
|
13
|
+
parseYaml,
|
|
14
|
+
readableSource,
|
|
18
15
|
requireArray,
|
|
19
16
|
requireEnum,
|
|
20
17
|
requireKeys,
|
|
21
18
|
requireNumber,
|
|
19
|
+
requirePattern,
|
|
22
20
|
requireRecord,
|
|
23
21
|
requireString,
|
|
22
|
+
requireStringList,
|
|
24
23
|
requireVersion,
|
|
24
|
+
requireVersion1,
|
|
25
25
|
result
|
|
26
|
-
} from "./chunk-
|
|
27
|
-
import {
|
|
28
|
-
PROJECT_FRAMEWORK_DIR,
|
|
29
|
-
SRC_PATTERNS,
|
|
30
|
-
classifySrc,
|
|
31
|
-
listRunDirs,
|
|
32
|
-
parseYaml,
|
|
33
|
-
readableSource
|
|
34
|
-
} from "./chunk-y3cxqvnx.js";
|
|
26
|
+
} from "./chunk-r1efatxk.js";
|
|
35
27
|
|
|
36
28
|
// src/hooks/lib/runFile.ts
|
|
37
29
|
import { existsSync, readFileSync } from "node:fs";
|
|
@@ -188,6 +180,8 @@ function nowRfc3339() {
|
|
|
188
180
|
var ON_EXCEED = ["block", "warn"];
|
|
189
181
|
var ON_HOST_TOKENS_EXCEED = ["warn", "block"];
|
|
190
182
|
var DEFAULT_ON_HOST_TOKENS_EXCEED = "warn";
|
|
183
|
+
var ON_GRANT_EXCEED = ["warn", "block"];
|
|
184
|
+
var DEFAULT_ON_GRANT_EXCEED = "warn";
|
|
191
185
|
var ECONOMIES = ["metered-usd", "host-tokens"];
|
|
192
186
|
var DEFAULT_ECONOMY = "metered-usd";
|
|
193
187
|
var DEFAULT_WARN_AT_PCT = 80;
|
|
@@ -205,6 +199,16 @@ function economyFor(budget, phaseId) {
|
|
|
205
199
|
function isHostTokens(budget, phaseId) {
|
|
206
200
|
return economyFor(budget, phaseId) === "host-tokens";
|
|
207
201
|
}
|
|
202
|
+
function requirePositiveGrant(value, path, issues) {
|
|
203
|
+
if (typeof value !== "number")
|
|
204
|
+
return;
|
|
205
|
+
if (value > 0)
|
|
206
|
+
return;
|
|
207
|
+
issues.push({
|
|
208
|
+
path,
|
|
209
|
+
message: "expected a grant greater than 0 — absence, not 0, means no grant was recorded"
|
|
210
|
+
});
|
|
211
|
+
}
|
|
208
212
|
function validateRunBudget(input) {
|
|
209
213
|
const issues = [];
|
|
210
214
|
const deprecations = [];
|
|
@@ -226,6 +230,19 @@ function validateRunBudget(input) {
|
|
|
226
230
|
if (doc.ceiling_host_tokens !== undefined && doc.ceiling_host_tokens !== null) {
|
|
227
231
|
requireNumber(doc.ceiling_host_tokens, "ceiling_host_tokens", issues);
|
|
228
232
|
}
|
|
233
|
+
if (doc.authorized_usd !== undefined && doc.authorized_usd !== null) {
|
|
234
|
+
requireNumber(doc.authorized_usd, "authorized_usd", issues);
|
|
235
|
+
requirePositiveGrant(doc.authorized_usd, "authorized_usd", issues);
|
|
236
|
+
}
|
|
237
|
+
if (doc.authorized_by !== undefined && doc.authorized_by !== null) {
|
|
238
|
+
requireString(doc.authorized_by, "authorized_by", issues);
|
|
239
|
+
}
|
|
240
|
+
if (doc.authorized_at !== undefined && doc.authorized_at !== null) {
|
|
241
|
+
requireString(doc.authorized_at, "authorized_at", issues);
|
|
242
|
+
}
|
|
243
|
+
if (doc.on_grant_exceed !== undefined && doc.on_grant_exceed !== null) {
|
|
244
|
+
requireEnum(doc.on_grant_exceed, ON_GRANT_EXCEED, "on_grant_exceed", issues);
|
|
245
|
+
}
|
|
229
246
|
if (doc.warn_at_pct !== undefined) {
|
|
230
247
|
requireNumber(doc.warn_at_pct, "warn_at_pct", issues);
|
|
231
248
|
const pct = doc.warn_at_pct;
|
|
@@ -258,6 +275,10 @@ function validateRunBudget(input) {
|
|
|
258
275
|
if (phase.ceiling_host_tokens !== undefined && phase.ceiling_host_tokens !== null) {
|
|
259
276
|
requireNumber(phase.ceiling_host_tokens, `${path}.ceiling_host_tokens`, issues);
|
|
260
277
|
}
|
|
278
|
+
if (phase.authorized_usd !== undefined && phase.authorized_usd !== null) {
|
|
279
|
+
requireNumber(phase.authorized_usd, `${path}.authorized_usd`, issues);
|
|
280
|
+
requirePositiveGrant(phase.authorized_usd, `${path}.authorized_usd`, issues);
|
|
281
|
+
}
|
|
261
282
|
const economy = ECONOMIES.includes(phase.economy) ? phase.economy : runEconomy;
|
|
262
283
|
if (economy === "host-tokens") {
|
|
263
284
|
const tokens = typeof phase.ceiling_host_tokens === "number" ? phase.ceiling_host_tokens : typeof phase.ceiling_usd === "number" ? phase.ceiling_usd : 0;
|
|
@@ -290,12 +311,17 @@ function asRunBudget(input) {
|
|
|
290
311
|
economy: doc.economy ?? DEFAULT_ECONOMY,
|
|
291
312
|
on_host_tokens_exceed: doc.on_host_tokens_exceed ?? DEFAULT_ON_HOST_TOKENS_EXCEED,
|
|
292
313
|
ceiling_host_tokens: doc.ceiling_host_tokens ?? null,
|
|
314
|
+
authorized_usd: doc.authorized_usd ?? null,
|
|
315
|
+
authorized_by: doc.authorized_by ?? null,
|
|
316
|
+
authorized_at: doc.authorized_at ?? null,
|
|
317
|
+
on_grant_exceed: doc.on_grant_exceed ?? DEFAULT_ON_GRANT_EXCEED,
|
|
293
318
|
phases: (doc.phases ?? []).map((phase) => ({
|
|
294
319
|
id: phase.id,
|
|
295
320
|
ceiling_usd: phase.ceiling_usd,
|
|
296
321
|
spent_usd: phase.spent_usd,
|
|
297
322
|
economy: phase.economy ?? null,
|
|
298
|
-
ceiling_host_tokens: phase.ceiling_host_tokens ?? null
|
|
323
|
+
ceiling_host_tokens: phase.ceiling_host_tokens ?? null,
|
|
324
|
+
authorized_usd: phase.authorized_usd ?? null
|
|
299
325
|
}))
|
|
300
326
|
};
|
|
301
327
|
}
|
|
@@ -312,11 +338,11 @@ function hostTokenCeiling(budget, phaseId) {
|
|
|
312
338
|
|
|
313
339
|
// src/core/budget/remainingWork.ts
|
|
314
340
|
import { existsSync as existsSync7, readFileSync as readFileSync7 } from "node:fs";
|
|
315
|
-
import { join as
|
|
341
|
+
import { join as join7 } from "node:path";
|
|
316
342
|
|
|
317
343
|
// src/core/run/buildProgress.ts
|
|
318
344
|
import { existsSync as existsSync6, readFileSync as readFileSync6 } from "node:fs";
|
|
319
|
-
import { join as
|
|
345
|
+
import { join as join6 } from "node:path";
|
|
320
346
|
|
|
321
347
|
// src/core/schemas/waves.ts
|
|
322
348
|
function validateWaves(input) {
|
|
@@ -424,25 +450,13 @@ var SKIPPED_DIRS = new Set([
|
|
|
424
450
|
"test-results"
|
|
425
451
|
]);
|
|
426
452
|
|
|
427
|
-
// src/core/facilitator/paths.ts
|
|
428
|
-
import { join as join2, relative, sep } from "node:path";
|
|
429
|
-
function agentDir(runDir, stageId) {
|
|
430
|
-
return join2(runDir, ".agent", stageId);
|
|
431
|
-
}
|
|
432
|
-
function evidencePath(runDir, stageId) {
|
|
433
|
-
return join2(agentDir(runDir, stageId), EVIDENCE_FILE);
|
|
434
|
-
}
|
|
435
|
-
function gateEvidencePath(runDir, phaseId, stageId) {
|
|
436
|
-
return join2(runDir, ...gateEvidenceRelPath(phaseId, stageId).split("/"));
|
|
437
|
-
}
|
|
438
|
-
|
|
439
453
|
// src/core/experts/expertDocument.ts
|
|
440
454
|
import { existsSync as existsSync3, readFileSync as readFileSync3 } from "node:fs";
|
|
441
|
-
import { join as
|
|
455
|
+
import { join as join3 } from "node:path";
|
|
442
456
|
|
|
443
457
|
// src/core/experts/loadExperts.ts
|
|
444
458
|
import { existsSync as existsSync2, readdirSync, readFileSync as readFileSync2, statSync } from "node:fs";
|
|
445
|
-
import { join as
|
|
459
|
+
import { join as join2 } from "node:path";
|
|
446
460
|
|
|
447
461
|
// src/core/init/competencyLevel.ts
|
|
448
462
|
var EVIDENCE_KINDS = ["code", "run", "test", "doc", "answer"];
|
|
@@ -574,21 +588,21 @@ var EXPERTS_DIRNAME = "experts";
|
|
|
574
588
|
var COMPETENCIES_FILE = "competencies.yml";
|
|
575
589
|
var EXPERT_FILE = "expert.md";
|
|
576
590
|
function expertsDir(root) {
|
|
577
|
-
return
|
|
591
|
+
return join2(root, PROJECT_FRAMEWORK_DIR, EXPERTS_DIRNAME);
|
|
578
592
|
}
|
|
579
593
|
function expertDir(root, name) {
|
|
580
|
-
return
|
|
594
|
+
return join2(expertsDir(root), name);
|
|
581
595
|
}
|
|
582
596
|
function loadExperts(root, now = new Date) {
|
|
583
597
|
const dir = expertsDir(root);
|
|
584
598
|
if (!existsSync2(dir))
|
|
585
599
|
return [];
|
|
586
|
-
const names = readdirSync(dir).filter((entry) => statSync(
|
|
600
|
+
const names = readdirSync(dir).filter((entry) => statSync(join2(dir, entry)).isDirectory()).sort();
|
|
587
601
|
return names.map((name) => loadExpert(root, name, now));
|
|
588
602
|
}
|
|
589
603
|
function loadExpert(root, name, now = new Date) {
|
|
590
604
|
const dir = expertDir(root, name);
|
|
591
|
-
const path =
|
|
605
|
+
const path = join2(dir, COMPETENCIES_FILE);
|
|
592
606
|
const empty = { name, dir, status: "unknown", lastTrained: null, areas: [], drifted: [] };
|
|
593
607
|
if (!existsSync2(path))
|
|
594
608
|
return { ...empty, error: `${COMPETENCIES_FILE} is missing` };
|
|
@@ -647,7 +661,7 @@ function str3(value) {
|
|
|
647
661
|
// src/core/experts/expertDocument.ts
|
|
648
662
|
var H2_RE = /^##\s+(.+?)\s*$/;
|
|
649
663
|
function readExpertDocument(root, name) {
|
|
650
|
-
const path =
|
|
664
|
+
const path = join3(expertDir(root, name), EXPERT_FILE);
|
|
651
665
|
if (!existsSync3(path)) {
|
|
652
666
|
return { path, exists: false, frontMatter: new Map, body: "" };
|
|
653
667
|
}
|
|
@@ -754,9 +768,9 @@ var DEFAULT_KNOWLEDGE_MAX_BYTES = 48 * 1024;
|
|
|
754
768
|
|
|
755
769
|
// src/core/experts/workspaceRepos.ts
|
|
756
770
|
import { existsSync as existsSync4, readFileSync as readFileSync4 } from "node:fs";
|
|
757
|
-
import { join as
|
|
771
|
+
import { join as join4 } from "node:path";
|
|
758
772
|
function readWorkspaceDocument(root) {
|
|
759
|
-
const path =
|
|
773
|
+
const path = join4(root, PROJECT_FRAMEWORK_DIR, "workspace.yml");
|
|
760
774
|
if (!existsSync4(path))
|
|
761
775
|
return null;
|
|
762
776
|
try {
|
|
@@ -827,7 +841,7 @@ var MAX_TOUCHED_BYTES = 64 * 1024;
|
|
|
827
841
|
|
|
828
842
|
// src/core/build/plan.ts
|
|
829
843
|
import { existsSync as existsSync5, readFileSync as readFileSync5 } from "node:fs";
|
|
830
|
-
import { join as
|
|
844
|
+
import { join as join5 } from "node:path";
|
|
831
845
|
|
|
832
846
|
// src/core/schemas/budget.ts
|
|
833
847
|
function validateBudget(input) {
|
|
@@ -858,7 +872,7 @@ var PLAN_PHASE = "03-plan";
|
|
|
858
872
|
var PLAN_BUDGET_FILE = "budget.yml";
|
|
859
873
|
function loadPlanPrices(planDir, stories) {
|
|
860
874
|
const empty = new Map;
|
|
861
|
-
const path =
|
|
875
|
+
const path = join5(planDir, PLAN_BUDGET_FILE);
|
|
862
876
|
if (!existsSync5(path))
|
|
863
877
|
return { prices: empty, issue: null };
|
|
864
878
|
let doc;
|
|
@@ -928,8 +942,8 @@ var PLAN_DIR = "03-plan";
|
|
|
928
942
|
var BUILD_PHASE2 = "04-build";
|
|
929
943
|
var STATUS_RE = /^status\s*:\s*(\w+)\s*$/m;
|
|
930
944
|
function buildProgress(runDir) {
|
|
931
|
-
const wavesPath =
|
|
932
|
-
const implicitPath =
|
|
945
|
+
const wavesPath = join6(runDir, PLAN_DIR, "waves.yml");
|
|
946
|
+
const implicitPath = join6(runDir, BUILD_PHASE2, IMPLICIT_PLAN_FILE);
|
|
933
947
|
const implicit = !existsSync6(wavesPath) && existsSync6(implicitPath);
|
|
934
948
|
const path = implicit ? implicitPath : wavesPath;
|
|
935
949
|
if (!existsSync6(path))
|
|
@@ -970,7 +984,7 @@ function implicitStatus(path) {
|
|
|
970
984
|
}
|
|
971
985
|
}
|
|
972
986
|
function statusOf(runDir, id) {
|
|
973
|
-
const path =
|
|
987
|
+
const path = join6(runDir, PLAN_DIR, "stories", `${id}.md`);
|
|
974
988
|
if (!existsSync6(path))
|
|
975
989
|
return "missing";
|
|
976
990
|
try {
|
|
@@ -981,7 +995,7 @@ function statusOf(runDir, id) {
|
|
|
981
995
|
}
|
|
982
996
|
function costByStory(runDir) {
|
|
983
997
|
const costs = new Map;
|
|
984
|
-
const path =
|
|
998
|
+
const path = join6(runDir, "events.jsonl");
|
|
985
999
|
if (!existsSync6(path))
|
|
986
1000
|
return costs;
|
|
987
1001
|
let text;
|
|
@@ -1081,7 +1095,7 @@ function measure(input) {
|
|
|
1081
1095
|
if (all.length === 0)
|
|
1082
1096
|
return fallback;
|
|
1083
1097
|
const ids = new Set(all.map((story) => story.id));
|
|
1084
|
-
const prices = progress.implicit ? new Map : loadPlanPrices(
|
|
1098
|
+
const prices = progress.implicit ? new Map : loadPlanPrices(join7(input.runDir, PLAN_DIR), ids).prices;
|
|
1085
1099
|
const verdicts = reviewVerdictsByStory(input.runDir);
|
|
1086
1100
|
const caps = new CapMath(input, prices, all.length);
|
|
1087
1101
|
const stories = [];
|
|
@@ -1186,7 +1200,7 @@ class CapMath {
|
|
|
1186
1200
|
}
|
|
1187
1201
|
function reviewVerdictsByStory(runDir) {
|
|
1188
1202
|
const counts = new Map;
|
|
1189
|
-
const path =
|
|
1203
|
+
const path = join7(runDir, "events.jsonl");
|
|
1190
1204
|
if (!existsSync7(path))
|
|
1191
1205
|
return counts;
|
|
1192
1206
|
let text;
|
|
@@ -1283,4 +1297,4 @@ function shortBy(estimate, remaining) {
|
|
|
1283
1297
|
return Math.max(0.01, Math.ceil((estimate - remaining) * 100) / 100);
|
|
1284
1298
|
}
|
|
1285
1299
|
|
|
1286
|
-
export { loadRunView, isAttendedByHostView, runSpend, hostTokensIn, renderRunEconomies, cursorStage, openRunViews, newestActiveRun, currentActor, nowRfc3339, DEFAULT_ON_HOST_TOKENS_EXCEED, DEFAULT_ECONOMY, economyFor, isHostTokens, validateRunBudget, asRunBudget,
|
|
1300
|
+
export { loadRunView, isAttendedByHostView, runSpend, hostTokensIn, renderRunEconomies, cursorStage, openRunViews, newestActiveRun, currentActor, nowRfc3339, DEFAULT_ON_HOST_TOKENS_EXCEED, DEFAULT_ON_GRANT_EXCEED, DEFAULT_ECONOMY, economyFor, isHostTokens, validateRunBudget, asRunBudget, expertsDir, loadExperts, readExpertDomain, pathsIntersect, stackExpertNames, remainingWork, wouldExceed, wouldExceedHostTokens, raiseCommand, shortBy };
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
parseFrontMatter
|
|
3
|
-
} from "./chunk-5w5eg00k.js";
|
|
4
1
|
import {
|
|
5
2
|
asDocument,
|
|
6
3
|
isRecord,
|
|
4
|
+
parseFrontMatter,
|
|
5
|
+
parseHandoff,
|
|
7
6
|
requireArray,
|
|
8
7
|
requireEnum,
|
|
9
8
|
requireKeys,
|
|
@@ -11,10 +10,7 @@ import {
|
|
|
11
10
|
requireString,
|
|
12
11
|
requireVersion,
|
|
13
12
|
result
|
|
14
|
-
} from "./chunk-
|
|
15
|
-
import {
|
|
16
|
-
parseHandoff
|
|
17
|
-
} from "./chunk-y3cxqvnx.js";
|
|
13
|
+
} from "./chunk-r1efatxk.js";
|
|
18
14
|
|
|
19
15
|
// src/core/events/EventLog.ts
|
|
20
16
|
import { appendFileSync, existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
@@ -48,6 +44,7 @@ var EVENT_TYPES = [
|
|
|
48
44
|
"story.base_fastforwarded",
|
|
49
45
|
"story.review_retried",
|
|
50
46
|
"story.work_rescued",
|
|
47
|
+
"story.touches_widened",
|
|
51
48
|
"result.unreadable",
|
|
52
49
|
"operator_note",
|
|
53
50
|
"check.passed",
|
|
@@ -55,9 +52,11 @@ var EVENT_TYPES = [
|
|
|
55
52
|
"budget.warned",
|
|
56
53
|
"budget.blocked",
|
|
57
54
|
"budget.raised",
|
|
55
|
+
"budget.granted",
|
|
58
56
|
"fact.added",
|
|
59
57
|
"fact.retired",
|
|
60
58
|
"fact.superseded",
|
|
59
|
+
"fact.conflict_raised",
|
|
61
60
|
"doc.superseded",
|
|
62
61
|
"map.refreshed",
|
|
63
62
|
"ticket.synced",
|
|
@@ -472,6 +471,7 @@ var MAX_TASKS = 200;
|
|
|
472
471
|
var GATE_EXECUTOR_TYPES = ["human", "agent", "auto"];
|
|
473
472
|
var GATE_AUTHORITY_TYPES = ["direct", "delegated"];
|
|
474
473
|
var GATE_AUTHORITY_SOURCES = ["self", "run.created", "gate.policy_changed", "unrecorded"];
|
|
474
|
+
var TRIAGE_BUDGET_BASES = ["model-guess", "owner-grant", "preset"];
|
|
475
475
|
function isTerminal(status) {
|
|
476
476
|
return TERMINAL_STATUSES.includes(status);
|
|
477
477
|
}
|
|
@@ -585,6 +585,9 @@ function validateRunFile(input) {
|
|
|
585
585
|
requireString(slug, `triage.depends_on[${i}]`, issues);
|
|
586
586
|
});
|
|
587
587
|
}
|
|
588
|
+
if (doc.triage.budget_basis !== undefined) {
|
|
589
|
+
requireEnum(doc.triage.budget_basis, TRIAGE_BUDGET_BASES, "triage.budget_basis", issues);
|
|
590
|
+
}
|
|
588
591
|
} else {
|
|
589
592
|
issues.push({ path: "triage", message: "expected a mapping" });
|
|
590
593
|
}
|
|
@@ -815,4 +818,16 @@ function validateGateEvidence(value, base, issues) {
|
|
|
815
818
|
}
|
|
816
819
|
}
|
|
817
820
|
|
|
818
|
-
|
|
821
|
+
// src/core/facilitator/paths.ts
|
|
822
|
+
import { join as join2, relative, sep } from "node:path";
|
|
823
|
+
function agentDir(runDir, stageId) {
|
|
824
|
+
return join2(runDir, ".agent", stageId);
|
|
825
|
+
}
|
|
826
|
+
function evidencePath(runDir, stageId) {
|
|
827
|
+
return join2(agentDir(runDir, stageId), EVIDENCE_FILE);
|
|
828
|
+
}
|
|
829
|
+
function gateEvidencePath(runDir, phaseId, stageId) {
|
|
830
|
+
return join2(runDir, ...gateEvidenceRelPath(phaseId, stageId).split("/"));
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
export { EventLog, parseEvidence, PHASE_ID_RE, isTerminal, isFinished, isAttendedByHost, flatten, stageAt, deriveRunStatus, derivePhaseStatus, validateRunFile, asRunFile, evidencePath, gateEvidencePath };
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import {
|
|
2
|
+
findDuplicate
|
|
3
|
+
} from "./chunk-kqvbzyc0.js";
|
|
4
|
+
import {
|
|
5
|
+
evidencePath,
|
|
6
|
+
gateEvidencePath,
|
|
7
|
+
parseEvidence
|
|
8
|
+
} from "./chunk-9v45j71e.js";
|
|
9
|
+
import {
|
|
10
|
+
openBlocks,
|
|
11
|
+
parseQuestions
|
|
12
|
+
} from "./chunk-wzzywf6c.js";
|
|
13
|
+
|
|
14
|
+
// src/core/distill/distill.ts
|
|
15
|
+
var CONFLICT_THRESHOLD = 0.6;
|
|
16
|
+
function conflictOf(claim, facts) {
|
|
17
|
+
const hit = findDuplicate(claim.match, claim.area, facts, CONFLICT_THRESHOLD);
|
|
18
|
+
if (hit === null)
|
|
19
|
+
return null;
|
|
20
|
+
return hit.fact.fact.trim() === claim.text.trim() ? null : hit;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// src/core/run/decisionCards.ts
|
|
24
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
25
|
+
import { join } from "node:path";
|
|
26
|
+
function questionsCard(ctx) {
|
|
27
|
+
const blocks = openQuestionBlocks(join(ctx.runDir, ctx.phaseId, "questions.md"));
|
|
28
|
+
if (blocks.length === 0)
|
|
29
|
+
return null;
|
|
30
|
+
const recommendations = readRecommendations(ctx);
|
|
31
|
+
return {
|
|
32
|
+
kind: "questions",
|
|
33
|
+
run: ctx.runId,
|
|
34
|
+
gate: `${ctx.phaseId}/${ctx.stageId}`,
|
|
35
|
+
headline: "",
|
|
36
|
+
questions: blocks.map((block) => toQuestion(block, ctx.runId, recommendations)),
|
|
37
|
+
detail: [],
|
|
38
|
+
commands: []
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
function answerCommand(questionId, runId) {
|
|
42
|
+
return `tldrx answer ${questionId} "…" --run ${runId}`;
|
|
43
|
+
}
|
|
44
|
+
function toQuestion(block, runId, recommendations) {
|
|
45
|
+
const recommended = recommendations.get(block.id);
|
|
46
|
+
return {
|
|
47
|
+
id: block.id,
|
|
48
|
+
title: block.title,
|
|
49
|
+
whyAsked: block.whyAsked,
|
|
50
|
+
options: block.options.map(firstLine),
|
|
51
|
+
recommendation: recommended === undefined ? null : toRecommendation(recommended),
|
|
52
|
+
answerCommand: answerCommand(block.id, runId)
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function firstLine(option) {
|
|
56
|
+
return { letter: option.letter, text: option.text.split(`
|
|
57
|
+
`)[0] ?? "" };
|
|
58
|
+
}
|
|
59
|
+
function toRecommendation(entry) {
|
|
60
|
+
return { option: entry.option, why: entry.why, src: entry.src };
|
|
61
|
+
}
|
|
62
|
+
function openQuestionBlocks(path) {
|
|
63
|
+
if (!existsSync(path))
|
|
64
|
+
return [];
|
|
65
|
+
try {
|
|
66
|
+
return openBlocks(parseQuestions(readFileSync(path, "utf8")).blocks);
|
|
67
|
+
} catch {
|
|
68
|
+
return [];
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function readRecommendations(ctx) {
|
|
72
|
+
const out = new Map;
|
|
73
|
+
const paths = [
|
|
74
|
+
evidencePath(ctx.runDir, ctx.stageId),
|
|
75
|
+
gateEvidencePath(ctx.runDir, ctx.phaseId, ctx.stageId)
|
|
76
|
+
];
|
|
77
|
+
for (const path of paths) {
|
|
78
|
+
const text = readOrNull(path);
|
|
79
|
+
if (text === null)
|
|
80
|
+
continue;
|
|
81
|
+
const front = parseEvidence(text).front;
|
|
82
|
+
if (front === null)
|
|
83
|
+
continue;
|
|
84
|
+
for (const entry of front.recommend) {
|
|
85
|
+
if (!out.has(entry.q))
|
|
86
|
+
out.set(entry.q, entry);
|
|
87
|
+
}
|
|
88
|
+
if (out.size > 0)
|
|
89
|
+
return out;
|
|
90
|
+
}
|
|
91
|
+
return out;
|
|
92
|
+
}
|
|
93
|
+
function readOrNull(path) {
|
|
94
|
+
if (!existsSync(path))
|
|
95
|
+
return null;
|
|
96
|
+
try {
|
|
97
|
+
return readFileSync(path, "utf8");
|
|
98
|
+
} catch {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export { questionsCard, conflictOf };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
DEFAULT_ECONOMY,
|
|
3
|
+
DEFAULT_ON_GRANT_EXCEED,
|
|
3
4
|
DEFAULT_ON_HOST_TOKENS_EXCEED,
|
|
4
5
|
asRunBudget,
|
|
5
6
|
cursorStage,
|
|
@@ -7,7 +8,7 @@ import {
|
|
|
7
8
|
nowRfc3339,
|
|
8
9
|
openRunViews,
|
|
9
10
|
validateRunBudget
|
|
10
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-1c43349s.js";
|
|
11
12
|
import {
|
|
12
13
|
EventLog,
|
|
13
14
|
asRunFile,
|
|
@@ -19,7 +20,7 @@ import {
|
|
|
19
20
|
isTerminal,
|
|
20
21
|
stageAt,
|
|
21
22
|
validateRunFile
|
|
22
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-9v45j71e.js";
|
|
23
24
|
import {
|
|
24
25
|
backupPathFor,
|
|
25
26
|
isAlive,
|
|
@@ -28,17 +29,18 @@ import {
|
|
|
28
29
|
workspaceRootOfRunDir,
|
|
29
30
|
writeAtomic,
|
|
30
31
|
yamlScalar
|
|
31
|
-
} from "./chunk-
|
|
32
|
+
} from "./chunk-tg7ppsxr.js";
|
|
32
33
|
import {
|
|
34
|
+
isAdvisory,
|
|
33
35
|
noteDeprecations,
|
|
34
36
|
openBlocks,
|
|
35
37
|
parseQuestions
|
|
36
|
-
} from "./chunk-
|
|
38
|
+
} from "./chunk-wzzywf6c.js";
|
|
37
39
|
import {
|
|
38
40
|
listRunDirs,
|
|
39
41
|
parseYaml,
|
|
40
42
|
parseYamlRepairing
|
|
41
|
-
} from "./chunk-
|
|
43
|
+
} from "./chunk-r1efatxk.js";
|
|
42
44
|
|
|
43
45
|
// src/core/statusline/runSnapshot.ts
|
|
44
46
|
import { existsSync as existsSync3, readFileSync as readFileSync3 } from "node:fs";
|
|
@@ -136,7 +138,8 @@ function emitRunYaml(run) {
|
|
|
136
138
|
`budget: {ceiling_usd: ${money(run.budget.ceiling_usd)}, spent_usd: ${money(run.budget.spent_usd)}, ` + `per_agent_max_usd: ${money(run.budget.per_agent_max_usd)}}`
|
|
137
139
|
];
|
|
138
140
|
if (run.triage !== undefined) {
|
|
139
|
-
|
|
141
|
+
const basis = run.triage.budget_basis === undefined ? "" : `, budget_basis: ${yamlScalar(run.triage.budget_basis)}`;
|
|
142
|
+
lines.push(`triage: {split: ${yamlScalar(run.triage.split)}, depends_on: ${inlineList(run.triage.depends_on)}${basis}}`);
|
|
140
143
|
}
|
|
141
144
|
if (run.build !== undefined && run.build.epic_branch.length > 0) {
|
|
142
145
|
const model = run.build.branch_model === undefined ? "" : `, branch_model: ${yamlScalar(run.build.branch_model)}`;
|
|
@@ -183,12 +186,17 @@ function emitBudgetYaml(budget) {
|
|
|
183
186
|
...budget.economy === DEFAULT_ECONOMY ? [] : [`economy: ${yamlScalar(budget.economy)}`],
|
|
184
187
|
...budget.on_host_tokens_exceed === DEFAULT_ON_HOST_TOKENS_EXCEED ? [] : [`on_host_tokens_exceed: ${yamlScalar(budget.on_host_tokens_exceed)}`],
|
|
185
188
|
...budget.ceiling_host_tokens === null ? [] : [`ceiling_host_tokens: ${tokens(budget.ceiling_host_tokens)}`],
|
|
189
|
+
...budget.authorized_usd === null ? [] : [`authorized_usd: ${money(budget.authorized_usd)}`],
|
|
190
|
+
...budget.authorized_by === null ? [] : [`authorized_by: ${yamlScalar(budget.authorized_by)}`],
|
|
191
|
+
...budget.authorized_at === null ? [] : [`authorized_at: ${yamlScalar(budget.authorized_at)}`],
|
|
192
|
+
...budget.on_grant_exceed === DEFAULT_ON_GRANT_EXCEED ? [] : [`on_grant_exceed: ${yamlScalar(budget.on_grant_exceed)}`],
|
|
186
193
|
"phases:"
|
|
187
194
|
];
|
|
188
195
|
for (const phase of budget.phases) {
|
|
189
196
|
const economy = phase.economy === null ? "" : `, economy: ${yamlScalar(phase.economy)}`;
|
|
190
197
|
const hostTokens = phase.ceiling_host_tokens === null ? "" : `, ceiling_host_tokens: ${tokens(phase.ceiling_host_tokens)}`;
|
|
191
|
-
|
|
198
|
+
const grant = phase.authorized_usd === null ? "" : `, authorized_usd: ${money(phase.authorized_usd)}`;
|
|
199
|
+
lines.push(` - {id: ${yamlScalar(phase.id)}, ceiling_usd: ${money(phase.ceiling_usd)}, ` + `spent_usd: ${money(phase.spent_usd)}${economy}${hostTokens}${grant}}`);
|
|
192
200
|
}
|
|
193
201
|
return `${lines.join(`
|
|
194
202
|
`)}
|
|
@@ -357,7 +365,8 @@ class RunStore {
|
|
|
357
365
|
...mine,
|
|
358
366
|
ceiling_usd: theirs.ceiling_usd,
|
|
359
367
|
ceiling_host_tokens: theirs.ceiling_host_tokens,
|
|
360
|
-
economy: theirs.economy
|
|
368
|
+
economy: theirs.economy,
|
|
369
|
+
authorized_usd: theirs.authorized_usd
|
|
361
370
|
};
|
|
362
371
|
})
|
|
363
372
|
};
|
|
@@ -464,6 +473,15 @@ function openQuestionIds(path) {
|
|
|
464
473
|
return [];
|
|
465
474
|
}
|
|
466
475
|
}
|
|
476
|
+
function blockingQuestionIds(path) {
|
|
477
|
+
if (!existsSync2(path))
|
|
478
|
+
return [];
|
|
479
|
+
try {
|
|
480
|
+
return openBlocks(parseQuestions(readFileSync2(path, "utf8")).blocks).filter((block) => !isAdvisory(block)).map((block) => block.id);
|
|
481
|
+
} catch {
|
|
482
|
+
return [];
|
|
483
|
+
}
|
|
484
|
+
}
|
|
467
485
|
|
|
468
486
|
// src/core/statusline/runSnapshot.ts
|
|
469
487
|
function runSnapshot(root) {
|
|
@@ -559,7 +577,6 @@ function numberAfter(text, key) {
|
|
|
559
577
|
}
|
|
560
578
|
|
|
561
579
|
// src/core/run/waiting.ts
|
|
562
|
-
import { existsSync as existsSync5, readFileSync as readFileSync4 } from "node:fs";
|
|
563
580
|
import { basename as basename2, join as join4 } from "node:path";
|
|
564
581
|
|
|
565
582
|
// src/core/run/prepared.ts
|
|
@@ -634,7 +651,7 @@ function waitingFor(run, runDir) {
|
|
|
634
651
|
questions: []
|
|
635
652
|
};
|
|
636
653
|
}
|
|
637
|
-
const open =
|
|
654
|
+
const open = blockingQuestionIds(join4(runDir, cursor.phase, "questions.md"));
|
|
638
655
|
switch (entry.stage.status) {
|
|
639
656
|
case "awaiting_gate":
|
|
640
657
|
return {
|
|
@@ -698,15 +715,6 @@ function cancelledMessage(cancelled) {
|
|
|
698
715
|
const note = cancelled.note.trim();
|
|
699
716
|
return `run cancelled by ${by}${when}${note === "" ? "" : ` — ${note}`}`;
|
|
700
717
|
}
|
|
701
|
-
function openQuestionIds2(path) {
|
|
702
|
-
if (!existsSync5(path))
|
|
703
|
-
return [];
|
|
704
|
-
try {
|
|
705
|
-
return openBlocks(parseQuestions(readFileSync4(path, "utf8")).blocks).map((b) => b.id);
|
|
706
|
-
} catch {
|
|
707
|
-
return [];
|
|
708
|
-
}
|
|
709
|
-
}
|
|
710
718
|
|
|
711
719
|
// src/core/run/runStatus.ts
|
|
712
720
|
var BAR_CELLS = 5;
|