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.
@@ -301,7 +301,7 @@ function refineContextPublicUrls(data, ctx) {
301
301
  });
302
302
  }
303
303
  }
304
- var safeUrlSchema, jsonSchema7Schema, uiSchemaSchema, webhookHandlerSchema, triggerHandlerSchema, handlerSchema, taskActionSchema, uiFieldSchemaSchema, contextUiSchema, contextDataSchema, TASK_CONTEXT_FORMAT_VERSION, taskContextObjectBaseSchema, taskContextObjectSchema, taskContextSchema, assignToSchema, threadUpdateMessageSchema, threadUpdateStatuses, threadUpdateStatusSchema, threadUpdateInputSchema, taskPriorities, taskPrioritySchema, agentTelemetrySchema, createTaskBodySchema;
304
+ var safeUrlSchema, jsonSchema7Schema, uiSchemaSchema, webhookHandlerSchema, triggerHandlerSchema, handlerSchema, taskActionInputSchema, taskActionSchema, uiFieldSchemaSchema, contextUiSchema, contextDataSchema, TASK_CONTEXT_FORMAT_VERSION, taskContextObjectBaseSchema, taskContextObjectSchema, taskContextSchema, assignToSchema, threadUpdateMessageSchema, threadUpdateStatuses, threadUpdateStatusSchema, threadUpdateInputSchema, taskPriorities, taskPrioritySchema, agentTelemetrySchema, createTaskBodySchema, agentChatSeedActionSchema, agentChatSeedMessageSchema, createAgentChatBodySchema, agentChatTransportBodySchema;
305
305
  var init_task = __esm({
306
306
  "../core/src/schemas/task.ts"() {
307
307
  "use strict";
@@ -327,13 +327,15 @@ var init_task = __esm({
327
327
  tokenId: z.string().min(1)
328
328
  });
329
329
  handlerSchema = z.discriminatedUnion("type", [webhookHandlerSchema, triggerHandlerSchema]);
330
- taskActionSchema = z.object({
330
+ taskActionInputSchema = z.object({
331
331
  id: z.string().min(1),
332
332
  title: z.string().min(1),
333
333
  description: z.string().optional(),
334
334
  schema: jsonSchema7Schema.optional(),
335
335
  ui: uiSchemaSchema.optional(),
336
- data: z.record(z.string(), z.unknown()).optional(),
336
+ data: z.record(z.string(), z.unknown()).optional()
337
+ });
338
+ taskActionSchema = taskActionInputSchema.extend({
337
339
  // Optional handlers for this action - if present, must have at least 1
338
340
  handlers: z.array(handlerSchema).min(1).optional()
339
341
  });
@@ -424,6 +426,56 @@ var init_task = __esm({
424
426
  /** Agent release version — not shown in inbox UI. */
425
427
  agent: agentTelemetrySchema.optional()
426
428
  }).transform(normalizeTaskContextVersion).superRefine(refineContextPublicUrls);
429
+ agentChatSeedActionSchema = z.object({
430
+ /** Stable action id echoed back on submit; auto-derived from title when omitted. */
431
+ id: z.string().min(1).optional(),
432
+ title: z.string().min(1),
433
+ description: z.string().optional(),
434
+ /** JSON Schema object for the form fields. */
435
+ schema: z.record(z.string(), z.unknown()).optional(),
436
+ /** RJSF ui schema overrides (ui:widget, ui:enumNames, etc.). */
437
+ ui: z.record(z.string(), z.unknown()).optional(),
438
+ /** Optional default field values. */
439
+ data: z.record(z.string(), z.unknown()).optional()
440
+ });
441
+ agentChatSeedMessageSchema = z.object({
442
+ role: z.enum(["user", "assistant"]),
443
+ text: z.string().min(1),
444
+ /** Optional display-name override for the message sender. */
445
+ senderName: z.string().min(1).optional(),
446
+ /**
447
+ * Optional structured input request rendered as a styled RobotRock action
448
+ * widget below the message text. Use this instead of asking the user to reply
449
+ * in plain text so the request is always a proper action form.
450
+ */
451
+ requestActionInput: z.object({
452
+ prompt: z.string().optional(),
453
+ action: agentChatSeedActionSchema
454
+ }).optional()
455
+ });
456
+ createAgentChatBodySchema = z.object({
457
+ /** Trigger chat agent id (task slug). Required unless `parentChatId` is set. */
458
+ agentIdentifier: z.string().min(1).optional(),
459
+ /** Inherit tenant/connection/agent from an existing chat (agent-spawned chats). */
460
+ parentChatId: z.string().min(1).optional(),
461
+ /** Convex tenantTriggerConnections id; resolved from the tenant when omitted. */
462
+ connectionId: z.string().min(1).optional(),
463
+ /** Source application id; groups the chat under an inbox section. */
464
+ app: z.string().min(1).optional(),
465
+ assignTo: assignToSchema.optional(),
466
+ title: z.string().min(1),
467
+ messages: z.array(agentChatSeedMessageSchema).default([]),
468
+ /** Provenance label stored on the session ("cron" | "agent" | ...). */
469
+ source: z.string().min(1).optional()
470
+ }).refine((data) => Boolean(data.agentIdentifier || data.parentChatId), {
471
+ message: "Provide either agentIdentifier or parentChatId"
472
+ });
473
+ agentChatTransportBodySchema = z.object({
474
+ chatId: z.string().min(1),
475
+ publicAccessToken: z.string().min(1),
476
+ lastEventId: z.string().optional(),
477
+ isStreaming: z.boolean().optional()
478
+ });
427
479
  }
428
480
  });
429
481
 
@@ -463,7 +515,7 @@ function refineContextPublicUrls2(data, ctx) {
463
515
  });
464
516
  }
465
517
  }
466
- var safeUrlSchema2, jsonSchema7Schema2, uiSchemaSchema2, webhookHandlerSchema2, triggerHandlerSchema2, handlerSchema2, taskActionSchema2, uiFieldSchemaSchema2, contextUiSchema2, contextDataSchema2, TASK_CONTEXT_FORMAT_VERSION2, taskContextObjectBaseSchema2, taskContextObjectSchema2, taskContextSchema2, assignToSchema2, threadUpdateMessageSchema2, threadUpdateStatuses2, threadUpdateStatusSchema2, threadUpdateInputSchema2, taskPriorities2, taskPrioritySchema2, agentTelemetrySchema2, createTaskBodySchema2, threadUpdateBodySchema;
518
+ var safeUrlSchema2, jsonSchema7Schema2, uiSchemaSchema2, webhookHandlerSchema2, triggerHandlerSchema2, handlerSchema2, taskActionInputSchema2, taskActionSchema2, uiFieldSchemaSchema2, contextUiSchema2, contextDataSchema2, TASK_CONTEXT_FORMAT_VERSION2, taskContextObjectBaseSchema2, taskContextObjectSchema2, taskContextSchema2, assignToSchema2, threadUpdateMessageSchema2, threadUpdateStatuses2, threadUpdateStatusSchema2, threadUpdateInputSchema2, taskPriorities2, taskPrioritySchema2, agentTelemetrySchema2, createTaskBodySchema2, threadUpdateBodySchema;
467
519
  var init_schemas2 = __esm({
468
520
  "src/schemas/index.ts"() {
469
521
  "use strict";
@@ -489,13 +541,15 @@ var init_schemas2 = __esm({
489
541
  tokenId: z2.string().min(1)
490
542
  });
491
543
  handlerSchema2 = z2.discriminatedUnion("type", [webhookHandlerSchema2, triggerHandlerSchema2]);
492
- taskActionSchema2 = z2.object({
544
+ taskActionInputSchema2 = z2.object({
493
545
  id: z2.string().min(1),
494
546
  title: z2.string().min(1),
495
547
  description: z2.string().optional(),
496
548
  schema: jsonSchema7Schema2.optional(),
497
549
  ui: uiSchemaSchema2.optional(),
498
- data: z2.record(z2.string(), z2.unknown()).optional(),
550
+ data: z2.record(z2.string(), z2.unknown()).optional()
551
+ });
552
+ taskActionSchema2 = taskActionInputSchema2.extend({
499
553
  handlers: z2.array(handlerSchema2).min(1).optional()
500
554
  });
501
555
  uiFieldSchemaSchema2 = z2.object({
@@ -618,6 +672,240 @@ var init_approval_result = __esm({
618
672
  }
619
673
  });
620
674
 
675
+ // src/http.ts
676
+ async function parseResponseBody(response) {
677
+ const contentType = response.headers.get("content-type") ?? "";
678
+ const bodyText = await response.text();
679
+ if (!bodyText) {
680
+ return null;
681
+ }
682
+ if (contentType.toLowerCase().includes("application/json")) {
683
+ try {
684
+ return JSON.parse(bodyText);
685
+ } catch {
686
+ }
687
+ }
688
+ try {
689
+ return JSON.parse(bodyText);
690
+ } catch {
691
+ return bodyText;
692
+ }
693
+ }
694
+ function getErrorMessage(data, fallback) {
695
+ if (data && typeof data === "object" && !Array.isArray(data)) {
696
+ const maybeMessage = data.message;
697
+ if (typeof maybeMessage === "string" && maybeMessage.trim()) {
698
+ return maybeMessage;
699
+ }
700
+ }
701
+ if (typeof data === "string" && data.trim()) {
702
+ const compact = data.replace(/\s+/g, " ").trim();
703
+ const snippet = compact.length > 180 ? `${compact.slice(0, 180)}...` : compact;
704
+ return `${fallback}. Server returned non-JSON response: ${snippet}`;
705
+ }
706
+ return fallback;
707
+ }
708
+ var RobotRockError;
709
+ var init_http = __esm({
710
+ "src/http.ts"() {
711
+ "use strict";
712
+ RobotRockError = class extends Error {
713
+ constructor(message, statusCode, response) {
714
+ super(message);
715
+ this.statusCode = statusCode;
716
+ this.response = response;
717
+ this.name = "RobotRockError";
718
+ }
719
+ };
720
+ }
721
+ });
722
+
723
+ // ../core/src/types/task.ts
724
+ var init_task2 = __esm({
725
+ "../core/src/types/task.ts"() {
726
+ "use strict";
727
+ }
728
+ });
729
+
730
+ // ../core/src/types/json-schema-infer.ts
731
+ var init_json_schema_infer = __esm({
732
+ "../core/src/types/json-schema-infer.ts"() {
733
+ "use strict";
734
+ }
735
+ });
736
+
737
+ // ../core/src/types/index.ts
738
+ var init_types = __esm({
739
+ "../core/src/types/index.ts"() {
740
+ "use strict";
741
+ init_task2();
742
+ init_json_schema_infer();
743
+ }
744
+ });
745
+
746
+ // ../core/src/plans/index.ts
747
+ var init_plans = __esm({
748
+ "../core/src/plans/index.ts"() {
749
+ "use strict";
750
+ }
751
+ });
752
+
753
+ // ../core/src/handler-retry.ts
754
+ var HANDLER_RETRY_DELAYS_MS, HANDLER_MAX_ATTEMPTS;
755
+ var init_handler_retry = __esm({
756
+ "../core/src/handler-retry.ts"() {
757
+ "use strict";
758
+ HANDLER_RETRY_DELAYS_MS = [
759
+ 6e4,
760
+ // 1m
761
+ 3e5,
762
+ // 5m
763
+ 18e5,
764
+ // 30m
765
+ 36e5,
766
+ // 1h
767
+ 216e5,
768
+ // 6h
769
+ 864e5,
770
+ // 24h
771
+ 1728e5
772
+ // 48h
773
+ ];
774
+ HANDLER_MAX_ATTEMPTS = HANDLER_RETRY_DELAYS_MS.length + 1;
775
+ }
776
+ });
777
+
778
+ // ../core/src/legal.ts
779
+ var init_legal = __esm({
780
+ "../core/src/legal.ts"() {
781
+ "use strict";
782
+ }
783
+ });
784
+
785
+ // ../core/src/welcome-task.ts
786
+ var init_welcome_task = __esm({
787
+ "../core/src/welcome-task.ts"() {
788
+ "use strict";
789
+ }
790
+ });
791
+
792
+ // ../core/src/app-url.ts
793
+ var PRODUCTION_APP_URL, APP_SIGNUP_URL;
794
+ var init_app_url = __esm({
795
+ "../core/src/app-url.ts"() {
796
+ "use strict";
797
+ PRODUCTION_APP_URL = "https://app.robotrock.io";
798
+ APP_SIGNUP_URL = `${PRODUCTION_APP_URL}/sign-up`;
799
+ }
800
+ });
801
+
802
+ // ../core/src/attribution/index.ts
803
+ import { z as z3 } from "zod";
804
+ var ATTRIBUTION_COOKIE_MAX_AGE, attributionSchema;
805
+ var init_attribution = __esm({
806
+ "../core/src/attribution/index.ts"() {
807
+ "use strict";
808
+ init_app_url();
809
+ init_app_url();
810
+ ATTRIBUTION_COOKIE_MAX_AGE = 60 * 60 * 24 * 90;
811
+ attributionSchema = z3.object({
812
+ utmSource: z3.string().optional(),
813
+ utmMedium: z3.string().optional(),
814
+ utmCampaign: z3.string().optional(),
815
+ utmContent: z3.string().optional(),
816
+ utmTerm: z3.string().optional(),
817
+ gclid: z3.string().optional(),
818
+ landingUrl: z3.string().optional(),
819
+ referrer: z3.string().optional(),
820
+ capturedAt: z3.number()
821
+ });
822
+ }
823
+ });
824
+
825
+ // ../core/src/index.ts
826
+ var init_src = __esm({
827
+ "../core/src/index.ts"() {
828
+ "use strict";
829
+ init_schemas();
830
+ init_types();
831
+ init_utils();
832
+ init_plans();
833
+ init_handler_retry();
834
+ init_legal();
835
+ init_welcome_task();
836
+ init_attribution();
837
+ init_app_url();
838
+ }
839
+ });
840
+
841
+ // src/chats.ts
842
+ function createChatsApi(config) {
843
+ const headers = () => ({
844
+ "Content-Type": "application/json",
845
+ "X-Api-Key": config.apiKey
846
+ });
847
+ return {
848
+ async create(input) {
849
+ const bodyPayload = {
850
+ ...input,
851
+ app: input.app ?? config.app,
852
+ messages: input.messages ?? []
853
+ };
854
+ const validation = createAgentChatBodySchema.safeParse(bodyPayload);
855
+ if (!validation.success) {
856
+ throw new RobotRockError(
857
+ `Invalid chat: ${validation.error.issues[0]?.message}`,
858
+ 400,
859
+ validation.error.issues
860
+ );
861
+ }
862
+ const response = await fetch(`${config.baseUrl}/agent-chats`, {
863
+ method: "POST",
864
+ headers: headers(),
865
+ body: JSON.stringify(validation.data)
866
+ });
867
+ const data = await parseResponseBody(response);
868
+ if (!response.ok) {
869
+ throw new RobotRockError(
870
+ getErrorMessage(data, "Failed to create chat"),
871
+ response.status,
872
+ data
873
+ );
874
+ }
875
+ const result = data;
876
+ return { tenantSlug: result.tenantSlug, chats: result.chats };
877
+ },
878
+ async close(chatId, options) {
879
+ if (!chatId) {
880
+ throw new RobotRockError("chatId is required to close a chat", 400);
881
+ }
882
+ const response = await fetch(
883
+ `${config.baseUrl}/agent-chats/${encodeURIComponent(chatId)}/close`,
884
+ {
885
+ method: "POST",
886
+ headers: headers(),
887
+ body: JSON.stringify({ reason: options?.reason })
888
+ }
889
+ );
890
+ if (!response.ok) {
891
+ const data = await parseResponseBody(response);
892
+ throw new RobotRockError(
893
+ getErrorMessage(data, "Failed to close chat"),
894
+ response.status,
895
+ data
896
+ );
897
+ }
898
+ }
899
+ };
900
+ }
901
+ var init_chats = __esm({
902
+ "src/chats.ts"() {
903
+ "use strict";
904
+ init_src();
905
+ init_http();
906
+ }
907
+ });
908
+
621
909
  // src/client.ts
622
910
  function sleep(ms) {
623
911
  return new Promise((resolve) => setTimeout(resolve, ms));
@@ -695,54 +983,17 @@ function normalizeSendToHumanInput(task2, clientDefaults) {
695
983
  actions: normalizedActions
696
984
  };
697
985
  }
698
- async function parseResponseBody(response) {
699
- const contentType = response.headers.get("content-type") ?? "";
700
- const bodyText = await response.text();
701
- if (!bodyText) {
702
- return null;
703
- }
704
- if (contentType.toLowerCase().includes("application/json")) {
705
- try {
706
- return JSON.parse(bodyText);
707
- } catch {
708
- }
709
- }
710
- try {
711
- return JSON.parse(bodyText);
712
- } catch {
713
- return bodyText;
714
- }
715
- }
716
- function getErrorMessage(data, fallback) {
717
- if (data && typeof data === "object" && !Array.isArray(data)) {
718
- const maybeMessage = data.message;
719
- if (typeof maybeMessage === "string" && maybeMessage.trim()) {
720
- return maybeMessage;
721
- }
722
- }
723
- if (typeof data === "string" && data.trim()) {
724
- const compact = data.replace(/\s+/g, " ").trim();
725
- const snippet = compact.length > 180 ? `${compact.slice(0, 180)}...` : compact;
726
- return `${fallback}. Server returned non-JSON response: ${snippet}`;
727
- }
728
- return fallback;
729
- }
730
- var DEFAULT_POLL_INTERVAL_MS, DEFAULT_TIMEOUT_MS, RobotRockError, RobotRock;
986
+ var DEFAULT_POLL_INTERVAL_MS, DEFAULT_TIMEOUT_MS, RobotRock;
731
987
  var init_client = __esm({
732
988
  "src/client.ts"() {
733
989
  "use strict";
734
990
  init_schemas2();
735
991
  init_approval_result();
992
+ init_http();
993
+ init_chats();
994
+ init_http();
736
995
  DEFAULT_POLL_INTERVAL_MS = 2e3;
737
996
  DEFAULT_TIMEOUT_MS = 24 * 60 * 60 * 1e3;
738
- RobotRockError = class extends Error {
739
- constructor(message, statusCode, response) {
740
- super(message);
741
- this.statusCode = statusCode;
742
- this.response = response;
743
- this.name = "RobotRockError";
744
- }
745
- };
746
997
  RobotRock = class {
747
998
  apiKey;
748
999
  baseUrl;
@@ -751,6 +1002,10 @@ var init_client = __esm({
751
1002
  contextVersion;
752
1003
  webhook;
753
1004
  polling;
1005
+ /** Task CRUD: `create`, `get`, `cancel`, `sendUpdate`. */
1006
+ tasks;
1007
+ /** Chat CRUD: `create`, `close`. */
1008
+ chats;
754
1009
  constructor(config) {
755
1010
  if (config.webhook && config.polling) {
756
1011
  throw new Error(
@@ -771,11 +1026,19 @@ var init_client = __esm({
771
1026
  this.contextVersion = config.advanced?.contextVersion ?? TASK_CONTEXT_FORMAT_VERSION2;
772
1027
  this.webhook = config.webhook;
773
1028
  this.polling = config.polling ?? {};
1029
+ this.tasks = {
1030
+ create: (task2) => this.createTaskRequest(task2),
1031
+ get: (taskId) => this.getTaskById(taskId),
1032
+ cancel: (taskId) => this.cancelTaskRequest(taskId),
1033
+ sendUpdate: (input) => this.sendThreadUpdate(input)
1034
+ };
1035
+ this.chats = createChatsApi({
1036
+ baseUrl: this.baseUrl,
1037
+ apiKey: this.apiKey,
1038
+ app: this.app
1039
+ });
774
1040
  }
775
- /**
776
- * Create a task via POST /v1 without waiting for a human response.
777
- */
778
- async createTask(task2) {
1041
+ async createTaskRequest(task2) {
779
1042
  const normalizedTask = normalizeSendToHumanInput(task2, {
780
1043
  webhook: this.webhook,
781
1044
  app: this.app,
@@ -828,7 +1091,7 @@ var init_client = __esm({
828
1091
  contextVersion: this.contextVersion,
829
1092
  agentVersion: this.agentVersion
830
1093
  });
831
- const createdTaskTask = await this.createTask(task2);
1094
+ const createdTaskTask = await this.createTaskRequest(task2);
832
1095
  const hasHandlers = normalizedTask.actions.some(
833
1096
  (action) => Array.isArray(action.handlers) && action.handlers.length > 0
834
1097
  );
@@ -845,7 +1108,7 @@ var init_client = __esm({
845
1108
  const deadline = validUntilMs !== void 0 ? Math.min(pollingDeadline, validUntilMs) : pollingDeadline;
846
1109
  const taskId = createdTaskTask.taskId;
847
1110
  while (Date.now() < deadline) {
848
- const existing = await this.getTask(taskId);
1111
+ const existing = await this.getTaskById(taskId);
849
1112
  if (existing?.status === "handled" && existing.handled) {
850
1113
  return {
851
1114
  mode: "handled",
@@ -867,10 +1130,35 @@ var init_client = __esm({
867
1130
  }
868
1131
  throw new TaskTimeoutError(`No human response within ${timeoutMs}ms`);
869
1132
  }
1133
+ /**
1134
+ * Create a task via POST /v1 without waiting for a human response.
1135
+ * @deprecated Use `client.tasks.create()` instead.
1136
+ */
1137
+ async createTask(task2) {
1138
+ return this.tasks.create(task2);
1139
+ }
870
1140
  /**
871
1141
  * Get a task by public task id (returned as `task.taskId` from {@link sendToHuman}).
1142
+ * @deprecated Use `client.tasks.get()` instead.
872
1143
  */
873
1144
  async getTask(taskId) {
1145
+ return this.tasks.get(taskId);
1146
+ }
1147
+ /**
1148
+ * Log a status update against a thread.
1149
+ * @deprecated Use `client.tasks.sendUpdate()` instead.
1150
+ */
1151
+ async sendUpdate(input) {
1152
+ return this.tasks.sendUpdate(input);
1153
+ }
1154
+ /**
1155
+ * Cancel a task by public task id.
1156
+ * @deprecated Use `client.tasks.cancel()` instead.
1157
+ */
1158
+ async cancelTask(taskId) {
1159
+ return this.tasks.cancel(taskId);
1160
+ }
1161
+ async getTaskById(taskId) {
874
1162
  const response = await fetch(`${this.baseUrl}/tasks/${taskId}`, {
875
1163
  method: "GET",
876
1164
  headers: {
@@ -890,11 +1178,11 @@ var init_client = __esm({
890
1178
  }
891
1179
  return data;
892
1180
  }
893
- /**
894
- * Log a status update against a thread. The update shows in the inbox status
895
- * bar and thread update log for every task in the thread.
896
- */
897
- async sendUpdate({ threadId, message, status }) {
1181
+ async sendThreadUpdate({
1182
+ threadId,
1183
+ message,
1184
+ status
1185
+ }) {
898
1186
  if (!threadId) {
899
1187
  throw new RobotRockError("threadId is required to send an update", 400);
900
1188
  }
@@ -927,7 +1215,7 @@ var init_client = __esm({
927
1215
  }
928
1216
  return data.update;
929
1217
  }
930
- async cancelTask(taskId) {
1218
+ async cancelTaskRequest(taskId) {
931
1219
  const response = await fetch(`${this.baseUrl}/tasks/${taskId}/cancel`, {
932
1220
  method: "POST",
933
1221
  headers: {
@@ -1470,7 +1758,7 @@ async function sendRobotRockUpdate(payload) {
1470
1758
  ...baseConfig.version ? { version: baseConfig.version } : {},
1471
1759
  ...baseConfig.advanced ? { advanced: baseConfig.advanced } : {}
1472
1760
  });
1473
- return client.sendUpdate(update);
1761
+ return client.tasks.sendUpdate(update);
1474
1762
  }
1475
1763
  async function sendUpdateInWorkflow(payload) {
1476
1764
  return sendRobotRockUpdate(payload);
@@ -1496,7 +1784,7 @@ var init_workflow = __esm({
1496
1784
  import { tool } from "ai";
1497
1785
 
1498
1786
  // src/ai/approve-by-human-tool-core.ts
1499
- import { z as z3 } from "zod";
1787
+ import { z as z4 } from "zod";
1500
1788
 
1501
1789
  // src/ai/context.ts
1502
1790
  init_client();
@@ -1572,9 +1860,19 @@ async function sendUpdateForAi(context2, payload) {
1572
1860
  const client = createClient(
1573
1861
  resolveRobotRockConfig(context2.app ? { app: context2.app } : void 0)
1574
1862
  );
1575
- return client.sendUpdate(payload);
1863
+ return client.tasks.sendUpdate(payload);
1864
+ }
1865
+ return context2.client.tasks.sendUpdate(payload);
1866
+ }
1867
+ async function closeChatForAi(context2, payload) {
1868
+ if (context2.mode === "trigger" || context2.mode === "workflow") {
1869
+ const client = createClient(
1870
+ resolveRobotRockConfig(context2.app ? { app: context2.app } : void 0)
1871
+ );
1872
+ await client.chats.close(payload.chatId, { reason: payload.reason });
1873
+ return;
1576
1874
  }
1577
- return context2.client.sendUpdate(payload);
1875
+ await context2.client.chats.close(payload.chatId, { reason: payload.reason });
1578
1876
  }
1579
1877
  async function approveByHumanForAi(context2, payload) {
1580
1878
  if (context2.mode === "trigger") {
@@ -1633,11 +1931,11 @@ function toHumanToolResult(result) {
1633
1931
  }
1634
1932
 
1635
1933
  // src/ai/approve-by-human-tool-core.ts
1636
- var approveByHumanInputSchema = z3.object({
1637
- type: z3.string().optional().describe("Task type slug; defaults to ai-approval"),
1638
- name: z3.string().describe("Short title for the approval request"),
1639
- description: z3.string().describe("What needs approval and the consequences of approving or declining"),
1640
- contextSummary: z3.string().optional().describe("Optional markdown summary shown to the reviewer")
1934
+ var approveByHumanInputSchema = z4.object({
1935
+ type: z4.string().optional().describe("Task type slug; defaults to ai-approval"),
1936
+ name: z4.string().describe("Short title for the approval request"),
1937
+ description: z4.string().describe("What needs approval and the consequences of approving or declining"),
1938
+ contextSummary: z4.string().optional().describe("Optional markdown summary shown to the reviewer")
1641
1939
  });
1642
1940
  function resolveApproveByHumanToolConfig(clientOrContext, maybeOptions = {}) {
1643
1941
  const isDurable = typeof clientOrContext === "object" && clientOrContext !== null && "mode" in clientOrContext && (clientOrContext.mode === "trigger" || clientOrContext.mode === "workflow");
@@ -1687,16 +1985,16 @@ import { tool as tool2 } from "ai";
1687
1985
 
1688
1986
  // src/ai/create-send-to-human-tool-core.ts
1689
1987
  init_schemas2();
1690
- import { z as z4 } from "zod";
1691
- var sendToHumanToolInputSchema = z4.object({
1692
- type: z4.string().describe("Task type slug shown in the RobotRock inbox"),
1693
- name: z4.string().describe("Short title for the human reviewer"),
1694
- description: z4.string().optional().describe("What you need from the human and why you cannot proceed alone"),
1695
- context: z4.object({
1696
- data: z4.record(z4.string(), z4.unknown()).optional(),
1697
- ui: z4.record(z4.string(), z4.unknown()).optional()
1988
+ import { z as z5 } from "zod";
1989
+ var sendToHumanToolInputSchema = z5.object({
1990
+ type: z5.string().describe("Task type slug shown in the RobotRock inbox"),
1991
+ name: z5.string().describe("Short title for the human reviewer"),
1992
+ description: z5.string().optional().describe("What you need from the human and why you cannot proceed alone"),
1993
+ context: z5.object({
1994
+ data: z5.record(z5.string(), z5.unknown()).optional(),
1995
+ ui: z5.record(z5.string(), z5.unknown()).optional()
1698
1996
  }).optional().describe("Optional structured context for the inbox UI"),
1699
- validUntil: z4.string().datetime().optional().describe("Optional ISO deadline for the task"),
1997
+ validUntil: z5.string().datetime().optional().describe("Optional ISO deadline for the task"),
1700
1998
  assignTo: assignToSchema2.optional().describe(
1701
1999
  "Assign to tenant member emails and/or group slugs; narrows who sees the task in the inbox"
1702
2000
  )
@@ -1750,12 +2048,12 @@ import { tool as tool3 } from "ai";
1750
2048
  // src/ai/create-send-update-tool-core.ts
1751
2049
  init_schemas2();
1752
2050
  init_client();
1753
- import { z as z5 } from "zod";
1754
- var sendUpdateToolInputSchema = z5.object({
1755
- threadId: z5.string().optional().describe(
2051
+ import { z as z6 } from "zod";
2052
+ var sendUpdateToolInputSchema = z6.object({
2053
+ threadId: z6.string().optional().describe(
1756
2054
  "Thread to post the update to. Use the `threadId` returned by a prior send-to-human task. Omit only when a session threadId is configured on the tool."
1757
2055
  ),
1758
- message: z5.string().describe("Short progress update (1-2 sentences) shown in the inbox status bar"),
2056
+ message: z6.string().describe("Short progress update (1-2 sentences) shown in the inbox status bar"),
1759
2057
  status: threadUpdateStatusSchema2.optional().describe(
1760
2058
  "Lifecycle status driving the status-bar icon/color: info, queued, running, waiting, succeeded, failed, cancelled"
1761
2059
  )
@@ -1811,6 +2109,101 @@ function createSendUpdateTool(clientOrOptions, maybeOptions = {}) {
1811
2109
  return tool3(buildSendUpdateToolDefinition(clientOrOptions, maybeOptions));
1812
2110
  }
1813
2111
 
2112
+ // src/ai/request-action-input-tool.ts
2113
+ import { tool as tool4 } from "ai";
2114
+
2115
+ // src/ai/request-action-input-tool-core.ts
2116
+ import { z as z7 } from "zod";
2117
+ var requestActionInputActionSchema = z7.object({
2118
+ id: z7.string().min(1).optional().describe(
2119
+ "Stable action identifier echoed in tool output (e.g. pick-blog-topic). Strongly recommended \u2014 auto-derived from title when omitted."
2120
+ ),
2121
+ title: z7.string().min(1),
2122
+ description: z7.string().optional(),
2123
+ schema: z7.record(z7.string(), z7.unknown()).optional().describe("JSON Schema object for the human feedback form"),
2124
+ ui: z7.record(z7.string(), z7.unknown()).optional().describe(
2125
+ 'RJSF ui schema overrides (ui:widget, ui:enumNames, etc.). Use ui:widget "radio" for 2\u20136 discrete choices rendered as tappable cards.'
2126
+ ),
2127
+ data: z7.record(z7.string(), z7.unknown()).optional().describe("Optional default form field values")
2128
+ });
2129
+ var requestActionInputToolOutputSchema = z7.object({
2130
+ actionId: z7.string().min(1),
2131
+ data: z7.record(z7.string(), z7.unknown())
2132
+ });
2133
+ var requestActionInputToolInputSchema = z7.object({
2134
+ prompt: z7.string().optional().describe("Optional heading shown above the action widget in chat"),
2135
+ action: requestActionInputActionSchema.describe(
2136
+ "Action widget config: JSON Schema form fields and optional RJSF ui overrides (same shape as RobotRock inbox actions)"
2137
+ )
2138
+ });
2139
+ function buildRequestActionInputToolDefinition(options = {}) {
2140
+ const description = options.description ?? `Ask the user for structured input via a RobotRock action widget in chat. Always set action.id (e.g. "pick-blog-topic") and action.title. Provide action.schema and optional action.ui using the same JSON Schema and RJSF ui shape as inbox action widgets. Put every field you need in a single call \u2014 never invoke this tool more than once per turn. For 2\u20136 discrete options, use a string enum with ui:widget "radio" and ui:enumNames \u2014 never markdown bullet lists. After the tool returns, act on the user's data immediately; do not ask them to confirm choices they already submitted.`;
2141
+ return {
2142
+ description,
2143
+ inputSchema: requestActionInputToolInputSchema,
2144
+ outputSchema: requestActionInputToolOutputSchema
2145
+ };
2146
+ }
2147
+
2148
+ // src/ai/request-action-input-tool.ts
2149
+ function requestActionInputTool(options = {}) {
2150
+ const definition = buildRequestActionInputToolDefinition(options);
2151
+ return tool4(definition);
2152
+ }
2153
+
2154
+ // src/ai/report-status-tool.ts
2155
+ import { tool as tool5 } from "ai";
2156
+
2157
+ // src/ai/report-status-tool-core.ts
2158
+ import { z as z8 } from "zod";
2159
+ var reportStatusPhaseSchema = z8.enum([
2160
+ "info",
2161
+ "running",
2162
+ "waiting",
2163
+ "succeeded",
2164
+ "failed"
2165
+ ]);
2166
+ var reportStatusToolInputSchema = z8.object({
2167
+ message: z8.string().min(1).describe(
2168
+ "Short progress update shown in chat (1-2 sentences). Use while researching, drafting, or waiting on external work."
2169
+ ),
2170
+ phase: reportStatusPhaseSchema.optional().describe(
2171
+ "Lifecycle phase for the status marker. Defaults to running. Use succeeded or failed when a step finishes."
2172
+ )
2173
+ });
2174
+ var reportStatusToolOutputSchema = z8.object({
2175
+ message: z8.string().min(1),
2176
+ phase: reportStatusPhaseSchema
2177
+ });
2178
+ function normalizeReportStatusToolInput(input) {
2179
+ const parsed = reportStatusToolInputSchema.parse(input);
2180
+ return {
2181
+ message: parsed.message.trim(),
2182
+ phase: parsed.phase
2183
+ };
2184
+ }
2185
+ async function executeReportStatusTool(input) {
2186
+ const normalized = normalizeReportStatusToolInput(input);
2187
+ return {
2188
+ message: normalized.message,
2189
+ phase: normalized.phase ?? "running"
2190
+ };
2191
+ }
2192
+ function buildReportStatusToolDefinition(options = {}) {
2193
+ const description = options.description ?? 'Post a short progress update to the chat UI while you work. Call this before and during long steps (research, drafting, tool runs) so the user sees what is happening. Use phase "running" while in progress, "succeeded" or "failed" when a step completes, and "waiting" when blocked on external input.';
2194
+ return {
2195
+ description,
2196
+ inputSchema: reportStatusToolInputSchema,
2197
+ outputSchema: reportStatusToolOutputSchema,
2198
+ execute: executeReportStatusTool
2199
+ };
2200
+ }
2201
+
2202
+ // src/ai/report-status-tool.ts
2203
+ function reportStatusTool(options = {}) {
2204
+ return tool5(buildReportStatusToolDefinition(options));
2205
+ }
2206
+
1814
2207
  // src/ai/format-tool-approval-task.ts
1815
2208
  function defaultFormatToolApprovalTask(toolCall, options = {}) {
1816
2209
  const approveId = options.approveActionId ?? "approve";
@@ -2063,6 +2456,56 @@ async function runWithRobotRockApprovals(options) {
2063
2456
  throw new Error(`RobotRock approval loop exceeded maxRounds (${maxRounds})`);
2064
2457
  }
2065
2458
 
2459
+ // src/ai/close-chat-tool.ts
2460
+ import { tool as tool6 } from "ai";
2461
+
2462
+ // src/ai/close-chat-tool-core.ts
2463
+ import { z as z9 } from "zod";
2464
+ var closeChatToolInputSchema = z9.object({
2465
+ reason: z9.string().optional().describe(
2466
+ "Short reason the conversation is being closed (recorded on the audit trail)."
2467
+ )
2468
+ });
2469
+ function resolveCloseChatToolConfig(clientOrOptions, maybeOptions) {
2470
+ const isDurable = typeof clientOrOptions === "object" && clientOrOptions !== null && "mode" in clientOrOptions && (clientOrOptions.mode === "trigger" || clientOrOptions.mode === "workflow");
2471
+ const aiContext = normalizeRobotRockAiContext(
2472
+ isDurable ? {
2473
+ mode: clientOrOptions.mode,
2474
+ app: clientOrOptions.app
2475
+ } : clientOrOptions
2476
+ );
2477
+ const options = isDurable ? clientOrOptions : maybeOptions;
2478
+ if (!options?.chatId) {
2479
+ throw new Error(
2480
+ "closeChatTool: `chatId` is required. Pass the chatId of the chat the agent is running in."
2481
+ );
2482
+ }
2483
+ return { aiContext, options };
2484
+ }
2485
+ function buildCloseChatToolDefinition(clientOrOptions, maybeOptions) {
2486
+ const { aiContext, options } = resolveCloseChatToolConfig(
2487
+ clientOrOptions,
2488
+ maybeOptions
2489
+ );
2490
+ const description = options.description ?? "Close the current chat when the conversation is complete and no further human input is needed. Records the close on the audit trail.";
2491
+ return {
2492
+ description,
2493
+ inputSchema: closeChatToolInputSchema,
2494
+ execute: async (input) => {
2495
+ await closeChatForAi(aiContext, {
2496
+ chatId: options.chatId,
2497
+ reason: input.reason
2498
+ });
2499
+ return { closed: true, chatId: options.chatId };
2500
+ }
2501
+ };
2502
+ }
2503
+
2504
+ // src/ai/close-chat-tool.ts
2505
+ function closeChatTool(clientOrOptions, maybeOptions) {
2506
+ return tool6(buildCloseChatToolDefinition(clientOrOptions, maybeOptions));
2507
+ }
2508
+
2066
2509
  // src/ai/create-ai-tools.ts
2067
2510
  function createRobotRockAiTools(options) {
2068
2511
  const mode = options.mode ?? (options.client ? "polling" : "trigger");
@@ -2090,7 +2533,19 @@ function createRobotRockAiTools(options) {
2090
2533
  }) : createSendUpdateTool(pollingClient, {
2091
2534
  ...options.threadId ? { threadId: options.threadId } : {},
2092
2535
  ...toolOptions
2093
- })
2536
+ }),
2537
+ requestActionInput: (toolOptions = {}) => requestActionInputTool(toolOptions),
2538
+ reportStatus: (toolOptions = {}) => reportStatusTool(toolOptions),
2539
+ closeChat: (toolOptions) => {
2540
+ const chatId = toolOptions?.chatId ?? options.chatId;
2541
+ if (!chatId) {
2542
+ throw new Error(
2543
+ "createRobotRockAiTools: closeChat requires `chatId`. Pass it on `createRobotRockAiTools({ chatId })` or `closeChat({ chatId })`."
2544
+ );
2545
+ }
2546
+ const resolvedOptions = { ...toolOptions, chatId };
2547
+ return durableContext ? closeChatTool({ ...durableContext, ...resolvedOptions }) : closeChatTool(pollingClient, resolvedOptions);
2548
+ }
2094
2549
  };
2095
2550
  }
2096
2551
  export {
@@ -2102,6 +2557,8 @@ export {
2102
2557
  createRobotRockToolApproval,
2103
2558
  createSendToHumanTool,
2104
2559
  createSendUpdateTool,
2560
+ reportStatusTool,
2561
+ requestActionInputTool,
2105
2562
  resolveToolApprovalsViaRobotRock,
2106
2563
  runWithRobotRockApprovals
2107
2564
  };