@monoes/monomindcli 2.13.0 → 2.14.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/dist/src/commands/doctor-mcp-probe.d.ts +48 -0
- package/dist/src/commands/doctor-mcp-probe.d.ts.map +1 -0
- package/dist/src/commands/doctor-mcp-probe.js +205 -0
- package/dist/src/commands/doctor-mcp-probe.js.map +1 -0
- package/dist/src/commands/doctor-project-checks.d.ts +12 -1
- package/dist/src/commands/doctor-project-checks.d.ts.map +1 -1
- package/dist/src/commands/doctor-project-checks.js +55 -4
- package/dist/src/commands/doctor-project-checks.js.map +1 -1
- package/dist/src/commands/doctor.d.ts.map +1 -1
- package/dist/src/commands/doctor.js +10 -1
- package/dist/src/commands/doctor.js.map +1 -1
- package/dist/src/commands/init.d.ts.map +1 -1
- package/dist/src/commands/init.js +17 -0
- package/dist/src/commands/init.js.map +1 -1
- package/dist/src/commands/mcp.d.ts.map +1 -1
- package/dist/src/commands/mcp.js +4 -3
- package/dist/src/commands/mcp.js.map +1 -1
- package/dist/src/commands/org-observe.d.ts.map +1 -1
- package/dist/src/commands/org-observe.js +14 -0
- package/dist/src/commands/org-observe.js.map +1 -1
- package/dist/src/commands/org.d.ts.map +1 -1
- package/dist/src/commands/org.js +12 -2
- package/dist/src/commands/org.js.map +1 -1
- package/dist/src/init/claudemd-generator.d.ts.map +1 -1
- package/dist/src/init/claudemd-generator.js +2 -1
- package/dist/src/init/claudemd-generator.js.map +1 -1
- package/dist/src/init/mcp-generator.d.ts.map +1 -1
- package/dist/src/init/mcp-generator.js +3 -3
- package/dist/src/init/mcp-generator.js.map +1 -1
- package/dist/src/init/types.d.ts +6 -0
- package/dist/src/init/types.d.ts.map +1 -1
- package/dist/src/init/types.js.map +1 -1
- package/dist/src/init/write-runtime-config.d.ts.map +1 -1
- package/dist/src/init/write-runtime-config.js +12 -2
- package/dist/src/init/write-runtime-config.js.map +1 -1
- package/dist/src/orgrt/agent-runner.d.ts +38 -0
- package/dist/src/orgrt/agent-runner.d.ts.map +1 -1
- package/dist/src/orgrt/agent-runner.js +47 -0
- package/dist/src/orgrt/agent-runner.js.map +1 -1
- package/dist/src/orgrt/checkpoint.d.ts +12 -1
- package/dist/src/orgrt/checkpoint.d.ts.map +1 -1
- package/dist/src/orgrt/checkpoint.js +15 -6
- package/dist/src/orgrt/checkpoint.js.map +1 -1
- package/dist/src/orgrt/completion-gate.d.ts +74 -0
- package/dist/src/orgrt/completion-gate.d.ts.map +1 -1
- package/dist/src/orgrt/completion-gate.js +45 -0
- package/dist/src/orgrt/completion-gate.js.map +1 -1
- package/dist/src/orgrt/cost-tier.d.ts +144 -0
- package/dist/src/orgrt/cost-tier.d.ts.map +1 -0
- package/dist/src/orgrt/cost-tier.js +183 -0
- package/dist/src/orgrt/cost-tier.js.map +1 -0
- package/dist/src/orgrt/cross-org.d.ts.map +1 -1
- package/dist/src/orgrt/cross-org.js +14 -0
- package/dist/src/orgrt/cross-org.js.map +1 -1
- package/dist/src/orgrt/daemon.d.ts +12 -1
- package/dist/src/orgrt/daemon.d.ts.map +1 -1
- package/dist/src/orgrt/daemon.js +216 -49
- package/dist/src/orgrt/daemon.js.map +1 -1
- package/dist/src/orgrt/decisions.d.ts +14 -2
- package/dist/src/orgrt/decisions.d.ts.map +1 -1
- package/dist/src/orgrt/decisions.js +216 -10
- package/dist/src/orgrt/decisions.js.map +1 -1
- package/dist/src/orgrt/file-roots.d.ts +7 -0
- package/dist/src/orgrt/file-roots.d.ts.map +1 -1
- package/dist/src/orgrt/file-roots.js +38 -1
- package/dist/src/orgrt/file-roots.js.map +1 -1
- package/dist/src/orgrt/forwarder.d.ts.map +1 -1
- package/dist/src/orgrt/forwarder.js +16 -2
- package/dist/src/orgrt/forwarder.js.map +1 -1
- package/dist/src/orgrt/idle-deadline.d.ts +74 -1
- package/dist/src/orgrt/idle-deadline.d.ts.map +1 -1
- package/dist/src/orgrt/idle-deadline.js +52 -2
- package/dist/src/orgrt/idle-deadline.js.map +1 -1
- package/dist/src/orgrt/loadouts.d.ts +38 -0
- package/dist/src/orgrt/loadouts.d.ts.map +1 -0
- package/dist/src/orgrt/loadouts.js +129 -0
- package/dist/src/orgrt/loadouts.js.map +1 -0
- package/dist/src/orgrt/mailbox.d.ts +17 -1
- package/dist/src/orgrt/mailbox.d.ts.map +1 -1
- package/dist/src/orgrt/mailbox.js +50 -3
- package/dist/src/orgrt/mailbox.js.map +1 -1
- package/dist/src/orgrt/policy.d.ts +42 -2
- package/dist/src/orgrt/policy.d.ts.map +1 -1
- package/dist/src/orgrt/policy.js +53 -8
- package/dist/src/orgrt/policy.js.map +1 -1
- package/dist/src/orgrt/prompt-vars.d.ts +11 -0
- package/dist/src/orgrt/prompt-vars.d.ts.map +1 -0
- package/dist/src/orgrt/prompt-vars.js +49 -0
- package/dist/src/orgrt/prompt-vars.js.map +1 -0
- package/dist/src/orgrt/questions.d.ts +22 -1
- package/dist/src/orgrt/questions.d.ts.map +1 -1
- package/dist/src/orgrt/questions.js +24 -3
- package/dist/src/orgrt/questions.js.map +1 -1
- package/dist/src/orgrt/review-packet.d.ts +29 -0
- package/dist/src/orgrt/review-packet.d.ts.map +1 -0
- package/dist/src/orgrt/review-packet.js +67 -0
- package/dist/src/orgrt/review-packet.js.map +1 -0
- package/dist/src/orgrt/role-sandbox.d.ts.map +1 -1
- package/dist/src/orgrt/role-sandbox.js +16 -3
- package/dist/src/orgrt/role-sandbox.js.map +1 -1
- package/dist/src/orgrt/session-ledger.d.ts +61 -0
- package/dist/src/orgrt/session-ledger.d.ts.map +1 -0
- package/dist/src/orgrt/session-ledger.js +113 -0
- package/dist/src/orgrt/session-ledger.js.map +1 -0
- package/dist/src/orgrt/session.d.ts +45 -5
- package/dist/src/orgrt/session.d.ts.map +1 -1
- package/dist/src/orgrt/session.js +384 -27
- package/dist/src/orgrt/session.js.map +1 -1
- package/dist/src/orgrt/task-dag.d.ts +34 -1
- package/dist/src/orgrt/task-dag.d.ts.map +1 -1
- package/dist/src/orgrt/task-dag.js +58 -7
- package/dist/src/orgrt/task-dag.js.map +1 -1
- package/dist/src/orgrt/tool-spill.d.ts +94 -0
- package/dist/src/orgrt/tool-spill.d.ts.map +1 -0
- package/dist/src/orgrt/tool-spill.js +180 -0
- package/dist/src/orgrt/tool-spill.js.map +1 -0
- package/dist/src/orgrt/types.d.ts +138 -0
- package/dist/src/orgrt/types.d.ts.map +1 -1
- package/dist/src/orgrt/types.js +148 -0
- package/dist/src/orgrt/types.js.map +1 -1
- package/dist/src/platform-adapters/renderers/mcp.d.ts +20 -2
- package/dist/src/platform-adapters/renderers/mcp.d.ts.map +1 -1
- package/dist/src/platform-adapters/renderers/mcp.js +25 -5
- package/dist/src/platform-adapters/renderers/mcp.js.map +1 -1
- package/dist/src/protocol-capabilities.d.ts +2 -1
- package/dist/src/protocol-capabilities.d.ts.map +1 -1
- package/dist/src/protocol-capabilities.js +2 -1
- package/dist/src/protocol-capabilities.js.map +1 -1
- package/dist/src/ui/dashboard.html +450 -164
- package/dist/src/ui/org-hil.mjs +276 -0
- package/dist/src/ui/org-runtime.mjs +350 -0
- package/dist/src/ui/orgs.html +15 -4
- package/dist/src/ui/routes-org.mjs +290 -541
- package/dist/src/ui/server.mjs +25 -5
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +6 -5
|
@@ -1646,7 +1646,7 @@ select.pb-cfg-inp { padding: 4px 7px; cursor: pointer; }
|
|
|
1646
1646
|
<div class="nav-item" data-view="global-tokens" title="Token usage across all projects">
|
|
1647
1647
|
<span class="ico">$</span><span class="lbl">Global Tokens</span>
|
|
1648
1648
|
</div>
|
|
1649
|
-
<div class="nav-item" data-view="humaninput" title="
|
|
1649
|
+
<div class="nav-item" data-view="humaninput" title="Questions, approvals and decision gates waiting on you, across all orgs">
|
|
1650
1650
|
<span class="ico">❓</span><span class="lbl">Human Input</span>
|
|
1651
1651
|
<span class="bdg" id="bdg-humaninput">—</span>
|
|
1652
1652
|
</div>
|
|
@@ -2094,6 +2094,7 @@ select.pb-cfg-inp { padding: 4px 7px; cursor: pointer; }
|
|
|
2094
2094
|
<div id="org-detail-tabs">
|
|
2095
2095
|
<button class="odt-btn active" data-tab="chart" onclick="v2SwitchOrgTab('chart')">Chart</button>
|
|
2096
2096
|
<button class="odt-btn" data-tab="chat" onclick="v2SwitchOrgTab('chat')">Chat</button>
|
|
2097
|
+
<button class="odt-btn" data-tab="runtime" onclick="v2SwitchOrgTab('runtime')">Runtime</button>
|
|
2097
2098
|
<div class="odt-tab-sep" title="Secondary tabs"></div>
|
|
2098
2099
|
<button class="odt-btn secondary" data-tab="config" onclick="v2SwitchOrgTab('config')">Config</button>
|
|
2099
2100
|
<button class="odt-btn secondary" data-tab="files" onclick="v2SwitchOrgTab('files')">Files</button>
|
|
@@ -2103,6 +2104,7 @@ select.pb-cfg-inp { padding: 4px 7px; cursor: pointer; }
|
|
|
2103
2104
|
<div id="org-detail-body">
|
|
2104
2105
|
<div class="odt-pane active" id="odt-chart"></div>
|
|
2105
2106
|
<div class="odt-pane" id="odt-chat" style="flex-direction:column;height:100%;overflow:hidden;"></div>
|
|
2107
|
+
<div class="odt-pane" id="odt-runtime" style="overflow-y:auto"></div>
|
|
2106
2108
|
<div class="odt-pane" id="odt-config" style="overflow-y:auto"></div>
|
|
2107
2109
|
<div class="odt-pane" id="odt-files" style="overflow-y:auto"></div>
|
|
2108
2110
|
<div class="odt-pane" id="odt-approvals"></div>
|
|
@@ -2303,11 +2305,11 @@ select.pb-cfg-inp { padding: 4px 7px; cursor: pointer; }
|
|
|
2303
2305
|
<div class="vscroll">
|
|
2304
2306
|
<div style="display:flex;align-items:baseline;gap:10px;margin-bottom:4px">
|
|
2305
2307
|
<div class="pg-title" style="margin-bottom:0">Human Input</div>
|
|
2306
|
-
<span class="pg-sub" id="hi-sub" style="margin-bottom:0">
|
|
2308
|
+
<span class="pg-sub" id="hi-sub" style="margin-bottom:0">Questions, approvals and decision gates waiting on you</span>
|
|
2307
2309
|
</div>
|
|
2308
2310
|
<div class="hi-tab-bar" style="display:flex;gap:4px;margin-top:12px;border-bottom:1px solid var(--border);padding-bottom:6px">
|
|
2309
2311
|
<button class="odt-btn active" data-hitab="pending" onclick="hiSwitchTab('pending')">Pending <span style="font-size:10px;background:var(--surface-hi);color:var(--text-lo);border-radius:8px;padding:1px 6px;min-width:18px;display:inline-block;text-align:center" id="hi-tab-count-pending">—</span></button>
|
|
2310
|
-
<button class="odt-btn" data-hitab="answered" onclick="hiSwitchTab('answered')">
|
|
2312
|
+
<button class="odt-btn" data-hitab="answered" onclick="hiSwitchTab('answered')">Resolved <span style="font-size:10px;background:var(--surface-hi);color:var(--text-lo);border-radius:8px;padding:1px 6px;min-width:18px;display:inline-block;text-align:center" id="hi-tab-count-answered">—</span></button>
|
|
2311
2313
|
</div>
|
|
2312
2314
|
<div id="hi-content" style="margin-top:16px"><div class="loading-txt">Loading…</div></div>
|
|
2313
2315
|
</div>
|
|
@@ -2627,6 +2629,8 @@ async function init() {
|
|
|
2627
2629
|
sbProjEl.title = DIR;
|
|
2628
2630
|
_showNavProjectCtx(DIR);
|
|
2629
2631
|
} catch (_) {}
|
|
2632
|
+
// Count what is waiting on a human now, not only once the view is opened.
|
|
2633
|
+
_hiScheduleReload();
|
|
2630
2634
|
// deep-link: ?sess=<id>&proj=<path>
|
|
2631
2635
|
const params = new URLSearchParams(window.location.search);
|
|
2632
2636
|
let projParam = params.get('proj');
|
|
@@ -4446,38 +4450,57 @@ async function _gdCopyPath(p) {
|
|
|
4446
4450
|
// renderDocMarkdown() and gdCopyCode() moved to markdown.js (#124) — loaded via
|
|
4447
4451
|
// <script src="markdown.js"> above; both remain plain globals.
|
|
4448
4452
|
|
|
4453
|
+
// Human Input: everything an org is waiting on a human for — ask_human
|
|
4454
|
+
// questions, tool/action approvals and decision gates — across projects.
|
|
4455
|
+
// Each entry: { kind: 'question'|'approval'|'gate', project, item }.
|
|
4449
4456
|
let _hiEntries = [];
|
|
4450
4457
|
let _hiTab = 'pending';
|
|
4451
4458
|
|
|
4459
|
+
function _hiIsPending(e) {
|
|
4460
|
+
if (e.kind === 'question') return e.item.answer === null || e.item.answer === undefined;
|
|
4461
|
+
return (e.item.status || 'pending') === 'pending';
|
|
4462
|
+
}
|
|
4463
|
+
function _hiWhen(e) {
|
|
4464
|
+
const i = e.item;
|
|
4465
|
+
return i.answeredAt || i.resolvedAt || i.ts || i.createdAt || 0;
|
|
4466
|
+
}
|
|
4467
|
+
|
|
4452
4468
|
async function renderHumanInputView() {
|
|
4453
4469
|
const el = document.getElementById('hi-content');
|
|
4454
|
-
el.innerHTML = '<div class="loading-txt">Loading all projects…</div>';
|
|
4470
|
+
if (!_hiEntries.length) el.innerHTML = '<div class="loading-txt">Loading all projects…</div>';
|
|
4455
4471
|
try {
|
|
4456
4472
|
const data = await apiFetch('/api/projects');
|
|
4457
4473
|
const projects = (data?.projects || []).slice(0, 8);
|
|
4458
|
-
|
|
4459
|
-
|
|
4460
|
-
|
|
4461
|
-
updateHumanInputBadge(0);
|
|
4462
|
-
renderHiCurrentTab(projects.length);
|
|
4463
|
-
return;
|
|
4464
|
-
}
|
|
4474
|
+
// The project this dashboard serves always counts, even when it is not
|
|
4475
|
+
// among the most recently active Claude projects.
|
|
4476
|
+
if (DIR && !projects.some(p => p.path === DIR)) projects.unshift({ path: DIR, name: baseName(DIR) });
|
|
4465
4477
|
const results = await Promise.allSettled(
|
|
4466
|
-
projects.map(p => apiFetch(`/api/
|
|
4478
|
+
projects.map(p => apiFetch(`/api/hil?dir=${enc(p.path)}`).then(d => ({ project: p, d: d || {} })))
|
|
4467
4479
|
);
|
|
4468
4480
|
const entries = [];
|
|
4481
|
+
const errors = [];
|
|
4469
4482
|
for (const r of results) {
|
|
4470
4483
|
if (r.status !== 'fulfilled') continue;
|
|
4471
|
-
const { project,
|
|
4472
|
-
for (const
|
|
4484
|
+
const { project, d } = r.value;
|
|
4485
|
+
for (const item of d.questions || []) entries.push({ kind: 'question', project, item });
|
|
4486
|
+
for (const item of d.approvals || []) if (item.id) entries.push({ kind: 'approval', project, item });
|
|
4487
|
+
for (const item of d.gates || []) entries.push({ kind: 'gate', project, item });
|
|
4488
|
+
for (const e of d.errors || []) errors.push(`${project.name || project.path}/${e.org}: ${e.error}`);
|
|
4473
4489
|
}
|
|
4474
|
-
entries.sort((a, b) => (b
|
|
4490
|
+
entries.sort((a, b) => _hiWhen(b) - _hiWhen(a));
|
|
4475
4491
|
_hiEntries = entries;
|
|
4476
|
-
updateHumanInputBadge(entries.filter(
|
|
4477
|
-
renderHiCurrentTab(projects.length);
|
|
4492
|
+
updateHumanInputBadge(entries.filter(_hiIsPending).length);
|
|
4493
|
+
renderHiCurrentTab(projects.length, errors);
|
|
4478
4494
|
} catch (e) { el.innerHTML = `<div class="empty">Failed: ${esc(e.message)}</div>`; }
|
|
4479
4495
|
}
|
|
4480
4496
|
|
|
4497
|
+
// Coalesce bursts of org:question / org:gate / resolution events into one reload.
|
|
4498
|
+
let _hiReloadTimer = null;
|
|
4499
|
+
function _hiScheduleReload() {
|
|
4500
|
+
clearTimeout(_hiReloadTimer);
|
|
4501
|
+
_hiReloadTimer = setTimeout(renderHumanInputView, 400);
|
|
4502
|
+
}
|
|
4503
|
+
|
|
4481
4504
|
function _hiSwitchTab(tab) {
|
|
4482
4505
|
_hiTab = tab;
|
|
4483
4506
|
document.querySelectorAll('.hi-tab-bar .odt-btn').forEach(b =>
|
|
@@ -4485,39 +4508,80 @@ function _hiSwitchTab(tab) {
|
|
|
4485
4508
|
renderHiCurrentTab();
|
|
4486
4509
|
}
|
|
4487
4510
|
|
|
4488
|
-
|
|
4511
|
+
const _HI_KIND_LABEL = { question: 'Question', approval: 'Approval', gate: 'Decision gate' };
|
|
4512
|
+
|
|
4513
|
+
function _hiPill(text, color) {
|
|
4514
|
+
return `<span class="ss-pill" style="color:${color};border-color:${color}">${esc(text)}</span>`;
|
|
4515
|
+
}
|
|
4516
|
+
|
|
4517
|
+
function _hiEntryHtml(e) {
|
|
4518
|
+
const i = e.item;
|
|
4519
|
+
const pending = _hiIsPending(e);
|
|
4520
|
+
const data = `data-kind="${e.kind}" data-dir="${esc(e.project.path)}" data-org="${esc(i.org)}" data-id="${esc(e.kind === 'question' ? i.questionId : i.id)}"`;
|
|
4521
|
+
let badge = '';
|
|
4522
|
+
let body = '';
|
|
4523
|
+
let resolved = '';
|
|
4524
|
+
let actions = '';
|
|
4525
|
+
if (e.kind === 'question') {
|
|
4526
|
+
// Only a pending question can hold a run; once answered the flag is history.
|
|
4527
|
+
if (pending) badge = i.blocking === false
|
|
4528
|
+
? _hiPill('non-blocking', 'var(--text-lo)')
|
|
4529
|
+
: _hiPill('blocking — run waits', 'var(--amber, #d29922)');
|
|
4530
|
+
body = `<div style="color:var(--text-hi);white-space:pre-wrap;max-height:240px;overflow:auto">${esc(i.question)}</div>`;
|
|
4531
|
+
resolved = `<span style="color:var(--text-xs)">Answer:</span> ${esc(i.answer ?? '')}`;
|
|
4532
|
+
actions = `<input type="text" class="hi-answer-input" placeholder="Your answer…" aria-label="Answer" style="flex:1;padding:4px 8px;font-size:12px" />
|
|
4533
|
+
<button class="btn" style="font-size:11px" ${data} onclick="hiAct(this,'answer')">Answer</button>`;
|
|
4534
|
+
} else if (e.kind === 'approval') {
|
|
4535
|
+
badge = _hiPill(i.action || 'tool', 'var(--accent, #58a6ff)');
|
|
4536
|
+
body = `<div style="color:var(--text-hi)">Wants to run <b>${esc(i.action)}</b></div>` +
|
|
4537
|
+
(i.input ? `<pre style="font-size:11px;white-space:pre-wrap;max-height:160px;overflow:auto;margin:0;color:var(--text-lo)">${esc(JSON.stringify(i.input, null, 2))}</pre>` : '');
|
|
4538
|
+
resolved = `${_hiPill(i.status, i.status === 'approved' ? 'var(--green)' : 'var(--red)')}`;
|
|
4539
|
+
actions = `<button class="btn" style="font-size:11px;color:var(--green);border-color:var(--green)" ${data} onclick="hiAct(this,'approve')">Approve</button>
|
|
4540
|
+
<button class="btn" style="font-size:11px;color:var(--red);border-color:var(--red)" ${data} onclick="hiAct(this,'reject')">Deny</button>`;
|
|
4541
|
+
} else {
|
|
4542
|
+
body = `<div style="color:var(--text-hi);font-weight:600">${esc(i.name || '')}</div>
|
|
4543
|
+
<div style="color:var(--text-lo);white-space:pre-wrap;max-height:240px;overflow:auto">${esc(i.description || '')}</div>`;
|
|
4544
|
+
resolved = `${_hiPill(i.status, i.status === 'approved' ? 'var(--green)' : 'var(--red)')}${i.resolution ? ` ${esc(i.resolution)}` : ''}`;
|
|
4545
|
+
actions = `<input type="text" class="hi-answer-input" placeholder="Resolution note (optional)…" aria-label="Resolution note" style="flex:1;padding:4px 8px;font-size:12px" />
|
|
4546
|
+
<button class="btn" style="font-size:11px;color:var(--green);border-color:var(--green)" ${data} onclick="hiAct(this,'approve')">Approve</button>
|
|
4547
|
+
<button class="btn" style="font-size:11px;color:var(--red);border-color:var(--red)" ${data} onclick="hiAct(this,'reject')">Reject</button>`;
|
|
4548
|
+
}
|
|
4549
|
+
const who = i.resolvedBy ? ` · by ${esc(i.resolvedBy)}` : '';
|
|
4550
|
+
return `<div class="sess-row" style="flex-direction:column;align-items:stretch;gap:6px">
|
|
4551
|
+
<div style="display:flex;justify-content:space-between;gap:8px;align-items:center">
|
|
4552
|
+
<div><span style="color:var(--text-xs)">${_HI_KIND_LABEL[e.kind]}</span> · <b>${esc(i.org)}</b> · ${esc(i.role || i.roleId || '')} ${badge}
|
|
4553
|
+
<span style="color:var(--text-xs)">(${esc(e.project.name || e.project.path)})</span></div>
|
|
4554
|
+
<div style="color:var(--text-xs);font-size:11px;font-family:var(--mono)">${relTime(i.ts || i.createdAt)}</div>
|
|
4555
|
+
</div>
|
|
4556
|
+
${body}
|
|
4557
|
+
${pending
|
|
4558
|
+
? `<div style="display:flex;gap:6px;align-items:center">${actions}</div>`
|
|
4559
|
+
: `<div style="display:flex;justify-content:space-between;gap:8px;padding:6px 8px;background:var(--surface-hi);border-radius:4px">
|
|
4560
|
+
<div style="white-space:pre-wrap;max-height:160px;overflow:auto;min-width:0">${resolved}</div>
|
|
4561
|
+
<div style="color:var(--text-xs);font-size:11px;font-family:var(--mono);flex-shrink:0">${relTime(i.answeredAt || i.resolvedAt)}${who}</div>
|
|
4562
|
+
</div>`}
|
|
4563
|
+
</div>`;
|
|
4564
|
+
}
|
|
4565
|
+
|
|
4566
|
+
function renderHiCurrentTab(projectCount, errors) {
|
|
4489
4567
|
const el = document.getElementById('hi-content');
|
|
4490
|
-
const pending = _hiEntries.filter(
|
|
4491
|
-
const
|
|
4568
|
+
const pending = _hiEntries.filter(_hiIsPending);
|
|
4569
|
+
const done = _hiEntries.filter(e => !_hiIsPending(e));
|
|
4492
4570
|
document.getElementById('hi-tab-count-pending').textContent = pending.length || '—';
|
|
4493
|
-
document.getElementById('hi-tab-count-answered').textContent =
|
|
4494
|
-
const entries = _hiTab === 'answered' ?
|
|
4571
|
+
document.getElementById('hi-tab-count-answered').textContent = done.length || '—';
|
|
4572
|
+
const entries = _hiTab === 'answered' ? done : pending;
|
|
4495
4573
|
const projTxt = projectCount != null ? ` across ${projectCount} project${projectCount !== 1 ? 's' : ''}` : '';
|
|
4574
|
+
const blocking = pending.filter(e => e.kind !== 'question' || e.item.blocking !== false).length;
|
|
4496
4575
|
document.getElementById('hi-sub').textContent = _hiTab === 'answered'
|
|
4497
|
-
? (
|
|
4498
|
-
: (pending.length ? `${pending.length}
|
|
4576
|
+
? (done.length ? `${done.length} resolved${projTxt}` : `Nothing resolved yet${projTxt}`)
|
|
4577
|
+
: (pending.length ? `${pending.length} waiting on you (${blocking} holding a run)${projTxt}` : `Nothing waiting on you${projTxt}`);
|
|
4578
|
+
const errHtml = (errors || []).length
|
|
4579
|
+
? `<div class="empty" style="color:var(--red)">Unreadable: ${errors.map(esc).join('; ')}</div>` : '';
|
|
4499
4580
|
if (!entries.length) {
|
|
4500
|
-
el.innerHTML =
|
|
4581
|
+
el.innerHTML = `${errHtml}<div class="empty"><div class="empty-ico">❓</div><div>${_hiTab === 'answered' ? 'Nothing resolved yet' : 'Nothing waiting on you'}</div></div>`;
|
|
4501
4582
|
return;
|
|
4502
4583
|
}
|
|
4503
|
-
el.innerHTML =
|
|
4504
|
-
<div class="sess-row" data-question-id="${esc(q.questionId)}" style="flex-direction:column;align-items:stretch;gap:6px">
|
|
4505
|
-
<div style="display:flex;justify-content:space-between;gap:8px">
|
|
4506
|
-
<div><b>${esc(q.org)}</b> · ${esc(q.role)} <span style="color:var(--text-xs)">(${esc(project.name || project.path)})</span></div>
|
|
4507
|
-
<div style="color:var(--text-xs);font-size:11px;font-family:var(--mono)">${relTime(q.ts)}</div>
|
|
4508
|
-
</div>
|
|
4509
|
-
<div style="color:var(--text-hi)">${esc(q.question)}</div>
|
|
4510
|
-
${_hiTab === 'answered' ? `
|
|
4511
|
-
<div style="display:flex;justify-content:space-between;gap:8px;padding:6px 8px;background:var(--surface-hi);border-radius:4px">
|
|
4512
|
-
<div><span style="color:var(--text-xs)">Answer:</span> ${esc(q.answer)}</div>
|
|
4513
|
-
<div style="color:var(--text-xs);font-size:11px;font-family:var(--mono);flex-shrink:0">${relTime(q.answeredAt)}</div>
|
|
4514
|
-
</div>` : `
|
|
4515
|
-
<div style="display:flex;gap:6px">
|
|
4516
|
-
<input type="text" class="hi-answer-input" placeholder="Your answer…" style="flex:1;padding:4px 8px;font-size:12px" />
|
|
4517
|
-
<button class="btn" style="font-size:11px" onclick="submitHumanAnswer('${esc(String(project.path).replace(/['\\]/g,'\\$&'))}','${esc(String(q.org).replace(/['\\]/g,'\\$&'))}','${esc(String(q.role).replace(/['\\]/g,'\\$&'))}','${esc(String(q.questionId).replace(/['\\]/g,'\\$&'))}',this)">Answer</button>
|
|
4518
|
-
</div>`}
|
|
4519
|
-
</div>
|
|
4520
|
-
`).join('')}</div>`;
|
|
4584
|
+
el.innerHTML = `${errHtml}<div class="sess-list">${entries.map(_hiEntryHtml).join('')}</div>`;
|
|
4521
4585
|
}
|
|
4522
4586
|
|
|
4523
4587
|
function updateHumanInputBadge(count) {
|
|
@@ -4525,26 +4589,38 @@ function updateHumanInputBadge(count) {
|
|
|
4525
4589
|
if (b) b.textContent = String(count);
|
|
4526
4590
|
}
|
|
4527
4591
|
|
|
4528
|
-
|
|
4529
|
-
|
|
4530
|
-
const input = row.querySelector('.hi-answer-input');
|
|
4531
|
-
const answer = input.value.trim();
|
|
4532
|
-
if (!answer) return;
|
|
4533
|
-
btnEl.disabled = true;
|
|
4592
|
+
/** POST a human decision; toasts the outcome. Resolves to the response data, or null on failure. */
|
|
4593
|
+
async function _hilPost(url, body) {
|
|
4534
4594
|
try {
|
|
4535
|
-
const res = await fetch(
|
|
4536
|
-
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
4537
|
-
body: JSON.stringify({ dir, org, role, questionId, answer }),
|
|
4595
|
+
const res = await fetch(url, {
|
|
4596
|
+
method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body),
|
|
4538
4597
|
});
|
|
4539
|
-
const data = await res.json();
|
|
4540
|
-
if (data.ok) {
|
|
4541
|
-
|
|
4542
|
-
|
|
4543
|
-
|
|
4544
|
-
|
|
4545
|
-
|
|
4546
|
-
|
|
4547
|
-
|
|
4598
|
+
const data = await res.json().catch(() => ({}));
|
|
4599
|
+
if (!res.ok || !data.ok) { showToast('Not delivered', data.error || `HTTP ${res.status}`, 'err'); return null; }
|
|
4600
|
+
showToast('Done', data.delivery === 'live'
|
|
4601
|
+
? 'Delivered to the running org'
|
|
4602
|
+
: 'Recorded — the org picks it up when it next runs', 'ok');
|
|
4603
|
+
return data;
|
|
4604
|
+
} catch (e) { showToast('Error', e.message, 'err'); return null; }
|
|
4605
|
+
}
|
|
4606
|
+
|
|
4607
|
+
async function _hiAct(btnEl, verb) {
|
|
4608
|
+
const { kind, dir, org, id } = btnEl.dataset;
|
|
4609
|
+
const row = btnEl.closest('.sess-row');
|
|
4610
|
+
const text = row?.querySelector('.hi-answer-input')?.value.trim() || '';
|
|
4611
|
+
const q = `?dir=${enc(dir)}`;
|
|
4612
|
+
let ok = null;
|
|
4613
|
+
row?.querySelectorAll('button').forEach(b => { b.disabled = true; });
|
|
4614
|
+
if (kind === 'question') {
|
|
4615
|
+
if (!text) { row?.querySelectorAll('button').forEach(b => { b.disabled = false; }); return; }
|
|
4616
|
+
ok = await _hilPost('/api/questions/answer', { dir, org, questionId: id, answer: text });
|
|
4617
|
+
} else if (kind === 'approval') {
|
|
4618
|
+
ok = await _hilPost(`/api/org/${enc(org)}/approvals/${enc(id)}${q}`, { action: verb });
|
|
4619
|
+
} else if (kind === 'gate') {
|
|
4620
|
+
ok = await _hilPost(`/api/org/${enc(org)}/gates/${enc(id)}${q}`, { approved: verb === 'approve', resolution: text || undefined });
|
|
4621
|
+
}
|
|
4622
|
+
if (!ok) { row?.querySelectorAll('button').forEach(b => { b.disabled = false; }); return; }
|
|
4623
|
+
renderHumanInputView();
|
|
4548
4624
|
}
|
|
4549
4625
|
|
|
4550
4626
|
function _cvExcerptText(ev) {
|
|
@@ -5909,6 +5985,7 @@ function v2RenderOrgTab(tab) {
|
|
|
5909
5985
|
return;
|
|
5910
5986
|
}
|
|
5911
5987
|
if (tab === 'chart') v2RenderOrgChart();
|
|
5988
|
+
else if (tab === 'runtime') v2RenderOrgRuntime();
|
|
5912
5989
|
else if (tab === 'roles') v2RenderOrgRoles();
|
|
5913
5990
|
else if (tab === 'health') v2RenderOrgHealth();
|
|
5914
5991
|
else if (tab === 'heartbeats') v2RenderOrgHeartbeats();
|
|
@@ -7121,34 +7198,38 @@ function v2RenderOrgLive() {
|
|
|
7121
7198
|
}
|
|
7122
7199
|
|
|
7123
7200
|
// ── APPROVALS ──────────────────────────────────────────────
|
|
7201
|
+
// Tool/action approvals (<org>/approvals.json): a role asked to run a
|
|
7202
|
+
// sensitive tool (Bash, WebFetch, org_complete, policy.approvalTools…).
|
|
7124
7203
|
async function v2RenderOrgApprovals() {
|
|
7125
7204
|
const el = document.getElementById('odt-approvals');
|
|
7126
7205
|
if (!el || !_v2SelOrg) return;
|
|
7127
7206
|
const _rendOrg = _v2SelOrg;
|
|
7128
|
-
el.innerHTML = '<div class="loading-txt">Loading…</div>';
|
|
7207
|
+
if (!el.querySelector('table')) el.innerHTML = '<div class="loading-txt">Loading…</div>';
|
|
7129
7208
|
try {
|
|
7130
7209
|
const _enc = encodeURIComponent(_rendOrg);
|
|
7131
|
-
const
|
|
7210
|
+
const res = await fetch(`/api/org/${_enc}/approvals${DIR ? `?dir=${encodeURIComponent(DIR)}` : ''}`);
|
|
7211
|
+
const data = await res.json().catch(() => ({}));
|
|
7132
7212
|
if (_v2SelOrg !== _rendOrg) return;
|
|
7133
|
-
|
|
7134
|
-
|
|
7213
|
+
if (!res.ok) { el.innerHTML = `<div class="empty">Failed: ${esc(data.error || `HTTP ${res.status}`)}</div>`; return; }
|
|
7214
|
+
const approvals = data.approvals || [];
|
|
7215
|
+
if (!approvals.length) { el.innerHTML = '<div class="empty">No approval requests</div>'; return; }
|
|
7135
7216
|
el.innerHTML = `<table style="width:100%;border-collapse:collapse;font-size:12px">
|
|
7136
7217
|
<thead><tr style="color:var(--text-xs);text-align:left">
|
|
7137
|
-
<th style="padding:6px 8px">
|
|
7218
|
+
<th style="padding:6px 8px">Role</th><th>Action</th><th>Input</th><th>Status</th><th>When</th><th></th>
|
|
7138
7219
|
</tr></thead><tbody>` +
|
|
7139
|
-
approvals.slice(0,
|
|
7140
|
-
const cls = a.status === 'approved' ? 'on' : a.status === '
|
|
7141
|
-
const
|
|
7142
|
-
const aid = a.id || '';
|
|
7220
|
+
approvals.slice(0, 100).map(a => {
|
|
7221
|
+
const cls = a.status === 'approved' ? 'on' : a.status === 'denied' ? 'warn' : '';
|
|
7222
|
+
const input = a.input ? JSON.stringify(a.input) : '';
|
|
7143
7223
|
return `<tr style="border-top:1px solid var(--border)">
|
|
7144
|
-
<td style="padding:6px 8px;color:var(--text-hi)">${esc(a.
|
|
7145
|
-
<td style="padding:6px 8px;color:var(--text-
|
|
7146
|
-
<td style="padding:6px 8px"
|
|
7147
|
-
<td style="padding:6px 8px
|
|
7224
|
+
<td style="padding:6px 8px;color:var(--text-hi)">${esc(a.roleId || '—')}</td>
|
|
7225
|
+
<td style="padding:6px 8px;color:var(--text-hi);font-family:var(--mono)">${esc(a.action || '—')}</td>
|
|
7226
|
+
<td style="padding:6px 8px;color:var(--text-lo);max-width:260px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-family:var(--mono);font-size:11px" title="${esc(input)}">${esc(input.slice(0, 120) || '—')}</td>
|
|
7227
|
+
<td style="padding:6px 8px"><span class="ss-pill ${cls}">${esc(a.status)}</span>${a.resolvedBy ? `<div style="color:var(--text-xs);font-size:10px">by ${esc(a.resolvedBy)}</div>` : ''}</td>
|
|
7228
|
+
<td style="padding:6px 8px;color:var(--text-xs);font-size:11px;font-family:var(--mono)">${relTime(a.resolvedAt || a.ts)}</td>
|
|
7148
7229
|
<td style="padding:6px 8px;white-space:nowrap">
|
|
7149
|
-
${pending
|
|
7150
|
-
? `<button class="btn" style="font-size:10px;color:var(--green);border-color:var(--green)" title="Approve" aria-label="Approve" data-aid="${esc(
|
|
7151
|
-
<button class="btn" style="font-size:10px;color:var(--red);border-color:var(--red);margin-left:3px" title="
|
|
7230
|
+
${a.status === 'pending' && a.id
|
|
7231
|
+
? `<button class="btn" style="font-size:10px;color:var(--green);border-color:var(--green)" title="Approve" aria-label="Approve" data-aid="${esc(a.id)}" onclick="orgApprovalAction(this.dataset.aid,'approve')">✓</button>
|
|
7232
|
+
<button class="btn" style="font-size:10px;color:var(--red);border-color:var(--red);margin-left:3px" title="Deny" aria-label="Deny" data-aid="${esc(a.id)}" onclick="orgApprovalAction(this.dataset.aid,'reject')">✕</button>`
|
|
7152
7233
|
: ''}
|
|
7153
7234
|
</td>
|
|
7154
7235
|
</tr>`;
|
|
@@ -7157,15 +7238,10 @@ async function v2RenderOrgApprovals() {
|
|
|
7157
7238
|
}
|
|
7158
7239
|
|
|
7159
7240
|
async function _orgApprovalAction(id, action) {
|
|
7160
|
-
if (!confirm(`${action} this request?`)) return;
|
|
7161
|
-
|
|
7162
|
-
|
|
7163
|
-
|
|
7164
|
-
method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action })
|
|
7165
|
-
});
|
|
7166
|
-
showToast('Done', `${action}d`, 'ok');
|
|
7167
|
-
v2RenderOrgApprovals();
|
|
7168
|
-
} catch (e) { showToast('Error', e.message, 'err'); }
|
|
7241
|
+
if (!confirm(`${action === 'approve' ? 'Approve' : 'Deny'} this request?`)) return;
|
|
7242
|
+
const _enc = encodeURIComponent(_v2SelOrg);
|
|
7243
|
+
const ok = await _hilPost(`/api/org/${_enc}/approvals/${encodeURIComponent(id)}${DIR ? `?dir=${encodeURIComponent(DIR)}` : ''}`, { action });
|
|
7244
|
+
if (ok) v2RenderOrgApprovals();
|
|
7169
7245
|
}
|
|
7170
7246
|
|
|
7171
7247
|
// ── DECISIONS ──────────────────────────────────────────────
|
|
@@ -7210,20 +7286,35 @@ function v2RenderDecisionsTable() {
|
|
|
7210
7286
|
if (!filtered.length) { el.innerHTML = `${filterBar}<div class="empty">No decision gates match</div>`; return; }
|
|
7211
7287
|
el.innerHTML = filterBar + `<table style="width:100%;border-collapse:collapse;font-size:12px">
|
|
7212
7288
|
<thead><tr style="color:var(--text-xs);text-align:left">
|
|
7213
|
-
<th style="padding:6px 8px">Role</th><th>Gate</th><th>Status</th><th>Resolution</th><th>Date</th>
|
|
7289
|
+
<th style="padding:6px 8px">Role</th><th>Gate</th><th>Status</th><th>Resolution</th><th>Date</th><th></th>
|
|
7214
7290
|
</tr></thead><tbody>` +
|
|
7215
7291
|
filtered.slice(0, 100).map(g => {
|
|
7216
7292
|
const cls = g.status === 'approved' ? 'on' : g.status === 'rejected' ? 'warn' : '';
|
|
7217
7293
|
return `<tr style="border-top:1px solid var(--border)">
|
|
7218
7294
|
<td style="padding:6px 8px;color:var(--text-hi)">${esc(g.roleId || '—')}</td>
|
|
7219
7295
|
<td style="padding:6px 8px;color:var(--text-lo);max-width:200px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap" title="${esc(g.description || '')}">${esc(g.name || '—')}</td>
|
|
7220
|
-
<td style="padding:6px 8px"><span class="ss-pill ${cls}">${esc(g.status || 'pending')}</span
|
|
7221
|
-
<td style="padding:6px 8px;color:var(--text-lo);max-width:180px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">${esc(g.resolution || '—')}</td>
|
|
7296
|
+
<td style="padding:6px 8px"><span class="ss-pill ${cls}">${esc(g.status || 'pending')}</span>${g.resolvedBy ? `<div style="color:var(--text-xs);font-size:10px">by ${esc(g.resolvedBy)}</div>` : ''}</td>
|
|
7297
|
+
<td style="padding:6px 8px;color:var(--text-lo);max-width:180px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap" title="${esc(g.resolution || '')}">${esc(g.resolution || '—')}</td>
|
|
7222
7298
|
<td style="padding:6px 8px;color:var(--text-xs);font-size:11px;font-family:var(--mono)">${relTime(g.resolvedAt || g.createdAt)}</td>
|
|
7299
|
+
<td style="padding:6px 8px;white-space:nowrap">
|
|
7300
|
+
${(g.status || 'pending') === 'pending'
|
|
7301
|
+
? `<button class="btn" style="font-size:10px;color:var(--green);border-color:var(--green)" title="Approve" aria-label="Approve gate" data-gid="${esc(g.id)}" onclick="orgGateAction(this.dataset.gid,true)">✓</button>
|
|
7302
|
+
<button class="btn" style="font-size:10px;color:var(--red);border-color:var(--red);margin-left:3px" title="Reject" aria-label="Reject gate" data-gid="${esc(g.id)}" onclick="orgGateAction(this.dataset.gid,false)">✕</button>`
|
|
7303
|
+
: ''}
|
|
7304
|
+
</td>
|
|
7223
7305
|
</tr>`;
|
|
7224
7306
|
}).join('') + '</tbody></table>';
|
|
7225
7307
|
}
|
|
7226
7308
|
|
|
7309
|
+
async function _orgGateAction(id, approved) {
|
|
7310
|
+
const gate = _v2DecisionsCache.find(g => g.id === id);
|
|
7311
|
+
const resolution = prompt(`${approved ? 'Approve' : 'Reject'} "${gate?.name || id}"?\nOptional note for the role:`, '');
|
|
7312
|
+
if (resolution === null) return;
|
|
7313
|
+
const _enc = encodeURIComponent(_v2SelOrg);
|
|
7314
|
+
const ok = await _hilPost(`/api/org/${_enc}/gates/${encodeURIComponent(id)}${DIR ? `?dir=${encodeURIComponent(DIR)}` : ''}`, { approved, resolution: resolution.trim() || undefined });
|
|
7315
|
+
if (ok) v2RenderOrgDecisions();
|
|
7316
|
+
}
|
|
7317
|
+
|
|
7227
7318
|
// ── SECRETS ────────────────────────────────────────────────
|
|
7228
7319
|
async function v2RenderOrgSecrets() {
|
|
7229
7320
|
const el = document.getElementById('odt-secrets');
|
|
@@ -7941,7 +8032,10 @@ window.odtSendChatMessage = async () => {
|
|
|
7941
8032
|
const _sendQ = DIR ? `?dir=${encodeURIComponent(DIR)}` : '';
|
|
7942
8033
|
const res = await fetch(`/api/orgs/${encodeURIComponent(_v2SelOrg)}/chat${_sendQ}`, { method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify({ text, role: role || undefined, runId: _odtChatCurrentId || null }) });
|
|
7943
8034
|
const data = await res.json().catch(() => ({}));
|
|
7944
|
-
if (!data.
|
|
8035
|
+
if (!res.ok || !data.ok) {
|
|
8036
|
+
inp.value = text;
|
|
8037
|
+
showToast('Not delivered', data.error || `HTTP ${res.status}`, 'err');
|
|
8038
|
+
} else if (data.delivery !== 'live') {
|
|
7945
8039
|
const lbl = document.getElementById('odt-chat-live-lbl');
|
|
7946
8040
|
if (lbl) { const prev = lbl.textContent; lbl.textContent = 'QUEUED (org offline)'; setTimeout(() => { lbl.textContent = prev; }, 4000); }
|
|
7947
8041
|
}
|
|
@@ -8491,8 +8585,16 @@ function _odtHandleLiveEvent(ev) {
|
|
|
8491
8585
|
}
|
|
8492
8586
|
if (ev?.type === 'org:question' && ev.from) {
|
|
8493
8587
|
if (ev.org === _v2SelOrg) v2ShowQuestionBubble(ev.from, ev.question || '');
|
|
8494
|
-
|
|
8495
|
-
|
|
8588
|
+
}
|
|
8589
|
+
_v2RuntimeNudge(ev);
|
|
8590
|
+
// Anything a human must act on, or just did: refresh the Human Input list,
|
|
8591
|
+
// its badge, and the selected org's approvals/decisions panes.
|
|
8592
|
+
if (['org:question', 'org:gate', 'org:question-answered', 'org:approval:resolved', 'org:gate:resolved'].includes(ev?.type)) {
|
|
8593
|
+
_hiScheduleReload();
|
|
8594
|
+
if (ev.org === _v2SelOrg) {
|
|
8595
|
+
if (document.getElementById('odt-approvals')?.offsetParent) v2RenderOrgApprovals();
|
|
8596
|
+
if (document.getElementById('odt-decisions')?.offsetParent) v2RenderOrgDecisions();
|
|
8597
|
+
}
|
|
8496
8598
|
}
|
|
8497
8599
|
if (ev?.type === 'org:question-answered' && ev.role) {
|
|
8498
8600
|
if (ev.org === _v2SelOrg) v2ClearQuestionBubble(ev.role);
|
|
@@ -8603,90 +8705,273 @@ function _odtHandleLiveEvent(ev) {
|
|
|
8603
8705
|
}
|
|
8604
8706
|
}
|
|
8605
8707
|
|
|
8708
|
+
// ── RUNTIME ────────────────────────────────────────────────
|
|
8709
|
+
// What the org is doing, from the runtime's own sources (GET /api/org/:n/runtime):
|
|
8710
|
+
// live status, idle watchdog + hold deadline, budget on its enforcement basis,
|
|
8711
|
+
// per-role model/effort/tier and usage (cache tokens included), the task DAG
|
|
8712
|
+
// with evidence-gate failures, gate/watchdog audit events, and how runs ended.
|
|
8713
|
+
let _v2RuntimeTimer = null;
|
|
8714
|
+
let _v2RuntimeReload = null;
|
|
8715
|
+
|
|
8716
|
+
function _rtTok(n) {
|
|
8717
|
+
if (n === null || n === undefined) return '?';
|
|
8718
|
+
n = Number(n) || 0;
|
|
8719
|
+
if (n >= 1e9) return `${(n / 1e9).toFixed(2)}B`;
|
|
8720
|
+
if (n >= 1e6) return `${(n / 1e6).toFixed(1)}M`;
|
|
8721
|
+
if (n >= 1e3) return `${(n / 1e3).toFixed(1)}k`;
|
|
8722
|
+
return String(n);
|
|
8723
|
+
}
|
|
8724
|
+
function _rtClock(iso) {
|
|
8725
|
+
const t = typeof iso === 'number' ? iso : Date.parse(iso || '');
|
|
8726
|
+
return t ? new Date(t).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }) : '—';
|
|
8727
|
+
}
|
|
8728
|
+
function _rtIn(iso) {
|
|
8729
|
+
const ms = (typeof iso === 'number' ? iso : Date.parse(iso || '')) - Date.now();
|
|
8730
|
+
if (!Number.isFinite(ms)) return '';
|
|
8731
|
+
return ms <= 0 ? 'now' : `in ${fmtDur(ms)}`;
|
|
8732
|
+
}
|
|
8733
|
+
const _RT_CLOSED_BY = {
|
|
8734
|
+
'org-complete': 'boss called org_complete',
|
|
8735
|
+
'idle-stop': 'idle watchdog stopped it',
|
|
8736
|
+
'failed-start': 'failed to start',
|
|
8737
|
+
'scheduled-deadline': 'scheduled deadline',
|
|
8738
|
+
'boss-restart': 'boss restarted',
|
|
8739
|
+
'boss-restart-exhausted': 'boss restarts exhausted',
|
|
8740
|
+
'mark-complete': 'marked complete by hand',
|
|
8741
|
+
};
|
|
8742
|
+
const _RT_AUDIT_COLOR = {
|
|
8743
|
+
'task-evidence-refused': 'var(--amber, #d29922)',
|
|
8744
|
+
'task-evidence-escalated': 'var(--red)',
|
|
8745
|
+
'org-complete-refused': 'var(--amber, #d29922)',
|
|
8746
|
+
'hold-expired': 'var(--amber, #d29922)',
|
|
8747
|
+
'no-progress': 'var(--amber, #d29922)',
|
|
8748
|
+
'idle-nudge': 'var(--text-lo)',
|
|
8749
|
+
'idle-stop': 'var(--red)',
|
|
8750
|
+
};
|
|
8751
|
+
|
|
8752
|
+
function _rtCard(label, value, sub) {
|
|
8753
|
+
return `<div style="border:1px solid var(--border);border-radius:6px;padding:10px 12px;min-width:0">
|
|
8754
|
+
<div style="font-size:9px;letter-spacing:1.5px;color:var(--text-xs)">${label}</div>
|
|
8755
|
+
<div style="font-size:14px;color:var(--text-hi);margin-top:4px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">${value}</div>
|
|
8756
|
+
${sub ? `<div style="font-size:11px;color:var(--text-lo);margin-top:2px">${sub}</div>` : ''}
|
|
8757
|
+
</div>`;
|
|
8758
|
+
}
|
|
8759
|
+
function _rtTable(title, head, rows, empty) {
|
|
8760
|
+
const h = `<div style="font-size:9px;letter-spacing:2px;color:var(--text-xs);margin:18px 0 6px">${title}</div>`;
|
|
8761
|
+
if (!rows.length) return `${h}<div class="empty" style="padding:8px 0">${empty}</div>`;
|
|
8762
|
+
return `${h}<div style="overflow-x:auto"><table style="width:100%;border-collapse:collapse;font-size:12px">
|
|
8763
|
+
<thead><tr style="color:var(--text-xs);text-align:left">${head.map(c => `<th style="padding:4px 8px;font-weight:500;white-space:nowrap">${c}</th>`).join('')}</tr></thead>
|
|
8764
|
+
<tbody>${rows.map(r => `<tr style="border-top:1px solid var(--border)">${r.map(c => `<td style="padding:5px 8px;vertical-align:top">${c}</td>`).join('')}</tr>`).join('')}</tbody></table></div>`;
|
|
8765
|
+
}
|
|
8766
|
+
|
|
8767
|
+
async function v2RenderOrgRuntime() {
|
|
8768
|
+
const el = document.getElementById('odt-runtime');
|
|
8769
|
+
if (!el || !_v2SelOrg) return;
|
|
8770
|
+
clearTimeout(_v2RuntimeTimer);
|
|
8771
|
+
const _rendOrg = _v2SelOrg;
|
|
8772
|
+
if (!el.dataset.org || el.dataset.org !== _rendOrg) el.innerHTML = '<div class="loading-txt">Loading…</div>';
|
|
8773
|
+
let v;
|
|
8774
|
+
try {
|
|
8775
|
+
const res = await fetch(`/api/org/${enc(_rendOrg)}/runtime${DIR ? `?dir=${enc(DIR)}` : ''}`);
|
|
8776
|
+
v = await res.json().catch(() => ({}));
|
|
8777
|
+
if (!res.ok) throw new Error(v.error || `HTTP ${res.status}`);
|
|
8778
|
+
} catch (e) {
|
|
8779
|
+
if (_v2SelOrg === _rendOrg) el.innerHTML = `<div class="empty">Failed: ${esc(e.message)}</div>`;
|
|
8780
|
+
return;
|
|
8781
|
+
}
|
|
8782
|
+
if (_v2SelOrg !== _rendOrg || _v2OrgTab !== 'runtime') return;
|
|
8783
|
+
el.dataset.org = _rendOrg;
|
|
8784
|
+
const s = v.settings || {};
|
|
8785
|
+
const mono = t => `<span style="font-family:var(--mono)">${t}</span>`;
|
|
8786
|
+
|
|
8787
|
+
// Status strip
|
|
8788
|
+
const statusSub = v.live
|
|
8789
|
+
? `${esc(v.run || '')}${v.startedAt ? ` · up ${fmtDur(Date.now() - v.startedAt)}` : ''}`
|
|
8790
|
+
: `${esc(v.run || 'no run yet')}${v.closedBy ? ` · ${esc(_RT_CLOSED_BY[v.closedBy] || v.closedBy)}` : ''}`;
|
|
8791
|
+
let idle = '—', idleSub = s.idle_minutes === 0 ? 'watchdog disabled' : `window ${s.idle_minutes}m`;
|
|
8792
|
+
if (v.idle) {
|
|
8793
|
+
if (v.idle.idle_hold && v.idle.idle_hold !== 'unknown') {
|
|
8794
|
+
idle = `held: ${esc(v.idle.idle_hold)}`;
|
|
8795
|
+
idleSub = v.idle.idle_hold_until ? `hold ends ${_rtClock(v.idle.idle_hold_until)} (${_rtIn(v.idle.idle_hold_until)})` : 'hold has no deadline';
|
|
8796
|
+
} else if (v.idle.idle_stop_at) {
|
|
8797
|
+
idle = `stops ${_rtIn(v.idle.idle_stop_at)}`;
|
|
8798
|
+
idleSub = `if idle until ${_rtClock(v.idle.idle_stop_at)}`;
|
|
8799
|
+
} else idle = 'not reported yet';
|
|
8800
|
+
}
|
|
8801
|
+
const b = v.budget || {};
|
|
8802
|
+
const pct = b.tokens && b.used !== null ? Math.min(100, (b.used / b.tokens) * 100) : null;
|
|
8803
|
+
const budget = b.used === null ? 'unknown' : `${_rtTok(b.used)} / ${_rtTok(b.tokens)}`;
|
|
8804
|
+
const budgetWhy = b.used === null ? ' · run predates cache metering' : '';
|
|
8805
|
+
const budgetSub = `${esc(b.basis)} basis${pct !== null ? ` · ${pct.toFixed(1)}%` : ''}${budgetWhy} · ${fmtCost(v.totals?.cost_usd)}`;
|
|
8806
|
+
const gate = s.completion_evidence ? 'evidence required' : 'off';
|
|
8807
|
+
const gateSub = s.completion_evidence
|
|
8808
|
+
? `escalate after ${s.max_evidence_attempts} refusals · org_complete: ${esc(s.completion)}`
|
|
8809
|
+
: `org_complete gate: ${esc(s.completion)}`;
|
|
8810
|
+
|
|
8811
|
+
let html = '<div style="padding:16px">';
|
|
8812
|
+
if (!v.valid) html += `<div class="empty" style="color:var(--red);text-align:left;margin-bottom:10px">Definition does not parse — <code>org run</code> will refuse it:<br>${v.problems.map(esc).join('<br>')}</div>`;
|
|
8813
|
+
if (v.liveError) html += `<div class="empty" style="color:var(--amber, #d29922);text-align:left;margin-bottom:10px">A daemon is registered for this org but did not answer: ${esc(v.liveError)}</div>`;
|
|
8814
|
+
html += `<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(170px,1fr));gap:10px">
|
|
8815
|
+
${_rtCard('STATUS', `<span class="ss-pill ${v.live ? 'on' : ''}">${esc(v.status)}</span>`, statusSub)}
|
|
8816
|
+
${_rtCard('IDLE WATCHDOG', idle, idleSub)}
|
|
8817
|
+
${_rtCard('BUDGET (THIS RUN)', budget, budgetSub)}
|
|
8818
|
+
${_rtCard('COMPLETION GATE', gate, gateSub)}
|
|
8819
|
+
${_rtCard('SESSIONS', `per ${esc(s.session_scope)}`, `workspace: ${esc(s.workspace)} · max ${s.max_concurrent_agents} agents`)}
|
|
8820
|
+
</div>`;
|
|
8821
|
+
|
|
8822
|
+
// Roles
|
|
8823
|
+
html += _rtTable(`ROLES${v.run ? ` — USAGE IN ${esc(v.run)}` : ''}`,
|
|
8824
|
+
['Role', 'Runtime', 'Model', 'Effort', 'Tier', 'Status', 'In', 'Out', 'Cache read', 'Cache write', 'Cost', 'Last active'],
|
|
8825
|
+
(v.roles || []).map(r => [
|
|
8826
|
+
`<span style="color:var(--text-hi)">${esc(r.title)}</span>${r.title !== r.id ? `<div style="font-size:10px;color:var(--text-xs)">${esc(r.id)}</div>` : ''}`,
|
|
8827
|
+
esc(r.runtime),
|
|
8828
|
+
r.tierError
|
|
8829
|
+
? `<span style="color:var(--red)" title="${esc(r.tierError)}">tier error</span>`
|
|
8830
|
+
: `<span style="white-space:nowrap">${mono(esc(r.model || 'default'))}</span><div style="font-size:10px;color:var(--text-xs)">${esc(r.modelSource)}</div>`,
|
|
8831
|
+
esc(r.effort || '—'),
|
|
8832
|
+
esc(r.tier || '—'),
|
|
8833
|
+
`<span class="ss-pill ${r.status === 'running' || r.status === 'working' ? 'on' : ''}">${esc(r.status)}</span>`,
|
|
8834
|
+
// A run recorded before the four-way meter only has an input+output total.
|
|
8835
|
+
...(r.usage.legacy_tokens && !r.usage.tokens
|
|
8836
|
+
? [`<span title="Recorded before the meter split input, output and cache tokens">${mono(_rtTok(r.usage.legacy_tokens))} in+out</span>`, '—', '—', '—']
|
|
8837
|
+
: [mono(_rtTok(r.usage.tokens_in)), mono(_rtTok(r.usage.tokens_out)),
|
|
8838
|
+
mono(_rtTok(r.usage.cache_read)), mono(_rtTok(r.usage.cache_creation))]),
|
|
8839
|
+
mono(fmtCost(r.usage.cost_usd)),
|
|
8840
|
+
`<span style="color:var(--text-xs)">${r.lastActivity ? relTime(r.lastActivity) : '—'}</span>`,
|
|
8841
|
+
]), 'No roles');
|
|
8842
|
+
|
|
8843
|
+
// Tasks
|
|
8844
|
+
const open = (v.tasks || []).filter(t => !['done', 'cancelled', 'merged', 'split'].includes(t.status));
|
|
8845
|
+
html += _rtTable(`TASKS (${open.length} open of ${(v.tasks || []).length})`,
|
|
8846
|
+
['Task', 'Assignee', 'Status', 'Evidence refusals', 'Loadout', 'Blocked'],
|
|
8847
|
+
[...open, ...(v.tasks || []).filter(t => !open.includes(t))].slice(0, 60).map(t => [
|
|
8848
|
+
`<span style="color:var(--text-hi)">${esc(t.title || t.id)}</span><div style="font-size:10px;color:var(--text-xs)">${esc(t.id)}</div>`,
|
|
8849
|
+
esc(t.assignee || '—'),
|
|
8850
|
+
`<span class="ss-pill ${t.status === 'done' ? 'on' : t.status === 'failed' || t.status === 'blocked' ? 'warn' : ''}">${esc(t.status)}</span>`,
|
|
8851
|
+
t.evidenceFailures ? `<span style="color:${t.evidenceFailures >= s.max_evidence_attempts ? 'var(--red)' : 'var(--amber, #d29922)'}">${t.evidenceFailures} / ${s.max_evidence_attempts}</span>` : '—',
|
|
8852
|
+
esc(t.loadout || '—'),
|
|
8853
|
+
t.blockedUntil ? `until ${_rtClock(t.blockedUntil)}${t.blockedReason ? ` — ${esc(t.blockedReason)}` : ''}` : '—',
|
|
8854
|
+
]), 'No task graph for this run (the boss has not used org_task)');
|
|
8855
|
+
|
|
8856
|
+
// Audit trail
|
|
8857
|
+
html += _rtTable('EVIDENCE GATE & WATCHDOG EVENTS', ['When', 'Event', 'Role', 'Detail'],
|
|
8858
|
+
(v.audit || []).map(a => [
|
|
8859
|
+
`<span style="color:var(--text-xs);white-space:nowrap">${_rtClock(a.ts)}</span>`,
|
|
8860
|
+
`<span class="ss-pill" style="color:${_RT_AUDIT_COLOR[a.reason] || 'var(--text-lo)'};border-color:currentColor;white-space:nowrap">${esc(a.reason)}</span>`,
|
|
8861
|
+
esc(a.from || '—'),
|
|
8862
|
+
`<div style="white-space:pre-wrap;max-height:120px;overflow:auto">${esc(a.msg)}${a.data?.refusal ? `\n<span style="color:var(--text-lo)">${esc(a.data.refusal)}</span>` : ''}</div>`,
|
|
8863
|
+
]), 'None in this run');
|
|
8864
|
+
|
|
8865
|
+
// History
|
|
8866
|
+
html += _rtTable('RECENT RUNS', ['Run', 'Ended', 'Duration', 'How it ended', 'Outcome', 'Open tasks at stop', 'Tokens', 'Cost'],
|
|
8867
|
+
(v.history || []).map(h => [
|
|
8868
|
+
mono(esc(h.run)),
|
|
8869
|
+
`<span style="color:var(--text-xs)">${relTime(h.endedAt)}</span>`,
|
|
8870
|
+
h.durationMs ? fmtDur(h.durationMs) : '—',
|
|
8871
|
+
esc(h.closedBy ? (_RT_CLOSED_BY[h.closedBy] || h.closedBy) : 'stopped (manual or shutdown)') + (h.crashes?.length ? `<div style="color:var(--red);font-size:10px">crashed: ${h.crashes.map(esc).join(', ')}</div>` : ''),
|
|
8872
|
+
h.outcome
|
|
8873
|
+
? `<span class="ss-pill ${h.outcome.status === 'achieved' ? 'on' : 'warn'}">${esc(h.outcome.status)}</span>${h.blocker ? ` blocker: ${esc(h.blocker)}` : ''}<div style="font-size:11px;color:var(--text-lo);max-width:320px;white-space:pre-wrap;max-height:80px;overflow:auto">${esc(h.blockerDetail || h.outcome.summary || '')}</div>`
|
|
8874
|
+
: '<span style="color:var(--text-xs)">none claimed</span>',
|
|
8875
|
+
h.runnableTasksAtStop ? `<span style="color:var(--amber, #d29922)">${h.runnableTasksAtStop}</span>` : '0',
|
|
8876
|
+
mono(_rtTok(h.totalTokens)),
|
|
8877
|
+
mono(fmtCost(h.totalCostUsd)),
|
|
8878
|
+
]), 'No finished runs yet');
|
|
8879
|
+
html += '</div>';
|
|
8880
|
+
el.innerHTML = html;
|
|
8881
|
+
// A live run changes by the second; poll lightly on top of the SSE nudges.
|
|
8882
|
+
if (v.live) _v2RuntimeTimer = setTimeout(() => { if (_v2OrgTab === 'runtime') v2RenderOrgRuntime(); }, 15000);
|
|
8883
|
+
}
|
|
8884
|
+
|
|
8885
|
+
/** SSE nudge: refresh the Runtime pane shortly after an event for its org. */
|
|
8886
|
+
function _v2RuntimeNudge(ev) {
|
|
8887
|
+
if (_v2OrgTab !== 'runtime' || !ev?.org || ev.org !== _v2SelOrg) return;
|
|
8888
|
+
clearTimeout(_v2RuntimeReload);
|
|
8889
|
+
_v2RuntimeReload = setTimeout(v2RenderOrgRuntime, 2000);
|
|
8890
|
+
}
|
|
8891
|
+
|
|
8606
8892
|
// ── ORG CONFIG ─────────────────────────────────────────────
|
|
8893
|
+
// Edits the Org Runtime v2 definition (goal, schedule, run_config, default
|
|
8894
|
+
// cost tier). Saved through POST /api/org/:n/config, which merges into the file
|
|
8895
|
+
// on disk and refuses anything `org run` would refuse. Roles, policies and
|
|
8896
|
+
// providers stay in the JSON (Files tab / `monomind org edit`).
|
|
8607
8897
|
function v2RenderOrgConfig() {
|
|
8608
8898
|
const el = document.getElementById('odt-config');
|
|
8609
8899
|
if (!el || !_v2OrgData || !_v2SelOrg) return;
|
|
8610
|
-
const d = _v2OrgData;
|
|
8900
|
+
const d = _v2OrgData.config || _v2OrgData;
|
|
8611
8901
|
const rc = d.run_config || {};
|
|
8612
|
-
const
|
|
8613
|
-
const
|
|
8614
|
-
const
|
|
8615
|
-
const
|
|
8616
|
-
const
|
|
8617
|
-
|
|
8618
|
-
|
|
8619
|
-
|
|
8620
|
-
|
|
8621
|
-
|
|
8622
|
-
+sec('
|
|
8623
|
-
fld('
|
|
8624
|
-
+fld('
|
|
8625
|
-
+
|
|
8626
|
-
+fld('
|
|
8627
|
-
|
|
8628
|
-
|
|
8629
|
-
|
|
8630
|
-
|
|
8631
|
-
|
|
8632
|
-
|
|
8633
|
-
+fld('
|
|
8634
|
-
+fld('
|
|
8635
|
-
+fld('
|
|
8636
|
-
|
|
8637
|
-
|
|
8638
|
-
)
|
|
8639
|
-
+
|
|
8640
|
-
fld('Approval Policy', sel('oc-gov-policy', govPolicies, gov.policy||'auto'))
|
|
8641
|
-
+fld('Approvals File', inp('oc-gov-file', gov.approvals_file||(`.monomind/orgs/${d.name}-approvals.json`)))
|
|
8642
|
-
)
|
|
8643
|
-
+'<div style="display:flex;align-items:center;gap:10px"><button class="btn" style="border-color:var(--accent);color:var(--accent)" onclick="v2SaveOrgConfig()">Save Config</button><span id="oc-status-msg" style="font-size:11px;color:var(--text-lo)"></span></div>'
|
|
8644
|
-
+'</div>';
|
|
8902
|
+
const inp = (id, val, type, extra) => `<input class="filter-input" id="${id}" type="${type || 'text'}" value="${esc(String(val ?? ''))}" ${extra || ''} style="width:100%;box-sizing:border-box">`;
|
|
8903
|
+
const sel = (id, opts, cur) => `<select class="filter-input" id="${id}" style="width:100%;box-sizing:border-box;cursor:pointer">${opts.map(([v, l]) => `<option value="${esc(v)}"${String(cur ?? '') === v ? ' selected' : ''}>${esc(l)}</option>`).join('')}</select>`;
|
|
8904
|
+
const fld = (lbl, html, hint) => `<div style="display:flex;flex-direction:column;gap:4px"><div style="font-size:10px;color:var(--text-lo)">${lbl}</div>${html}${hint ? `<div style="font-size:10px;color:var(--text-xs)">${hint}</div>` : ''}</div>`;
|
|
8905
|
+
const sec = (title, inner) => `<div style="margin-bottom:22px"><div style="font-size:9px;letter-spacing:2px;color:var(--text-xs);margin-bottom:10px;padding-bottom:5px;border-bottom:1px solid var(--border)">${title}</div><div style="display:grid;grid-template-columns:1fr 1fr;gap:11px">${inner}</div></div>`;
|
|
8906
|
+
const tiers = ['standard', 'economy', 'budget', ...Object.keys(d.cost_tiers?.tiers || {})];
|
|
8907
|
+
el.innerHTML = '<div style="padding:18px;overflow-y:auto;max-width:720px">'
|
|
8908
|
+
+ sec('GENERAL',
|
|
8909
|
+
fld('Name', inp('oc-name', d.name || '', 'text', 'readonly style="opacity:0.5"'))
|
|
8910
|
+
+ fld('Schedule', inp('oc-schedule', d.schedule ?? '', 'text', 'placeholder="cron, or empty for none"'))
|
|
8911
|
+
+ `<div style="grid-column:1/-1">${fld('Goal', `<textarea id="oc-goal" class="filter-input" rows="4" style="width:100%;box-sizing:border-box;resize:vertical;line-height:1.5">${esc(d.goal || '')}</textarea>`)}</div>`)
|
|
8912
|
+
+ sec('BUDGET & CONCURRENCY',
|
|
8913
|
+
fld('Budget tokens (per run)', inp('oc-budget', rc.budget_tokens ?? '', 'number', 'min="1" placeholder="1000000"'), 'Must be positive.')
|
|
8914
|
+
+ fld('Budget basis', sel('oc-basis', [['', 'uncached (default)'], ['uncached', 'uncached — input + output'], ['billable', 'billable — includes cache tokens']], rc.budget_tokens_basis ?? ''), 'billable is ~100x larger on a cached run; resize the budget with it.')
|
|
8915
|
+
+ fld('Max concurrent agents', inp('oc-max-agents', rc.max_concurrent_agents ?? '', 'number', 'min="1" placeholder="4"'))
|
|
8916
|
+
+ fld('Max turns per message', inp('oc-max-turns', rc.max_turns_per_message ?? '', 'number', 'min="1"')))
|
|
8917
|
+
+ sec('COMPLETION',
|
|
8918
|
+
fld('org_complete gate', sel('oc-completion', [['', 'boss (default)'], ['boss', 'boss — partial needs a real blocker'], ['dag', 'dag — also refuse while tasks are runnable']], rc.completion ?? ''))
|
|
8919
|
+
+ fld('Evidence gate on org_task_done', sel('oc-evidence', [['false', 'off'], ['true', 'on — commands + exit codes pinned to HEAD']], String(!!rc.completion_evidence)))
|
|
8920
|
+
+ fld('Escalate after N evidence refusals', inp('oc-evidence-max', rc.max_evidence_attempts ?? '', 'number', 'min="1" placeholder="3"'), 'Then the task is failed and handed to the boss.'))
|
|
8921
|
+
+ sec('SESSIONS & WATCHDOG',
|
|
8922
|
+
fld('Idle minutes (0 disables)', inp('oc-idle', rc.idle_minutes ?? '', 'number', 'min="0" step="any" placeholder="10"'), 'Every hold on the idle clock expires after an hour.')
|
|
8923
|
+
+ fld('Session scope', sel('oc-scope', [['', 'role (default)'], ['role', 'role — one session per role'], ['task', 'task — one session per task']], rc.session_scope ?? ''))
|
|
8924
|
+
+ fld('Workspace', inp('oc-workspace', rc.workspace ?? '', 'text', 'placeholder="repo | isolated | worktree | worktree-per-role | /abs/path"'))
|
|
8925
|
+
+ fld('Memory namespace', inp('oc-namespace', rc.memory_namespace ?? '', 'text', `placeholder="org:${esc(d.name || '')}"`)))
|
|
8926
|
+
+ sec('COST TIERS',
|
|
8927
|
+
fld('Default tier', sel('oc-tier', [['', 'none — models as configured'], ...[...new Set(tiers)].map(t => [t, t])], d.cost_tiers?.default ?? ''), 'Per-role tiers and exemptions: edit cost_tiers.roles in the JSON.'))
|
|
8928
|
+
+ '<div style="display:flex;align-items:flex-start;gap:10px"><button class="btn" style="border-color:var(--accent);color:var(--accent)" onclick="v2SaveOrgConfig()">Save Config</button><span id="oc-status-msg" style="font-size:11px;color:var(--text-lo);white-space:pre-wrap"></span></div>'
|
|
8929
|
+
+ '</div>';
|
|
8645
8930
|
}
|
|
8646
8931
|
|
|
8647
8932
|
async function _v2SaveOrgConfig() {
|
|
8648
8933
|
if (!_v2SelOrg || !_v2OrgData) return;
|
|
8649
8934
|
const g = id => document.getElementById(id);
|
|
8650
|
-
//
|
|
8651
|
-
|
|
8652
|
-
const
|
|
8653
|
-
const
|
|
8654
|
-
|
|
8655
|
-
|
|
8656
|
-
|
|
8657
|
-
|
|
8658
|
-
|
|
8659
|
-
|
|
8660
|
-
|
|
8661
|
-
|
|
8662
|
-
|
|
8663
|
-
|
|
8664
|
-
max_concurrent_agents:
|
|
8665
|
-
|
|
8666
|
-
|
|
8667
|
-
|
|
8668
|
-
|
|
8669
|
-
|
|
8670
|
-
|
|
8671
|
-
|
|
8672
|
-
|
|
8673
|
-
policy: (g('oc-gov-policy')?.value)||'auto',
|
|
8674
|
-
approvals_file: (g('oc-gov-file')?.value||'').trim()||(`.monomind/orgs/${_v2SelOrg}-approvals.json`),
|
|
8935
|
+
// '' = not set → null removes the key so the runtime default applies.
|
|
8936
|
+
const num = id => { const v = (g(id)?.value || '').trim(); return v === '' ? null : Number(v); };
|
|
8937
|
+
const str = id => { const v = (g(id)?.value || '').trim(); return v === '' ? null : v; };
|
|
8938
|
+
const patch = {
|
|
8939
|
+
goal: g('oc-goal')?.value ?? '',
|
|
8940
|
+
// A numeric schedule (an interval) stays a number; a cron stays a string.
|
|
8941
|
+
schedule: (() => {
|
|
8942
|
+
const v = str('oc-schedule');
|
|
8943
|
+
const was = (_v2OrgData.config || _v2OrgData).schedule;
|
|
8944
|
+
return v !== null && typeof was === 'number' && /^\d+$/.test(v) ? Number(v) : v;
|
|
8945
|
+
})(),
|
|
8946
|
+
run_config: {
|
|
8947
|
+
budget_tokens: num('oc-budget'),
|
|
8948
|
+
budget_tokens_basis: str('oc-basis'),
|
|
8949
|
+
max_concurrent_agents: num('oc-max-agents'),
|
|
8950
|
+
max_turns_per_message: num('oc-max-turns'),
|
|
8951
|
+
completion: str('oc-completion'),
|
|
8952
|
+
completion_evidence: g('oc-evidence')?.value === 'true' ? true : null,
|
|
8953
|
+
max_evidence_attempts: num('oc-evidence-max'),
|
|
8954
|
+
idle_minutes: num('oc-idle'),
|
|
8955
|
+
session_scope: str('oc-scope'),
|
|
8956
|
+
workspace: str('oc-workspace'),
|
|
8957
|
+
memory_namespace: str('oc-namespace'),
|
|
8675
8958
|
},
|
|
8959
|
+
cost_tiers_default: str('oc-tier') || '',
|
|
8676
8960
|
};
|
|
8677
|
-
const msg =
|
|
8678
|
-
if (msg) { msg.style.color='var(--text-lo)'; msg.textContent='Saving
|
|
8961
|
+
const msg = g('oc-status-msg');
|
|
8962
|
+
if (msg) { msg.style.color = 'var(--text-lo)'; msg.textContent = 'Saving…'; }
|
|
8679
8963
|
try {
|
|
8680
|
-
const
|
|
8681
|
-
|
|
8682
|
-
|
|
8683
|
-
|
|
8684
|
-
|
|
8685
|
-
|
|
8686
|
-
|
|
8687
|
-
|
|
8688
|
-
|
|
8689
|
-
|
|
8964
|
+
const r = await fetch(`/api/org/${enc(_v2SelOrg)}/config${DIR ? `?dir=${enc(DIR)}` : ''}`, {
|
|
8965
|
+
method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(patch),
|
|
8966
|
+
});
|
|
8967
|
+
const json = await r.json().catch(() => ({}));
|
|
8968
|
+
if (!r.ok || !json.ok) throw new Error((json.error || `HTTP ${r.status}`).split('; ').join('\n'));
|
|
8969
|
+
if (_v2OrgData.config) _v2OrgData.config = json.def;
|
|
8970
|
+
Object.assign(_v2OrgData, { goal: json.def.goal, schedule: json.def.schedule, run_config: json.def.run_config, cost_tiers: json.def.cost_tiers });
|
|
8971
|
+
if (msg) { msg.style.color = 'oklch(68% 0.20 150)'; msg.textContent = 'Saved — applies from the next run'; setTimeout(() => { if (msg) msg.textContent = ''; }, 4000); }
|
|
8972
|
+
showToast('Config saved', 'Validated against the org schema', 'ok');
|
|
8973
|
+
} catch (e) {
|
|
8974
|
+
if (msg) { msg.style.color = 'oklch(60% 0.22 25)'; msg.textContent = `Not saved:\n${e.message}`; }
|
|
8690
8975
|
}
|
|
8691
8976
|
}
|
|
8692
8977
|
|
|
@@ -14218,7 +14503,7 @@ Object.assign(window, {
|
|
|
14218
14503
|
gdOpen: _gdOpen,
|
|
14219
14504
|
gdRevealPath: _gdRevealPath,
|
|
14220
14505
|
gdCopyPath: _gdCopyPath,
|
|
14221
|
-
|
|
14506
|
+
hiAct: _hiAct,
|
|
14222
14507
|
v2ViewOrgFile: _v2ViewOrgFile,
|
|
14223
14508
|
minimapJump: _minimapJump,
|
|
14224
14509
|
stopLoop: _stopLoop,
|
|
@@ -14227,6 +14512,7 @@ Object.assign(window, {
|
|
|
14227
14512
|
v2TriggerAvatarUpload: _v2TriggerAvatarUpload,
|
|
14228
14513
|
v2HandleAvatarFile: _v2HandleAvatarFile,
|
|
14229
14514
|
orgApprovalAction: _orgApprovalAction,
|
|
14515
|
+
orgGateAction: _orgGateAction,
|
|
14230
14516
|
generateOrgSettingsCmd: _generateOrgSettingsCmd,
|
|
14231
14517
|
setOdtChatMode: _setOdtChatMode,
|
|
14232
14518
|
v2SaveOrgConfig: _v2SaveOrgConfig,
|