robotrock 0.9.0 → 1.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/dist/ai/index.d.ts +18 -7
- package/dist/ai/index.js +852 -115
- package/dist/ai/index.js.map +1 -1
- package/dist/ai/trigger.d.ts +4 -3
- package/dist/ai/trigger.js +810 -115
- package/dist/ai/trigger.js.map +1 -1
- package/dist/ai/workflow.d.ts +15 -4
- package/dist/ai/workflow.js +729 -115
- package/dist/ai/workflow.js.map +1 -1
- package/dist/client-XTnFHGFE.d.ts +248 -0
- package/dist/eve/agent/index.d.ts +188 -0
- package/dist/eve/agent/index.js +2322 -0
- package/dist/eve/agent/index.js.map +1 -0
- package/dist/eve/index.d.ts +5 -0
- package/dist/eve/index.js +446 -0
- package/dist/eve/index.js.map +1 -0
- package/dist/eve/tools/identity/index.d.ts +6 -0
- package/dist/eve/tools/identity/index.js +144 -0
- package/dist/eve/tools/identity/index.js.map +1 -0
- package/dist/eve/tools/identity/my-access.d.ts +58 -0
- package/dist/eve/tools/identity/my-access.js +106 -0
- package/dist/eve/tools/identity/my-access.js.map +1 -0
- package/dist/eve/tools/identity/whoami.d.ts +45 -0
- package/dist/eve/tools/identity/whoami.js +101 -0
- package/dist/eve/tools/identity/whoami.js.map +1 -0
- package/dist/eve/tools/inbox/create-task.d.ts +113 -0
- package/dist/eve/tools/inbox/create-task.js +1557 -0
- package/dist/eve/tools/inbox/create-task.js.map +1 -0
- package/dist/eve/tools/inbox/index.d.ts +5 -0
- package/dist/eve/tools/inbox/index.js +1557 -0
- package/dist/eve/tools/inbox/index.js.map +1 -0
- package/dist/eve/tools/index.d.ts +17 -0
- package/dist/eve/tools/index.js +1654 -0
- package/dist/eve/tools/index.js.map +1 -0
- package/dist/index-BL9qKHA8.d.ts +141 -0
- package/dist/index.d.ts +12 -44
- package/dist/index.js +793 -97
- package/dist/index.js.map +1 -1
- package/dist/schemas/index.d.ts +11 -1
- package/dist/schemas/index.js +126 -8
- package/dist/schemas/index.js.map +1 -1
- package/dist/tenant-5YKDrdC-.d.ts +23 -0
- package/dist/{tool-approval-bridge-G765kMJR.d.ts → tool-approval-bridge-C4bTm8vu.d.ts} +93 -2
- package/dist/trigger/index.d.ts +2 -1
- package/dist/trigger/index.js +495 -87
- package/dist/trigger/index.js.map +1 -1
- package/dist/{trigger-D0shjqk0.d.ts → trigger-Dn0DFiyU.d.ts} +64 -3
- package/dist/workflow/index.d.ts +2 -1
- package/dist/workflow/index.js +496 -88
- package/dist/workflow/index.js.map +1 -1
- package/package.json +44 -7
- package/dist/client-Cy7YLxms.d.ts +0 -145
package/dist/index.js
CHANGED
|
@@ -108,6 +108,25 @@ function isBlockedHostname(hostname) {
|
|
|
108
108
|
}
|
|
109
109
|
return isBlockedIpv4(host) || isBlockedIpv6(host);
|
|
110
110
|
}
|
|
111
|
+
function isLoopbackHandlerUrl(urlString) {
|
|
112
|
+
let url;
|
|
113
|
+
try {
|
|
114
|
+
url = new URL(urlString);
|
|
115
|
+
} catch {
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") {
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
if (url.username || url.password) {
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
const host = url.hostname.toLowerCase();
|
|
125
|
+
return host === "localhost" || host === "127.0.0.1" || host === "::1" || host === "[::1]" || host.endsWith(".localhost");
|
|
126
|
+
}
|
|
127
|
+
function isAllowedHandlerUrl(urlString) {
|
|
128
|
+
return isPublicHttpUrl(urlString) || isLoopbackHandlerUrl(urlString);
|
|
129
|
+
}
|
|
111
130
|
function isPublicHttpUrl(urlString) {
|
|
112
131
|
let url;
|
|
113
132
|
try {
|
|
@@ -124,6 +143,7 @@ function isPublicHttpUrl(urlString) {
|
|
|
124
143
|
return !isBlockedHostname(url.hostname);
|
|
125
144
|
}
|
|
126
145
|
var PUBLIC_HTTP_URL_ERROR = "URL must be a public http:// or https:// address";
|
|
146
|
+
var HANDLER_URL_ERROR = "Handler URL must be a public or loopback http:// or https:// address";
|
|
127
147
|
|
|
128
148
|
// ../core/src/schemas/task.ts
|
|
129
149
|
import { z } from "zod";
|
|
@@ -185,6 +205,9 @@ function validateContextPublicUrls(context2) {
|
|
|
185
205
|
var safeUrlSchema = z.string().refine((url) => isPublicHttpUrl(url), {
|
|
186
206
|
message: PUBLIC_HTTP_URL_ERROR
|
|
187
207
|
});
|
|
208
|
+
var handlerUrlSchema = z.string().refine((url) => isAllowedHandlerUrl(url), {
|
|
209
|
+
message: HANDLER_URL_ERROR
|
|
210
|
+
});
|
|
188
211
|
var jsonSchema7Schema = z.custom(
|
|
189
212
|
(val) => typeof val === "object" && val !== null,
|
|
190
213
|
{ message: "Must be a valid JSON Schema object" }
|
|
@@ -194,7 +217,7 @@ var uiSchemaSchema = z.custom((val) => typeof val === "object" && val !== null,
|
|
|
194
217
|
});
|
|
195
218
|
var webhookHandlerSchema = z.object({
|
|
196
219
|
type: z.literal("webhook"),
|
|
197
|
-
url:
|
|
220
|
+
url: handlerUrlSchema,
|
|
198
221
|
headers: z.record(z.string(), z.string())
|
|
199
222
|
});
|
|
200
223
|
var triggerHandlerSchema = webhookHandlerSchema.extend({
|
|
@@ -202,13 +225,15 @@ var triggerHandlerSchema = webhookHandlerSchema.extend({
|
|
|
202
225
|
tokenId: z.string().min(1)
|
|
203
226
|
});
|
|
204
227
|
var handlerSchema = z.discriminatedUnion("type", [webhookHandlerSchema, triggerHandlerSchema]);
|
|
205
|
-
var
|
|
228
|
+
var taskActionInputSchema = z.object({
|
|
206
229
|
id: z.string().min(1),
|
|
207
230
|
title: z.string().min(1),
|
|
208
231
|
description: z.string().optional(),
|
|
209
232
|
schema: jsonSchema7Schema.optional(),
|
|
210
233
|
ui: uiSchemaSchema.optional(),
|
|
211
|
-
data: z.record(z.string(), z.unknown()).optional()
|
|
234
|
+
data: z.record(z.string(), z.unknown()).optional()
|
|
235
|
+
});
|
|
236
|
+
var taskActionSchema = taskActionInputSchema.extend({
|
|
212
237
|
// Optional handlers for this action - if present, must have at least 1
|
|
213
238
|
handlers: z.array(handlerSchema).min(1).optional()
|
|
214
239
|
});
|
|
@@ -316,10 +341,100 @@ var createTaskBodySchema = taskContextObjectBaseSchema.extend({
|
|
|
316
341
|
/** Agent release version — not shown in inbox UI. */
|
|
317
342
|
agent: agentTelemetrySchema.optional()
|
|
318
343
|
}).transform(normalizeTaskContextVersion).superRefine(refineContextPublicUrls);
|
|
344
|
+
var agentChatSeedActionSchema = z.object({
|
|
345
|
+
/** Stable action id echoed back on submit; auto-derived from title when omitted. */
|
|
346
|
+
id: z.string().min(1).optional(),
|
|
347
|
+
title: z.string().min(1),
|
|
348
|
+
description: z.string().optional(),
|
|
349
|
+
/** JSON Schema object for the form fields. */
|
|
350
|
+
schema: z.record(z.string(), z.unknown()).optional(),
|
|
351
|
+
/** RJSF ui schema overrides (ui:widget, ui:enumNames, etc.). */
|
|
352
|
+
ui: z.record(z.string(), z.unknown()).optional(),
|
|
353
|
+
/** Optional default field values. */
|
|
354
|
+
data: z.record(z.string(), z.unknown()).optional()
|
|
355
|
+
});
|
|
356
|
+
var agentChatSeedMessageSchema = z.object({
|
|
357
|
+
role: z.enum(["user", "assistant"]),
|
|
358
|
+
text: z.string().min(1),
|
|
359
|
+
/** Optional display-name override for the message sender. */
|
|
360
|
+
senderName: z.string().min(1).optional(),
|
|
361
|
+
/**
|
|
362
|
+
* Optional structured input request rendered as a styled RobotRock action
|
|
363
|
+
* widget below the message text. Use this instead of asking the user to reply
|
|
364
|
+
* in plain text so the request is always a proper action form.
|
|
365
|
+
*/
|
|
366
|
+
requestActionInput: z.object({
|
|
367
|
+
prompt: z.string().optional(),
|
|
368
|
+
action: agentChatSeedActionSchema
|
|
369
|
+
}).optional()
|
|
370
|
+
});
|
|
371
|
+
var eveAgentChatTransportSchema = z.object({
|
|
372
|
+
kind: z.literal("eve"),
|
|
373
|
+
chatId: z.string().min(1),
|
|
374
|
+
baseUrl: z.string().url(),
|
|
375
|
+
sessionId: z.string().optional(),
|
|
376
|
+
continuationToken: z.string().optional(),
|
|
377
|
+
streamIndex: z.number().int().nonnegative().optional(),
|
|
378
|
+
isStreaming: z.boolean().optional()
|
|
379
|
+
});
|
|
380
|
+
var createAgentChatBodySchema = z.object({
|
|
381
|
+
/** Agent id (eve agent name). Required unless `parentChatId` is set. */
|
|
382
|
+
agentIdentifier: z.string().min(1).optional(),
|
|
383
|
+
/** Inherit tenant/connection/agent from an existing chat (agent-spawned chats). */
|
|
384
|
+
parentChatId: z.string().min(1).optional(),
|
|
385
|
+
/** Convex tenantEveConnections id; resolved from the tenant when omitted. */
|
|
386
|
+
eveConnectionId: z.string().min(1).optional(),
|
|
387
|
+
/** Source application id; groups the chat under an inbox section. */
|
|
388
|
+
app: z.string().min(1).optional(),
|
|
389
|
+
assignTo: assignToSchema.optional(),
|
|
390
|
+
title: z.string().min(1),
|
|
391
|
+
messages: z.array(agentChatSeedMessageSchema).default([]),
|
|
392
|
+
/** Provenance label stored on the session ("cron" | "agent" | ...). */
|
|
393
|
+
source: z.string().min(1).optional()
|
|
394
|
+
}).refine((data) => Boolean(data.agentIdentifier || data.parentChatId), {
|
|
395
|
+
message: "Provide either agentIdentifier or parentChatId"
|
|
396
|
+
});
|
|
397
|
+
var agentChatAuditInputBodySchema = z.object({
|
|
398
|
+
eveSessionId: z.string().min(1),
|
|
399
|
+
userId: z.string().min(1),
|
|
400
|
+
actionId: z.string().min(1),
|
|
401
|
+
actionTitle: z.string().optional(),
|
|
402
|
+
prompt: z.string().optional(),
|
|
403
|
+
data: z.record(z.string(), z.unknown()).optional(),
|
|
404
|
+
idempotencyKey: z.string().optional(),
|
|
405
|
+
/** Eve input request id — used to merge staged HITL metadata. */
|
|
406
|
+
requestId: z.string().optional(),
|
|
407
|
+
/** Eve tool call id — fallback lookup for staged HITL metadata. */
|
|
408
|
+
toolCallId: z.string().optional()
|
|
409
|
+
});
|
|
410
|
+
var eveHitlStagedOptionSchema = z.object({
|
|
411
|
+
id: z.string(),
|
|
412
|
+
label: z.string()
|
|
413
|
+
});
|
|
414
|
+
var agentChatStageHitlBodySchema = z.object({
|
|
415
|
+
eveSessionId: z.string().min(1),
|
|
416
|
+
requests: z.array(
|
|
417
|
+
z.object({
|
|
418
|
+
requestId: z.string().min(1),
|
|
419
|
+
toolCallId: z.string().min(1),
|
|
420
|
+
toolName: z.string().min(1),
|
|
421
|
+
prompt: z.string().min(1),
|
|
422
|
+
display: z.enum(["confirmation", "select", "text"]).optional(),
|
|
423
|
+
allowFreeform: z.boolean().optional(),
|
|
424
|
+
options: z.array(eveHitlStagedOptionSchema).optional(),
|
|
425
|
+
toolInput: z.record(z.string(), z.unknown()).optional()
|
|
426
|
+
})
|
|
427
|
+
)
|
|
428
|
+
});
|
|
429
|
+
var agentChatLinkTaskBodySchema = z.object({
|
|
430
|
+
eveSessionId: z.string().min(1),
|
|
431
|
+
publicTaskId: z.string().min(1),
|
|
432
|
+
toolCallId: z.string().min(1)
|
|
433
|
+
});
|
|
319
434
|
|
|
320
435
|
// src/schemas/index.ts
|
|
321
|
-
var
|
|
322
|
-
message:
|
|
436
|
+
var handlerUrlSchema2 = z2.string().refine((url) => isAllowedHandlerUrl(url), {
|
|
437
|
+
message: HANDLER_URL_ERROR
|
|
323
438
|
});
|
|
324
439
|
var jsonSchema7Schema2 = z2.custom(
|
|
325
440
|
(val) => typeof val === "object" && val !== null,
|
|
@@ -330,7 +445,7 @@ var uiSchemaSchema2 = z2.custom((val) => typeof val === "object" && val !== null
|
|
|
330
445
|
});
|
|
331
446
|
var webhookHandlerSchema2 = z2.object({
|
|
332
447
|
type: z2.literal("webhook"),
|
|
333
|
-
url:
|
|
448
|
+
url: handlerUrlSchema2,
|
|
334
449
|
headers: z2.record(z2.string(), z2.string())
|
|
335
450
|
});
|
|
336
451
|
var triggerHandlerSchema2 = webhookHandlerSchema2.extend({
|
|
@@ -338,13 +453,15 @@ var triggerHandlerSchema2 = webhookHandlerSchema2.extend({
|
|
|
338
453
|
tokenId: z2.string().min(1)
|
|
339
454
|
});
|
|
340
455
|
var handlerSchema2 = z2.discriminatedUnion("type", [webhookHandlerSchema2, triggerHandlerSchema2]);
|
|
341
|
-
var
|
|
456
|
+
var taskActionInputSchema2 = z2.object({
|
|
342
457
|
id: z2.string().min(1),
|
|
343
458
|
title: z2.string().min(1),
|
|
344
459
|
description: z2.string().optional(),
|
|
345
460
|
schema: jsonSchema7Schema2.optional(),
|
|
346
461
|
ui: uiSchemaSchema2.optional(),
|
|
347
|
-
data: z2.record(z2.string(), z2.unknown()).optional()
|
|
462
|
+
data: z2.record(z2.string(), z2.unknown()).optional()
|
|
463
|
+
});
|
|
464
|
+
var taskActionSchema2 = taskActionInputSchema2.extend({
|
|
348
465
|
handlers: z2.array(handlerSchema2).min(1).optional()
|
|
349
466
|
});
|
|
350
467
|
var uiFieldSchemaSchema2 = z2.object({
|
|
@@ -485,6 +602,293 @@ function toDiscriminatedApprovalResult(actions, task) {
|
|
|
485
602
|
};
|
|
486
603
|
}
|
|
487
604
|
|
|
605
|
+
// src/http.ts
|
|
606
|
+
var RobotRockError = class extends Error {
|
|
607
|
+
constructor(message, statusCode, response) {
|
|
608
|
+
super(message);
|
|
609
|
+
this.statusCode = statusCode;
|
|
610
|
+
this.response = response;
|
|
611
|
+
this.name = "RobotRockError";
|
|
612
|
+
}
|
|
613
|
+
};
|
|
614
|
+
async function parseResponseBody(response) {
|
|
615
|
+
const contentType = response.headers.get("content-type") ?? "";
|
|
616
|
+
const bodyText = await response.text();
|
|
617
|
+
if (!bodyText) {
|
|
618
|
+
return null;
|
|
619
|
+
}
|
|
620
|
+
if (contentType.toLowerCase().includes("application/json")) {
|
|
621
|
+
try {
|
|
622
|
+
return JSON.parse(bodyText);
|
|
623
|
+
} catch {
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
try {
|
|
627
|
+
return JSON.parse(bodyText);
|
|
628
|
+
} catch {
|
|
629
|
+
return bodyText;
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
function getErrorMessage(data, fallback) {
|
|
633
|
+
if (data && typeof data === "object" && !Array.isArray(data)) {
|
|
634
|
+
const record = data;
|
|
635
|
+
const maybeMessage = record.message;
|
|
636
|
+
const maybeHint = record.hint;
|
|
637
|
+
if (typeof maybeMessage === "string" && maybeMessage.trim()) {
|
|
638
|
+
if (typeof maybeHint === "string" && maybeHint.trim()) {
|
|
639
|
+
return `${maybeMessage.trim()} ${maybeHint.trim()}`;
|
|
640
|
+
}
|
|
641
|
+
return maybeMessage;
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
if (typeof data === "string" && data.trim()) {
|
|
645
|
+
const compact = data.replace(/\s+/g, " ").trim();
|
|
646
|
+
const snippet = compact.length > 180 ? `${compact.slice(0, 180)}...` : compact;
|
|
647
|
+
return `${fallback}. Server returned non-JSON response: ${snippet}`;
|
|
648
|
+
}
|
|
649
|
+
return fallback;
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
// ../core/src/handler-retry.ts
|
|
653
|
+
var HANDLER_RETRY_DELAYS_MS = [
|
|
654
|
+
6e4,
|
|
655
|
+
// 1m
|
|
656
|
+
3e5,
|
|
657
|
+
// 5m
|
|
658
|
+
18e5,
|
|
659
|
+
// 30m
|
|
660
|
+
36e5,
|
|
661
|
+
// 1h
|
|
662
|
+
216e5,
|
|
663
|
+
// 6h
|
|
664
|
+
864e5,
|
|
665
|
+
// 24h
|
|
666
|
+
1728e5
|
|
667
|
+
// 48h
|
|
668
|
+
];
|
|
669
|
+
var HANDLER_MAX_ATTEMPTS = HANDLER_RETRY_DELAYS_MS.length + 1;
|
|
670
|
+
|
|
671
|
+
// ../core/src/attribution/index.ts
|
|
672
|
+
import { z as z3 } from "zod";
|
|
673
|
+
|
|
674
|
+
// ../core/src/app-url.ts
|
|
675
|
+
var PRODUCTION_APP_URL = "https://app.robotrock.io";
|
|
676
|
+
var PRODUCTION_API_URL = "https://api.robotrock.io/v1";
|
|
677
|
+
var DEV_API_URL = "http://localhost:4001/v1";
|
|
678
|
+
var APP_SIGNUP_URL = `${PRODUCTION_APP_URL}/sign-up`;
|
|
679
|
+
function normalizeRobotRockApiBaseUrl(baseUrl) {
|
|
680
|
+
const trimmed = baseUrl.trim().replace(/\/+$/, "");
|
|
681
|
+
if (trimmed.endsWith("/v1")) {
|
|
682
|
+
return trimmed;
|
|
683
|
+
}
|
|
684
|
+
return `${trimmed}/v1`;
|
|
685
|
+
}
|
|
686
|
+
function getRobotRockApiBaseUrl() {
|
|
687
|
+
const explicit = process.env.ROBOTROCK_BASE_URL?.trim() || process.env.ROBOTROCK_API_URL?.trim();
|
|
688
|
+
if (explicit) {
|
|
689
|
+
return normalizeRobotRockApiBaseUrl(explicit);
|
|
690
|
+
}
|
|
691
|
+
if (process.env.NODE_ENV === "production") {
|
|
692
|
+
return PRODUCTION_API_URL;
|
|
693
|
+
}
|
|
694
|
+
return DEV_API_URL;
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
// ../core/src/attribution/index.ts
|
|
698
|
+
var ATTRIBUTION_COOKIE_MAX_AGE = 60 * 60 * 24 * 90;
|
|
699
|
+
var attributionSchema = z3.object({
|
|
700
|
+
utmSource: z3.string().optional(),
|
|
701
|
+
utmMedium: z3.string().optional(),
|
|
702
|
+
utmCampaign: z3.string().optional(),
|
|
703
|
+
utmContent: z3.string().optional(),
|
|
704
|
+
utmTerm: z3.string().optional(),
|
|
705
|
+
gclid: z3.string().optional(),
|
|
706
|
+
landingUrl: z3.string().optional(),
|
|
707
|
+
referrer: z3.string().optional(),
|
|
708
|
+
capturedAt: z3.number()
|
|
709
|
+
});
|
|
710
|
+
|
|
711
|
+
// src/auth-headers.ts
|
|
712
|
+
function buildRobotRockAuthHeaders(auth) {
|
|
713
|
+
if (auth.kind === "apiKey") {
|
|
714
|
+
return {
|
|
715
|
+
"X-Api-Key": auth.apiKey
|
|
716
|
+
};
|
|
717
|
+
}
|
|
718
|
+
return {
|
|
719
|
+
Authorization: `Bearer ${auth.token}`,
|
|
720
|
+
"X-RobotRock-Tenant-Slug": auth.tenantSlug,
|
|
721
|
+
"X-RobotRock-Connection-Id": auth.connectionId
|
|
722
|
+
};
|
|
723
|
+
}
|
|
724
|
+
function resolveRobotRockAuthConfig(overrides) {
|
|
725
|
+
if (overrides?.agentService) {
|
|
726
|
+
return {
|
|
727
|
+
kind: "agentService",
|
|
728
|
+
...overrides.agentService
|
|
729
|
+
};
|
|
730
|
+
}
|
|
731
|
+
const apiKey = overrides?.apiKey ?? process.env.ROBOTROCK_API_KEY;
|
|
732
|
+
if (!apiKey) {
|
|
733
|
+
throw new Error(
|
|
734
|
+
"RobotRock auth is required. Set ROBOTROCK_API_KEY, ROBOTROCK_AGENT_SERVICE_TOKEN with tenant context, or pass auth when creating the client."
|
|
735
|
+
);
|
|
736
|
+
}
|
|
737
|
+
return { kind: "apiKey", apiKey };
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
// src/chats.ts
|
|
741
|
+
function createChatsApi(config) {
|
|
742
|
+
const headers = () => ({
|
|
743
|
+
"Content-Type": "application/json",
|
|
744
|
+
...buildRobotRockAuthHeaders(config.auth)
|
|
745
|
+
});
|
|
746
|
+
return {
|
|
747
|
+
async create(input) {
|
|
748
|
+
const bodyPayload = {
|
|
749
|
+
...input,
|
|
750
|
+
app: input.app ?? config.app,
|
|
751
|
+
messages: input.messages ?? []
|
|
752
|
+
};
|
|
753
|
+
const validation = createAgentChatBodySchema.safeParse(bodyPayload);
|
|
754
|
+
if (!validation.success) {
|
|
755
|
+
throw new RobotRockError(
|
|
756
|
+
`Invalid chat: ${validation.error.issues[0]?.message}`,
|
|
757
|
+
400,
|
|
758
|
+
validation.error.issues
|
|
759
|
+
);
|
|
760
|
+
}
|
|
761
|
+
const response = await fetch(`${config.baseUrl}/agent-chats`, {
|
|
762
|
+
method: "POST",
|
|
763
|
+
headers: headers(),
|
|
764
|
+
body: JSON.stringify(validation.data)
|
|
765
|
+
});
|
|
766
|
+
const data = await parseResponseBody(response);
|
|
767
|
+
if (!response.ok) {
|
|
768
|
+
throw new RobotRockError(
|
|
769
|
+
getErrorMessage(data, "Failed to create chat"),
|
|
770
|
+
response.status,
|
|
771
|
+
data
|
|
772
|
+
);
|
|
773
|
+
}
|
|
774
|
+
const result = data;
|
|
775
|
+
return { tenantSlug: result.tenantSlug, chats: result.chats };
|
|
776
|
+
},
|
|
777
|
+
async close(chatId, options) {
|
|
778
|
+
if (!chatId) {
|
|
779
|
+
throw new RobotRockError("chatId is required to close a chat", 400);
|
|
780
|
+
}
|
|
781
|
+
const response = await fetch(
|
|
782
|
+
`${config.baseUrl}/agent-chats/${encodeURIComponent(chatId)}/close`,
|
|
783
|
+
{
|
|
784
|
+
method: "POST",
|
|
785
|
+
headers: headers(),
|
|
786
|
+
body: JSON.stringify({ reason: options?.reason })
|
|
787
|
+
}
|
|
788
|
+
);
|
|
789
|
+
if (!response.ok) {
|
|
790
|
+
const data = await parseResponseBody(response);
|
|
791
|
+
throw new RobotRockError(
|
|
792
|
+
getErrorMessage(data, "Failed to close chat"),
|
|
793
|
+
response.status,
|
|
794
|
+
data
|
|
795
|
+
);
|
|
796
|
+
}
|
|
797
|
+
},
|
|
798
|
+
async stageHitlRequests(input) {
|
|
799
|
+
const validation = agentChatStageHitlBodySchema.safeParse(input);
|
|
800
|
+
if (!validation.success) {
|
|
801
|
+
throw new RobotRockError(
|
|
802
|
+
`Invalid stage HITL input: ${validation.error.issues[0]?.message}`,
|
|
803
|
+
400,
|
|
804
|
+
validation.error.issues
|
|
805
|
+
);
|
|
806
|
+
}
|
|
807
|
+
const response = await fetch(`${config.baseUrl}/agent-chats/stage-hitl`, {
|
|
808
|
+
method: "POST",
|
|
809
|
+
headers: headers(),
|
|
810
|
+
body: JSON.stringify(validation.data)
|
|
811
|
+
});
|
|
812
|
+
if (!response.ok) {
|
|
813
|
+
const data = await parseResponseBody(response);
|
|
814
|
+
throw new RobotRockError(
|
|
815
|
+
getErrorMessage(data, "Failed to stage chat HITL requests"),
|
|
816
|
+
response.status,
|
|
817
|
+
data
|
|
818
|
+
);
|
|
819
|
+
}
|
|
820
|
+
},
|
|
821
|
+
async getStagedHitlRequests(eveSessionId) {
|
|
822
|
+
const trimmed = eveSessionId.trim();
|
|
823
|
+
if (!trimmed) {
|
|
824
|
+
throw new RobotRockError("eveSessionId is required", 400);
|
|
825
|
+
}
|
|
826
|
+
const url = new URL(`${config.baseUrl}/agent-chats/staged-hitl`);
|
|
827
|
+
url.searchParams.set("eveSessionId", trimmed);
|
|
828
|
+
const response = await fetch(url.toString(), {
|
|
829
|
+
method: "GET",
|
|
830
|
+
headers: headers()
|
|
831
|
+
});
|
|
832
|
+
const data = await parseResponseBody(response);
|
|
833
|
+
if (!response.ok) {
|
|
834
|
+
throw new RobotRockError(
|
|
835
|
+
getErrorMessage(data, "Failed to fetch staged chat HITL requests"),
|
|
836
|
+
response.status,
|
|
837
|
+
data
|
|
838
|
+
);
|
|
839
|
+
}
|
|
840
|
+
const requests = data.requests;
|
|
841
|
+
return Array.isArray(requests) ? requests : [];
|
|
842
|
+
},
|
|
843
|
+
async logInputSubmission(input) {
|
|
844
|
+
const validation = agentChatAuditInputBodySchema.safeParse(input);
|
|
845
|
+
if (!validation.success) {
|
|
846
|
+
throw new RobotRockError(
|
|
847
|
+
`Invalid audit input: ${validation.error.issues[0]?.message}`,
|
|
848
|
+
400,
|
|
849
|
+
validation.error.issues
|
|
850
|
+
);
|
|
851
|
+
}
|
|
852
|
+
const response = await fetch(`${config.baseUrl}/agent-chats/audit-input`, {
|
|
853
|
+
method: "POST",
|
|
854
|
+
headers: headers(),
|
|
855
|
+
body: JSON.stringify(validation.data)
|
|
856
|
+
});
|
|
857
|
+
if (!response.ok) {
|
|
858
|
+
const data = await parseResponseBody(response);
|
|
859
|
+
throw new RobotRockError(
|
|
860
|
+
getErrorMessage(data, "Failed to log chat input submission"),
|
|
861
|
+
response.status,
|
|
862
|
+
data
|
|
863
|
+
);
|
|
864
|
+
}
|
|
865
|
+
},
|
|
866
|
+
async linkTask(input) {
|
|
867
|
+
const validation = agentChatLinkTaskBodySchema.safeParse(input);
|
|
868
|
+
if (!validation.success) {
|
|
869
|
+
throw new RobotRockError(
|
|
870
|
+
`Invalid link task input: ${validation.error.issues[0]?.message}`,
|
|
871
|
+
400,
|
|
872
|
+
validation.error.issues
|
|
873
|
+
);
|
|
874
|
+
}
|
|
875
|
+
const response = await fetch(`${config.baseUrl}/agent-chats/link-task`, {
|
|
876
|
+
method: "POST",
|
|
877
|
+
headers: headers(),
|
|
878
|
+
body: JSON.stringify(validation.data)
|
|
879
|
+
});
|
|
880
|
+
if (!response.ok) {
|
|
881
|
+
const data = await parseResponseBody(response);
|
|
882
|
+
throw new RobotRockError(
|
|
883
|
+
getErrorMessage(data, "Failed to link inbox task to chat"),
|
|
884
|
+
response.status,
|
|
885
|
+
data
|
|
886
|
+
);
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
};
|
|
890
|
+
}
|
|
891
|
+
|
|
488
892
|
// src/client.ts
|
|
489
893
|
var DEFAULT_POLL_INTERVAL_MS = 2e3;
|
|
490
894
|
var DEFAULT_TIMEOUT_MS = 24 * 60 * 60 * 1e3;
|
|
@@ -522,22 +926,18 @@ function serializeValidUntil(value) {
|
|
|
522
926
|
}
|
|
523
927
|
throw new RobotRockError("Invalid validUntil: expected a Date or parseable date string", 400);
|
|
524
928
|
}
|
|
525
|
-
var RobotRockError = class extends Error {
|
|
526
|
-
constructor(message, statusCode, response) {
|
|
527
|
-
super(message);
|
|
528
|
-
this.statusCode = statusCode;
|
|
529
|
-
this.response = response;
|
|
530
|
-
this.name = "RobotRockError";
|
|
531
|
-
}
|
|
532
|
-
};
|
|
533
929
|
var RobotRock = class {
|
|
534
|
-
|
|
930
|
+
auth;
|
|
535
931
|
baseUrl;
|
|
536
932
|
app;
|
|
537
933
|
agentVersion;
|
|
538
934
|
contextVersion;
|
|
539
935
|
webhook;
|
|
540
936
|
polling;
|
|
937
|
+
/** Task CRUD: `create`, `get`, `cancel`, `sendUpdate`. */
|
|
938
|
+
tasks;
|
|
939
|
+
/** Chat CRUD: `create`, `close`. */
|
|
940
|
+
chats;
|
|
541
941
|
constructor(config) {
|
|
542
942
|
if (config.webhook && config.polling) {
|
|
543
943
|
throw new Error(
|
|
@@ -545,24 +945,42 @@ var RobotRock = class {
|
|
|
545
945
|
);
|
|
546
946
|
}
|
|
547
947
|
const apiKey = config.apiKey ?? process.env.ROBOTROCK_API_KEY;
|
|
548
|
-
|
|
948
|
+
const agentService = config.agentService;
|
|
949
|
+
if (apiKey && agentService) {
|
|
549
950
|
throw new Error(
|
|
550
|
-
"RobotRock
|
|
951
|
+
"RobotRock client cannot configure both apiKey and agentService."
|
|
551
952
|
);
|
|
552
953
|
}
|
|
553
|
-
this.
|
|
554
|
-
|
|
555
|
-
|
|
954
|
+
this.auth = resolveRobotRockAuthConfig({
|
|
955
|
+
...apiKey ? { apiKey } : {},
|
|
956
|
+
...agentService ? { agentService } : {}
|
|
957
|
+
});
|
|
958
|
+
this.baseUrl = config.baseUrl ?? getRobotRockApiBaseUrl();
|
|
556
959
|
this.app = config.app;
|
|
557
960
|
this.agentVersion = config.version ?? resolveAgentVersionFromEnv();
|
|
558
961
|
this.contextVersion = config.advanced?.contextVersion ?? TASK_CONTEXT_FORMAT_VERSION2;
|
|
559
962
|
this.webhook = config.webhook;
|
|
560
963
|
this.polling = config.polling ?? {};
|
|
964
|
+
this.tasks = {
|
|
965
|
+
create: (task) => this.createTaskRequest(task),
|
|
966
|
+
get: (taskId) => this.getTaskById(taskId),
|
|
967
|
+
cancel: (taskId) => this.cancelTaskRequest(taskId),
|
|
968
|
+
sendUpdate: (input) => this.sendThreadUpdate(input)
|
|
969
|
+
};
|
|
970
|
+
this.chats = createChatsApi({
|
|
971
|
+
baseUrl: this.baseUrl,
|
|
972
|
+
auth: this.auth,
|
|
973
|
+
app: this.app
|
|
974
|
+
});
|
|
561
975
|
}
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
976
|
+
authHeaders(extra) {
|
|
977
|
+
return {
|
|
978
|
+
"Content-Type": "application/json",
|
|
979
|
+
...buildRobotRockAuthHeaders(this.auth),
|
|
980
|
+
...extra
|
|
981
|
+
};
|
|
982
|
+
}
|
|
983
|
+
async createTaskRequest(task) {
|
|
566
984
|
const normalizedTask = normalizeSendToHumanInput(task, {
|
|
567
985
|
webhook: this.webhook,
|
|
568
986
|
app: this.app,
|
|
@@ -586,13 +1004,9 @@ var RobotRock = class {
|
|
|
586
1004
|
validation.error.issues
|
|
587
1005
|
);
|
|
588
1006
|
}
|
|
589
|
-
const headers =
|
|
590
|
-
"
|
|
591
|
-
|
|
592
|
-
};
|
|
593
|
-
if (task.idempotencyKey) {
|
|
594
|
-
headers["Idempotency-Key"] = task.idempotencyKey;
|
|
595
|
-
}
|
|
1007
|
+
const headers = this.authHeaders(
|
|
1008
|
+
task.idempotencyKey ? { "Idempotency-Key": task.idempotencyKey } : void 0
|
|
1009
|
+
);
|
|
596
1010
|
const response = await fetch(`${this.baseUrl}/`, {
|
|
597
1011
|
method: "POST",
|
|
598
1012
|
headers,
|
|
@@ -615,7 +1029,7 @@ var RobotRock = class {
|
|
|
615
1029
|
contextVersion: this.contextVersion,
|
|
616
1030
|
agentVersion: this.agentVersion
|
|
617
1031
|
});
|
|
618
|
-
const createdTaskTask = await this.
|
|
1032
|
+
const createdTaskTask = await this.createTaskRequest(task);
|
|
619
1033
|
const hasHandlers = normalizedTask.actions.some(
|
|
620
1034
|
(action) => Array.isArray(action.handlers) && action.handlers.length > 0
|
|
621
1035
|
);
|
|
@@ -632,7 +1046,7 @@ var RobotRock = class {
|
|
|
632
1046
|
const deadline = validUntilMs !== void 0 ? Math.min(pollingDeadline, validUntilMs) : pollingDeadline;
|
|
633
1047
|
const taskId = createdTaskTask.taskId;
|
|
634
1048
|
while (Date.now() < deadline) {
|
|
635
|
-
const existing = await this.
|
|
1049
|
+
const existing = await this.getTaskById(taskId);
|
|
636
1050
|
if (existing?.status === "handled" && existing.handled) {
|
|
637
1051
|
return {
|
|
638
1052
|
mode: "handled",
|
|
@@ -654,15 +1068,38 @@ var RobotRock = class {
|
|
|
654
1068
|
}
|
|
655
1069
|
throw new TaskTimeoutError(`No human response within ${timeoutMs}ms`);
|
|
656
1070
|
}
|
|
1071
|
+
/**
|
|
1072
|
+
* Create a task via POST /v1 without waiting for a human response.
|
|
1073
|
+
* @deprecated Use `client.tasks.create()` instead.
|
|
1074
|
+
*/
|
|
1075
|
+
async createTask(task) {
|
|
1076
|
+
return this.tasks.create(task);
|
|
1077
|
+
}
|
|
657
1078
|
/**
|
|
658
1079
|
* Get a task by public task id (returned as `task.taskId` from {@link sendToHuman}).
|
|
1080
|
+
* @deprecated Use `client.tasks.get()` instead.
|
|
659
1081
|
*/
|
|
660
1082
|
async getTask(taskId) {
|
|
1083
|
+
return this.tasks.get(taskId);
|
|
1084
|
+
}
|
|
1085
|
+
/**
|
|
1086
|
+
* Log a status update against a thread.
|
|
1087
|
+
* @deprecated Use `client.tasks.sendUpdate()` instead.
|
|
1088
|
+
*/
|
|
1089
|
+
async sendUpdate(input) {
|
|
1090
|
+
return this.tasks.sendUpdate(input);
|
|
1091
|
+
}
|
|
1092
|
+
/**
|
|
1093
|
+
* Cancel a task by public task id.
|
|
1094
|
+
* @deprecated Use `client.tasks.cancel()` instead.
|
|
1095
|
+
*/
|
|
1096
|
+
async cancelTask(taskId) {
|
|
1097
|
+
return this.tasks.cancel(taskId);
|
|
1098
|
+
}
|
|
1099
|
+
async getTaskById(taskId) {
|
|
661
1100
|
const response = await fetch(`${this.baseUrl}/tasks/${taskId}`, {
|
|
662
1101
|
method: "GET",
|
|
663
|
-
headers:
|
|
664
|
-
"X-Api-Key": this.apiKey
|
|
665
|
-
}
|
|
1102
|
+
headers: this.authHeaders()
|
|
666
1103
|
});
|
|
667
1104
|
if (response.status === 404) {
|
|
668
1105
|
return null;
|
|
@@ -677,11 +1114,11 @@ var RobotRock = class {
|
|
|
677
1114
|
}
|
|
678
1115
|
return data;
|
|
679
1116
|
}
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
1117
|
+
async sendThreadUpdate({
|
|
1118
|
+
threadId,
|
|
1119
|
+
message,
|
|
1120
|
+
status
|
|
1121
|
+
}) {
|
|
685
1122
|
if (!threadId) {
|
|
686
1123
|
throw new RobotRockError("threadId is required to send an update", 400);
|
|
687
1124
|
}
|
|
@@ -697,10 +1134,7 @@ var RobotRock = class {
|
|
|
697
1134
|
`${this.baseUrl}/threads/${encodeURIComponent(threadId)}/updates`,
|
|
698
1135
|
{
|
|
699
1136
|
method: "POST",
|
|
700
|
-
headers:
|
|
701
|
-
"Content-Type": "application/json",
|
|
702
|
-
"X-Api-Key": this.apiKey
|
|
703
|
-
},
|
|
1137
|
+
headers: this.authHeaders(),
|
|
704
1138
|
body: JSON.stringify(validation.data)
|
|
705
1139
|
}
|
|
706
1140
|
);
|
|
@@ -714,12 +1148,10 @@ var RobotRock = class {
|
|
|
714
1148
|
}
|
|
715
1149
|
return data.update;
|
|
716
1150
|
}
|
|
717
|
-
async
|
|
1151
|
+
async cancelTaskRequest(taskId) {
|
|
718
1152
|
const response = await fetch(`${this.baseUrl}/tasks/${taskId}/cancel`, {
|
|
719
1153
|
method: "POST",
|
|
720
|
-
headers:
|
|
721
|
-
"X-Api-Key": this.apiKey
|
|
722
|
-
}
|
|
1154
|
+
headers: this.authHeaders()
|
|
723
1155
|
});
|
|
724
1156
|
if (!response.ok) {
|
|
725
1157
|
const data = await parseResponseBody(response);
|
|
@@ -773,50 +1205,26 @@ function normalizeSendToHumanInput(task, clientDefaults) {
|
|
|
773
1205
|
actions: normalizedActions
|
|
774
1206
|
};
|
|
775
1207
|
}
|
|
776
|
-
async function parseResponseBody(response) {
|
|
777
|
-
const contentType = response.headers.get("content-type") ?? "";
|
|
778
|
-
const bodyText = await response.text();
|
|
779
|
-
if (!bodyText) {
|
|
780
|
-
return null;
|
|
781
|
-
}
|
|
782
|
-
if (contentType.toLowerCase().includes("application/json")) {
|
|
783
|
-
try {
|
|
784
|
-
return JSON.parse(bodyText);
|
|
785
|
-
} catch {
|
|
786
|
-
}
|
|
787
|
-
}
|
|
788
|
-
try {
|
|
789
|
-
return JSON.parse(bodyText);
|
|
790
|
-
} catch {
|
|
791
|
-
return bodyText;
|
|
792
|
-
}
|
|
793
|
-
}
|
|
794
|
-
function getErrorMessage(data, fallback) {
|
|
795
|
-
if (data && typeof data === "object" && !Array.isArray(data)) {
|
|
796
|
-
const maybeMessage = data.message;
|
|
797
|
-
if (typeof maybeMessage === "string" && maybeMessage.trim()) {
|
|
798
|
-
return maybeMessage;
|
|
799
|
-
}
|
|
800
|
-
}
|
|
801
|
-
if (typeof data === "string" && data.trim()) {
|
|
802
|
-
const compact = data.replace(/\s+/g, " ").trim();
|
|
803
|
-
const snippet = compact.length > 180 ? `${compact.slice(0, 180)}...` : compact;
|
|
804
|
-
return `${fallback}. Server returned non-JSON response: ${snippet}`;
|
|
805
|
-
}
|
|
806
|
-
return fallback;
|
|
807
|
-
}
|
|
808
1208
|
|
|
809
1209
|
// src/env.ts
|
|
810
|
-
var DEFAULT_BASE_URL = "https://api.robotrock.io/v1";
|
|
811
1210
|
function resolveRobotRockConfig(overrides) {
|
|
1211
|
+
const agentService = overrides?.agentService;
|
|
812
1212
|
const apiKey = overrides?.apiKey ?? process.env.ROBOTROCK_API_KEY;
|
|
813
|
-
if (
|
|
1213
|
+
if (agentService && apiKey) {
|
|
814
1214
|
throw new Error(
|
|
815
|
-
"RobotRock
|
|
1215
|
+
"RobotRock client cannot configure both apiKey and agentService."
|
|
816
1216
|
);
|
|
817
1217
|
}
|
|
818
|
-
const baseUrl = overrides?.baseUrl ??
|
|
1218
|
+
const baseUrl = overrides?.baseUrl ?? getRobotRockApiBaseUrl();
|
|
819
1219
|
const app = overrides?.app ?? process.env.ROBOTROCK_APP;
|
|
1220
|
+
if (agentService) {
|
|
1221
|
+
return app ? { agentService, baseUrl, app } : { agentService, baseUrl };
|
|
1222
|
+
}
|
|
1223
|
+
if (!apiKey) {
|
|
1224
|
+
throw new Error(
|
|
1225
|
+
"RobotRock API key is required. Set ROBOTROCK_API_KEY or pass agentService when creating the client."
|
|
1226
|
+
);
|
|
1227
|
+
}
|
|
820
1228
|
return app ? { apiKey, baseUrl, app } : { apiKey, baseUrl };
|
|
821
1229
|
}
|
|
822
1230
|
function resolveRobotRockClient(client, configOverrides) {
|
|
@@ -917,20 +1325,291 @@ function shouldStopAgentForHandledAction(actionId) {
|
|
|
917
1325
|
return isPlatformTerminalAction(actionId);
|
|
918
1326
|
}
|
|
919
1327
|
|
|
1328
|
+
// src/eve/input-request.ts
|
|
1329
|
+
var EVE_INPUT_SUBMIT_ACTION_ID = "submit";
|
|
1330
|
+
var EVE_INPUT_OTHER_CHOICE_ID = "__other__";
|
|
1331
|
+
var EVE_INPUT_OTHER_CHOICE_LABEL = "Type your own answer";
|
|
1332
|
+
var CONFIRMATION_DEFAULT_OPTIONS = [
|
|
1333
|
+
{ id: "approve", label: "Approve", style: "primary" },
|
|
1334
|
+
{ id: "deny", label: "Deny", style: "danger" }
|
|
1335
|
+
];
|
|
1336
|
+
var SELECT_PER_OPTION_THRESHOLD = 6;
|
|
1337
|
+
function asRecord(value) {
|
|
1338
|
+
if (value == null || typeof value !== "object" || Array.isArray(value)) {
|
|
1339
|
+
return null;
|
|
1340
|
+
}
|
|
1341
|
+
return value;
|
|
1342
|
+
}
|
|
1343
|
+
function readStringField(data, key) {
|
|
1344
|
+
const value = data[key];
|
|
1345
|
+
if (typeof value !== "string") {
|
|
1346
|
+
return void 0;
|
|
1347
|
+
}
|
|
1348
|
+
const trimmed = value.trim();
|
|
1349
|
+
return trimmed.length > 0 ? trimmed : void 0;
|
|
1350
|
+
}
|
|
1351
|
+
function resolveEveInputDisplay(request, toolName) {
|
|
1352
|
+
if (request.display) {
|
|
1353
|
+
return request.display;
|
|
1354
|
+
}
|
|
1355
|
+
if (toolName === "ask_question") {
|
|
1356
|
+
return request.options && request.options.length > 0 ? "select" : "text";
|
|
1357
|
+
}
|
|
1358
|
+
if (request.options && request.options.length > 0) {
|
|
1359
|
+
return "select";
|
|
1360
|
+
}
|
|
1361
|
+
return "confirmation";
|
|
1362
|
+
}
|
|
1363
|
+
function confirmationActions(request) {
|
|
1364
|
+
const options = request.options && request.options.length > 0 ? request.options : CONFIRMATION_DEFAULT_OPTIONS;
|
|
1365
|
+
return options.map((option) => ({
|
|
1366
|
+
id: option.id,
|
|
1367
|
+
title: option.label,
|
|
1368
|
+
...option.description ? { description: option.description } : {}
|
|
1369
|
+
}));
|
|
1370
|
+
}
|
|
1371
|
+
function textSubmitAction(prompt) {
|
|
1372
|
+
return {
|
|
1373
|
+
id: EVE_INPUT_SUBMIT_ACTION_ID,
|
|
1374
|
+
title: "Submit",
|
|
1375
|
+
schema: {
|
|
1376
|
+
type: "object",
|
|
1377
|
+
required: ["answer"],
|
|
1378
|
+
properties: {
|
|
1379
|
+
answer: {
|
|
1380
|
+
type: "string",
|
|
1381
|
+
title: "Your answer"
|
|
1382
|
+
}
|
|
1383
|
+
}
|
|
1384
|
+
},
|
|
1385
|
+
ui: {
|
|
1386
|
+
answer: {
|
|
1387
|
+
"ui:title": prompt.trim() || "Your answer",
|
|
1388
|
+
"ui:widget": "textarea"
|
|
1389
|
+
}
|
|
1390
|
+
}
|
|
1391
|
+
};
|
|
1392
|
+
}
|
|
1393
|
+
function selectPerOptionActions(options) {
|
|
1394
|
+
return options.map((option) => ({
|
|
1395
|
+
id: option.id,
|
|
1396
|
+
title: option.label,
|
|
1397
|
+
...option.description ? { description: option.description } : {}
|
|
1398
|
+
}));
|
|
1399
|
+
}
|
|
1400
|
+
function selectFormAction(request) {
|
|
1401
|
+
const options = request.options ?? [];
|
|
1402
|
+
const allowFreeform = request.allowFreeform === true;
|
|
1403
|
+
const enumValues = options.map((option) => option.id);
|
|
1404
|
+
const enumNames = options.map((option) => option.label);
|
|
1405
|
+
if (allowFreeform) {
|
|
1406
|
+
enumValues.push(EVE_INPUT_OTHER_CHOICE_ID);
|
|
1407
|
+
enumNames.push(EVE_INPUT_OTHER_CHOICE_LABEL);
|
|
1408
|
+
}
|
|
1409
|
+
const properties = {
|
|
1410
|
+
choice: {
|
|
1411
|
+
type: "string",
|
|
1412
|
+
title: "Choose one",
|
|
1413
|
+
enum: enumValues
|
|
1414
|
+
}
|
|
1415
|
+
};
|
|
1416
|
+
if (allowFreeform) {
|
|
1417
|
+
properties.other = {
|
|
1418
|
+
type: "string",
|
|
1419
|
+
title: "Your answer"
|
|
1420
|
+
};
|
|
1421
|
+
}
|
|
1422
|
+
const schema = {
|
|
1423
|
+
type: "object",
|
|
1424
|
+
required: ["choice"],
|
|
1425
|
+
properties
|
|
1426
|
+
};
|
|
1427
|
+
if (allowFreeform) {
|
|
1428
|
+
schema.allOf = [
|
|
1429
|
+
{
|
|
1430
|
+
if: {
|
|
1431
|
+
properties: {
|
|
1432
|
+
choice: { const: EVE_INPUT_OTHER_CHOICE_ID }
|
|
1433
|
+
},
|
|
1434
|
+
required: ["choice"]
|
|
1435
|
+
},
|
|
1436
|
+
then: {
|
|
1437
|
+
required: ["other"],
|
|
1438
|
+
properties: {
|
|
1439
|
+
other: {
|
|
1440
|
+
type: "string",
|
|
1441
|
+
minLength: 1
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
}
|
|
1446
|
+
];
|
|
1447
|
+
}
|
|
1448
|
+
const ui = {
|
|
1449
|
+
choice: {
|
|
1450
|
+
"ui:widget": "radio",
|
|
1451
|
+
"ui:enumNames": enumNames
|
|
1452
|
+
}
|
|
1453
|
+
};
|
|
1454
|
+
if (allowFreeform) {
|
|
1455
|
+
ui.other = {
|
|
1456
|
+
"ui:placeholder": "Enter your answer"
|
|
1457
|
+
};
|
|
1458
|
+
}
|
|
1459
|
+
return {
|
|
1460
|
+
id: EVE_INPUT_SUBMIT_ACTION_ID,
|
|
1461
|
+
title: "Submit",
|
|
1462
|
+
schema,
|
|
1463
|
+
ui
|
|
1464
|
+
};
|
|
1465
|
+
}
|
|
1466
|
+
function normalizeEveSelectFormData(data) {
|
|
1467
|
+
const choice = typeof data.choice === "string" ? data.choice.trim() : void 0;
|
|
1468
|
+
if (!choice) {
|
|
1469
|
+
return data;
|
|
1470
|
+
}
|
|
1471
|
+
if (choice === EVE_INPUT_OTHER_CHOICE_ID) {
|
|
1472
|
+
return {
|
|
1473
|
+
choice,
|
|
1474
|
+
...data.other !== void 0 ? { other: data.other } : {}
|
|
1475
|
+
};
|
|
1476
|
+
}
|
|
1477
|
+
return { choice };
|
|
1478
|
+
}
|
|
1479
|
+
function eveInputRequestToActions(request, options) {
|
|
1480
|
+
const display = resolveEveInputDisplay(request, options?.toolName);
|
|
1481
|
+
switch (display) {
|
|
1482
|
+
case "confirmation":
|
|
1483
|
+
return confirmationActions(request);
|
|
1484
|
+
case "text":
|
|
1485
|
+
return [textSubmitAction(request.prompt)];
|
|
1486
|
+
case "select": {
|
|
1487
|
+
const selectOptions = request.options ?? [];
|
|
1488
|
+
if (selectOptions.length === 0) {
|
|
1489
|
+
return [textSubmitAction(request.prompt)];
|
|
1490
|
+
}
|
|
1491
|
+
if (selectOptions.length <= SELECT_PER_OPTION_THRESHOLD && request.allowFreeform !== true) {
|
|
1492
|
+
return selectPerOptionActions(selectOptions);
|
|
1493
|
+
}
|
|
1494
|
+
return [selectFormAction(request)];
|
|
1495
|
+
}
|
|
1496
|
+
}
|
|
1497
|
+
}
|
|
1498
|
+
function eveInputRequestTaskType(request, options) {
|
|
1499
|
+
const display = resolveEveInputDisplay(request, options?.toolName);
|
|
1500
|
+
if (display === "confirmation" && options?.toolName) {
|
|
1501
|
+
return `eve-approval:${options.toolName}`;
|
|
1502
|
+
}
|
|
1503
|
+
return `eve-input:${display}`;
|
|
1504
|
+
}
|
|
1505
|
+
function taskHandledToEveInputResponse(request, actionId, actionData, options) {
|
|
1506
|
+
const base = { requestId: request.requestId };
|
|
1507
|
+
if (isPlatformTerminalAction(actionId)) {
|
|
1508
|
+
const feedback = parsePlatformRejectRequestData(actionData)?.feedback;
|
|
1509
|
+
return {
|
|
1510
|
+
...base,
|
|
1511
|
+
optionId: "deny",
|
|
1512
|
+
...feedback ? { text: feedback } : {}
|
|
1513
|
+
};
|
|
1514
|
+
}
|
|
1515
|
+
const display = resolveEveInputDisplay(request, options?.toolName);
|
|
1516
|
+
const selectOptions = request.options ?? [];
|
|
1517
|
+
const data = asRecord(actionData);
|
|
1518
|
+
if (display === "confirmation") {
|
|
1519
|
+
return { ...base, optionId: actionId };
|
|
1520
|
+
}
|
|
1521
|
+
const matchingOption = selectOptions.find((option) => option.id === actionId);
|
|
1522
|
+
if (matchingOption) {
|
|
1523
|
+
return { ...base, optionId: actionId };
|
|
1524
|
+
}
|
|
1525
|
+
if (actionId === EVE_INPUT_SUBMIT_ACTION_ID && data) {
|
|
1526
|
+
const submissionData = display === "select" && request.allowFreeform === true ? normalizeEveSelectFormData(data) : data;
|
|
1527
|
+
const answer = readStringField(submissionData, "answer");
|
|
1528
|
+
if (answer) {
|
|
1529
|
+
return { ...base, text: answer };
|
|
1530
|
+
}
|
|
1531
|
+
const choice = readStringField(submissionData, "choice");
|
|
1532
|
+
if (choice === EVE_INPUT_OTHER_CHOICE_ID) {
|
|
1533
|
+
const other = readStringField(submissionData, "other");
|
|
1534
|
+
if (other) {
|
|
1535
|
+
return { ...base, text: other };
|
|
1536
|
+
}
|
|
1537
|
+
}
|
|
1538
|
+
if (choice) {
|
|
1539
|
+
if (selectOptions.some((option) => option.id === choice)) {
|
|
1540
|
+
return { ...base, optionId: choice };
|
|
1541
|
+
}
|
|
1542
|
+
return { ...base, text: choice };
|
|
1543
|
+
}
|
|
1544
|
+
}
|
|
1545
|
+
if (data) {
|
|
1546
|
+
const answer = readStringField(data, "answer");
|
|
1547
|
+
if (answer) {
|
|
1548
|
+
return { ...base, text: answer };
|
|
1549
|
+
}
|
|
1550
|
+
}
|
|
1551
|
+
return { ...base, optionId: actionId };
|
|
1552
|
+
}
|
|
1553
|
+
|
|
1554
|
+
// src/eve/tool-display.ts
|
|
1555
|
+
var DEFAULT_TOOL_DISPLAY_LABELS = {
|
|
1556
|
+
refund_charge: "Refund a customer charge",
|
|
1557
|
+
deploy_release: "Deploy a release",
|
|
1558
|
+
get_weather: "Get weather",
|
|
1559
|
+
get_my_access: "Check my access",
|
|
1560
|
+
whoami: "Who am I",
|
|
1561
|
+
create_robotrock_task: "Create a RobotRock task"
|
|
1562
|
+
};
|
|
1563
|
+
var toolDisplayLabelOverrides = {};
|
|
1564
|
+
function setToolDisplayLabelOverrides(overrides) {
|
|
1565
|
+
toolDisplayLabelOverrides = overrides;
|
|
1566
|
+
}
|
|
1567
|
+
function formatToolName(toolName) {
|
|
1568
|
+
const spaced = toolName.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[-_]+/g, " ").trim();
|
|
1569
|
+
if (!spaced) {
|
|
1570
|
+
return "Tool";
|
|
1571
|
+
}
|
|
1572
|
+
return spaced.charAt(0).toUpperCase() + spaced.slice(1);
|
|
1573
|
+
}
|
|
1574
|
+
function getToolDisplayLabel(toolName) {
|
|
1575
|
+
const trimmed = toolName.trim();
|
|
1576
|
+
if (!trimmed) {
|
|
1577
|
+
return "Tool";
|
|
1578
|
+
}
|
|
1579
|
+
return toolDisplayLabelOverrides[trimmed] ?? DEFAULT_TOOL_DISPLAY_LABELS[trimmed] ?? formatToolName(trimmed);
|
|
1580
|
+
}
|
|
1581
|
+
function formatEveApprovalTitle(request, toolName) {
|
|
1582
|
+
const display = resolveEveInputDisplay(request, toolName);
|
|
1583
|
+
if (display !== "confirmation" || !toolName) {
|
|
1584
|
+
return request.prompt;
|
|
1585
|
+
}
|
|
1586
|
+
return `Approve: ${getToolDisplayLabel(toolName)}`;
|
|
1587
|
+
}
|
|
1588
|
+
|
|
1589
|
+
// src/eve/resume-message.ts
|
|
1590
|
+
function buildTaskHandledResumeMessage(payload) {
|
|
1591
|
+
const handledBy = payload.handledBy?.trim() || "someone";
|
|
1592
|
+
const actionTitle = payload.action.title.trim();
|
|
1593
|
+
return `RobotRock task ${payload.taskId} was handled: ${actionTitle} by ${handledBy}. Briefly tell the user what was decided and what happens next.`;
|
|
1594
|
+
}
|
|
1595
|
+
|
|
1596
|
+
// src/eve/index.ts
|
|
1597
|
+
var ROBOTROCK_READY_HOOK_SLUG = "robotrock-ready";
|
|
1598
|
+
|
|
920
1599
|
// src/webhook.ts
|
|
921
1600
|
import { createHmac, timingSafeEqual } from "crypto";
|
|
922
|
-
import { z as
|
|
1601
|
+
import { z as z4 } from "zod";
|
|
923
1602
|
var ROBOTROCK_SIGNATURE_HEADER = "x-robotrock-signature";
|
|
924
|
-
var robotRockWebhookPayloadBodySchema =
|
|
925
|
-
taskId:
|
|
926
|
-
action:
|
|
927
|
-
id:
|
|
928
|
-
title:
|
|
929
|
-
data:
|
|
1603
|
+
var robotRockWebhookPayloadBodySchema = z4.object({
|
|
1604
|
+
taskId: z4.string().min(1),
|
|
1605
|
+
action: z4.object({
|
|
1606
|
+
id: z4.string().min(1),
|
|
1607
|
+
title: z4.string().min(1),
|
|
1608
|
+
data: z4.unknown()
|
|
930
1609
|
}),
|
|
931
|
-
handledBy:
|
|
932
|
-
handledAt:
|
|
933
|
-
handlerType:
|
|
1610
|
+
handledBy: z4.string().min(1).optional(),
|
|
1611
|
+
handledAt: z4.string().min(1),
|
|
1612
|
+
handlerType: z4.string().min(1)
|
|
934
1613
|
});
|
|
935
1614
|
var RobotRockWebhookError = class extends Error {
|
|
936
1615
|
constructor(message, code, details) {
|
|
@@ -1216,6 +1895,10 @@ function finishRobotRockHumanWaitOtel(session, handled, outcome) {
|
|
|
1216
1895
|
export {
|
|
1217
1896
|
DEFAULT_TASK_PRIORITY,
|
|
1218
1897
|
DEFAULT_THREAD_UPDATE_STATUS,
|
|
1898
|
+
DEFAULT_TOOL_DISPLAY_LABELS,
|
|
1899
|
+
EVE_INPUT_OTHER_CHOICE_ID,
|
|
1900
|
+
EVE_INPUT_OTHER_CHOICE_LABEL,
|
|
1901
|
+
EVE_INPUT_SUBMIT_ACTION_ID,
|
|
1219
1902
|
LOWEST_TASK_PRIORITY,
|
|
1220
1903
|
PLATFORM_MARK_DONE_ACTION_ID,
|
|
1221
1904
|
PLATFORM_MARK_DONE_ACTION_TITLE,
|
|
@@ -1223,6 +1906,7 @@ export {
|
|
|
1223
1906
|
PLATFORM_REJECT_REQUEST_ACTION_TITLE,
|
|
1224
1907
|
PLATFORM_TERMINAL_ACTION_IDS,
|
|
1225
1908
|
PlatformRejectRequestError,
|
|
1909
|
+
ROBOTROCK_READY_HOOK_SLUG,
|
|
1226
1910
|
RobotRock,
|
|
1227
1911
|
RobotRockError,
|
|
1228
1912
|
RobotRockWebhookError,
|
|
@@ -1230,29 +1914,41 @@ export {
|
|
|
1230
1914
|
TASK_PRIORITY_RANK,
|
|
1231
1915
|
TaskExpiredError,
|
|
1232
1916
|
TaskTimeoutError,
|
|
1917
|
+
agentChatSeedMessageSchema,
|
|
1233
1918
|
agentTelemetrySchema2 as agentTelemetrySchema,
|
|
1234
1919
|
assertNotPlatformRejectRequest,
|
|
1235
1920
|
assignToSchema2 as assignToSchema,
|
|
1236
1921
|
attachWebhookToActions,
|
|
1237
1922
|
beginRobotRockHumanWaitOtel,
|
|
1923
|
+
buildTaskHandledResumeMessage,
|
|
1238
1924
|
captureRobotRockOtelHandle,
|
|
1925
|
+
createAgentChatBodySchema,
|
|
1926
|
+
createChatsApi,
|
|
1239
1927
|
createClient,
|
|
1240
1928
|
createTaskBodySchema2 as createTaskBodySchema,
|
|
1241
1929
|
endRobotRockHumanWaitSpan,
|
|
1930
|
+
eveInputRequestTaskType,
|
|
1931
|
+
eveInputRequestToActions,
|
|
1242
1932
|
finishRobotRockHumanWaitOtel,
|
|
1933
|
+
formatEveApprovalTitle,
|
|
1934
|
+
getToolDisplayLabel,
|
|
1243
1935
|
isPlatformMarkDoneAction,
|
|
1244
1936
|
isPlatformRejectRequestAction,
|
|
1245
1937
|
isPlatformTerminalAction,
|
|
1938
|
+
normalizeEveSelectFormData,
|
|
1246
1939
|
parseHandledOutcome,
|
|
1247
1940
|
parsePlatformRejectRequestData,
|
|
1248
1941
|
recordRobotRockHandledToOtel,
|
|
1942
|
+
resolveEveInputDisplay,
|
|
1249
1943
|
resolveRobotRockClient,
|
|
1250
1944
|
resolveRobotRockConfig,
|
|
1945
|
+
setToolDisplayLabelOverrides,
|
|
1251
1946
|
shouldRecordRobotRockOtel,
|
|
1252
1947
|
shouldStopAgentForHandledAction,
|
|
1253
1948
|
startRobotRockHumanWaitSpan,
|
|
1254
1949
|
stripPlatformOtelFields,
|
|
1255
1950
|
taskContextSchema2 as taskContextSchema,
|
|
1951
|
+
taskHandledToEveInputResponse,
|
|
1256
1952
|
taskPriorities2 as taskPriorities,
|
|
1257
1953
|
taskPrioritySchema2 as taskPrioritySchema,
|
|
1258
1954
|
threadUpdateBodySchema,
|