dsh-desktop-kit 2.7.7 → 2.7.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/README.md +19 -16
- package/client.js +294 -4
- package/docs/TEST-2.7.8.md +29 -0
- package/docs/TEST-2.7.9.md +28 -0
- package/package.json +2 -2
- package/plugins/dsh-agent-extensions/README.md +24 -1
- package/plugins/dsh-agent-extensions/client.js +79 -2
- package/plugins/dsh-agent-extensions/index.js +92 -6
- package/plugins/dsh-agent-extensions/lib/agents.js +141 -12
- package/plugins/dsh-agent-extensions/lib/api.js +55 -12
- package/plugins/dsh-agent-extensions/lib/runs.js +60 -0
- package/plugins/dsh-agent-extensions/lib/worktrees.js +143 -0
- package/plugins/dsh-agent-extensions/screenshots/agents-1440.png +0 -0
- package/plugins/dsh-agent-extensions/screenshots/agents-390.png +0 -0
- package/plugins/dsh-guardrails/README.md +13 -0
- package/plugins/dsh-guardrails/client.js +220 -0
- package/plugins/dsh-guardrails/index.js +6 -0
- package/plugins/dsh-guardrails/lib/api.js +103 -0
- package/plugins/dsh-guardrails/package.json +2 -0
- package/plugins/dsh-guardrails/screenshots/permissions-1440.png +0 -0
- package/plugins/dsh-guardrails/screenshots/permissions-390.png +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2.7.9 (2026-11-12)
|
|
4
|
+
|
|
5
|
+
- **Worktrees.** `project_worktree` (`list|create|remove`) создаёт `.dsh/worktrees/<slug>` на ветке `dsh/<slug>`, пишет путь в `.git/info/exclude`, отказывает в удалении грязного worktree без `force`.
|
|
6
|
+
- **Фоновые агенты.** `project_agent` получил `background=true`, `worktree=auto|<slug>`, `status` и `cancel`; реестр `.dsh/agents/runs.json` хранит id, ветку, статус и короткий результат.
|
|
7
|
+
- **Agents UI.** Секции Worktrees и «Фоновые запуски», кнопки «Обновить» и «Остановить»; адаптив 390/768/1440 без overflow/markup.
|
|
8
|
+
- **Тесты.** `worktrees-tests`, `background-agents-tests`, полный `npm test`.
|
|
9
|
+
|
|
10
|
+
## 2.7.8 (2026-11-12)
|
|
11
|
+
|
|
12
|
+
- **Permissions UI.** Настройки → **Права**: эффективный preset DSH (`current`), sandbox/approval, каталог пресетов, жёсткие правила kit и защищённые пути. Страница read-only, без второго picker и без хранения approvals.
|
|
13
|
+
- **Guardrails RPC.** `/api/dsh-guardrails/permissions`: `catalog/current/resolve/optionOf` + снимок политики; привязка к сессии и workspace.
|
|
14
|
+
- **UI.** 390/768/1440: overflow 0, console 0, markup 0; скриншоты 1440/390.
|
|
15
|
+
- **2.7.7** (staged) содержала MCP prompts; 2.7.8 включает и prompts, и Permissions UI.
|
|
16
|
+
|
|
3
17
|
## 2.7.7 (2026-11-12)
|
|
4
18
|
|
|
5
19
|
- **MCP prompts.** Settings → MCP → **Prompts**: список промптов сервера, аргументы, запуск, результат и копирование. Новые RPC `prompts`/`prompt`; клиент поддерживает `prompts/list` и `prompts/get` с пагинацией.
|
package/README.md
CHANGED
|
@@ -4,17 +4,17 @@
|
|
|
4
4
|
|
|
5
5
|
Набор из **16 плагинов и 30 навыков** для DeepSeek Harness 0.2.x: русский интерфейс, автопилот навыков, токен-лаборатория, студия навыков, память проектов, обмен памятью между машинами (GitHub и USB), guardrails, карта репозитория, чекпоинты, plan mode, browser QA, GitHub review, Claude Code-подобные hooks/agents/commands, перенос чатов и резервные копии профиля.
|
|
6
6
|
|
|
7
|
-

|
|
8
8
|
|
|
9
9
|
## MCP: подключения без JSON
|
|
10
10
|
|
|
11
11
|
Настройки → **MCP**: пресеты GitHub, PostgreSQL и Filesystem, живые статусы, health, список tools и безопасное хранение токенов в `~/.dsh/mcp.secrets.json`. Промпты сервера открываются кнопкой **Prompts**: аргументы, запуск и копирование результата.
|
|
12
12
|
|
|
13
|
-

|
|
14
14
|
|
|
15
|
-

|
|
16
16
|
|
|
17
|
-

|
|
18
18
|
|
|
19
19
|
## Права и субагенты
|
|
20
20
|
|
|
@@ -22,10 +22,13 @@
|
|
|
22
22
|
- **`.dsh/agents/*.md`** реально ограничивает `tools`, `permission` и `model` ребёнка; reviewer не может править, researcher — писать.
|
|
23
23
|
- **Approval** — штатная карточка DSH: Allow once / Always allow / Reject.
|
|
24
24
|
- **`.dsh/`** создаётся автоматически: agents, commands, rules, hooks, verify, mcp, PROJECT.md.
|
|
25
|
+
- **Worktrees и фоновые агенты.** `project_worktree` создаёт ветку `dsh/<slug>` и отдельную папку `.dsh/worktrees/<slug>`; `project_agent ... worktree=auto background=true` запускает агента в изоляции, а страница **Агенты** показывает worktrees и очередь запусков с кнопкой «Остановить».
|
|
25
26
|
|
|
26
|
-

|
|
27
28
|
|
|
28
|
-
|
|
29
|
+

|
|
30
|
+
|
|
31
|
+
**v2.7.9** · MIT · проверено на DSH `0.2.1-rc.5` и `0.2.2-rc.1` (Windows 11) · без внешних зависимостей и телеметрии
|
|
29
32
|
|
|
30
33
|
**План развития:** [docs/ROADMAP-3.0.md](docs/ROADMAP-3.0.md) — Memory Fabric, семантическое
|
|
31
34
|
слияние, локальный индекс памяти, шифрованные переносы, Memory Hub и npm trusted publishing.
|
|
@@ -113,27 +116,27 @@
|
|
|
113
116
|
- перед каждой синхронизацией проверяется, что репозиторий всё ещё приватный;
|
|
114
117
|
- секреты и PII вычищаются, домашние пути заменяются токеном `%DSH_HOME%`; LLM не вызывается — **0 токенов**.
|
|
115
118
|
|
|
116
|
-

|
|
117
120
|
|
|
118
|
-

|
|
119
122
|
|
|
120
123
|
## Скриншоты
|
|
121
124
|
|
|
122
125
|
### Skills Studio: каталог, стоимость и редактор
|
|
123
126
|
|
|
124
|
-

|
|
125
128
|
|
|
126
|
-

|
|
127
130
|
|
|
128
131
|
### Token Lab: куда уходит контекст
|
|
129
132
|
|
|
130
|
-

|
|
131
134
|
|
|
132
135
|
### Тёмная тема и мобильный
|
|
133
136
|
|
|
134
|
-

|
|
135
138
|
|
|
136
|
-

|
|
137
140
|
|
|
138
141
|
## Окно-напоминание: пиковый тариф
|
|
139
142
|
|
|
@@ -143,11 +146,11 @@ DeepSeek включает двойной тариф в пиковые часы (
|
|
|
143
146
|
- **в окне** — красная карточка: причина, границы окна в местном времени и UTC, живой отсчёт до конца и рекомендация, когда вернуться;
|
|
144
147
|
- **после окна** — карточка исчезает; состояние всегда доступно через `window.__DSH_PEAK_GUARD__.state()`.
|
|
145
148
|
|
|
146
|
-

|
|
147
150
|
|
|
148
|
-

|
|
149
152
|
|
|
150
|
-

|
|
151
154
|
|
|
152
155
|
Подробности и настройки — [plugins/dsh-peak-guard/README.md](plugins/dsh-peak-guard/README.md).
|
|
153
156
|
|
package/client.js
CHANGED
|
@@ -7411,7 +7411,25 @@ var(--dsw-alias-bg-layer-1,#fff);box-shadow:inset 0 1px 0 color-mix(in srgb,#fff
|
|
|
7411
7411
|
.ag-error{padding:8px 10px;border:1px solid rgba(159,18,57,.16);border-radius:9px;background:rgba(159,18,57,.06);color:#9f1239;font-size:12px;line-height:1.4}
|
|
7412
7412
|
.ag-loading{color:var(--ag-muted);font-size:13px;padding:18px 0}
|
|
7413
7413
|
.ag-note{margin:0;color:var(--ag-muted);font-size:11px;line-height:1.45}
|
|
7414
|
-
|
|
7414
|
+
.ag-btn{font:inherit;font-size:11px;font-weight:650;border:1px solid var(--ag-line);background:var(--ag-panel);color:var(--ag-text);border-radius:8px;padding:6px 9px;cursor:pointer;min-height:30px}
|
|
7415
|
+
.ag-btn:hover{border-color:rgba(15,118,110,.35);background:rgba(15,118,110,.05)}
|
|
7416
|
+
.ag-btn:focus-visible{outline:2px solid var(--ag-accent);outline-offset:2px}
|
|
7417
|
+
.ag-section{display:flex;flex-direction:column;gap:8px;min-width:0}
|
|
7418
|
+
.ag-section h3{margin:0;font-size:15px;letter-spacing:-.01em}
|
|
7419
|
+
.ag-hint{margin:0;color:var(--ag-muted);font-size:11.5px;line-height:1.45}
|
|
7420
|
+
.ag-items{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px;min-width:0}
|
|
7421
|
+
.ag-item{border:1px solid var(--ag-line);border-radius:12px;background:var(--ag-panel);padding:11px 12px;min-width:0;display:flex;flex-direction:column;gap:5px}
|
|
7422
|
+
.ag-item-top{display:flex;justify-content:space-between;gap:8px;align-items:baseline;min-width:0}
|
|
7423
|
+
.ag-item-top code{font:inherit;font-weight:650;font-size:12px;color:var(--ag-text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
7424
|
+
.ag-item-top span{flex:none;font-size:9.5px;font-weight:750;letter-spacing:.06em;text-transform:uppercase;color:var(--ag-muted)}
|
|
7425
|
+
.ag-item p{margin:0;color:var(--ag-muted);font-size:11px;line-height:1.4;overflow-wrap:anywhere}
|
|
7426
|
+
.ag-item p.ag-task{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
|
|
7427
|
+
.ag-status-running{color:#9a3412;border-color:rgba(154,52,18,.22);background:rgba(154,52,18,.06)}
|
|
7428
|
+
.ag-status-completed{color:#0f766e;border-color:rgba(15,118,110,.22);background:rgba(15,118,110,.06)}
|
|
7429
|
+
.ag-status-failed,.ag-status-canceled{color:#9f1239;border-color:rgba(159,18,57,.18);background:rgba(159,18,57,.05)}
|
|
7430
|
+
.ag-mini-empty{padding:12px;border:1px dashed var(--ag-line);border-radius:10px;color:var(--ag-muted);font-size:11.5px;line-height:1.45;background:linear-gradient(160deg,var(--ag-soft),transparent 80%)}
|
|
7431
|
+
.ag-actions{display:flex;gap:6px;flex-wrap:wrap;align-items:center;margin-top:2px}
|
|
7432
|
+
@media(max-width:860px){.ag-grid{grid-template-columns:1fr}.ag-stats{grid-template-columns:1fr 1fr}.ag-items{grid-template-columns:1fr}}
|
|
7415
7433
|
@media(max-width:520px){.ag-stats{grid-template-columns:repeat(3,minmax(0,1fr));gap:8px}.ag-stat{padding:10px 12px}.ag-stat b{font-size:20px}.ag-stat span{font-size:9px;letter-spacing:.04em}.ag-root{gap:16px}}
|
|
7416
7434
|
@media(prefers-reduced-motion:reduce){.ag-card{transition:none}}
|
|
7417
7435
|
`;
|
|
@@ -7459,6 +7477,7 @@ var(--dsw-alias-bg-layer-1,#fff);box-shadow:inset 0 1px 0 color-mix(in srgb,#fff
|
|
|
7459
7477
|
|
|
7460
7478
|
function AgentsSection(props) {
|
|
7461
7479
|
const [state, setState] = useState({ loading: true, error: null, data: null });
|
|
7480
|
+
const [canceling, setCanceling] = useState("");
|
|
7462
7481
|
const useSessions = props && props.useSessions;
|
|
7463
7482
|
const useWorkspaces = props && props.useWorkspaces;
|
|
7464
7483
|
const sessionId = useSessions ? useSessions(currentSessionSelector) : undefined;
|
|
@@ -7468,6 +7487,16 @@ var(--dsw-alias-bg-layer-1,#fff);box-shadow:inset 0 1px 0 color-mix(in srgb,#fff
|
|
|
7468
7487
|
?? workspaces.find((item) => Array.isArray(item.sessionIds) && item.sessionIds.length > 0)
|
|
7469
7488
|
?? workspaces[0];
|
|
7470
7489
|
const cwd = typeof workspace?.path === "string" ? workspace.path : undefined;
|
|
7490
|
+
|
|
7491
|
+
const load = async () => {
|
|
7492
|
+
try {
|
|
7493
|
+
const data = await call(window.__DSH_AGENTS_CTX__, "list", { sessionId, cwd });
|
|
7494
|
+
setState({ loading: false, error: null, data });
|
|
7495
|
+
} catch (error) {
|
|
7496
|
+
setState({ loading: false, error: fallbackError(error), data: null });
|
|
7497
|
+
}
|
|
7498
|
+
};
|
|
7499
|
+
|
|
7471
7500
|
useEffect(() => {
|
|
7472
7501
|
let cancelled = false;
|
|
7473
7502
|
(async () => {
|
|
@@ -7480,7 +7509,22 @@ var(--dsw-alias-bg-layer-1,#fff);box-shadow:inset 0 1px 0 color-mix(in srgb,#fff
|
|
|
7480
7509
|
})();
|
|
7481
7510
|
return () => { cancelled = true; };
|
|
7482
7511
|
}, [sessionId, cwd]);
|
|
7512
|
+
|
|
7513
|
+
const cancel = async (id) => {
|
|
7514
|
+
setCanceling(id);
|
|
7515
|
+
try {
|
|
7516
|
+
await call(window.__DSH_AGENTS_CTX__, "cancel", { sessionId, cwd, id });
|
|
7517
|
+
await load();
|
|
7518
|
+
} catch (error) {
|
|
7519
|
+
setState((current) => ({ ...current, error: fallbackError(error) }));
|
|
7520
|
+
} finally {
|
|
7521
|
+
setCanceling("");
|
|
7522
|
+
}
|
|
7523
|
+
};
|
|
7524
|
+
|
|
7483
7525
|
const agents = state.data?.agents ?? [];
|
|
7526
|
+
const worktrees = state.data?.worktrees ?? [];
|
|
7527
|
+
const runs = state.data?.runs ?? [];
|
|
7484
7528
|
const readOnly = agents.filter((agent) => agent.permission === "read-only").length;
|
|
7485
7529
|
const models = new Set(agents.map((agent) => agent.model).filter(Boolean)).size;
|
|
7486
7530
|
return h("section", { className: "ag-root", "aria-label": "Агенты проекта" },
|
|
@@ -7488,8 +7532,9 @@ var(--dsw-alias-bg-layer-1,#fff);box-shadow:inset 0 1px 0 color-mix(in srgb,#fff
|
|
|
7488
7532
|
h("div", null,
|
|
7489
7533
|
h("p", { className: "ag-eyebrow" }, "Agents · project roles"),
|
|
7490
7534
|
h("h2", { className: "ag-title" }, "Агенты"),
|
|
7491
|
-
h("p", { className: "ag-sub" }, "Проектные роли из `.dsh/agents/*.md`. Их `tools`, `permission` и `model` применяются к дочернему агенту при запуске `project_agent`."),
|
|
7535
|
+
h("p", { className: "ag-sub" }, "Проектные роли из `.dsh/agents/*.md`. Их `tools`, `permission` и `model` применяются к дочернему агенту при запуске `project_agent`. Фоновые запуски и worktrees — здесь же."),
|
|
7492
7536
|
),
|
|
7537
|
+
h("button", { className: "ag-btn", type: "button", onClick: () => void load() }, "Обновить"),
|
|
7493
7538
|
),
|
|
7494
7539
|
state.error ? h("div", { className: "ag-error", role: "alert" }, state.error) : null,
|
|
7495
7540
|
h("div", { className: "ag-stats" },
|
|
@@ -7504,6 +7549,38 @@ var(--dsw-alias-bg-layer-1,#fff);box-shadow:inset 0 1px 0 color-mix(in srgb,#fff
|
|
|
7504
7549
|
h("pre", { className: "ag-code" }, "---\nname: reviewer\ndescription: Ревью диффа\nmodel: deepseek-flash\ntools: read, grep\npermission: read-only\n---\n\nТы ревьюер. Верни только findings."),
|
|
7505
7550
|
) : null,
|
|
7506
7551
|
agents.length > 0 ? h("div", { className: "ag-grid" }, agents.map((agent) => h(AgentCard, { key: agent.name, agent }))) : null,
|
|
7552
|
+
h("div", { className: "ag-section" },
|
|
7553
|
+
h("h3", null, "Worktrees"),
|
|
7554
|
+
h("p", { className: "ag-hint" }, "Изолированные рабочие копии: `project_worktree create` или `project_agent` с `worktree=auto`."),
|
|
7555
|
+
state.data?.worktreeError ? h("div", { className: "ag-error", role: "alert" }, state.data.worktreeError) : null,
|
|
7556
|
+
worktrees.length > 0 ? h("div", { className: "ag-items" }, worktrees.map((tree) => h("article", { className: "ag-item", key: tree.id },
|
|
7557
|
+
h("div", { className: "ag-item-top" },
|
|
7558
|
+
h("code", { title: tree.branch || tree.id }, tree.branch || tree.id),
|
|
7559
|
+
h("span", { className: tree.dirty ? "ag-status-running" : "ag-status-completed" }, tree.dirty ? "dirty" : "clean"),
|
|
7560
|
+
),
|
|
7561
|
+
h("p", { title: tree.path }, tree.path),
|
|
7562
|
+
))) : h("div", { className: "ag-mini-empty" }, "Worktree пока нет. Создайте через инструмент `project_worktree`."),
|
|
7563
|
+
),
|
|
7564
|
+
h("div", { className: "ag-section" },
|
|
7565
|
+
h("h3", null, "Фоновые запуски"),
|
|
7566
|
+
h("p", { className: "ag-hint" }, "`project_agent` с `background=true` возвращает id сразу; статус сохраняется в `.dsh/agents/runs.json`."),
|
|
7567
|
+
runs.length > 0 ? h("div", { className: "ag-items" }, runs.slice().reverse().map((run) => h("article", { className: "ag-item", key: run.id },
|
|
7568
|
+
h("div", { className: "ag-item-top" },
|
|
7569
|
+
h("code", { title: run.id }, run.name),
|
|
7570
|
+
h("span", { className: `ag-status-${run.status}` }, run.status),
|
|
7571
|
+
),
|
|
7572
|
+
run.task ? h("p", { className: "ag-task", title: run.task }, run.task) : null,
|
|
7573
|
+
h("div", { className: "ag-actions" },
|
|
7574
|
+
run.worktree?.branch ? h("p", null, run.worktree.branch) : null,
|
|
7575
|
+
run.status === "running" ? h("button", {
|
|
7576
|
+
className: "ag-btn",
|
|
7577
|
+
type: "button",
|
|
7578
|
+
disabled: canceling === run.id,
|
|
7579
|
+
onClick: () => void cancel(run.id),
|
|
7580
|
+
}, canceling === run.id ? "Останавливаем…" : "Остановить") : null,
|
|
7581
|
+
),
|
|
7582
|
+
))) : h("div", { className: "ag-mini-empty" }, "Фоновых запусков нет."),
|
|
7583
|
+
),
|
|
7507
7584
|
h("p", { className: "ag-note" }, state.data?.project ? `Проект: ${state.data.project}` : "Проект определяется по текущей рабочей папке."),
|
|
7508
7585
|
);
|
|
7509
7586
|
}
|
|
@@ -7530,7 +7607,220 @@ var(--dsw-alias-bg-layer-1,#fff);box-shadow:inset 0 1px 0 color-mix(in srgb,#fff
|
|
|
7530
7607
|
return module.exports;
|
|
7531
7608
|
})());
|
|
7532
7609
|
|
|
7533
|
-
|
|
7610
|
+
/* ---- dsh-guardrails ---- */
|
|
7611
|
+
__parts.push((function () {
|
|
7612
|
+
var module = { exports: {} };
|
|
7613
|
+
var exports = module.exports;
|
|
7614
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
7615
|
+
const React = require("react");
|
|
7616
|
+
const h = React.createElement;
|
|
7617
|
+
const useEffect = React.useEffect;
|
|
7618
|
+
const useState = React.useState;
|
|
7619
|
+
|
|
7620
|
+
const CSS = `
|
|
7621
|
+
.pr-root{--pr-line:var(--dsw-alias-border-l1,rgba(20,40,36,.13));--pr-soft:var(--dsw-alias-bg-layer-1,#f4f7f5);--pr-panel:var(--dsw-alias-bg-layer-2,#fff);--pr-text:var(--dsw-alias-label-primary,#17231f);--pr-muted:var(--dsw-alias-label-secondary,#61706b);--pr-accent:var(--dsw-alias-brand-primary,#0f766e);display:flex;flex-direction:column;gap:20px;max-width:1040px;min-width:0;color:var(--pr-text)}
|
|
7622
|
+
.pr-head{display:flex;align-items:flex-end;justify-content:space-between;gap:20px;flex-wrap:wrap}
|
|
7623
|
+
.pr-eyebrow{margin:0 0 6px;font-size:11px;font-weight:700;letter-spacing:.14em;text-transform:uppercase;color:var(--pr-accent)}
|
|
7624
|
+
.pr-title{margin:0;font-size:clamp(24px,2.6vw,32px);line-height:1.1;letter-spacing:-.025em;font-weight:680}
|
|
7625
|
+
.pr-sub{margin:8px 0 0;max-width:68ch;color:var(--pr-muted);font-size:14px;line-height:1.5}
|
|
7626
|
+
.pr-mode{padding:20px;border:1px solid var(--pr-line);border-radius:18px;background:linear-gradient(145deg,var(--pr-panel),var(--pr-soft));min-width:0}
|
|
7627
|
+
.pr-mode-top{display:flex;justify-content:space-between;align-items:flex-start;gap:12px;flex-wrap:wrap}
|
|
7628
|
+
.pr-mode h3{margin:0;font-size:24px;line-height:1.1;letter-spacing:-.03em}
|
|
7629
|
+
.pr-chips{display:flex;gap:6px;flex-wrap:wrap}
|
|
7630
|
+
.pr-chip{padding:3px 8px;border:1px solid var(--pr-line);border-radius:999px;background:var(--pr-panel);font-size:10px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--pr-muted);white-space:nowrap}
|
|
7631
|
+
.pr-chip.sandbox{color:#0f766e;border-color:rgba(15,118,110,.25);background:rgba(15,118,110,.07)}
|
|
7632
|
+
.pr-chip.approval{color:#9a3412;border-color:rgba(154,52,18,.22);background:rgba(154,52,18,.06)}
|
|
7633
|
+
.pr-mode p{margin:8px 0 0;color:var(--pr-muted);font-size:13px;line-height:1.5;max-width:70ch}
|
|
7634
|
+
.pr-note{display:flex;gap:8px;align-items:flex-start;margin-top:14px;padding:10px 12px;border:1px solid rgba(15,118,110,.16);border-radius:10px;background:rgba(15,118,110,.05);color:var(--pr-muted);font-size:12px;line-height:1.45}
|
|
7635
|
+
.pr-note b{color:var(--pr-text);font-weight:650}
|
|
7636
|
+
.pr-stats{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px}
|
|
7637
|
+
.pr-stat{padding:14px 16px;border:1px solid var(--pr-line);border-radius:14px;background:var(--pr-panel);min-width:0}
|
|
7638
|
+
.pr-stat b{display:block;font-size:24px;line-height:1.1;letter-spacing:-.03em;font-variant-numeric:tabular-nums}
|
|
7639
|
+
.pr-stat span{display:block;margin-top:5px;color:var(--pr-muted);font-size:11px;font-weight:650;letter-spacing:.07em;text-transform:uppercase}
|
|
7640
|
+
.pr-section{display:flex;flex-direction:column;gap:10px;min-width:0}
|
|
7641
|
+
.pr-section h3{margin:0;font-size:15px;letter-spacing:-.01em}
|
|
7642
|
+
.pr-section .pr-hint{margin:0;color:var(--pr-muted);font-size:12px;line-height:1.45}
|
|
7643
|
+
.pr-presets{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:8px}
|
|
7644
|
+
.pr-preset{padding:12px;border:1px solid var(--pr-line);border-radius:12px;background:var(--pr-panel);min-width:0}
|
|
7645
|
+
.pr-preset.active{border-color:rgba(15,118,110,.35);background:rgba(15,118,110,.06)}
|
|
7646
|
+
.pr-preset b{display:flex;justify-content:space-between;gap:8px;font-size:13px;align-items:center}
|
|
7647
|
+
.pr-preset span{display:block;margin-top:4px;color:var(--pr-muted);font-size:11.5px;line-height:1.4}
|
|
7648
|
+
.pr-preset em{font-style:normal;color:var(--pr-accent);font-size:10px;font-weight:700;letter-spacing:.08em;text-transform:uppercase}
|
|
7649
|
+
.pr-list{display:flex;flex-direction:column;border:1px solid var(--pr-line);border-radius:14px;overflow:hidden;background:var(--pr-panel)}
|
|
7650
|
+
.pr-row{display:flex;gap:10px;align-items:baseline;padding:9px 12px;border-top:1px solid var(--pr-line);font-size:12px;line-height:1.4;min-width:0}
|
|
7651
|
+
.pr-row:first-child{border-top:0}
|
|
7652
|
+
.pr-sev{flex:none;width:46px;font-size:9.5px;font-weight:750;letter-spacing:.08em;text-transform:uppercase}
|
|
7653
|
+
.pr-sev.deny{color:#9f1239}.pr-sev.ask{color:#9a3412}
|
|
7654
|
+
.pr-row code{flex:none;color:var(--pr-text);font:inherit;font-weight:650;white-space:nowrap}
|
|
7655
|
+
.pr-row span{color:var(--pr-muted);overflow:hidden;text-overflow:ellipsis;min-width:0}
|
|
7656
|
+
.pr-path{padding:9px 12px;border-top:1px solid var(--pr-line);font-size:12px;line-height:1.45;min-width:0}
|
|
7657
|
+
.pr-path:first-child{border-top:0}
|
|
7658
|
+
.pr-path b{display:block;font-size:12px}
|
|
7659
|
+
.pr-path code{display:block;margin-top:3px;color:var(--pr-muted);font:11.5px/1.4 ui-monospace,SFMono-Regular,Consolas,monospace;overflow-wrap:anywhere}
|
|
7660
|
+
.pr-error{padding:8px 10px;border:1px solid rgba(159,18,57,.16);border-radius:9px;background:rgba(159,18,57,.06);color:#9f1239;font-size:12px;line-height:1.4}
|
|
7661
|
+
.pr-loading{color:var(--pr-muted);font-size:13px;padding:18px 0}
|
|
7662
|
+
.pr-empty{padding:18px;border:1px dashed var(--pr-line);border-radius:14px;color:var(--pr-muted);font-size:12.5px;line-height:1.5;background:linear-gradient(160deg,var(--pr-soft),transparent 75%)}
|
|
7663
|
+
.pr-foot{display:flex;justify-content:space-between;gap:12px;flex-wrap:wrap;color:var(--pr-muted);font-size:11px;line-height:1.4}
|
|
7664
|
+
@media(max-width:860px){.pr-presets{grid-template-columns:1fr 1fr}.pr-stats{grid-template-columns:1fr 1fr}}
|
|
7665
|
+
@media(max-width:520px){.pr-presets{grid-template-columns:1fr}.pr-stats{grid-template-columns:repeat(3,minmax(0,1fr));gap:8px}.pr-stat{padding:10px 12px}.pr-stat b{font-size:20px}.pr-stat span{font-size:9px;letter-spacing:.04em}.pr-root{gap:16px}.pr-mode{padding:16px}.pr-note{flex-direction:column;gap:4px}.pr-row{display:flex;flex-wrap:wrap;gap:4px 8px}.pr-row code{white-space:normal;overflow-wrap:anywhere}.pr-row span{flex-basis:100%;padding-left:0}}
|
|
7666
|
+
`;
|
|
7667
|
+
|
|
7668
|
+
const SEVERITY_ORDER = { deny: 0, ask: 1 };
|
|
7669
|
+
|
|
7670
|
+
function currentSessionSelector(state) {
|
|
7671
|
+
try {
|
|
7672
|
+
const sessions = Object.values(state.byId || {});
|
|
7673
|
+
const main = sessions.find((session) => session && session.retainedBy && (session.retainedBy.mainView || 0) > 0);
|
|
7674
|
+
if (main) return main.id;
|
|
7675
|
+
const ids = Array.isArray(state.ids) ? state.ids : [];
|
|
7676
|
+
return ids.length > 0 ? ids[ids.length - 1] : undefined;
|
|
7677
|
+
} catch { return undefined; }
|
|
7678
|
+
}
|
|
7679
|
+
|
|
7680
|
+
async function call(ctx, operation, payload) {
|
|
7681
|
+
let signal;
|
|
7682
|
+
try { signal = AbortSignal.timeout(30000); } catch { signal = undefined; }
|
|
7683
|
+
const result = await ctx.connection.rpc.call("/api", `dsh-guardrails/${operation}`, payload ?? {}, signal);
|
|
7684
|
+
if (!result || result.ok !== true) throw new Error(result?.error?.message || "Ошибка RPC");
|
|
7685
|
+
return result.value;
|
|
7686
|
+
}
|
|
7687
|
+
|
|
7688
|
+
function errorOf(value) { return value instanceof Error ? value.message : String(value); }
|
|
7689
|
+
|
|
7690
|
+
function SandboxChip({ value }) {
|
|
7691
|
+
const labels = {
|
|
7692
|
+
"read-only": "Read only",
|
|
7693
|
+
"workspace-write": "Workspace write",
|
|
7694
|
+
"danger-full-access": "Full access",
|
|
7695
|
+
};
|
|
7696
|
+
return h("span", { className: "pr-chip sandbox" }, labels[value] || value || "sandbox");
|
|
7697
|
+
}
|
|
7698
|
+
|
|
7699
|
+
function PermissionsSection(props) {
|
|
7700
|
+
const [state, setState] = useState({ loading: true, error: null, data: null });
|
|
7701
|
+
const useSessions = props && props.useSessions;
|
|
7702
|
+
const useWorkspaces = props && props.useWorkspaces;
|
|
7703
|
+
const sessionId = useSessions ? useSessions(currentSessionSelector) : undefined;
|
|
7704
|
+
const workspaceSnapshot = useWorkspaces ? useWorkspaces((snapshot) => snapshot) : undefined;
|
|
7705
|
+
const workspaces = Array.isArray(workspaceSnapshot?.items) ? workspaceSnapshot.items : [];
|
|
7706
|
+
const workspace = workspaces.find((item) => Array.isArray(item.sessionIds) && item.sessionIds.includes(sessionId))
|
|
7707
|
+
?? workspaces.find((item) => Array.isArray(item.sessionIds) && item.sessionIds.length > 0)
|
|
7708
|
+
?? workspaces[0];
|
|
7709
|
+
const cwd = typeof workspace?.path === "string" ? workspace.path : undefined;
|
|
7710
|
+
|
|
7711
|
+
useEffect(() => {
|
|
7712
|
+
let cancelled = false;
|
|
7713
|
+
(async () => {
|
|
7714
|
+
try {
|
|
7715
|
+
const data = await call(window.__DSH_GUARDRAILS_CTX__, "permissions", { sessionId, cwd });
|
|
7716
|
+
if (!cancelled) setState({ loading: false, error: null, data });
|
|
7717
|
+
} catch (error) {
|
|
7718
|
+
if (!cancelled) setState({ loading: false, error: errorOf(error), data: null });
|
|
7719
|
+
}
|
|
7720
|
+
})();
|
|
7721
|
+
return () => { cancelled = true; };
|
|
7722
|
+
}, [sessionId, cwd]);
|
|
7723
|
+
|
|
7724
|
+
const preset = state.data?.preset;
|
|
7725
|
+
const guard = state.data?.guard;
|
|
7726
|
+
const options = Array.isArray(preset?.catalog?.options) ? preset.catalog.options : [];
|
|
7727
|
+
const rules = Array.isArray(guard?.rules)
|
|
7728
|
+
? [...guard.rules].sort((a, b) => (SEVERITY_ORDER[a.severity] ?? 9) - (SEVERITY_ORDER[b.severity] ?? 9))
|
|
7729
|
+
: [];
|
|
7730
|
+
const protectedPaths = Array.isArray(guard?.protectedPaths) ? guard.protectedPaths : [];
|
|
7731
|
+
const current = preset?.current || "custom";
|
|
7732
|
+
const spec = preset?.spec;
|
|
7733
|
+
const option = preset?.option;
|
|
7734
|
+
const title = option?.name || current;
|
|
7735
|
+
|
|
7736
|
+
return h("section", { className: "pr-root", "aria-label": "Права и ограничения" },
|
|
7737
|
+
h("div", { className: "pr-head" },
|
|
7738
|
+
h("div", null,
|
|
7739
|
+
h("p", { className: "pr-eyebrow" }, "Guardrails · effective permissions"),
|
|
7740
|
+
h("h2", { className: "pr-title" }, "Права"),
|
|
7741
|
+
h("p", { className: "pr-sub" }, "Что разрешено в этом чате и что kit запрещает в любом режиме. Режим переключается штатным picker DSH в композере — здесь только прозрачный срез."),
|
|
7742
|
+
),
|
|
7743
|
+
),
|
|
7744
|
+
state.error ? h("div", { className: "pr-error", role: "alert" }, state.error) : null,
|
|
7745
|
+
state.loading ? h("div", { className: "pr-loading" }, "Читаем эффективные права…") : null,
|
|
7746
|
+
!state.loading && state.data ? h(React.Fragment, null,
|
|
7747
|
+
h("div", { className: "pr-mode" },
|
|
7748
|
+
h("div", { className: "pr-mode-top" },
|
|
7749
|
+
h("div", null,
|
|
7750
|
+
h("h3", null, title),
|
|
7751
|
+
h("p", null, option?.description || spec?.description || "Эффективный режим этого чата."),
|
|
7752
|
+
),
|
|
7753
|
+
h("div", { className: "pr-chips" },
|
|
7754
|
+
h(SandboxChip, { value: spec?.sandbox }),
|
|
7755
|
+
h("span", { className: "pr-chip approval" }, spec?.approval === "never" ? "approval never" : "approval ask"),
|
|
7756
|
+
),
|
|
7757
|
+
),
|
|
7758
|
+
h("div", { className: "pr-note" },
|
|
7759
|
+
h("b", null, "Не второй picker."),
|
|
7760
|
+
h("span", null, "Смена режима — только в DSH. Эта страница не меняет разрешения и не хранит approvals; каждая карточка подтверждения остаётся в чате."),
|
|
7761
|
+
),
|
|
7762
|
+
),
|
|
7763
|
+
h("div", { className: "pr-stats" },
|
|
7764
|
+
h("div", { className: "pr-stat" }, h("b", null, rules.length), h("span", null, "жёстких правил")),
|
|
7765
|
+
h("div", { className: "pr-stat" }, h("b", null, protectedPaths.length), h("span", null, "защищённых путей")),
|
|
7766
|
+
h("div", { className: "pr-stat" }, h("b", null, options.length), h("span", null, "пресетов DSH")),
|
|
7767
|
+
),
|
|
7768
|
+
h("div", { className: "pr-section" },
|
|
7769
|
+
h("h3", null, "Пресеты DSH"),
|
|
7770
|
+
h("p", { className: "pr-hint" }, "Доступны в композере. Текущий выделен; kit не дублирует переключатель."),
|
|
7771
|
+
h("div", { className: "pr-presets" }, options.map((item) => h("div", { key: item.value, className: `pr-preset${item.value === current ? " active" : ""}` },
|
|
7772
|
+
h("b", null, h("span", null, item.name || item.value), item.value === current ? h("em", null, "сейчас") : null),
|
|
7773
|
+
h("span", null, item.description || "Без описания"),
|
|
7774
|
+
))),
|
|
7775
|
+
),
|
|
7776
|
+
h("div", { className: "pr-section" },
|
|
7777
|
+
h("h3", null, "Жёсткие правила kit"),
|
|
7778
|
+
h("p", { className: "pr-hint" }, "Действуют даже в Full access. Отключаются только политикой guardrails, не режимом DSH."),
|
|
7779
|
+
rules.length > 0 ? h("div", { className: "pr-list" }, rules.map((rule) => h("div", { className: "pr-row", key: rule.id },
|
|
7780
|
+
h("span", { className: `pr-sev ${rule.severity}` }, rule.severity),
|
|
7781
|
+
h("code", null, rule.id),
|
|
7782
|
+
h("span", { title: rule.description }, rule.description || ""),
|
|
7783
|
+
))) : h("div", { className: "pr-empty" }, "Правила отключены политикой guardrails."),
|
|
7784
|
+
),
|
|
7785
|
+
h("div", { className: "pr-section" },
|
|
7786
|
+
h("h3", null, "Защищённые пути"),
|
|
7787
|
+
h("p", { className: "pr-hint" }, "Запись, редактирование и удаление этих путей отклоняются до выполнения."),
|
|
7788
|
+
protectedPaths.length > 0 ? h("div", { className: "pr-list" }, protectedPaths.map((entry) => h("div", { className: "pr-path", key: entry.id },
|
|
7789
|
+
h("b", null, entry.id),
|
|
7790
|
+
h("code", null, `${entry.match} /${entry.pattern}/`),
|
|
7791
|
+
))) : h("div", { className: "pr-empty" }, "Защищённые пути отключены."),
|
|
7792
|
+
),
|
|
7793
|
+
) : null,
|
|
7794
|
+
h("div", { className: "pr-foot" },
|
|
7795
|
+
h("span", null, guard?.policyPath ? `Политика: ${guard.policyPath}` : "Политика guardrails"),
|
|
7796
|
+
h("span", null, guard ? (guard.enabled ? "hooks активны" : "hooks отключены") : ""),
|
|
7797
|
+
),
|
|
7798
|
+
);
|
|
7799
|
+
}
|
|
7800
|
+
|
|
7801
|
+
function apply(ctx) {
|
|
7802
|
+
window.__DSH_GUARDRAILS_CTX__ = ctx;
|
|
7803
|
+
ctx.effect(() => {
|
|
7804
|
+
const style = document.createElement("style");
|
|
7805
|
+
style.id = "dsh-guardrails";
|
|
7806
|
+
style.textContent = CSS;
|
|
7807
|
+
document.head.appendChild(style);
|
|
7808
|
+
return () => style.remove();
|
|
7809
|
+
}, "guardrails: styles");
|
|
7810
|
+
ctx.effect(() => ctx.slots.inject("settings.section", () => ctx.slots.register({
|
|
7811
|
+
name: "settings.section",
|
|
7812
|
+
id: "guardrails",
|
|
7813
|
+
order: 35,
|
|
7814
|
+
label: () => "Права",
|
|
7815
|
+
}, PermissionsSection)), "guardrails: settings");
|
|
7816
|
+
}
|
|
7817
|
+
|
|
7818
|
+
exports.apply = apply;
|
|
7819
|
+
exports.inject = ["slots", "connection"];
|
|
7820
|
+
return module.exports;
|
|
7821
|
+
})());
|
|
7822
|
+
|
|
7823
|
+
const __partNames = ["dsh-gkb-ru-ui","dsh-token-lab","dsh-peak-guard","dsh-session-move","dsh-profile-transfer","dsh-memory-bridge","dsh-mcp-manager","dsh-agent-extensions","dsh-guardrails"];
|
|
7534
7824
|
const __kitErrors = [];
|
|
7535
7825
|
const __pushKitError = (label, error) => {
|
|
7536
7826
|
const message = String(error && error.message ? error.message : error);
|
|
@@ -7538,7 +7828,7 @@ var(--dsw-alias-bg-layer-1,#fff);box-shadow:inset 0 1px 0 color-mix(in srgb,#fff
|
|
|
7538
7828
|
if (__kitErrors.length <= 5) console.error("[dsh-desktop-kit] registration failed:", label, message);
|
|
7539
7829
|
};
|
|
7540
7830
|
try {
|
|
7541
|
-
window.__DSH_DESKTOP_KIT__ = { version: "2.7.
|
|
7831
|
+
window.__DSH_DESKTOP_KIT__ = { version: "2.7.9", parts: __partNames, errors: __kitErrors, ready: false };
|
|
7542
7832
|
} catch {}
|
|
7543
7833
|
const __inject = [];
|
|
7544
7834
|
for (const __part of __parts) {
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# DSH Desktop Kit 2.7.8 — тест
|
|
2
|
+
|
|
3
|
+
Дата: 2026-11-12.
|
|
4
|
+
|
|
5
|
+
## Permissions UI
|
|
6
|
+
|
|
7
|
+
- Настройки → **Права**.
|
|
8
|
+
- RPC `/api/dsh-guardrails/permissions` — read-only.
|
|
9
|
+
- `permissionPresets.catalog/current/resolve/optionOf` + `loadPolicy`.
|
|
10
|
+
- Каталог пресетов, текущий режим, sandbox/approval, жёсткие правила, защищённые пути.
|
|
11
|
+
- Смена режима не дублируется: только штатный picker DSH.
|
|
12
|
+
|
|
13
|
+
## MCP prompts (из 2.7.7)
|
|
14
|
+
|
|
15
|
+
- `prompts/list`, `prompts/get`, UI-модалка, `/mcp prompts`, `/mcp prompt`.
|
|
16
|
+
|
|
17
|
+
## Проверки
|
|
18
|
+
|
|
19
|
+
- `node tools/permissions-ui-tests.mjs` — OK.
|
|
20
|
+
- `node tools/mcp-manager-tests.mjs` — OK.
|
|
21
|
+
- `npm test` — exit 0.
|
|
22
|
+
- `build:client` — 9 частей; `check:client` — OK.
|
|
23
|
+
- browser_qa audit 390/768/1440 (Permissions): overflow 0, console 0, markup 0.
|
|
24
|
+
- Скриншоты: `plugins/dsh-guardrails/screenshots/permissions-1440.png`, `permissions-390.png`.
|
|
25
|
+
|
|
26
|
+
## Ограничения
|
|
27
|
+
|
|
28
|
+
- История approvals не хранится: DSH показывает карточку в чате, kit её не дублирует.
|
|
29
|
+
- OAuth и sampling MCP не реализованы.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# DSH Desktop Kit 2.7.9 — тест
|
|
2
|
+
|
|
3
|
+
Дата: 2026-11-12.
|
|
4
|
+
|
|
5
|
+
## Worktrees
|
|
6
|
+
|
|
7
|
+
- `project_worktree action=list|create|remove`.
|
|
8
|
+
- `.dsh/worktrees/<slug>` + branch `dsh/<slug>`; путь исключён через `.git/info/exclude`.
|
|
9
|
+
- Грязный worktree не удаляется без `force`; ветка остаётся.
|
|
10
|
+
- Тест: `tools/worktrees-tests.mjs` (создание, dirty, force remove, ветка).
|
|
11
|
+
|
|
12
|
+
## Фоновые агенты
|
|
13
|
+
|
|
14
|
+
- `project_agent background=true` возвращает id сразу.
|
|
15
|
+
- `.dsh/agents/runs.json` — durable-реестр (id, childId, branch, status, preview).
|
|
16
|
+
- `status` и `cancel`; отмена через `subagents.interrupt` + dispose.
|
|
17
|
+
- Тест: `tools/background-agents-tests.mjs` (running → completed, cancel).
|
|
18
|
+
|
|
19
|
+
## UI
|
|
20
|
+
|
|
21
|
+
- Настройки → Агенты: Worktrees и Фоновые запуски.
|
|
22
|
+
- browser_qa audit 390/768/1440 — overflow 0, console 0, markup 0.
|
|
23
|
+
- Скриншоты обновлены.
|
|
24
|
+
|
|
25
|
+
## Полный прогон
|
|
26
|
+
|
|
27
|
+
- `npm test` — exit 0.
|
|
28
|
+
- `build:client` — 9 частей; `check:client` — OK.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-desktop-kit",
|
|
3
|
-
"version": "2.7.
|
|
3
|
+
"version": "2.7.9",
|
|
4
4
|
"description": "DeepSeek Harness desktop kit 2.7: 16 plugins and 30 skills — self-applying skills, token lab, Skills Studio, project memory, GitHub and USB memory transfer, guardrails, repo map, checkpoints, plan mode, browser QA, GitHub review, Claude Code-like hooks/agents/commands, Russian UI, peak guard, chat move and profile transfer.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"deepseek-harness",
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
"build:client": "node tools/build-client.mjs",
|
|
75
75
|
"build:skills": "node tools/build-skill-manifest.mjs",
|
|
76
76
|
"check:client": "node tools/check-client.mjs client.js",
|
|
77
|
-
"test": "node tools/smoke-tests.mjs && node tools/memory-tests.mjs && node tools/skills-sync-tests.mjs && node tools/kit-upgrade-tests.mjs && node plugins/dsh-profile-transfer/test/transfer.test.mjs && node --test plugins/dsh-memory-bridge/test/bridge.test.mjs plugins/dsh-memory-bridge/test/github.test.mjs plugins/dsh-memory-bridge/test/merge.test.mjs plugins/dsh-memory-bridge/test/user-specific.test.mjs && node tools/repo-map-tests.mjs && node tools/checkpoints-tests.mjs && node tools/plan-mode-tests.mjs && node tools/browser-qa-tests.mjs && node tools/ui-gate-tests.mjs && node tools/agent-extensions-tests.mjs && node tools/mcp-manager-tests.mjs && node tools/github-review-tests.mjs && node plugins/dsh-guardrails/tools/guardrails-tests.mjs",
|
|
77
|
+
"test": "node tools/smoke-tests.mjs && node tools/memory-tests.mjs && node tools/skills-sync-tests.mjs && node tools/kit-upgrade-tests.mjs && node plugins/dsh-profile-transfer/test/transfer.test.mjs && node --test plugins/dsh-memory-bridge/test/bridge.test.mjs plugins/dsh-memory-bridge/test/github.test.mjs plugins/dsh-memory-bridge/test/merge.test.mjs plugins/dsh-memory-bridge/test/user-specific.test.mjs && node tools/repo-map-tests.mjs && node tools/checkpoints-tests.mjs && node tools/plan-mode-tests.mjs && node tools/browser-qa-tests.mjs && node tools/ui-gate-tests.mjs && node tools/agent-extensions-tests.mjs && node tools/mcp-manager-tests.mjs && node tools/github-review-tests.mjs && node tools/worktrees-tests.mjs && node tools/background-agents-tests.mjs && node tools/permissions-ui-tests.mjs && node plugins/dsh-guardrails/tools/guardrails-tests.mjs",
|
|
78
78
|
"skills:cost": "node tools/skill-cost.mjs",
|
|
79
79
|
"skills:check": "node tools/skill-cost.mjs --check",
|
|
80
80
|
"release": "node tools/release.mjs"
|
|
@@ -4,7 +4,7 @@ Claude Code-like project extensions for DSH, all opt-in and project-local.
|
|
|
4
4
|
|
|
5
5
|
## Settings → Агенты
|
|
6
6
|
|
|
7
|
-
Экран показывает все `.dsh/agents/*.md`: описание, enforced permission, tools и модель.
|
|
7
|
+
Экран показывает все `.dsh/agents/*.md`: описание, enforced permission, tools и модель. Ниже — активные **worktrees** и **фоновые запуски** с кнопкой «Остановить».
|
|
8
8
|
|
|
9
9
|

|
|
10
10
|
|
|
@@ -68,6 +68,29 @@ project_agent action=run name=reviewer task="Проверь текущий ди
|
|
|
68
68
|
|
|
69
69
|
The child runs in its own context through the DSH `subagents` service and returns only its final text.
|
|
70
70
|
|
|
71
|
+
Background and isolated runs:
|
|
72
|
+
|
|
73
|
+
```text
|
|
74
|
+
project_agent action=run name=reviewer task="..." background=true
|
|
75
|
+
project_agent action=status
|
|
76
|
+
project_agent action=cancel id=<run-id>
|
|
77
|
+
|
|
78
|
+
project_agent action=run name=reviewer task="..." worktree=auto
|
|
79
|
+
project_worktree action=list
|
|
80
|
+
project_worktree action=create slug=feature-x base=HEAD
|
|
81
|
+
project_worktree action=remove slug=feature-x
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
A background run returns immediately with a durable id and writes a small record
|
|
85
|
+
to `.dsh/agents/runs.json` (status, worktree/branch, short result preview; no
|
|
86
|
+
transcript). The child keeps running after the parent turn. `cancel` interrupts
|
|
87
|
+
the live child and marks the record canceled.
|
|
88
|
+
|
|
89
|
+
Worktrees live in `.dsh/worktrees/<slug>` on branch `dsh/<slug>`. The path is
|
|
90
|
+
added to `.git/info/exclude`, not to the tracked `.gitignore`, so the main
|
|
91
|
+
worktree stays clean. `remove` refuses a dirty worktree unless `force=true`;
|
|
92
|
+
the branch is kept for review.
|
|
93
|
+
|
|
71
94
|
`tools`, `permission` and `model` are enforced, not just documentation:
|
|
72
95
|
|
|
73
96
|
- `tools: read, grep` — the child receives a deny for any other tool;
|