dsh-kenari-plugin 0.1.1 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +15 -10
- package/README.md +15 -10
- package/client/index.js +125 -14
- package/cordis.patch.yml +19 -22
- package/lib/catalog-view.d.ts +4 -0
- package/lib/catalog-view.d.ts.map +1 -1
- package/lib/catalog-view.js.map +1 -1
- package/lib/catalog.d.ts +18 -4
- package/lib/catalog.d.ts.map +1 -1
- package/lib/catalog.js +19 -5
- package/lib/catalog.js.map +1 -1
- package/lib/default-route.d.ts +24 -0
- package/lib/default-route.d.ts.map +1 -1
- package/lib/default-route.js +59 -1
- package/lib/default-route.js.map +1 -1
- package/lib/index.d.ts +4 -2
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +19 -8
- package/lib/index.js.map +1 -1
- package/lib/llm/candidates.d.ts +4 -1
- package/lib/llm/candidates.d.ts.map +1 -1
- package/lib/llm/candidates.js +9 -5
- package/lib/llm/candidates.js.map +1 -1
- package/lib/llm/recovery.d.ts +19 -7
- package/lib/llm/recovery.d.ts.map +1 -1
- package/lib/llm/recovery.js +97 -12
- package/lib/llm/recovery.js.map +1 -1
- package/lib/llm/retry.d.ts +14 -16
- package/lib/llm/retry.d.ts.map +1 -1
- package/lib/llm/retry.js +20 -15
- package/lib/llm/retry.js.map +1 -1
- package/package.json +4 -2
package/lib/llm/recovery.js
CHANGED
|
@@ -1,20 +1,33 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Kenari
|
|
2
|
+
* Kenari 路由的模型失败恢复:同路由重试 → 换模型 → 回退默认 provider,逐级升级。
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* 三个阶段都由本模块负责,`dsh-llm-retry` 在 Kenari 路由上是关掉的(见 `./retry.ts`)。
|
|
5
|
+
* 原因:`agent/request-error` 是没有默认行为的瀑布,不调用 `next()` 的监听器会否决它后面的
|
|
6
|
+
* 一切;只要注册顺序被 live reload 翻过来一次,排在后面的 llm-retry 就永久出局,而插件
|
|
7
|
+
* 观察不到。实测线上 `llm/retry` 事件数为 0、换模型发生在第一次失败上,说明承诺的 5 次重试
|
|
8
|
+
* 从未执行。把重试收进自己的状态机,行为才是可验证、可测试的。
|
|
6
9
|
*
|
|
7
10
|
* 两个监听器的注册顺序是设计的一部分,不是随手写的:
|
|
8
11
|
* 1. `agent/request` 用 `prepend` 挂在**最外层**。瀑布里外层拿最终决定权,而
|
|
9
12
|
* `installModelSelection` 会用「本 step 组装时捕获的」原模型回写 —— 排在内层会被它改回去
|
|
10
13
|
* (`packages/core/agent/src/model-selection.ts:88-105`)。
|
|
11
|
-
* 2. `agent/request-error`
|
|
12
|
-
*
|
|
14
|
+
* 2. `agent/request-error` 用默认顺序,但**先 `await next()`**:链上(`dsh-llm-retry`、
|
|
15
|
+
* compaction 等)若已决定重试,我们原样放行;只有链上放弃时才走自己的升级。这样无论
|
|
16
|
+
* 注册顺序如何,插件都不会饿死别人的恢复策略。
|
|
13
17
|
* @module dsh-kenari-plugin/llm/recovery
|
|
14
18
|
*/
|
|
15
19
|
import { createUserMessage } from '@deepseek-ai/dsh-llm';
|
|
16
20
|
import { isRecoveryProvider, sleepUnlessAborted } from './retry.js';
|
|
17
|
-
/**
|
|
21
|
+
/** 先让链上决定。`next()` 抛错不该毁掉我们的恢复,但要留痕。 */
|
|
22
|
+
async function settleDownstream(next) {
|
|
23
|
+
try {
|
|
24
|
+
return { type: 'decision', decision: await next() };
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
return { type: 'error', error };
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/** 决定本次失败后进入哪个升级阶段;undefined 表示放弃。 */
|
|
18
31
|
function planStage(stage, code, deps) {
|
|
19
32
|
if (!deps.switchEnabled)
|
|
20
33
|
return undefined;
|
|
@@ -29,6 +42,15 @@ export function installModelRecovery(ctx, deps) {
|
|
|
29
42
|
if (!deps.enabled)
|
|
30
43
|
return;
|
|
31
44
|
const states = new WeakMap();
|
|
45
|
+
/**
|
|
46
|
+
* 一个会话在本 step 内由链上策略(`dsh-llm-retry`)排程的重试次数。
|
|
47
|
+
*
|
|
48
|
+
* 用途只有一个:**判断这轮重试该由谁负责**。链上已经在重试时我们不再补一轮自己的预算,
|
|
49
|
+
* 否则两条机制会叠加成两倍的等待与请求。出厂配置把 Kenari 路由的 dsh 重试关掉了
|
|
50
|
+
* (`cordis.patch.yml` 的 `maxRetries: 0`),所以正常情况这里恒为 0,重试由我们负责;
|
|
51
|
+
* 用户可以重新打开它,那时就交给 dsh,我们只在它一次都没出手时才兜底。
|
|
52
|
+
*/
|
|
53
|
+
const scheduled = new WeakMap();
|
|
32
54
|
// 最外层:先拿到全链结果再覆盖,才能盖过 installModelSelection 用组装快照做的回写
|
|
33
55
|
ctx.on('agent/request', async ({ agent }, next) => {
|
|
34
56
|
const resolved = await next();
|
|
@@ -44,7 +66,16 @@ export function installModelRecovery(ctx, deps) {
|
|
|
44
66
|
...(target.reasoningEffort === undefined ? {} : { reasoningEffort: target.reasoningEffort }),
|
|
45
67
|
};
|
|
46
68
|
}, { prepend: true });
|
|
47
|
-
ctx.on('agent/request-error', async ({ agent, turn, step, provider, failure, signal }) => {
|
|
69
|
+
ctx.on('agent/request-error', async ({ agent, turn, step, provider, failure, signal }, next) => {
|
|
70
|
+
// 先交回链上:别人的重试决定优先,我们只在链上放弃后才介入。
|
|
71
|
+
// 不调用 next() 会否决链上后面的一切 —— 那是这次「重试静默消失」的根因之一。
|
|
72
|
+
const downstream = await settleDownstream(next);
|
|
73
|
+
if (downstream.type === 'decision' && downstream.decision?.kind === 'retry') {
|
|
74
|
+
return downstream.decision;
|
|
75
|
+
}
|
|
76
|
+
if (downstream.type === 'error') {
|
|
77
|
+
deps.logger?.warn(`kenari: agent/request-error 下游策略抛错,改由本地恢复接手:${String(downstream.error)}`);
|
|
78
|
+
}
|
|
48
79
|
if (!isRecoveryProvider(provider, deps.providers))
|
|
49
80
|
return undefined;
|
|
50
81
|
if (signal.aborted)
|
|
@@ -54,10 +85,47 @@ export function installModelRecovery(ctx, deps) {
|
|
|
54
85
|
const currentModel = current?.model;
|
|
55
86
|
if (currentModel === undefined)
|
|
56
87
|
return undefined;
|
|
88
|
+
const route = `${provider}/${currentModel}`;
|
|
57
89
|
const stepKey = `${turn}:${step}`;
|
|
58
90
|
const previous = states.get(session);
|
|
59
|
-
const state = previous?.stepKey === stepKey
|
|
91
|
+
const state = previous?.stepKey === stepKey
|
|
92
|
+
? previous
|
|
93
|
+
: { stepKey, stage: 0, tried: new Set(), ownRetries: 0, retryNoticeSent: false };
|
|
60
94
|
states.set(session, state);
|
|
95
|
+
state.tried.add(route);
|
|
96
|
+
// 路由变了(换过模型或回退过 provider)就重置重试预算:新模型应当拿到完整的一轮
|
|
97
|
+
if (state.ownRoute !== route) {
|
|
98
|
+
state.ownRoute = route;
|
|
99
|
+
state.ownRetries = 0;
|
|
100
|
+
state.retryNoticeSent = false;
|
|
101
|
+
}
|
|
102
|
+
// 阶段 0:同一路由重试。瞬时类失败重发有意义,重试预算按路由走。
|
|
103
|
+
// 链上(llm-retry)这轮已经排程过重试时让位给它 —— 两条机制各数一遍会叠成两倍等待。
|
|
104
|
+
const chained = scheduled.get(session);
|
|
105
|
+
const chainedRetries = chained !== undefined && chained.stepKey === stepKey ? chained.count : 0;
|
|
106
|
+
if (chainedRetries === 0
|
|
107
|
+
&& deps.retryCodes.includes(failure.code)
|
|
108
|
+
&& state.ownRetries < deps.retryMaxRetries) {
|
|
109
|
+
state.ownRetries += 1;
|
|
110
|
+
deps.logger?.info(`kenari: ${route} 失败(${failure.code}),本地重试 ${state.ownRetries}/${deps.retryMaxRetries}`);
|
|
111
|
+
if (deps.retryNoticeEnabled && !state.retryNoticeSent) {
|
|
112
|
+
// 重试期间界面上只有一次失败的尝试,不解释的话就是一个没有理由的等待
|
|
113
|
+
state.retryNoticeSent = true;
|
|
114
|
+
agent.inject(createUserMessage({
|
|
115
|
+
content: [{
|
|
116
|
+
type: 'text',
|
|
117
|
+
text: `[kenari: ${route} 失败(${failure.code}),本地重试最多 ${deps.retryMaxRetries} 次(每次 ${deps.retryDelayMs}ms)]`,
|
|
118
|
+
}],
|
|
119
|
+
source: { kind: 'plugin', plugin: 'kenari', form: 'notice', summary: `${route} 重试 ${state.ownRetries}/${deps.retryMaxRetries}` },
|
|
120
|
+
}));
|
|
121
|
+
}
|
|
122
|
+
const retried = await sleepUnlessAborted(deps.retryDelayMs, signal);
|
|
123
|
+
if (!retried) {
|
|
124
|
+
states.delete(session);
|
|
125
|
+
return undefined;
|
|
126
|
+
}
|
|
127
|
+
return { kind: 'retry' };
|
|
128
|
+
}
|
|
61
129
|
const planned = planStage(state.stage, failure.code, deps);
|
|
62
130
|
if (planned === undefined) {
|
|
63
131
|
states.delete(session);
|
|
@@ -70,6 +138,8 @@ export function installModelRecovery(ctx, deps) {
|
|
|
70
138
|
provider,
|
|
71
139
|
model: currentModel,
|
|
72
140
|
...(current?.contextWindow === undefined ? {} : { contextWindow: current.contextWindow }),
|
|
141
|
+
// 本 step 里失败过的路由一律不再回头:同窗口的两个模型(`>=` 判定)否则会来回横跳
|
|
142
|
+
exclude: [...state.tried],
|
|
73
143
|
signal,
|
|
74
144
|
});
|
|
75
145
|
if (candidate !== undefined) {
|
|
@@ -88,7 +158,7 @@ export function installModelRecovery(ctx, deps) {
|
|
|
88
158
|
}
|
|
89
159
|
state.stage = entered;
|
|
90
160
|
state.target = target;
|
|
91
|
-
const from =
|
|
161
|
+
const from = route;
|
|
92
162
|
const to = `${target.provider}/${target.model}`;
|
|
93
163
|
deps.logger?.warn(`kenari: ${from} 失败(${failure.code}),${entered === 1 ? '换模型' : '回退默认 provider'}为 ${to}`);
|
|
94
164
|
if (deps.noticeEnabled) {
|
|
@@ -99,8 +169,8 @@ export function installModelRecovery(ctx, deps) {
|
|
|
99
169
|
source: { kind: 'plugin', plugin: 'kenari', form: 'notice', summary: `${from} → ${to}` },
|
|
100
170
|
}));
|
|
101
171
|
}
|
|
102
|
-
const
|
|
103
|
-
if (!
|
|
172
|
+
const switched = await sleepUnlessAborted(deps.switchDelayMs, signal);
|
|
173
|
+
if (!switched) {
|
|
104
174
|
states.delete(session);
|
|
105
175
|
return undefined;
|
|
106
176
|
}
|
|
@@ -109,10 +179,25 @@ export function installModelRecovery(ctx, deps) {
|
|
|
109
179
|
// 用户显式选模型(会话日志追加 model/selection)时让人的选择优先。
|
|
110
180
|
// 只有 /model 命令会追加该事件,所以不会误清我们的 override。
|
|
111
181
|
ctx.on('session/event', (session, event) => {
|
|
182
|
+
const shape = event;
|
|
112
183
|
// `model/selection` 的事件类型由 `@deepseek-ai/dsh-session-controller` 声明合并而来。
|
|
113
184
|
// 为了一个字符串比对上整个 session-controller 依赖不划算,所以按形状读。
|
|
114
|
-
if (
|
|
185
|
+
if (shape.type === 'model/selection') {
|
|
115
186
|
states.delete(session);
|
|
187
|
+
scheduled.delete(session);
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
// 数链上排程的重试。事件自带 turn/step,所以能按 step 归集,不必读会话上下文。
|
|
191
|
+
if (shape.type !== 'llm/retry')
|
|
192
|
+
return;
|
|
193
|
+
const { turn, step } = shape.data ?? {};
|
|
194
|
+
if (turn === undefined || step === undefined)
|
|
195
|
+
return;
|
|
196
|
+
const stepKey = `${turn}:${step}`;
|
|
197
|
+
const previous = scheduled.get(session);
|
|
198
|
+
scheduled.set(session, previous?.stepKey === stepKey
|
|
199
|
+
? { stepKey, count: previous.count + 1 }
|
|
200
|
+
: { stepKey, count: 1 });
|
|
116
201
|
});
|
|
117
202
|
}
|
|
118
203
|
//# sourceMappingURL=recovery.js.map
|
package/lib/llm/recovery.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"recovery.js","sourceRoot":"","sources":["../../src/llm/recovery.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"recovery.js","sourceRoot":"","sources":["../../src/llm/recovery.ts"],"names":[],"mappings":"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"}
|
package/lib/llm/retry.d.ts
CHANGED
|
@@ -1,31 +1,29 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* 模型失败恢复的共享件:路由判定、dsh 重试的关闭策略、可取消延时。
|
|
3
3
|
*
|
|
4
4
|
* 为什么策略在这里拼:`LlmAdapter.providerRetryPolicy()` 只在适配器注册那一刻被调用一次,
|
|
5
5
|
* 返回值随即被 dsh 冻结进注册记录(`packages/llm/llm/src/index.ts:436-442`)。所以这里产出的
|
|
6
|
-
* 是「注册时的配置快照」——
|
|
7
|
-
* 对结构性字段的说明一致。
|
|
6
|
+
* 是「注册时的配置快照」—— 与 plugin-rules #10 对结构性字段的说明一致。
|
|
8
7
|
* @module dsh-kenari-plugin/llm/retry
|
|
9
8
|
*/
|
|
10
9
|
import type { ResolvedRetryPolicy } from '@deepseek-ai/dsh-llm';
|
|
11
10
|
/** 判定一个 provider 路由是否参与模型失败恢复。路由 id 是精确匹配,不做大小写归一。 */
|
|
12
11
|
export declare function isRecoveryProvider(provider: string, configured: readonly string[]): boolean;
|
|
13
|
-
/** 重试策略的可调输入,取自插件 Config。 */
|
|
14
|
-
export interface KenariRetryInput {
|
|
15
|
-
/** 首次请求之后的额外重试次数。 */
|
|
16
|
-
maxRetries?: number;
|
|
17
|
-
/** 每次重试前的固定等待毫秒数。 */
|
|
18
|
-
delayMs?: number;
|
|
19
|
-
/** 允许重试的失败码;省略则用 dsh 默认集,但显式传空数组会被拒绝。 */
|
|
20
|
-
retryableCodes?: readonly string[];
|
|
21
|
-
}
|
|
22
12
|
/**
|
|
23
|
-
*
|
|
13
|
+
* 让 dsh 自带的重试在 Kenari 路由上停手:重试由插件自己的状态机负责(见 `./recovery.ts`)。
|
|
24
14
|
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
15
|
+
* 为什么不把重试交给 `dsh-llm-retry`:它会**静默消失**。`agent/request-error` 是没有
|
|
16
|
+
* 「默认行为」的瀑布 —— 排在后面的监听器只在前面调用 `next()` 时才被调用。只要注册顺序
|
|
17
|
+
* 被翻过来一次(live patch / HMR 重载后重注册),排在外层的策略就把 llm-retry 永久饿死,
|
|
18
|
+
* 而插件没有任何办法观察到这件事。实测线上会话里 `llm/retry` 事件数为 0,换模型发生在
|
|
19
|
+
* 第一次失败上 —— 承诺的 5 次重试从未执行过。重试是插件对用户承诺的行为,不能建立在一个
|
|
20
|
+
* 自己看不见的机制上,所以这里把它关掉,由 `recovery.ts` 自己数次数、自己等。
|
|
21
|
+
*
|
|
22
|
+
* 关闭方式只有一种:`maxRetries: 0` 让 `previousRetry >= maxRetries` 立刻成立,llm-retry
|
|
23
|
+
* 直接 `next()`。`retryableCodes` 必须非空(dsh 的校验),给一个占位码即可 —— 0 次重试时
|
|
24
|
+
* 它永远不会被读到。
|
|
27
25
|
*/
|
|
28
|
-
export declare function
|
|
26
|
+
export declare function dshRetryDisabledPolicy(): ResolvedRetryPolicy;
|
|
29
27
|
/**
|
|
30
28
|
* 可取消的等待。返回 false 表示等待期间被取消,调用方应当放弃本次恢复而不是继续重试。
|
|
31
29
|
*
|
package/lib/llm/retry.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"retry.d.ts","sourceRoot":"","sources":["../../src/llm/retry.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"retry.d.ts","sourceRoot":"","sources":["../../src/llm/retry.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAA;AAE/D,sDAAsD;AACtD,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAE3F;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,sBAAsB,IAAI,mBAAmB,CAQ5D;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,CAapF"}
|
package/lib/llm/retry.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* 模型失败恢复的共享件:路由判定、dsh 重试的关闭策略、可取消延时。
|
|
3
3
|
*
|
|
4
4
|
* 为什么策略在这里拼:`LlmAdapter.providerRetryPolicy()` 只在适配器注册那一刻被调用一次,
|
|
5
5
|
* 返回值随即被 dsh 冻结进注册记录(`packages/llm/llm/src/index.ts:436-442`)。所以这里产出的
|
|
6
|
-
* 是「注册时的配置快照」——
|
|
7
|
-
* 对结构性字段的说明一致。
|
|
6
|
+
* 是「注册时的配置快照」—— 与 plugin-rules #10 对结构性字段的说明一致。
|
|
8
7
|
* @module dsh-kenari-plugin/llm/retry
|
|
9
8
|
*/
|
|
10
9
|
import { resolveRetryPolicy } from '@deepseek-ai/dsh-llm';
|
|
@@ -13,21 +12,27 @@ export function isRecoveryProvider(provider, configured) {
|
|
|
13
12
|
return configured.includes(provider);
|
|
14
13
|
}
|
|
15
14
|
/**
|
|
16
|
-
*
|
|
15
|
+
* 让 dsh 自带的重试在 Kenari 路由上停手:重试由插件自己的状态机负责(见 `./recovery.ts`)。
|
|
17
16
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
17
|
+
* 为什么不把重试交给 `dsh-llm-retry`:它会**静默消失**。`agent/request-error` 是没有
|
|
18
|
+
* 「默认行为」的瀑布 —— 排在后面的监听器只在前面调用 `next()` 时才被调用。只要注册顺序
|
|
19
|
+
* 被翻过来一次(live patch / HMR 重载后重注册),排在外层的策略就把 llm-retry 永久饿死,
|
|
20
|
+
* 而插件没有任何办法观察到这件事。实测线上会话里 `llm/retry` 事件数为 0,换模型发生在
|
|
21
|
+
* 第一次失败上 —— 承诺的 5 次重试从未执行过。重试是插件对用户承诺的行为,不能建立在一个
|
|
22
|
+
* 自己看不见的机制上,所以这里把它关掉,由 `recovery.ts` 自己数次数、自己等。
|
|
23
|
+
*
|
|
24
|
+
* 关闭方式只有一种:`maxRetries: 0` 让 `previousRetry >= maxRetries` 立刻成立,llm-retry
|
|
25
|
+
* 直接 `next()`。`retryableCodes` 必须非空(dsh 的校验),给一个占位码即可 —— 0 次重试时
|
|
26
|
+
* 它永远不会被读到。
|
|
20
27
|
*/
|
|
21
|
-
export function
|
|
22
|
-
|
|
23
|
-
const config = {
|
|
28
|
+
export function dshRetryDisabledPolicy() {
|
|
29
|
+
return resolveRetryPolicy({
|
|
24
30
|
mode: 'normal',
|
|
25
|
-
maxRetries:
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
return resolveRetryPolicy(config, 'kenari.modelRecovery.retryPolicy');
|
|
31
|
+
maxRetries: 0,
|
|
32
|
+
retryableCodes: ['TRANSPORT'],
|
|
33
|
+
// 退避参数用最小值:0 次重试时它不会被执行,但 dsh 要求 initialDelayMs 为正
|
|
34
|
+
backoff: { initialDelayMs: 1, maxDelayMs: 1, jitterRatio: 0 },
|
|
35
|
+
}, 'kenari.modelRecovery.dshRetryDisabled');
|
|
31
36
|
}
|
|
32
37
|
/**
|
|
33
38
|
* 可取消的等待。返回 false 表示等待期间被取消,调用方应当放弃本次恢复而不是继续重试。
|
package/lib/llm/retry.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"retry.js","sourceRoot":"","sources":["../../src/llm/retry.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"retry.js","sourceRoot":"","sources":["../../src/llm/retry.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AAGzD,sDAAsD;AACtD,MAAM,UAAU,kBAAkB,CAAC,QAAgB,EAAE,UAA6B;IAChF,OAAO,UAAU,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAA;AACtC,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,sBAAsB;IACpC,OAAO,kBAAkB,CAAC;QACxB,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE,CAAC;QACb,cAAc,EAAE,CAAC,WAAW,CAAC;QAC7B,mDAAmD;QACnD,OAAO,EAAE,EAAE,cAAc,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE;KAC9D,EAAE,uCAAuC,CAAC,CAAA;AAC7C,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAAC,EAAU,EAAE,MAAmB;IAChE,IAAI,MAAM,CAAC,OAAO;QAAE,OAAO,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;IACjD,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,IAAI,KAAgD,CAAA;QACpD,MAAM,MAAM,GAAG,CAAC,MAAe,EAAQ,EAAE;YACvC,IAAI,KAAK,KAAK,SAAS;gBAAE,YAAY,CAAC,KAAK,CAAC,CAAA;YAC5C,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;YAC5C,OAAO,CAAC,MAAM,CAAC,CAAA;QACjB,CAAC,CAAA;QACD,MAAM,OAAO,GAAG,GAAS,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;QACzC,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,CAAA;QAC1C,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAA;IAC3D,CAAC,CAAC,CAAA;AACJ,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-kenari-plugin",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Kenari (kenari.id) as a first-class citizen of DeepSeek Harness: session models, web search/fetch provider with fallback, and REST capability tools.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -39,7 +39,9 @@
|
|
|
39
39
|
"test:default-route": "node test/default-route.mjs",
|
|
40
40
|
"test:view": "node test/catalog-view.mjs",
|
|
41
41
|
"test:favicon": "node test/favicon.mjs",
|
|
42
|
-
"test:session-title": "node test/session-title.mjs"
|
|
42
|
+
"test:session-title": "node test/session-title.mjs",
|
|
43
|
+
"test:model-recovery": "node test/model-recovery.mjs",
|
|
44
|
+
"test:retry-repro": "node test/retry-repro.mjs"
|
|
43
45
|
},
|
|
44
46
|
"dsh": {
|
|
45
47
|
"bundle": {
|