@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 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 buildAdditionalContexts(input) {
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 (no .agent/inject.json / env)" };
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 for this session+file" };
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 for this path" };
271
+ return { ok: false, reason: "nothing to inject" };
308
272
  }
309
- const factory = await loadFactory();
310
- const msg = factory !== null ? factory({ content: [{ type: "text", text }], source: { kind: "user" } }) : handcraft(text);
311
- if (!assertUserMessage(msg)) {
312
- input.trace?.(`inject rejected: shape check failed (${factory !== null ? "factory" : "handcraft"})`);
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
- `inject ok: ${String(text.length)}B via ${factory !== null ? "factory" : "handcraft"}`
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 buildAdditionalContexts({
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 && r.messages !== void 0 ? r.messages : null;
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) return { kind: "accept", additionalContexts: injected2 };
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: 'user';
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: 'user' },
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 !== 'user')
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: 'user' } })
251
+ ? factory({ content: [{ type: 'text', text }], source: { kind: SOURCE_KIND } })
233
252
  : handcraft(text);
234
253
  if (!assertUserMessage(msg)) {
235
254
  // 形状不合法 ⇒ **不注入**(而不是把坏形状交给宿主,那正是事故)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@minidoll/agentkit-dsh-plugin",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "DeepSeek Harness 插件:项目自包含的 lifecycle hooks(配置随仓库走,per-session 发现)",
5
5
  "license": "MIT",
6
6
  "type": "module",