fleetmesh-node 0.2.0__py3-none-any.whl
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.
- fleetmesh_node-0.2.0.dist-info/METADATA +57 -0
- fleetmesh_node-0.2.0.dist-info/RECORD +338 -0
- fleetmesh_node-0.2.0.dist-info/WHEEL +5 -0
- fleetmesh_node-0.2.0.dist-info/entry_points.txt +2 -0
- fleetmesh_node-0.2.0.dist-info/licenses/LICENSE +121 -0
- fleetmesh_node-0.2.0.dist-info/top_level.txt +2 -0
- node/RELEASE +1 -0
- node/__init__.py +35 -0
- node/app/__init__.py +49 -0
- node/app/commands.py +265 -0
- node/app/config.py +131 -0
- node/app/control.py +670 -0
- node/app/core.py +867 -0
- node/app/plugins.py +76 -0
- node/app/router.py +83 -0
- node/app/socket.py +431 -0
- node/app/systemd.py +36 -0
- node/app/views.py +204 -0
- node/channels/__init__.py +378 -0
- node/channels/bus.py +331 -0
- node/channels/journal.py +46 -0
- node/commands/__init__.py +140 -0
- node/commands/adapter.py +210 -0
- node/commands/base.py +278 -0
- node/commands/cluster_cmds.py +183 -0
- node/commands/compute_cmds.py +401 -0
- node/commands/core.py +474 -0
- node/commands/gossip_cmds.py +106 -0
- node/commands/nsite_cmds.py +141 -0
- node/commands/peering.py +402 -0
- node/commands/plugin_cmds.py +135 -0
- node/commands/sidecar_cmds.py +120 -0
- node/commands/sync.py +388 -0
- node/commands/transport_cmds.py +451 -0
- node/commands/video_cmds.py +99 -0
- node/compartment/__init__.py +124 -0
- node/compartment/base.py +868 -0
- node/compartment/command.py +458 -0
- node/compartment/context.py +163 -0
- node/compartment/every.py +108 -0
- node/compartment/handler.py +131 -0
- node/compartment/lifecycle.py +460 -0
- node/compartment/lint.py +388 -0
- node/compartment/need.py +158 -0
- node/compartment/problem.py +521 -0
- node/compartment/refusals.py +120 -0
- node/compartment/registry.py +790 -0
- node/compartment/schema.py +290 -0
- node/compartment/sidecar.py +499 -0
- node/compartment/stores.py +662 -0
- node/compartment/wire.py +225 -0
- node/compartment/worker.py +61 -0
- node/config/__init__.py +43 -0
- node/config/base.py +116 -0
- node/config/change.py +441 -0
- node/config/changestore.py +201 -0
- node/config/compartments.py +1144 -0
- node/config/node.py +318 -0
- node/config/parameter.py +563 -0
- node/config/protocols.py +274 -0
- node/config/records.py +171 -0
- node/examples/__init__.py +1 -0
- node/examples/demo.py +226 -0
- node/examples/follower.py +46 -0
- node/interfaces.py +235 -0
- node/keystore/__init__.py +27 -0
- node/keystore/core.py +103 -0
- node/keystore/enclave.py +688 -0
- node/meshnode.py +111 -0
- node/metrics/__init__.py +396 -0
- node/metrics/adapters.py +102 -0
- node/metrics/face.py +110 -0
- node/planes/__init__.py +21 -0
- node/planes/identity/__init__.py +20 -0
- node/planes/identity/announce/__init__.py +9 -0
- node/planes/identity/announce/compartment.py +447 -0
- node/planes/identity/announce/types.py +26 -0
- node/planes/identity/discovery/__init__.py +9 -0
- node/planes/identity/discovery/compartment.py +522 -0
- node/planes/identity/discovery/types.py +13 -0
- node/planes/identity/linked/__init__.py +1 -0
- node/planes/identity/linked/compartment.py +270 -0
- node/planes/identity/linked/types.py +25 -0
- node/planes/identity/presence/__init__.py +7 -0
- node/planes/identity/presence/compartment.py +409 -0
- node/planes/identity/presence/types.py +14 -0
- node/planes/operations/__init__.py +13 -0
- node/planes/operations/scheduler/__init__.py +9 -0
- node/planes/operations/scheduler/compartment.py +353 -0
- node/planes/operations/scheduler/types.py +21 -0
- node/planes/operations/updates/__init__.py +17 -0
- node/planes/operations/updates/core.py +310 -0
- node/planes/operations/updates/store.py +79 -0
- node/planes/operations/updates/types.py +20 -0
- node/planes/peering/__init__.py +28 -0
- node/planes/peering/budget/__init__.py +43 -0
- node/planes/peering/budget/chain.py +43 -0
- node/planes/peering/budget/core.py +537 -0
- node/planes/peering/budget/evaluation.py +80 -0
- node/planes/peering/budget/meter.py +100 -0
- node/planes/peering/budget/receipts.py +439 -0
- node/planes/peering/budget/shape.py +101 -0
- node/planes/peering/budget/tick.py +110 -0
- node/planes/peering/budget/types.py +67 -0
- node/planes/peering/budget/windows.py +346 -0
- node/planes/peering/capacity/__init__.py +7 -0
- node/planes/peering/capacity/compartment.py +342 -0
- node/planes/peering/capacity/types.py +24 -0
- node/planes/peering/peering/__init__.py +25 -0
- node/planes/peering/peering/core.py +464 -0
- node/planes/peering/peering/envelope.py +142 -0
- node/planes/peering/peering/handshake.py +198 -0
- node/planes/peering/peering/shape.py +80 -0
- node/planes/peering/peering/types.py +39 -0
- node/planes/peering/reporter/__init__.py +42 -0
- node/planes/peering/reporter/constants.py +29 -0
- node/planes/peering/reporter/core.py +299 -0
- node/planes/peering/reporter/finding.py +74 -0
- node/planes/peering/reporter/findings.py +171 -0
- node/planes/peering/reporter/publish.py +285 -0
- node/planes/peering/reporter/reading.py +193 -0
- node/planes/peering/reporter/shape.py +86 -0
- node/planes/peering/reporter/types.py +50 -0
- node/planes/peering/strain/__init__.py +9 -0
- node/planes/peering/strain/compartment.py +204 -0
- node/planes/peering/strain/types.py +21 -0
- node/planes/peering/trust/__init__.py +9 -0
- node/planes/peering/trust/compartment.py +239 -0
- node/planes/peering/trust/types.py +30 -0
- node/planes/services/__init__.py +33 -0
- node/planes/services/compute/__init__.py +160 -0
- node/planes/services/compute/common.py +134 -0
- node/planes/services/compute/compartment.py +681 -0
- node/planes/services/compute/executors/__init__.py +121 -0
- node/planes/services/compute/executors/agent.py +840 -0
- node/planes/services/compute/executors/bacalhau.py +605 -0
- node/planes/services/compute/executors/base.py +127 -0
- node/planes/services/compute/executors/harness.py +439 -0
- node/planes/services/compute/executors/mcp.py +454 -0
- node/planes/services/compute/executors/media.py +898 -0
- node/planes/services/compute/executors/nondet.py +283 -0
- node/planes/services/compute/executors/providers.py +499 -0
- node/planes/services/compute/executors/registry.py +140 -0
- node/planes/services/compute/executors/wasm.py +244 -0
- node/planes/services/compute/executors/zk.py +604 -0
- node/planes/services/compute/jobs.py +227 -0
- node/planes/services/compute/load.py +180 -0
- node/planes/services/compute/market.py +433 -0
- node/planes/services/compute/sandbox.py +282 -0
- node/planes/services/compute/types.py +84 -0
- node/planes/services/compute_wire/__init__.py +58 -0
- node/planes/services/compute_wire/compartment.py +256 -0
- node/planes/services/compute_wire/messages.py +320 -0
- node/planes/services/compute_wire/sealing.py +147 -0
- node/planes/services/compute_wire/session.py +572 -0
- node/planes/services/compute_wire/types.py +49 -0
- node/planes/services/compute_wire/verify.py +331 -0
- node/planes/services/dvm/__init__.py +24 -0
- node/planes/services/dvm/gateway.py +526 -0
- node/planes/services/dvm/protocol.py +35 -0
- node/planes/services/dvm/translate.py +15 -0
- node/planes/services/dvm/types.py +25 -0
- node/planes/services/gateway/__init__.py +40 -0
- node/planes/services/gateway/settle.py +279 -0
- node/planes/services/gateway/translate.py +644 -0
- node/planes/services/gossip/__init__.py +22 -0
- node/planes/services/gossip/core.py +929 -0
- node/planes/services/gossip/envelope.py +154 -0
- node/planes/services/gossip/mesh.py +127 -0
- node/planes/services/gossip/types.py +50 -0
- node/planes/services/nsite/__init__.py +7 -0
- node/planes/services/nsite/client.py +62 -0
- node/planes/services/nsite/compartment.py +752 -0
- node/planes/services/nsite/types.py +44 -0
- node/planes/services/paidfetch/__init__.py +7 -0
- node/planes/services/paidfetch/compartment.py +862 -0
- node/planes/services/paidfetch/types.py +99 -0
- node/planes/services/settle/__init__.py +27 -0
- node/planes/services/settle/compartment.py +384 -0
- node/planes/services/settle/engine.py +456 -0
- node/planes/services/settle/gateways.py +541 -0
- node/planes/services/settle/payment.py +512 -0
- node/planes/services/settle/store.py +74 -0
- node/planes/services/settle/types.py +58 -0
- node/planes/services/sidecars/__init__.py +8 -0
- node/planes/services/sidecars/compartment.py +461 -0
- node/planes/services/sidecars/manifest.py +304 -0
- node/planes/services/sidecars/manifests/10-grasp.json +37 -0
- node/planes/services/sidecars/manifests/20-ncontainer.json +31 -0
- node/planes/services/sidecars/manifests/30-magpie.json +28 -0
- node/planes/services/sidecars/types.py +43 -0
- node/planes/services/video/__init__.py +106 -0
- node/planes/services/video/barter.py +407 -0
- node/planes/services/video/cli.py +158 -0
- node/planes/services/video/compartment.py +932 -0
- node/planes/services/video/ingest.py +524 -0
- node/planes/services/video/ledger.py +705 -0
- node/planes/services/video/packager.py +847 -0
- node/planes/services/video/player.py +456 -0
- node/planes/services/video/timed_segments.py +124 -0
- node/planes/services/video/transit.py +478 -0
- node/planes/store/__init__.py +24 -0
- node/planes/store/blob_replication/__init__.py +7 -0
- node/planes/store/blob_replication/compartment.py +496 -0
- node/planes/store/blob_replication/types.py +35 -0
- node/planes/store/blob_wire/__init__.py +48 -0
- node/planes/store/blob_wire/client.py +57 -0
- node/planes/store/blob_wire/core.py +359 -0
- node/planes/store/blob_wire/intake.py +74 -0
- node/planes/store/blob_wire/messages.py +85 -0
- node/planes/store/projected/__init__.py +7 -0
- node/planes/store/projected/compartment.py +258 -0
- node/planes/store/projected/types.py +29 -0
- node/planes/store/retention/__init__.py +7 -0
- node/planes/store/retention/compartment.py +214 -0
- node/planes/store/retention/types.py +12 -0
- node/planes/store/sync/__init__.py +35 -0
- node/planes/store/sync/core.py +311 -0
- node/planes/store/sync/envelope.py +106 -0
- node/planes/store/sync/handover.py +206 -0
- node/planes/store/sync/initiator.py +128 -0
- node/planes/store/sync/responder.py +114 -0
- node/planes/store/sync/rules.py +179 -0
- node/planes/store/sync/shape.py +80 -0
- node/planes/store/sync/types.py +51 -0
- node/planes/transport/__init__.py +23 -0
- node/planes/transport/bridge/__init__.py +13 -0
- node/planes/transport/bridge/compartment.py +785 -0
- node/planes/transport/bridge/ratelimit.py +44 -0
- node/planes/transport/bridge/types.py +43 -0
- node/planes/transport/lan/__init__.py +9 -0
- node/planes/transport/lan/compartment.py +446 -0
- node/planes/transport/lan/types.py +25 -0
- node/planes/transport/link/__init__.py +7 -0
- node/planes/transport/link/compartment.py +257 -0
- node/planes/transport/link/types.py +27 -0
- node/planes/transport/mediation/__init__.py +45 -0
- node/planes/transport/mediation/compartment.py +238 -0
- node/planes/transport/mediation/leaf.py +107 -0
- node/planes/transport/mediation/messages.py +302 -0
- node/planes/transport/mediation/service.py +233 -0
- node/planes/transport/mediation/store.py +37 -0
- node/planes/transport/mediation/types.py +20 -0
- node/planes/transport/p2p/__init__.py +7 -0
- node/planes/transport/p2p/compartment.py +349 -0
- node/planes/transport/p2p/types.py +22 -0
- node/protocols/__init__.py +277 -0
- node/protocols/activitypub/__init__.py +56 -0
- node/protocols/activitypub/actor.py +230 -0
- node/protocols/activitypub/federation.py +490 -0
- node/protocols/activitypub/media.py +145 -0
- node/protocols/activitypub/models.py +286 -0
- node/protocols/activitypub/package.py +271 -0
- node/protocols/activitypub/store.py +564 -0
- node/protocols/arcade/__init__.py +26 -0
- node/protocols/atproto/__init__.py +13 -0
- node/protocols/atproto/firehose.py +126 -0
- node/protocols/atproto/metering.py +165 -0
- node/protocols/atproto/package.py +155 -0
- node/protocols/atproto/repo.py +250 -0
- node/protocols/atproto/xrpc.py +285 -0
- node/protocols/base.py +109 -0
- node/protocols/circles/__init__.py +29 -0
- node/protocols/dm/__init__.py +50 -0
- node/protocols/grasp/__init__.py +22 -0
- node/protocols/isotime.py +15 -0
- node/protocols/nostr/__init__.py +51 -0
- node/protocols/nostr/blobstore.py +383 -0
- node/protocols/nostr/blossom.py +627 -0
- node/protocols/nostr/client.py +377 -0
- node/protocols/nostr/descriptor.py +211 -0
- node/protocols/nostr/eventstore.py +304 -0
- node/protocols/nostr/package.py +182 -0
- node/protocols/nostr/peers.py +109 -0
- node/protocols/nostr/relayserver.py +656 -0
- node/protocols/p2panda/__init__.py +22 -0
- node/protocols/p2panda/identity.py +19 -0
- node/protocols/p2panda/package.py +236 -0
- node/protocols/p2panda/rung.py +152 -0
- node/protocols/p2panda/store.py +273 -0
- node/protocols/rhio/__init__.py +33 -0
- node/protocols/rhio/package.py +275 -0
- node/protocols/rhio/rung.py +136 -0
- node/protocols/rhio/store.py +1423 -0
- node/protocols/ssb/muxrpc.py +256 -0
- node/protocols/ssb/package.py +267 -0
- node/protocols/textile/__init__.py +111 -0
- node/protocols/textile/transducers.py +259 -0
- node/release.py +119 -0
- node/storage/__init__.py +88 -0
- node/storage/accounts.py +305 -0
- node/storage/adapter.py +161 -0
- node/storage/base.py +385 -0
- node/storage/dialect.py +209 -0
- node/storage/grants.py +317 -0
- node/storage/locator.py +150 -0
- node/storage/postgres.py +294 -0
- node/storage/projected.py +953 -0
- node/storage/sharding.py +448 -0
- node/storage/standing.py +568 -0
- node/testnet/__init__.py +9 -0
- node/testnet/cluster.py +731 -0
- node/testnet/fips_daemon.py +230 -0
- node/transport/__init__.py +94 -0
- node/transport/const.py +110 -0
- node/transport/didcomm_inbound.py +251 -0
- node/transport/dnstxt.py +169 -0
- node/transport/fips_rung/__init__.py +67 -0
- node/transport/fips_rung/core.py +666 -0
- node/transport/fips_rung/listener.py +96 -0
- node/transport/fips_rung/types.py +12 -0
- node/transport/host.py +334 -0
- node/transport/iroh_rung/__init__.py +29 -0
- node/transport/iroh_rung/core.py +477 -0
- node/transport/iroh_rung/quic.py +942 -0
- node/transport/iroh_rung/types.py +22 -0
- node/transport/ladder.py +145 -0
- node/transport/lineproto.py +49 -0
- node/transport/mdns.py +303 -0
- node/transport/netfetch.py +283 -0
- node/transport/relay_rung/__init__.py +23 -0
- node/transport/relay_rung/cooldown.py +87 -0
- node/transport/relay_rung/core.py +138 -0
- node/transport/relay_rung/inbound.py +161 -0
- node/transport/relay_rung/ledger.py +104 -0
- node/transport/relay_rung/listen.py +121 -0
- node/transport/relay_rung/outbound.py +181 -0
- node/transport/relay_rung/shape.py +71 -0
- node/transport/relay_rung/types.py +58 -0
- node/transport/relay_rung/wire.py +63 -0
- node/transport/router.py +761 -0
- node/transport/rung.py +276 -0
- node/transport/ssb_rung.py +166 -0
- node/transport/ws.py +502 -0
- sidecar/fleetmesh_announce.py +338 -0
- sidecar/fleetmesh_p2p.py +373 -0
- sidecar/fleetmesh_rhio.py +352 -0
- sidecar/fleetmesh_ssb.py +603 -0
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"""Executor contract: quote and result types, the ExecutorProtocol and the ComputeExecutor base.
|
|
2
|
+
|
|
3
|
+
Every runtime driver subclasses ComputeExecutor; nothing here runs anything.
|
|
4
|
+
"""
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import abc
|
|
8
|
+
from typing import Any, Mapping, Optional, Protocol
|
|
9
|
+
|
|
10
|
+
from ..sandbox import ComputeLimits
|
|
11
|
+
from ..types import JobRunResult
|
|
12
|
+
|
|
13
|
+
__all__ = [
|
|
14
|
+
'ComputeQuote',
|
|
15
|
+
'ComputeExecutionResult',
|
|
16
|
+
'ExecutorProtocol',
|
|
17
|
+
'JobRunResult',
|
|
18
|
+
'ComputeExecutor',
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
class ComputeQuote:
|
|
22
|
+
"""Deterministic or non-deterministic price and fuel quote."""
|
|
23
|
+
|
|
24
|
+
def __init__(
|
|
25
|
+
self,
|
|
26
|
+
fuel_estimate: int,
|
|
27
|
+
rate_unit: str,
|
|
28
|
+
rate_amount: int,
|
|
29
|
+
engine_id: str,
|
|
30
|
+
) -> None:
|
|
31
|
+
self.fuel_estimate = fuel_estimate
|
|
32
|
+
self.rate_unit = rate_unit
|
|
33
|
+
self.rate_amount = rate_amount
|
|
34
|
+
self.engine_id = engine_id
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class ComputeExecutionResult:
|
|
38
|
+
"""Outcome of a completed deterministic or non-deterministic compute job."""
|
|
39
|
+
|
|
40
|
+
def __init__(
|
|
41
|
+
self,
|
|
42
|
+
result_hash: str,
|
|
43
|
+
raw_output: bytes,
|
|
44
|
+
fuel_used: int,
|
|
45
|
+
engine_id: str,
|
|
46
|
+
wasi_profile_digest: str = '',
|
|
47
|
+
counters: Optional[Mapping[str, int | float]] = None,
|
|
48
|
+
verification_mode: str = 'delivery',
|
|
49
|
+
) -> None:
|
|
50
|
+
self.result_hash = result_hash
|
|
51
|
+
self.raw_output = raw_output
|
|
52
|
+
self.fuel_used = fuel_used
|
|
53
|
+
self.engine_id = engine_id
|
|
54
|
+
self.wasi_profile_digest = wasi_profile_digest
|
|
55
|
+
self.counters: dict[str, int | float] = dict(counters or {})
|
|
56
|
+
self.verification_mode = verification_mode
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class ExecutorProtocol(Protocol):
|
|
60
|
+
"""Structural interface the registry requires of an execution runtime.
|
|
61
|
+
|
|
62
|
+
Third-party runtimes can be registered without inheriting ComputeExecutor
|
|
63
|
+
as long as they carry an engine id, a WASI profile digest (empty when the
|
|
64
|
+
engine is not WASM) and the quote/execute pair. ComputeExecutor is the
|
|
65
|
+
in-tree base that satisfies it.
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
engine_id: str
|
|
69
|
+
wasi_profile_digest: str
|
|
70
|
+
|
|
71
|
+
def quote(
|
|
72
|
+
self,
|
|
73
|
+
module_spec: Mapping[str, Any],
|
|
74
|
+
input_spec: Mapping[str, Any],
|
|
75
|
+
limits: ComputeLimits,
|
|
76
|
+
) -> ComputeQuote: ...
|
|
77
|
+
|
|
78
|
+
def execute(
|
|
79
|
+
self,
|
|
80
|
+
job_payload: Mapping[str, Any],
|
|
81
|
+
fuel_cap: int,
|
|
82
|
+
) -> ComputeExecutionResult: ...
|
|
83
|
+
|
|
84
|
+
def is_live_runnable(self) -> bool: ...
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class ComputeExecutor(abc.ABC):
|
|
88
|
+
"""Abstract driver trait for modular compute runtimes."""
|
|
89
|
+
|
|
90
|
+
engine_id: str
|
|
91
|
+
wasi_profile_digest: str
|
|
92
|
+
|
|
93
|
+
def is_live_runnable(self) -> bool:
|
|
94
|
+
"""Check whether this runtime driver can service live execution requests on this host."""
|
|
95
|
+
return True
|
|
96
|
+
|
|
97
|
+
@abc.abstractmethod
|
|
98
|
+
def quote(
|
|
99
|
+
self,
|
|
100
|
+
module_spec: Mapping[str, Any],
|
|
101
|
+
input_spec: Mapping[str, Any],
|
|
102
|
+
limits: ComputeLimits,
|
|
103
|
+
) -> ComputeQuote:
|
|
104
|
+
"""Estimate fuel and return price quote."""
|
|
105
|
+
pass
|
|
106
|
+
|
|
107
|
+
@abc.abstractmethod
|
|
108
|
+
def execute(
|
|
109
|
+
self,
|
|
110
|
+
job_payload: Mapping[str, Any],
|
|
111
|
+
fuel_cap: int,
|
|
112
|
+
) -> ComputeExecutionResult:
|
|
113
|
+
"""Execute a verified module with metered fuel limits."""
|
|
114
|
+
pass
|
|
115
|
+
|
|
116
|
+
@staticmethod
|
|
117
|
+
def verification_mode(job_payload: Mapping[str, Any], default: str = 'delivery') -> str:
|
|
118
|
+
"""Read the requested verification mode from a job payload.
|
|
119
|
+
|
|
120
|
+
Accepts ``{'verification': {'mode': ...}}``, the ``verify`` alias, or
|
|
121
|
+
a bare string. Hoisted here because three executors used to carry
|
|
122
|
+
identical copies of this parsing, which is how they drift apart.
|
|
123
|
+
"""
|
|
124
|
+
verification = job_payload.get('verification') or job_payload.get('verify') or {}
|
|
125
|
+
if isinstance(verification, Mapping):
|
|
126
|
+
return str(verification.get('mode', default))
|
|
127
|
+
return str(verification) if verification else default
|
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
"""Standardized Modular Agent Harness for FleetMesh Non-Deterministic Compute.
|
|
2
|
+
|
|
3
|
+
Provides a reusable, composable specification for AI agents running on FleetMesh:
|
|
4
|
+
- `AgentSkillModule`: Modular capability unit (`inference`, `discover`, `scrobbles`,
|
|
5
|
+
`subtitles_cinema`, `studio_create`, `vault_hygiene`, or custom application skills).
|
|
6
|
+
- `AgentProfileSpec`: Complete personality, expertise, grounding mode, tool/skill bindings,
|
|
7
|
+
and inference tuning parameters for built-in or user-created bots.
|
|
8
|
+
- `AgentHarnessRegistry`: Manages the built-in roster (`Concierge`, `Eddy`, `Mojo`,
|
|
9
|
+
`Splice`, `Myco`, `Rhizo`) and custom user/operator-created agents, and compiles
|
|
10
|
+
standardized system prompts and tool filters for `LlmExecutor` and `AgentExecutor`.
|
|
11
|
+
"""
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import json
|
|
15
|
+
from dataclasses import dataclass
|
|
16
|
+
from typing import Any, Mapping, Optional, Sequence
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass(frozen=True)
|
|
20
|
+
class AgentSkillModule:
|
|
21
|
+
"""Modular capability pack that can be attached to an agent profile."""
|
|
22
|
+
|
|
23
|
+
module_id: str
|
|
24
|
+
label: str
|
|
25
|
+
description: str
|
|
26
|
+
default_tools: tuple[str, ...] = ()
|
|
27
|
+
action_types: tuple[str, ...] = ()
|
|
28
|
+
|
|
29
|
+
def to_dict(self) -> dict[str, Any]:
|
|
30
|
+
return {
|
|
31
|
+
'module_id': self.module_id,
|
|
32
|
+
'label': self.label,
|
|
33
|
+
'description': self.description,
|
|
34
|
+
'default_tools': list(self.default_tools),
|
|
35
|
+
'action_types': list(self.action_types),
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
HARNESS_SKILL_MODULES: dict[str, AgentSkillModule] = {
|
|
40
|
+
'inference': AgentSkillModule(
|
|
41
|
+
module_id='inference',
|
|
42
|
+
label='General Inference & Reasoning',
|
|
43
|
+
description='Direct LLM inference, open-ended Q&A, research synthesis, coding, writing, and math calculation.',
|
|
44
|
+
default_tools=('calc', 'sha256', 'regex_search', 'finish'),
|
|
45
|
+
action_types=(),
|
|
46
|
+
),
|
|
47
|
+
'discover': AgentSkillModule(
|
|
48
|
+
module_id='discover',
|
|
49
|
+
label='Mesh Discovery & Unseen Radar',
|
|
50
|
+
description='Surfaces unseen uploads from subscriptions, catalog highlights, favorites, and peer recommendations.',
|
|
51
|
+
default_tools=('fetch_blob', 'regex_search', 'finish'),
|
|
52
|
+
action_types=('watch_vod', 'send_peer_recommendation'),
|
|
53
|
+
),
|
|
54
|
+
'scrobbles': AgentSkillModule(
|
|
55
|
+
module_id='scrobbles',
|
|
56
|
+
label='Scrobble Analytics & Privacy Hygiene',
|
|
57
|
+
description='Analyzes 75%-rule watch history patterns, category breakdowns, and NIP-09 Kind 5 private tombstones.',
|
|
58
|
+
default_tools=('calc', 'finish'),
|
|
59
|
+
action_types=('tombstone_scrobble', 'create_social_strand'),
|
|
60
|
+
),
|
|
61
|
+
'subtitles_cinema': AgentSkillModule(
|
|
62
|
+
module_id='subtitles_cinema',
|
|
63
|
+
label='Subtitles & Movie Studio',
|
|
64
|
+
description='Generates/cleans synced WebVTT (.vtt) subtitles, extracts embedded tracks, and enriches cinema metadata & chapters.',
|
|
65
|
+
default_tools=('fetch_blob', 'regex_search', 'finish'),
|
|
66
|
+
action_types=('attach_cleaned_subtitles', 'extract_embedded_tracks', 'apply_movie_metadata'),
|
|
67
|
+
),
|
|
68
|
+
'studio_create': AgentSkillModule(
|
|
69
|
+
module_id='studio_create',
|
|
70
|
+
label='Creator Co-Pilot & Social Strands',
|
|
71
|
+
description='Co-pilots Upload Wizard titles, descriptions, hashtags, and authors Kind 30897 Social Strands.',
|
|
72
|
+
default_tools=('sha256', 'finish'),
|
|
73
|
+
action_types=('apply_vod_metadata', 'create_social_strand'),
|
|
74
|
+
),
|
|
75
|
+
'vault_hygiene': AgentSkillModule(
|
|
76
|
+
module_id='vault_hygiene',
|
|
77
|
+
label='Sovereign Vault & CAS Cleanup',
|
|
78
|
+
description='Audits Sovereign Vault storage quotas, prunes orphaned Rhio CAS blobs, and inspects sub-ask federation.',
|
|
79
|
+
default_tools=('fetch_blob', 'sha256', 'sub_ask', 'finish'),
|
|
80
|
+
action_types=('cleanup_vault_and_storage',),
|
|
81
|
+
),
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
@dataclass
|
|
86
|
+
class AgentProfileSpec:
|
|
87
|
+
"""Modular specification for a built-in or user-created AI agent."""
|
|
88
|
+
|
|
89
|
+
agent_id: str
|
|
90
|
+
name: str
|
|
91
|
+
role: str
|
|
92
|
+
call_sign: str
|
|
93
|
+
tagline: str
|
|
94
|
+
style: str
|
|
95
|
+
expertise: tuple[str, ...] = ()
|
|
96
|
+
skill_modules: tuple[str, ...] = ('inference',)
|
|
97
|
+
grounding_mode: str = 'adaptive' # 'adaptive' | 'always_grounded' | 'pure_inference'
|
|
98
|
+
system_prompt: str = ''
|
|
99
|
+
default_temperature: float = 0.4
|
|
100
|
+
preferred_provider: str = ''
|
|
101
|
+
preferred_model: str = ''
|
|
102
|
+
accent_color: str = '#38bdf8'
|
|
103
|
+
badge_icon: str = '🌊'
|
|
104
|
+
is_builtin: bool = False
|
|
105
|
+
owner_id: str = 'system'
|
|
106
|
+
visibility: str = 'public'
|
|
107
|
+
|
|
108
|
+
def allowed_tool_names(self) -> set[str]:
|
|
109
|
+
"""Compute the union of FleetMesh ReAct tool names enabled by this agent's skill modules."""
|
|
110
|
+
names: set[str] = {'finish'}
|
|
111
|
+
for mod_id in self.skill_modules:
|
|
112
|
+
mod = HARNESS_SKILL_MODULES.get(mod_id)
|
|
113
|
+
if mod is not None:
|
|
114
|
+
names.update(mod.default_tools)
|
|
115
|
+
return names
|
|
116
|
+
|
|
117
|
+
def allowed_action_types(self) -> set[str]:
|
|
118
|
+
"""Compute the union of Propose & Confirm action card types enabled by this agent's skill modules."""
|
|
119
|
+
actions: set[str] = set()
|
|
120
|
+
for mod_id in self.skill_modules:
|
|
121
|
+
mod = HARNESS_SKILL_MODULES.get(mod_id)
|
|
122
|
+
if mod is not None:
|
|
123
|
+
actions.update(mod.action_types)
|
|
124
|
+
return actions
|
|
125
|
+
|
|
126
|
+
def compile_system_prompt(
|
|
127
|
+
self,
|
|
128
|
+
*,
|
|
129
|
+
domain_context: Optional[Mapping[str, Any]] = None,
|
|
130
|
+
extra_instructions: str = '',
|
|
131
|
+
) -> str:
|
|
132
|
+
"""Build the standardized modular system prompt for `LlmExecutor` or `AgentExecutor`."""
|
|
133
|
+
active_mods = [
|
|
134
|
+
HARNESS_SKILL_MODULES[m]
|
|
135
|
+
for m in self.skill_modules
|
|
136
|
+
if m in HARNESS_SKILL_MODULES
|
|
137
|
+
]
|
|
138
|
+
mod_lines = [f"- [{m.module_id}] {m.label}: {m.description}" for m in active_mods]
|
|
139
|
+
exp_str = ', '.join(self.expertise) if self.expertise else 'General Mesh Intelligence'
|
|
140
|
+
|
|
141
|
+
sections = [
|
|
142
|
+
f"You are {self.name} ({self.call_sign}) — {self.role}.",
|
|
143
|
+
f"Tagline: {self.tagline}",
|
|
144
|
+
f"Personality & Communication Style: {self.style}",
|
|
145
|
+
f"Core Expertise: {exp_str}",
|
|
146
|
+
]
|
|
147
|
+
if mod_lines:
|
|
148
|
+
sections.append("Enabled Harness Skill Modules:\n" + "\n".join(mod_lines))
|
|
149
|
+
if self.system_prompt.strip():
|
|
150
|
+
sections.append(f"Agent Directives:\n{self.system_prompt.strip()}")
|
|
151
|
+
if extra_instructions.strip():
|
|
152
|
+
sections.append(extra_instructions.strip())
|
|
153
|
+
|
|
154
|
+
# Include grounded node context based on grounding_mode
|
|
155
|
+
if domain_context and self.grounding_mode != 'pure_inference':
|
|
156
|
+
sections.append(
|
|
157
|
+
"Grounded Node Context (JSON):\n" + json.dumps(dict(domain_context), sort_keys=True)
|
|
158
|
+
)
|
|
159
|
+
elif self.grounding_mode == 'pure_inference':
|
|
160
|
+
sections.append(
|
|
161
|
+
"Operating Mode: Pure Inference Engine. Focus directly on answering the user's prompt "
|
|
162
|
+
"with high precision, clarity, and analytical depth without injecting unrequested media catalog actions."
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
return "\n\n".join(sections)
|
|
166
|
+
|
|
167
|
+
def to_dict(self) -> dict[str, Any]:
|
|
168
|
+
return {
|
|
169
|
+
'agent_id': self.agent_id,
|
|
170
|
+
'name': self.name,
|
|
171
|
+
'role': self.role,
|
|
172
|
+
'call_sign': self.call_sign,
|
|
173
|
+
'tagline': self.tagline,
|
|
174
|
+
'style': self.style,
|
|
175
|
+
'expertise': list(self.expertise),
|
|
176
|
+
'skill_modules': list(self.skill_modules),
|
|
177
|
+
'grounding_mode': self.grounding_mode,
|
|
178
|
+
'system_prompt': self.system_prompt,
|
|
179
|
+
'default_temperature': float(self.default_temperature),
|
|
180
|
+
'preferred_provider': self.preferred_provider,
|
|
181
|
+
'preferred_model': self.preferred_model,
|
|
182
|
+
'accent_color': self.accent_color,
|
|
183
|
+
'badge_icon': self.badge_icon,
|
|
184
|
+
'is_builtin': bool(self.is_builtin),
|
|
185
|
+
'owner_id': self.owner_id,
|
|
186
|
+
'visibility': self.visibility,
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
@classmethod
|
|
190
|
+
def from_mapping(cls, data: Mapping[str, Any]) -> AgentProfileSpec:
|
|
191
|
+
raw_id = str(data.get('agent_id') or data.get('name') or 'eddy').strip().lower()
|
|
192
|
+
agent_id = ''.join(ch if (ch.isalnum() or ch in ('-', '_')) else '-' for ch in raw_id).strip('-') or 'custom-bot'
|
|
193
|
+
name = str(data.get('name') or agent_id.capitalize()).strip()
|
|
194
|
+
role = str(data.get('role') or 'Custom Mesh Companion').strip()
|
|
195
|
+
call_sign = str(data.get('call_sign') or f"{name.upper()} // MESH-BOT").strip()
|
|
196
|
+
tagline = str(data.get('tagline') or f"{name} — {role}").strip()
|
|
197
|
+
style = str(data.get('style') or data.get('vibe') or 'Helpful, sovereign mesh assistant.').strip()
|
|
198
|
+
raw_exp = data.get('expertise')
|
|
199
|
+
if isinstance(raw_exp, str):
|
|
200
|
+
expertise = tuple(s.strip() for s in raw_exp.split(',') if s.strip())
|
|
201
|
+
elif isinstance(raw_exp, Sequence):
|
|
202
|
+
expertise = tuple(str(s).strip() for s in raw_exp if str(s).strip())
|
|
203
|
+
else:
|
|
204
|
+
expertise = ('Custom Mesh Workflows',)
|
|
205
|
+
|
|
206
|
+
raw_mods = data.get('skill_modules')
|
|
207
|
+
if isinstance(raw_mods, str):
|
|
208
|
+
mods = tuple(s.strip() for s in raw_mods.split(',') if s.strip() in HARNESS_SKILL_MODULES)
|
|
209
|
+
elif isinstance(raw_mods, Sequence):
|
|
210
|
+
mods = tuple(str(s).strip() for s in raw_mods if str(s).strip() in HARNESS_SKILL_MODULES)
|
|
211
|
+
else:
|
|
212
|
+
mods = ('inference', 'discover')
|
|
213
|
+
if not mods:
|
|
214
|
+
mods = ('inference',)
|
|
215
|
+
|
|
216
|
+
g_mode = str(data.get('grounding_mode') or 'adaptive').strip().lower()
|
|
217
|
+
if g_mode not in ('adaptive', 'always_grounded', 'pure_inference'):
|
|
218
|
+
g_mode = 'adaptive'
|
|
219
|
+
|
|
220
|
+
return cls(
|
|
221
|
+
agent_id=agent_id,
|
|
222
|
+
name=name,
|
|
223
|
+
role=role,
|
|
224
|
+
call_sign=call_sign,
|
|
225
|
+
tagline=tagline,
|
|
226
|
+
style=style,
|
|
227
|
+
expertise=expertise,
|
|
228
|
+
skill_modules=mods,
|
|
229
|
+
grounding_mode=g_mode,
|
|
230
|
+
system_prompt=str(data.get('system_prompt') or '').strip(),
|
|
231
|
+
default_temperature=float(data.get('default_temperature', 0.4)),
|
|
232
|
+
preferred_provider=str(data.get('preferred_provider') or '').strip().lower(),
|
|
233
|
+
preferred_model=str(data.get('preferred_model') or '').strip(),
|
|
234
|
+
accent_color=str(data.get('accent_color') or '#38bdf8').strip(),
|
|
235
|
+
badge_icon=str(data.get('badge_icon') or '🤖').strip(),
|
|
236
|
+
is_builtin=bool(data.get('is_builtin', False)),
|
|
237
|
+
owner_id=str(data.get('owner_id') or 'system').strip(),
|
|
238
|
+
visibility=str(data.get('visibility') or 'public').strip(),
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
BUILTIN_AGENT_PROFILES: dict[str, AgentProfileSpec] = {
|
|
243
|
+
'concierge': AgentProfileSpec(
|
|
244
|
+
agent_id='concierge',
|
|
245
|
+
name='Concierge',
|
|
246
|
+
role='General-Purpose Inference & Mesh Concierge',
|
|
247
|
+
call_sign='CONCIERGE // MESH-INFERENCE',
|
|
248
|
+
tagline='Sovereign General-Purpose Inference & Executive Mesh Concierge',
|
|
249
|
+
style=(
|
|
250
|
+
'Crisp, articulate, analytical, and zero-fluff. Operates as a direct general-purpose '
|
|
251
|
+
'inference engine for reasoning, Q&A, coding, writing, and analysis, while seamlessly '
|
|
252
|
+
'orchestrating any MeshTube workflow on command.'
|
|
253
|
+
),
|
|
254
|
+
expertise=(
|
|
255
|
+
'Direct LLM Inference & Reasoning',
|
|
256
|
+
'Research, Writing & Code Synthesis',
|
|
257
|
+
'Full-Spectrum MeshTube Orchestration',
|
|
258
|
+
),
|
|
259
|
+
skill_modules=('inference', 'discover', 'scrobbles', 'subtitles_cinema', 'studio_create', 'vault_hygiene'),
|
|
260
|
+
grounding_mode='adaptive',
|
|
261
|
+
system_prompt=(
|
|
262
|
+
'When the user asks a general knowledge, analytical, coding, or writing question, answer directly '
|
|
263
|
+
'and thoroughly as an inference engine. When the user asks about videos, subtitles, scrobbles, '
|
|
264
|
+
'uploads, or vault storage, ground your response in the node context and propose concrete actions.'
|
|
265
|
+
),
|
|
266
|
+
default_temperature=0.25,
|
|
267
|
+
accent_color='#6366f1',
|
|
268
|
+
badge_icon='🛎️',
|
|
269
|
+
is_builtin=True,
|
|
270
|
+
),
|
|
271
|
+
'eddy': AgentProfileSpec(
|
|
272
|
+
agent_id='eddy',
|
|
273
|
+
name='Eddy',
|
|
274
|
+
role='Discovery & Current Navigator',
|
|
275
|
+
call_sign='EDDY // CURRENT-NAV',
|
|
276
|
+
tagline='Swirling Ocean Current & Unseen Content Navigator',
|
|
277
|
+
style=(
|
|
278
|
+
'Warm, laid-back coastal current guide connecting tides across FleetMesh’s Three Sails. '
|
|
279
|
+
'Speaks with smooth wave flow and has an uncanny radar for unseen gems and scrobble grooves.'
|
|
280
|
+
),
|
|
281
|
+
expertise=(
|
|
282
|
+
'Unseen Subscription Radar',
|
|
283
|
+
'Scrobble Vibe & Groove Mapping',
|
|
284
|
+
'Peer Crate Recommendations',
|
|
285
|
+
),
|
|
286
|
+
skill_modules=('discover', 'scrobbles', 'inference'),
|
|
287
|
+
grounding_mode='always_grounded',
|
|
288
|
+
system_prompt=(
|
|
289
|
+
'Prioritize surfacing unseen uploads from subscribed channels, highlighting catalog gems that match '
|
|
290
|
+
'the viewer’s 75%-rule scrobble history, and suggesting peer recommendations.'
|
|
291
|
+
),
|
|
292
|
+
default_temperature=0.45,
|
|
293
|
+
accent_color='#38bdf8',
|
|
294
|
+
badge_icon='🌊',
|
|
295
|
+
is_builtin=True,
|
|
296
|
+
),
|
|
297
|
+
'mojo': AgentProfileSpec(
|
|
298
|
+
agent_id='mojo',
|
|
299
|
+
name='Mojo',
|
|
300
|
+
role='Analog Studio Producer & Crate-Digger',
|
|
301
|
+
call_sign='MOJO // GROOVE-DESK',
|
|
302
|
+
tagline='Soulful Studio Producer, Upload Co-Pilot & Crate-Digger',
|
|
303
|
+
style=(
|
|
304
|
+
'Soulful New Orleans analog funk producer at the mixing console. Energetic, creative, and '
|
|
305
|
+
'studio-savvy—loves crafting magnetic titles, synopses, chapter cuts, and Social Strands.'
|
|
306
|
+
),
|
|
307
|
+
expertise=(
|
|
308
|
+
'Upload Wizard Metadata Co-Pilot',
|
|
309
|
+
'Movie Studio Synopsis & Chapter Cuts',
|
|
310
|
+
'Kind 30897 Social Strands Authoring',
|
|
311
|
+
),
|
|
312
|
+
skill_modules=('studio_create', 'subtitles_cinema', 'discover', 'inference'),
|
|
313
|
+
grounding_mode='always_grounded',
|
|
314
|
+
system_prompt=(
|
|
315
|
+
'Act as a hands-on creative producer. Help creators punch up VoD titles, descriptions, hashtags, '
|
|
316
|
+
'movie studio metadata, chapter markers, and high-signal Social Strands.'
|
|
317
|
+
),
|
|
318
|
+
default_temperature=0.6,
|
|
319
|
+
accent_color='#f59e0b',
|
|
320
|
+
badge_icon='🎛️',
|
|
321
|
+
is_builtin=True,
|
|
322
|
+
),
|
|
323
|
+
'splice': AgentProfileSpec(
|
|
324
|
+
agent_id='splice',
|
|
325
|
+
name='Splice',
|
|
326
|
+
role='Cinema Archivist & Subtitle Timing Specialist',
|
|
327
|
+
call_sign='SPLICE // TIMECODE-LAB',
|
|
328
|
+
tagline='Precision 35mm Editor, WebVTT Syncer & Cinema Archivist',
|
|
329
|
+
style=(
|
|
330
|
+
'Precision film editor and booth projectionist. Speaks with crisp timecode accuracy, '
|
|
331
|
+
'obsessed with clean WebVTT subtitle cues, embedded track extraction, and cinema metadata.'
|
|
332
|
+
),
|
|
333
|
+
expertise=(
|
|
334
|
+
'WebVTT (.vtt) Subtitle Sync & Cleanup',
|
|
335
|
+
'Embedded FFmpeg Track Extraction',
|
|
336
|
+
'Cinema Metadata & Chapter Timing',
|
|
337
|
+
),
|
|
338
|
+
skill_modules=('subtitles_cinema', 'studio_create', 'inference'),
|
|
339
|
+
grounding_mode='always_grounded',
|
|
340
|
+
system_prompt=(
|
|
341
|
+
'Focus on subtitle timing accuracy, WebVTT cue formatting, extracting embedded subtitle/audio tracks, '
|
|
342
|
+
'and structuring movie director, release year, genre, rating badge, and chapter timecodes.'
|
|
343
|
+
),
|
|
344
|
+
default_temperature=0.2,
|
|
345
|
+
accent_color='#f43f5e',
|
|
346
|
+
badge_icon='🎞️',
|
|
347
|
+
is_builtin=True,
|
|
348
|
+
),
|
|
349
|
+
'myco': AgentProfileSpec(
|
|
350
|
+
agent_id='myco',
|
|
351
|
+
name='Myco',
|
|
352
|
+
role='Sovereign Vault & Privacy Custodian',
|
|
353
|
+
call_sign='MYCO // SPORE-VAULT',
|
|
354
|
+
tagline='Mycelial Storage Custodian & Scrobble Privacy Guardian',
|
|
355
|
+
style=(
|
|
356
|
+
'Calm, ecological mycelial network steward focused on sovereign data hygiene, '
|
|
357
|
+
'zero-waste CAS storage pruning, and private NIP-09 Kind 5 scrobble tombstoning.'
|
|
358
|
+
),
|
|
359
|
+
expertise=(
|
|
360
|
+
'Orphaned Rhio CAS Blob Garbage Collection',
|
|
361
|
+
'NIP-09 Kind 5 Private Scrobble Tombstones',
|
|
362
|
+
'Sovereign Vault Quota & Storage Audits',
|
|
363
|
+
),
|
|
364
|
+
skill_modules=('vault_hygiene', 'scrobbles', 'inference'),
|
|
365
|
+
grounding_mode='always_grounded',
|
|
366
|
+
system_prompt=(
|
|
367
|
+
'Focus on storage health, reclaiming orphaned Rhio CAS blobs, auditing Sovereign Vault usage, '
|
|
368
|
+
'and protecting viewer privacy through NIP-09 Kind 5 scrobble tombstones.'
|
|
369
|
+
),
|
|
370
|
+
default_temperature=0.3,
|
|
371
|
+
accent_color='#10b981',
|
|
372
|
+
badge_icon='🍄',
|
|
373
|
+
is_builtin=True,
|
|
374
|
+
),
|
|
375
|
+
'rhizo': AgentProfileSpec(
|
|
376
|
+
agent_id='rhizo',
|
|
377
|
+
name='Rhizo',
|
|
378
|
+
role='Federated Graph & Barter Strategist',
|
|
379
|
+
call_sign='RHIZO // MESH-ECON',
|
|
380
|
+
tagline='P2P Routing Scout, Compute Auditor & Barter Economist',
|
|
381
|
+
style=(
|
|
382
|
+
'Sharp cyberpunk mesh economist and P2P routing scout who analyzes Kind 30811 compute receipts, '
|
|
383
|
+
'Cashu/Credits/IPD barter efficiency, and federated node discovery.'
|
|
384
|
+
),
|
|
385
|
+
expertise=(
|
|
386
|
+
'Triple-Rail Settlement (Credits / Cashu / IPD)',
|
|
387
|
+
'Kind 30811 NonDet Compute Receipt Auditing',
|
|
388
|
+
'Cross-Node Federation & Sub-Ask Routing',
|
|
389
|
+
),
|
|
390
|
+
skill_modules=('inference', 'discover', 'vault_hygiene'),
|
|
391
|
+
grounding_mode='adaptive',
|
|
392
|
+
system_prompt=(
|
|
393
|
+
'Provide crisp technical insight into FleetMesh non-deterministic compute execution, token fuel '
|
|
394
|
+
'metering, Cashu NUT-18 bearer settlement, IPD barter balances, and federated peer discovery.'
|
|
395
|
+
),
|
|
396
|
+
default_temperature=0.3,
|
|
397
|
+
accent_color='#a855f7',
|
|
398
|
+
badge_icon='⚡',
|
|
399
|
+
is_builtin=True,
|
|
400
|
+
),
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
|
|
404
|
+
class AgentHarnessRegistry:
|
|
405
|
+
"""Registry managing built-in and custom `AgentProfileSpec` definitions on a MeshNode."""
|
|
406
|
+
|
|
407
|
+
def __init__(self, default_agent_id: str = 'eddy') -> None:
|
|
408
|
+
self._profiles: dict[str, AgentProfileSpec] = dict(BUILTIN_AGENT_PROFILES)
|
|
409
|
+
self.default_agent_id: str = default_agent_id.lower() if default_agent_id else 'eddy'
|
|
410
|
+
|
|
411
|
+
def register(self, profile: AgentProfileSpec | Mapping[str, Any], *, set_default: bool = False) -> AgentProfileSpec:
|
|
412
|
+
spec = profile if isinstance(profile, AgentProfileSpec) else AgentProfileSpec.from_mapping(profile)
|
|
413
|
+
self._profiles[spec.agent_id] = spec
|
|
414
|
+
if set_default:
|
|
415
|
+
self.default_agent_id = spec.agent_id
|
|
416
|
+
return spec
|
|
417
|
+
|
|
418
|
+
def remove(self, agent_id: str) -> bool:
|
|
419
|
+
key = agent_id.strip().lower()
|
|
420
|
+
if key in BUILTIN_AGENT_PROFILES:
|
|
421
|
+
return False
|
|
422
|
+
return self._profiles.pop(key, None) is not None
|
|
423
|
+
|
|
424
|
+
def get(self, agent_id_or_name: Optional[str] = None) -> AgentProfileSpec:
|
|
425
|
+
if agent_id_or_name:
|
|
426
|
+
key = agent_id_or_name.strip().lower()
|
|
427
|
+
if key in self._profiles:
|
|
428
|
+
return self._profiles[key]
|
|
429
|
+
for spec in self._profiles.values():
|
|
430
|
+
if spec.name.lower() == key:
|
|
431
|
+
return spec
|
|
432
|
+
return self._profiles.get(self.default_agent_id) or BUILTIN_AGENT_PROFILES['eddy']
|
|
433
|
+
|
|
434
|
+
def list_profiles(self) -> list[dict[str, Any]]:
|
|
435
|
+
return [spec.to_dict() for spec in self._profiles.values()]
|
|
436
|
+
|
|
437
|
+
@staticmethod
|
|
438
|
+
def list_skill_modules() -> list[dict[str, Any]]:
|
|
439
|
+
return [mod.to_dict() for mod in HARNESS_SKILL_MODULES.values()]
|