diff --git a/pyproject.toml b/pyproject.toml index 22f4e2f6..490da66a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,7 +27,7 @@ keywords = [ requires-python = ">=3.12.2" dependencies = [ "hio==0.6.14", - "keri>=1.2.13,<1.3", + "keri==1.2.14", "mnemonic==0.21", "multicommand>=1.0.0", "falcon==4.0.2", diff --git a/src/keria/app/agenting.py b/src/keria/app/agenting.py index 1adc0423..b1bb07a9 100644 --- a/src/keria/app/agenting.py +++ b/src/keria/app/agenting.py @@ -39,6 +39,7 @@ querying, connecting, grouping, + tocking, ) from keri.app import delegating as kdelegating from keri.app.grouping import Counselor @@ -164,6 +165,32 @@ def readConfigFile(configDir: str, configFile: str, temp=False): ) +def keriTocks(cf): + """Resolve KERIpy cadences while reserving the existing KERIA Agent keys. + + Keep the config file intact so Agent can still read its legacy flat tocks. + Unknown keys and KERIpy values remain subject to KERIpy's validation. + """ + return tocking.resolveTocks( + cf.get().get("tocks") if cf is not None else None, + reserved=( + "signify", + "initer", + "querier", + "escrower", + "parser", + "witnesser", + "delegator", + "exchangeSender", + "granter", + "admitter", + "groupRequester", + "seeker", + "exchangecue", + ), + ) + + def runAgency(config: KERIAServerConfig, temp=False): """Runs a KERIA Agency with the given Doers by calling Doist.do(). Useful for testing.""" set_log_level(config.logLevel, logger) @@ -361,6 +388,7 @@ def create(self, caid, salt=None): bran=self.bran, ks=ks, cf=agent_cf, + tocks=keriTocks(agent_cf), temp=self.temp, salt=salt, ) @@ -441,8 +469,17 @@ def get(self, caid): ks = keeping.Keeper(name=caid, base=self.base, temp=self.temp, reopen=True) + cf = configing.Configer( + name=caid, base=self.base, temp=self.temp, reopen=True, clear=False + ) agentHby = habbing.Habery( - name=caid, base=self.base, bran=self.bran, ks=ks, temp=self.temp + name=caid, + base=self.base, + bran=self.bran, + ks=ks, + temp=self.temp, + cf=cf, + tocks=keriTocks(cf), ) agentHab = agentHby.habByName(f"agent-{caid}", ns="agent") diff --git a/src/keria/testing/testing_helper.py b/src/keria/testing/testing_helper.py index 0a56ee4b..03c021be 100644 --- a/src/keria/testing/testing_helper.py +++ b/src/keria/testing/testing_helper.py @@ -751,7 +751,13 @@ def openKeria(salter=None, cf=None, temp=True): ) agency = agenting.Agency(name="agency", bran=None, temp=True) - with habbing.openHby(name="keria", salt=salter.qb64, temp=temp, cf=cf) as hby: + with habbing.openHby( + name="keria", + salt=salter.qb64, + temp=temp, + cf=cf, + tocks=agenting.keriTocks(cf), + ) as hby: ims = eventing.messagize(serder, sigers=sigers) parsing.Parser(kvy=hby.kvy).parseOne(ims=ims) diff --git a/tests/app/test_agenting.py b/tests/app/test_agenting.py index bcebdd80..236b3e0c 100644 --- a/tests/app/test_agenting.py +++ b/tests/app/test_agenting.py @@ -34,6 +34,7 @@ from keria.app import agenting, aiding from keria.core import longrunning, httping +from keria.db import basing as agencybasing from keria.testing.testing_helper import SCRIPTS_DIR @@ -192,6 +193,67 @@ def test_load_ends(helpers): assert isinstance(end, agenting.ConfigResourceEnd) +def test_keri_tocks_preserves_agent_config(monkeypatch): + config = {"tocks": {"escrower": 1.0, "initer": 0.0, "receiptor": 0.25}} + with configing.openCF(temp=True) as cf: + cf.put(config) + monkeypatch.setenv("KERI_RECEIPTOR_TOCK", "0.125") + tocks = agenting.keriTocks(cf) + assert tocks["receiptor"] == 0.125 + assert "escrower" not in tocks + assert "initer" not in tocks + assert cf.get() == config + + cf.put({"tocks": {"escrower": 1.0, "receiptorTypo": 0.25}}) + with pytest.raises(kering.ConfigurationError, match="receiptorTypo"): + agenting.keriTocks(cf) + + +def test_agency_reopens_legacy_tocks(): + """Reload an evicted Agent from disk and retain its legacy tock settings.""" + caid = core.Signer().verfer.qb64 + with dbing.openLMDB(cls=agencybasing.AgencyBaser, temp=True) as adb: + cf = agenting.readConfigFile(SCRIPTS_DIR, "legacy-tocks") + agency = agenting.Agency(name="agency", bran=None, cf=cf, adb=adb) + doist = doing.Doist() + doist.enter(doers=[agency]) + # Provision the Agent and write its own config from the legacy fixture. + agent = agency.create(caid) + try: + assert agent.tocks["escrower"] == 1.0 + assert agent.hby.tocks["receiptor"] == 0.25 + persisted_config_path = agent.hby.cf.path + # Close the Agent's resources and evict it from the Agency cache, + # leaving its configuration and databases on disk for reopening. + agency.shut(agent) + assert caid not in agency.agents + + # This cache miss is the reopen: Agency.get constructs a new Agent + # from its persisted config and databases, rather than returning + # the previously cached instance or provisioning it again. + agent = agency.get(caid) + assert agent.hby.cf.path == persisted_config_path + assert agent.hby.cf.get()["tocks"] == cf.get()["tocks"] + assert agent.tocks["escrower"] == 1.0 + assert agent.hby.tocks["receiptor"] == 0.25 + finally: + agency.shut(agent) + doist.exit() + for resource in ( + agent.seeker, + agent.exnseeker, + agent.monitor.opr, + agent.notifier.noter, + agent.rep.mbx, + agent.rgy.reger, + agent.mgr.rb, + agent.hby, + agent.hby.cf, + ): + resource.close(clear=True) + cf.close() + + def test_load_tocks_config(helpers): with helpers.openKeria() as (agency, agent, app, client): agenting.loadEnds(app=app) diff --git a/tests/scripts/keri/cf/legacy-tocks.json b/tests/scripts/keri/cf/legacy-tocks.json new file mode 100755 index 00000000..731d3ced --- /dev/null +++ b/tests/scripts/keri/cf/legacy-tocks.json @@ -0,0 +1,7 @@ +{ + "tocks": { + "initer": 0.0, + "escrower": 1.0, + "receiptor": 0.25 + } +} diff --git a/uv.lock b/uv.lock index c192bc77..276d90e0 100644 --- a/uv.lock +++ b/uv.lock @@ -493,7 +493,7 @@ wheels = [ [[package]] name = "keri" -version = "1.2.13" +version = "1.2.14" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "apispec" }, @@ -517,7 +517,7 @@ dependencies = [ { name = "qrcode" }, { name = "semver" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/29/0f/6a7fd0cbd681c31fad385e2a5f27ac9ea9e6f7aae6f30b5814f960acdf17/keri-1.2.13.tar.gz", hash = "sha256:aef033ec3b17d4b04d8801b5365b225b6cba5630a67aaad2f306aaecbd256fcd", size = 479577, upload-time = "2026-05-06T18:24:47.865Z" } +sdist = { url = "https://files.pythonhosted.org/packages/71/c4/2d63280bb5d9599d9766d22db58a45ddd1fdb0fb12b839692a7a3a76b3c4/keri-1.2.14.tar.gz", hash = "sha256:f921a42794aa83988987f97d27337da38194dcd7fb0124511374112997660656", size = 494448, upload-time = "2026-09-09T15:28:06.354Z" } [[package]] name = "keria" @@ -555,7 +555,7 @@ requires-dist = [ { name = "falcon", specifier = "==4.0.2" }, { name = "hio", specifier = "==0.6.14" }, { name = "http-sfv", specifier = "==0.9.9" }, - { name = "keri", specifier = ">=1.2.13,<1.3" }, + { name = "keri", specifier = "==1.2.14" }, { name = "marshmallow-dataclass", specifier = "==8.7.1" }, { name = "mnemonic", specifier = "==0.21" }, { name = "multicommand", specifier = ">=1.0.0" },