@onefl666/dsh-great-plan 0.5.0 → 0.6.0

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.
package/README.md CHANGED
@@ -18,6 +18,7 @@ A DSH Web plugin: choose the model and reasoning effort, then execute a reviewed
18
18
  - **Unlimited-token-budget plans** — in plan mode and fresh-session execution, remind the model that its token budget is unlimited and not to cut investigation, tool calls, or necessary questions to save tokens (advisory only). Turn this off on the plugin's Settings page.
19
19
  - **Great Plan settings page** — a page in the native Settings panel with four switches: **Inject brainstorming prompt**, **Inject unlimited token budget prompt**, **Include original user instructions**, and **Include runtime context in Minimal mode** (all on by default). The plan-prompt switches apply to the next plan without restarting DSH; the fresh-session switches apply to the next execution.
20
20
  - **Evidence-first plans** — in plan mode, the planner is asked to include background, an evidence chain, and a handoff for the executor (advisory only).
21
+ - **English-only, compact model prompts** — everything the plugin adds to the model's context (the plan-mode guidance and the fresh-session first message) is short, fixed English text regardless of the UI language, and it does not repeat rules the native plan-mode policy already states. Your own messages and the plan are carried unchanged.
21
22
  - **First-message preview and this-run switches** — before **Start fresh and execute…** confirms, the dialog previews exactly what the new session's first message will contain: the execution intro, the optional budget reminder, the optional Minimal runtime context, the original user instructions (with count and size), the approved plan, and the canonical approval handoff, plus total size and an approximate token count. Budget, instructions, and runtime context can be toggled for this run only; global Settings stay unchanged. An oversized hint appears above ~32,000 characters, and instructions are never truncated.
22
23
  - **Recoverable compaction failure** — if compaction cannot run (unavailable backend, no compactable history, a failed request, or cancellation), the plan is no longer approved automatically: the dialog explains the cause and offers **Retry compaction**, **Execute anyway**, or **Cancel**. Failure copy is localized in the dialog.
23
24
  - **Plan inside the dialog and Back to editing** — expand the complete reviewed plan without closing the dialog, and use **Back to editing** (the native Discuss behavior: the card is hidden and the review stays pending) to return to the composer. The backdrop now dims the page behind the dialog.
@@ -33,7 +34,7 @@ The native **Approve**, **Request changes**, and **View full plan** controls are
33
34
 
34
35
  ## Installation
35
36
 
36
- npm package name: `@onefl666/dsh-great-plan`. Install the published package with `plugin_manager` `install_bundle`, using the package spec as the target (for example `@onefl666/dsh-great-plan@0.5.0`). For a local checkout, use the absolute package directory as the target instead. The plugin then appears in the Plugins list as **Great Plan**.
37
+ npm package name: `@onefl666/dsh-great-plan`. Install the published package with `plugin_manager` `install_bundle`, using the package spec as the target (for example `@onefl666/dsh-great-plan@0.6.0`). For a local checkout, use the absolute package directory as the target instead. The plugin then appears in the Plugins list as **Great Plan**.
37
38
 
38
39
  ## Usage
39
40
 
@@ -73,25 +74,27 @@ Open **Settings** from the sidebar and choose **Great Plan**. **Inject brainstor
73
74
 
74
75
  ## Development
75
76
 
76
- There is no build step or installed dependency. The Host half imports a few DSH packages (`@deepseek-ai/schemastery`, `@deepseek-ai/dsh-compaction`) that the running installation supplies, declared as peer dependencies. Run the gate (syntax check + host tests); it finds DSH's bundled Node when none is on `PATH`:
77
+ There is no build step or installed dependency. The Host half imports a few DSH packages (`@deepseek-ai/schemastery`, `@deepseek-ai/dsh-compaction`) that the running installation supplies, declared as peer dependencies. Run the gate (syntax check + offline tests); it finds DSH's bundled Node when none is on `PATH`:
77
78
 
78
79
  ```bash
79
80
  bash tools/gate.sh
80
81
  ```
81
82
 
82
- A green gate proves parsing and the offline tests only, not GUI behavior. The same gate runs in CI (`.github/workflows/gate.yml`, Node 24) on push and pull requests. Restart DSH after Host changes; touch `client.js` and reload the page after Client changes. Architecture, the manual GUI checklist, and conventions are in [AGENTS.md](https://github.com/onefl666/dsh-great-plan/blob/main/AGENTS.md).
83
+ A green gate proves parsing and the offline tests only, not GUI behavior. The same gate runs in CI (`.github/workflows/gate.yml`, Node 24) on push and pull requests. Restart DSH after Host changes; touch `lib/client.js` and reload the page after Client changes. Architecture, the manual GUI checklist, and conventions are in [AGENTS.md](https://github.com/onefl666/dsh-great-plan/blob/main/AGENTS.md).
83
84
 
84
85
  ## Project structure
85
86
 
86
87
  | Path | Role |
87
88
  |---|---|
88
- | `index.js`, `host/` | Host half: the `/great-plan-compact` command and the plan-mode prompt section |
89
- | `client.js`, `client.<name>.js` | Client half: entry plus `ui`, `settings`, `model`, `preset`, `actions`, `panel` chunks |
89
+ | `lib/index.js`, `lib/host/` | Host half: the `/great-plan-compact` command and the plan-mode prompt section |
90
+ | `lib/client.js`, `lib/client.<name>.js` | Client half: entry plus `ui`, `settings`, `model`, `preset`, `actions`, `panel` chunks |
90
91
  | `locale/` | Plugins-list title and description |
91
- | `test/`, `tools/gate.sh` | Offline tests and the verification gate |
92
+ | `test/manifest.test.js`, `test/host/`, `test/client/` | Offline tests: package manifest, then Host and Client suites |
93
+ | `docs/promo/` | Promo video sources; repository-only, rendered frames are git-ignored |
94
+ | `tools/gate.sh` | Verification gate |
92
95
  | `.github/workflows/gate.yml` | CI: runs the gate on push and pull requests |
93
96
 
94
- The npm package ships only `index.js`, `host/`, `client*.js`, `cordis.patch.yml`, `locale/`, `icon.svg`, the READMEs, and `LICENSE`; `test/`, `tools/`, `.github/`, and `AGENTS.md` are repository-only.
97
+ The npm package ships only `lib/index.js`, `lib/host/`, `lib/client*.js`, `cordis.patch.yml`, `locale/`, `icon.svg`, the READMEs, and `LICENSE`; `test/`, `tools/`, `.github/`, `docs/`, and `AGENTS.md` are repository-only.
95
98
 
96
99
  See the Module Map in [AGENTS.md](https://github.com/onefl666/dsh-great-plan/blob/main/AGENTS.md) for details.
97
100
 
package/README.zh-CN.md CHANGED
@@ -18,6 +18,7 @@
18
18
  - **无限 Token 预算提示**:plan 模式与新会话执行时,提示模型 token 预算无上限,不要为节省用量而减少调研、工具调用或必要提问(建议性约束)。可在插件的设置页关闭。
19
19
  - **卓越计划设置页**:在原生「设置」面板中增加一页,提供 **注入头脑风暴提示词**、**注入 token 预算无上限提示词**、**携带原始用户指令** 与 **极简模式下附带运行环境** 四个开关(默认开启)。计划提示词开关在下次生成计划时生效,新会话开关在下次执行时生效。
20
20
  - **证据优先的计划**:plan 模式下要求计划包含背景、证据链和交接说明(建议性约束)。
21
+ - **英文、精简的模型提示词**:插件注入模型上下文的内容(plan 模式提示词段与新会话首条消息)始终是固定的精简英文,与界面语言无关,也不重复原生 plan 模式策略已有的规则。你自己发送的消息与计划内容原样携带。
21
22
  - **首条消息预览与本次执行开关**:「清空上下文并执行…」确认前,浮层会预览新会话首条消息的实际内容:执行说明、可选的预算提醒、可选的极简运行环境、原始用户指令(含条数与字符数)、已批准计划以及原生批准提示,并显示总字符数与估算 tokens。预算、指令、运行环境三个开关只影响本次执行,不修改全局设置。超过约 32,000 字符会显示超大提示,指令永不截断。
22
23
  - **可恢复的压缩失败**:压缩无法进行(后端不可用、无可压缩历史、请求失败或取消)时不再自动批准计划,浮层会说明原因并提供 **重试压缩**、**仍然执行**、**取消**。浮层内的失败文案已本地化。
23
24
  - **浮层内查看计划与返回修改**:无需关闭浮层即可展开完整计划;「返回修改」等价原生 Discuss(隐藏审核卡、计划仍待审)。遮罩会压暗页面。
@@ -33,7 +34,7 @@
33
34
 
34
35
  ## 安装
35
36
 
36
- npm 包名为 `@onefl666/dsh-great-plan`。通过 `plugin_manager` 的 `install_bundle` 安装已发布版本,target 用包 spec(例如 `@onefl666/dsh-great-plan@0.5.0`);本地开发时改用包目录的绝对路径。安装后插件在 Plugins 列表中显示为 **卓越计划**。
37
+ npm 包名为 `@onefl666/dsh-great-plan`。通过 `plugin_manager` 的 `install_bundle` 安装已发布版本,target 用包 spec(例如 `@onefl666/dsh-great-plan@0.6.0`);本地开发时改用包目录的绝对路径。安装后插件在 Plugins 列表中显示为 **卓越计划**。
37
38
 
38
39
  ## 使用
39
40
 
@@ -73,25 +74,27 @@ npm 包名为 `@onefl666/dsh-great-plan`。通过 `plugin_manager` 的 `install_
73
74
 
74
75
  ## 开发
75
76
 
76
- 无构建步骤、无安装依赖。Host 半会导入几个由运行中的 DSH 安装提供的包(`@deepseek-ai/schemastery`、`@deepseek-ai/dsh-compaction`),已在 `peerDependencies` 中声明。运行 gate(语法检查 + host 测试);`PATH` 中没有 node 时会自动使用 DSH 自带的 Node:
77
+ 无构建步骤、无安装依赖。Host 半会导入几个由运行中的 DSH 安装提供的包(`@deepseek-ai/schemastery`、`@deepseek-ai/dsh-compaction`),已在 `peerDependencies` 中声明。运行 gate(语法检查 + 离线测试);`PATH` 中没有 node 时会自动使用 DSH 自带的 Node:
77
78
 
78
79
  ```bash
79
80
  bash tools/gate.sh
80
81
  ```
81
82
 
82
- gate 通过只说明语法与离线测试正常,不代表 GUI 行为正确。同一 gate 在 CI(`.github/workflows/gate.yml`,Node 24)中随 push 与 pull request 运行。修改 Host 后需重启 DSH;修改 Client 后 touch `client.js` 并刷新页面。架构、GUI 手工清单与约定见 [AGENTS.md](https://github.com/onefl666/dsh-great-plan/blob/main/AGENTS.md)。
83
+ gate 通过只说明语法与离线测试正常,不代表 GUI 行为正确。同一 gate 在 CI(`.github/workflows/gate.yml`,Node 24)中随 push 与 pull request 运行。修改 Host 后需重启 DSH;修改 Client 后 touch `lib/client.js` 并刷新页面。架构、GUI 手工清单与约定见 [AGENTS.md](https://github.com/onefl666/dsh-great-plan/blob/main/AGENTS.md)。
83
84
 
84
85
  ## 项目结构
85
86
 
86
87
  | 路径 | 作用 |
87
88
  |---|---|
88
- | `index.js`、`host/` | Host 半:`/great-plan-compact` 命令与 plan 模式提示词段 |
89
- | `client.js`、`client.<name>.js` | Client 半:入口及 `ui`、`settings`、`model`、`preset`、`actions`、`panel` chunk |
89
+ | `lib/index.js`、`lib/host/` | Host 半:`/great-plan-compact` 命令与 plan 模式提示词段 |
90
+ | `lib/client.js`、`lib/client.<name>.js` | Client 半:入口及 `ui`、`settings`、`model`、`preset`、`actions`、`panel` chunk |
90
91
  | `locale/` | Plugins 列表的标题与描述 |
91
- | `test/`、`tools/gate.sh` | 离线测试与验证 gate |
92
+ | `test/manifest.test.js`、`test/host/`、`test/client/` | 离线测试:包清单,以及 Host 与 Client 两套用例 |
93
+ | `docs/promo/` | 宣传视频源文件;仅存在于仓库,渲染帧目录被 git 忽略 |
94
+ | `tools/gate.sh` | 验证 gate |
92
95
  | `.github/workflows/gate.yml` | CI:push 与 pull request 时运行 gate |
93
96
 
94
- npm 包只包含 `index.js`、`host/`、`client*.js`、`cordis.patch.yml`、`locale/`、`icon.svg`、两个 README 与 `LICENSE`;`test/`、`tools/`、`.github/`、`AGENTS.md` 仅存在于仓库。
97
+ npm 包只包含 `lib/index.js`、`lib/host/`、`lib/client*.js`、`cordis.patch.yml`、`locale/`、`icon.svg`、两个 README 与 `LICENSE`;`test/`、`tools/`、`.github/`、`docs/`、`AGENTS.md` 仅存在于仓库。
95
98
 
96
99
  详见 [AGENTS.md](https://github.com/onefl666/dsh-great-plan/blob/main/AGENTS.md) 的 Module Map。
97
100
 
@@ -6,8 +6,18 @@ window.__ModuleLoader__.load({
6
6
  return error instanceof Error ? error.message : String(error);
7
7
  }
8
8
 
9
- /** Model-facing reminder appended to fresh-session execution when the budget switch is on. */
10
- const UNLIMITED_BUDGET_REMINDER = 'Token budget: unlimited. Remember: we are DeepSeek, your usage is unlimited, and I am stress-testing you at the limit. Do not skimp on tokens — spend freely; you are very wealthy. Do not reduce investigation, tool calls, or necessary questions to save tokens; keep the final summary concise.';
9
+ /**
10
+ * Model-facing text is fixed English regardless of the UI locale; only the
11
+ * dialog chrome is translated.
12
+ */
13
+ const FRESH_PROMPT_INTRO = 'Execute the approved plan below in this fresh session. Do not re-plan or repeat its investigation: reuse its evidence, and first verify anything it marks as unverified or assumed.';
14
+
15
+ const UNLIMITED_BUDGET_REMINDER = 'Token budget: unlimited. Never cut investigation, tool calls, or needed questions to save tokens; keep the final summary concise.';
16
+
17
+ const PLAN_MODE_INSTRUCTIONS_HEADING = 'User messages from plan mode, oldest first (context for the plan):';
18
+
19
+ /** Model-facing note that always closes the Minimal runtime context. */
20
+ const FRESH_SESSION_NOTE = 'Fresh session: the previous conversation is not included.';
11
21
 
12
22
  /**
13
23
  * Exact model-facing text native plan mode appends after an approved review.
@@ -17,7 +27,7 @@ window.__ModuleLoader__.load({
17
27
  */
18
28
  const PLAN_APPROVED_HANDOFF = 'Plan approved — plan mode exited; carry out the plan starting with your next step.';
19
29
 
20
- /** Fixed wrapper tags keep the model-facing prompt parseable across locales. */
30
+ /** Fixed wrapper tags keep the model-facing prompt parseable. */
21
31
  const PLAN_MODE_INSTRUCTIONS_TAG = 'plan-mode-user-instructions';
22
32
  const FRESH_SESSION_CONTEXT_TAG = 'fresh-session-context';
23
33
  const INSTRUCTIONS_SEPARATOR = '\n\n---\n\n';
@@ -36,7 +46,7 @@ window.__ModuleLoader__.load({
36
46
  * Host command failure text mapped to the Client locale keys.
37
47
  *
38
48
  * The keys are the Host's exact user-visible static messages (see
39
- * `host/plan-compaction.js` exports `USAGE`, `FAILURES`, and `MESSAGES`)
49
+ * `lib/host/plan-compaction.js` exports `USAGE`, `FAILURES`, and `MESSAGES`)
40
50
  * plus the Client-side unavailable message. A cross-side test keeps both
41
51
  * halves in sync; dynamic Host messages intentionally fall back to raw
42
52
  * English.
@@ -57,18 +67,6 @@ window.__ModuleLoader__.load({
57
67
  'The /great-plan-compact command is not available.': 'compact.error.unavailable',
58
68
  };
59
69
 
60
- /** Stable action failure codes; each one has a `t('error.<code>')` locale key. */
61
- const ACTION_ERROR_CODES = [
62
- 'emptyPlan',
63
- 'reviewGone',
64
- 'createSessionFailed',
65
- 'modelApplyFailed',
66
- 'startSessionFailed',
67
- 'openSessionFailed',
68
- 'dismissFailed',
69
- 'cleanupFailed',
70
- ];
71
-
72
70
  /** English fallback messages for action failures; the Client localizes them. */
73
71
  const ACTION_ERROR_MESSAGES = {
74
72
  emptyPlan: 'The reviewed plan is empty.',
@@ -81,18 +79,37 @@ window.__ModuleLoader__.load({
81
79
  cleanupFailed: 'The unused new session could not be cleaned up: {message}',
82
80
  };
83
81
 
84
- /** Cached `Intl.DateTimeFormat` instances keyed by `<kind>|<zone>`. */
82
+ /** Stable action failure codes; each one has a `t('error.<code>')` locale key. */
83
+ const ACTION_ERROR_CODES = Object.keys(ACTION_ERROR_MESSAGES);
84
+
85
85
  const INTL_FORMATTERS = new Map();
86
86
 
87
- /** Fill `{name}` placeholders in one English template. */
88
87
  function formatTemplate(template, params) {
89
88
  if (params === null || typeof params !== 'object') return template;
90
89
  return Object.entries(params).reduce(
91
- (text, [name, value]) => text.replaceAll(`{${name}}`, String(value)),
90
+ (text, [name, value]) => text.replaceAll(`{${name}}`, () => String(value)),
92
91
  template,
93
92
  );
94
93
  }
95
94
 
95
+ function formatRpcFailure(failure) {
96
+ try {
97
+ const candidate = failure !== null && typeof failure === 'object' ? failure : {};
98
+ const detail = candidate.message === undefined
99
+ && candidate.code === undefined
100
+ && candidate.error !== null
101
+ && typeof candidate.error === 'object'
102
+ ? candidate.error
103
+ : candidate;
104
+ const message = typeof detail.message === 'string' && detail.message !== '' ? detail.message : '';
105
+ const code = typeof detail.code === 'string' && detail.code !== '' ? detail.code : '';
106
+ if (message !== '') return code === '' ? message : `${message} (${code})`;
107
+ return code === '' ? 'unknown error' : `unknown error (${code})`;
108
+ } catch {
109
+ return 'unknown error';
110
+ }
111
+ }
112
+
96
113
  /**
97
114
  * One user-facing action failure.
98
115
  *
@@ -181,24 +198,6 @@ window.__ModuleLoader__.load({
181
198
  return { unlimitedBudget: true, includeOriginalInstructions: true, includeMinimalContext: true };
182
199
  }
183
200
 
184
- /** Resolve one prompt label, with an English fallback when `t` is unavailable. */
185
- function translate(t, key, fallback, params) {
186
- if (typeof t === 'function') {
187
- try {
188
- const value = t(key, params);
189
- if (typeof value === 'string' && value !== '') return value;
190
- } catch {
191
- // Fall through to the fallback.
192
- }
193
- }
194
- if (params === undefined) return fallback;
195
- return Object.entries(params).reduce(
196
- (text, [name, value]) => text.replaceAll(`{${name}}`, String(value)),
197
- fallback,
198
- );
199
- }
200
-
201
- /** Join the text blocks of one user message. */
202
201
  function textOf(content) {
203
202
  if (typeof content === 'string') return content.trim();
204
203
  if (!Array.isArray(content)) return '';
@@ -209,7 +208,6 @@ window.__ModuleLoader__.load({
209
208
  .trim();
210
209
  }
211
210
 
212
- /** Whether one session event belongs to the real user rather than an injected context. */
213
211
  function isUserMessage(event) {
214
212
  if (event === null || typeof event !== 'object' || event.type !== 'user/message') return false;
215
213
  const kind = event.data?.source?.kind;
@@ -242,7 +240,6 @@ window.__ModuleLoader__.load({
242
240
  return commandStart;
243
241
  }
244
242
 
245
- /** Locate the latest turn start; used only when no plan-mode marker is loaded. */
246
243
  function findTurnStart(entries) {
247
244
  let turnStart;
248
245
  for (let index = 0; index < entries.length; index += 1) {
@@ -276,7 +273,6 @@ window.__ModuleLoader__.load({
276
273
  return [];
277
274
  }
278
275
 
279
- /** Trim, drop empty and non-string instruction texts while keeping the order. */
280
276
  function normalizeInstructionTexts(instructions) {
281
277
  if (!Array.isArray(instructions)) return [];
282
278
  return instructions
@@ -284,15 +280,9 @@ window.__ModuleLoader__.load({
284
280
  .filter((text) => text !== '');
285
281
  }
286
282
 
287
- /** Render the original-instructions block; empty when nothing is carried. */
288
- function buildInstructionBlock(texts, t) {
283
+ function buildInstructionBlock(texts) {
289
284
  if (texts.length === 0) return '';
290
- const heading = translate(
291
- t,
292
- 'clear.instructionsIntro',
293
- 'User instructions sent during plan mode, in chronological order (background for the approved plan):',
294
- );
295
- return `${heading}\n\n<${PLAN_MODE_INSTRUCTIONS_TAG}>\n${texts.join(INSTRUCTIONS_SEPARATOR)}\n</${PLAN_MODE_INSTRUCTIONS_TAG}>\n\n`;
285
+ return `${PLAN_MODE_INSTRUCTIONS_HEADING}\n\n<${PLAN_MODE_INSTRUCTIONS_TAG}>\n${texts.join(INSTRUCTIONS_SEPARATOR)}\n</${PLAN_MODE_INSTRUCTIONS_TAG}>\n\n`;
296
286
  }
297
287
 
298
288
  /**
@@ -361,7 +351,6 @@ window.__ModuleLoader__.load({
361
351
  return undefined;
362
352
  }
363
353
 
364
- /** Resolve the browser time zone, falling back to UTC. */
365
354
  function resolveTimeZone() {
366
355
  try {
367
356
  const zone = Intl.DateTimeFormat().resolvedOptions().timeZone;
@@ -392,7 +381,6 @@ window.__ModuleLoader__.load({
392
381
  return formatter;
393
382
  }
394
383
 
395
- /** Parse the numeric offset of a zone at one instant. */
396
384
  function offsetMinutesOf(zone, date) {
397
385
  try {
398
386
  const name = formatterOf('offset', zone)
@@ -423,7 +411,6 @@ window.__ModuleLoader__.load({
423
411
  }
424
412
  }
425
413
 
426
- /** Read the current browser-side runtime facts used by the minimal context. */
427
414
  function readRuntimeFacts(options = {}) {
428
415
  try {
429
416
  const nav = options.nav === undefined ? globalThis.navigator : options.nav;
@@ -435,40 +422,26 @@ window.__ModuleLoader__.load({
435
422
  }
436
423
  }
437
424
 
438
- /** Build the compact fresh-session environment block for Minimal mode. */
439
- function buildMinimalRuntimeContext({ workspaceDirectory, system, time } = {}, t) {
425
+ function buildMinimalRuntimeContext({ workspaceDirectory, system, time } = {}) {
440
426
  const lines = [];
441
427
  const directory = typeof workspaceDirectory === 'string' ? workspaceDirectory.trim() : '';
442
- if (directory !== '') {
443
- lines.push(translate(t, 'clear.context.workspace', 'Workspace directory: {path}', { path: directory }));
444
- }
428
+ if (directory !== '') lines.push(`Workspace: ${directory}`);
445
429
  const systemText = typeof system === 'string' ? system.trim() : '';
446
- if (systemText !== '') {
447
- lines.push(translate(t, 'clear.context.system', 'System: {system}', { system: systemText }));
448
- }
430
+ if (systemText !== '') lines.push(`Platform: ${systemText}`);
449
431
  const timeText = typeof time === 'string' ? time.trim() : '';
450
- if (timeText !== '') {
451
- lines.push(translate(t, 'clear.context.time', 'Time: {time}', { time: timeText }));
452
- }
453
- lines.push(translate(
454
- t,
455
- 'clear.context.cleared',
456
- 'Previous context cleared: this is a fresh session without the old conversation, system prompt, or time readings.',
457
- ));
432
+ if (timeText !== '') lines.push(`Time: ${timeText}`);
433
+ lines.push(FRESH_SESSION_NOTE);
458
434
  return `<${FRESH_SESSION_CONTEXT_TAG}>\n${lines.join('\n')}\n</${FRESH_SESSION_CONTEXT_TAG}>`;
459
435
  }
460
436
 
461
437
  /**
462
438
  * Resolve every part of the fresh-session first message from the switches.
463
439
  *
464
- * This is the single funnel shared by `composeFreshPrompt`,
465
- * `describeFreshPrompt`, and (through `assembleFreshPrompt`) the legacy
466
- * `buildPlanExecutionPrompt`, so the preview text and the sent text cannot
467
- * drift apart.
440
+ * The single funnel shared by `composeFreshPrompt` and `describeFreshPrompt`,
441
+ * so the preview text and the sent text cannot drift apart.
468
442
  */
469
443
  function resolveFreshPromptParts({
470
444
  plan,
471
- t,
472
445
  presetId,
473
446
  instructions,
474
447
  workspaceDirectory,
@@ -481,18 +454,17 @@ window.__ModuleLoader__.load({
481
454
  const includeBudget = values.unlimitedBudget !== false;
482
455
  const includeInstructions = values.includeOriginalInstructions !== false;
483
456
  const includeMinimalContext = values.includeMinimalContext !== false && presetId === 'minimal';
484
- const intro = translate(t, 'clear.promptIntro', 'The user approved the following plan. Execute it now without re-planning.');
457
+ const intro = FRESH_PROMPT_INTRO;
485
458
  const budget = includeBudget ? `${UNLIMITED_BUDGET_REMINDER}\n\n` : '';
486
459
  const runtimeContext = includeMinimalContext
487
460
  ? `${buildMinimalRuntimeContext({
488
461
  workspaceDirectory,
489
462
  system: runtimeFacts?.system,
490
463
  time: runtimeFacts?.time,
491
- }, t)}\n\n`
464
+ })}\n\n`
492
465
  : '';
493
466
  const instructionTexts = normalizeInstructionTexts(instructions);
494
467
  return {
495
- t,
496
468
  intro,
497
469
  budget,
498
470
  runtimeContext,
@@ -504,14 +476,13 @@ window.__ModuleLoader__.load({
504
476
 
505
477
  /** Assemble the six first-message blocks; the only place the text is built. */
506
478
  function assembleFreshPrompt({
507
- t,
508
479
  intro,
509
480
  budget,
510
481
  runtimeContext,
511
482
  carriedInstructions,
512
483
  planBody,
513
484
  }) {
514
- const instructionBlock = buildInstructionBlock(carriedInstructions, t);
485
+ const instructionBlock = buildInstructionBlock(carriedInstructions);
515
486
  const planBlock = `<approved-plan>\n${planBody}\n</approved-plan>`;
516
487
  const handoffBlock = `\n\n${PLAN_APPROVED_HANDOFF}`;
517
488
  return {
@@ -522,32 +493,6 @@ window.__ModuleLoader__.load({
522
493
  };
523
494
  }
524
495
 
525
- /**
526
- * Compose the legacy execution prompt from explicit parts.
527
- *
528
- * Kept byte-identical to the previous implementation; it now funnels
529
- * through the shared assembler.
530
- */
531
- function buildPlanExecutionPrompt(plan, t, options = {}) {
532
- const config = typeof options === 'boolean' ? { unlimitedBudget: options } : (options ?? {});
533
- const planBody = typeof plan === 'string' ? plan.trim() : '';
534
- if (planBody === '') throw new ActionError('emptyPlan');
535
- const intro = translate(t, 'clear.promptIntro', 'The user approved the following plan. Execute it now without re-planning.');
536
- const budget = config.unlimitedBudget === false ? '' : `${UNLIMITED_BUDGET_REMINDER}\n\n`;
537
- const runtimeContext = typeof config.runtimeContext === 'string' && config.runtimeContext.trim() !== ''
538
- ? `${config.runtimeContext.trim()}\n\n`
539
- : '';
540
- return assembleFreshPrompt({
541
- t,
542
- intro,
543
- budget,
544
- runtimeContext,
545
- carriedInstructions: normalizeInstructionTexts(config.instructions),
546
- planBody,
547
- }).text;
548
- }
549
-
550
- /** Compose one fresh-session prompt from the preset and switch state. */
551
496
  function composeFreshPrompt(args = {}) {
552
497
  return assembleFreshPrompt(resolveFreshPromptParts(args)).text;
553
498
  }
@@ -615,7 +560,6 @@ window.__ModuleLoader__.load({
615
560
  };
616
561
  }
617
562
 
618
- /** Remove inline markdown emphasis and code markers from one preview line. */
619
563
  function stripInlineMarkup(text) {
620
564
  return text
621
565
  .replace(/`+/g, '')
@@ -624,12 +568,10 @@ window.__ModuleLoader__.load({
624
568
  .replace(/[*_]/g, '');
625
569
  }
626
570
 
627
- /** Collapse every whitespace run to one space and trim. */
628
571
  function collapseWhitespace(text) {
629
572
  return text.replace(/\s+/g, ' ').trim();
630
573
  }
631
574
 
632
- /** Whether one line is an ATX markdown heading. */
633
575
  function isHeadingLine(line) {
634
576
  return /^\s{0,3}#{1,6}\s+\S/.test(line);
635
577
  }
@@ -725,10 +667,11 @@ window.__ModuleLoader__.load({
725
667
  const result = await ctx.remote.commands.execute(sessionId, COMPACT_COMMAND, [], signal);
726
668
  if (!result?.ok) {
727
669
  const failure = result?.error;
728
- const message = failure !== undefined && failure !== null
729
- ? `${failure.message} (${failure.code})`
730
- : 'The /great-plan-compact command failed.';
731
- const code = typeof failure?.code === 'string' ? failure.code : undefined;
670
+ if (failure === undefined || failure === null) {
671
+ return { ok: false, message: 'The /great-plan-compact command failed.' };
672
+ }
673
+ const code = typeof failure.code === 'string' ? failure.code : undefined;
674
+ const message = formatRpcFailure(failure);
732
675
  return code === undefined ? { ok: false, message } : { ok: false, message, code };
733
676
  }
734
677
  if (result.value === undefined) return { ok: false, message: COMPACT_UNAVAILABLE_MESSAGE };
@@ -843,7 +786,6 @@ window.__ModuleLoader__.load({
843
786
  title,
844
787
  titleSuffix,
845
788
  signal,
846
- t,
847
789
  readPromptSwitches,
848
790
  runtimeFacts,
849
791
  instructions,
@@ -888,7 +830,6 @@ window.__ModuleLoader__.load({
888
830
  : undefined;
889
831
  const promptText = composeFreshPrompt({
890
832
  plan: review?.plan,
891
- t,
892
833
  presetId,
893
834
  instructions: instructionTexts,
894
835
  workspaceDirectory: workspaceDirectory ?? cwd,
@@ -917,7 +858,7 @@ window.__ModuleLoader__.load({
917
858
  throw new ActionError('modelApplyFailed', { message: messageOf(error) });
918
859
  }
919
860
  if (!selected?.ok) {
920
- throw new ActionError('modelApplyFailed', { message: rpcFailureMessage(selected) });
861
+ throw new ActionError('modelApplyFailed', { message: formatRpcFailure(selected) });
921
862
  }
922
863
  })();
923
864
  await Promise.all([renameTask, selectionTask]);
@@ -933,7 +874,7 @@ window.__ModuleLoader__.load({
933
874
  throw new ActionError('startSessionFailed', { message: messageOf(error) });
934
875
  }
935
876
  if (!prompt?.ok) {
936
- throw new ActionError('startSessionFailed', { message: rpcFailureMessage(prompt) });
877
+ throw new ActionError('startSessionFailed', { message: formatRpcFailure(prompt) });
937
878
  }
938
879
  });
939
880
  } catch (error) {
@@ -974,13 +915,6 @@ window.__ModuleLoader__.load({
974
915
  return { sessionId: newSessionId, cancelError };
975
916
  }
976
917
 
977
- /** Render one RPC failure detail as `message (code)`. */
978
- function rpcFailureMessage(result) {
979
- const detail = result?.error;
980
- if (detail === undefined || detail === null) return 'unknown error';
981
- return `${detail.message} (${detail.code})`;
982
- }
983
-
984
918
  function createActions(ctx, sessionId, readPromptSwitches) {
985
919
  return {
986
920
  approveReview,
@@ -993,11 +927,11 @@ window.__ModuleLoader__.load({
993
927
  }
994
928
 
995
929
  return {
996
- messageOf,
930
+ FRESH_PROMPT_INTRO,
997
931
  UNLIMITED_BUDGET_REMINDER,
932
+ PLAN_MODE_INSTRUCTIONS_HEADING,
998
933
  PLAN_APPROVED_HANDOFF,
999
934
  PLAN_MODE_INSTRUCTIONS_TAG,
1000
- FRESH_SESSION_CONTEXT_TAG,
1001
935
  FRESH_PROMPT_WARN_CHARS,
1002
936
  ACTION_ERROR_CODES,
1003
937
  ACTION_ERROR_MESSAGES,
@@ -1008,13 +942,11 @@ window.__ModuleLoader__.load({
1008
942
  describeFreshPrompt,
1009
943
  planPreviewText,
1010
944
  normalizePromptSwitches,
1011
- textOf,
1012
945
  findPlanModeStart,
1013
946
  findTurnStart,
1014
947
  collectPlanModeInstructions,
1015
948
  readPlanModeInstructions,
1016
949
  readSystemName,
1017
- resolveTimeZone,
1018
950
  formatTimestamp,
1019
951
  readRuntimeFacts,
1020
952
  buildMinimalRuntimeContext,
@@ -1025,7 +957,7 @@ window.__ModuleLoader__.load({
1025
957
  createFreshExecution,
1026
958
  createActions,
1027
959
  composeFreshTitle,
1028
- buildPlanExecutionPrompt,
960
+ formatRpcFailure,
1029
961
  };
1030
962
  },
1031
963
  });
@@ -66,12 +66,6 @@ window.__ModuleLoader__.load({
66
66
  'clear.confirm': '创建新会话并执行',
67
67
  'clear.kept': '旧会话会保留,可在列表中重新打开。',
68
68
  'clear.titleSuffix': ' · 执行计划',
69
- 'clear.promptIntro': '用户已批准以下计划。请立即开始执行,不要重新规划。遵循计划中的证据与交接:复用已经确认的调研结果,不要重复调查;对标记为未验证或假设的内容先做核实。',
70
- 'clear.instructionsIntro': '本次 plan 模式期间用户发送的指令(按时间顺序,作为已批准计划的背景):',
71
- 'clear.context.workspace': '工作区目录:{path}',
72
- 'clear.context.system': '系统:{system}',
73
- 'clear.context.time': '时间:{time}',
74
- 'clear.context.cleared': '旧上下文已清空:这是一个新会话,不包含旧会话的对话、系统提示或时间读数。',
75
69
  'preview.title': '首条消息预览',
76
70
  'preview.plan': '已批准的计划',
77
71
  'preview.viewPlan': '查看完整计划',
@@ -205,12 +199,6 @@ window.__ModuleLoader__.load({
205
199
  'clear.confirm': 'Create session and execute',
206
200
  'clear.kept': 'The previous session is kept and can be reopened from the list.',
207
201
  'clear.titleSuffix': ' · Execute plan',
208
- 'clear.promptIntro': 'The user approved the following plan. Execute it now without re-planning. Follow the plan\'s evidence and handoff: reuse what it already establishes instead of repeating the investigation, and verify what it marks as unverified or assumed.',
209
- 'clear.instructionsIntro': 'User instructions sent during plan mode, in chronological order (background for the approved plan):',
210
- 'clear.context.workspace': 'Workspace directory: {path}',
211
- 'clear.context.system': 'System: {system}',
212
- 'clear.context.time': 'Time: {time}',
213
- 'clear.context.cleared': 'Previous context cleared: this is a fresh session without the old conversation, system prompt, or time readings.',
214
202
  'preview.title': 'First-message preview',
215
203
  'preview.plan': 'Approved plan',
216
204
  'preview.viewPlan': 'View full plan',