@tamsensedev/runner 0.0.0-stage → 0.2.1
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/LICENSE +5 -0
- package/README.md +47 -3
- package/dist/cli.js +2914 -0
- package/dist/cli.js.map +1 -0
- package/dist/recorder.cjs +396 -0
- package/dist/recorder.cjs.map +1 -0
- package/library/.claude-plugin/plugin.json +5 -0
- package/library/THIRD_PARTY_NOTICES.md +123 -0
- package/library/agents/code-architect.md +67 -0
- package/library/agents/code-explorer.md +61 -0
- package/library/agents/code-reviewer.md +73 -0
- package/library/agents/code-simplifier.md +62 -0
- package/library/agents/design-critic.md +52 -0
- package/library/agents/second-opinion.md +64 -0
- package/library/agents/security-auditor.md +86 -0
- package/library/agents/silent-failure-hunter.md +70 -0
- package/library/agents/test-coverage-analyzer.md +78 -0
- package/library/agents/test-runner.md +48 -0
- package/library/agents/ui-tester.md +49 -0
- package/library/licenses/Apache-2.0.txt +202 -0
- package/library/skills/accessibility/SKILL.md +144 -0
- package/library/skills/accessibility/references/wcag-checks.md +168 -0
- package/library/skills/addressing-review-feedback/SKILL.md +109 -0
- package/library/skills/app-screens/SKILL.md +96 -0
- package/library/skills/app-screens/references/shell-kpis-charts.md +108 -0
- package/library/skills/app-screens/references/states-density-phone.md +54 -0
- package/library/skills/app-screens/references/tables-forms-settings.md +104 -0
- package/library/skills/avoiding-generic-ai-design/SKILL.md +92 -0
- package/library/skills/avoiding-generic-ai-design/references/tells.md +112 -0
- package/library/skills/clean-commits/SKILL.md +85 -0
- package/library/skills/design-review/SKILL.md +107 -0
- package/library/skills/design-review/references/critique-passes.md +101 -0
- package/library/skills/design-review/references/screenshot-procedure.md +121 -0
- package/library/skills/differential-security-review/LICENSE +427 -0
- package/library/skills/differential-security-review/SKILL.md +105 -0
- package/library/skills/differential-security-review/references/adversarial.md +115 -0
- package/library/skills/differential-security-review/references/methodology.md +145 -0
- package/library/skills/differential-security-review/references/patterns.md +108 -0
- package/library/skills/false-positive-check/LICENSE +427 -0
- package/library/skills/false-positive-check/SKILL.md +95 -0
- package/library/skills/false-positive-check/references/bug-class-verification.md +128 -0
- package/library/skills/false-positive-check/references/deep-verification.md +113 -0
- package/library/skills/false-positive-check/references/evidence-templates.md +93 -0
- package/library/skills/false-positive-check/references/false-positive-patterns.md +122 -0
- package/library/skills/false-positive-check/references/gate-reviews.md +36 -0
- package/library/skills/false-positive-check/references/standard-verification.md +80 -0
- package/library/skills/focused-implementation/SKILL.md +104 -0
- package/library/skills/implementation-plan/SKILL.md +66 -0
- package/library/skills/interface-design/SKILL.md +144 -0
- package/library/skills/interface-design/references/craft-defaults.md +130 -0
- package/library/skills/interface-design/references/new-direction.md +62 -0
- package/library/skills/interface-design/references/writing.md +52 -0
- package/library/skills/interface-polish/SKILL.md +103 -0
- package/library/skills/interface-polish/references/detail-rules.md +154 -0
- package/library/skills/interface-polish/references/worst-case-data.md +158 -0
- package/library/skills/motion-design/SKILL.md +106 -0
- package/library/skills/motion-design/references/recipes.md +243 -0
- package/library/skills/postgres-performance/SKILL.md +97 -0
- package/library/skills/postgres-performance/references/rules.md +265 -0
- package/library/skills/project-orientation/SKILL.md +77 -0
- package/library/skills/reviewing-changes/SKILL.md +183 -0
- package/library/skills/safe-database-changes/SKILL.md +111 -0
- package/library/skills/safe-database-changes/references/backfills.md +75 -0
- package/library/skills/safe-database-changes/references/migration-tools.md +69 -0
- package/library/skills/safe-database-changes/references/postgres-ddl.md +199 -0
- package/library/skills/security-review/SKILL.md +132 -0
- package/library/skills/security-review/references/checks-by-area.md +107 -0
- package/library/skills/security-review/references/do-not-report.md +86 -0
- package/library/skills/spec-writing/SKILL.md +144 -0
- package/library/skills/systematic-debugging/SKILL.md +130 -0
- package/library/skills/systematic-debugging/references/flaky-tests.md +51 -0
- package/library/skills/systematic-debugging/references/root-cause-tracing.md +55 -0
- package/library/skills/task-interview/SKILL.md +84 -0
- package/library/skills/test-driven-development/SKILL.md +116 -0
- package/library/skills/test-driven-development/references/writing-good-tests.md +85 -0
- package/library/skills/ui-testing/SKILL.md +102 -0
- package/library/skills/ui-testing/references/headless-check.md +280 -0
- package/library/skills/ui-testing/references/playwright-patterns.md +134 -0
- package/library/skills/ui-testing/references/recording.md +57 -0
- package/library/skills/verification-before-completion/SKILL.md +113 -0
- package/library/skills/web-interface-guidelines/SKILL.md +202 -0
- package/package.json +46 -6
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts","../src/api.ts","../src/browser.ts","../src/commands.ts","../src/capabilities.ts","../src/claude.ts","../src/claudeResult.ts","../src/feed.ts","../src/handoff.ts","../src/input.ts","../src/specText.ts","../src/prompt.ts","../src/specTools.ts","../src/usageLimit.ts","../src/config.ts","../src/protocol.ts","../src/login.ts","../src/recorderEnv.ts","../src/recorder.ts","../src/scope.ts","../src/system.ts","../src/update.ts","../src/worker.ts","../src/feedQueue.ts","../src/lock.ts","../src/recordings.ts","../src/screenshot.ts","../src/workspace.ts","../src/git.ts"],"sourcesContent":["import type { InstalledBrowser, InstallTools } from './browser'\r\nimport type { Command } from './commands'\r\nimport type { RunnerConfig } from './config'\r\nimport type { RunnerIdentityResponse } from './protocol'\r\nimport { existsSync } from 'node:fs'\r\nimport { homedir } from 'node:os'\r\nimport process from 'node:process'\r\nimport { ApiError, createApi, DEFAULT_SERVER, serverOrigin } from './api'\r\nimport { browserStatusText, installBrowser, installedBrowser, offerBrowser, removeBrowser, runProcess } from './browser'\r\nimport { probeCapabilities } from './capabilities'\r\nimport { runClaude } from './claude'\r\nimport { HELP_TEXT, LOGIN_COMMAND, parseCommand, START_COMMAND } from './commands'\r\nimport { readConfig, removeConfig, writeConfig } from './config'\r\nimport { login } from './login'\r\nimport { IDENTITY_PATH } from './protocol'\r\nimport { bundledRecorder } from './recorderEnv'\r\nimport { describeDevice, teamKeyProblem } from './scope'\r\nimport { askYesNo, defaultDeviceName, openBrowser, platformLabel, sleep } from './system'\r\nimport { currentVersion, latestVersion, relaunch, updateToRun } from './update'\r\nimport { work } from './worker'\r\nimport { githubRemoteUrl } from './workspace'\r\n\r\nconst NOT_CONNECTED_TEXT = `This computer is not connected. Run ${LOGIN_COMMAND}`\r\nconst DISCONNECTED_TEXT = `This computer was disconnected on the website. Run ${LOGIN_COMMAND}`\r\nconst NO_TASKS_ACCESS_TEXT = 'Your account no longer has access to Tasks. Ask the project owner.'\r\nconst ACCESS_PROBLEMS: Record<number, string> = { 401: DISCONNECTED_TEXT, 403: NO_TASKS_ACCESS_TEXT }\r\nconst INTERRUPTED_EXIT_CODE = 130\r\n\r\nfunction print(line: string): void {\r\n process.stdout.write(`${line}\\n`)\r\n}\r\n\r\nfunction serverFor(flag: string | null, saved: RunnerConfig | null): string {\r\n return serverOrigin(flag ?? process.env.TAMSENSE_URL ?? saved?.server ?? DEFAULT_SERVER)\r\n}\r\n\r\n// Отказ в доступе объясняется человеку словами, остальные ошибки идут как есть\r\nfunction accessProblem(error: unknown): string | null {\r\n return error instanceof ApiError ? ACCESS_PROBLEMS[error.status] ?? null : null\r\n}\r\n\r\n// Прежний ключ этого компьютера отзывается: в списке на сайте не остаётся мёртвых записей\r\nasync function forgetDevice(config: RunnerConfig): Promise<void> {\r\n await createApi(config.server, config.token).request('DELETE', IDENTITY_PATH).catch(() => {})\r\n await removeConfig()\r\n}\r\n\r\nasync function runLogin(server: string | null, deviceName: string | null): Promise<void> {\r\n const saved = await readConfig()\r\n const api = createApi(serverFor(server, saved))\r\n if (saved) {\r\n await forgetDevice(saved)\r\n }\r\n const name = deviceName?.trim() || defaultDeviceName()\r\n const result = await login(\r\n { api, openBrowser, print, sleep, now: Date.now },\r\n { name, platform: platformLabel() },\r\n )\r\n const browserDeclined = saved?.browserDeclined ? { browserDeclined: true } : {}\r\n await writeConfig({ server: api.server, token: result.token, deviceId: result.device.id, deviceName: result.device.name, ...browserDeclined })\r\n print(`Connected: ${result.device.name}. To take your tasks, run ${START_COMMAND}`)\r\n}\r\n\r\n// Первый Ctrl+C останавливает текущую задачу и отчитывается о ней, второй закрывает программу сразу\r\nfunction stopOnInterrupt(): AbortSignal {\r\n const stop = new AbortController()\r\n process.once('SIGINT', () => {\r\n print('Stopping… Press Ctrl+C again to quit at once.')\r\n stop.abort()\r\n process.once('SIGINT', () => process.exit(INTERRUPTED_EXIT_CODE))\r\n })\r\n return stop.signal\r\n}\r\n\r\nfunction installTools(home: string): InstallTools {\r\n return { home, nodePath: process.execPath, env: process.env, print, run: runProcess, exists: existsSync }\r\n}\r\n\r\n// Браузер для проверок интерфейса ставится только с согласия человека\r\nasync function browserForStart(home: string, saved: RunnerConfig): Promise<InstalledBrowser | null> {\r\n return await installedBrowser(home) ?? await offerBrowser({\r\n interactive: Boolean(process.stdin.isTTY && process.stdout.isTTY),\r\n declined: saved.browserDeclined === true,\r\n print,\r\n ask: askYesNo,\r\n install: () => installBrowser(installTools(home)),\r\n rememberDecline: async () => writeConfig({ ...saved, browserDeclined: true }),\r\n })\r\n}\r\n\r\n// Вышла новая версия: она запускается вместо этой, и компьютер всегда работает на последней\r\nasync function startedNewVersion(): Promise<boolean> {\r\n const update = await updateToRun(process.env, currentVersion(), () => latestVersion())\r\n if (!update) {\r\n return false\r\n }\r\n print(`TAMsense runner ${update.latest} is out, this is ${update.current}. Starting the new version...`)\r\n try {\r\n process.exitCode = await relaunch(process.argv.slice(2), process.env)\r\n return true\r\n }\r\n catch {\r\n print('Could not start the new version. Going on with this one.')\r\n return false\r\n }\r\n}\r\n\r\nasync function runStart(parallel: number): Promise<void> {\r\n if (await startedNewVersion()) {\r\n return\r\n }\r\n const saved = await readConfig()\r\n if (!saved) {\r\n print(NOT_CONNECTED_TEXT)\r\n return\r\n }\r\n const api = createApi(saved.server, saved.token)\r\n const identity = await api.request<RunnerIdentityResponse>('GET', IDENTITY_PATH)\r\n const problem = teamKeyProblem(identity.device.team, process.env)\r\n if (problem) {\r\n throw new Error(problem)\r\n }\r\n print(`${identity.device.name} is connected to ${saved.server} as ${describeDevice(identity)}.`)\r\n const home = homedir()\r\n const browser = await browserForStart(home, saved)\r\n await work({ api, runAgent: runClaude, capabilities: probeCapabilities, print, sleep, home, remoteUrl: githubRemoteUrl, browser, recorder: bundledRecorder() }, stopOnInterrupt(), parallel)\r\n}\r\n\r\nasync function runBrowser(remove: boolean): Promise<void> {\r\n const home = homedir()\r\n if (remove) {\r\n print(await removeBrowser(home) ? 'Removed the headless browser.' : 'No headless browser is installed.')\r\n // удалил сам: при следующем запуске не предлагать поставить снова\r\n const saved = await readConfig()\r\n if (saved) {\r\n await writeConfig({ ...saved, browserDeclined: true })\r\n }\r\n return\r\n }\r\n const installed = await installedBrowser(home)\r\n if (installed) {\r\n print(browserStatusText(installed))\r\n return\r\n }\r\n await installBrowser(installTools(home))\r\n print('Claude uses it for interface checks from the next start.')\r\n}\r\n\r\nasync function runStatus(): Promise<void> {\r\n const saved = await readConfig()\r\n if (!saved) {\r\n print(NOT_CONNECTED_TEXT)\r\n return\r\n }\r\n const identity = await createApi(saved.server, saved.token).request<RunnerIdentityResponse>('GET', IDENTITY_PATH)\r\n print(`Connected: ${identity.device.name}, ${describeDevice(identity)}`)\r\n print(`Account: ${identity.email ?? 'unknown'}, project ${identity.projectName}`)\r\n print(`Server: ${saved.server}`)\r\n}\r\n\r\nasync function runLogout(): Promise<void> {\r\n const saved = await readConfig()\r\n if (!saved) {\r\n print(NOT_CONNECTED_TEXT)\r\n return\r\n }\r\n await forgetDevice(saved)\r\n print('Disconnected. This computer no longer takes your tasks.')\r\n}\r\n\r\nasync function run(command: Command): Promise<void> {\r\n if (command.name === 'login') {\r\n await runLogin(command.server, command.deviceName)\r\n }\r\n else if (command.name === 'start') {\r\n await runStart(command.parallel)\r\n }\r\n else if (command.name === 'browser') {\r\n await runBrowser(command.remove)\r\n }\r\n else if (command.name === 'status') {\r\n await runStatus()\r\n }\r\n else if (command.name === 'logout') {\r\n await runLogout()\r\n }\r\n else {\r\n print(HELP_TEXT)\r\n }\r\n}\r\n\r\ntry {\r\n await run(parseCommand(process.argv.slice(2), process.env))\r\n}\r\ncatch (error) {\r\n process.stderr.write(`${accessProblem(error) ?? (error instanceof Error ? error.message : String(error))}\\n`)\r\n process.exitCode = 1\r\n}\r\n","export const DEFAULT_SERVER = 'https://my.tamsense.com'\r\n\r\nexport type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE'\r\n\r\nexport type UploadBody = Uint8Array<ArrayBuffer> | ReadableStream<Uint8Array>\r\n\r\n// request: JSON туда и обратно; upload: файл как есть, без base64 (куски записи, видео), ответ JSON\r\nexport interface ApiClient {\r\n readonly server: string\r\n request: <Reply>(method: HttpMethod, path: string, body?: object) => Promise<Reply>\r\n upload: <Reply>(method: HttpMethod, path: string, body: UploadBody, contentType: string) => Promise<Reply>\r\n}\r\n\r\nexport class ApiError extends Error {\r\n readonly status: number\r\n readonly code: string | null\r\n\r\n constructor(status: number, code: string | null, message: string) {\r\n super(message)\r\n this.name = 'ApiError'\r\n this.status = status\r\n this.code = code\r\n }\r\n}\r\n\r\ninterface ErrorReply {\r\n message?: unknown\r\n statusMessage?: unknown\r\n data?: { code?: unknown }\r\n}\r\n\r\nexport function serverOrigin(address: string): string {\r\n let url: URL\r\n try {\r\n url = new URL(address)\r\n }\r\n catch {\r\n throw new Error(`\"${address}\" is not a valid address. Use a full address like ${DEFAULT_SERVER}`)\r\n }\r\n if (url.protocol !== 'https:' && url.protocol !== 'http:') {\r\n throw new Error(`\"${address}\" is not a web address. Use a full address like ${DEFAULT_SERVER}`)\r\n }\r\n return url.origin\r\n}\r\n\r\nfunction textOf(value: unknown): string | null {\r\n return typeof value === 'string' && value.trim() ? value : null\r\n}\r\n\r\nasync function errorOf(response: Response): Promise<ApiError> {\r\n const reply = await response.json().catch(() => ({})) as ErrorReply\r\n const code = textOf(reply.data?.code)\r\n const message = textOf(reply.message) ?? textOf(reply.statusMessage) ?? `TAMsense answered with status ${response.status}`\r\n return new ApiError(response.status, code, message)\r\n}\r\n\r\nexport function createApi(server: string, token: string | null = null, fetchImpl: typeof fetch = fetch): ApiClient {\r\n async function send<Reply>(method: HttpMethod, path: string, headers: Record<string, string>, body: BodyInit | undefined): Promise<Reply> {\r\n const all: Record<string, string> = { accept: 'application/json', ...headers }\r\n if (token) {\r\n all.authorization = `Bearer ${token}`\r\n }\r\n let response: Response\r\n try {\r\n // поток уходит по мере чтения файла: fetch в Node просит для этого duplex\r\n response = await fetchImpl(new URL(path, server), { method, headers: all, body, ...(body instanceof ReadableStream ? { duplex: 'half' } : {}) } as RequestInit)\r\n }\r\n catch (error) {\r\n throw new Error(`Could not reach ${server}. Check your internet connection.`, { cause: error })\r\n }\r\n if (!response.ok) {\r\n throw await errorOf(response)\r\n }\r\n return await response.json() as Reply\r\n }\r\n\r\n return {\r\n server,\r\n request: <Reply>(method: HttpMethod, path: string, body?: object) =>\r\n send<Reply>(method, path, body ? { 'content-type': 'application/json' } : {}, body && JSON.stringify(body)),\r\n upload: <Reply>(method: HttpMethod, path: string, body: UploadBody, contentType: string) =>\r\n send<Reply>(method, path, { 'content-type': contentType }, body),\r\n }\r\n}\r\n","import { spawn } from 'node:child_process'\r\nimport { existsSync } from 'node:fs'\r\nimport { mkdir, readFile, rm, writeFile } from 'node:fs/promises'\r\nimport { delimiter, dirname, join } from 'node:path'\r\nimport { createInterface } from 'node:readline'\r\nimport { BROWSER_COMMAND } from './commands'\r\n\r\nexport const PLAYWRIGHT_VERSION = '1.64.1'\r\n\r\n// Сначала облегчённый headless Chromium; если для этой системы его нет, обычный\r\nconst BROWSER_NAMES = ['chromium-headless-shell', 'chromium']\r\nconst MARKER_FILE = 'tamsense-browser.json'\r\nconst OUTPUT_TAIL_LINES = 40\r\nconst ERROR_TAIL_LINES = 3\r\n// eslint-disable-next-line no-control-regex\r\nconst ANSI_PATTERN = /\\u001B\\[[\\d;]*m/g\r\nconst OFFLINE_PATTERN = /ENOTFOUND|EAI_AGAIN|ETIMEDOUT|ECONNREFUSED|ECONNRESET|ENETUNREACH|getaddrinfo/i\r\n\r\nexport const BROWSER_QUESTION = 'Interface checks need a headless browser (about 150 MB, installed once into ~/.tamsense). Install it now? [Y/n] '\r\nconst BROWSER_HINT = `Interface checks can use a headless browser. To install it (about 150 MB), run ${BROWSER_COMMAND}`\r\nconst OFFLINE_TEXT = `No connection to download it. Check your internet connection and run ${BROWSER_COMMAND} again.`\r\n\r\nexport interface BrowserFolders {\r\n prefix: string\r\n packageDir: string\r\n browsers: string\r\n marker: string\r\n}\r\n\r\nexport interface InstalledBrowser {\r\n packageDir: string\r\n browsers: string\r\n version: string\r\n browser: string\r\n}\r\n\r\nexport interface ProcessResult {\r\n code: number\r\n output: string\r\n}\r\n\r\nexport interface ProcessOptions {\r\n cwd: string\r\n env: NodeJS.ProcessEnv\r\n onLine: (line: string) => void\r\n}\r\n\r\nexport type RunProcess = (command: string, args: string[], options: ProcessOptions) => Promise<ProcessResult>\r\n\r\nexport interface InstallTools {\r\n home: string\r\n nodePath: string\r\n env: NodeJS.ProcessEnv\r\n print: (line: string) => void\r\n run: RunProcess\r\n exists: (path: string) => boolean\r\n}\r\n\r\nexport interface OfferTools {\r\n interactive: boolean\r\n declined: boolean\r\n print: (line: string) => void\r\n ask: (question: string) => Promise<boolean>\r\n install: () => Promise<InstalledBrowser>\r\n rememberDecline: () => Promise<void>\r\n}\r\n\r\nexport function browserFolders(home: string): BrowserFolders {\r\n const prefix = join(home, '.tamsense', 'playwright')\r\n return {\r\n prefix,\r\n packageDir: join(prefix, 'node_modules', 'playwright-core'),\r\n browsers: join(home, '.tamsense', 'browsers'),\r\n marker: join(prefix, MARKER_FILE),\r\n }\r\n}\r\n\r\n// Установленным считается только браузер, который докачался до конца и той же версии, что ждёт программа\r\nexport async function installedBrowser(home: string): Promise<InstalledBrowser | null> {\r\n const folders = browserFolders(home)\r\n let saved: { version?: unknown, browser?: unknown }\r\n try {\r\n saved = JSON.parse(await readFile(folders.marker, 'utf8')) as typeof saved\r\n }\r\n catch {\r\n return null\r\n }\r\n const complete = saved.version === PLAYWRIGHT_VERSION && typeof saved.browser === 'string'\r\n && existsSync(join(folders.packageDir, 'package.json')) && existsSync(folders.browsers)\r\n return complete ? { packageDir: folders.packageDir, browsers: folders.browsers, version: PLAYWRIGHT_VERSION, browser: String(saved.browser) } : null\r\n}\r\n\r\n// Свой браузер не подсовывается Playwright проекта: путь к браузерам скрипт проверки ставит сам, когда грузит эту копию\r\nexport function browserEnv(browser: InstalledBrowser): Record<string, string> {\r\n return { TAMSENSE_PLAYWRIGHT: browser.packageDir, TAMSENSE_PLAYWRIGHT_BROWSERS: browser.browsers }\r\n}\r\n\r\nexport function browserStatusText(browser: InstalledBrowser): string {\r\n return `The headless browser is installed: ${browser.browser} with playwright-core ${browser.version}, in ${dirname(browser.browsers)}. To remove it, run ${BROWSER_COMMAND} remove`\r\n}\r\n\r\n// npm запускается тем же node, что и программа: так он находится, даже если npm нет в PATH, и обходится без оболочки\r\nexport function findNpm(nodePath: string, env: NodeJS.ProcessEnv, exists: (path: string) => boolean): string | null {\r\n const pathKey = Object.keys(env).find(name => name.toUpperCase() === 'PATH') ?? 'PATH'\r\n const folders = [dirname(nodePath), ...(env[pathKey] ?? '').split(delimiter).filter(Boolean)]\r\n for (const folder of folders) {\r\n for (const cli of [join(folder, 'node_modules', 'npm', 'bin', 'npm-cli.js'), join(folder, '..', 'lib', 'node_modules', 'npm', 'bin', 'npm-cli.js')]) {\r\n if (exists(cli)) {\r\n return cli\r\n }\r\n }\r\n }\r\n return null\r\n}\r\n\r\nexport const runProcess: RunProcess = (command, args, options) => new Promise((resolve, reject) => {\r\n const child = spawn(command, args, { cwd: options.cwd, env: options.env, stdio: ['ignore', 'pipe', 'pipe'], windowsHide: true })\r\n const tail: string[] = []\r\n for (const stream of [child.stdout, child.stderr]) {\r\n createInterface({ input: stream }).on('line', (line) => {\r\n const text = line.replace(ANSI_PATTERN, '').trim()\r\n if (text) {\r\n tail.push(text)\r\n tail.splice(0, Math.max(0, tail.length - OUTPUT_TAIL_LINES))\r\n options.onLine(text)\r\n }\r\n })\r\n }\r\n child.on('error', reject)\r\n child.on('close', code => resolve({ code: code ?? 1, output: tail.join('\\n') }))\r\n})\r\n\r\nfunction failureText(what: string, result: ProcessResult): string {\r\n if (OFFLINE_PATTERN.test(result.output)) {\r\n return `${what}: ${OFFLINE_TEXT}`\r\n }\r\n const lastLines = result.output.split('\\n').slice(-ERROR_TAIL_LINES).join(' ')\r\n return `${what} (exit code ${result.code})${lastLines ? `: ${lastLines}` : '.'}`\r\n}\r\n\r\n// Ставится в ~/.tamsense, не в проект: зависимости проектов и этой программы не меняются\r\nexport async function installBrowser(tools: InstallTools): Promise<InstalledBrowser> {\r\n const folders = browserFolders(tools.home)\r\n const npmCli = findNpm(tools.nodePath, tools.env, tools.exists)\r\n if (!npmCli) {\r\n throw new Error(`npm was not found next to ${tools.nodePath} or on PATH. Install Node.js with npm and run ${BROWSER_COMMAND} again.`)\r\n }\r\n const showLine = (line: string) => tools.print(` ${line}`)\r\n await rm(folders.marker, { force: true })\r\n await mkdir(folders.prefix, { recursive: true })\r\n\r\n tools.print(`Step 1 of 2: downloading playwright-core ${PLAYWRIGHT_VERSION}…`)\r\n const npmArgs = [npmCli, 'install', '--no-save', '--no-audit', '--no-fund', '--prefix', folders.prefix, `playwright-core@${PLAYWRIGHT_VERSION}`]\r\n const npm = await tools.run(tools.nodePath, npmArgs, { cwd: folders.prefix, env: tools.env, onLine: showLine })\r\n if (npm.code !== 0) {\r\n throw new Error(failureText('npm could not download playwright-core', npm))\r\n }\r\n\r\n tools.print('Step 2 of 2: downloading the browser, about 150 MB…')\r\n const env = { ...tools.env, PLAYWRIGHT_BROWSERS_PATH: folders.browsers }\r\n let result: ProcessResult = { code: 1, output: '' }\r\n for (const browser of BROWSER_NAMES) {\r\n result = await tools.run(tools.nodePath, [join(folders.packageDir, 'cli.js'), 'install', browser], { cwd: folders.prefix, env, onLine: showLine })\r\n if (result.code === 0) {\r\n await writeFile(folders.marker, `${JSON.stringify({ version: PLAYWRIGHT_VERSION, browser }, null, 4)}\\n`)\r\n tools.print(`Installed ${browser} into ${folders.browsers}.`)\r\n return { packageDir: folders.packageDir, browsers: folders.browsers, version: PLAYWRIGHT_VERSION, browser }\r\n }\r\n if (OFFLINE_PATTERN.test(result.output)) {\r\n break\r\n }\r\n }\r\n throw new Error(failureText('Could not download the browser', result))\r\n}\r\n\r\nexport async function removeBrowser(home: string): Promise<boolean> {\r\n const folders = browserFolders(home)\r\n const present = existsSync(folders.prefix) || existsSync(folders.browsers)\r\n await rm(folders.prefix, { recursive: true, force: true })\r\n await rm(folders.browsers, { recursive: true, force: true })\r\n return present\r\n}\r\n\r\n// Спрашивает один раз: отказ запоминается; без терминала только подсказка, сбой установки не мешает брать задачи\r\nexport async function offerBrowser(tools: OfferTools): Promise<InstalledBrowser | null> {\r\n if (tools.declined) {\r\n return null\r\n }\r\n if (!tools.interactive) {\r\n tools.print(BROWSER_HINT)\r\n return null\r\n }\r\n if (!await tools.ask(BROWSER_QUESTION)) {\r\n await tools.rememberDecline()\r\n tools.print(`Not installed. To install it later, run ${BROWSER_COMMAND}`)\r\n return null\r\n }\r\n try {\r\n return await tools.install()\r\n }\r\n catch (error) {\r\n tools.print(`Could not install the browser: ${error instanceof Error ? error.message : String(error)}`)\r\n tools.print('Tasks still run without it.')\r\n return null\r\n }\r\n}\r\n","import { parseArgs } from 'node:util'\r\nimport { DEFAULT_SERVER } from './api'\r\n\r\nexport const LOGIN_COMMAND = 'npx @tamsensedev/runner@latest login'\r\nexport const START_COMMAND = 'npx @tamsensedev/runner@latest start'\r\nexport const BROWSER_COMMAND = 'npx @tamsensedev/runner@latest browser'\r\n\r\nexport const DEFAULT_PARALLEL = 2\r\nexport const MAX_PARALLEL = 4\r\n\r\nexport type Command\r\n = | { name: 'login', server: string | null, deviceName: string | null }\r\n | { name: 'start', parallel: number }\r\n | { name: 'browser', remove: boolean }\r\n | { name: 'status' }\r\n | { name: 'logout' }\r\n | { name: 'help' }\r\n\r\nexport const HELP_TEXT = `Connects this computer to TAMsense so it can work on your tasks.\r\n\r\nUsage:\r\n ${LOGIN_COMMAND} connect this computer\r\n ${START_COMMAND} take your tasks and work on them until you press Ctrl+C\r\n ${BROWSER_COMMAND} install the headless browser for interface checks, or show it\r\n ${BROWSER_COMMAND} remove delete that browser\r\n npx @tamsensedev/runner@latest status show the connection\r\n npx @tamsensedev/runner@latest logout disconnect this computer\r\n\r\nLogin options:\r\n --name <name> how this computer is called in TAMsense (default: its network name)\r\n --server <url> TAMsense address (default: ${DEFAULT_SERVER})\r\n\r\nStart options:\r\n --parallel <n> how many tasks to work on at once, 1 to ${MAX_PARALLEL} (default: ${DEFAULT_PARALLEL}, or TAMSENSE_PARALLEL)`\r\n\r\n// Флаг важнее переменной окружения; неверное число останавливает запуск, а не тихо меняется на другое\r\nexport function parallelFor(flag: string | undefined, env: string | undefined): number {\r\n const [source, value] = flag !== undefined ? ['--parallel', flag] : ['TAMSENSE_PARALLEL', env?.trim() || undefined]\r\n if (value === undefined) {\r\n return DEFAULT_PARALLEL\r\n }\r\n const count = Number(value)\r\n if (!/^\\d+$/.test(value) || count < 1 || count > MAX_PARALLEL) {\r\n throw new Error(`${source} must be a whole number from 1 to ${MAX_PARALLEL}, got \"${value}\".`)\r\n }\r\n return count\r\n}\r\n\r\nexport function parseCommand(argv: string[], env: Record<string, string | undefined> = {}): Command {\r\n const { positionals, values } = parseArgs({\r\n args: argv,\r\n allowPositionals: true,\r\n strict: true,\r\n options: {\r\n server: { type: 'string' },\r\n name: { type: 'string' },\r\n parallel: { type: 'string' },\r\n help: { type: 'boolean' },\r\n },\r\n })\r\n const [name, detail] = positionals\r\n if (values.help) {\r\n return { name: 'help' }\r\n }\r\n if (name === 'browser' && (detail === undefined || detail === 'remove') && positionals.length <= 2) {\r\n return { name: 'browser', remove: detail === 'remove' }\r\n }\r\n if (positionals.length !== 1) {\r\n return { name: 'help' }\r\n }\r\n if (name === 'login') {\r\n return { name: 'login', server: values.server ?? null, deviceName: values.name ?? null }\r\n }\r\n if (name === 'start') {\r\n return { name: 'start', parallel: parallelFor(values.parallel, env.TAMSENSE_PARALLEL) }\r\n }\r\n if (name === 'status' || name === 'logout') {\r\n return { name }\r\n }\r\n return { name: 'help' }\r\n}\r\n","import type { AccountInfo, ApiKeySource, SDKControlInitializeResponse, SDKSystemMessage, SDKUserMessage } from '@anthropic-ai/claude-agent-sdk'\r\nimport type { RunnerAccount, RunnerCapabilities, RunnerSession } from './protocol'\r\nimport { homedir } from 'node:os'\r\nimport { query } from '@anthropic-ai/claude-agent-sdk'\r\n\r\nconst PROBE_TIMEOUT_MS = 60_000\r\nconst NO_ANSWER_TEXT = 'Claude Code did not start in time.'\r\nconst STOPPED_TEXT = 'Stopped before Claude Code answered.'\r\n\r\n// apiKeySource по SDK: ключ из окружения, из apiKeyHelper или ключ Console после /login (user, project, org, temporary из старых версий);\r\n// 'none' (и старое 'oauth') значит без ключа, то есть вход через claude.ai\r\nconst API_KEY_SOURCES = new Set<string>(['ANTHROPIC_API_KEY', 'apiKeyHelper', '/login managed key', 'user', 'project', 'org', 'temporary'] satisfies ApiKeySource[])\r\nconst LOGIN_SOURCES = new Set<string>(['none', 'oauth'] satisfies ApiKeySource[])\r\n\r\nconst ACCOUNT_WORDS: Record<RunnerAccount, string> = {\r\n subscription: 'on your subscription',\r\n api_key: 'on an API key',\r\n}\r\n\r\nexport function accountOfKeySource(source: string | undefined): RunnerAccount | null {\r\n if (source && API_KEY_SOURCES.has(source)) {\r\n return 'api_key'\r\n }\r\n return source && LOGIN_SOURCES.has(source) ? 'subscription' : null\r\n}\r\n\r\n// Bedrock, Vertex и другие облака не подписка и не ключ Anthropic\r\nexport function accountOfInfo(info: AccountInfo): RunnerAccount | null {\r\n if (info.apiProvider && info.apiProvider !== 'firstParty') {\r\n return null\r\n }\r\n if (info.apiKeySource && API_KEY_SOURCES.has(info.apiKeySource)) {\r\n return 'api_key'\r\n }\r\n return info.subscriptionType ? 'subscription' : null\r\n}\r\n\r\nexport function sessionOf(init: SDKSystemMessage): RunnerSession {\r\n return {\r\n model: init.model || null,\r\n account: accountOfKeySource(init.apiKeySource),\r\n claudeVersion: init.claude_code_version || null,\r\n }\r\n}\r\n\r\nexport function capabilitiesOf(init: SDKControlInitializeResponse): RunnerCapabilities {\r\n return {\r\n account: accountOfInfo(init.account),\r\n claudeVersion: init.claude_code_version || null,\r\n models: init.models.map(model => ({ value: model.value, displayName: model.displayName, description: model.description })),\r\n }\r\n}\r\n\r\n// В строке только короткие имена (opus, sonnet, haiku): точные версии и «по умолчанию» уходят на сайт, но здесь лишние\r\nfunction isAlias(value: string): boolean {\r\n return value !== 'default' && !value.startsWith('claude-')\r\n}\r\n\r\nexport function capabilitiesLine(capabilities: RunnerCapabilities): string {\r\n const version = capabilities.claudeVersion ? `Claude Code ${capabilities.claudeVersion}` : 'Claude Code'\r\n const account = capabilities.account ? ` ${ACCOUNT_WORDS[capabilities.account]}` : ''\r\n const names = capabilities.models.filter(model => isAlias(model.value)).map(model => model.displayName)\r\n return names.length > 0 ? `${version}${account}: ${names.join(', ')}` : `${version}${account}`\r\n}\r\n\r\n// Claude Code запускается без единого сообщения: он отвечает на знакомство (вход, версия, модели) и закрывается, модель не вызывается\r\nexport async function probeCapabilities(stop?: AbortSignal): Promise<RunnerCapabilities> {\r\n stop?.throwIfAborted()\r\n let release = () => {}\r\n const released = new Promise<void>((resolve) => {\r\n release = resolve\r\n })\r\n const nothing: AsyncIterable<SDKUserMessage> = {\r\n [Symbol.asyncIterator]: () => ({\r\n next: async () => {\r\n await released\r\n return { done: true, value: undefined }\r\n },\r\n }),\r\n }\r\n const session = query({ prompt: nothing, options: { cwd: homedir(), settingSources: ['user'], persistSession: false, strictMcpConfig: true } })\r\n const timeout = AbortSignal.timeout(PROBE_TIMEOUT_MS)\r\n const giveUp = stop ? AbortSignal.any([stop, timeout]) : timeout\r\n const gaveUp = new Promise<never>((_, reject) => {\r\n giveUp.addEventListener('abort', () => reject(new Error(stop?.aborted ? STOPPED_TEXT : NO_ANSWER_TEXT)), { once: true })\r\n })\r\n try {\r\n return capabilitiesOf(await Promise.race([session.initializationResult(), gaveUp]))\r\n }\r\n finally {\r\n release()\r\n session.close()\r\n }\r\n}\r\n","import type { CanUseTool, McpSdkServerConfigWithInstance, Options, Query } from '@anthropic-ai/claude-agent-sdk'\r\nimport type { AgentOutcome } from './claudeResult'\r\nimport type { FeedDraft } from './feed'\r\nimport type { AgentInput, ModelSwitch } from './input'\r\nimport type { TaskPrompt } from './prompt'\r\nimport type { RunnerEffort, RunnerHandoff, RunnerHandoffTarget, RunnerQuestions, RunnerSession, RunnerSpecResult, RunnerVerdict } from './protocol'\r\nimport type { AttachRequest } from './screenshot'\r\nimport type { ReviewRequest } from './specTools'\r\nimport type { UsageLimitWatch } from './usageLimit'\r\nimport { fileURLToPath } from 'node:url'\r\nimport { createSdkMcpServer, query, tool } from '@anthropic-ai/claude-agent-sdk'\r\nimport { z } from 'zod'\r\nimport { sessionOf } from './capabilities'\r\nimport { outcomeOf } from './claudeResult'\r\nimport { feedOf, hiddenStepsFilter } from './feed'\r\nimport { chooseHandoff, HANDOFF_OUTCOMES } from './handoff'\r\nimport { DEFAULT_MODEL } from './input'\r\nimport { HANDOFF_TOOL_NAME, REPLAY_TOOL_NAME, SCREENSHOT_TOOL_NAME, VERDICT_TOOL_NAME, VIDEO_TOOL_NAME } from './prompt'\r\nimport { questionsTool, specTool } from './specTools'\r\nimport { USAGE_LIMIT_REPORT, usageLimitWatch } from './usageLimit'\r\n\r\nexport type { AgentOutcome }\r\n\r\n// wantsVerdict: шаг проверки, Claude сдаёт вердикт инструментом, а не текстом\r\n// handoffs: куда Claude может передать задачу инструментом hand_off; пусто: инструмента нет\r\n// resumeSessionId: сеанс Claude Code прошлого запуска шага, разговор продолжается\r\n// onFeed: каждая запись журнала, attach: скриншот, запись браузера или видео к задаче, ответ уходит Claude как результат инструмента\r\n// model и effort: null значит как настроено в Claude Code; onSession: какая модель и чей вход на самом деле начали работу\r\n// input: первое сообщение, сообщения людей по ходу работы и смена модели\r\n// specWork: шаг ТЗ, Claude сдаёт вопросы или ТЗ своими инструментами, а сайт их проверяет; null на шагах кода и проверки\r\nexport interface AgentRequest {\r\n cwd: string\r\n prompt: TaskPrompt\r\n input: AgentInput\r\n env: Record<string, string | undefined>\r\n signal: AbortSignal\r\n wantsVerdict: boolean\r\n specWork: SpecWorkTools | null\r\n handoffs: RunnerHandoffTarget[]\r\n resumeSessionId: string | null\r\n model: string | null\r\n effort: RunnerEffort | null\r\n onFeed: (draft: FeedDraft) => void\r\n onSession: (session: RunnerSession) => void\r\n attach: (request: AttachRequest) => Promise<string>\r\n}\r\n\r\nexport interface SpecWorkTools {\r\n stage: 'interview' | 'spec'\r\n review: ReviewRequest\r\n}\r\n\r\nexport type RunAgent = (request: AgentRequest) => Promise<AgentOutcome>\r\n\r\nexport type StartQuery = typeof query\r\n\r\nconst NOBODY_TO_ASK = 'Nobody is at the computer to approve this during a TAMsense run. Do it another way, or explain in your report why it is needed.'\r\nconst NO_RESULT_TEXT = 'Claude finished without a result.'\r\nconst SERVER_NAME = 'tamsense'\r\n// библиотека помощников и навыков TAMsense лежит рядом с программой и подключается только на время задачи\r\nconst LIBRARY_PATH = fileURLToPath(new URL('../library', import.meta.url))\r\n\r\n// Режим auto сам решает, что можно; то, на что нужен человек, отклоняется: у компьютера никого нет\r\nconst denyWhenAsked: CanUseTool = async () => ({ behavior: 'deny', message: NOBODY_TO_ASK })\r\n\r\nconst CAPTION_FIELDS = {\r\n title: z.string().describe('short name of what it shows, at most 60 characters: \"Design option 1\", \"Feature testing\", \"Feature testing, attempt 2\". Never generic names like \"screenshot1\"'),\r\n purpose: z.string().describe('one line, at most 160 characters, on why it was made: \"Comparing two layouts for the empty orders state\"'),\r\n}\r\n\r\nconst ATTACH_TOOLS = {\r\n screenshot: {\r\n name: SCREENSHOT_TOOL_NAME,\r\n description: 'Attach a screenshot to the TAMsense task so people can see the result without opening the code. Attach the final look of each changed screen and anything a reviewer must see. PNG, JPEG or WebP up to 5 MB, at most 20 per run.',\r\n path: 'absolute path of the image file',\r\n },\r\n replay: {\r\n name: REPLAY_TOOL_NAME,\r\n description: 'Attach a browser replay (the .rrweb.ndjson file the TAMsense recorder wrote) to the task now, with your own title and purpose. Every recording is attached at the end of the run anyway; use this to attach one earlier or to name it better. Close the browser context first so the file is complete.',\r\n path: 'absolute path of the .rrweb.ndjson replay file',\r\n },\r\n video: {\r\n name: VIDEO_TOOL_NAME,\r\n description: 'Attach a browser video (.webm or .mp4, up to 200 MB) to the task now, with your own title and purpose. Videos recorded with video: true are attached at the end of the run anyway. Close the browser context first so the file is complete.',\r\n path: 'absolute path of the video file',\r\n },\r\n} as const\r\n\r\nfunction attachTool(kind: AttachRequest['kind'], attach: AgentRequest['attach']) {\r\n const spec = ATTACH_TOOLS[kind]\r\n return tool(\r\n spec.name,\r\n spec.description,\r\n { path: z.string().describe(spec.path), ...CAPTION_FIELDS },\r\n async args => ({ content: [{ type: 'text', text: await attach({ kind, ...args }) }] }),\r\n )\r\n}\r\n\r\nfunction verdictTool(onVerdict: (verdict: RunnerVerdict) => void) {\r\n return tool(\r\n VERDICT_TOOL_NAME,\r\n 'Hand in the result of this check. Call it exactly once, at the very end.',\r\n {\r\n passed: z.boolean().describe('true only if nothing must be fixed'),\r\n report: z.string().describe('what you checked and, if it failed, exactly what to fix with file and line'),\r\n },\r\n async (args) => {\r\n onVerdict({ passed: args.passed, report: args.report })\r\n return { content: [{ type: 'text', text: 'Verdict received. You can stop now.' }] }\r\n },\r\n )\r\n}\r\n\r\n// Передача проверяется здесь же: ошибка с перечнем допустимых шагов уходит Claude, принятая заменяет прежнюю\r\nexport function handoffTool(targets: RunnerHandoffTarget[], onHandoff: (handoff: RunnerHandoff) => void) {\r\n return tool(\r\n HANDOFF_TOOL_NAME,\r\n 'Hand the task to the next step of the TAMsense board when you finish. The next agent and the task owner read your note. Only one handoff counts: calling it again replaces the earlier one.',\r\n {\r\n to: z.string().describe('name or id of the step to hand the task to'),\r\n outcome: z.enum(HANDOFF_OUTCOMES).describe('pass: the work is done; problems: something must be fixed; ask: a question for that step'),\r\n note: z.string().describe('for the next agent and the owner: what was done, what to check, what is open'),\r\n },\r\n async (args) => {\r\n const choice = chooseHandoff(targets, args)\r\n if ('error' in choice) {\r\n return { content: [{ type: 'text', text: choice.error }], isError: true }\r\n }\r\n onHandoff(choice.handoff)\r\n return { content: [{ type: 'text', text: 'Handoff recorded. It happens when you finish.' }] }\r\n },\r\n )\r\n}\r\n\r\n// helperStarted: Claude уже звал свежего помощника в этом запуске; по нему инструмент ТЗ знает, была ли проверка свежим читателем\r\ninterface Submitted {\r\n verdict: RunnerVerdict | null\r\n handoff: RunnerHandoff | null\r\n questions: RunnerQuestions | null\r\n spec: RunnerSpecResult | null\r\n helperStarted: boolean\r\n}\r\n\r\nfunction specWorkTools(specWork: SpecWorkTools | null, submitted: Submitted) {\r\n if (!specWork) {\r\n return []\r\n }\r\n return specWork.stage === 'interview'\r\n ? [questionsTool(specWork.review, (questions) => { submitted.questions = questions })]\r\n : [specTool(specWork.review, (spec) => { submitted.spec = spec }, () => submitted.helperStarted)]\r\n}\r\n\r\n// Свои инструменты TAMsense: скриншоты, записи браузера и видео всегда, вердикт только на шаге проверки, вопросы или ТЗ на шаге ТЗ,\r\n// передача, если доска её разрешает\r\nfunction tamsenseServer(request: AgentRequest, submitted: Submitted): { server: McpSdkServerConfigWithInstance, allowed: string[] } {\r\n const tools = [\r\n attachTool('screenshot', request.attach),\r\n attachTool('replay', request.attach),\r\n attachTool('video', request.attach),\r\n ...(request.wantsVerdict ? [verdictTool((verdict) => { submitted.verdict = verdict })] : []),\r\n ...specWorkTools(request.specWork, submitted),\r\n ...(request.handoffs.length > 0 ? [handoffTool(request.handoffs, (handoff) => { submitted.handoff = handoff })] : []),\r\n ]\r\n return {\r\n server: createSdkMcpServer({ name: SERVER_NAME, version: '1.0.0', tools }),\r\n allowed: tools.map(item => `mcp__${SERVER_NAME}__${item.name}`),\r\n }\r\n}\r\n\r\nexport function modelOptions(request: Pick<AgentRequest, 'model' | 'effort'>): Pick<Options, 'model' | 'effort'> {\r\n return {\r\n ...(request.model === null ? {} : { model: request.model }),\r\n ...(request.effort === null ? {} : { effort: request.effort }),\r\n }\r\n}\r\n\r\nexport async function applySwitch(target: Pick<Query, 'setModel' | 'applyFlagSettings'>, change: ModelSwitch): Promise<void> {\r\n if (change.model !== null) {\r\n await target.setModel(change.model === DEFAULT_MODEL ? undefined : change.model)\r\n }\r\n if (change.effort !== null) {\r\n await target.applyFlagSettings({ effortLevel: change.effort })\r\n }\r\n}\r\n\r\n// resumeFailed: Claude Code не смог продолжить прошлый сеанс и не начал работу\r\ninterface Attempt {\r\n outcome: AgentOutcome\r\n sessionId: string | null\r\n resumeFailed: boolean\r\n}\r\n\r\ninterface AttemptContext {\r\n startQuery: StartQuery\r\n abortController: AbortController\r\n submitted: Submitted\r\n limit: UsageLimitWatch\r\n}\r\n\r\nasync function attempt(request: AgentRequest, context: AttemptContext, resume: string | null): Promise<Attempt> {\r\n let lastText = ''\r\n let sessionId: string | null = null\r\n let outcome: AgentOutcome = { completed: false, report: NO_RESULT_TEXT, model: null, usage: null, verdict: null }\r\n const tamsense = tamsenseServer(request, context.submitted)\r\n const shown = hiddenStepsFilter()\r\n const messages = context.startQuery({\r\n prompt: request.input.start(request.prompt.prompt),\r\n options: {\r\n cwd: request.cwd,\r\n systemPrompt: { type: 'preset', preset: 'claude_code', append: request.prompt.instructions },\r\n settingSources: ['user', 'project', 'local'],\r\n permissionMode: 'auto',\r\n // краткий пересказ размышлений Claude идёт в журнал задачи, как в Paseo\r\n thinking: { type: 'adaptive', display: 'summarized' },\r\n canUseTool: denyWhenAsked,\r\n plugins: [{ type: 'local', path: LIBRARY_PATH }],\r\n mcpServers: { [SERVER_NAME]: tamsense.server },\r\n allowedTools: tamsense.allowed,\r\n abortController: context.abortController,\r\n env: request.env,\r\n ...modelOptions(request),\r\n ...(resume === null ? {} : { resume }),\r\n },\r\n })\r\n request.input.onSwitch(change => applySwitch(messages, change))\r\n const failedToResume = () => resume !== null && sessionId === null && !request.signal.aborted\r\n try {\r\n for await (const message of messages) {\r\n if (message.type === 'system' && message.subtype === 'init') {\r\n sessionId = message.session_id\r\n request.onSession(sessionOf(message))\r\n }\r\n if (message.type === 'result' && (message.is_error || message.subtype !== 'success') && failedToResume()) {\r\n messages.close()\r\n return { outcome, sessionId, resumeFailed: true }\r\n }\r\n for (const draft of feedOf(message).filter(shown)) {\r\n if (draft.kind === 'agent' && draft.parent === null) {\r\n context.submitted.helperStarted = true\r\n }\r\n request.onFeed(draft)\r\n if (draft.kind === 'text' && draft.parent === null) {\r\n lastText = draft.text\r\n }\r\n }\r\n if (message.type === 'result') {\r\n outcome = outcomeOf(message, lastText)\r\n }\r\n context.limit.observe(message)\r\n request.input.observe(message)\r\n }\r\n }\r\n catch (error) {\r\n if (failedToResume()) {\r\n return { outcome, sessionId, resumeFailed: true }\r\n }\r\n throw error\r\n }\r\n return { outcome, sessionId, resumeFailed: false }\r\n}\r\n\r\n// Обычный Claude Code: его стандартные инструкции, настройки и навыки человека, вход человека в Claude.\r\n// Прошлый сеанс шага продолжается; если Claude Code не может его открыть, разговор начинается заново один раз\r\nexport function claudeRunner(startQuery: StartQuery): RunAgent {\r\n return async (request) => {\r\n request.signal.throwIfAborted()\r\n const abortController = new AbortController()\r\n request.signal.addEventListener('abort', () => {\r\n request.input.close()\r\n abortController.abort()\r\n }, { once: true })\r\n const submitted: Submitted = { verdict: null, handoff: null, questions: null, spec: null, helperStarted: false }\r\n const limit = usageLimitWatch()\r\n const context: AttemptContext = { startQuery, abortController, submitted, limit }\r\n try {\r\n let result = await attempt(request, context, request.resumeSessionId)\r\n if (result.resumeFailed) {\r\n result = await attempt(request, context, null)\r\n }\r\n const pausedUntil = limit.pausedUntil(result.outcome.completed)\r\n const outcome = pausedUntil ? { ...result.outcome, completed: false, report: USAGE_LIMIT_REPORT } : result.outcome\r\n return { ...outcome, verdict: submitted.verdict, handoff: submitted.handoff, questions: submitted.questions, spec: submitted.spec, sessionId: result.sessionId, pausedUntil }\r\n }\r\n finally {\r\n request.input.close()\r\n }\r\n }\r\n}\r\n\r\nexport const runClaude: RunAgent = claudeRunner(query)\r\n","import type { ModelUsage, SDKResultMessage } from '@anthropic-ai/claude-agent-sdk'\r\nimport type { RunnerHandoff, RunnerQuestions, RunnerSpecResult, RunnerUsage, RunnerVerdict } from './protocol'\r\n\r\n// handoff: куда Claude передал задачу инструментом hand_off; sessionId: сеанс Claude Code для продолжения;\r\n// pausedUntil: Claude упёрся в лимит, когда можно повторить (ISO); questions и spec: что Claude сдал на шаге ТЗ\r\nexport interface AgentOutcome {\r\n completed: boolean\r\n report: string\r\n model: string | null\r\n usage: RunnerUsage | null\r\n verdict: RunnerVerdict | null\r\n handoff?: RunnerHandoff | null\r\n sessionId?: string | null\r\n pausedUntil?: string | null\r\n questions?: RunnerQuestions | null\r\n spec?: RunnerSpecResult | null\r\n}\r\n\r\nconst MAX_ACTIVITY_LENGTH = 120\r\nconst FILE_TOOLS = new Set(['Read', 'Edit', 'Write', 'MultiEdit', 'NotebookEdit'])\r\n// помощник Claude Code в разных версиях называется Task или Agent\r\nconst HELPER_TOOLS = new Set(['Task', 'Agent'])\r\n\r\nfunction shorten(text: string): string {\r\n const line = text.replace(/\\s+/g, ' ').trim()\r\n return line.length > MAX_ACTIVITY_LENGTH ? `${line.slice(0, MAX_ACTIVITY_LENGTH - 1)}…` : line\r\n}\r\n\r\n// Строка для терминала: человек видит, чем Claude занят прямо сейчас\r\nexport function activityLine(tool: string, input: unknown): string {\r\n const fields = typeof input === 'object' && input !== null ? input as Record<string, unknown> : {}\r\n if (tool === 'Bash' && typeof fields.command === 'string') {\r\n return `$ ${shorten(fields.command)}`\r\n }\r\n if (FILE_TOOLS.has(tool) && typeof fields.file_path === 'string') {\r\n return `${tool} ${shorten(fields.file_path)}`\r\n }\r\n if (tool === 'Skill' && typeof fields.skill === 'string') {\r\n return `Skill ${shorten(fields.skill)}`\r\n }\r\n if (HELPER_TOOLS.has(tool) && typeof fields.subagent_type === 'string') {\r\n return `Agent ${shorten(fields.subagent_type)}`\r\n }\r\n return tool\r\n}\r\n\r\n// Расход по всем моделям запуска, включая помощников, которых Claude звал сам\r\nexport function usageOf(modelUsage: Record<string, ModelUsage>): RunnerUsage | null {\r\n const entries = Object.values(modelUsage)\r\n if (entries.length === 0) {\r\n return null\r\n }\r\n const sum = (pick: (usage: ModelUsage) => number) => entries.reduce((total, usage) => total + pick(usage), 0)\r\n const inputTokens = sum(usage => usage.inputTokens + usage.cacheReadInputTokens + usage.cacheCreationInputTokens)\r\n const outputTokens = sum(usage => usage.outputTokens)\r\n return { inputTokens, cachedInputTokens: sum(usage => usage.cacheReadInputTokens), outputTokens, totalTokens: inputTokens + outputTokens }\r\n}\r\n\r\n// Основной моделью считается та, что написала больше всего\r\nexport function mainModel(modelUsage: Record<string, ModelUsage>): string | null {\r\n const ranked = Object.entries(modelUsage).sort(([, first], [, second]) => second.outputTokens - first.outputTokens)\r\n return ranked[0]?.[0] ?? null\r\n}\r\n\r\nexport function outcomeOf(result: SDKResultMessage, lastText: string): AgentOutcome {\r\n const measured = { model: mainModel(result.modelUsage), usage: usageOf(result.modelUsage), verdict: null }\r\n if (result.subtype === 'success') {\r\n return { completed: !result.is_error, report: result.result || lastText, ...measured }\r\n }\r\n return { completed: false, report: result.errors.join('\\n') || `Claude stopped early: ${result.subtype}`, ...measured }\r\n}\r\n","import type { SDKMessage } from '@anthropic-ai/claude-agent-sdk'\r\nimport type { RunnerFeedEntry } from './protocol'\r\nimport { activityLine } from './claudeResult'\r\n\r\nexport type FeedDraft = Omit<RunnerFeedEntry, 'seq'>\r\n\r\nconst MAX_TEXT_LENGTH = 8_000\r\nconst MAX_RESULT_LENGTH = 2_000\r\nconst MAX_SUMMARY_LENGTH = 500\r\nconst HELPER_TOOLS = new Set(['Task', 'Agent'])\r\nconst MCP_PREFIX = /^mcp__[^_]+(?:_[^_]+)*__/\r\nconst ATTACH_LABELS: Record<string, string> = { attach_screenshot: 'Screenshot', attach_replay: 'Replay', attach_video: 'Video' }\r\n\r\ninterface ContentBlock {\r\n type: string\r\n text?: string\r\n thinking?: string\r\n id?: string\r\n name?: string\r\n input?: unknown\r\n tool_use_id?: string\r\n content?: unknown\r\n is_error?: boolean\r\n}\r\n\r\nfunction clip(text: string, max: number): string {\r\n const trimmed = text.trim()\r\n return trimmed.length > max ? `${trimmed.slice(0, max - 1)}…` : trimmed\r\n}\r\n\r\nfunction fieldsOf(input: unknown): Record<string, unknown> {\r\n return typeof input === 'object' && input !== null ? input as Record<string, unknown> : {}\r\n}\r\n\r\nfunction textField(fields: Record<string, unknown>, name: string): string | null {\r\n const value = fields[name]\r\n return typeof value === 'string' && value.trim() ? value : null\r\n}\r\n\r\n// Вызов инструмента одной строкой: команда, файл, навык, помощник или свой инструмент TAMsense\r\nfunction toolDraft(block: ContentBlock, parent: string | null): FeedDraft {\r\n const tool = (block.name ?? 'tool').replace(MCP_PREFIX, '')\r\n const fields = fieldsOf(block.input)\r\n const base = { tool, ref: block.id ?? null, parent, failed: false }\r\n if (tool === 'Skill' && textField(fields, 'skill')) {\r\n return { ...base, kind: 'skill', text: clip(textField(fields, 'skill')!, MAX_SUMMARY_LENGTH) }\r\n }\r\n if (HELPER_TOOLS.has(tool)) {\r\n const who = textField(fields, 'subagent_type') ?? 'general-purpose'\r\n const what = textField(fields, 'description')\r\n return { ...base, kind: 'agent', text: clip(what ? `${who}: ${what}` : who, MAX_SUMMARY_LENGTH) }\r\n }\r\n if (tool === 'submit_verdict') {\r\n const verdict = fields.passed === true ? 'Passed' : 'Did not pass'\r\n return { ...base, kind: 'tool', text: clip(`${verdict}. ${textField(fields, 'report') ?? ''}`, MAX_SUMMARY_LENGTH) }\r\n }\r\n if (tool === 'hand_off') {\r\n return { ...base, kind: 'tool', text: clip(`Hand off to ${textField(fields, 'to') ?? '?'}: ${textField(fields, 'note') ?? ''}`, MAX_SUMMARY_LENGTH) }\r\n }\r\n if (tool in ATTACH_LABELS) {\r\n return { ...base, kind: 'tool', text: clip(`${ATTACH_LABELS[tool]}: ${textField(fields, 'title') ?? textField(fields, 'path') ?? ''}`, MAX_SUMMARY_LENGTH) }\r\n }\r\n const command = textField(fields, 'command')\r\n const summary = tool === 'Bash' && command ? `$ ${command}` : activityLine(tool, block.input)\r\n return { ...base, kind: 'tool', text: clip(summary, MAX_SUMMARY_LENGTH) }\r\n}\r\n\r\nfunction resultText(content: unknown): string {\r\n if (typeof content === 'string') {\r\n return content\r\n }\r\n if (!Array.isArray(content)) {\r\n return ''\r\n }\r\n return content\r\n .map((part: ContentBlock) => part.type === 'text' ? part.text ?? '' : part.type === 'image' ? '[image]' : '')\r\n .filter(Boolean)\r\n .join('\\n')\r\n}\r\n\r\n// Сообщение Claude Code превращается в записи журнала; служебные сообщения и итог запуска в журнал не идут\r\nexport function feedOf(message: SDKMessage): FeedDraft[] {\r\n if (message.type !== 'assistant' && message.type !== 'user') {\r\n return []\r\n }\r\n const parent = message.parent_tool_use_id ?? null\r\n const content = message.message.content\r\n if (!Array.isArray(content)) {\r\n return []\r\n }\r\n const drafts: FeedDraft[] = []\r\n for (const block of content as ContentBlock[]) {\r\n if (message.type === 'assistant' && block.type === 'thinking' && block.thinking?.trim()) {\r\n drafts.push({ kind: 'thinking', text: clip(block.thinking, MAX_TEXT_LENGTH), tool: null, ref: null, parent, failed: false })\r\n }\r\n else if (message.type === 'assistant' && block.type === 'text' && block.text?.trim()) {\r\n drafts.push({ kind: 'text', text: clip(block.text, MAX_TEXT_LENGTH), tool: null, ref: null, parent, failed: false })\r\n }\r\n else if (message.type === 'assistant' && block.type === 'tool_use') {\r\n drafts.push(toolDraft(block, parent))\r\n }\r\n else if (message.type === 'user' && block.type === 'tool_result') {\r\n drafts.push({ kind: 'result', text: clip(resultText(block.content), MAX_RESULT_LENGTH), tool: null, ref: block.tool_use_id ?? null, parent, failed: block.is_error === true })\r\n }\r\n }\r\n return drafts\r\n}\r\n\r\nconst CONSOLE_LABELS: Partial<Record<FeedDraft['kind'], string>> = { tool: '', skill: 'Skill ', agent: 'Agent ' }\r\nconst MAX_CONSOLE_LENGTH = 120\r\n\r\n// В окне программы только действия Claude, его размышления и ответы видны на сайте\r\nexport function consoleLine(draft: FeedDraft): string | null {\r\n const label = CONSOLE_LABELS[draft.kind]\r\n return label === undefined ? null : clip(`${label}${draft.text}`.replace(/\\s+/g, ' '), MAX_CONSOLE_LENGTH)\r\n}\r\n\r\n// Служебные вызовы Claude Code человеку ничего не говорят: ни они, ни их результаты в журнал не идут\r\nconst HIDDEN_TOOLS = new Set(['ToolSearch', 'SubagentHandback'])\r\n\r\nexport function hiddenStepsFilter(): (draft: FeedDraft) => boolean {\r\n const hidden = new Set<string>()\r\n return (draft) => {\r\n if (draft.kind === 'tool' && draft.tool && HIDDEN_TOOLS.has(draft.tool)) {\r\n if (draft.ref) {\r\n hidden.add(draft.ref)\r\n }\r\n return false\r\n }\r\n return !(draft.kind === 'result' && draft.ref && hidden.has(draft.ref))\r\n }\r\n}\r\n","import type { RunnerHandoff, RunnerHandoffOutcome, RunnerHandoffTarget } from './protocol'\r\n\r\nexport const HANDOFF_OUTCOMES = ['pass', 'problems', 'ask'] as const satisfies readonly RunnerHandoffOutcome[]\r\n\r\nexport interface HandoffRequest {\r\n to: string\r\n outcome: RunnerHandoffOutcome\r\n note: string\r\n}\r\n\r\nexport type HandoffChoice = { handoff: RunnerHandoff } | { error: string }\r\n\r\nexport function handoffTargetLine(target: RunnerHandoffTarget): string {\r\n return target.label ? `${target.name} (${target.outcome}: ${target.label})` : `${target.name} (${target.outcome})`\r\n}\r\n\r\nfunction validTargets(targets: RunnerHandoffTarget[]): string {\r\n return `Valid targets: ${targets.map(handoffTargetLine).join(', ')}.`\r\n}\r\n\r\n// Шаг ищется по имени без учёта регистра или по номеру; передать можно только по правилу доски с этим итогом\r\nexport function chooseHandoff(targets: RunnerHandoffTarget[], request: HandoffRequest): HandoffChoice {\r\n const to = request.to.trim()\r\n const note = request.note.trim()\r\n if (!note) {\r\n return { error: 'Write a note for the next agent and the owner: what was done, what to check and what is open.' }\r\n }\r\n const matching = targets.filter(target => target.name.toLowerCase() === to.toLowerCase() || String(target.columnId) === to)\r\n if (matching.length === 0) {\r\n return { error: `\"${to}\" is not a step this task can be handed to. ${validTargets(targets)}` }\r\n }\r\n const edge = matching.find(target => target.outcome === request.outcome)\r\n if (!edge) {\r\n return { error: `${matching[0]!.name} does not take the outcome \"${request.outcome}\". ${validTargets(targets)}` }\r\n }\r\n return { handoff: { toColumnId: edge.columnId, outcome: edge.outcome, note } }\r\n}\r\n","import type { SDKMessage, SDKUserMessage } from '@anthropic-ai/claude-agent-sdk'\r\nimport type { RunnerEffort, RunnerInboxMessage } from './protocol'\r\n\r\n// Смена модели по ходу работы; model 'default' значит вернуться к модели Claude Code на компьютере\r\nexport interface ModelSwitch {\r\n model: string | null\r\n effort: RunnerEffort | null\r\n}\r\n\r\nexport const DEFAULT_MODEL = 'default'\r\n\r\n// Что видит запуск Claude: первое сообщение, ходы Claude и смена модели\r\nexport interface AgentInput {\r\n start: (prompt: string) => AsyncIterable<SDKUserMessage>\r\n observe: (message: SDKMessage) => void\r\n onSwitch: (apply: (change: ModelSwitch) => Promise<void>) => void\r\n close: () => void\r\n}\r\n\r\n// Что видит программа: сообщения людей, проверка входящих и что уже передано Claude\r\nexport interface InputChannel extends AgentInput {\r\n readonly closed: boolean\r\n push: (message: RunnerInboxMessage) => boolean\r\n checking: (work: () => Promise<void>) => Promise<void>\r\n takeDelivered: () => number[]\r\n restoreDelivered: (ids: number[]) => void\r\n hasDelivered: () => boolean\r\n switchModel: (change: ModelSwitch) => Promise<boolean>\r\n}\r\n\r\n// onIdle: Claude закончил ход, пора проверить, не написали ли ему ещё;\r\n// quietMs: сколько после хода ждать, не начнёт ли Claude Code следующий сам;\r\n// idleLimitMs: сколько ждать проверки после хода, прежде чем закрыть вход в любом случае\r\nexport interface InputOptions {\r\n onIdle?: () => void\r\n quietMs?: number\r\n idleLimitMs?: number\r\n}\r\n\r\nconst QUIET_MS = 1_000\r\nconst IDLE_LIMIT_MS = 30_000\r\n\r\ninterface Queued {\r\n ids: number[]\r\n message: SDKUserMessage\r\n}\r\n\r\nfunction userMessage(text: string): SDKUserMessage {\r\n return { type: 'user', message: { role: 'user', content: text }, parent_tool_use_id: null }\r\n}\r\n\r\nfunction humanText(message: RunnerInboxMessage): string {\r\n return `Message from ${message.authorName}: ${message.text}`\r\n}\r\n\r\n// steer: now прерывает текущий шаг Claude и начинает новый ход с сообщением; queue: next доходит на ближайшей границе шагов\r\nexport function humanMessage(message: RunnerInboxMessage): SDKUserMessage {\r\n return { ...userMessage(humanText(message)), priority: message.mode === 'steer' ? 'now' : 'next', client_composed: true }\r\n}\r\n\r\nexport function switchLine(change: ModelSwitch): string {\r\n const model = change.model === DEFAULT_MODEL ? 'the default model' : change.model\r\n const effort = change.effort && `${change.effort} effort`\r\n return `Switched to ${[model, effort].filter(Boolean).join(', ')}`\r\n}\r\n\r\n// Признаки того, что Claude Code работает над ходом: после итога они значат, что начался следующий\r\nfunction isActivity(message: SDKMessage): boolean {\r\n return message.type === 'assistant' || message.type === 'user' || (message.type === 'system' && message.subtype === 'init')\r\n}\r\n\r\n// Вход закрывается, когда Claude закончил ход, ждать нечего и проверка после хода прошла:\r\n// тогда запуск кончается как раньше, а сообщение, пришедшее под конец, ещё успевает дойти\r\nexport function inputChannel(options: InputOptions = {}): InputChannel {\r\n const queue: Queued[] = []\r\n const seen = new Set<number>()\r\n let delivered: number[] = []\r\n let started = false\r\n // первое сообщение запуска и номер потока, который сейчас читает Claude Code\r\n let opening = ''\r\n let generation = 0\r\n let closed = false\r\n // ход кончился, и с тех пор ничего не началось\r\n let idle = false\r\n // Claude Code уже отозвался на текущий ход\r\n let active = false\r\n // сколько итогов прерванных ходов ещё придёт: steer прерывает ход, и тот кончается своим итогом\r\n let interrupted = 0\r\n let drained = false\r\n let quiet = false\r\n let idleEpoch = 0\r\n let inFlight = 0\r\n let statesSeen = false\r\n let wake: (() => void) | null = null\r\n let timers: Array<ReturnType<typeof setTimeout>> = []\r\n let applySwitch: ((change: ModelSwitch) => Promise<void>) | null = null\r\n let waitingSwitch: { change: ModelSwitch, resolve: (applied: boolean) => void } | null = null\r\n\r\n function wakeUp(): void {\r\n wake?.()\r\n wake = null\r\n }\r\n\r\n function clearTimers(): void {\r\n timers.forEach(clearTimeout)\r\n timers = []\r\n }\r\n\r\n function close(): void {\r\n if (closed) {\r\n return\r\n }\r\n closed = true\r\n clearTimers()\r\n waitingSwitch?.resolve(false)\r\n waitingSwitch = null\r\n wakeUp()\r\n }\r\n\r\n function maybeClose(): void {\r\n if (started && idle && drained && quiet && queue.length === 0 && inFlight === 0) {\r\n close()\r\n }\r\n }\r\n\r\n function turnStarted(): void {\r\n idle = false\r\n clearTimers()\r\n }\r\n\r\n function turnEnded(): void {\r\n if (closed || idle) {\r\n return\r\n }\r\n idle = true\r\n idleEpoch += 1\r\n drained = !options.onIdle\r\n quiet = false\r\n timers = [\r\n setTimeout(() => {\r\n quiet = true\r\n maybeClose()\r\n }, options.quietMs ?? QUIET_MS),\r\n setTimeout(close, options.idleLimitMs ?? IDLE_LIMIT_MS),\r\n ]\r\n options.onIdle?.()\r\n }\r\n\r\n async function* stream(own: number): AsyncGenerator<SDKUserMessage> {\r\n for (;;) {\r\n if (closed || own !== generation) {\r\n return\r\n }\r\n const next = queue.shift()\r\n if (!next) {\r\n await new Promise<void>((resolve) => {\r\n wake = resolve\r\n })\r\n continue\r\n }\r\n if (active && !idle && next.message.priority === 'now') {\r\n interrupted += 1\r\n }\r\n turnStarted()\r\n delivered.push(...next.ids)\r\n yield next.message\r\n }\r\n }\r\n\r\n return {\r\n get closed() {\r\n return closed\r\n },\r\n // Сообщения, пришедшие, пока готовился код, идут вместе с задачей.\r\n // Повторный start для нового Claude Code (прошлый сеанс не открылся) шлёт то же первое сообщение снова, а прежний поток кончается\r\n start(prompt) {\r\n if (started) {\r\n queue.unshift({ ids: [], message: userMessage(opening) })\r\n }\r\n else {\r\n started = true\r\n const early = queue.splice(0)\r\n opening = [prompt, ...early.map(item => item.message.message.content as string)].join('\\n\\n')\r\n queue.push({ ids: early.flatMap(item => item.ids), message: userMessage(opening) })\r\n }\r\n generation += 1\r\n wakeUp()\r\n return stream(generation)\r\n },\r\n // Если Claude Code сообщает состояние сессии, верим только ему: idle приходит после фоновых задач\r\n observe(message) {\r\n if (message.type === 'system' && message.subtype === 'session_state_changed') {\r\n statesSeen = true\r\n if (message.state === 'idle') {\r\n turnEnded()\r\n }\r\n else {\r\n turnStarted()\r\n }\r\n return\r\n }\r\n if (statesSeen) {\r\n return\r\n }\r\n if (message.type === 'result') {\r\n active = false\r\n if (interrupted > 0) {\r\n interrupted -= 1\r\n return\r\n }\r\n turnEnded()\r\n }\r\n else if (isActivity(message)) {\r\n active = true\r\n turnStarted()\r\n }\r\n },\r\n onSwitch(apply) {\r\n applySwitch = apply\r\n if (waitingSwitch) {\r\n const { change, resolve } = waitingSwitch\r\n waitingSwitch = null\r\n void apply(change).then(() => resolve(true), () => resolve(false))\r\n }\r\n },\r\n close,\r\n push(message) {\r\n if (closed || seen.has(message.id)) {\r\n return false\r\n }\r\n seen.add(message.id)\r\n queue.push({ ids: [message.id], message: humanMessage(message) })\r\n wakeUp()\r\n return true\r\n },\r\n async checking(work) {\r\n const epoch = idle ? idleEpoch : null\r\n inFlight += 1\r\n try {\r\n await work()\r\n }\r\n finally {\r\n inFlight -= 1\r\n if (idle && epoch === idleEpoch) {\r\n drained = true\r\n }\r\n maybeClose()\r\n }\r\n },\r\n takeDelivered() {\r\n const ids = delivered\r\n delivered = []\r\n return ids\r\n },\r\n restoreDelivered(ids) {\r\n delivered = [...ids, ...delivered]\r\n },\r\n hasDelivered: () => delivered.length > 0,\r\n // До запуска Claude смена ждёт его; после конца работы менять уже нечего\r\n async switchModel(change) {\r\n if (closed) {\r\n return false\r\n }\r\n if (applySwitch) {\r\n await applySwitch(change)\r\n return true\r\n }\r\n const earlier = waitingSwitch\r\n earlier?.resolve(false)\r\n const merged = { model: change.model ?? earlier?.change.model ?? null, effort: change.effort ?? earlier?.change.effort ?? null }\r\n return new Promise<boolean>((resolve) => {\r\n waitingSwitch = { change: merged, resolve }\r\n })\r\n },\r\n }\r\n}\r\n","// ТЗ задачи компактным текстом для Claude: те же разделы, что видит человек на сайте.\r\n// Повторяет specPromptText сайта (apps/web/shared/utils/taskSpecView.ts): программа ставится на компьютер отдельно\r\n// и пакетов сайта не видит. Старые ТЗ (критерии и допущения строками, без новых разделов) читаются так же\r\n\r\ntype Fields = Record<string, unknown>\r\n\r\nfunction isRecord(value: unknown): value is Fields {\r\n return typeof value === 'object' && value !== null && !Array.isArray(value)\r\n}\r\n\r\nfunction text(value: unknown): string {\r\n return typeof value === 'string' ? value.trim() : ''\r\n}\r\n\r\nfunction texts(value: unknown): string[] {\r\n return Array.isArray(value) ? value.map(text).filter(Boolean) : []\r\n}\r\n\r\nfunction records(value: unknown): Fields[] {\r\n return Array.isArray(value) ? value.filter(isRecord) : []\r\n}\r\n\r\nfunction section(title: string, lines: string[]): string | null {\r\n return lines.length > 0 ? `${title}\\n${lines.join('\\n')}` : null\r\n}\r\n\r\nfunction criterionLine(value: unknown): string | null {\r\n if (typeof value === 'string') {\r\n return value.trim() ? `- ${value.trim()}` : null\r\n }\r\n if (!isRecord(value) || !text(value.text)) {\r\n return null\r\n }\r\n const kind = ['main', 'edge', 'keep'].includes(value.kind as string) ? `(${value.kind as string})` : ''\r\n const head = [text(value.id), kind].filter(Boolean).join(' ')\r\n return `- ${head ? `${head}: ` : ''}${text(value.text)}`\r\n}\r\n\r\nfunction assumptionLine(value: unknown): string | null {\r\n if (typeof value === 'string') {\r\n return value.trim() ? `- ${value.trim()}` : null\r\n }\r\n if (!isRecord(value) || !text(value.text)) {\r\n return null\r\n }\r\n return `- ${text(value.text)}${text(value.reason) ? ` (${text(value.reason)})` : ''}`\r\n}\r\n\r\nfunction changeLines(change: Fields, index: number): string[] {\r\n const action = ['create', 'modify', 'delete'].includes(change.action as string) ? ` (${change.action as string})` : ''\r\n const [produces, consumes, acceptance] = [texts(change.produces), texts(change.consumes), texts(change.acceptance)]\r\n return [\r\n `${index + 1}. ${text(change.repo) ? `${text(change.repo)} ` : ''}${text(change.file)}${action}: ${text(change.description)}`,\r\n ...(produces.length > 0 ? [` Produces: ${produces.join('; ')}`] : []),\r\n ...(consumes.length > 0 ? [` Consumes: ${consumes.join('; ')}`] : []),\r\n ...(acceptance.length > 0 ? [` Makes true: ${acceptance.join(', ')}`] : []),\r\n ]\r\n}\r\n\r\nconst CODE_USE_TEXT: Record<string, string> = { reuse: 'reuse', change: 'change', keep: 'do not touch' }\r\nconst RISK_LEVELS = ['low', 'medium', 'high']\r\n\r\nfunction present(lines: Array<string | null>): string[] {\r\n return lines.filter((line): line is string => line !== null)\r\n}\r\n\r\nexport function specPromptText(spec: unknown): string | null {\r\n if (!isRecord(spec)) {\r\n return null\r\n }\r\n const structured = Array.isArray(spec.outOfScope) || isRecord(spec.testPlan)\r\n const plan = isRecord(spec.testPlan) ? spec.testPlan : {}\r\n const risks = records(spec.risks).filter(item => text(item.risk))\r\n const riskLevel = RISK_LEVELS.includes(spec.riskLevel as string) ? spec.riskLevel as string : null\r\n return [\r\n text(spec.goal) && `Goal: ${text(spec.goal)}`,\r\n text(spec.context) && `${structured ? 'Summary' : 'Context'}: ${text(spec.context)}`,\r\n section('In scope:', texts(spec.inScope).map(item => `- ${item}`)),\r\n section('Out of scope (do not build):', texts(spec.outOfScope).map(item => `- ${item}`)),\r\n section('Decisions:', records(spec.decisions)\r\n .filter(item => text(item.question) && text(item.answer))\r\n .map(item => `- ${text(item.question)} → ${text(item.answer)} (${item.by === 'agent' ? 'agent\\'s call' : 'owner'})`)),\r\n section('Code map:', records(spec.codeMap)\r\n .filter(item => text(item.file))\r\n .map(item => `- ${text(item.repo) ? `${text(item.repo)} ` : ''}${text(item.file)}: ${text(item.role)}${CODE_USE_TEXT[item.use as string] ? ` [${CODE_USE_TEXT[item.use as string]}]` : ''}`)),\r\n section('Changes, in order:', records(spec.changes).filter(item => text(item.file)).flatMap(changeLines)),\r\n section('Acceptance criteria:', present(Array.isArray(spec.acceptance) ? spec.acceptance.map(criterionLine) : [])),\r\n section('Test plan:', [\r\n ...records(plan.automated).filter(item => text(item.command)).map(item => `- Run in ${text(item.repo) || 'the repository'}: ${text(item.command)}${text(item.expect) ? `. Passing: ${text(item.expect)}` : ''}`),\r\n ...records(plan.newTests).filter(item => text(item.seam)).map(item => `- New test at ${text(item.seam)}${texts(item.covers).length > 0 ? `, covers ${texts(item.covers).join(', ')}` : ''}`),\r\n ...records(plan.manual).filter(item => text(item.steps)).map(item => `- By hand${text(item.page) ? ` on ${text(item.page)}` : ''}: ${text(item.steps)}`),\r\n ]),\r\n risks.length > 0\r\n ? section(`Risks${riskLevel ? ` (risk level ${riskLevel})` : ''}:`, risks.map(item => `- ${text(item.risk)}${text(item.mitigation) ? `. Mitigation: ${text(item.mitigation)}` : ''}`))\r\n : riskLevel && `Risk level: ${riskLevel}`,\r\n section('Assumptions:', present(Array.isArray(spec.assumptions) ? spec.assumptions.map(assumptionLine) : [])),\r\n section('Open questions (not answered yet):', records(spec.openQuestions).filter(item => text(item.question)).map(item => `- ${text(item.question)}`)),\r\n ].filter(Boolean).join('\\n\\n')\r\n}\r\n","import type { RunnerJob, RunnerSpecWork } from './protocol'\r\nimport type { Workspace } from './workspace'\r\nimport { handoffTargetLine } from './handoff'\r\nimport { specPromptText } from './specText'\r\n\r\nexport interface TaskPrompt {\r\n // первое сообщение: сама задача\r\n prompt: string\r\n // дописывается к стандартным инструкциям Claude Code: как работать в этом запуске\r\n instructions: string\r\n}\r\n\r\nexport const VERDICT_TOOL_NAME = 'submit_verdict'\r\nexport const SCREENSHOT_TOOL_NAME = 'attach_screenshot'\r\nexport const REPLAY_TOOL_NAME = 'attach_replay'\r\nexport const VIDEO_TOOL_NAME = 'attach_video'\r\nexport const HANDOFF_TOOL_NAME = 'hand_off'\r\nexport const SUBMIT_QUESTIONS_TOOL_NAME = 'submit_questions'\r\nexport const SUBMIT_SPEC_TOOL_NAME = 'submit_spec'\r\n\r\nconst UNATTENDED_RULE = 'You are working on a TAMsense task on the user\\'s own computer. People on the task may send you messages during this run, each starting with \"Message from <name>:\". Follow them: they take priority over the original task where the two disagree. Nobody answers questions or approves actions during the run, so never stop to wait for a reply: decide yourself, keep working, and explain your decisions in your report.'\r\n\r\nconst MACHINE_RULE = 'This is the person\\'s own computer, and their other programs may be running on it. Stop only the processes you started yourself, by their process id, and stop them before you finish. Never kill processes by name or pattern (taskkill /IM, pkill, killall, Stop-Process -Name) and never touch programs you did not start.'\r\n\r\nconst CODE_RULES = [\r\n 'Make the changes the task asks for, and keep them focused on it.',\r\n 'When there is an approved spec, follow it: make the changes in its order with the names and data shapes it gives, build nothing from its out of scope list, and add the tests and run the commands of its test plan.',\r\n 'Check your work with the project\\'s own tools: run the tests, type checks and linters that exist, and fix what you broke.',\r\n 'Commit your work in every repository you changed, on the current branch. Do not push, do not switch or create branches, and do not rewrite history: TAMsense pushes the branch and opens the pull request.',\r\n]\r\n\r\nconst REWORK_RULE = 'This is a rework. The branch already has the earlier work and maybe the reviewer\\'s own commits: treat the current branch as the source of truth, keep the reviewer\\'s changes unless a comment asks otherwise, and address every comment.'\r\n\r\nconst CODE_REPORT_RULE = 'End with a short report for the reviewer: what you changed, how you checked it, and anything left open.'\r\n\r\nconst CHECK_RULES = [\r\n 'The work to check is on the current branch. See it with git diff against the base branch named for each repository below, and read the code around it.',\r\n 'Do not change, commit or push product code. You may run anything and create throwaway files outside the repositories.',\r\n 'Judge against the task, the spec and its acceptance criteria, checking each criterion by its id (AC-1 and so on) when the spec has ids. Be specific: every problem gets a file, a line and what to do about it.',\r\n `Finish by calling the ${VERDICT_TOOL_NAME} tool exactly once: passed is true only if nothing must be fixed; report says what you checked and, if it failed, exactly what to fix.`,\r\n]\r\n\r\nconst SCREENSHOT_RULE = `If you render screens (screenshots of the interface), attach the ones that show the result with the ${SCREENSHOT_TOOL_NAME} tool. People see them on the task without opening the code.`\r\n\r\nconst NAMING_RULE = 'Name everything you attach (screenshots, replays, videos) by what it shows and why it was made: a short title such as \"Design option 1\", \"Feature testing\" or \"Feature testing, attempt 2\", and a one-line purpose such as \"Comparing two layouts for the empty orders state\". Number design variants, name test runs by their goal and attempt, and never use generic names like \"screenshot1\".'\r\n\r\nconst RECORDING_RULE = `Every browser you open with Playwright from Node is recorded for the task as a replay people can watch, and requests from the tested site to the TAMsense tracker are blocked, so your runs never reach analytics. In every browser script, load the recorder from the TAMSENSE_RECORDER environment variable (require(process.env.TAMSENSE_RECORDER), or createRequire(import.meta.url)(process.env.TAMSENSE_RECORDER) in an .mjs script), open each context with await recorder.newRecordedContext(browser, { title, purpose, context: { viewport } }), call await recorder.mark(page, 'what just happened') at every meaningful step, and end with await recorder.closeRecorded(context). Add video: true when the page has a canvas, a map, a video player or iframes from another site (a replay cannot show them), or when someone asks for a video. Recordings are attached to the task when the run ends; ${REPLAY_TOOL_NAME} and ${VIDEO_TOOL_NAME} attach one earlier with a better title.`\r\n\r\nconst BROWSER_RULE = 'A headless browser is installed for interface checks. If the project has no Playwright of its own, load playwright-core from the folder in the TAMSENSE_PLAYWRIGHT environment variable, with PLAYWRIGHT_BROWSERS_PATH set to the folder in TAMSENSE_PLAYWRIGHT_BROWSERS. Never install browsers or packages yourself.'\r\n\r\n// browser: на компьютере стоит браузер TAMsense для проверок интерфейса; recorder: браузер агента записывается\r\nexport interface TaskPromptOptions {\r\n browser?: boolean\r\n recorder?: boolean\r\n}\r\n\r\nconst STAGE_GOALS = {\r\n code: 'Implement',\r\n review: 'Review the code for',\r\n test: 'Test',\r\n} as const\r\n\r\nconst SPEC_UNATTENDED_RULE = 'You are preparing a TAMsense task on the user\\'s own computer. Nobody answers you during this run: the owner answers your questions and reviews the spec on the task page after it. So never stop to wait for a reply and never ask in your messages: hand in what this stage asks for with the TAMsense tool, then finish. People on the task may send you messages during this run, each starting with \"Message from <name>:\". Follow them.'\r\n\r\nconst READ_ONLY_RULE = 'Read the code to research the task. Do not change, commit or push anything: nothing from this run is pushed, and the code step starts from a clean branch.'\r\n\r\nconst RESEARCH_RULE = 'Start by loading the tamsense:task-interview skill with the Skill tool and follow it: it holds the question test, the corner sweep and the rules for rounds. Research before asking: read the code in the repositories here and the evidence of the linked action, if there is one. Finding facts is your job; the decisions about intent are the owner\\'s.'\r\n\r\nconst BRIEF_RULE = 'Write the brief as you go: findings (what the code does today that matters, each with owner/repo path:line), dataFacts (numbers and facts from the linked action, each with its source; empty when none), approach (2 to 4 sentences), codeMap (the files the change will touch or must reuse, each with its role), assumptions (each corner you settled with a default, with the reason) and pendingDecisions (decisions that depend on an answer to this round; empty when none).'\r\n\r\nconst QUESTION_SHAPE_RULE = 'Each question has a snake_case id, one plain sentence the owner can answer without reading code, a whyItMatters line, and 2 to 4 concrete options grounded in what you saw, each saying what happens if it is chosen. Put the option you recommend first and set recommended on it (in a multiSelect question, on every option you recommend). multiSelect is true only when the options are additive. Never add an \"Other\" option: the page adds a free-text field. An unanswered question takes your recommended option, so recommend the one you would defend.'\r\n\r\nconst QUESTIONS_REPLY_RULE = 'Then reply in one or two plain sentences: what you understood from the code and why you ask these questions, or that no questions are needed. Do not repeat the questions: the page shows them.'\r\n\r\nconst SPEC_RULES = [\r\n 'Start by loading the tamsense:spec-writing skill with the Skill tool and follow it: it holds the shape of the spec, the rules for acceptance criteria, the size budget, the lint list and the builder check.',\r\n 'Start from the brief and the conversation below. Verify in the code what the spec depends on and research only what is missing; without a brief, research first as the tamsense task-interview skill describes.',\r\n 'Apply the answers: an answer the owner gave is a decision with by: owner; an answer marked \"Recommended option applied, not answered\" is an assumption with the reason \"not answered, the recommended option applies\". If an answer contradicts the code or another answer, check the code before following it and say so in your reply.',\r\n 'Where the task, the answers and the project context are silent and a safe default exists, choose it and record it as an assumption with its reason. A point with no safe default that changes the scope or the behaviour becomes an open question; keep them to 3 at most.',\r\n 'Before you hand the spec in, run the builder check of the skill once. It is required, also for a small task: start one fresh general-purpose subagent with the Agent tool, give it only the spec as text inside the builder check prompt of the skill, and settle each point it reports as the skill says.',\r\n `Hand in the FULL spec with ${SUBMIT_SPEC_TOOL_NAME}. TAMsense lints it and checks the files, and sends it back once if no fresh reader has checked it; if it returns feedback, the spec was not saved: fix it and call ${SUBMIT_SPEC_TOOL_NAME} again. After two rounds of lint feedback the spec is saved as it is.`,\r\n 'When the task already has a saved spec, the conversation holds the owner\\'s feedback or answers to its open questions: apply them and hand in the full updated spec. An answered open question becomes a decision; a corrected or confirmed assumption becomes a decision and leaves the assumptions.',\r\n 'Then reply with a short plain summary: the approach, the risk level and the open questions, if any. Do not paste the spec: the page shows it.',\r\n]\r\n\r\nconst SPEC_WORK_MISSING = 'TAMsense sent a spec step without its round and brief.'\r\n\r\nconst SPEC_STYLE_RULE = 'Write the brief, the questions, the spec and your reply in the language the task is written in. Replies are plain text, no Markdown. Use real file paths, function and table names; if you could not verify something, say so. No em dashes and no en dashes; use commas, colons or a plain hyphen.'\r\n\r\nfunction specText(spec: unknown): string | null {\r\n const text = specPromptText(spec)\r\n return text ? `Approved spec:\\n${text}` : null\r\n}\r\n\r\n// Контекст проекта пишет команда в настройках доски: Claude следует ему, как правилам проекта\r\nfunction contextText(context: string): string | null {\r\n return context.trim() ? `Project context from the team (follow it like the project's own rules):\\n${context.trim()}` : null\r\n}\r\n\r\nfunction bulletList(lines: string[]): string {\r\n return lines.map(line => `- ${line}`).join('\\n')\r\n}\r\n\r\nfunction repositoryLines(job: RunnerJob, workspace: Workspace): string[] {\r\n const sourceOf = new Map(job.repositories.map(repository => [repository.fullName, repository]))\r\n return workspace.repositories.map((repository) => {\r\n const source = sourceOf.get(repository.fullName)\r\n const pull = source?.pullRequestUrl ? `, pull request ${source.pullRequestUrl}` : ''\r\n return `${repository.folder} (${repository.fullName}), base branch origin/${source?.defaultBranch ?? 'main'}${pull}`\r\n })\r\n}\r\n\r\nfunction stepLine(stage: RunnerJob['stage']): string {\r\n return `This step of the board is \"${stage.name}\"${stage.helper ? `, and you work as the ${stage.helper}` : ''}.`\r\n}\r\n\r\n// Сообщения людей, которые ждали этого запуска, идут вместе с задачей\r\nfunction teamMessagesText(messages: RunnerJob['messages']): string | null {\r\n return messages.length > 0 ? `Messages from the team before this run:\\n${messages.map(message => `${message.authorName}: ${message.text}`).join('\\n')}` : null\r\n}\r\n\r\n// Куда агент может передать задачу по правилам доски; на шаге проверки вердикт всё равно нужен\r\nfunction handoffText(targets: RunnerJob['allowedHandoffs'], isCheck: boolean): string | null {\r\n if (targets.length === 0) {\r\n return null\r\n }\r\n return [\r\n `When you finish, hand the task on with the ${HANDOFF_TOOL_NAME} tool. Write a clear note for the next agent and the task owner: what was done, what to check and what is still open. The steps this task can go to from here:`,\r\n bulletList(targets.map(handoffTargetLine)),\r\n `If none of them fits, just finish without handing off.${isCheck ? ` Hand in your verdict with ${VERDICT_TOOL_NAME} as well.` : ''}`,\r\n ].join('\\n')\r\n}\r\n\r\nfunction threadText(thread: RunnerSpecWork['thread']): string | null {\r\n if (thread.length === 0) {\r\n return null\r\n }\r\n const lines = thread.map(message => `${message.role === 'user' ? 'Owner' : 'You, earlier'}: ${message.text}`)\r\n return `The conversation on the task so far (answers to the questions and the owner's feedback):\\n${lines.join('\\n\\n')}`\r\n}\r\n\r\nfunction questionRules(work: RunnerSpecWork): string[] {\r\n const handIn = `Call ${SUBMIT_QUESTIONS_TOOL_NAME} once with the brief and the questions, or an empty list when none are needed. If it returns problems, fix them and call it again.`\r\n if (work.round === 1) {\r\n return [\r\n RESEARCH_RULE,\r\n BRIEF_RULE,\r\n `Ask 0 to ${work.maxQuestions} questions, ideally 2 or 3, the most important first, and only the ones that pass the three tests of the skill. Zero questions is a good result: the spec is then written right away. Ask only questions that do not depend on another question of this round; put a dependent decision into pendingDecisions.`,\r\n QUESTION_SHAPE_RULE,\r\n handIn,\r\n QUESTIONS_REPLY_RULE,\r\n ]\r\n }\r\n const taken = work.takenIds.length > 0 ? ` These ids are taken by round 1: ${work.takenIds.join(', ')}.` : ''\r\n return [\r\n `This is the follow-up round ${work.round} of 2, and there is no round after it. Follow the tamsense:task-interview skill (load it with the Skill tool if this conversation has not loaded it yet). Read the answers in the conversation below. Do not research again what your brief covers; check only what the answers changed.`,\r\n `Ask at most ${work.maxQuestions} more questions, and only when an answer is vague (\"not sure\", \"depends\", \"maybe\"), contradicts another answer or the code, or unlocked a decision from pendingDecisions that passes the tests of the tamsense task-interview skill. Otherwise hand in no questions: whatever is still open becomes an assumption.${taken}`,\r\n QUESTION_SHAPE_RULE,\r\n 'Update the brief with what the answers changed and leave pendingDecisions empty.',\r\n handIn,\r\n QUESTIONS_REPLY_RULE,\r\n ]\r\n}\r\n\r\nfunction specGoal(job: RunnerJob, work: RunnerSpecWork): string {\r\n if (work.round === null) {\r\n return `Write the spec for TAMsense task #${job.taskId}: ${job.title}`\r\n }\r\n return work.round > 1 ? `Follow-up questions for TAMsense task #${job.taskId}: ${job.title}` : `Prepare TAMsense task #${job.taskId} for its spec: ${job.title}`\r\n}\r\n\r\n// Шаг ТЗ: Claude изучает код и сдаёт вопросы с брифом или ТЗ инструментами TAMsense; метод лежит в навыках task-interview и spec-writing\r\nfunction buildSpecPrompt(job: RunnerJob, work: RunnerSpecWork, workspace: Workspace): TaskPrompt {\r\n const prompt = [\r\n specGoal(job, work),\r\n job.description,\r\n work.linkedAction,\r\n work.brief ? `Brief from the clarification phase (research already done, verify only what you rely on):\\n${JSON.stringify(work.brief)}` : null,\r\n work.round === null && job.spec ? `Current saved spec (hand in the full updated version):\\n${JSON.stringify(job.spec)}` : null,\r\n job.handoffNote && `Note from another step: ${job.handoffNote}`,\r\n threadText(work.thread),\r\n teamMessagesText(job.messages),\r\n ].filter(Boolean).join('\\n\\n')\r\n\r\n const instructions = [\r\n SPEC_UNATTENDED_RULE,\r\n MACHINE_RULE,\r\n stepLine(job.stage),\r\n job.stage.instructions,\r\n contextText(job.projectContext),\r\n `The repositories for this task are checked out in the current folder, one subfolder each:\\n${bulletList(repositoryLines(job, workspace))}`,\r\n READ_ONLY_RULE,\r\n `How to work:\\n${bulletList(work.round === null ? SPEC_RULES : questionRules(work))}`,\r\n SPEC_STYLE_RULE,\r\n ].filter(Boolean).join('\\n\\n')\r\n\r\n return { prompt, instructions }\r\n}\r\n\r\nexport function buildTaskPrompt(job: RunnerJob, workspace: Workspace, options: TaskPromptOptions = {}): TaskPrompt {\r\n const { kind } = job.stage\r\n if (kind === 'interview' || kind === 'spec') {\r\n if (!job.specWork) {\r\n throw new Error(SPEC_WORK_MISSING)\r\n }\r\n return buildSpecPrompt(job, job.specWork, workspace)\r\n }\r\n const isCheck = kind !== 'code'\r\n const prompt = [\r\n `${STAGE_GOALS[kind]} TAMsense task #${job.taskId}: ${job.title}`,\r\n job.description,\r\n specText(job.spec),\r\n job.handoffNote && `Note from the previous step: ${job.handoffNote}`,\r\n job.review && `Review comments to address:\\n${job.review}`,\r\n teamMessagesText(job.messages),\r\n ].filter(Boolean).join('\\n\\n')\r\n\r\n const evidence = [SCREENSHOT_RULE, ...(options.recorder ? [RECORDING_RULE] : []), NAMING_RULE]\r\n const rules = isCheck ? [...CHECK_RULES, ...evidence] : [...CODE_RULES, ...(job.review ? [REWORK_RULE] : []), ...evidence, CODE_REPORT_RULE]\r\n const instructions = [\r\n UNATTENDED_RULE,\r\n MACHINE_RULE,\r\n stepLine(job.stage),\r\n job.stage.instructions,\r\n contextText(job.projectContext),\r\n `The repositories for this task are checked out in the current folder, one subfolder each, on branch ${job.branch}:\\n${bulletList(repositoryLines(job, workspace))}`,\r\n `How to work:\\n${bulletList(rules)}`,\r\n handoffText(job.allowedHandoffs, isCheck),\r\n options.browser && BROWSER_RULE,\r\n ].filter(Boolean).join('\\n\\n')\r\n\r\n return { prompt, instructions }\r\n}\r\n","import type { RunnerQuestions, RunnerReviewBody, RunnerReviewReply, RunnerSpecResult } from './protocol'\r\nimport { tool } from '@anthropic-ai/claude-agent-sdk'\r\nimport { z } from 'zod'\r\nimport { SUBMIT_QUESTIONS_TOOL_NAME, SUBMIT_SPEC_TOOL_NAME } from './prompt'\r\n\r\n// Схемы повторяют questionsPayloadSchema и specSchema агентов облака (packages/agents) слово в слово:\r\n// программа ставится на компьютер отдельно и пакетов сайта не видит. Тест следит, чтобы они не разошлись\r\n\r\nconst MAX_FIRST_ROUND = 4\r\n\r\nexport const questionSchema = z.object({\r\n id: z.string().describe('Stable snake_case id, unique in the task, for example \"empty_state\"'),\r\n question: z.string().describe('One plain sentence the owner can answer without reading code'),\r\n whyItMatters: z.string().describe('One line: what changes in the result depending on the answer'),\r\n options: z.array(z.object({\r\n label: z.string().describe('Short, concrete answer (1-6 words), without \"(Recommended)\": use the recommended flag'),\r\n description: z.string().describe('One sentence: what happens if this option is chosen'),\r\n recommended: z.boolean().describe('true on the option you recommend (in a multiSelect question, on every option you recommend). Recommended options come first'),\r\n })).min(2).max(4).describe('2-4 mutually exclusive options grounded in the code and data. The UI always adds a free-text \"Other\" field, so never add \"Other\" yourself'),\r\n multiSelect: z.boolean().describe('true only when the options are additive and several can apply together'),\r\n})\r\n\r\nexport const briefSchema = z.object({\r\n findings: z.array(z.object({\r\n fact: z.string().describe('What the code does today that matters for this task'),\r\n where: z.string().describe('Where it is: owner/repo path:line'),\r\n })).describe('Findings from the code'),\r\n dataFacts: z.array(z.string()).describe('Numbers and facts from the events or the business data, each with its source. Empty when the task needs no data'),\r\n approach: z.string().describe('The approach you would take, in 2-4 sentences'),\r\n codeMap: z.array(z.object({\r\n repo: z.string().describe('Repository full name (owner/name)'),\r\n file: z.string().describe('Path relative to the repository root'),\r\n role: z.string().describe('Why it matters: what to change or reuse there'),\r\n })).describe('The files the change will touch or must reuse'),\r\n assumptions: z.array(z.object({\r\n text: z.string().describe('The default you chose'),\r\n reason: z.string().describe('Why it is safe or why it was chosen'),\r\n })).describe('Corners settled with a default instead of a question'),\r\n pendingDecisions: z.array(z.string()).describe('Decisions that depend on an answer to this round and cannot be asked yet. Empty when none'),\r\n})\r\n\r\nexport const questionsPayloadSchema = z.object({\r\n brief: briefSchema,\r\n questions: z.array(questionSchema).max(MAX_FIRST_ROUND).describe(`0-${MAX_FIRST_ROUND} questions that pass the question test, most important first. An empty list when research settled everything`),\r\n})\r\n\r\nexport const specSchema = z.object({\r\n goal: z.string().describe('The problem solved, for whom, and the outcome a user or the business will notice'),\r\n context: z.string().describe('Summary: the problem in 1-2 sentences, then the chosen approach in 1-2 sentences'),\r\n inScope: z.array(z.string()).describe('What this task delivers, one item each'),\r\n outOfScope: z.array(z.string()).describe('What this task does not do: nearby additions a coder might be tempted to make and approaches that are ruled out. Never empty'),\r\n decisions: z.array(z.object({\r\n question: z.string(),\r\n answer: z.string(),\r\n by: z.enum(['owner', 'agent']).describe('owner: answered by a person; agent: your own call'),\r\n })).describe('Every answered question with by: owner, and your own calls that matter with by: agent'),\r\n codeMap: z.array(z.object({\r\n repo: z.string().describe('Repository full name (owner/name)'),\r\n file: z.string().describe('Path relative to the repository root'),\r\n role: z.string().describe('What the coder needs to know about this file'),\r\n use: z.enum(['reuse', 'change', 'keep']).describe('reuse: build on it; change: it is in changes; keep: do not touch'),\r\n })).describe('The files a coder must read before starting'),\r\n changes: z.array(z.object({\r\n repo: z.string().describe('Repository full name (owner/name) where the change happens'),\r\n file: z.string().describe('Path to the file, relative to the repository root'),\r\n action: z.enum(['create', 'modify', 'delete']),\r\n description: z.string().describe('The behaviour the file has after the change and which existing functions or patterns to extend. No code snippets or diffs'),\r\n produces: z.array(z.string()).describe('Names with signatures or data shapes this change creates or changes for other changes. Empty when none'),\r\n consumes: z.array(z.string()).describe('Names with signatures or data shapes this change uses from earlier changes. Empty when none'),\r\n acceptance: z.array(z.string()).describe('Ids of the criteria this change makes true, like AC-1'),\r\n })).min(1).describe('File changes in dependency order: each uses only what earlier changes or the code already provide'),\r\n acceptance: z.array(z.object({\r\n id: z.string().describe('AC-1, AC-2 and so on, in order'),\r\n kind: z.enum(['main', 'edge', 'keep']).describe('main: the main path; edge: an error, empty or limit case; keep: existing behaviour that must keep working'),\r\n text: z.string().describe('\"Given <state>, when <action>, then <observable result>\", saying where to look. No file paths or function names'),\r\n })).min(1).describe('Acceptance criteria: main path first, then edge cases, then what must keep working'),\r\n testPlan: z.object({\r\n automated: z.array(z.object({\r\n repo: z.string(),\r\n command: z.string().describe('The exact command to run'),\r\n expect: z.string().describe('What passing looks like'),\r\n })).describe('At least one command'),\r\n newTests: z.array(z.object({\r\n seam: z.string().describe('Where the test goes: the highest seam that covers the behaviour (an endpoint, a page, a public function)'),\r\n covers: z.array(z.string()).describe('Ids of the criteria it covers'),\r\n })),\r\n manual: z.array(z.object({\r\n page: z.string().describe('The page or screen'),\r\n steps: z.string().describe('What a person does and sees'),\r\n })),\r\n }),\r\n risks: z.array(z.object({\r\n risk: z.string(),\r\n mitigation: z.string(),\r\n })).describe('What could go wrong: migrations, existing data, access, performance, compatibility'),\r\n riskLevel: z.enum(['low', 'medium', 'high']).describe('How closely a person should review the result'),\r\n assumptions: z.array(z.object({\r\n text: z.string().describe('The default you chose'),\r\n reason: z.string().describe('Why, so the owner can confirm or correct it quickly'),\r\n })).describe('Defaults chosen where the task was silent. Empty when nothing was assumed'),\r\n openQuestions: z.array(questionSchema).describe('Questions only the owner can settle; the spec cannot be approved until each is answered or dismissed. Empty when none'),\r\n split: z.array(z.string()).describe('When the task holds more than one shippable goal or more than about 8 files: the separate tasks you propose, one line each. Empty otherwise'),\r\n})\r\n\r\n// Проверка на сайте теми же правилами, что в облаке; ответ уходит Claude как результат инструмента\r\nexport type ReviewRequest = (body: RunnerReviewBody) => Promise<RunnerReviewReply>\r\n\r\nfunction reply(text: string, isError = false) {\r\n return { content: [{ type: 'text' as const, text }], ...(isError ? { isError: true } : {}) }\r\n}\r\n\r\nfunction unreachable(error: unknown, toolName: string) {\r\n const message = error instanceof Error ? error.message : String(error)\r\n return reply(`Could not reach TAMsense to check this: ${message}. Call ${toolName} again in a moment.`, true)\r\n}\r\n\r\n// Принятые вопросы заменяют прежние; отклонённые не сохраняются, Claude получает замечания и присылает заново\r\nexport function questionsTool(review: ReviewRequest, onSubmit: (questions: RunnerQuestions) => void) {\r\n return tool(\r\n SUBMIT_QUESTIONS_TOOL_NAME,\r\n 'Hand in the brief and the clarifying questions of this round. Call it once, after the research. Pass an empty questions list when research settled everything: the spec is then written next. If it returns problems, the questions were not saved: fix them and call it again.',\r\n questionsPayloadSchema.shape,\r\n async (args) => {\r\n let verdict: RunnerReviewReply\r\n try {\r\n verdict = await review({ questions: args })\r\n }\r\n catch (error) {\r\n return unreachable(error, SUBMIT_QUESTIONS_TOOL_NAME)\r\n }\r\n if (!verdict.accepted) {\r\n return reply(verdict.feedback, true)\r\n }\r\n onSubmit(args)\r\n return reply(args.questions.length > 0\r\n ? 'Questions saved. The owner answers them on the task page after this run. Finish now with a reply of one or two sentences.'\r\n : 'Saved with no questions: the spec is written next. Finish now with a reply of one or two sentences.')\r\n },\r\n )\r\n}\r\n\r\nconst BUILDER_CHECK_FIRST = 'The spec passed the lint but was not saved yet: the builder check of the tamsense:spec-writing skill has not run in this session. Start one fresh general-purpose subagent with the Agent tool, give it only the spec as text inside the builder check prompt of the skill, settle each point it reports as the skill says, then call submit_spec again with the full spec.'\r\n\r\n// Сколько раз ТЗ уже возвращали, считает сам инструмент: сайт после двух доработок принимает ТЗ как есть.\r\n// Как в облаке, ТЗ без проверки свежим читателем один раз возвращается: builderCheckRan говорит, звал ли Claude помощника\r\nexport function specTool(review: ReviewRequest, onSubmit: (result: RunnerSpecResult) => void, builderCheckRan: () => boolean) {\r\n let repairs = 0\r\n let reminded = false\r\n return tool(\r\n SUBMIT_SPEC_TOOL_NAME,\r\n 'Hand in the implementation spec. It replaces the previous version entirely, so always send the FULL spec. TAMsense lints it and checks the files first; if it returns feedback, the spec was not saved: fix it and call submit_spec again.',\r\n specSchema.shape,\r\n async (args) => {\r\n let verdict: RunnerReviewReply\r\n try {\r\n verdict = await review({ spec: args, repairs })\r\n }\r\n catch (error) {\r\n return unreachable(error, SUBMIT_SPEC_TOOL_NAME)\r\n }\r\n if (!verdict.accepted) {\r\n repairs += 1\r\n return reply(verdict.feedback, true)\r\n }\r\n if (!builderCheckRan() && !reminded) {\r\n reminded = true\r\n return reply(BUILDER_CHECK_FIRST, true)\r\n }\r\n onSubmit({ spec: args, problems: verdict.problems })\r\n return reply(verdict.problems.length > 0\r\n ? `Spec saved with these problems left: ${verdict.problems.join('; ')}. Mention them in your reply.`\r\n : 'Spec saved.')\r\n },\r\n )\r\n}\r\n","import type { SDKMessage } from '@anthropic-ai/claude-agent-sdk'\r\n\r\nexport const USAGE_LIMIT_REPORT = 'Claude hit its usage limit'\r\n\r\nconst DEFAULT_PAUSE_MS = 30 * 60_000\r\nconst DAY_MS = 24 * 60 * 60_000\r\n// время сброса в секундах Unix, если число меньше этого; иначе в миллисекундах\r\nconst SECONDS_BELOW = 1e11\r\n\r\n// Тексты Claude Code про исчерпанный лимит (USAGE_LIMIT_ERROR_PREFIXES в SDK) и старый вид «usage limit reached|<время>»\r\nconst LIMIT_TEXT = /You['’]ve (?:hit|reached) your|You['’]re out of (?:usage credits|extra usage)|usage limit/i\r\nconst EPOCH_RESET = /\\|(\\d{10,13})\\b/\r\n// «resets 3pm», «resets 5:30am (Europe/Berlin)», «resets Oct 25, 3pm», «resets at 15:00»\r\nconst CLOCK_RESET = /resets?\\s+(?:at\\s+)?(?:([a-z]{3})[a-z]*\\.?\\s+(\\d{1,2}),?\\s+(?:at\\s+)?)?(\\d{1,2})(?::(\\d{2}))?\\s*([ap]m)?/i\r\nconst MONTHS = ['jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec']\r\n\r\nfunction fromEpoch(value: number): Date {\r\n return new Date(value < SECONDS_BELOW ? value * 1000 : value)\r\n}\r\n\r\nfunction hourOf(hour: number, meridiem: string | undefined): number | null {\r\n if (!meridiem) {\r\n return hour < 24 ? hour : null\r\n }\r\n if (hour < 1 || hour > 12) {\r\n return null\r\n }\r\n return (hour % 12) + (meridiem.toLowerCase() === 'pm' ? 12 : 0)\r\n}\r\n\r\n// Время сброса из текста Claude Code, в часовом поясе этого компьютера; null, если его нет или оно уже прошло\r\nexport function resetTimeOf(text: string, now: Date): Date | null {\r\n const epoch = EPOCH_RESET.exec(text)\r\n if (epoch) {\r\n const reset = fromEpoch(Number(epoch[1]))\r\n return reset > now ? reset : null\r\n }\r\n const clock = CLOCK_RESET.exec(text)\r\n if (!clock) {\r\n return null\r\n }\r\n const [, monthName, day, hourText, minuteText, meridiem] = clock\r\n const hour = hourOf(Number(hourText), meridiem)\r\n const minute = Number(minuteText ?? 0)\r\n const month = monthName ? MONTHS.indexOf(monthName.toLowerCase()) : now.getMonth()\r\n if (hour === null || minute > 59 || month < 0) {\r\n return null\r\n }\r\n const reset = new Date(now.getFullYear(), month, day ? Number(day) : now.getDate(), hour, minute)\r\n if (reset > now) {\r\n return reset\r\n }\r\n if (!monthName) {\r\n return new Date(reset.getTime() + DAY_MS)\r\n }\r\n // «Jan 2» в конце декабря: сброс в следующем году\r\n const nextYear = new Date(now.getFullYear() + 1, month, Number(day), hour, minute)\r\n return nextYear.getTime() - now.getTime() < 31 * DAY_MS ? nextYear : null\r\n}\r\n\r\nfunction assistantText(message: Extract<SDKMessage, { type: 'assistant' }>): string {\r\n return message.message.content.flatMap(block => block.type === 'text' ? [block.text] : []).join('\\n')\r\n}\r\n\r\nexport interface UsageLimitWatch {\r\n observe: (message: SDKMessage) => void\r\n // когда можно продолжить (ISO), если запуск не закончен из-за лимита; null, если лимит ни при чём\r\n pausedUntil: (completed: boolean, now?: Date) => string | null\r\n}\r\n\r\n// Следит за сообщениями Claude Code: отказ по лимиту подписки, ошибка rate_limit или текст про исчерпанный лимит.\r\n// Удачный итог хода стирает прошлые признаки: значит, Claude продолжил работу\r\nexport function usageLimitWatch(): UsageLimitWatch {\r\n let limitedByText = false\r\n let limitedByEvent = false\r\n let eventReset: Date | null = null\r\n let texts: string[] = []\r\n\r\n function hitText(text: string): void {\r\n limitedByText = true\r\n texts.push(text)\r\n }\r\n\r\n return {\r\n observe(message) {\r\n if (message.type === 'rate_limit_event') {\r\n const info = message.rate_limit_info\r\n limitedByEvent = info.status === 'rejected' && !info.isUsingOverage\r\n eventReset = limitedByEvent && info.resetsAt ? fromEpoch(info.resetsAt) : null\r\n }\r\n else if (message.type === 'assistant' && message.error) {\r\n const text = assistantText(message)\r\n if (message.error === 'rate_limit' || LIMIT_TEXT.test(text)) {\r\n hitText(text)\r\n }\r\n }\r\n else if (message.type === 'result') {\r\n if (message.subtype === 'success' && !message.is_error) {\r\n limitedByText = false\r\n texts = []\r\n return\r\n }\r\n const text = message.subtype === 'success' ? message.result : message.errors.join('\\n')\r\n if (LIMIT_TEXT.test(text)) {\r\n hitText(text)\r\n }\r\n }\r\n },\r\n pausedUntil(completed, now = new Date()) {\r\n if (completed || !(limitedByText || limitedByEvent)) {\r\n return null\r\n }\r\n const fromText = texts.map(text => resetTimeOf(text, now)).find(reset => reset !== null)\r\n const reset = eventReset && eventReset > now ? eventReset : fromText ?? new Date(now.getTime() + DEFAULT_PAUSE_MS)\r\n return reset.toISOString()\r\n },\r\n }\r\n}\r\n","import { mkdir, readFile, rm, writeFile } from 'node:fs/promises'\r\nimport { homedir } from 'node:os'\r\nimport { dirname, join } from 'node:path'\r\n\r\nexport interface RunnerConfig {\r\n server: string\r\n token: string\r\n deviceId: number\r\n deviceName: string\r\n // человек отказался ставить браузер для проверок интерфейса: при запуске больше не спрашивать\r\n browserDeclined?: boolean\r\n}\r\n\r\nconst OWNER_ONLY_FILE = 0o600\r\nconst OWNER_ONLY_FOLDER = 0o700\r\n\r\nexport function configPath(home: string = homedir()): string {\r\n return join(home, '.tamsense', 'runner.json')\r\n}\r\n\r\nfunction isRecord(value: unknown): value is Record<string, unknown> {\r\n return typeof value === 'object' && value !== null && !Array.isArray(value)\r\n}\r\n\r\nfunction parseConfig(value: unknown): RunnerConfig | null {\r\n if (!isRecord(value)) {\r\n return null\r\n }\r\n const { server, token, deviceId, deviceName, browserDeclined } = value\r\n const isValid = typeof server === 'string' && typeof token === 'string' && typeof deviceId === 'number' && typeof deviceName === 'string'\r\n return isValid ? { server, token, deviceId, deviceName, ...(browserDeclined === true ? { browserDeclined } : {}) } : null\r\n}\r\n\r\nfunction isMissingFile(error: unknown): boolean {\r\n return error instanceof Error && 'code' in error && error.code === 'ENOENT'\r\n}\r\n\r\n// Испорченный файл считается отсутствующим: программа предложит подключиться заново\r\nexport async function readConfig(path: string = configPath()): Promise<RunnerConfig | null> {\r\n try {\r\n return parseConfig(JSON.parse(await readFile(path, 'utf8')))\r\n }\r\n catch (error) {\r\n if (isMissingFile(error) || error instanceof SyntaxError) {\r\n return null\r\n }\r\n throw error\r\n }\r\n}\r\n\r\n// Ключ компьютера читает только его владелец\r\nexport async function writeConfig(config: RunnerConfig, path: string = configPath()): Promise<void> {\r\n await mkdir(dirname(path), { recursive: true, mode: OWNER_ONLY_FOLDER })\r\n await writeFile(path, `${JSON.stringify(config, null, 4)}\\n`, { mode: OWNER_ONLY_FILE })\r\n}\r\n\r\nexport async function removeConfig(path: string = configPath()): Promise<void> {\r\n await rm(path, { force: true })\r\n}\r\n","export interface ConnectedDevice {\r\n id: number\r\n name: string\r\n platform: string\r\n team: boolean\r\n createdAt: string\r\n lastSeenAt: string | null\r\n}\r\n\r\nexport interface PairingStartResponse {\r\n deviceCode: string\r\n userCode: string\r\n verificationPath: string\r\n expiresAt: string\r\n intervalSeconds: number\r\n}\r\n\r\nexport type PairingTokenResponse\r\n = | { status: 'pending' }\r\n | { status: 'denied' }\r\n | { status: 'expired' }\r\n | { status: 'connected', token: string, device: ConnectedDevice }\r\n\r\nexport interface RunnerIdentityResponse {\r\n device: ConnectedDevice\r\n email: string | null\r\n projectName: string\r\n}\r\n\r\n// pullRequestUrl: открытый PR задачи в этом репозитории, если он уже есть\r\nexport interface RunnerRepository {\r\n fullName: string\r\n defaultBranch: string\r\n pullRequestUrl?: string\r\n}\r\n\r\n// Шаг доски, ради которого компьютер получил задание: вопросы или ТЗ, код, проверка кода или тесты, и помощник, который его делает\r\nexport interface RunnerStage {\r\n kind: 'interview' | 'spec' | 'code' | 'review' | 'test'\r\n name: string\r\n helper: string | null\r\n instructions: string\r\n}\r\n\r\nexport interface RunnerThreadMessage {\r\n role: 'user' | 'assistant'\r\n text: string\r\n}\r\n\r\n// Работа шага ТЗ. round: 1 или 2 у вопросов, null у ТЗ; maxQuestions и takenIds: сколько вопросов можно задать и чьи id заняты;\r\n// brief: бриф раунда вопросов; linkedAction: находка аналитики, из которой создана задача; thread: ответы и замечания владельца\r\nexport interface RunnerSpecWork {\r\n round: number | null\r\n maxQuestions: number\r\n takenIds: string[]\r\n brief: unknown\r\n linkedAction: string | null\r\n thread: RunnerThreadMessage[]\r\n}\r\n\r\nexport interface RunnerJob {\r\n runId: number\r\n taskId: number\r\n title: string\r\n description: string\r\n spec: unknown\r\n branch: string\r\n repositories: RunnerRepository[]\r\n review: string | null\r\n stage: RunnerStage\r\n // контекст проекта от команды из настроек доски, пустая строка, если его нет\r\n projectContext: string\r\n // модель Claude (opus, sonnet, haiku) и усилие для этого запуска; null: как настроено в Claude Code на компьютере\r\n model: string | null\r\n effort: RunnerEffort | null\r\n // сообщения людей, которые ждали следующего запуска этого шага\r\n messages: RunnerQueuedMessage[]\r\n // куда агент может передать задачу с этого шага по правилам доски\r\n allowedHandoffs: RunnerHandoffTarget[]\r\n // записка от предыдущего шага: что сделано и что проверить\r\n handoffNote: string | null\r\n // сеанс Claude Code прошлого запуска этого шага на этом компьютере: разговор продолжается\r\n resumeSessionId: string | null\r\n // вопросы или ТЗ на шаге ТЗ; null на шагах кода и проверки\r\n specWork: RunnerSpecWork | null\r\n}\r\n\r\n// outcome: pass (готово), problems (нашлись проблемы), ask (спросить другой шаг)\r\nexport type RunnerHandoffOutcome = 'pass' | 'problems' | 'ask'\r\n\r\nexport interface RunnerHandoffTarget {\r\n columnId: number\r\n name: string\r\n role: string | null\r\n outcome: RunnerHandoffOutcome\r\n label: string\r\n}\r\n\r\n// Передача, которую выбрал агент: куда, с каким итогом и с какой запиской для следующего шага и владельца\r\nexport interface RunnerHandoff {\r\n toColumnId: number | null\r\n outcome: RunnerHandoffOutcome\r\n note: string\r\n}\r\n\r\nexport interface RunnerQueuedMessage {\r\n id: number\r\n text: string\r\n authorName: string\r\n}\r\n\r\n// Сообщение человека во время работы: steer сразу в идущий ход, queue когда ход закончится\r\nexport interface RunnerInboxMessage extends RunnerQueuedMessage {\r\n mode: 'steer' | 'queue'\r\n}\r\n\r\n// ack: сообщения, которые программа уже передала Claude\r\nexport interface RunnerInboxBody {\r\n ack: number[]\r\n}\r\n\r\n// keepGoing: false, если запуск закрыт на сайте (остановили или сочли пропавшим);\r\n// model и effort: смена модели, которую человек выбрал по ходу работы\r\nexport interface RunnerInbox {\r\n keepGoing: boolean\r\n messages: RunnerInboxMessage[]\r\n model: string | null\r\n effort: RunnerEffort | null\r\n}\r\n\r\nexport const RUNNER_EFFORTS = ['low', 'medium', 'high', 'xhigh', 'max'] as const\r\nexport type RunnerEffort = typeof RUNNER_EFFORTS[number]\r\n\r\n// Чей Claude: подписка человека или ключ API; null, если Claude Code не сказал\r\nexport type RunnerAccount = 'subscription' | 'api_key'\r\n\r\nexport interface RunnerModel {\r\n value: string\r\n displayName: string\r\n description: string\r\n}\r\n\r\n// Что умеет Claude на этом компьютере: программа сообщает при запуске и потом раз в полчаса\r\nexport interface RunnerCapabilities {\r\n account: RunnerAccount | null\r\n claudeVersion: string | null\r\n models: RunnerModel[]\r\n}\r\n\r\n// Какая модель на самом деле начала работу: приходит, как только Claude Code запустился\r\nexport interface RunnerSession {\r\n model: string | null\r\n account: RunnerAccount | null\r\n claudeVersion: string | null\r\n}\r\n\r\n// Запись журнала: что Claude думает, пишет и делает. ref связывает инструмент с его результатом,\r\n// parent: запись сделал помощник, которого Claude позвал этим инструментом\r\nexport type RunnerFeedKind = 'thinking' | 'text' | 'tool' | 'result' | 'skill' | 'agent'\r\n\r\nexport interface RunnerFeedEntry {\r\n seq: number\r\n kind: RunnerFeedKind\r\n text: string\r\n tool: string | null\r\n ref: string | null\r\n parent: string | null\r\n failed: boolean\r\n}\r\n\r\nexport interface RunnerFeedBody {\r\n entries: RunnerFeedEntry[]\r\n}\r\n\r\nexport const ATTACHMENT_TYPES = ['image/png', 'image/jpeg', 'image/webp'] as const\r\nexport type RunnerAttachmentType = typeof ATTACHMENT_TYPES[number]\r\nexport const MAX_ATTACHMENT_BYTES = 5 * 1024 * 1024\r\nexport const MAX_ATTACHMENTS_PER_RUN = 20\r\n\r\n// Скриншот, который Claude прикладывает к задаче; data в base64. title: что на нём, purpose: зачем он сделан\r\nexport interface RunnerAttachmentBody {\r\n name: string\r\n title: string\r\n purpose: string\r\n contentType: RunnerAttachmentType\r\n data: string\r\n}\r\n\r\nexport const VIDEO_TYPES = ['video/webm', 'video/mp4'] as const\r\nexport type RunnerVideoType = typeof VIDEO_TYPES[number]\r\n// сайт принимает видео до 200 МБ, если у него не настроено другое\r\nexport const MAX_VIDEO_BYTES = 200 * 1024 * 1024\r\n\r\nexport interface RunnerRecordingMark {\r\n at: number\r\n label: string\r\n}\r\n\r\n// Запись браузера или видео уже лежит на сайте по своему upload: этим запросом она появляется в задаче\r\nexport interface RunnerRecordingBody {\r\n kind: 'replay' | 'video'\r\n upload: string\r\n name: string\r\n title: string\r\n purpose: string\r\n contentType: string\r\n durationMs: number | null\r\n marks: RunnerRecordingMark[]\r\n}\r\n\r\nexport interface RunnerAttachmentResponse {\r\n id: number\r\n}\r\n\r\nexport interface RunnerNextResponse {\r\n job: RunnerJob | null\r\n}\r\n\r\nexport interface RunnerGitTokenResponse {\r\n token: string\r\n expiresAt: string\r\n}\r\n\r\nexport interface RunnerHeartbeatResponse {\r\n keepGoing: boolean\r\n}\r\n\r\nexport interface RunnerPushedBranch {\r\n repository: string\r\n branch: string\r\n sha: string\r\n}\r\n\r\nexport interface RunnerUsage {\r\n inputTokens: number\r\n cachedInputTokens: number\r\n outputTokens: number\r\n totalTokens: number\r\n}\r\n\r\n// Вердикт сдаёт Claude на шагах проверки: прошло или нет и почему\r\nexport interface RunnerVerdict {\r\n passed: boolean\r\n report: string\r\n}\r\n\r\n// Вопросы с брифом, которые Claude сдал инструментом submit_questions; форму проверяет сайт\r\nexport interface RunnerQuestions {\r\n brief: unknown\r\n questions: unknown[]\r\n}\r\n\r\n// ТЗ, которое Claude сдал инструментом submit_spec, и замечания проверки, которые в нём остались\r\nexport interface RunnerSpecResult {\r\n spec: unknown\r\n problems: string[]\r\n}\r\n\r\n// Проверка на сайте теми же правилами, что в облаке: вопросы раунда или ТЗ; repairs: сколько раз ТЗ уже возвращали\r\nexport type RunnerReviewBody = { questions: RunnerQuestions } | { spec: unknown, repairs: number }\r\n\r\n// accepted: можно сдавать; иначе feedback уходит Claude, и он присылает исправленное\r\nexport interface RunnerReviewReply {\r\n accepted: boolean\r\n feedback: string\r\n problems: string[]\r\n}\r\n\r\nexport interface RunnerFinishBody {\r\n outcome: 'succeeded' | 'failed'\r\n report: string\r\n pushed: RunnerPushedBranch[]\r\n model: string | null\r\n usage: RunnerUsage | null\r\n verdict: RunnerVerdict | null\r\n // передача, которую выбрал агент инструментом hand_off; null: по правилам доски\r\n handoff: RunnerHandoff | null\r\n // сеанс Claude Code, чтобы следующий запуск шага продолжил разговор\r\n sessionId: string | null\r\n // Claude упёрся в лимит подписки: когда можно повторить (ISO); null, если не упёрся\r\n pausedUntil: string | null\r\n // итог шага ТЗ: вопросы раунда или ТЗ; null, если Claude их не сдал\r\n questions: RunnerQuestions | null\r\n spec: RunnerSpecResult | null\r\n}\r\n\r\nexport const PAIRINGS_PATH = '/api/runner/pairings'\r\nexport const PAIRING_TOKEN_PATH = '/api/runner/pairings/token'\r\nexport const IDENTITY_PATH = '/api/runner/me'\r\nexport const NEXT_RUN_PATH = '/api/runner/runs/next'\r\n\r\nexport const CAPABILITIES_PATH = '/api/runner/me/capabilities'\r\n\r\nexport function runPath(runId: number, action: 'git-token' | 'heartbeat' | 'finish' | 'feed' | 'attachments' | 'recordings' | 'session' | 'inbox' | 'review'): string {\r\n return `/api/runner/runs/${runId}/${action}`\r\n}\r\n\r\n// Куски записи и тело видео идут отдельными запросами под одним upload, потом запись появляется в задаче через recordings\r\nexport function recordingUploadPath(runId: number, upload: string, part: 'chunks' | 'video'): string {\r\n return `/api/runner/runs/${runId}/recordings/${upload}/${part}`\r\n}\r\n","import type { ApiClient } from './api'\r\nimport type { ConnectedDevice, PairingStartResponse, PairingTokenResponse } from './protocol'\r\nimport { LOGIN_COMMAND } from './commands'\r\nimport { PAIRING_TOKEN_PATH, PAIRINGS_PATH } from './protocol'\r\n\r\nexport interface LoginRequest {\r\n name: string\r\n platform: string\r\n}\r\n\r\nexport interface LoginTools {\r\n api: ApiClient\r\n openBrowser: (url: string) => void\r\n print: (line: string) => void\r\n sleep: (ms: number) => Promise<void>\r\n now: () => number\r\n}\r\n\r\nexport interface LoginResult {\r\n token: string\r\n device: ConnectedDevice\r\n}\r\n\r\nconst MS_PER_SECOND = 1000\r\nconst EXPIRED_TEXT = `The code expired before it was confirmed. Run ${LOGIN_COMMAND} again.`\r\nconst DENIED_TEXT = 'The connection was cancelled in the browser. This computer was not connected.'\r\n\r\n// Как вход в gh: программа показывает код и ссылку, человек подтверждает их на сайте, программа ждёт ключ\r\nexport async function login(tools: LoginTools, request: LoginRequest): Promise<LoginResult> {\r\n const pairing = await tools.api.request<PairingStartResponse>('POST', PAIRINGS_PATH, request)\r\n const pageUrl = new URL(pairing.verificationPath, tools.api.server).toString()\r\n const expiresAt = Date.parse(pairing.expiresAt)\r\n\r\n tools.print(`Your code: ${pairing.userCode}`)\r\n tools.print(`Confirm it in the browser: ${pageUrl}`)\r\n tools.print('Waiting for confirmation…')\r\n tools.openBrowser(pageUrl)\r\n\r\n while (tools.now() < expiresAt) {\r\n await tools.sleep(pairing.intervalSeconds * MS_PER_SECOND)\r\n const reply = await tools.api.request<PairingTokenResponse>('POST', PAIRING_TOKEN_PATH, { deviceCode: pairing.deviceCode })\r\n if (reply.status === 'connected') {\r\n return { token: reply.token, device: reply.device }\r\n }\r\n if (reply.status === 'denied') {\r\n throw new Error(DENIED_TEXT)\r\n }\r\n if (reply.status === 'expired') {\r\n break\r\n }\r\n }\r\n throw new Error(EXPIRED_TEXT)\r\n}\r\n","import type { RunnerJob } from './protocol'\r\nimport { existsSync } from 'node:fs'\r\nimport { join } from 'node:path'\r\nimport { fileURLToPath } from 'node:url'\r\nimport { DEFAULT_PURPOSE_ENV, DEFAULT_TITLE_ENV, MAX_PURPOSE_LENGTH, MAX_TITLE_LENGTH, RECORDINGS_DIR_ENV, SERVER_ENV } from './recorder'\r\n\r\nexport const RECORDER_ENV = 'TAMSENSE_RECORDER'\r\n\r\n// Модуль записи собирается рядом с программой; в исходниках его нет, и тогда браузер агента не записывается\r\nexport function bundledRecorder(): string | null {\r\n const path = fileURLToPath(new URL('./recorder.cjs', import.meta.url))\r\n return existsSync(path) ? path : null\r\n}\r\n\r\n// Своя папка у каждого запуска: что не ушло на сайт, удаляется вместе с ней\r\nexport function recordingsFolder(home: string, runId: number): string {\r\n return join(home, '.tamsense', 'recordings', `run-${runId}`)\r\n}\r\n\r\nexport interface RecorderEnvInput {\r\n recorder: string\r\n folder: string\r\n server: string\r\n job: Pick<RunnerJob, 'taskId' | 'stage'>\r\n nodeOptions: string | undefined\r\n}\r\n\r\n// Внутри NODE_OPTIONS обратная косая черта в кавычках считается экранированием: путь идёт с прямыми\r\nexport function nodeOptionsWithRecorder(recorder: string, nodeOptions: string | undefined): string {\r\n return [nodeOptions?.trim(), `--require \"${recorder.replaceAll('\\\\', '/')}\"`].filter(Boolean).join(' ')\r\n}\r\n\r\n// Каждый процесс Node агента подгружает запись, и любой браузер Playwright записывается сам, с названием шага по умолчанию\r\nexport function recorderEnv(input: RecorderEnvInput): Record<string, string> {\r\n const { job } = input\r\n return {\r\n [RECORDER_ENV]: input.recorder,\r\n [RECORDINGS_DIR_ENV]: input.folder,\r\n [SERVER_ENV]: input.server,\r\n [DEFAULT_TITLE_ENV]: `${job.stage.name} browser session`.slice(0, MAX_TITLE_LENGTH),\r\n [DEFAULT_PURPOSE_ENV]: `Recorded automatically in the ${job.stage.name} step of task #${job.taskId}`.slice(0, MAX_PURPOSE_LENGTH),\r\n NODE_OPTIONS: nodeOptionsWithRecorder(input.recorder, input.nodeOptions),\r\n }\r\n}\r\n","import { appendFileSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'\r\nimport Module, { createRequire } from 'node:module'\r\nimport { basename, dirname, join } from 'node:path'\r\nimport process from 'node:process'\r\n\r\n// Запись браузера агента: rrweb пишет каждую страницу в свой файл NDJSON в папке запуска, по желанию ещё и видео.\r\n// Модуль самодостаточный: программа подключает его ко всем процессам Node агента через NODE_OPTIONS --require,\r\n// и тогда любой браузер Playwright записывается сам; скрипты агента импортируют его же, чтобы дать записи название и отметки\r\n\r\nexport const RECORDINGS_DIR_ENV = 'TAMSENSE_RECORDINGS_DIR'\r\nexport const SERVER_ENV = 'TAMSENSE_SERVER'\r\nexport const DEFAULT_TITLE_ENV = 'TAMSENSE_RECORDING_TITLE'\r\nexport const DEFAULT_PURPOSE_ENV = 'TAMSENSE_RECORDING_PURPOSE'\r\nexport const MARK_TAG = 'tamsense.mark'\r\nexport const REPLAY_SUFFIX = '.rrweb.ndjson'\r\nexport const META_SUFFIX = '.json'\r\nexport const MAX_TITLE_LENGTH = 60\r\nexport const MAX_PURPOSE_LENGTH = 160\r\n// трекер TAMsense по умолчанию шлёт сюда; адрес сервера из настроек программы добавляется к нему\r\nexport const DEFAULT_TRACKER_ORIGINS = ['https://my.tamsense.com']\r\n\r\nconst BINDING = '__tamsenseRecord'\r\nconst STATE_KEY = Symbol.for('tamsense.recorder')\r\nconst FLUSH_MS = 100\r\nconst MAX_SESSION_BYTES = 300 * 1024 * 1024\r\nconst GAP_CHECK_MS = 1500\r\nconst MAX_LABEL_LENGTH = 120\r\nconst RRWEB_META = 4\r\n\r\nexport interface RecordingCaption {\r\n title: string\r\n purpose: string\r\n}\r\n\r\nexport interface RecordedContextOptions extends RecordingCaption {\r\n // видео в придачу к записи: для canvas, карт, видео на странице, чужих iframe или когда его попросили\r\n video?: boolean\r\n // обычные настройки browser.newContext: viewport, storageState и другие\r\n context?: Record<string, unknown>\r\n}\r\n\r\nexport interface SessionMark {\r\n ts: number\r\n label: string\r\n}\r\n\r\n// Что знает программа о записи одной страницы; лежит рядом с файлом записи\r\nexport interface SessionMeta {\r\n version: 1\r\n id: string\r\n title: string\r\n purpose: string\r\n startedAt: number\r\n endedAt: number | null\r\n url: string\r\n marks: SessionMark[]\r\n video: string | null\r\n gaps: string[]\r\n blockedTrackerRequests: number\r\n truncated: boolean\r\n // записано само, без названия от агента\r\n automatic: boolean\r\n}\r\n\r\nexport interface RecordingSummary {\r\n title: string\r\n purpose: string\r\n replay: string\r\n video: string | null\r\n marks: number\r\n gaps: string[]\r\n blockedTrackerRequests: number\r\n}\r\n\r\ninterface PlaywrightVideo {\r\n path: () => Promise<string>\r\n}\r\n\r\ninterface PlaywrightPage {\r\n url: () => string\r\n isClosed: () => boolean\r\n on: (event: 'close', listener: () => void) => unknown\r\n evaluate: (fn: string | ((arg: never) => unknown), arg?: unknown) => Promise<unknown>\r\n video: () => PlaywrightVideo | null\r\n}\r\n\r\ninterface PlaywrightRoute {\r\n fulfill: (response: { status: number, contentType: string, body: string }) => Promise<void>\r\n request: () => { frame: () => { page: () => PlaywrightPage } }\r\n}\r\n\r\nexport interface PlaywrightContext {\r\n pages: () => PlaywrightPage[]\r\n on: (event: 'page' | 'close', listener: (page: PlaywrightPage) => void) => unknown\r\n exposeBinding: (name: string, callback: (source: { page: PlaywrightPage }, payload: string) => unknown) => Promise<void>\r\n addInitScript: (script: { content: string }) => Promise<void>\r\n route: (url: (url: URL) => boolean, handler: (route: PlaywrightRoute) => Promise<void>) => Promise<void>\r\n close: (options?: unknown) => Promise<void>\r\n}\r\n\r\nexport interface PlaywrightBrowser {\r\n newContext: (options?: Record<string, unknown>) => Promise<PlaywrightContext>\r\n}\r\n\r\ninterface Instrumentation {\r\n addListener: (listener: { runAfterCreateBrowserContext: (context: PlaywrightContext) => Promise<void> }) => void\r\n}\r\n\r\ninterface ContextRecording {\r\n caption: RecordingCaption\r\n automatic: boolean\r\n sessions: Session[]\r\n}\r\n\r\ninterface RecorderState {\r\n contexts: WeakMap<PlaywrightContext, ContextRecording>\r\n pages: WeakMap<PlaywrightPage, Session>\r\n instrumented: WeakSet<object>\r\n script: string | null\r\n hooked: boolean\r\n}\r\n\r\nfunction state(): RecorderState {\r\n const holder = globalThis as { [STATE_KEY]?: RecorderState }\r\n holder[STATE_KEY] ??= { contexts: new WeakMap(), pages: new WeakMap(), instrumented: new WeakSet(), script: null, hooked: false }\r\n return holder[STATE_KEY]\r\n}\r\n\r\nfunction recordingsDir(): string {\r\n const dir = process.env[RECORDINGS_DIR_ENV]\r\n if (!dir) {\r\n throw new Error(`${RECORDINGS_DIR_ENV} is not set: recordings work only inside a TAMsense run`)\r\n }\r\n return dir\r\n}\r\n\r\nfunction warn(text: string): void {\r\n process.stderr.write(`[tamsense recorder] ${text}\\n`)\r\n}\r\n\r\n// Правило блокировки трекера TAMsense: запрос на адрес, который кончается на /api/events, с ключом проекта в ?key=\r\n// или к серверу TAMsense. Так записи агента не попадают в аналитику и недельный разбор\r\nexport function isTrackerIngest(url: URL, tamsenseOrigins: readonly string[]): boolean {\r\n if (!/\\/api\\/events\\/?$/.test(url.pathname)) {\r\n return false\r\n }\r\n return url.searchParams.has('key') || tamsenseOrigins.includes(url.origin)\r\n}\r\n\r\nexport function trackerOrigins(env: Record<string, string | undefined> = process.env): string[] {\r\n const origins = new Set(DEFAULT_TRACKER_ORIGINS)\r\n try {\r\n if (env[SERVER_ENV]) {\r\n origins.add(new URL(env[SERVER_ENV]).origin)\r\n }\r\n }\r\n catch {}\r\n return [...origins]\r\n}\r\n\r\nexport function checkCaption(caption: Partial<RecordingCaption> | undefined): RecordingCaption {\r\n const title = typeof caption?.title === 'string' ? caption.title.trim() : ''\r\n const purpose = typeof caption?.purpose === 'string' ? caption.purpose.trim() : ''\r\n if (!title || !purpose) {\r\n throw new Error('Give it a title (what it shows, for example \"Design option 1\" or \"Feature testing, attempt 2\") and a purpose (one line on why it was made).')\r\n }\r\n if (title.length > MAX_TITLE_LENGTH || purpose.length > MAX_PURPOSE_LENGTH) {\r\n throw new Error(`Keep the title within ${MAX_TITLE_LENGTH} characters and the purpose within ${MAX_PURPOSE_LENGTH}.`)\r\n }\r\n return { title, purpose }\r\n}\r\n\r\nfunction clip(text: string, max: number): string {\r\n return text.length > max ? `${text.slice(0, max - 1).trimEnd()}…` : text\r\n}\r\n\r\n// Название записи, которую никто не назвал: из настроек запуска и имени скрипта, который открыл браузер\r\nexport function automaticCaption(env: Record<string, string | undefined> = process.env, script: string | undefined = process.argv[1]): RecordingCaption {\r\n const from = script ? `, opened by ${basename(script)}` : ''\r\n return {\r\n title: clip(env[DEFAULT_TITLE_ENV]?.trim() || 'Browser session', MAX_TITLE_LENGTH),\r\n purpose: clip(`${env[DEFAULT_PURPOSE_ENV]?.trim() || 'Recorded automatically while the agent used a browser'}${from}`, MAX_PURPOSE_LENGTH),\r\n }\r\n}\r\n\r\n// Сборка rrweb без модулей: UMD кладёт её в локальный module.exports, страница своего rrweb не видит и не теряет\r\nfunction rrwebSource(): string {\r\n const entry = createRequire(import.meta.url).resolve('rrweb')\r\n return readFileSync(join(dirname(entry), 'rrweb.umd.min.cjs'), 'utf8')\r\n}\r\n\r\n// Скрипт для каждой страницы: пишет только верхний документ, iframe того же сайта rrweb снимает сам.\r\n// События уходят в программу пачками через exposeBinding, пароли скрыты, как в rrweb по умолчанию\r\nexport function pageScript(rrweb: string): string {\r\n return `(() => {\r\nif (window.top !== window || window.__tamsenseRecording) return;\r\nwindow.__tamsenseRecording = true;\r\nconst rrweb = (() => { const module = { exports: {} }; const exports = module.exports; ${rrweb}\r\n; return module.exports; })();\r\nlet buffer = [];\r\nlet timer = null;\r\nconst send = (extra) => {\r\n clearTimeout(timer);\r\n timer = null;\r\n if (!buffer.length && !extra) return Promise.resolve();\r\n const events = buffer;\r\n buffer = [];\r\n return window.${BINDING}(JSON.stringify({ events, ...extra })).catch(() => {});\r\n};\r\nrrweb.record({\r\n emit(event) {\r\n buffer.push(event);\r\n if (!timer) timer = setTimeout(send, ${FLUSH_MS});\r\n },\r\n maskInputOptions: { password: true },\r\n});\r\nconst crossOrigin = (frame) => { try { return new URL(frame.src, location.href).origin !== location.origin; } catch { return false; } };\r\nconst checkGaps = () => {\r\n const gaps = [];\r\n if (document.querySelector('canvas')) gaps.push('canvas');\r\n if (document.querySelector('video')) gaps.push('video');\r\n if ([...document.querySelectorAll('iframe')].some(crossOrigin)) gaps.push('cross-origin iframe');\r\n if (gaps.length) send({ gaps });\r\n};\r\nwindow.__tamsenseFlush = () => send();\r\nwindow.__tamsenseMark = (label) => { rrweb.record.addCustomEvent('${MARK_TAG}', { label }); return send(); };\r\naddEventListener('pagehide', () => { send(); });\r\naddEventListener('load', () => setTimeout(checkGaps, ${GAP_CHECK_MS}));\r\n})();`\r\n}\r\n\r\nfunction script(): string {\r\n const current = state()\r\n current.script ??= pageScript(rrwebSource())\r\n return current.script\r\n}\r\n\r\nclass Session {\r\n readonly file: string\r\n readonly metaFile: string\r\n readonly meta: SessionMeta\r\n private bytes = 0\r\n\r\n constructor(dir: string, recording: ContextRecording, url: string) {\r\n const id = `${Date.now().toString(36)}-${Math.random().toString(16).slice(2, 8)}`\r\n this.file = join(dir, `${id}${REPLAY_SUFFIX}`)\r\n this.metaFile = join(dir, `${id}${META_SUFFIX}`)\r\n this.meta = { version: 1, id, ...recording.caption, startedAt: Date.now(), endedAt: null, url, marks: [], video: null, gaps: [], blockedTrackerRequests: 0, truncated: false, automatic: recording.automatic }\r\n writeFileSync(this.file, '')\r\n this.save()\r\n }\r\n\r\n save(): void {\r\n writeFileSync(this.metaFile, `${JSON.stringify(this.meta, null, 2)}\\n`)\r\n }\r\n\r\n receive(payload: string): void {\r\n let batch: { events?: unknown[], gaps?: string[] }\r\n try {\r\n batch = JSON.parse(payload) as typeof batch\r\n }\r\n catch {\r\n return\r\n }\r\n if (batch.gaps?.length) {\r\n this.meta.gaps = [...new Set([...this.meta.gaps, ...batch.gaps])]\r\n this.save()\r\n }\r\n const page = (batch.events as Array<{ type?: unknown, data?: { href?: unknown } }> | undefined)?.find(event => event.type === RRWEB_META && typeof event.data?.href === 'string' && event.data.href !== 'about:blank')\r\n if (page && this.meta.url === 'about:blank') {\r\n this.meta.url = String(page.data!.href)\r\n this.save()\r\n }\r\n const lines = (batch.events ?? []).map(event => JSON.stringify(event)).join('\\n')\r\n if (!lines || this.meta.truncated) {\r\n return\r\n }\r\n if (this.bytes + lines.length > MAX_SESSION_BYTES) {\r\n this.meta.truncated = true\r\n this.save()\r\n return\r\n }\r\n this.bytes += lines.length + 1\r\n appendFileSync(this.file, `${lines}\\n`)\r\n }\r\n\r\n end(): void {\r\n if (this.meta.endedAt === null) {\r\n this.meta.endedAt = Date.now()\r\n this.save()\r\n }\r\n }\r\n\r\n summary(): RecordingSummary {\r\n const { title, purpose, video, marks, gaps, blockedTrackerRequests } = this.meta\r\n return { title, purpose, replay: this.file, video, marks: marks.length, gaps, blockedTrackerRequests }\r\n }\r\n}\r\n\r\nasync function startSession(recording: ContextRecording, page: PlaywrightPage, injectNow: boolean): Promise<void> {\r\n const current = state()\r\n if (current.pages.has(page)) {\r\n return\r\n }\r\n const session = new Session(recordingsDir(), recording, page.url())\r\n current.pages.set(page, session)\r\n recording.sessions.push(session)\r\n page.on('close', () => session.end())\r\n const video = page.video()\r\n if (video) {\r\n session.meta.video = await video.path().catch(() => null)\r\n session.save()\r\n }\r\n // страница уже была открыта до записи: скрипт для будущих страниц её не коснётся\r\n if (injectNow) {\r\n await page.evaluate(script()).catch(() => {})\r\n }\r\n}\r\n\r\nasync function flush(context: PlaywrightContext): Promise<void> {\r\n await Promise.all(context.pages().filter(page => !page.isClosed()).map(page =>\r\n page.evaluate(() => (window as unknown as { __tamsenseFlush?: () => Promise<void> }).__tamsenseFlush?.(), undefined).catch(() => {}),\r\n ))\r\n}\r\n\r\n// Записывает контекст Playwright; второй вызов для того же контекста только меняет название и назначение записи.\r\n// automatic: контекст записан сам, название придумала программа\r\nexport async function recordContext(context: PlaywrightContext, caption: RecordingCaption, automatic = false): Promise<void> {\r\n const checked = checkCaption(caption)\r\n const current = state()\r\n const known = current.contexts.get(context)\r\n if (known) {\r\n Object.assign(known, { caption: checked, automatic })\r\n for (const session of known.sessions) {\r\n Object.assign(session.meta, checked, { automatic })\r\n session.save()\r\n }\r\n return\r\n }\r\n mkdirSync(recordingsDir(), { recursive: true })\r\n const recording: ContextRecording = { caption: checked, automatic, sessions: [] }\r\n current.contexts.set(context, recording)\r\n const origins = trackerOrigins()\r\n await context.route(url => isTrackerIngest(url, origins), async (route) => {\r\n try {\r\n const session = current.pages.get(route.request().frame().page())\r\n if (session) {\r\n session.meta.blockedTrackerRequests += 1\r\n session.save()\r\n }\r\n }\r\n catch {}\r\n await route.fulfill({ status: 200, contentType: 'application/json', body: '{\"ok\":true}' })\r\n })\r\n await context.exposeBinding(BINDING, ({ page }, payload) => current.pages.get(page)?.receive(payload))\r\n await context.addInitScript({ content: script() })\r\n context.on('page', page => void startSession(recording, page, false))\r\n context.on('close', () => recording.sessions.forEach(session => session.end()))\r\n for (const page of context.pages()) {\r\n await startSession(recording, page, true)\r\n }\r\n const close = context.close.bind(context)\r\n context.close = async (options?: unknown) => {\r\n await flush(context)\r\n return close(options)\r\n }\r\n}\r\n\r\n// Новый записанный контекст: название и назначение обязательны, video: true добавляет видео Playwright\r\nexport async function newRecordedContext(browser: PlaywrightBrowser, options: RecordedContextOptions): Promise<PlaywrightContext> {\r\n const caption = checkCaption(options)\r\n const extra = options.video ? { recordVideo: { dir: join(recordingsDir(), 'video') } } : {}\r\n const context = await browser.newContext({ ...options.context, ...extra })\r\n await recordContext(context, caption)\r\n return context\r\n}\r\n\r\n// Отметка шага: видна на шкале плеера и в списке отметок, клик по ней перематывает запись к этому месту\r\nexport async function mark(page: PlaywrightPage, label: string): Promise<void> {\r\n const text = clip(String(label ?? '').trim(), MAX_LABEL_LENGTH)\r\n const session = state().pages.get(page)\r\n if (!text || !session) {\r\n warn(text ? 'mark() was called for a page that is not recorded' : 'mark() needs a label')\r\n return\r\n }\r\n session.meta.marks.push({ ts: Date.now(), label: text })\r\n session.save()\r\n await page.evaluate(value => (window as unknown as { __tamsenseMark?: (label: string) => Promise<void> }).__tamsenseMark?.(value), text).catch(() => {})\r\n}\r\n\r\n// Закрывает записанный контекст и говорит, что записано; подсказывает видео, если записи не хватит\r\nexport async function closeRecorded(context: PlaywrightContext): Promise<RecordingSummary[]> {\r\n await context.close()\r\n const sessions = state().contexts.get(context)?.sessions ?? []\r\n const summaries = sessions.map(session => session.summary())\r\n for (const summary of summaries) {\r\n if (summary.gaps.length > 0 && !summary.video) {\r\n warn(`\"${summary.title}\" has ${summary.gaps.join(', ')}: the replay may not show it. Record it again with video: true.`)\r\n }\r\n }\r\n return summaries\r\n}\r\n\r\nfunction instrument(exports: unknown): void {\r\n const chromium = (exports as { chromium?: { launch?: unknown, _instrumentation?: Instrumentation } } | null)?.chromium\r\n const instrumentation = (exports as { _instrumentation?: Instrumentation } | null)?._instrumentation ?? chromium?._instrumentation\r\n const current = state()\r\n if (typeof chromium?.launch !== 'function' || typeof instrumentation?.addListener !== 'function' || current.instrumented.has(instrumentation)) {\r\n return\r\n }\r\n current.instrumented.add(instrumentation)\r\n instrumentation.addListener({\r\n runAfterCreateBrowserContext: async (context) => {\r\n try {\r\n if (!current.contexts.has(context)) {\r\n await recordContext(context, automaticCaption(), true)\r\n }\r\n }\r\n catch (error) {\r\n warn(`could not record this browser: ${error instanceof Error ? error.message : String(error)}`)\r\n }\r\n },\r\n })\r\n}\r\n\r\n// Сам цепляется к Playwright, как только его загрузят: агент не может забыть включить запись\r\nexport function installAutoRecording(): void {\r\n const current = state()\r\n if (current.hooked) {\r\n return\r\n }\r\n current.hooked = true\r\n const loader = Module as unknown as { _load: (request: string, parent: unknown, isMain: boolean) => unknown }\r\n const load = loader._load\r\n loader._load = function (this: unknown, ...args: [string, unknown, boolean]) {\r\n const exports = load.apply(this, args)\r\n try {\r\n instrument(exports)\r\n }\r\n catch {}\r\n return exports\r\n }\r\n}\r\n\r\nif (process.env[RECORDINGS_DIR_ENV]) {\r\n installAutoRecording()\r\n}\r\n","import type { RunnerIdentityResponse } from './protocol'\r\n\r\nconst TEAM_KEY_MISSING_TEXT = 'This is a team computer: Claude must run on the Anthropic API key of your company, not on a personal Claude subscription. Set ANTHROPIC_API_KEY on this computer and start again.'\r\n\r\n// Через общий компьютер работает вся команда, а подписку Claude правила Anthropic разрешают только её владельцу\r\nexport function teamKeyProblem(isTeam: boolean, env: Record<string, string | undefined>): string | null {\r\n return isTeam && !env.ANTHROPIC_API_KEY?.trim() ? TEAM_KEY_MISSING_TEXT : null\r\n}\r\n\r\nexport function describeDevice(identity: RunnerIdentityResponse): string {\r\n return identity.device.team\r\n ? `a team computer of ${identity.projectName}`\r\n : `the personal computer of ${identity.email ?? 'its owner'}`\r\n}\r\n","import { spawn } from 'node:child_process'\r\nimport { hostname } from 'node:os'\r\nimport { delimiter } from 'node:path'\r\nimport { createInterface } from 'node:readline/promises'\r\nimport { setTimeout as wait } from 'node:timers/promises'\r\n\r\nconst PLATFORM_LABELS: Partial<Record<NodeJS.Platform, string>> = {\r\n win32: 'Windows',\r\n darwin: 'macOS',\r\n linux: 'Linux',\r\n}\r\n\r\nconst MAX_NAME_LENGTH = 80\r\n\r\nexport function platformLabel(platform: NodeJS.Platform = process.platform): string {\r\n return PLATFORM_LABELS[platform] ?? platform\r\n}\r\n\r\nexport function defaultDeviceName(): string {\r\n return hostname().slice(0, MAX_NAME_LENGTH) || 'My computer'\r\n}\r\n\r\n// На Windows ссылку открывает rundll32: через cmd знак & в адресе разрезал бы команду\r\nfunction browserCommand(url: string, platform: NodeJS.Platform): [string, string[]] {\r\n if (platform === 'win32') {\r\n return ['rundll32', ['url.dll,FileProtocolHandler', url]]\r\n }\r\n return [platform === 'darwin' ? 'open' : 'xdg-open', [url]]\r\n}\r\n\r\n// Браузер может не открыться (сервер без экрана): ссылка уже напечатана, человек откроет её сам\r\nexport function openBrowser(url: string, platform: NodeJS.Platform = process.platform): void {\r\n const [command, args] = browserCommand(url, platform)\r\n const child = spawn(command, args, { detached: true, stdio: 'ignore' })\r\n child.on('error', () => {})\r\n child.unref()\r\n}\r\n\r\n// Claude и проекты зовут node и npm: если программу запустили так, что её папки Node нет в PATH, папка добавляется в конец\r\nexport function withNodeOnPath(env: NodeJS.ProcessEnv, nodeDir: string, separator: string = delimiter): NodeJS.ProcessEnv {\r\n const key = Object.keys(env).find(name => name.toUpperCase() === 'PATH') ?? 'PATH'\r\n const entries = (env[key] ?? '').split(separator).filter(Boolean)\r\n const normalize = (dir: string) => dir.replace(/[\\\\/]+$/, '').toLowerCase()\r\n if (entries.some(entry => normalize(entry) === normalize(nodeDir))) {\r\n return env\r\n }\r\n return { ...env, [key]: [...entries, nodeDir].join(separator) }\r\n}\r\n\r\n// Пустой ответ значит «да», как подсказывает [Y/n]; непонятный ответ переспрашивается\r\nexport async function askYesNo(question: string): Promise<boolean> {\r\n const prompt = createInterface({ input: process.stdin, output: process.stdout })\r\n try {\r\n for (;;) {\r\n const answer = (await prompt.question(question)).trim().toLowerCase()\r\n if (answer === '' || answer === 'y' || answer === 'yes') {\r\n return true\r\n }\r\n if (answer === 'n' || answer === 'no') {\r\n return false\r\n }\r\n }\r\n }\r\n finally {\r\n prompt.close()\r\n }\r\n}\r\n\r\n// Остановка будит ожидание сразу: Ctrl+C не ждёт конца паузы между опросами\r\nexport async function sleep(ms: number, signal?: AbortSignal): Promise<void> {\r\n await wait(ms, undefined, { signal }).catch(() => {})\r\n}\r\n","import { spawn } from 'node:child_process'\r\nimport { readFileSync } from 'node:fs'\r\nimport process from 'node:process'\r\n\r\nexport const PACKAGE_NAME = '@tamsensedev/runner'\r\nexport const RELAUNCHED_ENV = 'TAMSENSE_RELAUNCHED'\r\nexport const NO_UPDATE_ENV = 'TAMSENSE_NO_UPDATE'\r\n\r\nconst REGISTRY_URL = `https://registry.npmjs.org/${PACKAGE_NAME.replace('/', '%2f')}/latest`\r\nconst REGISTRY_TIMEOUT_MS = 3000\r\nconst RELEASE_PATTERN = /^(\\d+)\\.(\\d+)\\.(\\d+)$/\r\n\r\nexport interface UpdateCheck {\r\n current: string\r\n latest: string\r\n}\r\n\r\ntype FetchLatest = (url: string, signal: AbortSignal) => Promise<unknown>\r\n\r\nfunction releaseParts(version: string): number[] | null {\r\n const match = RELEASE_PATTERN.exec(version.trim())\r\n return match ? match.slice(1).map(Number) : null\r\n}\r\n\r\n// Сравниваются только выпуски вида 1.2.3: предварительные версии и сборки из исходников не обновляются\r\nexport function isNewer(latest: string, current: string): boolean {\r\n const next = releaseParts(latest)\r\n const now = releaseParts(current)\r\n if (!next || !now) {\r\n return false\r\n }\r\n const index = next.findIndex((part, position) => part !== now[position])\r\n return index !== -1 && next[index]! > now[index]!\r\n}\r\n\r\nexport function currentVersion(): string | null {\r\n try {\r\n const manifest = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8')) as { name?: unknown, version?: unknown }\r\n return manifest.name === PACKAGE_NAME && typeof manifest.version === 'string' ? manifest.version : null\r\n }\r\n catch {\r\n return null\r\n }\r\n}\r\n\r\nasync function fetchLatest(url: string, signal: AbortSignal): Promise<unknown> {\r\n const response = await fetch(url, { signal, headers: { accept: 'application/json' } })\r\n return response.ok ? await response.json() : null\r\n}\r\n\r\n// Последняя версия на npm; без сети или при ответе с ошибкой обновления нет, программа работает как есть\r\nexport async function latestVersion(fetchJson: FetchLatest = fetchLatest, timeoutMs = REGISTRY_TIMEOUT_MS): Promise<string | null> {\r\n try {\r\n const body = await fetchJson(REGISTRY_URL, AbortSignal.timeout(timeoutMs))\r\n const version = body && typeof body === 'object' ? (body as { version?: unknown }).version : null\r\n return typeof version === 'string' ? version : null\r\n }\r\n catch {\r\n return null\r\n }\r\n}\r\n\r\n// После перезапуска и при TAMSENSE_NO_UPDATE обновление не проверяется: так перезапуск не зацикливается\r\nexport async function updateToRun(env: NodeJS.ProcessEnv, current: string | null, latest: () => Promise<string | null>): Promise<UpdateCheck | null> {\r\n if (!current || env[RELAUNCHED_ENV] || env[NO_UPDATE_ENV]) {\r\n return null\r\n }\r\n const found = await latest()\r\n return found && isNewer(found, current) ? { current, latest: found } : null\r\n}\r\n\r\nexport function relaunchArgs(argv: string[]): string[] {\r\n return ['--yes', `${PACKAGE_NAME}@latest`, ...argv]\r\n}\r\n\r\n// Новая версия запускается в этом же окне; Ctrl+C достаётся ей, а эта ждёт и выходит с её кодом\r\nexport function relaunch(argv: string[], env: NodeJS.ProcessEnv): Promise<number> {\r\n const isWindows = process.platform === 'win32'\r\n return new Promise((resolve, reject) => {\r\n const child = spawn(isWindows ? 'npx.cmd' : 'npx', relaunchArgs(argv), { stdio: 'inherit', shell: isWindows, env: { ...env, [RELAUNCHED_ENV]: '1' } })\r\n const ignoreInterrupt = () => {}\r\n process.on('SIGINT', ignoreInterrupt)\r\n child.on('error', (error) => {\r\n process.off('SIGINT', ignoreInterrupt)\r\n reject(error)\r\n })\r\n child.on('close', (code) => {\r\n process.off('SIGINT', ignoreInterrupt)\r\n resolve(code ?? 1)\r\n })\r\n })\r\n}\r\n","import type { ApiClient } from './api'\r\nimport type { InstalledBrowser } from './browser'\r\nimport type { RunAgent, SpecWorkTools } from './claude'\r\nimport type { FeedDraft } from './feed'\r\nimport type { FeedQueue } from './feedQueue'\r\nimport type { InputChannel } from './input'\r\nimport type { RunnerAttachmentResponse, RunnerCapabilities, RunnerFinishBody, RunnerGitTokenResponse, RunnerInbox, RunnerInboxBody, RunnerInboxMessage, RunnerJob, RunnerNextResponse, RunnerReviewReply, RunnerSession } from './protocol'\r\nimport type { AttachFields } from './screenshot'\r\nimport type { WorkspaceTools } from './workspace'\r\nimport { rm } from 'node:fs/promises'\r\nimport { dirname } from 'node:path'\r\nimport process from 'node:process'\r\nimport { ApiError } from './api'\r\nimport { browserEnv } from './browser'\r\nimport { capabilitiesLine } from './capabilities'\r\nimport { consoleLine } from './feed'\r\nimport { feedQueue } from './feedQueue'\r\nimport { inputChannel, switchLine } from './input'\r\nimport { keyedLock } from './lock'\r\nimport { buildTaskPrompt } from './prompt'\r\nimport { CAPABILITIES_PATH, MAX_ATTACHMENTS_PER_RUN, NEXT_RUN_PATH, runPath } from './protocol'\r\nimport { recorderEnv, recordingsFolder } from './recorderEnv'\r\nimport { attachRecording, uploadPendingRecordings } from './recordings'\r\nimport { readScreenshot } from './screenshot'\r\nimport { withNodeOnPath } from './system'\r\nimport { USAGE_LIMIT_REPORT } from './usageLimit'\r\nimport { commitIdentityEnv, prepareWorkspace, publishWorkspace } from './workspace'\r\n\r\nexport interface WorkerTools extends WorkspaceTools {\r\n api: ApiClient\r\n runAgent: RunAgent\r\n print: (line: string) => void\r\n sleep: (ms: number, signal?: AbortSignal) => Promise<void>\r\n // браузер для проверок интерфейса, если человек согласился его поставить\r\n browser?: InstalledBrowser | null\r\n // что умеет Claude на компьютере, без траты токенов; без него сайт этого не узнаёт\r\n capabilities?: (stop: AbortSignal) => Promise<RunnerCapabilities>\r\n // модуль записи браузера агента (recorder.cjs рядом с программой); без него браузер не записывается\r\n recorder?: string | null\r\n}\r\n\r\nconst POLL_INTERVAL_MS = 5_000\r\nconst RETRY_AFTER_ERROR_MS = 15_000\r\n// заодно с сообщениями людей сайт узнаёт, что компьютер ещё работает над задачей\r\nconst INBOX_INTERVAL_MS = 2_000\r\nconst MAX_MESSAGE_LINE = 200\r\nconst CAPABILITIES_INTERVAL_MS = 30 * 60_000\r\n// журнал уходит на сайт почти сразу: человек видит работу Claude вживую\r\nconst FEED_INTERVAL_MS = 1_500\r\nconst FATAL_STATUSES = new Set([401, 403])\r\n\r\nconst STOPPED_HERE = 'stopped_here'\r\nconst CLOSED_ON_SITE = 'closed_on_site'\r\n\r\nfunction messageOf(error: unknown): string {\r\n return error instanceof Error ? error.message : String(error)\r\n}\r\n\r\n// Отказ в доступе не лечится повтором: компьютер отключили или у человека забрали задачи\r\nfunction isFatal(error: unknown): boolean {\r\n return error instanceof ApiError && FATAL_STATUSES.has(error.status)\r\n}\r\n\r\nasync function gitToken(api: ApiClient, job: RunnerJob): Promise<string> {\r\n return (await api.request<RunnerGitTokenResponse>('POST', runPath(job.runId, 'git-token'))).token\r\n}\r\n\r\n// Ответ уходит Claude: приложено, или почему нет, чтобы он поправил путь или формат\r\nasync function attachScreenshot(api: ApiClient, job: RunnerJob, screenshot: AttachFields, attached: { count: number }): Promise<string> {\r\n if (attached.count >= MAX_ATTACHMENTS_PER_RUN) {\r\n return `This run already attached ${MAX_ATTACHMENTS_PER_RUN} screenshots. Mention the rest in your report.`\r\n }\r\n const body = await readScreenshot(screenshot)\r\n if (typeof body === 'string') {\r\n return body\r\n }\r\n try {\r\n await api.request<RunnerAttachmentResponse>('POST', runPath(job.runId, 'attachments'), body)\r\n }\r\n catch (error) {\r\n return `Could not attach the screenshot: ${messageOf(error)}`\r\n }\r\n attached.count += 1\r\n return 'Attached to the task.'\r\n}\r\n\r\n// Сайт показывает, какая модель и чей вход взялись за задачу; сбой не мешает работе\r\nfunction sessionReporter(api: ApiClient, job: RunnerJob, print: (line: string) => void): { report: (session: RunnerSession) => void, sent: () => Promise<void> } {\r\n let sending: Promise<void> | null = null\r\n return {\r\n report: (session) => {\r\n sending ??= api.request('POST', runPath(job.runId, 'session'), session)\r\n .then(() => {}, error => print(`Could not tell TAMsense which Claude took the task: ${messageOf(error)}`))\r\n },\r\n sent: async () => {\r\n await sending\r\n },\r\n }\r\n}\r\n\r\n// Запись журнала уходит на сайт, а действия Claude ещё и печатаются здесь\r\nfunction journal(feed: FeedQueue, print: (line: string) => void): (draft: FeedDraft) => void {\r\n return (draft) => {\r\n feed.push(draft)\r\n const line = consoleLine(draft)\r\n if (line) {\r\n print(` ${line}`)\r\n }\r\n }\r\n}\r\n\r\nfunction messageLine(message: Pick<RunnerInboxMessage, 'authorName' | 'text'>): string {\r\n const line = `Message from ${message.authorName}: ${message.text}`.replace(/\\s+/g, ' ').trim()\r\n return line.length > MAX_MESSAGE_LINE ? `${line.slice(0, MAX_MESSAGE_LINE - 1)}…` : line\r\n}\r\n\r\n// input: что уходит Claude; после его хода входящие проверяются сразу, не дожидаясь очередной проверки\r\ninterface Inbox {\r\n input: InputChannel\r\n poll: () => Promise<void>\r\n settled: () => Promise<void>\r\n}\r\n\r\n// Входящие запуска: жив ли он на сайте, новые сообщения людей и смена модели; в ответ сайт узнаёт, что уже передано Claude.\r\n// Сбой не останавливает работу: следующая проверка повторит, а строка печатается одна на всю полосу сбоев\r\nfunction inbox(job: RunnerJob, tools: WorkerTools, record: (draft: FeedDraft) => void, closedOnSite: () => void): Inbox {\r\n const input = inputChannel({ onIdle: () => void poll() })\r\n let running: Promise<void> | null = null\r\n let again = false\r\n let failing = false\r\n\r\n async function check(): Promise<void> {\r\n const ack = input.takeDelivered()\r\n const body: RunnerInboxBody = { ack }\r\n let reply: RunnerInbox\r\n try {\r\n reply = await tools.api.request<RunnerInbox>('POST', runPath(job.runId, 'inbox'), body)\r\n }\r\n catch (error) {\r\n input.restoreDelivered(ack)\r\n if (!failing) {\r\n tools.print(`Could not check for new messages on TAMsense, trying again: ${messageOf(error)}`)\r\n }\r\n failing = true\r\n return\r\n }\r\n failing = false\r\n if (!reply.keepGoing) {\r\n closedOnSite()\r\n return\r\n }\r\n for (const message of reply.messages) {\r\n if (input.push(message)) {\r\n tools.print(` ${messageLine(message)}`)\r\n }\r\n }\r\n if (reply.model !== null || reply.effort !== null) {\r\n const change = { model: reply.model, effort: reply.effort }\r\n void input.switchModel(change).then(\r\n (applied) => {\r\n if (applied) {\r\n record({ kind: 'tool', text: switchLine(change), tool: 'model', ref: null, parent: null, failed: false })\r\n }\r\n },\r\n error => tools.print(`Could not switch the model: ${messageOf(error)}`),\r\n )\r\n }\r\n }\r\n\r\n // проверка, попросившая слова во время другой, идёт сразу после неё: Claude мог как раз закончить ход\r\n function poll(): Promise<void> {\r\n if (running) {\r\n again = true\r\n return running\r\n }\r\n running = input.checking(check).finally(() => {\r\n running = null\r\n if (again) {\r\n again = false\r\n void poll()\r\n }\r\n })\r\n return running\r\n }\r\n\r\n return {\r\n input,\r\n poll,\r\n settled: async () => {\r\n for (let current = running; current; current = running) {\r\n await current\r\n }\r\n },\r\n }\r\n}\r\n\r\n// Шаг ТЗ: вопросы и ТЗ сдаются инструментами, а сайт проверяет их теми же правилами, что в облаке\r\nfunction specWorkTools(job: RunnerJob, api: ApiClient): SpecWorkTools | null {\r\n const { kind } = job.stage\r\n if (kind !== 'interview' && kind !== 'spec') {\r\n return null\r\n }\r\n return { stage: kind, review: body => api.request<RunnerReviewReply>('POST', runPath(job.runId, 'review'), body) }\r\n}\r\n\r\nasync function executeJob(job: RunnerJob, tools: WorkerTools, signal: AbortSignal, input: InputChannel, record: (draft: FeedDraft) => void): Promise<RunnerFinishBody> {\r\n const isCheck = job.stage.kind === 'review' || job.stage.kind === 'test'\r\n tools.print('Preparing the code…')\r\n const workspace = await prepareWorkspace(job, await gitToken(tools.api, job), tools)\r\n const identity = await commitIdentityEnv(tools.home)\r\n signal.throwIfAborted()\r\n\r\n tools.print('Claude is working…')\r\n const attached = { count: 0 }\r\n const session = sessionReporter(tools.api, job, tools.print)\r\n const recordings = recordingsFolder(tools.home, job.runId)\r\n const baseEnv = withNodeOnPath(process.env, dirname(process.execPath))\r\n const recording = tools.recorder ? recorderEnv({ recorder: tools.recorder, folder: recordings, server: tools.api.server, job, nodeOptions: baseEnv.NODE_OPTIONS }) : {}\r\n const outcome = await tools.runAgent({\r\n cwd: workspace.root,\r\n prompt: buildTaskPrompt(job, workspace, { browser: Boolean(tools.browser), recorder: Boolean(tools.recorder) }),\r\n input,\r\n env: { ...baseEnv, ...(tools.browser ? browserEnv(tools.browser) : {}), ...recording, ...identity },\r\n signal,\r\n wantsVerdict: isCheck,\r\n specWork: specWorkTools(job, tools.api),\r\n handoffs: job.allowedHandoffs,\r\n resumeSessionId: job.resumeSessionId,\r\n model: job.model,\r\n effort: job.effort,\r\n onFeed: record,\r\n onSession: session.report,\r\n attach: request => request.kind === 'screenshot'\r\n ? attachScreenshot(tools.api, job, request, attached)\r\n : attachRecording(tools.api, job.runId, recordings, request),\r\n })\r\n await session.sent()\r\n signal.throwIfAborted()\r\n\r\n // ключ живёт час, а работа бывает дольше: для отправки ветки берётся свежий\r\n // проверка и шаг ТЗ ничего не отправляют: их итог это вердикт, вопросы или ТЗ; код отправляется, только если Claude закончил работу\r\n const shouldPublish = job.stage.kind === 'code' && outcome.completed\r\n const pushed = shouldPublish ? await publishWorkspace(workspace, job, await gitToken(tools.api, job), identity) : []\r\n return {\r\n outcome: outcome.completed ? 'succeeded' : 'failed',\r\n report: outcome.report,\r\n pushed,\r\n model: outcome.model,\r\n usage: outcome.usage,\r\n verdict: outcome.verdict,\r\n handoff: outcome.handoff ?? null,\r\n sessionId: outcome.sessionId ?? null,\r\n pausedUntil: outcome.pausedUntil ?? null,\r\n questions: outcome.questions ?? null,\r\n spec: outcome.spec ?? null,\r\n }\r\n}\r\n\r\nfunction clockOf(iso: string): string {\r\n const time = new Date(iso)\r\n return `${String(time.getHours()).padStart(2, '0')}:${String(time.getMinutes()).padStart(2, '0')}`\r\n}\r\n\r\nexport function resultLine(finish: RunnerFinishBody): string {\r\n if (finish.pausedUntil) {\r\n return `Paused: ${USAGE_LIMIT_REPORT}, resumes at ${clockOf(finish.pausedUntil)}`\r\n }\r\n if (finish.verdict) {\r\n return finish.verdict.passed ? 'Check passed.' : `Check did not pass: ${finish.verdict.report}`\r\n }\r\n if (finish.questions) {\r\n return finish.questions.questions.length > 0 ? `Done. ${finish.questions.questions.length} question(s) wait for the owner on TAMsense.` : 'Done. No questions needed, the spec is written next.'\r\n }\r\n if (finish.spec) {\r\n return 'Done. The spec waits for the owner on TAMsense.'\r\n }\r\n return finish.outcome === 'succeeded'\r\n ? `Done. Pushed ${finish.pushed.length} branch(es), the pull requests open on TAMsense.`\r\n : `Failed: ${finish.report}`\r\n}\r\n\r\n// Всё, что браузер агента записал и агент не приложил сам, уходит в задачу до итога запуска: потом сайт закроет запуск\r\nasync function sendRecordings(job: RunnerJob, tools: WorkerTools): Promise<void> {\r\n const folder = recordingsFolder(tools.home, job.runId)\r\n try {\r\n const report = await uploadPendingRecordings(tools.api, job.runId, folder)\r\n if (report.uploaded > 0) {\r\n tools.print(`Attached ${report.uploaded} browser recording(s) to the task.`)\r\n }\r\n if (report.skipped > 0) {\r\n tools.print(`${report.skipped} more recording(s) were not attached: the run reached its limit.`)\r\n }\r\n for (const line of report.failed) {\r\n tools.print(`Could not attach a recording: ${line}`)\r\n }\r\n }\r\n finally {\r\n await rm(folder, { recursive: true, force: true })\r\n }\r\n}\r\n\r\nfunction failureReport(error: unknown, signal: AbortSignal): string {\r\n return signal.reason === STOPPED_HERE ? 'The run was stopped on the computer.' : `The run stopped on the computer: ${messageOf(error)}`\r\n}\r\n\r\nexport async function runJob(job: RunnerJob, tools: WorkerTools, stop: AbortSignal): Promise<void> {\r\n tools.print(`Task #${job.taskId}: ${job.title} (${job.stage.name})`)\r\n const run = new AbortController()\r\n const stopRun = () => run.abort(STOPPED_HERE)\r\n if (stop.aborted) {\r\n stopRun()\r\n }\r\n stop.addEventListener('abort', stopRun, { once: true })\r\n const feed = feedQueue(entries => tools.api.request('POST', runPath(job.runId, 'feed'), { entries }))\r\n const feeding = setInterval(() => void feed.flush(), FEED_INTERVAL_MS)\r\n const record = journal(feed, tools.print)\r\n const messages = inbox(job, tools, record, () => run.abort(CLOSED_ON_SITE))\r\n const { input } = messages\r\n run.signal.addEventListener('abort', input.close, { once: true })\r\n void messages.poll()\r\n const polling = setInterval(() => void messages.poll(), INBOX_INTERVAL_MS)\r\n\r\n let finish: RunnerFinishBody\r\n try {\r\n finish = await executeJob(job, tools, run.signal, input, record)\r\n }\r\n catch (error) {\r\n finish = { outcome: 'failed', report: failureReport(error, run.signal), pushed: [], model: null, usage: null, verdict: null, handoff: null, sessionId: null, pausedUntil: null, questions: null, spec: null }\r\n }\r\n finally {\r\n clearInterval(polling)\r\n clearInterval(feeding)\r\n stop.removeEventListener('abort', stopRun)\r\n input.close()\r\n }\r\n // последние переданные Claude сообщения подтверждаются до итога\r\n await messages.settled()\r\n if (input.hasDelivered()) {\r\n await messages.poll()\r\n }\r\n await feed.flush()\r\n\r\n if (run.signal.reason === CLOSED_ON_SITE) {\r\n await rm(recordingsFolder(tools.home, job.runId), { recursive: true, force: true })\r\n tools.print('TAMsense closed this run while it was in progress. The result was not sent.')\r\n return\r\n }\r\n await sendRecordings(job, tools).catch(error => tools.print(`Could not attach the browser recordings: ${messageOf(error)}`))\r\n try {\r\n await tools.api.request('POST', runPath(job.runId, 'finish'), finish)\r\n tools.print(resultLine(finish))\r\n }\r\n catch (error) {\r\n tools.print(`Could not send the result to TAMsense: ${messageOf(error)}`)\r\n }\r\n}\r\n\r\n// При запуске и потом раз в полчаса: вход и модели могли смениться. Ошибка печатается один раз и ничего не останавливает\r\nasync function reportCapabilities(tools: WorkerTools, stop: AbortSignal): Promise<void> {\r\n if (!tools.capabilities) {\r\n return\r\n }\r\n let printed = false\r\n let complained = false\r\n while (!stop.aborted) {\r\n try {\r\n const capabilities = await tools.capabilities(stop)\r\n await tools.api.request('PUT', CAPABILITIES_PATH, capabilities)\r\n if (!printed) {\r\n tools.print(capabilitiesLine(capabilities))\r\n printed = true\r\n }\r\n }\r\n catch (error) {\r\n if (!complained && !stop.aborted) {\r\n tools.print(`Could not tell TAMsense which Claude models this computer has: ${messageOf(error)}`)\r\n complained = true\r\n }\r\n }\r\n await tools.sleep(CAPABILITIES_INTERVAL_MS, stop)\r\n }\r\n}\r\n\r\n// Несколько задач сразу: строки каждой подписаны номером задачи, иначе их не различить\r\nfunction jobTools(job: RunnerJob, tools: WorkerTools, parallel: number): WorkerTools {\r\n return parallel > 1 ? { ...tools, print: line => tools.print(`[#${job.taskId}] ${line}`) } : tools\r\n}\r\n\r\n// Берёт не больше parallel задач сразу; два запуска одной задачи идут по очереди, у них одна рабочая папка\r\nexport async function work(tools: WorkerTools, stop: AbortSignal, parallel: number = 1): Promise<void> {\r\n tools.print(parallel > 1 ? `Waiting for tasks, up to ${parallel} at once. Press Ctrl+C to stop.` : 'Waiting for tasks. Press Ctrl+C to stop.')\r\n // отказ в доступе тоже останавливает все идущие задачи, каждая отчитывается\r\n const halt = new AbortController()\r\n const stopAll = AbortSignal.any([stop, halt.signal])\r\n const running = new Set<Promise<void>>()\r\n const taskLock = keyedLock()\r\n const reporting = reportCapabilities(tools, stopAll)\r\n const start = (job: RunnerJob) => {\r\n const runTools = jobTools(job, tools, parallel)\r\n const task: Promise<void> = taskLock.run(String(job.taskId), () => runJob(job, runTools, stopAll))\r\n .catch(error => runTools.print(`The run stopped: ${messageOf(error)}`))\r\n .finally(() => running.delete(task))\r\n running.add(task)\r\n }\r\n try {\r\n while (!stopAll.aborted) {\r\n if (running.size >= parallel) {\r\n await Promise.race(running)\r\n continue\r\n }\r\n let reply: RunnerNextResponse\r\n try {\r\n reply = await tools.api.request<RunnerNextResponse>('POST', NEXT_RUN_PATH)\r\n }\r\n catch (error) {\r\n if (isFatal(error)) {\r\n halt.abort()\r\n throw error\r\n }\r\n tools.print(`Could not reach TAMsense: ${messageOf(error)}. Trying again soon.`)\r\n await tools.sleep(RETRY_AFTER_ERROR_MS, stopAll)\r\n continue\r\n }\r\n if (reply.job) {\r\n start(reply.job)\r\n }\r\n else {\r\n await tools.sleep(POLL_INTERVAL_MS, stopAll)\r\n }\r\n }\r\n }\r\n finally {\r\n await Promise.allSettled([...running, reporting])\r\n }\r\n}\r\n","import type { FeedDraft } from './feed'\r\nimport type { RunnerFeedEntry } from './protocol'\r\n\r\nexport interface FeedQueue {\r\n push: (draft: FeedDraft) => RunnerFeedEntry\r\n flush: () => Promise<void>\r\n}\r\n\r\nconst MAX_BATCH = 200\r\nconst MAX_WAITING = 2_000\r\n\r\n// Журнал копится и уходит на сайт пачками. Неотправленное ждёт следующей попытки;\r\n// если сайт долго недоступен, теряются самые старые записи, а не работа Claude\r\nexport function feedQueue(send: (entries: RunnerFeedEntry[]) => Promise<void>): FeedQueue {\r\n let seq = 0\r\n let waiting: RunnerFeedEntry[] = []\r\n let sending: Promise<void> = Promise.resolve()\r\n\r\n async function drain(): Promise<void> {\r\n while (waiting.length > 0) {\r\n const batch = waiting.slice(0, MAX_BATCH)\r\n try {\r\n await send(batch)\r\n }\r\n catch {\r\n return\r\n }\r\n const lastSent = batch.at(-1)!.seq\r\n waiting = waiting.filter(entry => entry.seq > lastSent)\r\n }\r\n }\r\n\r\n return {\r\n push(draft) {\r\n seq += 1\r\n const entry = { ...draft, seq }\r\n waiting.push(entry)\r\n if (waiting.length > MAX_WAITING) {\r\n waiting = waiting.slice(-MAX_WAITING)\r\n }\r\n return entry\r\n },\r\n flush() {\r\n sending = sending.then(drain)\r\n return sending\r\n },\r\n }\r\n}\r\n","export interface KeyedLock {\r\n run: <T>(key: string, work: () => Promise<T>) => Promise<T>\r\n}\r\n\r\n// Работа с одним ключом идёт по очереди, с разными ключами параллельно\r\nexport function keyedLock(): KeyedLock {\r\n const tails = new Map<string, Promise<void>>()\r\n return {\r\n async run<T>(key: string, work: () => Promise<T>): Promise<T> {\r\n const previous = tails.get(key) ?? Promise.resolve()\r\n let release = () => {}\r\n const done = new Promise<void>((resolve) => {\r\n release = resolve\r\n })\r\n const tail = previous.then(() => done)\r\n tails.set(key, tail)\r\n await previous\r\n try {\r\n return await work()\r\n }\r\n finally {\r\n release()\r\n if (tails.get(key) === tail) {\r\n tails.delete(key)\r\n }\r\n }\r\n },\r\n }\r\n}\r\n","import type { ApiClient } from './api'\r\nimport type { RunnerAttachmentResponse, RunnerRecordingBody, RunnerRecordingMark, RunnerVideoType } from './protocol'\r\nimport type { RecordingCaption, SessionMeta } from './recorder'\r\nimport type { AttachFields } from './screenshot'\r\nimport { randomUUID } from 'node:crypto'\r\nimport { createReadStream } from 'node:fs'\r\nimport { readdir, readFile, stat, writeFile } from 'node:fs/promises'\r\nimport { basename, dirname, extname, isAbsolute, join } from 'node:path'\r\nimport { createInterface } from 'node:readline'\r\nimport { Readable } from 'node:stream'\r\nimport { promisify } from 'node:util'\r\nimport { gzip } from 'node:zlib'\r\nimport { ApiError } from './api'\r\nimport { MAX_VIDEO_BYTES, recordingUploadPath, runPath } from './protocol'\r\nimport { checkCaption, MARK_TAG, MAX_TITLE_LENGTH, META_SUFFIX, REPLAY_SUFFIX } from './recorder'\r\n\r\nconst gzipAsync = promisify(gzip)\r\n\r\n// кусок записи до сжатия; сайт принимает до 4 МБ сжатого и 32 МБ несжатого\r\nexport const CHUNK_TARGET_BYTES = 2 * 1024 * 1024\r\nconst MAX_LINE_BYTES = 30 * 1024 * 1024\r\nconst LEDGER_FILE = 'uploaded.json'\r\nconst RRWEB_FULL_SNAPSHOT = 2\r\nconst RRWEB_META = 4\r\nconst RRWEB_CUSTOM = 5\r\n\r\nconst VIDEO_TYPES_BY_EXTENSION: Record<string, RunnerVideoType> = {\r\n '.webm': 'video/webm',\r\n '.mp4': 'video/mp4',\r\n}\r\n\r\nexport type RecordingKind = 'replay' | 'video'\r\n\r\n// Запись или видео в папке запуска, которые ещё не ушли на сайт\r\nexport interface PendingRecording {\r\n kind: RecordingKind\r\n file: string\r\n meta: SessionMeta | null\r\n}\r\n\r\ninterface ReplayEvent {\r\n type: number\r\n timestamp: number\r\n data?: { tag?: unknown, payload?: { label?: unknown } }\r\n}\r\n\r\ninterface ReplayScan {\r\n startTs: number\r\n snapshot: boolean\r\n marks: Array<{ ts: number, label: string }>\r\n}\r\n\r\nfunction eventOf(line: string): ReplayEvent | null {\r\n try {\r\n const event = JSON.parse(line) as ReplayEvent\r\n return typeof event?.type === 'number' && typeof event.timestamp === 'number' ? event : null\r\n }\r\n catch {\r\n return null\r\n }\r\n}\r\n\r\n// Новый кусок начинается там, где rrweb снимает страницу заново (переход на другую страницу), или когда кусок вырос:\r\n// так же режет записи трекер, и плеер перематывает от ближайшего снимка\r\nexport async function* replayChunks(lines: AsyncIterable<string>, scan: ReplayScan, targetBytes: number = CHUNK_TARGET_BYTES): AsyncGenerator<string> {\r\n let chunk: string[] = []\r\n let bytes = 0\r\n let previous: ReplayEvent | null = null\r\n for await (const line of lines) {\r\n const event = line.trim() ? eventOf(line) : null\r\n if (!event) {\r\n continue\r\n }\r\n if (line.length > MAX_LINE_BYTES) {\r\n throw new Error('One event of this replay is larger than 30 MB, so it cannot be uploaded.')\r\n }\r\n const startsGroup = event.type === RRWEB_META || (event.type === RRWEB_FULL_SNAPSHOT && previous?.type !== RRWEB_META)\r\n if (chunk.length > 0 && (startsGroup || bytes + line.length > targetBytes)) {\r\n yield `${chunk.join('\\n')}\\n`\r\n chunk = []\r\n bytes = 0\r\n }\r\n chunk.push(line)\r\n bytes += line.length + 1\r\n previous = event\r\n scan.startTs = Math.min(scan.startTs, event.timestamp)\r\n scan.snapshot ||= event.type === RRWEB_FULL_SNAPSHOT\r\n if (event.type === RRWEB_CUSTOM && event.data?.tag === MARK_TAG && typeof event.data.payload?.label === 'string') {\r\n scan.marks.push({ ts: event.timestamp, label: event.data.payload.label })\r\n }\r\n }\r\n if (chunk.length > 0) {\r\n yield `${chunk.join('\\n')}\\n`\r\n }\r\n}\r\n\r\nexport function marksFrom(marks: Array<{ ts: number, label: string }>, startTs: number): RunnerRecordingMark[] {\r\n return marks.map(mark => ({ at: Math.max(0, mark.ts - startTs), label: mark.label })).sort((a, b) => a.at - b.at)\r\n}\r\n\r\nfunction sessionIdOf(file: string): string {\r\n return basename(file).replace(REPLAY_SUFFIX, '')\r\n}\r\n\r\nasync function readMeta(file: string): Promise<SessionMeta | null> {\r\n try {\r\n return JSON.parse(await readFile(file, 'utf8')) as SessionMeta\r\n }\r\n catch {\r\n return null\r\n }\r\n}\r\n\r\n// Подпись рядом с записью: файл .json того же сеанса; у видео из записи сеанс находится по пути видео\r\nasync function metaFor(kind: RecordingKind, file: string): Promise<SessionMeta | null> {\r\n if (kind === 'replay') {\r\n return readMeta(join(dirname(file), `${sessionIdOf(file)}${META_SUFFIX}`))\r\n }\r\n const folder = basename(dirname(file)) === 'video' ? dirname(dirname(file)) : dirname(file)\r\n for (const name of await readdir(folder).catch(() => [])) {\r\n if (name.endsWith(META_SUFFIX) && name !== LEDGER_FILE) {\r\n const meta = await readMeta(join(folder, name))\r\n if (meta?.video === file) {\r\n return meta\r\n }\r\n }\r\n }\r\n return null\r\n}\r\n\r\nasync function ledger(dir: string): Promise<Set<string>> {\r\n try {\r\n return new Set(JSON.parse(await readFile(join(dir, LEDGER_FILE), 'utf8')) as string[])\r\n }\r\n catch {\r\n return new Set()\r\n }\r\n}\r\n\r\nasync function remember(dir: string, file: string): Promise<void> {\r\n const done = await ledger(dir)\r\n done.add(file)\r\n await writeFile(join(dir, LEDGER_FILE), JSON.stringify([...done]))\r\n}\r\n\r\nasync function nonEmpty(file: string | null): Promise<boolean> {\r\n return file !== null && ((await stat(file).catch(() => null))?.size ?? 0) > 0\r\n}\r\n\r\nexport async function pendingRecordings(dir: string): Promise<PendingRecording[]> {\r\n const done = await ledger(dir)\r\n const names = (await readdir(dir).catch(() => [] as string[])).sort()\r\n const pending: PendingRecording[] = []\r\n for (const name of names) {\r\n if (!name.endsWith(META_SUFFIX) || name === LEDGER_FILE) {\r\n continue\r\n }\r\n const meta = await readMeta(join(dir, name))\r\n const replay = join(dir, `${name.slice(0, -META_SUFFIX.length)}${REPLAY_SUFFIX}`)\r\n if (!done.has(replay) && await nonEmpty(replay)) {\r\n pending.push({ kind: 'replay', file: replay, meta })\r\n }\r\n if (meta?.video && !done.has(meta.video) && await nonEmpty(meta.video)) {\r\n pending.push({ kind: 'video', file: meta.video, meta })\r\n }\r\n }\r\n return pending\r\n}\r\n\r\ninterface Upload {\r\n api: ApiClient\r\n runId: number\r\n}\r\n\r\nasync function uploadReplay(upload: Upload, file: string, caption: RecordingCaption): Promise<number> {\r\n const id = randomUUID()\r\n const scan: ReplayScan = { startTs: Number.POSITIVE_INFINITY, snapshot: false, marks: [] }\r\n const lines = createInterface({ input: createReadStream(file, 'utf8'), crlfDelay: Infinity })\r\n for await (const chunk of replayChunks(lines, scan)) {\r\n await upload.api.upload('POST', recordingUploadPath(upload.runId, id, 'chunks'), new Uint8Array(await gzipAsync(chunk)), 'application/gzip')\r\n }\r\n if (!scan.snapshot) {\r\n throw new Error('This replay has no page snapshot, so it cannot be played. Was the page ever opened?')\r\n }\r\n return finish(upload, { kind: 'replay', upload: id, name: basename(file), ...caption, contentType: 'application/x-ndjson', durationMs: null, marks: marksFrom(scan.marks, scan.startTs) })\r\n}\r\n\r\nasync function uploadVideo(upload: Upload, file: string, caption: RecordingCaption, meta: SessionMeta | null): Promise<number> {\r\n const contentType = VIDEO_TYPES_BY_EXTENSION[extname(file).toLowerCase()]!\r\n const info = await stat(file)\r\n if (info.size > MAX_VIDEO_BYTES) {\r\n throw new Error(`The video is larger than ${MAX_VIDEO_BYTES / 1024 / 1024} MB. Record a shorter one.`)\r\n }\r\n const id = randomUUID()\r\n const body = Readable.toWeb(createReadStream(file)) as ReadableStream<Uint8Array>\r\n await upload.api.upload('PUT', recordingUploadPath(upload.runId, id, 'video'), body, contentType)\r\n const startedAt = meta?.startedAt ?? info.birthtimeMs\r\n const endedAt = meta?.endedAt ?? info.mtimeMs\r\n const durationMs = Math.max(0, Math.round(endedAt - startedAt))\r\n const marks = marksFrom(meta?.marks ?? [], startedAt).filter(mark => mark.at <= durationMs)\r\n return finish(upload, { kind: 'video', upload: id, name: basename(file), ...caption, contentType, durationMs, marks })\r\n}\r\n\r\nasync function finish(upload: Upload, body: RunnerRecordingBody): Promise<number> {\r\n return (await upload.api.request<RunnerAttachmentResponse>('POST', runPath(upload.runId, 'recordings'), body)).id\r\n}\r\n\r\n// Ответ Claude на attach_replay и attach_video: приложено, или почему нет, чтобы он поправил путь или подпись\r\nexport async function attachRecording(api: ApiClient, runId: number, recordingsDir: string, request: AttachFields & { kind: RecordingKind }): Promise<string> {\r\n if (!isAbsolute(request.path)) {\r\n return `Give the absolute path of the ${request.kind === 'replay' ? 'replay (.rrweb.ndjson)' : 'video (.webm or .mp4)'} file.`\r\n }\r\n const fitsKind = request.kind === 'replay' ? request.path.endsWith(REPLAY_SUFFIX) : extname(request.path).toLowerCase() in VIDEO_TYPES_BY_EXTENSION\r\n if (!fitsKind) {\r\n return request.kind === 'replay' ? `A replay is the ${REPLAY_SUFFIX} file the TAMsense recorder wrote.` : 'Only WebM and MP4 videos can be attached.'\r\n }\r\n if (!await nonEmpty(request.path)) {\r\n return `There is no recording at ${request.path}. Close the browser context first (closeRecorded) so the file is complete.`\r\n }\r\n let caption: RecordingCaption\r\n try {\r\n caption = checkCaption(request)\r\n }\r\n catch (error) {\r\n return error instanceof Error ? error.message : String(error)\r\n }\r\n try {\r\n const meta = await metaFor(request.kind, request.path)\r\n const upload = { api, runId }\r\n await (request.kind === 'replay' ? uploadReplay(upload, request.path, caption) : uploadVideo(upload, request.path, caption, meta))\r\n await remember(recordingsDir, request.path)\r\n }\r\n catch (error) {\r\n return `Could not attach the ${request.kind}: ${error instanceof Error ? error.message : String(error)}`\r\n }\r\n return 'Attached to the task.'\r\n}\r\n\r\nfunction numbered(key: string, title: string, used: Map<string, number>): string {\r\n const count = (used.get(key) ?? 0) + 1\r\n used.set(key, count)\r\n const suffix = count > 1 ? ` (${count})` : ''\r\n return `${title.slice(0, MAX_TITLE_LENGTH - suffix.length)}${suffix}`\r\n}\r\n\r\n// сайт отказал, потому что запуск уже приложил сколько можно записей этого вида\r\nfunction overLimit(error: unknown): boolean {\r\n return error instanceof ApiError && error.status === 422 && /at most/i.test(error.message)\r\n}\r\n\r\nexport interface UploadReport {\r\n uploaded: number\r\n failed: string[]\r\n // не ушли из-за лимита запуска\r\n skipped: number\r\n}\r\n\r\n// В конце запуска на сайт уходит всё записанное, что агент не приложил сам: сначала то, что агент назвал сам, потом\r\n// записанное автоматически. Одинаковые названия получают номер\r\nexport async function uploadPendingRecordings(api: ApiClient, runId: number, dir: string): Promise<UploadReport> {\r\n const report: UploadReport = { uploaded: 0, failed: [], skipped: 0 }\r\n const used = new Map<string, number>()\r\n const full = new Set<RecordingKind>()\r\n const upload = { api, runId }\r\n const pending = (await pendingRecordings(dir)).sort((a, b) => Number(a.meta?.automatic ?? true) - Number(b.meta?.automatic ?? true))\r\n for (const recording of pending) {\r\n if (full.has(recording.kind)) {\r\n report.skipped += 1\r\n continue\r\n }\r\n const title = recording.meta?.title || 'Browser session'\r\n const caption = checkCaption({\r\n title: numbered(`${recording.kind}:${title}`, title, used),\r\n purpose: recording.meta?.purpose || 'Recorded automatically while the agent used a browser',\r\n })\r\n try {\r\n await (recording.kind === 'replay' ? uploadReplay(upload, recording.file, caption) : uploadVideo(upload, recording.file, caption, recording.meta))\r\n await remember(dir, recording.file)\r\n report.uploaded += 1\r\n }\r\n catch (error) {\r\n if (overLimit(error)) {\r\n full.add(recording.kind)\r\n report.skipped += 1\r\n continue\r\n }\r\n report.failed.push(`${caption.title} (${recording.kind}): ${error instanceof Error ? error.message : String(error)}`)\r\n }\r\n }\r\n return report\r\n}\r\n","import type { RunnerAttachmentBody, RunnerAttachmentType } from './protocol'\r\nimport { readFile, stat } from 'node:fs/promises'\r\nimport { basename, extname, isAbsolute } from 'node:path'\r\nimport { MAX_ATTACHMENT_BYTES } from './protocol'\r\nimport { checkCaption } from './recorder'\r\n\r\nconst TYPES_BY_EXTENSION: Record<string, RunnerAttachmentType> = {\r\n '.png': 'image/png',\r\n '.jpg': 'image/jpeg',\r\n '.jpeg': 'image/jpeg',\r\n '.webp': 'image/webp',\r\n}\r\n\r\n// Файл, который Claude прикладывает к задаче: путь, что на нём (title) и зачем он сделан (purpose)\r\nexport interface AttachFields {\r\n path: string\r\n title: string\r\n purpose: string\r\n}\r\n\r\nexport type AttachRequest = AttachFields & ({ kind: 'screenshot' } | { kind: 'replay' | 'video' })\r\n\r\n// Ответ Claude, если файл не годится: он поправит путь, формат или подпись и попробует снова\r\nexport async function readScreenshot(request: AttachFields): Promise<RunnerAttachmentBody | string> {\r\n if (!isAbsolute(request.path)) {\r\n return 'Give the absolute path of the image file.'\r\n }\r\n const contentType = TYPES_BY_EXTENSION[extname(request.path).toLowerCase()]\r\n if (!contentType) {\r\n return 'Only PNG, JPEG and WebP images can be attached.'\r\n }\r\n let caption: { title: string, purpose: string }\r\n try {\r\n caption = checkCaption(request)\r\n }\r\n catch (error) {\r\n return error instanceof Error ? error.message : String(error)\r\n }\r\n const info = await stat(request.path).catch(() => null)\r\n if (!info?.isFile()) {\r\n return `There is no file at ${request.path}.`\r\n }\r\n if (info.size > MAX_ATTACHMENT_BYTES) {\r\n return 'The image is larger than 5 MB. Take a smaller screenshot (viewport instead of full page, or JPEG).'\r\n }\r\n const data = (await readFile(request.path)).toString('base64')\r\n return { name: basename(request.path), ...caption, contentType, data }\r\n}\r\n","import type { RunnerJob, RunnerPushedBranch, RunnerRepository } from './protocol'\r\nimport { existsSync } from 'node:fs'\r\nimport { rm } from 'node:fs/promises'\r\nimport { join } from 'node:path'\r\nimport { git, runGit } from './git'\r\nimport { keyedLock } from './lock'\r\n\r\nexport interface WorkspaceRepository {\r\n fullName: string\r\n folder: string\r\n path: string\r\n baseSha: string\r\n}\r\n\r\nexport interface Workspace {\r\n root: string\r\n repositories: WorkspaceRepository[]\r\n}\r\n\r\nexport interface WorkspaceTools {\r\n home: string\r\n remoteUrl: (fullName: string) => string\r\n}\r\n\r\nconst FALLBACK_IDENTITY = { name: 'TAMsense', email: 'noreply@tamsense.com' }\r\n\r\n// Копия репозитория в ~/.tamsense/repos общая для всех задач: git с ней работает по очереди\r\nconst cacheLock = keyedLock()\r\n\r\nexport function githubRemoteUrl(fullName: string): string {\r\n return `https://github.com/${fullName}.git`\r\n}\r\n\r\nfunction tamsenseFolder(home: string, ...parts: string[]): string {\r\n return join(home, '.tamsense', ...parts)\r\n}\r\n\r\n// Папка репозитория называется его именем; два репозитория с одинаковым именем получают имя владельца в начале\r\nexport function folderNames(repositories: RunnerRepository[]): string[] {\r\n const shortName = (fullName: string) => fullName.split('/').pop() ?? fullName\r\n const counts = new Map<string, number>()\r\n for (const repository of repositories) {\r\n counts.set(shortName(repository.fullName), (counts.get(shortName(repository.fullName)) ?? 0) + 1)\r\n }\r\n return repositories.map(repository => counts.get(shortName(repository.fullName)) === 1\r\n ? shortName(repository.fullName)\r\n : repository.fullName.replace('/', '__'))\r\n}\r\n\r\nasync function refreshCache(cache: string, repository: RunnerRepository, token: string, tools: WorkspaceTools): Promise<void> {\r\n if (existsSync(join(cache, '.git'))) {\r\n await git(['fetch', '--prune', 'origin'], { cwd: cache, token })\r\n return\r\n }\r\n await git(['clone', '--no-checkout', tools.remoteUrl(repository.fullName), cache], { cwd: tools.home, token })\r\n}\r\n\r\nasync function removeWorktree(cache: string, path: string): Promise<void> {\r\n await runGit(['worktree', 'remove', '--force', path], { cwd: cache })\r\n await rm(path, { recursive: true, force: true })\r\n await git(['worktree', 'prune'], { cwd: cache })\r\n}\r\n\r\n// Доработка продолжает уже отправленную ветку задачи, первый запуск начинает её с основной ветки\r\nasync function baseRef(cache: string, job: RunnerJob, repository: RunnerRepository): Promise<string> {\r\n const pushed = await runGit(['rev-parse', '--verify', '--quiet', `refs/remotes/origin/${job.branch}`], { cwd: cache })\r\n return pushed.code === 0 ? `origin/${job.branch}` : `origin/${repository.defaultBranch}`\r\n}\r\n\r\nexport async function prepareWorkspace(job: RunnerJob, token: string, tools: WorkspaceTools): Promise<Workspace> {\r\n const root = tamsenseFolder(tools.home, 'work', `task-${job.taskId}`)\r\n const folders = folderNames(job.repositories)\r\n const repositories: WorkspaceRepository[] = []\r\n for (const [index, repository] of job.repositories.entries()) {\r\n const cache = tamsenseFolder(tools.home, 'repos', repository.fullName.replace('/', '__'))\r\n const folder = folders[index] ?? repository.fullName\r\n const path = join(root, folder)\r\n const baseSha = await cacheLock.run(repository.fullName, async () => {\r\n await refreshCache(cache, repository, token, tools)\r\n await removeWorktree(cache, path)\r\n await git(['worktree', 'add', '--force', '-B', job.branch, path, await baseRef(cache, job, repository)], { cwd: cache })\r\n return git(['rev-parse', 'HEAD'], { cwd: path })\r\n })\r\n repositories.push({ fullName: repository.fullName, folder, path, baseSha })\r\n }\r\n return { root, repositories }\r\n}\r\n\r\n// Коммиты без автора git не создаёт: если человек его не задал, коммиты подписываются именем TAMsense\r\nexport async function commitIdentityEnv(home: string): Promise<Record<string, string>> {\r\n const configured = await runGit(['config', '--get', 'user.email'], { cwd: home })\r\n if (configured.code === 0 && configured.stdout) {\r\n return {}\r\n }\r\n return {\r\n GIT_AUTHOR_NAME: FALLBACK_IDENTITY.name,\r\n GIT_AUTHOR_EMAIL: FALLBACK_IDENTITY.email,\r\n GIT_COMMITTER_NAME: FALLBACK_IDENTITY.name,\r\n GIT_COMMITTER_EMAIL: FALLBACK_IDENTITY.email,\r\n }\r\n}\r\n\r\n// Незакоммиченное Claude досохраняется одним коммитом; ветка отправляется, только если в ней есть новое\r\nexport async function publishWorkspace(workspace: Workspace, job: RunnerJob, token: string, identity: Record<string, string>): Promise<RunnerPushedBranch[]> {\r\n const pushed: RunnerPushedBranch[] = []\r\n for (const repository of workspace.repositories) {\r\n const options = { cwd: repository.path, env: identity }\r\n const sha = await cacheLock.run(repository.fullName, async () => {\r\n await git(['add', '--all'], options)\r\n const hasStaged = (await runGit(['diff', '--cached', '--quiet'], options)).code === 1\r\n if (hasStaged) {\r\n await git(['commit', '--message', `Task #${job.taskId}: ${job.title}`], options)\r\n }\r\n const newCommits = Number(await git(['rev-list', '--count', `${repository.baseSha}..HEAD`], options))\r\n if (newCommits === 0) {\r\n return null\r\n }\r\n await git(['push', 'origin', `HEAD:refs/heads/${job.branch}`], { ...options, token })\r\n return git(['rev-parse', 'HEAD'], options)\r\n })\r\n if (sha) {\r\n pushed.push({ repository: repository.fullName, branch: job.branch, sha })\r\n }\r\n }\r\n return pushed\r\n}\r\n","import { Buffer } from 'node:buffer'\r\nimport { spawn } from 'node:child_process'\r\n\r\nexport interface GitResult {\r\n code: number\r\n stdout: string\r\n stderr: string\r\n}\r\n\r\nexport interface GitOptions {\r\n cwd: string\r\n // ключ GitHub уходит только этому процессу git через окружение: в настройки репозитория и к Claude он не попадает\r\n token?: string\r\n env?: Record<string, string>\r\n}\r\n\r\nconst GITHUB_ORIGIN = 'https://github.com/'\r\n\r\nexport function githubAuthEnv(token: string): Record<string, string> {\r\n const credentials = Buffer.from(`x-access-token:${token}`).toString('base64')\r\n return {\r\n GIT_CONFIG_COUNT: '1',\r\n GIT_CONFIG_KEY_0: `http.${GITHUB_ORIGIN}.extraheader`,\r\n GIT_CONFIG_VALUE_0: `AUTHORIZATION: basic ${credentials}`,\r\n GIT_TERMINAL_PROMPT: '0',\r\n }\r\n}\r\n\r\nexport function runGit(args: string[], options: GitOptions): Promise<GitResult> {\r\n const env = { ...process.env, ...options.env, ...(options.token ? githubAuthEnv(options.token) : {}) }\r\n return new Promise((resolve, reject) => {\r\n const child = spawn('git', args, { cwd: options.cwd, env, stdio: ['ignore', 'pipe', 'pipe'] })\r\n let stdout = ''\r\n let stderr = ''\r\n child.stdout.on('data', (chunk: Buffer) => {\r\n stdout += chunk.toString()\r\n })\r\n child.stderr.on('data', (chunk: Buffer) => {\r\n stderr += chunk.toString()\r\n })\r\n child.on('error', reject)\r\n child.on('close', code => resolve({ code: code ?? 1, stdout: stdout.trim(), stderr: stderr.trim() }))\r\n })\r\n}\r\n\r\n// Ошибка git с его собственным текстом: человек увидит её в отчёте задачи\r\nexport async function git(args: string[], options: GitOptions): Promise<string> {\r\n const result = await runGit(args, options)\r\n if (result.code !== 0) {\r\n throw new Error(`git ${args[0]} failed: ${result.stderr || result.stdout || `exit code ${result.code}`}`)\r\n }\r\n return result.stdout\r\n}\r\n"],"mappings":"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","names":["existsSync","homedir","process","reply","text","present","query","tool","z","text","tool","HELPER_TOOLS","text","tool","text","applySwitch","work","text","work","text","text","reset","texts","z","tool","query","mkdir","readFile","rm","writeFile","homedir","dirname","join","isRecord","reply","existsSync","join","fileURLToPath","dirname","join","process","process","text","clip","script","join","dirname","process","fileURLToPath","existsSync","join","spawn","delimiter","createInterface","spawn","readFileSync","process","rm","dirname","process","work","readFile","writeFile","basename","dirname","join","createInterface","RRWEB_META","basename","readFile","join","dirname","writeFile","createInterface","recordingsDir","readFile","stat","basename","extname","isAbsolute","isAbsolute","extname","stat","readFile","basename","existsSync","rm","join","spawn","join","existsSync","rm","print","reply","specWorkTools","process","dirname","finish","rm","run","process","existsSync","homedir"]}
|