# 実行環境の制約と回避策(DSH_HOME 差し替え) - 作成: 2026-10-18 - 種別: 実測(このリポジトリの開発環境に固有) ## 1. 制約: ワークスペース外は読み取り専用 本環境のファイルサンドボックスは `workspace-write`。実測: ``` $ mkdir -p "$HOME/.dsh/profiles/__sandbox_probe__" mkdir: Read-only file system $ mkdir -p "$PWD/.probe" && rmdir "$PWD/.probe" (成功) ``` **つまり以下が既定では実行できない**: - `$HOME/.dsh/profiles//` の新規作成(=新しいプロファイルの作成) - `dsh plugin --profile add `(pnpm がプロファイル配下へ書き込む) - DSH 実行時の `$HOME/.dsh/{sessions,storages,logs}/` への書き込み ## 2. 回避策: `DSH_HOME` をワークスペース内へ向ける `@deepseek-ai/dsh-home-paths` の実装で確認(`lib/*.js`): ``` DSH_HOME_ENV = "DSH_HOME" 優先順位(高い順): 明示的に設定されたパス > $DSH_HOME > join(homedir(), ".dsh") ``` ### 実証 1: 合成ツリーの出力(マウントなし) ``` $ DSH_HOME="$PWD/.dsh-home" dsh --profile headless --dump-config (96行の合成ツリーを出力、exit 0) ``` 出力の各ブロックには由来がコメントで付く(実測): ```yaml # == @deepseek-ai/dsh-base - id: tool-plugin-manager name: '@deepseek-ai/dsh-plugin-manager/tools' disabled: true ... # == @deepseek-ai/dsh-base, patched by /.dsh-home/profiles/headless/cordis.patch.yml ``` **`name:` にサブパス(`@deepseek-ai/dsh-plugin-manager/tools`)を書けることを実測で確認。** ### 実証 2: 実際の起動 ``` $ DSH_HOME="$PWD/.dsh-home" dsh --profile headless --help Usage: dsh --profile headless [options] [task...] ... (exit 0) ``` DSH がサンドボックス内で完全に起動した。プロファイルは `.dsh-home/profiles/headless/` に `package.json` / `cordis.yml` / `cordis.patch.yml` / `pnpm-workspace.yaml` を置くだけで機能する(`$HOME/.dsh/profiles/headless/` から複製)。 ## 3. 副次的に判明した事実 - `--dump-config` は**マウントせずに**合成済みの木を出せる。行数は headless プロファイルで **96行**。 - `.dsh-home/` は `.gitignore` 済み(ビルド成果物ではなく実行時ディレクトリのため)。 - マスク済みのツリーを `docs/research/2026-10-18-composed-tree-headless.yml` に保存した。 ## 4. 実モデルでの疎通(実測・成功) `DSH_HOME` 差し替え + **環境変数経由の鍵**で、サンドボックス内で実モデルを走らせることに成功した。 鍵は画面に表示せず、ディスクにも書いていない(漏洩チェック済み)。 | プロバイダ / モデル | 結果 | |---|---| | `opencode-go` / `deepseek-v4.1-flash` | **403** `An active OpenCode Go subscription is required to use Go models.` | | `commandcode` / `deepseek/deepseek-v4.1-flash` | **成功**(exit 0 / `inputTokens: 6048, outputTokens: 21` / `final: "OK"`) | 実行の形: ```sh KEY=$(python3 -c "...credentials.yaml の refs から取得...") # 値は表示しない DSH_HOME="$PWD/.dsh-home" COMMANDCODE_API_KEY="$KEY" \ dsh --profile headless --json "Reply with exactly: OK" ``` `--json` で得られる生イベント(実測・8行): ```jsonl {"type":"session","sessionId":"session-…","cwd":""} {"type":"status","phase":"turn_start","turn":1} {"type":"status","phase":"step_start","turn":1,"step":1} {"type":"thinking","text":"…"} {"type":"text","text":"OK"} {"type":"status","phase":"step_end","turn":1,"step":1,"usage":{"inputTokens":6048,"outputTokens":21,"totalTokens":6069}} {"type":"status","phase":"turn_end","turn":1,"reason":{"kind":"completed"}} {"type":"final","text":"OK"} ``` **含意**: Phase 0 の検証環境は確立した。我々の実装が購読する `session/event`(`@deepseek-ai/dsh-session` のイベント)も 同じ経路で動かせる。`session` イベントの `cwd` は絶対パスを含むため、**コミットする際はマスクが必要**。 ## 5. 未解決 - **プロバイダ**: `opencode-go` はサブスクリプションが有効でないため 403。検証には `commandcode` を使う。 なお既定の `dsh-tui` プロファイルは `opencode-go` を既定にしているため、**そのままでは動かない状態**である (本プロジェクトとは別の話だが、記録として残す)。