zames_pro 2.82.141 → 2.83.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.83.0] - 2026-10-27
11
+
12
+ ### Added
13
+
14
+ - **ui:** selectable terminal color themes
15
+
16
+ ## [2.82.142] - 2026-10-27
17
+
18
+ ### Fixed
19
+
20
+ - **release:** bump package-lock.json root versions too
21
+
10
22
  ## [2.82.141] - 2026-10-26
11
23
 
12
24
  ### Fixed
package/README.md CHANGED
@@ -620,6 +620,15 @@ right away (where possible without a restart).
620
620
  (help, messages, spinner) and the language the agent answers you in. The
621
621
  locale lives in `ui.locale` in the config file.
622
622
 
623
+ ### Themes
624
+
625
+ `/config set ui.theme <id>` (or the `/config` menu, row «Terminal color theme»)
626
+ repaints the whole terminal in a different palette, live — no restart. `zames`
627
+ is the default; the bundled palettes are ports of well-known editor schemes:
628
+
629
+ `zames` (default), `one-dark`, `dracula`, `gruvbox`, `nord`, `monokai`,
630
+ `solarized-dark`, `tokyo-night`.
631
+
623
632
  Agent data is stored in `~/.zames`: browser profile, logs, undo history, sessions.
624
633
 
625
634
  ## FAQ
@@ -1330,6 +1339,16 @@ enum переключаются на месте; числа и строки от
1330
1339
  (справка, сообщения, спиннер), и язык, на котором агент вам отвечает. Локаль
1331
1340
  хранится в `ui.locale` в файле конфига.
1332
1341
 
1342
+ ### Темы
1343
+
1344
+ `/config set ui.theme <id>` (или меню `/config`, строка «Цветовая тема
1345
+ терминала») перекрашивает весь терминал в другую палитру, сразу — без
1346
+ перезапуска. Тема по умолчанию — `zames`; в комплекте порты известных схем
1347
+ редакторов:
1348
+
1349
+ `zames` (по умолчанию), `one-dark`, `dracula`, `gruvbox`, `nord`, `monokai`,
1350
+ `solarized-dark`, `tokyo-night`.
1351
+
1333
1352
  Данные агента хранятся в `~/.zames`: профиль браузера, логи, история undo,
1334
1353
  сессии.
1335
1354
 
@@ -6,7 +6,7 @@ import { createHash } from 'crypto';
6
6
  import { execFileSync } from 'child_process';
7
7
  import { extractPathToken } from './path-token.js';
8
8
  export { extractPathToken };
9
- export const IMAGE_EXTS = [
9
+ const IMAGE_EXTS = [
10
10
  '.png',
11
11
  '.jpg',
12
12
  '.jpeg',
@@ -16,7 +16,7 @@ export const IMAGE_EXTS = [
16
16
  '.svg',
17
17
  '.ico',
18
18
  ];
19
- export const MAX_ATTACHMENT_BYTES = 20 * 1024 * 1024;
19
+ const MAX_ATTACHMENT_BYTES = 20 * 1024 * 1024;
20
20
  // Stable content hash for de-duplicating identical attachments (a pasted image
21
21
  // is saved to a NEW temp file every time, so its path is not a stable identity).
22
22
  export function contentHash(data) {
@@ -286,7 +286,7 @@ export function hasClipboardTool() {
286
286
  }
287
287
  // True when the Windows filesystem is visible from here, i.e. a
288
288
  // powershell.exe call can reach the Windows clipboard.
289
- export function windowsReachable() {
289
+ function windowsReachable() {
290
290
  if (process.platform === 'win32')
291
291
  return true;
292
292
  if (process.platform !== 'linux')
@@ -410,9 +410,6 @@ export async function readClipboardImageDetailed() {
410
410
  }
411
411
  return { data: null, via: 'unsupported-platform' };
412
412
  }
413
- export async function readClipboardImage() {
414
- return (await readClipboardImageDetailed()).data;
415
- }
416
413
  function tryCommand(bin, args) {
417
414
  try {
418
415
  const out = execFileSync(bin, args, {
@@ -501,7 +498,7 @@ export function winPathToWsl(winPath) {
501
498
  return '/mnt/' + drive + '/' + rest;
502
499
  }
503
500
  // Run the embedded PowerShell script and return the clipboard image bytes.
504
- export async function readWindowsClipboardImage() {
501
+ async function readWindowsClipboardImage() {
505
502
  if (!isWsl())
506
503
  return null;
507
504
  const out = tryCommand('powershell.exe', [
package/dist/backlog.js CHANGED
@@ -13,8 +13,8 @@ const NL = String.fromCharCode(10);
13
13
  const NEW_ITEM_PREFIX = 'N';
14
14
  // Past these thresholds BACKLOG.md costs more tokens (on /improve) than it is
15
15
  // worth, so the operator is nudged to collapse it.
16
- export const BACKLOG_WARN_LINES = 500;
17
- export const BACKLOG_WARN_CHARS = 60_000;
16
+ const BACKLOG_WARN_LINES = 500;
17
+ const BACKLOG_WARN_CHARS = 60_000;
18
18
  // Parse a `/backlog <text>` note into the shape appendBacklogItem() wants. An
19
19
  // optional leading `P0..P3` token sets the priority; the first line becomes the
20
20
  // title and the rest the body. PURE so the operator's note is placed exactly as
@@ -214,7 +214,7 @@ export function sanitizeHeadlessUA(ua) {
214
214
  // reported "no session" for whichever provider had signed in second. A legacy
215
215
  // record without `providers` still reads: `{provider:'qwen'}` means Qwen and a
216
216
  // bare `{at}` means DeepSeek (the only writer before the second backend).
217
- export const AUTH_MARKER_FILE = path.join(os.homedir(), '.zames', 'auth.json');
217
+ const AUTH_MARKER_FILE = path.join(os.homedir(), '.zames', 'auth.json');
218
218
  /**
219
219
  * PURE: does the marker text say `provider` has a stored session? Legacy
220
220
  * shapes are honored (see the comment above AUTH_MARKER_FILE).
@@ -269,7 +269,7 @@ export function mergeAuthMarker(raw, provider, at) {
269
269
  return JSON.stringify({ providers }, null, 2);
270
270
  }
271
271
  // Best-effort: read the marker text from disk ('' when absent/unreadable).
272
- export async function readAuthMarker() {
272
+ async function readAuthMarker() {
273
273
  try {
274
274
  return await fs.readFile(AUTH_MARKER_FILE, 'utf-8');
275
275
  }
package/dist/commands.js CHANGED
@@ -161,7 +161,7 @@ export function parseTranscript(body) {
161
161
  }
162
162
  return out;
163
163
  }
164
- export const EN_DURATION_UNITS = { h: 'h', m: 'm', s: 's' };
164
+ const EN_DURATION_UNITS = { h: 'h', m: 'm', s: 's' };
165
165
  // Human-readable duration: "8s", "5m 12s", "1h 01m 01s". Seconds are padded
166
166
  // only when a larger unit is present, so a lone "45s" is not "45s" padded.
167
167
  export function formatDuration(ms, units = EN_DURATION_UNITS) {
@@ -421,7 +421,7 @@ export const RESTORED_HISTORY_LIMIT = 20;
421
421
  * history) is far bigger and is filtered by the noise checks below; a real
422
422
  * answer above this size is shortened instead of being dropped.
423
423
  */
424
- export const RESTORED_MESSAGE_MAX = 100_000;
424
+ const RESTORED_MESSAGE_MAX = 100_000;
425
425
  /**
426
426
  * DeepSeek stores the assistant turn VERBATIM: either a raw tool-call JSON
427
427
  * ({"tool":"Read","args":{...}}, frequently dirty/truncated) or a `respond`
package/dist/compact.js CHANGED
@@ -3,7 +3,7 @@ import { buildCompactPrompt, buildCompactCarryover, isUsableCompactSummary, trim
3
3
  import { loadHooks, runLifecycleHooks } from './hooks.js';
4
4
  const NL = String.fromCharCode(10);
5
5
  /** How many times the summary call is retried before the local fallback. */
6
- export const COMPACT_SUMMARY_ATTEMPTS = 3;
6
+ const COMPACT_SUMMARY_ATTEMPTS = 3;
7
7
  /** How many restored messages the local fallback keeps. */
8
8
  export const COMPACT_FALLBACK_LIMIT = 40;
9
9
  /**
package/dist/config.js CHANGED
@@ -5,6 +5,7 @@ import { DEFAULT_DIAGNOSTICS } from './diagnostics.js';
5
5
  import { DEFAULT_LOCALE } from './i18n.js';
6
6
  import { writeJsonAtomic } from './fsutil.js';
7
7
  import { PROVIDER_IDS } from './providers.js';
8
+ import { DEFAULT_THEME, themeNames } from './theme.js';
8
9
  const ZAMES_HOME = path.join(os.homedir(), '.zames');
9
10
  const HOME_CONFIG = path.join(ZAMES_HOME, 'config.json');
10
11
  // The PROJECT config path is resolved LAZILY: `/cd` changes process.cwd() at
@@ -132,6 +133,7 @@ export const DEFAULTS = {
132
133
  },
133
134
  ui: {
134
135
  locale: DEFAULT_LOCALE,
136
+ theme: DEFAULT_THEME,
135
137
  contextLimit: 1_000_000,
136
138
  // 0 = auto: terminal width, capped at 100 columns (the historical width).
137
139
  answerWidth: 0,
@@ -289,6 +291,13 @@ export const CONFIG_SCHEMA = [
289
291
  labelKey: 'cfg.f.maxIterations',
290
292
  groupKey: 'cfg.group.agent',
291
293
  },
294
+ {
295
+ path: 'ui.theme',
296
+ type: 'enum',
297
+ values: themeNames(),
298
+ labelKey: 'cfg.f.ui_theme',
299
+ groupKey: 'cfg.group.ui',
300
+ },
292
301
  {
293
302
  path: 'ui.answerWidth',
294
303
  type: 'number',
@@ -748,7 +757,7 @@ function parseConfigValue(field, raw) {
748
757
  export function validateConfigValue(field, raw) {
749
758
  return parseConfigValue(field, raw);
750
759
  }
751
- export function configPathFor(scope) {
760
+ function configPathFor(scope) {
752
761
  return scope === 'project' ? projectConfigPath() : HOME_CONFIG;
753
762
  }
754
763
  /**
@@ -33,7 +33,7 @@ import { extractAnswer, extractTokenUsage, isFinishedWithoutAnswer, isRateLimitT
33
33
  import { SendRuntime } from './send-runtime.js';
34
34
  import { readGlmBodyFeatures, unsafeGlmModes, glmFlagsMismatch, } from './glm-flags.js';
35
35
  import { interactiveBrowserLogin, loginIsInteractive, canShowWindow, } from './browser-login.js';
36
- export const GLM_USER_DATA_DIR = path.join(os.homedir(), '.zames', 'glm-profile');
36
+ const GLM_USER_DATA_DIR = path.join(os.homedir(), '.zames', 'glm-profile');
37
37
  const MAX_NET_BODY = 64 * 1024;
38
38
  function capNetBody(body) {
39
39
  if (body.length <= MAX_NET_BODY)
@@ -224,7 +224,7 @@ export function extractTokenUsage(body) {
224
224
  return null;
225
225
  }
226
226
  /** True when a chunk marks the end of the stream (`done: true`). */
227
- export function isDoneChunk(obj) {
227
+ function isDoneChunk(obj) {
228
228
  const d = completionData(obj);
229
229
  if (!d)
230
230
  return false;
package/dist/glm-ui.js CHANGED
@@ -49,8 +49,6 @@ export function chatHistoryUrl(chatId) {
49
49
  }
50
50
  /** GET - the current user (or guest) record. */
51
51
  export const AUTH_URL = 'https://chat.z.ai/api/v1/auths/';
52
- /** POST - creates a chat and returns { id }. */
53
- export const NEW_CHAT_URL = 'https://chat.z.ai/api/v1/chats/new';
54
52
  /** POST - the SSE completion endpoint (the answer stream). */
55
53
  export const ANSWER_URL_RE = /\/api\/v2\/chat\/completions/i;
56
54
  // ---------- input ----------
@@ -142,30 +140,12 @@ export function glmContextWindow(model) {
142
140
  return null;
143
141
  }
144
142
  // ---------- service statuses / errors ----------
145
- // A placeholder that must never be taken for an answer.
146
- export const STATUS_RE = /^(thinking|generating|thinking\s+process|thought\s+process)[\s.…]*$/i;
147
- // z.ai's captcha gate (an Aliyun slider). Visible text on the modal.
148
- export const CAPTCHA_RE = /security verification|drag the slider/i;
149
- export const CAPTCHA_SELECTORS = [
150
- '.aliyun-captcha',
151
- '[class*="aliyunCaptcha"]',
152
- '[id*="aliyunCaptcha"]',
153
- ];
154
143
  // Rate limit / busy notices. Kept NARROW: the classifier additionally requires
155
144
  // an error payload or an answer-less body (same rule as qwen-net-capture.ts),
156
145
  // so a normal answer that merely mentions "try again" is not mistaken for a
157
146
  // limit.
158
147
  export const RATE_LIMIT_RE = /too many requests|rate limit|too frequent|please try again later|слишком часто/i;
159
148
  export const SERVER_BUSY_RE = /server is busy|service unavailable|try again later|high demand/i;
160
- export const GUEST_LIMIT_RE = /guest.*limit|log ?in to continue|sign ?in to continue|гостевой лимит/i;
161
- // ---------- mode flags ----------
162
- // The completion body carries the mode flags. `preview_mode` and the
163
- // `general_agent` flag change the ANSWER FORMAT (they enable z.ai's own agent
164
- // runtime, which emits its own tool calls), and that would break the zames
165
- // tool-call protocol. The request body is rewritten before the send and then
166
- // verified (glm-browser's _installRuntimeRewrite/_verifyModes + glm-flags).
167
- export const SAFE_PREVIEW_MODE = false;
168
- export const SAFE_ENABLE_THINKING = true;
169
149
  // ---------- attachments ----------
170
150
  // The observed upload control (a real filechooser on click).
171
151
  export const UPLOAD_BUTTON_SELECTOR = '#upload-file-button, button[class*="upload"]';
package/dist/hooks.js CHANGED
@@ -31,7 +31,7 @@ function cutHookText(s, max) {
31
31
  cut--;
32
32
  return s.slice(0, cut);
33
33
  }
34
- export function hooksConfigPath(workdir) {
34
+ function hooksConfigPath(workdir) {
35
35
  return path.join(workdir, '.zames', 'hooks.json');
36
36
  }
37
37
  /**
@@ -55,6 +55,10 @@ export const configMessages = {
55
55
  ru: 'Язык интерфейса и ответов агента',
56
56
  en: 'Interface and agent reply language',
57
57
  },
58
+ 'cfg.f.ui_theme': {
59
+ ru: 'Цветовая тема терминала',
60
+ en: 'Terminal color theme',
61
+ },
58
62
  'cfg.f.ui_answerWidth': {
59
63
  ru: 'Ширина контента в колонках: ответы, ввод, инструменты (0 — авто, до 100)',
60
64
  en: 'Content width in columns: answers, input, tools (0 = auto, capped at 100)',
package/dist/index.js CHANGED
@@ -4,7 +4,7 @@ import path from 'path';
4
4
  import fs from 'fs/promises';
5
5
  import { readFileSync, statSync } from 'fs';
6
6
  import { fileURLToPath } from 'url';
7
- import { theme } from './theme.js';
7
+ import { theme, applyTheme, themeColors } from './theme.js';
8
8
  import { DeepSeekBrowser, headlessUACacheReady } from './browser.js';
9
9
  import { QwenBrowser } from './qwen-browser.js';
10
10
  import { GlmBrowser } from './glm-browser.js';
@@ -119,6 +119,10 @@ if (providerArg !== null) {
119
119
  }
120
120
  // Apply the configured answer width to the markdown renderer (0 = auto).
121
121
  setAnswerWidth(config.ui?.answerWidth ?? 0);
122
+ // The color palette must be selected BEFORE the first line is rendered (the
123
+ // banner, startup notices): theme.ts starts on the default and every call site
124
+ // reads the live object, so one apply here repaints the whole session.
125
+ applyTheme(config.ui?.theme);
122
126
  // Current interface/agent language. Changed by the /config lang <ru|en> command.
123
127
  let currentLocale = isLocale(config.ui?.locale)
124
128
  ? config.ui.locale
@@ -1934,6 +1938,18 @@ async function main() {
1934
1938
  editor.setPrompt(buildPrompt());
1935
1939
  }
1936
1940
  }
1941
+ // Repaint the WHOLE terminal in the new palette immediately. The theme
1942
+ // object is shared and mutated in place, so every later render uses it;
1943
+ // the editor is asked for a full redraw because the rows already on screen
1944
+ // (status, prompt, hints) were painted with the OLD colors.
1945
+ if (path === 'ui.theme') {
1946
+ const applied = applyTheme(value);
1947
+ config.ui.theme = applied;
1948
+ if (editor) {
1949
+ editor.setTheme?.(themeColors());
1950
+ editor.refreshStatus();
1951
+ }
1952
+ }
1937
1953
  // Apply a live change to the answer width immediately, so the next
1938
1954
  // rendered answer uses the new width without a restart.
1939
1955
  if (path === 'ui.answerWidth') {
@@ -2155,6 +2171,13 @@ async function main() {
2155
2171
  getDefault: (path) => getByPath(DEFAULTS, path),
2156
2172
  set: (field, raw) => configSetRaw(field, raw),
2157
2173
  reset: (field) => configResetField(field),
2174
+ // The menu itself must repaint when the THEME row changes: its widgets
2175
+ // read the shared UI map, and without a bump the operator would cycle
2176
+ // palette ids while the colors stayed those of the previous one.
2177
+ onAfterChange: (field) => {
2178
+ if (field.path === 'ui.theme')
2179
+ editor?.setTheme?.(themeColors());
2180
+ },
2158
2181
  locale: currentLocale,
2159
2182
  };
2160
2183
  try {
package/dist/mcp.js CHANGED
@@ -110,7 +110,7 @@ export function normalizeServerConfig(raw) {
110
110
  return out;
111
111
  }
112
112
  /** Candidate config paths, ordered from global to project-local. */
113
- export async function discoverConfigFiles(workdir) {
113
+ async function discoverConfigFiles(workdir) {
114
114
  const path = await import('path');
115
115
  const os = await import('os');
116
116
  const fsMod = await import('fs/promises');
@@ -127,7 +127,7 @@ export async function discoverConfigFiles(workdir) {
127
127
  return out;
128
128
  }
129
129
  /** Read and merge every config file. Later files override earlier ones. */
130
- export async function loadConfigFiles(files) {
130
+ async function loadConfigFiles(files) {
131
131
  const merged = {};
132
132
  const fsMod = await import('fs/promises');
133
133
  for (const f of files) {
@@ -35,7 +35,7 @@ import { extractAnswer, extractTokenUsage, isFinishedWithoutAnswer, isRateLimitT
35
35
  // contexts on ONE --user-data-dir cannot coexist (SingletonLock), and keeping
36
36
  // Qwen's cookies apart also means switching providers never logs the other
37
37
  // one out.
38
- export const QWEN_USER_DATA_DIR = path.join(os.homedir(), '.zames', 'qwen-profile');
38
+ const QWEN_USER_DATA_DIR = path.join(os.homedir(), '.zames', 'qwen-profile');
39
39
  // Escape a string for use inside a RegExp (the model id may carry dots).
40
40
  function escapeRegExp(s) {
41
41
  return String(s).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
package/dist/qwen-ui.js CHANGED
@@ -169,8 +169,6 @@ export const LOGIN_SELECTORS = [
169
169
  'input[autocomplete="username"]',
170
170
  ];
171
171
  // ---------- service statuses / errors ----------
172
- // A generic "generating" label that must never be taken for an answer.
173
- export const STATUS_RE = /^(thinking|searching|generating|thinking completed|остановить|stop)[\s.…]*$/i;
174
172
  // Qwen's rate-limit / busy notices (best-effort; the toast may be short-lived).
175
173
  export const RATE_LIMIT_RE = /too many requests|rate limit|too frequent|please try again later|слишком часто/i;
176
174
  export const SERVER_BUSY_RE = /server is busy|service unavailable|try again later/i;
package/dist/run-task.js CHANGED
@@ -123,7 +123,7 @@ async function promptOnce(question) {
123
123
  // Enter sends the buffer to onQueue; an empty Enter is ignored. Backspace,
124
124
  // Ctrl+U, Esc sequences (arrows/Home/End/Delete are ignored) and bracketed
125
125
  // paste are supported. Returns a cleanup function.
126
- export function watchInput({ onEscape, onChange, onQueue, } = {}) {
126
+ function watchInput({ onEscape, onChange, onQueue, } = {}) {
127
127
  const stdin = process.stdin;
128
128
  if (!stdin.isTTY || !stdin.setRawMode)
129
129
  return () => { };
package/dist/scheduler.js CHANGED
@@ -26,7 +26,7 @@ export function parseInterval(text) {
26
26
  return ms;
27
27
  }
28
28
  /** Largest delay setInterval accepts (32-bit signed milliseconds). */
29
- export const MAX_TIMER_MS = 2_147_483_647;
29
+ const MAX_TIMER_MS = 2_147_483_647;
30
30
  // Render an interval back to the shortest human form (used in listings).
31
31
  // Non-finite input (a cron job whose next fire could not be computed sets
32
32
  // nextAt to Infinity) renders as "-" instead of "Infinityh".
package/dist/sessions.js CHANGED
@@ -68,7 +68,7 @@ function safeName(id) {
68
68
  // Save/update a session. workdir is needed so that when launched from the same
69
69
  // project we restore exactly its last chat.
70
70
  /** Hard cap on the todo checklist stored with a session. */
71
- export const SESSION_TODOS_CAP = 1000;
71
+ const SESSION_TODOS_CAP = 1000;
72
72
  export function saveSession({ id, title = '', workdir = '', todos, provider, }) {
73
73
  if (!id)
74
74
  return null;
package/dist/spinner.js CHANGED
@@ -22,7 +22,7 @@ export function stripEllipsis(phrase) {
22
22
  // (3 dots) so the trailing hint does not "jump" when the phase changes. Kept in
23
23
  // one place so the two status lines animate identically.
24
24
  export const DOTS = ['', '.', '..', '...'];
25
- export const DOTS_PAD = ' ';
25
+ const DOTS_PAD = ' ';
26
26
  // The colored dot string for a phase (brown, matching the base text).
27
27
  export function renderDots(n) {
28
28
  const d = DOTS[n] ?? '';
@@ -9,10 +9,10 @@ import { spawn } from 'node:child_process';
9
9
  // hidden), never an error in the UI.
10
10
  // Longest status text that still fits a typical terminal row together with
11
11
  // the rest of the status bar.
12
- export const STATUS_BADGE_MAX = 80;
12
+ const STATUS_BADGE_MAX = 80;
13
13
  /** Clip a status badge to STATUS_BADGE_MAX with an ellipsis (never splits a
14
14
  * surrogate pair). */
15
- export function clipBadge(text, max = STATUS_BADGE_MAX) {
15
+ function clipBadge(text, max = STATUS_BADGE_MAX) {
16
16
  const s = String(text ?? '');
17
17
  if (s.length <= max)
18
18
  return s;
package/dist/subagent.js CHANGED
@@ -14,11 +14,11 @@ import { markInternalChat } from './sessions.js';
14
14
  // subagents would need a second page and a shared send scheduler (a change to
15
15
  // browser.ts, which owns the live Playwright context) — out of scope here.
16
16
  /** Hard cap on iterations for one subagent run. */
17
- export const SUBAGENT_MAX_ITER = 30;
17
+ const SUBAGENT_MAX_ITER = 30;
18
18
  // The report is fed back into the PARENT chat as a tool result, so a runaway
19
19
  // subagent must not be able to blow up the parent's context with one answer.
20
20
  // The tail is trimmed with a marker, not silently dropped.
21
- export const SUBAGENT_REPORT_CAP = 20_000;
21
+ const SUBAGENT_REPORT_CAP = 20_000;
22
22
  /** Cap a subagent report before it becomes a parent-context tool result. */
23
23
  export function capReport(text, cap = SUBAGENT_REPORT_CAP) {
24
24
  if (text.length <= cap)
@@ -41,7 +41,7 @@ export function capReport(text, cap = SUBAGENT_REPORT_CAP) {
41
41
  * standard system prompt itself (tool list + call format), and the model needs
42
42
  * exactly that to work. The persona only adds role + output contract.
43
43
  */
44
- export function subagentPersona(type) {
44
+ function subagentPersona(type) {
45
45
  const base = 'You are a SUBAGENT launched by a main coding agent to complete ONE ' +
46
46
  'self-contained sub-task in an ISOLATED chat. You do NOT see the main ' +
47
47
  "conversation's history, so rely only on the instructions below and on " +
@@ -199,7 +199,7 @@ export function renderContextSection(context) {
199
199
  // of truth for `### <Tool>` blocks; the prose that cannot be derived
200
200
  // ("Choosing the right tool", the call formats) stays hard-coded below.
201
201
  // Guarded by test/system-prompt-tools.test.ts.
202
- export function renderToolSection(tools) {
202
+ function renderToolSection(tools) {
203
203
  return tools
204
204
  .map((t2) => `### ${t2.name}\n${t2.description}\nParameters: ${JSON.stringify(t2.parameters)}`)
205
205
  .join('\n\n');