Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions .github/workflows/deepseek-harness-plugin-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
name: DeepSeek Harness Plugin CI

on:
push:
paths:
- 'deepseek-harness-plugin/**'
- '.github/workflows/deepseek-harness-plugin-ci.yml'
pull_request:
paths:
- 'deepseek-harness-plugin/**'
- '.github/workflows/deepseek-harness-plugin-ci.yml'

permissions:
contents: read

jobs:
test:
strategy:
matrix:
node: [22.19.0, 24]
runs-on: ubuntu-latest
defaults:
run:
working-directory: deepseek-harness-plugin
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: 11.19.0
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
cache: pnpm
cache-dependency-path: deepseek-harness-plugin/pnpm-lock.yaml
- run: pnpm install --frozen-lockfile
- run: pnpm run check
- run: mkdir -p .artifacts && npm pack --pack-destination .artifacts
- name: Verify package contents and bundled ADB executables
shell: bash
run: |
archive="$(find .artifacts -maxdepth 1 -name '*.tgz' -print -quit)"
test -n "$archive"
tar -tzf "$archive" > "$RUNNER_TEMP/archive-files.txt"
tar -tvzf "$archive" > "$RUNNER_TEMP/archive-verbose.txt"
grep -Fx 'package/lib/index.js' "$RUNNER_TEMP/archive-files.txt"
grep -Fx 'package/cordis.patch.yml' "$RUNNER_TEMP/archive-files.txt"
grep -E '^-rwxr-xr-x .* package/assets/platform-tools/darwin/adb$' "$RUNNER_TEMP/archive-verbose.txt"
grep -E '^-rwxr-xr-x .* package/assets/platform-tools/linux-x64/adb$' "$RUNNER_TEMP/archive-verbose.txt"
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Bilingual-pair consistency record. Both languages carry equal authority.
2026-08-18-phone-action-preflight-avoids-device-rediscovery.md: 0c4ea7f6922b7c6d9c689be2bc44a36128323679
2026-08-18-phone-action-preflight-avoids-device-rediscovery.zh.md: a9ade83506dba048bd7285e3cf20ba5cb5dd6bb0
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Agent Note: Phone action preflight avoids device rediscovery

Status: implemented

English | [中文](2026-08-18-phone-action-preflight-avoids-device-rediscovery.zh.md)

## Problem

Every `phone_control` call ran `adb devices -l`, including calls after the child had locked a serial. The fourth identical no-progress action checked its repetition fuse only after that discovery command, so an action documented to fail before ADB still accessed the device service. Explicit observe or wait results that changed the screenshot also left the old repetition count stored. Unit tests covered the state helpers but did not boot the plugin through the Loader or prove registry disposal.

## Decision

The first observation discovers and locks one authorized serial. Every later action uses that serial directly with `-s`; a disconnected or unauthorized target fails its addressed ADB command and the task never selects another phone. Mutation validation builds the allowlisted command and checks the repetition fuse before resolving the cached serial or invoking ADB. Publishing any observation whose screenshot fingerprint differs from the preceding observation clears the repetition state.

Observation identifiers use the package-owned `ObservationId` brand after tool-input validation and generation. A keyless subprocess fixture loads the real bundle patch through the Harness app boot and Loader, snapshots the model-visible provider and tool protocol, disposes the plugin fiber, and verifies that its routes and tools disappear. The fixture copies writable Loader configuration into its isolated temporary directory before testing disposal.

## Alternatives considered

**Re-enumerate devices before every action.** This preserved a custom disconnect diagnostic, but paid an extra ADB process for every action and let the preflight fuse touch ADB. Addressed commands already report disconnect and authorization failures without allowing device switching.

**Keep no-progress clearing only in mutation completion.** This missed changed frames produced by explicit observe and wait calls, allowing a stale count to become active if a later frame matched the older fingerprint.

**Retain helper-only tests.** Pure tests are fast but cannot prove Loader patch composition, model-visible schemas, or Cordis fiber disposal.

## Consequences

Every action after initial discovery removes one `adb devices -l` process from the critical path, and the fourth identical unchanged mutation fails before any ADB process. Disconnect errors now come from the fixed-serial ADB operation instead of a preceding custom enumeration diagnostic. Changed observations reset the repetition fuse regardless of how the observation was requested.

The Loader smoke adds test-only app-boot, local settings and attachment providers, and their subprocess harness. It does not contact a phone or model endpoint; real-device latency and endpoint compatibility remain deployment smoke responsibilities.
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Agent Note: 手机 action 预检避免重复设备发现

Status: implemented

[English](2026-08-18-phone-action-preflight-avoids-device-rediscovery.md) | 中文

## Problem

每次 `phone_control` 调用都会执行 `adb devices -l`,包括子任务已锁定 serial 后的调用。第四次相同无进展 action 只在该发现命令之后检查重复保护,因此按文档应在 ADB 前失败的 action 仍会访问设备服务。显式 observe 或 wait 得到变化截图时,旧的重复计数也会继续保留。单元测试覆盖了状态 helper,但没有通过 Loader 启动插件,也没有证明 registry 会随 fiber 清理。

## Decision

首次观察发现并锁定一台已授权设备。后续 action 都通过 `-s` 直接使用该 serial;目标断开或失去授权时,定向 ADB 命令失败,任务不会选择另一台手机。修改操作的校验会先构造白名单命令并检查重复保护,然后才解析缓存 serial 或调用 ADB。任何新观察的截图 fingerprint 与前一观察不同时,发布该观察会清除重复状态。

观察标识在工具输入校验和生成后使用包拥有的 `ObservationId` brand。免密钥子进程 fixture 通过 Harness app boot 和 Loader 加载真实 bundle patch,对模型可见的 provider 与工具协议生成 snapshot,处置插件 fiber,并验证路由和工具随之消失。Fixture 在测试处置前把 Loader 可写配置复制到隔离的临时目录。

## Alternatives considered

**每次 action 前重新枚举设备。** 这种方案保留自定义断开诊断,但每次 action 都多执行一个 ADB 进程,而且使预检保护仍会接触 ADB。固定 serial 的定向命令已经可以报告断开和授权错误,并且不会允许切换设备。

**只在修改操作完成时清除无进展状态。** 这种方案会遗漏显式 observe 和 wait 产生的变化画面;如果后续画面再次匹配更早的 fingerprint,旧计数会重新生效。

**只保留 helper 单元测试。** 纯函数测试速度快,但不能证明 Loader patch 组合、模型可见 schema 或 Cordis fiber 处置。

## Consequences

首次发现后的每次 action 都从关键路径移除一个 `adb devices -l` 进程,第四次相同且画面未变化的修改会在任何 ADB 进程前失败。断开错误改由固定 serial 的 ADB 操作报告,不再来自提前执行的自定义枚举诊断。无论通过哪种方式请求观察,只要画面变化就会重置重复保护。

Loader smoke 增加仅供测试使用的 app-boot、本地设置与附件 provider,以及子进程 harness。它不连接手机或模型端点;真机延迟和端点兼容性仍由部署 smoke 负责验证。
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Bilingual-pair consistency record. Both languages carry equal authority.
2026-08-18-coremate-direct-phone-command.md: 6631d2477d8b94d4a8af3aa3196d7739a96a588e
2026-08-18-coremate-direct-phone-command.zh.md: 3733112a277464729833efc77956e58ec7a44987
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Agent Note: CoreMate direct phone command

Status: implemented

English | [中文](2026-08-18-coremate-direct-phone-command.zh.md)

## Problem

Starting a phone task required the parent model to select `phone_agent`, even when the human had already expressed that intent. A direct command still had to preserve the same phone-model configuration, child persona, tool restriction, ADB policy, cancellation, and task result semantics. Independent command and tool implementations could drift or control the same deployment-selected phone concurrently.

## Decision

The plugin registers `/coremate <phone task>` through the official Harness command registry. The command sends its trimmed argument directly to a package-owned `PhoneTaskCoordinator`; it never sends the command line to the parent model. `phone_agent` calls the same coordinator, which owns configuration checks, credential resolution, child creation, settlement, and teardown cancellation through one injected start operation.

One plugin instance admits one phone task at a time across both entry points. A competing call fails before model or ADB access because every task selects the same first authorized phone. Plugin disposal aborts the active child signal and awaits settlement. The tool preserves every final content block; the generic command result joins final text blocks and reports a stable run-complete message when none exist.

## Alternatives considered

**Convert `/coremate` into a parent-model prompt.** This would spend a parent-model request and leave tool selection nondeterministic even though the command already names the desired execution path.

**Invoke the registered `phone_agent` tool from the command handler.** The tool runtime owns model-call scheduling and logging, not direct UI composition. An internal coordinator gives both adapters one implementation without fabricating a model tool call.

**Allow command and tool tasks to overlap.** Per-child tool serialization does not prevent two children from selecting and controlling the same physical phone. A plugin-wide admission check fails predictably before either shared resource is touched.

**Project child screenshots into the command result.** The official generic command result accepts text and an optional source event, while the phone screenshots belong to the child session. A client-specific projection would enlarge the plugin beyond the host-only command feature.

## Consequences

Humans can address the phone runner deterministically with one slash command, while ordinary model-led delegation remains available. Direct commands avoid parent-model tokens but display text only. A second session cannot run a phone task concurrently through the same plugin instance, even if a deployment later connects multiple authorized phones; adding explicit device selection would require revisiting that admission rule.
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Agent Note: CoreMate 直接手机命令

Status: implemented

[English](2026-08-18-coremate-direct-phone-command.md) | 中文

## Problem

启动手机任务必须由父模型选择 `phone_agent`,即使用户已经明确表达该意图。直接命令仍需保持相同的手机模型配置、子任务 persona、工具限制、ADB policy、取消和任务结果语义。命令与工具各自实现会产生行为偏差,也可能同时控制部署所选择的同一台手机。

## Decision

插件通过官方 Harness 命令 registry 注册 `/coremate <phone task>`。命令把 trim 后的参数直接交给包内 `PhoneTaskCoordinator`,不会把命令行发送给父模型。`phone_agent` 调用同一个 coordinator;该模块通过一个注入的启动操作统一拥有配置检查、凭据解析、子任务创建、任务结算和卸载取消。

同一插件实例在两个入口之间一次只接纳一个手机任务。由于每个任务都会选择同一台排序第一的已授权手机,并发调用会在访问模型或 ADB 前失败。插件卸载会中止活动子任务的 signal 并等待任务结束。工具会保留所有最终 content block;通用命令结果会连接最终文本 block,没有文本时返回稳定的 run 完成消息。

## Alternatives considered

**把 `/coremate` 转换为父模型 prompt。** 这会消耗一次父模型请求,而且即使命令已经指定执行路径,工具选择仍具有不确定性。

**从命令 handler 调用已注册的 `phone_agent` 工具。** 工具 runtime 管理模型调用的调度和日志,不负责直接 UI 组合。内部 coordinator 让两个 adapter 共用一个实现,并且不伪造模型工具调用。

**允许命令和工具任务重叠。** 子任务内部的工具串行不能阻止两个子任务选择并控制同一台实体手机。插件级接纳检查会在访问共享资源前给出确定失败。

**把子任务截图投影到命令结果。** 官方通用命令结果接受文本和可选 source event,而手机截图属于子会话。客户端专用投影会让插件范围超出仅在 host 注册命令的功能。

## Consequences

用户可以用一个斜杠命令确定地调用手机 runner,同时仍可使用普通的模型委派。直接命令不消耗父模型 token,但只显示文本。即使部署以后连接多台已授权手机,第二个会话也不能通过同一插件实例并发运行手机任务;增加显式设备选择时必须重新评估这项接纳规则。
4 changes: 4 additions & 0 deletions deepseek-harness-plugin/.gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
assets/platform-tools/darwin/adb binary
assets/platform-tools/linux-x64/adb binary
assets/platform-tools/win32-x64/*.dll binary
assets/platform-tools/win32-x64/*.exe binary
5 changes: 5 additions & 0 deletions deepseek-harness-plugin/.githooks/post-merge
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/usr/bin/env bash
set -euo pipefail

repo_root="$(git rev-parse --show-toplevel)"
exec node "${repo_root}/scripts/dev-auto-reload.mjs" after-pull
34 changes: 34 additions & 0 deletions deepseek-harness-plugin/.github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: CI

on:
push:
pull_request:

permissions:
contents: read

jobs:
test:
strategy:
matrix:
node: [22.19.0, 24]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: 11.19.0
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
cache: pnpm
- run: pnpm install --frozen-lockfile
- run: pnpm run check
- run: mkdir -p .artifacts && npm pack --pack-destination .artifacts
- name: Verify bundled ADB executables
shell: bash
run: |
archive="$(find .artifacts -maxdepth 1 -name '*.tgz' -print -quit)"
tar -tvzf "$archive" > "$RUNNER_TEMP/archive-verbose.txt"
grep -E '^-rwxr-xr-x .* package/assets/platform-tools/darwin/adb$' "$RUNNER_TEMP/archive-verbose.txt"
grep -E '^-rwxr-xr-x .* package/assets/platform-tools/linux-x64/adb$' "$RUNNER_TEMP/archive-verbose.txt"
65 changes: 65 additions & 0 deletions deepseek-harness-plugin/.github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
name: GitHub Release

on:
push:
tags:
- 'v*'

permissions:
contents: write

jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: pnpm/action-setup@v4
with:
version: 11.19.0

- uses: actions/setup-node@v4
with:
node-version: 22.19.0
cache: pnpm

- name: Verify tag matches package version
shell: bash
run: test "v$(node -p "require('./package.json').version")" = "$GITHUB_REF_NAME"

- run: pnpm install --frozen-lockfile
- run: pnpm run check

- name: Pack release artifact
shell: bash
run: |
mkdir -p .artifacts
npm pack --pack-destination .artifacts

- name: Verify artifact and write checksum
shell: bash
run: |
archive="$(find .artifacts -maxdepth 1 -name '*.tgz' -print -quit)"
test -n "$archive"
tar -tzf "$archive" > "$RUNNER_TEMP/archive-files.txt"
tar -tvzf "$archive" > "$RUNNER_TEMP/archive-verbose.txt"
grep -Fx 'package/lib/index.js' "$RUNNER_TEMP/archive-files.txt"
grep -Fx 'package/cordis.patch.yml' "$RUNNER_TEMP/archive-files.txt"
grep -E '^-rwxr-xr-x .* package/assets/platform-tools/darwin/adb$' "$RUNNER_TEMP/archive-verbose.txt"
grep -E '^-rwxr-xr-x .* package/assets/platform-tools/linux-x64/adb$' "$RUNNER_TEMP/archive-verbose.txt"
archive_name="$(basename "$archive")"
(
cd .artifacts
sha256sum "$archive_name" > "$archive_name.sha256"
)

- name: Create GitHub Release
env:
GH_TOKEN: ${{ github.token }}
run: >-
gh release create "$GITHUB_REF_NAME"
.artifacts/*.tgz
.artifacts/*.sha256
--verify-tag
--title "dsh-coremate-mobile $GITHUB_REF_NAME"
--generate-notes
6 changes: 6 additions & 0 deletions deepseek-harness-plugin/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
node_modules/
lib/
coverage/
.artifacts/
*.tgz
.DS_Store
21 changes: 21 additions & 0 deletions deepseek-harness-plugin/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2026 DeepSeek

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.
3 changes: 3 additions & 0 deletions deepseek-harness-plugin/README.i18n.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Bilingual-pair consistency record. Both languages carry equal authority.
README.md: 1e6181df4e9a161e6eb20e05f4d3425456b83c01
README.zh.md: 857edcf990cda065eb3dccad83abc883418d6d9d
Loading
Loading