mocode-ai 1.0.2 → 1.0.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/dist/agent/index.js +5 -3
- package/dist/i18n/index.js +4 -0
- package/dist/tools/builtins/ask-human.js +41 -24
- package/dist/tools/validation.js +11 -1
- package/package.json +1 -1
package/dist/agent/index.js
CHANGED
|
@@ -50,10 +50,12 @@ function writeChangeOverview() {
|
|
|
50
50
|
const changes = [...merged.values()];
|
|
51
51
|
const added = changes.reduce((n, c) => n + c.added, 0);
|
|
52
52
|
const removed = changes.reduce((n, c) => n + c.removed, 0);
|
|
53
|
-
layout.contentWrite(` ${ui.
|
|
53
|
+
layout.contentWrite(` ${ui.bold}${ui.green}◆${ui.reset} ${t('agent.changes')} ${t('agent.files', { count: changes.length })} ${ui.green}+${added}${ui.reset} ${ui.red}−${removed}${ui.reset}\n`);
|
|
54
54
|
for (const change of changes) {
|
|
55
|
-
|
|
55
|
+
const kindLabel = change.kind === 'A' ? t('agent.changeAdded') : t('agent.changeModified');
|
|
56
|
+
layout.contentWrite(` ${kindLabel} ${change.path} ${ui.green}+${change.added}${ui.reset} ${ui.red}−${change.removed}${ui.reset}\n`);
|
|
56
57
|
}
|
|
58
|
+
layout.contentWrite('\n');
|
|
57
59
|
}
|
|
58
60
|
/** 取 userInput 的首行:字符串直接 split;多模态 parts 找首个 text part 再 split。 */
|
|
59
61
|
function firstLineOf(ui) {
|
|
@@ -253,7 +255,7 @@ onContextUpdate) {
|
|
|
253
255
|
? `${validation.status}: ${validation.skipReason}`
|
|
254
256
|
: validation.status;
|
|
255
257
|
const symbol = validation.status === 'passed' ? '◆' : validation.status === 'failed' ? '×' : '!';
|
|
256
|
-
layout.contentWrite(` ${
|
|
258
|
+
layout.contentWrite(` ${color}${symbol}${ui.reset} ${t('agent.validationResult', { command, status: detail })}\n\n`);
|
|
257
259
|
},
|
|
258
260
|
onDone: (elapsedMs, usage) => {
|
|
259
261
|
flushToolBatch();
|
package/dist/i18n/index.js
CHANGED
|
@@ -174,6 +174,8 @@ const zhCN = {
|
|
|
174
174
|
'agent.toolsFailed': '探索失败',
|
|
175
175
|
'agent.changes': '文件变更',
|
|
176
176
|
'agent.files': '{count} 个文件',
|
|
177
|
+
'agent.changeAdded': ' 新增',
|
|
178
|
+
'agent.changeModified': ' 修改',
|
|
177
179
|
'agent.complete': '完成',
|
|
178
180
|
'toolSummary.lines': '{count} 行',
|
|
179
181
|
'toolSummary.files': '{count} 个文件',
|
|
@@ -406,6 +408,8 @@ const en = {
|
|
|
406
408
|
'agent.toolsFailed': 'Exploration failed',
|
|
407
409
|
'agent.changes': 'Changes',
|
|
408
410
|
'agent.files': '{count} file(s)',
|
|
411
|
+
'agent.changeAdded': ' Added',
|
|
412
|
+
'agent.changeModified': ' Modified',
|
|
409
413
|
'agent.complete': 'Complete',
|
|
410
414
|
'toolSummary.lines': '{count} lines',
|
|
411
415
|
'toolSummary.files': '{count} files',
|
|
@@ -6,7 +6,7 @@ function optionToChoice(o) {
|
|
|
6
6
|
if (o === null || o === undefined)
|
|
7
7
|
return { label: '' };
|
|
8
8
|
if (typeof o === 'string')
|
|
9
|
-
return { label: o };
|
|
9
|
+
return { label: o.trim() };
|
|
10
10
|
if (typeof o === 'number' || typeof o === 'boolean')
|
|
11
11
|
return { label: String(o) };
|
|
12
12
|
if (typeof o === 'object') {
|
|
@@ -89,14 +89,31 @@ export function coerceOptions(raw) {
|
|
|
89
89
|
}
|
|
90
90
|
return [];
|
|
91
91
|
}
|
|
92
|
+
/**
|
|
93
|
+
* 在通用 JSON Schema 校验前统一模型的常见错参。
|
|
94
|
+
* - 兼容历史字符串选项和 description/detail 等别名;
|
|
95
|
+
* - 丢弃 `{}` 等无可读内容的项;
|
|
96
|
+
* - 少于两个有效选项时统一为 `options: []`,明确表示自由文本面板。
|
|
97
|
+
*/
|
|
98
|
+
export function normalizeAskHumanArguments(args) {
|
|
99
|
+
const options = coerceOptions(args.options);
|
|
100
|
+
if (options.length < 2) {
|
|
101
|
+
args.options = [];
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
args.options = options.slice(0, 4).map((option) => ({
|
|
105
|
+
label: option.label,
|
|
106
|
+
...(option.detail ? { description: option.detail } : {}),
|
|
107
|
+
}));
|
|
108
|
+
}
|
|
92
109
|
// ---------- ask_human ----------
|
|
93
110
|
export const askHumanTool = {
|
|
94
111
|
name: 'ask_human',
|
|
95
112
|
description: [
|
|
96
|
-
'
|
|
97
|
-
'
|
|
98
|
-
'
|
|
99
|
-
'
|
|
113
|
+
'Ask the user a question and wait for their response.',
|
|
114
|
+
' CHOICES: pass 2-4 concrete options via `options`, each as { label, description? }.',
|
|
115
|
+
' FREE-TEXT: pass `options: []` when the answer cannot be reduced to choices (e.g. "paste the error message").',
|
|
116
|
+
' Every non-empty option requires a non-empty `label`; never emit [{}], omit label, or omit `options`.',
|
|
100
117
|
' DO NOT call when the task is clear and you can pick a sensible default.',
|
|
101
118
|
].join(' '),
|
|
102
119
|
parameters: {
|
|
@@ -104,39 +121,39 @@ export const askHumanTool = {
|
|
|
104
121
|
properties: {
|
|
105
122
|
question: {
|
|
106
123
|
type: 'string',
|
|
124
|
+
minLength: 1,
|
|
107
125
|
description: 'The question to ask the user; keep it concise (shown as the panel title)',
|
|
108
126
|
},
|
|
109
127
|
options: {
|
|
110
128
|
type: 'array',
|
|
129
|
+
minItems: 0,
|
|
130
|
+
maxItems: 4,
|
|
111
131
|
items: {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
{
|
|
115
|
-
type: '
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
description: {
|
|
123
|
-
type: 'string',
|
|
124
|
-
description: 'What picking this option means or implies; explain the tradeoff when the label alone leaves the user unsure.',
|
|
125
|
-
},
|
|
126
|
-
},
|
|
127
|
-
required: ['label'],
|
|
132
|
+
type: 'object',
|
|
133
|
+
properties: {
|
|
134
|
+
label: {
|
|
135
|
+
type: 'string',
|
|
136
|
+
minLength: 1,
|
|
137
|
+
description: 'Short option title (1-5 words), shown as the choice itself.',
|
|
138
|
+
},
|
|
139
|
+
description: {
|
|
140
|
+
type: 'string',
|
|
141
|
+
description: 'What picking this option means or implies; explain the tradeoff when the label alone leaves the user unsure.',
|
|
128
142
|
},
|
|
129
|
-
|
|
143
|
+
},
|
|
144
|
+
required: ['label'],
|
|
145
|
+
additionalProperties: false,
|
|
130
146
|
},
|
|
131
|
-
description: '
|
|
147
|
+
description: 'Pass [] for free-text input, or 2-4 concrete choices with non-empty labels.',
|
|
132
148
|
},
|
|
133
149
|
context: {
|
|
134
150
|
type: 'string',
|
|
135
151
|
description: 'Background explanation shown under the title; may be multiline.',
|
|
136
152
|
},
|
|
137
153
|
},
|
|
138
|
-
required: ['question'],
|
|
154
|
+
required: ['question', 'options'],
|
|
139
155
|
},
|
|
156
|
+
normalizeArguments: normalizeAskHumanArguments,
|
|
140
157
|
async execute(args) {
|
|
141
158
|
const question = String(args.question ?? '');
|
|
142
159
|
const options = coerceOptions(args.options);
|
package/dist/tools/validation.js
CHANGED
|
@@ -53,7 +53,7 @@ function formatErrors(errors) {
|
|
|
53
53
|
return `${location} ${error.message ?? error.keyword}`;
|
|
54
54
|
}).join('; ');
|
|
55
55
|
}
|
|
56
|
-
/** Validate
|
|
56
|
+
/** Validate after tool-local normalization; AJV itself never coerces or mutates arguments. */
|
|
57
57
|
export function validateToolArguments(tool, args) {
|
|
58
58
|
if (!args || typeof args !== 'object' || Array.isArray(args)) {
|
|
59
59
|
return {
|
|
@@ -62,6 +62,16 @@ export function validateToolArguments(tool, args) {
|
|
|
62
62
|
message: '参数根节点必须是 JSON object',
|
|
63
63
|
};
|
|
64
64
|
}
|
|
65
|
+
try {
|
|
66
|
+
tool.normalizeArguments?.(args);
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
return {
|
|
70
|
+
valid: false,
|
|
71
|
+
code: 'INVALID_ARGUMENTS',
|
|
72
|
+
message: `参数规范化失败: ${error instanceof Error ? error.message : String(error)}`,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
65
75
|
const compiled = compile(tool.parameters);
|
|
66
76
|
if (!compiled.valid) {
|
|
67
77
|
return {
|