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
@@ -157,6 +157,25 @@ function isBlockedHostname(hostname) {
157
157
  }
158
158
  return isBlockedIpv4(host) || isBlockedIpv6(host);
159
159
  }
160
+ function isLoopbackHandlerUrl(urlString) {
161
+ let url;
162
+ try {
163
+ url = new URL(urlString);
164
+ } catch {
165
+ return false;
166
+ }
167
+ if (url.protocol !== "http:" && url.protocol !== "https:") {
168
+ return false;
169
+ }
170
+ if (url.username || url.password) {
171
+ return false;
172
+ }
173
+ const host = url.hostname.toLowerCase();
174
+ return host === "localhost" || host === "127.0.0.1" || host === "::1" || host === "[::1]" || host.endsWith(".localhost");
175
+ }
176
+ function isAllowedHandlerUrl(urlString) {
177
+ return isPublicHttpUrl(urlString) || isLoopbackHandlerUrl(urlString);
178
+ }
160
179
  function isPublicHttpUrl(urlString) {
161
180
  let url;
162
181
  try {
@@ -173,6 +192,7 @@ function isPublicHttpUrl(urlString) {
173
192
  return !isBlockedHostname(url.hostname);
174
193
  }
175
194
  var PUBLIC_HTTP_URL_ERROR = "URL must be a public http:// or https:// address";
195
+ var HANDLER_URL_ERROR = "Handler URL must be a public or loopback http:// or https:// address";
176
196
 
177
197
  // ../core/src/schemas/task.ts
178
198
  import { z } from "zod";
@@ -234,6 +254,9 @@ function validateContextPublicUrls(context2) {
234
254
  var safeUrlSchema = z.string().refine((url) => isPublicHttpUrl(url), {
235
255
  message: PUBLIC_HTTP_URL_ERROR
236
256
  });
257
+ var handlerUrlSchema = z.string().refine((url) => isAllowedHandlerUrl(url), {
258
+ message: HANDLER_URL_ERROR
259
+ });
237
260
  var jsonSchema7Schema = z.custom(
238
261
  (val) => typeof val === "object" && val !== null,
239
262
  { message: "Must be a valid JSON Schema object" }
@@ -243,7 +266,7 @@ var uiSchemaSchema = z.custom((val) => typeof val === "object" && val !== null,
243
266
  });
244
267
  var webhookHandlerSchema = z.object({
245
268
  type: z.literal("webhook"),
246
- url: safeUrlSchema,
269
+ url: handlerUrlSchema,
247
270
  headers: z.record(z.string(), z.string())
248
271
  });
249
272
  var triggerHandlerSchema = webhookHandlerSchema.extend({
@@ -251,13 +274,15 @@ var triggerHandlerSchema = webhookHandlerSchema.extend({
251
274
  tokenId: z.string().min(1)
252
275
  });
253
276
  var handlerSchema = z.discriminatedUnion("type", [webhookHandlerSchema, triggerHandlerSchema]);
254
- var taskActionSchema = z.object({
277
+ var taskActionInputSchema = z.object({
255
278
  id: z.string().min(1),
256
279
  title: z.string().min(1),
257
280
  description: z.string().optional(),
258
281
  schema: jsonSchema7Schema.optional(),
259
282
  ui: uiSchemaSchema.optional(),
260
- data: z.record(z.string(), z.unknown()).optional(),
283
+ data: z.record(z.string(), z.unknown()).optional()
284
+ });
285
+ var taskActionSchema = taskActionInputSchema.extend({
261
286
  // Optional handlers for this action - if present, must have at least 1
262
287
  handlers: z.array(handlerSchema).min(1).optional()
263
288
  });
@@ -365,10 +390,100 @@ var createTaskBodySchema = taskContextObjectBaseSchema.extend({
365
390
  /** Agent release version — not shown in inbox UI. */
366
391
  agent: agentTelemetrySchema.optional()
367
392
  }).transform(normalizeTaskContextVersion).superRefine(refineContextPublicUrls);
393
+ var agentChatSeedActionSchema = z.object({
394
+ /** Stable action id echoed back on submit; auto-derived from title when omitted. */
395
+ id: z.string().min(1).optional(),
396
+ title: z.string().min(1),
397
+ description: z.string().optional(),
398
+ /** JSON Schema object for the form fields. */
399
+ schema: z.record(z.string(), z.unknown()).optional(),
400
+ /** RJSF ui schema overrides (ui:widget, ui:enumNames, etc.). */
401
+ ui: z.record(z.string(), z.unknown()).optional(),
402
+ /** Optional default field values. */
403
+ data: z.record(z.string(), z.unknown()).optional()
404
+ });
405
+ var agentChatSeedMessageSchema = z.object({
406
+ role: z.enum(["user", "assistant"]),
407
+ text: z.string().min(1),
408
+ /** Optional display-name override for the message sender. */
409
+ senderName: z.string().min(1).optional(),
410
+ /**
411
+ * Optional structured input request rendered as a styled RobotRock action
412
+ * widget below the message text. Use this instead of asking the user to reply
413
+ * in plain text so the request is always a proper action form.
414
+ */
415
+ requestActionInput: z.object({
416
+ prompt: z.string().optional(),
417
+ action: agentChatSeedActionSchema
418
+ }).optional()
419
+ });
420
+ var eveAgentChatTransportSchema = z.object({
421
+ kind: z.literal("eve"),
422
+ chatId: z.string().min(1),
423
+ baseUrl: z.string().url(),
424
+ sessionId: z.string().optional(),
425
+ continuationToken: z.string().optional(),
426
+ streamIndex: z.number().int().nonnegative().optional(),
427
+ isStreaming: z.boolean().optional()
428
+ });
429
+ var createAgentChatBodySchema = z.object({
430
+ /** Agent id (eve agent name). Required unless `parentChatId` is set. */
431
+ agentIdentifier: z.string().min(1).optional(),
432
+ /** Inherit tenant/connection/agent from an existing chat (agent-spawned chats). */
433
+ parentChatId: z.string().min(1).optional(),
434
+ /** Convex tenantEveConnections id; resolved from the tenant when omitted. */
435
+ eveConnectionId: z.string().min(1).optional(),
436
+ /** Source application id; groups the chat under an inbox section. */
437
+ app: z.string().min(1).optional(),
438
+ assignTo: assignToSchema.optional(),
439
+ title: z.string().min(1),
440
+ messages: z.array(agentChatSeedMessageSchema).default([]),
441
+ /** Provenance label stored on the session ("cron" | "agent" | ...). */
442
+ source: z.string().min(1).optional()
443
+ }).refine((data) => Boolean(data.agentIdentifier || data.parentChatId), {
444
+ message: "Provide either agentIdentifier or parentChatId"
445
+ });
446
+ var agentChatAuditInputBodySchema = z.object({
447
+ eveSessionId: z.string().min(1),
448
+ userId: z.string().min(1),
449
+ actionId: z.string().min(1),
450
+ actionTitle: z.string().optional(),
451
+ prompt: z.string().optional(),
452
+ data: z.record(z.string(), z.unknown()).optional(),
453
+ idempotencyKey: z.string().optional(),
454
+ /** Eve input request id — used to merge staged HITL metadata. */
455
+ requestId: z.string().optional(),
456
+ /** Eve tool call id — fallback lookup for staged HITL metadata. */
457
+ toolCallId: z.string().optional()
458
+ });
459
+ var eveHitlStagedOptionSchema = z.object({
460
+ id: z.string(),
461
+ label: z.string()
462
+ });
463
+ var agentChatStageHitlBodySchema = z.object({
464
+ eveSessionId: z.string().min(1),
465
+ requests: z.array(
466
+ z.object({
467
+ requestId: z.string().min(1),
468
+ toolCallId: z.string().min(1),
469
+ toolName: z.string().min(1),
470
+ prompt: z.string().min(1),
471
+ display: z.enum(["confirmation", "select", "text"]).optional(),
472
+ allowFreeform: z.boolean().optional(),
473
+ options: z.array(eveHitlStagedOptionSchema).optional(),
474
+ toolInput: z.record(z.string(), z.unknown()).optional()
475
+ })
476
+ )
477
+ });
478
+ var agentChatLinkTaskBodySchema = z.object({
479
+ eveSessionId: z.string().min(1),
480
+ publicTaskId: z.string().min(1),
481
+ toolCallId: z.string().min(1)
482
+ });
368
483
 
369
484
  // src/schemas/index.ts
370
- var safeUrlSchema2 = z2.string().refine((url) => isPublicHttpUrl(url), {
371
- message: PUBLIC_HTTP_URL_ERROR
485
+ var handlerUrlSchema2 = z2.string().refine((url) => isAllowedHandlerUrl(url), {
486
+ message: HANDLER_URL_ERROR
372
487
  });
373
488
  var jsonSchema7Schema2 = z2.custom(
374
489
  (val) => typeof val === "object" && val !== null,
@@ -379,7 +494,7 @@ var uiSchemaSchema2 = z2.custom((val) => typeof val === "object" && val !== null
379
494
  });
380
495
  var webhookHandlerSchema2 = z2.object({
381
496
  type: z2.literal("webhook"),
382
- url: safeUrlSchema2,
497
+ url: handlerUrlSchema2,
383
498
  headers: z2.record(z2.string(), z2.string())
384
499
  });
385
500
  var triggerHandlerSchema2 = webhookHandlerSchema2.extend({
@@ -387,13 +502,15 @@ var triggerHandlerSchema2 = webhookHandlerSchema2.extend({
387
502
  tokenId: z2.string().min(1)
388
503
  });
389
504
  var handlerSchema2 = z2.discriminatedUnion("type", [webhookHandlerSchema2, triggerHandlerSchema2]);
390
- var taskActionSchema2 = z2.object({
505
+ var taskActionInputSchema2 = z2.object({
391
506
  id: z2.string().min(1),
392
507
  title: z2.string().min(1),
393
508
  description: z2.string().optional(),
394
509
  schema: jsonSchema7Schema2.optional(),
395
510
  ui: uiSchemaSchema2.optional(),
396
- data: z2.record(z2.string(), z2.unknown()).optional(),
511
+ data: z2.record(z2.string(), z2.unknown()).optional()
512
+ });
513
+ var taskActionSchema2 = taskActionInputSchema2.extend({
397
514
  handlers: z2.array(handlerSchema2).min(1).optional()
398
515
  });
399
516
  var uiFieldSchemaSchema2 = z2.object({
@@ -525,6 +642,293 @@ function toDiscriminatedApprovalResult(actions, task) {
525
642
  };
526
643
  }
527
644
 
645
+ // src/http.ts
646
+ var RobotRockError = class extends Error {
647
+ constructor(message, statusCode, response) {
648
+ super(message);
649
+ this.statusCode = statusCode;
650
+ this.response = response;
651
+ this.name = "RobotRockError";
652
+ }
653
+ };
654
+ async function parseResponseBody(response) {
655
+ const contentType = response.headers.get("content-type") ?? "";
656
+ const bodyText = await response.text();
657
+ if (!bodyText) {
658
+ return null;
659
+ }
660
+ if (contentType.toLowerCase().includes("application/json")) {
661
+ try {
662
+ return JSON.parse(bodyText);
663
+ } catch {
664
+ }
665
+ }
666
+ try {
667
+ return JSON.parse(bodyText);
668
+ } catch {
669
+ return bodyText;
670
+ }
671
+ }
672
+ function getErrorMessage(data, fallback) {
673
+ if (data && typeof data === "object" && !Array.isArray(data)) {
674
+ const record = data;
675
+ const maybeMessage = record.message;
676
+ const maybeHint = record.hint;
677
+ if (typeof maybeMessage === "string" && maybeMessage.trim()) {
678
+ if (typeof maybeHint === "string" && maybeHint.trim()) {
679
+ return `${maybeMessage.trim()} ${maybeHint.trim()}`;
680
+ }
681
+ return maybeMessage;
682
+ }
683
+ }
684
+ if (typeof data === "string" && data.trim()) {
685
+ const compact = data.replace(/\s+/g, " ").trim();
686
+ const snippet = compact.length > 180 ? `${compact.slice(0, 180)}...` : compact;
687
+ return `${fallback}. Server returned non-JSON response: ${snippet}`;
688
+ }
689
+ return fallback;
690
+ }
691
+
692
+ // ../core/src/handler-retry.ts
693
+ var HANDLER_RETRY_DELAYS_MS = [
694
+ 6e4,
695
+ // 1m
696
+ 3e5,
697
+ // 5m
698
+ 18e5,
699
+ // 30m
700
+ 36e5,
701
+ // 1h
702
+ 216e5,
703
+ // 6h
704
+ 864e5,
705
+ // 24h
706
+ 1728e5
707
+ // 48h
708
+ ];
709
+ var HANDLER_MAX_ATTEMPTS = HANDLER_RETRY_DELAYS_MS.length + 1;
710
+
711
+ // ../core/src/attribution/index.ts
712
+ import { z as z3 } from "zod";
713
+
714
+ // ../core/src/app-url.ts
715
+ var PRODUCTION_APP_URL = "https://app.robotrock.io";
716
+ var PRODUCTION_API_URL = "https://api.robotrock.io/v1";
717
+ var DEV_API_URL = "http://localhost:4001/v1";
718
+ var APP_SIGNUP_URL = `${PRODUCTION_APP_URL}/sign-up`;
719
+ function normalizeRobotRockApiBaseUrl(baseUrl) {
720
+ const trimmed = baseUrl.trim().replace(/\/+$/, "");
721
+ if (trimmed.endsWith("/v1")) {
722
+ return trimmed;
723
+ }
724
+ return `${trimmed}/v1`;
725
+ }
726
+ function getRobotRockApiBaseUrl() {
727
+ const explicit = process.env.ROBOTROCK_BASE_URL?.trim() || process.env.ROBOTROCK_API_URL?.trim();
728
+ if (explicit) {
729
+ return normalizeRobotRockApiBaseUrl(explicit);
730
+ }
731
+ if (process.env.NODE_ENV === "production") {
732
+ return PRODUCTION_API_URL;
733
+ }
734
+ return DEV_API_URL;
735
+ }
736
+
737
+ // ../core/src/attribution/index.ts
738
+ var ATTRIBUTION_COOKIE_MAX_AGE = 60 * 60 * 24 * 90;
739
+ var attributionSchema = z3.object({
740
+ utmSource: z3.string().optional(),
741
+ utmMedium: z3.string().optional(),
742
+ utmCampaign: z3.string().optional(),
743
+ utmContent: z3.string().optional(),
744
+ utmTerm: z3.string().optional(),
745
+ gclid: z3.string().optional(),
746
+ landingUrl: z3.string().optional(),
747
+ referrer: z3.string().optional(),
748
+ capturedAt: z3.number()
749
+ });
750
+
751
+ // src/auth-headers.ts
752
+ function buildRobotRockAuthHeaders(auth) {
753
+ if (auth.kind === "apiKey") {
754
+ return {
755
+ "X-Api-Key": auth.apiKey
756
+ };
757
+ }
758
+ return {
759
+ Authorization: `Bearer ${auth.token}`,
760
+ "X-RobotRock-Tenant-Slug": auth.tenantSlug,
761
+ "X-RobotRock-Connection-Id": auth.connectionId
762
+ };
763
+ }
764
+ function resolveRobotRockAuthConfig(overrides) {
765
+ if (overrides?.agentService) {
766
+ return {
767
+ kind: "agentService",
768
+ ...overrides.agentService
769
+ };
770
+ }
771
+ const apiKey = overrides?.apiKey ?? process.env.ROBOTROCK_API_KEY;
772
+ if (!apiKey) {
773
+ throw new Error(
774
+ "RobotRock auth is required. Set ROBOTROCK_API_KEY, ROBOTROCK_AGENT_SERVICE_TOKEN with tenant context, or pass auth when creating the client."
775
+ );
776
+ }
777
+ return { kind: "apiKey", apiKey };
778
+ }
779
+
780
+ // src/chats.ts
781
+ function createChatsApi(config) {
782
+ const headers = () => ({
783
+ "Content-Type": "application/json",
784
+ ...buildRobotRockAuthHeaders(config.auth)
785
+ });
786
+ return {
787
+ async create(input) {
788
+ const bodyPayload = {
789
+ ...input,
790
+ app: input.app ?? config.app,
791
+ messages: input.messages ?? []
792
+ };
793
+ const validation = createAgentChatBodySchema.safeParse(bodyPayload);
794
+ if (!validation.success) {
795
+ throw new RobotRockError(
796
+ `Invalid chat: ${validation.error.issues[0]?.message}`,
797
+ 400,
798
+ validation.error.issues
799
+ );
800
+ }
801
+ const response = await fetch(`${config.baseUrl}/agent-chats`, {
802
+ method: "POST",
803
+ headers: headers(),
804
+ body: JSON.stringify(validation.data)
805
+ });
806
+ const data = await parseResponseBody(response);
807
+ if (!response.ok) {
808
+ throw new RobotRockError(
809
+ getErrorMessage(data, "Failed to create chat"),
810
+ response.status,
811
+ data
812
+ );
813
+ }
814
+ const result = data;
815
+ return { tenantSlug: result.tenantSlug, chats: result.chats };
816
+ },
817
+ async close(chatId, options) {
818
+ if (!chatId) {
819
+ throw new RobotRockError("chatId is required to close a chat", 400);
820
+ }
821
+ const response = await fetch(
822
+ `${config.baseUrl}/agent-chats/${encodeURIComponent(chatId)}/close`,
823
+ {
824
+ method: "POST",
825
+ headers: headers(),
826
+ body: JSON.stringify({ reason: options?.reason })
827
+ }
828
+ );
829
+ if (!response.ok) {
830
+ const data = await parseResponseBody(response);
831
+ throw new RobotRockError(
832
+ getErrorMessage(data, "Failed to close chat"),
833
+ response.status,
834
+ data
835
+ );
836
+ }
837
+ },
838
+ async stageHitlRequests(input) {
839
+ const validation = agentChatStageHitlBodySchema.safeParse(input);
840
+ if (!validation.success) {
841
+ throw new RobotRockError(
842
+ `Invalid stage HITL input: ${validation.error.issues[0]?.message}`,
843
+ 400,
844
+ validation.error.issues
845
+ );
846
+ }
847
+ const response = await fetch(`${config.baseUrl}/agent-chats/stage-hitl`, {
848
+ method: "POST",
849
+ headers: headers(),
850
+ body: JSON.stringify(validation.data)
851
+ });
852
+ if (!response.ok) {
853
+ const data = await parseResponseBody(response);
854
+ throw new RobotRockError(
855
+ getErrorMessage(data, "Failed to stage chat HITL requests"),
856
+ response.status,
857
+ data
858
+ );
859
+ }
860
+ },
861
+ async getStagedHitlRequests(eveSessionId) {
862
+ const trimmed = eveSessionId.trim();
863
+ if (!trimmed) {
864
+ throw new RobotRockError("eveSessionId is required", 400);
865
+ }
866
+ const url = new URL(`${config.baseUrl}/agent-chats/staged-hitl`);
867
+ url.searchParams.set("eveSessionId", trimmed);
868
+ const response = await fetch(url.toString(), {
869
+ method: "GET",
870
+ headers: headers()
871
+ });
872
+ const data = await parseResponseBody(response);
873
+ if (!response.ok) {
874
+ throw new RobotRockError(
875
+ getErrorMessage(data, "Failed to fetch staged chat HITL requests"),
876
+ response.status,
877
+ data
878
+ );
879
+ }
880
+ const requests = data.requests;
881
+ return Array.isArray(requests) ? requests : [];
882
+ },
883
+ async logInputSubmission(input) {
884
+ const validation = agentChatAuditInputBodySchema.safeParse(input);
885
+ if (!validation.success) {
886
+ throw new RobotRockError(
887
+ `Invalid audit input: ${validation.error.issues[0]?.message}`,
888
+ 400,
889
+ validation.error.issues
890
+ );
891
+ }
892
+ const response = await fetch(`${config.baseUrl}/agent-chats/audit-input`, {
893
+ method: "POST",
894
+ headers: headers(),
895
+ body: JSON.stringify(validation.data)
896
+ });
897
+ if (!response.ok) {
898
+ const data = await parseResponseBody(response);
899
+ throw new RobotRockError(
900
+ getErrorMessage(data, "Failed to log chat input submission"),
901
+ response.status,
902
+ data
903
+ );
904
+ }
905
+ },
906
+ async linkTask(input) {
907
+ const validation = agentChatLinkTaskBodySchema.safeParse(input);
908
+ if (!validation.success) {
909
+ throw new RobotRockError(
910
+ `Invalid link task input: ${validation.error.issues[0]?.message}`,
911
+ 400,
912
+ validation.error.issues
913
+ );
914
+ }
915
+ const response = await fetch(`${config.baseUrl}/agent-chats/link-task`, {
916
+ method: "POST",
917
+ headers: headers(),
918
+ body: JSON.stringify(validation.data)
919
+ });
920
+ if (!response.ok) {
921
+ const data = await parseResponseBody(response);
922
+ throw new RobotRockError(
923
+ getErrorMessage(data, "Failed to link inbox task to chat"),
924
+ response.status,
925
+ data
926
+ );
927
+ }
928
+ }
929
+ };
930
+ }
931
+
528
932
  // src/client.ts
529
933
  var DEFAULT_POLL_INTERVAL_MS = 2e3;
530
934
  var DEFAULT_TIMEOUT_MS = 24 * 60 * 60 * 1e3;
@@ -562,22 +966,18 @@ function serializeValidUntil(value) {
562
966
  }
563
967
  throw new RobotRockError("Invalid validUntil: expected a Date or parseable date string", 400);
564
968
  }
565
- var RobotRockError = class extends Error {
566
- constructor(message, statusCode, response) {
567
- super(message);
568
- this.statusCode = statusCode;
569
- this.response = response;
570
- this.name = "RobotRockError";
571
- }
572
- };
573
969
  var RobotRock = class {
574
- apiKey;
970
+ auth;
575
971
  baseUrl;
576
972
  app;
577
973
  agentVersion;
578
974
  contextVersion;
579
975
  webhook;
580
976
  polling;
977
+ /** Task CRUD: `create`, `get`, `cancel`, `sendUpdate`. */
978
+ tasks;
979
+ /** Chat CRUD: `create`, `close`. */
980
+ chats;
581
981
  constructor(config) {
582
982
  if (config.webhook && config.polling) {
583
983
  throw new Error(
@@ -585,24 +985,42 @@ var RobotRock = class {
585
985
  );
586
986
  }
587
987
  const apiKey = config.apiKey ?? process.env.ROBOTROCK_API_KEY;
588
- if (!apiKey) {
988
+ const agentService = config.agentService;
989
+ if (apiKey && agentService) {
589
990
  throw new Error(
590
- "RobotRock API key is required. Set ROBOTROCK_API_KEY or pass apiKey when creating the client."
991
+ "RobotRock client cannot configure both apiKey and agentService."
591
992
  );
592
993
  }
593
- this.apiKey = apiKey;
594
- const rawBase = config.baseUrl ?? "https://api.robotrock.io/v1";
595
- this.baseUrl = rawBase.replace(/\/+$/, "");
994
+ this.auth = resolveRobotRockAuthConfig({
995
+ ...apiKey ? { apiKey } : {},
996
+ ...agentService ? { agentService } : {}
997
+ });
998
+ this.baseUrl = config.baseUrl ?? getRobotRockApiBaseUrl();
596
999
  this.app = config.app;
597
1000
  this.agentVersion = config.version ?? resolveAgentVersionFromEnv();
598
1001
  this.contextVersion = config.advanced?.contextVersion ?? TASK_CONTEXT_FORMAT_VERSION2;
599
1002
  this.webhook = config.webhook;
600
1003
  this.polling = config.polling ?? {};
1004
+ this.tasks = {
1005
+ create: (task) => this.createTaskRequest(task),
1006
+ get: (taskId) => this.getTaskById(taskId),
1007
+ cancel: (taskId) => this.cancelTaskRequest(taskId),
1008
+ sendUpdate: (input) => this.sendThreadUpdate(input)
1009
+ };
1010
+ this.chats = createChatsApi({
1011
+ baseUrl: this.baseUrl,
1012
+ auth: this.auth,
1013
+ app: this.app
1014
+ });
601
1015
  }
602
- /**
603
- * Create a task via POST /v1 without waiting for a human response.
604
- */
605
- async createTask(task) {
1016
+ authHeaders(extra) {
1017
+ return {
1018
+ "Content-Type": "application/json",
1019
+ ...buildRobotRockAuthHeaders(this.auth),
1020
+ ...extra
1021
+ };
1022
+ }
1023
+ async createTaskRequest(task) {
606
1024
  const normalizedTask = normalizeSendToHumanInput(task, {
607
1025
  webhook: this.webhook,
608
1026
  app: this.app,
@@ -626,13 +1044,9 @@ var RobotRock = class {
626
1044
  validation.error.issues
627
1045
  );
628
1046
  }
629
- const headers = {
630
- "Content-Type": "application/json",
631
- "X-Api-Key": this.apiKey
632
- };
633
- if (task.idempotencyKey) {
634
- headers["Idempotency-Key"] = task.idempotencyKey;
635
- }
1047
+ const headers = this.authHeaders(
1048
+ task.idempotencyKey ? { "Idempotency-Key": task.idempotencyKey } : void 0
1049
+ );
636
1050
  const response = await fetch(`${this.baseUrl}/`, {
637
1051
  method: "POST",
638
1052
  headers,
@@ -655,7 +1069,7 @@ var RobotRock = class {
655
1069
  contextVersion: this.contextVersion,
656
1070
  agentVersion: this.agentVersion
657
1071
  });
658
- const createdTaskTask = await this.createTask(task);
1072
+ const createdTaskTask = await this.createTaskRequest(task);
659
1073
  const hasHandlers = normalizedTask.actions.some(
660
1074
  (action) => Array.isArray(action.handlers) && action.handlers.length > 0
661
1075
  );
@@ -672,7 +1086,7 @@ var RobotRock = class {
672
1086
  const deadline = validUntilMs !== void 0 ? Math.min(pollingDeadline, validUntilMs) : pollingDeadline;
673
1087
  const taskId = createdTaskTask.taskId;
674
1088
  while (Date.now() < deadline) {
675
- const existing = await this.getTask(taskId);
1089
+ const existing = await this.getTaskById(taskId);
676
1090
  if (existing?.status === "handled" && existing.handled) {
677
1091
  return {
678
1092
  mode: "handled",
@@ -694,15 +1108,38 @@ var RobotRock = class {
694
1108
  }
695
1109
  throw new TaskTimeoutError(`No human response within ${timeoutMs}ms`);
696
1110
  }
1111
+ /**
1112
+ * Create a task via POST /v1 without waiting for a human response.
1113
+ * @deprecated Use `client.tasks.create()` instead.
1114
+ */
1115
+ async createTask(task) {
1116
+ return this.tasks.create(task);
1117
+ }
697
1118
  /**
698
1119
  * Get a task by public task id (returned as `task.taskId` from {@link sendToHuman}).
1120
+ * @deprecated Use `client.tasks.get()` instead.
699
1121
  */
700
1122
  async getTask(taskId) {
1123
+ return this.tasks.get(taskId);
1124
+ }
1125
+ /**
1126
+ * Log a status update against a thread.
1127
+ * @deprecated Use `client.tasks.sendUpdate()` instead.
1128
+ */
1129
+ async sendUpdate(input) {
1130
+ return this.tasks.sendUpdate(input);
1131
+ }
1132
+ /**
1133
+ * Cancel a task by public task id.
1134
+ * @deprecated Use `client.tasks.cancel()` instead.
1135
+ */
1136
+ async cancelTask(taskId) {
1137
+ return this.tasks.cancel(taskId);
1138
+ }
1139
+ async getTaskById(taskId) {
701
1140
  const response = await fetch(`${this.baseUrl}/tasks/${taskId}`, {
702
1141
  method: "GET",
703
- headers: {
704
- "X-Api-Key": this.apiKey
705
- }
1142
+ headers: this.authHeaders()
706
1143
  });
707
1144
  if (response.status === 404) {
708
1145
  return null;
@@ -717,11 +1154,11 @@ var RobotRock = class {
717
1154
  }
718
1155
  return data;
719
1156
  }
720
- /**
721
- * Log a status update against a thread. The update shows in the inbox status
722
- * bar and thread update log for every task in the thread.
723
- */
724
- async sendUpdate({ threadId, message, status }) {
1157
+ async sendThreadUpdate({
1158
+ threadId,
1159
+ message,
1160
+ status
1161
+ }) {
725
1162
  if (!threadId) {
726
1163
  throw new RobotRockError("threadId is required to send an update", 400);
727
1164
  }
@@ -737,10 +1174,7 @@ var RobotRock = class {
737
1174
  `${this.baseUrl}/threads/${encodeURIComponent(threadId)}/updates`,
738
1175
  {
739
1176
  method: "POST",
740
- headers: {
741
- "Content-Type": "application/json",
742
- "X-Api-Key": this.apiKey
743
- },
1177
+ headers: this.authHeaders(),
744
1178
  body: JSON.stringify(validation.data)
745
1179
  }
746
1180
  );
@@ -754,12 +1188,10 @@ var RobotRock = class {
754
1188
  }
755
1189
  return data.update;
756
1190
  }
757
- async cancelTask(taskId) {
1191
+ async cancelTaskRequest(taskId) {
758
1192
  const response = await fetch(`${this.baseUrl}/tasks/${taskId}/cancel`, {
759
1193
  method: "POST",
760
- headers: {
761
- "X-Api-Key": this.apiKey
762
- }
1194
+ headers: this.authHeaders()
763
1195
  });
764
1196
  if (!response.ok) {
765
1197
  const data = await parseResponseBody(response);
@@ -813,50 +1245,26 @@ function normalizeSendToHumanInput(task, clientDefaults) {
813
1245
  actions: normalizedActions
814
1246
  };
815
1247
  }
816
- async function parseResponseBody(response) {
817
- const contentType = response.headers.get("content-type") ?? "";
818
- const bodyText = await response.text();
819
- if (!bodyText) {
820
- return null;
821
- }
822
- if (contentType.toLowerCase().includes("application/json")) {
823
- try {
824
- return JSON.parse(bodyText);
825
- } catch {
826
- }
827
- }
828
- try {
829
- return JSON.parse(bodyText);
830
- } catch {
831
- return bodyText;
832
- }
833
- }
834
- function getErrorMessage(data, fallback) {
835
- if (data && typeof data === "object" && !Array.isArray(data)) {
836
- const maybeMessage = data.message;
837
- if (typeof maybeMessage === "string" && maybeMessage.trim()) {
838
- return maybeMessage;
839
- }
840
- }
841
- if (typeof data === "string" && data.trim()) {
842
- const compact = data.replace(/\s+/g, " ").trim();
843
- const snippet = compact.length > 180 ? `${compact.slice(0, 180)}...` : compact;
844
- return `${fallback}. Server returned non-JSON response: ${snippet}`;
845
- }
846
- return fallback;
847
- }
848
1248
 
849
1249
  // src/env.ts
850
- var DEFAULT_BASE_URL = "https://api.robotrock.io/v1";
851
1250
  function resolveRobotRockConfig(overrides) {
1251
+ const agentService = overrides?.agentService;
852
1252
  const apiKey = overrides?.apiKey ?? process.env.ROBOTROCK_API_KEY;
853
- if (!apiKey) {
1253
+ if (agentService && apiKey) {
854
1254
  throw new Error(
855
- "RobotRock API key is required. Set ROBOTROCK_API_KEY or pass apiKey when creating the client."
1255
+ "RobotRock client cannot configure both apiKey and agentService."
856
1256
  );
857
1257
  }
858
- const baseUrl = overrides?.baseUrl ?? process.env.ROBOTROCK_BASE_URL ?? process.env.ROBOTROCK_API_URL ?? DEFAULT_BASE_URL;
1258
+ const baseUrl = overrides?.baseUrl ?? getRobotRockApiBaseUrl();
859
1259
  const app = overrides?.app ?? process.env.ROBOTROCK_APP;
1260
+ if (agentService) {
1261
+ return app ? { agentService, baseUrl, app } : { agentService, baseUrl };
1262
+ }
1263
+ if (!apiKey) {
1264
+ throw new Error(
1265
+ "RobotRock API key is required. Set ROBOTROCK_API_KEY or pass agentService when creating the client."
1266
+ );
1267
+ }
860
1268
  return app ? { apiKey, baseUrl, app } : { apiKey, baseUrl };
861
1269
  }
862
1270
 
@@ -1228,7 +1636,7 @@ async function sendRobotRockUpdate(payload) {
1228
1636
  ...baseConfig.version ? { version: baseConfig.version } : {},
1229
1637
  ...baseConfig.advanced ? { advanced: baseConfig.advanced } : {}
1230
1638
  });
1231
- return client.sendUpdate(update);
1639
+ return client.tasks.sendUpdate(update);
1232
1640
  }
1233
1641
  async function sendUpdateInWorkflow(payload) {
1234
1642
  return sendRobotRockUpdate(payload);