dsh-better-ask-user 0.1.2 → 0.1.4

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 (2) hide show
  1. package/package.json +1 -1
  2. package/src/ask-tool.js +27 -54
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-better-ask-user",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "Independent ask_user_question plugin: configurable wait, input-extended countdown, markdown/mobile card, native or default timeout behavior, and PTC deadline exemption.",
5
5
  "license": "GPL-3.0",
6
6
  "repository": {
package/src/ask-tool.js CHANGED
@@ -1,17 +1,10 @@
1
- /** ask_user_question scope shadow: host-controlled wait and timeout behavior. */
1
+ /** ask_user_question scope shadow: official contract plus the plugin-managed wait. */
2
2
  import { ToolArgsError, validateJsonSchemaValue } from '@deepseek-ai/dsh-tools'
3
- import { TIMED_WAIT_PARAMETER } from '@deepseek-ai/dsh-user-questions'
4
3
  import { buildDefaultAnswer } from './default-answer.js'
5
4
 
6
5
  /** Model-visible notice for the native pending result. */
7
6
  export const PENDING_QUESTION_NOTICE = 'No answer batch arrived before the timeout. This is pending, not a skipped answer. Continue useful independent work. The user can still answer; their reply will be a user message identified as answer_to_pending_question with this callId and the original questions. Do not treat this as permission.'
8
7
 
9
- /** Compatibility wait field description; the host owns the real wait. */
10
- const COMPATIBILITY_TIMEOUT = {
11
- type: 'integer',
12
- description: 'Compatibility wait field. The host controls the wait duration and timeout behavior; any value supplied here is ignored.',
13
- }
14
-
15
8
  function questionRequest(questions, exec) {
16
9
  return {
17
10
  questions: questions.map(question => ({
@@ -22,7 +15,6 @@ function questionRequest(questions, exec) {
22
15
  options: question.options.map(option => ({ ...option })),
23
16
  } : {}),
24
17
  ...(question.multi_select !== undefined ? { multiSelect: question.multi_select } : {}),
25
- ...(question.detail !== undefined ? { detail: question.detail } : {}),
26
18
  })),
27
19
  ...(exec.agent !== undefined ? { agent: exec.agent } : {}),
28
20
  signal: exec.signal,
@@ -44,33 +36,30 @@ function answerResult(result) {
44
36
  selected: [...answer.selected],
45
37
  ...(answer.custom !== undefined ? { custom: answer.custom } : {}),
46
38
  })),
47
- ...(result.autoSelected === true ? { autoSelected: true } : {}),
48
39
  }
49
40
  }
50
41
 
51
- function answersOutputSchema(schema) {
52
- const next = structuredClone(schema)
53
- const branches = Array.isArray(next.oneOf) ? next.oneOf : [next]
54
- for (const branch of branches) {
55
- if (branch?.properties?.answers !== undefined) {
56
- branch.properties.autoSelected = {
57
- type: 'boolean',
58
- description: 'True when the host selected default options after the wait expired; this is not a user answer.',
59
- }
60
- }
42
+ /** The timed path can return the official pending shape; keep it valid. */
43
+ function outputWithPending(base) {
44
+ const output = { ...base.output }
45
+ const schema = structuredClone(base.output.schema)
46
+ if (Array.isArray(schema?.oneOf) && schema.oneOf.some(branch => branch?.properties?.pending !== undefined)) return output
47
+ output.schema = {
48
+ oneOf: [
49
+ schema,
50
+ {
51
+ type: 'object',
52
+ additionalProperties: false,
53
+ properties: {
54
+ pending: { type: 'boolean' },
55
+ callId: { type: 'string' },
56
+ message: { type: 'string' },
57
+ },
58
+ required: ['pending', 'callId', 'message'],
59
+ },
60
+ ],
61
61
  }
62
- return next
63
- }
64
-
65
- function timeoutParameters(parameters) {
66
- const next = structuredClone(parameters)
67
- if (next?.properties === undefined) next.properties = {}
68
- if (next.properties[TIMED_WAIT_PARAMETER] === undefined) {
69
- next.properties[TIMED_WAIT_PARAMETER] = COMPATIBILITY_TIMEOUT
70
- } else {
71
- next.properties[TIMED_WAIT_PARAMETER] = { ...next.properties[TIMED_WAIT_PARAMETER], ...COMPATIBILITY_TIMEOUT }
72
- }
73
- return next
62
+ return output
74
63
  }
75
64
 
76
65
  /**
@@ -78,23 +67,13 @@ function timeoutParameters(parameters) {
78
67
  * @param ctx - root host context with userQuestions and optional goals.
79
68
  * @param base - existing official ask_user_question definition.
80
69
  * @param readConfig - live config reader.
81
- * @returns a ToolDefinition replacing the base behavior.
70
+ * @returns a ToolDefinition with the official contract and the plugin wait.
82
71
  */
83
72
  export function createBetterAskTool(ctx, base, readConfig) {
84
- const parameters = timeoutParameters(base.parameters)
85
- const output = {
86
- ...base.output,
87
- schema: answersOutputSchema(base.output.schema),
88
- render: (_args, value) => [{
89
- type: 'text',
90
- text: value?.autoSelected === true
91
- ? 'auto-selected default answer after the question wait expired; this is not a user answer. Verify before relying on it.\n' + JSON.stringify(value)
92
- : JSON.stringify(value),
93
- }],
94
- }
73
+ const output = outputWithPending(base)
95
74
 
96
75
  const execute = async (args, exec) => {
97
- const violations = validateJsonSchemaValue(parameters, args, '')
76
+ const violations = validateJsonSchemaValue(base.parameters, args, '')
98
77
  if (violations.length > 0) throw new ToolArgsError(violations)
99
78
  validateQuestionIds(args.questions)
100
79
 
@@ -108,7 +87,7 @@ export function createBetterAskTool(ctx, base, readConfig) {
108
87
  const seconds = config.questionTimeoutSeconds
109
88
  const questions = questionRequest(args.questions, exec)
110
89
  const defaultAnswer = mode === 'default' && seconds !== -1
111
- ? buildDefaultAnswer(args.questions)
90
+ ? answerResult(buildDefaultAnswer(args.questions))
112
91
  : undefined
113
92
 
114
93
  if (seconds === -1) {
@@ -124,17 +103,11 @@ export function createBetterAskTool(ctx, base, readConfig) {
124
103
  better: { waitMs, timeoutMode: mode, defaultAnswer },
125
104
  }, exec.callId, waitMs)
126
105
  if ('pending' in result) {
127
- if (mode === 'default' && defaultAnswer !== undefined) return defaultAnswer
106
+ if (mode === 'default' && defaultAnswer !== undefined) return answerResult(defaultAnswer)
128
107
  return { ...result, message: PENDING_QUESTION_NOTICE }
129
108
  }
130
109
  return answerResult(result)
131
110
  }
132
111
 
133
- return {
134
- ...base,
135
- description: base.description + ' The host controls the wait duration and timeout behavior; the timeout argument is a compatibility field and is ignored.',
136
- parameters,
137
- output,
138
- execute,
139
- }
112
+ return { ...base, output, execute }
140
113
  }