@toddzheng024/dscode-bundle 0.1.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/LICENSE +21 -0
- package/README.md +5 -0
- package/THIRD_PARTY_NOTICES.md +11 -0
- package/bin/apply_patch +4 -0
- package/bootstrap.mjs +21 -0
- package/cordis.patch.yml +756 -0
- package/package.json +327 -0
- package/plugins/auto-review/audit.mjs +20 -0
- package/plugins/auto-review/index.mjs +181 -0
- package/plugins/auto-review/policy.mjs +59 -0
- package/plugins/dscode/index.mjs +35 -0
- package/plugins/session-metrics/index.mjs +44 -0
- package/plugins/session-metrics/pricing.mjs +18 -0
- package/plugins/session-metrics/store.mjs +24 -0
- package/plugins/session-metrics/view.mjs +58 -0
- package/plugins/tui-tools/hooks.mjs +23 -0
- package/plugins/tui-tools/index.mjs +164 -0
- package/plugins/ultra/policy.mjs +11 -0
- package/presets/dscode/agent.cordis.yml +288 -0
- package/presets/dscode/preset.yml +3 -0
- package/vendor/bash/LICENSE +21 -0
- package/vendor/bash/index.js +449 -0
- package/vendor/bash/types/background.d.ts +19 -0
- package/vendor/bash/types/index.d.ts +22 -0
- package/vendor/bash/types/render.d.ts +38 -0
- package/vendor/deepseek/LICENSE +21 -0
- package/vendor/deepseek/index.js +2102 -0
- package/vendor/deepseek/types/adapter.d.ts +163 -0
- package/vendor/deepseek/types/file-id.d.ts +19 -0
- package/vendor/deepseek/types/file-store.d.ts +83 -0
- package/vendor/deepseek/types/files-api.d.ts +103 -0
- package/vendor/deepseek/types/image-tokens.d.ts +19 -0
- package/vendor/deepseek/types/index.d.ts +106 -0
- package/vendor/deepseek/types/request-pricing.d.ts +48 -0
- package/vendor/deepseek/types/serialize.d.ts +85 -0
- package/vendor/deepseek/types/sse.d.ts +24 -0
- package/vendor/deepseek/types/translate.d.ts +37 -0
- package/vendor/deepseek/types/types.d.ts +178 -0
- package/vendor/deepseek/types/upload-index.d.ts +68 -0
- package/vendor/persistent/LICENSE +21 -0
- package/vendor/persistent/index.js +386 -0
- package/vendor/persistent/types/index.d.ts +24 -0
- package/vendor/tui/LICENSE +21 -0
- package/vendor/tui/devtools-CdTl3MNy.mjs +3643 -0
- package/vendor/tui/index.mjs +39105 -0
- package/vendor/tui/invariant.mjs +21 -0
- package/vendor/tui/rolldown-runtime-CMFfr-1z.mjs +26 -0
- package/vendor/tui/startup.mjs +109 -0
- package/vendor/tui/theme-DCT8Y2xf.mjs +628 -0
- package/vendor/tui/types/app.d.ts +304 -0
- package/vendor/tui/types/approval.d.ts +59 -0
- package/vendor/tui/types/attachments.d.ts +52 -0
- package/vendor/tui/types/authorization-panel.d.ts +22 -0
- package/vendor/tui/types/authorization.d.ts +36 -0
- package/vendor/tui/types/commands.d.ts +52 -0
- package/vendor/tui/types/editor-keys.d.ts +105 -0
- package/vendor/tui/types/editor.d.ts +6 -0
- package/vendor/tui/types/fork.d.ts +8 -0
- package/vendor/tui/types/git-workflow.d.ts +32 -0
- package/vendor/tui/types/history.d.ts +97 -0
- package/vendor/tui/types/index.d.ts +124 -0
- package/vendor/tui/types/input-split.d.ts +54 -0
- package/vendor/tui/types/internals.d.ts +26 -0
- package/vendor/tui/types/invariant.d.ts +15 -0
- package/vendor/tui/types/kernel-panels.d.ts +167 -0
- package/vendor/tui/types/keyboard.d.ts +80 -0
- package/vendor/tui/types/mentions.d.ts +81 -0
- package/vendor/tui/types/model-capabilities.d.ts +82 -0
- package/vendor/tui/types/models.d.ts +119 -0
- package/vendor/tui/types/permissions.d.ts +27 -0
- package/vendor/tui/types/plugin-inventory.d.ts +11 -0
- package/vendor/tui/types/presets.d.ts +22 -0
- package/vendor/tui/types/provider-settings.d.ts +239 -0
- package/vendor/tui/types/questions.d.ts +54 -0
- package/vendor/tui/types/render/animations.d.ts +265 -0
- package/vendor/tui/types/render/editor.d.ts +162 -0
- package/vendor/tui/types/render/export.d.ts +9 -0
- package/vendor/tui/types/render/fuzzy.d.ts +21 -0
- package/vendor/tui/types/render/inspector.d.ts +36 -0
- package/vendor/tui/types/render/lines.d.ts +66 -0
- package/vendor/tui/types/render/markdown.d.ts +29 -0
- package/vendor/tui/types/render/projection.d.ts +461 -0
- package/vendor/tui/types/render/status.d.ts +196 -0
- package/vendor/tui/types/render/text.d.ts +58 -0
- package/vendor/tui/types/render/tool-detail.d.ts +94 -0
- package/vendor/tui/types/render/tool-preview.d.ts +28 -0
- package/vendor/tui/types/render/width.d.ts +29 -0
- package/vendor/tui/types/session-directory.d.ts +173 -0
- package/vendor/tui/types/session-switch.d.ts +17 -0
- package/vendor/tui/types/settings-file.d.ts +41 -0
- package/vendor/tui/types/skills.d.ts +47 -0
- package/vendor/tui/types/startup.d.ts +65 -0
- package/vendor/tui/types/store.d.ts +58 -0
- package/vendor/tui/types/subagents.d.ts +70 -0
- package/vendor/tui/types/theme-panel.d.ts +24 -0
- package/vendor/tui/types/theme.d.ts +215 -0
- package/vendor/tui/types/version.d.ts +18 -0
- package/vendor/tui/types/whale-glyph.d.ts +6 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { appendMetric } from './store.mjs';
|
|
3
|
+
import { estimateCost, PRICE_VERSION } from './pricing.mjs';
|
|
4
|
+
import { setMetricSource } from './view.mjs';
|
|
5
|
+
export const name = 'dscode-session-metrics';
|
|
6
|
+
export const inject = ['llm', 'agents', 'tokenMeter', 'sessionProjections'];
|
|
7
|
+
export function apply(ctx) {
|
|
8
|
+
const snapshots = new WeakMap();
|
|
9
|
+
ctx.effect(() => setMetricSource(id => {
|
|
10
|
+
const agent = ctx.agents.get(id);
|
|
11
|
+
if (!agent) return undefined;
|
|
12
|
+
const session = agent.session;
|
|
13
|
+
const cached = snapshots.get(session);
|
|
14
|
+
if (cached?.seq === session.seq) return cached.value;
|
|
15
|
+
const state = ctx.sessionProjections.stateOf(session, 'contextPressure');
|
|
16
|
+
const measurement = ctx.tokenMeter.measure(session);
|
|
17
|
+
const value = { events: session.snapshotEvents(), used: measurement.totalTokens, capacity: state?.contextWindow };
|
|
18
|
+
snapshots.set(session, { seq: session.seq, value });
|
|
19
|
+
return value;
|
|
20
|
+
}));
|
|
21
|
+
const home = process.env.DSH_HOME;
|
|
22
|
+
const record = (id, entry) => { try { appendMetric(home, id, entry); } catch { ctx.logger.warn('Session cost telemetry could not be saved.'); } };
|
|
23
|
+
ctx.on('llm/stream', async function* (options, next) {
|
|
24
|
+
if (!options.sessionId || !home) { yield* next(); return; }
|
|
25
|
+
const id = randomUUID(), time = Date.now();
|
|
26
|
+
const recipients = new Set([options.sessionId]);
|
|
27
|
+
let child = ctx.agents.get(options.sessionId);
|
|
28
|
+
while (child?.session.header.origin === 'subagent' && child.session.header.parentSession && !recipients.has(child.session.header.parentSession)) {
|
|
29
|
+
recipients.add(child.session.header.parentSession);
|
|
30
|
+
child = ctx.agents.get(child.session.header.parentSession);
|
|
31
|
+
}
|
|
32
|
+
const save = entry => { for (const recipient of recipients) record(recipient, { ...entry, sessionId: options.sessionId }); };
|
|
33
|
+
save( { kind: 'start', id, time, provider: options.provider, model: options.model, purpose: options.purpose ?? 'agent' });
|
|
34
|
+
let usage;
|
|
35
|
+
try {
|
|
36
|
+
for await (const chunk of next()) {
|
|
37
|
+
if (chunk.type === 'usage') usage = chunk.usage;
|
|
38
|
+
yield chunk;
|
|
39
|
+
}
|
|
40
|
+
} finally {
|
|
41
|
+
save({ kind: 'end', id, time, usage: usage ?? null, cost: estimateCost(options.provider, options.model, usage, time), priceVersion: PRICE_VERSION });
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// USD per million tokens. Snapshot of the official page opened 2026-09-11.
|
|
2
|
+
// https://api-docs.deepseek.com/quick_start/pricing/
|
|
3
|
+
export const PRICE_SOURCE = 'https://api-docs.deepseek.com/quick_start/pricing/';
|
|
4
|
+
export const PRICE_VERSION = 'deepseek-2026-09-11';
|
|
5
|
+
export function estimateCost(provider, model, usage, time) {
|
|
6
|
+
if (provider !== 'deepseek-official' || !usage || !Number.isFinite(time)) return null;
|
|
7
|
+
// Earlier requests require an older price table; never back-price them at today's rate.
|
|
8
|
+
if (time < Date.UTC(2026, 8, 11)) return null;
|
|
9
|
+
const flash = ['deepseek-flash', 'deepseek-v4-flash', 'deepseek-v4-flash-vision-exp'].includes(model)
|
|
10
|
+
|| model === 'deepseek-v4-pro' && time >= Date.UTC(2026, 8, 14, 4);
|
|
11
|
+
if (!flash && model !== 'deepseek-v4-pro') return null;
|
|
12
|
+
const date = new Date(time), day = date.getUTCDay(), hour = date.getUTCHours();
|
|
13
|
+
const peak = day >= 1 && day <= 5 && (hour >= 1 && hour < 4 || hour >= 6 && hour < 10);
|
|
14
|
+
const [read, input, output] = flash ? [0.003, 0.15, 0.6] : [0.022, 0.66, 1.98];
|
|
15
|
+
const values = [usage.inputTokens, usage.outputTokens, usage.cacheReadTokens ?? 0, usage.cacheWriteTokens ?? 0];
|
|
16
|
+
if (!values.every(n => Number.isFinite(n) && n >= 0) || values[3] !== 0) return null;
|
|
17
|
+
return (values[0] * input + values[1] * output + values[2] * read) * (peak ? 2 : 1) / 1e6;
|
|
18
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { appendFileSync, mkdirSync, readFileSync, statSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { createHash } from 'node:crypto';
|
|
4
|
+
const cache = new Map();
|
|
5
|
+
export const ledgerPath = (home, id) => join(home, 'session-metrics', createHash('sha256').update(id).digest('hex') + '.jsonl');
|
|
6
|
+
export function appendMetric(home, id, entry) {
|
|
7
|
+
const path = ledgerPath(home, id);
|
|
8
|
+
mkdirSync(join(home, 'session-metrics'), { recursive: true, mode: 0o700 });
|
|
9
|
+
appendFileSync(path, JSON.stringify(entry) + '\n', { mode: 0o600, flush: true });
|
|
10
|
+
}
|
|
11
|
+
export function readMetrics(home, id) {
|
|
12
|
+
const path = ledgerPath(home, id);
|
|
13
|
+
try {
|
|
14
|
+
const st = statSync(path), key = `${st.mtimeMs}:${st.size}`;
|
|
15
|
+
if (cache.get(path)?.key === key) return cache.get(path).value;
|
|
16
|
+
let corrupt = false;
|
|
17
|
+
const rows = readFileSync(path, 'utf8').split('\n').filter(Boolean).flatMap(line => {
|
|
18
|
+
try { return [JSON.parse(line)]; } catch { corrupt = true; return []; }
|
|
19
|
+
});
|
|
20
|
+
const value = { rows, corrupt };
|
|
21
|
+
cache.set(path, { key, value });
|
|
22
|
+
return value;
|
|
23
|
+
} catch (e) { return { rows: [], corrupt: e.code !== 'ENOENT' }; }
|
|
24
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { readMetrics } from './store.mjs';
|
|
2
|
+
import { estimateCost } from './pricing.mjs';
|
|
3
|
+
let source;
|
|
4
|
+
export function setMetricSource(next) { source = next; return () => { if (source === next) source = undefined; }; }
|
|
5
|
+
export function summarize(rows, events = [], corrupt = false) {
|
|
6
|
+
const requests = new Map();
|
|
7
|
+
let first = Infinity;
|
|
8
|
+
for (const row of rows) {
|
|
9
|
+
if (row.kind === 'start') first = Math.min(first, row.time);
|
|
10
|
+
requests.set(row.id, { ...requests.get(row.id), ...row });
|
|
11
|
+
}
|
|
12
|
+
// Backfill old main/compaction calls from the durable log, only before the new ledger.
|
|
13
|
+
let route = {};
|
|
14
|
+
const history = [];
|
|
15
|
+
for (const event of events) {
|
|
16
|
+
if (event.type === 'request/header') route = event.data.header.config;
|
|
17
|
+
if (event.time >= first) continue;
|
|
18
|
+
if (event.type === 'assistant/message' || event.type === 'compaction/summary' && event.data.llmStreamCall) {
|
|
19
|
+
const r = event.type === 'compaction/summary' ? event.data : route;
|
|
20
|
+
history.push({ usage: event.data.usage, cost: estimateCost(r.provider, r.model, event.data.usage, event.time), kind: 'end' });
|
|
21
|
+
} else if (event.type === 'assistant/attempt') history.push({ kind: 'end', cost: null });
|
|
22
|
+
}
|
|
23
|
+
let cost = 0, unknown = corrupt || history.length > 0, input = 0, hit = 0, cacheUnknown = false, calls = 0, pending = 0;
|
|
24
|
+
// Historical overhead (review/title) was not recorded by this collector: mark partial.
|
|
25
|
+
for (const row of [...history, ...requests.values()]) {
|
|
26
|
+
calls++;
|
|
27
|
+
if (row.kind !== 'end') { pending++; continue; }
|
|
28
|
+
if (!Number.isFinite(row.cost)) unknown = true;
|
|
29
|
+
else cost += row.cost;
|
|
30
|
+
const u = row.usage;
|
|
31
|
+
if (!u || !Number.isFinite(u.inputTokens) || !Number.isFinite(u.outputTokens)) { cacheUnknown = true; continue; }
|
|
32
|
+
const total = u.inputTokens + (u.cacheReadTokens ?? 0) + (u.cacheWriteTokens ?? 0);
|
|
33
|
+
input += total; hit += u.cacheReadTokens ?? 0;
|
|
34
|
+
if (total > 0 && u.cacheReadTokens === undefined) cacheUnknown = true;
|
|
35
|
+
}
|
|
36
|
+
return { cost, unknown, calls, pending, cache: input > 0 && !cacheUnknown ? Math.min(100, hit / input * 100) : null };
|
|
37
|
+
}
|
|
38
|
+
export function formatFooter(metrics, context, columns = 80) {
|
|
39
|
+
const ctx = Number.isFinite(context) ? `${Math.round(context)}%` : '--';
|
|
40
|
+
const cache = metrics.cache === null ? '--' : `${metrics.cache.toFixed(1)}%`;
|
|
41
|
+
const dollars = metrics.unknown && metrics.cost === 0 ? '--' : `~$${metrics.cost.toFixed(metrics.cost < 1 ? 4 : 2)}${metrics.unknown ? '+' : ''}${metrics.pending ? '…' : ''}`;
|
|
42
|
+
const full = `ctx ${ctx} · session ${dollars} · cache ${cache}`;
|
|
43
|
+
if (full.length <= columns) return full;
|
|
44
|
+
const short = `ctx ${ctx} $ ${dollars.replace('~$', '~')} hit ${cache}`;
|
|
45
|
+
if (short.length <= columns) return short;
|
|
46
|
+
const tiny = `${ctx} ${dollars} ${cache}`;
|
|
47
|
+
return tiny.length <= columns ? tiny : tiny.slice(0, Math.max(0, columns));
|
|
48
|
+
}
|
|
49
|
+
export function footerFor(id, stats, columns) {
|
|
50
|
+
try {
|
|
51
|
+
const data = id ? source?.(id) : undefined;
|
|
52
|
+
const ledger = id && process.env.DSH_HOME ? readMetrics(process.env.DSH_HOME, id) : { rows: [], corrupt: false };
|
|
53
|
+
const summary = summarize(ledger.rows, data?.events ?? [], ledger.corrupt);
|
|
54
|
+
const used = data?.used;
|
|
55
|
+
const capacity = data?.capacity ?? stats.contextWindow;
|
|
56
|
+
return formatFooter(summary, Number.isFinite(used) && capacity > 0 ? used / capacity * 100 : undefined, columns);
|
|
57
|
+
} catch { return formatFooter({ cost: 0, unknown: true, cache: null }, undefined, columns); }
|
|
58
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export const hookEvents = ['SessionStart', 'UserPromptSubmit', 'PreToolUse', 'PostToolUse', 'Stop'];
|
|
2
|
+
// Fail visibly instead of letting the upstream compatibility bridge silently skip gates.
|
|
3
|
+
export function validateHooks(raw) {
|
|
4
|
+
const hooks = raw?.hooks ?? raw;
|
|
5
|
+
if (!hooks || typeof hooks !== 'object' || Array.isArray(hooks)) throw new Error('hooks must be an event map');
|
|
6
|
+
for (const [event, groups] of Object.entries(hooks)) {
|
|
7
|
+
if (!hookEvents.includes(event)) throw new Error(`Unsupported hook event: ${event}`);
|
|
8
|
+
if (!Array.isArray(groups)) throw new Error(`${event}: expected matcher groups`);
|
|
9
|
+
for (const group of groups) {
|
|
10
|
+
if (!group || !Array.isArray(group.hooks)) throw new Error(`${event}: expected hooks array`);
|
|
11
|
+
if (group.matcher !== undefined) {
|
|
12
|
+
if (typeof group.matcher !== 'string') throw new Error(`${event}: matcher must be a regex string`);
|
|
13
|
+
new RegExp(group.matcher);
|
|
14
|
+
}
|
|
15
|
+
for (const hook of group.hooks) {
|
|
16
|
+
if (!hook || (hook.type !== undefined && hook.type !== 'command') || hook.async === true || typeof hook.command !== 'string' || !hook.command.trim()) throw new Error(`${event}: only synchronous command hooks are supported`);
|
|
17
|
+
const timeout = hook.timeout ?? hook.timeoutSec;
|
|
18
|
+
if (timeout !== undefined && (!Number.isFinite(timeout) || timeout <= 0 || timeout > 600)) throw new Error(`${event}: timeout must be 0–600 seconds, excluding zero`);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return hooks;
|
|
23
|
+
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { readFile, readdir, access } from 'node:fs/promises';
|
|
2
|
+
import { dirname, join, resolve } from 'node:path';
|
|
3
|
+
import { parse } from 'yaml';
|
|
4
|
+
import { hookEvents, validateHooks } from './hooks.mjs';
|
|
5
|
+
import { redact } from '../auto-review/policy.mjs';
|
|
6
|
+
|
|
7
|
+
export const name = 'dscode-tui-tools';
|
|
8
|
+
export const inject = ['commands', 'agents', 'tools', 'skills', 'permissionPresets'];
|
|
9
|
+
const phases = ['pending', 'loading', 'active', 'failed', 'unavailable', 'unloading'];
|
|
10
|
+
const ok = text => ({ kind: 'success', text });
|
|
11
|
+
const fail = text => ({ kind: 'error', text });
|
|
12
|
+
const show = value => typeof value === 'string' ? value : JSON.stringify(value ?? null);
|
|
13
|
+
const exists = path => access(path).then(() => true, () => false);
|
|
14
|
+
|
|
15
|
+
export async function findConflicts(cwd, configs, winners, env = process.env) {
|
|
16
|
+
let project = resolve(cwd);
|
|
17
|
+
while (!await exists(join(project, '.git')) && dirname(project) !== project) project = dirname(project);
|
|
18
|
+
if (!await exists(join(project, '.git'))) project = resolve(cwd);
|
|
19
|
+
const roots = [];
|
|
20
|
+
const skipSystemRoots = new Set();
|
|
21
|
+
for (const config of configs) {
|
|
22
|
+
if (config.includeDefaultRoots !== false) {
|
|
23
|
+
roots.push(join(project, '.dsh/skills'), join(project, '.agents/skills'));
|
|
24
|
+
const dshHome = config.dshHome ?? env.DSH_HOME;
|
|
25
|
+
const agentsHome = config.agentsHome ?? env.DSH_AGENTS_HOME;
|
|
26
|
+
if (typeof dshHome === 'string') { const path = resolve(dshHome, 'skills'); roots.push(path); skipSystemRoots.add(path); }
|
|
27
|
+
if (typeof agentsHome === 'string') roots.push(resolve(agentsHome, 'skills'));
|
|
28
|
+
}
|
|
29
|
+
roots.push(...(config.customSkillDirs ?? []).filter(x => typeof x === 'string').map(x => resolve(x)));
|
|
30
|
+
const bundled = config.bundledSkillDir ?? (config.includeDefaultRoots !== false ? env.DSH_BUNDLED_SKILL_DIR : undefined);
|
|
31
|
+
if (typeof bundled === 'string') roots.push(resolve(bundled));
|
|
32
|
+
}
|
|
33
|
+
const candidates = new Map();
|
|
34
|
+
const errors = [];
|
|
35
|
+
for (const root of new Set(roots)) {
|
|
36
|
+
let entries;
|
|
37
|
+
try { entries = await readdir(root, { withFileTypes: true }); }
|
|
38
|
+
catch (e) { if (e.code !== 'ENOENT') errors.push(`${root}: ${e.code}`); continue; }
|
|
39
|
+
for (const entry of entries) {
|
|
40
|
+
if (entry.name === '.system' && skipSystemRoots.has(root)) continue;
|
|
41
|
+
const path = entry.isFile() && entry.name.endsWith('.md') ? join(root, entry.name) : join(root, entry.name, 'SKILL.md');
|
|
42
|
+
try {
|
|
43
|
+
const raw = await readFile(path, 'utf8');
|
|
44
|
+
const match = raw.match(/^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/);
|
|
45
|
+
const meta = match && parse(match[1]);
|
|
46
|
+
if (typeof meta?.name !== 'string' || typeof meta.description !== 'string') continue;
|
|
47
|
+
const list = candidates.get(meta.name) ?? [];
|
|
48
|
+
list.push(path); candidates.set(meta.name, list);
|
|
49
|
+
} catch (e) { if (!['ENOENT', 'ENOTDIR'].includes(e.code)) errors.push(`${path}: invalid or unreadable`); }
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
const lines = [];
|
|
53
|
+
for (const [name, paths] of candidates) if (paths.length > 1) {
|
|
54
|
+
const winner = winners.find(s => s.name === name);
|
|
55
|
+
lines.push(`${name}: effective source=${winner?.source ?? 'not in catalog'} provider=${winner?.provider ?? '?'}\n${paths.map(p => ` ${p}`).join('\n')}`);
|
|
56
|
+
}
|
|
57
|
+
return [...lines, ...errors, 'Scope: filesystem roots only; runtime/remote provider shadowed candidates are not exposed by DSH.'].join('\n');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function apply(ctx) {
|
|
61
|
+
const entries = () => [...(ctx.get('loader')?.entries() ?? [])].filter(e => e.options.group !== true);
|
|
62
|
+
const mcps = () => entries().filter(e => e.options.name === '@deepseek-ai/dsh-mcp-client');
|
|
63
|
+
const hook = () => entries().find(e => e.options.id === 'dscode-hooks');
|
|
64
|
+
const state = e => e.disabled ? 'disabled' : phases[e.fiber?.state] ?? 'unavailable';
|
|
65
|
+
let changing = false;
|
|
66
|
+
const mutate = async fn => {
|
|
67
|
+
if (changing || ctx.agents.list().some(a => a.status === 'running')) return fail('Wait until all agents are idle before changing process-wide plugins.');
|
|
68
|
+
changing = true;
|
|
69
|
+
try { return await fn(); } finally { changing = false; }
|
|
70
|
+
};
|
|
71
|
+
const register = (name, description, handler) => ctx.commands.register({ name, description, handler: async inv => {
|
|
72
|
+
try { const result = await handler(inv); return { ...result, text: redact(result.text ?? '') }; }
|
|
73
|
+
catch (e) { return fail(redact(`${name}: ${e.message}`)); }
|
|
74
|
+
}});
|
|
75
|
+
register('status', 'Session, model, permissions, usage and plugin health', ({ agent }) => {
|
|
76
|
+
const session = agent.session;
|
|
77
|
+
const route = session.requestHeader()?.config ?? agent.options;
|
|
78
|
+
const events = session.snapshotEvents();
|
|
79
|
+
const projections = ctx.get('sessionProjections');
|
|
80
|
+
const usage = projections?.stateOf(session, 'tokenUsage')?.totals;
|
|
81
|
+
const pressure = projections?.stateOf(session, 'contextPressure');
|
|
82
|
+
return ok([
|
|
83
|
+
`Session: ${session.id}`, `Workspace: ${session.header.cwd ?? process.cwd()}`,
|
|
84
|
+
`Agent: ${agent.status} | preset: ${session.header.agentPreset ?? 'standard'}`,
|
|
85
|
+
`Model: ${route?.provider ?? 'default'} / ${route?.model ?? 'default'}`,
|
|
86
|
+
`Effort: ${route?.reasoningEffort ?? 'model default'}${route?.reasoningEffort === 'ultra' ? ' (DeepSeek wire: max; collaboration enabled)' : ''}`,
|
|
87
|
+
`Permission: ${show(ctx.permissionPresets.current(session))}`,
|
|
88
|
+
`Tokens: ${show(usage ?? 'no provider usage yet')}`,
|
|
89
|
+
`Context: ${pressure?.pressureTokens ?? pressure?.surfaceTokens ?? '?'} / ${pressure?.contextWindow ?? '?'} tokens`,
|
|
90
|
+
`Events: ${events.length}; /review-usage shows reviewer tokens`,
|
|
91
|
+
`Tools: ${ctx.tools.schemas(agent).length}; MCP entries: ${mcps().length}`,
|
|
92
|
+
`Plugins: ${entries().filter(e => state(e) === 'active').length} active, ${entries().filter(e => state(e) === 'failed').length} failed`,
|
|
93
|
+
'Use /doctor for diagnostics; /statusline for live context/token display.',
|
|
94
|
+
].join('\n'));
|
|
95
|
+
});
|
|
96
|
+
register('doctor', 'Read-only runtime diagnostics (no model requests)', async ({ agent, signal }) => {
|
|
97
|
+
const tools = ctx.tools.schemas(agent).map(t => t.name);
|
|
98
|
+
const catalog = await ctx.skills.snapshot({ cwd: agent.session.header.cwd, scope: agent, signal });
|
|
99
|
+
const computer = ctx.get('computerUse');
|
|
100
|
+
return ok([
|
|
101
|
+
`Node: ${process.version}; platform: ${process.platform}/${process.arch}`,
|
|
102
|
+
...entries().filter(e => !e.disabled && state(e) !== 'active').map(e => `CHECK plugin ${e.id}: ${state(e)}`),
|
|
103
|
+
`Skills: ${catalog.skills.length}; discovery ${catalog.complete ? 'complete' : 'incomplete'}`,
|
|
104
|
+
`Core tools: ${['bash', 'skill', 'computer_use_activate'].map(n => `${n}=${tools.includes(n)}`).join(', ')}`,
|
|
105
|
+
...mcps().map(e => `MCP ${e.id}: ${state(e)}; ${tools.filter(n => n.startsWith(`mcp__${e.options.config?.serverName}__`)).length} registered tools`),
|
|
106
|
+
`Computer Use: ${computer ? show(computer.status()) : 'service unavailable'}`,
|
|
107
|
+
'Credentials and remote model access: not tested. No credential values are read or printed.',
|
|
108
|
+
'For deterministic execution checks outside this session: npm run doctor (in the installation directory).',
|
|
109
|
+
].join('\n'));
|
|
110
|
+
});
|
|
111
|
+
register('mcp', 'MCP list, tools <id>, enable/disable/reconnect <id>', async ({ agent, rawInput }) => {
|
|
112
|
+
const [action = 'list', id, extra] = rawInput.trim().split(/\s+/).filter(Boolean);
|
|
113
|
+
const list = mcps();
|
|
114
|
+
if (action === 'list') return ok(list.map(e => `${e.id}: ${state(e)} | server=${show(e.options.config?.serverName)} | transport=${show(e.options.config?.transport)}`).join('\n') + '\n/mcp tools|enable|disable|reconnect <entry-id> — changes last for this process; persist startup config in mcp.local.yml.');
|
|
115
|
+
const matches = list.filter(e => e.id === id || e.options.id === id);
|
|
116
|
+
const entry = matches.length === 1 ? matches[0] : undefined;
|
|
117
|
+
if (!entry || extra) return fail('Usage: /mcp [list | tools|enable|disable|reconnect <entry-id>]');
|
|
118
|
+
if (action === 'tools') return ok(ctx.tools.schemas(agent).filter(t => t.name.startsWith(`mcp__${entry.options.config?.serverName}__`)).map(t => t.name).join('\n') || 'No tools currently registered.');
|
|
119
|
+
if (!['enable', 'disable', 'reconnect'].includes(action)) return fail('Unknown MCP action.');
|
|
120
|
+
return mutate(async () => {
|
|
121
|
+
if (action === 'reconnect' && entry.disabled) return fail('Enable this server before reconnecting.');
|
|
122
|
+
if (action === 'reconnect') await entry.update({ disabled: true });
|
|
123
|
+
await entry.update({ disabled: action === 'disable' });
|
|
124
|
+
return ok(`${entry.id}: ${state(entry)}. Use /mcp tools ${entry.id} to check discovery.`);
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
register('skills', 'Skill catalog, sources, details and filesystem conflicts', async ({ agent, rawInput, signal }) => {
|
|
128
|
+
const options = { cwd: agent.session.header.cwd ?? process.cwd(), scope: agent, signal };
|
|
129
|
+
const { skills, complete } = await ctx.skills.snapshot(options);
|
|
130
|
+
const arg = rawInput.trim();
|
|
131
|
+
if (arg === 'conflicts') return ok(await findConflicts(options.cwd, entries().filter(e => !e.disabled && e.options.name === '@deepseek-ai/dsh-skill-filesystem').map(e => e.options.config ?? {}), skills));
|
|
132
|
+
if (arg && arg !== 'list') {
|
|
133
|
+
const skill = await ctx.skills.get(arg, options);
|
|
134
|
+
return skill ? ok(`${skill.name}\n${skill.description}\nsource: ${skill.source}\nprovider: ${skill.provider}\npath: ${skill.path ?? '(provider managed)'}\ninvocation: ${show(skill.invocation)}`) : fail(`Unknown skill: ${arg}`);
|
|
135
|
+
}
|
|
136
|
+
return ok(`Discovery: ${complete ? 'complete' : 'incomplete'}\n${skills.map(s => `${s.name} [${s.source}; ${s.provider}] user=${s.invocation.userInvocable} model=${s.invocation.modelInvocable}\n ${s.description}`).join('\n')}\n/skills <name> for details; /skills conflicts for duplicate filesystem names.`);
|
|
137
|
+
});
|
|
138
|
+
const hookPaths = new WeakMap();
|
|
139
|
+
const hookPath = entry => {
|
|
140
|
+
const path = entry.fiber?.config?.configPath ?? entry.options.config?.configPath;
|
|
141
|
+
if (typeof path === 'string') { hookPaths.set(entry, path); return path; }
|
|
142
|
+
const cached = hookPaths.get(entry) ?? ctx.get('dscodePaths')?.hooks;
|
|
143
|
+
if (typeof cached !== 'string') throw new Error('Hook configuration path has not resolved');
|
|
144
|
+
return cached;
|
|
145
|
+
};
|
|
146
|
+
register('hooks', 'Hook status and reload/disable/enable (installation-owned config)', async ({ rawInput }) => {
|
|
147
|
+
const entry = hook();
|
|
148
|
+
if (!entry) return fail('Hooks plugin is not mounted. Restart dscode after setup.');
|
|
149
|
+
const action = rawInput.trim() || 'list';
|
|
150
|
+
if (action !== 'list') {
|
|
151
|
+
if (!['reload', 'disable', 'enable'].includes(action)) return fail('Usage: /hooks [list|reload|disable|enable]');
|
|
152
|
+
return mutate(async () => {
|
|
153
|
+
if (action !== 'disable') validateHooks(JSON.parse(await readFile(hookPath(entry), 'utf8')));
|
|
154
|
+
if (action === 'reload') await entry.update({ disabled: true });
|
|
155
|
+
await entry.update({ disabled: action === 'disable' });
|
|
156
|
+
return ok(`Hooks: ${state(entry)}. Configuration changes apply after reload or restart.`);
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
const path = hookPath(entry);
|
|
160
|
+
const raw = JSON.parse(await readFile(path, 'utf8'));
|
|
161
|
+
const hooks = raw.hooks ?? raw;
|
|
162
|
+
return ok(`Hooks: ${state(entry)}\nConfig: ${path}\n${Object.entries(hooks).map(([event, groups]) => `${event}: ${hookEvents.includes(event) ? 'supported' : 'UNSUPPORTED'}; ${Array.isArray(groups) ? groups.length : 0} groups`).join('\n')}\nSupported: ${hookEvents.join(', ')}\nOnly synchronous command hooks. Runs as your OS user, outside tool approval. Edit only trusted installation-owned config; /hooks reload applies it. No project hook auto-loading.\nPreCompact/PostCompact, PermissionRequest and subagent events are not supported by this bridge.`);
|
|
163
|
+
});
|
|
164
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export const ULTRA_POLICY = `DSCODE ULTRA — max reasoning with deliberate collaboration.
|
|
2
|
+
Actively look for useful independent tasks: investigation, implementation in disjoint files, and independent review. Delegate only when it improves the outcome; simple tasks need no child agents. Give each child a bounded objective, relevant context, file ownership and acceptance criteria. Keep useful work for yourself while children run. Never have multiple agents edit the same files concurrently. Shared files are not isolated worktrees.
|
|
3
|
+
In ultra use subagent/subagent_fork and send_message for delegation, not workflow or ralph. Use at most three child agents concurrently across this root session. Do not recursively proliferate agents or duplicate investigations. The parent owns integration, verifies child claims, resolves conflicts and runs appropriate checks. Seek independent review of substantial changes when useful. Parent/child messages are available; sibling direct messaging is not. Preserve the user's permission policy: ultra grants no extra authority. Track progress and costs, reuse findings, and stop delegating when coordination costs outweigh value.`;
|
|
4
|
+
export function ultraRequest(options, messages) {
|
|
5
|
+
if (options.reasoningEffort !== 'ultra' || options.purpose || !options.tools?.some(t => t.name === 'subagent' || t.name === 'subagent_fork')) return messages;
|
|
6
|
+
const copy = messages.map(m => ({ ...m }));
|
|
7
|
+
const system = copy.findLast(m => m.role === 'system' && typeof m.content === 'string');
|
|
8
|
+
if (system) system.content += '\n\n' + ULTRA_POLICY;
|
|
9
|
+
else copy.unshift({ role: 'system', content: ULTRA_POLICY });
|
|
10
|
+
return copy;
|
|
11
|
+
}
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
# The `dscode` agent preset: the full coding agent, mounted once per process.
|
|
2
|
+
#
|
|
3
|
+
# This file is an AGENT-PLANE composition. The roster mounts it ONCE under a
|
|
4
|
+
# standing scope; every session naming it joins by scope parentage, so the
|
|
5
|
+
# tools and prompt sections registered here cover each joined agent while a
|
|
6
|
+
# session's own state stays keyed per Session/Agent inside the plugins. The
|
|
7
|
+
# host composition (`base.cordis.yml` + `web.cordis.yml`) keeps everything a
|
|
8
|
+
# preset must not own: the registries themselves, the sandbox and approval
|
|
9
|
+
# stack, persistence, and the model route.
|
|
10
|
+
#
|
|
11
|
+
# A service row here MUST sit inside a group carrying an `isolate` realm.
|
|
12
|
+
# Without one it publishes into the root realm, where it is process-global —
|
|
13
|
+
# another preset publishing the same name collides, and a host reader would
|
|
14
|
+
# resolve one preset's instance for every session; `dsh-agent-presets` rejects
|
|
15
|
+
# that at mount. `true` means an entry-local realm: this standing mount's own
|
|
16
|
+
# private instance, apart from every other preset's. (A shared label does NOT
|
|
17
|
+
# pool instances — `provide()` throws on the second registration under the
|
|
18
|
+
# same realm symbol; labels join REALMS, and are not what this file needs.)
|
|
19
|
+
|
|
20
|
+
# ── identity ────────────────────────────────────────────────────────────────
|
|
21
|
+
|
|
22
|
+
# The preset's own persona, shadowing the deployment default for this agent.
|
|
23
|
+
# `{{model}}` and `{{cwd}}` resolve from the agent's own route and workspace.
|
|
24
|
+
- id: persona
|
|
25
|
+
name: '@deepseek-ai/dsh-persona'
|
|
26
|
+
config:
|
|
27
|
+
suffix: Your working directory is {{cwd}}.
|
|
28
|
+
prefix: >-
|
|
29
|
+
You are a coding agent powered by the {{model}} model. Use the persistent shell for file reading, searching, and editing. Prefer rg and precise patches.
|
|
30
|
+
|
|
31
|
+
- id: agent-instructions
|
|
32
|
+
name: '@deepseek-ai/dsh-agent-instructions'
|
|
33
|
+
config:
|
|
34
|
+
maxBytes: 65536
|
|
35
|
+
|
|
36
|
+
- id: persistent-shell
|
|
37
|
+
name: cordis:group
|
|
38
|
+
group: true
|
|
39
|
+
isolate:
|
|
40
|
+
terminals: true
|
|
41
|
+
config:
|
|
42
|
+
- id: pty
|
|
43
|
+
name: '@deepseek-ai/dsh-terminal'
|
|
44
|
+
|
|
45
|
+
- id: terminal-bash
|
|
46
|
+
name: '@deepseek-ai/dsh-terminal-bash'
|
|
47
|
+
disabled: !!js process.platform === 'win32'
|
|
48
|
+
config:
|
|
49
|
+
timeoutMs: 300000
|
|
50
|
+
|
|
51
|
+
- id: persistent-bash
|
|
52
|
+
name: '@toddzheng024/dscode-bundle/persistent'
|
|
53
|
+
disabled: !!js process.platform === 'win32'
|
|
54
|
+
config:
|
|
55
|
+
timeoutMs: 300000
|
|
56
|
+
description: |-
|
|
57
|
+
Run commands in a bash shell
|
|
58
|
+
* When invoking this tool, the contents of the "command" parameter does NOT need to be XML-escaped.
|
|
59
|
+
* Network access depends on the task environment. Prefer configured mirrors/proxies when they are available.
|
|
60
|
+
* State is persistent across command calls and discussions with the user.
|
|
61
|
+
* To inspect a particular line range of a file, e.g. lines 10-25, try 'sed -n 10,25p /path/to/the/file'.
|
|
62
|
+
* Please avoid commands that may produce a very large amount of output.
|
|
63
|
+
* Please run long lived commands in the background, e.g. 'sleep 10 &' or start a server in the background.
|
|
64
|
+
|
|
65
|
+
- id: terminal-pwsh
|
|
66
|
+
name: '@deepseek-ai/dsh-terminal-bash'
|
|
67
|
+
disabled: !!js process.platform !== 'win32'
|
|
68
|
+
config:
|
|
69
|
+
shellDialect: pwsh
|
|
70
|
+
timeoutMs: 300000
|
|
71
|
+
|
|
72
|
+
- id: persistent-pwsh
|
|
73
|
+
name: '@deepseek-ai/dsh-tool-pwsh-persistent'
|
|
74
|
+
disabled: !!js process.platform !== 'win32'
|
|
75
|
+
config:
|
|
76
|
+
timeoutMs: 300000
|
|
77
|
+
description: |-
|
|
78
|
+
Run commands in a PowerShell shell
|
|
79
|
+
* When invoking this tool, the contents of the "command" parameter does NOT need to be XML-escaped.
|
|
80
|
+
* You don't have access to the internet via this tool.
|
|
81
|
+
* State is persistent across command calls and discussions with the user.
|
|
82
|
+
* Use native Windows paths (C:\...) and $env:NAME variables; this is PowerShell, not bash.
|
|
83
|
+
* Please avoid commands that may produce a very large amount of output.
|
|
84
|
+
* Please run long lived commands in the background, e.g. 'Start-Job' or start a server with Start-Process.
|
|
85
|
+
|
|
86
|
+
- id: dscode-policy
|
|
87
|
+
name: '@toddzheng024/dscode-bundle/policy'
|
|
88
|
+
|
|
89
|
+
# A fresh shell for reviewed escalation. It does not inherit PTY state.
|
|
90
|
+
- id: shell-retry
|
|
91
|
+
name: '@toddzheng024/dscode-bundle/bash'
|
|
92
|
+
config:
|
|
93
|
+
toolName: shell_retry
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
# ── background jobs ────────────────────────────────────────────────────────
|
|
97
|
+
|
|
98
|
+
# Only the model-facing controls. The task REGISTRY stays on the host plane:
|
|
99
|
+
# its producers sit outside any realm this file could put it in — `tool-bash`
|
|
100
|
+
# above resolves it with `ctx.get`, and an entry-local realm here is invisible
|
|
101
|
+
# to every sibling row, so `run_in_background` would answer "background jobs
|
|
102
|
+
# unavailable" while these controls sat in the catalog. The registry is keyed by
|
|
103
|
+
# owning agent anyway, so one host instance serves every session. What a preset
|
|
104
|
+
# chooses is whether its agent can collect and stop background work at all.
|
|
105
|
+
- id: tool-jobs
|
|
106
|
+
name: '@deepseek-ai/dsh-tool-jobs'
|
|
107
|
+
|
|
108
|
+
# ── skills ──────────────────────────────────────────────────────────────────
|
|
109
|
+
|
|
110
|
+
# The skill REGISTRY lives in the host composition and is layered per scope:
|
|
111
|
+
# these rows register into THIS preset's layer of it, so they need no realm.
|
|
112
|
+
# `skill-filesystem` contributes local-root discovery for agents on this preset, and
|
|
113
|
+
# `tool-skill` gives them the catalog and loader; the merged catalog also
|
|
114
|
+
# carries whatever the deployment registered globally (repository plugins).
|
|
115
|
+
- id: skill-filesystem
|
|
116
|
+
name: '@deepseek-ai/dsh-skill-filesystem'
|
|
117
|
+
|
|
118
|
+
- id: tool-skill
|
|
119
|
+
name: '@deepseek-ai/dsh-tool-skill'
|
|
120
|
+
|
|
121
|
+
# ── goals ───────────────────────────────────────────────────────────────────
|
|
122
|
+
|
|
123
|
+
# The goal service and session driver stay on the host plane, where the Gateway
|
|
124
|
+
# can resolve them. The human command and model-facing tool register into this
|
|
125
|
+
# preset's scoped layers.
|
|
126
|
+
- id: command-goal
|
|
127
|
+
name: '@deepseek-ai/dsh-command-goal'
|
|
128
|
+
|
|
129
|
+
- id: tool-goal
|
|
130
|
+
name: '@deepseek-ai/dsh-tool-goal'
|
|
131
|
+
|
|
132
|
+
# ── plan mode ───────────────────────────────────────────────────────────────
|
|
133
|
+
|
|
134
|
+
# Plan state is per-agent by nature, so an entry-local realm is not a
|
|
135
|
+
# workaround here — it is the correct lifetime.
|
|
136
|
+
- id: planning
|
|
137
|
+
name: cordis:group
|
|
138
|
+
group: true
|
|
139
|
+
isolate:
|
|
140
|
+
planMode: true
|
|
141
|
+
config:
|
|
142
|
+
- id: plan-mode
|
|
143
|
+
name: '@deepseek-ai/dsh-plan-mode'
|
|
144
|
+
config:
|
|
145
|
+
section: |
|
|
146
|
+
You are in plan mode. Stay in plan mode until exit_plan_mode succeeds or the user switches the session mode. Imperative language to implement changes means plan the implementation, not execute it. A user's conversational agreement — including an answer confirming something you asked — approves nothing and does not end plan mode; fold the confirmed decision into the plan and submit it through exit_plan_mode.
|
|
147
|
+
|
|
148
|
+
Explore first. Use non-mutating reads, searches, static analysis, and checks to ground the plan in the actual repository. Do not edit or write files, change configuration, run formatters or code generation that rewrites tracked files, commit, or otherwise carry out the plan. Prefer existing functions and patterns over new machinery.
|
|
149
|
+
|
|
150
|
+
The tool catalog stays the same across modes for request-cache stability. These plan-mode rules override any later tool description or guidance that suggests using mutation tools; those tools remain listed to keep the tool catalog unchanged. Do not use todo_write to track this planning phase: it tracks implementation after an approved plan, while the plan itself belongs in exit_plan_mode.
|
|
151
|
+
|
|
152
|
+
Resolve discoverable facts by inspection. Use ask_user_question only for user-owned choices or material ambiguity that inspection cannot answer. Do not ask the user where code lives or how current behavior works when you can find out.
|
|
153
|
+
|
|
154
|
+
Make the plan decision-complete: state the goal and success criteria; group implementation changes by subsystem; identify public API, schema, and data-flow changes; cover edge cases, failure modes, tests, acceptance criteria, and explicit assumptions. Keep it concise enough to review but detailed enough that another engineer can implement it without making design decisions.
|
|
155
|
+
|
|
156
|
+
When ready, call exit_plan_mode with the complete plan markdown, starting with a # title. Make exit_plan_mode the only and final tool call in that assistant response: it presents the plan for approval, and implementation begins only in a later step after approval. Do not paste the final plan as a plain reply or ask "should I proceed?" through prose or ask_user_question. If review rejects it, incorporate the feedback and present again. If the review channel is unavailable or aborted, stay in plan mode and ask the user to switch modes manually; do not proceed with implementation.
|
|
157
|
+
|
|
158
|
+
# ── compaction ──────────────────────────────────────────────────────────────
|
|
159
|
+
|
|
160
|
+
# `compaction-basic` reads `toolResultPrune` through `ctx.get`, so the pruner must
|
|
161
|
+
# share this realm rather than sit outside it.
|
|
162
|
+
#
|
|
163
|
+
# `tokenMeter` is deliberately NOT in this realm: the meter stays on the HOST
|
|
164
|
+
# plane, and the rows here resolve that one instance. It takes no configuration,
|
|
165
|
+
# keys every fold by Session, and owns the context-meter projection units the
|
|
166
|
+
# browser reads for every session — behind a realm those units would come and go
|
|
167
|
+
# with whichever presets happen to be mounted. What a preset chooses is whether
|
|
168
|
+
# its agent compacts at all, which is `compaction-basic` below.
|
|
169
|
+
- id: compaction
|
|
170
|
+
name: cordis:group
|
|
171
|
+
group: true
|
|
172
|
+
isolate:
|
|
173
|
+
compaction: true
|
|
174
|
+
toolResultPruner: true
|
|
175
|
+
config:
|
|
176
|
+
- id: compaction-basic
|
|
177
|
+
name: '@deepseek-ai/dsh-compaction-basic'
|
|
178
|
+
|
|
179
|
+
- id: command-compact
|
|
180
|
+
name: '@deepseek-ai/dsh-command-compact'
|
|
181
|
+
|
|
182
|
+
- id: tool-result-pruner
|
|
183
|
+
name: '@deepseek-ai/dsh-compaction-tool-result-pruner'
|
|
184
|
+
config:
|
|
185
|
+
thresholdChars: 8192
|
|
186
|
+
headChars: 4096
|
|
187
|
+
tailChars: 1024
|
|
188
|
+
|
|
189
|
+
# ── delegation and workflows ────────────────────────────────────────────────
|
|
190
|
+
|
|
191
|
+
# The `subagents` registry and its spawn/fork backends live in the HOST
|
|
192
|
+
# composition: the registry is a process singleton whose cross-session queries
|
|
193
|
+
# the api-proxy serves to the browser, and a provider name may only be
|
|
194
|
+
# registered once. This preset contributes the delegation TOOLS, which resolve
|
|
195
|
+
# that host registry.
|
|
196
|
+
#
|
|
197
|
+
# `workflows` is different — nothing outside an agent reads it — so every row
|
|
198
|
+
# that reaches it shares one entry-local realm here, and a consumer left
|
|
199
|
+
# outside would resolve a host registry this preset does not populate.
|
|
200
|
+
- id: delegation
|
|
201
|
+
name: cordis:group
|
|
202
|
+
group: true
|
|
203
|
+
isolate:
|
|
204
|
+
workflowEngine: true
|
|
205
|
+
config:
|
|
206
|
+
- id: tool-subagent-control
|
|
207
|
+
name: '@deepseek-ai/dsh-tool-subagent-control'
|
|
208
|
+
|
|
209
|
+
- id: tool-subagent-list-agents
|
|
210
|
+
name: '@deepseek-ai/dsh-tool-subagent-control/list-agents'
|
|
211
|
+
|
|
212
|
+
- id: tool-subagent
|
|
213
|
+
name: '@deepseek-ai/dsh-tool-subagent'
|
|
214
|
+
config:
|
|
215
|
+
provider: spawn
|
|
216
|
+
toolName: subagent
|
|
217
|
+
modelSelectionSettings: true
|
|
218
|
+
backgroundMode: continuable
|
|
219
|
+
maxDepth: 1
|
|
220
|
+
|
|
221
|
+
# Fork omits model selection so provider/model stay equal to the parent and
|
|
222
|
+
# the inherited history remains eligible for KV Cache reuse. This preset
|
|
223
|
+
# keeps fork continuable; parent and child inherit the same messaging tool,
|
|
224
|
+
# while the parent id and return guidance follow the inherited history.
|
|
225
|
+
- id: tool-subagent-fork
|
|
226
|
+
name: '@deepseek-ai/dsh-tool-subagent'
|
|
227
|
+
config:
|
|
228
|
+
provider: fork
|
|
229
|
+
toolName: subagent_fork
|
|
230
|
+
backgroundMode: continuable
|
|
231
|
+
maxDepth: 1
|
|
232
|
+
|
|
233
|
+
# Production dsh does not install these optional providers. Install the
|
|
234
|
+
# matching Bundle in this Profile and restart the Host, then copy this
|
|
235
|
+
# preset and remove `disabled` from the matching tool row. Host availability
|
|
236
|
+
# alone grants no tool.
|
|
237
|
+
- id: tool-subagent-codex
|
|
238
|
+
name: '@deepseek-ai/dsh-tool-subagent'
|
|
239
|
+
disabled: true
|
|
240
|
+
config:
|
|
241
|
+
provider: codex
|
|
242
|
+
toolName: subagent_codex
|
|
243
|
+
backgroundMode: one-shot
|
|
244
|
+
maxDepth: provider-managed
|
|
245
|
+
|
|
246
|
+
- id: tool-subagent-claude-code
|
|
247
|
+
name: '@deepseek-ai/dsh-tool-subagent'
|
|
248
|
+
disabled: true
|
|
249
|
+
config:
|
|
250
|
+
provider: claude-code
|
|
251
|
+
toolName: subagent_claude_code
|
|
252
|
+
backgroundMode: one-shot
|
|
253
|
+
maxDepth: provider-managed
|
|
254
|
+
|
|
255
|
+
- id: workflow-worker-thread
|
|
256
|
+
name: '@deepseek-ai/dsh-workflow-worker-thread'
|
|
257
|
+
config:
|
|
258
|
+
provider: spawn
|
|
259
|
+
|
|
260
|
+
- id: tool-workflow
|
|
261
|
+
name: '@deepseek-ai/dsh-tool-workflow'
|
|
262
|
+
|
|
263
|
+
- id: tool-ralph
|
|
264
|
+
name: '@deepseek-ai/dsh-tool-ralph'
|
|
265
|
+
config:
|
|
266
|
+
subagentProvider: spawn
|
|
267
|
+
maxRounds: 64
|
|
268
|
+
|
|
269
|
+
# ── remaining model-facing rows ─────────────────────────────────────────────
|
|
270
|
+
|
|
271
|
+
- id: tool-ask-user
|
|
272
|
+
name: '@deepseek-ai/dsh-tool-ask-user'
|
|
273
|
+
|
|
274
|
+
- id: tool-todo
|
|
275
|
+
name: '@deepseek-ai/dsh-tool-todo'
|
|
276
|
+
config:
|
|
277
|
+
allowParallelInProgress: true
|
|
278
|
+
|
|
279
|
+
# The `web` service and its search provider stay in the host composition; only
|
|
280
|
+
# the model-facing tool is per-session.
|
|
281
|
+
- id: tool-web
|
|
282
|
+
name: '@deepseek-ai/dsh-tool-web'
|
|
283
|
+
config:
|
|
284
|
+
fetch: true
|
|
285
|
+
searchTimeoutMs: 60000
|
|
286
|
+
|
|
287
|
+
- id: present
|
|
288
|
+
name: '@deepseek-ai/dsh-tool-present'
|