dsh-plugin-subagent-bridge 0.14.2 → 0.15.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.
Files changed (3) hide show
  1. package/README.md +18 -0
  2. package/index.js +30 -3
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -56,6 +56,24 @@ dsh_task({ prompt: "…", cwd: "/repo/clawer" })
56
56
  - 返回结果里带上 `workspace: { id, title, path }`,方便确认落在哪个项目;
57
57
  - 项目操作失败不影响派发本身(分组是外观,不因此让任务失败)。
58
58
 
59
+ ## 没人值守:让它自己拿主意(`assumeDefaults`)
60
+
61
+ worker 卡在 `ask_user_question` 上时,需要有人在界面上点一下;但调用方是另一个 agent 时,那个人往往不在。
62
+
63
+ 所以桥在派发时会**自动追加一条常驻指令**:
64
+
65
+ ```
66
+ (如果缺少必要信息或需要在多个方案里选择,直接采用最合理的默认做法继续做完,
67
+ 不要把选择权抛回给用户、不要调用 ask_user_question 等回答;
68
+ 在结果里用一行说明你假设了什么。)
69
+ ```
70
+
71
+ - 默认开启,配置项 `assumeDefaults: false` 可关掉;
72
+ - `dsh_task`、`dsh_spawn`、`dsh_followup` 三条路径都会加;
73
+ - prompt 里本来就提到 `ask_user_question` 时不追加,避免和调用方的明确意图打架。
74
+
75
+ 效果:worker 不会停下来等人,而是选一个合理默认做法做完,并在结果里交代假设,调用方据此复核。
76
+
59
77
  ## 回答 worker 的问题(`dsh_answer`)
60
78
 
61
79
  worker 需要用户拍板时会调用 `ask_user_question`,然后**卡在那里等**。如果人不在,任务就停住了。
package/index.js CHANGED
@@ -75,6 +75,7 @@ const DEFAULTS = {
75
75
  codexBin: 'codex',
76
76
  codexSandbox: 'workspace-write',
77
77
  codexTimeoutMs: 300000,
78
+ assumeDefaults: true,
78
79
  claudeBin: 'claude',
79
80
  claudeModel: '',
80
81
  claudeTimeoutMs: 300000,
@@ -309,6 +310,28 @@ const TOOLS = [
309
310
  },
310
311
  ]
311
312
 
313
+ /**
314
+ * Standing instruction appended to a delegated task.
315
+ *
316
+ * A worker that stops to ask a question needs somebody at the UI; when the caller is
317
+ * another agent, nobody is. The caller would rather have a reasonable assumption and a
318
+ * finished result, so the worker is told to make one and report it.
319
+ */
320
+ const ASSUMPTION_POLICY =
321
+ '如果缺少必要信息或需要在多个方案里选择,直接采用最合理的默认做法继续做完,不要把选择权抛回给用户、不要调用 ask_user_question 等回答;在结果里用一行说明你假设了什么。'
322
+
323
+ /**
324
+ * Attach the standing instruction when the deployment wants it.
325
+ * @param settings - resolved settings.
326
+ * @param message - caller-supplied text.
327
+ * @returns the text to deliver.
328
+ */
329
+ function withAssumptionPolicy(settings, message) {
330
+ if (settings.assumeDefaults !== true) return message
331
+ if (message.includes('ask_user_question')) return message
332
+ return `${message}\n\n(${ASSUMPTION_POLICY})`
333
+ }
334
+
312
335
  /**
313
336
  * Take over the questions a worker asks, so the calling agent can answer them.
314
337
  *
@@ -965,7 +988,7 @@ export function createApi(ctx, state, settings) {
965
988
  const before = Number(sessions()?.get?.(sessionId)?.seq ?? 0)
966
989
  const requestId = randomUUID()
967
990
  const accepted = await service.prompt(
968
- { sessionId, requestId, mode: mode === 'steer' ? 'steer' : 'queue', content: [{ type: 'text', text: message }] },
991
+ { sessionId, requestId, mode: mode === 'steer' ? 'steer' : 'queue', content: [{ type: 'text', text: withAssumptionPolicy(settings, message) }] },
969
992
  AbortSignal.timeout(30000),
970
993
  )
971
994
  state.dispatched.add(sessionId)
@@ -1000,7 +1023,7 @@ export function createApi(ctx, state, settings) {
1000
1023
  const before = Number(sessions()?.get?.(target.sessionId)?.seq ?? 0)
1001
1024
  const requestId = randomUUID()
1002
1025
  const accepted = await service.prompt(
1003
- { sessionId: target.sessionId, requestId, mode: mode === 'steer' ? 'steer' : 'queue', content: [{ type: 'text', text: message }] },
1026
+ { sessionId: target.sessionId, requestId, mode: mode === 'steer' ? 'steer' : 'queue', content: [{ type: 'text', text: withAssumptionPolicy(settings, message) }] },
1004
1027
  AbortSignal.timeout(30000),
1005
1028
  )
1006
1029
  state.baseline.set(target.sessionId, before)
@@ -1079,7 +1102,7 @@ export function createApi(ctx, state, settings) {
1079
1102
  const finished = waitForTurnEnd(ctx, targetId, waitAfter, seconds * 1000)
1080
1103
  const requestId = randomUUID()
1081
1104
  const accepted = await service.prompt(
1082
- { sessionId: targetId, requestId, mode: deliveryMode, content: [{ type: 'text', text: prompt }] },
1105
+ { sessionId: targetId, requestId, mode: deliveryMode, content: [{ type: 'text', text: withAssumptionPolicy(settings, prompt) }] },
1083
1106
  AbortSignal.timeout(30000),
1084
1107
  )
1085
1108
  state.dispatched.add(targetId)
@@ -2309,6 +2332,10 @@ function resolveSettings(config) {
2309
2332
  }
2310
2333
  if (key === 'codexBin' && typeof value === 'string' && value !== '') merged.codexBin = value
2311
2334
  if (key === 'codexSandbox' && typeof value === 'string' && value !== '') merged.codexSandbox = value
2335
+ if (key === 'assumeDefaults') {
2336
+ merged.assumeDefaults = value === true || value === 'true'
2337
+ continue
2338
+ }
2312
2339
  if (key === 'claudeBin' && typeof value === 'string' && value !== '') merged.claudeBin = value
2313
2340
  if (key === 'claudeModel' && typeof value === 'string') merged.claudeModel = value
2314
2341
  if (key === 'workersFile' && typeof value === 'string' && value !== '') merged.workersFile = expandHome(value)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-plugin-subagent-bridge",
3
- "version": "0.14.2",
3
+ "version": "0.15.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": [