@morlay/dsh-context-assembler 0.0.1-alpha.2 → 0.0.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/README.md +29 -26
- package/dist/index.mjs +1 -1
- package/dist/rows.d.mts +13 -11
- package/dist/rows.mjs +15 -10
- package/dist/scope.d.mts +33 -39
- package/dist/scope.mjs +102 -2
- package/locale/en.json +1 -1
- package/locale/zh.json +1 -1
- package/package.json +29 -29
- package/src/index.ts +5 -5
- package/src/rows.ts +14 -13
- package/src/scope/index.ts +131 -99
- package/dist/scope-CI-fNq5A.mjs +0 -104
package/README.md
CHANGED
|
@@ -5,13 +5,13 @@
|
|
|
5
5
|
见 [`src/rows.ts` 的 `contextChannel()`](./src/rows.ts)。引用展开原先也在这个包里,
|
|
6
6
|
现在独立成 [`@morlay/dsh-reference`](../dsh-reference/README.md)(它只挂 `agent/pre-step`、不依赖通道)。
|
|
7
7
|
|
|
8
|
-
| 出口 | 行 id / 插件 name | 做什么
|
|
9
|
-
| ---------------------- | --------------------------------------------------------- |
|
|
10
|
-
| `.` | `context-assembler`(插件 name `context-assembler-tree`) | **组装出口**:按 config 决定装哪些能力、各带什么参数(缺省即四套能力)
|
|
11
|
-
| `./assembler` | `context-assembler` | 注入通道:唯一渲染者与唯一覆盖判定处,发布 `ctx.contextAssembler`
|
|
12
|
-
| `./agent-instructions` | `context-agent-instructions` | 工作区指令链(`$DSH_HOME/AGENTS.md` + 项目根到 cwd 逐级)
|
|
13
|
-
| `./skill-catalog` | `context-skill-catalog` | skill 目录规则块 + 模型侧 `skill` 工具
|
|
14
|
-
| `./scope` | `context-scope`
|
|
8
|
+
| 出口 | 行 id / 插件 name | 做什么 |
|
|
9
|
+
| ---------------------- | --------------------------------------------------------- | ---------------------------------------------------------------------------- |
|
|
10
|
+
| `.` | `context-assembler`(插件 name `context-assembler-tree`) | **组装出口**:按 config 决定装哪些能力、各带什么参数(缺省即四套能力) |
|
|
11
|
+
| `./assembler` | `context-assembler` | 注入通道:唯一渲染者与唯一覆盖判定处,发布 `ctx.contextAssembler` |
|
|
12
|
+
| `./agent-instructions` | `context-agent-instructions` | 工作区指令链(`$DSH_HOME/AGENTS.md` + 项目根到 cwd 逐级) |
|
|
13
|
+
| `./skill-catalog` | `context-skill-catalog` | skill 目录规则块 + 模型侧 `skill` 工具 |
|
|
14
|
+
| `./scope` | `context-assembler-scope` | 按会话收口:工具白名单、instruction 总开关、动态快照开关(模式把定义推给它) |
|
|
15
15
|
|
|
16
16
|
规则、id 表与分层的 home 在 [上下文注入规则](./.agents/designs/20260921-上下文注入规则.md);术语见
|
|
17
17
|
[本包 CONTEXT](./.agents/CONTEXT.md)。
|
|
@@ -23,8 +23,8 @@
|
|
|
23
23
|
([`@morlay/dsh-agent-toolkit`](../../profile/dsh-agent-toolkit/README.md) 的 `guidance`)这类消费者住在别的包里,
|
|
24
24
|
隔离会把它们挡在组外(行停在 waiting,不报错);理由见
|
|
25
25
|
[ADR 通道作为全局服务装配不隔离](./.agents/adrs/20260923-通道作为全局服务装配不隔离.md)。`scope` **不在这份 patch 里**
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
——它是模式那一层的收口行,由 [`@morlay/dsh-session-mode`](../../profile/dsh-session-mode/README.md) 的 patch 与模式定义
|
|
27
|
+
一起装(行 id 与 name 归本包的 `scopeRow()`,单一 home)。
|
|
28
28
|
|
|
29
29
|
工具说明(汉化 / 精简 / 用法分组)也不在这里:它归
|
|
30
30
|
[`@morlay/dsh-agent-toolkit`](../../profile/dsh-agent-toolkit/README.md),同样在 profile 平面装一次。
|
|
@@ -32,8 +32,8 @@
|
|
|
32
32
|
## 装配形态
|
|
33
33
|
|
|
34
34
|
本包是 bundle:`cordis.patch.yml` 由 [`tool/patch.ts`](./tool/patch.ts) 从 [`src/rows.ts`](./src/rows.ts) 渲染,
|
|
35
|
-
`rows` 出口导出同一份清单。`dsh.profile.bundles`
|
|
36
|
-
|
|
35
|
+
`rows` 出口导出同一份清单。`dsh.profile.bundles` 列出本包即装一次,**服务全局共享、不隔离**;模式的收口是
|
|
36
|
+
另一行(`scopeRow()`:工具白名单 / instruction / 动态快照),别的包的行(工具说明、skill 目录)直接 `inject`
|
|
37
37
|
同一份通道。
|
|
38
38
|
|
|
39
39
|
## 为什么合成一个包
|
|
@@ -70,7 +70,7 @@ user 消息注入(每条按文本幂等,只有变化的那条重发)。
|
|
|
70
70
|
| `hiddenSkills(visible)` | 声明了 `requires` 而入口工具一个都不可见的 skill 名 |
|
|
71
71
|
|
|
72
72
|
配置(`keep` / `suppress` / `replace`)的默认值在 [`src/assembler/defaults.ts`](./src/assembler/defaults.ts)。
|
|
73
|
-
**全局一份、不隔离**:模式差异由 `context-scope` 登记(`setInstructions` / `restrictTools`)收口,跨包的
|
|
73
|
+
**全局一份、不隔离**:模式差异由 `context-assembler-scope` 登记(`setInstructions` / `restrictTools`)收口,跨包的
|
|
74
74
|
消费者(工具说明、技能目录)直接 `inject` 同一份通道(见
|
|
75
75
|
[ADR 通道作为全局服务装配不隔离](./.agents/adrs/20260923-通道作为全局服务装配不隔离.md))。
|
|
76
76
|
|
|
@@ -79,14 +79,14 @@ user 消息注入(每条按文本幂等,只有变化的那条重发)。
|
|
|
79
79
|
`$DSH_HOME/AGENTS.md` 加项目根到 cwd 的逐级 `AGENTS.md` / `AGENTS.local.md`,**一条文件一条 id**
|
|
80
80
|
(`agent-instructions:<根标识>:<文件>`,根标识是根目录的 8 位摘要——同进程两个项目根的同名文件因此不会互相
|
|
81
81
|
顶掉),文件变化时只重发变了的那一份。取代上游 `@deepseek-ai/dsh-agent-instructions`(host 那行由上游
|
|
82
|
-
web-app bundle
|
|
82
|
+
web-app bundle 自己设在装配层;我们那份由本包的组装出口在 profile 平面装)。
|
|
83
83
|
|
|
84
84
|
- **不跟踪 `read`/`write`/`edit`**:上游靠 touch 上浮触发刷新;本部署的 `AGENTS.md` 几乎不变,按
|
|
85
85
|
`mtime:size` 对账足够。
|
|
86
86
|
- **超预算可见**:单文件超过 `maxBytes` 时截断并留一行提示,不静默丢内容。
|
|
87
87
|
- **与上游的 baseline 认领对齐**:`source` 带 `kind: "agent-instructions"` + `baseline: true` +
|
|
88
88
|
`baselineIdentity`(身份与上游 `workspaceBaselineIdentity` 逐字相等)。少了后两样会出一个只在真会话里
|
|
89
|
-
|
|
89
|
+
看得见的毛病:**同会话切到另一套装配后出现两条 AGENTS.md**——上游认为基线不存在,再注入一条自己的模板
|
|
90
90
|
("Use them as guidance…"),两条口径矛盾且模型无法理解为覆盖。
|
|
91
91
|
- 配置:`instructionFileCandidates` / `localInstructionFileCandidates`(覆盖成只读 `AGENTS*`,不含 CLAUDE
|
|
92
92
|
系列)、`maxBytes`(65536)、`dshHome`。
|
|
@@ -105,10 +105,15 @@ web-app bundle 自己设在 preset 平面)。
|
|
|
105
105
|
|
|
106
106
|
## scope
|
|
107
107
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
108
|
+
按会话收口的出口(行 id `context-assembler-scope`):工具白名单(装配期投影 + 执行层 guard + 与工具同源的
|
|
109
|
+
`tool:<工具名>` 说明 section,三侧同判据)、`instructions`(关掉规则块与降级 section)、`runtimeContext`
|
|
110
|
+
(关掉沙箱 / 审批那两条动态快照,按 scope 抑制)。
|
|
111
|
+
|
|
112
|
+
**定义由消费方推给它**,它自己不认识"模式":`ctx.sessionToolScope.apply(agent, mode)` 是唯一入口,落在该
|
|
113
|
+
agent 的 `ctx` 上(抑制器、guard 都在那里),同一个 agent 再 apply 一次就是换一份——旧的 effect 树收回、按新
|
|
114
|
+
的装一遍。它解决的是"模式只能决定加什么、管不了 host 平面":`dsh.profile.bundles` 打开的包(工具行、
|
|
115
|
+
实验性的 Agent Teams)在 host 平面插行,对所有会话一视同仁;模式要表达"我只有这几个工具、一条提示词都不要"
|
|
116
|
+
时,唯一与来源无关的做法是在会话语义上收口。
|
|
112
117
|
|
|
113
118
|
白名单同时登记给通道(`restrictTools`):技能目录与用法组正文按 `visibleTools` 收口,所以被收窄的工具既不在
|
|
114
119
|
目录、也不会被讲——注册表里"装着"不等于这个会话"能用"。
|
|
@@ -125,15 +130,12 @@ scope 抑制)。它解决的是"preset 只能决定加什么、管不了 host
|
|
|
125
130
|
capabilities: [assembler, agent-instructions, skill-catalog]
|
|
126
131
|
```
|
|
127
132
|
|
|
128
|
-
|
|
133
|
+
**模式平面**:收口那一行(不带 config——模式定义在 `session-mode` 行的 `config.modes` 里)由
|
|
134
|
+
[`@morlay/dsh-session-mode`](../../profile/dsh-session-mode/README.md) 的 patch 装,通道由 profile 那份共享:
|
|
129
135
|
|
|
130
136
|
```yaml
|
|
131
|
-
- id: context-scope
|
|
137
|
+
- id: context-assembler-scope
|
|
132
138
|
name: "@morlay/dsh-context-assembler/scope"
|
|
133
|
-
config:
|
|
134
|
-
allowTools: [ask_user_question, web_search, web_fetch]
|
|
135
|
-
instructions: false
|
|
136
|
-
runtimeContext: false
|
|
137
139
|
```
|
|
138
140
|
|
|
139
141
|
几点硬要求:
|
|
@@ -141,9 +143,10 @@ scope 抑制)。它解决的是"preset 只能决定加什么、管不了 host
|
|
|
141
143
|
- **每个能力仍是独立的 cordis 插件**(组装出口只是 `ctx.plugin()` 装它们),各自带自己的 `inject`;合成单入口会让
|
|
142
144
|
`inject` 变并集,一个可选搭档缺席就拖垮整包。
|
|
143
145
|
- **通道不做隔离**:消费者跨包(`@morlay/dsh-agent-toolkit` 的工具说明、本包的技能目录),隔离会把它们挡在组外
|
|
144
|
-
(行停在 waiting);模式差异改由 `context-scope` 登记给通道,见
|
|
146
|
+
(行停在 waiting);模式差异改由 `context-assembler-scope` 登记给通道,见
|
|
145
147
|
[ADR 通道作为全局服务装配不隔离](./.agents/adrs/20260923-通道作为全局服务装配不隔离.md)。真装配的判据是
|
|
146
|
-
`pnpm exec tsx packages/desktop/dsh-desktop-host/tool/verify-
|
|
148
|
+
`pnpm exec tsx packages/desktop/dsh-desktop-host/tool/verify-session-mode.mts`(装配层可见一份、收口行装上、
|
|
149
|
+
官方 agent preset 不在)。
|
|
147
150
|
- **子出口仍可单独装配**(`@morlay/dsh-context-assembler/assembler` 等):同一些插件,只是默认走组装出口。
|
|
148
151
|
|
|
149
152
|
## 维护注意
|
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { n as agent_instructions_exports } from "./agent-instructions-BNEiZl0P.mjs";
|
|
2
2
|
import { r as assembler_exports } from "./assembler-_F9urbPj.mjs";
|
|
3
|
-
import {
|
|
3
|
+
import { t as scope_exports } from "./scope.mjs";
|
|
4
4
|
import { t as skill_catalog_exports } from "./skill-catalog.mjs";
|
|
5
5
|
import z from "@deepseek-ai/schemastery";
|
|
6
6
|
//#region src/index.ts
|
package/dist/rows.d.mts
CHANGED
|
@@ -6,8 +6,9 @@
|
|
|
6
6
|
* (`@morlay/dsh-agent-toolkit/guidance`,在别的包里)与 skill 目录这类消费者都在别的行上
|
|
7
7
|
* `inject` 它,隔离会把它们挡在组外(行停在 waiting,不报错)。
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
9
|
+
* 本包自己的 bundle 只装通道(`PATCH_ROWS`)。模式的收口是另一行(`scopeRow()`),它由
|
|
10
|
+
* [`@morlay/dsh-session-mode`](../../../profile/dsh-session-mode/README.md) 的 patch 与模式定义一起装——
|
|
11
|
+
* 那一行的 config 就是模式清单。
|
|
11
12
|
*/
|
|
12
13
|
/** 组装出口(包根)就是那个"按 config 装能力"的插件。 */
|
|
13
14
|
declare const CONTEXT_PACKAGE = "@morlay/dsh-context-assembler";
|
|
@@ -23,18 +24,19 @@ interface ContextRow {
|
|
|
23
24
|
*/
|
|
24
25
|
declare function contextChannel(config?: Readonly<Record<string, unknown>>): ContextRow;
|
|
25
26
|
/**
|
|
26
|
-
*
|
|
27
|
+
* 模式收口那一行:`scope` 出口按 `ctx.sessionModes` 的模式定义收口(工具白名单、instruction 总开关、
|
|
28
|
+
* 动态快照开关)。
|
|
27
29
|
*
|
|
28
|
-
*
|
|
29
|
-
* `ctx.
|
|
30
|
+
* 行 id 用全名 `context-assembler-scope`:它的搭档是同一份装配里的 `context-assembler`(通道),两个 id 放在
|
|
31
|
+
* 一起才看得出这一行属于哪个包、做什么——`context-scope` 那种短名会被误当成 `ctx.contextScope` 之类的服务。
|
|
32
|
+
*
|
|
33
|
+
* 行本身**不带 config**:装哪些模式、每个模式收什么,都由 `session-mode` 行的 `config.modes` 决定,
|
|
34
|
+
* 这一行只把它落到会话上(`inject` `sessionModes`,由
|
|
35
|
+
* [`@morlay/dsh-session-mode`](../../../profile/dsh-session-mode/README.md) 的 patch 插一行)。
|
|
30
36
|
*/
|
|
31
|
-
declare function scopeRow(
|
|
32
|
-
readonly id: string;
|
|
33
|
-
readonly name: string;
|
|
34
|
-
readonly config?: Readonly<Record<string, unknown>>;
|
|
35
|
-
};
|
|
37
|
+
declare function scopeRow(): ContextRow;
|
|
36
38
|
/**
|
|
37
|
-
* bundle patch
|
|
39
|
+
* bundle patch 的行清单:只有通道一行(模式收口那一行归 `session-mode` 的 patch 装)。
|
|
38
40
|
*
|
|
39
41
|
* 形态是 `insert`:这些行由本包提供给 host 平面。写成 `- id: x` 那种"改已有行"的形态时,装配期找不到
|
|
40
42
|
* 目标行(只 warn 后跳过),通道根本没装上,引用它的行停在 waiting。
|
package/dist/rows.mjs
CHANGED
|
@@ -6,8 +6,9 @@
|
|
|
6
6
|
* (`@morlay/dsh-agent-toolkit/guidance`,在别的包里)与 skill 目录这类消费者都在别的行上
|
|
7
7
|
* `inject` 它,隔离会把它们挡在组外(行停在 waiting,不报错)。
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
9
|
+
* 本包自己的 bundle 只装通道(`PATCH_ROWS`)。模式的收口是另一行(`scopeRow()`),它由
|
|
10
|
+
* [`@morlay/dsh-session-mode`](../../../profile/dsh-session-mode/README.md) 的 patch 与模式定义一起装——
|
|
11
|
+
* 那一行的 config 就是模式清单。
|
|
11
12
|
*/
|
|
12
13
|
/** 组装出口(包根)就是那个"按 config 装能力"的插件。 */
|
|
13
14
|
const CONTEXT_PACKAGE = "@morlay/dsh-context-assembler";
|
|
@@ -23,20 +24,24 @@ function contextChannel(config) {
|
|
|
23
24
|
};
|
|
24
25
|
}
|
|
25
26
|
/**
|
|
26
|
-
*
|
|
27
|
+
* 模式收口那一行:`scope` 出口按 `ctx.sessionModes` 的模式定义收口(工具白名单、instruction 总开关、
|
|
28
|
+
* 动态快照开关)。
|
|
27
29
|
*
|
|
28
|
-
*
|
|
29
|
-
* `ctx.
|
|
30
|
+
* 行 id 用全名 `context-assembler-scope`:它的搭档是同一份装配里的 `context-assembler`(通道),两个 id 放在
|
|
31
|
+
* 一起才看得出这一行属于哪个包、做什么——`context-scope` 那种短名会被误当成 `ctx.contextScope` 之类的服务。
|
|
32
|
+
*
|
|
33
|
+
* 行本身**不带 config**:装哪些模式、每个模式收什么,都由 `session-mode` 行的 `config.modes` 决定,
|
|
34
|
+
* 这一行只把它落到会话上(`inject` `sessionModes`,由
|
|
35
|
+
* [`@morlay/dsh-session-mode`](../../../profile/dsh-session-mode/README.md) 的 patch 插一行)。
|
|
30
36
|
*/
|
|
31
|
-
function scopeRow(
|
|
37
|
+
function scopeRow() {
|
|
32
38
|
return {
|
|
33
|
-
id: "context-scope",
|
|
34
|
-
name: "@morlay/dsh-context-assembler/scope"
|
|
35
|
-
...config === void 0 ? {} : { config }
|
|
39
|
+
id: "context-assembler-scope",
|
|
40
|
+
name: "@morlay/dsh-context-assembler/scope"
|
|
36
41
|
};
|
|
37
42
|
}
|
|
38
43
|
/**
|
|
39
|
-
* bundle patch
|
|
44
|
+
* bundle patch 的行清单:只有通道一行(模式收口那一行归 `session-mode` 的 patch 装)。
|
|
40
45
|
*
|
|
41
46
|
* 形态是 `insert`:这些行由本包提供给 host 平面。写成 `- id: x` 那种"改已有行"的形态时,装配期找不到
|
|
42
47
|
* 目标行(只 warn 后跳过),通道根本没装上,引用它的行停在 waiting。
|
package/dist/scope.d.mts
CHANGED
|
@@ -1,47 +1,41 @@
|
|
|
1
1
|
import z from "@deepseek-ai/schemastery";
|
|
2
|
-
import { Context } from "@deepseek-ai/cordis";
|
|
2
|
+
import { Context, Service } from "@deepseek-ai/cordis";
|
|
3
|
+
import { Agent } from "@deepseek-ai/dsh-agent";
|
|
3
4
|
declare namespace index_d_exports {
|
|
4
|
-
export { Config, apply, inject, name };
|
|
5
|
+
export { Config, SessionToolScope, SessionToolScopeMode, apply, inject, name };
|
|
5
6
|
}
|
|
6
|
-
declare const name = "context-scope";
|
|
7
|
+
declare const name = "context-assembler-scope";
|
|
8
|
+
/** 收口要的服务;`sessionToolScope` 由本行自己发布,`tools` 的 guard 挂在每个 agent 的 ctx 上。 */
|
|
7
9
|
declare const inject: string[];
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
10
|
+
/** 本行不吃 config:要收成什么样由消费方在 `apply` 时给。 */
|
|
11
|
+
declare const Config: z<Schemastery.ObjectS<{}>, Schemastery.ObjectT<{}>, "plain">;
|
|
12
|
+
/** 一次收口的全部输入:模式定义里本出口用到的那几项。 */
|
|
13
|
+
interface SessionToolScopeMode {
|
|
14
|
+
/** 拒绝对话时用的模式名。 */
|
|
15
|
+
readonly name: string;
|
|
16
|
+
/** 这个会话能用的工具;其余既不进目录,调用也被拒。 */
|
|
17
|
+
readonly allowTools: readonly string[];
|
|
18
|
+
/** 缺省为要;`false` 表示这个会话不要任何 instruction 类注入。 */
|
|
19
|
+
readonly instructions?: boolean | undefined;
|
|
20
|
+
/** 缺省为要;`false` 表示这个会话不要动态快照。 */
|
|
21
|
+
readonly runtimeContext?: boolean | undefined;
|
|
22
|
+
}
|
|
23
|
+
declare module "@deepseek-ai/cordis" {
|
|
24
|
+
interface Context {
|
|
25
|
+
sessionToolScope: SessionToolScope;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/** 按会话收口工具与注入的服务。 */
|
|
29
|
+
declare class SessionToolScope extends Service {
|
|
30
|
+
private readonly applied;
|
|
31
|
+
constructor(ctx: Context);
|
|
24
32
|
/**
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
* 抑制是**按 scope** 的(上游按装配的 scope 链查抑制器),所以本行装在 preset 子树里就只作用于这个
|
|
29
|
-
* 模式的会话;它只挡注入,不改任何提供方的行为(沙箱该怎么判还怎么判)。
|
|
33
|
+
* 把某个会话收口到这份定义上(同一个 agent 再调就是换一份)。
|
|
34
|
+
* @param agent - 目标会话的 agent。
|
|
35
|
+
* @param mode - 收口的全部输入。
|
|
30
36
|
*/
|
|
31
|
-
|
|
37
|
+
apply(agent: Agent, mode: SessionToolScopeMode): void;
|
|
32
38
|
}
|
|
33
|
-
declare
|
|
34
|
-
/**
|
|
35
|
-
* 把某个 preset 的工具收成"只有这些"。
|
|
36
|
-
*
|
|
37
|
-
* 为什么需要它:preset composition 只能决定"加什么",管不了 host 层——`dsh.profile.bundles` 打开的
|
|
38
|
-
* bundle(例如实验性的 Agent Teams)会在 host 层插工具行与 section,对所有 preset 一视同仁。模式想
|
|
39
|
-
* 表达"我只有这几个工具、一条提示词都不要"时,唯一与来源无关的做法是收口"有什么"。
|
|
40
|
-
*
|
|
41
|
-
* 为什么不是 `tools.restrict()`:它会改动可见工具集,从而触发 `tools/change`;上游 `tool-subagent`
|
|
42
|
-
* 正是用该事件做 composition reconcile——两边互相触发会变成装配风暴。这里用投影过滤加执行层 guard:
|
|
43
|
-
* 既不碰注册表、也不发事件。
|
|
44
|
-
*/
|
|
45
|
-
declare function apply(ctx: Context, config: Config): void;
|
|
39
|
+
declare function apply(ctx: Context): void;
|
|
46
40
|
//#endregion
|
|
47
|
-
export { Config, apply, inject, name, index_d_exports as t };
|
|
41
|
+
export { Config, SessionToolScope, SessionToolScopeMode, apply, inject, name, index_d_exports as t };
|
package/dist/scope.mjs
CHANGED
|
@@ -1,2 +1,102 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
|
|
2
|
+
import z from "@deepseek-ai/schemastery";
|
|
3
|
+
import { Service } from "@deepseek-ai/cordis";
|
|
4
|
+
//#region src/scope/index.ts
|
|
5
|
+
/**
|
|
6
|
+
* 按**会话**收口:工具白名单、instruction 总开关、动态快照。
|
|
7
|
+
*
|
|
8
|
+
* 这是本包的 `scope` 出口(行 id `context-assembler-scope`)。它不再吃 config,也不认识"模式"这个概念——
|
|
9
|
+
* 收口要什么由消费方在会话建立时**推**进来:`ctx.sessionToolScope.apply(agent, mode)`(
|
|
10
|
+
* [`@morlay/dsh-session-mode`](../../../profile/dsh-session-mode/README.md) 的模式定义里正好是这几项)。
|
|
11
|
+
* 方向因此是单向的:装配层读这个服务,这个包不认识装配层。
|
|
12
|
+
*
|
|
13
|
+
* `apply` 落到该 agent 身上的三件事,各走一条与"谁注册了这个工具"无关的路:
|
|
14
|
+
*
|
|
15
|
+
* 1. **装配期投影**(每次装配按已注册的那份):模型目录与工具说明的 `tool:<工具名>` section 同源过滤;
|
|
16
|
+
* 2. **执行层 guard**:落在 `agent.ctx` 上,调用被拒时给出该模式的文案;
|
|
17
|
+
* 3. **通道开关**:`instructions` 关掉该会话的规则块(工作区指令、技能目录、用法正文)、
|
|
18
|
+
* `runtimeContext` 抑制该会话的动态快照(沙箱与审批策略那两条)。
|
|
19
|
+
*
|
|
20
|
+
* 为什么不用 `tools.restrict()`:它会改动可见工具集,从而触发 `tools/change`;上游 `tool-subagent` 正是用
|
|
21
|
+
* 该事件做 composition reconcile——两边互相触发会变成装配风暴。这里用投影过滤加执行层 guard:既不碰注册
|
|
22
|
+
* 表、也不发事件。
|
|
23
|
+
*
|
|
24
|
+
* 为什么按 `agent.ctx` 注册而不是先建一棵 preset 子树:会话的 persona / 工具收口本来就是"这个 agent 的
|
|
25
|
+
* 事",`agent.ctx` 的 scope 已经够了(上游给子 agent 装 persona 用的也是这条路);一棵子树要连带上游的
|
|
26
|
+
* `agent-presets` 注册表、每 revision 一份 Loader 与 `isolate` realm,模式本身用不上。
|
|
27
|
+
*
|
|
28
|
+
* 模式在空白窗口里可以切换,所以状态是**可替换**的:同一个 agent 再 `apply` 一次就是换一份——旧的
|
|
29
|
+
* disposer 全部收回,再按新的装一遍。
|
|
30
|
+
*/
|
|
31
|
+
var scope_exports = /* @__PURE__ */ __exportAll({
|
|
32
|
+
Config: () => Config,
|
|
33
|
+
SessionToolScope: () => SessionToolScope,
|
|
34
|
+
apply: () => apply,
|
|
35
|
+
inject: () => inject,
|
|
36
|
+
name: () => name
|
|
37
|
+
});
|
|
38
|
+
const name = "context-assembler-scope";
|
|
39
|
+
/** 收口要的服务;`sessionToolScope` 由本行自己发布,`tools` 的 guard 挂在每个 agent 的 ctx 上。 */
|
|
40
|
+
const inject = ["tools", "systemPrompt"];
|
|
41
|
+
/** 本行不吃 config:要收成什么样由消费方在 `apply` 时给。 */
|
|
42
|
+
const Config = z.object({});
|
|
43
|
+
const OUT_OF_SCOPE = (toolName, modeName) => `${toolName} 不在「${modeName}」的工具范围内,用它不会有结果;按当前模式提供的工具完成任务,或让用户切到别的模式。`;
|
|
44
|
+
/** 单个工具的说明 section 名前缀:`tool:<工具名>`(`tools:` 那类是聚合块,不归白名单管)。 */
|
|
45
|
+
const TOOL_SECTION_PREFIX = "tool:";
|
|
46
|
+
/** 通道是可选的搭档:没有它就没有 instruction 可关、也没人收窄技能目录,工具目录与执行 guard 照常生效。 */
|
|
47
|
+
function channelOf(ctx) {
|
|
48
|
+
try {
|
|
49
|
+
return ctx.get("contextAssembler");
|
|
50
|
+
} catch {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/** 按会话收口工具与注入的服务。 */
|
|
55
|
+
var SessionToolScope = class extends Service {
|
|
56
|
+
applied = /* @__PURE__ */ new WeakMap();
|
|
57
|
+
constructor(ctx) {
|
|
58
|
+
super(ctx, "sessionToolScope");
|
|
59
|
+
ctx.on("system-prompt/assemble", async (_assembly, context, next) => {
|
|
60
|
+
const agent = context.agent;
|
|
61
|
+
const applied = agent === void 0 ? void 0 : this.applied.get(agent);
|
|
62
|
+
if (applied === void 0) return next();
|
|
63
|
+
const allow = new Set(applied.mode.allowTools);
|
|
64
|
+
const result = await next();
|
|
65
|
+
return {
|
|
66
|
+
...result,
|
|
67
|
+
sections: result.sections.filter((section) => section.name.startsWith(TOOL_SECTION_PREFIX) ? allow.has(section.name.slice(5)) : true),
|
|
68
|
+
tools: result.tools.filter((tool) => allow.has(tool.name))
|
|
69
|
+
};
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* 把某个会话收口到这份定义上(同一个 agent 再调就是换一份)。
|
|
74
|
+
* @param agent - 目标会话的 agent。
|
|
75
|
+
* @param mode - 收口的全部输入。
|
|
76
|
+
*/
|
|
77
|
+
apply(agent, mode) {
|
|
78
|
+
const previous = this.applied.get(agent);
|
|
79
|
+
if (previous !== void 0) previous.dispose();
|
|
80
|
+
const allow = new Set(mode.allowTools);
|
|
81
|
+
const dispose = agent.ctx.effect(() => {
|
|
82
|
+
const stoppers = [];
|
|
83
|
+
if (mode.runtimeContext === false) stoppers.push(agent.ctx.systemPrompt.suppressRuntimeContext());
|
|
84
|
+
const fiber = agent.ctx.inject(["tools"], (scope) => {
|
|
85
|
+
scope.tools.guard((exec) => allow.has(exec.name) ? void 0 : OUT_OF_SCOPE(exec.name, mode.name));
|
|
86
|
+
});
|
|
87
|
+
return [...stoppers, () => fiber.dispose()];
|
|
88
|
+
}, "sessionToolScope: per-session gate");
|
|
89
|
+
const channel = channelOf(this.ctx);
|
|
90
|
+
channel?.setInstructions(agent, mode.instructions !== false);
|
|
91
|
+
channel?.restrictTools(agent, (tool) => allow.has(tool));
|
|
92
|
+
this.applied.set(agent, {
|
|
93
|
+
mode,
|
|
94
|
+
dispose
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
function apply(ctx) {
|
|
99
|
+
new SessionToolScope(ctx);
|
|
100
|
+
}
|
|
101
|
+
//#endregion
|
|
102
|
+
export { Config, SessionToolScope, apply, inject, name, scope_exports as t };
|
package/locale/en.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"meta": {
|
|
3
3
|
"title": "Context Assembler",
|
|
4
|
-
"description": "Prompt-injection capability set: the single injection channel plus workspace instructions, skill catalog
|
|
4
|
+
"description": "Prompt-injection capability set: the single injection channel plus workspace instructions, skill catalog and the scope gate that narrows tools per session mode."
|
|
5
5
|
}
|
|
6
6
|
}
|
package/locale/zh.json
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@morlay/dsh-context-assembler",
|
|
3
|
-
"version": "0.0.1-alpha.
|
|
4
|
-
"description": "Prompt-injection capability set for dsh: the single channel (context assembler) plus the injectors it carries — workspace instructions, skill catalog
|
|
3
|
+
"version": "0.0.1-alpha.3",
|
|
4
|
+
"description": "Prompt-injection capability set for dsh: the single channel (context assembler) plus the injectors it carries — workspace instructions, skill catalog — plus the scope gate that narrows tools per session mode. Reference expansion is its own package (@morlay/dsh-reference).",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"context",
|
|
7
7
|
"dsh",
|
|
@@ -40,39 +40,39 @@
|
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@deepseek-ai/cordis": "4.0.4",
|
|
42
42
|
"@deepseek-ai/cordis-plugin-include": "1.0.9",
|
|
43
|
-
"@deepseek-ai/dsh-agent": "0.1.7-
|
|
44
|
-
"@deepseek-ai/dsh-agent-instructions": "0.1.7-
|
|
45
|
-
"@deepseek-ai/dsh-agent-loop": "0.1.7-
|
|
46
|
-
"@deepseek-ai/dsh-agent-loop-testkit": "0.1.7-
|
|
47
|
-
"@deepseek-ai/dsh-commands": "0.1.7-
|
|
48
|
-
"@deepseek-ai/dsh-compaction-basic": "0.1.7-
|
|
49
|
-
"@deepseek-ai/dsh-fs": "0.1.7-
|
|
50
|
-
"@deepseek-ai/dsh-llm": "0.1.7-
|
|
51
|
-
"@deepseek-ai/dsh-scope": "0.1.7-
|
|
52
|
-
"@deepseek-ai/dsh-session": "0.1.7-
|
|
53
|
-
"@deepseek-ai/dsh-session-persistence-jsonl": "0.1.7-
|
|
54
|
-
"@deepseek-ai/dsh-skill": "0.1.7-
|
|
55
|
-
"@deepseek-ai/dsh-skill-filesystem": "0.1.7-
|
|
56
|
-
"@deepseek-ai/dsh-system-prompt": "0.1.7-
|
|
57
|
-
"@deepseek-ai/dsh-token-meter": "0.1.7-
|
|
58
|
-
"@deepseek-ai/dsh-tool-skill": "0.1.7-
|
|
59
|
-
"@deepseek-ai/dsh-tools": "0.1.7-
|
|
60
|
-
"@morlay/session-rdb": "0.0.21-alpha.
|
|
43
|
+
"@deepseek-ai/dsh-agent": "0.1.7-rc.1",
|
|
44
|
+
"@deepseek-ai/dsh-agent-instructions": "0.1.7-rc.1",
|
|
45
|
+
"@deepseek-ai/dsh-agent-loop": "0.1.7-rc.1",
|
|
46
|
+
"@deepseek-ai/dsh-agent-loop-testkit": "0.1.7-rc.1",
|
|
47
|
+
"@deepseek-ai/dsh-commands": "0.1.7-rc.1",
|
|
48
|
+
"@deepseek-ai/dsh-compaction-basic": "0.1.7-rc.1",
|
|
49
|
+
"@deepseek-ai/dsh-fs": "0.1.7-rc.1",
|
|
50
|
+
"@deepseek-ai/dsh-llm": "0.1.7-rc.1",
|
|
51
|
+
"@deepseek-ai/dsh-scope": "0.1.7-rc.1",
|
|
52
|
+
"@deepseek-ai/dsh-session": "0.1.7-rc.1",
|
|
53
|
+
"@deepseek-ai/dsh-session-persistence-jsonl": "0.1.7-rc.1",
|
|
54
|
+
"@deepseek-ai/dsh-skill": "0.1.7-rc.1",
|
|
55
|
+
"@deepseek-ai/dsh-skill-filesystem": "0.1.7-rc.1",
|
|
56
|
+
"@deepseek-ai/dsh-system-prompt": "0.1.7-rc.1",
|
|
57
|
+
"@deepseek-ai/dsh-token-meter": "0.1.7-rc.1",
|
|
58
|
+
"@deepseek-ai/dsh-tool-skill": "0.1.7-rc.1",
|
|
59
|
+
"@deepseek-ai/dsh-tools": "0.1.7-rc.1",
|
|
60
|
+
"@morlay/session-rdb": "0.0.21-alpha.8",
|
|
61
61
|
"@types/js-yaml": "^4.0.9",
|
|
62
62
|
"@types/mdast": "^4.0.4",
|
|
63
63
|
"js-yaml": "^4.1.0"
|
|
64
64
|
},
|
|
65
65
|
"peerDependencies": {
|
|
66
66
|
"@deepseek-ai/cordis": "4.0.4",
|
|
67
|
-
"@deepseek-ai/dsh-agent": "0.1.7-
|
|
68
|
-
"@deepseek-ai/dsh-agent-instructions": "0.1.7-
|
|
69
|
-
"@deepseek-ai/dsh-commands": "0.1.7-
|
|
70
|
-
"@deepseek-ai/dsh-fs": "0.1.7-
|
|
71
|
-
"@deepseek-ai/dsh-llm": "0.1.7-
|
|
72
|
-
"@deepseek-ai/dsh-session": "0.1.7-
|
|
73
|
-
"@deepseek-ai/dsh-skill": "0.1.7-
|
|
74
|
-
"@deepseek-ai/dsh-system-prompt": "0.1.7-
|
|
75
|
-
"@deepseek-ai/dsh-tools": "0.1.7-
|
|
67
|
+
"@deepseek-ai/dsh-agent": "0.1.7-rc.1",
|
|
68
|
+
"@deepseek-ai/dsh-agent-instructions": "0.1.7-rc.1",
|
|
69
|
+
"@deepseek-ai/dsh-commands": "0.1.7-rc.1",
|
|
70
|
+
"@deepseek-ai/dsh-fs": "0.1.7-rc.1",
|
|
71
|
+
"@deepseek-ai/dsh-llm": "0.1.7-rc.1",
|
|
72
|
+
"@deepseek-ai/dsh-session": "0.1.7-rc.1",
|
|
73
|
+
"@deepseek-ai/dsh-skill": "0.1.7-rc.1",
|
|
74
|
+
"@deepseek-ai/dsh-system-prompt": "0.1.7-rc.1",
|
|
75
|
+
"@deepseek-ai/dsh-tools": "0.1.7-rc.1"
|
|
76
76
|
},
|
|
77
77
|
"dsh": {
|
|
78
78
|
"bundle": {
|
package/src/index.ts
CHANGED
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
* 组装出口:按 config 把各能力装成一个 cordis 插件树。
|
|
3
3
|
*
|
|
4
4
|
* 装配面因此只有**一行**(`@morlay/dsh-context-assembler`):`isolate` 是行级选项,一行声明就覆盖它整棵子树,
|
|
5
|
-
* 而这里 `ctx.plugin()` 出来的子插件都继承行 ctx 的 isolate map 与 scope
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* 而这里 `ctx.plugin()` 出来的子插件都继承行 ctx 的 isolate map 与 scope——**通道服务由这一行装一次**。
|
|
6
|
+
* **每个子插件各有自己的 `inject`**(合成单入口会让 inject 变并集,一个可选搭档缺席就拖垮整包),这是这层
|
|
7
|
+
* 组装唯一必须守住的东西。
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
9
|
+
* 模式之间的差异(哪个模式要哪些能力、工具收到什么程度)不在这里用 config 裁:由
|
|
10
|
+
* `context-assembler-scope` 那一行把模式定义登记给通道(`options` 只用来给留下的能力传参,能力名就是子出口名)。
|
|
11
11
|
*/
|
|
12
12
|
import type { Context } from "@deepseek-ai/cordis";
|
|
13
13
|
import z from "@deepseek-ai/schemastery";
|
package/src/rows.ts
CHANGED
|
@@ -5,8 +5,9 @@
|
|
|
5
5
|
* (`@morlay/dsh-agent-toolkit/guidance`,在别的包里)与 skill 目录这类消费者都在别的行上
|
|
6
6
|
* `inject` 它,隔离会把它们挡在组外(行停在 waiting,不报错)。
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
8
|
+
* 本包自己的 bundle 只装通道(`PATCH_ROWS`)。模式的收口是另一行(`scopeRow()`),它由
|
|
9
|
+
* [`@morlay/dsh-session-mode`](../../../profile/dsh-session-mode/README.md) 的 patch 与模式定义一起装——
|
|
10
|
+
* 那一行的 config 就是模式清单。
|
|
10
11
|
*/
|
|
11
12
|
|
|
12
13
|
/** 组装出口(包根)就是那个"按 config 装能力"的插件。 */
|
|
@@ -32,25 +33,25 @@ export function contextChannel(config?: Readonly<Record<string, unknown>>): Cont
|
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
/**
|
|
35
|
-
*
|
|
36
|
+
* 模式收口那一行:`scope` 出口按 `ctx.sessionModes` 的模式定义收口(工具白名单、instruction 总开关、
|
|
37
|
+
* 动态快照开关)。
|
|
36
38
|
*
|
|
37
|
-
*
|
|
38
|
-
* `ctx.
|
|
39
|
+
* 行 id 用全名 `context-assembler-scope`:它的搭档是同一份装配里的 `context-assembler`(通道),两个 id 放在
|
|
40
|
+
* 一起才看得出这一行属于哪个包、做什么——`context-scope` 那种短名会被误当成 `ctx.contextScope` 之类的服务。
|
|
41
|
+
*
|
|
42
|
+
* 行本身**不带 config**:装哪些模式、每个模式收什么,都由 `session-mode` 行的 `config.modes` 决定,
|
|
43
|
+
* 这一行只把它落到会话上(`inject` `sessionModes`,由
|
|
44
|
+
* [`@morlay/dsh-session-mode`](../../../profile/dsh-session-mode/README.md) 的 patch 插一行)。
|
|
39
45
|
*/
|
|
40
|
-
export function scopeRow(
|
|
41
|
-
readonly id: string;
|
|
42
|
-
readonly name: string;
|
|
43
|
-
readonly config?: Readonly<Record<string, unknown>>;
|
|
44
|
-
} {
|
|
46
|
+
export function scopeRow(): ContextRow {
|
|
45
47
|
return {
|
|
46
|
-
id: "context-scope",
|
|
48
|
+
id: "context-assembler-scope",
|
|
47
49
|
name: "@morlay/dsh-context-assembler/scope",
|
|
48
|
-
...(config === undefined ? {} : { config }),
|
|
49
50
|
};
|
|
50
51
|
}
|
|
51
52
|
|
|
52
53
|
/**
|
|
53
|
-
* bundle patch
|
|
54
|
+
* bundle patch 的行清单:只有通道一行(模式收口那一行归 `session-mode` 的 patch 装)。
|
|
54
55
|
*
|
|
55
56
|
* 形态是 `insert`:这些行由本包提供给 host 平面。写成 `- id: x` 那种"改已有行"的形态时,装配期找不到
|
|
56
57
|
* 目标行(只 warn 后跳过),通道根本没装上,引用它的行停在 waiting。
|
package/src/scope/index.ts
CHANGED
|
@@ -1,118 +1,150 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* 按**会话**收口:工具白名单、instruction 总开关、动态快照。
|
|
3
|
+
*
|
|
4
|
+
* 这是本包的 `scope` 出口(行 id `context-assembler-scope`)。它不再吃 config,也不认识"模式"这个概念——
|
|
5
|
+
* 收口要什么由消费方在会话建立时**推**进来:`ctx.sessionToolScope.apply(agent, mode)`(
|
|
6
|
+
* [`@morlay/dsh-session-mode`](../../../profile/dsh-session-mode/README.md) 的模式定义里正好是这几项)。
|
|
7
|
+
* 方向因此是单向的:装配层读这个服务,这个包不认识装配层。
|
|
8
|
+
*
|
|
9
|
+
* `apply` 落到该 agent 身上的三件事,各走一条与"谁注册了这个工具"无关的路:
|
|
10
|
+
*
|
|
11
|
+
* 1. **装配期投影**(每次装配按已注册的那份):模型目录与工具说明的 `tool:<工具名>` section 同源过滤;
|
|
12
|
+
* 2. **执行层 guard**:落在 `agent.ctx` 上,调用被拒时给出该模式的文案;
|
|
13
|
+
* 3. **通道开关**:`instructions` 关掉该会话的规则块(工作区指令、技能目录、用法正文)、
|
|
14
|
+
* `runtimeContext` 抑制该会话的动态快照(沙箱与审批策略那两条)。
|
|
15
|
+
*
|
|
16
|
+
* 为什么不用 `tools.restrict()`:它会改动可见工具集,从而触发 `tools/change`;上游 `tool-subagent` 正是用
|
|
17
|
+
* 该事件做 composition reconcile——两边互相触发会变成装配风暴。这里用投影过滤加执行层 guard:既不碰注册
|
|
18
|
+
* 表、也不发事件。
|
|
19
|
+
*
|
|
20
|
+
* 为什么按 `agent.ctx` 注册而不是先建一棵 preset 子树:会话的 persona / 工具收口本来就是"这个 agent 的
|
|
21
|
+
* 事",`agent.ctx` 的 scope 已经够了(上游给子 agent 装 persona 用的也是这条路);一棵子树要连带上游的
|
|
22
|
+
* `agent-presets` 注册表、每 revision 一份 Loader 与 `isolate` realm,模式本身用不上。
|
|
23
|
+
*
|
|
24
|
+
* 模式在空白窗口里可以切换,所以状态是**可替换**的:同一个 agent 再 `apply` 一次就是换一份——旧的
|
|
25
|
+
* disposer 全部收回,再按新的装一遍。
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
import { Service, type Context } from "@deepseek-ai/cordis";
|
|
2
29
|
import type { Agent } from "@deepseek-ai/dsh-agent";
|
|
3
|
-
import { scopeChainOf, scopeOf } from "@deepseek-ai/dsh-scope";
|
|
4
30
|
import z from "@deepseek-ai/schemastery";
|
|
5
31
|
import type {} from "../assembler/index.ts";
|
|
6
32
|
|
|
7
|
-
export const name = "context-scope";
|
|
33
|
+
export const name = "context-assembler-scope";
|
|
8
34
|
|
|
35
|
+
/** 收口要的服务;`sessionToolScope` 由本行自己发布,`tools` 的 guard 挂在每个 agent 的 ctx 上。 */
|
|
9
36
|
export const inject = ["tools", "systemPrompt"];
|
|
10
37
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
* 这里是"连与工具无关的 instruction 也不要"的总开关。
|
|
25
|
-
*/
|
|
26
|
-
instructions?: boolean;
|
|
27
|
-
/**
|
|
28
|
-
* 是否要 runtime context(动态快照:文件沙箱策略、审批策略)。缺省要;`false` 表示这个模式不要它们
|
|
29
|
-
* ——对话模式没有文件与 shell 工具,"能改工作区哪些文件、要不要走审批"对它全是噪音。
|
|
30
|
-
*
|
|
31
|
-
* 抑制是**按 scope** 的(上游按装配的 scope 链查抑制器),所以本行装在 preset 子树里就只作用于这个
|
|
32
|
-
* 模式的会话;它只挡注入,不改任何提供方的行为(沙箱该怎么判还怎么判)。
|
|
33
|
-
*/
|
|
34
|
-
runtimeContext?: boolean;
|
|
38
|
+
/** 本行不吃 config:要收成什么样由消费方在 `apply` 时给。 */
|
|
39
|
+
export const Config = z.object({});
|
|
40
|
+
|
|
41
|
+
/** 一次收口的全部输入:模式定义里本出口用到的那几项。 */
|
|
42
|
+
export interface SessionToolScopeMode {
|
|
43
|
+
/** 拒绝对话时用的模式名。 */
|
|
44
|
+
readonly name: string;
|
|
45
|
+
/** 这个会话能用的工具;其余既不进目录,调用也被拒。 */
|
|
46
|
+
readonly allowTools: readonly string[];
|
|
47
|
+
/** 缺省为要;`false` 表示这个会话不要任何 instruction 类注入。 */
|
|
48
|
+
readonly instructions?: boolean | undefined;
|
|
49
|
+
/** 缺省为要;`false` 表示这个会话不要动态快照。 */
|
|
50
|
+
readonly runtimeContext?: boolean | undefined;
|
|
35
51
|
}
|
|
36
52
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}
|
|
53
|
+
declare module "@deepseek-ai/cordis" {
|
|
54
|
+
interface Context {
|
|
55
|
+
sessionToolScope: SessionToolScope;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
42
58
|
|
|
43
|
-
const OUT_OF_SCOPE = (toolName: string): string =>
|
|
44
|
-
`${toolName}
|
|
59
|
+
const OUT_OF_SCOPE = (toolName: string, modeName: string): string =>
|
|
60
|
+
`${toolName} 不在「${modeName}」的工具范围内,用它不会有结果;按当前模式提供的工具完成任务,或让用户切到别的模式。`;
|
|
45
61
|
|
|
46
62
|
/** 单个工具的说明 section 名前缀:`tool:<工具名>`(`tools:` 那类是聚合块,不归白名单管)。 */
|
|
47
63
|
const TOOL_SECTION_PREFIX = "tool:";
|
|
48
64
|
|
|
49
|
-
/**
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
*
|
|
56
|
-
* 为什么不是 `tools.restrict()`:它会改动可见工具集,从而触发 `tools/change`;上游 `tool-subagent`
|
|
57
|
-
* 正是用该事件做 composition reconcile——两边互相触发会变成装配风暴。这里用投影过滤加执行层 guard:
|
|
58
|
-
* 既不碰注册表、也不发事件。
|
|
59
|
-
*/
|
|
60
|
-
export function apply(ctx: Context, config: Config): void {
|
|
61
|
-
const allow = new Set(config.allowTools ?? []);
|
|
62
|
-
if (allow.size === 0) {
|
|
63
|
-
throw new Error(
|
|
64
|
-
"context-scope: `allowTools` must name at least one tool; omit the row instead of mounting an empty scope",
|
|
65
|
-
);
|
|
66
|
-
}
|
|
67
|
-
const presetKey = scopeOf(ctx);
|
|
68
|
-
const scoped = new WeakSet<Agent>();
|
|
65
|
+
/** 一个 agent 当前装着的那一份。 */
|
|
66
|
+
interface Applied {
|
|
67
|
+
readonly mode: SessionToolScopeMode;
|
|
68
|
+
/** 收回这一份在 `agent.ctx` 上的注册(抑制器与执行层 guard)。 */
|
|
69
|
+
readonly dispose: () => void;
|
|
70
|
+
}
|
|
69
71
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
72
|
+
/** 通道是可选的搭档:没有它就没有 instruction 可关、也没人收窄技能目录,工具目录与执行 guard 照常生效。 */
|
|
73
|
+
function channelOf(ctx: Context):
|
|
74
|
+
| {
|
|
75
|
+
setInstructions(agent: Agent, on: boolean): void;
|
|
76
|
+
restrictTools(agent: Agent, allowed: (tool: string) => boolean): void;
|
|
77
|
+
}
|
|
78
|
+
| undefined {
|
|
79
|
+
try {
|
|
80
|
+
return ctx.get("contextAssembler");
|
|
81
|
+
} catch {
|
|
82
|
+
return undefined;
|
|
73
83
|
}
|
|
84
|
+
}
|
|
74
85
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
86
|
+
/** 按会话收口工具与注入的服务。 */
|
|
87
|
+
export class SessionToolScope extends Service {
|
|
88
|
+
private readonly applied = new WeakMap<Agent, Applied>();
|
|
89
|
+
|
|
90
|
+
constructor(ctx: Context) {
|
|
91
|
+
super(ctx, "sessionToolScope");
|
|
92
|
+
// 装配期过滤:这里只认已经 `apply` 过的会话;没登记过的(没装 session-mode 的部署)一律放行。
|
|
93
|
+
ctx.on("system-prompt/assemble", async (_assembly, context, next) => {
|
|
94
|
+
const agent = context.agent;
|
|
95
|
+
const applied = agent === undefined ? undefined : this.applied.get(agent);
|
|
96
|
+
if (applied === undefined) return next();
|
|
97
|
+
const allow = new Set(applied.mode.allowTools);
|
|
98
|
+
const result = await next();
|
|
99
|
+
return {
|
|
100
|
+
...result,
|
|
101
|
+
// 工具自己的说明 section 与工具目录同源:目录里没有的工具,它的说明也不该留在提示词里。
|
|
102
|
+
sections: result.sections.filter((section) =>
|
|
103
|
+
section.name.startsWith(TOOL_SECTION_PREFIX)
|
|
104
|
+
? allow.has(section.name.slice(TOOL_SECTION_PREFIX.length))
|
|
105
|
+
: true,
|
|
106
|
+
),
|
|
107
|
+
tools: result.tools.filter((tool) => allow.has(tool.name)),
|
|
108
|
+
};
|
|
98
109
|
});
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
)
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* 把某个会话收口到这份定义上(同一个 agent 再调就是换一份)。
|
|
114
|
+
* @param agent - 目标会话的 agent。
|
|
115
|
+
* @param mode - 收口的全部输入。
|
|
116
|
+
*/
|
|
117
|
+
apply(agent: Agent, mode: SessionToolScopeMode): void {
|
|
118
|
+
const previous = this.applied.get(agent);
|
|
119
|
+
if (previous !== undefined) previous.dispose();
|
|
120
|
+
const allow = new Set(mode.allowTools);
|
|
121
|
+
|
|
122
|
+
// 落在 `agent.ctx` 上的那两件用一个 effect 装:抑制器是 scope 层的一次注册;执行层 guard 要等
|
|
123
|
+
// `tools` 激活才装得上(`inject` 的回调),它挂在那个 inject fiber 下,所以收回时连 fiber 一起收。
|
|
124
|
+
const dispose = agent.ctx.effect(() => {
|
|
125
|
+
const stoppers: (() => void)[] = [];
|
|
126
|
+
if (mode.runtimeContext === false) {
|
|
127
|
+
// service 在 `agent.ctx` 上是 shadow:抑制落在该 agent 的 scope 层,只覆盖这个会话。
|
|
128
|
+
stoppers.push(agent.ctx.systemPrompt.suppressRuntimeContext());
|
|
129
|
+
}
|
|
130
|
+
// 必须落在 `agent.ctx` 上才是这个会话的作用域。
|
|
131
|
+
const fiber = agent.ctx.inject(["tools"], (scope) => {
|
|
132
|
+
scope.tools.guard((exec) =>
|
|
133
|
+
allow.has(exec.name) ? undefined : OUT_OF_SCOPE(exec.name, mode.name),
|
|
134
|
+
);
|
|
135
|
+
});
|
|
136
|
+
return [...stoppers, () => fiber.dispose()];
|
|
137
|
+
}, "sessionToolScope: per-session gate");
|
|
138
|
+
|
|
139
|
+
// 与"谁注册了这个工具"无关的那两件:通道按 agent 覆盖,不需要收回。
|
|
140
|
+
const channel = channelOf(this.ctx);
|
|
141
|
+
channel?.setInstructions(agent, mode.instructions !== false);
|
|
142
|
+
channel?.restrictTools(agent, (tool) => allow.has(tool));
|
|
143
|
+
|
|
144
|
+
this.applied.set(agent, { mode, dispose });
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export function apply(ctx: Context): void {
|
|
149
|
+
new SessionToolScope(ctx);
|
|
118
150
|
}
|
package/dist/scope-CI-fNq5A.mjs
DELETED
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
|
|
2
|
-
import z from "@deepseek-ai/schemastery";
|
|
3
|
-
import "@deepseek-ai/cordis";
|
|
4
|
-
//#region ../../../vendor/deepseek-harness/packages/core/scope/lib/index.js
|
|
5
|
-
/**
|
|
6
|
-
* Scoped-context primitive: mint a Cordis context that tags registrations with
|
|
7
|
-
* an opaque identity and build routing-only event carriers for that identity.
|
|
8
|
-
*
|
|
9
|
-
* @module @deepseek-ai/dsh-scope
|
|
10
|
-
*/
|
|
11
|
-
/** Context tag written by {@link createScope}. */
|
|
12
|
-
const kScope = Symbol("dsh.scope");
|
|
13
|
-
/**
|
|
14
|
-
* The enclosing scope of each key. One relation powers both directions of
|
|
15
|
-
* scope nesting: registration views inherit DOWN the chain (a child scope
|
|
16
|
-
* sees its ancestors' layers — {@link ScopedLayers}), and event admission
|
|
17
|
-
* extends UP it (a listener tagged with an ancestor receives events dispatched
|
|
18
|
-
* to a descendant key — {@link scopeTarget}).
|
|
19
|
-
*/
|
|
20
|
-
const scopeParents = /* @__PURE__ */ new WeakMap();
|
|
21
|
-
/**
|
|
22
|
-
* The chain from a key to its root ancestor.
|
|
23
|
-
* @param key - the starting key, or `undefined` for the empty chain.
|
|
24
|
-
* @returns keys nearest-first: `[key, parent, grandparent, …]`.
|
|
25
|
-
*/
|
|
26
|
-
function scopeChainOf(key) {
|
|
27
|
-
const chain = [];
|
|
28
|
-
for (let cursor = key; cursor !== void 0; cursor = scopeParents.get(cursor)) chain.push(cursor);
|
|
29
|
-
return chain;
|
|
30
|
-
}
|
|
31
|
-
/**
|
|
32
|
-
* Read the nearest scope tag inherited by a context.
|
|
33
|
-
* @param ctx - context to inspect.
|
|
34
|
-
* @returns its scope key, or `undefined` for an unscoped context.
|
|
35
|
-
*/
|
|
36
|
-
function scopeOf(ctx) {
|
|
37
|
-
return ctx[kScope];
|
|
38
|
-
}
|
|
39
|
-
//#endregion
|
|
40
|
-
//#region src/scope/index.ts
|
|
41
|
-
var scope_exports = /* @__PURE__ */ __exportAll({
|
|
42
|
-
Config: () => Config,
|
|
43
|
-
apply: () => apply,
|
|
44
|
-
inject: () => inject,
|
|
45
|
-
name: () => name
|
|
46
|
-
});
|
|
47
|
-
const name = "context-scope";
|
|
48
|
-
const inject = ["tools", "systemPrompt"];
|
|
49
|
-
const Config = z.object({
|
|
50
|
-
allowTools: z.array(z.string()).default([]),
|
|
51
|
-
instructions: z.boolean().default(true),
|
|
52
|
-
runtimeContext: z.boolean().default(true)
|
|
53
|
-
});
|
|
54
|
-
const OUT_OF_SCOPE = (toolName) => `${toolName} 不在本模式的工具范围内,用它不会有结果;按当前模式提供的工具完成任务,或让用户切到别的模式。`;
|
|
55
|
-
/** 单个工具的说明 section 名前缀:`tool:<工具名>`(`tools:` 那类是聚合块,不归白名单管)。 */
|
|
56
|
-
const TOOL_SECTION_PREFIX = "tool:";
|
|
57
|
-
/**
|
|
58
|
-
* 把某个 preset 的工具收成"只有这些"。
|
|
59
|
-
*
|
|
60
|
-
* 为什么需要它:preset composition 只能决定"加什么",管不了 host 层——`dsh.profile.bundles` 打开的
|
|
61
|
-
* bundle(例如实验性的 Agent Teams)会在 host 层插工具行与 section,对所有 preset 一视同仁。模式想
|
|
62
|
-
* 表达"我只有这几个工具、一条提示词都不要"时,唯一与来源无关的做法是收口"有什么"。
|
|
63
|
-
*
|
|
64
|
-
* 为什么不是 `tools.restrict()`:它会改动可见工具集,从而触发 `tools/change`;上游 `tool-subagent`
|
|
65
|
-
* 正是用该事件做 composition reconcile——两边互相触发会变成装配风暴。这里用投影过滤加执行层 guard:
|
|
66
|
-
* 既不碰注册表、也不发事件。
|
|
67
|
-
*/
|
|
68
|
-
function apply(ctx, config) {
|
|
69
|
-
const allow = new Set(config.allowTools ?? []);
|
|
70
|
-
if (allow.size === 0) throw new Error("context-scope: `allowTools` must name at least one tool; omit the row instead of mounting an empty scope");
|
|
71
|
-
const presetKey = scopeOf(ctx);
|
|
72
|
-
const scoped = /* @__PURE__ */ new WeakSet();
|
|
73
|
-
if (config.runtimeContext === false) ctx.systemPrompt.suppressRuntimeContext();
|
|
74
|
-
const belongs = (agent) => {
|
|
75
|
-
if (presetKey === void 0) return true;
|
|
76
|
-
const key = scopeOf(agent.ctx);
|
|
77
|
-
return key !== void 0 && scopeChainOf(key).includes(presetKey);
|
|
78
|
-
};
|
|
79
|
-
/** 每个会话只做一次:拨提示词开关,并挂上执行层白名单。 */
|
|
80
|
-
const scopeOnce = (agent) => {
|
|
81
|
-
if (scoped.has(agent) || !belongs(agent)) return;
|
|
82
|
-
scoped.add(agent);
|
|
83
|
-
try {
|
|
84
|
-
const channel = ctx.get("contextAssembler");
|
|
85
|
-
if (config.instructions === false) channel?.setInstructions(agent, false);
|
|
86
|
-
channel?.restrictTools(agent, (tool) => allow.has(tool));
|
|
87
|
-
} catch {}
|
|
88
|
-
agent.ctx.inject(["tools"], () => {
|
|
89
|
-
agent.ctx.tools.guard((exec) => allow.has(exec.name) ? void 0 : OUT_OF_SCOPE(exec.name));
|
|
90
|
-
});
|
|
91
|
-
};
|
|
92
|
-
ctx.on("system-prompt/assemble", async (_assembly, context, next) => {
|
|
93
|
-
const agent = context.agent;
|
|
94
|
-
if (agent !== void 0) scopeOnce(agent);
|
|
95
|
-
const result = await next();
|
|
96
|
-
return {
|
|
97
|
-
...result,
|
|
98
|
-
sections: result.sections.filter((section) => section.name.startsWith(TOOL_SECTION_PREFIX) ? allow.has(section.name.slice(5)) : true),
|
|
99
|
-
tools: result.tools.filter((tool) => allow.has(tool.name))
|
|
100
|
-
};
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
|
-
//#endregion
|
|
104
|
-
export { scope_exports as a, name as i, apply as n, inject as r, Config as t };
|