alphacouncil-agent 0.9.2 → 0.9.4
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/.claude/commands/alpha.md +22 -3
- package/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/.grok/commands/alpha.md +22 -3
- package/.opencode/command/alpha.md +22 -3
- package/AGENTS.md +27 -4
- package/CHANGELOG.md +67 -0
- package/CLAUDE.md +41 -7
- package/README.ja.md +21 -11
- package/README.md +35 -11
- package/README.zh-CN.md +31 -11
- package/commands/alpha.md +22 -3
- package/data/build-profile.v1.json +1 -1
- package/data/master-selector-method-locales.v1.mjs +286 -0
- package/docs/INSTALL.md +43 -6
- package/docs/releases/v0.9.3.md +86 -0
- package/docs/releases/v0.9.4.md +79 -0
- package/docs/report-contract.md +50 -1
- package/knowledge/ai-assisted-solo/experiments/runs/a.json +4 -4
- package/knowledge/ai-assisted-solo/experiments/runs/b.json +2 -2
- package/knowledge/ai-assisted-solo/experiments/runs/c.json +2 -2
- package/knowledge/ai-assisted-solo/experiments/runs/d13.json +16 -16
- package/knowledge/ai-assisted-solo/experiments/runs/d26.json +29 -29
- package/knowledge/ai-assisted-solo/experiments/runs/e-d13.json +19 -19
- package/knowledge/ai-assisted-solo/experiments/runs/e-d26.json +32 -32
- package/knowledge/ai-assisted-solo/experiments/runs/h_ai_reference.json +33 -33
- package/knowledge/ai-assisted-solo/experiments/simulation-input.json +79 -79
- package/knowledge/ai-assisted-solo/experiments/simulation-manifest.json +19 -19
- package/knowledge/solo-test/masters/master_ackman/manifest.json +14 -4
- package/knowledge/solo-test/masters/master_aschenbrenner/manifest.json +14 -4
- package/knowledge/solo-test/masters/master_asness/manifest.json +14 -4
- package/knowledge/solo-test/masters/master_buffett/manifest.json +14 -4
- package/knowledge/solo-test/masters/master_burry/manifest.json +14 -4
- package/knowledge/solo-test/masters/master_cathie_wood/manifest.json +14 -4
- package/knowledge/solo-test/masters/master_dalio/manifest.json +14 -4
- package/knowledge/solo-test/masters/master_damodaran/manifest.json +14 -4
- package/knowledge/solo-test/masters/master_druckenmiller/manifest.json +14 -4
- package/knowledge/solo-test/masters/master_duan_yongping/manifest.json +14 -4
- package/knowledge/solo-test/masters/master_fisher/manifest.json +14 -4
- package/knowledge/solo-test/masters/master_forensic_short/manifest.json +14 -4
- package/knowledge/solo-test/masters/master_graham/manifest.json +14 -4
- package/knowledge/solo-test/masters/master_jhunjhunwala/manifest.json +14 -4
- package/knowledge/solo-test/masters/master_klarman/manifest.json +14 -4
- package/knowledge/solo-test/masters/master_li_lu/manifest.json +14 -4
- package/knowledge/solo-test/masters/master_lynch/manifest.json +14 -4
- package/knowledge/solo-test/masters/master_marks/manifest.json +14 -4
- package/knowledge/solo-test/masters/master_munger/manifest.json +14 -4
- package/knowledge/solo-test/masters/master_natenberg/manifest.json +14 -4
- package/knowledge/solo-test/masters/master_pabrai/manifest.json +14 -4
- package/knowledge/solo-test/masters/master_simons/manifest.json +14 -4
- package/knowledge/solo-test/masters/master_sinclair/manifest.json +14 -4
- package/knowledge/solo-test/masters/master_soros/manifest.json +14 -4
- package/knowledge/solo-test/masters/master_taleb/manifest.json +14 -4
- package/knowledge/solo-test/masters/master_thorp/manifest.json +14 -4
- package/mcp/lib/codex.mjs +3 -3
- package/mcp/lib/constants.mjs +61 -25
- package/mcp/lib/council-options.mjs +95 -40
- package/mcp/lib/council-selection.mjs +95 -9
- package/mcp/lib/gates.mjs +78 -11
- package/mcp/lib/lang.mjs +109 -2
- package/mcp/lib/markdown.mjs +444 -135
- package/mcp/lib/master-catalog.mjs +28 -3
- package/mcp/lib/orchestrator.mjs +952 -265
- package/mcp/lib/packets.mjs +173 -20
- package/mcp/lib/personas/engine.mjs +30 -30
- package/mcp/lib/personas-v3/compiler.mjs +2 -0
- package/mcp/lib/personas-v3/loader.mjs +10 -7
- package/mcp/lib/personas-v3/source-acquisition.mjs +10 -3
- package/mcp/lib/prompts.mjs +33 -0
- package/mcp/lib/rpc.mjs +75 -33
- package/mcp/lib/run-store.mjs +23 -0
- package/package.json +2 -2
- package/schemas/persona-v3.schema.json +37 -2
- package/scripts/lib/packaged-host-parity.mjs +54 -5
- package/scripts/lib/persona-v3-solo-test-packs.mjs +31 -5
- package/scripts/package-smoke.mjs +31 -12
- package/scripts/run-tests.mjs +23 -2
- package/skills/alphacouncil-agent/SKILL.md +50 -7
package/mcp/lib/markdown.mjs
CHANGED
|
@@ -3,51 +3,59 @@ import { join } from "node:path";
|
|
|
3
3
|
import { DEBATE_ROLES, REPORT_SECTIONS } from "./constants.mjs";
|
|
4
4
|
import { writeJson } from "./fsutil.mjs";
|
|
5
5
|
import { headingIncludesAlias, normalizeHeading, parseHeadings } from "./headings.mjs";
|
|
6
|
-
import { isChineseLanguage } from "./lang.mjs";
|
|
6
|
+
import { isChineseLanguage, languageKey, localized } from "./lang.mjs";
|
|
7
7
|
import { sha256 } from "./personas-v3/canonical.mjs";
|
|
8
|
+
import { compiledPersonaPacks } from "./personas-v3/registry.mjs";
|
|
8
9
|
import { bullets, clip, clipAtBoundary, fence } from "./text.mjs";
|
|
9
10
|
import { completenessStatus, validateFinalReport, verificationStatus, withCompletenessBanner, withDisclaimer, withVerificationBanner } from "./gates.mjs";
|
|
10
11
|
import { agentState, appendEvent, artifactPaths, runPath, taskState } from "./run-store.mjs";
|
|
11
12
|
import { personaTitle, registry } from "./personas/registry.mjs";
|
|
12
13
|
|
|
13
|
-
export function renderPacketMarkdown(packet, index) {
|
|
14
|
+
export function renderPacketMarkdown(packet, index = 0, language = packet?.language) {
|
|
15
|
+
const key = languageKey(language);
|
|
16
|
+
const label = {
|
|
17
|
+
zh: { title: "证据分析子代理", symbol: "代码", asOf: "截至", confidence: "置信度", richness: "信息丰富度", summary: "摘要", claims: "论断", evidence: "证据", sources: "来源", metrics: "指标", questions: "未决问题", raw: "原始 worker 响应(仅供审计)", none: "无" },
|
|
18
|
+
en: { title: "Evidence Analyst Subagent", symbol: "Symbol", asOf: "As-of", confidence: "Confidence", richness: "Information richness", summary: "Summary", claims: "Claims", evidence: "Evidence", sources: "Sources", metrics: "Metrics", questions: "Open Questions", raw: "Raw Worker Response (audit only)", none: "None" },
|
|
19
|
+
ja: { title: "証拠分析サブエージェント", symbol: "銘柄コード", asOf: "基準日", confidence: "信頼度", richness: "情報充足度", summary: "要約", claims: "主張", evidence: "根拠", sources: "出典", metrics: "指標", questions: "未解決事項", raw: "ワーカーの生応答(監査専用)", none: "なし" },
|
|
20
|
+
ko: { title: "증거 분석 하위 에이전트", symbol: "종목 코드", asOf: "기준일", confidence: "신뢰도", richness: "정보 충실도", summary: "요약", claims: "주장", evidence: "근거", sources: "출처", metrics: "지표", questions: "미해결 질문", raw: "원본 워커 응답(감사 전용)", none: "없음" },
|
|
21
|
+
}[key];
|
|
14
22
|
const claims = packet.claims.length
|
|
15
23
|
? packet.claims.map((claim, claimIndex) => [
|
|
16
24
|
`${claimIndex + 1}. ${claim.claim || ""}`,
|
|
17
|
-
` -
|
|
18
|
-
` -
|
|
19
|
-
` -
|
|
25
|
+
` - ${label.evidence}: ${claim.evidence || ""}`,
|
|
26
|
+
` - ${label.confidence}: ${claim.confidence || "low"}`,
|
|
27
|
+
` - ${label.sources}: ${(claim.source_ids || []).join(", ") || label.none}`,
|
|
20
28
|
].join("\n")).join("\n")
|
|
21
|
-
:
|
|
29
|
+
: `${label.none}.`;
|
|
22
30
|
const sources = packet.sources.length
|
|
23
31
|
? packet.sources.map((source) => `- ${source.id || "S?"}: ${source.title || ""} (${source.published_at || "unknown"}) ${source.url || ""}`).join("\n")
|
|
24
|
-
:
|
|
32
|
+
: `- ${label.none}`;
|
|
25
33
|
return [
|
|
26
|
-
`##
|
|
34
|
+
`## ${label.title} ${index + 1}: ${packet.task}`,
|
|
27
35
|
"",
|
|
28
|
-
`-
|
|
29
|
-
`-
|
|
36
|
+
`- ${label.symbol}: ${packet.symbol}`,
|
|
37
|
+
`- ${label.asOf}: ${packet.as_of}`,
|
|
30
38
|
packet.thread_id ? `- Visible thread ID: ${packet.thread_id}` : "",
|
|
31
39
|
packet.thread_title ? `- Visible thread title: ${packet.thread_title}` : "",
|
|
32
|
-
`-
|
|
33
|
-
`-
|
|
40
|
+
`- ${label.confidence}: ${packet.confidence}`,
|
|
41
|
+
`- ${label.richness}: ${packet.information_richness || "unrated"}`,
|
|
34
42
|
"",
|
|
35
|
-
|
|
43
|
+
`### ${label.summary}`,
|
|
36
44
|
packet.summary || "",
|
|
37
45
|
"",
|
|
38
|
-
|
|
46
|
+
`### ${label.claims}`,
|
|
39
47
|
claims,
|
|
40
48
|
"",
|
|
41
|
-
|
|
49
|
+
`### ${label.metrics}`,
|
|
42
50
|
fence(packet.metrics || {}, "json"),
|
|
43
51
|
"",
|
|
44
|
-
|
|
52
|
+
`### ${label.sources}`,
|
|
45
53
|
sources,
|
|
46
54
|
"",
|
|
47
|
-
|
|
55
|
+
`### ${label.questions}`,
|
|
48
56
|
bullets(packet.open_questions),
|
|
49
57
|
"",
|
|
50
|
-
|
|
58
|
+
`### ${label.raw}`,
|
|
51
59
|
fence(packet.raw_text || "", "text"),
|
|
52
60
|
].join("\n");
|
|
53
61
|
}
|
|
@@ -63,35 +71,68 @@ export function renderPacketMarkdown(packet, index) {
|
|
|
63
71
|
export function renderMasterMarkdown(opinion, lang) {
|
|
64
72
|
if (!opinion) return "";
|
|
65
73
|
const title = masterTitle(opinion.master, lang);
|
|
74
|
+
const labels = {
|
|
75
|
+
zh: { statement: "本轮专属方法席发言(不是大师本人引语)", stance: "立场", verdict: "冻结判断", confidence: "置信度", worker: "专属 worker", summary: "方法席说明", findings: "关键发现", disagreements: "与分析师的分歧", disqualifiers: "触发的排除条件", change: "改变判断所需证据", sources: "来源" },
|
|
76
|
+
en: { statement: "Dedicated Method-Seat Statement (not a quote from the named person)", stance: "Stance", verdict: "Frozen verdict", confidence: "Confidence", worker: "Dedicated worker", summary: "Method-seat explanation", findings: "Key Findings", disagreements: "Disagreements With The Analysts", disqualifiers: "Disqualifiers Triggered", change: "What Would Change The View", sources: "Sources" },
|
|
77
|
+
ja: { statement: "専用メソッド席の発言(本人の発言・引用ではありません)", stance: "スタンス", verdict: "凍結済み判定", confidence: "信頼度", worker: "専用ワーカー", summary: "メソッド席の説明", findings: "主な所見", disagreements: "分析担当との相違", disqualifiers: "発動した除外条件", change: "判断が変わる条件", sources: "出典" },
|
|
78
|
+
ko: { statement: "전용 방법론 좌석 발언(본인의 실제 발언이나 인용이 아님)", stance: "입장", verdict: "동결된 판단", confidence: "신뢰도", worker: "전용 워커", summary: "방법론 좌석 설명", findings: "핵심 발견", disagreements: "분석가와의 이견", disqualifiers: "발동된 제외 조건", change: "판단 변경 조건", sources: "출처" },
|
|
79
|
+
}[languageKey(lang)];
|
|
66
80
|
return [
|
|
67
81
|
`## ${title}`,
|
|
68
82
|
"",
|
|
69
83
|
`- ID: ${opinion.master}`,
|
|
70
|
-
`-
|
|
71
|
-
`-
|
|
72
|
-
`-
|
|
84
|
+
`- ${labels.stance}: ${opinion.stance || "unknown"}`,
|
|
85
|
+
`- ${labels.verdict}: ${opinion.verdict || ""}`,
|
|
86
|
+
`- ${labels.confidence}: ${opinion.confidence || "low"}`,
|
|
87
|
+
`- ${labels.worker}: ${opinion.dedicated_worker?.status || opinion.voice_status || "not_recorded"}${opinion.dedicated_worker?.pid ? ` (pid ${opinion.dedicated_worker.pid})` : ""}`,
|
|
73
88
|
opinion.thread_id ? `- Visible thread ID: ${opinion.thread_id}` : "",
|
|
74
89
|
"",
|
|
75
|
-
|
|
76
|
-
opinion.summary || "",
|
|
90
|
+
`### ${labels.statement}`,
|
|
91
|
+
opinion.voice_statement || opinion.summary || "",
|
|
77
92
|
"",
|
|
78
|
-
|
|
93
|
+
`### ${labels.summary}`,
|
|
94
|
+
opinion.deterministic_summary || opinion.summary || "",
|
|
95
|
+
"",
|
|
96
|
+
`### ${labels.findings}`,
|
|
79
97
|
bullets(opinion.key_findings),
|
|
80
98
|
"",
|
|
81
|
-
|
|
99
|
+
`### ${labels.disagreements}`,
|
|
82
100
|
bullets(opinion.disagreements),
|
|
83
101
|
"",
|
|
84
|
-
|
|
102
|
+
`### ${labels.disqualifiers}`,
|
|
85
103
|
bullets(opinion.disqualifiers_triggered),
|
|
86
104
|
"",
|
|
87
|
-
|
|
105
|
+
`### ${labels.change}`,
|
|
88
106
|
bullets(opinion.what_would_change_my_mind),
|
|
89
107
|
"",
|
|
90
|
-
|
|
108
|
+
`### ${labels.sources}`,
|
|
91
109
|
(opinion.source_ids || []).length ? (opinion.source_ids || []).map((id) => `- ${id}`).join("\n") : "- None",
|
|
92
110
|
].filter((line) => line !== "").join("\n");
|
|
93
111
|
}
|
|
94
112
|
|
|
113
|
+
function renderMasterStatements(run) {
|
|
114
|
+
const key = languageKey(run?.language);
|
|
115
|
+
const heading = { zh: "逐席专属方法输出", en: "Dedicated Method-Seat Outputs", ja: "専用メソッド席ごとの出力", ko: "전용 방법론 좌석별 출력" }[key];
|
|
116
|
+
const statementLabel = { zh: "本轮发言(不是本人引语)", en: "Recorded statement (not a quote)", ja: "今回の発言(本人の引用ではありません)", ko: "이번 발언(본인 인용이 아님)" }[key];
|
|
117
|
+
const findingsLabel = { zh: "关键发现", en: "Key findings", ja: "主な所見", ko: "핵심 발견" }[key];
|
|
118
|
+
const disagreementsLabel = { zh: "与分析师分歧", en: "Disagreements", ja: "分析担当との相違", ko: "분석가와의 이견" }[key];
|
|
119
|
+
const changeLabel = { zh: "改变判断条件", en: "What would change the view", ja: "判断が変わる条件", ko: "판단 변경 조건" }[key];
|
|
120
|
+
const sourcesLabel = { zh: "来源或明确缺口", en: "Sources or explicit gaps", ja: "出典または明示的な欠落", ko: "출처 또는 명시적 데이터 공백" }[key];
|
|
121
|
+
const voiced = (run?.master_opinions || []).filter((opinion) => Boolean(opinion.voice_statement));
|
|
122
|
+
if (!voiced.length) return "";
|
|
123
|
+
const items = voiced.map((opinion) => [
|
|
124
|
+
`### ${masterTitle(opinion.master, run.language)} (\`${opinion.master}\`)`,
|
|
125
|
+
`- ${key === "zh" ? "立场" : key === "ja" ? "スタンス" : key === "ko" ? "입장" : "Stance"}: ${opinion.stance || "unknown"}`,
|
|
126
|
+
`- ${key === "zh" ? "专属 worker" : key === "ja" ? "専用ワーカー" : key === "ko" ? "전용 워커" : "Dedicated worker"}: ${opinion.dedicated_worker?.status || opinion.voice_status || "not_recorded"}`,
|
|
127
|
+
`- ${statementLabel}: ${opinion.voice_statement || opinion.verdict || opinion.summary || ""}`,
|
|
128
|
+
`- ${findingsLabel}: ${(opinion.key_findings || []).slice(0, 4).join(";") || "—"}`,
|
|
129
|
+
`- ${disagreementsLabel}: ${(opinion.disagreements || []).slice(0, 3).join(";") || "—"}`,
|
|
130
|
+
`- ${changeLabel}: ${(opinion.what_would_change_my_mind || []).slice(0, 3).join(";") || "—"}`,
|
|
131
|
+
`- ${sourcesLabel}: ${(opinion.source_ids || []).join(", ") || (opinion.disqualifiers_triggered || []).join(", ") || "—"}`,
|
|
132
|
+
].join("\n")).join("\n\n");
|
|
133
|
+
return `### ${heading}\n\n${items}`;
|
|
134
|
+
}
|
|
135
|
+
|
|
95
136
|
/**
|
|
96
137
|
* Printed above every rendered bench, in the report's language.
|
|
97
138
|
*
|
|
@@ -110,19 +151,31 @@ export function masterCorrelationNote(run) {
|
|
|
110
151
|
stances.set(key, (stances.get(key) || 0) + 1);
|
|
111
152
|
}
|
|
112
153
|
const spread = [...stances.entries()].map(([stance, n]) => `${stance}=${n}`).join(", ");
|
|
113
|
-
const
|
|
114
|
-
return
|
|
115
|
-
|
|
154
|
+
const key = languageKey(run?.language);
|
|
155
|
+
return {
|
|
156
|
+
zh: [
|
|
116
157
|
"> **这些席位不是独立样本。** 它们共享同一个基础模型、同一份证据简报和同一个上下文,",
|
|
117
158
|
`> 因此错误是相关的。本次立场分布(${spread})**不能当作票数来计算**:一致本身是预期结果,`,
|
|
118
159
|
"> 不是发现。有信息量的是分歧席位,以及它的分歧来自信息差还是方法差。",
|
|
119
160
|
].join("\n")
|
|
120
|
-
|
|
161
|
+
,
|
|
162
|
+
en: [
|
|
121
163
|
"> **These seats are not independent samples.** They share a base model, an evidence",
|
|
122
164
|
`> brief and a context window, so their errors are correlated. The stance spread (${spread})`,
|
|
123
165
|
"> **is not a vote count**: agreement is the expected outcome, not a finding. The",
|
|
124
166
|
"> informative seat is the dissenting one, and why it dissents.",
|
|
125
|
-
].join("\n")
|
|
167
|
+
].join("\n"),
|
|
168
|
+
ja: [
|
|
169
|
+
"> **これらの席は独立標本ではありません。** 同じ基盤モデル、証拠要約、コンテキストを共有するため、",
|
|
170
|
+
`> 誤りも相関します。立場の分布(${spread})を**投票数として数えてはいけません**。一致は予想される結果であり、発見ではありません。`,
|
|
171
|
+
"> 情報量が高いのは反対する席と、その相違が情報差か方法差かという点です。",
|
|
172
|
+
].join("\n"),
|
|
173
|
+
ko: [
|
|
174
|
+
"> **이 좌석들은 독립 표본이 아닙니다.** 동일한 기반 모델, 증거 요약, 컨텍스트를 공유하므로 오류도 상관되어 있습니다.",
|
|
175
|
+
`> 입장 분포(${spread})를 **투표수로 계산하면 안 됩니다**. 일치는 예상되는 결과이지 새로운 발견이 아닙니다.`,
|
|
176
|
+
"> 정보량이 높은 부분은 반대 좌석과 그 차이가 정보 차이인지 방법론 차이인지입니다.",
|
|
177
|
+
].join("\n"),
|
|
178
|
+
}[key];
|
|
126
179
|
}
|
|
127
180
|
|
|
128
181
|
/**
|
|
@@ -137,7 +190,7 @@ export function masterCorrelationNote(run) {
|
|
|
137
190
|
export function renderBenchSummary(run) {
|
|
138
191
|
const opinions = run?.master_opinions || [];
|
|
139
192
|
if (!opinions.length) return "";
|
|
140
|
-
const
|
|
193
|
+
const key = languageKey(run?.language);
|
|
141
194
|
const counts = new Map();
|
|
142
195
|
for (const o of opinions) counts.set(o.stance || "unknown", (counts.get(o.stance || "unknown") || 0) + 1);
|
|
143
196
|
const ranked = [...counts.entries()].sort((a, b) => b[1] - a[1]);
|
|
@@ -145,19 +198,24 @@ export function renderBenchSummary(run) {
|
|
|
145
198
|
const minority = opinions.filter((o) => (o.stance || "unknown") !== majority);
|
|
146
199
|
const concurring = opinions.filter((o) => (o.stance || "unknown") === majority);
|
|
147
200
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
201
|
+
// The table is the frozen deterministic judgment; the dedicated worker statement is
|
|
202
|
+
// rendered once in the per-seat detail block below. Keeping them separate prevents a
|
|
203
|
+
// fluent explanation from being mistaken for, or duplicating, the frozen method result.
|
|
204
|
+
const row = (o) => `| ${masterTitle(o.master, run?.language)} (\`${o.master}\`) | ${o.stance || "unknown"} | ${o.confidence || "low"} | ${clip(o.verdict || o.deterministic_summary || o.summary || o.voice_statement || "", 140)} |`;
|
|
205
|
+
const copy = {
|
|
206
|
+
zh: { head: ["| 方法 | 立场 | 置信度 | 判断 |", "|---|---|---|---|"], minority: "### 少数派(先读这个)", minorityNone: "### 少数派:无\n\n所有席位立场一致。鉴于它们共享模型与证据,一致是预期结果而非确认——本轮没有产生任何独立的反对意见。", divergence: `${minority.length} 席与多数不同。分歧席位是本轮信息量最高的部分——请先判断分歧来自信息差还是方法差。`, rest: "### 其余席位" },
|
|
207
|
+
en: { head: ["| Method | Stance | Confidence | Verdict |", "|---|---|---|---|"], minority: "### Minority report (read this first)", minorityNone: "### Minority report: none\n\nEvery seat agreed. Given a shared model and a shared brief, agreement is the expected outcome rather than confirmation: this run produced no independent dissent.", divergence: `${minority.length} seat(s) diverge. Divergence is the highest-information part of this run: establish whether it comes from the evidence slice or from the method.`, rest: "### Concurring seats" },
|
|
208
|
+
ja: { head: ["| メソッド | スタンス | 信頼度 | 判定 |", "|---|---|---|---|"], minority: "### 少数意見(最初に読む)", minorityNone: "### 少数意見:なし\n\n全席が一致しました。同じモデルと証拠を共有するため、一致は確認ではなく予想される結果です。本輪では独立した反対意見が出ませんでした。", divergence: `${minority.length}席が多数と異なります。最も情報量が高い部分なので、相違が証拠範囲と方法のどちらに由来するか確認してください。`, rest: "### その他の席" },
|
|
209
|
+
ko: { head: ["| 방법론 | 입장 | 신뢰도 | 판단 |", "|---|---|---|---|"], minority: "### 소수 의견(먼저 확인)", minorityNone: "### 소수 의견: 없음\n\n모든 좌석이 일치했습니다. 동일한 모델과 증거를 공유하므로 일치는 확인이 아니라 예상되는 결과입니다. 이번 실행에는 독립적인 반대 의견이 없었습니다.", divergence: `${minority.length}개 좌석이 다수와 다릅니다. 가장 정보량이 높은 부분이므로 차이가 증거 범위와 방법론 중 어디에서 비롯됐는지 확인하십시오.`, rest: "### 나머지 좌석" },
|
|
210
|
+
}[key];
|
|
211
|
+
const head = copy.head;
|
|
152
212
|
|
|
153
213
|
const sections = [masterCorrelationNote(run), ""];
|
|
154
214
|
if (minority.length) {
|
|
155
215
|
sections.push(
|
|
156
|
-
|
|
216
|
+
copy.minority,
|
|
157
217
|
"",
|
|
158
|
-
|
|
159
|
-
? `${minority.length} 席与多数不同。分歧席位是本次运行里信息量最高的部分——请先判断分歧来自信息差还是方法差。`
|
|
160
|
-
: `${minority.length} seat(s) diverge. Divergence is the highest-information part of this run: establish whether it comes from the evidence slice or from the method.`,
|
|
218
|
+
copy.divergence,
|
|
161
219
|
"",
|
|
162
220
|
...head,
|
|
163
221
|
...minority.map(row),
|
|
@@ -165,14 +223,12 @@ export function renderBenchSummary(run) {
|
|
|
165
223
|
);
|
|
166
224
|
} else {
|
|
167
225
|
sections.push(
|
|
168
|
-
|
|
169
|
-
? "### 少数派:无\n\n所有席位立场一致。鉴于它们共享模型与证据,一致是预期结果而非确认——本次运行没有产生任何独立的反对意见。"
|
|
170
|
-
: "### Minority report: none\n\nEvery seat agreed. Given a shared model and a shared brief, agreement is the expected outcome rather than confirmation: this run produced no independent dissent.",
|
|
226
|
+
copy.minorityNone,
|
|
171
227
|
"",
|
|
172
228
|
);
|
|
173
229
|
}
|
|
174
230
|
sections.push(
|
|
175
|
-
|
|
231
|
+
copy.rest,
|
|
176
232
|
"",
|
|
177
233
|
...head,
|
|
178
234
|
...concurring.map(row),
|
|
@@ -214,6 +270,9 @@ export function renderDecisionTable(decisions, lang) {
|
|
|
214
270
|
function masterTitle(id, lang) {
|
|
215
271
|
if (!id) return "Master";
|
|
216
272
|
try {
|
|
273
|
+
const v3 = compiledPersonaPacks().get(id);
|
|
274
|
+
const v3Title = v3?.admitted_label?.[languageKey(lang)];
|
|
275
|
+
if (v3Title) return `${v3Title} (${id})`;
|
|
217
276
|
const persona = registry().get(id);
|
|
218
277
|
const title = personaTitle(persona, lang);
|
|
219
278
|
return title && title !== id ? `${title} (${id})` : id;
|
|
@@ -222,81 +281,102 @@ function masterTitle(id, lang) {
|
|
|
222
281
|
}
|
|
223
282
|
}
|
|
224
283
|
|
|
225
|
-
export function renderDebateRounds(rounds) {
|
|
284
|
+
export function renderDebateRounds(rounds, language = "English") {
|
|
226
285
|
if (!Array.isArray(rounds) || rounds.length === 0) return "";
|
|
286
|
+
const key = languageKey(language);
|
|
287
|
+
const label = {
|
|
288
|
+
zh: { round: "轮次", long: "多头论据", short: "空头论据", questions: "提出的问题", answered: "逐题回答", raw: "原始 worker 响应(仅供审计)", title: "辩论轮次" },
|
|
289
|
+
en: { round: "Round", long: "Long Thesis", short: "Short Thesis", questions: "Questions", answered: "Questions Answered", raw: "Raw Worker Response (audit only)", title: "Debate Rounds" },
|
|
290
|
+
ja: { round: "ラウンド", long: "強気論拠", short: "弱気論拠", questions: "質問", answered: "質問への回答", raw: "ワーカーの生応答(監査専用)", title: "討論ラウンド" },
|
|
291
|
+
ko: { round: "라운드", long: "강세 논거", short: "약세 논거", questions: "질문", answered: "질문별 답변", raw: "원본 워커 응답(감사 전용)", title: "토론 라운드" },
|
|
292
|
+
}[key];
|
|
227
293
|
const blocks = rounds.map((round) => [
|
|
228
|
-
`####
|
|
294
|
+
`#### ${label.round} ${round.round}`,
|
|
229
295
|
"",
|
|
230
296
|
round.summary || "",
|
|
231
297
|
"",
|
|
232
|
-
|
|
298
|
+
`##### ${label.long}`,
|
|
233
299
|
bullets(round.long_thesis),
|
|
234
300
|
"",
|
|
235
|
-
|
|
301
|
+
`##### ${label.short}`,
|
|
236
302
|
bullets(round.short_thesis),
|
|
237
303
|
"",
|
|
238
|
-
|
|
304
|
+
`##### ${label.questions}`,
|
|
239
305
|
bullets(round.questions),
|
|
240
306
|
"",
|
|
241
|
-
|
|
307
|
+
`##### ${label.answered}`,
|
|
242
308
|
bullets(round.questions_answered),
|
|
243
309
|
"",
|
|
244
|
-
|
|
310
|
+
`##### ${label.raw}`,
|
|
245
311
|
fence(round.raw_text || "", "text"),
|
|
246
312
|
].join("\n"));
|
|
247
|
-
return [
|
|
313
|
+
return [`### ${label.title}`, "", ...blocks].join("\n\n");
|
|
248
314
|
}
|
|
249
315
|
|
|
250
|
-
export function renderDebateMarkdown(agent) {
|
|
316
|
+
export function renderDebateMarkdown(agent, language = agent?.language) {
|
|
251
317
|
if (!agent) return "";
|
|
318
|
+
const key = languageKey(language);
|
|
319
|
+
const label = {
|
|
320
|
+
zh: { rating: "评级", winner: "胜方", verdict: "判断", confidence: "置信度", summary: "摘要", long: "多头论据", short: "空头论据", valuation: "估值区间", catalysts: "催化剂", risks: "风险", position: "仓位", invalidation: "失效条件", sources: "来源 ID", report: "报告正文", raw: "原始 worker 响应(仅供审计)", none: "无" },
|
|
321
|
+
en: { rating: "Rating", winner: "Winner", verdict: "Verdict", confidence: "Confidence", summary: "Summary", long: "Long Thesis", short: "Short Thesis", valuation: "Valuation Range", catalysts: "Catalysts", risks: "Risks", position: "Position", invalidation: "Invalidation", sources: "Source IDs", report: "Report Markdown", raw: "Raw Worker Response (audit only)", none: "None" },
|
|
322
|
+
ja: { rating: "評価", winner: "優勢側", verdict: "判断", confidence: "信頼度", summary: "要約", long: "強気論拠", short: "弱気論拠", valuation: "評価レンジ", catalysts: "カタリスト", risks: "リスク", position: "ポジション", invalidation: "無効化条件", sources: "出典 ID", report: "レポート本文", raw: "ワーカーの生応答(監査専用)", none: "なし" },
|
|
323
|
+
ko: { rating: "등급", winner: "우세 측", verdict: "판단", confidence: "신뢰도", summary: "요약", long: "강세 논거", short: "약세 논거", valuation: "가치평가 범위", catalysts: "촉매", risks: "위험", position: "포지션", invalidation: "무효화 조건", sources: "출처 ID", report: "보고서 본문", raw: "원본 워커 응답(감사 전용)", none: "없음" },
|
|
324
|
+
}[key];
|
|
252
325
|
return [
|
|
253
326
|
`## ${agent.role}`,
|
|
254
327
|
"",
|
|
255
|
-
`-
|
|
256
|
-
`-
|
|
257
|
-
`-
|
|
258
|
-
`-
|
|
328
|
+
`- ${label.rating}: ${agent.rating}`,
|
|
329
|
+
`- ${label.winner}: ${agent.winner}`,
|
|
330
|
+
`- ${label.verdict}: ${agent.verdict}`,
|
|
331
|
+
`- ${label.confidence}: ${agent.confidence}`,
|
|
259
332
|
agent.thread_id ? `- Visible thread ID: ${agent.thread_id}` : "",
|
|
260
333
|
agent.thread_title ? `- Visible thread title: ${agent.thread_title}` : "",
|
|
261
334
|
"",
|
|
262
|
-
|
|
335
|
+
`### ${label.summary}`,
|
|
263
336
|
agent.summary || "",
|
|
264
337
|
"",
|
|
265
|
-
|
|
338
|
+
`### ${label.long}`,
|
|
266
339
|
bullets(agent.long_thesis),
|
|
267
340
|
"",
|
|
268
|
-
|
|
341
|
+
`### ${label.short}`,
|
|
269
342
|
bullets(agent.short_thesis),
|
|
270
343
|
"",
|
|
271
|
-
|
|
272
|
-
agent.valuation_range ||
|
|
344
|
+
`### ${label.valuation}`,
|
|
345
|
+
agent.valuation_range || label.none,
|
|
273
346
|
"",
|
|
274
|
-
|
|
347
|
+
`### ${label.catalysts}`,
|
|
275
348
|
bullets(agent.catalysts),
|
|
276
349
|
"",
|
|
277
|
-
|
|
350
|
+
`### ${label.risks}`,
|
|
278
351
|
bullets(agent.risks),
|
|
279
352
|
"",
|
|
280
|
-
|
|
281
|
-
agent.position ||
|
|
353
|
+
`### ${label.position}`,
|
|
354
|
+
agent.position || label.none,
|
|
282
355
|
"",
|
|
283
|
-
|
|
356
|
+
`### ${label.invalidation}`,
|
|
284
357
|
bullets(agent.invalidation),
|
|
285
358
|
"",
|
|
286
|
-
|
|
359
|
+
`### ${label.sources}`,
|
|
287
360
|
bullets(agent.source_ids),
|
|
288
361
|
"",
|
|
289
|
-
|
|
362
|
+
`### ${label.report}`,
|
|
290
363
|
agent.report_markdown || "",
|
|
291
364
|
"",
|
|
292
|
-
renderDebateRounds(agent.debate_rounds),
|
|
293
|
-
|
|
365
|
+
renderDebateRounds(agent.debate_rounds, language),
|
|
366
|
+
`### ${label.raw}`,
|
|
294
367
|
fence(agent.raw_text || "", "text"),
|
|
295
368
|
].filter(Boolean).join("\n");
|
|
296
369
|
}
|
|
297
370
|
|
|
298
371
|
export function writeAllAgentsMarkdown(run, debate = {}) {
|
|
299
372
|
const dir = runPath(run.run_id);
|
|
373
|
+
const key = languageKey(run.language);
|
|
374
|
+
const label = {
|
|
375
|
+
zh: { title: "全部代理审计追踪", metadata: "运行元数据", runId: "运行 ID", symbol: "代码", asOf: "截至", language: "语言", execution: "执行模式", visibility: "要求可见", dry: "演练", status: "状态", phase: "阶段", started: "开始", updated: "更新", completed: "完成", tasks: "任务", taskStatus: "证据席状态", debateStatus: "辩论席状态", evidence: "证据分析子代理", masters: "方法席", debate: "多空辩论与组合经理", none: "无" },
|
|
376
|
+
en: { title: "Full Agent Audit Trace", metadata: "Run Metadata", runId: "Run ID", symbol: "Symbol", asOf: "As-of", language: "Language", execution: "Execution mode", visibility: "Visibility required", dry: "Dry run", status: "Status", phase: "Phase", started: "Started", updated: "Updated", completed: "Completed", tasks: "Tasks", taskStatus: "Evidence-Seat Status", debateStatus: "Debate-Seat Status", evidence: "Evidence Analyst Subagents", masters: "Method-Seat Bench", debate: "Bull/Bear Debate and Portfolio Manager", none: "None" },
|
|
377
|
+
ja: { title: "全エージェント監査トレース", metadata: "実行メタデータ", runId: "実行 ID", symbol: "銘柄コード", asOf: "基準日", language: "言語", execution: "実行モード", visibility: "可視性要件", dry: "ドライラン", status: "状態", phase: "フェーズ", started: "開始", updated: "更新", completed: "完了", tasks: "タスク", taskStatus: "証拠席の状態", debateStatus: "討論席の状態", evidence: "証拠分析サブエージェント", masters: "メソッド席", debate: "強気・弱気討論とポートフォリオ管理者", none: "なし" },
|
|
378
|
+
ko: { title: "전체 에이전트 감사 추적", metadata: "실행 메타데이터", runId: "실행 ID", symbol: "종목 코드", asOf: "기준일", language: "언어", execution: "실행 모드", visibility: "가시성 요구", dry: "드라이런", status: "상태", phase: "단계", started: "시작", updated: "갱신", completed: "완료", tasks: "작업", taskStatus: "증거 좌석 상태", debateStatus: "토론 좌석 상태", evidence: "증거 분석 하위 에이전트", masters: "방법론 좌석", debate: "강세·약세 토론 및 포트폴리오 관리자", none: "없음" },
|
|
379
|
+
}[key];
|
|
300
380
|
const taskStatus = run.tasks.map((task) => {
|
|
301
381
|
const state = taskState(run, task);
|
|
302
382
|
return `- ${task}: ${state.status}${state.output ? ` (${state.output})` : ""}${state.error ? ` - ${state.error}` : ""}`;
|
|
@@ -306,41 +386,41 @@ export function writeAllAgentsMarkdown(run, debate = {}) {
|
|
|
306
386
|
return `- ${role}: ${state.status}${state.output ? ` (${state.output})` : ""}${state.error ? ` - ${state.error}` : ""}`;
|
|
307
387
|
}).join("\n");
|
|
308
388
|
const sections = [
|
|
309
|
-
`# AlphaCouncil Agent
|
|
389
|
+
`# AlphaCouncil Agent ${label.title}: ${run.symbol}`,
|
|
310
390
|
"",
|
|
311
|
-
|
|
391
|
+
`## ${label.metadata}`,
|
|
312
392
|
"",
|
|
313
|
-
`-
|
|
314
|
-
`-
|
|
315
|
-
`-
|
|
316
|
-
`-
|
|
317
|
-
`-
|
|
318
|
-
`-
|
|
319
|
-
`-
|
|
320
|
-
`-
|
|
321
|
-
`-
|
|
322
|
-
`-
|
|
323
|
-
`-
|
|
324
|
-
`-
|
|
325
|
-
`-
|
|
393
|
+
`- ${label.runId}: ${run.run_id}`,
|
|
394
|
+
`- ${label.symbol}: ${run.symbol}`,
|
|
395
|
+
`- ${label.asOf}: ${run.as_of}`,
|
|
396
|
+
`- ${label.language}: ${run.language || "auto"}`,
|
|
397
|
+
`- ${label.execution}: ${run.execution_mode || "background_codex_exec"}`,
|
|
398
|
+
`- ${label.visibility}: ${run.visibility_required || false}`,
|
|
399
|
+
`- ${label.dry}: ${run.dry_run}`,
|
|
400
|
+
`- ${label.status}: ${run.status || "unknown"}`,
|
|
401
|
+
`- ${label.phase}: ${run.phase || "unknown"}`,
|
|
402
|
+
`- ${label.started}: ${run.started_at}`,
|
|
403
|
+
`- ${label.updated}: ${run.updated_at || ""}`,
|
|
404
|
+
`- ${label.completed}: ${run.completed_at || ""}`,
|
|
405
|
+
`- ${label.tasks}: ${run.tasks.join(", ")}`,
|
|
326
406
|
"",
|
|
327
|
-
|
|
407
|
+
`## ${label.taskStatus}`,
|
|
328
408
|
"",
|
|
329
|
-
taskStatus ||
|
|
409
|
+
taskStatus || `- ${label.none}`,
|
|
330
410
|
"",
|
|
331
|
-
|
|
411
|
+
`## ${label.debateStatus}`,
|
|
332
412
|
"",
|
|
333
|
-
agentStatus ||
|
|
413
|
+
agentStatus || `- ${label.none}`,
|
|
334
414
|
"",
|
|
335
|
-
|
|
415
|
+
`# ${label.evidence}`,
|
|
336
416
|
"",
|
|
337
|
-
...run.packets.map(renderPacketMarkdown),
|
|
417
|
+
...run.packets.map((packet, index) => renderPacketMarkdown(packet, index, run.language)),
|
|
338
418
|
];
|
|
339
419
|
const opinions = run.master_opinions || [];
|
|
340
420
|
if (opinions.length) {
|
|
341
421
|
sections.push(
|
|
342
422
|
"",
|
|
343
|
-
|
|
423
|
+
`# ${label.masters}`,
|
|
344
424
|
"",
|
|
345
425
|
renderBenchSummary(run),
|
|
346
426
|
"",
|
|
@@ -350,13 +430,13 @@ export function writeAllAgentsMarkdown(run, debate = {}) {
|
|
|
350
430
|
if (debate.bull || debate.bear || debate.manager) {
|
|
351
431
|
sections.push(
|
|
352
432
|
"",
|
|
353
|
-
|
|
433
|
+
`# ${label.debate}`,
|
|
354
434
|
"",
|
|
355
|
-
renderDebateMarkdown(debate.bull),
|
|
435
|
+
renderDebateMarkdown(debate.bull, run.language),
|
|
356
436
|
"",
|
|
357
|
-
renderDebateMarkdown(debate.bear),
|
|
437
|
+
renderDebateMarkdown(debate.bear, run.language),
|
|
358
438
|
"",
|
|
359
|
-
renderDebateMarkdown(debate.manager),
|
|
439
|
+
renderDebateMarkdown(debate.manager, run.language),
|
|
360
440
|
);
|
|
361
441
|
}
|
|
362
442
|
const path = join(dir, "all_agents.md");
|
|
@@ -367,7 +447,7 @@ export function writeAllAgentsMarkdown(run, debate = {}) {
|
|
|
367
447
|
export function writeAnalystMarkdownFiles(run, debate = {}) {
|
|
368
448
|
const dir = runPath(run.run_id);
|
|
369
449
|
for (const [index, packet] of (run.packets || []).entries()) {
|
|
370
|
-
writeFileSync(join(dir, `${packet.task}.md`), `${renderPacketMarkdown(packet, index)}\n`);
|
|
450
|
+
writeFileSync(join(dir, `${packet.task}.md`), `${renderPacketMarkdown(packet, index, run.language)}\n`);
|
|
371
451
|
}
|
|
372
452
|
const debateFiles = [
|
|
373
453
|
["bull_researcher", debate.bull],
|
|
@@ -375,7 +455,7 @@ export function writeAnalystMarkdownFiles(run, debate = {}) {
|
|
|
375
455
|
["portfolio_manager", debate.manager],
|
|
376
456
|
];
|
|
377
457
|
for (const [role, packet] of debateFiles) {
|
|
378
|
-
if (packet) writeFileSync(join(dir, `${role}.md`), `${renderDebateMarkdown({ ...packet, role })}\n`);
|
|
458
|
+
if (packet) writeFileSync(join(dir, `${role}.md`), `${renderDebateMarkdown({ ...packet, role }, run.language)}\n`);
|
|
379
459
|
}
|
|
380
460
|
for (const opinion of run.master_opinions || []) {
|
|
381
461
|
writeFileSync(join(dir, `${opinion.master}.md`), `${renderMasterMarkdown(opinion, run.language)}\n`);
|
|
@@ -398,6 +478,8 @@ function recordedBenchMarker(run) {
|
|
|
398
478
|
stance: String(opinion?.stance || "unknown"),
|
|
399
479
|
summary: String(opinion?.summary || ""),
|
|
400
480
|
verdict: String(opinion?.verdict || ""),
|
|
481
|
+
voice_statement: String(opinion?.voice_statement || ""),
|
|
482
|
+
voice_status: String(opinion?.voice_status || ""),
|
|
401
483
|
}));
|
|
402
484
|
return `<!-- ${RECORDED_BENCH_MARKER_PREFIX}${sha256(subject)} -->`;
|
|
403
485
|
}
|
|
@@ -422,12 +504,18 @@ function withRecordedMasterBench(run, markdown) {
|
|
|
422
504
|
const lines = body.split(/\r?\n/);
|
|
423
505
|
const headings = parseHeadings(body);
|
|
424
506
|
const removals = [];
|
|
425
|
-
const commentaryTitle =
|
|
426
|
-
|
|
427
|
-
: "PM Commentary on Method Seats (non-authoritative)"
|
|
507
|
+
const commentaryTitle = localized(run.language, {
|
|
508
|
+
zh: "PM 对方法席位的叙述(非系统记录)",
|
|
509
|
+
en: "PM Commentary on Method Seats (non-authoritative)",
|
|
510
|
+
ja: "PMによるメソッド席の説明(非公式記録)",
|
|
511
|
+
ko: "PM의 방법론 좌석 설명(비공식 기록)",
|
|
512
|
+
});
|
|
428
513
|
|
|
429
514
|
for (const [index, heading] of headings.entries()) {
|
|
430
|
-
|
|
515
|
+
// Only a publication section can own the bench. Localized per-seat subheadings such as
|
|
516
|
+
// Japanese "メソッド席" may contain a bench alias, but they are level 3+ details and
|
|
517
|
+
// must not be scheduled for a second overlapping removal during idempotent assembly.
|
|
518
|
+
if (heading.level > 2 || !isMasterBenchHeading(heading.title)) continue;
|
|
431
519
|
const next = headings.slice(index + 1).find((candidate) => candidate.level <= heading.level);
|
|
432
520
|
const start = heading.line - 1;
|
|
433
521
|
const end = next ? next.line - 1 : lines.length;
|
|
@@ -442,11 +530,52 @@ function withRecordedMasterBench(run, markdown) {
|
|
|
442
530
|
lines.splice(start, end - start);
|
|
443
531
|
}
|
|
444
532
|
const cleaned = lines.join("\n").trimEnd();
|
|
445
|
-
const heading =
|
|
446
|
-
|
|
533
|
+
const heading = localized(run.language, {
|
|
534
|
+
zh: "## 大师席位",
|
|
535
|
+
en: "## Master Bench",
|
|
536
|
+
ja: "## マスター・ベンチ",
|
|
537
|
+
ko: "## 마스터 벤치",
|
|
538
|
+
});
|
|
539
|
+
const systemBench = `${heading}\n\n${recordedBenchMarker(run)}\n\n${renderBenchSummary(run)}\n\n${renderMasterStatements(run)}`;
|
|
447
540
|
return `${cleaned ? `${cleaned}\n\n` : ""}${systemBench}\n`;
|
|
448
541
|
}
|
|
449
542
|
|
|
543
|
+
function withRecordedPriceSnapshot(run, markdown) {
|
|
544
|
+
const markerStart = "<!-- alphacouncil:recorded-price-snapshot:v1:begin -->";
|
|
545
|
+
const markerEnd = "<!-- alphacouncil:recorded-price-snapshot:v1:end -->";
|
|
546
|
+
let cleaned = String(markdown || "");
|
|
547
|
+
let start = cleaned.indexOf(markerStart);
|
|
548
|
+
while (start !== -1) {
|
|
549
|
+
const end = cleaned.indexOf(markerEnd, start + markerStart.length);
|
|
550
|
+
cleaned = end === -1
|
|
551
|
+
? cleaned.slice(0, start)
|
|
552
|
+
: `${cleaned.slice(0, start)}${cleaned.slice(end + markerEnd.length)}`;
|
|
553
|
+
start = cleaned.indexOf(markerStart);
|
|
554
|
+
}
|
|
555
|
+
const quote = run?.grounding?.quote;
|
|
556
|
+
const key = languageKey(run?.language);
|
|
557
|
+
const heading = { zh: "## 系统记录价格快照", en: "## System-Recorded Price Snapshot", ja: "## システム記録価格スナップショット", ko: "## 시스템 기록 가격 스냅샷" }[key];
|
|
558
|
+
const unavailable = { zh: "本轮未取得可验证报价;不得补造价格。", en: "No verifiable quote was retrieved; no price was invented.", ja: "検証可能な価格を取得できなかったため、価格は補完していません。", ko: "검증 가능한 시세를 가져오지 못했으며 가격을 임의로 만들지 않았습니다." }[key];
|
|
559
|
+
const label = {
|
|
560
|
+
zh: { symbol: "代码", price: "价格", change: "涨跌", time: "报价时间", exchange: "交易所", feed: "数据源", source: "原始链接", unknown: "未知", unavailable: "不可用", delayed: "延迟数据" },
|
|
561
|
+
en: { symbol: "Symbol", price: "Price", change: "Change", time: "Quote time", exchange: "Exchange", feed: "Feed", source: "Source", unknown: "unknown", unavailable: "unavailable", delayed: "delayed data" },
|
|
562
|
+
ja: { symbol: "銘柄コード", price: "価格", change: "騰落", time: "価格時刻", exchange: "取引所", feed: "データ源", source: "原典リンク", unknown: "不明", unavailable: "取得不可", delayed: "遅延データ" },
|
|
563
|
+
ko: { symbol: "종목 코드", price: "가격", change: "등락", time: "시세 시각", exchange: "거래소", feed: "데이터 소스", source: "원문 링크", unknown: "알 수 없음", unavailable: "확인 불가", delayed: "지연 데이터" },
|
|
564
|
+
}[key];
|
|
565
|
+
const body = quote && Number.isFinite(Number(quote.price))
|
|
566
|
+
? [
|
|
567
|
+
`- ${label.symbol}: ${quote.symbol || run.symbol}`,
|
|
568
|
+
`- ${label.price}: ${quote.price} ${quote.currency || ""}`.trimEnd(),
|
|
569
|
+
`- ${label.change}: ${quote.change ?? label.unavailable} (${quote.change_pct ?? label.unavailable}%)`,
|
|
570
|
+
`- ${label.time}: ${quote.quote_time || label.unknown}`,
|
|
571
|
+
`- ${label.exchange}: ${quote.exchange || label.unknown}`,
|
|
572
|
+
`- ${label.feed}: ${quote.source || label.unknown}; ${quote.note || label.delayed}`,
|
|
573
|
+
`- ${label.source}: ${quote.source_url || label.unavailable}`,
|
|
574
|
+
].join("\n")
|
|
575
|
+
: `- ${unavailable}`;
|
|
576
|
+
return `${cleaned.trimEnd()}\n\n${markerStart}\n${heading}\n\n${body}\n${markerEnd}\n`;
|
|
577
|
+
}
|
|
578
|
+
|
|
450
579
|
function withDegradedLedger(run, markdown, completeness) {
|
|
451
580
|
const markerStart = "<!-- alphacouncil:degraded-ledger:v1:begin -->";
|
|
452
581
|
const markerEnd = "<!-- alphacouncil:degraded-ledger:v1:end -->";
|
|
@@ -462,20 +591,27 @@ function withDegradedLedger(run, markdown, completeness) {
|
|
|
462
591
|
const tasks = completeness.degraded_evidence || [];
|
|
463
592
|
const roles = completeness.degraded_debate || [];
|
|
464
593
|
if (!tasks.length && !roles.length) return cleaned;
|
|
465
|
-
const
|
|
594
|
+
const key = languageKey(run.language);
|
|
595
|
+
const unavailable = { zh: "证据不可用", en: "evidence unavailable", ja: "証拠を取得できません", ko: "증거를 확보하지 못함" }[key];
|
|
596
|
+
const debateUnavailable = { zh: "辩论席不可用", en: "debate seat unavailable", ja: "討論席を利用できません", ko: "토론 좌석을 사용할 수 없음" }[key];
|
|
597
|
+
const deadlineText = { zh: "全局时限耗尽", en: "global deadline exhausted", ja: "全体期限を超過", ko: "전체 기한 소진" }[key];
|
|
466
598
|
const rows = [
|
|
467
599
|
...tasks.map((task) => {
|
|
468
600
|
const state = taskState(run, task);
|
|
469
|
-
return `- ${task}: ${state.status}; ${state.error ||
|
|
601
|
+
return `- ${task}: ${state.status}; ${state.error || unavailable}${state.deadline_exhausted ? `; ${deadlineText}` : ""}`;
|
|
470
602
|
}),
|
|
471
603
|
...roles.map((role) => {
|
|
472
604
|
const state = agentState(run, role);
|
|
473
|
-
return `- ${role}: ${state.status}; ${state.error ||
|
|
605
|
+
return `- ${role}: ${state.status}; ${state.error || debateUnavailable}`;
|
|
474
606
|
}),
|
|
475
607
|
].join("\n");
|
|
476
|
-
const
|
|
477
|
-
|
|
478
|
-
:
|
|
608
|
+
const intro = {
|
|
609
|
+
zh: "**QUICK 运行已降级——一个或多个席位失败。** 报告结构可能通过质量检查,但下列席位没有提供可用证据;本轮不代表完整覆盖:",
|
|
610
|
+
en: "**DEGRADED QUICK RUN — one or more seats failed.** The report structure may pass quality checks, but the following seats supplied no usable evidence and this run is not full coverage:",
|
|
611
|
+
ja: "**QUICK 実行は縮退しました——1つ以上の席が失敗しました。** レポート構造が品質検査を通っても、次の席は利用可能な証拠を提供していないため、完全な網羅ではありません:",
|
|
612
|
+
ko: "**QUICK 실행 성능 저하—하나 이상의 좌석이 실패했습니다.** 보고서 구조가 품질 검사를 통과해도 다음 좌석은 사용 가능한 증거를 제공하지 않았으므로 전체 범위를 대표하지 않습니다:",
|
|
613
|
+
}[key];
|
|
614
|
+
const banner = `> [!WARNING]\n> ${intro}\n>\n${rows.split("\n").map((line) => `> ${line}`).join("\n")}`;
|
|
479
615
|
return `${markerStart}\n${banner}\n${markerEnd}\n\n${cleaned.trimStart()}`;
|
|
480
616
|
}
|
|
481
617
|
|
|
@@ -492,16 +628,23 @@ function withQuickScope(run, markdown) {
|
|
|
492
628
|
start = cleaned.indexOf(markerStart);
|
|
493
629
|
}
|
|
494
630
|
if (run.council_mode !== "quick") return cleaned;
|
|
495
|
-
const
|
|
496
|
-
|
|
497
|
-
: "
|
|
631
|
+
const copy = {
|
|
632
|
+
zh: "**QUICK_V1 范围。** 本轮不是 full council:固定 4 个核心证据席、1–4 个方法席、一次并行多空陈述和短 PM;没有三轮交叉问答或对抗核验。`full_council_equivalent=false`。",
|
|
633
|
+
en: "**QUICK_V1 SCOPE.** This is not a full council: four fixed core evidence seats, one to four method seats, one parallel bull/bear statement and a short PM; no three-round cross-exam or adversarial verifier. `full_council_equivalent=false`.",
|
|
634
|
+
ja: "**QUICK_V1 の範囲。** full council ではありません。固定4つの中核証拠席、1〜4のメソッド席、強気・弱気の並列1ラウンド、短いPMのみで、3ラウンドの交差質問や対抗検証はありません。`full_council_equivalent=false`。",
|
|
635
|
+
ko: "**QUICK_V1 범위.** full council이 아닙니다. 고정된 4개 핵심 증거 좌석, 1~4개 방법론 좌석, 병렬 강세·약세 1라운드와 짧은 PM만 실행하며 3라운드 교차 질문이나 적대적 검증은 없습니다. `full_council_equivalent=false`.",
|
|
636
|
+
}[languageKey(run.language)];
|
|
637
|
+
const banner = `> [!NOTE]\n> ${copy}`;
|
|
498
638
|
return `${markerStart}\n${banner}\n${markerEnd}\n\n${cleaned.trimStart()}`;
|
|
499
639
|
}
|
|
500
640
|
|
|
501
641
|
export function finalReportMarkdown(run, manager) {
|
|
502
642
|
const gate = verificationStatus(run);
|
|
503
643
|
const completeness = completenessStatus(run);
|
|
504
|
-
const reportBody =
|
|
644
|
+
const reportBody = withRecordedPriceSnapshot(
|
|
645
|
+
run,
|
|
646
|
+
withRecordedMasterBench(run, manager.report_markdown || manager.summary),
|
|
647
|
+
);
|
|
505
648
|
return withDisclaimer(
|
|
506
649
|
withCompletenessBanner(
|
|
507
650
|
withQuickScope(
|
|
@@ -517,33 +660,40 @@ export function finalReportMarkdown(run, manager) {
|
|
|
517
660
|
|
|
518
661
|
export function writeArtifactIndex(run, debate = {}) {
|
|
519
662
|
const artifacts = artifactPaths(run);
|
|
663
|
+
const key = languageKey(run.language);
|
|
664
|
+
const label = {
|
|
665
|
+
zh: { title: "AlphaCouncil 工件索引", run: "运行 ID", status: "状态", quality: "报告质量", main: "主要文件", final: "最终报告", handoff: "聊天交接摘要", trace: "全部代理审计追踪", evidence: "证据 JSON", decision: "决策 JSON", sources: "来源清单", statusFile: "状态", events: "事件", qualityFile: "报告质量", analysts: "分析师 Markdown 文件", masters: "方法席 Markdown 文件" },
|
|
666
|
+
en: { title: "AlphaCouncil Artifact Index", run: "Run ID", status: "Status", quality: "Report quality", main: "Main Files", final: "Final report", handoff: "Chat handoff summary", trace: "Full agent audit trace", evidence: "Evidence JSON", decision: "Decision JSON", sources: "Source manifest", statusFile: "Status", events: "Events", qualityFile: "Report quality", analysts: "Analyst Markdown Files", masters: "Method-Seat Markdown Files" },
|
|
667
|
+
ja: { title: "AlphaCouncil 成果物索引", run: "実行 ID", status: "状態", quality: "レポート品質", main: "主要ファイル", final: "最終レポート", handoff: "チャット引継ぎ要約", trace: "全エージェント監査トレース", evidence: "証拠 JSON", decision: "判断 JSON", sources: "出典一覧", statusFile: "状態", events: "イベント", qualityFile: "レポート品質", analysts: "分析担当 Markdown ファイル", masters: "メソッド席 Markdown ファイル" },
|
|
668
|
+
ko: { title: "AlphaCouncil 산출물 색인", run: "실행 ID", status: "상태", quality: "보고서 품질", main: "주요 파일", final: "최종 보고서", handoff: "채팅 인계 요약", trace: "전체 에이전트 감사 추적", evidence: "증거 JSON", decision: "판단 JSON", sources: "출처 목록", statusFile: "상태", events: "이벤트", qualityFile: "보고서 품질", analysts: "분석가 Markdown 파일", masters: "방법론 좌석 Markdown 파일" },
|
|
669
|
+
}[key];
|
|
520
670
|
const lines = [
|
|
521
|
-
`# ${run.symbol}
|
|
671
|
+
`# ${run.symbol} ${label.title}`,
|
|
522
672
|
"",
|
|
523
|
-
`-
|
|
524
|
-
`-
|
|
525
|
-
`-
|
|
673
|
+
`- ${label.run}: ${run.run_id}`,
|
|
674
|
+
`- ${label.status}: ${run.status}`,
|
|
675
|
+
`- ${label.quality}: ${run.report_quality?.status || "not_checked"}`,
|
|
526
676
|
"",
|
|
527
|
-
|
|
677
|
+
`## ${label.main}`,
|
|
528
678
|
"",
|
|
529
|
-
`-
|
|
530
|
-
`-
|
|
531
|
-
`-
|
|
532
|
-
`-
|
|
533
|
-
`-
|
|
534
|
-
`-
|
|
535
|
-
`-
|
|
536
|
-
`-
|
|
537
|
-
`-
|
|
679
|
+
`- ${label.final}: ${artifacts.final_report_md}`,
|
|
680
|
+
`- ${label.handoff}: ${artifacts.user_response_md}`,
|
|
681
|
+
`- ${label.trace}: ${artifacts.all_agents_md}`,
|
|
682
|
+
`- ${label.evidence}: ${artifacts.evidence_json}`,
|
|
683
|
+
`- ${label.decision}: ${artifacts.decision_json}`,
|
|
684
|
+
`- ${label.sources}: ${artifacts.source_manifest_json}`,
|
|
685
|
+
`- ${label.statusFile}: ${artifacts.status_json}`,
|
|
686
|
+
`- ${label.events}: ${artifacts.events_jsonl}`,
|
|
687
|
+
`- ${label.qualityFile}: ${artifacts.report_quality_json}`,
|
|
538
688
|
"",
|
|
539
|
-
|
|
689
|
+
`## ${label.analysts}`,
|
|
540
690
|
"",
|
|
541
691
|
...(run.tasks || []).map((task) => `- ${task}: ${artifacts.analyst_markdown[task]}`),
|
|
542
692
|
debate.bull ? `- bull_researcher: ${artifacts.analyst_markdown.bull_researcher}` : "",
|
|
543
693
|
debate.bear ? `- bear_researcher: ${artifacts.analyst_markdown.bear_researcher}` : "",
|
|
544
694
|
debate.manager ? `- portfolio_manager: ${artifacts.analyst_markdown.portfolio_manager}` : "",
|
|
545
695
|
...((run.master_opinions || []).length
|
|
546
|
-
? ["",
|
|
696
|
+
? ["", `## ${label.masters}`, "",
|
|
547
697
|
...(run.master_opinions || []).map((o) => `- ${o.master} (${o.stance || "unknown"}): ${join(runPath(run.run_id), `${o.master}.md`)}`)]
|
|
548
698
|
: []),
|
|
549
699
|
].filter(Boolean);
|
|
@@ -559,7 +709,7 @@ function quickUserResponse(run, manager, artifacts, chinese) {
|
|
|
559
709
|
const managerCompleted = agentState(run, "portfolio_manager").status === "completed"
|
|
560
710
|
&& manager?.decision_available !== false;
|
|
561
711
|
const masters = (run.master_opinions || []).map((opinion) =>
|
|
562
|
-
`- ${masterTitle(opinion.master, run.language)} (\`${opinion.master}\`) — ${opinion.stance || "unknown"}, ${opinion.confidence || "low"}: ${clipAtBoundary(opinion.
|
|
712
|
+
`- ${masterTitle(opinion.master, run.language)} (\`${opinion.master}\`) — ${opinion.stance || "unknown"}, ${opinion.confidence || "low"}: ${clipAtBoundary(opinion.voice_statement || opinion.summary || opinion.verdict, 360)}`,
|
|
563
713
|
).join("\n") || (chinese ? "- 没有已记录的方法席结论。" : "- No recorded method-seat conclusion.");
|
|
564
714
|
const analysts = (run.tasks || []).map((task) => {
|
|
565
715
|
const packet = (run.packets || []).find((item) => item.task === task);
|
|
@@ -670,7 +820,7 @@ function quickUserResponse(run, manager, artifacts, chinese) {
|
|
|
670
820
|
].join("\n");
|
|
671
821
|
}
|
|
672
822
|
|
|
673
|
-
|
|
823
|
+
function legacyUserResponseMarkdown(run, manager) {
|
|
674
824
|
const chinese = isChineseLanguage(run.language);
|
|
675
825
|
const artifacts = artifactPaths(run);
|
|
676
826
|
const decisionAvailable = manager?.decision_available !== false;
|
|
@@ -732,6 +882,165 @@ export function userResponseMarkdown(run, manager) {
|
|
|
732
882
|
].join("\n");
|
|
733
883
|
}
|
|
734
884
|
|
|
885
|
+
function handoffCopy(language) {
|
|
886
|
+
return {
|
|
887
|
+
zh: {
|
|
888
|
+
title: "AlphaCouncil 运行摘要", status: "运行状态与时限", statusLabel: "状态", contract: "报告契约", scope: "执行范围", elapsed: "耗时", deadline: "硬截止时间", deadlineMet: "是否在截止前落盘",
|
|
889
|
+
fullScope: "full_v2:8 个证据席、每个已选方法席的专属 worker、三轮多空交叉问答和 PM;插件托管运行硬上限 30 分钟。", quickScope: "quick_v1:4 个证据席、1–4 个方法席、单轮多空和短 PM;不等同 full council。",
|
|
890
|
+
price: "系统记录价格", noPrice: "未取得可验证报价;没有补造价格。", delayed: "延迟行情", conclusion: "结论", rating: "评级", winner: "多空胜负", confidence: "置信度", judgment: "判断", noDecision: "NEEDS_MANAGER_REVIEW;工具或 PM 失败不能转换成投资评级。",
|
|
891
|
+
masters: "逐席大神方法输出(不是本人引语)", analysts: "分析师逐席内容", worker: "专属代理", key: "关键内容", earnings: "最新财报", forward: "前瞻门槛", news: "新闻/行业信号", recentNews: "近期公司与行业新闻", newsSummary: "新闻席摘要", noDatedNews: "本轮没有取得 as_of 之前 120 天内且带日期的新闻来源。", newsExcluded: "新闻时间门禁排除", valuation: "估值/价位", position: "仓位", gaps: "数据缺口与失败席", invalidation: "失效条件", files: "文件位置", report: "完整报告", index: "代理工件索引", trace: "全部代理追踪", quality: "报告质量检查", missing: "未覆盖。", noGaps: "未记录额外缺口。", noInvalidation: "没有正式失效条件。",
|
|
892
|
+
},
|
|
893
|
+
en: {
|
|
894
|
+
title: "AlphaCouncil Run Summary", status: "Run Status and Deadline", statusLabel: "Status", contract: "Report contract", scope: "Execution scope", elapsed: "Elapsed", deadline: "Hard deadline", deadlineMet: "Persisted before deadline",
|
|
895
|
+
fullScope: "full_v2: eight evidence seats, one dedicated worker per selected method, three-round cross-examination and PM; plugin-managed runs have a hard thirty-minute ceiling.", quickScope: "quick_v1: four evidence seats, one to four method seats, one debate round and short PM; not equivalent to full council.",
|
|
896
|
+
price: "System-Recorded Price", noPrice: "No verifiable quote was retrieved; no price was invented.", delayed: "delayed quote", conclusion: "Conclusion", rating: "Rating", winner: "Debate winner", confidence: "Confidence", judgment: "Judgment", noDecision: "NEEDS_MANAGER_REVIEW; a tool or PM failure cannot be converted into an investment rating.",
|
|
897
|
+
masters: "Recorded Method-Seat Statements (not quotes from the named people)", analysts: "Analyst Views by Seat", worker: "dedicated worker", key: "Key Content", earnings: "Latest earnings", forward: "Forward thresholds", news: "News / industry signal", recentNews: "Recent Company and Industry News", newsSummary: "News-seat summary", noDatedNews: "No dated news source inside the 120 days through as_of was retrieved.", newsExcluded: "Recent-news gate excluded", valuation: "Valuation / price range", position: "Position", gaps: "Data Gaps and Failed Seats", invalidation: "Invalidation", files: "File Locations", report: "Full report", index: "Agent artifact index", trace: "Full agent trace", quality: "Report quality check", missing: "Not covered.", noGaps: "No additional gap was recorded.", noInvalidation: "No formal invalidation conditions are available.",
|
|
898
|
+
},
|
|
899
|
+
ja: {
|
|
900
|
+
title: "AlphaCouncil 実行サマリー", status: "実行状況と期限", statusLabel: "状態", contract: "レポート契約", scope: "実行範囲", elapsed: "所要時間", deadline: "ハード期限", deadlineMet: "期限内に保存", fullScope: "full_v2:8つの証拠席、選択した各メソッド専用ワーカー、3ラウンドの多空質疑、PM。プラグイン管理実行は30分で必ず終端状態になります。", quickScope: "quick_v1:4つの証拠席、1–4のメソッド席、1ラウンドの多空議論、短いPM。full council相当ではありません。", price: "システム記録価格", noPrice: "検証可能な価格を取得できず、価格は補完していません。", delayed: "遅延価格", conclusion: "結論", rating: "評価", winner: "勝者", confidence: "信頼度", judgment: "判断", noDecision: "NEEDS_MANAGER_REVIEW。ツールまたはPMの失敗を投資評価に変換できません。", masters: "メソッド席ごとの記録(本人の発言・引用ではありません)", analysts: "分析担当ごとの内容", worker: "専用ワーカー", key: "主要内容", earnings: "直近決算", forward: "先行条件", news: "ニュース・業界シグナル", recentNews: "直近の企業・業界ニュース", newsSummary: "ニュース席の要約", noDatedNews: "as_of までの120日間にある日付付きニュース出典を取得できませんでした。", newsExcluded: "ニュース時刻ゲートで除外", valuation: "評価レンジ・価格条件", position: "ポジション", gaps: "データ欠落と失敗した席", invalidation: "無効化条件", files: "ファイル", report: "完全レポート", index: "代理成果物一覧", trace: "全代理トレース", quality: "レポート品質検査", missing: "未取得。", noGaps: "追加の欠落は記録されていません。", noInvalidation: "正式な無効化条件はありません。",
|
|
901
|
+
},
|
|
902
|
+
ko: {
|
|
903
|
+
title: "AlphaCouncil 실행 요약", status: "실행 상태 및 기한", statusLabel: "상태", contract: "보고서 계약", scope: "실행 범위", elapsed: "소요 시간", deadline: "하드 기한", deadlineMet: "기한 내 저장", fullScope: "full_v2: 8개 증거 좌석, 선택된 각 방법론 전용 워커, 3라운드 롱/숏 질의응답, PM. 플러그인 관리 실행은 30분 안에 반드시 종단 상태가 됩니다.", quickScope: "quick_v1: 4개 증거 좌석, 1–4개 방법론 좌석, 단일 롱/숏 라운드, 짧은 PM. full council과 동등하지 않습니다.", price: "시스템 기록 가격", noPrice: "검증 가능한 시세를 가져오지 못했으며 가격을 임의로 만들지 않았습니다.", delayed: "지연 시세", conclusion: "결론", rating: "등급", winner: "토론 승자", confidence: "신뢰도", judgment: "판단", noDecision: "NEEDS_MANAGER_REVIEW. 도구 또는 PM 실패를 투자 등급으로 바꿀 수 없습니다.", masters: "방법론 좌석별 기록(본인의 실제 발언이나 인용이 아님)", analysts: "분석가 좌석별 내용", worker: "전용 워커", key: "핵심 내용", earnings: "최근 실적", forward: "선행 조건", news: "뉴스·산업 신호", recentNews: "최근 기업 및 산업 뉴스", newsSummary: "뉴스 좌석 요약", noDatedNews: "as_of까지 120일 이내의 날짜가 있는 뉴스 출처를 확보하지 못했습니다.", newsExcluded: "뉴스 시간 게이트에서 제외", valuation: "가치평가 범위·가격 조건", position: "포지션", gaps: "데이터 공백 및 실패 좌석", invalidation: "무효화 조건", files: "파일 위치", report: "전체 보고서", index: "에이전트 산출물 색인", trace: "전체 에이전트 추적", quality: "보고서 품질 검사", missing: "미확보.", noGaps: "추가 공백이 기록되지 않았습니다.", noInvalidation: "공식 무효화 조건이 없습니다.",
|
|
904
|
+
},
|
|
905
|
+
}[languageKey(language)];
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
function localizedFailure(error, language) {
|
|
909
|
+
const value = String(error || "");
|
|
910
|
+
const key = languageKey(language);
|
|
911
|
+
const labels = {
|
|
912
|
+
zh: { parse_failed: "返回格式无法修复", timeout: "超时", timed_out: "超时", global_deadline: "全局时限耗尽", qna_incomplete: "问答不完整", unexpected_error: "意外工具错误", failed: "子代理未成功返回", skipped: "因上游门禁未运行", degraded: "降级", pending: "尚未运行", missing: "缺失" },
|
|
913
|
+
en: { parse_failed: "response format could not be repaired", timeout: "timed out", timed_out: "timed out", global_deadline: "global deadline exhausted", qna_incomplete: "Q&A was incomplete", unexpected_error: "unexpected tool error", failed: "subagent did not return successfully", skipped: "not run because an upstream gate failed", degraded: "degraded", pending: "not started", missing: "missing" },
|
|
914
|
+
ja: { parse_failed: "応答形式を修復できませんでした", timeout: "タイムアウト", timed_out: "タイムアウト", global_deadline: "全体期限を超過", qna_incomplete: "質疑応答が不完全", unexpected_error: "予期しないツールエラー", failed: "サブエージェントが正常に応答しませんでした", skipped: "上流ゲートの失敗により未実行", degraded: "縮退", pending: "未開始", missing: "欠落" },
|
|
915
|
+
ko: { parse_failed: "응답 형식을 복구하지 못함", timeout: "시간 초과", timed_out: "시간 초과", global_deadline: "전체 기한 소진", qna_incomplete: "질의응답 불완전", unexpected_error: "예기치 않은 도구 오류", failed: "하위 에이전트가 정상 응답하지 못함", skipped: "상위 게이트 실패로 실행하지 않음", degraded: "성능 저하", pending: "시작 전", missing: "누락" },
|
|
916
|
+
}[key];
|
|
917
|
+
if (value.startsWith("exit code")) return labels.failed;
|
|
918
|
+
if (value.includes("gate_failed") || value.startsWith("global_deadline_before")) return labels.skipped;
|
|
919
|
+
return labels[value] || labels.missing;
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
function recentNewsHandoff(run, copy) {
|
|
923
|
+
const packet = (run.packets || []).find((item) => item.task === "news_industry_management");
|
|
924
|
+
const asOfTime = Date.parse(`${String(run.as_of || "").slice(0, 10)}T23:59:59.999Z`);
|
|
925
|
+
const cutoff = Number.isFinite(asOfTime) ? asOfTime - (120 * 24 * 60 * 60 * 1000) : -Infinity;
|
|
926
|
+
const excluded = { undated: 0, future: 0, stale: 0 };
|
|
927
|
+
const eligible = (packet?.sources || []).filter((source) => {
|
|
928
|
+
const published = Date.parse(source?.published_at || "");
|
|
929
|
+
if (!Number.isFinite(published)) { excluded.undated += 1; return false; }
|
|
930
|
+
if (Number.isFinite(asOfTime) && published > asOfTime) { excluded.future += 1; return false; }
|
|
931
|
+
if (Number.isFinite(asOfTime) && published < cutoff) { excluded.stale += 1; return false; }
|
|
932
|
+
return true;
|
|
933
|
+
}).sort((a, b) => String(b.published_at || "").localeCompare(String(a.published_at || ""))).slice(0, 6);
|
|
934
|
+
const sources = eligible.length
|
|
935
|
+
? eligible.map((source) => `- ${source.published_at} — ${clipAtBoundary(source.title || "", 240)}${source.url ? ` — ${source.url}` : ""}`).join("\n")
|
|
936
|
+
: `- ${copy.noDatedNews}`;
|
|
937
|
+
const totalExcluded = excluded.undated + excluded.future + excluded.stale;
|
|
938
|
+
const exclusion = totalExcluded ? {
|
|
939
|
+
zh: `- ${copy.newsExcluded} ${totalExcluded} 个来源:无日期 ${excluded.undated}、晚于 as_of ${excluded.future}、早于 120 天窗口 ${excluded.stale}。`,
|
|
940
|
+
en: `- ${copy.newsExcluded} ${totalExcluded}: undated=${excluded.undated}, after_as_of=${excluded.future}, older_than_120d=${excluded.stale}.`,
|
|
941
|
+
ja: `- ${copy.newsExcluded}:合計 ${totalExcluded} 件(日付なし ${excluded.undated}、as_of 後 ${excluded.future}、120日超 ${excluded.stale})。`,
|
|
942
|
+
ko: `- ${copy.newsExcluded}: 총 ${totalExcluded}개(날짜 없음 ${excluded.undated}, as_of 이후 ${excluded.future}, 120일 초과 ${excluded.stale}).`,
|
|
943
|
+
}[languageKey(run.language)] : "";
|
|
944
|
+
return { packet, sources, exclusion };
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
function localizedDisplayValue(value, language) {
|
|
948
|
+
const key = languageKey(language);
|
|
949
|
+
const token = value === true ? "true" : value === false ? "false" : String(value || "unknown");
|
|
950
|
+
const maps = {
|
|
951
|
+
zh: { unknown: "未知", unavailable: "不可用", true: "是", false: "否", pending: "待运行", running: "运行中", waiting: "等待中", completed: "已完成", complete: "完整", failed: "失败", degraded: "降级", incomplete: "不完整", skipped: "未运行", declined: "不适用", constructive: "建设性", cautious: "谨慎", opposed: "反对", out_of_scope: "证据范围外", high: "高", medium: "中", low: "低", Buy: "买入", Overweight: "增持", Hold: "持有", Underweight: "减持", Sell: "卖出", bull: "多方", bear: "空方", balanced: "平衡" },
|
|
952
|
+
en: { unknown: "unknown", unavailable: "unavailable", true: "yes", false: "no" },
|
|
953
|
+
ja: { unknown: "不明", unavailable: "取得不可", true: "はい", false: "いいえ", pending: "待機中", running: "実行中", waiting: "待機中", completed: "完了", complete: "完了", failed: "失敗", degraded: "縮退", incomplete: "不完全", skipped: "未実行", declined: "適用外", constructive: "前向き", cautious: "慎重", opposed: "反対", out_of_scope: "証拠範囲外", high: "高", medium: "中", low: "低", Buy: "買い", Overweight: "オーバーウェイト", Hold: "中立", Underweight: "アンダーウェイト", Sell: "売り", bull: "強気", bear: "弱気", balanced: "均衡" },
|
|
954
|
+
ko: { unknown: "알 수 없음", unavailable: "확인 불가", true: "예", false: "아니요", pending: "대기 중", running: "실행 중", waiting: "대기 중", completed: "완료", complete: "완료", failed: "실패", degraded: "성능 저하", incomplete: "불완전", skipped: "미실행", declined: "적용 범위 밖", constructive: "긍정적", cautious: "신중", opposed: "반대", out_of_scope: "증거 범위 밖", high: "높음", medium: "중간", low: "낮음", Buy: "매수", Overweight: "비중 확대", Hold: "보유", Underweight: "비중 축소", Sell: "매도", bull: "강세", bear: "약세", balanced: "균형" },
|
|
955
|
+
};
|
|
956
|
+
return maps[key]?.[token] || token;
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
export function userResponseMarkdown(run, manager) {
|
|
960
|
+
const artifacts = artifactPaths(run);
|
|
961
|
+
const copy = handoffCopy(run.language);
|
|
962
|
+
const news = recentNewsHandoff(run, copy);
|
|
963
|
+
const decisionAvailable = manager?.decision_available !== false;
|
|
964
|
+
const quote = run?.grounding?.quote;
|
|
965
|
+
const priceLine = quote && Number.isFinite(Number(quote.price))
|
|
966
|
+
? `${quote.price} ${quote.currency || ""}; ${quote.quote_time || localizedDisplayValue("unknown", run.language)}; ${quote.exchange || localizedDisplayValue("unknown", run.language)}; ${copy.delayed}; ${quote.source_url || localizedDisplayValue("unavailable", run.language)}`
|
|
967
|
+
: copy.noPrice;
|
|
968
|
+
const masterLines = (run.masters || []).map((id) => {
|
|
969
|
+
const opinion = (run.master_opinions || []).find((item) => item.master === id);
|
|
970
|
+
const state = run.master_status?.[id] || { status: "pending" };
|
|
971
|
+
if (!opinion) return `- ${masterTitle(id, run.language)} (\`${id}\`) [${localizedDisplayValue(state.status, run.language)}]: ${localizedFailure(state.error, run.language) || copy.missing}`;
|
|
972
|
+
return `- ${masterTitle(id, run.language)} (\`${id}\`) [${localizedDisplayValue(opinion.stance, run.language)}/${localizedDisplayValue(opinion.confidence || "low", run.language)}; ${copy.worker}=${localizedDisplayValue(opinion.dedicated_worker?.status || opinion.voice_status || "unknown", run.language)}]: ${clipAtBoundary(opinion.voice_statement || opinion.summary || opinion.verdict, 520)}`;
|
|
973
|
+
}).join("\n") || `- ${copy.missing}`;
|
|
974
|
+
const analystLines = (run.tasks || []).map((task) => {
|
|
975
|
+
const packet = (run.packets || []).find((item) => item.task === task);
|
|
976
|
+
const state = taskState(run, task);
|
|
977
|
+
return `- \`${task}\` [${localizedDisplayValue(state.status, run.language)}/${localizedDisplayValue(packet?.confidence || "low", run.language)}]: ${clipAtBoundary(packet?.summary || "", 520) || localizedFailure(state.error, run.language) || copy.missing}`;
|
|
978
|
+
}).join("\n") || `- ${copy.missing}`;
|
|
979
|
+
const gaps = [...new Set([
|
|
980
|
+
...(run.tasks || []).filter((task) => taskState(run, task).status !== "completed")
|
|
981
|
+
.map((task) => `${task}: ${localizedFailure(taskState(run, task).error || taskState(run, task).status, run.language)}`),
|
|
982
|
+
...(run.masters || []).filter((id) => !(run.master_opinions || []).some((opinion) => opinion.master === id))
|
|
983
|
+
.map((id) => `${id}: ${localizedFailure(run.master_status?.[id]?.error || run.master_status?.[id]?.status, run.language)}`),
|
|
984
|
+
...(run.packets || []).flatMap((packet) => packet.open_questions || []),
|
|
985
|
+
])].slice(0, 12).map((item) => `- ${clipAtBoundary(item, 360)}`).join("\n") || `- ${copy.noGaps}`;
|
|
986
|
+
const invalidation = decisionAvailable
|
|
987
|
+
? (manager.invalidation || []).slice(0, 3).map((item) => `- ${clipAtBoundary(item, 260)}`).join("\n") || `- ${copy.noInvalidation}`
|
|
988
|
+
: `- ${copy.noInvalidation}`;
|
|
989
|
+
const elapsed = run.started_at
|
|
990
|
+
? Math.max(0, Date.parse(run.completed_at || new Date().toISOString()) - Date.parse(run.started_at))
|
|
991
|
+
: localizedDisplayValue("unknown", run.language);
|
|
992
|
+
return [
|
|
993
|
+
`# ${run.symbol} ${copy.title}`,
|
|
994
|
+
"",
|
|
995
|
+
`## ${copy.status}`,
|
|
996
|
+
`- ${copy.statusLabel}: ${localizedDisplayValue(run.status, run.language)}`,
|
|
997
|
+
`- ${copy.contract}: ${run.council_mode === "quick" ? "quick_v1" : "full_v2"}`,
|
|
998
|
+
`- ${copy.scope}: ${run.council_mode === "quick" ? copy.quickScope : copy.fullScope}`,
|
|
999
|
+
`- ${copy.elapsed}: ${elapsed} ms`,
|
|
1000
|
+
`- ${copy.deadline}: ${run.deadline_at || localizedDisplayValue("unknown", run.language)}`,
|
|
1001
|
+
`- ${copy.deadlineMet}: ${localizedDisplayValue(run.deadline_at && run.completed_at ? Date.parse(run.completed_at) <= Date.parse(run.deadline_at) : "unknown", run.language)}`,
|
|
1002
|
+
"",
|
|
1003
|
+
`## ${copy.price}`,
|
|
1004
|
+
`- ${priceLine}`,
|
|
1005
|
+
"",
|
|
1006
|
+
`## ${copy.conclusion}`,
|
|
1007
|
+
`- ${copy.rating}: ${localizedDisplayValue(decisionAvailable ? manager.rating : "unavailable", run.language)}`,
|
|
1008
|
+
`- ${copy.winner}: ${localizedDisplayValue(decisionAvailable ? (manager.winner || "unknown") : "unavailable", run.language)}`,
|
|
1009
|
+
`- ${copy.confidence}: ${localizedDisplayValue(decisionAvailable ? (manager.confidence || "low") : "unavailable", run.language)}`,
|
|
1010
|
+
`- ${copy.judgment}: ${decisionAvailable ? clipAtBoundary(manager.verdict || manager.summary, 720) : copy.noDecision}`,
|
|
1011
|
+
"",
|
|
1012
|
+
`## ${copy.masters}`,
|
|
1013
|
+
masterLines,
|
|
1014
|
+
"",
|
|
1015
|
+
`## ${copy.analysts}`,
|
|
1016
|
+
analystLines,
|
|
1017
|
+
"",
|
|
1018
|
+
`## ${copy.recentNews}`,
|
|
1019
|
+
`- ${copy.newsSummary}: ${clipAtBoundary(news.packet?.summary || "", 620) || copy.missing}`,
|
|
1020
|
+
news.sources,
|
|
1021
|
+
news.exclusion,
|
|
1022
|
+
"",
|
|
1023
|
+
`## ${copy.key}`,
|
|
1024
|
+
`- ${copy.earnings}: ${clipAtBoundary(packetSummary(run, "earnings_deep_dive"), 520) || copy.missing}`,
|
|
1025
|
+
`- ${copy.forward}: ${clipAtBoundary(packetSummary(run, "forward_expectations"), 520) || copy.missing}`,
|
|
1026
|
+
`- ${copy.news}: ${clipAtBoundary(packetSummary(run, "news_industry_management"), 620) || copy.missing}`,
|
|
1027
|
+
`- ${copy.valuation}: ${decisionAvailable ? (clipAtBoundary(manager.valuation_range, 620) || copy.missing) : localizedDisplayValue("unavailable", run.language)}`,
|
|
1028
|
+
`- ${copy.position}: ${decisionAvailable ? (clipAtBoundary(manager.position, 520) || copy.missing) : localizedDisplayValue("unavailable", run.language)}`,
|
|
1029
|
+
"",
|
|
1030
|
+
`## ${copy.gaps}`,
|
|
1031
|
+
gaps,
|
|
1032
|
+
"",
|
|
1033
|
+
`## ${copy.invalidation}`,
|
|
1034
|
+
invalidation,
|
|
1035
|
+
"",
|
|
1036
|
+
`## ${copy.files}`,
|
|
1037
|
+
`- ${copy.report}: ${artifacts.final_report_md}`,
|
|
1038
|
+
`- ${copy.index}: ${artifacts.artifact_index_md}`,
|
|
1039
|
+
`- ${copy.trace}: ${artifacts.all_agents_md}`,
|
|
1040
|
+
`- ${copy.quality}: ${artifacts.report_quality_json}`,
|
|
1041
|
+
].join("\n");
|
|
1042
|
+
}
|
|
1043
|
+
|
|
735
1044
|
export function writeUserResponse(run, manager) {
|
|
736
1045
|
const markdown = userResponseMarkdown(run, manager);
|
|
737
1046
|
writeFileSync(artifactPaths(run).user_response_md, `${markdown}\n`);
|