@minidoll/agentkit-dsh-plugin 0.1.0 → 0.1.2
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/dist/index.js +18 -54
- package/dist/inject.d.ts +13 -1
- package/dist/inject.js +22 -3
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -170,45 +170,9 @@ function findProjectRoot(candidates, sessionCwd) {
|
|
|
170
170
|
}
|
|
171
171
|
|
|
172
172
|
// src/inject.ts
|
|
173
|
-
import { randomUUID } from "node:crypto";
|
|
174
173
|
import { existsSync as existsSync4, readFileSync as readFileSync3, readdirSync as readdirSync2 } from "node:fs";
|
|
175
174
|
import { join as join4 } from "node:path";
|
|
176
175
|
var MAX_CONTEXT_BYTES = 900;
|
|
177
|
-
var factoryLoader = null;
|
|
178
|
-
async function loadFactory() {
|
|
179
|
-
factoryLoader ??= (async () => {
|
|
180
|
-
try {
|
|
181
|
-
const pkg = "@deepseek-ai/dsh-llm";
|
|
182
|
-
const mod = await import(pkg);
|
|
183
|
-
return typeof mod.createUserMessage === "function" ? mod.createUserMessage : null;
|
|
184
|
-
} catch {
|
|
185
|
-
return null;
|
|
186
|
-
}
|
|
187
|
-
})();
|
|
188
|
-
return factoryLoader;
|
|
189
|
-
}
|
|
190
|
-
function handcraft(text) {
|
|
191
|
-
return {
|
|
192
|
-
role: "user",
|
|
193
|
-
id: randomUUID(),
|
|
194
|
-
content: [{ type: "text", text }],
|
|
195
|
-
source: { kind: "user" }
|
|
196
|
-
};
|
|
197
|
-
}
|
|
198
|
-
function assertUserMessage(value) {
|
|
199
|
-
if (typeof value !== "object" || value === null) return false;
|
|
200
|
-
const m = value;
|
|
201
|
-
if (m.role !== "user") return false;
|
|
202
|
-
if (typeof m.id !== "string" || m.id === "") return false;
|
|
203
|
-
const src = m.source;
|
|
204
|
-
if (src === void 0 || src.kind !== "user") return false;
|
|
205
|
-
const content = m.content;
|
|
206
|
-
if (!Array.isArray(content) || content.length === 0) return false;
|
|
207
|
-
return content.every((b) => {
|
|
208
|
-
const blk = b;
|
|
209
|
-
return blk.type === "text" && typeof blk.text === "string" && blk.text.length > 0;
|
|
210
|
-
});
|
|
211
|
-
}
|
|
212
176
|
var INJECT_CONFIG_REL = ".agent/inject.json";
|
|
213
177
|
function injectEnabled(root) {
|
|
214
178
|
if (process.env.AGENTKIT_CONTEXT_INJECT === "1") return true;
|
|
@@ -286,16 +250,16 @@ function buildInjectText(input) {
|
|
|
286
250
|
\u2026\uFF08\u5DF2\u622A\u65AD\uFF09`;
|
|
287
251
|
return text;
|
|
288
252
|
}
|
|
289
|
-
async function
|
|
253
|
+
async function buildInjectBlock(input) {
|
|
290
254
|
try {
|
|
291
255
|
if (!injectEnabled(input.root)) {
|
|
292
256
|
input.trace?.("inject skipped: disabled (no .agent/inject.json / env)");
|
|
293
|
-
return { ok: false, reason: "inject disabled
|
|
257
|
+
return { ok: false, reason: "inject disabled" };
|
|
294
258
|
}
|
|
295
259
|
const key = `${input.sessionId ?? ""}|${input.filePath ?? ""}`;
|
|
296
260
|
if (injected.has(key)) {
|
|
297
261
|
input.trace?.("inject skipped: already injected for this session+file");
|
|
298
|
-
return { ok: false, reason: "already injected
|
|
262
|
+
return { ok: false, reason: "already injected" };
|
|
299
263
|
}
|
|
300
264
|
const text = buildInjectText({
|
|
301
265
|
root: input.root,
|
|
@@ -304,19 +268,16 @@ async function buildAdditionalContexts(input) {
|
|
|
304
268
|
});
|
|
305
269
|
if (text === null) {
|
|
306
270
|
input.trace?.("inject skipped: nothing to inject for this path");
|
|
307
|
-
return { ok: false, reason: "nothing to inject
|
|
271
|
+
return { ok: false, reason: "nothing to inject" };
|
|
308
272
|
}
|
|
309
|
-
const
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
return { ok: false, reason: "shape check failed" };
|
|
273
|
+
const block = { type: "text", text };
|
|
274
|
+
if (block.type !== "text" || typeof block.text !== "string" || block.text.length === 0) {
|
|
275
|
+
input.trace?.("inject rejected: bad text block");
|
|
276
|
+
return { ok: false, reason: "bad text block" };
|
|
314
277
|
}
|
|
315
278
|
injected.add(key);
|
|
316
|
-
input.trace?.(
|
|
317
|
-
|
|
318
|
-
);
|
|
319
|
-
return { ok: true, messages: [msg] };
|
|
279
|
+
input.trace?.(`inject ok: ${String(text.length)}B as tool-result content block`);
|
|
280
|
+
return { ok: true, block };
|
|
320
281
|
} catch (err) {
|
|
321
282
|
input.trace?.(`inject skipped: ${err.message}`);
|
|
322
283
|
return { ok: false, reason: err.message };
|
|
@@ -507,13 +468,13 @@ function createProjectHooksPlugin(ctx) {
|
|
|
507
468
|
});
|
|
508
469
|
const filePath = inside[0];
|
|
509
470
|
const sessionId = exec.agent?.session?.id ?? null;
|
|
510
|
-
const r = await
|
|
471
|
+
const r = await buildInjectBlock({
|
|
511
472
|
root,
|
|
512
473
|
...filePath !== void 0 ? { filePath } : {},
|
|
513
474
|
...sessionId !== null ? { sessionId } : {},
|
|
514
475
|
trace: (line) => trace(process.env.DSH_HOME ?? join6(homedir(), ".dsh"), line)
|
|
515
476
|
});
|
|
516
|
-
return r.ok
|
|
477
|
+
return r.ok ? r : null;
|
|
517
478
|
} catch (err) {
|
|
518
479
|
trace(
|
|
519
480
|
process.env.DSH_HOME ?? join6(homedir(), ".dsh"),
|
|
@@ -522,12 +483,15 @@ function createProjectHooksPlugin(ctx) {
|
|
|
522
483
|
return null;
|
|
523
484
|
}
|
|
524
485
|
};
|
|
525
|
-
const handle = async (event, exec) => {
|
|
486
|
+
const handle = async (event, exec, result) => {
|
|
526
487
|
try {
|
|
527
488
|
const decision = await runHooks(event, exec);
|
|
528
489
|
if (event === "PostToolUse" && decision.kind === "allow") {
|
|
529
490
|
const injected2 = await tryInjectContext(exec);
|
|
530
|
-
if (injected2 !== null
|
|
491
|
+
if (injected2 !== null && injected2.block !== void 0) {
|
|
492
|
+
const original = Array.isArray(result?.content) ? result.content.filter((b) => typeof b === "object" && b !== null) : [];
|
|
493
|
+
return { kind: "accept", content: [...original, injected2.block] };
|
|
494
|
+
}
|
|
531
495
|
}
|
|
532
496
|
return decision;
|
|
533
497
|
} catch (err) {
|
|
@@ -653,7 +617,7 @@ function createProjectHooksPlugin(ctx) {
|
|
|
653
617
|
return decision;
|
|
654
618
|
};
|
|
655
619
|
ctx.on("tools/pre-execute", (exec) => handle("PreToolUse", exec));
|
|
656
|
-
ctx.on("tools/post-execute", (exec) => handle("PostToolUse", exec));
|
|
620
|
+
ctx.on("tools/post-execute", (exec, result) => handle("PostToolUse", exec, result));
|
|
657
621
|
return state;
|
|
658
622
|
}
|
|
659
623
|
function apply(ctx) {
|
package/dist/inject.d.ts
CHANGED
|
@@ -12,12 +12,24 @@ interface TextBlock {
|
|
|
12
12
|
* 拿不到时**照抄契约**手工构造 —— 形状必须与 `dsh-llm` 的
|
|
13
13
|
* `MessageBase`/`UserMessage`/`TextBlock` 完全一致。
|
|
14
14
|
*/
|
|
15
|
+
/**
|
|
16
|
+
* 注入消息的自定义 producer kind(2026-10-30 真机问题的修复)。
|
|
17
|
+
*
|
|
18
|
+
* `dsh-llm` 的类型注释写得很清楚:
|
|
19
|
+
* "user messages carry **any producer's kind**, and consumers fall through
|
|
20
|
+
* unknown kinds. There is no shared catch-all `plugin` kind."
|
|
21
|
+
*
|
|
22
|
+
* 此前我用 `{kind:'user'}` —— 于是 UI 把它当成**真用户输入**,
|
|
23
|
+
* 渲染进了 Think/推理区,把正常结论挤到折叠区里。
|
|
24
|
+
* 改成自定义 kind 后,消费者按"未知来源"走默认路径,不会误当用户输入。
|
|
25
|
+
*/
|
|
26
|
+
export declare const SOURCE_KIND = "agentkit-context";
|
|
15
27
|
export interface UserMessageLike {
|
|
16
28
|
role: 'user';
|
|
17
29
|
id: string;
|
|
18
30
|
content: TextBlock[];
|
|
19
31
|
source: {
|
|
20
|
-
kind:
|
|
32
|
+
kind: string;
|
|
21
33
|
};
|
|
22
34
|
}
|
|
23
35
|
/**
|
package/dist/inject.js
CHANGED
|
@@ -30,6 +30,25 @@ import { existsSync, readFileSync, readdirSync } from 'node:fs';
|
|
|
30
30
|
import { join } from 'node:path';
|
|
31
31
|
/** 注入内容的硬上限(字节)。超过就截断 —— 注入不是"越多越好"。 */
|
|
32
32
|
export const MAX_CONTEXT_BYTES = 900;
|
|
33
|
+
/**
|
|
34
|
+
* 构造一条合法的 UserMessage。
|
|
35
|
+
*
|
|
36
|
+
* `createUserMessage` 拿得到就用它(id 由官方生成,最保险);
|
|
37
|
+
* 拿不到时**照抄契约**手工构造 —— 形状必须与 `dsh-llm` 的
|
|
38
|
+
* `MessageBase`/`UserMessage`/`TextBlock` 完全一致。
|
|
39
|
+
*/
|
|
40
|
+
/**
|
|
41
|
+
* 注入消息的自定义 producer kind(2026-10-30 真机问题的修复)。
|
|
42
|
+
*
|
|
43
|
+
* `dsh-llm` 的类型注释写得很清楚:
|
|
44
|
+
* "user messages carry **any producer's kind**, and consumers fall through
|
|
45
|
+
* unknown kinds. There is no shared catch-all `plugin` kind."
|
|
46
|
+
*
|
|
47
|
+
* 此前我用 `{kind:'user'}` —— 于是 UI 把它当成**真用户输入**,
|
|
48
|
+
* 渲染进了 Think/推理区,把正常结论挤到折叠区里。
|
|
49
|
+
* 改成自定义 kind 后,消费者按"未知来源"走默认路径,不会误当用户输入。
|
|
50
|
+
*/
|
|
51
|
+
export const SOURCE_KIND = 'agentkit-context';
|
|
33
52
|
/** 尝试加载官方工厂(结果缓存;拿不到就是 null,不重试)。 */
|
|
34
53
|
let factoryLoader = null;
|
|
35
54
|
async function loadFactory() {
|
|
@@ -54,7 +73,7 @@ function handcraft(text) {
|
|
|
54
73
|
role: 'user',
|
|
55
74
|
id: randomUUID(),
|
|
56
75
|
content: [{ type: 'text', text }],
|
|
57
|
-
source: { kind:
|
|
76
|
+
source: { kind: SOURCE_KIND },
|
|
58
77
|
};
|
|
59
78
|
}
|
|
60
79
|
/**
|
|
@@ -71,7 +90,7 @@ export function assertUserMessage(value) {
|
|
|
71
90
|
if (typeof m.id !== 'string' || m.id === '')
|
|
72
91
|
return false;
|
|
73
92
|
const src = m.source;
|
|
74
|
-
if (src === undefined || src.kind !==
|
|
93
|
+
if (src === undefined || src.kind !== SOURCE_KIND)
|
|
75
94
|
return false;
|
|
76
95
|
const content = m.content;
|
|
77
96
|
if (!Array.isArray(content) || content.length === 0)
|
|
@@ -229,7 +248,7 @@ export async function buildAdditionalContexts(input) {
|
|
|
229
248
|
}
|
|
230
249
|
const factory = await loadFactory();
|
|
231
250
|
const msg = factory !== null
|
|
232
|
-
? factory({ content: [{ type: 'text', text }], source: { kind:
|
|
251
|
+
? factory({ content: [{ type: 'text', text }], source: { kind: SOURCE_KIND } })
|
|
233
252
|
: handcraft(text);
|
|
234
253
|
if (!assertUserMessage(msg)) {
|
|
235
254
|
// 形状不合法 ⇒ **不注入**(而不是把坏形状交给宿主,那正是事故)
|