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.
- package/README.md +1 -1
- package/package.json +1 -1
- package/workframe-api/handler_modules/handler_auth.py +1 -1
- package/workframe-api/handler_modules/handler_chat.py +21 -2
- package/workframe-api/hermes_admin.py +45 -13
- package/workframe-api/hermes_profiles.py +72 -17
- package/workframe-api/profile_gateway.py +16 -1
- package/workframe-api/rooms.py +9 -4
- package/workframe-api/server.py +4 -0
- package/workframe-api/site_meta.py +23 -7
- package/workframe-api/test_agent_identity_authority.py +143 -0
- package/workframe-ui/public/apple-touch-icon.png +0 -0
- package/workframe-ui/public/assets/{arc-C_mfAdSB.js → arc-CcePMZIA.js} +1 -1
- package/workframe-ui/public/assets/architecture-7EHR7CIX-DOU7ZjKI.js +1 -0
- package/workframe-ui/public/assets/{architectureDiagram-3BPJPVTR-B_BBjtYN.js → architectureDiagram-3BPJPVTR-kn-syfTx.js} +1 -1
- package/workframe-ui/public/assets/{blockDiagram-GPEHLZMM-CQgRGCnf.js → blockDiagram-GPEHLZMM-DzNpIHkp.js} +1 -1
- package/workframe-ui/public/assets/branding/apple-touch-icon.png +0 -0
- package/workframe-ui/public/assets/branding/favicon.svg +9 -0
- package/workframe-ui/public/assets/branding/icon-192.png +0 -0
- package/workframe-ui/public/assets/branding/icon-32.png +0 -0
- package/workframe-ui/public/assets/branding/icon-512.png +0 -0
- package/workframe-ui/public/assets/branding/icon-96.png +0 -0
- package/workframe-ui/public/assets/branding/og-default.png +0 -0
- package/workframe-ui/public/assets/branding/og-default.svg +11 -0
- package/workframe-ui/public/assets/branding/workframe-color.svg +6 -0
- package/workframe-ui/public/assets/branding/workframe-logo-horizontal.svg +43 -0
- package/workframe-ui/public/assets/branding/workframe-logo-vertical.svg +43 -0
- package/workframe-ui/public/assets/branding/workframe-mark-1024.svg +9 -0
- package/workframe-ui/public/assets/branding/workframe-slogan.svg +35 -0
- package/workframe-ui/public/assets/branding/workframe-wordmark.svg +19 -0
- package/workframe-ui/public/assets/branding/workframe.svg +6 -0
- package/workframe-ui/public/assets/branding/workframe_icon_color.svg +6 -0
- package/workframe-ui/public/assets/{c4Diagram-AAUBKEIU-D-qcrcwZ.js → c4Diagram-AAUBKEIU-DVWSIwwX.js} +1 -1
- package/workframe-ui/public/assets/channel-DHwVoOv6.js +1 -0
- package/workframe-ui/public/assets/{chunk-2J33WTMH-iqAEgUi9.js → chunk-2J33WTMH-oUrzZhyP.js} +1 -1
- package/workframe-ui/public/assets/{chunk-3OPIFGDE-iUcUq-bF.js → chunk-3OPIFGDE-DjxnqFMO.js} +1 -1
- package/workframe-ui/public/assets/{chunk-4BX2VUAB-C5JgXKRg.js → chunk-4BX2VUAB-BYZHOUd5.js} +1 -1
- package/workframe-ui/public/assets/{chunk-55IACEB6-DzhLEunm.js → chunk-55IACEB6-BKp-79Nb.js} +1 -1
- package/workframe-ui/public/assets/{chunk-5ZQYHXKU-_zQHxLyI.js → chunk-5ZQYHXKU-Djlk6-gp.js} +1 -1
- package/workframe-ui/public/assets/{chunk-727SXJPM-DS-52in2.js → chunk-727SXJPM-DwaLHvVO.js} +1 -1
- package/workframe-ui/public/assets/{chunk-AQP2D5EJ-CCqiqPd-.js → chunk-AQP2D5EJ-BKzQQcHz.js} +1 -1
- package/workframe-ui/public/assets/{chunk-BSJP7CBP-OJ_itMAX.js → chunk-BSJP7CBP-BXXaVunA.js} +1 -1
- package/workframe-ui/public/assets/{chunk-CSCIHK7Q-DI6HSEKJ.js → chunk-CSCIHK7Q-DkpcxLfx.js} +1 -1
- package/workframe-ui/public/assets/{chunk-FMBD7UC4-DNAZXeqV.js → chunk-FMBD7UC4-msI1fUy2.js} +1 -1
- package/workframe-ui/public/assets/{chunk-KSCS5N6A-D_BoWW9-.js → chunk-KSCS5N6A-BOvsXH5N.js} +1 -1
- package/workframe-ui/public/assets/{chunk-L5ZTLDWV-XM6sSJCQ.js → chunk-L5ZTLDWV-DvPOBk7Z.js} +1 -1
- package/workframe-ui/public/assets/{chunk-LZXEDZCA-B1Hpet8E.js → chunk-LZXEDZCA-Doe_jJhs.js} +2 -2
- package/workframe-ui/public/assets/{chunk-ND2GUHAM-Bc92coLi.js → chunk-ND2GUHAM-DR7GNhFP.js} +1 -1
- package/workframe-ui/public/assets/{chunk-NZK2D7GU-DsPENZUC.js → chunk-NZK2D7GU-Cv82JKZc.js} +1 -1
- package/workframe-ui/public/assets/{chunk-O5CBEL6O-RjhFF1lf.js → chunk-O5CBEL6O-BxPPKZK8.js} +1 -1
- package/workframe-ui/public/assets/chunk-QZHKN3VN-Br0-9tm7.js +1 -0
- package/workframe-ui/public/assets/chunk-WU5MYG2G-DYQX9yrg.js +1 -0
- package/workframe-ui/public/assets/{chunk-XPW4576I-DYerufPu.js → chunk-XPW4576I-DzgvYRRw.js} +1 -1
- package/workframe-ui/public/assets/classDiagram-4FO5ZUOK-Brqc6s0d.js +1 -0
- package/workframe-ui/public/assets/classDiagram-v2-Q7XG4LA2-Brqc6s0d.js +1 -0
- package/workframe-ui/public/assets/{cose-bilkent-S5V4N54A-BkswoDoD.js → cose-bilkent-S5V4N54A-ETOvxuP2.js} +1 -1
- package/workframe-ui/public/assets/{dagre-BM42HDAG-C75M_CTv.js → dagre-BM42HDAG-Cx28MGYq.js} +1 -1
- package/workframe-ui/public/assets/{diagram-2AECGRRQ-9ylAnB1d.js → diagram-2AECGRRQ-QHhMUULb.js} +1 -1
- package/workframe-ui/public/assets/{diagram-5GNKFQAL-Bl3WnHCB.js → diagram-5GNKFQAL-D5gDzxY2.js} +1 -1
- package/workframe-ui/public/assets/{diagram-KO2AKTUF-D0Ye3svD.js → diagram-KO2AKTUF-ByDYazkb.js} +1 -1
- package/workframe-ui/public/assets/{diagram-LMA3HP47-CoAn2THb.js → diagram-LMA3HP47-CWg67SyJ.js} +1 -1
- package/workframe-ui/public/assets/{diagram-OG6HWLK6-C7EysBha.js → diagram-OG6HWLK6-ssMcKSm_.js} +1 -1
- package/workframe-ui/public/assets/{dist-CzIj9wwn.js → dist-CpHOvtGr.js} +1 -1
- package/workframe-ui/public/assets/{erDiagram-TEJ5UH35-AervmYSk.js → erDiagram-TEJ5UH35-BmL9efhY.js} +1 -1
- package/workframe-ui/public/assets/eventmodeling-FCH6USID-S-FXSXpt.js +1 -0
- package/workframe-ui/public/assets/{flowDiagram-I6XJVG4X-DlGnJe8_.js → flowDiagram-I6XJVG4X-CPWX2r-j.js} +1 -1
- package/workframe-ui/public/assets/{ganttDiagram-6RSMTGT7-Dvh5SnNo.js → ganttDiagram-6RSMTGT7-dbMUKarp.js} +1 -1
- package/workframe-ui/public/assets/{gitGraph-WXDBUCRP-fi-tPIds.js → gitGraph-WXDBUCRP-Cw3Soqev.js} +1 -1
- package/workframe-ui/public/assets/{gitGraphDiagram-PVQCEYII-_72qSuU_.js → gitGraphDiagram-PVQCEYII-wpl8ML96.js} +1 -1
- package/workframe-ui/public/assets/index-BPbXwgtE.js +133 -0
- package/workframe-ui/public/assets/index-D7_Z82LT.css +1 -0
- package/workframe-ui/public/assets/{info-J43DQDTF-OEAgdAvP.js → info-J43DQDTF-Dpy8GEXy.js} +1 -1
- package/workframe-ui/public/assets/{infoDiagram-5YYISTIA-rCXHubHd.js → infoDiagram-5YYISTIA-CfAgUZ9f.js} +1 -1
- package/workframe-ui/public/assets/{ishikawaDiagram-YF4QCWOH-1MUwVnW9.js → ishikawaDiagram-YF4QCWOH-BZ6_CJTU.js} +1 -1
- package/workframe-ui/public/assets/{journeyDiagram-JHISSGLW-C34qfpF3.js → journeyDiagram-JHISSGLW-DsdF__AF.js} +1 -1
- package/workframe-ui/public/assets/{kanban-definition-UN3LZRKU-B3Hns9lE.js → kanban-definition-UN3LZRKU-Bo7wwOYe.js} +1 -1
- package/workframe-ui/public/assets/{line-BKcrvW_y.js → line-BifoaLZZ.js} +1 -1
- package/workframe-ui/public/assets/{linear-BL3C-fBh.js → linear-DFFjuuFL.js} +1 -1
- package/workframe-ui/public/assets/{mermaid-parser.core-CV_JAhfz.js → mermaid-parser.core-BDDyJ-KR.js} +2 -2
- package/workframe-ui/public/assets/{mermaid.core-CPPFZ0AZ.js → mermaid.core-BhBk99ho.js} +3 -3
- package/workframe-ui/public/assets/{mindmap-definition-RKZ34NQL-9erqZdGi.js → mindmap-definition-RKZ34NQL-k4-yj5dU.js} +1 -1
- package/workframe-ui/public/assets/{packet-YPE3B663-CZlmsvNS.js → packet-YPE3B663-Bfrh39KO.js} +1 -1
- package/workframe-ui/public/assets/{pie-LRSECV5Y-lqaCPGhY.js → pie-LRSECV5Y-CxCgAHor.js} +1 -1
- package/workframe-ui/public/assets/{pieDiagram-4H26LBE5-C1zz8JvX.js → pieDiagram-4H26LBE5-C876ADMr.js} +1 -1
- package/workframe-ui/public/assets/{quadrantDiagram-W4KKPZXB-Dmp-2UMf.js → quadrantDiagram-W4KKPZXB-fqq0fkmJ.js} +1 -1
- package/workframe-ui/public/assets/{radar-GUYGQ44K-DjureLKl.js → radar-GUYGQ44K-BJnY1Jnx.js} +1 -1
- package/workframe-ui/public/assets/{requirementDiagram-4Y6WPE33-DXQI-c25.js → requirementDiagram-4Y6WPE33-DTBusY6q.js} +1 -1
- package/workframe-ui/public/assets/{sankeyDiagram-5OEKKPKP-BaZ9qvKi.js → sankeyDiagram-5OEKKPKP-D8kiVsJV.js} +1 -1
- package/workframe-ui/public/assets/{sequenceDiagram-3UESZ5HK-iAl57VoB.js → sequenceDiagram-3UESZ5HK-DfzDwdKl.js} +1 -1
- package/workframe-ui/public/assets/{src-DaHiggVY.js → src-Cy1USNLY.js} +1 -1
- package/workframe-ui/public/assets/{stateDiagram-AJRCARHV-kWK_vPK0.js → stateDiagram-AJRCARHV-sVqLyvFR.js} +1 -1
- package/workframe-ui/public/assets/stateDiagram-v2-BHNVJYJU-COrv6OF1.js +1 -0
- package/workframe-ui/public/assets/{timeline-definition-PNZ67QCA-BK7AMTE2.js → timeline-definition-PNZ67QCA-0WDiG_ak.js} +1 -1
- package/workframe-ui/public/assets/{treeView-BLDUP644-XvrJnjfl.js → treeView-BLDUP644-BrHz58-9.js} +1 -1
- package/workframe-ui/public/assets/{treemap-LRROVOQU-DwiXEpvw.js → treemap-LRROVOQU-kAd70w_y.js} +1 -1
- package/workframe-ui/public/assets/{vennDiagram-CIIHVFJN-BwHfuFiD.js → vennDiagram-CIIHVFJN-DnXBmEMU.js} +1 -1
- package/workframe-ui/public/assets/{wardley-L42UT6IY-CjVb_35b.js → wardley-L42UT6IY-hmH7OEGp.js} +1 -1
- package/workframe-ui/public/assets/{wardleyDiagram-YWT4CUSO-pTplM3xe.js → wardleyDiagram-YWT4CUSO-DYeDIRJn.js} +1 -1
- package/workframe-ui/public/assets/{xychartDiagram-2RQKCTM6-CXLIJGkG.js → xychartDiagram-2RQKCTM6-BRJldh4u.js} +1 -1
- package/workframe-ui/public/favicon.ico +0 -0
- package/workframe-ui/public/favicon.svg +9 -7
- package/workframe-ui/public/icon-192.png +0 -0
- package/workframe-ui/public/icon-32.png +0 -0
- package/workframe-ui/public/icon-512.png +0 -0
- package/workframe-ui/public/icon-96.png +0 -0
- package/workframe-ui/public/index.html +13 -9
- package/workframe-ui/public/manifest.webmanifest +27 -3
- package/workframe-ui/public/og-default.png +0 -0
- package/workframe-ui/public/workframe-build.json +4 -4
- package/workframe-ui/public/workframe-mark-1024.svg +9 -0
- package/workframe-ui/public/assets/architecture-7EHR7CIX-DLru-uUG.js +0 -1
- package/workframe-ui/public/assets/channel-BtxFT9GI.js +0 -1
- package/workframe-ui/public/assets/chunk-QZHKN3VN-CewU2mqh.js +0 -1
- package/workframe-ui/public/assets/chunk-WU5MYG2G-BCUMJc-u.js +0 -1
- package/workframe-ui/public/assets/classDiagram-4FO5ZUOK-B-h6w58g.js +0 -1
- package/workframe-ui/public/assets/classDiagram-v2-Q7XG4LA2-B-h6w58g.js +0 -1
- package/workframe-ui/public/assets/eventmodeling-FCH6USID-yOFv10Xp.js +0 -1
- package/workframe-ui/public/assets/index-BY14qQF5.css +0 -1
- package/workframe-ui/public/assets/index-DxrMmuBD.js +0 -133
- package/workframe-ui/public/assets/stateDiagram-v2-BHNVJYJU-9gufjyOk.js +0 -1
package/README.md
CHANGED
package/package.json
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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":
|
|
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
|
-
|
|
546
|
-
|
|
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
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
display_name
|
|
448
|
-
role
|
|
449
|
-
tagline
|
|
450
|
-
|
|
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
|
|
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 =
|
|
559
|
+
user_soul = _profile_user_soul(prof)
|
|
505
560
|
if user_soul:
|
|
506
|
-
blocks.append("##
|
|
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
|
-
|
|
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
|
package/workframe-api/rooms.py
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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 = ?")
|
package/workframe-api/server.py
CHANGED
|
@@ -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 =
|
|
15
|
-
|
|
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
|
-
|
|
203
|
-
|
|
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":
|
|
233
|
+
"background_color": "#ffffff",
|
|
218
234
|
"theme_color": meta.get("theme_color") or DEFAULT_THEME_COLOR,
|
|
219
235
|
"orientation": "any",
|
|
220
|
-
"icons": icons
|
|
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")
|
|
Binary file
|
|
@@ -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-
|
|
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};
|