create-workframe 0.1.2 → 0.1.3

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 (91) hide show
  1. package/README.md +14 -1
  2. package/SECURITY.md +3 -3
  3. package/bin/create-workframe.js +3 -3
  4. package/package.json +1 -1
  5. package/workframe-api/README.md +3 -5
  6. package/workframe-api/concierge.py +173 -0
  7. package/workframe-api/llm_error_glossary.py +236 -0
  8. package/workframe-api/llm_proxy.py +3 -0
  9. package/workframe-api/openrouter_catalog.py +114 -0
  10. package/workframe-api/package.json +1 -1
  11. package/workframe-api/server.py +144 -48
  12. package/workframe-ui/public/assets/{arc-COAT3laO.js → arc-CRhulRPr.js} +1 -1
  13. package/workframe-ui/public/assets/architecture-7EHR7CIX-CwxC-UJz.js +1 -0
  14. package/workframe-ui/public/assets/{architectureDiagram-3BPJPVTR-BFjWV24l.js → architectureDiagram-3BPJPVTR-B1Gq5onc.js} +1 -1
  15. package/workframe-ui/public/assets/{blockDiagram-GPEHLZMM-DSQLPfrj.js → blockDiagram-GPEHLZMM-DtXEetq-.js} +1 -1
  16. package/workframe-ui/public/assets/{c4Diagram-AAUBKEIU-DKEHv1t2.js → c4Diagram-AAUBKEIU-Bw7NEN3_.js} +1 -1
  17. package/workframe-ui/public/assets/channel-BaZSQvmW.js +1 -0
  18. package/workframe-ui/public/assets/{chunk-2J33WTMH-DHZg-DUi.js → chunk-2J33WTMH-91lObBre.js} +1 -1
  19. package/workframe-ui/public/assets/{chunk-3OPIFGDE-BB-OYTfp.js → chunk-3OPIFGDE-D5IopWdW.js} +1 -1
  20. package/workframe-ui/public/assets/{chunk-4BX2VUAB-C93q0YIm.js → chunk-4BX2VUAB-DRHbh4Yd.js} +1 -1
  21. package/workframe-ui/public/assets/{chunk-55IACEB6-MAYniqik.js → chunk-55IACEB6-DcfKoh86.js} +1 -1
  22. package/workframe-ui/public/assets/{chunk-5ZQYHXKU-ChgN6YJs.js → chunk-5ZQYHXKU-BMtcWXOU.js} +1 -1
  23. package/workframe-ui/public/assets/{chunk-727SXJPM-B_FYwdAv.js → chunk-727SXJPM-BW7tTfej.js} +1 -1
  24. package/workframe-ui/public/assets/{chunk-AQP2D5EJ-1_Hw_h1A.js → chunk-AQP2D5EJ-CGoSQAgm.js} +1 -1
  25. package/workframe-ui/public/assets/{chunk-BSJP7CBP-CFiDQ1Rv.js → chunk-BSJP7CBP-RW1Jng9K.js} +1 -1
  26. package/workframe-ui/public/assets/{chunk-CSCIHK7Q-DZ9UMTlB.js → chunk-CSCIHK7Q-D8PwFfuF.js} +1 -1
  27. package/workframe-ui/public/assets/{chunk-FMBD7UC4-DlMlyFgw.js → chunk-FMBD7UC4-rUp_DSZ2.js} +1 -1
  28. package/workframe-ui/public/assets/{chunk-KSCS5N6A-DHXtQ_Hf.js → chunk-KSCS5N6A-C3eMeGne.js} +1 -1
  29. package/workframe-ui/public/assets/{chunk-L5ZTLDWV-CuQzg-QG.js → chunk-L5ZTLDWV-DaE5LwXn.js} +1 -1
  30. package/workframe-ui/public/assets/{chunk-LZXEDZCA-BHzjzCGg.js → chunk-LZXEDZCA-Cmn67nL3.js} +2 -2
  31. package/workframe-ui/public/assets/{chunk-ND2GUHAM-DHXx05n2.js → chunk-ND2GUHAM-Bg-FPAN5.js} +1 -1
  32. package/workframe-ui/public/assets/{chunk-NZK2D7GU-CV5pmDM_.js → chunk-NZK2D7GU-orlL5R9g.js} +1 -1
  33. package/workframe-ui/public/assets/{chunk-O5CBEL6O-6tkCHxsV.js → chunk-O5CBEL6O-BaPyh9SK.js} +1 -1
  34. package/workframe-ui/public/assets/chunk-QZHKN3VN-DXcsnu-x.js +1 -0
  35. package/workframe-ui/public/assets/chunk-WU5MYG2G-B2O0DZxu.js +1 -0
  36. package/workframe-ui/public/assets/{chunk-XPW4576I-BClwIiCp.js → chunk-XPW4576I-CXYFE8tT.js} +1 -1
  37. package/workframe-ui/public/assets/classDiagram-4FO5ZUOK-C_TyUBYX.js +1 -0
  38. package/workframe-ui/public/assets/classDiagram-v2-Q7XG4LA2-C_TyUBYX.js +1 -0
  39. package/workframe-ui/public/assets/{cose-bilkent-S5V4N54A-DOrGV6DQ.js → cose-bilkent-S5V4N54A-DDzKMTbp.js} +1 -1
  40. package/workframe-ui/public/assets/{dagre-BM42HDAG-DXTPvJkX.js → dagre-BM42HDAG-DCa6EvS3.js} +1 -1
  41. package/workframe-ui/public/assets/{diagram-2AECGRRQ-xX_v-pbf.js → diagram-2AECGRRQ-C-WIQoE_.js} +1 -1
  42. package/workframe-ui/public/assets/{diagram-5GNKFQAL-Cd2pXbBe.js → diagram-5GNKFQAL-UDk1nRH6.js} +1 -1
  43. package/workframe-ui/public/assets/{diagram-KO2AKTUF-Df3XvUtk.js → diagram-KO2AKTUF-DjLxUTPx.js} +1 -1
  44. package/workframe-ui/public/assets/{diagram-LMA3HP47-CsijIPaD.js → diagram-LMA3HP47-DNA9GSwJ.js} +1 -1
  45. package/workframe-ui/public/assets/{diagram-OG6HWLK6-aq5fmfHd.js → diagram-OG6HWLK6-DO2nQu2f.js} +1 -1
  46. package/workframe-ui/public/assets/{dist-D1c0mkbB.js → dist-DyW8oFIW.js} +1 -1
  47. package/workframe-ui/public/assets/{erDiagram-TEJ5UH35-DnFysVRY.js → erDiagram-TEJ5UH35-DFtJLZx1.js} +1 -1
  48. package/workframe-ui/public/assets/eventmodeling-FCH6USID-BTmiKS74.js +1 -0
  49. package/workframe-ui/public/assets/{flowDiagram-I6XJVG4X-C6Ebi3su.js → flowDiagram-I6XJVG4X-B6K6EnPA.js} +1 -1
  50. package/workframe-ui/public/assets/{ganttDiagram-6RSMTGT7-BQXQtUpa.js → ganttDiagram-6RSMTGT7-CbuWdupl.js} +1 -1
  51. package/workframe-ui/public/assets/{gitGraph-WXDBUCRP-Dt0zIs_M.js → gitGraph-WXDBUCRP-BW_jwUPU.js} +1 -1
  52. package/workframe-ui/public/assets/{gitGraphDiagram-PVQCEYII-BF8gHzRn.js → gitGraphDiagram-PVQCEYII-B4MYuP9j.js} +1 -1
  53. package/workframe-ui/public/assets/index-BYacg3Nj.js +133 -0
  54. package/workframe-ui/public/assets/{info-J43DQDTF-CSmszQJT.js → info-J43DQDTF-Xa61PMDn.js} +1 -1
  55. package/workframe-ui/public/assets/{infoDiagram-5YYISTIA-CVTKGW6p.js → infoDiagram-5YYISTIA-BNbQgdUG.js} +1 -1
  56. package/workframe-ui/public/assets/{ishikawaDiagram-YF4QCWOH-Z8pT09Lv.js → ishikawaDiagram-YF4QCWOH-BDfiCzHo.js} +1 -1
  57. package/workframe-ui/public/assets/{journeyDiagram-JHISSGLW-r3wD68_T.js → journeyDiagram-JHISSGLW-Dn52B8tT.js} +1 -1
  58. package/workframe-ui/public/assets/{kanban-definition-UN3LZRKU-Il8VglqN.js → kanban-definition-UN3LZRKU-rhwkS_g6.js} +1 -1
  59. package/workframe-ui/public/assets/{line-oyjpfz2A.js → line-BQJgrj-x.js} +1 -1
  60. package/workframe-ui/public/assets/{linear-Cf7p5tVp.js → linear-Bq2ZvWSJ.js} +1 -1
  61. package/workframe-ui/public/assets/{mermaid-parser.core-YmbZ-AfY.js → mermaid-parser.core-CwFK_i8p.js} +2 -2
  62. package/workframe-ui/public/assets/{mermaid.core-BFdCAqCo.js → mermaid.core-C9CAm83c.js} +3 -3
  63. package/workframe-ui/public/assets/{mindmap-definition-RKZ34NQL-Cy2iCtEl.js → mindmap-definition-RKZ34NQL-BXwEccNZ.js} +1 -1
  64. package/workframe-ui/public/assets/{packet-YPE3B663-DwOBZL6K.js → packet-YPE3B663-D754iSKT.js} +1 -1
  65. package/workframe-ui/public/assets/{pie-LRSECV5Y-04PPhnKK.js → pie-LRSECV5Y-BQ2V4UWk.js} +1 -1
  66. package/workframe-ui/public/assets/{pieDiagram-4H26LBE5-LxIpgHqi.js → pieDiagram-4H26LBE5-CNHecl4C.js} +1 -1
  67. package/workframe-ui/public/assets/{quadrantDiagram-W4KKPZXB-0nBYfYm4.js → quadrantDiagram-W4KKPZXB-Bu6ciL2O.js} +1 -1
  68. package/workframe-ui/public/assets/{radar-GUYGQ44K-D2-vBqps.js → radar-GUYGQ44K-Dr8b9Qak.js} +1 -1
  69. package/workframe-ui/public/assets/{requirementDiagram-4Y6WPE33-DbuU0nlu.js → requirementDiagram-4Y6WPE33-Beua6Xju.js} +1 -1
  70. package/workframe-ui/public/assets/{sankeyDiagram-5OEKKPKP-B2hQ6B2x.js → sankeyDiagram-5OEKKPKP-CSDFJ0P_.js} +1 -1
  71. package/workframe-ui/public/assets/{sequenceDiagram-3UESZ5HK-BBrU30e1.js → sequenceDiagram-3UESZ5HK-KKziodNI.js} +1 -1
  72. package/workframe-ui/public/assets/{src-BJEDmV70.js → src-PKTIkQR0.js} +1 -1
  73. package/workframe-ui/public/assets/{stateDiagram-AJRCARHV-7FGO4kkH.js → stateDiagram-AJRCARHV-Bu8FNMcj.js} +1 -1
  74. package/workframe-ui/public/assets/stateDiagram-v2-BHNVJYJU-E9A5RCa9.js +1 -0
  75. package/workframe-ui/public/assets/{timeline-definition-PNZ67QCA-ptDm4rCN.js → timeline-definition-PNZ67QCA-DMa-RPqy.js} +1 -1
  76. package/workframe-ui/public/assets/{treeView-BLDUP644-CS6Z-0q8.js → treeView-BLDUP644-CzQeUY7D.js} +1 -1
  77. package/workframe-ui/public/assets/{treemap-LRROVOQU-DqV4Y2VA.js → treemap-LRROVOQU-BT4RYAhY.js} +1 -1
  78. package/workframe-ui/public/assets/{vennDiagram-CIIHVFJN-C0UrZJYt.js → vennDiagram-CIIHVFJN-nKaUH6GG.js} +1 -1
  79. package/workframe-ui/public/assets/{wardley-L42UT6IY-bNDN3_Sa.js → wardley-L42UT6IY-eSpvH7au.js} +1 -1
  80. package/workframe-ui/public/assets/{wardleyDiagram-YWT4CUSO-jWiJsefM.js → wardleyDiagram-YWT4CUSO-B5u4s4a5.js} +1 -1
  81. package/workframe-ui/public/assets/{xychartDiagram-2RQKCTM6-Dsh_fLCy.js → xychartDiagram-2RQKCTM6-DcXo5hDi.js} +1 -1
  82. package/workframe-ui/public/index.html +1 -1
  83. package/workframe-ui/public/assets/architecture-7EHR7CIX-DUyH3hWG.js +0 -1
  84. package/workframe-ui/public/assets/channel-g7r_RGaY.js +0 -1
  85. package/workframe-ui/public/assets/chunk-QZHKN3VN-C5UQehWY.js +0 -1
  86. package/workframe-ui/public/assets/chunk-WU5MYG2G-DhWllrI8.js +0 -1
  87. package/workframe-ui/public/assets/classDiagram-4FO5ZUOK-BBM_8T8E.js +0 -1
  88. package/workframe-ui/public/assets/classDiagram-v2-Q7XG4LA2-BBM_8T8E.js +0 -1
  89. package/workframe-ui/public/assets/eventmodeling-FCH6USID-Ci8mdb44.js +0 -1
  90. package/workframe-ui/public/assets/index-lRpzpNPT.js +0 -133
  91. package/workframe-ui/public/assets/stateDiagram-v2-BHNVJYJU-DLTSizMg.js +0 -1
@@ -46,6 +46,9 @@ import turn_credentials
46
46
  import llm_proxy
47
47
  import action_proxy
48
48
  import internal_proxy_auth
49
+ import concierge
50
+ import llm_error_glossary
51
+ import openrouter_catalog
49
52
  import updates as stack_updates
50
53
 
51
54
  HERMES_DATA = Path(os.environ.get("HERMES_DATA", "/opt/data"))
@@ -12302,8 +12305,9 @@ def _provider_pool_entry(provider: str, env_var: str, *, base_url: str = "") ->
12302
12305
 
12303
12306
 
12304
12307
  def _runtime_provider_pool_entry(provider: str, env_var: str) -> dict[str, Any]:
12305
- """Credential pool row for u-* profiles — keys live in .env leases, upstream via API proxy."""
12306
- return _provider_pool_entry(provider, env_var, base_url=_llm_proxy_base_url(provider))
12308
+ """Credential pool row for u-* profiles — lease token lives in OPENAI_API_KEY."""
12309
+ lease_env = "OPENAI_API_KEY"
12310
+ return _provider_pool_entry(provider, lease_env, base_url=_llm_proxy_base_url(provider))
12307
12311
 
12308
12312
 
12309
12313
  def _provider_pool_has_entries(auth: dict[str, Any], provider: str) -> bool:
@@ -13313,23 +13317,75 @@ def _enrich_room_chat_payload(payload: dict[str, Any], user_id: str) -> dict[str
13313
13317
  return body
13314
13318
 
13315
13319
 
13316
- def _emit_stream_chat_error(handler: BaseHTTPRequestHandler, message: str) -> None:
13317
- """SSE error frame — keeps UI stream parser on the happy path."""
13318
- text = str(message or "").strip() or "Chat failed."
13319
- payload = json.dumps({"error": text, "text": text, "message": text})
13320
+ def _emit_stream_chat_error(
13321
+ handler: BaseHTTPRequestHandler,
13322
+ message: str = "",
13323
+ *,
13324
+ entry: dict[str, Any] | None = None,
13325
+ ) -> None:
13326
+ """SSE error frame — structured playbook when entry supplied."""
13327
+ payload = llm_error_glossary.notice_payload(entry) if entry else {}
13328
+ if not payload:
13329
+ text = str(message or "").strip() or "Chat failed."
13330
+ payload = {"error": text, "text": text, "message": text}
13320
13331
  try:
13321
13332
  handler.send_response(200)
13322
13333
  handler.send_header("Content-Type", "text/event-stream; charset=utf-8")
13323
13334
  handler.send_header("Cache-Control", "no-cache")
13324
13335
  handler.send_header("Connection", "keep-alive")
13325
13336
  handler.end_headers()
13326
- handler.wfile.write(f"event: error\ndata: {payload}\n\n".encode("utf-8"))
13337
+ body = json.dumps(payload)
13338
+ handler.wfile.write(f"event: error\ndata: {body}\n\n".encode("utf-8"))
13327
13339
  handler.wfile.write(b"event: done\ndata: {}\n\n")
13328
13340
  handler.wfile.flush()
13329
13341
  except (BrokenPipeError, ConnectionResetError, OSError):
13330
13342
  return
13331
13343
 
13332
13344
 
13345
+ def _emit_stream_concierge(handler: BaseHTTPRequestHandler, entry: dict[str, Any]) -> None:
13346
+ """Deterministic assistant reply when LLM path is unavailable."""
13347
+ payload = llm_error_glossary.notice_payload(entry)
13348
+ text = str(payload.get("message") or "")
13349
+ hint = str(payload.get("hint") or "").strip()
13350
+ if hint:
13351
+ text = f"{text}\n\n{hint}"
13352
+ try:
13353
+ handler.send_response(200)
13354
+ handler.send_header("Content-Type", "text/event-stream; charset=utf-8")
13355
+ handler.send_header("Cache-Control", "no-cache")
13356
+ handler.send_header("Connection", "keep-alive")
13357
+ handler.end_headers()
13358
+ handler.wfile.write(
13359
+ f"event: concierge\ndata: {json.dumps(payload)}\n\n".encode("utf-8"),
13360
+ )
13361
+ handler.wfile.write(
13362
+ f"event: message.complete\ndata: {json.dumps({'content': text, 'text': text})}\n\n".encode(
13363
+ "utf-8",
13364
+ ),
13365
+ )
13366
+ handler.wfile.write(b"event: done\ndata: {}\n\n")
13367
+ handler.wfile.flush()
13368
+ except (BrokenPipeError, ConnectionResetError, OSError):
13369
+ return
13370
+
13371
+
13372
+ def _log_llm_failure(
13373
+ handler: BaseHTTPRequestHandler,
13374
+ code: str,
13375
+ *,
13376
+ provider: str = "",
13377
+ model: str = "",
13378
+ profile: str = "",
13379
+ ) -> None:
13380
+ if hasattr(handler, "_log_audit"):
13381
+ handler._log_audit( # type: ignore[attr-defined]
13382
+ "llm_failure",
13383
+ "llm",
13384
+ profile or provider or "unknown",
13385
+ f"code={code} provider={provider} model={model}",
13386
+ )
13387
+
13388
+
13333
13389
  def stream_profile_chat(handler: BaseHTTPRequestHandler, profile: str, payload: dict[str, Any]) -> None:
13334
13390
  _triggering_user = str(payload.get("user_id", "") or "")
13335
13391
  _workspace_id = str(payload.get("workspace_id", "") or "")
@@ -13345,6 +13401,15 @@ def stream_profile_chat(handler: BaseHTTPRequestHandler, profile: str, payload:
13345
13401
  if not text:
13346
13402
  raise ValueError("text required")
13347
13403
  llm_provider = _llm_billing_provider(prof)
13404
+ llm_ready = bool(
13405
+ _triggering_user
13406
+ and _user_can_use_llm(_triggering_user, _workspace_id, llm_provider)
13407
+ )
13408
+ if _triggering_user and not llm_ready:
13409
+ entry = concierge.respond(text, situation="no_provider")
13410
+ _log_llm_failure(handler, str(entry.get("code") or "no_llm_provider"), provider=llm_provider, profile=prof)
13411
+ _emit_stream_concierge(handler, entry)
13412
+ return
13348
13413
  try:
13349
13414
  lifecycle = ensure_profile_api(
13350
13415
  prof,
@@ -13358,7 +13423,15 @@ def stream_profile_chat(handler: BaseHTTPRequestHandler, profile: str, payload:
13358
13423
  )
13359
13424
  _overlay_turn_user_env(prof, _triggering_user, _workspace_id, _turn_run_id)
13360
13425
  except ValueError as exc:
13361
- _emit_stream_chat_error(handler, str(exc))
13426
+ err_text = str(exc)
13427
+ if "no_llm_provider_for_user" in err_text:
13428
+ entry = concierge.respond(text, situation="no_provider")
13429
+ _log_llm_failure(handler, "no_llm_provider", provider=llm_provider, profile=prof)
13430
+ _emit_stream_concierge(handler, entry)
13431
+ return
13432
+ entry = llm_error_glossary.classify_exception_text(err_text)
13433
+ _log_llm_failure(handler, str(entry.get("code") or ""), provider=llm_provider, profile=prof)
13434
+ _emit_stream_chat_error(handler, entry=entry)
13362
13435
  return
13363
13436
  upstream_body = json.dumps(_profile_turn_payload(prof, text, _room_id)).encode("utf-8")
13364
13437
 
@@ -13487,34 +13560,21 @@ def stream_profile_chat(handler: BaseHTTPRequestHandler, profile: str, payload:
13487
13560
  handler.wfile.flush()
13488
13561
  if not saw_complete or not complete_text:
13489
13562
  config_key = _read_config_model_api_key(prof)
13563
+ _, model_name = _read_model_from_config(prof)
13490
13564
  if config_key.startswith(turn_credentials.LEASE_PREFIX) and not turn_credentials.validate_lease(
13491
13565
  config_key,
13492
13566
  ):
13493
- err_text = (
13494
- "Turn credential expired before the model replied. "
13495
- "Send the message again."
13496
- )
13497
- err_code = "invalid_lease"
13498
- elif not saw_complete:
13499
- err_text = (
13500
- "Model provider returned no reply. "
13501
- "The request may have failed at the gateway or provider."
13502
- )
13503
- err_code = "provider_empty_reply"
13567
+ entry = llm_error_glossary.playbook_entry("invalid_lease")
13504
13568
  else:
13505
- err_text = (
13506
- "Model provider returned an empty reply. "
13507
- "Check the model name in Settings or try again."
13508
- )
13509
- err_code = "provider_empty_reply"
13510
- err_payload = json.dumps(
13511
- {
13512
- "error": err_text,
13513
- "text": err_text,
13514
- "message": err_text,
13515
- "code": err_code,
13516
- },
13569
+ entry = llm_error_glossary.playbook_entry("provider_empty_reply")
13570
+ _log_llm_failure(
13571
+ handler,
13572
+ str(entry.get("code") or "provider_empty_reply"),
13573
+ provider=llm_provider,
13574
+ model=str(model_name or ""),
13575
+ profile=prof,
13517
13576
  )
13577
+ err_payload = json.dumps(llm_error_glossary.notice_payload(entry))
13518
13578
  handler.wfile.write(f"event: error\ndata: {err_payload}\n\n".encode("utf-8"))
13519
13579
  handler.wfile.flush()
13520
13580
  handler.wfile.write(b"event: done\ndata: {}\n\n")
@@ -14252,16 +14312,16 @@ def _resolve_command(token: str) -> dict[str, Any] | None:
14252
14312
  # overrides live in the profile's config.yaml under `model.default` and
14253
14313
  # `fallback_providers`; this constant is the install-wide default the
14254
14314
  # migration writes when a profile is missing either field.
14255
- HERMES_DEFAULT_PRIMARY = "openrouter/owl-alpha"
14315
+ HERMES_DEFAULT_PRIMARY = "google/gemini-2.5-flash"
14256
14316
  HERMES_DEFAULT_FALLBACK_CHAIN: list[dict[str, str]] = [
14257
- {"provider": "openrouter", "model": "openrouter/nex-agi"},
14258
- {"provider": "openrouter", "model": "openrouter/nvidia/nemotron-3-ultra-550b-a55b:free"},
14317
+ {"provider": "openrouter", "model": "anthropic/claude-sonnet-4.5"},
14318
+ {"provider": "openrouter", "model": "meta-llama/llama-3.3-70b-instruct:free"},
14259
14319
  ]
14260
14320
 
14261
14321
  # ponytail: minimum-viable agentic models per LLM provider; user can escalate in settings.
14262
14322
  PROVIDER_MVP_MODELS: dict[str, dict[str, Any]] = {
14263
14323
  "openrouter": {
14264
- "primary": "openrouter/owl-alpha",
14324
+ "primary": "google/gemini-2.5-flash",
14265
14325
  "fallbacks": list(HERMES_DEFAULT_FALLBACK_CHAIN),
14266
14326
  },
14267
14327
  "openai": {
@@ -14273,8 +14333,8 @@ PROVIDER_MVP_MODELS: dict[str, dict[str, Any]] = {
14273
14333
  "fallbacks": [{"provider": "anthropic", "model": "claude-3-5-haiku-20241022"}],
14274
14334
  },
14275
14335
  "google": {
14276
- "primary": "gemini-2.0-flash",
14277
- "fallbacks": [{"provider": "google", "model": "gemini-2.0-flash"}],
14336
+ "primary": "gemini-2.5-flash",
14337
+ "fallbacks": [{"provider": "google", "model": "gemini-2.5-flash-lite"}],
14278
14338
  },
14279
14339
  "deepseek": {
14280
14340
  "primary": "deepseek-chat",
@@ -14285,8 +14345,8 @@ PROVIDER_MVP_MODELS: dict[str, dict[str, Any]] = {
14285
14345
  "fallbacks": [{"provider": "openai-codex", "model": "gpt-5.4-medium"}],
14286
14346
  },
14287
14347
  "nous": {
14288
- "primary": "openrouter/owl-alpha",
14289
- "fallbacks": [{"provider": "nous", "model": "openrouter/owl-alpha"}],
14348
+ "primary": "nousresearch/hermes-4-70b",
14349
+ "fallbacks": [{"provider": "nous", "model": "nousresearch/hermes-4-70b"}],
14290
14350
  },
14291
14351
  }
14292
14352
 
@@ -14394,15 +14454,21 @@ HERMES_MODEL_CATALOG: list[dict[str, str]] = [
14394
14454
  {"provider": "OpenRouter", "model": "openrouter/auto",
14395
14455
  "label": "OpenRouter Auto",
14396
14456
  "description": "OpenRouter picks the best model per prompt."},
14397
- {"provider": "OpenRouter", "model": "openrouter/owl-alpha",
14398
- "label": "Owl Alpha",
14399
- "description": "Workframe default primary strong general agent."},
14400
- {"provider": "OpenRouter", "model": "openrouter/nex-agi",
14401
- "label": "Nex AGI",
14402
- "description": "Fallback-tier reasoning model on OpenRouter."},
14403
- {"provider": "OpenRouter", "model": "openrouter/nvidia/nemotron-3-ultra-550b-a55b:free",
14404
- "label": "Nemotron 3 Ultra (free)",
14405
- "description": "OpenRouter free tier long-context agentic fallback."},
14457
+ {"provider": "OpenRouter", "model": "google/gemini-2.5-flash",
14458
+ "label": "Gemini 2.5 Flash",
14459
+ "description": "Fast, capable default for OpenRouter BYOK."},
14460
+ {"provider": "OpenRouter", "model": "anthropic/claude-sonnet-4.5",
14461
+ "label": "Claude Sonnet 4.5 (via OR)",
14462
+ "description": "Strong agentic fallback on OpenRouter."},
14463
+ {"provider": "OpenRouter", "model": "meta-llama/llama-3.3-70b-instruct:free",
14464
+ "label": "Llama 3.3 70B (free)",
14465
+ "description": "Free-tier OpenRouter fallback when available."},
14466
+ {"provider": "OpenRouter", "model": "z-ai/glm-5.1",
14467
+ "label": "GLM 5.1",
14468
+ "description": "High-availability model on OpenRouter."},
14469
+ {"provider": "OpenRouter", "model": "nousresearch/hermes-4-70b",
14470
+ "label": "Hermes 4 70B",
14471
+ "description": "Agent-tuned model on OpenRouter."},
14406
14472
  {"provider": "OpenRouter", "model": "openrouter/nvidia/llama-3.1-nemotron-70b-instruct",
14407
14473
  "label": "Nemotron 70B Instruct",
14408
14474
  "description": "NVIDIA Nemotron via OpenRouter."},
@@ -14780,6 +14846,17 @@ def hermes_models(profile: str = "", user_id: str = "", workspace_id: str = "")
14780
14846
  ] if (picker_llm or connected) else [],
14781
14847
  block,
14782
14848
  )
14849
+ if user_id and "openrouter" in {n.lower() for n in (picker_llm or connected)}:
14850
+ resolved = _resolve_credential(user_id, workspace_id, "openrouter", user_only=True)
14851
+ secret = _credential_secret(resolved or {}, user_id) if resolved else ""
14852
+ live = openrouter_catalog.live_suggestions(secret, limit=30)
14853
+ if live:
14854
+ seen = {str(r.get("model") or "") for r in suggestions}
14855
+ suggestions = [
14856
+ {**row, "label": f"Live · {row.get('label', row.get('model', ''))}"}
14857
+ for row in live
14858
+ if str(row.get("model") or "") not in seen
14859
+ ] + suggestions
14783
14860
  return {
14784
14861
  "ok": True,
14785
14862
  "profile": primary_profile,
@@ -17391,11 +17468,29 @@ class Handler(BaseHTTPRequestHandler):
17391
17468
  VALUES (?,?,?,?,?,?,?,?,?,?,?,?)""",
17392
17469
  (cred_id, None, user_id, None, provider, credential_type, cred_ref, label, 1, user_id, now, now),
17393
17470
  )
17471
+ conn.execute(
17472
+ """UPDATE credential_bindings
17473
+ SET is_active = 0, deleted_at = ?, updated_at = ?
17474
+ WHERE user_id = ? AND provider = ? AND deleted_at IS NULL AND id != ?""",
17475
+ (now, now, user_id, provider, cred_id),
17476
+ )
17394
17477
  conn.commit()
17395
17478
  conn.close()
17396
17479
  except sqlite3.Error as exc:
17397
17480
  return self._json(500, {"ok": False, "error": f"db_error: {exc}"})
17398
17481
  self._log_audit("credential_stored", "credential_binding", cred_id, f"provider={provider}")
17482
+ health: dict[str, Any] = {}
17483
+ if provider == "openrouter":
17484
+ secret_probe = _credential_secret(
17485
+ {
17486
+ "credential_ref": cred_ref,
17487
+ "scope": "user",
17488
+ "user_id": user_id,
17489
+ },
17490
+ user_id,
17491
+ )
17492
+ if secret_probe:
17493
+ health = openrouter_catalog.probe_account(secret_probe)
17399
17494
  _bootstrap_model_after_llm_connect(user_id, str(body.get("workspace_id") or ""), provider)
17400
17495
  return self._json(200, {
17401
17496
  "ok": True,
@@ -17407,6 +17502,7 @@ class Handler(BaseHTTPRequestHandler):
17407
17502
  "user_id": user_id,
17408
17503
  "created_at": now,
17409
17504
  "updated_at": now,
17505
+ "health": health,
17410
17506
  **payload,
17411
17507
  })
17412
17508
 
@@ -1 +1 @@
1
- import{n as e,t}from"./path-BWPyau1x.js";import{a as n,c as r,d as i,f as a,i as o,l as s,m as c,n as l,o as u,p as d,r as f,u as p}from"./dist-D1c0mkbB.js";function m(e){return e.innerRadius}function h(e){return e.outerRadius}function g(e){return e.startAngle}function _(e){return e.endAngle}function v(e){return e&&e.padAngle}function y(e,t,n,r,i,a,o,s){var c=n-e,l=r-t,u=o-i,d=s-a,f=d*c-u*l;if(!(f*f<1e-12))return f=(u*(t-a)-d*(e-i))/f,[e+f*c,t+f*l]}function b(e,t,n,r,i,a,o){var c=e-n,l=t-r,u=(o?a:-a)/d(c*c+l*l),f=u*l,p=-u*c,m=e+f,h=t+p,g=n+f,_=r+p,v=(m+g)/2,y=(h+_)/2,b=g-m,x=_-h,S=b*b+x*x,C=i-a,w=m*_-g*h,T=(x<0?-1:1)*d(s(0,C*C*S-w*w)),E=(w*x-b*T)/S,D=(-w*b-x*T)/S,O=(w*x+b*T)/S,k=(-w*b+x*T)/S,A=E-v,j=D-y,M=O-v,N=k-y;return A*A+j*j>M*M+N*N&&(E=O,D=k),{cx:E,cy:D,x01:-f,y01:-p,x11:E*(i/C-1),y11:D*(i/C-1)}}function x(){var s=m,x=h,S=e(0),C=null,w=g,T=_,E=v,D=null,O=t(k);function k(){var e,t,m=+s.apply(this,arguments),h=+x.apply(this,arguments),g=w.apply(this,arguments)-r,_=T.apply(this,arguments)-r,v=l(_-g),k=_>g;if(D||=e=O(),h<m&&(t=h,h=m,m=t),!(h>1e-12))D.moveTo(0,0);else if(v>c-1e-12)D.moveTo(h*u(g),h*a(g)),D.arc(0,0,h,g,_,!k),m>1e-12&&(D.moveTo(m*u(_),m*a(_)),D.arc(0,0,m,_,g,k));else{var A=g,j=_,M=g,N=_,P=v,F=v,I=E.apply(this,arguments)/2,L=I>1e-12&&(C?+C.apply(this,arguments):d(m*m+h*h)),R=p(l(h-m)/2,+S.apply(this,arguments)),z=R,B=R,V,H;if(L>1e-12){var U=o(L/m*a(I)),W=o(L/h*a(I));(P-=U*2)>1e-12?(U*=k?1:-1,M+=U,N-=U):(P=0,M=N=(g+_)/2),(F-=W*2)>1e-12?(W*=k?1:-1,A+=W,j-=W):(F=0,A=j=(g+_)/2)}var G=h*u(A),K=h*a(A),q=m*u(N),J=m*a(N);if(R>1e-12){var Y=h*u(j),X=h*a(j),Z=m*u(M),Q=m*a(M),$;if(v<i)if($=y(G,K,Z,Q,Y,X,q,J)){var ee=G-$[0],te=K-$[1],ne=Y-$[0],re=X-$[1],ie=1/a(f((ee*ne+te*re)/(d(ee*ee+te*te)*d(ne*ne+re*re)))/2),ae=d($[0]*$[0]+$[1]*$[1]);z=p(R,(m-ae)/(ie-1)),B=p(R,(h-ae)/(ie+1))}else z=B=0}F>1e-12?B>1e-12?(V=b(Z,Q,G,K,h,B,k),H=b(Y,X,q,J,h,B,k),D.moveTo(V.cx+V.x01,V.cy+V.y01),B<R?D.arc(V.cx,V.cy,B,n(V.y01,V.x01),n(H.y01,H.x01),!k):(D.arc(V.cx,V.cy,B,n(V.y01,V.x01),n(V.y11,V.x11),!k),D.arc(0,0,h,n(V.cy+V.y11,V.cx+V.x11),n(H.cy+H.y11,H.cx+H.x11),!k),D.arc(H.cx,H.cy,B,n(H.y11,H.x11),n(H.y01,H.x01),!k))):(D.moveTo(G,K),D.arc(0,0,h,A,j,!k)):D.moveTo(G,K),!(m>1e-12)||!(P>1e-12)?D.lineTo(q,J):z>1e-12?(V=b(q,J,Y,X,m,-z,k),H=b(G,K,Z,Q,m,-z,k),D.lineTo(V.cx+V.x01,V.cy+V.y01),z<R?D.arc(V.cx,V.cy,z,n(V.y01,V.x01),n(H.y01,H.x01),!k):(D.arc(V.cx,V.cy,z,n(V.y01,V.x01),n(V.y11,V.x11),!k),D.arc(0,0,m,n(V.cy+V.y11,V.cx+V.x11),n(H.cy+H.y11,H.cx+H.x11),k),D.arc(H.cx,H.cy,z,n(H.y11,H.x11),n(H.y01,H.x01),!k))):D.arc(0,0,m,N,M,k)}if(D.closePath(),e)return D=null,e+``||null}return k.centroid=function(){var e=(+s.apply(this,arguments)+ +x.apply(this,arguments))/2,t=(+w.apply(this,arguments)+ +T.apply(this,arguments))/2-i/2;return[u(t)*e,a(t)*e]},k.innerRadius=function(t){return arguments.length?(s=typeof t==`function`?t:e(+t),k):s},k.outerRadius=function(t){return arguments.length?(x=typeof t==`function`?t:e(+t),k):x},k.cornerRadius=function(t){return arguments.length?(S=typeof t==`function`?t:e(+t),k):S},k.padRadius=function(t){return arguments.length?(C=t==null?null:typeof t==`function`?t:e(+t),k):C},k.startAngle=function(t){return arguments.length?(w=typeof t==`function`?t:e(+t),k):w},k.endAngle=function(t){return arguments.length?(T=typeof t==`function`?t:e(+t),k):T},k.padAngle=function(t){return arguments.length?(E=typeof t==`function`?t:e(+t),k):E},k.context=function(e){return arguments.length?(D=e??null,k):D},k}export{x as t};
1
+ import{n as e,t}from"./path-BWPyau1x.js";import{a as n,c as r,d as i,f as a,i as o,l as s,m as c,n as l,o as u,p as d,r as f,u as p}from"./dist-DyW8oFIW.js";function m(e){return e.innerRadius}function h(e){return e.outerRadius}function g(e){return e.startAngle}function _(e){return e.endAngle}function v(e){return e&&e.padAngle}function y(e,t,n,r,i,a,o,s){var c=n-e,l=r-t,u=o-i,d=s-a,f=d*c-u*l;if(!(f*f<1e-12))return f=(u*(t-a)-d*(e-i))/f,[e+f*c,t+f*l]}function b(e,t,n,r,i,a,o){var c=e-n,l=t-r,u=(o?a:-a)/d(c*c+l*l),f=u*l,p=-u*c,m=e+f,h=t+p,g=n+f,_=r+p,v=(m+g)/2,y=(h+_)/2,b=g-m,x=_-h,S=b*b+x*x,C=i-a,w=m*_-g*h,T=(x<0?-1:1)*d(s(0,C*C*S-w*w)),E=(w*x-b*T)/S,D=(-w*b-x*T)/S,O=(w*x+b*T)/S,k=(-w*b+x*T)/S,A=E-v,j=D-y,M=O-v,N=k-y;return A*A+j*j>M*M+N*N&&(E=O,D=k),{cx:E,cy:D,x01:-f,y01:-p,x11:E*(i/C-1),y11:D*(i/C-1)}}function x(){var s=m,x=h,S=e(0),C=null,w=g,T=_,E=v,D=null,O=t(k);function k(){var e,t,m=+s.apply(this,arguments),h=+x.apply(this,arguments),g=w.apply(this,arguments)-r,_=T.apply(this,arguments)-r,v=l(_-g),k=_>g;if(D||=e=O(),h<m&&(t=h,h=m,m=t),!(h>1e-12))D.moveTo(0,0);else if(v>c-1e-12)D.moveTo(h*u(g),h*a(g)),D.arc(0,0,h,g,_,!k),m>1e-12&&(D.moveTo(m*u(_),m*a(_)),D.arc(0,0,m,_,g,k));else{var A=g,j=_,M=g,N=_,P=v,F=v,I=E.apply(this,arguments)/2,L=I>1e-12&&(C?+C.apply(this,arguments):d(m*m+h*h)),R=p(l(h-m)/2,+S.apply(this,arguments)),z=R,B=R,V,H;if(L>1e-12){var U=o(L/m*a(I)),W=o(L/h*a(I));(P-=U*2)>1e-12?(U*=k?1:-1,M+=U,N-=U):(P=0,M=N=(g+_)/2),(F-=W*2)>1e-12?(W*=k?1:-1,A+=W,j-=W):(F=0,A=j=(g+_)/2)}var G=h*u(A),K=h*a(A),q=m*u(N),J=m*a(N);if(R>1e-12){var Y=h*u(j),X=h*a(j),Z=m*u(M),Q=m*a(M),$;if(v<i)if($=y(G,K,Z,Q,Y,X,q,J)){var ee=G-$[0],te=K-$[1],ne=Y-$[0],re=X-$[1],ie=1/a(f((ee*ne+te*re)/(d(ee*ee+te*te)*d(ne*ne+re*re)))/2),ae=d($[0]*$[0]+$[1]*$[1]);z=p(R,(m-ae)/(ie-1)),B=p(R,(h-ae)/(ie+1))}else z=B=0}F>1e-12?B>1e-12?(V=b(Z,Q,G,K,h,B,k),H=b(Y,X,q,J,h,B,k),D.moveTo(V.cx+V.x01,V.cy+V.y01),B<R?D.arc(V.cx,V.cy,B,n(V.y01,V.x01),n(H.y01,H.x01),!k):(D.arc(V.cx,V.cy,B,n(V.y01,V.x01),n(V.y11,V.x11),!k),D.arc(0,0,h,n(V.cy+V.y11,V.cx+V.x11),n(H.cy+H.y11,H.cx+H.x11),!k),D.arc(H.cx,H.cy,B,n(H.y11,H.x11),n(H.y01,H.x01),!k))):(D.moveTo(G,K),D.arc(0,0,h,A,j,!k)):D.moveTo(G,K),!(m>1e-12)||!(P>1e-12)?D.lineTo(q,J):z>1e-12?(V=b(q,J,Y,X,m,-z,k),H=b(G,K,Z,Q,m,-z,k),D.lineTo(V.cx+V.x01,V.cy+V.y01),z<R?D.arc(V.cx,V.cy,z,n(V.y01,V.x01),n(H.y01,H.x01),!k):(D.arc(V.cx,V.cy,z,n(V.y01,V.x01),n(V.y11,V.x11),!k),D.arc(0,0,m,n(V.cy+V.y11,V.cx+V.x11),n(H.cy+H.y11,H.cx+H.x11),k),D.arc(H.cx,H.cy,z,n(H.y11,H.x11),n(H.y01,H.x01),!k))):D.arc(0,0,m,N,M,k)}if(D.closePath(),e)return D=null,e+``||null}return k.centroid=function(){var e=(+s.apply(this,arguments)+ +x.apply(this,arguments))/2,t=(+w.apply(this,arguments)+ +T.apply(this,arguments))/2-i/2;return[u(t)*e,a(t)*e]},k.innerRadius=function(t){return arguments.length?(s=typeof t==`function`?t:e(+t),k):s},k.outerRadius=function(t){return arguments.length?(x=typeof t==`function`?t:e(+t),k):x},k.cornerRadius=function(t){return arguments.length?(S=typeof t==`function`?t:e(+t),k):S},k.padRadius=function(t){return arguments.length?(C=t==null?null:typeof t==`function`?t:e(+t),k):C},k.startAngle=function(t){return arguments.length?(w=typeof t==`function`?t:e(+t),k):w},k.endAngle=function(t){return arguments.length?(T=typeof t==`function`?t:e(+t),k):T},k.padAngle=function(t){return arguments.length?(E=typeof t==`function`?t:e(+t),k):E},k.context=function(e){return arguments.length?(D=e??null,k):D},k}export{x as t};
@@ -0,0 +1 @@
1
+ import"./chunk-NNHCCRGN-DlpIbxXb.js";import{x as e}from"./mermaid-parser.core-CwFK_i8p.js";export{e as createArchitectureServices};