@nanobpm/nano-workforce 0.195.1 → 0.196.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 +12 -0
- package/app/agentic/cockpit/cockpit-route.test.ts +15 -1
- package/app/agentic/cockpit/cockpit-route.ts +40 -11
- package/app/agentic/cockpit/mount.test.ts +391 -0
- package/app/convergeGate.test.ts +73 -0
- package/app/github.ts +78 -7
- package/package.json +1 -1
- package/pages/cockpit/cockpit.css +17 -0
- package/pages/cockpit/mount.js +280 -17
- package/pages/cockpit/standalone.html +9 -3
- package/pages/delivery-graph-detail.page.json +1 -0
- package/pages/delivery-graphs.page.json +3 -2
- package/pages/epic-detail.page.json +2 -0
- package/pages/epic.page.json +1 -0
- package/pages/feature.page.json +1 -0
- package/pages/home.page.json +11 -0
- package/pages/lineage.page.json +2 -0
- package/pages/overview.page.json +4 -0
- package/pages/tasks.page.json +1 -0
- package/resources/prompts/review-round.md +10 -1
- package/test/agent-link-column.test.ts +66 -0
package/pages/home.page.json
CHANGED
|
@@ -173,6 +173,17 @@
|
|
|
173
173
|
"keyField": "process_key"
|
|
174
174
|
}
|
|
175
175
|
},
|
|
176
|
+
{
|
|
177
|
+
"field": "process_key",
|
|
178
|
+
"header": "Agent",
|
|
179
|
+
"template": "Transcript",
|
|
180
|
+
"width": "7rem",
|
|
181
|
+
"link": {
|
|
182
|
+
"kind": "page",
|
|
183
|
+
"page": "cockpit",
|
|
184
|
+
"keyField": "process_key"
|
|
185
|
+
}
|
|
186
|
+
},
|
|
176
187
|
{
|
|
177
188
|
"field": "incident_message",
|
|
178
189
|
"header": "Incident",
|
package/pages/lineage.page.json
CHANGED
|
@@ -122,6 +122,7 @@
|
|
|
122
122
|
"truncate": true,
|
|
123
123
|
"link": { "kind": "processExplorer", "keyField": "process_key" }
|
|
124
124
|
},
|
|
125
|
+
{ "field": "process_key", "header": "Agent", "template": "Transcript", "width": "7rem", "link": { "kind": "page", "page": "cockpit", "keyField": "process_key" } },
|
|
125
126
|
{ "field": "pr_count", "header": "PRs", "width": "5rem" },
|
|
126
127
|
{ "field": "updated_at", "header": "Updated", "width": "9rem", "format": "datetime" }
|
|
127
128
|
],
|
|
@@ -145,6 +146,7 @@
|
|
|
145
146
|
"columns": [
|
|
146
147
|
{ "field": "title", "template": "{{title}}", "header": "PR", "subtitleField": "pr_key", "truncate": true, "linkField": "url" },
|
|
147
148
|
{ "field": "status", "header": "Status", "link": { "kind": "processExplorer", "keyField": "process_key" } },
|
|
149
|
+
{ "field": "process_key", "header": "Agent", "template": "Transcript", "width": "7rem", "link": { "kind": "page", "page": "cockpit", "keyField": "process_key" } },
|
|
148
150
|
{ "field": "current_round", "header": "Round" },
|
|
149
151
|
{ "field": "outcome", "header": "Outcome", "truncate": true },
|
|
150
152
|
{ "field": "updated_at", "header": "Updated", "width": "9rem", "format": "datetime" }
|
package/pages/overview.page.json
CHANGED
|
@@ -112,6 +112,7 @@
|
|
|
112
112
|
"columns": [
|
|
113
113
|
{ "field": "title", "template": "{{title}}", "header": "Item", "subtitleField": "pr_key", "truncate": true, "width": "36%", "link": { "kind": "page", "page": "home", "keyField": "pr_key" } },
|
|
114
114
|
{ "field": "status", "header": "Status", "link": { "kind": "processExplorer", "keyField": "process_key" } },
|
|
115
|
+
{ "field": "process_key", "header": "Agent", "template": "Transcript", "width": "7rem", "link": { "kind": "page", "page": "cockpit", "keyField": "process_key" } },
|
|
115
116
|
{ "field": "incident_message", "header": "Incident", "truncate": true, "badge": { "tone": "danger", "label": "1" } },
|
|
116
117
|
{ "field": "current_round", "template": "{{current_round}} · {{active_worker}}", "header": "Round · Agent" },
|
|
117
118
|
{ "field": "updated_at", "header": "Updated", "width": "9rem", "format": "datetime" }
|
|
@@ -149,6 +150,7 @@
|
|
|
149
150
|
"columns": [
|
|
150
151
|
{ "field": "title", "template": "{{title}}", "header": "Item", "subtitleField": "plan_key", "truncate": true, "width": "36%", "link": { "kind": "page", "page": "epic-detail", "keyField": "plan_key" } },
|
|
151
152
|
{ "field": "status", "header": "Status", "link": { "kind": "processExplorer", "keyField": "process_key" } },
|
|
153
|
+
{ "field": "process_key", "header": "Agent", "template": "Transcript", "width": "7rem", "link": { "kind": "page", "page": "cockpit", "keyField": "process_key" } },
|
|
152
154
|
{ "field": "delivery_label", "header": "Landing" },
|
|
153
155
|
{ "field": "wave_label", "header": "Wave" },
|
|
154
156
|
{ "field": "updated_at", "header": "Updated", "width": "9rem", "format": "datetime" }
|
|
@@ -186,6 +188,7 @@
|
|
|
186
188
|
"columns": [
|
|
187
189
|
{ "field": "title", "template": "{{title}}", "header": "Item", "subtitleField": "feature_key", "truncate": true, "width": "36%", "link": { "kind": "page", "page": "feature", "keyField": "feature_key" } },
|
|
188
190
|
{ "field": "status", "header": "Status", "link": { "kind": "processExplorer", "keyField": "process_key" } },
|
|
191
|
+
{ "field": "process_key", "header": "Agent", "template": "Transcript", "width": "7rem", "link": { "kind": "page", "page": "cockpit", "keyField": "process_key" } },
|
|
189
192
|
{ "field": "delivery_label", "header": "Delivery" },
|
|
190
193
|
{ "field": "updated_at", "header": "Updated", "width": "9rem", "format": "datetime" }
|
|
191
194
|
],
|
|
@@ -227,6 +230,7 @@
|
|
|
227
230
|
"columns": [
|
|
228
231
|
{ "field": "title", "template": "{{title}}", "header": "Item", "subtitleField": "run_key", "truncate": true, "width": "30%", "link": { "kind": "page", "page": "delivery-graph-detail", "keyField": "run_key" } },
|
|
229
232
|
{ "field": "status", "header": "Status", "link": { "kind": "processExplorer", "keyField": "process_key" } },
|
|
233
|
+
{ "field": "process_key", "header": "Agent", "template": "Transcript", "width": "7rem", "link": { "kind": "page", "page": "cockpit", "keyField": "process_key" } },
|
|
230
234
|
{
|
|
231
235
|
"field": "stage",
|
|
232
236
|
"header": "Pipeline",
|
package/pages/tasks.page.json
CHANGED
|
@@ -106,6 +106,7 @@
|
|
|
106
106
|
"header": "Process",
|
|
107
107
|
"link": { "kind": "processExplorer", "keyField": "process_key" }
|
|
108
108
|
},
|
|
109
|
+
{ "field": "process_key", "header": "Agent", "template": "Transcript", "width": "7rem", "link": { "kind": "page", "page": "cockpit", "keyField": "process_key" } },
|
|
109
110
|
{ "field": "updated_at", "header": "Updated", "format": "datetime" }
|
|
110
111
|
],
|
|
111
112
|
"rowKey": "user_task_key",
|
|
@@ -71,6 +71,11 @@ Because several agents may run on the same host at once:
|
|
|
71
71
|
"low confidence" list Copilot folds into the **review body** (`.../reviews`
|
|
72
72
|
`body`). These are NOT in the default inline-comment API set, so a plain
|
|
73
73
|
`.../comments` read misses them; scan the review body for them explicitly.
|
|
74
|
+
Newer Copilot reviews use an **overview format** (body starts `<!-- ccr-overview-v2 -->`)
|
|
75
|
+
with no "Suppressed comments" block. There, findings without an inline thread appear
|
|
76
|
+
**only** in the "What changed in this PR" file table, as clauses in the notes column
|
|
77
|
+
ending `(<severity>, N vote[s])`. The gate treats **each such clause** as an advisory
|
|
78
|
+
(even if it duplicates an inline thread), so triage and ack every one (step 5a).
|
|
74
79
|
If `answer` is present, it is a human's instruction: either the decision on the
|
|
75
80
|
escalation you raised last round, or change requests from merge approval of this
|
|
76
81
|
converged PR. Act on it first, on this same PR branch (never open a new PR).
|
|
@@ -130,7 +135,11 @@ Because several agents may run on the same host at once:
|
|
|
130
135
|
new line — a line-based ack would go stale and the gate would escalate to a human
|
|
131
136
|
every round (issue #787). Because the ack is keyed on the prose, a decline you
|
|
132
137
|
made in an earlier round stays acknowledged across the drift and you need **not**
|
|
133
|
-
re-ack it. The ack thread may sit on any valid diff line.
|
|
138
|
+
re-ack it. The ack thread may sit on any valid diff line. For an **overview-format
|
|
139
|
+
table finding**, `<path>` is the file column (backticks and any invisible zero-width
|
|
140
|
+
characters removed) and `<advisory text>` is the finding clause **verbatim, without**
|
|
141
|
+
its trailing `(<severity>, N vote[s])` tag — for the row's first finding, include any
|
|
142
|
+
leading row summary before it (e.g. `nano-ack: a.json :: Adds X; A is wrong`). Example:
|
|
134
143
|
|
|
135
144
|
```sh
|
|
136
145
|
# Post the ack thread (pick any changed line in the diff for path/line). Use the PR's real HEAD
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// #833 — every grid whose cell links a process instance to the explorer also offers an "Agent" cell
|
|
2
|
+
// linking the SAME process_key to the cockpit (which focuses that process: live terminal, else its
|
|
3
|
+
// latest transcript, plus the engine agent history). Guards the class: a new active-run grid can't
|
|
4
|
+
// ship with a process-explorer link but no way to reach its agent — regardless of which column
|
|
5
|
+
// carries the explorer link (Status, Phase, Stage, Process, …).
|
|
6
|
+
import assert from "node:assert/strict";
|
|
7
|
+
import { readdirSync, readFileSync } from "node:fs";
|
|
8
|
+
import { test } from "node:test";
|
|
9
|
+
|
|
10
|
+
function isObject(value: unknown): value is Record<string, unknown> {
|
|
11
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// The `link` object of a column, if it has one (no `as`: narrowed through type guards).
|
|
15
|
+
function columnLink(column: unknown): Record<string, unknown> | undefined {
|
|
16
|
+
if (!isObject(column)) return undefined;
|
|
17
|
+
return isObject(column.link) ? column.link : undefined;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// The process-explorer link of a column, wherever it lives: a plain column carries it at `link`,
|
|
21
|
+
// but a `pipeline` column carries it on its `locus` (the cell that links the row's process instance —
|
|
22
|
+
// e.g. the Feature runs grid's Pipeline column, pages/feature.page.json). Guarding only `link` would
|
|
23
|
+
// let a pipeline-locus explorer link ship with no Agent column and this test still pass (#834).
|
|
24
|
+
function explorerLink(column: unknown): Record<string, unknown> | undefined {
|
|
25
|
+
const direct = columnLink(column);
|
|
26
|
+
if (direct?.kind === "processExplorer") return direct;
|
|
27
|
+
if (!isObject(column)) return undefined;
|
|
28
|
+
const locus = isObject(column.locus) ? column.locus : undefined;
|
|
29
|
+
const locusLink = locus && isObject(locus.link) ? locus.link : undefined;
|
|
30
|
+
return locusLink?.kind === "processExplorer" ? locusLink : undefined;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Collect every `columns` array anywhere in the page tree (top-level grids, tab grids, detail
|
|
34
|
+
// children, …) without asserting the parsed JSON's shape.
|
|
35
|
+
function grids(node: unknown, out: unknown[][]): void {
|
|
36
|
+
if (Array.isArray(node)) {
|
|
37
|
+
for (const n of node) grids(n, out);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
if (!isObject(node)) return;
|
|
41
|
+
if (Array.isArray(node.columns)) out.push(node.columns);
|
|
42
|
+
for (const v of Object.values(node)) grids(v, out);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
test("#833: every process-explorer grid also links its process to the cockpit via an Agent column", () => {
|
|
46
|
+
let checked = 0;
|
|
47
|
+
for (const file of readdirSync("pages").filter((f) => f.endsWith(".page.json"))) {
|
|
48
|
+
const all: unknown[][] = [];
|
|
49
|
+
grids(JSON.parse(readFileSync(`pages/${file}`, "utf8")), all);
|
|
50
|
+
for (const cols of all) {
|
|
51
|
+
// A grid qualifies whenever ANY of its columns links to the process explorer — not just a
|
|
52
|
+
// Status/Phase header, and not just a plain `link`: a pipeline column's `locus.link` counts too
|
|
53
|
+
// — so a Stage/Process/Pipeline (or any future) explorer link is guarded too.
|
|
54
|
+
const explorer = cols.some((c) => explorerLink(c) !== undefined);
|
|
55
|
+
if (!explorer) continue;
|
|
56
|
+
checked++;
|
|
57
|
+
const agent = cols.find((c) => isObject(c) && c.header === "Agent");
|
|
58
|
+
assert.deepEqual(
|
|
59
|
+
columnLink(agent),
|
|
60
|
+
{ kind: "page", page: "cockpit", keyField: "process_key" },
|
|
61
|
+
`${file}: grid missing the Agent → cockpit link`,
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
assert.ok(checked > 0, "found no process-explorer grids to check");
|
|
66
|
+
});
|