prism-mcp-server 20.21.14 → 20.21.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -158,6 +158,42 @@ or by re-enabling after each run.
158
158
  <details>
159
159
  <summary>Release history (optional)</summary>
160
160
 
161
+ ## What's New in v20.21.16
162
+
163
+ ### Conversations: checked on your device, and free with an account
164
+
165
+ - A local answer to a conversation is checked on your device before it is
166
+ served. If the check fails or cannot run, the answer goes to the cloud when
167
+ your plan and settings allow it, and is withheld otherwise.
168
+ - On Pro and Team, a local answer that passes is also confirmed by Synalux, on
169
+ a copy pseudonymized on your device; the mapping stays on your device. With
170
+ an image attached, or with the cloud off, the checked local answer is served.
171
+ - When the on-device screen's 4b is unsure about a conversation, the 9b reads
172
+ it again before the conversation is refused or sent to the cloud.
173
+ - Everything local is free with no account and no cap on the local model size.
174
+ A free Synalux account adds multi-turn `prism_infer` and 20 cloud answers a
175
+ day. Pro ($15/month) and Team ($20/seat/month) are Prism's own plans.
176
+ - Optional You.com web search: set `YDC_API_KEY` to enable
177
+ `youcom_web_search` (contributed by @mouse-value-add in #227).
178
+ - Fixed: the 2b and 4b no longer answer Prism's own requests as a tool router.
179
+ A system prompt built into those models had turned some answers into tool
180
+ calls and made the on-device screen refuse some ordinary requests.
181
+
182
+ ## What's New in v20.21.15
183
+
184
+ ### Follow-ups: see what runs locally, and a stricter screen when the classifier is down
185
+
186
+ - `local_savings` (and `prism savings`) shows a line for follow-ups, calls that
187
+ carried your conversation. It covers how many the local model answered (and
188
+ how many of those the 9b answered), how many the on-device screen refused and
189
+ at which stage, how many your plan's multi-turn limits refused, and how many
190
+ went to the cloud.
191
+ - A short chat answer ("16", "Yes") counts as an answer. Before, the quality
192
+ gate treated it as empty, and a paid plan re-asked the cloud.
193
+ - When the on-device classifier fails every read on a follow-up, the follow-up
194
+ is no longer answered on a keyword check alone. It goes to the cloud if your
195
+ plan allows it, and is refused otherwise.
196
+
161
197
  ## What's New in v20.21.14
162
198
 
163
199
  ### Skills load when they help, and you can see why they loaded
@@ -1135,7 +1171,7 @@ The free tier runs entirely on your machine. Paid tiers add cloud sync through t
1135
1171
  | Inference | Local Ollama models | Local models + Gemini 3.6 Flash fallback |
1136
1172
  | API keys required | None | Synalux subscription key |
1137
1173
  | Web search / scrape | Not included | Via Synalux portal (provider keys server-side) |
1138
- | What leaves your machine | Nothing | Memory text, file paths, search queries, and inference prompts/drafts when their cloud feature is used, sent to the portal over TLS. Cloud memory writes are PHI-redacted; inference and route requests are transient. |
1174
+ | What leaves your machine | Nothing | Memory text, file paths, search queries, and inference prompts/drafts when their cloud feature is used, sent to the portal over TLS. Cloud memory writes have personal identifiers redacted; inference and route requests are transient. |
1139
1175
  | Works offline | ✅ | Local features yes; sync/cloud no |
1140
1176
 
1141
1177
  **Handling sensitive data.** Cloud memory writes pass through automatic
@@ -1144,8 +1180,7 @@ and clinical identifiers are stripped before storage). Cloud inference and
1144
1180
  route correction send the request over TLS for processing and do not store it
1145
1181
  as Prism memory. The **local tier** (no Synalux key) is the air-gap;
1146
1182
  `route_guard: "local"` only skips the route correction, and `cloud_fallback`
1147
- and `verify` govern the other two channels. **Enterprise** includes a HIPAA
1148
- Business Associate Agreement.
1183
+ and `verify` govern the other two channels.
1149
1184
 
1150
1185
  ---
1151
1186
 
@@ -1171,8 +1206,8 @@ and telemetry still contact the portal and carry neither.
1171
1206
  |---|---|---|---|---|---|---|
1172
1207
  | Qwen3.5-4B Q4_K_S | `prism-coder:2b` | 3.3 GB | ✅ | 100% | On-device / lowest RAM (4.5 GiB free) | Free |
1173
1208
  | Qwen3.5-4B Q4_K_M | `prism-coder:4b` | 3.5 GB | ✅ | 100% | Verifier (5.2 GiB free) | Free |
1174
- | Qwen3.5-9B (LoRA) | `prism-coder:9b` | 6.7 GB | ✅ | 95.7%² | Default router / workhorse (9 GiB free) | Standard+ |
1175
- | Qwen3.5-27B (LoRA) | `prism-coder:27b` | 16.8 GB | — | 100% | Complex code / quality (21 GiB free) | Advanced+ |
1209
+ | Qwen3.5-9B (LoRA) | `prism-coder:9b` | 6.7 GB | ✅ | 95.7%² | Default router / workhorse (9 GiB free) | Free |
1210
+ | Qwen3.5-27B (LoRA) | `prism-coder:27b` | 16.8 GB | — | 100% | Complex code / quality (21 GiB free) | Free |
1176
1211
 
1177
1212
  ¹ Self-run on a narrow 115-case MCP tool-selection suite, `temperature: 0`,
1178
1213
  measured through the call path `prism_infer` actually uses (`/api/chat`, each
@@ -1284,74 +1319,64 @@ an image is never escalated; screenshots stay on this device.
1284
1319
 
1285
1320
  ## Why Prism Coder
1286
1321
 
1287
- ### vs AI coding assistants
1288
-
1289
- Product capabilities and plans change frequently. The comparison below is
1290
- intentionally limited to publicly documented differences; it is not a claim
1291
- that another product lacks an unlisted feature.
1292
-
1293
- Legend: ✅ documented, ◐ conditional or plan-dependent, — not compared, ? verify
1294
- with the provider.
1295
-
1296
- | Capability | Prism Coder | GitHub Copilot | Cursor | Amazon Q Developer |
1297
- |---|:---:|:---:|:---:|:---:|
1298
- | Local/open-weight inference | ✅ | ◐ | ◐ | ◐ |
1299
- | Offline workflow | ✅ | ◐ | ? | ? |
1300
- | Cross-session memory | ✅ | ◐ ([docs](https://docs.github.com/en/copilot/reference/copilot-cli-reference/cli-command-reference)) | ◐ | ◐ |
1301
- | MCP integration | ✅ | ✅ ([docs](https://docs.github.com/en/copilot/reference/copilot-cli-reference/cli-command-reference)) | ✅ ([pricing](https://cursor.com/en-US/pricing)) | ◐ |
1302
- | Local-first model routing | ✅ | ◐ | ◐ | ◐ |
1303
- | Session drift and grounding checks | ✅ | — | — | — |
1304
- | Setup surface | ✅ five hosts | ✅ CLI/IDE | ✅ editor/agents | ✅ IDE/CLI ([overview](https://aws.amazon.com/q/developer/build/)) |
1305
- | Pricing model | ✅ Synalux tiers | ◐ | ◐ ([pricing](https://cursor.com/en-US/pricing)) | ✅ free + $19 Pro ([pricing](https://aws.amazon.com/q/developer/pricing/)) |
1322
+ **Your local data stays on your device.** Memory, local models and the answer
1323
+ check run on your machine, with no account. Data leaves it only when you turn
1324
+ on a cloud feature, and then only what that feature needs; see
1325
+ [Local-first and privacy](#local-first-and-privacy).
1306
1326
 
1307
- Prism-specific compliance, contractual, and pricing terms are documented in
1308
- the Synalux service agreement. Do not infer a competitor's HIPAA, BAA, or data
1309
- handling status from this table.
1327
+ - **Free without an account**, with no cap on the local model size.
1328
+ - **Memory and models in one MCP server:** persistent memory, a knowledge graph
1329
+ and automatic routing to local Ollama models, with cloud answers only when
1330
+ your plan and settings allow them.
1331
+ - **Checked answers:** a multi-turn local answer is checked on your device
1332
+ before it is served.
1310
1333
 
1311
- ### vs local AI / memory tools
1334
+ ### How it compares
1312
1335
 
1313
- | Feature | Prism Coder | Ollama | LM Studio | Mem0 | Zep |
1314
- |---|:---:|:---:|:---:|:---:|:---:|
1315
- | Local inference cascade | ✅ | ✅ runtime | ✅ app | — | — |
1316
- | Cloud fallback | ✅ optional | — | ◐ provider-dependent | ◐ | ◐ |
1317
- | Persistent memory | ✅ | — | ◐ project context | ✅ | ✅ |
1318
- | Knowledge/tool integration | ✅ MCP + ingestion | ◐ APIs | ◐ integrations | ✅ SDK/API | ✅ SDK/API |
1319
- | MCP server | ✅ native | — | ◐ client integration | ◐ client integration | ◐ client integration |
1336
+ Prices from each vendor's pricing page, checked on 2026-09-26. Plans change
1337
+ often; follow the links for current terms. The table compares published plans
1338
+ only; it is not a claim that a product lacks an unlisted feature.
1320
1339
 
1321
- ### Pricing
1322
-
1323
- Prism's current published tiers are listed below. Competitor pricing is
1324
- usage- and plan-dependent, so consult the provider directly: [GitHub
1325
- Copilot](https://github.com/features/copilot/plans), [Cursor](https://cursor.com/en-US/pricing),
1326
- and [Amazon Q Developer](https://aws.amazon.com/q/developer/pricing/).
1340
+ | Product | What it is | Free | Individual | Team, per seat |
1341
+ |---|---|---|---|---|
1342
+ | **Prism Coder** | Local memory and model routing (MCP), optional cloud | Everything local, no account | Pro $15/mo | $20, 2-seat minimum |
1343
+ | [GitHub Copilot](https://docs.github.com/en/copilot/get-started/plans) | Coding assistant | Limited | Pro $10, Pro+ $39, Max $100 | Business $19, Enterprise $39 |
1344
+ | [Cursor](https://cursor.com/pricing) | AI code editor | Hobby, limited | Pro $20 | Teams $40 |
1345
+ | [Windsurf](https://devin.ai/blog/windsurf-pricing-plans) | AI code editor | Free plan | Pro $20, Max $200 | Teams $40 |
1346
+ | [Claude](https://claude.com/pricing) | Assistant; Claude Code from Pro | Free (no Claude Code) | Pro $20, Max from $100 | Team $25 ($20 billed annually) |
1347
+ | [Amazon Q Developer](https://aws.amazon.com/q/developer/pricing/) | Coding assistant | 50 agentic requests a month | Pro $19 per user | -- |
1348
+ | [Ollama](https://ollama.com/pricing) | Local model runtime, cloud models | Local models, starter cloud credits | Pro $20 ($60 of cloud credits), Max $100 | Team $500/mo, unlimited users |
1349
+ | [Mem0](https://mem0.ai/pricing) | Memory API | 10,000 adds, 1,000 retrievals a month | Starter $19; graph memory from Pro $249 | -- |
1350
+ | [Supermemory](https://supermemory.ai/pricing/) | Memory API | $5 of credits a month | Pro $19 (3 seats), Max $100 | -- |
1351
+ | [Zep](https://www.getzep.com/pricing/) | Memory (knowledge graph) | 10,000 credits a month | Flex $125 | -- |
1352
+ | [Pieces](https://pieces.app/pricing) | On-device developer memory | None (7-day trial) | Pro $18.99 | Enterprise $22.99 |
1353
+ | [MemPalace](https://www.mempalace.net/) | Open-source local memory (MIT) | Free | -- | -- |
1327
1354
 
1328
1355
  ---
1329
1356
 
1330
1357
  ## Plans
1331
1358
 
1332
- All on-device models are free to run locally via Ollama on every tier. A subscription gates **cloud** features, higher automatic-routing ceilings, and increased limits. On-device models run through your Ollama regardless of plan; the ceiling applies only to cloud inference and automatic `prism_infer` routing.
1359
+ Everything that runs on your machine is free, with no account and no cap on the local model. A free Synalux account adds what needs Synalux; Pro and Team add cloud features and higher limits.
1333
1360
 
1334
- | | **Free** | **Standard** $19/mo | **Advanced** $49/mo | **Enterprise** $99/mo |
1361
+ | | **Free** (no account) | **Free account** | **Pro** $15/mo | **Team** $20/seat/mo |
1335
1362
  |---|---|---|---|---|
1336
- | Seats | 1 | 1 | up to 5 | up to 25 |
1337
- | Automatic `prism_infer` ceiling | up to 4b | up to 9b | up to 27b | up to 27b |
1338
- | Cloud inference | -- | ✅ | ✅ | ✅ (priority) |
1339
- | Cloud Coder (Web IDE) | -- | ✅ | ✅ | ✅ (priority) |
1340
- | Cloud search | -- | ✅ | ✅ | ✅ |
1341
- | Max output tokens | 512 | 1,024 | 2,048 | 4,096 |
1342
- | Cloud fallback | -- | Gemini 3.6 Flash | Gemini 3.6 Flash | Gemini 3.6 Flash (priority) |
1343
- | Multi-turn `prism_infer` (conversation carried across calls) | -- | 12 turns / 32k chars | 20 turns / 64k chars | 30 turns / 96k chars |
1344
- | Grounding verifier (fact-check AI output) | -- | ✅ | ✅ | ✅ |
1345
- | Memory sync (cloud) | -- | ✅ | ✅ | ✅ |
1346
- | Knowledge / session memory | limited | unlimited | unlimited | unlimited |
1347
- | Analytics dashboard | -- | ✅ | ✅ | ✅ |
1348
- | HIPAA BAA | -- | -- | -- | ✅ |
1349
-
1350
- Paid plans start with a 14-day trial. No card is required to start; add payment
1363
+ | Local models via Ollama (automatic `prism_infer` routing) | up to 27b | up to 27b | up to 27b | up to 27b |
1364
+ | Local memory, knowledge graph, dashboard, MCP tools | ✅ | ✅ | ✅ | ✅ |
1365
+ | Multi-turn `prism_infer`, with the on-device answer check | -- | 12 turns / 32k chars | 12 turns / 32k chars | 20 turns / 64k chars |
1366
+ | Cloud answers (Gemini 3.6 Flash; a free account's conversations: 3.5 Flash-Lite) | -- | 20/day | 1,000/month (500 in the trial; 200/day max) | 1,500/month per seat (200/day max) |
1367
+ | Cloud confirmation of a local answer (on a pseudonymized copy) | -- | -- | ✅ | ✅ |
1368
+ | Grounding verifier (fact-check AI output; counts as a cloud answer) | -- | -- | ✅ | ✅ |
1369
+ | Cloud search | -- | -- | 1,000/month (150/day max) | 1,500/month per seat (150/day max) |
1370
+ | Memory sync (cloud) | -- | -- | ✅ | ✅ |
1371
+ | Full skill library, super-skills, agent routing, account and team skills | -- | -- | ✅ | ✅ |
1372
+ | Seats | 1 | 1 | 1 | 2 or more |
1373
+
1374
+ Pro starts with a 14-day trial. No card is required to start; add payment
1351
1375
  details before the trial ends to continue, or the subscription cancels
1352
- automatically and local Prism Free remains available. View the exact deadline
1353
- under **⚙ Account & Settings → Account** in `prism dashboard`. 25+ seats:
1354
- [contact sales](https://synalux.ai/support)
1376
+ automatically and Prism Free remains available. View the exact deadline under
1377
+ **⚙ Account & Settings → Account** in `prism dashboard`. A cloud confirmation
1378
+ counts as one of the day's cloud answers. To start a Team plan:
1379
+ [contact sales](https://synalux.ai/support).
1355
1380
 
1356
1381
  ---
1357
1382
 
@@ -1445,11 +1470,11 @@ It is paid because it cannot run without Synalux behind it:
1445
1470
  ```typescript
1446
1471
  // Call 1
1447
1472
  prism_infer({ prompt: "My project codename is Nightjar. Reply OK.", mode: "chat" })
1448
- // → "OK" (local 9b, $0)
1473
+ // → "OK" (local model, $0)
1449
1474
 
1450
1475
  // Call 2 — the model never saw call 1
1451
1476
  prism_infer({ prompt: "What is my codename? One word.", mode: "chat" })
1452
- // → "I don't have that information." (local 9b, correct and useless)
1477
+ // → "I don't have that information." (local model, correct and useless)
1453
1478
 
1454
1479
  // Call 3 — a coding follow-up with no thread
1455
1480
  prism_infer({ prompt: "Now add a timeout parameter to it.", mode: "code" })
@@ -1474,7 +1499,7 @@ prism_infer({
1474
1499
  prompt: "What is my codename? One word.",
1475
1500
  mode: "chat",
1476
1501
  })
1477
- // → "Nightjar" (local 9b, $0; history_turns: 2)
1502
+ // → "Nightjar" (local model, $0; history_turns: 2)
1478
1503
 
1479
1504
  prism_infer({
1480
1505
  messages: [
@@ -1484,7 +1509,7 @@ prism_infer({
1484
1509
  prompt: "Write the one-line call that stores its result in n.",
1485
1510
  mode: "code",
1486
1511
  })
1487
- // → "n = countActiveUsers(data)" (local 9b, $0)
1512
+ // → "n = countActiveUsers(data)" (local model, $0)
1488
1513
 
1489
1514
  // A turn the on-device screen finds uncertain, alone or in context, is not
1490
1515
  // served locally: it goes to Synalux cloud on a paid plan, or is refused with
@@ -1501,9 +1526,14 @@ prism_infer({
1501
1526
  // → "SYN-4471" (Gemini 3.6 Flash; used_cloud: true)
1502
1527
  ```
1503
1528
 
1504
- Measured on the real 9b through the real handler with cloud off: 7 of 12
1505
- benign follow-ups are served locally, the rest refuse and name the reason;
1506
- every injection variant the reviewers built refuses.
1529
+ Measured on 20.21.14 through the real handler, on the local 9b with cloud off:
1530
+ - Every benign follow-up that reached the local 9b with its conversation
1531
+ attached was answered correctly. Without the conversation, most answers were
1532
+ invented.
1533
+ - The on-device screen still refuses too many benign follow-ups: 11 of 26 in
1534
+ our tests. Each refusal names its reason. Cutting these false refusals is
1535
+ the current work.
1536
+ - Every reserved multi-turn probe was refused before any generation (11 of 11).
1507
1537
  </details>
1508
1538
 
1509
1539
  | Mode | Think | Model | Use case |
@@ -1556,6 +1586,10 @@ host, or run `prism savings` from a terminal — `--period all|month|week|sessio
1556
1586
  prism-coder:9b: 41 call(s), ~505K tokens
1557
1587
  prism-coder:4b: 12 call(s), ~4.8K tokens
1558
1588
 
1589
+ Follow-ups with your conversation:
1590
+ 12 answered locally (12 by the 9b) · 2 refused by the on-device screen · 0 sent to cloud
1591
+ Refusals by stage: follow-up alone 1 · turns together 1
1592
+
1559
1593
  Counts tokens a local model handled instead of your cloud model. On the token
1560
1594
  axis, the token count is measured — a floor, with known undercounts listed
1561
1595
  when present. On the displacement axis, prism cannot observe the call your
@@ -1577,6 +1611,21 @@ the same tokens; and most users are on flat plans where a currency figure means
1577
1611
  nothing at all. Tokens are the one unit prism measured itself. If you know your
1578
1612
  own effective rate, multiply — the split is printed for exactly that reason.
1579
1613
 
1614
+ **Follow-ups.** Calls that carried your conversation (`messages`) get their
1615
+ own line:
1616
+ - how many the local model answered, and how many of those the 9b answered;
1617
+ - how many the on-device screen refused, and at which stage (the follow-up read
1618
+ alone, an earlier turn read alone, or the turns read together);
1619
+ - how many went to the cloud.
1620
+
1621
+ A refused follow-up went back to your host instead of being answered locally,
1622
+ so this line shows how much of your follow-up work local serving actually
1623
+ took. Refusals because your plan does not include multi-turn history, or the
1624
+ history is over its cap, are counted on their own line. They are recorded only
1625
+ when the host asks for a report (`escalation: "report"`); otherwise they fail
1626
+ before anything is recorded. The line appears for the `week`, `month`, `all`
1627
+ and `--days` views, which read the durable ledger.
1628
+
1580
1629
  Refused calls are excluded, the VS Code panel-playground share is disclosed
1581
1630
  separately, and the known sources of undercount are listed inline rather than
1582
1631
  left implicit — so the durable (`week`/`month`/`all`/`--days`) headline is a
@@ -1682,10 +1731,10 @@ command surface, safety model, and verified acceptance cases.
1682
1731
  <img src="docs/scm_search_cli.jpg" alt="prism search — semantic code search with relevance scores" width="500" />
1683
1732
  </p>
1684
1733
 
1685
- ### `prism review` — AI code review with HIPAA checks
1734
+ ### `prism review` — AI code review with security and privacy checks
1686
1735
 
1687
1736
  <p align="center">
1688
- <img src="docs/scm_review_cli.jpg" alt="prism review — AI code review with security and HIPAA findings" width="400" />
1737
+ <img src="docs/scm_review_cli.jpg" alt="prism review — AI code review with security and privacy findings" width="400" />
1689
1738
  </p>
1690
1739
 
1691
1740
  ### `prism scan` — security scanner for secrets, Dockerfiles, licenses
@@ -1745,9 +1794,9 @@ AI chat, voice input, SOAP note generator, team collaboration, and video calls
1745
1794
  <summary>Feature details</summary>
1746
1795
 
1747
1796
  - **AI**: Chat participant (`@synalux`), multi-agent pipeline, voice input, model switching, 10 tones
1748
- - **Clinical**: SOAP note generator, role-based access, document signing, patient board
1797
+ - **Clinical**: SOAP note generator, role-based access, document signing
1749
1798
  - **Collaboration**: Team chat, DMs, video calls, customer board, visual builder, DevContainers
1750
- - **Privacy**: Local Ollama by default. `preferLocal=true` tries local first. Enterprise BAA available.
1799
+ - **Privacy**: Local Ollama by default. `preferLocal=true` tries local first.
1751
1800
  </details>
1752
1801
 
1753
1802
  ### Prism AAC
@@ -1846,25 +1895,37 @@ It reads `~/.prism-mcp/data.db` and POSTs entries to the portal. Ledger entries
1846
1895
 
1847
1896
  | Feature | Details |
1848
1897
  |---------|---------|
1849
- | Local inference | Direct Ollama use is unrestricted; automatic `prism_infer` routing selects up to 4B |
1898
+ | Local inference | Direct Ollama use is unrestricted; automatic `prism_infer` routing selects up to 27B, as far as your machine's memory allows |
1850
1899
  | Session memory | Persistent sessions, handoffs, ledger — all local SQLite |
1851
1900
  | Knowledge search | Semantic search across session history |
1852
1901
  | Skills | All skills available locally (run `sync-skills.sh` to populate) |
1853
1902
  | Drift detection | Server-side GATE 5 reminders |
1854
1903
 
1855
- ### Paid (Synalux subscription)
1904
+ ### Free account (Synalux sign-in, $0)
1856
1905
 
1857
1906
  Everything in Free, plus:
1858
1907
 
1859
1908
  | Feature | Details |
1860
1909
  |---------|---------|
1861
- | Model ceiling | Automatic `prism_infer` routing up to 27B + Gemini 3.6 Flash fallback when local is unavailable |
1910
+ | Multi-turn `prism_infer` | Conversation history carried across calls; each local answer is checked on your device before it is served |
1911
+ | Cloud answers | 20 a day from Gemini when a local answer cannot be used: 3.5 Flash-Lite in a conversation, 3.6 Flash for a single prompt |
1912
+
1913
+ Sign in from `prism dashboard` under **Account**.
1914
+
1915
+ ### Pro ($15/mo) and Team ($20/seat/mo, 2-seat minimum)
1916
+
1917
+ Everything in the free account, plus:
1918
+
1919
+ | Feature | Details |
1920
+ |---------|---------|
1921
+ | Cloud answers | 1,000 a month (Pro; 500 during the 14-day trial) or 1,500 a month per seat (Team), at most 200 a day, from Gemini 3.6 Flash; a cloud confirmation counts as one |
1922
+ | Answer confirmation | A local answer that passed the on-device check is also confirmed by Synalux, on a copy with names, dates and other identifiers replaced on your device |
1862
1923
  | Skill routing | Portal resolves which skills to load based on your project and prompt |
1863
1924
  | Cross-device memory | Supabase cloud sync — sessions survive across machines |
1864
1925
  | Grounding verifier | L3 NLI verification on model outputs |
1865
- | Team features | Multi-agent Hivemind, workspace collaboration |
1926
+ | Team features | Multi-agent Hivemind, workspace collaboration, account and team skills |
1866
1927
 
1867
- The paid tier adds **intelligent routing** — the Synalux portal determines which skills are relevant to your current project and prompt, so your agent gets domain expertise (stripe patterns, training protocols, clinical standards) instead of loading everything. Free users with the repo can run `sync-skills.sh` to populate all skills locally; paid routing adds project-aware and prompt-aware selection.
1928
+ The paid tiers add **intelligent routing** — the Synalux portal determines which skills are relevant to your current project and prompt, so your agent gets domain expertise (stripe patterns, training protocols, clinical standards) instead of loading everything. Free users with the repo can run `sync-skills.sh` to populate all skills locally; paid routing adds project-aware and prompt-aware selection.
1868
1929
 
1869
1930
  - Contributions require signing the [CLA](./CLA.md).
1870
1931
  - "Prism" and "Synalux" are trade names of Synalux LLC; the Apache license does
package/dist/config.js CHANGED
@@ -14,6 +14,9 @@ import { fileURLToPath } from "node:url";
14
14
  * is needed. Get one at https://brave.com/search/api/
15
15
  * GOOGLE_API_KEY — (optional) API key for Google AI Studio / Gemini. Enables paper analysis.
16
16
  * BRAVE_ANSWERS_API_KEY — (optional) API key for Brave Answers (AI grounding). Enables brave_answers tool.
17
+ * YDC_API_KEY — (optional) API key for You.com Search. When set, the optional
18
+ * youcom_web_search tool is registered as an alternative web search
19
+ * provider. Get one at https://you.com/platform/api-keys
17
20
  * SUPABASE_URL — (optional) Your Supabase project URL. Enables session memory tools.
18
21
  * SUPABASE_KEY — (optional) Your Supabase anon/service key. Enables session memory tools.
19
22
  * PRISM_USER_ID — (optional) Unique tenant ID for multi-user Supabase instances.
@@ -72,6 +75,18 @@ export const SEMANTIC_SCHOLAR_API_KEY = process.env.SEMANTIC_SCHOLAR_API_KEY;
72
75
  if (!BRAVE_ANSWERS_API_KEY && process.env.PRISM_DEBUG_LOGGING === "true") {
73
76
  console.error("Warning: BRAVE_ANSWERS_API_KEY environment variable is missing. Brave Answers tool will be unavailable.");
74
77
  }
78
+ // ─── Optional: You.com Search API Key ─────────────────────────
79
+ // Used by the youcom_web_search tool. When set, registers an optional
80
+ // web search tool that uses You.com's search API.
81
+ // Get a key at https://you.com/platform/api-keys
82
+ // You.com's own docs use YDC_API_KEY: https://you.com/docs/using-the-api/authentication
83
+ // Trimmed, and blank means unset: a whitespace-only value would otherwise
84
+ // register the tool while every call reports the key as not configured.
85
+ export const YDC_API_KEY = process.env.YDC_API_KEY?.trim() || undefined;
86
+ if (process.env.PRISM_DEBUG_LOGGING === "true") {
87
+ // Log presence or absence — non-critical, just informational
88
+ console.error(`[Prism] You.com search: ${YDC_API_KEY ? "configured" : "not configured (youcom_web_search tool disabled)"}`);
89
+ }
75
90
  // ─── Optional: Voyage AI Embeddings ──────────────────────────
76
91
  // Set embedding_provider=voyage to enable. Requires VOYAGE_API_KEY.
77
92
  export const VOYAGE_API_KEY = process.env.VOYAGE_API_KEY; // embedding_provider=voyage
@@ -1,4 +1,5 @@
1
1
  import { invalidateEntitlements } from "../utils/entitlements.js";
2
+ import { clearInferencePolicies } from "../utils/inferencePolicy.js";
2
3
  import { getSynaluxJwt, invalidateSynaluxJwt } from "../utils/synaluxJwt.js";
3
4
  import { resolvePortalBaseUrl, usablePortalKey } from "../utils/synaluxSearch.js";
4
5
  import { isSynaluxSignedOut, setSynaluxSignedOut } from "../utils/synaluxCredentialState.js";
@@ -31,6 +32,7 @@ const defaultDeps = {
31
32
  getJwt: getSynaluxJwt,
32
33
  invalidateJwt: invalidateSynaluxJwt,
33
34
  invalidateEntitlements,
35
+ clearInferencePolicies,
34
36
  closeStorage: async () => (await import("../storage/index.js")).closeStorage(),
35
37
  resolvePortalBaseUrl,
36
38
  usablePortalKey,
@@ -139,6 +141,7 @@ function normalizeAccount(value, origin) {
139
141
  role_key: typeof raw.role_key === "string" ? raw.role_key.slice(0, 80) : null,
140
142
  plan: plan,
141
143
  subscription_plan: subscriptionPlan,
144
+ prism_plan: raw.prism_plan === "pro" || raw.prism_plan === "team" ? raw.prism_plan : null,
142
145
  plan_source: raw.plan_source === "managed" ? "managed" : "stripe",
143
146
  billing_status: billingStatus,
144
147
  trial_ends_at: billingStatus === "trialing" || billingStatus === "sync_pending" ? trialEndsAt : null,
@@ -245,6 +248,7 @@ export async function connectDashboardAccount(code, overrides = {}) {
245
248
  process.env.PRISM_SYNALUX_API_KEY = token;
246
249
  deps.invalidateJwt();
247
250
  deps.invalidateEntitlements();
251
+ deps.clearInferencePolicies();
248
252
  await deps.closeStorage();
249
253
  return accountWithJwt(deps, origin);
250
254
  });
@@ -298,6 +302,7 @@ export async function signOutDashboardAccount(overrides = {}) {
298
302
  delete process.env.PRISM_SYNALUX_API_KEY;
299
303
  deps.invalidateJwt();
300
304
  deps.invalidateEntitlements();
305
+ deps.clearInferencePolicies();
301
306
  await deps.setSetting("PRISM_SYNALUX_SIGNED_OUT", "true");
302
307
  await deps.setSetting("PRISM_SYNALUX_API_KEY", "");
303
308
  await deps.closeStorage();
@@ -1428,7 +1428,7 @@ export function renderDashboardHTML(version) {
1428
1428
  </div>
1429
1429
 
1430
1430
  <div style="margin-top:1.25rem;padding:0.85rem;border-radius:var(--radius-sm);background:rgba(16,185,129,0.08);border:1px solid rgba(16,185,129,0.2);font-size:0.78rem;color:var(--text-secondary)">
1431
- ✅ <strong>All 6 enforcement layers are active.</strong> Ethics enforcement is NOT tier-gated — applies equally to Free, Standard, Advanced, and Enterprise.
1431
+ ✅ <strong>All 6 enforcement layers are active.</strong> Ethics enforcement is NOT tier-gated — applies equally to Free, the free account, Pro and Team.
1432
1432
  <a href="/ethics" style="color:var(--accent-purple);margin-left:0.5rem">View full policy →</a>
1433
1433
  </div>
1434
1434
  </div>
@@ -2109,13 +2109,22 @@ var _searchBoost = document.getElementById('searchContextBoost');
2109
2109
  if (_searchBoost)
2110
2110
  _searchBoost.addEventListener('change', performSearch);
2111
2111
  var currentAccount = null;
2112
+ // Prism's own Pro and Team arrive as the tier they include (plan) plus
2113
+ // prism_plan; show their names when the tier is theirs.
2114
+ function accountDisplayPlan(account) {
2115
+ if (account && account.prism_plan === 'pro' && account.plan === 'standard') return 'pro';
2116
+ if (account && account.prism_plan === 'team' && account.plan === 'advanced') return 'team';
2117
+ return account ? account.plan : 'free';
2118
+ }
2112
2119
  function accountPlanLabel(plan) {
2113
- var labels = { free: 'Free', standard: 'Standard', advanced: 'Advanced', enterprise: 'Enterprise' };
2120
+ var labels = { free: 'Free', pro: 'Pro', team: 'Team', standard: 'Standard', advanced: 'Advanced', enterprise: 'Enterprise' };
2114
2121
  return labels[plan] || 'Free';
2115
2122
  }
2116
2123
  function accountPlanSummary(plan) {
2117
2124
  var summaries = {
2118
2125
  free: 'Local memory, local models, and the Mind Palace dashboard remain available without an account.',
2126
+ pro: 'Prism Pro is active: cloud answers, answer confirmation, sync, and the full skill library.',
2127
+ team: 'Prism Team is active for this seat: cloud answers, answer confirmation, sync, and team skills.',
2119
2128
  standard: 'Cloud sync and paid Prism services are active for this account.',
2120
2129
  advanced: 'Advanced cloud capacity and team features are active for this account.',
2121
2130
  enterprise: 'Enterprise capacity, governance, and support are active for this account.'
@@ -2141,13 +2150,13 @@ function accountBillingBadge(account) {
2141
2150
  function accountSubscriptionSummary(account) {
2142
2151
  if (account.billing_status === 'trialing') {
2143
2152
  var end = accountTrialEndLabel(account.trial_ends_at);
2144
- return accountPlanLabel(account.plan) + ' trial is active' + (end ? ' through ' + end : '') +
2153
+ return accountPlanLabel(accountDisplayPlan(account)) + ' trial is active' + (end ? ' through ' + end : '') +
2145
2154
  '. Add payment details before the trial ends to continue; otherwise it cancels automatically.';
2146
2155
  }
2147
2156
  if (['past_due', 'unpaid', 'incomplete', 'paused'].indexOf(account.billing_status) >= 0) {
2148
2157
  var billedPlan = account.subscription_plan
2149
2158
  ? accountPlanLabel(account.subscription_plan) + ' plan'
2150
- : account.plan === 'free' ? 'paid subscription' : accountPlanLabel(account.plan) + ' plan';
2159
+ : account.plan === 'free' ? 'paid subscription' : accountPlanLabel(accountDisplayPlan(account)) + ' plan';
2151
2160
  return 'Your ' + billedPlan + ' needs billing attention. Update payment details to restore or keep cloud features active.';
2152
2161
  }
2153
2162
  if (account.billing_status === 'sync_pending') {
@@ -2155,7 +2164,7 @@ function accountSubscriptionSummary(account) {
2155
2164
  var syncTrialEnd = accountTrialEndLabel(account.trial_ends_at);
2156
2165
  return 'Stripe confirms your ' + syncingPlan + ' subscription' +
2157
2166
  (syncTrialEnd ? ' with a trial through ' + syncTrialEnd : '') +
2158
- '. Synalux is updating access; current access remains ' + accountPlanLabel(account.plan) + '. Retry shortly or manage the subscription.';
2167
+ '. Synalux is updating access; current access remains ' + accountPlanLabel(accountDisplayPlan(account)) + '. Retry shortly or manage the subscription.';
2159
2168
  }
2160
2169
  if (account.billing_status === 'canceled' || account.billing_status === 'incomplete_expired') {
2161
2170
  return 'This paid subscription is no longer active. Local Prism Free remains available.';
@@ -2163,7 +2172,7 @@ function accountSubscriptionSummary(account) {
2163
2172
  if (account.billing_status === 'unknown') {
2164
2173
  return 'Prism could not verify the current trial or payment status. Open billing to review the subscription; local features remain available.';
2165
2174
  }
2166
- return accountPlanSummary(account.plan);
2175
+ return accountPlanSummary(accountDisplayPlan(account));
2167
2176
  }
2168
2177
  function renderPlanLadder(plan) {
2169
2178
  var tiers = [
@@ -2199,7 +2208,7 @@ function renderAccountChip(account, error) {
2199
2208
  ? planStatus
2200
2209
  : account.plan === 'free' && planStatus === 'Payment due'
2201
2210
  ? planStatus
2202
- : accountPlanLabel(account.plan) + planStatus;
2211
+ : accountPlanLabel(accountDisplayPlan(account)) + planStatus;
2203
2212
  chip.innerHTML = '<span class="role-icon">🤖</span><span class="identity-label">' + escapeHtml(account.name || 'Synalux user') + '</span>' +
2204
2213
  '<span class="plan-mini' + planStatusClass + '">' + escapeHtml(chipPlanLabel) + '</span>';
2205
2214
  }
@@ -2249,7 +2258,7 @@ function renderAccountPanel(account, error) {
2249
2258
  account.billing && account.billing.action === 'included' ? 'View plans' : 'Start 14-day trial';
2250
2259
  managedBadge = account.plan_source === 'managed' ? '<span class="account-badge managed">Managed</span>' : '';
2251
2260
  panel.innerHTML = '<div class="account-card"><div class="account-card-head"><div>' +
2252
- '<div class="account-badges"><span class="account-badge">' + escapeHtml(accountPlanLabel(account.plan)) + '</span>' +
2261
+ '<div class="account-badges"><span class="account-badge">' + escapeHtml(accountPlanLabel(accountDisplayPlan(account))) + '</span>' +
2253
2262
  '<span class="account-badge role">' + escapeHtml(role) + '</span>' + managedBadge + accountBillingBadge(account) + '</div>' +
2254
2263
  '<div class="account-name">' + escapeHtml(account.name || 'Synalux user') + '</div>' +
2255
2264
  '<div class="account-summary">' + escapeHtml(accountSubscriptionSummary(account)) + '</div></div></div>' +
package/dist/server.js CHANGED
@@ -60,7 +60,7 @@ ListResourcesRequestSchema, ListResourceTemplatesRequestSchema, ReadResourceRequ
60
60
  // Claude Desktop that the attached resource has changed.
61
61
  // Without this, the paperclipped context becomes stale.
62
62
  SubscribeRequestSchema, UnsubscribeRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
63
- import { SERVER_CONFIG, SESSION_MEMORY_ENABLED, PRISM_USER_ID, PRISM_ENABLE_HIVEMIND, WATCHDOG_INTERVAL_MS, WATCHDOG_STALE_MIN, WATCHDOG_FROZEN_MIN, WATCHDOG_OFFLINE_MIN, WATCHDOG_LOOP_THRESHOLD, PRISM_SCHEDULER_ENABLED, PRISM_SCHEDULER_INTERVAL_MS, PRISM_HDC_ENABLED, PRISM_TASK_ROUTER_ENABLED_ENV, PRISM_DARK_FACTORY_ENABLED, } from "./config.js";
63
+ import { SERVER_CONFIG, SESSION_MEMORY_ENABLED, PRISM_USER_ID, PRISM_ENABLE_HIVEMIND, WATCHDOG_INTERVAL_MS, WATCHDOG_STALE_MIN, WATCHDOG_FROZEN_MIN, WATCHDOG_OFFLINE_MIN, WATCHDOG_LOOP_THRESHOLD, PRISM_SCHEDULER_ENABLED, PRISM_SCHEDULER_INTERVAL_MS, PRISM_HDC_ENABLED, PRISM_TASK_ROUTER_ENABLED_ENV, PRISM_DARK_FACTORY_ENABLED, YDC_API_KEY, } from "./config.js";
64
64
  import { startWatchdog, drainAlerts } from "./hivemindWatchdog.js";
65
65
  import { startScheduler } from "./backgroundScheduler.js";
66
66
  import { startDarkFactoryRunner } from "./darkfactory/runner.js";
@@ -91,7 +91,7 @@ import { triggerSkillManifestSync } from "./skillManifestSync.js";
91
91
  import { EVIDENCE_WORKFLOW_POLICY_TEXT } from "./evidenceWorkflowPolicy.js";
92
92
  import { LOCAL_FIRST_POLICY_TEXT } from "./localFirstPolicy.js";
93
93
  // ─── Import Tool Definitions (schemas) and Handlers (implementations) ─────
94
- import { WEB_SEARCH_TOOL, BRAVE_WEB_SEARCH_CODE_MODE_TOOL, LOCAL_SEARCH_TOOL, BRAVE_LOCAL_SEARCH_CODE_MODE_TOOL, CODE_MODE_TRANSFORM_TOOL, BRAVE_ANSWERS_TOOL, RESEARCH_PAPER_ANALYSIS_TOOL, webSearchHandler, braveWebSearchCodeModeHandler, localSearchHandler, braveLocalSearchCodeModeHandler, codeModeTransformHandler, braveAnswersHandler, researchPaperAnalysisHandler, } from "./tools/index.js";
94
+ import { WEB_SEARCH_TOOL, BRAVE_WEB_SEARCH_CODE_MODE_TOOL, LOCAL_SEARCH_TOOL, BRAVE_LOCAL_SEARCH_CODE_MODE_TOOL, CODE_MODE_TRANSFORM_TOOL, BRAVE_ANSWERS_TOOL, RESEARCH_PAPER_ANALYSIS_TOOL, YOUCOM_WEB_SEARCH_TOOL, webSearchHandler, braveWebSearchCodeModeHandler, localSearchHandler, braveLocalSearchCodeModeHandler, codeModeTransformHandler, braveAnswersHandler, researchPaperAnalysisHandler, youcomWebSearchHandler, } from "./tools/index.js";
95
95
  // Session memory tools — only used if Supabase is configured
96
96
  import { SESSION_SAVE_LEDGER_TOOL, SESSION_SAVE_HANDOFF_TOOL, SESSION_LOAD_CONTEXT_TOOL, SESSION_BOOTSTRAP_TOOL, SESSION_ROUTE_PROMPT_TOOL, KNOWLEDGE_SEARCH_TOOL, KNOWLEDGE_FORGET_TOOL,
97
97
  // ─── v0.4.0: New tool definitions (Enhancements #2 and #4) ───
@@ -180,6 +180,8 @@ const BASE_TOOLS = [
180
180
  BRAVE_ANSWERS_TOOL, // brave_answers — AI-grounded answers
181
181
  RESEARCH_PAPER_ANALYSIS_TOOL, // gemini_research_paper_analysis — paper analysis
182
182
  PRISM_INFER_TOOL, // prism_infer — local-first inference (token-saving cascade)
183
+ // youcom_web_search — optional You.com web search, gated on YDC_API_KEY
184
+ ...(YDC_API_KEY ? [YOUCOM_WEB_SEARCH_TOOL] : []),
183
185
  ];
184
186
  // Hook-free first-turn startup is owned by session_bootstrap. Its static tool
185
187
  // description is surfaced by native MCP hosts before any tool call and keeps
@@ -748,6 +750,10 @@ export function createServer() {
748
750
  case "prism_infer":
749
751
  result = await prismInferHandler(args);
750
752
  break;
753
+ // ── Optional You.com Web Search (only when YDC_API_KEY is set) ──
754
+ case "youcom_web_search":
755
+ result = await youcomWebSearchHandler(args);
756
+ break;
751
757
  // ── Session Memory Tools (only callable when Supabase is configured) ──
752
758
  // REVIEWER NOTE: Even though these tools won't appear in the
753
759
  // tool list without Supabase, we still guard each handler call
@@ -42,8 +42,8 @@ const LEDGER_UNAVAILABLE_ERROR = "Inference metrics ledger is unavailable";
42
42
  const INSERT_METRIC_SQL = `INSERT OR IGNORE INTO infer_metrics
43
43
  (ts, caller, mode, backend, model, used_cloud, gate_outcome,
44
44
  refusal_reason, prompt_tokens, completion_tokens, latency_ms, ram_free_mb,
45
- source_event_id, history_turns, refusal_layer)
46
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`;
45
+ source_event_id, history_turns, refusal_layer, layer1_second_read)
46
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`;
47
47
  function closeClient(context) {
48
48
  const activeClient = client;
49
49
  client = null;
@@ -85,7 +85,8 @@ function ensureTable() {
85
85
  ram_free_mb INTEGER,
86
86
  source_event_id TEXT,
87
87
  history_turns INTEGER,
88
- refusal_layer TEXT
88
+ refusal_layer TEXT,
89
+ layer1_second_read TEXT
89
90
  )`);
90
91
  // Existing ledgers predate external panel-spool ingestion. SQLite has
91
92
  // no ADD COLUMN IF NOT EXISTS, so use the repository's established
@@ -94,6 +95,7 @@ function ensureTable() {
94
95
  "source_event_id TEXT",
95
96
  "history_turns INTEGER",
96
97
  "refusal_layer TEXT",
98
+ "layer1_second_read TEXT",
97
99
  ]) {
98
100
  try {
99
101
  await client.execute(`ALTER TABLE infer_metrics ADD COLUMN ${column}`);
@@ -158,6 +160,7 @@ function metricArgs(row) {
158
160
  row.completion_tokens ?? null, row.latency_ms ?? null,
159
161
  row.ram_free_mb ?? null, row.source_event_id ?? null,
160
162
  row.history_turns ?? null, row.refusal_layer ?? null,
163
+ row.layer1_second_read ?? null,
161
164
  ];
162
165
  }
163
166
  /** Aggregate all persisted rows (optionally since a timestamp). */
@@ -284,6 +287,32 @@ export async function queryLocalSavings(sinceTs) {
284
287
  GROUP BY COALESCE(model, backend)`,
285
288
  args: whereArgs,
286
289
  });
290
+ const followWhere = `WHERE history_turns > 0${sinceTs != null ? " AND ts >= ?" : ""}`;
291
+ const PLAN_REFUSAL = `COALESCE(refusal_reason, '') IN ('multi_turn_not_in_plan', 'history_over_plan_cap')`;
292
+ // A 9b model: '9b' right after a non-digit, so a future '19b' is not counted.
293
+ const NINE_B = `(LOWER(COALESCE(model, backend)) GLOB '*[^0-9]9b*' OR LOWER(COALESCE(model, backend)) GLOB '9b*')`;
294
+ const follow = await client.execute({
295
+ sql: `SELECT
296
+ SUM(CASE WHEN ${SERVED_LOCAL} THEN 1 ELSE 0 END) AS served,
297
+ SUM(CASE WHEN ${SERVED_LOCAL} AND ${NINE_B} THEN 1 ELSE 0 END) AS served_9b,
298
+ SUM(CASE WHEN used_cloud = 1 THEN 1 ELSE 0 END) AS cloud,
299
+ SUM(CASE WHEN used_cloud = 0 AND NOT (${SERVED_LOCAL}) AND NOT (${PLAN_REFUSAL}) THEN 1 ELSE 0 END) AS refused,
300
+ SUM(CASE WHEN used_cloud = 0 AND NOT (${SERVED_LOCAL}) AND ${PLAN_REFUSAL} THEN 1 ELSE 0 END) AS refused_by_plan
301
+ FROM infer_metrics ${followWhere}`,
302
+ args: whereArgs,
303
+ });
304
+ const followLayers = await client.execute({
305
+ sql: `SELECT COALESCE(refusal_layer, 'unrecorded') AS layer, COUNT(*) AS n
306
+ FROM infer_metrics ${followWhere} AND used_cloud = 0 AND NOT (${SERVED_LOCAL})
307
+ AND NOT (${PLAN_REFUSAL})
308
+ GROUP BY COALESCE(refusal_layer, 'unrecorded')`,
309
+ args: whereArgs,
310
+ });
311
+ const f = follow.rows[0];
312
+ const refused_by_layer = {};
313
+ for (const row of followLayers.rows) {
314
+ refused_by_layer[String(row.layer)] = Number(row.n ?? 0);
315
+ }
287
316
  const r = agg.rows[0];
288
317
  const by_model = {};
289
318
  for (const row of byM.rows) {
@@ -310,6 +339,14 @@ export async function queryLocalSavings(sinceTs) {
310
339
  first_ts: r.first_ts == null ? null : Number(r.first_ts),
311
340
  last_ts: r.last_ts == null ? null : Number(r.last_ts),
312
341
  by_model,
342
+ followups: {
343
+ served_local: Number(f.served ?? 0),
344
+ served_local_9b: Number(f.served_9b ?? 0),
345
+ refused: Number(f.refused ?? 0),
346
+ refused_by_plan: Number(f.refused_by_plan ?? 0),
347
+ cloud: Number(f.cloud ?? 0),
348
+ refused_by_layer,
349
+ },
313
350
  };
314
351
  }
315
352
  catch (e) {