@morlay/dsh-subagent 0.0.5 → 0.0.7

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@morlay/dsh-subagent",
3
- "version": "0.0.5",
3
+ "version": "0.0.7",
4
4
  "description": "Thin-shell fork of @deepseek-ai/dsh-subagent: takes over the official `subagent` row for two wording changes — the continuable child's return guidance in Chinese wording and the child's Chinese delegation-scope runtime context (`subagent:delegation`) — plus the row-id reuse (same `subagent` id, so the official settings card stays); everything not intentionally changed is imported from the vendored upstream source.",
5
5
  "keywords": [
6
6
  "dsh",
@@ -14,49 +14,57 @@
14
14
  "url": "https://github.com/morlay/dsh-plugin.git"
15
15
  },
16
16
  "files": [
17
+ "src",
17
18
  "locale/*.json",
18
19
  "dist",
19
- "!**/__tests__"
20
+ "!**/__tests__",
21
+ "!**/vendor/**/__tests__"
20
22
  ],
21
23
  "type": "module",
22
24
  "exports": {
23
- ".": "./dist/index.mjs",
24
- "./rows": "./dist/rows.mjs",
25
+ ".": {
26
+ "types": "./dist/index.d.mts",
27
+ "default": "./dist/index.mjs"
28
+ },
29
+ "./rows": {
30
+ "types": "./dist/rows.d.mts",
31
+ "default": "./dist/rows.mjs"
32
+ },
25
33
  "./package.json": "./package.json",
26
34
  "./locale/*.json": "./locale/*.json"
27
35
  },
28
36
  "dependencies": {
29
- "@deepseek-ai/dsh-brand": "^0.2.0-rc.2",
30
- "@deepseek-ai/schemastery": "~3.18.4"
37
+ "@deepseek-ai/dsh-brand": "^0.2.1-alpha.5",
38
+ "@deepseek-ai/schemastery": "~3.18.5-alpha.2"
31
39
  },
32
40
  "devDependencies": {
33
- "@deepseek-ai/dsh-agent-loop": "0.2.0-rc.2",
34
- "@deepseek-ai/dsh-agent-loop-testkit": "0.2.0-rc.2",
35
- "@deepseek-ai/dsh-chunked-list": "0.2.0-rc.2",
36
- "@deepseek-ai/dsh-invariants": "0.2.0-rc.2",
37
- "@deepseek-ai/dsh-session-persistence-jsonl": "0.2.0-rc.2",
38
- "@deepseek-ai/dsh-subagent-fork-in-process": "0.2.0-rc.2",
39
- "@deepseek-ai/dsh-subagent-spawn-in-process": "0.2.0-rc.2",
40
- "@deepseek-ai/dsh-system-prompt": "0.2.0-rc.2",
41
- "@deepseek-ai/dsh-tool-subagent-control": "0.2.0-rc.2",
42
- "@deepseek-ai/dsh-util-values": "0.2.0-rc.2",
43
- "@deepseek-ai/schemastery": "~3.18.4",
41
+ "@deepseek-ai/dsh-agent-loop": "0.2.1-alpha.5",
42
+ "@deepseek-ai/dsh-agent-loop-testkit": "0.2.1-alpha.5",
43
+ "@deepseek-ai/dsh-chunked-list": "0.2.1-alpha.5",
44
+ "@deepseek-ai/dsh-session-persistence-jsonl": "0.2.1-alpha.5",
45
+ "@deepseek-ai/dsh-subagent-fork-in-process": "0.2.1-alpha.5",
46
+ "@deepseek-ai/dsh-subagent-spawn-in-process": "0.2.1-alpha.5",
47
+ "@deepseek-ai/dsh-tool-subagent-control": "0.2.1-alpha.5",
48
+ "@deepseek-ai/dsh-util-values": "0.2.1-alpha.5",
49
+ "@deepseek-ai/schemastery": "~3.18.5-alpha.2",
44
50
  "zod": "^4.4.3"
45
51
  },
46
52
  "peerDependencies": {
47
- "@deepseek-ai/cordis": "~4.0.4",
48
- "@deepseek-ai/dsh-agent": "^0.2.0-rc.2",
49
- "@deepseek-ai/dsh-agent-preset-registry": "^0.2.0-rc.2",
50
- "@deepseek-ai/dsh-attachment": "^0.2.0-rc.2",
51
- "@deepseek-ai/dsh-llm": "^0.2.0-rc.2",
52
- "@deepseek-ai/dsh-scope": "^0.2.0-rc.2",
53
- "@deepseek-ai/dsh-session": "^0.2.0-rc.2",
54
- "@deepseek-ai/dsh-session-persistence": "^0.2.0-rc.2",
55
- "@deepseek-ai/dsh-session-query": "^0.2.0-rc.2",
56
- "@deepseek-ai/dsh-subagent": "^0.2.0-rc.2",
57
- "@deepseek-ai/dsh-tools": "^0.2.0-rc.2",
58
- "@deepseek-ai/dsh-typert-protocol": "^0.2.0-rc.2",
59
- "@deepseek-ai/dsh-util-time": "^0.2.0-rc.2"
53
+ "@deepseek-ai/cordis": "~4.0.5-alpha.2",
54
+ "@deepseek-ai/dsh-agent": "^0.2.1-alpha.5",
55
+ "@deepseek-ai/dsh-agent-preset-registry": "^0.2.1-alpha.5",
56
+ "@deepseek-ai/dsh-attachment": "^0.2.1-alpha.5",
57
+ "@deepseek-ai/dsh-llm": "^0.2.1-alpha.5",
58
+ "@deepseek-ai/dsh-scope": "^0.2.1-alpha.5",
59
+ "@deepseek-ai/dsh-session": "^0.2.1-alpha.5",
60
+ "@deepseek-ai/dsh-session-persistence": "^0.2.1-alpha.5",
61
+ "@deepseek-ai/dsh-session-query": "^0.2.1-alpha.5",
62
+ "@deepseek-ai/dsh-subagent": "^0.2.1-alpha.5",
63
+ "@deepseek-ai/dsh-system-prompt": "0.2.1-alpha.5",
64
+ "@deepseek-ai/dsh-tools": "^0.2.1-alpha.5",
65
+ "@deepseek-ai/dsh-typert-protocol": "^0.2.1-alpha.5",
66
+ "@deepseek-ai/dsh-util-time": "^0.2.1-alpha.5",
67
+ "@deepseek-ai/dsh-working-directory": "^0.2.1-alpha.5"
60
68
  },
61
69
  "peerDependenciesMeta": {
62
70
  "@deepseek-ai/dsh-session-persistence": {
@@ -0,0 +1,185 @@
1
+ /**
2
+ * Model-visible messages owned by continuable-subagent orchestration.
3
+ *
4
+ * @module @deepseek-ai/dsh-subagent/continuation-messages
5
+ */
6
+
7
+ import type { Agent } from '@deepseek-ai/dsh-agent'
8
+ import { boundContextSummary, createUserMessage } from '@deepseek-ai/dsh-llm'
9
+ import type { ContentBlock } from '@deepseek-ai/dsh-llm'
10
+ import type { SessionId } from '@deepseek-ai/dsh-session'
11
+ import type { SubagentResult } from './vendor/dsh-subagent/types.ts'
12
+
13
+ /** Durable attribution for one model-authored message between Agents. */
14
+ export interface AgentMessageSource {
15
+ readonly kind: 'agent-message'
16
+ /** A message another agent addressed to this one (`relay` context form). */
17
+ readonly form: 'relay'
18
+ /** Session id of the Agent whose tool call produced the message. */
19
+ readonly senderSessionId: SessionId
20
+ }
21
+
22
+ /**
23
+ * Durable attribution for the runtime's own account of a continuable child
24
+ * settling. Deliberately a different kind from
25
+ * {@link AgentMessageSource}: an Agent message is content the sender chose,
26
+ * while this message is the manager stating what became of the child, and a
27
+ * transcript that merged them would credit the child with words it never wrote.
28
+ */
29
+ export interface SubagentSettledMessageSource {
30
+ readonly kind: 'subagent-settled'
31
+ /** A runtime account shown without expanding the row (`notice` context form). */
32
+ readonly form: 'notice'
33
+ /** One-line account of how the child ended. */
34
+ readonly summary: string
35
+ /** Session id of the child that settled. */
36
+ readonly senderSessionId: SessionId
37
+ }
38
+
39
+ declare module '@deepseek-ai/dsh-llm' {
40
+ interface MessageSourceMap {
41
+ 'agent-message': AgentMessageSource
42
+ 'subagent-settled': SubagentSettledMessageSource
43
+ }
44
+ }
45
+
46
+ /** Build durable attribution for one adjacent-Agent message. */
47
+ function agentMessageSource(sender: Agent): AgentMessageSource {
48
+ return {
49
+ kind: 'agent-message',
50
+ form: 'relay',
51
+ senderSessionId: sender.id,
52
+ }
53
+ }
54
+
55
+ /**
56
+ * Build the model-visible and durable representation of one adjacent-Agent message.
57
+ * @param sender - exact live Agent that authored the message.
58
+ * @param content - model-visible message blocks supplied by the sender.
59
+ * @returns the durable user-message representation delivered to the recipient.
60
+ */
61
+ export function createAgentMessage(
62
+ sender: Agent,
63
+ content: ContentBlock[],
64
+ ): ReturnType<typeof createUserMessage> {
65
+ return createUserMessage({
66
+ content: [
67
+ { type: 'text' as const, text: `Agent ${sender.id} sent a message: ` },
68
+ ...content,
69
+ ],
70
+ source: agentMessageSource(sender),
71
+ })
72
+ }
73
+
74
+ /**
75
+ * Whether this session's return guidance takes this package's Chinese wording.
76
+ *
77
+ * **默认对任意 preset 生效**:名单(本行的 `config.localizedReturnGuidancePresets`)不配就是不限制——官方四个
78
+ * shipped preset、部署自建的、以及还没绑 preset 的会话(不装 registry 的部署、还没 join 的 agent)都拿中文。
79
+ * 配了名单才反过来收窄成"只有这些 preset 的会话用中文"。文案是模型可见的东西,判据留在 host 平面
80
+ * (`subagents` 是进程单例,这一行不能搬进 preset realm)。
81
+ *
82
+ * @param composedPreset - the preset id the session is mounted on, if any.
83
+ * @param configured - preset ids this deployment restricts the Chinese wording to; empty means every session.
84
+ * @returns whether the Chinese guidance applies to this session.
85
+ */
86
+ export function localizedReturnGuidance(
87
+ composedPreset: string | undefined,
88
+ configured: readonly string[],
89
+ ): boolean {
90
+ if (configured.length === 0) return true
91
+ return composedPreset !== undefined && configured.includes(composedPreset)
92
+ }
93
+ /**
94
+ * Append adjacent-Agent return guidance to a continuable child's initial task.
95
+ * @param parentId - durable parent session id named in the guidance.
96
+ * @param prompt - initial model-visible task blocks.
97
+ * @returns task blocks followed by the continuable return guidance.
98
+ */
99
+ export function withContinuableReturnGuidance(
100
+ parentId: SessionId,
101
+ prompt: ContentBlock[],
102
+ ): ContentBlock[] {
103
+ const encodedParentId = JSON.stringify(parentId)
104
+ return [
105
+ ...prompt,
106
+ {
107
+ type: 'text',
108
+ text: `你的父智能体 id 是 ${encodedParentId}。完成前用 send_message({ agent_id: ${encodedParentId}, `
109
+ + 'message: "<自包含的结论>" }) 把结果发给它。父智能体与你共享工作区,但不会自动收到你的对话、'
110
+ + '工具输出与推理过程。发现会改变父智能体下一步行动的事情时也尽早再发一条;发消息不会结束你的回合。',
111
+ },
112
+ ]
113
+ }
114
+
115
+ /**
116
+ * One line telling a parent that a background child is finished and why, in
117
+ * the parent's own task vocabulary.
118
+ * @param childId - the durable child the parent knows by id.
119
+ * @param stopReason - how the child's last ordinary turn ended.
120
+ * @param continuable - whether the child accepts a later message.
121
+ * @returns the model-facing opening line of the settlement notice.
122
+ */
123
+ function settlementSummary(childId: SessionId, stopReason: SubagentResult['stopReason'], continuable: boolean): string {
124
+ const subject = `Background subagent ${childId}`
125
+ switch (stopReason) {
126
+ case 'completed':
127
+ return continuable
128
+ ? `${subject} finished and will do no further work unless you send it more.`
129
+ : `${subject} finished. It cannot receive follow-up messages.`
130
+ case 'aborted':
131
+ return `${subject} was stopped before it finished.`
132
+ case 'max-tokens':
133
+ return `${subject} ran out of room before it finished.`
134
+ // A pre-step rejection — a hook deny, a policy plugin — discarded input
135
+ // the child had claimed, so the parent must not treat the task as done.
136
+ case 'refusal':
137
+ return `${subject} declined the task.`
138
+ case 'error':
139
+ return `${subject} failed before it finished.`
140
+ /* v8 ignore next 4 -- `SubagentResult['stopReason']` is merge-extensible, so this arm
141
+ * needs a backend that adds a variant; an unnameable ending is reported as unfinished
142
+ * rather than silently as success. */
143
+ default:
144
+ return `${subject} ended abnormally (${String(stopReason)}) before it finished.`
145
+ }
146
+ }
147
+
148
+ /**
149
+ * Build the runtime-owned settlement notice from the child's nonempty closing text.
150
+ * @param childId - durable child session id named in the notice.
151
+ * @param terminal - recorded terminal state for the settled Activation.
152
+ * @param continuable - whether the child can accept another task.
153
+ * @returns the durable user-message representation delivered to the parent.
154
+ */
155
+ export function createSettlementMessage(
156
+ childId: SessionId,
157
+ terminal: SubagentResult,
158
+ continuable = true,
159
+ ): ReturnType<typeof createUserMessage> {
160
+ const summary = settlementSummary(childId, terminal.stopReason, continuable)
161
+ // Parent providers receive this notice as a user message and may reject
162
+ // nontext assistant blocks. Keep this conversion local so SDK/UI consumers
163
+ // retain the complete child output.
164
+ const closingText = terminal.output.flatMap(block =>
165
+ block.type === 'text' && block.text.length > 0 ? [block] : [],
166
+ )
167
+ return createUserMessage({
168
+ content: [
169
+ { type: 'text' as const, text: summary },
170
+ ...closingText.length === 0
171
+ ? [{ type: 'text' as const, text: 'It left no closing message.' }]
172
+ : [{ type: 'text' as const, text: 'Its closing message:' }, ...closingText],
173
+ ...terminal.structured !== undefined
174
+ ? [{ type: 'text' as const, text: `Structured result: ${JSON.stringify(terminal.structured)}` }] : [],
175
+ ...terminal.diagnostic !== undefined
176
+ ? [{ type: 'text' as const, text: terminal.diagnostic }] : [],
177
+ ],
178
+ source: {
179
+ kind: 'subagent-settled' as const,
180
+ form: 'notice' as const,
181
+ summary: boundContextSummary(summary),
182
+ senderSessionId: childId,
183
+ },
184
+ })
185
+ }
@@ -0,0 +1,39 @@
1
+ // 汉化 `subagent:delegation`:上游在**子代理自己的 agent 作用域**上注册这条运行时上下文
2
+ // (`child-agent.ts` 的 `applyChildComposition`),于是两条常规接管手法都不通:
3
+ //
4
+ // - **同名遮蔽**:同一 scope 里重复注册同名 context 会抛错(`NamedEntries.insert` 报
5
+ // `prompt context "subagent:delegation" is already registered in this scope`),而装配用的 scope 就是子代理自己
6
+ // (`assembleContextFor(agent)` → `scope: agent`),与上游那次注册同层;
7
+ // - **fork `child-agent.ts`**:注册这条文本的调用方现在只剩本包已复制的 `manager.ts`(上游 0.2.1-alpha.2 把一次性
8
+ // driver 并进了它)——但 fork 要再复制一份 290 行、并改本包 manager 的接线,而且对外部 backend(`subagent-acp` /
9
+ // `subagent-dsh-sdk`)在别的进程里装配的子代理无效。
10
+ //
11
+ // 这里改的是**装配结果**:`SystemPrompt.assemble()` 先 merge 好 contexts、再进 `system-prompt/assemble` 瀑布,本包
12
+ // 在瀑布里把该名字那条的文本换成中文。它在**该次**装配内生效(第一次装配就是中文),两条派发路径一起覆盖;父 agent
13
+ // 的装配不碰(这条本来只对子代理注册)。
14
+ import type { Context } from "@deepseek-ai/cordis";
15
+ import type {} from "@deepseek-ai/dsh-system-prompt";
16
+
17
+ // 上游注册的运行时上下文名:同名才叫接管。
18
+ export const DELEGATION_CONTEXT_NAME = "subagent:delegation";
19
+
20
+ // 模型看到的委派范围说明:权限在创建时固定、需要审批的操作一律被拒,超出范围时说明限制而不是重试。
21
+ export const DELEGATION_CONTEXT_TEXT =
22
+ "你是被派发的子代理:权限范围在创建时就固定,本会话内不能放宽——需要审批的操作一律被自动拒绝。\n" +
23
+ "任务需要超出这个范围时不要重试被拒的操作,在回复里说明这个限制,让派发你的智能体处理。";
24
+
25
+ // 装配期替换那一条文本;子代理装配才动(`parentSession` 是委派出来的判据)。
26
+ export function installDelegationContext(ctx: Context): void {
27
+ ctx.on("system-prompt/assemble", async (assembly, context, next) => {
28
+ const assembled = await next();
29
+ if (context.agent?.session.header.parentSession === undefined) return assembled;
30
+ return {
31
+ ...assembled,
32
+ contexts: assembled.contexts.map((entry) =>
33
+ entry.name === DELEGATION_CONTEXT_NAME
34
+ ? { ...entry, text: DELEGATION_CONTEXT_TEXT }
35
+ : entry,
36
+ ),
37
+ };
38
+ });
39
+ }