( state: PiM0M1State, db: ContextDatabase, projectDocs = readProjectDocsForPiM0(state).renderedBlock, decayPressureMultiplier = 1, // Atomic-snapshot override: when materializeM0Pi reads markers + memories in // one transaction, it passes the SAME memory set here so the rendered m[0] // can't include a memory whose id is above the persisted maxMemoryId watermark // (which would duplicate it across the m[0]/m[1] split). Mirrors OpenCode, // where renderM0 takes memories as a parameter rather than re-reading. memoriesOverride?: Memory[], compartmentsOverride?: PiCompartment[], userProfileOverride?: UserMemory[], workspaceOverride?: WorkspaceRenderContext, )
| 1003 | } |
| 1004 | |
| 1005 | export function renderM0Pi( |
| 1006 | state: PiM0M1State, |
| 1007 | db: ContextDatabase, |
| 1008 | projectDocs = readProjectDocsForPiM0(state).renderedBlock, |
| 1009 | decayPressureMultiplier = 1, |
| 1010 | // Atomic-snapshot override: when materializeM0Pi reads markers + memories in |
| 1011 | // one transaction, it passes the SAME memory set here so the rendered m[0] |
| 1012 | // can't include a memory whose id is above the persisted maxMemoryId watermark |
| 1013 | // (which would duplicate it across the m[0]/m[1] split). Mirrors OpenCode, |
| 1014 | // where renderM0 takes memories as a parameter rather than re-reading. |
| 1015 | memoriesOverride?: Memory[], |
| 1016 | compartmentsOverride?: PiCompartment[], |
| 1017 | userProfileOverride?: UserMemory[], |
| 1018 | workspaceOverride?: WorkspaceRenderContext, |
| 1019 | ): string { |
| 1020 | const memPath = memoryProjectPath(state); |
| 1021 | const workspace = |
| 1022 | workspaceOverride ?? resolveWorkspaceRenderContextPi(state, db); |
| 1023 | const allMemories = |
| 1024 | memoriesOverride ?? |
| 1025 | (memPath |
| 1026 | ? workspace.isWorkspaced |
| 1027 | ? getMemoriesByProjects( |
| 1028 | db, |
| 1029 | workspace.expandedIdentities, |
| 1030 | ["active", "permanent"], |
| 1031 | Date.now(), |
| 1032 | workspace.ownIdentities, |
| 1033 | workspace.shareCategories, |
| 1034 | ) |
| 1035 | : getMemoriesByProject(db, memPath, ["active", "permanent"]) |
| 1036 | : []); |
| 1037 | // Use the V2 trim + render helpers (shared with OpenCode) so both harnesses |
| 1038 | // emit the SAME structured <project-memory><memory id= category= importance=> |
| 1039 | // shape and the same permanent-first / importance-DESC ordering. A divergent |
| 1040 | // shape here would put different bytes on the wire between OpenCode and Pi. |
| 1041 | // Always trim with the default memory-budget fallback (matching OpenCode), |
| 1042 | // not gated on a truthy injectionBudgetTokens — an unset budget must NOT mean |
| 1043 | // "render every memory untrimmed", which would grow m[0] without bound. |
| 1044 | const memoryRenderOptions: MemoryRenderOptions = { |
| 1045 | sourceNameByMemoryId: sourceNamesForPiMemories({ |
| 1046 | memories: allMemories, |
| 1047 | projectPath: memPath, |
| 1048 | workspace, |
| 1049 | }), |
| 1050 | }; |
| 1051 | const memories = |
| 1052 | allMemories.length > 0 |
| 1053 | ? workspace.isWorkspaced |
| 1054 | ? trimWorkspaceMemoriesToBudgetV2( |
| 1055 | state.sessionId, |
| 1056 | allMemories, |
| 1057 | state.injectionBudgetTokens ?? DEFAULT_MEMORY_BUDGET_TOKENS, |
| 1058 | workspace, |
| 1059 | memoryRenderOptions, |
| 1060 | ).renderOrder |
| 1061 | : trimMemoriesToBudgetV2( |
| 1062 | state.sessionId, |
no test coverage detected