dominds 1.26.0 → 1.26.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,7 +4,9 @@ Chinese version: [中文版](./cli-usage.zh.md)
4
4
 
5
5
  The `dominds` CLI provides a unified entry point, but the **primary interaction experience is the Web UI** (the default `dominds` command). This guide focuses on the Web UI workflow.
6
6
 
7
- > Note: In this document, **rtws (runtime workspace)** refers to the runtime root directory Dominds uses (by default `process.cwd()`, switchable via `-C <abs-dir>`; `-C` only accepts absolute paths).
7
+ > Note: In this document, **rtws (runtime workspace)** refers to the runtime root directory Dominds uses (by default the directory where `dominds` is launched, switchable via `-C <dir>`). Relative `-C` paths are resolved against the original launch directory by the `dominds` supervisor before the runner starts.
8
+
9
+ > Process model: in production, `dominds` is a lightweight supervisor. It parses global options such as `-C`, starts `dominds-runner` in the resolved rtws, keeps the terminal stdio attached to the runner, and restarts long-running WebUI runners after crashes with exponential backoff starting at 1 second and capped at 30 minutes. Self-update restarts are coordinated by this supervisor, so the old runner can fully exit and release server resources before the new runner starts; if the old runner does not exit after a restart request, the supervisor terminates it before starting the next runner. Development WebUI runs (`NODE_ENV=dev` or `--mode dev`, including `dev-server.sh`) bypass the supervisor and are managed by the development launcher instead.
8
10
 
9
11
  > Note: `dominds tui` / `dominds run` are currently reserved subcommand names and do not have a stable implementation yet. As a result, this guide does not document TUI options or detailed usage.
10
12
 
@@ -58,8 +60,7 @@ dominds read [options] [member-id]
58
60
 
59
61
  # Certificate tools: create/inspect local HTTPS certificates
60
62
  dominds cert create [--host <host>] [--days <days>] [--force]
61
- dominds cert self-cert [--host <host>] [--days <days>] [--force]
62
- dominds cert status --host <host> [--port <port>] [--origin]
63
+ dominds cert status [--host <host>] [--port <port>] [--origin]
63
64
 
64
65
  # rtws creation: scaffold a new runtime workspace
65
66
  dominds create <template> [directory]
@@ -90,7 +91,7 @@ Start the web-based user interface for the current rtws. This provides a graphic
90
91
 
91
92
  - `-p, --port <port>` - Port to listen on; a bare port binds strictly, suffix `+` tries higher ports, and suffix `-` tries lower ports (omitting `--port` is equivalent to `5666-`)
92
93
  - `-h, --host <host>` - Host to bind to (default: localhost)
93
- - `-C, --cwd <abs-dir>` - Change to rtws directory before starting; absolute paths only
94
+ - `-C, --cwd <dir>` - Change to rtws directory before starting; relative paths are resolved against the original launch directory
94
95
  - `--help` - Show help message
95
96
 
96
97
  **Examples:**
@@ -100,6 +101,7 @@ dominds
100
101
  dominds webui -p 8080
101
102
  dominds webui -p 8080+
102
103
  dominds webui -C /path/to/my-rtws
104
+ dominds -C ux-rtws webui
103
105
  ```
104
106
 
105
107
  **Common use cases:**
@@ -129,7 +131,7 @@ Read and inspect agent prompts/configuration for the rtws. This is commonly used
129
131
 
130
132
  **Options:**
131
133
 
132
- - `-C, --cwd <abs-dir>` - Change to rtws directory before reading; absolute paths only
134
+ - `-C, --cwd <dir>` - Change to rtws directory before reading; relative paths are resolved against the original launch directory
133
135
  - `--only-prompt` - Show only system prompts
134
136
  - `--only-mem` - Show only memory
135
137
  - `--help` - Show help message
@@ -148,8 +150,7 @@ dominds read --only-mem
148
150
 
149
151
  ```bash
150
152
  dominds cert create [--host <host>] [--days <days>] [--force]
151
- dominds cert self-cert [--host <host>] [--days <days>] [--force]
152
- dominds cert status --host <host> [--port <port>] [--origin]
153
+ dominds cert status [--host <host>] [--port <port>] [--origin]
153
154
  ```
154
155
 
155
156
  Create or inspect local HTTPS certificates for the Dominds WebUI. Certificates live in `~/.dominds/certs/` and match DNS/IP hostnames, not ports; one certificate covers every WebUI port on that host.
@@ -167,11 +168,11 @@ Create or inspect local HTTPS certificates for the Dominds WebUI. Certificates l
167
168
  ```bash
168
169
  dominds cert create
169
170
  dominds cert create --host 192.168.1.10 --host my-host.local
170
- dominds cert status --host 0.0.0.0
171
- dominds cert status --host 0.0.0.0 --port 5666 --origin
171
+ dominds cert status
172
+ dominds cert status --port 5666 --origin
172
173
  ```
173
174
 
174
- `localhost`, `loopback`, `127.0.0.0/8`, `::1`, `0.0.0.0`, and `::` are not certificate hosts. `0.0.0.0` / `::` only mean bind-all; certificate matching uses detected non-loopback LAN hosts.
175
+ `localhost`, `loopback`, `127.0.0.0/8`, `169.254.0.0/16`, `::1`, `fe80::/10`, `0.0.0.0`, and `::` are not certificate hosts. `0.0.0.0` / `::` only mean bind-all; certificate matching uses detected non-loopback LAN hosts.
175
176
 
176
177
  ### rtws Creation
177
178
 
@@ -4,7 +4,9 @@
4
4
 
5
5
  `dominds` 提供统一的命令行入口,但**主要交互界面是 Web UI**(默认命令 `dominds`)。本文档以 Web UI 工作流为主。
6
6
 
7
- > 注:本文统一使用 **rtws(运行时工作区)** 表示 Dominds 运行时使用的根目录(默认等于 `process.cwd()`,可通过 `-C <abs-dir>` 切换;`-C` 只接受绝对路径)。
7
+ > 注:本文统一使用 **rtws(运行时工作区)** 表示 Dominds 运行时使用的根目录(默认是启动 `dominds` 时所在目录,可通过 `-C <dir>` 切换)。相对路径形式的 `-C` 由 `dominds` supervisor 按原始启动目录解析为绝对路径,再启动 runner。
8
+
9
+ > 进程模型:生产模式下,`dominds` 是轻量 supervisor,负责解析 `-C` 等全局选项、在解析后的 rtws 中启动 `dominds-runner`、让 runner 继承当前终端 stdio,并在长期运行的 WebUI runner 崩溃后用指数退避保活重启(初始 1 秒,最长 30 分钟)。self-update 重启也由 supervisor 协调,因此旧 runner 可以完整退出并释放 server 资源,再启动新版 runner;如果旧 runner 在发出重启请求后仍不退出,supervisor 会终止它再启动下一轮 runner。开发模式 WebUI(`NODE_ENV=dev` 或 `--mode dev`,包括 `dev-server.sh`)不走 supervisor,由开发启动器自行管理。
8
10
 
9
11
  > 说明:`dominds tui` / `dominds run` 相关功能目前尚未提供稳定实现(子命令名保留用于未来规划),因此本指南不再展开 TUI 的命令选项与用法细节。
10
12
 
@@ -58,8 +60,7 @@ dominds read [options] [member-id]
58
60
 
59
61
  # 证书工具:创建/检查本机 HTTPS 证书
60
62
  dominds cert create [--host <host>] [--days <days>] [--force]
61
- dominds cert self-cert [--host <host>] [--days <days>] [--force]
62
- dominds cert status --host <host> [--port <port>] [--origin]
63
+ dominds cert status [--host <host>] [--port <port>] [--origin]
63
64
 
64
65
  # rtws 创建:搭建新项目/运行时工作区
65
66
  dominds create <template> [directory]
@@ -90,7 +91,7 @@ dominds webui [options]
90
91
 
91
92
  - `-p, --port <port>` - 监听端口;裸端口严格绑定,后缀 `+` 向更大端口自动尝试,后缀 `-` 向更小端口自动尝试(未指定时等价于 `5666-`)
92
93
  - `-h, --host <host>` - 绑定的主机(默认:localhost)
93
- - `-C, --cwd <abs-dir>` - 启动前更改 rtws 目录;只接受绝对路径
94
+ - `-C, --cwd <dir>` - 启动前更改 rtws 目录;相对路径按原始启动目录解析
94
95
  - `--help` - 显示帮助消息
95
96
 
96
97
  **示例:**
@@ -107,6 +108,7 @@ dominds webui -p 8080+
107
108
 
108
109
  # 在特定 rtws 启动 Web UI
109
110
  dominds webui -C /path/to/my-rtws
111
+ dominds -C ux-rtws webui
110
112
  ```
111
113
 
112
114
  **常见用途:**
@@ -136,7 +138,7 @@ dominds read [options] [member-id]
136
138
 
137
139
  **选项:**
138
140
 
139
- - `-C, --cwd <abs-dir>` - 读取前更改 rtws 目录;只接受绝对路径
141
+ - `-C, --cwd <dir>` - 读取前更改 rtws 目录;相对路径按原始启动目录解析
140
142
  - `--only-prompt` - 仅显示系统提示词
141
143
  - `--only-mem` - 仅显示内存
142
144
  - `--help` - 显示帮助消息
@@ -155,8 +157,7 @@ dominds read --only-mem
155
157
 
156
158
  ```bash
157
159
  dominds cert create [--host <host>] [--days <days>] [--force]
158
- dominds cert self-cert [--host <host>] [--days <days>] [--force]
159
- dominds cert status --host <host> [--port <port>] [--origin]
160
+ dominds cert status [--host <host>] [--port <port>] [--origin]
160
161
  ```
161
162
 
162
163
  创建或检查 Dominds WebUI 的本机 HTTPS 证书。证书保存在 `~/.dominds/certs/`,按 DNS/IP 主机名匹配,不绑定端口;同一张证书可覆盖该主机上的所有 WebUI 端口。
@@ -174,11 +175,11 @@ dominds cert status --host <host> [--port <port>] [--origin]
174
175
  ```bash
175
176
  dominds cert create
176
177
  dominds cert create --host 192.168.1.10 --host my-host.local
177
- dominds cert status --host 0.0.0.0
178
- dominds cert status --host 0.0.0.0 --port 5666 --origin
178
+ dominds cert status
179
+ dominds cert status --port 5666 --origin
179
180
  ```
180
181
 
181
- `localhost`、`loopback`、`127.0.0.0/8`、`::1`、`0.0.0.0`、`::` 不会作为 HTTPS 证书主机。`0.0.0.0` / `::` 只表示绑定所有地址,匹配证书时会使用检测到的非 loopback LAN 主机。
182
+ `localhost`、`loopback`、`127.0.0.0/8`、`169.254.0.0/16`、`::1`、`fe80::/10`、`0.0.0.0`、`::` 不会作为 HTTPS 证书主机。`0.0.0.0` / `::` 只表示绑定所有地址,匹配证书时会使用检测到的非 loopback LAN 主机。
182
183
 
183
184
  ### rtws 创建
184
185
 
@@ -4,7 +4,7 @@ Chinese version: [中文版](./dialog-persistence.zh.md)
4
4
 
5
5
  This document describes the persistence layer and storage mechanisms for the Dominds dialog system, including file system conventions, data structures, and storage patterns.
6
6
 
7
- > Note: In this document, **rtws (runtime workspace)** refers to Dominds' runtime root directory (by default `process.cwd()`, switchable via `-C <abs-dir>`; `-C` only accepts absolute paths).
7
+ > Note: In this document, **rtws (runtime workspace)** refers to Dominds' runtime root directory (by default the directory where `dominds` is launched, switchable via `-C <dir>`; relative `-C` paths are resolved against the original launch directory).
8
8
 
9
9
  ## Current Implementation Status
10
10
 
@@ -264,8 +264,8 @@ Example / 示例(概念):
264
264
 
265
265
  ### rtws(运行时工作区)
266
266
 
267
- - EN: **rtws** (runtime workspace) is the directory Dominds treats as its runtime root (by default it is `process.cwd()`, and can be changed via `-C <abs-dir>`; `-C` only accepts absolute paths). Files like `.minds/` and `.dialogs/` live under the rtws.
268
- - ZH: **rtws(运行时工作区)**是 Dominds 运行时使用的根目录(默认等于 `process.cwd()`,可通过 `-C <abs-dir>` 切换;`-C` 只接受绝对路径)。诸如 `.minds/`、`.dialogs/` 等运行态目录均位于 rtws 下。
267
+ - EN: **rtws** (runtime workspace) is the directory Dominds treats as its runtime root (by default it is the directory where `dominds` is launched, and can be changed via `-C <dir>`; relative `-C` paths are resolved against the original launch directory). Files like `.minds/` and `.dialogs/` live under the rtws.
268
+ - ZH: **rtws(运行时工作区)**是 Dominds 运行时使用的根目录(默认是启动 `dominds` 时所在目录,可通过 `-C <dir>` 切换;相对路径形式的 `-C` 按原始启动目录解析)。诸如 `.minds/`、`.dialogs/` 等运行态目录均位于 rtws 下。
269
269
 
270
270
  - EN: Wording rule: when the meaning is **rtws**, prefer writing “rtws (runtime workspace)” (or just “rtws” after the first mention) rather than the ambiguous generic “workspace”.
271
271
  - ZH: 用词规则:当语义指向 **rtws** 时,优先写“rtws(运行时工作区)”(或在已定义后只写“rtws”),避免在对外提示/文档中只写“工作区”从而与其他语境的 workspace/workdir 混淆。
@@ -259,6 +259,10 @@ function resolveReminderContextFollowingDialogState(prompt, currentTurnDialogMsg
259
259
  return 'none';
260
260
  return prompt.origin === 'user' ? 'user_message' : 'runtime_notice';
261
261
  }
262
+ function resolveReminderContextHealthState(snapshot) {
263
+ const remediationLevel = getContextHealthRemediationLevel(snapshot);
264
+ return remediationLevel === undefined ? 'normal' : remediationLevel;
265
+ }
262
266
  async function resolveReminderContextFooterState(args) {
263
267
  const latest = await persistence_1.DialogPersistence.loadDialogLatest(args.dlg.id, args.dlg.status);
264
268
  const deferredReplyReassertion = latest?.deferredReplyReassertion;
@@ -273,6 +277,7 @@ async function resolveReminderContextFooterState(args) {
273
277
  followingDialogState: resolveReminderContextFollowingDialogState(args.prompt, args.currentTurnDialogMsgsForContext),
274
278
  pendingUserInterjectionReply,
275
279
  interDialogReplyObligation,
280
+ contextHealthState: resolveReminderContextHealthState(args.dlg.getLastContextHealth()),
276
281
  };
277
282
  }
278
283
  function splitDialogMsgsForReminderInsertion(args) {
@@ -715,8 +720,8 @@ const TELLASK_SPECIAL_VIRTUAL_TOOLS = [
715
720
  },
716
721
  ];
717
722
  const CONTEXT_HEALTH_TOOL_RESULT_VISIBLE_BYTE_LIMIT = 2000;
718
- const CONTEXT_HEALTH_LARGE_TOOL_RETURN_UNAVAILABLE_ZH = '这次函数返回内容太大,清理头脑之前当前不可见。';
719
- const CONTEXT_HEALTH_LARGE_TOOL_RETURN_UNAVAILABLE_EN = 'This function returned too much content, so it is not visible before clear_mind.';
723
+ const CONTEXT_HEALTH_LARGE_TOOL_RETURN_UNAVAILABLE_ZH = '这次函数返回内容太大,清理头脑之前不会显示给你。';
724
+ const CONTEXT_HEALTH_LARGE_TOOL_RETURN_UNAVAILABLE_EN = 'This function returned too much content. It will not be shown to you before you clear your mind.';
720
725
  function getContextHealthRemediationLevel(snapshot) {
721
726
  if (snapshot?.kind !== 'available') {
722
727
  return undefined;
@@ -736,9 +741,9 @@ function formatContextHealthLargeToolReturnUnavailable(args) {
736
741
  return [
737
742
  CONTEXT_HEALTH_LARGE_TOOL_RETURN_UNAVAILABLE_ZH,
738
743
  '',
739
- '不要重试获取这类大段输出。现在先做两件事:',
744
+ '不要再尝试获取各种大段的输出,都不会显示给你。现在先做两件事:',
740
745
  '1. 把需要回传给主线对话的结论、证据定位和风险整理清楚。',
741
- '2. 若还有恢复工作会丢的信息,写入提醒项。',
746
+ '2. 对于下一程恢复工作需要的信息,写入提醒项。',
742
747
  '',
743
748
  '然后尽快完成当前支线回复;如果你有 clear_mind({}),再调用它开启新一程。',
744
749
  '',
@@ -748,9 +753,9 @@ function formatContextHealthLargeToolReturnUnavailable(args) {
748
753
  return [
749
754
  CONTEXT_HEALTH_LARGE_TOOL_RETURN_UNAVAILABLE_ZH,
750
755
  '',
751
- '不要重试获取这类大段输出。现在先做两件事:',
752
- '1. 把下一程还需要知道的信息写入差遣牒合适章节。',
753
- '2. 把差遣牒没有覆盖、但恢复工作会丢的信息写入提醒项。',
756
+ '不要再尝试获取各种大段的输出,都不会显示给你。现在先做两件事:',
757
+ '1. 把下一程对话需要知道的此程细节信息写入差遣牒合适章节。',
758
+ '2. 对于不适合差遣牒章节覆盖、但下一程恢复工作需要的信息写入提醒项。',
754
759
  '',
755
760
  '然后调用 clear_mind({}) 开启新一程。',
756
761
  '',
@@ -761,11 +766,11 @@ function formatContextHealthLargeToolReturnUnavailable(args) {
761
766
  return [
762
767
  CONTEXT_HEALTH_LARGE_TOOL_RETURN_UNAVAILABLE_EN,
763
768
  '',
764
- 'Do not retry fetching this kind of large output. Do two things now:',
765
- '1. Organize the conclusion, evidence pointers, and risks that need to go back to the Main Dialog.',
766
- '2. If any resume-critical details would otherwise be lost, write them into reminders.',
769
+ 'Do not try again to fetch any kind of large output; it still will not be shown. Do two things now:',
770
+ '1. Organize the conclusions, evidence pointers, and risks that need to go back to the Mainline dialog.',
771
+ '2. Write any details needed to resume the next course into reminders.',
767
772
  '',
768
- 'Then finish the current Side Dialog reply as soon as possible; if you have clear_mind({}), call it to start a new course.',
773
+ 'Then finish the current Sideline dialog reply as soon as possible; if you have clear_mind({}), call it to start a new course.',
769
774
  '',
770
775
  `Detail: this return was about ${approxBytes} bytes.`,
771
776
  ].join('\n');
@@ -773,9 +778,9 @@ function formatContextHealthLargeToolReturnUnavailable(args) {
773
778
  return [
774
779
  CONTEXT_HEALTH_LARGE_TOOL_RETURN_UNAVAILABLE_EN,
775
780
  '',
776
- 'Do not retry fetching this kind of large output. Do two things now:',
777
- '1. Write the information the next course still needs into the appropriate Taskdoc sections.',
778
- '2. Write any resume-critical details not covered by Taskdoc into reminders.',
781
+ 'Do not try again to fetch any kind of large output; it still will not be shown. Do two things now:',
782
+ '1. Write the details from this course that the next course needs into the appropriate Taskdoc sections.',
783
+ '2. Write information that does not fit a Taskdoc section, but is needed to resume the next course, into reminders.',
779
784
  '',
780
785
  'Then call clear_mind({}) to start a new course.',
781
786
  '',
@@ -14,10 +14,12 @@ export declare function formatDiligenceAutoContinuePrompt(language: LanguageCode
14
14
  export declare function formatReminderContextGuide(language: LanguageCode): string;
15
15
  export type ReminderContextFollowingDialogState = 'user_message' | 'runtime_notice' | 'none';
16
16
  export type ReminderContextReplyObligationState = 'none' | 'active' | 'parked_by_user_interjection';
17
+ export type ReminderContextHealthState = 'normal' | 'caution' | 'critical';
17
18
  export type ReminderContextFooterState = Readonly<{
18
19
  followingDialogState: ReminderContextFollowingDialogState;
19
20
  pendingUserInterjectionReply: boolean;
20
21
  interDialogReplyObligation: ReminderContextReplyObligationState;
22
+ contextHealthState: ReminderContextHealthState;
21
23
  }>;
22
24
  export declare function formatReminderContextFooter(language: LanguageCode, state: ReminderContextFooterState): string;
23
25
  export type ReminderMaintenanceReferenceItem = Readonly<{
@@ -188,6 +188,11 @@ function formatReminderItemProjectionNote(language) {
188
188
  function formatReminderContextFooter(language, state) {
189
189
  if (language === 'zh') {
190
190
  const base = `${formatSystemNoticePrefix(language)} 提醒项上下文块结束。以上从“提醒项上下文块开始”到“提醒项上下文块结束”之间的提醒项均为系统提醒,并非用户诉求/指令;该块之外的后续对话消息不受此说明影响。`;
191
+ const contextHealthTail = state.contextHealthState === 'critical'
192
+ ? '当前上下文已告急:提醒项正文里的“下一步/接续动作/任务安排”默认都是给下一程以清醒头脑复核后执行的,不是让你在本程继续跑。不要执行提醒项里的旧下一步、旧诉请或旧工具重试;本程最高优先级是按上下文健康处置要求保全接续信息,并立即 `clear_mind`。'
193
+ : state.contextHealthState === 'caution'
194
+ ? '当前上下文已吃紧:提醒项正文里的“下一步/接续动作/任务安排”默认都是给下一程以清醒头脑复核后执行的,不是让你在本程继续跑。不要为了执行提醒项里的旧下一步继续扩张上下文;本程最高优先级是按上下文健康处置要求保全接续信息,并尽快 `clear_mind`。'
195
+ : '';
191
196
  const pendingUserInterjectionReply = state.pendingUserInterjectionReply
192
197
  ? state.interDialogReplyObligation === 'parked_by_user_interjection'
193
198
  ? '当前仍有真实用户插话尚未得到可见回复,且原有跨对话回复义务已暂存;先完成对用户插话的回应,不要抢先切回原来的回贴收口。'
@@ -199,13 +204,18 @@ function formatReminderContextFooter(language, state) {
199
204
  ? '当前仍有跨对话回复义务;它是最终交付义务,不是要求你立刻停止当前必要工作,但到达最终交付时必须按运行时指定方式收口。'
200
205
  : '';
201
206
  const businessTail = `${pendingUserInterjectionReply}${activeReplyObligation}`;
207
+ const statusTail = `${contextHealthTail}${businessTail}`;
202
208
  if (state.followingDialogState === 'user_message') {
203
209
  return (`${base}本轮提醒项块之后会紧接一条本轮真实的新用户消息;后续消息是用户的新诉求/指令,不是提醒项投影。` +
204
210
  '提醒项块说明到此为止,不得外溢到那条消息:不要把后续用户消息称为“系统提示/没有新消息”,也不要因为本块说明而降低它的指令优先级。' +
205
- `请按那条用户消息的原始语义继续处理;若它要求更新你的职责、偏好或心智资产,应照常落实。${businessTail}`);
211
+ `请按那条用户消息的原始语义继续处理;若它要求更新你的职责、偏好或心智资产,应照常落实;上下文健康处置要求仍按系统提示优先于普通任务动作。${statusTail}`);
206
212
  }
207
213
  if (state.followingDialogState === 'runtime_notice') {
208
- return `${base}本轮提醒项块之后会接着出现一条运行时提示;它不是用户的新诉求/指令,请按其中的运行时要求继续推进。${businessTail}`;
214
+ return `${base}本轮提醒项块之后会接着出现一条运行时提示;它不是用户的新诉求/指令,请按其中的运行时要求继续推进。${statusTail}`;
215
+ }
216
+ if (state.contextHealthState !== 'normal') {
217
+ return (`${base}本轮没有新的用户消息或运行时提示;这是工具调用后的自动续推。` +
218
+ `不要把“没有新消息”理解为空系统提示。${statusTail}`);
209
219
  }
210
220
  return (`${base}本轮没有新的用户消息或运行时提示;这是工具调用后的自动续推。` +
211
221
  '请基于已有任务状态判断下一步:若已有明确、相关且有价值的动作,就继续执行;若当前确实只能等待外部结果或用户输入,不要为了避免“等待”而寻找无关小事。' +
@@ -214,6 +224,11 @@ function formatReminderContextFooter(language, state) {
214
224
  const base = `${formatSystemNoticePrefix(language)} Reminder context block ends. The reminder items between ` +
215
225
  '"Reminder context block begins" and "Reminder context block ends" are system reminders, ' +
216
226
  'not user requests/instructions; this reminder-block guidance does not apply to subsequent dialog messages outside this block. ';
227
+ const contextHealthTail = state.contextHealthState === 'critical'
228
+ ? 'Context health is critical: any "next step", continuation action, or task plan inside reminders is meant for the next course to review and run with a clear head, not for this course to keep executing. Do not perform old next steps, old inter-dialog requests, or old tool retries from reminders; this course must first preserve the continuation details required by the context-health guidance, then call `clear_mind` immediately. '
229
+ : state.contextHealthState === 'caution'
230
+ ? 'Context health is tight: any "next step", continuation action, or task plan inside reminders is meant for the next course to review and run with a clear head, not for this course to keep executing. Do not expand context by performing old next steps from reminders; this course must first preserve the continuation details required by the context-health guidance, then call `clear_mind` soon. '
231
+ : '';
217
232
  const pendingUserInterjectionReply = state.pendingUserInterjectionReply
218
233
  ? state.interDialogReplyObligation === 'parked_by_user_interjection'
219
234
  ? "There is still a real user interjection without a visible reply, and the earlier inter-dialog reply obligation is parked; finish answering the user's interjection first, and do not switch back to closing the earlier reply yet. "
@@ -225,13 +240,18 @@ function formatReminderContextFooter(language, state) {
225
240
  ? 'An inter-dialog reply obligation is still active; it is a final delivery obligation, not a demand to stop necessary current work immediately, but final delivery must close through the runtime-specified path. '
226
241
  : '';
227
242
  const businessTail = `${pendingUserInterjectionReply}${activeReplyObligation}`;
243
+ const statusTail = `${contextHealthTail}${businessTail}`;
228
244
  if (state.followingDialogState === 'user_message') {
229
245
  return (`${base}A real new user message for this round immediately follows this reminder block; the following message is a new user request/instruction, not a reminder projection. ` +
230
246
  'The reminder-block guidance ends here and must not spill over onto that message: do not label the following user message as a "system notice" or "no new message", and do not lower its instruction priority because of this block. ' +
231
- `Handle that user message according to its original meaning; if it asks you to update your responsibilities, preferences, or mind assets, carry that out normally. ${businessTail}`);
247
+ `Handle that user message according to its original meaning; if it asks you to update your responsibilities, preferences, or mind assets, carry that out normally. Context-health remediation still takes system-guided priority over ordinary task actions. ${statusTail}`);
232
248
  }
233
249
  if (state.followingDialogState === 'runtime_notice') {
234
- return `${base}A runtime notice follows this reminder block in this round; it is not a new user request/instruction, so follow that runtime guidance and continue the work. ${businessTail}`;
250
+ return `${base}A runtime notice follows this reminder block in this round; it is not a new user request/instruction, so follow that runtime guidance and continue the work. ${statusTail}`;
251
+ }
252
+ if (state.contextHealthState !== 'normal') {
253
+ return (`${base}There is no new user message or runtime notice in this round; this is an automatic continuation after a tool call. ` +
254
+ `Do not interpret the absence of a new message as an empty system notice. ${statusTail}`);
235
255
  }
236
256
  return (`${base}There is no new user message or runtime notice in this round; this is an automatic continuation after a tool call. ` +
237
257
  'Judge the next step from the existing task state: if there is a clear, relevant, valuable action, continue with it; if the work genuinely can only wait for an external result or user input, do not invent unrelated work just to avoid "waiting". ' +
@@ -224,7 +224,7 @@ function validateCertificateHost(host) {
224
224
  if (!(0, network_hosts_1.isLanHttpsHost)(host)) {
225
225
  return {
226
226
  kind: 'invalid',
227
- message: '--host must not be localhost, loopback, 127.0.0.0/8, ::1, 0.0.0.0, or ::',
227
+ message: '--host must not be localhost, loopback, 127.0.0.0/8, 169.254.0.0/16, ::1, fe80::/10, 0.0.0.0, or ::',
228
228
  };
229
229
  }
230
230
  if (net.isIP(host) !== 0)
@@ -15,14 +15,11 @@ const promises_1 = __importDefault(require("fs/promises"));
15
15
  const path_1 = __importDefault(require("path"));
16
16
  const time_1 = require("@longrun-ai/kernel/utils/time");
17
17
  const log_1 = require("../log");
18
+ const supervisor_protocol_1 = require("../supervisor-protocol");
18
19
  const dominds_running_version_1 = require("./dominds-running-version");
19
20
  const log = (0, log_1.createLogger)('dominds-self-update');
20
21
  const BACKGROUND_CHECK_INTERVAL_MS = 30 * 60 * 1000;
21
22
  const LATEST_VERSION_CHECK_TIMEOUT_MS = 60000;
22
- const RESTART_PORT_RELEASE_TIMEOUT_MS = 15000;
23
- const RESTART_PORT_PROBE_INTERVAL_MS = 150;
24
- const RESTART_EXIT_GRACE_MS = 1000;
25
- const RESTART_FORCE_KILL_AFTER_MS = 30000;
26
23
  const COMMAND_OUTPUT_LOG_LIMIT = 2000;
27
24
  const PROXY_URL_ENV_KEYS = new Set([
28
25
  'HTTP_PROXY',
@@ -34,7 +31,6 @@ const PROXY_URL_ENV_KEYS = new Set([
34
31
  ]);
35
32
  const IDLE_RESTART_STATE = { kind: 'idle' };
36
33
  const PROCESS_START_ARGV = [...process.argv];
37
- const PROCESS_START_EXEC_ARGV = [...process.execArgv];
38
34
  const PROCESS_START_CWD = process.cwd();
39
35
  let runtimeConfig = null;
40
36
  let latestObservation = { kind: 'unknown' };
@@ -86,19 +82,6 @@ function detectRunKind(mode) {
86
82
  }
87
83
  return 'global';
88
84
  }
89
- function hasInteractiveConsole() {
90
- return Boolean(process.stdin.isTTY || process.stdout.isTTY || process.stderr.isTTY);
91
- }
92
- function getRestartHelperStdio() {
93
- return hasInteractiveConsole() ? 'inherit' : 'ignore';
94
- }
95
- function getRestartPortProbeHost(host) {
96
- if (host === '0.0.0.0')
97
- return '127.0.0.1';
98
- if (host === '::')
99
- return '::1';
100
- return host;
101
- }
102
85
  function normalizeComparablePath(value) {
103
86
  const normalized = path_1.default.normalize(value);
104
87
  return process.platform === 'win32' ? normalized.toLowerCase() : normalized;
@@ -118,9 +101,6 @@ function buildRestartTraceContext() {
118
101
  ].join('-'));
119
102
  return { debugDir, traceFile };
120
103
  }
121
- function getRestartHelperEntrypoint() {
122
- return path_1.default.resolve(__dirname, 'dominds-self-update-restart-helper.js');
123
- }
124
104
  async function appendRestartTrace(trace, event, details = {}) {
125
105
  const payload = {
126
106
  ...details,
@@ -170,13 +150,6 @@ function getCurrentProcessEntrypoint() {
170
150
  }
171
151
  return entrypoint;
172
152
  }
173
- function buildCurrentProcessRestartArgs() {
174
- getCurrentProcessEntrypoint();
175
- return [...PROCESS_START_EXEC_ARGV, ...PROCESS_START_ARGV.slice(1)];
176
- }
177
- function buildCurrentDomindsCliArgs() {
178
- return PROCESS_START_ARGV.slice(2);
179
- }
180
153
  function getNpxWorkspaceDirAbs() {
181
154
  const entrypoint = getCurrentProcessEntrypoint().replace(/\\/g, '/');
182
155
  const marker = '/node_modules/dominds/';
@@ -223,28 +196,12 @@ async function readNpxDomindsSpec() {
223
196
  }
224
197
  return null;
225
198
  }
226
- async function buildNpxLatestRestartLaunchSpec() {
227
- const npm = await resolveNpmCommandSpec();
228
- return {
229
- command: npm.command,
230
- args: [...npm.args, 'exec', '-y', '--', 'dominds@latest', ...buildCurrentDomindsCliArgs()],
231
- };
232
- }
233
- function buildCurrentProcessRestartLaunchSpec() {
234
- return {
235
- command: process.execPath,
236
- args: buildCurrentProcessRestartArgs(),
237
- };
238
- }
239
199
  function truncateCommandOutput(value) {
240
200
  const raw = typeof value === 'string' ? value.trim() : '';
241
201
  if (raw.length <= COMMAND_OUTPUT_LOG_LIMIT)
242
202
  return raw;
243
203
  return `${raw.slice(0, COMMAND_OUTPUT_LOG_LIMIT)}...[truncated ${raw.length - COMMAND_OUTPUT_LOG_LIMIT} chars]`;
244
204
  }
245
- function delayMs(ms) {
246
- return new Promise((resolve) => setTimeout(resolve, ms));
247
- }
248
205
  function formatPathEnvExcerpt(pathEnv) {
249
206
  if (pathEnv === null || pathEnv.trim() === '')
250
207
  return null;
@@ -1382,57 +1339,31 @@ async function installLatestDominds() {
1382
1339
  publishStatusUpdateSoon();
1383
1340
  return await getDomindsSelfUpdateStatus();
1384
1341
  }
1385
- async function spawnDetachedRestartHelper(params) {
1386
- const stdioMode = getRestartHelperStdio();
1387
- const helperEntrypoint = getRestartHelperEntrypoint();
1388
- await promises_1.default.access(helperEntrypoint);
1389
- const helperPayload = {
1390
- command: params.command,
1391
- args: [...params.args],
1392
- cwd: params.cwd,
1393
- host: getRestartPortProbeHost(params.host),
1394
- port: params.port,
1395
- retiringPid: process.pid,
1396
- forceKillAfterMs: RESTART_FORCE_KILL_AFTER_MS,
1397
- probeIntervalMs: RESTART_PORT_PROBE_INTERVAL_MS,
1398
- portReleaseTimeoutMs: RESTART_PORT_RELEASE_TIMEOUT_MS,
1399
- stdioMode,
1342
+ async function requestSupervisorRestart(params) {
1343
+ if (typeof process.send !== 'function') {
1344
+ throw new Error('Dominds restart requires the dominds supervisor process; this runner has no supervisor IPC channel');
1345
+ }
1346
+ const cfg = assertRuntimeConfig();
1347
+ const message = {
1348
+ type: supervisor_protocol_1.DOMINDS_SUPERVISOR_RESTART_WEBUI,
1349
+ cwd: process.cwd(),
1350
+ host: cfg.host,
1351
+ port: cfg.port,
1400
1352
  traceFile: params.trace.traceFile,
1401
1353
  debugDir: params.trace.debugDir,
1354
+ currentVersion: params.currentVersion,
1355
+ targetVersion: params.targetVersion,
1356
+ runKind: params.runKind,
1402
1357
  };
1403
- const helper = (0, child_process_1.spawn)(process.execPath, [helperEntrypoint, JSON.stringify(helperPayload)], {
1404
- cwd: params.cwd,
1405
- env: process.env,
1406
- detached: stdioMode !== 'inherit',
1407
- stdio: stdioMode,
1408
- windowsHide: stdioMode !== 'inherit',
1409
- });
1410
- const helperPid = typeof helper.pid === 'number' ? helper.pid : null;
1411
- const helperSpawned = new Promise((resolve, reject) => {
1412
- helper.once('spawn', () => {
1413
- appendRestartTraceSoon(params.trace, 'parent.helper_spawn_event', {
1414
- helperPid,
1415
- });
1416
- resolve(helperPid);
1417
- });
1418
- helper.once('error', (error) => {
1419
- appendRestartTraceSoon(params.trace, 'parent.helper_error_event', {
1420
- message: error.message,
1421
- stack: error.stack ?? null,
1422
- });
1423
- reject(error);
1424
- });
1425
- });
1426
- helper.once('exit', (code, signal) => {
1427
- appendRestartTraceSoon(params.trace, 'parent.helper_exit_event', {
1428
- code,
1429
- signal,
1358
+ await new Promise((resolve, reject) => {
1359
+ process.send?.(message, (error) => {
1360
+ if (error !== null) {
1361
+ reject(error);
1362
+ return;
1363
+ }
1364
+ resolve();
1430
1365
  });
1431
1366
  });
1432
- if (stdioMode !== 'inherit') {
1433
- helper.unref();
1434
- }
1435
- return await helperSpawned;
1436
1367
  }
1437
1368
  async function stopAndExitForRestart() {
1438
1369
  const cfg = assertRuntimeConfig();
@@ -1444,7 +1375,6 @@ async function stopAndExitForRestart() {
1444
1375
  await appendRestartTraceBestEffort(trace, 'parent.stop_and_exit.start', {
1445
1376
  host: cfg.host,
1446
1377
  port: cfg.port,
1447
- exitGraceMs: RESTART_EXIT_GRACE_MS,
1448
1378
  });
1449
1379
  }
1450
1380
  const stopPromise = cfg
@@ -1481,18 +1411,8 @@ async function stopAndExitForRestart() {
1481
1411
  return undefined;
1482
1412
  });
1483
1413
  cfg.closeWebSocketClients();
1484
- await delayMs(RESTART_EXIT_GRACE_MS);
1485
- if (!stopSettled) {
1486
- log.warn('Exiting Dominds process before graceful HTTP server stop completed during restart', undefined, {
1487
- host: cfg.host,
1488
- port: cfg.port,
1489
- graceMs: RESTART_EXIT_GRACE_MS,
1490
- });
1491
- }
1414
+ await stopPromise;
1492
1415
  if (trace !== null) {
1493
- if (stopSettled) {
1494
- await stopPromise;
1495
- }
1496
1416
  await appendRestartTraceBestEffort(trace, 'parent.process_exit', {
1497
1417
  host: cfg.host,
1498
1418
  port: cfg.port,
@@ -1519,19 +1439,17 @@ async function restartDomindsIntoLatest() {
1519
1439
  throw new Error('No restartable Dominds update is currently available');
1520
1440
  }
1521
1441
  const runKind = detectRunKind(cfg.mode);
1522
- let launchSpec;
1523
- const previousRestartRequiredState = restartState.kind === 'restart_required' ? restartState : null;
1524
- if (previousRestartRequiredState !== null) {
1525
- launchSpec = buildCurrentProcessRestartLaunchSpec();
1442
+ if (runKind === 'disabled') {
1443
+ throw new Error('Dominds restart requires a production run kind');
1526
1444
  }
1527
- else if (runKind === 'npx') {
1445
+ const previousRestartRequiredState = restartState.kind === 'restart_required' ? restartState : null;
1446
+ if (previousRestartRequiredState === null && runKind === 'npx') {
1528
1447
  const npxSpec = await readNpxDomindsSpec();
1529
1448
  if (npxSpec?.kind !== 'latest') {
1530
1449
  throw new Error('Dominds npx restart requires launching Dominds with dominds@latest');
1531
1450
  }
1532
- launchSpec = await buildNpxLatestRestartLaunchSpec();
1533
1451
  }
1534
- else {
1452
+ else if (previousRestartRequiredState === null) {
1535
1453
  throw new Error('Dominds restart requires a completed install or a restartable session');
1536
1454
  }
1537
1455
  restartState = { kind: 'restarting', targetVersion: status.targetVersion };
@@ -1544,29 +1462,23 @@ async function restartDomindsIntoLatest() {
1544
1462
  runKind,
1545
1463
  currentVersion: status.currentVersion,
1546
1464
  targetVersion: status.targetVersion,
1547
- launchCommand: launchSpec.command,
1548
- launchArgs: launchSpec.args,
1549
1465
  restartCwd,
1550
1466
  host: cfg.host,
1551
1467
  port: cfg.port,
1552
1468
  traceFile: trace.traceFile,
1553
1469
  });
1554
- const helperPid = await spawnDetachedRestartHelper({
1555
- command: launchSpec.command,
1556
- args: launchSpec.args,
1557
- cwd: restartCwd,
1558
- host: cfg.host,
1559
- port: cfg.port,
1470
+ await requestSupervisorRestart({
1560
1471
  trace,
1472
+ runKind,
1473
+ currentVersion: status.currentVersion,
1474
+ targetVersion: status.targetVersion,
1561
1475
  });
1562
- await appendRestartTraceBestEffort(trace, 'parent.helper_spawned', {
1563
- helperPid,
1476
+ await appendRestartTraceBestEffort(trace, 'parent.supervisor_restart_requested', {
1564
1477
  retiringPid: process.pid,
1565
1478
  host: cfg.host,
1566
1479
  port: cfg.port,
1567
1480
  });
1568
- log.info('Dominds restart helper spawned', undefined, {
1569
- helperPid,
1481
+ log.info('Dominds supervisor restart requested', undefined, {
1570
1482
  traceFile: trace.traceFile,
1571
1483
  });
1572
1484
  setImmediate(() => {