robotrock 0.9.0 → 1.0.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.
@@ -399,13 +399,15 @@ var triggerHandlerSchema = webhookHandlerSchema.extend({
399
399
  tokenId: z.string().min(1)
400
400
  });
401
401
  var handlerSchema = z.discriminatedUnion("type", [webhookHandlerSchema, triggerHandlerSchema]);
402
- var taskActionSchema = z.object({
402
+ var taskActionInputSchema = z.object({
403
403
  id: z.string().min(1),
404
404
  title: z.string().min(1),
405
405
  description: z.string().optional(),
406
406
  schema: jsonSchema7Schema.optional(),
407
407
  ui: uiSchemaSchema.optional(),
408
- data: z.record(z.string(), z.unknown()).optional(),
408
+ data: z.record(z.string(), z.unknown()).optional()
409
+ });
410
+ var taskActionSchema = taskActionInputSchema.extend({
409
411
  // Optional handlers for this action - if present, must have at least 1
410
412
  handlers: z.array(handlerSchema).min(1).optional()
411
413
  });
@@ -513,6 +515,56 @@ var createTaskBodySchema = taskContextObjectBaseSchema.extend({
513
515
  /** Agent release version — not shown in inbox UI. */
514
516
  agent: agentTelemetrySchema.optional()
515
517
  }).transform(normalizeTaskContextVersion).superRefine(refineContextPublicUrls);
518
+ var agentChatSeedActionSchema = z.object({
519
+ /** Stable action id echoed back on submit; auto-derived from title when omitted. */
520
+ id: z.string().min(1).optional(),
521
+ title: z.string().min(1),
522
+ description: z.string().optional(),
523
+ /** JSON Schema object for the form fields. */
524
+ schema: z.record(z.string(), z.unknown()).optional(),
525
+ /** RJSF ui schema overrides (ui:widget, ui:enumNames, etc.). */
526
+ ui: z.record(z.string(), z.unknown()).optional(),
527
+ /** Optional default field values. */
528
+ data: z.record(z.string(), z.unknown()).optional()
529
+ });
530
+ var agentChatSeedMessageSchema = z.object({
531
+ role: z.enum(["user", "assistant"]),
532
+ text: z.string().min(1),
533
+ /** Optional display-name override for the message sender. */
534
+ senderName: z.string().min(1).optional(),
535
+ /**
536
+ * Optional structured input request rendered as a styled RobotRock action
537
+ * widget below the message text. Use this instead of asking the user to reply
538
+ * in plain text so the request is always a proper action form.
539
+ */
540
+ requestActionInput: z.object({
541
+ prompt: z.string().optional(),
542
+ action: agentChatSeedActionSchema
543
+ }).optional()
544
+ });
545
+ var createAgentChatBodySchema = z.object({
546
+ /** Trigger chat agent id (task slug). Required unless `parentChatId` is set. */
547
+ agentIdentifier: z.string().min(1).optional(),
548
+ /** Inherit tenant/connection/agent from an existing chat (agent-spawned chats). */
549
+ parentChatId: z.string().min(1).optional(),
550
+ /** Convex tenantTriggerConnections id; resolved from the tenant when omitted. */
551
+ connectionId: z.string().min(1).optional(),
552
+ /** Source application id; groups the chat under an inbox section. */
553
+ app: z.string().min(1).optional(),
554
+ assignTo: assignToSchema.optional(),
555
+ title: z.string().min(1),
556
+ messages: z.array(agentChatSeedMessageSchema).default([]),
557
+ /** Provenance label stored on the session ("cron" | "agent" | ...). */
558
+ source: z.string().min(1).optional()
559
+ }).refine((data) => Boolean(data.agentIdentifier || data.parentChatId), {
560
+ message: "Provide either agentIdentifier or parentChatId"
561
+ });
562
+ var agentChatTransportBodySchema = z.object({
563
+ chatId: z.string().min(1),
564
+ publicAccessToken: z.string().min(1),
565
+ lastEventId: z.string().optional(),
566
+ isStreaming: z.boolean().optional()
567
+ });
516
568
 
517
569
  // src/schemas/index.ts
518
570
  var safeUrlSchema2 = z2.string().refine((url) => isPublicHttpUrl(url), {
@@ -535,13 +587,15 @@ var triggerHandlerSchema2 = webhookHandlerSchema2.extend({
535
587
  tokenId: z2.string().min(1)
536
588
  });
537
589
  var handlerSchema2 = z2.discriminatedUnion("type", [webhookHandlerSchema2, triggerHandlerSchema2]);
538
- var taskActionSchema2 = z2.object({
590
+ var taskActionInputSchema2 = z2.object({
539
591
  id: z2.string().min(1),
540
592
  title: z2.string().min(1),
541
593
  description: z2.string().optional(),
542
594
  schema: jsonSchema7Schema2.optional(),
543
595
  ui: uiSchemaSchema2.optional(),
544
- data: z2.record(z2.string(), z2.unknown()).optional(),
596
+ data: z2.record(z2.string(), z2.unknown()).optional()
597
+ });
598
+ var taskActionSchema2 = taskActionInputSchema2.extend({
545
599
  handlers: z2.array(handlerSchema2).min(1).optional()
546
600
  });
547
601
  var uiFieldSchemaSchema2 = z2.object({
@@ -673,6 +727,149 @@ function toDiscriminatedApprovalResult(actions, task2) {
673
727
  };
674
728
  }
675
729
 
730
+ // src/http.ts
731
+ var RobotRockError = class extends Error {
732
+ constructor(message, statusCode, response) {
733
+ super(message);
734
+ this.statusCode = statusCode;
735
+ this.response = response;
736
+ this.name = "RobotRockError";
737
+ }
738
+ };
739
+ async function parseResponseBody(response) {
740
+ const contentType = response.headers.get("content-type") ?? "";
741
+ const bodyText = await response.text();
742
+ if (!bodyText) {
743
+ return null;
744
+ }
745
+ if (contentType.toLowerCase().includes("application/json")) {
746
+ try {
747
+ return JSON.parse(bodyText);
748
+ } catch {
749
+ }
750
+ }
751
+ try {
752
+ return JSON.parse(bodyText);
753
+ } catch {
754
+ return bodyText;
755
+ }
756
+ }
757
+ function getErrorMessage(data, fallback) {
758
+ if (data && typeof data === "object" && !Array.isArray(data)) {
759
+ const maybeMessage = data.message;
760
+ if (typeof maybeMessage === "string" && maybeMessage.trim()) {
761
+ return maybeMessage;
762
+ }
763
+ }
764
+ if (typeof data === "string" && data.trim()) {
765
+ const compact = data.replace(/\s+/g, " ").trim();
766
+ const snippet = compact.length > 180 ? `${compact.slice(0, 180)}...` : compact;
767
+ return `${fallback}. Server returned non-JSON response: ${snippet}`;
768
+ }
769
+ return fallback;
770
+ }
771
+
772
+ // ../core/src/handler-retry.ts
773
+ var HANDLER_RETRY_DELAYS_MS = [
774
+ 6e4,
775
+ // 1m
776
+ 3e5,
777
+ // 5m
778
+ 18e5,
779
+ // 30m
780
+ 36e5,
781
+ // 1h
782
+ 216e5,
783
+ // 6h
784
+ 864e5,
785
+ // 24h
786
+ 1728e5
787
+ // 48h
788
+ ];
789
+ var HANDLER_MAX_ATTEMPTS = HANDLER_RETRY_DELAYS_MS.length + 1;
790
+
791
+ // ../core/src/attribution/index.ts
792
+ import { z as z3 } from "zod";
793
+
794
+ // ../core/src/app-url.ts
795
+ var PRODUCTION_APP_URL = "https://app.robotrock.io";
796
+ var APP_SIGNUP_URL = `${PRODUCTION_APP_URL}/sign-up`;
797
+
798
+ // ../core/src/attribution/index.ts
799
+ var ATTRIBUTION_COOKIE_MAX_AGE = 60 * 60 * 24 * 90;
800
+ var attributionSchema = z3.object({
801
+ utmSource: z3.string().optional(),
802
+ utmMedium: z3.string().optional(),
803
+ utmCampaign: z3.string().optional(),
804
+ utmContent: z3.string().optional(),
805
+ utmTerm: z3.string().optional(),
806
+ gclid: z3.string().optional(),
807
+ landingUrl: z3.string().optional(),
808
+ referrer: z3.string().optional(),
809
+ capturedAt: z3.number()
810
+ });
811
+
812
+ // src/chats.ts
813
+ function createChatsApi(config) {
814
+ const headers = () => ({
815
+ "Content-Type": "application/json",
816
+ "X-Api-Key": config.apiKey
817
+ });
818
+ return {
819
+ async create(input) {
820
+ const bodyPayload = {
821
+ ...input,
822
+ app: input.app ?? config.app,
823
+ messages: input.messages ?? []
824
+ };
825
+ const validation = createAgentChatBodySchema.safeParse(bodyPayload);
826
+ if (!validation.success) {
827
+ throw new RobotRockError(
828
+ `Invalid chat: ${validation.error.issues[0]?.message}`,
829
+ 400,
830
+ validation.error.issues
831
+ );
832
+ }
833
+ const response = await fetch(`${config.baseUrl}/agent-chats`, {
834
+ method: "POST",
835
+ headers: headers(),
836
+ body: JSON.stringify(validation.data)
837
+ });
838
+ const data = await parseResponseBody(response);
839
+ if (!response.ok) {
840
+ throw new RobotRockError(
841
+ getErrorMessage(data, "Failed to create chat"),
842
+ response.status,
843
+ data
844
+ );
845
+ }
846
+ const result = data;
847
+ return { tenantSlug: result.tenantSlug, chats: result.chats };
848
+ },
849
+ async close(chatId, options) {
850
+ if (!chatId) {
851
+ throw new RobotRockError("chatId is required to close a chat", 400);
852
+ }
853
+ const response = await fetch(
854
+ `${config.baseUrl}/agent-chats/${encodeURIComponent(chatId)}/close`,
855
+ {
856
+ method: "POST",
857
+ headers: headers(),
858
+ body: JSON.stringify({ reason: options?.reason })
859
+ }
860
+ );
861
+ if (!response.ok) {
862
+ const data = await parseResponseBody(response);
863
+ throw new RobotRockError(
864
+ getErrorMessage(data, "Failed to close chat"),
865
+ response.status,
866
+ data
867
+ );
868
+ }
869
+ }
870
+ };
871
+ }
872
+
676
873
  // src/client.ts
677
874
  var DEFAULT_POLL_INTERVAL_MS = 2e3;
678
875
  var DEFAULT_TIMEOUT_MS = 24 * 60 * 60 * 1e3;
@@ -710,14 +907,6 @@ function serializeValidUntil(value) {
710
907
  }
711
908
  throw new RobotRockError("Invalid validUntil: expected a Date or parseable date string", 400);
712
909
  }
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
910
  var RobotRock = class {
722
911
  apiKey;
723
912
  baseUrl;
@@ -726,6 +915,10 @@ var RobotRock = class {
726
915
  contextVersion;
727
916
  webhook;
728
917
  polling;
918
+ /** Task CRUD: `create`, `get`, `cancel`, `sendUpdate`. */
919
+ tasks;
920
+ /** Chat CRUD: `create`, `close`. */
921
+ chats;
729
922
  constructor(config) {
730
923
  if (config.webhook && config.polling) {
731
924
  throw new Error(
@@ -746,11 +939,19 @@ var RobotRock = class {
746
939
  this.contextVersion = config.advanced?.contextVersion ?? TASK_CONTEXT_FORMAT_VERSION2;
747
940
  this.webhook = config.webhook;
748
941
  this.polling = config.polling ?? {};
942
+ this.tasks = {
943
+ create: (task2) => this.createTaskRequest(task2),
944
+ get: (taskId) => this.getTaskById(taskId),
945
+ cancel: (taskId) => this.cancelTaskRequest(taskId),
946
+ sendUpdate: (input) => this.sendThreadUpdate(input)
947
+ };
948
+ this.chats = createChatsApi({
949
+ baseUrl: this.baseUrl,
950
+ apiKey: this.apiKey,
951
+ app: this.app
952
+ });
749
953
  }
750
- /**
751
- * Create a task via POST /v1 without waiting for a human response.
752
- */
753
- async createTask(task2) {
954
+ async createTaskRequest(task2) {
754
955
  const normalizedTask = normalizeSendToHumanInput(task2, {
755
956
  webhook: this.webhook,
756
957
  app: this.app,
@@ -803,7 +1004,7 @@ var RobotRock = class {
803
1004
  contextVersion: this.contextVersion,
804
1005
  agentVersion: this.agentVersion
805
1006
  });
806
- const createdTaskTask = await this.createTask(task2);
1007
+ const createdTaskTask = await this.createTaskRequest(task2);
807
1008
  const hasHandlers = normalizedTask.actions.some(
808
1009
  (action) => Array.isArray(action.handlers) && action.handlers.length > 0
809
1010
  );
@@ -820,7 +1021,7 @@ var RobotRock = class {
820
1021
  const deadline = validUntilMs !== void 0 ? Math.min(pollingDeadline, validUntilMs) : pollingDeadline;
821
1022
  const taskId = createdTaskTask.taskId;
822
1023
  while (Date.now() < deadline) {
823
- const existing = await this.getTask(taskId);
1024
+ const existing = await this.getTaskById(taskId);
824
1025
  if (existing?.status === "handled" && existing.handled) {
825
1026
  return {
826
1027
  mode: "handled",
@@ -842,10 +1043,35 @@ var RobotRock = class {
842
1043
  }
843
1044
  throw new TaskTimeoutError(`No human response within ${timeoutMs}ms`);
844
1045
  }
1046
+ /**
1047
+ * Create a task via POST /v1 without waiting for a human response.
1048
+ * @deprecated Use `client.tasks.create()` instead.
1049
+ */
1050
+ async createTask(task2) {
1051
+ return this.tasks.create(task2);
1052
+ }
845
1053
  /**
846
1054
  * Get a task by public task id (returned as `task.taskId` from {@link sendToHuman}).
1055
+ * @deprecated Use `client.tasks.get()` instead.
847
1056
  */
848
1057
  async getTask(taskId) {
1058
+ return this.tasks.get(taskId);
1059
+ }
1060
+ /**
1061
+ * Log a status update against a thread.
1062
+ * @deprecated Use `client.tasks.sendUpdate()` instead.
1063
+ */
1064
+ async sendUpdate(input) {
1065
+ return this.tasks.sendUpdate(input);
1066
+ }
1067
+ /**
1068
+ * Cancel a task by public task id.
1069
+ * @deprecated Use `client.tasks.cancel()` instead.
1070
+ */
1071
+ async cancelTask(taskId) {
1072
+ return this.tasks.cancel(taskId);
1073
+ }
1074
+ async getTaskById(taskId) {
849
1075
  const response = await fetch(`${this.baseUrl}/tasks/${taskId}`, {
850
1076
  method: "GET",
851
1077
  headers: {
@@ -865,11 +1091,11 @@ var RobotRock = class {
865
1091
  }
866
1092
  return data;
867
1093
  }
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 }) {
1094
+ async sendThreadUpdate({
1095
+ threadId,
1096
+ message,
1097
+ status
1098
+ }) {
873
1099
  if (!threadId) {
874
1100
  throw new RobotRockError("threadId is required to send an update", 400);
875
1101
  }
@@ -902,7 +1128,7 @@ var RobotRock = class {
902
1128
  }
903
1129
  return data.update;
904
1130
  }
905
- async cancelTask(taskId) {
1131
+ async cancelTaskRequest(taskId) {
906
1132
  const response = await fetch(`${this.baseUrl}/tasks/${taskId}/cancel`, {
907
1133
  method: "POST",
908
1134
  headers: {
@@ -961,38 +1187,6 @@ function normalizeSendToHumanInput(task2, clientDefaults) {
961
1187
  actions: normalizedActions
962
1188
  };
963
1189
  }
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
1190
 
997
1191
  // src/env.ts
998
1192
  var DEFAULT_BASE_URL = "https://api.robotrock.io/v1";