@dsh-enhanced/personal-memory 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 dsh-enhanced contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,115 @@
1
+ # @dsh-enhanced/personal-memory
2
+
3
+ 面向个人助理的短期上下文之外长期记忆:保存简短且稳定的事实、偏好、约定和经验,按 user/agent 与 user-global/workspace 双维隔离,通过 `assistant-policy` 的持久审批提案写入。研究资料、文档和项目知识不属于这里,应交给 `personal-wiki`。
4
+
5
+ ## 安装
6
+
7
+ 本插件要求 `@dsh-enhanced/assistant-policy` 已经挂载并提供 `ctx.assistantPolicy`。生产环境还应组合
8
+ `@dsh-enhanced/assistant-delivery`,由当前 Agent 会话绑定的 owner 私聊派生审批 principal、workspace
9
+ 和投递 binding:
10
+
11
+ ```sh
12
+ dsh plugin --profile web add @dsh-enhanced/assistant-policy
13
+ dsh plugin --profile web add @dsh-enhanced/assistant-delivery
14
+ dsh plugin --profile web add @dsh-enhanced/personal-memory
15
+ dsh --profile web --dump-config
16
+ ```
17
+
18
+ 上面的 Policy 片段只覆盖 Memory 自身的前台权限,不足以发送或点击审批卡。生产飞书闭环应运行
19
+ `dsh-lark-setup --profile web --create-app --allow-agent-tools`;向导会为 exact owner binding 写入
20
+ `dsh-enhanced-personal-memory` 的 `approval.send` 与 owner `approval.decide` 规则。若手工维护 Policy,必须
21
+ 提供同等精确的 source、workspace、principal 与 binding 限制,不能使用通配 owner 或 workspace。
22
+
23
+ 默认数据库位于 `$DSH_HOME/personal-memory/memory.sqlite`。单独安装后的 policy 默认没有 allow 规则,所以记忆搜索、快照和提案都会 fail closed;需要在 profile 中按实际 preset 与绝对 workspace 放行:
24
+
25
+ ```yaml
26
+ - id: dsh-enhanced-assistant-policy
27
+ name: '@dsh-enhanced/assistant-policy'
28
+ config:
29
+ databasePath: !!js dshHomePath('assistant-policy/policy.sqlite')
30
+ rules:
31
+ - id: allow-primary-memory-service
32
+ effect: allow
33
+ subject: { kind: agent, id: primary, workspace: /absolute/workspace }
34
+ actions: [export, propose, search, snapshot]
35
+ resource: { kind: memory, id: "*" }
36
+ context: { initiators: [foreground] }
37
+ - id: allow-primary-memory-tools
38
+ effect: allow
39
+ subject: { kind: agent, id: primary, workspace: /absolute/workspace }
40
+ actions: [execute]
41
+ resource: { kind: tool, id: memory_* }
42
+ context: { initiators: [foreground] }
43
+ budgets: []
44
+ ```
45
+
46
+ ## 行为与工具
47
+
48
+ - `memory_search`:显式检索当前 agent 可见的四个域;ASCII 与中文 unigram/bigram 均可召回,排序和 tie-break 确定,读取不更新任何计数。
49
+ - `memory_manage`:只创建 add/replace/remove 提案,返回完整 diff、proposal id、TTL 和版本;它没有直接提交路径,也不接受模型提供 principal 或 TTL。principal、workspace 和飞书投递 binding 必须来自当前 Agent 的 authenticated Delivery owner route,TTL 来自可信配置;没有活动绑定时 fail closed。
50
+ - `ctx.personalMemory.decideProposal(...)`:供绑定 owner 的可信通道/UI 决定提案。principal、版本、决定内容和理由均受幂等/CAS 约束。
51
+ - `ctx.personalMemory.exportJson(agent)`:导出版本化 JSON,不包含哈希、状态、时间戳、token 表或审计表。
52
+ - `ctx.personalMemory.proposeImport(...)`:先验证整个有界文档,再为每条记录创建审批提案;Agent 路径同样由 Delivery 派生审批 authority,未批准前不会写入 memory record。仅在未组合 Delivery 的可信本地/headless 集成中,程序化调用方可以显式传入 principal;一旦 Delivery 可用就不能用该字段覆盖绑定 owner。
53
+
54
+ 每次 `agent/session-start` 最多注入一次冻结快照。快照合并 user-global、当前 workspace、当前 agent-global 和当前 agent-workspace,受 top-K、字节和粗略 token 三重预算约束;`sensitive` 记录不会进入环境快照。内容被包在 `<memory_source>` 中并明确标为“不可信数据而非指令”,所有 XML 元字符在预算计算前转义,记录无法闭合数据边界。`memory_search` 与 `memory_manage` 的模型可见结果也分别使用有界、转义的 `<memory_search_results>` / `<memory_proposal_results>` framing。缺少绝对 cwd 或 agent preset 时不搜索、不提案、不注入,也不会退化到共享域。
55
+
56
+ ## 数据与一致性
57
+
58
+ SQLite 使用 WAL、`busy_timeout`、外键、FULL synchronous 和前向 schema 版本;目录为 `0700`,数据库为 `0600`。记录包含 stable id、内容哈希、provenance、trust、confidence、sensitivity、TTL、supersedes 和 version。replace/remove 在批准后重新读取 target version 并以 CAS 提交;审批与内存位于两个数据库,若进程恰好在 policy 批准后退出,可用原决定安全重放。并发变化会把提案标为 `conflicted`,不会覆盖新值。
59
+
60
+ 创建提案时先在 Memory 数据库持久化 creation intent,再调用 Policy 原子创建 proposal + dispatch,最后在
61
+ 同一个本地事务 attach Policy ID 并删除 intent。任一步崩溃后,reconcile 都能从 intent 自动续做,不依赖
62
+ 原 Agent turn 或人工重发。恢复使用 Policy 的原子 recover-or-create:同一个 `BEGIN IMMEDIATE` 先匹配
63
+ requester、principal、action、resource、绝对 deadline、diff、summary 和 exact dispatch route;若已有提案,
64
+ 即使 deadline 已过也返回既有终态,若尚无提案则只在 deadline 前创建。deadline 后 Policy 会永久 tombstone
65
+ 该幂等键,普通 propose 也不能复活或留下孤儿审批卡;Memory 随后把 intent 持久标为 `conflicted`。pending
66
+ 扫描使用持久 round-robin 顺序;一页未决定提案不会饿死后页已决提案。已经 attach Policy ID 的本地提案只按
67
+ 该 ID 做只读恢复;Policy 行缺失会 durable conflict,绝不重新创建 proposal/dispatch。proposal 与 intent 的
68
+ 判定也在同一个本地写事务中完成,避免多进程交错留下双状态或 poison intent。旧 schema 的 crash-gap intent
69
+ 先做 scoped read-only lookup;确有旧 Policy 行时精确回放原 TTL/dispatch,否则回到绝对 deadline 的原子恢复。
70
+
71
+ JSON 导入是一组独立、可重放的提案,不承诺跨所有记录的一次性批准或全批原子提交。SQLite 仅承诺单机多连接语义,不用于多主机共享文件系统。
72
+
73
+ ## 配置上限
74
+
75
+ | 字段 | 默认值 | 作用 |
76
+ |---|---:|---|
77
+ | `approvalMode` | `delivery-required` | 生产默认要求 authenticated Delivery route;仅可信本地/headless 集成可显式设为 `delivery-or-headless` 并传 principal |
78
+ | `maxContentBytes` | 4096 | 单条内容最大 UTF-8 字节数 |
79
+ | `maxRecordsPerIdentity` | 1000 | 每个完整身份域的活动记录上限 |
80
+ | `searchLimit` | 20 | 未显式指定时的搜索上限 |
81
+ | `snapshotLimit` | 20 | 会话快照候选上限 |
82
+ | `snapshotMaxBytes` | 8192 | 快照最大 UTF-8 字节数 |
83
+ | `snapshotMaxTokens` | 2048 | 快照粗略 token 上限 |
84
+ | `defaultProposalTtlMs` | 900000 | 默认提案有效期(15 分钟) |
85
+ | `maxImportRecords` | 100 | 单次导入最大记录数 |
86
+ | `reconcileIntervalMs` | 15000 | 提交“会话结束后才被批准”的提案的轮询间隔;`0` 关闭定时器,上限为 Node timer 的 `2147483647` ms |
87
+ | `reconcileLimit` | 50 | 每轮 reconcile 检查的本地待决提案上限 |
88
+
89
+ ## 延迟审批的提交闭环
90
+
91
+ `memory_manage` 只创建提案,永不直接写入。提案与 Policy 的 durable approval-dispatch route 在同一事务创建,Delivery 会把 Policy 冻结的 summary/diff/版本投递给 exact owner binding。审批可能发生在原 turn 结束之后(例如几分钟后在飞书卡片上点击),因此本插件会周期性调用 `reconcileProposals()`:读取 policy 账本中该提案的**终态**,再用与 `decideProposal` 完全相同的事务路径提交。
92
+
93
+ 三条边界:
94
+
95
+ - **不推断批准**:pending 仍然是 pending;只有 policy 账本给出 `approved`/`rejected` 才落盘。
96
+ - **幂等**:重复 reconcile 不会重复写入或重复结算,重启后同样安全。
97
+ - **完整校验**:提交前重新校验 proposal id、requester、principal、action、resource、summary、diff hash、expiry、版本与 decider;任一字段不符会把本地提案持久标为 `conflicted`,绝不写 memory。
98
+ - **不复活 intent**:Policy 在一个事务内完成 existing-exact / create-before-deadline / abandon-after-deadline 三选一;abandonment tombstone 会阻止其他进程随后创建孤儿 dispatch。
99
+ - **不反向调用**:policy 从不回调本插件;方向始终是本插件读取 policy。
100
+
101
+ 需要完全由宿主驱动时,把 `reconcileIntervalMs` 设为 `0` 并自行调用 `reconcileProposals()`。可信的无 Delivery
102
+ 集成还必须显式配置 `approvalMode: delivery-or-headless`;Delivery 热重载或尚未就绪不会让生产默认自动
103
+ 降级为 caller-supplied principal。
104
+
105
+ ## 权限与非目标
106
+
107
+ - 文件系统:只读写配置的 SQLite 数据库及其 WAL/SHM 辅助文件。
108
+ - 网络:本插件本身不发网络请求;组合 Delivery 时只写 durable dispatch,由 channel 插件负责发送审批卡片。
109
+ - 子进程、凭据、浏览器、安装脚本:无。
110
+ - 数据敏感性:`sensitive` 只表示禁止环境快照;显式、已授权的搜索仍可能把内容发送给当前模型。真正的密码/API key 应放在后续 `credentials-keychain`,不要写入 memory。
111
+ - 非目标:向量数据库、自动无审批写入、自动遗忘/dreaming、知识图谱、Wiki 长文、跨设备同步和多用户 ACL。
112
+
113
+ ## 兼容性
114
+
115
+ 已针对 DeepSeek Harness `0.1.0-rc.8`(源码提交 `141eb6fef83422698aef7a981029e843e8161534`)验证。详见仓库[兼容性基线](../../docs/compatibility.md)。
@@ -0,0 +1,18 @@
1
+ - insert:
2
+ - id: dsh-enhanced-personal-memory
3
+ name: '@dsh-enhanced/personal-memory'
4
+ inject:
5
+ - assistantPolicy
6
+ config:
7
+ databasePath: !!js dshHomePath('personal-memory/memory.sqlite')
8
+ approvalMode: delivery-required
9
+ maxContentBytes: 4096
10
+ maxRecordsPerIdentity: 1000
11
+ searchLimit: 20
12
+ snapshotLimit: 20
13
+ snapshotMaxBytes: 8192
14
+ snapshotMaxTokens: 2048
15
+ defaultProposalTtlMs: 900000
16
+ maxImportRecords: 100
17
+ reconcileIntervalMs: 15000
18
+ reconcileLimit: 50
package/lib/index.d.ts ADDED
@@ -0,0 +1,11 @@
1
+ import type { Context } from '@deepseek-ai/cordis';
2
+ import { PersonalMemoryService } from './service.js';
3
+ import type { Config } from './service.js';
4
+ import { version } from './version.js';
5
+ export declare const name = "dsh-enhanced-personal-memory";
6
+ export { PersonalMemoryService, version };
7
+ export type { Config };
8
+ export * from './types.js';
9
+ export declare function apply(ctx: Context, config: import('./service.js').Config): void;
10
+ export default PersonalMemoryService;
11
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAA;AACpD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAC1C,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAEtC,eAAO,MAAM,IAAI,iCAAiC,CAAA;AAClD,OAAO,EAAE,qBAAqB,EAAE,OAAO,EAAE,CAAA;AACzC,YAAY,EAAE,MAAM,EAAE,CAAA;AACtB,cAAc,YAAY,CAAA;AAE1B,wBAAgB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,cAAc,EAAE,MAAM,GAAG,IAAI,CAE/E;AAED,eAAe,qBAAqB,CAAA"}
package/lib/index.js ADDED
@@ -0,0 +1,10 @@
1
+ import { PersonalMemoryService } from './service.js';
2
+ import { version } from './version.js';
3
+ export const name = 'dsh-enhanced-personal-memory';
4
+ export { PersonalMemoryService, version };
5
+ export * from './types.js';
6
+ export function apply(ctx, config) {
7
+ new PersonalMemoryService(ctx, config);
8
+ }
9
+ export default PersonalMemoryService;
10
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAA;AAEpD,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAEtC,MAAM,CAAC,MAAM,IAAI,GAAG,8BAA8B,CAAA;AAClD,OAAO,EAAE,qBAAqB,EAAE,OAAO,EAAE,CAAA;AAEzC,cAAc,YAAY,CAAA;AAE1B,MAAM,UAAU,KAAK,CAAC,GAAY,EAAE,MAAqC;IACvE,IAAI,qBAAqB,CAAC,GAAG,EAAE,MAAM,CAAC,CAAA;AACxC,CAAC;AAED,eAAe,qBAAqB,CAAA"}
@@ -0,0 +1,29 @@
1
+ import { type ApprovalDecisionInput, type ApprovalProposalLookupInput, type ApprovalProposalInput, type ApprovalProposalRecoveryInput, type ApprovalProposalRecoveryResult, type ApprovalProposalResult, type ApprovalProposalSnapshot } from '@dsh-enhanced/assistant-policy';
2
+ import { MemoryStore } from './store.js';
3
+ import type { MemoryProposalDecisionInput, MemoryProposalInput, MemoryProposalResult } from './types.js';
4
+ export interface MemoryApprovalPolicy {
5
+ propose(input: ApprovalProposalInput): ApprovalProposalResult;
6
+ recoverOrCreateProposal(input: ApprovalProposalRecoveryInput): ApprovalProposalRecoveryResult;
7
+ decideProposal(input: ApprovalDecisionInput): ApprovalProposalResult;
8
+ getProposal(proposalId: string): ApprovalProposalSnapshot | undefined;
9
+ getProposalByIdempotencyKey?(input: ApprovalProposalLookupInput): ApprovalProposalSnapshot | undefined;
10
+ }
11
+ export declare class MemoryProposalManager {
12
+ #private;
13
+ private readonly store;
14
+ private readonly policy;
15
+ constructor(store: MemoryStore, policy: MemoryApprovalPolicy);
16
+ propose(input: MemoryProposalInput): MemoryProposalResult;
17
+ decide(input: MemoryProposalDecisionInput): MemoryProposalResult;
18
+ getProposal(proposalId: string): MemoryProposalResult | undefined;
19
+ /**
20
+ * Commit locally pending proposals whose policy decision already settled
21
+ * elsewhere, for example on an approval card minutes after the turn ended.
22
+ *
23
+ * Approval is never inferred: the decision is read back from the policy ledger,
24
+ * and only a terminal policy status is settled here. Each settle reuses the
25
+ * existing idempotent commit path, so repeated reconciles are safe.
26
+ */
27
+ reconcile(limit: number): MemoryProposalResult[];
28
+ }
29
+ //# sourceMappingURL=proposals.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"proposals.d.ts","sourceRoot":"","sources":["../src/proposals.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,KAAK,qBAAqB,EAC1B,KAAK,2BAA2B,EAChC,KAAK,qBAAqB,EAC1B,KAAK,6BAA6B,EAClC,KAAK,8BAA8B,EACnC,KAAK,sBAAsB,EAC3B,KAAK,wBAAwB,EAC9B,MAAM,gCAAgC,CAAA;AACvC,OAAO,EAGL,WAAW,EAGZ,MAAM,YAAY,CAAA;AACnB,OAAO,KAAK,EAEV,2BAA2B,EAC3B,mBAAmB,EACnB,oBAAoB,EAIrB,MAAM,YAAY,CAAA;AAEnB,MAAM,WAAW,oBAAoB;IACnC,OAAO,CAAC,KAAK,EAAE,qBAAqB,GAAG,sBAAsB,CAAA;IAC7D,uBAAuB,CAAC,KAAK,EAAE,6BAA6B,GAAG,8BAA8B,CAAA;IAC7F,cAAc,CAAC,KAAK,EAAE,qBAAqB,GAAG,sBAAsB,CAAA;IACpE,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,wBAAwB,GAAG,SAAS,CAAA;IACrE,2BAA2B,CAAC,CAAC,KAAK,EAAE,2BAA2B,GAAG,wBAAwB,GAAG,SAAS,CAAA;CACvG;AAiED,qBAAa,qBAAqB;;IAE9B,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,MAAM;gBADN,KAAK,EAAE,WAAW,EAClB,MAAM,EAAE,oBAAoB;IAG/C,OAAO,CAAC,KAAK,EAAE,mBAAmB,GAAG,oBAAoB;IAwJzD,MAAM,CAAC,KAAK,EAAE,2BAA2B,GAAG,oBAAoB;IA+BhE,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,oBAAoB,GAAG,SAAS;IAKjE;;;;;;;OAOG;IACH,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,oBAAoB,EAAE;CA+EjD"}
@@ -0,0 +1,329 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { ApprovalSettlementConflict, validateApprovalSettlement, } from '@dsh-enhanced/assistant-policy';
3
+ import { hashMemoryMutation, isMissingPolicyProposalId, MemoryStore, MemoryStoreError, missingPolicyProposalId, } from './store.js';
4
+ function deterministicProposalId(idempotencyKey) {
5
+ return `memory-${createHash('sha256').update(idempotencyKey).digest('hex').slice(0, 32)}`;
6
+ }
7
+ function proposalDiff(mutation) {
8
+ return JSON.stringify(mutation, null, 2);
9
+ }
10
+ function proposalSummary(mutation) {
11
+ const target = mutation.op === 'add' ? mutation.entry.kind : mutation.id;
12
+ return `${mutation.op} ${mutation.identity.owner} ${mutation.identity.scope} memory ${target}`;
13
+ }
14
+ function hashMemoryProposalIntent(input) {
15
+ const dispatch = input.dispatch === undefined
16
+ ? null
17
+ : {
18
+ sourceId: input.dispatch.sourceId,
19
+ bindingId: input.dispatch.bindingId,
20
+ workspace: input.dispatch.workspace,
21
+ principal: input.dispatch.principal,
22
+ };
23
+ return createHash('sha256').update(JSON.stringify({
24
+ mutation: input.mutation,
25
+ ttlMs: input.ttlMs,
26
+ dispatch,
27
+ })).digest('hex');
28
+ }
29
+ function proposalCreationVersion(proposal) {
30
+ const expectedCurrentVersion = proposal.status === 'pending' ? 1 : 2;
31
+ if (proposal.version !== expectedCurrentVersion) {
32
+ throw new MemoryStoreError('invalid-entry', 'assistant-policy returned an invalid proposal lifecycle version');
33
+ }
34
+ return 1;
35
+ }
36
+ function permanentPolicyRecoveryFailure(error) {
37
+ if (error === null || typeof error !== 'object' || !('code' in error))
38
+ return false;
39
+ return ['idempotency-conflict', 'invalid-input', 'invalid-path', 'unauthorized']
40
+ .includes(String(error.code));
41
+ }
42
+ function result(proposal, replayed, record) {
43
+ const diff = proposalDiff(proposal.mutation);
44
+ return Object.freeze({
45
+ proposalId: proposal.proposalId,
46
+ policyProposalId: proposal.policyProposalId,
47
+ status: proposal.status,
48
+ version: proposal.version,
49
+ expiresAt: proposal.expiresAt,
50
+ mutation: proposal.mutation,
51
+ diff,
52
+ summary: proposalSummary(proposal.mutation),
53
+ replayed,
54
+ ...(record === undefined ? {} : { record }),
55
+ });
56
+ }
57
+ export class MemoryProposalManager {
58
+ store;
59
+ policy;
60
+ constructor(store, policy) {
61
+ this.store = store;
62
+ this.policy = policy;
63
+ }
64
+ propose(input) {
65
+ if (input.idempotencyKey.trim() === '') {
66
+ throw new MemoryStoreError('invalid-entry', 'proposal idempotencyKey must not be empty');
67
+ }
68
+ const proposalId = deterministicProposalId(input.idempotencyKey);
69
+ const mutation = this.store.normalizeMutation(input.mutation, { preflight: false });
70
+ const normalized = {
71
+ ...input,
72
+ mutation,
73
+ };
74
+ const mutationHash = hashMemoryProposalIntent(normalized);
75
+ const prepared = this.store.prepareProposalIntent({
76
+ ...normalized,
77
+ proposalId,
78
+ mutationHash,
79
+ });
80
+ if (prepared.kind === 'conflict') {
81
+ throw new MemoryStoreError('idempotency-conflict', 'proposal key was used for another mutation');
82
+ }
83
+ if (prepared.kind === 'proposal') {
84
+ return this.#replayStoredProposal(prepared.proposal, normalized);
85
+ }
86
+ return this.#submitProposal(prepared.intent, prepared.replayed);
87
+ }
88
+ #replayStoredProposal(existing, authority) {
89
+ if (isMissingPolicyProposalId(existing.policyProposalId)) {
90
+ if (existing.policyProposalId !== missingPolicyProposalId(existing.proposalId, authority.ttlMs, authority.dispatch)) {
91
+ throw new MemoryStoreError('idempotency-conflict', 'expired proposal intent replay changed its authority');
92
+ }
93
+ return result(existing, true);
94
+ }
95
+ if (existing.status !== 'pending')
96
+ return result(existing, true);
97
+ return this.#recoverAttachedProposal(existing);
98
+ }
99
+ #submitProposal(input, intentReplayed) {
100
+ const proposalId = deterministicProposalId(input.idempotencyKey);
101
+ const diff = proposalDiff(input.mutation);
102
+ const mutationHash = hashMemoryProposalIntent(input);
103
+ const legacyMutationHash = hashMemoryMutation(input.mutation);
104
+ if (input.proposalId !== proposalId
105
+ || (input.mutationHash !== mutationHash && input.mutationHash !== legacyMutationHash)) {
106
+ throw new MemoryStoreError('idempotency-conflict', 'durable proposal intent does not match its canonical payload');
107
+ }
108
+ const policyContent = {
109
+ idempotencyKey: `personal-memory:${input.idempotencyKey}`,
110
+ requester: input.requester,
111
+ principal: input.principal,
112
+ action: `memory.${input.mutation.op}`,
113
+ resource: { kind: 'memory', id: proposalId },
114
+ diff,
115
+ summary: proposalSummary(input.mutation),
116
+ ...(input.dispatch === undefined ? {} : { dispatch: input.dispatch }),
117
+ };
118
+ let policyProposal;
119
+ try {
120
+ const notAfter = input.createdAt + input.ttlMs;
121
+ if (!Number.isSafeInteger(notAfter)) {
122
+ throw new MemoryStoreError('invalid-entry', 'proposal intent deadline exceeds the safe timestamp range');
123
+ }
124
+ const legacyReplay = input.mutationHash === legacyMutationHash
125
+ && input.mutationHash !== mutationHash;
126
+ const legacyExisting = legacyReplay
127
+ ? this.policy.getProposalByIdempotencyKey?.({
128
+ idempotencyKey: policyContent.idempotencyKey,
129
+ requester: policyContent.requester,
130
+ principal: policyContent.principal,
131
+ action: policyContent.action,
132
+ resource: policyContent.resource,
133
+ })
134
+ : undefined;
135
+ if (legacyExisting !== undefined) {
136
+ // The read is deliberately scoped and non-creating. Policy proposal rows
137
+ // are immutable/non-deletable, so ordinary propose now performs an exact
138
+ // legacy TTL/dispatch replay without opening a missing-row create path.
139
+ policyProposal = this.policy.propose({ ...policyContent, ttlMs: input.ttlMs });
140
+ }
141
+ else {
142
+ const recovered = this.policy.recoverOrCreateProposal({ ...policyContent, notAfter });
143
+ if (recovered.kind === 'abandoned') {
144
+ const conflicted = this.store.conflictProposalIntent(proposalId);
145
+ return result(conflicted.proposal, intentReplayed || recovered.replayed || conflicted.replayed);
146
+ }
147
+ policyProposal = recovered.proposal;
148
+ }
149
+ }
150
+ catch (error) {
151
+ if (!permanentPolicyRecoveryFailure(error)
152
+ && !(error instanceof MemoryStoreError && error.code === 'invalid-entry'))
153
+ throw error;
154
+ const conflicted = this.store.conflictProposalIntent(proposalId);
155
+ return result(conflicted.proposal, intentReplayed || conflicted.replayed);
156
+ }
157
+ let expectedVersion;
158
+ try {
159
+ expectedVersion = proposalCreationVersion(policyProposal);
160
+ }
161
+ catch {
162
+ const conflicted = this.store.conflictProposalIntent(proposalId);
163
+ return result(conflicted.proposal, intentReplayed || conflicted.replayed);
164
+ }
165
+ const attached = this.store.getProposal(proposalId);
166
+ if (attached !== undefined && attached.policyProposalId !== policyProposal.proposalId) {
167
+ return this.#settleSecurityConflict(attached);
168
+ }
169
+ const saved = this.store.saveProposal({
170
+ proposalId,
171
+ policyProposalId: policyProposal.proposalId,
172
+ idempotencyKey: input.idempotencyKey,
173
+ requester: input.requester,
174
+ principal: input.principal,
175
+ mutation: input.mutation,
176
+ mutationHash,
177
+ expiresAt: policyProposal.expiresAt,
178
+ version: expectedVersion,
179
+ });
180
+ const replayed = intentReplayed || policyProposal.replayed || saved.replayed;
181
+ if (saved.proposal.status !== 'pending' || policyProposal.status === 'pending') {
182
+ return result(saved.proposal, replayed);
183
+ }
184
+ const snapshot = this.policy.getProposal(saved.proposal.policyProposalId);
185
+ return this.#settleValidated(saved.proposal, snapshot, saved.proposal.version, replayed);
186
+ }
187
+ #recoverAttachedProposal(proposal) {
188
+ const snapshot = this.policy.getProposal(proposal.policyProposalId);
189
+ if (snapshot === undefined)
190
+ return this.#settleSecurityConflict(proposal);
191
+ if (snapshot.status !== 'pending') {
192
+ return this.#settleValidated(proposal, snapshot, proposal.version, true);
193
+ }
194
+ const exactPending = snapshot.proposalId === proposal.policyProposalId
195
+ && snapshot.requester === proposal.requester
196
+ && snapshot.principal === proposal.principal
197
+ && snapshot.action === `memory.${proposal.mutation.op}`
198
+ && snapshot.resource.kind === 'memory'
199
+ && snapshot.resource.id === proposal.proposalId
200
+ && snapshot.summary === proposalSummary(proposal.mutation)
201
+ && snapshot.diffHash === createHash('sha256').update(proposalDiff(proposal.mutation)).digest('hex')
202
+ && snapshot.expiresAt === proposal.expiresAt
203
+ && snapshot.version === proposal.version
204
+ && snapshot.decidedBy === undefined
205
+ && snapshot.decisionReason === undefined;
206
+ return exactPending ? result(proposal, true) : this.#settleSecurityConflict(proposal);
207
+ }
208
+ decide(input) {
209
+ const proposal = this.store.getProposal(input.proposalId);
210
+ if (proposal === undefined)
211
+ throw new MemoryStoreError('not-found', 'memory proposal was not found');
212
+ if (isMissingPolicyProposalId(proposal.policyProposalId)) {
213
+ if (input.principal !== proposal.principal) {
214
+ throw new MemoryStoreError('invalid-entry', 'memory proposal is bound to another principal');
215
+ }
216
+ return result(proposal, true);
217
+ }
218
+ const decision = this.policy.decideProposal({
219
+ proposalId: proposal.policyProposalId,
220
+ principal: input.principal,
221
+ expectedVersion: input.expectedVersion,
222
+ decision: input.decision,
223
+ reason: input.reason,
224
+ });
225
+ if (decision.status === 'pending') {
226
+ return this.#settleSecurityConflict(proposal);
227
+ }
228
+ if (proposal.status !== 'pending') {
229
+ const replay = this.store.settleProposal({
230
+ proposalId: proposal.proposalId,
231
+ policyStatus: decision.status,
232
+ policyVersion: decision.version,
233
+ });
234
+ return result(replay.proposal, decision.replayed || replay.replayed, replay.record);
235
+ }
236
+ const snapshot = this.policy.getProposal(proposal.policyProposalId);
237
+ return this.#settleValidated(proposal, snapshot, proposal.version, decision.replayed);
238
+ }
239
+ getProposal(proposalId) {
240
+ const proposal = this.store.getProposal(proposalId);
241
+ return proposal === undefined ? undefined : result(proposal, true);
242
+ }
243
+ /**
244
+ * Commit locally pending proposals whose policy decision already settled
245
+ * elsewhere, for example on an approval card minutes after the turn ended.
246
+ *
247
+ * Approval is never inferred: the decision is read back from the policy ledger,
248
+ * and only a terminal policy status is settled here. Each settle reuses the
249
+ * existing idempotent commit path, so repeated reconciles are safe.
250
+ */
251
+ reconcile(limit) {
252
+ const settled = [];
253
+ const intentLimit = limit === 1 ? 1 : Math.floor(limit / 2);
254
+ const intents = this.store.listProposalIntents(intentLimit);
255
+ for (const intent of intents) {
256
+ try {
257
+ // Re-resolve proposal-vs-intent under the local write lock before any
258
+ // Policy call. This also removes poison residue left by older versions
259
+ // that could represent both states in separate tables.
260
+ const prepared = this.store.prepareProposalIntent({
261
+ ...intent,
262
+ proposalId: intent.proposalId,
263
+ mutationHash: intent.mutationHash,
264
+ });
265
+ if (prepared.kind === 'conflict') {
266
+ this.store.deferProposalIntent(intent.proposalId);
267
+ continue;
268
+ }
269
+ const recovered = prepared.kind === 'proposal'
270
+ ? this.#replayStoredProposal(prepared.proposal, intent)
271
+ : this.#submitProposal(prepared.intent, true);
272
+ if (recovered.status !== 'pending')
273
+ settled.push(recovered);
274
+ }
275
+ catch {
276
+ this.store.deferProposalIntent(intent.proposalId);
277
+ }
278
+ }
279
+ const pendingLimit = Math.max(1, limit - intents.length);
280
+ for (const pending of this.store.listPendingProposals(pendingLimit)) {
281
+ try {
282
+ const decision = this.policy.getProposal(pending.policyProposalId);
283
+ if (decision?.status !== 'pending') {
284
+ settled.push(this.#settleValidated(pending, decision, pending.version, false));
285
+ continue;
286
+ }
287
+ this.store.deferPendingProposal(pending.proposalId);
288
+ }
289
+ catch {
290
+ this.store.deferPendingProposal(pending.proposalId);
291
+ }
292
+ }
293
+ return settled;
294
+ }
295
+ #settleValidated(proposal, snapshot, expectedVersion, policyReplayed) {
296
+ try {
297
+ const terminal = validateApprovalSettlement(snapshot, {
298
+ proposalId: proposal.policyProposalId,
299
+ requester: proposal.requester,
300
+ principal: proposal.principal,
301
+ action: `memory.${proposal.mutation.op}`,
302
+ resource: { kind: 'memory', id: proposal.proposalId },
303
+ summary: proposalSummary(proposal.mutation),
304
+ diff: proposalDiff(proposal.mutation),
305
+ expiresAt: proposal.expiresAt,
306
+ expectedVersion,
307
+ });
308
+ const applied = this.store.settleProposal({
309
+ proposalId: proposal.proposalId,
310
+ policyStatus: terminal.status,
311
+ policyVersion: terminal.version,
312
+ });
313
+ return result(applied.proposal, policyReplayed || applied.replayed, applied.record);
314
+ }
315
+ catch (error) {
316
+ if (!(error instanceof ApprovalSettlementConflict))
317
+ throw error;
318
+ return this.#settleSecurityConflict(proposal);
319
+ }
320
+ }
321
+ #settleSecurityConflict(proposal) {
322
+ const conflicted = this.store.settleProposal({
323
+ proposalId: proposal.proposalId,
324
+ policyStatus: 'conflicted',
325
+ });
326
+ return result(conflicted.proposal, conflicted.replayed);
327
+ }
328
+ }
329
+ //# sourceMappingURL=proposals.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"proposals.js","sourceRoot":"","sources":["../src/proposals.ts"],"names":[],"mappings":"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"}