pi-harness-runtime 1.1.413 → 1.1.415
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/package.json +1 -1
- package/packages/a2a-adapter/package.json +1 -1
- package/packages/agent-policy/package.json +1 -1
- package/packages/architecture-generator/package.json +1 -1
- package/packages/auth/package.json +1 -1
- package/packages/autonomous-refactor/package.json +1 -1
- package/packages/autonomous-runtime/package.json +1 -1
- package/packages/cache-strategy/package.json +1 -1
- package/packages/capability-registry/package.json +1 -1
- package/packages/checkpoint/package.json +1 -1
- package/packages/cli-plugin-sdk/package.json +1 -1
- package/packages/clipboard/package.json +1 -1
- package/packages/clipboard-plugin/package.json +1 -1
- package/packages/code-generation/package.json +1 -1
- package/packages/code-review/package.json +1 -1
- package/packages/codex-adapter/package.json +1 -1
- package/packages/codex-watcher/package.json +1 -1
- package/packages/config-capture/package.json +1 -1
- package/packages/connection-runtime/package.json +1 -1
- package/packages/context-compiler/package.json +1 -1
- package/packages/context-discovery/package.json +1 -1
- package/packages/context-manager/package.json +1 -1
- package/packages/contracts/package.json +1 -1
- package/packages/cookie-sanitizer/package.json +1 -1
- package/packages/cost-optimizer/package.json +1 -1
- package/packages/dependency-analyzer/package.json +1 -1
- package/packages/django-plugin/package.json +1 -1
- package/packages/doc-generator/package.json +1 -1
- package/packages/durable-review/package.json +1 -1
- package/packages/e2e-tester/package.json +1 -1
- package/packages/evaluation-engine/package.json +1 -1
- package/packages/evaluation-runner/package.json +1 -1
- package/packages/event-bus/package.json +1 -1
- package/packages/event-sink/package.json +1 -1
- package/packages/event-store/package.json +1 -1
- package/packages/experience-replay/package.json +1 -1
- package/packages/feedback-collector/package.json +1 -1
- package/packages/file-copy-helper/package.json +1 -1
- package/packages/framework-detector/package.json +1 -1
- package/packages/framework-plugin-sdk/package.json +1 -1
- package/packages/frappe-plugin/package.json +1 -1
- package/packages/generic-web-plugin/package.json +1 -1
- package/packages/health-monitor/package.json +1 -1
- package/packages/intent-analyzer/package.json +1 -1
- package/packages/jev-judge/package.json +1 -1
- package/packages/knowledge-graph/package.json +1 -1
- package/packages/knowledge-retrieval/package.json +1 -1
- package/packages/laravel-plugin/package.json +1 -1
- package/packages/learning-engine/package.json +1 -1
- package/packages/logging/package.json +1 -1
- package/packages/mcp-adapter/package.json +1 -1
- package/packages/memory-engine/package.json +1 -1
- package/packages/milestone-manager/package.json +1 -1
- package/packages/model-registry/package.json +1 -1
- package/packages/nextjs-plugin/package.json +1 -1
- package/packages/notification/package.json +1 -1
- package/packages/observability/package.json +1 -1
- package/packages/okf-indexer/package.json +1 -1
- package/packages/paperclip-integration/package.json +1 -1
- package/packages/performance-optimizer/package.json +1 -1
- package/packages/permission-mode/package.json +1 -1
- package/packages/privilege-broker/package.json +1 -1
- package/packages/project-analyzer/package.json +1 -1
- package/packages/project-bootstrap/package.json +1 -1
- package/packages/projection-engine/package.json +1 -1
- package/packages/prompt-compiler/package.json +1 -1
- package/packages/prompt-versioning/package.json +1 -1
- package/packages/provider-adapter-sdk/package.json +1 -1
- package/packages/provider-router/package.json +1 -1
- package/packages/provider-selector/package.json +1 -1
- package/packages/providers/package.json +1 -1
- package/packages/qdrant-skills/package.json +1 -1
- package/packages/quota-manager/package.json +1 -1
- package/packages/rate-limiter/package.json +1 -1
- package/packages/react-vite-plugin/package.json +1 -1
- package/packages/relay/package.json +1 -1
- package/packages/release-manager/package.json +1 -1
- package/packages/requirement-compiler/package.json +1 -1
- package/packages/runtime/package.json +1 -1
- package/packages/scheduler/package.json +1 -1
- package/packages/scheduler-adapter/package.json +1 -1
- package/packages/session/package.json +1 -1
- package/packages/session-api/package.json +1 -1
- package/packages/session-export/package.json +1 -1
- package/packages/shared-context/package.json +1 -1
- package/packages/skill-mcp-client/package.json +1 -1
- package/packages/skill-registry/package.json +1 -1
- package/packages/skills/dist/index.d.ts +1 -0
- package/packages/skills/dist/index.d.ts.map +1 -1
- package/packages/skills/dist/index.js +1 -0
- package/packages/skills/dist/index.js.map +1 -1
- package/packages/skills/dist/loader.d.ts +19 -7
- package/packages/skills/dist/loader.d.ts.map +1 -1
- package/packages/skills/dist/loader.js +39 -22
- package/packages/skills/dist/loader.js.map +1 -1
- package/packages/skills/dist/sanitize.d.ts +27 -0
- package/packages/skills/dist/sanitize.d.ts.map +1 -0
- package/packages/skills/dist/sanitize.js +75 -0
- package/packages/skills/dist/sanitize.js.map +1 -0
- package/packages/skills/dist/vector-schema.d.ts +93 -4
- package/packages/skills/dist/vector-schema.d.ts.map +1 -1
- package/packages/skills/dist/vector-schema.js +71 -4
- package/packages/skills/dist/vector-schema.js.map +1 -1
- package/packages/skills/package.json +1 -1
- package/packages/skills/src/index.ts +1 -0
- package/packages/skills/src/loader.ts +53 -27
- package/packages/skills/src/sanitize.ts +86 -0
- package/packages/skills/src/vector-schema.ts +127 -4
- package/packages/skills/test/remote-skills.test.ts +41 -0
- package/packages/skills/test/sanitize.test.ts +88 -0
- package/packages/skills/test/vector-schema.test.ts +167 -0
- package/packages/sprint-planner/package.json +1 -1
- package/packages/subscription-engine/package.json +1 -1
- package/packages/task-compiler/package.json +1 -1
- package/packages/telegram/package.json +1 -1
- package/packages/tencentdb-memory/package.json +1 -1
- package/packages/tencentdb-sync/package.json +1 -1
- package/packages/test-data-generator/package.json +1 -1
- package/packages/test-generator/package.json +1 -1
- package/packages/todo-bd-sync/package.json +1 -1
- package/packages/token-estimation/package.json +1 -1
- package/packages/token-optimizer/package.json +1 -1
- package/packages/tooling-preference/package.json +1 -1
- package/packages/trajectory/package.json +1 -1
- package/packages/tui/package.json +1 -1
- package/packages/two-phase-review/package.json +1 -1
- package/packages/types/package.json +1 -1
- package/packages/voice-api/package.json +1 -1
- package/packages/webmcp-tester/package.json +1 -1
- package/packages/wiki/package.json +1 -1
- package/packages/workflow-events/package.json +1 -1
- package/packages/workspace-scanner/package.json +1 -1
- package/packages/worktree/package.json +1 -1
- package/packages/write-review/package.json +1 -1
- package/scripts/sync-to-qdrant.ts +29 -69
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The sanitization of a skill for the shared collection.
|
|
3
|
+
*
|
|
4
|
+
* The collection is shared, so a client name must not travel into it. The
|
|
5
|
+
* rules lived in `scripts/sync-to-qdrant.ts` - the only writer that could
|
|
6
|
+
* reach them. The runtime registry writes into the same collection, so it
|
|
7
|
+
* needs them too: a writer that skipped the rules would leave the raw
|
|
8
|
+
* name and the raw body of a client skill in a shared cluster.
|
|
9
|
+
*
|
|
10
|
+
* Both writers therefore build their points with the same rules, and the
|
|
11
|
+
* name of a point is the sanitized name. One skill, one point.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* The patterns of the client specific names.
|
|
15
|
+
*
|
|
16
|
+
* The list is about the names of the clients and the projects of the
|
|
17
|
+
* users of the harness - not about the skills themselves.
|
|
18
|
+
*/
|
|
19
|
+
const SANITIZATION_RULES = [
|
|
20
|
+
// Client company names (case-insensitive)
|
|
21
|
+
{ pattern: /\binpac\b/gi, replacement: "[Client A]" },
|
|
22
|
+
{ pattern: /\bm-capital\b/gi, replacement: "[Client A]" },
|
|
23
|
+
{ pattern: /\bmcapital\b/gi, replacement: "[Client A]" },
|
|
24
|
+
{ pattern: /\bdigisoft\b/gi, replacement: "[Client B]" },
|
|
25
|
+
// Project/app specific names
|
|
26
|
+
{ pattern: /\bgse-insurance\b/gi, replacement: "[Insurance Project]" },
|
|
27
|
+
{ pattern: /\btbs-import\b/gi, replacement: "[Import Project]" },
|
|
28
|
+
{ pattern: /\bdigisoft_erp\b/gi, replacement: "[Client B Erp]" },
|
|
29
|
+
{ pattern: /\bm_capital\b/gi, replacement: "[Client A]" },
|
|
30
|
+
{ pattern: /\binpac_\b/gi, replacement: "[Client A]_" },
|
|
31
|
+
{ pattern: /\bpaperclip_\b/gi, replacement: "[Client D]_" },
|
|
32
|
+
{ pattern: /\bopenclaw_\b/gi, replacement: "[Client F]_" },
|
|
33
|
+
// Generic replacements
|
|
34
|
+
{ pattern: /Company Name/gi, replacement: "[Client Name]" },
|
|
35
|
+
{ pattern: /your company/gi, replacement: "[Client]" },
|
|
36
|
+
{ pattern: /our client/gi, replacement: "[Client]" },
|
|
37
|
+
{ pattern: /client-specific/gi, replacement: "[Project-specific]" },
|
|
38
|
+
];
|
|
39
|
+
/**
|
|
40
|
+
* The names that are renamed as a whole.
|
|
41
|
+
*
|
|
42
|
+
* A name that would be unreadable after the pattern rules - a name that
|
|
43
|
+
* carries the client name in its middle - is renamed here.
|
|
44
|
+
*/
|
|
45
|
+
const SKILL_NAME_MAP = {
|
|
46
|
+
"gse-insurance-step5-slot-filter": "insurance-project-step-filter",
|
|
47
|
+
"inpac-pe-approval-workflow": "client-a-pe-approval-workflow",
|
|
48
|
+
"inpac-po-approval-workflow": "client-a-po-approval-workflow",
|
|
49
|
+
"inpac-pr-approval-workflow": "client-a-pr-approval-workflow",
|
|
50
|
+
"tbs-import-clearance-lcv": "import-project-clearance",
|
|
51
|
+
whispertool: "whisper-tool",
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* Replace the client specific names of a text.
|
|
55
|
+
*
|
|
56
|
+
* The text stays the same length, so a vector that was built from it
|
|
57
|
+
* keeps its shape.
|
|
58
|
+
*/
|
|
59
|
+
export function sanitizeText(text) {
|
|
60
|
+
let result = text;
|
|
61
|
+
for (const rule of SANITIZATION_RULES) {
|
|
62
|
+
result = result.replace(rule.pattern, rule.replacement);
|
|
63
|
+
}
|
|
64
|
+
return result;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* The name of a skill in the shared collection.
|
|
68
|
+
*
|
|
69
|
+
* The name is the identity of the point, so it has to be the same for
|
|
70
|
+
* every writer: the map first, then the pattern rules.
|
|
71
|
+
*/
|
|
72
|
+
export function skillPointName(name) {
|
|
73
|
+
return SKILL_NAME_MAP[name] || sanitizeText(name);
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=sanitize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sanitize.js","sourceRoot":"","sources":["../src/sanitize.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAQH;;;;;GAKG;AACH,MAAM,kBAAkB,GAAuB;IAC7C,0CAA0C;IAC1C,EAAE,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,YAAY,EAAE;IACrD,EAAE,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,YAAY,EAAE;IACzD,EAAE,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,YAAY,EAAE;IACxD,EAAE,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,YAAY,EAAE;IAExD,6BAA6B;IAC7B,EAAE,OAAO,EAAE,qBAAqB,EAAE,WAAW,EAAE,qBAAqB,EAAE;IACtE,EAAE,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,kBAAkB,EAAE;IAChE,EAAE,OAAO,EAAE,oBAAoB,EAAE,WAAW,EAAE,gBAAgB,EAAE;IAChE,EAAE,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,YAAY,EAAE;IACzD,EAAE,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,aAAa,EAAE;IACvD,EAAE,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,aAAa,EAAE;IAC3D,EAAE,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,aAAa,EAAE;IAE1D,uBAAuB;IACvB,EAAE,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,eAAe,EAAE;IAC3D,EAAE,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,UAAU,EAAE;IACtD,EAAE,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,UAAU,EAAE;IACpD,EAAE,OAAO,EAAE,mBAAmB,EAAE,WAAW,EAAE,oBAAoB,EAAE;CACpE,CAAC;AAEF;;;;;GAKG;AACH,MAAM,cAAc,GAA2B;IAC9C,iCAAiC,EAAE,+BAA+B;IAClE,4BAA4B,EAAE,+BAA+B;IAC7D,4BAA4B,EAAE,+BAA+B;IAC7D,4BAA4B,EAAE,+BAA+B;IAC7D,0BAA0B,EAAE,0BAA0B;IACtD,WAAW,EAAE,cAAc;CAC3B,CAAC;AAEF;;;;;GAKG;AACH,MAAM,UAAU,YAAY,CAAC,IAAY;IACxC,IAAI,MAAM,GAAG,IAAI,CAAC;IAClB,KAAK,MAAM,IAAI,IAAI,kBAAkB,EAAE,CAAC;QACvC,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;IACzD,CAAC;IACD,OAAO,MAAM,CAAC;AACf,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,IAAY;IAC1C,OAAO,cAAc,CAAC,IAAI,CAAC,IAAI,YAAY,CAAC,IAAI,CAAC,CAAC;AACnD,CAAC"}
|
|
@@ -45,12 +45,101 @@ export interface SkillVectorSchema {
|
|
|
45
45
|
model?: string;
|
|
46
46
|
}
|
|
47
47
|
/**
|
|
48
|
-
*
|
|
49
|
-
* does.
|
|
48
|
+
* The payload of a skill point.
|
|
50
49
|
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
50
|
+
* Both writers of the collection - the runtime registry and
|
|
51
|
+
* `scripts/sync-to-qdrant.ts` - store the SAME payload under the SAME
|
|
52
|
+
* point id. A point is addressed by its id, so a writer that used a
|
|
53
|
+
* different id for the same skill would leave two points behind, and a
|
|
54
|
+
* writer whose id happened to collide with a foreign one would overwrite
|
|
55
|
+
* an unrelated skill.
|
|
53
56
|
*/
|
|
57
|
+
export type SkillPointPayload = {
|
|
58
|
+
/** The name of the skill. The point id is derived from it. */
|
|
59
|
+
name: string;
|
|
60
|
+
/** What the skill does. */
|
|
61
|
+
description: string;
|
|
62
|
+
/** The body of the skill. */
|
|
63
|
+
body: string;
|
|
64
|
+
/** The text the vector was built from. */
|
|
65
|
+
text: string;
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* The body of a payload is truncated.
|
|
69
|
+
*
|
|
70
|
+
* A payload is an index, not a file store - the complete skill comes from
|
|
71
|
+
* the skill service.
|
|
72
|
+
*/
|
|
73
|
+
export declare const SKILL_PAYLOAD_BODY_LIMIT = 2000;
|
|
74
|
+
/** The text of a payload is truncated as well. */
|
|
75
|
+
export declare const SKILL_PAYLOAD_TEXT_LIMIT = 1500;
|
|
76
|
+
/**
|
|
77
|
+
* A stable hash of a string.
|
|
78
|
+
*
|
|
79
|
+
* The same string always produces the same number, in every process and
|
|
80
|
+
* on every machine - a point id has to survive a restart.
|
|
81
|
+
*
|
|
82
|
+
* Two uses:
|
|
83
|
+
* - the id of a point is the hash of the name its payload carries, so the
|
|
84
|
+
* point id and the payload name always belong together. Two writers
|
|
85
|
+
* that store the same name address the same point.
|
|
86
|
+
* - the hash of the text of a skill says whether its content changed.
|
|
87
|
+
*/
|
|
88
|
+
export declare function stableHash(value: string): number;
|
|
89
|
+
/**
|
|
90
|
+
* The text a skill vector is built from.
|
|
91
|
+
*
|
|
92
|
+
* The title is added when it differs from the name: a skill whose
|
|
93
|
+
* directory is named differently from its title carries both.
|
|
94
|
+
*/
|
|
95
|
+
export declare function skillPointText(input: {
|
|
96
|
+
/** The name the payload carries - the identity of the skill. */
|
|
97
|
+
name: string;
|
|
98
|
+
/** The title of the skill, when it differs from the name. */
|
|
99
|
+
title?: string;
|
|
100
|
+
/** What the skill does. */
|
|
101
|
+
description: string;
|
|
102
|
+
/** The body of the skill. */
|
|
103
|
+
body: string;
|
|
104
|
+
}): string;
|
|
105
|
+
/**
|
|
106
|
+
* A point of the skill collection.
|
|
107
|
+
*
|
|
108
|
+
* The id is the hash of the name the payload carries, so the point id and
|
|
109
|
+
* the payload name always belong together: two writers that store the
|
|
110
|
+
* same name address the same point.
|
|
111
|
+
*/
|
|
112
|
+
export interface SkillQdrantPoint {
|
|
113
|
+
id: number;
|
|
114
|
+
payload: SkillPointPayload;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* The point of a skill.
|
|
118
|
+
*
|
|
119
|
+
* The runtime registry and `scripts/sync-to-qdrant.ts` both build their
|
|
120
|
+
* points here, so they cannot drift apart. A writer that used its own id
|
|
121
|
+
* would leave a second point for every skill the other one indexes, and
|
|
122
|
+
* an id that happens to collide with a foreign one would overwrite an
|
|
123
|
+
* unrelated skill.
|
|
124
|
+
*/
|
|
125
|
+
export declare function skillQdrantPoint(payload: SkillPointPayload): SkillQdrantPoint;
|
|
126
|
+
/**
|
|
127
|
+
* The payload of a skill point.
|
|
128
|
+
*
|
|
129
|
+
* The payload is sanitized: the collection is shared, so the client names
|
|
130
|
+
* of a user must not travel into it. Both writers use this function, so
|
|
131
|
+
* both write the same sanitized payload for the same skill.
|
|
132
|
+
*/
|
|
133
|
+
export declare function skillPointPayload(input: {
|
|
134
|
+
/** The name the payload carries - the identity of the skill. */
|
|
135
|
+
name: string;
|
|
136
|
+
/** The title of the skill, when it differs from the name. */
|
|
137
|
+
title?: string;
|
|
138
|
+
/** What the skill does. */
|
|
139
|
+
description: string;
|
|
140
|
+
/** The body of the skill. */
|
|
141
|
+
body: string;
|
|
142
|
+
}): SkillPointPayload;
|
|
54
143
|
export declare function describeSkillSchemaMismatch(actual: {
|
|
55
144
|
dimensions?: number;
|
|
56
145
|
distance?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vector-schema.d.ts","sourceRoot":"","sources":["../src/vector-schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;
|
|
1
|
+
{"version":3,"file":"vector-schema.d.ts","sourceRoot":"","sources":["../src/vector-schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAIH;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,gCAAgC,CAAC;AAE9D;;;;;;;GAOG;AACH,eAAO,MAAM,uBAAuB,sBAAsB,CAAC;AAE3D,0CAA0C;AAC1C,eAAO,MAAM,6BAA6B,2CAA2C,CAAC;AAEtF,gDAAgD;AAChD,eAAO,MAAM,kCAAkC,MAAM,CAAC;AAEtD,yDAAyD;AACzD,MAAM,WAAW,iBAAiB;IACjC,uBAAuB;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,2BAA2B;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,6CAA6C;IAC7C,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;GASG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC9B,8DAA8D;IAC9D,IAAI,EAAE,MAAM,CAAC;IACb,2BAA2B;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,6BAA6B;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,0CAA0C;IAC1C,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,OAAO,CAAC;AAE7C,kDAAkD;AAClD,eAAO,MAAM,wBAAwB,OAAO,CAAC;AAE7C;;;;;;;;;;;GAWG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAMhD;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE;IACpC,gEAAgE;IAChE,IAAI,EAAE,MAAM,CAAC;IACb,6DAA6D;IAC7D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2BAA2B;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,6BAA6B;IAC7B,IAAI,EAAE,MAAM,CAAC;CACd,GAAG,MAAM,CAMT;AAED;;;;;;GAMG;AACH,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,iBAAiB,CAAC;CAC5B;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,iBAAiB,GAAG,gBAAgB,CAE7E;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE;IACvC,gEAAgE;IAChE,IAAI,EAAE,MAAM,CAAC;IACb,6DAA6D;IAC7D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2BAA2B;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,6BAA6B;IAC7B,IAAI,EAAE,MAAM,CAAC;CACd,GAAG,iBAAiB,CAWpB;AACD,wBAAgB,2BAA2B,CAC1C,MAAM,EAAE;IAAE,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,EAClD,QAAQ,EAAE,iBAAiB,GACzB,MAAM,GAAG,IAAI,CASf"}
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
* module owns the collection - that is a decision of the skills domain, not
|
|
16
16
|
* of the vector store.
|
|
17
17
|
*/
|
|
18
|
+
import { sanitizeText, skillPointName } from "./sanitize.js";
|
|
18
19
|
/**
|
|
19
20
|
* The collection the runtime indexes into and searches.
|
|
20
21
|
*
|
|
@@ -36,12 +37,78 @@ export const LEGACY_SKILL_COLLECTION_MODEL = "sentence-transformers/all-MiniLM-L
|
|
|
36
37
|
/** The vector size of the legacy collection. */
|
|
37
38
|
export const LEGACY_SKILL_COLLECTION_DIMENSIONS = 384;
|
|
38
39
|
/**
|
|
39
|
-
*
|
|
40
|
-
* does.
|
|
40
|
+
* The body of a payload is truncated.
|
|
41
41
|
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
42
|
+
* A payload is an index, not a file store - the complete skill comes from
|
|
43
|
+
* the skill service.
|
|
44
44
|
*/
|
|
45
|
+
export const SKILL_PAYLOAD_BODY_LIMIT = 2000;
|
|
46
|
+
/** The text of a payload is truncated as well. */
|
|
47
|
+
export const SKILL_PAYLOAD_TEXT_LIMIT = 1500;
|
|
48
|
+
/**
|
|
49
|
+
* A stable hash of a string.
|
|
50
|
+
*
|
|
51
|
+
* The same string always produces the same number, in every process and
|
|
52
|
+
* on every machine - a point id has to survive a restart.
|
|
53
|
+
*
|
|
54
|
+
* Two uses:
|
|
55
|
+
* - the id of a point is the hash of the name its payload carries, so the
|
|
56
|
+
* point id and the payload name always belong together. Two writers
|
|
57
|
+
* that store the same name address the same point.
|
|
58
|
+
* - the hash of the text of a skill says whether its content changed.
|
|
59
|
+
*/
|
|
60
|
+
export function stableHash(value) {
|
|
61
|
+
let hash = 0;
|
|
62
|
+
for (let i = 0; i < value.length; i++) {
|
|
63
|
+
hash = (Math.imul(31, hash) + value.charCodeAt(i)) | 0;
|
|
64
|
+
}
|
|
65
|
+
return Math.abs(hash);
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* The text a skill vector is built from.
|
|
69
|
+
*
|
|
70
|
+
* The title is added when it differs from the name: a skill whose
|
|
71
|
+
* directory is named differently from its title carries both.
|
|
72
|
+
*/
|
|
73
|
+
export function skillPointText(input) {
|
|
74
|
+
const parts = [input.name];
|
|
75
|
+
if (input.title && input.title !== input.name)
|
|
76
|
+
parts.push(input.title);
|
|
77
|
+
parts.push(input.description);
|
|
78
|
+
parts.push(input.body.slice(0, SKILL_PAYLOAD_TEXT_LIMIT));
|
|
79
|
+
return parts.join(" ");
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* The point of a skill.
|
|
83
|
+
*
|
|
84
|
+
* The runtime registry and `scripts/sync-to-qdrant.ts` both build their
|
|
85
|
+
* points here, so they cannot drift apart. A writer that used its own id
|
|
86
|
+
* would leave a second point for every skill the other one indexes, and
|
|
87
|
+
* an id that happens to collide with a foreign one would overwrite an
|
|
88
|
+
* unrelated skill.
|
|
89
|
+
*/
|
|
90
|
+
export function skillQdrantPoint(payload) {
|
|
91
|
+
return { id: stableHash(payload.name), payload };
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* The payload of a skill point.
|
|
95
|
+
*
|
|
96
|
+
* The payload is sanitized: the collection is shared, so the client names
|
|
97
|
+
* of a user must not travel into it. Both writers use this function, so
|
|
98
|
+
* both write the same sanitized payload for the same skill.
|
|
99
|
+
*/
|
|
100
|
+
export function skillPointPayload(input) {
|
|
101
|
+
const name = skillPointName(input.name);
|
|
102
|
+
const title = input.title ? skillPointName(input.title) : undefined;
|
|
103
|
+
const description = sanitizeText(input.description);
|
|
104
|
+
const body = sanitizeText(input.body).slice(0, SKILL_PAYLOAD_BODY_LIMIT);
|
|
105
|
+
return {
|
|
106
|
+
name,
|
|
107
|
+
description,
|
|
108
|
+
body,
|
|
109
|
+
text: skillPointText({ name, title, description, body }),
|
|
110
|
+
};
|
|
111
|
+
}
|
|
45
112
|
export function describeSkillSchemaMismatch(actual, expected) {
|
|
46
113
|
if (actual.dimensions !== undefined && actual.dimensions !== expected.dimensions) {
|
|
47
114
|
const model = expected.model ? ` (${expected.model})` : "";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vector-schema.js","sourceRoot":"","sources":["../src/vector-schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH;;;;;GAKG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,6BAA6B,CAAC;AAE9D;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,mBAAmB,CAAC;AAE3D,0CAA0C;AAC1C,MAAM,CAAC,MAAM,6BAA6B,GAAG,wCAAwC,CAAC;AAEtF,gDAAgD;AAChD,MAAM,CAAC,MAAM,kCAAkC,GAAG,GAAG,CAAC;
|
|
1
|
+
{"version":3,"file":"vector-schema.js","sourceRoot":"","sources":["../src/vector-schema.ts"],"names":[],"mappings":"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"}
|
|
@@ -14,6 +14,7 @@ export * from "./parser.js";
|
|
|
14
14
|
export * from "./scanner.js";
|
|
15
15
|
export * from "./loader.js";
|
|
16
16
|
export * from "./vector-schema.js";
|
|
17
|
+
export * from "./sanitize.js";
|
|
17
18
|
export * from "./remote-source.js";
|
|
18
19
|
export * from "./skill-commands.js";
|
|
19
20
|
export * from "./skill-tool.js";
|
|
@@ -22,8 +22,13 @@ import {
|
|
|
22
22
|
LEGACY_SKILL_COLLECTION_MODEL,
|
|
23
23
|
SKILL_COLLECTION,
|
|
24
24
|
describeSkillSchemaMismatch,
|
|
25
|
+
skillPointPayload,
|
|
26
|
+
skillQdrantPoint,
|
|
27
|
+
stableHash,
|
|
28
|
+
type SkillPointPayload,
|
|
25
29
|
type SkillVectorSchema,
|
|
26
30
|
} from "./vector-schema.js";
|
|
31
|
+
import { skillPointName } from "./sanitize.js";
|
|
27
32
|
import {
|
|
28
33
|
describeRemoteFailure,
|
|
29
34
|
isStale,
|
|
@@ -39,11 +44,15 @@ import {
|
|
|
39
44
|
// At runtime, packages/ is resolved from the monorepo root or node_modules.
|
|
40
45
|
|
|
41
46
|
export interface QdrantSkillPoint {
|
|
47
|
+
/**
|
|
48
|
+
* The id of the point.
|
|
49
|
+
*
|
|
50
|
+
* It is the hash of the name in the payload, so the point id and the
|
|
51
|
+
* payload name always belong together.
|
|
52
|
+
*/
|
|
42
53
|
id: number;
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
body: string;
|
|
46
|
-
text: string;
|
|
54
|
+
/** The payload of the point. */
|
|
55
|
+
payload: SkillPointPayload;
|
|
47
56
|
}
|
|
48
57
|
|
|
49
58
|
export interface QdrantClientLike {
|
|
@@ -149,7 +158,7 @@ const RUNTIME_VECTOR_DISTANCE = "Cosine";
|
|
|
149
158
|
export interface SkillVectorPoint {
|
|
150
159
|
id: number;
|
|
151
160
|
vector: number[];
|
|
152
|
-
payload:
|
|
161
|
+
payload: SkillPointPayload;
|
|
153
162
|
}
|
|
154
163
|
|
|
155
164
|
/**
|
|
@@ -331,6 +340,15 @@ export class SkillRegistry {
|
|
|
331
340
|
* disk and how many it can reach through the vector store.
|
|
332
341
|
*/
|
|
333
342
|
private remoteSkills: Map<string, Skill> = new Map();
|
|
343
|
+
/**
|
|
344
|
+
* The local skills by their name in the shared collection.
|
|
345
|
+
*
|
|
346
|
+
* The name of a point is the SANITIZED name of the skill, because the
|
|
347
|
+
* collection is shared. A hit of the collection therefore carries a name
|
|
348
|
+
* that this machine does not use - the registry is keyed by the name on
|
|
349
|
+
* this machine. This map bridges the two.
|
|
350
|
+
*/
|
|
351
|
+
private _skillsByPointName: Map<string, Skill> = new Map();
|
|
334
352
|
/**
|
|
335
353
|
* The remote skills whose content is COMPLETE.
|
|
336
354
|
*
|
|
@@ -613,13 +631,17 @@ export class SkillRegistry {
|
|
|
613
631
|
|
|
614
632
|
/** The point of a skill in the vector index. */
|
|
615
633
|
private _toQdrantPoint(skill: Skill): QdrantSkillPoint {
|
|
616
|
-
|
|
617
|
-
|
|
634
|
+
// The payload and the point id come from the shared schema, so the
|
|
635
|
+
// runtime and `scripts/sync-to-qdrant.ts` store the same point for the
|
|
636
|
+
// same skill. A writer that used its own id would leave two points
|
|
637
|
+
// behind - or overwrite an unrelated one.
|
|
638
|
+
const payload = skillPointPayload({
|
|
618
639
|
name: skill.id,
|
|
640
|
+
title: skill.frontmatter.name,
|
|
619
641
|
description: skill.frontmatter.description,
|
|
620
|
-
body: skill.body
|
|
621
|
-
|
|
622
|
-
|
|
642
|
+
body: skill.body,
|
|
643
|
+
});
|
|
644
|
+
return skillQdrantPoint(payload);
|
|
623
645
|
}
|
|
624
646
|
|
|
625
647
|
/**
|
|
@@ -677,7 +699,7 @@ export class SkillRegistry {
|
|
|
677
699
|
*/
|
|
678
700
|
private _scheduleIndex(skill: Skill): void {
|
|
679
701
|
const point = this._toQdrantPoint(skill);
|
|
680
|
-
const hash =
|
|
702
|
+
const hash = stableHash(point.payload.text);
|
|
681
703
|
if (this._indexedHashes.get(skill.id) === hash) return;
|
|
682
704
|
|
|
683
705
|
this._indexedHashes.set(skill.id, hash);
|
|
@@ -729,12 +751,12 @@ export class SkillRegistry {
|
|
|
729
751
|
while (this._indexQueue.length > 0) {
|
|
730
752
|
const batch = this._indexQueue.splice(0, this._batchSize);
|
|
731
753
|
try {
|
|
732
|
-
const vectors = await indexer.embed(batch.map((item) => item.point.text));
|
|
754
|
+
const vectors = await indexer.embed(batch.map((item) => item.point.payload.text));
|
|
733
755
|
const points: SkillVectorPoint[] = [];
|
|
734
756
|
batch.forEach((item, position) => {
|
|
735
757
|
const vector = vectors[position];
|
|
736
758
|
if (!vector || vector.length === 0) return;
|
|
737
|
-
points.push({ id: item.point.id, vector, payload: item.point });
|
|
759
|
+
points.push({ id: item.point.id, vector, payload: item.point.payload });
|
|
738
760
|
});
|
|
739
761
|
if (points.length > 0) await indexer.upsert(points);
|
|
740
762
|
} catch {
|
|
@@ -749,20 +771,13 @@ export class SkillRegistry {
|
|
|
749
771
|
}
|
|
750
772
|
}
|
|
751
773
|
|
|
752
|
-
/** Simple deterministic ID for Qdrant (no bigints needed). */
|
|
753
|
-
private _hashId(id: string): number {
|
|
754
|
-
let h = 0;
|
|
755
|
-
for (let i = 0; i < id.length; i++) {
|
|
756
|
-
h = (Math.imul(31, h) + id.charCodeAt(i)) | 0;
|
|
757
|
-
}
|
|
758
|
-
return Math.abs(h);
|
|
759
|
-
}
|
|
760
|
-
|
|
761
774
|
/**
|
|
762
775
|
* Register a skill (auto-indexes to Qdrant if configured).
|
|
763
776
|
*/
|
|
764
777
|
register(skill: Skill): void {
|
|
765
778
|
this.skills.set(skill.id, skill);
|
|
779
|
+
// The shared collection knows the skill by its sanitized name.
|
|
780
|
+
this._skillsByPointName.set(skillPointName(skill.id), skill);
|
|
766
781
|
this.updateIndex(skill);
|
|
767
782
|
// Queue the skill for the vector index without blocking registration.
|
|
768
783
|
this._scheduleIndex(skill);
|
|
@@ -775,6 +790,12 @@ export class SkillRegistry {
|
|
|
775
790
|
const skill = this.skills.get(skillId);
|
|
776
791
|
if (skill) {
|
|
777
792
|
this.skills.delete(skillId);
|
|
793
|
+
// Only drop the shared name when it still points at this skill: two
|
|
794
|
+
// skills may share a sanitized name.
|
|
795
|
+
const pointName = skillPointName(skill.id);
|
|
796
|
+
if (this._skillsByPointName.get(pointName) === skill) {
|
|
797
|
+
this._skillsByPointName.delete(pointName);
|
|
798
|
+
}
|
|
778
799
|
this.index.entries = this.index.entries.filter(
|
|
779
800
|
(e) => e.id !== skillId
|
|
780
801
|
);
|
|
@@ -788,8 +809,13 @@ export class SkillRegistry {
|
|
|
788
809
|
*/
|
|
789
810
|
get(skillId: string): Skill | undefined {
|
|
790
811
|
// A server skill is only known after a vector search found it, so it is
|
|
791
|
-
// looked up second - the local skill always wins.
|
|
792
|
-
|
|
812
|
+
// looked up second - the local skill always wins. The shared name of a
|
|
813
|
+
// skill resolves to the local skill as well.
|
|
814
|
+
return (
|
|
815
|
+
this.skills.get(skillId) ??
|
|
816
|
+
this._skillsByPointName.get(skillId) ??
|
|
817
|
+
this.remoteSkills.get(skillId)
|
|
818
|
+
);
|
|
793
819
|
}
|
|
794
820
|
|
|
795
821
|
/**
|
|
@@ -1051,7 +1077,7 @@ export class SkillRegistry {
|
|
|
1051
1077
|
const payload = pt.payload ?? {};
|
|
1052
1078
|
const name = typeof payload.name === "string" ? payload.name : undefined;
|
|
1053
1079
|
if (!name) continue;
|
|
1054
|
-
const local = this.skills.get(name);
|
|
1080
|
+
const local = this._skillsByPointName.get(name) ?? this.skills.get(name);
|
|
1055
1081
|
if (local) {
|
|
1056
1082
|
results.push({ skill: local, score: pt.score, source: "local" });
|
|
1057
1083
|
continue;
|
|
@@ -1084,12 +1110,12 @@ export class SkillRegistry {
|
|
|
1084
1110
|
const batch = skills.slice(i, i + this._batchSize);
|
|
1085
1111
|
const points = batch.map((skill) => this._toQdrantPoint(skill));
|
|
1086
1112
|
try {
|
|
1087
|
-
const vectors = await indexer.embed(points.map((point) => point.text));
|
|
1113
|
+
const vectors = await indexer.embed(points.map((point) => point.payload.text));
|
|
1088
1114
|
const payload: SkillVectorPoint[] = [];
|
|
1089
1115
|
points.forEach((point, position) => {
|
|
1090
1116
|
const vector = vectors[position];
|
|
1091
1117
|
if (!vector || vector.length === 0) return;
|
|
1092
|
-
payload.push({ id: point.id, vector, payload: point });
|
|
1118
|
+
payload.push({ id: point.id, vector, payload: point.payload });
|
|
1093
1119
|
});
|
|
1094
1120
|
if (payload.length > 0) await indexer.upsert(payload);
|
|
1095
1121
|
} catch {
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The sanitization of a skill for the shared collection.
|
|
3
|
+
*
|
|
4
|
+
* The collection is shared, so a client name must not travel into it. The
|
|
5
|
+
* rules lived in `scripts/sync-to-qdrant.ts` - the only writer that could
|
|
6
|
+
* reach them. The runtime registry writes into the same collection, so it
|
|
7
|
+
* needs them too: a writer that skipped the rules would leave the raw
|
|
8
|
+
* name and the raw body of a client skill in a shared cluster.
|
|
9
|
+
*
|
|
10
|
+
* Both writers therefore build their points with the same rules, and the
|
|
11
|
+
* name of a point is the sanitized name. One skill, one point.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/** A rule that replaces a client specific text. */
|
|
15
|
+
interface SanitizationRule {
|
|
16
|
+
pattern: RegExp;
|
|
17
|
+
replacement: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* The patterns of the client specific names.
|
|
22
|
+
*
|
|
23
|
+
* The list is about the names of the clients and the projects of the
|
|
24
|
+
* users of the harness - not about the skills themselves.
|
|
25
|
+
*/
|
|
26
|
+
const SANITIZATION_RULES: SanitizationRule[] = [
|
|
27
|
+
// 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]" },
|
|
32
|
+
|
|
33
|
+
// 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
|
+
|
|
42
|
+
// Generic replacements
|
|
43
|
+
{ pattern: /Company Name/gi, replacement: "[Client Name]" },
|
|
44
|
+
{ pattern: /your company/gi, replacement: "[Client]" },
|
|
45
|
+
{ pattern: /our client/gi, replacement: "[Client]" },
|
|
46
|
+
{ pattern: /client-specific/gi, replacement: "[Project-specific]" },
|
|
47
|
+
];
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* The names that are renamed as a whole.
|
|
51
|
+
*
|
|
52
|
+
* A name that would be unreadable after the pattern rules - a name that
|
|
53
|
+
* carries the client name in its middle - is renamed here.
|
|
54
|
+
*/
|
|
55
|
+
const SKILL_NAME_MAP: Record<string, string> = {
|
|
56
|
+
"gse-insurance-step5-slot-filter": "insurance-project-step-filter",
|
|
57
|
+
"inpac-pe-approval-workflow": "client-a-pe-approval-workflow",
|
|
58
|
+
"inpac-po-approval-workflow": "client-a-po-approval-workflow",
|
|
59
|
+
"inpac-pr-approval-workflow": "client-a-pr-approval-workflow",
|
|
60
|
+
"tbs-import-clearance-lcv": "import-project-clearance",
|
|
61
|
+
whispertool: "whisper-tool",
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Replace the client specific names of a text.
|
|
66
|
+
*
|
|
67
|
+
* The text stays the same length, so a vector that was built from it
|
|
68
|
+
* keeps its shape.
|
|
69
|
+
*/
|
|
70
|
+
export function sanitizeText(text: string): string {
|
|
71
|
+
let result = text;
|
|
72
|
+
for (const rule of SANITIZATION_RULES) {
|
|
73
|
+
result = result.replace(rule.pattern, rule.replacement);
|
|
74
|
+
}
|
|
75
|
+
return result;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* The name of a skill in the shared collection.
|
|
80
|
+
*
|
|
81
|
+
* The name is the identity of the point, so it has to be the same for
|
|
82
|
+
* every writer: the map first, then the pattern rules.
|
|
83
|
+
*/
|
|
84
|
+
export function skillPointName(name: string): string {
|
|
85
|
+
return SKILL_NAME_MAP[name] || sanitizeText(name);
|
|
86
|
+
}
|