instar 0.28.95 → 0.28.97
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/dashboard/index.html +249 -2
- package/dist/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +90 -1
- package/dist/commands/server.js.map +1 -1
- package/dist/core/MachineHeartbeat.d.ts +94 -0
- package/dist/core/MachineHeartbeat.d.ts.map +1 -0
- package/dist/core/MachineHeartbeat.js +155 -0
- package/dist/core/MachineHeartbeat.js.map +1 -0
- package/dist/core/ProjectAutoAdvancePoller.d.ts +107 -0
- package/dist/core/ProjectAutoAdvancePoller.d.ts.map +1 -0
- package/dist/core/ProjectAutoAdvancePoller.js +176 -0
- package/dist/core/ProjectAutoAdvancePoller.js.map +1 -0
- package/dist/core/ProjectRoundCompleteMessage.d.ts +158 -0
- package/dist/core/ProjectRoundCompleteMessage.d.ts.map +1 -0
- package/dist/core/ProjectRoundCompleteMessage.js +253 -0
- package/dist/core/ProjectRoundCompleteMessage.js.map +1 -0
- package/dist/core/ProjectRoundExecution.d.ts +111 -0
- package/dist/core/ProjectRoundExecution.d.ts.map +1 -0
- package/dist/core/ProjectRoundExecution.js +359 -0
- package/dist/core/ProjectRoundExecution.js.map +1 -0
- package/dist/core/ProjectRoundLock.d.ts +67 -0
- package/dist/core/ProjectRoundLock.d.ts.map +1 -0
- package/dist/core/ProjectRoundLock.js +161 -0
- package/dist/core/ProjectRoundLock.js.map +1 -0
- package/dist/core/ProjectRoundRunner.d.ts +146 -0
- package/dist/core/ProjectRoundRunner.d.ts.map +1 -0
- package/dist/core/ProjectRoundRunner.js +371 -0
- package/dist/core/ProjectRoundRunner.js.map +1 -0
- package/dist/core/ProjectRoundWorktrees.d.ts +57 -0
- package/dist/core/ProjectRoundWorktrees.d.ts.map +1 -0
- package/dist/core/ProjectRoundWorktrees.js +111 -0
- package/dist/core/ProjectRoundWorktrees.js.map +1 -0
- package/dist/server/AgentServer.d.ts +12 -0
- package/dist/server/AgentServer.d.ts.map +1 -1
- package/dist/server/AgentServer.js +3 -0
- package/dist/server/AgentServer.js.map +1 -1
- package/dist/server/routes.d.ts +19 -0
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +782 -7
- package/dist/server/routes.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +48 -48
- package/upgrades/0.28.96.md +347 -0
- package/upgrades/0.28.97.md +146 -0
- package/upgrades/side-effects/project-scope-phase1-7-skill-surface.md +175 -0
- package/upgrades/side-effects/project-scope-phase1b-connect-the-dots.md +184 -0
- package/upgrades/side-effects/project-scope-phase1b-pr3.md +228 -0
- package/upgrades/side-effects/project-scope-phase1b-pr4.md +198 -0
- package/upgrades/side-effects/project-scope-phase1b-pr5.md +150 -0
- package/upgrades/side-effects/project-scope-phase1b-pr6.md +136 -0
- package/upgrades/side-effects/project-scope-phase1b-pr7.md +175 -0
package/dashboard/index.html
CHANGED
|
@@ -2523,6 +2523,7 @@
|
|
|
2523
2523
|
<button class="tab" data-tab="systems" onclick="switchTab('systems')">Health</button>
|
|
2524
2524
|
<button class="tab" data-tab="integrated-being" onclick="switchTab('integrated-being')">Integrated-Being</button>
|
|
2525
2525
|
<button class="tab" data-tab="pr-pipeline" onclick="switchTab('pr-pipeline')">PR Pipeline</button>
|
|
2526
|
+
<button class="tab" data-tab="projects" onclick="switchTab('projects')">Projects <span class="tab-count" id="tabProjectCount">0</span></button>
|
|
2526
2527
|
<button class="tab" data-tab="initiatives" onclick="switchTab('initiatives')">Initiatives <span class="tab-count" id="tabInitiativeCount">0</span></button>
|
|
2527
2528
|
<button class="tab" data-tab="commitments" onclick="switchTab('commitments')">Commitments <span class="tab-count" id="tabCommitmentCount">0</span></button>
|
|
2528
2529
|
<button class="tab" data-tab="tokens" onclick="switchTab('tokens')">Tokens</button>
|
|
@@ -2892,6 +2893,21 @@
|
|
|
2892
2893
|
<div id="prPipelineList" style="display:flex;flex-direction:column;gap:12px"></div>
|
|
2893
2894
|
</div>
|
|
2894
2895
|
|
|
2896
|
+
<!-- Projects Tab — PROJECT-SCOPE-SPEC Phase 1.10 read-only view -->
|
|
2897
|
+
<div id="projectsPanel" style="display:none;flex-direction:column;padding:20px;gap:16px;overflow-y:auto">
|
|
2898
|
+
<div style="display:flex;justify-content:space-between;align-items:center">
|
|
2899
|
+
<h2 style="margin:0">Projects</h2>
|
|
2900
|
+
<button onclick="loadProjects()" style="padding:6px 12px">Refresh</button>
|
|
2901
|
+
</div>
|
|
2902
|
+
<div style="font-size:12px;color:var(--text-dim);line-height:1.4">
|
|
2903
|
+
Multi-spec project plans. Each project bundles related child initiatives across rounds with structurally-gated round advance.
|
|
2904
|
+
</div>
|
|
2905
|
+
<div id="projectsEmpty" style="padding:40px;text-align:center;color:var(--text-dim);display:none">
|
|
2906
|
+
No active projects. Create one via <code>POST /projects</code> with a plan-doc path.
|
|
2907
|
+
</div>
|
|
2908
|
+
<div id="projectsList" style="display:flex;flex-direction:column;gap:12px"></div>
|
|
2909
|
+
</div>
|
|
2910
|
+
|
|
2895
2911
|
<!-- Initiatives Tab — multi-phase long-running work -->
|
|
2896
2912
|
<div id="initiativesPanel" style="display:none;flex-direction:column;padding:20px;gap:16px;overflow-y:auto">
|
|
2897
2913
|
<div style="display:flex;justify-content:space-between;align-items:center">
|
|
@@ -2904,6 +2920,9 @@
|
|
|
2904
2920
|
<option value="archived">Archived</option>
|
|
2905
2921
|
<option value="abandoned">Abandoned</option>
|
|
2906
2922
|
</select>
|
|
2923
|
+
<label style="font-size:12px;color:var(--text-dim);display:flex;align-items:center;gap:4px">
|
|
2924
|
+
<input type="checkbox" id="initiativesShowAll" onchange="loadInitiatives()"> Show projects + children
|
|
2925
|
+
</label>
|
|
2907
2926
|
<button onclick="loadInitiatives()" style="padding:6px 12px">Refresh</button>
|
|
2908
2927
|
</div>
|
|
2909
2928
|
</div>
|
|
@@ -4185,6 +4204,13 @@
|
|
|
4185
4204
|
display: ['flex'],
|
|
4186
4205
|
onActivate: () => { if (typeof loadPrPipeline === 'function') loadPrPipeline(); },
|
|
4187
4206
|
},
|
|
4207
|
+
{
|
|
4208
|
+
id: 'projects',
|
|
4209
|
+
panels: ['projectsPanel'],
|
|
4210
|
+
display: ['flex'],
|
|
4211
|
+
onActivate: () => { if (typeof loadProjects === 'function') loadProjects(); if (typeof startProjectsPoll === 'function') startProjectsPoll(); },
|
|
4212
|
+
onDeactivate: () => { if (typeof stopProjectsPoll === 'function') stopProjectsPoll(); },
|
|
4213
|
+
},
|
|
4188
4214
|
{
|
|
4189
4215
|
id: 'initiatives',
|
|
4190
4216
|
panels: ['initiativesPanel'],
|
|
@@ -6682,6 +6708,216 @@
|
|
|
6682
6708
|
}
|
|
6683
6709
|
}
|
|
6684
6710
|
|
|
6711
|
+
// ── Projects Tab (PROJECT-SCOPE-SPEC Phase 1.10) ─────────────
|
|
6712
|
+
//
|
|
6713
|
+
// Read-only project view. Per spec: progress bar per round +
|
|
6714
|
+
// pipelineStage histogram, drift status badge per pending item
|
|
6715
|
+
// (cached verdict only; clicking does NOT trigger a new check),
|
|
6716
|
+
// halt + ack buttons, 15s poll, textContent rendering on every
|
|
6717
|
+
// user/agent-authored string, 409 response silently refreshes.
|
|
6718
|
+
let __projectsPollTimer = null;
|
|
6719
|
+
function startProjectsPoll() {
|
|
6720
|
+
if (__projectsPollTimer) return;
|
|
6721
|
+
__projectsPollTimer = setInterval(() => { loadProjects().catch(() => {}); }, 15000);
|
|
6722
|
+
}
|
|
6723
|
+
function stopProjectsPoll() {
|
|
6724
|
+
if (__projectsPollTimer) { clearInterval(__projectsPollTimer); __projectsPollTimer = null; }
|
|
6725
|
+
}
|
|
6726
|
+
|
|
6727
|
+
function escapeText(s) {
|
|
6728
|
+
// textContent assignment handles XSS for us; this is a no-op
|
|
6729
|
+
// helper that documents the intent for future authors.
|
|
6730
|
+
return typeof s === 'string' ? s : '';
|
|
6731
|
+
}
|
|
6732
|
+
|
|
6733
|
+
async function loadProjects() {
|
|
6734
|
+
const list = document.getElementById('projectsList');
|
|
6735
|
+
const empty = document.getElementById('projectsEmpty');
|
|
6736
|
+
const countBadge = document.getElementById('tabProjectCount');
|
|
6737
|
+
if (!list || !empty) return;
|
|
6738
|
+
|
|
6739
|
+
let projRes = null;
|
|
6740
|
+
try {
|
|
6741
|
+
projRes = await apiFetch('/projects').catch(() => null);
|
|
6742
|
+
} catch { /* handled by null check below */ }
|
|
6743
|
+
|
|
6744
|
+
while (list.firstChild) list.removeChild(list.firstChild);
|
|
6745
|
+
empty.style.display = 'none';
|
|
6746
|
+
|
|
6747
|
+
if (!projRes || !Array.isArray(projRes.items)) {
|
|
6748
|
+
empty.style.display = 'block';
|
|
6749
|
+
empty.textContent = 'Projects endpoint unavailable.';
|
|
6750
|
+
if (countBadge) countBadge.textContent = '0';
|
|
6751
|
+
return;
|
|
6752
|
+
}
|
|
6753
|
+
|
|
6754
|
+
const items = projRes.items.filter((p) => p.status === 'active');
|
|
6755
|
+
if (countBadge) countBadge.textContent = String(items.length);
|
|
6756
|
+
if (items.length === 0) {
|
|
6757
|
+
empty.style.display = 'block';
|
|
6758
|
+
return;
|
|
6759
|
+
}
|
|
6760
|
+
|
|
6761
|
+
// We need each project's children for the progress bar; fetch
|
|
6762
|
+
// them in parallel via /projects/:id (server returns children
|
|
6763
|
+
// alongside the project record). Cap at 25 to avoid a flood.
|
|
6764
|
+
const detailed = await Promise.all(items.slice(0, 25).map((p) =>
|
|
6765
|
+
apiFetch('/projects/' + encodeURIComponent(p.id) + '?reconcile=false').catch(() => null)
|
|
6766
|
+
));
|
|
6767
|
+
|
|
6768
|
+
for (const detail of detailed) {
|
|
6769
|
+
if (!detail || !detail.project) continue;
|
|
6770
|
+
list.appendChild(renderProjectCard(detail.project, detail.children || []));
|
|
6771
|
+
}
|
|
6772
|
+
}
|
|
6773
|
+
|
|
6774
|
+
function renderProjectCard(project, children) {
|
|
6775
|
+
const card = document.createElement('div');
|
|
6776
|
+
card.style.padding = '14px';
|
|
6777
|
+
card.style.border = '1px solid var(--border)';
|
|
6778
|
+
card.style.borderRadius = '6px';
|
|
6779
|
+
card.style.background = 'var(--bg-dim)';
|
|
6780
|
+
|
|
6781
|
+
const titleRow = document.createElement('div');
|
|
6782
|
+
titleRow.style.display = 'flex';
|
|
6783
|
+
titleRow.style.justifyContent = 'space-between';
|
|
6784
|
+
titleRow.style.alignItems = 'baseline';
|
|
6785
|
+
titleRow.style.gap = '12px';
|
|
6786
|
+
|
|
6787
|
+
const title = document.createElement('div');
|
|
6788
|
+
title.style.fontWeight = '600';
|
|
6789
|
+
title.style.fontSize = '15px';
|
|
6790
|
+
title.textContent = project.title || project.id;
|
|
6791
|
+
titleRow.appendChild(title);
|
|
6792
|
+
|
|
6793
|
+
const idEl = document.createElement('div');
|
|
6794
|
+
idEl.style.fontSize = '11px';
|
|
6795
|
+
idEl.style.color = 'var(--text-dim)';
|
|
6796
|
+
idEl.style.fontFamily = 'monospace';
|
|
6797
|
+
idEl.textContent = project.id + ' v' + project.version;
|
|
6798
|
+
titleRow.appendChild(idEl);
|
|
6799
|
+
card.appendChild(titleRow);
|
|
6800
|
+
|
|
6801
|
+
// Drift status badge.
|
|
6802
|
+
const drift = project.rounds && project.rounds[0] ? project.rounds[0].lastDriftVerdict : null;
|
|
6803
|
+
if (drift && drift.verdict) {
|
|
6804
|
+
const badge = document.createElement('span');
|
|
6805
|
+
badge.style.display = 'inline-block';
|
|
6806
|
+
badge.style.marginTop = '4px';
|
|
6807
|
+
badge.style.padding = '2px 8px';
|
|
6808
|
+
badge.style.fontSize = '11px';
|
|
6809
|
+
badge.style.borderRadius = '10px';
|
|
6810
|
+
badge.style.border = '1px solid var(--border)';
|
|
6811
|
+
const colors = {
|
|
6812
|
+
'no-drift': '#4a9a4a',
|
|
6813
|
+
'minor-drift': '#e2b94a',
|
|
6814
|
+
'premise-violated': '#d65f5f',
|
|
6815
|
+
'manual-review-required': '#888',
|
|
6816
|
+
};
|
|
6817
|
+
badge.style.color = colors[drift.verdict] || '#888';
|
|
6818
|
+
badge.textContent = 'drift: ' + drift.verdict;
|
|
6819
|
+
card.appendChild(badge);
|
|
6820
|
+
}
|
|
6821
|
+
|
|
6822
|
+
// Rounds progress bars.
|
|
6823
|
+
const rounds = Array.isArray(project.rounds) ? project.rounds : [];
|
|
6824
|
+
const childById = new Map(children.map((c) => [c.id, c]));
|
|
6825
|
+
for (let i = 0; i < rounds.length; i++) {
|
|
6826
|
+
const r = rounds[i];
|
|
6827
|
+
const wrap = document.createElement('div');
|
|
6828
|
+
wrap.style.marginTop = '10px';
|
|
6829
|
+
|
|
6830
|
+
const rTitle = document.createElement('div');
|
|
6831
|
+
rTitle.style.fontSize = '12px';
|
|
6832
|
+
rTitle.style.color = 'var(--text-dim)';
|
|
6833
|
+
rTitle.textContent = `Round ${i}: ${r.name} — ${r.status || 'pending'}`;
|
|
6834
|
+
wrap.appendChild(rTitle);
|
|
6835
|
+
|
|
6836
|
+
// pipelineStage histogram counts.
|
|
6837
|
+
const stages = {};
|
|
6838
|
+
let total = 0;
|
|
6839
|
+
for (const id of (r.itemIds || [])) {
|
|
6840
|
+
const child = childById.get(id);
|
|
6841
|
+
const stage = (child && child.pipelineStage) || 'outline';
|
|
6842
|
+
stages[stage] = (stages[stage] || 0) + 1;
|
|
6843
|
+
total++;
|
|
6844
|
+
}
|
|
6845
|
+
const merged = stages['merged'] || 0;
|
|
6846
|
+
const bar = document.createElement('div');
|
|
6847
|
+
bar.style.marginTop = '4px';
|
|
6848
|
+
bar.style.height = '6px';
|
|
6849
|
+
bar.style.borderRadius = '3px';
|
|
6850
|
+
bar.style.background = 'var(--bg)';
|
|
6851
|
+
bar.style.overflow = 'hidden';
|
|
6852
|
+
const fill = document.createElement('div');
|
|
6853
|
+
fill.style.height = '100%';
|
|
6854
|
+
fill.style.background = '#4a9a4a';
|
|
6855
|
+
fill.style.width = total > 0 ? `${Math.round((merged / total) * 100)}%` : '0%';
|
|
6856
|
+
bar.appendChild(fill);
|
|
6857
|
+
wrap.appendChild(bar);
|
|
6858
|
+
|
|
6859
|
+
const histo = document.createElement('div');
|
|
6860
|
+
histo.style.fontSize = '11px';
|
|
6861
|
+
histo.style.color = 'var(--text-dim)';
|
|
6862
|
+
histo.style.marginTop = '4px';
|
|
6863
|
+
histo.textContent = Object.entries(stages).map(([k, v]) => `${k}: ${v}`).join(' • ') || 'no items';
|
|
6864
|
+
wrap.appendChild(histo);
|
|
6865
|
+
card.appendChild(wrap);
|
|
6866
|
+
}
|
|
6867
|
+
|
|
6868
|
+
// Halt + ack buttons.
|
|
6869
|
+
const actions = document.createElement('div');
|
|
6870
|
+
actions.style.display = 'flex';
|
|
6871
|
+
actions.style.gap = '8px';
|
|
6872
|
+
actions.style.marginTop = '12px';
|
|
6873
|
+
|
|
6874
|
+
const haltBtn = document.createElement('button');
|
|
6875
|
+
haltBtn.textContent = 'Halt';
|
|
6876
|
+
haltBtn.style.padding = '4px 10px';
|
|
6877
|
+
haltBtn.style.fontSize = '12px';
|
|
6878
|
+
haltBtn.addEventListener('click', () => projectHalt(project.id));
|
|
6879
|
+
actions.appendChild(haltBtn);
|
|
6880
|
+
|
|
6881
|
+
const ackBtn = document.createElement('button');
|
|
6882
|
+
ackBtn.textContent = 'Ack round 0';
|
|
6883
|
+
ackBtn.style.padding = '4px 10px';
|
|
6884
|
+
ackBtn.style.fontSize = '12px';
|
|
6885
|
+
ackBtn.addEventListener('click', () => projectAck(project.id, 0));
|
|
6886
|
+
actions.appendChild(ackBtn);
|
|
6887
|
+
card.appendChild(actions);
|
|
6888
|
+
|
|
6889
|
+
return card;
|
|
6890
|
+
}
|
|
6891
|
+
|
|
6892
|
+
async function projectHalt(projectId) {
|
|
6893
|
+
try {
|
|
6894
|
+
const res = await apiFetch('/projects/' + encodeURIComponent(projectId) + '/halt', {
|
|
6895
|
+
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
6896
|
+
body: JSON.stringify({ reason: 'user-halt-from-dashboard' }),
|
|
6897
|
+
});
|
|
6898
|
+
// 409 from a stale read → silent refresh, no error toast.
|
|
6899
|
+
if (res && res.error && /version mismatch/.test(res.error)) {
|
|
6900
|
+
await loadProjects();
|
|
6901
|
+
return;
|
|
6902
|
+
}
|
|
6903
|
+
await loadProjects();
|
|
6904
|
+
} catch { /* ignore — refresh on next poll */ }
|
|
6905
|
+
}
|
|
6906
|
+
|
|
6907
|
+
async function projectAck(projectId, forRoundIndex) {
|
|
6908
|
+
try {
|
|
6909
|
+
const res = await apiFetch('/projects/' + encodeURIComponent(projectId) + '/ack', {
|
|
6910
|
+
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
6911
|
+
body: JSON.stringify({ forRoundIndex }),
|
|
6912
|
+
});
|
|
6913
|
+
if (res && res.error && /version mismatch/.test(res.error)) {
|
|
6914
|
+
await loadProjects();
|
|
6915
|
+
return;
|
|
6916
|
+
}
|
|
6917
|
+
await loadProjects();
|
|
6918
|
+
} catch { /* ignore — refresh on next poll */ }
|
|
6919
|
+
}
|
|
6920
|
+
|
|
6685
6921
|
// ── Initiatives Tab (INITIATIVE-TRACKER-SPEC v1) ─────────────
|
|
6686
6922
|
//
|
|
6687
6923
|
// Fetches /initiatives and /initiatives/digest on panel activation.
|
|
@@ -6703,9 +6939,20 @@
|
|
|
6703
6939
|
let itemsRes = null;
|
|
6704
6940
|
let digestRes = null;
|
|
6705
6941
|
try {
|
|
6706
|
-
|
|
6942
|
+
// PROJECT-SCOPE-SPEC Phase 1.10 — by default hide project-kind
|
|
6943
|
+
// records (shown in the Projects tab) and parented children
|
|
6944
|
+
// (shown inside their parent's card). Server-side filter so
|
|
6945
|
+
// we don't ship them over the wire.
|
|
6946
|
+
const showAll = document.getElementById('initiativesShowAll');
|
|
6947
|
+
const params = [];
|
|
6948
|
+
if (filter && filter.value) params.push(`status=${encodeURIComponent(filter.value)}`);
|
|
6949
|
+
if (!showAll || !showAll.checked) {
|
|
6950
|
+
params.push('excludeKind=project');
|
|
6951
|
+
params.push('excludeParented=true');
|
|
6952
|
+
}
|
|
6953
|
+
const q = params.length ? '?' + params.join('&') : '';
|
|
6707
6954
|
const [a, b] = await Promise.all([
|
|
6708
|
-
apiFetch('/initiatives' +
|
|
6955
|
+
apiFetch('/initiatives' + q).catch(() => null),
|
|
6709
6956
|
apiFetch('/initiatives/digest').catch(() => null),
|
|
6710
6957
|
]);
|
|
6711
6958
|
itemsRes = a;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/commands/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAkQH,UAAU,YAAY;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;2DACuD;IACvD,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAw4CD,wBAAsB,WAAW,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/commands/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAkQH,UAAU,YAAY;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;2DACuD;IACvD,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAw4CD,wBAAsB,WAAW,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAknKtE;AAED,wBAAsB,UAAU,CAAC,OAAO,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAsDzE;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAuD5E"}
|
package/dist/commands/server.js
CHANGED
|
@@ -5860,6 +5860,95 @@ export async function startServer(options) {
|
|
|
5860
5860
|
const projectDigestCache = new ProjectDigestCache(config.stateDir, initiativeTracker);
|
|
5861
5861
|
initiativeTracker.setDigestCacheInvalidator(() => projectDigestCache.writeDigestCache());
|
|
5862
5862
|
projectDigestCache.writeDigestCache();
|
|
5863
|
+
// Project-scope Phase 1b PR 3 — round runner (single chokepoint for
|
|
5864
|
+
// /advance, /halt, /ack, /accept-partial; lock-protected; future
|
|
5865
|
+
// autonomous-delegating run loop).
|
|
5866
|
+
const machineIdForProjects = coordinator.identity?.machineId ?? os.hostname();
|
|
5867
|
+
const { ProjectRoundRunner } = await import('../core/ProjectRoundRunner.js');
|
|
5868
|
+
const projectRoundRunner = new ProjectRoundRunner({
|
|
5869
|
+
tracker: initiativeTracker,
|
|
5870
|
+
stateDir: config.stateDir,
|
|
5871
|
+
machineId: machineIdForProjects,
|
|
5872
|
+
});
|
|
5873
|
+
// Project-scope Phase 1b PR 4 — machine heartbeat + auto-advance poller.
|
|
5874
|
+
// Heartbeat writes .instar/machine-health/<machineId>.json every 30 min
|
|
5875
|
+
// (git-synced); the claim-ownership endpoint queries it for the >48h
|
|
5876
|
+
// staleness check that spec § P5 requires. Auto-advance poller scans
|
|
5877
|
+
// for project rounds whose autoAdvanceAt has elapsed and bookkeeps the
|
|
5878
|
+
// next round.
|
|
5879
|
+
const { MachineHeartbeat } = await import('../core/MachineHeartbeat.js');
|
|
5880
|
+
const machineHeartbeatApi = new MachineHeartbeat({
|
|
5881
|
+
stateDir: config.stateDir,
|
|
5882
|
+
machineId: machineIdForProjects,
|
|
5883
|
+
});
|
|
5884
|
+
machineHeartbeatApi.start();
|
|
5885
|
+
const machineHeartbeat = { api: machineHeartbeatApi, config: { machineId: machineIdForProjects } };
|
|
5886
|
+
// Wire the run-round executor: fire-and-forget launch of
|
|
5887
|
+
// ProjectRoundExecution.runRound() after a successful auto-advance.
|
|
5888
|
+
// The executor itself acquires the lock + spawns + polls + cleans up.
|
|
5889
|
+
// Errors are surfaced via the poller's result.executorErrors and
|
|
5890
|
+
// logged to stderr; they don't take the server down.
|
|
5891
|
+
const { runRound } = await import('../core/ProjectRoundExecution.js');
|
|
5892
|
+
const { ProjectAutoAdvancePoller } = await import('../core/ProjectAutoAdvancePoller.js');
|
|
5893
|
+
const projectAutoAdvancePoller = new ProjectAutoAdvancePoller({
|
|
5894
|
+
tracker: initiativeTracker,
|
|
5895
|
+
runner: projectRoundRunner,
|
|
5896
|
+
machineId: machineIdForProjects,
|
|
5897
|
+
executor: async ({ projectId, roundIndex }) => {
|
|
5898
|
+
const proj = initiativeTracker.get(projectId);
|
|
5899
|
+
if (!proj || !proj.targetRepoPath)
|
|
5900
|
+
return;
|
|
5901
|
+
await runRound({
|
|
5902
|
+
tracker: initiativeTracker,
|
|
5903
|
+
projectId,
|
|
5904
|
+
roundIndex,
|
|
5905
|
+
targetRepoPath: proj.targetRepoPath,
|
|
5906
|
+
}, { stateDir: config.stateDir });
|
|
5907
|
+
},
|
|
5908
|
+
});
|
|
5909
|
+
// Tick once a minute; .unref() so the timer never keeps the process alive.
|
|
5910
|
+
const projectAutoAdvanceTimer = setInterval(() => {
|
|
5911
|
+
projectAutoAdvancePoller.tick().catch((err) => {
|
|
5912
|
+
console.error('[ProjectAutoAdvancePoller] tick error:', err);
|
|
5913
|
+
});
|
|
5914
|
+
}, 60_000);
|
|
5915
|
+
if (typeof projectAutoAdvanceTimer.unref === 'function')
|
|
5916
|
+
projectAutoAdvanceTimer.unref();
|
|
5917
|
+
// Post-restore reconciler — downgrade any round still flagged
|
|
5918
|
+
// in-progress to pending. The previous owner may have crashed or
|
|
5919
|
+
// migrated, and no TaskFlow exists yet to detect an actually-live
|
|
5920
|
+
// run. One-shot at startup.
|
|
5921
|
+
try {
|
|
5922
|
+
for (const proj of initiativeTracker.list({ kind: 'project', status: 'active' })) {
|
|
5923
|
+
const rounds = proj.rounds ?? [];
|
|
5924
|
+
let any = false;
|
|
5925
|
+
const nextRounds = rounds.map((r) => {
|
|
5926
|
+
if (r.status === 'in-progress') {
|
|
5927
|
+
any = true;
|
|
5928
|
+
return { ...r, status: 'pending' };
|
|
5929
|
+
}
|
|
5930
|
+
return r;
|
|
5931
|
+
});
|
|
5932
|
+
if (any) {
|
|
5933
|
+
await initiativeTracker.update(proj.id, {
|
|
5934
|
+
rounds: nextRounds,
|
|
5935
|
+
ifMatch: proj.version,
|
|
5936
|
+
}).catch(() => { });
|
|
5937
|
+
}
|
|
5938
|
+
}
|
|
5939
|
+
}
|
|
5940
|
+
catch (err) {
|
|
5941
|
+
console.error('[ProjectAutoAdvancePoller] post-restore reconciler error:', err);
|
|
5942
|
+
}
|
|
5943
|
+
// Project drift checker — used by POST /projects/:id/drift-check.
|
|
5944
|
+
// Reuses the shared IntelligenceProvider. Cache + ledger are optional
|
|
5945
|
+
// and not wired here yet (the dashboard caller can pass per-call
|
|
5946
|
+
// overrides); a follow-up can wire ProjectDriftCheckerCache + the
|
|
5947
|
+
// DriftSpendLedger once spend telemetry is needed in prod.
|
|
5948
|
+
const { ProjectDriftChecker } = await import('../core/ProjectDriftChecker.js');
|
|
5949
|
+
const projectDriftChecker = new ProjectDriftChecker({
|
|
5950
|
+
intelligence: sharedIntelligence,
|
|
5951
|
+
});
|
|
5863
5952
|
// TaskFlow registry — opt-in via config.taskFlow.enabled (default: off in v1).
|
|
5864
5953
|
// Owns its own SQLite file under .instar/task-flows.db. The maintenance
|
|
5865
5954
|
// sweeper and due-waker start with the registry; both .unref() their timers
|
|
@@ -5964,7 +6053,7 @@ export async function startServer(options) {
|
|
|
5964
6053
|
},
|
|
5965
6054
|
});
|
|
5966
6055
|
}
|
|
5967
|
-
const server = new AgentServer({ config, sessionManager, state, scheduler, telegram, relationships, feedback, feedbackAnomalyDetector, dispatches, updateChecker, autoUpdater, autoDispatcher, quotaTracker, quotaManager, publisher, viewer, tunnel, evolution, watchdog, topicMemory, triageNurse, projectMapper, coherenceGate: scopeVerifier, contextHierarchy, canonicalState, operationGate, sentinel, adaptiveTrust, memoryMonitor, orphanReaper, coherenceMonitor, commitmentTracker, semanticMemory, activitySentinel, messageRouter, summarySentinel, spawnManager, systemReviewer, capabilityMapper, selfKnowledgeTree, coverageAuditor, topicResumeMap: _topicResumeMap ?? undefined, sessionRefresh: _sessionRefresh ?? undefined, autonomyManager, trustElevationTracker, autonomousEvolution, coordinator: coordinator.enabled ? coordinator : undefined, localSigningKeyPem, whatsapp: whatsappAdapter, slack: slackAdapter, imessage: imessageAdapter, whatsappBusinessBackend, messageBridge, hookEventReceiver, worktreeMonitor, subagentTracker, instructionsVerifier, handshakeManager: threadlineHandshake, threadlineRouter, threadResumeMap, topicLinkageHandler: topicLinkageHandler ?? undefined, threadlineRelayClient, threadlineReplyWaiters, listenerManager: listenerManager ?? undefined, responseReviewGate, messagingToneGate, outboundDedupGate, telemetryHeartbeat, pasteManager, featureRegistry, discoveryEvaluator, unifiedTrust, liveConfig, sharedStateLedger, ledgerSessionRegistry, worktreeManager, oidcEnrolledRepos: parallelDevConfig?.oidcEnrolledRepos, initiativeTracker, proxyCoordinator, telegramBridgeConfig, telegramBridge: telegramBridge ?? undefined, threadlineObservability, workingMemory, taskFlowRegistry, threadlineFlowBridge });
|
|
6056
|
+
const server = new AgentServer({ config, sessionManager, state, scheduler, telegram, relationships, feedback, feedbackAnomalyDetector, dispatches, updateChecker, autoUpdater, autoDispatcher, quotaTracker, quotaManager, publisher, viewer, tunnel, evolution, watchdog, topicMemory, triageNurse, projectMapper, coherenceGate: scopeVerifier, contextHierarchy, canonicalState, operationGate, sentinel, adaptiveTrust, memoryMonitor, orphanReaper, coherenceMonitor, commitmentTracker, semanticMemory, activitySentinel, messageRouter, summarySentinel, spawnManager, systemReviewer, capabilityMapper, selfKnowledgeTree, coverageAuditor, topicResumeMap: _topicResumeMap ?? undefined, sessionRefresh: _sessionRefresh ?? undefined, autonomyManager, trustElevationTracker, autonomousEvolution, coordinator: coordinator.enabled ? coordinator : undefined, localSigningKeyPem, whatsapp: whatsappAdapter, slack: slackAdapter, imessage: imessageAdapter, whatsappBusinessBackend, messageBridge, hookEventReceiver, worktreeMonitor, subagentTracker, instructionsVerifier, handshakeManager: threadlineHandshake, threadlineRouter, threadResumeMap, topicLinkageHandler: topicLinkageHandler ?? undefined, threadlineRelayClient, threadlineReplyWaiters, listenerManager: listenerManager ?? undefined, responseReviewGate, messagingToneGate, outboundDedupGate, telemetryHeartbeat, pasteManager, featureRegistry, discoveryEvaluator, unifiedTrust, liveConfig, sharedStateLedger, ledgerSessionRegistry, worktreeManager, oidcEnrolledRepos: parallelDevConfig?.oidcEnrolledRepos, initiativeTracker, projectRoundRunner, projectDriftChecker, machineHeartbeat, proxyCoordinator, telegramBridgeConfig, telegramBridge: telegramBridge ?? undefined, threadlineObservability, workingMemory, taskFlowRegistry, threadlineFlowBridge });
|
|
5968
6057
|
await server.start();
|
|
5969
6058
|
void taskFlowSweeper;
|
|
5970
6059
|
void taskFlowDueWaker;
|