@unson/brainbase-mcp 0.10.15 → 0.12.0
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/contracts/judgment-value-proof/schema.json +19 -1
- package/dist/execution-authority-http.d.ts +4 -2
- package/dist/execution-authority-http.js +101 -6
- package/dist/execution-authority.d.ts +89 -8
- package/dist/execution-authority.js +360 -127
- package/dist/judgment-value-proof.d.ts +15 -0
- package/dist/judgment-value-proof.js +17 -0
- package/dist/knowledge-promotion.d.ts +61 -3
- package/dist/knowledge-promotion.js +112 -1
- package/dist/local-web-host.d.ts +4 -4
- package/dist/local-web-host.js +137 -12
- package/dist/server-support.d.ts +140 -0
- package/dist/server-support.js +158 -0
- package/package.json +6 -2
- package/ui/graph-registry-view.js +27 -1
- package/ui/local-web-shell.js +80 -8
- package/ui/project-workspace.js +1 -1
- package/ui/value-proof-review.css +54 -0
- package/ui/value-proof-review.js +127 -5
- package/ui/world-model-view.css +61 -0
- package/ui/world-model-view.js +215 -10
|
@@ -154,7 +154,25 @@
|
|
|
154
154
|
"entity_id": { "type": "string", "minLength": 1 },
|
|
155
155
|
"application": { "type": "string", "minLength": 1 },
|
|
156
156
|
"layer": { "enum": ["philosophy", "objective", "world_model", "method", "constraint", "other"] },
|
|
157
|
-
"version": { "type": ["string", "null"] }
|
|
157
|
+
"version": { "type": ["string", "null"] },
|
|
158
|
+
"source": {
|
|
159
|
+
"oneOf": [
|
|
160
|
+
{ "type": "null" },
|
|
161
|
+
{ "$ref": "#/$defs/basis_source" }
|
|
162
|
+
]
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
"basis_source": {
|
|
167
|
+
"type": "object",
|
|
168
|
+
"additionalProperties": false,
|
|
169
|
+
"required": ["kind", "entity_id", "entity_type"],
|
|
170
|
+
"properties": {
|
|
171
|
+
"kind": { "const": "local_graph" },
|
|
172
|
+
"entity_id": { "type": "string", "minLength": 1 },
|
|
173
|
+
"entity_type": { "enum": ["person", "org", "project", "decision"] },
|
|
174
|
+
"version": { "type": ["string", "null"] },
|
|
175
|
+
"digest": { "type": ["string", "null"] }
|
|
158
176
|
}
|
|
159
177
|
},
|
|
160
178
|
"inheritance_source": {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
2
|
-
import { ExecutionAuthorityService, type ExecutionProblemReference } from './execution-authority.js';
|
|
2
|
+
import { ExecutionAuthorityService, type ExecutionAttribution, type ExecutionProblemReference } from './execution-authority.js';
|
|
3
3
|
type MaybePromise<T> = T | Promise<T>;
|
|
4
4
|
/** Context resolved by the host after authentication and tenant selection. */
|
|
5
5
|
export interface TrustedExecutionAuthorityRequestContext {
|
|
@@ -8,9 +8,11 @@ export interface TrustedExecutionAuthorityRequestContext {
|
|
|
8
8
|
scopeId: string;
|
|
9
9
|
/** A non-empty value means that the host already verified the mutation origin. */
|
|
10
10
|
verifiedMutationOrigin?: string;
|
|
11
|
+
/** Resolved by the host from authentication. Request bodies cannot supply it. */
|
|
12
|
+
attribution?: ExecutionAttribution;
|
|
11
13
|
}
|
|
12
14
|
/** The factory is the boundary where a host binds a service to its trusted context. */
|
|
13
|
-
export type ExecutionAuthorityServiceFactory = (context: TrustedExecutionAuthorityRequestContext) => MaybePromise<Pick<ExecutionAuthorityService, 'start' | 'readIntent'
|
|
15
|
+
export type ExecutionAuthorityServiceFactory = (context: TrustedExecutionAuthorityRequestContext) => MaybePromise<Pick<ExecutionAuthorityService, 'start' | 'readIntent'> & Partial<Pick<ExecutionAuthorityService, 'register' | 'reportEffect'>>>;
|
|
14
16
|
export interface ExecutionAuthorityMutationVerificationInput {
|
|
15
17
|
request: IncomingMessage;
|
|
16
18
|
context: TrustedExecutionAuthorityRequestContext;
|
|
@@ -21,6 +21,19 @@ const startRequestSchema = z.object({
|
|
|
21
21
|
constraintRefs: z.array(boundedText).max(128),
|
|
22
22
|
problem: problemSchema,
|
|
23
23
|
}).strict();
|
|
24
|
+
const attributionSchema = z.object({
|
|
25
|
+
mode: z.enum(['person', 'delegated_service', 'service']),
|
|
26
|
+
servicePrincipal: boundedText.optional(),
|
|
27
|
+
delegationRef: boundedText.optional(),
|
|
28
|
+
correlationId: boundedText.optional(),
|
|
29
|
+
}).strict();
|
|
30
|
+
const effectReportSchema = z.object({
|
|
31
|
+
tenantId: boundedText,
|
|
32
|
+
principal: boundedText,
|
|
33
|
+
scopeId: boundedText,
|
|
34
|
+
status: z.enum(['started', 'unknown']),
|
|
35
|
+
capabilityDigest: boundedText,
|
|
36
|
+
}).strict();
|
|
24
37
|
class HttpInputError extends Error {
|
|
25
38
|
constructor(message) {
|
|
26
39
|
super(message);
|
|
@@ -141,16 +154,21 @@ function matchRoute(path, basePath) {
|
|
|
141
154
|
const suffix = normalized.slice(prefix.length);
|
|
142
155
|
if (suffix === 'start')
|
|
143
156
|
return { operation: 'start' };
|
|
157
|
+
if (suffix === 'register')
|
|
158
|
+
return { operation: 'register' };
|
|
144
159
|
const intentPrefix = 'intents/';
|
|
145
160
|
if (suffix.startsWith(intentPrefix)) {
|
|
146
|
-
const
|
|
161
|
+
const rest = suffix.slice(intentPrefix.length);
|
|
162
|
+
const reportSuffix = '/effect';
|
|
163
|
+
const operation = rest.endsWith(reportSuffix) ? 'report_effect' : 'read';
|
|
164
|
+
const encodedOperationId = operation === 'report_effect' ? rest.slice(0, -reportSuffix.length) : rest;
|
|
147
165
|
if (encodedOperationId.length === 0 || encodedOperationId.includes('/'))
|
|
148
166
|
return { operation: 'unknown' };
|
|
149
167
|
try {
|
|
150
|
-
return { operation
|
|
168
|
+
return { operation, operationId: decodeURIComponent(encodedOperationId) };
|
|
151
169
|
}
|
|
152
170
|
catch {
|
|
153
|
-
return { operation
|
|
171
|
+
return { operation, operationId: undefined };
|
|
154
172
|
}
|
|
155
173
|
}
|
|
156
174
|
return { operation: 'unknown' };
|
|
@@ -168,6 +186,21 @@ function assertContext(context) {
|
|
|
168
186
|
|| !boundedText.safeParse(context.scopeId).success) {
|
|
169
187
|
throw new HttpInputError('Authenticated execution authority context is required');
|
|
170
188
|
}
|
|
189
|
+
if (context.attribution !== undefined && !attributionIsConsistent(context.attribution, context.principal)) {
|
|
190
|
+
throw new HttpInputError('Authenticated execution attribution is inconsistent');
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
/** Mirrors the service rules so an inconsistent host context is an authentication failure. */
|
|
194
|
+
function attributionIsConsistent(value, principal) {
|
|
195
|
+
const parsed = attributionSchema.safeParse(value);
|
|
196
|
+
if (!parsed.success)
|
|
197
|
+
return false;
|
|
198
|
+
const { mode, servicePrincipal, delegationRef } = parsed.data;
|
|
199
|
+
if (mode === 'person')
|
|
200
|
+
return servicePrincipal === undefined && delegationRef === undefined;
|
|
201
|
+
if (servicePrincipal === undefined || delegationRef === undefined)
|
|
202
|
+
return false;
|
|
203
|
+
return mode === 'delegated_service' ? servicePrincipal !== principal : servicePrincipal === principal;
|
|
171
204
|
}
|
|
172
205
|
function bindTrustedIdentity(body, context) {
|
|
173
206
|
if (!isRecord(body))
|
|
@@ -201,6 +234,18 @@ function parseStartBody(body, context) {
|
|
|
201
234
|
throw new HttpInputError(parsed.error.issues[0]?.message ?? 'Request body is invalid');
|
|
202
235
|
return parsed.data;
|
|
203
236
|
}
|
|
237
|
+
function parseEffectReportBody(body, context, operationId) {
|
|
238
|
+
const bound = bindTrustedIdentity(body, context);
|
|
239
|
+
if (hasOwn(bound, 'operationId')) {
|
|
240
|
+
if (bound.operationId !== operationId)
|
|
241
|
+
throw new HttpInputError('operationId does not match the request path');
|
|
242
|
+
delete bound.operationId;
|
|
243
|
+
}
|
|
244
|
+
const parsed = effectReportSchema.safeParse(bound);
|
|
245
|
+
if (!parsed.success)
|
|
246
|
+
throw new HttpInputError(parsed.error.issues[0]?.message ?? 'Request body is invalid');
|
|
247
|
+
return { ...parsed.data, operationId };
|
|
248
|
+
}
|
|
204
249
|
function serviceError(error) {
|
|
205
250
|
if (!(error instanceof ExecutionAuthorityError)) {
|
|
206
251
|
return { status: 500, code: 'internal_error', message: 'Execution authority operation failed' };
|
|
@@ -208,6 +253,7 @@ function serviceError(error) {
|
|
|
208
253
|
const status = (() => {
|
|
209
254
|
switch (error.code) {
|
|
210
255
|
case 'scope_mismatch':
|
|
256
|
+
case 'capability_mismatch':
|
|
211
257
|
case 'authority_revoked':
|
|
212
258
|
case 'authority_expired':
|
|
213
259
|
case 'approval_revoked':
|
|
@@ -224,7 +270,12 @@ function serviceError(error) {
|
|
|
224
270
|
case 'approval_unknown':
|
|
225
271
|
case 'constraint_unknown':
|
|
226
272
|
case 'reservation_unknown':
|
|
273
|
+
case 'store_unavailable':
|
|
227
274
|
return 503;
|
|
275
|
+
case 'intent_not_found':
|
|
276
|
+
return 404;
|
|
277
|
+
case 'effect_unconfigured':
|
|
278
|
+
return 501;
|
|
228
279
|
case 'operation_conflict':
|
|
229
280
|
case 'revision_conflict':
|
|
230
281
|
case 'fencing_unsupported':
|
|
@@ -247,7 +298,9 @@ function writeError(response, status, code, message, headers = {}) {
|
|
|
247
298
|
writeJson(response, status, { error: { code, message } }, headers);
|
|
248
299
|
}
|
|
249
300
|
function methodAllowed(operation, method) {
|
|
250
|
-
|
|
301
|
+
if (operation === 'read')
|
|
302
|
+
return method === 'GET';
|
|
303
|
+
return operation === 'unknown' ? false : method === 'POST';
|
|
251
304
|
}
|
|
252
305
|
async function verifyMutationRequest(options, request, context) {
|
|
253
306
|
if (typeof context.verifiedMutationOrigin === 'string' && context.verifiedMutationOrigin.trim().length > 0)
|
|
@@ -333,6 +386,38 @@ export function createExecutionAuthorityHttpHandler(options) {
|
|
|
333
386
|
}
|
|
334
387
|
return true;
|
|
335
388
|
}
|
|
389
|
+
const requestOptions = context.attribution ? { attribution: context.attribution } : {};
|
|
390
|
+
if (route.operation === 'report_effect') {
|
|
391
|
+
const parsedOperationId = boundedText.safeParse(route.operationId);
|
|
392
|
+
let report;
|
|
393
|
+
try {
|
|
394
|
+
if (!parsedOperationId.success)
|
|
395
|
+
throw new HttpInputError('operationId is invalid');
|
|
396
|
+
report = parseEffectReportBody(body, context, parsedOperationId.data);
|
|
397
|
+
}
|
|
398
|
+
catch (error) {
|
|
399
|
+
writeError(response, 400, 'invalid_request', error instanceof Error ? error.message : 'Request body is invalid');
|
|
400
|
+
return true;
|
|
401
|
+
}
|
|
402
|
+
if (!await verifyMutationRequest(options, request, context)) {
|
|
403
|
+
writeError(response, 403, 'mutation_origin_unverified', 'Mutation origin could not be verified');
|
|
404
|
+
return true;
|
|
405
|
+
}
|
|
406
|
+
try {
|
|
407
|
+
const service = await options.serviceFactory(context);
|
|
408
|
+
if (typeof service.reportEffect !== 'function') {
|
|
409
|
+
writeError(response, 501, 'operation_unconfigured', 'This host does not accept effect reports');
|
|
410
|
+
return true;
|
|
411
|
+
}
|
|
412
|
+
const intent = await service.reportEffect(report);
|
|
413
|
+
writeJson(response, 200, { action: 'report_effect', operationId: report.operationId, result: { intent } });
|
|
414
|
+
}
|
|
415
|
+
catch (error) {
|
|
416
|
+
const failure = serviceError(error);
|
|
417
|
+
writeError(response, failure.status, failure.code, failure.message);
|
|
418
|
+
}
|
|
419
|
+
return true;
|
|
420
|
+
}
|
|
336
421
|
let parsed;
|
|
337
422
|
try {
|
|
338
423
|
parsed = parseStartBody(body, context);
|
|
@@ -347,8 +432,18 @@ export function createExecutionAuthorityHttpHandler(options) {
|
|
|
347
432
|
}
|
|
348
433
|
try {
|
|
349
434
|
const service = await options.serviceFactory(context);
|
|
350
|
-
|
|
351
|
-
|
|
435
|
+
if (route.operation === 'register') {
|
|
436
|
+
if (typeof service.register !== 'function') {
|
|
437
|
+
writeError(response, 501, 'operation_unconfigured', 'This host does not register executions');
|
|
438
|
+
return true;
|
|
439
|
+
}
|
|
440
|
+
const result = await service.register(parsed, requestOptions);
|
|
441
|
+
writeJson(response, 200, { action: 'register', operationId: parsed.operationId, result });
|
|
442
|
+
}
|
|
443
|
+
else {
|
|
444
|
+
const result = await service.start(parsed, requestOptions);
|
|
445
|
+
writeJson(response, 200, { action: 'start', operationId: parsed.operationId, result });
|
|
446
|
+
}
|
|
352
447
|
}
|
|
353
448
|
catch (error) {
|
|
354
449
|
const failure = serviceError(error);
|
|
@@ -14,6 +14,23 @@ export interface ExecutionStartInput {
|
|
|
14
14
|
/** Evidence reference only. It never grants execution permission. */
|
|
15
15
|
problem: ExecutionProblemReference;
|
|
16
16
|
}
|
|
17
|
+
/**
|
|
18
|
+
* Who started the execution, as resolved by the host from authenticated
|
|
19
|
+
* context. Request bodies never supply it.
|
|
20
|
+
*/
|
|
21
|
+
export type ExecutionAttributionMode = 'person' | 'delegated_service' | 'service';
|
|
22
|
+
export interface ExecutionAttribution {
|
|
23
|
+
/** person: the principal acted directly. delegated_service: a service acted for the principal. service: an automated job acted on its own authority. */
|
|
24
|
+
mode: ExecutionAttributionMode;
|
|
25
|
+
/** Authenticated service principal. Required unless mode is person. */
|
|
26
|
+
servicePrincipal?: string;
|
|
27
|
+
/** Verified delegation, or for mode service the authority grant that the job relies on. */
|
|
28
|
+
delegationRef?: string;
|
|
29
|
+
correlationId?: string;
|
|
30
|
+
}
|
|
31
|
+
export interface ExecutionRequestOptions {
|
|
32
|
+
attribution?: ExecutionAttribution;
|
|
33
|
+
}
|
|
17
34
|
export type ExecutionCheckKind = 'authority' | 'approval' | 'constraint' | 'reservation';
|
|
18
35
|
export type ExecutionVerificationPhase = 'initial' | 'final';
|
|
19
36
|
export type ExecutionCheckStatus = 'approved' | 'revoked' | 'expired' | 'unknown';
|
|
@@ -23,6 +40,8 @@ export interface ExecutionCheckRequest extends ExecutionStartInput {
|
|
|
23
40
|
phase: ExecutionVerificationPhase;
|
|
24
41
|
/** The revision returned by the first check. Providers may fence on it. */
|
|
25
42
|
expectedRevision?: string;
|
|
43
|
+
/** Present when the host resolved an attribution; providers verify delegation with it. */
|
|
44
|
+
attribution?: ExecutionAttribution;
|
|
26
45
|
}
|
|
27
46
|
export interface ExecutionCheckResult {
|
|
28
47
|
status: ExecutionCheckStatus;
|
|
@@ -82,6 +101,7 @@ export interface ExecutionStartCapability {
|
|
|
82
101
|
scopeId: string;
|
|
83
102
|
issuedAt: string;
|
|
84
103
|
expiresAt: string;
|
|
104
|
+
attribution?: ExecutionAttribution;
|
|
85
105
|
checks: Record<ExecutionCheckKind, {
|
|
86
106
|
revision: string;
|
|
87
107
|
fencingToken: string;
|
|
@@ -91,7 +111,8 @@ export interface ExecutionStartCapability {
|
|
|
91
111
|
export interface ExecutionEffectResult {
|
|
92
112
|
status: 'started' | 'unknown';
|
|
93
113
|
}
|
|
94
|
-
|
|
114
|
+
/** registered: checks passed and a permit was issued; the effect owner has not reported yet. */
|
|
115
|
+
export type ExecutionIntentPhase = 'recovery_required' | 'reservation_marked' | 'registered' | 'started' | 'unknown' | 'rejected';
|
|
95
116
|
export type ExecutionIntentEffectStatus = 'not_started' | 'started' | 'unknown';
|
|
96
117
|
export type ExecutionIntentReservationMarkStatus = 'not_started' | 'started' | 'unknown';
|
|
97
118
|
export interface ExecutionCheckRecord {
|
|
@@ -102,7 +123,7 @@ export interface ExecutionCheckRecord {
|
|
|
102
123
|
expiresAt?: string;
|
|
103
124
|
evidence: Record<string, string>;
|
|
104
125
|
}
|
|
105
|
-
export type ExecutionAuthorityErrorCode = 'validation_error' | 'authority_revoked' | 'authority_expired' | 'authority_unknown' | 'approval_revoked' | 'approval_expired' | 'approval_unknown' | 'constraint_revoked' | 'constraint_expired' | 'constraint_unknown' | 'reservation_revoked' | 'reservation_expired' | 'reservation_unknown' | 'read_revoked' | 'read_expired' | 'read_unknown' | 'revision_conflict' | 'fencing_unsupported' | 'capability_expired' | 'operation_conflict' | 'scope_mismatch' | 'recovery_required' | 'reservation_mark_unknown' | 'effect_unknown' | 'ledger_invalid';
|
|
126
|
+
export type ExecutionAuthorityErrorCode = 'validation_error' | 'authority_revoked' | 'authority_expired' | 'authority_unknown' | 'approval_revoked' | 'approval_expired' | 'approval_unknown' | 'constraint_revoked' | 'constraint_expired' | 'constraint_unknown' | 'reservation_revoked' | 'reservation_expired' | 'reservation_unknown' | 'read_revoked' | 'read_expired' | 'read_unknown' | 'revision_conflict' | 'fencing_unsupported' | 'capability_expired' | 'operation_conflict' | 'scope_mismatch' | 'recovery_required' | 'reservation_mark_unknown' | 'effect_unknown' | 'effect_unconfigured' | 'capability_mismatch' | 'intent_not_found' | 'store_unavailable' | 'ledger_invalid';
|
|
106
127
|
export interface ExecutionIntentFailure {
|
|
107
128
|
code: ExecutionAuthorityErrorCode;
|
|
108
129
|
reason: string;
|
|
@@ -120,6 +141,7 @@ export interface ExecutionIntentRecord {
|
|
|
120
141
|
constraintRefs: string[];
|
|
121
142
|
problem: ExecutionProblemReference;
|
|
122
143
|
payloadDigest: string;
|
|
144
|
+
attribution?: ExecutionAttribution;
|
|
123
145
|
phase: ExecutionIntentPhase;
|
|
124
146
|
reservationMark: ExecutionIntentReservationMarkStatus;
|
|
125
147
|
effect: {
|
|
@@ -133,6 +155,8 @@ export interface ExecutionIntentRecord {
|
|
|
133
155
|
capability?: {
|
|
134
156
|
expiresAt: string;
|
|
135
157
|
digest: string;
|
|
158
|
+
/** Stored only for registrations so a lost response can be replayed. Never returned to readers. */
|
|
159
|
+
grant?: ExecutionStartCapability;
|
|
136
160
|
};
|
|
137
161
|
failure?: ExecutionIntentFailure;
|
|
138
162
|
createdAt: string;
|
|
@@ -146,14 +170,51 @@ export interface ExecutionStartResult {
|
|
|
146
170
|
};
|
|
147
171
|
replayed: boolean;
|
|
148
172
|
}
|
|
173
|
+
export interface ExecutionRegistrationResult {
|
|
174
|
+
intent: ExecutionIntentRecord;
|
|
175
|
+
capability: ExecutionStartCapability;
|
|
176
|
+
capabilityDigest: string;
|
|
177
|
+
replayed: boolean;
|
|
178
|
+
}
|
|
179
|
+
export interface ExecutionEffectReport {
|
|
180
|
+
operationId: string;
|
|
181
|
+
tenantId: string;
|
|
182
|
+
principal: string;
|
|
183
|
+
scopeId: string;
|
|
184
|
+
/** Binds the report to the issued permit. It is not a secret. */
|
|
185
|
+
capabilityDigest: string;
|
|
186
|
+
status: 'started' | 'unknown';
|
|
187
|
+
}
|
|
188
|
+
export interface ExecutionIntentKey {
|
|
189
|
+
tenantId: string;
|
|
190
|
+
operationId: string;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Durable intent storage. A host injects a database adapter here; the default
|
|
194
|
+
* is the local sidecar. insert and update must each be atomic per key.
|
|
195
|
+
*/
|
|
196
|
+
export interface ExecutionIntentStore {
|
|
197
|
+
initialize?(): Promise<void>;
|
|
198
|
+
read(key: ExecutionIntentKey): Promise<ExecutionIntentRecord | undefined>;
|
|
199
|
+
/** Stores the intent when the key is absent; otherwise returns the existing record with created false. */
|
|
200
|
+
insert(intent: ExecutionIntentRecord): Promise<{
|
|
201
|
+
intent: ExecutionIntentRecord;
|
|
202
|
+
created: boolean;
|
|
203
|
+
}>;
|
|
204
|
+
/** Writes mutate(current) atomically. Returns undefined when absent. A throwing mutate writes nothing and rethrows. */
|
|
205
|
+
update(key: ExecutionIntentKey, mutate: (current: ExecutionIntentRecord) => ExecutionIntentRecord): Promise<ExecutionIntentRecord | undefined>;
|
|
206
|
+
}
|
|
149
207
|
export interface ExecutionAuthorityServiceOptions {
|
|
150
|
-
|
|
208
|
+
/** Used for the default sidecar store when store is omitted. */
|
|
209
|
+
dataDir?: string;
|
|
210
|
+
store?: ExecutionIntentStore;
|
|
151
211
|
authority: ExecutionAuthorityPort;
|
|
152
212
|
approval: ExecutionApprovalPort;
|
|
153
213
|
constraint: ExecutionConstraintPort;
|
|
154
214
|
reservation: ExecutionReservationPort;
|
|
155
215
|
readAccess: ExecutionReadAccessPort;
|
|
156
|
-
effect
|
|
216
|
+
/** Required for start. Registration-only hosts leave the effect to its owner. */
|
|
217
|
+
effect?: ExecutionEffectPort;
|
|
157
218
|
clock?: () => Date;
|
|
158
219
|
sidecarPath?: string;
|
|
159
220
|
}
|
|
@@ -163,11 +224,11 @@ export declare class ExecutionAuthorityError extends Error {
|
|
|
163
224
|
}
|
|
164
225
|
/**
|
|
165
226
|
* Persists the execution intent before crossing either the reservation or
|
|
166
|
-
* external-effect boundary. The reservation and intent
|
|
227
|
+
* external-effect boundary. The reservation and intent stores are separate
|
|
167
228
|
* transactions; callers must reconcile recovery_required/unknown states.
|
|
168
229
|
*/
|
|
169
230
|
export declare class ExecutionAuthorityService {
|
|
170
|
-
private readonly
|
|
231
|
+
private readonly store;
|
|
171
232
|
private readonly authority;
|
|
172
233
|
private readonly approval;
|
|
173
234
|
private readonly constraint;
|
|
@@ -175,11 +236,23 @@ export declare class ExecutionAuthorityService {
|
|
|
175
236
|
private readonly readAccess;
|
|
176
237
|
private readonly effect;
|
|
177
238
|
private readonly clock;
|
|
178
|
-
private readonly sidecarPath;
|
|
179
239
|
constructor(options: ExecutionAuthorityServiceOptions);
|
|
180
240
|
initialize(): Promise<void>;
|
|
181
|
-
|
|
241
|
+
/** Runs the checks, marks the reservation, and calls the in-process effect port. */
|
|
242
|
+
start(input: ExecutionStartInput, options?: ExecutionRequestOptions): Promise<ExecutionStartResult>;
|
|
243
|
+
/**
|
|
244
|
+
* Registers the execution: runs the checks, marks the reservation, and
|
|
245
|
+
* issues the permit. The effect owner reports the outcome with reportEffect.
|
|
246
|
+
*/
|
|
247
|
+
register(input: ExecutionStartInput, options?: ExecutionRequestOptions): Promise<ExecutionRegistrationResult>;
|
|
248
|
+
/** Records the effect owner's outcome for a registered execution. */
|
|
249
|
+
reportEffect(report: ExecutionEffectReport): Promise<ExecutionIntentRecord>;
|
|
182
250
|
readIntent(input: Pick<ExecutionStartInput, 'operationId' | 'tenantId' | 'principal' | 'scopeId'>): Promise<ExecutionIntentRecord | undefined>;
|
|
251
|
+
/**
|
|
252
|
+
* Shared path for start and register: initial checks, durable intent,
|
|
253
|
+
* fenced final checks, permit, and the reservation mark.
|
|
254
|
+
*/
|
|
255
|
+
private prepare;
|
|
183
256
|
private findIntent;
|
|
184
257
|
private verifyReadAccess;
|
|
185
258
|
private recordIntent;
|
|
@@ -188,8 +261,16 @@ export declare class ExecutionAuthorityService {
|
|
|
188
261
|
private persistRecovery;
|
|
189
262
|
private verifyAll;
|
|
190
263
|
private handleExisting;
|
|
264
|
+
private replayRegistration;
|
|
265
|
+
/** Store failures are never success and never switch to another ledger. */
|
|
266
|
+
private storeCall;
|
|
191
267
|
private now;
|
|
192
268
|
}
|
|
269
|
+
/** The default store: a JSON sidecar under the local SSOT lock. */
|
|
270
|
+
export declare function createSidecarExecutionIntentStore(options: {
|
|
271
|
+
dataDir: string;
|
|
272
|
+
sidecarPath?: string;
|
|
273
|
+
}): ExecutionIntentStore;
|
|
193
274
|
/**
|
|
194
275
|
* Binds the existing reservation store to the execution port without calling
|
|
195
276
|
* it from inside the execution sidecar transaction. The store remains the
|