dsh-paper-search 0.1.0 → 0.1.2
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/CHANGELOG.md +72 -1
- package/README.md +27 -0
- package/lib/index.js +27 -0
- package/lib/sources.js +48 -18
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,13 +4,84 @@
|
|
|
4
4
|
|
|
5
5
|
| dsh-paper-search | DeepSeek Harness | Diuji pada |
|
|
6
6
|
|---|---|---|
|
|
7
|
-
| 0.1.
|
|
7
|
+
| 0.1.2 | 0.1.7-rc.2 | 28 Sep 2026, Windows, Node 24 |
|
|
8
|
+
| ~~0.1.0~~ | — | **JANGAN DIPAKAI — plugin gagal dimuat, lihat 0.1.1** |
|
|
8
9
|
|
|
9
10
|
Paket ini tidak memakai API internal DSH selain `ctx.tools.register` dan
|
|
10
11
|
`ctx.get('skills').registerProvider` — keduanya sudah dipakai plugin lain yang
|
|
11
12
|
berjalan di profil ini (`dsh-search-cascade`), jadi titik patahnya sama dan sudah
|
|
12
13
|
diketahui.
|
|
13
14
|
|
|
15
|
+
## [0.1.2] - 2026-09-28
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
|
|
19
|
+
- **Judul Garuda masih bisa terduplikasi.** Perbaikan di rilis sebelumnya baru
|
|
20
|
+
menangani pengulangan **persis** (`X X X`). Setelah mengukur **50 judul
|
|
21
|
+
sungguhan** (5 kueri, diambil apa adanya dari halaman Garuda), ternyata ada
|
|
22
|
+
**empat** bentuk pengulangan, bukan satu:
|
|
23
|
+
|
|
24
|
+
| Bentuk | Contoh | Panjang |
|
|
25
|
+
|---|---|---|
|
|
26
|
+
| A — periode persis | `X X X X X X` | 255 → 43 |
|
|
27
|
+
| B — diulang sesudah `": "` | `X: X` | 194 → 96 |
|
|
28
|
+
| C — awalan lalu `X: X` | `NLP, Machine Learning X: X` | 270 → 145 |
|
|
29
|
+
| D — frasa diulang di awal | `IMPLEMENTASI PENDIDIKAN KARAKTER IMPLEMENTASI …` | 306 → 295 |
|
|
30
|
+
|
|
31
|
+
`runtuhkanPeriodik()` diganti **`runtuhkanJudul()`** yang menangani keempatnya,
|
|
32
|
+
termasuk pengulangan dengan **kapitalisasi berbeda** (252 → 125) — Garuda
|
|
33
|
+
memang sering mengulang judul dalam huruf besar lalu huruf kecil.
|
|
34
|
+
|
|
35
|
+
**Yang paling penting, dan inilah yang diuji:** judul **sah** berformat
|
|
36
|
+
`Judul: subjudul`, `Judul: (studi kasus)`, dan `Judul: kata kunci` **tidak**
|
|
37
|
+
ikut terpotong. Karena itu aturannya menuntut bagian sesudah `": "` benar-benar
|
|
38
|
+
**sama** dengan akhiran bagian sebelumnya — bukan sekadar ada titik dua. Pada
|
|
39
|
+
50 judul itu hasilnya: **14 diringkas, 36 dibiarkan, 0 cacat**, dan **15 judul
|
|
40
|
+
bertitik dua tetap utuh**.
|
|
41
|
+
|
|
42
|
+
### Added
|
|
43
|
+
|
|
44
|
+
- **11 asersi regresi baru** di `test/uji-sumber.mjs`, memakai teks yang diambil
|
|
45
|
+
apa adanya dari Garuda. Lima di antaranya justru menjaga agar judul **sah**
|
|
46
|
+
tidak terpotong — tanpa itu, aturannya bisa "lulus" hanya dengan memotong
|
|
47
|
+
setiap judul yang bertitik dua.
|
|
48
|
+
|
|
49
|
+
## [0.1.1] - 2026-09-28
|
|
50
|
+
|
|
51
|
+
### Fixed
|
|
52
|
+
|
|
53
|
+
- **Plugin gagal dimuat sama sekali — perbaikan yang wajib.** DSH mewajibkan
|
|
54
|
+
setiap tool mendeklarasikan `output: { schema, render }`. Versi 0.1.0 tidak
|
|
55
|
+
punya itu, sehingga `ctx.tools.register` **melempar**:
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
TypeError: tool "paper_search" must declare output { schema, render, presentationMeta? }
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Karena lemparan itu terjadi di dalam `apply()`, **seluruh plugin batal
|
|
62
|
+
dimuat** — kedua tool **dan** skill-nya tidak pernah terdaftar. Pengguna 0.1.0
|
|
63
|
+
tidak akan melihat `paper_search` sama sekali.
|
|
64
|
+
|
|
65
|
+
Ditemukan lewat **uji boot DSH sungguhan**, bukan lewat uji `ctx` tiruan:
|
|
66
|
+
`register` tiruan hanya menampung definisinya, jadi tidak pernah menegakkan
|
|
67
|
+
syarat ini. Inilah bukti bahwa tiruan tidak bisa menggantikan harness nyata.
|
|
68
|
+
|
|
69
|
+
**Kalau Anda memasang 0.1.0, naikkan ke 0.1.1.**
|
|
70
|
+
|
|
71
|
+
### Added
|
|
72
|
+
|
|
73
|
+
- `test/uji-di-dsh.mjs` — **uji boot DSH sungguhan.** Menyalin profil `headless`
|
|
74
|
+
ke profil uji (profil `desktop` yang hidup **tidak disentuh**), menyisipkan
|
|
75
|
+
plugin ini plus satu **plugin saksi**, menjalankan DSH nyata, lalu memeriksa
|
|
76
|
+
dari DALAM bahwa: kedua tool terdaftar, provider skill terdaftar, skill
|
|
77
|
+
muncul di katalog, dan `paper_search` benar-benar dipanggil sampai
|
|
78
|
+
mengembalikan paper sungguhan. **22 asersi, 0 gagal.**
|
|
79
|
+
|
|
80
|
+
Uji ini juga mencatat dua jebakan yang sudah terukur, supaya tidak diulang:
|
|
81
|
+
tanpa `inject` service tidak terlihat dari plugin lain, dan pada detik ke-4
|
|
82
|
+
harness sudah membongkar pluginnya sehingga pemeriksaan **harus** berjalan di
|
|
83
|
+
dalam `apply()`.
|
|
84
|
+
|
|
14
85
|
## [0.1.0] - 2026-09-28
|
|
15
86
|
|
|
16
87
|
Rilis pertama.
|
package/README.md
CHANGED
|
@@ -97,6 +97,33 @@ For Indonesian topics, **use Indonesian keywords**: `"pendidikan karakter"`
|
|
|
97
97
|
returns real Indonesian journals, while the English equivalent returns
|
|
98
98
|
international results that do not match the intent.
|
|
99
99
|
|
|
100
|
+
## Verification status
|
|
101
|
+
|
|
102
|
+
Stated precisely, because "tested" on its own is not a useful claim:
|
|
103
|
+
|
|
104
|
+
**Verified — the connectors, against live APIs and pages.** `node test/uji-sumber.mjs`
|
|
105
|
+
runs all sixteen against the real services: 8/8 assertions. On the last run 15 of
|
|
106
|
+
16 answered. The one that did not was PubMed Central returning a transient
|
|
107
|
+
`HTTP 500 error forwarding request` — and it was reported as a failure, which is
|
|
108
|
+
the behaviour this package exists to provide.
|
|
109
|
+
|
|
110
|
+
**Verified — the wiring, against a mock Cordis context.** `node test/uji-plugin.mjs`,
|
|
111
|
+
29/29 assertions: both tools register, the skill provider reports rank 450,
|
|
112
|
+
the frontmatter parses within the 500-character catalogue limit, and both tools
|
|
113
|
+
reach the network end to end.
|
|
114
|
+
|
|
115
|
+
**Verified — loading the bundle inside a live harness.** `node test/uji-di-dsh.mjs`
|
|
116
|
+
copies the `headless` profile to a throwaway test profile, inserts this plugin
|
|
117
|
+
plus a witness plugin, boots a real DSH, and checks **from inside** that both
|
|
118
|
+
tools are registered, the skill provider is registered, the skill appears in the
|
|
119
|
+
catalogue, and `paper_search` is actually called and returns real papers.
|
|
120
|
+
**22 assertions, 0 failures.**
|
|
121
|
+
|
|
122
|
+
That last test earned its place: it caught a fatal bug the mock never could.
|
|
123
|
+
DSH requires every tool to declare `output: { schema, render }`, and 0.1.0 did
|
|
124
|
+
not — so registration threw, and because the throw happened inside `apply()`,
|
|
125
|
+
the whole plugin failed to load. **0.1.0 is unusable; use 0.1.1.**
|
|
126
|
+
|
|
100
127
|
## What makes the reporting honest
|
|
101
128
|
|
|
102
129
|
Every result separates **sources that failed** from sources that answered:
|
package/lib/index.js
CHANGED
|
@@ -188,6 +188,31 @@ function formatHasil(hasil) {
|
|
|
188
188
|
|
|
189
189
|
// ── Tool ─────────────────────────────────────────────────────────────────────
|
|
190
190
|
|
|
191
|
+
/**
|
|
192
|
+
* Bentuk `output` yang DIWAJIBKAN DSH untuk setiap tool.
|
|
193
|
+
*
|
|
194
|
+
* Ditemukan lewat uji boot sungguhan (`test/uji-di-dsh.mjs`) — dan ini bug yang
|
|
195
|
+
* lolos dari `ctx` tiruan, karena `register` tiruan hanya menampung definisinya.
|
|
196
|
+
* DSH melempar:
|
|
197
|
+
*
|
|
198
|
+
* TypeError: tool "paper_search" must declare output { schema, render,
|
|
199
|
+
* presentationMeta? }
|
|
200
|
+
*
|
|
201
|
+
* Karena lemparan itu terjadi di dalam `apply()`, SELURUH plugin gagal dimuat —
|
|
202
|
+
* tool maupun skill-nya sama sekali tidak terdaftar.
|
|
203
|
+
*
|
|
204
|
+
* Kedua tool di sini mengembalikan TEKS yang sudah diformat, jadi `schema`
|
|
205
|
+
* menyatakan string dan `render` membungkusnya menjadi blok konten — bentuk
|
|
206
|
+
* yang sama dengan `txt()` milik `dsh-search-cascade`
|
|
207
|
+
* (`[{ type: 'text', text: '...' }]`).
|
|
208
|
+
*/
|
|
209
|
+
function keluaranTeks(deskripsi) {
|
|
210
|
+
return {
|
|
211
|
+
schema: { type: 'string', description: deskripsi },
|
|
212
|
+
render: (_args, value) => [{ type: 'text', text: String(value ?? '') }],
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
191
216
|
function toolSearch(kredensial) {
|
|
192
217
|
return {
|
|
193
218
|
name: 'paper_search',
|
|
@@ -207,6 +232,7 @@ function toolSearch(kredensial) {
|
|
|
207
232
|
},
|
|
208
233
|
required: ['query'],
|
|
209
234
|
},
|
|
235
|
+
output: keluaranTeks('Laporan teks: query, jumlah sumber yang menjawab, jumlah paper setelah dedup, baris status per sumber (OK/GAGAL), lalu daftar paper.'),
|
|
210
236
|
execute: async (args) => {
|
|
211
237
|
const hasil = await jalankanPencarian(args ?? {}, kredensial);
|
|
212
238
|
return formatHasil(hasil);
|
|
@@ -229,6 +255,7 @@ function toolStatus(kredensial) {
|
|
|
229
255
|
},
|
|
230
256
|
required: [],
|
|
231
257
|
},
|
|
258
|
+
output: keluaranTeks('Laporan teks: daftar kredensial yang terbaca beserta asalnya, baris status tiap sumber (OK/GAGAL beserta waktu respons), dan catatan tambahan.'),
|
|
232
259
|
execute: async (args) => {
|
|
233
260
|
const probe = args?.probe !== false;
|
|
234
261
|
const barisPilihan = pilihSumber(args?.sources);
|
package/lib/sources.js
CHANGED
|
@@ -74,26 +74,56 @@ function normDoi(doi) {
|
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
/**
|
|
77
|
-
*
|
|
78
|
-
* mengisi lebar tampilan.
|
|
79
|
-
*
|
|
80
|
-
* kali dalam satu <xmp> sehingga panjangnya 255 huruf.
|
|
77
|
+
* Garuda menulis judul di dalam elemen <xmp> dengan BEBERAPA bentuk pengulangan,
|
|
78
|
+
* dan mengulanginya untuk mengisi lebar tampilan. Keempatnya diukur 28 Sep 2026
|
|
79
|
+
* dari **50 judul sungguhan** (5 kueri):
|
|
81
80
|
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
*
|
|
81
|
+
* A. periode persis "X X X X X X" (255 huruf)
|
|
82
|
+
* B. diulang sesudah ": " "X: X" (194 huruf)
|
|
83
|
+
* C. awalan lalu "X: X" "NLP, Machine Learning X: X" (270 huruf)
|
|
84
|
+
* D. frasa diulang di awal "IMPLEMENTASI PENDIDIKAN KARAKTER IMPLEMENTASI …"
|
|
85
|
+
*
|
|
86
|
+
* Yang TIDAK boleh disentuh — dan sudah diperiksa pada 50 sampel itu: judul SAH
|
|
87
|
+
* berformat "Judul: subjudul", "Judul: (studi kasus)", dan "Judul: kata kunci".
|
|
88
|
+
* Karena itu aturan (c) menuntut bagian sesudah ": " benar-benar SAMA dengan
|
|
89
|
+
* akhiran bagian sebelumnya — bukan sekadar ada titik dua. Pada 50 sampel,
|
|
90
|
+
* tidak satu pun judul sah ikut terpotong.
|
|
91
|
+
*
|
|
92
|
+
* Sengaja konservatif: lebih baik judul berlebihan daripada judul terpotong salah.
|
|
86
93
|
*/
|
|
87
|
-
function
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
94
|
+
export function runtuhkanJudul(s) {
|
|
95
|
+
let t = (s ?? '').trim();
|
|
96
|
+
for (let putaran = 0; putaran < 4; putaran++) {
|
|
97
|
+
const sebelum = t;
|
|
98
|
+
|
|
99
|
+
// (A) periode persis: cari periode terkecil, sisakan satu salinan.
|
|
100
|
+
const n = t.length;
|
|
101
|
+
for (let p = 10; p <= Math.floor(n / 2); p++) {
|
|
102
|
+
let cocok = true;
|
|
103
|
+
for (let i = p; i < n; i++) {
|
|
104
|
+
if (t[i] !== t[i % p]) { cocok = false; break; }
|
|
105
|
+
}
|
|
106
|
+
if (cocok) { t = t.slice(0, p).trim(); break; }
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// (D) frasa yang diulang di awal: "A A sisa" -> "A sisa".
|
|
110
|
+
// `A` harus berakhir di batas kata, supaya tidak memotong di tengah kata.
|
|
111
|
+
for (let p = 8; p <= Math.floor(t.length / 2); p++) {
|
|
112
|
+
const a = t.slice(0, p);
|
|
113
|
+
if (!/[\s.)\]]$/.test(a)) continue;
|
|
114
|
+
if (t.startsWith(a + a)) { t = (a + t.slice(2 * p)).trim(); break; }
|
|
95
115
|
}
|
|
96
|
-
|
|
116
|
+
|
|
117
|
+
// (B)/(C) diulang sesudah ": " terakhir — dibandingkan TANPA peduli huruf
|
|
118
|
+
// besar/kecil, karena Garuda sering mengulang judul dengan kapitalisasi beda.
|
|
119
|
+
const pisah = t.lastIndexOf(': ');
|
|
120
|
+
if (pisah > 0) {
|
|
121
|
+
const ekor = t.slice(pisah + 2).trim();
|
|
122
|
+
const kepala = t.slice(0, pisah).trim();
|
|
123
|
+
if (ekor.length >= 20 && kepala.toLowerCase().endsWith(ekor.toLowerCase())) t = kepala;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (t === sebelum) break;
|
|
97
127
|
}
|
|
98
128
|
return t;
|
|
99
129
|
}
|
|
@@ -101,7 +131,7 @@ function runtuhkanPeriodik(s) {
|
|
|
101
131
|
function buatPaper(p) {
|
|
102
132
|
const doi = normDoi(p.doi);
|
|
103
133
|
return {
|
|
104
|
-
title: potong(
|
|
134
|
+
title: potong(runtuhkanJudul(bersihTeks(p.title) ?? ''), 300),
|
|
105
135
|
authors: Array.isArray(p.authors) ? p.authors.map(bersihTeks).filter(Boolean).slice(0, 15) : [],
|
|
106
136
|
year: Number.isFinite(Number(p.year)) && Number(p.year) > 1500 ? Number(p.year) : null,
|
|
107
137
|
source: p.source,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-paper-search",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Literature search for DeepSeek Harness: thirteen international sources (Crossref, OpenAlex, DOAJ, Europe PMC, PubMed, PubMed Central, arXiv, Semantic Scholar, CORE, Zenodo, HAL, IACR ePrint, Unpaywall) and three Indonesian journal portals (Garuda, Sinta, IOS OneSearch), all native HTTP with no external CLI required, plus a status tool that reports which sources answer and which credentials are configured.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|