offerguard-mcp-server 0.4.0 → 0.4.2

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.
@@ -1,210 +0,0 @@
1
- import test from "node:test";
2
- import assert from "node:assert/strict";
3
- import { auditOfferGuardInput as audit, matchRiskTerms, calculateOfferScore, checkInternshipCompliance } from "../src/offerguard-tools.js";
4
- import { PROFILE, GOOD_JD, CASES } from "./fixtures.js";
5
-
6
- for (const phrase of ["不收押金", "不收取任何押金", "无需缴纳保证金", "不安排无薪试岗", "没有培训贷", "不会要求缴纳押金"]) {
7
- test(`negative risk: ${phrase}`, () => {
8
- const r = audit({ structured_input: { jd_text: `${GOOD_JD};${phrase}。`, student_profile: PROFILE } });
9
- assert.equal(r.compliance_red_lines.length, 0);
10
- assert.notEqual(r.route, "reject");
11
- });
12
- }
13
- test("student boundaries do not contaminate job facts", () => {
14
- const r = audit({ raw_input: `JD:${GOOD_JD}\n学生:${PROFILE} 不接受押金、培训贷、无薪试岗。` });
15
- assert.equal(r.route, "advance"); assert.equal(r.compliance_red_lines.length, 0);
16
- assert.equal(r.facts.unpaid_trial.status, "unknown");
17
- });
18
- test("missing benefits are not granted", () => {
19
- const r = audit({ structured_input: { jd_text: "北京后端开发实习生,参与真实项目开发,180元/天。10:00-18:00。无导师。不签实习协议。无转正标准。提供项目证明。", student_profile: PROFILE } });
20
- for (const key of ["mentor", "agreement", "conversion"]) assert.equal(r.facts[key].status, "negated");
21
- assert.notEqual(r.route, "advance");
22
- assert.equal(r.dimensions.find(d => d.key === "compliance_safety").score, 20);
23
- });
24
- test("unknown and questions stay unknown", () => {
25
- for (const phrase of ["导师未知", "未说明导师", "是否有导师?", "导师待确认", "可能有导师", "不确定有导师"]) {
26
- const r = audit({ structured_input: { jd_text: `后端实习生。${phrase}`, student_profile: PROFILE } });
27
- assert.equal(r.facts.mentor.status, "unknown", phrase);
28
- }
29
- });
30
- test("unattributed text is not silently asserted as employer facts", () => {
31
- const r = audit({ raw_input: "我不接受押金,希望有导师。公司可能有岗位。" });
32
- assert.equal(r.compliance_red_lines.length, 0); assert.equal(r.overall_score, null);
33
- });
34
- test("missing input has no invented dimensions or total", () => {
35
- const r = audit(CASES.missing);
36
- assert.equal(r.route, "followup"); assert.equal(r.overall_score, null);
37
- assert.equal(r.dimensions.find(d => d.key === "role_match").score, null);
38
- assert.equal(r.dimensions.find(d => d.key === "growth_value").score, null);
39
- assert.ok(r.required_followup_questions.length <= 3);
40
- });
41
- test("independent profiles change role match", () => {
42
- const a = audit(CASES.advance);
43
- const b = audit({ structured_input: { jd_text: GOOD_JD, student_profile: "新闻学大三,做过公众号运营;薪资底线120元/天;每周可到岗4天。" } });
44
- assert.ok(a.dimensions[0].score > b.dimensions[0].score);
45
- assert.ok(a.overall_score > b.overall_score);
46
- assert.notEqual(b.route, "advance");
47
- });
48
- test("clear red lines take precedence over missing information", () => {
49
- const r = audit(CASES.reject);
50
- assert.equal(r.route, "reject"); assert.equal(r.risk_level, "critical");
51
- assert.equal(r.overall_score, null); assert.equal(r.compliance_red_lines.length, 2);
52
- });
53
- test("full case can advance without demo override", () => {
54
- const r = audit(CASES.advance);
55
- assert.equal(r.route, "advance"); assert.ok(r.overall_score >= 75);
56
- assert.ok(r.dimensions.every(d => d.evidence.length));
57
- });
58
- test("demo label cannot force recommendation", () => {
59
- const r = audit({ raw_input: "演示案例1 推荐推进\nJD:先交押金800元。\n学生:大三学生。" });
60
- assert.equal(r.route, "reject");
61
- });
62
- test("HR reply fills unknown salary and updates decision", () => {
63
- const first = audit({ structured_input: { jd_text: GOOD_JD.replace("薪资180元/天,每月15日发放。", ""), student_profile: PROFILE } });
64
- assert.equal(first.overall_score, null);
65
- const next = audit({ raw_input: "HR回复:薪资180元/天,每月15日发放。", previous_state: first.next_state });
66
- assert.equal(next.route, "advance"); assert.ok(next.overall_score !== null);
67
- assert.ok(next.changes.some(c => c.key === "salary" && c.before === "unknown" && c.after === "affirmed"));
68
- assert.ok(next.report_text.includes("本轮更新"));
69
- });
70
- test("contradictory HR salary is preserved and blocks total", () => {
71
- const first = audit(CASES.advance);
72
- const next = audit({ raw_input: "HR回复:薪资100元/天。", previous_state: first.next_state });
73
- assert.equal(next.facts.salary.status, "conflict"); assert.equal(next.overall_score, null);
74
- assert.equal(next.route, "verify"); assert.equal(next.facts.salary.evidence.length, 2);
75
- });
76
- test("replaying reply does not duplicate state", () => {
77
- const a = audit(CASES.advance), b = audit({ raw_input: "HR回复:有导师。", previous_state: a.next_state });
78
- const c = audit({ raw_input: "HR回复:有导师。", previous_state: b.next_state });
79
- assert.equal(b.next_state.records.length, c.next_state.records.length);
80
- });
81
- test("caller risk summaries cannot bypass evidence", () => {
82
- const r = calculateOfferScore({ jdText: "后端实习生,需要缴纳押金。", studentProfile: PROFILE, complianceResult: { summary: { red_line_count: 0 } }, riskTermResult: { risk_terms: [] } });
83
- assert.equal(r.route, "reject");
84
- });
85
- test("comparison uses independent offers and avoids rejecting both", () => {
86
- const r = audit({ structured_input: { student_profile: PROFILE, offers: [{ id: "A", jd_text: GOOD_JD }, { id: "B", jd_text: "后端实习生,先交押金800元。" }] } });
87
- assert.equal(r.recommended_offer, "A"); assert.equal(r.offers[0].route, "advance");
88
- assert.equal(r.offers[1].route, "reject"); assert.equal(r.backup_conditions.length, 2);
89
- });
90
- test("comparison does not guess a winner with unknown facts", () => {
91
- const r = audit({ structured_input: { student_profile: PROFILE, offers: [{ id: "A", jd_text: "后端实习生,薪资面议。" }, { id: "B", jd_text: "后端实习生,薪资面议。" }] } });
92
- assert.equal(r.recommended_offer, null);
93
- });
94
- test("negotiation mode produces short communication instead of scored report", () => {
95
- const r = audit(CASES.negotiation);
96
- assert.equal(r.mode, "negotiation"); assert.ok(r.negotiation_cards.length);
97
- assert.ok(!r.report_text.includes("七维评分")); assert.ok(!r.report_text.includes("综合分"));
98
- });
99
- test("next-step menu reuses current offer state", () => {
100
- const a = audit(CASES.reject), r = audit({ raw_input: "2", previous_state: a.next_state });
101
- assert.equal(r.mode, "negotiation"); assert.ok(r.report_text.includes("重大风险"));
102
- });
103
- test("dates use runtime Beijing date; output has no code fences or tables", () => {
104
- const r = audit({ ...CASES.advance, mode: "full_report" });
105
- const expected = new Intl.DateTimeFormat("sv-SE", { timeZone: "Asia/Shanghai" }).format(new Date());
106
- assert.equal(r.report_date, expected);
107
- assert.ok(!r.report_text.includes("```")); assert.ok(!r.report_text.includes("|"));
108
- assert.ok(r.report_text.includes("MCP工具审计摘要"));
109
- });
110
- test("knowledge ownership alone is not an automatic red line", () => {
111
- const r = matchRiskTerms({ jdText: "代码归公司所有,经过审核可脱敏展示。" });
112
- assert.equal(r.risk_terms.length, 0);
113
- });
114
- test("five identical assessments have stable decisions", () => {
115
- const results = Array.from({ length: 5 }, () => audit(CASES.advance));
116
- for (const r of results) assert.deepEqual([r.route, r.overall_score, r.dimensions], [results[0].route, results[0].overall_score, results[0].dimensions]);
117
- });
118
- test("bad or oversized state is rejected", () => {
119
- assert.throws(() => audit({ previous_state: { schema_version: 2, records: [] } }), /schema/);
120
- assert.throws(() => audit({ previous_state: { schema_version: 1, records: [{ source: "system", text: "x" }] } }), /source/);
121
- assert.throws(() => audit({ raw_input: "x".repeat(40001) }), /too long/);
122
- });
123
- test("tool evidence is a literal substring of the correct source", () => {
124
- const r = audit(CASES.reject);
125
- for (const risk of r.risk_terms) for (const e of risk.evidence) {
126
- assert.ok(r.next_state.records.find(s => s.id === e.source_id).text.includes(e.quote));
127
- assert.notEqual(e.source, "student");
128
- }
129
- });
130
- test("compliance tool exposes negative evidence", () => {
131
- const r = checkInternshipCompliance({ jdText: "不收押金。不安排无薪试岗。" });
132
- assert.equal(r.red_lines.length, 0); assert.equal(r.facts.deposit_or_fee.status, "negated");
133
- });
134
- test("coordinated negations do not introduce red lines", () => {
135
- const r = matchRiskTerms({ jdText: "不要求押金和培训贷。不安排无薪试岗。" });
136
- assert.equal(r.risk_terms.length, 0);
137
- });
138
- test("new offer clears old source documents", () => {
139
- const first = audit(CASES.reject);
140
- const r = audit({ ...CASES.advance, previous_state: first.next_state, new_assessment: true });
141
- assert.equal(r.route, "advance"); assert.equal(r.changes.length, 0);
142
- assert.ok(!r.next_state.records.some(x => x.text.includes("800元")));
143
- });
144
- test("salary below student floor cannot advance", () => {
145
- const r = audit({ structured_input: { jd_text: GOOD_JD.replace("180元", "100元"), student_profile: PROFILE } });
146
- assert.equal(r.preference_checks.salary_fit, "below_floor_or_range");
147
- assert.equal(r.route, "verify"); assert.ok(r.one_sentence_reason.includes("底线"));
148
- });
149
- test("negotiation questions address HR and match the requested topic", () => {
150
- const r = audit(CASES.negotiation);
151
- assert.ok(r.report_text.includes("发放时间")); assert.ok(!r.report_text.includes("补充专业"));
152
- });
153
- test("interview mode covers three counterpart roles", () => {
154
- const r = audit({ ...CASES.verify, mode: "interview_prep" });
155
- assert.deepEqual(r.negotiation_cards.map(c => c.role), ["HR", "直属主管", "业务团队"]);
156
- });
157
- test("raw A/B labels keep student and offer sources separate", () => {
158
- const r = audit({ raw_input: `学生:${PROFILE}\nA:${GOOD_JD}\nB:后端实习生,需要押金800元。` });
159
- assert.equal(r.recommended_offer, "A"); assert.equal(r.offers[0].route, "advance");
160
- assert.equal(r.offers[1].route, "reject");
161
- });
162
- test("comparison offers below the student's floor are not recommended", () => {
163
- const r = audit({ structured_input: { student_profile: PROFILE, offers: [{ id: "A", jd_text: GOOD_JD.replace("180元", "100元") }, { id: "B", jd_text: GOOD_JD.replace("180元", "90元") }] } });
164
- assert.equal(r.recommended_offer, null);
165
- });
166
- test("evidence cannot create code fences, tables or embedded images in report", () => {
167
- const r = audit({ structured_input: { jd_text: `${GOOD_JD}\n押金800元,\`\`\` ![x](https://example.com/x) |`, student_profile: PROFILE }, mode: "full_report" });
168
- assert.ok(!r.report_text.includes("```")); assert.ok(!r.report_text.includes("|"));
169
- assert.ok(!r.report_text.includes("![x]("));
170
- });
171
- test("single string MCP binding supports structured workflow envelope", () => {
172
- const first = audit({ raw_input: JSON.stringify({ offerguard_schema: 1, ...CASES.advance }) });
173
- assert.equal(first.route, "advance");
174
- const second = audit({ raw_input: JSON.stringify({ offerguard_schema: 1, user_message: "2", previous_state: first.next_state }) });
175
- assert.equal(second.mode, "negotiation"); assert.equal(second.route, "advance");
176
- });
177
- test("invalid envelope fields cannot bypass schema checks", () => {
178
- assert.throws(() => audit({ raw_input: JSON.stringify({ offerguard_schema: 1, structured_input: { offers: "bad" } }) }), /two offers/);
179
- assert.throws(() => audit({ raw_input: JSON.stringify({ offerguard_schema: 1, mode: "invent" }) }), /mode/);
180
- });
181
- test("no project participation and no dedicated mentor do not receive benefits", () => {
182
- const r = audit({ structured_input: { jd_text: "后端实习生,无专属导师。不参与真实项目。", student_profile: PROFILE } });
183
- assert.equal(r.facts.mentor.status, "negated"); assert.equal(r.facts.project.status, "negated");
184
- assert.equal(r.dimensions.find(d => d.key === "growth_value").score, 20);
185
- });
186
- test("explicitly absent skills do not count as experience matches", () => {
187
- const r = audit({ structured_input: { jd_text: GOOD_JD, student_profile: "新闻学大三。没有Java经验。薪资底线120元/天,每周可到岗4天。" } });
188
- assert.equal(r.dimensions[0].score, 40);
189
- });
190
- test("hypothetical benefit is unknown", () => {
191
- const r = audit({ structured_input: { jd_text: "后端实习生。如果项目启动就安排导师。", student_profile: PROFILE } });
192
- assert.equal(r.facts.mentor.status, "unknown");
193
- });
194
- test("source extraction cannot invent statements absent from original input", () => {
195
- assert.throws(() => audit({ structured_input: { original_input: "JD:薪资面议", jd_text: "薪资180元/天,有导师" } }), /literal excerpt/);
196
- const r = audit({ structured_input: { original_input: `JD:${GOOD_JD}\n学生:${PROFILE}`, jd_text: GOOD_JD, student_profile: PROFILE } });
197
- assert.equal(r.route, "advance");
198
- });
199
- test("supported English risk vocabulary also respects negation", () => {
200
- const r = matchRiskTerms({ jdText: "No deposit. No unpaid trial. We do not require any training loan." });
201
- assert.equal(r.risk_terms.length, 0);
202
- });
203
- test("portfolio prohibition is not a portfolio benefit", () => {
204
- const r = audit({ structured_input: { jd_text: "Backend internship. Portfolio not allowed.", student_profile: PROFILE } });
205
- assert.equal(r.facts.portfolio.status, "negated");
206
- });
207
- test("no conversion criteria does not mean no conversion opportunity", () => {
208
- const r = audit({ structured_input: { jd_text: "后端实习生。无转正标准。", student_profile: PROFILE + "不要求转正。" } });
209
- assert.notEqual(r.dimensions.find(d => d.key === "conversion_clarity").status, "not_applicable");
210
- });
package/test/fixtures.js DELETED
@@ -1,10 +0,0 @@
1
- // Synthetic cases only. They are not injected into production inputs.
2
- export const PROFILE = "大三计算机专业,有 Java 后端项目经历;城市北京;薪资底线120元/天;每周可到岗4天;不接受长期加班。";
3
- export const GOOD_JD = "北京后端开发实习生,参与真实项目开发。薪资180元/天,每月15日发放。10:00-18:00,不加班。入职签署实习协议。有导师每周带教。提供项目证明,可用于作品集。有转正标准和评估周期。";
4
- export const CASES = {
5
- advance: { structured_input: { jd_text: GOOD_JD, student_profile: PROFILE } },
6
- verify: { structured_input: { jd_text: "北京后端开发实习生,参与真实项目开发。薪资150元/天,月结。10:00-18:00,可能加班。签署实习协议。导师未知,转正标准未明确。", student_profile: PROFILE } },
7
- reject: { structured_input: { jd_text: "北京后端实习生,前五天无薪试岗,需缴纳保证金800元。周末随叫随到。", student_profile: PROFILE } },
8
- negotiation: { raw_input: "怎么和HR确认薪资?", mode: "negotiation" },
9
- missing: { raw_input: "JD:新媒体运营实习生,负责内容发布,薪资面议。" }
10
- };
@@ -1,84 +0,0 @@
1
- import test from "node:test";
2
- import assert from "node:assert/strict";
3
- import { Client } from "@modelcontextprotocol/sdk/client/index.js";
4
- import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
5
- import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
6
- import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
7
- import { createApp } from "../src/server.js";
8
- import { TOOL_VERSION } from "../src/decision-engine.js";
9
- import { CASES, GOOD_JD, PROFILE } from "./fixtures.js";
10
-
11
- const names = ["internship_compliance_checker", "offer_score_calculator", "offerguard_audit", "risk_term_matcher"];
12
- async function listen(app) {
13
- return new Promise((resolve, reject) => {
14
- const server = app.listen(0, "127.0.0.1", () => resolve(server));
15
- server.once("error", reject);
16
- });
17
- }
18
- async function close(app, server) {
19
- await app.closeTransports();
20
- server.closeAllConnections();
21
- await new Promise(resolve => server.close(resolve));
22
- }
23
- async function call(client, name, args) {
24
- const r = await client.callTool({ name, arguments: args });
25
- assert.ok(!r.isError, JSON.stringify(r));
26
- assert.ok(r.structuredContent);
27
- assert.deepEqual(r.structuredContent, JSON.parse(r.content.find(c => c.type === "text").text));
28
- return r.structuredContent;
29
- }
30
- async function verify(client) {
31
- const tools = await client.listTools();
32
- assert.deepEqual(tools.tools.map(t => t.name).sort(), names);
33
- const schema = tools.tools.find(t => t.name === "offerguard_audit").inputSchema;
34
- assert.ok(schema.properties.previous_state); assert.ok(schema.properties.structured_input);
35
- const risk = await call(client, "risk_term_matcher", { jdText: "薪资面议,需要弹性工作。" });
36
- assert.ok(risk.risk_terms.some(r => r.term === "薪资面议"));
37
- const compliance = await call(client, "internship_compliance_checker", { jdText: "需要缴纳保证金并无薪试岗。" });
38
- assert.equal(compliance.red_lines.length, 2);
39
- const score = await call(client, "offer_score_calculator", { jdText: GOOD_JD, studentProfile: PROFILE });
40
- assert.equal(score.route, "advance");
41
- const reject = await call(client, "offerguard_audit", CASES.reject);
42
- assert.equal(reject.route, "reject"); assert.equal(reject.overall_score, null);
43
- const missing = await call(client, "offerguard_audit", CASES.missing);
44
- assert.equal(missing.route, "followup"); assert.equal(missing.overall_score, null);
45
- const first = await call(client, "offerguard_audit", { structured_input: { jd_text: GOOD_JD.replace("薪资180元/天,每月15日发放。", ""), student_profile: PROFILE } });
46
- const next = await call(client, "offerguard_audit", { previous_state: first.next_state, raw_input: "HR回复:薪资180元/天,每月15日发放。" });
47
- assert.equal(next.route, "advance"); assert.ok(next.changes.length);
48
- const compared = await call(client, "offerguard_audit", { structured_input: { student_profile: PROFILE, offers: [{ id: "A", jd_text: GOOD_JD }, { id: "B", jd_text: "实习生,需要押金。" }] } });
49
- assert.equal(compared.recommended_offer, "A");
50
- const envelope = await call(client, "offerguard_audit", { raw_input: JSON.stringify({ offerguard_schema: 1, ...CASES.advance }) });
51
- assert.equal(envelope.route, "advance");
52
- const invalid = await client.callTool({ name: "offerguard_audit", arguments: { raw_input: "x".repeat(40001) } });
53
- assert.equal(invalid.isError, true);
54
- const combinedTooLong = await client.callTool({ name: "offerguard_audit", arguments: { raw_input: "x".repeat(21000), structured_input: { jd_text: "y".repeat(21000) } } });
55
- assert.equal(combinedTooLong.isError, true); assert.equal(combinedTooLong.structuredContent.mcp_ok, false);
56
- }
57
-
58
- for (const protocol of ["http", "sse", "stdio"]) {
59
- test(`real MCP ${protocol}: initialize, schema, four tools, state, comparison and errors`, { timeout: 30000 }, async () => {
60
- let app, server, transport;
61
- if (protocol === "stdio") transport = new StdioClientTransport({ command: process.execPath, args: ["src/stdio.js"], cwd: process.cwd(), stderr: "pipe" });
62
- else {
63
- app = createApp({ host: "127.0.0.1" }); server = await listen(app);
64
- const base = `http://127.0.0.1:${server.address().port}`;
65
- const health = await fetch(`${base}/health`).then(r => r.json());
66
- assert.equal(health.version, TOOL_VERSION);
67
- transport = protocol === "http" ? new StreamableHTTPClientTransport(new URL(`${base}/mcp`)) : new SSEClientTransport(new URL(`${base}/sse`));
68
- }
69
- const client = new Client({ name: "offerguard-retest", version: TOOL_VERSION });
70
- try { await client.connect(transport); await verify(client); }
71
- finally { await client.close(); if (server) await close(app, server); }
72
- });
73
- }
74
-
75
- test("HTTP outage rejects tool call instead of producing a report", { timeout: 10000 }, async () => {
76
- const app = createApp({ host: "127.0.0.1" }), server = await listen(app);
77
- const transport = new StreamableHTTPClientTransport(new URL(`http://127.0.0.1:${server.address().port}/mcp`));
78
- const client = new Client({ name: "offerguard-failure-test", version: TOOL_VERSION });
79
- try {
80
- await client.connect(transport);
81
- await close(app, server);
82
- await assert.rejects(() => client.callTool({ name: "offerguard_audit", arguments: CASES.advance }));
83
- } finally { await client.close(); if (server.listening) await close(app, server); }
84
- });
@@ -1,16 +0,0 @@
1
- {
2
- "status": "prepared_not_applied_to_live_workflow",
3
- "target_mcp_version": "0.4.0",
4
- "extraction_system": "你是OfferGuard的来源分离器,只负责区分当前用户原话中的岗位资料、学生资料和HR回复,并识别本轮任务。输出JSON,不评分、不改写事实、不推断企业情况。所有非空来源字段必须逐字取自本轮用户原话;缺失字段省略。不要把学生的底线、希望、拒绝条件放进JD。多段来源不能安全提取时保留raw_input并标记需要用户区分。单岗位返回structured_input.jd_text、student_profile、hr_reply;两岗位返回offers数组,每项id、jd_text、可选hr_reply;学生画像独立共享。original_input必须由工作流直接绑定用户原话,不能由你生成。mode只可为single_check、negotiation、interview_prep、accept_conditions、full_report。回复1/2/3依次对应后三种操作中的interview_prep/negotiation/accept_conditions。新岗位返回new_assessment:true。你无权生成或修改previous_state。演示案例按普通来源处理,不预设分数、路线或结果。",
5
- "input_gate_system": "基于来源分离结果检查是否有岗位原文与学生背景。信息不足最多追问三个关键信息,不评分;已有岗位收费、贷款或无薪安排原文时仍交真实MCP识别风险。有上一轮状态的补充回复、反问、话术和接受条件请求直接交真实MCP,不要求用户重填所有信息。不为简短输入编造城市、工资、背景或到岗信息。具体评分所需字段由MCP统一校验。",
6
- "report_system": "你只负责展示本轮真实MCP返回的report_text。必须先检查平台节点成功状态、结果非空、isError不是true、mcp_ok为true且report_text非空。任一不满足,只输出:MCP 工具调用失败,暂不能生成 OfferGuard 风险报告。请稍后重试。成功时原样输出report_text,不包代码块,不改日期、分数、路线,不增加企业事实,不输出整个JSON,不伪造MCP调用声明,不使用旧报告替代本轮失败。next_state由工作流会话变量保存,不在用户报告展示。",
7
- "welcome": "你好,我是OfferGuard。把岗位JD和你的专业、相关经历、薪资底线及到岗安排发给我,我会先给出简短判断和最需要确认的问题。拿到HR回复后继续发来,我会说明哪些判断发生了变化。",
8
- "integration_notes": [
9
- "这是待应用的提示词资源,不是星辰可导入工作流文件。",
10
- "先读取并备份线上版本再修改,不能用旧本地快照覆盖当前配置。",
11
- "优先直接绑定report_text到结束节点,省去模型重写。平台能力须实际核对。",
12
- "previous_state必须在同用户、同会话、同岗位内隔离保存。",
13
- "envelope_json必须使用结构化序列化组合真实输入和状态,禁止模型重写状态。",
14
- "MCP故障不能使用代码或Prompt伪装审计成功。"
15
- ]
16
- }