@happyvertical/smrt-ingestion 0.57.20 → 0.57.22
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/FEEDBACK.md +137 -0
- package/PROPOSALS.md +146 -5
- package/README.md +168 -2
- package/REVIEW.md +611 -7
- package/agents/execution.md +87 -0
- package/dist/chunks/{execution-NoDh3pjs.js → execution-BzVQax0n.js} +528 -42
- package/dist/chunks/execution-BzVQax0n.js.map +1 -0
- package/dist/chunks/{feedback-CJB900iG.js → feedback-D3UTF8fm.js} +175 -36
- package/dist/chunks/feedback-D3UTF8fm.js.map +1 -0
- package/dist/chunks/{proposal-errors-VxcTU6Db.js → proposal-contracts-77Uo1B1j.js} +38 -44
- package/dist/chunks/proposal-contracts-77Uo1B1j.js.map +1 -0
- package/dist/chunks/reviewer-instruction-CEc3AOuU.js +33 -0
- package/dist/chunks/reviewer-instruction-CEc3AOuU.js.map +1 -0
- package/dist/dto.d.ts +75 -0
- package/dist/dto.d.ts.map +1 -1
- package/dist/execution-contracts.d.ts +4 -1
- package/dist/execution-contracts.d.ts.map +1 -1
- package/dist/execution-dto.d.ts +16 -0
- package/dist/execution-dto.d.ts.map +1 -1
- package/dist/execution-internal.d.ts +2 -0
- package/dist/execution-internal.d.ts.map +1 -1
- package/dist/execution.d.ts +101 -4
- package/dist/execution.d.ts.map +1 -1
- package/dist/execution.js +2 -2
- package/dist/feedback-dto.d.ts +18 -4
- package/dist/feedback-dto.d.ts.map +1 -1
- package/dist/feedback-internal.d.ts +9 -0
- package/dist/feedback-internal.d.ts.map +1 -1
- package/dist/feedback.d.ts +10 -0
- package/dist/feedback.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/manifest.json +339 -3
- package/dist/models.d.ts +15 -0
- package/dist/models.d.ts.map +1 -1
- package/dist/models.js +82 -2
- package/dist/models.js.map +1 -1
- package/dist/policy.d.ts +3 -0
- package/dist/policy.d.ts.map +1 -1
- package/dist/presented-filename.d.ts +11 -0
- package/dist/presented-filename.d.ts.map +1 -0
- package/dist/proposal-contracts.d.ts +31 -6
- package/dist/proposal-contracts.d.ts.map +1 -1
- package/dist/proposal-dto.d.ts +17 -0
- package/dist/proposal-dto.d.ts.map +1 -1
- package/dist/proposal-errors.d.ts +20 -0
- package/dist/proposal-errors.d.ts.map +1 -1
- package/dist/proposal-validation.d.ts +26 -0
- package/dist/proposal-validation.d.ts.map +1 -1
- package/dist/proposals.d.ts +6 -1
- package/dist/proposals.d.ts.map +1 -1
- package/dist/proposals.js +268 -71
- package/dist/proposals.js.map +1 -1
- package/dist/redaction.d.ts +9 -0
- package/dist/redaction.d.ts.map +1 -0
- package/dist/review-dto.d.ts +108 -5
- package/dist/review-dto.d.ts.map +1 -1
- package/dist/review-host.d.ts +52 -3
- package/dist/review-host.d.ts.map +1 -1
- package/dist/review-instruction-view.d.ts +12 -0
- package/dist/review-instruction-view.d.ts.map +1 -0
- package/dist/review-summary.d.ts.map +1 -1
- package/dist/reviewer-instruction.d.ts +20 -0
- package/dist/reviewer-instruction.d.ts.map +1 -0
- package/dist/server.d.ts +252 -13
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +1190 -85
- package/dist/server.js.map +1 -1
- package/dist/smrt-knowledge.json +137 -23
- package/dist/sources/common.d.ts +11 -0
- package/dist/sources/common.d.ts.map +1 -1
- package/dist/sources/email.d.ts.map +1 -1
- package/dist/sources/http.d.ts.map +1 -1
- package/dist/sources/watch-folder.d.ts.map +1 -1
- package/dist/svelte/components/ActionReview.svelte +54 -55
- package/dist/svelte/components/ActionReview.svelte.d.ts +8 -1
- package/dist/svelte/components/ActionReview.svelte.d.ts.map +1 -1
- package/dist/svelte/components/CandidatePicker.svelte +72 -0
- package/dist/svelte/components/CandidatePicker.svelte.d.ts +15 -0
- package/dist/svelte/components/CandidatePicker.svelte.d.ts.map +1 -0
- package/dist/svelte/components/EvidenceViewer.svelte +60 -4
- package/dist/svelte/components/EvidenceViewer.svelte.d.ts +12 -1
- package/dist/svelte/components/EvidenceViewer.svelte.d.ts.map +1 -1
- package/dist/svelte/components/InboxView.svelte +172 -19
- package/dist/svelte/components/InboxView.svelte.d.ts +36 -2
- package/dist/svelte/components/InboxView.svelte.d.ts.map +1 -1
- package/dist/svelte/components/IntakeInbox.svelte +6 -3
- package/dist/svelte/components/IntakeInbox.svelte.d.ts +4 -1
- package/dist/svelte/components/IntakeInbox.svelte.d.ts.map +1 -1
- package/dist/svelte/components/IntakeReview.svelte +4 -2
- package/dist/svelte/components/IntakeReview.svelte.d.ts +2 -0
- package/dist/svelte/components/IntakeReview.svelte.d.ts.map +1 -1
- package/dist/svelte/components/IntakeWorkspace.svelte +27 -0
- package/dist/svelte/components/IntakeWorkspace.svelte.d.ts +19 -0
- package/dist/svelte/components/IntakeWorkspace.svelte.d.ts.map +1 -0
- package/dist/svelte/components/ReviewView.svelte +653 -71
- package/dist/svelte/components/ReviewView.svelte.d.ts +20 -1
- package/dist/svelte/components/ReviewView.svelte.d.ts.map +1 -1
- package/dist/svelte/components/TriageView.svelte +669 -0
- package/dist/svelte/components/TriageView.svelte.d.ts +16 -0
- package/dist/svelte/components/TriageView.svelte.d.ts.map +1 -0
- package/dist/svelte/i18n.d.ts +56 -0
- package/dist/svelte/i18n.d.ts.map +1 -1
- package/dist/svelte/i18n.js +56 -0
- package/dist/svelte/index.d.ts +2 -1
- package/dist/svelte/index.d.ts.map +1 -1
- package/dist/svelte/index.js +1 -0
- package/dist/svelte/view-utils.d.ts +67 -0
- package/dist/svelte/view-utils.d.ts.map +1 -1
- package/dist/svelte/view-utils.js +157 -0
- package/package.json +13 -13
- package/dist/chunks/execution-NoDh3pjs.js.map +0 -1
- package/dist/chunks/execution-internal-DWteDHM2.js +0 -7
- package/dist/chunks/execution-internal-DWteDHM2.js.map +0 -1
- package/dist/chunks/feedback-CJB900iG.js.map +0 -1
- package/dist/chunks/proposal-errors-VxcTU6Db.js.map +0 -1
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { t as discoveryTransaction } from "./execution-internal-DWteDHM2.js";
|
|
2
1
|
import { ObjectRegistry, detectEngine, isEmbeddedDatabase, withEmbeddedWriteTransaction } from "@happyvertical/smrt-core";
|
|
3
2
|
import { createHash, randomUUID } from "node:crypto";
|
|
4
3
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
@@ -45,6 +44,49 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
45
44
|
}) : target, mod));
|
|
46
45
|
var __toCommonJS = (mod) => __hasOwnProp.call(mod, "module.exports") ? mod["module.exports"] : __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
47
46
|
//#endregion
|
|
47
|
+
//#region src/execution-internal.ts
|
|
48
|
+
var discoveryTransaction = /* @__PURE__ */ Symbol("ingestion.discoveryTransaction");
|
|
49
|
+
var generationSnapshotRead = /* @__PURE__ */ Symbol("ingestion.generationSnapshotRead");
|
|
50
|
+
var listReadGate = /* @__PURE__ */ Symbol("ingestion.listReadGate");
|
|
51
|
+
//#endregion
|
|
52
|
+
//#region src/proposal-errors.ts
|
|
53
|
+
var HandlerArgumentsInvalidError = class extends Error {
|
|
54
|
+
constructor(message) {
|
|
55
|
+
super(message);
|
|
56
|
+
this.name = "HandlerArgumentsInvalidError";
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
var ProposalsNotConfiguredError = class extends Error {
|
|
60
|
+
constructor() {
|
|
61
|
+
super("Intake proposals are not configured");
|
|
62
|
+
this.name = "ProposalsNotConfiguredError";
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
var GenerationSourceStaleError = class extends Error {
|
|
66
|
+
constructor() {
|
|
67
|
+
super("Generation source superseded");
|
|
68
|
+
this.name = "GenerationSourceStaleError";
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
var GenerationSnapshotStaleError = class extends Error {
|
|
72
|
+
constructor() {
|
|
73
|
+
super("Generation snapshot stale");
|
|
74
|
+
this.name = "GenerationSnapshotStaleError";
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
//#endregion
|
|
78
|
+
//#region src/redaction.ts
|
|
79
|
+
var UNFINISHED_ACTION_STATES = ["executing", "outcome_unknown"];
|
|
80
|
+
function redactedCeiling(ceiling, unfinishedActions) {
|
|
81
|
+
const execution = ceiling.execution;
|
|
82
|
+
if (unfinishedActions || !execution || typeof execution !== "object" || Array.isArray(execution) || !Object.hasOwn(execution, "principalId")) return ceiling;
|
|
83
|
+
const { principalId: _principal, ...rest } = execution;
|
|
84
|
+
return {
|
|
85
|
+
...ceiling,
|
|
86
|
+
execution: rest
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
//#endregion
|
|
48
90
|
//#region ../../node_modules/.pnpm/ajv@8.20.0/node_modules/ajv/dist/compile/codegen/code.js
|
|
49
91
|
var require_code$1 = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
50
92
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
@@ -6377,6 +6419,7 @@ function resolveIntakePolicy(layers) {
|
|
|
6377
6419
|
providers: [],
|
|
6378
6420
|
access: [],
|
|
6379
6421
|
requireReview: false,
|
|
6422
|
+
submitterReview: false,
|
|
6380
6423
|
maxAttempts: 1,
|
|
6381
6424
|
maxSteps: 10,
|
|
6382
6425
|
maxBytes: 65536,
|
|
@@ -6390,6 +6433,7 @@ function resolveIntakePolicy(layers) {
|
|
|
6390
6433
|
for (const key of lists) result[key] = [...app[key] ?? []];
|
|
6391
6434
|
for (const key of limits) result[key] = app[key] ?? result[key];
|
|
6392
6435
|
result.minimumCertainty = app.minimumCertainty ?? 1;
|
|
6436
|
+
result.submitterReview = app.submitterReview === true;
|
|
6393
6437
|
result.automaticHandlers = [...app.automation?.handlers ?? []];
|
|
6394
6438
|
result.consequentialAutomation = app.automation?.consequential === true;
|
|
6395
6439
|
for (const [index, layer] of layers.entries()) {
|
|
@@ -6397,6 +6441,7 @@ function resolveIntakePolicy(layers) {
|
|
|
6397
6441
|
result.versions.push(layer.version);
|
|
6398
6442
|
result.enabled &&= layer.enabled !== false;
|
|
6399
6443
|
result.requireReview ||= layer.requireReview === true;
|
|
6444
|
+
result.submitterReview &&= layer.submitterReview !== false;
|
|
6400
6445
|
for (const key of lists) {
|
|
6401
6446
|
const values = layer[key];
|
|
6402
6447
|
if (values !== void 0) {
|
|
@@ -6426,6 +6471,12 @@ function resolveIntakePolicy(layers) {
|
|
|
6426
6471
|
}
|
|
6427
6472
|
//#endregion
|
|
6428
6473
|
//#region src/execution.ts
|
|
6474
|
+
var IntakeAccessDeniedError = class extends Error {
|
|
6475
|
+
constructor() {
|
|
6476
|
+
super("Intake unavailable");
|
|
6477
|
+
this.name = "IntakeAccessDeniedError";
|
|
6478
|
+
}
|
|
6479
|
+
};
|
|
6429
6480
|
var RetentionExpired = class extends Error {
|
|
6430
6481
|
constructor(itemId, deadline) {
|
|
6431
6482
|
super("Intake retention expired");
|
|
@@ -6435,6 +6486,9 @@ var RetentionExpired = class extends Error {
|
|
|
6435
6486
|
itemId;
|
|
6436
6487
|
deadline;
|
|
6437
6488
|
};
|
|
6489
|
+
var OWN_SCAN_BATCH = 50;
|
|
6490
|
+
var OWN_SCAN_CEILING = 1e3;
|
|
6491
|
+
var UUID_CURSOR = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
6438
6492
|
function object(value) {
|
|
6439
6493
|
const parsed = typeof value === "string" ? JSON.parse(value) : value;
|
|
6440
6494
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error("Invalid intake binding");
|
|
@@ -6460,7 +6514,7 @@ function strings(value) {
|
|
|
6460
6514
|
function time(value) {
|
|
6461
6515
|
return new Date(value).getTime();
|
|
6462
6516
|
}
|
|
6463
|
-
var IntakeExecutionService = class {
|
|
6517
|
+
var IntakeExecutionService = class IntakeExecutionService {
|
|
6464
6518
|
constructor(options, config) {
|
|
6465
6519
|
this.options = options;
|
|
6466
6520
|
this.db = options.db;
|
|
@@ -6501,7 +6555,7 @@ var IntakeExecutionService = class {
|
|
|
6501
6555
|
async restoreRetention(db, itemId, deadline) {
|
|
6502
6556
|
const expiry = new Date(deadline).toISOString();
|
|
6503
6557
|
const item = (await db.query("UPDATE intake_items SET expires_at=CASE WHEN expires_at>? THEN ? ELSE expires_at END WHERE id=? AND tenant_id=? AND confidential_scope_id=? RETURNING id,expires_at,visibility", expiry, expiry, itemId, this.scope.tenantId, this.scope.confidentialScopeId)).rows[0];
|
|
6504
|
-
if (!item) throw new
|
|
6558
|
+
if (!item) throw new IntakeAccessDeniedError();
|
|
6505
6559
|
if (!this.retained(item)) await this.redactExecution(db, item);
|
|
6506
6560
|
return item;
|
|
6507
6561
|
}
|
|
@@ -6512,7 +6566,8 @@ var IntakeExecutionService = class {
|
|
|
6512
6566
|
"intake_review_decisions",
|
|
6513
6567
|
"intake_executions",
|
|
6514
6568
|
"intake_plans",
|
|
6515
|
-
"intake_feedback"
|
|
6569
|
+
"intake_feedback",
|
|
6570
|
+
"intake_dispositions"
|
|
6516
6571
|
]) await db.query(`UPDATE ${table} SET data='{}' WHERE item_id=? AND tenant_id=? AND confidential_scope_id=?`, item.id, this.scope.tenantId, this.scope.confidentialScopeId);
|
|
6517
6572
|
}
|
|
6518
6573
|
async narrowRetention(db, item, deadline) {
|
|
@@ -6548,8 +6603,8 @@ var IntakeExecutionService = class {
|
|
|
6548
6603
|
operation: "read",
|
|
6549
6604
|
capturedCeiling: object(object(item.data).capturedCeiling),
|
|
6550
6605
|
db
|
|
6551
|
-
})) throw new
|
|
6552
|
-
if (!expired && (item.visibility !== "active" || time(item.expires_at) <= this.now().getTime() || item.cancelled)) throw new
|
|
6606
|
+
})) throw new IntakeAccessDeniedError();
|
|
6607
|
+
if (!expired && (item.visibility !== "active" || time(item.expires_at) <= this.now().getTime() || item.cancelled)) throw new IntakeAccessDeniedError();
|
|
6553
6608
|
return item;
|
|
6554
6609
|
}
|
|
6555
6610
|
ceiling(item) {
|
|
@@ -6583,26 +6638,54 @@ var IntakeExecutionService = class {
|
|
|
6583
6638
|
].includes(handler.capability.effect ?? "") || typeof handler.capability.idempotent !== "boolean" || typeof handler.capability.openWorld !== "boolean") throw new Error("Undeclared capability");
|
|
6584
6639
|
return handler;
|
|
6585
6640
|
}
|
|
6586
|
-
|
|
6641
|
+
/**
|
|
6642
|
+
* `arguments` marks arguments being previewed (operation preview, which
|
|
6643
|
+
* also carries corrections, and plan preview): only their schema refusal is
|
|
6644
|
+
* the correctable `HandlerArgumentsInvalidError`. Apply-time and
|
|
6645
|
+
* external-send revalidation and result-schema checks keep the plain error.
|
|
6646
|
+
*/
|
|
6647
|
+
validate(schema, value, limit, arguments_ = false) {
|
|
6587
6648
|
if (Buffer.byteLength(canonical(value)) > limit || schema.type !== "object" || schema.additionalProperties !== false) throw new Error("Invalid handler payload");
|
|
6588
|
-
if (!this.ajv.compile(schema)(value)) throw new Error("Invalid handler payload");
|
|
6649
|
+
if (!this.ajv.compile(schema)(value)) throw arguments_ ? new HandlerArgumentsInvalidError("Invalid handler payload") : /* @__PURE__ */ new Error("Invalid handler payload");
|
|
6589
6650
|
}
|
|
6590
|
-
|
|
6651
|
+
/**
|
|
6652
|
+
* The read-only half of `access`: current host authority, policy and
|
|
6653
|
+
* ceiling allowlists and the retention deadline they imply. It writes
|
|
6654
|
+
* nothing, so a listing may call it per (item, handler) without opening a
|
|
6655
|
+
* transaction per row.
|
|
6656
|
+
*/
|
|
6657
|
+
async authority(db, item, handler, operation, onHostError) {
|
|
6591
6658
|
const ceiling = this.ceiling(item);
|
|
6592
|
-
|
|
6593
|
-
|
|
6594
|
-
|
|
6595
|
-
|
|
6596
|
-
|
|
6597
|
-
|
|
6598
|
-
|
|
6599
|
-
|
|
6600
|
-
|
|
6659
|
+
let current;
|
|
6660
|
+
try {
|
|
6661
|
+
current = await this.config.authorize({
|
|
6662
|
+
db,
|
|
6663
|
+
scope: this.scope,
|
|
6664
|
+
itemId: String(item.id),
|
|
6665
|
+
capturedCeiling: ceiling,
|
|
6666
|
+
operation,
|
|
6667
|
+
handlerId: handler.id
|
|
6668
|
+
});
|
|
6669
|
+
} catch (error) {
|
|
6670
|
+
onHostError?.(error);
|
|
6671
|
+
throw error;
|
|
6672
|
+
}
|
|
6673
|
+
if (!current.allowed || current.mutationBoundary !== "serialized" || current.principalId !== ceiling.principalId) throw new IntakeAccessDeniedError();
|
|
6601
6674
|
const policy = resolveIntakePolicy(current.policy);
|
|
6602
6675
|
const operationKey = "model" in handler.operation ? `${handler.operation.model}:${handler.operation.action}@${handler.operation.version}` : handler.operation.playbookKey;
|
|
6603
|
-
if (!policy.enabled || !policy.handlers.includes(handler.id) || !ceiling.handlers.includes(handler.id) || !policy.operations.includes(operationKey) || !ceiling.operations.includes(operationKey) || !policy.access.includes(this.scope.confidentialScopeId)) throw new
|
|
6604
|
-
if (operation === "review" && (!current.reviewer || !policy.reviewers.includes(this.scope.actorId))) throw new
|
|
6676
|
+
if (!policy.enabled || !policy.handlers.includes(handler.id) || !ceiling.handlers.includes(handler.id) || !policy.operations.includes(operationKey) || !ceiling.operations.includes(operationKey) || !policy.access.includes(this.scope.confidentialScopeId)) throw new IntakeAccessDeniedError();
|
|
6677
|
+
if (operation === "review" && (!current.reviewer || !policy.reviewers.includes(this.scope.actorId))) throw new IntakeAccessDeniedError();
|
|
6678
|
+
if (operation === "review_own" && (current.submitter !== true || !policy.submitterReview || item.submitter_id !== this.scope.actorId || ceiling.principalId !== this.scope.actorId)) throw new IntakeAccessDeniedError();
|
|
6605
6679
|
const deadline = Math.min(time(item.expires_at), time(item.created_at) + policy.retentionMs);
|
|
6680
|
+
return {
|
|
6681
|
+
current,
|
|
6682
|
+
ceiling,
|
|
6683
|
+
policy,
|
|
6684
|
+
deadline
|
|
6685
|
+
};
|
|
6686
|
+
}
|
|
6687
|
+
async access(db, item, handler, operation) {
|
|
6688
|
+
const { current, ceiling, policy, deadline } = await this.authority(db, item, handler, operation);
|
|
6606
6689
|
const enforce = async (executor) => {
|
|
6607
6690
|
const intents = this.retentionIntents.getStore();
|
|
6608
6691
|
const itemId = String(item.id);
|
|
@@ -6723,7 +6806,7 @@ var IntakeExecutionService = class {
|
|
|
6723
6806
|
}
|
|
6724
6807
|
async bound(db, actionId, lock = false, expired = false, proposalId) {
|
|
6725
6808
|
const [found] = await this.rows(db, "intake_actions", "id=?", [actionId]);
|
|
6726
|
-
if (!found) throw new
|
|
6809
|
+
if (!found) throw new IntakeAccessDeniedError();
|
|
6727
6810
|
const item = await this.item(db, String(found.item_id), lock, expired);
|
|
6728
6811
|
const [action] = await this.rows(db, "intake_actions", "id=?", [actionId]);
|
|
6729
6812
|
const [proposal] = await this.rows(db, "intake_proposals", proposalId ? "action_id=? AND id=?" : "action_id=? AND revision=?", [actionId, proposalId ?? action.proposal_revision]);
|
|
@@ -6739,6 +6822,38 @@ var IntakeExecutionService = class {
|
|
|
6739
6822
|
handler
|
|
6740
6823
|
};
|
|
6741
6824
|
}
|
|
6825
|
+
/**
|
|
6826
|
+
* Read authority for one handler binding of a listed item, read-only. It
|
|
6827
|
+
* applies the handler checks `listReviews` applies before projecting an
|
|
6828
|
+
* action: the bound handler must resolve to a single operation whose
|
|
6829
|
+
* operation and capability still match the saved binding (no version
|
|
6830
|
+
* drift), and the execution read gate (host authority, policy and ceiling
|
|
6831
|
+
* handler/operation allowlists, `policy.access`, principal) must pass, but
|
|
6832
|
+
* without the retention write; an item past the deadline `access` would
|
|
6833
|
+
* narrow to is not listable, and neither is a cancelled item, which
|
|
6834
|
+
* `listReviews` refuses once it binds an action. Per-action target,
|
|
6835
|
+
* evidence and plan gates are not evaluated here; `load` still enforces
|
|
6836
|
+
* them. A refusal or any other failed check answers false and the list
|
|
6837
|
+
* discloses nothing about why; a host `authorize` that throws propagates,
|
|
6838
|
+
* as it does from `load`, so the page is rejected. Package-internal:
|
|
6839
|
+
* reachable only through a symbol the service holds,
|
|
6840
|
+
* so callers cannot hand it an unscoped row.
|
|
6841
|
+
*/
|
|
6842
|
+
async [listReadGate](item, binding) {
|
|
6843
|
+
let hostFailure;
|
|
6844
|
+
try {
|
|
6845
|
+
if (item.cancelled) return false;
|
|
6846
|
+
const handler = this.operation(this.handler(binding.handlerId, binding.handlerVersion));
|
|
6847
|
+
if (canonical(handler.operation) !== canonical(binding.operation) || canonical(handler.capability) !== canonical(binding.capability)) return false;
|
|
6848
|
+
const { deadline } = await this.authority(this.db, item, handler, "read", (error) => {
|
|
6849
|
+
hostFailure = { error };
|
|
6850
|
+
});
|
|
6851
|
+
return deadline > this.now().getTime();
|
|
6852
|
+
} catch {
|
|
6853
|
+
if (hostFailure) throw hostFailure.error;
|
|
6854
|
+
return false;
|
|
6855
|
+
}
|
|
6856
|
+
}
|
|
6742
6857
|
review(bound) {
|
|
6743
6858
|
return {
|
|
6744
6859
|
actionId: String(bound.action.id),
|
|
@@ -6759,14 +6874,14 @@ var IntakeExecutionService = class {
|
|
|
6759
6874
|
const run = (executor) => this.discoveryContext(executor, itemId, handlerId, handlerVersion, work, false, revalidate);
|
|
6760
6875
|
return db ? run(db) : this.tx(run);
|
|
6761
6876
|
}
|
|
6762
|
-
[feedbackActionTransaction](actionId, reviewer, work, executor, requiredProviders, proposalId) {
|
|
6877
|
+
[feedbackActionTransaction](actionId, reviewer, work, executor, requiredProviders, proposalId, targetRevisions = true) {
|
|
6763
6878
|
const operation = async (db) => {
|
|
6764
6879
|
const load = async () => {
|
|
6765
6880
|
const bound = await this.bound(db, actionId, true, false, proposalId);
|
|
6766
6881
|
const { context, permissions } = await this.access(db, bound.item, bound.handler, reviewer ? "review" : "read");
|
|
6767
6882
|
if (requiredProviders?.some((provider) => !context.policy.providers.includes(provider))) throw new Error("Feedback source provider unavailable");
|
|
6768
6883
|
await this.verifyEvidence(db, bound);
|
|
6769
|
-
for (const target of bound.binding.targetPreconditions) await context.assertTarget(target.model, target.id, bound.action.state === "succeeded" ? void 0 : target.revision);
|
|
6884
|
+
for (const target of bound.binding.targetPreconditions) await context.assertTarget(target.model, target.id, bound.action.state === "succeeded" || !targetRevisions ? void 0 : target.revision);
|
|
6770
6885
|
if (bound.binding.plan) {
|
|
6771
6886
|
const plan = (await this.rows(db, "intake_plans", "item_id=? AND plan_key=?", [bound.item.id, bound.binding.plan.key])).sort((a, b) => Number(b.revision) - Number(a.revision))[0];
|
|
6772
6887
|
if (!plan || plan.digest !== bound.binding.plan.digest) throw new Error("Plan superseded");
|
|
@@ -6796,10 +6911,16 @@ var IntakeExecutionService = class {
|
|
|
6796
6911
|
configuration: input.configuration
|
|
6797
6912
|
}) !== analysis.input_digest) throw new Error("Analysis input integrity");
|
|
6798
6913
|
if (!this.retained(bound.item)) throw new Error("Feedback unavailable");
|
|
6914
|
+
const resolved = !!await this.disposition(db, String(bound.item.id));
|
|
6915
|
+
const state = String(bound.action.state);
|
|
6916
|
+
const settled = !resolved && (["executing", "succeeded"].includes(state) || state === "authorized" && await this.approvalLive(db, bound.action));
|
|
6917
|
+
const origin = object(bound.action.data).origin;
|
|
6799
6918
|
return {
|
|
6800
6919
|
db,
|
|
6801
6920
|
context,
|
|
6802
6921
|
permissions,
|
|
6922
|
+
origin: typeof origin === "string" ? origin : "",
|
|
6923
|
+
settled,
|
|
6803
6924
|
review: this.review(bound),
|
|
6804
6925
|
itemId: String(bound.item.id),
|
|
6805
6926
|
attemptId: String(attempt.id),
|
|
@@ -6832,7 +6953,7 @@ var IntakeExecutionService = class {
|
|
|
6832
6953
|
"leaseMs",
|
|
6833
6954
|
"retentionMs"
|
|
6834
6955
|
]) if (next[key] > prior[key]) throw new Error("Feedback cannot widen policy");
|
|
6835
|
-
if (prior.requireReview && !next.requireReview || next.minimumCertainty < prior.minimumCertainty || !prior.consequentialAutomation && next.consequentialAutomation) throw new Error("Feedback cannot widen policy");
|
|
6956
|
+
if (prior.requireReview && !next.requireReview || next.minimumCertainty < prior.minimumCertainty || !prior.consequentialAutomation && next.consequentialAutomation || !prior.submitterReview && next.submitterReview) throw new Error("Feedback cannot widen policy");
|
|
6836
6957
|
return result;
|
|
6837
6958
|
};
|
|
6838
6959
|
return executor ? operation(executor) : this.tx(operation);
|
|
@@ -6848,13 +6969,13 @@ var IntakeExecutionService = class {
|
|
|
6848
6969
|
if (!Array.isArray(references)) throw new Error("Feedback lineage unavailable");
|
|
6849
6970
|
if (references.length === 0) return;
|
|
6850
6971
|
if (!this.options.feedback) throw new Error("Feedback configuration unavailable");
|
|
6851
|
-
const { IntakeFeedbackService } = await import("./feedback-
|
|
6972
|
+
const { IntakeFeedbackService } = await import("./feedback-D3UTF8fm.js").then((n) => n.r);
|
|
6852
6973
|
await new IntakeFeedbackService(this.options, this).assertActionLineage(String(bound.action.id), db, String(bound.proposal.id));
|
|
6853
6974
|
}
|
|
6854
6975
|
async getAction(actionId) {
|
|
6855
6976
|
return this.tx(async (db) => {
|
|
6856
6977
|
const [action] = await this.rows(db, "intake_actions", "id=?", [actionId]);
|
|
6857
|
-
if (!action) throw new
|
|
6978
|
+
if (!action) throw new IntakeAccessDeniedError();
|
|
6858
6979
|
const item = await this.item(db, String(action.item_id), true, true);
|
|
6859
6980
|
const expiredReview = async () => {
|
|
6860
6981
|
await this.redactExecution(db, item);
|
|
@@ -6890,7 +7011,7 @@ var IntakeExecutionService = class {
|
|
|
6890
7011
|
/** Bounded reload discovery. The cursor is an opaque action ID, never authority. */
|
|
6891
7012
|
async listReviews(itemId, input = {}) {
|
|
6892
7013
|
const limit = input.limit ?? 20;
|
|
6893
|
-
if (!Number.isSafeInteger(limit) || limit < 1 || limit > 50 || input.cursor !== void 0 &&
|
|
7014
|
+
if (!Number.isSafeInteger(limit) || limit < 1 || limit > 50 || input.cursor !== void 0 && !UUID_CURSOR.test(input.cursor)) throw new Error("Invalid review page");
|
|
6894
7015
|
return this.tx(async (db) => {
|
|
6895
7016
|
const item = await this.item(db, itemId, true, true);
|
|
6896
7017
|
const rows = (await db.query(`SELECT * FROM intake_actions WHERE tenant_id=? AND confidential_scope_id=? AND item_id=?${input.cursor ? " AND id>?" : ""} ORDER BY id LIMIT ?`, this.scope.tenantId, this.scope.confidentialScopeId, itemId, ...input.cursor ? [input.cursor] : [], limit + 1)).rows;
|
|
@@ -6958,12 +7079,14 @@ var IntakeExecutionService = class {
|
|
|
6958
7079
|
if (attempt && ["completed", "partial"].includes(String(attempt.state)) && String(analysis.current_attempt_id) === String(attempt.id)) recoveryAttemptId = String(attempt.id);
|
|
6959
7080
|
}
|
|
6960
7081
|
}
|
|
7082
|
+
const origin = object(action.data).origin;
|
|
6961
7083
|
const entry = {
|
|
6962
7084
|
review: this.review(bound),
|
|
6963
7085
|
args: structuredClone(bound.binding.args),
|
|
6964
7086
|
handlerId: bound.handler.id,
|
|
6965
7087
|
handlerVersion: bound.handler.version,
|
|
6966
7088
|
attemptId: String(proposal.analysis_attempt_id),
|
|
7089
|
+
...typeof origin === "string" && origin ? { origin } : {},
|
|
6967
7090
|
dependencies: structuredClone(bound.binding.dependencies)
|
|
6968
7091
|
};
|
|
6969
7092
|
if (action.state === "succeeded") entry.result = await this.successful(db, action, item);
|
|
@@ -7043,7 +7166,12 @@ var IntakeExecutionService = class {
|
|
|
7043
7166
|
};
|
|
7044
7167
|
});
|
|
7045
7168
|
}
|
|
7046
|
-
|
|
7169
|
+
/**
|
|
7170
|
+
* `reviewed` marks arguments a reviewer is previewing or correcting: only
|
|
7171
|
+
* then is a refusal the typed `HandlerArgumentsInvalidError`. Apply-time
|
|
7172
|
+
* verification and plan step expansion keep the plain error.
|
|
7173
|
+
*/
|
|
7174
|
+
async buildBinding(db, item, input, reviewed = false) {
|
|
7047
7175
|
const handler = this.operation(this.handler(input.handlerId, input.handlerVersion));
|
|
7048
7176
|
const { policy, context } = await this.access(db, item, handler, "preview");
|
|
7049
7177
|
const dependencies = input.dependencies ?? {};
|
|
@@ -7073,8 +7201,8 @@ var IntakeExecutionService = class {
|
|
|
7073
7201
|
}
|
|
7074
7202
|
concrete[field] = `00000000-0000-4000-8000-000000000000`;
|
|
7075
7203
|
}
|
|
7076
|
-
this.validate(handler.argsSchema, concrete, policy.maxBytes);
|
|
7077
|
-
if (!(await handler.validate(input.args, context)).ok) throw new Error("Invalid handler arguments");
|
|
7204
|
+
this.validate(handler.argsSchema, concrete, policy.maxBytes, reviewed);
|
|
7205
|
+
if (!(await handler.validate(input.args, context)).ok) throw reviewed ? new HandlerArgumentsInvalidError("Invalid handler arguments") : /* @__PURE__ */ new Error("Invalid handler arguments");
|
|
7078
7206
|
const preview = await handler.preview(input.args, context);
|
|
7079
7207
|
if (canonical(preview.normalizedArgs) !== canonical(input.args)) throw new Error("Preview arguments must be normalized before publication");
|
|
7080
7208
|
for (const target of preview.targetPreconditions) await context.assertTarget(target.model, target.id, target.revision);
|
|
@@ -7124,7 +7252,7 @@ var IntakeExecutionService = class {
|
|
|
7124
7252
|
if (!this.retained(item)) throw new RetentionExpired(String(item.id), time(item.expires_at));
|
|
7125
7253
|
id(input.requestId);
|
|
7126
7254
|
const [action] = await this.rows(db, "intake_actions", "id=? AND item_id=?", [input.actionId, input.itemId]);
|
|
7127
|
-
if (!action) throw new
|
|
7255
|
+
if (!action) throw new IntakeAccessDeniedError();
|
|
7128
7256
|
const prior = await this.rows(db, "intake_proposals", "action_id=?", [input.actionId]);
|
|
7129
7257
|
const requestHash = intakeBindingDigest(input);
|
|
7130
7258
|
const replay = prior.find((row) => object(row.data).requestId === input.requestId);
|
|
@@ -7146,6 +7274,7 @@ var IntakeExecutionService = class {
|
|
|
7146
7274
|
"executing",
|
|
7147
7275
|
"outcome_unknown"
|
|
7148
7276
|
].includes(String(action.state))) throw new Error("Proposal conflict");
|
|
7277
|
+
if (await this.disposition(db, String(item.id))) throw new Error("Item resolved");
|
|
7149
7278
|
const [attempt] = await this.rows(db, "intake_analysis_attempts", "id=? AND item_id=?", [input.attemptId, input.itemId]);
|
|
7150
7279
|
const [analysis] = await this.rows(db, "intake_analyses", "id=?", [attempt.analysis_id]);
|
|
7151
7280
|
const bindingHash = intakeBindingDigest({
|
|
@@ -7187,10 +7316,24 @@ var IntakeExecutionService = class {
|
|
|
7187
7316
|
const frozen = JSON.parse(canonical(input));
|
|
7188
7317
|
return this.tx(async (db) => {
|
|
7189
7318
|
const item = await this.item(db, frozen.itemId, true);
|
|
7190
|
-
|
|
7319
|
+
await this.assertReviewerRevision(db, item, frozen);
|
|
7320
|
+
const binding = await this.buildBinding(db, item, frozen, true);
|
|
7191
7321
|
return this.publish(db, item, frozen, binding);
|
|
7192
7322
|
});
|
|
7193
7323
|
}
|
|
7324
|
+
/**
|
|
7325
|
+
* A reviewer-selected action records a human routing choice: re-previewing it
|
|
7326
|
+
* needs the reviewer grant (not just `preview`) and keeps its handler fixed,
|
|
7327
|
+
* so only the arguments can be corrected.
|
|
7328
|
+
*/
|
|
7329
|
+
async assertReviewerRevision(db, item, input) {
|
|
7330
|
+
const [action] = await this.rows(db, "intake_actions", "id=? AND item_id=?", [input.actionId, item.id]);
|
|
7331
|
+
if (!action || object(action.data).origin !== "reviewer-selected") return;
|
|
7332
|
+
const [current] = await this.rows(db, "intake_proposals", "action_id=? AND revision=?", [action.id, action.proposal_revision]);
|
|
7333
|
+
const binding = current ? object(object(current.data).binding) : void 0;
|
|
7334
|
+
if (!binding || binding.handlerId !== input.handlerId || binding.handlerVersion !== input.handlerVersion) throw new Error("Reviewer-selected handler is fixed");
|
|
7335
|
+
await this.access(db, item, this.operation(this.handler(input.handlerId, input.handlerVersion)), "review");
|
|
7336
|
+
}
|
|
7194
7337
|
/** Capture owner facts in the same transaction; these are never correctness labels. */
|
|
7195
7338
|
async feedbackEvent(db, bound, eventId, kind) {
|
|
7196
7339
|
if (!this.options.feedback || !this.retained(bound.item)) return;
|
|
@@ -7223,6 +7366,8 @@ var IntakeExecutionService = class {
|
|
|
7223
7366
|
handlerVersion: bound.handler.version,
|
|
7224
7367
|
signal,
|
|
7225
7368
|
reviewer: kind === "action_decision" ? eventData.reviewer : null,
|
|
7369
|
+
decisionMode: kind === "action_decision" ? eventData.mode ?? "review" : null,
|
|
7370
|
+
selfDecision: kind === "action_decision" && eventData.selfDecision === true,
|
|
7226
7371
|
observedBy: this.scope.actorId,
|
|
7227
7372
|
principalId: eventData.principalId ?? null,
|
|
7228
7373
|
reason: kind === "action_decision" ? eventData.reason ?? "" : "",
|
|
@@ -7247,7 +7392,7 @@ var IntakeExecutionService = class {
|
|
|
7247
7392
|
data: canonical(data)
|
|
7248
7393
|
});
|
|
7249
7394
|
}
|
|
7250
|
-
async submitDecision(input) {
|
|
7395
|
+
async submitDecision(input, mode = "review") {
|
|
7251
7396
|
const frozen = JSON.parse(canonical(input));
|
|
7252
7397
|
id(frozen.requestId);
|
|
7253
7398
|
if (![
|
|
@@ -7256,9 +7401,10 @@ var IntakeExecutionService = class {
|
|
|
7256
7401
|
"defer",
|
|
7257
7402
|
"correct"
|
|
7258
7403
|
].includes(frozen.decision) || (frozen.reason?.length ?? 0) > 2048) throw new Error("Invalid decision");
|
|
7404
|
+
if (mode === "review_own" && frozen.decision === "correct") throw new Error("Invalid decision");
|
|
7259
7405
|
return this.tx(async (db) => {
|
|
7260
7406
|
const bound = await this.bound(db, frozen.actionId, true);
|
|
7261
|
-
const { policy } = await this.access(db, bound.item, bound.handler,
|
|
7407
|
+
const { policy } = await this.access(db, bound.item, bound.handler, mode);
|
|
7262
7408
|
await this.feedbackLineage(db, bound);
|
|
7263
7409
|
const decisions = await this.rows(db, "intake_review_decisions", "action_id=?", [frozen.actionId]);
|
|
7264
7410
|
const requestHash = intakeBindingDigest(frozen);
|
|
@@ -7270,6 +7416,7 @@ var IntakeExecutionService = class {
|
|
|
7270
7416
|
}
|
|
7271
7417
|
if (bound.action.state !== "waiting_review" || Number(bound.proposal.revision) !== frozen.expectedRevision || Number(bound.action.review_version) !== frozen.expectedReviewVersion || bound.proposal.binding_hash !== frozen.bindingHash) throw new Error("Stale review");
|
|
7272
7418
|
await this.verifyEvidence(db, bound);
|
|
7419
|
+
if (frozen.decision !== "reject" && await this.disposition(db, String(bound.item.id))) throw new Error("Item resolved");
|
|
7273
7420
|
const expiresAt = new Date(Math.min(time(bound.item.expires_at), this.now().getTime() + policy.approvalMs)).toISOString();
|
|
7274
7421
|
const decisionEvent = await this.insert(db, "intake_review_decisions", {
|
|
7275
7422
|
item_id: bound.item.id,
|
|
@@ -7286,7 +7433,9 @@ var IntakeExecutionService = class {
|
|
|
7286
7433
|
expiresAt,
|
|
7287
7434
|
policyVersions: policy.versions,
|
|
7288
7435
|
decidedAt: this.now().toISOString(),
|
|
7289
|
-
kind: "human"
|
|
7436
|
+
kind: "human",
|
|
7437
|
+
mode,
|
|
7438
|
+
selfDecision: bound.item.submitter_id === this.scope.actorId
|
|
7290
7439
|
})
|
|
7291
7440
|
});
|
|
7292
7441
|
await this.feedbackEvent(db, bound, String(decisionEvent.id), "action_decision");
|
|
@@ -7304,7 +7453,7 @@ var IntakeExecutionService = class {
|
|
|
7304
7453
|
args: frozen.correctedArgs,
|
|
7305
7454
|
dependencies: bound.binding.dependencies
|
|
7306
7455
|
};
|
|
7307
|
-
const binding = await this.buildBinding(db, bound.item, corrected);
|
|
7456
|
+
const binding = await this.buildBinding(db, bound.item, corrected, true);
|
|
7308
7457
|
await this.feedbackLineage(db, bound);
|
|
7309
7458
|
return this.publish(db, bound.item, corrected, binding);
|
|
7310
7459
|
}
|
|
@@ -7315,6 +7464,21 @@ var IntakeExecutionService = class {
|
|
|
7315
7464
|
defer: "deferred"
|
|
7316
7465
|
}[frozen.decision];
|
|
7317
7466
|
await db.query("UPDATE intake_actions SET state=? WHERE id=? AND tenant_id=? AND confidential_scope_id=?", state, frozen.actionId, this.scope.tenantId, this.scope.confidentialScopeId);
|
|
7467
|
+
const provenance = object(bound.action.data);
|
|
7468
|
+
if (frozen.decision === "approve" && mode === "review" && provenance.origin === "reviewer-selected") {
|
|
7469
|
+
const [attempt] = await this.rows(db, "intake_analysis_attempts", "id=? AND item_id=?", [bound.proposal.analysis_attempt_id, bound.item.id]);
|
|
7470
|
+
if (attempt) await this.routingFeedback(db, bound.item, attempt, {
|
|
7471
|
+
requestId: frozen.requestId,
|
|
7472
|
+
reason: typeof provenance.reason === "string" ? provenance.reason : "",
|
|
7473
|
+
...typeof provenance.reviewer === "string" ? { startedBy: provenance.reviewer } : {},
|
|
7474
|
+
handler: {
|
|
7475
|
+
handlerId: bound.handler.id,
|
|
7476
|
+
handlerVersion: bound.handler.version,
|
|
7477
|
+
args: structuredClone(bound.binding.args),
|
|
7478
|
+
review: this.review(bound)
|
|
7479
|
+
}
|
|
7480
|
+
});
|
|
7481
|
+
}
|
|
7318
7482
|
return {
|
|
7319
7483
|
...this.review(bound),
|
|
7320
7484
|
state
|
|
@@ -7339,6 +7503,7 @@ var IntakeExecutionService = class {
|
|
|
7339
7503
|
async approved(db, bound) {
|
|
7340
7504
|
await this.feedbackLineage(db, bound);
|
|
7341
7505
|
await this.verifyEvidence(db, bound);
|
|
7506
|
+
if (await this.disposition(db, String(bound.item.id))) throw new Error("Item resolved");
|
|
7342
7507
|
if (bound.binding.plan) {
|
|
7343
7508
|
const latest = (await this.rows(db, "intake_plans", "item_id=? AND plan_key=?", [bound.item.id, bound.binding.plan.key])).sort((a, b) => Number(b.revision) - Number(a.revision))[0];
|
|
7344
7509
|
if (!latest || latest.digest !== bound.binding.plan.digest || Number(latest.revision) !== bound.binding.plan.revision) throw new Error("Plan superseded");
|
|
@@ -7392,6 +7557,7 @@ var IntakeExecutionService = class {
|
|
|
7392
7557
|
]);
|
|
7393
7558
|
if (!decision || decision.decision !== "approve" || decision.binding_hash !== bound.proposal.binding_hash || time(object(decision.data).expiresAt) <= this.now().getTime()) throw new Error("Approval required");
|
|
7394
7559
|
const decisionData = object(decision.data);
|
|
7560
|
+
if (decisionData.mode === "review_own" && (decisionData.reviewer !== bound.item.submitter_id || bound.item.submitter_id !== this.ceiling(bound.item).principalId)) throw new Error("Approval required");
|
|
7395
7561
|
if (decisionData.kind === "machine") {
|
|
7396
7562
|
const access = await this.access(db, bound.item, bound.handler, "execute");
|
|
7397
7563
|
const consequential = bound.handler.execution.kind === "external" || bound.handler.capability.effect === "destructive" || bound.handler.capability.openWorld;
|
|
@@ -7514,6 +7680,7 @@ var IntakeExecutionService = class {
|
|
|
7514
7680
|
};
|
|
7515
7681
|
if (!(await db.query("UPDATE intake_actions SET state='succeeded',result_digest=? WHERE id=? AND tenant_id=? AND confidential_scope_id=? AND fence=? AND state IN ('executing','outcome_unknown') RETURNING id", resultDigest, bound.action.id, this.scope.tenantId, this.scope.confidentialScopeId, execution.fence)).rows.length) throw new Error("Execution fence lost");
|
|
7516
7682
|
await db.query("UPDATE intake_executions SET state='succeeded',result_digest=?,data=? WHERE id=? AND tenant_id=? AND confidential_scope_id=?", resultDigest, canonical(data), execution.id, this.scope.tenantId, this.scope.confidentialScopeId);
|
|
7683
|
+
await this.releaseRedactedPrincipal(db, String(bound.item.id));
|
|
7517
7684
|
await this.feedbackEvent(db, bound, String(execution.id), "downstream_outcome");
|
|
7518
7685
|
return {
|
|
7519
7686
|
state: "succeeded",
|
|
@@ -7527,7 +7694,7 @@ var IntakeExecutionService = class {
|
|
|
7527
7694
|
let reserved;
|
|
7528
7695
|
const result = await this.tx(async (db) => {
|
|
7529
7696
|
const [action] = await this.rows(db, "intake_actions", "id=?", [actionId]);
|
|
7530
|
-
if (!action) throw new
|
|
7697
|
+
if (!action) throw new IntakeAccessDeniedError();
|
|
7531
7698
|
const item = await this.item(db, String(action.item_id), true, true);
|
|
7532
7699
|
const [current] = await this.rows(db, "intake_actions", "id=?", [actionId]);
|
|
7533
7700
|
if (current.state === "succeeded") return this.successful(db, current, item);
|
|
@@ -7744,6 +7911,7 @@ var IntakeExecutionService = class {
|
|
|
7744
7911
|
...object(execution.data),
|
|
7745
7912
|
reconciliation: observed.kind
|
|
7746
7913
|
}), execution.id, this.scope.tenantId, this.scope.confidentialScopeId);
|
|
7914
|
+
await this.releaseRedactedPrincipal(db, String(item.id));
|
|
7747
7915
|
await this.feedbackEvent(db, {
|
|
7748
7916
|
...bound,
|
|
7749
7917
|
item
|
|
@@ -7761,7 +7929,7 @@ var IntakeExecutionService = class {
|
|
|
7761
7929
|
const bound = await this.bound(db, actionId, true);
|
|
7762
7930
|
const { current, policy, context } = await this.access(db, bound.item, bound.handler, "execute");
|
|
7763
7931
|
const consequential = bound.handler.execution.kind === "external" || bound.handler.capability.effect === "destructive" || bound.handler.capability.openWorld;
|
|
7764
|
-
if (bound.action.state !== "waiting_review" || !policy.automaticHandlers.includes(bound.handler.id) || consequential && !policy.consequentialAutomation || !this.config.evaluateAutomatic) throw new Error("Automatic execution unavailable");
|
|
7932
|
+
if (bound.action.state !== "waiting_review" || !policy.automaticHandlers.includes(bound.handler.id) || consequential && !policy.consequentialAutomation || !this.config.evaluateAutomatic || object(bound.action.data).origin === "reviewer-selected" || await this.disposition(db, String(bound.item.id))) throw new Error("Automatic execution unavailable");
|
|
7765
7933
|
await this.feedbackLineage(db, bound);
|
|
7766
7934
|
const evaluated = await this.config.evaluateAutomatic({
|
|
7767
7935
|
bindingHash: String(bound.proposal.binding_hash),
|
|
@@ -7794,6 +7962,226 @@ var IntakeExecutionService = class {
|
|
|
7794
7962
|
};
|
|
7795
7963
|
});
|
|
7796
7964
|
}
|
|
7965
|
+
/**
|
|
7966
|
+
* Whether an `authorized` action still holds an unexpired approval for its
|
|
7967
|
+
* current review version. An expired one can be neither applied nor rejected,
|
|
7968
|
+
* so it must not keep the item open; `approved()` refuses resolved items.
|
|
7969
|
+
*/
|
|
7970
|
+
async approvalLive(db, action) {
|
|
7971
|
+
return (await this.rows(db, "intake_review_decisions", "action_id=? AND review_version=? AND decision='approve'", [action.id, action.review_version])).some((row) => time(object(row.data).expiresAt) > this.now().getTime());
|
|
7972
|
+
}
|
|
7973
|
+
/** Current terminal reviewer disposition, if any. */
|
|
7974
|
+
async disposition(db, itemId) {
|
|
7975
|
+
const [row] = await this.rows(db, "intake_dispositions", "item_id=? ORDER BY id LIMIT 1", [itemId]);
|
|
7976
|
+
return row;
|
|
7977
|
+
}
|
|
7978
|
+
dispositionDTO(row) {
|
|
7979
|
+
const data = object(row.data);
|
|
7980
|
+
return {
|
|
7981
|
+
kind: "resolved_no_action",
|
|
7982
|
+
reviewer: typeof data.reviewer === "string" ? data.reviewer : "",
|
|
7983
|
+
reason: typeof data.reason === "string" ? data.reason : "",
|
|
7984
|
+
decidedAt: typeof data.decidedAt === "string" ? data.decidedAt : ""
|
|
7985
|
+
};
|
|
7986
|
+
}
|
|
7987
|
+
/** The current revision's completed/partial attempt; failed revisions need reprocessing. */
|
|
7988
|
+
async currentAttempt(db, item) {
|
|
7989
|
+
const [analysis] = await this.rows(db, "intake_analyses", "item_id=? AND revision=?", [item.id, item.analysis_revision]);
|
|
7990
|
+
if (!analysis?.current_attempt_id) throw new Error("Analysis unavailable");
|
|
7991
|
+
const [attempt] = await this.rows(db, "intake_analysis_attempts", "id=? AND item_id=?", [analysis.current_attempt_id, item.id]);
|
|
7992
|
+
if (!attempt || !["completed", "partial"].includes(String(attempt.state)) || !attempt.output_digest) throw new Error("Analysis unavailable");
|
|
7993
|
+
return attempt;
|
|
7994
|
+
}
|
|
7995
|
+
/** Reviewer authority under a handler this item's captured ceiling offers; no new grant. */
|
|
7996
|
+
async reviewerAccess(db, item) {
|
|
7997
|
+
const handlers = [...this.config.handlers].sort((a, b) => `${a.id}@${a.version}`.localeCompare(`${b.id}@${b.version}`));
|
|
7998
|
+
for (const handler of handlers) try {
|
|
7999
|
+
return await this.access(db, item, handler, "review");
|
|
8000
|
+
} catch (error) {
|
|
8001
|
+
if (error instanceof RetentionExpired) throw error;
|
|
8002
|
+
}
|
|
8003
|
+
throw new Error("Intake unavailable");
|
|
8004
|
+
}
|
|
8005
|
+
/** Extracted text for routing examples: the attempt's own results or its pinned extraction. */
|
|
8006
|
+
async extractedQuery(db, item, attempt) {
|
|
8007
|
+
let source = attempt;
|
|
8008
|
+
const output = object(object(attempt.data).output);
|
|
8009
|
+
if (output.proposals) {
|
|
8010
|
+
const pin = object(object(output.proposals).source);
|
|
8011
|
+
const [row] = await this.rows(db, "intake_analysis_attempts", "id=? AND item_id=?", [pin.attemptId, item.id]);
|
|
8012
|
+
if (!row || row.output_digest !== pin.outputDigest) throw new Error("Feedback source unavailable");
|
|
8013
|
+
source = row;
|
|
8014
|
+
}
|
|
8015
|
+
const results = object(object(source.data).output).results;
|
|
8016
|
+
if (!Array.isArray(results)) return "";
|
|
8017
|
+
return results.flatMap((entry) => {
|
|
8018
|
+
const segments = object(entry).segments;
|
|
8019
|
+
return Array.isArray(segments) ? segments.map((segment) => String(object(segment).text ?? "")) : [];
|
|
8020
|
+
}).join("\n").slice(0, 4096);
|
|
8021
|
+
}
|
|
8022
|
+
/** Optional explicit routing projection; the reviewer's action or disposition stays authoritative. */
|
|
8023
|
+
async routingFeedback(db, item, attempt, choice) {
|
|
8024
|
+
if (!this.options.feedback || !this.retained(item)) return;
|
|
8025
|
+
if (!await this.options.feedback.authorize({
|
|
8026
|
+
db,
|
|
8027
|
+
scope: this.scope,
|
|
8028
|
+
itemId: String(item.id),
|
|
8029
|
+
operation: "capture"
|
|
8030
|
+
})) return;
|
|
8031
|
+
const key = `routing:${choice.requestId}`;
|
|
8032
|
+
const [replay] = await this.rows(db, "intake_feedback", "item_id=? AND request_key=?", [item.id, key]);
|
|
8033
|
+
if (replay) return;
|
|
8034
|
+
let data;
|
|
8035
|
+
try {
|
|
8036
|
+
const [analysis] = await this.rows(db, "intake_analyses", "id=?", [attempt.analysis_id]);
|
|
8037
|
+
const inputs = object(analysis.data).inputs;
|
|
8038
|
+
if (!Array.isArray(inputs)) throw new Error("Invalid evidence");
|
|
8039
|
+
const body = {
|
|
8040
|
+
version: 1,
|
|
8041
|
+
choice: choice.handler ? "handler" : "none",
|
|
8042
|
+
handlerId: choice.handler?.handlerId ?? null,
|
|
8043
|
+
handlerVersion: choice.handler?.handlerVersion ?? null,
|
|
8044
|
+
args: choice.handler?.args ?? null,
|
|
8045
|
+
actionId: choice.handler?.review.actionId ?? null,
|
|
8046
|
+
proposalId: choice.handler?.review.proposalId ?? null,
|
|
8047
|
+
revision: choice.handler?.review.revision ?? null,
|
|
8048
|
+
bindingHash: choice.handler?.review.bindingHash ?? null,
|
|
8049
|
+
attemptId: String(attempt.id),
|
|
8050
|
+
attemptDigest: String(attempt.output_digest),
|
|
8051
|
+
analysisRevision: Number(analysis.revision),
|
|
8052
|
+
evidence: inputs,
|
|
8053
|
+
query: await this.extractedQuery(db, item, attempt),
|
|
8054
|
+
reviewer: this.scope.actorId,
|
|
8055
|
+
startedBy: choice.startedBy ?? null,
|
|
8056
|
+
approvedBy: choice.handler ? this.scope.actorId : null,
|
|
8057
|
+
reason: choice.reason,
|
|
8058
|
+
requestId: choice.requestId,
|
|
8059
|
+
decidedAt: this.now().toISOString()
|
|
8060
|
+
};
|
|
8061
|
+
data = {
|
|
8062
|
+
...body,
|
|
8063
|
+
digest: intakeBindingDigest(body)
|
|
8064
|
+
};
|
|
8065
|
+
} catch {
|
|
8066
|
+
return;
|
|
8067
|
+
}
|
|
8068
|
+
if (Buffer.byteLength(canonical(data)) > this.options.feedback.maxBytes) return;
|
|
8069
|
+
await this.insert(db, "intake_feedback", {
|
|
8070
|
+
item_id: item.id,
|
|
8071
|
+
request_key: key,
|
|
8072
|
+
supersedes_id: null,
|
|
8073
|
+
kind: "reviewer_routing",
|
|
8074
|
+
data: canonical(data)
|
|
8075
|
+
});
|
|
8076
|
+
}
|
|
8077
|
+
/** Human-chosen handler for any item. Same preview/approval path; never automation-eligible. */
|
|
8078
|
+
async startAction(input) {
|
|
8079
|
+
const frozen = JSON.parse(canonical(JSON.parse(JSON.stringify(input))));
|
|
8080
|
+
id(frozen.requestId);
|
|
8081
|
+
if (frozen.reason !== void 0 && (typeof frozen.reason !== "string" || frozen.reason.length > 2048) || !frozen.args || typeof frozen.args !== "object" || Array.isArray(frozen.args)) throw new Error("Invalid reviewer action");
|
|
8082
|
+
return this.tx(async (db) => {
|
|
8083
|
+
const item = await this.item(db, frozen.itemId, true);
|
|
8084
|
+
const handler = this.handler(frozen.handlerId, frozen.handlerVersion);
|
|
8085
|
+
if (!("execution" in handler)) throw new Error("Handler unavailable");
|
|
8086
|
+
await this.access(db, item, handler, "review");
|
|
8087
|
+
const evidence = await this.rows(db, "intake_evidence", "item_id=? AND state='durable'", [item.id]);
|
|
8088
|
+
if (!handler.discovery?.mediaTypes.some((type) => evidence.some((entry) => entry.media_type === type))) throw new Error("Handler unavailable");
|
|
8089
|
+
if (await this.disposition(db, String(item.id))) throw new Error("Item resolved");
|
|
8090
|
+
const attempt = await this.currentAttempt(db, item);
|
|
8091
|
+
const actionKey = `reviewer:${frozen.requestId}`;
|
|
8092
|
+
const provenance = canonical({
|
|
8093
|
+
origin: "reviewer-selected",
|
|
8094
|
+
intentionKey: actionKey,
|
|
8095
|
+
reviewer: this.scope.actorId,
|
|
8096
|
+
reason: frozen.reason ?? ""
|
|
8097
|
+
});
|
|
8098
|
+
let [action] = await this.rows(db, "intake_actions", "item_id=? AND action_key=?", [item.id, actionKey]);
|
|
8099
|
+
if (action && canonical(object(action.data)) !== provenance) throw new Error("Action identity conflict");
|
|
8100
|
+
action ??= await this.insert(db, "intake_actions", {
|
|
8101
|
+
item_id: item.id,
|
|
8102
|
+
action_key: actionKey,
|
|
8103
|
+
state: "proposed",
|
|
8104
|
+
proposal_revision: 0,
|
|
8105
|
+
review_version: 0,
|
|
8106
|
+
execution_attempt: 0,
|
|
8107
|
+
fence: 0,
|
|
8108
|
+
result_digest: "",
|
|
8109
|
+
data: provenance
|
|
8110
|
+
});
|
|
8111
|
+
const replayed = (await this.rows(db, "intake_proposals", "action_id=?", [action.id])).find((row) => object(row.data).requestId === frozen.requestId);
|
|
8112
|
+
const preview = {
|
|
8113
|
+
itemId: String(item.id),
|
|
8114
|
+
actionId: String(action.id),
|
|
8115
|
+
attemptId: String(attempt.id),
|
|
8116
|
+
expectedRevision: replayed ? Number(replayed.revision) - 1 : Number(action.proposal_revision),
|
|
8117
|
+
requestId: frozen.requestId,
|
|
8118
|
+
handlerId: handler.id,
|
|
8119
|
+
handlerVersion: handler.version,
|
|
8120
|
+
args: frozen.args
|
|
8121
|
+
};
|
|
8122
|
+
const binding = await this.buildBinding(db, item, preview);
|
|
8123
|
+
return this.publish(db, item, preview, binding);
|
|
8124
|
+
});
|
|
8125
|
+
}
|
|
8126
|
+
/** The full disposition (reviewer, reason) under the current reviewer authority. */
|
|
8127
|
+
async getItemDisposition(itemId) {
|
|
8128
|
+
return this.tx(async (db) => {
|
|
8129
|
+
const item = await this.item(db, itemId);
|
|
8130
|
+
await this.reviewerAccess(db, item);
|
|
8131
|
+
const row = await this.disposition(db, String(item.id));
|
|
8132
|
+
return row ? this.dispositionDTO(row) : void 0;
|
|
8133
|
+
});
|
|
8134
|
+
}
|
|
8135
|
+
/** Close an item as handled without an action. Terminal; replay returns the same disposition. */
|
|
8136
|
+
async resolveItem(input) {
|
|
8137
|
+
const frozen = JSON.parse(canonical(JSON.parse(JSON.stringify(input))));
|
|
8138
|
+
id(frozen.requestId);
|
|
8139
|
+
if (frozen.disposition !== "resolve_no_action" || frozen.reason !== void 0 && (typeof frozen.reason !== "string" || frozen.reason.length > 2048)) throw new Error("Invalid disposition");
|
|
8140
|
+
return this.tx(async (db) => {
|
|
8141
|
+
const item = await this.item(db, frozen.itemId, true);
|
|
8142
|
+
const { policy } = await this.reviewerAccess(db, item);
|
|
8143
|
+
const requestHash = intakeBindingDigest(frozen);
|
|
8144
|
+
const key = `disposition:${frozen.requestId}`;
|
|
8145
|
+
const existing = await this.disposition(db, String(item.id));
|
|
8146
|
+
if (existing) {
|
|
8147
|
+
const data = object(existing.data);
|
|
8148
|
+
if (existing.request_key === key && data.requestHash === requestHash && data.reviewer === this.scope.actorId) return this.dispositionDTO(existing);
|
|
8149
|
+
throw new Error("Item resolved");
|
|
8150
|
+
}
|
|
8151
|
+
const pending = [];
|
|
8152
|
+
for (const action of await this.rows(db, "intake_actions", "item_id=? AND state IN ('authorized','executing','outcome_unknown','succeeded')", [item.id])) if (action.state !== "authorized" || await this.approvalLive(db, action)) pending.push(action);
|
|
8153
|
+
if (pending.length) throw new Error("Item has pending or applied actions");
|
|
8154
|
+
if (!this.retained(item)) throw new RetentionExpired(String(item.id), time(item.expires_at));
|
|
8155
|
+
let attempt;
|
|
8156
|
+
try {
|
|
8157
|
+
attempt = await this.currentAttempt(db, item);
|
|
8158
|
+
} catch {
|
|
8159
|
+
attempt = void 0;
|
|
8160
|
+
}
|
|
8161
|
+
for (const table of ["intake_analysis_attempts", "intake_analyses"]) await db.query(`UPDATE ${table} SET state='superseded' WHERE tenant_id=? AND confidential_scope_id=? AND item_id=? AND state IN ('queued','running')`, this.scope.tenantId, this.scope.confidentialScopeId, item.id);
|
|
8162
|
+
await db.query("UPDATE intake_dispatches SET state='completed' WHERE tenant_id=? AND confidential_scope_id=? AND item_id=? AND state<>'completed'", this.scope.tenantId, this.scope.confidentialScopeId, item.id);
|
|
8163
|
+
const row = await this.insert(db, "intake_dispositions", {
|
|
8164
|
+
item_id: item.id,
|
|
8165
|
+
request_key: key,
|
|
8166
|
+
disposition: "resolved_no_action",
|
|
8167
|
+
data: canonical({
|
|
8168
|
+
reviewer: this.scope.actorId,
|
|
8169
|
+
reason: frozen.reason ?? "",
|
|
8170
|
+
requestId: frozen.requestId,
|
|
8171
|
+
requestHash,
|
|
8172
|
+
decidedAt: this.now().toISOString(),
|
|
8173
|
+
attemptId: attempt ? String(attempt.id) : null,
|
|
8174
|
+
attemptDigest: attempt ? String(attempt.output_digest) : null,
|
|
8175
|
+
policyVersions: policy.versions
|
|
8176
|
+
})
|
|
8177
|
+
});
|
|
8178
|
+
if (attempt) await this.routingFeedback(db, item, attempt, {
|
|
8179
|
+
requestId: frozen.requestId,
|
|
8180
|
+
reason: frozen.reason ?? ""
|
|
8181
|
+
});
|
|
8182
|
+
return this.dispositionDTO(row);
|
|
8183
|
+
});
|
|
8184
|
+
}
|
|
7797
8185
|
async previewPlan(input) {
|
|
7798
8186
|
const frozen = JSON.parse(canonical(input));
|
|
7799
8187
|
id(frozen.planKey);
|
|
@@ -7803,8 +8191,8 @@ var IntakeExecutionService = class {
|
|
|
7803
8191
|
const handler = this.handler(frozen.handlerId, frozen.handlerVersion);
|
|
7804
8192
|
if (!("expand" in handler) || !("playbookKey" in handler.operation)) throw new Error("Playbook handler required");
|
|
7805
8193
|
const { policy, context } = await this.access(db, item, handler, "preview");
|
|
7806
|
-
this.validate(handler.argsSchema, frozen.args, policy.maxBytes);
|
|
7807
|
-
if (!(await handler.validate(frozen.args, context)).ok) throw new
|
|
8194
|
+
this.validate(handler.argsSchema, frozen.args, policy.maxBytes, true);
|
|
8195
|
+
if (!(await handler.validate(frozen.args, context)).ok) throw new HandlerArgumentsInvalidError("Invalid plan arguments");
|
|
7808
8196
|
const preview = await handler.preview(frozen.args, context);
|
|
7809
8197
|
if (canonical(preview.normalizedArgs) !== canonical(frozen.args) || Buffer.byteLength(canonical(preview)) > policy.maxBytes) throw new Error("Invalid plan preview");
|
|
7810
8198
|
for (const target of preview.targetPreconditions) await context.assertTarget(target.model, target.id, target.revision);
|
|
@@ -7997,6 +8385,104 @@ var IntakeExecutionService = class {
|
|
|
7997
8385
|
}
|
|
7998
8386
|
return results;
|
|
7999
8387
|
}
|
|
8388
|
+
/** The recorded submitter decides their own proposal under `review_own`
|
|
8389
|
+
* authority. Apply remains the ordinary once-only path under live grants. */
|
|
8390
|
+
async submitOwnDecision(input) {
|
|
8391
|
+
return this.submitDecision(input, "review_own");
|
|
8392
|
+
}
|
|
8393
|
+
/** Bounded listing of this actor's own active submissions with their saved
|
|
8394
|
+
* reviews. The submitter column is the authenticated receipt actor recorded by
|
|
8395
|
+
* the server; every page reloads current item/handler/target authority. */
|
|
8396
|
+
async listOwnProposals(input = {}) {
|
|
8397
|
+
const limit = input.limit ?? 20;
|
|
8398
|
+
if (!Number.isSafeInteger(limit) || limit < 1 || limit > 50 || input.cursor !== void 0 && !UUID_CURSOR.test(input.cursor)) throw new Error("Invalid review page");
|
|
8399
|
+
const readable = [];
|
|
8400
|
+
let more = false;
|
|
8401
|
+
let after = input.cursor;
|
|
8402
|
+
let hidden = 0;
|
|
8403
|
+
scan: for (;;) {
|
|
8404
|
+
const batch = (await this.db.query(`SELECT * FROM intake_items WHERE tenant_id=? AND confidential_scope_id=? AND submitter_id=? AND visibility='active' AND expires_at>?${after ? " AND id>?" : ""} ORDER BY id LIMIT ?`, this.scope.tenantId, this.scope.confidentialScopeId, this.scope.actorId, this.now().toISOString(), ...after ? [after] : [], OWN_SCAN_BATCH)).rows;
|
|
8405
|
+
for (const row of batch) if (await this.readableOwn(row)) {
|
|
8406
|
+
if (readable.length === limit) {
|
|
8407
|
+
more = true;
|
|
8408
|
+
break scan;
|
|
8409
|
+
}
|
|
8410
|
+
readable.push(row);
|
|
8411
|
+
} else if (++hidden >= OWN_SCAN_CEILING) throw new Error("Own proposal scan limit");
|
|
8412
|
+
if (batch.length < OWN_SCAN_BATCH) break;
|
|
8413
|
+
after = String(batch[batch.length - 1].id);
|
|
8414
|
+
}
|
|
8415
|
+
const reviews = new IntakeExecutionService(this.options, {
|
|
8416
|
+
...this.config,
|
|
8417
|
+
assertTarget: async (target) => {
|
|
8418
|
+
try {
|
|
8419
|
+
return await this.config.assertTarget(target);
|
|
8420
|
+
} catch {
|
|
8421
|
+
throw new IntakeAccessDeniedError();
|
|
8422
|
+
}
|
|
8423
|
+
}
|
|
8424
|
+
});
|
|
8425
|
+
const items = [];
|
|
8426
|
+
for (const row of readable) {
|
|
8427
|
+
const item = {
|
|
8428
|
+
id: String(row.id),
|
|
8429
|
+
receiptState: String(row.receipt_state),
|
|
8430
|
+
processingState: String(row.processing_state),
|
|
8431
|
+
analysisRevision: Number(row.analysis_revision),
|
|
8432
|
+
cancelled: !!row.cancelled,
|
|
8433
|
+
traceId: String(row.trace_id)
|
|
8434
|
+
};
|
|
8435
|
+
try {
|
|
8436
|
+
items.push({
|
|
8437
|
+
item,
|
|
8438
|
+
reviews: await reviews.listReviews(item.id, { limit: 50 })
|
|
8439
|
+
});
|
|
8440
|
+
} catch (error) {
|
|
8441
|
+
if (!(error instanceof IntakeAccessDeniedError)) throw error;
|
|
8442
|
+
items.push({
|
|
8443
|
+
item,
|
|
8444
|
+
reviews: null,
|
|
8445
|
+
unavailable: true
|
|
8446
|
+
});
|
|
8447
|
+
}
|
|
8448
|
+
}
|
|
8449
|
+
return {
|
|
8450
|
+
items,
|
|
8451
|
+
...more && items.length ? { nextCursor: items[items.length - 1].item.id } : {}
|
|
8452
|
+
};
|
|
8453
|
+
}
|
|
8454
|
+
/** Once a privacy-redacted item has no external action left to reconcile,
|
|
8455
|
+
* drop the principal that redaction had to retain for it. Caller holds the
|
|
8456
|
+
* item lock. */
|
|
8457
|
+
async releaseRedactedPrincipal(db, itemId) {
|
|
8458
|
+
const [item] = await this.rows(db, "intake_items", "id=?", [itemId]);
|
|
8459
|
+
if (!item || item.visibility === "active") return;
|
|
8460
|
+
if ((await this.rows(db, "intake_actions", `item_id=? AND state IN (${UNFINISHED_ACTION_STATES.map(() => "?").join(",")})`, [itemId, ...UNFINISHED_ACTION_STATES])).length) return;
|
|
8461
|
+
const data = object(item.data);
|
|
8462
|
+
const ceiling = object(data.capturedCeiling ?? {});
|
|
8463
|
+
const released = redactedCeiling(ceiling, false);
|
|
8464
|
+
if (canonical(released) === canonical(ceiling)) return;
|
|
8465
|
+
await db.query("UPDATE intake_items SET data=? WHERE tenant_id=? AND confidential_scope_id=? AND id=?", canonical({
|
|
8466
|
+
...data,
|
|
8467
|
+
capturedCeiling: released
|
|
8468
|
+
}), this.scope.tenantId, this.scope.confidentialScopeId, itemId);
|
|
8469
|
+
}
|
|
8470
|
+
/** Only an explicit denial hides a row; a failing grant store propagates
|
|
8471
|
+
* instead of silently shrinking the list. */
|
|
8472
|
+
async readableOwn(row) {
|
|
8473
|
+
const capturedCeiling = object(object(row.data).capturedCeiling ?? {});
|
|
8474
|
+
try {
|
|
8475
|
+
return await this.options.authorize({
|
|
8476
|
+
scope: this.scope,
|
|
8477
|
+
operation: "read",
|
|
8478
|
+
capturedCeiling,
|
|
8479
|
+
db: this.db
|
|
8480
|
+
}) === true;
|
|
8481
|
+
} catch (error) {
|
|
8482
|
+
if (error instanceof IntakeAccessDeniedError) return false;
|
|
8483
|
+
throw error;
|
|
8484
|
+
}
|
|
8485
|
+
}
|
|
8000
8486
|
};
|
|
8001
8487
|
async function continueIntakeReview(service, actionId, task) {
|
|
8002
8488
|
await task.assertAuthorized();
|
|
@@ -8024,6 +8510,6 @@ async function continueIntakeReview(service, actionId, task) {
|
|
|
8024
8510
|
return service.applyAction(actionId);
|
|
8025
8511
|
}
|
|
8026
8512
|
//#endregion
|
|
8027
|
-
export {
|
|
8513
|
+
export { listReadGate as _, resolveIntakePolicy as a, require_ajv as c, GenerationSnapshotStaleError as d, GenerationSourceStaleError as f, generationSnapshotRead as g, discoveryTransaction as h, intakeBindingDigest as i, UNFINISHED_ACTION_STATES as l, ProposalsNotConfiguredError as m, IntakeExecutionService as n, feedbackActionTransaction as o, HandlerArgumentsInvalidError as p, continueIntakeReview as r, feedbackDiscoveryContext as s, IntakeAccessDeniedError as t, redactedCeiling as u, __exportAll as v, __toESM as y };
|
|
8028
8514
|
|
|
8029
|
-
//# sourceMappingURL=execution-
|
|
8515
|
+
//# sourceMappingURL=execution-BzVQax0n.js.map
|