@tangle-network/agent-knowledge 1.2.0 → 1.4.0
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/AGENTS.md +36 -0
- package/LICENSE +43 -0
- package/README.md +155 -0
- package/dist/{chunk-TXNYP4WI.js → chunk-4PNXQ2NT.js} +1 -1
- package/dist/chunk-4PNXQ2NT.js.map +1 -0
- package/dist/{chunk-JLCQ6O7W.js → chunk-HKYD765Q.js} +574 -441
- package/dist/chunk-HKYD765Q.js.map +1 -0
- package/dist/chunk-WCYW2GDA.js +551 -0
- package/dist/chunk-WCYW2GDA.js.map +1 -0
- package/dist/chunk-YMKHCTS2.js +19 -0
- package/dist/chunk-YMKHCTS2.js.map +1 -0
- package/dist/cli.js +77 -34
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +686 -406
- package/dist/index.js +591 -168
- package/dist/index.js.map +1 -1
- package/dist/profiles/index.d.ts +194 -0
- package/dist/profiles/index.js +368 -0
- package/dist/profiles/index.js.map +1 -0
- package/dist/sources/index.d.ts +386 -0
- package/dist/sources/index.js +34 -0
- package/dist/sources/index.js.map +1 -0
- package/dist/{types-DTUp66Gr.d.ts → types-CAeh7Lwb.d.ts} +1 -1
- package/dist/viz/index.d.ts +1 -1
- package/dist/viz/index.js +1 -1
- package/package.json +22 -3
- package/dist/chunk-JLCQ6O7W.js.map +0 -1
- package/dist/chunk-TXNYP4WI.js.map +0 -1
package/AGENTS.md
CHANGED
|
@@ -58,3 +58,39 @@ Use `knowledgeReleaseReportFromOptimization()` before promotion. It projects opt
|
|
|
58
58
|
- Use `KbStore` for storage. Implement D1 in the consuming app when needed.
|
|
59
59
|
- Use `KnowledgeDiscoveryDispatcher` for research workers. Production apps should wire this to their own swarm/fleet runtime.
|
|
60
60
|
- Do not bypass `lint` or `validate` before using generated knowledge in an agent.
|
|
61
|
+
|
|
62
|
+
## Pluggable Sources + Freshness + Changes
|
|
63
|
+
|
|
64
|
+
Agents that need to stay current against external authorities should compose:
|
|
65
|
+
|
|
66
|
+
- `createCornellLiiSource({ selectors })` — US Code + Wex from law.cornell.edu.
|
|
67
|
+
- `createIrsPublicationsSource({ publications, revenueProcedures })` — IRS index + named pubs.
|
|
68
|
+
- `createStateSosSource({ state, baseUrl, entities })` — generic state SOS adapter.
|
|
69
|
+
|
|
70
|
+
Every fetch returns `KnowledgeFragment[]` with `provenance.verifiable` indicating whether the authority was successfully authenticated. Refuse to cite fragments with `verifiable: false`.
|
|
71
|
+
|
|
72
|
+
Track per-tenant freshness with `createFileSystemFreshnessStore({ root })` and re-fetch only when `stale({ workspaceId, sourceId, ttlMs })` returns true.
|
|
73
|
+
|
|
74
|
+
Diff snapshots with `detectChanges(prev, next)`. Each `KnowledgeChange` carries `affectedDimensions` — pass those to your eval scheduler to re-run only the relevant campaigns.
|
|
75
|
+
|
|
76
|
+
## Authorship
|
|
77
|
+
|
|
78
|
+
Do not add `Co-Authored-By:` trailers (or any other AI-attribution lines) to commits, PR descriptions, or other artifacts in this repo. Author = the human running the session. Applies to every contributor, including AI agents and subagents — do not include the default Claude Code template trailer.
|
|
79
|
+
|
|
80
|
+
## Comment & doc discipline (no historical narrative)
|
|
81
|
+
|
|
82
|
+
Comments describe **what the code does and why** — never what it used to do, what it replaced, which audit found a bug, or what the prior version looked like. History belongs in commit messages and PR descriptions, not the source tree.
|
|
83
|
+
|
|
84
|
+
- Bad: `// replaces the inline retry loop`, `// fix for the silent-zero bug`, `// the 2yr rewrite added this`, `// audit fix`
|
|
85
|
+
- Good: `// value: null when retries exhaust — callers must inspect succeeded`
|
|
86
|
+
|
|
87
|
+
Applies to docstrings, README sections, SKILL.md, AGENTS.md, CLAUDE.md — anywhere the source tree carries prose.
|
|
88
|
+
|
|
89
|
+
## No fallbacks. Fail loud.
|
|
90
|
+
|
|
91
|
+
Sloppy fallbacks corrupt every signal downstream. No silent zeros, no `?? default` on required fields, no `try/catch { return null }` that erases diagnostic info, no legacy back-compat mode defaulted on for new code.
|
|
92
|
+
|
|
93
|
+
External-boundary calls (LLM, network, FS, subprocess) return *typed outcomes* (`{ succeeded, value, error }`). Callers MUST inspect `succeeded` before using `value`. Named, opted-in fallback rotations (`policy.fallbackModels: [...]`) are fine; deep `?? "kimi"` helpers are not.
|
|
94
|
+
|
|
95
|
+
Full doctrine: `~/dotfiles/claude/AGENTS.md` → "No fallbacks. Fail loud."
|
|
96
|
+
|
package/LICENSE
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Tangle Network
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
*** Add File: /home/drew/worktrees/publish-open-source/modal-inference-blueprint/LICENSE-MIT
|
|
23
|
+
MIT License
|
|
24
|
+
|
|
25
|
+
Copyright (c) 2026 Tangle Network
|
|
26
|
+
|
|
27
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
28
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
29
|
+
in the Software without restriction, including without limitation the rights
|
|
30
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
31
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
32
|
+
furnished to do so, subject to the following conditions:
|
|
33
|
+
|
|
34
|
+
The above copyright notice and this permission notice shall be included in all
|
|
35
|
+
copies or substantial portions of the Software.
|
|
36
|
+
|
|
37
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
38
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
39
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
40
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
41
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
42
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
43
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -198,3 +198,158 @@ await runAgentControlLoop({
|
|
|
198
198
|
},
|
|
199
199
|
})
|
|
200
200
|
```
|
|
201
|
+
|
|
202
|
+
## Researcher profile
|
|
203
|
+
|
|
204
|
+
`@tangle-network/agent-knowledge/profiles` ships a sandbox-SDK
|
|
205
|
+
`AgentProfile` preset for source-grounded research agents. Pairs with
|
|
206
|
+
`runLoop` from `@tangle-network/agent-runtime/loops` — the profile owns
|
|
207
|
+
the prompt + output adapter + validator; the kernel owns iteration,
|
|
208
|
+
concurrency, cost, and trace emission.
|
|
209
|
+
|
|
210
|
+
```ts
|
|
211
|
+
import { runLoop } from '@tangle-network/agent-runtime/loops'
|
|
212
|
+
import { multiHarnessResearcherFanout } from '@tangle-network/agent-knowledge/profiles'
|
|
213
|
+
|
|
214
|
+
const research = multiHarnessResearcherFanout({
|
|
215
|
+
harnesses: ['opencode/zai-coding-plan/glm-5.1', 'claude-code', 'codex'],
|
|
216
|
+
})
|
|
217
|
+
|
|
218
|
+
const result = await runLoop({
|
|
219
|
+
driver: research.driver,
|
|
220
|
+
agentRuns: research.agentRuns,
|
|
221
|
+
output: research.output,
|
|
222
|
+
validator: research.validator,
|
|
223
|
+
task: {
|
|
224
|
+
question: 'What content does cpg-founder ICP engage with on Twitter?',
|
|
225
|
+
knowledgeNamespace: 'cust_42',
|
|
226
|
+
sources: ['twitter', 'web'],
|
|
227
|
+
maxItems: 20,
|
|
228
|
+
minConfidence: 0.6,
|
|
229
|
+
},
|
|
230
|
+
ctx: { sandboxClient },
|
|
231
|
+
})
|
|
232
|
+
|
|
233
|
+
if (result.winner?.verdict?.valid) {
|
|
234
|
+
// result.winner.output.proposedWrites: KnowledgeUpdate[]
|
|
235
|
+
// The profile does NOT materialize. Decide whether to apply.
|
|
236
|
+
for (const write of result.winner.output.proposedWrites) {
|
|
237
|
+
// route through applyKnowledgeWriteBlocks / a KbStore put when ready
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
Three invariants are enforced by the validator:
|
|
243
|
+
|
|
244
|
+
- **Namespace isolation** — every `KnowledgeItem` + `KnowledgeUpdate`
|
|
245
|
+
must carry `task.knowledgeNamespace`. Cross-tenant writes hard-fail.
|
|
246
|
+
- **Provenance** — every item carries at least one evidence entry.
|
|
247
|
+
- **Citation density** — quotes-with-source / items >= 0.7 by default.
|
|
248
|
+
|
|
249
|
+
Validator scoring (default; overridable):
|
|
250
|
+
|
|
251
|
+
```
|
|
252
|
+
score = 0.4 · citation_density
|
|
253
|
+
+ 0.2 · source_diversity
|
|
254
|
+
+ 0.2 · recency_match
|
|
255
|
+
+ 0.2 · gap_coverage
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
The output preserves agent intelligence — `items`, `citations`,
|
|
259
|
+
`proposedWrites` are typed; `gaps`, `notes`, and any extras the agent
|
|
260
|
+
emitted land in `raw` rather than getting dropped.
|
|
261
|
+
|
|
262
|
+
## Pluggable Knowledge Sources
|
|
263
|
+
|
|
264
|
+
Static knowledge rots. Authorities like Cornell LII, the IRS, and state
|
|
265
|
+
Secretaries of State change without warning — a ruling vacates an FTC
|
|
266
|
+
non-compete rule, a CFR section renumbers, a state replaces Beverly-Killea
|
|
267
|
+
with RULLCA. The `@tangle-network/agent-knowledge/sources` subpath ships
|
|
268
|
+
three primitives that bridge "live authority" → "eval re-runs":
|
|
269
|
+
|
|
270
|
+
- `KnowledgeSource` — pluggable contract (`fetch(opts) → KnowledgeFragment[]`).
|
|
271
|
+
Every fragment carries `provenance` (URL, source-attested timestamp,
|
|
272
|
+
jurisdiction, `verifiable` flag) and `dimensionHints` (which eval
|
|
273
|
+
dimensions a change in this fragment should re-score).
|
|
274
|
+
- `KnowledgeFreshnessStore` — per-`(workspaceId, sourceId)` last-refresh
|
|
275
|
+
tracker. Filesystem adapter ships in-package; D1 / Postgres adapter
|
|
276
|
+
scaffold is shipped as `createD1FreshnessStoreStub(adapter)`.
|
|
277
|
+
- `detectChanges(prev, next)` — diffs two fragment snapshots, emits
|
|
278
|
+
`KnowledgeChange[]` tagged with the affected eval dimensions so a cron
|
|
279
|
+
scheduler knows exactly which campaigns to re-run.
|
|
280
|
+
|
|
281
|
+
Three concrete sources ship in-package:
|
|
282
|
+
|
|
283
|
+
```ts
|
|
284
|
+
import {
|
|
285
|
+
createCornellLiiSource,
|
|
286
|
+
createIrsPublicationsSource,
|
|
287
|
+
createStateSosSource,
|
|
288
|
+
createFileSystemFreshnessStore,
|
|
289
|
+
detectChanges,
|
|
290
|
+
type KnowledgeChange,
|
|
291
|
+
type KnowledgeFragment,
|
|
292
|
+
} from '@tangle-network/agent-knowledge'
|
|
293
|
+
|
|
294
|
+
const sources = [
|
|
295
|
+
// Federal statutes + Wex encyclopedia from law.cornell.edu.
|
|
296
|
+
createCornellLiiSource({
|
|
297
|
+
selectors: [
|
|
298
|
+
{ kind: 'uscode', path: '18/1836' }, // DTSA
|
|
299
|
+
{ kind: 'wex', path: 'restraint_of_trade', dimensionHints: ['jurisdictional_accuracy'] },
|
|
300
|
+
],
|
|
301
|
+
}),
|
|
302
|
+
// IRS publications index + named publications + revenue procedures.
|
|
303
|
+
createIrsPublicationsSource({
|
|
304
|
+
publications: ['p15', 'p17', 'p463'],
|
|
305
|
+
revenueProcedures: [],
|
|
306
|
+
}),
|
|
307
|
+
// Generic state SOS adapter — one config per state you need tracked.
|
|
308
|
+
createStateSosSource({
|
|
309
|
+
state: 'CA',
|
|
310
|
+
baseUrl: 'https://www.sos.ca.gov',
|
|
311
|
+
entities: [{
|
|
312
|
+
id: 'business-entities-forms',
|
|
313
|
+
path: '/business-programs/business-entities/forms',
|
|
314
|
+
title: 'CA Business Entities Forms',
|
|
315
|
+
selector: { kind: 'whole' },
|
|
316
|
+
}],
|
|
317
|
+
}),
|
|
318
|
+
]
|
|
319
|
+
|
|
320
|
+
const freshness = createFileSystemFreshnessStore({ root: './kb' })
|
|
321
|
+
|
|
322
|
+
// Worked example: Cornell LII updates the Wex `restraint_of_trade` entry
|
|
323
|
+
// to reflect Ryan-LLC v. FTC. The cron tick below detects the change,
|
|
324
|
+
// extracts the `jurisdictional_accuracy` dimension hint, and hands it to
|
|
325
|
+
// the eval scheduler which re-runs only the campaigns tagged with that
|
|
326
|
+
// dimension.
|
|
327
|
+
async function tick({ workspaceId, prevSnapshots }: {
|
|
328
|
+
workspaceId: string
|
|
329
|
+
prevSnapshots: Record<string, KnowledgeFragment[]>
|
|
330
|
+
}): Promise<KnowledgeChange[]> {
|
|
331
|
+
const allChanges: KnowledgeChange[] = []
|
|
332
|
+
for (const source of sources) {
|
|
333
|
+
const stale = await freshness.stale({
|
|
334
|
+
workspaceId,
|
|
335
|
+
sourceId: source.id,
|
|
336
|
+
ttlMs: 24 * 60 * 60 * 1000,
|
|
337
|
+
})
|
|
338
|
+
if (!stale) continue
|
|
339
|
+
|
|
340
|
+
const next = await source.fetch({ cacheDir: './.agent-knowledge/http-cache' })
|
|
341
|
+
const prev = prevSnapshots[source.id] ?? []
|
|
342
|
+
const { changes } = detectChanges(prev, next)
|
|
343
|
+
allChanges.push(...changes)
|
|
344
|
+
|
|
345
|
+
await freshness.mark({ workspaceId, sourceId: source.id, when: new Date() })
|
|
346
|
+
prevSnapshots[source.id] = next
|
|
347
|
+
}
|
|
348
|
+
return allChanges
|
|
349
|
+
}
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
Polite-by-default: every HTTP fetch carries the package User-Agent, is
|
|
353
|
+
throttled to 1 req/sec/origin, caches successful responses to disk, and
|
|
354
|
+
marks `verifiable: false` on block pages / 4xx rather than promoting
|
|
355
|
+
un-grounded content. See `src/sources/http.ts` for the invariants.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/viz/index.ts"],"sourcesContent":["import type { KnowledgeGraph, KnowledgeGraphEdge, KnowledgeGraphNode } from '../types'\n\nexport interface KnowledgeVizNode extends KnowledgeGraphNode {\n degree: number\n community: number\n}\n\nexport interface KnowledgeVizEdge extends KnowledgeGraphEdge {\n id: string\n}\n\nexport interface KnowledgeCommunity {\n id: number\n nodeIds: string[]\n topTitles: string[]\n cohesion: number\n}\n\nexport interface KnowledgeVizGraph {\n nodes: KnowledgeVizNode[]\n edges: KnowledgeVizEdge[]\n communities: KnowledgeCommunity[]\n}\n\nexport interface KnowledgeGap {\n type: 'isolated-node' | 'sparse-community' | 'bridge-node'\n title: string\n nodeIds: string[]\n suggestion: string\n}\n\nexport interface SurprisingConnection {\n source: KnowledgeVizNode\n target: KnowledgeVizNode\n score: number\n reasons: string[]\n}\n\nexport function toKnowledgeVizGraph(graph: KnowledgeGraph): KnowledgeVizGraph {\n const adjacency = buildAdjacency(graph)\n const communities = assignCommunities(graph.nodes, adjacency)\n const communityByNode = new Map<number, number>()\n communities.forEach((community) => {\n for (const nodeId of community.nodeIds) communityByNode.set(hashNode(nodeId), community.id)\n })\n const nodes = graph.nodes.map((node) => ({\n ...node,\n degree: node.inDegree + node.outDegree,\n community: communityByNode.get(hashNode(node.id)) ?? 0,\n }))\n return {\n nodes,\n edges: graph.edges.map((edge) => ({ ...edge, id: `${edge.source}->${edge.target}` })),\n communities,\n }\n}\n\nexport function detectKnowledgeGaps(graph: KnowledgeVizGraph, limit = 10): KnowledgeGap[] {\n const gaps: KnowledgeGap[] = []\n const isolated = graph.nodes.filter((node) => node.degree <= 1 && !isStructural(node.path))\n if (isolated.length > 0) {\n gaps.push({\n type: 'isolated-node',\n title: `${isolated.length} isolated page${isolated.length === 1 ? '' : 's'}`,\n nodeIds: isolated.map((node) => node.id),\n suggestion:\n 'Add cross-links, sources, or follow-up research to connect these pages to the knowledge graph.',\n })\n }\n for (const community of graph.communities) {\n if (community.nodeIds.length >= 3 && community.cohesion < 0.15) {\n gaps.push({\n type: 'sparse-community',\n title: `Sparse cluster: ${community.topTitles[0] ?? `community ${community.id}`}`,\n nodeIds: community.nodeIds,\n suggestion:\n 'This cluster has weak internal evidence. Add synthesis pages or relation links between its strongest concepts.',\n })\n }\n }\n const byCommunityNeighbors = new Map<string, Set<number>>()\n const nodeById = new Map(graph.nodes.map((node) => [node.id, node]))\n for (const edge of graph.edges) {\n const source = nodeById.get(edge.source)\n const target = nodeById.get(edge.target)\n if (!source || !target) continue\n if (!byCommunityNeighbors.has(source.id)) byCommunityNeighbors.set(source.id, new Set())\n if (!byCommunityNeighbors.has(target.id)) byCommunityNeighbors.set(target.id, new Set())\n byCommunityNeighbors.get(source.id)!.add(target.community)\n byCommunityNeighbors.get(target.id)!.add(source.community)\n }\n for (const node of graph.nodes) {\n if ((byCommunityNeighbors.get(node.id)?.size ?? 0) >= 3) {\n gaps.push({\n type: 'bridge-node',\n title: `Bridge page: ${node.title}`,\n nodeIds: [node.id],\n suggestion: 'This page connects multiple knowledge areas. Keep it well cited and current.',\n })\n }\n }\n return gaps.slice(0, limit)\n}\n\nexport function findSurprisingConnections(\n graph: KnowledgeVizGraph,\n limit = 10,\n): SurprisingConnection[] {\n const nodeById = new Map(graph.nodes.map((node) => [node.id, node]))\n const scored: SurprisingConnection[] = []\n for (const edge of graph.edges) {\n const source = nodeById.get(edge.source)\n const target = nodeById.get(edge.target)\n if (!source || !target) continue\n let score = edge.weight\n const reasons = [...edge.reasons]\n if (source.community !== target.community) {\n score += 3\n reasons.push('cross-community')\n }\n if (source.tags.some((tag) => !target.tags.includes(tag))) {\n score += 1\n reasons.push('cross-tag')\n }\n if (score >= 3) scored.push({ source, target, score, reasons })\n }\n return scored.sort((a, b) => b.score - a.score).slice(0, limit)\n}\n\nfunction buildAdjacency(graph: KnowledgeGraph): Map<string, Set<string>> {\n const out = new Map<string, Set<string>>()\n for (const node of graph.nodes) out.set(node.id, new Set())\n for (const edge of graph.edges) {\n out.get(edge.source)?.add(edge.target)\n out.get(edge.target)?.add(edge.source)\n }\n return out\n}\n\nfunction assignCommunities(\n nodes: KnowledgeGraphNode[],\n adjacency: Map<string, Set<string>>,\n): KnowledgeCommunity[] {\n const seen = new Set<string>()\n const communities: KnowledgeCommunity[] = []\n for (const node of nodes) {\n if (seen.has(node.id)) continue\n const queue = [node.id]\n const ids: string[] = []\n seen.add(node.id)\n while (queue.length > 0) {\n const id = queue.shift()!\n ids.push(id)\n for (const next of adjacency.get(id) ?? []) {\n if (!seen.has(next)) {\n seen.add(next)\n queue.push(next)\n }\n }\n }\n const memberNodes = ids\n .map((id) => nodes.find((candidate) => candidate.id === id))\n .filter((item): item is KnowledgeGraphNode => Boolean(item))\n communities.push({\n id: communities.length,\n nodeIds: ids,\n topTitles: memberNodes\n .sort((a, b) => b.inDegree + b.outDegree - (a.inDegree + a.outDegree))\n .slice(0, 5)\n .map((item) => item.title),\n cohesion: cohesion(ids, adjacency),\n })\n }\n return communities.sort((a, b) => b.nodeIds.length - a.nodeIds.length)\n}\n\nfunction cohesion(ids: string[], adjacency: Map<string, Set<string>>): number {\n if (ids.length < 2) return 0\n let edges = 0\n const idSet = new Set(ids)\n for (const id of ids) {\n for (const next of adjacency.get(id) ?? []) {\n if (idSet.has(next)) edges++\n }\n }\n return edges / (ids.length * (ids.length - 1))\n}\n\nfunction hashNode(id: string): number {\n let hash = 0\n for (const char of id) hash = (hash * 31 + char.charCodeAt(0)) | 0\n return hash\n}\n\nfunction isStructural(path: string): boolean {\n return path.endsWith('/index.md') || path.endsWith('/log.md')\n}\n"],"mappings":"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","names":[]}
|