zames_pro 2.69.0 → 2.71.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/README.md +8 -0
- package/dist/agent-loop.js +71 -1
- package/dist/browser.js +114 -212
- package/dist/commands.js +14 -0
- package/dist/config.js +18 -0
- package/dist/deepseek-ui.js +225 -0
- package/dist/i18n/config.js +8 -0
- package/dist/i18n/doctor.js +20 -0
- package/dist/index.js +56 -1
- package/dist/net-capture.js +3 -2
- package/dist/run-task.js +2 -1
- package/dist/subagent.js +119 -0
- package/dist/system-prompt.js +38 -1
- package/dist/tools.js +24 -1
- package/dist/ui-health.js +71 -0
- package/package.json +1 -1
- package/scripts/self-smoke.mjs +45 -1
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// UI health check — a cheap "can the agent still drive this page?" probe.
|
|
2
|
+
//
|
|
3
|
+
// WHY: every selector in deepseek-ui.ts is an assumption about a page we do
|
|
4
|
+
// not control. When DeepSeek ships a redesign, the FIRST symptom used to be a
|
|
5
|
+
// task hanging on "input field not found" (or a silently mis-clicked send),
|
|
6
|
+
// minutes into a run. This module turns that into an explicit answer BEFORE
|
|
7
|
+
// the task: it checks the capabilities the agent ALWAYS needs and reports the
|
|
8
|
+
// missing ones. The page object is injected (the browser owns Playwright), so
|
|
9
|
+
// the decision logic here stays pure and unit-testable.
|
|
10
|
+
//
|
|
11
|
+
// Only ALWAYS-PRESENT capabilities are probed. The Stop button and the answer
|
|
12
|
+
// nodes are TRANSIENT (Stop exists only during generation, answers only after
|
|
13
|
+
// a turn), so a presence probe on a healthy idle page would report false
|
|
14
|
+
// negatives; they are deliberately excluded.
|
|
15
|
+
import { INPUT_SELECTORS, SEND_SELECTORS, TOGGLE_SELECTORS, NEW_CHAT_SELECTOR, NEW_CHAT_TEXT_RE, } from './deepseek-ui.js';
|
|
16
|
+
// Critical: without the input the prompt cannot be typed, without send it
|
|
17
|
+
// cannot be submitted — a task would hang on `ds.input_missing`. Advisory:
|
|
18
|
+
// toggles/newChat degrade gracefully (the toggles fall back to the configured
|
|
19
|
+
// state, newChat falls back to navigating to the base URL).
|
|
20
|
+
export const UI_CAPABILITIES = [
|
|
21
|
+
{ capability: 'input', selectors: INPUT_SELECTORS, critical: true },
|
|
22
|
+
{ capability: 'send', selectors: SEND_SELECTORS, critical: true },
|
|
23
|
+
{ capability: 'toggles', selectors: TOGGLE_SELECTORS, critical: false },
|
|
24
|
+
{ capability: 'newChat', selectors: [NEW_CHAT_SELECTOR], critical: false },
|
|
25
|
+
];
|
|
26
|
+
async function hasAny(page, sel, textRe) {
|
|
27
|
+
try {
|
|
28
|
+
const n = textRe
|
|
29
|
+
? await page.countByText(sel, textRe)
|
|
30
|
+
: await page.count(sel);
|
|
31
|
+
return n > 0;
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Probe the always-present DeepSeek controls. Never throws: a dead page is
|
|
39
|
+
* reported as every capability missing, which the caller surfaces to the
|
|
40
|
+
* operator instead of letting the next task hang.
|
|
41
|
+
*/
|
|
42
|
+
export async function probeUiHealth(page) {
|
|
43
|
+
const present = [];
|
|
44
|
+
const missing = [];
|
|
45
|
+
const missingCritical = [];
|
|
46
|
+
for (const spec of UI_CAPABILITIES) {
|
|
47
|
+
let found = false;
|
|
48
|
+
// The new-chat control is matched by an EXACT text label on a broad
|
|
49
|
+
// element set (it is a <div> on the current build, not a <button>).
|
|
50
|
+
const textRe = spec.capability === 'newChat' ? NEW_CHAT_TEXT_RE : undefined;
|
|
51
|
+
for (const sel of spec.selectors) {
|
|
52
|
+
if (await hasAny(page, sel, textRe)) {
|
|
53
|
+
found = true;
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
if (found)
|
|
58
|
+
present.push(spec.capability);
|
|
59
|
+
else {
|
|
60
|
+
missing.push(spec.capability);
|
|
61
|
+
if (spec.critical)
|
|
62
|
+
missingCritical.push(spec.capability);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return {
|
|
66
|
+
ok: missingCritical.length === 0,
|
|
67
|
+
present,
|
|
68
|
+
missing,
|
|
69
|
+
missingCritical,
|
|
70
|
+
};
|
|
71
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zames_pro",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.71.0",
|
|
4
4
|
"description": "Terminal coding agent that drives chat.deepseek.com through Playwright — 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": {
|
package/scripts/self-smoke.mjs
CHANGED
|
@@ -42,8 +42,9 @@ fs.mkdirSync(WORK, { recursive: true })
|
|
|
42
42
|
|
|
43
43
|
// --- project modules (dynamic: they read HOME at import) --------------------
|
|
44
44
|
const { DeepSeekBrowser } = await import('../src/browser.ts')
|
|
45
|
-
const { createTools } = await import('../src/tools.ts')
|
|
45
|
+
const { createTools, filterToolsForReadOnly } = await import('../src/tools.ts')
|
|
46
46
|
const { runAgentLoop } = await import('../src/agent-loop.ts')
|
|
47
|
+
const { createSubagentRunner } = await import('../src/subagent.ts')
|
|
47
48
|
const { loadConfig } = await import('../src/config.ts')
|
|
48
49
|
const { Transcript } = await import('../src/transcript.ts')
|
|
49
50
|
|
|
@@ -149,6 +150,49 @@ async function main() {
|
|
|
149
150
|
maxAfterToolRetries: cfg.browser.maxAfterToolRetries,
|
|
150
151
|
})
|
|
151
152
|
record('follow-up answered', outC.trim().length > 0, JSON.stringify(outC.slice(0, 80)))
|
|
153
|
+
const onSubagent = createSubagentRunner({
|
|
154
|
+
browser,
|
|
155
|
+
workdir: WORK,
|
|
156
|
+
locale: 'ru',
|
|
157
|
+
runAgentLoop,
|
|
158
|
+
buildTools: (readOnly) => {
|
|
159
|
+
const base = createTools(WORK, {})
|
|
160
|
+
return readOnly ? filterToolsForReadOnly(base) : base
|
|
161
|
+
},
|
|
162
|
+
transcript,
|
|
163
|
+
maxSubagents: 2,
|
|
164
|
+
askDeadlineMs: cfg.browser.askDeadlineMs,
|
|
165
|
+
maxAfterToolRetries: cfg.browser.maxAfterToolRetries,
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
// Scenario D — subagents (N36): the model delegates via Task; the runner
|
|
169
|
+
// opens a separate chat, returns only the report, and restores the parent
|
|
170
|
+
// chat. Requires browser.subagents (off by default).
|
|
171
|
+
const subTools = createTools(WORK, { subagents: true })
|
|
172
|
+
const outD = await runAgentLoop({
|
|
173
|
+
browser,
|
|
174
|
+
tools: subTools,
|
|
175
|
+
task:
|
|
176
|
+
'Use the Task tool with subagent_type "explore" to find which word is written in the file smoke.txt. ' +
|
|
177
|
+
'prompt the subagent in a self-contained way. Then, based on its report, call respond with that word.',
|
|
178
|
+
workdir: WORK,
|
|
179
|
+
maxIterations: 20,
|
|
180
|
+
freshChat: true,
|
|
181
|
+
sendSystemPrompt: true,
|
|
182
|
+
transcript,
|
|
183
|
+
onSubagent,
|
|
184
|
+
onToolCall: (n) => console.log(' tool: ' + n),
|
|
185
|
+
onAssistantMessage: (m) => console.log(' answer: ' + m),
|
|
186
|
+
onWarning: (m) => console.log(' warn: ' + m),
|
|
187
|
+
locale: 'ru',
|
|
188
|
+
askDeadlineMs: cfg.browser.askDeadlineMs,
|
|
189
|
+
maxAfterToolRetries: cfg.browser.maxAfterToolRetries,
|
|
190
|
+
})
|
|
191
|
+
record(
|
|
192
|
+
'subagent: parent used the report',
|
|
193
|
+
/HELLO_SMOKE/i.test(outD),
|
|
194
|
+
JSON.stringify(outD.slice(0, 120)),
|
|
195
|
+
)
|
|
152
196
|
}
|
|
153
197
|
|
|
154
198
|
let failed = false
|