pal-code 0.1.1__tar.gz → 0.1.3__tar.gz

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 (297) hide show
  1. {pal_code-0.1.1 → pal_code-0.1.3}/LICENSE +1 -1
  2. {pal_code-0.1.1 → pal_code-0.1.3}/PKG-INFO +6 -6
  3. {pal_code-0.1.1 → pal_code-0.1.3}/pyproject.toml +7 -4
  4. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/__init__.py +1 -1
  5. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/cli.py +18 -13
  6. pal_code-0.1.3/src/pal/context_window.py +42 -0
  7. pal_code-0.1.3/src/pal/llm_wrapper/__init__.py +34 -0
  8. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/studio/routes.py +2 -2
  9. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/studio/runs.py +21 -2
  10. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/terminal/bench.py +8 -1
  11. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/terminal/cli.py +300 -68
  12. pal_code-0.1.3/src/pal/terminal/logo.py +140 -0
  13. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/terminal/session.py +13 -0
  14. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/terminal/setup.py +129 -3
  15. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/terminal/ui.py +102 -48
  16. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/terminal/voice.py +3 -3
  17. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/terminal/workflow.py +3 -3
  18. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/web/app.py +6 -0
  19. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/web/static/studio.css +2 -0
  20. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/web/static/voice.js +1 -1
  21. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/web/templates/index.html +2 -2
  22. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/web/templates/studio.html +2 -2
  23. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal_code.egg-info/PKG-INFO +6 -6
  24. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal_code.egg-info/SOURCES.txt +1 -0
  25. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal_code.egg-info/requires.txt +4 -4
  26. pal_code-0.1.1/src/pal/llm_wrapper/__init__.py +0 -34
  27. pal_code-0.1.1/src/pal/terminal/logo.py +0 -117
  28. {pal_code-0.1.1 → pal_code-0.1.3}/README.md +0 -0
  29. {pal_code-0.1.1 → pal_code-0.1.3}/setup.cfg +0 -0
  30. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/__main__.py +0 -0
  31. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/access_manager/__init__.py +0 -0
  32. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/access_manager/components/__init__.py +0 -0
  33. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/access_manager/components/access_control.py +0 -0
  34. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/access_manager/components/base.py +0 -0
  35. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/access_manager/components/intervention.py +0 -0
  36. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/access_manager/components/privilege_registry.py +0 -0
  37. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/access_manager/pipeline.py +0 -0
  38. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/access_manager/utils/__init__.py +0 -0
  39. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/agent_constraints/__init__.py +0 -0
  40. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/agent_constraints/components/AGENT_OUTPUT.md +0 -0
  41. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/agent_constraints/components/__init__.py +0 -0
  42. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/agent_constraints/components/constraints.py +0 -0
  43. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/agent_constraints/pipeline.py +0 -0
  44. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/agent_constraints/utils/__init__.py +0 -0
  45. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/agents/__init__.py +0 -0
  46. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/agents/coding.py +0 -0
  47. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/agents/docs.py +0 -0
  48. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/chat/__init__.py +0 -0
  49. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/chat/demo.py +0 -0
  50. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/chat/providers.py +0 -0
  51. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/chat/service.py +0 -0
  52. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/chat/services/__init__.py +0 -0
  53. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/chat/services/base.py +0 -0
  54. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/chat/services/data.py +0 -0
  55. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/chat/services/media.py +0 -0
  56. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/chat/services/web.py +0 -0
  57. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/chat/sessions.py +0 -0
  58. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/chat/tools.py +0 -0
  59. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/chat/uploads.py +0 -0
  60. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/compression/__init__.py +0 -0
  61. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/compression/components/__init__.py +0 -0
  62. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/compression/components/adverbs.py +0 -0
  63. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/compression/components/hedges.py +0 -0
  64. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/compression/components/interjections.py +0 -0
  65. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/compression/components/minifier.py +0 -0
  66. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/compression/components/output_compressor.py +0 -0
  67. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/compression/components/pleonasms.py +0 -0
  68. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/compression/components/punctuations.py +0 -0
  69. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/compression/components/stopwords.py +0 -0
  70. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/compression/history.py +0 -0
  71. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/compression/pipeline.py +0 -0
  72. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/compression/utils/__init__.py +0 -0
  73. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/compression/utils/negations.py +0 -0
  74. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/compression/utils/nltk_setup.py +0 -0
  75. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/compression/utils/pronouns.py +0 -0
  76. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/compression/utils/proselint_setup.py +0 -0
  77. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/compression/utils/spacy_setup.py +0 -0
  78. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/config/default.yaml +0 -0
  79. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/config.py +0 -0
  80. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/context_manager/__init__.py +0 -0
  81. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/context_manager/chat_context.py +0 -0
  82. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/context_manager/components/__init__.py +0 -0
  83. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/context_manager/components/assembler.py +0 -0
  84. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/context_manager/components/context_cache.py +0 -0
  85. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/context_manager/components/kv_bridge.py +0 -0
  86. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/context_manager/components/pager.py +0 -0
  87. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/context_manager/components/simple_context_manager.py +0 -0
  88. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/context_manager/components/turns.py +0 -0
  89. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/context_manager/pipeline.py +0 -0
  90. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/context_manager/utils/__init__.py +0 -0
  91. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/context_manager/utils/hashing.py +0 -0
  92. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/context_manager/utils/relevance.py +0 -0
  93. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/context_manager/utils/storage.py +0 -0
  94. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/documents/__init__.py +0 -0
  95. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/documents/components/__init__.py +0 -0
  96. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/documents/components/chunker.py +0 -0
  97. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/documents/components/index.py +0 -0
  98. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/documents/utils/__init__.py +0 -0
  99. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/errors.py +0 -0
  100. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/input_converters/__init__.py +0 -0
  101. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/input_converters/components/__init__.py +0 -0
  102. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/input_converters/components/base.py +0 -0
  103. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/input_converters/components/pandoc_converter.py +0 -0
  104. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/input_converters/components/pdf_converter.py +0 -0
  105. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/input_converters/components/pptx_converter.py +0 -0
  106. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/input_converters/components/xlsx_converter.py +0 -0
  107. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/input_converters/pipeline.py +0 -0
  108. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/input_converters/utils/__init__.py +0 -0
  109. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/input_converters/workspace.py +0 -0
  110. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/input_savers/INPUT_RULES.md +0 -0
  111. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/input_savers/__init__.py +0 -0
  112. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/input_savers/components/__init__.py +0 -0
  113. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/input_savers/components/base.py +0 -0
  114. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/input_savers/components/methods.py +0 -0
  115. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/input_savers/components/verifier.py +0 -0
  116. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/input_savers/pipeline.py +0 -0
  117. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/input_savers/utils/__init__.py +0 -0
  118. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/input_savers/utils/guards.py +0 -0
  119. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/knowledge_graph/__init__.py +0 -0
  120. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/knowledge_graph/components/__init__.py +0 -0
  121. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/knowledge_graph/components/base.py +0 -0
  122. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/knowledge_graph/components/builder.py +0 -0
  123. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/knowledge_graph/components/retriever.py +0 -0
  124. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/knowledge_graph/components/workspace.py +0 -0
  125. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/knowledge_graph/pipeline.py +0 -0
  126. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/knowledge_graph/utils/__init__.py +0 -0
  127. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/latency_tracker/__init__.py +0 -0
  128. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/latency_tracker/pipeline.py +0 -0
  129. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/llm_scheduler/__init__.py +0 -0
  130. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/llm_scheduler/api.py +0 -0
  131. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/llm_scheduler/code.py +0 -0
  132. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/llm_scheduler/core.py +0 -0
  133. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/llm_scheduler/local.py +0 -0
  134. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/llm_scheduler/pipeline.py +0 -0
  135. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/llm_scheduler/types.py +0 -0
  136. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/llm_wrapper/adapter.py +0 -0
  137. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/llm_wrapper/components/__init__.py +0 -0
  138. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/llm_wrapper/components/adapter_types.py +0 -0
  139. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/llm_wrapper/components/base.py +0 -0
  140. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/llm_wrapper/components/features.py +0 -0
  141. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/llm_wrapper/components/litellm_wrapper.py +0 -0
  142. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/llm_wrapper/components/openrouter_wrapper.py +0 -0
  143. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/llm_wrapper/modes.py +0 -0
  144. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/llm_wrapper/pipeline.py +0 -0
  145. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/llm_wrapper/utils/__init__.py +0 -0
  146. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/llm_wrapper/utils/registry.py +0 -0
  147. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/llm_wrapper/utils/token_counting.py +0 -0
  148. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/local_llm/__init__.py +0 -0
  149. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/local_llm/components/__init__.py +0 -0
  150. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/local_llm/components/huggingface_local.py +0 -0
  151. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/local_llm/components/local_llm.py +0 -0
  152. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/logging_setup.py +0 -0
  153. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/memory/__init__.py +0 -0
  154. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/memory/components/__init__.py +0 -0
  155. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/memory/components/lru_k.py +0 -0
  156. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/memory/components/manager.py +0 -0
  157. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/memory/components/note.py +0 -0
  158. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/memory/components/storage_manager.py +0 -0
  159. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/memory/pipeline.py +0 -0
  160. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/memory/utils/__init__.py +0 -0
  161. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/memory/utils/keyword_overlap.py +0 -0
  162. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/memory_tracker/__init__.py +0 -0
  163. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/memory_tracker/components/__init__.py +0 -0
  164. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/memory_tracker/components/process.py +0 -0
  165. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/memory_tracker/pipeline.py +0 -0
  166. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/metrics/__init__.py +0 -0
  167. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/metrics/components/__init__.py +0 -0
  168. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/metrics/components/collector.py +0 -0
  169. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/metrics/components/exporter.py +0 -0
  170. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/metrics/pipeline.py +0 -0
  171. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/metrics/utils/__init__.py +0 -0
  172. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/notes_manager/__init__.py +0 -0
  173. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/notes_manager/components/__init__.py +0 -0
  174. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/notes_manager/components/privacy.py +0 -0
  175. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/notes_manager/components/recall.py +0 -0
  176. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/notes_manager/components/tidy.py +0 -0
  177. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/notes_manager/pipeline.py +0 -0
  178. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/orchestrator/__init__.py +0 -0
  179. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/orchestrator/components/__init__.py +0 -0
  180. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/orchestrator/components/agent.py +0 -0
  181. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/orchestrator/components/cgroup_manager.py +0 -0
  182. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/orchestrator/components/context_interrupt.py +0 -0
  183. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/orchestrator/components/limits.py +0 -0
  184. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/orchestrator/components/scheduler.py +0 -0
  185. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/orchestrator/components/syscall_scheduler.py +0 -0
  186. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/orchestrator/pipeline.py +0 -0
  187. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/orchestrator/utils/__init__.py +0 -0
  188. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/orchestrator/utils/device.py +0 -0
  189. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/orchestrator/utils/protocol.py +0 -0
  190. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/os_kernel/__init__.py +0 -0
  191. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/os_kernel/filesystem.py +0 -0
  192. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/os_kernel/network.py +0 -0
  193. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/os_kernel/pipeline.py +0 -0
  194. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/os_kernel/sysinfo.py +0 -0
  195. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/paths.py +0 -0
  196. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/pipeline.py +0 -0
  197. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/py.typed +0 -0
  198. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/redact.py +0 -0
  199. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/sdk/__init__.py +0 -0
  200. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/sdk/adapters.py +0 -0
  201. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/sdk/agent.py +0 -0
  202. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/sdk/components/__init__.py +0 -0
  203. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/sdk/components/autogen_adapter.py +0 -0
  204. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/sdk/components/autogen_client.py +0 -0
  205. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/sdk/components/framework_adapter_base.py +0 -0
  206. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/sdk/components/metagpt_adapter.py +0 -0
  207. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/sdk/components/open_interpreter_adapter.py +0 -0
  208. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/sdk/components/tool_catalog.py +0 -0
  209. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/sdk/os_api.py +0 -0
  210. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/sdk/patterns.py +0 -0
  211. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/sdk/pipeline.py +0 -0
  212. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/sdk/queries.py +0 -0
  213. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/sdk/savers.py +0 -0
  214. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/sdk/utils/__init__.py +0 -0
  215. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/storage_manager/__init__.py +0 -0
  216. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/storage_manager/components/__init__.py +0 -0
  217. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/storage_manager/components/base.py +0 -0
  218. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/storage_manager/components/local_file_backend.py +0 -0
  219. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/storage_manager/components/storage_queue.py +0 -0
  220. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/storage_manager/components/vector_store_backend.py +0 -0
  221. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/storage_manager/pipeline.py +0 -0
  222. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/storage_manager/utils/__init__.py +0 -0
  223. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/storage_manager/utils/memory_adapter.py +0 -0
  224. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/storage_manager/utils/sdk.py +0 -0
  225. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/studio/__init__.py +0 -0
  226. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/studio/builder.py +0 -0
  227. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/studio/catalog.py +0 -0
  228. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/studio/checks.py +0 -0
  229. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/studio/clients.py +0 -0
  230. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/studio/editor.py +0 -0
  231. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/studio/files.py +0 -0
  232. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/studio/models.py +0 -0
  233. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/studio/plan.py +0 -0
  234. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/studio/planner.py +0 -0
  235. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/studio/policy.py +0 -0
  236. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/task_router/__init__.py +0 -0
  237. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/task_router/components/__init__.py +0 -0
  238. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/task_router/components/answerer.py +0 -0
  239. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/task_router/components/base.py +0 -0
  240. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/task_router/components/classifier.py +0 -0
  241. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/task_router/components/gates.py +0 -0
  242. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/task_router/pipeline.py +0 -0
  243. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/task_router/utils/__init__.py +0 -0
  244. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/terminal/__init__.py +0 -0
  245. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/terminal/backend.py +0 -0
  246. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/terminal/clients.py +0 -0
  247. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/terminal/commands.py +0 -0
  248. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/terminal/decide.py +0 -0
  249. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/terminal/files.py +0 -0
  250. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/terminal/flow_cli.py +0 -0
  251. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/terminal/hardware.py +0 -0
  252. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/terminal/history.py +0 -0
  253. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/terminal/keys.py +0 -0
  254. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/terminal/local_models.py +0 -0
  255. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/terminal/models.py +0 -0
  256. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/terminal/policy.py +0 -0
  257. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/terminal/project.py +0 -0
  258. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/terminal/review.py +0 -0
  259. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/terminal/security.py +0 -0
  260. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/terminal/syntax.py +0 -0
  261. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/terminal/theme.py +0 -0
  262. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/token_tracker/__init__.py +0 -0
  263. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/token_tracker/pipeline.py +0 -0
  264. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/tokens.py +0 -0
  265. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/tool_catalog.py +0 -0
  266. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/tool_manager/__init__.py +0 -0
  267. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/tool_manager/builtin.py +0 -0
  268. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/tool_manager/components/__init__.py +0 -0
  269. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/tool_manager/components/base.py +0 -0
  270. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/tool_manager/components/conflict_resolver.py +0 -0
  271. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/tool_manager/components/param_validator.py +0 -0
  272. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/tool_manager/components/tool_call_queue.py +0 -0
  273. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/tool_manager/components/tool_registry.py +0 -0
  274. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/tool_manager/pipeline.py +0 -0
  275. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/tool_manager/utils/__init__.py +0 -0
  276. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/web/__init__.py +0 -0
  277. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/web/static/editor.js +0 -0
  278. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/web/static/fonts/LICENSE-Inter.txt +0 -0
  279. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/web/static/fonts/inter-400.woff2 +0 -0
  280. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/web/static/fonts/inter-500.woff2 +0 -0
  281. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/web/static/fonts/inter-600.woff2 +0 -0
  282. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/web/static/fonts/inter-700.woff2 +0 -0
  283. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/web/static/inter.css +0 -0
  284. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/web/static/pal-logo-256.png +0 -0
  285. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/web/static/pal-logo-32.png +0 -0
  286. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/web/static/pal-logo-64.png +0 -0
  287. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal/web/static/studio.js +0 -0
  288. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal_code.egg-info/dependency_links.txt +0 -0
  289. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal_code.egg-info/entry_points.txt +0 -0
  290. {pal_code-0.1.1 → pal_code-0.1.3}/src/pal_code.egg-info/top_level.txt +0 -0
  291. {pal_code-0.1.1 → pal_code-0.1.3}/tests/test_cli.py +0 -0
  292. {pal_code-0.1.1 → pal_code-0.1.3}/tests/test_config.py +0 -0
  293. {pal_code-0.1.1 → pal_code-0.1.3}/tests/test_lockfile.py +0 -0
  294. {pal_code-0.1.1 → pal_code-0.1.3}/tests/test_paths.py +0 -0
  295. {pal_code-0.1.1 → pal_code-0.1.3}/tests/test_redact.py +0 -0
  296. {pal_code-0.1.1 → pal_code-0.1.3}/tests/test_run_script.py +0 -0
  297. {pal_code-0.1.1 → pal_code-0.1.3}/tests/test_tool_catalog.py +0 -0
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2026 Nandini Singh
3
+ Copyright (c) 2026 Vrushal More
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -1,8 +1,8 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pal-code
3
- Version: 0.1.1
3
+ Version: 0.1.3
4
4
  Summary: Pal: build and plan with AI in your terminal (pal cli) or browser (pal web), on a layer that cuts tokens and latency.
5
- Author: Nandini Singh
5
+ Author: Vrushal More
6
6
  License: MIT
7
7
  Keywords: ai,coding-assistant,cli,llm,ollama,tokens,code-graph
8
8
  Classifier: Development Status :: 4 - Beta
@@ -26,6 +26,9 @@ Requires-Dist: graphifyy>=0.9
26
26
  Requires-Dist: pypdf>=4.0
27
27
  Requires-Dist: python-pptx>=0.6.23
28
28
  Requires-Dist: openpyxl>=3.1
29
+ Requires-Dist: sounddevice>=0.4
30
+ Requires-Dist: numpy>=1.24
31
+ Requires-Dist: faster-whisper>=1.0
29
32
  Provides-Extra: documents
30
33
  Requires-Dist: pymupdf4llm>=0.0.17; extra == "documents"
31
34
  Provides-Extra: graph
@@ -42,11 +45,8 @@ Provides-Extra: inprocess
42
45
  Requires-Dist: torch; extra == "inprocess"
43
46
  Requires-Dist: transformers; extra == "inprocess"
44
47
  Provides-Extra: voice
45
- Requires-Dist: sounddevice>=0.4; extra == "voice"
46
- Requires-Dist: numpy>=1.24; extra == "voice"
47
- Requires-Dist: faster-whisper>=1.0; extra == "voice"
48
48
  Provides-Extra: full
49
- Requires-Dist: pal-code[documents,voice]; extra == "full"
49
+ Requires-Dist: pal-code[documents]; extra == "full"
50
50
  Provides-Extra: dev
51
51
  Requires-Dist: pytest>=8; extra == "dev"
52
52
  Requires-Dist: ruff>=0.5; extra == "dev"
@@ -4,12 +4,12 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "pal-code"
7
- version = "0.1.1"
7
+ version = "0.1.3"
8
8
  description = "Pal: build and plan with AI in your terminal (pal cli) or browser (pal web), on a layer that cuts tokens and latency."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
11
11
  license = { text = "MIT" }
12
- authors = [{ name = "Nandini Singh" }]
12
+ authors = [{ name = "Vrushal More" }]
13
13
  keywords = ["ai", "coding-assistant", "cli", "llm", "ollama", "tokens", "code-graph"]
14
14
  classifiers = [
15
15
  "Development Status :: 4 - Beta",
@@ -32,6 +32,9 @@ dependencies = [
32
32
  "pypdf>=4.0", # PDF -> markdown
33
33
  "python-pptx>=0.6.23", # PowerPoint -> markdown
34
34
  "openpyxl>=3.1", # Excel -> markdown
35
+ "sounddevice>=0.4", # the mic: recording (PortAudio comes with it on Windows and macOS)
36
+ "numpy>=1.24",
37
+ "faster-whisper>=1.0", # speech to text on this computer (Whisper); nothing leaves the computer
35
38
  ]
36
39
 
37
40
  [project.optional-dependencies]
@@ -42,8 +45,8 @@ litellm = [] # kept so older install lines still work: litel
42
45
  vector = ["chromadb"]
43
46
  embeddings = ["sentence-transformers"]
44
47
  inprocess = ["torch", "transformers"]
45
- voice = ["sounddevice>=0.4", "numpy>=1.24", "faster-whisper>=1.0"] # talk instead of typing (Ctrl+Space); speech-to-text on this computer
46
- full = ["pal-code[documents,voice]"]
48
+ voice = [] # kept so older install lines still work: the mic is installed by default now
49
+ full = ["pal-code[documents]"]
47
50
  dev = ["pytest>=8", "ruff>=0.5", "mypy>=1.10", "types-requests", "types-PyYAML"]
48
51
 
49
52
  [project.scripts]
@@ -13,7 +13,7 @@ def _version() -> str:
13
13
  return version(name)
14
14
  except PackageNotFoundError:
15
15
  continue
16
- return "0.1.1" # a source checkout that is not installed
16
+ return "0.1.3" # a source checkout that is not installed
17
17
 
18
18
 
19
19
  __version__ = _version()
@@ -212,7 +212,8 @@ def load_dotenv(path: str | os.PathLike | None = None, env=None) -> list[str]:
212
212
  return loaded
213
213
 
214
214
 
215
- def _serve(settings: Settings, open_browser: bool = False, page: str = "", graph=None, documents: list | None = None) -> int:
215
+ def _serve(settings: Settings, open_browser: bool = False, page: str = "", graph=None, documents: list | None = None, project_dir=None,
216
+ background=None) -> int:
216
217
  from pal.chat import ChatService
217
218
  from pal.web import create_app
218
219
 
@@ -221,7 +222,10 @@ def _serve(settings: Settings, open_browser: bool = False, page: str = "", graph
221
222
  service = ChatService(settings, graph=graph) if graph is not None else ChatService(settings)
222
223
  if documents:
223
224
  service.preload_docs = list(documents)
224
- app = create_app(service)
225
+ app = create_app(service, studio={"project_dir": project_dir}) if project_dir else create_app(service)
226
+ if background is not None: # pal web: the folder's setup runs while the page is already usable
227
+ import threading as _bg
228
+ _bg.Thread(target=background, args=(service,), daemon=True, name="pal-web-setup").start()
225
229
  if s.host not in _LOOPBACK:
226
230
  print(f"WARNING: listening on {s.host}. Pal has no login: anyone who can reach this port can use "
227
231
  "your API keys' quota and read stored memory. Put it behind an authenticating reverse proxy.",
@@ -229,20 +233,21 @@ def _serve(settings: Settings, open_browser: bool = False, page: str = "", graph
229
233
  shown = "localhost" if s.host in ("0.0.0.0", "::") else s.host # noqa: S104
230
234
  url = f"http://{shown}:{s.port}" + ("/" + page.strip("/") if page else "")
231
235
  print(f"Pal {__version__} on {url} (Ctrl+C to stop)")
232
- from pal.chat.providers import API_PROVIDERS
233
- keys = [p for p, var in API_PROVIDERS.items() if os.environ.get(var)]
234
- print("API key found for: " + ", ".join(keys) if keys else
235
- "No API key in the environment: paste one in the page (API key menu), or set e.g. GEMINI_API_KEY.")
236
- status = service.local_status()
237
- if status["ok"] is False:
238
- print(f"WARNING: local model: {status['detail']}. Input reduction and local answers are off until it runs; "
239
- "the app still works and sends everything to the API model.", file=sys.stderr)
240
- else:
241
- print(f"Local model: {status['detail']}")
236
+ if background is None: # pal serve: say what is set up; pal web says nothing more and starts at once
237
+ from pal.chat.providers import API_PROVIDERS
238
+ keys = [p for p, var in API_PROVIDERS.items() if os.environ.get(var)]
239
+ print("API key found for: " + ", ".join(keys) if keys else
240
+ "No API key in the environment: paste one in the page (API key menu), or set e.g. GEMINI_API_KEY.")
241
+ status = service.local_status()
242
+ if status["ok"] is False:
243
+ print(f"WARNING: local model: {status['detail']}. Input reduction and local answers are off until it runs; "
244
+ "the app still works and sends everything to the API model.", file=sys.stderr)
245
+ else:
246
+ print(f"Local model: {status['detail']}")
242
247
  if open_browser:
243
248
  import threading
244
249
  import webbrowser
245
- threading.Timer(1.2, lambda: webbrowser.open(url)).start()
250
+ threading.Timer(0.3, lambda: webbrowser.open(url)).start()
246
251
  try:
247
252
  try:
248
253
  from waitress import serve
@@ -0,0 +1,42 @@
1
+ """How many tokens a model can take in one request (its context window), for the "tokens left" counters in the terminal and the web page.
2
+
3
+ API models: the size LiteLLM knows for the model, else a safe default. Local models: the context Pal gives Ollama (num_ctx), which is what
4
+ really limits them here, whatever the model could do on paper."""
5
+ from __future__ import annotations
6
+
7
+ from functools import lru_cache
8
+
9
+ LOCAL_CONTEXT = 8192 # Pal runs local models with num_ctx 8192 (pal.terminal.backend.make_core)
10
+ API_DEFAULT = 128_000 # a model LiteLLM does not know: most current API models take at least this
11
+ LOCAL_PROVIDERS = ("local", "ollama", "lmstudio", "vllm", "llamacpp", "llama.cpp", "huggingface")
12
+
13
+
14
+ @lru_cache(maxsize=256)
15
+ def context_window(provider: str, model: str) -> int:
16
+ provider = (provider or "").lower()
17
+ if provider in LOCAL_PROVIDERS:
18
+ return LOCAL_CONTEXT
19
+ if not model:
20
+ return API_DEFAULT
21
+ try:
22
+ import litellm
23
+ for name in (f"{provider}/{model}" if provider else model, model, model.split("/")[-1]):
24
+ try:
25
+ info = litellm.get_model_info(name)
26
+ except Exception: # noqa: BLE001 (not in LiteLLM's table under this name)
27
+ continue
28
+ size = info.get("max_input_tokens") or info.get("max_tokens")
29
+ if size:
30
+ return int(size)
31
+ except Exception: # noqa: BLE001 (LiteLLM missing or broken: the default)
32
+ pass
33
+ return API_DEFAULT
34
+
35
+
36
+ def tokens_left(window: int, last_prompt: int) -> int:
37
+ """What is left of the window after the last request's input (the next one is usually at least as big)."""
38
+ return max(0, window - max(0, last_prompt))
39
+
40
+
41
+ def short(n: int) -> str:
42
+ return f"{n / 1000:.1f}k".replace(".0k", "k") if n >= 10_000 else f"{n:,}"
@@ -0,0 +1,34 @@
1
+ """The model layer. Its names are loaded on first use (PEP 562), so importing a light submodule such as pal.llm_wrapper.modes (the provider
2
+ names and key variables) does not load the whole model stack: pal cli draws its first screen before any of it is needed."""
3
+ from importlib import import_module
4
+
5
+ _WHERE = {
6
+ "LLMAdapter": "pal.llm_wrapper.adapter",
7
+ "LLMAdapterResponse": "pal.llm_wrapper.components.adapter_types",
8
+ "LLMAdapterSyscall": "pal.llm_wrapper.components.adapter_types",
9
+ "LLMConfig": "pal.llm_wrapper.components.adapter_types",
10
+ "RouterStrategy": "pal.llm_wrapper.components.adapter_types",
11
+ "DeploymentType": "pal.llm_wrapper.components.base",
12
+ "LLMResponse": "pal.llm_wrapper.components.base",
13
+ "LLMSyscall": "pal.llm_wrapper.components.base",
14
+ "LLMWrapper": "pal.llm_wrapper.components.base",
15
+ "WrapperCapabilities": "pal.llm_wrapper.components.base",
16
+ "WrapperConfig": "pal.llm_wrapper.components.base",
17
+ "LLMWrapperPipeline": "pal.llm_wrapper.pipeline",
18
+ "available_backends": "pal.llm_wrapper.utils.registry",
19
+ "build_wrapper": "pal.llm_wrapper.utils.registry",
20
+ }
21
+
22
+ __all__ = list(_WHERE)
23
+
24
+
25
+ def __getattr__(name: str):
26
+ if name in _WHERE:
27
+ value = getattr(import_module(_WHERE[name]), name)
28
+ globals()[name] = value
29
+ return value
30
+ raise AttributeError(f"module 'pal.llm_wrapper' has no attribute {name!r}")
31
+
32
+
33
+ def __dir__():
34
+ return sorted(set(globals()) | set(_WHERE))
@@ -49,7 +49,7 @@ def clean_references(raw) -> list[dict]:
49
49
 
50
50
 
51
51
  def register_studio(app, service, *, planner_core: Callable | None = None, core_factory: Callable | None = None, discover_fn: Callable | None = None,
52
- hot_turns: int | None = None) -> RunStore:
52
+ hot_turns: int | None = None, project_dir: Path | None = None) -> RunStore:
53
53
  """Add the Studio page and endpoints to the app. The three callables are for tests: the planner's model, the builder's model, local-model discovery."""
54
54
  cfg = service.settings
55
55
  home = Path(cfg.storage_dir).expanduser() if cfg.storage_dir else data_dir()
@@ -71,7 +71,7 @@ def register_studio(app, service, *, planner_core: Callable | None = None, core_
71
71
  raise UserInputError("no local model is chosen: start Ollama and pull a model, or switch to an API model")
72
72
  return local_core(m["model"], num_ctx())
73
73
 
74
- runs = RunStore(home / "studio", core_factory or default_core_factory, max_runs=cfg.studio.max_runs, hot_turns=hot_turns)
74
+ runs = RunStore(home / "studio", core_factory or default_core_factory, max_runs=cfg.studio.max_runs, hot_turns=hot_turns, project_dir=project_dir)
75
75
  app.extensions["studio_runs"] = runs
76
76
 
77
77
  def local() -> dict:
@@ -95,9 +95,22 @@ class Run:
95
95
  return self.arms[name]
96
96
 
97
97
 
98
+ def build_target(project: Path, folder: str) -> Path:
99
+ """Where a build is written in the folder Pal was started in: the folder itself when it is empty, else a new subfolder named after the
100
+ product (with -2, -3 ... when that name is taken), so nothing that is already there is overwritten."""
101
+ project = Path(project)
102
+ if not any(p for p in project.iterdir() if not p.name.startswith(".")):
103
+ return project
104
+ target, n = project / folder, 2
105
+ while target.exists() and (not target.is_dir() or any(target.iterdir())):
106
+ target, n = project / f"{folder}-{n}", n + 1
107
+ return target
108
+
109
+
98
110
  class RunStore:
99
- def __init__(self, root: Path, core_factory: Callable, max_runs: int = 6, hot_turns: int | None = None):
111
+ def __init__(self, root: Path, core_factory: Callable, max_runs: int = 6, hot_turns: int | None = None, project_dir: Path | None = None):
100
112
  self.root, self.core_factory, self.max_runs, self.hot_turns = Path(root), core_factory, max_runs, hot_turns
113
+ self.project_dir = Path(project_dir).resolve() if project_dir else None # pal web's folder: the build is written there
101
114
  self._runs: dict[str, Run] = {}
102
115
  self._editors: dict[tuple[str, str], EditorSession] = {}
103
116
  self._lock = threading.Lock()
@@ -140,11 +153,17 @@ class RunStore:
140
153
  break
141
154
  policy = Policy(dict(plan["permissions"]))
142
155
  save = plan["permissions"].get("write_project")
143
- state = ArmState(arm, label, (self.root / run.id / arm / plan["product"]["folder"]) if save else None)
156
+ where = None
157
+ if save:
158
+ where = (build_target(self.project_dir, plan["product"]["folder"]) if self.project_dir and arm != "baseline"
159
+ else self.root / run.id / arm / plan["product"]["folder"]) # the plain client's comparison copy stays in Pal's folder
160
+ state = ArmState(arm, label, where)
144
161
  run.arms[arm] = state
145
162
  if state.workspace:
146
163
  state.workspace.mkdir(parents=True, exist_ok=True)
147
164
  run.emit("arm_start", f"{label}: starting" if twice else "Starting", arm=arm)
165
+ if state.workspace and self.project_dir and arm != "baseline":
166
+ run.emit("saved_to", f"Files are written to {state.workspace}", arm=arm, path=str(state.workspace))
148
167
  client: PlainClient | PalClient
149
168
  if arm == "baseline":
150
169
  client = PlainClient(core, SYSTEM)
@@ -67,6 +67,7 @@ class BenchResult:
67
67
  plain: Arm
68
68
  pal: Arm
69
69
  when: float = field(default_factory=time.time)
70
+ pal_files: dict = field(default_factory=dict, repr=False) # what Pal's way wrote: {path: bytes}, offered to keep in your folder
70
71
 
71
72
  @property
72
73
  def saved(self) -> int:
@@ -78,6 +79,7 @@ class BenchResult:
78
79
 
79
80
  def to_dict(self) -> dict:
80
81
  d = asdict(self)
82
+ d.pop("pal_files", None)
81
83
  d.update(saved=self.saved, saved_pct=self.saved_pct, plain_total=self.plain.total, pal_total=self.pal.total)
82
84
  return d
83
85
 
@@ -161,7 +163,12 @@ def run_bench(folder: Path, task: str, core_factory: Callable[[], Any], model: s
161
163
  pal_arm = _run_arm("pal", pal, steps, progress)
162
164
  finally:
163
165
  client.close()
164
- result = BenchResult(task, model, str(folder), plain_arm, pal_arm)
166
+ kept = {}
167
+ for rel in sorted({f for st in pal_arm.steps for f in st.files}):
168
+ f = pal_dir / rel
169
+ if f.is_file():
170
+ kept[rel] = f.read_bytes()
171
+ result = BenchResult(task, model, str(folder), plain_arm, pal_arm, pal_files=kept)
165
172
  save(result, home)
166
173
  return result
167
174