dsh-scout 0.8.0 → 0.8.1
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/dist/index.d.ts +14 -2
- package/dist/index.js +20 -11
- package/dist/model.d.ts +7 -5
- package/dist/model.js +12 -11
- package/package.json +12 -1
- package/tests/model.test.mjs +67 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,11 +1,23 @@
|
|
|
1
1
|
import type { Context } from '@deepseek-ai/cordis';
|
|
2
|
+
import z from '@deepseek-ai/schemastery';
|
|
2
3
|
export declare const name = "dsh-scout";
|
|
3
4
|
export declare const inject: string[];
|
|
4
|
-
export interface
|
|
5
|
+
export interface Config {
|
|
5
6
|
/** Default directory for case exports; case files land under `<scoutDir>/<caseId>/`. */
|
|
6
7
|
scoutDir?: string;
|
|
7
8
|
/** Persist the five-file export after every mutation when `scoutDir` is set. */
|
|
8
9
|
autoPersist?: boolean;
|
|
10
|
+
/** Trusted authority host suffixes used by the E3 evidence policy. */
|
|
11
|
+
authorityHostSuffixes?: readonly string[];
|
|
9
12
|
}
|
|
10
|
-
export declare
|
|
13
|
+
export declare const Config: z<Schemastery.ObjectS<{
|
|
14
|
+
scoutDir: z<string, string>;
|
|
15
|
+
autoPersist: z<boolean, boolean>;
|
|
16
|
+
authorityHostSuffixes: z<string[], string[]>;
|
|
17
|
+
}>, Schemastery.ObjectT<{
|
|
18
|
+
scoutDir: z<string, string>;
|
|
19
|
+
autoPersist: z<boolean, boolean>;
|
|
20
|
+
authorityHostSuffixes: z<string[], string[]>;
|
|
21
|
+
}>>;
|
|
22
|
+
export declare function apply(ctx: Context, config?: Config): void;
|
|
11
23
|
export * from './model.js';
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import { defineTool } from '@deepseek-ai/dsh-tools';
|
|
2
|
-
import
|
|
2
|
+
import z from '@deepseek-ai/schemastery';
|
|
3
|
+
import { addClaim, addSource, appendEvent, CLAIM_DIMENSIONS, CLAIM_IMPACTS, CLAIM_STATUSES, createCase, decideCase, DEFAULT_AUTHORITY_HOST_SUFFIXES, EVIDENCE_LEVELS, EXPORT_FILE_NAMES, exportCaseFiles, generateInterviewQuestions, importCaseFromFiles, inferEvidenceLevel, inferSourceType, renderComparison, renderReport, SOURCE_TYPES, verifyClaim, verifyIdentity, } from './model.js';
|
|
3
4
|
export const name = 'dsh-scout';
|
|
4
|
-
export const inject = ['tools'
|
|
5
|
+
export const inject = ['tools'];
|
|
6
|
+
export const Config = z.object({
|
|
7
|
+
scoutDir: z.string().default('dsh-scout'),
|
|
8
|
+
autoPersist: z.boolean().default(false),
|
|
9
|
+
authorityHostSuffixes: z.array(z.string()).default([...DEFAULT_AUTHORITY_HOST_SUFFIXES]),
|
|
10
|
+
});
|
|
5
11
|
export function apply(ctx, config = {}) {
|
|
6
12
|
const cases = new Map();
|
|
7
13
|
const caseKey = (caseId, agentId) => `${String(agentId ?? 'unscoped')}::${caseId}`;
|
|
@@ -11,8 +17,11 @@ export function apply(ctx, config = {}) {
|
|
|
11
17
|
throw new Error(`Unknown case: ${key.split('::').at(-1)}`);
|
|
12
18
|
return scoutCase;
|
|
13
19
|
};
|
|
14
|
-
|
|
15
|
-
|
|
20
|
+
// Optional host services: unavailable hosts degrade gracefully (tools report
|
|
21
|
+
// the missing service instead of failing to register).
|
|
22
|
+
const fs = ctx.get('fs');
|
|
23
|
+
const web = ctx.get('web');
|
|
24
|
+
const authoritySuffixes = config.authorityHostSuffixes ?? DEFAULT_AUTHORITY_HOST_SUFFIXES;
|
|
16
25
|
const defaultScoutDir = config.scoutDir?.replace(/\/+$/, '') || 'dsh-scout';
|
|
17
26
|
const caseExportDir = (caseId) => `${defaultScoutDir}/${caseId}`;
|
|
18
27
|
const persistFiles = async (scoutCase, targetDir) => {
|
|
@@ -45,7 +54,7 @@ export function apply(ctx, config = {}) {
|
|
|
45
54
|
detail,
|
|
46
55
|
});
|
|
47
56
|
};
|
|
48
|
-
const registerSource = (scoutCase, source) => recordEvent(addSource(scoutCase, source), 'source_added', {
|
|
57
|
+
const registerSource = (scoutCase, source) => recordEvent(addSource(scoutCase, source, authoritySuffixes), 'source_added', {
|
|
49
58
|
sourceId: source.sourceId,
|
|
50
59
|
type: source.type,
|
|
51
60
|
title: source.title,
|
|
@@ -199,8 +208,8 @@ export function apply(ctx, config = {}) {
|
|
|
199
208
|
errors.push({ url, error: `invalid evidenceLevel: ${String(item.evidenceLevel)}` });
|
|
200
209
|
continue;
|
|
201
210
|
}
|
|
202
|
-
const typeGuess = inferSourceType(url, explicitType);
|
|
203
|
-
const levelGuess = inferEvidenceLevel(url, typeGuess.type, explicitLevel);
|
|
211
|
+
const typeGuess = inferSourceType(url, explicitType, authoritySuffixes);
|
|
212
|
+
const levelGuess = inferEvidenceLevel(url, typeGuess.type, explicitLevel, authoritySuffixes);
|
|
204
213
|
let sourceId = `src-${nextId}`;
|
|
205
214
|
while (scoutCase.sources.some(source => source.sourceId === sourceId)) {
|
|
206
215
|
nextId += 1;
|
|
@@ -349,8 +358,8 @@ export function apply(ctx, config = {}) {
|
|
|
349
358
|
return;
|
|
350
359
|
}
|
|
351
360
|
try {
|
|
352
|
-
const typeGuess = inferSourceType(url);
|
|
353
|
-
const levelGuess = inferEvidenceLevel(url, typeGuess.type);
|
|
361
|
+
const typeGuess = inferSourceType(url, undefined, authoritySuffixes);
|
|
362
|
+
const levelGuess = inferEvidenceLevel(url, typeGuess.type, undefined, authoritySuffixes);
|
|
354
363
|
let sourceId = `src-${nextId}`;
|
|
355
364
|
while (scoutCase.sources.some(source => source.sourceId === sourceId)) {
|
|
356
365
|
nextId += 1;
|
|
@@ -542,7 +551,7 @@ export function apply(ctx, config = {}) {
|
|
|
542
551
|
evidenceLevel: args.evidenceLevel,
|
|
543
552
|
status: 'captured',
|
|
544
553
|
};
|
|
545
|
-
let nextCase = recordEvent(addSource(scoutCase, source), 'source_added', {
|
|
554
|
+
let nextCase = recordEvent(addSource(scoutCase, source, authoritySuffixes), 'source_added', {
|
|
546
555
|
sourceId: source.sourceId,
|
|
547
556
|
evidenceLevel: source.evidenceLevel,
|
|
548
557
|
});
|
|
@@ -577,7 +586,7 @@ export function apply(ctx, config = {}) {
|
|
|
577
586
|
legalRepresentative: args.legalRepresentative,
|
|
578
587
|
brandRelationship: args.brandRelationship,
|
|
579
588
|
sourceIds: args.sourceIds.split(',').map(value => value.trim()).filter(Boolean),
|
|
580
|
-
})), 'identity_verified', {
|
|
589
|
+
}, authoritySuffixes)), 'identity_verified', {
|
|
581
590
|
legalEntity: args.legalEntity,
|
|
582
591
|
registrationNumber: args.registrationNumber,
|
|
583
592
|
});
|
package/dist/model.d.ts
CHANGED
|
@@ -17,6 +17,8 @@ export interface ScoutEvent {
|
|
|
17
17
|
at: string;
|
|
18
18
|
detail: Record<string, unknown>;
|
|
19
19
|
}
|
|
20
|
+
/** Default trusted authority host suffixes used by the E3 evidence policy. */
|
|
21
|
+
export declare const DEFAULT_AUTHORITY_HOST_SUFFIXES: readonly ["gov", "gov.cn", "gov.uk", "gov.hk", "gov.au", "gc.ca", "brreg.no", "handelsregister.de", "hkexnews.hk", "sse.com.cn", "szse.cn", "neeq.com.cn"];
|
|
20
22
|
export interface ScoutSource {
|
|
21
23
|
sourceId: string;
|
|
22
24
|
type: SourceType;
|
|
@@ -80,15 +82,15 @@ export declare function createCase(input: {
|
|
|
80
82
|
location?: string;
|
|
81
83
|
decisionObjective?: string;
|
|
82
84
|
}): ScoutCase;
|
|
83
|
-
export declare function addSource(scoutCase: ScoutCase, source: ScoutSource): ScoutCase;
|
|
84
|
-
export declare function isTrustedAuthorityUrl(value: string | null): boolean;
|
|
85
|
+
export declare function addSource(scoutCase: ScoutCase, source: ScoutSource, authoritySuffixes?: readonly string[]): ScoutCase;
|
|
86
|
+
export declare function isTrustedAuthorityUrl(value: string | null, authoritySuffixes?: readonly string[]): boolean;
|
|
85
87
|
/** Guess a source category from its URL when the caller did not provide one. */
|
|
86
|
-
export declare function inferSourceType(url: string | null, explicit?: SourceType): {
|
|
88
|
+
export declare function inferSourceType(url: string | null, explicit?: SourceType, authoritySuffixes?: readonly string[]): {
|
|
87
89
|
type: SourceType;
|
|
88
90
|
inferred: boolean;
|
|
89
91
|
};
|
|
90
92
|
/** Guess an evidence level from the URL and category when not provided explicitly. */
|
|
91
|
-
export declare function inferEvidenceLevel(url: string | null, type: SourceType, explicit?: EvidenceLevel): {
|
|
93
|
+
export declare function inferEvidenceLevel(url: string | null, type: SourceType, explicit?: EvidenceLevel, authoritySuffixes?: readonly string[]): {
|
|
92
94
|
evidenceLevel: EvidenceLevel;
|
|
93
95
|
inferred: boolean;
|
|
94
96
|
};
|
|
@@ -107,7 +109,7 @@ export declare function verifyIdentity(scoutCase: ScoutCase, input: {
|
|
|
107
109
|
legalRepresentative: string;
|
|
108
110
|
brandRelationship: string;
|
|
109
111
|
sourceIds: string[];
|
|
110
|
-
}): ScoutCase;
|
|
112
|
+
}, authoritySuffixes?: readonly string[]): ScoutCase;
|
|
111
113
|
export declare function decideCase(scoutCase: ScoutCase): ScoutCase;
|
|
112
114
|
/** Derive a deduplicated, prioritized interview question list from the case. */
|
|
113
115
|
export declare function generateInterviewQuestions(scoutCase: ScoutCase): string[];
|
package/dist/model.js
CHANGED
|
@@ -56,7 +56,8 @@ const JOB_PLATFORM_HOST_HINTS = [
|
|
|
56
56
|
'nowcoder.com',
|
|
57
57
|
'jobs.feishu.cn',
|
|
58
58
|
];
|
|
59
|
-
|
|
59
|
+
/** Default trusted authority host suffixes used by the E3 evidence policy. */
|
|
60
|
+
export const DEFAULT_AUTHORITY_HOST_SUFFIXES = [
|
|
60
61
|
'gov',
|
|
61
62
|
'gov.cn',
|
|
62
63
|
'gov.uk',
|
|
@@ -111,17 +112,17 @@ export function createCase(input) {
|
|
|
111
112
|
],
|
|
112
113
|
};
|
|
113
114
|
}
|
|
114
|
-
export function addSource(scoutCase, source) {
|
|
115
|
+
export function addSource(scoutCase, source, authoritySuffixes = DEFAULT_AUTHORITY_HOST_SUFFIXES) {
|
|
115
116
|
if (scoutCase.sources.some(item => item.sourceId === source.sourceId)) {
|
|
116
117
|
throw new Error(`Duplicate source: ${source.sourceId}`);
|
|
117
118
|
}
|
|
118
119
|
if (source.evidenceLevel === 'E3'
|
|
119
|
-
&& (!IDENTITY_SOURCE_TYPES.has(source.type) || !isTrustedAuthorityUrl(source.url))) {
|
|
120
|
+
&& (!IDENTITY_SOURCE_TYPES.has(source.type) || !isTrustedAuthorityUrl(source.url, authoritySuffixes))) {
|
|
120
121
|
throw new Error('E3 sources require a supported registry, regulator, or official filing HTTPS origin');
|
|
121
122
|
}
|
|
122
123
|
return { ...scoutCase, sources: [...scoutCase.sources, source] };
|
|
123
124
|
}
|
|
124
|
-
export function isTrustedAuthorityUrl(value) {
|
|
125
|
+
export function isTrustedAuthorityUrl(value, authoritySuffixes = DEFAULT_AUTHORITY_HOST_SUFFIXES) {
|
|
125
126
|
if (!value)
|
|
126
127
|
return false;
|
|
127
128
|
try {
|
|
@@ -131,21 +132,21 @@ export function isTrustedAuthorityUrl(value) {
|
|
|
131
132
|
if (url.port && url.port !== '443')
|
|
132
133
|
return false;
|
|
133
134
|
const hostname = url.hostname.toLowerCase().replace(/\.$/, '');
|
|
134
|
-
return
|
|
135
|
+
return authoritySuffixes.some(suffix => hostname === suffix || hostname.endsWith(`.${suffix}`));
|
|
135
136
|
}
|
|
136
137
|
catch {
|
|
137
138
|
return false;
|
|
138
139
|
}
|
|
139
140
|
}
|
|
140
141
|
/** Guess a source category from its URL when the caller did not provide one. */
|
|
141
|
-
export function inferSourceType(url, explicit) {
|
|
142
|
+
export function inferSourceType(url, explicit, authoritySuffixes = DEFAULT_AUTHORITY_HOST_SUFFIXES) {
|
|
142
143
|
if (explicit && SOURCE_TYPES.includes(explicit))
|
|
143
144
|
return { type: explicit, inferred: false };
|
|
144
145
|
if (url) {
|
|
145
146
|
try {
|
|
146
147
|
const hostname = new URL(url).hostname.toLowerCase().replace(/\.$/, '');
|
|
147
148
|
// Trusted authority origins (official registry/regulator) take priority.
|
|
148
|
-
if (isTrustedAuthorityUrl(url)) {
|
|
149
|
+
if (isTrustedAuthorityUrl(url, authoritySuffixes)) {
|
|
149
150
|
const isRegistry = REGISTRY_HOST_SUFFIXES.some(suffix => hostname === suffix || hostname.endsWith(`.${suffix}`));
|
|
150
151
|
return { type: isRegistry ? 'company_registry' : 'regulator', inferred: true };
|
|
151
152
|
}
|
|
@@ -161,10 +162,10 @@ export function inferSourceType(url, explicit) {
|
|
|
161
162
|
return { type: 'other', inferred: true };
|
|
162
163
|
}
|
|
163
164
|
/** Guess an evidence level from the URL and category when not provided explicitly. */
|
|
164
|
-
export function inferEvidenceLevel(url, type, explicit) {
|
|
165
|
+
export function inferEvidenceLevel(url, type, explicit, authoritySuffixes = DEFAULT_AUTHORITY_HOST_SUFFIXES) {
|
|
165
166
|
if (explicit && EVIDENCE_LEVELS.includes(explicit))
|
|
166
167
|
return { evidenceLevel: explicit, inferred: false };
|
|
167
|
-
if (IDENTITY_SOURCE_TYPES.has(type) && isTrustedAuthorityUrl(url)) {
|
|
168
|
+
if (IDENTITY_SOURCE_TYPES.has(type) && isTrustedAuthorityUrl(url, authoritySuffixes)) {
|
|
168
169
|
return { evidenceLevel: 'E3', inferred: true };
|
|
169
170
|
}
|
|
170
171
|
if (type === 'user_provided') {
|
|
@@ -228,7 +229,7 @@ export function verifyClaim(scoutCase, input) {
|
|
|
228
229
|
claims[claimIndex] = verified;
|
|
229
230
|
return { ...scoutCase, claims };
|
|
230
231
|
}
|
|
231
|
-
export function verifyIdentity(scoutCase, input) {
|
|
232
|
+
export function verifyIdentity(scoutCase, input, authoritySuffixes = DEFAULT_AUTHORITY_HOST_SUFFIXES) {
|
|
232
233
|
const requiredValues = [
|
|
233
234
|
input.legalEntity,
|
|
234
235
|
input.registrationNumber,
|
|
@@ -250,7 +251,7 @@ export function verifyIdentity(scoutCase, input) {
|
|
|
250
251
|
});
|
|
251
252
|
const hasAuthoritativeSource = sources.some(source => source.evidenceLevel === 'E3'
|
|
252
253
|
&& IDENTITY_SOURCE_TYPES.has(source.type)
|
|
253
|
-
&& isTrustedAuthorityUrl(source.url));
|
|
254
|
+
&& isTrustedAuthorityUrl(source.url, authoritySuffixes));
|
|
254
255
|
if (!hasAuthoritativeSource) {
|
|
255
256
|
throw new Error('Company identity verification requires a linked E3 registry, regulator, or official filing source');
|
|
256
257
|
}
|
package/package.json
CHANGED
|
@@ -1,11 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-scout",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.1",
|
|
4
4
|
"description": "Evidence-driven company and job intelligence plugin for DeepSeek Harness.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "dist/index.js",
|
|
8
8
|
"types": "dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"default": "./dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"./cordis.patch.yml": "./cordis.patch.yml",
|
|
15
|
+
"./package.json": "./package.json"
|
|
16
|
+
},
|
|
9
17
|
"files": [
|
|
10
18
|
"dist",
|
|
11
19
|
"cordis.patch.yml",
|
|
@@ -44,6 +52,9 @@
|
|
|
44
52
|
"@deepseek-ai/dsh-tools": "0.1.0-rc.6",
|
|
45
53
|
"typescript": "^5.8.3"
|
|
46
54
|
},
|
|
55
|
+
"dependencies": {
|
|
56
|
+
"@deepseek-ai/schemastery": "^3.18.1"
|
|
57
|
+
},
|
|
47
58
|
"dsh": {
|
|
48
59
|
"bundle": {
|
|
49
60
|
"patch": "./cordis.patch.yml"
|
package/tests/model.test.mjs
CHANGED
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
verifyClaim,
|
|
19
19
|
verifyIdentity,
|
|
20
20
|
} from '../dist/model.js'
|
|
21
|
-
import { apply, inject, name } from '../dist/index.js'
|
|
21
|
+
import { apply, Config, inject, name } from '../dist/index.js'
|
|
22
22
|
|
|
23
23
|
test('starts conservatively until company identity is verified', () => {
|
|
24
24
|
const scoutCase = createCase({
|
|
@@ -327,6 +327,7 @@ test('only proceeds after identity and all required role dimensions are verified
|
|
|
327
327
|
test('isolates identical case ids by DSH agent session identity', async () => {
|
|
328
328
|
const registered = []
|
|
329
329
|
apply({
|
|
330
|
+
get(name) { return this[name] },
|
|
330
331
|
tools: {
|
|
331
332
|
register(tool) {
|
|
332
333
|
registered.push(tool)
|
|
@@ -413,6 +414,7 @@ test('exports a disposable DSH plugin tool surface', async () => { const regist
|
|
|
413
414
|
const disposed = []
|
|
414
415
|
const effects = []
|
|
415
416
|
apply({
|
|
417
|
+
get(name) { return this[name] },
|
|
416
418
|
tools: {
|
|
417
419
|
register(tool) {
|
|
418
420
|
registered.push(tool)
|
|
@@ -425,7 +427,7 @@ test('exports a disposable DSH plugin tool surface', async () => { const regist
|
|
|
425
427
|
})
|
|
426
428
|
|
|
427
429
|
assert.equal(name, 'dsh-scout')
|
|
428
|
-
assert.deepEqual(inject, ['tools'
|
|
430
|
+
assert.deepEqual(inject, ['tools'])
|
|
429
431
|
assert.deepEqual(
|
|
430
432
|
registered.map(tool => tool.name).sort(),
|
|
431
433
|
[
|
|
@@ -521,6 +523,7 @@ test('exports and imports a case through the fs-backed tools', async () => {
|
|
|
521
523
|
},
|
|
522
524
|
}
|
|
523
525
|
apply({
|
|
526
|
+
get(name) { return this[name] },
|
|
524
527
|
tools: {
|
|
525
528
|
register(tool) {
|
|
526
529
|
registered.push(tool)
|
|
@@ -651,6 +654,7 @@ test('auto-persists each mutation when configured, and exports to the default di
|
|
|
651
654
|
},
|
|
652
655
|
}
|
|
653
656
|
apply({
|
|
657
|
+
get(name) { return this[name] },
|
|
654
658
|
tools: {
|
|
655
659
|
register(tool) {
|
|
656
660
|
registered.push(tool)
|
|
@@ -690,6 +694,7 @@ test('auto-persists each mutation when configured, and exports to the default di
|
|
|
690
694
|
test('scout_questions tool returns the derived question list', async () => {
|
|
691
695
|
const registered = []
|
|
692
696
|
apply({
|
|
697
|
+
get(name) { return this[name] },
|
|
693
698
|
tools: {
|
|
694
699
|
register(tool) {
|
|
695
700
|
registered.push(tool)
|
|
@@ -718,6 +723,7 @@ test('auto-persist and default export dir degrade gracefully without fs or scout
|
|
|
718
723
|
// autoPersist enabled but no fs service: mutations still succeed
|
|
719
724
|
const noFs = []
|
|
720
725
|
apply({
|
|
726
|
+
get(name) { return this[name] },
|
|
721
727
|
tools: { register(tool) { noFs.push(tool); return () => undefined } },
|
|
722
728
|
effect(execute) { Array.from(execute()) },
|
|
723
729
|
}, { scoutDir: '/nowhere', autoPersist: true })
|
|
@@ -743,6 +749,7 @@ test('auto-persist and default export dir degrade gracefully without fs or scout
|
|
|
743
749
|
async writeText(target, content) { store.set(target.displayPath, content); return { operation: 'create' } },
|
|
744
750
|
}
|
|
745
751
|
apply({
|
|
752
|
+
get(name) { return this[name] },
|
|
746
753
|
tools: { register(tool) { registered.push(tool); return () => undefined } },
|
|
747
754
|
fs: mockFs,
|
|
748
755
|
effect(execute) { Array.from(execute()) },
|
|
@@ -854,6 +861,7 @@ test('infers source type and evidence level from URLs', () => {
|
|
|
854
861
|
test('scout_ingest batch-registers sources with inference and isolates errors', async () => {
|
|
855
862
|
const registered = []
|
|
856
863
|
apply({
|
|
864
|
+
get(name) { return this[name] },
|
|
857
865
|
tools: { register(tool) { registered.push(tool); return () => undefined } },
|
|
858
866
|
effect(execute) { Array.from(execute()) },
|
|
859
867
|
})
|
|
@@ -906,6 +914,7 @@ test('scout_ingest isolates null items and invalid explicit enums, and avoids su
|
|
|
906
914
|
|
|
907
915
|
const registered = []
|
|
908
916
|
apply({
|
|
917
|
+
get(name) { return this[name] },
|
|
909
918
|
tools: { register(tool) { registered.push(tool); return () => undefined } },
|
|
910
919
|
effect(execute) { Array.from(execute()) },
|
|
911
920
|
})
|
|
@@ -999,6 +1008,7 @@ test('scout_compare tool compares cases and inference rejects invalid explicit e
|
|
|
999
1008
|
|
|
1000
1009
|
const registered = []
|
|
1001
1010
|
apply({
|
|
1011
|
+
get(name) { return this[name] },
|
|
1002
1012
|
tools: { register(tool) { registered.push(tool); return () => undefined } },
|
|
1003
1013
|
effect(execute) { Array.from(execute()) },
|
|
1004
1014
|
})
|
|
@@ -1020,6 +1030,7 @@ test('scout_compare tool compares cases and inference rejects invalid explicit e
|
|
|
1020
1030
|
test('scout_compare deduplicates case ids and empty claims render cleanly', async () => {
|
|
1021
1031
|
const registered = []
|
|
1022
1032
|
apply({
|
|
1033
|
+
get(name) { return this[name] },
|
|
1023
1034
|
tools: { register(tool) { registered.push(tool); return () => undefined } },
|
|
1024
1035
|
effect(execute) { Array.from(execute()) },
|
|
1025
1036
|
})
|
|
@@ -1068,6 +1079,7 @@ test('scout_ingest drafts claims from items alongside sources', async () => {
|
|
|
1068
1079
|
|
|
1069
1080
|
const registered = []
|
|
1070
1081
|
apply({
|
|
1082
|
+
get(name) { return this[name] },
|
|
1071
1083
|
tools: { register(tool) { registered.push(tool); return () => undefined } },
|
|
1072
1084
|
effect(execute) { Array.from(execute()) },
|
|
1073
1085
|
})
|
|
@@ -1115,6 +1127,7 @@ test('scout_ingest drafts claims from items alongside sources', async () => {
|
|
|
1115
1127
|
test('scout_ingest rejects invalid claim enums explicitly and keeps claim ids collision-safe', async () => {
|
|
1116
1128
|
const registered = []
|
|
1117
1129
|
apply({
|
|
1130
|
+
get(name) { return this[name] },
|
|
1118
1131
|
tools: { register(tool) { registered.push(tool); return () => undefined } },
|
|
1119
1132
|
effect(execute) { Array.from(execute()) },
|
|
1120
1133
|
})
|
|
@@ -1167,6 +1180,7 @@ test('covers registry boundaries, non-object claims, and verified-E0 rejection v
|
|
|
1167
1180
|
const registered = []
|
|
1168
1181
|
const store = new Map()
|
|
1169
1182
|
apply({
|
|
1183
|
+
get(name) { return this[name] },
|
|
1170
1184
|
tools: { register(tool) { registered.push(tool); return () => undefined } },
|
|
1171
1185
|
fs: {
|
|
1172
1186
|
async resolve(path) { return { targetKey: path, displayPath: path } },
|
|
@@ -1211,6 +1225,7 @@ test('scout_search registers provider results as sources and requires the web se
|
|
|
1211
1225
|
const registered = []
|
|
1212
1226
|
const searched = []
|
|
1213
1227
|
apply({
|
|
1228
|
+
get(name) { return this[name] },
|
|
1214
1229
|
tools: { register(tool) { registered.push(tool); return () => undefined } },
|
|
1215
1230
|
web: {
|
|
1216
1231
|
async search(request) {
|
|
@@ -1259,6 +1274,7 @@ test('scout_search registers provider results as sources and requires the web se
|
|
|
1259
1274
|
// web service missing -> explicit error
|
|
1260
1275
|
const noWeb = []
|
|
1261
1276
|
apply({
|
|
1277
|
+
get(name) { return this[name] },
|
|
1262
1278
|
tools: { register(tool) { noWeb.push(tool); return () => undefined } },
|
|
1263
1279
|
effect(execute) { Array.from(execute()) },
|
|
1264
1280
|
})
|
|
@@ -1277,6 +1293,7 @@ test('scout_search handles provider edge cases and limit bounds', async () => {
|
|
|
1277
1293
|
const providerCalls = []
|
|
1278
1294
|
let failSearch = false
|
|
1279
1295
|
apply({
|
|
1296
|
+
get(name) { return this[name] },
|
|
1280
1297
|
tools: { register(tool) { registered.push(tool); return () => undefined } },
|
|
1281
1298
|
web: {
|
|
1282
1299
|
async search(request) {
|
|
@@ -1339,6 +1356,7 @@ test('scout_search handles provider edge cases and limit bounds', async () => {
|
|
|
1339
1356
|
// non-array sources -> reported as an error, tool still succeeds
|
|
1340
1357
|
const noArray = []
|
|
1341
1358
|
apply({
|
|
1359
|
+
get(name) { return this[name] },
|
|
1342
1360
|
tools: { register(tool) { noArray.push(tool); return () => undefined } },
|
|
1343
1361
|
web: { async search() { return { sources: 'nope' } } },
|
|
1344
1362
|
effect(execute) { Array.from(execute()) },
|
|
@@ -1354,3 +1372,50 @@ test('scout_search handles provider edge cases and limit bounds', async () => {
|
|
|
1354
1372
|
assert.equal(nonArray.added.length, 0)
|
|
1355
1373
|
assert.match(nonArray.errors[0].error, /non-array sources/)
|
|
1356
1374
|
})
|
|
1375
|
+
|
|
1376
|
+
test('exports a schemastery Config with sensible defaults', () => {
|
|
1377
|
+
const parsed = Config({})
|
|
1378
|
+
assert.equal(parsed.scoutDir, 'dsh-scout')
|
|
1379
|
+
assert.equal(parsed.autoPersist, false)
|
|
1380
|
+
assert.ok(parsed.authorityHostSuffixes.includes('gov.cn'))
|
|
1381
|
+
assert.ok(parsed.authorityHostSuffixes.includes('gsxt.gov.cn') === false)
|
|
1382
|
+
})
|
|
1383
|
+
|
|
1384
|
+
test('custom authority host suffixes extend the E3 trust boundary', async () => {
|
|
1385
|
+
const registered = []
|
|
1386
|
+
apply({
|
|
1387
|
+
get(name) { return this[name] },
|
|
1388
|
+
tools: {
|
|
1389
|
+
register(tool) {
|
|
1390
|
+
registered.push(tool)
|
|
1391
|
+
return () => undefined
|
|
1392
|
+
},
|
|
1393
|
+
},
|
|
1394
|
+
effect(execute) {
|
|
1395
|
+
Array.from(execute())
|
|
1396
|
+
},
|
|
1397
|
+
}, {
|
|
1398
|
+
authorityHostSuffixes: ['example-registry.example'],
|
|
1399
|
+
})
|
|
1400
|
+
const tools = new Map(registered.map(tool => [tool.name, tool]))
|
|
1401
|
+
const start = tools.get('scout_start')
|
|
1402
|
+
const addSource = tools.get('scout_add_source')
|
|
1403
|
+
await start.execute({
|
|
1404
|
+
caseId: 'custom-trust',
|
|
1405
|
+
companyName: 'Custom Co',
|
|
1406
|
+
roleTitle: 'Role',
|
|
1407
|
+
location: '',
|
|
1408
|
+
}, { agent: { id: 'session-a' } })
|
|
1409
|
+
// The configured suffix makes this registry origin a valid E3 source.
|
|
1410
|
+
const result = await addSource.execute({
|
|
1411
|
+
caseId: 'custom-trust',
|
|
1412
|
+
sourceId: 'registry',
|
|
1413
|
+
sourceType: 'company_registry',
|
|
1414
|
+
title: 'Custom registry',
|
|
1415
|
+
url: 'https://registry.example-registry.example/rec/1',
|
|
1416
|
+
evidenceLevel: 'E3',
|
|
1417
|
+
}, { agent: { id: 'session-a' } })
|
|
1418
|
+
assert.match(result, /"evidenceLevel": "E3"/)
|
|
1419
|
+
// The default trust boundary still rejects the same origin without the override.
|
|
1420
|
+
assert.equal(isTrustedAuthorityUrl('https://registry.example-registry.example/rec/1'), false)
|
|
1421
|
+
})
|