@aipanel/dsh-plugin 1.2.9 → 1.2.11

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.
Files changed (2) hide show
  1. package/dist/index.js +168 -31
  2. package/package.json +6 -4
package/dist/index.js CHANGED
@@ -26,6 +26,17 @@ var EXT_MSG = {
26
26
  };
27
27
  var SEVERITY_ERROR = 1;
28
28
  var SEVERITY_WARN = 2;
29
+ var MUTATING_TOOLS = /* @__PURE__ */ new Set(["write", "edit", "apply_patch"]);
30
+ var OPENCODE_ENV = {
31
+ CONFIG_DIR: "OPENCODE_CONFIG_DIR",
32
+ CONTEXT_API_URL: "OPENCODE_CONTEXT_API_URL",
33
+ VITE_LOGS_API_URL: "OPENCODE_VITE_LOGS_API_URL",
34
+ LOG_FILES_JSON: "OPENCODE_LOG_FILES_JSON",
35
+ VERBOSE: "OPENCODE_VERBOSE",
36
+ ENABLE_LINT: "OPENCODE_ENABLE_LINT",
37
+ VUE_DEVTOOLS_API_URL: "OPENCODE_VUE_DEVTOOLS_API_URL",
38
+ WORKSPACE: "OPENCODE_WORKSPACE"
39
+ };
29
40
 
30
41
  // ../../core/es/common/logger-core.mjs
31
42
  var LogLevel = /* @__PURE__ */ ((LogLevel2) => {
@@ -36,6 +47,28 @@ var LogLevel = /* @__PURE__ */ ((LogLevel2) => {
36
47
  LogLevel2[LogLevel2["NONE"] = 4] = "NONE";
37
48
  return LogLevel2;
38
49
  })(LogLevel || {});
50
+ var LEVEL_NAMES = {
51
+ [
52
+ 0
53
+ /* DEBUG */
54
+ ]: "DEBUG",
55
+ [
56
+ 1
57
+ /* INFO */
58
+ ]: "INFO",
59
+ [
60
+ 2
61
+ /* WARN */
62
+ ]: "WARN",
63
+ [
64
+ 3
65
+ /* ERROR */
66
+ ]: "ERROR",
67
+ [
68
+ 4
69
+ /* NONE */
70
+ ]: "NONE"
71
+ };
39
72
  var globalConfig = {
40
73
  verbose: false,
41
74
  level: 1,
@@ -47,6 +80,14 @@ var globalConfig = {
47
80
  function getConfig() {
48
81
  return globalConfig;
49
82
  }
83
+ function getTimestamp() {
84
+ const now = /* @__PURE__ */ new Date();
85
+ const hours = String(now.getHours()).padStart(2, "0");
86
+ const minutes = String(now.getMinutes()).padStart(2, "0");
87
+ const seconds = String(now.getSeconds()).padStart(2, "0");
88
+ const ms = String(now.getMilliseconds()).padStart(3, "0");
89
+ return `${hours}:${minutes}:${seconds}.${ms}`;
90
+ }
50
91
  function formatValue(value, depth = 0) {
51
92
  if (depth > 3) return "...";
52
93
  if (value === null) return "null";
@@ -119,21 +160,6 @@ var LEVEL_COLORS = {
119
160
  [LogLevel.ERROR]: COLORS.red,
120
161
  [LogLevel.NONE]: COLORS.reset
121
162
  };
122
- var LEVEL_NAMES = {
123
- [LogLevel.DEBUG]: "DEBUG",
124
- [LogLevel.INFO]: "INFO",
125
- [LogLevel.WARN]: "WARN",
126
- [LogLevel.ERROR]: "ERROR",
127
- [LogLevel.NONE]: "NONE"
128
- };
129
- function getTimestamp() {
130
- const now = /* @__PURE__ */ new Date();
131
- const hours = String(now.getHours()).padStart(2, "0");
132
- const minutes = String(now.getMinutes()).padStart(2, "0");
133
- const seconds = String(now.getSeconds()).padStart(2, "0");
134
- const ms = String(now.getMilliseconds()).padStart(3, "0");
135
- return `${hours}:${minutes}:${seconds}.${ms}`;
136
- }
137
163
  function getCallerInfo(depth = 3) {
138
164
  const stack = new Error().stack;
139
165
  if (!stack) return "";
@@ -279,6 +305,19 @@ var JS_EXTENSIONS = /* @__PURE__ */ new Set([
279
305
  function isJsFile(filePath) {
280
306
  return JS_EXTENSIONS.has(path.extname(filePath));
281
307
  }
308
+ var DIAGNOSTICS_TOOL_DESCRIPTION = [
309
+ "\u8FD0\u884C ESLint \u4E0E TypeScript \u7C7B\u578B\u8BCA\u65AD\uFF0C\u8FD4\u56DE\u8BCA\u65AD\u7ED3\u679C\u3002",
310
+ "",
311
+ "**\u652F\u6301\u7684\u6587\u4EF6\u7C7B\u578B**\uFF1A",
312
+ `- ESLint\uFF1AJavaScript / TypeScript / Vue \u6E90\u7801\uFF08${[...JS_EXTENSIONS].map((e) => `*${e}`).join(" ")}\uFF09`,
313
+ "- TypeScript \u7C7B\u578B\u68C0\u67E5\uFF1A*.ts *.tsx *.vue",
314
+ "",
315
+ "**\u4F55\u65F6\u4F7F\u7528\u6B64\u5DE5\u5177**\uFF1A",
316
+ "- \u521A\u5B8C\u6210\u4EE3\u7801\u4FEE\u6539\uFF0C\u60F3\u9A8C\u8BC1\u662F\u5426\u6709 ESLint \u9519\u8BEF\u6216\u7C7B\u578B\u9519\u8BEF",
317
+ "- \u5728\u63D0\u4EA4\u4EE3\u7801\u524D\u8FDB\u884C\u8D28\u91CF\u68C0\u67E5",
318
+ "- \u6392\u67E5\u7F16\u8F91\u5668\u672A\u663E\u793A\u4F46\u5B9E\u9645\u5B58\u5728\u7684\u7C7B\u578B\u95EE\u9898",
319
+ "- \u4E0D\u4F20\u53C2\u6570\u53EF\u5168\u91CF\u8BCA\u65AD\u6574\u4E2A\u9879\u76EE"
320
+ ].join("\n");
282
321
  var ESLintClass;
283
322
  function loadESLint(workspace) {
284
323
  if (ESLintClass) return;
@@ -294,7 +333,12 @@ function loadESLint(workspace) {
294
333
  }
295
334
  async function lintFiles(pattern, cwd, warnLimit = 5) {
296
335
  loadESLint(cwd);
297
- if (!ESLintClass) return {};
336
+ if (!ESLintClass) {
337
+ return {
338
+ text: `[ESLint] \u672A\u8FD0\u884C\uFF1A\u65E0\u6CD5\u5728 workspace "${cwd}" \u89E3\u6790\u5230 eslint\uFF08\u4EC5\u663E\u793A TypeScript \u8BCA\u65AD\uFF09`,
339
+ diagnostics: []
340
+ };
341
+ }
298
342
  try {
299
343
  const eslint = new ESLintClass({ cwd });
300
344
  const results = await eslint.lintFiles(pattern);
@@ -342,7 +386,10 @@ async function lintFiles(pattern, cwd, warnLimit = 5) {
342
386
  return { text: lines.length > 0 ? lines.join("\n") : void 0, diagnostics };
343
387
  } catch (err) {
344
388
  log2.warn("ESLint failed", { pattern, error: err.message });
345
- return {};
389
+ return {
390
+ text: `[ESLint] \u8FD0\u884C\u5931\u8D25\uFF1A${err.message}\uFF08\u4EC5\u663E\u793A TypeScript \u8BCA\u65AD\uFF09`,
391
+ diagnostics: []
392
+ };
346
393
  }
347
394
  }
348
395
  var _vueTscBin;
@@ -506,6 +553,21 @@ async function runProjectDiagnostics(workspace) {
506
553
  return { eslintOutput, tscOutput: mergedTsc };
507
554
  }
508
555
 
556
+ // ../../core/es/common/utils.mjs
557
+ var NODE_MENTION_RE = /@节点\[(n[0-9a-z]+)\]/g;
558
+ function parseNodeMentions(text) {
559
+ const out = [];
560
+ const seen = /* @__PURE__ */ new Set();
561
+ for (const m of text.matchAll(NODE_MENTION_RE)) {
562
+ const id = m[1];
563
+ if (!seen.has(id)) {
564
+ seen.add(id);
565
+ out.push(id);
566
+ }
567
+ }
568
+ return out;
569
+ }
570
+
509
571
  // dsh-plugin/src/events-relay.ts
510
572
  var log3 = createNodeLogger("DshEventRelay");
511
573
  var FLUSH_DELAY_MS = 120;
@@ -532,6 +594,8 @@ function setupEventRelay(ctx, config) {
532
594
  const lastSent = /* @__PURE__ */ new Map();
533
595
  const lastTitles = /* @__PURE__ */ new Map();
534
596
  const dirty = /* @__PURE__ */ new Set();
597
+ const childOf = /* @__PURE__ */ new Map();
598
+ const subagentRunningByParent = /* @__PURE__ */ new Map();
535
599
  let flushTimer = null;
536
600
  let lastPostErrorAt = 0;
537
601
  const scheduleFlush = () => {
@@ -549,15 +613,48 @@ function setupEventRelay(ctx, config) {
549
613
  const ensureState = (sessionId) => {
550
614
  let s = states.get(sessionId);
551
615
  if (s === void 0) {
552
- s = { running: false, thinking: false };
616
+ s = { running: false, thinking: false, pending: false, subagentRunning: 0 };
553
617
  states.set(sessionId, s);
554
618
  }
555
619
  return s;
556
620
  };
621
+ const updateSubagentCount = (parentId, childId, running) => {
622
+ let set = subagentRunningByParent.get(parentId);
623
+ if (running) {
624
+ if (set === void 0) {
625
+ set = /* @__PURE__ */ new Set();
626
+ subagentRunningByParent.set(parentId, set);
627
+ }
628
+ set.add(childId);
629
+ } else if (set !== void 0) {
630
+ set.delete(childId);
631
+ }
632
+ const count = set === void 0 || set.size === 0 ? 0 : set.size;
633
+ if (set !== void 0 && set.size === 0) subagentRunningByParent.delete(parentId);
634
+ const s = ensureState(parentId);
635
+ if (s.subagentRunning !== count) {
636
+ s.subagentRunning = count;
637
+ markDirty(parentId);
638
+ }
639
+ };
640
+ const setPending = (sessionId, pending, kind) => {
641
+ const s = ensureState(sessionId);
642
+ if (s.pending === pending && s.pendingKind === kind) return;
643
+ s.pending = pending;
644
+ s.pendingKind = pending ? kind : void 0;
645
+ markDirty(sessionId);
646
+ };
557
647
  const handleAgentStatus = ({ agent, status }) => {
558
648
  const sessionId = String(agent.session.id);
559
649
  if (!sessionId) return;
560
650
  const running = status === "running";
651
+ const header = agent.session.header;
652
+ if (header?.origin === "subagent" && header.parentSession !== void 0) {
653
+ const parentId = String(header.parentSession);
654
+ childOf.set(sessionId, parentId);
655
+ updateSubagentCount(parentId, sessionId, running);
656
+ return;
657
+ }
561
658
  const s = ensureState(sessionId);
562
659
  if (s.running === running) return;
563
660
  s.running = running;
@@ -610,8 +707,25 @@ function setupEventRelay(ctx, config) {
610
707
  if (last === void 0 || last.thinking !== s.thinking) {
611
708
  events.push({ type: "thinking", sessionId, thinking: s.thinking });
612
709
  }
710
+ if (last === void 0 || last.pending !== s.pending || last.pendingKind !== s.pendingKind) {
711
+ events.push({
712
+ type: "session.pending",
713
+ sessionId,
714
+ pending: s.pending,
715
+ ...s.pendingKind === void 0 ? {} : { kind: s.pendingKind }
716
+ });
717
+ }
718
+ if (last === void 0 || last.subagentRunning !== s.subagentRunning) {
719
+ events.push({ type: "session.subagents", sessionId, running: s.subagentRunning });
720
+ }
613
721
  if (events.length === 0) continue;
614
- lastSent.set(sessionId, { running: s.running, thinking: s.thinking });
722
+ lastSent.set(sessionId, {
723
+ running: s.running,
724
+ thinking: s.thinking,
725
+ pending: s.pending,
726
+ pendingKind: s.pendingKind,
727
+ subagentRunning: s.subagentRunning
728
+ });
615
729
  for (const event of events) post(event);
616
730
  }
617
731
  };
@@ -637,22 +751,45 @@ function setupEventRelay(ctx, config) {
637
751
  }
638
752
  });
639
753
  };
640
- ctx.on("agent/status", handleAgentStatus);
754
+ ctx.on("agent/status", handleAgentStatus, { global: true });
641
755
  ctx.on("session/event", handleSessionEvent, { global: true });
756
+ ctx.on(
757
+ "approval/request",
758
+ async (request, next) => {
759
+ const sessionId = String(request.agent?.session?.id ?? "");
760
+ if (!sessionId) return next();
761
+ setPending(sessionId, true, "approval");
762
+ try {
763
+ return await next();
764
+ } finally {
765
+ setPending(sessionId, false);
766
+ }
767
+ },
768
+ // scoped(Scoped<Agent>)事件:root ctx 需 global 才能收到所有 agent
769
+ { global: true }
770
+ );
771
+ ctx.on(
772
+ "user-questions/request",
773
+ async (request, next) => {
774
+ const sessionId = String(request.agent?.session?.id ?? "");
775
+ if (!sessionId) return next();
776
+ const isPlanReview = request.questions?.some((q) => q.intent?.kind === "plan-review");
777
+ setPending(sessionId, true, isPlanReview ? "plan-review" : "question");
778
+ try {
779
+ return await next();
780
+ } finally {
781
+ setPending(sessionId, false);
782
+ }
783
+ },
784
+ // scoped(Scoped<Agent>)事件:root ctx 需 global 才能收到所有 agent
785
+ { global: true }
786
+ );
642
787
  }
643
788
 
644
789
  // dsh-plugin/src/index.ts
645
790
  var name = "aipanel";
646
791
  var inject = ["tools"];
647
792
  var log4 = createNodeLogger("DshPlugin");
648
- var MUTATING_TOOLS = /* @__PURE__ */ new Set(["write", "edit", "apply_patch"]);
649
- function collectNodeIds(text) {
650
- const ids = [];
651
- const re = new RegExp("@\u8282\u70B9\\[(n[0-9a-z]+)\\]", "g");
652
- let m;
653
- while ((m = re.exec(text)) !== null) ids.push(m[1]);
654
- return ids;
655
- }
656
793
  function buildNodeContext(e) {
657
794
  const lines = [`\u8282\u70B9 ID\uFF1A${e.id ?? ""}`];
658
795
  const loc = e.line ? e.column ? `:${e.line}:${e.column}` : `:${e.line}` : "";
@@ -757,14 +894,14 @@ function applyProviderSettings(ctx, config) {
757
894
  function apply(ctx, config = {}) {
758
895
  const cwd = config.cwd ?? process.cwd();
759
896
  const enableDiagnostics = config.enableDiagnostics ?? false;
760
- const autoDiagnose = config.autoDiagnose ?? process.env.OPENCODE_ENABLE_LINT === "1";
897
+ const autoDiagnose = config.autoDiagnose ?? process.env[OPENCODE_ENV.ENABLE_LINT] === "1";
761
898
  const vitePort = config.vitePort ?? 0;
762
899
  const contextApiPath = config.contextApiPath ?? CONTEXT_API_PATH;
763
900
  const tools = ctx.tools;
764
901
  if (enableDiagnostics) {
765
902
  const diagnosticsTool = {
766
903
  name: "run_diagnostics",
767
- description: "\u8FD0\u884C ESLint \u548C vue-tsc \u7C7B\u578B\u68C0\u67E5\uFF0C\u8FD4\u56DE\u8BCA\u65AD\u7ED3\u679C\u3002\n\n**\u4F55\u65F6\u4F7F\u7528\u6B64\u5DE5\u5177**\uFF1A\n- \u521A\u5B8C\u6210\u4EE3\u7801\u4FEE\u6539\uFF0C\u60F3\u9A8C\u8BC1\u662F\u5426\u6709 ESLint \u9519\u8BEF\u6216\u7C7B\u578B\u9519\u8BEF\n- \u5728\u63D0\u4EA4\u4EE3\u7801\u524D\u8FDB\u884C\u8D28\u91CF\u68C0\u67E5\n- \u6392\u67E5\u7F16\u8F91\u5668\u672A\u663E\u793A\u4F46\u5B9E\u9645\u5B58\u5728\u7684\u7C7B\u578B\u95EE\u9898\n- \u4E0D\u4F20\u53C2\u6570\u53EF\u5168\u91CF\u8BCA\u65AD\u6574\u4E2A\u9879\u76EE\n\n**\u8BCA\u65AD\u5185\u5BB9**\uFF1A\n- ESLint \u89C4\u5219\u68C0\u67E5\uFF08error \u548C warning\uFF09\n- vue-tsc \u7C7B\u578B\u68C0\u67E5\uFF08TypeScript \u7C7B\u578B\u9519\u8BEF\u548C\u8B66\u544A\uFF09",
904
+ description: DIAGNOSTICS_TOOL_DESCRIPTION,
768
905
  parameters: {
769
906
  type: "object",
770
907
  additionalProperties: false,
@@ -885,7 +1022,7 @@ ${diagText}` }]
885
1022
  if (message.source.kind !== "user") continue;
886
1023
  for (const block of message.content) {
887
1024
  if (block.type !== "text") continue;
888
- for (const id of collectNodeIds(block.text)) ids.add(id);
1025
+ for (const id of parseNodeMentions(block.text)) ids.add(id);
889
1026
  }
890
1027
  }
891
1028
  if (ids.size === 0) return decision;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aipanel/dsh-plugin",
3
- "version": "1.2.9",
3
+ "version": "1.2.11",
4
4
  "type": "module",
5
5
  "description": "AIPanel for DeepSeek Harness (dsh):注入审查工具 run_diagnostics、编辑后自动诊断。",
6
6
  "main": "./dist/index.js",
@@ -16,14 +16,16 @@
16
16
  },
17
17
  "devDependencies": {
18
18
  "@deepseek-ai/cordis": "^4.0.2",
19
- "@deepseek-ai/dsh-settings": "^0.1.2-rc.1",
20
19
  "@deepseek-ai/dsh-agent": "^0.1.2-rc.1",
21
20
  "@deepseek-ai/dsh-llm": "^0.1.2-rc.1",
21
+ "@deepseek-ai/dsh-session": "^0.1.2-rc.1",
22
+ "@deepseek-ai/dsh-settings": "^0.1.2-rc.1",
22
23
  "@deepseek-ai/dsh-tools": "^0.1.2-rc.1",
24
+ "@deepseek-ai/dsh-user-approval": "^0.1.2-rc.1",
25
+ "@deepseek-ai/dsh-user-questions": "^0.1.2-rc.1",
23
26
  "@deepseek-ai/dsh-util-values": "^0.1.2-rc.1",
24
- "@deepseek-ai/dsh-session": "^0.1.2-rc.1",
25
27
  "esbuild": "^0.25.0",
26
- "@aipanel/core": "1.2.9"
28
+ "@aipanel/core": "1.2.11"
27
29
  },
28
30
  "scripts": {
29
31
  "build": "esbuild src/index.ts --bundle --outfile=dist/index.js --platform=node --format=esm --target=node18 --external:@deepseek-ai/* --external:node:* --external:vue-tsc",