@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 +10 -7
- package/README.zh-CN.md +10 -7
- package/{client.actions.js → lib/client.actions.js} +58 -126
- package/{client.js → lib/client.js} +0 -12
- package/{client.panel.js → lib/client.panel.js} +189 -337
- package/{client.preset.js → lib/client.preset.js} +7 -19
- package/{client.settings.js → lib/client.settings.js} +3 -61
- package/{client.ui.js → lib/client.ui.js} +1 -7
- package/{host → lib/host}/apply.js +2 -1
- package/{host → lib/host}/plan-compaction.js +49 -10
- package/lib/host/plan-evidence.js +195 -0
- package/{host → lib/host}/plan-range.js +5 -2
- package/package.json +8 -7
- package/host/plan-evidence.js +0 -252
- /package/{client.model.js → lib/client.model.js} +0 -0
- /package/{host → lib/host}/config.js +0 -0
- /package/{index.js → lib/index.js} +0 -0
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.
|
|
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 +
|
|
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/`, `
|
|
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.
|
|
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(语法检查 +
|
|
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/`、`
|
|
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
|
-
/**
|
|
10
|
-
|
|
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
|
|
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
|
-
/**
|
|
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
|
-
|
|
288
|
-
function buildInstructionBlock(texts, t) {
|
|
283
|
+
function buildInstructionBlock(texts) {
|
|
289
284
|
if (texts.length === 0) return '';
|
|
290
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
*
|
|
465
|
-
*
|
|
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 =
|
|
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
|
-
}
|
|
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
|
|
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
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
const code = typeof failure
|
|
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:
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
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',
|