@victframework/server 0.1.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.
@@ -0,0 +1,36 @@
1
+ import type { ServerActorContext } from './auth.js';
2
+ /**
3
+ * Stage 06B — the remote Application data/action adapter.
4
+ *
5
+ * Wraps the Stage 05 verified Application data port with the SAME server
6
+ * authorization boundary (APP-010, API-006):
7
+ *
8
+ * - resource queries and mutations preserve the declared resource ID,
9
+ * revision, and release binding — never re-derived or widened;
10
+ * - VICT actions use the same authorization boundary (scopes below the UI);
11
+ * - LOCAL actions are client-local by definition and are REFUSED remotely;
12
+ * - cross-actor and stale-release access fails;
13
+ * - hostile query/filter containers produce structured non-echoing errors.
14
+ */
15
+ export interface RemoteApplicationDataOptions {
16
+ /** The verified Stage 05 application-data port (composed). */
17
+ readonly data: ApplicationDataPortLike;
18
+ /** The actor→resource ownership policy: resource records carry actorId. */
19
+ readonly ownsResource?: (actor: ServerActorContext, resource: {
20
+ readonly actorId?: unknown;
21
+ }) => boolean;
22
+ /** The expected release binding (stale-release denial). */
23
+ readonly expectedReleaseVersion?: string;
24
+ }
25
+ /** The neutral application-data port surface used by the server. */
26
+ export interface ApplicationDataPortLike {
27
+ query(request: Record<string, unknown>): Promise<unknown>;
28
+ mutate(request: Record<string, unknown>): Promise<unknown>;
29
+ }
30
+ /** Hostile-container containment: any throw from a filter object is a stable error. */
31
+ /** The remote resource query boundary. */
32
+ export declare function remoteQuery(actor: ServerActorContext, options: RemoteApplicationDataOptions, input: Record<string, unknown>): Promise<unknown>;
33
+ /** The remote resource mutation boundary. */
34
+ export declare function remoteMutate(actor: ServerActorContext, options: RemoteApplicationDataOptions, input: Record<string, unknown>): Promise<unknown>;
35
+ /** The VICT-action boundary (governed Vict actions through the same server authorization). */
36
+ export declare function remoteAction(actor: ServerActorContext, options: RemoteApplicationDataOptions, input: Record<string, unknown>): Promise<unknown>;
@@ -0,0 +1,108 @@
1
+ import { VictControlError } from '@victframework/runtime';
2
+ /** Bounded string fields on the remote boundary (closed schemas). */
3
+ function bounded(value, field, max = 128) {
4
+ if (typeof value !== 'string' || value.length === 0 || value.length > max) {
5
+ throw new VictControlError('VICT_APPDATA_FIELD_INVALID', `${field} must be a bounded string.`);
6
+ }
7
+ return value;
8
+ }
9
+ /** Hostile-container containment: any throw from a filter object is a stable error. */
10
+ /** The remote resource query boundary. */
11
+ export async function remoteQuery(actor, options, input) {
12
+ const resourceId = bounded(input.resourceId, 'resourceId');
13
+ const releaseVersion = bounded(input.releaseVersion, 'releaseVersion');
14
+ if (options.expectedReleaseVersion !== undefined &&
15
+ releaseVersion !== options.expectedReleaseVersion) {
16
+ throw new VictControlError('VICT_APPDATA_RELEASE_STALE', 'The declared release binding does not match the currently selected release.');
17
+ }
18
+ // The authenticated actor owns the read; hostile filter containers are
19
+ // contained BEFORE they reach the adapter.
20
+ let filters;
21
+ if (input.filters !== undefined) {
22
+ if (typeof input.filters !== 'object' ||
23
+ input.filters === null ||
24
+ Array.isArray(input.filters)) {
25
+ throw new VictControlError('VICT_APPDATA_FILTER_INVALID', 'The filter container must be a plain object.');
26
+ }
27
+ filters = input.filters;
28
+ // Hostile getters/proxies are contained: enumeration failures collapse
29
+ // to the stable structured error without echoing the hostile value.
30
+ try {
31
+ for (const key of Object.keys(filters)) {
32
+ bounded(key, 'filterKey');
33
+ }
34
+ }
35
+ catch (error) {
36
+ if (error instanceof VictControlError) {
37
+ throw error;
38
+ }
39
+ throw new VictControlError('VICT_APPDATA_FILTER_INVALID', 'The filter container could not be read safely.');
40
+ }
41
+ }
42
+ // Hostile containers (throwing getters/proxies) are contained at this
43
+ // boundary: any downstream throw collapses to the stable structured
44
+ // error — raw hostile content never echoes.
45
+ try {
46
+ return await options.data.query({
47
+ kind: 'query',
48
+ resourceId,
49
+ releaseVersion,
50
+ actorId: actor.actorId,
51
+ ...(filters !== undefined ? { filters } : {}),
52
+ });
53
+ }
54
+ catch (error) {
55
+ if (error instanceof VictControlError) {
56
+ throw error;
57
+ }
58
+ throw new VictControlError('VICT_APPDATA_FILTER_INVALID', 'The query could not be processed safely.');
59
+ }
60
+ }
61
+ /** The remote resource mutation boundary. */
62
+ export async function remoteMutate(actor, options, input) {
63
+ const resourceId = bounded(input.resourceId, 'resourceId');
64
+ const releaseVersion = bounded(input.releaseVersion, 'releaseVersion');
65
+ const expectedRevision = bounded(input.expectedRevision ?? '0', 'expectedRevision');
66
+ if (options.expectedReleaseVersion !== undefined &&
67
+ releaseVersion !== options.expectedReleaseVersion) {
68
+ throw new VictControlError('VICT_APPDATA_RELEASE_STALE', 'The declared release binding does not match the currently selected release.');
69
+ }
70
+ const actionKind = bounded(input.actionKind ?? 'mutation', 'actionKind', 32);
71
+ if (actionKind === 'local') {
72
+ // Local/view actions are CLIENT-LOCAL by definition and can never be
73
+ // dispatched through the server boundary.
74
+ throw new VictControlError('VICT_APPDATA_LOCAL_ACTION_DENIED', 'Local view actions are client-local and cannot be dispatched remotely.');
75
+ }
76
+ try {
77
+ return await options.data.mutate({
78
+ kind: 'mutate',
79
+ resourceId,
80
+ releaseVersion,
81
+ actorId: actor.actorId,
82
+ expectedRevision,
83
+ actionKind,
84
+ });
85
+ }
86
+ catch (error) {
87
+ if (error instanceof VictControlError) {
88
+ throw error;
89
+ }
90
+ throw new VictControlError('VICT_APPDATA_FILTER_INVALID', 'The mutation could not be processed safely.');
91
+ }
92
+ }
93
+ /** The VICT-action boundary (governed Vict actions through the same server authorization). */
94
+ export async function remoteAction(actor, options, input) {
95
+ const actionKind = bounded(input.actionKind, 'actionKind', 32);
96
+ if (actionKind === 'local') {
97
+ throw new VictControlError('VICT_APPDATA_LOCAL_ACTION_DENIED', 'Local view actions are client-local and cannot be dispatched remotely.');
98
+ }
99
+ if (actionKind === 'query' || actionKind === 'mutation') {
100
+ return actionKind === 'query'
101
+ ? remoteQuery(actor, options, input)
102
+ : remoteMutate(actor, options, input);
103
+ }
104
+ // capability / signal / navigation actions must reference their exact
105
+ // declared identities; the composition supplies the authorized handler.
106
+ throw new VictControlError('VICT_APPDATA_ACTION_UNAVAILABLE', 'The declared action kind is not composed in this deployment.');
107
+ }
108
+ //# sourceMappingURL=app-remote.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app-remote.js","sourceRoot":"","sources":["../src/app-remote.ts"],"names":[],"mappings":"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"}
package/dist/auth.d.ts ADDED
@@ -0,0 +1,53 @@
1
+ import { type ActorDirectory, type AuthenticatedActorContext } from '@victframework/runtime';
2
+ /**
3
+ * Stage 06B — the authenticated actor boundary (SEC-001, MSTR-007).
4
+ *
5
+ * Authentication and authorization remain DISTINCT: the authenticator only
6
+ * establishes WHO the caller is; scope checks happen below the transport in
7
+ * `@victframework/control` and every command handler. Client-supplied actor ids,
8
+ * roles, scopes, and the Mastra memory identity are NEVER authoritative —
9
+ * the authoritative context is derived ONLY from the server-side actor
10
+ * directory.
11
+ *
12
+ * A deterministic local test authenticator is provided; no production
13
+ * identity-provider integration is claimed.
14
+ */
15
+ /** Stable, non-echoing authentication failure codes. */
16
+ export type AuthErrorCode = 'VICT_AUTH_TOKEN_MISSING' | 'VICT_AUTH_TOKEN_UNKNOWN' | 'VICT_AUTH_MALFORMED';
17
+ /** Structured authentication failure (never echoes the presented token). */
18
+ export declare class AuthenticationError extends Error {
19
+ readonly code: AuthErrorCode;
20
+ constructor(code: AuthErrorCode);
21
+ }
22
+ /**
23
+ * The server-side actor context: the ONLY authoritative identity surface.
24
+ * The Mastra memory identity is derived exclusively from the authenticated
25
+ * VICT actor (`vict-actor-<actorId>`; MSTR-007).
26
+ */
27
+ export interface ServerActorContext extends AuthenticatedActorContext {
28
+ /** Bounded presentation token of the authenticated session (not a secret). */
29
+ readonly presentedTokenKind: 'local-test';
30
+ }
31
+ /**
32
+ * The neutral server-side authentication port. Implementations resolve a
33
+ * transport credential to ONE actor id; the directory supplies the record
34
+ * and the context derivation fails closed on unknown, disabled, malformed,
35
+ * or mismatched actors.
36
+ */
37
+ export interface Authenticator {
38
+ /** Authenticate the transport credential; returns the actor id. */
39
+ authenticate(token: string | undefined): Promise<string>;
40
+ }
41
+ /**
42
+ * Deterministic local test authenticator: fixed token → actorId mapping
43
+ * supplied by operator test configuration. NOT a production identity
44
+ * provider; the composition boundary is identical to a real one.
45
+ */
46
+ export declare function createLocalTestAuthenticator(mapping: Readonly<Record<string, string>>): Authenticator;
47
+ /** Compose the authenticator with the authoritative actor directory. */
48
+ export declare function createServerAuthenticator(options: {
49
+ readonly authenticator: Authenticator;
50
+ readonly directory: ActorDirectory;
51
+ }): {
52
+ resolve(token: string | undefined): Promise<ServerActorContext>;
53
+ };
package/dist/auth.js ADDED
@@ -0,0 +1,44 @@
1
+ import { authenticatedActorContext, assertControlId, } from '@victframework/runtime';
2
+ /** Structured authentication failure (never echoes the presented token). */
3
+ export class AuthenticationError extends Error {
4
+ code;
5
+ constructor(code) {
6
+ super(`Authentication failed (${code}).`);
7
+ this.name = 'AuthenticationError';
8
+ this.code = code;
9
+ }
10
+ }
11
+ /**
12
+ * Deterministic local test authenticator: fixed token → actorId mapping
13
+ * supplied by operator test configuration. NOT a production identity
14
+ * provider; the composition boundary is identical to a real one.
15
+ */
16
+ export function createLocalTestAuthenticator(mapping) {
17
+ // Tokens must be bounded plain strings; hostile inputs fail closed as
18
+ // unknown tokens (never echoed).
19
+ return {
20
+ async authenticate(token) {
21
+ if (typeof token !== 'string' || token.length === 0 || token.length > 256) {
22
+ throw new AuthenticationError('VICT_AUTH_TOKEN_MISSING');
23
+ }
24
+ const actorId = mapping[token];
25
+ if (actorId === undefined || typeof actorId !== 'string') {
26
+ throw new AuthenticationError('VICT_AUTH_TOKEN_UNKNOWN');
27
+ }
28
+ return actorId;
29
+ },
30
+ };
31
+ }
32
+ /** Compose the authenticator with the authoritative actor directory. */
33
+ export function createServerAuthenticator(options) {
34
+ return {
35
+ async resolve(token) {
36
+ const actorId = await options.authenticator.authenticate(token);
37
+ assertControlId(actorId, 'actorId');
38
+ const actor = await options.directory.get(actorId);
39
+ const context = authenticatedActorContext(actor, actorId);
40
+ return { ...context, presentedTokenKind: 'local-test' };
41
+ },
42
+ };
43
+ }
44
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,yBAAyB,EACzB,eAAe,GAIhB,MAAM,wBAAwB,CAAC;AAoBhC,4EAA4E;AAC5E,MAAM,OAAO,mBAAoB,SAAQ,KAAK;IACnC,IAAI,CAAgB;IAC7B,YAAY,IAAmB;QAC7B,KAAK,CAAC,0BAA0B,IAAI,IAAI,CAAC,CAAC;QAC1C,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAC;QAClC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF;AAuBD;;;;GAIG;AACH,MAAM,UAAU,4BAA4B,CAC1C,OAAyC;IAEzC,sEAAsE;IACtE,iCAAiC;IACjC,OAAO;QACL,KAAK,CAAC,YAAY,CAAC,KAAK;YACtB,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC;gBAC1E,MAAM,IAAI,mBAAmB,CAAC,yBAAyB,CAAC,CAAC;YAC3D,CAAC;YACD,MAAM,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;YAC/B,IAAI,OAAO,KAAK,SAAS,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;gBACzD,MAAM,IAAI,mBAAmB,CAAC,yBAAyB,CAAC,CAAC;YAC3D,CAAC;YACD,OAAO,OAAO,CAAC;QACjB,CAAC;KACF,CAAC;AACJ,CAAC;AAED,wEAAwE;AACxE,MAAM,UAAU,yBAAyB,CAAC,OAGzC;IACC,OAAO;QACL,KAAK,CAAC,OAAO,CAAC,KAAyB;YACrC,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,aAAa,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;YAChE,eAAe,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;YACpC,MAAM,KAAK,GAA4B,MAAM,OAAO,CAAC,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YAC5E,MAAM,OAAO,GAAG,yBAAyB,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;YAC1D,OAAO,EAAE,GAAG,OAAO,EAAE,kBAAkB,EAAE,YAAqB,EAAE,CAAC;QACnE,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,192 @@
1
+ import type { AgentControlStores, ControlAuditEvent } from '@victframework/runtime';
2
+ import type { ServerActorContext } from './auth.js';
3
+ /**
4
+ * Stage 06B — the transport-free, versioned command service (API-002).
5
+ *
6
+ * The HTTP transport and the CLI consume the SAME typed dispatcher; no
7
+ * caller bypasses governance. Properties enforced HERE (below the
8
+ * transport, fail closed):
9
+ *
10
+ * - ONE command registry: authorization scope, closed payload field set,
11
+ * and the mutation/idempotency policy are declared TOGETHER per command,
12
+ * so the command list, the scope matrix, and the mutation list cannot
13
+ * silently diverge;
14
+ * - AUTHORIZATION MATRIX: every command's required scope is asserted from
15
+ * the closed scope vocabulary BEFORE any store access;
16
+ * - CLOSED PAYLOAD SCHEMAS: unknown fields and non-object payloads are
17
+ * rejected (never silently converted to `{}`); payloads are canonicalized
18
+ * ONCE into plain data (own enumerable properties only — accessors,
19
+ * proxies that throw, and enumeration traps fail with a stable,
20
+ * non-echoing error) and that SAME canonical form is used for the
21
+ * request digest and for execution;
22
+ * - DURABLE COMMAND IDEMPOTENCY: every state-changing command requires a
23
+ * bounded `Idempotency-Key`; receipts are NAMESPACED by (authenticated
24
+ * actor, command, key) and bind the canonical request digest. A pending
25
+ * receipt carries a durable LEASE (owner + expiry): concurrent
26
+ * duplicates answer `IN_PROGRESS`, a crashed claim's expired lease is
27
+ * taken over on retry (fenced re-execution through the domain's own
28
+ * idempotency), deterministic failures settle `failed` and replay, and
29
+ * retryable infrastructure failures RELEASE the claim instead of being
30
+ * permanently confused with command failure;
31
+ * - SAFE RECEIPT RETENTION: receipts store a per-command SAFE replay
32
+ * projection (stable codes, identifiers, content references) — never the
33
+ * full command response, rationale, application rows, model content, or
34
+ * tool data. An authorized replay result is reconstructed from its
35
+ * authoritative domain when necessary;
36
+ * - stable, structured, non-echoing errors.
37
+ */
38
+ /** The versioned command envelope marker. */
39
+ export declare const VICT_COMMAND_SCHEMA = "vict.command@1";
40
+ /** Closed command names (version 1). */
41
+ export declare const VICT_COMMANDS: readonly ["health.inspect", "compatibility.inspect", "actor.whoami", "changeset.propose", "changeset.revise", "changeset.execute-check", "changeset.attach-evidence", "changeset.decide", "changeset.commit", "changeset.get", "changeset.list", "release.publish", "release.select", "release.rollback", "release.get-selected", "activation.select", "run.cancel", "agent.turn.start", "agent.turn.cancel", "agent.turn.get", "agent.tool.approve", "agent.tool.decline", "stream.inspect", "app.data.query", "app.data.mutate", "app.data.action"];
42
+ export type VictCommandName = (typeof VICT_COMMANDS)[number];
43
+ /**
44
+ * True when the command mutates durable state (idempotency-governed).
45
+ * Derived from the ONE registry — the mutation policy can never silently
46
+ * diverge from the command list.
47
+ */
48
+ export declare function isMutationCommand(command: string): boolean;
49
+ /** A closed, versioned command request. */
50
+ export interface VictCommandRequest {
51
+ readonly command: VictCommandName;
52
+ /** Bounded, command-specific payload (validated per command below). */
53
+ readonly payload: Record<string, unknown>;
54
+ /**
55
+ * Durable mutation idempotency key (REQUIRED for state-changing
56
+ * commands; validated against the closed bounded format).
57
+ */
58
+ readonly idempotencyKey?: string;
59
+ }
60
+ /** A safe command result. */
61
+ export interface VictCommandResult {
62
+ readonly ok: true;
63
+ readonly data: Record<string, unknown>;
64
+ }
65
+ /** A structured command error (safe, non-echoing). */
66
+ export interface VictCommandError {
67
+ readonly ok: false;
68
+ readonly code: string;
69
+ }
70
+ export type VictCommandOutcome = VictCommandResult | VictCommandError;
71
+ export interface VictCommandServiceOptions {
72
+ readonly stores: AgentControlStores;
73
+ /** The composed control-plane service (ChangeSets/releases/audit). */
74
+ readonly controlPlane: ControlPlanePort;
75
+ /** The composed agent-turn service (optional in governance-only deployments). */
76
+ readonly turnService?: TurnServiceLike;
77
+ readonly clock?: () => number;
78
+ /** The remote Application data/action boundary. */
79
+ readonly appData?: AppDataPort;
80
+ /**
81
+ * Durable lease duration for pending idempotency claims (default
82
+ * 60_000 ms). A crashed claimer's lease expires and the key becomes
83
+ * recoverable.
84
+ */
85
+ readonly idempotencyLeaseMs?: number;
86
+ /** The lease owner token (defaults to a per-service instance token). */
87
+ readonly idempotencyOwner?: string;
88
+ }
89
+ /** The subset of AgentTurnService the dispatcher uses. */
90
+ export interface TurnServicePort {
91
+ startTurn(actor: ServerActorContext, input: {
92
+ threadId: string;
93
+ input: string;
94
+ }): Promise<unknown>;
95
+ cancelTurn(actor: ServerActorContext, input: {
96
+ turnId: string;
97
+ reasonCode?: string;
98
+ }): Promise<unknown>;
99
+ getTurn(actor: ServerActorContext, turnId: string): Promise<unknown>;
100
+ decideToolApproval(actor: ServerActorContext, input: {
101
+ approvalId: string;
102
+ decision: 'approved' | 'declined';
103
+ reason?: string;
104
+ }): Promise<unknown>;
105
+ reconcileAfterRestart(): Promise<{
106
+ cancelled: number;
107
+ failed: number;
108
+ pendingApprovals: number;
109
+ }>;
110
+ }
111
+ type TurnServiceLike = TurnServicePort;
112
+ /**
113
+ * The remote Application data/action port (Stage 05 adapter semantics
114
+ * preserved: typed resource queries and mutations across the SAME server
115
+ * authorization boundary). Implemented in `app-remote.ts`.
116
+ */
117
+ export interface AppDataPort {
118
+ query(actor: ServerActorContext, input: Record<string, unknown>): Promise<unknown>;
119
+ mutate(actor: ServerActorContext, input: Record<string, unknown>): Promise<unknown>;
120
+ }
121
+ /**
122
+ * The versioned command dispatcher: transport-free and shared by HTTP and
123
+ * the CLI. Every command re-derives its authorization from the
124
+ * authenticated server context (never from the payload) and mutates only
125
+ * through the durable idempotency policy.
126
+ */
127
+ export declare class VictCommandService {
128
+ #private;
129
+ constructor(options: VictCommandServiceOptions);
130
+ /** Dispatch one command (closed schemas; durable idempotency; safe errors). */
131
+ dispatch(actor: ServerActorContext, request: VictCommandRequest): Promise<VictCommandOutcome>;
132
+ }
133
+ /** The ControlPlanePort: the control-plane surface the server composes. */
134
+ export interface ControlPlanePort {
135
+ propose(actor: ServerActorContext, input: {
136
+ changesetId: string;
137
+ base: {
138
+ kind: 'activation' | 'release';
139
+ subjectId: string;
140
+ expectedVersion: string;
141
+ };
142
+ operations: readonly unknown[];
143
+ rationale: string;
144
+ riskClass: 'low' | 'medium' | 'high';
145
+ requiredApproverCount: number;
146
+ expiresAt: number;
147
+ }): Promise<unknown>;
148
+ revise(actor: ServerActorContext, input: Record<string, unknown>): Promise<unknown>;
149
+ executeChangeSetCheck(actor: ServerActorContext, input: {
150
+ changesetId: string;
151
+ kind: 'validation' | 'simulation';
152
+ }): Promise<unknown>;
153
+ attachValidationEvidence(actor: ServerActorContext, input: {
154
+ changesetId: string;
155
+ runId: string;
156
+ }): Promise<unknown>;
157
+ attachSimulationEvidence(actor: ServerActorContext, input: {
158
+ changesetId: string;
159
+ runId: string;
160
+ }): Promise<unknown>;
161
+ decide(actor: ServerActorContext, input: {
162
+ changesetId: string;
163
+ decision: 'approved' | 'declined';
164
+ }): Promise<unknown>;
165
+ commit(actor: ServerActorContext, input: {
166
+ changesetId: string;
167
+ }): Promise<unknown>;
168
+ decline(actor: ServerActorContext, input: {
169
+ changesetId: string;
170
+ }): Promise<unknown>;
171
+ get(actor: ServerActorContext, changesetId: string): Promise<unknown>;
172
+ list(actor: ServerActorContext): Promise<unknown>;
173
+ publishRelease(actor: ServerActorContext, content: Record<string, unknown>): Promise<unknown>;
174
+ selectRelease(actor: ServerActorContext, input: Record<string, unknown>): Promise<unknown>;
175
+ rollbackRelease(actor: ServerActorContext, input: Record<string, unknown>): Promise<unknown>;
176
+ getSelectedRelease(actor: ServerActorContext, applicationId: string): Promise<unknown>;
177
+ auditTrail(subject: {
178
+ subjectType?: string;
179
+ subjectId?: string;
180
+ }): Promise<readonly ControlAuditEvent[]>;
181
+ selectActivation(actor: ServerActorContext, input: {
182
+ graphId: string;
183
+ activationVersion: string;
184
+ }): Promise<unknown>;
185
+ cancelRun?(input: {
186
+ runId: string;
187
+ actorId: string;
188
+ requestId: string;
189
+ reasonCode: string;
190
+ }): Promise<unknown>;
191
+ }
192
+ export {};