Compare commits

...
9 Commits
Author SHA1 Message Date
mesalogo f9137f0463 fix: restore cross-platform release packaging
GoodBuddy Agents / Validate Agent source (push) Canceled after 0s
GoodBuddy Agents / Build Linux arm64 Agent (push) Canceled after 0s
GoodBuddy Agents / Build Linux x64 Agent (push) Canceled after 0s
Cross-platform packages / Validate source (push) Canceled after 0s
GoodBuddy Agent Release / Authorize Agent release (push) Canceled after 0s
Cross-platform packages / Authorize desktop packaging (push) Canceled after 0s
Cross-platform packages / linux arm64 (push) Canceled after 0s
Cross-platform packages / macos arm64 (push) Canceled after 0s
Cross-platform packages / windows arm64 (push) Canceled after 0s
Cross-platform packages / linux x64 (push) Canceled after 0s
Cross-platform packages / macos x64 (push) Canceled after 0s
Cross-platform packages / windows x64 (push) Canceled after 0s
Cross-platform packages / Publish GitHub and OSS release (push) Canceled after 0s
GoodBuddy Agent Release / Build Linux arm64 compound package (push) Canceled after 0s
GoodBuddy Agent Release / Build Linux x64 compound package (push) Canceled after 0s
GoodBuddy Agent Release / Sign cumulative Agent catalog (push) Canceled after 0s
GoodBuddy Agent Release / Publish Agent to GitHub and OSS (push) Canceled after 0s
The unpublished 0.11.3 candidate could not validate the DeepSeek Harness node-pty prebuilds on every native target, and catalog signing could misread large compound Agent payloads. Release packaging now verifies the rc.8 native layout and writes deterministic stored .gbagent archives with bounded central-directory, mode, CRC, SHA-256, and signature checks.

The 0.11.4 recovery also pins reviewed dependency lifecycle scripts, packages the omitted ONNX Runtime license, and carries the approved unpublished notes forward without changing the immutable failed tags or adding a LoongArch package.

Release note: 修复跨平台桌面包和大文件 Agent 包的发行校验失败,并将未发布的 0.11.3 内容顺延至 0.11.4。
2026-08-26 01:15:34 +08:00
mesalogo 798ac814a9 fix: update bundled DeepSeek Harness to rc.8
GoodBuddy Agents / Validate Agent source (push) Canceled after 0s
GoodBuddy Agents / Build Linux arm64 Agent (push) Canceled after 0s
GoodBuddy Agents / Build Linux x64 Agent (push) Canceled after 0s
GoodBuddy Agent Release / Authorize Agent release (push) Canceled after 0s
Cross-platform packages / Validate source (push) Canceled after 0s
Cross-platform packages / windows x64 (push) Canceled after 0s
Cross-platform packages / Publish GitHub and OSS release (push) Canceled after 0s
GoodBuddy Agent Release / Build Linux arm64 compound package (push) Canceled after 0s
GoodBuddy Agent Release / Build Linux x64 compound package (push) Canceled after 0s
GoodBuddy Agent Release / Sign cumulative Agent catalog (push) Canceled after 0s
GoodBuddy Agent Release / Publish Agent to GitHub and OSS (push) Canceled after 0s
Cross-platform packages / Authorize desktop packaging (push) Canceled after 0s
Cross-platform packages / linux arm64 (push) Canceled after 0s
Cross-platform packages / macos arm64 (push) Canceled after 0s
Cross-platform packages / windows arm64 (push) Canceled after 0s
Cross-platform packages / linux x64 (push) Canceled after 0s
Cross-platform packages / macos x64 (push) Canceled after 0s
The desktop runtime still pinned the rc.6 Harness family. It now uses the coherent rc.8 package set, adapts the revised image-attachment limits and error contract, and derives release checks from the lockfile. The managed SSH Agent package remains unchanged.

Release note: 内置 DeepSeek Harness 已升级至 0.1.0-rc.8,并适配新版图片附件限制;托管 SSH Agent 包不受影响。
2026-08-25 23:26:43 +08:00
mesalogo 6913e9ce16 fix: stabilize extension startup deadline validation
Cross-platform packages / Validate source (push) Canceled after 0s
Cross-platform packages / Authorize desktop packaging (push) Canceled after 0s
Cross-platform packages / linux arm64 (push) Canceled after 0s
Cross-platform packages / macos arm64 (push) Canceled after 0s
Cross-platform packages / windows arm64 (push) Canceled after 0s
Cross-platform packages / linux x64 (push) Canceled after 0s
Cross-platform packages / macos x64 (push) Canceled after 0s
Cross-platform packages / windows x64 (push) Canceled after 0s
Cross-platform packages / Publish GitHub and OSS release (push) Canceled after 0s
The total-deadline test relied on a timer firing at an exact millisecond boundary, so a shared runner could legally begin the second extension before observing expiration. Use the existing finite synchronous-work fixture to exceed the total budget deterministically while preserving the same production behavior assertion.
2026-08-25 22:30:52 +08:00
mesalogo 9aca3b2627 docs: trim GoodBuddy 0.11.3 release notes
Cross-platform packages / Validate source (push) Canceled after 0s
Cross-platform packages / Authorize desktop packaging (push) Canceled after 0s
Cross-platform packages / linux arm64 (push) Canceled after 0s
Cross-platform packages / macos arm64 (push) Canceled after 0s
Cross-platform packages / windows arm64 (push) Canceled after 0s
Cross-platform packages / linux x64 (push) Canceled after 0s
Cross-platform packages / macos x64 (push) Canceled after 0s
Cross-platform packages / windows x64 (push) Canceled after 0s
Cross-platform packages / Publish GitHub and OSS release (push) Canceled after 0s
Remove the Agent package verification and installation bullet from both language versions while keeping the remaining approved 0.11.3 notes aligned.
2026-08-25 22:19:53 +08:00
mesalogo 0cb7007429 fix: unify GoodBuddy release signing identity
Cross-platform packages / Publish GitHub and OSS release (push) Canceled after 0s
Cross-platform packages / Validate source (push) Canceled after 0s
Cross-platform packages / Authorize desktop packaging (push) Canceled after 0s
Cross-platform packages / linux arm64 (push) Canceled after 0s
Cross-platform packages / macos arm64 (push) Canceled after 0s
Cross-platform packages / windows arm64 (push) Canceled after 0s
Cross-platform packages / linux x64 (push) Canceled after 0s
Cross-platform packages / macos x64 (push) Canceled after 0s
Cross-platform packages / windows x64 (push) Canceled after 0s
GoodBuddy Agents / Validate Agent source (push) Canceled after 0s
GoodBuddy Agents / Build Linux arm64 Agent (push) Canceled after 0s
GoodBuddy Agents / Build Linux x64 Agent (push) Canceled after 0s
Use one generic GoodBuddy Ed25519 identity for the Agent bundle, bundled Runtime, outer package, and signed catalog while preserving each layer's existing signature domain.

Rename the production Environment contract to GOODBUDDY_SIGNING_*, register the matching production public key, and keep desktop and branch CI workflows isolated from the private key.
2026-08-25 22:02:47 +08:00
mesalogo bcd347e092 fix: align Unix endpoint tests with platform behavior
GoodBuddy Agents / Validate Agent source (push) Canceled after 0s
GoodBuddy Agents / Build Linux arm64 Agent (push) Canceled after 0s
GoodBuddy Agents / Build Linux x64 Agent (push) Canceled after 0s
Cross-platform packages / macos x64 (push) Canceled after 0s
Cross-platform packages / windows x64 (push) Canceled after 0s
Cross-platform packages / Publish GitHub and OSS release (push) Canceled after 0s
Cross-platform packages / Validate source (push) Canceled after 0s
Cross-platform packages / Authorize desktop packaging (push) Canceled after 0s
Cross-platform packages / linux arm64 (push) Canceled after 0s
Cross-platform packages / macos arm64 (push) Canceled after 0s
Cross-platform packages / windows arm64 (push) Canceled after 0s
Cross-platform packages / linux x64 (push) Canceled after 0s
Node and libuv remove a bound Unix socket path when the server closes. The removed test required protecting a same-user replacement inside the Agent-owned 0700 directory, which would need disproportionate custom socket publication machinery without crossing a real trust boundary.

Keep the practical live-endpoint and shutdown lifecycle coverage instead.
2026-08-25 21:31:47 +08:00
mesalogo 9c50de521e fix: stabilize Linux release validation
Cross-platform packages / Validate source (push) Canceled after 0s
Cross-platform packages / Authorize desktop packaging (push) Canceled after 0s
Cross-platform packages / linux arm64 (push) Canceled after 0s
Cross-platform packages / macos arm64 (push) Canceled after 0s
Cross-platform packages / windows arm64 (push) Canceled after 0s
Cross-platform packages / linux x64 (push) Canceled after 0s
Cross-platform packages / macos x64 (push) Canceled after 0s
Cross-platform packages / windows x64 (push) Canceled after 0s
Cross-platform packages / Publish GitHub and OSS release (push) Canceled after 0s
GoodBuddy Agents / Validate Agent source (push) Canceled after 0s
GoodBuddy Agents / Build Linux arm64 Agent (push) Canceled after 0s
GoodBuddy Agents / Build Linux x64 Agent (push) Canceled after 0s
Ubuntu validation exposed a missing Unix socket test import, a platform-dependent cancellation-message assertion, and a maximum-frame test that could exceed the default timeout under shared-runner load. The tests now exercise the intended behavior consistently without changing product code.
2026-08-25 21:21:58 +08:00
mesalogo da00a850ec fix: report verified Agent CI archives
GoodBuddy Agents / Validate Agent source (push) Canceled after 0s
GoodBuddy Agents / Build Linux arm64 Agent (push) Canceled after 0s
GoodBuddy Agents / Build Linux x64 Agent (push) Canceled after 0s
Cross-platform packages / Validate source (push) Canceled after 0s
Cross-platform packages / Authorize desktop packaging (push) Canceled after 0s
Cross-platform packages / linux arm64 (push) Canceled after 0s
Cross-platform packages / macos arm64 (push) Canceled after 0s
Cross-platform packages / windows arm64 (push) Canceled after 0s
Cross-platform packages / linux x64 (push) Canceled after 0s
Cross-platform packages / macos x64 (push) Canceled after 0s
Cross-platform packages / windows x64 (push) Canceled after 0s
Cross-platform packages / Publish GitHub and OSS release (push) Canceled after 0s
The native Linux CI builder completed both deterministic package assemblies but read an obsolete nested return field while producing its result JSON. It now reports the archive name from the actual assembleAgentPackage result and guards that shape in workflow tests.
2026-08-25 21:13:46 +08:00
mesalogo fcedaf578e fix: recover managed SSH Agent package release
GoodBuddy Agents / Validate Agent source (push) Canceled after 0s
GoodBuddy Agents / Build Linux arm64 Agent (push) Canceled after 0s
GoodBuddy Agents / Build Linux x64 Agent (push) Canceled after 0s
Cross-platform packages / Validate source (push) Canceled after 0s
Cross-platform packages / Authorize desktop packaging (push) Canceled after 0s
Cross-platform packages / linux arm64 (push) Canceled after 0s
Cross-platform packages / macos arm64 (push) Canceled after 0s
Cross-platform packages / windows arm64 (push) Canceled after 0s
Cross-platform packages / linux x64 (push) Canceled after 0s
Cross-platform packages / macos x64 (push) Canceled after 0s
Cross-platform packages / windows x64 (push) Canceled after 0s
Cross-platform packages / Publish GitHub and OSS release (push) Canceled after 0s
Linux .gbagent extraction inherited permissive directory modes, so compound packages failed secure Runtime verification on POSIX runners. Package roots and nested bundle directories now use private modes.

The production workflow now uses one GoodBuddy Agent signing identity for the Agent, bundled Runtime, outer package, and catalog. The unpublished 0.11.2 candidate is carried forward as desktop and Agent 0.11.3 without a LoongArch preview.

Release note: 修复 Linux 上 Agent 包解压后无法通过权限验证的问题,并将复合包各层统一为一组 GoodBuddy Agent 发布密钥。
2026-08-25 20:53:28 +08:00
46 changed files with 1360 additions and 848 deletions
+6 -10
View File
@@ -189,25 +189,21 @@ jobs:
}
NODE
- name: Preflight production signing identities
- name: Preflight production GoodBuddy signing identity
shell: bash
env:
GOODBUDDY_AGENT_SIGNING_KEY_ID: ${{ vars.GOODBUDDY_AGENT_SIGNING_KEY_ID }}
GOODBUDDY_REMOTE_RUNTIME_SIGNING_KEY_ID: ${{ vars.GOODBUDDY_REMOTE_RUNTIME_SIGNING_KEY_ID }}
GOODBUDDY_SIGNING_KEY_ID: ${{ vars.GOODBUDDY_SIGNING_KEY_ID }}
run: |
set -euo pipefail
node build/agent-bundle.cjs preflight
node build/remote-runtime-bundle.cjs preflight
- name: Build production compound Agent package
shell: bash
env:
AGENT_ARCH: ${{ matrix.arch }}
AGENT_VERSION: ${{ needs.authorize.outputs.version }}
GOODBUDDY_AGENT_SIGNING_KEY_ID: ${{ vars.GOODBUDDY_AGENT_SIGNING_KEY_ID }}
GOODBUDDY_AGENT_SIGNING_PRIVATE_KEY: ${{ secrets.GOODBUDDY_AGENT_SIGNING_PRIVATE_KEY }}
GOODBUDDY_REMOTE_RUNTIME_SIGNING_KEY_ID: ${{ vars.GOODBUDDY_REMOTE_RUNTIME_SIGNING_KEY_ID }}
GOODBUDDY_REMOTE_RUNTIME_SIGNING_PRIVATE_KEY: ${{ secrets.GOODBUDDY_REMOTE_RUNTIME_SIGNING_PRIVATE_KEY }}
GOODBUDDY_SIGNING_KEY_ID: ${{ vars.GOODBUDDY_SIGNING_KEY_ID }}
GOODBUDDY_SIGNING_PRIVATE_KEY: ${{ secrets.GOODBUDDY_SIGNING_PRIVATE_KEY }}
NODE_ARCHIVE: ${{ steps.inputs.outputs.node-archive }}
OPENCODE_ARCHIVE: ${{ steps.inputs.outputs.opencode-archive }}
run: |
@@ -348,8 +344,8 @@ jobs:
env:
AGENT_VERSION: ${{ needs.authorize.outputs.version }}
PREVIOUS_ARGUMENTS: ${{ steps.previous.outputs.arguments }}
GOODBUDDY_AGENT_SIGNING_KEY_ID: ${{ vars.GOODBUDDY_AGENT_SIGNING_KEY_ID }}
GOODBUDDY_AGENT_SIGNING_PRIVATE_KEY: ${{ secrets.GOODBUDDY_AGENT_SIGNING_PRIVATE_KEY }}
GOODBUDDY_SIGNING_KEY_ID: ${{ vars.GOODBUDDY_SIGNING_KEY_ID }}
GOODBUDDY_SIGNING_PRIVATE_KEY: ${{ secrets.GOODBUDDY_SIGNING_PRIVATE_KEY }}
run: |
set -euo pipefail
generated_at="$(git show -s --format=%cI "$GITHUB_SHA")"
+5 -5
View File
@@ -227,12 +227,12 @@ Release note: 修复左上角项目设置与消息通道项目设置不一致的
the checked-in public key registry.
- `.github/workflows/agent-release.yml` is the only production compound Agent
publication path. It requires an annotated immutable Agent tag, the protected
`agent-signing` Environment, native x64/arm64 builds, production Agent and
Runtime signing-key preflights, a signed cumulative catalog, a non-Latest
GitHub Agent Release, and synchronized immutable Beijing OSS objects plus the
final signed latest-catalog pointer.
`agent-signing` Environment, native x64/arm64 builds, one production
GoodBuddy signing identity for every layer of the compound package and
catalog, a non-Latest GitHub Agent Release, and synchronized immutable
Beijing OSS objects plus the final signed latest-catalog pointer.
- `.github/workflows/packages.yml` and `release:package` build only desktop
products. Missing Agent packages, remote Runtime inputs, or Agent signing
products. Missing Agent packages, remote Runtime inputs, or GoodBuddy signing
credentials must never block an ordinary desktop package or release.
- Keep electron-builder invocations on `--publish never`. Main-branch builds
run validation and build the production bundle without running the native
+6 -6
View File
@@ -197,14 +197,14 @@ Linux x64/arm64 复合包。每个 job 从 `agent-runtime-lock.json` 解析官
发布路径:只接受指向受保护 `main` 历史的 annotated
`agent-v${agent-runtime-lock.agentVersion}` 标签,在 `agent-signing` Environment 中
原生构建两种架构,生成并签名累计目录,然后同步到非 Latest 的 GitHub Agent Release
与北京 OSS。该 Environment 保护以下两组变量与 Secret:
与北京 OSS。该 Environment 只保护一组 GoodBuddy 通用发布变量与 Secret:
- `GOODBUDDY_AGENT_SIGNING_KEY_ID` /
`GOODBUDDY_AGENT_SIGNING_PRIVATE_KEY`
- `GOODBUDDY_REMOTE_RUNTIME_SIGNING_KEY_ID` /
`GOODBUDDY_REMOTE_RUNTIME_SIGNING_PRIVATE_KEY`
- `GOODBUDDY_SIGNING_KEY_ID` /
`GOODBUDDY_SIGNING_PRIVATE_KEY`
私钥只注入对应签名步骤;预检只读取 key ID 并确认公钥已在
该 GoodBuddy 身份统一签署内部 Agent、固定 Runtime manifest、外层 `.gbagent`
描述符和累计目录,各层通过既有签名域区分用途,不再要求内部 Runtime 单独配置生产
身份。私钥只注入签名步骤;预检只读取 key ID 并确认公钥已在
`resources/agent-release-keys.json` 注册为 production 且未撤销。
累计目录使用 `agent-catalog.json` 与 `agent-catalog.sig`。每个条目绑定 Agent 版本、
+1 -1
View File
@@ -23,7 +23,7 @@
- [x] **直连模型 Runtime**:支持问答、知识总结、受控工具执行和图像生成。
- [x] **OpenCode 与 Continue**:使用隔离子进程、环境变量白名单、统一配置、取消、总执行时限、有界流式输出和活动记录;共享进程回收逻辑保留 Windows 完整进程树终止,并对采用独立进程组的 POSIX 子进程执行组回收。交互提问只由前台对话回答,定时任务、远程通道和委派等后台执行遇到提问时会立即失败并提示改为前台运行,避免无限等待。
- [x] **托管 SSH OpenCode 闭环(技术预览)**:该能力由“设置 > 平台功能”中的独立“远程项目(技术预览)”页签控制并默认关闭;关闭时不影响任何本地项目、普通桌面功能或桌面发布。启用后可管理固定 Host Key 的 SSH Host、浏览有界远端目录并创建 Ask/Execute 项目;Ask 在 Runtime 边界通过 bubblewrap 保持 Workspace 只读,Execute 使用所选 SSH 账号的完整权限。Main 通过签名 Agent、私有 Unix socket、ACP v3、持久恢复 cursor 和 Main-only 模型桥维持长任务、重连与会话恢复,模型凭据不进入远端。远端组件不嵌入桌面包,也不会在项目激活时自动下载;用户必须先在该设置页按 Linux x64/arm64 手动下载最新兼容的签名 `.gbagent`,或导入/导出离线包。每个复合包包含 Agent、固定 Node 与由桌面源码统一维护适配的 OpenCode Runtime;在线来源跟随“关于与更新”的 GitHub/北京 OSS 选择,签名累计目录绑定最低桌面版本、Agent 协议、架构、大小、SHA-256 和固定 URL。缺少某架构包只使该架构托管 SSH 不可用。持久化验证继续精确绑定 Agent installation 与签名 Runtime digest;托管 SSH 会话只显示支持的 OpenCode 选择。Windows 到 Linux x64 的安装、provider-free 模型桥、断线重放、Session 续接和零残留清理已完成真实 Host 验证;签名 Agent 0.11.2-e2e.12 与 OpenCode Runtime 1.18.9 已完成一次只提交一遍、只调用一个 Ask 原生 read、无标题轮次且精确返回证据内容的有界真实模型验收,当前源码 lock 已转为正式 Agent 0.11.2。
- [x] **托管 SSH OpenCode 闭环(技术预览)**:该能力由“设置 > 平台功能”中的独立“远程项目(技术预览)”页签控制并默认关闭;关闭时不影响任何本地项目、普通桌面功能或桌面发布。启用后可管理固定 Host Key 的 SSH Host、浏览有界远端目录并创建 Ask/Execute 项目;Ask 在 Runtime 边界通过 bubblewrap 保持 Workspace 只读,Execute 使用所选 SSH 账号的完整权限。Main 通过签名 Agent、私有 Unix socket、ACP v3、持久恢复 cursor 和 Main-only 模型桥维持长任务、重连与会话恢复,模型凭据不进入远端。远端组件不嵌入桌面包,也不会在项目激活时自动下载;用户必须先在该设置页按 Linux x64/arm64 手动下载最新兼容的签名 `.gbagent`,或导入/导出离线包。每个复合包包含 Agent、固定 Node 与由桌面源码统一维护适配的 OpenCode Runtime;在线来源跟随“关于与更新”的 GitHub/北京 OSS 选择,签名累计目录绑定最低桌面版本、Agent 协议、架构、大小、SHA-256 和固定 URL。缺少某架构包只使该架构托管 SSH 不可用。持久化验证继续精确绑定 Agent installation 与签名 Runtime digest;托管 SSH 会话只显示支持的 OpenCode 选择。Windows 到 Linux x64 的安装、provider-free 模型桥、断线重放、Session 续接和零残留清理已完成真实 Host 验证;签名 Agent 0.11.2-e2e.12 与 OpenCode Runtime 1.18.9 已完成一次只提交一遍、只调用一个 Ask 原生 read、无标题轮次且精确返回证据内容的有界真实模型验收,当前源码 lock 已转为正式 Agent 0.11.4。
- [x] **SSH Host 运行环境手动更新**:远程运行环境卡片只读取 Host 已安装 Agent/Runtime 与当前本地复合包所需版本,不会联网。用户显式触发后,Main 按 Agent、Runtime 顺序使用对应 Host 架构的已验证本地 `.gbagent` 强制重新校验并安装,显示阶段进度并允许在最终收尾前取消;尚未下载时提示先到平台功能设置下载或导入。更新期间保留旧版本卡片,失败或取消后刷新实际状态并允许重试;成功后定向清理该 Host 缓存并使引用项目重新验证,不删除 Host 配置、凭据、项目设置或 Workspace 文件。
- [x] **DeepSeek Harness(预览)**:使用 GoodBuddy 固定 Host 和 OpenAI 兼容模型连接;Ask 只允许调用 Host 中真实注册的 `read`、`skill` 以及 Main 管理的 Web Search/Fetch 代理,拒绝插件同名冒充,Execute 放行全部已启用内置及插件工具,并以当前用户权限运行。图像输入跟随所选模型连接的能力声明,文本模型在 Host 或模型调用前拒绝图片,图片模型通过有界内联内容和临时 Attachment Store 接收 JPEG/PNG。
- [x] **DSH npm 插件市场**:市场默认关闭,由用户显式开启后搜索公共 npm 的 `dsh-plugin` 包,使用捆绑 npm 执行精确版本安装和普通 lifecycle scripts,并支持启停、JSON 配置、移除、失败启动自动停用和离线管理已安装插件;关闭市场只隐藏目录与管理界面,不改变已有插件的启停状态,第三方代码不受 Ask 初始化隔离。
+1 -1
View File
@@ -1,6 +1,6 @@
{
"formatVersion": 1,
"agentVersion": "0.11.2",
"agentVersion": "0.11.4",
"protocol": {
"major": 2,
"minor": 0
+16 -10
View File
@@ -47,9 +47,9 @@ const manifestFileName = 'manifest.json'
const signatureFileName = 'manifest.sig'
const koffiPackageName = 'koffi'
const signingPrivateKeyEnvironment =
'GOODBUDDY_AGENT_SIGNING_PRIVATE_KEY'
'GOODBUDDY_SIGNING_PRIVATE_KEY'
const signingKeyIdEnvironment =
'GOODBUDDY_AGENT_SIGNING_KEY_ID'
'GOODBUDDY_SIGNING_KEY_ID'
const semanticVersionPattern =
/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+((?:[0-9a-zA-Z-]+)(?:\.[0-9a-zA-Z-]+)*))?$/u
@@ -372,7 +372,7 @@ function productionSigningKey(environment = process.env) {
const privateKey = environment[signingPrivateKeyEnvironment]
if (!keyId || !privateKey) {
throw new Error(
`${signingKeyIdEnvironment} and ${signingPrivateKeyEnvironment} are required for production Agent signing`
`${signingKeyIdEnvironment} and ${signingPrivateKeyEnvironment} are required for production GoodBuddy signing`
)
}
return { keyId, privateKey }
@@ -385,11 +385,11 @@ function preflightProductionSigningKey(options = {}) {
const registry =
options.registry ?? readTrustedKeyRegistry(projectRoot)
return preflightRegisteredProductionKey({
component: 'Agent',
component: 'GoodBuddy',
keyId,
registry,
missingKeyIdMessage:
`${signingKeyIdEnvironment} is not configured; provision the production Agent signing key ID before building a release`
`${signingKeyIdEnvironment} is not configured; provision the production GoodBuddy signing key ID before building a release`
})
}
@@ -427,7 +427,7 @@ function trustedKeyForManifest(
(candidate) => candidate.keyId === key.keyId
)
if (revocation) {
throw new Error(`Agent signing key is revoked: ${key.keyId}`)
throw new Error(`GoodBuddy signing key is revoked: ${key.keyId}`)
}
return key
}
@@ -791,7 +791,9 @@ function buildAgentBundle(options) {
options.registry ?? readTrustedKeyRegistry(projectRoot)
const signingEnvironment = options.environment ?? process.env
const signingIdentity =
options.testSigningIdentity ?? productionSigningKey(signingEnvironment)
options.testSigningIdentity ??
options.signingIdentity ??
productionSigningKey(signingEnvironment)
const signingRecord = options.testSigningIdentity
? registry.keys.find(
(key) => key.keyId === signingIdentity.keyId
@@ -812,10 +814,13 @@ function buildAgentBundle(options) {
publicKeySpkiBase64(signingIdentity.privateKey) !==
signingRecord.publicKeySpkiBase64
) {
throw new Error('Agent signing private key does not match the trusted key')
throw new Error(
'GoodBuddy signing private key does not match the trusted key'
)
}
if (
!options.testSigningIdentity &&
!options.signingIdentity &&
signingEnvironment === process.env
) {
delete process.env[signingPrivateKeyEnvironment]
@@ -1102,7 +1107,7 @@ function parseArguments(argv) {
}
if (command === 'preflight') {
if (Object.keys(options).length > 0) {
throw new Error('Agent signing preflight does not accept arguments')
throw new Error('GoodBuddy signing preflight does not accept arguments')
}
return { command }
}
@@ -1115,7 +1120,7 @@ function main(argv = process.argv.slice(2)) {
if (options.command === 'preflight') {
const signingRecord = preflightProductionSigningKey()
console.log(
`Production Agent signing registry preflight passed: ${signingRecord.keyId}`
`Production GoodBuddy signing registry preflight passed: ${signingRecord.keyId}`
)
return
}
@@ -1166,6 +1171,7 @@ module.exports = {
createAgentArchive,
createManifest,
detectElfArchitecture,
expectedManifestMode,
importAgentArchive,
lockedRuntimeInput,
preflightProductionSigningKey,
+216 -113
View File
@@ -6,18 +6,14 @@ const {
} = require('node:crypto')
const {
closeSync,
fstatSync,
openSync,
readFileSync,
readSync,
statSync,
writeFileSync
} = require('node:fs')
const { basename, resolve } = require('node:path')
const {
Unzip,
UnzipInflate,
UnzipPassThrough
} = require('fflate')
const { crc32 } = require('node:zlib')
const {
productionSigningKey,
publicKeySpkiBase64,
@@ -29,6 +25,11 @@ const {
descriptorContentDigest,
signatureDomain: packageSignatureDomain
} = require('./agent-package.cjs')
const {
localFileHeaderSignature,
readExactZipBytes,
readZipCentralDirectory
} = require('./zip-central-directory.cjs')
const root = resolve(__dirname, '..')
const catalogName = 'agent-catalog.json'
@@ -41,6 +42,11 @@ const semanticVersionPattern =
/^(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)(?:-(?:(?:0|[1-9]\d*)|(?:\d*[A-Za-z-][0-9A-Za-z-]*))(?:\.(?:(?:0|[1-9]\d*)|(?:\d*[A-Za-z-][0-9A-Za-z-]*)))*)?$/u
const sha256Pattern = /^[a-f0-9]{64}$/u
const maximumPackageBytes = 512 * 1024 * 1024
const maximumPackageEntries = 50_002
const maximumPackageEntryBytes = 384 * 1024 * 1024
const maximumExpandedPackageBytes = 1024 * 1024 * 1024
const maximumMetadataBytes = 1024 * 1024
const maximumCentralDirectoryBytes = 32 * 1024 * 1024
const maximumCatalogEntries = 200
const windowsReservedNamePattern =
/^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\.|$)/iu
@@ -183,14 +189,6 @@ function assertPackageDescriptor(descriptor) {
function readPackageMetadata(archivePath, registry) {
const archive = resolve(archivePath)
const status = statSync(archive)
if (
!status.isFile() ||
status.size <= 0 ||
status.size > maximumPackageBytes
) {
throw new Error('Agent package archive is not a bounded file')
}
const streamed = streamPackageMetadata(archive)
const descriptorBytes = streamed.descriptor
const signatureBytes = streamed.signature
@@ -225,7 +223,7 @@ function readPackageMetadata(archivePath, registry) {
return {
archive: expectedArchive,
descriptor,
size: status.size,
size: streamed.size,
sha256: streamed.sha256
}
}
@@ -282,127 +280,232 @@ function verifyPackageFileInventory(descriptor, actualFiles) {
}
}
function streamPackageMetadata(archivePath) {
const handle = openSync(archivePath, 'r')
const hash = createHash('sha256')
const buffer = Buffer.allocUnsafe(64 * 1024)
const values = new Map()
const files = new Map()
function centralDirectoryEntries(handle, archiveSize) {
const parsed = readZipCentralDirectory(handle, archiveSize, {
label: 'Agent package',
minimumEntries: 1,
maximumEntries: maximumPackageEntries,
maximumCentralDirectoryBytes
})
const entries = []
const seen = new Set()
let fatalError
let entries = 0
let expandedBytes = 0
const unzip = new Unzip((file) => {
entries += 1
if (entries > 50_002) {
fatalError ??= new Error(
'Agent package contains too many entries'
)
file.terminate()
return
for (const entry of parsed.entries) {
if (
entry.flags !== 0x0800 ||
entry.compression !== 0 ||
entry.compressedSize === 0xffffffff ||
entry.size === 0xffffffff ||
entry.compressedSize !== entry.size ||
entry.size > maximumPackageEntryBytes ||
entry.diskStart !== 0 ||
entry.localOffset === 0xffffffff
) {
throw new Error('Agent package ZIP entry is unsupported')
}
if (!safePackagePath(file.name) || seen.has(file.name)) {
fatalError ??= new Error(
if (
!safePackagePath(entry.name) ||
seen.has(entry.name)
) {
throw new Error(
'Agent package contains an unsafe or duplicate path'
)
file.terminate()
return
}
seen.add(file.name)
const wanted =
file.name === 'agent-package.json' ||
file.name === 'agent-package.sig'
if (wanted && values.has(file.name)) {
fatalError ??= new Error(
'Agent package contains duplicate metadata'
seen.add(entry.name)
expandedBytes += entry.size
if (expandedBytes > maximumExpandedPackageBytes) {
throw new Error(
'Agent package expanded payload exceeds its limit'
)
file.terminate()
return
}
const chunks = []
let size = 0
const fileHash = createHash('sha256')
file.ondata = (error, data, final) => {
if (error) {
fatalError ??= error
return
}
size += data.byteLength
expandedBytes += data.byteLength
if (
size > 384 * 1024 * 1024 ||
expandedBytes > 1024 * 1024 * 1024
) {
fatalError ??= new Error(
'Agent package expanded payload exceeds its limit'
)
file.terminate()
return
}
fileHash.update(data)
if (wanted) {
if (size > 1024 * 1024) {
fatalError ??= new Error(
'Agent package metadata exceeds its limit'
)
file.terminate()
return
}
chunks.push(Buffer.from(data))
}
if (final) {
files.set(file.name, {
size,
sha256: fileHash.digest('hex')
})
if (wanted) {
values.set(file.name, Buffer.concat(chunks, size))
}
}
const local = readExactZipBytes(
handle,
30,
entry.localOffset,
'Agent package local file header'
)
const localNameLength = local.readUInt16LE(26)
const localExtraLength = local.readUInt16LE(28)
if (
local.readUInt32LE(0) !== localFileHeaderSignature ||
local.readUInt16LE(6) !== entry.flags ||
local.readUInt16LE(8) !== entry.compression ||
local.readUInt32LE(14) !== entry.checksum ||
local.readUInt32LE(18) !== entry.compressedSize ||
local.readUInt32LE(22) !== entry.size ||
localNameLength !== entry.nameBytes.length
) {
throw new Error('Agent package local file header is invalid')
}
try {
file.start()
} catch (error) {
fatalError ??= error
file.terminate()
const localName = readExactZipBytes(
handle,
localNameLength,
entry.localOffset + 30,
'Agent package local file name'
)
if (!localName.equals(entry.nameBytes)) {
throw new Error('Agent package local file name is invalid')
}
})
unzip.register(UnzipPassThrough)
unzip.register(UnzipInflate)
const dataOffset =
entry.localOffset +
30 +
localNameLength +
localExtraLength
const recordEnd = dataOffset + entry.compressedSize
if (recordEnd > parsed.centralOffset) {
throw new Error('Agent package entry exceeds its data region')
}
entries.push({
checksum: entry.checksum,
dataOffset,
name: entry.name,
recordStart: entry.localOffset,
recordEnd
})
}
entries.sort(
(left, right) => left.recordStart - right.recordStart
)
for (let index = 1; index < entries.length; index += 1) {
if (entries[index].recordStart < entries[index - 1].recordEnd) {
throw new Error('Agent package ZIP entries overlap')
}
}
return entries
}
function sameFileSnapshot(left, right) {
return (
left.dev === right.dev &&
left.ino === right.ino &&
left.size === right.size &&
left.mtimeMs === right.mtimeMs &&
left.ctimeMs === right.ctimeMs
)
}
function streamPackageMetadata(archivePath) {
const handle = openSync(archivePath, 'r')
const values = new Map()
const files = new Map()
try {
while (true) {
const initialStatus = fstatSync(handle)
if (
!initialStatus.isFile() ||
initialStatus.size <= 0 ||
initialStatus.size > maximumPackageBytes
) {
throw new Error(
'Agent package archive is not a bounded file'
)
}
const entries = centralDirectoryEntries(
handle,
initialStatus.size
)
const states = entries.map((entry) => {
const wanted =
entry.name === 'agent-package.json' ||
entry.name === 'agent-package.sig'
const size = entry.recordEnd - entry.dataOffset
if (wanted && size > maximumMetadataBytes) {
throw new Error('Agent package metadata exceeds its limit')
}
return {
...entry,
bytes: 0,
contents: wanted
? Buffer.allocUnsafe(size)
: undefined,
crc32: 0,
hash: createHash('sha256')
}
})
const archiveHash = createHash('sha256')
const buffer = Buffer.allocUnsafe(1024 * 1024)
let position = 0
let firstEntry = 0
while (position < initialStatus.size) {
const bytesRead = readSync(
handle,
buffer,
0,
buffer.length,
null
Math.min(buffer.length, initialStatus.size - position),
position
)
if (bytesRead === 0) {
unzip.push(new Uint8Array(), true)
break
if (bytesRead <= 0) {
throw new Error('Agent package archive is truncated')
}
const chunk = Uint8Array.from(
buffer.subarray(0, bytesRead)
const chunk = buffer.subarray(0, bytesRead)
const chunkEnd = position + bytesRead
archiveHash.update(chunk)
while (
firstEntry < states.length &&
states[firstEntry].recordEnd <= position
) {
firstEntry += 1
}
for (
let index = firstEntry;
index < states.length &&
states[index].dataOffset < chunkEnd;
index += 1
) {
const state = states[index]
const dataStart = Math.max(position, state.dataOffset)
const dataEnd = Math.min(chunkEnd, state.recordEnd)
if (dataStart >= dataEnd) {
continue
}
const data = chunk.subarray(
dataStart - position,
dataEnd - position
)
state.hash.update(data)
state.crc32 = crc32(data, state.crc32)
if (state.contents) {
data.copy(
state.contents,
dataStart - state.dataOffset
)
}
state.bytes += data.byteLength
}
position = chunkEnd
}
if (!sameFileSnapshot(initialStatus, fstatSync(handle))) {
throw new Error(
'Agent package archive changed during verification'
)
hash.update(chunk)
unzip.push(chunk, false)
if (fatalError) {
throw fatalError
}
for (const state of states) {
const size = state.recordEnd - state.dataOffset
if (
state.bytes !== size ||
(state.crc32 >>> 0) !== state.checksum
) {
throw new Error(
`Agent package ZIP checksum verification failed: ${state.name}`
)
}
files.set(state.name, {
size,
sha256: state.hash.digest('hex')
})
if (state.contents) {
values.set(state.name, state.contents)
}
}
return {
descriptor: values.get('agent-package.json'),
signature: values.get('agent-package.sig'),
files,
sha256: archiveHash.digest('hex'),
size: initialStatus.size
}
} finally {
closeSync(handle)
}
if (fatalError) {
throw fatalError
}
return {
descriptor: values.get('agent-package.json'),
signature: values.get('agent-package.sig'),
files,
sha256: hash.digest('hex')
}
}
function catalogEntry(metadata) {
+1 -1
View File
@@ -186,7 +186,7 @@ async function buildCiAgentBundle(options) {
remoteRuntimeVersion:
runtimeLock.runtimes.opencode.version,
architecture: options.arch,
archive: second.package.archive,
archive: second.archive,
archiveSha256: archiveDigest
}
} finally {
+146 -62
View File
@@ -19,9 +19,10 @@ const {
} = require('node:fs')
const { basename, dirname, join, relative, resolve, sep } = require('node:path')
const { tmpdir } = require('node:os')
const { Zip, ZipPassThrough } = require('fflate')
const { crc32 } = require('node:zlib')
const {
buildAgentBundle,
expectedManifestMode: expectedAgentManifestMode,
preflightProductionSigningKey,
productionSigningKey,
publicKeySpkiBase64,
@@ -36,6 +37,11 @@ const {
readRemoteRuntimeLock,
verifyBundleDirectory: verifyRuntimeBundle
} = require('./remote-runtime-bundle.cjs')
const {
centralDirectoryHeaderSignature: zipCentralHeaderSignature,
endOfCentralDirectorySignature: zipEndSignature,
localFileHeaderSignature: zipLocalHeaderSignature
} = require('./zip-central-directory.cjs')
const root = join(__dirname, '..')
const packageDescriptorName = 'agent-package.json'
@@ -44,7 +50,10 @@ const signatureDomain = Buffer.from(
'GoodBuddy Agent Package Descriptor Signature v1\0',
'utf8'
)
const archiveEpoch = new Date('1980-01-01T00:00:00.000Z')
const zipUtf8Flag = 0x0800
const zipVersion = 20
const zipMadeByUnix = (3 << 8) | zipVersion
const zipDosDate = (1 << 5) | 1
const semanticVersionPattern =
/^(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)(?:-(?:(?:0|[1-9]\d*)|(?:\d*[A-Za-z-][0-9A-Za-z-]*))(?:\.(?:(?:0|[1-9]\d*)|(?:\d*[A-Za-z-][0-9A-Za-z-]*)))*)?$/u
@@ -89,10 +98,11 @@ function listFiles(directory) {
)
}
function sha256File(filePath) {
function fileDigests(filePath) {
const hash = createHash('sha256')
const handle = openSync(filePath, 'r')
const buffer = Buffer.allocUnsafe(1024 * 1024)
let checksum = 0
try {
while (true) {
const bytesRead = readSync(
@@ -105,12 +115,17 @@ function sha256File(filePath) {
if (bytesRead === 0) {
break
}
hash.update(buffer.subarray(0, bytesRead))
const chunk = buffer.subarray(0, bytesRead)
hash.update(chunk)
checksum = crc32(chunk, checksum)
}
} finally {
closeSync(handle)
}
return hash.digest('hex')
return {
crc32: checksum >>> 0,
sha256: hash.digest('hex')
}
}
function copyTree(source, destination) {
@@ -122,6 +137,13 @@ function copyTree(source, destination) {
}
}
function packageFileMode(path) {
if (path.startsWith('agent/')) {
return expectedAgentManifestMode(path.slice('agent/'.length))
}
return /\/bin\/opencode$/u.test(path) ? '0755' : '0644'
}
function stagePackagePayload(options) {
const staging = options.staging
copyTree(options.agentBundle, join(staging, 'agent'))
@@ -157,20 +179,18 @@ function stagePackagePayload(options) {
}
function payloadFiles(staging) {
return listFiles(staging)
const crc32ByPath = new Map()
const files = listFiles(staging)
.map((filePath) => {
const path = relative(staging, filePath).split(sep).join('/')
const size = statSync(filePath).size
const digests = fileDigests(filePath)
crc32ByPath.set(path, digests.crc32)
return {
path,
size,
sha256: sha256File(filePath),
mode:
path === 'agent/goodbuddy-agent' ||
path === 'agent/node' ||
/\/bin\/opencode$/u.test(path)
? '0755'
: '0644'
sha256: digests.sha256,
mode: packageFileMode(path)
}
})
.filter(
@@ -178,18 +198,44 @@ function payloadFiles(staging) {
file.path !== packageDescriptorName &&
file.path !== packageSignatureName
)
return { crc32ByPath, files }
}
function createPackageArchive(staging, output) {
function createPackageArchive(
staging,
output,
knownCrc32 = new Map()
) {
mkdirSync(dirname(output), { recursive: true })
const outputHandle = openSync(output, 'w')
let archiveError
let completed = false
const archive = new Zip((error, data, final) => {
if (error) {
archiveError ??= error
return
const entries = listFiles(staging).map((filePath) => {
const name = relative(staging, filePath).split(sep).join('/')
const nameBytes = Buffer.from(name, 'utf8')
const size = statSync(filePath).size
if (
nameBytes.length === 0 ||
nameBytes.length > 0xffff ||
size > 0xffffffff
) {
throw new Error(`Agent package ZIP entry is too large: ${name}`)
}
return {
filePath,
name,
nameBytes,
size,
crc32:
knownCrc32.get(name) ?? fileDigests(filePath).crc32,
mode: packageFileMode(name),
offset: 0
}
})
if (entries.length === 0 || entries.length > 0xffff) {
throw new Error('Agent package ZIP entry count is invalid')
}
const outputHandle = openSync(output, 'w')
let outputOffset = 0
const archiveHash = createHash('sha256')
const writeBytes = (data) => {
let offset = 0
while (offset < data.byteLength) {
const written = writeSync(
@@ -199,69 +245,101 @@ function createPackageArchive(staging, output) {
data.byteLength - offset
)
if (written <= 0) {
archiveError ??= new Error(
'Agent package archive write made no progress'
)
return
throw new Error('Agent package archive write made no progress')
}
offset += written
}
completed ||= final
})
archiveHash.update(data)
outputOffset += data.byteLength
}
const buffer = Buffer.allocUnsafe(1024 * 1024)
try {
for (const filePath of listFiles(staging)) {
const name = relative(staging, filePath).split(sep).join('/')
const input = new ZipPassThrough(name)
input.mtime = archiveEpoch
archive.add(input)
const inputHandle = openSync(filePath, 'r')
const fileSize = statSync(filePath).size
for (const entry of entries) {
entry.offset = outputOffset
const header = Buffer.alloc(30)
header.writeUInt32LE(zipLocalHeaderSignature, 0)
header.writeUInt16LE(zipVersion, 4)
header.writeUInt16LE(zipUtf8Flag, 6)
header.writeUInt16LE(0, 8)
header.writeUInt16LE(0, 10)
header.writeUInt16LE(zipDosDate, 12)
header.writeUInt32LE(entry.crc32, 14)
header.writeUInt32LE(entry.size, 18)
header.writeUInt32LE(entry.size, 22)
header.writeUInt16LE(entry.nameBytes.length, 26)
header.writeUInt16LE(0, 28)
writeBytes(header)
writeBytes(entry.nameBytes)
const inputHandle = openSync(entry.filePath, 'r')
try {
let position = 0
if (fileSize === 0) {
input.push(new Uint8Array(), true)
}
while (position < fileSize) {
while (position < entry.size) {
const bytesRead = readSync(
inputHandle,
buffer,
0,
buffer.length,
Math.min(buffer.length, entry.size - position),
null
)
if (bytesRead <= 0) {
throw new Error(
`Agent package source read made no progress: ${name}`
`Agent package source read made no progress: ${entry.name}`
)
}
position += bytesRead
input.push(
Uint8Array.from(buffer.subarray(0, bytesRead)),
position === fileSize
)
writeBytes(buffer.subarray(0, bytesRead))
}
} finally {
closeSync(inputHandle)
}
if (archiveError) {
throw archiveError
}
}
archive.end()
if (archiveError) {
throw archiveError
}
if (!completed) {
throw new Error('Agent package archive did not finish')
const centralOffset = outputOffset
for (const entry of entries) {
const record = Buffer.alloc(46)
record.writeUInt32LE(zipCentralHeaderSignature, 0)
record.writeUInt16LE(zipMadeByUnix, 4)
record.writeUInt16LE(zipVersion, 6)
record.writeUInt16LE(zipUtf8Flag, 8)
record.writeUInt16LE(0, 10)
record.writeUInt16LE(0, 12)
record.writeUInt16LE(zipDosDate, 14)
record.writeUInt32LE(entry.crc32, 16)
record.writeUInt32LE(entry.size, 20)
record.writeUInt32LE(entry.size, 24)
record.writeUInt16LE(entry.nameBytes.length, 28)
record.writeUInt16LE(0, 30)
record.writeUInt16LE(0, 32)
record.writeUInt16LE(0, 34)
record.writeUInt16LE(0, 36)
const unixMode =
0o100000 | Number.parseInt(entry.mode, 8)
record.writeUInt32LE((unixMode << 16) >>> 0, 38)
record.writeUInt32LE(entry.offset, 42)
writeBytes(record)
writeBytes(entry.nameBytes)
}
const centralSize = outputOffset - centralOffset
const end = Buffer.alloc(22)
end.writeUInt32LE(zipEndSignature, 0)
end.writeUInt16LE(0, 4)
end.writeUInt16LE(0, 6)
end.writeUInt16LE(entries.length, 8)
end.writeUInt16LE(entries.length, 10)
end.writeUInt32LE(centralSize, 12)
end.writeUInt32LE(centralOffset, 16)
end.writeUInt16LE(0, 20)
writeBytes(end)
fsyncSync(outputHandle)
} catch (error) {
archive.terminate()
throw error
} finally {
closeSync(outputHandle)
}
return {
size: outputOffset,
sha256: archiveHash.digest('hex')
}
}
function assembleAgentPackage(options) {
@@ -345,6 +423,7 @@ function assembleAgentPackage(options) {
agentBundle: resolve(options.agentBundle),
runtimeBundle: runtime.bundleDirectory
})
const payload = payloadFiles(staging)
const initial = {
format: 'goodbuddy-agent-package',
formatVersion: 1,
@@ -364,7 +443,7 @@ function assembleAgentPackage(options) {
protocol: runtime.manifest.protocol
},
contentDigest: `sha256:${'0'.repeat(64)}`,
files: payloadFiles(staging)
files: payload.files
}
const descriptor = {
...initial,
@@ -382,12 +461,16 @@ function assembleAgentPackage(options) {
`${signature.toString('base64')}\n`,
'utf8'
)
createPackageArchive(staging, options.output)
const archive = createPackageArchive(
staging,
options.output,
payload.crc32ByPath
)
return {
descriptor,
archive: basename(options.output),
size: statSync(options.output).size,
sha256: sha256File(options.output)
size: archive.size,
sha256: archive.sha256
}
} finally {
rmSync(staging, { recursive: true, force: true })
@@ -400,6 +483,7 @@ function buildAgentPackage(options) {
options.registry ?? readTrustedKeyRegistry(projectRoot)
const signingIdentity =
options.testSigningIdentity ??
options.signingIdentity ??
productionSigningKey(options.environment ?? process.env)
const agentRoot = mkdtempSync(
join(tmpdir(), `goodbuddy-agent-build-${options.architecture}-`)
@@ -417,7 +501,7 @@ function buildAgentPackage(options) {
...(options.agentLock ? { lock: options.agentLock } : {}),
...(options.testSigningIdentity
? { testSigningIdentity: options.testSigningIdentity }
: {})
: { signingIdentity })
})
const runtime = buildRuntimeBundle({
projectRoot,
@@ -428,7 +512,7 @@ function buildAgentPackage(options) {
...(options.runtimeLock ? { lock: options.runtimeLock } : {}),
...(options.testSigningIdentity
? { testSigningIdentity: options.testSigningIdentity }
: {})
: { signingIdentity })
})
return assembleAgentPackage({
...options,
+53 -97
View File
@@ -5,6 +5,7 @@ const {
createWriteStream,
existsSync,
closeSync,
fstatSync,
mkdirSync,
mkdtempSync,
openSync,
@@ -38,6 +39,9 @@ const { sha256File } = require('./file-hash.cjs')
const {
detectBinaryArchitecture
} = require('./binary-architecture.cjs')
const {
readZipCentralDirectory
} = require('./zip-central-directory.cjs')
const root = join(__dirname, '..')
const packageJson = JSON.parse(
@@ -59,11 +63,17 @@ const harnessHostEntry =
'out/main/deepseek-harness-host-bootstrap.js'
const harnessBundleManifest = 'out/main/package.json'
const harnessPackageVersions = {
'@deepseek-ai/dsh-agent': '0.1.0-rc.6',
'@napi-rs/canvas': '1.0.3',
'node-pty': '1.1.0'
'@deepseek-ai/dsh-agent':
packageLock.packages['node_modules/@deepseek-ai/dsh-agent']
?.version,
'@napi-rs/canvas':
packageLock.packages['node_modules/@napi-rs/canvas']
?.version,
'node-pty':
packageLock.packages['node_modules/node-pty']?.version
}
const koffiVersion = '3.1.4'
const koffiVersion =
packageLock.packages['node_modules/koffi']?.version
const harnessLicenseFiles = [
'agent-client-protocol-Apache-2.0.txt',
'deepseek-cordis-MIT.txt',
@@ -516,9 +526,10 @@ function targetHarnessPaths(options) {
koffiPackage,
koffiBinary,
nodePtyBinary:
options.platform === 'linux'
? 'build/Release/pty.node'
: `prebuilds/${platformName}-${options.arch}/pty.node`,
`prebuilds/${platformName}-${options.arch}/` +
(options.platform === 'windows'
? 'conpty.node'
: 'pty.node'),
nodePtyDirectory: `${platformName}-${options.arch}`
}
}
@@ -994,7 +1005,7 @@ function verifyHarnessPackage(
'DeepSeek Harness Canvas'
)
if (options.platform === 'darwin') {
if (options.platform === 'macos') {
const helper = join(
unpackedRoot,
'node_modules',
@@ -1268,98 +1279,42 @@ async function createPortableZip(
}
function readZipEntryNames(filePath) {
const fileSize = statSync(filePath).size
if (fileSize < 22) {
throw new Error('Portable ZIP 缺少中央目录')
}
const endChunkSize = Math.min(fileSize, 65_557)
const endChunkStart = fileSize - endChunkSize
const endChunk = readChunk(
filePath,
endChunkSize,
endChunkStart
)
let endOffset = -1
for (let index = endChunk.length - 22; index >= 0; index -= 1) {
if (
endChunk.readUInt32LE(index) === 0x06054b50 &&
index + 22 + endChunk.readUInt16LE(index + 20) ===
endChunk.length
) {
endOffset = index
break
}
}
if (endOffset < 0) {
throw new Error('Portable ZIP 缺少中央目录')
}
const diskNumber = endChunk.readUInt16LE(endOffset + 4)
const centralDisk = endChunk.readUInt16LE(endOffset + 6)
const diskEntryCount = endChunk.readUInt16LE(endOffset + 8)
const entryCount = endChunk.readUInt16LE(endOffset + 10)
const centralSize = endChunk.readUInt32LE(endOffset + 12)
const centralOffset = endChunk.readUInt32LE(endOffset + 16)
const absoluteEndOffset = endChunkStart + endOffset
if (
diskNumber !== 0 ||
centralDisk !== 0 ||
diskEntryCount !== entryCount ||
entryCount === 0xffff ||
centralSize === 0xffffffff ||
centralOffset === 0xffffffff ||
entryCount < portableRequiredFiles.length + 1 ||
entryCount > maxPortableZipEntries ||
centralSize < 46 ||
centralSize > maxPortableCentralDirectoryBytes ||
centralOffset + centralSize !== absoluteEndOffset
) {
throw new Error('Portable ZIP 中央目录无效')
}
const centralDirectory = readChunk(
filePath,
centralSize,
centralOffset
)
const names = []
let offset = 0
for (let index = 0; index < entryCount; index += 1) {
if (
offset + 46 > centralDirectory.length ||
centralDirectory.readUInt32LE(offset) !== 0x02014b50
) {
throw new Error('Portable ZIP 中央目录条目无效')
}
const nameLength = centralDirectory.readUInt16LE(offset + 28)
const extraLength = centralDirectory.readUInt16LE(offset + 30)
const commentLength = centralDirectory.readUInt16LE(offset + 32)
const entryLength = 46 + nameLength + extraLength + commentLength
if (offset + entryLength > centralDirectory.length) {
throw new Error('Portable ZIP 中央目录条目越界')
}
const name = centralDirectory
.subarray(offset + 46, offset + 46 + nameLength)
.toString(
centralDirectory.readUInt16LE(offset + 8) & 0x0800
? 'utf8'
: 'latin1'
const handle = openSync(filePath, 'r')
try {
let parsed
try {
parsed = readZipCentralDirectory(
handle,
fstatSync(handle).size,
{
label: 'Portable ZIP',
minimumEntries: portableRequiredFiles.length + 1,
maximumEntries: maxPortableZipEntries,
maximumCentralDirectoryBytes:
maxPortableCentralDirectoryBytes
}
)
.replaceAll('\\', '/')
if (
!name ||
name.startsWith('/') ||
/^[a-z]:\//iu.test(name) ||
name.includes('\0') ||
name.split('/').some((part) => part === '..')
) {
throw new Error(`Portable ZIP 包含不安全路径:${name}`)
} catch (error) {
throw new Error('Portable ZIP 中央目录无效', {
cause: error
})
}
names.push(name)
offset += entryLength
return parsed.entries.map((entry) => {
const name = entry.name.replaceAll('\\', '/')
if (
!name ||
name.startsWith('/') ||
/^[a-z]:\//iu.test(name) ||
name.includes('\0') ||
name.split('/').some((part) => part === '..')
) {
throw new Error(`Portable ZIP 包含不安全路径:${name}`)
}
return name
})
} finally {
closeSync(handle)
}
if (offset !== centralDirectory.length) {
throw new Error('Portable ZIP 中央目录数量不一致')
}
return names
}
function verifyPortableZip(filePath) {
@@ -1692,6 +1647,7 @@ module.exports = {
replaceOutput,
stageTargetRuntimeDependencies,
targetRuntimePackageNames,
targetHarnessPaths,
verifyHarnessPackage,
verifyArchiveIntegrity,
verifyUnpackedOutput,
+1 -1
View File
@@ -183,7 +183,7 @@ async function run() {
provider: 'goodbuddy',
model: 'qwen-plus',
supportsImageInput: false,
harnessVersion: '0.1.0-rc.6',
harnessVersion: '0.1.0-rc.8',
credentialRefs: ['GOODBUDDY_HARNESS_MODEL_API_KEY'],
skillPackages: [],
extensionPackages: [],
+7 -6
View File
@@ -44,9 +44,9 @@ const supportedArchitectures = Object.freeze(['x64', 'arm64'])
const manifestFileName = 'manifest.json'
const signatureFileName = 'manifest.sig'
const signingPrivateKeyEnvironment =
'GOODBUDDY_REMOTE_RUNTIME_SIGNING_PRIVATE_KEY'
'GOODBUDDY_SIGNING_PRIVATE_KEY'
const signingKeyIdEnvironment =
'GOODBUDDY_REMOTE_RUNTIME_SIGNING_KEY_ID'
'GOODBUDDY_SIGNING_KEY_ID'
const signatureDomain = Buffer.from(
'GoodBuddy Remote Runtime Bundle Manifest Signature v1\0',
'utf8'
@@ -265,7 +265,7 @@ function productionSigningIdentity(environment = process.env) {
const privateKey = environment[signingPrivateKeyEnvironment]
if (!keyId || !privateKey) {
throw new Error(
`${signingKeyIdEnvironment} and ${signingPrivateKeyEnvironment} are required for production Runtime signing`
`${signingKeyIdEnvironment} and ${signingPrivateKeyEnvironment} are required to sign the bundled Runtime`
)
}
return { keyId, privateKey }
@@ -318,11 +318,11 @@ function preflightProductionSigningKey(options = {}) {
const registry =
options.registry ?? readTrustedKeyRegistry(projectRoot)
return preflightRegisteredProductionKey({
component: 'Runtime',
component: 'GoodBuddy',
keyId,
registry,
missingKeyIdMessage:
`${signingKeyIdEnvironment} is not configured; provision the production Runtime signing key ID before building a release`
`${signingKeyIdEnvironment} is not configured; provision the production GoodBuddy signing key ID before building a release`
})
}
@@ -768,6 +768,7 @@ function buildRuntimeBundle(options) {
options.registry ?? readTrustedKeyRegistry(projectRoot)
const signingIdentity =
options.testSigningIdentity ??
options.signingIdentity ??
productionSigningIdentity(options.environment ?? process.env)
const verificationEnvironment =
options.testSigningIdentity ? 'test' : 'production'
@@ -1050,7 +1051,7 @@ function main(argv = process.argv.slice(2)) {
if (options.command === 'preflight') {
const record = preflightProductionSigningKey()
console.log(
`Production Runtime signing preflight passed: ${record.keyId}`
`Production GoodBuddy signing preflight passed for bundled Runtime: ${record.keyId}`
)
return
}
+1 -1
View File
@@ -88,7 +88,7 @@ async function main() {
`${JSON.stringify(
{
name: '@deepseek-ai/dsh-llm',
version: '0.1.0-rc.6',
version: '0.1.0-rc.8',
private: true,
type: 'module'
},
+141
View File
@@ -0,0 +1,141 @@
const { readSync } = require('node:fs')
const localFileHeaderSignature = 0x04034b50
const centralDirectoryHeaderSignature = 0x02014b50
const endOfCentralDirectorySignature = 0x06054b50
function readExactZipBytes(handle, length, position, label) {
const output = Buffer.allocUnsafe(length)
let offset = 0
while (offset < length) {
const bytesRead = readSync(
handle,
output,
offset,
length - offset,
position + offset
)
if (bytesRead <= 0) {
throw new Error(`${label} is truncated`)
}
offset += bytesRead
}
return output
}
function readZipCentralDirectory(handle, archiveSize, limits) {
if (archiveSize < 22) {
throw new Error(`${limits.label} central directory is missing`)
}
const endSize = Math.min(archiveSize, 65_557)
const endStart = archiveSize - endSize
const end = readExactZipBytes(
handle,
endSize,
endStart,
`${limits.label} end record`
)
let endOffset = -1
for (let index = end.length - 22; index >= 0; index -= 1) {
if (
end.readUInt32LE(index) === endOfCentralDirectorySignature &&
index + 22 + end.readUInt16LE(index + 20) === end.length
) {
endOffset = index
break
}
}
if (endOffset < 0) {
throw new Error(`${limits.label} central directory is missing`)
}
const diskNumber = end.readUInt16LE(endOffset + 4)
const centralDisk = end.readUInt16LE(endOffset + 6)
const diskEntryCount = end.readUInt16LE(endOffset + 8)
const entryCount = end.readUInt16LE(endOffset + 10)
const centralSize = end.readUInt32LE(endOffset + 12)
const centralOffset = end.readUInt32LE(endOffset + 16)
const absoluteEndOffset = endStart + endOffset
if (
diskNumber !== 0 ||
centralDisk !== 0 ||
diskEntryCount !== entryCount ||
entryCount === 0 ||
entryCount === 0xffff ||
entryCount < limits.minimumEntries ||
entryCount > limits.maximumEntries ||
centralSize === 0xffffffff ||
centralSize < 46 ||
centralSize > limits.maximumCentralDirectoryBytes ||
centralOffset === 0xffffffff ||
centralOffset + centralSize !== absoluteEndOffset
) {
throw new Error(`${limits.label} central directory is invalid`)
}
const central = readExactZipBytes(
handle,
centralSize,
centralOffset,
`${limits.label} central directory`
)
const entries = []
let offset = 0
for (let index = 0; index < entryCount; index += 1) {
if (
offset + 46 > central.length ||
central.readUInt32LE(offset) !==
centralDirectoryHeaderSignature
) {
throw new Error(
`${limits.label} central directory entry is invalid`
)
}
const nameLength = central.readUInt16LE(offset + 28)
const extraLength = central.readUInt16LE(offset + 30)
const commentLength = central.readUInt16LE(offset + 32)
const entryLength =
46 + nameLength + extraLength + commentLength
if (offset + entryLength > central.length) {
throw new Error(
`${limits.label} central directory entry exceeds its bounds`
)
}
const flags = central.readUInt16LE(offset + 8)
const nameBytes = central.subarray(
offset + 46,
offset + 46 + nameLength
)
const name = new TextDecoder(
flags & 0x0800 ? 'utf-8' : 'latin1',
{ fatal: true }
).decode(nameBytes)
entries.push({
name,
nameBytes: Buffer.from(nameBytes),
flags,
compression: central.readUInt16LE(offset + 10),
checksum: central.readUInt32LE(offset + 16),
compressedSize: central.readUInt32LE(offset + 20),
size: central.readUInt32LE(offset + 24),
diskStart: central.readUInt16LE(offset + 34),
localOffset: central.readUInt32LE(offset + 42)
})
offset += entryLength
}
if (offset !== central.length) {
throw new Error(
`${limits.label} central directory count is invalid`
)
}
return {
centralOffset,
entries
}
}
module.exports = {
centralDirectoryHeaderSignature,
endOfCentralDirectorySignature,
localFileHeaderSignature,
readExactZipBytes,
readZipCentralDirectory
}
@@ -7,7 +7,7 @@
| 文档状态 | 实现与发布验收基线 |
| 设计目标 | 将 DeepSeek Harness 作为 GoodBuddy 的第三个 Agent Runtime |
| Runtime 标识 | `deepseek-harness` |
| 首版依赖基线 | 实际使用的 `@deepseek-ai/dsh-*` 底层库,精确锁定 `0.1.0-rc.6` |
| 当前依赖基线 | 实际使用的 `@deepseek-ai/dsh-*` 底层库,精确锁定 `0.1.0-rc.8` |
| 上游状态 | Developer Preview,允许出现破坏性变更 |
| 上游许可证 | MIT |
| GoodBuddy 目标平台 | Windows、macOS、Linux,x64 与 arm64 |
@@ -446,7 +446,7 @@ DeepSeek Harness 首版只使用符合下列边界的 GoodBuddy 模型连接:
- 文本模型收到图片时必须在启动 Host 或发起模型网络调用前返回明确错误,不能静默丢弃图片。
- 图片模型只接受内联 JPEG/PNG,不接受 URL、文件路径、ACP `uri` 或其他媒体类型。
- Main 已通过 `nativeImage` 解码用户选择的图片并生成有界模型输入;Utility 仍须独立执行严格 base64、签名、容器结构、CRC(PNG)、完整解码、尺寸和摘要校验,不能把 Main 校验当作跨进程信任替代。
- 每条消息最多 8 张图,单图编码后最多 1 MiB,图片合计最多 2 MiB,单图最多 1,600 万像素,累计解码像素最多 3,200 万(重复引用也计入预算)。进程内 Store 另设 32 MiB、256 个唯一对象的总上限。
- 每条消息最多 8 张图,单图编码后最多 1 MiB,图片合计最多 2 MiB,单边最长 8,192 px,单图最多 1,600 万像素,累计解码像素最多 3,200 万(重复引用也计入预算)。进程内 Store 另设 32 MiB、256 个唯一对象的总上限。
- Attachment Store 只服务当前非持久 Harness Session;引用按 Session 释放,Host 退出时清空,不写入磁盘或 GoodBuddy 第二份会话日志。
- GoodBuddy 历史、Prompt、系统指令分别保持不同信任层。
- 任何用户文本都不能进入 Cordis 配置表达式或模块名。
@@ -459,7 +459,7 @@ DeepSeek Harness 首版只使用符合下列边界的 GoodBuddy 模型连接:
| --- | --- |
| 单个 JSON-RPC 帧 | 8 MiB |
| 单图 / 单条消息图片 | 1 MiB / 8 张且合计 2 MiB |
| 单图 / 单条消息解码像素 | 1,600 万 / 3,200 万 |
| 单图尺寸 / 单条消息解码像素 | 单边最长 8,192 px 且 1,600 万像素 / 3,200 万像素 |
| Host 临时图片存储 | 32 MiB 且最多 256 个唯一对象 |
| 单个文本或推理事件 | 64 KiB |
| 单次请求累计协议输出 | 4 MiB |
@@ -497,7 +497,7 @@ Runtime: GoodBuddy 内置 DeepSeek Harness
模型配置: 跟随 GoodBuddy · 企业网关(qwen-plus)
状态: 已就绪
路径: <受控 Host 路径>
版本: 0.1.0-rc.6
版本: 0.1.0-rc.8
执行权限: 当前用户权限
Host 始终由当前 GoodBuddy 版本提供,不存在自定义 Host 入口。
@@ -2,7 +2,7 @@
## 状态
本文记录当前代码实现,不定义额外的信任框架。Windows 到 Linux x64 的安装、Main-only 模型桥、断线恢复、输出重放、同一 OpenCode Session 续接与终态清理已经在真实 Host 上完成 provider-free 验证。上一轮 Linux x64 验收使用 Agent `0.11.2-e2e.12` 与 OpenCode Runtime `1.18.9` 完成了一次有界真实模型工具调用;加入每 helper 随机 loopback 路径 capability 后,当前源码 lock 已转为正式 Agent `0.11.2`,尚未复用旧版本号。远程 OpenCode 功能仍处于发布前验证阶段;公开签名 key registry 已供应,当前发布门槛是通过独立 Agent workflow 正式发布并公开验证 Linux x64/arm64 复合包和签名累计目录。
本文记录当前代码实现,不定义额外的信任框架。Windows 到 Linux x64 的安装、Main-only 模型桥、断线恢复、输出重放、同一 OpenCode Session 续接与终态清理已经在真实 Host 上完成 provider-free 验证。上一轮 Linux x64 验收使用 Agent `0.11.2-e2e.12` 与 OpenCode Runtime `1.18.9` 完成了一次有界真实模型工具调用;加入每 helper 随机 loopback 路径 capability 后,当前源码 lock 已转为正式 Agent `0.11.4`。失败的 `agent-v0.11.3` 保持不可变且未发布。远程 OpenCode 功能仍处于发布前验证阶段;公开签名 key registry 已供应,当前发布门槛是通过独立 Agent workflow 正式发布并公开验证 Linux x64/arm64 复合包和签名累计目录。
## 产品语义
@@ -170,7 +170,7 @@ Execute 不经过 Ask 的 bubblewrap profile:
- 2026-08 的本地 fixture 完整验证 Linux x64 Agent `0.11.2-e2e.12`、Node `24.19.0`
和 Agent protocol `2.0`;当时没有 arm64 fixture,因此该记录不能作为当前独立发布
的双架构验收。当前源码 lock 是正式版本 `0.11.2`,需要由新的复合包发布流程另行验证。
的双架构验收。当前源码 lock 是正式版本 `0.11.4`,需要由新的复合包发布流程另行验证。
- 正常 Host 更新路径把 Linux x64 Host 的 Agent 更新为 `0.11.2-e2e.12`,并确认
OpenCode Runtime 已安装版本与所需版本均为 `1.18.9`。
- 一条新的 Ask 用户操作只提交一次。OpenCode 先在 build 模型轮次请求一个原生
@@ -216,7 +216,9 @@ Renderer 暴露固定方法名、数字 RPC code 和有界 service code,不转
`.remote-runtime-resources` 或任何可安装 Linux 远端 payload。
- `build/agent-package.cjs` 在对应原生 Linux 架构组装确定性 `.gbagent`,外层签名覆盖
描述符和每个内部文件身份;内部 Agent 与 Runtime 仍分别使用既有签名 manifest 和 lock
校验。`build/agent-catalog.cjs` 为双架构包生成签名累计目录,拒绝同一版本/架构改变字节。
校验,但所有 production 层统一使用一组 GoodBuddy 通用发布身份并通过签名域区分用途,
不要求内部 Runtime 单独配置密钥。`build/agent-catalog.cjs` 为双架构包生成签名累计目录,
拒绝同一版本/架构改变字节。
- `.github/workflows/agents.yml` 只使用进程内临时测试 key 做分支/PR 原生验证,不发布。
`.github/workflows/agent-release.yml` 才可在 annotated `agent-v<version>` 标签和受保护
`agent-signing` Environment 中构建 production 包。Agent GitHub Release 必须
+316 -313
View File
@@ -1,39 +1,39 @@
{
"name": "goodbuddy",
"version": "0.11.2",
"version": "0.11.4",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "goodbuddy",
"version": "0.11.2",
"version": "0.11.4",
"license": "0BSD",
"dependencies": {
"@agentclientprotocol/sdk": "0.25.1",
"@antv/g6": "^5.1.1",
"@deepseek-ai/cordis": "4.0.1",
"@deepseek-ai/dsh-agent": "0.1.0-rc.6",
"@deepseek-ai/dsh-agent-loop": "0.1.0-rc.6",
"@deepseek-ai/dsh-bash-local": "0.1.0-rc.6",
"@deepseek-ai/dsh-credentials": "0.1.0-rc.6",
"@deepseek-ai/dsh-fs-local": "0.1.0-rc.6",
"@deepseek-ai/dsh-llm": "0.1.0-rc.6",
"@deepseek-ai/dsh-llm-pi-ai": "0.1.0-rc.6",
"@deepseek-ai/dsh-pwsh-local": "0.1.0-rc.6",
"@deepseek-ai/dsh-sandbox": "0.1.0-rc.6",
"@deepseek-ai/dsh-sandbox-policy": "0.1.0-rc.6",
"@deepseek-ai/dsh-session": "0.1.0-rc.6",
"@deepseek-ai/dsh-shell-env": "0.1.0-rc.6",
"@deepseek-ai/dsh-skill": "0.1.0-rc.6",
"@deepseek-ai/dsh-subprocess-local": "0.1.0-rc.6",
"@deepseek-ai/dsh-system-prompt": "0.1.0-rc.6",
"@deepseek-ai/dsh-token-meter": "0.1.0-rc.6",
"@deepseek-ai/dsh-tool-bash": "0.1.0-rc.6",
"@deepseek-ai/dsh-tool-fs": "0.1.0-rc.6",
"@deepseek-ai/dsh-tool-pwsh": "0.1.0-rc.6",
"@deepseek-ai/dsh-tool-skill": "0.1.0-rc.6",
"@deepseek-ai/dsh-tools": "0.1.0-rc.6",
"@deepseek-ai/dsh-user-approval": "0.1.0-rc.6",
"@deepseek-ai/dsh-agent": "0.1.0-rc.8",
"@deepseek-ai/dsh-agent-loop": "0.1.0-rc.8",
"@deepseek-ai/dsh-bash-local": "0.1.0-rc.8",
"@deepseek-ai/dsh-credentials": "0.1.0-rc.8",
"@deepseek-ai/dsh-fs-local": "0.1.0-rc.8",
"@deepseek-ai/dsh-llm": "0.1.0-rc.8",
"@deepseek-ai/dsh-llm-pi-ai": "0.1.0-rc.8",
"@deepseek-ai/dsh-pwsh-local": "0.1.0-rc.8",
"@deepseek-ai/dsh-sandbox": "0.1.0-rc.8",
"@deepseek-ai/dsh-sandbox-policy": "0.1.0-rc.8",
"@deepseek-ai/dsh-session": "0.1.0-rc.8",
"@deepseek-ai/dsh-shell-env": "0.1.0-rc.8",
"@deepseek-ai/dsh-skill": "0.1.0-rc.8",
"@deepseek-ai/dsh-subprocess-local": "0.1.0-rc.8",
"@deepseek-ai/dsh-system-prompt": "0.1.0-rc.8",
"@deepseek-ai/dsh-token-meter": "0.1.0-rc.8",
"@deepseek-ai/dsh-tool-bash": "0.1.0-rc.8",
"@deepseek-ai/dsh-tool-fs": "0.1.0-rc.8",
"@deepseek-ai/dsh-tool-pwsh": "0.1.0-rc.8",
"@deepseek-ai/dsh-tool-skill": "0.1.0-rc.8",
"@deepseek-ai/dsh-tools": "0.1.0-rc.8",
"@deepseek-ai/dsh-user-approval": "0.1.0-rc.8",
"@huggingface/tokenizers": "^0.1.3",
"@modelcontextprotocol/sdk": "^1.30.0",
"@napi-rs/canvas": "1.0.3",
@@ -1485,96 +1485,96 @@
"license": "MIT"
},
"node_modules/@deepseek-ai/dsh-agent": {
"version": "0.1.0-rc.6",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-agent/-/dsh-agent-0.1.0-rc.6.tgz",
"integrity": "sha512-vtqq2pWTrzn0dKfj5kREZRpP82AwtGjGx9V1lYnKvF+Uc/a8zyWbSvjDE7V1d3YQAQJzs2cWO31hURWDekDXIA==",
"version": "0.1.0-rc.8",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-agent/-/dsh-agent-0.1.0-rc.8.tgz",
"integrity": "sha512-fOl46ZvzoYHalrlyfOJAHNhzyKeVDTUnZZKNSJEr9GY/98WJTjcV21agVEofTXldkJ7a/ws2HpJkVNJVFMtslg==",
"license": "MIT",
"peerDependencies": {
"@deepseek-ai/cordis": "^4.0.1",
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.6",
"@deepseek-ai/dsh-llm": "^0.1.0-rc.6",
"@deepseek-ai/dsh-scope": "^0.1.0-rc.6",
"@deepseek-ai/dsh-session": "^0.1.0-rc.6",
"@deepseek-ai/dsh-system-prompt": "^0.1.0-rc.6",
"@deepseek-ai/dsh-typert-protocol": "^0.1.0-rc.6"
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.8",
"@deepseek-ai/dsh-llm": "^0.1.0-rc.8",
"@deepseek-ai/dsh-scope": "^0.1.0-rc.8",
"@deepseek-ai/dsh-session": "^0.1.0-rc.8",
"@deepseek-ai/dsh-system-prompt": "^0.1.0-rc.8",
"@deepseek-ai/dsh-typert-protocol": "^0.1.0-rc.8"
}
},
"node_modules/@deepseek-ai/dsh-agent-loop": {
"version": "0.1.0-rc.6",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-agent-loop/-/dsh-agent-loop-0.1.0-rc.6.tgz",
"integrity": "sha512-yShuKIMW360H14L4y13j3gz3Ix1s/3lwEEpfJW4hnFAE09h9Z4yJA7UfTQmQdzMRMnuj4uWFPw0e0BbapnkFuw==",
"version": "0.1.0-rc.8",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-agent-loop/-/dsh-agent-loop-0.1.0-rc.8.tgz",
"integrity": "sha512-6Vg5oLy3UZ6lJKMPxldqqBylKSMOpbV/FRrqzDqn3hGdksYBzt9K2hUJm/rE2RyguYKUKFPm2hExs0nn4HKmWA==",
"license": "MIT",
"dependencies": {
"@deepseek-ai/schemastery": "^3.18.1"
},
"peerDependencies": {
"@deepseek-ai/cordis": "^4.0.1",
"@deepseek-ai/dsh-agent": "^0.1.0-rc.6",
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.6",
"@deepseek-ai/dsh-llm": "^0.1.0-rc.6",
"@deepseek-ai/dsh-scope": "^0.1.0-rc.6",
"@deepseek-ai/dsh-session": "^0.1.0-rc.6",
"@deepseek-ai/dsh-session-persistence": "^0.1.0-rc.6",
"@deepseek-ai/dsh-settings": "^0.1.0-rc.6",
"@deepseek-ai/dsh-system-prompt": "^0.1.0-rc.6",
"@deepseek-ai/dsh-tools": "^0.1.0-rc.6"
"@deepseek-ai/dsh-agent": "^0.1.0-rc.8",
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.8",
"@deepseek-ai/dsh-llm": "^0.1.0-rc.8",
"@deepseek-ai/dsh-scope": "^0.1.0-rc.8",
"@deepseek-ai/dsh-session": "^0.1.0-rc.8",
"@deepseek-ai/dsh-session-persistence": "^0.1.0-rc.8",
"@deepseek-ai/dsh-settings": "^0.1.0-rc.8",
"@deepseek-ai/dsh-system-prompt": "^0.1.0-rc.8",
"@deepseek-ai/dsh-tools": "^0.1.0-rc.8"
}
},
"node_modules/@deepseek-ai/dsh-attachment": {
"version": "0.1.0-rc.6",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-attachment/-/dsh-attachment-0.1.0-rc.6.tgz",
"integrity": "sha512-3P6N17NQ8jqSQGzeCs+svCIqArU8oq0YmgEAo+axN9aVuUDferWU4DLRSX59UGpmyldX4LQn81toA+c+DqMcHg==",
"version": "0.1.0-rc.8",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-attachment/-/dsh-attachment-0.1.0-rc.8.tgz",
"integrity": "sha512-cCrg4WWiav7pGtbdU8dJTpAG28cPnkWVB2YPOrlBCgBbhQziGcG9Dv67Ti6L3PI4OQKC55gYBFinQghFO/7FGg==",
"license": "MIT",
"peer": true,
"peerDependencies": {
"@deepseek-ai/cordis": "^4.0.1",
"@deepseek-ai/dsh-brand": "^0.1.0-rc.6",
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.6"
"@deepseek-ai/dsh-brand": "^0.1.0-rc.8",
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.8"
}
},
"node_modules/@deepseek-ai/dsh-bash-local": {
"version": "0.1.0-rc.6",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-bash-local/-/dsh-bash-local-0.1.0-rc.6.tgz",
"integrity": "sha512-YAc5W9uR7sN1Rrobp6fQlCaOdHqCWatTktV68UsoDuOJs5eM3nWEUUUVN8jdZCIDZhktT5hqbhe3+DzOX3Xg1w==",
"version": "0.1.0-rc.8",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-bash-local/-/dsh-bash-local-0.1.0-rc.8.tgz",
"integrity": "sha512-vMf3dMRrvVvwWCLJxobYONSebPtWqy2ZZKpRZ6o5fBrZbM88irbvL6F/6r/mu4ADTIDigUtpoxlRpOEQKbSuTg==",
"license": "MIT",
"dependencies": {
"@deepseek-ai/schemastery": "^3.18.1"
},
"peerDependencies": {
"@deepseek-ai/cordis": "^4.0.1",
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.6",
"@deepseek-ai/dsh-settings": "^0.1.0-rc.6",
"@deepseek-ai/dsh-shell": "^0.1.0-rc.6",
"@deepseek-ai/dsh-subprocess": "^0.1.0-rc.6",
"@deepseek-ai/dsh-timeout": "^0.1.0-rc.6"
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.8",
"@deepseek-ai/dsh-settings": "^0.1.0-rc.8",
"@deepseek-ai/dsh-shell": "^0.1.0-rc.8",
"@deepseek-ai/dsh-subprocess": "^0.1.0-rc.8",
"@deepseek-ai/dsh-timeout": "^0.1.0-rc.8"
}
},
"node_modules/@deepseek-ai/dsh-brand": {
"version": "0.1.0-rc.6",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-brand/-/dsh-brand-0.1.0-rc.6.tgz",
"integrity": "sha512-E8j9Nby24qP4rfrdcfc7bpt1CHpGT3tYmycOJJkEOH4ptIdT1m2ro9nmnSd5CWYukTr64A77vjm2WGqHRI92UA==",
"version": "0.1.0-rc.8",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-brand/-/dsh-brand-0.1.0-rc.8.tgz",
"integrity": "sha512-402aUAfHxIZJrArBV4gDf0I/A/69A/By26FX6HTIOyFmCnB4wUBKh8jnglz1CorKWdZy8AKldAhh8HQhQEbGOQ==",
"license": "MIT",
"peer": true,
"peerDependencies": {
"@deepseek-ai/cordis": "^4.0.1",
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.6"
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.8"
}
},
"node_modules/@deepseek-ai/dsh-code-runtime": {
"version": "0.1.0-rc.6",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-code-runtime/-/dsh-code-runtime-0.1.0-rc.6.tgz",
"integrity": "sha512-aw8D4IOeMo11A3uxQeE4LFoW3bvaQnVkGFqqtS+lsINDARrOCJHXLUgecoKpys+Lc5erZZ4UQDnymJmL4OCcKA==",
"version": "0.1.0-rc.8",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-code-runtime/-/dsh-code-runtime-0.1.0-rc.8.tgz",
"integrity": "sha512-6ZKyEE7dHH1UkKe5b0lg18Byww/7ocZyrNMVjAoUWEWp5i96Brf9Eavu9q3zepBMDMZJErCfV0pyt7KDhBJSig==",
"license": "MIT",
"peer": true,
"peerDependencies": {
"@deepseek-ai/cordis": "^4.0.1",
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.6"
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.8"
}
},
"node_modules/@deepseek-ai/dsh-commands": {
"version": "0.1.0-rc.6",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-commands/-/dsh-commands-0.1.0-rc.6.tgz",
"integrity": "sha512-dq1GZmGTPXEGG01ksZ6Jj6DxAkzAbg+nepvfnM/P74d6EUcsFsEy7CfS18n0VJ6/2Wp0xOWsQyyuFElqbynO7w==",
"version": "0.1.0-rc.8",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-commands/-/dsh-commands-0.1.0-rc.8.tgz",
"integrity": "sha512-/i99EDLrUyd4bUVn4d7XS46vg09ZE0ervbE/aQaVUWLjDYKrarb7hPx9slADAg5/cUlJsN6L6CP544h9qlUo4g==",
"license": "MIT",
"peer": true,
"dependencies": {
@@ -1582,58 +1582,60 @@
},
"peerDependencies": {
"@deepseek-ai/cordis": "^4.0.1",
"@deepseek-ai/dsh-agent": "^0.1.0-rc.6",
"@deepseek-ai/dsh-brand": "^0.1.0-rc.6",
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.6",
"@deepseek-ai/dsh-scope": "^0.1.0-rc.6",
"@deepseek-ai/dsh-session": "^0.1.0-rc.6",
"@deepseek-ai/dsh-typert-protocol": "^0.1.0-rc.6"
"@deepseek-ai/dsh-agent": "^0.1.0-rc.8",
"@deepseek-ai/dsh-attachment": "^0.1.0-rc.8",
"@deepseek-ai/dsh-brand": "^0.1.0-rc.8",
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.8",
"@deepseek-ai/dsh-llm": "^0.1.0-rc.8",
"@deepseek-ai/dsh-scope": "^0.1.0-rc.8",
"@deepseek-ai/dsh-session": "^0.1.0-rc.8",
"@deepseek-ai/dsh-typert-protocol": "^0.1.0-rc.8"
}
},
"node_modules/@deepseek-ai/dsh-compaction": {
"version": "0.1.0-rc.6",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-compaction/-/dsh-compaction-0.1.0-rc.6.tgz",
"integrity": "sha512-Uu8qgrHom13gdwwxAnqmNuWM8MqafkRlhjQMMJYmaMVVxjEUewSJ6lwtMbUEBR5ViLsfv4pTm5TnhxNCjedBUw==",
"version": "0.1.0-rc.8",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-compaction/-/dsh-compaction-0.1.0-rc.8.tgz",
"integrity": "sha512-sWNqZqejzpKsnEMPsNVA/WYK4S59XlYJtnWw8wCPushzQdUt5zhf+g7LVx0cStSozEfwurxRvaZdOmNaa4WhNQ==",
"license": "MIT",
"peer": true,
"peerDependencies": {
"@deepseek-ai/cordis": "^4.0.1",
"@deepseek-ai/dsh-brand": "^0.1.0-rc.6",
"@deepseek-ai/dsh-commands": "^0.1.0-rc.6",
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.6",
"@deepseek-ai/dsh-llm": "^0.1.0-rc.6",
"@deepseek-ai/dsh-session": "^0.1.0-rc.6"
"@deepseek-ai/dsh-brand": "^0.1.0-rc.8",
"@deepseek-ai/dsh-commands": "^0.1.0-rc.8",
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.8",
"@deepseek-ai/dsh-llm": "^0.1.0-rc.8",
"@deepseek-ai/dsh-session": "^0.1.0-rc.8"
}
},
"node_modules/@deepseek-ai/dsh-credentials": {
"version": "0.1.0-rc.6",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-credentials/-/dsh-credentials-0.1.0-rc.6.tgz",
"integrity": "sha512-zyYRs3A9gxfZjZfONzJdMhM0Gzbslpha+FGYkLDRAozgPj0N7mZdE+Qflx2V4WNrCnsSjuvOW0HFBxWtSRUTUw==",
"version": "0.1.0-rc.8",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-credentials/-/dsh-credentials-0.1.0-rc.8.tgz",
"integrity": "sha512-VopOhtUEa/fSmJr3fyKbsEG043kX1oGuBrmELAbkhmuDigl1cN88g0VPb9rMG2/dJ1h08wg+h0RJP+Ex4LjRWw==",
"license": "MIT",
"peerDependencies": {
"@deepseek-ai/cordis": "^4.0.1",
"@deepseek-ai/dsh-brand": "^0.1.0-rc.6",
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.6"
"@deepseek-ai/dsh-brand": "^0.1.0-rc.8",
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.8"
}
},
"node_modules/@deepseek-ai/dsh-fs": {
"version": "0.1.0-rc.6",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-fs/-/dsh-fs-0.1.0-rc.6.tgz",
"integrity": "sha512-OTkwb4QsZgmjtA/8ZEPh1FapmrBr3N989/G4Wmo1JkAvKbMxkYty6LxjckOawTTz7GJTfUoZCrw9uopDfIMMNw==",
"version": "0.1.0-rc.8",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-fs/-/dsh-fs-0.1.0-rc.8.tgz",
"integrity": "sha512-ge5GW56+z20vYACQ2jqtevusdgMXa64laeU/Chql+VIA3+6xyJ8gLX+oj/1O5TlzhvmZ7ZPfZGBFBEVsEZzTQw==",
"license": "MIT",
"peer": true,
"peerDependencies": {
"@deepseek-ai/cordis": "^4.0.1",
"@deepseek-ai/dsh-brand": "^0.1.0-rc.6",
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.6",
"@deepseek-ai/dsh-llm": "^0.1.0-rc.6",
"@deepseek-ai/dsh-sandbox": "^0.1.0-rc.6"
"@deepseek-ai/dsh-brand": "^0.1.0-rc.8",
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.8",
"@deepseek-ai/dsh-llm": "^0.1.0-rc.8",
"@deepseek-ai/dsh-sandbox": "^0.1.0-rc.8"
}
},
"node_modules/@deepseek-ai/dsh-fs-local": {
"version": "0.1.0-rc.6",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-fs-local/-/dsh-fs-local-0.1.0-rc.6.tgz",
"integrity": "sha512-zP0OW474s7tvzEVQnBBTGL6Jrv/YyQXt4nswIbc3Aqr9JyHSfhZ/5IJoipZqfYXLS3ISWMhrF2npRg8L1NGsIQ==",
"version": "0.1.0-rc.8",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-fs-local/-/dsh-fs-local-0.1.0-rc.8.tgz",
"integrity": "sha512-yWk6WrAHs8b6395XnZZOCf3wY90xYKjh/tyU3/rwFy4oU8zq9gzn/x8rdaZD0zUp+wE0XqfBf2AS4Jo03SKHyQ==",
"license": "MIT",
"dependencies": {
"@deepseek-ai/schemastery": "^3.18.1",
@@ -1641,25 +1643,25 @@
},
"peerDependencies": {
"@deepseek-ai/cordis": "^4.0.1",
"@deepseek-ai/dsh-fs": "^0.1.0-rc.6",
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.6"
"@deepseek-ai/dsh-fs": "^0.1.0-rc.8",
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.8"
}
},
"node_modules/@deepseek-ai/dsh-home-paths": {
"version": "0.1.0-rc.6",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-home-paths/-/dsh-home-paths-0.1.0-rc.6.tgz",
"integrity": "sha512-gIiUBmqB3L8inFr+hvjZv2/i6EVJOHIHCHg7bBFVhcl4HDK94+8wUCXTLGy80tcuISzIQiIaLuJq/NhSmV9Amw==",
"version": "0.1.0-rc.8",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-home-paths/-/dsh-home-paths-0.1.0-rc.8.tgz",
"integrity": "sha512-9cQhH9CZsw7Zi73VnZk7CcuEVA6yR/Ox+7lp3VHq53fFYSPi0RL+kMWmh5XCrZB4U1OM3+PVsWsA5II9tB+t0Q==",
"license": "MIT",
"peer": true,
"peerDependencies": {
"@deepseek-ai/cordis": "^4.0.1",
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.6"
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.8"
}
},
"node_modules/@deepseek-ai/dsh-invariants": {
"version": "0.1.0-rc.6",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-invariants/-/dsh-invariants-0.1.0-rc.6.tgz",
"integrity": "sha512-WfEfOi99a4cpOugRAHTBSTnesLieu3ist1q9PXDXFBHX++K1rAl9+sB7YrdnbB8LH0UOY532gS9xJUYU6w0SLw==",
"version": "0.1.0-rc.8",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-invariants/-/dsh-invariants-0.1.0-rc.8.tgz",
"integrity": "sha512-u0lYqyxOYwfsVnbsfGXZos5vFvA4cqFnBEW3/ezgljNwkYwzeUP/Y5wjPnQjP+ZzBn3CnVeIF6s2N2Vk3iA5mQ==",
"license": "MIT",
"peer": true,
"dependencies": {
@@ -1670,50 +1672,50 @@
}
},
"node_modules/@deepseek-ai/dsh-jobs": {
"version": "0.1.0-rc.6",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-jobs/-/dsh-jobs-0.1.0-rc.6.tgz",
"integrity": "sha512-fmyvSOVsNObmRnciuH57ZntuCSb0gflRleCeQx1ToGHRoGrR4Ndnstx33SuIXUQt6OSUhD2w2WKQTReXogpnSQ==",
"version": "0.1.0-rc.8",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-jobs/-/dsh-jobs-0.1.0-rc.8.tgz",
"integrity": "sha512-HqQudOA4VkjCMpxWiqTCMmuiTa/pehW6yG7dDsctO1vW5K48hWQ/h7czdJKgrh4SMuK4kchv3IipS1KaJmdaog==",
"license": "MIT",
"peer": true,
"peerDependencies": {
"@deepseek-ai/cordis": "^4.0.1",
"@deepseek-ai/dsh-agent": "^0.1.0-rc.6",
"@deepseek-ai/dsh-brand": "^0.1.0-rc.6",
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.6",
"@deepseek-ai/dsh-session": "^0.1.0-rc.6"
"@deepseek-ai/dsh-agent": "^0.1.0-rc.8",
"@deepseek-ai/dsh-brand": "^0.1.0-rc.8",
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.8",
"@deepseek-ai/dsh-session": "^0.1.0-rc.8"
}
},
"node_modules/@deepseek-ai/dsh-launch-environment": {
"version": "0.1.0-rc.6",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-launch-environment/-/dsh-launch-environment-0.1.0-rc.6.tgz",
"integrity": "sha512-tTRJ1464PJUDe1Em1qq0mfdgGREzGGWo3JSqP6xeYDoX+MRXVP9/ChsZ5k6VBMjARAxw0HGBf7WR6VcupHbMZg==",
"version": "0.1.0-rc.8",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-launch-environment/-/dsh-launch-environment-0.1.0-rc.8.tgz",
"integrity": "sha512-VvYo5R7tu4QhX/dsNpuBy3AaauizU6yPCeWQUNzI3ZjGA0VHkkZhInilcgxVJkPA1lwEXYB+OoCAo07PUy6D/w==",
"license": "MIT",
"peer": true,
"peerDependencies": {
"@deepseek-ai/cordis": "^4.0.1",
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.6"
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.8"
}
},
"node_modules/@deepseek-ai/dsh-llm": {
"version": "0.1.0-rc.6",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-llm/-/dsh-llm-0.1.0-rc.6.tgz",
"integrity": "sha512-kuFGC8bHlzGTwlRxQhXjf3CYWl8M4NzH+EYIkrW8rri4iMc9W53xrdvkil5No/DUlMm8g1u7GdeiWYFy0TMvtA==",
"version": "0.1.0-rc.8",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-llm/-/dsh-llm-0.1.0-rc.8.tgz",
"integrity": "sha512-hf6RmX2Stn1UtzpktSHQLkIktQkBm9i/3yFfJlbGSkwyw5Cv9sTYJbyoHzsSnnh6i8nRRcMcBhvMgm23I00oIw==",
"license": "MIT",
"dependencies": {
"@deepseek-ai/schemastery": "^3.18.1"
},
"peerDependencies": {
"@deepseek-ai/cordis": "^4.0.1",
"@deepseek-ai/dsh-attachment": "^0.1.0-rc.6",
"@deepseek-ai/dsh-brand": "^0.1.0-rc.6",
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.6",
"@deepseek-ai/dsh-timeout": "^0.1.0-rc.6"
"@deepseek-ai/dsh-attachment": "^0.1.0-rc.8",
"@deepseek-ai/dsh-brand": "^0.1.0-rc.8",
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.8",
"@deepseek-ai/dsh-timeout": "^0.1.0-rc.8"
}
},
"node_modules/@deepseek-ai/dsh-llm-pi-ai": {
"version": "0.1.0-rc.6",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-llm-pi-ai/-/dsh-llm-pi-ai-0.1.0-rc.6.tgz",
"integrity": "sha512-5RvzkpVCYLg9A3IGdm04px7XOaF/xikuMLe2toBY4A0qtJraXiZtUN1QBOL9i6u7DTOLG9oHP/USsbWRpyI+1Q==",
"version": "0.1.0-rc.8",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-llm-pi-ai/-/dsh-llm-pi-ai-0.1.0-rc.8.tgz",
"integrity": "sha512-r9dmHYy759gKtAG0S96wTCS6O8WFDqGLQu8t+zGY6LnfdT4qUJdmWtJ+GwCn+BGwq1AglBzjabvyRBp6BxXcMQ==",
"license": "MIT",
"dependencies": {
"@deepseek-ai/schemastery": "^3.18.1",
@@ -1721,104 +1723,104 @@
},
"peerDependencies": {
"@deepseek-ai/cordis": "^4.0.1",
"@deepseek-ai/dsh-attachment": "^0.1.0-rc.6",
"@deepseek-ai/dsh-credentials": "^0.1.0-rc.6",
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.6",
"@deepseek-ai/dsh-launch-environment": "^0.1.0-rc.6",
"@deepseek-ai/dsh-llm": "^0.1.0-rc.6",
"@deepseek-ai/dsh-settings": "^0.1.0-rc.6",
"@deepseek-ai/dsh-timeout": "^0.1.0-rc.6"
"@deepseek-ai/dsh-attachment": "^0.1.0-rc.8",
"@deepseek-ai/dsh-credentials": "^0.1.0-rc.8",
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.8",
"@deepseek-ai/dsh-launch-environment": "^0.1.0-rc.8",
"@deepseek-ai/dsh-llm": "^0.1.0-rc.8",
"@deepseek-ai/dsh-settings": "^0.1.0-rc.8",
"@deepseek-ai/dsh-timeout": "^0.1.0-rc.8"
}
},
"node_modules/@deepseek-ai/dsh-pwsh-local": {
"version": "0.1.0-rc.6",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-pwsh-local/-/dsh-pwsh-local-0.1.0-rc.6.tgz",
"integrity": "sha512-TtITwtlDvxyXDE4HKJW9R6uWKU5kr72Y+8JHYV3Kj/HPAzQqwbVo/2e9wvq7H4t5Soo8K+KG5ZqBpMmpp+FAWg==",
"version": "0.1.0-rc.8",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-pwsh-local/-/dsh-pwsh-local-0.1.0-rc.8.tgz",
"integrity": "sha512-lHAEwC4pL5bVSKgwaeO2dMM9s/jnpDBRuTeVD2HkunpFfT1Kq4lFi+/YXj00L7mW9DBP6FMfwRRV/0Wgo1FtdA==",
"license": "MIT",
"dependencies": {
"@deepseek-ai/schemastery": "^3.18.1"
},
"peerDependencies": {
"@deepseek-ai/cordis": "^4.0.1",
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.6",
"@deepseek-ai/dsh-settings": "^0.1.0-rc.6",
"@deepseek-ai/dsh-shell": "^0.1.0-rc.6",
"@deepseek-ai/dsh-subprocess": "^0.1.0-rc.6",
"@deepseek-ai/dsh-timeout": "^0.1.0-rc.6"
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.8",
"@deepseek-ai/dsh-settings": "^0.1.0-rc.8",
"@deepseek-ai/dsh-shell": "^0.1.0-rc.8",
"@deepseek-ai/dsh-subprocess": "^0.1.0-rc.8",
"@deepseek-ai/dsh-timeout": "^0.1.0-rc.8"
}
},
"node_modules/@deepseek-ai/dsh-sandbox": {
"version": "0.1.0-rc.6",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-sandbox/-/dsh-sandbox-0.1.0-rc.6.tgz",
"integrity": "sha512-SLZjuivQQKHTx4H7xlsjraEGGMIEA8BYOwvlm/9uZBrBrlttMU+d8Ne6QYpDcOwIEubL8mvrWRg8j3rtY3lVyA==",
"version": "0.1.0-rc.8",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-sandbox/-/dsh-sandbox-0.1.0-rc.8.tgz",
"integrity": "sha512-dJpy5p/KjKkHG9AnMKU4+/ilj/4DCglsq3kof/jhIwtsZJCix07TEXP9vV8LCv4fU8af7cK3antXw4eVMqkwuQ==",
"license": "MIT",
"peerDependencies": {
"@deepseek-ai/cordis": "^4.0.1",
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.6",
"@deepseek-ai/dsh-llm": "^0.1.0-rc.6",
"@deepseek-ai/dsh-session": "^0.1.0-rc.6"
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.8",
"@deepseek-ai/dsh-llm": "^0.1.0-rc.8",
"@deepseek-ai/dsh-session": "^0.1.0-rc.8"
}
},
"node_modules/@deepseek-ai/dsh-sandbox-policy": {
"version": "0.1.0-rc.6",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-sandbox-policy/-/dsh-sandbox-policy-0.1.0-rc.6.tgz",
"integrity": "sha512-XEB7+pJZVPWmZXv27EX/4V/1noVLUF5ZHyy3EY5tC4XtEyayWKtvHL4y9OTLIYhlU4Ualv5ag8s5ueGGq3ID8Q==",
"version": "0.1.0-rc.8",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-sandbox-policy/-/dsh-sandbox-policy-0.1.0-rc.8.tgz",
"integrity": "sha512-xQOlYWbUr1Oj6cnRd5aHZIz52EOEAdSO1HJIfeDkQrVaOgohFsiiCIp6PNFumuGNWdCEnf4Q18ebafLNtazYlA==",
"license": "MIT",
"dependencies": {
"@deepseek-ai/schemastery": "^3.18.1"
},
"peerDependencies": {
"@deepseek-ai/cordis": "^4.0.1",
"@deepseek-ai/dsh-agent": "^0.1.0-rc.6",
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.6",
"@deepseek-ai/dsh-sandbox": "^0.1.0-rc.6",
"@deepseek-ai/dsh-session": "^0.1.0-rc.6",
"@deepseek-ai/dsh-system-prompt": "^0.1.0-rc.6"
"@deepseek-ai/dsh-agent": "^0.1.0-rc.8",
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.8",
"@deepseek-ai/dsh-sandbox": "^0.1.0-rc.8",
"@deepseek-ai/dsh-session": "^0.1.0-rc.8",
"@deepseek-ai/dsh-system-prompt": "^0.1.0-rc.8"
}
},
"node_modules/@deepseek-ai/dsh-scope": {
"version": "0.1.0-rc.6",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-scope/-/dsh-scope-0.1.0-rc.6.tgz",
"integrity": "sha512-UlDLV4syLoJinNg9imhXrSAHrdaTa5Ff8gg46rzjFJGPUOhAk3DZff0hryT5OhrBi0A5Tj92qVpg2pRVvxnUzQ==",
"version": "0.1.0-rc.8",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-scope/-/dsh-scope-0.1.0-rc.8.tgz",
"integrity": "sha512-/o+PxkVV2zUy9lYlMFr4TWSb+ESW4DE+fI891yIbsLzOJdFgfbmmLpGrlWgnz8/unIGJZxjeXdwyppV04Z277Q==",
"license": "MIT",
"peer": true,
"peerDependencies": {
"@deepseek-ai/cordis": "^4.0.1",
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.6"
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.8"
}
},
"node_modules/@deepseek-ai/dsh-session": {
"version": "0.1.0-rc.6",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-session/-/dsh-session-0.1.0-rc.6.tgz",
"integrity": "sha512-8tu8I6VWC7050GAUXWhcEWQw4pakALQc8TlhKr52m7Y4+kIKeNt3FBgP86PaGPBtpK0p5zUPRQNkFpzZbBdxyw==",
"version": "0.1.0-rc.8",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-session/-/dsh-session-0.1.0-rc.8.tgz",
"integrity": "sha512-Qs5Mgn32WPPVB4rKHjsZBgLOkh0TQurUAcc9GaDVudK4dUkpno2HDh++qu/PrwRA3CZ/iwPAoFHCpB3AMMaoLg==",
"license": "MIT",
"peerDependencies": {
"@deepseek-ai/cordis": "^4.0.1",
"@deepseek-ai/dsh-brand": "^0.1.0-rc.6",
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.6",
"@deepseek-ai/dsh-llm": "^0.1.0-rc.6",
"@deepseek-ai/dsh-scope": "^0.1.0-rc.6",
"@deepseek-ai/dsh-typert-protocol": "^0.1.0-rc.6"
"@deepseek-ai/dsh-brand": "^0.1.0-rc.8",
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.8",
"@deepseek-ai/dsh-llm": "^0.1.0-rc.8",
"@deepseek-ai/dsh-scope": "^0.1.0-rc.8",
"@deepseek-ai/dsh-typert-protocol": "^0.1.0-rc.8"
}
},
"node_modules/@deepseek-ai/dsh-session-persistence": {
"version": "0.1.0-rc.6",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-session-persistence/-/dsh-session-persistence-0.1.0-rc.6.tgz",
"integrity": "sha512-AbNBe+IYCbZqSHqOACVdj8QTynm2HZ0cThrEuI6nGMtlWLYLx6lzZ1rgO/56Av9mIScjyTBGJAIKhEYaMTBG9g==",
"version": "0.1.0-rc.8",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-session-persistence/-/dsh-session-persistence-0.1.0-rc.8.tgz",
"integrity": "sha512-JujorG/UCern0+zxJzNswyWl2tFG9sLzfVNvA5YkTLaRLZt7ZsEhxG4DxWZ6uj+3RJ8sf4bqdKqvpIn9t1pwUw==",
"license": "MIT",
"peer": true,
"peerDependencies": {
"@deepseek-ai/cordis": "^4.0.1",
"@deepseek-ai/dsh-brand": "^0.1.0-rc.6",
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.6",
"@deepseek-ai/dsh-session": "^0.1.0-rc.6",
"@deepseek-ai/dsh-timeout": "^0.1.0-rc.6"
"@deepseek-ai/dsh-brand": "^0.1.0-rc.8",
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.8",
"@deepseek-ai/dsh-session": "^0.1.0-rc.8",
"@deepseek-ai/dsh-timeout": "^0.1.0-rc.8"
}
},
"node_modules/@deepseek-ai/dsh-session-projection": {
"version": "0.1.0-rc.6",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-session-projection/-/dsh-session-projection-0.1.0-rc.6.tgz",
"integrity": "sha512-DYLALBPdEI1LZjJ4B6rdGdGY4gy+iR2+5Xh2xJoAGa/pTyN5Z55TNfvuMJrmeRBjAuDXNUQpmURbvos5rJ4veg==",
"version": "0.1.0-rc.8",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-session-projection/-/dsh-session-projection-0.1.0-rc.8.tgz",
"integrity": "sha512-q00B+rl7WAuK0Bgh93SY2+ZpqaH2kJhfJ9n6Jg6R/m4plnH0Ip4oCAHl6u8rBzDgb1mniQGEQDz/7BN5Q3It8Q==",
"license": "MIT",
"peer": true,
"dependencies": {
@@ -1826,126 +1828,127 @@
},
"peerDependencies": {
"@deepseek-ai/cordis": "^4.0.1",
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.6",
"@deepseek-ai/dsh-session": "^0.1.0-rc.6"
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.8",
"@deepseek-ai/dsh-session": "^0.1.0-rc.8"
}
},
"node_modules/@deepseek-ai/dsh-settings": {
"version": "0.1.0-rc.6",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-settings/-/dsh-settings-0.1.0-rc.6.tgz",
"integrity": "sha512-5ZlH2FNRU0kkdcCoEJohvdb2fiNnxM+iZqN3icbR3WQbMm8RPRrXlZlps5YUaQKPRbNRHA2LkOXW998QRlhHcA==",
"version": "0.1.0-rc.8",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-settings/-/dsh-settings-0.1.0-rc.8.tgz",
"integrity": "sha512-pqbMoiSP+Ieww9/4gcHYXcBCDlno1zYDu2OPq+5xdZ+McnK+5YBtHsiRpnR9cHQxGTH7lkfIv+adkOueQW0utg==",
"license": "MIT",
"peer": true,
"peerDependencies": {
"@deepseek-ai/cordis": "^4.0.1",
"@deepseek-ai/dsh-brand": "^0.1.0-rc.6",
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.6",
"@deepseek-ai/dsh-brand": "^0.1.0-rc.8",
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.8",
"@deepseek-ai/schemastery": "^3.18.1"
}
},
"node_modules/@deepseek-ai/dsh-shell": {
"version": "0.1.0-rc.6",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-shell/-/dsh-shell-0.1.0-rc.6.tgz",
"integrity": "sha512-l/pcwbdpM+Zgp2AzH0kH0z7bPU7L3k4uG1t5Du1V5ilk70tyMSxz2upNfAbjkDHZZZO18LTs33aq+zRWHX1CUg==",
"version": "0.1.0-rc.8",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-shell/-/dsh-shell-0.1.0-rc.8.tgz",
"integrity": "sha512-RNhp7Y25DXUH7o36X4GcN0lnOLg6NXTlWZpYNURT7aDj3UHcLLajRsyddiAh1EbwQnl0GRKRZhLaZHrrG6gMIw==",
"license": "MIT",
"peer": true,
"peerDependencies": {
"@deepseek-ai/cordis": "^4.0.1",
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.6",
"@deepseek-ai/dsh-sandbox": "^0.1.0-rc.6",
"@deepseek-ai/dsh-settings": "^0.1.0-rc.6",
"@deepseek-ai/dsh-subprocess": "^0.1.0-rc.6"
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.8",
"@deepseek-ai/dsh-sandbox": "^0.1.0-rc.8",
"@deepseek-ai/dsh-settings": "^0.1.0-rc.8",
"@deepseek-ai/dsh-subprocess": "^0.1.0-rc.8"
}
},
"node_modules/@deepseek-ai/dsh-shell-env": {
"version": "0.1.0-rc.6",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-shell-env/-/dsh-shell-env-0.1.0-rc.6.tgz",
"integrity": "sha512-znxHFSqduw8U/AxKSO8SgSt703Bl+irRzTU0ukJT+ySlnyAwSAg8sZHLo5ZlEt5K8BdWhxz8Vc5jibOtJPB3ZQ==",
"version": "0.1.0-rc.8",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-shell-env/-/dsh-shell-env-0.1.0-rc.8.tgz",
"integrity": "sha512-E/Rlka8BnpB7GM67ZJPjvZyp0O9L5x28giyJhHWZcPHN4rsEQFVVqk0cH6iDT5STy+dMc9BAUW+im18kATIxsA==",
"license": "MIT",
"dependencies": {
"@deepseek-ai/schemastery": "^3.18.1"
},
"peerDependencies": {
"@deepseek-ai/cordis": "^4.0.1",
"@deepseek-ai/dsh-home-paths": "^0.1.0-rc.6",
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.6",
"@deepseek-ai/dsh-session-persistence": "^0.1.0-rc.6",
"@deepseek-ai/dsh-shell": "^0.1.0-rc.6",
"@deepseek-ai/dsh-tools": "^0.1.0-rc.6"
"@deepseek-ai/dsh-home-paths": "^0.1.0-rc.8",
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.8",
"@deepseek-ai/dsh-session-persistence": "^0.1.0-rc.8",
"@deepseek-ai/dsh-shell": "^0.1.0-rc.8",
"@deepseek-ai/dsh-tools": "^0.1.0-rc.8"
}
},
"node_modules/@deepseek-ai/dsh-skill": {
"version": "0.1.0-rc.6",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-skill/-/dsh-skill-0.1.0-rc.6.tgz",
"integrity": "sha512-VyRlCOASIRuTflKwn4NvbdAXxSPnnLJ+RAQUI4GK5pOtPwI2DhJrbovqpyp5kNAkZMsqvIeHWu5PcCEJ+FpQ2w==",
"version": "0.1.0-rc.8",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-skill/-/dsh-skill-0.1.0-rc.8.tgz",
"integrity": "sha512-MqcOzl4Y5pQEvFBNVAi5w0IZWH5kVbXmp1RYScrl+8HpVATZbgumDhztVnEc2kUrZu+o6LKb1hnbunK6JHUE5g==",
"license": "MIT",
"dependencies": {
"@deepseek-ai/schemastery": "^3.18.1"
},
"peerDependencies": {
"@deepseek-ai/cordis": "^4.0.1",
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.6",
"@deepseek-ai/dsh-llm": "^0.1.0-rc.6",
"@deepseek-ai/dsh-scope": "^0.1.0-rc.6"
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.8",
"@deepseek-ai/dsh-llm": "^0.1.0-rc.8",
"@deepseek-ai/dsh-scope": "^0.1.0-rc.8"
}
},
"node_modules/@deepseek-ai/dsh-subprocess": {
"version": "0.1.0-rc.6",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-subprocess/-/dsh-subprocess-0.1.0-rc.6.tgz",
"integrity": "sha512-nZaZRjSnE1he+GAd14vURAH7n3Fw5eGx3nzMbkB96Y6Qx+oQB/7PTuXxKVxlCh8TSfMJTf851ahwWb1eALjKlw==",
"version": "0.1.0-rc.8",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-subprocess/-/dsh-subprocess-0.1.0-rc.8.tgz",
"integrity": "sha512-rTWwWCq/WrBTjaXhApu7gaho6UHwMZq0+l7nZ06YMUreNZoCHfg67pAXopMqTgr/iotphmXQdChrLgBabzL1gA==",
"license": "MIT",
"peer": true,
"peerDependencies": {
"@deepseek-ai/cordis": "^4.0.1",
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.6"
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.8"
}
},
"node_modules/@deepseek-ai/dsh-subprocess-local": {
"version": "0.1.0-rc.6",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-subprocess-local/-/dsh-subprocess-local-0.1.0-rc.6.tgz",
"integrity": "sha512-D2daTRaprE25ti1Ra69eURPwLULpnRyUHqAGtcMHddcjNIsZ4yR0DGinYOsgoRymzu3t02qLxyoio05atSViRw==",
"version": "0.1.0-rc.8",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-subprocess-local/-/dsh-subprocess-local-0.1.0-rc.8.tgz",
"integrity": "sha512-2QMd6Nwjd+ARor4g+GSZ3dNyTvEf2IPOje47nQwa3UCmltLxn8K8gjDAN/5sGr/IF56P14FeF4xFag7tMK+Zdg==",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
"node-pty": "^1.1.0"
"koffi": "^3.1.0",
"node-pty": "1.2.0-beta.15"
},
"peerDependencies": {
"@deepseek-ai/cordis": "^4.0.1",
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.6",
"@deepseek-ai/dsh-subprocess": "^0.1.0-rc.6",
"@deepseek-ai/dsh-timeout": "^0.1.0-rc.6"
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.8",
"@deepseek-ai/dsh-subprocess": "^0.1.0-rc.8",
"@deepseek-ai/dsh-timeout": "^0.1.0-rc.8"
}
},
"node_modules/@deepseek-ai/dsh-system-prompt": {
"version": "0.1.0-rc.6",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-system-prompt/-/dsh-system-prompt-0.1.0-rc.6.tgz",
"integrity": "sha512-E7g+XChh4q4/wX++v56z1pV4SA1Rtz42xkznLPPi9FlXrrzJxwHMOUzBZ9Rz3Y1kLhQ++HJG3ZatmNx3rjFilg==",
"version": "0.1.0-rc.8",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-system-prompt/-/dsh-system-prompt-0.1.0-rc.8.tgz",
"integrity": "sha512-yk8Z85rzX2EwpKNWA5i+K3I3mqsIIUc9niwbDpXO8vlHvpC2qR3RNHjJIJN2EZYDqfSzTVtkbxHFsuMzDXIbew==",
"license": "MIT",
"dependencies": {
"@deepseek-ai/schemastery": "^3.18.1"
},
"peerDependencies": {
"@deepseek-ai/cordis": "^4.0.1",
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.6",
"@deepseek-ai/dsh-llm": "^0.1.0-rc.6",
"@deepseek-ai/dsh-scope": "^0.1.0-rc.6"
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.8",
"@deepseek-ai/dsh-llm": "^0.1.0-rc.8",
"@deepseek-ai/dsh-scope": "^0.1.0-rc.8"
}
},
"node_modules/@deepseek-ai/dsh-timeout": {
"version": "0.1.0-rc.6",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-timeout/-/dsh-timeout-0.1.0-rc.6.tgz",
"integrity": "sha512-CUean0fAnfsJVszFEip7PsU/S26W+JfDFfsza2dCtlw8n6xlkbHA9Gjxdk2aTwqDGCgXEPkRW7mYkdJ0n6FR7w==",
"version": "0.1.0-rc.8",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-timeout/-/dsh-timeout-0.1.0-rc.8.tgz",
"integrity": "sha512-RwOC/qHribE6b+LStAO7aAgweefLE5sqDO7Uz6/mnTDyJxCt7HNQnE/Op8sAvRa33d752aUkPfO9S0mgzVAoFQ==",
"license": "MIT",
"peer": true,
"peerDependencies": {
"@deepseek-ai/cordis": "^4.0.1",
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.6"
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.8"
}
},
"node_modules/@deepseek-ai/dsh-token-meter": {
"version": "0.1.0-rc.6",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-token-meter/-/dsh-token-meter-0.1.0-rc.6.tgz",
"integrity": "sha512-qU5FT4n1RJXP3Ss8NJ5TTvPo8rZ6cxcrCedp1t0sTAj93SGssAanq47voSmznPP52nwz/SLeunznAIT0jhbsZQ==",
"version": "0.1.0-rc.8",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-token-meter/-/dsh-token-meter-0.1.0-rc.8.tgz",
"integrity": "sha512-ZcS6vuuJhoqjUrbyROuiiYk4nrvChkkYEfIoj7CjGbNArWOhbStmBaiPyahtcB1kThcnUKdu7vv7DaZJdLw8Lg==",
"license": "MIT",
"dependencies": {
"@deepseek-ai/schemastery": "^3.18.1",
@@ -1953,40 +1956,40 @@
},
"peerDependencies": {
"@deepseek-ai/cordis": "^4.0.1",
"@deepseek-ai/dsh-compaction": "^0.1.0-rc.6",
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.6",
"@deepseek-ai/dsh-llm": "^0.1.0-rc.6",
"@deepseek-ai/dsh-session": "^0.1.0-rc.6",
"@deepseek-ai/dsh-session-projection": "^0.1.0-rc.6"
"@deepseek-ai/dsh-compaction": "^0.1.0-rc.8",
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.8",
"@deepseek-ai/dsh-llm": "^0.1.0-rc.8",
"@deepseek-ai/dsh-session": "^0.1.0-rc.8",
"@deepseek-ai/dsh-session-projection": "^0.1.0-rc.8"
}
},
"node_modules/@deepseek-ai/dsh-tool-bash": {
"version": "0.1.0-rc.6",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-tool-bash/-/dsh-tool-bash-0.1.0-rc.6.tgz",
"integrity": "sha512-yzo/xPCObiFe+OU0e8E5nVOGpW6mp9vx/3q+MHQ0aqEbqrqHUfVMGPobfDAd/I1Ja/9Nag5NIvHpI2Hwx7lACg==",
"version": "0.1.0-rc.8",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-tool-bash/-/dsh-tool-bash-0.1.0-rc.8.tgz",
"integrity": "sha512-VvP8Ky8ifqS7Iy7XUxc6cWB7sse3X6gqr92m0hBuodwQblzrZq2b6IyzfTzXKZKLqt5mMUaLRt7IPIDOOG+WEQ==",
"license": "MIT",
"dependencies": {
"@deepseek-ai/schemastery": "^3.18.1"
},
"peerDependencies": {
"@deepseek-ai/cordis": "^4.0.1",
"@deepseek-ai/dsh-agent": "^0.1.0-rc.6",
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.6",
"@deepseek-ai/dsh-jobs": "^0.1.0-rc.6",
"@deepseek-ai/dsh-llm": "^0.1.0-rc.6",
"@deepseek-ai/dsh-sandbox": "^0.1.0-rc.6",
"@deepseek-ai/dsh-sandbox-policy": "^0.1.0-rc.6",
"@deepseek-ai/dsh-shell": "^0.1.0-rc.6",
"@deepseek-ai/dsh-shell-env": "^0.1.0-rc.6",
"@deepseek-ai/dsh-system-prompt": "^0.1.0-rc.6",
"@deepseek-ai/dsh-tools": "^0.1.0-rc.6",
"@deepseek-ai/dsh-user-approval": "^0.1.0-rc.6"
"@deepseek-ai/dsh-agent": "^0.1.0-rc.8",
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.8",
"@deepseek-ai/dsh-jobs": "^0.1.0-rc.8",
"@deepseek-ai/dsh-llm": "^0.1.0-rc.8",
"@deepseek-ai/dsh-sandbox": "^0.1.0-rc.8",
"@deepseek-ai/dsh-sandbox-policy": "^0.1.0-rc.8",
"@deepseek-ai/dsh-shell": "^0.1.0-rc.8",
"@deepseek-ai/dsh-shell-env": "^0.1.0-rc.8",
"@deepseek-ai/dsh-system-prompt": "^0.1.0-rc.8",
"@deepseek-ai/dsh-tools": "^0.1.0-rc.8",
"@deepseek-ai/dsh-user-approval": "^0.1.0-rc.8"
}
},
"node_modules/@deepseek-ai/dsh-tool-fs": {
"version": "0.1.0-rc.6",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-tool-fs/-/dsh-tool-fs-0.1.0-rc.6.tgz",
"integrity": "sha512-bJfo8cCacX3WBygG8GbIlGz3jQ15MyIv4htGCjKpIIRHrXHSAelNtzceO2xQ21/9BkXrvg+i50mqySOcKQ0b3w==",
"version": "0.1.0-rc.8",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-tool-fs/-/dsh-tool-fs-0.1.0-rc.8.tgz",
"integrity": "sha512-pxo2YfVlqcw0sCA2+4DtqaaYsuxkx1QfvjT3VO+ckZMWn/UnGOUuzjJtKhVynwjm8prttGqK2LBXUTBZPlKTWg==",
"license": "MIT",
"dependencies": {
"@deepseek-ai/schemastery": "^3.18.1",
@@ -1994,106 +1997,106 @@
},
"peerDependencies": {
"@deepseek-ai/cordis": "^4.0.1",
"@deepseek-ai/dsh-attachment": "^0.1.0-rc.6",
"@deepseek-ai/dsh-fs": "^0.1.0-rc.6",
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.6",
"@deepseek-ai/dsh-llm": "^0.1.0-rc.6",
"@deepseek-ai/dsh-sandbox": "^0.1.0-rc.6",
"@deepseek-ai/dsh-sandbox-policy": "^0.1.0-rc.6",
"@deepseek-ai/dsh-session": "^0.1.0-rc.6",
"@deepseek-ai/dsh-system-prompt": "^0.1.0-rc.6",
"@deepseek-ai/dsh-tools": "^0.1.0-rc.6",
"@deepseek-ai/dsh-user-approval": "^0.1.0-rc.6"
"@deepseek-ai/dsh-attachment": "^0.1.0-rc.8",
"@deepseek-ai/dsh-fs": "^0.1.0-rc.8",
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.8",
"@deepseek-ai/dsh-llm": "^0.1.0-rc.8",
"@deepseek-ai/dsh-sandbox": "^0.1.0-rc.8",
"@deepseek-ai/dsh-sandbox-policy": "^0.1.0-rc.8",
"@deepseek-ai/dsh-session": "^0.1.0-rc.8",
"@deepseek-ai/dsh-system-prompt": "^0.1.0-rc.8",
"@deepseek-ai/dsh-tools": "^0.1.0-rc.8",
"@deepseek-ai/dsh-user-approval": "^0.1.0-rc.8"
}
},
"node_modules/@deepseek-ai/dsh-tool-pwsh": {
"version": "0.1.0-rc.6",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-tool-pwsh/-/dsh-tool-pwsh-0.1.0-rc.6.tgz",
"integrity": "sha512-x8ZlXUpOmcEiQobSTNSj18TPWVzyGe68pXJi6njea28DKVSe5Cx/L1bl9WImxru9f06+BkAm2ievJ+VKe89aeA==",
"version": "0.1.0-rc.8",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-tool-pwsh/-/dsh-tool-pwsh-0.1.0-rc.8.tgz",
"integrity": "sha512-B5F08tsSD7DHqXDXjn/Q11FavHw1U8/WAEqp8AC47vDRymPJIgTM8zSlJ0Lx7aZaKPTW4zQYtSmwUhy93q6zfw==",
"license": "MIT",
"dependencies": {
"@deepseek-ai/schemastery": "^3.18.1"
},
"peerDependencies": {
"@deepseek-ai/cordis": "^4.0.1",
"@deepseek-ai/dsh-agent": "^0.1.0-rc.6",
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.6",
"@deepseek-ai/dsh-jobs": "^0.1.0-rc.6",
"@deepseek-ai/dsh-llm": "^0.1.0-rc.6",
"@deepseek-ai/dsh-sandbox": "^0.1.0-rc.6",
"@deepseek-ai/dsh-sandbox-policy": "^0.1.0-rc.6",
"@deepseek-ai/dsh-shell": "^0.1.0-rc.6",
"@deepseek-ai/dsh-shell-env": "^0.1.0-rc.6",
"@deepseek-ai/dsh-system-prompt": "^0.1.0-rc.6",
"@deepseek-ai/dsh-tools": "^0.1.0-rc.6",
"@deepseek-ai/dsh-user-approval": "^0.1.0-rc.6"
"@deepseek-ai/dsh-agent": "^0.1.0-rc.8",
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.8",
"@deepseek-ai/dsh-jobs": "^0.1.0-rc.8",
"@deepseek-ai/dsh-llm": "^0.1.0-rc.8",
"@deepseek-ai/dsh-sandbox": "^0.1.0-rc.8",
"@deepseek-ai/dsh-sandbox-policy": "^0.1.0-rc.8",
"@deepseek-ai/dsh-shell": "^0.1.0-rc.8",
"@deepseek-ai/dsh-shell-env": "^0.1.0-rc.8",
"@deepseek-ai/dsh-system-prompt": "^0.1.0-rc.8",
"@deepseek-ai/dsh-tools": "^0.1.0-rc.8",
"@deepseek-ai/dsh-user-approval": "^0.1.0-rc.8"
}
},
"node_modules/@deepseek-ai/dsh-tool-skill": {
"version": "0.1.0-rc.6",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-tool-skill/-/dsh-tool-skill-0.1.0-rc.6.tgz",
"integrity": "sha512-AebSfHSt6j0PXuN3yK9+X4aC6tHSnFdm2hZglII7aEHr/lL9UbynPb3NTumh6thXZ9Earvwhf9JdOxHMsjHqfw==",
"version": "0.1.0-rc.8",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-tool-skill/-/dsh-tool-skill-0.1.0-rc.8.tgz",
"integrity": "sha512-oQAPyXiQDDZ73rZwST7KqwUAr0Al3U1dxWdtzETsMzvNSQE9fmMBz5jUObFIEjxxJcA07Pp1DFzuJdpkCIDeCw==",
"license": "MIT",
"dependencies": {
"@deepseek-ai/schemastery": "^3.18.1"
},
"peerDependencies": {
"@deepseek-ai/cordis": "^4.0.1",
"@deepseek-ai/dsh-agent": "^0.1.0-rc.6",
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.6",
"@deepseek-ai/dsh-llm": "^0.1.0-rc.6",
"@deepseek-ai/dsh-skill": "^0.1.0-rc.6",
"@deepseek-ai/dsh-tools": "^0.1.0-rc.6"
"@deepseek-ai/dsh-agent": "^0.1.0-rc.8",
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.8",
"@deepseek-ai/dsh-llm": "^0.1.0-rc.8",
"@deepseek-ai/dsh-skill": "^0.1.0-rc.8",
"@deepseek-ai/dsh-tools": "^0.1.0-rc.8"
}
},
"node_modules/@deepseek-ai/dsh-tools": {
"version": "0.1.0-rc.6",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-tools/-/dsh-tools-0.1.0-rc.6.tgz",
"integrity": "sha512-Tu08EPK3JyK0iNjH4FGzu/1uADynNSS6SmwOLdfytUN0YNqwNuKFSt2OJUg19famNlTgy992DcHfDu0T+gLXFg==",
"version": "0.1.0-rc.8",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-tools/-/dsh-tools-0.1.0-rc.8.tgz",
"integrity": "sha512-tvuGAVUS4yoPW9zH7CGWJgMlAzGjfu2UlRiricldlcwSKAaJZPZx/K/YfTivUyDTER6aK1DJUnIpVG7RD/DoHQ==",
"license": "MIT",
"dependencies": {
"@deepseek-ai/schemastery": "^3.18.1"
},
"peerDependencies": {
"@deepseek-ai/cordis": "^4.0.1",
"@deepseek-ai/dsh-agent": "^0.1.0-rc.6",
"@deepseek-ai/dsh-code-runtime": "^0.1.0-rc.6",
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.6",
"@deepseek-ai/dsh-llm": "^0.1.0-rc.6",
"@deepseek-ai/dsh-scope": "^0.1.0-rc.6",
"@deepseek-ai/dsh-session": "^0.1.0-rc.6",
"@deepseek-ai/dsh-system-prompt": "^0.1.0-rc.6",
"@deepseek-ai/dsh-user-approval": "^0.1.0-rc.6"
"@deepseek-ai/dsh-agent": "^0.1.0-rc.8",
"@deepseek-ai/dsh-code-runtime": "^0.1.0-rc.8",
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.8",
"@deepseek-ai/dsh-llm": "^0.1.0-rc.8",
"@deepseek-ai/dsh-scope": "^0.1.0-rc.8",
"@deepseek-ai/dsh-session": "^0.1.0-rc.8",
"@deepseek-ai/dsh-system-prompt": "^0.1.0-rc.8",
"@deepseek-ai/dsh-user-approval": "^0.1.0-rc.8"
}
},
"node_modules/@deepseek-ai/dsh-typert-protocol": {
"version": "0.1.0-rc.6",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-typert-protocol/-/dsh-typert-protocol-0.1.0-rc.6.tgz",
"integrity": "sha512-weWzN8r01YCkoDCAM7BsKw2YhRrD4zL8N2SAZu9hovYtXSq8xHXsP4Zh8RLYIlYcuotjyff/6hic+0TJPd14YA==",
"version": "0.1.0-rc.8",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-typert-protocol/-/dsh-typert-protocol-0.1.0-rc.8.tgz",
"integrity": "sha512-scwEAefRBL7gRpaXiEdiHRMvlr8dLmUbiEvy0LOAX/8X3FZBd/5tHQ/8lCppAQNTDZMI/LSiSMpu5XrFId+7cQ==",
"license": "MIT",
"peer": true,
"peerDependencies": {
"@deepseek-ai/cordis": "^4.0.1",
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.6"
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.8"
}
},
"node_modules/@deepseek-ai/dsh-user-approval": {
"version": "0.1.0-rc.6",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-user-approval/-/dsh-user-approval-0.1.0-rc.6.tgz",
"integrity": "sha512-9rnkSDGOpu2XUeGwbPeTzVUTFWTND1PMPM5L/ZQPptV5yyZlQiNxM2rCC6OdL+ZVerwxEqrRhZIQn/KVtQfKag==",
"version": "0.1.0-rc.8",
"resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-user-approval/-/dsh-user-approval-0.1.0-rc.8.tgz",
"integrity": "sha512-6SFCxp7dgNFEyzQIcs5i9C+5djgXsAeV0iKmZUCLlcIeVM1chcCMHyGcoXlw8yyB14qFMrMs+TPvhvpu2WWxAw==",
"license": "MIT",
"dependencies": {
"@deepseek-ai/schemastery": "^3.18.1"
},
"peerDependencies": {
"@deepseek-ai/cordis": "^4.0.1",
"@deepseek-ai/dsh-agent": "^0.1.0-rc.6",
"@deepseek-ai/dsh-brand": "^0.1.0-rc.6",
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.6",
"@deepseek-ai/dsh-llm": "^0.1.0-rc.6",
"@deepseek-ai/dsh-scope": "^0.1.0-rc.6",
"@deepseek-ai/dsh-session": "^0.1.0-rc.6",
"@deepseek-ai/dsh-system-prompt": "^0.1.0-rc.6"
"@deepseek-ai/dsh-agent": "^0.1.0-rc.8",
"@deepseek-ai/dsh-brand": "^0.1.0-rc.8",
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.8",
"@deepseek-ai/dsh-llm": "^0.1.0-rc.8",
"@deepseek-ai/dsh-scope": "^0.1.0-rc.8",
"@deepseek-ai/dsh-session": "^0.1.0-rc.8",
"@deepseek-ai/dsh-system-prompt": "^0.1.0-rc.8"
}
},
"node_modules/@deepseek-ai/schemastery": {
@@ -12161,9 +12164,9 @@
"license": "MIT"
},
"node_modules/node-pty": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/node-pty/-/node-pty-1.1.0.tgz",
"integrity": "sha512-20JqtutY6JPXTUnL0ij1uad7Qe1baT46lyolh2sSENDd4sTzKZ4nmAFkeAARDKwmlLjPx6XKRlwRUxwjOy+lUg==",
"version": "1.2.0-beta.15",
"resolved": "https://registry.npmjs.org/node-pty/-/node-pty-1.2.0-beta.15.tgz",
"integrity": "sha512-vORSzHXi4Ofl7HemVWpuudLqCPdaQb4LfpRCUpE5HPxhp4JYscl8zZwxh11p26v2wvW24WMwnMfLjhRLixrfxA==",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
+38 -24
View File
@@ -1,6 +1,6 @@
{
"name": "goodbuddy",
"version": "0.11.2",
"version": "0.11.4",
"private": true,
"description": "Secure desktop AI workspace with controlled Agent Runtimes",
"desktopName": "GoodBuddy",
@@ -193,7 +193,7 @@
"to": "licenses/ppu-ocv-MIT.txt"
},
{
"from": "node_modules/onnxruntime-web/LICENSE",
"from": "resources/licenses/onnxruntime-MIT.txt",
"to": "licenses/onnxruntime-web-MIT.txt"
},
{
@@ -261,28 +261,28 @@
"@agentclientprotocol/sdk": "0.25.1",
"@antv/g6": "^5.1.1",
"@deepseek-ai/cordis": "4.0.1",
"@deepseek-ai/dsh-agent": "0.1.0-rc.6",
"@deepseek-ai/dsh-agent-loop": "0.1.0-rc.6",
"@deepseek-ai/dsh-bash-local": "0.1.0-rc.6",
"@deepseek-ai/dsh-credentials": "0.1.0-rc.6",
"@deepseek-ai/dsh-fs-local": "0.1.0-rc.6",
"@deepseek-ai/dsh-llm": "0.1.0-rc.6",
"@deepseek-ai/dsh-llm-pi-ai": "0.1.0-rc.6",
"@deepseek-ai/dsh-pwsh-local": "0.1.0-rc.6",
"@deepseek-ai/dsh-sandbox": "0.1.0-rc.6",
"@deepseek-ai/dsh-sandbox-policy": "0.1.0-rc.6",
"@deepseek-ai/dsh-session": "0.1.0-rc.6",
"@deepseek-ai/dsh-shell-env": "0.1.0-rc.6",
"@deepseek-ai/dsh-skill": "0.1.0-rc.6",
"@deepseek-ai/dsh-subprocess-local": "0.1.0-rc.6",
"@deepseek-ai/dsh-system-prompt": "0.1.0-rc.6",
"@deepseek-ai/dsh-token-meter": "0.1.0-rc.6",
"@deepseek-ai/dsh-tool-bash": "0.1.0-rc.6",
"@deepseek-ai/dsh-tool-fs": "0.1.0-rc.6",
"@deepseek-ai/dsh-tool-pwsh": "0.1.0-rc.6",
"@deepseek-ai/dsh-tool-skill": "0.1.0-rc.6",
"@deepseek-ai/dsh-tools": "0.1.0-rc.6",
"@deepseek-ai/dsh-user-approval": "0.1.0-rc.6",
"@deepseek-ai/dsh-agent": "0.1.0-rc.8",
"@deepseek-ai/dsh-agent-loop": "0.1.0-rc.8",
"@deepseek-ai/dsh-bash-local": "0.1.0-rc.8",
"@deepseek-ai/dsh-credentials": "0.1.0-rc.8",
"@deepseek-ai/dsh-fs-local": "0.1.0-rc.8",
"@deepseek-ai/dsh-llm": "0.1.0-rc.8",
"@deepseek-ai/dsh-llm-pi-ai": "0.1.0-rc.8",
"@deepseek-ai/dsh-pwsh-local": "0.1.0-rc.8",
"@deepseek-ai/dsh-sandbox": "0.1.0-rc.8",
"@deepseek-ai/dsh-sandbox-policy": "0.1.0-rc.8",
"@deepseek-ai/dsh-session": "0.1.0-rc.8",
"@deepseek-ai/dsh-shell-env": "0.1.0-rc.8",
"@deepseek-ai/dsh-skill": "0.1.0-rc.8",
"@deepseek-ai/dsh-subprocess-local": "0.1.0-rc.8",
"@deepseek-ai/dsh-system-prompt": "0.1.0-rc.8",
"@deepseek-ai/dsh-token-meter": "0.1.0-rc.8",
"@deepseek-ai/dsh-tool-bash": "0.1.0-rc.8",
"@deepseek-ai/dsh-tool-fs": "0.1.0-rc.8",
"@deepseek-ai/dsh-tool-pwsh": "0.1.0-rc.8",
"@deepseek-ai/dsh-tool-skill": "0.1.0-rc.8",
"@deepseek-ai/dsh-tools": "0.1.0-rc.8",
"@deepseek-ai/dsh-user-approval": "0.1.0-rc.8",
"@huggingface/tokenizers": "^0.1.3",
"@modelcontextprotocol/sdk": "^1.30.0",
"@napi-rs/canvas": "1.0.3",
@@ -357,5 +357,19 @@
"@koromix/koffi-linux-x64": "3.1.4",
"@koromix/koffi-win32-arm64": "3.1.4",
"@koromix/koffi-win32-x64": "3.1.4"
},
"allowScripts": {
"@deepseek-ai/dsh-subprocess-local@0.1.0-rc.8": true,
"@google/genai@1.52.0": true,
"cpu-features@0.0.10": true,
"electron-winstaller@5.4.0": true,
"esbuild@0.25.12": true,
"esbuild@0.28.1": true,
"fsevents@2.3.3": true,
"koffi@3.1.4": true,
"node-pty@1.2.0-beta.15": true,
"opencode-ai@1.18.9": true,
"protobufjs@7.6.5": true,
"ssh2@1.17.0": true
}
}
+2 -17
View File
@@ -2,23 +2,8 @@
"formatVersion": 1,
"keys": [
{
"keyId": "e2e-20260823-020914-ce95d6f6",
"publicKeySpkiBase64": "MCowBQYDK2VwAyEA8w12MNcxOrU18MArvcuJmVkGKzJsa/r93q14HGX7DUU=",
"environment": "production"
},
{
"keyId": "goodbuddy-e2e-agent-8b0b39d2e63d7cf163c23bb7",
"publicKeySpkiBase64": "MCowBQYDK2VwAyEAx5lS8/stctKe/ri3kSNLR3nmkTV1eTpS0c1TYtJuHoM=",
"environment": "production"
},
{
"keyId": "goodbuddy-e2e-v8-de8355f998071143411bc075",
"publicKeySpkiBase64": "MCowBQYDK2VwAyEAY7rOjqPT3FAw2+F5roTqyssz4Zzzx0iJhk6tGhCVivc=",
"environment": "production"
},
{
"keyId": "goodbuddy-e2e-v12-b5adef071b79b4fdec57ea98",
"publicKeySpkiBase64": "MCowBQYDK2VwAyEATKD9wXjCeC35SMDrczzpou5PusRkxqSgNb5Se/jYOFU=",
"keyId": "goodbuddy-signing-2026-08",
"publicKeySpkiBase64": "MCowBQYDK2VwAyEAfUkXqa3WYwb1QevpHW/XWNhGo59dETJYjrJONx6SKEQ=",
"environment": "production"
}
],
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) Microsoft Corporation. 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:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
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.
+10 -8
View File
@@ -2,12 +2,12 @@
"formatVersion": 1,
"releases": [
{
"version": "0.11.2",
"releasedAt": "2026-08-25",
"version": "0.11.4",
"releasedAt": "2026-08-26",
"notes": {
"zh-CN": {
"highlights": [
"GoodBuddy 0.11.2 新增托管 SSH 远程项目、内置 Granite 向量模型、会话分支和应用内反馈,并改进魔法笔记、知识图谱、模型下载及桌面布局。"
"GoodBuddy 0.11.4 新增托管 SSH 远程项目、内置 Granite 向量模型、会话分支和应用内反馈,并改进魔法笔记、知识图谱、模型下载及桌面布局。"
],
"features": [
"**托管 SSH 远程项目(技术预览)。** 在“设置 → 平台功能”中启用后,可管理 Linux x64/arm64 Host,并通过 Ask 或 Execute 使用签名 Agent 与兼容的 OpenCode Runtime。Agent 包需在设置中手动下载、更新或离线导入,打开项目不会自动下载;SSH 断线后,Host 上的任务可继续运行并恢复未同步输出。",
@@ -15,6 +15,7 @@
"**独立会话分支。** 本地会话可通过“在新会话中继续”创建独立分支并保留来源,不复制原会话的任务、队列、运行记录、记忆或成果归属。",
"**应用内反馈。** 可在“关于与更新”中提交问题、建议或体验反馈;邮箱和单张截图均为可选项,失败后会保留草稿供重试。",
"**魔法笔记待办数量。** 左侧导航可显示未完成待办数量,并可在“平台功能 → 魔法笔记”中关闭;超过 99 项时显示“99+”。",
"**DeepSeek Harness Runtime。** 内置 DeepSeek Harness 已从 `0.1.0-rc.6` 升级至 `0.1.0-rc.8`;GoodBuddy 同步适配新版图片附件限制,现有受控 Host、Ask/Execute 与插件边界保持不变。",
"**Linux 与 LoongArch 构建。** Linux x64/arm64 桌面版新增 RPM 安装包,并提供独立、可复现的 Debian loong64 实验性预览构建流程。"
],
"fixes": [
@@ -26,14 +27,14 @@
"**界面信息与会话列表。** 恢复关键页面说明和视觉层级;会话更多操作仅在悬停、键盘聚焦或菜单打开时显示,为标题和时间保留更多空间。"
],
"notices": [
"远程项目默认关闭且完全可选。首次使用前必须手动准备兼容的 `.gbagent` 包,下载源跟随“关于与更新”中的 GitHub 或镜像节点选择;本次正式 Agent 版本为 `0.11.2`。",
"LoongArch 仍是独立实验性预览,不进入标准更新索引;当前不包含 Agent、Remote Runtime、OpenCode 和原生 Canvas。",
"远程项目默认关闭且完全可选。首次使用前必须手动准备兼容的 `.gbagent` 包,下载源跟随“关于与更新”中的 GitHub 或镜像节点选择;本次正式 Agent 版本为 `0.11.4`。",
"LoongArch 仍是独立实验性预览,不进入标准更新索引;当前不包含 Agent、Remote Runtime、OpenCode 和原生 Canvas。本次 `0.11.4` 不提供 LoongArch 预览包。",
"反馈不会自动附带会话、日志、文件、设置或凭据。"
]
},
"en-US": {
"highlights": [
"GoodBuddy 0.11.2 adds managed SSH remote projects, a built-in Granite embedding model, conversation branching, and in-app feedback, with improvements to Magic Notes, knowledge graphs, model downloads, and desktop layout."
"GoodBuddy 0.11.4 adds managed SSH remote projects, a built-in Granite embedding model, conversation branching, and in-app feedback, with improvements to Magic Notes, knowledge graphs, model downloads, and desktop layout."
],
"features": [
"**Managed SSH remote projects (technical preview).** Enable Remote Projects under Settings → Platform Features to manage Linux x64/arm64 Hosts and use signed Agents with a compatible OpenCode Runtime in Ask or Execute. Agent packages must be downloaded, updated, or imported manually in Settings; opening a project never downloads one automatically. Host work can continue through an SSH disconnect and recover unsynchronized output.",
@@ -41,6 +42,7 @@
"**Independent conversation branches.** Continue a local conversation in a new branch that records its source without copying Tasks, queued input, runs, memories, or artifact ownership.",
"**In-app feedback.** Submit problems, ideas, or experience notes from About & Updates. An email address and one screenshot are optional, and failed submissions retain the draft for retry.",
"**Magic Notes todo count.** The primary navigation can show incomplete todo counts, with an option under Platform Features → Magic Notes to hide the badge. Counts above 99 display as “99+”.",
"**DeepSeek Harness Runtime.** The bundled DeepSeek Harness is updated from `0.1.0-rc.6` to `0.1.0-rc.8`. GoodBuddy now matches the updated image attachment limits while preserving its controlled Host, Ask/Execute, and plugin boundaries.",
"**Linux and LoongArch builds.** Linux x64/arm64 desktop releases add RPM installers, and the repository now provides a separate reproducible experimental Debian loong64 preview build."
],
"fixes": [
@@ -52,8 +54,8 @@
"**Interface guidance and conversation rows.** Restored essential workflow guidance and visual hierarchy. Conversation actions now appear only on hover, keyboard focus, or while their menu is open, leaving more room for titles and timestamps."
],
"notices": [
"Remote Projects are disabled by default and remain optional. A compatible `.gbagent` package must be prepared manually before first use, and its source follows the GitHub or mirror selection in About & Updates. This release uses the formal Agent version `0.11.2`.",
"LoongArch remains a separate experimental preview outside the standard update index. It currently excludes Agent, Remote Runtime, OpenCode, and native Canvas.",
"Remote Projects are disabled by default and remain optional. A compatible `.gbagent` package must be prepared manually before first use, and its source follows the GitHub or mirror selection in About & Updates. This release uses the formal Agent version `0.11.4`.",
"LoongArch remains a separate experimental preview outside the standard update index. It currently excludes Agent, Remote Runtime, OpenCode, and native Canvas. No LoongArch preview package is provided for `0.11.4`.",
"Feedback never automatically includes conversations, logs, files, settings, or credentials."
]
}
+6 -35
View File
@@ -1,10 +1,13 @@
import {
chmodSync,
mkdtempSync,
rmSync,
unlinkSync
rmSync
} from 'node:fs'
import { createConnection, type Socket } from 'node:net'
import {
createConnection,
createServer,
type Socket
} from 'node:net'
import { tmpdir } from 'node:os'
import { join, resolve } from 'node:path'
import { PassThrough } from 'node:stream'
@@ -79,38 +82,6 @@ describe('private endpoint lifecycle', () => {
}
)
runOnUnix(
'never unlinks a replacement socket it did not bind',
async () => {
const root = privateTemporaryDirectory()
const socketPath = resolve(root, 'agent.sock')
const endpoint = new PrivateEndpoint({
socketPath,
peerIdentity: currentPeerIdentity(),
challenge: new InstallationChallengeVerifier(Buffer.alloc(32, 7)),
controllers: new ControllerRegistry(),
...endpointIdentity,
onAttach: vi.fn()
})
await endpoint.listen()
unlinkSync(socketPath)
const replacement = createServer()
await new Promise<void>((resolveListen, reject) => {
replacement.once('error', reject)
replacement.listen(socketPath, resolveListen)
})
await endpoint.close()
const client = createConnection(socketPath)
await expect(onceConnected(client)).resolves.toBeUndefined()
client.destroy()
await new Promise<void>((resolveClose) =>
replacement.close(() => resolveClose())
)
}
)
runOnUnix(
'destroys an authenticated persistent socket during shutdown',
async () => {
+4 -1
View File
@@ -243,7 +243,10 @@ describe('WorkspaceRegistry', () => {
abort.abort(new Error('cancelled'))
releaseInspection?.('not-a-repository')
await expect(pending).rejects.toThrow('cancelled')
await expect(pending).rejects.toMatchObject({
code: 'aborted',
message: 'Workspace operation was aborted'
})
expect(registry.activeHandleCount()).toBe(0)
expect(existsSync(storagePath)).toBe(false)
})
+1 -1
View File
@@ -7,7 +7,7 @@ export type BundledRuntimePaths = {
}
export const bundledContinueVersion = '1.5.47'
export const bundledDeepSeekHarnessVersion = '0.1.0-rc.6'
export const bundledDeepSeekHarnessVersion = '0.1.0-rc.8'
export function resolveBundledRuntimePaths(input: {
appPath: string
@@ -329,7 +329,7 @@ function createInProcessLaunch(
provider: 'goodbuddy',
model: options.model,
supportsImageInput: options.supportsImageInput,
harnessVersion: '0.1.0-rc.6',
harnessVersion: '0.1.0-rc.8',
credentialRefs: options.credentialRefs,
skillPackages: options.skillPackages,
extensionPackages: options.extensionPackages,
@@ -5,11 +5,13 @@ import {
runtimeExtensionConfigurationSchema,
runtimeExtensionIdSchema
} from '../../shared/runtime-extension-contracts'
import { bundledDeepSeekHarnessVersion } from './bundled-runtimes'
export const DEEPSEEK_HARNESS_CONTROL_PROTOCOL =
'goodbuddy.deepseek-harness.control'
export const DEEPSEEK_HARNESS_CONTROL_VERSION = 2
export const DEEPSEEK_HARNESS_HOST_VERSION = '0.1.0-rc.6'
export const DEEPSEEK_HARNESS_HOST_VERSION =
bundledDeepSeekHarnessVersion
export const DEEPSEEK_HARNESS_CREDENTIAL_REF =
'GOODBUDDY_HARNESS_MODEL_API_KEY'
export const DEEPSEEK_HARNESS_MAX_FRAME_BYTES =
@@ -112,9 +112,10 @@ describe('DeepSeek Harness extension loader', () => {
it('bounds the complete extension startup sequence', async () => {
const ctx = new Context()
const importModule = vi.fn(
() => new Promise<never>(() => undefined)
)
const importModule = vi.fn(async () => {
blockEventLoop(25)
return {}
})
const result = await loadControlledHarnessExtensions(
ctx,
@@ -156,7 +156,7 @@ function setup(
if (method === 'goodbuddy/handshake') {
return {
controlProtocolVersion: 1,
harnessVersion: '0.1.0-rc.6',
harnessVersion: '0.1.0-rc.8',
acpProtocolVersion: 1,
supports: {
cancellation: true,
@@ -95,7 +95,7 @@ describe.skipIf(!enabled)('DSH marketplace live E2E', () => {
api: 'openai-completions',
provider: 'goodbuddy',
model: 'deepseek-test',
harnessVersion: '0.1.0-rc.6',
harnessVersion: '0.1.0-rc.8',
credentialRefs: ['GOODBUDDY_API_KEY'],
skillPackages: [],
extensionPackages: extensions,
@@ -8,6 +8,7 @@ const canvas = createCanvas(1, 1)
const transparentPng = canvas.toBuffer('image/png')
const jpeg = canvas.toBuffer('image/jpeg')
const secondPng = createCanvas(2, 1).toBuffer('image/png')
const overwidePng = createCanvas(8_193, 1).toBuffer('image/png')
describe('GoodBuddy Harness attachment store', () => {
it('decodes, stores, verifies, and releases inline images', async () => {
@@ -32,12 +33,18 @@ describe('GoodBuddy Harness attachment store', () => {
const stored = await store.readImage(first)
expect(stored.ref).toBe(first)
expect(Buffer.from(stored.data).equals(transparentPng)).toBe(true)
await expect(
store.readImage({
...first,
width: first.width + 1
})
).rejects.toMatchObject({ code: 'INVALID_ATTACHMENT_REF' })
store.releaseImage(first)
await expect(store.readImage(first)).resolves.toBeDefined()
store.releaseImage(second)
await expect(store.readImage(first)).rejects.toMatchObject({
code: 'NOT_FOUND'
code: 'ATTACHMENT_NOT_FOUND'
})
const jpegRef = await store.saveImage({
@@ -62,7 +69,7 @@ describe('GoodBuddy Harness attachment store', () => {
data: transparentPng,
mediaType: 'image/jpeg'
})
).rejects.toMatchObject({ code: 'INVALID_IMAGE' })
).rejects.toMatchObject({ code: 'IMAGE_TYPE_MISMATCH' })
await expect(
store.saveImage({
data: Buffer.from([
@@ -81,6 +88,14 @@ describe('GoodBuddy Harness attachment store', () => {
mediaType: 'image/png'
})
).rejects.toMatchObject({ code: 'INVALID_IMAGE' })
await expect(
store.saveImage({
data: overwidePng,
mediaType: 'image/png'
})
).rejects.toMatchObject({
code: 'IMAGE_DIMENSION_TOO_LARGE'
})
await store.saveImage({
data: transparentPng,
@@ -91,7 +106,7 @@ describe('GoodBuddy Harness attachment store', () => {
data: secondPng,
mediaType: 'image/png'
})
).rejects.toMatchObject({ code: 'STORAGE_LIMIT' })
).rejects.toMatchObject({ code: 'ATTACHMENT_WRITE_FAILED' })
})
it('does not retain a partial batch when capacity is exceeded', async () => {
@@ -104,7 +119,7 @@ describe('GoodBuddy Harness attachment store', () => {
{ data: transparentPng, mediaType: 'image/png' },
{ data: jpeg, mediaType: 'image/jpeg' }
])
).rejects.toMatchObject({ code: 'STORAGE_LIMIT' })
).rejects.toMatchObject({ code: 'ATTACHMENT_WRITE_FAILED' })
await expect(
store.saveImage({
data: jpeg,
@@ -130,4 +145,21 @@ describe('GoodBuddy Harness attachment store', () => {
height: 1
})
})
})
it('enforces the upstream per-message image count', async () => {
const store = new GoodBuddyHarnessAttachmentStore(new Context())
const input = {
data: transparentPng,
mediaType: 'image/png' as const
}
await expect(
store.saveImages(
Array.from(
{ length: store.imageLimits.maxImagesPerMessage + 1 },
() => input
)
)
).rejects.toMatchObject({ code: 'TOO_MANY_IMAGES' })
})
})
@@ -6,6 +6,7 @@ import {
AttachmentError,
AttachmentId,
AttachmentStore,
type ImageAdmissionErrorCode,
type ImageAttachmentLimits,
type ImageAttachmentRef,
type ImageMediaType,
@@ -23,6 +24,7 @@ export const GOODBUDDY_HARNESS_IMAGE_LIMITS: ImageAttachmentLimits =
maxImagesPerMessage: 8,
maxMessageImageBytes: 2 * 1024 * 1024,
maxImagePixels: 16_000_000,
maxImageDimension: 8_192,
mediaTypes: Object.freeze([
'image/png',
'image/jpeg'
@@ -47,8 +49,12 @@ export type GoodBuddyHarnessAttachmentStoreConfig = {
maxBatchImagePixels?: number
}
function invalidImage(message: string, cause?: unknown): AttachmentError {
return new AttachmentError(message, 'INVALID_IMAGE', {
function imageError(
message: string,
code: ImageAdmissionErrorCode,
cause?: unknown
): AttachmentError {
return new AttachmentError(message, code, {
...(cause === undefined ? {} : { cause })
})
}
@@ -88,6 +94,57 @@ function matchesSignature(
)
}
function validateImageInputBounds(
input: SaveImageAttachment,
limits: ImageAttachmentLimits
): void {
if (!limits.mediaTypes.includes(input.mediaType)) {
throw imageError(
'Image media type is not supported',
'UNSUPPORTED_IMAGE_TYPE'
)
}
if (input.data.byteLength === 0) {
throw imageError('Image data is empty', 'INVALID_IMAGE')
}
if (input.data.byteLength > limits.maxImageBytes) {
throw imageError(
'Image exceeds the per-image byte limit',
'IMAGE_TOO_LARGE'
)
}
}
function validateImageDimensions(
width: number,
height: number,
limits: ImageAttachmentLimits
): void {
if (
!Number.isSafeInteger(width) ||
!Number.isSafeInteger(height) ||
width < 1 ||
height < 1
) {
throw imageError('Image dimensions are invalid', 'INVALID_IMAGE')
}
if (
width > limits.maxImageDimension ||
height > limits.maxImageDimension
) {
throw imageError(
'Image exceeds the per-side dimension limit',
'IMAGE_DIMENSION_TOO_LARGE'
)
}
if (width * height > limits.maxImagePixels) {
throw imageError(
'Image dimensions exceed the pixel limit',
'IMAGE_TOO_MANY_PIXELS'
)
}
}
function pngDimensions(data: Buffer): {
width: number
height: number
@@ -200,34 +257,26 @@ async function inspectImage(
input: SaveImageAttachment,
limits: ImageAttachmentLimits
): Promise<InspectedImage> {
if (!limits.mediaTypes.includes(input.mediaType)) {
throw invalidImage('Image media type is not supported')
}
if (
input.data.byteLength === 0 ||
input.data.byteLength > limits.maxImageBytes
) {
throw invalidImage('Image exceeds the per-image byte limit')
}
validateImageInputBounds(input, limits)
const data = Buffer.from(input.data)
if (!matchesSignature(data, input.mediaType)) {
throw invalidImage('Image media type does not match its bytes')
throw imageError(
'Image media type does not match its bytes',
'IMAGE_TYPE_MISMATCH'
)
}
const encodedDimensions =
input.mediaType === 'image/png'
? pngDimensions(data)
: jpegDimensions(data)
if (!encodedDimensions) {
throw invalidImage('Image container is malformed')
}
if (
encodedDimensions.width < 1 ||
encodedDimensions.height < 1 ||
encodedDimensions.width * encodedDimensions.height >
limits.maxImagePixels
) {
throw invalidImage('Image dimensions exceed the pixel limit')
throw imageError('Image container is malformed', 'INVALID_IMAGE')
}
validateImageDimensions(
encodedDimensions.width,
encodedDimensions.height,
limits
)
let width: number
let height: number
let loadImage: typeof import('@napi-rs/canvas')['loadImage']
@@ -237,7 +286,7 @@ async function inspectImage(
} catch (error) {
throw new AttachmentError(
'Harness image decoder is unavailable',
'DECODER_UNAVAILABLE',
'ATTACHMENT_WRITE_FAILED',
{ cause: error }
)
}
@@ -246,18 +295,21 @@ async function inspectImage(
width = image.naturalWidth || image.width
height = image.naturalHeight || image.height
} catch (error) {
throw invalidImage('Image bytes could not be decoded', error)
throw imageError(
'Image bytes could not be decoded',
'INVALID_IMAGE',
error
)
}
validateImageDimensions(width, height, limits)
if (
!Number.isSafeInteger(width) ||
!Number.isSafeInteger(height) ||
width < 1 ||
height < 1 ||
width * height > limits.maxImagePixels ||
width !== encodedDimensions.width ||
height !== encodedDimensions.height
) {
throw invalidImage('Image dimensions exceed the pixel limit')
throw imageError(
'Decoded image dimensions do not match its container',
'INVALID_IMAGE'
)
}
return { data, width, height }
}
@@ -314,6 +366,23 @@ export class GoodBuddyHarnessAttachmentStore extends AttachmentStore {
async saveImages(
inputs: readonly SaveImageAttachment[]
): Promise<ImageAttachmentRef[]> {
if (inputs.length > this.imageLimits.maxImagesPerMessage) {
throw new AttachmentError(
'Images exceed the per-message count limit',
'TOO_MANY_IMAGES'
)
}
let batchBytes = 0
for (const input of inputs) {
validateImageInputBounds(input, this.imageLimits)
batchBytes += input.data.byteLength
if (batchBytes > this.imageLimits.maxMessageImageBytes) {
throw new AttachmentError(
'Images exceed the per-message byte limit',
'IMAGES_TOO_LARGE'
)
}
}
const inspectedByContent = new Map<string, InspectedImage>()
const candidates: Array<{
input: SaveImageAttachment
@@ -322,13 +391,6 @@ export class GoodBuddyHarnessAttachmentStore extends AttachmentStore {
}> = []
let batchPixels = 0
for (const input of inputs) {
if (
!this.imageLimits.mediaTypes.includes(input.mediaType) ||
input.data.byteLength === 0 ||
input.data.byteLength > this.imageLimits.maxImageBytes
) {
throw invalidImage('Image exceeds the attachment limits')
}
const digest = createHash('sha256')
.update(input.data)
.digest('hex')
@@ -340,7 +402,10 @@ export class GoodBuddyHarnessAttachmentStore extends AttachmentStore {
}
batchPixels += inspected.width * inspected.height
if (batchPixels > this.maxBatchImagePixels) {
throw invalidImage('Images exceed the batch pixel limit')
throw imageError(
'Images exceed the batch pixel limit',
'INVALID_IMAGE'
)
}
candidates.push({
input,
@@ -370,7 +435,7 @@ export class GoodBuddyHarnessAttachmentStore extends AttachmentStore {
) {
throw new AttachmentError(
'Harness attachment store is full',
'STORAGE_LIMIT'
'ATTACHMENT_WRITE_FAILED'
)
}
return candidates.map(({ input, inspected, attachmentId }) => {
@@ -407,7 +472,7 @@ export class GoodBuddyHarnessAttachmentStore extends AttachmentStore {
if (!stored) {
throw new AttachmentError(
'Harness image attachment was not found',
'NOT_FOUND'
'ATTACHMENT_NOT_FOUND'
)
}
if (
@@ -419,8 +484,8 @@ export class GoodBuddyHarnessAttachmentStore extends AttachmentStore {
stored.ref.name !== ref.name
) {
throw new AttachmentError(
'Harness image attachment failed integrity validation',
'INTEGRITY'
'Harness image attachment reference is invalid',
'INVALID_ATTACHMENT_REF'
)
}
return {
@@ -22,7 +22,7 @@ function controlPlane() {
provider: 'goodbuddy',
model: 'deepseek-test',
workspace: resolve('workspace'),
harnessVersion: '0.1.0-rc.6',
harnessVersion: '0.1.0-rc.8',
execution: { mode: 'host' },
credentialRefs: ['GOODBUDDY_API_KEY'],
skills: []
@@ -75,7 +75,7 @@ function stubAgentContext() {
provider: 'goodbuddy',
model: 'deepseek-test',
workspace: resolve('workspace'),
harnessVersion: '0.1.0-rc.6',
harnessVersion: '0.1.0-rc.8',
execution: { mode: 'host' },
credentialRefs: ['GOODBUDDY_API_KEY'],
skills: [],
@@ -172,7 +172,7 @@ describe('GoodBuddy Harness internal control plane', () => {
model: 'vision-test',
supportsImageInput: true,
workspace: resolve('workspace'),
harnessVersion: '0.1.0-rc.6',
harnessVersion: '0.1.0-rc.8',
execution: { mode: 'host' },
credentialRefs: ['GOODBUDDY_API_KEY'],
skills: []
@@ -223,7 +223,7 @@ describe('GoodBuddy Harness internal control plane', () => {
subject.releaseAttachments(refs)
await expect(store.readImage(refs[0]!)).rejects.toMatchObject({
code: 'NOT_FOUND'
code: 'ATTACHMENT_NOT_FOUND'
})
await expect(
subject.storePromptImages([
@@ -13,6 +13,7 @@ import type { Context } from '@deepseek-ai/cordis'
import type { AgentHandle } from '@deepseek-ai/dsh-agent'
import {
AttachmentError,
isImageAdmissionError,
type ImageAttachmentRef,
type SaveImageAttachment
} from '@deepseek-ai/dsh-attachment'
@@ -1059,16 +1060,13 @@ export class GoodBuddyHarnessControlPlane {
return await store.saveImages(inputs)
} catch (error) {
if (error instanceof AttachmentError) {
if (error.code === 'INVALID_IMAGE') {
if (isImageAdmissionError(error)) {
throw RequestError.invalidParams(
undefined,
'invalid inline image prompt content'
)
}
if (
error.code === 'STORAGE_LIMIT' ||
error.code === 'DECODER_UNAVAILABLE'
) {
if (error.code === 'ATTACHMENT_WRITE_FAILED') {
throw RequestError.internalError(
undefined,
'Harness image attachment service is unavailable'
+2 -2
View File
@@ -192,7 +192,7 @@ describe('runtime discovery', () => {
binaryPath: '',
bundledPath: bundledScript,
bundledValidation: 'canonical-file',
bundledVersion: '0.1.0-rc.6',
bundledVersion: '0.1.0-rc.8',
binaryNames: [],
label: 'GoodBuddy DeepSeek Harness Host'
})
@@ -200,7 +200,7 @@ describe('runtime discovery', () => {
expect(detection).toMatchObject({
available: true,
path: await realpath(bundledScript),
version: '0.1.0-rc.6',
version: '0.1.0-rc.8',
source: 'bundled'
})
expect(detection.detail).toContain('内置')
+5 -5
View File
@@ -34,7 +34,7 @@ describe('controlled DeepSeek Harness host', () => {
api: 'openai-completions',
provider: 'goodbuddy',
model: 'deepseek-test',
harnessVersion: '0.1.0-rc.6',
harnessVersion: '0.1.0-rc.8',
credentialRefs: ['GOODBUDDY_API_KEY'],
dshHome: 'C:\\controlled-dsh-home',
skillPackages: []
@@ -82,7 +82,7 @@ describe('controlled DeepSeek Harness host', () => {
provider: 'goodbuddy',
model: 'qwen-plus',
supportsImageInput: true,
harnessVersion: '0.1.0-rc.6',
harnessVersion: '0.1.0-rc.8',
credentialRefs: ['GOODBUDDY_API_KEY'],
skillPackages: [],
stream: {
@@ -140,7 +140,7 @@ describe('controlled DeepSeek Harness host', () => {
api: 'openai-completions',
provider: 'goodbuddy',
model: 'deepseek-test',
harnessVersion: '0.1.0-rc.6',
harnessVersion: '0.1.0-rc.8',
credentialRefs: ['GOODBUDDY_API_KEY'],
skillPackages: [],
stream: {
@@ -177,7 +177,7 @@ describe('controlled DeepSeek Harness host', () => {
api: 'openai-completions',
provider: 'goodbuddy',
model: 'deepseek-test',
harnessVersion: '0.1.0-rc.6',
harnessVersion: '0.1.0-rc.8',
credentialRefs: ['GOODBUDDY_API_KEY'],
skillPackages: [],
extensionPackages: [
@@ -232,7 +232,7 @@ describe('controlled DeepSeek Harness host', () => {
api: 'openai-completions',
provider: 'goodbuddy',
model: 'deepseek-test',
harnessVersion: '0.1.0-rc.6',
harnessVersion: '0.1.0-rc.8',
credentialRefs: ['GOODBUDDY_API_KEY'],
skillPackages: [
{ id: 'web-3d-game', directory: skillDirectory }
@@ -31,7 +31,7 @@ describe.skipIf(!entrypoint)(
api: 'openai-completions',
provider: 'goodbuddy',
model: 'deepseek-test',
harnessVersion: '0.1.0-rc.6',
harnessVersion: '0.1.0-rc.8',
credentialRefs: ['GOODBUDDY_API_KEY'],
skillPackages: [],
extensionPackages: [
@@ -344,25 +344,31 @@ describe('AgentAttachTransport', () => {
expect(transport.closed).toBe(true)
})
it('writes one maximum-sized blob frame in one SSH write', async () => {
const channel = new FakeChannel()
channel.onWrite = () => {
if (channel.writes.length === 1) {
queueMicrotask(() => channel.remote(packet(welcome)))
it(
'writes one maximum-sized blob frame in one SSH write',
async () => {
const channel = new FakeChannel()
channel.onWrite = () => {
if (channel.writes.length === 1) {
queueMicrotask(() => channel.remote(packet(welcome)))
}
}
}
const transport = await connect(channel)
const blob = frame(
{ direction: 'main-to-agent', kind: 'blob' },
Buffer.alloc(AGENT_PROTOCOL_LIMITS.maximumBlobFrameBytes)
)
const transport = await connect(channel)
const blob = frame(
{ direction: 'main-to-agent', kind: 'blob' },
Buffer.alloc(AGENT_PROTOCOL_LIMITS.maximumBlobFrameBytes)
)
await transport.send(blob)
await transport.send(blob)
expect(channel.writes).toHaveLength(2)
expect(channel.writes[1]).toEqual(Buffer.from(encodeAgentFrame(blob)))
transport.dispose()
})
expect(channel.writes).toHaveLength(2)
expect(channel.writes[1]).toEqual(
Buffer.from(encodeAgentFrame(blob))
)
transport.dispose()
},
15_000
)
it('rejects an oversized blob before writing to SSH', async () => {
const channel = new FakeChannel()
@@ -67,6 +67,7 @@ const MAXIMUM_EXPANDED_BYTES = 1024 * 1024 * 1024
const MAXIMUM_FILE_BYTES = 384 * 1024 * 1024
const MAXIMUM_METADATA_BYTES = 1024 * 1024
const MAXIMUM_ENTRIES = 50_002
const PRIVATE_DIRECTORY_MODE = 0o700
const SIGNATURE_DOMAIN = Buffer.from(
'GoodBuddy Agent Package Descriptor Signature v1\0',
'utf8'
@@ -91,7 +92,10 @@ export async function extractAndVerifyAgentPackage(options: {
const archivePath = resolve(options.archivePath)
const destinationDirectory = resolve(options.destinationDirectory)
await assertArchiveFile(archivePath)
await mkdir(destinationDirectory, { recursive: false })
await mkdir(destinationDirectory, {
recursive: false,
mode: PRIVATE_DIRECTORY_MODE
})
try {
await extractArchive(archivePath, destinationDirectory)
return await verifyExtractedAgentPackage({
@@ -354,7 +358,8 @@ async function extractArchive(
throw new Error('Agent package entry escapes its destination')
}
const handlePromise = mkdir(dirname(destinationPath), {
recursive: true
recursive: true,
mode: PRIVATE_DIRECTORY_MODE
}).then(async () => {
const handle = await open(destinationPath, 'wx')
openHandles.add(handle)
+3 -3
View File
@@ -171,7 +171,7 @@ const detectAgentRuntimes = vi.fn<
deepseekHarness: {
available: true,
path: 'bundled://deepseek-harness',
version: '0.1.0-rc.6',
version: '0.1.0-rc.8',
source: 'bundled',
detail: '内置 Harness Adapter 已就绪'
}
@@ -2665,11 +2665,11 @@ describe('SettingsPanel runtime files', () => {
)
).toHaveClass('runtime-overview__path')
expect(
within(harnessOverview).getByText('0.1.0-rc.6')
within(harnessOverview).getByText('0.1.0-rc.8')
).toBeInTheDocument()
expect(
within(harnessOverview).getByText(
'内置 DeepSeek Harness 0.1.0-rc.6 已就绪'
'内置 DeepSeek Harness 0.1.0-rc.8 已就绪'
)
).toBeInTheDocument()
expect(
+4 -4
View File
@@ -512,7 +512,7 @@ describe('Agent production input handling', () => {
it('requires CI-only production key variables and redacts them', () => {
expect(() =>
agentBundle.productionSigningKey({})
).toThrow('required for production Agent signing')
).toThrow('required for production GoodBuddy signing')
const secret = [
'-----BEGIN PRIVATE KEY-----',
@@ -521,8 +521,8 @@ describe('Agent production input handling', () => {
].join('\n')
const error = `signing_key=${secret} key id prod-secret-id`
const redacted = agentBundle.redactSecrets(error, {
GOODBUDDY_AGENT_SIGNING_PRIVATE_KEY: secret,
GOODBUDDY_AGENT_SIGNING_KEY_ID: 'prod-secret-id'
GOODBUDDY_SIGNING_PRIVATE_KEY: secret,
GOODBUDDY_SIGNING_KEY_ID: 'prod-secret-id'
})
expect(redacted).not.toContain('fixture-secret-material')
expect(redacted).not.toContain('prod-secret-id')
@@ -536,7 +536,7 @@ describe('Agent production input handling', () => {
registry
})
).toThrow(
'Production Agent public signing key ID "production-release-key" is absent from resources/agent-release-keys.json'
'Production GoodBuddy public signing key ID "production-release-key" is absent from resources/agent-release-keys.json'
)
})
+71 -17
View File
@@ -6,6 +6,7 @@ import {
} from 'node:crypto'
import {
chmodSync,
lstatSync,
mkdirSync,
mkdtempSync,
readFileSync,
@@ -106,6 +107,14 @@ type AgentPackageModule = {
}
type AgentCatalogModule = {
readPackageMetadata(
archivePath: string,
registry: AgentReleaseKeyRegistry
): {
descriptor: AgentPackageDescriptor
size: number
sha256: string
}
createCatalog(options: {
projectRoot: string
x64Package: string
@@ -269,7 +278,7 @@ afterAll(() => {
})
describe('compound Agent packages', () => {
it('assembles deterministic signed Agent and Runtime archives', async () => {
it('assembles deterministic signed archives with private extraction', async () => {
const secondRoot = join(temporaryRoot, 'second')
mkdirSync(secondRoot)
const secondPath = join(
@@ -288,17 +297,22 @@ describe('compound Agent packages', () => {
'remote-runtime-lock.json'
])
)
expect(packageResult.descriptor.files).toContainEqual(
expect.objectContaining({
path: 'agent/helpers/fixture-helper',
mode: '0755'
})
)
const contentRoot = join(temporaryRoot, 'verified')
await expect(
extractAndVerifyAgentPackage({
archivePath: packagePath,
destinationDirectory: contentRoot,
architecture: 'x64',
desktopVersion: '0.11.0',
trustedRegistry: registry
})
).resolves.toMatchObject({
const verified = await extractAndVerifyAgentPackage({
archivePath: packagePath,
destinationDirectory: contentRoot,
architecture: 'x64',
desktopVersion: '0.11.0',
trustedRegistry: registry
})
expect(verified).toMatchObject({
descriptor: {
version: agentLock.agentVersion,
architecture: 'x64',
@@ -308,6 +322,15 @@ describe('compound Agent packages', () => {
}
}
})
if (process.platform !== 'win32') {
for (const directory of [
verified.rootDirectory,
verified.agentBundle.bundleDirectory,
verified.runtimeBundle.bundleDirectory
]) {
expect(lstatSync(directory).mode & 0o777).toBe(0o700)
}
}
const second = agentPackage.assembleAgentPackage({
projectRoot: process.cwd(),
@@ -412,7 +435,10 @@ describe('compound Agent packages', () => {
)
const mismatched = {
...descriptor,
version: '0.11.3'
version:
descriptor.version === '999.0.0'
? '999.0.1'
: '999.0.0'
}
mismatched.contentDigest =
agentPackage.descriptorContentDigest(mismatched)
@@ -564,12 +590,15 @@ describe('compound Agent packages', () => {
changedRoot,
agentPackageArchiveName(agentLock.agentVersion, 'x64')
)
const changedEntries = unzipSync(
new Uint8Array(readFileSync(packagePath))
)
const originalPackage = readFileSync(packagePath)
const changedPackage = Buffer.concat([
originalPackage,
Buffer.from([0])
])
changedPackage.writeUInt16LE(1, originalPackage.length - 2)
writeFileSync(
changedPath,
Buffer.from(zipSync(changedEntries, { level: 1 }))
changedPackage
)
expect(() =>
agentCatalog.createCatalog({
@@ -596,6 +625,18 @@ describe('compound Agent packages', () => {
).toThrow('identity is immutable')
})
it('verifies stored payload bytes that contain a ZIP descriptor signature', () => {
expect(
agentCatalog.readPackageMetadata(packagePath, registry)
).toMatchObject({
descriptor: {
architecture: 'x64'
},
size: packageResult.size,
sha256: packageResult.sha256
})
})
it('imports and exports a fully verified offline package atomically', async () => {
const keyRegistryPath = join(
temporaryRoot,
@@ -872,6 +913,7 @@ function writeAgentBundle(
): void {
mkdirSync(join(directory, 'lib'), { recursive: true })
mkdirSync(join(directory, 'licenses'), { recursive: true })
mkdirSync(join(directory, 'helpers'), { recursive: true })
writeFileSync(join(directory, 'node'), elf(architecture))
writeFileSync(
join(directory, 'goodbuddy-agent'),
@@ -881,6 +923,10 @@ function writeAgentBundle(
join(directory, 'lib', 'agent.cjs'),
'module.exports={}\n'
)
writeFileSync(
join(directory, 'helpers', 'fixture-helper'),
'#!/bin/sh\n'
)
for (const [name, contents] of [
['GoodBuddy-0BSD.txt', '0BSD'],
['Node.js-MIT.txt', 'MIT'],
@@ -891,7 +937,11 @@ function writeAgentBundle(
writeFileSync(join(directory, 'licenses', name), contents)
}
writeKoffiPayload(directory, architecture)
for (const path of ['node', 'goodbuddy-agent']) {
for (const path of [
'node',
'goodbuddy-agent',
'helpers/fixture-helper'
]) {
chmodSync(join(directory, path), 0o755)
}
const manifest = agentBundle.createManifest(directory, {
@@ -1015,7 +1065,11 @@ function createRuntimeInput(
)
writeFileSync(
join(packageRoot, 'bin', 'opencode'),
elf(architecture)
Buffer.concat([
elf(architecture),
Buffer.from([0x50, 0x4b, 0x07, 0x08]),
Buffer.from('runtime payload')
])
)
chmodSync(join(packageRoot, 'bin', 'opencode'), 0o755)
const archive = join(
+4 -7
View File
@@ -97,10 +97,10 @@ describe('independent Agent release workflow', () => {
)
expect(buildScripts).toContain('--ignore-scripts')
expect(build).toContain(
'GOODBUDDY_AGENT_SIGNING_PRIVATE_KEY'
'GOODBUDDY_SIGNING_PRIVATE_KEY'
)
expect(build).toContain(
'GOODBUDDY_REMOTE_RUNTIME_SIGNING_PRIVATE_KEY'
expect(workflow).not.toContain(
'GOODBUDDY_REMOTE_RUNTIME_SIGNING_'
)
expect(buildScripts).toContain('.gbagent')
})
@@ -169,10 +169,7 @@ describe('independent Agent release workflow', () => {
it('keeps private keys out of authorization and public publication jobs', () => {
expect(serializedJob('authorize')).not.toContain('secrets.')
expect(serializedJob('publish')).not.toContain(
'GOODBUDDY_AGENT_SIGNING_PRIVATE_KEY'
)
expect(serializedJob('publish')).not.toContain(
'GOODBUDDY_REMOTE_RUNTIME_SIGNING_PRIVATE_KEY'
'GOODBUDDY_SIGNING_PRIVATE_KEY'
)
})
})
+5 -1
View File
@@ -162,7 +162,7 @@ describe('GoodBuddy Agents workflow', () => {
)
expect(workflow).not.toContain('secrets.')
expect(workflow).not.toContain(
'GOODBUDDY_AGENT_SIGNING_PRIVATE_KEY'
'GOODBUDDY_SIGNING_PRIVATE_KEY'
)
expect(ciBundleSource).toContain(
"generateKeyPairSync('ed25519')"
@@ -176,6 +176,10 @@ describe('GoodBuddy Agents workflow', () => {
)
expect(ciBundleSource).toContain('assembleAgentPackage')
expect(ciBundleSource).toContain('buildRuntimeBundle')
expect(ciBundleSource).toContain('archive: second.archive')
expect(ciBundleSource).not.toContain(
'second.package.archive'
)
expect(build).toContain('.gbagent')
expect(ciBundleSource).not.toContain(
'readTrustedKeyRegistry'
+36 -2
View File
@@ -93,6 +93,12 @@ interface ReleaseBuilderModule {
targetRuntimePackageNames: (
options: ReleaseOptions
) => string[]
targetHarnessPaths: (
options: ReleaseOptions
) => {
nodePtyBinary: string
nodePtyDirectory: string
}
stageTargetRuntimeDependencies: (
options: ReleaseOptions,
dependencies?: {
@@ -446,6 +452,10 @@ describe('release build arguments', () => {
build: {
asarUnpack: string[]
npmRebuild: boolean
extraResources: Array<{
from: string
to: string
}>
}
optionalDependencies: Record<string, string>
}
@@ -468,6 +478,10 @@ describe('release build arguments', () => {
expect(packageJson.build.asarUnpack).not.toContain(
'node_modules/npm/**/*'
)
expect(packageJson.build.extraResources).toContainEqual({
from: 'resources/licenses/onnxruntime-MIT.txt',
to: 'licenses/onnxruntime-web-MIT.txt'
})
expect(packageJson.optionalDependencies).toEqual({
'@koromix/koffi-darwin-arm64': '3.1.4',
'@koromix/koffi-darwin-x64': '3.1.4',
@@ -863,7 +877,7 @@ describe('release build arguments', () => {
join(projectRoot, 'package.json'),
JSON.stringify({
dependencies: {
'@deepseek-ai/dsh-llm': '0.1.0-rc.6'
'@deepseek-ai/dsh-llm': '0.1.0-rc.8'
}
})
)
@@ -923,7 +937,7 @@ describe('release build arguments', () => {
)
).toMatchObject({
name: '@deepseek-ai/dsh-llm',
version: '0.1.0-rc.6'
version: '0.1.0-rc.8'
})
} finally {
rmSync(projectRoot, { recursive: true, force: true })
@@ -948,6 +962,26 @@ describe('release binary architecture detection', () => {
})
describe('release Harness package verification', () => {
it.each([
['windows', 'x64', 'prebuilds/win32-x64/conpty.node'],
['windows', 'arm64', 'prebuilds/win32-arm64/conpty.node'],
['macos', 'x64', 'prebuilds/darwin-x64/pty.node'],
['macos', 'arm64', 'prebuilds/darwin-arm64/pty.node'],
['linux', 'x64', 'prebuilds/linux-x64/pty.node'],
['linux', 'arm64', 'prebuilds/linux-arm64/pty.node']
] as const)(
'verifies the rc.8 node-pty prebuild for %s %s',
(platform, arch, expected) => {
expect(
releaseBuilder.targetHarnessPaths({
...windowsOptions,
platform,
arch
}).nodePtyBinary
).toBe(expected)
}
)
it('fails closed when the packaged Harness Host is missing', () => {
const directory = mkdtempSync(
join(tmpdir(), 'goodbuddy-harness-closure-')
+1 -4
View File
@@ -76,10 +76,7 @@ describe('desktop packages workflow', () => {
expect(workflow).not.toContain('agent-signing:')
expect(workflow).not.toContain('remote-runtime-signing:')
expect(workflow).not.toContain(
'GOODBUDDY_AGENT_SIGNING_PRIVATE_KEY'
)
expect(workflow).not.toContain(
'GOODBUDDY_REMOTE_RUNTIME_SIGNING_PRIVATE_KEY'
'GOODBUDDY_SIGNING_PRIVATE_KEY'
)
})
+31 -2
View File
@@ -51,7 +51,11 @@ type RemoteRuntimeBundleModule = {
outputRoot: string
lock: RemoteRuntimeLock
registry: AgentReleaseKeyRegistry
testSigningIdentity: {
testSigningIdentity?: {
keyId: string
privateKey: KeyObject
}
signingIdentity?: {
keyId: string
privateKey: KeyObject
}
@@ -311,7 +315,7 @@ describe('Remote Runtime bundle tooling', () => {
runtimeBundle.preflightProductionSigningKey({
projectRoot: process.cwd(),
environment: {
GOODBUDDY_REMOTE_RUNTIME_SIGNING_KEY_ID:
GOODBUDDY_SIGNING_KEY_ID:
'runtime-fixture'
},
registry: productionRegistry
@@ -321,6 +325,31 @@ describe('Remote Runtime bundle tooling', () => {
environment: 'production'
})
})
it('signs the bundled Runtime with the shared GoodBuddy identity', () => {
const productionRegistry: AgentReleaseKeyRegistry = {
...registry,
keys: registry.keys.map((key) => ({
...key,
environment: 'production'
}))
}
const built = runtimeBundle.buildRuntimeBundle({
projectRoot: process.cwd(),
architecture: 'x64',
runtimeArchive,
outputRoot: join(temporaryRoot, 'production'),
lock,
registry: productionRegistry,
signingIdentity: {
keyId: 'runtime-fixture',
privateKey
},
enforceFilesystemMode: false
})
expect(built.manifest.signingKeyId).toBe('runtime-fixture')
})
})
function build(outputRoot: string): VerifiedBundle {