Compare commits

...
4 Commits
Author SHA1 Message Date
f79770cdb0 release: prepare MemHub 1.0.2 (#19)
Co-authored-by: alvinfei <alvinfei@tencent.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-17 01:54:14 +08:00
9ba602ffd2 ci: upload tagged Linux runtimes to draft releases (#16)
Co-authored-by: alvinfei <alvinfei@tencent.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-17 01:51:58 +08:00
842fbe6044 fix(memhub): relaunch safely with existing runtimes (#18)
Co-authored-by: alvinfei <alvinfei@tencent.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-17 01:50:23 +08:00
268ad16d26 release: prepare Hy Memory 2.0.2 and MemHub 1.0.1 (#15)
* release: prepare Hy Memory 2.0.2 and MemHub 1.0.1

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(memhub): retry grant reads across atomic replacement

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: alvinfei <alvinfei@tencent.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-17 00:25:43 +08:00
51 changed files with 603 additions and 169 deletions

No files matched your search

+62
View File
@@ -102,3 +102,65 @@ jobs:
if-no-files-found: error
compression-level: 0
retention-days: 14
publish-draft-release:
if: ${{ inputs.run_lifecycle && startsWith(github.ref, 'refs/tags/v') }}
needs: matrix
runs-on: ubuntu-24.04
permissions:
contents: write
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262
with:
persist-credentials: false
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093
with:
pattern: memhub-runtime-linux-*
path: memhub/release-assets
merge-multiple: true
- name: Verify tag and Linux release assets
shell: bash
run: |
set -euo pipefail
version=$(node -p 'require("./package.json").version')
node scripts/verify-release-version.mjs \
--channel stable \
--version "$version" \
--build-number "$(node -p 'require("./package.json").release.build_number')" \
--tag "$GITHUB_REF_NAME"
for arch in x64 arm64; do
archive="release-assets/memhub-runtime-v${version}-linux-${arch}.tar.gz"
test -f "$archive"
test -f "$archive.sha256"
(
cd release-assets
sha256sum -c "$(basename "$archive.sha256")"
)
done
- name: Upload Linux assets to a draft GitHub Release
env:
GH_TOKEN: ${{ github.token }}
shell: bash
run: |
set -euo pipefail
if gh release view "$GITHUB_REF_NAME" \
--repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then
test "$(
gh release view "$GITHUB_REF_NAME" \
--repo "$GITHUB_REPOSITORY" \
--json isDraft \
--jq .isDraft
)" = "true"
else
gh release create "$GITHUB_REF_NAME" \
--repo "$GITHUB_REPOSITORY" \
--verify-tag \
--draft \
--generate-notes \
--title "$GITHUB_REF_NAME"
fi
gh release upload "$GITHUB_REF_NAME" \
release-assets/memhub-runtime-v*-linux-*.tar.gz \
release-assets/memhub-runtime-v*-linux-*.tar.gz.sha256 \
--repo "$GITHUB_REPOSITORY" \
--clobber
+23 -8
View File
@@ -2,6 +2,12 @@
Hy Memory and MemHub are versioned and released independently.
## Hy Memory 2.0.2 — 2026-10-17
- Published
[`hy-memory==2.0.2`](https://pypi.org/project/hy-memory/2.0.2/) with the
current SDK fixes and Apache-2.0 package metadata.
## Hy Memory 2.0.1 — 2026-10-16
- Published the Python distribution as
@@ -10,13 +16,22 @@ Hy Memory and MemHub are versioned and released independently.
Because PyPI release metadata is immutable, the next SDK patch release must
carry the updated license metadata; `2.0.1` retains its published metadata.
## MemHub
## MemHub 1.0.2 — Unreleased
MemHub has no public release yet. The source version is `1.0.0` (build 4), the
selected npm package is `memhub-cli`, and both npm publication and GitHub
Release artifacts are still pending. Internal release-candidate builds are not
public releases.
- Updated the source version to `1.0.2` (build 6).
- Pinned the public SDK to `hy-memory==2.0.2`.
- Pinned `onnxruntime==1.23.2` to retain macOS x64 packaging support.
- Fixed relaunch and upgrade failures when a prior managed Runtime exists.
- Embedded canonical build identity when an external packaging pipeline omits
release environment fields.
- Added automatic Linux Runtime upload to tagged Draft Releases.
No compatibility or deprecation policy has been adopted beyond the explicit
CLI/Runtime protocol ranges stored in the release manifests. Future public
releases must record user-visible changes and supported platforms here.
## MemHub 1.0.1 — Superseded before release
- The candidate tag was not published as a GitHub Release; `1.0.2` replaces it
after the managed-Runtime relaunch fix.
## MemHub 1.0.0
- Published the initial GitHub Release with macOS arm64 and Linux Runtime
artifacts.
+8 -8
View File
@@ -32,7 +32,7 @@ English | [中文](README.zh.md)
## Latest News
* *<mark>[new]</mark>* 🔥🔥🔥 **[2026-10]**: 🚀 **Released** **Hy Memory 2.0 (`hy-memory==2.0.1`)** on [PyPI](https://pypi.org/project/hy-memory/2.0.1/) and open-sourced the **Hy-Memory** monorepo together with **[MemHub](memhub/README.md)**!
* *<mark>[new]</mark>* 🔥🔥🔥 **[2026-10]**: 🚀 **Released** **Hy Memory 2.0 (`hy-memory==2.0.2`)** on [PyPI](https://pypi.org/project/hy-memory/2.0.2/) and open-sourced the **Hy-Memory** monorepo together with **[MemHub](memhub/README.md)**!
* *<mark>[new]</mark>* 🔥🔥🔥 **[2026-10]**: 🚀 **Open-sourced** **[Hy-Memory-30B-A3B](https://huggingface.co/tencent/Hy-Memory-30B-A3B)** on Hugging Face along with its [technical report](https://arxiv.org/abs/2610.22196) on attributed reinforcement learning for long-term memory writing!
* *<mark>[new]</mark>* 🔥🔥 **[2026-09]**: 📊 **Released** **[CMB: A Coding Memory Benchmark for Long-Horizon and Interleaved Coding Agents](docs/papers/CMB_A_Coding_Memory_Benchmark_for_Long_Horizon_and_Interleaved_Coding_Agents.pdf)** (submitted to **ICLR 2027**).
* *<mark>[new]</mark>* 🔥🔥 **[2026-08]**: 🎉 **Accepted** at **EMNLP 2026**: **[Memory Beyond Recall: A Dual-Process Cognitive Memory System for Self-Evolving LLM Agents](https://arxiv.org/abs/2606.09483)**!
@@ -63,13 +63,13 @@ Welcome to the **Hy Memory Research Family**, covering cognitive chat memory, lo
### 1. Installation
```bash
pip install hy-memory==2.0.1 # core (includes ChromaDB and Kuzu)
pip install "hy-memory[qdrant]==2.0.1" # + Qdrant
pip install "hy-memory[faiss]==2.0.1" # + FAISS
pip install "hy-memory[graph]==2.0.1" # + Neo4j
pip install "hy-memory[mysql]==2.0.1" # + MySQL cache
pip install "hy-memory[rerank]==2.0.1" # + local multilingual cross-encoder rerank
pip install "hy-memory[all]==2.0.1" # everything
pip install hy-memory==2.0.2 # core (includes ChromaDB and Kuzu)
pip install "hy-memory[qdrant]==2.0.2" # + Qdrant
pip install "hy-memory[faiss]==2.0.2" # + FAISS
pip install "hy-memory[graph]==2.0.2" # + Neo4j
pip install "hy-memory[mysql]==2.0.2" # + MySQL cache
pip install "hy-memory[rerank]==2.0.2" # + local multilingual cross-encoder rerank
pip install "hy-memory[all]==2.0.2" # everything
```
### 2. Configuration
+8 -8
View File
@@ -32,7 +32,7 @@
## 最新动态
* *<mark>[new]</mark>* 🔥🔥🔥 **[2026-10]**: 🚀 **正式发布** **Hy Memory 2.0 (`hy-memory==2.0.1`)**(已上线 [PyPI](https://pypi.org/project/hy-memory/2.0.1/)),并开源 **Hy-Memory** monorepo 与 **[MemHub](memhub/README.md)**!
* *<mark>[new]</mark>* 🔥🔥🔥 **[2026-10]**: 🚀 **正式发布** **Hy Memory 2.0 (`hy-memory==2.0.2`)**(已上线 [PyPI](https://pypi.org/project/hy-memory/2.0.2/)),并开源 **Hy-Memory** monorepo 与 **[MemHub](memhub/README.md)**!
* *<mark>[new]</mark>* 🔥🔥🔥 **[2026-10]**: 🚀 **模型开源** 在 Hugging Face 发布 **[Hy-Memory-30B-A3B](https://huggingface.co/tencent/Hy-Memory-30B-A3B)** 长期记忆写入模型及[技术报告](https://arxiv.org/abs/2610.22196)!
* *<mark>[new]</mark>* 🔥🔥 **[2026-09]**: 📊 **基准与方法发布** 推出长周期与交错式编程记忆评测基准 **[CMB: A Coding Memory Benchmark for Long-Horizon and Interleaved Coding Agents](docs/papers/CMB_A_Coding_Memory_Benchmark_for_Long_Horizon_and_Interleaved_Coding_Agents.pdf)**(已投稿 **ICLR 2027**)。
* *<mark>[new]</mark>* 🔥🔥 **[2026-08]**: 🎉 **论文录用** 双系统认知记忆架构论文 **[Memory Beyond Recall: A Dual-Process Cognitive Memory System for Self-Evolving LLM Agents](https://arxiv.org/abs/2606.09483)** 被 **EMNLP 2026** 接收!
@@ -63,13 +63,13 @@
### 1. 安装
```bash
pip install hy-memory==2.0.1 # 核心(含 ChromaDB 与 Kuzu)
pip install "hy-memory[qdrant]==2.0.1" # + Qdrant
pip install "hy-memory[faiss]==2.0.1" # + FAISS
pip install "hy-memory[graph]==2.0.1" # + Neo4j
pip install "hy-memory[mysql]==2.0.1" # + MySQL 缓存
pip install "hy-memory[rerank]==2.0.1" # + 本地多语言 cross-encoder rerank
pip install "hy-memory[all]==2.0.1" # 全部
pip install hy-memory==2.0.2 # 核心(含 ChromaDB 与 Kuzu)
pip install "hy-memory[qdrant]==2.0.2" # + Qdrant
pip install "hy-memory[faiss]==2.0.2" # + FAISS
pip install "hy-memory[graph]==2.0.2" # + Neo4j
pip install "hy-memory[mysql]==2.0.2" # + MySQL 缓存
pip install "hy-memory[rerank]==2.0.2" # + 本地多语言 cross-encoder rerank
pip install "hy-memory[all]==2.0.2" # 全部
```
### 2. 配置
+1 -1
View File
@@ -37,7 +37,7 @@ do not belong in the public source tree.
- Root GitHub workflows run SDK and MemHub checks from their respective working
directories. Native lifecycle workflows produce reviewable Actions artifacts;
tag releases remain fail-closed on public identity, signing and notarization.
- Hy Memory is published as `hy-memory==2.0.1`. MemHub pins that public
- Hy Memory is published as `hy-memory==2.0.2`. MemHub pins that public
distribution; its selected npm package is `memhub-cli`, which is not yet
published.
- Hy-Memory source uses Apache-2.0 at the repository root. Third-party
+1 -1
View File
@@ -5,7 +5,7 @@
## 安装与快速开始
```bash
pip install hy-memory==2.0.1
pip install hy-memory==2.0.2
# 可选后端
pip install 'hy-memory[qdrant]==2.0.1'
+36 -2
View File
@@ -15,12 +15,12 @@ write、search,并把搜索结果格式化为可交给模型的 memory text ch
```text
distribution: hy-memory
version: 2.0.1
version: 2.0.2
mode: 按运行时配置选择
reader: hybrid
```
仓库 pin、lockfile 和运行时版本守卫均固定到公开的 `hy-memory==2.0.1`。
仓库 pin、lockfile 和运行时版本守卫均固定到公开的 `hy-memory==2.0.2`。
MemHub 在本地 Runtime 内嵌入 SDK,但不绕过自身的来源授权、模型授权、Evidence、
Governance 和撤权边界。
@@ -37,6 +37,40 @@ MemHub 对 SDK 使用以下公开、可复现的本地策略:
- API Key 进入系统安全存储,不写入仓库、安装包、日志或 HY metadata;
- 服务端数据库、租户队列及宿主 shell 中的 ambient 凭据不会进入 SDK config。
发布包固定应用以下 SDK 行为配置:
| 环境变量 | 值 |
|---|---|
| `MEMORY_MODE` | `ultra` |
| `MEMORY_ENABLE_GRAPH` | `true` |
| `MEMORY_GRAPH_RERANK_ENABLED` | `false` |
| `MEMORY_GRAPH_TEMPORAL_SPINE` | `true` |
| `MEMORY_TOPIC_SUMMARY_ENABLED` | `false` |
| `MEMORY_EPISODE_SUMMARY_ENABLED` | `false` |
| `MEMORY_EPISODE_RECALL_ENABLED` | `true` |
| `MEMORY_DYNAMIC_EPISODE_ENABLED` | `true` |
| `MEMORY_SESSION_EPISODE_ENABLED` | `false` |
| `MEMORY_SESSION_EPISODE_CONTEXT_ENABLED` | `false` |
| `MEMORY_EPISODE_CAPSULE_CONTEXT_ENABLED` | `true` |
| `MEMORY_EPISODE_CAPSULE_QUERY_MIN` | `0.65` |
| `MEMORY_EPISODE_CAPSULE_ASSIGNMENT_MIN` | `0.60` |
| `MEMORY_EPISODE_CAPSULE_FIRST_RANK_MAX` | `3` |
| `MEMORY_EPISODE_CAPSULE_MEMBER_LIMIT` | `3` |
| `MEMORY_ENABLE_INTENTION` | `false` |
| `MEMORY_ENABLE_QUERY_REWRITE` | `false` |
| `MEMORY_FEW_SHOT_ENABLED` | `true` |
| `MEMORY_HYBRID_V2_QUERY_EXPAND` | `1` |
| `MEMORY_HYBRID_V2_GEO_INTENT_PRIOR` | `0.6` |
| `MEMORY_HYBRID_V2_DIVERSITY` | `1` |
| `MEMORY_HYBRID_V2_TEMPORAL_ADMIT_PRIOR` | `0.55` |
| `MEMORY_HYBRID_V2_TEMPORAL_BOOST` | `0.18` |
| `MEMORY_CROSS_DOMAIN_CON_THRESHOLD` | `0.5` |
| `MEMORY_READER_ENABLE_SUMMARY` | `false` |
| `MEMORY_SUMMARY_ENABLED_IN_SYS2` | `false` |
| `MEMORY_LOCAL_RERANK_ENABLED` | `false` |
| `RECONCILE_ENABLED` | `true` |
| `HY_MEMORY_READER` | `hybrid` |
这些默认值集中在
`apps/core/src/one_memory/hy_runtime_config.py`,并由发布测试和 lockfile 共同约束。
独立脚本若绕过 MemHub 直接导入 SDK,应自行显式配置存储目录和模型端点。
+4 -4
View File
@@ -6,13 +6,13 @@ SDK 和记忆引擎。One Memory / `one-memory` 是历史模块、数据目录
## 发行与平台合同
公共 Python SDK 为 `hy-memory==2.0.1`。MemHub 源码版本为 `1.0.0`
(build 4),GitHub 仓库为
公共 Python SDK 为 `hy-memory==2.0.2`。MemHub 源码版本为 `1.0.2`
(build 6),GitHub 仓库为
[`Tencent-Hunyuan/Hy-Memory`](https://github.com/Tencent-Hunyuan/Hy-Memory),npm 包名为
`memhub-cli`。当前代码架构见
[`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md)。
`v1.0.0` 的发行目标是 Linux x64/arm64 Runtime 与 macOS arm64/x64 App 和 Runtime。
`v1.0.2` 的发行目标是 Linux x64/arm64 Runtime 与 macOS arm64/x64 App 和 Runtime。
Linux 制品由 GitHub Actions 在原生架构 runner 上构建并完成生命周期验收;macOS
制品由发行流水线完成 Developer ID 签名、公证和验收后手动上传。Windows 不支持。
只有同时出现在 GitHub Release 与 `release-manifest.json` 中、且通过公开校验的制品才属于
@@ -99,7 +99,7 @@ MemHub 的 SQLite 数据默认位于 `~/.one-memory`,不在 npm 包或 Runtime
## Hy Memory
当前开发依赖固定为公开 PyPI 包 `hy-memory==2.0.1`。SDK 配置、权限边界及 search
当前开发依赖固定为公开 PyPI 包 `hy-memory==2.0.2`。SDK 配置、权限边界及 search
返回格式见 [`MEMORY.md`](MEMORY.md)。HY search 返回的 `content` 已包含 SDK 统一生成的
逻辑时间和演化上下文;调用方不应再次转换时间戳或重复拼接 `When`。
+94 -6
View File
@@ -479,6 +479,23 @@
}
]
},
{
"SPDXID": "SPDXRef-Package-pypi-coloredlogs",
"name": "coloredlogs",
"versionInfo": "15.0.1",
"downloadLocation": "NOASSERTION",
"filesAnalyzed": false,
"licenseConcluded": "NOASSERTION",
"licenseDeclared": "NOASSERTION",
"copyrightText": "NOASSERTION",
"externalRefs": [
{
"referenceCategory": "PACKAGE-MANAGER",
"referenceType": "purl",
"referenceLocator": "pkg:pypi/coloredlogs@15.0.1"
}
]
},
{
"SPDXID": "SPDXRef-Package-pypi-distro",
"name": "distro",
@@ -751,6 +768,23 @@
}
]
},
{
"SPDXID": "SPDXRef-Package-pypi-humanfriendly",
"name": "humanfriendly",
"versionInfo": "10.0",
"downloadLocation": "NOASSERTION",
"filesAnalyzed": false,
"licenseConcluded": "NOASSERTION",
"licenseDeclared": "NOASSERTION",
"copyrightText": "NOASSERTION",
"externalRefs": [
{
"referenceCategory": "PACKAGE-MANAGER",
"referenceType": "purl",
"referenceLocator": "pkg:pypi/humanfriendly@10.0"
}
]
},
{
"SPDXID": "SPDXRef-Package-pypi-idna",
"name": "idna",
@@ -1125,6 +1159,23 @@
}
]
},
{
"SPDXID": "SPDXRef-Package-pypi-mpmath",
"name": "mpmath",
"versionInfo": "1.3.0",
"downloadLocation": "NOASSERTION",
"filesAnalyzed": false,
"licenseConcluded": "NOASSERTION",
"licenseDeclared": "NOASSERTION",
"copyrightText": "NOASSERTION",
"externalRefs": [
{
"referenceCategory": "PACKAGE-MANAGER",
"referenceType": "purl",
"referenceLocator": "pkg:pypi/mpmath@1.3.0"
}
]
},
{
"SPDXID": "SPDXRef-Package-pypi-multidict",
"name": "multidict",
@@ -1230,7 +1281,7 @@
{
"SPDXID": "SPDXRef-Package-pypi-onnxruntime",
"name": "onnxruntime",
"versionInfo": "1.29.0",
"versionInfo": "1.23.2",
"downloadLocation": "NOASSERTION",
"filesAnalyzed": false,
"licenseConcluded": "NOASSERTION",
@@ -1240,7 +1291,7 @@
{
"referenceCategory": "PACKAGE-MANAGER",
"referenceType": "purl",
"referenceLocator": "pkg:pypi/onnxruntime@1.29.0"
"referenceLocator": "pkg:pypi/onnxruntime@1.23.2"
}
]
},
@@ -1992,6 +2043,23 @@
}
]
},
{
"SPDXID": "SPDXRef-Package-pypi-sympy",
"name": "sympy",
"versionInfo": "1.14.0",
"downloadLocation": "NOASSERTION",
"filesAnalyzed": false,
"licenseConcluded": "NOASSERTION",
"licenseDeclared": "NOASSERTION",
"copyrightText": "NOASSERTION",
"externalRefs": [
{
"referenceCategory": "PACKAGE-MANAGER",
"referenceType": "purl",
"referenceLocator": "pkg:pypi/sympy@1.14.0"
}
]
},
{
"SPDXID": "SPDXRef-Package-pypi-tenacity",
"name": "tenacity",
@@ -2267,12 +2335,12 @@
{
"SPDXID": "SPDXRef-Package-hy-memory",
"name": "hy-memory",
"versionInfo": "2.0.1",
"versionInfo": "2.0.2",
"downloadLocation": "NOASSERTION",
"filesAnalyzed": false,
"licenseConcluded": "MIT",
"licenseDeclared": "MIT",
"copyrightText": "Copyright (c) 2025 HY Memory Contributors"
"licenseConcluded": "Apache-2.0",
"licenseDeclared": "Apache-2.0",
"copyrightText": "Copyright (C) 2026 Tencent. All rights reserved."
},
{
"SPDXID": "SPDXRef-Package-memmy-agent-derived",
@@ -2427,6 +2495,11 @@
"relationshipType": "DEPENDS_ON",
"relatedSpdxElement": "SPDXRef-Package-pypi-cloudpickle"
},
{
"spdxElementId": "SPDXRef-Package-one-memory",
"relationshipType": "DEPENDS_ON",
"relatedSpdxElement": "SPDXRef-Package-pypi-coloredlogs"
},
{
"spdxElementId": "SPDXRef-Package-one-memory",
"relationshipType": "DEPENDS_ON",
@@ -2507,6 +2580,11 @@
"relationshipType": "DEPENDS_ON",
"relatedSpdxElement": "SPDXRef-Package-pypi-huggingface-hub"
},
{
"spdxElementId": "SPDXRef-Package-one-memory",
"relationshipType": "DEPENDS_ON",
"relatedSpdxElement": "SPDXRef-Package-pypi-humanfriendly"
},
{
"spdxElementId": "SPDXRef-Package-one-memory",
"relationshipType": "DEPENDS_ON",
@@ -2617,6 +2695,11 @@
"relationshipType": "DEPENDS_ON",
"relatedSpdxElement": "SPDXRef-Package-pypi-more-itertools"
},
{
"spdxElementId": "SPDXRef-Package-one-memory",
"relationshipType": "DEPENDS_ON",
"relatedSpdxElement": "SPDXRef-Package-pypi-mpmath"
},
{
"spdxElementId": "SPDXRef-Package-one-memory",
"relationshipType": "DEPENDS_ON",
@@ -2872,6 +2955,11 @@
"relationshipType": "DEPENDS_ON",
"relatedSpdxElement": "SPDXRef-Package-pypi-starlette"
},
{
"spdxElementId": "SPDXRef-Package-one-memory",
"relationshipType": "DEPENDS_ON",
"relatedSpdxElement": "SPDXRef-Package-pypi-sympy"
},
{
"spdxElementId": "SPDXRef-Package-one-memory",
"relationshipType": "DEPENDS_ON",
+65 -21
View File
@@ -9,32 +9,72 @@ Per-package license texts are not reproduced here yet; the license
column is filled in as part of release review, and the SBOM records
`NOASSERTION` until then rather than guessing.
## hy-memory 2.0.1
## hy-memory 2.0.2
License: MIT
License: Apache-2.0
```text
MIT License
Tencent is pleased to support the community by making Hy-Memory available.
Copyright (c) 2025 HY Memory Contributors
Copyright (C) 2026 Tencent. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
Hy-Memory is licensed under Apache-2.0. Hy-Memory does not impose any additional restrictions beyond those specified in the license.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
For the avoidance of doubt, Hy-Memory refers solely to code, parameters, and weights made publicly available by Tencent in accordance with Apache-2.0.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Terms of the Apache-2.0 License:
--------------------------------------------------------------------
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
You must give any other recipients of the Work or Derivative Works a copy of this License; and
You must cause any modified files to carry prominent notices stating that You changed the files; and
You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.
You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
```
## MemTensor memmy-agent (adapted)
@@ -85,7 +125,7 @@ No Memmy brand, icon, font or mascot assets are distributed.
- tar 7.5.22
- three 0.185.1
## Python (PyPI) — 122 packages
## Python (PyPI) — 126 packages
- aiohappyeyeballs 2.7.1
- aiohttp 3.14.3
@@ -104,6 +144,7 @@ No Memmy brand, icon, font or mascot assets are distributed.
- chromadb 1.5.9
- click 8.5.0
- cloudpickle 3.1.2
- coloredlogs 15.0.1
- distro 1.9.0
- durationpy 0.11
- execnet 2.1.2
@@ -120,6 +161,7 @@ No Memmy brand, icon, font or mascot assets are distributed.
- httptools 0.8.0
- httpx 0.28.1
- huggingface-hub 1.29.0
- humanfriendly 10.0
- idna 3.19
- importlib-resources 7.1.0
- iniconfig 2.3.0
@@ -142,13 +184,14 @@ No Memmy brand, icon, font or mascot assets are distributed.
- mdurl 0.1.2
- mmh3 5.3.0
- more-itertools 11.1.0
- mpmath 1.3.0
- multidict 6.7.1
- mypy 2.3.1
- mypy-extensions 1.1.0
- narwhals 2.25.0
- numpy 2.5.2
- oauthlib 3.3.1
- onnxruntime 1.29.0
- onnxruntime 1.23.2
- openai 2.54.0
- opentelemetry-api 1.44.0
- opentelemetry-exporter-otlp-proto-common 1.44.0
@@ -193,6 +236,7 @@ No Memmy brand, icon, font or mascot assets are distributed.
- six 1.17.0
- sniffio 1.3.1
- starlette 1.6.0
- sympy 1.14.0
- tenacity 9.1.4
- threadpoolctl 3.6.0
- tiktoken 0.14.0
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@one-memory/cli",
"version": "1.0.0",
"version": "1.0.2",
"description": "MemHub bootstrap CLI for installing and managing the local-first shared memory runtime",
"license": "Apache-2.0",
"repository": {
+3 -3
View File
@@ -4,7 +4,7 @@ build-backend = "uv_build"
[project]
name = "one-memory"
version = "1.0.0"
version = "1.0.2"
description = "MemHub local-first memory core"
readme = "README.md"
requires-python = ">=3.12"
@@ -12,7 +12,7 @@ dependencies = [
"fastapi>=0.141.1",
"filelock>=3.32.4",
# Public SDK version validated against MemHub's permission and recall contracts.
"hy-memory==2.0.1",
"hy-memory==2.0.2",
# Retrieval's derived vector index directly consumes these locked versions.
"chromadb==1.5.9",
"numpy==2.5.2",
@@ -24,7 +24,7 @@ dependencies = [
# transitive version we do not ask for is one an unrelated upgrade could
# drop. Deliberately NOT sentence-transformers or torch: those would take
# the installer past 2GB, and onnxruntime alone runs the exported model.
"onnxruntime>=1.22.0",
"onnxruntime==1.23.2",
"tokenizers>=0.21.0",
"pydantic-settings>=2.15.0",
"uvicorn[standard]>=0.52.4",
@@ -43,7 +43,7 @@ _NATIVE_WRITES = frozenset(
_TIMESTAMPS = frozenset({"created_at", "updated_at"})
# SDK 2.0.1 reads `config` off the coding store while routing a coding
# SDK 2.0.2 reads `config` off the coding store while routing a coding
# turn. The wrapper must answer it, but the native `MemoryConfig` carries
# credentials (llm/embedder api keys, graph and cache passwords) and is a live
# handle into the backend, so it is never handed out. Only this redacted
@@ -199,7 +199,7 @@ class HyPermissionCodingStore:
def config(self) -> Any:
"""Read-only, redacted view of the native coding config.
SDK 2.0.1 reads `config` when it routes a coding turn. Answering
SDK 2.0.2 reads `config` when it routes a coding turn. Answering
it is safe only as a projection: the native MemoryConfig is a live
backend handle and carries credentials, so callers get plain values
with every credential-bearing key collapsed to None. This carries no
@@ -543,7 +543,7 @@ class HyPermissionCodingStore:
) -> list[Any]:
"""Read recent raw trace rows, still bounded by this lease.
Public SDK 2.0.1 asks for recent history before a coding write. The native call is
Public SDK 2.0.2 asks for recent history before a coding write. The native call is
scoped to the lease's own user and agent, and rows this lease cannot
attest are filtered out, so unapproved conversations never reach a
coding prompt through this path.
@@ -120,7 +120,7 @@ _UPDATE_FIELDS = frozenset(
"supersedes",
"superseded_by",
"source_raw_memory_id",
# SDK 2.0.1 sends both of these on an in-place reconcile UPDATE.
# SDK 2.0.2 sends both of these on an in-place reconcile UPDATE.
# They are metadata only and must never carry identity, permission or
# tenant state: `owner` is a provenance label whose only values are
# "user" / "agent" (who said this line), and `custom` is the SDK's own
@@ -462,7 +462,7 @@ class HyPermissionVectorStore:
if node.user_id != self.__user_id or node.layer not in _CHAT_LAYERS:
return False
if node.layer is MemoryLayer.L0_BASIC_INFO:
# Public SDK 2.0.1 migrates verified legacy agent-level profiles into one
# Public SDK 2.0.2 migrates verified legacy agent-level profiles into one
# user-level chain. Existing nodes still require a full proof in
# __load; accepting their historical agent id does not authorize
# unproven cross-agent memories.
@@ -819,7 +819,7 @@ class HyPermissionVectorStore:
):
self.__deny("update_fields")
allowed |= {"custom"}
# `custom` is an allowed field name (SDK 2.0.1 sends it on a
# `custom` is an allowed field name (SDK 2.0.2 sends it on a
# reconcile UPDATE), but the System 2 evidence counter it can hold
# stays digest-only: only the digest scope may advance it, and only
# by one per write. Letting the name through must not let an
@@ -14,7 +14,7 @@ from pathlib import Path
from typing import Literal, cast
HY_MEMORY_DISTRIBUTION = "hy-memory"
HY_MEMORY_VERSION = "2.0.1"
HY_MEMORY_VERSION = "2.0.2"
HY_MEMORY_NAMESPACE = "governed-v2"
DiagnosticLogLevel = Literal["error", "warning", "info", "debug"]
@@ -35,7 +35,7 @@ _COLLECTION_LOGGERS = (
# One Memory deliberately stays on ultra because its existing, user-visible
# profile/graph flow calls digest(), which the pinned public SDK exposes only
# for ultra clients. Reader and non-reranker behavior remain pinned for
# compatibility with public SDK 2.0.1.
# compatibility with public SDK 2.0.2.
HY_MEMORY_MODE = "ultra"
# The SDK only recognises `legacy` and `hybrid` (see
# hy_memory/pipelines/_retrieval/config.py: `ALL_READERS = (READER_LEGACY,
@@ -3,7 +3,7 @@
The result decides whether the `unknown` job state can ever be retired, so it
is probed against the installed SDK rather than written down from memory.
What was verified against `hy_memory-internal==2.0.1`:
What was verified against `hy-memory==2.0.2`:
- `HyMemoryClient.add(..., request_id=...)` accepts a caller-supplied id and
echoes it back. It is described as a request/receipt handle, and the SDK
@@ -328,7 +328,7 @@ def test_revoking_during_coding_extraction_stops_the_route(tmp_path):
def test_exposed_coding_config_is_a_redacted_projection_not_the_native_handle(tmp_path):
"""Public SDK 2.0.1 reads `config` off the coding store; it must not hand out secrets.
"""Public SDK 2.0.2 reads `config` off the coding store; it must not hand out secrets.
The wrapper answers `config` so the SDK's routing works, but the native
MemoryConfig is a live backend handle carrying api keys and passwords. This
@@ -1324,7 +1324,7 @@ def test_hy_runtime_matches_public_sdk_without_reranker_or_service_backends(
config = build_hy_memory_config(profile, api_key=None, data_dir=tmp_path)
assert HY_MEMORY_DISTRIBUTION == "hy-memory"
assert HY_MEMORY_VERSION == "2.0.1"
assert HY_MEMORY_VERSION == "2.0.2"
assert HY_MEMORY_MODE == "ultra"
assert HY_MEMORY_READER == "hybrid"
assert os.environ["MEMORY_MODE"] == "ultra"
@@ -325,7 +325,7 @@ def grant_records(supervisor, records, expected_revision=0):
@asynccontextmanager
async def raw_client(tmp_path, *, tool=None, conversation_id="raw-session"):
assert hy_memory_version() == "2.0.1"
assert hy_memory_version() == "2.0.2"
root = tmp_path / "source"
root.mkdir()
database = tmp_path / "one-memory.sqlite3"
@@ -627,7 +627,7 @@ async def assert_outputs(supervisor, task, records, client, *, raw_records=None)
memory = by_content[record.verbatim]
output = next(output for output in outputs if output.memory_id == memory.memory_id)
assert output.owner == CLAIMS[record.verbatim]
assert output.engine == "hy-memory" and output.engine_version == "2.0.1"
assert output.engine == "hy-memory" and output.engine_version == "2.0.2"
assert output.hy_memory_id == memory.memory_id
assert output.memory_revision == memory.revision
assert output.support_refs == (evidence_ref(record),)
@@ -819,7 +819,7 @@ def test_approved_native_candidates_are_reused_without_disclosing_denied_nodes(t
assert not oracle.errors, oracle.errors
assert await node_snapshots(client, DENIED_IDS) == before
assert result.status == "completed", (result.last_error_code, oracle.stages)
# SDK 2.0.1 keeps the `limit=1` pre-check removed, so
# SDK 2.0.2 keeps the `limit=1` pre-check removed, so
# skip reconcile, so the second write now compares against the
# already-approved candidates instead of blindly ADDing them. The
# contract is the reuse itself, not the absence of the call: one
@@ -592,7 +592,7 @@ def test_update_cannot_retarget_identity_or_add_denied_chain():
{"user_id": "other"},
{"node_id": "other"},
{"supersedes": ["outside"]},
# `custom` remains an allowed field name for SDK 2.0.1; the field it
# `custom` remains an allowed field name for SDK 2.0.2; the field it
# must not smuggle through is the digest-only System 2 counter.
{"custom": {"s2_evidence_count": 1}},
{"tag_ners": "not-a-list"},
@@ -258,7 +258,7 @@ def test_native_entry_points_preserve_payload_response_and_restore_instances():
def test_permission_transport_pins_the_public_sdk_identity():
"""The transport fails closed unless the audited SDK identity is installed."""
assert HY_MEMORY_DISTRIBUTION == "hy-memory"
assert HY_MEMORY_VERSION == "2.0.1"
assert HY_MEMORY_VERSION == "2.0.2"
@pytest.mark.parametrize("entry", ["complete", "complete_messages", "embed"])
@@ -445,7 +445,7 @@ def test_config_drift_or_overrides_deny_before_send(change):
elif change == "model":
provider._llm_config.model = "not-approved"
elif change == "backend":
# Public SDK 2.0.1 keeps LLMBackend narrowed to OPENAI, so the
# Public SDK 2.0.2 keeps LLMBackend narrowed to OPENAI, so the
# drifted-backend case uses an illegal sentinel value
# instead. It must differ from the approved openai backend,
# otherwise no drift is constructed at all.
@@ -62,5 +62,10 @@ def test_the_installed_sdk_is_the_pinned_one():
def test_the_pin_matches_the_lock_file_and_its_wheel():
lock = (DOCUMENT.parent / "apps/core/uv.lock").read_text(encoding="utf-8")
assert f"{HY_MEMORY_DISTRIBUTION}/{HY_MEMORY_VERSION}/" in lock
assert re.search(
rf'\[\[package\]\]\nname = "{HY_MEMORY_DISTRIBUTION}"\n'
rf'version = "{HY_MEMORY_VERSION}"',
lock,
)
assert f"hy_memory-{HY_MEMORY_VERSION}-" in lock
assert f'specifier = "=={HY_MEMORY_VERSION}"' in lock
@@ -69,7 +69,7 @@ def test_no_idempotency_key_means_unknown_must_remain() -> None:
def test_measured_sdk_verdict_is_recorded() -> None:
"""Pin the measured answer for `hy_memory-internal==2.0.1`.
"""Pin the measured answer for `hy-memory==2.0.2`.
The finding: `add()` takes a `request_id` but nothing de-duplicates on it,
while `MemoryInspector.get_pipeline_logs(request_id=...)` can look the
@@ -4,7 +4,7 @@ Dev history: when the model extracted no fact, the coding extractor stored the
raw dialogue as a working fact, and with no words of the person's it took the
first 600 characters of the rendered trace. MemHub showed that as the memory
content, which is where `[0] assistant: ... [1] tool(?): ...` came from
(D87). The SDK fix is present in 2.0.1: it does not store that fallback by
(D87). The SDK fix is present in 2.0.2: it does not store that fallback by
default.
This pins the default here, so a future bump that turns it back on, or our own
+75 -26
View File
@@ -593,6 +593,18 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
]
[[package]]
name = "coloredlogs"
version = "15.0.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "humanfriendly" },
]
sdist = { url = "https://files.pythonhosted.org/packages/cc/c7/eed8f27100517e8c0e6b923d5f0845d0cb99763da6fdee00478f91db7325/coloredlogs-15.0.1.tar.gz", hash = "sha256:7c991aa71a4577af2f82600d8f8f3a89f936baeaf9b50a9c197da014e5bf16b0", size = 278520, upload-time = "2021-06-11T10:22:45.202Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/a7/06/3d6badcf13db419e25b07041d9c7b4a2c331d3f4e7134445ec5df57714cd/coloredlogs-15.0.1-py2.py3-none-any.whl", hash = "sha256:612ee75c546f53e92e70049c9dbfcc18c935a2b9a53b66085ce9ef6a6e5c0934", size = 46018, upload-time = "2021-06-11T10:22:42.561Z" },
]
[[package]]
name = "cryptography"
version = "50.0.1"
@@ -965,9 +977,21 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/4e/a5/47c2ea9b228ccbcba8467e9a64823146e8ebbad29855e591d8f5eedcc9c7/huggingface_hub-1.29.0-py3-none-any.whl", hash = "sha256:b00f7782afc14db4bc6572763810a635bdfbab8623d957bfb553bd18e03852cd", size = 795768, upload-time = "2026-08-27T12:18:35.431Z" },
]
[[package]]
name = "humanfriendly"
version = "10.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "pyreadline3", marker = "sys_platform == 'win32'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/cc/3f/2c29224acb2e2df4d2046e4c73ee2662023c58ff5b113c4c1adac0886c43/humanfriendly-10.0.tar.gz", hash = "sha256:6b0b831ce8f15f7300721aa49829fc4e83921a9a301cc7f606be6686a2288ddc", size = 360702, upload-time = "2021-09-17T21:40:43.31Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/f0/0f/310fb31e39e2d734ccaa2c0fb981ee41f7bd5056ce9bc29b2248bd569169/humanfriendly-10.0-py2.py3-none-any.whl", hash = "sha256:1697e1a8a8f550fd43c2865cd84542fc175a61dcb779b6fee18cf6b6ccba1477", size = 86794, upload-time = "2021-09-17T21:40:39.897Z" },
]
[[package]]
name = "hy-memory"
version = "2.0.1"
version = "2.0.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "chromadb" },
@@ -984,9 +1008,9 @@ dependencies = [
{ name = "scikit-learn" },
{ name = "tiktoken" },
]
sdist = { url = "https://files.pythonhosted.org/packages/d8/0d/9174fa81cb7413254e71684db1c94775b6191f1a004179ca9d7b6b312f49/hy_memory-2.0.1.tar.gz", hash = "sha256:78764c3f8e4458e211e572a0d9dfc74fb2f19aaa3ba5fdc775a9693daf6f6a34", size = 819506, upload-time = "2026-10-15T17:24:49.881Z" }
sdist = { url = "https://files.pythonhosted.org/packages/b4/45/367b6d319f8f6c4fb99ee84d440e19616e64096fa50c3cc7672f7ef98653/hy_memory-2.0.2.tar.gz", hash = "sha256:1ba3b0d0bb1eb15ce9bf9b055190449614f93b550d02fb1646469dd01162c77e", size = 823220, upload-time = "2026-10-16T16:01:48.002Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/e1/35/783fd23ce9cde1b3248a3fd4abf04c7ae21d5ee9d2cd820c752bbed7db9d/hy_memory-2.0.1-py3-none-any.whl", hash = "sha256:87c1b0e3a419b950b1d72009a5a03b453a56b0daa25d851de85932f204a244f0", size = 883333, upload-time = "2026-10-15T17:24:47.568Z" },
{ url = "https://files.pythonhosted.org/packages/1f/0e/2a95b967ec29165fe36e3f6d7da3e07baeb6d7404a023d4b816223c8af60/hy_memory-2.0.2-py3-none-any.whl", hash = "sha256:e76419c3313db4fd4850249ec833ce7f98d17049a3e346b06bb1461038021384", size = 886789, upload-time = "2026-10-16T16:01:45.899Z" },
]
[[package]]
@@ -1513,6 +1537,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/e8/3d/1087453384dbde46a8c7f9356eead2c58be8a7bf156bca40243377c85715/more_itertools-11.1.0-py3-none-any.whl", hash = "sha256:4b65538ae22f6fed0ce4874efd317463a7489796a0939fa66824dd542125a192", size = 72226, upload-time = "2026-05-22T14:14:28.824Z" },
]
[[package]]
name = "mpmath"
version = "1.3.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f", size = 508106, upload-time = "2023-03-07T16:47:11.061Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198, upload-time = "2023-03-07T16:47:09.197Z" },
]
[[package]]
name = "multidict"
version = "6.7.1"
@@ -1768,7 +1801,7 @@ wheels = [
[[package]]
name = "one-memory"
version = "1.0.0"
version = "1.0.2"
source = { editable = "." }
dependencies = [
{ name = "aiomysql" },
@@ -1804,10 +1837,10 @@ requires-dist = [
{ name = "fastapi", specifier = ">=0.141.1" },
{ name = "filelock", specifier = ">=3.32.4" },
{ name = "httpx", specifier = ">=0.28.1" },
{ name = "hy-memory", specifier = "==2.0.1" },
{ name = "hy-memory", specifier = "==2.0.2" },
{ name = "keyring", specifier = ">=25.7.0" },
{ name = "numpy", specifier = "==2.5.2" },
{ name = "onnxruntime", specifier = ">=1.22.0" },
{ name = "onnxruntime", specifier = "==1.23.2" },
{ name = "pydantic-settings", specifier = ">=2.15.0" },
{ name = "tokenizers", specifier = ">=0.21.0" },
{ name = "uvicorn", extras = ["standard"], specifier = ">=0.52.4" },
@@ -1827,34 +1860,29 @@ dev = [
[[package]]
name = "onnxruntime"
version = "1.29.0"
version = "1.23.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "coloredlogs" },
{ name = "flatbuffers" },
{ name = "numpy" },
{ name = "packaging" },
{ name = "protobuf" },
{ name = "sympy" },
]
wheels = [
{ url = "https://files.pythonhosted.org/packages/d4/80/381c1e9efed9cc32d00aa7cab0547dc84116cec906c3ffe3613686d6963a/onnxruntime-1.29.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:3a3814c041251d6a77fdf513fb282056538ee826d2f1178a0df3c549d3fff6ba", size = 21430049, upload-time = "2026-08-17T22:53:48.286Z" },
{ url = "https://files.pythonhosted.org/packages/30/12/4be0e345d38fe707a701ca07e8f63c05b152a2e6285d1e43a7faf63fedd2/onnxruntime-1.29.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:d2fb19e848f7c33ed8d3182b52504aaa11c5e8da438bbb47296f85b133cbcf6b", size = 20816870, upload-time = "2026-08-17T22:53:51.169Z" },
{ url = "https://files.pythonhosted.org/packages/96/eb/e6968f5e41aac3125f2ff5708855f09cb0b70d85ed3115b625b0b58305ba/onnxruntime-1.29.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:2b80d8c7ec2cc7438e4da3760b88c24568cba72c9ace96d668800a6c79419acb", size = 23136745, upload-time = "2026-08-17T22:53:53.92Z" },
{ url = "https://files.pythonhosted.org/packages/b4/80/5b28f1f1111210fc4a336ddbc6950f468ebf9a6a265420568f4f43fa33ce/onnxruntime-1.29.0-cp312-cp312-win_amd64.whl", hash = "sha256:4acf2b4948b7ede87221ca6332344b8facdc8059d6ac751a7d367d04532b02dd", size = 14001407, upload-time = "2026-08-17T22:53:56.486Z" },
{ url = "https://files.pythonhosted.org/packages/6f/d6/6883f89ea4b044e6e8447ebfaf9bcecdf457b7d80a683635e130b25498e0/onnxruntime-1.29.0-cp312-cp312-win_arm64.whl", hash = "sha256:dc61a79cb39afd66ab3f01fd2c23591a7f01de89c1668e1fb6315067fc279164", size = 13746981, upload-time = "2026-08-17T22:53:58.977Z" },
{ url = "https://files.pythonhosted.org/packages/41/f8/d375facf60edaf41f5732f9f689c98a800fcc52df5cf6ddfb406703eb5a1/onnxruntime-1.29.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:be0f8ed688cfb1d4d5765a137193b7bfab0c8ea214eed99260b380bb525a3a7f", size = 21429708, upload-time = "2026-08-17T22:54:01.44Z" },
{ url = "https://files.pythonhosted.org/packages/c9/17/b9ad04051a8c4f504852ce0e8e10f9a6b2f1a331eedcdcc503df776dd0ea/onnxruntime-1.29.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:d67673c5367727860922c5262d724472f1b5539fb7ccf4c81a638f9b71719803", size = 20816263, upload-time = "2026-08-17T22:54:04.088Z" },
{ url = "https://files.pythonhosted.org/packages/83/2c/d8eb945d2a372149df9705a8d5c8d7c6c46c987c5446dbcea9e1ea7f6556/onnxruntime-1.29.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:e2128f31f449e922c62dbe5d8b6b7b079f0bcaf2d56a102fa203cb6e5bb5ab19", size = 23136817, upload-time = "2026-08-17T22:54:06.714Z" },
{ url = "https://files.pythonhosted.org/packages/e1/3b/66b424c63fa92dfaa48d1719efaae66fc8c256b9426a832eda51d8dfe1e9/onnxruntime-1.29.0-cp313-cp313-win_amd64.whl", hash = "sha256:2945e1f82f81f27e88decea88c7861f45baea23818950d467bf3909aa303119e", size = 14001310, upload-time = "2026-08-17T22:54:09.13Z" },
{ url = "https://files.pythonhosted.org/packages/83/22/d6a700e3a6322fa3d56fbe7cee9ffc53f35e77ffcd6b7e97f4b7722a27ab/onnxruntime-1.29.0-cp313-cp313-win_arm64.whl", hash = "sha256:4b940b0d777590c7e20bf298f5c16af1ea6ad1b400a1c822a6be192f64f4d954", size = 13747112, upload-time = "2026-08-17T22:54:11.608Z" },
{ url = "https://files.pythonhosted.org/packages/4a/89/c4af146de3d60a32c89fea48d5d34bfd044faaf8957270043a03bd1b462b/onnxruntime-1.29.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:533f8370ce124304e5cb08ab961836cf755631e3dd77adc5f3bbdab70c2b7d99", size = 20826136, upload-time = "2026-08-17T22:54:14.315Z" },
{ url = "https://files.pythonhosted.org/packages/9d/f2/e6bbacd11dfe8d070613261a758795ea128b9fc9bea391a2a7da2e4c7a08/onnxruntime-1.29.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:c1ad3f437153fe77f9d01a08fbaac0beb030e09b8a80ace1603bcf69b6c95481", size = 23138951, upload-time = "2026-08-17T22:54:17.154Z" },
{ url = "https://files.pythonhosted.org/packages/ff/a3/718e1b83096a1bc7b0fc8014c23d4cf795559fe666961cfac4fc038a4871/onnxruntime-1.29.0-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:e74b278af1d949876f5d91d1268fd6c680e79f2bac194967394eaba9fdf69e7e", size = 21431104, upload-time = "2026-08-17T22:54:20.118Z" },
{ url = "https://files.pythonhosted.org/packages/4e/17/c75e78ddc1fe69b6ebaef7fe88ac83f29bfe10955e3a0d2436d93473c91c/onnxruntime-1.29.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:939e5d65f332e6d399774b2bd0d3559fd8fa629c1e77833db29d968d2384f23d", size = 20818488, upload-time = "2026-08-17T22:54:23.147Z" },
{ url = "https://files.pythonhosted.org/packages/65/54/9f197c578d3d3d7bea16971e233e5483981228eec73748585cf7b5933403/onnxruntime-1.29.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:6c0c37b92f67ed68dd36221ce0403e1d9bd4f7efce724439978a2597848530e5", size = 23136994, upload-time = "2026-08-17T22:54:26.321Z" },
{ url = "https://files.pythonhosted.org/packages/24/53/4616a55d2495679cfd0195f968feb3d74fe30e26467d168ee243ac97c089/onnxruntime-1.29.0-cp314-cp314-win_amd64.whl", hash = "sha256:4a3129ae56e70d2618ff773920166916310370a7e3cacb60b9e0e8910092725f", size = 14350643, upload-time = "2026-08-17T22:54:28.794Z" },
{ url = "https://files.pythonhosted.org/packages/0a/0f/c338cb5500a522c7e671a3bb1276f4562404fbecce8a0e274565aa968484/onnxruntime-1.29.0-cp314-cp314-win_arm64.whl", hash = "sha256:e417ef8628dcce310d2d53023e750ea298ec14d4341ae6dc3a572bfd9bc7fa97", size = 14124294, upload-time = "2026-08-17T22:54:31.015Z" },
{ url = "https://files.pythonhosted.org/packages/b8/e7/61064289a9a1301b25c1f0f574fe98aba31c2d388db3c1dbec664f78621f/onnxruntime-1.29.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:11264bb58f7b7cf6af835ab10d36838d73680580820fd6f51d90124a1ca8f449", size = 20826174, upload-time = "2026-08-17T22:54:34.283Z" },
{ url = "https://files.pythonhosted.org/packages/60/21/d0c04b561b46e9bff89b5f500fb7415b8ca0669f7902204f76ab06bb0c7e/onnxruntime-1.29.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:1ea91cef3b971506e51ae9c37c16d027774ec64994a524ec1bdfb027d68a9832", size = 23138547, upload-time = "2026-08-17T22:54:37.491Z" },
{ url = "https://files.pythonhosted.org/packages/1b/9e/f748cd64161213adeef83d0cb16cb8ace1e62fa501033acdd9f9341fff57/onnxruntime-1.23.2-cp312-cp312-macosx_13_0_arm64.whl", hash = "sha256:b8f029a6b98d3cf5be564d52802bb50a8489ab73409fa9db0bf583eabb7c2321", size = 17195929, upload-time = "2025-10-22T03:47:36.24Z" },
{ url = "https://files.pythonhosted.org/packages/91/9d/a81aafd899b900101988ead7fb14974c8a58695338ab6a0f3d6b0100f30b/onnxruntime-1.23.2-cp312-cp312-macosx_13_0_x86_64.whl", hash = "sha256:218295a8acae83905f6f1aed8cacb8e3eb3bd7513a13fe4ba3b2664a19fc4a6b", size = 19157705, upload-time = "2025-10-22T03:46:40.415Z" },
{ url = "https://files.pythonhosted.org/packages/3c/35/4e40f2fba272a6698d62be2cd21ddc3675edfc1a4b9ddefcc4648f115315/onnxruntime-1.23.2-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:76ff670550dc23e58ea9bc53b5149b99a44e63b34b524f7b8547469aaa0dcb8c", size = 15226915, upload-time = "2025-10-22T03:46:27.773Z" },
{ url = "https://files.pythonhosted.org/packages/ef/88/9cc25d2bafe6bc0d4d3c1db3ade98196d5b355c0b273e6a5dc09c5d5d0d5/onnxruntime-1.23.2-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f9b4ae77f8e3c9bee50c27bc1beede83f786fe1d52e99ac85aa8d65a01e9b77", size = 17382649, upload-time = "2025-10-22T03:47:02.782Z" },
{ url = "https://files.pythonhosted.org/packages/c0/b4/569d298f9fc4d286c11c45e85d9ffa9e877af12ace98af8cab52396e8f46/onnxruntime-1.23.2-cp312-cp312-win_amd64.whl", hash = "sha256:25de5214923ce941a3523739d34a520aac30f21e631de53bba9174dc9c004435", size = 13470528, upload-time = "2025-10-22T03:47:28.106Z" },
{ url = "https://files.pythonhosted.org/packages/3d/41/fba0cabccecefe4a1b5fc8020c44febb334637f133acefc7ec492029dd2c/onnxruntime-1.23.2-cp313-cp313-macosx_13_0_arm64.whl", hash = "sha256:2ff531ad8496281b4297f32b83b01cdd719617e2351ffe0dba5684fb283afa1f", size = 17196337, upload-time = "2025-10-22T03:46:35.168Z" },
{ url = "https://files.pythonhosted.org/packages/fe/f9/2d49ca491c6a986acce9f1d1d5fc2099108958cc1710c28e89a032c9cfe9/onnxruntime-1.23.2-cp313-cp313-macosx_13_0_x86_64.whl", hash = "sha256:162f4ca894ec3de1a6fd53589e511e06ecdc3ff646849b62a9da7489dee9ce95", size = 19157691, upload-time = "2025-10-22T03:46:43.518Z" },
{ url = "https://files.pythonhosted.org/packages/1c/a1/428ee29c6eaf09a6f6be56f836213f104618fb35ac6cc586ff0f477263eb/onnxruntime-1.23.2-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:45d127d6e1e9b99d1ebeae9bcd8f98617a812f53f46699eafeb976275744826b", size = 15226898, upload-time = "2025-10-22T03:46:30.039Z" },
{ url = "https://files.pythonhosted.org/packages/f2/2b/b57c8a2466a3126dbe0a792f56ad7290949b02f47b86216cd47d857e4b77/onnxruntime-1.23.2-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8bace4e0d46480fbeeb7bbe1ffe1f080e6663a42d1086ff95c1551f2d39e7872", size = 17382518, upload-time = "2025-10-22T03:47:05.407Z" },
{ url = "https://files.pythonhosted.org/packages/4a/93/aba75358133b3a941d736816dd392f687e7eab77215a6e429879080b76b6/onnxruntime-1.23.2-cp313-cp313-win_amd64.whl", hash = "sha256:1f9cc0a55349c584f083c1c076e611a7c35d5b867d5d6e6d6c823bf821978088", size = 13470276, upload-time = "2025-10-22T03:47:31.193Z" },
{ url = "https://files.pythonhosted.org/packages/7c/3d/6830fa61c69ca8e905f237001dbfc01689a4e4ab06147020a4518318881f/onnxruntime-1.23.2-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9d2385e774f46ac38f02b3a91a91e30263d41b2f1f4f26ae34805b2a9ddef466", size = 15229610, upload-time = "2025-10-22T03:46:32.239Z" },
{ url = "https://files.pythonhosted.org/packages/b6/ca/862b1e7a639460f0ca25fd5b6135fb42cf9deea86d398a92e44dfda2279d/onnxruntime-1.23.2-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e2b9233c4947907fd1818d0e581c049c41ccc39b2856cc942ff6d26317cee145", size = 17394184, upload-time = "2025-10-22T03:47:08.127Z" },
]
[[package]]
@@ -2497,6 +2525,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/bd/24/12818598c362d7f300f18e74db45963dbcb85150324092410c8b49405e42/pyproject_hooks-1.2.0-py3-none-any.whl", hash = "sha256:9e5c6bfa8dcc30091c74b0cf803c81fdd29d94f01992a7707bc97babb1141913", size = 10216, upload-time = "2024-09-29T09:24:11.978Z" },
]
[[package]]
name = "pyreadline3"
version = "3.5.6"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/b6/6d/f94028646d7bbe6d9d873c47ee7c246f2d29129d253f0d96cb6fcab70733/pyreadline3-3.5.6.tar.gz", hash = "sha256:61e53218b99656091ddb077df9e71f25850e72e030b6183b39c9b7e6e4f4a9bf", size = 100368, upload-time = "2026-05-14T17:55:04.471Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/f7/5e/35c856e186b74678c24927847ad9895a51f1bc02a0c6126477a6c6040064/pyreadline3-3.5.6-py3-none-any.whl", hash = "sha256:8449b734232e42a5dcd74048e39b60db2839a4c38cf3ae2bf7707d58b5389c0d", size = 85243, upload-time = "2026-05-14T17:55:03.262Z" },
]
[[package]]
name = "pytest"
version = "9.1.1"
@@ -3050,6 +3087,18 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/c8/cb/6a6a47d5b464bd08695d254f3da6e7986cc70c9fa5d778eda57538edfe56/starlette-1.6.0-py3-none-any.whl", hash = "sha256:a86dd39d14bb45f85a3d18525215a9ef0cfd1f192ac793220e72598c90335f0c", size = 75969, upload-time = "2026-08-08T18:27:56.196Z" },
]
[[package]]
name = "sympy"
version = "1.14.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "mpmath" },
]
sdist = { url = "https://files.pythonhosted.org/packages/83/d3/803453b36afefb7c2bb238361cd4ae6125a569b4db67cd9e79846ba2d68c/sympy-1.14.0.tar.gz", hash = "sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517", size = 7793921, upload-time = "2025-04-27T18:05:01.611Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5", size = 6299353, upload-time = "2025-04-27T18:04:59.103Z" },
]
[[package]]
name = "tenacity"
version = "9.1.4"
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@one-memory/desktop",
"version": "1.0.0",
"version": "1.0.2",
"private": true,
"main": "dist/main.js",
"scripts": {
@@ -0,0 +1,27 @@
import { readFile } from "node:fs/promises";
import path from "node:path";
import { expect, it } from "vitest";
it("bakes fallback release identity when the packaging pipeline omits it", async () => {
const config = await readFile(
path.resolve(__dirname, "../tsup.config.ts"),
"utf8",
);
expect(config).toContain("resolveBuildNumber");
expect(config).toContain(
"const releaseBuildNumber = resolveBuildNumber(process.env)",
);
expect(config).toMatch(
/__ONE_MEMORY_BUILD_NUMBER__: JSON\.stringify\(\s*String\(releaseBuildNumber\)/,
);
expect(config).toMatch(
/process\.env\.ONE_MEMORY_COMMIT\?\.trim\(\) \|\| readSourceCommit\(\)/,
);
expect(config).toMatch(
/execFileSync\("git", \["rev-parse", "HEAD"\],[\s\S]*?encoding: "utf8"/,
);
expect(config).toContain(
"__ONE_MEMORY_COMMIT__: JSON.stringify(releaseCommit)",
);
});
@@ -234,7 +234,9 @@ describe("desktop lifecycle failure paths", () => {
await f.flush();
expect(f.createWindow).not.toHaveBeenCalled();
expect(f.quit().preventDefault).toHaveBeenCalled();
f.stopping.resolve();
// Teardown can fail after the startup error (for example, while writing a
// diagnostic). It must not replace the useful error with a second dialog.
f.stopping.reject(new Error("diagnostic_detail_key_invalid"));
await f.flush();
// Whatever did come up before the failure still has to be torn down.
expect(f.stop).toHaveBeenCalledTimes(1);
+3 -1
View File
@@ -108,7 +108,9 @@ export function startDesktopLifecycle(options: LifecycleOptions): void {
shutdown = startup
.catch(() => {})
.then(options.stop)
.catch(options.onError)
// Quit-time failures are logged by stop itself. A second modal here can
// only obscure the startup error that initiated this shutdown.
.catch(report)
.finally(() => {
stopped = true;
app.quit();
+4 -3
View File
@@ -124,9 +124,10 @@ export function hourlyRuntimeInstanceId(): string {
* `build_number` is the only ordering authority the installer compares, so a
* package that omits it defaults to 0 and can never outrank an earlier install:
* R->R.1 would be refused as a downgrade. The value is baked into the bundle's
* `LSEnvironment` at pack time (`apps/desktop/scripts/after-pack.cjs`); this
* reads it back and never invents one, so a source checkout that did not pass a
* build number stays undefined rather than fabricating order.
* `LSEnvironment` at pack time (`apps/desktop/scripts/after-pack.cjs`) and is
* also compiled from the canonical root release contract. This reader accepts
* either source so Finder launches still carry ordering even when an external
* packaging pipeline omitted the plist field.
*/
export function resolvePackagedBuildNumber(
environmentValue: unknown,
+1 -1
View File
@@ -1404,7 +1404,7 @@ if (desktopVersion !== null) {
component: "desktop.lifecycle",
event: "desktop.runtime_stop",
outcome: event.outcome,
details: { runtime: event.detail },
details: { runtime_state: event.detail },
});
},
});
+20 -7
View File
@@ -1,12 +1,29 @@
import { execFileSync } from "node:child_process";
import { createRequire } from "node:module";
import { fileURLToPath } from "node:url";
import { defineConfig } from "tsup";
const require = createRequire(import.meta.url);
const { resolveBuildVersion } =
const { resolveBuildNumber, resolveBuildVersion } =
require("../../scripts/release-version.cjs") as {
resolveBuildNumber(environment?: NodeJS.ProcessEnv, root?: string): number;
resolveBuildVersion(environment?: NodeJS.ProcessEnv, root?: string): string;
};
const releaseVersion = resolveBuildVersion(process.env);
const releaseBuildNumber = resolveBuildNumber(process.env);
const releaseCommit =
process.env.ONE_MEMORY_COMMIT?.trim() || readSourceCommit();
function readSourceCommit(): string {
try {
return execFileSync("git", ["rev-parse", "HEAD"], {
cwd: fileURLToPath(new URL("../..", import.meta.url)),
encoding: "utf8",
}).trim();
} catch {
return "unknown";
}
}
export default defineConfig({
entry: ["src/main.ts", "src/preload.ts"],
@@ -27,11 +44,7 @@ export default defineConfig({
__ONE_MEMORY_TRIAL_PROFILE__: JSON.stringify(
process.env.ONE_MEMORY_BUILD_TRIAL_PROFILE ?? null,
),
__ONE_MEMORY_BUILD_NUMBER__: JSON.stringify(
process.env.ONE_MEMORY_BUILD_NUMBER ?? "unknown",
),
__ONE_MEMORY_COMMIT__: JSON.stringify(
process.env.ONE_MEMORY_COMMIT ?? "unknown",
),
__ONE_MEMORY_BUILD_NUMBER__: JSON.stringify(String(releaseBuildNumber)),
__ONE_MEMORY_COMMIT__: JSON.stringify(releaseCommit),
},
});
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@one-memory/site",
"version": "1.0.0",
"version": "1.0.2",
"private": true,
"type": "module",
"scripts": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@one-memory/web",
"version": "1.0.0",
"version": "1.0.2",
"private": true,
"type": "module",
"scripts": {
+2 -2
View File
@@ -153,7 +153,7 @@ write. Retrieval results are checked against current Governance and access
scope before being returned to the UI or an authorized host.
The currently pinned SDK is the public PyPI distribution
`hy-memory==2.0.1`. Runtime configuration and permission boundaries are
`hy-memory==2.0.2`. Runtime configuration and permission boundaries are
documented in [`../MEMORY.md`](../MEMORY.md).
## Permission Boundaries
@@ -353,7 +353,7 @@ Support is declared per immutable GitHub Release manifest:
- macOS arm64 and x64 App/Runtime artifacts must carry Developer ID signatures,
notarization tickets, and native-platform acceptance receipts.
- Windows is not supported.
- `hy-memory==2.0.1` is the public Python SDK dependency.
- `hy-memory==2.0.2` is the public Python SDK dependency.
See [`INSTALLATION.md`](INSTALLATION.md) for the release matrix. A source test,
fake hosting test, or cross-build does not substitute for a native-platform
+5 -3
View File
@@ -1,6 +1,6 @@
# MemHub 安装与发行
MemHub `v1.0.0`(build 4)的发行目标如下:
MemHub `v1.0.2`(build 6)的发行目标如下:
| 平台 | 发行物 | 构建与验收 |
| --- | --- | --- |
@@ -12,8 +12,10 @@ MemHub `v1.0.0`(build 4)的发行目标如下:
正式支持以对应 GitHub Release 的 `release-manifest.json` 为准。Manifest 记录版本、
build、Git commit、协议范围、文件大小和 SHA-256;缺少条目的平台不属于该版本支持范围。
Linux Runtime 可通过仓库的 `Linux lifecycle` GitHub Actions 手动构建和下载。macOS
制品由发行流水线完成签名与公证后上传;GitHub 不保存 Apple 私钥。
在 `v*` tag 上手动运行仓库的 `Linux lifecycle` GitHub Actions 后,Linux Runtime
和 SHA-256 文件会自动上传到对应的 Draft Release;普通分支运行只保留 Actions
Artifact。macOS 制品由发行流水线完成签名与公证后上传到同一个 Draft Release;
GitHub 不保存 Apple 私钥。所有平台制品核对完成后再发布 Release。
最低支持 macOS 13。当前更新方式是从 GitHub Releases 手动下载新版本,不包含
auto-updater。
+2 -2
View File
@@ -1,8 +1,8 @@
{
"name": "one-memory",
"version": "1.0.0",
"version": "1.0.2",
"release": {
"build_number": 4,
"build_number": 6,
"macos_min_version": "13.0",
"app_update": "manual",
"runtime_manifest_schema": 1,
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@one-memory/contracts",
"version": "1.0.0",
"version": "1.0.2",
"private": true,
"type": "module",
"types": "./src/index.ts",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@one-memory/host-bridge",
"version": "1.0.0",
"version": "1.0.2",
"private": true,
"type": "module",
"exports": {
+26 -18
View File
@@ -374,25 +374,33 @@ export class HostGrantStore {
}
async #read(): Promise<StoreData> {
await this.#checkDirectory();
const file = path.join(this.#directory, FILE_NAME);
checkPrivate(await lstat(file), "file");
const handle = await open(
file,
constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK,
);
let raw: string;
try {
const info = await handle.stat();
checkPrivate(info, "file");
if (info.size > MAX_STORE_BYTES) fail("host_grant_store_corrupt");
raw = await handle.readFile("utf8");
} finally {
await handle.close();
const deadline = performance.now() + this.#lockTimeoutMs;
for (;;) {
await this.#checkDirectory();
const file = path.join(this.#directory, FILE_NAME);
checkPrivate(await lstat(file), "file");
const handle = await open(
file,
constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK,
);
try {
const info = await handle.stat();
// On Linux an atomic rename over this path sets the old, still-open
// inode's link count to zero. That is a normal concurrent commit, not
// an unsafe store. Close this stale handle and validate the new path.
if (info.nlink === 0) {
if (performance.now() >= deadline) fail("host_grant_store_busy");
continue;
}
checkPrivate(info, "file");
if (info.size > MAX_STORE_BYTES) fail("host_grant_store_corrupt");
const data = parseStore(await handle.readFile("utf8"));
this.#observe(data);
return data;
} finally {
await handle.close();
}
}
const data = parseStore(raw);
this.#observe(data);
return data;
}
#observe(data: StoreData): void {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@one-memory/operations",
"version": "1.0.0",
"version": "1.0.2",
"private": true,
"type": "module",
"types": "./src/index.ts",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@one-memory/runtime-control",
"version": "1.0.0",
"version": "1.0.2",
"private": true,
"type": "module",
"bin": {
@@ -217,6 +217,50 @@ describe("local Runtime installation", () => {
},
);
it("ignores Finder metadata without weakening payload verification", async () => {
const value = await distribution();
const plan = await planLocalInstall({
source: value.source,
installRoot: value.installRoot,
platform: process.platform,
arch: process.arch,
});
const first = await applyLocalInstall(plan);
await writeFile(path.join(plan.version_directory, ".DS_Store"), "finder");
await expect(
verifyInstalledDistribution(
plan.version_directory,
process.platform,
process.arch,
),
).resolves.toMatchObject({ version: plan.version });
const repeated = await applyLocalInstall(plan);
expect(first.created_version).toBe(true);
expect(repeated.created_version).toBe(false);
await writeFile(
path.join(plan.version_directory, "unexpected-runtime-file"),
"not metadata",
);
await expect(
verifyInstalledDistribution(
plan.version_directory,
process.platform,
process.arch,
),
).rejects.toThrow("distribution_manifest_incomplete");
});
it("does not let a non-file masquerade as Finder metadata", async () => {
const value = await distribution();
await symlink("../outside", path.join(value.source, ".DS_Store"));
await expect(
verifyInstalledDistribution(value.source, process.platform, process.arch),
).rejects.toThrow("distribution_path_invalid");
});
it.each(["extra", "hash"])(
"same-version reuse still rejects %s changes to installed canonical trees",
async (change) => {
@@ -622,6 +622,12 @@ async function treeFiles(root: string, current = root): Promise<string[]> {
entries.sort((left, right) => left.name.localeCompare(right.name));
const result: string[] = [];
for (const entry of entries) {
// Finder may add this metadata file after installation. It is not Runtime
// payload and must not make an otherwise verified install unreusable.
if (entry.name === ".DS_Store") {
if (!entry.isFile()) throw new Error("distribution_path_invalid");
continue;
}
const target = path.join(current, entry.name);
const relative = path.relative(root, target);
if (!safeRelative(relative)) throw new Error("distribution_path_invalid");
@@ -256,6 +256,7 @@ describe("installPackagedRuntime", () => {
});
const selectedBefore = await readlink(current);
const inodeBefore = (await lstat(current)).ino;
await writeFile(path.join(current, ".DS_Store"), "finder metadata");
await expect(
installPackagedRuntime({
resourcesPath: resources,
@@ -429,6 +429,11 @@ async function treeFiles(root: string, current = root): Promise<string[]> {
entries.sort((left, right) => left.name.localeCompare(right.name));
const result: string[] = [];
for (const entry of entries) {
// Finder metadata is not part of the packaged Runtime identity.
if (entry.name === ".DS_Store") {
if (!entry.isFile()) throw new Error("distribution_path_invalid");
continue;
}
const target = path.join(current, entry.name);
const relative = path.relative(root, target);
if (entry.isSymbolicLink()) {
+3 -3
View File
@@ -36,7 +36,7 @@ MEMMY_LICENSE = (
MEMMY_PROVENANCE = MEMMY_LICENSE.with_name("PROVENANCE.md")
MEMMY_PIN = "25acd5f29fc7df73b943713abe61f1b2f9839887"
HY_MEMORY_NAME = "hy-memory"
HY_MEMORY_VERSION = "2.0.1"
HY_MEMORY_VERSION = "2.0.2"
class SbomError(RuntimeError):
@@ -78,14 +78,14 @@ def hy_memory_notice(dist_info: Path | None = None) -> dict[str, str]:
or license_name is None
or name.group(1).strip() != HY_MEMORY_NAME
or version.group(1).strip() != HY_MEMORY_VERSION
or license_name.group(1).strip() != "MIT"
or license_name.group(1).strip() != "Apache-2.0"
or copyright_match is None
):
raise SbomError("hy-memory license metadata is incomplete")
return {
"name": HY_MEMORY_NAME,
"version": HY_MEMORY_VERSION,
"license": "MIT",
"license": "Apache-2.0",
"copyright": copyright_match.group(0).strip(),
"text": text,
}
+2 -2
View File
@@ -12,8 +12,8 @@ test("installation docs disclose publication and lifecycle boundaries", async ()
"utf8",
);
assert.match(readme, /公共 Python SDK 为 `hy-memory==2\.0\.1`/);
assert.match(readme, /`v1\.0\.0` 的发行目标是 Linux x64\/arm64 Runtime/);
assert.match(readme, /公共 Python SDK 为 `hy-memory==2\.0\.2`/);
assert.match(readme, /`v1\.0\.2` 的发行目标是 Linux x64\/arm64 Runtime/);
assert.match(readme, /Linux 制品由 GitHub Actions/);
assert.match(readme, /macOS.*Developer ID 签名、公证/s);
assert.match(readme, /release-manifest\.json/);
@@ -97,4 +97,29 @@ describe("Linux runtime scripts", () => {
assert.match(script, /cleanup-uninstall/);
assert.doesNotMatch(script, /install:\s*["']blocked["']/);
});
it("uploads successful tagged builds only to a draft release", async () => {
const workflow = await readFile(
path.join(directory, "../../../../.github/workflows/linux-lifecycle.yml"),
"utf8",
);
assert.match(
workflow,
/publish-draft-release:[\s\S]*startsWith\(github\.ref, 'refs\/tags\/v'\)/,
);
assert.match(
workflow,
/publish-draft-release:[\s\S]*permissions:\s*\n\s*contents: write/,
);
assert.match(workflow, /actions\/download-artifact@[a-f0-9]{40}/);
assert.match(
workflow,
/verify-release-version\.mjs[\s\S]*--channel stable/,
);
assert.match(workflow, /sha256sum -c/);
assert.match(workflow, /--json isDraft[\s\S]*--jq \.isDraft/);
assert.match(workflow, /gh release create[\s\S]*--draft/);
assert.match(workflow, /gh release upload[\s\S]*--clobber/);
});
});
+10 -9
View File
@@ -13,7 +13,7 @@ spec.loader.exec_module(module)
def test_hy_memory_notice_comes_from_the_installed_dist_info(tmp_path: Path) -> None:
dist_info = tmp_path / "hy_memory-2.0.1.dist-info"
dist_info = tmp_path / "hy_memory-2.0.2.dist-info"
licenses = dist_info / "licenses"
licenses.mkdir(parents=True)
(dist_info / "METADATA").write_text(
@@ -21,26 +21,27 @@ def test_hy_memory_notice_comes_from_the_installed_dist_info(tmp_path: Path) ->
(
"Metadata-Version: 2.4",
"Name: hy-memory",
"Version: 2.0.1",
"License-Expression: MIT",
"Version: 2.0.2",
"License-Expression: Apache-2.0",
"",
)
),
encoding="utf-8",
)
license_text = (
"MIT License\n\nCopyright (c) 2025 HY Memory Contributors\n\n"
"Permission is hereby granted, free of charge, to any person obtaining a copy"
"Apache License\nVersion 2.0, January 2004\n\n"
"Copyright (C) 2026 Tencent. All rights reserved.\n\n"
"Licensed under the Apache License, Version 2.0"
)
(licenses / "LICENSE").write_text(license_text, encoding="utf-8")
notice = module.hy_memory_notice(dist_info)
assert notice["name"] == "hy-memory"
assert notice["version"] == "2.0.1"
assert notice["license"] == "MIT"
assert notice["copyright"] == "Copyright (c) 2025 HY Memory Contributors"
assert "Permission is hereby granted" in notice["text"]
assert notice["version"] == "2.0.2"
assert notice["license"] == "Apache-2.0"
assert notice["copyright"] == "Copyright (C) 2026 Tencent. All rights reserved."
assert "Licensed under the Apache License" in notice["text"]
def test_hy_memory_notice_refuses_missing_dist_info(tmp_path: Path) -> None:
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "hy-memory"
version = "2.0.1"
version = "2.0.2"
description = "HY Memory - Industrial-grade dual-system cognitive memory framework"
readme = "README.md"
requires-python = ">=3.9"