misakanet 2.42.1 → 2.42.3

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "misakanet",
3
- "version": "2.42.1",
3
+ "version": "2.42.3",
4
4
  "description": "Failure-memory network for AI coding agents: search evidence-rated failure lessons, submit new failures, and turn crashes into reusable knowledge.",
5
5
  "author": {
6
6
  "name": "Ikalus1988",
package/README.ja.md CHANGED
@@ -335,10 +335,6 @@ jobs:
335
335
 
336
336
  → [入力と出力](docs/agents/external-usage.md) · [`actions: read` が省略できない理由](docs/agents/external-usage.md)
337
337
 
338
- ### 8秒で見る
339
-
340
- ![Search lesson demo](promotional/search%20lesson.gif)
341
-
342
338
  ## ドキュメント
343
339
 
344
340
  **ジャーニーを選んでください** — MisakaNet の使い方は、やりたいことに応じて異なります:
package/README.md CHANGED
@@ -346,10 +346,6 @@ jobs:
346
346
 
347
347
  → [inputs and outputs](docs/agents/external-usage.md) · [why `actions: read` is not optional](docs/agents/external-usage.md)
348
348
 
349
- ### See it in 8 seconds
350
-
351
- ![Search lesson demo](promotional/search%20lesson.gif)
352
-
353
349
  ## Documentation
354
350
 
355
351
  **Choose your journey** — MisakaNet is useful in different ways depending on what you are trying to do:
package/index.js CHANGED
@@ -109,6 +109,19 @@ export async function apply(ctx, config = {}) {
109
109
  if (options.failOnStartupError) throw error;
110
110
  // Expected on npm skill-only installs: the skill (SKILL.md) is the payload,
111
111
  // and a missing client must not take the whole profile down with it.
112
+ //
113
+ // Returning quietly is right for **boot** and wrong for **diagnosis**. `lib/client.js`
114
+ // mounts the browser half either way, so the observable result is a rendered MisakaNet
115
+ // panel whose tools never reach the agent — intake #2759 reported exactly that ("the
116
+ // sidebar is decorative", zero requests to misakanet.org) with nothing in the host log
117
+ // to separate "this profile never installed the client" from "this profile is broken".
118
+ // The other two guarded paths already log; this one was the only silent branch, and it is
119
+ // the branch that runs most often. Measured 2026-10-08 with the client unresolvable:
120
+ // `apply()` returned and `logger.warn` was called 0 times.
121
+ ctx?.logger?.warn?.(
122
+ 'misakanet: @deepseek-ai/dsh-mcp-client is not resolvable, so the MCP tools are not '
123
+ + `mounted (the skill still works): ${error?.message ?? error}`,
124
+ );
112
125
  return;
113
126
  }
114
127
 
package/lib/client.js CHANGED
@@ -41,10 +41,13 @@
41
41
  *
42
42
  * WHAT IT DELIBERATELY DOES NOT DO
43
43
  * --------------------------------
44
- * No lesson links (the site's directory slugs are truncated, so a URL cannot be built from an id), no
45
- * gap-report button (that belongs with the intake side, not with a satisfaction vote), and no
44
+ * No gap-report button (that belongs with the intake side, not with a satisfaction vote), and no
46
45
  * credentials — the endpoints are public, anonymous and rate-limited. And no auto-voting, ever.
47
46
  *
47
+ * Lesson links, by contrast, ARE built, in two places. A lesson's `id` is the slug its page is
48
+ * published under, so `/lessons/<id>` is a real address rather than a guess — and both
49
+ * call sites build it the same way (`MisakanetOverlayRow`, `MisakanetShellToast`).
50
+ *
48
51
  * KNOWN LIMITS (v1, stated rather than hidden)
49
52
  * --------------------------------------------
50
53
  * The verdict learns that a search happened from this module's own per-session memory, not from a host
@@ -191,7 +194,7 @@ window.__ModuleLoader__.load({
191
194
  "panel.trust.checking": "checking…",
192
195
  "panel.trust.unavailable": "unavailable",
193
196
  "panel.trust.human": "human confirmation",
194
- "panel.trust.none": "no level",
197
+ "panel.trust.noLevel": "no level",
195
198
  "panel.trust.used": " · used ",
196
199
  "panel.trust.footnote": "The level is the lesson's own stored evidence. The count is other people's confirmations, not yours: one makes the reuse event appear, the second is what agents read as E4.",
197
200
  "panel.activity.session": "this session: ",
@@ -300,7 +303,7 @@ window.__ModuleLoader__.load({
300
303
  "panel.trust.checking": "核验中……",
301
304
  "panel.trust.unavailable": "不可用",
302
305
  "panel.trust.human": "人工确认",
303
- "panel.trust.none": "无等级",
306
+ "panel.trust.noLevel": "无等级",
304
307
  "panel.trust.used": " · 已复用 ",
305
308
  "panel.trust.footnote": "等级来自课程自己存的证据;计数是别人的确认,不是你的:前者让复用事件出现,后者才是 agent 读到的 E4。",
306
309
  "panel.activity.session": "本会话:",
@@ -441,18 +444,30 @@ window.__ModuleLoader__.load({
441
444
  if (sessionId) lastSessionId = sessionId;
442
445
  }
443
446
 
447
+ /**
448
+ * One shape for a session record, because two places can create one.
449
+ *
450
+ * `overlayOf()` creates a record too, and it used to build a smaller literal. Whoever created
451
+ * the record first won, and the overlay always renders first — so the record that
452
+ * `sessionLog()` found already there had no `sessionId`, and nothing ever added it. That is
453
+ * not a warning, it is a dead end: every recorder ends in `notify(log.sessionId)`, which
454
+ * returns immediately on a null id, so the panel and the toasts stopped repainting while the
455
+ * numbers underneath them kept changing.
456
+ */
457
+ function newSessionLog(sessionId) {
458
+ return {
459
+ sessionId: sessionId,
460
+ searches: [], intakes: [], votes: [],
461
+ lessons: Object.create(null),
462
+ // The verdict's own state: which message it was offered on, and whether it is spent.
463
+ shownFor: "", voted: false,
464
+ };
465
+ }
466
+
444
467
  function sessionLog(sessionId) {
445
468
  if (!sessionId) return null;
446
469
  touchSession(sessionId);
447
- if (!sessions[sessionId]) {
448
- sessions[sessionId] = {
449
- sessionId: sessionId,
450
- searches: [], intakes: [], votes: [],
451
- lessons: Object.create(null),
452
- // The verdict's own state: which message it was offered on, and whether it is spent.
453
- shownFor: "", voted: false,
454
- };
455
- }
470
+ if (!sessions[sessionId]) sessions[sessionId] = newSessionLog(sessionId);
456
471
  return sessions[sessionId];
457
472
  }
458
473
 
@@ -521,6 +536,12 @@ window.__ModuleLoader__.load({
521
536
  */
522
537
  function playCue(cue) {
523
538
  if (!cue || !voiceEnabled()) return false;
539
+ // The cue is the one string in this file that comes straight out of a response body and
540
+ // lands in a URL. Every cue the server names is `[a-z0-9-]` (`connect-success`,
541
+ // `pair-success`, `lesson-found`, `failure-warning`), so anything outside that shape is not a
542
+ // cue we ship: refuse it instead of letting it walk out of the path. Same-origin either
543
+ // way, so this is hardening and not a fix for a live defect.
544
+ if (!/^[a-z0-9-]+$/.test(String(cue))) return false;
524
545
  try {
525
546
  var audio = new window.Audio(API + "/assets/voice/" + cue + ".mp3");
526
547
  var played = audio.play();
@@ -575,9 +596,11 @@ var DEFAULT_MCP_URL = "https://misakanet.org/mcp";
575
596
 
576
597
  function overlayOf(sessionId) {
577
598
  var log = sessions[sessionId];
578
- if (!log) {
579
- log = sessions[sessionId] = { searches: [], intakes: [], votes: [], lessons: {} };
580
- }
599
+ // The overlay renders before any tool row does, so this is the record a session really
600
+ // starts with — same constructor as `sessionLog()`, and deliberately no
601
+ // `touchSession()`: merely mounting the card must not claim the frame-wide seats'
602
+ // "last session".
603
+ if (!log) log = sessions[sessionId] = newSessionLog(sessionId);
581
604
  if (!log.overlay) {
582
605
  log.overlay = { open: false, query: "", status: "idle", results: [], error: null };
583
606
  }
@@ -610,7 +633,16 @@ var DEFAULT_MCP_URL = "https://misakanet.org/mcp";
610
633
  }
611
634
  setOverlay(sessionId, { open: true, query: query, status: "loading", results: [], error: null });
612
635
  var timedOut = false;
613
- var timer = setTimeout(function () { timedOut = true; }, OVERLAY_TIMEOUT_MS);
636
+ var timer = setTimeout(function () {
637
+ timedOut = true;
638
+ // The flag below silences a late answer, and that was the whole effect: the card sat on
639
+ // "Searching…" for good, with no error and nothing to retry, so
640
+ // `OVERLAY_TIMEOUT_MS` was inert. Someone is looking at this card, so a stalled request
641
+ // has to land in the same state a failed one does — `setOverlay` draws the
642
+ // reason through `overlay.failed`, which already knows how to say it.
643
+ setOverlay(sessionId, { status: "error", results: [],
644
+ error: "no answer after " + Math.round(OVERLAY_TIMEOUT_MS / 1000) + "s" });
645
+ }, OVERLAY_TIMEOUT_MS);
614
646
  var url = SEARCH_ENDPOINT + "?q=" + encodeURIComponent(query) + "&limit=" + OVERLAY_LIMIT;
615
647
  fetch(url, { headers: { accept: "application/json" } })
616
648
  .then(function (response) {
@@ -734,8 +766,8 @@ var DEFAULT_MCP_URL = "https://misakanet.org/mcp";
734
766
  log.lessons[entry.lessonId].count += 1;
735
767
  log.lessons[entry.lessonId].last = entry.at;
736
768
  }
737
- return entry;
738
769
  notifyShell();
770
+ return entry;
739
771
  }
740
772
 
741
773
  /** Record a vote and retire the sighting it answered. */
@@ -766,7 +798,12 @@ var DEFAULT_MCP_URL = "https://misakanet.org/mcp";
766
798
  function intakeState(payload) {
767
799
  if (!payload || typeof payload !== "object") return "unknown";
768
800
  if (payload.converted) return "converted";
769
- if (payload.answered) return T("intake.answered");
801
+ // An ASCII state id, like every other state here. It used to return the localized
802
+ // `T("intake.answered")` instead, which the panel then compared against the literal
803
+ // `"answered"`: on a Chinese interface an answered report never matched, so it stayed in
804
+ // the pending count with the "we will re-check this" line attached. The copy is the
805
+ // labels' job; see `MisakanetIntakeRow` and the panel.
806
+ if (payload.answered) return "answered";
770
807
  if (payload.already_have) return "already_have";
771
808
  if (payload.pending || payload.duplicate || payload.submitted) return "pending";
772
809
  return "unknown";
@@ -805,8 +842,8 @@ var DEFAULT_MCP_URL = "https://misakanet.org/mcp";
805
842
  bumpStat("reportsFiled");
806
843
  if (entry.state === "converted") bumpStat("reportsConverted");
807
844
  notify(log.sessionId);
808
- return entry;
809
845
  notifyShell();
846
+ return entry;
810
847
  }
811
848
 
812
849
  /** Concatenate the text blocks of a result node; "" when there are none. */
@@ -1071,7 +1108,7 @@ var DEFAULT_MCP_URL = "https://misakanet.org/mcp";
1071
1108
  var labels = {
1072
1109
  filing: "filing…",
1073
1110
  pending: "filed — waiting for a maintainer",
1074
- answered: "answered",
1111
+ answered: T("intake.answered"),
1075
1112
  already_have: "not filed — the corpus already covers this",
1076
1113
  converted: "your report is cited by a lesson",
1077
1114
  unknown: T("intake.submitted"),
@@ -1220,7 +1257,7 @@ var DEFAULT_MCP_URL = "https://misakanet.org/mcp";
1220
1257
  } else {
1221
1258
  rows.push(h("div", { key: "s2r" }, intakes.slice().reverse().map(function (entry) {
1222
1259
  var labels = {
1223
- filing: "filing", pending: T("intake.waiting"), answered: "answered",
1260
+ filing: "filing", pending: T("intake.waiting"), answered: T("intake.answered"),
1224
1261
  already_have: T("intake.alreadyHave"), converted: T("intake.cited"), unknown: "submitted",
1225
1262
  };
1226
1263
  var issue = String(entry.issue || "").replace(/^issue-/, "#").replace(/^.*\/issues\//, "#");
@@ -1250,7 +1287,7 @@ var DEFAULT_MCP_URL = "https://misakanet.org/mcp";
1250
1287
  : count(confirmations, T("panel.trust.human")) + (confirmations >= 2 ? " → E4" : ""));
1251
1288
  return h("div", { key: "l" + lesson.lessonId, style: { borderTop: "1px solid " + MUTED, padding: "4px 0" } },
1252
1289
  h("span", {}, lesson.title || lesson.lessonId),
1253
- h("span", { style: DIM }, " " + (lesson.evidence || T("panel.trust.none")) + " · " + counted +
1290
+ h("span", { style: DIM }, " " + (lesson.evidence || T("panel.trust.noLevel")) + " · " + counted +
1254
1291
  T("panel.trust.used") + lesson.count + "× · " + timeRange(lesson)));
1255
1292
  })));
1256
1293
  }
@@ -1325,7 +1362,9 @@ var DEFAULT_MCP_URL = "https://misakanet.org/mcp";
1325
1362
  /** One result row: the title, where it came from, and a link to read it. */
1326
1363
  function MisakanetOverlayRow(result) {
1327
1364
  var path = String(result && result.id ? result.id : "");
1328
- var href = path ? "https://misakanet.org/lessons/" + path : "https://misakanet.org/";
1365
+ // Encoded, like the shell toast at the other call site: a no-op for the `[a-z0-9-]` slugs
1366
+ // the server hands out, and a correct URL for anything else.
1367
+ var href = path ? "https://misakanet.org/lessons/" + encodeURIComponent(path) : "https://misakanet.org/";
1329
1368
  return h("a", {
1330
1369
  href: href, target: "_blank", rel: "noreferrer",
1331
1370
  style: { display: "block", padding: "7px 8px", borderRadius: "8px", textDecoration: "none",
@@ -1469,7 +1508,10 @@ var DEFAULT_MCP_URL = "https://misakanet.org/mcp";
1469
1508
  var searched = log.searches ? log.searches.length : 0;
1470
1509
  var withLesson = log.searches ? log.searches.filter(function (s) { return s.lessonId; }).length : 0;
1471
1510
  var ids = log.searches ? log.searches.map(function (s) { return s.lessonId; }).filter(Boolean) : [];
1472
- var helpful = log.votes ? log.votes.filter(function (v) { return v && v.helpful; }).length : 0;
1511
+ // `recordVote` stores the verdict under `kind`; filtering a `helpful` field nothing writes
1512
+ // made this line print 0 for every session, next to the panel's own count of the same
1513
+ // votes. Same expression the panel uses.
1514
+ var helpful = log.votes ? log.votes.filter(function (v) { return v && v.kind === "helpful"; }).length : 0;
1473
1515
  var overlay = log.overlay || {};
1474
1516
  var lines = [
1475
1517
  "MisakaNet — this session",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "misakanet",
3
- "version": "2.42.1",
3
+ "version": "2.42.3",
4
4
  "license": "Apache-2.0",
5
5
  "description": "MisakaNet failure-memory skill — evidence-rated failure lessons for AI agents. DSH bundle: registers the MCP tools as mcp__misakanet__* (misakanet_search/get_lesson/…), served by the public Streamable HTTP endpoint https://misakanet.org/mcp — live tools from an npm install, in any profile, with no local python. A profile that prefers the repo's own stdio server can override the bundle row.",
6
6
  "icon": "icon.svg",
@@ -83,7 +83,7 @@
83
83
  "wrangler": "^4.146.0"
84
84
  },
85
85
  "overrides": {
86
- "sharp": "0.35.4"
86
+ "sharp": "0.35.5"
87
87
  },
88
88
  "main": "index.js",
89
89
  "exports": {