@mulmoclaude/core 4.9.0 → 4.9.1

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 (85) hide show
  1. package/assets/helps/bug-report-faq.md +12 -0
  2. package/assets/helps/error-recovery.md +140 -26
  3. package/assets/helps/sandbox.md +2 -2
  4. package/dist/collection/index.cjs +3 -2
  5. package/dist/collection/index.cjs.map +1 -1
  6. package/dist/collection/index.js +3 -2
  7. package/dist/collection/index.js.map +1 -1
  8. package/dist/collection/registry/server/index.cjs +3 -3
  9. package/dist/collection/registry/server/index.js +3 -3
  10. package/dist/collection/server/index.cjs +2 -2
  11. package/dist/collection/server/index.js +2 -2
  12. package/dist/collection-watchers/index.cjs +4 -4
  13. package/dist/collection-watchers/index.js +4 -4
  14. package/dist/{discovery-BY-nMCuh.js → discovery-BkIKvdh0.js} +3 -3
  15. package/dist/{discovery-BY-nMCuh.js.map → discovery-BkIKvdh0.js.map} +1 -1
  16. package/dist/{discovery-2TvulIz8.cjs → discovery-tvFo-bUJ.cjs} +3 -3
  17. package/dist/{discovery-2TvulIz8.cjs.map → discovery-tvFo-bUJ.cjs.map} +1 -1
  18. package/dist/{dist-D8zokgGo.js → dist-BzoA9pDR.js} +6 -1
  19. package/dist/dist-BzoA9pDR.js.map +1 -0
  20. package/dist/{dist-CsgSfWwR.cjs → dist-Gj7ygaW2.cjs} +6 -1
  21. package/dist/dist-Gj7ygaW2.cjs.map +1 -0
  22. package/dist/errorMessage-BQNpSYdT.js +1 -0
  23. package/dist/errorMessage-CT-va_Pv.cjs +1 -0
  24. package/dist/errors-C7TI_t_4.js +10 -0
  25. package/dist/errors-C7TI_t_4.js.map +1 -0
  26. package/dist/errors-CdXsfFCF.cjs +15 -0
  27. package/dist/errors-CdXsfFCF.cjs.map +1 -0
  28. package/dist/feeds/server/index.cjs +4 -4
  29. package/dist/feeds/server/index.js +4 -4
  30. package/dist/google/index.cjs +3 -2
  31. package/dist/google/index.cjs.map +1 -1
  32. package/dist/google/index.js +3 -2
  33. package/dist/google/index.js.map +1 -1
  34. package/dist/{listen-UZA68Upt.cjs → listen-BXthb-9C.cjs} +2 -2
  35. package/dist/{listen-UZA68Upt.cjs.map → listen-BXthb-9C.cjs.map} +1 -1
  36. package/dist/{listen-D5av7GzB.js → listen-Dca-NoBi.js} +2 -2
  37. package/dist/{listen-D5av7GzB.js.map → listen-Dca-NoBi.js.map} +1 -1
  38. package/dist/notifier/index.cjs +1 -1
  39. package/dist/notifier/index.js +1 -1
  40. package/dist/notifier/types.d.ts +20 -11
  41. package/dist/{notifier-DZJj0sOh.cjs → notifier-B65Mvngb.cjs} +12 -7
  42. package/dist/notifier-B65Mvngb.cjs.map +1 -0
  43. package/dist/{notifier-DhnwR82U.js → notifier-vAp5t6wL.js} +12 -7
  44. package/dist/notifier-vAp5t6wL.js.map +1 -0
  45. package/dist/{promptSafety-Bte62OvU.js → promptSafety-BpBTh5xO.js} +2 -2
  46. package/dist/{promptSafety-Bte62OvU.js.map → promptSafety-BpBTh5xO.js.map} +1 -1
  47. package/dist/{promptSafety-Cv-SoTd4.cjs → promptSafety-caJWd1ZQ.cjs} +2 -2
  48. package/dist/{promptSafety-Cv-SoTd4.cjs.map → promptSafety-caJWd1ZQ.cjs.map} +1 -1
  49. package/dist/remote-host/index.cjs +1 -1
  50. package/dist/remote-host/index.js +1 -1
  51. package/dist/remote-host/server/index.cjs +4 -3
  52. package/dist/remote-host/server/index.cjs.map +1 -1
  53. package/dist/remote-host/server/index.js +4 -3
  54. package/dist/remote-host/server/index.js.map +1 -1
  55. package/dist/{remote-host-CFnl1I-L.js → remote-host-Cjox2F2J.js} +2 -2
  56. package/dist/{remote-host-CFnl1I-L.js.map → remote-host-Cjox2F2J.js.map} +1 -1
  57. package/dist/{remote-host-EQPlccB9.cjs → remote-host-DhAHBYzN.cjs} +2 -2
  58. package/dist/{remote-host-EQPlccB9.cjs.map → remote-host-DhAHBYzN.cjs.map} +1 -1
  59. package/dist/remote-view/index.cjs +1 -1
  60. package/dist/remote-view/index.js +1 -1
  61. package/dist/scheduler/index.cjs +2 -1
  62. package/dist/scheduler/index.cjs.map +1 -1
  63. package/dist/scheduler/index.js +2 -1
  64. package/dist/scheduler/index.js.map +1 -1
  65. package/dist/{server-DXUt34Rt.cjs → server-Div1i_k5.cjs} +5 -4
  66. package/dist/{server-DXUt34Rt.cjs.map → server-Div1i_k5.cjs.map} +1 -1
  67. package/dist/{server-DyYwfNB6.js → server-Dk9x1VLG.js} +5 -4
  68. package/dist/{server-DyYwfNB6.js.map → server-Dk9x1VLG.js.map} +1 -1
  69. package/dist/utils/index.cjs +3 -10
  70. package/dist/utils/index.js +2 -9
  71. package/dist/whisper/index.cjs +2 -1
  72. package/dist/whisper/index.cjs.map +1 -1
  73. package/dist/whisper/index.js +2 -1
  74. package/dist/whisper/index.js.map +1 -1
  75. package/dist/wiki/index.cjs +1 -1
  76. package/dist/wiki/index.js +1 -1
  77. package/dist/workspace-setup/index.js +5 -0
  78. package/dist/workspace-setup/index.js.map +1 -1
  79. package/package.json +3 -3
  80. package/dist/dist-CsgSfWwR.cjs.map +0 -1
  81. package/dist/dist-D8zokgGo.js.map +0 -1
  82. package/dist/notifier-DZJj0sOh.cjs.map +0 -1
  83. package/dist/notifier-DhnwR82U.js.map +0 -1
  84. package/dist/utils/index.cjs.map +0 -1
  85. package/dist/utils/index.js.map +0 -1
@@ -56,6 +56,18 @@ settings is the user's opt-in. The RemoteHost channel must also be connected —
56
56
  what supplies the Firebase auth, so with the phone link down the send is a no-op by design. A user
57
57
  who never connected a phone has nothing to receive the push regardless of the setting.
58
58
 
59
+ ## MulmoClaude is answering with a different model than I expected
60
+
61
+ configKey: chatModel
62
+ source: server/agent/config.ts
63
+
64
+ Read `chatModel` from the live settings. Absent means MulmoClaude passes no `--model` at all and the
65
+ CLI resolves the model from `~/.claude/settings.json` — the same file the VS Code and Cursor Claude
66
+ Code extensions write their `/model` pick to, so a switch made in another editor changes MulmoClaude
67
+ too, with nothing on screen to say so. Setting the key from Settings → Model is what pins MulmoClaude
68
+ independently of that shared file. A session already running keeps its model until the next turn, so
69
+ "it changed partway through a conversation" is the expected shape of this, not a second bug.
70
+
59
71
  ## My chats never get titles or summaries
60
72
 
61
73
  configKey: chatIndex
@@ -124,11 +124,17 @@ Report body:
124
124
 
125
125
  ```markdown
126
126
  ## What happened
127
+
127
128
  ## What I expected
129
+
128
130
  ## Steps to reproduce
131
+
129
132
  1.
133
+
130
134
  ## Environment
135
+
131
136
  (paste the diagnostics report here)
137
+
132
138
  ## Attachments
133
139
  ```
134
140
 
@@ -280,7 +286,7 @@ naming the file:
280
286
  quotes, unquoted key.
281
287
  - `role file does not match the role schema, skipping` — the `issues`
282
288
  field names each field, e.g. `icon: Invalid input: expected string,
283
- received undefined`. All of `id`, `name`, `icon`, `prompt`,
289
+ received undefined`. All of `id`, `name`, `icon`, `prompt`,
284
290
  `availablePlugins` are required; `availablePlugins` must be an array
285
291
  even for one entry.
286
292
  - `role file is empty, skipping` — zero-length or whitespace only.
@@ -328,7 +334,7 @@ it:
328
334
  `my role.json`, rejected as `Invalid role id 'my role'.` Neither name
329
335
  reaches the role. Renaming is the only fix.
330
336
  - `… the id is not a usable role id` — the reverse, e.g. `"id": "my
331
- role"` in `designer.json`. `delete designer` still works, and renaming
337
+ role"` in `designer.json`. `delete designer` still works, and renaming
332
338
  to `my role.json` would take that away. Change the `id`.
333
339
  - `… neither is a usable role id` — pick one id that matches the pattern
334
340
  and use it for the file name and the `id` together.
@@ -926,12 +932,12 @@ They are genuinely different failures — a permanent load failure, a startup ra
926
932
  and guessing between them is what makes this expensive. Since #2842 the log answers it directly,
927
933
  so read these three before forming a theory:
928
934
 
929
- | Log line | What it tells you |
930
- |---|---|
931
- | `spawning agent … broker=tsx` | This install is on the SLOW path: the bundle is missing, so the broker is transcoded from source on every spawn (seconds to tens of seconds over a Windows/macOS bind mount). A `broker bundle missing` warn accompanies it once per process. |
932
- | `[mcp] broker ready bootMs=… initializeMs=…` | The broker DID connect, and how long it took. `broker cold boot is slow` replaces it past 5 s. |
933
- | `brokerEverReady=false reason=never-ready` on the retry warn | No beacon arrived for that chat, and the host kept looking until the beacon's own delivery budget was spent — the broker did not come up at all. The turn is NOT replayed: a replay would sit out another full connect wait and end in the same error. |
934
- | `reason=ready-during-wait` on the retry warn | The beacon arrived while the host waited, so the broker lost the race by a moment and IS connected now. The turn is replayed, which is what fixes this one. |
935
+ | Log line | What it tells you |
936
+ | ------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
937
+ | `spawning agent … broker=tsx` | This install is on the SLOW path: the bundle is missing, so the broker is transcoded from source on every spawn (seconds to tens of seconds over a Windows/macOS bind mount). A `broker bundle missing` warn accompanies it once per process. |
938
+ | `[mcp] broker ready bootMs=… initializeMs=…` | The broker DID connect, and how long it took. `broker cold boot is slow` replaces it past 5 s. |
939
+ | `brokerEverReady=false reason=never-ready` on the retry warn | No beacon arrived for that chat, and the host kept looking until the beacon's own delivery budget was spent — the broker did not come up at all. The turn is NOT replayed: a replay would sit out another full connect wait and end in the same error. |
940
+ | `reason=ready-during-wait` on the retry warn | The beacon arrived while the host waited, so the broker lost the race by a moment and IS connected now. The turn is replayed, which is what fixes this one. |
935
941
  | `brokerEverStarted=` on the `MCP tools were unavailable` warn | Whether the broker PROCESS ever existed, which is a different question from whether it answered. `true` with `brokerEverReady=false` means it launched and never finished booting — the boot is the problem (the mount, the `tsx` path). `false` means it never launched — the spawn is the problem. Diagnostic only, and not authenticated: under Docker everything needed to forge it sits in the per-session MCP config inside the workspace mount, so read it as evidence about a healthy install rather than as proof against a hostile one. |
936
942
 
937
943
  ### Fix
@@ -968,7 +974,7 @@ so read these three before forming a theory:
968
974
 
969
975
  - The `google` tool (or a `google.calendar.*` remote command) fails with **"Google account not linked on this host"**.
970
976
  - **"Google sign-in service unreachable"** or **"Google sign-in service returned HTTP …"**.
971
- - **"multiple client_secret_*.json files found"**.
977
+ - **"multiple client_secret\_*.json files found"**.
972
978
  - **"Google Calendar API: HTTP 403"** with a hint about enabling the API.
973
979
  - **"Google Calendar API: HTTP 403 — Request had insufficient authentication scopes"** when pushing
974
980
  a collection to a calendar that is NOT in the account's own calendar list.
@@ -1121,8 +1127,7 @@ Pass the file instead. `putItems` accepts **`itemsFile`** — an absolute path t
1121
1127
  a JSON file holding the array of record objects, read by the host:
1122
1128
 
1123
1129
  ```jsonc
1124
- { "action": "putItems", "slug": "slots", "mode": "create",
1125
- "itemsFile": "/absolute/path/to/generated-slots.json" }
1130
+ { "action": "putItems", "slug": "slots", "mode": "create", "itemsFile": "/absolute/path/to/generated-slots.json" }
1126
1131
  ```
1127
1132
 
1128
1133
  Write the generated file **under the workspace**. Paths outside it are refused:
@@ -1144,17 +1149,17 @@ Rules that make it fail cleanly rather than silently:
1144
1149
 
1145
1150
  Reading the refusal you got:
1146
1151
 
1147
- | Message | What it means |
1148
- | --- | --- |
1149
- | `must be an ABSOLUTE path` | You passed a relative path. Pass the full one. |
1150
- | `must be inside the workspace` | The file is outside the workspace (or a symlink out of it). Regenerate it under the workspace. |
1151
- | `is a symbolic link` | Symlinks are never followed. Pass the real path. |
1152
- | `changed while it was being opened` | The file was replaced mid-call. Finish writing it, then call putItems. |
1153
- | `grew while it was being read` | The file was still being written. Wait for the script to finish, then call putItems. |
1154
- | `could not read \`itemsFile\`` | The host cannot see that path — the usual cause is a file written to a temp dir outside the mount. Write it under the workspace. |
1155
- | `is not a regular file` | The path is a directory, device, or fifo. |
1156
- | `could not be read as JSON` | The file exists and was read, but does not parse. This is YOUR file's shape, not a host problem — check the script that wrote it (a truncated write, a trailing comma, log output mixed into the file). |
1157
- | `must hold a non-empty JSON array of record objects` | It parsed, but is `[]`, an object, or an array of scalars. The file must be `[{…}, {…}]` — the same row objects you would have passed as `items`. |
1152
+ | Message | What it means |
1153
+ | ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
1154
+ | `must be an ABSOLUTE path` | You passed a relative path. Pass the full one. |
1155
+ | `must be inside the workspace` | The file is outside the workspace (or a symlink out of it). Regenerate it under the workspace. |
1156
+ | `is a symbolic link` | Symlinks are never followed. Pass the real path. |
1157
+ | `changed while it was being opened` | The file was replaced mid-call. Finish writing it, then call putItems. |
1158
+ | `grew while it was being read` | The file was still being written. Wait for the script to finish, then call putItems. |
1159
+ | `could not read \`itemsFile\`` | The host cannot see that path — the usual cause is a file written to a temp dir outside the mount. Write it under the workspace. |
1160
+ | `is not a regular file` | The path is a directory, device, or fifo. |
1161
+ | `could not be read as JSON` | The file exists and was read, but does not parse. This is YOUR file's shape, not a host problem — check the script that wrote it (a truncated write, a trailing comma, log output mixed into the file). |
1162
+ | `must hold a non-empty JSON array of record objects` | It parsed, but is `[]`, an object, or an array of scalars. The file must be `[{…}, {…}]` — the same row objects you would have passed as `items`. |
1158
1163
 
1159
1164
  Do NOT spawn the MCP bridge yourself. A hand-written JSON-RPC client fails
1160
1165
  invisibly and can leave a partially written collection that nothing reports.
@@ -1196,10 +1201,14 @@ the calendar can place. So format the string, never convert it:
1196
1201
 
1197
1202
  ```js
1198
1203
  // WRONG — appends `Z` and shifts the hours by the generating machine's offset
1199
- { startAt: new Date(`${day}T08:00`).toISOString() } // "2026-08-17T15:00:00.000Z"
1204
+ {
1205
+ startAt: new Date(`${day}T08:00`).toISOString();
1206
+ } // "2026-08-17T15:00:00.000Z"
1200
1207
 
1201
1208
  // RIGHT — the clock you meant, written as the clock
1202
- { startAt: `${day}T08:00` } // "2026-08-17T08:00"
1209
+ {
1210
+ startAt: `${day}T08:00`;
1211
+ } // "2026-08-17T08:00"
1203
1212
  ```
1204
1213
 
1205
1214
  The conversion is the more expensive half: had the format passed, a Tokyo court's
@@ -1249,7 +1258,7 @@ second line, with the plugin-load errors above it, is what a bug report needs.
1249
1258
  ### Symptoms
1250
1259
 
1251
1260
  - Reading or writing a collection fails with `shared collection unavailable:
1252
- connect remote-host first`.
1261
+ connect remote-host first`.
1253
1262
  - A collection whose `schema.json` declares `"storage": { "type": "firestore" }`
1254
1263
  never appears in the list at all.
1255
1264
  - Reads and writes fail with `permission-denied` from Firestore even though the
@@ -1269,7 +1278,7 @@ The backend deliberately fails loudly instead of returning nothing.
1269
1278
 
1270
1279
  2. **The collection never appears** — discovery REFUSED the schema, and the
1271
1280
  reason is in the server log under `collections` (`schema.json rejected after
1272
- validation, skipping`). For a shared collection the usual reason is the app
1281
+ validation, skipping`). For a shared collection the usual reason is the app
1273
1282
  declaration: `apps/{aid}/collections/{cid}/items` needs an `aid`, which comes
1274
1283
  from `app.json` at the repository root, not from the schema.
1275
1284
 
@@ -1382,6 +1391,14 @@ treatment, and the workspace mounted if you want it to follow the port.
1382
1391
  message.
1383
1392
  - The platform token (bot token, app token) was revoked or regenerated.
1384
1393
  - The bridge process is not running at all. Check before assuming anything above.
1394
+ Since #3084 a bridge that died says why on its last line — `[<transport>]
1395
+ unhandled rejection — exiting: …` or `[<transport>] uncaught exception —
1396
+ exiting: …`, naming the transport. A bridge stopped on purpose names the
1397
+ signal: Ctrl-C prints `[<transport>] SIGINT — shutting down`, while a plain
1398
+ `kill <pid>` sends SIGTERM and prints `[<transport>] SIGTERM — shutting down`.
1399
+ **No such line and the process gone** means either an older npm build (they
1400
+ had no handlers at all, so a missed `await` left only a stack trace) or a kill
1401
+ no handler can catch (`kill -9` / SIGKILL, or the OOM killer).
1385
1402
 
1386
1403
  ### What to collect if none of it explains the silence
1387
1404
 
@@ -1391,6 +1408,54 @@ the server's `[server] listening port=…` line, and whether
1391
1408
  port, and did the bridge agree" — which is what every one of these turns out to
1392
1409
  be.
1393
1410
 
1411
+ ## A webhook bridge exits at startup, or never binds its port
1412
+
1413
+ ### Symptoms
1414
+
1415
+ One of the webhook bridges — `line`, `line-works`, `google-chat`, `messenger`,
1416
+ `teams`, `twilio-sms`, `viber`, `webhook`, `whatsapp` — exits immediately after
1417
+ being started, printing one of:
1418
+
1419
+ ```text
1420
+ Port 3002 is already in use. Set LINE_BRIDGE_PORT to a free port, or LINE_BRIDGE_PORT=0 to let the OS pick one.
1421
+ LINE_BRIDGE_PORT="302a" is not a usable port. Set it to an integer from 0 to 65535 (LINE_BRIDGE_PORT=0 asks the OS for a free port), or unset it to use 3002.
1422
+ ```
1423
+
1424
+ Both name the env var to change, and each bridge has its own — `WEBHOOK_PORT`,
1425
+ `TWILIO_WEBHOOK_PORT`, `VIBER_WEBHOOK_PORT` and so on. Read the message rather
1426
+ than guessing the name.
1427
+
1428
+ ### "Already in use" is usually the server, not a second bridge
1429
+
1430
+ The server's port walk (3002-3021) covers the whole bridge band (3002-3013), so
1431
+ a server that found 3001 busy and moved forward can be sitting on a bridge's
1432
+ default. Check what the server actually bound before moving the bridge:
1433
+
1434
+ ```bash
1435
+ cat "${MULMOCLAUDE_WORKSPACE_PATH:-$HOME/mulmoclaude}/.server-port"
1436
+ lsof -nP -iTCP:3002 -sTCP:LISTEN
1437
+ ```
1438
+
1439
+ Two ways out, both fine:
1440
+
1441
+ - Give the bridge a port outside the server's band: `LINE_BRIDGE_PORT=3200`.
1442
+ - Let the OS choose: `LINE_BRIDGE_PORT=0`. The startup banner then names the
1443
+ port it actually got — read the banner, not the env var, when tunnelling to it
1444
+ (`Webhook listening on http://localhost:52431/webhook`).
1445
+
1446
+ ### Before #3084 both of these were silent
1447
+
1448
+ A bridge of that vintage reads its port as `Number(process.env.X) || <default>`
1449
+ and calls a bare `app.listen`. So a **typo ran on the default without a word**
1450
+ (`302a` → `NaN` → falsy → the default), `X=0` was impossible (falsy too), and a
1451
+ busy port surfaced as an unhandled `EADDRINUSE` with no mention of which env var
1452
+ to change. If neither message above appears and the bridge simply dies, or it is
1453
+ answering on a port you did not ask for, that is the old build: reinstall it
1454
+ (`npm i -g @mulmobridge/<platform>@latest`).
1455
+
1456
+ A bridge that starts but never replies is a different failure — see "A messaging
1457
+ bridge's bot does not reply, and nothing errors" above.
1458
+
1394
1459
  ## `renderShapeScript` says Chromium is not installed
1395
1460
 
1396
1461
  `renderShapeScript` rasterises a ShapeScript model by driving Puppeteer's
@@ -1420,3 +1485,52 @@ The same message with `(launch failed: …)` appended means the browser is
1420
1485
  installed but would not start — usually a sandbox with no permission to
1421
1486
  spawn it. Report the parenthesised reason to the user rather than the
1422
1487
  install hint alone.
1488
+
1489
+ ## The server refuses to start — "MulmoClaude is already running against this workspace"
1490
+
1491
+ ### Symptoms
1492
+
1493
+ - `yarn dev`, `yarn server` or `npx mulmoclaude` exits immediately with:
1494
+ `MulmoClaude is already running against this workspace at http://localhost:<port>`
1495
+ - It happens even with a free port named explicitly (`PORT=3100 yarn dev`).
1496
+
1497
+ ### Why
1498
+
1499
+ Two servers over one workspace overwrite each other's `.session-token`. After
1500
+ that a stateless plugin dispatch authenticates cleanly against the *wrong*
1501
+ server, while the session-scoped `/api/internal/tool-result` push lands where
1502
+ the session does not exist and is dropped — so plugin views simply never render
1503
+ on one of the two and nothing reports an error. The guard refuses that setup
1504
+ rather than letting it happen quietly (#3079).
1505
+
1506
+ The check reads `<workspace>/.server-port` and probes the port it names, so it
1507
+ is about the WORKSPACE, not the port: a busy 3001 held by some other program
1508
+ still walks forward as before.
1509
+
1510
+ ### Fix
1511
+
1512
+ Usually the message is right and there is already a server to use — open the URL
1513
+ it printed.
1514
+
1515
+ To run a genuinely separate second instance, give it its own workspace (no flag
1516
+ needed, this is the supported shape):
1517
+
1518
+ ```bash
1519
+ MULMOCLAUDE_WORKSPACE_PATH=~/mulmoclaude-scratch PORT=3100 yarn dev
1520
+ ```
1521
+
1522
+ If the first server was killed hard (`kill -9`, a crashed container) the sidecar
1523
+ can name a port something else has since taken; the probe treats a non-MulmoClaude
1524
+ answer as "nobody there", so that case does not stop a launch. A stop therefore
1525
+ means a real instance answered.
1526
+
1527
+ To share one workspace between two servers anyway — accepting the token stomping:
1528
+
1529
+ ```bash
1530
+ MULMOCLAUDE_ALLOW_MULTIPLE_INSTANCES=1 yarn dev
1531
+ ```
1532
+
1533
+ Use the env var for `yarn dev`. The `--allow-multiple-instances` flag works on
1534
+ `npx mulmoclaude` and `yarn server`, but **not** on `yarn dev`: that is a compound
1535
+ `a && b && c` script, yarn appends extra args to the last command only, and the
1536
+ guard that stops the launch runs in the first one.
@@ -23,11 +23,11 @@ The container runs with `--cap-drop ALL` and as the host user's UID/GID, so it h
23
23
 
24
24
  ## Disabling the Sandbox
25
25
 
26
- Set the environment variable `DISABLE_SANDBOX=1` to always run the agent directly on the host, even when Docker is available. Equivalently, pass the `--disable-sandbox` CLI flag — `yarn dev --disable-sandbox` or `npx mulmoclaude --disable-sandbox`. The flag form is handy on Windows PowerShell (no inline `VAR=value` syntax), in IDE / launcher run configs, and for quick ad-hoc debugging. Both set the same internal switch; the env var stays supported in parallel.
26
+ Set the environment variable `DISABLE_SANDBOX=1` to always run the agent directly on the host, even when Docker is available. Equivalently, pass the `--disable-sandbox` CLI flag — `npx mulmoclaude --disable-sandbox` or `yarn server --disable-sandbox` (**not** `yarn dev`, whose compound script drops trailing args; use the env var there). The flag form is handy on Windows PowerShell (no inline `VAR=value` syntax), in IDE / launcher run configs, and for quick ad-hoc debugging. Both set the same internal switch; the env var stays supported in parallel.
27
27
 
28
28
  ## Debug aids (opt-in env vars)
29
29
 
30
- These flags exist for development / debugging only. Off by default so production runs aren't surprised. Each has an equivalent `--flag` CLI form (drop the `=1`, kebab-case the name) accepted by both `yarn dev` and `npx mulmoclaude` — e.g. `DISABLE_SANDBOX=1` ≡ `--disable-sandbox`, `PERSIST_TOOL_CALLS=1` ≡ `--persist-tool-calls`, `DISABLE_MACOS_REMINDER_NOTIFICATIONS=1` ≡ `--disable-macos-reminders`, `JOURNAL_FORCE_RUN_ON_STARTUP=1` ≡ `--journal-force-run`, `CHAT_INDEX_FORCE_RUN_ON_STARTUP=1` ≡ `--chat-index-force-run`. Secret-bearing vars (auth token, API keys) have no flag form on purpose — argv is visible via `ps` / shell history.
30
+ These flags exist for development / debugging only. Off by default so production runs aren't surprised. Each has an equivalent `--flag` CLI form (drop the `=1`, kebab-case the name) accepted by `npx mulmoclaude` and `yarn server` — but **not** `yarn dev`, which drops trailing args — e.g. `DISABLE_SANDBOX=1` ≡ `--disable-sandbox`, `PERSIST_TOOL_CALLS=1` ≡ `--persist-tool-calls`, `DISABLE_MACOS_REMINDER_NOTIFICATIONS=1` ≡ `--disable-macos-reminders`, `JOURNAL_FORCE_RUN_ON_STARTUP=1` ≡ `--journal-force-run`, `CHAT_INDEX_FORCE_RUN_ON_STARTUP=1` ≡ `--chat-index-force-run`. Secret-bearing vars (auth token, API keys) have no flag form on purpose — argv is visible via `ps` / shell history.
31
31
 
32
32
  - `DISABLE_SANDBOX=1` — see above. Bypasses the Docker sandbox.
33
33
  - `PERSIST_TOOL_CALLS=1` — also persist `tool_call` events to the session jsonl alongside `tool_result`. Useful for reading the args sent to a tool after the run is over (page refresh / server restart). Off by default because args can be large and may carry payload bytes (inline images, full MulmoScript JSON) you didn't expect to land in the jsonl. See [issue #1096](https://github.com/receptron/mulmoclaude/issues/1096) for the rationale.
@@ -1,9 +1,10 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_dist = require("../dist-CsgSfWwR.cjs");
2
+ const require_dist = require("../dist-Gj7ygaW2.cjs");
3
3
  const require_itemId = require("../itemId-CGT2J7YK.cjs");
4
- const require_promptSafety = require("../promptSafety-Cv-SoTd4.cjs");
4
+ const require_promptSafety = require("../promptSafety-caJWd1ZQ.cjs");
5
5
  const require_project = require("../project-C1ep9pvo.cjs");
6
6
  const require_iconGlyph = require("../iconGlyph-fSBp6k4J.cjs");
7
+ require("../errorMessage-CT-va_Pv.cjs");
7
8
  //#region src/collection/core/presentCollection.ts
8
9
  var TOOL_NAME = "presentCollection";
9
10
  /** Stamp a host's opaque project scope onto a card payload. The ONLY way a
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":[],"sources":["../../src/collection/core/presentCollection.ts","../../src/collection/core/enumColors.ts","../../src/collection/core/draft.ts","../../src/collection/core/chatSeed.ts","../../src/collection/core/shortcutInfo.ts","../../src/collection/core/sortItems.ts","../../src/collection/core/sortValueOf.ts","../../src/collection/core/textSearch.ts","../../src/collection/core/recordKeys.ts","../../src/collection/core/itemLabel.ts","../../src/collection/core/ontologyGraph.ts"],"sourcesContent":["// presentCollection tool — definition + pure server-side executor.\n//\n// Isomorphic (no Vue, no Node): bundled to the browser as the plugin's\n// `execute`, and run server-side via the host's plugin dispatch route. The\n// executor only validates + echoes the addressing; the live schema + items\n// are fetched client-side by the View through the host's /api/collections\n// routes, so a bad slug surfaces as the View's \"not found\" state.\n\nimport type { ToolContext, ToolDefinition, ToolResult } from \"gui-chat-protocol\";\n\nexport const TOOL_NAME = \"presentCollection\";\n\n/** Render payload carried in the tool result's `data` field; the View mounts\n * off these. Same shape as the tool args. */\nexport interface PresentCollectionData {\n /** Slug of the collection to display (e.g. \"clients\", \"invoices\"). */\n collectionSlug: string;\n /** Optional primary-key value of a single item to open on mount. */\n itemId?: string | undefined;\n /** Which ROOT this card's collection lives in, as a host-opaque scope token.\n *\n * A collection's identity is `(root, slug)`, but a card payload named only\n * the slug — so the host re-resolved it through whatever binding was current\n * when the card RENDERED, which in a multi-root host may be a different\n * project than the one the card was made in. A card built in project A could\n * then read project B's data.\n *\n * **Stamped by the HOST, never accepted from tool arguments.** The tool args\n * are a model-controlled channel: a model that could name the scope could\n * name another project's, and the card — and any token minted for it — would\n * address a project the user never opened in this conversation. So\n * `executePresentCollection` DROPS any `scope` it is handed, and a host adds\n * its own with {@link withCardScope} when it applies the result, where it\n * already knows which binding produced the call.\n *\n * Host-opaque: pass an opaque project id, NEVER an absolute path — this\n * payload reaches the browser and, through a view, an LLM-authored iframe.\n *\n * Absent — the single-workspace case — the payload and every reconciliation\n * decision are exactly what they were before this field existed. */\n scope?: string | undefined;\n}\n\n/** Stamp a host's opaque project scope onto a card payload. The ONLY way a\n * scope gets in: the executor refuses to take one from tool arguments (see\n * `PresentCollectionData.scope`). `undefined` returns the payload untouched,\n * so a single-workspace host calling this unconditionally still produces a\n * payload with no `scope` property at all. */\nexport function withCardScope(data: PresentCollectionData, scope: string | undefined): PresentCollectionData {\n const trimmed = typeof scope === \"string\" ? scope.trim() : \"\";\n return trimmed.length > 0 ? { ...data, scope: trimmed } : data;\n}\n\n/** A card's identity for reconciliation. Two cards match only when they name\n * the same collection IN THE SAME SCOPE, so two projects' `tasks` cards stay\n * two cards. With no scope on either side this is the slug, unchanged. */\nexport function collectionCardKey(data: Pick<PresentCollectionData, \"collectionSlug\" | \"scope\">): string {\n return data.scope === undefined ? data.collectionSlug : `${data.scope}\\u0000${data.collectionSlug}`;\n}\n\n/** Do two card payloads address the same card? The reconciliation predicate a\n * host should use instead of comparing slugs. */\nexport function sameCollectionCard(\n one: Pick<PresentCollectionData, \"collectionSlug\" | \"scope\">,\n other: Pick<PresentCollectionData, \"collectionSlug\" | \"scope\">,\n): boolean {\n return collectionCardKey(one) === collectionCardKey(other);\n}\n\nexport type PresentCollectionArgs = PresentCollectionData;\n\nexport const TOOL_DEFINITION: ToolDefinition = {\n type: \"function\",\n name: TOOL_NAME,\n description:\n \"Display a schema-driven collection inline in the chat as an interactive, editable card. Shows the collection's list of records. Pass `itemId` to open one specific record on mount.\",\n parameters: {\n type: \"object\",\n properties: {\n collectionSlug: {\n type: \"string\",\n description: \"The slug of the collection to display (e.g. 'clients', 'invoices', 'contacts').\",\n },\n itemId: {\n type: \"string\",\n description: \"Optional primary-key value of a single record to open in detail view on mount. Omit to show the full list.\",\n },\n },\n required: [\"collectionSlug\"],\n },\n prompt: `After making changes to schema-driven collections, use ${TOOL_NAME} to present either the collection or the item`,\n};\n\n/** A trimmed non-empty string, or undefined. The tool's args arrive from an\n * LLM (and `scope` from the host), so every field is validated the same way\n * rather than trusted. */\nconst cleaned = (value: unknown): string | undefined => (typeof value === \"string\" && value.trim().length > 0 ? value.trim() : undefined);\n\n/** Normalise the tool args into the card payload, or `null` when the required\n * slug is missing. Pure and exported so the addressing rules are testable\n * without going through a `ToolResult`.\n *\n * `scope` is deliberately NOT read from the args — see\n * `PresentCollectionData.scope`. Everything here comes from a model-controlled\n * channel, and the scope decides which project the card reads; a host stamps\n * it afterwards with `withCardScope`.\n *\n * Keys are added one at a time rather than spread with `undefined` values, so\n * a payload with no `itemId` OMITS the property entirely — which is what keeps\n * a single-workspace card byte-identical to what it has always been, in both\n * `deepEqual` and JSON. */\nexport const toPresentCollectionData = (args: PresentCollectionArgs): PresentCollectionData | null => {\n const collectionSlug = cleaned(args?.collectionSlug);\n if (!collectionSlug) return null;\n const itemId = cleaned(args.itemId);\n return { collectionSlug, ...(itemId ? { itemId } : {}) };\n};\n\nexport const executePresentCollection = async (\n _context: ToolContext,\n args: PresentCollectionArgs,\n): Promise<ToolResult<PresentCollectionData, PresentCollectionData>> => {\n const data = toPresentCollectionData(args);\n if (!data) {\n return {\n message: \"presentCollection error: collectionSlug is required\",\n instructions: \"Tell the user you couldn't display the collection because no collection was specified, and ask which collection they mean.\",\n };\n }\n const { collectionSlug, itemId } = data;\n const target = itemId ? `${collectionSlug} / ${itemId}` : collectionSlug;\n return {\n message: `Presented collection ${target}`,\n // `data` is the view's source (also the host's render-eligibility signal);\n // `jsonData` is what the LLM sees. Same payload, two audiences.\n data,\n jsonData: data,\n // Phrased to stay correct for BOTH storage kinds without loading the\n // schema (this executor is deliberately pure/isomorphic): a writable\n // collection offers edit/create/delete in the card; a read-only\n // `dataSource` collection hides those and changes flow through its\n // data file instead.\n instructions:\n \"The collection has been presented to the user as an interactive card. They can browse and open records directly; on a writable collection they can also edit, create, and delete (a read-only dataSource collection shows no edit controls — its records change by editing the backing data file). No further action is needed unless they ask.\",\n };\n};\n","// A standard, ordered colour palette for `enum` field values, shared by every\n// collection surface (list, calendar, kanban). Each value in an\n// enum's `values` array is assigned the palette entry at its index — cycling\n// when an enum declares more values than the palette holds — so colouring is\n// automatic and consistent without any per-value schema config.\n//\n// Tailwind only detects class names that appear as complete string literals,\n// so every surface's classes are spelled out per colour below rather than\n// built from a colour name at runtime.\n//\n// One enum is special: the field a schema's `notifyWhen` targets (the\n// \"notification enum\"). Its flagged values read the notification severity\n// colours — the first flagged value (most urgent) red, the rest amber — and\n// every other value reads neutral grey, mirroring the notification bell\n// (red = urgent, amber = nudge) rather than the rotating palette.\n// `resolveEnumColor` encapsulates that rule.\n\nimport { fieldText, fieldTextOrNull } from \"./fieldText\";\nimport type { CollectionSchema } from \"./schema\";\n\nexport interface EnumColorClasses {\n /** Stat-card style: border + fill + text + hover. */\n card: string;\n /** Small status dot (kanban column header). */\n dot: string;\n /** Pill / badge / inline `<select>` fill + text (no border width). */\n badge: string;\n /** Border colour, paired with a `border` width class by the caller. */\n border: string;\n}\n\n// The palette deliberately EXCLUDES the warm warning band (red / orange /\n// amber): those are reserved for the notification-enum severity colours\n// (`ENUM_ALERT` red, `ENUM_NUDGE` amber), so a normal enum value can never\n// draw a colour that reads like a notification. Eight well-separated cool /\n// green / magenta hues keep enough variety for wide enums.\nconst PALETTE: readonly EnumColorClasses[] = [\n {\n card: \"border-indigo-200 bg-indigo-50 text-indigo-600 hover:bg-indigo-100\",\n dot: \"bg-indigo-500\",\n badge: \"bg-indigo-100 text-indigo-700\",\n border: \"border-indigo-200\",\n },\n { card: \"border-sky-200 bg-sky-50 text-sky-600 hover:bg-sky-100\", dot: \"bg-sky-500\", badge: \"bg-sky-100 text-sky-700\", border: \"border-sky-200\" },\n { card: \"border-cyan-200 bg-cyan-50 text-cyan-600 hover:bg-cyan-100\", dot: \"bg-cyan-500\", badge: \"bg-cyan-100 text-cyan-700\", border: \"border-cyan-200\" },\n { card: \"border-teal-200 bg-teal-50 text-teal-600 hover:bg-teal-100\", dot: \"bg-teal-500\", badge: \"bg-teal-100 text-teal-700\", border: \"border-teal-200\" },\n {\n card: \"border-emerald-200 bg-emerald-50 text-emerald-600 hover:bg-emerald-100\",\n dot: \"bg-emerald-500\",\n badge: \"bg-emerald-100 text-emerald-700\",\n border: \"border-emerald-200\",\n },\n { card: \"border-lime-200 bg-lime-50 text-lime-600 hover:bg-lime-100\", dot: \"bg-lime-500\", badge: \"bg-lime-100 text-lime-700\", border: \"border-lime-200\" },\n {\n card: \"border-violet-200 bg-violet-50 text-violet-600 hover:bg-violet-100\",\n dot: \"bg-violet-500\",\n badge: \"bg-violet-100 text-violet-700\",\n border: \"border-violet-200\",\n },\n {\n card: \"border-fuchsia-200 bg-fuchsia-50 text-fuchsia-600 hover:bg-fuchsia-100\",\n dot: \"bg-fuchsia-500\",\n badge: \"bg-fuchsia-100 text-fuchsia-700\",\n border: \"border-fuchsia-200\",\n },\n];\n\n/** Neutral styling for the empty / Uncategorized bucket — never a palette\n * colour, so an unset or unknown value reads grey across every surface. */\nexport const ENUM_NEUTRAL: EnumColorClasses = {\n card: \"border-slate-200 bg-slate-50 text-slate-500 hover:bg-slate-100\",\n dot: \"bg-slate-300\",\n badge: \"bg-slate-100 text-slate-500\",\n border: \"border-slate-200\",\n};\n\n/** The urgent notification colour (red), matching the bell's `urgent`\n * severity. The first value a schema's `notifyWhen` flags reads this. */\nexport const ENUM_ALERT: EnumColorClasses = {\n card: \"border-red-200 bg-red-50 text-red-600 hover:bg-red-100\",\n dot: \"bg-red-500\",\n badge: \"bg-red-100 text-red-700\",\n border: \"border-red-200\",\n};\n\n/** The nudge notification colour (amber), matching the bell's `nudge`\n * severity. Flagged `notifyWhen` values after the first read this. */\nexport const ENUM_NUDGE: EnumColorClasses = {\n card: \"border-amber-200 bg-amber-50 text-amber-600 hover:bg-amber-100\",\n dot: \"bg-amber-500\",\n badge: \"bg-amber-100 text-amber-700\",\n border: \"border-amber-200\",\n};\n\n/** Classes for the enum value at `index` in its field's `values` array. A\n * negative index (value unset or not among the declared values) reads\n * neutral. */\nexport function enumColorClasses(index: number): EnumColorClasses {\n if (index < 0) return ENUM_NEUTRAL;\n return PALETTE[index % PALETTE.length] ?? ENUM_NEUTRAL;\n}\n\n/** Index of `value` within an enum field's declared `values`, or -1 when the\n * value is empty / unknown (→ neutral). */\nexport function enumValueIndex(values: readonly string[] | undefined, value: unknown): number {\n // No text form (array/object) means no enum member — \"[object Object]\" must\n // not index into the palette.\n const text = fieldTextOrNull(value);\n if (text === null || text === \"\") return -1;\n return values?.indexOf(text) ?? -1;\n}\n\n/** The flagged values when `fieldKey` is the schema's `notifyWhen` target (the\n * \"notification enum\"); undefined for every other field. */\nfunction notifyValuesFor(schema: CollectionSchema, fieldKey: string): readonly string[] | undefined {\n const spec = schema.notifyWhen;\n return spec && spec.field === fieldKey ? spec.in : undefined;\n}\n\n/** Resolve a value's colour for enum field `fieldKey`:\n * - Notification enum (`notifyWhen` targets it): the first flagged value (in\n * `notifyWhen.in` order, the most urgent) reads notification red, the rest\n * amber, and every non-flagged value neutral grey.\n * - Any other enum: the standard palette by the value's declared index. */\nexport function resolveEnumColor(schema: CollectionSchema, fieldKey: string, value: unknown): EnumColorClasses {\n const notifyValues = notifyValuesFor(schema, fieldKey);\n if (notifyValues) {\n const str = fieldText(value);\n const rank = notifyValues.indexOf(str);\n if (rank < 0) return ENUM_NEUTRAL;\n return rank === 0 ? ENUM_ALERT : ENUM_NUDGE;\n }\n const spec = schema.fields[fieldKey];\n return enumColorClasses(enumValueIndex(spec?.type === \"enum\" ? spec.values : undefined, value));\n}\n","// Pure edit-draft logic for the collection record form, extracted from\n// CollectionView.vue so the parent (build draft / save / validate) and\n// the extracted CollectionRecordPanel (per-row table mutators) share one\n// implementation. No Vue, no I/O — every function maps a draft + schema\n// to a value, so the omission/validation semantics are unit-testable.\n\nimport { fieldText } from \"./fieldText\";\nimport { fieldVisible } from \"./actionVisible\";\nimport { COMPUTED_TYPES } from \"./schema\";\nimport type { CollectionFieldSpec as FieldSpec, CollectionFieldType as FieldType, CollectionItem, CollectionSchema } from \"./schema\";\nimport type { EditState, TableRowDraft } from \"./uiTypes\";\n\n/** Build a row draft, reading each sub-field's persisted value via\n * `readSub`. An empty row passes a reader that always returns\n * `undefined`; a row-from-item reads the source record. Boolean\n * presence uses `typeof raw === \"boolean\"` so an existing explicit\n * `false` is recorded as present and round-trips on a no-op save. */\nfunction buildTableRowDraft(subFields: Record<string, FieldSpec>, readSub: (subKey: string) => unknown): TableRowDraft {\n const text: Record<string, string> = {};\n const bool: Record<string, boolean> = {};\n const boolOriginallyPresent: Record<string, boolean> = {};\n const boolTouched: Record<string, boolean> = {};\n for (const [subKey, subField] of Object.entries(subFields)) {\n const raw = readSub(subKey);\n if (subField.type === \"boolean\") {\n bool[subKey] = raw === true;\n boolOriginallyPresent[subKey] = typeof raw === \"boolean\";\n boolTouched[subKey] = false;\n } else {\n text[subKey] = fieldText(raw);\n }\n }\n return { text, bool, boolOriginallyPresent, boolTouched };\n}\n\n/** A fresh, empty row draft for a `table` field's sub-schema. */\nexport function emptyRow(subFields: Record<string, FieldSpec>): TableRowDraft {\n return buildTableRowDraft(subFields, () => undefined);\n}\n\n/** Build a row draft from an existing persisted row. */\nexport function rowFromItem(item: Record<string, unknown>, subFields: Record<string, FieldSpec>): TableRowDraft {\n return buildTableRowDraft(subFields, (subKey) => item[subKey]);\n}\n\n/** Own-property boolean read: a field named `toString` / `constructor` /\n * `__proto__` must read `false` here, not inherit a truthy Object.prototype\n * member and force an untouched boolean into every saved record. */\nfunction ownFlag(flags: Record<string, boolean>, key: string): boolean {\n return Object.hasOwn(flags, key) && flags[key] === true;\n}\n\n/** Decide whether a boolean field's draft value should be emitted (vs.\n * omitted so a downstream default applies). */\nfunction shouldEmitBoolean(state: EditState, key: string, field: FieldSpec): boolean {\n return ownFlag(state.boolOriginallyPresent, key) || ownFlag(state.boolTouched, key) || Boolean(field.required);\n}\n\n/** Convert a scalar draft slot to its persisted form. `undefined` = omit. */\nfunction scalarDraftToValue(raw: string | undefined, fieldType: FieldType): unknown {\n if (raw === undefined || raw === \"\") return undefined;\n if (fieldType === \"number\" || fieldType === \"money\") {\n const num = Number(raw);\n return Number.isFinite(num) ? num : raw;\n }\n return raw;\n}\n\n/** Convert one table row draft to its persisted record. */\nfunction rowDraftToRecord(rowDraft: TableRowDraft, subFields: Record<string, FieldSpec>): Record<string, unknown> {\n const row: Record<string, unknown> = {};\n for (const [subKey, subField] of Object.entries(subFields)) {\n if (subField.type === \"boolean\") {\n const value = ownFlag(rowDraft.bool, subKey);\n if (ownFlag(rowDraft.boolOriginallyPresent, subKey) || ownFlag(rowDraft.boolTouched, subKey) || value || subField.required) row[subKey] = value;\n continue;\n }\n const value = scalarDraftToValue(rowDraft.text[subKey], subField.type);\n if (value !== undefined) row[subKey] = value;\n }\n return row;\n}\n\n/** Convert a full edit draft to the record to persist.\n *\n * Collected as entries rather than assigned into an accumulator: a field may\n * be named `__proto__` (JSON.parse hands that over as an own key, so it\n * reaches here like any other), and `record[key] = value` would run the\n * prototype setter for that one name — the user's value would vanish between\n * the form and the save, with nothing to see. `fromEntries` defines an own\n * property whatever the name is. The read side of this file already guards the\n * same hazard with `ownFlag` (Codex review on #2910). */\nexport function draftToRecord(state: EditState, schema: CollectionSchema): CollectionItem {\n const entries: [string, unknown][] = [];\n for (const [key, field] of Object.entries(schema.fields)) {\n if (COMPUTED_TYPES.has(field.type)) continue; // never persisted (toggle projects an enum field)\n if (field.type === \"boolean\") {\n if (shouldEmitBoolean(state, key, field)) entries.push([key, ownFlag(state.bool, key)]);\n continue;\n }\n if (field.type === \"table\" && field.of) {\n const subFields = field.of;\n entries.push([key, (state.table[key] ?? []).map((rowDraft) => rowDraftToRecord(rowDraft, subFields))]);\n continue;\n }\n const value = scalarDraftToValue(state.text[key], field.type);\n if (value !== undefined) entries.push([key, value]);\n }\n return Object.fromEntries(entries);\n}\n\n/** Normalise a raw inline-edit input (table-cell checkbox/select) to its\n * persisted form. Mirrors `draftToRecord`'s boolean strictness; an empty\n * enum selection (the placeholder option) clears the field via `undefined`. */\nexport function coerceInlineValue(field: FieldSpec, raw: boolean | string): unknown {\n if (field.type === \"boolean\") return raw === true;\n return raw === \"\" ? undefined : raw;\n}\n\n/** Build the full record to PUT for a single-cell inline edit, without\n * mutating `item`. A `undefined` value omits the key (enum cleared),\n * matching `draftToRecord`'s omission semantics. */\nexport function buildUpdatedRecord(item: CollectionItem, key: string, value: unknown): CollectionItem {\n if (value === undefined) {\n const { [key]: __omit, ...rest } = item;\n return rest;\n }\n return { ...item, [key]: value };\n}\n\n/** Empty for required-field validation — NOT a truthiness check (a\n * numeric `0` is a filled value). */\nfunction isMissingDraftValue(value: unknown): boolean {\n return value === undefined || value === null || value === \"\";\n}\n\n/** Label of the first required table sub-field empty in any row, else null. */\nfunction firstMissingTableSubField(field: FieldSpec, rows: TableRowDraft[] | undefined): string | null {\n if (field.type !== \"table\" || !rows) return null;\n for (const [rowIdx, row] of rows.entries()) {\n for (const [subKey, subField] of Object.entries(field.of)) {\n if (!subField.required || subField.type === \"boolean\") continue;\n if (isMissingDraftValue(row.text[subKey])) return `${field.label} #${rowIdx + 1}: ${subField.label}`;\n }\n }\n return null;\n}\n\n/** Field types the required check never flags: booleans always hold a\n * value, and the computed/projected kinds (COMPUTED_TYPES — the shared\n * set, so a future computed type can't drift past this check) have no\n * draft input at all. */\nconst NEVER_MISSING_TYPES: ReadonlySet<FieldType> = new Set<FieldType>([\"boolean\", ...COMPUTED_TYPES]);\n\nfunction validateOneField(key: string, field: FieldSpec, draft: EditState, record: CollectionItem): string | null {\n // A `when`-hidden field has no input the user can fill — never missing.\n if (!fieldVisible(field, record)) return null;\n if (field.type === \"table\" && field.of) {\n const rows = draft.table[key];\n if (field.required && (!rows || rows.length === 0)) return field.label;\n return firstMissingTableSubField(field, rows);\n }\n if (!field.required) return null;\n if (draft.mode === \"create\" && field.primary === true) return null; // server auto-generates id\n if (NEVER_MISSING_TYPES.has(field.type)) return null;\n return isMissingDraftValue(draft.text[key]) ? field.label : null;\n}\n\n/** Human-readable label of the first missing required field, or null. */\nexport function firstMissingRequiredField(draft: EditState, schema: CollectionSchema): string | null {\n const record = draftToRecord(draft, schema);\n for (const [key, field] of Object.entries(schema.fields)) {\n const missing = validateOneField(key, field, draft, record);\n if (missing) return missing;\n }\n return null;\n}\n","// The slash-command chat seed a collection view sends to the agent when the\n// user starts a chat from a skill-backed collection or one of its records.\n// Skill-backed collections have a `/<slug>` command, so seed that. (Feeds are\n// skill-less and seeded differently; that branch stays in the view because it\n// is i18n-driven.)\n\n/** `/<slug> <message>`, or `/<slug> id=<itemId> <message>` when a specific\n * record is addressed. The `id=` selector goes BEFORE the free-text message so\n * the skill's argument parser reads it as a flag rather than as prose. */\nexport function skillCommandSeed(slug: string, message: string, itemId?: string): string {\n return itemId ? `/${slug} id=${itemId} ${message}` : `/${slug} ${message}`;\n}\n","// The one place an index row becomes the `{slug,title,icon,color}` a pinned\n// shortcut reconciles against.\n//\n// Both the collections index and the feeds index did this inline, and the\n// awkward part is identical in each: `color` must be ADDED only when there is\n// one, never assigned as `undefined` — the shortcut shapes declare it narrow\n// (see `CollectionSummary.color`) because the remote-host handlers pass them\n// through `Jsonify`, which drops `undefined` from a member union. Writing that\n// out twice is how the two copies drift.\n\nimport type { CollectionShortcutInfo } from \"./uiTypes\";\n\n/** The fields an index row supplies. Both `CollectionSummary` and\n * `FeedSummary` satisfy it; `icon` is optional because a feed row may carry\n * an empty one and rely on the surface's default. */\nexport interface ShortcutInfoSource {\n slug: string;\n title: string;\n icon?: string | undefined;\n color?: string | undefined;\n}\n\n/** An index row as shortcut-reconcile input. `fallbackIcon` stands in when the\n * row names no icon (feeds do this); `color` is omitted entirely rather than\n * set to `undefined` when the row has none. */\nexport function toShortcutInfo(row: ShortcutInfoSource, fallbackIcon: string): CollectionShortcutInfo {\n return {\n slug: row.slug,\n title: row.title,\n icon: row.icon && row.icon.length > 0 ? row.icon : fallbackIcon,\n ...(row.color === undefined ? {} : { color: row.color }),\n };\n}\n","// Sorting for the collection list table. The header sort toggle (next to\n// each field title) cycles a single active column through none → asc →\n// desc → none; this module turns that state plus a per-field value\n// extractor into an ordered copy of the rows.\n//\n// Two invariants the comparator guarantees:\n// 1. Rows with an empty/missing value for the sorted field always sink\n// to the bottom, regardless of direction.\n// 2. Ties (and the empty group) keep their original order — the sort is\n// stable via the captured source index.\n//\n// Field-type → comparable mapping (see `isSortableField`):\n// string/text/email → string · number/money → numeric ·\n// date/datetime → epoch-ms · enum → declared-index · boolean/toggle/flag →\n// false<true · ref → display label · derived → its display type.\n// markdown/table/image/file/embed get no sort button.\n\nimport { fieldTextOrNull } from \"./fieldText\";\nimport type { CollectionItem, CollectionFieldSpec, CollectionFieldType } from \"./schema\";\n\nexport type SortDirection = \"asc\" | \"desc\";\n\nexport interface SortState {\n /** Field key of the single active sort column. */\n field: string;\n direction: SortDirection;\n}\n\n/** A row's comparable value for the active field. Exactly one of `num` /\n * `str` is set when not empty; `empty` rows always sort last. */\nexport interface SortValue {\n empty: boolean;\n num?: number;\n str?: string;\n}\n\nconst EMPTY: SortValue = { empty: true };\n\n/** Field types that render no value text in the table, so offer no sort. */\nconst NON_SORTABLE: ReadonlySet<CollectionFieldType> = new Set<CollectionFieldType>([\"markdown\", \"table\", \"image\", \"file\", \"embed\", \"backlinks\", \"rollup\"]);\n\nexport function isSortableField(field: CollectionFieldSpec): boolean {\n return !NON_SORTABLE.has(field.type);\n}\n\n/** Cycle one column's state: none → asc → desc → none. */\nexport function nextSortDirection(current: SortDirection | null): SortDirection | null {\n if (current === null) return \"asc\";\n if (current === \"asc\") return \"desc\";\n return null;\n}\n\n// ── SortValue constructors (one per comparable kind) ────────────────\n\nexport function numericSortValue(raw: unknown): SortValue {\n if (raw == null || raw === \"\") return EMPTY;\n const num = typeof raw === \"number\" ? raw : Number(raw);\n return Number.isFinite(num) ? { empty: false, num } : EMPTY;\n}\n\nexport function stringSortValue(raw: unknown): SortValue {\n // An array/object has no sort key — treat it as empty (sorts last) instead of\n // grouping every such record together under \"[object Object]\".\n const str = fieldTextOrNull(raw);\n if (str === null) return EMPTY;\n return str.trim() === \"\" ? EMPTY : { empty: false, str };\n}\n\nexport function dateSortValue(raw: unknown): SortValue {\n const text = fieldTextOrNull(raw);\n if (text === null || text === \"\") return EMPTY;\n const epoch = Date.parse(text);\n // Unparseable dates fall back to a lexical compare rather than vanishing.\n return Number.isNaN(epoch) ? stringSortValue(raw) : { empty: false, num: epoch };\n}\n\n/** Enum sorts by the value's index in the declared `values` list. A value\n * outside the list (or unset) is treated as empty → last. */\nexport function enumSortValue(values: readonly string[] | undefined, raw: unknown): SortValue {\n const text = fieldTextOrNull(raw);\n if (text === null || text === \"\") return EMPTY;\n const idx = values ? values.indexOf(text) : -1;\n return idx < 0 ? EMPTY : { empty: false, num: idx };\n}\n\n/** Boolean / toggle: false < true, never empty (unset reads as false). */\nexport function boolSortValue(checked: boolean): SortValue {\n return { empty: false, num: checked ? 1 : 0 };\n}\n\n// ── Comparator + driver ─────────────────────────────────────────────\n\nexport function compareSortValues(left: SortValue, right: SortValue): number {\n if (left.num !== undefined && right.num !== undefined) return left.num - right.num;\n const leftStr = left.str ?? String(left.num ?? \"\");\n const rightStr = right.str ?? String(right.num ?? \"\");\n return leftStr.localeCompare(rightStr);\n}\n\n/** Stable sort of `items` by `valueOf`. Empties always last; ties hold\n * source order. Returns a new array (does not mutate `items`). */\nexport function sortItems(items: readonly CollectionItem[], direction: SortDirection, valueOf: (item: CollectionItem) => SortValue): CollectionItem[] {\n const dir = direction === \"asc\" ? 1 : -1;\n return items\n .map((item, index) => ({ item, index, sv: valueOf(item) }))\n .sort((left, right) => {\n if (left.sv.empty || right.sv.empty) {\n if (left.sv.empty && right.sv.empty) return left.index - right.index;\n return left.sv.empty ? 1 : -1;\n }\n const base = compareSortValues(left.sv, right.sv);\n return base !== 0 ? base * dir : left.index - right.index;\n })\n .map((decorated) => decorated.item);\n}\n","// Field-type → comparable value for the collection list-table sort. Splits\n// the header sort's per-row value extraction out of the Vue view so the\n// dispatch (which field type maps to which SortValue constructor) is testable\n// on its own. The comparators live in `./sortItems`; this module only routes.\n//\n// A few field kinds can't be sorted from the raw cell alone — `toggle` and\n// `flag` are projected booleans, `derived` is a formula, and `ref` sorts by\n// its resolved display label. Those readers live in the view's rendering\n// composable, so they are passed in (dependency injection) rather than\n// imported, keeping this module pure and framework-free.\n\nimport { numericSortValue, stringSortValue, dateSortValue, enumSortValue, boolSortValue, type SortValue } from \"./sortItems\";\nimport type { CollectionItem, CollectionFieldSpec as FieldSpec } from \"./schema\";\n\n/** Resolve a `ref` cell to its human display label (target slug + item id). */\nexport type RefDisplayResolver = (targetSlug: string, itemSlug: string) => string;\n\n/** The row readers the view supplies for the field kinds that can't sort off\n * the raw cell value. All come from the rendering composable. */\nexport interface SortValueDeps {\n toggleChecked: (item: CollectionItem, field: FieldSpec) => boolean;\n flagValueOf: (key: string, item: CollectionItem) => boolean;\n evaluateDerived: (field: FieldSpec, key: string, item: CollectionItem) => number | null;\n deriveRecord: (item: CollectionItem) => CollectionItem;\n resolveRefDisplay: RefDisplayResolver;\n}\n\n/** Comparable value for scalar fields that key off the raw cell value. */\nexport function scalarSortValue(field: FieldSpec, raw: unknown, resolveRefDisplay: RefDisplayResolver): SortValue {\n switch (field.type) {\n case \"number\":\n case \"money\":\n return numericSortValue(raw);\n case \"date\":\n case \"datetime\":\n return dateSortValue(raw);\n case \"enum\":\n return enumSortValue(field.values, raw);\n case \"boolean\":\n return boolSortValue(raw === true);\n case \"ref\":\n return field.to && typeof raw === \"string\" && raw ? stringSortValue(resolveRefDisplay(field.to, raw)) : stringSortValue(raw);\n default:\n return stringSortValue(raw);\n }\n}\n\n/** Derived rows sort by their display type: money/number → numeric,\n * date → epoch, anything else → the enriched value as a string. */\nexport function derivedSortValue(\n field: FieldSpec,\n key: string,\n item: CollectionItem,\n deps: Pick<SortValueDeps, \"evaluateDerived\" | \"deriveRecord\">,\n): SortValue {\n const display = field.type === \"derived\" ? field.display : undefined;\n if (display === undefined || display === \"number\" || display === \"money\") {\n return numericSortValue(deps.evaluateDerived(field, key, item));\n }\n const enriched = deps.deriveRecord(item);\n if (display === \"date\") return dateSortValue(enriched[key]);\n return stringSortValue(enriched[key]);\n}\n\n/** Comparable value for one row under the active field. Toggle, flag, and\n * derived need the whole record; every other type keys off the raw cell. */\nexport function sortValueOf(field: FieldSpec, key: string, item: CollectionItem, deps: SortValueDeps): SortValue {\n if (field.type === \"toggle\") return boolSortValue(deps.toggleChecked(item, field));\n if (field.type === \"flag\") return boolSortValue(deps.flagValueOf(key, item));\n if (field.type === \"derived\") return derivedSortValue(field, key, item, deps);\n return scalarSortValue(field, item[key], deps.resolveRefDisplay);\n}\n","// Free-text record matching for the collection list search box. Pure and\n// framework-free so the view's `filteredItems` computed just calls it.\n\nimport { fieldTextOrNull } from \"./fieldText\";\nimport type { CollectionItem } from \"./schema\";\n\n/** Case-insensitive substring match across an item's scalar fields.\n * Object/array-valued fields (table rows, nested records) have no text\n * form (`fieldTextOrNull` → `null`) and are skipped — they don't render as\n * searchable text in the list table.\n *\n * `query` is matched against each cell's lower-cased text but is NOT\n * lower-cased here: callers pass an already-normalised (trimmed +\n * lower-cased) query, so an upper-case `query` intentionally matches\n * nothing. Keeping the normalisation at the call site avoids re-lowering\n * the same query once per row. */\nexport function itemMatchesQuery(item: CollectionItem, query: string): boolean {\n // An empty query matches every record — otherwise a row whose only fields are\n // object/array-valued (no scalar cell reaches `includes(\"\")`) would vanish\n // when the search box is cleared. The list caller already short-circuits the\n // empty case, so this only guards a future caller that doesn't.\n if (query === \"\") return true;\n return Object.values(item).some((value) => {\n const text = fieldTextOrNull(value);\n return text !== null && text.toLowerCase().includes(query);\n });\n}\n","// Composite cell keys for the list table's per-cell state maps, plus the\n// load-time \"which enum cells were empty\" snapshot the inline enum dropdown\n// reads. Pure and framework-free so the view keeps only the reactive refs.\n\nimport { fieldText } from \"./fieldText\";\nimport type { CollectionItem } from \"./schema\";\n\n/** The schema slice `snapshotEmptyEnums` reads — minimal structural shape so\n * both the client and server collection-schema types satisfy it as-is. */\nexport interface EnumSnapshotSchema {\n fields: Record<string, { type: string }>;\n primaryKey: string;\n}\n\n/** Stable key for one cell (`<rowId>:<fieldKey>`) in the per-cell state maps. */\nexport function cellKey(rowId: string, fieldKey: string): string {\n return `${rowId}:${fieldKey}`;\n}\n\n/** A row's stable identity: the primary-key value as text (`\"\"` when the schema\n * has no primary key, or the value is absent). Every per-row / per-cell state\n * map keys off this, and it is the SAME derivation `snapshotEmptyEnums` uses\n * for its record ids (`fieldText`), so `cellKey(rowIdOf(...), field)` and the\n * empty-enum snapshot always agree. A real primary key is a scalar, so this\n * matches its plain string form. */\nexport function rowIdOf(primaryKey: string | undefined, item: CollectionItem): string {\n return primaryKey ? fieldText(item[primaryKey]) : \"\";\n}\n\n/** The set of enum cells that were empty in the freshly-fetched records — the\n * only cells whose inline dropdown offers an empty option. */\nexport function snapshotEmptyEnums(schema: EnumSnapshotSchema, records: CollectionItem[]): Set<string> {\n const empty = new Set<string>();\n const enumKeys = Object.entries(schema.fields)\n .filter(([, field]) => field.type === \"enum\")\n .map(([fieldKey]) => fieldKey);\n if (enumKeys.length === 0) return empty;\n for (const record of records) {\n const recordId = fieldText(record[schema.primaryKey]);\n for (const fieldKey of enumKeys) {\n if (record[fieldKey] == null || record[fieldKey] === \"\") empty.add(cellKey(recordId, fieldKey));\n }\n }\n return empty;\n}\n","// Shared chip/label resolution for the collection calendar surfaces (month\n// grid + day view), so both label a record identically. Pure, type-only\n// dependency on the schema shape.\n\nimport { fieldText } from \"./fieldText\";\nimport type { CollectionItem, CollectionSchema } from \"./schema\";\n\n// Text-like field types that make a sensible human-readable label.\nconst LABEL_FIELD_TYPES = new Set([\"string\", \"text\", \"markdown\", \"email\"]);\n\n/** Which field labels a record: the schema's explicit `displayField`, else the\n * first non-primary text-like field (so a collection without a displayField\n * shows e.g. the title rather than the opaque primaryKey), else null → the\n * caller falls back to the primary-key value. */\nexport function labelFieldFor(schema: CollectionSchema): string | null {\n if (schema.displayField) return schema.displayField;\n for (const [key, spec] of Object.entries(schema.fields)) {\n if (key !== schema.primaryKey && LABEL_FIELD_TYPES.has(spec.type)) return key;\n }\n return null;\n}\n\n/** A record's primary-key value as a string. */\nexport function itemIdOf(item: CollectionItem, schema: CollectionSchema): string {\n return fieldText(item[schema.primaryKey]);\n}\n\n/** A record's display label: the resolved `labelField` value, else the\n * primary key. Pass the result of `labelFieldFor(schema)` as `labelField`\n * (compute it once per render rather than per item). */\nexport function itemLabelOf(item: CollectionItem, schema: CollectionSchema, labelField: string | null): string {\n if (labelField) {\n const value = item[labelField];\n // Accept any primitive (string / number / boolean) so a numeric or boolean\n // display field still labels the record; objects/arrays fall through to the\n // id rather than rendering as \"[object Object]\".\n if (typeof value === \"string\" || typeof value === \"number\" || typeof value === \"boolean\") {\n const text = String(value);\n if (text.length > 0) return text;\n }\n }\n return itemIdOf(item, schema);\n}\n","// Pure builder for the workspace ontology graph — the `/collections` Map\n// panel (phase 2 of plans/done/collection-ontology.md step ①; plan:\n// plans/done/feat-collection-ontology-graph.md). Consumes the per-collection\n// ontology entries `buildWorkspaceOntology` derives server-side and turns\n// them into a node/edge structure a graph view can render directly.\n// Browser-safe (no `node:*` imports) so the Vue plugin builds the same\n// graph from the raw entries — same parity discipline as `deriveAll` and\n// the sibling `wiki/graph.ts`.\n\n/** One relationship a schema declares: a `ref` (the record stores the\n * target's primaryKey slug) or `embed` (display-only pull) pointing at\n * collection `to`, or `backlinks` / `rollup` (display-only REVERSE\n * refs — `to` is the relation's source collection, i.e. its `from`,\n * and `via` names the exact ref field there being reversed).\n * A `ref` column inside a `table` field is reported with a dotted path\n * (`lines.clientId`). Whether `to` exists is NOT checked — resolution\n * is fail-soft at render. */\nexport interface OntologyRelation {\n field: string;\n kind: \"ref\" | \"embed\" | \"backlinks\" | \"rollup\";\n to: string;\n via?: string;\n}\n\nexport interface CollectionOntologyEntry {\n slug: string;\n title: string;\n icon: string;\n primaryKey: string;\n /** The effective display field: the schema's `displayField`, falling\n * back to the primaryKey exactly as render-time labelling does. */\n displayField: string;\n /** Records in the collection, or `null` when the backend couldn't be\n * reached to count them (an engine that failed to load, a closed\n * session). Deliberately not 0: an unreachable collection reported as\n * empty invites \"restoring\" data that is intact but out of reach. */\n recordCount: number | null;\n relations: OntologyRelation[];\n}\n\nexport interface OntologyGraphNode {\n slug: string;\n title: string;\n icon: string;\n recordCount: number | null;\n /** No discovered collection has this slug — the node exists only\n * because a relation points at it. Rendered as a ghost so the graph\n * doubles as a broken-ref lint surface (lint, not lock). */\n missing?: boolean;\n}\n\nexport interface OntologyGraphEdge {\n /** Tail: the collection whose records store (`ref`) or pull (`embed`)\n * the link — or, for an uncollapsed reverse relation, the collection\n * whose refs the `backlinks`/`rollup` field aggregates. */\n from: string;\n /** Head: the collection the link points at. */\n to: string;\n /** Field declaring the relation — on `from` for `ref`/`embed`, on\n * `to` for an uncollapsed `backlinks`/`rollup`. */\n field: string;\n kind: OntologyRelation[\"kind\"];\n /** `backlinks`/`rollup` field names on `to` that declare the reverse\n * of this `ref` edge, collapsed here so one link never draws twice. */\n reverseFields?: string[];\n}\n\nexport interface OntologyGraph {\n nodes: OntologyGraphNode[];\n edges: OntologyGraphEdge[];\n}\n\nconst isForwardKind = (kind: OntologyRelation[\"kind\"]): boolean => kind === \"ref\" || kind === \"embed\";\n\n/** Fold a reverse relation (`backlinks`/`rollup` declared on `owner`)\n * into the edge list: collapse onto the forward `ref` edge whose field\n * is the relation's declared `via` (several refs can link the same\n * pair — matches.homeTeam vs matches.awayTeam — so the pair alone is\n * ambiguous), else emit its own edge oriented in true data direction\n * (refs' source → owner). A `via` naming no forward edge stays\n * uncollapsed — fail-soft, like every other dangling reference. */\nconst addReverseRelation = (edges: OntologyGraphEdge[], owner: string, rel: OntologyRelation): void => {\n const forward = edges.find((edge) => edge.kind === \"ref\" && edge.from === rel.to && edge.to === owner && (rel.via === undefined || edge.field === rel.via));\n if (forward) {\n forward.reverseFields = [...(forward.reverseFields ?? []), rel.field];\n return;\n }\n edges.push({ from: rel.to, to: owner, field: rel.field, kind: rel.kind });\n};\n\n/** Two passes so every forward `ref` edge exists before any reverse\n * relation tries to collapse onto one — entry order must not matter. */\nconst collectEdges = (entries: CollectionOntologyEntry[]): OntologyGraphEdge[] => {\n const edges: OntologyGraphEdge[] = [];\n for (const entry of entries) {\n for (const rel of entry.relations) {\n if (isForwardKind(rel.kind)) edges.push({ from: entry.slug, to: rel.to, field: rel.field, kind: rel.kind });\n }\n }\n for (const entry of entries) {\n for (const rel of entry.relations) {\n if (!isForwardKind(rel.kind)) addReverseRelation(edges, entry.slug, rel);\n }\n }\n return edges;\n};\n\n/** Slugs referenced by an edge endpoint but backed by no discovered\n * collection, slug-sorted for deterministic node order. */\nconst ghostSlugs = (edges: OntologyGraphEdge[], known: ReadonlySet<string>): string[] => {\n const ghosts = new Set<string>();\n for (const edge of edges) {\n if (!known.has(edge.from)) ghosts.add(edge.from);\n if (!known.has(edge.to)) ghosts.add(edge.to);\n }\n return [...ghosts].sort();\n};\n\n/** Build the renderable graph: one node per discovered collection (in\n * the entries' slug-sorted order) plus ghost nodes for dangling\n * targets, one edge per underlying link. */\nexport const buildOntologyGraph = (entries: CollectionOntologyEntry[]): OntologyGraph => {\n const edges = collectEdges(entries);\n const known = new Set(entries.map((entry) => entry.slug));\n const nodes: OntologyGraphNode[] = entries.map(({ slug, title, icon, recordCount }) => ({ slug, title, icon, recordCount }));\n for (const slug of ghostSlugs(edges, known)) {\n // A ghost node has no collection behind it at all, so it genuinely holds\n // no records — 0, not the \"couldn't count\" null.\n nodes.push({ slug, title: slug, icon: \"\", recordCount: 0, missing: true });\n }\n return { nodes, edges };\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":[],"sources":["../../src/collection/core/presentCollection.ts","../../src/collection/core/enumColors.ts","../../src/collection/core/draft.ts","../../src/collection/core/chatSeed.ts","../../src/collection/core/shortcutInfo.ts","../../src/collection/core/sortItems.ts","../../src/collection/core/sortValueOf.ts","../../src/collection/core/textSearch.ts","../../src/collection/core/recordKeys.ts","../../src/collection/core/itemLabel.ts","../../src/collection/core/ontologyGraph.ts"],"sourcesContent":["// presentCollection tool — definition + pure server-side executor.\n//\n// Isomorphic (no Vue, no Node): bundled to the browser as the plugin's\n// `execute`, and run server-side via the host's plugin dispatch route. The\n// executor only validates + echoes the addressing; the live schema + items\n// are fetched client-side by the View through the host's /api/collections\n// routes, so a bad slug surfaces as the View's \"not found\" state.\n\nimport type { ToolContext, ToolDefinition, ToolResult } from \"gui-chat-protocol\";\n\nexport const TOOL_NAME = \"presentCollection\";\n\n/** Render payload carried in the tool result's `data` field; the View mounts\n * off these. Same shape as the tool args. */\nexport interface PresentCollectionData {\n /** Slug of the collection to display (e.g. \"clients\", \"invoices\"). */\n collectionSlug: string;\n /** Optional primary-key value of a single item to open on mount. */\n itemId?: string | undefined;\n /** Which ROOT this card's collection lives in, as a host-opaque scope token.\n *\n * A collection's identity is `(root, slug)`, but a card payload named only\n * the slug — so the host re-resolved it through whatever binding was current\n * when the card RENDERED, which in a multi-root host may be a different\n * project than the one the card was made in. A card built in project A could\n * then read project B's data.\n *\n * **Stamped by the HOST, never accepted from tool arguments.** The tool args\n * are a model-controlled channel: a model that could name the scope could\n * name another project's, and the card — and any token minted for it — would\n * address a project the user never opened in this conversation. So\n * `executePresentCollection` DROPS any `scope` it is handed, and a host adds\n * its own with {@link withCardScope} when it applies the result, where it\n * already knows which binding produced the call.\n *\n * Host-opaque: pass an opaque project id, NEVER an absolute path — this\n * payload reaches the browser and, through a view, an LLM-authored iframe.\n *\n * Absent — the single-workspace case — the payload and every reconciliation\n * decision are exactly what they were before this field existed. */\n scope?: string | undefined;\n}\n\n/** Stamp a host's opaque project scope onto a card payload. The ONLY way a\n * scope gets in: the executor refuses to take one from tool arguments (see\n * `PresentCollectionData.scope`). `undefined` returns the payload untouched,\n * so a single-workspace host calling this unconditionally still produces a\n * payload with no `scope` property at all. */\nexport function withCardScope(data: PresentCollectionData, scope: string | undefined): PresentCollectionData {\n const trimmed = typeof scope === \"string\" ? scope.trim() : \"\";\n return trimmed.length > 0 ? { ...data, scope: trimmed } : data;\n}\n\n/** A card's identity for reconciliation. Two cards match only when they name\n * the same collection IN THE SAME SCOPE, so two projects' `tasks` cards stay\n * two cards. With no scope on either side this is the slug, unchanged. */\nexport function collectionCardKey(data: Pick<PresentCollectionData, \"collectionSlug\" | \"scope\">): string {\n return data.scope === undefined ? data.collectionSlug : `${data.scope}\\u0000${data.collectionSlug}`;\n}\n\n/** Do two card payloads address the same card? The reconciliation predicate a\n * host should use instead of comparing slugs. */\nexport function sameCollectionCard(\n one: Pick<PresentCollectionData, \"collectionSlug\" | \"scope\">,\n other: Pick<PresentCollectionData, \"collectionSlug\" | \"scope\">,\n): boolean {\n return collectionCardKey(one) === collectionCardKey(other);\n}\n\nexport type PresentCollectionArgs = PresentCollectionData;\n\nexport const TOOL_DEFINITION: ToolDefinition = {\n type: \"function\",\n name: TOOL_NAME,\n description:\n \"Display a schema-driven collection inline in the chat as an interactive, editable card. Shows the collection's list of records. Pass `itemId` to open one specific record on mount.\",\n parameters: {\n type: \"object\",\n properties: {\n collectionSlug: {\n type: \"string\",\n description: \"The slug of the collection to display (e.g. 'clients', 'invoices', 'contacts').\",\n },\n itemId: {\n type: \"string\",\n description: \"Optional primary-key value of a single record to open in detail view on mount. Omit to show the full list.\",\n },\n },\n required: [\"collectionSlug\"],\n },\n prompt: `After making changes to schema-driven collections, use ${TOOL_NAME} to present either the collection or the item`,\n};\n\n/** A trimmed non-empty string, or undefined. The tool's args arrive from an\n * LLM (and `scope` from the host), so every field is validated the same way\n * rather than trusted. */\nconst cleaned = (value: unknown): string | undefined => (typeof value === \"string\" && value.trim().length > 0 ? value.trim() : undefined);\n\n/** Normalise the tool args into the card payload, or `null` when the required\n * slug is missing. Pure and exported so the addressing rules are testable\n * without going through a `ToolResult`.\n *\n * `scope` is deliberately NOT read from the args — see\n * `PresentCollectionData.scope`. Everything here comes from a model-controlled\n * channel, and the scope decides which project the card reads; a host stamps\n * it afterwards with `withCardScope`.\n *\n * Keys are added one at a time rather than spread with `undefined` values, so\n * a payload with no `itemId` OMITS the property entirely — which is what keeps\n * a single-workspace card byte-identical to what it has always been, in both\n * `deepEqual` and JSON. */\nexport const toPresentCollectionData = (args: PresentCollectionArgs): PresentCollectionData | null => {\n const collectionSlug = cleaned(args?.collectionSlug);\n if (!collectionSlug) return null;\n const itemId = cleaned(args.itemId);\n return { collectionSlug, ...(itemId ? { itemId } : {}) };\n};\n\nexport const executePresentCollection = async (\n _context: ToolContext,\n args: PresentCollectionArgs,\n): Promise<ToolResult<PresentCollectionData, PresentCollectionData>> => {\n const data = toPresentCollectionData(args);\n if (!data) {\n return {\n message: \"presentCollection error: collectionSlug is required\",\n instructions: \"Tell the user you couldn't display the collection because no collection was specified, and ask which collection they mean.\",\n };\n }\n const { collectionSlug, itemId } = data;\n const target = itemId ? `${collectionSlug} / ${itemId}` : collectionSlug;\n return {\n message: `Presented collection ${target}`,\n // `data` is the view's source (also the host's render-eligibility signal);\n // `jsonData` is what the LLM sees. Same payload, two audiences.\n data,\n jsonData: data,\n // Phrased to stay correct for BOTH storage kinds without loading the\n // schema (this executor is deliberately pure/isomorphic): a writable\n // collection offers edit/create/delete in the card; a read-only\n // `dataSource` collection hides those and changes flow through its\n // data file instead.\n instructions:\n \"The collection has been presented to the user as an interactive card. They can browse and open records directly; on a writable collection they can also edit, create, and delete (a read-only dataSource collection shows no edit controls — its records change by editing the backing data file). No further action is needed unless they ask.\",\n };\n};\n","// A standard, ordered colour palette for `enum` field values, shared by every\n// collection surface (list, calendar, kanban). Each value in an\n// enum's `values` array is assigned the palette entry at its index — cycling\n// when an enum declares more values than the palette holds — so colouring is\n// automatic and consistent without any per-value schema config.\n//\n// Tailwind only detects class names that appear as complete string literals,\n// so every surface's classes are spelled out per colour below rather than\n// built from a colour name at runtime.\n//\n// One enum is special: the field a schema's `notifyWhen` targets (the\n// \"notification enum\"). Its flagged values read the notification severity\n// colours — the first flagged value (most urgent) red, the rest amber — and\n// every other value reads neutral grey, mirroring the notification bell\n// (red = urgent, amber = nudge) rather than the rotating palette.\n// `resolveEnumColor` encapsulates that rule.\n\nimport { fieldText, fieldTextOrNull } from \"./fieldText\";\nimport type { CollectionSchema } from \"./schema\";\n\nexport interface EnumColorClasses {\n /** Stat-card style: border + fill + text + hover. */\n card: string;\n /** Small status dot (kanban column header). */\n dot: string;\n /** Pill / badge / inline `<select>` fill + text (no border width). */\n badge: string;\n /** Border colour, paired with a `border` width class by the caller. */\n border: string;\n}\n\n// The palette deliberately EXCLUDES the warm warning band (red / orange /\n// amber): those are reserved for the notification-enum severity colours\n// (`ENUM_ALERT` red, `ENUM_NUDGE` amber), so a normal enum value can never\n// draw a colour that reads like a notification. Eight well-separated cool /\n// green / magenta hues keep enough variety for wide enums.\nconst PALETTE: readonly EnumColorClasses[] = [\n {\n card: \"border-indigo-200 bg-indigo-50 text-indigo-600 hover:bg-indigo-100\",\n dot: \"bg-indigo-500\",\n badge: \"bg-indigo-100 text-indigo-700\",\n border: \"border-indigo-200\",\n },\n { card: \"border-sky-200 bg-sky-50 text-sky-600 hover:bg-sky-100\", dot: \"bg-sky-500\", badge: \"bg-sky-100 text-sky-700\", border: \"border-sky-200\" },\n { card: \"border-cyan-200 bg-cyan-50 text-cyan-600 hover:bg-cyan-100\", dot: \"bg-cyan-500\", badge: \"bg-cyan-100 text-cyan-700\", border: \"border-cyan-200\" },\n { card: \"border-teal-200 bg-teal-50 text-teal-600 hover:bg-teal-100\", dot: \"bg-teal-500\", badge: \"bg-teal-100 text-teal-700\", border: \"border-teal-200\" },\n {\n card: \"border-emerald-200 bg-emerald-50 text-emerald-600 hover:bg-emerald-100\",\n dot: \"bg-emerald-500\",\n badge: \"bg-emerald-100 text-emerald-700\",\n border: \"border-emerald-200\",\n },\n { card: \"border-lime-200 bg-lime-50 text-lime-600 hover:bg-lime-100\", dot: \"bg-lime-500\", badge: \"bg-lime-100 text-lime-700\", border: \"border-lime-200\" },\n {\n card: \"border-violet-200 bg-violet-50 text-violet-600 hover:bg-violet-100\",\n dot: \"bg-violet-500\",\n badge: \"bg-violet-100 text-violet-700\",\n border: \"border-violet-200\",\n },\n {\n card: \"border-fuchsia-200 bg-fuchsia-50 text-fuchsia-600 hover:bg-fuchsia-100\",\n dot: \"bg-fuchsia-500\",\n badge: \"bg-fuchsia-100 text-fuchsia-700\",\n border: \"border-fuchsia-200\",\n },\n];\n\n/** Neutral styling for the empty / Uncategorized bucket — never a palette\n * colour, so an unset or unknown value reads grey across every surface. */\nexport const ENUM_NEUTRAL: EnumColorClasses = {\n card: \"border-slate-200 bg-slate-50 text-slate-500 hover:bg-slate-100\",\n dot: \"bg-slate-300\",\n badge: \"bg-slate-100 text-slate-500\",\n border: \"border-slate-200\",\n};\n\n/** The urgent notification colour (red), matching the bell's `urgent`\n * severity. The first value a schema's `notifyWhen` flags reads this. */\nexport const ENUM_ALERT: EnumColorClasses = {\n card: \"border-red-200 bg-red-50 text-red-600 hover:bg-red-100\",\n dot: \"bg-red-500\",\n badge: \"bg-red-100 text-red-700\",\n border: \"border-red-200\",\n};\n\n/** The nudge notification colour (amber), matching the bell's `nudge`\n * severity. Flagged `notifyWhen` values after the first read this. */\nexport const ENUM_NUDGE: EnumColorClasses = {\n card: \"border-amber-200 bg-amber-50 text-amber-600 hover:bg-amber-100\",\n dot: \"bg-amber-500\",\n badge: \"bg-amber-100 text-amber-700\",\n border: \"border-amber-200\",\n};\n\n/** Classes for the enum value at `index` in its field's `values` array. A\n * negative index (value unset or not among the declared values) reads\n * neutral. */\nexport function enumColorClasses(index: number): EnumColorClasses {\n if (index < 0) return ENUM_NEUTRAL;\n return PALETTE[index % PALETTE.length] ?? ENUM_NEUTRAL;\n}\n\n/** Index of `value` within an enum field's declared `values`, or -1 when the\n * value is empty / unknown (→ neutral). */\nexport function enumValueIndex(values: readonly string[] | undefined, value: unknown): number {\n // No text form (array/object) means no enum member — \"[object Object]\" must\n // not index into the palette.\n const text = fieldTextOrNull(value);\n if (text === null || text === \"\") return -1;\n return values?.indexOf(text) ?? -1;\n}\n\n/** The flagged values when `fieldKey` is the schema's `notifyWhen` target (the\n * \"notification enum\"); undefined for every other field. */\nfunction notifyValuesFor(schema: CollectionSchema, fieldKey: string): readonly string[] | undefined {\n const spec = schema.notifyWhen;\n return spec && spec.field === fieldKey ? spec.in : undefined;\n}\n\n/** Resolve a value's colour for enum field `fieldKey`:\n * - Notification enum (`notifyWhen` targets it): the first flagged value (in\n * `notifyWhen.in` order, the most urgent) reads notification red, the rest\n * amber, and every non-flagged value neutral grey.\n * - Any other enum: the standard palette by the value's declared index. */\nexport function resolveEnumColor(schema: CollectionSchema, fieldKey: string, value: unknown): EnumColorClasses {\n const notifyValues = notifyValuesFor(schema, fieldKey);\n if (notifyValues) {\n const str = fieldText(value);\n const rank = notifyValues.indexOf(str);\n if (rank < 0) return ENUM_NEUTRAL;\n return rank === 0 ? ENUM_ALERT : ENUM_NUDGE;\n }\n const spec = schema.fields[fieldKey];\n return enumColorClasses(enumValueIndex(spec?.type === \"enum\" ? spec.values : undefined, value));\n}\n","// Pure edit-draft logic for the collection record form, extracted from\n// CollectionView.vue so the parent (build draft / save / validate) and\n// the extracted CollectionRecordPanel (per-row table mutators) share one\n// implementation. No Vue, no I/O — every function maps a draft + schema\n// to a value, so the omission/validation semantics are unit-testable.\n\nimport { fieldText } from \"./fieldText\";\nimport { fieldVisible } from \"./actionVisible\";\nimport { COMPUTED_TYPES } from \"./schema\";\nimport type { CollectionFieldSpec as FieldSpec, CollectionFieldType as FieldType, CollectionItem, CollectionSchema } from \"./schema\";\nimport type { EditState, TableRowDraft } from \"./uiTypes\";\n\n/** Build a row draft, reading each sub-field's persisted value via\n * `readSub`. An empty row passes a reader that always returns\n * `undefined`; a row-from-item reads the source record. Boolean\n * presence uses `typeof raw === \"boolean\"` so an existing explicit\n * `false` is recorded as present and round-trips on a no-op save. */\nfunction buildTableRowDraft(subFields: Record<string, FieldSpec>, readSub: (subKey: string) => unknown): TableRowDraft {\n const text: Record<string, string> = {};\n const bool: Record<string, boolean> = {};\n const boolOriginallyPresent: Record<string, boolean> = {};\n const boolTouched: Record<string, boolean> = {};\n for (const [subKey, subField] of Object.entries(subFields)) {\n const raw = readSub(subKey);\n if (subField.type === \"boolean\") {\n bool[subKey] = raw === true;\n boolOriginallyPresent[subKey] = typeof raw === \"boolean\";\n boolTouched[subKey] = false;\n } else {\n text[subKey] = fieldText(raw);\n }\n }\n return { text, bool, boolOriginallyPresent, boolTouched };\n}\n\n/** A fresh, empty row draft for a `table` field's sub-schema. */\nexport function emptyRow(subFields: Record<string, FieldSpec>): TableRowDraft {\n return buildTableRowDraft(subFields, () => undefined);\n}\n\n/** Build a row draft from an existing persisted row. */\nexport function rowFromItem(item: Record<string, unknown>, subFields: Record<string, FieldSpec>): TableRowDraft {\n return buildTableRowDraft(subFields, (subKey) => item[subKey]);\n}\n\n/** Own-property boolean read: a field named `toString` / `constructor` /\n * `__proto__` must read `false` here, not inherit a truthy Object.prototype\n * member and force an untouched boolean into every saved record. */\nfunction ownFlag(flags: Record<string, boolean>, key: string): boolean {\n return Object.hasOwn(flags, key) && flags[key] === true;\n}\n\n/** Decide whether a boolean field's draft value should be emitted (vs.\n * omitted so a downstream default applies). */\nfunction shouldEmitBoolean(state: EditState, key: string, field: FieldSpec): boolean {\n return ownFlag(state.boolOriginallyPresent, key) || ownFlag(state.boolTouched, key) || Boolean(field.required);\n}\n\n/** Convert a scalar draft slot to its persisted form. `undefined` = omit. */\nfunction scalarDraftToValue(raw: string | undefined, fieldType: FieldType): unknown {\n if (raw === undefined || raw === \"\") return undefined;\n if (fieldType === \"number\" || fieldType === \"money\") {\n const num = Number(raw);\n return Number.isFinite(num) ? num : raw;\n }\n return raw;\n}\n\n/** Convert one table row draft to its persisted record. */\nfunction rowDraftToRecord(rowDraft: TableRowDraft, subFields: Record<string, FieldSpec>): Record<string, unknown> {\n const row: Record<string, unknown> = {};\n for (const [subKey, subField] of Object.entries(subFields)) {\n if (subField.type === \"boolean\") {\n const value = ownFlag(rowDraft.bool, subKey);\n if (ownFlag(rowDraft.boolOriginallyPresent, subKey) || ownFlag(rowDraft.boolTouched, subKey) || value || subField.required) row[subKey] = value;\n continue;\n }\n const value = scalarDraftToValue(rowDraft.text[subKey], subField.type);\n if (value !== undefined) row[subKey] = value;\n }\n return row;\n}\n\n/** Convert a full edit draft to the record to persist.\n *\n * Collected as entries rather than assigned into an accumulator: a field may\n * be named `__proto__` (JSON.parse hands that over as an own key, so it\n * reaches here like any other), and `record[key] = value` would run the\n * prototype setter for that one name — the user's value would vanish between\n * the form and the save, with nothing to see. `fromEntries` defines an own\n * property whatever the name is. The read side of this file already guards the\n * same hazard with `ownFlag` (Codex review on #2910). */\nexport function draftToRecord(state: EditState, schema: CollectionSchema): CollectionItem {\n const entries: [string, unknown][] = [];\n for (const [key, field] of Object.entries(schema.fields)) {\n if (COMPUTED_TYPES.has(field.type)) continue; // never persisted (toggle projects an enum field)\n if (field.type === \"boolean\") {\n if (shouldEmitBoolean(state, key, field)) entries.push([key, ownFlag(state.bool, key)]);\n continue;\n }\n if (field.type === \"table\" && field.of) {\n const subFields = field.of;\n entries.push([key, (state.table[key] ?? []).map((rowDraft) => rowDraftToRecord(rowDraft, subFields))]);\n continue;\n }\n const value = scalarDraftToValue(state.text[key], field.type);\n if (value !== undefined) entries.push([key, value]);\n }\n return Object.fromEntries(entries);\n}\n\n/** Normalise a raw inline-edit input (table-cell checkbox/select) to its\n * persisted form. Mirrors `draftToRecord`'s boolean strictness; an empty\n * enum selection (the placeholder option) clears the field via `undefined`. */\nexport function coerceInlineValue(field: FieldSpec, raw: boolean | string): unknown {\n if (field.type === \"boolean\") return raw === true;\n return raw === \"\" ? undefined : raw;\n}\n\n/** Build the full record to PUT for a single-cell inline edit, without\n * mutating `item`. A `undefined` value omits the key (enum cleared),\n * matching `draftToRecord`'s omission semantics. */\nexport function buildUpdatedRecord(item: CollectionItem, key: string, value: unknown): CollectionItem {\n if (value === undefined) {\n const { [key]: __omit, ...rest } = item;\n return rest;\n }\n return { ...item, [key]: value };\n}\n\n/** Empty for required-field validation — NOT a truthiness check (a\n * numeric `0` is a filled value). */\nfunction isMissingDraftValue(value: unknown): boolean {\n return value === undefined || value === null || value === \"\";\n}\n\n/** Label of the first required table sub-field empty in any row, else null. */\nfunction firstMissingTableSubField(field: FieldSpec, rows: TableRowDraft[] | undefined): string | null {\n if (field.type !== \"table\" || !rows) return null;\n for (const [rowIdx, row] of rows.entries()) {\n for (const [subKey, subField] of Object.entries(field.of)) {\n if (!subField.required || subField.type === \"boolean\") continue;\n if (isMissingDraftValue(row.text[subKey])) return `${field.label} #${rowIdx + 1}: ${subField.label}`;\n }\n }\n return null;\n}\n\n/** Field types the required check never flags: booleans always hold a\n * value, and the computed/projected kinds (COMPUTED_TYPES — the shared\n * set, so a future computed type can't drift past this check) have no\n * draft input at all. */\nconst NEVER_MISSING_TYPES: ReadonlySet<FieldType> = new Set<FieldType>([\"boolean\", ...COMPUTED_TYPES]);\n\nfunction validateOneField(key: string, field: FieldSpec, draft: EditState, record: CollectionItem): string | null {\n // A `when`-hidden field has no input the user can fill — never missing.\n if (!fieldVisible(field, record)) return null;\n if (field.type === \"table\" && field.of) {\n const rows = draft.table[key];\n if (field.required && (!rows || rows.length === 0)) return field.label;\n return firstMissingTableSubField(field, rows);\n }\n if (!field.required) return null;\n if (draft.mode === \"create\" && field.primary === true) return null; // server auto-generates id\n if (NEVER_MISSING_TYPES.has(field.type)) return null;\n return isMissingDraftValue(draft.text[key]) ? field.label : null;\n}\n\n/** Human-readable label of the first missing required field, or null. */\nexport function firstMissingRequiredField(draft: EditState, schema: CollectionSchema): string | null {\n const record = draftToRecord(draft, schema);\n for (const [key, field] of Object.entries(schema.fields)) {\n const missing = validateOneField(key, field, draft, record);\n if (missing) return missing;\n }\n return null;\n}\n","// The slash-command chat seed a collection view sends to the agent when the\n// user starts a chat from a skill-backed collection or one of its records.\n// Skill-backed collections have a `/<slug>` command, so seed that. (Feeds are\n// skill-less and seeded differently; that branch stays in the view because it\n// is i18n-driven.)\n\n/** `/<slug> <message>`, or `/<slug> id=<itemId> <message>` when a specific\n * record is addressed. The `id=` selector goes BEFORE the free-text message so\n * the skill's argument parser reads it as a flag rather than as prose. */\nexport function skillCommandSeed(slug: string, message: string, itemId?: string): string {\n return itemId ? `/${slug} id=${itemId} ${message}` : `/${slug} ${message}`;\n}\n","// The one place an index row becomes the `{slug,title,icon,color}` a pinned\n// shortcut reconciles against.\n//\n// Both the collections index and the feeds index did this inline, and the\n// awkward part is identical in each: `color` must be ADDED only when there is\n// one, never assigned as `undefined` — the shortcut shapes declare it narrow\n// (see `CollectionSummary.color`) because the remote-host handlers pass them\n// through `Jsonify`, which drops `undefined` from a member union. Writing that\n// out twice is how the two copies drift.\n\nimport type { CollectionShortcutInfo } from \"./uiTypes\";\n\n/** The fields an index row supplies. Both `CollectionSummary` and\n * `FeedSummary` satisfy it; `icon` is optional because a feed row may carry\n * an empty one and rely on the surface's default. */\nexport interface ShortcutInfoSource {\n slug: string;\n title: string;\n icon?: string | undefined;\n color?: string | undefined;\n}\n\n/** An index row as shortcut-reconcile input. `fallbackIcon` stands in when the\n * row names no icon (feeds do this); `color` is omitted entirely rather than\n * set to `undefined` when the row has none. */\nexport function toShortcutInfo(row: ShortcutInfoSource, fallbackIcon: string): CollectionShortcutInfo {\n return {\n slug: row.slug,\n title: row.title,\n icon: row.icon && row.icon.length > 0 ? row.icon : fallbackIcon,\n ...(row.color === undefined ? {} : { color: row.color }),\n };\n}\n","// Sorting for the collection list table. The header sort toggle (next to\n// each field title) cycles a single active column through none → asc →\n// desc → none; this module turns that state plus a per-field value\n// extractor into an ordered copy of the rows.\n//\n// Two invariants the comparator guarantees:\n// 1. Rows with an empty/missing value for the sorted field always sink\n// to the bottom, regardless of direction.\n// 2. Ties (and the empty group) keep their original order — the sort is\n// stable via the captured source index.\n//\n// Field-type → comparable mapping (see `isSortableField`):\n// string/text/email → string · number/money → numeric ·\n// date/datetime → epoch-ms · enum → declared-index · boolean/toggle/flag →\n// false<true · ref → display label · derived → its display type.\n// markdown/table/image/file/embed get no sort button.\n\nimport { fieldTextOrNull } from \"./fieldText\";\nimport type { CollectionItem, CollectionFieldSpec, CollectionFieldType } from \"./schema\";\n\nexport type SortDirection = \"asc\" | \"desc\";\n\nexport interface SortState {\n /** Field key of the single active sort column. */\n field: string;\n direction: SortDirection;\n}\n\n/** A row's comparable value for the active field. Exactly one of `num` /\n * `str` is set when not empty; `empty` rows always sort last. */\nexport interface SortValue {\n empty: boolean;\n num?: number;\n str?: string;\n}\n\nconst EMPTY: SortValue = { empty: true };\n\n/** Field types that render no value text in the table, so offer no sort. */\nconst NON_SORTABLE: ReadonlySet<CollectionFieldType> = new Set<CollectionFieldType>([\"markdown\", \"table\", \"image\", \"file\", \"embed\", \"backlinks\", \"rollup\"]);\n\nexport function isSortableField(field: CollectionFieldSpec): boolean {\n return !NON_SORTABLE.has(field.type);\n}\n\n/** Cycle one column's state: none → asc → desc → none. */\nexport function nextSortDirection(current: SortDirection | null): SortDirection | null {\n if (current === null) return \"asc\";\n if (current === \"asc\") return \"desc\";\n return null;\n}\n\n// ── SortValue constructors (one per comparable kind) ────────────────\n\nexport function numericSortValue(raw: unknown): SortValue {\n if (raw == null || raw === \"\") return EMPTY;\n const num = typeof raw === \"number\" ? raw : Number(raw);\n return Number.isFinite(num) ? { empty: false, num } : EMPTY;\n}\n\nexport function stringSortValue(raw: unknown): SortValue {\n // An array/object has no sort key — treat it as empty (sorts last) instead of\n // grouping every such record together under \"[object Object]\".\n const str = fieldTextOrNull(raw);\n if (str === null) return EMPTY;\n return str.trim() === \"\" ? EMPTY : { empty: false, str };\n}\n\nexport function dateSortValue(raw: unknown): SortValue {\n const text = fieldTextOrNull(raw);\n if (text === null || text === \"\") return EMPTY;\n const epoch = Date.parse(text);\n // Unparseable dates fall back to a lexical compare rather than vanishing.\n return Number.isNaN(epoch) ? stringSortValue(raw) : { empty: false, num: epoch };\n}\n\n/** Enum sorts by the value's index in the declared `values` list. A value\n * outside the list (or unset) is treated as empty → last. */\nexport function enumSortValue(values: readonly string[] | undefined, raw: unknown): SortValue {\n const text = fieldTextOrNull(raw);\n if (text === null || text === \"\") return EMPTY;\n const idx = values ? values.indexOf(text) : -1;\n return idx < 0 ? EMPTY : { empty: false, num: idx };\n}\n\n/** Boolean / toggle: false < true, never empty (unset reads as false). */\nexport function boolSortValue(checked: boolean): SortValue {\n return { empty: false, num: checked ? 1 : 0 };\n}\n\n// ── Comparator + driver ─────────────────────────────────────────────\n\nexport function compareSortValues(left: SortValue, right: SortValue): number {\n if (left.num !== undefined && right.num !== undefined) return left.num - right.num;\n const leftStr = left.str ?? String(left.num ?? \"\");\n const rightStr = right.str ?? String(right.num ?? \"\");\n return leftStr.localeCompare(rightStr);\n}\n\n/** Stable sort of `items` by `valueOf`. Empties always last; ties hold\n * source order. Returns a new array (does not mutate `items`). */\nexport function sortItems(items: readonly CollectionItem[], direction: SortDirection, valueOf: (item: CollectionItem) => SortValue): CollectionItem[] {\n const dir = direction === \"asc\" ? 1 : -1;\n return items\n .map((item, index) => ({ item, index, sv: valueOf(item) }))\n .sort((left, right) => {\n if (left.sv.empty || right.sv.empty) {\n if (left.sv.empty && right.sv.empty) return left.index - right.index;\n return left.sv.empty ? 1 : -1;\n }\n const base = compareSortValues(left.sv, right.sv);\n return base !== 0 ? base * dir : left.index - right.index;\n })\n .map((decorated) => decorated.item);\n}\n","// Field-type → comparable value for the collection list-table sort. Splits\n// the header sort's per-row value extraction out of the Vue view so the\n// dispatch (which field type maps to which SortValue constructor) is testable\n// on its own. The comparators live in `./sortItems`; this module only routes.\n//\n// A few field kinds can't be sorted from the raw cell alone — `toggle` and\n// `flag` are projected booleans, `derived` is a formula, and `ref` sorts by\n// its resolved display label. Those readers live in the view's rendering\n// composable, so they are passed in (dependency injection) rather than\n// imported, keeping this module pure and framework-free.\n\nimport { numericSortValue, stringSortValue, dateSortValue, enumSortValue, boolSortValue, type SortValue } from \"./sortItems\";\nimport type { CollectionItem, CollectionFieldSpec as FieldSpec } from \"./schema\";\n\n/** Resolve a `ref` cell to its human display label (target slug + item id). */\nexport type RefDisplayResolver = (targetSlug: string, itemSlug: string) => string;\n\n/** The row readers the view supplies for the field kinds that can't sort off\n * the raw cell value. All come from the rendering composable. */\nexport interface SortValueDeps {\n toggleChecked: (item: CollectionItem, field: FieldSpec) => boolean;\n flagValueOf: (key: string, item: CollectionItem) => boolean;\n evaluateDerived: (field: FieldSpec, key: string, item: CollectionItem) => number | null;\n deriveRecord: (item: CollectionItem) => CollectionItem;\n resolveRefDisplay: RefDisplayResolver;\n}\n\n/** Comparable value for scalar fields that key off the raw cell value. */\nexport function scalarSortValue(field: FieldSpec, raw: unknown, resolveRefDisplay: RefDisplayResolver): SortValue {\n switch (field.type) {\n case \"number\":\n case \"money\":\n return numericSortValue(raw);\n case \"date\":\n case \"datetime\":\n return dateSortValue(raw);\n case \"enum\":\n return enumSortValue(field.values, raw);\n case \"boolean\":\n return boolSortValue(raw === true);\n case \"ref\":\n return field.to && typeof raw === \"string\" && raw ? stringSortValue(resolveRefDisplay(field.to, raw)) : stringSortValue(raw);\n default:\n return stringSortValue(raw);\n }\n}\n\n/** Derived rows sort by their display type: money/number → numeric,\n * date → epoch, anything else → the enriched value as a string. */\nexport function derivedSortValue(\n field: FieldSpec,\n key: string,\n item: CollectionItem,\n deps: Pick<SortValueDeps, \"evaluateDerived\" | \"deriveRecord\">,\n): SortValue {\n const display = field.type === \"derived\" ? field.display : undefined;\n if (display === undefined || display === \"number\" || display === \"money\") {\n return numericSortValue(deps.evaluateDerived(field, key, item));\n }\n const enriched = deps.deriveRecord(item);\n if (display === \"date\") return dateSortValue(enriched[key]);\n return stringSortValue(enriched[key]);\n}\n\n/** Comparable value for one row under the active field. Toggle, flag, and\n * derived need the whole record; every other type keys off the raw cell. */\nexport function sortValueOf(field: FieldSpec, key: string, item: CollectionItem, deps: SortValueDeps): SortValue {\n if (field.type === \"toggle\") return boolSortValue(deps.toggleChecked(item, field));\n if (field.type === \"flag\") return boolSortValue(deps.flagValueOf(key, item));\n if (field.type === \"derived\") return derivedSortValue(field, key, item, deps);\n return scalarSortValue(field, item[key], deps.resolveRefDisplay);\n}\n","// Free-text record matching for the collection list search box. Pure and\n// framework-free so the view's `filteredItems` computed just calls it.\n\nimport { fieldTextOrNull } from \"./fieldText\";\nimport type { CollectionItem } from \"./schema\";\n\n/** Case-insensitive substring match across an item's scalar fields.\n * Object/array-valued fields (table rows, nested records) have no text\n * form (`fieldTextOrNull` → `null`) and are skipped — they don't render as\n * searchable text in the list table.\n *\n * `query` is matched against each cell's lower-cased text but is NOT\n * lower-cased here: callers pass an already-normalised (trimmed +\n * lower-cased) query, so an upper-case `query` intentionally matches\n * nothing. Keeping the normalisation at the call site avoids re-lowering\n * the same query once per row. */\nexport function itemMatchesQuery(item: CollectionItem, query: string): boolean {\n // An empty query matches every record — otherwise a row whose only fields are\n // object/array-valued (no scalar cell reaches `includes(\"\")`) would vanish\n // when the search box is cleared. The list caller already short-circuits the\n // empty case, so this only guards a future caller that doesn't.\n if (query === \"\") return true;\n return Object.values(item).some((value) => {\n const text = fieldTextOrNull(value);\n return text !== null && text.toLowerCase().includes(query);\n });\n}\n","// Composite cell keys for the list table's per-cell state maps, plus the\n// load-time \"which enum cells were empty\" snapshot the inline enum dropdown\n// reads. Pure and framework-free so the view keeps only the reactive refs.\n\nimport { fieldText } from \"./fieldText\";\nimport type { CollectionItem } from \"./schema\";\n\n/** The schema slice `snapshotEmptyEnums` reads — minimal structural shape so\n * both the client and server collection-schema types satisfy it as-is. */\nexport interface EnumSnapshotSchema {\n fields: Record<string, { type: string }>;\n primaryKey: string;\n}\n\n/** Stable key for one cell (`<rowId>:<fieldKey>`) in the per-cell state maps. */\nexport function cellKey(rowId: string, fieldKey: string): string {\n return `${rowId}:${fieldKey}`;\n}\n\n/** A row's stable identity: the primary-key value as text (`\"\"` when the schema\n * has no primary key, or the value is absent). Every per-row / per-cell state\n * map keys off this, and it is the SAME derivation `snapshotEmptyEnums` uses\n * for its record ids (`fieldText`), so `cellKey(rowIdOf(...), field)` and the\n * empty-enum snapshot always agree. A real primary key is a scalar, so this\n * matches its plain string form. */\nexport function rowIdOf(primaryKey: string | undefined, item: CollectionItem): string {\n return primaryKey ? fieldText(item[primaryKey]) : \"\";\n}\n\n/** The set of enum cells that were empty in the freshly-fetched records — the\n * only cells whose inline dropdown offers an empty option. */\nexport function snapshotEmptyEnums(schema: EnumSnapshotSchema, records: CollectionItem[]): Set<string> {\n const empty = new Set<string>();\n const enumKeys = Object.entries(schema.fields)\n .filter(([, field]) => field.type === \"enum\")\n .map(([fieldKey]) => fieldKey);\n if (enumKeys.length === 0) return empty;\n for (const record of records) {\n const recordId = fieldText(record[schema.primaryKey]);\n for (const fieldKey of enumKeys) {\n if (record[fieldKey] == null || record[fieldKey] === \"\") empty.add(cellKey(recordId, fieldKey));\n }\n }\n return empty;\n}\n","// Shared chip/label resolution for the collection calendar surfaces (month\n// grid + day view), so both label a record identically. Pure, type-only\n// dependency on the schema shape.\n\nimport { fieldText } from \"./fieldText\";\nimport type { CollectionItem, CollectionSchema } from \"./schema\";\n\n// Text-like field types that make a sensible human-readable label.\nconst LABEL_FIELD_TYPES = new Set([\"string\", \"text\", \"markdown\", \"email\"]);\n\n/** Which field labels a record: the schema's explicit `displayField`, else the\n * first non-primary text-like field (so a collection without a displayField\n * shows e.g. the title rather than the opaque primaryKey), else null → the\n * caller falls back to the primary-key value. */\nexport function labelFieldFor(schema: CollectionSchema): string | null {\n if (schema.displayField) return schema.displayField;\n for (const [key, spec] of Object.entries(schema.fields)) {\n if (key !== schema.primaryKey && LABEL_FIELD_TYPES.has(spec.type)) return key;\n }\n return null;\n}\n\n/** A record's primary-key value as a string. */\nexport function itemIdOf(item: CollectionItem, schema: CollectionSchema): string {\n return fieldText(item[schema.primaryKey]);\n}\n\n/** A record's display label: the resolved `labelField` value, else the\n * primary key. Pass the result of `labelFieldFor(schema)` as `labelField`\n * (compute it once per render rather than per item). */\nexport function itemLabelOf(item: CollectionItem, schema: CollectionSchema, labelField: string | null): string {\n if (labelField) {\n const value = item[labelField];\n // Accept any primitive (string / number / boolean) so a numeric or boolean\n // display field still labels the record; objects/arrays fall through to the\n // id rather than rendering as \"[object Object]\".\n if (typeof value === \"string\" || typeof value === \"number\" || typeof value === \"boolean\") {\n const text = String(value);\n if (text.length > 0) return text;\n }\n }\n return itemIdOf(item, schema);\n}\n","// Pure builder for the workspace ontology graph — the `/collections` Map\n// panel (phase 2 of plans/done/collection-ontology.md step ①; plan:\n// plans/done/feat-collection-ontology-graph.md). Consumes the per-collection\n// ontology entries `buildWorkspaceOntology` derives server-side and turns\n// them into a node/edge structure a graph view can render directly.\n// Browser-safe (no `node:*` imports) so the Vue plugin builds the same\n// graph from the raw entries — same parity discipline as `deriveAll` and\n// the sibling `wiki/graph.ts`.\n\n/** One relationship a schema declares: a `ref` (the record stores the\n * target's primaryKey slug) or `embed` (display-only pull) pointing at\n * collection `to`, or `backlinks` / `rollup` (display-only REVERSE\n * refs — `to` is the relation's source collection, i.e. its `from`,\n * and `via` names the exact ref field there being reversed).\n * A `ref` column inside a `table` field is reported with a dotted path\n * (`lines.clientId`). Whether `to` exists is NOT checked — resolution\n * is fail-soft at render. */\nexport interface OntologyRelation {\n field: string;\n kind: \"ref\" | \"embed\" | \"backlinks\" | \"rollup\";\n to: string;\n via?: string;\n}\n\nexport interface CollectionOntologyEntry {\n slug: string;\n title: string;\n icon: string;\n primaryKey: string;\n /** The effective display field: the schema's `displayField`, falling\n * back to the primaryKey exactly as render-time labelling does. */\n displayField: string;\n /** Records in the collection, or `null` when the backend couldn't be\n * reached to count them (an engine that failed to load, a closed\n * session). Deliberately not 0: an unreachable collection reported as\n * empty invites \"restoring\" data that is intact but out of reach. */\n recordCount: number | null;\n relations: OntologyRelation[];\n}\n\nexport interface OntologyGraphNode {\n slug: string;\n title: string;\n icon: string;\n recordCount: number | null;\n /** No discovered collection has this slug — the node exists only\n * because a relation points at it. Rendered as a ghost so the graph\n * doubles as a broken-ref lint surface (lint, not lock). */\n missing?: boolean;\n}\n\nexport interface OntologyGraphEdge {\n /** Tail: the collection whose records store (`ref`) or pull (`embed`)\n * the link — or, for an uncollapsed reverse relation, the collection\n * whose refs the `backlinks`/`rollup` field aggregates. */\n from: string;\n /** Head: the collection the link points at. */\n to: string;\n /** Field declaring the relation — on `from` for `ref`/`embed`, on\n * `to` for an uncollapsed `backlinks`/`rollup`. */\n field: string;\n kind: OntologyRelation[\"kind\"];\n /** `backlinks`/`rollup` field names on `to` that declare the reverse\n * of this `ref` edge, collapsed here so one link never draws twice. */\n reverseFields?: string[];\n}\n\nexport interface OntologyGraph {\n nodes: OntologyGraphNode[];\n edges: OntologyGraphEdge[];\n}\n\nconst isForwardKind = (kind: OntologyRelation[\"kind\"]): boolean => kind === \"ref\" || kind === \"embed\";\n\n/** Fold a reverse relation (`backlinks`/`rollup` declared on `owner`)\n * into the edge list: collapse onto the forward `ref` edge whose field\n * is the relation's declared `via` (several refs can link the same\n * pair — matches.homeTeam vs matches.awayTeam — so the pair alone is\n * ambiguous), else emit its own edge oriented in true data direction\n * (refs' source → owner). A `via` naming no forward edge stays\n * uncollapsed — fail-soft, like every other dangling reference. */\nconst addReverseRelation = (edges: OntologyGraphEdge[], owner: string, rel: OntologyRelation): void => {\n const forward = edges.find((edge) => edge.kind === \"ref\" && edge.from === rel.to && edge.to === owner && (rel.via === undefined || edge.field === rel.via));\n if (forward) {\n forward.reverseFields = [...(forward.reverseFields ?? []), rel.field];\n return;\n }\n edges.push({ from: rel.to, to: owner, field: rel.field, kind: rel.kind });\n};\n\n/** Two passes so every forward `ref` edge exists before any reverse\n * relation tries to collapse onto one — entry order must not matter. */\nconst collectEdges = (entries: CollectionOntologyEntry[]): OntologyGraphEdge[] => {\n const edges: OntologyGraphEdge[] = [];\n for (const entry of entries) {\n for (const rel of entry.relations) {\n if (isForwardKind(rel.kind)) edges.push({ from: entry.slug, to: rel.to, field: rel.field, kind: rel.kind });\n }\n }\n for (const entry of entries) {\n for (const rel of entry.relations) {\n if (!isForwardKind(rel.kind)) addReverseRelation(edges, entry.slug, rel);\n }\n }\n return edges;\n};\n\n/** Slugs referenced by an edge endpoint but backed by no discovered\n * collection, slug-sorted for deterministic node order. */\nconst ghostSlugs = (edges: OntologyGraphEdge[], known: ReadonlySet<string>): string[] => {\n const ghosts = new Set<string>();\n for (const edge of edges) {\n if (!known.has(edge.from)) ghosts.add(edge.from);\n if (!known.has(edge.to)) ghosts.add(edge.to);\n }\n return [...ghosts].sort();\n};\n\n/** Build the renderable graph: one node per discovered collection (in\n * the entries' slug-sorted order) plus ghost nodes for dangling\n * targets, one edge per underlying link. */\nexport const buildOntologyGraph = (entries: CollectionOntologyEntry[]): OntologyGraph => {\n const edges = collectEdges(entries);\n const known = new Set(entries.map((entry) => entry.slug));\n const nodes: OntologyGraphNode[] = entries.map(({ slug, title, icon, recordCount }) => ({ slug, title, icon, recordCount }));\n for (const slug of ghostSlugs(edges, known)) {\n // A ghost node has no collection behind it at all, so it genuinely holds\n // no records — 0, not the \"couldn't count\" null.\n nodes.push({ slug, title: slug, icon: \"\", recordCount: 0, missing: true });\n }\n return { nodes, edges };\n};\n"],"mappings":"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"}