roe-typescript 0.1.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -37,6 +37,31 @@ export ROE_API_KEY="your-api-key"
37
37
  export ROE_ORGANIZATION_ID="your-org-uuid"
38
38
  ```
39
39
 
40
+ ## Job Result Inspection
41
+
42
+ After waiting for a job, you can inspect its outcome using status helpers:
43
+
44
+ ```typescript
45
+ import { isJobSuccess, isJobFailure, isJobCancelled } from "roe-typescript";
46
+
47
+ const result = await job.wait();
48
+
49
+ if (isJobSuccess(result)) {
50
+ for (const output of result.outputs) {
51
+ console.log(`${output.key}: ${output.value}`);
52
+ }
53
+ } else if (isJobCancelled(result)) {
54
+ console.log("Job was cancelled");
55
+ } else if (isJobFailure(result)) {
56
+ console.log("Error:", result.error_message);
57
+ }
58
+
59
+ // Available fields
60
+ result.status // JobStatus enum value or null
61
+ result.error_message // Error string or null
62
+ result.outputs // AgentDatum[]
63
+ ```
64
+
40
65
  ## Full Example
41
66
 
42
67
  Create an agent that extracts structured data from websites:
@@ -139,10 +164,252 @@ client.agents.jobs.downloadReference(jobId, resourceId)
139
164
  client.agents.jobs.deleteData(jobId)
140
165
  ```
141
166
 
167
+ ### Policies
168
+
169
+ ```typescript
170
+ client.policies.list() // List policies
171
+ client.policies.retrieve("policy-uuid") // Get policy
172
+ client.policies.create({ name, content, ... }) // Create policy
173
+ client.policies.update("policy-uuid", { ... }) // Update policy
174
+ client.policies.delete("policy-uuid") // Delete policy
175
+ ```
176
+
177
+ ### Policy Versions
178
+
179
+ ```typescript
180
+ client.policies.versions.list(policyId)
181
+ client.policies.versions.retrieve(policyId, versionId)
182
+ client.policies.versions.create({ policyId, content, ... })
183
+ ```
184
+
185
+ ## Rori Agents (Agentic Workflows)
186
+
187
+ Rori agents are autonomous investigation agents that follow policies (SOPs), use tools, and produce structured verdicts. Unlike extraction engines which transform data, Rori agents reason over evidence, apply policy rules, and return dispositions. All Rori agents are policy-aware — you define the rules, they run the investigation.
188
+
189
+ ### Policies
190
+
191
+ Policies define the rules, instructions, and disposition classifications that Rori agents follow. Creating a policy atomically creates the policy and its first version in one call:
192
+
193
+ ```typescript
194
+ const policy = await client.policies.create({
195
+ name: "AML Investigation Policy",
196
+ content: {
197
+ guidelines: {
198
+ categories: [
199
+ {
200
+ title: "Structuring",
201
+ rules: [
202
+ {
203
+ title: "Cash structuring below reporting thresholds",
204
+ description: "Multiple deposits just under $10,000 within short timeframes",
205
+ flag: "RED_FLAG",
206
+ },
207
+ ],
208
+ },
209
+ {
210
+ title: "Layering",
211
+ rules: [
212
+ {
213
+ title: "Rapid movement between accounts",
214
+ description: "Funds transferred through multiple accounts to obscure origin",
215
+ flag: "RED_FLAG",
216
+ sub_rules: [
217
+ { title: "Cross-border wire transfers with no business purpose" },
218
+ { title: "Shell company intermediaries" },
219
+ ],
220
+ },
221
+ ],
222
+ },
223
+ ],
224
+ },
225
+ instructions: "Investigate the alert against each category. Use available data sources to gather evidence.",
226
+ dispositions: {
227
+ classifications: [
228
+ { name: "Suspicious", description: "Activity warrants SAR filing" },
229
+ { name: "Not Suspicious", description: "Activity has legitimate explanation" },
230
+ { name: "Needs Escalation", description: "Requires senior analyst review" },
231
+ ],
232
+ },
233
+ summary_template: {
234
+ template: "Investigation of {{subject}} found {{verdict}} based on {{findings_count}} findings.",
235
+ },
236
+ },
237
+ });
238
+ ```
239
+
240
+ Iterate on policies by creating new versions:
241
+
242
+ ```typescript
243
+ // Create a new version (automatically becomes the current version)
244
+ const newVersion = await client.policies.versions.create({
245
+ policyId: policy.id,
246
+ content: { /* Updated policy content */ },
247
+ versionName: "v2 - added layering rules",
248
+ });
249
+
250
+ // List all versions
251
+ const versions = await client.policies.versions.list(policy.id);
252
+
253
+ // Retrieve a specific version
254
+ const version = await client.policies.versions.retrieve(policy.id, newVersion.id);
255
+
256
+ // Update policy metadata
257
+ await client.policies.update(policy.id, { name: "Updated Policy Name" });
258
+
259
+ // List all policies
260
+ const policies = await client.policies.list();
261
+
262
+ // Delete a policy
263
+ await client.policies.delete(policy.id);
264
+ ```
265
+
266
+ ### Policy Content Reference
267
+
268
+ | Field | Type | Description |
269
+ |-------|------|-------------|
270
+ | `guidelines` | object | Categories → Rules → Sub-rules hierarchy |
271
+ | `guidelines.categories[].title` | string | Category name |
272
+ | `guidelines.categories[].rules[].title` | string | Rule name |
273
+ | `guidelines.categories[].rules[].description` | string | Rule details |
274
+ | `guidelines.categories[].rules[].flag` | string | `"RED_FLAG"` or `"GREEN_FLAG"` |
275
+ | `guidelines.categories[].rules[].sub_rules[].title` | string | Sub-rule name |
276
+ | `instructions` | string | Free-text investigation instructions |
277
+ | `dispositions.classifications[].name` | string | Outcome label (e.g., "Suspicious") |
278
+ | `dispositions.classifications[].description` | string | When to apply this outcome |
279
+ | `summary_template.template` | string | Handlebars template for report generation |
280
+ | `optional.sar_narrative_template.template` | string | SAR narrative template (AML-specific) |
281
+
282
+ ### Product Compliance
283
+
284
+ Analyze product listings against your compliance policy:
285
+
286
+ ```typescript
287
+ const agent = await client.agents.create({
288
+ name: "Product Compliance",
289
+ engineClassId: "ProductPolicyEngine",
290
+ inputDefinitions: [
291
+ { key: "product_listings", data_type: "text/plain", description: "Product listing to analyze" },
292
+ ],
293
+ engineConfig: {
294
+ policy_version_id: policy.current_version_id!,
295
+ product_listings: "${product_listings}",
296
+ },
297
+ });
298
+
299
+ const outputs = await client.agents.runSync(agent.id, {
300
+ product_listings: "Nike Air Max 90, brand new, $45 — ships from Shenzhen",
301
+ });
302
+ ```
303
+
304
+ ### AML Investigation
305
+
306
+ Investigate anti-money laundering alerts:
307
+
308
+ ```typescript
309
+ const agent = await client.agents.create({
310
+ name: "AML Investigation",
311
+ engineClassId: "AMLInvestigationEngine",
312
+ inputDefinitions: [
313
+ { key: "alert_data", data_type: "text/plain", description: "Alert data and context" },
314
+ ],
315
+ engineConfig: {
316
+ policy_version_id: policy.current_version_id!,
317
+ alert_data: "${alert_data}",
318
+ },
319
+ });
320
+
321
+ const job = await client.agents.run({
322
+ agentId: agent.id,
323
+ inputs: { alert_data: "Customer John Doe, 5 cash deposits of $9,500 in 3 days" },
324
+ });
325
+ const result = await job.wait();
326
+ ```
327
+
328
+ ### Fraud Investigation
329
+
330
+ Investigate fraud alerts and suspicious activity:
331
+
332
+ ```typescript
333
+ const agent = await client.agents.create({
334
+ name: "Fraud Investigation",
335
+ engineClassId: "FraudInvestigationEngine",
336
+ inputDefinitions: [
337
+ { key: "alert_data", data_type: "text/plain", description: "Alert data and context" },
338
+ ],
339
+ engineConfig: {
340
+ policy_version_id: policy.current_version_id!,
341
+ alert_data: "${alert_data}",
342
+ },
343
+ });
344
+
345
+ const job = await client.agents.run({
346
+ agentId: agent.id,
347
+ inputs: { alert_data: "Chargeback spike: 47 disputes in 24h from merchant ACME-1234" },
348
+ });
349
+ const result = await job.wait();
350
+ ```
351
+
352
+ ### Merchant Risk
353
+
354
+ Analyze merchant risk profiles:
355
+
356
+ ```typescript
357
+ const agent = await client.agents.create({
358
+ name: "Merchant Risk Analysis",
359
+ engineClassId: "MerchantRiskEngine",
360
+ inputDefinitions: [
361
+ { key: "merchant_context", data_type: "text/plain", description: "Merchant name and context" },
362
+ ],
363
+ engineConfig: {
364
+ policy_version_id: policy.current_version_id!,
365
+ merchant_context: "${merchant_context}",
366
+ },
367
+ });
368
+
369
+ const job = await client.agents.run({
370
+ agentId: agent.id,
371
+ inputs: { merchant_context: "ACME Corp - Online electronics retailer, MCC 5732" },
372
+ });
373
+ const result = await job.wait();
374
+ ```
375
+
376
+ ### Agent Configuration Options
377
+
378
+ All Rori agents accept these options in `engineConfig`:
379
+
380
+ | Option | Type | Default | Description |
381
+ |--------|------|---------|-------------|
382
+ | `policy_version_id` | string | — | Policy version UUID (required) |
383
+ | `context_sources` | array | `[]` | External data sources (SQL connections, APIs) |
384
+ | `enable_planning` | boolean | `true` | Enable autonomous tool-use planning |
385
+ | `enable_memory` | boolean | `false` | Retain context across runs for the same entity |
386
+ | `reasoning_effort` | string | `"medium"` | `"low"`, `"medium"`, or `"high"` |
387
+
388
+ Example with advanced configuration:
389
+
390
+ ```typescript
391
+ const agent = await client.agents.create({
392
+ name: "AML Investigation (Advanced)",
393
+ engineClassId: "AMLInvestigationEngine",
394
+ inputDefinitions: [
395
+ { key: "alert_data", data_type: "text/plain", description: "Alert data and context" },
396
+ ],
397
+ engineConfig: {
398
+ policy_version_id: policy.current_version_id!,
399
+ alert_data: "${alert_data}",
400
+ reasoning_effort: "high",
401
+ context_sources: [
402
+ { type: "sql", name: "Transactions DB", connection_id: "conn-uuid" },
403
+ ],
404
+ },
405
+ });
406
+ ```
407
+
142
408
  ## Supported Models
143
409
 
144
410
  | Model | Value |
145
411
  |-------|-------|
412
+ | GPT-5.4 | `gpt-5.4-2026-03-05` |
146
413
  | GPT-5.2 | `gpt-5.2-2025-12-11` |
147
414
  | GPT-5.1 | `gpt-5.1-2025-11-13` |
148
415
  | GPT-5 | `gpt-5-2025-08-07` |
@@ -152,24 +419,25 @@ client.agents.jobs.deleteData(jobId)
152
419
  | O3 Pro | `o3-pro-2025-06-10` |
153
420
  | O3 | `o3-2025-04-16` |
154
421
  | O4 Mini | `o4-mini-2025-04-16` |
155
- | GPT-4o | `gpt-4o-2024-11-20` |
156
- | Grok 4 | `grok-4-0709` |
157
- | Claude Sonnet 4.5 | `claude-sonnet-4-5-20250929` |
158
- | Claude Sonnet 4 | `claude-sonnet-4-20250514` |
159
- | Claude 3.7 Sonnet | `claude-3-7-sonnet-20250219` |
160
- | Claude Haiku 4.5 | `claude-haiku-4-5-20251001` |
161
- | Claude 3.5 Haiku | `claude-3-5-haiku-20241022` |
422
+ | Claude Opus 4.6 | `claude-opus-4-6` |
423
+ | Claude Sonnet 4.6 | `claude-sonnet-4-6` |
162
424
  | Claude Opus 4.5 | `claude-opus-4-5-20251101` |
425
+ | Claude Sonnet 4.5 | `claude-sonnet-4-5-20250929` |
163
426
  | Claude Opus 4.1 | `claude-opus-4-1-20250805` |
164
427
  | Claude Opus 4 | `claude-opus-4-20250514` |
428
+ | Claude Sonnet 4 | `claude-sonnet-4-20250514` |
429
+ | Claude Haiku 4.5 | `claude-haiku-4-5-20251001` |
165
430
  | Gemini 3 Pro | `gemini-3-pro-preview` |
431
+ | Gemini 3 Flash | `gemini-3-flash-preview` |
166
432
  | Gemini 2.5 Pro | `gemini-2.5-pro` |
167
433
  | Gemini 2.5 Flash | `gemini-2.5-flash` |
434
+ | Grok 4 | `grok-4-0709` |
435
+ | Grok 4.1 Fast Reasoning | `grok-4-1-fast-reasoning` |
168
436
 
169
437
  ## Engine Classes
170
438
 
171
439
  | Engine | ID |
172
- |--------|-----|
440
+ |--------|----|
173
441
  | Multimodal Extraction | `MultimodalExtractionEngine` |
174
442
  | Document Insights | `PDFExtractionEngine` |
175
443
  | Document Segmentation | `PDFPageSelectionEngine` |
@@ -178,10 +446,12 @@ client.agents.jobs.deleteData(jobId)
178
446
  | Web Search | `URLFinderEngine` |
179
447
  | Perplexity Search | `PerplexitySearchEngine` |
180
448
  | Maps Search | `GoogleMapsEntityExtractionEngine` |
181
- | Merchant Risk | `MerchantRiskAnalysisEngine` |
182
- | Product Policy | `ProductPolicyEngine` |
183
449
  | LinkedIn Crawler | `LinkedInScraperEngine` |
184
450
  | Social Media | `SocialScraperEngine` |
451
+ | Product Compliance | `ProductPolicyEngine` |
452
+ | Merchant Risk | `MerchantRiskEngine` |
453
+ | AML Investigation | `AMLInvestigationEngine` |
454
+ | Fraud Investigation | `FraudInvestigationEngine` |
185
455
 
186
456
  ## Links
187
457
 
@@ -34,6 +34,8 @@ export declare class AgentJobsAPI {
34
34
  retrieveStatusMany(jobIds: string[]): Promise<AgentJobStatusBatch[]>;
35
35
  retrieveResultMany(jobIds: string[]): Promise<AgentJobResultBatch[]>;
36
36
  downloadReference(jobId: string, resourceId: string, asAttachment?: boolean): Promise<Buffer>;
37
+ cancel(jobId: string): Promise<void>;
38
+ cancelAll(agentId: string): Promise<void>;
37
39
  deleteData(jobId: string): Promise<JobDataDeleteResponse>;
38
40
  private iterChunks;
39
41
  }
@@ -69,19 +71,22 @@ export declare class AgentsAPI {
69
71
  agentId: string;
70
72
  inputs: Record<string, unknown>;
71
73
  timeoutSeconds?: number;
74
+ metadata?: Record<string, unknown>;
72
75
  }): Promise<Job>;
73
76
  runMany(params: {
74
77
  agentId: string;
75
78
  batchInputs: Record<string, unknown>[];
76
79
  timeoutSeconds?: number;
80
+ metadata?: Record<string, unknown>;
77
81
  }): Promise<JobBatch>;
78
- runSync(agentId: string, inputs: Record<string, unknown>): Promise<AgentDatum[]>;
82
+ runSync(agentId: string, inputs: Record<string, unknown>, metadata?: Record<string, unknown>): Promise<AgentDatum[]>;
79
83
  runVersion(params: {
80
84
  agentId: string;
81
85
  versionId: string;
82
86
  inputs: Record<string, unknown>;
83
87
  timeoutSeconds?: number;
88
+ metadata?: Record<string, unknown>;
84
89
  }): Promise<Job>;
85
- runVersionSync(agentId: string, versionId: string, inputs: Record<string, unknown>): Promise<AgentDatum[]>;
90
+ runVersionSync(agentId: string, versionId: string, inputs: Record<string, unknown>, metadata?: Record<string, unknown>): Promise<AgentDatum[]>;
86
91
  private iterChunks;
87
92
  }
@@ -116,10 +116,18 @@ export class AgentJobsAPI {
116
116
  }
117
117
  async downloadReference(jobId, resourceId, asAttachment = false) {
118
118
  validateUuid(jobId, "jobId");
119
- validateUuid(resourceId, "resourceId");
119
+ // resourceId is a filename/identifier, not a UUID — no validation needed
120
120
  const params = asAttachment ? { download: "true" } : undefined;
121
121
  return this.http.getBytes(`/v1/agents/jobs/${jobId}/references/${resourceId}/`, params);
122
122
  }
123
+ async cancel(jobId) {
124
+ validateUuid(jobId, "jobId");
125
+ await this.http.post({ url: `/v1/agents/jobs/${jobId}/cancel/` });
126
+ }
127
+ async cancelAll(agentId) {
128
+ validateUuid(agentId, "agentId");
129
+ await this.http.post({ url: `/v1/agents/${agentId}/jobs/cancel-all/` });
130
+ }
123
131
  async deleteData(jobId) {
124
132
  validateUuid(jobId, "jobId");
125
133
  return this.http.post({
@@ -196,7 +204,7 @@ export class AgentsAPI {
196
204
  }
197
205
  async run(params) {
198
206
  validateUuid(params.agentId, "agentId");
199
- const response = await this.httpClient.postWithDynamicInputs(`/v1/agents/run/${params.agentId}/async/`, params.inputs);
207
+ const response = await this.httpClient.postWithDynamicInputs(`/v1/agents/run/${params.agentId}/async/`, params.inputs, undefined, params.metadata);
200
208
  // Handle both string response and object response formats
201
209
  const jobId = typeof response === "string" ? response : response?.job_id;
202
210
  if (!jobId || typeof jobId !== "string") {
@@ -213,9 +221,12 @@ export class AgentsAPI {
213
221
  for (const chunk of this.iterChunks(params.batchInputs, AgentsAPI.MAX_BATCH_SIZE)) {
214
222
  if (!chunk.length)
215
223
  continue;
224
+ const json = { inputs: chunk };
225
+ if (params.metadata !== undefined)
226
+ json.metadata = params.metadata;
216
227
  const resp = await this.httpClient.post({
217
228
  url: `/v1/agents/run/${params.agentId}/async/many/`,
218
- json: { inputs: chunk },
229
+ json,
219
230
  });
220
231
  // Validate each returned job ID
221
232
  for (const jobId of resp) {
@@ -230,15 +241,15 @@ export class AgentsAPI {
230
241
  }
231
242
  return new JobBatch({ agentsApi: this, jobIds: allJobIds, timeoutSeconds: params.timeoutSeconds });
232
243
  }
233
- async runSync(agentId, inputs) {
244
+ async runSync(agentId, inputs, metadata) {
234
245
  validateUuid(agentId, "agentId");
235
- const resp = await this.httpClient.postWithDynamicInputs(`/v1/agents/run/${agentId}/`, inputs);
246
+ const resp = await this.httpClient.postWithDynamicInputs(`/v1/agents/run/${agentId}/`, inputs, undefined, metadata);
236
247
  return resp;
237
248
  }
238
249
  async runVersion(params) {
239
250
  validateUuid(params.agentId, "agentId");
240
251
  validateUuid(params.versionId, "versionId");
241
- const response = await this.httpClient.postWithDynamicInputs(`/v1/agents/run/${params.agentId}/versions/${params.versionId}/async/`, params.inputs);
252
+ const response = await this.httpClient.postWithDynamicInputs(`/v1/agents/run/${params.agentId}/versions/${params.versionId}/async/`, params.inputs, undefined, params.metadata);
242
253
  // Handle both string response and object response formats
243
254
  const jobId = typeof response === "string" ? response : response?.job_id;
244
255
  if (!jobId || typeof jobId !== "string") {
@@ -249,10 +260,10 @@ export class AgentsAPI {
249
260
  }
250
261
  return new Job({ agentsApi: this, jobId, timeoutSeconds: params.timeoutSeconds });
251
262
  }
252
- async runVersionSync(agentId, versionId, inputs) {
263
+ async runVersionSync(agentId, versionId, inputs, metadata) {
253
264
  validateUuid(agentId, "agentId");
254
265
  validateUuid(versionId, "versionId");
255
- return this.httpClient.postWithDynamicInputs(`/v1/agents/run/${agentId}/versions/${versionId}/`, inputs);
266
+ return this.httpClient.postWithDynamicInputs(`/v1/agents/run/${agentId}/versions/${versionId}/`, inputs, undefined, metadata);
256
267
  }
257
268
  *iterChunks(items, size) {
258
269
  for (let i = 0; i < items.length; i += size) {
@@ -0,0 +1,39 @@
1
+ import { RoeConfig } from "../config.js";
2
+ import { RoeHTTPClient } from "../utils/httpClient.js";
3
+ import { PaginatedResponse } from "../models/responses.js";
4
+ import { Policy, PolicyVersion } from "../models/policy.js";
5
+ export declare class PolicyVersionsAPI {
6
+ private readonly policiesApi;
7
+ constructor(policiesApi: PoliciesAPI);
8
+ private get http();
9
+ list(policyId: string): Promise<PolicyVersion[]>;
10
+ retrieve(policyId: string, versionId: string): Promise<PolicyVersion>;
11
+ create(params: {
12
+ policyId: string;
13
+ content: Record<string, unknown>;
14
+ versionName?: string;
15
+ baseVersionId?: string;
16
+ }): Promise<PolicyVersion>;
17
+ }
18
+ export declare class PoliciesAPI {
19
+ readonly config: RoeConfig;
20
+ readonly httpClient: RoeHTTPClient;
21
+ readonly versions: PolicyVersionsAPI;
22
+ constructor(config: RoeConfig, httpClient: RoeHTTPClient);
23
+ list(params?: {
24
+ page?: number;
25
+ pageSize?: number;
26
+ }): Promise<PaginatedResponse<Policy>>;
27
+ retrieve(policyId: string): Promise<Policy>;
28
+ create(params: {
29
+ name: string;
30
+ content: Record<string, unknown>;
31
+ description?: string;
32
+ versionName?: string;
33
+ }): Promise<Policy>;
34
+ update(policyId: string, updates: {
35
+ name?: string;
36
+ description?: string;
37
+ }): Promise<Policy>;
38
+ delete(policyId: string): Promise<void>;
39
+ }
@@ -0,0 +1,90 @@
1
+ import { PaginationHelper } from "../utils/pagination.js";
2
+ import { isUuidString } from "../utils/fileDetection.js";
3
+ import { BadRequestError, RoeAPIException } from "../exceptions.js";
4
+ /**
5
+ * Validates that a value is a valid UUID string.
6
+ * @throws BadRequestError if the value is not a valid UUID
7
+ */
8
+ function validateUuid(value, fieldName) {
9
+ if (!isUuidString(value)) {
10
+ throw new BadRequestError(`Invalid ${fieldName}: "${value}" is not a valid UUID`, 400);
11
+ }
12
+ }
13
+ export class PolicyVersionsAPI {
14
+ constructor(policiesApi) {
15
+ this.policiesApi = policiesApi;
16
+ }
17
+ get http() {
18
+ return this.policiesApi.httpClient;
19
+ }
20
+ async list(policyId) {
21
+ validateUuid(policyId, "policyId");
22
+ const data = await this.http.get(`/v1/policies/${policyId}/versions/`);
23
+ if (Array.isArray(data))
24
+ return data;
25
+ return data.results ?? [];
26
+ }
27
+ async retrieve(policyId, versionId) {
28
+ validateUuid(policyId, "policyId");
29
+ validateUuid(versionId, "versionId");
30
+ return this.http.get(`/v1/policies/${policyId}/versions/${versionId}/`);
31
+ }
32
+ async create(params) {
33
+ validateUuid(params.policyId, "policyId");
34
+ if (params.baseVersionId !== undefined)
35
+ validateUuid(params.baseVersionId, "baseVersionId");
36
+ const payload = { content: params.content };
37
+ if (params.versionName !== undefined)
38
+ payload.version_name = params.versionName;
39
+ if (params.baseVersionId !== undefined)
40
+ payload.base_version_id = params.baseVersionId;
41
+ const data = await this.http.post({
42
+ url: `/v1/policies/${params.policyId}/versions/`,
43
+ json: payload,
44
+ });
45
+ if (!data.id) {
46
+ throw new RoeAPIException(`Unexpected response from server: ${JSON.stringify(data)}`);
47
+ }
48
+ // POST returns partial data; re-fetch to get the full version
49
+ return this.retrieve(params.policyId, data.id);
50
+ }
51
+ }
52
+ export class PoliciesAPI {
53
+ constructor(config, httpClient) {
54
+ this.config = config;
55
+ this.httpClient = httpClient;
56
+ this.versions = new PolicyVersionsAPI(this);
57
+ }
58
+ async list(params) {
59
+ const query = PaginationHelper.buildQueryParams(this.config.organizationId, params?.page, params?.pageSize);
60
+ return this.httpClient.get("/v1/policies/", query);
61
+ }
62
+ async retrieve(policyId) {
63
+ validateUuid(policyId, "policyId");
64
+ return this.httpClient.get(`/v1/policies/${policyId}/`);
65
+ }
66
+ async create(params) {
67
+ const payload = {
68
+ name: params.name,
69
+ content: params.content,
70
+ description: params.description ?? "",
71
+ organization_id: this.config.organizationId,
72
+ };
73
+ if (params.versionName !== undefined)
74
+ payload.version_name = params.versionName;
75
+ return this.httpClient.post({ url: "/v1/policies/", json: payload });
76
+ }
77
+ async update(policyId, updates) {
78
+ validateUuid(policyId, "policyId");
79
+ const payload = {};
80
+ if (updates.name !== undefined)
81
+ payload.name = updates.name;
82
+ if (updates.description !== undefined)
83
+ payload.description = updates.description;
84
+ return this.httpClient.put(`/v1/policies/${policyId}/`, payload);
85
+ }
86
+ async delete(policyId) {
87
+ validateUuid(policyId, "policyId");
88
+ await this.httpClient.delete(`/v1/policies/${policyId}/`);
89
+ }
90
+ }
package/dist/client.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { AgentsAPI } from "./api/agents.js";
2
+ import { PoliciesAPI } from "./api/policies.js";
2
3
  import { RoeAuth } from "./auth.js";
3
4
  import { RoeConfig, RoeConfigInput } from "./config.js";
4
5
  import { RoeHTTPClient } from "./utils/httpClient.js";
@@ -7,7 +8,9 @@ export declare class RoeClient {
7
8
  readonly auth: RoeAuth;
8
9
  readonly httpClient: RoeHTTPClient;
9
10
  private readonly _agents;
11
+ private readonly _policies;
10
12
  constructor(input?: RoeConfigInput);
11
13
  get agents(): AgentsAPI;
14
+ get policies(): PoliciesAPI;
12
15
  close(): void;
13
16
  }
package/dist/client.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { AgentsAPI } from "./api/agents.js";
2
+ import { PoliciesAPI } from "./api/policies.js";
2
3
  import { RoeAuth } from "./auth.js";
3
4
  import { RoeConfig } from "./config.js";
4
5
  import { RoeHTTPClient } from "./utils/httpClient.js";
@@ -8,10 +9,14 @@ export class RoeClient {
8
9
  this.auth = new RoeAuth(this.config);
9
10
  this.httpClient = new RoeHTTPClient(this.config, this.auth);
10
11
  this._agents = new AgentsAPI(this.config, this.httpClient);
12
+ this._policies = new PoliciesAPI(this.config, this.httpClient);
11
13
  }
12
14
  get agents() {
13
15
  return this._agents;
14
16
  }
17
+ get policies() {
18
+ return this._policies;
19
+ }
15
20
  close() {
16
21
  this.httpClient.close();
17
22
  }
@@ -43,6 +43,13 @@ export function extractErrorMessage(data, statusCode) {
43
43
  if (typeof data === "string") {
44
44
  return data;
45
45
  }
46
+ // DRF ValidationError returns a plain list like ["error message"]
47
+ if (Array.isArray(data)) {
48
+ if (data.length > 0) {
49
+ return data.map(String).join("; ");
50
+ }
51
+ return `HTTP ${statusCode} error`;
52
+ }
46
53
  if (typeof data === "object" && data !== null) {
47
54
  // Try common error message field names
48
55
  const obj = data;
package/dist/index.d.ts CHANGED
@@ -4,7 +4,9 @@ export type { RoeConfigInput } from "./config.js";
4
4
  export { RoeAuth } from "./auth.js";
5
5
  export * from "./exceptions.js";
6
6
  export * from "./api/agents.js";
7
+ export * from "./api/policies.js";
7
8
  export * from "./models/agent.js";
9
+ export * from "./models/policy.js";
8
10
  export * from "./models/job.js";
9
11
  export * from "./models/responses.js";
10
12
  export * from "./models/file.js";
package/dist/index.js CHANGED
@@ -3,7 +3,9 @@ export { RoeConfig } from "./config.js";
3
3
  export { RoeAuth } from "./auth.js";
4
4
  export * from "./exceptions.js";
5
5
  export * from "./api/agents.js";
6
+ export * from "./api/policies.js";
6
7
  export * from "./models/agent.js";
8
+ export * from "./models/policy.js";
7
9
  export * from "./models/job.js";
8
10
  export * from "./models/responses.js";
9
11
  export * from "./models/file.js";
@@ -22,7 +22,7 @@ export declare class JobBatch {
22
22
  private readonly jobIds;
23
23
  private readonly timeoutSeconds;
24
24
  private completed;
25
- private statusCache;
25
+ private statuses;
26
26
  constructor(opts: {
27
27
  agentsApi: AgentsAPI;
28
28
  jobIds: string[];
@@ -33,5 +33,5 @@ export declare class JobBatch {
33
33
  intervalSeconds?: number;
34
34
  timeoutSeconds?: number;
35
35
  }): Promise<AgentJobResult[]>;
36
- retrieveStatus(): Promise<Record<string, number>>;
36
+ retrieveStatus(): Promise<Record<string, AgentJobStatus>>;
37
37
  }