@quiel/cli 0.5.3 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -0
- package/dist/cli.js +460 -53
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -15,6 +15,7 @@ This package is the part that runs **on your machine**.
|
|
|
15
15
|
- **Gives your agent the platform's tools over MCP** — take the next task, read project context and documents, read the files a human attached to the task, ask a human (and ask them to attach a file), request approval for a dangerous action, submit work.
|
|
16
16
|
- **Installs hooks into your agent** so a shell command is checked against the project's trust profile *before* it runs. A command that matches a stop pattern is refused; a command that needs a human gets one.
|
|
17
17
|
- **Keeps the connection and the task lease alive**, so the platform knows the task is being worked on and not abandoned.
|
|
18
|
+
- **Lets the agent idle, and wakes it when work appears.** An empty queue is a normal state, not a failure: the agent stops its turn instead of asking for a task over and over. A background watcher notices when something is queued for it and wakes the session. Waiting costs a socket and a few bytes — not model turns, not context.
|
|
18
19
|
- **Counts the tokens each task costs** and reports the totals, so a project can be looked at afterwards.
|
|
19
20
|
- **Rescues unfinished work when your subscription limit runs out.** It commits what the agent managed and pushes it to the task's own branch, marked as interrupted, so the next person or agent continues from there instead of starting over. It never pushes to your main branch.
|
|
20
21
|
|
|
@@ -106,6 +107,8 @@ Everything goes into your project directory, so it is visible in `git status` an
|
|
|
106
107
|
|
|
107
108
|
Plus one line in `.gitignore` for the directory the agent works in.
|
|
108
109
|
|
|
110
|
+
For Claude Code, `Stop` gets **two** commands, and they are not duplicates: one decides whether the agent may stop, the other waits for work while it is stopped and wakes the session when something appears. The second is declared with `asyncRewake`, so it runs in the background and never blocks you.
|
|
111
|
+
|
|
109
112
|
**Your token goes into none of them.** It is stored separately in `~/.quiel/credentials.json` with `0600` permissions, outside the repository. `.quiel.json` — the file that *is* committed — holds only the platform address, the project key, the agent id, its roles and which agent program you chose.
|
|
110
113
|
|
|
111
114
|
Existing configuration is merged, not overwritten: other MCP servers, other hooks and your own comments survive. Running `init` again is the normal way to reconnect an agent, and it does not duplicate anything.
|
|
@@ -118,6 +121,7 @@ Existing configuration is merged, not overwritten: other MCP servers, other hook
|
|
|
118
121
|
| `quiel connect` | Hold the connection to the platform |
|
|
119
122
|
| `quiel status [--check]` | Show the project, the configured agent program, mode and current task |
|
|
120
123
|
| `quiel mode auto\|manual` | Switch between autonomous and manual |
|
|
124
|
+
| `quiel wait-for-task` | Wait for a task to appear and wake the sleeping session (started by a hook, not by you) |
|
|
121
125
|
| `quiel mcp` | Run the MCP server over stdio (your agent starts this itself) |
|
|
122
126
|
| `quiel hook <name> [--format …]` | Run a hook (your agent's configuration calls this) |
|
|
123
127
|
|
package/dist/cli.js
CHANGED
|
@@ -372,35 +372,7 @@ var attachmentSchema = z7.object({
|
|
|
372
372
|
|
|
373
373
|
// ../shared/src/dto/auth.ts
|
|
374
374
|
import { z as z8 } from "zod";
|
|
375
|
-
var PASSWORD_MIN_LENGTH = 10;
|
|
376
|
-
var PASSWORD_MAX_LENGTH = 128;
|
|
377
|
-
var PasswordSchema = z8.string().min(PASSWORD_MIN_LENGTH, `\u041F\u0430\u0440\u043E\u043B\u044C \u043A\u043E\u0440\u043E\u0447\u0435 ${PASSWORD_MIN_LENGTH} \u0441\u0438\u043C\u0432\u043E\u043B\u043E\u0432`).max(PASSWORD_MAX_LENGTH, `\u041F\u0430\u0440\u043E\u043B\u044C \u0434\u043B\u0438\u043D\u043D\u0435\u0435 ${PASSWORD_MAX_LENGTH} \u0441\u0438\u043C\u0432\u043E\u043B\u043E\u0432`);
|
|
378
375
|
var EmailSchema = z8.string().trim().min(1, "\u0423\u043A\u0430\u0436\u0438\u0442\u0435 \u043F\u043E\u0447\u0442\u0443").email("\u041F\u043E\u0445\u043E\u0436\u0435, \u0432 \u0430\u0434\u0440\u0435\u0441\u0435 \u043E\u043F\u0435\u0447\u0430\u0442\u043A\u0430").toLowerCase();
|
|
379
|
-
var RegisterSchema = z8.object({
|
|
380
|
-
name: z8.string().trim().min(1, "\u0423\u043A\u0430\u0436\u0438\u0442\u0435 \u0438\u043C\u044F").max(120),
|
|
381
|
-
email: EmailSchema,
|
|
382
|
-
password: PasswordSchema
|
|
383
|
-
}).refine(
|
|
384
|
-
(value) => {
|
|
385
|
-
const localPart = value.email.split("@")[0] ?? "";
|
|
386
|
-
return localPart.length < 3 || !value.password.toLowerCase().includes(localPart);
|
|
387
|
-
},
|
|
388
|
-
{ message: "\u041F\u0430\u0440\u043E\u043B\u044C \u043D\u0435 \u0434\u043E\u043B\u0436\u0435\u043D \u043F\u043E\u0432\u0442\u043E\u0440\u044F\u0442\u044C \u0430\u0434\u0440\u0435\u0441 \u043F\u043E\u0447\u0442\u044B", path: ["password"] }
|
|
389
|
-
);
|
|
390
|
-
var LoginSchema = z8.object({
|
|
391
|
-
email: EmailSchema,
|
|
392
|
-
// На входе НЕ применяем правила длины: пароль мог быть задан раньше, по
|
|
393
|
-
// другим правилам, и «слишком короткий» на форме входа — это отказ в
|
|
394
|
-
// доступе законному владельцу, а заодно подсказка перебирающему, что
|
|
395
|
-
// проверять длину не нужно.
|
|
396
|
-
password: z8.string().min(1, "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u043F\u0430\u0440\u043E\u043B\u044C")
|
|
397
|
-
});
|
|
398
|
-
var ForgotPasswordSchema = z8.object({ email: EmailSchema });
|
|
399
|
-
var ResetPasswordSchema = z8.object({
|
|
400
|
-
token: z8.string().min(1),
|
|
401
|
-
password: PasswordSchema
|
|
402
|
-
});
|
|
403
|
-
var ResendVerificationSchema = z8.object({ email: EmailSchema });
|
|
404
376
|
|
|
405
377
|
// ../shared/src/dto/milestone.ts
|
|
406
378
|
import { z as z9 } from "zod";
|
|
@@ -499,14 +471,8 @@ var ProfileNameSchema = z11.string().trim().min(1, "\u0423\u043A\u0430\u0436\u04
|
|
|
499
471
|
var UpdateProfileSchema = z11.object({
|
|
500
472
|
name: ProfileNameSchema
|
|
501
473
|
});
|
|
502
|
-
var ChangePasswordSchema = z11.object({
|
|
503
|
-
currentPassword: z11.string().min(1).max(200).optional(),
|
|
504
|
-
newPassword: PasswordSchema
|
|
505
|
-
});
|
|
506
474
|
var RequestEmailChangeSchema = z11.object({
|
|
507
|
-
newEmail: EmailSchema
|
|
508
|
-
/** Нужен, если у аккаунта есть пароль — см. `ChangePasswordSchema`. */
|
|
509
|
-
currentPassword: z11.string().min(1).max(200).optional()
|
|
475
|
+
newEmail: EmailSchema
|
|
510
476
|
});
|
|
511
477
|
var ConfirmEmailChangeSchema = z11.object({
|
|
512
478
|
token: z11.string().min(1)
|
|
@@ -815,6 +781,26 @@ var ProjectSettingsSchema = z14.object({
|
|
|
815
781
|
* Пусто — задача заводится без роли и видна всей команде проекта.
|
|
816
782
|
*/
|
|
817
783
|
reviewRoleKey: z14.string().trim().min(1).max(60).optional(),
|
|
784
|
+
/**
|
|
785
|
+
* Канал Slack, куда уходят события проекта (SL-04, ADR-007).
|
|
786
|
+
*
|
|
787
|
+
* КАНАЛ НА ПРОЕКТ, А НЕ НА ОРГАНИЗАЦИЮ. Установка одна на компанию —
|
|
788
|
+
* так устроен Slack, — но лента у каждого проекта своя: в организации
|
|
789
|
+
* с пятью проектами общий канал превращается в шум, который выключают
|
|
790
|
+
* через неделю.
|
|
791
|
+
*
|
|
792
|
+
* ПУСТО — ПОДДЕРЖИВАЕМЫЙ РЕЖИМ, А НЕ ПОЛОМКА. Проект без канала
|
|
793
|
+
* работает на личных сообщениях: подтверждения и вопросы всё равно
|
|
794
|
+
* приходят названному человеку в личку. Канал добавляет ОБЩУЮ
|
|
795
|
+
* видимость, а не доставку.
|
|
796
|
+
*/
|
|
797
|
+
slackChannelId: z14.string().trim().min(1).max(40).optional(),
|
|
798
|
+
/**
|
|
799
|
+
* Имя канала на момент выбора — только чтобы показать человеку
|
|
800
|
+
* «#релизы», а не `C09ABCDEF`. Slack допускает переименование, поэтому
|
|
801
|
+
* истина — идентификатор, а это подпись к нему.
|
|
802
|
+
*/
|
|
803
|
+
slackChannelName: z14.string().trim().min(1).max(120).optional(),
|
|
818
804
|
maxParallelTasksPerAgent: z14.number().int().positive().default(1),
|
|
819
805
|
leaseTtlSec: z14.number().int().positive().default(120),
|
|
820
806
|
taskTimeoutMin: z14.number().int().positive().default(90),
|
|
@@ -1568,7 +1554,17 @@ var taskCreatePayloadSchema = z23.object({
|
|
|
1568
1554
|
executor: z23.enum(executorValues2),
|
|
1569
1555
|
blocksCurrent: z23.boolean(),
|
|
1570
1556
|
acceptance: z23.string().optional(),
|
|
1571
|
-
labels: z23.array(z23.string().min(1)).optional()
|
|
1557
|
+
labels: z23.array(z23.string().min(1)).optional(),
|
|
1558
|
+
/**
|
|
1559
|
+
* Задачи, от которых новая зависит, — связи ставятся сразу.
|
|
1560
|
+
*
|
|
1561
|
+
* Без этого поля менеджеру, разложившему спецификацию на двадцать
|
|
1562
|
+
* пять задач, приходилось идти по списку вторым проходом. Связь,
|
|
1563
|
+
* замыкающая кольцо, молча пропускается: задача уже создана, и ронять
|
|
1564
|
+
* создание из-за одной плохой связи значило бы терять работу целиком.
|
|
1565
|
+
* Ответ честно говорит, сколько связей поставлено.
|
|
1566
|
+
*/
|
|
1567
|
+
dependsOn: z23.array(z23.string().min(1)).max(50).optional()
|
|
1572
1568
|
}).refine((v) => Boolean(v.roleKey) !== Boolean(v.assigneeId), {
|
|
1573
1569
|
message: "\u041D\u0443\u0436\u0435\u043D \u0440\u043E\u0432\u043D\u043E \u043E\u0434\u0438\u043D \u0430\u0434\u0440\u0435\u0441\u0430\u0442: roleKey \u0418\u041B\u0418 assigneeId"
|
|
1574
1570
|
});
|
|
@@ -1587,7 +1583,16 @@ var taskBriefSchema = z23.object({
|
|
|
1587
1583
|
var taskCreateAckSchema = z23.object({
|
|
1588
1584
|
task: taskBriefSchema,
|
|
1589
1585
|
/** Во что ушла ТЕКУЩАЯ задача автора, если `blocksCurrent: true`. */
|
|
1590
|
-
currentTaskStatus: z23.enum(taskStatusValues3).optional()
|
|
1586
|
+
currentTaskStatus: z23.enum(taskStatusValues3).optional(),
|
|
1587
|
+
/**
|
|
1588
|
+
* Сколько связей из `dependsOn` поставлено.
|
|
1589
|
+
*
|
|
1590
|
+
* Может быть меньше запрошенного: связь, замыкающая кольцо, или
|
|
1591
|
+
* задача из чужого проекта пропускаются молча — задача уже создана, и
|
|
1592
|
+
* ронять создание из-за плохой связи значило бы терять работу.
|
|
1593
|
+
* Поэтому число, а не «ок»: по нему видно, что не всё прошло.
|
|
1594
|
+
*/
|
|
1595
|
+
dependenciesLinked: z23.number().int().nonnegative().optional()
|
|
1591
1596
|
});
|
|
1592
1597
|
var taskListPayloadSchema = z23.object({
|
|
1593
1598
|
status: z23.array(z23.enum(taskStatusValues3)).optional(),
|
|
@@ -1599,6 +1604,57 @@ var taskListPayloadSchema = z23.object({
|
|
|
1599
1604
|
var taskListAckSchema = z23.object({
|
|
1600
1605
|
tasks: z23.array(taskBriefSchema)
|
|
1601
1606
|
});
|
|
1607
|
+
var taskPlanPayloadSchema = z23.object({
|
|
1608
|
+
taskId: z23.string().min(1),
|
|
1609
|
+
patch: z23.object({
|
|
1610
|
+
title: z23.string().min(1).max(300).optional(),
|
|
1611
|
+
descriptionMd: z23.string().max(1e5).optional(),
|
|
1612
|
+
acceptance: z23.string().max(2e4).nullable().optional(),
|
|
1613
|
+
type: z23.enum(taskTypeValues2).optional(),
|
|
1614
|
+
priority: z23.enum(priorityValues2).optional(),
|
|
1615
|
+
roleKey: z23.string().min(1).max(60).nullable().optional(),
|
|
1616
|
+
assigneeId: z23.string().min(1).nullable().optional(),
|
|
1617
|
+
milestoneId: z23.string().min(1).nullable().optional(),
|
|
1618
|
+
labels: z23.array(z23.string().min(1)).max(30).optional(),
|
|
1619
|
+
estimateMin: z23.number().int().positive().nullable().optional()
|
|
1620
|
+
}).refine((patch) => Object.keys(patch).length > 0, {
|
|
1621
|
+
message: "\u041D\u0443\u0436\u043D\u043E \u043F\u0435\u0440\u0435\u0434\u0430\u0442\u044C \u0445\u043E\u0442\u044F \u0431\u044B \u043E\u0434\u043D\u043E \u043F\u043E\u043B\u0435"
|
|
1622
|
+
})
|
|
1623
|
+
});
|
|
1624
|
+
var taskPlanAckSchema = z23.object({
|
|
1625
|
+
task: taskBriefSchema
|
|
1626
|
+
});
|
|
1627
|
+
var taskLinkPayloadSchema = z23.object({
|
|
1628
|
+
/** Задача, которая блокирует (её делают первой). */
|
|
1629
|
+
blockerId: z23.string().min(1),
|
|
1630
|
+
/** Задача, которая ждёт (§7.3: `fromId` — «эта задача зависит от…»). */
|
|
1631
|
+
blockedId: z23.string().min(1),
|
|
1632
|
+
/** `BLOCKS` держит очередь, `RELATES` — просто связь «см. также». */
|
|
1633
|
+
kind: z23.enum(["BLOCKS", "RELATES"]).default("BLOCKS")
|
|
1634
|
+
});
|
|
1635
|
+
var taskLinkAckSchema = z23.object({
|
|
1636
|
+
linked: z23.boolean(),
|
|
1637
|
+
/** Задача-ждущая ушла в BLOCKED прямо сейчас (была IN_PROGRESS). */
|
|
1638
|
+
blockedNow: z23.boolean()
|
|
1639
|
+
});
|
|
1640
|
+
var taskUnlinkPayloadSchema = z23.object({
|
|
1641
|
+
blockerId: z23.string().min(1),
|
|
1642
|
+
blockedId: z23.string().min(1)
|
|
1643
|
+
});
|
|
1644
|
+
var taskUnlinkAckSchema = z23.object({
|
|
1645
|
+
/** `false` — такой связи и не было; это не ошибка, а отчёт. */
|
|
1646
|
+
unlinked: z23.boolean()
|
|
1647
|
+
});
|
|
1648
|
+
var taskPeekPayloadSchema = z23.object({});
|
|
1649
|
+
var taskPeekAckSchema = z23.object({
|
|
1650
|
+
/**
|
|
1651
|
+
* Нашлась задача, которую диспетчер выдал бы прямо сейчас.
|
|
1652
|
+
*
|
|
1653
|
+
* Подсказка, а не обещание: пока агент просыпается, задачу может взять
|
|
1654
|
+
* другой. Цена ошибки — один пустой `next_task`.
|
|
1655
|
+
*/
|
|
1656
|
+
available: z23.boolean()
|
|
1657
|
+
});
|
|
1602
1658
|
var taskUpdatePayloadSchema = z23.object({
|
|
1603
1659
|
taskId: z23.string().min(1),
|
|
1604
1660
|
patch: z23.object({
|
|
@@ -1880,12 +1936,28 @@ var ImportTasksRequestSchema = z26.object({
|
|
|
1880
1936
|
var IntegrationStatus = {
|
|
1881
1937
|
/** Работает и проверена вживую. */
|
|
1882
1938
|
READY: "ready",
|
|
1939
|
+
/**
|
|
1940
|
+
* Сделана целиком, часть пути пройдена вживую, но не весь.
|
|
1941
|
+
*
|
|
1942
|
+
* Заведено для Slack 23 сентября 2026. «Скоро» про него стало враньём в
|
|
1943
|
+
* тот день, когда владелец установил приложение, привязал себя командой
|
|
1944
|
+
* `/quiel link` и выбрал каналы двум проектам — всё это прошло на
|
|
1945
|
+
* настоящем рабочем пространстве. «Работает и проверена вживую» тоже
|
|
1946
|
+
* неправда: ни одно сообщение в канал ещё не наблюдалось, потому что в
|
|
1947
|
+
* проектах не было событий.
|
|
1948
|
+
*
|
|
1949
|
+
* Тот же приём, что `support: 'documented'` у видов агентских программ
|
|
1950
|
+
* (`agents/kinds.ts`): пометка снимается после живого прогона, а не
|
|
1951
|
+
* после того, как код написан.
|
|
1952
|
+
*/
|
|
1953
|
+
BETA: "beta",
|
|
1883
1954
|
/** Запланирована, но ещё не сделана. Показываем, чтобы не обещать голосом. */
|
|
1884
1955
|
PLANNED: "planned"
|
|
1885
1956
|
};
|
|
1886
1957
|
var IntegrationKey = {
|
|
1887
1958
|
GITHUB: "github",
|
|
1888
1959
|
TELEGRAM: "telegram",
|
|
1960
|
+
SLACK: "slack",
|
|
1889
1961
|
WEBHOOKS: "webhooks"
|
|
1890
1962
|
};
|
|
1891
1963
|
var INTEGRATIONS = [
|
|
@@ -1903,6 +1975,22 @@ var INTEGRATIONS = [
|
|
|
1903
1975
|
scope: "both",
|
|
1904
1976
|
requiresOrgAdmin: false
|
|
1905
1977
|
},
|
|
1978
|
+
{
|
|
1979
|
+
// ПЕРВАЯ ОБЩАЯ поверхность продукта: канал проекта, где команда
|
|
1980
|
+
// видит работу агентов, не открывая дашборд (ADR-007). Остальные
|
|
1981
|
+
// каналы уведомлений личные.
|
|
1982
|
+
//
|
|
1983
|
+
// Статус `beta`, а не `planned` и не `ready`. Установка, привязка
|
|
1984
|
+
// человека командой `/quiel link` и выбор канала проекту прошли на
|
|
1985
|
+
// настоящем рабочем пространстве 23 сентября 2026 — «скоро» про это
|
|
1986
|
+
// было бы враньём. Но ни одно сообщение в канал ещё не наблюдалось:
|
|
1987
|
+
// в проектах не было событий. Пометка снимается после первого
|
|
1988
|
+
// дошедшего сообщения, а не после того, как код написан.
|
|
1989
|
+
key: IntegrationKey.SLACK,
|
|
1990
|
+
status: IntegrationStatus.BETA,
|
|
1991
|
+
scope: "org",
|
|
1992
|
+
requiresOrgAdmin: true
|
|
1993
|
+
},
|
|
1906
1994
|
{
|
|
1907
1995
|
// Одна механика вместо десяти интеграций: Zapier, n8n, Make и любой
|
|
1908
1996
|
// свой эндпоинт. Пока не сделана — и список об этом честно говорит.
|
|
@@ -1919,6 +2007,15 @@ var BY_KEY = new Map(INTEGRATIONS.map((info) => [info.key, info]));
|
|
|
1919
2007
|
var NotificationChannel = {
|
|
1920
2008
|
INAPP: "inapp",
|
|
1921
2009
|
TELEGRAM: "telegram",
|
|
2010
|
+
/**
|
|
2011
|
+
* Личное сообщение в Slack (SL-06).
|
|
2012
|
+
*
|
|
2013
|
+
* ИМЕННО ЛИЧНОЕ, а не канал проекта. Канал — общая видимость и живёт
|
|
2014
|
+
* отдельно (`slack-channel` в воркере): он не адресован никому и не
|
|
2015
|
+
* зависит от настроек человека. Смешай мы их в одном канале доставки —
|
|
2016
|
+
* выключив «Slack», человек выключил бы и ленту всей команды.
|
|
2017
|
+
*/
|
|
2018
|
+
SLACK: "slack",
|
|
1922
2019
|
EMAIL: "email",
|
|
1923
2020
|
PUSH: "push"
|
|
1924
2021
|
};
|
|
@@ -1932,18 +2029,21 @@ var NotificationUrgency = {
|
|
|
1932
2029
|
var ALL = [
|
|
1933
2030
|
NotificationChannel.INAPP,
|
|
1934
2031
|
NotificationChannel.TELEGRAM,
|
|
2032
|
+
NotificationChannel.SLACK,
|
|
1935
2033
|
NotificationChannel.EMAIL,
|
|
1936
2034
|
NotificationChannel.PUSH
|
|
1937
2035
|
];
|
|
1938
2036
|
var FAST = [
|
|
1939
2037
|
NotificationChannel.INAPP,
|
|
1940
2038
|
NotificationChannel.TELEGRAM,
|
|
2039
|
+
NotificationChannel.SLACK,
|
|
1941
2040
|
NotificationChannel.PUSH
|
|
1942
2041
|
];
|
|
1943
2042
|
var QUIET = [NotificationChannel.INAPP];
|
|
1944
2043
|
var SLOW = [
|
|
1945
2044
|
NotificationChannel.INAPP,
|
|
1946
2045
|
NotificationChannel.TELEGRAM,
|
|
2046
|
+
NotificationChannel.SLACK,
|
|
1947
2047
|
NotificationChannel.EMAIL
|
|
1948
2048
|
];
|
|
1949
2049
|
function defaultsFor(urgency) {
|
|
@@ -2007,8 +2107,20 @@ var NOTIFICATION_KINDS = [
|
|
|
2007
2107
|
kind(
|
|
2008
2108
|
"agent_session_limit",
|
|
2009
2109
|
"\u0421\u0435\u0441\u0441\u0438\u044F \u0430\u0433\u0435\u043D\u0442\u0430 \u0438\u0441\u0447\u0435\u0440\u043F\u0430\u043D\u0430",
|
|
2010
|
-
"\u0410\u0433\u0435\u043D\u0442 \u0432\u044B\u043F\u043E\u043B\u043D\u0438\u043B \u043F\u043E\u0434\u0440\u044F\u0434 \u0441\u0442\u043E\u043B\u044C\u043A\u043E \u0437\u0430\u0434\u0430\u0447, \u0441\u043A\u043E\u043B\u044C\u043A\u043E \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043D\u043E \u0434\u043E \u043E\u0447\u0438\u0441\u0442\u043A\u0438 \u043A\u043E\u043D\u0442\u0435\u043A\u0441\u0442\u0430.",
|
|
2011
|
-
|
|
2110
|
+
"\u0410\u0433\u0435\u043D\u0442 \u0432\u044B\u043F\u043E\u043B\u043D\u0438\u043B \u043F\u043E\u0434\u0440\u044F\u0434 \u0441\u0442\u043E\u043B\u044C\u043A\u043E \u0437\u0430\u0434\u0430\u0447, \u0441\u043A\u043E\u043B\u044C\u043A\u043E \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043D\u043E \u0434\u043E \u043E\u0447\u0438\u0441\u0442\u043A\u0438 \u043A\u043E\u043D\u0442\u0435\u043A\u0441\u0442\u0430, \u0438 \u043E\u0441\u0442\u0430\u043D\u043E\u0432\u0438\u043B\u0441\u044F.",
|
|
2111
|
+
// ACTIONABLE, А НЕ INFORMATIONAL, И РАЗНИЦА НЕ В СЛОВЕ.
|
|
2112
|
+
//
|
|
2113
|
+
// У informational push, почта и Telegram выключены по умолчанию —
|
|
2114
|
+
// уведомление оставалось в колокольчике. 24 сентября 2026 владелец
|
|
2115
|
+
// обнаружил, что агент простоял ночь: он остановился по лимиту и
|
|
2116
|
+
// ждал `/clear`, а узнать об этом было неоткуда. Проверено в базе:
|
|
2117
|
+
// у трёх таких записей стояло `{push: false, email: false,
|
|
2118
|
+
// telegram: false}`.
|
|
2119
|
+
//
|
|
2120
|
+
// Уведомление, которое ТРЕБУЕТ ДЕЙСТВИЯ человека, и есть
|
|
2121
|
+
// определение actionable: без `/clear` очередь стоит, даже если
|
|
2122
|
+
// режим проекта автономный.
|
|
2123
|
+
NotificationUrgency.ACTIONABLE
|
|
2012
2124
|
),
|
|
2013
2125
|
kind(
|
|
2014
2126
|
"task_auto_accepted",
|
|
@@ -2030,6 +2142,7 @@ import { z as z27 } from "zod";
|
|
|
2030
2142
|
var NotificationChannelFlagsSchema = z27.object({
|
|
2031
2143
|
inapp: z27.boolean().optional(),
|
|
2032
2144
|
telegram: z27.boolean().optional(),
|
|
2145
|
+
slack: z27.boolean().optional(),
|
|
2033
2146
|
email: z27.boolean().optional(),
|
|
2034
2147
|
push: z27.boolean().optional()
|
|
2035
2148
|
}).partial();
|
|
@@ -3306,7 +3419,8 @@ import { z as z29 } from "zod";
|
|
|
3306
3419
|
var HookCommandEntrySchema = z29.object({
|
|
3307
3420
|
type: z29.literal("command"),
|
|
3308
3421
|
command: z29.string().min(1),
|
|
3309
|
-
timeout: z29.number().int().positive().optional()
|
|
3422
|
+
timeout: z29.number().int().positive().optional(),
|
|
3423
|
+
asyncRewake: z29.boolean().optional()
|
|
3310
3424
|
}).passthrough();
|
|
3311
3425
|
var HookMatcherGroupSchema = z29.object({
|
|
3312
3426
|
matcher: z29.string().optional(),
|
|
@@ -3326,6 +3440,14 @@ var CLI_BIN = "quiel";
|
|
|
3326
3440
|
var HOOK_SPECS = [
|
|
3327
3441
|
{ event: "SessionStart", command: `${CLI_BIN} hook session-start` },
|
|
3328
3442
|
{ event: "Stop", command: `${CLI_BIN} hook stop`, timeout: 10 },
|
|
3443
|
+
// ВТОРАЯ КОМАНДА НА ТОМ ЖЕ СОБЫТИИ, И ОНА НЕ ДУБЛЬ. `hook stop` решает,
|
|
3444
|
+
// можно ли остановиться; эта — что будет, ПОКА агент стоит. Без неё
|
|
3445
|
+
// разрешённый простой означал бы молчание до прихода человека.
|
|
3446
|
+
//
|
|
3447
|
+
// Выходит мгновенно с кодом 0, если режим ручной, задача на руках или
|
|
3448
|
+
// другой ожидатель уже работает, — поэтому висит она только тогда,
|
|
3449
|
+
// когда агенту действительно нечего делать.
|
|
3450
|
+
{ event: "Stop", command: `${CLI_BIN} wait-for-task`, asyncRewake: true },
|
|
3329
3451
|
{
|
|
3330
3452
|
event: "StopFailure",
|
|
3331
3453
|
matcher: "rate_limit|overloaded",
|
|
@@ -3348,7 +3470,8 @@ function upsertHookSpec(groups, spec) {
|
|
|
3348
3470
|
const commandEntry = {
|
|
3349
3471
|
type: "command",
|
|
3350
3472
|
command: spec.command,
|
|
3351
|
-
...spec.timeout !== void 0 ? { timeout: spec.timeout } : {}
|
|
3473
|
+
...spec.timeout !== void 0 ? { timeout: spec.timeout } : {},
|
|
3474
|
+
...spec.asyncRewake !== void 0 ? { asyncRewake: spec.asyncRewake } : {}
|
|
3352
3475
|
};
|
|
3353
3476
|
const groupIndex = groups.findIndex((g) => sameMatcher(g.matcher, spec.matcher));
|
|
3354
3477
|
if (groupIndex === -1) {
|
|
@@ -3515,6 +3638,30 @@ var ClientStateSchema = z30.object({
|
|
|
3515
3638
|
tasksCompletedInSession: z30.number().int().nonnegative().default(0),
|
|
3516
3639
|
/** Кэш `ProjectSettings.contextResetAfterTasks` (§7.2 ТЗ) — сколько задач подряд можно сдать, прежде чем `Stop`-хук попросит `/clear`. */
|
|
3517
3640
|
contextResetAfterTasks: z30.number().int().positive().default(DEFAULT_CONTEXT_RESET_AFTER_TASKS),
|
|
3641
|
+
/**
|
|
3642
|
+
* Идентификаторы задач, сданных в этой сессии, — а не одно число.
|
|
3643
|
+
*
|
|
3644
|
+
* Счётчик `tasksCompletedInSession` увеличивается на КАЖДУЮ успешную
|
|
3645
|
+
* сдачу, и задача, возвращённая с ревью и сданная повторно, считалась
|
|
3646
|
+
* второй. Владелец увидел «Сессия выполнила 9 задач» при лимите 8 и
|
|
3647
|
+
* справедливо решил, что счётчик сломан: он не сломан, он считает
|
|
3648
|
+
* сдачи, а сообщение называет их задачами.
|
|
3649
|
+
*
|
|
3650
|
+
* Храним ключи, чтобы говорить правду в обоих числах.
|
|
3651
|
+
*/
|
|
3652
|
+
completedTaskIds: z30.array(z30.string()).default([]),
|
|
3653
|
+
/**
|
|
3654
|
+
* Когда `next_task` в последний раз ответил «задач нет».
|
|
3655
|
+
*
|
|
3656
|
+
* БЕЗ ЭТОГО ПОЛЯ `Stop`-ХУК НЕ ОТЛИЧАЕТ ПУСТУЮ ОЧЕРЕДЬ ОТ ЛЕНИ. Он
|
|
3657
|
+
* безусловно требовал «вызови next_task», и при пустой очереди агент
|
|
3658
|
+
* крутил этот цикл часами: в боевом проекте очередь пустовала по
|
|
3659
|
+
* 15–25 минут подряд, и каждый круг тратил ход модели впустую.
|
|
3660
|
+
*
|
|
3661
|
+
* `null` — очередь не была пуста с начала сессии либо последняя
|
|
3662
|
+
* попытка что-то принесла.
|
|
3663
|
+
*/
|
|
3664
|
+
queueEmptyAt: z30.string().nullable().default(null),
|
|
3518
3665
|
/** Кэш стоп-паттернов/правил доверия для `PreToolUse` фазы 4. */
|
|
3519
3666
|
trust: TrustCacheSchema.default(null),
|
|
3520
3667
|
/** Снимок задачи на момент последнего `PreCompact` — пишет src/hooks/pre-compact.ts, читает SessionStart(source="compact"), чтобы напомнить модели, чем она занималась. */
|
|
@@ -5790,7 +5937,7 @@ ${question.answerMd ?? ""}${describeAnswerAttachments(question)}`;
|
|
|
5790
5937
|
// package.json
|
|
5791
5938
|
var package_default = {
|
|
5792
5939
|
name: "@quiel/cli",
|
|
5793
|
-
version: "0.
|
|
5940
|
+
version: "0.6.0",
|
|
5794
5941
|
description: "CLI \u0438 MCP-\u0441\u0435\u0440\u0432\u0435\u0440 Quiel: \u043F\u043E\u0434\u043A\u043B\u044E\u0447\u0430\u0435\u0442 \u0430\u0433\u0435\u043D\u0442\u0430 \u0443\u0447\u0430\u0441\u0442\u043D\u0438\u043A\u0430 (Claude Code, Codex, Cursor, OpenCode) \u043A \u043F\u043B\u0430\u0442\u0444\u043E\u0440\u043C\u0435",
|
|
5795
5942
|
homepage: "https://quiel.app",
|
|
5796
5943
|
repository: {
|
|
@@ -5976,7 +6123,7 @@ var nextTaskInputShape = {
|
|
|
5976
6123
|
"\u0421\u043A\u043E\u043B\u044C\u043A\u043E \u0441\u0435\u043A\u0443\u043D\u0434 \u0436\u0434\u0430\u0442\u044C \u043F\u043E\u044F\u0432\u043B\u0435\u043D\u0438\u044F \u0437\u0430\u0434\u0430\u0447\u0438 \u0432 \u043E\u0447\u0435\u0440\u0435\u0434\u0438, 0..600 (\xA711.1 \u0422\u0417). \u041F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E 300 \u2014 \u043A\u0430\u043A claimTimeoutSec \u0434\u0438\u0441\u043F\u0435\u0442\u0447\u0435\u0440\u0430 (\xA79.1 \u0422\u0417)."
|
|
5977
6124
|
)
|
|
5978
6125
|
};
|
|
5979
|
-
var DEFAULT_WAIT_SEC =
|
|
6126
|
+
var DEFAULT_WAIT_SEC = 90;
|
|
5980
6127
|
var CLAIM_ACK_BUFFER_MS = 15e3;
|
|
5981
6128
|
async function nextTaskHandler(args, runtime) {
|
|
5982
6129
|
const { client, stateFilePath } = runtime;
|
|
@@ -6002,8 +6149,9 @@ async function nextTaskHandler(args, runtime) {
|
|
|
6002
6149
|
);
|
|
6003
6150
|
if (!claimed.ok) return errorResult(claimed.message);
|
|
6004
6151
|
if ("empty" in claimed.data) {
|
|
6152
|
+
updateState({ queueEmptyAt: (/* @__PURE__ */ new Date()).toISOString() }, stateFilePath);
|
|
6005
6153
|
return textResult(
|
|
6006
|
-
`\u0417\u0430\u0434\u0430\u0447 \u0432 \u043E\u0447\u0435\u0440\u0435\u0434\u0438 \u0441\u0435\u0439\u0447\u0430\u0441 \u043D\u0435\u0442 (\u0436\u0434\u0430\u043B ${waitSec} \u0441). \u042D\u0442\u043E \u043D\u043E\u0440\u043C\u0430\u043B\u044C\u043D\u044B\u0439 \u0438\u0441\u0445\u043E\u0434, \u043D\u0435 \u043E\u0448\u0438\u0431\u043A\u0430 \
|
|
6154
|
+
`\u0417\u0430\u0434\u0430\u0447 \u0432 \u043E\u0447\u0435\u0440\u0435\u0434\u0438 \u0441\u0435\u0439\u0447\u0430\u0441 \u043D\u0435\u0442 (\u0436\u0434\u0430\u043B ${waitSec} \u0441). \u042D\u0442\u043E \u043D\u043E\u0440\u043C\u0430\u043B\u044C\u043D\u044B\u0439 \u0438\u0441\u0445\u043E\u0434, \u043D\u0435 \u043E\u0448\u0438\u0431\u043A\u0430. \u041C\u043E\u0436\u0435\u0448\u044C \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u0442\u044C \u0445\u043E\u0434 \u2014 \u0445\u0443\u043A \u0442\u0435\u0431\u044F \u043D\u0435 \u043E\u0441\u0442\u0430\u043D\u043E\u0432\u0438\u0442, \u043F\u043E\u043A\u0430 \u043E\u0447\u0435\u0440\u0435\u0434\u044C \u043F\u0443\u0441\u0442\u0430.`
|
|
6007
6155
|
);
|
|
6008
6156
|
}
|
|
6009
6157
|
const task = claimed.data;
|
|
@@ -6016,7 +6164,10 @@ async function nextTaskHandler(args, runtime) {
|
|
|
6016
6164
|
gitMode: task.project.gitMode,
|
|
6017
6165
|
branchName: task.project.branchName,
|
|
6018
6166
|
commitPrefix: task.project.commitPrefix
|
|
6019
|
-
}
|
|
6167
|
+
},
|
|
6168
|
+
// Очередь оказалась не пуста — пометка снимается, иначе хук
|
|
6169
|
+
// продолжал бы считать, что работы нет.
|
|
6170
|
+
queueEmptyAt: null
|
|
6020
6171
|
},
|
|
6021
6172
|
stateFilePath
|
|
6022
6173
|
);
|
|
@@ -6074,6 +6225,76 @@ ${worktreeNote}${continueNote}${warning}`
|
|
|
6074
6225
|
);
|
|
6075
6226
|
}
|
|
6076
6227
|
|
|
6228
|
+
// src/mcp/link.ts
|
|
6229
|
+
var linkTasksInputShape = {
|
|
6230
|
+
blockerId: taskLinkPayloadSchema.shape.blockerId.describe(
|
|
6231
|
+
"ID \u0437\u0430\u0434\u0430\u0447\u0438, \u043A\u043E\u0442\u043E\u0440\u0443\u044E \u043D\u0430\u0434\u043E \u0441\u0434\u0435\u043B\u0430\u0442\u044C \u041F\u0415\u0420\u0412\u041E\u0419 \u2014 \u043E\u043D\u0430 \u0431\u043B\u043E\u043A\u0438\u0440\u0443\u0435\u0442 \u0432\u0442\u043E\u0440\u0443\u044E."
|
|
6232
|
+
),
|
|
6233
|
+
blockedId: taskLinkPayloadSchema.shape.blockedId.describe(
|
|
6234
|
+
"ID \u0437\u0430\u0434\u0430\u0447\u0438, \u043A\u043E\u0442\u043E\u0440\u0430\u044F \u0416\u0414\u0401\u0422: \u0435\u0451 \u043D\u0435\u043B\u044C\u0437\u044F \u0431\u0440\u0430\u0442\u044C, \u043F\u043E\u043A\u0430 \u043D\u0435 \u0437\u0430\u043A\u0440\u044B\u0442\u0430 \u0431\u043B\u043E\u043A\u0438\u0440\u0443\u044E\u0449\u0430\u044F."
|
|
6235
|
+
),
|
|
6236
|
+
kind: taskLinkPayloadSchema.shape.kind.describe(
|
|
6237
|
+
"BLOCKS \u2014 \u0434\u0435\u0440\u0436\u0438\u0442 \u043E\u0447\u0435\u0440\u0435\u0434\u044C (\u043F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E). RELATES \u2014 \u043F\u0440\u043E\u0441\u0442\u043E \u0441\u0432\u044F\u0437\u044C \xAB\u0441\u043C. \u0442\u0430\u043A\u0436\u0435\xBB, \u043D\u0430 \u043E\u0447\u0435\u0440\u0435\u0434\u044C \u043D\u0435 \u0432\u043B\u0438\u044F\u0435\u0442."
|
|
6238
|
+
)
|
|
6239
|
+
};
|
|
6240
|
+
async function linkTasksHandler(args, runtime) {
|
|
6241
|
+
const parsedArgs = parseArgsOrError(taskLinkPayloadSchema, args);
|
|
6242
|
+
if (!parsedArgs.ok) return errorResult(parsedArgs.message);
|
|
6243
|
+
const result = await callServerOrError(
|
|
6244
|
+
runtime.client,
|
|
6245
|
+
"task:link",
|
|
6246
|
+
parsedArgs.data,
|
|
6247
|
+
taskLinkAckSchema
|
|
6248
|
+
);
|
|
6249
|
+
if (!result.ok) return errorResult(result.message);
|
|
6250
|
+
if (!result.data.linked) {
|
|
6251
|
+
return textResult(`\u0421\u0432\u044F\u0437\u044C \u0443\u0436\u0435 \u0431\u044B\u043B\u0430: ${args.blockerId} \u0431\u043B\u043E\u043A\u0438\u0440\u0443\u0435\u0442 ${args.blockedId}.`);
|
|
6252
|
+
}
|
|
6253
|
+
const stopped = result.data.blockedNow ? " \u0417\u0430\u0434\u0430\u0447\u0430 \u0431\u044B\u043B\u0430 \u0432 \u0440\u0430\u0431\u043E\u0442\u0435 \u0438 \u043E\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D\u0430 \u2014 \u043E\u043D\u0430 \u0436\u0434\u0451\u0442 \u0431\u043B\u043E\u043A\u0438\u0440\u0443\u044E\u0449\u0443\u044E." : "";
|
|
6254
|
+
return textResult(`\u0413\u043E\u0442\u043E\u0432\u043E: ${args.blockerId} \u0431\u043B\u043E\u043A\u0438\u0440\u0443\u0435\u0442 ${args.blockedId}.${stopped}`);
|
|
6255
|
+
}
|
|
6256
|
+
var unlinkTasksInputShape = {
|
|
6257
|
+
blockerId: taskUnlinkPayloadSchema.shape.blockerId.describe("ID \u0431\u043B\u043E\u043A\u0438\u0440\u0443\u044E\u0449\u0435\u0439 \u0437\u0430\u0434\u0430\u0447\u0438."),
|
|
6258
|
+
blockedId: taskUnlinkPayloadSchema.shape.blockedId.describe("ID \u0436\u0434\u0443\u0449\u0435\u0439 \u0437\u0430\u0434\u0430\u0447\u0438.")
|
|
6259
|
+
};
|
|
6260
|
+
async function unlinkTasksHandler(args, runtime) {
|
|
6261
|
+
const parsedArgs = parseArgsOrError(taskUnlinkPayloadSchema, args);
|
|
6262
|
+
if (!parsedArgs.ok) return errorResult(parsedArgs.message);
|
|
6263
|
+
const result = await callServerOrError(
|
|
6264
|
+
runtime.client,
|
|
6265
|
+
"task:unlink",
|
|
6266
|
+
parsedArgs.data,
|
|
6267
|
+
taskUnlinkAckSchema
|
|
6268
|
+
);
|
|
6269
|
+
if (!result.ok) return errorResult(result.message);
|
|
6270
|
+
return textResult(
|
|
6271
|
+
result.data.unlinked ? `\u0421\u0432\u044F\u0437\u044C \u0441\u043D\u044F\u0442\u0430: ${args.blockerId} \u0431\u043E\u043B\u044C\u0448\u0435 \u043D\u0435 \u0431\u043B\u043E\u043A\u0438\u0440\u0443\u0435\u0442 ${args.blockedId}.` : `\u0422\u0430\u043A\u043E\u0439 \u0441\u0432\u044F\u0437\u0438 \u043D\u0435 \u0431\u044B\u043B\u043E: ${args.blockerId} \u043D\u0435 \u0431\u043B\u043E\u043A\u0438\u0440\u043E\u0432\u0430\u043B ${args.blockedId}.`
|
|
6272
|
+
);
|
|
6273
|
+
}
|
|
6274
|
+
var planTaskInputShape = {
|
|
6275
|
+
taskId: taskPlanPayloadSchema.shape.taskId.describe(
|
|
6276
|
+
"ID \u0437\u0430\u0434\u0430\u0447\u0438 \u043F\u0440\u043E\u0435\u043A\u0442\u0430 \u2014 \u043B\u044E\u0431\u043E\u0439, \u0430\u0440\u0435\u043D\u0434\u0430 \u043D\u0435 \u043D\u0443\u0436\u043D\u0430."
|
|
6277
|
+
),
|
|
6278
|
+
patch: taskPlanPayloadSchema.shape.patch.describe(
|
|
6279
|
+
"\u041F\u043E\u043B\u044F \u043F\u043B\u0430\u043D\u0430: title, descriptionMd, acceptance, type, priority, roleKey, assigneeId, milestoneId, labels, estimateMin. \u0421\u0442\u0430\u0442\u0443\u0441 \u0438 \u0440\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442\u044B \u0440\u0430\u0431\u043E\u0442\u044B \u043C\u0435\u043D\u044F\u0442\u044C \u043D\u0435\u043B\u044C\u0437\u044F \u2014 \u0441\u0442\u0430\u0442\u0443\u0441 \u043F\u0435\u0440\u0435\u0432\u043E\u0434\u0438\u0442 \u0447\u0435\u043B\u043E\u0432\u0435\u043A, \u0440\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442\u044B \u043F\u0440\u0438\u0445\u043E\u0434\u044F\u0442 \u0438\u0437 report."
|
|
6280
|
+
)
|
|
6281
|
+
};
|
|
6282
|
+
async function planTaskHandler(args, runtime) {
|
|
6283
|
+
const parsedArgs = parseArgsOrError(taskPlanPayloadSchema, args);
|
|
6284
|
+
if (!parsedArgs.ok) return errorResult(parsedArgs.message);
|
|
6285
|
+
const result = await callServerOrError(
|
|
6286
|
+
runtime.client,
|
|
6287
|
+
"task:plan",
|
|
6288
|
+
parsedArgs.data,
|
|
6289
|
+
taskPlanAckSchema
|
|
6290
|
+
);
|
|
6291
|
+
if (!result.ok) return errorResult(result.message);
|
|
6292
|
+
return textResult(
|
|
6293
|
+
`\u0417\u0430\u0434\u0430\u0447\u0430 ${result.data.task.key} \u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0430:
|
|
6294
|
+
${JSON.stringify(result.data.task, null, 2)}`
|
|
6295
|
+
);
|
|
6296
|
+
}
|
|
6297
|
+
|
|
6077
6298
|
// src/mcp/release-task.ts
|
|
6078
6299
|
var releaseTaskInputShape = {
|
|
6079
6300
|
reason: taskReleasePayloadSchema.shape.reason.describe(
|
|
@@ -6150,8 +6371,15 @@ async function reportHandler(args, runtime) {
|
|
|
6150
6371
|
);
|
|
6151
6372
|
}
|
|
6152
6373
|
const taskLabel2 = state.currentTask.taskKey ?? state.currentTask.taskId;
|
|
6374
|
+
const completedTaskIds = state.completedTaskIds.includes(state.currentTask.taskId) ? state.completedTaskIds : [...state.completedTaskIds, state.currentTask.taskId];
|
|
6153
6375
|
updateState(
|
|
6154
|
-
{
|
|
6376
|
+
{
|
|
6377
|
+
currentTask: null,
|
|
6378
|
+
tasksCompletedInSession: state.tasksCompletedInSession + 1,
|
|
6379
|
+
completedTaskIds,
|
|
6380
|
+
// Сдали задачу — значит очередь только что была не пуста.
|
|
6381
|
+
queueEmptyAt: null
|
|
6382
|
+
},
|
|
6155
6383
|
runtime.stateFilePath
|
|
6156
6384
|
);
|
|
6157
6385
|
const duplicateNote = result.data.duplicate ? " (\u0441\u0435\u0440\u0432\u0435\u0440 \u043E\u0442\u043C\u0435\u0442\u0438\u043B \u044D\u0442\u043E \u043A\u0430\u043A \u043F\u043E\u0432\u0442\u043E\u0440 \u0443\u0436\u0435 \u043F\u0440\u0438\u043D\u044F\u0442\u043E\u0439 \u0441\u0434\u0430\u0447\u0438 \u2014 \u0437\u0430\u0434\u0432\u0430\u0438\u0432\u0430\u043D\u0438\u044F \u043D\u0435\u0442)" : "";
|
|
@@ -6471,6 +6699,9 @@ async function submitViaPullRequest(args, runtime, currentTask) {
|
|
|
6471
6699
|
|
|
6472
6700
|
// src/mcp/tasks.ts
|
|
6473
6701
|
var createTaskInputShape = {
|
|
6702
|
+
dependsOn: taskCreatePayloadSchema.shape.dependsOn.describe(
|
|
6703
|
+
"ID \u0437\u0430\u0434\u0430\u0447, \u043E\u0442 \u043A\u043E\u0442\u043E\u0440\u044B\u0445 \u043D\u043E\u0432\u0430\u044F \u0437\u0430\u0432\u0438\u0441\u0438\u0442: \u0435\u0451 \u043D\u0435 \u0432\u044B\u0434\u0430\u0434\u0443\u0442, \u043F\u043E\u043A\u0430 \u043E\u043D\u0438 \u043D\u0435 \u0437\u0430\u043A\u0440\u044B\u0442\u044B. \u0421\u0432\u044F\u0437\u0438 \u0441\u0442\u0430\u0432\u044F\u0442\u0441\u044F \u0441\u0440\u0430\u0437\u0443 \u2014 \u0432\u0442\u043E\u0440\u043E\u0439 \u043F\u0440\u043E\u0445\u043E\u0434 \u043F\u043E \u0441\u043F\u0438\u0441\u043A\u0443 \u043D\u0435 \u043D\u0443\u0436\u0435\u043D. \u041D\u0435\u043F\u043E\u0434\u0445\u043E\u0434\u044F\u0449\u0430\u044F \u0441\u0441\u044B\u043B\u043A\u0430 (\u0447\u0443\u0436\u043E\u0439 \u043F\u0440\u043E\u0435\u043A\u0442, \u043D\u0435\u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044E\u0449\u0430\u044F \u0437\u0430\u0434\u0430\u0447\u0430) \u043C\u043E\u043B\u0447\u0430 \u043F\u0440\u043E\u043F\u0443\u0441\u043A\u0430\u0435\u0442\u0441\u044F: \u0437\u0430\u0434\u0430\u0447\u0430 \u0443\u0436\u0435 \u0441\u043E\u0437\u0434\u0430\u043D\u0430, \u0438 \u0442\u0435\u0440\u044F\u0442\u044C \u0435\u0451 \u0438\u0437-\u0437\u0430 \u043E\u0434\u043D\u043E\u0439 \u0441\u0432\u044F\u0437\u0438 \u043D\u0435\u043B\u044C\u0437\u044F."
|
|
6704
|
+
),
|
|
6474
6705
|
title: taskCreatePayloadSchema.shape.title.describe("\u0417\u0430\u0433\u043E\u043B\u043E\u0432\u043E\u043A \u043D\u043E\u0432\u043E\u0439 \u0437\u0430\u0434\u0430\u0447\u0438, \u043A\u043E\u0440\u043E\u0442\u043A\u043E."),
|
|
6475
6706
|
descriptionMd: taskCreatePayloadSchema.shape.descriptionMd.describe(
|
|
6476
6707
|
"Markdown-\u043E\u043F\u0438\u0441\u0430\u043D\u0438\u0435 \u043D\u043E\u0432\u043E\u0439 \u0437\u0430\u0434\u0430\u0447\u0438 \u2014 \u0447\u0442\u043E \u0441\u0434\u0435\u043B\u0430\u0442\u044C \u0438 \u0437\u0430\u0447\u0435\u043C."
|
|
@@ -6509,7 +6740,8 @@ async function createTaskHandler(args, runtime) {
|
|
|
6509
6740
|
executor: args.executor,
|
|
6510
6741
|
blocksCurrent: args.blocksCurrent,
|
|
6511
6742
|
...args.acceptance ? { acceptance: args.acceptance } : {},
|
|
6512
|
-
...args.labels && args.labels.length > 0 ? { labels: args.labels } : {}
|
|
6743
|
+
...args.labels && args.labels.length > 0 ? { labels: args.labels } : {},
|
|
6744
|
+
...args.dependsOn && args.dependsOn.length > 0 ? { dependsOn: args.dependsOn } : {}
|
|
6513
6745
|
});
|
|
6514
6746
|
if (!parsedArgs.ok) return errorResult(parsedArgs.message);
|
|
6515
6747
|
const state = readState(runtime.stateFilePath);
|
|
@@ -6535,8 +6767,15 @@ async function createTaskHandler(args, runtime) {
|
|
|
6535
6767
|
const blockNote = result.data.currentTaskStatus ? `
|
|
6536
6768
|
|
|
6537
6769
|
\u0422\u0432\u043E\u044F \u0442\u0435\u043A\u0443\u0449\u0430\u044F \u0437\u0430\u0434\u0430\u0447\u0430 ${state.currentTask?.taskKey ?? state.currentTask?.taskId ?? ""} \u0437\u0430\u0431\u043B\u043E\u043A\u0438\u0440\u043E\u0432\u0430\u043D\u0430: \u043D\u043E\u0432\u044B\u0439 \u0441\u0442\u0430\u0442\u0443\u0441 \u2014 ${result.data.currentTaskStatus}. \u041F\u0440\u0435\u043A\u0440\u0430\u0442\u0438 \u0430\u043A\u0442\u0438\u0432\u043D\u0443\u044E \u0440\u0430\u0431\u043E\u0442\u0443 \u043D\u0430\u0434 \u043D\u0435\u0439 \u043F\u0440\u044F\u043C\u043E \u0441\u0435\u0439\u0447\u0430\u0441; \u043A\u043E\u0433\u0434\u0430 \u0434\u043E\u0439\u0434\u0451\u0448\u044C \u0434\u043E \u0441\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u044F, \u043A\u043E\u0442\u043E\u0440\u043E\u0435 \u0441\u0442\u043E\u0438\u0442 \u0437\u0430\u0444\u0438\u043A\u0441\u0438\u0440\u043E\u0432\u0430\u0442\u044C, \u0441\u0434\u0430\u0439 report(partial) \u0441 \u043E\u043F\u0438\u0441\u0430\u043D\u0438\u0435\u043C \u0441\u0434\u0435\u043B\u0430\u043D\u043D\u043E\u0433\u043E \u0438 \u0443\u043A\u0430\u0436\u0438 \u0432 nextSteps, \u0447\u0442\u043E \u0436\u0434\u0451\u0448\u044C \u043D\u043E\u0432\u0443\u044E \u0437\u0430\u0434\u0430\u0447\u0443 ${result.data.task.key}.` : "";
|
|
6770
|
+
const requested = args.dependsOn?.length ?? 0;
|
|
6771
|
+
const linked = result.data.dependenciesLinked ?? 0;
|
|
6772
|
+
const depsNote = requested === 0 ? "" : linked === requested ? `
|
|
6773
|
+
|
|
6774
|
+
\u0417\u0430\u0432\u0438\u0441\u0438\u043C\u043E\u0441\u0442\u0435\u0439 \u043F\u043E\u0441\u0442\u0430\u0432\u043B\u0435\u043D\u043E: ${linked}.` : `
|
|
6775
|
+
|
|
6776
|
+
\u0417\u0430\u0432\u0438\u0441\u0438\u043C\u043E\u0441\u0442\u0435\u0439 \u043F\u043E\u0441\u0442\u0430\u0432\u043B\u0435\u043D\u043E ${linked} \u0438\u0437 ${requested}: \u043E\u0441\u0442\u0430\u043B\u044C\u043D\u044B\u0435 \u0441\u0441\u044B\u043B\u043A\u0438 \u043D\u0435 \u043F\u043E\u0434\u043E\u0448\u043B\u0438 (\u043D\u0435\u0442 \u0442\u0430\u043A\u043E\u0439 \u0437\u0430\u0434\u0430\u0447\u0438 \u0438\u043B\u0438 \u043E\u043D\u0430 \u0438\u0437 \u0434\u0440\u0443\u0433\u043E\u0433\u043E \u043F\u0440\u043E\u0435\u043A\u0442\u0430). \u041F\u0440\u043E\u0432\u0435\u0440\u044C \u0438\u0445 \u0438 \u043F\u0440\u0438 \u043D\u0435\u043E\u0431\u0445\u043E\u0434\u0438\u043C\u043E\u0441\u0442\u0438 \u0441\u0432\u044F\u0436\u0438 \u0447\u0435\u0440\u0435\u0437 link_tasks.`;
|
|
6538
6777
|
return textResult(
|
|
6539
|
-
`\u0421\u043E\u0437\u0434\u0430\u043D\u0430 \u0437\u0430\u0434\u0430\u0447\u0430 ${result.data.task.key} (${result.data.task.status}) \u0434\u043B\u044F ${addressee}.${blockNote}`
|
|
6778
|
+
`\u0421\u043E\u0437\u0434\u0430\u043D\u0430 \u0437\u0430\u0434\u0430\u0447\u0430 ${result.data.task.key} (${result.data.task.status}) \u0434\u043B\u044F ${addressee}.${depsNote}${blockNote}`
|
|
6540
6779
|
);
|
|
6541
6780
|
}
|
|
6542
6781
|
var listTasksInputShape = {
|
|
@@ -6715,6 +6954,33 @@ function registerTools(server, runtime) {
|
|
|
6715
6954
|
},
|
|
6716
6955
|
(args) => updateTaskHandler(args, runtime)
|
|
6717
6956
|
);
|
|
6957
|
+
server.registerTool(
|
|
6958
|
+
"link_tasks",
|
|
6959
|
+
{
|
|
6960
|
+
title: "\u0421\u0432\u044F\u0437\u0430\u0442\u044C \u0437\u0430\u0434\u0430\u0447\u0438",
|
|
6961
|
+
description: "\u0417\u0430\u0434\u0430\u0442\u044C \u0437\u0430\u0432\u0438\u0441\u0438\u043C\u043E\u0441\u0442\u044C \xABA \u0431\u043B\u043E\u043A\u0438\u0440\u0443\u0435\u0442 B\xBB: \u043F\u043E\u043A\u0430 A \u043D\u0435 \u0437\u0430\u043A\u0440\u044B\u0442\u0430, \u0434\u0438\u0441\u043F\u0435\u0442\u0447\u0435\u0440 \u043D\u0435 \u0432\u044B\u0434\u0430\u0441\u0442 B. \u0420\u0430\u0431\u043E\u0442\u0430\u0435\u0442 \u0441 \u041B\u042E\u0411\u042B\u041C\u0418 \u0437\u0430\u0434\u0430\u0447\u0430\u043C\u0438 \u043F\u0440\u043E\u0435\u043A\u0442\u0430, \u0430\u0440\u0435\u043D\u0434\u0430 \u043D\u0435 \u043D\u0443\u0436\u043D\u0430 \u2014 \u0432 \u043E\u0442\u043B\u0438\u0447\u0438\u0435 \u043E\u0442 update_task. \u0422\u0440\u0435\u0431\u0443\u0435\u0442 \u0440\u043E\u043B\u044C \xAB\u043C\u0435\u043D\u0435\u0434\u0436\u0435\u0440\xBB \u043B\u0438\u0431\u043E \u0432\u043B\u0430\u0434\u0435\u043B\u044C\u0446\u0430/\u0430\u0434\u043C\u0438\u043D\u0438\u0441\u0442\u0440\u0430\u0442\u043E\u0440\u0430 \u043E\u0440\u0433\u0430\u043D\u0438\u0437\u0430\u0446\u0438\u0438. \u0421\u0432\u044F\u0437\u044C, \u0437\u0430\u043C\u044B\u043A\u0430\u044E\u0449\u0430\u044F \u043A\u043E\u043B\u044C\u0446\u043E, \u0431\u0443\u0434\u0435\u0442 \u043E\u0442\u0432\u0435\u0440\u0433\u043D\u0443\u0442\u0430: \u0437\u0430\u0434\u0430\u0447\u0438 \u0432 \u043D\u0451\u043C \u0436\u0434\u0430\u043B\u0438 \u0431\u044B \u0434\u0440\u0443\u0433 \u0434\u0440\u0443\u0433\u0430 \u0432\u0435\u0447\u043D\u043E.",
|
|
6962
|
+
inputSchema: linkTasksInputShape
|
|
6963
|
+
},
|
|
6964
|
+
(args) => linkTasksHandler(args, runtime)
|
|
6965
|
+
);
|
|
6966
|
+
server.registerTool(
|
|
6967
|
+
"plan_task",
|
|
6968
|
+
{
|
|
6969
|
+
title: "\u0421\u043F\u043B\u0430\u043D\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u0437\u0430\u0434\u0430\u0447\u0443",
|
|
6970
|
+
description: "\u0418\u0437\u043C\u0435\u043D\u0438\u0442\u044C \u043F\u043E\u043B\u044F \u041B\u042E\u0411\u041E\u0419 \u0437\u0430\u0434\u0430\u0447\u0438 \u043F\u0440\u043E\u0435\u043A\u0442\u0430: \u0437\u0430\u0433\u043E\u043B\u043E\u0432\u043E\u043A, \u043E\u043F\u0438\u0441\u0430\u043D\u0438\u0435, \u043A\u0440\u0438\u0442\u0435\u0440\u0438\u0438 \u043F\u0440\u0438\u0451\u043C\u043A\u0438, \u043F\u0440\u0438\u043E\u0440\u0438\u0442\u0435\u0442, \u0442\u0438\u043F, \u0430\u0434\u0440\u0435\u0441\u0430\u0442\u0430, \u043C\u0438\u043B\u0441\u0442\u043E\u0443\u043D, \u043C\u0435\u0442\u043A\u0438, \u043E\u0446\u0435\u043D\u043A\u0443. \u0410\u0440\u0435\u043D\u0434\u0430 \u043D\u0435 \u043D\u0443\u0436\u043D\u0430 \u2014 \u0432 \u043E\u0442\u043B\u0438\u0447\u0438\u0435 \u043E\u0442 update_task. \u0422\u0440\u0435\u0431\u0443\u0435\u0442 \u0440\u043E\u043B\u044C \xAB\u043C\u0435\u043D\u0435\u0434\u0436\u0435\u0440\xBB \u043B\u0438\u0431\u043E \u0432\u043B\u0430\u0434\u0435\u043B\u044C\u0446\u0430/\u0430\u0434\u043C\u0438\u043D\u0438\u0441\u0442\u0440\u0430\u0442\u043E\u0440\u0430 \u043E\u0440\u0433\u0430\u043D\u0438\u0437\u0430\u0446\u0438\u0438. \u0421\u0422\u0410\u0422\u0423\u0421 \u0438\u0437\u043C\u0435\u043D\u0438\u0442\u044C \u043D\u0435\u043B\u044C\u0437\u044F: \u0432 \u0440\u0430\u0431\u043E\u0442\u0443 \u0437\u0430\u0434\u0430\u0447\u0443 \u043F\u0435\u0440\u0435\u0432\u043E\u0434\u0438\u0442 \u0447\u0435\u043B\u043E\u0432\u0435\u043A. \u0420\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442\u044B \u0440\u0430\u0431\u043E\u0442\u044B \u0442\u043E\u0436\u0435 \u2014 \u043E\u043D\u0438 \u043F\u0440\u0438\u0445\u043E\u0434\u044F\u0442 \u0438\u0437 report.",
|
|
6971
|
+
inputSchema: planTaskInputShape
|
|
6972
|
+
},
|
|
6973
|
+
(args) => planTaskHandler(args, runtime)
|
|
6974
|
+
);
|
|
6975
|
+
server.registerTool(
|
|
6976
|
+
"unlink_tasks",
|
|
6977
|
+
{
|
|
6978
|
+
title: "\u0421\u043D\u044F\u0442\u044C \u0441\u0432\u044F\u0437\u044C \u043C\u0435\u0436\u0434\u0443 \u0437\u0430\u0434\u0430\u0447\u0430\u043C\u0438",
|
|
6979
|
+
description: "\u0423\u0431\u0440\u0430\u0442\u044C \u0437\u0430\u0432\u0438\u0441\u0438\u043C\u043E\u0441\u0442\u044C \xABA \u0431\u043B\u043E\u043A\u0438\u0440\u0443\u0435\u0442 B\xBB. \u0415\u0441\u043B\u0438 \u0441\u0432\u044F\u0437\u0438 \u043D\u0435 \u0431\u044B\u043B\u043E \u2014 \u044D\u0442\u043E \u043D\u0435 \u043E\u0448\u0438\u0431\u043A\u0430, \u0430 \u043E\u0442\u0447\u0451\u0442.",
|
|
6980
|
+
inputSchema: unlinkTasksInputShape
|
|
6981
|
+
},
|
|
6982
|
+
(args) => unlinkTasksHandler(args, runtime)
|
|
6983
|
+
);
|
|
6718
6984
|
server.registerTool(
|
|
6719
6985
|
"ask_human",
|
|
6720
6986
|
{
|
|
@@ -7158,7 +7424,20 @@ var StopInputSchema = z43.object({
|
|
|
7158
7424
|
session_id: z43.string(),
|
|
7159
7425
|
cwd: z43.string(),
|
|
7160
7426
|
stop_hook_active: z43.boolean(),
|
|
7161
|
-
last_assistant_message: z43.string().optional()
|
|
7427
|
+
last_assistant_message: z43.string().optional(),
|
|
7428
|
+
/**
|
|
7429
|
+
* Незавершённая фоновая работа сессии.
|
|
7430
|
+
*
|
|
7431
|
+
* ЧИТАЕМ ТОЛЬКО ДЛИНУ, И ЭТО НАМЕРЕННО. Внутренняя форма элементов в
|
|
7432
|
+
* документации не описана, а строить решение на угаданных полях —
|
|
7433
|
+
* ровно то, что §22.8 ТЗ запрещает. Нам достаточно ответа на вопрос
|
|
7434
|
+
* «есть ли незакрытая фоновая работа»: он не зависит от того, что
|
|
7435
|
+
* внутри.
|
|
7436
|
+
*
|
|
7437
|
+
* В §10.4 ТЗ этих полей нет — они появились в Claude Code позже.
|
|
7438
|
+
*/
|
|
7439
|
+
background_tasks: z43.array(z43.unknown()).optional(),
|
|
7440
|
+
session_crons: z43.array(z43.unknown()).optional()
|
|
7162
7441
|
}).passthrough();
|
|
7163
7442
|
function taskLabel(state) {
|
|
7164
7443
|
if (!state.currentTask) return "";
|
|
@@ -7171,6 +7450,13 @@ function stopHook(input, state) {
|
|
|
7171
7450
|
if (state.mode !== AgentMode.AUTONOMOUS) {
|
|
7172
7451
|
return { output: {} };
|
|
7173
7452
|
}
|
|
7453
|
+
if (hasUnfinishedBackgroundWork(input)) {
|
|
7454
|
+
return {
|
|
7455
|
+
output: {
|
|
7456
|
+
systemMessage: "\u0410\u0433\u0435\u043D\u0442 \u0436\u0434\u0451\u0442 \u0444\u043E\u043D\u043E\u0432\u0443\u044E \u0440\u0430\u0431\u043E\u0442\u0443 \u2014 \u0445\u0443\u043A \u043D\u0435 \u043C\u0435\u0448\u0430\u0435\u0442. \u0425\u043E\u0434 \u043F\u0440\u043E\u0434\u043E\u043B\u0436\u0438\u0442\u0441\u044F, \u043A\u043E\u0433\u0434\u0430 \u043E\u043D\u0430 \u0437\u0430\u043A\u043E\u043D\u0447\u0438\u0442\u0441\u044F."
|
|
7457
|
+
}
|
|
7458
|
+
};
|
|
7459
|
+
}
|
|
7174
7460
|
if (state.currentTask) {
|
|
7175
7461
|
return {
|
|
7176
7462
|
output: {
|
|
@@ -7179,23 +7465,39 @@ function stopHook(input, state) {
|
|
|
7179
7465
|
}
|
|
7180
7466
|
};
|
|
7181
7467
|
}
|
|
7182
|
-
if (state.tasksCompletedInSession
|
|
7468
|
+
if (state.tasksCompletedInSession >= state.contextResetAfterTasks) {
|
|
7469
|
+
return limitReached(state);
|
|
7470
|
+
}
|
|
7471
|
+
if (state.queueEmptyAt) {
|
|
7183
7472
|
return {
|
|
7184
7473
|
output: {
|
|
7185
|
-
|
|
7186
|
-
reason: "\u0412\u044B\u0437\u043E\u0432\u0438 next_task \u0434\u043B\u044F \u043F\u043E\u043B\u0443\u0447\u0435\u043D\u0438\u044F \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0435\u0439 \u0437\u0430\u0434\u0430\u0447\u0438."
|
|
7474
|
+
systemMessage: "\u041E\u0447\u0435\u0440\u0435\u0434\u044C \u043F\u0443\u0441\u0442\u0430 \u2014 \u0430\u0433\u0435\u043D\u0442 \u0441\u0432\u043E\u0431\u043E\u0434\u0435\u043D. \u041F\u043E\u0437\u043E\u0432\u0438\u0442\u0435 \u0435\u0433\u043E \u0441\u043D\u043E\u0432\u0430, \u043A\u043E\u0433\u0434\u0430 \u043F\u043E\u044F\u0432\u044F\u0442\u0441\u044F \u0437\u0430\u0434\u0430\u0447\u0438: \u043E\u043D \u0432\u043E\u0437\u044C\u043C\u0451\u0442 \u0438\u0445 \u0441 \u043F\u0435\u0440\u0432\u043E\u0433\u043E \u0436\u0435 next_task."
|
|
7187
7475
|
}
|
|
7188
7476
|
};
|
|
7189
7477
|
}
|
|
7478
|
+
return {
|
|
7479
|
+
output: {
|
|
7480
|
+
decision: "block",
|
|
7481
|
+
reason: "\u0412\u044B\u0437\u043E\u0432\u0438 next_task \u0434\u043B\u044F \u043F\u043E\u043B\u0443\u0447\u0435\u043D\u0438\u044F \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0435\u0439 \u0437\u0430\u0434\u0430\u0447\u0438."
|
|
7482
|
+
}
|
|
7483
|
+
};
|
|
7484
|
+
}
|
|
7485
|
+
function hasUnfinishedBackgroundWork(input) {
|
|
7486
|
+
return (input.background_tasks?.length ?? 0) > 0 || (input.session_crons?.length ?? 0) > 0;
|
|
7487
|
+
}
|
|
7488
|
+
function limitReached(state) {
|
|
7190
7489
|
const serverActions = [
|
|
7191
7490
|
{
|
|
7192
7491
|
type: "session-limit-reached",
|
|
7193
7492
|
payload: { tasksCompletedInSession: state.tasksCompletedInSession }
|
|
7194
7493
|
}
|
|
7195
7494
|
];
|
|
7495
|
+
const tasks = state.completedTaskIds.length;
|
|
7496
|
+
const submissions = state.tasksCompletedInSession;
|
|
7497
|
+
const counted = tasks > 0 && tasks !== submissions ? `${tasks} \u0437\u0430\u0434\u0430\u0447(\u0438) \u0437\u0430 ${submissions} \u0441\u0434\u0430\u0447(\u0438)` : `${submissions} \u0437\u0430\u0434\u0430\u0447(\u0438)`;
|
|
7196
7498
|
return {
|
|
7197
7499
|
output: {
|
|
7198
|
-
systemMessage: `\u0421\u0435\u0441\u0441\u0438\u044F \u0432\u044B\u043F\u043E\u043B\u043D\u0438\u043B\u0430 ${
|
|
7500
|
+
systemMessage: `\u0421\u0435\u0441\u0441\u0438\u044F \u0432\u044B\u043F\u043E\u043B\u043D\u0438\u043B\u0430 ${counted}. \u0421\u0434\u0435\u043B\u0430\u0439\u0442\u0435 /clear \u0438 \u0432\u043A\u043B\u044E\u0447\u0438\u0442\u0435 \u0440\u0435\u0436\u0438\u043C \u0430\u0433\u0435\u043D\u0442\u0430 \u0441\u043D\u043E\u0432\u0430.`
|
|
7199
7501
|
},
|
|
7200
7502
|
serverActions
|
|
7201
7503
|
};
|
|
@@ -7595,6 +7897,101 @@ function formatStatus(report) {
|
|
|
7595
7897
|
return lines.join("\n");
|
|
7596
7898
|
}
|
|
7597
7899
|
|
|
7900
|
+
// src/cli/wait-for-task.ts
|
|
7901
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync8, readFileSync as readFileSync9, rmSync, writeFileSync as writeFileSync10 } from "fs";
|
|
7902
|
+
import { dirname as dirname6, join as join11 } from "path";
|
|
7903
|
+
var DEFAULT_POLL_INTERVAL_MS = 15e3;
|
|
7904
|
+
var DEFAULT_MAX_WAIT_MS = 8 * 60 * 60 * 1e3;
|
|
7905
|
+
var CONNECT_TIMEOUT_MS = 8e3;
|
|
7906
|
+
var PEEK_TIMEOUT_MS = 8e3;
|
|
7907
|
+
var WAIT_EXIT_IDLE = 0;
|
|
7908
|
+
var WAIT_EXIT_WAKE = 2;
|
|
7909
|
+
function skipReason(state) {
|
|
7910
|
+
if (state.mode !== AgentMode.AUTONOMOUS) {
|
|
7911
|
+
return "\u0440\u0435\u0436\u0438\u043C \u043D\u0435 \u0430\u0432\u0442\u043E\u043D\u043E\u043C\u043D\u044B\u0439";
|
|
7912
|
+
}
|
|
7913
|
+
if (state.currentTask) {
|
|
7914
|
+
return "\u0437\u0430\u0434\u0430\u0447\u0430 \u0443\u0436\u0435 \u0432 \u0440\u0430\u0431\u043E\u0442\u0435";
|
|
7915
|
+
}
|
|
7916
|
+
return null;
|
|
7917
|
+
}
|
|
7918
|
+
function lockPath(stateFilePath) {
|
|
7919
|
+
return join11(dirname6(stateFilePath), "wait-for-task.lock");
|
|
7920
|
+
}
|
|
7921
|
+
function acquireLock(stateFilePath) {
|
|
7922
|
+
const path = lockPath(stateFilePath);
|
|
7923
|
+
if (existsSync11(path)) {
|
|
7924
|
+
const pid = Number.parseInt(readFileSync9(path, "utf8").trim(), 10);
|
|
7925
|
+
if (Number.isFinite(pid) && pid > 0 && isAlive(pid)) return false;
|
|
7926
|
+
}
|
|
7927
|
+
mkdirSync8(dirname6(path), { recursive: true });
|
|
7928
|
+
writeFileSync10(path, String(process.pid), "utf8");
|
|
7929
|
+
return true;
|
|
7930
|
+
}
|
|
7931
|
+
function releaseLock(stateFilePath) {
|
|
7932
|
+
rmSync(lockPath(stateFilePath), { force: true });
|
|
7933
|
+
}
|
|
7934
|
+
function isAlive(pid) {
|
|
7935
|
+
try {
|
|
7936
|
+
process.kill(pid, 0);
|
|
7937
|
+
return true;
|
|
7938
|
+
} catch {
|
|
7939
|
+
return false;
|
|
7940
|
+
}
|
|
7941
|
+
}
|
|
7942
|
+
function trimTrailingSlash4(url) {
|
|
7943
|
+
return url.endsWith("/") ? url.slice(0, -1) : url;
|
|
7944
|
+
}
|
|
7945
|
+
async function peekOnce(url, token, createSocket2) {
|
|
7946
|
+
const socket = createSocket2(`${trimTrailingSlash4(url)}/agent`, {
|
|
7947
|
+
auth: { token },
|
|
7948
|
+
autoConnect: false,
|
|
7949
|
+
reconnection: false
|
|
7950
|
+
});
|
|
7951
|
+
try {
|
|
7952
|
+
await connectOnce(socket, CONNECT_TIMEOUT_MS);
|
|
7953
|
+
const raw = await emitWithAckTimeout(socket, "task:peek", {}, PEEK_TIMEOUT_MS);
|
|
7954
|
+
const parsed = taskPeekAckSchema.safeParse(raw);
|
|
7955
|
+
return parsed.success ? parsed.data.available : null;
|
|
7956
|
+
} catch {
|
|
7957
|
+
return null;
|
|
7958
|
+
} finally {
|
|
7959
|
+
socket.disconnect();
|
|
7960
|
+
}
|
|
7961
|
+
}
|
|
7962
|
+
async function waitForTask(options = {}) {
|
|
7963
|
+
const context = resolveProjectContext(options.cwd ?? process.cwd(), options.credentialsFilePath);
|
|
7964
|
+
if (!context.ok) return { outcome: "idle", reason: "\u043F\u0440\u043E\u0435\u043A\u0442 \u043D\u0435 \u043F\u043E\u0434\u043A\u043B\u044E\u0447\u0451\u043D" };
|
|
7965
|
+
const stateFilePath = options.stateFilePath ?? getStateFilePath();
|
|
7966
|
+
const skip = skipReason(readState(stateFilePath));
|
|
7967
|
+
if (skip) return { outcome: "idle", reason: skip };
|
|
7968
|
+
if (!acquireLock(stateFilePath)) {
|
|
7969
|
+
return { outcome: "idle", reason: "\u043E\u0436\u0438\u0434\u0430\u0442\u0435\u043B\u044C \u0443\u0436\u0435 \u0437\u0430\u043F\u0443\u0449\u0435\u043D" };
|
|
7970
|
+
}
|
|
7971
|
+
const now = options.now ?? (() => Date.now());
|
|
7972
|
+
const sleep2 = options.sleep ?? ((ms) => new Promise((resolve4) => setTimeout(resolve4, ms)));
|
|
7973
|
+
const createSocket2 = options.createSocket ?? createSocket;
|
|
7974
|
+
const deadline = now() + (options.maxWaitMs ?? DEFAULT_MAX_WAIT_MS);
|
|
7975
|
+
const interval = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
7976
|
+
try {
|
|
7977
|
+
while (now() < deadline) {
|
|
7978
|
+
const reason = skipReason(readState(stateFilePath));
|
|
7979
|
+
if (reason) return { outcome: "idle", reason };
|
|
7980
|
+
const available = await peekOnce(context.config.url, context.credentials.token, createSocket2);
|
|
7981
|
+
if (available === true) {
|
|
7982
|
+
return {
|
|
7983
|
+
outcome: "wake",
|
|
7984
|
+
message: "\u0412 \u043E\u0447\u0435\u0440\u0435\u0434\u0438 \u043F\u043E\u044F\u0432\u0438\u043B\u0430\u0441\u044C \u0437\u0430\u0434\u0430\u0447\u0430. \u0412\u044B\u0437\u043E\u0432\u0438 next_task, \u0447\u0442\u043E\u0431\u044B \u0432\u0437\u044F\u0442\u044C \u0435\u0451."
|
|
7985
|
+
};
|
|
7986
|
+
}
|
|
7987
|
+
await sleep2(interval);
|
|
7988
|
+
}
|
|
7989
|
+
return { outcome: "idle", reason: "\u0438\u0441\u0442\u0435\u043A\u043B\u043E \u0432\u0440\u0435\u043C\u044F \u043E\u0436\u0438\u0434\u0430\u043D\u0438\u044F" };
|
|
7990
|
+
} finally {
|
|
7991
|
+
releaseLock(stateFilePath);
|
|
7992
|
+
}
|
|
7993
|
+
}
|
|
7994
|
+
|
|
7598
7995
|
// src/cli/program.ts
|
|
7599
7996
|
var CLI_VERSION = VERSION;
|
|
7600
7997
|
function printError(error) {
|
|
@@ -7677,6 +8074,16 @@ function createProgram() {
|
|
|
7677
8074
|
printError(result.message);
|
|
7678
8075
|
process.exitCode = 1;
|
|
7679
8076
|
});
|
|
8077
|
+
program.command("wait-for-task").description("\u0416\u0434\u0430\u0442\u044C \u043F\u043E\u044F\u0432\u043B\u0435\u043D\u0438\u044F \u0437\u0430\u0434\u0430\u0447\u0438 \u0438 \u0440\u0430\u0437\u0431\u0443\u0434\u0438\u0442\u044C \u0441\u0435\u0441\u0441\u0438\u044E (\u0441\u0442\u0430\u0432\u0438\u0442\u0441\u044F \u0445\u0443\u043A\u043E\u043C Stop \u0441 asyncRewake)").option("--cwd <dir>", "\u0420\u0430\u0431\u043E\u0447\u0438\u0439 \u043A\u0430\u0442\u0430\u043B\u043E\u0433 \u043F\u0440\u043E\u0435\u043A\u0442\u0430 (\u0434\u043B\u044F \u0442\u0435\u0441\u0442\u043E\u0432/\u0430\u0432\u0442\u043E\u043C\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u0438)").action(async (opts) => {
|
|
8078
|
+
const result = await waitForTask({ cwd: opts.cwd });
|
|
8079
|
+
if (result.outcome === "wake") {
|
|
8080
|
+
process.stderr.write(`${result.message}
|
|
8081
|
+
`);
|
|
8082
|
+
process.exitCode = WAIT_EXIT_WAKE;
|
|
8083
|
+
return;
|
|
8084
|
+
}
|
|
8085
|
+
process.exitCode = WAIT_EXIT_IDLE;
|
|
8086
|
+
});
|
|
7680
8087
|
program.command("hook").description(
|
|
7681
8088
|
"\u0412\u044B\u0437\u0432\u0430\u0442\u044C \u0445\u0443\u043A \u0430\u0433\u0435\u043D\u0442\u0441\u043A\u043E\u0439 \u043F\u0440\u043E\u0433\u0440\u0430\u043C\u043C\u044B (JSON \u043D\u0430 stdin) \u2014 \u043F\u0440\u043E\u043F\u0438\u0441\u044B\u0432\u0430\u0435\u0442\u0441\u044F \u043A\u043E\u043D\u0444\u0438\u0433\u043E\u043C \u043F\u0440\u0438 `quiel init`"
|
|
7682
8089
|
).argument("<name>", `\u0438\u043C\u044F \u0445\u0443\u043A\u0430: ${KNOWN_HOOK_NAMES.join(", ")}`).option(
|
package/package.json
CHANGED