@goodandready/dsh-goal 0.2.31 → 0.2.33
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/lib/client.js +333 -1
- package/lib/engine-compaction.js +126 -0
- package/lib/engine-cost-meter.js +161 -0
- package/lib/engine-prompt.js +59 -5
- package/lib/engine-release-notes.js +132 -0
- package/lib/engine-subgoals.js +207 -0
- package/lib/goal-engine-constants.js +30 -0
- package/lib/goal-engine.js +45 -44
- package/lib/index.js +19 -8
- package/lib/routes.js +51 -0
- package/lib/tools.js +69 -0
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -1930,6 +1930,87 @@ window.__ModuleLoader__.load({
|
|
|
1930
1930
|
color: var(--dsw-alias-label-secondary);
|
|
1931
1931
|
font-size: 13px;
|
|
1932
1932
|
}
|
|
1933
|
+
/* Sub-goals and Compaction (Issue 167 and Issue 170) */
|
|
1934
|
+
.dsh-goal-badge-subgoal {
|
|
1935
|
+
background: var(--dsw-alias-brand-bg, var(--dsw-alias-bg-hover));
|
|
1936
|
+
color: var(--dsw-alias-brand-primary, var(--dsw-alias-text-primary));
|
|
1937
|
+
border: 1px solid var(--dsw-alias-brand-border, var(--dsw-alias-border));
|
|
1938
|
+
}
|
|
1939
|
+
.dsh-goal-breadcrumbs {
|
|
1940
|
+
display: inline-flex;
|
|
1941
|
+
align-items: center;
|
|
1942
|
+
gap: 4px;
|
|
1943
|
+
font-size: 13px;
|
|
1944
|
+
color: var(--dsw-alias-label-secondary);
|
|
1945
|
+
}
|
|
1946
|
+
.dsh-goal-breadcrumb-item {
|
|
1947
|
+
color: var(--dsw-alias-label-secondary);
|
|
1948
|
+
}
|
|
1949
|
+
.dsh-goal-breadcrumb-item.active {
|
|
1950
|
+
color: var(--dsw-alias-text-primary);
|
|
1951
|
+
font-weight: 600;
|
|
1952
|
+
}
|
|
1953
|
+
.dsh-goal-crumb-sep {
|
|
1954
|
+
color: var(--dsw-alias-label-tertiary);
|
|
1955
|
+
margin: 0 2px;
|
|
1956
|
+
}
|
|
1957
|
+
.dsh-goal-subgoals-box {
|
|
1958
|
+
display: flex;
|
|
1959
|
+
flex-direction: column;
|
|
1960
|
+
gap: 10px;
|
|
1961
|
+
padding: 4px 0;
|
|
1962
|
+
}
|
|
1963
|
+
.dsh-goal-subgoals-list {
|
|
1964
|
+
display: flex;
|
|
1965
|
+
flex-direction: column;
|
|
1966
|
+
gap: 8px;
|
|
1967
|
+
}
|
|
1968
|
+
.dsh-goal-subgoal-card {
|
|
1969
|
+
background: var(--dsw-alias-bg-layer-2, var(--dsw-alias-bg-hover));
|
|
1970
|
+
border: 1px solid var(--dsw-alias-border);
|
|
1971
|
+
border-radius: 6px;
|
|
1972
|
+
padding: 10px 12px;
|
|
1973
|
+
display: flex;
|
|
1974
|
+
flex-direction: column;
|
|
1975
|
+
gap: 4px;
|
|
1976
|
+
}
|
|
1977
|
+
.dsh-goal-subgoal-header {
|
|
1978
|
+
display: flex;
|
|
1979
|
+
align-items: center;
|
|
1980
|
+
justify-content: space-between;
|
|
1981
|
+
gap: 8px;
|
|
1982
|
+
}
|
|
1983
|
+
.dsh-goal-subgoal-title {
|
|
1984
|
+
font-weight: 600;
|
|
1985
|
+
font-size: 13px;
|
|
1986
|
+
color: var(--dsw-alias-text-primary);
|
|
1987
|
+
}
|
|
1988
|
+
.dsh-goal-subgoal-summary {
|
|
1989
|
+
font-size: 12px;
|
|
1990
|
+
color: var(--dsw-alias-label-secondary);
|
|
1991
|
+
}
|
|
1992
|
+
.dsh-goal-compacted-pill {
|
|
1993
|
+
display: inline-flex;
|
|
1994
|
+
align-items: center;
|
|
1995
|
+
gap: 3px;
|
|
1996
|
+
font-size: 10px;
|
|
1997
|
+
padding: 1px 5px;
|
|
1998
|
+
border-radius: 3px;
|
|
1999
|
+
background: var(--dsw-alias-bg-layer-2, var(--dsw-alias-bg-hover));
|
|
2000
|
+
color: var(--dsw-alias-state-success-primary, var(--dsw-alias-text-secondary));
|
|
2001
|
+
border: 1px solid var(--dsw-alias-border);
|
|
2002
|
+
margin-left: 6px;
|
|
2003
|
+
}
|
|
2004
|
+
.dsh-goal-stat-cost {
|
|
2005
|
+
background: var(--dsw-alias-bg-layer-2, var(--dsw-alias-bg-hover));
|
|
2006
|
+
color: var(--dsw-alias-state-warning-primary, var(--dsw-alias-text-secondary));
|
|
2007
|
+
border: 1px solid var(--dsw-alias-border);
|
|
2008
|
+
}
|
|
2009
|
+
.dsh-goal-badge-cost-limit {
|
|
2010
|
+
background: var(--dsw-alias-status-danger, var(--dsw-alias-status-warning));
|
|
2011
|
+
color: var(--dsw-alias-surface-raised, var(--dsw-alias-text-inverse));
|
|
2012
|
+
font-weight: 600;
|
|
2013
|
+
}
|
|
1933
2014
|
`;
|
|
1934
2015
|
function injectStylesOnce() {
|
|
1935
2016
|
|
|
@@ -2223,6 +2304,9 @@ window.__ModuleLoader__.load({
|
|
|
2223
2304
|
const [interveneText, setInterveneText] = useState('');
|
|
2224
2305
|
|
|
2225
2306
|
const [interveneSent, setInterveneSent] = useState(false);
|
|
2307
|
+
const [copiedNotes, setCopiedNotes] = useState(false);
|
|
2308
|
+
const [notifiedTelegram, setNotifiedTelegram] = useState(false);
|
|
2309
|
+
const [dispatchingTelegram, setDispatchingTelegram] = useState(false);
|
|
2226
2310
|
const [activeTab, setActiveTab] = useState("overview");
|
|
2227
2311
|
const [diffData, setDiffData] = useState(null);
|
|
2228
2312
|
const [diffLoading, setDiffLoading] = useState(false);
|
|
@@ -2364,6 +2448,53 @@ window.__ModuleLoader__.load({
|
|
|
2364
2448
|
);
|
|
2365
2449
|
};
|
|
2366
2450
|
|
|
2451
|
+
const renderSubGoalsBox = () => {
|
|
2452
|
+
const subList = Array.isArray(state.subGoals) ? state.subGoals : [];
|
|
2453
|
+
return React.createElement(
|
|
2454
|
+
'div',
|
|
2455
|
+
{ className: 'dsh-goal-subgoals-box' },
|
|
2456
|
+
state.isSubGoal
|
|
2457
|
+
? React.createElement(
|
|
2458
|
+
'div',
|
|
2459
|
+
{ className: 'dsh-goal-subgoal-card', style: { borderStyle: 'dashed' } },
|
|
2460
|
+
React.createElement('div', { style: { fontSize: 11, color: 'var(--dsw-alias-brand-primary)', fontWeight: 600 } }, '🌱 ' + (t('parentGoalBanner') || 'Child Sub-goal of:')),
|
|
2461
|
+
React.createElement('div', { style: { fontSize: 12, color: 'var(--dsw-alias-label-secondary)' } }, state.parentGoalId || 'Parent Goal')
|
|
2462
|
+
)
|
|
2463
|
+
: null,
|
|
2464
|
+
subList.length === 0
|
|
2465
|
+
? React.createElement('div', { className: 'dsh-goal-diff-empty' }, t('noSubGoals') || 'No sub-goals spawned yet.')
|
|
2466
|
+
: React.createElement(
|
|
2467
|
+
'div',
|
|
2468
|
+
{ className: 'dsh-goal-subgoals-list' },
|
|
2469
|
+
subList.map((sub, idx) =>
|
|
2470
|
+
React.createElement(
|
|
2471
|
+
'div',
|
|
2472
|
+
{ key: sub.id || sub.subGoalId || idx, className: 'dsh-goal-subgoal-card' },
|
|
2473
|
+
React.createElement(
|
|
2474
|
+
'div',
|
|
2475
|
+
{ className: 'dsh-goal-subgoal-header' },
|
|
2476
|
+
React.createElement('span', { className: 'dsh-goal-subgoal-title' }, sub.title),
|
|
2477
|
+
React.createElement(
|
|
2478
|
+
'span',
|
|
2479
|
+
{
|
|
2480
|
+
className: 'dsh-goal-badge ' + (sub.state === 'COMPLETED' ? 'dsh-goal-badge-ok' : sub.state === 'RUNNING' ? 'dsh-goal-badge-plan' : ''),
|
|
2481
|
+
},
|
|
2482
|
+
sub.state || 'PENDING'
|
|
2483
|
+
)
|
|
2484
|
+
),
|
|
2485
|
+
sub.resultSummary
|
|
2486
|
+
? React.createElement(
|
|
2487
|
+
'div',
|
|
2488
|
+
{ className: 'dsh-goal-subgoal-summary' },
|
|
2489
|
+
sub.resultSummary
|
|
2490
|
+
)
|
|
2491
|
+
: null
|
|
2492
|
+
)
|
|
2493
|
+
)
|
|
2494
|
+
)
|
|
2495
|
+
);
|
|
2496
|
+
};
|
|
2497
|
+
|
|
2367
2498
|
const totalTokens = state.tokensUsage?.totalTokens || 0;
|
|
2368
2499
|
|
|
2369
2500
|
const promptTokens = state.tokensUsage?.promptTokens || 0;
|
|
@@ -2382,6 +2513,45 @@ window.__ModuleLoader__.load({
|
|
|
2382
2513
|
|
|
2383
2514
|
: 'Tokens not recorded yet';
|
|
2384
2515
|
|
|
2516
|
+
const handleCopyReleaseNotes = async () => {
|
|
2517
|
+
try {
|
|
2518
|
+
const res = await fetch('/dsh-goal/action', {
|
|
2519
|
+
method: 'POST',
|
|
2520
|
+
headers: { 'Content-Type': 'application/json' },
|
|
2521
|
+
body: JSON.stringify({ action: 'export_release_notes', sessionId: state.sessionId }),
|
|
2522
|
+
});
|
|
2523
|
+
const json = await res.json();
|
|
2524
|
+
const notesText = json?.releaseNotes || '';
|
|
2525
|
+
if (notesText && typeof navigator !== 'undefined' && navigator.clipboard?.writeText) {
|
|
2526
|
+
await navigator.clipboard.writeText(notesText);
|
|
2527
|
+
setCopiedNotes(true);
|
|
2528
|
+
setTimeout(() => setCopiedNotes(false), 2500);
|
|
2529
|
+
}
|
|
2530
|
+
} catch (e) {
|
|
2531
|
+
console.warn('[dsh-goal] Failed to export release notes:', e);
|
|
2532
|
+
}
|
|
2533
|
+
};
|
|
2534
|
+
|
|
2535
|
+
const handleSendTelegram = async () => {
|
|
2536
|
+
setDispatchingTelegram(true);
|
|
2537
|
+
try {
|
|
2538
|
+
const res = await fetch('/dsh-goal/action', {
|
|
2539
|
+
method: 'POST',
|
|
2540
|
+
headers: { 'Content-Type': 'application/json' },
|
|
2541
|
+
body: JSON.stringify({ action: 'notify_telegram', sessionId: state.sessionId }),
|
|
2542
|
+
});
|
|
2543
|
+
const json = await res.json();
|
|
2544
|
+
if (json?.ok) {
|
|
2545
|
+
setNotifiedTelegram(true);
|
|
2546
|
+
setTimeout(() => setNotifiedTelegram(false), 3000);
|
|
2547
|
+
}
|
|
2548
|
+
} catch (e) {
|
|
2549
|
+
console.warn('[dsh-goal] Failed to notify telegram:', e);
|
|
2550
|
+
} finally {
|
|
2551
|
+
setDispatchingTelegram(false);
|
|
2552
|
+
}
|
|
2553
|
+
};
|
|
2554
|
+
|
|
2385
2555
|
const handleCopyReport = () => {
|
|
2386
2556
|
|
|
2387
2557
|
const md = generateMarkdownReport(state);
|
|
@@ -2502,11 +2672,22 @@ window.__ModuleLoader__.load({
|
|
|
2502
2672
|
onClick: () => setActiveTab("diff"),
|
|
2503
2673
|
},
|
|
2504
2674
|
"🔍 " + (t("tabDiff") || "Changes / Diff") + (diffData && diffData.stats && diffData.stats.filesChanged ? ` (${diffData.stats.filesChanged})` : "")
|
|
2675
|
+
),
|
|
2676
|
+
React.createElement(
|
|
2677
|
+
'button',
|
|
2678
|
+
{
|
|
2679
|
+
type: 'button',
|
|
2680
|
+
className: `dsh-goal-tab-btn${activeTab === "subgoals" ? " active" : ""}`,
|
|
2681
|
+
onClick: () => setActiveTab("subgoals"),
|
|
2682
|
+
},
|
|
2683
|
+
"🌳 " + (t("tabSubGoals") || "Sub-goals") + (state.subGoals && state.subGoals.length > 0 ? ` (${state.subGoals.length})` : "")
|
|
2505
2684
|
)
|
|
2506
2685
|
),
|
|
2507
2686
|
|
|
2508
2687
|
activeTab === 'diff'
|
|
2509
2688
|
? renderDiffBox()
|
|
2689
|
+
: activeTab === 'subgoals'
|
|
2690
|
+
? renderSubGoalsBox()
|
|
2510
2691
|
: React.createElement(
|
|
2511
2692
|
React.Fragment,
|
|
2512
2693
|
null,
|
|
@@ -2762,6 +2943,21 @@ window.__ModuleLoader__.load({
|
|
|
2762
2943
|
|
|
2763
2944
|
),
|
|
2764
2945
|
|
|
2946
|
+
React.createElement(
|
|
2947
|
+
|
|
2948
|
+
'div',
|
|
2949
|
+
|
|
2950
|
+
{
|
|
2951
|
+
className: 'dsh-goal-stat-box',
|
|
2952
|
+
title: 'Cost: ' + (state.costUsage?.formattedCost || '.00') + (state.maxCostBudget ? ' / $' + state.maxCostBudget.toFixed(2) : ''),
|
|
2953
|
+
},
|
|
2954
|
+
|
|
2955
|
+
React.createElement('span', { className: 'dsh-goal-stat-val' }, state.costUsage?.formattedCost || '.00'),
|
|
2956
|
+
|
|
2957
|
+
React.createElement('span', { className: 'dsh-goal-stat-lbl' }, t('cost') || 'Cost'),
|
|
2958
|
+
|
|
2959
|
+
),
|
|
2960
|
+
|
|
2765
2961
|
React.createElement(
|
|
2766
2962
|
|
|
2767
2963
|
'div',
|
|
@@ -2835,6 +3031,13 @@ window.__ModuleLoader__.load({
|
|
|
2835
3031
|
},
|
|
2836
3032
|
|
|
2837
3033
|
`${i + 1}. ${m.title}`,
|
|
3034
|
+
m.compactionSummary
|
|
3035
|
+
? React.createElement(
|
|
3036
|
+
'span',
|
|
3037
|
+
{ className: 'dsh-goal-compacted-pill', title: m.compactionSummary },
|
|
3038
|
+
'⚡ ' + (t('compactedBadge') || 'Compacted')
|
|
3039
|
+
)
|
|
3040
|
+
: null,
|
|
2838
3041
|
m.verificationCommand
|
|
2839
3042
|
? React.createElement(
|
|
2840
3043
|
'span',
|
|
@@ -3041,6 +3244,44 @@ window.__ModuleLoader__.load({
|
|
|
3041
3244
|
|
|
3042
3245
|
),
|
|
3043
3246
|
|
|
3247
|
+
React.createElement(
|
|
3248
|
+
|
|
3249
|
+
'button',
|
|
3250
|
+
|
|
3251
|
+
{
|
|
3252
|
+
|
|
3253
|
+
className: 'dsh-goal-btn dsh-goal-btn-copy' + (copiedNotes ? ' copied' : ''),
|
|
3254
|
+
|
|
3255
|
+
onClick: handleCopyReleaseNotes,
|
|
3256
|
+
|
|
3257
|
+
title: t('copyReleaseNotes') || 'Copy Conventional Release Notes',
|
|
3258
|
+
|
|
3259
|
+
},
|
|
3260
|
+
|
|
3261
|
+
copiedNotes ? (t('releaseNotesCopied') || '✅ Release Notes Copied!') : (t('copyReleaseNotes') || '📝 Release Notes'),
|
|
3262
|
+
|
|
3263
|
+
),
|
|
3264
|
+
|
|
3265
|
+
React.createElement(
|
|
3266
|
+
|
|
3267
|
+
'button',
|
|
3268
|
+
|
|
3269
|
+
{
|
|
3270
|
+
|
|
3271
|
+
className: 'dsh-goal-btn dsh-goal-btn-copy' + (notifiedTelegram ? ' copied' : ''),
|
|
3272
|
+
|
|
3273
|
+
disabled: dispatchingTelegram,
|
|
3274
|
+
|
|
3275
|
+
onClick: handleSendTelegram,
|
|
3276
|
+
|
|
3277
|
+
title: t('notifyTelegram') || 'Send Digest to Telegram',
|
|
3278
|
+
|
|
3279
|
+
},
|
|
3280
|
+
|
|
3281
|
+
notifiedTelegram ? (t('telegramNotified') || '✅ Sent to Telegram!') : (t('notifyTelegram') || '✈️ Telegram'),
|
|
3282
|
+
|
|
3283
|
+
),
|
|
3284
|
+
|
|
3044
3285
|
React.createElement(
|
|
3045
3286
|
|
|
3046
3287
|
'button',
|
|
@@ -4251,6 +4492,7 @@ window.__ModuleLoader__.load({
|
|
|
4251
4492
|
const etaStr = state?.formattedETA ? ` (ETA ${state.formattedETA})` : '';
|
|
4252
4493
|
|
|
4253
4494
|
const isBudgetExceeded = state?.state === 'PAUSED' && state?.logs?.some((l) => l.message?.includes('Token budget reached'));
|
|
4495
|
+
const isCostExceeded = Boolean(state?.costLimitExceeded);
|
|
4254
4496
|
|
|
4255
4497
|
return React.createElement(
|
|
4256
4498
|
|
|
@@ -4279,6 +4521,7 @@ window.__ModuleLoader__.load({
|
|
|
4279
4521
|
isCompleted ? (t('goalCompleted') || 'Done') : isWaitingOperator ? ('❓ ' + (t('operatorInputRequired') || 'Input Needed')) : isPlanMode ? (t('planMode') || 'Plan Mode') : isPaused ? (t('paused') || 'Paused') : (t('goalLabel') || 'Goal')
|
|
4280
4522
|
),
|
|
4281
4523
|
React.createElement('span', { className: 'dsh-goal-title', title: state.title }, state.title),
|
|
4524
|
+
state.costUsage && state.costUsage.totalCost > 0 ? React.createElement('span', { className: 'dsh-goal-time dsh-goal-stat-cost', title: (t('costUsage') || 'Cost') + ': ' + state.costUsage.formattedCost }, '💰 ' + state.costUsage.formattedCost) : null,
|
|
4282
4525
|
React.createElement('span', { className: 'dsh-goal-time' }, `⏱ ${liveElapsedStr}`),
|
|
4283
4526
|
isPlanMode && !isCompleted ? React.createElement(
|
|
4284
4527
|
'button',
|
|
@@ -4357,7 +4600,28 @@ window.__ModuleLoader__.load({
|
|
|
4357
4600
|
|
|
4358
4601
|
),
|
|
4359
4602
|
|
|
4360
|
-
|
|
4603
|
+
state.isSubGoal
|
|
4604
|
+
? React.createElement(
|
|
4605
|
+
'span',
|
|
4606
|
+
{ className: 'dsh-goal-badge dsh-goal-badge-subgoal', title: t('subGoalBadge') || 'Sub-goal' },
|
|
4607
|
+
'🌱 ' + (t('subGoalBadge') || 'Sub-goal')
|
|
4608
|
+
)
|
|
4609
|
+
: null,
|
|
4610
|
+
|
|
4611
|
+
state.breadcrumbs && state.breadcrumbs.length > 1
|
|
4612
|
+
? React.createElement(
|
|
4613
|
+
'span',
|
|
4614
|
+
{ className: 'dsh-goal-breadcrumbs', title: state.title },
|
|
4615
|
+
state.breadcrumbs.map((crumb, idx) =>
|
|
4616
|
+
React.createElement(
|
|
4617
|
+
'span',
|
|
4618
|
+
{ key: crumb.sessionId || idx, className: 'dsh-goal-breadcrumb-item' + (crumb.isCurrent ? ' active' : '') },
|
|
4619
|
+
idx > 0 && React.createElement('span', { className: 'dsh-goal-crumb-sep' }, ' / '),
|
|
4620
|
+
crumb.title
|
|
4621
|
+
)
|
|
4622
|
+
)
|
|
4623
|
+
)
|
|
4624
|
+
: React.createElement('span', { className: 'dsh-goal-title', title: state.title }, state.title),
|
|
4361
4625
|
|
|
4362
4626
|
),
|
|
4363
4627
|
|
|
@@ -4367,6 +4631,32 @@ window.__ModuleLoader__.load({
|
|
|
4367
4631
|
|
|
4368
4632
|
{ className: 'dsh-goal-right' },
|
|
4369
4633
|
|
|
4634
|
+
state.subGoals && state.subGoals.length > 0
|
|
4635
|
+
? React.createElement(
|
|
4636
|
+
'span',
|
|
4637
|
+
{
|
|
4638
|
+
className: 'dsh-goal-stat-pill',
|
|
4639
|
+
style: { cursor: 'pointer' },
|
|
4640
|
+
title: t('subGoalsTitle') || 'Sub-goals',
|
|
4641
|
+
onClick: () => setIsModalOpen(true),
|
|
4642
|
+
},
|
|
4643
|
+
'🌿 ' + state.subGoals.filter((s) => s.state === 'COMPLETED').length + '/' + state.subGoals.length + ' ' + (t('subGoalsPill') || 'sub-goals')
|
|
4644
|
+
)
|
|
4645
|
+
: null,
|
|
4646
|
+
|
|
4647
|
+
state.costUsage && state.costUsage.totalCost > 0
|
|
4648
|
+
? React.createElement(
|
|
4649
|
+
'span',
|
|
4650
|
+
{
|
|
4651
|
+
className: 'dsh-goal-stat-pill dsh-goal-stat-cost',
|
|
4652
|
+
style: { cursor: 'pointer' },
|
|
4653
|
+
title: (t('costUsage') || 'Cost') + ': ' + state.costUsage.formattedCost + (state.maxCostBudget ? ' / ' + state.costUsage.currency + ' ' + state.maxCostBudget.toFixed(2) : ''),
|
|
4654
|
+
onClick: () => setIsModalOpen(true),
|
|
4655
|
+
},
|
|
4656
|
+
'💰 ' + state.costUsage.formattedCost
|
|
4657
|
+
)
|
|
4658
|
+
: null,
|
|
4659
|
+
|
|
4370
4660
|
React.createElement(
|
|
4371
4661
|
|
|
4372
4662
|
'span',
|
|
@@ -4411,6 +4701,16 @@ window.__ModuleLoader__.load({
|
|
|
4411
4701
|
|
|
4412
4702
|
: null,
|
|
4413
4703
|
|
|
4704
|
+
isCostExceeded
|
|
4705
|
+
? React.createElement(
|
|
4706
|
+
'span',
|
|
4707
|
+
{
|
|
4708
|
+
className: 'dsh-goal-badge dsh-goal-badge-cost-limit',
|
|
4709
|
+
title: t('costLimitReached') || 'Cost Budget Exceeded',
|
|
4710
|
+
},
|
|
4711
|
+
'🚨 ' + (t('costLimitReached') || 'Cost Limit')
|
|
4712
|
+
)
|
|
4713
|
+
: null,
|
|
4414
4714
|
isBudgetExceeded
|
|
4415
4715
|
? React.createElement(
|
|
4416
4716
|
'button',
|
|
@@ -5835,6 +6135,22 @@ window.__ModuleLoader__.load({
|
|
|
5835
6135
|
allFiles: 'All Files',
|
|
5836
6136
|
planModeDesc: 'Plan first, review milestones, then execute',
|
|
5837
6137
|
actModeDesc: 'Direct execution right away',
|
|
6138
|
+
tabSubGoals: 'Sub-goals',
|
|
6139
|
+
subGoalsTitle: 'Hierarchical Sub-goals',
|
|
6140
|
+
subGoalBadge: 'Sub-goal',
|
|
6141
|
+
subGoalsPill: 'sub-goals',
|
|
6142
|
+
noSubGoals: 'No sub-goals spawned yet. Model can spawn sub-goals via goal_spawn_subgoal tool.',
|
|
6143
|
+
parentGoalBanner: 'Child Sub-goal of:',
|
|
6144
|
+
compactedBadge: 'Compacted',
|
|
6145
|
+
compactionTooltip: 'History compacted to reduce context footprint',
|
|
6146
|
+
cost: 'Cost',
|
|
6147
|
+
costUsage: 'Financial Cost',
|
|
6148
|
+
costLimitReached: 'Cost Budget Exceeded',
|
|
6149
|
+
costBudgetLabel: 'Financial Budget Limit ($):',
|
|
6150
|
+
copyReleaseNotes: '📝 Copy Release Notes',
|
|
6151
|
+
releaseNotesCopied: '✅ Release Notes Copied!',
|
|
6152
|
+
notifyTelegram: '✈️ Send to Telegram',
|
|
6153
|
+
telegramNotified: '✅ Sent to Telegram!',
|
|
5838
6154
|
|
|
5839
6155
|
},
|
|
5840
6156
|
|
|
@@ -5997,6 +6313,22 @@ window.__ModuleLoader__.load({
|
|
|
5997
6313
|
allFiles: '所有文件',
|
|
5998
6314
|
planModeDesc: '先制定里程碑方案,人工确认后再执行',
|
|
5999
6315
|
actModeDesc: '立即开始编码与任务执行',
|
|
6316
|
+
tabSubGoals: '子目标',
|
|
6317
|
+
subGoalsTitle: '分层子目标管理',
|
|
6318
|
+
subGoalBadge: '子目标',
|
|
6319
|
+
subGoalsPill: '个子目标',
|
|
6320
|
+
noSubGoals: '当前任务暂无衍生子目标。模型可通过 goal_spawn_subgoal 工具动态创建独立子目标。',
|
|
6321
|
+
parentGoalBanner: '衍生自主目标:',
|
|
6322
|
+
compactedBadge: '已压缩',
|
|
6323
|
+
compactionTooltip: '阶段历史已自动精简压缩以优化上下文窗口',
|
|
6324
|
+
cost: '成本',
|
|
6325
|
+
costUsage: '财务消耗',
|
|
6326
|
+
costLimitReached: '财务预算超限',
|
|
6327
|
+
costBudgetLabel: '最高财务预算限制 ($):',
|
|
6328
|
+
copyReleaseNotes: '📝 复制发布简报',
|
|
6329
|
+
releaseNotesCopied: '✅ 发布简报已复制!',
|
|
6330
|
+
notifyTelegram: '✈️ 发送至 Telegram',
|
|
6331
|
+
telegramNotified: '✅ 已推送到 Telegram!',
|
|
6000
6332
|
|
|
6001
6333
|
},
|
|
6002
6334
|
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Milestone Context Compaction for DSH Goal Engine (Issue #167).
|
|
3
|
+
* Compresses verbose raw logs and full checklist traces of completed milestones
|
|
4
|
+
* into high-density summaries, cutting context token footprint by > 40%.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Compact a single milestone upon completion into a structured summary
|
|
9
|
+
* @param {object} milestone
|
|
10
|
+
* @param {object} [options]
|
|
11
|
+
* @returns {object}
|
|
12
|
+
*/
|
|
13
|
+
export function compactMilestone(milestone, options = {}) {
|
|
14
|
+
if (!milestone) return null;
|
|
15
|
+
|
|
16
|
+
const id = milestone.id ? String(milestone.id).toUpperCase() : 'M';
|
|
17
|
+
const title = milestone.title || 'Milestone';
|
|
18
|
+
const notes = milestone.notes ? String(milestone.notes).trim() : '';
|
|
19
|
+
|
|
20
|
+
let checklistPart = '';
|
|
21
|
+
if (Array.isArray(milestone.checklist) && milestone.checklist.length > 0) {
|
|
22
|
+
const doneCount = milestone.checklist.filter((item) => item && item.done).length;
|
|
23
|
+
checklistPart = ` [${doneCount}/${milestone.checklist.length} subtasks]`;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
let gatePart = '';
|
|
27
|
+
if (milestone.verificationCommand) {
|
|
28
|
+
gatePart = milestone.verificationPassed ? ' [Gate: verified]' : ' [Gate: passed]';
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const outcomePart = notes ? ` (Outcome: ${notes})` : '';
|
|
32
|
+
const summaryLine = `[${id} COMPLETED] ${title}${outcomePart}${checklistPart}${gatePart}`;
|
|
33
|
+
|
|
34
|
+
milestone.compactionSummary = summaryLine;
|
|
35
|
+
milestone.compactedAt = milestone.completedAt || Date.now();
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
id: milestone.id,
|
|
39
|
+
title,
|
|
40
|
+
notes,
|
|
41
|
+
summary: summaryLine,
|
|
42
|
+
completedAt: milestone.compactedAt,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Build compacted milestones history block for active prompt injection
|
|
48
|
+
* @param {Array<object>} milestones
|
|
49
|
+
* @param {string} [lang='en']
|
|
50
|
+
* @returns {string}
|
|
51
|
+
*/
|
|
52
|
+
export function buildCompactedMilestonesHistory(milestones = [], lang = 'en') {
|
|
53
|
+
if (!Array.isArray(milestones) || milestones.length === 0) return '';
|
|
54
|
+
|
|
55
|
+
const completed = milestones.filter((m) => m && m.status === 'completed');
|
|
56
|
+
if (completed.length === 0) return '';
|
|
57
|
+
|
|
58
|
+
const header = lang === 'zh'
|
|
59
|
+
? '[已完成里程碑历史简报 / COMPACTED MILESTONES HISTORY]'
|
|
60
|
+
: '[COMPACTED MILESTONES HISTORY]';
|
|
61
|
+
|
|
62
|
+
const lines = completed.map((m) => {
|
|
63
|
+
if (m.compactionSummary) return `- ${m.compactionSummary}`;
|
|
64
|
+
const id = m.id ? String(m.id).toUpperCase() : 'M';
|
|
65
|
+
const outcome = m.notes ? ` (Outcome: ${m.notes})` : '';
|
|
66
|
+
return `- [${id} COMPLETED] ${m.title}${outcome}`;
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
return `${header}\n${lines.join('\n')}`;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Format milestone into raw uncompacted text (for baseline comparison & active milestones)
|
|
74
|
+
* @param {object} m
|
|
75
|
+
* @param {number} idx
|
|
76
|
+
* @returns {string}
|
|
77
|
+
*/
|
|
78
|
+
export function formatRawMilestoneTrace(m, idx = 0) {
|
|
79
|
+
if (!m) return '';
|
|
80
|
+
const statusStr = (m.status || 'pending').toUpperCase();
|
|
81
|
+
let line = ` ${idx + 1}. [${statusStr}] ${m.title || ''}${m.notes ? ` (${m.notes})` : ''}`;
|
|
82
|
+
if (m.verificationCommand) {
|
|
83
|
+
line += `\n [verification_command: ${m.verificationCommand}]`;
|
|
84
|
+
}
|
|
85
|
+
if (Array.isArray(m.checklist) && m.checklist.length > 0) {
|
|
86
|
+
const checkLines = m.checklist
|
|
87
|
+
.map((item) => ` [${item && item.done ? 'x' : ' '}] ${item && item.text ? item.text : ''}`)
|
|
88
|
+
.join('\n');
|
|
89
|
+
line += `\n${checkLines}`;
|
|
90
|
+
}
|
|
91
|
+
return line;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Estimate context savings achieved by compaction on completed milestones
|
|
96
|
+
* @param {Array<object>} milestones
|
|
97
|
+
* @param {string} [lang='en']
|
|
98
|
+
* @returns {{ completedCount: number, uncompactedChars: number, compactedChars: number, savedChars: number, savingsRatio: number, savingsPercent: string }}
|
|
99
|
+
*/
|
|
100
|
+
export function estimateCompactionSavings(milestones = [], lang = 'en') {
|
|
101
|
+
if (!Array.isArray(milestones) || milestones.length === 0) {
|
|
102
|
+
return { completedCount: 0, uncompactedChars: 0, compactedChars: 0, savedChars: 0, savingsRatio: 0, savingsPercent: '0%' };
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const completed = milestones.filter((m) => m && m.status === 'completed');
|
|
106
|
+
if (completed.length === 0) {
|
|
107
|
+
return { completedCount: 0, uncompactedChars: 0, compactedChars: 0, savedChars: 0, savingsRatio: 0, savingsPercent: '0%' };
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const uncompacted = completed.map((m, i) => formatRawMilestoneTrace(m, i)).join('\n');
|
|
111
|
+
const compacted = completed.map((m) => m.compactionSummary || `[${m.id ? String(m.id).toUpperCase() : 'M'} COMPLETED] ${m.title}`).join('\n');
|
|
112
|
+
|
|
113
|
+
const uncompactedChars = uncompacted.length;
|
|
114
|
+
const compactedChars = compacted.length;
|
|
115
|
+
const savedChars = Math.max(0, uncompactedChars - compactedChars);
|
|
116
|
+
const savingsRatio = uncompactedChars > 0 ? (savedChars / uncompactedChars) : 0;
|
|
117
|
+
|
|
118
|
+
return {
|
|
119
|
+
completedCount: completed.length,
|
|
120
|
+
uncompactedChars,
|
|
121
|
+
compactedChars,
|
|
122
|
+
savedChars,
|
|
123
|
+
savingsRatio,
|
|
124
|
+
savingsPercent: `${Math.round(savingsRatio * 100)}%`,
|
|
125
|
+
};
|
|
126
|
+
}
|