pi-harness-runtime 1.1.415 → 1.1.417

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 (134) hide show
  1. package/package.json +1 -1
  2. package/packages/a2a-adapter/package.json +1 -1
  3. package/packages/agent-policy/package.json +1 -1
  4. package/packages/architecture-generator/package.json +1 -1
  5. package/packages/auth/package.json +1 -1
  6. package/packages/autonomous-refactor/package.json +1 -1
  7. package/packages/autonomous-runtime/package.json +1 -1
  8. package/packages/cache-strategy/package.json +1 -1
  9. package/packages/capability-registry/package.json +1 -1
  10. package/packages/checkpoint/package.json +1 -1
  11. package/packages/cli-plugin-sdk/package.json +1 -1
  12. package/packages/clipboard/package.json +1 -1
  13. package/packages/clipboard-plugin/package.json +1 -1
  14. package/packages/code-generation/package.json +1 -1
  15. package/packages/code-review/package.json +1 -1
  16. package/packages/codex-adapter/package.json +1 -1
  17. package/packages/codex-watcher/package.json +1 -1
  18. package/packages/config-capture/package.json +1 -1
  19. package/packages/connection-runtime/package.json +1 -1
  20. package/packages/context-compiler/package.json +1 -1
  21. package/packages/context-discovery/package.json +1 -1
  22. package/packages/context-manager/package.json +1 -1
  23. package/packages/contracts/package.json +1 -1
  24. package/packages/cookie-sanitizer/package.json +1 -1
  25. package/packages/cost-optimizer/package.json +1 -1
  26. package/packages/dependency-analyzer/package.json +1 -1
  27. package/packages/django-plugin/package.json +1 -1
  28. package/packages/doc-generator/package.json +1 -1
  29. package/packages/durable-review/package.json +1 -1
  30. package/packages/e2e-tester/package.json +1 -1
  31. package/packages/evaluation-engine/package.json +1 -1
  32. package/packages/evaluation-runner/package.json +1 -1
  33. package/packages/event-bus/package.json +1 -1
  34. package/packages/event-sink/package.json +1 -1
  35. package/packages/event-store/package.json +1 -1
  36. package/packages/experience-replay/package.json +1 -1
  37. package/packages/feedback-collector/package.json +1 -1
  38. package/packages/file-copy-helper/package.json +1 -1
  39. package/packages/framework-detector/package.json +1 -1
  40. package/packages/framework-plugin-sdk/package.json +1 -1
  41. package/packages/frappe-plugin/package.json +1 -1
  42. package/packages/generic-web-plugin/package.json +1 -1
  43. package/packages/health-monitor/package.json +1 -1
  44. package/packages/intent-analyzer/package.json +1 -1
  45. package/packages/jev-judge/package.json +1 -1
  46. package/packages/knowledge-graph/package.json +1 -1
  47. package/packages/knowledge-retrieval/package.json +1 -1
  48. package/packages/laravel-plugin/package.json +1 -1
  49. package/packages/learning-engine/package.json +1 -1
  50. package/packages/logging/package.json +1 -1
  51. package/packages/mcp-adapter/package.json +1 -1
  52. package/packages/memory-engine/package.json +1 -1
  53. package/packages/milestone-manager/package.json +1 -1
  54. package/packages/model-registry/package.json +1 -1
  55. package/packages/nextjs-plugin/package.json +1 -1
  56. package/packages/notification/package.json +1 -1
  57. package/packages/observability/package.json +1 -1
  58. package/packages/okf-indexer/package.json +1 -1
  59. package/packages/paperclip-integration/package.json +1 -1
  60. package/packages/performance-optimizer/package.json +1 -1
  61. package/packages/permission-mode/package.json +1 -1
  62. package/packages/privilege-broker/package.json +1 -1
  63. package/packages/project-analyzer/package.json +1 -1
  64. package/packages/project-bootstrap/package.json +1 -1
  65. package/packages/projection-engine/package.json +1 -1
  66. package/packages/prompt-compiler/package.json +1 -1
  67. package/packages/prompt-versioning/package.json +1 -1
  68. package/packages/provider-adapter-sdk/package.json +1 -1
  69. package/packages/provider-router/package.json +1 -1
  70. package/packages/provider-selector/package.json +1 -1
  71. package/packages/providers/package.json +1 -1
  72. package/packages/qdrant-skills/package.json +1 -1
  73. package/packages/quota-manager/package.json +1 -1
  74. package/packages/rate-limiter/package.json +1 -1
  75. package/packages/react-vite-plugin/package.json +1 -1
  76. package/packages/relay/package.json +1 -1
  77. package/packages/release-manager/package.json +1 -1
  78. package/packages/requirement-compiler/package.json +1 -1
  79. package/packages/runtime/package.json +1 -1
  80. package/packages/scheduler/package.json +1 -1
  81. package/packages/scheduler-adapter/package.json +1 -1
  82. package/packages/session/package.json +1 -1
  83. package/packages/session-api/package.json +1 -1
  84. package/packages/session-export/package.json +1 -1
  85. package/packages/shared-context/package.json +1 -1
  86. package/packages/skill-mcp-client/package.json +1 -1
  87. package/packages/skill-registry/package.json +1 -1
  88. package/packages/skills/dist/loader.d.ts +2 -1
  89. package/packages/skills/dist/loader.d.ts.map +1 -1
  90. package/packages/skills/dist/loader.js +4 -3
  91. package/packages/skills/dist/loader.js.map +1 -1
  92. package/packages/skills/dist/sanitize.d.ts +5 -0
  93. package/packages/skills/dist/sanitize.d.ts.map +1 -1
  94. package/packages/skills/dist/sanitize.js +37 -12
  95. package/packages/skills/dist/sanitize.js.map +1 -1
  96. package/packages/skills/dist/skill-commands.d.ts.map +1 -1
  97. package/packages/skills/dist/skill-commands.js +2 -1
  98. package/packages/skills/dist/skill-commands.js.map +1 -1
  99. package/packages/skills/dist/vector-schema.d.ts +28 -0
  100. package/packages/skills/dist/vector-schema.d.ts.map +1 -1
  101. package/packages/skills/dist/vector-schema.js +28 -0
  102. package/packages/skills/dist/vector-schema.js.map +1 -1
  103. package/packages/skills/package.json +1 -1
  104. package/packages/skills/src/loader.ts +4 -2
  105. package/packages/skills/src/sanitize.ts +37 -12
  106. package/packages/skills/src/skill-commands.ts +2 -1
  107. package/packages/skills/src/vector-schema.ts +30 -0
  108. package/packages/skills/test/sanitize.test.ts +43 -0
  109. package/packages/skills/test/vector-schema.test.ts +15 -2
  110. package/packages/skills/test/vector-threshold.test.ts +108 -0
  111. package/packages/sprint-planner/package.json +1 -1
  112. package/packages/subscription-engine/package.json +1 -1
  113. package/packages/task-compiler/package.json +1 -1
  114. package/packages/telegram/package.json +1 -1
  115. package/packages/tencentdb-memory/package.json +1 -1
  116. package/packages/tencentdb-sync/package.json +1 -1
  117. package/packages/test-data-generator/package.json +1 -1
  118. package/packages/test-generator/package.json +1 -1
  119. package/packages/todo-bd-sync/package.json +1 -1
  120. package/packages/token-estimation/package.json +1 -1
  121. package/packages/token-optimizer/package.json +1 -1
  122. package/packages/tooling-preference/package.json +1 -1
  123. package/packages/trajectory/package.json +1 -1
  124. package/packages/tui/package.json +1 -1
  125. package/packages/two-phase-review/package.json +1 -1
  126. package/packages/types/package.json +1 -1
  127. package/packages/voice-api/package.json +1 -1
  128. package/packages/webmcp-tester/package.json +1 -1
  129. package/packages/wiki/package.json +1 -1
  130. package/packages/workflow-events/package.json +1 -1
  131. package/packages/workspace-scanner/package.json +1 -1
  132. package/packages/worktree/package.json +1 -1
  133. package/packages/write-review/package.json +1 -1
  134. package/scripts/sync-to-qdrant.ts +18 -37
@@ -36,6 +36,34 @@ export const LEGACY_SKILL_COLLECTION = "pi-harness-skills";
36
36
  export const LEGACY_SKILL_COLLECTION_MODEL = "sentence-transformers/all-MiniLM-L6-v2";
37
37
  /** The vector size of the legacy collection. */
38
38
  export const LEGACY_SKILL_COLLECTION_DIMENSIONS = 384;
39
+ /**
40
+ * The score below which a hit of the runtime collection is not a hit.
41
+ *
42
+ * The score of a vector search is a cosine between two vectors, and its
43
+ * scale belongs to the model that produced them. Measured against the
44
+ * runtime collection (123 skills, `text-embedding-3-small`):
45
+ *
46
+ * - a query that names a skill scores 0.31 to 0.56,
47
+ * - a query about something else scores 0.10 to 0.23, and the wrong
48
+ * skill that it matched tops out at 0.2305.
49
+ *
50
+ * The threshold sits between the two (0.27). The old value of 0.5 belonged
51
+ * to the MiniLM collection, whose vectors are not comparable, and it
52
+ * threw away even a clear match (0.47) - so a search of the runtime
53
+ * collection returned nothing at all.
54
+ *
55
+ * It lives here, next to the model and the collection, so that the two
56
+ * cannot drift apart.
57
+ */
58
+ export const SKILL_MIN_SCORE = 0.27;
59
+ /**
60
+ * The score below which a hit of the legacy collection is not a hit.
61
+ *
62
+ * The legacy collection holds MiniLM vectors. Their scale was never
63
+ * measured against a query, so the old value is kept. It is named here
64
+ * so it is not mistaken for the threshold of the runtime.
65
+ */
66
+ export const LEGACY_SKILL_MIN_SCORE = 0.5;
39
67
  /**
40
68
  * The body of a payload is truncated.
41
69
  *
@@ -1 +1 @@
1
- {"version":3,"file":"vector-schema.js","sourceRoot":"","sources":["../src/vector-schema.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"vector-schema.js","sourceRoot":"","sources":["../src/vector-schema.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/skills",
3
- "version": "1.1.415",
3
+ "version": "1.1.417",
4
4
  "description": "File-based skill system with pi.dev compatibility and Hermes self-improvement",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -21,6 +21,7 @@ import {
21
21
  LEGACY_SKILL_COLLECTION_DIMENSIONS,
22
22
  LEGACY_SKILL_COLLECTION_MODEL,
23
23
  SKILL_COLLECTION,
24
+ SKILL_MIN_SCORE,
24
25
  describeSkillSchemaMismatch,
25
26
  skillPointPayload,
26
27
  skillQdrantPoint,
@@ -1051,12 +1052,13 @@ export class SkillRegistry {
1051
1052
  *
1052
1053
  * @param query Natural-language query
1053
1054
  * @param limit Max results (default 5)
1054
- * @param threshold Minimum cosine score (default 0.5)
1055
+ * @param threshold Minimum cosine score (default: the score of the model
1056
+ * of the runtime collection - see SKILL_MIN_SCORE)
1055
1057
  */
1056
1058
  async findVector(
1057
1059
  query: string,
1058
1060
  limit = 5,
1059
- threshold = 0.5,
1061
+ threshold = SKILL_MIN_SCORE,
1060
1062
  ): Promise<Array<{ skill: Skill; score: number; source: "local" | "remote" }>> {
1061
1063
  await this._ensureQdrant();
1062
1064
  if (!this._qdrant?.ready) return [];
@@ -22,22 +22,26 @@ interface SanitizationRule {
22
22
  *
23
23
  * The list is about the names of the clients and the projects of the
24
24
  * users of the harness - not about the skills themselves.
25
+ *
26
+ * The patterns match a plain fragment, without a word boundary. A client
27
+ * name rarely stands alone: it appears inside a directory name
28
+ * (`th_digisoft_erp_chart.json`), inside a Python module (`inpac_pharma`)
29
+ * or in a sentence ("integrated with Paperclip"). A word boundary would
30
+ * miss all of them - and `_` is a word character, so a boundary never
31
+ * follows an underscore.
25
32
  */
26
33
  const SANITIZATION_RULES: SanitizationRule[] = [
27
34
  // Client company names (case-insensitive)
28
- { pattern: /\binpac\b/gi, replacement: "[Client A]" },
29
- { pattern: /\bm-capital\b/gi, replacement: "[Client A]" },
30
- { pattern: /\bmcapital\b/gi, replacement: "[Client A]" },
31
- { pattern: /\bdigisoft\b/gi, replacement: "[Client B]" },
35
+ { pattern: /inpac/gi, replacement: "[Client A]" },
36
+ { pattern: /m-capital/gi, replacement: "[Client A]" },
37
+ { pattern: /mcapital/gi, replacement: "[Client A]" },
38
+ { pattern: /digisoft/gi, replacement: "[Client B]" },
32
39
 
33
40
  // Project/app specific names
34
- { pattern: /\bgse-insurance\b/gi, replacement: "[Insurance Project]" },
35
- { pattern: /\btbs-import\b/gi, replacement: "[Import Project]" },
36
- { pattern: /\bdigisoft_erp\b/gi, replacement: "[Client B Erp]" },
37
- { pattern: /\bm_capital\b/gi, replacement: "[Client A]" },
38
- { pattern: /\binpac_\b/gi, replacement: "[Client A]_" },
39
- { pattern: /\bpaperclip_\b/gi, replacement: "[Client D]_" },
40
- { pattern: /\bopenclaw_\b/gi, replacement: "[Client F]_" },
41
+ { pattern: /gse-insurance/gi, replacement: "[Insurance Project]" },
42
+ { pattern: /tbs-import/gi, replacement: "[Import Project]" },
43
+ { pattern: /paperclip/gi, replacement: "[Client D]" },
44
+ { pattern: /openclaw/gi, replacement: "[Client F]" },
41
45
 
42
46
  // Generic replacements
43
47
  { pattern: /Company Name/gi, replacement: "[Client Name]" },
@@ -80,7 +84,28 @@ export function sanitizeText(text: string): string {
80
84
  *
81
85
  * The name is the identity of the point, so it has to be the same for
82
86
  * every writer: the map first, then the pattern rules.
87
+ *
88
+ * The result is a skill name - lower case, words separated by a dash -
89
+ * because the name of a point is also the id of the skill that a search
90
+ * returns. The replacements of the rules carry a space and brackets,
91
+ * which no skill name may hold.
83
92
  */
84
93
  export function skillPointName(name: string): string {
85
- return SKILL_NAME_MAP[name] || sanitizeText(name);
94
+ const mapped = SKILL_NAME_MAP[name];
95
+ if (mapped) return mapped;
96
+ return kebabName(sanitizeText(name));
97
+ }
98
+
99
+ /**
100
+ * Bring a name into the shape of a skill name.
101
+ *
102
+ * The name of a skill is lower case and its words are separated by a
103
+ * dash: `^[a-z0-9]+(-[a-z0-9]+)*$`.
104
+ */
105
+ function kebabName(name: string): string {
106
+ return name
107
+ .toLowerCase()
108
+ .replace(/[^a-z0-9]+/g, "-")
109
+ .replace(/-{2,}/g, "-")
110
+ .replace(/^-+|-+$/g, "");
86
111
  }
@@ -10,6 +10,7 @@ import { resolve } from "node:path";
10
10
  import type { ExtensionAPI, ExtensionContext, ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
11
11
  import { getGlobalSkillRegistry } from "./loader.js";
12
12
  import { scanForSkillsSync } from "./scanner.js";
13
+ import { SKILL_MIN_SCORE } from "./vector-schema.js";
13
14
 
14
15
  /**
15
16
  * Skill command configuration
@@ -244,7 +245,7 @@ async function handleSkillSearch(
244
245
 
245
246
  // Try vector search if requested and available
246
247
  if (useVector && registry.isQdrantReady) {
247
- const vectorResults = await registry.findVector(query, 5, 0.5);
248
+ const vectorResults = await registry.findVector(query, 5, SKILL_MIN_SCORE);
248
249
  if (vectorResults.length > 0) {
249
250
  backend = "vector";
250
251
  results = vectorResults.map(r => ({ skill: r.skill, score: r.score }));
@@ -42,6 +42,36 @@ export const LEGACY_SKILL_COLLECTION_MODEL = "sentence-transformers/all-MiniLM-L
42
42
  /** The vector size of the legacy collection. */
43
43
  export const LEGACY_SKILL_COLLECTION_DIMENSIONS = 384;
44
44
 
45
+ /**
46
+ * The score below which a hit of the runtime collection is not a hit.
47
+ *
48
+ * The score of a vector search is a cosine between two vectors, and its
49
+ * scale belongs to the model that produced them. Measured against the
50
+ * runtime collection (123 skills, `text-embedding-3-small`):
51
+ *
52
+ * - a query that names a skill scores 0.31 to 0.56,
53
+ * - a query about something else scores 0.10 to 0.23, and the wrong
54
+ * skill that it matched tops out at 0.2305.
55
+ *
56
+ * The threshold sits between the two (0.27). The old value of 0.5 belonged
57
+ * to the MiniLM collection, whose vectors are not comparable, and it
58
+ * threw away even a clear match (0.47) - so a search of the runtime
59
+ * collection returned nothing at all.
60
+ *
61
+ * It lives here, next to the model and the collection, so that the two
62
+ * cannot drift apart.
63
+ */
64
+ export const SKILL_MIN_SCORE = 0.27;
65
+
66
+ /**
67
+ * The score below which a hit of the legacy collection is not a hit.
68
+ *
69
+ * The legacy collection holds MiniLM vectors. Their scale was never
70
+ * measured against a query, so the old value is kept. It is named here
71
+ * so it is not mistaken for the threshold of the runtime.
72
+ */
73
+ export const LEGACY_SKILL_MIN_SCORE = 0.5;
74
+
45
75
  /** The schema a skill collection is expected to have. */
46
76
  export interface SkillVectorSchema {
47
77
  /** The vector size. */
@@ -32,6 +32,26 @@ describe("the sanitization of a text", () => {
32
32
  expect(sanitizeText("your company")).toBe("[Client]");
33
33
  expect(sanitizeText("our client")).toBe("[Client]");
34
34
  });
35
+
36
+ test("a client name inside an identifier is replaced", () => {
37
+ // The name of a client rarely stands alone. A word boundary would
38
+ // miss every one of these - and an underscore is a word character,
39
+ // so a boundary never follows one.
40
+ expect(sanitizeText("inpac_pharma")).toBe("[Client A]_pharma");
41
+ expect(sanitizeText("th_digisoft_erp_chart.json")).toBe(
42
+ "th_[Client B]_erp_chart.json",
43
+ );
44
+ expect(sanitizeText("apps/inpac_pharma/setup")).toBe(
45
+ "apps/[Client A]_pharma/setup",
46
+ );
47
+ });
48
+
49
+ test("a client name in a sentence is replaced", () => {
50
+ expect(sanitizeText("integrated with Paperclip")).toBe(
51
+ "integrated with [Client D]",
52
+ );
53
+ expect(sanitizeText("OpenClaw secrets")).toBe("[Client F] secrets");
54
+ });
35
55
  });
36
56
 
37
57
  describe("the name of a skill in the shared collection", () => {
@@ -59,6 +79,29 @@ describe("the name of a skill in the shared collection", () => {
59
79
  expect(skillPointName(name)).not.toMatch(/inpac|gse-insurance|tbs-import/i);
60
80
  }
61
81
  });
82
+
83
+ test("the name is a valid skill name", () => {
84
+ // The name of a point is the id of the skill that a search returns,
85
+ // so it has to be a name that a skill may carry.
86
+ const NAME = /^[a-z0-9]+(-[a-z0-9]+)*$/;
87
+ for (const name of [
88
+ "inpac-pe-approval-workflow",
89
+ "openclaw-channel",
90
+ "gse-insurance-step5-slot-filter",
91
+ "tbs-import-clearance-lcv",
92
+ "whispertool",
93
+ "code-review",
94
+ ]) {
95
+ expect(skillPointName(name)).toMatch(NAME);
96
+ }
97
+ });
98
+
99
+ test("a name that needs no sanitization stays the same", () => {
100
+ expect(skillPointName("code-review")).toBe("code-review");
101
+ expect(skillPointName("frappe-doctype-field-order")).toBe(
102
+ "frappe-doctype-field-order",
103
+ );
104
+ });
62
105
  });
63
106
 
64
107
  describe("the payload of a point is sanitized", () => {
@@ -138,8 +138,21 @@ describe("the identity of a skill point", () => {
138
138
  body: "b",
139
139
  });
140
140
 
141
- expect(withTitle.text).toContain("A Human Title");
142
- expect(withoutTitle.text).not.toContain("A Human Title");
141
+ // The title is treated like the name: it is sanitized and it is
142
+ // brought into the shape of a skill name.
143
+ expect(withTitle.text).toContain("a-human-title");
144
+ expect(withoutTitle.text).not.toContain("a-human-title");
145
+ });
146
+
147
+ test("a title that is the name is not repeated in the text", () => {
148
+ const payload = skillPointPayload({
149
+ name: "a-skill",
150
+ title: "a-skill",
151
+ description: "d",
152
+ body: "b",
153
+ });
154
+
155
+ expect(payload.text.match(/a-skill/g)?.length).toBe(1);
143
156
  });
144
157
  });
145
158
 
@@ -0,0 +1,108 @@
1
+ /**
2
+ * The score that decides whether a hit of the vector store is a hit.
3
+ *
4
+ * The score of a vector search is a cosine between two vectors, and its
5
+ * scale belongs to the model that produced them. The runtime embeds with
6
+ * `text-embedding-3-small`, whose cosines for a query that names a skill
7
+ * land between 0.31 and 0.56 - while a query about something else reaches
8
+ * 0.23 at most.
9
+ *
10
+ * The threshold used to be 0.5, a value of the MiniLM collection, whose
11
+ * vectors are not comparable. Against the runtime collection it threw away
12
+ * every clear match (0.47) - a search of a filled collection returned
13
+ * nothing at all, and the skill was invisible.
14
+ *
15
+ * The threshold therefore lives next to the model, and the tests below
16
+ * hold it there.
17
+ */
18
+
19
+ import { describe, expect, it } from "bun:test";
20
+ import { readFileSync } from "node:fs";
21
+ import { fileURLToPath } from "node:url";
22
+
23
+ import {
24
+ createSkillRegistry,
25
+ type QdrantClientLike,
26
+ type QdrantEmbedderLike,
27
+ } from "../src/loader.js";
28
+ import { SKILL_MIN_SCORE } from "../src/vector-schema.js";
29
+
30
+ /** A client that answers a query with the given points. */
31
+ function fakeClient(points: Array<{ id: number; score: number }>): QdrantClientLike {
32
+ return {
33
+ async upsert(): Promise<void> {},
34
+ async query(): Promise<unknown> {
35
+ return { points };
36
+ },
37
+ async getCollection(): Promise<unknown> {
38
+ return { config: { params: { vectors: { size: 1536, distance: "Cosine" } } } };
39
+ },
40
+ };
41
+ }
42
+
43
+ /** An embedder that returns a fixed vector. */
44
+ const fakeEmbedder: QdrantEmbedderLike = {
45
+ async createEmbedding() {
46
+ return { embedding: [0.1, 0.2, 0.3], tokens: 3 };
47
+ },
48
+ };
49
+
50
+ const registryWith = (scores: number[]) =>
51
+ createSkillRegistry({
52
+ qdrantClient: fakeClient(
53
+ scores.map((score, index) => ({
54
+ id: index + 1,
55
+ score,
56
+ payload: { name: `skill-${index}`, description: "d", body: "b" },
57
+ })),
58
+ ),
59
+ embedder: fakeEmbedder,
60
+ collection: "pi-harness-skills-test",
61
+ });
62
+
63
+ describe("the score that decides whether a hit is a hit", () => {
64
+ it("belongs to the model of the collection", () => {
65
+ // The measured values: a query that names a skill scores 0.31 to
66
+ // 0.56, a query about something else 0.10 to 0.23.
67
+ expect(SKILL_MIN_SCORE).toBeGreaterThan(0.23);
68
+ expect(SKILL_MIN_SCORE).toBeLessThan(0.31);
69
+ });
70
+
71
+ it("keeps a clear match that the old threshold of 0.5 threw away", async () => {
72
+ // 0.47 is a clear match of the runtime model - and below 0.5.
73
+ const registry = registryWith([0.47]);
74
+
75
+ const results = await registry.findVector("approve a purchase order");
76
+
77
+ expect(results).toHaveLength(1);
78
+ expect(results[0].score).toBe(0.47);
79
+ });
80
+
81
+ it("drops a hit that is noise", async () => {
82
+ const registry = registryWith([0.23]);
83
+
84
+ const results = await registry.findVector("cook a risotto");
85
+
86
+ expect(results).toHaveLength(0);
87
+ });
88
+
89
+ it("keeps the hits above it and drops the hits below it", async () => {
90
+ const registry = registryWith([0.55, 0.12, 0.4, 0.2]);
91
+
92
+ const results = await registry.findVector("a query");
93
+
94
+ expect(results.map((r) => r.score)).toEqual([0.55, 0.4]);
95
+ });
96
+
97
+ it("is the threshold of the skill command too", () => {
98
+ // The command passed 0.5 itself, so the fix of the default would
99
+ // not have reached the search of the user.
100
+ const source = readFileSync(
101
+ fileURLToPath(new URL("../src/skill-commands.ts", import.meta.url)),
102
+ "utf8",
103
+ );
104
+
105
+ expect(source).toContain("SKILL_MIN_SCORE");
106
+ expect(source).not.toMatch(/findVector\([^)]*,\s*5,\s*0\.5/);
107
+ });
108
+ });
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/sprint-planner",
3
- "version": "1.1.415",
3
+ "version": "1.1.417",
4
4
  "type": "module",
5
5
  "description": "Sprint planning engine for pi-harness",
6
6
  "main": "dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/subscription-engine",
3
- "version": "1.1.415",
3
+ "version": "1.1.417",
4
4
  "type": "module",
5
5
  "main": "src/index.ts",
6
6
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/task-compiler",
3
- "version": "1.1.415",
3
+ "version": "1.1.417",
4
4
  "description": "Task Compiler for pi-harness-runtime — produces deterministic task DAG from CompiledRequirement",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/telegram",
3
- "version": "1.1.415",
3
+ "version": "1.1.417",
4
4
  "description": "pi-harness-runtime — Telegram inbound/outbound",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moocoding/tencentdb-memory",
3
- "version": "1.1.415",
3
+ "version": "1.1.417",
4
4
  "description": "TencentDB Agent Memory client - sync skills to centralized knowledge base",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/tencentdb-sync",
3
- "version": "1.1.415",
3
+ "version": "1.1.417",
4
4
  "description": "Sync SKILL.md skills to TencentDB-Agent-Memory server (RFC-0105/0106)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/test-data-generator",
3
- "version": "1.1.415",
3
+ "version": "1.1.417",
4
4
  "description": "Test data generation for pi-harness-runtime",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/test-generator",
3
- "version": "1.1.415",
3
+ "version": "1.1.417",
4
4
  "description": "Auto-Test Generator — generate unit and E2E tests from source code (RFC-0013)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moocoding/todo-bd-sync",
3
- "version": "1.1.415",
3
+ "version": "1.1.417",
4
4
  "description": "Two-way sync between rpiv-todo overlay and bd issue tracker",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/token-estimation",
3
- "version": "1.1.415",
3
+ "version": "1.1.417",
4
4
  "description": "pi-harness-runtime — token-estimation",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/token-optimizer",
3
- "version": "1.1.415",
3
+ "version": "1.1.417",
4
4
  "description": "Token Budget Optimizer — maximize prompt value within token limits (RFC-0011)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/tooling-preference",
3
- "version": "1.1.415",
3
+ "version": "1.1.417",
4
4
  "description": "pi-harness-runtime — JS/TS tooling preference (Biome vs ESLint/Prettier) proposal",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/trajectory",
3
- "version": "1.1.415",
3
+ "version": "1.1.417",
4
4
  "description": "Trajectory capture for the write-review loop — persists cycle records for learning",
5
5
  "type": "module",
6
6
  "main": "./dist/src/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/tui",
3
- "version": "1.1.415",
3
+ "version": "1.1.417",
4
4
  "description": "pi-harness-runtime — tui",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/two-phase-review",
3
- "version": "1.1.415",
3
+ "version": "1.1.417",
4
4
  "description": "pi-harness-runtime — two-phase implementation and review workflow (queue review signals, gate the phase, scope the diff)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/types",
3
- "version": "1.1.415",
3
+ "version": "1.1.417",
4
4
  "description": "Shared TypeScript types for pi-harness-runtime packages",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/voice-api",
3
- "version": "1.1.415",
3
+ "version": "1.1.417",
4
4
  "type": "module",
5
5
  "description": "Voice-to-text API using Whisper for Telegram and LINE integration",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/webmcp-tester",
3
- "version": "1.1.415",
3
+ "version": "1.1.417",
4
4
  "description": "WebMCP-powered E2E testing for pi-harness - natural language test scenarios",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/wiki",
3
- "version": "1.1.415",
3
+ "version": "1.1.417",
4
4
  "description": "LLM Wiki integration for pi-harness-runtime",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/workflow-events",
3
- "version": "1.1.415",
3
+ "version": "1.1.417",
4
4
  "type": "module",
5
5
  "main": "src/index.ts",
6
6
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/workspace-scanner",
3
- "version": "1.1.415",
3
+ "version": "1.1.417",
4
4
  "type": "module",
5
5
  "main": "src/index.ts",
6
6
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/worktree",
3
- "version": "1.1.415",
3
+ "version": "1.1.417",
4
4
  "description": "pi-harness-runtime — worktree",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moocoding/write-review",
3
- "version": "1.1.415",
3
+ "version": "1.1.417",
4
4
  "description": "Two-agent write-review loop with blackboard coordination",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",