create-workframe 0.1.26 → 0.1.28

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 (120) hide show
  1. package/README.md +1 -1
  2. package/package.json +1 -1
  3. package/workframe-api/handler_modules/handler_auth.py +1 -1
  4. package/workframe-api/handler_modules/handler_chat.py +21 -2
  5. package/workframe-api/hermes_admin.py +45 -13
  6. package/workframe-api/hermes_profiles.py +72 -17
  7. package/workframe-api/profile_gateway.py +16 -1
  8. package/workframe-api/rooms.py +9 -4
  9. package/workframe-api/server.py +4 -0
  10. package/workframe-api/site_meta.py +23 -7
  11. package/workframe-api/test_agent_identity_authority.py +143 -0
  12. package/workframe-ui/public/apple-touch-icon.png +0 -0
  13. package/workframe-ui/public/assets/{arc-C_mfAdSB.js → arc-CcePMZIA.js} +1 -1
  14. package/workframe-ui/public/assets/architecture-7EHR7CIX-DOU7ZjKI.js +1 -0
  15. package/workframe-ui/public/assets/{architectureDiagram-3BPJPVTR-B_BBjtYN.js → architectureDiagram-3BPJPVTR-kn-syfTx.js} +1 -1
  16. package/workframe-ui/public/assets/{blockDiagram-GPEHLZMM-CQgRGCnf.js → blockDiagram-GPEHLZMM-DzNpIHkp.js} +1 -1
  17. package/workframe-ui/public/assets/branding/apple-touch-icon.png +0 -0
  18. package/workframe-ui/public/assets/branding/favicon.svg +9 -0
  19. package/workframe-ui/public/assets/branding/icon-192.png +0 -0
  20. package/workframe-ui/public/assets/branding/icon-32.png +0 -0
  21. package/workframe-ui/public/assets/branding/icon-512.png +0 -0
  22. package/workframe-ui/public/assets/branding/icon-96.png +0 -0
  23. package/workframe-ui/public/assets/branding/og-default.png +0 -0
  24. package/workframe-ui/public/assets/branding/og-default.svg +11 -0
  25. package/workframe-ui/public/assets/branding/workframe-color.svg +6 -0
  26. package/workframe-ui/public/assets/branding/workframe-logo-horizontal.svg +43 -0
  27. package/workframe-ui/public/assets/branding/workframe-logo-vertical.svg +43 -0
  28. package/workframe-ui/public/assets/branding/workframe-mark-1024.svg +9 -0
  29. package/workframe-ui/public/assets/branding/workframe-slogan.svg +35 -0
  30. package/workframe-ui/public/assets/branding/workframe-wordmark.svg +19 -0
  31. package/workframe-ui/public/assets/branding/workframe.svg +6 -0
  32. package/workframe-ui/public/assets/branding/workframe_icon_color.svg +6 -0
  33. package/workframe-ui/public/assets/{c4Diagram-AAUBKEIU-D-qcrcwZ.js → c4Diagram-AAUBKEIU-DVWSIwwX.js} +1 -1
  34. package/workframe-ui/public/assets/channel-DHwVoOv6.js +1 -0
  35. package/workframe-ui/public/assets/{chunk-2J33WTMH-iqAEgUi9.js → chunk-2J33WTMH-oUrzZhyP.js} +1 -1
  36. package/workframe-ui/public/assets/{chunk-3OPIFGDE-iUcUq-bF.js → chunk-3OPIFGDE-DjxnqFMO.js} +1 -1
  37. package/workframe-ui/public/assets/{chunk-4BX2VUAB-C5JgXKRg.js → chunk-4BX2VUAB-BYZHOUd5.js} +1 -1
  38. package/workframe-ui/public/assets/{chunk-55IACEB6-DzhLEunm.js → chunk-55IACEB6-BKp-79Nb.js} +1 -1
  39. package/workframe-ui/public/assets/{chunk-5ZQYHXKU-_zQHxLyI.js → chunk-5ZQYHXKU-Djlk6-gp.js} +1 -1
  40. package/workframe-ui/public/assets/{chunk-727SXJPM-DS-52in2.js → chunk-727SXJPM-DwaLHvVO.js} +1 -1
  41. package/workframe-ui/public/assets/{chunk-AQP2D5EJ-CCqiqPd-.js → chunk-AQP2D5EJ-BKzQQcHz.js} +1 -1
  42. package/workframe-ui/public/assets/{chunk-BSJP7CBP-OJ_itMAX.js → chunk-BSJP7CBP-BXXaVunA.js} +1 -1
  43. package/workframe-ui/public/assets/{chunk-CSCIHK7Q-DI6HSEKJ.js → chunk-CSCIHK7Q-DkpcxLfx.js} +1 -1
  44. package/workframe-ui/public/assets/{chunk-FMBD7UC4-DNAZXeqV.js → chunk-FMBD7UC4-msI1fUy2.js} +1 -1
  45. package/workframe-ui/public/assets/{chunk-KSCS5N6A-D_BoWW9-.js → chunk-KSCS5N6A-BOvsXH5N.js} +1 -1
  46. package/workframe-ui/public/assets/{chunk-L5ZTLDWV-XM6sSJCQ.js → chunk-L5ZTLDWV-DvPOBk7Z.js} +1 -1
  47. package/workframe-ui/public/assets/{chunk-LZXEDZCA-B1Hpet8E.js → chunk-LZXEDZCA-Doe_jJhs.js} +2 -2
  48. package/workframe-ui/public/assets/{chunk-ND2GUHAM-Bc92coLi.js → chunk-ND2GUHAM-DR7GNhFP.js} +1 -1
  49. package/workframe-ui/public/assets/{chunk-NZK2D7GU-DsPENZUC.js → chunk-NZK2D7GU-Cv82JKZc.js} +1 -1
  50. package/workframe-ui/public/assets/{chunk-O5CBEL6O-RjhFF1lf.js → chunk-O5CBEL6O-BxPPKZK8.js} +1 -1
  51. package/workframe-ui/public/assets/chunk-QZHKN3VN-Br0-9tm7.js +1 -0
  52. package/workframe-ui/public/assets/chunk-WU5MYG2G-DYQX9yrg.js +1 -0
  53. package/workframe-ui/public/assets/{chunk-XPW4576I-DYerufPu.js → chunk-XPW4576I-DzgvYRRw.js} +1 -1
  54. package/workframe-ui/public/assets/classDiagram-4FO5ZUOK-Brqc6s0d.js +1 -0
  55. package/workframe-ui/public/assets/classDiagram-v2-Q7XG4LA2-Brqc6s0d.js +1 -0
  56. package/workframe-ui/public/assets/{cose-bilkent-S5V4N54A-BkswoDoD.js → cose-bilkent-S5V4N54A-ETOvxuP2.js} +1 -1
  57. package/workframe-ui/public/assets/{dagre-BM42HDAG-C75M_CTv.js → dagre-BM42HDAG-Cx28MGYq.js} +1 -1
  58. package/workframe-ui/public/assets/{diagram-2AECGRRQ-9ylAnB1d.js → diagram-2AECGRRQ-QHhMUULb.js} +1 -1
  59. package/workframe-ui/public/assets/{diagram-5GNKFQAL-Bl3WnHCB.js → diagram-5GNKFQAL-D5gDzxY2.js} +1 -1
  60. package/workframe-ui/public/assets/{diagram-KO2AKTUF-D0Ye3svD.js → diagram-KO2AKTUF-ByDYazkb.js} +1 -1
  61. package/workframe-ui/public/assets/{diagram-LMA3HP47-CoAn2THb.js → diagram-LMA3HP47-CWg67SyJ.js} +1 -1
  62. package/workframe-ui/public/assets/{diagram-OG6HWLK6-C7EysBha.js → diagram-OG6HWLK6-ssMcKSm_.js} +1 -1
  63. package/workframe-ui/public/assets/{dist-CzIj9wwn.js → dist-CpHOvtGr.js} +1 -1
  64. package/workframe-ui/public/assets/{erDiagram-TEJ5UH35-AervmYSk.js → erDiagram-TEJ5UH35-BmL9efhY.js} +1 -1
  65. package/workframe-ui/public/assets/eventmodeling-FCH6USID-S-FXSXpt.js +1 -0
  66. package/workframe-ui/public/assets/{flowDiagram-I6XJVG4X-DlGnJe8_.js → flowDiagram-I6XJVG4X-CPWX2r-j.js} +1 -1
  67. package/workframe-ui/public/assets/{ganttDiagram-6RSMTGT7-Dvh5SnNo.js → ganttDiagram-6RSMTGT7-dbMUKarp.js} +1 -1
  68. package/workframe-ui/public/assets/{gitGraph-WXDBUCRP-fi-tPIds.js → gitGraph-WXDBUCRP-Cw3Soqev.js} +1 -1
  69. package/workframe-ui/public/assets/{gitGraphDiagram-PVQCEYII-_72qSuU_.js → gitGraphDiagram-PVQCEYII-wpl8ML96.js} +1 -1
  70. package/workframe-ui/public/assets/index-BPbXwgtE.js +133 -0
  71. package/workframe-ui/public/assets/index-D7_Z82LT.css +1 -0
  72. package/workframe-ui/public/assets/{info-J43DQDTF-OEAgdAvP.js → info-J43DQDTF-Dpy8GEXy.js} +1 -1
  73. package/workframe-ui/public/assets/{infoDiagram-5YYISTIA-rCXHubHd.js → infoDiagram-5YYISTIA-CfAgUZ9f.js} +1 -1
  74. package/workframe-ui/public/assets/{ishikawaDiagram-YF4QCWOH-1MUwVnW9.js → ishikawaDiagram-YF4QCWOH-BZ6_CJTU.js} +1 -1
  75. package/workframe-ui/public/assets/{journeyDiagram-JHISSGLW-C34qfpF3.js → journeyDiagram-JHISSGLW-DsdF__AF.js} +1 -1
  76. package/workframe-ui/public/assets/{kanban-definition-UN3LZRKU-B3Hns9lE.js → kanban-definition-UN3LZRKU-Bo7wwOYe.js} +1 -1
  77. package/workframe-ui/public/assets/{line-BKcrvW_y.js → line-BifoaLZZ.js} +1 -1
  78. package/workframe-ui/public/assets/{linear-BL3C-fBh.js → linear-DFFjuuFL.js} +1 -1
  79. package/workframe-ui/public/assets/{mermaid-parser.core-CV_JAhfz.js → mermaid-parser.core-BDDyJ-KR.js} +2 -2
  80. package/workframe-ui/public/assets/{mermaid.core-CPPFZ0AZ.js → mermaid.core-BhBk99ho.js} +3 -3
  81. package/workframe-ui/public/assets/{mindmap-definition-RKZ34NQL-9erqZdGi.js → mindmap-definition-RKZ34NQL-k4-yj5dU.js} +1 -1
  82. package/workframe-ui/public/assets/{packet-YPE3B663-CZlmsvNS.js → packet-YPE3B663-Bfrh39KO.js} +1 -1
  83. package/workframe-ui/public/assets/{pie-LRSECV5Y-lqaCPGhY.js → pie-LRSECV5Y-CxCgAHor.js} +1 -1
  84. package/workframe-ui/public/assets/{pieDiagram-4H26LBE5-C1zz8JvX.js → pieDiagram-4H26LBE5-C876ADMr.js} +1 -1
  85. package/workframe-ui/public/assets/{quadrantDiagram-W4KKPZXB-Dmp-2UMf.js → quadrantDiagram-W4KKPZXB-fqq0fkmJ.js} +1 -1
  86. package/workframe-ui/public/assets/{radar-GUYGQ44K-DjureLKl.js → radar-GUYGQ44K-BJnY1Jnx.js} +1 -1
  87. package/workframe-ui/public/assets/{requirementDiagram-4Y6WPE33-DXQI-c25.js → requirementDiagram-4Y6WPE33-DTBusY6q.js} +1 -1
  88. package/workframe-ui/public/assets/{sankeyDiagram-5OEKKPKP-BaZ9qvKi.js → sankeyDiagram-5OEKKPKP-D8kiVsJV.js} +1 -1
  89. package/workframe-ui/public/assets/{sequenceDiagram-3UESZ5HK-iAl57VoB.js → sequenceDiagram-3UESZ5HK-DfzDwdKl.js} +1 -1
  90. package/workframe-ui/public/assets/{src-DaHiggVY.js → src-Cy1USNLY.js} +1 -1
  91. package/workframe-ui/public/assets/{stateDiagram-AJRCARHV-kWK_vPK0.js → stateDiagram-AJRCARHV-sVqLyvFR.js} +1 -1
  92. package/workframe-ui/public/assets/stateDiagram-v2-BHNVJYJU-COrv6OF1.js +1 -0
  93. package/workframe-ui/public/assets/{timeline-definition-PNZ67QCA-BK7AMTE2.js → timeline-definition-PNZ67QCA-0WDiG_ak.js} +1 -1
  94. package/workframe-ui/public/assets/{treeView-BLDUP644-XvrJnjfl.js → treeView-BLDUP644-BrHz58-9.js} +1 -1
  95. package/workframe-ui/public/assets/{treemap-LRROVOQU-DwiXEpvw.js → treemap-LRROVOQU-kAd70w_y.js} +1 -1
  96. package/workframe-ui/public/assets/{vennDiagram-CIIHVFJN-BwHfuFiD.js → vennDiagram-CIIHVFJN-DnXBmEMU.js} +1 -1
  97. package/workframe-ui/public/assets/{wardley-L42UT6IY-CjVb_35b.js → wardley-L42UT6IY-hmH7OEGp.js} +1 -1
  98. package/workframe-ui/public/assets/{wardleyDiagram-YWT4CUSO-pTplM3xe.js → wardleyDiagram-YWT4CUSO-DYeDIRJn.js} +1 -1
  99. package/workframe-ui/public/assets/{xychartDiagram-2RQKCTM6-CXLIJGkG.js → xychartDiagram-2RQKCTM6-BRJldh4u.js} +1 -1
  100. package/workframe-ui/public/favicon.ico +0 -0
  101. package/workframe-ui/public/favicon.svg +9 -7
  102. package/workframe-ui/public/icon-192.png +0 -0
  103. package/workframe-ui/public/icon-32.png +0 -0
  104. package/workframe-ui/public/icon-512.png +0 -0
  105. package/workframe-ui/public/icon-96.png +0 -0
  106. package/workframe-ui/public/index.html +13 -9
  107. package/workframe-ui/public/manifest.webmanifest +27 -3
  108. package/workframe-ui/public/og-default.png +0 -0
  109. package/workframe-ui/public/workframe-build.json +4 -4
  110. package/workframe-ui/public/workframe-mark-1024.svg +9 -0
  111. package/workframe-ui/public/assets/architecture-7EHR7CIX-DLru-uUG.js +0 -1
  112. package/workframe-ui/public/assets/channel-BtxFT9GI.js +0 -1
  113. package/workframe-ui/public/assets/chunk-QZHKN3VN-CewU2mqh.js +0 -1
  114. package/workframe-ui/public/assets/chunk-WU5MYG2G-BCUMJc-u.js +0 -1
  115. package/workframe-ui/public/assets/classDiagram-4FO5ZUOK-B-h6w58g.js +0 -1
  116. package/workframe-ui/public/assets/classDiagram-v2-Q7XG4LA2-B-h6w58g.js +0 -1
  117. package/workframe-ui/public/assets/eventmodeling-FCH6USID-yOFv10Xp.js +0 -1
  118. package/workframe-ui/public/assets/index-BY14qQF5.css +0 -1
  119. package/workframe-ui/public/assets/index-DxrMmuBD.js +0 -133
  120. package/workframe-ui/public/assets/stateDiagram-v2-BHNVJYJU-9gufjyOk.js +0 -1
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  Published on npm as **create-workframe**.
4
4
 
5
5
  ```bash
6
- npx create-workframe@0.1.26 MyProject
6
+ npx create-workframe@0.1.28 MyProject
7
7
  ```
8
8
 
9
9
  Scaffolds an isolated Workframe + Hermes project on Windows, macOS, and Linux.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-workframe",
3
- "version": "0.1.26",
3
+ "version": "0.1.28",
4
4
  "description": "Scaffold a Workframe + Hermes workspace with guided onboarding",
5
5
  "type": "module",
6
6
  "bin": {
@@ -535,7 +535,7 @@ class AuthRoutesMixin:
535
535
  tagline=tagline,
536
536
  )
537
537
  if soul:
538
- result = srv.profile_soul_set(native_slug, soul)
538
+ result = srv.profile_soul_set(native_slug, soul, layer="admin")
539
539
  if not result.get("ok"):
540
540
  self._json(400, result)
541
541
  return
@@ -337,7 +337,25 @@ class ChatRoutesMixin:
337
337
  self._json(404, {"error": "not found"})
338
338
  return
339
339
  profile = resolve_validated_profile(profile_soul_match.group(1))
340
- self._json(200, profile_soul_get(profile))
340
+ ws_id = ""
341
+ user_id = str(getattr(self, "auth_user", "") or "")
342
+ if user_id:
343
+ try:
344
+ conn = srv._workframe_db()
345
+ row = conn.execute(
346
+ """
347
+ SELECT workspace_id FROM workspace_memberships
348
+ WHERE user_id = ? AND status = 'active' AND deleted_at IS NULL
349
+ ORDER BY created_at ASC LIMIT 1
350
+ """,
351
+ (user_id,),
352
+ ).fetchone()
353
+ conn.close()
354
+ if row:
355
+ ws_id = str(row["workspace_id"])
356
+ except sqlite3.Error:
357
+ pass
358
+ self._json(200, profile_soul_get(profile, ws_id))
341
359
 
342
360
 
343
361
  def _route_pattern_get_hermes_profile_detail(self, path: str, qs: dict[str, list[str]]) -> None:
@@ -580,7 +598,8 @@ class ChatRoutesMixin:
580
598
  return
581
599
  profile = resolve_validated_profile(profile_soul_post.group(1))
582
600
  soul = str(body.get("soul", body.get("text", "")))
583
- self._json(200, profile_soul_set(profile, soul))
601
+ layer = str(body.get("layer") or "").strip()
602
+ self._json(200, profile_soul_set(profile, soul, layer=layer))
584
603
 
585
604
 
586
605
  def _route_pattern_post_hermes_profile_bind(self, path: str, body: dict) -> None:
@@ -464,7 +464,8 @@ def hermes_profile_detail(profile: str, workspace_id: str = "") -> dict[str, Any
464
464
  prof = _srv().resolve_validated_profile(profile)
465
465
  reg = _srv()._agent_registry_row(prof)
466
466
  db_row = _agent_db_row(prof, workspace_id)
467
- soul_text = _srv()._profile_soul_text(prof)
467
+ soul_layers = profile_soul_get(prof, workspace_id)
468
+ soul_text = str(soul_layers.get("composed") or soul_layers.get("soul") or "")
468
469
  block = _srv()._read_model_block(prof)
469
470
  try:
470
471
  state = _srv().gateway_data(prof)
@@ -486,6 +487,11 @@ def hermes_profile_detail(profile: str, workspace_id: str = "") -> dict[str, Any
486
487
  "description": str(reg.get("description") or ""),
487
488
  "soul": soul_text,
488
489
  "soul_exists": bool(soul_text),
490
+ "soul_system": str(soul_layers.get("system") or ""),
491
+ "soul_admin": str(soul_layers.get("admin") or ""),
492
+ "soul_workspace_spice": str(soul_layers.get("workspace_spice") or ""),
493
+ "soul_user": str(soul_layers.get("user") or ""),
494
+ "soul_composed": soul_text,
489
495
  "gateway_running": gateway_running,
490
496
  "gateway_state": gateway_state,
491
497
  "model": block.get("default", ""),
@@ -520,30 +526,56 @@ def hermes_profile_update(profile: str, body: dict[str, Any]) -> dict[str, Any]:
520
526
  return {"ok": True, "profile": prof, **patch}
521
527
 
522
528
 
523
- def profile_soul_get(profile: str) -> dict[str, Any]:
529
+ def profile_soul_get(profile: str, workspace_id: str = "") -> dict[str, Any]:
524
530
  prof = _srv().resolve_validated_profile(profile)
525
- text = _srv()._profile_soul_text(prof)
531
+ template = (
532
+ _srv()._runtime_template_slug(prof) if _srv()._is_runtime_profile_slug(prof) else prof
533
+ )
534
+ system = _srv()._profile_base_soul_text(prof)
535
+ admin = ""
536
+ if _srv()._is_native_profile(template):
537
+ admin = str(_srv()._agent_registry_row(template).get("admin_soul") or "").strip()
538
+ workspace_spice = _srv()._workspace_agent_spice(workspace_id)
539
+ user = _srv()._profile_user_soul(prof)
540
+ composed = _srv()._profile_soul_text(prof, workspace_id)
526
541
  path = _srv()._profile_soul_path(prof)
527
542
  return {
528
543
  "ok": True,
529
544
  "profile": prof,
530
- "soul": text,
545
+ "soul": composed,
546
+ "system": system,
547
+ "admin": admin,
548
+ "workspace_spice": workspace_spice,
549
+ "user": user,
550
+ "composed": composed,
531
551
  "path": str(path),
532
552
  "exists": path.is_file(),
533
553
  }
534
554
 
535
555
 
536
- def profile_soul_set(profile: str, soul: str) -> dict[str, Any]:
556
+ def profile_soul_set(profile: str, soul: str, layer: str = "") -> dict[str, Any]:
537
557
  prof = _srv().resolve_validated_profile(profile)
538
- if _srv()._is_native_profile(prof):
539
- path = _srv()._profile_soul_path(prof)
540
- path.parent.mkdir(parents=True, exist_ok=True)
541
- normalized = soul if not soul or soul.endswith("\n") else f"{soul}\n"
542
- path.write_text(normalized, encoding="utf-8")
543
- return {"ok": True, "profile": prof, "bytes": len(normalized.encode("utf-8")), "target": "soul_file"}
544
558
  lookup = _srv()._runtime_template_slug(prof) if _srv()._is_runtime_profile_slug(prof) else prof
545
- _srv()._apply_profile_identity(lookup, user_soul=soul)
546
- return {"ok": True, "profile": prof, "bytes": len(soul.encode("utf-8")), "target": "user_soul_overlay"}
559
+ layer_name = str(layer or "").strip().lower()
560
+ if not layer_name:
561
+ layer_name = "admin" if _srv()._is_native_profile(lookup) else "user"
562
+ if layer_name == "admin":
563
+ if not _srv()._is_native_profile(lookup):
564
+ return {"ok": False, "error": "admin_layer_native_only", "profile": prof}
565
+ _srv()._apply_profile_identity(lookup, admin_soul=soul)
566
+ target = "admin_soul"
567
+ elif layer_name == "user":
568
+ _srv()._apply_profile_identity(lookup, user_soul=soul)
569
+ target = "user_soul"
570
+ else:
571
+ return {"ok": False, "error": "invalid_layer", "profile": prof, "allowed_layers": ["admin", "user"]}
572
+ return {
573
+ "ok": True,
574
+ "profile": prof,
575
+ "bytes": len(str(soul or "").encode("utf-8")),
576
+ "target": target,
577
+ "layer": layer_name,
578
+ }
547
579
 
548
580
 
549
581
  def hermes_debug() -> dict[str, Any]:
@@ -434,6 +434,7 @@ def _seed_native_user_overlay(
434
434
  role: str = "",
435
435
  tagline: str = "",
436
436
  user_soul: str = "",
437
+ admin_soul: str = "",
437
438
  ) -> None:
438
439
  """Keep template SOUL/AGENTS on disk; layer user identity in registry overlay."""
439
440
  runtime = _srv().safe_profile_slug(runtime)
@@ -441,14 +442,18 @@ def _seed_native_user_overlay(
441
442
  if not runtime:
442
443
  return
443
444
  _write_profile_soul_if_stub(runtime, template)
444
- if display_name.strip() or role.strip() or tagline.strip() or user_soul.strip():
445
- _apply_profile_identity(
446
- runtime,
447
- display_name=display_name,
448
- role=role,
449
- tagline=tagline,
450
- user_soul=user_soul,
451
- )
445
+ overlay_soul = admin_soul.strip() or user_soul.strip()
446
+ if display_name.strip() or role.strip() or tagline.strip() or overlay_soul:
447
+ identity_kwargs: dict[str, str] = {
448
+ "display_name": display_name,
449
+ "role": role,
450
+ "tagline": tagline,
451
+ }
452
+ if _is_native_profile(template):
453
+ identity_kwargs["admin_soul"] = admin_soul.strip() or user_soul.strip()
454
+ elif user_soul.strip():
455
+ identity_kwargs["user_soul"] = user_soul.strip()
456
+ _apply_profile_identity(runtime, **identity_kwargs)
452
457
 
453
458
 
454
459
  def _apply_profile_identity(
@@ -458,6 +463,7 @@ def _apply_profile_identity(
458
463
  role: str = "",
459
464
  tagline: str = "",
460
465
  user_soul: str = "",
466
+ admin_soul: str = "",
461
467
  ) -> None:
462
468
  prof = _srv().safe_profile_slug(profile)
463
469
  patch: dict[str, Any] = {}
@@ -467,6 +473,8 @@ def _apply_profile_identity(
467
473
  patch["role"] = role.strip()
468
474
  if tagline.strip():
469
475
  patch["tagline"] = tagline.strip()
476
+ if admin_soul.strip():
477
+ patch["admin_soul"] = admin_soul.strip()
470
478
  if user_soul.strip():
471
479
  patch["user_soul"] = user_soul.strip()
472
480
  if patch.get("display_name"):
@@ -477,18 +485,65 @@ def _apply_profile_identity(
477
485
  _srv()._upsert_agent_registry_row(prof, patch)
478
486
 
479
487
 
480
- def _profile_identity_overlay(profile: str) -> str:
488
+ def _template_slug_for_profile(profile: str) -> str:
489
+ prof = _srv().safe_profile_slug(str(profile or "").strip())
490
+ if _srv()._is_runtime_profile_slug(prof):
491
+ return _runtime_template_slug(prof)
492
+ return prof
493
+
494
+
495
+ def _workspace_agent_spice(workspace_id: str) -> str:
496
+ workspace_id = str(workspace_id or "").strip()
497
+ if not workspace_id:
498
+ return ""
499
+ try:
500
+ conn = _srv()._workframe_db()
501
+ row = conn.execute(
502
+ "SELECT settings_json FROM workspaces WHERE id = ? AND deleted_at IS NULL",
503
+ (workspace_id,),
504
+ ).fetchone()
505
+ conn.close()
506
+ if row:
507
+ settings = _srv()._parse_workspace_settings(row)
508
+ return str(settings.get("agent_spice") or "").strip()
509
+ except (sqlite3.Error, OSError):
510
+ pass
511
+ return ""
512
+
513
+
514
+ def _profile_admin_soul(profile: str) -> str:
515
+ template = _template_slug_for_profile(profile)
516
+ if not _is_native_profile(template):
517
+ return ""
518
+ return str(_srv()._agent_registry_row(template).get("admin_soul") or "").strip()
519
+
520
+
521
+ def _profile_user_soul(profile: str) -> str:
522
+ prof = _srv().safe_profile_slug(profile)
523
+ template = _template_slug_for_profile(prof)
524
+ if _is_native_profile(template):
525
+ return ""
526
+ if _srv()._is_runtime_profile_slug(prof):
527
+ runtime_reg = _srv()._agent_registry_row(prof)
528
+ reg = _srv()._agent_registry_row(template)
529
+ return str(runtime_reg.get("user_soul") or reg.get("user_soul") or "").strip()
530
+ return str(_srv()._agent_registry_row(prof).get("user_soul") or "").strip()
531
+
532
+
533
+ def _profile_identity_overlay(profile: str, workspace_id: str = "") -> str:
481
534
  prof = _srv().safe_profile_slug(profile)
482
535
  if _srv()._is_runtime_profile_slug(prof):
483
536
  template = _runtime_template_slug(prof)
484
- # Keep runtime-local instructions separate from shared display
485
- # identity. Name/role/tagline/avatar must come from the template.
486
537
  reg = _srv()._agent_registry_row(template)
487
- runtime_reg = _srv()._agent_registry_row(prof)
488
538
  else:
489
539
  reg = _srv()._agent_registry_row(prof)
490
- runtime_reg = {}
491
540
  blocks: list[str] = []
541
+ admin_soul = _profile_admin_soul(prof)
542
+ if admin_soul:
543
+ blocks.append("## Manager identity\n" + admin_soul)
544
+ spice = _srv()._workspace_agent_spice(workspace_id)
545
+ if spice:
546
+ blocks.append("## Workspace preferences\n" + spice)
492
547
  identity_lines: list[str] = []
493
548
  display = str(reg.get("display_name") or "").strip()
494
549
  role = str(reg.get("role") or reg.get("description") or "").strip()
@@ -501,9 +556,9 @@ def _profile_identity_overlay(profile: str) -> str:
501
556
  identity_lines.append(f"- **Tagline:** {tagline}")
502
557
  if identity_lines:
503
558
  blocks.append("## User identity\n" + "\n".join(identity_lines))
504
- user_soul = str(runtime_reg.get("user_soul") or reg.get("user_soul") or "").strip()
559
+ user_soul = _profile_user_soul(prof)
505
560
  if user_soul:
506
- blocks.append("## Additional instructions\n" + user_soul)
561
+ blocks.append("## User preferences\n" + user_soul)
507
562
  return "\n\n".join(blocks).strip()
508
563
 
509
564
 
@@ -554,9 +609,9 @@ def _profile_base_soul_text(profile: str) -> str:
554
609
  return _render_soul_placeholders(raw).strip()
555
610
 
556
611
 
557
- def _profile_soul_text(profile: str) -> str:
612
+ def _profile_soul_text(profile: str, workspace_id: str = "") -> str:
558
613
  base = _profile_base_soul_text(profile)
559
- overlay = _profile_identity_overlay(profile)
614
+ overlay = _profile_identity_overlay(profile, workspace_id)
560
615
  if base and overlay:
561
616
  return f"{base.rstrip()}\n\n---\n\n{overlay}"
562
617
  return overlay or base
@@ -292,7 +292,22 @@ def _profile_turn_payload(profile: str, text: str, room_id: str = "") -> dict[st
292
292
  except Exception: # noqa: BLE001
293
293
  pass
294
294
  payload: dict[str, Any] = {"message": message}
295
- soul = _srv()._profile_soul_text(profile)
295
+ workspace_id = ""
296
+ if room_id:
297
+ try:
298
+ conn = _srv()._workframe_db()
299
+ try:
300
+ room = conn.execute(
301
+ "SELECT workspace_id FROM rooms WHERE id = ? AND deleted_at IS NULL",
302
+ (room_id,),
303
+ ).fetchone()
304
+ if room and room["workspace_id"]:
305
+ workspace_id = str(room["workspace_id"])
306
+ finally:
307
+ conn.close()
308
+ except Exception: # noqa: BLE001
309
+ pass
310
+ soul = _srv()._profile_soul_text(profile, workspace_id)
296
311
  if soul:
297
312
  payload["instructions"] = soul
298
313
  return payload
@@ -136,6 +136,7 @@ def _workspace_payload(row: sqlite3.Row, *, viewer_role: str = "") -> dict[str,
136
136
  payload["integrations"] = integrations
137
137
  payload["credential_mode"] = str(settings.get("credential_mode") or "byok").strip() or "byok"
138
138
  payload["tagline"] = str(settings.get("tagline") or "")
139
+ payload["agent_spice"] = str(settings.get("agent_spice") or "")
139
140
  if viewer_role in OWNER_ADMIN_ROLES:
140
141
  payload["admin_onboarding_done"] = bool(settings.get("admin_onboarding_done"))
141
142
  return payload
@@ -910,10 +911,11 @@ def _patch_workspace(workspace_id: str, body: dict[str, Any], user_id: str) -> t
910
911
  return 400, {"ok": False, "error": "workspace_id required"}
911
912
  if not user_id:
912
913
  return 401, {"ok": False, "error": "no_session"}
913
- allowed = {"display_name", "description", "avatar_url", "tagline"}
914
+ allowed = {"display_name", "description", "avatar_url", "tagline", "agent_spice"}
914
915
  updates = {key: str(body[key]).strip() for key in ("display_name", "description", "avatar_url") if key in body}
915
916
  tagline = str(body.get("tagline") or "").strip() if "tagline" in body else None
916
- if not updates and tagline is None:
917
+ agent_spice = str(body.get("agent_spice") or "").strip() if "agent_spice" in body else None
918
+ if not updates and tagline is None and agent_spice is None:
917
919
  return 400, {"ok": False, "error": "no_allowed_fields", "allowed": list(allowed)}
918
920
  if "avatar_url" in updates:
919
921
  _srv()._validate_me_profile_updates({"avatar_url": updates["avatar_url"]})
@@ -942,9 +944,12 @@ def _patch_workspace(workspace_id: str, body: dict[str, Any], user_id: str) -> t
942
944
  sets = [f"{key} = ?" for key in updates]
943
945
  vals = list(updates.values())
944
946
  now_ts = str(int(time.time()))
945
- if tagline is not None:
947
+ if tagline is not None or agent_spice is not None:
946
948
  settings = _srv()._parse_workspace_settings(row)
947
- settings["tagline"] = tagline
949
+ if tagline is not None:
950
+ settings["tagline"] = tagline
951
+ if agent_spice is not None:
952
+ settings["agent_spice"] = agent_spice
948
953
  sets.append("settings_json = ?")
949
954
  vals.append(json.dumps(settings, sort_keys=True))
950
955
  sets.append("updated_at = ?")
@@ -361,6 +361,10 @@ _soul_is_native_concierge = hermes_profiles._soul_is_native_concierge
361
361
  _strip_forbidden_child_skills = hermes_profiles._strip_forbidden_child_skills
362
362
  _seed_native_user_overlay = hermes_profiles._seed_native_user_overlay
363
363
  _apply_profile_identity = hermes_profiles._apply_profile_identity
364
+ _template_slug_for_profile = hermes_profiles._template_slug_for_profile
365
+ _workspace_agent_spice = hermes_profiles._workspace_agent_spice
366
+ _profile_admin_soul = hermes_profiles._profile_admin_soul
367
+ _profile_user_soul = hermes_profiles._profile_user_soul
364
368
  _profile_identity_overlay = hermes_profiles._profile_identity_overlay
365
369
  _install_child_base_artifacts = hermes_profiles._install_child_base_artifacts
366
370
  _profile_base_soul_text = hermes_profiles._profile_base_soul_text
@@ -11,10 +11,22 @@ import stack_config
11
11
 
12
12
  DEFAULT_TITLE = "Workframe"
13
13
  DEFAULT_SHORT_NAME = "Workframe"
14
- DEFAULT_DESCRIPTION = "Project chat and workspace for Hermes agent crews"
15
- DEFAULT_THEME_COLOR = "#0A0A0F"
14
+ DEFAULT_DESCRIPTION = (
15
+ "The Social OS for Autonomous Businesses — humans and persistent AI coworkers "
16
+ "share chat, boards, and files."
17
+ )
18
+ DEFAULT_THEME_COLOR = "#79689D"
16
19
  DEFAULT_OG_IMAGE = "/assets/branding/og-default.png"
17
20
  DEFAULT_FAVICON = "/favicon.svg"
21
+ DEFAULT_APPLE_TOUCH_ICON = "/apple-touch-icon.png"
22
+
23
+ DEFAULT_PWA_ICONS = [
24
+ {"src": "./favicon.svg", "sizes": "any", "type": "image/svg+xml", "purpose": "any"},
25
+ {"src": "./icon-32.png", "sizes": "32x32", "type": "image/png", "purpose": "any"},
26
+ {"src": "./icon-96.png", "sizes": "96x96", "type": "image/png", "purpose": "any"},
27
+ {"src": "./icon-192.png", "sizes": "192x192", "type": "image/png", "purpose": "any maskable"},
28
+ {"src": "./icon-512.png", "sizes": "512x512", "type": "image/png", "purpose": "any maskable"},
29
+ ]
18
30
 
19
31
  BRANDING_DIR_NAME = "site-branding"
20
32
  OG_FILENAME = "og-image"
@@ -169,8 +181,10 @@ def resolve_site_meta(
169
181
  fav_path = _branding_asset_path("favicon")
170
182
  if fav_path.is_file():
171
183
  favicon = _browser_asset_url(app_base_url, f"/api/public/branding/favicon{fav_path.suffix}")
184
+ apple_touch = favicon
172
185
  else:
173
186
  favicon = _browser_asset_url(app_base_url, DEFAULT_FAVICON)
187
+ apple_touch = _browser_asset_url(app_base_url, DEFAULT_APPLE_TOUCH_ICON)
174
188
 
175
189
  short_name = title if len(title) <= 16 else title[:15].rstrip() + "…"
176
190
  canonical = app_base_url.rstrip("/") + "/" if app_base_url else "/"
@@ -185,6 +199,7 @@ def resolve_site_meta(
185
199
  "theme_color": theme_color,
186
200
  "og_image": og_image,
187
201
  "favicon": favicon,
202
+ "apple_touch_icon": apple_touch,
188
203
  "canonical_url": canonical,
189
204
  "manifest_url": _browser_asset_url(app_base_url, "/manifest.webmanifest"),
190
205
  "source": {
@@ -197,10 +212,10 @@ def resolve_site_meta(
197
212
 
198
213
 
199
214
  def manifest_payload(meta: dict[str, Any]) -> dict[str, Any]:
200
- icons = []
201
215
  favicon = str(meta.get("favicon") or "").strip()
202
- if favicon:
203
- icons.append(
216
+ custom_icons = []
217
+ if favicon and favicon != _browser_asset_url("", DEFAULT_FAVICON):
218
+ custom_icons.append(
204
219
  {
205
220
  "src": favicon,
206
221
  "sizes": "any",
@@ -208,16 +223,17 @@ def manifest_payload(meta: dict[str, Any]) -> dict[str, Any]:
208
223
  "purpose": "any",
209
224
  },
210
225
  )
226
+ icons = custom_icons or DEFAULT_PWA_ICONS
211
227
  return {
212
228
  "name": meta.get("title") or DEFAULT_TITLE,
213
229
  "short_name": meta.get("short_name") or DEFAULT_SHORT_NAME,
214
230
  "description": meta.get("description") or DEFAULT_DESCRIPTION,
215
231
  "start_url": "./",
216
232
  "display": "standalone",
217
- "background_color": meta.get("theme_color") or DEFAULT_THEME_COLOR,
233
+ "background_color": "#ffffff",
218
234
  "theme_color": meta.get("theme_color") or DEFAULT_THEME_COLOR,
219
235
  "orientation": "any",
220
- "icons": icons or [{"src": "./favicon.svg", "sizes": "any", "type": "image/svg+xml", "purpose": "any"}],
236
+ "icons": icons,
221
237
  }
222
238
 
223
239
 
@@ -2,6 +2,7 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import json
5
6
  import os
6
7
  import sys
7
8
  import tempfile
@@ -17,6 +18,7 @@ os.environ.setdefault("HERMES_DATA", str(API_DIR / ".tmp-test-hermes"))
17
18
  os.environ.setdefault("DEV_LOCAL_UNSAFE", "true")
18
19
 
19
20
  import crew_registry # noqa: E402
21
+ import hermes_admin # noqa: E402
20
22
  import hermes_profiles # noqa: E402
21
23
  import runtime_cohort # noqa: E402
22
24
  import server # noqa: E402
@@ -88,9 +90,150 @@ def test_runtime_identity_sync_preserves_credentials() -> None:
88
90
  assert (runtime_dir / ".env").read_text(encoding="utf-8") == "OPENROUTER_API_KEY=user-secret\n"
89
91
 
90
92
 
93
+ def _native_profile_fixture() -> tuple[Path, Path, Path, str]:
94
+ root = Path(tempfile.mkdtemp())
95
+ native = "workframe-agent"
96
+ prof_dir = root / native
97
+ prof_dir.mkdir(parents=True)
98
+ soul_path = prof_dir / "SOUL.md"
99
+ soul_path.write_text(
100
+ "# Workframe concierge\n\nYou are the Workframe botfather orchestrator.\n",
101
+ encoding="utf-8",
102
+ )
103
+ agents_json = root / "workframe" / "agents.json"
104
+ agents_json.parent.mkdir(parents=True, exist_ok=True)
105
+ agents_json.write_text(
106
+ json.dumps({"version": 1, "agents": {native: {"profile": native}}}),
107
+ encoding="utf-8",
108
+ )
109
+ return root, soul_path, agents_json, native
110
+
111
+
112
+ def test_native_admin_soul_does_not_modify_soul_md() -> None:
113
+ root, soul_path, agents_json, native = _native_profile_fixture()
114
+ original = soul_path.read_text(encoding="utf-8")
115
+ try:
116
+ with (
117
+ patch.object(server, "NATIVE_PROFILE", native),
118
+ patch.object(server, "HERMES_DATA", root),
119
+ patch.object(server, "AGENTS_JSON", agents_json),
120
+ patch.object(server, "_profile_dir", side_effect=lambda slug: root / slug),
121
+ patch.object(server, "resolve_validated_profile", side_effect=lambda p: p),
122
+ patch.object(server, "_is_native_profile", side_effect=lambda p: p == native),
123
+ patch.object(server, "_primary_profile", return_value=native),
124
+ ):
125
+ result = hermes_admin.profile_soul_set(native, "Owner manager tone.", layer="admin")
126
+ assert result.get("ok")
127
+ assert result.get("target") == "admin_soul"
128
+ assert soul_path.read_text(encoding="utf-8") == original
129
+ finally:
130
+ import shutil
131
+
132
+ shutil.rmtree(root, ignore_errors=True)
133
+
134
+
135
+ def test_composed_soul_includes_admin_and_workspace_spice() -> None:
136
+ root, soul_path, agents_json, native = _native_profile_fixture()
137
+ try:
138
+ with (
139
+ patch.object(server, "NATIVE_PROFILE", native),
140
+ patch.object(server, "HERMES_DATA", root),
141
+ patch.object(server, "AGENTS_JSON", agents_json),
142
+ patch.object(server, "_profile_dir", side_effect=lambda slug: root / slug),
143
+ patch.object(server, "resolve_validated_profile", side_effect=lambda p: p),
144
+ patch.object(server, "_is_native_profile", side_effect=lambda p: p == native),
145
+ patch.object(server, "_primary_profile", return_value=native),
146
+ patch.object(
147
+ server,
148
+ "_workspace_agent_spice",
149
+ return_value="Prefer concise bullet replies.",
150
+ ),
151
+ ):
152
+ hermes_admin.profile_soul_set(native, "Lead with empathy.", layer="admin")
153
+ composed = hermes_profiles._profile_soul_text(native, "workspace-1")
154
+ assert "## Manager identity" in composed
155
+ assert "Lead with empathy." in composed
156
+ assert "## Workspace preferences" in composed
157
+ assert "Prefer concise bullet replies." in composed
158
+ layers = hermes_admin.profile_soul_get(native, "workspace-1")
159
+ assert layers["admin"] == "Lead with empathy."
160
+ assert layers["workspace_spice"] == "Prefer concise bullet replies."
161
+ assert "Lead with empathy." in layers["composed"]
162
+ finally:
163
+ import shutil
164
+
165
+ shutil.rmtree(root, ignore_errors=True)
166
+
167
+
168
+ def test_runtime_inherits_native_admin_soul() -> None:
169
+ root, _, agents_json, native = _native_profile_fixture()
170
+ runtime = "u-alice-workframe-agent"
171
+ (root / runtime).mkdir()
172
+ (root / runtime / "SOUL.md").write_text("runtime stub\n", encoding="utf-8")
173
+ try:
174
+ with (
175
+ patch.object(server, "NATIVE_PROFILE", native),
176
+ patch.object(server, "HERMES_DATA", root),
177
+ patch.object(server, "AGENTS_JSON", agents_json),
178
+ patch.object(server, "_profile_dir", side_effect=lambda slug: root / slug),
179
+ patch.object(server, "resolve_validated_profile", side_effect=lambda p: p),
180
+ patch.object(server, "_is_native_profile", side_effect=lambda p: p == native),
181
+ patch.object(server, "_primary_profile", return_value=native),
182
+ patch.object(server, "_is_runtime_profile_slug", side_effect=lambda p: p.startswith("u-")),
183
+ ):
184
+ hermes_admin.profile_soul_set(native, "Shared native admin overlay.", layer="admin")
185
+ composed = hermes_profiles._profile_soul_text(runtime)
186
+ assert "## Manager identity" in composed
187
+ assert "Shared native admin overlay." in composed
188
+ finally:
189
+ import shutil
190
+
191
+ shutil.rmtree(root, ignore_errors=True)
192
+
193
+
194
+ def test_child_user_soul_overlay_still_works() -> None:
195
+ root = Path(tempfile.mkdtemp())
196
+ child = "research-analyst"
197
+ prof_dir = root / child
198
+ prof_dir.mkdir(parents=True)
199
+ (prof_dir / "SOUL.md").write_text(
200
+ "# Research Analyst\n\nMission: dig into sources.\n",
201
+ encoding="utf-8",
202
+ )
203
+ agents_json = root / "workframe" / "agents.json"
204
+ agents_json.parent.mkdir(parents=True, exist_ok=True)
205
+ agents_json.write_text(json.dumps({"version": 1, "agents": {}}), encoding="utf-8")
206
+ try:
207
+ with (
208
+ patch.object(server, "NATIVE_PROFILE", "workframe-agent"),
209
+ patch.object(server, "HERMES_DATA", root),
210
+ patch.object(server, "AGENTS_JSON", agents_json),
211
+ patch.object(server, "_profile_dir", side_effect=lambda slug: root / slug),
212
+ patch.object(server, "resolve_validated_profile", side_effect=lambda p: p),
213
+ patch.object(server, "_is_native_profile", side_effect=lambda p: p == "workframe-agent"),
214
+ patch.object(server, "_is_runtime_profile_slug", return_value=False),
215
+ patch.object(server, "_primary_profile", return_value="workframe-agent"),
216
+ ):
217
+ result = hermes_admin.profile_soul_set(child, "Always cite sources.", layer="user")
218
+ assert result.get("ok")
219
+ assert result.get("target") == "user_soul"
220
+ composed = hermes_profiles._profile_soul_text(child)
221
+ assert "## User preferences" in composed
222
+ assert "Always cite sources." in composed
223
+ assert "## Manager identity" not in composed
224
+ finally:
225
+ import shutil
226
+
227
+ shutil.rmtree(root, ignore_errors=True)
228
+
229
+
91
230
  if __name__ == "__main__":
92
231
  test_runtime_display_name_comes_from_shared_agent()
93
232
  test_native_display_name_comes_from_shared_agent()
94
233
  test_runtime_identity_fields_ignore_runtime_registry_metadata()
95
234
  test_runtime_identity_sync_preserves_credentials()
235
+ test_native_admin_soul_does_not_modify_soul_md()
236
+ test_composed_soul_includes_admin_and_workspace_spice()
237
+ test_runtime_inherits_native_admin_soul()
238
+ test_child_user_soul_overlay_still_works()
96
239
  print("test_agent_identity_authority: ok")
@@ -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-CzIj9wwn.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-CpHOvtGr.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-BDDyJ-KR.js";export{e as createArchitectureServices};