@hcmai/sdk 0.3.6 → 0.3.8
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 +673 -1
- package/dist/index.js +3058 -50
- package/dist/index.js.map +1 -1
- package/package.json +5 -2
package/dist/index.d.ts
CHANGED
|
@@ -2082,6 +2082,20 @@ interface ProbeResult {
|
|
|
2082
2082
|
* **一个自己都分辨不了的分辨承诺比不承诺更糟**,它让人以为这件事已经处理了。
|
|
2083
2083
|
*/
|
|
2084
2084
|
declare function probeExtensionSource(probe: ReconProbe, keys: string[]): Promise<ProbeResult>;
|
|
2085
|
+
/**
|
|
2086
|
+
* 产物目录要用的 `sourceKey`——`recon` 打出去的**第一个**请求序列。
|
|
2087
|
+
*
|
|
2088
|
+
* 🔴 单独一个入口,而**不是**让命令层自己 `probe.get(MODELS_PATH)` 再就地取 key:
|
|
2089
|
+
* `probeExtensionSource` 此前有三个调用点,其中一个(recon 的这一跳)漏了
|
|
2090
|
+
* 「元素可能不是对象 / body 整体可能不是对象 / modelKey 可能是空串」这三道过滤,
|
|
2091
|
+
* 于是客户网关一改清单形状,`recon` 就在第一步以一条裸堆栈收场 —— 而顾问手上
|
|
2092
|
+
* 没有源码、没有安装包、也没有第二份基线可对照。
|
|
2093
|
+
* 三处各修各的会再漏第四处;收进 `modelKeys()` 这**一道门**,漏不掉。
|
|
2094
|
+
*
|
|
2095
|
+
* 🔴 探不到时回 `'unknown'` 而不是抛:探不到是**可降级**的(产物目录名带 unknown,
|
|
2096
|
+
* 顾问一眼看得出这份基线的层次身份没确认),而抛会让整轮爬取在第一步就没了。
|
|
2097
|
+
*/
|
|
2098
|
+
declare function computeReconSourceKey(probe: ReconProbe): Promise<string>;
|
|
2085
2099
|
interface Fingerprint {
|
|
2086
2100
|
tenantMetaManifestDigest: string;
|
|
2087
2101
|
manifestEntryCount: number;
|
|
@@ -2153,4 +2167,662 @@ declare const AUTH_INFO_PATH = "/api/auth/info";
|
|
|
2153
2167
|
*/
|
|
2154
2168
|
declare function fetchReconIdentity(probe: ReconProbe, requestedTenantId: string): Promise<ReconIdentity | null>;
|
|
2155
2169
|
|
|
2156
|
-
|
|
2170
|
+
/**
|
|
2171
|
+
* 定向重爬的披露标记。
|
|
2172
|
+
*
|
|
2173
|
+
* 🔴 `_kit` 前缀:这一节是**工具加的**,产品从不发它。下游遍历产物顶层键的人
|
|
2174
|
+
* 必须一眼看得出哪些是服务端说的、哪些是我们说的。
|
|
2175
|
+
*
|
|
2176
|
+
* 🔴 **为什么单独成一个模块**:有**三份**产物要写这一节 —— `gates.json`(编排层写)、
|
|
2177
|
+
* `mapping/_index.json`、`valuedomain/_index.json`(后两份由各自模块写)。
|
|
2178
|
+
* 而 `mapping` / `valuedomain` 不许反过来 import 编排层(那是它们的上层,import 会成环)。
|
|
2179
|
+
* 剩下的选择只有两个:把字面量抄三份,或者收到一个三方都在其下的地方。
|
|
2180
|
+
* 抄三份必然漂移,而漂移的表现是**顾问 grep `_kitCarryOver` 只搜到两份产物** ——
|
|
2181
|
+
* 第三份看着就像「这一轮没有沿用」,正是这一节存在的意义所要防的那种误读。
|
|
2182
|
+
*/
|
|
2183
|
+
declare const KIT_CARRY_OVER_KEY = "_kitCarryOver";
|
|
2184
|
+
|
|
2185
|
+
/**
|
|
2186
|
+
* 只读闸(action gates)的爬取与落盘(契约清单 §3.5,29 条)。
|
|
2187
|
+
*
|
|
2188
|
+
* `readOnly` 是产品里**唯一**让 AI 工具 `hcm_invoke_read_action` 免用户确认直接执行的开关。
|
|
2189
|
+
* 判错的代价不是「基线不准」,是 **AI 静默写客户生产库**。所以本模块一切从严、一切 fail-closed:
|
|
2190
|
+
* 凡是没能确凿看到「显式声明只读 + 全部候选都只读 + 端点自己也不认为有分歧」的条目,一律不进闸表。
|
|
2191
|
+
*/
|
|
2192
|
+
|
|
2193
|
+
/**
|
|
2194
|
+
* 「声明了只读、却被 fail-closed 规则拒掉」的动作必须留痕:不留痕的话,
|
|
2195
|
+
* 「我们拒了它」与「压根没这个动作」在产物里同形,下一个人无从复核我们拒得对不对。
|
|
2196
|
+
*/
|
|
2197
|
+
type RejectReason = 'conflict' | 'conflict-undeclared' | 'candidate-not-read-only' | 'no-candidates' | 'malformed-candidate' | 'malformed-key' | 'malformed-action';
|
|
2198
|
+
interface RejectedEntry {
|
|
2199
|
+
model: string;
|
|
2200
|
+
key: string | null;
|
|
2201
|
+
reason: RejectReason;
|
|
2202
|
+
note: string;
|
|
2203
|
+
}
|
|
2204
|
+
interface GateUnavailable {
|
|
2205
|
+
modelKey: string;
|
|
2206
|
+
status: number;
|
|
2207
|
+
error: string;
|
|
2208
|
+
note: string;
|
|
2209
|
+
bodyPreview?: string;
|
|
2210
|
+
}
|
|
2211
|
+
interface GateCoverage {
|
|
2212
|
+
scope: 'state-scoped';
|
|
2213
|
+
state: string | null;
|
|
2214
|
+
statesEchoed: string[];
|
|
2215
|
+
stateObserved: boolean;
|
|
2216
|
+
stateEchoAgrees: boolean;
|
|
2217
|
+
stateRequested: string;
|
|
2218
|
+
modelsWithoutStateEcho: number;
|
|
2219
|
+
sources: string[];
|
|
2220
|
+
modelsQueried: number;
|
|
2221
|
+
duplicateModelKeys: number;
|
|
2222
|
+
modelsAnswered: number;
|
|
2223
|
+
modelsUnavailable: number;
|
|
2224
|
+
accountingBalanced: boolean;
|
|
2225
|
+
unavailable: GateUnavailable[];
|
|
2226
|
+
rejected: RejectedEntry[];
|
|
2227
|
+
endpointGates: false;
|
|
2228
|
+
note: string;
|
|
2229
|
+
}
|
|
2230
|
+
|
|
2231
|
+
/**
|
|
2232
|
+
* `--model X` 在模型清单里一个都没匹配上。
|
|
2233
|
+
*
|
|
2234
|
+
* 🔴 **必须在写盘之前抛**。原先它一路走完编排:`fields.jsonl` 被清成 0 字节、
|
|
2235
|
+
* `gates.json` 清空,而摘要是 `rc=0 / modelsQueried:0 / driftSinceLastRecon: []`
|
|
2236
|
+
* ——读作「跑成功了,环境没变」。此后 `find` 任何词都印「0 命中 / 索引共 0 字段」,
|
|
2237
|
+
* 与「产品没有这个字段」同形(GC3 要分开的两件事),方向是**低报**。
|
|
2238
|
+
*
|
|
2239
|
+
* 代价在客户现场是最重的一档:FDE 手上**只有这一份基线**,没有源码、没有安装包可以重来,
|
|
2240
|
+
* 刚跑完的一次全量(可能几十分钟)会被一个拼错的模型名抹掉,而且没有任何迹象告诉他出事了。
|
|
2241
|
+
*/
|
|
2242
|
+
declare class TargetModelNotFound extends Error {
|
|
2243
|
+
constructor(message: string);
|
|
2244
|
+
}
|
|
2245
|
+
/**
|
|
2246
|
+
* 每个模型的爬取时刻,写进**内容**而不是靠文件 mtime。
|
|
2247
|
+
*
|
|
2248
|
+
* 🔴 理由是判据本身:**说错年龄比不说更坏**。mtime 会被 `cp`(不带 `-p`)、打包解包、
|
|
2249
|
+
* rsync 改写 —— 复评实测 `cp -r` 之后「最早」由 2026-08-13 变 2026-08-16,
|
|
2250
|
+
* 沿用行再次被报成刚爬的。而 FDE 的产物被拷来拷去(发同事 / 打包带走 / rsync 上跳板机 /
|
|
2251
|
+
* 装进交付包)**是主路径不是边缘场景**,光加一句免责声明等于让他继续读一个错日期。
|
|
2252
|
+
*
|
|
2253
|
+
* 🔴 落点是**独立文件**,不许往 `meta/<key>.model.json` 里塞键:那些是原样留证的产品
|
|
2254
|
+
* 响应体,污染它们就毁掉了「这是服务端原话」这个唯一价值。文件名带 `_kit` 前缀 ——
|
|
2255
|
+
* 一眼看得出是工具写的,产品从不发它。
|
|
2256
|
+
*/
|
|
2257
|
+
declare const KIT_CRAWLED_AT_FILE = "_kitCrawledAt.json";
|
|
2258
|
+
/**
|
|
2259
|
+
* 「带轮次戳的产物」名单是**判据的单一来源**:`runRecon` 按它写、`inspectRound` 按它读。
|
|
2260
|
+
*
|
|
2261
|
+
* 🔴 两边各写一份清单,下一个新产物必然只补一边 —— 而漏补的表现是
|
|
2262
|
+
* 「那份产物永远被判成本轮的」,一个只在中断时才暴露的静默缺陷。
|
|
2263
|
+
*/
|
|
2264
|
+
declare const ROUND_STAMPED_PRODUCTS: readonly ["topology.json", "valuedomain/_index.json", "mapping/_index.json"];
|
|
2265
|
+
/**
|
|
2266
|
+
* 🔴 三档**不许合并**,它们的下一步动作完全不同:
|
|
2267
|
+
* complete = 所有产物同属一轮,且这一轮跑到了最后(台账落了盘);
|
|
2268
|
+
* mixed = 存在不同轮次的产物 / 有产物没戳 / 有产物缺席 / 这一轮没跑完 —— 都要指名道姓;
|
|
2269
|
+
* unknown = 产物里根本没有 roundId(更早版本的 kit 爬的),**判不了**。
|
|
2270
|
+
* ⚠️ `unknown` 这一档不许省:老基线不带戳,把它判成 complete 或 mixed 都是撒谎。
|
|
2271
|
+
*/
|
|
2272
|
+
declare const ROUND_STATES: readonly ["complete", "mixed", "unknown"];
|
|
2273
|
+
type RoundState = (typeof ROUND_STATES)[number];
|
|
2274
|
+
/**
|
|
2275
|
+
* 档位 → 退出码。**这张表是唯一一处做这个映射的地方**,命令层只查表、一个字都不重判。
|
|
2276
|
+
*
|
|
2277
|
+
* 🔴 `unknown` 也非零:它是「判不了」,而脚本把「判不了」当成「没问题」继续往下走,
|
|
2278
|
+
* 正是这一整节要防的事。
|
|
2279
|
+
*/
|
|
2280
|
+
declare const ROUND_EXIT_CODES: Record<RoundState, number>;
|
|
2281
|
+
/** 零网络子命令的名字。产物里教的读法与摘要里给的提示都从这里取,不抄第二份。 */
|
|
2282
|
+
declare const ROUND_SUBCOMMAND = "hcm delivery recon round";
|
|
2283
|
+
/** `20260816T075429Z-a1b2c3d4`。前半给顺序,后半给身份(理由见上方注释)。 */
|
|
2284
|
+
declare function newRoundId(started: Date): string;
|
|
2285
|
+
interface RoundProduct {
|
|
2286
|
+
path: string;
|
|
2287
|
+
state: 'this-round' | 'older-round' | 'unstamped' | 'missing' | 'unreadable';
|
|
2288
|
+
roundId: string | null;
|
|
2289
|
+
}
|
|
2290
|
+
interface LedgerFilesMissing {
|
|
2291
|
+
checked: number;
|
|
2292
|
+
count: number;
|
|
2293
|
+
files: string[];
|
|
2294
|
+
}
|
|
2295
|
+
interface RoundResult {
|
|
2296
|
+
state: RoundState;
|
|
2297
|
+
roundId: string | null;
|
|
2298
|
+
roundStartedAt: unknown;
|
|
2299
|
+
finished: boolean | null;
|
|
2300
|
+
productsWrittenThisRound: number | null;
|
|
2301
|
+
ledgerFilesMissing: LedgerFilesMissing | null;
|
|
2302
|
+
products: RoundProduct[];
|
|
2303
|
+
dir: string;
|
|
2304
|
+
note: string;
|
|
2305
|
+
}
|
|
2306
|
+
/**
|
|
2307
|
+
* 读一份**已经落盘**的基线,回答「它是不是一轮完整跑出来的」。
|
|
2308
|
+
*
|
|
2309
|
+
* 只读文件、零网络:顾问在气隙机器上、或者刚被 Ctrl-C 打断之后,手上就只有这几个文件
|
|
2310
|
+
* (被打断的那一轮**不会**打印任何摘要 —— 所以这个判断不能只活在摘要里)。
|
|
2311
|
+
*/
|
|
2312
|
+
declare function inspectRound(root: string): RoundResult;
|
|
2313
|
+
/**
|
|
2314
|
+
* 「台账点了名、盘上却没有」是**第三种事**,措辞不许与下面那四档任何一条重合(连子串都不许):
|
|
2315
|
+
* 带戳索引的 `missing` = 那份产物**根本没有**(这一轮没写到那里),下一步是重跑 recon;
|
|
2316
|
+
* 本档 = **本轮写过、现在却找不到了**,下一步是先问「这份基线是怎么拷过来的」。
|
|
2317
|
+
* 把两者说成同一句话,顾问就会去重跑一个其实拷漏了的基线,而漏的那几份下次还会漏。
|
|
2318
|
+
*/
|
|
2319
|
+
declare const LEDGER_MISSING_WORD = "\u672C\u8F6E\u5199\u8FC7\u3001\u73B0\u5728\u5374**\u627E\u4E0D\u5230\u4E86**\uFF08\u62F7\u8D1D / \u88C1\u526A\u65F6\u6F0F\u5E26\uFF0C\u6216\u4E8B\u540E\u88AB\u5220\uFF09";
|
|
2320
|
+
declare const PRODUCT_STATE_WORDS: Record<Exclude<RoundProduct['state'], 'this-round'>, string>;
|
|
2321
|
+
/**
|
|
2322
|
+
* 两个 roundId 的时间差 —— 「旧到哪一轮」要说得出**多旧**才有用。
|
|
2323
|
+
* 解析不出来就不说(宁可少说一句,也不许说错一个时间)。
|
|
2324
|
+
*/
|
|
2325
|
+
declare function roundAge(roundId: unknown, other: unknown): string;
|
|
2326
|
+
/** 把 `inspectRound` 的结论说成人话。三档措辞**互不重叠**,读者不必回头查文档。 */
|
|
2327
|
+
declare function renderRound(result: RoundResult): string;
|
|
2328
|
+
interface CarryOver {
|
|
2329
|
+
mode: 'full' | 'targeted';
|
|
2330
|
+
onlyModel: string | null;
|
|
2331
|
+
modelsRecrawled: string[];
|
|
2332
|
+
modelsCarriedOver: string[];
|
|
2333
|
+
gatesCarriedOver: number;
|
|
2334
|
+
problems: string[];
|
|
2335
|
+
topologyRecomputed: boolean;
|
|
2336
|
+
note: string;
|
|
2337
|
+
}
|
|
2338
|
+
interface RunReconOptions {
|
|
2339
|
+
state?: string;
|
|
2340
|
+
onlyModel?: string | null;
|
|
2341
|
+
/** 服务端认定的 token 归属。落进 `valuedomain/_index.json`,`null` = 调用方没告诉我。 */
|
|
2342
|
+
identity?: unknown;
|
|
2343
|
+
}
|
|
2344
|
+
interface ReconSummary {
|
|
2345
|
+
driftSinceLastRecon: string[] | null;
|
|
2346
|
+
modelsQueried: number;
|
|
2347
|
+
metasCrawled: number;
|
|
2348
|
+
unavailable: number;
|
|
2349
|
+
gates: number;
|
|
2350
|
+
fieldRows: number;
|
|
2351
|
+
fieldsDropped: number;
|
|
2352
|
+
carryOver: CarryOver;
|
|
2353
|
+
crawledAtUnknown: number;
|
|
2354
|
+
topology: Record<string, unknown>;
|
|
2355
|
+
valuedomain: Record<string, unknown>;
|
|
2356
|
+
mapping: Record<string, unknown>;
|
|
2357
|
+
round: Record<string, unknown>;
|
|
2358
|
+
coverage: GateCoverage & {
|
|
2359
|
+
[KIT_CARRY_OVER_KEY]: CarryOver;
|
|
2360
|
+
};
|
|
2361
|
+
dir: string;
|
|
2362
|
+
}
|
|
2363
|
+
declare function runRecon(probe: ReconProbe, layout: ReconLayout, opts?: RunReconOptions): Promise<ReconSummary>;
|
|
2364
|
+
|
|
2365
|
+
declare const ACLASS_KINDS: readonly ["fieldKey", "fieldLabel", "enumValue", "modelName", "codeValue", "naturalKey", "caliber"];
|
|
2366
|
+
type AclassKind = (typeof ACLASS_KINDS)[number];
|
|
2367
|
+
/** 枚举元素里**属于租户事实**的那几个键。 */
|
|
2368
|
+
declare const ENUM_ELEMENT_KEYS: readonly ["value", "label", "key"];
|
|
2369
|
+
declare const CLASS_SCOPE = "class";
|
|
2370
|
+
declare const ENTRY_SCOPE = "entry";
|
|
2371
|
+
/**
|
|
2372
|
+
* 🔴 取值域要有个**唯一的**出处:`scope` 写错一个字母,那条类级台账就从
|
|
2373
|
+
* `unguardedKinds` 里**静默消失** —— 症状与「这一类有人守」逐字相同。
|
|
2374
|
+
*/
|
|
2375
|
+
declare const SCOPES: readonly ["class", "entry"];
|
|
2376
|
+
declare const REASON_UNREAD = "unread";
|
|
2377
|
+
declare const REASON_EMPTY = "empty";
|
|
2378
|
+
/**
|
|
2379
|
+
* **类级台账的 reason 取值域**。它存在的唯一理由是**给下游一个能绑的键集**:
|
|
2380
|
+
* `scan.ts` 要按这两档说出「为什么这一类是空的」。下游查表用 `[]` 而不是 `.get(…, '')`
|
|
2381
|
+
* —— 这里多一档而下游没跟上 ⇒ 当场炸,不是静默少一句话。
|
|
2382
|
+
*/
|
|
2383
|
+
declare const CLASS_REASONS: readonly ["unread", "empty"];
|
|
2384
|
+
/**
|
|
2385
|
+
* 一条台账。
|
|
2386
|
+
*
|
|
2387
|
+
* 🔴 台账是**结构**不是字符串:判据要按「类级 / 条目级」分档,而分档信息一旦只活在
|
|
2388
|
+
* 中文消息里,判据就得去解析散文 —— 改一个字就悄悄失效。
|
|
2389
|
+
*/
|
|
2390
|
+
interface AclassProblem {
|
|
2391
|
+
readonly scope: string;
|
|
2392
|
+
readonly reason: string;
|
|
2393
|
+
readonly kinds: readonly AclassKind[];
|
|
2394
|
+
readonly message: string;
|
|
2395
|
+
}
|
|
2396
|
+
type Aclass = Record<AclassKind, Set<string>>;
|
|
2397
|
+
/**
|
|
2398
|
+
* → 类级台账点名的类集合。
|
|
2399
|
+
*
|
|
2400
|
+
* 🔴 **认不出的 `scope` 一律抛,不许当成条目级悄悄跳过**:`'clsss'` 这种手误会让那一条
|
|
2401
|
+
* 从这个集合里消失,而「这一类没人守」与「这一类有人守」在返回值上**逐字同形**。
|
|
2402
|
+
*/
|
|
2403
|
+
declare function unguardedKinds(problems: readonly AclassProblem[]): Set<AclassKind>;
|
|
2404
|
+
interface DerivedAclass {
|
|
2405
|
+
aclass: Aclass;
|
|
2406
|
+
problems: AclassProblem[];
|
|
2407
|
+
}
|
|
2408
|
+
/**
|
|
2409
|
+
* → `(aclass, problems)`。`aclass[kind]` 是 `Set<string>`,七个键**恒在**。
|
|
2410
|
+
*
|
|
2411
|
+
* `root` = recon 产物根(`ReconLayout.dir` 那一层)。
|
|
2412
|
+
* `mappingArtifact` = 顾问手写的 `work/mapping.json` 路径;`null` 时 `naturalKey`
|
|
2413
|
+
* 这一类**没有来源**,此事进 problems。
|
|
2414
|
+
*
|
|
2415
|
+
* 🔴 七个键恒在(而不是「有内容才建键」):调用方按 `ACLASS_KINDS` 遍历时,
|
|
2416
|
+
* 「这一类是空的」与「根本没有这一类」必须是两个状态 —— 后者读起来像「没查过」。
|
|
2417
|
+
*/
|
|
2418
|
+
declare function deriveAclass(root: string, mappingArtifact?: string | null): DerivedAclass;
|
|
2419
|
+
|
|
2420
|
+
/** 工件契约版本。与 kit `init/artifacts.py::VERSION` 同值,刀 11 合流时以那一份为准。 */
|
|
2421
|
+
declare const MAPPING_ARTIFACT_VERSION = 1;
|
|
2422
|
+
/** 读不下去。`reason` 给程序判、`message` 给人读,两者都不许省。 */
|
|
2423
|
+
declare class MappingArtifactError extends Error {
|
|
2424
|
+
readonly reason: string;
|
|
2425
|
+
readonly message: string;
|
|
2426
|
+
constructor(reason: string, message: string);
|
|
2427
|
+
}
|
|
2428
|
+
/**
|
|
2429
|
+
* → 每个模型声明的业务键字段清单(没声明的模型给一个空数组,**不是**跳过)。
|
|
2430
|
+
*
|
|
2431
|
+
* 🔴 「这个模型没声明业务键」与「工件里根本没有这个模型」是两件事,
|
|
2432
|
+
* 上游按数组长度对得上模型条数才说得清是哪一件。
|
|
2433
|
+
*/
|
|
2434
|
+
declare function readBusinessKeyFields(file: string): string[][];
|
|
2435
|
+
|
|
2436
|
+
/** 一条登记项。三个键**恰好**是这些——多一个键不是「宽容」,是没人知道它什么意思。 */
|
|
2437
|
+
interface ProtocolFact {
|
|
2438
|
+
/** 协议形态本身:端点写模板形(`/api/models/{model}/meta`),错误码写字面。 */
|
|
2439
|
+
readonly token: string;
|
|
2440
|
+
/** `<相对仓根的测试文件>::<该文件里的 `// P:` 标记>`。 */
|
|
2441
|
+
readonly probe: string;
|
|
2442
|
+
/** 这条事实说的是什么;给人读,不参与判据。 */
|
|
2443
|
+
readonly note: string;
|
|
2444
|
+
}
|
|
2445
|
+
/** 台账作用域:整册级 vs 单条级。两者的下游去处不同(见 `scan.ts`)。 */
|
|
2446
|
+
declare const REGISTRY_SCOPE = "registry";
|
|
2447
|
+
declare const FACTS_SCOPES: readonly ["registry", "entry"];
|
|
2448
|
+
type FactsScope = (typeof FACTS_SCOPES)[number];
|
|
2449
|
+
/** 整册级成因。 */
|
|
2450
|
+
declare const REASON_NO_ENTRIES = "no-entries";
|
|
2451
|
+
/** 没给探针根 ⇒ 一条探针都没验过(**不是**「都验过了」)。 */
|
|
2452
|
+
declare const REASON_PROBE_UNVERIFIED = "probe-unverified";
|
|
2453
|
+
declare const REGISTRY_REASONS: readonly ["no-entries", "probe-unverified"];
|
|
2454
|
+
/** 单条级成因。 */
|
|
2455
|
+
declare const REASON_PROBE_SHAPE = "probe-shape";
|
|
2456
|
+
declare const REASON_PROBE_ABSENT = "probe-absent";
|
|
2457
|
+
declare const REASON_DUPLICATE_TOKEN = "duplicate-token";
|
|
2458
|
+
declare const FACTS_REASONS: readonly ["no-entries", "probe-unverified", "probe-shape", "probe-absent", "duplicate-token"];
|
|
2459
|
+
type FactsReason = (typeof FACTS_REASONS)[number];
|
|
2460
|
+
/** 一条台账。`token` 在整册级那一档是 `null`(那时候还没有具体条目可点名)。 */
|
|
2461
|
+
interface FactsProblem {
|
|
2462
|
+
readonly scope: FactsScope;
|
|
2463
|
+
readonly reason: FactsReason;
|
|
2464
|
+
/** 第几条(从 1 起);整册级是 0。 */
|
|
2465
|
+
readonly index: number;
|
|
2466
|
+
readonly token: string | null;
|
|
2467
|
+
readonly message: string;
|
|
2468
|
+
}
|
|
2469
|
+
/** 探针标记在测试文件里的写法。**与 `// C:` 是两套记号**,理由见模块 docstring。 */
|
|
2470
|
+
declare const PROBE_MARK_PREFIX = "// P:";
|
|
2471
|
+
/**
|
|
2472
|
+
* 随包登记册。
|
|
2473
|
+
*
|
|
2474
|
+
* 🔴 **两条,不是三条** —— kit 那边还登记着 `/api/auth/login`,这里**没有**,
|
|
2475
|
+
* 而这是按 kit 自己那条纪律作的决定:**写不出断行为的探针就不登记**。
|
|
2476
|
+
* 行为契约清单 §1.1 把「登录请求体带 tenantId、header 不带」判给了 CLI 既有的认证管道
|
|
2477
|
+
* (`packages/core/src/auth/password.ts`),那一半**没有**任何一条用例断它的 wire shape
|
|
2478
|
+
* ——「已核实」是一句读代码得来的话,不是一条会红的判据。
|
|
2479
|
+
* ⇒ 本刀补了一条真断它的用例(`fde-gate0-facts.test.ts` 里那条带 `// P:` 标记的),
|
|
2480
|
+
* 于是这条事实**付得起**探针,登记成立。**先有探针,才有登记**,顺序不许反。
|
|
2481
|
+
*/
|
|
2482
|
+
declare const PROTOCOL_FACTS: readonly ProtocolFact[];
|
|
2483
|
+
interface LoadedFacts {
|
|
2484
|
+
/** 进了 `P` 的 token(付过探针的那些)。 */
|
|
2485
|
+
facts: string[];
|
|
2486
|
+
problems: FactsProblem[];
|
|
2487
|
+
}
|
|
2488
|
+
/**
|
|
2489
|
+
* 装载登记册 → `(P, 台账)`。
|
|
2490
|
+
*
|
|
2491
|
+
* ## 🔴 探针在**什么时候**被验,与 kit 不同 —— 这一处必须说准
|
|
2492
|
+
*
|
|
2493
|
+
* kit 侧探针是**运行时**验的:它的包里带着自己的 `tests/`,现场跑一次就能确认每条登记
|
|
2494
|
+
* 都真有背书。**npm 包里没有测试**(`files` 只发 `dist/**`)⇒ 同一个做法搬过来的后果是
|
|
2495
|
+
* 每一次现场调用都判「一条探针都没验过」⇒ **rc 恒为 2**,闸从此说不出任何话。
|
|
2496
|
+
*
|
|
2497
|
+
* ⇒ 验的时刻**搬到构建期**:`PROTOCOL_FACTS` 是编译进包的常量,本仓的
|
|
2498
|
+
* `fde-gate0-facts.test.ts` 逐条把它的探针解析到真文件真标记 —— 那条用例红,包就发不出去。
|
|
2499
|
+
* 于是随包登记册在运行时**已经**是验过的,不必也无法再验一次。
|
|
2500
|
+
*
|
|
2501
|
+
* 🔴 **但「没查」与「查过」仍然不许同形**,所以这条豁免**只给随包那一份**:
|
|
2502
|
+
* · 传进来的就是 `PROTOCOL_FACTS`(同一个引用)且没给 `probeRoot` ⇒ 构建期那道闸背书,不记账;
|
|
2503
|
+
* · 传的是**别的**数组且没给 `probeRoot` ⇒ **没有任何东西验过它** ⇒ 记一条整册级
|
|
2504
|
+
* `probe-unverified`(下游据此判前提不成立)。
|
|
2505
|
+
* · 给了 `probeRoot` ⇒ 当场逐条解析(本仓那条用例走的正是这条路)。
|
|
2506
|
+
*/
|
|
2507
|
+
declare function loadProtocolFacts(entries?: readonly ProtocolFact[], options?: {
|
|
2508
|
+
probeRoot?: string;
|
|
2509
|
+
}): LoadedFacts;
|
|
2510
|
+
|
|
2511
|
+
declare const CHANNEL_ACLASS = "aclass";
|
|
2512
|
+
declare const CHANNEL_ARTIFACT = "artifact-path";
|
|
2513
|
+
declare const CHANNEL_UNREGISTERED = "protocol-unregistered";
|
|
2514
|
+
declare const CHANNEL_REGISTERED_IS_ACLASS = "registered-but-is-aclass";
|
|
2515
|
+
declare const CHANNEL_NO_PROBE = "no-probe";
|
|
2516
|
+
declare const CHANNELS: readonly ["aclass", "artifact-path", "protocol-unregistered", "registered-but-is-aclass", "no-probe"];
|
|
2517
|
+
type Channel = (typeof CHANNELS)[number];
|
|
2518
|
+
declare const PREMISE_REASONS: readonly ["no-skill-root", "nothing-scanned", "file-unreadable", "aclass-empty", "aclass-partially-unread", "registry-not-established", "channel-four-unchecked", "baseline-round-not-complete", "symlink-not-scanned"];
|
|
2519
|
+
type PremiseReason = (typeof PREMISE_REASONS)[number];
|
|
2520
|
+
/**
|
|
2521
|
+
* **哪些整册级台账算前提不成立**:除了「查过且为空」之外**全部**。
|
|
2522
|
+
*
|
|
2523
|
+
* 🔴 写成「全集减一个」而不是逐个枚举 —— 逐个枚举的那张表与 `REGISTRY_REASONS` 之间
|
|
2524
|
+
* 没有任何东西绑住,对面加一档时这里静默漏掉,而漏掉的后果是「登记册根本没立住」
|
|
2525
|
+
* 被判成 rc≤1,顾问照着一份**没有豁免通道**的判红清单去改 skill。
|
|
2526
|
+
*/
|
|
2527
|
+
declare const PREMISE_REGISTRY_REASONS: readonly string[];
|
|
2528
|
+
/**
|
|
2529
|
+
* 端点:只认 `/api/` 与 `/ws/` 起头(缺口③)。`{model}` 这种模板段要收得下 ——
|
|
2530
|
+
* 登记册登记的是**模板形态**,skill 正文里出现的往往是**具体形态**,两者必须能对上
|
|
2531
|
+
* (对不上 ⇒ `P` 恒不命中 ⇒ 每一处端点提及都判假红,而两边都全绿)。
|
|
2532
|
+
*
|
|
2533
|
+
* 🔴 **左边界锚点不是装饰**:没有它时 `docs/reference/api/model-api-quickref.md` 会被抽成
|
|
2534
|
+
* `/api/model-api-quickref.md` —— 一条**文档路径**被判进**可豁免的通道二**,消息叫人
|
|
2535
|
+
* 「登记一条并挂上断行为的探针」。那比单纯指错方向更远:它**诱导往登记册里塞非协议条目**,
|
|
2536
|
+
* 而登记册是整条公式里唯一的豁免通道,污染它等于给别的东西开后门。
|
|
2537
|
+
* 🔴 放宽到只排除字母数字**会重新打开这个缺陷**(多抽以 `.md` 收尾的文档路径),
|
|
2538
|
+
* 别顺手去调排除集。
|
|
2539
|
+
*/
|
|
2540
|
+
declare const ENDPOINT_PREFIXES: readonly ["api", "ws"];
|
|
2541
|
+
/**
|
|
2542
|
+
* 产物文件名的后缀集。🔴 **只有这一个载体**:把 `json` / `jsonl` 在正则里再硬编码一遍时,
|
|
2543
|
+
* 「哪些后缀算产物」会**分成两个答案**,症状是某一档静默不再命中 —— 而且往其中一个载体
|
|
2544
|
+
* 打的变异**根本不生效**,差点被读成「守卫有洞」。
|
|
2545
|
+
*/
|
|
2546
|
+
declare const ARTIFACT_SUFFIXES: readonly [".json", ".jsonl"];
|
|
2547
|
+
/**
|
|
2548
|
+
* 产物路径两档,`product-tree-path` 那一档**从产物根锚定**。
|
|
2549
|
+
*
|
|
2550
|
+
* 起手写的是「路径里有一段叫 `recon`/`index`/`meta`/`mapping`/`valuedomain`/`work`」——
|
|
2551
|
+
* 那张目录名表**过判得离谱**:`src/index/router.ts`、`docs/meta/field-spec.md` 全被判成
|
|
2552
|
+
* 产物路径,消息同样把人指错方向。⇒ 要求**真的从产物根起**。
|
|
2553
|
+
*/
|
|
2554
|
+
declare const ARTIFACT_ROOT = ".hcmnx";
|
|
2555
|
+
declare const RECON_DIRNAME = "recon";
|
|
2556
|
+
declare const RECON_SEGMENTS_BELOW_ROOT = 2;
|
|
2557
|
+
/**
|
|
2558
|
+
* 产物根**下面一层**的形状。收窄成「从产物根锚定」时 `index/models.md` 这份**真产物**
|
|
2559
|
+
* 掉出去了 ⇒ 补一档「**恰好两段**且(第一段, 末段后缀)是真产物真有的那几对」。
|
|
2560
|
+
* 🔴 **末段必须是产物后缀**:只判「两段 + 产物子目录」时,`meta/capabilities` /
|
|
2561
|
+
* `index/version` 这类**根本不是文件路径**的写法全被判红。
|
|
2562
|
+
* 🔴 判的是**(子目录, 后缀)这一对**,不是两个独立集合的乘积:乘积里有 `meta/*.md`
|
|
2563
|
+
* 这种真产物根本没有的组合,而散文里真有 `meta/README.md`。
|
|
2564
|
+
* ⚠️ 名单与**真跑一次 recon** 落盘的形状绑住(`==` 不是 `⊆`,现算):`⊆` 只管
|
|
2565
|
+
* 「真产物的形状都在表里」,不管表里有没有多余的对。
|
|
2566
|
+
*/
|
|
2567
|
+
declare const PRODUCT_RELATIVE_SHAPES: ReadonlyArray<readonly [string, string]>;
|
|
2568
|
+
declare const RED_REASONS: Record<Channel, readonly string[]>;
|
|
2569
|
+
/**
|
|
2570
|
+
* **每个 reason 一句自己的话,键集与 `RED_REASONS[CHANNEL_ARTIFACT]` 相等**。
|
|
2571
|
+
*
|
|
2572
|
+
* 起手这里是一句**二分**的三元表达式,而取值域早已是**三档** —— 中间没有任何东西绑住,
|
|
2573
|
+
* 于是新加的那一档落进 `else` 分支,对着 `index/models.md` 说「以 .json/.jsonl 收尾」
|
|
2574
|
+
* 「这一档会过判」:**两句全假**。后果比判错更难查 —— **判红是对的**,顾问会照着那两句
|
|
2575
|
+
* 去改,而那两句描述的是另一档。⇒ 查表少一档当场炸,不静默回落到别人的话。
|
|
2576
|
+
*/
|
|
2577
|
+
declare const ARTIFACT_WORDING: Record<string, (token: string) => string>;
|
|
2578
|
+
/**
|
|
2579
|
+
* 每一档类级成因一句自己的话,键集与 `aclass.CLASS_REASONS` 相等。
|
|
2580
|
+
* 起手这两句话是**内联的字面量**,与 `aclass.ts` 的两个常量各写各的,中间没有任何东西
|
|
2581
|
+
* 绑住 —— 只改其中一个值,全套照样全绿,而后果是前提消息里「为什么这一类是空的」那半句
|
|
2582
|
+
* 静默消失,顾问只知道「空了」不知道该去查哪一头。
|
|
2583
|
+
*/
|
|
2584
|
+
declare const ACLASS_EMPTY_WHY: Record<string, string>;
|
|
2585
|
+
/**
|
|
2586
|
+
* 一条判红。
|
|
2587
|
+
*
|
|
2588
|
+
* 🔴 判红是**结构**不是字符串:通道、成因、token、文件、行号各占一格。分档信息一旦只活在
|
|
2589
|
+
* 中文消息里,判据就得去解析散文,改一个字悄悄失效。
|
|
2590
|
+
* `path` / `line` 在整册级的通道三、通道四上是 `null` / `0`。
|
|
2591
|
+
*/
|
|
2592
|
+
interface Red {
|
|
2593
|
+
readonly channel: Channel;
|
|
2594
|
+
readonly reason: string;
|
|
2595
|
+
readonly token: string;
|
|
2596
|
+
readonly path: string | null;
|
|
2597
|
+
readonly line: number;
|
|
2598
|
+
readonly kinds: readonly AclassKind[];
|
|
2599
|
+
readonly message: string;
|
|
2600
|
+
}
|
|
2601
|
+
/** 一条**前提级**台账 —— 它让 rc 变成 2,而不是让判红清单多一条。 */
|
|
2602
|
+
interface Premise {
|
|
2603
|
+
readonly reason: PremiseReason;
|
|
2604
|
+
readonly message: string;
|
|
2605
|
+
}
|
|
2606
|
+
/** 缺口的两档。**分桶不是分对错**:说不准的那一档**看得见但不拦人**。 */
|
|
2607
|
+
declare const GAP_NOISE_TOKEN = "pure-digit-or-single-char";
|
|
2608
|
+
declare const GAP_CJK_INSIDE_LONGER_RUN = "cjk-inside-longer-run";
|
|
2609
|
+
declare const GAP_REASONS: readonly ["pure-digit-or-single-char", "cjk-inside-longer-run"];
|
|
2610
|
+
type GapReason = (typeof GAP_REASONS)[number];
|
|
2611
|
+
/**
|
|
2612
|
+
* 一条**查过、按缺口放行**的记录。
|
|
2613
|
+
*
|
|
2614
|
+
* 🔴 没有它,「这个 token 落在缺口里」与「压根没找过这个 token」在返回值上逐字同形。
|
|
2615
|
+
*/
|
|
2616
|
+
interface Gap {
|
|
2617
|
+
readonly reason: GapReason;
|
|
2618
|
+
readonly token: string;
|
|
2619
|
+
readonly kinds: readonly AclassKind[];
|
|
2620
|
+
readonly path: string;
|
|
2621
|
+
readonly line: number;
|
|
2622
|
+
readonly why: string;
|
|
2623
|
+
}
|
|
2624
|
+
/**
|
|
2625
|
+
* → 判红清单原样返回;通道名 / 成因认不出来就**抛**。
|
|
2626
|
+
*
|
|
2627
|
+
* 🔴 fail-loud:`'aclas'` 这种手误会让那一条从任何按通道分类的统计里静默消失,
|
|
2628
|
+
* 而「这一条判红了」与「这一条没判红」在按通道统计的结果上逐字同形。
|
|
2629
|
+
*/
|
|
2630
|
+
declare function checkReds(reds: readonly Red[]): Red[];
|
|
2631
|
+
/** → 缺口台账原样返回;`reason` 认不出来就**抛**(理由同 `checkReds`)。 */
|
|
2632
|
+
declare function checkGaps(gaps: readonly Gap[]): Gap[];
|
|
2633
|
+
/** → 前提级台账原样返回;`reason` 认不出来就**抛**(理由同 `checkReds`)。 */
|
|
2634
|
+
declare function checkPremise(problems: readonly Premise[]): Premise[];
|
|
2635
|
+
/**
|
|
2636
|
+
* → 基线轮次这一档的前提级台账(`complete` ⇒ 空表)。
|
|
2637
|
+
*
|
|
2638
|
+
* 🔴 **判据只读 `inspectRound` 的结论,一个字都不重判**:门 0 自己比几份 roundId,
|
|
2639
|
+
* 就是把 `inspectRound` 花整轮治掉的那个洞重新引回来。
|
|
2640
|
+
* 🔴 **不认识的档位一律算「不完整」**(fail-closed):生产者哪天多一档而这里没跟上时,
|
|
2641
|
+
* 静默放行等于把没见过的状态当成 complete。
|
|
2642
|
+
* ⚠️ **这一档的出路要联网,说清楚**:`unknown`(更早版本爬的老基线)唯一的出路是重跑一次
|
|
2643
|
+
* `recon`,而它**要连客户内网的 HCM**。⇒ 在**拿不到网**的机器上这条路确实可能走不通 ——
|
|
2644
|
+
* 那是一条真实的现场约束。放行它不是选项:那等于把「判不了」当「完整」。
|
|
2645
|
+
*/
|
|
2646
|
+
declare function baselineRoundPremise(round: Pick<RoundResult, 'state'> | null): Premise[];
|
|
2647
|
+
/**
|
|
2648
|
+
* 这个 token 落不落在**判红这一层声明的缺口**里(纯数字 / 单字符)。
|
|
2649
|
+
*
|
|
2650
|
+
* 🔴 **不是「过滤噪声」而是「声明缺口」**:`7`(码表的 sortOrder)与 `男`(枚举 label)
|
|
2651
|
+
* 是真的 A 类取值,写进 skill 正文本来就该判红。这里放行它们是因为拿它们当判据会把任何
|
|
2652
|
+
* 一份中文文档淹成红海 —— 代价是这一档**门 0 恒绿**。落在这里的 token 逐条进 `gaps`,
|
|
2653
|
+
* 不许说成「已覆盖」。
|
|
2654
|
+
*/
|
|
2655
|
+
declare function inGap(token: string): boolean;
|
|
2656
|
+
type Span = readonly [number, number];
|
|
2657
|
+
/**
|
|
2658
|
+
* → 这一行里的端点 `[(token, span)]`,**两种形态都收**。
|
|
2659
|
+
*
|
|
2660
|
+
* 🔴 **这是端点抽取唯一的入口**:判据断在其中一条正则上、而另一条形态压根不存在时,
|
|
2661
|
+
* 「端点抽得出来」在用例里成立、在真样本上整类落空。token 一律是**路径部分**,
|
|
2662
|
+
* `span` 是**整段**(URL 形态含 scheme + host),后面几档正则靠它避让。
|
|
2663
|
+
*/
|
|
2664
|
+
declare function endpointsIn(line: string): Array<[string, Span]>;
|
|
2665
|
+
/**
|
|
2666
|
+
* 端点归一:`{model}` / `{id}` 这类模板段一律写成 `{}`。
|
|
2667
|
+
*
|
|
2668
|
+
* 🔴 **幂等**(`normalize(normalize(t)) === normalize(t)`)—— 占位段的正则用 `*` 而不是 `+`,
|
|
2669
|
+
* `{}` 自身归一后还是 `{}`。不幂等时「登记册里的 token」与「查表用的键」会随调用次数变形,
|
|
2670
|
+
* 而症状是豁免时灵时不灵。
|
|
2671
|
+
*/
|
|
2672
|
+
declare function normalize(token: string): string;
|
|
2673
|
+
/**
|
|
2674
|
+
* → 查 `P` 用的模板段清单(每条是一个段数组)。
|
|
2675
|
+
*
|
|
2676
|
+
* 🔴 **这是那道接缝的落点**:登记册登记的是模板形态 `/api/models/{model}/meta`,
|
|
2677
|
+
* skill 正文里出现的是具体形态 `/api/models/Employee/meta`。按字符串相等查,`P` **恒不命中**,
|
|
2678
|
+
* 于是每一处端点提及都判假红 —— 而登记册那边全绿、抽取器那边也全绿,两边谁也发现不了。
|
|
2679
|
+
* 所以查表是**按段匹配**:段数相同、每一段要么逐字相等、要么登记的那一段是占位符。
|
|
2680
|
+
*/
|
|
2681
|
+
declare function factsIndex(facts: readonly string[]): string[][];
|
|
2682
|
+
/**
|
|
2683
|
+
* 这个 token 有没有被 `P` 覆盖(`token ∈ P` 的实际判据)。
|
|
2684
|
+
* 对没有斜杠的 token(错误码)退化成「与某条登记项逐字相等」。
|
|
2685
|
+
*
|
|
2686
|
+
* 🔴 **查询侧那个 `normalize` 是冗余的,别把它读成一道守卫**:`{}` 吃任意一段这一条
|
|
2687
|
+
* **不足以**推出冗余,还得有「抽取器产出的 token 里,花括号只可能**占满整段**」这一条
|
|
2688
|
+
* (它来自端点正则的段交替式)。两条合起来才推得出「查询侧归一改变不了任何结果」。
|
|
2689
|
+
* 留着这次调用只是为了两侧口径出自同一个函数。真正承重的是下面按段匹配本身。
|
|
2690
|
+
*/
|
|
2691
|
+
declare function covered(token: string, index: readonly string[][]): boolean;
|
|
2692
|
+
declare const SEGMENT_SEPARATOR = "/";
|
|
2693
|
+
/**
|
|
2694
|
+
* 把一个路径形态的 token 切成段。**路径分段这件事只有这一个出处**。
|
|
2695
|
+
*
|
|
2696
|
+
* 加这个函数不是为了好看:`artifactReason` 与用例里那几条「放宽面」谓词各自写
|
|
2697
|
+
* `token.split('/')` 时,「怎么切段」有好几份载体,而判据那一侧的载体一旦漂了,守卫看不见。
|
|
2698
|
+
*/
|
|
2699
|
+
declare function segmentsOf(token: string): string[];
|
|
2700
|
+
/**
|
|
2701
|
+
* 这个 token 是不是产物树里的路径片段(**无豁免**);不是就返 `null`。
|
|
2702
|
+
*
|
|
2703
|
+
* · `product-tree-path` —— **从产物根锚定**:有一段是 `.hcmnx`,或有一段是 `recon` 且它
|
|
2704
|
+
* 后面还有至少两段。这一档几乎不会误伤:它要求路径长得像**真的产物树**。
|
|
2705
|
+
* · `product-relative-path` —— **恰好两段**,且(第一段, 末段后缀)是真产物**真有**的那几对。
|
|
2706
|
+
* · `product-file-name` —— 文件名以 `.json` / `.jsonl` 收尾。这一档是**过判**:
|
|
2707
|
+
* `package.json` 也会红。留着它是因为产物**全部**是 json/jsonl,而收窄成「白名单文件名」
|
|
2708
|
+
* 就成了一张没人维护的登记表(产物多一份、白名单不动,那一份从此免检)。
|
|
2709
|
+
* ⚠️ 拆成独立 reason 就是为了让读的人分得清「这条是真产物路径」与「这条只是恰好以
|
|
2710
|
+
* .json 收尾」—— 两者该不该照着改并不一样。
|
|
2711
|
+
*/
|
|
2712
|
+
declare function artifactReason(token: string): string | null;
|
|
2713
|
+
declare function isCjk(char: string | undefined): boolean;
|
|
2714
|
+
/**
|
|
2715
|
+
* → `token` 在这一行里**按分词边界**出现的位置;没有就 -1。
|
|
2716
|
+
*
|
|
2717
|
+
* 🔴 边界只在**词字符**那一侧成立:`hireDate` 出现在 `myhireDates` 里不算数,
|
|
2718
|
+
* 而 `已离职` 出现在 `已离职员工` 里**算数**(CJK 侧没有边界,见缺口②)。
|
|
2719
|
+
* ⚠️ 「算数」不等于「判红」—— 它被分进第二个桶,见 `cjkRunAt`。
|
|
2720
|
+
*/
|
|
2721
|
+
declare function boundedFind(line: string, token: string): number;
|
|
2722
|
+
/**
|
|
2723
|
+
* 这一处命中是不是**落在更长的中文串里**(缺口②的分桶判据)。
|
|
2724
|
+
*
|
|
2725
|
+
* 🔴 中文没有分词边界,所以这一档**分桶而非分对错**:token 的边是汉字、而紧挨着它的那个字
|
|
2726
|
+
* 也是汉字 ⇒ 说不准(`数字员工` 里的 `员工`)。**假红会让顾问把门 0 整个关掉,比漏扫更糟**。
|
|
2727
|
+
* ⇒ 两侧都不是汉字 ⇒ 照常判红;任一侧是汉字 ⇒ 进 `gaps`,**不顶 rc,但逐条印出**。
|
|
2728
|
+
*/
|
|
2729
|
+
declare function cjkRunAt(line: string, start: number, token: string): boolean;
|
|
2730
|
+
/** A 类七子集 → 逐行命中。两条实现路径见模块 docstring。 */
|
|
2731
|
+
declare class Matcher {
|
|
2732
|
+
readonly owners: Map<string, AclassKind[]>;
|
|
2733
|
+
private readonly simple;
|
|
2734
|
+
private readonly byFirst;
|
|
2735
|
+
constructor(aclass: Partial<Aclass>);
|
|
2736
|
+
/**
|
|
2737
|
+
* → 这一行命中的 `[(token, **有边界的**那一处的位置)]`,同一 token 一行只报一次。
|
|
2738
|
+
*
|
|
2739
|
+
* 🔴 **位置必须来自 `boundedFind`,不许再算一遍 `indexOf`**。起手那版用 `boundedFind`
|
|
2740
|
+
* 确认「这一行**存在**一处合法边界命中」,返回的却是第一处裸子串 —— 可能根本不是那一处。
|
|
2741
|
+
* 下游把这个位置交给 `cjkRunAt` 做分桶,于是**在同一行前面加一处诱饵写法就能把一条
|
|
2742
|
+
* 真红压掉**。辖区 = 首/末字符为 CJK、另一端为词字符的**混合形态** token;纯 ASCII 与
|
|
2743
|
+
* 纯 CJK 两个位置必然相等,所以此前每一项变异都照不到它。
|
|
2744
|
+
* 「存在一处合法命中」是「这一处合法」的**必要条件**,不是充分条件。
|
|
2745
|
+
*
|
|
2746
|
+
* 🔴 **`sort` 不是洁癖,它是这份输出的确定性本身**:按插入序遍历会让非纯词形 token 的
|
|
2747
|
+
* 先后随输入变化,集合、rc、判词全都一样,只有顺序变 —— 那正是「静默」的定义,
|
|
2748
|
+
* 而它坏的是**验证仪器**:任何「前后两版输出逐字相等」的复核都会假红。
|
|
2749
|
+
*/
|
|
2750
|
+
hits(line: string): Array<[string, number]>;
|
|
2751
|
+
}
|
|
2752
|
+
/**
|
|
2753
|
+
* 全角**只映射字母与数字三段**,不动标点。这一半是**长度保持的 1:1 映射** ⇒ 它单独作用时
|
|
2754
|
+
* 所有 span 语义原样成立。
|
|
2755
|
+
* 🔴 **不做完整 NFKC**,两条都是实测,不是直觉:
|
|
2756
|
+
* ① NFKC 会把全角**标点**一并改写(全角括号、全角冒号、全角加号),而全角冒号参与
|
|
2757
|
+
* scheme-less host 的「名字:端口」形态、加号参与 naturalKey 的组合形态
|
|
2758
|
+
* (`code+orgUnitCode`)⇒ 端点抽取与通道一的口径都要重核;而那些全角标点在出厂
|
|
2759
|
+
* skill 里是**真有的**。它会动的还不止标点,还有带圈数字 —— 而带圈数字正是本模块
|
|
2760
|
+
* 缺口清单的**编号载体**。
|
|
2761
|
+
* ② U+F900–FAFF 这一段**恰好在 `CJK_RANGES` 里**,而它里面**有**码位 NFKC 之后落到
|
|
2762
|
+
* `CJK_RANGES` 之外 ⇒ `isCjk` 翻转,缺口② 的分桶跟着变。(是「有」不是「整段」。)
|
|
2763
|
+
*/
|
|
2764
|
+
declare const FULLWIDTH_ALNUM_RANGES: ReadonlyArray<readonly [number, number, number]>;
|
|
2765
|
+
/**
|
|
2766
|
+
* 归一之后才认出来的那一条红 / 缺口,**必须自己说出来**:不给它,顾问拿判词里那个词去
|
|
2767
|
+
* 文件里 grep 会**一无所获**,然后判定门 0 报了假红。
|
|
2768
|
+
* 🔴 **它带上原文里那一段的字面**:只说「grep 搜不到」是一句**没有出路**的话。
|
|
2769
|
+
*/
|
|
2770
|
+
declare const FOLDED_NOTE: string;
|
|
2771
|
+
/**
|
|
2772
|
+
* 同形不同码 → 逐字节可比的形态。**幂等**(第二次调用是恒等变换)。
|
|
2773
|
+
*
|
|
2774
|
+
* 🔴 **不做 casefold**:A 类里**有** `code` 这一类通用词,casefold 之后英文散文里的
|
|
2775
|
+
* `Claude Code` / `Code-First` 这类写法会整片判红 —— 假红面在扩大,而扩出来的那一批与
|
|
2776
|
+
* 环境事实无关。⇒ 缺口⑨ 收窄成「**大小写变形仍不判**」,不许说成已覆盖。
|
|
2777
|
+
* ⚠️ **只归一被扫的那一行,不归一 A 类 token 自己**(那一档至今没修,见缺口⑨)。
|
|
2778
|
+
*/
|
|
2779
|
+
declare function foldConfusables(line: string): string;
|
|
2780
|
+
/**
|
|
2781
|
+
* → 这条红 / 缺口该追加的判词(`token` 在原文里逐字找得到时是**空串**)。
|
|
2782
|
+
*
|
|
2783
|
+
* 🔴 **条件是「这个 token 在原文里找不到」,不是「这一行被归一改动过」**。两者不等价,
|
|
2784
|
+
* 而差出来的那一档会让判词**说假话**:一份带 BOM(`Cf` 类)的文件,整行都被归一改动过,
|
|
2785
|
+
* 可 `hireDate` **在原文里逐字就在那儿**、grep 找得到 —— 按行判的话会给它贴上
|
|
2786
|
+
* 「grep 会搜不到」。判词的整个立论是「一条把人指错方向的红比不红更贵」。
|
|
2787
|
+
*/
|
|
2788
|
+
declare function foldedNoteFor(original: string, folded: string, token: string): string;
|
|
2789
|
+
interface ScanResult {
|
|
2790
|
+
red: Red[];
|
|
2791
|
+
channels: Record<Channel, Red[]>;
|
|
2792
|
+
scanned: string[];
|
|
2793
|
+
gaps: Gap[];
|
|
2794
|
+
premise: Premise[];
|
|
2795
|
+
}
|
|
2796
|
+
interface ScanOptions {
|
|
2797
|
+
/** `loadProtocolFacts` 的台账。**不传 ⇒ 通道四一条都判不出来**,见下。 */
|
|
2798
|
+
factsProblems?: readonly FactsProblem[];
|
|
2799
|
+
/** `deriveAclass` 的台账。 */
|
|
2800
|
+
aclassProblems?: readonly AclassProblem[];
|
|
2801
|
+
}
|
|
2802
|
+
/**
|
|
2803
|
+
* → `{red, channels, scanned, gaps, premise}`。
|
|
2804
|
+
*
|
|
2805
|
+
* `skillRoot` = 一个 skill 的根目录。**扫描面 = 递归走得到的全部文件**(skill 正文 +
|
|
2806
|
+
* `references/` 全目录 + 别的任何子目录,同一判据)。
|
|
2807
|
+
* 🔴 **点目录不排除**:`grep -r` 进不了点目录、会假报零命中 —— 扫描面漏掉一支不会让任何
|
|
2808
|
+
* 东西红,「没扫到」与「扫过、干净」逐字同形。
|
|
2809
|
+
* 🔴 **「递归」这个词有一处例外**:**不进符号链接目录** ⇒ 软链目录 / 环路软链 / 断链
|
|
2810
|
+
* **不跟随**,但**会被逐条判成前提级**(rc=2),不再静默。指向**文件**的软链照旧扫。
|
|
2811
|
+
*
|
|
2812
|
+
* `factsProblems` **有默认值,但那个默认值不代表「查过了」**:不传 ⇒ 通道四一条都判不出来,
|
|
2813
|
+
* 而「登记册干干净净」与「根本没查登记册」在返回值上逐字同形 ⇒ 走到缺省就必然多一条
|
|
2814
|
+
* 前提级台账。
|
|
2815
|
+
* `aclassProblems` **两个去处**:类级那一档只用来把消息说清楚;**条目级那一档升前提**
|
|
2816
|
+
* (`aclass-partially-unread`)—— 此前条目级被整类吞掉 ⇒ 一行坏数据里的环境事实逐字写进
|
|
2817
|
+
* skill 也判不出来,而门 0 说「判红 0 条」、rc=0,是完整的静默假绿。
|
|
2818
|
+
*/
|
|
2819
|
+
declare function scan(skillRoot: string, aclass: Partial<Aclass>, facts: readonly string[], options?: ScanOptions): ScanResult;
|
|
2820
|
+
/**
|
|
2821
|
+
* → 三档退出码。**不许合并**。
|
|
2822
|
+
*
|
|
2823
|
+
* 🔴 前提级**压过**判红:前提不成立时那份判红清单本身不可信。判红清单照打 ——
|
|
2824
|
+
* rc 是给脚本的闸,清单是给人的依据。
|
|
2825
|
+
*/
|
|
2826
|
+
declare function exitCode(result: Pick<ScanResult, 'premise' | 'red'>): number;
|
|
2827
|
+
|
|
2828
|
+
export { ACLASS_EMPTY_WHY, ACLASS_KINDS, ARTIFACT_ROOT, ARTIFACT_SUFFIXES, ARTIFACT_WORDING, AUTH_INFO_PATH, type Aclass, type AclassKind, type AclassProblem, type ActionHttpMethod, type ActionInput, type ActionMeta, type ActionResult, type ActionScope, type AdminResetPasswordRequest, type AdminResetPasswordResult, type AnyStreamEvent, type ApprovalConfirmPayload, type ApprovalRejectPayload, type AttachmentMeta, type AuthScheme, BLIND_SPOTS, type BootstrapReconcileResult, type BootstrapTenantRequest, type BootstrapTenantResult, CHANNELS, CHANNEL_ACLASS, CHANNEL_ARTIFACT, CHANNEL_NO_PROBE, CHANNEL_REGISTERED_IS_ACLASS, CHANNEL_UNREGISTERED, CLASS_REASONS, CLASS_SCOPE, type CacheClearResult, type CacheStats, type CarryOver, type ChangePasswordInput, type Channel, type ChironCategory, type ChironManifest, type ChironSkill, type ChironSkillFile, type ChironStage, type ClearMetaCacheOpts, CliError, CliErrorCode, type CliErrorInit, type ClientAuthContext, type ClientCredentialsInput, type ConnectionMessage, type ControlChannelEnvelope, type ConversationListItem, type ConversationListResult, type ConversationState, type CreateResult, type CreatedConversation, DEFAULT_AGENT_ID, DEFAULT_SESSION_TTL_SECONDS, DOCTOR_ANCHORS, type DescribeOpts, type DoctorAnchor, type DoctorCheck, type DoctorReport, ENDPOINT_PREFIXES, ENTRY_SCOPE, ENUM_ELEMENT_KEYS, EXTENSION_SOURCE_HEADER, type EnvConfig, ExitCode, FACTS_REASONS, FOLDED_NOTE, FULLWIDTH_ALNUM_RANGES, type FactsProblem, type FieldMeta, type Fingerprint, type Fixture, type FormatOptions, GAP_CJK_INSIDE_LONGER_RUN, GAP_NOISE_TOKEN, GAP_REASONS, GATE_COMPONENTS, type Gap, type GlobalConfig, HcmClient, type HcmClientOpts, type HttpClientOptions, type IdentityMeta, type ImportClient, type ImportOptions, type ImportResult, type InteractionAnswerPayload, type InteractionRequest, type InteractionResolved, KIT_CARRY_OVER_KEY, KIT_CRAWLED_AT_FILE, LEDGER_MISSING_WORD, LayoutContainmentError, type ListTenantMetaOpts, type LoginOutcome, MANIFEST_PATH, MAPPING_ARTIFACT_VERSION, MAX_MANIFEST_PAGES, MAX_SOURCE_PROBES, META_PATH, MINI_CONTEXT_FILE, MINI_CONTEXT_KIND, MINI_CONTEXT_VERSION, MINI_PULL_MANIFEST_FILE, MINI_REMOTE_ROOT, MINI_STATE_DIR, MODELS_PATH, MappingArtifactError, Matcher, type MessageClass, type MessageCreatePayload, type MessageStreamHandle, type MigrationSummary, type MiniFileSnapshot, type MiniInitArgs, type MiniLocalFile, type MiniLocalStatus, type MiniPreviewTarget, type MiniProjectContext, type MiniPullArgs, type MiniPullManifest, type MiniPullResult, type MiniPushArgs, type MiniPushConflict, type MiniPushOperation, type MiniPushPlanItem, type MiniPushResult, type MiniSmokeCheckArgs, type MiniSmokeFetch, type MiniSmokeFetchResponse, type MiniSmokeResult, type MiniSmokeTargetKind, type MiniSmokeTargetResult, type MiniStatusEntry, type MiniSurface, type MiniTemplateArgs, type MiniTemplateKind, type MiniValidationResult, type MiniVerifyAgentReport, type MiniVerifyBrowserAcceptance, type MiniVerifyChangedFile, type MiniVerifyNextAction, type MiniVerifyPlan, type MiniVerifyPreviewUrl, type MiniVerifyReport, type MiniVerifySummary, type MiniWritableTemplateConfig, type ModelDescription, type ModelQueryDsl, type OneShotOpts, type OneShotResult, type OneShotToolCall, type Outcome, type OutputFormat, PASSWORD_CHANGE_REQUIRED, PREMISE_REASONS, PREMISE_REGISTRY_REASONS, PROACTIVE_REFRESH_MARGIN_SECONDS, PROBE_MARK_PREFIX, PRODUCT_RELATIVE_SHAPES, PRODUCT_STATE_WORDS, PROTOCOL_FACTS, type PairingLoginInput, type ParsedPlaceholder, type PasswordChangeChallenge, type PasswordLoginInput, type PatLoginInput, type Premise, type Principal, type ProfileConfig, type ProtocolFact, type QueryResult, REASON_DUPLICATE_TOKEN, REASON_EMPTY, REASON_NO_ENTRIES, REASON_PROBE_ABSENT, REASON_PROBE_SHAPE, REASON_PROBE_UNVERIFIED, REASON_UNREAD, RECON_DIRNAME, RECON_SEGMENTS_BELOW_ROOT, RED_REASONS, REGISTRY_REASONS, REGISTRY_SCOPE, ROUND_EXIT_CODES, ROUND_STAMPED_PRODUCTS, ROUND_STATES, ROUND_SUBCOMMAND, type ReconIdentity, ReconLayout, type ReconProbe, type ReconSummary, type Red, RefStore, type RefreshInput, type RelationMeta, type RemoveResult, type ResponseCompletedPayload, type ResponseFailedPayload, type ResponsePartDeltaPayload, type ResponseStartedPayload, type ResumeSummary, type ResumeTurn, type RoundResult, type RoundState, type RowResult, type RowStatus, SCOPES, SDK_VERSION, SEGMENT_SEPARATOR, type SaveMetaResult, type ScanOptions, type ScanResult, type SendMessageOpts, type SettingItem, type SettingWrite, type StreamCancelPayload, type StreamCommand, type StreamCommandEnvelope, type StreamCommandType, type StreamEventEnvelope, type StreamEventType, TargetModelNotFound, type TaskProgressPayload, type TenantMetaEntry, type TimelineItemLite, type TokenRecord, TokenStore, type ToolCallFailedPayload, type ToolCallResultPayload, type ToolCallStartedPayload, type V4RenderSink, type V4StreamEventType, VERSION_PATH, type WorkspaceFileContent, type WorkspaceFileItem, type WorkspaceFileListResult, WsClient, type WsClientOpts, absoluteDownloadUrl, adminResetPassword, archiveDir, artifactReason, assertSafeReferencePath, assertSafeSkillFilePath, assertSafeSkillId, baselineRoundPremise, bootstrapTenant, boundedFind, buildAnswer, buildConfirm, buildInterrupt, buildMiniAppTemplateFiles, buildMiniPreviewTargets, buildMiniVerifyAgentReport, buildReject, buildResumeSummary, buildSteer, camelizeKeys, changePassword, checkGaps, checkPremise, checkReds, cjkRunAt, classifyMessage, clearMetaCache, clearModelCache, compareFingerprint, computeFingerprint, computeReconSourceKey, conversationStateFile, covered, create, createConversation, createHttpClient, createV4Reducer, createWorkspaceFile, credentialsFile, defaultDownloadDir, defaultMiniRequiredScopes, deleteEnv, deleteIdentity, deleteTenantMeta, deleteWorkspaceFile, deriveAclass, deriveNamelessToolLabel, describePrincipal, detectDelegationPause, detectDirectInteractionPending, detectPasswordChangeChallenge, displayWidth, downloadDocument, endpointsIn, ensureSessionFresh, envDir, envFile, envsDir, exitCode, exitCodeFor, extractNextSteps, factsIndex, fetchConversationTimeline, fetchConversationTimelineStrict, fetchRecentConversations, fetchReconIdentity, fetchSkillCatalog, fetchSkillFile, fetchSkillMarkdown, fetchSkillReference, filterByStage, findLastAssistantSeq, flattenSkills, foldConfusables, foldedNoteFor, formatMiniVerifyReport, formatObject, formatRows, fromAxiosError, getCacheStats, getMiniStatus, getSettingDomain, getTenantMeta, globalConfigFile, guessMimeType, hcmConfigDir, httpProbe, identitiesDir, identityDir, identityMetaFile, inGap, inferEnvName, inferMiniWritableTemplateOptionsFromModel, initMiniAppProject, inspectRound, isCjk, isDelegationToolName, isServerSlidingSession, listEnvs, listIdentities, listProfiles, listTenantMeta, listWorkspaceFiles, loadConversationState, loadEnv, loadGlobalConfig, loadIdentity, loadProfile, loadProtocolFacts, loadWorkspaceFileContent, loginClientCredentials, loginPairing, loginPassword, loginPat, matchSkills, migrateLegacyProfiles, modelEntries, modelKeys, needsRefresh, newRoundId, normalize, normalizeReferences, oneShot, parseConfirmToolName, parseFixture, parseInteractionRequest, parseInteractionResolved, parsePlaceholder, parseSettingAssignment, parseSkillFrontmatter, parseSkillRequirements, patchSettingDomain, probeExtensionSource, profileDir, profileFile, pullMiniAppProject, pushMiniAppProject, readBusinessKeyFields, readMiniContext, readPullManifest, refreshToken, remove, renderRound, replHistoryFile, resetSettingItem, resolveActiveEnv, resolveActiveIdentity, resolveActiveProfile, resolveAllSkillsInstallOrder, resolveChironBase, resolveRefs, resolveSkillInstallOrder, roundAge, runDoctor, runImport, runMiniSmokeChecks, runRecon, safeSegment, saveConversationState, saveEnv, saveGlobalConfig, saveIdentity, saveProfile, saveTenantMeta, saveWorkspaceFileContent, scan, segmentsOf, sendMessageAndStream, snakeToCamel, tenantSegment, toJson, toOrigin, toPrincipal, toTable, toYaml, truncateDisplay, unguardedKinds, update, uploadDocument, validateMiniProject };
|