robotrock 1.0.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.
Files changed (50) hide show
  1. package/dist/ai/index.d.ts +5 -5
  2. package/dist/ai/index.js +285 -47
  3. package/dist/ai/index.js.map +1 -1
  4. package/dist/ai/trigger.d.ts +3 -3
  5. package/dist/ai/trigger.js +285 -47
  6. package/dist/ai/trigger.js.map +1 -1
  7. package/dist/ai/workflow.d.ts +3 -3
  8. package/dist/ai/workflow.js +285 -47
  9. package/dist/ai/workflow.js.map +1 -1
  10. package/dist/{client-CzVmjXpz.d.ts → client-XTnFHGFE.d.ts} +34 -5
  11. package/dist/eve/agent/index.d.ts +188 -0
  12. package/dist/eve/agent/index.js +2322 -0
  13. package/dist/eve/agent/index.js.map +1 -0
  14. package/dist/eve/index.d.ts +5 -0
  15. package/dist/eve/index.js +446 -0
  16. package/dist/eve/index.js.map +1 -0
  17. package/dist/eve/tools/identity/index.d.ts +6 -0
  18. package/dist/eve/tools/identity/index.js +144 -0
  19. package/dist/eve/tools/identity/index.js.map +1 -0
  20. package/dist/eve/tools/identity/my-access.d.ts +58 -0
  21. package/dist/eve/tools/identity/my-access.js +106 -0
  22. package/dist/eve/tools/identity/my-access.js.map +1 -0
  23. package/dist/eve/tools/identity/whoami.d.ts +45 -0
  24. package/dist/eve/tools/identity/whoami.js +101 -0
  25. package/dist/eve/tools/identity/whoami.js.map +1 -0
  26. package/dist/eve/tools/inbox/create-task.d.ts +113 -0
  27. package/dist/eve/tools/inbox/create-task.js +1557 -0
  28. package/dist/eve/tools/inbox/create-task.js.map +1 -0
  29. package/dist/eve/tools/inbox/index.d.ts +5 -0
  30. package/dist/eve/tools/inbox/index.js +1557 -0
  31. package/dist/eve/tools/inbox/index.js.map +1 -0
  32. package/dist/eve/tools/index.d.ts +17 -0
  33. package/dist/eve/tools/index.js +1654 -0
  34. package/dist/eve/tools/index.js.map +1 -0
  35. package/dist/index-BL9qKHA8.d.ts +141 -0
  36. package/dist/index.d.ts +5 -44
  37. package/dist/index.js +541 -42
  38. package/dist/index.js.map +1 -1
  39. package/dist/schemas/index.js +75 -12
  40. package/dist/schemas/index.js.map +1 -1
  41. package/dist/tenant-5YKDrdC-.d.ts +23 -0
  42. package/dist/{tool-approval-bridge-DbwUEBHv.d.ts → tool-approval-bridge-C4bTm8vu.d.ts} +1 -1
  43. package/dist/trigger/index.d.ts +1 -1
  44. package/dist/trigger/index.js +256 -42
  45. package/dist/trigger/index.js.map +1 -1
  46. package/dist/{trigger-BCKBbAV7.d.ts → trigger-Dn0DFiyU.d.ts} +2 -2
  47. package/dist/workflow/index.d.ts +1 -1
  48. package/dist/workflow/index.js +256 -42
  49. package/dist/workflow/index.js.map +1 -1
  50. package/package.json +44 -7
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: safeUrlSchema,
220
+ url: handlerUrlSchema,
198
221
  headers: z.record(z.string(), z.string())
199
222
  });
200
223
  var triggerHandlerSchema = webhookHandlerSchema.extend({
@@ -345,13 +368,22 @@ var agentChatSeedMessageSchema = z.object({
345
368
  action: agentChatSeedActionSchema
346
369
  }).optional()
347
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
+ });
348
380
  var createAgentChatBodySchema = z.object({
349
- /** Trigger chat agent id (task slug). Required unless `parentChatId` is set. */
381
+ /** Agent id (eve agent name). Required unless `parentChatId` is set. */
350
382
  agentIdentifier: z.string().min(1).optional(),
351
383
  /** Inherit tenant/connection/agent from an existing chat (agent-spawned chats). */
352
384
  parentChatId: z.string().min(1).optional(),
353
- /** Convex tenantTriggerConnections id; resolved from the tenant when omitted. */
354
- connectionId: z.string().min(1).optional(),
385
+ /** Convex tenantEveConnections id; resolved from the tenant when omitted. */
386
+ eveConnectionId: z.string().min(1).optional(),
355
387
  /** Source application id; groups the chat under an inbox section. */
356
388
  app: z.string().min(1).optional(),
357
389
  assignTo: assignToSchema.optional(),
@@ -362,16 +394,47 @@ var createAgentChatBodySchema = z.object({
362
394
  }).refine((data) => Boolean(data.agentIdentifier || data.parentChatId), {
363
395
  message: "Provide either agentIdentifier or parentChatId"
364
396
  });
365
- var agentChatTransportBodySchema = z.object({
366
- chatId: z.string().min(1),
367
- publicAccessToken: z.string().min(1),
368
- lastEventId: z.string().optional(),
369
- isStreaming: z.boolean().optional()
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)
370
433
  });
371
434
 
372
435
  // src/schemas/index.ts
373
- var safeUrlSchema2 = z2.string().refine((url) => isPublicHttpUrl(url), {
374
- message: PUBLIC_HTTP_URL_ERROR
436
+ var handlerUrlSchema2 = z2.string().refine((url) => isAllowedHandlerUrl(url), {
437
+ message: HANDLER_URL_ERROR
375
438
  });
376
439
  var jsonSchema7Schema2 = z2.custom(
377
440
  (val) => typeof val === "object" && val !== null,
@@ -382,7 +445,7 @@ var uiSchemaSchema2 = z2.custom((val) => typeof val === "object" && val !== null
382
445
  });
383
446
  var webhookHandlerSchema2 = z2.object({
384
447
  type: z2.literal("webhook"),
385
- url: safeUrlSchema2,
448
+ url: handlerUrlSchema2,
386
449
  headers: z2.record(z2.string(), z2.string())
387
450
  });
388
451
  var triggerHandlerSchema2 = webhookHandlerSchema2.extend({
@@ -568,8 +631,13 @@ async function parseResponseBody(response) {
568
631
  }
569
632
  function getErrorMessage(data, fallback) {
570
633
  if (data && typeof data === "object" && !Array.isArray(data)) {
571
- const maybeMessage = data.message;
634
+ const record = data;
635
+ const maybeMessage = record.message;
636
+ const maybeHint = record.hint;
572
637
  if (typeof maybeMessage === "string" && maybeMessage.trim()) {
638
+ if (typeof maybeHint === "string" && maybeHint.trim()) {
639
+ return `${maybeMessage.trim()} ${maybeHint.trim()}`;
640
+ }
573
641
  return maybeMessage;
574
642
  }
575
643
  }
@@ -605,7 +673,26 @@ import { z as z3 } from "zod";
605
673
 
606
674
  // ../core/src/app-url.ts
607
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";
608
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
+ }
609
696
 
610
697
  // ../core/src/attribution/index.ts
611
698
  var ATTRIBUTION_COOKIE_MAX_AGE = 60 * 60 * 24 * 90;
@@ -621,11 +708,40 @@ var attributionSchema = z3.object({
621
708
  capturedAt: z3.number()
622
709
  });
623
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
+
624
740
  // src/chats.ts
625
741
  function createChatsApi(config) {
626
742
  const headers = () => ({
627
743
  "Content-Type": "application/json",
628
- "X-Api-Key": config.apiKey
744
+ ...buildRobotRockAuthHeaders(config.auth)
629
745
  });
630
746
  return {
631
747
  async create(input) {
@@ -678,6 +794,97 @@ function createChatsApi(config) {
678
794
  data
679
795
  );
680
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
+ }
681
888
  }
682
889
  };
683
890
  }
@@ -720,7 +927,7 @@ function serializeValidUntil(value) {
720
927
  throw new RobotRockError("Invalid validUntil: expected a Date or parseable date string", 400);
721
928
  }
722
929
  var RobotRock = class {
723
- apiKey;
930
+ auth;
724
931
  baseUrl;
725
932
  app;
726
933
  agentVersion;
@@ -738,14 +945,17 @@ var RobotRock = class {
738
945
  );
739
946
  }
740
947
  const apiKey = config.apiKey ?? process.env.ROBOTROCK_API_KEY;
741
- if (!apiKey) {
948
+ const agentService = config.agentService;
949
+ if (apiKey && agentService) {
742
950
  throw new Error(
743
- "RobotRock API key is required. Set ROBOTROCK_API_KEY or pass apiKey when creating the client."
951
+ "RobotRock client cannot configure both apiKey and agentService."
744
952
  );
745
953
  }
746
- this.apiKey = apiKey;
747
- const rawBase = config.baseUrl ?? "https://api.robotrock.io/v1";
748
- this.baseUrl = rawBase.replace(/\/+$/, "");
954
+ this.auth = resolveRobotRockAuthConfig({
955
+ ...apiKey ? { apiKey } : {},
956
+ ...agentService ? { agentService } : {}
957
+ });
958
+ this.baseUrl = config.baseUrl ?? getRobotRockApiBaseUrl();
749
959
  this.app = config.app;
750
960
  this.agentVersion = config.version ?? resolveAgentVersionFromEnv();
751
961
  this.contextVersion = config.advanced?.contextVersion ?? TASK_CONTEXT_FORMAT_VERSION2;
@@ -759,10 +969,17 @@ var RobotRock = class {
759
969
  };
760
970
  this.chats = createChatsApi({
761
971
  baseUrl: this.baseUrl,
762
- apiKey: this.apiKey,
972
+ auth: this.auth,
763
973
  app: this.app
764
974
  });
765
975
  }
976
+ authHeaders(extra) {
977
+ return {
978
+ "Content-Type": "application/json",
979
+ ...buildRobotRockAuthHeaders(this.auth),
980
+ ...extra
981
+ };
982
+ }
766
983
  async createTaskRequest(task) {
767
984
  const normalizedTask = normalizeSendToHumanInput(task, {
768
985
  webhook: this.webhook,
@@ -787,13 +1004,9 @@ var RobotRock = class {
787
1004
  validation.error.issues
788
1005
  );
789
1006
  }
790
- const headers = {
791
- "Content-Type": "application/json",
792
- "X-Api-Key": this.apiKey
793
- };
794
- if (task.idempotencyKey) {
795
- headers["Idempotency-Key"] = task.idempotencyKey;
796
- }
1007
+ const headers = this.authHeaders(
1008
+ task.idempotencyKey ? { "Idempotency-Key": task.idempotencyKey } : void 0
1009
+ );
797
1010
  const response = await fetch(`${this.baseUrl}/`, {
798
1011
  method: "POST",
799
1012
  headers,
@@ -886,9 +1099,7 @@ var RobotRock = class {
886
1099
  async getTaskById(taskId) {
887
1100
  const response = await fetch(`${this.baseUrl}/tasks/${taskId}`, {
888
1101
  method: "GET",
889
- headers: {
890
- "X-Api-Key": this.apiKey
891
- }
1102
+ headers: this.authHeaders()
892
1103
  });
893
1104
  if (response.status === 404) {
894
1105
  return null;
@@ -923,10 +1134,7 @@ var RobotRock = class {
923
1134
  `${this.baseUrl}/threads/${encodeURIComponent(threadId)}/updates`,
924
1135
  {
925
1136
  method: "POST",
926
- headers: {
927
- "Content-Type": "application/json",
928
- "X-Api-Key": this.apiKey
929
- },
1137
+ headers: this.authHeaders(),
930
1138
  body: JSON.stringify(validation.data)
931
1139
  }
932
1140
  );
@@ -943,9 +1151,7 @@ var RobotRock = class {
943
1151
  async cancelTaskRequest(taskId) {
944
1152
  const response = await fetch(`${this.baseUrl}/tasks/${taskId}/cancel`, {
945
1153
  method: "POST",
946
- headers: {
947
- "X-Api-Key": this.apiKey
948
- }
1154
+ headers: this.authHeaders()
949
1155
  });
950
1156
  if (!response.ok) {
951
1157
  const data = await parseResponseBody(response);
@@ -1001,16 +1207,24 @@ function normalizeSendToHumanInput(task, clientDefaults) {
1001
1207
  }
1002
1208
 
1003
1209
  // src/env.ts
1004
- var DEFAULT_BASE_URL = "https://api.robotrock.io/v1";
1005
1210
  function resolveRobotRockConfig(overrides) {
1211
+ const agentService = overrides?.agentService;
1006
1212
  const apiKey = overrides?.apiKey ?? process.env.ROBOTROCK_API_KEY;
1007
- if (!apiKey) {
1213
+ if (agentService && apiKey) {
1008
1214
  throw new Error(
1009
- "RobotRock API key is required. Set ROBOTROCK_API_KEY or pass apiKey when creating the client."
1215
+ "RobotRock client cannot configure both apiKey and agentService."
1010
1216
  );
1011
1217
  }
1012
- const baseUrl = overrides?.baseUrl ?? process.env.ROBOTROCK_BASE_URL ?? process.env.ROBOTROCK_API_URL ?? DEFAULT_BASE_URL;
1218
+ const baseUrl = overrides?.baseUrl ?? getRobotRockApiBaseUrl();
1013
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
+ }
1014
1228
  return app ? { apiKey, baseUrl, app } : { apiKey, baseUrl };
1015
1229
  }
1016
1230
  function resolveRobotRockClient(client, configOverrides) {
@@ -1111,6 +1325,277 @@ function shouldStopAgentForHandledAction(actionId) {
1111
1325
  return isPlatformTerminalAction(actionId);
1112
1326
  }
1113
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
+
1114
1599
  // src/webhook.ts
1115
1600
  import { createHmac, timingSafeEqual } from "crypto";
1116
1601
  import { z as z4 } from "zod";
@@ -1410,6 +1895,10 @@ function finishRobotRockHumanWaitOtel(session, handled, outcome) {
1410
1895
  export {
1411
1896
  DEFAULT_TASK_PRIORITY,
1412
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,
1413
1902
  LOWEST_TASK_PRIORITY,
1414
1903
  PLATFORM_MARK_DONE_ACTION_ID,
1415
1904
  PLATFORM_MARK_DONE_ACTION_TITLE,
@@ -1417,6 +1906,7 @@ export {
1417
1906
  PLATFORM_REJECT_REQUEST_ACTION_TITLE,
1418
1907
  PLATFORM_TERMINAL_ACTION_IDS,
1419
1908
  PlatformRejectRequestError,
1909
+ ROBOTROCK_READY_HOOK_SLUG,
1420
1910
  RobotRock,
1421
1911
  RobotRockError,
1422
1912
  RobotRockWebhookError,
@@ -1430,26 +1920,35 @@ export {
1430
1920
  assignToSchema2 as assignToSchema,
1431
1921
  attachWebhookToActions,
1432
1922
  beginRobotRockHumanWaitOtel,
1923
+ buildTaskHandledResumeMessage,
1433
1924
  captureRobotRockOtelHandle,
1434
1925
  createAgentChatBodySchema,
1435
1926
  createChatsApi,
1436
1927
  createClient,
1437
1928
  createTaskBodySchema2 as createTaskBodySchema,
1438
1929
  endRobotRockHumanWaitSpan,
1930
+ eveInputRequestTaskType,
1931
+ eveInputRequestToActions,
1439
1932
  finishRobotRockHumanWaitOtel,
1933
+ formatEveApprovalTitle,
1934
+ getToolDisplayLabel,
1440
1935
  isPlatformMarkDoneAction,
1441
1936
  isPlatformRejectRequestAction,
1442
1937
  isPlatformTerminalAction,
1938
+ normalizeEveSelectFormData,
1443
1939
  parseHandledOutcome,
1444
1940
  parsePlatformRejectRequestData,
1445
1941
  recordRobotRockHandledToOtel,
1942
+ resolveEveInputDisplay,
1446
1943
  resolveRobotRockClient,
1447
1944
  resolveRobotRockConfig,
1945
+ setToolDisplayLabelOverrides,
1448
1946
  shouldRecordRobotRockOtel,
1449
1947
  shouldStopAgentForHandledAction,
1450
1948
  startRobotRockHumanWaitSpan,
1451
1949
  stripPlatformOtelFields,
1452
1950
  taskContextSchema2 as taskContextSchema,
1951
+ taskHandledToEveInputResponse,
1453
1952
  taskPriorities2 as taskPriorities,
1454
1953
  taskPrioritySchema2 as taskPrioritySchema,
1455
1954
  threadUpdateBodySchema,