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 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.71.0] - 2026-10-08
11
+
12
+ ### Added
13
+
14
+ - **subagent:** add the Task tool, enable subagents by default and instruct the model when to delegate
15
+
16
+ ## [2.70.0] - 2026-10-08
17
+
18
+ ### Added
19
+
20
+ - **browser:** centralize DeepSeek UI constants and add a UI health probe
21
+
10
22
  ## [2.69.0] - 2026-10-08
11
23
 
12
24
  ### Changed
package/README.md CHANGED
@@ -100,6 +100,10 @@ Key pieces:
100
100
  - **Project context** — reads `AGENTS.md`, `MEMORY.md`, skills (`SKILL.md`) and
101
101
  custom commands from the repo and `~/.zames`, the same idea as Codex / Claude
102
102
  Code.
103
+ - **Subagents** — the `Task` tool delegates a self-contained sub-task to an
104
+ agent in its own separate chat (isolated context), which returns only a
105
+ report, so heavy research does not flood the main context. On by default;
106
+ tune with `browser.subagents` / `browser.maxSubagents`.
103
107
  - **MCP support** — plug in external tool servers (e.g. `@playwright/mcp`).
104
108
  - **Scheduling** — `/loop`, `/cron` and `/jobs` repeat tasks on a timer.
105
109
  - **Bilingual UI** — Russian / English (`/config lang`).
@@ -573,6 +577,10 @@ zames не вызывает API модели. Он запускает headless C
573
577
  - **Контекст проекта** — читает `AGENTS.md`, `MEMORY.md`, навыки (`SKILL.md`) и
574
578
  пользовательские команды из репозитория и `~/.zames`, как в Codex / Claude
575
579
  Code.
580
+ - **Субагенты** — инструмент `Task` поручает самодостаточную подзадачу агенту
581
+ в отдельном чате (изолированный контекст), который возвращает только отчёт,
582
+ так что тяжёлая разведка не засоряет основной контекст. Включены по
583
+ умолчанию; настраиваются через `browser.subagents` / `browser.maxSubagents`.
576
584
  - **Поддержка MCP** — подключение внешних серверов инструментов (например,
577
585
  `@playwright/mcp`).
578
586
  - **Планировщик** — `/loop`, `/cron` и `/jobs` повторяют задачи по таймеру.
@@ -7,7 +7,7 @@ import { substituteAttachmentMarkers } from './commands.js';
7
7
  import { normText } from './browser.js';
8
8
  import { loadHooks, runPreToolUse, runPostToolUse, } from './hooks.js';
9
9
  import { loadPermissions, decidePermission, } from './permissions.js';
10
- 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 = 'ru', askDeadlineMs = 240_000, maxAfterToolRetries = 6, onAutoCompact = null, autoCompactPct = 95, contextLimit = 1_000_000, getTokenUsage = null, hooks = undefined, permissions = undefined, onAskPermission = undefined, selfImprovement = false, }) {
10
+ 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 = 'ru', askDeadlineMs = 240_000, maxAfterToolRetries = 6, onAutoCompact = null, autoCompactPct = 95, contextLimit = 1_000_000, getTokenUsage = null, hooks = undefined, permissions = undefined, onAskPermission = undefined, selfImprovement = false, onSubagent = null, }) {
11
11
  // Resolve the hook config ONCE per task: a read per tool call would be
12
12
  // wasteful, and a mid-task edit of hooks.json is not something to chase.
13
13
  // `undefined` means "read .zames/hooks.json"; an explicit null disables
@@ -705,6 +705,76 @@ export async function runAgentLoop({ browser, tools, task, workdir, maxIteration
705
705
  continue;
706
706
  }
707
707
  for (const call of callsToRun) {
708
+ // `Task` is a SEAM, not an ordinary tool: it is handled by the caller's
709
+ // onSubagent callback (a separate chat with its own context). It is not
710
+ // in `tools` when subagents are disabled, so it is matched by name here
711
+ // BEFORE the unknown-tool branch. With no callback we answer honestly
712
+ // instead of pretending the tool exists.
713
+ if (call.tool === 'Task') {
714
+ const rawPrompt = typeof call.args.prompt === 'string' ? call.args.prompt.trim() : '';
715
+ const rawType = String(call.args.subagent_type ?? '').toLowerCase();
716
+ const subType = rawType === 'explore' ? 'explore' : 'general';
717
+ const rawDesc = typeof call.args.description === 'string' ? call.args.description : '';
718
+ if (!onSubagent || !rawPrompt) {
719
+ const err = !onSubagent
720
+ ? 'Subagents are not available in this run. Do the work yourself with the other tools.'
721
+ : 'Task requires a non-empty `prompt` argument.';
722
+ safeToolResult(err);
723
+ transcript?.log('tool_error', { tool: 'Task', error: err });
724
+ results.push({ tool: 'Task', result: err });
725
+ continue;
726
+ }
727
+ syncToolAbort();
728
+ safeToolCall('Task', call.args);
729
+ transcript?.log('subagent_start', {
730
+ type: subType,
731
+ description: rawDesc,
732
+ prompt: rawPrompt,
733
+ });
734
+ const poll = setInterval(syncToolAbort, 100);
735
+ if (typeof poll.unref === 'function')
736
+ poll.unref();
737
+ const subStart = Date.now();
738
+ let subResult;
739
+ try {
740
+ subResult = await onSubagent({
741
+ prompt: rawPrompt,
742
+ description: rawDesc,
743
+ type: subType,
744
+ });
745
+ }
746
+ catch (e) {
747
+ subResult = {
748
+ ok: false,
749
+ text: `Subagent error: ${e.message}`,
750
+ };
751
+ }
752
+ finally {
753
+ clearInterval(poll);
754
+ }
755
+ const subText = String(subResult.text || '').trim();
756
+ const subOut = subResult.ok
757
+ ? subText || '(the subagent returned no report)'
758
+ : `Subagent failed: ${subText || 'unknown error'}`;
759
+ safeToolResult(subOut);
760
+ transcript?.log('subagent_done', {
761
+ type: subType,
762
+ ok: subResult.ok,
763
+ chars: subText.length,
764
+ durationMs: Date.now() - subStart,
765
+ });
766
+ results.push({ tool: 'Task', result: subOut });
767
+ // The subagent switched the chat underneath us and the callback has
768
+ // already restored the parent chat. Clear the stale-echo baseline:
769
+ // the capture left over from the subagent's chat would otherwise make
770
+ // the watchdog compare the parent's next answer against IT and fire.
771
+ lastRaw = '';
772
+ if (browser._abort || browser._stopped) {
773
+ transcript?.log('user_aborted');
774
+ return '(прервано пользователем)';
775
+ }
776
+ continue;
777
+ }
708
778
  const tool = tools.find((t) => t.name === call.tool);
709
779
  if (!tool) {
710
780
  const err = `Unknown tool: ${call.tool}`;