@joekytc/dsh-swarm 0.3.12 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/client/ConfigSection.tsx +127 -18
- package/client/ConfigSelect.tsx +1 -0
- package/client/config-store.ts +6 -3
- package/client/config.css +89 -1
- package/client/index.ts +8 -4
- package/client/session-bridge.ts +26 -2
- package/cordis.patch.yml +10 -52
- package/lib/client.js +215 -37
- package/lib/config.d.ts +7 -0
- package/lib/config.js +7 -1
- package/lib/dispatcher/agent-runner.d.ts +4 -0
- package/lib/dispatcher/agent-runner.js +28 -17
- package/lib/dispatcher/chain-auditor.d.ts +3 -2
- package/lib/dispatcher/chain-auditor.js +2 -2
- package/lib/dispatcher/model-candidates.d.ts +9 -3
- package/lib/dispatcher/model-candidates.js +27 -6
- package/lib/dispatcher/model-chain.d.ts +19 -0
- package/lib/dispatcher/model-chain.js +110 -0
- package/lib/dispatcher/session-events.d.ts +9 -0
- package/lib/dispatcher/session-events.js +28 -0
- package/lib/dispatcher/v-orchestrator.d.ts +2 -0
- package/lib/dispatcher/v-orchestrator.js +14 -9
- package/lib/domain/config-override.d.ts +20 -0
- package/lib/domain/config-override.js +46 -2
- package/lib/index.js +13 -3
- package/lib/roles/preset-installer.d.ts +50 -5
- package/lib/roles/preset-installer.js +154 -20
- package/lib/routes/kanban-http.js +24 -3
- package/lib/services/config-provider.js +5 -0
- package/package.json +23 -17
- package/personas/kanban-d/agent.cordis.yml +3 -1
- package/personas/kanban-dt/agent.cordis.yml +2 -1
- package/personas/kanban-p/agent.cordis.yml +1 -1
- package/personas/kanban-pt/agent.cordis.yml +1 -1
- package/personas/kanban-v/agent.cordis.yml +1 -1
- package/personas/kanban-w/agent.cordis.yml +1 -1
- package/personas/swarm/agent.cordis.yml +1 -1
package/lib/client.js
CHANGED
|
@@ -560,9 +560,27 @@ window.__ModuleLoader__.load({
|
|
|
560
560
|
() => EMPTY
|
|
561
561
|
);
|
|
562
562
|
}
|
|
563
|
+
var navigator = null;
|
|
564
|
+
function setSessionNavigator(next) {
|
|
565
|
+
navigator = next;
|
|
566
|
+
}
|
|
563
567
|
function openSession(id) {
|
|
568
|
+
if (navigator) {
|
|
569
|
+
let viaWorkspace = null;
|
|
570
|
+
try {
|
|
571
|
+
viaWorkspace = navigator();
|
|
572
|
+
} catch {
|
|
573
|
+
viaWorkspace = null;
|
|
574
|
+
}
|
|
575
|
+
if (viaWorkspace) {
|
|
576
|
+
viaWorkspace.openSession(id);
|
|
577
|
+
return;
|
|
578
|
+
}
|
|
579
|
+
}
|
|
564
580
|
if (!service) throw new Error("sessions service unavailable");
|
|
565
|
-
service.open
|
|
581
|
+
const open = service.open;
|
|
582
|
+
if (!open) throw new Error("host exposes neither uiWorkspace nor sessions.open; session jump unavailable");
|
|
583
|
+
open(id);
|
|
566
584
|
}
|
|
567
585
|
|
|
568
586
|
// client/BoardCard.tsx
|
|
@@ -1625,7 +1643,7 @@ window.__ModuleLoader__.load({
|
|
|
1625
1643
|
// client/config-store.ts
|
|
1626
1644
|
var SWARM_CONFIG_NS = "swarm-config";
|
|
1627
1645
|
function createConfigStore(fetchImpl) {
|
|
1628
|
-
let state = { effective: { wikiVault: { baseUrl: "", pagePrefix: "" }, roles: { models: {} }, reviewEngine: { mode: "delegate", managed: { provider: "", model: "" } }, imDelivery: { fallbackBotId: "" } }, sources: {}, catalog: { providers: [], models: {} }, ocrStatus: null, install: { phase: "idle", log: "" }, saving: false, error: null };
|
|
1646
|
+
let state = { effective: { wikiVault: { baseUrl: "", pagePrefix: "" }, roles: { models: {} }, chainFallback: { provider: "", model: "", reasoningEffort: "high" }, reviewEngine: { mode: "delegate", managed: { provider: "", model: "" } }, imDelivery: { fallbackBotId: "" } }, sources: {}, catalog: { providers: [], models: {} }, ocrStatus: null, install: { phase: "idle", log: "" }, saving: false, error: null };
|
|
1629
1647
|
const listeners = /* @__PURE__ */ new Set();
|
|
1630
1648
|
const setState = (patch) => {
|
|
1631
1649
|
state = { ...state, ...patch };
|
|
@@ -1745,6 +1763,7 @@ window.__ModuleLoader__.load({
|
|
|
1745
1763
|
"aria-haspopup": "listbox",
|
|
1746
1764
|
"aria-expanded": open,
|
|
1747
1765
|
disabled,
|
|
1766
|
+
title: current?.label ?? placeholder,
|
|
1748
1767
|
onClick: () => setOpen((v) => !v),
|
|
1749
1768
|
children: [
|
|
1750
1769
|
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: current ? void 0 : "dsh-kb-config__select-placeholder", children: current?.label ?? placeholder }),
|
|
@@ -1797,6 +1816,15 @@ window.__ModuleLoader__.load({
|
|
|
1797
1816
|
// client/ConfigSection.tsx
|
|
1798
1817
|
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
1799
1818
|
var ROLES = ["v", "p", "w", "d", "pt", "dt"];
|
|
1819
|
+
var MAX_FALLBACKS = 2;
|
|
1820
|
+
var ROLE_NAMES = {
|
|
1821
|
+
v: "\u770B\u677F\u7F16\u6392\u5B98\uFF08V\uFF09",
|
|
1822
|
+
p: "\u89C4\u5212\u5B98\uFF08P\uFF09",
|
|
1823
|
+
w: "\u77E5\u8BC6\u5B98\uFF08W\uFF09",
|
|
1824
|
+
d: "\u5168\u6808\u5F00\u53D1\u5B98\uFF08D\uFF09",
|
|
1825
|
+
pt: "\u8BA1\u5212\u5BA1\u67E5\u5B98\uFF08PT\uFF09",
|
|
1826
|
+
dt: "\u4EA4\u4ED8\u8BC4\u5BA1\u5B98\uFF08DT\uFF09"
|
|
1827
|
+
};
|
|
1800
1828
|
function ocrBanner(ocr, installing) {
|
|
1801
1829
|
if (installing) return null;
|
|
1802
1830
|
if (ocr === null) return "ocr \u72B6\u6001\u672A\u77E5\u2014\u2014\u59D4\u6258/\u6258\u7BA1\u8BC4\u5BA1\u53EF\u80FD\u4E0D\u53EF\u7528";
|
|
@@ -1810,6 +1838,7 @@ window.__ModuleLoader__.load({
|
|
|
1810
1838
|
const state = (0, import_react9.useSyncExternalStore)(store.subscribe, store.get);
|
|
1811
1839
|
const [draft, setDraft] = (0, import_react9.useState)(null);
|
|
1812
1840
|
const [wireResult, setWireResult] = (0, import_react9.useState)(null);
|
|
1841
|
+
const [openRole, setOpenRole] = (0, import_react9.useState)(void 0);
|
|
1813
1842
|
(0, import_react9.useEffect)(() => {
|
|
1814
1843
|
void store.load().then(() => setDraft(store.get().effective));
|
|
1815
1844
|
void store.loadOcrStatus();
|
|
@@ -1829,9 +1858,33 @@ window.__ModuleLoader__.load({
|
|
|
1829
1858
|
setDraft(next);
|
|
1830
1859
|
void store.save(next);
|
|
1831
1860
|
};
|
|
1861
|
+
const roleEntry = (r) => draft.roles.models[r] ?? { provider: "", model: "", reasoningEffort: "", fallbacks: [] };
|
|
1832
1862
|
const setModel = (r, patch) => {
|
|
1833
|
-
|
|
1834
|
-
|
|
1863
|
+
commit({ ...draft, roles: { models: { ...draft.roles.models, [r]: { ...roleEntry(r), ...patch } } } });
|
|
1864
|
+
};
|
|
1865
|
+
const setFallback = (r, idx, patch) => {
|
|
1866
|
+
const fbs = [...roleEntry(r).fallbacks];
|
|
1867
|
+
const cur = fbs[idx] ?? { provider: "", model: "", reasoningEffort: "" };
|
|
1868
|
+
fbs[idx] = { ...cur, ...patch };
|
|
1869
|
+
setModel(r, { fallbacks: fbs });
|
|
1870
|
+
};
|
|
1871
|
+
const addFallback = (r) => {
|
|
1872
|
+
const fbs = roleEntry(r).fallbacks;
|
|
1873
|
+
if (fbs.length >= MAX_FALLBACKS) return;
|
|
1874
|
+
setModel(r, { fallbacks: [...fbs, { provider: "", model: "", reasoningEffort: "" }] });
|
|
1875
|
+
};
|
|
1876
|
+
const removeFallback = (r, idx) => {
|
|
1877
|
+
setModel(r, { fallbacks: roleEntry(r).fallbacks.filter((_, i) => i !== idx) });
|
|
1878
|
+
};
|
|
1879
|
+
const copyChainToAll = (r) => {
|
|
1880
|
+
const src = roleEntry(r);
|
|
1881
|
+
if (!(src.provider && src.model)) return;
|
|
1882
|
+
const models = { ...draft.roles.models };
|
|
1883
|
+
for (const o of ROLES) {
|
|
1884
|
+
if (o === r) continue;
|
|
1885
|
+
models[o] = { provider: src.provider, model: src.model, reasoningEffort: src.reasoningEffort, fallbacks: src.fallbacks.map((f) => ({ ...f })) };
|
|
1886
|
+
}
|
|
1887
|
+
commit({ ...draft, roles: { models } });
|
|
1835
1888
|
};
|
|
1836
1889
|
const commitReviewEngine = (mode, managed) => {
|
|
1837
1890
|
commit({ ...draft, reviewEngine: { mode, managed } });
|
|
@@ -1839,11 +1892,26 @@ window.__ModuleLoader__.load({
|
|
|
1839
1892
|
const onBlur = () => {
|
|
1840
1893
|
void store.save(draft);
|
|
1841
1894
|
};
|
|
1895
|
+
const chainField = (label, value, placeholder, options, onPick) => /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("label", { className: "dsh-kb-config__field", children: [
|
|
1896
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "dsh-kb-config__field-label", children: label }),
|
|
1897
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ConfigSelect, { value, placeholder, options, onChange: onPick })
|
|
1898
|
+
] });
|
|
1899
|
+
const chainSummary = (m) => {
|
|
1900
|
+
if (!(m.provider && m.model)) return "\u672A\u914D\u7F6E";
|
|
1901
|
+
const n = (m.fallbacks ?? []).filter((f) => f.provider && f.model).length;
|
|
1902
|
+
return `${m.provider} / ${m.model}` + (n ? ` \xB7 ${n} \u964D\u7EA7` : "");
|
|
1903
|
+
};
|
|
1904
|
+
const providerOptions = state.catalog.providers.map((p) => ({ value: p.id, label: p.name }));
|
|
1905
|
+
const expandedRole = openRole === void 0 ? ROLES.find((x) => {
|
|
1906
|
+
const e = draft.roles.models[x];
|
|
1907
|
+
return Boolean(e?.provider && e?.model);
|
|
1908
|
+
}) ?? null : openRole;
|
|
1842
1909
|
const ocr = state.ocrStatus;
|
|
1843
1910
|
const banner = ocrBanner(ocr, installing);
|
|
1844
1911
|
const ocrBlocked = installing || ocr === null || !ocr.installed;
|
|
1845
1912
|
const re = draft.reviewEngine;
|
|
1846
1913
|
const managedFieldsEnabled = !ocrBlocked && re.mode === "managed";
|
|
1914
|
+
const cf = draft.chainFallback ?? { provider: "", model: "", reasoningEffort: "high" };
|
|
1847
1915
|
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "dsh-kb-config", children: [
|
|
1848
1916
|
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("section", { className: "dsh-kb-config__card", children: [
|
|
1849
1917
|
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("h3", { children: "\u77E5\u8BC6\u5E93" }),
|
|
@@ -1871,47 +1939,155 @@ window.__ModuleLoader__.load({
|
|
|
1871
1939
|
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("section", { className: "dsh-kb-config__card", children: [
|
|
1872
1940
|
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("h3", { children: "\u6A21\u578B\u94FE" }),
|
|
1873
1941
|
ROLES.map((r) => {
|
|
1874
|
-
const m =
|
|
1942
|
+
const m = roleEntry(r);
|
|
1943
|
+
const fbs = m.fallbacks ?? [];
|
|
1944
|
+
const configured = Boolean(m.provider && m.model);
|
|
1945
|
+
const open = expandedRole === r;
|
|
1875
1946
|
const providerModels = state.catalog.models[m.provider] ?? [];
|
|
1876
1947
|
const efforts = providerModels.find((x) => x.id === m.model)?.efforts ?? [];
|
|
1877
|
-
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "dsh-kb-config__role", children: [
|
|
1878
|
-
/* @__PURE__ */ (0, import_jsx_runtime9.
|
|
1879
|
-
|
|
1880
|
-
ConfigSelect,
|
|
1948
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "dsh-kb-config__role-block", children: [
|
|
1949
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
1950
|
+
"button",
|
|
1881
1951
|
{
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1952
|
+
type: "button",
|
|
1953
|
+
className: "dsh-kb-config__role-head",
|
|
1954
|
+
"aria-expanded": open,
|
|
1955
|
+
onClick: () => setOpenRole(open ? null : r),
|
|
1956
|
+
children: [
|
|
1957
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "dsh-kb-config__chevron" + (open ? " dsh-kb-config__chevron--open" : ""), "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("svg", { width: "14", height: "14", viewBox: "0 0 16 16", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1958
|
+
"path",
|
|
1959
|
+
{
|
|
1960
|
+
d: "M6 3.5 10.5 8 6 12.5",
|
|
1961
|
+
fill: "none",
|
|
1962
|
+
stroke: "currentColor",
|
|
1963
|
+
strokeWidth: "1.5",
|
|
1964
|
+
strokeLinecap: "round",
|
|
1965
|
+
strokeLinejoin: "round"
|
|
1966
|
+
}
|
|
1967
|
+
) }) }),
|
|
1968
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("strong", { children: ROLE_NAMES[r] }),
|
|
1969
|
+
!open && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "dsh-kb-config__role-summary", children: chainSummary(m) })
|
|
1970
|
+
]
|
|
1888
1971
|
}
|
|
1889
1972
|
),
|
|
1890
|
-
/* @__PURE__ */ (0, import_jsx_runtime9.
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1973
|
+
open && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "dsh-kb-config__role-body", children: [
|
|
1974
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "dsh-kb-config__group-label", children: "\u4E3B\u6A21\u578B" }),
|
|
1975
|
+
chainField(
|
|
1976
|
+
"\u4F9B\u5E94\u5546",
|
|
1977
|
+
m.provider,
|
|
1978
|
+
"\u9009\u62E9 provider",
|
|
1979
|
+
providerOptions,
|
|
1980
|
+
(v) => {
|
|
1981
|
+
if (v !== m.provider) setModel(r, { provider: v, model: "", reasoningEffort: "" });
|
|
1982
|
+
}
|
|
1983
|
+
),
|
|
1984
|
+
chainField(
|
|
1985
|
+
"\u6A21\u578B",
|
|
1986
|
+
m.model,
|
|
1987
|
+
"\u9009\u62E9\u6A21\u578B",
|
|
1988
|
+
providerModels.map((x) => ({ value: x.id, label: x.name })),
|
|
1989
|
+
(v) => {
|
|
1897
1990
|
if (v !== m.model) setModel(r, { model: v, reasoningEffort: "" });
|
|
1898
1991
|
}
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
value:
|
|
1905
|
-
|
|
1906
|
-
options: efforts.map((x) => ({ value: x.id, label: x.name })),
|
|
1907
|
-
onChange: (v) => {
|
|
1992
|
+
),
|
|
1993
|
+
chainField(
|
|
1994
|
+
"\u601D\u8003\u7B49\u7EA7",
|
|
1995
|
+
m.reasoningEffort,
|
|
1996
|
+
"\u8DDF\u968F\u9ED8\u8BA4",
|
|
1997
|
+
efforts.map((x) => ({ value: x.id, label: x.name })),
|
|
1998
|
+
(v) => {
|
|
1908
1999
|
if (v !== m.reasoningEffort) setModel(r, { reasoningEffort: v });
|
|
1909
2000
|
}
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
2001
|
+
),
|
|
2002
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "dsh-kb-config__group-label", children: "\u964D\u7EA7\uFF08\u6700\u591A 2 \u4E2A\uFF0C\u987A\u5E8F = \u5207\u6362\u4F18\u5148\u5E8F\uFF09" }),
|
|
2003
|
+
fbs.map((f, i) => {
|
|
2004
|
+
const fProviderModels = state.catalog.models[f.provider] ?? [];
|
|
2005
|
+
const fEfforts = fProviderModels.find((x) => x.id === f.model)?.efforts ?? [];
|
|
2006
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "dsh-kb-config__fb-group", children: [
|
|
2007
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "dsh-kb-config__fb-head", children: [
|
|
2008
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: "dsh-kb-config__fb-title", children: [
|
|
2009
|
+
"\u964D\u7EA7 ",
|
|
2010
|
+
i + 1
|
|
2011
|
+
] }),
|
|
2012
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
2013
|
+
"button",
|
|
2014
|
+
{
|
|
2015
|
+
type: "button",
|
|
2016
|
+
className: "dsh-kb-config__row-btn",
|
|
2017
|
+
title: "\u5220\u9664\u8BE5\u964D\u7EA7\u5019\u9009",
|
|
2018
|
+
onClick: () => removeFallback(r, i),
|
|
2019
|
+
children: "\u5220\u9664"
|
|
2020
|
+
}
|
|
2021
|
+
)
|
|
2022
|
+
] }),
|
|
2023
|
+
chainField(
|
|
2024
|
+
"\u4F9B\u5E94\u5546",
|
|
2025
|
+
f.provider,
|
|
2026
|
+
"\u9009\u62E9 provider",
|
|
2027
|
+
providerOptions,
|
|
2028
|
+
(v) => {
|
|
2029
|
+
if (v !== f.provider) setFallback(r, i, { provider: v, model: "", reasoningEffort: "" });
|
|
2030
|
+
}
|
|
2031
|
+
),
|
|
2032
|
+
chainField(
|
|
2033
|
+
"\u6A21\u578B",
|
|
2034
|
+
f.model,
|
|
2035
|
+
"\u9009\u62E9\u6A21\u578B",
|
|
2036
|
+
fProviderModels.map((x) => ({ value: x.id, label: x.name })),
|
|
2037
|
+
(v) => {
|
|
2038
|
+
if (v !== f.model) setFallback(r, i, { model: v, reasoningEffort: "" });
|
|
2039
|
+
}
|
|
2040
|
+
),
|
|
2041
|
+
chainField(
|
|
2042
|
+
"\u601D\u8003\u7B49\u7EA7",
|
|
2043
|
+
f.reasoningEffort,
|
|
2044
|
+
"\u8DDF\u968F\u9ED8\u8BA4",
|
|
2045
|
+
fEfforts.map((x) => ({ value: x.id, label: x.name })),
|
|
2046
|
+
(v) => {
|
|
2047
|
+
if (v !== f.reasoningEffort) setFallback(r, i, { reasoningEffort: v });
|
|
2048
|
+
}
|
|
2049
|
+
)
|
|
2050
|
+
] }, i);
|
|
2051
|
+
}),
|
|
2052
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "dsh-kb-config__role-actions", children: [
|
|
2053
|
+
fbs.length < MAX_FALLBACKS && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
2054
|
+
"button",
|
|
2055
|
+
{
|
|
2056
|
+
type: "button",
|
|
2057
|
+
className: "dsh-kb-config__row-btn",
|
|
2058
|
+
disabled: !configured,
|
|
2059
|
+
title: configured ? "\u8FFD\u52A0\u4E00\u4E2A\u964D\u7EA7\u5019\u9009" : "\u5148\u9009\u597D\u4E3B\u6A21\u578B\uFF08provider+\u6A21\u578B\uFF09\u518D\u8FFD\u52A0\u964D\u7EA7",
|
|
2060
|
+
onClick: () => addFallback(r),
|
|
2061
|
+
children: "+ \u964D\u7EA7"
|
|
2062
|
+
}
|
|
2063
|
+
),
|
|
2064
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
2065
|
+
"button",
|
|
2066
|
+
{
|
|
2067
|
+
type: "button",
|
|
2068
|
+
className: "dsh-kb-config__row-btn",
|
|
2069
|
+
disabled: !configured,
|
|
2070
|
+
title: "\u628A\u8BE5\u89D2\u8272\u7684\u4E3B\u6A21\u578B+\u964D\u7EA7\u94FE\u6574\u4F53\u590D\u5236\u5230\u5176\u4F59\u89D2\u8272",
|
|
2071
|
+
onClick: () => copyChainToAll(r),
|
|
2072
|
+
children: "\u590D\u5236\u672C\u94FE\u5230\u5168\u90E8\u89D2\u8272"
|
|
2073
|
+
}
|
|
2074
|
+
)
|
|
2075
|
+
] })
|
|
2076
|
+
] })
|
|
1913
2077
|
] }, r);
|
|
1914
|
-
})
|
|
2078
|
+
}),
|
|
2079
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "dsh-kb-config__role dsh-kb-config__role--official", children: [
|
|
2080
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "dsh-kb-config__badge", children: "\u5B98\u65B9\u515C\u5E95" }),
|
|
2081
|
+
cf.provider && cf.model ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { children: [
|
|
2082
|
+
cf.provider,
|
|
2083
|
+
" / ",
|
|
2084
|
+
cf.model,
|
|
2085
|
+
"\uFF08reasoningEffort=",
|
|
2086
|
+
cf.reasoningEffort,
|
|
2087
|
+
"\uFF09"
|
|
2088
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "dsh-kb-config__muted", children: "\u672A\u914D\u7F6E\u2014\u2014\u89D2\u8272\u94FE\u5168\u7A7A\u65F6\u8D70\u5BBF\u4E3B\u9ED8\u8BA4\u6A21\u578B" })
|
|
2089
|
+
] }),
|
|
2090
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "dsh-kb-config__help", children: "\u6D3E\u53D1\u987A\u5E8F\uFF1A\u4E3B\u6A21\u578B \u2192 \u964D\u7EA7\uFF08\u987A\u5E8F=\u4F18\u5148\u5E8F\uFF0C\u6700\u591A 2 \u4E2A\uFF09\u2192 \u5B98\u65B9\u515C\u5E95\uFF08\u6052\u57AB\u94FE\u5C3E\uFF09\u2192 \u5BBF\u4E3B\u9ED8\u8BA4\u6A21\u578B\uFF1B\u5019\u9009\u4E0D\u53EF\u7528\uFF08\u542B\u5B98\u65B9\u8D26\u53F7\u672A\u767B\u5F55\uFF09\u9759\u9ED8\u5207\u6362\u5E76\u7559 [model-fallback] \u8BC4\u8BBA" })
|
|
1915
2091
|
] }),
|
|
1916
2092
|
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("section", { className: "dsh-kb-config__card", children: [
|
|
1917
2093
|
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("h3", { children: "\u8BC4\u5BA1\u5F15\u64CE\uFF08ocr\uFF09" }),
|
|
@@ -2893,13 +3069,14 @@ window.__ModuleLoader__.load({
|
|
|
2893
3069
|
`;
|
|
2894
3070
|
|
|
2895
3071
|
// client/config.css
|
|
2896
|
-
var config_default = ".dsh-kb-config {\n /* \u4E0E kanban.css \u5927\u5706\u89D2\u5951\u7EA6\u5BF9\u9F50\uFF08DSH \u5361\u7247 16px\uFF09 */\n --dsh-kb-config-border: var(--dsw-alias-border-l2, #333);\n --dsh-kb-config-radius-lg: 16px;\n --dsh-kb-config-radius-md: 10px;\n --dsh-kb-config-input: var(--dsw-specific-input-major, rgba(127, 127, 127, 0.12));\n padding: 16px;\n font-size: 13px;\n}\n.dsh-kb-config__card { border: 1px solid var(--dsh-kb-config-border); border-radius: var(--dsh-kb-config-radius-lg); padding: 20px; margin-bottom: 16px; }\n.dsh-kb-config__card h3 { margin: 0 0 14px; }\n\n.dsh-kb-config input,\n.dsh-kb-config select {\n box-sizing: border-box;\n width: 100%;\n padding: 8px 12px;\n border: 1px solid var(--dsh-kb-config-border);\n border-radius: var(--dsh-kb-config-radius-md);\n background: var(--dsh-kb-config-input);\n color: var(--dsw-alias-label-primary, inherit);\n font: inherit;\n transition: border-color 150ms ease, background-color 150ms ease;\n}\n.dsh-kb-config input:focus,\n.dsh-kb-config select:focus {\n outline: none;\n /* \u7126\u70B9/\u9009\u4E2D\u5F3A\u8C03\u8272\u8DDF\u968F\u4E3B\u9898\u524D\u666F\uFF08\u6D45\u8272=\u6697\u3001\u6697\u8272=\u767D\uFF09\uFF0C\u4E0E dsh settings \u8272\u7CFB\u4E00\u81F4 */\n border-color: var(--dsw-alias-label-primary, currentColor);\n}\n\n/* DSH \u98CE\u683C\u4E0B\u62C9\uFF1Atrigger \u884C\uFF08\u503C + \u203A chevron\uFF09+ \u5F39\u51FA\u83DC\u5355\uFF08\u9009\u4E2D\u6253\u52FE\u3001hover \u9AD8\u4EAE\uFF09 */\n.dsh-kb-config__select { position: relative; min-width: 0; }\n.dsh-kb-config__select-trigger {\n box-sizing: border-box;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n width: 100%;\n padding: 8px 12px;\n border: 1px solid var(--dsh-kb-config-border);\n border-radius: var(--dsh-kb-config-radius-md);\n background: var(--dsh-kb-config-input);\n color: var(--dsw-alias-label-primary, inherit);\n font: inherit;\n text-align: left;\n cursor: pointer;\n transition: border-color 150ms ease, background-color 150ms ease;\n}\n.dsh-kb-config__select-trigger:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(127, 127, 127, 0.12)); }\n.dsh-kb-config__select-trigger[aria-expanded='true'] { border-color: var(--dsw-alias-label-primary, currentColor); }\n.dsh-kb-config__select-placeholder { color: var(--dsw-alias-label-tertiary, #999); }\n.dsh-kb-config__select-trigger > span:first-child { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.dsh-kb-config__select-chevron { flex: 0 0 auto; display: inline-flex; color: var(--dsw-alias-label-tertiary, #999); }\n.dsh-kb-config__menu {\n position: absolute;\n z-index: 30;\n top: calc(100% + 6px);\n left: 0;\n right: 0;\n margin: 0;\n padding: 6px;\n list-style: none;\n border: 1px solid var(--dsh-kb-config-border);\n border-radius: var(--dsh-kb-config-radius-lg);\n background: var(--dsw-alias-bg-layer-3, #222);\n color: var(--dsw-alias-label-primary, inherit);\n box-shadow: 0 8px 24px rgba(0, 0, 0, 0.24);\n max-height: 260px;\n overflow: auto;\n}\n.dsh-kb-config__option {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n width: 100%;\n padding: 8px 10px;\n border: none;\n border-radius: var(--dsh-kb-config-radius-md);\n background: transparent;\n color: inherit;\n font: inherit;\n text-align: left;\n cursor: pointer;\n transition: background-color 150ms ease;\n}\n.dsh-kb-config__option:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(127, 127, 127, 0.12)); }\n.dsh-kb-config__option[data-selected='true'] { color: var(--dsw-alias-state-business-primary, #4b7bff); }\n.dsh-kb-config__option > span:first-child { overflow: hidden; text-overflow: ellipsis; white-space:
|
|
3072
|
+
var config_default = ".dsh-kb-config {\n /* \u4E0E kanban.css \u5927\u5706\u89D2\u5951\u7EA6\u5BF9\u9F50\uFF08DSH \u5361\u7247 16px\uFF09 */\n --dsh-kb-config-border: var(--dsw-alias-border-l2, #333);\n --dsh-kb-config-radius-lg: 16px;\n --dsh-kb-config-radius-md: 10px;\n --dsh-kb-config-input: var(--dsw-specific-input-major, rgba(127, 127, 127, 0.12));\n padding: 16px;\n font-size: 13px;\n}\n.dsh-kb-config__card { border: 1px solid var(--dsh-kb-config-border); border-radius: var(--dsh-kb-config-radius-lg); padding: 20px; margin-bottom: 16px; }\n.dsh-kb-config__card h3 { margin: 0 0 14px; }\n\n.dsh-kb-config input,\n.dsh-kb-config select {\n box-sizing: border-box;\n width: 100%;\n padding: 8px 12px;\n border: 1px solid var(--dsh-kb-config-border);\n border-radius: var(--dsh-kb-config-radius-md);\n background: var(--dsh-kb-config-input);\n color: var(--dsw-alias-label-primary, inherit);\n font: inherit;\n transition: border-color 150ms ease, background-color 150ms ease;\n}\n.dsh-kb-config input:focus,\n.dsh-kb-config select:focus {\n outline: none;\n /* \u7126\u70B9/\u9009\u4E2D\u5F3A\u8C03\u8272\u8DDF\u968F\u4E3B\u9898\u524D\u666F\uFF08\u6D45\u8272=\u6697\u3001\u6697\u8272=\u767D\uFF09\uFF0C\u4E0E dsh settings \u8272\u7CFB\u4E00\u81F4 */\n border-color: var(--dsw-alias-label-primary, currentColor);\n}\n\n/* DSH \u98CE\u683C\u4E0B\u62C9\uFF1Atrigger \u884C\uFF08\u503C + \u203A chevron\uFF09+ \u5F39\u51FA\u83DC\u5355\uFF08\u9009\u4E2D\u6253\u52FE\u3001hover \u9AD8\u4EAE\uFF09 */\n.dsh-kb-config__select { position: relative; min-width: 0; }\n.dsh-kb-config__select-trigger {\n box-sizing: border-box;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n width: 100%;\n padding: 8px 12px;\n border: 1px solid var(--dsh-kb-config-border);\n border-radius: var(--dsh-kb-config-radius-md);\n background: var(--dsh-kb-config-input);\n color: var(--dsw-alias-label-primary, inherit);\n font: inherit;\n text-align: left;\n cursor: pointer;\n transition: border-color 150ms ease, background-color 150ms ease;\n}\n.dsh-kb-config__select-trigger:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(127, 127, 127, 0.12)); }\n.dsh-kb-config__select-trigger[aria-expanded='true'] { border-color: var(--dsw-alias-label-primary, currentColor); }\n.dsh-kb-config__select-placeholder { color: var(--dsw-alias-label-tertiary, #999); }\n.dsh-kb-config__select-trigger > span:first-child { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.dsh-kb-config__select-chevron { flex: 0 0 auto; display: inline-flex; color: var(--dsw-alias-label-tertiary, #999); }\n.dsh-kb-config__menu {\n position: absolute;\n z-index: 30;\n top: calc(100% + 6px);\n left: 0;\n right: 0;\n margin: 0;\n padding: 6px;\n list-style: none;\n border: 1px solid var(--dsh-kb-config-border);\n border-radius: var(--dsh-kb-config-radius-lg);\n background: var(--dsw-alias-bg-layer-3, #222);\n color: var(--dsw-alias-label-primary, inherit);\n box-shadow: 0 8px 24px rgba(0, 0, 0, 0.24);\n max-height: 260px;\n overflow: auto;\n}\n.dsh-kb-config__option {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n width: 100%;\n padding: 8px 10px;\n border: none;\n border-radius: var(--dsh-kb-config-radius-md);\n background: transparent;\n color: inherit;\n font: inherit;\n text-align: left;\n cursor: pointer;\n transition: background-color 150ms ease;\n}\n.dsh-kb-config__option:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(127, 127, 127, 0.12)); }\n.dsh-kb-config__option[data-selected='true'] { color: var(--dsw-alias-state-business-primary, #4b7bff); }\n.dsh-kb-config__option > span:first-child { overflow: hidden; text-overflow: ellipsis; white-space: normal; word-break: break-word; }\n.dsh-kb-config__option-check { flex: 0 0 auto; display: inline-flex; }\n\n.dsh-kb-config__label { display: block; margin: 12px 0 6px; font-weight: 600; }\n.dsh-kb-config__help { color: var(--dsw-alias-label-secondary, #888); font-size: 12px; display: block; margin: 6px 0 0; }\n.dsh-kb-config__role { display: grid; grid-template-columns: 32px 1fr 1fr 1fr auto; gap: 10px; margin-bottom: 12px; align-items: center; }\n.dsh-kb-config__src { color: var(--dsw-alias-label-secondary, #888); font-size: 12px; white-space: nowrap; padding-left: 4px; }\n.dsh-kb-config__footer { display: flex; justify-content: flex-end; margin-top: 4px; }\n.dsh-kb-config__footer button {\n padding: 6px 16px;\n border: 1px solid var(--dsh-kb-config-border);\n border-radius: var(--dsh-kb-config-radius-md);\n background: transparent;\n color: inherit;\n font: inherit;\n cursor: pointer;\n transition: background-color 150ms ease;\n}\n.dsh-kb-config__footer button:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(127, 127, 127, 0.12)); }\n.dsh-kb-config__error { color: var(--dsw-alias-state-error-primary, #e5484d); }\n\n/* \u6A21\u578B\u94FE\u5361\uFF1A\u5BBF\u4E3B provider \u5361\u5C55\u5F00\u98CE\u2014\u2014\u89D2\u8272\u5934\u53EF\u6298\u53E0\u3001\u5B57\u6BB5 label \u5728\u4E0A + \u63A7\u4EF6\u5168\u5BBD\u3001\u964D\u7EA7\u4E3A\u6761\u76EE\u7EC4 */\n.dsh-kb-config__role-block { margin-bottom: 4px; }\n.dsh-kb-config__role-head {\n display: flex;\n align-items: center;\n gap: 8px;\n width: 100%;\n padding: 10px 10px;\n border: none;\n border-radius: var(--dsh-kb-config-radius-md);\n background: transparent;\n color: inherit;\n font: inherit;\n text-align: left;\n cursor: pointer;\n}\n.dsh-kb-config__role-head:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(127, 127, 127, 0.12)); }\n.dsh-kb-config__chevron { flex: 0 0 auto; display: inline-flex; color: var(--dsw-alias-label-tertiary, #999); transition: transform 150ms ease; }\n.dsh-kb-config__chevron--open { transform: rotate(90deg); }\n.dsh-kb-config__role-summary {\n margin-left: auto;\n color: var(--dsw-alias-label-secondary, #888);\n font-size: 12px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n/* \u5C55\u5F00\u4F53\u80CC\u666F\u8272\uFF1A\u4E0E\u6298\u53E0\u89D2\u8272\u5934\u5F62\u6210\u5757\u7EA7\u533A\u5206\uFF08\u540C\u5BBF\u4E3B\u8BBE\u7F6E\u5185\u5D4C\u9762\u677F\u5C42\u6B21\uFF09 */\n.dsh-kb-config__role-body {\n padding: 12px 14px 16px;\n margin: 8px 0 4px;\n background: var(--dsw-alias-interactive-bg-hover, rgba(127, 127, 127, 0.08));\n border-radius: var(--dsh-kb-config-radius-md);\n}\n.dsh-kb-config__group-label { display: block; margin: 10px 0 8px; font-weight: 600; }\n.dsh-kb-config__group-label:first-child { margin-top: 0; }\n.dsh-kb-config__field { display: block; margin: 0 0 10px; }\n.dsh-kb-config__field-label { display: block; margin: 0 0 6px; color: var(--dsw-alias-label-secondary, #888); font-size: 12px; }\n.dsh-kb-config__fb-group {\n margin: 0 0 10px;\n padding: 10px 12px;\n border: 1px solid var(--dsh-kb-config-border);\n border-radius: var(--dsh-kb-config-radius-md);\n}\n.dsh-kb-config__fb-head {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin: 0 0 8px;\n}\n.dsh-kb-config__fb-title { color: var(--dsw-alias-label-secondary, #888); font-size: 12px; }\n.dsh-kb-config__row-btn {\n padding: 6px 10px;\n border: 1px solid var(--dsh-kb-config-border);\n border-radius: var(--dsh-kb-config-radius-md);\n background: transparent;\n color: inherit;\n font: inherit;\n font-size: 12px;\n white-space: nowrap;\n cursor: pointer;\n transition: background-color 150ms ease;\n}\n.dsh-kb-config__row-btn:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-hover, rgba(127, 127, 127, 0.12)); }\n.dsh-kb-config__row-btn:disabled { opacity: 0.5; cursor: not-allowed; }\n.dsh-kb-config__role-actions { display: flex; gap: 8px; margin-top: 12px; }\n.dsh-kb-config__role--official {\n display: flex;\n gap: 10px;\n align-items: center;\n margin: 12px 0 0;\n padding: 8px 10px;\n border: 1px dashed var(--dsh-kb-config-border);\n border-radius: var(--dsh-kb-config-radius-md);\n color: var(--dsw-alias-label-secondary, #888);\n font-size: 12px;\n}\n.dsh-kb-config__badge {\n flex: 0 0 auto;\n padding: 2px 8px;\n border-radius: 999px;\n background: var(--dsw-alias-interactive-bg-hover, rgba(127, 127, 127, 0.12));\n color: var(--dsw-alias-label-primary, inherit);\n font-size: 12px;\n white-space: nowrap;\n}\n.dsh-kb-config__muted { color: var(--dsw-alias-label-tertiary, #999); }\n";
|
|
2897
3073
|
|
|
2898
3074
|
// client/index.ts
|
|
2899
3075
|
var name = "kanban-board";
|
|
2900
|
-
var inject = ["slots", "sessions"];
|
|
3076
|
+
var inject = ["slots", "sessions", "uiWorkspace"];
|
|
2901
3077
|
function apply(ctx) {
|
|
2902
3078
|
setSessionsService(ctx.sessions);
|
|
3079
|
+
setSessionNavigator(() => ctx.uiWorkspace ?? null);
|
|
2903
3080
|
let style = null;
|
|
2904
3081
|
let configStyle = null;
|
|
2905
3082
|
if (typeof document !== "undefined") {
|
|
@@ -2936,6 +3113,7 @@ window.__ModuleLoader__.load({
|
|
|
2936
3113
|
if (style) style.remove();
|
|
2937
3114
|
if (configStyle) configStyle.remove();
|
|
2938
3115
|
setSessionsService(null);
|
|
3116
|
+
setSessionNavigator(null);
|
|
2939
3117
|
};
|
|
2940
3118
|
}
|
|
2941
3119
|
|
package/lib/config.d.ts
CHANGED
|
@@ -19,12 +19,19 @@ export interface KanbanConfig {
|
|
|
19
19
|
provider: string;
|
|
20
20
|
model: string;
|
|
21
21
|
reasoningEffort?: string;
|
|
22
|
+
/** 降级候选(顺序=优先序,最多 2 个);仅 primary 配齐时生效。 */
|
|
22
23
|
fallbacks?: Array<{
|
|
23
24
|
provider: string;
|
|
24
25
|
model: string;
|
|
25
26
|
reasoningEffort?: string;
|
|
26
27
|
}>;
|
|
27
28
|
}>>;
|
|
29
|
+
/** 全局官方兜底(所有角色共用):GUI 未配角色链时单候选直用;有配置时垫主+降级链尾。 */
|
|
30
|
+
chainFallback: {
|
|
31
|
+
provider: string;
|
|
32
|
+
model: string;
|
|
33
|
+
reasoningEffort?: string;
|
|
34
|
+
};
|
|
28
35
|
};
|
|
29
36
|
dispatcher: {
|
|
30
37
|
staleTimeoutSeconds: number;
|
package/lib/config.js
CHANGED
|
@@ -13,7 +13,7 @@ const modelItemSchema = () => Schema.object({
|
|
|
13
13
|
provider: Schema.string().required(),
|
|
14
14
|
model: Schema.string().required(),
|
|
15
15
|
reasoningEffort: Schema.string().default('high'),
|
|
16
|
-
})).default([]),
|
|
16
|
+
})).max(2).default([]),
|
|
17
17
|
});
|
|
18
18
|
export const Config = Schema.object({
|
|
19
19
|
storageDir: Schema.string().default('$DSH_HOME/storages/kanban'),
|
|
@@ -25,6 +25,12 @@ export const Config = Schema.object({
|
|
|
25
25
|
// 角色系统提示词经 personas/kanban-{v,p,w,d}/agent.cordis.yml 组合装配(agentPresets.mount),
|
|
26
26
|
// 随包安装到 $DSH_HOME/.agent-presets/(preset-installer),不再经 config 引用 md 文本。
|
|
27
27
|
models: Schema.dict(modelItemSchema()).default({}),
|
|
28
|
+
// 官方兜底默认空(不启用);部署经 bundle patch 配值(如官方账号路由 deepseek-account/deepseek-flash)。
|
|
29
|
+
chainFallback: Schema.object({
|
|
30
|
+
provider: Schema.string().default(''),
|
|
31
|
+
model: Schema.string().default(''),
|
|
32
|
+
reasoningEffort: Schema.string().default('high'),
|
|
33
|
+
}).default({ provider: '', model: '', reasoningEffort: 'high' }),
|
|
28
34
|
}),
|
|
29
35
|
dispatcher: Schema.object({
|
|
30
36
|
staleTimeoutSeconds: Schema.number().default(14400),
|
|
@@ -5,6 +5,10 @@ import type { Role } from '../domain/types.js';
|
|
|
5
5
|
import type { WikiVaultClient } from '../wiki/wiki-vault-client.js';
|
|
6
6
|
import { probeOcr } from '../services/ocr-cli.js';
|
|
7
7
|
import type { AgentModelOptions } from './dispatcher.js';
|
|
8
|
+
/** 关键事件追加落盘 storageDir/dispatcher.log(与 dispatcher.ts 同一文件、同一行格式)——
|
|
9
|
+
* agent-runner 未注入 logFile,按 dispatcher 同款方式经 configProvider 派生 storageDir;
|
|
10
|
+
* 写失败静默忽略(日志只是观测面,事件日志才是事实源)。 */
|
|
11
|
+
export declare function logToDispatcherLog(configProvider: ConfigProvider, msg: string): void;
|
|
8
12
|
/** 角色组合标记(会话10事故根因A):setup 成功组合角色工具面后写入,live 复用前校验。 */
|
|
9
13
|
interface RoleCompositionMarker {
|
|
10
14
|
role: Role;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { SessionId } from '@deepseek-ai/dsh-session';
|
|
2
2
|
import { writeFileSync } from 'node:fs';
|
|
3
|
+
import { randomUUID } from 'node:crypto';
|
|
3
4
|
import { join } from 'node:path';
|
|
4
5
|
import { probeOcr } from '../services/ocr-cli.js';
|
|
5
6
|
import { installRoleTools, buildReadOnlyWriteGuard, buildDTWriteGuard, buildPlanWriteGuard, buildKbWriteGuard, registerDtTaskChain, unregisterDtTaskChain } from '../roles/toolsets.js';
|
|
@@ -7,6 +8,7 @@ import { ensureLocalKbRoot } from '../wiki/local-kb.js';
|
|
|
7
8
|
import { installCleanFsTools } from '../roles/clean-fs-tools.js';
|
|
8
9
|
import { mountOrRecompose } from '../roles/preset-installer.js';
|
|
9
10
|
import { buildModelCandidates, isModelUnavailableError } from './model-candidates.js';
|
|
11
|
+
import { registerModelChain } from './model-chain.js';
|
|
10
12
|
import { toolName, isGuardSynthesizedReply, lastTurnEnd } from './session-events.js';
|
|
11
13
|
import { filterCandidatesByCatalog } from '../services/llm-catalog.js';
|
|
12
14
|
import { attachSessionToWorkspace, resolveOrCreateWorkspace } from './workspace-attach.js';
|
|
@@ -30,14 +32,14 @@ function isInfraError(err) {
|
|
|
30
32
|
function classifyTurnError(code, message) {
|
|
31
33
|
if (code === 'UNKNOWN_MODEL' || code === 'UNSUPPORTED_REASONING_EFFORT' || code === 'INVALID_REQUEST')
|
|
32
34
|
return 'config';
|
|
33
|
-
if (code
|
|
35
|
+
if ((code ?? '').includes('QUOTA') || /quota|rate.?limit|\b429\b|insufficient balance/i.test(message) || isInfraError(message))
|
|
34
36
|
return 'transient';
|
|
35
37
|
return 'unknown';
|
|
36
38
|
}
|
|
37
39
|
/** 关键事件追加落盘 storageDir/dispatcher.log(与 dispatcher.ts 同一文件、同一行格式)——
|
|
38
40
|
* agent-runner 未注入 logFile,按 dispatcher 同款方式经 configProvider 派生 storageDir;
|
|
39
41
|
* 写失败静默忽略(日志只是观测面,事件日志才是事实源)。 */
|
|
40
|
-
function logToDispatcherLog(configProvider, msg) {
|
|
42
|
+
export function logToDispatcherLog(configProvider, msg) {
|
|
41
43
|
try {
|
|
42
44
|
const storageDir = configProvider.getEffective().storageDir.replace('$DSH_HOME', process.env.DSH_HOME ?? process.cwd());
|
|
43
45
|
writeFileSync(join(storageDir, 'dispatcher.log'), new Date().toISOString() + ' ' + msg + '\n', { flag: 'a' });
|
|
@@ -266,7 +268,7 @@ ${task.body}`);
|
|
|
266
268
|
(task.attempts > 0 || state.events.some((e) => e.taskId === taskId && e.kind === 'task/claimed'));
|
|
267
269
|
let agent;
|
|
268
270
|
let context = '';
|
|
269
|
-
const setup = async (agentCtx) => {
|
|
271
|
+
const setup = async (agentCtx, agent) => {
|
|
270
272
|
// 思考等级强制(waterfall):宿主 selection 无 create-options 覆盖层(dsh-host-apiproxy 的
|
|
271
273
|
// selectionFor 不消费 agentOptions),AgentOptions 也仅有 provider/model/maxTokens——agentOptions
|
|
272
274
|
// 里的 reasoningEffort 不被宿主消费,新建角色会话思考等级会落回宿主默认。改走 DSH agent/request
|
|
@@ -283,7 +285,8 @@ ${task.body}`);
|
|
|
283
285
|
// P/W/D = full access(跨目录读:P 读仓库/外部实证、W 读计划、D 执行);
|
|
284
286
|
// 但 P 挂 plan 写护栏、W 挂只读护栏(写边界由工具级强制,防"改动源码",不靠 prompt 软约束);
|
|
285
287
|
// PT/DT/V → workspace-write(评审/编排最小权限,PT/DT 只读护栏由 ToolGuard 独立保证)。
|
|
286
|
-
|
|
288
|
+
// approval/sandbox 种子事件经 AgentSetup 第二参的 agent 显式直写(0.2.0 起 ctx.agent 挂载已移除)。
|
|
289
|
+
const session = agent?.session;
|
|
287
290
|
session?.append?.('approval/policy', { policy: 'never', source: 'delegation' });
|
|
288
291
|
const fullAccess = isDExecute || task.assignee === 'p' || task.assignee === 'w';
|
|
289
292
|
session?.append?.('sandbox/mode', fullAccess ? { mode: 'danger-full-access', source: 'delegation' } : { mode: 'workspace-write', source: 'delegation' });
|
|
@@ -318,16 +321,13 @@ ${task.body}`);
|
|
|
318
321
|
// PT/DT 是 workspace-write(sandbox_permissions 扩权到天花板是合法功能,剥了破坏官方能力);
|
|
319
322
|
// W 虽 fullAccess 但只读护栏全拒 write,shadow 无意义(最小挂载面,V 无执行工具不涉及)。
|
|
320
323
|
if (task.assignee === 'p' || isDExecute) {
|
|
321
|
-
installCleanFsTools(agentCtx,
|
|
324
|
+
installCleanFsTools(agentCtx, agent);
|
|
322
325
|
}
|
|
323
326
|
// 组合标记——角色工具面成功组合后,把 { role, taskId } 记到进程内 WeakMap(键=Agent 实例)。
|
|
324
|
-
//
|
|
325
|
-
//
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
const liveAgent = agentCtx.agent;
|
|
329
|
-
if (liveAgent)
|
|
330
|
-
markRoleComposition(liveAgent, { role: task.assignee, taskId: task.id });
|
|
327
|
+
// AgentSetup 第二参收到的 Agent 与发布后 agents.get(id) 返回的是同一实例,故此处键入的实例
|
|
328
|
+
// 即 resumeOrReuse 里 agents.get 命中的实例(live 复用走标记匹配,能力无损)。
|
|
329
|
+
if (agent)
|
|
330
|
+
markRoleComposition(agent, { role: task.assignee, taskId: task.id });
|
|
331
331
|
// 只读评审角色(PT/DT)注册 ToolGuard:拦截 tracked source 写入 / git mutation / 含写标记 bash。
|
|
332
332
|
// 以 dsh-tools 类型为准:tools.guard(execution => reason|undefined),execution.name/arguments 为实际字段。
|
|
333
333
|
if (task.assignee === 'pt' || task.assignee === 'dt') {
|
|
@@ -396,7 +396,7 @@ ${task.body}`);
|
|
|
396
396
|
const builtCandidates = buildModelCandidates(this.configProvider.getEffective(), task.assignee, this.defaultModel);
|
|
397
397
|
// 派发前预校验(LLM 目录):不存在的 provider/model 组合、模型未声明的 reasoningEffort
|
|
398
398
|
// 直接剔除,不再打进网络白烧会话(UNKNOWN_MODEL / UNSUPPORTED_REASONING_EFFORT 秒退实证形态——
|
|
399
|
-
//
|
|
399
|
+
// 必须前置拦截)。
|
|
400
400
|
// fail-open:llm 服务未就绪/目录探测失败 → 全放行,不阻断派发。
|
|
401
401
|
// 全部候选被拒 → block(model-unavailable) 终态(与 spawn 全候选失败同语义,等人工修配置)。
|
|
402
402
|
let candidates = builtCandidates;
|
|
@@ -422,6 +422,7 @@ ${task.body}`);
|
|
|
422
422
|
}
|
|
423
423
|
candidates = precheck.ok;
|
|
424
424
|
}
|
|
425
|
+
let chainStartIndex = 0;
|
|
425
426
|
if (candidates.length === 0) {
|
|
426
427
|
// 无任何候选配置:不传 agentOptions(用部署默认),单次尝试
|
|
427
428
|
agent = hasRunHistory
|
|
@@ -435,7 +436,7 @@ ${task.body}`);
|
|
|
435
436
|
else {
|
|
436
437
|
const agents = this.ctx.get('agents');
|
|
437
438
|
let spawnError = null;
|
|
438
|
-
for (const candidate of candidates) {
|
|
439
|
+
for (const [candidateIndex, candidate] of candidates.entries()) {
|
|
439
440
|
try {
|
|
440
441
|
// hasRunHistory → resumeOrReuse 直接返回 AgentLike(内部已解包 .agent);create 返回 { agent } 需解包。
|
|
441
442
|
// 统一归一化为 AgentLike,避免二次解包(h.agent=undefined → if(!agent) 误标 failed)。
|
|
@@ -443,6 +444,7 @@ ${task.body}`);
|
|
|
443
444
|
? await this.resumeOrReuse(agents, task.resumeSessionId ?? `kbn-${taskId}`, { agentOptions: candidate, setup, role: task.assignee, taskId: task.id })
|
|
444
445
|
: (await agents.create({ sessionId: SessionId(`kbn-${taskId}`), meta: { cwd: sessionCwd }, agentOptions: candidate, setup })).agent;
|
|
445
446
|
agent = h;
|
|
447
|
+
chainStartIndex = candidateIndex;
|
|
446
448
|
// 切换成功且非首选 → 发可审计 model/fallback 评论(记录证据,不弹用户)
|
|
447
449
|
if (candidate !== candidates[0]) {
|
|
448
450
|
try {
|
|
@@ -468,6 +470,14 @@ ${task.body}`);
|
|
|
468
470
|
throw spawnError;
|
|
469
471
|
}
|
|
470
472
|
}
|
|
473
|
+
if (agent && candidates.length > 0) {
|
|
474
|
+
registerModelChain(agent, {
|
|
475
|
+
candidates,
|
|
476
|
+
index: chainStartIndex,
|
|
477
|
+
taskId,
|
|
478
|
+
comment: async (body) => { await this.kanban.comment(taskId, body, 'system'); },
|
|
479
|
+
});
|
|
480
|
+
}
|
|
471
481
|
}
|
|
472
482
|
catch (err) {
|
|
473
483
|
// claim/buildContext/spawn 失败也走 failed(attempts 递增)→ 调度器重派/看门狗熔断;不再让任务永久 claimed
|
|
@@ -492,7 +502,7 @@ ${task.body}`);
|
|
|
492
502
|
const attachId = task.resumeSessionId ?? `kbn-${task.id}`;
|
|
493
503
|
await attachSessionToWorkspace(this.ctx, attachId, sessionCwd, 'task ' + task.id + ' ' + task.assignee + '/' + task.mode);
|
|
494
504
|
try {
|
|
495
|
-
agent.followup({ content: [{ type: 'text', text: context }], source: { kind: 'user' } });
|
|
505
|
+
agent.followup({ id: randomUUID(), content: [{ type: 'text', text: context }], source: { kind: 'user' } });
|
|
496
506
|
console.error('[dsh-swarm][debug] runner followup sent ' + taskId);
|
|
497
507
|
await agent.whenIdle();
|
|
498
508
|
console.error('[dsh-swarm][debug] runner whenIdle resolved ' + taskId);
|
|
@@ -510,7 +520,7 @@ ${task.body}`);
|
|
|
510
520
|
: (sessionLike.events ?? []);
|
|
511
521
|
if (turnEvents.filter((e) => (Number(e?.seq) || 0) > eventsBase).some(isGuardSynthesizedReply)) {
|
|
512
522
|
console.error('[dsh-swarm][debug] runner guard-synthesized reply detected (from-cache/from-security-guard) ' + taskId + ' — auto followup retry once');
|
|
513
|
-
agent.followup({ content: [{ type: 'text', text: '继续未完成的任务:上一条回复是网关安全护栏/语义缓存误拦的合成拒答,并非你的真实结论。请忽略它,从中断处继续执行任务,直至按协议收敛终态(kanban_complete 或 kanban_block)。' }], source: { kind: 'user' } });
|
|
523
|
+
agent.followup({ id: randomUUID(), content: [{ type: 'text', text: '继续未完成的任务:上一条回复是网关安全护栏/语义缓存误拦的合成拒答,并非你的真实结论。请忽略它,从中断处继续执行任务,直至按协议收敛终态(kanban_complete 或 kanban_block)。' }], source: { kind: 'user' } });
|
|
514
524
|
console.error('[dsh-swarm][debug] runner guard-retry followup sent ' + taskId);
|
|
515
525
|
await agent.whenIdle();
|
|
516
526
|
console.error('[dsh-swarm][debug] runner guard-retry whenIdle resolved ' + taskId);
|
|
@@ -650,7 +660,8 @@ ${task.body}`);
|
|
|
650
660
|
// GUI 默认组合 incarnation:缺 kanban_complete → 重跑 setup 幂等补挂后复用。
|
|
651
661
|
// setup 对 live ctx 的各步均可加:effort waterfall/approval+sandbox append(known 事件类型、
|
|
652
662
|
// latest-wins)/preset mount(bindings 覆盖)/角色工具(此前为默认组合,无同名冲突)/护栏。
|
|
653
|
-
|
|
663
|
+
// 双参签名(0.2.0):repair 重跑 setup 时显式传 live agent 实例(标记随修复自愈)。
|
|
664
|
+
await opts.setup(liveCtx, live);
|
|
654
665
|
if (toolsSvc.get('kanban_complete', live))
|
|
655
666
|
return live; // 修复后必须验证到位,防静默半修复
|
|
656
667
|
throw new Error('live session composition repair failed: kanban_complete still missing after re-setup (session ' + sessionId + ')');
|
|
@@ -11,8 +11,9 @@ import type { AuditEvidence, Task } from '../domain/types.js';
|
|
|
11
11
|
* a. 作用域收窄:仅扫描会话工作区(session.header.cwd)位于本链发起工作区
|
|
12
12
|
* (Chain.workspaceDir = /plan: 主 agent 所在工作空间)内的会话,
|
|
13
13
|
* 排除其他项目的主会话(如 评估 项目里调试 dsh-swarm 的会话);
|
|
14
|
-
* b. 行为判定:run_code
|
|
15
|
-
*
|
|
14
|
+
* b. 行为判定:run_code 按实际派发子调用(新旧宿主双名 tool/code-dispatch[-start] /
|
|
15
|
+
* tool/ptc-dispatch[-start],经 rootCallId 关联外层调用)判定是否真的发生写,而非把
|
|
16
|
+
* run_code 一律视为写;
|
|
16
17
|
* c. 只读排除:bash 命令 / 兜底 code 字符串仅当含写操作标记(BASH_WRITE_RE)且
|
|
17
18
|
* 含 workspacesRoot 路径时才计为写证据;纯只读排查(ls/cat/glob/read/grep)不产生证据。
|
|
18
19
|
* 2. 产物归属核对(fallback,机械可测):枚举 workspaces/<chainId>/ 下条目;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// src/dispatcher/chain-auditor.ts
|
|
2
2
|
import { readdirSync } from 'node:fs';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
|
-
import { eventType, toolArgs, toolName } from './session-events.js';
|
|
4
|
+
import { eventType, isRunCodeDispatchEvent, toolArgs, toolName } from './session-events.js';
|
|
5
5
|
import { isPathInside } from './target-repo.js';
|
|
6
6
|
import { headerPresetOf } from './session-preset.js';
|
|
7
7
|
/** 直接写工具(无条件视为写能力;只读工具如 read/glob/grep 不算)。 */
|
|
@@ -113,7 +113,7 @@ function sessionWriteEvidence(id, workspacesRoot, events) {
|
|
|
113
113
|
}
|
|
114
114
|
}
|
|
115
115
|
}
|
|
116
|
-
else if (
|
|
116
|
+
else if (isRunCodeDispatchEvent(ev)) {
|
|
117
117
|
const rootCallId = String(dataField(ev, 'rootCallId') ?? '');
|
|
118
118
|
const name = String(dataField(ev, 'name') ?? '');
|
|
119
119
|
if (!rootCallId || !name)
|
|
@@ -2,12 +2,18 @@ import type { KanbanConfig } from '../config.js';
|
|
|
2
2
|
import type { Role } from '../domain/types.js';
|
|
3
3
|
import type { AgentModelOptions } from './dispatcher.js';
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
5
|
+
* 模型候选链(四层):
|
|
6
|
+
* ① 角色 primary(config.roles.models[role])→ ② 降级候选(fallbacks,顺序=优先序,≤2)
|
|
7
|
+
* → ③ 全局官方兜底(config.roles.chainFallback,恒垫链尾)→ ④ 宿主默认模型(defaultModel)。
|
|
8
|
+
* 角色链全空 → 官方兜底单候选直用;官方兜底也未配 → 宿主默认模型;都无 → 空链(调用方不传 agentOptions)。
|
|
7
9
|
* reasoningEffort 未指定一律 'high'。
|
|
8
10
|
*
|
|
9
11
|
* @returns 有序候选链(不含不可用者;可能为空 = 无任何配置)
|
|
10
12
|
*/
|
|
11
13
|
export declare function buildModelCandidates(config: KanbanConfig, role: Role, defaultModel?: AgentModelOptions): AgentModelOptions[];
|
|
12
|
-
/**
|
|
14
|
+
/**
|
|
15
|
+
* 判定 create/resume 错误是否属于候选不可用(可静默切换下一候选)。
|
|
16
|
+
* 覆盖三类:provider/model 不可用(无适配器/不存在)、凭据缺失(官方账号未登录
|
|
17
|
+
* MISSING_CREDENTIAL / 登录提示——不切换会把登录态问题误判成任务失败)。
|
|
18
|
+
*/
|
|
13
19
|
export declare function isModelUnavailableError(err: unknown): boolean;
|