Skip to content

Commit 909f2e5

Browse files
committed
feat(agent-workspace): add unit memory snapshot capability lane
1 parent 47b0910 commit 909f2e5

10 files changed

Lines changed: 183 additions & 1 deletion

docs/brainstorms/2026-04-14-mainline-reality-reconciliation-and-next-direction-requirements.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,16 @@ npm run verify:agent-workspace:tauri
255255
- frontend payload-builder ingest-guardrail endpoint/body assertions,
256256
- runtime endpoint/payload/message assertions for ingest-guardrail execution.
257257

258+
#### M6.11 Progress Note (2026-04-14)
259+
260+
- [Done] typed conversation capability surface expanded with `inspect_unit_memory_snapshot` action.
261+
- [Done] conversation capability emission now includes readonly unit-layer memory snapshot execution (`memoryLayer: unit`, `memoryOperation: snapshot`) through `apply_memory_policy`.
262+
- [Done] contract/runtime planners preserve memory-layer passthrough for unit snapshot diagnostics.
263+
- [Done] regression coverage expanded for:
264+
- unit-memory capability wiring in conversation output,
265+
- frontend payload-builder unit memory-layer assertions,
266+
- runtime endpoint/payload/message assertions for unit memory snapshot execution.
267+
258268
### M7 (Future): Foundation Lane Re-entry (Evidence-First)
259269

260270
Entry criteria:

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,18 @@ Execution anchor:
233233
- frontend ingest-guardrail payload-builder endpoint/body assertions,
234234
- runtime endpoint/payload/message assertions for ingest-guardrail execution and rendering.
235235

236+
## Latest Mainline Increment (2026-04-14 M6.11 Unit Memory Snapshot Capability Lane)
237+
238+
- Added `inspect_unit_memory_snapshot` to the typed conversation action union on mainline.
239+
- Extended `runAgentConversation(...)` capability emission to include readonly unit-layer memory snapshot execution (`memoryLayer: unit`, `memoryOperation: snapshot`) via `apply_memory_policy`.
240+
- Kept contract/runtime planner behavior aligned for unit memory diagnostics payload shaping:
241+
- preserved layer passthrough (`unit`),
242+
- readonly snapshot operation guardrails unchanged.
243+
- Expanded regression coverage for:
244+
- unit-memory capability wiring in conversation output,
245+
- frontend unit memory payload-builder assertions,
246+
- runtime endpoint/payload/message assertions for unit memory snapshot execution.
247+
236248
## Mainline vs Working-Branch Snapshot (2026-04-14)
237249

238250
| Capability Slice | Working Branch (`feat/learning-multi-tutor-adapter`) | Mainline (`origin/main`) | Integration Status |

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,18 @@
233233
- 前端 ingest-guardrail payload-builder 的 endpoint/body 断言,
234234
- runtime ingest-guardrail 路径 endpoint/payload/消息断言。
235235

236+
## 主线最新增量(2026-04-14 M6.11 单元记忆快照能力链路)
237+
238+
- 主线 typed conversation action union 新增 `inspect_unit_memory_snapshot`
239+
- `runAgentConversation(...)` capability 输出新增只读单元层记忆快照动作(`memoryLayer: unit``memoryOperation: snapshot`,经 `apply_memory_policy` 执行)。
240+
- contract/runtime payload 规划保持对齐:
241+
- `unit` 层透传保持一致,
242+
- 只读 snapshot 护栏保持不变。
243+
- 回归覆盖同步扩展:
244+
- conversation 输出中的 unit-memory capability 连线断言,
245+
- 前端 unit memory payload-builder 断言,
246+
- runtime unit memory 路径 endpoint/payload/消息断言。
247+
236248
## 主线 vs 工作分支快照(2026-04-14)
237249

238250
| 能力切片 | 工作分支(`feat/learning-multi-tutor-adapter`| 主线(`origin/main`| 集成状态 |

src/agent_workspace.frontend.test.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,32 @@ describe('agent_workspace frontend operation contract', () => {
328328
});
329329
});
330330

331+
test('apply_memory_policy builds readonly unit memory snapshot payload', () => {
332+
const requestPlan = buildKnowledgeOperationRequestPayload({
333+
request: {
334+
userId: 'learner-a',
335+
atomId: 'atom-2',
336+
memoryLayer: 'unit',
337+
memoryOperation: 'snapshot',
338+
},
339+
execution: {
340+
operationId: 'apply_memory_policy',
341+
},
342+
});
343+
344+
expect(requestPlan).toEqual({
345+
operationId: 'apply_memory_policy',
346+
endpoint: '/api/knowledge/memory/policy',
347+
method: 'POST',
348+
resultPresentation: 'memory_policy_card',
349+
body: {
350+
userId: 'learner-a',
351+
layer: 'unit',
352+
operation: 'snapshot',
353+
},
354+
});
355+
});
356+
331357
test('apply_memory_policy rejects mutating memory operations from capability lane', () => {
332358
expect(() =>
333359
buildKnowledgeOperationRequestPayload({

src/agent_workspace.runtime.behavior.test.ts

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -978,6 +978,98 @@ describe('agent workspace runtime behavior', () => {
978978
expect(messages.textContent || '').toContain('Memory snapshot loaded');
979979
});
980980

981+
test('executes unit memory snapshot capability and forwards unit layer', async () => {
982+
const fetchMock = jest
983+
.fn()
984+
.mockResolvedValueOnce({
985+
ok: true,
986+
status: 200,
987+
json: async () => ({
988+
success: true,
989+
result: {
990+
userId: 'agent_user_default',
991+
message: 'Found 1 local knowledge point(s).',
992+
knowledgePoints: [
993+
{
994+
atomId: 'atom-memory-unit-1',
995+
title: 'Unit Memory Candidate',
996+
snippet: 'Inspect unit memory snapshot.',
997+
score: 0.71,
998+
capabilities: [
999+
{
1000+
actionId: 'inspect_unit_memory_snapshot',
1001+
label: 'Unit Memory Snapshot',
1002+
request: {
1003+
userId: 'agent_user_default',
1004+
atomId: 'atom-memory-unit-1',
1005+
memoryLayer: 'unit',
1006+
memoryOperation: 'snapshot',
1007+
memoryLimit: 10,
1008+
},
1009+
execution: {
1010+
kind: 'knowledge_operation',
1011+
operationId: 'apply_memory_policy',
1012+
resultPresentation: 'memory_policy_card',
1013+
},
1014+
},
1015+
],
1016+
},
1017+
],
1018+
},
1019+
}),
1020+
})
1021+
.mockResolvedValueOnce({
1022+
ok: true,
1023+
status: 200,
1024+
json: async () => ({
1025+
success: true,
1026+
result: {
1027+
layer: 'unit',
1028+
operation: 'snapshot',
1029+
entries: [
1030+
{ key: 'u1', value: 'unit memory 1' },
1031+
],
1032+
evictedCount: 0,
1033+
stats: {
1034+
session: 3,
1035+
unit: 4,
1036+
longTerm: 2,
1037+
},
1038+
},
1039+
}),
1040+
});
1041+
(global as unknown as Record<string, unknown>).fetch = fetchMock;
1042+
1043+
const runtime = runtimeModule.createAgentWorkspaceRuntime({ defaultUserId: 'agent_user_default' });
1044+
runtime.init();
1045+
1046+
const input = document.getElementById('agent-workspace-input') as HTMLTextAreaElement;
1047+
const form = document.getElementById('agent-workspace-form') as HTMLFormElement;
1048+
input.value = 'show unit memory snapshot';
1049+
form.dispatchEvent(new dom!.window.Event('submit', { bubbles: true, cancelable: true }));
1050+
await flushAsync();
1051+
1052+
const actionButton = document.querySelector('.agent-workspace-action-button') as HTMLButtonElement;
1053+
expect(actionButton).not.toBeNull();
1054+
actionButton.click();
1055+
await flushAsync();
1056+
1057+
expect(fetchMock).toHaveBeenNthCalledWith(
1058+
2,
1059+
'/api/knowledge/memory/policy',
1060+
expect.objectContaining({ method: 'POST' })
1061+
);
1062+
const payload = JSON.parse(String(fetchMock.mock.calls[1]?.[1]?.body || '{}')) as {
1063+
layer?: string;
1064+
operation?: string;
1065+
};
1066+
expect(payload.layer).toBe('unit');
1067+
expect(payload.operation).toBe('snapshot');
1068+
1069+
const messages = document.getElementById('agent-workspace-messages') as HTMLElement;
1070+
expect(messages.textContent || '').toContain('unit/snapshot');
1071+
});
1072+
9811073
test('executes memory retrain-plan capability and preserves readonly operation', async () => {
9821074
const fetchMock = jest
9831075
.fn()

src/frontend/locales/en.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,7 @@
246246
"inspectMasteryMisconceptions": "Mastery Misconceptions",
247247
"inspectLearningQualitySnapshot": "Learning Quality",
248248
"inspectMemorySnapshot": "Memory Snapshot",
249+
"inspectUnitMemorySnapshot": "Unit Memory Snapshot",
249250
"inspectMemoryRetrainPlan": "Memory Retrain Plan",
250251
"inspectMemoryRead": "Memory Read",
251252
"closeLearningPath": "Close Path",

src/frontend/locales/zh.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,7 @@
246246
"inspectMasteryMisconceptions": "误解洞察",
247247
"inspectLearningQualitySnapshot": "学习质量",
248248
"inspectMemorySnapshot": "记忆快照",
249+
"inspectUnitMemorySnapshot": "单元记忆快照",
249250
"inspectMemoryRetrainPlan": "记忆复训计划",
250251
"inspectMemoryRead": "记忆读取",
251252
"closeLearningPath": "关闭路径",

src/learning/KnowledgeLearningPlatform.test.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ describe('KnowledgeLearningPlatform', () => {
131131
expect(result.trace.retrievalModes).toContain('keyword');
132132

133133
const firstPoint = result.knowledgePoints[0];
134-
expect(firstPoint.capabilities.length).toBeGreaterThanOrEqual(17);
134+
expect(firstPoint.capabilities.length).toBeGreaterThanOrEqual(18);
135135
const actionIds = firstPoint.capabilities.map((capability) => capability.actionId);
136136
expect(actionIds).toEqual(
137137
expect.arrayContaining([
@@ -150,6 +150,7 @@ describe('KnowledgeLearningPlatform', () => {
150150
'inspect_mastery_misconceptions',
151151
'inspect_learning_quality_snapshot',
152152
'inspect_memory_snapshot',
153+
'inspect_unit_memory_snapshot',
153154
'inspect_memory_retrain_plan',
154155
'inspect_memory_read',
155156
])
@@ -187,6 +188,11 @@ describe('KnowledgeLearningPlatform', () => {
187188
expect(memoryCapability?.request.memoryOperation).toBe('snapshot');
188189
expect(memoryCapability?.execution.operationId).toBe('apply_memory_policy');
189190
expect(memoryCapability?.execution.resultPresentation).toBe('memory_policy_card');
191+
const unitMemoryCapability = firstPoint.capabilities.find((capability) => capability.actionId === 'inspect_unit_memory_snapshot');
192+
expect(unitMemoryCapability?.request.memoryLayer).toBe('unit');
193+
expect(unitMemoryCapability?.request.memoryOperation).toBe('snapshot');
194+
expect(unitMemoryCapability?.execution.operationId).toBe('apply_memory_policy');
195+
expect(unitMemoryCapability?.execution.resultPresentation).toBe('memory_policy_card');
190196
const retrainMemoryCapability = firstPoint.capabilities.find((capability) => capability.actionId === 'inspect_memory_retrain_plan');
191197
expect(retrainMemoryCapability?.request.memoryLayer).toBe('session');
192198
expect(retrainMemoryCapability?.request.memoryOperation).toBe('retrain_plan');

src/learning/KnowledgeLearningPlatform.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -989,6 +989,27 @@ export class KnowledgeLearningPlatform implements KnowledgeLearningPlatformAPI {
989989
resultPresentation: 'memory_policy_card',
990990
},
991991
},
992+
{
993+
capabilityId: `cap_unit_memory_snapshot_${item.atom.id}`,
994+
actionId: 'inspect_unit_memory_snapshot',
995+
label: 'Unit Memory Snapshot',
996+
labelKey: 'agentWorkspace.actions.inspectUnitMemorySnapshot',
997+
request: {
998+
...focusRequest,
999+
memoryLayer: 'unit',
1000+
memoryOperation: 'snapshot',
1001+
memoryLimit: 10,
1002+
},
1003+
failure: {
1004+
messageKey: 'agentWorkspace.messages.memoryPolicyFailed',
1005+
fallbackMessage: 'Unable to inspect unit memory snapshot for "{title}".',
1006+
},
1007+
execution: {
1008+
kind: 'knowledge_operation',
1009+
operationId: 'apply_memory_policy',
1010+
resultPresentation: 'memory_policy_card',
1011+
},
1012+
},
9921013
{
9931014
capabilityId: `cap_memory_retrain_plan_${item.atom.id}`,
9941015
actionId: 'inspect_memory_retrain_plan',

src/learning/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -558,6 +558,7 @@ export type AgentConversationActionId =
558558
| 'inspect_mastery_misconceptions'
559559
| 'inspect_learning_quality_snapshot'
560560
| 'inspect_memory_snapshot'
561+
| 'inspect_unit_memory_snapshot'
561562
| 'inspect_memory_retrain_plan'
562563
| 'inspect_memory_read';
563564

0 commit comments

Comments
 (0)