clearai-dsh 0.1.1 → 0.1.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.
- package/CHANGELOG.md +17 -0
- package/package.json +1 -1
- package/presets/clearai/plugins/clearai-kernel.js +103 -15
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project are recorded here. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
4
4
|
|
|
5
|
+
## [0.1.2] — 2026-09-12
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- **A gate could be impossible to open.** The plan review is a real gate — only a person's approval writes the authorisation mark, and the kernel refuses to start work without it. But the review card was raised **only** when a plan was created, so after a person chose *revise first, then resubmit*, the model revised the plan and there was **no entry point left** to present it again. The plan stayed unauthorised while the kernel correctly refused to work: a mechanism turned into a dead end.
|
|
10
|
+
|
|
11
|
+
Now `AmendPlan` and `RefinePlan` present an unauthorised plan again automatically, and the new **`RequestPlanReview`** tool is an explicit entry point for the model or a person to re-present it. Only approval writes the mark; every other outcome still writes nothing.
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- `RequestPlanReview` — re-present the current plan for review without changing anything. Returns `already_confirmed` when the plan is already authorised.
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
|
|
19
|
+
- **The continuation window no longer shows a machine id.** The native goal chip displayed `ClearAI 续跑窗口 · 目标 g-…` — a mechanism word plus an id, on a surface the platform renders for people. It now reads as a sentence about the work (`继续做完:<what you asked for>`). The window's identity is no longer the text: ownership is tracked in the ledger, and a change of wording goes through `goals.edit`, which **does not touch the round budget**, so revising a goal still cannot refresh it.
|
|
20
|
+
|
|
5
21
|
## [0.1.1] — 2026-09-12
|
|
6
22
|
|
|
7
23
|
### Fixed
|
|
@@ -33,5 +49,6 @@ The first release: ClearAI as a native DSH plugin.
|
|
|
33
49
|
- **Restart `dsh web` after installing**: both halves are cached in the running process, so a browser refresh is not enough.
|
|
34
50
|
- Known gaps — what is deliberately not implemented, and what has only been verified to a stated depth — are listed in [`docs/known-gaps.md`](docs/known-gaps.md).
|
|
35
51
|
|
|
52
|
+
[0.1.2]: https://github.com/Clearailhc/clearai-dsh/releases/tag/v0.1.2
|
|
36
53
|
[0.1.1]: https://github.com/Clearailhc/clearai-dsh/releases/tag/v0.1.1
|
|
37
54
|
[0.1.0]: https://github.com/Clearailhc/clearai-dsh/releases/tag/v0.1.0
|
package/package.json
CHANGED
|
@@ -473,7 +473,7 @@ export const CONFIG_KEYS = [
|
|
|
473
473
|
*/
|
|
474
474
|
export const MECHANISM_TOOLS = {
|
|
475
475
|
goal: ['SetGoal', 'CloseGoal'],
|
|
476
|
-
plan: ['CreatePlan', 'CheckPlan', 'AmendPlan', 'RefinePlan', 'VoidPlanStep', 'ClosePlan', 'AdvancePlan'],
|
|
476
|
+
plan: ['CreatePlan', 'CheckPlan', 'RequestPlanReview', 'AmendPlan', 'RefinePlan', 'VoidPlanStep', 'ClosePlan', 'AdvancePlan'],
|
|
477
477
|
worldline: ['ForkPlan', 'AdvanceWorldline', 'ConvergeFork', 'WorldlineStatus', 'AwaitWorldlines', 'AbandonFork'],
|
|
478
478
|
scout: ['SpawnScout', 'MapScouts'],
|
|
479
479
|
/** 外脑:写侧两件(读侧全走宿主原生的技能目录与 `skill` 工具)。 */
|
|
@@ -1948,14 +1948,31 @@ export function apply(ctx, config = {}) {
|
|
|
1948
1948
|
* 两处的字面值由两侧的测试各钉一遍(内核这一份 + 客户端那一份),漂移会当场红。
|
|
1949
1949
|
*/
|
|
1950
1950
|
/** 窗口身份里**不再写档位**(§34):「多问我 / 自己跑」不是用户的配置,是运行时状态。 */
|
|
1951
|
+
/** 一句话摘要:压平空白、超长截断(平台上那句给人看的话用它;内核里没有客户端的 `brief`)。 */
|
|
1952
|
+
function clip(text, max) {
|
|
1953
|
+
const flat = String(text ?? '').replace(/\s+/g, ' ').trim()
|
|
1954
|
+
return flat.length <= max ? flat : `${flat.slice(0, max - 1)}…`
|
|
1955
|
+
}
|
|
1951
1956
|
function windowTarget(state, derived) {
|
|
1952
1957
|
const goal = state.goal
|
|
1953
|
-
if (goal !== null && goal.status === 'open') return
|
|
1954
|
-
if (derived.activePlan !== null) return
|
|
1955
|
-
return '
|
|
1958
|
+
if (goal !== null && goal.status === 'open') return `继续做完:${clip(goal.claim, 26)}`
|
|
1959
|
+
if (derived.activePlan !== null) return `继续推进:${clip(derived.activePlan.brief ?? derived.activePlan.id, 26)}`
|
|
1960
|
+
return '继续把手上这一步做完'
|
|
1956
1961
|
}
|
|
1962
|
+
/**
|
|
1963
|
+
* 窗口上那句**给人看的话**(§17.1 的第二次修正,2026-09-12 实测)。
|
|
1964
|
+
*
|
|
1965
|
+
* 原来这里是 `ClearAI 续跑窗口 · 目标 g-mtyirobr2y2l`:一个机制词加一串机器 id。
|
|
1966
|
+
* 而这句是**印在平台面板上、给人看**的 —— 人该看到"在做什么",不是我们在内部怎么称呼它。
|
|
1967
|
+
* (同一屏上还并排着原生那句「进行中的目标」,机制词叠机制词。)
|
|
1968
|
+
*
|
|
1969
|
+
* 换成人话之后,身份就不能再靠"文本相等"判了(目标一改口径文本就变),于是:
|
|
1970
|
+
* · **归属**仍看我们账上记的那一枚(`before.goal === current.id`)与"这句还是不是我们写的";
|
|
1971
|
+
* · 文本变了走 **`goals.edit`** —— 改这句话**不动轮数**,所以反复修订刷不出预算;
|
|
1972
|
+
* · 只有额度(`maxGoalRounds`)变了才换窗口(那是授权变了)。
|
|
1973
|
+
*/
|
|
1957
1974
|
function windowObjective(target) {
|
|
1958
|
-
return
|
|
1975
|
+
return target
|
|
1959
1976
|
}
|
|
1960
1977
|
|
|
1961
1978
|
function continuationView(agent) {
|
|
@@ -2038,10 +2055,19 @@ export function apply(ctx, config = {}) {
|
|
|
2038
2055
|
* 是我们的 ⇒ 身份可以换;不是我们的 ⇒ 以人为准,一个字都不动(§17.2)。
|
|
2039
2056
|
*/
|
|
2040
2057
|
const ours = before !== null && before.label !== null && before.label !== undefined && before.label === current.objective
|
|
2041
|
-
|
|
2058
|
+
// 额度变了 = 授权变了 ⇒ 换一枚新窗口(清 + 建,按宿主契约)。
|
|
2059
|
+
if (ours && current.maxGoalRounds !== maxGoalRounds) {
|
|
2042
2060
|
goals.clear(agent, { id: current.id, revision: current.revision })
|
|
2043
2061
|
const view = goals.create(agent, { objective, maxGoalRounds })
|
|
2044
|
-
return said(`\n续跑窗口已换新(
|
|
2062
|
+
return said(`\n续跑窗口已换新(额度变成 ${windowLabel(view.maxGoalRounds)})。`, armed(view.id, view.objective))
|
|
2063
|
+
}
|
|
2064
|
+
/**
|
|
2065
|
+
* 台上那句话该更新了(服务对象换了口径 / 换了对象)⇒ **改这句话,不换窗口**。
|
|
2066
|
+
* `edit` 不动轮数,所以"反复修订刷新预算"这条捷径不成立。
|
|
2067
|
+
*/
|
|
2068
|
+
if (ours && current.objective !== objective) {
|
|
2069
|
+
goals.edit(agent, { id: current.id, revision: current.revision }, { objective })
|
|
2070
|
+
return said('', armed(current.id, objective))
|
|
2045
2071
|
}
|
|
2046
2072
|
if (before !== null && before.state === 'armed' && before.goal === current.id) return ''
|
|
2047
2073
|
// 账没跟上(旧会话升级上来、或上一次落账没发出去):补一条,不碰平台对象。
|
|
@@ -2251,6 +2277,37 @@ export function apply(ctx, config = {}) {
|
|
|
2251
2277
|
}
|
|
2252
2278
|
}
|
|
2253
2279
|
|
|
2280
|
+
/**
|
|
2281
|
+
* 请人审阅**已经立起来的**计划,并按结果落授权记号。
|
|
2282
|
+
*
|
|
2283
|
+
* 为什么必须有这条路:`CreatePlan` 会请人审阅,但**改完不会再请** —— 而审阅卡上那句
|
|
2284
|
+
* 「改完再呈一次」正是我们承诺的。2026-09-12 实测的死胡同:人在审阅里选了「先改再交」,
|
|
2285
|
+
* 模型照意见改了计划,然后**没有任何入口**能再呈一次 ⇒ 计划永远停在未授权,
|
|
2286
|
+
* 而内核又如实拒绝开工。**打不开的门比没有门更糟**:它把机制变成死胡同。
|
|
2287
|
+
*
|
|
2288
|
+
* 尺子与 CreatePlan 完全一样:只有 approved 才落记号,其余三种结局一个字都不落。
|
|
2289
|
+
*/
|
|
2290
|
+
async function reviewExistingPlan(plan, exec, mutations, stepsOverride) {
|
|
2291
|
+
const steps = stepsOverride ?? plan.steps
|
|
2292
|
+
const review = await requestPlanReview(
|
|
2293
|
+
exec.agent,
|
|
2294
|
+
renderPlanForReview(
|
|
2295
|
+
plan.id,
|
|
2296
|
+
plan.brief ?? '',
|
|
2297
|
+
null,
|
|
2298
|
+
steps.map((step) => ({ id: step.id, do: step.do, artifacts: step.artifacts ?? [], done_criteria: step.done_criteria })),
|
|
2299
|
+
),
|
|
2300
|
+
exec.signal,
|
|
2301
|
+
)
|
|
2302
|
+
if (review.outcome === 'approved') {
|
|
2303
|
+
mutations.push({ t: 'plan/confirmed', plan: plan.id, by: 'user', at: new Date().toISOString() })
|
|
2304
|
+
return { confirmed: true, note: '\n人在审阅里**批准**了这份计划——授权记号已落账,可以开工。' }
|
|
2305
|
+
}
|
|
2306
|
+
if (review.outcome === 'declined') return { confirmed: false, note: `\n人又一次选择**先改再交**${review.note === '' ? '' : `,他的意见:${review.note}`}——仍未授权,按意见再改。` }
|
|
2307
|
+
if (review.outcome === 'cancelled') return { confirmed: false, note: '\n人把审阅撤下、改为先说话:仍未授权,等他的下一步指令。' }
|
|
2308
|
+
return { confirmed: false, note: `\n(这份计划还没有得到人的授权:${review.note}。**不要开工**——如实停下等人。)` }
|
|
2309
|
+
}
|
|
2310
|
+
|
|
2254
2311
|
/** 给人审阅的计划正文(markdown)。原生审阅界面渲染它,所以它得是人读得懂的一份计划。 */
|
|
2255
2312
|
function renderPlanForReview(planId, brief, goal, steps) {
|
|
2256
2313
|
const lines = [`# 计划:${typeof brief === 'string' && brief.trim() !== '' ? brief.trim() : planId}`]
|
|
@@ -2481,7 +2538,8 @@ export function apply(ctx, config = {}) {
|
|
|
2481
2538
|
`${isRevision ? `目标已修订到 rev${revision}` : `目标已立(${goalId})`},登记 ${hypotheses.length} 条假设。${scoutNote}` +
|
|
2482
2539
|
applyContinuationPolicy(exec.agent, state, hostService.derive(sessionId), {
|
|
2483
2540
|
goalOpen: true,
|
|
2484
|
-
|
|
2541
|
+
// 平台上那句话(给人看)用**目标的主张**说,不写 id;身份与额度由账上那枚窗口负责。
|
|
2542
|
+
target: `继续做完:${clip(String(args.claim ?? ''), 26)}`,
|
|
2485
2543
|
mutations,
|
|
2486
2544
|
}),
|
|
2487
2545
|
})
|
|
@@ -2779,12 +2837,14 @@ export function apply(ctx, config = {}) {
|
|
|
2779
2837
|
const problem = validateSteps([args.step])
|
|
2780
2838
|
if (problem !== null) return fail('invalid_step', problem)
|
|
2781
2839
|
if (plan.steps.some((step) => step.id === args.step.id)) return fail('duplicate_step', `步骤 id 已存在:${args.step.id}`)
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2840
|
+
const amended = { id: args.step.id, do: args.step.do, artifacts: args.step.artifacts ?? [], done_criteria: args.step.done_criteria, tests: args.step.tests ?? null }
|
|
2841
|
+
mutations.push({ t: 'plan/amended', plan: plan.id, step: amended })
|
|
2842
|
+
/**
|
|
2843
|
+
* **没授权的计划:改完再呈一次**(审阅卡上承诺的就是这句)。
|
|
2844
|
+
* 已经授权的计划不再打扰人 —— 补一步不是重新立约。
|
|
2845
|
+
*/
|
|
2846
|
+
const again = plan.confirmed_at === null ? await reviewExistingPlan(plan, exec, mutations, [...plan.steps, amended]) : { note: '' }
|
|
2847
|
+
return done({ ok: true, code: 'plan_amended', progress_changed: false, message: `已补一步 ${args.step.id}(进度不变)。${again.note}` })
|
|
2788
2848
|
},
|
|
2789
2849
|
})
|
|
2790
2850
|
|
|
@@ -2813,7 +2873,35 @@ export function apply(ctx, config = {}) {
|
|
|
2813
2873
|
const selfRef = SELF_REFERENCE.find(([pattern]) => pattern.test(criteria))
|
|
2814
2874
|
if (selfRef !== undefined) return fail('criteria_self_reference', selfRef[1])
|
|
2815
2875
|
mutations.push({ t: 'plan/refined', plan: plan.id, step: step.id, old_criteria: step.done_criteria, new_criteria: criteria, reason: args.reason ?? null })
|
|
2816
|
-
|
|
2876
|
+
const refinedSteps = plan.steps.map((item) => (item.id === step.id ? { ...item, done_criteria: criteria } : item))
|
|
2877
|
+
const againRefined = plan.confirmed_at === null ? await reviewExistingPlan(plan, exec, mutations, refinedSteps) : { note: '' }
|
|
2878
|
+
return done({ ok: true, code: 'plan_refined', progress_changed: false, message: `步骤 ${step.id} 的判据已精化(进度不变,旧判据留痕)。${againRefined.note}` })
|
|
2879
|
+
},
|
|
2880
|
+
})
|
|
2881
|
+
|
|
2882
|
+
defineTool({
|
|
2883
|
+
name: 'RequestPlanReview',
|
|
2884
|
+
description:
|
|
2885
|
+
'把**当前这份计划**再呈给人审阅一次(不改任何东西)。计划还没获授权、而你已经按人的意见改完时用它——审阅卡不会自己回来,必须有人再呈一次。人批准 ⇒ 授权记号落账,可以开工;否则一个字都不落,如实停下。已经授权的计划不必再问。',
|
|
2886
|
+
parameters: { type: 'object', properties: {}, additionalProperties: false },
|
|
2887
|
+
output: CARD_OUTPUT,
|
|
2888
|
+
async execute(_args, exec) {
|
|
2889
|
+
const call = open(exec)
|
|
2890
|
+
if (call.ok !== true) return call.response
|
|
2891
|
+
const { hostService, sessionId, state, mutations } = call
|
|
2892
|
+
const done = finish(hostService, sessionId, mutations)
|
|
2893
|
+
const plan = activePlanOf(state)
|
|
2894
|
+
if (plan === null) return fail('no_active_plan', '没有活动计划。')
|
|
2895
|
+
if (plan.confirmed_at !== null) {
|
|
2896
|
+
return done({ ok: true, code: 'already_confirmed', progress_changed: false, message: `计划 ${plan.id} 已经获授权(${plan.confirmed_by ?? 'user'}),不必再问。` })
|
|
2897
|
+
}
|
|
2898
|
+
const review = await reviewExistingPlan(plan, exec, mutations)
|
|
2899
|
+
return done({
|
|
2900
|
+
ok: true,
|
|
2901
|
+
code: review.confirmed ? 'plan_confirmed' : 'plan_review_pending',
|
|
2902
|
+
progress_changed: false,
|
|
2903
|
+
message: review.confirmed ? `计划 ${plan.id} 已获授权。${review.note}` : `计划 ${plan.id} 仍未授权。${review.note}`,
|
|
2904
|
+
})
|
|
2817
2905
|
},
|
|
2818
2906
|
})
|
|
2819
2907
|
|