conductor-board 2.2.0 → 2.4.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/index.html CHANGED
@@ -6,10 +6,10 @@
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
7
  <meta name="theme-color" content="#0a0a0f" />
8
8
  <title>Agent Conductor — Board</title>
9
- <script type="module" crossorigin src="./assets/index-DvTrz5lj.js"></script>
9
+ <script type="module" crossorigin src="./assets/index-D5hLWXgI.js"></script>
10
10
  <link rel="modulepreload" crossorigin href="./assets/motion-Dmvx5jlk.js">
11
11
  <link rel="modulepreload" crossorigin href="./assets/yaml-NA7d4LV6.js">
12
- <link rel="stylesheet" crossorigin href="./assets/index-ilfk-igS.css">
12
+ <link rel="stylesheet" crossorigin href="./assets/index-CKHEldJ6.css">
13
13
  </head>
14
14
  <body>
15
15
  <div id="root"></div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "conductor-board",
3
- "version": "2.2.0",
3
+ "version": "2.4.0",
4
4
  "description": "Gated workflows for AI agents — live Kanban board included",
5
5
  "license": "MIT",
6
6
  "author": "mettafive",
package/server/server.js CHANGED
@@ -223,6 +223,7 @@ function archiveIfDone(historyDir, snapshot, archived) {
223
223
 
224
224
  const record = {
225
225
  run_id: runId,
226
+ run_name: status.run_name || null,
226
227
  workflow: status.workflow || "workflow",
227
228
  status: status.status,
228
229
  started_at: status.started_at || null,
@@ -508,7 +509,15 @@ function serveStatic(req, res) {
508
509
  return;
509
510
  }
510
511
  const ext = path.extname(filePath);
511
- res.writeHead(200, { "content-type": MIME[ext] || "application/octet-stream" });
512
+ const headers = { "content-type": MIME[ext] || "application/octet-stream" };
513
+ // index.html must always revalidate so a fresh build is picked up on reload;
514
+ // the hashed /assets/ files are content-addressed, so cache them hard.
515
+ if (ext === ".html") {
516
+ headers["cache-control"] = "no-cache, no-store, must-revalidate";
517
+ } else if (urlPath.startsWith("/assets/")) {
518
+ headers["cache-control"] = "public, max-age=31536000, immutable";
519
+ }
520
+ res.writeHead(200, headers);
512
521
  fs.createReadStream(filePath).pipe(res);
513
522
  }
514
523
 
@@ -900,6 +909,73 @@ export function startServer({ statusPath, conductorPath: explicitConductor, port
900
909
  });
901
910
  return;
902
911
  }
912
+ // developer notes / directives on activity cards — the flow-manager feedback loop.
913
+ // Body actions: create {card, cardTitle, step, text, directive, scope}; edit {id, text, directive,
914
+ // scope}; remove {id, action:"remove"}. Edits/removals are logged to the note's audit history,
915
+ // never destroyed — the record stays, the footnote grows ("edited from X to Y").
916
+ if (req.method === "POST" && (m = url.match(/^\/api\/workflow\/([^/]+)\/comment$/))) {
917
+ const wf = findWf(decodeURIComponent(m[1]));
918
+ if (!wf) return json(res, 404, { error: "not found" });
919
+ readBody(req).then((bodyStr) => {
920
+ let body;
921
+ try {
922
+ body = JSON.parse(bodyStr || "{}");
923
+ } catch {
924
+ return json(res, 400, { error: "invalid request body" });
925
+ }
926
+ let status;
927
+ try {
928
+ status = JSON.parse(fs.readFileSync(wf.statusPath, "utf8"));
929
+ } catch {
930
+ return json(res, 500, { error: "could not read status.json" });
931
+ }
932
+ const notes = (status.developer_notes = Array.isArray(status.developer_notes) ? status.developer_notes : []);
933
+ const at = new Date().toISOString();
934
+ const text = typeof body.text === "string" ? body.text.trim() : "";
935
+
936
+ if (body.id) {
937
+ const n = notes.find((x) => x && x.id === body.id);
938
+ if (!n) return json(res, 404, { error: "note not found" });
939
+ n.history = Array.isArray(n.history) ? n.history : [];
940
+ if (body.action === "remove") {
941
+ n.history.push({ at, action: "removed", from: n.text });
942
+ n.status = "removed";
943
+ } else {
944
+ if (n.text !== text)
945
+ n.history.push({ at, action: n.status === "removed" ? "restored" : "edited", from: n.text, to: text });
946
+ n.text = text;
947
+ n.directive = !!body.directive;
948
+ if (typeof body.scope === "string") n.scope = body.scope;
949
+ n.updated_at = at;
950
+ n.status = "open"; // an edit reopens the ask so the next run reconsiders it
951
+ delete n.resolution;
952
+ }
953
+ } else {
954
+ // create
955
+ if (!body.card || !text) return json(res, 400, { error: "card id and text required" });
956
+ notes.push({
957
+ id: `${body.card}:${Date.now()}`,
958
+ at,
959
+ updated_at: at,
960
+ step: body.step || "",
961
+ card: body.card,
962
+ card_title: typeof body.cardTitle === "string" ? body.cardTitle : undefined,
963
+ text,
964
+ directive: !!body.directive,
965
+ scope: typeof body.scope === "string" ? body.scope : undefined,
966
+ status: "open",
967
+ history: [{ at, action: "created", to: text }],
968
+ });
969
+ }
970
+ try {
971
+ fs.writeFileSync(wf.statusPath, JSON.stringify(status, null, 2));
972
+ } catch (e) {
973
+ return json(res, 500, { error: `write failed: ${e.message}` });
974
+ }
975
+ return json(res, 200, { ok: true });
976
+ });
977
+ return;
978
+ }
903
979
  if ((m = url.match(/^\/api\/workflow\/([^/]+)\/history$/))) {
904
980
  const wf = findWf(decodeURIComponent(m[1]));
905
981
  return wf ? json(res, 200, listHistory(wf.historyDir)) : json(res, 404, { error: "not found" });