yaml-flow 5.2.6 → 5.3.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.
Files changed (84) hide show
  1. package/README.md +6 -6
  2. package/board-livecards-server-runtime.js +260 -35
  3. package/browser/board-livegraph-engine.js +61 -33
  4. package/browser/board-livegraph-engine.js.map +1 -1
  5. package/browser/card-compute.js +18 -18
  6. package/browser/live-cards.js +317 -156
  7. package/browser/live-cards.schema.json +15 -10
  8. package/dist/board-livegraph-runtime/index.cjs +61 -33
  9. package/dist/board-livegraph-runtime/index.cjs.map +1 -1
  10. package/dist/board-livegraph-runtime/index.d.cts +1 -1
  11. package/dist/board-livegraph-runtime/index.d.ts +1 -1
  12. package/dist/board-livegraph-runtime/index.js +61 -33
  13. package/dist/board-livegraph-runtime/index.js.map +1 -1
  14. package/dist/card-compute/index.cjs +101 -39
  15. package/dist/card-compute/index.cjs.map +1 -1
  16. package/dist/card-compute/index.d.cts +13 -8
  17. package/dist/card-compute/index.d.ts +13 -8
  18. package/dist/card-compute/index.js +101 -39
  19. package/dist/card-compute/index.js.map +1 -1
  20. package/dist/cli/board-live-cards-cli.cjs +7205 -202
  21. package/dist/cli/board-live-cards-cli.cjs.map +1 -1
  22. package/dist/cli/board-live-cards-cli.d.cts +6 -6
  23. package/dist/cli/board-live-cards-cli.d.ts +6 -6
  24. package/dist/cli/board-live-cards-cli.js +7204 -202
  25. package/dist/cli/board-live-cards-cli.js.map +1 -1
  26. package/dist/continuous-event-graph/index.cjs +59 -31
  27. package/dist/continuous-event-graph/index.cjs.map +1 -1
  28. package/dist/continuous-event-graph/index.d.cts +2 -2
  29. package/dist/continuous-event-graph/index.d.ts +2 -2
  30. package/dist/continuous-event-graph/index.js +59 -31
  31. package/dist/continuous-event-graph/index.js.map +1 -1
  32. package/dist/index.cjs +126 -54
  33. package/dist/index.cjs.map +1 -1
  34. package/dist/index.d.cts +1 -1
  35. package/dist/index.d.ts +1 -1
  36. package/dist/index.js +126 -54
  37. package/dist/index.js.map +1 -1
  38. package/dist/{live-cards-bridge-CeNxiVcm.d.ts → live-cards-bridge-EQjytzI_.d.ts} +10 -5
  39. package/dist/{live-cards-bridge-z_rJCSbi.d.cts → live-cards-bridge-x5XREkXm.d.cts} +10 -5
  40. package/examples/browser/boards/portfolio-tracker/cards/holdings-table.json +1 -1
  41. package/examples/browser/boards/portfolio-tracker/cards/portfolio-form.json +1 -1
  42. package/examples/browser/boards/portfolio-tracker/cards/portfolio-risk-assessment.json +1 -1
  43. package/examples/browser/boards/portfolio-tracker/cards/portfolio-value.json +1 -1
  44. package/examples/browser/boards/portfolio-tracker/cards/price-fetch.json +2 -2
  45. package/examples/browser/boards/portfolio-tracker/cards/rebalancing-strategy.json +1 -1
  46. package/examples/browser/boards/portfolio-tracker/portfolio-tracker.js +10 -10
  47. package/examples/cli/step-machine-cli/portfolio-tracker/cards/holdings-table.json +1 -1
  48. package/examples/cli/step-machine-cli/portfolio-tracker/cards/portfolio-form.json +1 -1
  49. package/examples/cli/step-machine-cli/portfolio-tracker/cards/portfolio-value.json +1 -1
  50. package/examples/cli/step-machine-cli/portfolio-tracker/cards/price-fetch.json +2 -2
  51. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/add-cards-cli.js +1 -1
  52. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/update-holdings-cli.js +1 -1
  53. package/examples/cli/step-machine-cli/portfolio-tracker/portfolio-tracker.flow.yaml +1 -1
  54. package/examples/example-board/agent-instructions-cardlayout.md +29 -1
  55. package/examples/example-board/agent-instructions.md +271 -45
  56. package/examples/example-board/cards/card-concentration.json +8 -5
  57. package/examples/example-board/cards/card-market-prices.json +14 -9
  58. package/examples/example-board/cards/card-my-identity.json +28 -0
  59. package/examples/example-board/cards/card-portfolio-value.json +1 -1
  60. package/examples/example-board/cards/card-portfolio.json +1 -1
  61. package/examples/example-board/cards/card-rebalance-impact.json +65 -0
  62. package/examples/example-board/cards/card-rebalance-sim.json +67 -0
  63. package/examples/example-board/demo-chat-handler.js +2 -1
  64. package/examples/example-board/demo-server-config.json +6 -1
  65. package/examples/example-board/demo-server.js +91 -8
  66. package/examples/example-board/demo-shell-browser.html +6 -6
  67. package/examples/example-board/demo-shell-with-server.html +4 -4
  68. package/examples/example-board/demo-task-executor.js +457 -246
  69. package/examples/example-board/scripts/copilot_wrapper.bat +16 -0
  70. package/examples/example-board/scripts/copilot_wrapper_helper.ps1 +19 -10
  71. package/examples/example-board/scripts/workiq_wrapper.mjs +66 -0
  72. package/examples/npm-libs/continuous-event-graph/live-cards-board.ts +5 -5
  73. package/examples/npm-libs/continuous-event-graph/soc-incident-board.ts +3 -3
  74. package/examples/npm-libs/event-graph/research-pipeline.ts +5 -5
  75. package/examples/npm-libs/graph-of-graphs/multi-stage-etl.ts +9 -9
  76. package/examples/step-machine-cli/portfolio-tracker/cards/holdings-table.json +1 -1
  77. package/examples/step-machine-cli/portfolio-tracker/cards/portfolio-form.json +1 -1
  78. package/examples/step-machine-cli/portfolio-tracker/cards/portfolio-value.json +1 -1
  79. package/examples/step-machine-cli/portfolio-tracker/cards/price-fetch.json +3 -3
  80. package/examples/step-machine-cli/portfolio-tracker/handlers/add-cards-cli.js +1 -1
  81. package/examples/step-machine-cli/portfolio-tracker/handlers/update-holdings-cli.js +1 -1
  82. package/examples/step-machine-cli/portfolio-tracker/portfolio-tracker.flow.yaml +1 -1
  83. package/package.json +2 -2
  84. package/schema/live-cards.schema.json +15 -10
@@ -126,6 +126,22 @@ for /f "skip=50 tokens=*" %%f in ('dir /b /o-d "!LOG_DIR!\!LOG_AGENT!_*.log" 2^>
126
126
 
127
127
  powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0copilot_wrapper_helper.ps1" "%OUTPUT_FILE%" "!RESULT_TYPE!" "!RESULT_SHAPE_FILE!"
128
128
 
129
+ REM If JSON extraction failed (exit 2) and result_type is json, retry once with a
130
+ REM correction prompt in the same session (--resume SESSION_UUID continues the conversation).
131
+ SET "PS1_EXIT=!ERRORLEVEL!"
132
+ if "!PS1_EXIT!"=="2" (
133
+ SET "RETRY_PROMPT_FILE=%TEMP%\copilot-retry-!RANDOM!.txt"
134
+ (
135
+ echo Your previous response did not contain a valid JSON object.
136
+ echo Please respond with ONLY the JSON object — no markdown, no explanation, no preamble.
137
+ echo Start your response with { and end with }.
138
+ ) > "!RETRY_PROMPT_FILE!"
139
+ type "!RETRY_PROMPT_FILE!" | call copilot --allow-all --resume !SESSION_UUID! !MODEL_FLAG! > "%OUTPUT_FILE%" 2>&1
140
+ del "!RETRY_PROMPT_FILE!" 2>nul
141
+ REM Re-run PS1 with -IsRetry so it writes skeleton on second failure rather than exit 2
142
+ powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0copilot_wrapper_helper.ps1" "%OUTPUT_FILE%" "!RESULT_TYPE!" "!RESULT_SHAPE_FILE!" "-IsRetry"
143
+ )
144
+
129
145
  if exist "!CACHE_SESSION_PATH!" (
130
146
  for %%f in ("!CACHE_SESSION_PATH!\*") do (
131
147
  move /y "%%f" "%SESSION_DIR%\" >nul 2>&1
@@ -9,13 +9,16 @@
9
9
  # if result_shape_file is absent, accepts any valid JSON object
10
10
  # result_shape_file - (json result_type only) JSON file whose top-level keys are required in output
11
11
  #
12
- # raw result_type: right for chat responses and task executor sources.
12
+ # raw result_type: right for chat responses and task executor source_defs.
13
13
  # json result_type: right for structured calls where the input contained {prompt, result_shape}.
14
14
 
15
15
  param(
16
16
  [Parameter(Mandatory)][string]$OutputFile,
17
17
  [Parameter(Mandatory)][string]$ResultType,
18
- [string]$ResultShapeFile = ''
18
+ [string]$ResultShapeFile = '',
19
+ # When $true (retry pass), write shape-skeleton fallback instead of exiting with code 2.
20
+ # On first pass, exit 2 signals the caller (.bat) to retry with a correction prompt.
21
+ [switch]$IsRetry
19
22
  )
20
23
 
21
24
  if (-not (Test-Path $OutputFile)) { exit 0 }
@@ -47,7 +50,7 @@ foreach ($line in ($cleaned -split "`n")) {
47
50
  $t -match '^X ' -or # X failed tool ops
48
51
  $t -match '^\$ ' -or # $ shell commands
49
52
  $t -match '^[\u2514\u251c]' -or # └ ├ tree lines
50
- $t -match 'session-state.*\.json' -or # session-state file refs
53
+ $t -match 'session-state.*\.json' -or # session-state file projections
51
54
  $t -match 'agent.decision has been simulated' -or
52
55
  $t -match 'has been simulated and saved' -or
53
56
  $t -match '^\d+ (files?|lines?|matches?) found$' -or # "3 files found"
@@ -163,7 +166,7 @@ if (-not $foundJson) {
163
166
  if ($foundJson) {
164
167
  [IO.File]::WriteAllText($OutputFile, $foundJson, [Text.Encoding]::UTF8)
165
168
  } else {
166
- # No matching JSON found — record raw in noise file, write shape-skeleton fallback
169
+ # No matching JSON found — record raw in noise file for upstream visibility
167
170
  $NoiseFile = $OutputFile + '.noise'
168
171
  $fallbackNoise = "FALLBACK=no_json_match`nSHAPE_KEYS=$($shapeKeys -join ',')`nRAW_LENGTH=$($cleaned.Length)`n---`n$cleaned"
169
172
  if (Test-Path $NoiseFile) {
@@ -172,10 +175,16 @@ if ($foundJson) {
172
175
  } else {
173
176
  [IO.File]::WriteAllText($NoiseFile, $fallbackNoise, [Text.Encoding]::UTF8)
174
177
  }
175
- $fallback = if ($shapeKeys.Count -gt 0) {
176
- $obj = [ordered]@{}
177
- foreach ($k in $shapeKeys) { $obj[$k] = $null }
178
- $obj | ConvertTo-Json -Depth 2 -Compress
179
- } else { '{}' }
180
- [IO.File]::WriteAllText($OutputFile, $fallback, [Text.Encoding]::UTF8)
178
+ if ($IsRetry) {
179
+ # Second pass — write shape-skeleton so the card gets a structured (empty) result
180
+ $fallback = if ($shapeKeys.Count -gt 0) {
181
+ $obj = [ordered]@{}
182
+ foreach ($k in $shapeKeys) { $obj[$k] = $null }
183
+ $obj | ConvertTo-Json -Depth 2 -Compress
184
+ } else { '{}' }
185
+ [IO.File]::WriteAllText($OutputFile, $fallback, [Text.Encoding]::UTF8)
186
+ } else {
187
+ # First pass — exit 2 to signal the caller (.bat) to retry with a correction prompt
188
+ exit 2
189
+ }
181
190
  }
@@ -0,0 +1,66 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * workiq_wrapper.mjs — Calls the demo-server /api/workiq/ask proxy endpoint.
4
+ *
5
+ * Usage: node workiq_wrapper.mjs <out_file>
6
+ * WORKIQ_QUERY env var: the interpolated query string
7
+ * WORKIQ_SERVER_URL env var: base URL of demo-server (default: http://127.0.0.1:7799)
8
+ *
9
+ * The demo-server has a TTY so workiq can produce output there.
10
+ * Writes raw WorkIQ response text to <out_file>.
11
+ */
12
+
13
+ import http from 'node:http';
14
+ import fs from 'node:fs';
15
+
16
+ const outFile = process.argv[2];
17
+ const query = process.env.WORKIQ_QUERY;
18
+ const serverBase = (process.env.WORKIQ_SERVER_URL || 'http://127.0.0.1:7799').replace(/\/$/, '');
19
+
20
+ if (!outFile) { console.error('workiq_wrapper: missing <out_file> argument'); process.exit(1); }
21
+ if (!query) { console.error('workiq_wrapper: WORKIQ_QUERY env var not set'); process.exit(1); }
22
+
23
+ const body = JSON.stringify({ query });
24
+ const url = new URL('/api/workiq/ask', serverBase);
25
+
26
+ const reqOptions = {
27
+ hostname: url.hostname,
28
+ port: url.port || 80,
29
+ path: url.pathname,
30
+ method: 'POST',
31
+ headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(body) },
32
+ };
33
+
34
+ const req = http.request(reqOptions, (res) => {
35
+ let data = '';
36
+ res.on('data', chunk => { data += chunk; });
37
+ res.on('end', () => {
38
+ try {
39
+ const json = JSON.parse(data);
40
+ if (json.error) {
41
+ fs.writeFileSync(outFile, `workiq error: ${json.error}`, 'utf8');
42
+ process.exit(1);
43
+ }
44
+ fs.writeFileSync(outFile, json.response || '', 'utf8');
45
+ process.exit(0);
46
+ } catch {
47
+ fs.writeFileSync(outFile, data, 'utf8');
48
+ process.exit(0);
49
+ }
50
+ });
51
+ });
52
+
53
+ req.on('error', (err) => {
54
+ fs.writeFileSync(outFile, `workiq proxy error: ${err.message}\nIs demo-server running at ${serverBase}?`, 'utf8');
55
+ process.exit(1);
56
+ });
57
+
58
+ req.setTimeout(60_000, () => {
59
+ req.destroy();
60
+ fs.writeFileSync(outFile, 'workiq proxy timeout after 60s', 'utf8');
61
+ process.exit(1);
62
+ });
63
+
64
+ req.write(body);
65
+ req.end();
66
+
@@ -46,9 +46,9 @@ const cards: LiveCard[] = [
46
46
  card_data: {},
47
47
  data: { requires: ['market-feed'] },
48
48
  compute: {
49
- total: { fn: 'sum', input: 'sources.market-feed.prices' },
50
- avg: { fn: 'avg', input: 'sources.market-feed.prices' },
51
- count: { fn: 'count', input: 'sources.market-feed.prices' },
49
+ total: { fn: 'sum', input: 'source_defs.market-feed.prices' },
50
+ avg: { fn: 'avg', input: 'source_defs.market-feed.prices' },
51
+ count: { fn: 'count', input: 'source_defs.market-feed.prices' },
52
52
  },
53
53
  },
54
54
  {
@@ -132,8 +132,8 @@ const board: LiveBoard = {
132
132
  card_data: {},
133
133
  data: { requires: ['equity-feed', 'bond-feed'] },
134
134
  compute: {
135
- equity_total: { fn: 'sum', input: 'sources.equity-feed.prices' },
136
- bond_total: { fn: 'sum', input: 'sources.bond-feed.yields' },
135
+ equity_total: { fn: 'sum', input: 'source_defs.equity-feed.prices' },
136
+ bond_total: { fn: 'sum', input: 'source_defs.bond-feed.yields' },
137
137
  },
138
138
  },
139
139
  {
@@ -2,7 +2,7 @@
2
2
  * SOC Incident Board — Correct Model
3
3
  *
4
4
  * A Security Operations Center dashboard demonstrating:
5
- * • External data sources (alert-feed, threat-intel) — NO handlers
5
+ * • External data source_defs (alert-feed, threat-intel) — NO handlers
6
6
  * • Handler-driven compute (severity-score, blast-radius)
7
7
  * • Fire-and-forget side effects (slack-alert, create-ticket)
8
8
  * • Per-task refreshStrategy: 'once' on create-ticket (no duplicate tickets)
@@ -76,7 +76,7 @@ const config: GraphConfig = {
76
76
  refreshStrategy: 'data-changed',
77
77
  },
78
78
  tasks: {
79
- // External sources — no handler, data pushed externally
79
+ // External source_defs — no handler, data pushed externally
80
80
  'alert-feed': {
81
81
  provides: ['alert-feed'],
82
82
  description: 'SIEM alert feed (external push)',
@@ -203,7 +203,7 @@ console.log(`create-ticket: refreshStrategy 'once' (no duplicate tickets)\n`);
203
203
 
204
204
  console.log(`--- T0: Initial incident (${siemAlerts.length} alerts, ${threatIntelV1.length} IOCs) ---\n`);
205
205
 
206
- // Push both sources simultaneously — engine stores data, auto-hash computed
206
+ // Push both source_defs simultaneously — engine stores data, auto-hash computed
207
207
  graph.pushAll([
208
208
  {
209
209
  type: 'task-completed',
@@ -29,18 +29,18 @@ const graph: GraphConfig = {
29
29
  },
30
30
  tasks: {
31
31
  fetch_sources: {
32
- provides: ['raw-sources'],
32
+ provides: ['raw-asources'],
33
33
  description: 'Fetch source documents from the web',
34
34
  },
35
35
  analyse_sentiment: {
36
- requires: ['raw-sources'],
36
+ requires: ['raw-asources'],
37
37
  provides: ['sentiment-result'],
38
- description: 'Run sentiment analysis on sources',
38
+ description: 'Run sentiment analysis on source_defs',
39
39
  },
40
40
  analyse_entities: {
41
- requires: ['raw-sources'],
41
+ requires: ['raw-asources'],
42
42
  provides: ['entity-result'],
43
- description: 'Extract named entities from sources',
43
+ description: 'Extract named entities from source_defs',
44
44
  },
45
45
  merge_analysis: {
46
46
  requires: ['sentiment-result', 'entity-result'],
@@ -13,7 +13,7 @@
13
13
  * - Fan-out / fan-in in the outer graph
14
14
  *
15
15
  * Outer graph:
16
- * discover-sources → extract-batch → transform-batch → [load ∥ validate] → finalize
16
+ * discover-bsources → extract-batch → transform-batch → [load ∥ validate] → finalize
17
17
  *
18
18
  * Inner extract graph (event-graph, per source):
19
19
  * connect → [fetch-metadata ∥ fetch-schema] → snapshot-data
@@ -129,11 +129,11 @@ const outerGraph: GraphConfig = {
129
129
  id: 'etl-pipeline',
130
130
  settings: { completion: 'all-tasks-complete' },
131
131
  tasks: {
132
- 'discover-sources': {
133
- provides: ['sources-discovered'],
132
+ 'discover-bsources': {
133
+ provides: ['source_defs-discovered'],
134
134
  },
135
135
  'extract-batch': {
136
- requires: ['sources-discovered'],
136
+ requires: ['source_defs-discovered'],
137
137
  provides: ['extraction-complete'],
138
138
  },
139
139
  'transform-batch': {
@@ -191,7 +191,7 @@ async function runTransformFlow(record: { id: string; raw_data: string }) {
191
191
  // 4. Sample data
192
192
  // ============================================================================
193
193
 
194
- const sources = [
194
+ const source_defs = [
195
195
  { id: 'src-orders', db: 'orders_db' },
196
196
  { id: 'src-users', db: 'users_db' },
197
197
  { id: 'src-products', db: 'products_db' },
@@ -211,13 +211,13 @@ const records = [
211
211
  // ============================================================================
212
212
 
213
213
  const outerHandlers: Record<string, () => Promise<void>> = {
214
- 'discover-sources': async () => {
215
- console.log(` Found ${sources.length} data sources`);
214
+ 'discover-bsources': async () => {
215
+ console.log(` Found ${source_defs.length} data source_defs`);
216
216
  },
217
217
 
218
218
  'extract-batch': async () => {
219
- console.log(` Extracting from ${sources.length} sources (concurrency: 2, mode: event-graph)`);
220
- const result = await batch(sources, {
219
+ console.log(` Extracting from ${source_defs.length} source_defs (concurrency: 2, mode: event-graph)`);
220
+ const result = await batch(source_defs, {
221
221
  concurrency: 2,
222
222
  processor: runExtractGraph,
223
223
  onItemComplete: (src, res) => {
@@ -2,7 +2,7 @@
2
2
  "id": "holdings-table",
3
3
  "meta": { "title": "Holdings Table" },
4
4
  "requires": ["holdings", "prices"],
5
- "provides": [{ "bindTo": "table", "src": "computed_values.table" }],
5
+ "provides": [{ "bindTo": "table", "ref": "computed_values.table" }],
6
6
  "card_data": {},
7
7
  "compute": [
8
8
  {
@@ -6,7 +6,7 @@
6
6
  "provides": [
7
7
  {
8
8
  "bindTo": "holdings",
9
- "src": "card_data.holdings"
9
+ "ref": "card_data.holdings"
10
10
  }
11
11
  ],
12
12
  "card_data": {
@@ -2,7 +2,7 @@
2
2
  "id": "portfolio-value",
3
3
  "meta": { "title": "Portfolio Total Value" },
4
4
  "requires": ["table"],
5
- "provides": [{ "bindTo": "totalValue", "src": "computed_values.totalValue" }],
5
+ "provides": [{ "bindTo": "totalValue", "ref": "computed_values.totalValue" }],
6
6
  "card_data": {},
7
7
  "compute": [
8
8
  { "bindTo": "totalValue", "expr": "$sum(requires.table.rows.value)" }
@@ -2,14 +2,14 @@
2
2
  "id": "price-fetch",
3
3
  "meta": { "title": "Fetch Market Prices" },
4
4
  "requires": ["holdings"],
5
- "provides": [{ "bindTo": "prices", "src": "sources.prices" }],
5
+ "provides": [{ "bindTo": "prices", "ref": "fetched_sources.prices" }],
6
6
  "card_data": {},
7
- "sources": [
7
+ "source_defs": [
8
8
  { "cli": "node ../fetch-prices.js --tmp-file-name tmp_file1", "bindTo": "prices", "outputFile": "prices.json" }
9
9
  ],
10
10
  "view": {
11
11
  "elements": [
12
- { "kind": "table", "label": "Market Prices", "data": { "bind": "sources.prices" } }
12
+ { "kind": "table", "label": "Market Prices", "data": { "bind": "fetched_sources.prices" } }
13
13
  ]
14
14
  }
15
15
  }
@@ -12,7 +12,7 @@ try {
12
12
  process.exit(0);
13
13
  }
14
14
 
15
- runBoardCli(['add-cards', '--rg', boardDir, '--card-glob', cardsGlob]);
15
+ runBoardCli(['upsert-card', '--rg', boardDir, '--card-glob', cardsGlob]);
16
16
 
17
17
  writeResult({
18
18
  result: 'success',
@@ -22,7 +22,7 @@ try {
22
22
  card.state.holdings = holdings;
23
23
  fs.writeFileSync(cardPath, `${JSON.stringify(card, null, 2)}\n`, 'utf-8');
24
24
 
25
- runBoardCli(['update-card', '--rg', boardDir, '--card-id', 'portfolio-form', '--restart']);
25
+ runBoardCli(['upsert-card', '--rg', boardDir, '--card', cardPath, '--restart']);
26
26
 
27
27
  writeResult({
28
28
  result: 'success',
@@ -44,7 +44,7 @@ steps:
44
44
  expects_data: [board_dir, cards_template_dir]
45
45
  produces_data: [cards_dir]
46
46
  handler:
47
- cli: node %%BOARDCLI_CMD%% add-cards --rg "%%BOARD_DIR%%" --card-glob "%%CARDS_GLOB%%"
47
+ cli: node %%BOARDCLI_CMD%% upsert-card --rg "%%BOARD_DIR%%" --card-glob "%%CARDS_GLOB%%"
48
48
  result-mode: exit-code
49
49
  input-transforms:
50
50
  BOARD_DIR: board_dir
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yaml-flow",
3
- "version": "5.2.6",
3
+ "version": "5.3.0",
4
4
  "description": "Unified workflow engine: step-machine (sequential) + event-graph (stateless DAG) with pluggable storage",
5
5
  "author": "",
6
6
  "license": "MIT",
@@ -101,7 +101,7 @@
101
101
  "scripts": {
102
102
  "build": "tsup",
103
103
  "build:browser": "tsup --config tsup.browser.config.ts",
104
- "build:example": "node example-board-src/build.js",
104
+ "build:example": "node demo-src/example-board/build.js",
105
105
  "dev": "tsup --watch",
106
106
  "test": "vitest",
107
107
  "test:run": "vitest run",
@@ -80,10 +80,10 @@
80
80
  "type": "array",
81
81
  "items": {
82
82
  "type": "object",
83
- "required": ["bindTo", "src"],
83
+ "required": ["bindTo", "ref"],
84
84
  "properties": {
85
85
  "bindTo": { "type": "string", "description": "Token name published downstream" },
86
- "src": { "type": "string", "description": "Path to read value from (card_data.*, requires.*, fetched_sources.*, computed_values.*)" }
86
+ "ref": { "type": "string", "description": "Path to read value from (card_data.*, requires.*, fetched_sources.*, computed_values.*)" }
87
87
  }
88
88
  },
89
89
  "description": "Explicit bindings exposing computed or card_data values downstream as named tokens"
@@ -100,14 +100,19 @@
100
100
  },
101
101
 
102
102
  "source_def": {
103
- "description": "One source entry. The engine requires 'bindTo' (compute namespace key) and 'outputFile' (delivery signal path). bindTo and outputFile must be unique across all sources in a card. Every other property is yours — add whatever your task-executor needs: kind, url, headers, mailbox, channel, model, query, etc. The full object is passed verbatim as the --in JSON to the executor.",
103
+ "description": "One source entry. The engine requires 'bindTo' (compute namespace key) and 'outputFile' (delivery signal path). bindTo and outputFile must be unique across all source_defs in a card. Every other property is yours — add whatever your task-executor needs: kind, url, headers, mailbox, channel, model, query, etc. The full object is passed verbatim as the --in JSON to the executor.",
104
104
  "type": "object",
105
105
  "required": ["bindTo", "outputFile"],
106
106
  "additionalProperties": true,
107
107
  "properties": {
108
108
  "bindTo": { "type": "string", "description": "Key under fetched_sources.* available in compute expressions" },
109
109
  "outputFile": { "type": "string", "description": "Board-relative path the executor writes its JSON result to. Presence of this file signals delivery." },
110
- "optionalForCompletionGating": { "type": "boolean", "default": false, "description": "When true this source does not gate card completion. Default false when absent, so sources are completion-gating by default." },
110
+ "projections": {
111
+ "type": "object",
112
+ "description": "Named data projections from card_data or requires, evaluated before the executor is called. Each key is a ref name; each value is a JSONata expression rooted at card_data or requires. The resolved values are passed to the executor as _projections. fetched_sources, computed_values, and source_defs are not accessible here — sources run before those exist.",
113
+ "additionalProperties": { "type": "string" }
114
+ },
115
+ "optionalForCompletionGating": { "type": "boolean", "default": false, "description": "When true this source does not gate card completion. Default false when absent, so source_defs are completion-gating by default." },
111
116
  "timeout": { "type": "integer", "minimum": 0, "default": 120000, "description": "Executor/script timeout in ms. Default: 120 000 (2 min)." },
112
117
  "script": { "type": "string", "description": "Legacy direct-run: shell command executed when no .task-executor is registered. stdout is captured as the result." }
113
118
  }
@@ -121,7 +126,7 @@
121
126
  "kind": {
122
127
  "enum": ["metric", "table", "editable-table", "chart", "form", "filter", "list",
123
128
  "notes", "todo", "alert", "narrative", "badge", "text",
124
- "markdown", "custom", "actions"]
129
+ "markdown", "ref", "custom", "actions"]
125
130
  },
126
131
  "label": { "type": "string", "description": "Heading above this element" },
127
132
  "className": { "type": "string", "description": "Bootstrap grid class, e.g. 'col-12 col-md-6'" },
@@ -215,7 +220,7 @@
215
220
  },
216
221
 
217
222
  "title": "LiveCard",
218
- "description": "A unified card node. Behavior depends on which sections are present (sources, compute, view, etc.)",
223
+ "description": "A unified card node. Behavior depends on which sections are present (source_defs, compute, view, etc.)",
219
224
  "type": "object",
220
225
  "required": ["id"],
221
226
  "additionalProperties": false,
@@ -258,22 +263,22 @@
258
263
  },
259
264
  "llm_task_completion_inference": {
260
265
  "type": "object",
261
- "description": "Runtime state written by the inference adapter (advanced/undocumented). Prefer the standard sources → compute → provides pattern for LLM-based signals.",
266
+ "description": "Runtime state written by the inference adapter (advanced/undocumented). Prefer the standard source_defs → compute → provides pattern for LLM-based signals.",
262
267
  "properties": {
263
268
  "inferenceRequested": { "type": "string", "format": "date-time", "description": "Timestamp when the latest inference request was initiated" },
264
269
  "inferenceCompletedAt": { "type": "string", "format": "date-time", "description": "Timestamp when the latest inference request completed" },
265
270
  "isTaskCompleted": { "type": "boolean", "description": "Whether the task is considered complete by the adapter" },
266
271
  "reasoning": { "type": "string", "description": "Explanation of completion decision" },
267
- "evidence": { "type": "array", "description": "Supporting evidence from sources/compute" }
272
+ "evidence": { "type": "array", "description": "Supporting evidence from source_defs/compute" }
268
273
  },
269
274
  "additionalProperties": true
270
275
  }
271
276
  },
272
277
  "additionalProperties": true
273
278
  },
274
- "sources": {
279
+ "source_defs": {
275
280
  "type": "array",
276
- "description": "Source entries. Each entry is passed verbatim to the board's .task-executor (registered via init --task-executor) as the --in JSON file. The executor fetches/generates the data and writes JSON to --out. If no executor is registered, the built-in executor runs the entry's 'cli' command directly. Sources gate completion by default. Set optionalForCompletionGating: true for enrichment-only sources that should not block task-completed.",
281
+ "description": "Source entries. Each entry is passed verbatim to the board's .task-executor (registered via init --task-executor) as the --in JSON file. The executor fetches/generates the data and writes JSON to --out. If no executor is registered, the built-in executor runs the entry's 'cli' command directly. Sources gate completion by default. Set optionalForCompletionGating: true for enrichment-only source_defs that should not block task-completed.",
277
282
  "items": { "$ref": "#/definitions/source_def" }
278
283
  },
279
284
  "compute": {