@jjh32456/dsh-longlive 1.6.7 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +70 -1508
- package/LICENSE +1 -1
- package/QUICKSTART.md +34 -13
- package/README.en.md +18 -7
- package/README.md +34 -9
- package/SPEC.md +5 -5
- package/cordis.patch.yml +23 -9
- package/docs/ANSWER-GUARD.md +92 -0
- package/docs/ARCHITECTURE.md +4 -3
- package/docs/AUTO-RECALL-WIRING.md +1 -1
- package/docs/AUTO-RECALL.md +15 -9
- package/docs/CONFIG.md +168 -35
- package/docs/ERRORS.md +17 -5
- package/docs/FAQ.md +7 -7
- package/docs/FEATURES.md +96 -28
- package/docs/MIGRATION-1.6.0.md +2 -2
- package/docs/OPERATIONS.md +6 -6
- package/docs/PANEL-INTEGRATION.md +6 -0
- package/docs/PRIVACY.md +88 -0
- package/docs/QUICKSTART.md +5 -5
- package/docs/README.en.md +4 -2
- package/docs/RESIDENT-WATCHER.md +1 -1
- package/docs/SESSION-WATCH.md +6 -6
- package/docs/TROUBLESHOOTING.md +4 -4
- package/package.json +47 -117
- package/runbook.md +2 -2
- package/scripts/backfill-auto-cards.mjs +98 -0
- package/scripts/install-surgery-task.ps1 +5 -2
- package/scripts/lib/session-watch-fixture.mjs +1 -1
- package/scripts/longlive-doctor.mjs +6 -0
- package/scripts/migrate-180.mjs +163 -0
- package/scripts/recall-index-build.mjs +128 -0
- package/scripts/session-watch-drill.mjs +1 -1
- package/scripts/surgery-run.cmd +1 -1
- package/scripts/unattended-surgery.ps1 +5 -2
- package/scripts/verify-card-anchors.mjs +138 -0
- package/src/actions/audit.js +125 -0
- package/src/actions/evidence.js +122 -0
- package/src/actions/fs.js +162 -0
- package/src/activity/index.js +760 -0
- package/src/activity/model.js +351 -0
- package/src/activity/store.js +644 -0
- package/src/activity/thinking.js +176 -0
- package/src/adapters/constants.js +11 -0
- package/src/adapters/engram/data/aliases.json +51 -0
- package/src/adapters/engram/data/policy.json +8 -0
- package/src/adapters/engram/data/routing-rules.json +546 -0
- package/src/adapters/engram/index.js +235 -0
- package/src/adapters/registry.js +315 -0
- package/src/answer/audit.js +193 -0
- package/src/answer/evidence.js +30 -0
- package/src/answer/guard.js +1370 -0
- package/src/answer/index.js +1067 -0
- package/src/answer/query.js +111 -0
- package/src/answer/second-hop.mjs +101 -0
- package/src/auto-truncate.js +1 -1
- package/src/config.js +171 -47
- package/src/deps.js +6 -4
- package/src/features.js +182 -6
- package/src/heartbeat.js +156 -23
- package/src/identity/handover.js +52 -1
- package/src/identity/index.js +13 -5
- package/src/identity/inject-position.js +2 -2
- package/src/index.js +97 -7
- package/src/lifecycle/gap-clocks.js +328 -0
- package/src/lifecycle/index.js +269 -0
- package/src/live.js +22 -2
- package/src/memory/activity-capture.js +229 -0
- package/src/memory/anchors.js +194 -0
- package/src/memory/audit-records.js +222 -0
- package/src/memory/auto-cards.js +401 -0
- package/src/memory/capture.js +442 -0
- package/src/memory/card-anchors.js +203 -0
- package/src/memory/conflicts.js +106 -0
- package/src/memory/decay.js +311 -0
- package/src/memory/distill.js +471 -0
- package/src/memory/event-index.js +917 -0
- package/src/memory/evidence-chain.js +295 -0
- package/src/memory/evidence.js +145 -0
- package/src/memory/filter-runner.js +1 -1
- package/src/memory/filter.js +5 -1
- package/src/memory/forget-v2.js +410 -0
- package/src/memory/forget.js +71 -0
- package/src/memory/frames-activation.js +247 -0
- package/src/memory/fts-query.js +174 -0
- package/src/memory/governance.js +604 -0
- package/src/memory/index.js +1 -0
- package/src/memory/l2-gate.js +169 -0
- package/src/memory/lock.js +121 -0
- package/src/memory/migrate.js +139 -0
- package/src/memory/recall-v2.js +223 -0
- package/src/memory/recall.js +65 -2
- package/src/memory/records.js +425 -0
- package/src/memory/retention.js +128 -0
- package/src/memory/review.js +124 -0
- package/src/memory/trust.js +88 -0
- package/src/memory/version-chain.js +264 -0
- package/src/memory/wal.js +137 -0
- package/src/memory/working-frame.js +516 -0
- package/src/metrics/classify.mjs +5 -3
- package/src/metrics/index.mjs +21 -1
- package/src/metrics/prestep-latency.mjs +300 -0
- package/src/metrics/store.mjs +25 -0
- package/src/policy-profile.js +18 -3
- package/src/recall/anaphora.mjs +87 -0
- package/src/recall/cards.mjs +7 -0
- package/src/recall/embedding.mjs +8 -8
- package/src/recall/entities.mjs +112 -0
- package/src/recall/index.js +235 -19
- package/src/recall/inject-order.mjs +10 -8
- package/src/recall/privacy.mjs +105 -16
- package/src/recall/recall-core.mjs +116 -10
- package/src/recall/shared-visibility.js +302 -0
- package/src/recall/time-normalizer.mjs +196 -0
- package/src/role/activation.js +191 -0
- package/src/role/attention.js +318 -0
- package/src/role/base.js +36 -0
- package/src/role/channels.js +423 -0
- package/src/role/index.js +425 -0
- package/src/role/interfaces.js +115 -0
- package/src/role/personality.js +370 -0
- package/src/role/readers.js +151 -0
- package/src/role/relationships.js +477 -0
- package/src/role/store.js +202 -0
- package/src/role/writeprotect.js +373 -0
- package/src/router/aliases.json +176 -25
- package/src/router/background/agent-runner.mjs +1 -1
- package/src/router/background/anchor.mjs +1 -1
- package/src/router/background/audit.mjs +2 -2
- package/src/router/background/budget-ledger.mjs +2 -2
- package/src/router/background/host-sim.mjs +1 -1
- package/src/router/background/memory.mjs +4 -4
- package/src/router/background/queue.mjs +1 -1
- package/src/router/background/retry.mjs +1 -1
- package/src/router/background/router.mjs +1 -1
- package/src/router/background/stream.mjs +1 -1
- package/src/router/background/tool-policy.mjs +1 -1
- package/src/router/design-headings.json +5 -5
- package/src/router/face/config.mjs +1 -1
- package/src/router/face/identity.mjs +3 -3
- package/src/router/judge.mjs +8 -7
- package/src/router/policies/generic.json +21 -4
- package/src/router/route.js +109 -22
- package/src/router/router.mjs +10 -6
- package/src/router/routing-rules.json +260 -445
- package/src/router/run-sim.mjs +8 -8
- package/src/router/runtime.js +23 -12
- package/src/router/service/board.mjs +95 -15
- package/src/router/service/config.mjs +5 -5
- package/src/router/service/context.mjs +1 -1
- package/src/router/service/hint.mjs +14 -0
- package/src/router/service/queue.mjs +1 -1
- package/src/router/service/service.mjs +27 -11
- package/src/router/shadow.mjs +4 -3
- package/src/surgery-recovery.js +2 -2
- package/src/tool-output.js +2 -2
- package/src/tools.js +39 -7
- package/src/truncate-visible.js +2 -2
- package/src/ui/actions.js +1312 -0
- package/src/ui/panel-api.js +367 -0
- package/src/ui/panel-auth.js +251 -0
- package/src/ui/panel-integrity.js +243 -0
- package/src/ui/routes-readonly.js +695 -0
- package/src/ui/routes.js +3 -0
- package/docs/DEV.md +0 -279
- package/docs/PRESENCE-BUS-WIRING.md +0 -491
- package/docs/PRESENCE-BUS.md +0 -694
- package/docs/SURGERY.md +0 -186
- package/scripts/mark-core-cards.mjs +0 -289
- package/scripts/pack.mjs +0 -392
- package/scripts/router-proposals.mjs +0 -386
- package/src/router/fixtures/M1.json +0 -16
- package/src/router/fixtures/M2.json +0 -16
- package/src/router/fixtures/M3.json +0 -20
- package/src/router/fixtures/M4.json +0 -20
- package/src/router/fixtures/M5.json +0 -45
- package/src/router/fixtures/M6.json +0 -53
- package/src/router/fixtures/M7.json +0 -46
- package/src/router/fixtures/S1.json +0 -20
- package/src/router/fixtures/S2.json +0 -23
- package/src/router/fixtures/S3.json +0 -20
- package/src/router/fixtures/S4.json +0 -34
- package/src/router/fixtures/S5.json +0 -43
- package/src/router/fixtures/S6.json +0 -47
- package/src/router/fixtures/T1.json +0 -23
- package/src/router/fixtures/T2.json +0 -16
- package/src/router/fixtures/T3.json +0 -19
- package/src/router/fixtures/T4.json +0 -16
- package/src/router/fixtures/X1.json +0 -17
- package/src/router/fixtures/X2.json +0 -20
- package/src/router/gen-design-headings.mjs +0 -107
- package/src/router/policies/owner-rulings-2026-11-03.json +0 -62
- package/test/auto-recall.test.mjs +0 -474
- package/test/auto-truncate.test.mjs +0 -931
- package/test/autobind.test.mjs +0 -385
- package/test/bus.test.mjs +0 -1572
- package/test/cli-guard.test.mjs +0 -198
- package/test/conclusion.test.mjs +0 -408
- package/test/config-schema.test.mjs +0 -204
- package/test/d6-output.test.mjs +0 -194
- package/test/d6.test.mjs +0 -395
- package/test/deps.test.mjs +0 -342
- package/test/doctor-setup.test.mjs +0 -297
- package/test/embedding-recall.test.mjs +0 -347
- package/test/feature-flags.test.mjs +0 -568
- package/test/filter-shadow.test.mjs +0 -170
- package/test/fixtures/baseline-0.8.0-registration.json +0 -556
- package/test/fixtures/d6-header-tools.json +0 -1
- package/test/fixtures/lock-child.mjs +0 -67
- package/test/fixtures/pre-step-container.mjs +0 -100
- package/test/fixtures/privacy-memory/topics/pii-unmarked.md +0 -9
- package/test/fixtures/privacy-memory/topics/secret-local-only.md +0 -11
- package/test/fixtures/profile-session-longlive-config.json +0 -20
- package/test/fixtures/proposal-cas-child.mjs +0 -66
- package/test/host-engine-status.test.mjs +0 -307
- package/test/hot-truncate.test.mjs +0 -1349
- package/test/identity-card-marker.test.mjs +0 -307
- package/test/identity-router/run.mjs +0 -721
- package/test/integration-pre-step.test.mjs +0 -553
- package/test/live.test.mjs +0 -1020
- package/test/maintenance-schedule.test.mjs +0 -538
- package/test/memory.test.mjs +0 -2828
- package/test/metrics.test.mjs +0 -478
- package/test/orchestrator.test.mjs +0 -266
- package/test/p1_probe.mjs +0 -97
- package/test/packaging.test.mjs +0 -182
- package/test/portability.test.mjs +0 -343
- package/test/rc4-regressions.test.mjs +0 -562
- package/test/rc5-regressions.test.mjs +0 -197
- package/test/rc6-chinese.test.mjs +0 -394
- package/test/router-1100/apply-off.test.mjs +0 -175
- package/test/router-1100/budget-reconcile.test.mjs +0 -215
- package/test/router-1100/config-context.test.mjs +0 -211
- package/test/router-1100/helpers.mjs +0 -129
- package/test/router-1100/hint-envelope.test.mjs +0 -150
- package/test/router-1100/lock-crossproc.test.mjs +0 -206
- package/test/router-1100/proposal-cas-crossproc.test.mjs +0 -184
- package/test/router-1100/router-apply.test.mjs +0 -372
- package/test/router-1100/router-fixb.test.mjs +0 -602
- package/test/router-1100/router-proposals.test.mjs +0 -405
- package/test/router-1100/router-queue-lease-wal.test.mjs +0 -355
- package/test/router-1100/router-security-cr.test.mjs +0 -333
- package/test/router-1100/service-lifecycle.test.mjs +0 -220
- package/test/router-1100/tools-contract.test.mjs +0 -164
- package/test/router-1100/turn-reflect-tick.test.mjs +0 -89
- package/test/router-1100/v6-contract.test.mjs +0 -158
- package/test/router-1100/v6b-contract.test.mjs +0 -293
- package/test/router-1100/v6c-contract.test.mjs +0 -249
- package/test/router-1100/wal-rotation.test.mjs +0 -375
- package/test/router-1100/wp1-config.test.mjs +0 -170
- package/test/router-1100/wp1-default-off.test.mjs +0 -77
- package/test/router-1100/wp1-model-status.test.mjs +0 -90
- package/test/router-1100/wp1-status.test.mjs +0 -220
- package/test/router-1100/wp1-tools.test.mjs +0 -146
- package/test/router-1100/wp5-cli.test.mjs +0 -240
- package/test/router-1100/wp5-model-auto.test.mjs +0 -418
- package/test/router-1100/wp6-merge.test.mjs +0 -386
- package/test/router-1100/wp6-wake.test.mjs +0 -502
- package/test/router-background/audit.test.mjs +0 -37
- package/test/router-background/budget-ledger.test.mjs +0 -330
- package/test/router-background/cross-process.test.mjs +0 -222
- package/test/router-background/fixtures/cross-child.mjs +0 -85
- package/test/router-background/fixtures/real-shapes.mjs +0 -239
- package/test/router-background/router.test.mjs +0 -1044
- package/test/router-background/stream-retry.test.mjs +0 -358
- package/test/router-background/tool-agent.test.mjs +0 -384
- package/test/router-face/bridge.test.mjs +0 -169
- package/test/router-face/face.test.mjs +0 -316
- package/test/router-failclosed.test.mjs +0 -466
- package/test/router-identity.test.mjs +0 -384
- package/test/router-l1-l2.contract.test.mjs +0 -1075
- package/test/router-memory-tools.test.mjs +0 -262
- package/test/router-policy.test.mjs +0 -225
- package/test/router-runtime.test.mjs +0 -438
- package/test/router-shadow.test.mjs +0 -287
- package/test/router.test.mjs +0 -903
- package/test/run-rc.mjs +0 -299
- package/test/run.mjs +0 -2469
- package/test/safety-defaults.test.mjs +0 -179
- package/test/seeded-load.test.mjs +0 -436
- package/test/session-original.test.mjs +0 -338
- package/test/surgery-recovery.test.mjs +0 -936
- package/test/t7_drive.mjs +0 -151
- package/test/tool-output.test.mjs +0 -393
- package/test/tool-schema.test.mjs +0 -177
- package/test/tools_contract.test.mjs +0 -716
- package/test/truncate-visible.test.mjs +0 -1150
- package/test/ui-flags.test.mjs +0 -477
- package/test/v4-hardening.test.mjs +0 -427
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
// answer/tool.js — `answer_audit`:只读查询生成后审计。
|
|
2
|
+
//
|
|
3
|
+
// 只在 answerGuard.enabled 时注册(tools/list 默认仍是 22 个基线工具),
|
|
4
|
+
// 与 routerTools / d6Tools 一样由 registerTools 追加,绝不改变默认工具面。
|
|
5
|
+
|
|
6
|
+
import { auditFilePath, auditMetrics, readAuditRows } from './audit.js';
|
|
7
|
+
|
|
8
|
+
export const ANSWER_AUDIT_TOOL_NAME = 'answer_audit';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @param {string|null} memoryRoot
|
|
12
|
+
* @returns {object} tool definition consumed by src/tools.js registerTools
|
|
13
|
+
*/
|
|
14
|
+
export function createAnswerTools(memoryRoot) {
|
|
15
|
+
const root = typeof memoryRoot === 'string' && memoryRoot.trim() !== '' ? memoryRoot.trim() : null;
|
|
16
|
+
return [{
|
|
17
|
+
name: ANSWER_AUDIT_TOOL_NAME,
|
|
18
|
+
description:
|
|
19
|
+
'Query the post-generation answer audit written by the 1.6.5 answer guard ' +
|
|
20
|
+
'(`<memoryRoot>/.longlive/answer-audit.jsonl`). One row per audited turn: whether a ' +
|
|
21
|
+
'history question was answered without a turn citation, whether the assistant claimed a ' +
|
|
22
|
+
'tool it never called, numbers/dates absent from context and tool results, and nonexistent ' +
|
|
23
|
+
'turn/card references. READ-ONLY: it never edits the assistant text or the session log. ' +
|
|
24
|
+
'Filters: sessionId, turn, verdict (clean|flagged), code (HISTORY_CLAIM_NO_CITATION, ' +
|
|
25
|
+
'ANSWERED_WITHOUT_EVIDENCE, FAKE_TOOL_CLAIM, UNGROUNDED_NUMBER, FAKE_TURN_REF, FAKE_CARD_REF), ' +
|
|
26
|
+
'since (ISO time). Default returns the newest 20 rows plus byVerdict/byCode counts.',
|
|
27
|
+
parameters: {
|
|
28
|
+
sessionId: {
|
|
29
|
+
type: 'string',
|
|
30
|
+
description: 'Only rows for this session id.',
|
|
31
|
+
},
|
|
32
|
+
turn: {
|
|
33
|
+
type: 'integer',
|
|
34
|
+
description: 'Only rows for this 1-based turn number.',
|
|
35
|
+
},
|
|
36
|
+
verdict: {
|
|
37
|
+
type: 'string',
|
|
38
|
+
enum: ['clean', 'flagged'],
|
|
39
|
+
description: 'Only rows with this verdict.',
|
|
40
|
+
},
|
|
41
|
+
code: {
|
|
42
|
+
type: 'string',
|
|
43
|
+
description: 'Only rows containing this flag code.',
|
|
44
|
+
},
|
|
45
|
+
since: {
|
|
46
|
+
type: 'string',
|
|
47
|
+
description: 'Only rows at or after this ISO timestamp.',
|
|
48
|
+
},
|
|
49
|
+
limit: {
|
|
50
|
+
type: 'integer',
|
|
51
|
+
description: 'Maximum rows returned (default 20, max 500).',
|
|
52
|
+
},
|
|
53
|
+
summary: {
|
|
54
|
+
type: 'boolean',
|
|
55
|
+
description: 'Default true: include byVerdict / byCode / metrics. false returns rows only.',
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
output: {
|
|
59
|
+
schema: { type: 'object', additionalProperties: true },
|
|
60
|
+
render: (_args, value) => (typeof value === 'string' ? value : JSON.stringify(value, null, 2)),
|
|
61
|
+
},
|
|
62
|
+
invoke: (args = {}) => answerAudit(args, { memoryRoot: root }),
|
|
63
|
+
}];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* 工具实现(也直接给测试用)。永远不抛。
|
|
68
|
+
* @param {object} args
|
|
69
|
+
* @param {{memoryRoot?:string|null,limit?:number}} config
|
|
70
|
+
*/
|
|
71
|
+
export function answerAudit(args = {}, config = {}) {
|
|
72
|
+
const memoryRoot = typeof config.memoryRoot === 'string' && config.memoryRoot.trim() !== ''
|
|
73
|
+
? config.memoryRoot.trim()
|
|
74
|
+
: null;
|
|
75
|
+
if (!memoryRoot) {
|
|
76
|
+
return {
|
|
77
|
+
ok: false,
|
|
78
|
+
code: 'E_NO_MEMORY_ROOT',
|
|
79
|
+
error: '没有可用的记忆根目录;审计文件位于 <memoryRoot>/.longlive/answer-audit.jsonl。',
|
|
80
|
+
path: '',
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
const limit = Number.isFinite(Number(args.limit))
|
|
84
|
+
? Math.max(1, Math.min(500, Number(args.limit)))
|
|
85
|
+
: 20;
|
|
86
|
+
const read = readAuditRows(memoryRoot, {
|
|
87
|
+
limit,
|
|
88
|
+
sessionId: typeof args.sessionId === 'string' ? args.sessionId : undefined,
|
|
89
|
+
turn: Number.isFinite(Number(args.turn)) ? Number(args.turn) : undefined,
|
|
90
|
+
verdict: typeof args.verdict === 'string' ? args.verdict : undefined,
|
|
91
|
+
code: typeof args.code === 'string' ? args.code : undefined,
|
|
92
|
+
since: typeof args.since === 'string' ? args.since : undefined,
|
|
93
|
+
});
|
|
94
|
+
const base = {
|
|
95
|
+
ok: read.ok,
|
|
96
|
+
path: read.path || auditFilePath(memoryRoot),
|
|
97
|
+
total: read.total,
|
|
98
|
+
matched: read.matched,
|
|
99
|
+
corrupt: read.corrupt,
|
|
100
|
+
...(read.error ? { note: read.error } : {}),
|
|
101
|
+
rows: read.rows,
|
|
102
|
+
};
|
|
103
|
+
if (args.summary === false) return base;
|
|
104
|
+
return {
|
|
105
|
+
...base,
|
|
106
|
+
byVerdict: read.byVerdict,
|
|
107
|
+
byCode: read.byCode,
|
|
108
|
+
sessions: read.sessions,
|
|
109
|
+
metrics: auditMetrics(read.rows),
|
|
110
|
+
};
|
|
111
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
// src/answer/second-hop.mjs — B4:证据包字段覆盖不足 → 第二跳补读(最多 1 跳)
|
|
2
|
+
//
|
|
3
|
+
// §11.6-B4:证据包「字段覆盖检查」不足 → 补读第二跳(最多 1 跳)。
|
|
4
|
+
// 断言口径:构造缺 when 的证据 → 触发第二跳。本模块不猜内容:第二跳只放宽
|
|
5
|
+
// 证据检索阈值/条数并回读相邻轮,字段仍缺则如实返回 missingFieldsAfter。
|
|
6
|
+
|
|
7
|
+
export const SECOND_HOP_SCHEMA = 'second-hop/v1';
|
|
8
|
+
export const DEFAULT_REQUIRED_FIELDS = Object.freeze(['when']);
|
|
9
|
+
|
|
10
|
+
const WHEN_PATTERNS = [
|
|
11
|
+
/\d{4}[-/年]\d{1,2}[-/月]\d{1,2}/u,
|
|
12
|
+
/\d{4}[-/年]\d{1,2}月?/u,
|
|
13
|
+
/今天|今日|昨天|昨日|前天|明天|后天|上周|本周|上周[一二三四五六日天]|周[一二三四五六日天]/u,
|
|
14
|
+
/\d{1,2}\s*月\s*\d{1,2}\s*日/u,
|
|
15
|
+
/\d{1,2}[::]\d{2}/u,
|
|
16
|
+
/刚才|刚刚|之前|上次|上次那回/u,
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
/** Check which required dimensions the evidence text covers. */
|
|
20
|
+
export function evidenceFieldCoverage(items, { requiredFields = DEFAULT_REQUIRED_FIELDS } = {}) {
|
|
21
|
+
const text = (Array.isArray(items) ? items : [])
|
|
22
|
+
.map((item) => [item?.user, item?.assistant].filter(Boolean).join('\n'))
|
|
23
|
+
.join('\n');
|
|
24
|
+
const present = {
|
|
25
|
+
when: WHEN_PATTERNS.some((pattern) => pattern.test(text)),
|
|
26
|
+
what: text.trim() !== '',
|
|
27
|
+
who: /[\u3400-\u9fffA-Za-z]{2,}/u.test(text),
|
|
28
|
+
};
|
|
29
|
+
const missingFields = (Array.isArray(requiredFields) ? requiredFields : DEFAULT_REQUIRED_FIELDS)
|
|
30
|
+
.filter((field) => present[field] !== true);
|
|
31
|
+
return { requiredFields: [...requiredFields], present, missingFields, covered: missingFields.length === 0 };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function turnNumbers(items) {
|
|
35
|
+
return (Array.isArray(items) ? items : []).map((item) => Number(item?.turn)).filter(Number.isFinite);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* First hop + at most one second hop. `retrieve` is injected (pure/testable seam).
|
|
40
|
+
* Returns the first-hop result extended with `items` (first + added second-hop turns)
|
|
41
|
+
* and a `secondHop` receipt. Never exceeds one extra hop.
|
|
42
|
+
*/
|
|
43
|
+
export function retrieveEvidenceWithSecondHop({
|
|
44
|
+
turns = [],
|
|
45
|
+
question = '',
|
|
46
|
+
currentTurn = 0,
|
|
47
|
+
maxItems = 3,
|
|
48
|
+
minScore = 1.8,
|
|
49
|
+
retrieve,
|
|
50
|
+
requiredFields = DEFAULT_REQUIRED_FIELDS,
|
|
51
|
+
relaxFactor = 0.6,
|
|
52
|
+
maxHop = 1,
|
|
53
|
+
} = {}) {
|
|
54
|
+
if (typeof retrieve !== 'function') throw new Error('E_SECOND_HOP_RETRIEVE: retrieve function required');
|
|
55
|
+
const first = retrieve({ turns, question, currentTurn, maxItems, minScore });
|
|
56
|
+
const coverageFirst = evidenceFieldCoverage(first?.items, { requiredFields });
|
|
57
|
+
if (coverageFirst.covered || maxHop < 1) {
|
|
58
|
+
return {
|
|
59
|
+
...first,
|
|
60
|
+
secondHop: {
|
|
61
|
+
schema: SECOND_HOP_SCHEMA,
|
|
62
|
+
triggered: false,
|
|
63
|
+
reason: coverageFirst.covered ? 'covered' : 'max-hop',
|
|
64
|
+
missingFieldsFirst: coverageFirst.missingFields,
|
|
65
|
+
addedTurns: [],
|
|
66
|
+
missingFieldsAfter: coverageFirst.missingFields,
|
|
67
|
+
hop: 1,
|
|
68
|
+
maxHop,
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
const relaxedMinScore = Math.max(0, Number(minScore) * Number(relaxFactor));
|
|
73
|
+
const second = retrieve({
|
|
74
|
+
turns,
|
|
75
|
+
question,
|
|
76
|
+
currentTurn,
|
|
77
|
+
maxItems: Math.max(1, Number(maxItems) + 2),
|
|
78
|
+
minScore: relaxedMinScore,
|
|
79
|
+
});
|
|
80
|
+
const firstTurns = new Set(turnNumbers(first?.items));
|
|
81
|
+
const added = (Array.isArray(second?.items) ? second.items : []).filter((item) => !firstTurns.has(Number(item?.turn)));
|
|
82
|
+
const combined = [...(Array.isArray(first?.items) ? first.items : []), ...added].slice(0, Math.max(1, Number(maxItems) + 2));
|
|
83
|
+
const coverageAfter = evidenceFieldCoverage(combined, { requiredFields });
|
|
84
|
+
return {
|
|
85
|
+
...first,
|
|
86
|
+
items: combined,
|
|
87
|
+
zeroHit: combined.length === 0,
|
|
88
|
+
secondHop: {
|
|
89
|
+
schema: SECOND_HOP_SCHEMA,
|
|
90
|
+
triggered: true,
|
|
91
|
+
reason: 'field-coverage',
|
|
92
|
+
missingFieldsFirst: coverageFirst.missingFields,
|
|
93
|
+
addedTurns: added.map((item) => Number(item.turn)).filter(Number.isFinite),
|
|
94
|
+
missingFieldsAfter: coverageAfter.missingFields,
|
|
95
|
+
filled: coverageAfter.covered,
|
|
96
|
+
hop: 2,
|
|
97
|
+
maxHop,
|
|
98
|
+
relaxedMinScore,
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
}
|
package/src/auto-truncate.js
CHANGED
|
@@ -55,7 +55,7 @@ import {
|
|
|
55
55
|
// `autoTruncate: {}` cannot cut a session by accident; `mode:'request'` also
|
|
56
56
|
// requires enabled:true, and the runner additionally needs the watchdog.
|
|
57
57
|
export const AUTO_TRUNCATE_DEFAULTS = Object.freeze({
|
|
58
|
-
enabled:
|
|
58
|
+
enabled: true,
|
|
59
59
|
mode: 'dryrun',
|
|
60
60
|
tokensThreshold: 300_000,
|
|
61
61
|
minTurns: 12,
|