dsh-plugin-subagent-bridge 0.10.1 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -0
- package/index.js +35 -8
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -56,6 +56,26 @@ dsh_task({ prompt: "…", cwd: "/repo/clawer" })
|
|
|
56
56
|
- 返回结果里带上 `workspace: { id, title, path }`,方便确认落在哪个项目;
|
|
57
57
|
- 项目操作失败不影响派发本身(分组是外观,不因此让任务失败)。
|
|
58
58
|
|
|
59
|
+
## 排队还是立刻打断(`mode`)
|
|
60
|
+
|
|
61
|
+
DSH 的 prompt 有两种投递模式,桥把它们交给调用方选:
|
|
62
|
+
|
|
63
|
+
| `mode` | 行为 |
|
|
64
|
+
| --- | --- |
|
|
65
|
+
| `queue`(默认) | 等当前回合跑完再处理这条消息 |
|
|
66
|
+
| `steer` | **立刻打断**当前回合,把这条消息插进去 |
|
|
67
|
+
|
|
68
|
+
`dsh_task`、`dsh_spawn`、`dsh_followup` 都支持:
|
|
69
|
+
|
|
70
|
+
```js
|
|
71
|
+
dsh_followup({ worker: "w1", message: "别改了,先把现在的结果汇报一下", mode: "steer" })
|
|
72
|
+
dsh_task({ sessionId: "session-…", prompt: "停一下,改成只跑单测", mode: "steer" })
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
和 `dsh_dispatch` 的关系:`dsh_dispatch` 一直有 `mode`,但它只投递、不等待;`dsh_task` 会阻塞到回合结束,所以以前对正在运行的会话直接拒绝。现在传 `mode: "steer"` 就允许插进运行中的会话——**它会先打断当前回合**,然后这个新回合跑完才返回。
|
|
76
|
+
|
|
77
|
+
返回里带 `mode` 字段,方便确认这次是排队还是打断。
|
|
78
|
+
|
|
59
79
|
## 指定模型
|
|
60
80
|
|
|
61
81
|
`dsh_task`、`dsh_spawn`、`dsh_followup` 都可以带 `model`,让 worker 跑在指定模型上:
|
package/index.js
CHANGED
|
@@ -115,6 +115,13 @@ const TOOLS = [
|
|
|
115
115
|
description:
|
|
116
116
|
'Grouping for the new session. true creates a DSH project (workspace) for `cwd`, titled after its last path segment, when none owns that directory yet; a string uses that title instead. Omit (or false) to only reuse an existing project, leaving the session ungrouped when the directory has none.',
|
|
117
117
|
},
|
|
118
|
+
mode: {
|
|
119
|
+
type: 'string',
|
|
120
|
+
enum: ['queue', 'steer'],
|
|
121
|
+
description:
|
|
122
|
+
'delivery: "queue" (default) waits for the running turn to finish, "steer" interrupts it and inserts this message now.',
|
|
123
|
+
},
|
|
124
|
+
|
|
118
125
|
reasoningEffort: {
|
|
119
126
|
type: 'string',
|
|
120
127
|
description: 'Reasoning level for `model`, if that model offers levels (low, medium, high, max...).',
|
|
@@ -151,6 +158,13 @@ const TOOLS = [
|
|
|
151
158
|
description:
|
|
152
159
|
'Grouping for the new worker. true creates a DSH project (workspace) for `cwd` when none owns it; a string uses that title. Omit to only reuse an existing project.',
|
|
153
160
|
},
|
|
161
|
+
mode: {
|
|
162
|
+
type: 'string',
|
|
163
|
+
enum: ['queue', 'steer'],
|
|
164
|
+
description:
|
|
165
|
+
'delivery: "queue" (default) waits for the running turn to finish, "steer" interrupts it and inserts this message now.',
|
|
166
|
+
},
|
|
167
|
+
|
|
154
168
|
reasoningEffort: { type: 'string', description: 'Reasoning level for `model`, when that model offers levels.' },
|
|
155
169
|
},
|
|
156
170
|
required: ['task_name', 'message'],
|
|
@@ -171,6 +185,12 @@ const TOOLS = [
|
|
|
171
185
|
description: 'Switch this worker to another model before the turn, as "provider/model". Omit to keep its current one.',
|
|
172
186
|
},
|
|
173
187
|
reasoningEffort: { type: 'string', description: 'Reasoning level for `model`, when that model offers levels.' },
|
|
188
|
+
mode: {
|
|
189
|
+
type: 'string',
|
|
190
|
+
enum: ['queue', 'steer'],
|
|
191
|
+
description:
|
|
192
|
+
'delivery: "queue" (default) waits for the running turn to finish, "steer" interrupts it and inserts this message now.',
|
|
193
|
+
},
|
|
174
194
|
},
|
|
175
195
|
required: ['worker', 'message'],
|
|
176
196
|
additionalProperties: false,
|
|
@@ -590,6 +610,7 @@ async function callTool(id, params, api, signal) {
|
|
|
590
610
|
from: args.from,
|
|
591
611
|
cwd: args.cwd,
|
|
592
612
|
workspace: args.workspace === true || (typeof args.workspace === 'string' && args.workspace !== '') ? args.workspace : undefined,
|
|
613
|
+
mode: args.mode === 'steer' ? 'steer' : undefined,
|
|
593
614
|
model: typeof args.model === 'string' && args.model !== '' ? args.model : undefined,
|
|
594
615
|
reasoningEffort:
|
|
595
616
|
typeof args.reasoningEffort === 'string' && args.reasoningEffort !== '' ? args.reasoningEffort : undefined,
|
|
@@ -606,6 +627,7 @@ async function callTool(id, params, api, signal) {
|
|
|
606
627
|
await api.followup({
|
|
607
628
|
worker,
|
|
608
629
|
message,
|
|
630
|
+
mode: args.mode === 'steer' ? 'steer' : undefined,
|
|
609
631
|
model: typeof args.model === 'string' && args.model !== '' ? args.model : undefined,
|
|
610
632
|
reasoningEffort:
|
|
611
633
|
typeof args.reasoningEffort === 'string' && args.reasoningEffort !== '' ? args.reasoningEffort : undefined,
|
|
@@ -644,6 +666,7 @@ async function callTool(id, params, api, signal) {
|
|
|
644
666
|
sessionId,
|
|
645
667
|
agentPreset: typeof args.agentPreset === 'string' && args.agentPreset !== '' ? args.agentPreset : undefined,
|
|
646
668
|
workspace: args.workspace === true || (typeof args.workspace === 'string' && args.workspace !== '') ? args.workspace : undefined,
|
|
669
|
+
mode: args.mode === 'steer' ? 'steer' : undefined,
|
|
647
670
|
model: typeof args.model === 'string' && args.model !== '' ? args.model : undefined,
|
|
648
671
|
reasoningEffort:
|
|
649
672
|
typeof args.reasoningEffort === 'string' && args.reasoningEffort !== '' ? args.reasoningEffort : undefined,
|
|
@@ -718,7 +741,7 @@ export function createApi(ctx, state, settings) {
|
|
|
718
741
|
})
|
|
719
742
|
},
|
|
720
743
|
|
|
721
|
-
async spawn({ taskName, message, forkTurns, from, cwd, model, reasoningEffort, workspace }) {
|
|
744
|
+
async spawn({ taskName, message, forkTurns, from, cwd, model, reasoningEffort, workspace, mode }) {
|
|
722
745
|
const service = controller()
|
|
723
746
|
if (service === undefined || service === null) throw new Error('session controller is unavailable')
|
|
724
747
|
if (state.workers.has(taskName)) throw new Error(`task_name ${JSON.stringify(taskName)} is already in use`)
|
|
@@ -750,7 +773,7 @@ export function createApi(ctx, state, settings) {
|
|
|
750
773
|
const before = Number(sessions()?.get?.(sessionId)?.seq ?? 0)
|
|
751
774
|
const requestId = randomUUID()
|
|
752
775
|
const accepted = await service.prompt(
|
|
753
|
-
{ sessionId, requestId, mode: 'queue', content: [{ type: 'text', text: message }] },
|
|
776
|
+
{ sessionId, requestId, mode: mode === 'steer' ? 'steer' : 'queue', content: [{ type: 'text', text: message }] },
|
|
754
777
|
AbortSignal.timeout(30000),
|
|
755
778
|
)
|
|
756
779
|
state.dispatched.add(sessionId)
|
|
@@ -775,7 +798,7 @@ export function createApi(ctx, state, settings) {
|
|
|
775
798
|
}
|
|
776
799
|
},
|
|
777
800
|
|
|
778
|
-
async followup({ worker, message, model, reasoningEffort }) {
|
|
801
|
+
async followup({ worker, message, model, reasoningEffort, mode }) {
|
|
779
802
|
const target = resolveWorker(state, worker)
|
|
780
803
|
if (target === undefined) throw new Error(`unknown worker ${JSON.stringify(worker)}`)
|
|
781
804
|
const service = controller()
|
|
@@ -785,7 +808,7 @@ export function createApi(ctx, state, settings) {
|
|
|
785
808
|
const before = Number(sessions()?.get?.(target.sessionId)?.seq ?? 0)
|
|
786
809
|
const requestId = randomUUID()
|
|
787
810
|
const accepted = await service.prompt(
|
|
788
|
-
{ sessionId: target.sessionId, requestId, mode: 'queue', content: [{ type: 'text', text: message }] },
|
|
811
|
+
{ sessionId: target.sessionId, requestId, mode: mode === 'steer' ? 'steer' : 'queue', content: [{ type: 'text', text: message }] },
|
|
789
812
|
AbortSignal.timeout(30000),
|
|
790
813
|
)
|
|
791
814
|
state.baseline.set(target.sessionId, before)
|
|
@@ -824,7 +847,7 @@ export function createApi(ctx, state, settings) {
|
|
|
824
847
|
return rows
|
|
825
848
|
},
|
|
826
849
|
|
|
827
|
-
async task({ prompt, cwd, sessionId, agentPreset, timeoutSeconds, model, reasoningEffort, workspace }) {
|
|
850
|
+
async task({ prompt, cwd, sessionId, agentPreset, timeoutSeconds, model, reasoningEffort, workspace, mode }) {
|
|
828
851
|
const service = controller()
|
|
829
852
|
if (service === undefined || service === null) throw new Error('session controller is unavailable')
|
|
830
853
|
const seconds = Math.min(
|
|
@@ -846,17 +869,20 @@ export function createApi(ctx, state, settings) {
|
|
|
846
869
|
}
|
|
847
870
|
if (typeof targetId !== 'string' || targetId === '') throw new Error('session controller did not return a session id')
|
|
848
871
|
created = true
|
|
849
|
-
} else if (ctx.get('agents')?.get?.(targetId)?.status === 'running') {
|
|
850
|
-
|
|
872
|
+
} else if (mode !== 'steer' && ctx.get('agents')?.get?.(targetId)?.status === 'running') {
|
|
873
|
+
/* Steering exists precisely to reach a running session; queueing into one would
|
|
874
|
+
* silently wait for a turn this call is not tracking. */
|
|
875
|
+
throw new Error('that session is running; pass mode:"steer" to interrupt it, or use dsh_dispatch plus dsh_read_result')
|
|
851
876
|
}
|
|
852
877
|
|
|
853
878
|
const appliedModel = await applyModelSpec(ctx, targetId, model, reasoningEffort)
|
|
879
|
+
const deliveryMode = mode === 'steer' ? 'steer' : 'queue'
|
|
854
880
|
const turnBefore = lastTurn(ctx, targetId)
|
|
855
881
|
const before = Number(sessions()?.get?.(targetId)?.seq ?? 0)
|
|
856
882
|
const finished = waitForTurnEnd(ctx, targetId, turnBefore, seconds * 1000)
|
|
857
883
|
const requestId = randomUUID()
|
|
858
884
|
const accepted = await service.prompt(
|
|
859
|
-
{ sessionId: targetId, requestId, mode:
|
|
885
|
+
{ sessionId: targetId, requestId, mode: deliveryMode, content: [{ type: 'text', text: prompt }] },
|
|
860
886
|
AbortSignal.timeout(30000),
|
|
861
887
|
)
|
|
862
888
|
state.dispatched.add(targetId)
|
|
@@ -895,6 +921,7 @@ export function createApi(ctx, state, settings) {
|
|
|
895
921
|
: {}),
|
|
896
922
|
...(appliedModel === undefined ? {} : { model: appliedModel }),
|
|
897
923
|
...(placed === undefined ? {} : { workspace: placed }),
|
|
924
|
+
mode: deliveryMode,
|
|
898
925
|
text: collected.text,
|
|
899
926
|
truncated: collected.truncated,
|
|
900
927
|
turns: lastTurn(ctx, targetId),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-plugin-subagent-bridge",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Subagent bridge: lets Codex delegate a task to DeepSeek Harness the way it would spawn a subagent (one call, waits for the answer), plus a codex_dispatch tool for the reverse direction.",
|
|
6
6
|
"keywords": [
|