@dsh-enhanced/assistant-policy 0.1.7 → 0.1.14
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/README.md +2 -2
- package/bin/dsh-model-setup.js +8 -0
- package/lib/index.d.ts +1 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js.map +1 -1
- package/lib/ledger.d.ts +24 -10
- package/lib/ledger.d.ts.map +1 -1
- package/lib/ledger.js +179 -56
- package/lib/ledger.js.map +1 -1
- package/lib/model-setup.d.ts +65 -0
- package/lib/model-setup.d.ts.map +1 -0
- package/lib/model-setup.js +364 -0
- package/lib/model-setup.js.map +1 -0
- package/lib/service.d.ts.map +1 -1
- package/lib/service.js +17 -1
- package/lib/service.js.map +1 -1
- package/lib/sqlite.d.ts +1 -1
- package/lib/sqlite.d.ts.map +1 -1
- package/lib/sqlite.js +153 -5
- package/lib/sqlite.js.map +1 -1
- package/lib/version.d.ts +1 -1
- package/lib/version.d.ts.map +1 -1
- package/lib/version.js +1 -1
- package/lib/version.js.map +1 -1
- package/package.json +7 -2
package/README.md
CHANGED
|
@@ -127,9 +127,9 @@ DSH 原生 `user-approval` 仍只负责 open turn 内的即时询问;本插件
|
|
|
127
127
|
- 文件系统:创建一个 SQLite 数据库及 WAL/SHM 辅助文件;新目录权限 `0700`,主数据库 `0600`。数据库使用 WAL、`busy_timeout`、外键、FULL synchronous 和前向 schema 版本。
|
|
128
128
|
- 网络:插件不直接打开 socket;启用隔离自动 reviewer 时会调用宿主 `dsh-llm`,所选 provider route 可能使用网络。provider 的认证与传输仍由宿主拥有,本插件不直接读取其凭据。
|
|
129
129
|
- 子进程:无。
|
|
130
|
-
-
|
|
130
|
+
- 凭据:运行时的 Policy service 不读取或保存凭据值。审计按敏感 key/value、shell `command`、授权头、token、password、path/cwd 做脱敏,资源 id 仅保存 SHA-256。自动 reviewer 输入只要需要任何 secret 脱敏就失去自动放行资格并转人工。包另附带两个安装期 CLI(`dsh-permission-setup`、`dsh-model-setup`),仅供安装器/部署者手动调用,不在 Cordis 运行时加载:`dsh-permission-setup` 只原子改写 `settings.yaml` 的 `permission.defaultPreset`;`dsh-model-setup` 原子写入 `agent-default-model`(自定义网关另写 `llm-pi-ai.providers.<route>`),并在显式 `--store-key` 时把仅从环境变量读取的 API Key 原子写入 `$DSH_HOME/.credentials.yaml`(`0600`,目录 `0700`),密钥绝不作为命令行参数、缺失时 fail-closed。`dsh-model-setup` 还支持 agent route(如 `traex-agent`):这类 route 不涉及 API Key,`--enable-in-profile <profile>` 只在该 profile 的 `cordis.patch.yml` 原子改写对应 provider 行的 `enabled`(保留其它行/注释/`!!js`),非 YAML 序列的 patch 会 fail-closed 而非被覆盖。
|
|
131
131
|
- 浏览器:无。
|
|
132
|
-
-
|
|
132
|
+
- 安装脚本:无 npm 生命周期脚本;上述 CLI 仅在被显式调用时运行。
|
|
133
133
|
|
|
134
134
|
审批展示预算由包根的 frozen `APPROVAL_DISPLAY_BUDGET` 统一公开:Delivery 默认文本上限 64 KiB,summary 最多 120 UTF-8 bytes,diff 最多 60 KiB,预留 4 KiB 给渠道渲染。原始 diff 只在 routed dispatch 仍为 pending 时存在;成功 enqueue、批准、拒绝或过期都会在同一状态事务中清除,v2→v3 迁移也会清除无路由、已 enqueue 和终态记录的遗留原文。业务域仍不得把 credential、token 或其他秘密写进 diff。`proposalMaintenanceIntervalMs` 默认每 15 秒有界地过期 stale proposal,设为 `0` 可关闭,最大值为 `2147483647`。审计是 append-only;当前版本不会自动删除记录,部署者应根据自己的隐私与合规周期备份或轮换整个数据库。SQLite 提供单机多连接并发安全,不承诺多主机共享文件系统上的一致性。
|
|
135
135
|
|
package/lib/index.d.ts
CHANGED
|
@@ -13,7 +13,7 @@ export type { NativeFullReviewerReconciliation, PolicyBudgetConfig } from './ser
|
|
|
13
13
|
export { APPROVAL_DISPLAY_BUDGET } from './ledger.js';
|
|
14
14
|
export { ApprovalSettlementConflict, validateApprovalSettlement } from './settlement.js';
|
|
15
15
|
export type { ApprovalSettlementConflictReason, ApprovalSettlementExpectation, ValidatedApprovalSettlement, } from './settlement.js';
|
|
16
|
-
export type { ApprovalDecisionInput, ApprovalDispatchResult, ApprovalDispatchCursor, ApprovalDispatchRoute, ApprovalDispatchSnapshot, ApprovalDispatchState, ApprovalProposalInput, ApprovalProposalLookupInput, ApprovalProposalRecoveryInput, ApprovalProposalRecoveryResult, ApprovalProposalResult, ApprovalProposalStatus, AuditEvent, BudgetReservationResult, ApprovalProposalSnapshot, EmergencyStopState, } from './ledger.js';
|
|
16
|
+
export type { ApprovalDecisionInput, ApprovalDispatchResult, ApprovalDispatchCursor, ApprovalDispatchRoute, ApprovalDispatchRouteV1, ApprovalDispatchRouteV2, ApprovalDispatchSnapshot, ApprovalDispatchState, ApprovalProposalInput, ApprovalProposalLookupInput, ApprovalProposalRecoveryInput, ApprovalProposalRecoveryResult, ApprovalProposalResult, ApprovalProposalStatus, AuditEvent, BudgetReservationResult, ApprovalProposalSnapshot, EmergencyStopState, } from './ledger.js';
|
|
17
17
|
export * from './types.js';
|
|
18
18
|
export declare function apply(ctx: Context, config: import('./service.js').Config): void;
|
|
19
19
|
export default AssistantPolicyService;
|
package/lib/index.d.ts.map
CHANGED
|
@@ -1 +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,sBAAsB,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAC7D,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAEtC,eAAO,MAAM,IAAI,kCAAkC,CAAA;AACnD,OAAO,EAAE,sBAAsB,EAAE,MAAM,EAAE,OAAO,EAAE,CAAA;AAClD,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,oBAAoB,EACpB,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,wBAAwB,CAAA;AAC/B,YAAY,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AAC9D,OAAO,EAAE,wCAAwC,EAAE,MAAM,iCAAiC,CAAA;AAC1F,OAAO,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAA;AACzD,YAAY,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AAC9E,OAAO,EAAE,2BAA2B,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAA;AACnF,YAAY,EAAE,gCAAgC,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAA;AACxF,OAAO,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAA;AACrD,OAAO,EAAE,0BAA0B,EAAE,0BAA0B,EAAE,MAAM,iBAAiB,CAAA;AACxF,YAAY,EACV,gCAAgC,EAChC,6BAA6B,EAC7B,2BAA2B,GAC5B,MAAM,iBAAiB,CAAA;AACxB,YAAY,EACV,qBAAqB,EACrB,sBAAsB,EACtB,sBAAsB,EACtB,qBAAqB,EACrB,wBAAwB,EACxB,qBAAqB,EACrB,qBAAqB,EACrB,2BAA2B,EAC3B,6BAA6B,EAC7B,8BAA8B,EAC9B,sBAAsB,EACtB,sBAAsB,EACtB,UAAU,EACV,uBAAuB,EACvB,wBAAwB,EACxB,kBAAkB,GACnB,MAAM,aAAa,CAAA;AACpB,cAAc,YAAY,CAAA;AAE1B,wBAAgB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,cAAc,EAAE,MAAM,GAAG,IAAI,CAE/E;AAED,eAAe,sBAAsB,CAAA"}
|
|
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,sBAAsB,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAC7D,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAEtC,eAAO,MAAM,IAAI,kCAAkC,CAAA;AACnD,OAAO,EAAE,sBAAsB,EAAE,MAAM,EAAE,OAAO,EAAE,CAAA;AAClD,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,oBAAoB,EACpB,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,wBAAwB,CAAA;AAC/B,YAAY,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AAC9D,OAAO,EAAE,wCAAwC,EAAE,MAAM,iCAAiC,CAAA;AAC1F,OAAO,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAA;AACzD,YAAY,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AAC9E,OAAO,EAAE,2BAA2B,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAA;AACnF,YAAY,EAAE,gCAAgC,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAA;AACxF,OAAO,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAA;AACrD,OAAO,EAAE,0BAA0B,EAAE,0BAA0B,EAAE,MAAM,iBAAiB,CAAA;AACxF,YAAY,EACV,gCAAgC,EAChC,6BAA6B,EAC7B,2BAA2B,GAC5B,MAAM,iBAAiB,CAAA;AACxB,YAAY,EACV,qBAAqB,EACrB,sBAAsB,EACtB,sBAAsB,EACtB,qBAAqB,EACrB,uBAAuB,EACvB,uBAAuB,EACvB,wBAAwB,EACxB,qBAAqB,EACrB,qBAAqB,EACrB,2BAA2B,EAC3B,6BAA6B,EAC7B,8BAA8B,EAC9B,sBAAsB,EACtB,sBAAsB,EACtB,UAAU,EACV,uBAAuB,EACvB,wBAAwB,EACxB,kBAAkB,GACnB,MAAM,aAAa,CAAA;AACpB,cAAc,YAAY,CAAA;AAE1B,wBAAgB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,cAAc,EAAE,MAAM,GAAG,IAAI,CAE/E;AAED,eAAe,sBAAsB,CAAA"}
|
package/lib/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,sBAAsB,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAC7D,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAEtC,MAAM,CAAC,MAAM,IAAI,GAAG,+BAA+B,CAAA;AACnD,OAAO,EAAE,sBAAsB,EAAE,MAAM,EAAE,OAAO,EAAE,CAAA;AAClD,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,oBAAoB,EACpB,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,wBAAwB,CAAA;AAE/B,OAAO,EAAE,wCAAwC,EAAE,MAAM,iCAAiC,CAAA;AAC1F,OAAO,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAA;AAEzD,OAAO,EAAE,2BAA2B,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAA;AAEnF,OAAO,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAA;AACrD,OAAO,EAAE,0BAA0B,EAAE,0BAA0B,EAAE,MAAM,iBAAiB,CAAA;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,sBAAsB,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAC7D,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAEtC,MAAM,CAAC,MAAM,IAAI,GAAG,+BAA+B,CAAA;AACnD,OAAO,EAAE,sBAAsB,EAAE,MAAM,EAAE,OAAO,EAAE,CAAA;AAClD,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,oBAAoB,EACpB,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,wBAAwB,CAAA;AAE/B,OAAO,EAAE,wCAAwC,EAAE,MAAM,iCAAiC,CAAA;AAC1F,OAAO,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAA;AAEzD,OAAO,EAAE,2BAA2B,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAA;AAEnF,OAAO,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAA;AACrD,OAAO,EAAE,0BAA0B,EAAE,0BAA0B,EAAE,MAAM,iBAAiB,CAAA;AA0BxF,cAAc,YAAY,CAAA;AAE1B,MAAM,UAAU,KAAK,CAAC,GAAY,EAAE,MAAqC;IACvE,IAAI,sBAAsB,CAAC,GAAG,EAAE,MAAM,CAAC,CAAA;AACzC,CAAC;AAED,eAAe,sBAAsB,CAAA"}
|
package/lib/ledger.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type PolicyLedgerErrorCode = 'budget-exhausted' | 'idempotency-conflict' | 'invalid-input' | 'invalid-path' | 'invalid-state' | 'not-found' | 'schema-too-new' | 'unauthorized' | 'version-conflict';
|
|
1
|
+
export type PolicyLedgerErrorCode = 'budget-exhausted' | 'idempotency-conflict' | 'invalid-input' | 'invalid-path' | 'invalid-state' | 'legacy-unverifiable' | 'not-found' | 'schema-too-new' | 'unauthorized' | 'version-conflict';
|
|
2
2
|
export declare class PolicyLedgerError extends Error {
|
|
3
3
|
readonly code: PolicyLedgerErrorCode;
|
|
4
4
|
constructor(code: PolicyLedgerErrorCode, message: string);
|
|
@@ -48,7 +48,7 @@ export interface ApprovalProposalInput {
|
|
|
48
48
|
diff: string;
|
|
49
49
|
summary: string;
|
|
50
50
|
ttlMs: number;
|
|
51
|
-
dispatch?: Readonly<
|
|
51
|
+
dispatch?: Readonly<ApprovalDispatchRouteV2>;
|
|
52
52
|
}
|
|
53
53
|
/**
|
|
54
54
|
* Durable-domain creation input with an absolute deadline. Unlike `ttlMs`, the
|
|
@@ -67,14 +67,28 @@ export interface ApprovalProposalRecoveryInput {
|
|
|
67
67
|
diff: string;
|
|
68
68
|
summary: string;
|
|
69
69
|
notAfter: number;
|
|
70
|
-
dispatch?: Readonly<
|
|
70
|
+
dispatch?: Readonly<ApprovalDispatchRouteV2>;
|
|
71
71
|
}
|
|
72
|
-
export interface
|
|
72
|
+
export interface ApprovalDispatchRouteV1 {
|
|
73
|
+
/** Omitted by pre-v2 callers and persisted explicitly as route version 1. */
|
|
74
|
+
routeVersion?: 1;
|
|
73
75
|
sourceId: string;
|
|
74
76
|
bindingId: string;
|
|
75
77
|
workspace: string;
|
|
76
78
|
principal: string;
|
|
77
79
|
}
|
|
80
|
+
export interface ApprovalDispatchRouteV2 {
|
|
81
|
+
routeVersion: 2;
|
|
82
|
+
sourceId: string;
|
|
83
|
+
bindingId: string;
|
|
84
|
+
bindingVersion: number;
|
|
85
|
+
bindingGeneration: number;
|
|
86
|
+
workspace: string;
|
|
87
|
+
principal: string;
|
|
88
|
+
principalRecordId: string;
|
|
89
|
+
principalVersion: number;
|
|
90
|
+
}
|
|
91
|
+
export type ApprovalDispatchRoute = ApprovalDispatchRouteV1 | ApprovalDispatchRouteV2;
|
|
78
92
|
export type ApprovalProposalStatus = 'approved' | 'expired' | 'pending' | 'rejected';
|
|
79
93
|
export interface ApprovalProposalResult {
|
|
80
94
|
proposalId: string;
|
|
@@ -140,7 +154,7 @@ export interface ApprovalProposalLookupInput {
|
|
|
140
154
|
id: string;
|
|
141
155
|
};
|
|
142
156
|
}
|
|
143
|
-
export type ApprovalDispatchState = 'enqueued' | 'pending';
|
|
157
|
+
export type ApprovalDispatchState = 'enqueued' | 'pending' | 'quarantined';
|
|
144
158
|
/** Stable high-water mark for a pending-dispatch scan. */
|
|
145
159
|
export interface ApprovalDispatchCursor {
|
|
146
160
|
createdAt: number;
|
|
@@ -150,12 +164,8 @@ export interface ApprovalDispatchCursor {
|
|
|
150
164
|
* Immutable, policy-derived payload and route for one durable approval delivery.
|
|
151
165
|
* Callers can choose when to enqueue it, but cannot substitute display content.
|
|
152
166
|
*/
|
|
153
|
-
|
|
167
|
+
interface ApprovalDispatchSnapshotBase {
|
|
154
168
|
proposalId: string;
|
|
155
|
-
sourceId: string;
|
|
156
|
-
bindingId: string;
|
|
157
|
-
workspace: string;
|
|
158
|
-
principal: string;
|
|
159
169
|
requester: string;
|
|
160
170
|
action: string;
|
|
161
171
|
resource: {
|
|
@@ -173,6 +183,9 @@ export interface ApprovalDispatchSnapshot {
|
|
|
173
183
|
enqueuedAt: number | undefined;
|
|
174
184
|
version: number;
|
|
175
185
|
}
|
|
186
|
+
export type ApprovalDispatchSnapshot = Readonly<ApprovalDispatchSnapshotBase & ((ApprovalDispatchRouteV1 & {
|
|
187
|
+
routeVersion: 1;
|
|
188
|
+
}) | ApprovalDispatchRouteV2)>;
|
|
176
189
|
export interface ApprovalDispatchResult {
|
|
177
190
|
proposalId: string;
|
|
178
191
|
state: 'enqueued';
|
|
@@ -250,4 +263,5 @@ export declare class PolicyLedger {
|
|
|
250
263
|
lastAuditSequence: number;
|
|
251
264
|
};
|
|
252
265
|
}
|
|
266
|
+
export {};
|
|
253
267
|
//# sourceMappingURL=ledger.d.ts.map
|
package/lib/ledger.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ledger.d.ts","sourceRoot":"","sources":["../src/ledger.ts"],"names":[],"mappings":"AAMA,MAAM,MAAM,qBAAqB,GAC7B,kBAAkB,GAClB,sBAAsB,GACtB,eAAe,GACf,cAAc,GACd,eAAe,GACf,WAAW,GACX,gBAAgB,GAChB,cAAc,GACd,kBAAkB,CAAA;AAEtB,qBAAa,iBAAkB,SAAQ,KAAK;IAC9B,QAAQ,CAAC,IAAI,EAAE,qBAAqB;gBAA3B,IAAI,EAAE,qBAAqB,EAAE,OAAO,EAAE,MAAM;CAIlE;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;CACnB;AAED,iFAAiF;AACjF,eAAO,MAAM,uBAAuB;;;;;EAKlC,CAAA;AAEF,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,MAAM,CAAA;IAChB,cAAc,EAAE,MAAM,CAAA;CACvB;AAED,MAAM,WAAW,uBAAuB;IACtC,aAAa,EAAE,MAAM,CAAA;IACrB,MAAM,EAAE,WAAW,GAAG,UAAU,GAAG,UAAU,CAAA;IAC7C,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,OAAO,CAAA;CAClB;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,OAAO,CAAA;IAChB,MAAM,EAAE,MAAM,GAAG,SAAS,CAAA;IAC1B,KAAK,EAAE,MAAM,GAAG,SAAS,CAAA;IACzB,SAAS,EAAE,MAAM,GAAG,SAAS,CAAA;IAC7B,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,qBAAqB;IACpC,cAAc,EAAE,MAAM,CAAA;IACtB,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAA;IACtC,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,QAAQ,CAAC,
|
|
1
|
+
{"version":3,"file":"ledger.d.ts","sourceRoot":"","sources":["../src/ledger.ts"],"names":[],"mappings":"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"}
|
package/lib/ledger.js
CHANGED
|
@@ -22,6 +22,7 @@ const textLimits = Object.freeze({
|
|
|
22
22
|
action: 256,
|
|
23
23
|
resourceId: 4_096,
|
|
24
24
|
workspace: 4_096,
|
|
25
|
+
principalRecordId: 500,
|
|
25
26
|
summary: APPROVAL_DISPLAY_BUDGET.maxSummaryBytes,
|
|
26
27
|
diff: APPROVAL_DISPLAY_BUDGET.maxDiffBytes,
|
|
27
28
|
reason: 2_048,
|
|
@@ -37,23 +38,103 @@ function requireBoundedText(value, field, maxBytes, options = {}) {
|
|
|
37
38
|
throw new PolicyLedgerError('invalid-input', `${field} must not exceed ${maxBytes} UTF-8 bytes`);
|
|
38
39
|
}
|
|
39
40
|
}
|
|
41
|
+
function requirePositiveSafeInteger(value, field) {
|
|
42
|
+
if (!Number.isSafeInteger(value) || value <= 0) {
|
|
43
|
+
throw new PolicyLedgerError('invalid-input', `${field} must be a positive safe integer`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function approvalDispatchRouteVersion(route) {
|
|
47
|
+
return route.routeVersion ?? 1;
|
|
48
|
+
}
|
|
49
|
+
function storedApprovalDispatchRoute(row) {
|
|
50
|
+
const common = {
|
|
51
|
+
sourceId: row.source_id,
|
|
52
|
+
bindingId: row.binding_id,
|
|
53
|
+
workspace: row.workspace,
|
|
54
|
+
principal: row.dispatch_principal,
|
|
55
|
+
};
|
|
56
|
+
if (row.route_version === 1) {
|
|
57
|
+
if (row.binding_version !== null || row.binding_generation !== null
|
|
58
|
+
|| row.principal_record_id !== null || row.principal_version !== null) {
|
|
59
|
+
throw new PolicyLedgerError('invalid-state', 'legacy approval dispatch contains v2 authority fields');
|
|
60
|
+
}
|
|
61
|
+
return Object.freeze({ routeVersion: 1, ...common });
|
|
62
|
+
}
|
|
63
|
+
const bindingVersion = row.binding_version;
|
|
64
|
+
const bindingGeneration = row.binding_generation;
|
|
65
|
+
const principalRecordId = row.principal_record_id;
|
|
66
|
+
const principalVersion = row.principal_version;
|
|
67
|
+
if (row.route_version !== 2
|
|
68
|
+
|| !Number.isSafeInteger(bindingVersion) || bindingVersion === null || bindingVersion <= 0
|
|
69
|
+
|| !Number.isSafeInteger(bindingGeneration) || bindingGeneration === null || bindingGeneration <= 0
|
|
70
|
+
|| typeof principalRecordId !== 'string'
|
|
71
|
+
|| principalRecordId.trim() === ''
|
|
72
|
+
|| Buffer.byteLength(principalRecordId, 'utf8') > textLimits.principalRecordId
|
|
73
|
+
|| !Number.isSafeInteger(principalVersion) || principalVersion === null || principalVersion <= 0) {
|
|
74
|
+
throw new PolicyLedgerError('invalid-state', 'approval dispatch v2 authority fields are invalid');
|
|
75
|
+
}
|
|
76
|
+
return Object.freeze({
|
|
77
|
+
routeVersion: 2,
|
|
78
|
+
...common,
|
|
79
|
+
bindingVersion,
|
|
80
|
+
bindingGeneration,
|
|
81
|
+
principalRecordId,
|
|
82
|
+
principalVersion,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
function sameApprovalDispatchRoute(stored, input) {
|
|
86
|
+
const routeVersion = approvalDispatchRouteVersion(input);
|
|
87
|
+
if (stored.routeVersion !== routeVersion
|
|
88
|
+
|| stored.sourceId !== input.sourceId
|
|
89
|
+
|| stored.bindingId !== input.bindingId
|
|
90
|
+
|| stored.workspace !== input.workspace
|
|
91
|
+
|| stored.principal !== input.principal)
|
|
92
|
+
return false;
|
|
93
|
+
return routeVersion === 1 || (stored.routeVersion === 2 && input.routeVersion === 2
|
|
94
|
+
&& stored.bindingVersion === input.bindingVersion
|
|
95
|
+
&& stored.bindingGeneration === input.bindingGeneration
|
|
96
|
+
&& stored.principalRecordId === input.principalRecordId
|
|
97
|
+
&& stored.principalVersion === input.principalVersion);
|
|
98
|
+
}
|
|
40
99
|
function dispatchPayloadHash(input) {
|
|
41
|
-
const canonicalPayload = JSON.stringify(
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
100
|
+
const canonicalPayload = JSON.stringify(input.route.routeVersion === 2
|
|
101
|
+
? [
|
|
102
|
+
'approval-dispatch-payload-v2',
|
|
103
|
+
input.proposalId,
|
|
104
|
+
input.route.sourceId,
|
|
105
|
+
input.route.bindingId,
|
|
106
|
+
input.route.bindingVersion,
|
|
107
|
+
input.route.bindingGeneration,
|
|
108
|
+
input.route.workspace,
|
|
109
|
+
input.route.principal,
|
|
110
|
+
input.route.principalRecordId,
|
|
111
|
+
input.route.principalVersion,
|
|
112
|
+
input.requester,
|
|
113
|
+
input.action,
|
|
114
|
+
input.resourceKind,
|
|
115
|
+
input.resourceId,
|
|
116
|
+
input.summary,
|
|
117
|
+
input.diff,
|
|
118
|
+
input.diffHash,
|
|
119
|
+
input.expiresAt,
|
|
120
|
+
input.proposalVersion,
|
|
121
|
+
]
|
|
122
|
+
: [
|
|
123
|
+
input.proposalId,
|
|
124
|
+
input.route.sourceId,
|
|
125
|
+
input.route.bindingId,
|
|
126
|
+
input.route.workspace,
|
|
127
|
+
input.route.principal,
|
|
128
|
+
input.requester,
|
|
129
|
+
input.action,
|
|
130
|
+
input.resourceKind,
|
|
131
|
+
input.resourceId,
|
|
132
|
+
input.summary,
|
|
133
|
+
input.diff,
|
|
134
|
+
input.diffHash,
|
|
135
|
+
input.expiresAt,
|
|
136
|
+
input.proposalVersion,
|
|
137
|
+
]);
|
|
57
138
|
return createHash('sha256').update(canonicalPayload).digest('hex');
|
|
58
139
|
}
|
|
59
140
|
/**
|
|
@@ -62,23 +143,45 @@ function dispatchPayloadHash(input) {
|
|
|
62
143
|
*/
|
|
63
144
|
function approvalRecoveryIntentHash(input) {
|
|
64
145
|
const dispatch = input.dispatch;
|
|
65
|
-
const canonicalIntent = JSON.stringify(
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
146
|
+
const canonicalIntent = JSON.stringify(dispatch?.routeVersion === 2
|
|
147
|
+
? [
|
|
148
|
+
'approval-recovery-intent-v2',
|
|
149
|
+
input.idempotencyKey,
|
|
150
|
+
input.notAfter,
|
|
151
|
+
input.requester,
|
|
152
|
+
input.principal,
|
|
153
|
+
input.action,
|
|
154
|
+
input.resource.kind,
|
|
155
|
+
input.resource.id,
|
|
156
|
+
input.diff,
|
|
157
|
+
input.summary,
|
|
158
|
+
1,
|
|
159
|
+
dispatch.sourceId,
|
|
160
|
+
dispatch.bindingId,
|
|
161
|
+
dispatch.bindingVersion,
|
|
162
|
+
dispatch.bindingGeneration,
|
|
163
|
+
dispatch.workspace,
|
|
164
|
+
dispatch.principal,
|
|
165
|
+
dispatch.principalRecordId,
|
|
166
|
+
dispatch.principalVersion,
|
|
167
|
+
]
|
|
168
|
+
: [
|
|
169
|
+
'approval-recovery-intent-v1',
|
|
170
|
+
input.idempotencyKey,
|
|
171
|
+
input.notAfter,
|
|
172
|
+
input.requester,
|
|
173
|
+
input.principal,
|
|
174
|
+
input.action,
|
|
175
|
+
input.resource.kind,
|
|
176
|
+
input.resource.id,
|
|
177
|
+
input.diff,
|
|
178
|
+
input.summary,
|
|
179
|
+
dispatch === undefined ? 0 : 1,
|
|
180
|
+
dispatch?.sourceId ?? null,
|
|
181
|
+
dispatch?.bindingId ?? null,
|
|
182
|
+
dispatch?.workspace ?? null,
|
|
183
|
+
dispatch?.principal ?? null,
|
|
184
|
+
]);
|
|
82
185
|
return createHash('sha256').update(canonicalIntent).digest('hex');
|
|
83
186
|
}
|
|
84
187
|
function requirePositive(value, field) {
|
|
@@ -319,10 +422,25 @@ export class PolicyLedger {
|
|
|
319
422
|
requireBoundedText(input.summary, 'summary', textLimits.summary);
|
|
320
423
|
requireBoundedText(input.diff, 'diff', textLimits.diff, { allowEmpty: true });
|
|
321
424
|
if (input.dispatch !== undefined) {
|
|
425
|
+
const routeVersion = input.dispatch.routeVersion;
|
|
426
|
+
if (routeVersion !== 2) {
|
|
427
|
+
throw new PolicyLedgerError('invalid-input', 'new approval dispatch routes must use routeVersion 2');
|
|
428
|
+
}
|
|
322
429
|
requireBoundedText(input.dispatch.sourceId, 'dispatch.sourceId', textLimits.id);
|
|
323
430
|
requireBoundedText(input.dispatch.bindingId, 'dispatch.bindingId', textLimits.id);
|
|
324
431
|
requireBoundedText(input.dispatch.workspace, 'dispatch.workspace', textLimits.workspace);
|
|
325
432
|
requireBoundedText(input.dispatch.principal, 'dispatch.principal', textLimits.id);
|
|
433
|
+
const route = input.dispatch;
|
|
434
|
+
requirePositiveSafeInteger(route.bindingVersion, 'dispatch.bindingVersion');
|
|
435
|
+
requirePositiveSafeInteger(route.bindingGeneration, 'dispatch.bindingGeneration');
|
|
436
|
+
if (typeof route.principalRecordId !== 'string') {
|
|
437
|
+
throw new PolicyLedgerError('invalid-input', 'dispatch.principalRecordId must be text');
|
|
438
|
+
}
|
|
439
|
+
requireBoundedText(route.principalRecordId, 'dispatch.principalRecordId', textLimits.principalRecordId);
|
|
440
|
+
if (route.principalRecordId.normalize('NFC').trim() !== route.principalRecordId) {
|
|
441
|
+
throw new PolicyLedgerError('invalid-input', 'dispatch.principalRecordId must be normalized and trimmed');
|
|
442
|
+
}
|
|
443
|
+
requirePositiveSafeInteger(route.principalVersion, 'dispatch.principalVersion');
|
|
326
444
|
if (!isAbsolute(input.dispatch.workspace)) {
|
|
327
445
|
throw new PolicyLedgerError('invalid-path', 'dispatch.workspace must be absolute');
|
|
328
446
|
}
|
|
@@ -342,16 +460,17 @@ export class PolicyLedger {
|
|
|
342
460
|
}
|
|
343
461
|
#assertExistingProposalMatches(existing, input, diffHash, expiryMatches) {
|
|
344
462
|
const existingDispatch = this.#database.prepare(`
|
|
345
|
-
SELECT source_id, binding_id,
|
|
463
|
+
SELECT route_version, source_id, binding_id, binding_version, binding_generation,
|
|
464
|
+
workspace, principal AS dispatch_principal, principal_record_id, principal_version,
|
|
465
|
+
state, payload_hash, proposal_version
|
|
346
466
|
FROM approval_dispatches WHERE proposal_id = ?
|
|
347
467
|
`).get(existing.id);
|
|
468
|
+
const storedRoute = existingDispatch === undefined
|
|
469
|
+
? undefined
|
|
470
|
+
: storedApprovalDispatchRoute(existingDispatch);
|
|
348
471
|
const sameDispatch = input.dispatch === undefined
|
|
349
472
|
? existingDispatch === undefined
|
|
350
|
-
:
|
|
351
|
-
&& existingDispatch.source_id === input.dispatch.sourceId
|
|
352
|
-
&& existingDispatch.binding_id === input.dispatch.bindingId
|
|
353
|
-
&& existingDispatch.workspace === input.dispatch.workspace
|
|
354
|
-
&& existingDispatch.principal === input.dispatch.principal;
|
|
473
|
+
: storedRoute !== undefined && sameApprovalDispatchRoute(storedRoute, input.dispatch);
|
|
355
474
|
const sameDiff = input.dispatch === undefined
|
|
356
475
|
? existingDispatch === undefined && existing.diff_hash === diffHash
|
|
357
476
|
: existingDispatch !== undefined
|
|
@@ -407,10 +526,11 @@ export class PolicyLedger {
|
|
|
407
526
|
});
|
|
408
527
|
this.#database.prepare(`
|
|
409
528
|
INSERT INTO approval_dispatches(
|
|
410
|
-
proposal_id, source_id, binding_id,
|
|
529
|
+
proposal_id, route_version, source_id, binding_id, binding_version,
|
|
530
|
+
binding_generation, workspace, principal, principal_record_id, principal_version,
|
|
411
531
|
state, payload_hash, created_at, proposal_version
|
|
412
|
-
) VALUES (?, ?, ?, ?, ?, 'pending', ?, ?, 1)
|
|
413
|
-
`).run(id, input.dispatch.sourceId, input.dispatch.bindingId, input.dispatch.workspace, input.dispatch.principal, payloadHash, createdAt);
|
|
532
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'pending', ?, ?, 1)
|
|
533
|
+
`).run(id, approvalDispatchRouteVersion(input.dispatch), input.dispatch.sourceId, input.dispatch.bindingId, input.dispatch.routeVersion === 2 ? input.dispatch.bindingVersion : null, input.dispatch.routeVersion === 2 ? input.dispatch.bindingGeneration : null, input.dispatch.workspace, input.dispatch.principal, input.dispatch.routeVersion === 2 ? input.dispatch.principalRecordId : null, input.dispatch.routeVersion === 2 ? input.dispatch.principalVersion : null, payloadHash, createdAt);
|
|
414
534
|
}
|
|
415
535
|
return {
|
|
416
536
|
proposalId: id,
|
|
@@ -632,8 +752,10 @@ export class PolicyLedger {
|
|
|
632
752
|
const cursorCreatedAt = after?.createdAt ?? null;
|
|
633
753
|
const cursorProposalId = after?.proposalId ?? '';
|
|
634
754
|
const rows = this.#database.prepare(`
|
|
635
|
-
SELECT d.proposal_id, d.
|
|
636
|
-
d.
|
|
755
|
+
SELECT d.proposal_id, d.route_version, d.source_id, d.binding_id,
|
|
756
|
+
d.binding_version, d.binding_generation, d.workspace,
|
|
757
|
+
d.principal AS dispatch_principal, d.principal_record_id,
|
|
758
|
+
d.principal_version, d.state, d.payload_hash,
|
|
637
759
|
d.created_at AS dispatch_created_at, d.enqueued_at,
|
|
638
760
|
d.version AS dispatch_version, d.proposal_version, p.requester,
|
|
639
761
|
p.principal AS proposal_principal, p.action, p.resource_kind,
|
|
@@ -641,7 +763,8 @@ export class PolicyLedger {
|
|
|
641
763
|
p.expires_at, p.version AS current_proposal_version
|
|
642
764
|
FROM approval_dispatches d
|
|
643
765
|
JOIN approval_proposals p ON p.id = d.proposal_id
|
|
644
|
-
WHERE d.state = 'pending' AND
|
|
766
|
+
WHERE d.state = 'pending' AND d.route_version = 2
|
|
767
|
+
AND p.status = 'pending' AND p.expires_at > ?
|
|
645
768
|
AND (? IS NULL OR d.created_at > ? OR (d.created_at = ? AND d.proposal_id > ?))
|
|
646
769
|
ORDER BY d.created_at ASC, d.proposal_id ASC
|
|
647
770
|
LIMIT ?
|
|
@@ -655,6 +778,10 @@ export class PolicyLedger {
|
|
|
655
778
|
}
|
|
656
779
|
return this.#transaction(() => {
|
|
657
780
|
const dispatch = this.#dispatch(proposalId);
|
|
781
|
+
const route = storedApprovalDispatchRoute(dispatch);
|
|
782
|
+
if (route.routeVersion !== 2) {
|
|
783
|
+
throw new PolicyLedgerError('legacy-unverifiable', 'legacy approval dispatch cannot be enqueued or replayed');
|
|
784
|
+
}
|
|
658
785
|
if (dispatch.state === 'enqueued') {
|
|
659
786
|
return this.#dispatchResult(dispatch, true);
|
|
660
787
|
}
|
|
@@ -727,8 +854,10 @@ export class PolicyLedger {
|
|
|
727
854
|
}
|
|
728
855
|
#dispatch(proposalId) {
|
|
729
856
|
const dispatch = this.#database.prepare(`
|
|
730
|
-
SELECT d.proposal_id, d.
|
|
731
|
-
d.
|
|
857
|
+
SELECT d.proposal_id, d.route_version, d.source_id, d.binding_id,
|
|
858
|
+
d.binding_version, d.binding_generation, d.workspace,
|
|
859
|
+
d.principal AS dispatch_principal, d.principal_record_id,
|
|
860
|
+
d.principal_version, d.state, d.payload_hash,
|
|
732
861
|
d.created_at AS dispatch_created_at, d.enqueued_at,
|
|
733
862
|
d.version AS dispatch_version, d.proposal_version, p.requester,
|
|
734
863
|
p.principal AS proposal_principal, p.action, p.resource_kind,
|
|
@@ -753,14 +882,10 @@ export class PolicyLedger {
|
|
|
753
882
|
if (row.current_proposal_version !== row.proposal_version) {
|
|
754
883
|
throw new PolicyLedgerError('invalid-state', 'approval dispatch proposal version changed');
|
|
755
884
|
}
|
|
885
|
+
const route = storedApprovalDispatchRoute(row);
|
|
756
886
|
const expectedPayloadHash = dispatchPayloadHash({
|
|
757
887
|
proposalId: row.proposal_id,
|
|
758
|
-
route
|
|
759
|
-
sourceId: row.source_id,
|
|
760
|
-
bindingId: row.binding_id,
|
|
761
|
-
workspace: row.workspace,
|
|
762
|
-
principal: row.dispatch_principal,
|
|
763
|
-
},
|
|
888
|
+
route,
|
|
764
889
|
requester: row.requester,
|
|
765
890
|
action: row.action,
|
|
766
891
|
resourceKind: row.resource_kind,
|
|
@@ -776,9 +901,7 @@ export class PolicyLedger {
|
|
|
776
901
|
}
|
|
777
902
|
return Object.freeze({
|
|
778
903
|
proposalId: row.proposal_id,
|
|
779
|
-
|
|
780
|
-
bindingId: row.binding_id,
|
|
781
|
-
workspace: row.workspace,
|
|
904
|
+
...route,
|
|
782
905
|
principal: row.proposal_principal,
|
|
783
906
|
requester: row.requester,
|
|
784
907
|
action: row.action,
|