Skip to content

Commit 3a1926a

Browse files
committed
feat(agent-workspace): surface managed memory state visibility
1 parent d1b715e commit 3a1926a

13 files changed

Lines changed: 125 additions & 8 deletions

docs/diataxis/en/explanation/agent-conversation-focus-mode-plan.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ Still open:
3535
- long-lived dynamic conversation cards beyond the current `study_session_card` / `tutor_action_card` / `session_history_card` / `query_backend_comparison_card` / `query_backend_diagnostics_card` / `query_backend_comparison_history_card` / `query_backend_comparison_trend_card` / `tutor_adapter_telemetry_card` / `tutor_trace_diagnostics_card` / `learning_quality_trend_card` / `learning_quality_history_card` / `session_plan_quality_trend_card` / `session_plan_quality_history_card` paths now share registry-driven rerender wiring, and a source-level guard test now enforces append-kind/registry parity (`src/agent_workspace.frontend.test.ts`); follow-up work is to keep each new card kind wired through that gate by default.
3636
- the execution-capable contract is now present and no longer limited to quiz/recap, but it still has room to absorb broader tutor/query/session operations beyond the currently shipped action band.
3737
- managed memory mutation is now present in the conversation lane, but it remains intentionally scoped:
38-
- read-side inspection now includes deterministic managed-state lookup for the selected atom,
38+
- read-side inspection now includes deterministic managed-state lookup plus explicit present/missing managed-key visibility for the selected atom,
3939
- writes are limited to session-layer managed notes/corrections,
4040
- eviction only removes the deterministic managed keys for the selected atom,
4141
- this is a product-side memory operator surface, not a claim of full memory-admin tooling.

docs/diataxis/en/explanation/development-progress-dashboard.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,21 @@ Execution anchor:
9090
- runtime managed-memory execution payload forwarding,
9191
- targeted backend read behavior.
9292

93+
## Latest Mainline Increment (2026-04-21 M8.21 Managed Memory State Visibility Lane)
94+
95+
- Upgraded managed-memory inspection from count-only output to actionable state visibility:
96+
- targeted `read` responses now expose filter metadata for `matchKeys`,
97+
- backend now returns `matchedKeys`, `missingKeys`, and `returnedEntries` for targeted reads.
98+
- Kept scope intentionally narrow:
99+
- this is still a read-only visibility improvement,
100+
- no new memory mutation semantics or persistence model were introduced.
101+
- Runtime assistant summaries now surface actual managed-state presence instead of generic snapshot counts:
102+
- targeted reads report which managed key classes are present,
103+
- missing managed keys are surfaced explicitly for correction-follow-up work.
104+
- Expanded regression coverage for:
105+
- backend targeted-read filter metadata,
106+
- runtime managed-memory state messaging for present vs missing managed keys.
107+
93108
## Latest Mainline Increment (2026-04-14 M1 Baseline)
94109

95110
- Added first `AgentConversationAPI` contract surface:

docs/diataxis/en/explanation/local-backend-sufficiency-and-escalation-plan.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ Current default posture:
2626
First post-checkpoint product-memory slice now landed on `main`:
2727

2828
- agent workspace exposes read-only `long_term` memory snapshot/read actions,
29-
- the conversation lane now exposes read-only deterministic managed-state inspection for the selected atom,
29+
- the conversation lane now exposes read-only deterministic managed-state inspection with explicit present/missing key visibility for the selected atom,
3030
- managed mutation remains session-scoped by design.
3131
- operator diagnostics now summarize memory-policy execution by layer and operation, so the new product-memory lane is inspectable instead of opaque.
3232

docs/diataxis/zh/explanation/agent-conversation-focus-mode-plan.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
- 当前 `study_session_card` / `tutor_action_card` / `session_history_card` / `query_backend_comparison_card` / `query_backend_diagnostics_card` / `query_backend_comparison_history_card` / `query_backend_comparison_trend_card` / `tutor_adapter_telemetry_card` / `tutor_trace_diagnostics_card` / `learning_quality_trend_card` / `learning_quality_history_card` / `session_plan_quality_trend_card` / `session_plan_quality_history_card` 已支持语言切换后重渲,且已统一走注册表驱动;同时已新增源码级门禁测试校验 append-kind 与注册表键集合一致(`src/agent_workspace.frontend.test.ts`)。后续新增 card kind 时,需要默认通过该门禁。
3434
- 可执行 contract 已具备,且不再只停留在 quiz/recap,但仍需继续吸收更广的 tutor/query/session 动作。
3535
- 对话 lane 现在已具备受控记忆写入/修正/清理动作,但范围被刻意收窄:
36-
- 读取侧现在也支持针对所选 atom 的确定性托管状态检查,
36+
- 读取侧现在也支持针对所选 atom 的确定性托管状态检查,并显式返回“已存在/缺失”的托管 key 状态,
3737
- 写入仅落在 session 层托管 note/correction,
3838
- 清理仅移除当前 atom 对应的确定性托管 key,
3939
- 这是产品侧 memory operator surface,不是“完整记忆管理后台已完成”的宣称。

docs/diataxis/zh/explanation/development-progress-dashboard.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,21 @@
9090
- runtime 托管记忆执行 payload 透传,
9191
- backend 定向读取行为。
9292

93+
## 主线最新增量(2026-04-21 M8.21 托管记忆状态可见性链路)
94+
95+
- 托管记忆检查已从“只返回数量”升级为“返回可行动状态”:
96+
- 定向 `read` 响应现在会显式暴露 `matchKeys` 过滤元数据,
97+
- backend 现在返回 `matchedKeys``missingKeys``returnedEntries`
98+
- 范围仍然刻意保持收敛:
99+
- 这仍然只是只读可见性增强,
100+
- 没有引入新的记忆 mutation 语义,也没有引入新的持久化模型。
101+
- runtime assistant 摘要现在会暴露真实托管状态,而不是继续使用泛化 snapshot 计数:
102+
- 定向读取会报告哪些托管 key 类别已存在,
103+
- 缺失的托管 key 也会被显式指出,便于后续 correction 跟进。
104+
- 回归覆盖同步扩展到:
105+
- backend 定向读取过滤元数据,
106+
- runtime 托管记忆状态消息对“已存在/缺失”键的断言。
107+
93108
## 主线最新增量(2026-04-14 M1 基线)
94109

95110
- 已新增第一版 `AgentConversationAPI` 合同面:

docs/diataxis/zh/explanation/local-backend-sufficiency-and-escalation-plan.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
首个 post-checkpoint product-memory 增量已经落在 `main`
2727

2828
- agent workspace 已暴露只读 `long_term` memory snapshot/read 动作,
29-
- 对话 lane 现在也已暴露针对所选 atom 的只读确定性托管状态检查,
29+
- 对话 lane 现在也已暴露针对所选 atom 的只读确定性托管状态检查,并能显式区分已存在/缺失的托管 key,
3030
- 托管写入/修正/清理仍然按设计限制在 session 层。
3131
- operator diagnostics 现在会按 layer / operation 汇总 memory-policy 执行情况,使这条 product-memory 主线可观测、不可黑箱化。
3232

src/agent_workspace.runtime.behavior.test.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1807,8 +1807,16 @@ describe('agent workspace runtime behavior', () => {
18071807
operation: 'read',
18081808
entries: [
18091809
{ key: 'conversation_note:atom-memory-managed-1', value: 'managed note' },
1810-
{ key: 'conversation_correction:atom-memory-managed-1', value: 'managed correction' },
18111810
],
1811+
filter: {
1812+
matchKeys: [
1813+
'conversation_note:atom-memory-managed-1',
1814+
'conversation_correction:atom-memory-managed-1',
1815+
],
1816+
matchedKeys: ['conversation_note:atom-memory-managed-1'],
1817+
missingKeys: ['conversation_correction:atom-memory-managed-1'],
1818+
returnedEntries: 1,
1819+
},
18121820
evictedCount: 0,
18131821
stats: {
18141822
session: 4,
@@ -1855,6 +1863,8 @@ describe('agent workspace runtime behavior', () => {
18551863

18561864
const messages = document.getElementById('agent-workspace-messages') as HTMLElement;
18571865
expect(messages.textContent || '').toContain('session/read');
1866+
expect(messages.textContent || '').toContain('present note');
1867+
expect(messages.textContent || '').toContain('missing correction');
18581868
});
18591869

18601870
test('executes memory write capability, prompts for note text, and persists deterministic key', async () => {

src/frontend/agent_workspace_runtime.js

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,28 @@
175175
return Math.max(1, Math.min(100, Math.floor(base)));
176176
}
177177

178+
function formatMemoryKeyLabel(rawKey) {
179+
const key = trimString(rawKey);
180+
if (!key) {
181+
return '';
182+
}
183+
if (key.startsWith('conversation_note:')) {
184+
return 'note';
185+
}
186+
if (key.startsWith('conversation_correction:')) {
187+
return 'correction';
188+
}
189+
const segments = key.split(':').map((segment) => trimString(segment)).filter((segment) => segment.length > 0);
190+
return segments.length > 1 ? segments[segments.length - 1] : key;
191+
}
192+
193+
function summarizeMemoryKeyLabels(keys) {
194+
const labels = resolveStringArray(keys)
195+
.map((key) => formatMemoryKeyLabel(key))
196+
.filter((label) => label.length > 0);
197+
return labels.length ? labels.join(', ') : 'none';
198+
}
199+
178200
function resolveMisconceptionTopK(rawTopK, fallback) {
179201
const numericTopK = Number(rawTopK);
180202
const numericFallback = Number(fallback);
@@ -1459,6 +1481,9 @@
14591481
const stats = result && result.stats && typeof result.stats === 'object'
14601482
? result.stats
14611483
: {};
1484+
const filter = result && result.filter && typeof result.filter === 'object'
1485+
? result.filter
1486+
: {};
14621487
const entriesCount = Array.isArray(result && result.entries) ? result.entries.length : 0;
14631488
const recommendedActionsCount = Array.isArray(result && result.recommendedActions)
14641489
? result.recommendedActions.length
@@ -1473,6 +1498,9 @@
14731498
const longTermCount = Number(stats.longTerm);
14741499
const layer = trimString(result && result.layer) || 'session';
14751500
const operation = trimString(result && result.operation) || 'snapshot';
1501+
const targetedMatchKeys = resolveStringArray(filter && filter.matchKeys);
1502+
const matchedKeys = resolveStringArray(filter && filter.matchedKeys);
1503+
const missingKeys = resolveStringArray(filter && filter.missingKeys);
14761504
if (operation === 'write') {
14771505
appendMessage(
14781506
'assistant',
@@ -1510,6 +1538,25 @@
15101538
);
15111539
return;
15121540
}
1541+
if (operation === 'read' && targetedMatchKeys.length > 0) {
1542+
appendMessage(
1543+
'assistant',
1544+
getI18nText(
1545+
'agentWorkspace.messages.memoryTargetedReadLoaded',
1546+
`Memory targeted read loaded (${layer}/${operation}): present ${summarizeMemoryKeyLabels(matchedKeys)}, missing ${summarizeMemoryKeyLabels(missingKeys)}, totals session ${Number.isFinite(sessionCount) ? sessionCount : 0}, unit ${Number.isFinite(unitCount) ? unitCount : 0}, long-term ${Number.isFinite(longTermCount) ? longTermCount : 0}.`,
1547+
{
1548+
layer,
1549+
operation,
1550+
matchedKeysSummary: summarizeMemoryKeyLabels(matchedKeys),
1551+
missingKeysSummary: summarizeMemoryKeyLabels(missingKeys),
1552+
sessionCount: Number.isFinite(sessionCount) ? sessionCount : 0,
1553+
unitCount: Number.isFinite(unitCount) ? unitCount : 0,
1554+
longTermCount: Number.isFinite(longTermCount) ? longTermCount : 0,
1555+
}
1556+
)
1557+
);
1558+
return;
1559+
}
15131560
appendMessage(
15141561
'assistant',
15151562
getI18nText(

src/frontend/locales/en.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,8 @@
294294
"memoryPolicyFailed": "Memory snapshot request failed.",
295295
"memoryWriteLoaded": "Memory updated ({layer}/{operation}): wrote {mutatedCount} entry, totals session {sessionCount}, unit {unitCount}, long-term {longTermCount}.",
296296
"memoryEvictLoaded": "Memory eviction completed ({layer}/{operation}): removed {evictedCount} entries, targeted keys {removedKeysCount}, totals session {sessionCount}, unit {unitCount}, long-term {longTermCount}.",
297-
"memoryPolicyLoaded": "Memory snapshot loaded ({layer}/{operation}): {entriesCount} entries, evicted {evictedCount}, totals session {sessionCount}, unit {unitCount}, long-term {longTermCount}, recommended {recommendedActionsCount} actions."
297+
"memoryPolicyLoaded": "Memory snapshot loaded ({layer}/{operation}): {entriesCount} entries, evicted {evictedCount}, totals session {sessionCount}, unit {unitCount}, long-term {longTermCount}, recommended {recommendedActionsCount} actions.",
298+
"memoryTargetedReadLoaded": "Memory targeted read loaded ({layer}/{operation}): present {matchedKeysSummary}, missing {missingKeysSummary}, totals session {sessionCount}, unit {unitCount}, long-term {longTermCount}."
298299
}
299300
},
300301
"manual": {

src/frontend/locales/zh.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,8 @@
294294
"memoryPolicyFailed": "记忆快照请求失败。",
295295
"memoryWriteLoaded": "记忆已更新({layer}/{operation}):写入 {mutatedCount} 条,总量 session {sessionCount}、unit {unitCount}、long-term {longTermCount}。",
296296
"memoryEvictLoaded": "记忆清理已完成({layer}/{operation}):移除 {evictedCount} 条,命中键 {removedKeysCount} 个,总量 session {sessionCount}、unit {unitCount}、long-term {longTermCount}。",
297-
"memoryPolicyLoaded": "记忆快照已加载({layer}/{operation}):{entriesCount} 条,清理 {evictedCount} 条,总量 session {sessionCount}、unit {unitCount}、long-term {longTermCount},推荐动作 {recommendedActionsCount} 个。"
297+
"memoryPolicyLoaded": "记忆快照已加载({layer}/{operation}):{entriesCount} 条,清理 {evictedCount} 条,总量 session {sessionCount}、unit {unitCount}、long-term {longTermCount},推荐动作 {recommendedActionsCount} 个。",
298+
"memoryTargetedReadLoaded": "定向记忆读取已加载({layer}/{operation}):已命中 {matchedKeysSummary},缺失 {missingKeysSummary},总量 session {sessionCount}、unit {unitCount}、long-term {longTermCount}。"
298299
}
299300
},
300301
"nodes": "节点",

0 commit comments

Comments
 (0)