@kienntpixon/dieu-phoi 0.7.0 → 0.7.2
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 +57 -0
- package/README.md +3 -0
- package/agents/dp-exec.md +2 -0
- package/agents/dp-helper.md +2 -0
- package/agents/dp-scout.md +2 -0
- package/agents/dp-verify.md +2 -0
- package/bin/dieu-phoi.cjs +14 -2
- package/hook.cjs +788 -34
- package/lib/aggregate.cjs +49 -6
- package/lib/args.cjs +1 -1
- package/lib/choose.cjs +41 -25
- package/lib/commands.cjs +212 -42
- package/lib/knowledge.cjs +77 -22
- package/lib/procs.cjs +223 -0
- package/lib/profile-scan.cjs +85 -0
- package/lib/profile-seed.cjs +147 -0
- package/lib/profile.cjs +141 -1
- package/lib/prune.cjs +56 -8
- package/lib/setup.cjs +47 -15
- package/lib/worktree.cjs +186 -0
- package/package.json +3 -1
- package/profile/lessons.md +83 -0
- package/profile/unity-lanes.md +316 -0
package/lib/aggregate.cjs
CHANGED
|
@@ -125,6 +125,7 @@ function buildAgents(events) {
|
|
|
125
125
|
const userVerdicts = [];
|
|
126
126
|
const autoVerdicts = [];
|
|
127
127
|
const overrides = [];
|
|
128
|
+
const implicitOverrides = []; // core 0.7.2 (K4): deviations the hook logged; not the user's overrides
|
|
128
129
|
const steers = [];
|
|
129
130
|
const get = (id) => {
|
|
130
131
|
if (!map.has(id)) map.set(id, { agent_id: id, dispatch: null, completes: [], verdicts: [], steers: [] });
|
|
@@ -135,7 +136,7 @@ function buildAgents(events) {
|
|
|
135
136
|
if (!ev || typeof ev !== 'object') return;
|
|
136
137
|
if (isInternalComplete(ev, dispatched)) return;
|
|
137
138
|
const e = Object.assign({ _idx: idx }, ev);
|
|
138
|
-
if (ev.kind === 'override') { overrides.push(e); return; }
|
|
139
|
+
if (ev.kind === 'override') { (ev.source === 'implicit' ? implicitOverrides : overrides).push(e); return; }
|
|
139
140
|
if (ev.kind === 'verdict') {
|
|
140
141
|
const v = Object.assign(e, normalizeVerdict(ev));
|
|
141
142
|
if (v.by === 'user') { userVerdicts.push(v); return; }
|
|
@@ -173,6 +174,8 @@ function buildAgents(events) {
|
|
|
173
174
|
task_id: pick('task_id') || (verdict && verdict.task_id) || (a.steers[0] && a.steers[0].task_id) || null,
|
|
174
175
|
try: Number(pick('try')) || 1,
|
|
175
176
|
role: pick('role') || 'exec',
|
|
177
|
+
// core 0.7.1 (C5): the one batched verifier of a fast request (tag `|batch`, dispatch `batch:true`)
|
|
178
|
+
batch: pick('batch') === true,
|
|
176
179
|
score: d.score !== undefined ? d.score : null,
|
|
177
180
|
urgency: d.urgency || null,
|
|
178
181
|
model: core.normalizeModel(cl.model) || core.normalizeModel(d.model) || null,
|
|
@@ -200,7 +203,7 @@ function buildAgents(events) {
|
|
|
200
203
|
last_complete_ts: a.completes.length ? Math.max(...a.completes.map(tsOf)) : 0,
|
|
201
204
|
});
|
|
202
205
|
}
|
|
203
|
-
return { agents, userVerdicts, autoVerdicts, overrides, steers };
|
|
206
|
+
return { agents, userVerdicts, autoVerdicts, overrides, implicitOverrides, steers };
|
|
204
207
|
}
|
|
205
208
|
|
|
206
209
|
/** Group agents into tasks (sample unit = task_id; agents without task_id are their own task). */
|
|
@@ -284,7 +287,9 @@ function buildTasks(agents, userVerdicts, autoVerdicts) {
|
|
|
284
287
|
const autoOverturned = autoAccept && user.some((v) => v.outcome === 'reject' && tsOf(v) > Math.min(...autoTs));
|
|
285
288
|
const lastUser = user[user.length - 1] || null;
|
|
286
289
|
const accepted = lastUser ? lastUser.outcome === 'accept' : attempts.some((a) => a.outcome === 'pass');
|
|
287
|
-
|
|
290
|
+
// core 0.7.1 (C5): the batched verifier checks every fast step of a request; it is billed to its own cell
|
|
291
|
+
// (batchVerifyCells), never to the task_type x tier of the fast step whose task_id it carries.
|
|
292
|
+
const costed = t.agents.filter((a) => (a.completed || a.outcome) && !isBatchVerify(a));
|
|
288
293
|
tasks.push({
|
|
289
294
|
key: t.key,
|
|
290
295
|
task_id: t.task_id,
|
|
@@ -315,6 +320,43 @@ function buildTasks(agents, userVerdicts, autoVerdicts) {
|
|
|
315
320
|
return tasks;
|
|
316
321
|
}
|
|
317
322
|
|
|
323
|
+
/** core 0.7.1 (C5): a role=verify agent dispatched with `batch:true`. */
|
|
324
|
+
function isBatchVerify(a) {
|
|
325
|
+
return !!a && a.role === 'verify' && a.batch === true;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* core 0.7.1 (C5), CLI only: batched verifiers grouped by their own model x effort. Agents with a complete or a
|
|
330
|
+
* verdict; cost/time per agent = sum over its complete segments (null when unknown, left out of the percentiles).
|
|
331
|
+
*/
|
|
332
|
+
function batchVerifyCells(agents) {
|
|
333
|
+
const groups = new Map();
|
|
334
|
+
for (const a of agents || []) {
|
|
335
|
+
if (!isBatchVerify(a) || !(a.completed || a.outcome)) continue;
|
|
336
|
+
const key = `${a.model || '?'}|${a.effort || '?'}`;
|
|
337
|
+
if (!groups.has(key)) groups.set(key, { model: a.model, effort: a.effort, agents: [] });
|
|
338
|
+
groups.get(key).agents.push(a);
|
|
339
|
+
}
|
|
340
|
+
const out = [];
|
|
341
|
+
for (const g of groups.values()) {
|
|
342
|
+
const costs = g.agents.map((a) => a.cost_usd);
|
|
343
|
+
const times = g.agents.map((a) => (a.duration_s === null ? null : a.duration_s / 60));
|
|
344
|
+
out.push({
|
|
345
|
+
model: g.model,
|
|
346
|
+
effort: g.effort,
|
|
347
|
+
n: g.agents.length,
|
|
348
|
+
tasks: [...new Set(g.agents.map((a) => a.task_id).filter(Boolean))].length,
|
|
349
|
+
cost_total_usd: round6(costs.reduce((s, c) => s + (typeof c === 'number' && isFinite(c) ? c : 0), 0)),
|
|
350
|
+
cost_mean_usd: round6(mean(costs)),
|
|
351
|
+
cost_p50_usd: round6(percentile(costs, 0.5)),
|
|
352
|
+
time_mean_min: round6(mean(times)),
|
|
353
|
+
time_p50_min: round6(percentile(times, 0.5)),
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
out.sort((a, b) => String(a.model).localeCompare(String(b.model)) || String(a.effort).localeCompare(String(b.effort)));
|
|
357
|
+
return out;
|
|
358
|
+
}
|
|
359
|
+
|
|
318
360
|
function cellKey(type, model, effort) {
|
|
319
361
|
return `${type}|${model || '?'}|${effort || '?'}`;
|
|
320
362
|
}
|
|
@@ -522,18 +564,19 @@ function orchestrationSummary(events) {
|
|
|
522
564
|
}
|
|
523
565
|
|
|
524
566
|
function analyze(events, opts) {
|
|
525
|
-
const { agents, userVerdicts, autoVerdicts, overrides, steers } = buildAgents(withoutDiscarded(events));
|
|
567
|
+
const { agents, userVerdicts, autoVerdicts, overrides, implicitOverrides, steers } = buildAgents(withoutDiscarded(events));
|
|
526
568
|
const tasks = buildTasks(agents, userVerdicts, autoVerdicts);
|
|
527
569
|
const cells = buildCells(tasks, opts);
|
|
528
570
|
return {
|
|
529
|
-
agents, tasks, cells, overrides, userVerdicts, autoVerdicts, steerEvents: steers, steers: buildSteers(tasks, steers, agents),
|
|
571
|
+
agents, tasks, cells, overrides, implicitOverrides, userVerdicts, autoVerdicts, steerEvents: steers, steers: buildSteers(tasks, steers, agents),
|
|
530
572
|
orchestration: orchestrationSummary(events),
|
|
573
|
+
batch_verify: batchVerifyCells(agents),
|
|
531
574
|
};
|
|
532
575
|
}
|
|
533
576
|
|
|
534
577
|
module.exports = {
|
|
535
578
|
MIN_N, FAIL_OUTCOMES, HEAVY_STEERS, ABORTED, QUOTA_REASON_RE, HANDOFF_REASON_RE, discardedTaskIds, withoutDiscarded, CANONICAL_CELL_FIELDS, normalizeVerdict, isSelf, isInternalComplete, buildAgents, buildTasks,
|
|
536
|
-
buildCells, buildSteers, canonicalCells, orchestrationSummary, analyze, cellKey, mean, percentile, tsOf, steerIdOf,
|
|
579
|
+
buildCells, buildSteers, canonicalCells, isBatchVerify, batchVerifyCells, orchestrationSummary, analyze, cellKey, mean, percentile, tsOf, steerIdOf,
|
|
537
580
|
};
|
|
538
581
|
|
|
539
582
|
// ------------------------------------------------------------------ core 0.7.0 (s2): stats split by mode
|
package/lib/args.cjs
CHANGED
|
@@ -4,7 +4,7 @@ const fs = require('fs');
|
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const core = require('../hook.cjs');
|
|
6
6
|
|
|
7
|
-
const BOOLEAN_FLAGS = new Set(['json', 'help', 'h', 'purge', 'version', 'v', 'dry-run', 'no-sync', 'approve', 'yes', 'force', 'follow', 'remove', 'dirty', 'no-cache', 'untagged', 'no-knowledge', 'net', 'with-codebase-memory', 'cbm', 'allow-custom-source', 'auto']);
|
|
7
|
+
const BOOLEAN_FLAGS = new Set(['json', 'help', 'h', 'purge', 'version', 'v', 'dry-run', 'no-sync', 'approve', 'yes', 'force', 'follow', 'remove', 'dirty', 'no-cache', 'untagged', 'no-knowledge', 'net', 'with-codebase-memory', 'cbm', 'allow-custom-source', 'auto', 'include-local']);
|
|
8
8
|
|
|
9
9
|
/** Minimal argv parser: positionals, --k v, --k=v, boolean flags. */
|
|
10
10
|
function parseArgs(argv) {
|
package/lib/choose.cjs
CHANGED
|
@@ -132,9 +132,19 @@ function loadProfile(skillDir) {
|
|
|
132
132
|
const v = Number(r.orch_ctx_soft_cap);
|
|
133
133
|
if (isFinite(v) && v > 0) softCap = Math.min(DEFAULT_ORCH_CTX_SOFT_CAP, Math.floor(v));
|
|
134
134
|
}
|
|
135
|
+
// core 0.7.1 (retro 2026-10-26, C4): per-type tier floor from the user's preference (e.g. art-gen -> opus/medium).
|
|
136
|
+
// Only tightens: choose takes max(floor, safety floors, escalation); an entry off the ladder is ignored.
|
|
137
|
+
const tierFloor = {};
|
|
138
|
+
if (r.tier_floor && typeof r.tier_floor === 'object' && !Array.isArray(r.tier_floor)) {
|
|
139
|
+
for (const [k, v] of Object.entries(r.tier_floor)) {
|
|
140
|
+
if (k.startsWith('_')) continue;
|
|
141
|
+
const idx = parsePrior(v);
|
|
142
|
+
if (idx !== null) tierFloor[String(k).toLowerCase()] = idx;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
135
145
|
return {
|
|
136
146
|
source, dept: r.dept || null, priors, lambda, explore_rate: explore, time_cap_min: timeCap, extra_task_types: extra,
|
|
137
|
-
orch_ctx_soft_cap: softCap,
|
|
147
|
+
orch_ctx_soft_cap: softCap, tier_floor: tierFloor,
|
|
138
148
|
};
|
|
139
149
|
}
|
|
140
150
|
|
|
@@ -302,20 +312,8 @@ function estimateFor(idx, cell, pricing) {
|
|
|
302
312
|
|
|
303
313
|
// ------------------------------------------------------------------ task type guard
|
|
304
314
|
|
|
305
|
-
// Ad-hoc labels
|
|
306
|
-
|
|
307
|
-
const TYPE_ALIASES = {
|
|
308
|
-
'bug-fix': 'debug', bugfix: 'debug', fix: 'debug', bug: 'debug',
|
|
309
|
-
config: 'mechanical-edit', shell: 'mechanical-edit', deploy: 'mechanical-edit', ops: 'mechanical-edit', edit: 'mechanical-edit',
|
|
310
|
-
docs: 'content-write', doc: 'content-write', documentation: 'content-write', writing: 'content-write', copy: 'content-write',
|
|
311
|
-
design: 'plan-design', plan: 'plan-design', planning: 'plan-design', architecture: 'plan-design',
|
|
312
|
-
research: 'research-synth', synth: 'research-synth',
|
|
313
|
-
test: 'test-write', tests: 'test-write',
|
|
314
|
-
analysis: 'data-analysis', data: 'data-analysis',
|
|
315
|
-
impl: 'feature-impl', feature: 'feature-impl', implement: 'feature-impl',
|
|
316
|
-
'code-review': 'review',
|
|
317
|
-
'art-fix': 'art-gen', art: 'art-gen',
|
|
318
|
-
};
|
|
315
|
+
// Ad-hoc labels → the closest fixed type: the table lives in hook.cjs (core 0.7.1) so dispatch tags map the same way.
|
|
316
|
+
const TYPE_ALIASES = core.TYPE_ALIASES;
|
|
319
317
|
|
|
320
318
|
/**
|
|
321
319
|
* { type, known, mapped_from, warning }: a fixed/extra type passes; an ad-hoc label with a known alias target
|
|
@@ -451,6 +449,11 @@ function choose(opts) {
|
|
|
451
449
|
if (!verifiable) { safetyFloor = Math.max(safetyFloor, OPUS_MEDIUM); floorWhy.push('không kiểm chứng bằng máy → tối thiểu opus/medium'); }
|
|
452
450
|
if (!reversible && role === 'exec') { safetyFloor = Math.max(safetyFloor, OPUS_MEDIUM); floorWhy.push('khó hoàn tác → tối thiểu opus/medium'); }
|
|
453
451
|
|
|
452
|
+
// Preference floor from the profile (tier_floor): never lowers a safety floor (max), never above the ceiling.
|
|
453
|
+
const prefFloor = known && profile.tier_floor && Number.isInteger(profile.tier_floor[type])
|
|
454
|
+
? Math.min(TOP, profile.tier_floor[type]) : 0;
|
|
455
|
+
const prefWhy = prefFloor > 0 ? `tier_floor của profile: ${type} tối thiểu ${tierName(prefFloor)}` : null;
|
|
456
|
+
|
|
454
457
|
const noExploreWhy = [];
|
|
455
458
|
if (score >= 9) noExploreWhy.push(`score ${score}≥9`);
|
|
456
459
|
if (!verifiable) noExploreWhy.push('không kiểm chứng bằng máy');
|
|
@@ -489,6 +492,7 @@ function choose(opts) {
|
|
|
489
492
|
tier: tierName(idx),
|
|
490
493
|
role,
|
|
491
494
|
task_type: type,
|
|
495
|
+
score,
|
|
492
496
|
mode,
|
|
493
497
|
verify_plan: vp ? vp.verify_plan : null,
|
|
494
498
|
verify_reason: vp ? vp.verify_reason : null,
|
|
@@ -501,7 +505,7 @@ function choose(opts) {
|
|
|
501
505
|
|
|
502
506
|
// Unknown task type (not fixed, not in the profile, no alias): its stats would never pool — ask first.
|
|
503
507
|
if (!known) {
|
|
504
|
-
return finish(Math.max(prior, safetyFloor), rt.warning, false, { action: 'ask_user', unknown_type: true });
|
|
508
|
+
return finish(Math.max(prior, safetyFloor), rt.warning, false, { action: 'ask_user', unknown_type: true, reason_code: 'unknown-type' });
|
|
505
509
|
}
|
|
506
510
|
|
|
507
511
|
if (overrideIdx !== null && overrideIdx < safetyFloor) {
|
|
@@ -509,34 +513,37 @@ function choose(opts) {
|
|
|
509
513
|
}
|
|
510
514
|
// Escalation beats override/approval: the user must decide first.
|
|
511
515
|
if (esc.ask) {
|
|
512
|
-
const userIdx = overrideIdx !== null ? overrideIdx : (approvedIdx !== null ? approvedIdx :
|
|
516
|
+
const userIdx = overrideIdx !== null ? overrideIdx : (approvedIdx !== null ? approvedIdx : prefFloor);
|
|
513
517
|
const idx = Math.max(userIdx, esc.floor, safetyFloor);
|
|
514
|
-
return finish(idx, esc.note, false, { action: 'ask_user', fails: esc.fails, tried: esc.tried });
|
|
518
|
+
return finish(idx, esc.note, false, { action: 'ask_user', fails: esc.fails, tried: esc.tried, reason_code: 'ask' });
|
|
515
519
|
}
|
|
516
520
|
|
|
517
521
|
// Tier approved by the user in the plan table: no Thompson/exploration; only floors can raise it.
|
|
518
522
|
if (approvedIdx !== null) {
|
|
519
523
|
const forced = Math.max(safetyFloor, esc.floor);
|
|
520
524
|
if (approvedIdx >= forced) {
|
|
521
|
-
|
|
522
|
-
|
|
525
|
+
let n = esc.note ? `; ${esc.note}` : '';
|
|
526
|
+
if (approvedIdx < prefFloor) n += `; dưới ${prefWhy} (người dùng duyệt nên giữ)`;
|
|
527
|
+
return finish(approvedIdx, `người dùng đã duyệt ${tierName(approvedIdx)} trong bảng phân công${n}`, false, { deviation: false, reason_code: 'plan' });
|
|
523
528
|
}
|
|
524
529
|
const why = [];
|
|
525
530
|
if (safetyFloor > approvedIdx) why.push(`sàn an toàn ${tierName(safetyFloor)} (${floorWhy.join(', ')})`);
|
|
526
531
|
if (esc.floor > approvedIdx) why.push(esc.note || `leo thang → ${tierName(esc.floor)}`);
|
|
527
|
-
return finish(forced, `lệch khỏi tier đã duyệt ${tierName(approvedIdx)} → ${tierName(forced)}: ${why.join('; ')}`, false, { deviation: true, approved_tier: tierName(approvedIdx) });
|
|
532
|
+
return finish(forced, `lệch khỏi tier đã duyệt ${tierName(approvedIdx)} → ${tierName(forced)}: ${why.join('; ')}`, false, { deviation: true, approved_tier: tierName(approvedIdx), reason_code: 'plan' });
|
|
528
533
|
}
|
|
529
534
|
if (esc.note && !esc.ask) notes.push(esc.note);
|
|
530
535
|
|
|
531
|
-
const floor = Math.max(safetyFloor, esc.ask ? 0 : esc.floor);
|
|
536
|
+
const floor = Math.max(safetyFloor, esc.ask ? 0 : esc.floor, prefFloor);
|
|
532
537
|
const base = Math.min(TOP, Math.max(prior, floor));
|
|
533
538
|
if (base > prior) {
|
|
534
539
|
const why = floorWhy.slice();
|
|
535
540
|
if (esc.floor > prior && esc.floor >= safetyFloor && !esc.ask) why.push(`leo thang → ${tierName(esc.floor)}`);
|
|
541
|
+
if (prefFloor > prior) why.push(prefWhy);
|
|
536
542
|
notes.unshift(`sàn ${tierName(base)} cao hơn ${priorWhy} ${tierName(prior)}: ${why.join(', ') || 'luật an toàn'}`);
|
|
537
543
|
} else if (floorWhy.length) {
|
|
538
544
|
notes.push(`sàn an toàn: ${floorWhy.join(', ')}`);
|
|
539
545
|
}
|
|
546
|
+
if (prefFloor > 0 && base <= prior) notes.push(prefWhy);
|
|
540
547
|
|
|
541
548
|
const minIdx = noExplore ? base : floor;
|
|
542
549
|
const candidates = [];
|
|
@@ -552,10 +559,15 @@ function choose(opts) {
|
|
|
552
559
|
|
|
553
560
|
let chosen = base;
|
|
554
561
|
let why;
|
|
562
|
+
// core 0.7.1 (C3): why this tier, as a code — thompson | floor | escalation | prior | user-prior | explore | plan | override | ask | unknown-type
|
|
563
|
+
let code;
|
|
564
|
+
let tsScore = null;
|
|
555
565
|
const baseCell = cellAt(base);
|
|
556
566
|
const scored = pool.filter((x) => x.cell && x.cell.enough);
|
|
557
567
|
if (!baseCell || !baseCell.enough || !scored.length) {
|
|
558
568
|
why = `${priorWhy} ${tierName(base)} (${baseCell ? `n=${baseCell.n}` : 'n=0'}, chưa đủ dữ liệu)`;
|
|
569
|
+
if (base > prior) code = esc.floor === base && esc.floor > Math.max(safetyFloor, prefFloor) ? 'escalation' : 'floor';
|
|
570
|
+
else code = priorWhy === 'prior' ? 'prior' : 'user-prior';
|
|
559
571
|
} else {
|
|
560
572
|
let best = null;
|
|
561
573
|
for (const x of scored) {
|
|
@@ -568,6 +580,8 @@ function choose(opts) {
|
|
|
568
580
|
if (!best || s < best.s) best = { i: x.i, s, p };
|
|
569
581
|
}
|
|
570
582
|
chosen = best.i;
|
|
583
|
+
code = 'thompson';
|
|
584
|
+
tsScore = Math.round(best.s * 1000) / 1000;
|
|
571
585
|
why = `Thompson: ${tierName(chosen)} điểm ${best.s.toFixed(3)} (p≈${best.p.toFixed(2)}, ${scored.length} ô đủ dữ liệu)`;
|
|
572
586
|
}
|
|
573
587
|
|
|
@@ -576,17 +590,19 @@ function choose(opts) {
|
|
|
576
590
|
if (!noExplore && chosen - 1 >= floor && u < profile.explore_rate) {
|
|
577
591
|
chosen -= 1;
|
|
578
592
|
explored = true;
|
|
593
|
+
code = 'explore';
|
|
579
594
|
why = `thăm dò xuống ${tierName(chosen)} (xác suất ${profile.explore_rate}); ${why}`;
|
|
580
595
|
}
|
|
581
596
|
|
|
582
597
|
if (overrideIdx !== null) {
|
|
583
598
|
const sugg = tierName(chosen);
|
|
584
|
-
|
|
585
|
-
|
|
599
|
+
const below = overrideIdx < prefFloor ? `; dưới ${prefWhy} (người dùng chọn nên giữ)` : '';
|
|
600
|
+
return finish(overrideIdx, `người dùng chọn ${tierName(overrideIdx)} (đề xuất là ${sugg}: ${why})${below}`, false, {
|
|
601
|
+
overridden: overrideIdx !== chosen, suggested_tier: sugg, reason_code: 'override', suggested_reason_code: code, thompson_score: tsScore,
|
|
586
602
|
});
|
|
587
603
|
}
|
|
588
604
|
if (noExplore) notes.push(`không thăm dò: ${noExploreWhy.join(', ')}`);
|
|
589
|
-
return finish(chosen, [why].concat(notes).join('; '), explored);
|
|
605
|
+
return finish(chosen, [why].concat(notes).join('; '), explored, { reason_code: code, thompson_score: tsScore });
|
|
590
606
|
}
|
|
591
607
|
|
|
592
608
|
module.exports = {
|