zames_pro 2.7.0 → 2.9.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 +21 -0
- package/dist/agent-loop.js +53 -2
- package/dist/attachments.js +332 -0
- package/dist/browser.js +99 -9
- package/dist/i18n.js +20 -0
- package/dist/index.js +164 -20
- package/dist/input.js +155 -6
- package/dist/system-prompt.js +6 -2
- package/dist/xml-toolcall.js +75 -0
- package/package.json +1 -1
- package/scripts/postinstall.mjs +78 -51
package/dist/xml-toolcall.js
CHANGED
|
@@ -13,6 +13,71 @@ function readAttr(attrs, name) {
|
|
|
13
13
|
const m = attrs.match(re);
|
|
14
14
|
return m ? m[2] : null;
|
|
15
15
|
}
|
|
16
|
+
// The model sometimes produces a "hybrid" call: the tool name is an attribute
|
|
17
|
+
// AND the arguments are inline JSON in the SAME opening tag, without any
|
|
18
|
+
// <parameter> children. A real case from the transcript:
|
|
19
|
+
// <|DSML|invoke name="GitAdd", "args" {"paths": "AGENTS.md src/index.ts"}>
|
|
20
|
+
// The standard <parameter> parser misses it, and the call is silently lost
|
|
21
|
+
// (the agent then stalls). Here we look for the first JSON object inside the
|
|
22
|
+
// attributes/body and parse it as args.
|
|
23
|
+
function parseInlineJsonArgs(s) {
|
|
24
|
+
const Q = String.fromCharCode(34);
|
|
25
|
+
// Find a plausible start of a JSON object: `{` or `"args" {`/`'args' {`.
|
|
26
|
+
const startRe = /\{|["']args["']\s*[:=]?\s*\{/i;
|
|
27
|
+
const m = startRe.exec(s);
|
|
28
|
+
if (!m)
|
|
29
|
+
return null;
|
|
30
|
+
const from = m.index;
|
|
31
|
+
// Scan for a balanced {...} block (string-aware) and try to parse it.
|
|
32
|
+
let depth = 0;
|
|
33
|
+
let inStr = false;
|
|
34
|
+
let esc = false;
|
|
35
|
+
let end = -1;
|
|
36
|
+
for (let i = from; i < s.length; i++) {
|
|
37
|
+
const c = s[i];
|
|
38
|
+
if (esc) {
|
|
39
|
+
esc = false;
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
if (c === '\\') {
|
|
43
|
+
esc = true;
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
if (c === Q) {
|
|
47
|
+
inStr = !inStr;
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
if (inStr)
|
|
51
|
+
continue;
|
|
52
|
+
if (c === '{')
|
|
53
|
+
depth++;
|
|
54
|
+
else if (c === '}') {
|
|
55
|
+
depth--;
|
|
56
|
+
if (depth === 0) {
|
|
57
|
+
end = i + 1;
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (end === -1)
|
|
63
|
+
return null;
|
|
64
|
+
let frag = s.slice(from, end).trim();
|
|
65
|
+
// Drop a leading `"args" {` / `'args' {` / `args = {` prefix so only the
|
|
66
|
+
// object itself is left.
|
|
67
|
+
const lead = /^["']?args["']?\s*[:=]?\s*/.exec(frag);
|
|
68
|
+
if (lead)
|
|
69
|
+
frag = frag.slice(lead[0].length);
|
|
70
|
+
for (const candidate of [frag, frag.replace(/'/g, Q)]) {
|
|
71
|
+
try {
|
|
72
|
+
const parsed = JSON.parse(candidate);
|
|
73
|
+
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
74
|
+
return parsed;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
catch { }
|
|
78
|
+
}
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
16
81
|
// Extracts parameters from an <invoke> body. The opening and closing tags may
|
|
17
82
|
// carry an arbitrary prefix (DSML, etc.), and some models confuse tag pairs
|
|
18
83
|
// (<parameter …> … </|DSML| parameter>). The regex below matches any pair
|
|
@@ -57,6 +122,16 @@ export function parseXmlToolCalls(text) {
|
|
|
57
122
|
const closeMatch = closeRe.exec(rest);
|
|
58
123
|
const body = closeMatch ? rest.slice(0, closeMatch.index) : rest;
|
|
59
124
|
const args = parseParameters(body);
|
|
125
|
+
// Hybrid form: the arguments are inline JSON in the SAME invoke tag
|
|
126
|
+
// (e.g. `<|DSML|invoke name="GitAdd", "args" {"paths": "..."}>`), with no
|
|
127
|
+
// <parameter> children. Fall back to the raw invoke head + body.
|
|
128
|
+
if (Object.keys(args).length === 0) {
|
|
129
|
+
const inline = parseInlineJsonArgs(m[0] + body);
|
|
130
|
+
if (inline) {
|
|
131
|
+
calls.push({ tool: name, args: inline });
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
60
135
|
// Some models put the whole JSON arguments object into a single
|
|
61
136
|
// parameter named args. We unwrap it so there's no
|
|
62
137
|
// {args: {args: {...}}}.
|
package/package.json
CHANGED
package/scripts/postinstall.mjs
CHANGED
|
@@ -1,67 +1,94 @@
|
|
|
1
|
-
// Automatically install the Chromium build and system dependencies
|
|
2
|
-
// that Playwright needs.
|
|
3
|
-
//
|
|
4
|
-
// Runs when the zames package is installed so the user does not have to run
|
|
5
|
-
// "npx playwright install chromium" / "install-deps chromium" by hand.
|
|
6
|
-
//
|
|
7
|
-
// On Linux (including WSL) the browser binary also needs system libraries.
|
|
8
|
-
// They are installed via "install-deps", which requires root (sudo).
|
|
9
|
-
// If we are not root and sudo is not passwordless, we skip it and tell the
|
|
10
|
-
// user the exact command to run.
|
|
11
|
-
//
|
|
12
|
-
// Install failure is NOT fatal: without network access (or in CI) the
|
|
13
|
-
// package install must still succeed.
|
|
14
|
-
|
|
15
1
|
import { spawnSync } from 'node:child_process'
|
|
16
2
|
|
|
3
|
+
const NL = String.fromCharCode(10)
|
|
17
4
|
const isWin = process.platform === 'win32'
|
|
5
|
+
const isMac = process.platform === 'darwin'
|
|
6
|
+
const isLinux = process.platform === 'linux'
|
|
7
|
+
|
|
8
|
+
function run(cmd, args, opts) {
|
|
9
|
+
return spawnSync(cmd, args, Object.assign({ stdio: 'inherit', shell: isWin }, opts || {}))
|
|
10
|
+
}
|
|
18
11
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
12
|
+
function has(cmd) {
|
|
13
|
+
if (isWin) {
|
|
14
|
+
const r = spawnSync('where', [cmd], { stdio: 'ignore' })
|
|
15
|
+
return !r.error && r.status === 0
|
|
16
|
+
}
|
|
17
|
+
const r = spawnSync('sh', ['-c', 'command -v "$1"', 'sh', cmd], { stdio: 'ignore' })
|
|
18
|
+
return !r.error && r.status === 0
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function isRoot() {
|
|
22
|
+
return typeof process.getuid === 'function' && process.getuid() === 0
|
|
27
23
|
}
|
|
28
24
|
|
|
29
25
|
function canSudo() {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
26
|
+
if (isRoot()) return true
|
|
27
|
+
const r = spawnSync('sudo', ['-n', 'true'], { stdio: 'ignore' })
|
|
28
|
+
return !r.error && r.status === 0
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const MANAGERS = [
|
|
32
|
+
{ bin: 'apt-get', args: (p) => ['install', '-y', p], update: ['update'] },
|
|
33
|
+
{ bin: 'dnf', args: (p) => ['install', '-y', p] },
|
|
34
|
+
{ bin: 'yum', args: (p) => ['install', '-y', p] },
|
|
35
|
+
{ bin: 'pacman', args: (p) => ['-S', '--noconfirm', p] },
|
|
36
|
+
{ bin: 'zypper', args: (p) => ['install', '-y', p] },
|
|
37
|
+
{ bin: 'apk', args: (p) => ['add', p] },
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
function asRoot(cmd, args) {
|
|
41
|
+
if (isRoot()) return run(cmd, args)
|
|
42
|
+
return run('sudo', [cmd].concat(args))
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function installPackages(pkgs) {
|
|
46
|
+
const mgr = MANAGERS.find((m) => has(m.bin))
|
|
47
|
+
if (!mgr) return false
|
|
48
|
+
if (mgr.update) asRoot(mgr.bin, mgr.update)
|
|
49
|
+
let any = false
|
|
50
|
+
for (const p of pkgs) {
|
|
51
|
+
const r = asRoot(mgr.bin, mgr.args(p))
|
|
52
|
+
if (!r.error && r.status === 0) any = true
|
|
53
|
+
}
|
|
54
|
+
return any
|
|
35
55
|
}
|
|
36
56
|
|
|
37
|
-
// 1) Browser binary for this Playwright version.
|
|
38
57
|
const browser = run('playwright', ['install', 'chromium'])
|
|
39
58
|
if (browser.error || browser.status !== 0) {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
59
|
+
console.warn(NL + 'zames: could not install Chromium for Playwright automatically.' + NL +
|
|
60
|
+
' Run it manually: npx playwright install chromium' + NL)
|
|
61
|
+
process.exit(0)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (isLinux) {
|
|
65
|
+
if (canSudo()) {
|
|
66
|
+
const deps = run('playwright', ['install-deps', 'chromium'])
|
|
67
|
+
if (deps.error || deps.status !== 0) {
|
|
68
|
+
console.warn(NL + 'zames: could not install Chromium system dependencies automatically.' + NL +
|
|
69
|
+
' Run it manually: sudo npx playwright install-deps chromium' + NL)
|
|
70
|
+
}
|
|
71
|
+
} else {
|
|
72
|
+
console.warn(NL + 'zames: on Linux/WSL Chromium needs system libraries (sudo required).' + NL +
|
|
73
|
+
' Run it once manually: sudo npx playwright install-deps chromium' + NL)
|
|
74
|
+
}
|
|
45
75
|
}
|
|
46
76
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
)
|
|
63
|
-
}
|
|
77
|
+
if (isLinux) {
|
|
78
|
+
const haveXclip = has('xclip') || has('xsel')
|
|
79
|
+
const haveWayland = has('wl-paste')
|
|
80
|
+
if (!haveXclip || !haveWayland) {
|
|
81
|
+
if (!canSudo()) {
|
|
82
|
+
console.warn(NL + 'zames: pasting images from the clipboard needs a clipboard tool.' + NL +
|
|
83
|
+
' Install one (root required), e.g.:' + NL +
|
|
84
|
+
' sudo apt install xclip # X11 (Debian/Ubuntu)' + NL +
|
|
85
|
+
' sudo apt install wl-clipboard # Wayland' + NL +
|
|
86
|
+
' Without it, paste a file path or use the attach button in the browser.' + NL)
|
|
87
|
+
} else if (!installPackages(['xclip', 'wl-clipboard'])) {
|
|
88
|
+
console.warn(NL + 'zames: could not install a clipboard tool automatically.' + NL +
|
|
89
|
+
' Install xclip (X11) or wl-clipboard (Wayland) manually.' + NL)
|
|
90
|
+
}
|
|
91
|
+
}
|
|
64
92
|
}
|
|
65
93
|
|
|
66
|
-
// Never fail the package install because of a browser download.
|
|
67
94
|
process.exit(0)
|