clm-kernel 0.0.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.
Files changed (333) hide show
  1. clm/__init__.py +230 -0
  2. clm/__main__.py +25 -0
  3. clm/cross_cutting/__init__.py +50 -0
  4. clm/cross_cutting/_test_runner.py +36 -0
  5. clm/cross_cutting/async_utils.py +25 -0
  6. clm/cross_cutting/cel_builder.py +123 -0
  7. clm/cross_cutting/cel_converter.py +697 -0
  8. clm/cross_cutting/cel_engine.py +252 -0
  9. clm/cross_cutting/cel_ops.py +72 -0
  10. clm/cross_cutting/cel_ops_db.py +157 -0
  11. clm/cross_cutting/cel_ops_files.py +99 -0
  12. clm/cross_cutting/cel_ops_state.py +71 -0
  13. clm/cross_cutting/cel_ops_strings.py +64 -0
  14. clm/cross_cutting/cel_resolver.py +63 -0
  15. clm/cross_cutting/cel_slices.py +56 -0
  16. clm/cross_cutting/cel_store.py +330 -0
  17. clm/cross_cutting/clm_logger.py +140 -0
  18. clm/cross_cutting/config/__init__.py +155 -0
  19. clm/cross_cutting/config/config_constants.py +392 -0
  20. clm/cross_cutting/config/env_parameters.py +90 -0
  21. clm/cross_cutting/config/logging.py +378 -0
  22. clm/cross_cutting/config/settings.py +504 -0
  23. clm/cross_cutting/context.py +5 -0
  24. clm/cross_cutting/domain_models.py +55 -0
  25. clm/cross_cutting/domain_types.py +61 -0
  26. clm/cross_cutting/effects.py +67 -0
  27. clm/cross_cutting/errors.py +153 -0
  28. clm/cross_cutting/governance.py +331 -0
  29. clm/cross_cutting/guards.py +41 -0
  30. clm/cross_cutting/mcard.py +321 -0
  31. clm/cross_cutting/native.py +185 -0
  32. clm/cross_cutting/observability.py +289 -0
  33. clm/cross_cutting/pcard.py +434 -0
  34. clm/cross_cutting/registry.py +71 -0
  35. clm/cross_cutting/resources.py +84 -0
  36. clm/cross_cutting/secrets.py +44 -0
  37. clm/cross_cutting/subprocess_utils.py +118 -0
  38. clm/cross_cutting/telemetry.py +164 -0
  39. clm/cross_cutting/testing/__init__.py +8 -0
  40. clm/cross_cutting/testing/comparator.py +187 -0
  41. clm/cross_cutting/testing/datasets.py +87 -0
  42. clm/cross_cutting/timeutil.py +20 -0
  43. clm/cross_cutting/types/new_type_check.py +0 -0
  44. clm/cross_cutting/utils/__init__.py +1 -0
  45. clm/cross_cutting/utils/url_safety.py +50 -0
  46. clm/cross_cutting/vcard.py +560 -0
  47. clm/cross_cutting/writer.py +118 -0
  48. clm/layer0/README.md +50 -0
  49. clm/layer0/__init__.py +223 -0
  50. clm/layer0/algebra.py +60 -0
  51. clm/layer0/algebra_baldwin.py +133 -0
  52. clm/layer0/algebra_baldwin_generators.py +36 -0
  53. clm/layer0/algebra_equivalence.py +262 -0
  54. clm/layer0/algebra_ingest.py +306 -0
  55. clm/layer0/algebra_manifest_io.py +156 -0
  56. clm/layer0/assembler.py +481 -0
  57. clm/layer0/cel/__init__.py +23 -0
  58. clm/layer0/cel_eval.py +491 -0
  59. clm/layer0/codec.py +106 -0
  60. clm/layer0/context.py +122 -0
  61. clm/layer0/db.py +160 -0
  62. clm/layer0/exceptions.py +73 -0
  63. clm/layer0/fibration.py +106 -0
  64. clm/layer0/gates.py +50 -0
  65. clm/layer0/hash.py +80 -0
  66. clm/layer0/loader.py +826 -0
  67. clm/layer0/math_utils.py +163 -0
  68. clm/layer0/mcard.py +96 -0
  69. clm/layer0/model/__init__.py +0 -0
  70. clm/layer0/model/action.py +223 -0
  71. clm/layer0/model/card.py +280 -0
  72. clm/layer0/model/card_triad.py +427 -0
  73. clm/layer0/model/compatibility.py +60 -0
  74. clm/layer0/model/dictionary.py +46 -0
  75. clm/layer0/model/dots.py +331 -0
  76. clm/layer0/model/event_producer.py +88 -0
  77. clm/layer0/model/g_time.py +89 -0
  78. clm/layer0/model/handle.py +163 -0
  79. clm/layer0/model/hash/__init__.py +6 -0
  80. clm/layer0/model/hash/algorithms/__init__.py +3 -0
  81. clm/layer0/model/hash/algorithms/custom_hash.py +7 -0
  82. clm/layer0/model/hash/algorithms/local_sha256.py +178 -0
  83. clm/layer0/model/hash/constants.py +53 -0
  84. clm/layer0/model/hash/enums.py +60 -0
  85. clm/layer0/model/hash/validator.py +307 -0
  86. clm/layer0/model/interpreter.py +275 -0
  87. clm/layer0/model/pagination.py +45 -0
  88. clm/layer0/model/pcard.py +716 -0
  89. clm/layer0/model/schema.py +44 -0
  90. clm/layer0/model/utils/__init__.py +1 -0
  91. clm/layer0/model/utils/content_analyzer.py +152 -0
  92. clm/layer0/model/validators/__init__.py +1 -0
  93. clm/layer0/model/validators/base_validator.py +44 -0
  94. clm/layer0/model/validators/binary_validator.py +66 -0
  95. clm/layer0/model/validators/text_validator.py +82 -0
  96. clm/layer0/model/validators/validation_registry.py +49 -0
  97. clm/layer0/model/vcard.py +823 -0
  98. clm/layer0/model/vcard_ext/__init__.py +39 -0
  99. clm/layer0/model/vcard_ext/core.py +60 -0
  100. clm/layer0/model/vcard_ext/network.py +23 -0
  101. clm/layer0/model/vcard_ext/observability.py +63 -0
  102. clm/layer0/model/vcard_ext/storage.py +60 -0
  103. clm/layer0/model/vcard_ext/vendors.py +211 -0
  104. clm/layer0/model/vcard_sandwich.py +119 -0
  105. clm/layer0/model/vcard_vocabulary.py +463 -0
  106. clm/layer0/model/workflow.py +274 -0
  107. clm/layer0/narrative.py +431 -0
  108. clm/layer0/ontology/collection.py +125 -0
  109. clm/layer0/parser.py +429 -0
  110. clm/layer0/relations.py +73 -0
  111. clm/layer0/rule_evaluator.py +251 -0
  112. clm/layer0/schema/__init__.py +38 -0
  113. clm/layer0/schema/ddl.py +97 -0
  114. clm/layer0/templates/abstract.yaml +99 -0
  115. clm/layer0/templates/balanced.yaml +253 -0
  116. clm/layer0/templates/concrete.yaml +172 -0
  117. clm/layer0/type_registry.py +223 -0
  118. clm/layer0/types.py +204 -0
  119. clm/layer0/utils.py +886 -0
  120. clm/layer0/verifier_core.py +262 -0
  121. clm/layer1/__init__.py +31 -0
  122. clm/layer1/action_dispatcher.py +275 -0
  123. clm/layer1/adapter_registry.py +233 -0
  124. clm/layer1/algebra_baldwin_generators.py +192 -0
  125. clm/layer1/arrow_compiler.py +525 -0
  126. clm/layer1/batch_processor.py +152 -0
  127. clm/layer1/cel/__init__.py +27 -0
  128. clm/layer1/cel/builder.py +173 -0
  129. clm/layer1/engine.py +759 -0
  130. clm/layer1/engine_async.py +89 -0
  131. clm/layer1/eval_callback.py +49 -0
  132. clm/layer1/gate_composer.py +229 -0
  133. clm/layer1/hoare_sandwich.py +298 -0
  134. clm/layer1/identity_guard.py +348 -0
  135. clm/layer1/matrix.py +138 -0
  136. clm/layer1/neural_runtime.py +221 -0
  137. clm/layer1/operations/__init__.py +123 -0
  138. clm/layer1/operations/augment.py +156 -0
  139. clm/layer1/operations/base.py +109 -0
  140. clm/layer1/operations/builtins.py +338 -0
  141. clm/layer1/operations/dispatch.py +144 -0
  142. clm/layer1/operations/exclude.py +107 -0
  143. clm/layer1/operations/handle.py +197 -0
  144. clm/layer1/operations/identity.py +139 -0
  145. clm/layer1/operations/invert.py +66 -0
  146. clm/layer1/operations/loader.py +116 -0
  147. clm/layer1/operations/manipulators/__init__.py +23 -0
  148. clm/layer1/operations/manipulators/document.py +211 -0
  149. clm/layer1/operations/manipulators/media.py +134 -0
  150. clm/layer1/operations/manipulators/structured.py +159 -0
  151. clm/layer1/operations/manipulators/tabular.py +334 -0
  152. clm/layer1/operations/port.py +65 -0
  153. clm/layer1/operations/services.py +529 -0
  154. clm/layer1/operations/split.py +117 -0
  155. clm/layer1/operations/substitute.py +75 -0
  156. clm/layer1/petri.py +1052 -0
  157. clm/layer1/pocketflow_orchestrator.py +408 -0
  158. clm/layer1/pocketflow_scheduler.py +519 -0
  159. clm/layer1/protocols.py +197 -0
  160. clm/layer1/runtime_adapter.py +718 -0
  161. clm/layer1/runtimes/__init__.py +71 -0
  162. clm/layer1/runtimes/_generated_assertions.py +125 -0
  163. clm/layer1/runtimes/_generated_types.py +42 -0
  164. clm/layer1/runtimes/base.py +265 -0
  165. clm/layer1/runtimes/binary.py +101 -0
  166. clm/layer1/runtimes/factory.py +203 -0
  167. clm/layer1/runtimes/javascript.py +321 -0
  168. clm/layer1/runtimes/javascript_runtime.js +33 -0
  169. clm/layer1/runtimes/js_scripts/isomorphism.mjs +22 -0
  170. clm/layer1/runtimes/js_scripts/loader.mjs +52 -0
  171. clm/layer1/runtimes/lambda_calc.py +126 -0
  172. clm/layer1/runtimes/python.py +288 -0
  173. clm/layer1/runtimes/python_runtime.py +41 -0
  174. clm/layer1/runtimes/script.py +114 -0
  175. clm/layer1/sandbox.py +334 -0
  176. clm/layer1/savepoint_guard.py +136 -0
  177. clm/layer1/sparse_net.py +122 -0
  178. clm/layer1/spec_tester.py +165 -0
  179. clm/layer1/store.py +295 -0
  180. clm/layer1/test_runner.py +498 -0
  181. clm/layer1/type_inspector.py +166 -0
  182. clm/layer1/verifier.py +772 -0
  183. clm/layer1/vm.py +91 -0
  184. clm/layer2/__init__.py +78 -0
  185. clm/layer2/card_collection.py +678 -0
  186. clm/layer2/classifier.py +479 -0
  187. clm/layer2/engine/__init__.py +1 -0
  188. clm/layer2/engine/abstract_sql_engine.py +320 -0
  189. clm/layer2/engine/base.py +193 -0
  190. clm/layer2/engine/duckdb_engine.py +576 -0
  191. clm/layer2/engine/sqlite_engine.py +518 -0
  192. clm/layer2/fiber/__init__.py +31 -0
  193. clm/layer2/fiber/context.py +89 -0
  194. clm/layer2/fiber/disposable.py +163 -0
  195. clm/layer2/fiber/fiber.py +125 -0
  196. clm/layer2/fiber/hoare.py +191 -0
  197. clm/layer2/file_io.py +376 -0
  198. clm/layer2/file_registrar.py +77 -0
  199. clm/layer2/ingest.py +202 -0
  200. clm/layer2/lineage.py +322 -0
  201. clm/layer2/mcard_fs.py +262 -0
  202. clm/layer2/merkle.py +11 -0
  203. clm/layer2/mime.py +244 -0
  204. clm/layer2/mime_detector.py +245 -0
  205. clm/layer2/private_collection.py +118 -0
  206. clm/layer2/shared_store.py +11 -0
  207. clm/layer2/storage/__init__.py +190 -0
  208. clm/layer2/storage/db_bitemporal.py +141 -0
  209. clm/layer2/storage/db_driver.py +331 -0
  210. clm/layer2/storage/db_hyperlinks.py +214 -0
  211. clm/layer2/storage/db_pool.py +139 -0
  212. clm/layer2/storage/db_repository.py +229 -0
  213. clm/layer2/storage/savepoint.py +78 -0
  214. clm/layer2/storage/sqlite_wal.py +74 -0
  215. clm/layer2/tridb.py +245 -0
  216. clm/layer2/vcard_receipt.py +71 -0
  217. clm/layer2/vfs.py +17 -0
  218. clm/layer3/__init__.py +141 -0
  219. clm/layer3/agency/__init__.py +19 -0
  220. clm/layer3/agency/config.py +220 -0
  221. clm/layer3/agency/providers/__init__.py +15 -0
  222. clm/layer3/agency/providers/base.py +91 -0
  223. clm/layer3/agency/providers/mlc_llm.py +169 -0
  224. clm/layer3/agency/providers/ollama.py +203 -0
  225. clm/layer3/agency/router.py +71 -0
  226. clm/layer3/agency/runtime.py +524 -0
  227. clm/layer3/gateway.py +353 -0
  228. clm/layer3/open_interpreter_adapter.py +159 -0
  229. clm/layer3/rag/__init__.py +70 -0
  230. clm/layer3/rag/cli.py +228 -0
  231. clm/layer3/rag/config.py +153 -0
  232. clm/layer3/rag/embeddings/__init__.py +16 -0
  233. clm/layer3/rag/embeddings/base.py +79 -0
  234. clm/layer3/rag/embeddings/ollama.py +211 -0
  235. clm/layer3/rag/embeddings/vision.py +290 -0
  236. clm/layer3/rag/engine.py +276 -0
  237. clm/layer3/rag/graph/__init__.py +22 -0
  238. clm/layer3/rag/graph/community.py +198 -0
  239. clm/layer3/rag/graph/engine.py +433 -0
  240. clm/layer3/rag/graph/extractor.py +255 -0
  241. clm/layer3/rag/graph/schema.py +115 -0
  242. clm/layer3/rag/graph/store.py +641 -0
  243. clm/layer3/rag/indexer.py +333 -0
  244. clm/layer3/rag/llm_providers.json +52 -0
  245. clm/layer3/rag/semantic_versioning.py +313 -0
  246. clm/layer3/rag/vector/__init__.py +28 -0
  247. clm/layer3/rag/vector/handle_vector_store.py +749 -0
  248. clm/layer3/rag/vector/schema.py +151 -0
  249. clm/layer3/rag/vector/store.py +632 -0
  250. clm/layer3/reticulum/__init__.py +55 -0
  251. clm/layer3/reticulum/bridge.py +74 -0
  252. clm/layer3/reticulum/daemon.py +88 -0
  253. clm/layer3/reticulum/discovery.py +164 -0
  254. clm/layer3/reticulum/identity.py +107 -0
  255. clm/layer3/reticulum/media.py +148 -0
  256. clm/layer3/reticulum/transport.py +133 -0
  257. clm/layer3/satori/__init__.py +209 -0
  258. clm/layer3/satori/alpha_conversion.py +168 -0
  259. clm/layer3/satori/beta_reduction.py +359 -0
  260. clm/layer3/satori/continuation.py +38 -0
  261. clm/layer3/satori/eta_conversion.py +205 -0
  262. clm/layer3/satori/free_variables.py +163 -0
  263. clm/layer3/satori/io_effects.py +370 -0
  264. clm/layer3/satori/lambda_runtime.py +726 -0
  265. clm/layer3/satori/lambda_term.py +230 -0
  266. clm/layer3/satori/sheaf_audit.py +261 -0
  267. clm/layer3/satori/speech_act.py +279 -0
  268. clm/layer4/README.md +9 -0
  269. clm/layer4/__init__.py +67 -0
  270. clm/layer4/_sovereign_executor.py +580 -0
  271. clm/layer4/action_commands.py +719 -0
  272. clm/layer4/api.py +236 -0
  273. clm/layer4/bootstrap/__init__.py +13 -0
  274. clm/layer4/bootstrap/bootstrapper.py +118 -0
  275. clm/layer4/bootstrap/cleanup.py +44 -0
  276. clm/layer4/bootstrap/clm_hooks.py +49 -0
  277. clm/layer4/bootstrap/genesis.py +77 -0
  278. clm/layer4/bootstrap/injection.py +125 -0
  279. clm/layer4/cli.py +549 -0
  280. clm/layer4/collection_manager.py +553 -0
  281. clm/layer4/commands/__init__.py +81 -0
  282. clm/layer4/commands/action_cmd.py +42 -0
  283. clm/layer4/commands/check_cmd.py +82 -0
  284. clm/layer4/commands/envelope.py +73 -0
  285. clm/layer4/commands/evaluate_cmd.py +224 -0
  286. clm/layer4/commands/mcard_cmd.py +24 -0
  287. clm/layer4/commands/petri_cmd.py +92 -0
  288. clm/layer4/commands/tridb_cmd.py +319 -0
  289. clm/layer4/commands/version_cmd.py +27 -0
  290. clm/layer4/diff_commands.py +167 -0
  291. clm/layer4/eoa_sponsor.py +232 -0
  292. clm/layer4/events.py +69 -0
  293. clm/layer4/flux_gateway.py +133 -0
  294. clm/layer4/gateway.py +91 -0
  295. clm/layer4/loader.py +307 -0
  296. clm/layer4/mcard_commands.py +166 -0
  297. clm/layer4/membrane.py +273 -0
  298. clm/layer4/navigation.py +50 -0
  299. clm/layer4/protocol/protocol_entry_python.py +108 -0
  300. clm/layer4/protocol/python_sync.py +126 -0
  301. clm/layer4/runner.py +150 -0
  302. clm/layer4/satori.py +262 -0
  303. clm/layer4/server/python_sync_client.py +150 -0
  304. clm/layer4/server/python_sync_server.py +139 -0
  305. clm/layer4/server/service_api.py +315 -0
  306. clm/layer4/server/start_servers.py +107 -0
  307. clm/layer4/server/stop_servers.py +24 -0
  308. clm/layer4/simulated_optimization_fix.py +0 -0
  309. clm/layer4/storage.py +463 -0
  310. clm/layer4/tridb_admin.py +268 -0
  311. clm/layer4/websocket/test_ws_client.py +104 -0
  312. clm/layer4/websocket/test_ws_server.py +138 -0
  313. clm/layer4/websocket/ws_server_local.py +160 -0
  314. clm/layer5/__init__.py +51 -0
  315. clm/layer5/async_batch_writer.py +133 -0
  316. clm/layer5/learning.py +276 -0
  317. clm/layer5/telemetry_hook.py +107 -0
  318. clm/layer5/type_lattice.py +238 -0
  319. clm/py.typed +0 -0
  320. clm/schemas/__init__.py +652 -0
  321. clm/schemas/abstract_face_schema.yaml +77 -0
  322. clm/schemas/cel_schema.yaml +416 -0
  323. clm/schemas/lifecycle/__init__.py +45 -0
  324. clm/schemas/lifecycle/models.py +123 -0
  325. clm/schemas/lifecycle/service.py +364 -0
  326. clm/schemas/mcard_schema.sql +192 -0
  327. clm/schemas/mcard_vector_schema.sql +283 -0
  328. clm/schemas/mlp-context-v1.json +91 -0
  329. clm_kernel-0.0.0.dist-info/METADATA +121 -0
  330. clm_kernel-0.0.0.dist-info/RECORD +333 -0
  331. clm_kernel-0.0.0.dist-info/WHEEL +4 -0
  332. clm_kernel-0.0.0.dist-info/entry_points.txt +3 -0
  333. clm_kernel-0.0.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,359 @@
1
+ """
2
+ Beta Reduction - Function application in Lambda Calculus
3
+
4
+ Beta reduction (β-reduction) is the computational rule:
5
+ (λx.M) N →β M[x:=N]
6
+
7
+ This module provides:
8
+ - Single-step beta reduction
9
+ - Full normalization with multiple strategies
10
+ - Capture-avoiding substitution
11
+ - Redex detection and search
12
+
13
+ This module provides Python parity with mcard-js/src/ptr/lambda/BetaReduction.ts
14
+ """
15
+
16
+ from collections.abc import Callable
17
+ from dataclasses import dataclass
18
+ from typing import Literal
19
+
20
+ from clm.layer2.card_collection import CardCollection
21
+ from clm.layer3.satori.alpha_conversion import alpha_rename
22
+ from clm.layer3.satori.free_variables import generate_fresh_for, is_free_in
23
+ from clm.layer3.satori.lambda_term import (
24
+ is_abs,
25
+ is_app,
26
+ is_var,
27
+ load_term,
28
+ mk_abs,
29
+ mk_app,
30
+ store_term,
31
+ )
32
+
33
+ # ─────────────────────────────────────────────────────────────────────────────
34
+ # Types
35
+ # ─────────────────────────────────────────────────────────────────────────────
36
+
37
+ ReductionStrategy = Literal["normal", "applicative", "lazy"]
38
+
39
+
40
+ @dataclass
41
+ class NormalizationResult:
42
+ """Result of normalization"""
43
+
44
+ normal_form: str # Hash of the normal form
45
+ steps: int # Number of reduction steps
46
+ reduction_path: list[str] # Hashes of intermediate forms
47
+
48
+
49
+ # ─────────────────────────────────────────────────────────────────────────────
50
+ # Substitution with Capture Avoidance
51
+ # ─────────────────────────────────────────────────────────────────────────────
52
+
53
+
54
+ def substitute_with_capture(
55
+ collection: CardCollection, term_hash: str, var_name: str, replacement_hash: str
56
+ ) -> str | None:
57
+ """
58
+ Perform capture-avoiding substitution: M[x:=N]
59
+
60
+ This is the core operation that replaces all free occurrences of x in M with N,
61
+ while avoiding variable capture by renaming bound variables as needed.
62
+ """
63
+ term = load_term(collection, term_hash)
64
+ if term is None:
65
+ return None
66
+
67
+ if is_var(term):
68
+ if term.name == var_name:
69
+ return replacement_hash
70
+ return term_hash
71
+
72
+ elif is_abs(term):
73
+ if term.param == var_name:
74
+ # x is bound here - no substitution in body
75
+ return term_hash
76
+
77
+ # Check for capture: if param is free in replacement
78
+ param_free_in_repl = is_free_in(collection, term.param, replacement_hash)
79
+ if param_free_in_repl is None:
80
+ return None
81
+
82
+ if param_free_in_repl:
83
+ # Need to alpha-rename to avoid capture
84
+ fresh = generate_fresh_for(
85
+ collection, term.param, term.body, replacement_hash
86
+ )
87
+ renamed_body = alpha_rename(collection, term.body, term.param, fresh)
88
+ if renamed_body is None:
89
+ return None
90
+
91
+ new_body = substitute_with_capture(
92
+ collection, renamed_body, var_name, replacement_hash
93
+ )
94
+ if new_body is None:
95
+ return None
96
+
97
+ return store_term(collection, mk_abs(fresh, new_body))
98
+
99
+ # Safe to substitute directly
100
+ new_body = substitute_with_capture(
101
+ collection, term.body, var_name, replacement_hash
102
+ )
103
+ if new_body is None:
104
+ return None
105
+
106
+ return store_term(collection, mk_abs(term.param, new_body))
107
+
108
+ elif is_app(term):
109
+ new_func = substitute_with_capture(
110
+ collection, term.func, var_name, replacement_hash
111
+ )
112
+ new_arg = substitute_with_capture(
113
+ collection, term.arg, var_name, replacement_hash
114
+ )
115
+ if new_func is None or new_arg is None:
116
+ return None
117
+ return store_term(collection, mk_app(new_func, new_arg))
118
+
119
+ return term_hash
120
+
121
+
122
+ # ─────────────────────────────────────────────────────────────────────────────
123
+ # Redex Detection
124
+ # ─────────────────────────────────────────────────────────────────────────────
125
+
126
+
127
+ def is_redex(collection: CardCollection, hash_value: str) -> bool | None:
128
+ """
129
+ Check if a term is a beta-redex: (λx.M) N
130
+ """
131
+ term = load_term(collection, hash_value)
132
+ if term is None:
133
+ return None
134
+
135
+ if is_app(term):
136
+ func = load_term(collection, term.func)
137
+ if func is not None and is_abs(func):
138
+ return True
139
+
140
+ return False
141
+
142
+
143
+ def find_leftmost_redex(collection: CardCollection, hash_value: str) -> str | None:
144
+ """
145
+ Find the leftmost-outermost beta-redex (normal order).
146
+ Returns the hash of the redex, or None if none found.
147
+ """
148
+ term = load_term(collection, hash_value)
149
+ if term is None:
150
+ return None
151
+
152
+ if is_app(term):
153
+ # Check if this is a redex
154
+ func = load_term(collection, term.func)
155
+ if func is not None and is_abs(func):
156
+ return hash_value
157
+
158
+ # Try left subtree first
159
+ left_redex = find_leftmost_redex(collection, term.func)
160
+ if left_redex:
161
+ return left_redex
162
+
163
+ # Then right subtree
164
+ return find_leftmost_redex(collection, term.arg)
165
+
166
+ elif is_abs(term):
167
+ return find_leftmost_redex(collection, term.body)
168
+
169
+ return None
170
+
171
+
172
+ def find_innermost_redex(collection: CardCollection, hash_value: str) -> str | None:
173
+ """
174
+ Find the innermost-leftmost beta-redex (applicative order).
175
+ Returns the hash of the redex, or None if none found.
176
+ """
177
+ term = load_term(collection, hash_value)
178
+ if term is None:
179
+ return None
180
+
181
+ if is_app(term):
182
+ # Try subtrees first
183
+ left_redex = find_innermost_redex(collection, term.func)
184
+ if left_redex:
185
+ return left_redex
186
+
187
+ right_redex = find_innermost_redex(collection, term.arg)
188
+ if right_redex:
189
+ return right_redex
190
+
191
+ # Check if this is a redex
192
+ func = load_term(collection, term.func)
193
+ if func is not None and is_abs(func):
194
+ return hash_value
195
+
196
+ elif is_abs(term):
197
+ return find_innermost_redex(collection, term.body)
198
+
199
+ return None
200
+
201
+
202
+ # ─────────────────────────────────────────────────────────────────────────────
203
+ # Beta Reduction
204
+ # ─────────────────────────────────────────────────────────────────────────────
205
+
206
+
207
+ def beta_reduce(collection: CardCollection, hash_value: str) -> str | None:
208
+ """
209
+ Perform a single beta reduction step on a redex.
210
+
211
+ (λx.M) N →β M[x:=N]
212
+
213
+ Returns the hash of the reduced term, or None if not a redex.
214
+ """
215
+ term = load_term(collection, hash_value)
216
+ if term is None:
217
+ return None
218
+
219
+ if not is_app(term):
220
+ return None
221
+
222
+ func = load_term(collection, term.func)
223
+ if func is None or not is_abs(func):
224
+ return None
225
+
226
+ # Perform substitution: body[param:=arg]
227
+ return substitute_with_capture(collection, func.body, func.param, term.arg)
228
+
229
+
230
+ def reduce_step(
231
+ collection: CardCollection, hash_value: str, strategy: ReductionStrategy = "normal"
232
+ ) -> str | None:
233
+ """
234
+ Perform a single reduction step using the specified strategy.
235
+ Returns None if no reduction is possible.
236
+ """
237
+ term = load_term(collection, hash_value)
238
+ if term is None:
239
+ return None
240
+
241
+ # Find redex based on strategy
242
+ if strategy == "normal":
243
+ redex_hash = find_leftmost_redex(collection, hash_value)
244
+ elif strategy == "applicative":
245
+ redex_hash = find_innermost_redex(collection, hash_value)
246
+ else: # lazy
247
+ redex_hash = find_leftmost_redex(collection, hash_value)
248
+
249
+ if not redex_hash:
250
+ return None
251
+
252
+ # Reduce the redex
253
+ reduced = beta_reduce(collection, redex_hash)
254
+ if reduced is None:
255
+ return None
256
+
257
+ # Replace the redex in the original term
258
+ return replace_subterm(collection, hash_value, redex_hash, reduced)
259
+
260
+
261
+ def replace_subterm(
262
+ collection: CardCollection, term_hash: str, target_hash: str, replacement_hash: str
263
+ ) -> str | None:
264
+ """
265
+ Replace a subterm (identified by hash) with another term.
266
+ """
267
+ if term_hash == target_hash:
268
+ return replacement_hash
269
+
270
+ term = load_term(collection, term_hash)
271
+ if term is None:
272
+ return None
273
+
274
+ if is_var(term):
275
+ return term_hash
276
+
277
+ elif is_abs(term):
278
+ new_body = replace_subterm(collection, term.body, target_hash, replacement_hash)
279
+ if new_body is None:
280
+ return None
281
+ if new_body == term.body:
282
+ return term_hash
283
+ return store_term(collection, mk_abs(term.param, new_body))
284
+
285
+ elif is_app(term):
286
+ new_func = replace_subterm(collection, term.func, target_hash, replacement_hash)
287
+ new_arg = replace_subterm(collection, term.arg, target_hash, replacement_hash)
288
+ if new_func is None or new_arg is None:
289
+ return None
290
+ if new_func == term.func and new_arg == term.arg:
291
+ return term_hash
292
+ return store_term(collection, mk_app(new_func, new_arg))
293
+
294
+ return term_hash
295
+
296
+
297
+ # ─────────────────────────────────────────────────────────────────────────────
298
+ # Normalization
299
+ # ─────────────────────────────────────────────────────────────────────────────
300
+
301
+
302
+ def normalize(
303
+ collection: CardCollection,
304
+ hash_value: str,
305
+ strategy: ReductionStrategy = "normal",
306
+ max_steps: int = 1000,
307
+ on_step: Callable[[int, str], None] | None = None,
308
+ ) -> NormalizationResult | None:
309
+ """
310
+ Normalize a Lambda term by repeatedly applying reduction steps.
311
+
312
+ Args:
313
+ collection: The card collection containing terms
314
+ hash_value: Hash of the term to normalize
315
+ strategy: Reduction strategy ('normal', 'applicative', 'lazy')
316
+ max_steps: Maximum number of reduction steps
317
+ on_step: Optional callback for IO effects, called after each step
318
+
319
+ Returns the normal form if found within max_steps, or None if:
320
+ - Term not found
321
+ - Reduction diverges (exceeds max_steps)
322
+ """
323
+ current = hash_value
324
+ path = [current]
325
+ steps = 0
326
+
327
+ while steps < max_steps:
328
+ next_term = reduce_step(collection, current, strategy)
329
+
330
+ if next_term is None:
331
+ # No more reductions - we're in normal form
332
+ return NormalizationResult(
333
+ normal_form=current, steps=steps, reduction_path=path
334
+ )
335
+
336
+ steps += 1
337
+ current = next_term
338
+ path.append(current)
339
+
340
+ # IO Effect: emit step event
341
+ if on_step is not None:
342
+ on_step(steps, current)
343
+
344
+ # Exceeded max steps - possibly divergent
345
+ return None
346
+
347
+
348
+ def is_normal_form(collection: CardCollection, hash_value: str) -> bool | None:
349
+ """Check if a term is in normal form (contains no redexes)"""
350
+ redex = find_leftmost_redex(collection, hash_value)
351
+ return redex is None
352
+
353
+
354
+ def has_normal_form(
355
+ collection: CardCollection, hash_value: str, max_steps: int = 1000
356
+ ) -> bool | None:
357
+ """Check if a term has a normal form (within max_steps)"""
358
+ result = normalize(collection, hash_value, "normal", max_steps)
359
+ return result is not None
@@ -0,0 +1,38 @@
1
+ """Layer 3 Satori Delimited Continuations (DEL-390-02).
2
+
3
+ Provides delimited continuation tokens and multi-turn state resumption
4
+ for Satori conversational speech-act threads.
5
+ """
6
+ from __future__ import annotations
7
+
8
+ import time
9
+ import uuid
10
+ from dataclasses import dataclass, field
11
+ from typing import Any, Dict, List, Optional
12
+
13
+
14
+ @dataclass
15
+ class ContinuationToken:
16
+ """Represents a suspended execution turn in a conversational speech-act thread."""
17
+ turn_id: str = field(default_factory=lambda: str(uuid.uuid4()))
18
+ timestamp: float = field(default_factory=time.time)
19
+ captured_state: Dict[str, Any] = field(default_factory=dict)
20
+ prompt_history: List[Dict[str, Any]] = field(default_factory=list)
21
+ resume_target: Optional[str] = None
22
+ is_resumed: bool = False
23
+
24
+ def resume(self, response_act: Dict[str, Any]) -> Dict[str, Any]:
25
+ """Resumes the suspended continuation by appending the new speech act."""
26
+ if self.is_resumed:
27
+ raise RuntimeError(f"ContinuationToken {self.turn_id} has already been resumed.")
28
+ self.is_resumed = True
29
+ self.prompt_history.append(response_act)
30
+ return {
31
+ "turn_id": self.turn_id,
32
+ "resumed_at": time.time(),
33
+ "history_depth": len(self.prompt_history),
34
+ "state": self.captured_state,
35
+ }
36
+
37
+
38
+ __all__ = ["ContinuationToken"]
@@ -0,0 +1,205 @@
1
+ """
2
+ Eta Conversion - Extensional equality in Lambda Calculus
3
+
4
+ Eta conversion (η-conversion) captures extensional equivalence:
5
+ λx.(M x) ≡η M (when x is not free in M)
6
+
7
+ This expresses that two functions are equal if they produce the same
8
+ result for all inputs.
9
+
10
+ This module provides Python parity with mcard-js/src/ptr/lambda/EtaConversion.ts
11
+ """
12
+
13
+ from clm.layer2.card_collection import CardCollection
14
+ from clm.layer3.satori.alpha_conversion import alpha_equivalent
15
+ from clm.layer3.satori.free_variables import generate_fresh_for, is_free_in
16
+ from clm.layer3.satori.lambda_term import (
17
+ is_abs,
18
+ is_app,
19
+ is_var,
20
+ load_term,
21
+ mk_abs,
22
+ mk_app,
23
+ mk_var,
24
+ store_term,
25
+ )
26
+
27
+ # ─────────────────────────────────────────────────────────────────────────────
28
+ # Eta Reduction
29
+ # ─────────────────────────────────────────────────────────────────────────────
30
+
31
+
32
+ def eta_reduce(collection: CardCollection, hash_value: str) -> str | None:
33
+ """
34
+ Perform eta reduction on a term.
35
+
36
+ λx.(M x) →η M (when x is not free in M)
37
+
38
+ Returns the reduced term hash, or the original if not reducible.
39
+ """
40
+ term = load_term(collection, hash_value)
41
+ if term is None:
42
+ return None
43
+
44
+ if is_abs(term):
45
+ # Check if body is application
46
+ body = load_term(collection, term.body)
47
+ if body is None:
48
+ return None
49
+
50
+ if is_app(body):
51
+ # Check if arg is the bound variable
52
+ arg = load_term(collection, body.arg)
53
+ if arg is None:
54
+ return None
55
+
56
+ if is_var(arg) and arg.name == term.param:
57
+ # Check if param is NOT free in func
58
+ free_check = is_free_in(collection, term.param, body.func)
59
+ if free_check is not None and not free_check:
60
+ # η-reduction applies!
61
+ return body.func
62
+
63
+ # No reduction possible at top level, try subterms
64
+ if is_abs(term):
65
+ new_body = eta_reduce(collection, term.body)
66
+ if new_body is None:
67
+ return None
68
+ if new_body != term.body:
69
+ return store_term(collection, mk_abs(term.param, new_body))
70
+ return hash_value
71
+
72
+ elif is_app(term):
73
+ new_func = eta_reduce(collection, term.func)
74
+ new_arg = eta_reduce(collection, term.arg)
75
+ if new_func is None or new_arg is None:
76
+ return None
77
+ if new_func != term.func or new_arg != term.arg:
78
+ return store_term(collection, mk_app(new_func, new_arg))
79
+ return hash_value
80
+
81
+ return hash_value
82
+
83
+
84
+ # ─────────────────────────────────────────────────────────────────────────────
85
+ # Eta Expansion
86
+ # ─────────────────────────────────────────────────────────────────────────────
87
+
88
+
89
+ def eta_expand(
90
+ collection: CardCollection, hash_value: str, fresh_var: str | None = None
91
+ ) -> str | None:
92
+ """
93
+ Perform eta expansion on a term.
94
+
95
+ M →η λx.(M x) (where x is fresh)
96
+
97
+ Returns the expanded term hash.
98
+ """
99
+ term = load_term(collection, hash_value)
100
+ if term is None:
101
+ return None
102
+
103
+ # Generate fresh variable if not provided
104
+ if fresh_var is None:
105
+ fresh_var = generate_fresh_for(collection, "η", hash_value)
106
+
107
+ # Create variable term
108
+ var_hash = store_term(collection, mk_var(fresh_var))
109
+
110
+ # Create application: M x
111
+ app_hash = store_term(collection, mk_app(hash_value, var_hash))
112
+
113
+ # Create abstraction: λx.(M x)
114
+ return store_term(collection, mk_abs(fresh_var, app_hash))
115
+
116
+
117
+ # ─────────────────────────────────────────────────────────────────────────────
118
+ # Eta Equivalence
119
+ # ─────────────────────────────────────────────────────────────────────────────
120
+
121
+
122
+ def eta_equivalent(collection: CardCollection, hash1: str, hash2: str) -> bool | None:
123
+ """
124
+ Check if two terms are η-equivalent.
125
+
126
+ Performs full η-reduction on both terms and checks α-equivalence.
127
+ """
128
+ # Fully eta-reduce both terms
129
+ reduced1 = fully_eta_reduce(collection, hash1)
130
+ reduced2 = fully_eta_reduce(collection, hash2)
131
+
132
+ if reduced1 is None or reduced2 is None:
133
+ return None
134
+
135
+ # Check alpha equivalence
136
+ return alpha_equivalent(collection, reduced1, reduced2)
137
+
138
+
139
+ def fully_eta_reduce(
140
+ collection: CardCollection, hash_value: str, max_steps: int = 100
141
+ ) -> str | None:
142
+ """
143
+ Repeatedly apply eta reduction until no more reductions apply.
144
+ """
145
+ current = hash_value
146
+ steps = 0
147
+
148
+ while steps < max_steps:
149
+ reduced = eta_reduce(collection, current)
150
+ if reduced is None:
151
+ return None
152
+
153
+ if reduced == current:
154
+ # No more reductions
155
+ return current
156
+
157
+ current = reduced
158
+ steps += 1
159
+
160
+ return current
161
+
162
+
163
+ # ─────────────────────────────────────────────────────────────────────────────
164
+ # Combined Beta-Eta Reduction
165
+ # ─────────────────────────────────────────────────────────────────────────────
166
+
167
+
168
+ def beta_eta_reduce(collection: CardCollection, hash_value: str) -> str | None:
169
+ """
170
+ Perform one step of beta or eta reduction.
171
+ Tries beta first, then eta.
172
+ """
173
+ from clm.layer3.satori.beta_reduction import reduce_step
174
+
175
+ # Try beta reduction first
176
+ beta_result = reduce_step(collection, hash_value, "normal")
177
+ if beta_result is not None:
178
+ return beta_result
179
+
180
+ # Try eta reduction
181
+ eta_result = eta_reduce(collection, hash_value)
182
+ if eta_result is not None and eta_result != hash_value:
183
+ return eta_result
184
+
185
+ return None
186
+
187
+
188
+ def beta_eta_normalize(
189
+ collection: CardCollection, hash_value: str, max_steps: int = 1000
190
+ ) -> str | None:
191
+ """
192
+ Normalize a term using both beta and eta reductions.
193
+ """
194
+ current = hash_value
195
+ steps = 0
196
+
197
+ while steps < max_steps:
198
+ reduced = beta_eta_reduce(collection, current)
199
+ if reduced is None:
200
+ return current
201
+
202
+ current = reduced
203
+ steps += 1
204
+
205
+ return None