@nanobpm/nano-workforce 0.130.0 → 0.131.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -0
- package/app/agentic/cockpit/supply-boot.test.ts +49 -2
- package/app/agentic/cockpit/supply-boot.ts +44 -2
- package/app/agentic/cockpit/supply-render.test.ts +21 -0
- package/app/agentic/cockpit/supply-render.ts +19 -9
- package/app/agentic/cockpit/supply-view.test.ts +15 -0
- package/app/agentic/cockpit/supply-view.ts +9 -0
- package/app/deliveryGraph.test.ts +279 -0
- package/app/deliveryGraph.ts +381 -2
- package/app/deliveryGraphCompiler.test.ts +119 -0
- package/app/deliveryGraphCompiler.ts +213 -44
- package/app/deliveryGraphDeploy.test.ts +148 -0
- package/docs/adr/0005-agent-authored-delivery-graphs.md +25 -0
- package/openapi.yaml +43 -1
- package/package.json +1 -1
- package/pages/cockpit/cockpit.css +12 -0
- package/pages/cockpit/mount.js +47 -8
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nanobpm/nano-workforce",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.131.0",
|
|
4
4
|
"description": "Nano Workforce — an Agent Graph Orchestration application for Agentic SDLC: durable BPMN processes that coordinate a graph of AI agents across the software delivery lifecycle.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "main.ts",
|
|
@@ -173,6 +173,18 @@
|
|
|
173
173
|
border-radius: 6px;
|
|
174
174
|
}
|
|
175
175
|
|
|
176
|
+
/* The "connected — waiting for output" status note under the terminal (blank-terminal defect fix):
|
|
177
|
+
hidden until a live drill is connected-but-quiet, so a blank panel reads as "waiting" not "broken". */
|
|
178
|
+
.cockpit-terminal-note {
|
|
179
|
+
margin: 8px 0 0;
|
|
180
|
+
font-size: 0.85em;
|
|
181
|
+
color: #7c8794;
|
|
182
|
+
font-style: italic;
|
|
183
|
+
}
|
|
184
|
+
.cockpit-terminal-note[data-terminal-note="none"] {
|
|
185
|
+
display: none;
|
|
186
|
+
}
|
|
187
|
+
|
|
176
188
|
/* ── Past sessions (H3 read path / #222): the captured-session history + replay. ──────────────── */
|
|
177
189
|
|
|
178
190
|
.cockpit-past-header {
|
package/pages/cockpit/mount.js
CHANGED
|
@@ -72,6 +72,7 @@ function workerView(worker, staleAfterMs, byJobKey) {
|
|
|
72
72
|
host: worker.host ?? "\u2014",
|
|
73
73
|
jobKeys,
|
|
74
74
|
jobs: jobKeys.length,
|
|
75
|
+
drillable: jobKeys.length > 0,
|
|
75
76
|
correlations,
|
|
76
77
|
liveness: liveness(worker, staleAfterMs),
|
|
77
78
|
staleMs: worker.staleMs,
|
|
@@ -141,12 +142,18 @@ function workerRow(doc, worker, onDrill, onOpenWorker) {
|
|
|
141
142
|
button.setAttribute("data-stream", worker.stream);
|
|
142
143
|
if (onOpenWorker) button.addEventListener("click", () => onOpenWorker(worker.instance));
|
|
143
144
|
nameCell.appendChild(button);
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
if (
|
|
149
|
-
|
|
145
|
+
// The inline live-terminal drill — ONLY for a worker that currently holds a job. An idle worker's
|
|
146
|
+
// `stream` is its bare instance id, which no producer writes to, so drilling it opens a permanently
|
|
147
|
+
// blank "live" terminal. Suppress the affordance when there is nothing live to stream (mirrors
|
|
148
|
+
// app/agentic/cockpit/supply-render.ts).
|
|
149
|
+
if (worker.drillable) {
|
|
150
|
+
const drill = el(doc, "button", "cockpit-worker-drill", "terminal");
|
|
151
|
+
drill.setAttribute("type", "button");
|
|
152
|
+
drill.setAttribute("data-instance", worker.instance);
|
|
153
|
+
drill.setAttribute("data-stream", worker.stream);
|
|
154
|
+
if (onDrill) drill.addEventListener("click", () => onDrill(worker.stream));
|
|
155
|
+
nameCell.appendChild(drill);
|
|
156
|
+
}
|
|
150
157
|
row.appendChild(nameCell);
|
|
151
158
|
|
|
152
159
|
row.appendChild(el(doc, "td", "cockpit-td cockpit-supply-family", worker.family));
|
|
@@ -494,6 +501,12 @@ export function mountCockpit(host, opts = {}) {
|
|
|
494
501
|
const terminalHost = el(doc, "div", "cockpit-terminal-host");
|
|
495
502
|
terminalHost.setAttribute("data-terminal", "host");
|
|
496
503
|
terminalPanel.appendChild(terminalHost);
|
|
504
|
+
// Status note under the terminal, shown while a LIVE drill has connected but no output has arrived
|
|
505
|
+
// yet (a quiet job between frames), so a blank panel reads as "waiting" not "broken". Cleared on
|
|
506
|
+
// the first frame and on every mode change (mirrors app/agentic/cockpit/supply-boot.ts).
|
|
507
|
+
const terminalNote = el(doc, "p", "cockpit-terminal-note");
|
|
508
|
+
terminalNote.setAttribute("data-terminal-note", "none");
|
|
509
|
+
terminalPanel.appendChild(terminalNote);
|
|
497
510
|
shell.appendChild(listRegion);
|
|
498
511
|
shell.appendChild(pastRegion);
|
|
499
512
|
shell.appendChild(terminalPanel);
|
|
@@ -524,6 +537,19 @@ export function mountCockpit(host, opts = {}) {
|
|
|
524
537
|
if (next === "live") terminalTitle.textContent = "Worker terminal — live";
|
|
525
538
|
else if (next === "replay") terminalTitle.textContent = "Worker terminal — replay (past session)";
|
|
526
539
|
else terminalTitle.textContent = "Worker terminal";
|
|
540
|
+
// Any mode change replaces what's behind the panel, so the prior "waiting" note is stale — clear
|
|
541
|
+
// it. A live drill re-arms it once its fresh terminal is mounted.
|
|
542
|
+
setNote(undefined);
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
function setNote(text) {
|
|
546
|
+
if (text == null) {
|
|
547
|
+
terminalNote.textContent = "";
|
|
548
|
+
terminalNote.setAttribute("data-terminal-note", "none");
|
|
549
|
+
return;
|
|
550
|
+
}
|
|
551
|
+
terminalNote.textContent = text;
|
|
552
|
+
terminalNote.setAttribute("data-terminal-note", "waiting");
|
|
527
553
|
}
|
|
528
554
|
|
|
529
555
|
function teardownTerminal() {
|
|
@@ -586,8 +612,19 @@ export function mountCockpit(host, opts = {}) {
|
|
|
586
612
|
teardownTerminal();
|
|
587
613
|
try {
|
|
588
614
|
terminalHost.replaceChildren();
|
|
589
|
-
const
|
|
590
|
-
terminal =
|
|
615
|
+
const rawSink = xtermSink(terminalHost);
|
|
616
|
+
terminal = rawSink;
|
|
617
|
+
// Wrap the sink so the first byte of live output clears the "waiting" note (mirrors supply-boot).
|
|
618
|
+
let cleared = false;
|
|
619
|
+
const sink = {
|
|
620
|
+
write: (chunk) => {
|
|
621
|
+
if (!cleared) {
|
|
622
|
+
cleared = true;
|
|
623
|
+
setNote(undefined);
|
|
624
|
+
}
|
|
625
|
+
rawSink.write(chunk);
|
|
626
|
+
},
|
|
627
|
+
};
|
|
591
628
|
let session;
|
|
592
629
|
const client = new RelayChannelClient({
|
|
593
630
|
connect: connectRelay,
|
|
@@ -599,6 +636,8 @@ export function mountCockpit(host, opts = {}) {
|
|
|
599
636
|
client.open();
|
|
600
637
|
drill = { stream, client };
|
|
601
638
|
setMode("live", stream);
|
|
639
|
+
// Arm the "waiting for output" note (after setMode, which clears it) until the first frame.
|
|
640
|
+
setNote("Connected — waiting for live output…");
|
|
602
641
|
} catch (err) {
|
|
603
642
|
// The new terminal failed to build after the prior one was torn down: reset the region to idle
|
|
604
643
|
// (and drop any partially-built terminal) so the UI never shows a stale "live"/"replay"
|