yaml-flow 5.2.1 → 5.2.5

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.
@@ -527,7 +527,7 @@ export function createExampleBoardServerRuntime(options = {}) {
527
527
 
528
528
  for (const sourceDef of cardDefinition.sources) {
529
529
  if (!sourceDef || !sourceDef.bindTo || !sourceDef.outputFile) continue;
530
- const filePath = path.join(boardDir, sourceDef.outputFile);
530
+ const filePath = path.join(boardDir, cardDefinition.id, sourceDef.outputFile);
531
531
  if (!fs.existsSync(filePath)) continue;
532
532
 
533
533
  const raw = fs.readFileSync(filePath, 'utf-8').trim();
@@ -563,13 +563,15 @@ export function createExampleBoardServerRuntime(options = {}) {
563
563
  function readChatSignal(cardId) {
564
564
  const chatsDir = path.join(tmpCardsDir, cardId, 'chats');
565
565
  if (!fs.existsSync(chatsDir)) {
566
- return { count: 0, latest_mtime_ms: 0 };
566
+ return { count: 0, latest_mtime_ms: 0, processing: false };
567
567
  }
568
568
 
569
569
  let count = 0;
570
570
  let latestMtimeMs = 0;
571
+ let processing = false;
571
572
  for (const entry of fs.readdirSync(chatsDir, { withFileTypes: true })) {
572
573
  if (!entry.isFile()) continue;
574
+ if (entry.name === '.processing') { processing = true; continue; }
573
575
  count += 1;
574
576
  try {
575
577
  const st = fs.statSync(path.join(chatsDir, entry.name));
@@ -580,7 +582,7 @@ export function createExampleBoardServerRuntime(options = {}) {
580
582
  }
581
583
  }
582
584
 
583
- return { count, latest_mtime_ms: latestMtimeMs };
585
+ return { count, latest_mtime_ms: latestMtimeMs, processing };
584
586
  }
585
587
 
586
588
  function buildPublishedRuntimePayload() {
@@ -642,6 +644,21 @@ export function createExampleBoardServerRuntime(options = {}) {
642
644
  fs.copyFileSync(src, dst);
643
645
  }
644
646
 
647
+ // Concatenate agent-instructions*.md files into copilot-instructions.md at boardSetupRoot
648
+ const boardSetupRoot = path.dirname(boardDir);
649
+ const agentInstructionFiles = ['agent-instructions.md', 'agent-instructions-cardlayout.md'];
650
+ const srcDir = path.dirname(cardsDir); // board source dir where agent-instructions*.md live
651
+ const parts = [];
652
+ for (const fname of agentInstructionFiles) {
653
+ const fpath = path.join(srcDir, fname);
654
+ if (fs.existsSync(fpath)) {
655
+ parts.push(fs.readFileSync(fpath, 'utf-8').trimEnd());
656
+ }
657
+ }
658
+ if (parts.length > 0) {
659
+ fs.writeFileSync(path.join(boardSetupRoot, 'copilot-instructions.md'), parts.join('\n\n') + '\n', 'utf-8');
660
+ }
661
+
645
662
  didDemoSetup = true;
646
663
  }
647
664
 
@@ -930,8 +947,9 @@ export function createExampleBoardServerRuntime(options = {}) {
930
947
  if (!entry.isFile()) continue;
931
948
  const name = entry.name;
932
949
  const parsed = String(name).match(/^(\d+)[-_]([a-z0-9_-]+)\.txt$/i);
933
- const serial = parsed ? parseInt(parsed[1], 10) : 0;
934
- const role = parsed ? parsed[2].toLowerCase() : 'system';
950
+ if (!parsed) continue; // skip .processing and other non-chat files
951
+ const serial = parseInt(parsed[1], 10);
952
+ const role = parsed[2].toLowerCase();
935
953
  const filePath = path.join(chatsDir, name);
936
954
  const text = fs.readFileSync(filePath, 'utf-8');
937
955
  const stat = fs.statSync(filePath);
@@ -975,23 +993,44 @@ export function createExampleBoardServerRuntime(options = {}) {
975
993
 
976
994
  // Fire-and-forget invocation of .chat-handler after a user chat message is persisted.
977
995
  // boardDir/.chat-handler must contain the handler command as a single-line string.
978
- // Called with: --boardId <id> --cardId <id> --extra <json>
979
- // extra: { chatDir: <abs path>, boardDir: <abs path>, lastChatFile: <filename> }
996
+ // Called with: --boardId <id> --cardId <id> --extraEncJson <base64json>
997
+ // extraEncJson decodes to:
998
+ // boardSetupRoot — absolute path to board root (parent of runtime/, surface/, runtime-out/)
999
+ // boardRuntimeDir — relative: 'runtime'
1000
+ // runtimeStatusDir— relative: 'runtime-out'
1001
+ // cardsDir — relative: 'surface/tmp-cards'
1002
+ // chatDir — relative (from cardsDir): e.g. 'card-portfolio/chats'
1003
+ // lastChatFile — filename of the just-written user message, e.g. '001_user.txt'
980
1004
  // Handler failures are logged and silently ignored — chat-send response is never affected.
981
1005
  function invokeChatHandler(cardId, chatsDir, lastChatFile) {
982
1006
  const handlerFile = path.join(boardDir, '.chat-handler');
983
1007
  if (!fs.existsSync(handlerFile)) return;
984
1008
  const handlerCmd = fs.readFileSync(handlerFile, 'utf-8').trim();
985
1009
  if (!handlerCmd) return;
986
- const extra = Buffer.from(JSON.stringify({ chatDir: chatsDir, boardDir, lastChatFile })).toString('base64');
1010
+ const boardSetupRoot = path.dirname(boardDir);
1011
+ const processingFile = path.join(chatsDir, '.processing');
1012
+ try { fs.mkdirSync(chatsDir, { recursive: true }); fs.writeFileSync(processingFile, '', 'utf-8'); } catch {}
1013
+ const extra = Buffer.from(JSON.stringify({
1014
+ boardSetupRoot,
1015
+ boardRuntimeDir: path.relative(boardSetupRoot, boardDir),
1016
+ runtimeStatusDir: path.relative(boardSetupRoot, runtimeOutDir),
1017
+ cardsDir: path.relative(boardSetupRoot, tmpCardsDir),
1018
+ chatDir: chatsDir,
1019
+ lastChatFile,
1020
+ })).toString('base64');
987
1021
  try {
988
- const proc = spawn(handlerCmd, ['--boardId', boardId, '--cardId', String(cardId), '--extraEncJson', extra], {
1022
+ const proc = spawn(handlerCmd, [
1023
+ '--boardId', boardId, '--cardId', String(cardId),
1024
+ '--extraEncJson', extra,
1025
+ '--cleanOnExit', processingFile,
1026
+ ], {
989
1027
  shell: true,
990
1028
  stdio: 'ignore',
991
1029
  });
992
1030
  proc.unref();
993
1031
  console.log(`[chat-handler] invoked for card "${cardId}" (boardId: "${boardId}")`);
994
1032
  } catch (err) {
1033
+ try { fs.unlinkSync(processingFile); } catch {}
995
1034
  console.warn(`[chat-handler] spawn failed for card "${cardId}":`, (err && err.message) || String(err));
996
1035
  }
997
1036
  }
@@ -64,12 +64,16 @@ var LiveCard = (function () {
64
64
  .lc-staged-file { display:flex; align-items:center; gap:.5rem; padding:.125rem 0; }
65
65
  .lc-chat-el { display:flex; flex-direction:column; }
66
66
  .lc-chat-body { flex:1; overflow-y:auto; max-height:300px; padding:.25rem; }
67
- .lc-chat-bubble { padding:.5rem .75rem; margin:.375rem 0; border-radius:.75rem; max-width:85%; word-wrap:break-word; font-size:.875rem; line-height:1.4; }
68
- .lc-chat-bubble-user { background:var(--bs-primary-bg-subtle,#cfe2ff); margin-left:auto; }
69
- .lc-chat-bubble-assistant { background:var(--bs-light,#f8f9fa); }
70
- .lc-chat-bubble-system { background:transparent; color:var(--bs-secondary,#6c757d); font-style:italic; text-align:center; max-width:100%; font-size:.8rem; }
67
+ .lc-chat-bubble { padding:.5rem .75rem; margin:.375rem 0; border-radius:.75rem; max-width:85%; word-wrap:break-word; font-size:.875rem; line-height:1.4; display:flex; gap:.5rem; align-items:flex-start; }
68
+ .lc-chat-bubble-user { background:var(--bs-primary-bg-subtle,#cfe2ff); margin-left:auto; flex-direction:row-reverse; }
69
+ .lc-chat-bubble-assistant { background:var(--bs-light,#f8f9fa); border:1px solid var(--bs-border-color,#dee2e6); }
70
+ .lc-chat-bubble-system { background:transparent; color:var(--bs-secondary,#6c757d); font-style:italic; text-align:center; max-width:100%; font-size:.8rem; align-self:center; gap:0; padding:.125rem .5rem; }
71
+ .lc-chat-icon { flex-shrink:0; line-height:1.4; opacity:.6; display:flex; align-items:center; margin-top:.15rem; }
72
+ .lc-chat-bubble-content { flex:1; min-width:0; }
73
+ .lc-chat-bubble-content p:last-child { margin-bottom:0; }
71
74
  .lc-chat-bubble-pending { opacity:.85; }
72
75
  .lc-chat-bubble-pending .spinner-border { width:.75rem; height:.75rem; margin-left:.4rem; border-width:.12em; vertical-align:middle; }
76
+ .lc-chat-processing { display:flex; align-items:center; gap:.5rem; padding:.375rem .75rem; color:var(--bs-secondary,#6c757d); font-size:.8rem; font-style:italic; }
73
77
  .lc-chat-input-bar { display:flex; gap:.25rem; align-items:center; }
74
78
  .lc-chat-modal-input-row { display:flex; align-items:center; gap:.375rem; }
75
79
  .lc-chat-modal-input-row .form-control { min-width:0; }
@@ -403,25 +407,46 @@ var LiveCard = (function () {
403
407
  function _appendModalChatMessage(role, text, files) {
404
408
  _ensureChatModal();
405
409
  if (!_chatModal.body) return;
410
+ if (!text && !files) return; // skip empty messages
406
411
 
407
- const bubble = document.createElement('div');
408
412
  const normalizedRole = role === 'user' || role === 'assistant' ? role : 'system';
409
413
  const roleClass = normalizedRole === 'user'
410
414
  ? 'lc-chat-bubble-user'
411
415
  : (normalizedRole === 'assistant' ? 'lc-chat-bubble-assistant' : 'lc-chat-bubble-system');
416
+
417
+ const bubble = document.createElement('div');
412
418
  bubble.className = 'lc-chat-bubble ' + roleClass;
413
- bubble.textContent = text || '';
419
+
420
+ if (normalizedRole !== 'system') {
421
+ // SVG icons: person for user, sparkle-star for assistant
422
+ const userSvg = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="8" r="4"/><path d="M4 20c0-4 3.6-7 8-7s8 3 8 7"/></svg>';
423
+ const asstSvg = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/></svg>';
424
+ const iconEl = document.createElement('span');
425
+ iconEl.className = 'lc-chat-icon';
426
+ iconEl.setAttribute('aria-hidden', 'true');
427
+ iconEl.innerHTML = normalizedRole === 'user' ? userSvg : asstSvg;
428
+ bubble.appendChild(iconEl);
429
+ }
430
+
431
+ const content = document.createElement('div');
432
+ content.className = 'lc-chat-bubble-content';
433
+ if (normalizedRole === 'assistant') {
434
+ content.innerHTML = _renderMd(text || '');
435
+ } else {
436
+ content.textContent = text || '';
437
+ }
414
438
 
415
439
  if (Array.isArray(files) && files.length) {
416
440
  const meta = document.createElement('div');
417
- meta.className = 'lc-chat-inline-meta';
418
- meta.textContent = files.map(function (f) {
441
+ meta.className = 'small mt-1 text-muted';
442
+ meta.textContent = '\uD83D\uDCCE ' + files.map(function (f) {
419
443
  if (!f) return 'file';
420
444
  return typeof f === 'string' ? f : (f.name || 'file');
421
445
  }).join(', ');
422
- bubble.appendChild(meta);
446
+ content.appendChild(meta);
423
447
  }
424
448
 
449
+ bubble.appendChild(content);
425
450
  _chatModal.body.appendChild(bubble);
426
451
  _chatModal.body.scrollTop = _chatModal.body.scrollHeight;
427
452
  }
@@ -471,9 +496,29 @@ var LiveCard = (function () {
471
496
  _chatModal.body.innerHTML = '';
472
497
  if (!normalized.length) {
473
498
  _chatModal.body.innerHTML = '<div class="text-muted small">No messages yet.</div>';
499
+ _syncProcessingBar(nodeId);
474
500
  return;
475
501
  }
476
502
  normalized.forEach(function (m) { _appendModalChatMessage(m.role, m.text, m.files); });
503
+ _syncProcessingBar(nodeId);
504
+ }
505
+
506
+ function _syncProcessingBar(nodeId) {
507
+ if (!_chatModal.body) return;
508
+ const node = nodeId ? cfg.resolve(nodeId) : null;
509
+ const isProcessing = !!(node && node.card_data && node.card_data.__chat_signal && node.card_data.__chat_signal.processing);
510
+ let ind = _chatModal.body.querySelector('.lc-chat-processing');
511
+ if (isProcessing) {
512
+ if (!ind) {
513
+ ind = document.createElement('div');
514
+ ind.className = 'lc-chat-processing';
515
+ ind.innerHTML = '<span class="spinner-border spinner-border-sm" role="status" aria-label="AI working"></span><span>\u2728 AI working\u2026</span>';
516
+ _chatModal.body.appendChild(ind);
517
+ }
518
+ _chatModal.body.scrollTop = _chatModal.body.scrollHeight;
519
+ } else {
520
+ if (ind) ind.remove();
521
+ }
477
522
  }
478
523
 
479
524
  async function openChatModal(nodeId) {
@@ -2130,6 +2175,7 @@ var LiveCard = (function () {
2130
2175
  const nodeId = _chatModal.currentNodeId;
2131
2176
  if (!nodeId || !_chatModal.backdrop || !_chatModal.backdrop.classList.contains('lc-open')) return;
2132
2177
  _clearPendingModalChatMessages();
2178
+ _syncProcessingBar(nodeId);
2133
2179
  _refreshModalChatHistory(nodeId).catch(function () {});
2134
2180
  }
2135
2181
 
@@ -1459,7 +1459,7 @@ function nextEntryAfterFetchFailure(entry, reason) {
1459
1459
  return next;
1460
1460
  }
1461
1461
  function runtimePath(boardDir, cardId) {
1462
- return path__namespace.join(boardDir, `${cardId}.runtime.json`);
1462
+ return path__namespace.join(boardDir, cardId, "runtime.json");
1463
1463
  }
1464
1464
  function readRuntimeState(boardDir, cardId) {
1465
1465
  const p = runtimePath(boardDir, cardId);
@@ -1471,7 +1471,9 @@ function readRuntimeState(boardDir, cardId) {
1471
1471
  }
1472
1472
  }
1473
1473
  function writeRuntimeState(boardDir, cardId, state) {
1474
- fs__namespace.writeFileSync(runtimePath(boardDir, cardId), JSON.stringify(state, null, 2));
1474
+ const p = runtimePath(boardDir, cardId);
1475
+ fs__namespace.mkdirSync(path__namespace.dirname(p), { recursive: true });
1476
+ fs__namespace.writeFileSync(p, JSON.stringify(state, null, 2));
1475
1477
  }
1476
1478
  function appendEventToJournal(boardDir, event) {
1477
1479
  const journalPath = path__namespace.join(boardDir, JOURNAL_FILE);
@@ -1798,7 +1800,7 @@ function createBoardReactiveGraph(boardDir) {
1798
1800
  const sourcesData = {};
1799
1801
  for (const src of allSources) {
1800
1802
  if (src.outputFile) {
1801
- const filePath = path__namespace.join(boardDir, src.outputFile);
1803
+ const filePath = path__namespace.join(boardDir, cardId, src.outputFile);
1802
1804
  if (fs__namespace.existsSync(filePath)) {
1803
1805
  const raw = fs__namespace.readFileSync(filePath, "utf-8").trim();
1804
1806
  try {
@@ -2329,10 +2331,10 @@ function cmdSourceDataFetched(args) {
2329
2331
  process.exit(1);
2330
2332
  }
2331
2333
  const { cbk, rg, cid, b, d, cs } = payload;
2332
- const destPath = path__namespace.join(rg, d);
2334
+ const destPath = path__namespace.join(rg, cid, d);
2333
2335
  fs__namespace.mkdirSync(path__namespace.dirname(destPath), { recursive: true });
2334
2336
  fs__namespace.renameSync(tmpFile, destPath);
2335
- console.log(`[source-data-fetched] ${cid}.${b} \u2192 ${d}`);
2337
+ console.log(`[source-data-fetched] ${cid}.${b} \u2192 ${cid}/${d}`);
2336
2338
  const fetchedAt = (/* @__PURE__ */ new Date()).toISOString();
2337
2339
  const cbkDecoded = decodeCallbackToken2(cbk);
2338
2340
  if (!cbkDecoded) {
@@ -2664,7 +2666,7 @@ function cmdInferenceDone(args) {
2664
2666
  inferenceCompletedAt
2665
2667
  };
2666
2668
  fs__namespace.writeFileSync(cardPath, JSON.stringify(card, null, 2), "utf-8");
2667
- const runtimePath2 = path__namespace.join(dir, `${taskName}.runtime.json`);
2669
+ const runtimePath2 = path__namespace.join(dir, taskName, "runtime.json");
2668
2670
  let runtime = { _sources: {} };
2669
2671
  if (fs__namespace.existsSync(runtimePath2)) {
2670
2672
  try {
@@ -2674,6 +2676,7 @@ function cmdInferenceDone(args) {
2674
2676
  }
2675
2677
  const inferenceEntry = runtime._inferenceEntry ?? {};
2676
2678
  runtime._inferenceEntry = nextEntryAfterFetchDelivery(inferenceEntry, inferenceCompletedAt);
2679
+ fs__namespace.mkdirSync(path__namespace.dirname(runtimePath2), { recursive: true });
2677
2680
  fs__namespace.writeFileSync(runtimePath2, JSON.stringify(runtime, null, 2), "utf-8");
2678
2681
  appendEventToJournal(dir, {
2679
2682
  type: "task-progress",