@ozzylabs/feedradar 0.1.6 → 0.1.7
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/cli/dismiss.d.ts +2 -1
- package/dist/cli/dismiss.d.ts.map +1 -1
- package/dist/cli/dismiss.js +4 -1
- package/dist/cli/dismiss.js.map +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +7 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/items.d.ts +44 -0
- package/dist/cli/items.d.ts.map +1 -0
- package/dist/cli/items.js +288 -0
- package/dist/cli/items.js.map +1 -0
- package/dist/cli/research.d.ts +21 -0
- package/dist/cli/research.d.ts.map +1 -1
- package/dist/cli/research.js +54 -10
- package/dist/cli/research.js.map +1 -1
- package/dist/cli/review.d.ts +23 -0
- package/dist/cli/review.d.ts.map +1 -1
- package/dist/cli/review.js +293 -2
- package/dist/cli/review.js.map +1 -1
- package/dist/cli/triage.d.ts +136 -0
- package/dist/cli/triage.d.ts.map +1 -0
- package/dist/cli/triage.js +1110 -0
- package/dist/cli/triage.js.map +1 -0
- package/dist/cli/undismiss.d.ts +30 -0
- package/dist/cli/undismiss.d.ts.map +1 -0
- package/dist/cli/undismiss.js +133 -0
- package/dist/cli/undismiss.js.map +1 -0
- package/dist/cli/workflow/generate-combined-with-triage.d.ts +115 -0
- package/dist/cli/workflow/generate-combined-with-triage.d.ts.map +1 -0
- package/dist/cli/workflow/generate-combined-with-triage.js +446 -0
- package/dist/cli/workflow/generate-combined-with-triage.js.map +1 -0
- package/dist/cli/workflow.d.ts +6 -5
- package/dist/cli/workflow.d.ts.map +1 -1
- package/dist/cli/workflow.js +13 -8
- package/dist/cli/workflow.js.map +1 -1
- package/dist/core/recipes.d.ts.map +1 -1
- package/dist/core/recipes.js +6 -0
- package/dist/core/recipes.js.map +1 -1
- package/dist/core/transitions.d.ts +30 -0
- package/dist/core/transitions.d.ts.map +1 -0
- package/dist/core/transitions.js +103 -0
- package/dist/core/transitions.js.map +1 -0
- package/dist/core/triage/adapter.d.ts +80 -0
- package/dist/core/triage/adapter.d.ts.map +1 -0
- package/dist/core/triage/adapter.js +128 -0
- package/dist/core/triage/adapter.js.map +1 -0
- package/dist/core/triage/index.d.ts +105 -0
- package/dist/core/triage/index.d.ts.map +1 -0
- package/dist/core/triage/index.js +246 -0
- package/dist/core/triage/index.js.map +1 -0
- package/dist/core/triage/prompt.d.ts +30 -0
- package/dist/core/triage/prompt.d.ts.map +1 -0
- package/dist/core/triage/prompt.js +157 -0
- package/dist/core/triage/prompt.js.map +1 -0
- package/dist/core/triage/response.d.ts +114 -0
- package/dist/core/triage/response.d.ts.map +1 -0
- package/dist/core/triage/response.js +188 -0
- package/dist/core/triage/response.js.map +1 -0
- package/dist/recipes/aws-whats-new.yaml +29 -0
- package/dist/recipes/dev-to.yaml +24 -0
- package/dist/schemas/item.d.ts +151 -5
- package/dist/schemas/item.d.ts.map +1 -1
- package/dist/schemas/item.js +164 -4
- package/dist/schemas/item.js.map +1 -1
- package/dist/schemas/recipe.d.ts +10 -0
- package/dist/schemas/recipe.d.ts.map +1 -1
- package/dist/schemas/recipe.js +10 -1
- package/dist/schemas/recipe.js.map +1 -1
- package/dist/schemas/source.d.ts +43 -0
- package/dist/schemas/source.d.ts.map +1 -1
- package/dist/schemas/source.js +34 -0
- package/dist/schemas/source.js.map +1 -1
- package/dist/templates/agents/AGENTS.md +30 -0
- package/dist/templates/workflows/combined-with-triage.template.yaml.tmpl +133 -0
- package/package.json +1 -1
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { TriageDecisionValueSchema } from "../../schemas/item.js";
|
|
3
|
+
/**
|
|
4
|
+
* Triage response parser + validator (ADR-0018 §W4).
|
|
5
|
+
*
|
|
6
|
+
* The agent returns a JSON array (one entry per input item) on stdout. This
|
|
7
|
+
* module turns that raw string into a `Map<itemId, ValidatedTriageEntry>`,
|
|
8
|
+
* applying the safety rules from ADR-0018:
|
|
9
|
+
*
|
|
10
|
+
* 1. **Strict JSON parse.** Total parse failure is reported to the caller as
|
|
11
|
+
* a `TriageResponseParseError`; the caller (adapter.ts) decides whether
|
|
12
|
+
* to retry the agent invocation or fall back to all-unsure.
|
|
13
|
+
* 2. **Schema validate per entry.** Entries that fail Zod parse become
|
|
14
|
+
* `unsure` entries with a synthesized reason; only the malformed entry is
|
|
15
|
+
* downgraded, the rest of the array is kept.
|
|
16
|
+
* 3. **Hallucinated id reject.** Entries whose `id` is not in the input set
|
|
17
|
+
* are dropped from the result entirely (the caller's full-coverage check
|
|
18
|
+
* will turn the missing items into `unsure` entries with reason
|
|
19
|
+
* `"agent-omitted"`). Storing a hallucinated id on disk would corrupt the
|
|
20
|
+
* items index.
|
|
21
|
+
* 4. **Duplicate id reject.** When the agent emits two entries for the same
|
|
22
|
+
* id, the **first** is kept and the duplicate triggers a warning. This is
|
|
23
|
+
* safer than overwriting — agents that hallucinate duplicates often emit
|
|
24
|
+
* contradictory decisions, and the first is at least likely to reflect
|
|
25
|
+
* the policy more directly.
|
|
26
|
+
* 5. **Confidence threshold demotion.** Entries below
|
|
27
|
+
* `policy.confidenceThreshold` are demoted to `decision: "unsure"`. The
|
|
28
|
+
* original confidence is preserved so downstream feedback analysis can
|
|
29
|
+
* correlate "low confidence + demoted" outcomes.
|
|
30
|
+
* 6. **Digest without group → unsure.** A `decision: "digest"` entry without
|
|
31
|
+
* a non-empty `group` is structurally invalid (the digest CLI needs the
|
|
32
|
+
* key to collect siblings). We demote rather than reject so the operator
|
|
33
|
+
* still gets a record.
|
|
34
|
+
*
|
|
35
|
+
* The output is a `Map`, not an array, so the adapter can do O(1) coverage
|
|
36
|
+
* checks against the input id set.
|
|
37
|
+
*/
|
|
38
|
+
/**
|
|
39
|
+
* Raw schema for one element of the agent's JSON response. We accept any
|
|
40
|
+
* shape that has the four required fields (id / decision / confidence /
|
|
41
|
+
* reason) and an optional group; unknown fields are dropped silently so the
|
|
42
|
+
* agent has room to add metadata without breaking parse.
|
|
43
|
+
*
|
|
44
|
+
* `decision` is parsed via `TriageDecisionValueSchema` so the same enum is
|
|
45
|
+
* shared with `TriageDecisionSchema` on the item — any drift would be caught
|
|
46
|
+
* at this boundary instead of corrupting the items index.
|
|
47
|
+
*/
|
|
48
|
+
const AgentEntrySchema = z.object({
|
|
49
|
+
id: z.string().min(1),
|
|
50
|
+
decision: TriageDecisionValueSchema,
|
|
51
|
+
confidence: z.number().min(0).max(1),
|
|
52
|
+
reason: z.string().min(1),
|
|
53
|
+
group: z.string().min(1).optional(),
|
|
54
|
+
});
|
|
55
|
+
/**
|
|
56
|
+
* The whole response body is just an array of entries. We extract this as a
|
|
57
|
+
* named schema so the malformed-array error message stays consistent across
|
|
58
|
+
* test cases.
|
|
59
|
+
*/
|
|
60
|
+
const AgentResponseSchema = z.array(AgentEntrySchema);
|
|
61
|
+
/**
|
|
62
|
+
* Thrown by `parseTriageResponse` when the agent's stdout could not be
|
|
63
|
+
* parsed as JSON at all, or when the top-level value is not an array of
|
|
64
|
+
* entries. The adapter catches this and treats it as a total-fallback
|
|
65
|
+
* situation (every item becomes `triaged_unsure`, `fallback: true`).
|
|
66
|
+
*
|
|
67
|
+
* Per-entry validation failures (one entry malformed but the array parses)
|
|
68
|
+
* do NOT throw — they are recorded in `warnings[]` and the malformed entry
|
|
69
|
+
* is dropped so the rest of the array still applies.
|
|
70
|
+
*/
|
|
71
|
+
export class TriageResponseParseError extends Error {
|
|
72
|
+
constructor(message) {
|
|
73
|
+
super(message);
|
|
74
|
+
this.name = "TriageResponseParseError";
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Best-effort JSON extraction from agent stdout.
|
|
79
|
+
*
|
|
80
|
+
* Cheap models occasionally wrap their JSON in Markdown code fences
|
|
81
|
+
* (```json ... ```) or prepend / append a sentence even when instructed not
|
|
82
|
+
* to. We strip a leading / trailing code fence and locate the outermost
|
|
83
|
+
* `[ ... ]` slice so the JSON.parse call has a fighting chance. If neither
|
|
84
|
+
* heuristic helps, we let `JSON.parse` fail and propagate the error.
|
|
85
|
+
*/
|
|
86
|
+
function extractJsonArrayPayload(raw) {
|
|
87
|
+
const trimmed = raw.trim();
|
|
88
|
+
// Strip a single ```json / ``` ... ``` fence if present.
|
|
89
|
+
const fenceMatch = trimmed.match(/^```(?:json)?\s*\n([\s\S]*?)\n```$/);
|
|
90
|
+
const fenced = fenceMatch ? fenceMatch[1].trim() : trimmed;
|
|
91
|
+
// If the agent already emitted a clean array, return it directly.
|
|
92
|
+
if (fenced.startsWith("[")) {
|
|
93
|
+
return fenced;
|
|
94
|
+
}
|
|
95
|
+
// Otherwise locate the first `[` ... last `]` slice. This is intentionally
|
|
96
|
+
// greedy: cheap-model preamble usually sits before `[`, so trimming to the
|
|
97
|
+
// outermost brackets recovers the array.
|
|
98
|
+
const first = fenced.indexOf("[");
|
|
99
|
+
const last = fenced.lastIndexOf("]");
|
|
100
|
+
if (first === -1 || last === -1 || last <= first) {
|
|
101
|
+
return fenced;
|
|
102
|
+
}
|
|
103
|
+
return fenced.slice(first, last + 1);
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Parse and validate the agent's triage response against the input item set
|
|
107
|
+
* and per-source policy.
|
|
108
|
+
*
|
|
109
|
+
* `inputItems` is consulted for two reasons: (a) hallucinated-id reject — we
|
|
110
|
+
* only accept ids that appear in the input set, and (b) the caller (adapter)
|
|
111
|
+
* uses the returned `entries` Map to figure out which items the agent
|
|
112
|
+
* omitted entirely (those become `unsure` with reason `"agent-omitted"`).
|
|
113
|
+
*/
|
|
114
|
+
export function parseTriageResponse(raw, inputItems, policy) {
|
|
115
|
+
const payload = extractJsonArrayPayload(raw);
|
|
116
|
+
let parsedJson;
|
|
117
|
+
try {
|
|
118
|
+
parsedJson = JSON.parse(payload);
|
|
119
|
+
}
|
|
120
|
+
catch (err) {
|
|
121
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
122
|
+
throw new TriageResponseParseError(`triage response is not valid JSON: ${message}`);
|
|
123
|
+
}
|
|
124
|
+
const arrayResult = AgentResponseSchema.safeParse(parsedJson);
|
|
125
|
+
if (!arrayResult.success) {
|
|
126
|
+
// If the top-level shape failed (e.g. agent returned an object instead of
|
|
127
|
+
// an array), bail to the total-fallback path. Per-entry shape errors are
|
|
128
|
+
// handled below (we still get a partial array there).
|
|
129
|
+
if (!Array.isArray(parsedJson)) {
|
|
130
|
+
throw new TriageResponseParseError(`triage response top-level value is not an array: ${arrayResult.error.message}`);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
const inputIds = new Set(inputItems.map((i) => i.id));
|
|
134
|
+
const entries = new Map();
|
|
135
|
+
const warnings = [];
|
|
136
|
+
// Iterate the raw parsed JSON (which we know is an array at this point) so
|
|
137
|
+
// we can per-entry validate and gather warnings without aborting on the
|
|
138
|
+
// first malformed entry. We re-run AgentEntrySchema per element to get
|
|
139
|
+
// precise error messages.
|
|
140
|
+
const rawArray = Array.isArray(parsedJson) ? parsedJson : [];
|
|
141
|
+
for (let idx = 0; idx < rawArray.length; idx++) {
|
|
142
|
+
const entryResult = AgentEntrySchema.safeParse(rawArray[idx]);
|
|
143
|
+
if (!entryResult.success) {
|
|
144
|
+
warnings.push(`entry[${idx}] failed schema validation: ${entryResult.error.issues
|
|
145
|
+
.map((i) => `${i.path.join(".") || "<root>"}: ${i.message}`)
|
|
146
|
+
.join("; ")}`);
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
const entry = entryResult.data;
|
|
150
|
+
if (!inputIds.has(entry.id)) {
|
|
151
|
+
warnings.push(`entry[${idx}] references unknown id "${entry.id}" (hallucinated, rejected)`);
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
if (entries.has(entry.id)) {
|
|
155
|
+
warnings.push(`entry[${idx}] duplicates id "${entry.id}" (kept first, ignored)`);
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
// Apply demotion rules. We track the original decision in the warning so
|
|
159
|
+
// the operator (or feedback CLI) can see why a low-confidence research
|
|
160
|
+
// decision became unsure.
|
|
161
|
+
let decision = entry.decision;
|
|
162
|
+
let reason = entry.reason;
|
|
163
|
+
let demoted = false;
|
|
164
|
+
if (decision === "digest" && (entry.group === undefined || entry.group.trim() === "")) {
|
|
165
|
+
decision = "unsure";
|
|
166
|
+
reason = `digest decision without group key (demoted from "digest"): ${entry.reason}`;
|
|
167
|
+
demoted = true;
|
|
168
|
+
warnings.push(`entry[${idx}] "${entry.id}" demoted: digest without group key`);
|
|
169
|
+
}
|
|
170
|
+
if (decision !== "unsure" && entry.confidence < policy.confidenceThreshold) {
|
|
171
|
+
const original = decision;
|
|
172
|
+
decision = "unsure";
|
|
173
|
+
reason = `confidence ${entry.confidence.toFixed(2)} below threshold ${policy.confidenceThreshold.toFixed(2)} (demoted from "${original}"): ${entry.reason}`;
|
|
174
|
+
demoted = true;
|
|
175
|
+
warnings.push(`entry[${idx}] "${entry.id}" demoted: confidence ${entry.confidence.toFixed(2)} < threshold ${policy.confidenceThreshold.toFixed(2)}`);
|
|
176
|
+
}
|
|
177
|
+
entries.set(entry.id, {
|
|
178
|
+
id: entry.id,
|
|
179
|
+
decision,
|
|
180
|
+
confidence: entry.confidence,
|
|
181
|
+
reason,
|
|
182
|
+
group: decision === "digest" ? entry.group : undefined,
|
|
183
|
+
demoted,
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
return { entries, warnings };
|
|
187
|
+
}
|
|
188
|
+
//# sourceMappingURL=response.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"response.js","sourceRoot":"","sources":["../../../src/core/triage/response.ts"],"names":[],"mappings":"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"}
|
|
@@ -85,3 +85,32 @@ jsonSelectors:
|
|
|
85
85
|
summary: $.additionalFields.postBody
|
|
86
86
|
publisherId: $.id
|
|
87
87
|
trustLevel: untrusted
|
|
88
|
+
# Default triage policy bundled with this recipe (ADR-0018 §W3 / #241).
|
|
89
|
+
# Propagates onto `sources/<id>.yaml > triagePolicy:` when the user runs
|
|
90
|
+
# `radar source add <id> --recipe aws-whats-new`. The rules block is
|
|
91
|
+
# free-form markdown — see docs/user-guide.md "triage workflow" §policy
|
|
92
|
+
# 書き方ガイド for tuning tips. Cheap-model channel (gemini-2.5-flash-lite)
|
|
93
|
+
# keeps per-source triage cost well below $0.01/month even at AWS's volume
|
|
94
|
+
# (~700 items/month at peak).
|
|
95
|
+
triagePolicy:
|
|
96
|
+
agent: gemini-cli
|
|
97
|
+
confidenceThreshold: 0.7
|
|
98
|
+
rules: |
|
|
99
|
+
重要 (research):
|
|
100
|
+
- 新サービス / 新機能の GA, Preview ローンチ
|
|
101
|
+
- 価格改定 / 料金体系変更
|
|
102
|
+
- リブランド・ブランド統合 (例: QuickSight → Quick)
|
|
103
|
+
- セキュリティ関連 (脆弱性対応、認証方式変更)
|
|
104
|
+
- メジャーな API 変更 / 既存サービスの semantics 変更
|
|
105
|
+
集約 (digest):
|
|
106
|
+
- 既存サービスへの incremental な機能追加 (UI option, filter 追加)
|
|
107
|
+
group: ui-incremental
|
|
108
|
+
- 連携サービス追加 (新 connector, 新 integration)
|
|
109
|
+
group: integrations
|
|
110
|
+
- performance 改善・SLA 向上 announcement
|
|
111
|
+
group: performance-sla
|
|
112
|
+
除外 (dismiss):
|
|
113
|
+
- リージョン拡張のみ (例: "now available in <region>")
|
|
114
|
+
- SDK バージョン bump
|
|
115
|
+
- ドキュメント更新通知
|
|
116
|
+
- 既存リソースの minor 設定追加 (例: "now supports tagging")
|
package/dist/recipes/dev-to.yaml
CHANGED
|
@@ -38,3 +38,27 @@ pagination:
|
|
|
38
38
|
# `jsonSelectors` omitted intentionally: the default chain (ADR-0012 §D2 /
|
|
39
39
|
# #174) handles the dev.to shape end-to-end.
|
|
40
40
|
trustLevel: untrusted
|
|
41
|
+
# Default triage policy bundled with this recipe (ADR-0018 §W3 / #241).
|
|
42
|
+
# dev.to mixes technical deep-dives with marketing posts and "hello world"
|
|
43
|
+
# tutorials, so the rules below bias toward research for framework /
|
|
44
|
+
# performance / security topics, digest tutorial round-ups, and dismiss
|
|
45
|
+
# personal promo / intro content. Adjust per workspace by overriding the
|
|
46
|
+
# generated `sources/<id>.yaml > triagePolicy.rules:` block.
|
|
47
|
+
triagePolicy:
|
|
48
|
+
agent: gemini-cli
|
|
49
|
+
confidenceThreshold: 0.7
|
|
50
|
+
rules: |
|
|
51
|
+
重要 (research):
|
|
52
|
+
- 新フレームワーク / 新ツールの紹介 / GA 発表記事
|
|
53
|
+
- performance benchmark / architecture deep-dive
|
|
54
|
+
- security 脆弱性報告 / CVE 解説
|
|
55
|
+
- メジャーアップデート (vN.0 リリース解説)
|
|
56
|
+
集約 (digest):
|
|
57
|
+
- tutorial 系の週次 round-up (関連トピック数本まとめ)
|
|
58
|
+
group: tutorial-roundup
|
|
59
|
+
- "Top N libraries for X" のようなキュレーション記事
|
|
60
|
+
group: curation
|
|
61
|
+
除外 (dismiss):
|
|
62
|
+
- 個人ブログ宣伝 / SaaS プロダクト promo
|
|
63
|
+
- 入門記事 ("hello world", "what is X" 系)
|
|
64
|
+
- "I built X in N hours" 系の体験記
|
package/dist/schemas/item.d.ts
CHANGED
|
@@ -1,21 +1,139 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
/**
|
|
3
|
-
* Item status state machine (ADR-0008).
|
|
3
|
+
* Item status state machine (ADR-0008 + ADR-0018).
|
|
4
|
+
*
|
|
5
|
+
* Original 4-state machine:
|
|
4
6
|
*
|
|
5
7
|
* detected ──► (dismissed | researched) ──► reviewed
|
|
6
8
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
+
* Triage extension (ADR-0018) adds 3 intermediate states. `triaged_dismiss`
|
|
10
|
+
* is intentionally **not** a separate status — it is collapsed into existing
|
|
11
|
+
* `dismissed` with the `dismissedBy: "human" | "triage_<agent>"` sub-field
|
|
12
|
+
* recording the origin. Total status count: 4 → 7.
|
|
13
|
+
*
|
|
14
|
+
* detected
|
|
15
|
+
* ├── triage ──► triaged_research ──► researched ──► reviewed
|
|
16
|
+
* ├── triage ──► triaged_digest ──► researched (digest 合流)
|
|
17
|
+
* ├── triage ──► triaged_unsure ──► (human loop) ──► research / dismiss
|
|
18
|
+
* └── triage ──► dismissed (dismissedBy: triage_<agent>)
|
|
19
|
+
*
|
|
20
|
+
* dismissed ──► detected (via `radar undismiss`)
|
|
21
|
+
*
|
|
22
|
+
* Status semantics (per ADR-0018 §W-B):
|
|
23
|
+
*
|
|
24
|
+
* - `detected`: watch run emitted the item after filter, triage not yet run
|
|
25
|
+
* - `triaged_research`: triage classified as research-worthy
|
|
26
|
+
* - `triaged_digest`: triage classified as digest candidate (group key in `triage.group`)
|
|
27
|
+
* - `triaged_unsure`: triage confidence below threshold; human judgment needed
|
|
9
28
|
* - `researched`: research report written
|
|
10
29
|
* - `reviewed`: research report reviewed (terminal happy path)
|
|
30
|
+
* - `dismissed`: human or triage agent decided not to research (terminal,
|
|
31
|
+
* reversible via `radar undismiss`; origin in `dismissedBy`)
|
|
11
32
|
*/
|
|
12
33
|
export declare const ItemStatusSchema: z.ZodEnum<{
|
|
13
34
|
detected: "detected";
|
|
14
|
-
|
|
35
|
+
triaged_research: "triaged_research";
|
|
36
|
+
triaged_digest: "triaged_digest";
|
|
37
|
+
triaged_unsure: "triaged_unsure";
|
|
15
38
|
researched: "researched";
|
|
16
39
|
reviewed: "reviewed";
|
|
40
|
+
dismissed: "dismissed";
|
|
17
41
|
}>;
|
|
18
42
|
export type ItemStatus = z.infer<typeof ItemStatusSchema>;
|
|
43
|
+
/**
|
|
44
|
+
* Decision values produced by a triage agent (ADR-0018).
|
|
45
|
+
*
|
|
46
|
+
* - `research`: item is research-worthy on its own
|
|
47
|
+
* - `digest`: item belongs to a group worth summarizing as a single digest
|
|
48
|
+
* - `dismiss`: item is not research-worthy; collapse into `dismissed`
|
|
49
|
+
* - `unsure`: triage agent's confidence is below `confidenceThreshold`; defer
|
|
50
|
+
* to human judgment
|
|
51
|
+
*/
|
|
52
|
+
export declare const TriageDecisionValueSchema: z.ZodEnum<{
|
|
53
|
+
research: "research";
|
|
54
|
+
digest: "digest";
|
|
55
|
+
dismiss: "dismiss";
|
|
56
|
+
unsure: "unsure";
|
|
57
|
+
}>;
|
|
58
|
+
export type TriageDecisionValue = z.infer<typeof TriageDecisionValueSchema>;
|
|
59
|
+
/**
|
|
60
|
+
* One feedback datapoint on a prior triage decision (ADR-0018 §W5).
|
|
61
|
+
*
|
|
62
|
+
* `radar triage feedback <item-id> --correct | --wrong [--reason <text>]`
|
|
63
|
+
* appends to the array. Stored as a list so multiple reviewers (or the same
|
|
64
|
+
* reviewer revisiting later) can leave independent verdicts without
|
|
65
|
+
* overwriting each other; downstream stats aggregations decide how to
|
|
66
|
+
* combine them.
|
|
67
|
+
*/
|
|
68
|
+
export declare const TriageFeedbackSchema: z.ZodObject<{
|
|
69
|
+
correct: z.ZodBoolean;
|
|
70
|
+
reason: z.ZodOptional<z.ZodString>;
|
|
71
|
+
feedbackAt: z.ZodString;
|
|
72
|
+
}, z.core.$strip>;
|
|
73
|
+
export type TriageFeedback = z.infer<typeof TriageFeedbackSchema>;
|
|
74
|
+
/**
|
|
75
|
+
* Triage decision attached to an item by a triage agent (ADR-0018 §W2 / §W5).
|
|
76
|
+
*
|
|
77
|
+
* Schema rationale (post-review #238 W-I):
|
|
78
|
+
*
|
|
79
|
+
* - The outer field on `Item` is the **singular** `triage` (not `triages`).
|
|
80
|
+
* This preserves the option of moving to a multi-agent shape (`triage: {
|
|
81
|
+
* decisions: [...], consensus: ... }`) later via a Zod union without
|
|
82
|
+
* renaming the field. PR-1 ships the single-agent inner shape only;
|
|
83
|
+
* multi-agent extension is a future PR and explicitly NOT in scope.
|
|
84
|
+
* - `decision` is a discriminated string, not a nested object, so the same
|
|
85
|
+
* union-extension pattern can reuse the field name on the inner decision
|
|
86
|
+
* list without ambiguity.
|
|
87
|
+
* - `feedback` is an array, not a single object — multiple human reviewers
|
|
88
|
+
* can leave independent verdicts (see `TriageFeedbackSchema` docstring).
|
|
89
|
+
*/
|
|
90
|
+
export declare const TriageDecisionSchema: z.ZodObject<{
|
|
91
|
+
decision: z.ZodEnum<{
|
|
92
|
+
research: "research";
|
|
93
|
+
digest: "digest";
|
|
94
|
+
dismiss: "dismiss";
|
|
95
|
+
unsure: "unsure";
|
|
96
|
+
}>;
|
|
97
|
+
confidence: z.ZodNumber;
|
|
98
|
+
reason: z.ZodString;
|
|
99
|
+
group: z.ZodOptional<z.ZodString>;
|
|
100
|
+
agent: z.ZodString;
|
|
101
|
+
triagedAt: z.ZodString;
|
|
102
|
+
feedback: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
103
|
+
correct: z.ZodBoolean;
|
|
104
|
+
reason: z.ZodOptional<z.ZodString>;
|
|
105
|
+
feedbackAt: z.ZodString;
|
|
106
|
+
}, z.core.$strip>>>;
|
|
107
|
+
}, z.core.$strip>;
|
|
108
|
+
export type TriageDecision = z.infer<typeof TriageDecisionSchema>;
|
|
109
|
+
/**
|
|
110
|
+
* Origin of a `dismissed` decision (ADR-0018 §W2 / §W6).
|
|
111
|
+
*
|
|
112
|
+
* Distinguishes human dismiss from triage-agent dismiss so `radar undismiss`
|
|
113
|
+
* can apply the right safety behavior: triage-origin dismisses are reversible
|
|
114
|
+
* without a flag, human-origin dismisses require `--force` (= confirms the
|
|
115
|
+
* user is overriding their own prior decision, not just an agent's).
|
|
116
|
+
*
|
|
117
|
+
* Defined as a string enum (not a union with `AgentIdSchema`) because the
|
|
118
|
+
* `triage_` prefix lets schema validation reject malformed values like
|
|
119
|
+
* `"claude-code"` (missing prefix) that would silently collide with the
|
|
120
|
+
* `human` case.
|
|
121
|
+
*
|
|
122
|
+
* Maintenance note: this enum mirrors `AgentIdSchema` (`src/schemas/research.ts`)
|
|
123
|
+
* with a `triage_` prefix. When a new agent adapter is added, both enums
|
|
124
|
+
* must be updated in lockstep. A test (`tests/schemas/item.test.ts`)
|
|
125
|
+
* iterates each `AgentIdSchema` value and asserts the corresponding
|
|
126
|
+
* `triage_<agent>` variant parses; that test will fail loudly if the two
|
|
127
|
+
* lists drift out of sync.
|
|
128
|
+
*/
|
|
129
|
+
export declare const DismissedBySchema: z.ZodEnum<{
|
|
130
|
+
human: "human";
|
|
131
|
+
"triage_claude-code": "triage_claude-code";
|
|
132
|
+
"triage_codex-cli": "triage_codex-cli";
|
|
133
|
+
"triage_gemini-cli": "triage_gemini-cli";
|
|
134
|
+
triage_copilot: "triage_copilot";
|
|
135
|
+
}>;
|
|
136
|
+
export type DismissedBy = z.infer<typeof DismissedBySchema>;
|
|
19
137
|
export declare const ItemSchema: z.ZodObject<{
|
|
20
138
|
id: z.ZodString;
|
|
21
139
|
sourceId: z.ZodString;
|
|
@@ -28,11 +146,39 @@ export declare const ItemSchema: z.ZodObject<{
|
|
|
28
146
|
matchedKeywords: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
29
147
|
status: z.ZodDefault<z.ZodEnum<{
|
|
30
148
|
detected: "detected";
|
|
31
|
-
|
|
149
|
+
triaged_research: "triaged_research";
|
|
150
|
+
triaged_digest: "triaged_digest";
|
|
151
|
+
triaged_unsure: "triaged_unsure";
|
|
32
152
|
researched: "researched";
|
|
33
153
|
reviewed: "reviewed";
|
|
154
|
+
dismissed: "dismissed";
|
|
34
155
|
}>>;
|
|
35
156
|
injectionFlags: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
157
|
+
triage: z.ZodOptional<z.ZodObject<{
|
|
158
|
+
decision: z.ZodEnum<{
|
|
159
|
+
research: "research";
|
|
160
|
+
digest: "digest";
|
|
161
|
+
dismiss: "dismiss";
|
|
162
|
+
unsure: "unsure";
|
|
163
|
+
}>;
|
|
164
|
+
confidence: z.ZodNumber;
|
|
165
|
+
reason: z.ZodString;
|
|
166
|
+
group: z.ZodOptional<z.ZodString>;
|
|
167
|
+
agent: z.ZodString;
|
|
168
|
+
triagedAt: z.ZodString;
|
|
169
|
+
feedback: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
170
|
+
correct: z.ZodBoolean;
|
|
171
|
+
reason: z.ZodOptional<z.ZodString>;
|
|
172
|
+
feedbackAt: z.ZodString;
|
|
173
|
+
}, z.core.$strip>>>;
|
|
174
|
+
}, z.core.$strip>>;
|
|
175
|
+
dismissedBy: z.ZodOptional<z.ZodEnum<{
|
|
176
|
+
human: "human";
|
|
177
|
+
"triage_claude-code": "triage_claude-code";
|
|
178
|
+
"triage_codex-cli": "triage_codex-cli";
|
|
179
|
+
"triage_gemini-cli": "triage_gemini-cli";
|
|
180
|
+
triage_copilot: "triage_copilot";
|
|
181
|
+
}>>;
|
|
36
182
|
}, z.core.$strip>;
|
|
37
183
|
export type Item = z.infer<typeof ItemSchema>;
|
|
38
184
|
//# sourceMappingURL=item.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"item.d.ts","sourceRoot":"","sources":["../../src/schemas/item.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB
|
|
1
|
+
{"version":3,"file":"item.d.ts","sourceRoot":"","sources":["../../src/schemas/item.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,eAAO,MAAM,gBAAgB;;;;;;;;EAQ3B,CAAC;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE1D;;;;;;;;GAQG;AACH,eAAO,MAAM,yBAAyB;;;;;EAAsD,CAAC;AAC7F,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAE5E;;;;;;;;GAQG;AACH,eAAO,MAAM,oBAAoB;;;;iBAW/B,CAAC;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAElE;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;iBAmC/B,CAAC;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAElE;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,iBAAiB;;;;;;EAM5B,CAAC;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAE5D,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAyCrB,CAAC;AACH,MAAM,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC"}
|
package/dist/schemas/item.js
CHANGED
|
@@ -1,15 +1,154 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
/**
|
|
3
|
-
* Item status state machine (ADR-0008).
|
|
3
|
+
* Item status state machine (ADR-0008 + ADR-0018).
|
|
4
|
+
*
|
|
5
|
+
* Original 4-state machine:
|
|
4
6
|
*
|
|
5
7
|
* detected ──► (dismissed | researched) ──► reviewed
|
|
6
8
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
+
* Triage extension (ADR-0018) adds 3 intermediate states. `triaged_dismiss`
|
|
10
|
+
* is intentionally **not** a separate status — it is collapsed into existing
|
|
11
|
+
* `dismissed` with the `dismissedBy: "human" | "triage_<agent>"` sub-field
|
|
12
|
+
* recording the origin. Total status count: 4 → 7.
|
|
13
|
+
*
|
|
14
|
+
* detected
|
|
15
|
+
* ├── triage ──► triaged_research ──► researched ──► reviewed
|
|
16
|
+
* ├── triage ──► triaged_digest ──► researched (digest 合流)
|
|
17
|
+
* ├── triage ──► triaged_unsure ──► (human loop) ──► research / dismiss
|
|
18
|
+
* └── triage ──► dismissed (dismissedBy: triage_<agent>)
|
|
19
|
+
*
|
|
20
|
+
* dismissed ──► detected (via `radar undismiss`)
|
|
21
|
+
*
|
|
22
|
+
* Status semantics (per ADR-0018 §W-B):
|
|
23
|
+
*
|
|
24
|
+
* - `detected`: watch run emitted the item after filter, triage not yet run
|
|
25
|
+
* - `triaged_research`: triage classified as research-worthy
|
|
26
|
+
* - `triaged_digest`: triage classified as digest candidate (group key in `triage.group`)
|
|
27
|
+
* - `triaged_unsure`: triage confidence below threshold; human judgment needed
|
|
9
28
|
* - `researched`: research report written
|
|
10
29
|
* - `reviewed`: research report reviewed (terminal happy path)
|
|
30
|
+
* - `dismissed`: human or triage agent decided not to research (terminal,
|
|
31
|
+
* reversible via `radar undismiss`; origin in `dismissedBy`)
|
|
32
|
+
*/
|
|
33
|
+
export const ItemStatusSchema = z.enum([
|
|
34
|
+
"detected",
|
|
35
|
+
"triaged_research",
|
|
36
|
+
"triaged_digest",
|
|
37
|
+
"triaged_unsure",
|
|
38
|
+
"researched",
|
|
39
|
+
"reviewed",
|
|
40
|
+
"dismissed",
|
|
41
|
+
]);
|
|
42
|
+
/**
|
|
43
|
+
* Decision values produced by a triage agent (ADR-0018).
|
|
44
|
+
*
|
|
45
|
+
* - `research`: item is research-worthy on its own
|
|
46
|
+
* - `digest`: item belongs to a group worth summarizing as a single digest
|
|
47
|
+
* - `dismiss`: item is not research-worthy; collapse into `dismissed`
|
|
48
|
+
* - `unsure`: triage agent's confidence is below `confidenceThreshold`; defer
|
|
49
|
+
* to human judgment
|
|
50
|
+
*/
|
|
51
|
+
export const TriageDecisionValueSchema = z.enum(["research", "digest", "dismiss", "unsure"]);
|
|
52
|
+
/**
|
|
53
|
+
* One feedback datapoint on a prior triage decision (ADR-0018 §W5).
|
|
54
|
+
*
|
|
55
|
+
* `radar triage feedback <item-id> --correct | --wrong [--reason <text>]`
|
|
56
|
+
* appends to the array. Stored as a list so multiple reviewers (or the same
|
|
57
|
+
* reviewer revisiting later) can leave independent verdicts without
|
|
58
|
+
* overwriting each other; downstream stats aggregations decide how to
|
|
59
|
+
* combine them.
|
|
60
|
+
*/
|
|
61
|
+
export const TriageFeedbackSchema = z.object({
|
|
62
|
+
/**
|
|
63
|
+
* `true` when the human agrees with the triage decision; `false` when they
|
|
64
|
+
* judge it wrong. Stored as a boolean rather than free-text verdict so the
|
|
65
|
+
* `radar triage stats` aggregation (#242) has a deterministic field to
|
|
66
|
+
* count on.
|
|
67
|
+
*/
|
|
68
|
+
correct: z.boolean(),
|
|
69
|
+
/** Optional rationale shown alongside `--wrong` (or `--correct` for nuance). */
|
|
70
|
+
reason: z.string().optional(),
|
|
71
|
+
feedbackAt: z.string().datetime(),
|
|
72
|
+
});
|
|
73
|
+
/**
|
|
74
|
+
* Triage decision attached to an item by a triage agent (ADR-0018 §W2 / §W5).
|
|
75
|
+
*
|
|
76
|
+
* Schema rationale (post-review #238 W-I):
|
|
77
|
+
*
|
|
78
|
+
* - The outer field on `Item` is the **singular** `triage` (not `triages`).
|
|
79
|
+
* This preserves the option of moving to a multi-agent shape (`triage: {
|
|
80
|
+
* decisions: [...], consensus: ... }`) later via a Zod union without
|
|
81
|
+
* renaming the field. PR-1 ships the single-agent inner shape only;
|
|
82
|
+
* multi-agent extension is a future PR and explicitly NOT in scope.
|
|
83
|
+
* - `decision` is a discriminated string, not a nested object, so the same
|
|
84
|
+
* union-extension pattern can reuse the field name on the inner decision
|
|
85
|
+
* list without ambiguity.
|
|
86
|
+
* - `feedback` is an array, not a single object — multiple human reviewers
|
|
87
|
+
* can leave independent verdicts (see `TriageFeedbackSchema` docstring).
|
|
11
88
|
*/
|
|
12
|
-
export const
|
|
89
|
+
export const TriageDecisionSchema = z.object({
|
|
90
|
+
decision: TriageDecisionValueSchema,
|
|
91
|
+
/**
|
|
92
|
+
* Triage agent's self-reported confidence in the decision. Compared against
|
|
93
|
+
* the source's `triagePolicy.confidenceThreshold` (default 0.7) to decide
|
|
94
|
+
* whether to promote `dismiss` / `research` / `digest` outright or
|
|
95
|
+
* downgrade to `unsure` for human review. Stored regardless of outcome so
|
|
96
|
+
* later feedback analysis can correlate confidence with correctness.
|
|
97
|
+
*/
|
|
98
|
+
confidence: z.number().min(0).max(1),
|
|
99
|
+
/** Short natural-language rationale from the triage agent. */
|
|
100
|
+
reason: z.string().min(1),
|
|
101
|
+
/**
|
|
102
|
+
* Grouping key used when `decision === "digest"`. Stored as a free-form
|
|
103
|
+
* slug — `radar research --digest --triage-group <group>` collects every
|
|
104
|
+
* `triaged_digest` item sharing this key (ADR-0018 §W-H). Optional because
|
|
105
|
+
* non-digest decisions do not produce a group.
|
|
106
|
+
*/
|
|
107
|
+
group: z.string().optional(),
|
|
108
|
+
/**
|
|
109
|
+
* Identifier of the agent that produced the decision. Free-form string
|
|
110
|
+
* rather than `AgentIdSchema` because the triage channel may use a more
|
|
111
|
+
* specific model identifier (e.g. `"gemini-2.5-flash-lite"`) than the
|
|
112
|
+
* coarse adapter id used elsewhere.
|
|
113
|
+
*/
|
|
114
|
+
agent: z.string().min(1),
|
|
115
|
+
triagedAt: z.string().datetime(),
|
|
116
|
+
/**
|
|
117
|
+
* Append-only feedback log. Defaults to `[]` so items written before the
|
|
118
|
+
* feedback CLI runs (the common case) deserialize cleanly. Existing items
|
|
119
|
+
* predating ADR-0018 have no `triage` field at all and therefore never
|
|
120
|
+
* need a default for this nested array — the default exists for the
|
|
121
|
+
* `triage` exists, `feedback` not yet populated case.
|
|
122
|
+
*/
|
|
123
|
+
feedback: z.array(TriageFeedbackSchema).default([]),
|
|
124
|
+
});
|
|
125
|
+
/**
|
|
126
|
+
* Origin of a `dismissed` decision (ADR-0018 §W2 / §W6).
|
|
127
|
+
*
|
|
128
|
+
* Distinguishes human dismiss from triage-agent dismiss so `radar undismiss`
|
|
129
|
+
* can apply the right safety behavior: triage-origin dismisses are reversible
|
|
130
|
+
* without a flag, human-origin dismisses require `--force` (= confirms the
|
|
131
|
+
* user is overriding their own prior decision, not just an agent's).
|
|
132
|
+
*
|
|
133
|
+
* Defined as a string enum (not a union with `AgentIdSchema`) because the
|
|
134
|
+
* `triage_` prefix lets schema validation reject malformed values like
|
|
135
|
+
* `"claude-code"` (missing prefix) that would silently collide with the
|
|
136
|
+
* `human` case.
|
|
137
|
+
*
|
|
138
|
+
* Maintenance note: this enum mirrors `AgentIdSchema` (`src/schemas/research.ts`)
|
|
139
|
+
* with a `triage_` prefix. When a new agent adapter is added, both enums
|
|
140
|
+
* must be updated in lockstep. A test (`tests/schemas/item.test.ts`)
|
|
141
|
+
* iterates each `AgentIdSchema` value and asserts the corresponding
|
|
142
|
+
* `triage_<agent>` variant parses; that test will fail loudly if the two
|
|
143
|
+
* lists drift out of sync.
|
|
144
|
+
*/
|
|
145
|
+
export const DismissedBySchema = z.enum([
|
|
146
|
+
"human",
|
|
147
|
+
"triage_claude-code",
|
|
148
|
+
"triage_codex-cli",
|
|
149
|
+
"triage_gemini-cli",
|
|
150
|
+
"triage_copilot",
|
|
151
|
+
]);
|
|
13
152
|
export const ItemSchema = z.object({
|
|
14
153
|
id: z.string().min(1),
|
|
15
154
|
sourceId: z.string().min(1),
|
|
@@ -30,5 +169,26 @@ export const ItemSchema = z.object({
|
|
|
30
169
|
* automatic.
|
|
31
170
|
*/
|
|
32
171
|
injectionFlags: z.array(z.string()).default([]),
|
|
172
|
+
/**
|
|
173
|
+
* Triage decision attached by `radar triage` (ADR-0018). Optional so:
|
|
174
|
+
*
|
|
175
|
+
* 1. Items written before PR-1 (no `triage:` key in YAML) validate cleanly
|
|
176
|
+
* (W-F migration requirement).
|
|
177
|
+
* 2. Items detected after PR-1 but before triage runs also validate.
|
|
178
|
+
*
|
|
179
|
+
* Field name is intentionally singular even though a future multi-agent
|
|
180
|
+
* triage extension will hold an array of decisions — the migration plan
|
|
181
|
+
* is to widen this field to `z.union([TriageDecisionSchema, MultiAgentTriageSchema])`
|
|
182
|
+
* without renaming, preserving on-disk compat (W-I post-review).
|
|
183
|
+
*/
|
|
184
|
+
triage: TriageDecisionSchema.optional(),
|
|
185
|
+
/**
|
|
186
|
+
* Origin of a `dismissed` decision (ADR-0018 §W6). Only meaningful when
|
|
187
|
+
* `status === "dismissed"`; we intentionally do NOT enforce this with a
|
|
188
|
+
* superRefine so legacy `dismissed` items (no `dismissedBy` field, written
|
|
189
|
+
* before ADR-0018) keep validating. Consumers needing origin information
|
|
190
|
+
* should treat `undefined` as "human" for those legacy items.
|
|
191
|
+
*/
|
|
192
|
+
dismissedBy: DismissedBySchema.optional(),
|
|
33
193
|
});
|
|
34
194
|
//# sourceMappingURL=item.js.map
|
package/dist/schemas/item.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"item.js","sourceRoot":"","sources":["../../src/schemas/item.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB
|
|
1
|
+
{"version":3,"file":"item.js","sourceRoot":"","sources":["../../src/schemas/item.ts"],"names":[],"mappings":"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"}
|
package/dist/schemas/recipe.d.ts
CHANGED
|
@@ -122,6 +122,16 @@ export declare const RecipeFileSchema: z.ZodObject<{
|
|
|
122
122
|
trusted: "trusted";
|
|
123
123
|
untrusted: "untrusted";
|
|
124
124
|
}>>;
|
|
125
|
+
triagePolicy: z.ZodOptional<z.ZodObject<{
|
|
126
|
+
agent: z.ZodEnum<{
|
|
127
|
+
"claude-code": "claude-code";
|
|
128
|
+
"codex-cli": "codex-cli";
|
|
129
|
+
"gemini-cli": "gemini-cli";
|
|
130
|
+
copilot: "copilot";
|
|
131
|
+
}>;
|
|
132
|
+
confidenceThreshold: z.ZodDefault<z.ZodNumber>;
|
|
133
|
+
rules: z.ZodString;
|
|
134
|
+
}, z.core.$strip>>;
|
|
125
135
|
}, z.core.$strip>;
|
|
126
136
|
export type RecipeFile = z.infer<typeof RecipeFileSchema>;
|
|
127
137
|
//# sourceMappingURL=recipe.d.ts.map
|