@praesidias/sdk 0.0.0-stage → 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.
- package/LICENSE +202 -0
- package/README.md +82 -2
- package/dist/index.cjs +2021 -0
- package/dist/index.d.cts +671 -0
- package/dist/index.d.ts +671 -0
- package/dist/index.js +1964 -0
- package/package.json +61 -3
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,671 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Faults: Praesidias could not give a clear answer. They are thrown, and whenever one is thrown
|
|
3
|
+
* the guarded action has NOT run. A policy answer (blocked, needs approval) is never an error;
|
|
4
|
+
* it is a result.
|
|
5
|
+
*
|
|
6
|
+
* No error ever carries the API key, a request body or a reply body: only a code, a status and
|
|
7
|
+
* a correlation id.
|
|
8
|
+
*/
|
|
9
|
+
declare class PraesidiasError extends Error {
|
|
10
|
+
/** A stable, machine-readable code. */
|
|
11
|
+
readonly code: string;
|
|
12
|
+
readonly status: number | null;
|
|
13
|
+
readonly correlationId: string | null;
|
|
14
|
+
constructor(message: string, details: {
|
|
15
|
+
code: string;
|
|
16
|
+
status?: number | null;
|
|
17
|
+
correlationId?: string | null;
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
/** Praesidias could not be reached, timed out, or answered in a way that cannot be trusted. */
|
|
21
|
+
declare class PraesidiasUnavailableError extends PraesidiasError {
|
|
22
|
+
}
|
|
23
|
+
/** The key was refused (401 or 403). Not retried. */
|
|
24
|
+
declare class PraesidiasAuthError extends PraesidiasError {
|
|
25
|
+
}
|
|
26
|
+
/** The SDK is set up in a way that cannot work: no key, an insecure address, the kit not enabled. */
|
|
27
|
+
declare class PraesidiasConfigError extends PraesidiasError {
|
|
28
|
+
}
|
|
29
|
+
/** Praesidias refused the request itself (a 4xx that is not about the key). */
|
|
30
|
+
declare class PraesidiasRequestError extends PraesidiasError {
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
type LogEvent = {
|
|
34
|
+
event: 'request' | 'retry' | 'response' | 'failure';
|
|
35
|
+
method: string;
|
|
36
|
+
path: string;
|
|
37
|
+
attempt: number;
|
|
38
|
+
status?: number;
|
|
39
|
+
code?: string;
|
|
40
|
+
correlationId: string;
|
|
41
|
+
durationMs?: number;
|
|
42
|
+
};
|
|
43
|
+
/** The header a workload identity token is sent in (workload binding). */
|
|
44
|
+
declare const WORKLOAD_TOKEN_HEADER = "x-praesidias-workload-token";
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Telling Praesidias which tools an agent has (POST /agent/tools), so the Console can show them
|
|
48
|
+
* and say which have a rule. One shape for every reporter: the SDK, the adapters, the gateways
|
|
49
|
+
* and the command line. Only names and the TYPES of inputs are sent, never values.
|
|
50
|
+
*/
|
|
51
|
+
/** Who is reporting. The core accepts exactly these. */
|
|
52
|
+
type ToolSource = 'claude_code' | 'codex' | 'mcp' | 'sdk' | 'adapter' | 'gateway';
|
|
53
|
+
type ToolInputType = 'string' | 'number' | 'boolean' | 'array' | 'object';
|
|
54
|
+
type ReportedTool = {
|
|
55
|
+
/** The tool's own name, as the agent knows it. */
|
|
56
|
+
name: string;
|
|
57
|
+
/** The action Praesidias decides on when the tool is used. */
|
|
58
|
+
action: string;
|
|
59
|
+
description?: string | null;
|
|
60
|
+
/** The tool's inputs by name: their types only. */
|
|
61
|
+
inputs?: Record<string, {
|
|
62
|
+
type: ToolInputType;
|
|
63
|
+
description?: string;
|
|
64
|
+
}>;
|
|
65
|
+
};
|
|
66
|
+
/** At most this many tools in one request; more are sent in several. */
|
|
67
|
+
declare const TOOLS_PER_REPORT = 500;
|
|
68
|
+
/**
|
|
69
|
+
* The tools as the core accepts them: anything it would refuse is left out or shortened here,
|
|
70
|
+
* so one odd tool never loses the whole list. The same action twice keeps the last.
|
|
71
|
+
*/
|
|
72
|
+
declare function toolsForReport(tools: readonly ReportedTool[]): ReportedTool[];
|
|
73
|
+
/** A JSON Schema's top-level properties as input types (MCP's inputSchema, a tool's parameters). */
|
|
74
|
+
declare function inputsOfSchema(schema: unknown): NonNullable<ReportedTool['inputs']>;
|
|
75
|
+
|
|
76
|
+
type Resource = {
|
|
77
|
+
type: string;
|
|
78
|
+
id?: string | null;
|
|
79
|
+
};
|
|
80
|
+
type PraesidiasOptions = {
|
|
81
|
+
/** The agent's key. Defaults to the PRAESIDIAS_API_KEY environment variable. Never hard-code it. */
|
|
82
|
+
apiKey?: string;
|
|
83
|
+
/** Where Praesidias is. Defaults to PRAESIDIAS_URL. https, except on localhost. */
|
|
84
|
+
baseUrl?: string;
|
|
85
|
+
/** The system this agent acts on, as your areas name it (for example "payments"). */
|
|
86
|
+
system?: string;
|
|
87
|
+
/** The Console's address, for links in results. Defaults to PRAESIDIAS_CONSOLE_URL. */
|
|
88
|
+
consoleUrl?: string;
|
|
89
|
+
/** Per-request timeout. Default 10 seconds. */
|
|
90
|
+
timeoutMs?: number;
|
|
91
|
+
/** Retries on network errors and 429/502/503/504 only. Default 2. */
|
|
92
|
+
retries?: number;
|
|
93
|
+
/** What to do when an action needs approval. Default "wait". */
|
|
94
|
+
onApproval?: 'wait' | 'return';
|
|
95
|
+
/** How long "wait" waits. Default 15 minutes. */
|
|
96
|
+
approvalTimeoutMs?: number;
|
|
97
|
+
/** Called for each request, retry and reply. It is never given the key or a body. */
|
|
98
|
+
logger?: (event: LogEvent) => void;
|
|
99
|
+
/** Replace fetch (for tests or a proxy). */
|
|
100
|
+
fetch?: typeof fetch;
|
|
101
|
+
/** Accept plain http for an address that is not localhost. Do not use in production. */
|
|
102
|
+
allowInsecure?: boolean;
|
|
103
|
+
/**
|
|
104
|
+
* When this agent is bound to its workload in the Console: a file the platform keeps a fresh
|
|
105
|
+
* identity token in (for Kubernetes, the projected service account token's path). Read on every
|
|
106
|
+
* request. Defaults to PRAESIDIAS_WORKLOAD_TOKEN_FILE.
|
|
107
|
+
*/
|
|
108
|
+
workloadTokenFile?: string;
|
|
109
|
+
/**
|
|
110
|
+
* The workload identity token itself, or a function that returns a fresh one (or null to send
|
|
111
|
+
* none with that request). Use instead of `workloadTokenFile` when the token does not come from
|
|
112
|
+
* a file. Given, it takes the place of PRAESIDIAS_WORKLOAD_TOKEN_FILE.
|
|
113
|
+
*/
|
|
114
|
+
workloadToken?: string | (() => string | null | undefined | Promise<string | null | undefined>);
|
|
115
|
+
/** @internal Timings, shortened by tests. */
|
|
116
|
+
timing?: Partial<Timing>;
|
|
117
|
+
};
|
|
118
|
+
type Timing = {
|
|
119
|
+
retryDelayMs: number;
|
|
120
|
+
/** How long "who am I" (and with it Audit mode) is cached. */
|
|
121
|
+
meCacheMs: number;
|
|
122
|
+
pollStartMs: number;
|
|
123
|
+
pollMaxMs: number;
|
|
124
|
+
/** How long an authorization proof says it is good for. */
|
|
125
|
+
authorizationTtlMs: number;
|
|
126
|
+
};
|
|
127
|
+
type AgentInfo = {
|
|
128
|
+
organizationId: string;
|
|
129
|
+
principalId: string;
|
|
130
|
+
name: string | null;
|
|
131
|
+
kind: string | null;
|
|
132
|
+
/** The grants to assert. Whether they are still good is for the decision to say. */
|
|
133
|
+
grantIds: string[];
|
|
134
|
+
hasAccess?: boolean;
|
|
135
|
+
frozen: boolean;
|
|
136
|
+
mode: 'enforce' | 'audit';
|
|
137
|
+
audit: {
|
|
138
|
+
level: 'new_actions' | 'everything';
|
|
139
|
+
endsAt: string;
|
|
140
|
+
} | null;
|
|
141
|
+
agentKitEnabled: boolean;
|
|
142
|
+
authorizationKind: string;
|
|
143
|
+
/** True when this agent is bound to its workload: every request carries its token. */
|
|
144
|
+
workloadRequired?: boolean;
|
|
145
|
+
};
|
|
146
|
+
/** What a decision says beyond yes or no, for the rules that use them (schemaVersion 3). */
|
|
147
|
+
type DecisionDetails = {
|
|
148
|
+
/** The request's fields that did not match what the rule expects: missing, wrong type, too long. */
|
|
149
|
+
problems: Array<{
|
|
150
|
+
path: string;
|
|
151
|
+
problem: string;
|
|
152
|
+
}>;
|
|
153
|
+
/** The budget that would have gone over its cap, when that was the reason. */
|
|
154
|
+
budget: {
|
|
155
|
+
budgetId: string;
|
|
156
|
+
spent: string;
|
|
157
|
+
amount: string;
|
|
158
|
+
cap: string;
|
|
159
|
+
} | null;
|
|
160
|
+
/** Why the approvals given were not accepted, one code per approval. */
|
|
161
|
+
approvalFailures: string[];
|
|
162
|
+
};
|
|
163
|
+
type Decision = {
|
|
164
|
+
decisionId: string;
|
|
165
|
+
allowed: boolean;
|
|
166
|
+
/** Praesidias's reason code and the same thing in plain words. */
|
|
167
|
+
reason: {
|
|
168
|
+
code: string;
|
|
169
|
+
message: string;
|
|
170
|
+
};
|
|
171
|
+
/** The facts sent with the request, exactly as decided on. */
|
|
172
|
+
context: Record<string, unknown>;
|
|
173
|
+
/** The case of the rule that decided, when the rule has cases. */
|
|
174
|
+
caseId: string | null;
|
|
175
|
+
/**
|
|
176
|
+
* The fields the decision is locked to: the authorization and the execution must carry the
|
|
177
|
+
* same values. The SDK sends them for you.
|
|
178
|
+
*/
|
|
179
|
+
boundPaths: string[];
|
|
180
|
+
details: DecisionDetails;
|
|
181
|
+
/** True when the only thing missing is a person's approval. */
|
|
182
|
+
needsApproval: boolean;
|
|
183
|
+
expiresAt: string | null;
|
|
184
|
+
action: string;
|
|
185
|
+
resource: Resource;
|
|
186
|
+
system: string;
|
|
187
|
+
rulesVersion: number | null;
|
|
188
|
+
correlationId: string;
|
|
189
|
+
};
|
|
190
|
+
/** Praesidias's signed statement that this action on this resource was authorized. */
|
|
191
|
+
type Proof = {
|
|
192
|
+
id: string;
|
|
193
|
+
decisionId: string;
|
|
194
|
+
kind: string;
|
|
195
|
+
subjectId: string;
|
|
196
|
+
hash: string;
|
|
197
|
+
signature: string;
|
|
198
|
+
signingKeyId: string | null;
|
|
199
|
+
issuedAt: string;
|
|
200
|
+
payload: Record<string, unknown>;
|
|
201
|
+
/** The whole proof as Praesidias returned it, to hand to a service that will verify it. */
|
|
202
|
+
raw: Record<string, unknown>;
|
|
203
|
+
};
|
|
204
|
+
type ApprovalStatus = {
|
|
205
|
+
decisionId: string;
|
|
206
|
+
state: 'waiting' | 'approved' | 'rules_changed' | 'not_waiting';
|
|
207
|
+
approvalsHave: number;
|
|
208
|
+
approvalsNeeded: number | null;
|
|
209
|
+
approvalArtifactIds: string[];
|
|
210
|
+
};
|
|
211
|
+
type CallOptions = {
|
|
212
|
+
/** Overrides the client's system for this call. */
|
|
213
|
+
system?: string;
|
|
214
|
+
/** Facts about the request, recorded with the decision. Never put secrets here. */
|
|
215
|
+
context?: Record<string, unknown>;
|
|
216
|
+
/** Reuse the same key to make a repeat of this exact call safe. One is made if you give none. */
|
|
217
|
+
idempotencyKey?: string;
|
|
218
|
+
/** Ties this call to your own request id in every record. */
|
|
219
|
+
correlationId?: string;
|
|
220
|
+
signal?: AbortSignal;
|
|
221
|
+
};
|
|
222
|
+
type GuardOptions = CallOptions & {
|
|
223
|
+
onApproval?: 'wait' | 'return';
|
|
224
|
+
approvalTimeoutMs?: number;
|
|
225
|
+
};
|
|
226
|
+
/** What the guarded function is given. */
|
|
227
|
+
type GuardContext = {
|
|
228
|
+
decisionId: string;
|
|
229
|
+
correlationId: string;
|
|
230
|
+
/** The signed authorization. Null only when running under Audit mode, where there is none. */
|
|
231
|
+
proof: Proof | null;
|
|
232
|
+
/** True when Praesidias denied this and it is going ahead only because of Audit mode. */
|
|
233
|
+
auditMode: boolean;
|
|
234
|
+
};
|
|
235
|
+
type Common = {
|
|
236
|
+
decisionId: string | null;
|
|
237
|
+
correlationId: string;
|
|
238
|
+
consoleUrl: string | null;
|
|
239
|
+
};
|
|
240
|
+
type GuardResult<T> = (Common & {
|
|
241
|
+
status: 'executed';
|
|
242
|
+
value: T;
|
|
243
|
+
proof: Proof | null;
|
|
244
|
+
/**
|
|
245
|
+
* Set when it ran under Audit mode: what would have happened otherwise. `unknown`, with
|
|
246
|
+
* `offline`, when Praesidias could not be reached: it ran because the agent is in Audit
|
|
247
|
+
* mode, and the request is sent to Praesidias to record once it can be reached again.
|
|
248
|
+
*/
|
|
249
|
+
audit: {
|
|
250
|
+
wouldHave: 'blocked' | 'waited' | 'unknown';
|
|
251
|
+
level: 'new_actions' | 'everything';
|
|
252
|
+
offline?: true;
|
|
253
|
+
} | null;
|
|
254
|
+
/** False when the action ran but reporting its outcome failed. The action still ran. */
|
|
255
|
+
outcomeRecorded: boolean;
|
|
256
|
+
}) | (Common & {
|
|
257
|
+
status: 'blocked';
|
|
258
|
+
reason: {
|
|
259
|
+
code: string;
|
|
260
|
+
message: string;
|
|
261
|
+
};
|
|
262
|
+
}) | (Common & {
|
|
263
|
+
status: 'needs_approval';
|
|
264
|
+
reason: {
|
|
265
|
+
code: string;
|
|
266
|
+
message: string;
|
|
267
|
+
};
|
|
268
|
+
approvalsHave: number;
|
|
269
|
+
approvalsNeeded: number | null;
|
|
270
|
+
/** Wait again (for example after telling the user), with the same or a new timeout. */
|
|
271
|
+
resume: (options?: {
|
|
272
|
+
approvalTimeoutMs?: number;
|
|
273
|
+
signal?: AbortSignal;
|
|
274
|
+
}) => Promise<GuardResult<T>>;
|
|
275
|
+
});
|
|
276
|
+
/**
|
|
277
|
+
* A client for one agent. It needs only the agent's key: who the agent is, and which grants it
|
|
278
|
+
* must assert, are read from Praesidias.
|
|
279
|
+
*/
|
|
280
|
+
declare class Praesidias {
|
|
281
|
+
private readonly http;
|
|
282
|
+
private readonly timing;
|
|
283
|
+
private readonly system;
|
|
284
|
+
private readonly consoleUrl;
|
|
285
|
+
private readonly onApproval;
|
|
286
|
+
private readonly approvalTimeoutMs;
|
|
287
|
+
private cachedMe;
|
|
288
|
+
/** The last "who am I" ever read, however old: only to know, offline, if Audit mode was on. */
|
|
289
|
+
private lastMe;
|
|
290
|
+
/** Requests run offline under Audit mode, oldest first, to record once Praesidias answers. */
|
|
291
|
+
private readonly offline;
|
|
292
|
+
private flushing;
|
|
293
|
+
constructor(options?: PraesidiasOptions);
|
|
294
|
+
toJSON(): {
|
|
295
|
+
praesidias: string;
|
|
296
|
+
};
|
|
297
|
+
/**
|
|
298
|
+
* Who this key is. Cached briefly. If it cannot be read, this throws: the SDK never guesses
|
|
299
|
+
* who the agent is or whether it is in Audit mode.
|
|
300
|
+
*/
|
|
301
|
+
me(options?: {
|
|
302
|
+
fresh?: boolean;
|
|
303
|
+
signal?: AbortSignal;
|
|
304
|
+
}): Promise<AgentInfo>;
|
|
305
|
+
/** How many requests that ran offline, under Audit mode, are still to be recorded. */
|
|
306
|
+
get offlinePending(): number;
|
|
307
|
+
/**
|
|
308
|
+
* Sends the requests that ran offline under Audit mode as ordinary decisions, oldest first, so
|
|
309
|
+
* they are in the record. Stops at the first that cannot be sent; the rest wait for next time.
|
|
310
|
+
*/
|
|
311
|
+
flushOffline(): Promise<number>;
|
|
312
|
+
/**
|
|
313
|
+
* Tells Praesidias which tools this agent has, so the Console can list them and say which have
|
|
314
|
+
* a rule. Names and input types only. Never throws and never holds the agent up: false when it
|
|
315
|
+
* could not be sent (an older Praesidias without this, or one that cannot be reached).
|
|
316
|
+
*/
|
|
317
|
+
reportTools(source: ToolSource, tools: readonly ReportedTool[]): Promise<boolean>;
|
|
318
|
+
private linkTo;
|
|
319
|
+
private systemFor;
|
|
320
|
+
/** Asks Praesidias whether this agent may do this action on this resource. Nothing is run. */
|
|
321
|
+
decide(action: string, resource: Resource, options?: CallOptions & {
|
|
322
|
+
approvalArtifactIds?: string[];
|
|
323
|
+
}): Promise<Decision>;
|
|
324
|
+
/**
|
|
325
|
+
* Gets Praesidias's signed authorization for an allowed decision. Call it straight after an
|
|
326
|
+
* ALLOW and before running the action; a decision can be authorized once.
|
|
327
|
+
*/
|
|
328
|
+
commit(decision: Decision, options?: Pick<CallOptions, 'idempotencyKey' | 'signal'>): Promise<Proof>;
|
|
329
|
+
/** Whether a decision that needs approval has been approved yet. */
|
|
330
|
+
approvalStatus(decisionId: string, options?: {
|
|
331
|
+
signal?: AbortSignal;
|
|
332
|
+
}): Promise<ApprovalStatus>;
|
|
333
|
+
/** Reports what happened after an authorized action ran. */
|
|
334
|
+
reportOutcome(proof: Proof, outcome: {
|
|
335
|
+
status: 'succeeded';
|
|
336
|
+
} | {
|
|
337
|
+
status: 'failed';
|
|
338
|
+
error?: unknown;
|
|
339
|
+
}): Promise<void>;
|
|
340
|
+
/**
|
|
341
|
+
* Runs `fn` only if Praesidias allows this action on this resource.
|
|
342
|
+
*
|
|
343
|
+
* decide → (wait for approval if needed) → get the signed authorization → run fn → report
|
|
344
|
+
*
|
|
345
|
+
* Returns `executed`, `blocked` or `needs_approval`. Throws a PraesidiasError when Praesidias
|
|
346
|
+
* could not give a clear answer; in that case `fn` has not run. If `fn` itself throws, its
|
|
347
|
+
* error is reported as a failed outcome and thrown on unchanged.
|
|
348
|
+
*/
|
|
349
|
+
guard<T>(action: string, resource: Resource, fn: (context: GuardContext) => Promise<T> | T, options?: GuardOptions): Promise<GuardResult<T>>;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* The subject of an authorization must be a UUID. This derives one from the resource, the same
|
|
354
|
+
* way every time (a version 5 UUID), so the same resource in the same organization always has
|
|
355
|
+
* the same subject, in every SDK.
|
|
356
|
+
*/
|
|
357
|
+
declare function subjectIdFor(organizationId: string, resource: {
|
|
358
|
+
type: string;
|
|
359
|
+
id?: string | null;
|
|
360
|
+
}): string;
|
|
361
|
+
|
|
362
|
+
declare function sentenceFor(code: string): string;
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* The mapping: how a tool call becomes an action on a resource. One format, used everywhere a
|
|
366
|
+
* tool is governed: the MCP gateway, the API gateway's sibling, and every framework adapter.
|
|
367
|
+
*
|
|
368
|
+
* Every tool is governed unless the mapping says otherwise. With nothing said about a tool, a
|
|
369
|
+
* call to `create_issue` in a tool set named `github` is the action `github.create_issue` on a
|
|
370
|
+
* resource of type `create_issue`, whose id is taken from the first of a few common argument
|
|
371
|
+
* names.
|
|
372
|
+
*/
|
|
373
|
+
type Condition = {
|
|
374
|
+
arg: string;
|
|
375
|
+
} & ({
|
|
376
|
+
gt: number;
|
|
377
|
+
} | {
|
|
378
|
+
gte: number;
|
|
379
|
+
} | {
|
|
380
|
+
lt: number;
|
|
381
|
+
} | {
|
|
382
|
+
lte: number;
|
|
383
|
+
} | {
|
|
384
|
+
eq: string | number | boolean;
|
|
385
|
+
} | {
|
|
386
|
+
ne: string | number | boolean;
|
|
387
|
+
} | {
|
|
388
|
+
in: Array<string | number>;
|
|
389
|
+
} | {
|
|
390
|
+
startsWith: string;
|
|
391
|
+
} | {
|
|
392
|
+
contains: string;
|
|
393
|
+
} | {
|
|
394
|
+
exists: boolean;
|
|
395
|
+
});
|
|
396
|
+
type FieldSource = string | {
|
|
397
|
+
arg: string;
|
|
398
|
+
as?: 'integer' | 'string' | 'boolean';
|
|
399
|
+
};
|
|
400
|
+
type ToolMapping = {
|
|
401
|
+
/** The action name. `{server}` and `{tool}` are filled in. */
|
|
402
|
+
action?: string;
|
|
403
|
+
resource?: {
|
|
404
|
+
type?: string;
|
|
405
|
+
idFrom?: string | string[];
|
|
406
|
+
};
|
|
407
|
+
/** Checked in order; the first that matches replaces the action (and resource, if given). */
|
|
408
|
+
when?: Array<{
|
|
409
|
+
if: Condition | Condition[];
|
|
410
|
+
action: string;
|
|
411
|
+
resource?: ToolMapping['resource'];
|
|
412
|
+
}>;
|
|
413
|
+
/** Argument names whose VALUES are recorded with the decision. Nothing else is. */
|
|
414
|
+
context?: string[];
|
|
415
|
+
/**
|
|
416
|
+
* The request's fields, for a rule that states what its action carries (schemaVersion 3):
|
|
417
|
+
* field name to argument (`"amount": "amount_cents"`, or `{ "arg": "to.account" }`), with an
|
|
418
|
+
* optional type to read a numeric or yes/no string as (`"as": "integer" | "string" | "boolean"`).
|
|
419
|
+
* Given, the request carries exactly these fields and nothing else, so it matches the rule's
|
|
420
|
+
* list. A value that is missing or cannot be read as asked is left out, and Praesidias refuses
|
|
421
|
+
* the request: nothing is guessed.
|
|
422
|
+
*/
|
|
423
|
+
fields?: Record<string, FieldSource>;
|
|
424
|
+
/** Overrides the system for this tool. */
|
|
425
|
+
system?: string;
|
|
426
|
+
/**
|
|
427
|
+
* Let this tool through WITHOUT asking Praesidias. For tools that only read and that you have
|
|
428
|
+
* decided need no record. Off unless you set it.
|
|
429
|
+
*/
|
|
430
|
+
ungoverned?: boolean;
|
|
431
|
+
};
|
|
432
|
+
type MappingFile = {
|
|
433
|
+
/** The system these tools act on, as your areas name it. */
|
|
434
|
+
system: string;
|
|
435
|
+
/** A short name for this set of tools (an MCP server, an agent's toolbox), used in default action names. */
|
|
436
|
+
server: string;
|
|
437
|
+
defaults?: Pick<ToolMapping, 'action' | 'resource' | 'context' | 'fields'>;
|
|
438
|
+
/** By tool name. A name may end in `*` to cover a family of tools. */
|
|
439
|
+
tools?: Record<string, ToolMapping>;
|
|
440
|
+
};
|
|
441
|
+
type Mapped = {
|
|
442
|
+
governed: false;
|
|
443
|
+
} | {
|
|
444
|
+
governed: true;
|
|
445
|
+
action: string;
|
|
446
|
+
resource: {
|
|
447
|
+
type: string;
|
|
448
|
+
id: string | null;
|
|
449
|
+
};
|
|
450
|
+
system: string;
|
|
451
|
+
context: Record<string, unknown>;
|
|
452
|
+
};
|
|
453
|
+
declare class MappingError extends Error {
|
|
454
|
+
}
|
|
455
|
+
/** Checks a mapping when it is loaded, so a mistake stops the program at start instead of surfacing mid-call. */
|
|
456
|
+
declare function checkMapping(raw: unknown): MappingFile;
|
|
457
|
+
declare function loadMapping(path: string): MappingFile;
|
|
458
|
+
/** Plain comparisons on one argument. No expressions are ever evaluated. */
|
|
459
|
+
declare function matches(condition: Condition, args: unknown): boolean;
|
|
460
|
+
/**
|
|
461
|
+
* The action a tool is decided as, before any `when` picks another for particular arguments:
|
|
462
|
+
* what a tool list reports. Null for a tool the mapping leaves ungoverned, or whose action name
|
|
463
|
+
* Praesidias would not accept.
|
|
464
|
+
*/
|
|
465
|
+
declare function toolAction(file: MappingFile, tool: string): string | null;
|
|
466
|
+
/** Turns one tool call into the action and resource Praesidias is asked about. */
|
|
467
|
+
declare function mapToolCall(file: MappingFile, tool: string, args: unknown): Mapped;
|
|
468
|
+
|
|
469
|
+
/** A tool as a framework describes it: its name, and optionally what it does and its inputs. */
|
|
470
|
+
type DescribedTool = {
|
|
471
|
+
name: string;
|
|
472
|
+
description?: unknown;
|
|
473
|
+
schema?: unknown;
|
|
474
|
+
};
|
|
475
|
+
/**
|
|
476
|
+
* What happened to one tool call. `ran` says whether the tool's own code was run; `message`
|
|
477
|
+
* is a plain sentence fit to hand back to a model or a person.
|
|
478
|
+
*/
|
|
479
|
+
type ToolOutcome<T> = {
|
|
480
|
+
status: 'executed';
|
|
481
|
+
ran: true;
|
|
482
|
+
value: T;
|
|
483
|
+
message: string;
|
|
484
|
+
decisionId: string | null;
|
|
485
|
+
audit: boolean;
|
|
486
|
+
} | {
|
|
487
|
+
status: 'passed_through';
|
|
488
|
+
ran: true;
|
|
489
|
+
value: T;
|
|
490
|
+
message: string;
|
|
491
|
+
decisionId: null;
|
|
492
|
+
audit: false;
|
|
493
|
+
} | {
|
|
494
|
+
status: 'blocked';
|
|
495
|
+
ran: false;
|
|
496
|
+
message: string;
|
|
497
|
+
decisionId: string | null;
|
|
498
|
+
code: string;
|
|
499
|
+
consoleUrl: string | null;
|
|
500
|
+
} | {
|
|
501
|
+
status: 'needs_approval';
|
|
502
|
+
ran: false;
|
|
503
|
+
message: string;
|
|
504
|
+
decisionId: string | null;
|
|
505
|
+
approvalsHave: number;
|
|
506
|
+
approvalsNeeded: number | null;
|
|
507
|
+
consoleUrl: string | null;
|
|
508
|
+
}
|
|
509
|
+
/** Praesidias could not confirm the action is allowed. It was not carried out. */
|
|
510
|
+
| {
|
|
511
|
+
status: 'unavailable';
|
|
512
|
+
ran: false;
|
|
513
|
+
message: string;
|
|
514
|
+
decisionId: null;
|
|
515
|
+
code: string;
|
|
516
|
+
};
|
|
517
|
+
type ToolGateOptions = {
|
|
518
|
+
/**
|
|
519
|
+
* What to do when a tool call needs a person's approval. "return" (the default for tools)
|
|
520
|
+
* answers at once so a chat agent never hangs: the model is told it needs approval, and the
|
|
521
|
+
* same call made again after someone approves goes ahead. "wait" holds the call open.
|
|
522
|
+
*/
|
|
523
|
+
onApproval?: 'return' | 'wait';
|
|
524
|
+
/** With "wait": how long, in milliseconds. */
|
|
525
|
+
approvalTimeoutMs?: number;
|
|
526
|
+
/** One line per tool call. Tool and action names only: never arguments, never a key. */
|
|
527
|
+
log?: (line: string) => void;
|
|
528
|
+
};
|
|
529
|
+
/**
|
|
530
|
+
* Governs an agent's tools with one mapping. This is what every framework adapter is built on:
|
|
531
|
+
* give it the tool's name, its arguments, and the function that does the work.
|
|
532
|
+
*
|
|
533
|
+
* The work runs only when Praesidias allows the action. Nothing is ever let through because
|
|
534
|
+
* Praesidias could not be asked: that is "unavailable", and the tool does not run.
|
|
535
|
+
*/
|
|
536
|
+
declare class ToolGate {
|
|
537
|
+
private readonly praesidias;
|
|
538
|
+
readonly mapping: MappingFile;
|
|
539
|
+
private readonly onApproval;
|
|
540
|
+
private readonly approvalTimeoutMs;
|
|
541
|
+
private readonly log;
|
|
542
|
+
private readonly waiting;
|
|
543
|
+
/** Every tool this gate has been given, by action, as last sent to Praesidias. */
|
|
544
|
+
private readonly known;
|
|
545
|
+
private reportTimer;
|
|
546
|
+
private lastReport;
|
|
547
|
+
constructor(praesidias: Praesidias, mapping: MappingFile, options?: ToolGateOptions);
|
|
548
|
+
/**
|
|
549
|
+
* Tells Praesidias about tools this agent has (the adapters call it with the tools they are
|
|
550
|
+
* given), so the Console lists them and says which have a rule. Tools given together, or one
|
|
551
|
+
* at a time in a row, go in one report, sent shortly after; a list that has not changed is not
|
|
552
|
+
* sent again. Never throws and never holds anything up.
|
|
553
|
+
*/
|
|
554
|
+
reportTools(tools: readonly DescribedTool[]): void;
|
|
555
|
+
/** Runs `fn` if Praesidias allows this tool call, and says what happened either way. */
|
|
556
|
+
run<T>(tool: string, args: unknown, fn: () => Promise<T> | T): Promise<ToolOutcome<T>>;
|
|
557
|
+
/**
|
|
558
|
+
* A tool function that governs itself. When the action is not allowed it does not throw: it
|
|
559
|
+
* returns the sentence saying why, which is what a model needs to read in order to tell the
|
|
560
|
+
* user or to try again later. Pass `refuse` to turn that sentence into your framework's shape.
|
|
561
|
+
*/
|
|
562
|
+
wrap<A, T, R = string>(tool: string, fn: (args: A) => Promise<T> | T, refuse?: (outcome: Exclude<ToolOutcome<T>, {
|
|
563
|
+
ran: true;
|
|
564
|
+
}>) => R): (args: A) => Promise<T | R>;
|
|
565
|
+
}
|
|
566
|
+
/** A gate for an agent's tools: `createToolGate(new Praesidias(), mapping)`. */
|
|
567
|
+
declare function createToolGate(praesidias: Praesidias, mapping: MappingFile, options?: ToolGateOptions): ToolGate;
|
|
568
|
+
|
|
569
|
+
/**
|
|
570
|
+
* Reads a shell command the way Praesidias needs to decide on it: which programs it runs, with
|
|
571
|
+
* which subcommand and flags. It never runs anything and never expands anything.
|
|
572
|
+
*
|
|
573
|
+
* A compound command (`&&`, `||`, `;`, `|`, `&`, newlines, subshells) is split into its parts,
|
|
574
|
+
* and every part is decided on. Anything that would make the shell build or run code that cannot
|
|
575
|
+
* be read here (`eval`, `$(…)`, backticks, `<(…)`, `sh -c`, a program named by a variable) makes
|
|
576
|
+
* the whole command "unparsed": it is then decided once, as the program `__unparsed__`.
|
|
577
|
+
*
|
|
578
|
+
* The same rules are implemented in the Python SDK (praesidias/agent_actions.py); both run the
|
|
579
|
+
* cases in spec/tool-actions-cases.json and must agree.
|
|
580
|
+
*/
|
|
581
|
+
type ShellPart = {
|
|
582
|
+
/** The program run, without its directory: `/usr/bin/git` is `git`. Lowercase. */
|
|
583
|
+
program: string;
|
|
584
|
+
/** The first argument that is not a flag (for `git push`, `push`). Empty when there is none. */
|
|
585
|
+
subcommand: string;
|
|
586
|
+
/** Every flag, as written, plus `-rf` split into `-r` and `-f`, and `--x=y` also as `--x`. */
|
|
587
|
+
flags: string[];
|
|
588
|
+
/** This part's own text. */
|
|
589
|
+
command: string;
|
|
590
|
+
};
|
|
591
|
+
type ParsedShell = {
|
|
592
|
+
parsed: true;
|
|
593
|
+
parts: ShellPart[];
|
|
594
|
+
} | {
|
|
595
|
+
parsed: false;
|
|
596
|
+
};
|
|
597
|
+
declare const UNPARSED = "__unparsed__";
|
|
598
|
+
/**
|
|
599
|
+
* `posix` is sh/bash/zsh. `powershell` differs where it matters here: a backslash is an ordinary
|
|
600
|
+
* character (Windows paths), and `''` inside single quotes is a quote. Its escape character, the
|
|
601
|
+
* backtick, makes a command unparsed in both.
|
|
602
|
+
*/
|
|
603
|
+
type ShellDialect = 'posix' | 'powershell';
|
|
604
|
+
/** Reads a shell command into the parts Praesidias decides on. See the top of this file. */
|
|
605
|
+
declare function parseShellCommand(command: string, dialect?: ShellDialect): ParsedShell;
|
|
606
|
+
|
|
607
|
+
/**
|
|
608
|
+
* What Praesidias decides on for one tool call of a coding agent (Claude Code, Codex): the
|
|
609
|
+
* action, the resource and the facts sent with it. Fixed by the contract between the kit and
|
|
610
|
+
* the Console's starting policy, and by spec/tool-actions-cases.json, which the Python SDK runs
|
|
611
|
+
* too (praesidias/agent_actions.py).
|
|
612
|
+
*
|
|
613
|
+
* File contents are never sent: only paths.
|
|
614
|
+
*/
|
|
615
|
+
type CodingAgent = 'claude_code' | 'codex';
|
|
616
|
+
type AgentAction = {
|
|
617
|
+
action: string;
|
|
618
|
+
resourceType: string;
|
|
619
|
+
resourceId: string | null;
|
|
620
|
+
context: Record<string, unknown>;
|
|
621
|
+
};
|
|
622
|
+
type AgentToolCall = {
|
|
623
|
+
agent: CodingAgent;
|
|
624
|
+
/** The tool's name as the agent reports it (`Bash`, `Read`, `mcp__github__create_issue`). */
|
|
625
|
+
tool: string;
|
|
626
|
+
/** The tool's input as the agent reports it. */
|
|
627
|
+
input: unknown;
|
|
628
|
+
/** Where the agent is working. Relative paths are read from here. */
|
|
629
|
+
cwd?: string;
|
|
630
|
+
/** The project's root (CLAUDE_PROJECT_DIR), for `in_project`. Defaults to `cwd`. */
|
|
631
|
+
projectDir?: string;
|
|
632
|
+
/** The user's home folder, shown as `~`. */
|
|
633
|
+
home?: string;
|
|
634
|
+
/** For an MCP tool: the input names its server declares. Only those are sent. */
|
|
635
|
+
declaredInputs?: string[] | null;
|
|
636
|
+
/** Which path rules apply. Defaults to this machine's. */
|
|
637
|
+
platform?: 'posix' | 'win32';
|
|
638
|
+
};
|
|
639
|
+
/** The longest action name the decision endpoint accepts. */
|
|
640
|
+
declare const MAX_ACTION_LENGTH = 64;
|
|
641
|
+
/**
|
|
642
|
+
* True when any folder or file name in the path is a secret's (`.env`, `*.pem`, `.ssh`, …).
|
|
643
|
+
* A glob is read with its wildcards taken as empty, so `.env*` and `**\/*.pem` count too.
|
|
644
|
+
*/
|
|
645
|
+
declare function isSensitivePath(path: string): boolean;
|
|
646
|
+
/** The action name rules: `[A-Za-z0-9_.:-]`, at most 64 characters. A longer one keeps a hash. */
|
|
647
|
+
declare function fitActionName(name: string): string;
|
|
648
|
+
/** `mcp__<server>__<tool>` as the action `mcp.<server>.<tool>`; null when the name is not an MCP tool's. */
|
|
649
|
+
declare function mcpAction(tool: string): {
|
|
650
|
+
action: string;
|
|
651
|
+
server: string;
|
|
652
|
+
tool: string;
|
|
653
|
+
} | null;
|
|
654
|
+
/** The action for a tool the table does not know: `claude_code.<tool>` or `codex.<tool>`. */
|
|
655
|
+
declare function otherAction(agent: CodingAgent, tool: string): string;
|
|
656
|
+
/** Every part of a shell command, each its own `shell.run`; one `__unparsed__` if it cannot be read. */
|
|
657
|
+
declare function shellActions(command: string, dialect?: ShellDialect): AgentAction[];
|
|
658
|
+
/** The files a Codex patch touches (`*** Add File:`, `*** Update File:`, `*** Delete File:`, `*** Move to:`). */
|
|
659
|
+
declare function patchPaths(patch: string): string[];
|
|
660
|
+
/**
|
|
661
|
+
* The actions for one tool call: usually one; one per part of a compound shell command and one
|
|
662
|
+
* per file of a Codex patch. Every one must be allowed for the call to go ahead.
|
|
663
|
+
*/
|
|
664
|
+
declare function agentToolActions(call: AgentToolCall): AgentAction[];
|
|
665
|
+
/** The inputs a built-in tool's action carries, as the tool list reports them (POST /agent/tools). */
|
|
666
|
+
declare const BUILT_IN_INPUTS: Record<string, Record<string, {
|
|
667
|
+
type: 'string' | 'number' | 'boolean' | 'array' | 'object';
|
|
668
|
+
description: string;
|
|
669
|
+
}>>;
|
|
670
|
+
|
|
671
|
+
export { type AgentAction, type AgentInfo, type AgentToolCall, type ApprovalStatus, BUILT_IN_INPUTS, type CallOptions, type CodingAgent, type Condition, type Decision, type DecisionDetails, type DescribedTool, type FieldSource, type GuardContext, type GuardOptions, type GuardResult, type LogEvent, MAX_ACTION_LENGTH, type Mapped, MappingError, type MappingFile, type ParsedShell, Praesidias, PraesidiasAuthError, PraesidiasConfigError, PraesidiasError, type PraesidiasOptions, PraesidiasRequestError, PraesidiasUnavailableError, type Proof, type ReportedTool, type Resource, type ShellDialect, type ShellPart, TOOLS_PER_REPORT, ToolGate, type ToolGateOptions, type ToolInputType, type ToolMapping, type ToolOutcome, type ToolSource, UNPARSED, WORKLOAD_TOKEN_HEADER, agentToolActions, checkMapping, createToolGate, fitActionName, inputsOfSchema, isSensitivePath, loadMapping, mapToolCall, matches, mcpAction, otherAction, parseShellCommand, patchPaths, sentenceFor, shellActions, subjectIdFor, toolAction, toolsForReport };
|