zames_pro 2.92.0 → 2.94.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/CHANGELOG.md +12 -0
- package/dist/agent-loop.js +68 -3
- package/dist/answer-parse.js +6 -1
- package/dist/index.js +14 -0
- package/dist/run-task.js +1 -0
- package/dist/xml-toolcall.js +42 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [2.94.0] - 2026-11-02
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **dev:** dump unrecognized tool calls and hot-reload the parser
|
|
15
|
+
|
|
16
|
+
## [2.93.0] - 2026-11-02
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
- **deepseek:** recognize every tool-call shape the chat returns
|
|
21
|
+
|
|
10
22
|
## [2.92.0] - 2026-11-01
|
|
11
23
|
|
|
12
24
|
### Changed
|
package/dist/agent-loop.js
CHANGED
|
@@ -13,7 +13,7 @@ import { shouldRunDiagnostics, runDiagnostics, } from './diagnostics.js';
|
|
|
13
13
|
// The tools that WRITE/EDIT a file (and take a `path`). The completion nudge
|
|
14
14
|
// only cares about these — `Bash`/`Git*` in MUTATING_TOOLS are not edits.
|
|
15
15
|
const EDIT_TOOLS = new Set(['Write', 'Edit', 'MultiEdit', 'ApplyPatch']);
|
|
16
|
-
export async function runAgentLoop({ browser, tools, task, workdir, maxIterations = 0, freshChat = false, sendSystemPrompt = false, transcript = null, attachments = [], onThinking = () => { }, onSendPause = () => { }, onSendState = () => { }, onNotice = () => { }, onAssistantThought = () => { }, onToolCall = () => { }, onToolResult = () => { }, onAssistantMessage = () => { }, onChatReady = () => { }, onWarning = () => { }, debugLog = false, locale = DEFAULT_LOCALE, askDeadlineMs = 240_000, maxAfterToolRetries = 6, infiniteRetries = true, maxRetryWaitMs = 30 * 60_000, onAutoCompact = null, onContextLimit = null, onProviderExhausted = null, autoCompactPct = 95, contextLimit = 1_000_000, getTokenUsage = null, hooks = undefined, permissions = undefined, diagnostics = null, onAskPermission = undefined, selfImprovement = false, onSubagent = null, onAskUser = null, }) {
|
|
16
|
+
export async function runAgentLoop({ browser, tools, task, workdir, maxIterations = 0, freshChat = false, sendSystemPrompt = false, transcript = null, attachments = [], onThinking = () => { }, onSendPause = () => { }, onSendState = () => { }, onNotice = () => { }, onAssistantThought = () => { }, onToolCall = () => { }, onToolResult = () => { }, onAssistantMessage = () => { }, onChatReady = () => { }, onWarning = () => { }, debugLog = false, devDump = false, locale = DEFAULT_LOCALE, askDeadlineMs = 240_000, maxAfterToolRetries = 6, infiniteRetries = true, maxRetryWaitMs = 30 * 60_000, onAutoCompact = null, onContextLimit = null, onProviderExhausted = null, autoCompactPct = 95, contextLimit = 1_000_000, getTokenUsage = null, hooks = undefined, permissions = undefined, diagnostics = null, onAskPermission = undefined, selfImprovement = false, onSubagent = null, onAskUser = null, }) {
|
|
17
17
|
// Resolve the hook config ONCE per task: a read per tool call would be
|
|
18
18
|
// wasteful, and a mid-task edit of hooks.json is not something to chase.
|
|
19
19
|
// `undefined` means "read .zames/hooks.json"; an explicit null disables
|
|
@@ -513,6 +513,14 @@ export async function runAgentLoop({ browser, tools, task, workdir, maxIteration
|
|
|
513
513
|
// agent stalls even though the model tried to call a tool.
|
|
514
514
|
const looksLikeToolCall = responseLooksLikeToolCall(rawResponse);
|
|
515
515
|
if (looksLikeToolCall && malformedRetries < MAX_MALFORMED_RETRIES) {
|
|
516
|
+
if (devDump) {
|
|
517
|
+
const dumped = await dumpUnparsedCall(rawResponse, {
|
|
518
|
+
provider: providerLabel,
|
|
519
|
+
where: 'loop:unparsed',
|
|
520
|
+
});
|
|
521
|
+
if (dumped)
|
|
522
|
+
safeWarning('[dev] unparsed call saved: ' + dumped);
|
|
523
|
+
}
|
|
516
524
|
malformedRetries++;
|
|
517
525
|
unparsedRetries++;
|
|
518
526
|
transcript?.log('malformed_toolcall', {
|
|
@@ -1344,5 +1352,62 @@ ${diag}`;
|
|
|
1344
1352
|
}
|
|
1345
1353
|
// The pure helpers (tool-call parser + answer heuristics) live in
|
|
1346
1354
|
// answer-parse.ts and are imported here for runAgentLoop. They are
|
|
1347
|
-
// NOT re-exported: importers get them from answer-parse.js directly
|
|
1348
|
-
|
|
1355
|
+
// NOT re-exported: importers get them from answer-parse.js directly.
|
|
1356
|
+
// parseToolCall and responseLooksLikeToolCall live in let bindings:
|
|
1357
|
+
// their modules (answer-parse.ts, xml-toolcall.ts) are NOT hot-reloadable,
|
|
1358
|
+
// so an edit to the parser was reported by the dev watcher and then
|
|
1359
|
+
// silently ignored for the whole run. /reload swaps them through
|
|
1360
|
+
// _setParserBindings().
|
|
1361
|
+
import { truncateToolResult, normForStale, looksLikeUnfinishedWork, isMeaningfulRespond, extractPreToolText, } from './answer-parse.js';
|
|
1362
|
+
import * as answerParse from './answer-parse.js';
|
|
1363
|
+
let parseToolCall = answerParse.parseToolCall;
|
|
1364
|
+
let responseLooksLikeToolCall = answerParse.responseLooksLikeToolCall;
|
|
1365
|
+
export function _setParserBindings(m) {
|
|
1366
|
+
parseToolCall = m.parseToolCall;
|
|
1367
|
+
responseLooksLikeToolCall = m.responseLooksLikeToolCall;
|
|
1368
|
+
}
|
|
1369
|
+
let unparsedDumps = 0;
|
|
1370
|
+
const UNPARSED_DUMP_MAX = 20;
|
|
1371
|
+
const UNPARSED_DUMP_BYTES = 64 * 1024;
|
|
1372
|
+
/** Dev mode: save an unrecognized (but call-looking) answer for analysis. */
|
|
1373
|
+
export async function dumpUnparsedCall(raw, meta) {
|
|
1374
|
+
if (unparsedDumps >= UNPARSED_DUMP_MAX)
|
|
1375
|
+
return '';
|
|
1376
|
+
const text = String(raw || '');
|
|
1377
|
+
if (!text.trim())
|
|
1378
|
+
return '';
|
|
1379
|
+
const fs = await import('node:fs/promises');
|
|
1380
|
+
const p = await import('node:path');
|
|
1381
|
+
const dir = p.join(meta.workdir || process.cwd(), '.zames', 'unparsed');
|
|
1382
|
+
try {
|
|
1383
|
+
await fs.mkdir(dir, { recursive: true });
|
|
1384
|
+
unparsedDumps++;
|
|
1385
|
+
const stamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
1386
|
+
const name = stamp + '-' + String(meta.where || 'x').replace(/[^a-z0-9_-]+/gi, '_');
|
|
1387
|
+
const file = p.join(dir, name + '.txt');
|
|
1388
|
+
const NL = String.fromCharCode(10);
|
|
1389
|
+
const head = '# unparsed tool-call candidate' +
|
|
1390
|
+
NL +
|
|
1391
|
+
'when: ' +
|
|
1392
|
+
new Date().toISOString() +
|
|
1393
|
+
NL +
|
|
1394
|
+
'provider: ' +
|
|
1395
|
+
(meta.provider || '-') +
|
|
1396
|
+
NL +
|
|
1397
|
+
'where: ' +
|
|
1398
|
+
meta.where +
|
|
1399
|
+
NL +
|
|
1400
|
+
'length: ' +
|
|
1401
|
+
text.length +
|
|
1402
|
+
NL +
|
|
1403
|
+
NL +
|
|
1404
|
+
'--- raw answer ---' +
|
|
1405
|
+
NL +
|
|
1406
|
+
NL;
|
|
1407
|
+
await fs.writeFile(file, head + text.slice(0, UNPARSED_DUMP_BYTES), 'utf-8');
|
|
1408
|
+
return file;
|
|
1409
|
+
}
|
|
1410
|
+
catch {
|
|
1411
|
+
return '';
|
|
1412
|
+
}
|
|
1413
|
+
}
|
package/dist/answer-parse.js
CHANGED
|
@@ -71,7 +71,12 @@ export function responseLooksLikeToolCall(rawResponse) {
|
|
|
71
71
|
// colons like "path: ...".
|
|
72
72
|
(/^\s*[\[\{]/.test(raw) &&
|
|
73
73
|
/["']?(?:tool|args|command|path|old_string|content|content_base64)["']?\s*:/.test(raw)) ||
|
|
74
|
-
/<\s*\|?\s*(DSML|invoke|parameter)/i.test(raw) ||
|
|
74
|
+
/<\s*\|?\s*(DSML|invoke|parameter|tool_use|tool_call|function_call|antml)/i.test(raw) ||
|
|
75
|
+
// OpenAI-style call wrappers: {"function_call": {...}},
|
|
76
|
+
// {"tool_calls": [...]}, {"name": "Bash", "arguments": "..."}. A real
|
|
77
|
+
// shape from the live chat — without these markers the answer was taken
|
|
78
|
+
// as prose and the agent silently stopped instead of calling the tool.
|
|
79
|
+
/["'](?:function_call|tool_calls?|arguments|parameters)["']\s*:/.test(raw) ||
|
|
75
80
|
/^\s*\[?\s*\{[^}]*$/.test(raw.trim()));
|
|
76
81
|
}
|
|
77
82
|
// A respond message is only a real FINAL answer when it carries some meaning.
|
package/dist/index.js
CHANGED
|
@@ -369,6 +369,19 @@ async function reloadModules() {
|
|
|
369
369
|
mod.attach = loaded.get('attach-refs');
|
|
370
370
|
if (pick('run-task', 'runTask'))
|
|
371
371
|
mod.runTask = pick('run-task', 'runTask');
|
|
372
|
+
// answer-parse.ts and xml-toolcall.ts are NOT hot-reloadable: the loop
|
|
373
|
+
// keeps the parser functions in LIVE bindings, so /reload must push the
|
|
374
|
+
// freshly imported copy through it (a parser fix must not need a restart).
|
|
375
|
+
try {
|
|
376
|
+
const ap = (await import('./answer-parse.js'));
|
|
377
|
+
const loopMod = loaded.get('agent-loop');
|
|
378
|
+
const setter = loopMod ? loopMod['_setParserBindings'] : undefined;
|
|
379
|
+
if (typeof setter === 'function')
|
|
380
|
+
setter(ap);
|
|
381
|
+
}
|
|
382
|
+
catch (e) {
|
|
383
|
+
errors.push('answer-parse: ' + e.message);
|
|
384
|
+
}
|
|
372
385
|
return { count: loaded.size, errors };
|
|
373
386
|
}
|
|
374
387
|
// Initial load so that mod.buildSystemPrompt and the rest are populated.
|
|
@@ -1016,6 +1029,7 @@ function runTaskDeps() {
|
|
|
1016
1029
|
config,
|
|
1017
1030
|
runAgentLoop: mod.runAgentLoop,
|
|
1018
1031
|
createSpinner: mod.createSpinner,
|
|
1032
|
+
devDump: devMode,
|
|
1019
1033
|
onAssistantMessage: (msg) => {
|
|
1020
1034
|
lastAssistantMessage = msg;
|
|
1021
1035
|
},
|
package/dist/run-task.js
CHANGED
|
@@ -403,6 +403,7 @@ export async function runTask(deps, browser, tools, taskText, workdir, opts, att
|
|
|
403
403
|
},
|
|
404
404
|
onChatReady,
|
|
405
405
|
debugLog: debug,
|
|
406
|
+
devDump: deps.devDump === true,
|
|
406
407
|
locale,
|
|
407
408
|
askDeadlineMs: providerAskDeadlineMs(config),
|
|
408
409
|
maxAfterToolRetries: config.browser.maxAfterToolRetries,
|
package/dist/xml-toolcall.js
CHANGED
|
@@ -252,5 +252,46 @@ export function parseXmlToolCalls(text) {
|
|
|
252
252
|
// name in such an answer, so the call can't be restored — but it also must
|
|
253
253
|
// not be silently taken as the final answer: agent-loop will ask to retry
|
|
254
254
|
// (looksLikeToolCall triggers on the word parameter/DSML).
|
|
255
|
-
|
|
255
|
+
//
|
|
256
|
+
// ONE exception is restorable: the tool name itself arrives as a parameter
|
|
257
|
+
// (a `name="tool"` parameter next to `name="args"`). A live answer used
|
|
258
|
+
// exactly this shape; before, it was dropped and the agent stalled after a
|
|
259
|
+
// call that never ran.
|
|
260
|
+
return parseBareDsmlCall(text);
|
|
261
|
+
}
|
|
262
|
+
// A call that arrived as a bare list of parameter tags with NO invoke wrapper:
|
|
263
|
+
// the tool name is the `tool` (or `name`) parameter and the arguments are the
|
|
264
|
+
// `args` parameter (JSON) or the remaining ones.
|
|
265
|
+
function parseBareDsmlCall(text) {
|
|
266
|
+
if (!/<[^>]*?parameter/i.test(text))
|
|
267
|
+
return null;
|
|
268
|
+
const args = parseParameters(text);
|
|
269
|
+
const toolRaw = args['tool'] ?? args['tool_name'] ?? args['name'];
|
|
270
|
+
if (typeof toolRaw !== 'string' || !toolRaw.trim())
|
|
271
|
+
return null;
|
|
272
|
+
const tool = toolRaw.trim();
|
|
273
|
+
let finalArgs = {};
|
|
274
|
+
if ('args' in args) {
|
|
275
|
+
const a = args['args'];
|
|
276
|
+
if (a && typeof a === 'object' && !Array.isArray(a)) {
|
|
277
|
+
finalArgs = a;
|
|
278
|
+
}
|
|
279
|
+
else if (typeof a === 'string') {
|
|
280
|
+
try {
|
|
281
|
+
const parsed = JSON.parse(a);
|
|
282
|
+
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
283
|
+
finalArgs = parsed;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
catch { }
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
for (const k of Object.keys(args)) {
|
|
290
|
+
if (k === 'tool' || k === 'tool_name' || k === 'name' || k === 'args') {
|
|
291
|
+
continue;
|
|
292
|
+
}
|
|
293
|
+
if (!(k in finalArgs))
|
|
294
|
+
finalArgs[k] = args[k];
|
|
295
|
+
}
|
|
296
|
+
return { tool, args: finalArgs };
|
|
256
297
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zames_pro",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.94.0",
|
|
4
4
|
"description": "Terminal coding agent that drives a real browser to chat with DeepSeek, Qwen or GLM — no API key required. Reads and edits files, runs commands, commits to git, like Claude Code / Codex CLI.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|