( memoryDraft: string, pendingDraft: string, input: ApproveLocalMemoryProposalInput, )
| 468 | } |
| 469 | |
| 470 | export function approveLocalMemoryProposalDraft( |
| 471 | memoryDraft: string, |
| 472 | pendingDraft: string, |
| 473 | input: ApproveLocalMemoryProposalInput, |
| 474 | ): ApproveLocalMemoryProposalResult { |
| 475 | const proposalId = normalizeId(input.proposalId, 'proposal-'); |
| 476 | const entryId = normalizeId(input.entryId, 'mem-'); |
| 477 | if (!proposalId || !entryId) return { ok: false, reason: 'invalid_id' }; |
| 478 | |
| 479 | const proposal = findLocalMemoryEntryFullSection(pendingDraft, proposalId); |
| 480 | if (!proposal) return { ok: false, reason: 'not_found' }; |
| 481 | const status = normalizeEntryStatus(proposal.meta?.status, true); |
| 482 | if (status !== 'draft' && status !== 'review_required') |
| 483 | return { ok: false, reason: 'not_pending' }; |
| 484 | if (!proposal.content.trim()) return { ok: false, reason: 'empty_content' }; |
| 485 | |
| 486 | const approved = appendApprovedLocalMemoryEntryDraft(memoryDraft, { |
| 487 | id: entryId, |
| 488 | title: proposal.title, |
| 489 | content: proposal.content, |
| 490 | source: 'chat_extracted', |
| 491 | scope: normalizeScope(proposal.meta?.scope), |
| 492 | sessionId: proposal.meta?.sessionId, |
| 493 | proposalId, |
| 494 | sourceTurnId: proposal.meta?.sourceTurnId, |
| 495 | confirmedAt: input.confirmedAt, |
| 496 | approvalSurface: input.approvalSurface ?? 'settings_review_queue', |
| 497 | tags: parseTags(proposal.meta?.tags), |
| 498 | }); |
| 499 | if (!approved.ok) { |
| 500 | switch (approved.reason) { |
| 501 | case 'oversize': |
| 502 | case 'invalid_session_id': |
| 503 | return { ok: false, reason: approved.reason }; |
| 504 | default: |
| 505 | return { ok: false, reason: 'empty_content' }; |
| 506 | } |
| 507 | } |
| 508 | |
| 509 | const pendingWithoutProposal = removeLocalMemoryEntrySection(pendingDraft, proposal.range); |
| 510 | if (new TextEncoder().encode(pendingWithoutProposal).byteLength > LOCAL_MEMORY_MAX_BYTES) { |
| 511 | return { ok: false, reason: 'oversize' }; |
| 512 | } |
| 513 | const parsed = parseLocalMemoryMarkdown(approved.draft); |
| 514 | const entry = parsed.activeEntries.find((candidate) => candidate.id === entryId); |
| 515 | if (!entry) return { ok: false, reason: 'not_found' }; |
| 516 | return { ok: true, memoryDraft: approved.draft, pendingDraft: pendingWithoutProposal, entry }; |
| 517 | } |
| 518 | |
| 519 | export function rejectLocalMemoryProposalDraft( |
| 520 | currentDraft: string, |
no test coverage detected