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.
Files changed (52) hide show
  1. package/dist/ai/index.d.ts +18 -7
  2. package/dist/ai/index.js +852 -115
  3. package/dist/ai/index.js.map +1 -1
  4. package/dist/ai/trigger.d.ts +4 -3
  5. package/dist/ai/trigger.js +810 -115
  6. package/dist/ai/trigger.js.map +1 -1
  7. package/dist/ai/workflow.d.ts +15 -4
  8. package/dist/ai/workflow.js +729 -115
  9. package/dist/ai/workflow.js.map +1 -1
  10. package/dist/client-XTnFHGFE.d.ts +248 -0
  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 +12 -44
  37. package/dist/index.js +793 -97
  38. package/dist/index.js.map +1 -1
  39. package/dist/schemas/index.d.ts +11 -1
  40. package/dist/schemas/index.js +126 -8
  41. package/dist/schemas/index.js.map +1 -1
  42. package/dist/tenant-5YKDrdC-.d.ts +23 -0
  43. package/dist/{tool-approval-bridge-G765kMJR.d.ts → tool-approval-bridge-C4bTm8vu.d.ts} +93 -2
  44. package/dist/trigger/index.d.ts +2 -1
  45. package/dist/trigger/index.js +495 -87
  46. package/dist/trigger/index.js.map +1 -1
  47. package/dist/{trigger-D0shjqk0.d.ts → trigger-Dn0DFiyU.d.ts} +64 -3
  48. package/dist/workflow/index.d.ts +2 -1
  49. package/dist/workflow/index.js +496 -88
  50. package/dist/workflow/index.js.map +1 -1
  51. package/package.json +44 -7
  52. package/dist/client-Cy7YLxms.d.ts +0 -145
@@ -305,6 +305,25 @@ function isBlockedHostname(hostname) {
305
305
  }
306
306
  return isBlockedIpv4(host) || isBlockedIpv6(host);
307
307
  }
308
+ function isLoopbackHandlerUrl(urlString) {
309
+ let url;
310
+ try {
311
+ url = new URL(urlString);
312
+ } catch {
313
+ return false;
314
+ }
315
+ if (url.protocol !== "http:" && url.protocol !== "https:") {
316
+ return false;
317
+ }
318
+ if (url.username || url.password) {
319
+ return false;
320
+ }
321
+ const host = url.hostname.toLowerCase();
322
+ return host === "localhost" || host === "127.0.0.1" || host === "::1" || host === "[::1]" || host.endsWith(".localhost");
323
+ }
324
+ function isAllowedHandlerUrl(urlString) {
325
+ return isPublicHttpUrl(urlString) || isLoopbackHandlerUrl(urlString);
326
+ }
308
327
  function isPublicHttpUrl(urlString) {
309
328
  let url;
310
329
  try {
@@ -321,6 +340,7 @@ function isPublicHttpUrl(urlString) {
321
340
  return !isBlockedHostname(url.hostname);
322
341
  }
323
342
  var PUBLIC_HTTP_URL_ERROR = "URL must be a public http:// or https:// address";
343
+ var HANDLER_URL_ERROR = "Handler URL must be a public or loopback http:// or https:// address";
324
344
 
325
345
  // ../core/src/schemas/task.ts
326
346
  import { z } from "zod";
@@ -382,6 +402,9 @@ function validateContextPublicUrls(context2) {
382
402
  var safeUrlSchema = z.string().refine((url) => isPublicHttpUrl(url), {
383
403
  message: PUBLIC_HTTP_URL_ERROR
384
404
  });
405
+ var handlerUrlSchema = z.string().refine((url) => isAllowedHandlerUrl(url), {
406
+ message: HANDLER_URL_ERROR
407
+ });
385
408
  var jsonSchema7Schema = z.custom(
386
409
  (val) => typeof val === "object" && val !== null,
387
410
  { message: "Must be a valid JSON Schema object" }
@@ -391,7 +414,7 @@ var uiSchemaSchema = z.custom((val) => typeof val === "object" && val !== null,
391
414
  });
392
415
  var webhookHandlerSchema = z.object({
393
416
  type: z.literal("webhook"),
394
- url: safeUrlSchema,
417
+ url: handlerUrlSchema,
395
418
  headers: z.record(z.string(), z.string())
396
419
  });
397
420
  var triggerHandlerSchema = webhookHandlerSchema.extend({
@@ -399,13 +422,15 @@ var triggerHandlerSchema = webhookHandlerSchema.extend({
399
422
  tokenId: z.string().min(1)
400
423
  });
401
424
  var handlerSchema = z.discriminatedUnion("type", [webhookHandlerSchema, triggerHandlerSchema]);
402
- var taskActionSchema = z.object({
425
+ var taskActionInputSchema = z.object({
403
426
  id: z.string().min(1),
404
427
  title: z.string().min(1),
405
428
  description: z.string().optional(),
406
429
  schema: jsonSchema7Schema.optional(),
407
430
  ui: uiSchemaSchema.optional(),
408
- data: z.record(z.string(), z.unknown()).optional(),
431
+ data: z.record(z.string(), z.unknown()).optional()
432
+ });
433
+ var taskActionSchema = taskActionInputSchema.extend({
409
434
  // Optional handlers for this action - if present, must have at least 1
410
435
  handlers: z.array(handlerSchema).min(1).optional()
411
436
  });
@@ -513,10 +538,100 @@ var createTaskBodySchema = taskContextObjectBaseSchema.extend({
513
538
  /** Agent release version — not shown in inbox UI. */
514
539
  agent: agentTelemetrySchema.optional()
515
540
  }).transform(normalizeTaskContextVersion).superRefine(refineContextPublicUrls);
541
+ var agentChatSeedActionSchema = z.object({
542
+ /** Stable action id echoed back on submit; auto-derived from title when omitted. */
543
+ id: z.string().min(1).optional(),
544
+ title: z.string().min(1),
545
+ description: z.string().optional(),
546
+ /** JSON Schema object for the form fields. */
547
+ schema: z.record(z.string(), z.unknown()).optional(),
548
+ /** RJSF ui schema overrides (ui:widget, ui:enumNames, etc.). */
549
+ ui: z.record(z.string(), z.unknown()).optional(),
550
+ /** Optional default field values. */
551
+ data: z.record(z.string(), z.unknown()).optional()
552
+ });
553
+ var agentChatSeedMessageSchema = z.object({
554
+ role: z.enum(["user", "assistant"]),
555
+ text: z.string().min(1),
556
+ /** Optional display-name override for the message sender. */
557
+ senderName: z.string().min(1).optional(),
558
+ /**
559
+ * Optional structured input request rendered as a styled RobotRock action
560
+ * widget below the message text. Use this instead of asking the user to reply
561
+ * in plain text so the request is always a proper action form.
562
+ */
563
+ requestActionInput: z.object({
564
+ prompt: z.string().optional(),
565
+ action: agentChatSeedActionSchema
566
+ }).optional()
567
+ });
568
+ var eveAgentChatTransportSchema = z.object({
569
+ kind: z.literal("eve"),
570
+ chatId: z.string().min(1),
571
+ baseUrl: z.string().url(),
572
+ sessionId: z.string().optional(),
573
+ continuationToken: z.string().optional(),
574
+ streamIndex: z.number().int().nonnegative().optional(),
575
+ isStreaming: z.boolean().optional()
576
+ });
577
+ var createAgentChatBodySchema = z.object({
578
+ /** Agent id (eve agent name). Required unless `parentChatId` is set. */
579
+ agentIdentifier: z.string().min(1).optional(),
580
+ /** Inherit tenant/connection/agent from an existing chat (agent-spawned chats). */
581
+ parentChatId: z.string().min(1).optional(),
582
+ /** Convex tenantEveConnections id; resolved from the tenant when omitted. */
583
+ eveConnectionId: z.string().min(1).optional(),
584
+ /** Source application id; groups the chat under an inbox section. */
585
+ app: z.string().min(1).optional(),
586
+ assignTo: assignToSchema.optional(),
587
+ title: z.string().min(1),
588
+ messages: z.array(agentChatSeedMessageSchema).default([]),
589
+ /** Provenance label stored on the session ("cron" | "agent" | ...). */
590
+ source: z.string().min(1).optional()
591
+ }).refine((data) => Boolean(data.agentIdentifier || data.parentChatId), {
592
+ message: "Provide either agentIdentifier or parentChatId"
593
+ });
594
+ var agentChatAuditInputBodySchema = z.object({
595
+ eveSessionId: z.string().min(1),
596
+ userId: z.string().min(1),
597
+ actionId: z.string().min(1),
598
+ actionTitle: z.string().optional(),
599
+ prompt: z.string().optional(),
600
+ data: z.record(z.string(), z.unknown()).optional(),
601
+ idempotencyKey: z.string().optional(),
602
+ /** Eve input request id — used to merge staged HITL metadata. */
603
+ requestId: z.string().optional(),
604
+ /** Eve tool call id — fallback lookup for staged HITL metadata. */
605
+ toolCallId: z.string().optional()
606
+ });
607
+ var eveHitlStagedOptionSchema = z.object({
608
+ id: z.string(),
609
+ label: z.string()
610
+ });
611
+ var agentChatStageHitlBodySchema = z.object({
612
+ eveSessionId: z.string().min(1),
613
+ requests: z.array(
614
+ z.object({
615
+ requestId: z.string().min(1),
616
+ toolCallId: z.string().min(1),
617
+ toolName: z.string().min(1),
618
+ prompt: z.string().min(1),
619
+ display: z.enum(["confirmation", "select", "text"]).optional(),
620
+ allowFreeform: z.boolean().optional(),
621
+ options: z.array(eveHitlStagedOptionSchema).optional(),
622
+ toolInput: z.record(z.string(), z.unknown()).optional()
623
+ })
624
+ )
625
+ });
626
+ var agentChatLinkTaskBodySchema = z.object({
627
+ eveSessionId: z.string().min(1),
628
+ publicTaskId: z.string().min(1),
629
+ toolCallId: z.string().min(1)
630
+ });
516
631
 
517
632
  // src/schemas/index.ts
518
- var safeUrlSchema2 = z2.string().refine((url) => isPublicHttpUrl(url), {
519
- message: PUBLIC_HTTP_URL_ERROR
633
+ var handlerUrlSchema2 = z2.string().refine((url) => isAllowedHandlerUrl(url), {
634
+ message: HANDLER_URL_ERROR
520
635
  });
521
636
  var jsonSchema7Schema2 = z2.custom(
522
637
  (val) => typeof val === "object" && val !== null,
@@ -527,7 +642,7 @@ var uiSchemaSchema2 = z2.custom((val) => typeof val === "object" && val !== null
527
642
  });
528
643
  var webhookHandlerSchema2 = z2.object({
529
644
  type: z2.literal("webhook"),
530
- url: safeUrlSchema2,
645
+ url: handlerUrlSchema2,
531
646
  headers: z2.record(z2.string(), z2.string())
532
647
  });
533
648
  var triggerHandlerSchema2 = webhookHandlerSchema2.extend({
@@ -535,13 +650,15 @@ var triggerHandlerSchema2 = webhookHandlerSchema2.extend({
535
650
  tokenId: z2.string().min(1)
536
651
  });
537
652
  var handlerSchema2 = z2.discriminatedUnion("type", [webhookHandlerSchema2, triggerHandlerSchema2]);
538
- var taskActionSchema2 = z2.object({
653
+ var taskActionInputSchema2 = z2.object({
539
654
  id: z2.string().min(1),
540
655
  title: z2.string().min(1),
541
656
  description: z2.string().optional(),
542
657
  schema: jsonSchema7Schema2.optional(),
543
658
  ui: uiSchemaSchema2.optional(),
544
- data: z2.record(z2.string(), z2.unknown()).optional(),
659
+ data: z2.record(z2.string(), z2.unknown()).optional()
660
+ });
661
+ var taskActionSchema2 = taskActionInputSchema2.extend({
545
662
  handlers: z2.array(handlerSchema2).min(1).optional()
546
663
  });
547
664
  var uiFieldSchemaSchema2 = z2.object({
@@ -673,6 +790,293 @@ function toDiscriminatedApprovalResult(actions, task2) {
673
790
  };
674
791
  }
675
792
 
793
+ // src/http.ts
794
+ var RobotRockError = class extends Error {
795
+ constructor(message, statusCode, response) {
796
+ super(message);
797
+ this.statusCode = statusCode;
798
+ this.response = response;
799
+ this.name = "RobotRockError";
800
+ }
801
+ };
802
+ async function parseResponseBody(response) {
803
+ const contentType = response.headers.get("content-type") ?? "";
804
+ const bodyText = await response.text();
805
+ if (!bodyText) {
806
+ return null;
807
+ }
808
+ if (contentType.toLowerCase().includes("application/json")) {
809
+ try {
810
+ return JSON.parse(bodyText);
811
+ } catch {
812
+ }
813
+ }
814
+ try {
815
+ return JSON.parse(bodyText);
816
+ } catch {
817
+ return bodyText;
818
+ }
819
+ }
820
+ function getErrorMessage(data, fallback) {
821
+ if (data && typeof data === "object" && !Array.isArray(data)) {
822
+ const record = data;
823
+ const maybeMessage = record.message;
824
+ const maybeHint = record.hint;
825
+ if (typeof maybeMessage === "string" && maybeMessage.trim()) {
826
+ if (typeof maybeHint === "string" && maybeHint.trim()) {
827
+ return `${maybeMessage.trim()} ${maybeHint.trim()}`;
828
+ }
829
+ return maybeMessage;
830
+ }
831
+ }
832
+ if (typeof data === "string" && data.trim()) {
833
+ const compact = data.replace(/\s+/g, " ").trim();
834
+ const snippet = compact.length > 180 ? `${compact.slice(0, 180)}...` : compact;
835
+ return `${fallback}. Server returned non-JSON response: ${snippet}`;
836
+ }
837
+ return fallback;
838
+ }
839
+
840
+ // ../core/src/handler-retry.ts
841
+ var HANDLER_RETRY_DELAYS_MS = [
842
+ 6e4,
843
+ // 1m
844
+ 3e5,
845
+ // 5m
846
+ 18e5,
847
+ // 30m
848
+ 36e5,
849
+ // 1h
850
+ 216e5,
851
+ // 6h
852
+ 864e5,
853
+ // 24h
854
+ 1728e5
855
+ // 48h
856
+ ];
857
+ var HANDLER_MAX_ATTEMPTS = HANDLER_RETRY_DELAYS_MS.length + 1;
858
+
859
+ // ../core/src/attribution/index.ts
860
+ import { z as z3 } from "zod";
861
+
862
+ // ../core/src/app-url.ts
863
+ var PRODUCTION_APP_URL = "https://app.robotrock.io";
864
+ var PRODUCTION_API_URL = "https://api.robotrock.io/v1";
865
+ var DEV_API_URL = "http://localhost:4001/v1";
866
+ var APP_SIGNUP_URL = `${PRODUCTION_APP_URL}/sign-up`;
867
+ function normalizeRobotRockApiBaseUrl(baseUrl) {
868
+ const trimmed = baseUrl.trim().replace(/\/+$/, "");
869
+ if (trimmed.endsWith("/v1")) {
870
+ return trimmed;
871
+ }
872
+ return `${trimmed}/v1`;
873
+ }
874
+ function getRobotRockApiBaseUrl() {
875
+ const explicit = process.env.ROBOTROCK_BASE_URL?.trim() || process.env.ROBOTROCK_API_URL?.trim();
876
+ if (explicit) {
877
+ return normalizeRobotRockApiBaseUrl(explicit);
878
+ }
879
+ if (process.env.NODE_ENV === "production") {
880
+ return PRODUCTION_API_URL;
881
+ }
882
+ return DEV_API_URL;
883
+ }
884
+
885
+ // ../core/src/attribution/index.ts
886
+ var ATTRIBUTION_COOKIE_MAX_AGE = 60 * 60 * 24 * 90;
887
+ var attributionSchema = z3.object({
888
+ utmSource: z3.string().optional(),
889
+ utmMedium: z3.string().optional(),
890
+ utmCampaign: z3.string().optional(),
891
+ utmContent: z3.string().optional(),
892
+ utmTerm: z3.string().optional(),
893
+ gclid: z3.string().optional(),
894
+ landingUrl: z3.string().optional(),
895
+ referrer: z3.string().optional(),
896
+ capturedAt: z3.number()
897
+ });
898
+
899
+ // src/auth-headers.ts
900
+ function buildRobotRockAuthHeaders(auth) {
901
+ if (auth.kind === "apiKey") {
902
+ return {
903
+ "X-Api-Key": auth.apiKey
904
+ };
905
+ }
906
+ return {
907
+ Authorization: `Bearer ${auth.token}`,
908
+ "X-RobotRock-Tenant-Slug": auth.tenantSlug,
909
+ "X-RobotRock-Connection-Id": auth.connectionId
910
+ };
911
+ }
912
+ function resolveRobotRockAuthConfig(overrides) {
913
+ if (overrides?.agentService) {
914
+ return {
915
+ kind: "agentService",
916
+ ...overrides.agentService
917
+ };
918
+ }
919
+ const apiKey = overrides?.apiKey ?? process.env.ROBOTROCK_API_KEY;
920
+ if (!apiKey) {
921
+ throw new Error(
922
+ "RobotRock auth is required. Set ROBOTROCK_API_KEY, ROBOTROCK_AGENT_SERVICE_TOKEN with tenant context, or pass auth when creating the client."
923
+ );
924
+ }
925
+ return { kind: "apiKey", apiKey };
926
+ }
927
+
928
+ // src/chats.ts
929
+ function createChatsApi(config) {
930
+ const headers = () => ({
931
+ "Content-Type": "application/json",
932
+ ...buildRobotRockAuthHeaders(config.auth)
933
+ });
934
+ return {
935
+ async create(input) {
936
+ const bodyPayload = {
937
+ ...input,
938
+ app: input.app ?? config.app,
939
+ messages: input.messages ?? []
940
+ };
941
+ const validation = createAgentChatBodySchema.safeParse(bodyPayload);
942
+ if (!validation.success) {
943
+ throw new RobotRockError(
944
+ `Invalid chat: ${validation.error.issues[0]?.message}`,
945
+ 400,
946
+ validation.error.issues
947
+ );
948
+ }
949
+ const response = await fetch(`${config.baseUrl}/agent-chats`, {
950
+ method: "POST",
951
+ headers: headers(),
952
+ body: JSON.stringify(validation.data)
953
+ });
954
+ const data = await parseResponseBody(response);
955
+ if (!response.ok) {
956
+ throw new RobotRockError(
957
+ getErrorMessage(data, "Failed to create chat"),
958
+ response.status,
959
+ data
960
+ );
961
+ }
962
+ const result = data;
963
+ return { tenantSlug: result.tenantSlug, chats: result.chats };
964
+ },
965
+ async close(chatId, options) {
966
+ if (!chatId) {
967
+ throw new RobotRockError("chatId is required to close a chat", 400);
968
+ }
969
+ const response = await fetch(
970
+ `${config.baseUrl}/agent-chats/${encodeURIComponent(chatId)}/close`,
971
+ {
972
+ method: "POST",
973
+ headers: headers(),
974
+ body: JSON.stringify({ reason: options?.reason })
975
+ }
976
+ );
977
+ if (!response.ok) {
978
+ const data = await parseResponseBody(response);
979
+ throw new RobotRockError(
980
+ getErrorMessage(data, "Failed to close chat"),
981
+ response.status,
982
+ data
983
+ );
984
+ }
985
+ },
986
+ async stageHitlRequests(input) {
987
+ const validation = agentChatStageHitlBodySchema.safeParse(input);
988
+ if (!validation.success) {
989
+ throw new RobotRockError(
990
+ `Invalid stage HITL input: ${validation.error.issues[0]?.message}`,
991
+ 400,
992
+ validation.error.issues
993
+ );
994
+ }
995
+ const response = await fetch(`${config.baseUrl}/agent-chats/stage-hitl`, {
996
+ method: "POST",
997
+ headers: headers(),
998
+ body: JSON.stringify(validation.data)
999
+ });
1000
+ if (!response.ok) {
1001
+ const data = await parseResponseBody(response);
1002
+ throw new RobotRockError(
1003
+ getErrorMessage(data, "Failed to stage chat HITL requests"),
1004
+ response.status,
1005
+ data
1006
+ );
1007
+ }
1008
+ },
1009
+ async getStagedHitlRequests(eveSessionId) {
1010
+ const trimmed = eveSessionId.trim();
1011
+ if (!trimmed) {
1012
+ throw new RobotRockError("eveSessionId is required", 400);
1013
+ }
1014
+ const url = new URL(`${config.baseUrl}/agent-chats/staged-hitl`);
1015
+ url.searchParams.set("eveSessionId", trimmed);
1016
+ const response = await fetch(url.toString(), {
1017
+ method: "GET",
1018
+ headers: headers()
1019
+ });
1020
+ const data = await parseResponseBody(response);
1021
+ if (!response.ok) {
1022
+ throw new RobotRockError(
1023
+ getErrorMessage(data, "Failed to fetch staged chat HITL requests"),
1024
+ response.status,
1025
+ data
1026
+ );
1027
+ }
1028
+ const requests = data.requests;
1029
+ return Array.isArray(requests) ? requests : [];
1030
+ },
1031
+ async logInputSubmission(input) {
1032
+ const validation = agentChatAuditInputBodySchema.safeParse(input);
1033
+ if (!validation.success) {
1034
+ throw new RobotRockError(
1035
+ `Invalid audit input: ${validation.error.issues[0]?.message}`,
1036
+ 400,
1037
+ validation.error.issues
1038
+ );
1039
+ }
1040
+ const response = await fetch(`${config.baseUrl}/agent-chats/audit-input`, {
1041
+ method: "POST",
1042
+ headers: headers(),
1043
+ body: JSON.stringify(validation.data)
1044
+ });
1045
+ if (!response.ok) {
1046
+ const data = await parseResponseBody(response);
1047
+ throw new RobotRockError(
1048
+ getErrorMessage(data, "Failed to log chat input submission"),
1049
+ response.status,
1050
+ data
1051
+ );
1052
+ }
1053
+ },
1054
+ async linkTask(input) {
1055
+ const validation = agentChatLinkTaskBodySchema.safeParse(input);
1056
+ if (!validation.success) {
1057
+ throw new RobotRockError(
1058
+ `Invalid link task input: ${validation.error.issues[0]?.message}`,
1059
+ 400,
1060
+ validation.error.issues
1061
+ );
1062
+ }
1063
+ const response = await fetch(`${config.baseUrl}/agent-chats/link-task`, {
1064
+ method: "POST",
1065
+ headers: headers(),
1066
+ body: JSON.stringify(validation.data)
1067
+ });
1068
+ if (!response.ok) {
1069
+ const data = await parseResponseBody(response);
1070
+ throw new RobotRockError(
1071
+ getErrorMessage(data, "Failed to link inbox task to chat"),
1072
+ response.status,
1073
+ data
1074
+ );
1075
+ }
1076
+ }
1077
+ };
1078
+ }
1079
+
676
1080
  // src/client.ts
677
1081
  var DEFAULT_POLL_INTERVAL_MS = 2e3;
678
1082
  var DEFAULT_TIMEOUT_MS = 24 * 60 * 60 * 1e3;
@@ -710,22 +1114,18 @@ function serializeValidUntil(value) {
710
1114
  }
711
1115
  throw new RobotRockError("Invalid validUntil: expected a Date or parseable date string", 400);
712
1116
  }
713
- var RobotRockError = class extends Error {
714
- constructor(message, statusCode, response) {
715
- super(message);
716
- this.statusCode = statusCode;
717
- this.response = response;
718
- this.name = "RobotRockError";
719
- }
720
- };
721
1117
  var RobotRock = class {
722
- apiKey;
1118
+ auth;
723
1119
  baseUrl;
724
1120
  app;
725
1121
  agentVersion;
726
1122
  contextVersion;
727
1123
  webhook;
728
1124
  polling;
1125
+ /** Task CRUD: `create`, `get`, `cancel`, `sendUpdate`. */
1126
+ tasks;
1127
+ /** Chat CRUD: `create`, `close`. */
1128
+ chats;
729
1129
  constructor(config) {
730
1130
  if (config.webhook && config.polling) {
731
1131
  throw new Error(
@@ -733,24 +1133,42 @@ var RobotRock = class {
733
1133
  );
734
1134
  }
735
1135
  const apiKey = config.apiKey ?? process.env.ROBOTROCK_API_KEY;
736
- if (!apiKey) {
1136
+ const agentService = config.agentService;
1137
+ if (apiKey && agentService) {
737
1138
  throw new Error(
738
- "RobotRock API key is required. Set ROBOTROCK_API_KEY or pass apiKey when creating the client."
1139
+ "RobotRock client cannot configure both apiKey and agentService."
739
1140
  );
740
1141
  }
741
- this.apiKey = apiKey;
742
- const rawBase = config.baseUrl ?? "https://api.robotrock.io/v1";
743
- this.baseUrl = rawBase.replace(/\/+$/, "");
1142
+ this.auth = resolveRobotRockAuthConfig({
1143
+ ...apiKey ? { apiKey } : {},
1144
+ ...agentService ? { agentService } : {}
1145
+ });
1146
+ this.baseUrl = config.baseUrl ?? getRobotRockApiBaseUrl();
744
1147
  this.app = config.app;
745
1148
  this.agentVersion = config.version ?? resolveAgentVersionFromEnv();
746
1149
  this.contextVersion = config.advanced?.contextVersion ?? TASK_CONTEXT_FORMAT_VERSION2;
747
1150
  this.webhook = config.webhook;
748
1151
  this.polling = config.polling ?? {};
1152
+ this.tasks = {
1153
+ create: (task2) => this.createTaskRequest(task2),
1154
+ get: (taskId) => this.getTaskById(taskId),
1155
+ cancel: (taskId) => this.cancelTaskRequest(taskId),
1156
+ sendUpdate: (input) => this.sendThreadUpdate(input)
1157
+ };
1158
+ this.chats = createChatsApi({
1159
+ baseUrl: this.baseUrl,
1160
+ auth: this.auth,
1161
+ app: this.app
1162
+ });
749
1163
  }
750
- /**
751
- * Create a task via POST /v1 without waiting for a human response.
752
- */
753
- async createTask(task2) {
1164
+ authHeaders(extra) {
1165
+ return {
1166
+ "Content-Type": "application/json",
1167
+ ...buildRobotRockAuthHeaders(this.auth),
1168
+ ...extra
1169
+ };
1170
+ }
1171
+ async createTaskRequest(task2) {
754
1172
  const normalizedTask = normalizeSendToHumanInput(task2, {
755
1173
  webhook: this.webhook,
756
1174
  app: this.app,
@@ -774,13 +1192,9 @@ var RobotRock = class {
774
1192
  validation.error.issues
775
1193
  );
776
1194
  }
777
- const headers = {
778
- "Content-Type": "application/json",
779
- "X-Api-Key": this.apiKey
780
- };
781
- if (task2.idempotencyKey) {
782
- headers["Idempotency-Key"] = task2.idempotencyKey;
783
- }
1195
+ const headers = this.authHeaders(
1196
+ task2.idempotencyKey ? { "Idempotency-Key": task2.idempotencyKey } : void 0
1197
+ );
784
1198
  const response = await fetch(`${this.baseUrl}/`, {
785
1199
  method: "POST",
786
1200
  headers,
@@ -803,7 +1217,7 @@ var RobotRock = class {
803
1217
  contextVersion: this.contextVersion,
804
1218
  agentVersion: this.agentVersion
805
1219
  });
806
- const createdTaskTask = await this.createTask(task2);
1220
+ const createdTaskTask = await this.createTaskRequest(task2);
807
1221
  const hasHandlers = normalizedTask.actions.some(
808
1222
  (action) => Array.isArray(action.handlers) && action.handlers.length > 0
809
1223
  );
@@ -820,7 +1234,7 @@ var RobotRock = class {
820
1234
  const deadline = validUntilMs !== void 0 ? Math.min(pollingDeadline, validUntilMs) : pollingDeadline;
821
1235
  const taskId = createdTaskTask.taskId;
822
1236
  while (Date.now() < deadline) {
823
- const existing = await this.getTask(taskId);
1237
+ const existing = await this.getTaskById(taskId);
824
1238
  if (existing?.status === "handled" && existing.handled) {
825
1239
  return {
826
1240
  mode: "handled",
@@ -842,15 +1256,38 @@ var RobotRock = class {
842
1256
  }
843
1257
  throw new TaskTimeoutError(`No human response within ${timeoutMs}ms`);
844
1258
  }
1259
+ /**
1260
+ * Create a task via POST /v1 without waiting for a human response.
1261
+ * @deprecated Use `client.tasks.create()` instead.
1262
+ */
1263
+ async createTask(task2) {
1264
+ return this.tasks.create(task2);
1265
+ }
845
1266
  /**
846
1267
  * Get a task by public task id (returned as `task.taskId` from {@link sendToHuman}).
1268
+ * @deprecated Use `client.tasks.get()` instead.
847
1269
  */
848
1270
  async getTask(taskId) {
1271
+ return this.tasks.get(taskId);
1272
+ }
1273
+ /**
1274
+ * Log a status update against a thread.
1275
+ * @deprecated Use `client.tasks.sendUpdate()` instead.
1276
+ */
1277
+ async sendUpdate(input) {
1278
+ return this.tasks.sendUpdate(input);
1279
+ }
1280
+ /**
1281
+ * Cancel a task by public task id.
1282
+ * @deprecated Use `client.tasks.cancel()` instead.
1283
+ */
1284
+ async cancelTask(taskId) {
1285
+ return this.tasks.cancel(taskId);
1286
+ }
1287
+ async getTaskById(taskId) {
849
1288
  const response = await fetch(`${this.baseUrl}/tasks/${taskId}`, {
850
1289
  method: "GET",
851
- headers: {
852
- "X-Api-Key": this.apiKey
853
- }
1290
+ headers: this.authHeaders()
854
1291
  });
855
1292
  if (response.status === 404) {
856
1293
  return null;
@@ -865,11 +1302,11 @@ var RobotRock = class {
865
1302
  }
866
1303
  return data;
867
1304
  }
868
- /**
869
- * Log a status update against a thread. The update shows in the inbox status
870
- * bar and thread update log for every task in the thread.
871
- */
872
- async sendUpdate({ threadId, message, status }) {
1305
+ async sendThreadUpdate({
1306
+ threadId,
1307
+ message,
1308
+ status
1309
+ }) {
873
1310
  if (!threadId) {
874
1311
  throw new RobotRockError("threadId is required to send an update", 400);
875
1312
  }
@@ -885,10 +1322,7 @@ var RobotRock = class {
885
1322
  `${this.baseUrl}/threads/${encodeURIComponent(threadId)}/updates`,
886
1323
  {
887
1324
  method: "POST",
888
- headers: {
889
- "Content-Type": "application/json",
890
- "X-Api-Key": this.apiKey
891
- },
1325
+ headers: this.authHeaders(),
892
1326
  body: JSON.stringify(validation.data)
893
1327
  }
894
1328
  );
@@ -902,12 +1336,10 @@ var RobotRock = class {
902
1336
  }
903
1337
  return data.update;
904
1338
  }
905
- async cancelTask(taskId) {
1339
+ async cancelTaskRequest(taskId) {
906
1340
  const response = await fetch(`${this.baseUrl}/tasks/${taskId}/cancel`, {
907
1341
  method: "POST",
908
- headers: {
909
- "X-Api-Key": this.apiKey
910
- }
1342
+ headers: this.authHeaders()
911
1343
  });
912
1344
  if (!response.ok) {
913
1345
  const data = await parseResponseBody(response);
@@ -961,50 +1393,26 @@ function normalizeSendToHumanInput(task2, clientDefaults) {
961
1393
  actions: normalizedActions
962
1394
  };
963
1395
  }
964
- async function parseResponseBody(response) {
965
- const contentType = response.headers.get("content-type") ?? "";
966
- const bodyText = await response.text();
967
- if (!bodyText) {
968
- return null;
969
- }
970
- if (contentType.toLowerCase().includes("application/json")) {
971
- try {
972
- return JSON.parse(bodyText);
973
- } catch {
974
- }
975
- }
976
- try {
977
- return JSON.parse(bodyText);
978
- } catch {
979
- return bodyText;
980
- }
981
- }
982
- function getErrorMessage(data, fallback) {
983
- if (data && typeof data === "object" && !Array.isArray(data)) {
984
- const maybeMessage = data.message;
985
- if (typeof maybeMessage === "string" && maybeMessage.trim()) {
986
- return maybeMessage;
987
- }
988
- }
989
- if (typeof data === "string" && data.trim()) {
990
- const compact = data.replace(/\s+/g, " ").trim();
991
- const snippet = compact.length > 180 ? `${compact.slice(0, 180)}...` : compact;
992
- return `${fallback}. Server returned non-JSON response: ${snippet}`;
993
- }
994
- return fallback;
995
- }
996
1396
 
997
1397
  // src/env.ts
998
- var DEFAULT_BASE_URL = "https://api.robotrock.io/v1";
999
1398
  function resolveRobotRockConfig(overrides) {
1399
+ const agentService = overrides?.agentService;
1000
1400
  const apiKey = overrides?.apiKey ?? process.env.ROBOTROCK_API_KEY;
1001
- if (!apiKey) {
1401
+ if (agentService && apiKey) {
1002
1402
  throw new Error(
1003
- "RobotRock API key is required. Set ROBOTROCK_API_KEY or pass apiKey when creating the client."
1403
+ "RobotRock client cannot configure both apiKey and agentService."
1004
1404
  );
1005
1405
  }
1006
- const baseUrl = overrides?.baseUrl ?? process.env.ROBOTROCK_BASE_URL ?? process.env.ROBOTROCK_API_URL ?? DEFAULT_BASE_URL;
1406
+ const baseUrl = overrides?.baseUrl ?? getRobotRockApiBaseUrl();
1007
1407
  const app = overrides?.app ?? process.env.ROBOTROCK_APP;
1408
+ if (agentService) {
1409
+ return app ? { agentService, baseUrl, app } : { agentService, baseUrl };
1410
+ }
1411
+ if (!apiKey) {
1412
+ throw new Error(
1413
+ "RobotRock API key is required. Set ROBOTROCK_API_KEY or pass agentService when creating the client."
1414
+ );
1415
+ }
1008
1416
  return app ? { apiKey, baseUrl, app } : { apiKey, baseUrl };
1009
1417
  }
1010
1418