@lazyingart/agintiflow 0.20.327 → 0.20.329

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.
@@ -675,3 +675,28 @@ demonstrated evidence from inferred recommendations, and finished normally.
675
675
  The retained trace contains no completion-evidence rejection or forced
676
676
  source-mutation loop. Focused completion, evidence, progressive-tool, runtime,
677
677
  and dynamic-budget suites plus the full npm suite pass for `0.20.303`.
678
+
679
+ ### Response-only context compaction after provider handoff
680
+
681
+ `response-only-context-handoff-092` exercises a high-risk continuation boundary
682
+ for DeepSeek-first operation with LocalLLM fallback. Recent same-session
683
+ response-only evidence showed DeepSeek quota failures followed by LocalLLM
684
+ resume attempts that failed before inference because the retained transcript
685
+ exceeded the LocalLLM context window. Normal agent-step requests already had a
686
+ local context-budget compaction retry, but the explicit response-only branch
687
+ called the direct-response client without that recovery path.
688
+
689
+ AgInTiFlow now catches only `LOCALLLM_CONTEXT_BUDGET_EXCEEDED` in the
690
+ response-only branch, compacts the authoritative retained goal/evidence once,
691
+ persists `model.local_context_budget_exceeded` and
692
+ `history.compacted_for_local_context_retry`, and retries the same response-only
693
+ request with a bounded output reserve. The source-free evidence guard remains
694
+ active after compaction, so unsupported publication, validation, forecast,
695
+ benchmark, or metric claims still retry once and then fail closed.
696
+
697
+ The focused regression seeds a DeepSeek-owned response-only session, inflates
698
+ retained same-session context, resumes with a normal "answer from the saved
699
+ status" prompt, triggers a DeepSeek quota handoff, and verifies that LocalLLM is
700
+ called only after compaction. The run completes without `session.failed`, keeps
701
+ the provider handoff active on the same session, and records the compaction
702
+ events as durable evidence.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lazyingart/agintiflow",
3
- "version": "0.20.327",
3
+ "version": "0.20.329",
4
4
  "type": "module",
5
5
  "description": "AgInTiFlow is a project-aware agent workspace for hybrid wet-dry R&D, hardware-aware intelligence, software automation, and industrial workflows.",
6
6
  "license": "Apache-2.0",
@@ -14,7 +14,11 @@ import {
14
14
  } from "../src/agent-runner.js";
15
15
  import { resolveRuntimeConfig } from "../src/config.js";
16
16
  import { SessionStore } from "../src/session-store.js";
17
- import { deriveScsTaskContract, finishResultClaimsIncompleteWork } from "../src/scs-evidence.js";
17
+ import {
18
+ deriveScsTaskContract,
19
+ evaluateSourceFreeResponseClaims,
20
+ finishResultClaimsIncompleteWork,
21
+ } from "../src/scs-evidence.js";
18
22
 
19
23
  const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
20
24
  const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "agintiflow-truthful-completion-"));
@@ -180,6 +184,114 @@ assert.equal(
180
184
  "agent-owned pending validation was accepted as a completed result"
181
185
  );
182
186
 
187
+ const sourceFreeResearchGoal = [
188
+ "Correct the research status from the host-managed response context.",
189
+ `AGINTI_EVIDENCE_SCOPE_JSON: ${JSON.stringify({
190
+ mode: "host-managed-response",
191
+ request: "Correct the research status from the host-managed response context.",
192
+ })}`,
193
+ ].join("\n");
194
+ const unsafeSourceFreeClaim = evaluateSourceFreeResponseClaims({
195
+ goal: sourceFreeResearchGoal,
196
+ candidateResult:
197
+ "The publication appeared in 2025 and was validated on a 12,000-case benchmark with 94.2% accuracy.",
198
+ evidenceLedger: { itemCount: 0, categories: [], items: [] },
199
+ });
200
+ assert.equal(
201
+ unsafeSourceFreeClaim.ok,
202
+ false,
203
+ "source-free response-only output accepted unsupported external factual claims"
204
+ );
205
+ assert(unsafeSourceFreeClaim.categories.includes("publication"));
206
+ assert(unsafeSourceFreeClaim.categories.includes("benchmark_or_metric"));
207
+ const unsafeChineseSourceFreeClaim = evaluateSourceFreeResponseClaims({
208
+ goal: sourceFreeResearchGoal,
209
+ candidateResult:
210
+ "这是未验证的背景说明。2025年Nature子刊预印本未公开,已有初步验证,响应延迟低于100ms,并预测2026年底前上线。",
211
+ evidenceLedger: { itemCount: 0, categories: [], items: [] },
212
+ });
213
+ assert.equal(
214
+ unsafeChineseSourceFreeClaim.ok,
215
+ false,
216
+ "Chinese source-free response-only output accepted publication, validation, metric, or forecast claims"
217
+ );
218
+ assert(unsafeChineseSourceFreeClaim.categories.includes("publication"));
219
+ assert(unsafeChineseSourceFreeClaim.categories.includes("validation"));
220
+ assert(unsafeChineseSourceFreeClaim.categories.includes("benchmark_or_metric"));
221
+ assert(unsafeChineseSourceFreeClaim.categories.includes("forecast"));
222
+ const unsafeJapaneseSourceFreeClaim = evaluateSourceFreeResponseClaims({
223
+ goal: sourceFreeResearchGoal,
224
+ candidateResult:
225
+ "2026年末までに公開される見込みで、査読済み研究によりレイテンシ80ms未満が検証済みです。",
226
+ evidenceLedger: { itemCount: 0, categories: [], items: [] },
227
+ });
228
+ assert.equal(
229
+ unsafeJapaneseSourceFreeClaim.ok,
230
+ false,
231
+ "Japanese source-free response-only output accepted publication, validation, metric, or forecast claims"
232
+ );
233
+ assert(unsafeJapaneseSourceFreeClaim.categories.includes("forecast"));
234
+ assert(unsafeJapaneseSourceFreeClaim.categories.includes("validation"));
235
+ assert(unsafeJapaneseSourceFreeClaim.categories.includes("benchmark_or_metric"));
236
+ const framedHypothesisClaim = evaluateSourceFreeResponseClaims({
237
+ goal: sourceFreeResearchGoal,
238
+ candidateResult:
239
+ "Without fresh evidence, this is an unverified hypothesis only: the result may need a new literature check before any publication or benchmark claim is trusted.",
240
+ evidenceLedger: { itemCount: 0, categories: [], items: [] },
241
+ });
242
+ assert.equal(
243
+ framedHypothesisClaim.ok,
244
+ true,
245
+ "explicit unverified hypothesis framing was rejected for source-free response-only output"
246
+ );
247
+ const locallyDeniedChineseClaim = evaluateSourceFreeResponseClaims({
248
+ goal: sourceFreeResearchGoal,
249
+ candidateResult:
250
+ "没有本次新证据,这只是未验证假设:无法验证Nature子刊、2025年发表、已有验证或100ms延迟等说法。",
251
+ evidenceLedger: { itemCount: 0, categories: [], items: [] },
252
+ });
253
+ assert.equal(
254
+ locallyDeniedChineseClaim.ok,
255
+ true,
256
+ "local Chinese unverifiable/hypothesis framing was rejected"
257
+ );
258
+ const separatedUnverifiedClaim = evaluateSourceFreeResponseClaims({
259
+ goal: sourceFreeResearchGoal,
260
+ candidateResult:
261
+ "This paragraph is unverified. The Nature publication appeared in 2025 and validation reached 94.2% accuracy.",
262
+ evidenceLedger: { itemCount: 0, categories: [], items: [] },
263
+ });
264
+ assert.equal(
265
+ separatedUnverifiedClaim.ok,
266
+ false,
267
+ "a generic unverified phrase in one sentence governed a separate unsupported factual claim"
268
+ );
269
+ const ordinaryPureChat = evaluateSourceFreeResponseClaims({
270
+ goal: sourceFreeResearchGoal,
271
+ candidateResult: "A recursive function needs a base case so it can stop.",
272
+ evidenceLedger: { itemCount: 0, categories: [], items: [] },
273
+ });
274
+ assert.equal(
275
+ ordinaryPureChat.ok,
276
+ true,
277
+ "ordinary source-free pure chat was incorrectly rejected"
278
+ );
279
+ const sourcedResponseOnlyClaim = evaluateSourceFreeResponseClaims({
280
+ goal: sourceFreeResearchGoal,
281
+ candidateResult:
282
+ "The retained manifest says the benchmark accuracy is 94.2%.",
283
+ evidenceLedger: {
284
+ itemCount: 1,
285
+ categories: ["command"],
286
+ items: [{ category: "command", verified: true }],
287
+ },
288
+ });
289
+ assert.equal(
290
+ sourcedResponseOnlyClaim.ok,
291
+ true,
292
+ "response-only output with current scoped evidence was rejected"
293
+ );
294
+
183
295
  const staleCompletionRepair =
184
296
  "The proposed completion was rejected because the requested action is not supported by concrete runtime evidence. Reason: Missing required git action(s): commit.";
185
297
  const repairCleanup = removeSupersededCompletionRepairInstructions([
@@ -140,6 +140,7 @@ import {
140
140
  evaluateScsSemanticContract,
141
141
  evaluateRequestedArtifactRequirements,
142
142
  augmentScsTaskContractWithProjectVerification,
143
+ evaluateSourceFreeResponseClaims,
143
144
  agintiEvidenceScopeLine,
144
145
  filterExplicitlyExcludedOutputPaths,
145
146
  extractMarkdownCommandEvidence,
@@ -4099,7 +4100,127 @@ async function finishWithDirectAnswer({ config, state, store, observers, session
4099
4100
  };
4100
4101
  }
4101
4102
 
4103
+ function responseOnlyResultFromMessage(message = {}) {
4104
+ if (!message) {
4105
+ throw new Error("Response-only model request returned no assistant message.");
4106
+ }
4107
+ if (Array.isArray(message.tool_calls) && message.tool_calls.length) {
4108
+ throw new Error("Response-only model request returned an unexpected tool call.");
4109
+ }
4110
+ const result = redactSensitiveText(message.content || "").trim();
4111
+ if (!result) {
4112
+ throw new Error("Response-only model request returned empty content.");
4113
+ }
4114
+ return result;
4115
+ }
4116
+
4117
+ async function assessResponseOnlySourceFreeClaims({ config, state, store, result }) {
4118
+ const scoped = currentContinuationEvidence(state, await store.loadEvents());
4119
+ const ledger = buildScsEvidenceLedger({
4120
+ state: scoped.state,
4121
+ context: {
4122
+ events: scoped.events,
4123
+ taskProfile: config.taskProfile,
4124
+ goal: config.goal,
4125
+ },
4126
+ });
4127
+ return evaluateSourceFreeResponseClaims({
4128
+ goal: completionContractGoal(config, state),
4129
+ candidateResult: result,
4130
+ evidenceLedger: ledger,
4131
+ });
4132
+ }
4133
+
4134
+ function responseOnlySourceFreeRepairInstruction(assessment = {}) {
4135
+ const categories = Array.isArray(assessment.categories) && assessment.categories.length
4136
+ ? assessment.categories.join(", ")
4137
+ : "external factual claims";
4138
+ return [
4139
+ "No fresh AgInTi evidence manifest or scoped tool evidence is available for this response-only turn.",
4140
+ `Your previous answer asserted unsupported external facts (${categories}).`,
4141
+ "Do not claim publications, years, validation, forecasts, benchmarks, quantitative metrics, citations, or source-backed conclusions.",
4142
+ "This includes multilingual claim wording such as 已有验证, 预测, 検証済み, or 予測.",
4143
+ "Return a concise answer that is explicitly framed as an unverified hypothesis, or state that the requested external claim cannot be verified from this run.",
4144
+ ].join(" ");
4145
+ }
4146
+
4147
+ function responseOnlySourceFreeStopResult(assessment = {}) {
4148
+ const categories = Array.isArray(assessment.categories) && assessment.categories.length
4149
+ ? assessment.categories.join(", ")
4150
+ : "external factual claims";
4151
+ return [
4152
+ "No result: this response-only turn has no fresh AgInTi evidence manifest or scoped tool evidence.",
4153
+ `I cannot verify the requested ${categories} from this run. Resume with an evidence-producing research/tool scope or provide a fresh evidence manifest.`,
4154
+ ].join(" ");
4155
+ }
4156
+
4102
4157
  async function finishWithResponseOnlyModelTurn({ client, config, state, store, observers, sessionId }) {
4158
+ async function requestWithResponseOnlyLocalContextRecovery({ step, mode }) {
4159
+ try {
4160
+ return await requestDirectResponse(client, config, state.messages);
4161
+ } catch (error) {
4162
+ if (!isLocalContextBudgetError(error)) throw error;
4163
+ state.meta = state.meta || {};
4164
+ const retried = state.meta.localContextBudgetRetries || {};
4165
+ const retryKey = `response-only:${mode}`;
4166
+ if (retried[retryKey]) throw error;
4167
+
4168
+ const requestMessages = Array.isArray(state.messages) ? state.messages : [];
4169
+ const currentOutputTokens = Math.max(0, Number(config.maxOutputTokens || 0));
4170
+ const retryOutputTokens = currentOutputTokens
4171
+ ? Math.min(currentOutputTokens, 4096)
4172
+ : 4096;
4173
+ const retryRuntimeConfig = {
4174
+ ...config,
4175
+ maxOutputTokens: retryOutputTokens,
4176
+ };
4177
+ const compactMessages = buildContextBudgetCompactionMessages(
4178
+ state,
4179
+ retryRuntimeConfig,
4180
+ null,
4181
+ step,
4182
+ {
4183
+ heading: "A response-only LocalLLM request exceeded the local context window.",
4184
+ detail: redactSensitiveText(error instanceof Error ? error.message : String(error)),
4185
+ recoveryInstruction:
4186
+ "Answer the current response-only request directly from the compacted authoritative context above. Do not claim fresh external evidence unless the compacted context contains a current evidence manifest or scoped tool evidence.",
4187
+ }
4188
+ );
4189
+ const detail = {
4190
+ step,
4191
+ mode,
4192
+ provider: config.provider,
4193
+ model: config.model,
4194
+ messageCharsBefore: countMessageChars(requestMessages),
4195
+ messageCharsAfter: countMessageChars(compactMessages),
4196
+ messageTokensBefore: estimateMessageTokens(requestMessages),
4197
+ messageTokensAfter: estimateMessageTokens(compactMessages),
4198
+ maxOutputTokens: retryOutputTokens,
4199
+ error: redactSensitiveText(error instanceof Error ? error.message : String(error)),
4200
+ };
4201
+ state.messages = compactMessages;
4202
+ resetStaticDiscoveryAfterContextLoss(state, "response-only-local-context-budget-retry", {
4203
+ preserveStaticEvidence: true,
4204
+ });
4205
+ state.meta.localContextBudgetRetries = {
4206
+ ...retried,
4207
+ [retryKey]: true,
4208
+ };
4209
+ state.meta.lastResponseOnlyContextBudgetRecovery = detail;
4210
+ await store.appendEvent("model.local_context_budget_exceeded", detail);
4211
+ await store.appendEvent("history.compacted_for_local_context_retry", detail);
4212
+ observers.event("model.local_context_budget_exceeded", detail);
4213
+ observers.event("history.compacted_for_local_context_retry", detail);
4214
+ emitConsole(
4215
+ config,
4216
+ "Local provider context exceeded its configured window for a response-only turn; compacted authoritative context and retrying once.",
4217
+ { kind: "meta" }
4218
+ );
4219
+ await store.saveState(state);
4220
+ return await requestDirectResponse(client, retryRuntimeConfig, state.messages);
4221
+ }
4222
+ }
4223
+
4103
4224
  await store.appendEvent("model.requested", {
4104
4225
  step: 1,
4105
4226
  provider: config.provider,
@@ -4113,17 +4234,116 @@ async function finishWithResponseOnlyModelTurn({ client, config, state, store, o
4113
4234
  mode: "response-only",
4114
4235
  });
4115
4236
 
4116
- const response = await requestDirectResponse(client, config, state.messages);
4237
+ const response = await requestWithResponseOnlyLocalContextRecovery({
4238
+ step: 1,
4239
+ mode: "response-only",
4240
+ });
4117
4241
  const rawAssistantMessage = response.choices[0]?.message;
4118
- if (!rawAssistantMessage) {
4119
- throw new Error("Response-only model request returned no assistant message.");
4120
- }
4121
- if (Array.isArray(rawAssistantMessage.tool_calls) && rawAssistantMessage.tool_calls.length) {
4122
- throw new Error("Response-only model request returned an unexpected tool call.");
4123
- }
4124
- const result = redactSensitiveText(rawAssistantMessage.content || "").trim();
4125
- if (!result) {
4126
- throw new Error("Response-only model request returned empty content.");
4242
+ let result = responseOnlyResultFromMessage(rawAssistantMessage);
4243
+ let finalAssistantMessage = rawAssistantMessage;
4244
+ let sourceFreeAssessment = await assessResponseOnlySourceFreeClaims({
4245
+ config,
4246
+ state,
4247
+ store,
4248
+ result,
4249
+ });
4250
+ if (!sourceFreeAssessment.ok) {
4251
+ const detail = {
4252
+ step: 1,
4253
+ mode: "response-only",
4254
+ reason: sourceFreeAssessment.reason,
4255
+ categories: sourceFreeAssessment.categories,
4256
+ hasEvidence: sourceFreeAssessment.hasEvidence,
4257
+ explicitlyUnverified: sourceFreeAssessment.explicitlyUnverified,
4258
+ preview: publicCompletionText(result, 300),
4259
+ unsupportedClaims: sourceFreeAssessment.unsupportedClaims || [],
4260
+ };
4261
+ await store.appendEvent("response_only.source_free_claim_rejected", detail);
4262
+ observers.event("response_only.source_free_claim_rejected", detail);
4263
+ state.messages.push({
4264
+ role: "user",
4265
+ content: responseOnlySourceFreeRepairInstruction(sourceFreeAssessment),
4266
+ });
4267
+ await store.saveState(state);
4268
+ await store.appendEvent("model.requested", {
4269
+ step: 2,
4270
+ provider: config.provider,
4271
+ model: config.model,
4272
+ mode: "response-only-repair",
4273
+ });
4274
+ observers.event("model.requested", {
4275
+ step: 2,
4276
+ provider: config.provider,
4277
+ model: config.model,
4278
+ mode: "response-only-repair",
4279
+ });
4280
+ const repairResponse = await requestWithResponseOnlyLocalContextRecovery({
4281
+ step: 2,
4282
+ mode: "response-only-repair",
4283
+ });
4284
+ finalAssistantMessage = repairResponse.choices[0]?.message;
4285
+ result = responseOnlyResultFromMessage(finalAssistantMessage);
4286
+ sourceFreeAssessment = await assessResponseOnlySourceFreeClaims({
4287
+ config,
4288
+ state,
4289
+ store,
4290
+ result,
4291
+ });
4292
+ if (sourceFreeAssessment.ok) {
4293
+ const repaired = {
4294
+ step: 2,
4295
+ mode: "response-only-repair",
4296
+ reason: sourceFreeAssessment.reason,
4297
+ categories: sourceFreeAssessment.categories,
4298
+ explicitlyUnverified: sourceFreeAssessment.explicitlyUnverified,
4299
+ unsupportedClaims: sourceFreeAssessment.unsupportedClaims || [],
4300
+ };
4301
+ await store.appendEvent("response_only.source_free_claim_repaired", repaired);
4302
+ observers.event("response_only.source_free_claim_repaired", repaired);
4303
+ } else {
4304
+ result = responseOnlySourceFreeStopResult(sourceFreeAssessment);
4305
+ const fallback = {
4306
+ step: 2,
4307
+ mode: "response-only-fail-closed",
4308
+ reason: sourceFreeAssessment.reason,
4309
+ categories: sourceFreeAssessment.categories,
4310
+ unsupportedClaims: sourceFreeAssessment.unsupportedClaims || [],
4311
+ result: publicCompletionText(result, 500),
4312
+ };
4313
+ state.meta = state.meta || {};
4314
+ state.meta.responseOnly = {
4315
+ stoppedAt: new Date().toISOString(),
4316
+ provider: config.provider,
4317
+ model: config.model,
4318
+ sourceFreeClaimBlocked: true,
4319
+ };
4320
+ state.updatedAt = state.meta.responseOnly.stoppedAt;
4321
+ state.messages.push({ role: "assistant", content: result });
4322
+ appendChatEntry(state, "assistant", result);
4323
+ updateGoalStatus(state, "paused", "source_free_evidence_required", state.updatedAt);
4324
+ await store.saveState(state);
4325
+ await store.appendEvent("response_only.source_free_claim_failed_closed", fallback);
4326
+ await store.appendEvent("session.stopped", {
4327
+ reason: "source_free_evidence_required",
4328
+ result,
4329
+ mode: "response-only",
4330
+ });
4331
+ observers.event("response_only.source_free_claim_failed_closed", fallback);
4332
+ observers.event("session.stopped", {
4333
+ reason: "source_free_evidence_required",
4334
+ result,
4335
+ sessionId,
4336
+ mode: "response-only",
4337
+ });
4338
+ emitConsole(config, result, { kind: "assistant", markdown: true });
4339
+ return {
4340
+ sessionId,
4341
+ stopped: true,
4342
+ reason: "source_free_evidence_required",
4343
+ result,
4344
+ ...goalRunMetadata(state),
4345
+ };
4346
+ }
4127
4347
  }
4128
4348
 
4129
4349
  state.meta = state.meta || {};
@@ -4135,7 +4355,7 @@ async function finishWithResponseOnlyModelTurn({ client, config, state, store, o
4135
4355
  state.stepsCompleted = 1;
4136
4356
  state.updatedAt = state.meta.responseOnly.completedAt;
4137
4357
  state.messages.push(preserveAssistantMessage({
4138
- ...rawAssistantMessage,
4358
+ ...finalAssistantMessage,
4139
4359
  role: "assistant",
4140
4360
  content: result,
4141
4361
  tool_calls: undefined,
@@ -300,6 +300,7 @@ export async function requestDirectResponse(client, config, messages = []) {
300
300
  "Response-only scope: return the requested final content directly as the assistant response.",
301
301
  "Do not produce an execution plan, call tools, mention internal runtime details, or stop at a placeholder.",
302
302
  "Preserve every material requirement and all source-grounded details supplied in the current request.",
303
+ "If no fresh evidence manifest or source text is present, do not claim publications, years, validation, forecasts, benchmarks, quantitative metrics, citations, or source-backed conclusions; in English, Chinese, Japanese, or any other response language, frame such material locally as an unverified hypothesis or say it cannot be verified from this run.",
303
304
  ].join(" "),
304
305
  },
305
306
  ];
@@ -1825,6 +1825,191 @@ export function isResponseOnlyEvidenceScope(goal = "") {
1825
1825
  return ["chat-response", "host-managed-response", "plan-response", "read-only-answer"].includes(mode);
1826
1826
  }
1827
1827
 
1828
+ function responseOnlyScopeHasFreshEvidenceManifest(goal = "") {
1829
+ const payload = parseAgintiEvidenceScope(goal);
1830
+ if (!payload || typeof payload !== "object") return false;
1831
+ const manifestCandidates = [
1832
+ payload.evidenceManifest,
1833
+ payload.evidence_manifest,
1834
+ payload.freshEvidenceManifest,
1835
+ payload.fresh_evidence_manifest,
1836
+ payload.sourceManifest,
1837
+ payload.source_manifest,
1838
+ payload.manifestDigest,
1839
+ payload.manifest_digest,
1840
+ payload.evidenceDigest,
1841
+ payload.evidence_digest,
1842
+ payload.sourceDigest,
1843
+ payload.source_digest,
1844
+ ];
1845
+ return manifestCandidates.some((item) => {
1846
+ if (typeof item === "string") return item.trim().length >= 12;
1847
+ if (item && typeof item === "object") return Object.keys(item).length > 0;
1848
+ return Array.isArray(item) && item.length > 0;
1849
+ });
1850
+ }
1851
+
1852
+ function sourceFreeResponseHasEvidence(ledger = {}) {
1853
+ if (!ledger || typeof ledger !== "object") return false;
1854
+ if (Number(ledger.itemCount || 0) > 0) return true;
1855
+ if (Array.isArray(ledger.items) && ledger.items.some((item) => item?.verified !== false)) return true;
1856
+ if (Array.isArray(ledger.categories) && ledger.categories.length > 0) return true;
1857
+ return false;
1858
+ }
1859
+
1860
+ function sourceFreeClaimSegments(text = "") {
1861
+ return String(text || "")
1862
+ .split(/(?:[\n\r]+|(?<=[.!?。!?;;]))/u)
1863
+ .map((item) => item.trim())
1864
+ .filter(Boolean);
1865
+ }
1866
+
1867
+ function sourceFreeClaimSegmentHasExplicitUnverifiedFraming(text = "") {
1868
+ const value = String(text || "");
1869
+ const admitsNoEvidence =
1870
+ /\b(?:unverified|not\s+verified|cannot\s+verify|can't\s+verify|could\s+not\s+verify|no\s+(?:fresh\s+)?(?:evidence|sources?|manifest)|without\s+(?:fresh\s+)?(?:evidence|sources?|manifest))\b/iu.test(
1871
+ value
1872
+ ) ||
1873
+ /(?:没有|沒有|无|無|缺少|未获得|未取得|未取得到)(?:新鲜|新鮮|当前|當前|本次|新的)?(?:证据|證據|来源|來源|资料|資料|文献|文獻|检索|檢索|材料)/u.test(
1874
+ value
1875
+ ) ||
1876
+ /(?:无法|無法|不能|未能|没法|沒法)(?:在本次|从本次|從本次)?(?:验证|驗證|核实|核實|证实|證實|确认|確認|证明|證明|支持)/u.test(
1877
+ value
1878
+ ) ||
1879
+ /(?:証拠|出典|根拠|資料|ソース)(?:が)?(?:ない|ありません|不足)|(?:検証|確認|裏付け)(?:できない|されていない|できません)|未検証/u.test(
1880
+ value
1881
+ );
1882
+ const framesAsHypothesis =
1883
+ /\b(?:hypothesis|hypotheses|speculative|speculation|not\s+(?:a\s+)?(?:verified|evidence-backed|source-backed)\s+claim)\b/iu.test(
1884
+ value
1885
+ ) ||
1886
+ /(?:假设|假說|推测|推測|猜测|猜測|臆测|臆測|未验证|未經驗證|未经验证|未核实|未核實)/u.test(
1887
+ value
1888
+ ) ||
1889
+ /(?:仮説|推測|憶測|未検証|未確認)/u.test(value);
1890
+ return admitsNoEvidence && framesAsHypothesis;
1891
+ }
1892
+
1893
+ function sourceFreeClaimSegmentDeniesVerification(text = "") {
1894
+ const value = String(text || "");
1895
+ return (
1896
+ /\b(?:cannot|can't|could\s+not|do\s+not|don't|unable\s+to|not\s+able\s+to|no\s+(?:fresh\s+)?(?:evidence|source|manifest)\s+to|without\s+(?:fresh\s+)?(?:evidence|sources?|manifest),?\s+(?:i\s+)?(?:cannot|can't|could\s+not)?)\b[^.!?;。!?;\n]{0,140}\b(?:verify|confirm|substantiate|support|validate|prove|claim)\b/iu.test(
1897
+ value
1898
+ ) ||
1899
+ /(?:无法|無法|不能|未能|没法|沒法|不应|不應|不能够|不能夠|没有证据|沒有證據|无证据|無證據)[^。!?;\n]{0,80}(?:验证|驗證|核实|核實|证实|證實|确认|確認|证明|證明|支持|声称|聲稱|断言|斷言)/u.test(
1900
+ value
1901
+ ) ||
1902
+ /(?:検証|確認|裏付け|断言|主張)(?:できない|できません|されていない)|(?:証拠|出典|根拠)(?:が)?(?:ない|ありません)[^。!?;\n]{0,60}(?:検証|確認|主張|断言)/u.test(
1903
+ value
1904
+ )
1905
+ );
1906
+ }
1907
+
1908
+ function sourceFreeExternalClaimCategoriesForSegment(text = "") {
1909
+ const value = String(text || "");
1910
+ if (!value.trim()) return [];
1911
+ const categories = [];
1912
+ const add = (category, pattern) => {
1913
+ pattern.lastIndex = 0;
1914
+ if (pattern.test(value)) categories.push(category);
1915
+ };
1916
+ add(
1917
+ "publication",
1918
+ /\b(?:paper|study|article|preprint|publication|manuscript|dataset|trial|journal|conference|arxiv|doi|nature|science|cell)\b[^.!?;。!?;\n]{0,140}\b(?:published|appeared|released|accepted|reported|found|showed|demonstrated|validated)\b|\b(?:published|accepted|released)\b[^.!?;。!?;\n]{0,80}\b(?:paper|study|article|preprint|publication|manuscript|dataset|trial|journal|conference|arxiv|doi|nature|science|cell)\b|(?:Nature|Science|Cell|子刊|期刊|论文|論文|预印本|預印本|文章|研究|数据集|資料集|データセット|論文|研究|プレプリント|ジャーナル)[^.!?;。!?;\n]{0,80}(?:发表|發表|刊登|出版|公开|公開|收录|收録|发布|發布|掲載|発表|公開|出版)|(?:发表|發表|刊登|出版|公开|公開|收录|收録|发布|發布|掲載|発表|公開|出版)[^.!?;。!?;\n]{0,80}(?:Nature|Science|Cell|子刊|期刊|论文|論文|预印本|預印本|文章|研究|数据集|資料集|データセット|論文|研究|プレプリント|ジャーナル)/iu
1919
+ );
1920
+ add(
1921
+ "year",
1922
+ /\b(?:published|released|announced|accepted|reported|validated|verified|evaluated|benchmarked|forecast(?:ed)?|projected|predicted)\b[^.!?;。!?;\n]{0,100}\b(?:19|20)\d{2}\b|\b(?:19|20)\d{2}\b[^.!?;。!?;\n]{0,100}\b(?:publication|paper|study|article|benchmark|forecast|projection|dataset|trial|validation|release)\b|(?:19|20)\d{2}\s*年?[^.!?;。!?;\n]{0,80}(?:Nature|Science|Cell|子刊|期刊|论文|論文|预印本|預印本|文章|研究|发表|發表|发布|發布|预测|預測|预计|預計|验证|驗證|基准|基準|指标|指標|論文|研究|発表|公開|掲載|予測|検証|ベンチマーク)|(?:Nature|Science|Cell|子刊|期刊|论文|論文|预印本|預印本|文章|研究|发表|發表|发布|發布|预测|預測|预计|預計|验证|驗證|基准|基準|指标|指標|論文|研究|発表|公開|掲載|予測|検証|ベンチマーク)[^.!?;。!?;\n]{0,80}(?:19|20)\d{2}\s*年?/iu
1923
+ );
1924
+ add(
1925
+ "validation",
1926
+ /\b(?:validated|verified|proven|confirmed|replicated|peer-reviewed|source-backed|evidence-backed|grounded\s+in\s+(?:sources?|evidence)|the\s+evidence\s+(?:shows|confirms|validates|proves))\b|(?:已有|已经|已經|已经有|已經有|已|初步|经过|經過|得到|获得|獲得)[^。!?;\n]{0,20}(?:验证|驗證|核实|核實|证实|證實|确认|確認|证明|證明)|(?:验证|驗證|核实|核實|证实|證實|确认|確認|证明|證明)(?:通过|通過|完成|成功|结果|結果)|(?:検証済み|確認済み|実証済み|裏付けられた|査読済み)/iu
1927
+ );
1928
+ add(
1929
+ "forecast",
1930
+ /\b(?:forecast|forecasted|predict(?:s|ed|ion)?|project(?:s|ed|ion)?|expected\s+to|will\s+(?:reach|increase|decrease|grow|decline|outperform|underperform)|cagr)\b|(?:预测|預測|预计|預計|估计|估計|推算|推測|到\s*(?:19|20)\d{2}\s*年?(?:底|末)?(?:前|之前)?|(?:19|20)\d{2}\s*年?(?:底|末)?(?:前|之前)?[^。!?;\n]{0,40}(?:将|將|会|會|预计|預計|预测|預測))|(?:予測|予想|見込み|推定|年末まで|までに)/iu
1931
+ );
1932
+ add(
1933
+ "benchmark_or_metric",
1934
+ /\b(?:benchmark(?:ed|s)?|metric|score|accuracy|precision|recall|f1|auc|bleu|rouge|latency|throughput|validated\s+on|evaluated\s+on)\b[^.!?;。!?;\n]{0,100}\b\d[\d,.]*(?:\s*(?:%|percent|cases?|subjects?|participants?|patients?|samples?|records?|tokens?\/s|requests?\/s|ms|seconds?|x))?\b|\b\d[\d,.]*(?:\.\d+)?\s*(?:%|percent|cases?|subjects?|participants?|patients?|samples?|records?|benchmarks?|tokens?\/s|requests?\/s|ms|seconds?)\b[^.!?;。!?;\n]{0,100}\b(?:accuracy|validated|verified|benchmark|forecast|prediction|projection|reliable|better|improved|increase|decrease)\b|(?:响应延迟|響應延遲|响应时间|響應時間|延迟|延遲|准确率|準確率|精度|召回|吞吐|基准|基準|指标|指標|分数|分數|反応遅延|レイテンシ|精度|ベンチマーク|指標|スコア)[^。!?;\n]{0,60}\d[\d,.]*(?:\s*(?:%|%|ms|毫秒|秒|例|个|個|件|倍|x))?|\d[\d,.]*(?:\.\d+)?\s*(?:%|%|ms|毫秒|秒|例|个|個|件|倍|x)[^。!?;\n]{0,60}(?:响应延迟|響應延遲|响应时间|響應時間|延迟|延遲|准确率|準確率|精度|召回|吞吐|基准|基準|指标|指標|分数|分數|反応遅延|レイテンシ|精度|ベンチマーク|指標|スコア)/iu
1935
+ );
1936
+ add(
1937
+ "external_evidence",
1938
+ /\b(?:according\s+to|as\s+reported\s+by|sources?\s+(?:show|say|indicate|confirm|report)|cit(?:e|ation|ed)|doi\s*[:/]|arxiv\s*[:/]|the\s+(?:paper|study|source|evidence)\s+(?:shows|states|reports|confirms|validates))\b|(?:根据|根據|据|據|来源|來源|资料|資料|证据|證據|文献|文獻|论文|論文|引用)[^。!?;\n]{0,50}(?:显示|顯示|表明|指出|报道|報道|证明|證明|验证|驗證|确认|確認)|(?:によると|出典|証拠|根拠|引用|論文|研究)[^。!?;\n]{0,50}(?:示す|示した|報告|確認|検証)/iu
1939
+ );
1940
+ return unique(categories);
1941
+ }
1942
+
1943
+ function sourceFreeExternalClaimAssessment(text = "") {
1944
+ const segments = sourceFreeClaimSegments(text);
1945
+ const categories = [];
1946
+ const unsupported = [];
1947
+ for (const segment of segments) {
1948
+ const segmentCategories = sourceFreeExternalClaimCategoriesForSegment(segment);
1949
+ if (!segmentCategories.length) continue;
1950
+ categories.push(...segmentCategories);
1951
+ const deniesVerification = sourceFreeClaimSegmentDeniesVerification(segment);
1952
+ const explicitlyUnverified = sourceFreeClaimSegmentHasExplicitUnverifiedFraming(segment);
1953
+ const assertsEvidence = segmentCategories.some((category) =>
1954
+ ["validation", "external_evidence"].includes(category)
1955
+ );
1956
+ if (!deniesVerification && (assertsEvidence || !explicitlyUnverified)) {
1957
+ unsupported.push({
1958
+ categories: segmentCategories,
1959
+ preview: compact(segment, 240),
1960
+ });
1961
+ }
1962
+ }
1963
+ return {
1964
+ categories: unique(categories),
1965
+ unsupported,
1966
+ explicitlyUnverified:
1967
+ categories.length > 0 &&
1968
+ unsupported.length === 0 &&
1969
+ segments.some((segment) => sourceFreeClaimSegmentHasExplicitUnverifiedFraming(segment)),
1970
+ };
1971
+ }
1972
+
1973
+ export function evaluateSourceFreeResponseClaims({
1974
+ goal = "",
1975
+ candidateResult = "",
1976
+ evidenceLedger = {},
1977
+ } = {}) {
1978
+ if (!isResponseOnlyEvidenceScope(goal)) {
1979
+ return {
1980
+ checked: false,
1981
+ ok: true,
1982
+ reason: "Not a response-only evidence scope.",
1983
+ categories: [],
1984
+ hasEvidence: false,
1985
+ explicitlyUnverified: false,
1986
+ };
1987
+ }
1988
+ const hasEvidence =
1989
+ responseOnlyScopeHasFreshEvidenceManifest(goal) ||
1990
+ sourceFreeResponseHasEvidence(evidenceLedger);
1991
+ const claimAssessment = sourceFreeExternalClaimAssessment(candidateResult);
1992
+ const categories = claimAssessment.categories;
1993
+ const unsupportedClaims = claimAssessment.unsupported;
1994
+ const explicitlyUnverified = claimAssessment.explicitlyUnverified;
1995
+ const ok = hasEvidence || categories.length === 0 || unsupportedClaims.length === 0;
1996
+ return {
1997
+ checked: true,
1998
+ ok,
1999
+ reason: ok
2000
+ ? hasEvidence
2001
+ ? "Current scoped evidence is available for response-only factual claims."
2002
+ : explicitlyUnverified
2003
+ ? "Every source-free external claim is locally framed as unverified hypothesis or unverifiable."
2004
+ : "No source-grounded external claim was detected."
2005
+ : `Source-free response-only output claimed external facts without current evidence: ${categories.join(", ")}.`,
2006
+ categories,
2007
+ unsupportedClaims,
2008
+ hasEvidence,
2009
+ explicitlyUnverified,
2010
+ };
2011
+ }
2012
+
1828
2013
  export function scopedChatopsEvidenceGoal(goal = "", taskProfile = "") {
1829
2014
  const payload = parseAgintiEvidenceScope(goal);
1830
2015
  if (!payload) {