dsh-industry-research 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,16 @@ All notable changes to this project are documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.3.0] - 2026-08-26
9
+
10
+ ### Added
11
+
12
+ - Multi-perspective bull/bear debate synthesis in `industry_report`: `synthesizePerspectives` deterministically derives bull and bear perspective points from the loaded artifacts and evidence, `renderPerspectivesNote` writes a `perspectives-note.md` artifact (appended to the SHA-256 ledger), and the report result gains a `perspectives` outcome — a forked subagent job when `ctx.jobs` is mounted, otherwise `skipped(jobs unavailable)`.
13
+
14
+ ### Fixed
15
+
16
+ - Serialize `recordVersion` writes in the artifact version ledger (`src/versions.ts`): each path's ledger appends are chained through a per-path promise tail so a read never observes a torn write and no record is lost to a stale read when the report flow and its background jobs (red review, bull/bear debate) append `versions.jsonl` concurrently.
17
+
8
18
  ## [0.2.0] - 2026-08-23
9
19
 
10
20
  ### Added
package/README.md CHANGED
@@ -35,7 +35,7 @@
35
35
  - **`industry_map`** — build or update an industry chain map (`chain.json`): upstream/midstream/downstream nodes, edges, and metric slots. A metric with a `value` must carry a `sourceRef`; a value-less slot is an explicit gap. Node `status` assertions carry a `statusAsOf`, and an optional `taxonomyCode` must hit the built-in 国民经济行业分类 大类 table. With `renderSvg: true` it also writes a deterministic, tier-layered `chain.svg` network diagram with funnel/hub bottleneck highlighting. A `depth` argument scales the web assist (quick/standard/comprehensive). Seed notes/files and an optional `ctx.web` digest are registered as citable sources (`S1`, `S2`, …).
36
36
  - **`industry_track`** — public-source policy/news tracking over the official `ctx.web` seam: per-topic search, host allow/block lists, `since` filtering, bounded snapshot fetches with SHA-256 provenance hashes, and append + dedupe into `timeline.jsonl` with a retention cap. Entries may carry a validated `evidenceCategory`; a `depth` argument scales source counts. Fails loud when the web capability is unreachable.
37
37
  - **`company_scan`** — company cards (`card.json` + `card.md`) from your workspace data files (SHA-256-hashed, with Markdown outlines and figure-candidate lines so every number cites a file and a line), plus an optional `ctx.web` citation complement. Cards carry a listing `status` (with `statusAsOf`), a format-checked `ticker`, and sourced price/value `metrics` (each with `source` + `asOf`). Batch mode (`companies`) isolates one bad company without aborting the batch; `depth` scales the web complement. Text formats only in v1 (no PDF).
38
- - **`industry_report`** — one auditable report from the map + timeline + cards. Before producing, a deterministic delivery contract checks blocks, status/price assertions, and placeholder residue (fail loud, no half-assembled report). Timeline entries are grouped by `evidenceCategory`. With a mounted `ctx.researchReport` engine the evidence/sections/claims are sealed by its `assemble` and per-claim verdicts come back; without one, the builtin fallback writes versioned `reports/<YYYYMMDD-HHmmss>/report.md` + `manifest.json` with a SHA-256 source-traceability table and honestly labels `engine: 'builtin-fallback'`.
38
+ - **`industry_report`** — one auditable report from the map + timeline + cards. Before producing, a deterministic delivery contract checks blocks, status/price assertions, and placeholder residue (fail loud, no half-assembled report). Timeline entries are grouped by `evidenceCategory`. A deterministic bull/bear (正反方) synthesis lists sourced, dated values against declared gaps and data-quality findings, is embedded as the report's multi-perspective section, and is forked as a subagent job writing `perspectives-note.md` into the version ledger. With a mounted `ctx.researchReport` engine the evidence/sections/claims are sealed by its `assemble` and per-claim verdicts come back; without one, the builtin fallback writes versioned `reports/<YYYYMMDD-HHmmss>/report.md` + `manifest.json` with a SHA-256 source-traceability table and honestly labels `engine: 'builtin-fallback'`.
39
39
  - **Artifact version ledger** — `industry_map` / `company_scan` / `industry_report` append to `versions.jsonl` (root-relative path + SHA-256 + timestamp + change type) on every artifact write; reads verify the hash and fail loud on mismatch.
40
40
  - **Two methodology skills** — `industry-research-method` (chain decomposition, supply/demand framework, sourcing discipline, gap-declaration discipline) and `company-research-method` (company framework, public-source list, compliance wording).
41
41
  - **Typed Cordis events** — `industry-research/map`, `industry-research/track`, `industry-research/report` emitted after each committed artifact.
@@ -110,7 +110,7 @@ Reads the workspace data files (`.md/.txt/.csv/.tsv/.json`; v1 does not parse PD
110
110
 
111
111
  ### `industry_report({ industry, sections?, companies?, draft? })`
112
112
 
113
- Assembles evidence (`E-chain`, `E-timeline`, `E-company-<slug>`), verifies the read artifacts against `versions.jsonl` (hash mismatch fails loud), and either validates your `draft` (sections + claims; every claim's `evidenceIds` must reference registered evidence) or builds the mechanical auto-draft (sourced metrics and recent timeline entries become claims, grouped by `evidenceCategory`). A deterministic delivery contract runs before production and fails loud on any missing block, placeholder, or unsourced/undated assertion. A deterministic adversarial machine-check always runs, and a red-team review job (`red-review-note.md`) is spawned when `ctx.jobs` is mounted (else `review: skipped(jobs unavailable)`). Engine path: sealed directory + `sealHash` + per-claim verdicts. Fallback path: versioned Markdown + manifest, claims honestly marked `unverified`. Emits `industry-research/report`.
113
+ Assembles evidence (`E-chain`, `E-timeline`, `E-company-<slug>`), verifies the read artifacts against `versions.jsonl` (hash mismatch fails loud), and either validates your `draft` (sections + claims; every claim's `evidenceIds` must reference registered evidence) or builds the mechanical auto-draft (sourced metrics and recent timeline entries become claims, grouped by `evidenceCategory`). A deterministic delivery contract runs before production and fails loud on any missing block, placeholder, or unsourced/undated assertion. A deterministic adversarial machine-check always runs, and a red-team review job (`red-review-note.md`) is spawned when `ctx.jobs` is mounted (else `review: skipped(jobs unavailable)`). A deterministic bull/bear (正反方) synthesis embeds a multi-perspective section in the report and forks a debate job (`perspectives-note.md`) when `ctx.jobs` is mounted. Engine path: sealed directory + `sealHash` + per-claim verdicts. Fallback path: versioned Markdown + manifest, claims honestly marked `unverified`. Emits `industry-research/report`.
114
114
 
115
115
  ## Skills
116
116
 
@@ -130,6 +130,7 @@ Both load on demand through the standard `skill` tool (`加载 industry-research
130
130
  <workspace>/<industryRoot>/<industry>/sources.json citable source registry (S1, S2, …)
131
131
  <workspace>/<industryRoot>/<industry>/notes/ seed notes
132
132
  <workspace>/<industryRoot>/<industry>/red-review-note.md industry_report (red-team review, jobs)
133
+ <workspace>/<industryRoot>/<industry>/perspectives-note.md industry_report (bull/bear debate, jobs)
133
134
  <workspace>/<industryRoot>/<industry>/reports/<ts>/ industry_report (report.md + manifest.json)
134
135
  <workspace>/<industryRoot>/companies/<name>/card.* company_scan
135
136
  ```
package/README.zh.md CHANGED
@@ -34,7 +34,7 @@
34
34
  - **`industry_map`**——构建/更新产业链结构图(`chain.json`):上/中/下游节点、边、指标槽位。带 `value` 的指标必须带 `sourceRef`;无值槽位即显式缺口。种子笔记/文件与可选 `ctx.web` 摘要登记为可引用来源(`S1`、`S2`……)。
35
35
  - **`industry_track`**——经官方 `ctx.web` 缝隙跟踪行业政策/要闻:按主题检索、来源主机白/黑名单、`since` 过滤、预算内快照抓取(SHA-256 溯源哈希),追加去重写入 `timeline.jsonl`(保留上限可配)。web 能力不可达时响亮失败。
36
36
  - **`company_scan`**——以工作区数据文件生成公司速览卡(`card.json` + `card.md`):SHA-256 哈希、Markdown 大纲、数字候选行(每个数字可标注文件与行号),另可选 `ctx.web` 引用补充。v1 只读文本格式(不解析 PDF)。
37
- - **`industry_report`**——汇总结构图、时间线与公司卡产出可核查报告。挂载 `ctx.researchReport` 引擎时证据/章节/claims 提交其 `assemble` 封存并回传逐 claim 结论;缺席时内置降级路径写入版本化 `reports/<YYYYMMDD-HHmmss>/report.md` + `manifest.json`(含 SHA-256 来源回溯表),并如实标注 `engine: 'builtin-fallback'`。
37
+ - **`industry_report`**——汇总结构图、时间线与公司卡产出可核查报告。确定性正反方(bull/bear)合成将「有来源且带日期的数值」列为多方、「缺口与数据质量发现」列为空方,嵌入报告多视角小节,并派生 subagent job 写回 `perspectives-note.md` 入账本。挂载 `ctx.researchReport` 引擎时证据/章节/claims 提交其 `assemble` 封存并回传逐 claim 结论;缺席时内置降级路径写入版本化 `reports/<YYYYMMDD-HHmmss>/report.md` + `manifest.json`(含 SHA-256 来源回溯表),并如实标注 `engine: 'builtin-fallback'`。
38
38
  - **两个方法论技能**——`industry-research-method`(产业链拆分、供需框架、口径纪律、缺口声明纪律)与 `company-research-method`(公司研究框架、公开源清单、合规话术)。
39
39
  - **类型化 Cordis 事件**——每份产物落盘后发出 `industry-research/map`、`industry-research/track`、`industry-research/report`。
40
40
 
@@ -108,7 +108,7 @@ dsh plugin --profile demo remove dsh-industry-research # 卸载
108
108
 
109
109
  ### `industry_report({ industry, sections?, companies?, draft? })`
110
110
 
111
- 组装证据(`E-chain`、`E-timeline`、`E-company-<slug>`),读取时对照 `versions.jsonl` 校验哈希(不一致响亮失败),或校验你提供的 `draft`(章节 + claims;每条 claim 的 `evidenceIds` 必须引用已登记证据),或机械自动生成草稿(有来源的指标与近期时间线条目生成 claims,按 `evidenceCategory` 分组)。产出前跑确定性交付契约校验(缺区块/占位符/无来源或无日期断言即响亮失败)。确定性对抗机器检查始终执行;挂载 `ctx.jobs` 时派生红方审阅 job 写回 `red-review-note.md`(否则 `review: skipped(jobs unavailable)`)。引擎路径:封存目录 + `sealHash` + 逐 claim 结论。降级路径:版本化 Markdown + manifest,claims 如实标记 `unverified`。发出 `industry-research/report` 事件。
111
+ 组装证据(`E-chain`、`E-timeline`、`E-company-<slug>`),读取时对照 `versions.jsonl` 校验哈希(不一致响亮失败),或校验你提供的 `draft`(章节 + claims;每条 claim 的 `evidenceIds` 必须引用已登记证据),或机械自动生成草稿(有来源的指标与近期时间线条目生成 claims,按 `evidenceCategory` 分组)。产出前跑确定性交付契约校验(缺区块/占位符/无来源或无日期断言即响亮失败)。确定性对抗机器检查始终执行;挂载 `ctx.jobs` 时派生红方审阅 job 写回 `red-review-note.md`(否则 `review: skipped(jobs unavailable)`)。确定性正反方(bull/bear)合成嵌入报告多视角小节,挂载 `ctx.jobs` 时派生辩论 job 写回 `perspectives-note.md`。引擎路径:封存目录 + `sealHash` + 逐 claim 结论。降级路径:版本化 Markdown + manifest,claims 如实标记 `unverified`。发出 `industry-research/report` 事件。
112
112
 
113
113
  ## Skills
114
114
 
@@ -128,6 +128,7 @@ dsh plugin --profile demo remove dsh-industry-research # 卸载
128
128
  <工作区>/<industryRoot>/<行业>/sources.json 可引用来源登记(S1、S2……)
129
129
  <工作区>/<industryRoot>/<行业>/notes/ 种子笔记
130
130
  <工作区>/<industryRoot>/<行业>/red-review-note.md industry_report(红方审阅,需 jobs)
131
+ <工作区>/<industryRoot>/<行业>/perspectives-note.md industry_report(正反方辩论,需 jobs)
131
132
  <工作区>/<industryRoot>/<行业>/reports/<ts>/ industry_report(report.md + manifest.json)
132
133
  <工作区>/<industryRoot>/companies/<公司>/card.* company_scan
133
134
  ```
package/lib/index.js CHANGED
@@ -1087,18 +1087,30 @@ async function readVersions(path) {
1087
1087
  * @param capturedAt - ISO-8601 capture time.
1088
1088
  * @returns the inferred change kind.
1089
1089
  */
1090
+ /**
1091
+ * Per-path serialization for {@link recordVersion}. The ledger is read-modify-
1092
+ * written, and the report flow plus its background jobs (red review, bull/bear
1093
+ * debate) can append the same `versions.jsonl` concurrently. Chaining each
1094
+ * path's writes guarantees a read never observes a torn write and no record is
1095
+ * lost to a stale read.
1096
+ */
1097
+ const ledgerTails = /* @__PURE__ */ new Map();
1090
1098
  async function recordVersion(path, artifact, content, capturedAt) {
1091
- const records = await readVersions(path);
1092
- const change = records.some((record) => record.artifact === artifact) ? "updated" : "created";
1093
- records.push({
1094
- artifact,
1095
- sha256: sha256Of(content),
1096
- capturedAt,
1097
- change
1099
+ const run = (ledgerTails.get(path) ?? Promise.resolve()).then(async () => {
1100
+ const records = await readVersions(path);
1101
+ const change = records.some((record) => record.artifact === artifact) ? "updated" : "created";
1102
+ records.push({
1103
+ artifact,
1104
+ sha256: sha256Of(content),
1105
+ capturedAt,
1106
+ change
1107
+ });
1108
+ await mkdir(dirname(path), { recursive: true });
1109
+ await writeFile(path, `${records.map((record) => JSON.stringify(record)).join("\n")}\n`, "utf8");
1110
+ return change;
1098
1111
  });
1099
- await mkdir(dirname(path), { recursive: true });
1100
- await writeFile(path, `${records.map((record) => JSON.stringify(record)).join("\n")}\n`, "utf8");
1101
- return change;
1112
+ ledgerTails.set(path, run.then(() => void 0, () => void 0));
1113
+ return run;
1102
1114
  }
1103
1115
  /**
1104
1116
  * Verify an artifact's content against its latest ledger record. A missing
@@ -2739,6 +2751,115 @@ function renderRedReviewNote(draft, artifacts, evidenceIds, generatedAt) {
2739
2751
  return lines.join("\n");
2740
2752
  }
2741
2753
  //#endregion
2754
+ //#region src/perspectives.ts
2755
+ /**
2756
+ * Multi-perspective (bull/bear) debate synthesis for `industry_report`. Like
2757
+ * the red-team review, the synthesis is deterministic code over the already
2758
+ * loaded artifacts: the bull (利好/支撑) side lists sourced, dated values and
2759
+ * coverage, and the bear (利空/风险) side lists declared gaps and data-quality
2760
+ * findings — every point cites its evidence id or is an honest gap, never an
2761
+ * invented number. A forked subagent job writes the rendered note into the
2762
+ * workspace and appends it to the versions.jsonl SHA-256 ledger.
2763
+ * @module dsh-industry-research/perspectives
2764
+ */
2765
+ /** One side of the debate. */
2766
+ const PERSPECTIVE_SIDES = ["bull", "bear"];
2767
+ /** Maximum sourced chain metrics surfaced on the bull side. */
2768
+ const BULL_CHAIN_CAP = 8;
2769
+ /** Maximum sourced card metrics surfaced on the bull side. */
2770
+ const BULL_CARD_CAP = 6;
2771
+ /** Maximum gap/finding lines surfaced on the bear side. */
2772
+ const BEAR_CAP = 12;
2773
+ /**
2774
+ * Synthesize the bull/bear perspectives from the loaded artifacts. The bull
2775
+ * side lists sourced, dated values (chain metrics, card metrics, and timeline
2776
+ * coverage); the bear side lists declared gaps and deterministic data-quality
2777
+ * findings. Pure and byte-stable for the same inputs — never model-authored.
2778
+ * @param draft - the report draft under synthesis.
2779
+ * @param artifacts - the source artifacts the report assembles from.
2780
+ * @param evidenceIds - registered evidence ids (for dead-link checks).
2781
+ * @returns the bull/bear synthesis.
2782
+ */
2783
+ function synthesizePerspectives(draft, artifacts, evidenceIds) {
2784
+ const bull = [];
2785
+ const bear = [];
2786
+ if (artifacts.chain !== void 0) for (const node of artifacts.chain.map.nodes) {
2787
+ for (const metric of node.metrics) {
2788
+ if (metric.value === void 0 || metric.sourceRef === void 0) continue;
2789
+ if (bull.length >= BULL_CHAIN_CAP) break;
2790
+ bull.push({
2791
+ text: `产业链指标:${node.name} 的「${metric.key}」为 ${metric.value}${metric.unit ?? ""}${metric.asOf !== void 0 ? `(截至 ${metric.asOf}` : ""}${metric.asOf !== void 0 ? `,来源 ${metric.sourceRef})` : ""}`,
2792
+ evidenceIds: ["E-chain"]
2793
+ });
2794
+ }
2795
+ if (bull.length >= BULL_CHAIN_CAP) break;
2796
+ }
2797
+ if (artifacts.timeline !== void 0 && artifacts.timeline.entries.length > 0) bull.push({
2798
+ text: `政策与动态已收录 ${artifacts.timeline.entries.length} 条,时间线可追溯。`,
2799
+ evidenceIds: ["E-timeline"]
2800
+ });
2801
+ let cardPoints = 0;
2802
+ for (const { card } of artifacts.cards) {
2803
+ for (const metric of card.metrics ?? []) {
2804
+ if (cardPoints >= BULL_CARD_CAP) break;
2805
+ cardPoints += 1;
2806
+ bull.push({
2807
+ text: `公司数据:${card.name} 的「${metric.key}」为 ${metric.value}${metric.unit ?? ""}(截至 ${metric.asOf},来源 ${metric.source})。`,
2808
+ evidenceIds: [`E-company-${card.slug}`]
2809
+ });
2810
+ }
2811
+ if (cardPoints >= BULL_CARD_CAP) break;
2812
+ }
2813
+ for (const gap of artifacts.gaps) {
2814
+ if (bear.length >= BEAR_CAP) break;
2815
+ bear.push({
2816
+ text: `缺口:${gap}`,
2817
+ evidenceIds: []
2818
+ });
2819
+ }
2820
+ for (const finding of adversarialCheck(draft, artifacts, evidenceIds)) {
2821
+ if (bear.length >= BEAR_CAP) break;
2822
+ bear.push({
2823
+ text: `数据质量风险:${finding}`,
2824
+ evidenceIds: []
2825
+ });
2826
+ }
2827
+ return {
2828
+ bull,
2829
+ bear
2830
+ };
2831
+ }
2832
+ /**
2833
+ * Render the multi-perspective note (`perspectives-note.md`): bull and bear
2834
+ * sections over the same source-traceability table, then a synthesis line. The
2835
+ * note is written into the industry workspace by the forked debate job and
2836
+ * appended to the versions.jsonl SHA-256 ledger.
2837
+ * @param industry - the industry display name.
2838
+ * @param draft - the report draft.
2839
+ * @param artifacts - the source artifacts.
2840
+ * @param evidenceIds - registered evidence ids.
2841
+ * @param generatedAt - ISO-8601 generation time.
2842
+ * @returns the note Markdown text.
2843
+ */
2844
+ function renderPerspectivesNote(industry, draft, artifacts, evidenceIds, generatedAt) {
2845
+ const synthesis = synthesizePerspectives(draft, artifacts, evidenceIds);
2846
+ const lines = [
2847
+ "# 多视角正反方笔记(bull/bear)",
2848
+ "",
2849
+ `> 行业:${industry};生成时间:${generatedAt}。本笔记由多视角正反方辩论 job 产出,正反方观点均来自同一份来源回溯表,仅供研究,不构成投资建议。`,
2850
+ "",
2851
+ "## 多方视角(bull · 利好/支撑)"
2852
+ ];
2853
+ if (synthesis.bull.length > 0) for (const point of synthesis.bull) lines.push(`- ${point.text}${point.evidenceIds.length > 0 ? `(证据 ${point.evidenceIds.join(", ")})` : ""}`);
2854
+ else lines.push("- (无)");
2855
+ lines.push("", "## 空方视角(bear · 利空/风险)");
2856
+ if (synthesis.bear.length > 0) for (const point of synthesis.bear) lines.push(`- ${point.text}`);
2857
+ else lines.push("- (无)");
2858
+ lines.push("", "## 综合(再合成)", "", `多方 ${synthesis.bull.length} 项、空方 ${synthesis.bear.length} 项;数值以来源证据为准,缺口如实声明。`);
2859
+ lines.push("");
2860
+ return lines.join("\n");
2861
+ }
2862
+ //#endregion
2742
2863
  //#region src/report.ts
2743
2864
  /**
2744
2865
  * Report assembly for `industry_report`: load nothing itself — it turns the
@@ -2985,9 +3106,13 @@ function autoDraft(industry, artifacts, sections = [...AUTO_SECTIONS]) {
2985
3106
  * @param evidence - the registered evidence.
2986
3107
  * @param generatedAt - ISO-8601 generation time.
2987
3108
  * @param machineCheck - deterministic adversarial-check findings (empty = clean).
3109
+ * @param perspectives - the bull/bear synthesis for the multi-perspective section.
2988
3110
  * @returns the Markdown text.
2989
3111
  */
2990
- function renderFallbackMarkdown(industry, draft, evidence, generatedAt, machineCheck = []) {
3112
+ function renderFallbackMarkdown(industry, draft, evidence, generatedAt, machineCheck = [], perspectives = {
3113
+ bull: [],
3114
+ bear: []
3115
+ }) {
2991
3116
  const lines = [
2992
3117
  `# ${draft.title}`,
2993
3118
  "",
@@ -3009,6 +3134,12 @@ function renderFallbackMarkdown(industry, draft, evidence, generatedAt, machineC
3009
3134
  lines.push("| claim | 内容 | 证据 | 状态 |", "|---|---|---|---|");
3010
3135
  for (const claim of draft.claims) lines.push(`| ${claim.id} | ${claim.text} | ${claim.evidenceIds.join(", ")} | unverified |`);
3011
3136
  } else lines.push("无 claims。");
3137
+ lines.push("", "## 多视角(正反方)", "", "### 多方(bull · 利好/支撑)");
3138
+ if (perspectives.bull.length > 0) for (const point of perspectives.bull) lines.push(`- ${point.text}${point.evidenceIds.length > 0 ? `(证据 ${point.evidenceIds.join(", ")})` : ""}`);
3139
+ else lines.push("- (无)");
3140
+ lines.push("", "### 空方(bear · 利空/风险)");
3141
+ if (perspectives.bear.length > 0) for (const point of perspectives.bear) lines.push(`- ${point.text}`);
3142
+ else lines.push("- (无)");
3012
3143
  lines.push("", "## 机器对抗检查", "");
3013
3144
  if (machineCheck.length > 0) for (const finding of machineCheck) lines.push(`- ${finding}`);
3014
3145
  else lines.push("未发现可攻击点。");
@@ -3024,9 +3155,13 @@ function renderFallbackMarkdown(industry, draft, evidence, generatedAt, machineC
3024
3155
  * @param gaps - artifact-level gaps to record in the manifest.
3025
3156
  * @param generatedAt - ISO-8601 generation time.
3026
3157
  * @param machineCheck - deterministic adversarial-check findings.
3158
+ * @param perspectives - the bull/bear synthesis to embed in report.md + manifest.
3027
3159
  * @returns the written file paths.
3028
3160
  */
3029
- async function writeFallbackReport(reportDir, industry, draft, evidence, gaps, generatedAt, machineCheck = []) {
3161
+ async function writeFallbackReport(reportDir, industry, draft, evidence, gaps, generatedAt, machineCheck = [], perspectives = {
3162
+ bull: [],
3163
+ bear: []
3164
+ }) {
3030
3165
  await mkdir(reportDir, { recursive: true });
3031
3166
  const reportPath = join(reportDir, "report.md");
3032
3167
  const manifestPath = join(reportDir, "manifest.json");
@@ -3049,9 +3184,13 @@ async function writeFallbackReport(reportDir, industry, draft, evidence, gaps, g
3049
3184
  status: "unverified"
3050
3185
  })),
3051
3186
  gaps: [...gaps],
3052
- machineCheck: [...machineCheck]
3187
+ machineCheck: [...machineCheck],
3188
+ perspectives: {
3189
+ bull: [...perspectives.bull],
3190
+ bear: [...perspectives.bear]
3191
+ }
3053
3192
  };
3054
- await writeFile(reportPath, renderFallbackMarkdown(industry, draft, evidence, generatedAt, machineCheck), "utf8");
3193
+ await writeFile(reportPath, renderFallbackMarkdown(industry, draft, evidence, generatedAt, machineCheck, perspectives), "utf8");
3055
3194
  await writeFile(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`, "utf8");
3056
3195
  return {
3057
3196
  reportPath,
@@ -3347,6 +3486,10 @@ function buildIndustryReportTool(ctx, config) {
3347
3486
  review: {
3348
3487
  type: "json",
3349
3488
  required: true
3489
+ },
3490
+ perspectives: {
3491
+ type: "json",
3492
+ required: true
3350
3493
  }
3351
3494
  },
3352
3495
  additionalProperties: false
@@ -3356,6 +3499,7 @@ function buildIndustryReportTool(ctx, config) {
3356
3499
  const lines = current.engine === "research-report" ? [`行业「${current.industry}」研究报告已经独立核查引擎封存 → ${current.reportDir}`, `sealHash:${current.sealHash ?? ""};claims ${current.claims} 条(verified ${(current.verdicts ?? []).filter((verdict) => verdict.status === "verified").length} 条)。`] : [`行业「${current.industry}」研究报告(builtin-fallback,未经过独立核查引擎)→ ${current.reportPath}`, `claims ${current.claims} 条均标记 unverified;来源回溯表见 report.md 附录与 ${current.manifestPath ?? ""}。`];
3357
3500
  lines.push(`机器对抗检查:${current.machineCheck.length > 0 ? `${current.machineCheck.length} 项发现` : "未发现可攻击点"}。`);
3358
3501
  lines.push(`红方审阅:${current.review.mode === "job" ? `job ${current.review.jobId} 已派生` : `skipped(${current.review.note})`}。`);
3502
+ lines.push(`多视角正反方:${current.perspectives.mode === "job" ? `job ${current.perspectives.jobId} 已派生` : `skipped(${current.perspectives.note})`}。`);
3359
3503
  if (current.gaps.length > 0) lines.push(`缺口声明:${current.gaps.join(";")}`);
3360
3504
  lines.push("仅供研究,不构成投资建议。");
3361
3505
  return [{
@@ -3422,6 +3566,42 @@ function buildIndustryReportTool(ctx, config) {
3422
3566
  })
3423
3567
  };
3424
3568
  }
3569
+ const synthesis = synthesizePerspectives(draft, artifacts, evidenceIds);
3570
+ let perspectives;
3571
+ const perspectivesNotePath = join(dir, "perspectives-note.md");
3572
+ if (jobs === void 0) perspectives = {
3573
+ mode: "skipped",
3574
+ note: "jobs unavailable"
3575
+ };
3576
+ else {
3577
+ const noteContent = renderPerspectivesNote(name, draft, artifacts, evidenceIds, generatedAt);
3578
+ perspectives = {
3579
+ mode: "job",
3580
+ jobId: jobs.start({
3581
+ kind: "subagent",
3582
+ label: "多视角正反方辩论(bull/bear)",
3583
+ owner: exec.agent,
3584
+ run: () => {
3585
+ let cancelled = false;
3586
+ return {
3587
+ cancel: () => {
3588
+ cancelled = true;
3589
+ },
3590
+ done: (async () => {
3591
+ if (!cancelled) {
3592
+ await writeFile(perspectivesNotePath, noteContent, "utf8");
3593
+ await recordVersion(versionsPath, rootRelative(root, perspectivesNotePath), noteContent, generatedAt);
3594
+ }
3595
+ return {
3596
+ status: cancelled ? "killed" : "completed",
3597
+ output: `bull ${synthesis.bull.length} 项 / bear ${synthesis.bear.length} 项`
3598
+ };
3599
+ })()
3600
+ };
3601
+ }
3602
+ })
3603
+ };
3604
+ }
3425
3605
  const engine = lookupEngine(ctx);
3426
3606
  let value;
3427
3607
  if (engine !== void 0) {
@@ -3445,13 +3625,14 @@ function buildIndustryReportTool(ctx, config) {
3445
3625
  gaps: artifacts.gaps,
3446
3626
  generatedAt,
3447
3627
  machineCheck,
3448
- review
3628
+ review,
3629
+ perspectives
3449
3630
  };
3450
3631
  } else {
3451
3632
  const reportsDir = reportsDirOf(dir);
3452
3633
  const dirName = reportDirName(/* @__PURE__ */ new Date(), (candidate) => existsSync(join(reportsDir, candidate)));
3453
3634
  const reportDir = join(reportsDir, dirName);
3454
- const { reportPath, manifestPath } = await writeFallbackReport(reportDir, name, draft, evidence, artifacts.gaps, generatedAt, machineCheck);
3635
+ const { reportPath, manifestPath } = await writeFallbackReport(reportDir, name, draft, evidence, artifacts.gaps, generatedAt, machineCheck, synthesis);
3455
3636
  await recordVersion(versionsPath, rootRelative(root, reportPath), await readFile(reportPath, "utf8"), generatedAt);
3456
3637
  await recordVersion(versionsPath, rootRelative(root, manifestPath), await readFile(manifestPath, "utf8"), generatedAt);
3457
3638
  value = {
@@ -3467,7 +3648,8 @@ function buildIndustryReportTool(ctx, config) {
3467
3648
  gaps: artifacts.gaps,
3468
3649
  generatedAt,
3469
3650
  machineCheck,
3470
- review
3651
+ review,
3652
+ perspectives
3471
3653
  };
3472
3654
  }
3473
3655
  const payload = {
@@ -3490,7 +3672,7 @@ function buildIndustryReportTool(ctx, config) {
3490
3672
  * @module dsh-industry-research/version
3491
3673
  */
3492
3674
  /** The package version. */
3493
- const VERSION = "0.2.0";
3675
+ const VERSION = "0.3.0";
3494
3676
  //#endregion
3495
3677
  //#region src/index.ts
3496
3678
  /**
@@ -3573,4 +3755,4 @@ function apply(ctx, config = {}) {
3573
3755
  logger.info(`industry-research ${VERSION} mounted: 4 tools, skills from ${skillsRoot}`);
3574
3756
  }
3575
3757
  //#endregion
3576
- export { ARTIFACT_CHANGES, AUTO_SECTIONS, CHAIN_TIERS, COMPANY_STATUSES, Config, DEPTH_LEVELS, DEPTH_PROFILES, DISCLAIMER, EVIDENCE_CATEGORIES, INDUSTRY_TAXONOMY, READABLE_EXTENSIONS, TICKER_FORMATS, VERSION, adversarialCheck, analyzeBottlenecks, apply, autoDraft, boundFigures, buildCompanyScanTool, buildEvidence, buildIndustryMapTool, buildIndustryReportTool, buildIndustryTrackTool, chainGaps, chainPathOf, chainSvgPathOf, companyDirOf, industryDirOf, inject, isIsoDate, latestHashes, loadArtifacts, loadSources, lookupEngine, lookupJobs, lookupWeb, mergeTimeline, name, normalizeUrl, notesDirOf, readCard, readResearchState, readTimeline, readVersions, recordVersion, registerSource, renderCardMarkdown, renderChainSvg, renderFallbackMarkdown, renderRedReviewNote, reportDirName, reportsDirOf, requestSignal, requireWeb, researchStatePathOf, resolveConfig, resolveContained, resolveDepth, resolveIndustryRoot, resolveWorkspaceFile, rootRelative, safeSegment, saveSources, scanFile, sha256Of, sourceAllowed, sourcesPathOf, summarizeDelta, taxonomyEntry, timelinePathOf, updateResearchState, validateChainMap, validateCompanyCard, validateDeliveryContract, validateDraft, validateEvidenceCategory, validateStatus, validateTicker, verifyVersion, versionsPathOf, webErrorMessage, workspaceOf, writeCard, writeFallbackReport, writeResearchState, xmlEscape };
3758
+ export { ARTIFACT_CHANGES, AUTO_SECTIONS, CHAIN_TIERS, COMPANY_STATUSES, Config, DEPTH_LEVELS, DEPTH_PROFILES, DISCLAIMER, EVIDENCE_CATEGORIES, INDUSTRY_TAXONOMY, PERSPECTIVE_SIDES, READABLE_EXTENSIONS, TICKER_FORMATS, VERSION, adversarialCheck, analyzeBottlenecks, apply, autoDraft, boundFigures, buildCompanyScanTool, buildEvidence, buildIndustryMapTool, buildIndustryReportTool, buildIndustryTrackTool, chainGaps, chainPathOf, chainSvgPathOf, companyDirOf, industryDirOf, inject, isIsoDate, latestHashes, loadArtifacts, loadSources, lookupEngine, lookupJobs, lookupWeb, mergeTimeline, name, normalizeUrl, notesDirOf, readCard, readResearchState, readTimeline, readVersions, recordVersion, registerSource, renderCardMarkdown, renderChainSvg, renderFallbackMarkdown, renderPerspectivesNote, renderRedReviewNote, reportDirName, reportsDirOf, requestSignal, requireWeb, researchStatePathOf, resolveConfig, resolveContained, resolveDepth, resolveIndustryRoot, resolveWorkspaceFile, rootRelative, safeSegment, saveSources, scanFile, sha256Of, sourceAllowed, sourcesPathOf, summarizeDelta, synthesizePerspectives, taxonomyEntry, timelinePathOf, updateResearchState, validateChainMap, validateCompanyCard, validateDeliveryContract, validateDraft, validateEvidenceCategory, validateStatus, validateTicker, verifyVersion, versionsPathOf, webErrorMessage, workspaceOf, writeCard, writeFallbackReport, writeResearchState, xmlEscape };
@@ -37,6 +37,8 @@ export type { JobHooksLike, JobOutcomeLike, JobStartLike, JobsLike } from './job
37
37
  export { readResearchState, summarizeDelta, updateResearchState, writeResearchState } from './research-state.js';
38
38
  export type { ResearchState, ResearchStateDelta } from './research-state.js';
39
39
  export { adversarialCheck, renderRedReviewNote } from './adversarial.js';
40
+ export { PERSPECTIVE_SIDES, renderPerspectivesNote, synthesizePerspectives } from './perspectives.js';
41
+ export type { PerspectivePoint, PerspectivesSynthesis, PerspectiveSide } from './perspectives.js';
40
42
  export { loadSources, registerSource, saveSources, sha256Of } from './sources.js';
41
43
  export type { SourceEntry, SourceRegistry } from './sources.js';
42
44
  export { EVIDENCE_CATEGORIES, mergeTimeline, normalizeUrl, readTimeline, sourceAllowed, validateEvidenceCategory } from './timeline.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
@@ -37,6 +37,7 @@ export { ARTIFACT_CHANGES, latestHashes, readVersions, recordVersion, rootRelati
37
37
  export { lookupJobs } from "./jobs.js";
38
38
  export { readResearchState, summarizeDelta, updateResearchState, writeResearchState } from "./research-state.js";
39
39
  export { adversarialCheck, renderRedReviewNote } from "./adversarial.js";
40
+ export { PERSPECTIVE_SIDES, renderPerspectivesNote, synthesizePerspectives } from "./perspectives.js";
40
41
  export { loadSources, registerSource, saveSources, sha256Of } from "./sources.js";
41
42
  export { EVIDENCE_CATEGORIES, mergeTimeline, normalizeUrl, readTimeline, sourceAllowed, validateEvidenceCategory } from "./timeline.js";
42
43
  export { boundFigures, readCard, renderCardMarkdown, scanFile, validateCompanyCard, validateTicker, writeCard, DISCLAIMER, READABLE_EXTENSIONS, TICKER_FORMATS } from "./company.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Multi-perspective (bull/bear) debate synthesis for `industry_report`. Like
3
+ * the red-team review, the synthesis is deterministic code over the already
4
+ * loaded artifacts: the bull (利好/支撑) side lists sourced, dated values and
5
+ * coverage, and the bear (利空/风险) side lists declared gaps and data-quality
6
+ * findings — every point cites its evidence id or is an honest gap, never an
7
+ * invented number. A forked subagent job writes the rendered note into the
8
+ * workspace and appends it to the versions.jsonl SHA-256 ledger.
9
+ * @module dsh-industry-research/perspectives
10
+ */
11
+ import type { LoadedArtifacts, ReportDraft } from './report.js';
12
+ /** One side of the debate. */
13
+ export declare const PERSPECTIVE_SIDES: readonly ["bull", "bear"];
14
+ /** One debate side. */
15
+ export type PerspectiveSide = (typeof PERSPECTIVE_SIDES)[number];
16
+ /** One synthesized point: a claim and the evidence ids backing it. */
17
+ export interface PerspectivePoint {
18
+ /** The point text (a sourced observation or an honest gap). */
19
+ text: string;
20
+ /** Evidence ids backing the point (empty when the point is a declared gap). */
21
+ evidenceIds: string[];
22
+ }
23
+ /** The bull/bear synthesis for one report assembly. */
24
+ export interface PerspectivesSynthesis {
25
+ /** Positive/supporting observations (sourced, dated values and coverage). */
26
+ bull: PerspectivePoint[];
27
+ /** Negative/risk observations (gaps and data-quality findings). */
28
+ bear: PerspectivePoint[];
29
+ }
30
+ /**
31
+ * Synthesize the bull/bear perspectives from the loaded artifacts. The bull
32
+ * side lists sourced, dated values (chain metrics, card metrics, and timeline
33
+ * coverage); the bear side lists declared gaps and deterministic data-quality
34
+ * findings. Pure and byte-stable for the same inputs — never model-authored.
35
+ * @param draft - the report draft under synthesis.
36
+ * @param artifacts - the source artifacts the report assembles from.
37
+ * @param evidenceIds - registered evidence ids (for dead-link checks).
38
+ * @returns the bull/bear synthesis.
39
+ */
40
+ export declare function synthesizePerspectives(draft: ReportDraft, artifacts: LoadedArtifacts, evidenceIds: ReadonlySet<string>): PerspectivesSynthesis;
41
+ /**
42
+ * Render the multi-perspective note (`perspectives-note.md`): bull and bear
43
+ * sections over the same source-traceability table, then a synthesis line. The
44
+ * note is written into the industry workspace by the forked debate job and
45
+ * appended to the versions.jsonl SHA-256 ledger.
46
+ * @param industry - the industry display name.
47
+ * @param draft - the report draft.
48
+ * @param artifacts - the source artifacts.
49
+ * @param evidenceIds - registered evidence ids.
50
+ * @param generatedAt - ISO-8601 generation time.
51
+ * @returns the note Markdown text.
52
+ */
53
+ export declare function renderPerspectivesNote(industry: string, draft: ReportDraft, artifacts: LoadedArtifacts, evidenceIds: ReadonlySet<string>, generatedAt: string): string;
54
+ //# sourceMappingURL=perspectives.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"perspectives.d.ts","sourceRoot":"","sources":["../../src/perspectives.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAE/D,8BAA8B;AAC9B,eAAO,MAAM,iBAAiB,2BAA4B,CAAA;AAE1D,uBAAuB;AACvB,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAA;AAEhE,sEAAsE;AACtE,MAAM,WAAW,gBAAgB;IAC/B,+DAA+D;IAC/D,IAAI,EAAE,MAAM,CAAA;IACZ,+EAA+E;IAC/E,WAAW,EAAE,MAAM,EAAE,CAAA;CACtB;AAED,uDAAuD;AACvD,MAAM,WAAW,qBAAqB;IACpC,6EAA6E;IAC7E,IAAI,EAAE,gBAAgB,EAAE,CAAA;IACxB,mEAAmE;IACnE,IAAI,EAAE,gBAAgB,EAAE,CAAA;CACzB;AAWD;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,eAAe,EAAE,WAAW,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,qBAAqB,CA6C9I;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,eAAe,EAAE,WAAW,EAAE,WAAW,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAyBtK"}
@@ -0,0 +1,119 @@
1
+ /**
2
+ * Multi-perspective (bull/bear) debate synthesis for `industry_report`. Like
3
+ * the red-team review, the synthesis is deterministic code over the already
4
+ * loaded artifacts: the bull (利好/支撑) side lists sourced, dated values and
5
+ * coverage, and the bear (利空/风险) side lists declared gaps and data-quality
6
+ * findings — every point cites its evidence id or is an honest gap, never an
7
+ * invented number. A forked subagent job writes the rendered note into the
8
+ * workspace and appends it to the versions.jsonl SHA-256 ledger.
9
+ * @module dsh-industry-research/perspectives
10
+ */
11
+ import { adversarialCheck } from "./adversarial.js";
12
+ /** One side of the debate. */
13
+ export const PERSPECTIVE_SIDES = ['bull', 'bear'];
14
+ /** Maximum sourced chain metrics surfaced on the bull side. */
15
+ const BULL_CHAIN_CAP = 8;
16
+ /** Maximum sourced card metrics surfaced on the bull side. */
17
+ const BULL_CARD_CAP = 6;
18
+ /** Maximum gap/finding lines surfaced on the bear side. */
19
+ const BEAR_CAP = 12;
20
+ /**
21
+ * Synthesize the bull/bear perspectives from the loaded artifacts. The bull
22
+ * side lists sourced, dated values (chain metrics, card metrics, and timeline
23
+ * coverage); the bear side lists declared gaps and deterministic data-quality
24
+ * findings. Pure and byte-stable for the same inputs — never model-authored.
25
+ * @param draft - the report draft under synthesis.
26
+ * @param artifacts - the source artifacts the report assembles from.
27
+ * @param evidenceIds - registered evidence ids (for dead-link checks).
28
+ * @returns the bull/bear synthesis.
29
+ */
30
+ export function synthesizePerspectives(draft, artifacts, evidenceIds) {
31
+ const bull = [];
32
+ const bear = [];
33
+ if (artifacts.chain !== undefined) {
34
+ for (const node of artifacts.chain.map.nodes) {
35
+ for (const metric of node.metrics) {
36
+ if (metric.value === undefined || metric.sourceRef === undefined)
37
+ continue;
38
+ if (bull.length >= BULL_CHAIN_CAP)
39
+ break;
40
+ bull.push({
41
+ text: `产业链指标:${node.name} 的「${metric.key}」为 ${metric.value}${metric.unit ?? ''}${metric.asOf !== undefined ? `(截至 ${metric.asOf}` : ''}${metric.asOf !== undefined ? `,来源 ${metric.sourceRef})` : ''}`,
42
+ evidenceIds: ['E-chain'],
43
+ });
44
+ }
45
+ if (bull.length >= BULL_CHAIN_CAP)
46
+ break;
47
+ }
48
+ }
49
+ if (artifacts.timeline !== undefined && artifacts.timeline.entries.length > 0) {
50
+ bull.push({ text: `政策与动态已收录 ${artifacts.timeline.entries.length} 条,时间线可追溯。`, evidenceIds: ['E-timeline'] });
51
+ }
52
+ let cardPoints = 0;
53
+ for (const { card } of artifacts.cards) {
54
+ for (const metric of (card.metrics ?? [])) {
55
+ if (cardPoints >= BULL_CARD_CAP)
56
+ break;
57
+ cardPoints += 1;
58
+ bull.push({
59
+ text: `公司数据:${card.name} 的「${metric.key}」为 ${metric.value}${metric.unit ?? ''}(截至 ${metric.asOf},来源 ${metric.source})。`,
60
+ evidenceIds: [`E-company-${card.slug}`],
61
+ });
62
+ }
63
+ if (cardPoints >= BULL_CARD_CAP)
64
+ break;
65
+ }
66
+ for (const gap of artifacts.gaps) {
67
+ if (bear.length >= BEAR_CAP)
68
+ break;
69
+ bear.push({ text: `缺口:${gap}`, evidenceIds: [] });
70
+ }
71
+ for (const finding of adversarialCheck(draft, artifacts, evidenceIds)) {
72
+ if (bear.length >= BEAR_CAP)
73
+ break;
74
+ bear.push({ text: `数据质量风险:${finding}`, evidenceIds: [] });
75
+ }
76
+ return { bull, bear };
77
+ }
78
+ /**
79
+ * Render the multi-perspective note (`perspectives-note.md`): bull and bear
80
+ * sections over the same source-traceability table, then a synthesis line. The
81
+ * note is written into the industry workspace by the forked debate job and
82
+ * appended to the versions.jsonl SHA-256 ledger.
83
+ * @param industry - the industry display name.
84
+ * @param draft - the report draft.
85
+ * @param artifacts - the source artifacts.
86
+ * @param evidenceIds - registered evidence ids.
87
+ * @param generatedAt - ISO-8601 generation time.
88
+ * @returns the note Markdown text.
89
+ */
90
+ export function renderPerspectivesNote(industry, draft, artifacts, evidenceIds, generatedAt) {
91
+ const synthesis = synthesizePerspectives(draft, artifacts, evidenceIds);
92
+ const lines = [
93
+ '# 多视角正反方笔记(bull/bear)',
94
+ '',
95
+ `> 行业:${industry};生成时间:${generatedAt}。本笔记由多视角正反方辩论 job 产出,正反方观点均来自同一份来源回溯表,仅供研究,不构成投资建议。`,
96
+ '',
97
+ '## 多方视角(bull · 利好/支撑)',
98
+ ];
99
+ if (synthesis.bull.length > 0) {
100
+ for (const point of synthesis.bull) {
101
+ lines.push(`- ${point.text}${point.evidenceIds.length > 0 ? `(证据 ${point.evidenceIds.join(', ')})` : ''}`);
102
+ }
103
+ }
104
+ else {
105
+ lines.push('- (无)');
106
+ }
107
+ lines.push('', '## 空方视角(bear · 利空/风险)');
108
+ if (synthesis.bear.length > 0) {
109
+ for (const point of synthesis.bear)
110
+ lines.push(`- ${point.text}`);
111
+ }
112
+ else {
113
+ lines.push('- (无)');
114
+ }
115
+ lines.push('', '## 综合(再合成)', '', `多方 ${synthesis.bull.length} 项、空方 ${synthesis.bear.length} 项;数值以来源证据为准,缺口如实声明。`);
116
+ lines.push('');
117
+ return lines.join('\n');
118
+ }
119
+ //# sourceMappingURL=perspectives.js.map