@qlzt/dsh-tool-goal 0.0.0-stage → 0.2.1-alpha.3
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/LICENSE +21 -0
- package/README.i18n.yaml +79 -0
- package/README.md +161 -2
- package/README.zh.md +162 -0
- package/lib/index.js +379 -0
- package/lib/types/authority.d.ts +40 -0
- package/lib/types/index.d.ts +27 -0
- package/lib/types/wrapup.d.ts +12 -0
- package/package.json +54 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 DeepSeek
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.i18n.yaml
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# Bilingual-pair consistency record for README.md (docs/i18n/README.md): per heading
|
|
2
|
+
# section, a hash of its English and Chinese blocks outside code blocks and generated regions.
|
|
3
|
+
# After editing either side, bring the other along and re-record with:
|
|
4
|
+
# pnpm run verify-translation-pairing --write packages/goal/tool-goal/README.md
|
|
5
|
+
/:
|
|
6
|
+
en: 1965bb8d690c2163
|
|
7
|
+
zh: ccbb80e24190cb70
|
|
8
|
+
/deepseek-ai-dsh-tool-goal:
|
|
9
|
+
en: 942b1a25c527fe81
|
|
10
|
+
zh: f688d651470c7c5a
|
|
11
|
+
/deepseek-ai-dsh-tool-goal/summary:
|
|
12
|
+
en: e949bc54b6e81b98
|
|
13
|
+
zh: 728896898f55353f
|
|
14
|
+
/deepseek-ai-dsh-tool-goal/table-of-contents:
|
|
15
|
+
en: d152484eb41ac6b4
|
|
16
|
+
zh: 09388d293f9be9cb
|
|
17
|
+
/deepseek-ai-dsh-tool-goal/use-this-package:
|
|
18
|
+
en: 0a5939086aa67be0
|
|
19
|
+
zh: c97e18432e623631
|
|
20
|
+
/deepseek-ai-dsh-tool-goal/use-this-package/tools:
|
|
21
|
+
en: 1f9ad059567a448a
|
|
22
|
+
zh: d3f70b470e9317c4
|
|
23
|
+
/deepseek-ai-dsh-tool-goal/use-this-package/configure-it:
|
|
24
|
+
en: e3f5e8d634a488b7
|
|
25
|
+
zh: 04dcf916aa2114d2
|
|
26
|
+
/deepseek-ai-dsh-tool-goal/use-this-package/authority-rules:
|
|
27
|
+
en: a00f0dac577c3e9e
|
|
28
|
+
zh: 5dedb3dfcce449bc
|
|
29
|
+
/deepseek-ai-dsh-tool-goal/understand-the-implementation:
|
|
30
|
+
en: 4e9fdb5a89f4baa3
|
|
31
|
+
zh: 50d182467ae1f3a7
|
|
32
|
+
/deepseek-ai-dsh-tool-goal/understand-the-implementation/design:
|
|
33
|
+
en: a30f36299c8107f7
|
|
34
|
+
zh: 65e6e1e19aeb5281
|
|
35
|
+
/deepseek-ai-dsh-tool-goal/understand-the-implementation/source-map:
|
|
36
|
+
en: 4d5c648787409868
|
|
37
|
+
zh: b4938bfe660f18aa
|
|
38
|
+
/deepseek-ai-dsh-tool-goal/understand-the-implementation/tool-output:
|
|
39
|
+
en: 79e54f40e0bfbe2b
|
|
40
|
+
zh: 895f7ba22749bc07
|
|
41
|
+
/deepseek-ai-dsh-tool-goal/further-exploration:
|
|
42
|
+
en: 870f9cbbc201e7f1
|
|
43
|
+
zh: 6fa3e87efd10940b
|
|
44
|
+
/deepseek-ai-dsh-tool-goal/model-experience:
|
|
45
|
+
en: 215e7ba838619b7b
|
|
46
|
+
zh: a311da3843709f90
|
|
47
|
+
/deepseek-ai-dsh-tool-goal/model-experience/system-prompt:
|
|
48
|
+
en: cf78b9dd5596495f
|
|
49
|
+
zh: bd44560f8c2435e8
|
|
50
|
+
/deepseek-ai-dsh-tool-goal/model-experience/system-prompt/what-the-model-sees:
|
|
51
|
+
en: c4ddc481746664fc
|
|
52
|
+
zh: f86e0ba3bb0043be
|
|
53
|
+
/deepseek-ai-dsh-tool-goal/model-experience/system-prompt/what-the-model-sees/goal-policy:
|
|
54
|
+
en: 452e35a67b130e54
|
|
55
|
+
zh: ad6030b6cb0eaabc
|
|
56
|
+
/deepseek-ai-dsh-tool-goal/model-experience/system-prompt/token-effect:
|
|
57
|
+
en: 0ca47969efda70ab
|
|
58
|
+
zh: bfb6dd046182bcfe
|
|
59
|
+
/deepseek-ai-dsh-tool-goal/model-experience/system-prompt/kv-cache-effect:
|
|
60
|
+
en: 1228efe95b389486
|
|
61
|
+
zh: 0eb2e21c6ab3bc33
|
|
62
|
+
/deepseek-ai-dsh-tool-goal/model-experience/tool-schemas-and-results:
|
|
63
|
+
en: cadc497841430a21
|
|
64
|
+
zh: af4cc374d7d16917
|
|
65
|
+
/deepseek-ai-dsh-tool-goal/model-experience/tool-schemas-and-results/what-the-model-sees:
|
|
66
|
+
en: a935557ea8afce8e
|
|
67
|
+
zh: 3e2b8208d0a22672
|
|
68
|
+
/deepseek-ai-dsh-tool-goal/model-experience/tool-schemas-and-results/token-effect:
|
|
69
|
+
en: 636b06a2958786bd
|
|
70
|
+
zh: 4bb09f24ef382b3b
|
|
71
|
+
/deepseek-ai-dsh-tool-goal/model-experience/tool-schemas-and-results/kv-cache-effect:
|
|
72
|
+
en: c2d2e98ce08fb40e
|
|
73
|
+
zh: 54d278190a1a9ed5
|
|
74
|
+
/deepseek-ai-dsh-tool-goal/known-limitations-and-deferred-work:
|
|
75
|
+
en: c9b0f788467a5062
|
|
76
|
+
zh: 1a94e89fbb2e1e41
|
|
77
|
+
/deepseek-ai-dsh-tool-goal/known-limitations-and-deferred-work/dev-note:
|
|
78
|
+
en: 47101daa04549489
|
|
79
|
+
zh: 1a3babd48193c068
|
package/README.md
CHANGED
|
@@ -1,3 +1,162 @@
|
|
|
1
|
-
|
|
1
|
+
---
|
|
2
|
+
description: "The model-facing goal tools for users and maintainers choosing, composing, or debugging get_goal, create_goal, and update_goal."
|
|
3
|
+
kind: "package-reference"
|
|
4
|
+
---
|
|
2
5
|
|
|
3
|
-
|
|
6
|
+
# @deepseek-ai/dsh-tool-goal
|
|
7
|
+
|
|
8
|
+
English | [中文](README.zh.md)
|
|
9
|
+
|
|
10
|
+
## Summary
|
|
11
|
+
|
|
12
|
+
`dsh-tool-goal` lets a model read persisted goals and infer and create a long-running goal from a direct human request. Creating, editing, pausing, or resuming requires that direct request in a top-level agent turn; completing or blocking also works in an autonomous goal round. Updates require the exact goal id and revision returned by a prior read. `resume` rearms active-but-disarmed or blocked goals, while users resume durable paused goals through Web or `/goal resume`. Autonomous blocking requires the same condition for a configurable threshold of three consecutive rounds by default.
|
|
13
|
+
|
|
14
|
+
## Table of Contents
|
|
15
|
+
|
|
16
|
+
- [Use this package](#use-this-package)
|
|
17
|
+
- [Understand the implementation](#understand-the-implementation)
|
|
18
|
+
- [Further Exploration](#further-exploration)
|
|
19
|
+
- [Model Experience](#model-experience)
|
|
20
|
+
- [Known Limitations and Deferred Work](#known-limitations-and-deferred-work)
|
|
21
|
+
- [Dev Note](#dev-note)
|
|
22
|
+
|
|
23
|
+
-----
|
|
24
|
+
|
|
25
|
+
<a id="use-this-package"></a>
|
|
26
|
+
## Use this package
|
|
27
|
+
|
|
28
|
+
Mount `dsh-tool-goal` beside the goal service when the model should create and update persisted goals itself. The tools are the model-facing half of the goal surface; the `/goal` command is the human-facing half, and the continuation driver uses the same tools to complete or block goals at the end of autonomous rounds.
|
|
29
|
+
|
|
30
|
+
### Tools
|
|
31
|
+
|
|
32
|
+
All three tools return the same compact JSON — `{ goal: null }` when no goal is current, otherwise the goal's id, revision, objective, phase, rounds started, round cap, optional blocker reason, and whether continuation is armed — matching what Native callers already render.
|
|
33
|
+
|
|
34
|
+
| Tool | What it does |
|
|
35
|
+
|---|---|
|
|
36
|
+
| `get_goal()` | Reads the current goal, or `null` when none is current |
|
|
37
|
+
| `create_goal(objective, max_goal_rounds?)` | Creates one goal from a direct top-level human turn |
|
|
38
|
+
| `update_goal(goal_id, revision, action, objective?, max_goal_rounds?, blocked_reason?)` | `edit`, `pause`, `resume`, `complete`, or `blocked` on the exact goal revision |
|
|
39
|
+
|
|
40
|
+
Call `get_goal` before `update_goal` and copy the exact `goal_id` and `revision`; all calls are exclusive, so a model-ordered batch observes earlier mutations and their new revisions. Replacements belong only to `edit`; `blocked_reason` is required only for `blocked` and is persisted with the stable code `model-reported`. Strict-schema empty-string and zero fillers count as omitted, while meaningful values remain limited to their action.
|
|
41
|
+
|
|
42
|
+
### Configure it
|
|
43
|
+
|
|
44
|
+
```yaml
|
|
45
|
+
- id: tool-goal
|
|
46
|
+
name: '@deepseek-ai/dsh-tool-goal'
|
|
47
|
+
config:
|
|
48
|
+
blockedAfterConsecutiveRounds: 3
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
The value must be a positive safe integer. It supplies both the hard lower bound on model self-blocking and the number named in model guidance. The generated [configuration catalog](../../../docs/config-catalog.md#deepseek-aidsh-tool-goal) is the exhaustive source for every accepted field.
|
|
52
|
+
|
|
53
|
+
### Authority rules
|
|
54
|
+
|
|
55
|
+
The tools execute only for the exact live calling agent inside its active driver with an open turn. `create`, `edit`, `pause`, and `resume` additionally require a direct human message in a runtime-root agent's current turn — a subagent or a non-human producer cannot create or edit goals. `resume` rejects a durable paused goal before the goal service runs; that state belongs to the user-facing resume path. `complete` and `blocked` also accept the exact current goal round: a goal-sourced round may complete the goal immediately, but a blocked call is mechanically rejected until the configured number of consecutive rounds has passed — the model judges whether the same condition actually persisted and must describe it in `blocked_reason`. A direct human request may stop a goal immediately.
|
|
56
|
+
|
|
57
|
+
An autonomous round that successfully reports `complete` or `blocked` also ends the physical turn after that step, and the model receives a closing instruction to write the final message to the user. Direct-human mutations never trigger that stop: the assistant may acknowledge the change and the loop keeps concurrent human steering available.
|
|
58
|
+
|
|
59
|
+
-----
|
|
60
|
+
|
|
61
|
+
<a id="understand-the-implementation"></a>
|
|
62
|
+
## Understand the implementation
|
|
63
|
+
|
|
64
|
+
<details>
|
|
65
|
+
<summary>Implementation internals — click to expand</summary>
|
|
66
|
+
|
|
67
|
+
This section explains how the tools enforce authority and render output; the observable contract is covered in [Use this package](#use-this-package).
|
|
68
|
+
|
|
69
|
+
### Design
|
|
70
|
+
|
|
71
|
+
- **Authority at execution.** Every call resolves the exact live agent, its inherited `AgentRegistry` initiator, running status, and an open turn; `create`, `edit`, `pause`, and `resume` additionally require an accepted `{ kind: 'user' }` message or steering event in a runtime-root agent's current turn. A durable paused goal fails the `resume` action with `GOAL_TOOL_RESUME_PAUSED`; the user-facing command or Web control owns that transition. Durable fork lineage does not demote a resumed root; live subagent ownership does.
|
|
72
|
+
- **Host attestation of human input.** `{ kind: 'user' }` is assigned by `Agent.followup()` and `steer()` when their caller omits a source, so plugins, schedulers, and other non-human producers must pass their own source rather than inheriting human authority.
|
|
73
|
+
- **System-prompt guidance with the configured threshold.** The package registers one `tool:goal` system-prompt section whose fixed text interpolates `blockedAfterConsecutiveRounds`; the same value is the hard lower bound enforced at execution.
|
|
74
|
+
- **Wrap-up context for terminal rounds.** A successful autonomous `complete` or `blocked` defers a closing `<goal_complete>` or `<goal_blocked>` instruction so the model addresses the user once before the turn ends; direct-human mutations never defer this context.
|
|
75
|
+
|
|
76
|
+
### Source map
|
|
77
|
+
|
|
78
|
+
| File | Role |
|
|
79
|
+
|---|---|
|
|
80
|
+
| [`src/index.ts`](src/index.ts) | Plugin entry: tool registration, config, system-prompt section, result rendering |
|
|
81
|
+
| [`src/authority.ts`](src/authority.ts) | Execution-time authority checks and goal-round acceptance |
|
|
82
|
+
| [`src/wrapup.ts`](src/wrapup.ts) | Closing-message instruction for terminal autonomous updates |
|
|
83
|
+
|
|
84
|
+
### Tool output
|
|
85
|
+
|
|
86
|
+
All three tools share one canonical output: the compact JSON `{ goal: null }` or `{ goal: { id, revision, objective, phase, roundsStarted, maxGoalRounds, blockedReason? }, activation }`. `activation` in a result is a live observation and never becomes replay authority. UI clients receive pure generic cards — read for `get_goal`, other for mutations.
|
|
87
|
+
|
|
88
|
+
</details>
|
|
89
|
+
|
|
90
|
+
-----
|
|
91
|
+
|
|
92
|
+
<a id="further-exploration"></a>
|
|
93
|
+
## Further Exploration
|
|
94
|
+
|
|
95
|
+
The tools are the model-facing half of the goal surface; read these pages for the state they mutate and the policy they defer to.
|
|
96
|
+
|
|
97
|
+
- [Goal service](../goal/README.md) — the goal state and lifecycle the tools mutate.
|
|
98
|
+
- [Goal group map](../README.md) — the goal packages and how they compose.
|
|
99
|
+
- [Generated tool catalog](../../../docs/tool-catalog.md#deepseek-aidsh-tool-goal) — the exact schemas the model receives.
|
|
100
|
+
- [Goal-tool Agent Note](../../../.agents/notes/implemented/feature/2026-07-19-model-facing-goal-tools.md) — the authority split and UX decisions.
|
|
101
|
+
|
|
102
|
+
-----
|
|
103
|
+
|
|
104
|
+
<a id="model-experience"></a>
|
|
105
|
+
## Model Experience
|
|
106
|
+
|
|
107
|
+
### System prompt
|
|
108
|
+
|
|
109
|
+
#### What the model sees
|
|
110
|
+
|
|
111
|
+
A fixed goal policy allows inferring goal intent in any language, explains rearming after resume/fork, and limits completion/blocking claims; creation scope and read-before-update refs live in the tool definitions. Durable paused resume is rejected at execution with `GOAL_TOOL_RESUME_PAUSED`; the user-facing goal control owns that transition. The configured threshold is interpolated into that guidance.
|
|
112
|
+
|
|
113
|
+
##### Goal policy
|
|
114
|
+
|
|
115
|
+
```markdown
|
|
116
|
+
create_goal may infer goal intent from a direct human request in any language. After session resume or fork, an active goal is disarmed: when a human asks to continue or resume in any wording or language, use update_goal action resume to rearm it. Mark complete only when the objective is actually achieved. Mark blocked only after the same blocking condition persists for at least 3 consecutive goal rounds, and report that concrete condition in blocked_reason; difficulty, uncertainty, or useful remaining work is not blocked.
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
#### Token effect
|
|
120
|
+
|
|
121
|
+
Small fixed input cost on every request where this plugin's prompt registration is in scope.
|
|
122
|
+
|
|
123
|
+
#### KV Cache effect
|
|
124
|
+
|
|
125
|
+
Prefix-stable while the plugin scope, configured threshold, and guidance text are unchanged. Activation, disposal, or configuration changes may invalidate reuse from this prompt section.
|
|
126
|
+
|
|
127
|
+
### Tool schemas and results
|
|
128
|
+
|
|
129
|
+
#### What the model sees
|
|
130
|
+
|
|
131
|
+
The generated [`get_goal`, `create_goal`, and `update_goal` schemas](../../../docs/tool-catalog.md#deepseek-aidsh-tool-goal). Successful results are compact JSON. A mutation appends the goal domain's durable `goal/change` event without queuing model context. `activation` in a result is a live observation and never becomes replay authority.
|
|
132
|
+
|
|
133
|
+
#### Token effect
|
|
134
|
+
|
|
135
|
+
Fixed schema cost plus one compact result per call. The durable mutation adds no separate model-visible context.
|
|
136
|
+
|
|
137
|
+
#### KV Cache effect
|
|
138
|
+
|
|
139
|
+
Schemas are prefix-stable while their definitions and visibility are unchanged. Calls and results append after the reusable request prefix without invalidating earlier entries.
|
|
140
|
+
|
|
141
|
+
## Known Limitations and Deferred Work
|
|
142
|
+
|
|
143
|
+
<a id="known-limitations-and-deferred-work"></a>
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
These limits define when the goal tools are a poor fit or need special care. They are current package constraints, not a task backlog.
|
|
147
|
+
|
|
148
|
+
- **Semantic intent remains model judgment** — execution can prove that the current turn contains a direct human message, not whether the request is substantial enough to merit a goal.
|
|
149
|
+
- **Same-condition blocking remains model judgment** — the runtime enforces distinct admitted-round count, not semantic equivalence of obstacles; an independent evaluator is deferred.
|
|
150
|
+
- **No scheduling or direct human rendering** — these tools mutate state only; the same-session driver and `dsh-command-goal` are independent consumers of the same domain.
|
|
151
|
+
- **Goal-round authority requires a driver** — the autonomous `complete`/`blocked` path is dormant unless a continuation driver admits goal-sourced user turns; mounting this tool package alone does not create them.
|
|
152
|
+
- **Prompt registration is independent of filtering** — a scope may hide the tools while retaining their guidance unless the deployment scopes both registrations together.
|
|
153
|
+
|
|
154
|
+
<a id="dev-note"></a>
|
|
155
|
+
### Dev Note
|
|
156
|
+
|
|
157
|
+
<details>
|
|
158
|
+
<summary>Working context for maintainers — click to expand</summary>
|
|
159
|
+
|
|
160
|
+
This Dev Note is working context for maintainers; it is explicitly non-authoritative. Open question: whether the goal-policy section should be independently scoped from the tool registrations, so a scope cannot hide the tools while keeping the guidance.
|
|
161
|
+
|
|
162
|
+
</details>
|
package/README.zh.md
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "面向选择、组合或排查 get_goal、create_goal 与 update_goal 的用户与维护者的模型侧 goal 工具说明。"
|
|
3
|
+
kind: "package-reference"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# @deepseek-ai/dsh-tool-goal
|
|
7
|
+
|
|
8
|
+
[English](README.md) | 中文
|
|
9
|
+
|
|
10
|
+
## 概述
|
|
11
|
+
|
|
12
|
+
`dsh-tool-goal` 让模型读取持久 goal,并根据人类直接请求推断和创建长期 goal。创建、编辑、暂停或恢复要求该直接请求出现在顶层 agent(智能体)轮次中;完成或阻塞也可以在自主 Goal Round 中执行。更新必须使用先前读取到的精确 goal id 和 revision。`resume` 会重新启用 active-but-disarmed 或 blocked 的 goal,而持久的 paused goal 由用户通过 Web 或 `/goal resume` 恢复。自主阻塞要求同一条件持续达到可配置阈值,默认是连续三个 Round。
|
|
13
|
+
|
|
14
|
+
## 目录
|
|
15
|
+
|
|
16
|
+
- [使用本包](#use-this-package)
|
|
17
|
+
- [理解实现](#understand-the-implementation)
|
|
18
|
+
- [进一步探索](#further-exploration)
|
|
19
|
+
- [模型体验](#model-experience)
|
|
20
|
+
- [已知限制与延期工作](#known-limitations-and-deferred-work)
|
|
21
|
+
- [开发备注](#dev-note)
|
|
22
|
+
|
|
23
|
+
-----
|
|
24
|
+
|
|
25
|
+
<a id="use-this-package"></a>
|
|
26
|
+
## 使用本包
|
|
27
|
+
|
|
28
|
+
当模型需要自行创建和更新持久 goal 时,把 `dsh-tool-goal` 挂在 goal 服务旁边。这些工具是 goal 表面面向模型的一半;`/goal` 命令是面向人类的一半,续行驱动器在自主 Round 结束时使用同一套工具完成或阻塞 goal。
|
|
29
|
+
|
|
30
|
+
### 工具
|
|
31
|
+
|
|
32
|
+
三个工具都返回相同的紧凑 JSON——没有当前 goal 时为 `{ goal: null }`,否则返回 goal 的 id、revision、目标、phase、已开始 Round、Round 上限、可选的 blocker reason 与续行是否已启用——与 Native 调用方已经渲染的内容一致。
|
|
33
|
+
|
|
34
|
+
| 工具 | 作用 |
|
|
35
|
+
|---|---|
|
|
36
|
+
| `get_goal()` | 读取当前 goal;没有当前 goal 时返回 `null` |
|
|
37
|
+
| `create_goal(objective, max_goal_rounds?)` | 根据人类直接发起的顶层轮次创建一个 goal |
|
|
38
|
+
| `update_goal(goal_id, revision, action, objective?, max_goal_rounds?, blocked_reason?)` | 对精确 goal revision 执行 `edit`、`pause`、`resume`、`complete` 或 `blocked` |
|
|
39
|
+
|
|
40
|
+
在 `update_goal` 之前调用 `get_goal`,并复制精确的 `goal_id` 与 `revision`;所有调用都互斥,因此模型排序的批次能观察到更早变更及其新 revision。替换值只属于 `edit`;`blocked_reason` 只有在 `blocked` 时才必填,并以稳定代码 `model-reported` 持久化。严格 schema 下的空字符串和零填充值视为省略,而有意义的值仍限定到各自 action。
|
|
41
|
+
|
|
42
|
+
### 配置
|
|
43
|
+
|
|
44
|
+
```yaml
|
|
45
|
+
- id: tool-goal
|
|
46
|
+
name: '@deepseek-ai/dsh-tool-goal'
|
|
47
|
+
config:
|
|
48
|
+
blockedAfterConsecutiveRounds: 3
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
该值必须是正的安全整数。它既提供模型自行报告阻塞的硬下限,也决定模型指引中指明的数值。生成的[配置目录](../../../docs/config-catalog.zh.md#deepseek-aidsh-tool-goal)是每个受支持字段的穷尽式真源。
|
|
52
|
+
|
|
53
|
+
### 权限规则
|
|
54
|
+
|
|
55
|
+
工具只为活跃驱动器内、处于开放轮次中的精确活跃调用 agent 执行。`create`、`edit`、`pause` 和 `resume` 还要求运行时根 agent 的当前轮次中存在人类直接消息——subagent 或非人类生产方不能创建或编辑 goal。`resume` 会在 goal 服务执行前拒绝持久的 paused goal;该状态只属于面向用户的恢复路径。`complete` 和 `blocked` 还接受完全一致的当前 Goal Round:来源为 goal 的 Round 可以立即完成 goal,但 `blocked` 调用在达到配置的连续 Round 数量之前会被机械拒绝——模型判断同一条件是否确实持续,并必须在 `blocked_reason` 中说明。人类直接请求可以立即停止 goal。
|
|
56
|
+
|
|
57
|
+
成功报告 `complete` 或 `blocked` 的自主 Round 还会在该步骤后结束物理轮次,模型会收到一条结束指令,要求向用户写出最终消息。人类直接变更绝不会触发这种停止:assistant 可以确认变更,循环仍可接收并发的人类 steering(中途引导)。
|
|
58
|
+
|
|
59
|
+
-----
|
|
60
|
+
|
|
61
|
+
<a id="understand-the-implementation"></a>
|
|
62
|
+
## 理解实现
|
|
63
|
+
|
|
64
|
+
<details>
|
|
65
|
+
<summary>实现细节——点击展开</summary>
|
|
66
|
+
|
|
67
|
+
本节解释工具如何强制执行权限并渲染输出;可观察约定已在[使用本包](#use-this-package)中说明。
|
|
68
|
+
|
|
69
|
+
### 设计
|
|
70
|
+
|
|
71
|
+
- **执行时权限。** 每次调用都解析精确活跃 agent、其继承的 `AgentRegistry` initiator、running 状态与开放轮次;`create`、`edit`、`pause` 和 `resume` 还要求运行时根 agent 的当前轮次中存在已接受的 `{ kind: 'user' }` 消息或 steering 事件。持久的 paused goal 会让 `resume` 以 `GOAL_TOOL_RESUME_PAUSED` 失败;面向用户的命令或 Web 控件拥有该转换。持久 fork 谱系不会降低已恢复根 agent 的等级;活跃 subagent 所有权会降低。
|
|
72
|
+
- **人类输入的宿主证明。** `Agent.followup()` 与 `steer()` 会在调用方省略 source 时分配 `{ kind: 'user' }`,因此插件、调度器与其他非人类生产方必须传入自己的 source,不能继承人类权限。
|
|
73
|
+
- **带配置阈值的系统提示词指引。** 本包注册一个 `tool:goal` 系统提示词章节,其固定文本插入 `blockedAfterConsecutiveRounds`;同一数值就是执行时强制执行的硬下限。
|
|
74
|
+
- **终局 Round 的结束上下文。** 成功的自主 `complete` 或 `blocked` 会延后一条 `<goal_complete>` 或 `<goal_blocked>` 结束指令,让模型在轮次结束前向用户做一次交代;人类直接变更绝不会延后该上下文。
|
|
75
|
+
|
|
76
|
+
### 源码地图
|
|
77
|
+
|
|
78
|
+
| 文件 | 职责 |
|
|
79
|
+
|---|---|
|
|
80
|
+
| [`src/index.ts`](src/index.ts) | 插件入口:工具注册、配置、系统提示词章节、结果渲染 |
|
|
81
|
+
| [`src/authority.ts`](src/authority.ts) | 执行时权限检查与 Goal Round 接受 |
|
|
82
|
+
| [`src/wrapup.ts`](src/wrapup.ts) | 终局自主更新的结束消息指令 |
|
|
83
|
+
|
|
84
|
+
### 工具输出
|
|
85
|
+
|
|
86
|
+
三个工具共用一种规范输出:紧凑 JSON `{ goal: null }`,或 `{ goal: { id, revision, objective, phase, roundsStarted, maxGoalRounds, blockedReason? }, activation }`。结果中的 `activation` 是实时观察值,绝不会成为回放权限依据。UI 客户端收到纯通用卡片——`get_goal` 为 read,变更使用 other。
|
|
87
|
+
|
|
88
|
+
</details>
|
|
89
|
+
|
|
90
|
+
-----
|
|
91
|
+
|
|
92
|
+
<a id="further-exploration"></a>
|
|
93
|
+
## 进一步探索
|
|
94
|
+
|
|
95
|
+
这些工具是 goal 表面面向模型的一半;如需了解它们变更的状态及其所遵循的策略,请阅读以下页面。
|
|
96
|
+
|
|
97
|
+
- [goal 服务](../goal/README.zh.md)——工具变更的 goal 状态与生命周期。
|
|
98
|
+
- [goal 组地图](../README.zh.md)——goal 各包及其组合方式。
|
|
99
|
+
- [生成的工具目录](../../../docs/tool-catalog.zh.md#deepseek-aidsh-tool-goal)——模型接收的精确 schema。
|
|
100
|
+
- [goal 工具 Agent Note](../../../.agents/notes/implemented/feature/2026-07-19-model-facing-goal-tools.zh.md)——权限拆分与 UX 决策。
|
|
101
|
+
|
|
102
|
+
-----
|
|
103
|
+
|
|
104
|
+
<a id="model-experience"></a>
|
|
105
|
+
## 模型体验
|
|
106
|
+
|
|
107
|
+
### 系统提示词
|
|
108
|
+
|
|
109
|
+
#### 模型看到的内容
|
|
110
|
+
|
|
111
|
+
固定 goal 策略允许以任意语言推断 goal 意图,解释会话 resume/fork 后如何重新启用续行,并限制完成/阻塞声明;创建范围和更新前读取 ref 由工具定义说明。持久 paused 的 resume 会在执行时以 `GOAL_TOOL_RESUME_PAUSED` 拒绝;面向用户的 goal 控件拥有该转换。配置的阈值会插入该指引。
|
|
112
|
+
|
|
113
|
+
##### Goal 策略
|
|
114
|
+
|
|
115
|
+
```markdown
|
|
116
|
+
create_goal may infer goal intent from a direct human request in any language. After session resume or fork, an active goal is disarmed: when a human asks to continue or resume in any wording or language, use update_goal action resume to rearm it. Mark complete only when the objective is actually achieved. Mark blocked only after the same blocking condition persists for at least 3 consecutive goal rounds, and report that concrete condition in blocked_reason; difficulty, uncertainty, or useful remaining work is not blocked.
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
#### Token 影响
|
|
120
|
+
|
|
121
|
+
此插件的提示词注册位于请求范围内时,每次请求都会产生少量固定输入成本。
|
|
122
|
+
|
|
123
|
+
#### KV Cache 影响
|
|
124
|
+
|
|
125
|
+
插件范围、配置阈值和指引文本不变时,前缀保持稳定。启用、dispose(资源释放)或配置变更可能使此提示词章节的复用失效。
|
|
126
|
+
|
|
127
|
+
### 工具 schema 与结果
|
|
128
|
+
|
|
129
|
+
#### 模型看到的内容
|
|
130
|
+
|
|
131
|
+
生成的 [`get_goal`、`create_goal` 和 `update_goal` schema](../../../docs/tool-catalog.zh.md#deepseek-aidsh-tool-goal)。成功结果是紧凑 JSON。变更会追加 goal 领域的持久 `goal/change` 事件,而不会将模型上下文加入队列。结果中的 `activation` 是实时观察值,绝不会成为回放权限依据。
|
|
132
|
+
|
|
133
|
+
#### Token 影响
|
|
134
|
+
|
|
135
|
+
固定 schema 成本,加上每次调用的一条紧凑结果。持久变更不会增加单独的模型可见上下文。
|
|
136
|
+
|
|
137
|
+
#### KV Cache 影响
|
|
138
|
+
|
|
139
|
+
schema 的定义与可见性不变时,前缀保持稳定。调用和结果会追加到可复用请求前缀之后,不会使更早条目失效。
|
|
140
|
+
|
|
141
|
+
## 已知限制与延期工作
|
|
142
|
+
|
|
143
|
+
<a id="known-limitations-and-deferred-work"></a>
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
这些限制说明 goal 工具何时不合适或需要特别注意。它们是当前包约束,不是任务积压。
|
|
147
|
+
|
|
148
|
+
- **语义意图仍由模型判断**——执行只能证明当前轮次包含一条人类直接发送的消息,无法证明请求是否足够重大而值得创建 goal。
|
|
149
|
+
- **阻塞条件是否相同仍由模型判断**——运行时强制统计互不重复的已准入 Goal Round,而不判断障碍在语义上是否等价;独立评估器的实现暂缓。
|
|
150
|
+
- **不负责调度或直接面向人类呈现**——这些工具只变更状态;同会话驱动器与 `dsh-command-goal` 是同一领域的独立消费方。
|
|
151
|
+
- **Goal Round 权限需要驱动器**——除非续行驱动器准入 goal 来源的用户轮次,否则自主 `complete`/`blocked` 路径不会启用;只挂载这个包不会创建这些轮次。
|
|
152
|
+
- **提示词注册与过滤相互独立**——某个范围可能隐藏工具,却保留指引,除非部署将两项注册限定在同一范围。
|
|
153
|
+
|
|
154
|
+
<a id="dev-note"></a>
|
|
155
|
+
### 开发备注
|
|
156
|
+
|
|
157
|
+
<details>
|
|
158
|
+
<summary>维护者的工作上下文——点击展开</summary>
|
|
159
|
+
|
|
160
|
+
本开发备注是维护者的工作上下文,明确不具权威性。开放问题:goal 策略章节是否应与工具注册独立限定范围,避免某个范围隐藏工具却保留指引。
|
|
161
|
+
|
|
162
|
+
</details>
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
import z from "@deepseek-ai/schemastery";
|
|
2
|
+
import { GoalId } from "@qlzt/dsh-goal";
|
|
3
|
+
import { HarnessError, boundContextSummary, createUserMessage } from "@qlzt/dsh-llm";
|
|
4
|
+
import { defineTool } from "@qlzt/dsh-tools";
|
|
5
|
+
//#region lib/types/authority.js
|
|
6
|
+
/** Execution-time authority checks for the model-facing goal tools. */
|
|
7
|
+
/** Throw one structured tool-policy failure. */
|
|
8
|
+
function reject(message, code = "GOAL_TOOL_AUTHORITY_REQUIRED") {
|
|
9
|
+
throw new HarnessError(message, code);
|
|
10
|
+
}
|
|
11
|
+
/** Resolve the immutable event cut and open-turn boundary without copying the turn suffix. */
|
|
12
|
+
function openTurnEvents(ctx, agent) {
|
|
13
|
+
const events = agent.session.snapshotEvents();
|
|
14
|
+
const boundary = ctx.sessionProjections.stateOf(agent.session, "turnBoundary");
|
|
15
|
+
if (boundary === void 0 || boundary.openTurnStartSeq === null) reject("goal tools require an open model turn", "GOAL_TOOL_DRIVER_REQUIRED");
|
|
16
|
+
return {
|
|
17
|
+
events,
|
|
18
|
+
openTurnStartSeq: boundary.openTurnStartSeq
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Resolve and authenticate the calling agent and its driver boundary.
|
|
23
|
+
* @param ctx - Context carrying the live agent registry.
|
|
24
|
+
* @param exec - Tool execution metadata supplied by the registry.
|
|
25
|
+
* @returns The authenticated agent, immutable event cut, and open-turn boundary.
|
|
26
|
+
*/
|
|
27
|
+
function goalToolExecution(ctx, exec) {
|
|
28
|
+
const agent = exec.agent;
|
|
29
|
+
if (agent === void 0) return reject("goal tools require a calling agent", "GOAL_TOOL_AGENT_REQUIRED");
|
|
30
|
+
if (ctx.agents.get(agent.id) !== agent || agent.status !== "running" || ctx.agents.currentInitiator() !== agent) return reject("goal tools require the exact live calling agent inside its active driver", "GOAL_TOOL_DRIVER_REQUIRED");
|
|
31
|
+
return {
|
|
32
|
+
agent,
|
|
33
|
+
...openTurnEvents(ctx, agent)
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
/** Whether the captured open turn contains an event accepted by `predicate`. */
|
|
37
|
+
function someOpenTurnEvent(execution, predicate) {
|
|
38
|
+
for (let seq = execution.openTurnStartSeq + 1; seq < execution.events.length; seq += 1) {
|
|
39
|
+
const event = execution.events[seq];
|
|
40
|
+
if (event !== void 0 && predicate(event)) return true;
|
|
41
|
+
}
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Whether host-attested human input appears in the current root-agent turn.
|
|
46
|
+
* An omitted `Agent.followup()` / `steer()` source resolves to `user`, so non-human
|
|
47
|
+
* producers must supply their own source rather than inheriting this authority.
|
|
48
|
+
*/
|
|
49
|
+
function hasDirectHumanInput(ctx, execution) {
|
|
50
|
+
if (!ctx.agents.roots().includes(execution.agent)) return false;
|
|
51
|
+
return someOpenTurnEvent(execution, (event) => event.type === "user/message" && event.data.source.kind === "user");
|
|
52
|
+
}
|
|
53
|
+
/** Whether this turn is the current goal's exact admitted round. */
|
|
54
|
+
function isMatchingGoalRound(execution, goal) {
|
|
55
|
+
return someOpenTurnEvent(execution, (event) => event.type === "user/message" && event.data.source.kind === "goal" && event.data.source.goalId === goal.id && event.data.source.revision === goal.revision && event.data.source.round === goal.roundsStarted);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Require authority originating in a human message accepted by a runtime root.
|
|
59
|
+
* @param ctx - Context carrying the live agent graph.
|
|
60
|
+
* @param execution - Authenticated current tool execution.
|
|
61
|
+
*/
|
|
62
|
+
function requireDirectHuman(ctx, execution) {
|
|
63
|
+
if (hasDirectHumanInput(ctx, execution)) return;
|
|
64
|
+
reject("this goal operation requires a direct human turn on a top-level agent");
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Resolve completion authority from either direct human input or the exact goal round.
|
|
68
|
+
* @param ctx - Context carrying live agents and goal state.
|
|
69
|
+
* @param execution - Authenticated current tool execution.
|
|
70
|
+
* @returns The direct-human or exact-goal-round authority grant.
|
|
71
|
+
*/
|
|
72
|
+
function completionAuthority(ctx, execution) {
|
|
73
|
+
if (hasDirectHumanInput(ctx, execution)) return { kind: "direct-human" };
|
|
74
|
+
const goal = ctx.goals.get(execution.agent);
|
|
75
|
+
if (goal !== void 0 && isMatchingGoalRound(execution, goal)) return {
|
|
76
|
+
kind: "goal-round",
|
|
77
|
+
goal
|
|
78
|
+
};
|
|
79
|
+
return reject("complete and blocked require a direct human turn or the current goal round");
|
|
80
|
+
}
|
|
81
|
+
//#endregion
|
|
82
|
+
//#region lib/types/wrapup.js
|
|
83
|
+
/**
|
|
84
|
+
* Render the closing-message instruction injected after an autonomous goal
|
|
85
|
+
* round reports `complete` or `blocked`, replacing the former hard turn stop
|
|
86
|
+
* so the model still addresses the user once before the turn ends.
|
|
87
|
+
* @param objective - the terminal goal's objective, echoed for grounding.
|
|
88
|
+
* @param blockedReason - the validated report for `blocked`; omitted for `complete`.
|
|
89
|
+
* @returns a fresh one-block context for `ToolRunContext.deferContext()`.
|
|
90
|
+
*/
|
|
91
|
+
function renderWrapupContext(objective, blockedReason) {
|
|
92
|
+
const heading = `Objective: ${JSON.stringify(objective)}\n`;
|
|
93
|
+
return [{
|
|
94
|
+
type: "text",
|
|
95
|
+
text: blockedReason === void 0 ? "<goal_complete>\n" + heading + "The goal is marked complete and this autonomous run is ending. Write the closing message to the user now: state the outcome, summarize what was done and how it was verified, and point to the concrete results (files, commits, or other artifacts). Report only what earlier rounds and tool results in this session actually establish; when a detail is not in the session, say so instead of inventing it. Note anything the user should review or do next. Address the user directly. Do not call any more tools in this run; further work waits for the user's next instruction.\n</goal_complete>" : "<goal_blocked>\n" + heading + `Blocked: ${JSON.stringify(blockedReason)}\nThe goal is marked blocked and this autonomous run is ending. Write the closing message to the user now: state what has been completed so far, describe the concrete blocking condition and what you tried, and say exactly what you need from the user to continue. Report only what earlier rounds and tool results in this session actually establish; when a detail is not in the session, say so instead of inventing it. Address the user directly. Do not call any more tools in this run; further work waits for the user's next instruction.
|
|
96
|
+
</goal_blocked>`
|
|
97
|
+
}];
|
|
98
|
+
}
|
|
99
|
+
//#endregion
|
|
100
|
+
//#region lib/types/index.js
|
|
101
|
+
/**
|
|
102
|
+
* Model-facing `get_goal`, `create_goal`, and `update_goal` tools over the
|
|
103
|
+
* persisted same-session goal domain.
|
|
104
|
+
* @module @qlzt/dsh-tool-goal
|
|
105
|
+
*/
|
|
106
|
+
const name = "tool-goal";
|
|
107
|
+
const inject = [
|
|
108
|
+
"agents",
|
|
109
|
+
"goals",
|
|
110
|
+
"tools",
|
|
111
|
+
"systemPrompt",
|
|
112
|
+
"sessionProjections"
|
|
113
|
+
];
|
|
114
|
+
/** Schemastery config for the goal-tool policy. */
|
|
115
|
+
const Config = z.object({ blockedAfterConsecutiveRounds: z.number().step(1).min(1).default(3) });
|
|
116
|
+
const UPDATE_ACTIONS = [
|
|
117
|
+
"edit",
|
|
118
|
+
"pause",
|
|
119
|
+
"resume",
|
|
120
|
+
"complete",
|
|
121
|
+
"blocked"
|
|
122
|
+
];
|
|
123
|
+
const CREATE_DESCRIPTION = "Create a persisted goal that keeps this session working across automatic continuation rounds. Use it when the direct human request is a long-running objective, even if the user did not say \"goal\"; not for single-turn work.";
|
|
124
|
+
const GET_DESCRIPTION = "Read the current session goal, including the id and revision that update_goal requires.";
|
|
125
|
+
const GOAL_VALUE_SCHEMA = { oneOf: [{
|
|
126
|
+
type: "object",
|
|
127
|
+
additionalProperties: false,
|
|
128
|
+
properties: { goal: {
|
|
129
|
+
type: "null",
|
|
130
|
+
required: true
|
|
131
|
+
} }
|
|
132
|
+
}, {
|
|
133
|
+
type: "object",
|
|
134
|
+
additionalProperties: false,
|
|
135
|
+
properties: {
|
|
136
|
+
goal: {
|
|
137
|
+
type: "object",
|
|
138
|
+
additionalProperties: false,
|
|
139
|
+
required: true,
|
|
140
|
+
properties: {
|
|
141
|
+
id: {
|
|
142
|
+
type: "string",
|
|
143
|
+
required: true
|
|
144
|
+
},
|
|
145
|
+
revision: {
|
|
146
|
+
type: "integer",
|
|
147
|
+
required: true
|
|
148
|
+
},
|
|
149
|
+
objective: {
|
|
150
|
+
type: "string",
|
|
151
|
+
required: true
|
|
152
|
+
},
|
|
153
|
+
phase: {
|
|
154
|
+
type: "string",
|
|
155
|
+
required: true,
|
|
156
|
+
enum: [
|
|
157
|
+
"active",
|
|
158
|
+
"paused",
|
|
159
|
+
"blocked",
|
|
160
|
+
"complete"
|
|
161
|
+
]
|
|
162
|
+
},
|
|
163
|
+
roundsStarted: {
|
|
164
|
+
type: "integer",
|
|
165
|
+
required: true
|
|
166
|
+
},
|
|
167
|
+
maxGoalRounds: {
|
|
168
|
+
type: "integer",
|
|
169
|
+
required: true
|
|
170
|
+
},
|
|
171
|
+
blockedReason: {
|
|
172
|
+
type: "object",
|
|
173
|
+
additionalProperties: false,
|
|
174
|
+
properties: {
|
|
175
|
+
code: {
|
|
176
|
+
type: "string",
|
|
177
|
+
required: true
|
|
178
|
+
},
|
|
179
|
+
message: {
|
|
180
|
+
type: "string",
|
|
181
|
+
required: true
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
activation: {
|
|
188
|
+
type: "string",
|
|
189
|
+
required: true,
|
|
190
|
+
enum: ["armed", "disarmed"]
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}] };
|
|
194
|
+
/** Render policy guidance with its deployment-selected blocked threshold. */
|
|
195
|
+
function guidance(blockedAfter) {
|
|
196
|
+
return `create_goal may infer goal intent from a direct human request in any language. After session resume or fork, an active goal is disarmed: when a human asks to continue or resume in any wording or language, use update_goal action resume to rearm it. Mark complete only when the objective is actually achieved. Mark blocked only after the same blocking condition persists for at least ${blockedAfter} consecutive goal rounds, and report that concrete condition in blocked_reason; difficulty, uncertainty, or useful remaining work is not blocked.`;
|
|
197
|
+
}
|
|
198
|
+
/** Validate config even when apply is called directly outside Loader normalization. */
|
|
199
|
+
function resolveConfig(config) {
|
|
200
|
+
const blockedAfter = config.blockedAfterConsecutiveRounds ?? 3;
|
|
201
|
+
if (!Number.isSafeInteger(blockedAfter) || blockedAfter < 1) throw new TypeError("blockedAfterConsecutiveRounds must be a positive safe integer");
|
|
202
|
+
return { blockedAfterConsecutiveRounds: blockedAfter };
|
|
203
|
+
}
|
|
204
|
+
/** Whether optional text is meaningful rather than a strict-schema empty filler. */
|
|
205
|
+
function hasText(value) {
|
|
206
|
+
return value !== void 0 && value !== "";
|
|
207
|
+
}
|
|
208
|
+
/** Whether an optional round cap is meaningful rather than a strict-schema zero filler. */
|
|
209
|
+
function hasRoundCap(value) {
|
|
210
|
+
return value !== void 0 && value !== 0;
|
|
211
|
+
}
|
|
212
|
+
/** Build the exact compare-and-set ref from model arguments. */
|
|
213
|
+
function goalRef(goalId, revision) {
|
|
214
|
+
if (goalId.length === 0 || goalId !== goalId.trim() || !Number.isSafeInteger(revision) || revision < 1) throw new HarnessError("goal_id must be non-empty and revision must be a positive safe integer", "GOAL_TOOL_INVALID_UPDATE");
|
|
215
|
+
return {
|
|
216
|
+
id: GoalId(goalId),
|
|
217
|
+
revision
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
/** Stable compact model result; activation is an observation, not replay state. */
|
|
221
|
+
function goalValue(goal) {
|
|
222
|
+
if (goal === void 0) return { goal: null };
|
|
223
|
+
return {
|
|
224
|
+
goal: {
|
|
225
|
+
id: goal.id,
|
|
226
|
+
revision: goal.revision,
|
|
227
|
+
objective: goal.objective,
|
|
228
|
+
phase: goal.phase,
|
|
229
|
+
roundsStarted: goal.roundsStarted,
|
|
230
|
+
maxGoalRounds: goal.maxGoalRounds,
|
|
231
|
+
...goal.blockedReason === void 0 ? {} : { blockedReason: {
|
|
232
|
+
code: goal.blockedReason.code,
|
|
233
|
+
message: goal.blockedReason.message
|
|
234
|
+
} }
|
|
235
|
+
},
|
|
236
|
+
activation: goal.activation
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
/** Reusable canonical output declaration for all three goal controls. */
|
|
240
|
+
const GOAL_OUTPUT = {
|
|
241
|
+
schema: GOAL_VALUE_SCHEMA,
|
|
242
|
+
render: (_args, value) => [{
|
|
243
|
+
type: "text",
|
|
244
|
+
text: JSON.stringify(value)
|
|
245
|
+
}]
|
|
246
|
+
};
|
|
247
|
+
/** Generic, args-only pending presentation shared by the goal tools. */
|
|
248
|
+
function present(title, kind, rawInput) {
|
|
249
|
+
return {
|
|
250
|
+
card: "generic",
|
|
251
|
+
title,
|
|
252
|
+
kind,
|
|
253
|
+
...rawInput === void 0 ? {} : { rawInput }
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
/** Register the three Codex-shaped goal tools and their shared policy section. */
|
|
257
|
+
function apply(ctx, config) {
|
|
258
|
+
const resolved = resolveConfig(config);
|
|
259
|
+
ctx.systemPrompt.section({
|
|
260
|
+
name: "tool:goal",
|
|
261
|
+
order: ctx.systemPrompt.getSectionOrder("TOOL_GOAL"),
|
|
262
|
+
text: guidance(resolved.blockedAfterConsecutiveRounds)
|
|
263
|
+
});
|
|
264
|
+
ctx.tools.register(defineTool({
|
|
265
|
+
name: "get_goal",
|
|
266
|
+
description: GET_DESCRIPTION,
|
|
267
|
+
parameters: {},
|
|
268
|
+
output: GOAL_OUTPUT,
|
|
269
|
+
execute(_args, exec) {
|
|
270
|
+
const execution = goalToolExecution(ctx, exec);
|
|
271
|
+
return Promise.resolve(goalValue(ctx.goals.get(execution.agent)));
|
|
272
|
+
},
|
|
273
|
+
presentCall: () => present("Read current goal", "read")
|
|
274
|
+
}));
|
|
275
|
+
ctx.tools.register(defineTool({
|
|
276
|
+
name: "create_goal",
|
|
277
|
+
description: CREATE_DESCRIPTION,
|
|
278
|
+
parameters: {
|
|
279
|
+
objective: {
|
|
280
|
+
type: "string",
|
|
281
|
+
required: true,
|
|
282
|
+
description: "The concrete completion objective inferred from the direct human request."
|
|
283
|
+
},
|
|
284
|
+
max_goal_rounds: {
|
|
285
|
+
type: "number",
|
|
286
|
+
description: "Optional positive safe-integer limit on automatic continuation rounds."
|
|
287
|
+
}
|
|
288
|
+
},
|
|
289
|
+
output: GOAL_OUTPUT,
|
|
290
|
+
execute(args, exec) {
|
|
291
|
+
const execution = goalToolExecution(ctx, exec);
|
|
292
|
+
requireDirectHuman(ctx, execution);
|
|
293
|
+
const goal = ctx.goals.create(execution.agent, {
|
|
294
|
+
objective: args.objective,
|
|
295
|
+
...args.max_goal_rounds === void 0 ? {} : { maxGoalRounds: args.max_goal_rounds }
|
|
296
|
+
});
|
|
297
|
+
return Promise.resolve(goalValue(goal));
|
|
298
|
+
},
|
|
299
|
+
presentCall: (args) => present("Create goal", "other", args.objective)
|
|
300
|
+
}));
|
|
301
|
+
ctx.tools.register(defineTool({
|
|
302
|
+
name: "update_goal",
|
|
303
|
+
description: "Update the current goal.",
|
|
304
|
+
parameters: {
|
|
305
|
+
goal_id: {
|
|
306
|
+
type: "string",
|
|
307
|
+
required: true,
|
|
308
|
+
description: "Exact id returned by get_goal."
|
|
309
|
+
},
|
|
310
|
+
revision: {
|
|
311
|
+
type: "number",
|
|
312
|
+
required: true,
|
|
313
|
+
description: "Exact positive revision returned by get_goal."
|
|
314
|
+
},
|
|
315
|
+
action: {
|
|
316
|
+
type: "string",
|
|
317
|
+
required: true,
|
|
318
|
+
enum: UPDATE_ACTIONS,
|
|
319
|
+
description: "edit, pause, and resume require a direct top-level human request. complete and blocked are also allowed during an automatic continuation of this goal; blocked is rejected before the configured minimum round count."
|
|
320
|
+
},
|
|
321
|
+
objective: {
|
|
322
|
+
type: "string",
|
|
323
|
+
description: "Replacement objective; valid only with action edit."
|
|
324
|
+
},
|
|
325
|
+
max_goal_rounds: {
|
|
326
|
+
type: "number",
|
|
327
|
+
description: "Replacement cap; valid only with action edit."
|
|
328
|
+
},
|
|
329
|
+
blocked_reason: {
|
|
330
|
+
type: "string",
|
|
331
|
+
description: "Required only with action blocked: the concrete condition that persisted across rounds and blocks progress."
|
|
332
|
+
}
|
|
333
|
+
},
|
|
334
|
+
output: GOAL_OUTPUT,
|
|
335
|
+
execute(args, exec) {
|
|
336
|
+
const execution = goalToolExecution(ctx, exec);
|
|
337
|
+
const ref = goalRef(args.goal_id, args.revision);
|
|
338
|
+
const replacements = {
|
|
339
|
+
...hasText(args.objective) ? { objective: args.objective } : {},
|
|
340
|
+
...hasRoundCap(args.max_goal_rounds) ? { maxGoalRounds: args.max_goal_rounds } : {}
|
|
341
|
+
};
|
|
342
|
+
if (args.action === "edit") {
|
|
343
|
+
requireDirectHuman(ctx, execution);
|
|
344
|
+
if (hasText(args.blocked_reason)) throw new HarnessError("blocked_reason is valid only with action blocked", "GOAL_TOOL_INVALID_UPDATE");
|
|
345
|
+
const goal = ctx.goals.edit(execution.agent, ref, replacements);
|
|
346
|
+
return Promise.resolve(goalValue(goal));
|
|
347
|
+
}
|
|
348
|
+
if (args.action === "pause" || args.action === "resume") {
|
|
349
|
+
requireDirectHuman(ctx, execution);
|
|
350
|
+
if (hasText(args.objective) || hasRoundCap(args.max_goal_rounds) || hasText(args.blocked_reason)) throw new HarnessError("objective and max_goal_rounds are valid only with action edit; blocked_reason is valid only with action blocked", "GOAL_TOOL_INVALID_UPDATE");
|
|
351
|
+
const current = ctx.goals.get(execution.agent);
|
|
352
|
+
if (args.action === "resume" && current?.id === ref.id && current.revision === ref.revision && current.phase === "paused") throw new HarnessError("the model cannot resume a paused goal; the user must resume it", "GOAL_TOOL_RESUME_PAUSED");
|
|
353
|
+
const goal = args.action === "pause" ? ctx.goals.pause(execution.agent, ref) : ctx.goals.resume(execution.agent, ref);
|
|
354
|
+
return Promise.resolve(goalValue(goal));
|
|
355
|
+
}
|
|
356
|
+
const authority = completionAuthority(ctx, execution);
|
|
357
|
+
if (hasText(args.objective) || hasRoundCap(args.max_goal_rounds)) throw new HarnessError("objective and max_goal_rounds are valid only with action edit", "GOAL_TOOL_INVALID_UPDATE");
|
|
358
|
+
if (args.action === "complete" && hasText(args.blocked_reason)) throw new HarnessError("blocked_reason is valid only with action blocked", "GOAL_TOOL_INVALID_UPDATE");
|
|
359
|
+
if (args.action === "blocked" && (args.blocked_reason === void 0 || args.blocked_reason.trim().length === 0)) throw new HarnessError("blocked_reason is required with action blocked", "GOAL_TOOL_INVALID_UPDATE");
|
|
360
|
+
if (args.action === "blocked" && authority.kind === "goal-round" && authority.goal.roundsStarted < resolved.blockedAfterConsecutiveRounds) throw new HarnessError(`blocked requires at least ${resolved.blockedAfterConsecutiveRounds} consecutive goal rounds; current round is ${authority.goal.roundsStarted}`, "GOAL_TOOL_BLOCK_THRESHOLD");
|
|
361
|
+
const goal = args.action === "complete" ? ctx.goals.complete(execution.agent, ref) : ctx.goals.block(execution.agent, ref, {
|
|
362
|
+
code: "model-reported",
|
|
363
|
+
message: args.blocked_reason
|
|
364
|
+
});
|
|
365
|
+
if (authority.kind === "goal-round") exec.deferContext(createUserMessage({
|
|
366
|
+
content: args.action === "complete" ? renderWrapupContext(goal.objective) : renderWrapupContext(goal.objective, args.blocked_reason),
|
|
367
|
+
source: {
|
|
368
|
+
kind: "tool-goal",
|
|
369
|
+
form: "notice",
|
|
370
|
+
summary: boundContextSummary(`${args.action}: ${goal.objective}`)
|
|
371
|
+
}
|
|
372
|
+
}));
|
|
373
|
+
return Promise.resolve(goalValue(goal));
|
|
374
|
+
},
|
|
375
|
+
presentCall: (args) => present(`${args.action === "blocked" ? "Mark" : args.action.charAt(0).toUpperCase() + args.action.slice(1)} goal`, "other", hasText(args.blocked_reason) ? args.blocked_reason : hasText(args.objective) ? args.objective : hasRoundCap(args.max_goal_rounds) ? args.max_goal_rounds : args.goal_id)
|
|
376
|
+
}));
|
|
377
|
+
}
|
|
378
|
+
//#endregion
|
|
379
|
+
export { Config, apply, inject, name };
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/** Execution-time authority checks for the model-facing goal tools. */
|
|
2
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
3
|
+
import type { Agent } from '@qlzt/dsh-agent';
|
|
4
|
+
import type { GoalView } from '@qlzt/dsh-goal';
|
|
5
|
+
import type { SessionEvent, SessionSeq } from '@qlzt/dsh-session';
|
|
6
|
+
import type { ToolRunContext } from '@qlzt/dsh-tools';
|
|
7
|
+
/** The calling agent plus the immutable event cut and open-turn start seq used for authority checks. */
|
|
8
|
+
export interface GoalToolExecution {
|
|
9
|
+
readonly agent: Agent;
|
|
10
|
+
readonly events: readonly SessionEvent[];
|
|
11
|
+
readonly openTurnStartSeq: SessionSeq;
|
|
12
|
+
}
|
|
13
|
+
/** Hard authority granted to one state-changing call. */
|
|
14
|
+
export type GoalToolAuthority = {
|
|
15
|
+
readonly kind: 'direct-human';
|
|
16
|
+
} | {
|
|
17
|
+
readonly kind: 'goal-round';
|
|
18
|
+
readonly goal: GoalView;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Resolve and authenticate the calling agent and its driver boundary.
|
|
22
|
+
* @param ctx - Context carrying the live agent registry.
|
|
23
|
+
* @param exec - Tool execution metadata supplied by the registry.
|
|
24
|
+
* @returns The authenticated agent, immutable event cut, and open-turn boundary.
|
|
25
|
+
*/
|
|
26
|
+
export declare function goalToolExecution(ctx: Context, exec: ToolRunContext): GoalToolExecution;
|
|
27
|
+
/**
|
|
28
|
+
* Require authority originating in a human message accepted by a runtime root.
|
|
29
|
+
* @param ctx - Context carrying the live agent graph.
|
|
30
|
+
* @param execution - Authenticated current tool execution.
|
|
31
|
+
*/
|
|
32
|
+
export declare function requireDirectHuman(ctx: Context, execution: GoalToolExecution): void;
|
|
33
|
+
/**
|
|
34
|
+
* Resolve completion authority from either direct human input or the exact goal round.
|
|
35
|
+
* @param ctx - Context carrying live agents and goal state.
|
|
36
|
+
* @param execution - Authenticated current tool execution.
|
|
37
|
+
* @returns The direct-human or exact-goal-round authority grant.
|
|
38
|
+
*/
|
|
39
|
+
export declare function completionAuthority(ctx: Context, execution: GoalToolExecution): GoalToolAuthority;
|
|
40
|
+
//# sourceMappingURL=authority.d.ts.map
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Model-facing `get_goal`, `create_goal`, and `update_goal` tools over the
|
|
3
|
+
* persisted same-session goal domain.
|
|
4
|
+
* @module @qlzt/dsh-tool-goal
|
|
5
|
+
*/
|
|
6
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
7
|
+
import z from '@deepseek-ai/schemastery';
|
|
8
|
+
import type { ContextFormed } from '@qlzt/dsh-llm';
|
|
9
|
+
declare module '@qlzt/dsh-llm' {
|
|
10
|
+
interface MessageSourceMap {
|
|
11
|
+
'tool-goal': {
|
|
12
|
+
kind: 'tool-goal';
|
|
13
|
+
} & ContextFormed;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
export declare const name = "tool-goal";
|
|
17
|
+
export declare const inject: string[];
|
|
18
|
+
/** Model policy and hard lower bounds for goal-state updates. */
|
|
19
|
+
export interface Config {
|
|
20
|
+
/** Minimum admitted goal rounds before the model may self-report `blocked`. */
|
|
21
|
+
blockedAfterConsecutiveRounds?: number;
|
|
22
|
+
}
|
|
23
|
+
/** Schemastery config for the goal-tool policy. */
|
|
24
|
+
export declare const Config: z<Config>;
|
|
25
|
+
/** Register the three Codex-shaped goal tools and their shared policy section. */
|
|
26
|
+
export declare function apply(ctx: Context, config: Config): void;
|
|
27
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** Model-visible wrap-up instruction for a terminal autonomous goal update. */
|
|
2
|
+
import type { ContentBlock } from '@qlzt/dsh-llm';
|
|
3
|
+
/**
|
|
4
|
+
* Render the closing-message instruction injected after an autonomous goal
|
|
5
|
+
* round reports `complete` or `blocked`, replacing the former hard turn stop
|
|
6
|
+
* so the model still addresses the user once before the turn ends.
|
|
7
|
+
* @param objective - the terminal goal's objective, echoed for grounding.
|
|
8
|
+
* @param blockedReason - the validated report for `blocked`; omitted for `complete`.
|
|
9
|
+
* @returns a fresh one-block context for `ToolRunContext.deferContext()`.
|
|
10
|
+
*/
|
|
11
|
+
export declare function renderWrapupContext(objective: string, blockedReason?: string): ContentBlock[];
|
|
12
|
+
//# sourceMappingURL=wrapup.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,56 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@qlzt/dsh-tool-goal",
|
|
3
|
-
"
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"description": "Model-facing same-session goal tools with execution-time authority checks",
|
|
4
|
+
"version": "0.2.1-alpha.3",
|
|
5
|
+
"publishConfig": {
|
|
6
|
+
"access": "public"
|
|
7
|
+
},
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "https://gitee.com/zsping1989/deepseek-harness.git",
|
|
11
|
+
"directory": "packages/goal/tool-goal"
|
|
12
|
+
},
|
|
13
|
+
"type": "module",
|
|
14
|
+
"main": "lib/index.js",
|
|
15
|
+
"types": "lib/types/index.d.ts",
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"types": "./lib/types/index.d.ts",
|
|
19
|
+
"default": "./lib/index.js"
|
|
20
|
+
},
|
|
21
|
+
"./src/*": "./src/*",
|
|
22
|
+
"./package.json": "./package.json"
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"lib/index.js",
|
|
26
|
+
"lib/types/**/*.d.ts"
|
|
27
|
+
],
|
|
28
|
+
"license": "MIT",
|
|
29
|
+
"peerDependencies": {
|
|
30
|
+
"@qlzt/dsh-agent": "0.2.1-alpha.3",
|
|
31
|
+
"@qlzt/dsh-goal": "0.2.1-alpha.3",
|
|
32
|
+
"@qlzt/dsh-llm": "0.2.1-alpha.3",
|
|
33
|
+
"@qlzt/dsh-session": "0.2.1-alpha.3",
|
|
34
|
+
"@qlzt/dsh-system-prompt": "0.2.1-alpha.3",
|
|
35
|
+
"@qlzt/dsh-tools": "0.2.1-alpha.3",
|
|
36
|
+
"@deepseek-ai/cordis": "~4.0.5-alpha.1",
|
|
37
|
+
"@qlzt/dsh-session-projection": "0.2.1-alpha.3"
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"@deepseek-ai/schemastery": "~3.18.5-alpha.1"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@deepseek-ai/cordis-plugin-loader": "~1.0.6-alpha.1",
|
|
44
|
+
"@deepseek-ai/dsh-agent": "0.2.1-alpha.3",
|
|
45
|
+
"@deepseek-ai/dsh-agent-loop-testkit": "0.2.1-alpha.3",
|
|
46
|
+
"@deepseek-ai/dsh-agent-loop": "0.2.1-alpha.3",
|
|
47
|
+
"@deepseek-ai/dsh-goal": "0.2.1-alpha.3",
|
|
48
|
+
"@deepseek-ai/dsh-llm": "0.2.1-alpha.3",
|
|
49
|
+
"@deepseek-ai/dsh-session": "0.2.1-alpha.3",
|
|
50
|
+
"@deepseek-ai/dsh-system-prompt": "0.2.1-alpha.3",
|
|
51
|
+
"@deepseek-ai/dsh-tools": "0.2.1-alpha.3",
|
|
52
|
+
"@deepseek-ai/cordis": "~4.0.5-alpha.1",
|
|
53
|
+
"@deepseek-ai/dsh-session-projection": "0.2.1-alpha.3"
|
|
54
|
+
},
|
|
55
|
+
"dsh": {}
|
|
56
|
+
}
|