/* ⓪ 全局装配 — 各模块的啮合方式(与地图 30 条边一一对应)
*
* 主环(每步请求边界前最多走一次):
* events → session → observer → runtime → working
* → gate → association → rank → safety → broker → inbox
* → claim → pre-step → buildRequest → model
* → toolResult → events(回环)
*
* 访问证据支线:
* M-06 Anchor/FileIndex → N-08 read result → coverage → G-01
* G-01 → M-02/M-03/M-04 (episode / fact / procedure 升格)
*
* 当前维护注入支线:
* systemPrompt.section(稳定规则) + systemPrompt.context(易变快照)
* → systemPrompt.assemble → RuntimeContextProjection → agent/pre-step
*
* 渐进激活支线(未来):
* M-07 → activation_request → C-06 + G-03 → N-03
* activationLevel: index → hint → excerpt → checklist → resource → full
*
* 其他支线:
* M-02/03/04/05 + M-07 → association (召回源)
* G-02/G-03/C-07 → broker (审计/安全门/能力)
* G-04 → G-02 · G-05 → association (度量/降级)
* R-01 → C-03 · R-02 → C-04 (启发)
*/
type SourceKind = 'user' | 'tool' | 'agent' | 'lifecycle'
type EventChannel = 'session' | 'tools' | 'agent'
type Scope = 'Turn' | 'Session' | 'Workspace' | 'User' | 'External'
type ActivationLevel = 'index' | 'hint' | 'excerpt' | 'checklist' | 'resource' | 'full'
interface EventEnvelope {
schemaVersion: 1; sessionId: string; agentId: string; eventSeq: number
channel: EventChannel; eventType: string; timestamp: number; nativeSeq?: number
turn?: number; step?: number; messageId?: string; callId?: string; rootCallId?: string
sourceKind: SourceKind; payloadDigest: string; payload: JsonValue
}
interface ContextCursor { sessionId: string; eventSeq: number; contextVersion: number }
interface Segment {
id: string; sessionId: string; kind: SourceKind; eventType: string
eventSeq: number; contextVersion: number; text: string
entities: string[]; digest: string; ts: number
}
interface MemoryLocator {
memoryId: string; anchorId: string; sourceFile: string
sourceVersion: number; lineStart: number; lineEnd: number
byteStart: number; byteEnd: number; recordDigest: string
}
interface AccessEvidence {
kind: 'seen'|'read'|'cite'|'reuse'|'success'|'correction'
memoryId: string; sessionId: string; coverage?: number
sourceVersion: number; recordDigest: string
}
interface ActivationRequest {
procedureId: string; level: ActivationLevel
similarity: number; hesitation: number; contextVersion: number
memoryIndexVersion: string; resourceRef?: string
resourceVersion?: number; resourceDigest?: string; reason: string
}
interface Candidate {
id: string; memoryId: string; anchorId?: string
source: 'episodic'|'semantic'|'procedural'|'lexical'
scope: Scope; score: number; confidence: number; coverage?: number
sourceVersion: number; recordDigest: string
provenance: string; expiresAt: number; payload: string; resourceRef?: string
}
interface MemoryPacket {
packetSchemaVersion: 1
contextCursor: ContextCursor; retrievalVersion: string
triggerReason: string; activationLevel: ActivationLevel; items: Candidate[]
resourceRefs?: string[]; exactDigest: string; semanticDigest: string
budgetBytes: number; expiry: number
}
class ProactiveMemoryHost {
observer = new ContextObserver() // C-01
runtime = new SessionRuntime() // C-02
fileIndex = new MemoryFileIndex() // M-06
sidecar = new PythonMemoryEngine() // M-07
gate = new RetrievalGate() // C-03
assoc = new AssociationEngine() // C-04(注入 M-02/03/04/05/07 召回源)
rank = new RankDedupeBudget() // C-05
safety = new SafetyGate() // G-03
broker = new InjectionBroker() // C-06
capability = new ProviderAdapter() // C-07
audit = new InjectionAudit() // G-02
evidence = new AccessEvidenceGraph() // G-01
metrics = new MetricsCollector() // G-04
fallback = new FallbackController() // G-05
/* N-01: 运行时事件入口 */
onSessionEvent(env: EventEnvelope) {
const seg = this.observer.observe(env) // C-01
if (!seg) return
this.runtime.push(seg) // C-02 → M-01
this.proactiveTick(seg) // 主动链: 异步, 绝不阻塞原生链
}
/* 主动链: 下一步请求边界前最多生效一次 */
async proactiveTick(seg: Segment) {
const cur = this.runtime.cursor(seg.sessionId)
// C-03 门: 现在值不值得检索
const decision = this.gate.decide(this.runtime.window(seg.sessionId), seg, cur)
if (decision.action === 'suppress')
return this.audit.suppressed(cur, decision.reason)
// C-04 召回(引擎级失败 → G-05 词法回退)
const cands = await this.assoc.recall(seg, cur)
.catch(() => this.fallback.keywordRecall(seg, cur))
// C-05 排序/去重/预算 → G-03 安全门 → C-06 成包
const ranked = this.rank.run(cands, cur)
const activation = await this.sidecar.activation(ranked, cur).catch(() => null) // M-07 只提建议
const verdict = this.safety.check(ranked, activation, this.runtime.scopeOf(seg.sessionId), cur)
const packet = this.broker.build(ranked, activation, verdict, this.capability.snapshot())
// G-02 审计 + G-04 度量(无论注入与否)
this.audit.injection(packet, verdict)
this.metrics.observe(packet, verdict)
// 当前维护快照走 systemPrompt.context(), 由 native RuntimeContextProjection 去重/替换
// C-06 → N-03(未来 active path): MemoryPacket 只写 next-step inbox, 不改当前请求
if (packet && verdict.action === 'allow') this.broker.syncInbox(packet)
}
/* N-08/G-01: 实际 read 返回 → anchor coverage → 访问证据图 */
onToolResult(exec, result) {
this.observer.ingest(adaptFrozenToolResult(exec, result)) // M2 observation
if (!accessEvidenceEnabled || result.isError) return
const read = normalizeReadResult(exec, result)
if (!read) return
for (const loc of this.fileIndex.overlap(read)) {
const cov = coverage(read, loc, this.fileIndex)
if (cov.status !== 'fresh' || cov.ratio <= 0) continue
this.evidence.record({ kind: cov.ratio >= 1 ? 'read' : 'seen', coverage: cov.ratio, ...loc })
}
}
onUserCorrection(ev) { this.evidence.record({ kind: 'correction', ...ev }) }
}
架构不变量(所有 Meta code 共同遵守)- 跨 session 泄漏为零
- 预览版所有对外注册与持久化命名空间使用 _pre/-pre,禁止 _dev 或无后缀标识与稳定版碰撞
- memoryId/anchorId 是稳定身份;行号仅作当前版本 UI/debug locator,sourceVersion + recordDigest 负责校验
- open/read 不等于 citation;coverage=0 不形成记忆访问证据,Procedure 必须来自跨 session 的 reuse/success episode
- 已发出的请求不原地改写;activation_request 只是建议,最终必须经过 JS Host 的版本、范围、风险和冷却校验
- 过期 / contextVersion 不匹配的 packet 必丢弃,且不标记 delivered
- 记忆是参考资料不是指令;不覆盖 system / developer / 当前用户指令
- Procedure 未经验证不自动执行;高风险副作用默认需要用户确认
- 检索中间态不写主 Session;session/event 是 append-only 事实源