create-workframe 0.1.14 → 0.1.15
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/scripts/sync-canonical-to-package.mjs +3 -0
- package/workframe-api/activity_feed.py +53 -5
- package/workframe-api/api_errors.py +126 -0
- package/workframe-api/chat_bind.py +8 -1
- package/workframe-api/chat_stream.py +70 -26
- package/workframe-api/crew_registry.py +3 -6
- package/workframe-api/google_auth.py +20 -12
- package/workframe-api/handler_modules/handler_auth.py +66 -21
- package/workframe-api/handler_modules/handler_chat.py +25 -0
- package/workframe-api/handler_modules/handler_install.py +45 -0
- package/workframe-api/handler_modules/handler_provider.py +1 -0
- package/workframe-api/handler_modules/handler_workspace.py +30 -16
- package/workframe-api/hermes_admin.py +9 -2
- package/workframe-api/hermes_profiles.py +25 -5
- package/workframe-api/install_api.py +38 -4
- package/workframe-api/lane_bindings.py +24 -0
- package/workframe-api/llm_error_glossary.py +58 -4
- package/workframe-api/mention_invoke.py +89 -27
- package/workframe-api/model_surface.py +1 -1
- package/workframe-api/provider_bindings.py +44 -2
- package/workframe-api/provider_bootstrap.py +9 -0
- package/workframe-api/provider_catalog.py +3 -3
- package/workframe-api/rooms.py +3 -0
- package/workframe-api/route_registry.py +1 -0
- package/workframe-api/run_ledger.py +52 -8
- package/workframe-api/runtime_cohort.py +11 -1
- package/workframe-api/server.py +25 -1
- package/workframe-api/stack_config.py +4 -1
- package/workframe-api/test_api_errors.py +33 -0
- package/workframe-api/test_enrich_room_messages.py +72 -0
- package/workframe-api/test_exception_hygiene.py +4 -2
- package/workframe-api/test_hermes_admin.py +7 -0
- package/workframe-api/test_install_wizard_resume.py +39 -13
- package/workframe-api/test_model_surface_consistency.py +118 -100
- package/workframe-api/test_profile_model_yaml.py +4 -1
- package/workframe-api/test_provider_bindings.py +10 -0
- package/workframe-api/test_run_ledger.py +2 -1
- package/workframe-api/test_server_reexports.py +4 -0
- package/workframe-api/test_user_llm_prefs.py +99 -0
- package/workframe-api/time-overlay-split.py +58 -0
- package/workframe-api/time-ttft-quick.py +135 -0
- package/workframe-api/time-ttft.py +250 -0
- package/workframe-api/time-wf-stream.py +145 -0
- package/workframe-api/turn_overlay.py +96 -23
- package/workframe-api/updates.py +1 -2
- package/workframe-api/user_prefs.py +26 -5
- package/workframe-api/workspace_bootstrap.py +16 -10
- package/workframe-ui/public/assets/{arc-BT9iZUSd.js → arc-BARqpbNX.js} +1 -1
- package/workframe-ui/public/assets/architecture-7EHR7CIX-CYE89P6J.js +1 -0
- package/workframe-ui/public/assets/{architectureDiagram-3BPJPVTR-CaEDd3np.js → architectureDiagram-3BPJPVTR-bbpW6hfw.js} +1 -1
- package/workframe-ui/public/assets/{blockDiagram-GPEHLZMM-DHJhMrIS.js → blockDiagram-GPEHLZMM-aG0oR9eA.js} +1 -1
- package/workframe-ui/public/assets/brave-B885JYnH.svg +11 -0
- package/workframe-ui/public/assets/{c4Diagram-AAUBKEIU-Cdyz9hIV.js → c4Diagram-AAUBKEIU-BDi4E5jK.js} +1 -1
- package/workframe-ui/public/assets/channel-B4ex0Z2H.js +1 -0
- package/workframe-ui/public/assets/{chunk-2J33WTMH-BbZx76LX.js → chunk-2J33WTMH-BBU5tRNe.js} +1 -1
- package/workframe-ui/public/assets/{chunk-3OPIFGDE-DcSxjTIP.js → chunk-3OPIFGDE-SKhe5YD8.js} +1 -1
- package/workframe-ui/public/assets/{chunk-4BX2VUAB-B7F-kTY1.js → chunk-4BX2VUAB-D4uKMCPc.js} +1 -1
- package/workframe-ui/public/assets/{chunk-55IACEB6-BwwEiRTK.js → chunk-55IACEB6-SnqJA0wG.js} +1 -1
- package/workframe-ui/public/assets/{chunk-5ZQYHXKU-DAi_NksX.js → chunk-5ZQYHXKU-ZyBWNsT2.js} +1 -1
- package/workframe-ui/public/assets/{chunk-727SXJPM-BIaF4XCN.js → chunk-727SXJPM-DbxwKsiK.js} +1 -1
- package/workframe-ui/public/assets/{chunk-AQP2D5EJ-DjYSk1dG.js → chunk-AQP2D5EJ-Bm2iWcL0.js} +1 -1
- package/workframe-ui/public/assets/{chunk-BSJP7CBP-_twnyfgV.js → chunk-BSJP7CBP-CGwyQh1d.js} +1 -1
- package/workframe-ui/public/assets/{chunk-CSCIHK7Q-CkNmdy5s.js → chunk-CSCIHK7Q-XWSO96VW.js} +1 -1
- package/workframe-ui/public/assets/{chunk-FMBD7UC4-CjLqd-gl.js → chunk-FMBD7UC4-D0OTpUqL.js} +1 -1
- package/workframe-ui/public/assets/{chunk-KSCS5N6A-_6oNqFjj.js → chunk-KSCS5N6A-CM75wJ6j.js} +1 -1
- package/workframe-ui/public/assets/{chunk-L5ZTLDWV-CeO9Am_D.js → chunk-L5ZTLDWV-BCgtwaGP.js} +1 -1
- package/workframe-ui/public/assets/{chunk-LZXEDZCA-sRM4YeIe.js → chunk-LZXEDZCA-CZBc86Td.js} +2 -2
- package/workframe-ui/public/assets/{chunk-ND2GUHAM-CH8tMb21.js → chunk-ND2GUHAM-DIq9-Qtg.js} +1 -1
- package/workframe-ui/public/assets/{chunk-NZK2D7GU-DAI1bsII.js → chunk-NZK2D7GU-BTOOFYOR.js} +1 -1
- package/workframe-ui/public/assets/{chunk-O5CBEL6O-B2e-5a2G.js → chunk-O5CBEL6O-CYriwTot.js} +1 -1
- package/workframe-ui/public/assets/chunk-QZHKN3VN-sPK7umZx.js +1 -0
- package/workframe-ui/public/assets/chunk-WU5MYG2G-DtLDSmiK.js +1 -0
- package/workframe-ui/public/assets/{chunk-XPW4576I-DM2-0q37.js → chunk-XPW4576I-6iEhdiBi.js} +1 -1
- package/workframe-ui/public/assets/classDiagram-4FO5ZUOK-DWLy8pDS.js +1 -0
- package/workframe-ui/public/assets/classDiagram-v2-Q7XG4LA2-DWLy8pDS.js +1 -0
- package/workframe-ui/public/assets/{cose-bilkent-S5V4N54A-BnbbewLv.js → cose-bilkent-S5V4N54A-C716vjLB.js} +1 -1
- package/workframe-ui/public/assets/{dagre-BM42HDAG-C4zmB1Vt.js → dagre-BM42HDAG-Cs-MNVkM.js} +1 -1
- package/workframe-ui/public/assets/{diagram-2AECGRRQ-n-BJmsiC.js → diagram-2AECGRRQ-BqoyTWyW.js} +1 -1
- package/workframe-ui/public/assets/{diagram-5GNKFQAL-BzQGQnZi.js → diagram-5GNKFQAL-CDRTV5fY.js} +1 -1
- package/workframe-ui/public/assets/{diagram-KO2AKTUF-Cx_De-wS.js → diagram-KO2AKTUF-u9NjixEi.js} +1 -1
- package/workframe-ui/public/assets/{diagram-LMA3HP47-CIOI3Lok.js → diagram-LMA3HP47-rDd6T2os.js} +1 -1
- package/workframe-ui/public/assets/{diagram-OG6HWLK6-DkRuPLWY.js → diagram-OG6HWLK6-Bl4VX9RO.js} +1 -1
- package/workframe-ui/public/assets/{dist-XhCT1Q-V.js → dist-DSCcbFgV.js} +1 -1
- package/workframe-ui/public/assets/{erDiagram-TEJ5UH35-C3GwhDU1.js → erDiagram-TEJ5UH35-C9Nv-5nl.js} +1 -1
- package/workframe-ui/public/assets/eventmodeling-FCH6USID-B7kChV1b.js +1 -0
- package/workframe-ui/public/assets/{flowDiagram-I6XJVG4X-CLWH8z2e.js → flowDiagram-I6XJVG4X-D8Xhbd7s.js} +1 -1
- package/workframe-ui/public/assets/{ganttDiagram-6RSMTGT7-D4h66Vy3.js → ganttDiagram-6RSMTGT7-CMycBZqm.js} +1 -1
- package/workframe-ui/public/assets/gemini-Cq-0-rmr.svg +105 -0
- package/workframe-ui/public/assets/{gitGraph-WXDBUCRP-IW5F-Q6c.js → gitGraph-WXDBUCRP-DnIro8tZ.js} +1 -1
- package/workframe-ui/public/assets/{gitGraphDiagram-PVQCEYII-BjNIbmRM.js → gitGraphDiagram-PVQCEYII-B74H9ISZ.js} +1 -1
- package/workframe-ui/public/assets/index-BLVAsAeO.css +1 -0
- package/workframe-ui/public/assets/index-s4167RRh.js +132 -0
- package/workframe-ui/public/assets/{info-J43DQDTF-CcJspM79.js → info-J43DQDTF-B6mVw1B6.js} +1 -1
- package/workframe-ui/public/assets/{infoDiagram-5YYISTIA-DP4xR9jt.js → infoDiagram-5YYISTIA-Ddtfq_xy.js} +1 -1
- package/workframe-ui/public/assets/{ishikawaDiagram-YF4QCWOH-D4cZb21V.js → ishikawaDiagram-YF4QCWOH-DBb6veAL.js} +1 -1
- package/workframe-ui/public/assets/{journeyDiagram-JHISSGLW-DsLuHkIJ.js → journeyDiagram-JHISSGLW-DMgdeS5h.js} +1 -1
- package/workframe-ui/public/assets/{kanban-definition-UN3LZRKU-Wu65nOsR.js → kanban-definition-UN3LZRKU-DiV_LoDN.js} +1 -1
- package/workframe-ui/public/assets/{line-ByBDzRpy.js → line-BR9uEes_.js} +1 -1
- package/workframe-ui/public/assets/{linear-DzXi2vnq.js → linear-BhjkJfy9.js} +1 -1
- package/workframe-ui/public/assets/{mermaid-parser.core-CaB-1Ckn.js → mermaid-parser.core-CEWtaJIq.js} +2 -2
- package/workframe-ui/public/assets/{mermaid.core-D7HTHC17.js → mermaid.core-B6xKLKp-.js} +3 -3
- package/workframe-ui/public/assets/{mindmap-definition-RKZ34NQL-BfKsC9aj.js → mindmap-definition-RKZ34NQL-DypZm9OU.js} +1 -1
- package/workframe-ui/public/assets/{packet-YPE3B663-ClpU98TO.js → packet-YPE3B663-CCxb0WDS.js} +1 -1
- package/workframe-ui/public/assets/{pie-LRSECV5Y-KeOyY_-u.js → pie-LRSECV5Y-pTkr4g8B.js} +1 -1
- package/workframe-ui/public/assets/{pieDiagram-4H26LBE5-CUpjnzbs.js → pieDiagram-4H26LBE5-BventgTb.js} +1 -1
- package/workframe-ui/public/assets/{quadrantDiagram-W4KKPZXB-CJdPTszF.js → quadrantDiagram-W4KKPZXB-BQnvFJYk.js} +1 -1
- package/workframe-ui/public/assets/{radar-GUYGQ44K-C9BpandZ.js → radar-GUYGQ44K-EX9Pzmli.js} +1 -1
- package/workframe-ui/public/assets/{requirementDiagram-4Y6WPE33-CHDZPwry.js → requirementDiagram-4Y6WPE33-Bnw4xecM.js} +1 -1
- package/workframe-ui/public/assets/{sankeyDiagram-5OEKKPKP-CMJJh91s.js → sankeyDiagram-5OEKKPKP-spHh-EYp.js} +1 -1
- package/workframe-ui/public/assets/{sequenceDiagram-3UESZ5HK-BsV4GppP.js → sequenceDiagram-3UESZ5HK-CC97Qcbf.js} +1 -1
- package/workframe-ui/public/assets/{src-WHks82Up.js → src-DRcBNyeZ.js} +1 -1
- package/workframe-ui/public/assets/{stateDiagram-AJRCARHV-eSX9P8z0.js → stateDiagram-AJRCARHV-C8r8rx81.js} +1 -1
- package/workframe-ui/public/assets/stateDiagram-v2-BHNVJYJU-CnedwxVT.js +1 -0
- package/workframe-ui/public/assets/{timeline-definition-PNZ67QCA-Dg1UkhGo.js → timeline-definition-PNZ67QCA-CA4JxR_w.js} +1 -1
- package/workframe-ui/public/assets/{treeView-BLDUP644-B7yGsBuj.js → treeView-BLDUP644-BuGSo6xP.js} +1 -1
- package/workframe-ui/public/assets/{treemap-LRROVOQU-j_G2oyQ_.js → treemap-LRROVOQU-JyJMArE9.js} +1 -1
- package/workframe-ui/public/assets/{vennDiagram-CIIHVFJN-BRrT0jqC.js → vennDiagram-CIIHVFJN-BZAUo7xL.js} +1 -1
- package/workframe-ui/public/assets/{wardley-L42UT6IY-DEdUqez2.js → wardley-L42UT6IY-CMgm8t9M.js} +1 -1
- package/workframe-ui/public/assets/{wardleyDiagram-YWT4CUSO-C-bBK9Bb.js → wardleyDiagram-YWT4CUSO-CqI1c2zN.js} +1 -1
- package/workframe-ui/public/assets/{xychartDiagram-2RQKCTM6-DhH5xuR2.js → xychartDiagram-2RQKCTM6-DhTdH-jJ.js} +1 -1
- package/workframe-ui/public/index.html +2 -2
- package/workframe-ui/public/workframe-build.json +3 -3
- package/docs/security.md +0 -85
- package/workframe-ui/public/assets/architecture-7EHR7CIX-m1aPUQ_r.js +0 -1
- package/workframe-ui/public/assets/channel-3M69ekE5.js +0 -1
- package/workframe-ui/public/assets/chunk-QZHKN3VN-BMl9ed8P.js +0 -1
- package/workframe-ui/public/assets/chunk-WU5MYG2G-C08HH6BU.js +0 -1
- package/workframe-ui/public/assets/classDiagram-4FO5ZUOK-BFcOE4Aq.js +0 -1
- package/workframe-ui/public/assets/classDiagram-v2-Q7XG4LA2-BFcOE4Aq.js +0 -1
- package/workframe-ui/public/assets/eventmodeling-FCH6USID-DIVBL6CF.js +0 -1
- package/workframe-ui/public/assets/index-DL2vJP4L.css +0 -1
- package/workframe-ui/public/assets/index-ElS_D59P.js +0 -130
- package/workframe-ui/public/assets/stateDiagram-v2-BHNVJYJU-FzMK4PkM.js +0 -1
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import threading
|
|
6
|
+
import time
|
|
5
7
|
import uuid
|
|
6
8
|
from typing import Any
|
|
7
9
|
|
|
@@ -345,18 +347,56 @@ def _read_config_model_api_key(profile: str) -> str:
|
|
|
345
347
|
|
|
346
348
|
|
|
347
349
|
|
|
348
|
-
def _sync_profile_model_api_key(profile: str, api_key: str) -> None:
|
|
350
|
+
def _sync_profile_model_api_key(profile: str, api_key: str, *, wait_healthy: bool = False) -> None:
|
|
349
351
|
value = str(api_key or "").strip()
|
|
350
352
|
if not value:
|
|
351
353
|
_clear_profile_model_api_key(profile)
|
|
352
|
-
|
|
354
|
+
if wait_healthy:
|
|
355
|
+
_srv()._restart_runtime_profile_gateway(profile)
|
|
356
|
+
else:
|
|
357
|
+
_srv()._schedule_gateway_reload(profile)
|
|
353
358
|
return
|
|
354
359
|
if _read_config_model_api_key(profile) == value:
|
|
355
360
|
return
|
|
356
361
|
ok, err = _set_profile_model_api_key(profile, value)
|
|
357
362
|
if not ok:
|
|
358
363
|
raise ValueError(err or "profile model api_key write failed")
|
|
359
|
-
|
|
364
|
+
if wait_healthy:
|
|
365
|
+
_srv()._restart_runtime_profile_gateway(profile)
|
|
366
|
+
else:
|
|
367
|
+
_srv()._schedule_gateway_reload(profile)
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
def _schedule_profile_lease_yaml_reconcile(
|
|
371
|
+
profile: str,
|
|
372
|
+
user_id: str,
|
|
373
|
+
workspace_id: str,
|
|
374
|
+
provider: str,
|
|
375
|
+
) -> None:
|
|
376
|
+
"""Bind path — align config.yaml lease token without blocking the client."""
|
|
377
|
+
user = str(user_id or "").strip()
|
|
378
|
+
prov = str(provider or "openrouter").strip().lower()
|
|
379
|
+
if not user:
|
|
380
|
+
return
|
|
381
|
+
|
|
382
|
+
def _run() -> None:
|
|
383
|
+
try:
|
|
384
|
+
prof = _srv().resolve_hermes_profile(profile)
|
|
385
|
+
if not _srv()._is_runtime_profile_slug(prof):
|
|
386
|
+
return
|
|
387
|
+
env_var = _profile_lease_env_var(prof, prov)
|
|
388
|
+
token = _read_profile_lease_token(prof, env_var)
|
|
389
|
+
if not token or _read_config_model_api_key(prof) == token:
|
|
390
|
+
return
|
|
391
|
+
_sync_profile_model_api_key(prof, token)
|
|
392
|
+
except Exception: # noqa: BLE001
|
|
393
|
+
return
|
|
394
|
+
|
|
395
|
+
threading.Thread(
|
|
396
|
+
target=_run,
|
|
397
|
+
name=f"lease-yaml-{profile}",
|
|
398
|
+
daemon=True,
|
|
399
|
+
).start()
|
|
360
400
|
|
|
361
401
|
|
|
362
402
|
def _apply_turn_credential_lease(
|
|
@@ -369,6 +409,30 @@ def _apply_turn_credential_lease(
|
|
|
369
409
|
"""Issue per-run lease token into profile env; real secret stays in API vault."""
|
|
370
410
|
provider = str(provider or "openrouter").strip().lower()
|
|
371
411
|
prof = _srv().resolve_hermes_profile(profile)
|
|
412
|
+
env_var = _profile_lease_env_var(prof, provider)
|
|
413
|
+
config_lease = _read_config_model_api_key(prof)
|
|
414
|
+
if config_lease and not turn_credentials.validate_lease(config_lease):
|
|
415
|
+
_clear_profile_model_api_key(prof)
|
|
416
|
+
existing_token = _read_profile_lease_token(prof, env_var)
|
|
417
|
+
if existing_token and not turn_credentials.validate_lease(existing_token):
|
|
418
|
+
_strip_profile_llm_env(prof, include_leases=True)
|
|
419
|
+
_clear_profile_model_api_key(prof)
|
|
420
|
+
existing_token = ""
|
|
421
|
+
if existing_token:
|
|
422
|
+
lease_meta = turn_credentials.validate_lease(existing_token)
|
|
423
|
+
if lease_meta and _lease_reusable_for_turn(
|
|
424
|
+
existing_token,
|
|
425
|
+
user_id=user_id,
|
|
426
|
+
workspace_id=workspace_id,
|
|
427
|
+
provider=provider,
|
|
428
|
+
binding_id=str(lease_meta.get("credential_binding_id") or ""),
|
|
429
|
+
):
|
|
430
|
+
# ponytail: hot path — skip credential resolve + env rewrite when lease still valid
|
|
431
|
+
if _srv()._is_runtime_profile_slug(prof) and existing_token:
|
|
432
|
+
if _read_config_model_api_key(prof) != existing_token:
|
|
433
|
+
_sync_profile_model_api_key(prof, existing_token)
|
|
434
|
+
return existing_token
|
|
435
|
+
|
|
372
436
|
if _srv()._is_runtime_profile_slug(prof):
|
|
373
437
|
resolved = _require_runtime_owner_provider(user_id, workspace_id, provider)
|
|
374
438
|
else:
|
|
@@ -401,15 +465,6 @@ def _apply_turn_credential_lease(
|
|
|
401
465
|
user_id=user_id,
|
|
402
466
|
workspace_id=workspace_id,
|
|
403
467
|
)
|
|
404
|
-
env_var = _profile_lease_env_var(prof, provider)
|
|
405
|
-
config_lease = _read_config_model_api_key(prof)
|
|
406
|
-
if config_lease and not turn_credentials.validate_lease(config_lease):
|
|
407
|
-
_clear_profile_model_api_key(prof)
|
|
408
|
-
existing_token = _read_profile_lease_token(prof, env_var)
|
|
409
|
-
if existing_token and not turn_credentials.validate_lease(existing_token):
|
|
410
|
-
_strip_profile_llm_env(prof, include_leases=True)
|
|
411
|
-
_clear_profile_model_api_key(prof)
|
|
412
|
-
existing_token = ""
|
|
413
468
|
if _lease_reusable_for_turn(
|
|
414
469
|
existing_token,
|
|
415
470
|
user_id=user_id,
|
|
@@ -417,7 +472,6 @@ def _apply_turn_credential_lease(
|
|
|
417
472
|
provider=provider,
|
|
418
473
|
binding_id=binding_id,
|
|
419
474
|
):
|
|
420
|
-
# ponytail: hot path — skip yaml/env rewrite when lease still valid
|
|
421
475
|
if _srv()._is_runtime_profile_slug(prof) and existing_token:
|
|
422
476
|
if _read_config_model_api_key(prof) != existing_token:
|
|
423
477
|
_sync_profile_model_api_key(prof, existing_token)
|
|
@@ -462,11 +516,38 @@ def _user_action_env_specs() -> list[tuple[str, str]]:
|
|
|
462
516
|
return specs
|
|
463
517
|
|
|
464
518
|
|
|
519
|
+
_USER_ACTION_CRED_CACHE: dict[tuple[str, str], tuple[bool, float]] = {}
|
|
520
|
+
_USER_ACTION_CRED_TTL_SEC = 30.0
|
|
521
|
+
|
|
522
|
+
|
|
523
|
+
def _user_has_action_credentials(user_id: str, workspace_id: str) -> bool:
|
|
524
|
+
"""Cached negative scan — skip per-turn tool overlay when user has no PAT/OAuth tools."""
|
|
525
|
+
user = str(user_id or "").strip()
|
|
526
|
+
if not user:
|
|
527
|
+
return False
|
|
528
|
+
ws = str(workspace_id or "").strip()
|
|
529
|
+
key = (user, ws)
|
|
530
|
+
now = time.monotonic()
|
|
531
|
+
cached = _USER_ACTION_CRED_CACHE.get(key)
|
|
532
|
+
if cached and now - cached[1] < _USER_ACTION_CRED_TTL_SEC:
|
|
533
|
+
return cached[0]
|
|
534
|
+
has_any = False
|
|
535
|
+
for provider_id, _env_var in _user_action_env_specs():
|
|
536
|
+
resolved = _srv()._resolve_credential(user, ws, provider_id, user_only=True)
|
|
537
|
+
if resolved and _srv()._credential_secret(resolved, user):
|
|
538
|
+
has_any = True
|
|
539
|
+
break
|
|
540
|
+
_USER_ACTION_CRED_CACHE[key] = (has_any, now)
|
|
541
|
+
return has_any
|
|
542
|
+
|
|
543
|
+
|
|
465
544
|
def _overlay_turn_user_env(profile: str, user_id: str, workspace_id: str, run_id: str = "") -> None:
|
|
466
545
|
"""Issue per-run lease tokens for user-only tool credentials (never raw PATs on Agents mount)."""
|
|
467
546
|
user = str(user_id or "").strip()
|
|
468
547
|
if not user:
|
|
469
548
|
return
|
|
549
|
+
if not _user_has_action_credentials(user, workspace_id):
|
|
550
|
+
return
|
|
470
551
|
base_run = str(run_id or "").strip() or str(uuid.uuid4())
|
|
471
552
|
for provider_id, _env_var in _user_action_env_specs():
|
|
472
553
|
sub_run = f"{base_run}::{provider_id}"
|
|
@@ -658,16 +739,8 @@ def _overlay_turn_provider_env(
|
|
|
658
739
|
) -> str:
|
|
659
740
|
"""Per-run lease into profile env; upstream key resolved only inside workframe-api."""
|
|
660
741
|
run_id = str(run_id or "").strip() or str(uuid.uuid4())
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
config_before = _read_config_model_api_key(prof) if is_runtime else ""
|
|
664
|
-
token = _apply_turn_credential_lease(profile, user_id, workspace_id, provider, run_id)
|
|
665
|
-
if is_runtime and _read_config_model_api_key(prof) != config_before:
|
|
666
|
-
_srv()._invalidate_profile_health_cache(prof)
|
|
667
|
-
# ponytail: gateway restart after lease yaml sync can take ~40s on cold runtime profiles.
|
|
668
|
-
if not _srv()._wait_profile_api_healthy(prof, attempts=160, delay=0.25):
|
|
669
|
-
_srv().ensure_profile_api(prof, user_id, workspace_id, bootstrap_providers=False)
|
|
670
|
-
return token
|
|
742
|
+
# ponytail: yaml sync + gateway reload are async on the send path — never block TTFT (~40s).
|
|
743
|
+
return _apply_turn_credential_lease(profile, user_id, workspace_id, provider, run_id)
|
|
671
744
|
|
|
672
745
|
|
|
673
746
|
def _try_overlay_turn_provider_env(
|
package/workframe-api/updates.py
CHANGED
|
@@ -557,9 +557,8 @@ def apply_update(target: str, *, user_ack: bool = False) -> dict[str, Any]:
|
|
|
557
557
|
)
|
|
558
558
|
if update_decision.decision == "deny":
|
|
559
559
|
raise ValueError(update_decision.reason or "cell_update_denied")
|
|
560
|
-
if
|
|
560
|
+
if update_decision.decision != "allow":
|
|
561
561
|
raise ValueError(update_decision.reason or "cell_update_denied")
|
|
562
|
-
# ponytail: without user_ack, needs_user_action does not block apply until UI sends ack
|
|
563
562
|
target = str(target or "all").strip().lower()
|
|
564
563
|
if target not in {"hermes", "workframe", "all"}:
|
|
565
564
|
raise ValueError("invalid_update_target")
|
|
@@ -125,6 +125,15 @@ def read_user_llm_prefs(user_id: str) -> tuple[str, list[dict[str, str]]]:
|
|
|
125
125
|
chain.append({"provider": prov, "model": model})
|
|
126
126
|
except json.JSONDecodeError:
|
|
127
127
|
pass
|
|
128
|
+
if not primary and chain:
|
|
129
|
+
first = chain[0]
|
|
130
|
+
prov = str(first.get("provider") or "").strip()
|
|
131
|
+
model = str(first.get("model") or "").strip()
|
|
132
|
+
if model:
|
|
133
|
+
if prov and not model.lower().startswith(f"{prov}/"):
|
|
134
|
+
primary = f"{prov}/{model}"
|
|
135
|
+
else:
|
|
136
|
+
primary = model
|
|
128
137
|
return primary, chain
|
|
129
138
|
|
|
130
139
|
|
|
@@ -137,13 +146,25 @@ def write_user_llm_prefs(
|
|
|
137
146
|
user_id = str(user_id or "").strip()
|
|
138
147
|
if not user_id:
|
|
139
148
|
return
|
|
140
|
-
|
|
149
|
+
if not primary and fallback_chain is None:
|
|
150
|
+
return
|
|
151
|
+
user = get_workframe_user(user_id)
|
|
152
|
+
pids: dict[str, Any] = {}
|
|
153
|
+
if user and isinstance(user.get("platform_ids"), dict):
|
|
154
|
+
pids = dict(user["platform_ids"])
|
|
141
155
|
if primary:
|
|
142
|
-
|
|
156
|
+
pids["llm_primary"] = primary.strip()
|
|
143
157
|
if fallback_chain is not None:
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
158
|
+
pids["llm_fallback_chain"] = json.dumps(fallback_chain, separators=(",", ":"))
|
|
159
|
+
conn = _srv()._workframe_db()
|
|
160
|
+
try:
|
|
161
|
+
conn.execute(
|
|
162
|
+
"UPDATE users SET platform_ids = ?, updated_at = ? WHERE id = ?",
|
|
163
|
+
(json.dumps(pids, sort_keys=True), str(int(time.time())), user_id),
|
|
164
|
+
)
|
|
165
|
+
conn.commit()
|
|
166
|
+
finally:
|
|
167
|
+
conn.close()
|
|
147
168
|
|
|
148
169
|
|
|
149
170
|
def validate_me_profile_updates(updates: dict[str, Any]) -> None:
|
|
@@ -16,12 +16,12 @@ def _srv():
|
|
|
16
16
|
return srv
|
|
17
17
|
|
|
18
18
|
|
|
19
|
-
def _provision_invited_member_agent_runtimes(workspace_id: str, user_id: str) ->
|
|
19
|
+
def _provision_invited_member_agent_runtimes(workspace_id: str, user_id: str) -> bool:
|
|
20
20
|
"""Provision u-* runtimes for workspace agents when a member joins."""
|
|
21
21
|
workspace_id = str(workspace_id or "").strip()
|
|
22
22
|
user_id = str(user_id or "").strip()
|
|
23
23
|
if not workspace_id or not user_id:
|
|
24
|
-
return
|
|
24
|
+
return False
|
|
25
25
|
try:
|
|
26
26
|
conn = _srv()._workframe_db()
|
|
27
27
|
rows = conn.execute(
|
|
@@ -33,32 +33,35 @@ def _provision_invited_member_agent_runtimes(workspace_id: str, user_id: str) ->
|
|
|
33
33
|
).fetchall()
|
|
34
34
|
conn.close()
|
|
35
35
|
except Exception: # noqa: BLE001
|
|
36
|
-
return
|
|
36
|
+
return False
|
|
37
|
+
ok = True
|
|
37
38
|
for row in rows:
|
|
38
|
-
_provision_agent_dm_runtimes(workspace_id, str(row["id"]), [user_id])
|
|
39
|
+
if not _provision_agent_dm_runtimes(workspace_id, str(row["id"]), [user_id]):
|
|
40
|
+
ok = False
|
|
41
|
+
return ok
|
|
39
42
|
|
|
40
43
|
|
|
41
44
|
def _provision_agent_dm_runtimes(
|
|
42
45
|
workspace_id: str,
|
|
43
46
|
agent_profile_id: str,
|
|
44
47
|
user_ids: list[str],
|
|
45
|
-
) ->
|
|
48
|
+
) -> bool:
|
|
46
49
|
"""Create u-* runtime profiles when an agent DM room is created — install/onboarding only, not bind."""
|
|
47
50
|
workspace_id = str(workspace_id or "").strip()
|
|
48
51
|
agent_profile_id = str(agent_profile_id or "").strip()
|
|
49
52
|
if not workspace_id or not agent_profile_id:
|
|
50
|
-
return
|
|
53
|
+
return False
|
|
51
54
|
try:
|
|
52
55
|
conn = _srv()._workframe_db()
|
|
53
56
|
agent_row = _srv()._lookup_agent_profile(conn, workspace_id, agent_profile_id)
|
|
54
57
|
conn.close()
|
|
55
58
|
except Exception: # noqa: BLE001
|
|
56
|
-
return
|
|
59
|
+
return False
|
|
57
60
|
if not agent_row:
|
|
58
|
-
return
|
|
61
|
+
return False
|
|
59
62
|
template = str(agent_row["slug"] or "").strip()
|
|
60
63
|
if not template:
|
|
61
|
-
return
|
|
64
|
+
return False
|
|
62
65
|
try:
|
|
63
66
|
template = _srv().resolve_validated_profile(template)
|
|
64
67
|
except ValueError as exc:
|
|
@@ -66,7 +69,8 @@ def _provision_agent_dm_runtimes(
|
|
|
66
69
|
f"[provision_agent_dm_runtimes] skip {agent_profile_id}: invalid template {template!r}: {exc}",
|
|
67
70
|
flush=True,
|
|
68
71
|
)
|
|
69
|
-
return
|
|
72
|
+
return False
|
|
73
|
+
ok = True
|
|
70
74
|
for uid in user_ids:
|
|
71
75
|
user = str(uid or "").strip()
|
|
72
76
|
if not user:
|
|
@@ -77,10 +81,12 @@ def _provision_agent_dm_runtimes(
|
|
|
77
81
|
try:
|
|
78
82
|
_srv().ensure_runtime_profile(runtime, template, user, workspace_id)
|
|
79
83
|
except Exception as exc: # noqa: BLE001
|
|
84
|
+
ok = False
|
|
80
85
|
print(
|
|
81
86
|
f"[provision_agent_dm_runtimes] failed {runtime} for {user}: {exc}",
|
|
82
87
|
flush=True,
|
|
83
88
|
)
|
|
89
|
+
return ok
|
|
84
90
|
|
|
85
91
|
|
|
86
92
|
def bootstrap_agent_dm_lane(
|
|
@@ -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-DSCcbFgV.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-CEWtaJIq.js";export{e as createArchitectureServices};
|