# dsh-where-am-i > Neofetch for DeepSeek Harness. [English](./README.md) | **简体中文** DeepSeek Harness (dsh) 插件:把**当前设备信息**以 neofetch 风格注入**每个新建会话**——OS 发行版、内核、架构、主机名、CPU、内存、uptime、时区、locale、shell/terminal、Node 版本。**只在全新会话的第一步注入一次**,之后持久保留在该会话历史里;已有会话完全不受影响。 ## 一句话行为 - **全新会话**(创建后第一次发送消息):注入一条设备信息 user 消息 → 模型在会话开头看到它; - **已有会话**(重启前就存在的、恢复的、延续中的):系统提示词与会话历史**都不变**,一条也不加; - **同一个会话的后续步骤**:永不重复注入(消息已持久化在历史里,直到 compaction 遮蔽); - **子代理会话**(subagent/工作流工具派生的临时会话):默认跳过(避免每次 fan-out 都重复);`includeSubagents: true` 可放开。 ## 使用方法 将插件加进 dsh profile(发布为 npm 包后按包名安装): ```bash dsh plugin --profile web add "@gausszhou/dsh-where-am-i" ``` 本地开发时用 link 依赖(与 `dsh-opencode-session-id` 相同的方式):在 `~/.dsh/profiles/web/package.json` 的 `dependencies` 加 `"@gausszhou/dsh-where-am-i": "link:/home/gauss/Code/gausszhou/dsh-where-am-i"`,并把它加入 `dsh.profile.bundles`;插件自带的 `cordis.patch.yml`(`dsh.bundle.patch`)会完成 insert。 装完后**重启 dsh web**(`systemctl --user restart dsh-web`)让 bundle 生效。默认零配置即可用。 ## 新会话里模型看到的内容(本机实测) ```markdown System info (the machine this dsh session runs on; sampled at session start): host: gauss-KP user: gauss os: Ubuntu 26.04 LTS kernel: 7.0.0-29-generic arch: x86_64 cpu: 6 × Intel(R) Core(TM) i5-8400 CPU @ 2.80GHz memory: 7.9 GiB used / 15.0 GiB total uptime: 1d 4h 3m timezone: Asia/Shanghai (GMT+08:00) locale: zh_CN.UTF-8 shell: /bin/bash term: dumb node: v24.19.0 ``` ## 配置 默认零配置;全部可选项(均可省略,用默认值): | 键 | 默认 | 说明 | |---|---|---| | `fields` | 全部 13 项 | 只输出指定的字段行,按默认顺序排列,例如 `[os, arch, timezone]`;未知字段名被忽略;过滤后为空则回退到全部 | | `includeSubagents` | `false` | `true` 时也会往子代理会话注入(默认跳过,避免 fan-out 重复) | | `verbose` | `false` | 注入时把快照打到 stdout(web 部署可 `journalctl -u dsh-web` 看到) | 字段清单(emission 顺序):`host` `user` `os` `kernel` `arch` `cpu` `memory` `uptime` `timezone` `locale` `shell` `term` `node`。采样不到的字段(如无 `SHELL` 环境变量的 Windows)会自动省略该行。 ```yaml # ~/.dsh/profiles/web/cordis.patch.yml - id: where-am-i config: fields: [os, kernel, arch, cpu, memory, timezone] ``` ## 设计说明:dsh 现有提示词里已有哪些信息 实现前对 dsh 现有提示词做了调研(`dsh-system-prompt` 的段注册与变量、`dsh-agent-loop` 的变量提供、各 runtime-context 包),已有内容如下,本插件**只补缺口、不重复**: | 已有事实 | 来源 | 位置 | |---|---|---| | `You are an AI agent powered by DeepSeek Harness.` | `dsh-system-prompt` | 系统提示词段 `harness:identity`,order -100 | | DSH 源码 checkout 路径 | `dsh-app-boot` / `dsh-web-app` | 段 `harness:source`,order -99 | | Web GUI URL | `dsh-web-app`(仅 web profile) | 段 `app:web-surface`,order -98 | | 部署 persona | 配置 | 段 `deployment:persona`,order 0 | | 工具跨调用引导(read/write/edit/bash/web_search…) | 各 `dsh-tool-*` | 段 `tool:*`,order 100–199 | | `{{provider}}` `{{model}}` `{{cwd}}`(会话工作目录,每次组装求值) | `dsh-agent-loop` | 提示词变量 | | 文件沙箱策略、审批策略 | `dsh-sandbox-policy` / `dsh-user-approval` | runtime context(user 角色快照) | | 带时区时间戳 + **浏览器**时区 + 经过时长 | `dsh-time-context`(默认组合未启用) | runtime context | | tmux session/window/pane 位置 | `dsh-tmux-context`(非 web 组合) | runtime context | | `AGENTS.md` / `CLAUDE.md` 工作区指令 | `dsh-agent-instructions` | 持久 user 消息 | **完全缺失**(即本插件补的):OS/发行版名、内核 release、CPU 架构、主机名、CPU 型号与核心数、总/空闲内存、uptime、宿主 locale、shell/terminal、Node 版本、宿主进程时区。 ### 刻意省略的字段 - **`cwd`**:`{{cwd}}` 变量已按会话实时提供,harness 也明确警告不要从 checkout 路径推断工作目录——不重复。 - **日期/当前时间**:冻结在历史里的墙钟会过期;实时时间戳由 `dsh-time-context` 按请求提供(启用时)。 - **ASCII logo / 桌面壁纸**:纯 token 噪音,对模型无信息量。 ## 为什么不是「系统提示词段」,而是「新会话一次性消息」 第一版实现用了 `systemPrompt.section()`,但那意味着:插件挂载后,**每个会话的每一步**都渲染同一段文本——既做不到「一次」,也会改写所有已有会话的系统提示词前缀(KV cache 从第一个变化的 token 起失效一次)。 现在的实现改为 `dsh-time-context` / `dsh-tmux-context` 同一条 seam:注册一个**前置的 `agent/pre-step` waterfall 监听器**,先 `await next()` 让下游先决策,仅当决策进入(enter)时,向该步批次**追加一条持久、带来源的 user 消息**。agent loop 会在 `step/start` 后把批次里的每条消息 `session.append("user/message", …)` 持久化,于是: - 注入的消息成为**该会话自己的历史**(模型开头可见,compaction 前一直保留); - **不注册任何系统提示词段** → 已有会话的系统提示词字节级不变,前缀与 KV cache 全部保留; - 后续步骤(本会话或其他任何会话)看到历史里已存在消息 → 永不再次注入(跨重启、跨插件热重载都成立——判定只看会话日志本身)。 「全新会话」的判定:pre-step 时 `agent.session.events` 里还没有任何 `user/message`、`assistant/message` 或 `tool/result` 事件(第一步的在途提示词要等步骤进入后才持久化,time-context 的 README 明示这一点)。任何恢复/延续/已注入的会话都至少有一个这类事件 → 永久跳过。空批次(纯 runtime context、调度轮次等,没有 user 来源消息)也不会被注入。 设备信息在**注入时**采样(即新会话第一步的时刻),uptime/内存尽可能新鲜;消息是按会话的历史,会话之间字节差异零成本。 ### 对「已有会话前缀」的明确结论 - 系统提示词:插件**不注册段**,`renderPrompt` 对已有会话的输出与安装前一模一样(集成测试断言了这一点); - 会话历史:已有会话不会被追加任何消息; - 受影响面仅限**新建会话**:注入发生在它第一次请求之前,属于该会话自己的前缀,不存在旧缓存可供失效。 ## 实现细节 - **零子进程**:不调 `sw_vers` / `uname`,全部来自 `node:os`、一次同步读 `/etc/os-release`(Linux/WSL 取 `PRETTY_NAME`,回退 `ID VERSION_ID`)与 `Intl`。 - 架构名按 neofetch 习惯映射:`x64→x86_64`、`arm64→aarch64`、`ia32→i386`、`arm→armv7l`。 - macOS 从 Darwin release 反推营销版本(Darwin 24 ↔ macOS 15,19 ↔ 10.15,更老不猜)。 - 时区是**宿主进程**时区(IANA 名 + 当前 UTC 偏移,DST 感知);与 `dsh-time-context` 的浏览器时区是两个概念。 - 注入消息满足 harness 的消息契约(`assertMessageEventShape`:非空 `id`、`role: "user"`、`source.kind`、`content` 数组),source 形状与 time-context 的插件快照一致(`{ kind: 'plugin', plugin: 'where-am-i', form: 'snapshot', sections: [...] }`),UI/轨迹归因一致。 - 监听器 `inject: ["agents"]`(与 time-context 相同),树里没有 agents 注册表时不加载。 ## 验证 ```bash node test/verify.mjs # 单测:os-release 解析/架构映射/macOS 命名/字节与时间格式化/时区偏移/配置归一化/渲染子集/决策门(user 来源、历史门、子代理门)/消息形状/不重复 cwd/date + 本机真实快照 node test/smoke-apply.mjs # 接线:全新会话注入一次、已有会话零注入、同一会话永不重注入、无 user 来源不注入、reject/abort 透传、子代理门、fields 子集 node test/integration-real.mjs # 真实 cordis + dsh-system-prompt + dsh-session:系统提示词不被改动、注入消息被真实 Session 持久化并进入派生历史、同会话不再注入 ``` ## 说明与限制 - 快照在**新会话第一步**采样:该会话期间 uptime/内存/负载会过期(这正是 neofetch-at-session-start 语义;消息是历史,不再刷新)。 - `/etc/os-release` 缺失且非 Windows/macOS 时,`os` 行回退到 `os.version()`/`os.type()`,可能不如发行版名友好。 - `user` 行不隐去:模型能看见运行 dsh 的本地用户名(与 neofetch 一致)。在意隐私可以 `fields` 里去掉 `user`/`host`。 - 注入的是宿主机信息;若 dsh 跑在容器/远程执行里,报告的是容器/远程内核,而非开发机(如 WSL 会显示 WSL 内核号)。 - 消息会占用每条请求的 token(compaction 遮蔽前);这也是为什么已有会话不加、子代理默认不加——只付一次、只付给真正的新对话。