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.
@@ -1,4 +1,55 @@
1
- import { TaskContextFormatVersion, TaskContextInput, AssignToInput, TaskPriority, ThreadUpdateStatus, TaskResponse, DiscriminatedApprovalResult, Task, ThreadUpdate } from './schemas/index.js';
1
+ import { TaskContextInput, AssignToInput as AssignToInput$1, TaskPriority, ThreadUpdateStatus, TaskResponse, Task, ThreadUpdate, TaskContextFormatVersion, DiscriminatedApprovalResult } from './schemas/index.js';
2
+ import { AssignToInput, AgentChatSeedMessage } from '@robotrock/core';
3
+
4
+ /** A chat created via {@link ChatsApi.create}. */
5
+ type CreatedChat = {
6
+ chatId: string;
7
+ sessionId: string;
8
+ userId?: string;
9
+ agentIdentifier: string;
10
+ deepLink: string;
11
+ };
12
+ type CreateChatResult = {
13
+ tenantSlug: string;
14
+ chats: CreatedChat[];
15
+ };
16
+ type CreateChatInput = {
17
+ /** Agent to run the chat. Required unless `parentChatId` is provided. */
18
+ agentIdentifier?: string;
19
+ /** Spawn a chat from an existing chat; inherits its agent/app/owner and links audit. */
20
+ parentChatId?: string;
21
+ /** Trigger connection id to run the chat under (defaults to the tenant's). */
22
+ connectionId?: string;
23
+ /** Inbox app bucket. Overrides the client `app` when set. */
24
+ app?: string;
25
+ /** Assign to tenant users (email) and/or groups (slug). Narrows inbox visibility. */
26
+ assignTo?: AssignToInput;
27
+ /** Chat title shown in the inbox. */
28
+ title: string;
29
+ /** Seed messages to preload the conversation. Defaults to a welcome message. */
30
+ messages?: AgentChatSeedMessage[];
31
+ /** How the chat was created (statistics/audit). @default "api" */
32
+ source?: string;
33
+ };
34
+ type CloseChatOptions = {
35
+ /** Optional human-readable reason recorded on the audit trail. */
36
+ reason?: string;
37
+ };
38
+ interface ChatsApi {
39
+ /** Create one or more agent chats and assign them (task-style). */
40
+ create(input: CreateChatInput): Promise<CreateChatResult>;
41
+ /** Close (archive) a chat by its public chatId — e.g. when an agent is done. */
42
+ close(chatId: string, options?: CloseChatOptions): Promise<void>;
43
+ }
44
+ /**
45
+ * Build the `client.chats` namespace. Mirrors the `client.tasks` surface for the
46
+ * long-lived chat resource, so a single client creates both tasks and chats.
47
+ */
48
+ declare function createChatsApi(config: {
49
+ baseUrl: string;
50
+ apiKey: string;
51
+ app?: string;
52
+ }): ChatsApi;
2
53
 
3
54
  type RobotRockWebhookConfig = {
4
55
  url: string;
@@ -60,7 +111,7 @@ type SendToHumanInput<A extends readonly SendToHumanActionInput[] = readonly Sen
60
111
  /** Optional idempotency key to prevent duplicate tasks */
61
112
  idempotencyKey?: string;
62
113
  /** Assign to tenant users (email) and/or groups (slug). Narrows inbox visibility. */
63
- assignTo?: AssignToInput;
114
+ assignTo?: AssignToInput$1;
64
115
  /**
65
116
  * Groups related tasks together in the inbox. Omit to let the server generate
66
117
  * one (returned as `task.threadId`) and reuse it on later tasks in the thread.
@@ -106,13 +157,22 @@ type SendToHumanResult<A extends readonly SendToHumanActionInput[] = readonly Se
106
157
  mode: "handled";
107
158
  task: TaskResponse["task"];
108
159
  } & DiscriminatedApprovalResult<A>);
109
- declare class RobotRockError extends Error {
110
- readonly statusCode: number;
111
- readonly response?: unknown | undefined;
112
- constructor(message: string, statusCode: number, response?: unknown | undefined);
160
+
161
+ /** Task CRUD surface, exposed as `client.tasks`. */
162
+ interface TasksApi {
163
+ /** Create a task via POST /v1 without waiting for a human response. */
164
+ create<const A extends readonly SendToHumanActionInput[]>(task: SendToHumanWithAppInput<A>): Promise<TaskResponse["task"]>;
165
+ /** Get a task by public task id. */
166
+ get(taskId: string): Promise<Task | null>;
167
+ /** Cancel a task by public task id. */
168
+ cancel(taskId: string): Promise<void>;
169
+ /** Log a status update against a thread. */
170
+ sendUpdate(input: SendUpdateInput): Promise<ThreadUpdate>;
113
171
  }
114
172
  /**
115
- * RobotRock API client for creating and querying human-in-the-loop tasks.
173
+ * RobotRock API client for creating and querying human-in-the-loop tasks and
174
+ * chats. CRUD lives under `client.tasks` and `client.chats`; the headline
175
+ * `sendToHuman` verb stays top-level.
116
176
  */
117
177
  declare class RobotRock {
118
178
  private readonly apiKey;
@@ -122,24 +182,38 @@ declare class RobotRock {
122
182
  private readonly contextVersion;
123
183
  private readonly webhook?;
124
184
  private readonly polling;
185
+ /** Task CRUD: `create`, `get`, `cancel`, `sendUpdate`. */
186
+ readonly tasks: TasksApi;
187
+ /** Chat CRUD: `create`, `close`. */
188
+ readonly chats: ChatsApi;
125
189
  constructor(config: RobotRockConfig);
190
+ private createTaskRequest;
191
+ sendToHuman<const A extends readonly SendToHumanActionInput[]>(task: SendToHumanWithAppInput<A>): Promise<SendToHumanResult<A>>;
126
192
  /**
127
193
  * Create a task via POST /v1 without waiting for a human response.
194
+ * @deprecated Use `client.tasks.create()` instead.
128
195
  */
129
196
  createTask<const A extends readonly SendToHumanActionInput[]>(task: SendToHumanWithAppInput<A>): Promise<TaskResponse["task"]>;
130
- sendToHuman<const A extends readonly SendToHumanActionInput[]>(task: SendToHumanWithAppInput<A>): Promise<SendToHumanResult<A>>;
131
197
  /**
132
198
  * Get a task by public task id (returned as `task.taskId` from {@link sendToHuman}).
199
+ * @deprecated Use `client.tasks.get()` instead.
133
200
  */
134
201
  getTask(taskId: string): Promise<Task | null>;
135
202
  /**
136
- * Log a status update against a thread. The update shows in the inbox status
137
- * bar and thread update log for every task in the thread.
203
+ * Log a status update against a thread.
204
+ * @deprecated Use `client.tasks.sendUpdate()` instead.
205
+ */
206
+ sendUpdate(input: SendUpdateInput): Promise<ThreadUpdate>;
207
+ /**
208
+ * Cancel a task by public task id.
209
+ * @deprecated Use `client.tasks.cancel()` instead.
138
210
  */
139
- sendUpdate({ threadId, message, status }: SendUpdateInput): Promise<ThreadUpdate>;
140
211
  cancelTask(taskId: string): Promise<void>;
212
+ private getTaskById;
213
+ private sendThreadUpdate;
214
+ private cancelTaskRequest;
141
215
  }
142
216
  declare function createClient(config: RobotRockConfig): RobotRock;
143
217
  declare function attachWebhookToActions(actions: readonly SendToHumanActionInput[], webhook: RobotRockWebhookConfig): TaskContextInput["actions"];
144
218
 
145
- export { RobotRock as R, type SendToHumanActionInput as S, type SendToHumanInput as a, type RobotRockConfig as b, type RobotRockAdvancedConfig as c, RobotRockError as d, type RobotRockPollingClientConfig as e, type RobotRockPollingOptions as f, type RobotRockWebhookClientConfig as g, type RobotRockWebhookConfig as h, type SendToHumanResult as i, type SendToHumanValidUntil as j, type SendUpdateInput as k, attachWebhookToActions as l, createClient as m };
219
+ export { type ChatsApi as C, RobotRock as R, type SendToHumanActionInput as S, type TasksApi as T, type SendToHumanInput as a, type RobotRockConfig as b, type CloseChatOptions as c, type CreateChatInput as d, type CreateChatResult as e, type CreatedChat as f, type RobotRockAdvancedConfig as g, type RobotRockPollingClientConfig as h, type RobotRockPollingOptions as i, type RobotRockWebhookClientConfig as j, type RobotRockWebhookConfig as k, type SendToHumanResult as l, type SendToHumanValidUntil as m, type SendUpdateInput as n, attachWebhookToActions as o, createChatsApi as p, createClient as q };
package/dist/index.d.ts CHANGED
@@ -1,11 +1,18 @@
1
- import { R as RobotRock, b as RobotRockConfig } from './client-Cy7YLxms.js';
2
- export { c as RobotRockAdvancedConfig, d as RobotRockError, e as RobotRockPollingClientConfig, f as RobotRockPollingOptions, g as RobotRockWebhookClientConfig, h as RobotRockWebhookConfig, S as SendToHumanActionInput, a as SendToHumanInput, i as SendToHumanResult, j as SendToHumanValidUntil, k as SendUpdateInput, l as attachWebhookToActions, m as createClient } from './client-Cy7YLxms.js';
1
+ import { R as RobotRock, b as RobotRockConfig } from './client-CzVmjXpz.js';
2
+ export { C as ChatsApi, c as CloseChatOptions, d as CreateChatInput, e as CreateChatResult, f as CreatedChat, g as RobotRockAdvancedConfig, h as RobotRockPollingClientConfig, i as RobotRockPollingOptions, j as RobotRockWebhookClientConfig, k as RobotRockWebhookConfig, S as SendToHumanActionInput, a as SendToHumanInput, l as SendToHumanResult, m as SendToHumanValidUntil, n as SendUpdateInput, T as TasksApi, o as attachWebhookToActions, p as createChatsApi, q as createClient } from './client-CzVmjXpz.js';
3
+ export { AgentChatSeedMessage, CreateAgentChatBody, CreateAgentChatBodyInput, agentChatSeedMessageSchema, createAgentChatBodySchema } from '@robotrock/core';
3
4
  import { Task, DiscriminatedApprovalResult } from './schemas/index.js';
4
5
  export { AgentTelemetry, AgentTelemetryInput, ApprovalResult, AssignToInput, CreateTaskBody, CreateTaskBodyInput, DEFAULT_TASK_PRIORITY, DEFAULT_THREAD_UPDATE_STATUS, Handler, InferActionData, LOWEST_TASK_PRIORITY, TASK_CONTEXT_FORMAT_VERSION, TASK_PRIORITY_RANK, TaskAction, TaskContext, TaskContextFormatVersion, TaskContextInput, TaskPriority, TaskResponse, TaskResult, TaskStatus, ThreadUpdate, ThreadUpdateBody, ThreadUpdateBodyInput, ThreadUpdateInput, ThreadUpdateResponse, ThreadUpdateSource, ThreadUpdateStatus, TriggerHandler, TupleElementIndices, WebhookHandler, agentTelemetrySchema, assignToSchema, createTaskBodySchema, taskContextSchema, taskPriorities, taskPrioritySchema, threadUpdateBodySchema, threadUpdateInputSchema, threadUpdateStatusSchema, threadUpdateStatuses } from './schemas/index.js';
5
6
  import { z } from 'zod';
6
7
  export { E as EndRobotRockHumanWaitSpanOptions, b as RobotRockCreatedTask, c as RobotRockHandledOtelInput, a as RobotRockHandlerWebhookPayload, d as RobotRockHumanWaitOtelSession, e as RobotRockOtelHandle, f as RobotRockOtelRecordOptions, R as RobotRockPlatformOtelFields, g as beginRobotRockHumanWaitOtel, h as captureRobotRockOtelHandle, i as endRobotRockHumanWaitSpan, j as finishRobotRockHumanWaitOtel, r as recordRobotRockHandledToOtel, s as shouldRecordRobotRockOtel, k as startRobotRockHumanWaitSpan, l as stripPlatformOtelFields, t as toRobotRockHandledOtelInput } from './otel-platform-DzHyHkGk.js';
7
8
  import '@opentelemetry/api';
8
9
 
10
+ declare class RobotRockError extends Error {
11
+ readonly statusCode: number;
12
+ readonly response?: unknown | undefined;
13
+ constructor(message: string, statusCode: number, response?: unknown | undefined);
14
+ }
15
+
9
16
  /**
10
17
  * Read RobotRock client config from environment variables.
11
18
  *
@@ -138,4 +145,4 @@ interface VerifyRobotRockWebhookOptions {
138
145
  */
139
146
  declare function verifyRobotRockWebhook(request: Request, options?: VerifyRobotRockWebhookOptions): Promise<RobotRockWebhookPayload>;
140
147
 
141
- export { DiscriminatedApprovalResult, type HandledActionInput, type HandledOutcome, PLATFORM_MARK_DONE_ACTION_ID, PLATFORM_MARK_DONE_ACTION_TITLE, PLATFORM_REJECT_REQUEST_ACTION_ID, PLATFORM_REJECT_REQUEST_ACTION_TITLE, PLATFORM_TERMINAL_ACTION_IDS, type PlatformMarkDoneOutcome, type PlatformRejectRequestData, PlatformRejectRequestError, type PlatformRejectRequestOutcome, type PlatformTerminalActionId, RobotRock, RobotRockConfig, RobotRockWebhookError, type RobotRockWebhookErrorCode, type RobotRockWebhookPayload, Task, type TaskActionOutcome, TaskExpiredError, TaskTimeoutError, type VerifyRobotRockWebhookOptions, assertNotPlatformRejectRequest, isPlatformMarkDoneAction, isPlatformRejectRequestAction, isPlatformTerminalAction, parseHandledOutcome, parsePlatformRejectRequestData, resolveRobotRockClient, resolveRobotRockConfig, shouldStopAgentForHandledAction, toDiscriminatedApprovalResult, verifyRobotRockWebhook };
148
+ export { DiscriminatedApprovalResult, type HandledActionInput, type HandledOutcome, PLATFORM_MARK_DONE_ACTION_ID, PLATFORM_MARK_DONE_ACTION_TITLE, PLATFORM_REJECT_REQUEST_ACTION_ID, PLATFORM_REJECT_REQUEST_ACTION_TITLE, PLATFORM_TERMINAL_ACTION_IDS, type PlatformMarkDoneOutcome, type PlatformRejectRequestData, PlatformRejectRequestError, type PlatformRejectRequestOutcome, type PlatformTerminalActionId, RobotRock, RobotRockConfig, RobotRockError, RobotRockWebhookError, type RobotRockWebhookErrorCode, type RobotRockWebhookPayload, Task, type TaskActionOutcome, TaskExpiredError, TaskTimeoutError, type VerifyRobotRockWebhookOptions, assertNotPlatformRejectRequest, isPlatformMarkDoneAction, isPlatformRejectRequestAction, isPlatformTerminalAction, parseHandledOutcome, parsePlatformRejectRequestData, resolveRobotRockClient, resolveRobotRockConfig, shouldStopAgentForHandledAction, toDiscriminatedApprovalResult, verifyRobotRockWebhook };
package/dist/index.js CHANGED
@@ -202,13 +202,15 @@ var triggerHandlerSchema = webhookHandlerSchema.extend({
202
202
  tokenId: z.string().min(1)
203
203
  });
204
204
  var handlerSchema = z.discriminatedUnion("type", [webhookHandlerSchema, triggerHandlerSchema]);
205
- var taskActionSchema = z.object({
205
+ var taskActionInputSchema = z.object({
206
206
  id: z.string().min(1),
207
207
  title: z.string().min(1),
208
208
  description: z.string().optional(),
209
209
  schema: jsonSchema7Schema.optional(),
210
210
  ui: uiSchemaSchema.optional(),
211
- data: z.record(z.string(), z.unknown()).optional(),
211
+ data: z.record(z.string(), z.unknown()).optional()
212
+ });
213
+ var taskActionSchema = taskActionInputSchema.extend({
212
214
  // Optional handlers for this action - if present, must have at least 1
213
215
  handlers: z.array(handlerSchema).min(1).optional()
214
216
  });
@@ -316,6 +318,56 @@ var createTaskBodySchema = taskContextObjectBaseSchema.extend({
316
318
  /** Agent release version — not shown in inbox UI. */
317
319
  agent: agentTelemetrySchema.optional()
318
320
  }).transform(normalizeTaskContextVersion).superRefine(refineContextPublicUrls);
321
+ var agentChatSeedActionSchema = z.object({
322
+ /** Stable action id echoed back on submit; auto-derived from title when omitted. */
323
+ id: z.string().min(1).optional(),
324
+ title: z.string().min(1),
325
+ description: z.string().optional(),
326
+ /** JSON Schema object for the form fields. */
327
+ schema: z.record(z.string(), z.unknown()).optional(),
328
+ /** RJSF ui schema overrides (ui:widget, ui:enumNames, etc.). */
329
+ ui: z.record(z.string(), z.unknown()).optional(),
330
+ /** Optional default field values. */
331
+ data: z.record(z.string(), z.unknown()).optional()
332
+ });
333
+ var agentChatSeedMessageSchema = z.object({
334
+ role: z.enum(["user", "assistant"]),
335
+ text: z.string().min(1),
336
+ /** Optional display-name override for the message sender. */
337
+ senderName: z.string().min(1).optional(),
338
+ /**
339
+ * Optional structured input request rendered as a styled RobotRock action
340
+ * widget below the message text. Use this instead of asking the user to reply
341
+ * in plain text so the request is always a proper action form.
342
+ */
343
+ requestActionInput: z.object({
344
+ prompt: z.string().optional(),
345
+ action: agentChatSeedActionSchema
346
+ }).optional()
347
+ });
348
+ var createAgentChatBodySchema = z.object({
349
+ /** Trigger chat agent id (task slug). Required unless `parentChatId` is set. */
350
+ agentIdentifier: z.string().min(1).optional(),
351
+ /** Inherit tenant/connection/agent from an existing chat (agent-spawned chats). */
352
+ parentChatId: z.string().min(1).optional(),
353
+ /** Convex tenantTriggerConnections id; resolved from the tenant when omitted. */
354
+ connectionId: z.string().min(1).optional(),
355
+ /** Source application id; groups the chat under an inbox section. */
356
+ app: z.string().min(1).optional(),
357
+ assignTo: assignToSchema.optional(),
358
+ title: z.string().min(1),
359
+ messages: z.array(agentChatSeedMessageSchema).default([]),
360
+ /** Provenance label stored on the session ("cron" | "agent" | ...). */
361
+ source: z.string().min(1).optional()
362
+ }).refine((data) => Boolean(data.agentIdentifier || data.parentChatId), {
363
+ message: "Provide either agentIdentifier or parentChatId"
364
+ });
365
+ var agentChatTransportBodySchema = z.object({
366
+ chatId: z.string().min(1),
367
+ publicAccessToken: z.string().min(1),
368
+ lastEventId: z.string().optional(),
369
+ isStreaming: z.boolean().optional()
370
+ });
319
371
 
320
372
  // src/schemas/index.ts
321
373
  var safeUrlSchema2 = z2.string().refine((url) => isPublicHttpUrl(url), {
@@ -338,13 +390,15 @@ var triggerHandlerSchema2 = webhookHandlerSchema2.extend({
338
390
  tokenId: z2.string().min(1)
339
391
  });
340
392
  var handlerSchema2 = z2.discriminatedUnion("type", [webhookHandlerSchema2, triggerHandlerSchema2]);
341
- var taskActionSchema2 = z2.object({
393
+ var taskActionInputSchema2 = z2.object({
342
394
  id: z2.string().min(1),
343
395
  title: z2.string().min(1),
344
396
  description: z2.string().optional(),
345
397
  schema: jsonSchema7Schema2.optional(),
346
398
  ui: uiSchemaSchema2.optional(),
347
- data: z2.record(z2.string(), z2.unknown()).optional(),
399
+ data: z2.record(z2.string(), z2.unknown()).optional()
400
+ });
401
+ var taskActionSchema2 = taskActionInputSchema2.extend({
348
402
  handlers: z2.array(handlerSchema2).min(1).optional()
349
403
  });
350
404
  var uiFieldSchemaSchema2 = z2.object({
@@ -485,6 +539,149 @@ function toDiscriminatedApprovalResult(actions, task) {
485
539
  };
486
540
  }
487
541
 
542
+ // src/http.ts
543
+ var RobotRockError = class extends Error {
544
+ constructor(message, statusCode, response) {
545
+ super(message);
546
+ this.statusCode = statusCode;
547
+ this.response = response;
548
+ this.name = "RobotRockError";
549
+ }
550
+ };
551
+ async function parseResponseBody(response) {
552
+ const contentType = response.headers.get("content-type") ?? "";
553
+ const bodyText = await response.text();
554
+ if (!bodyText) {
555
+ return null;
556
+ }
557
+ if (contentType.toLowerCase().includes("application/json")) {
558
+ try {
559
+ return JSON.parse(bodyText);
560
+ } catch {
561
+ }
562
+ }
563
+ try {
564
+ return JSON.parse(bodyText);
565
+ } catch {
566
+ return bodyText;
567
+ }
568
+ }
569
+ function getErrorMessage(data, fallback) {
570
+ if (data && typeof data === "object" && !Array.isArray(data)) {
571
+ const maybeMessage = data.message;
572
+ if (typeof maybeMessage === "string" && maybeMessage.trim()) {
573
+ return maybeMessage;
574
+ }
575
+ }
576
+ if (typeof data === "string" && data.trim()) {
577
+ const compact = data.replace(/\s+/g, " ").trim();
578
+ const snippet = compact.length > 180 ? `${compact.slice(0, 180)}...` : compact;
579
+ return `${fallback}. Server returned non-JSON response: ${snippet}`;
580
+ }
581
+ return fallback;
582
+ }
583
+
584
+ // ../core/src/handler-retry.ts
585
+ var HANDLER_RETRY_DELAYS_MS = [
586
+ 6e4,
587
+ // 1m
588
+ 3e5,
589
+ // 5m
590
+ 18e5,
591
+ // 30m
592
+ 36e5,
593
+ // 1h
594
+ 216e5,
595
+ // 6h
596
+ 864e5,
597
+ // 24h
598
+ 1728e5
599
+ // 48h
600
+ ];
601
+ var HANDLER_MAX_ATTEMPTS = HANDLER_RETRY_DELAYS_MS.length + 1;
602
+
603
+ // ../core/src/attribution/index.ts
604
+ import { z as z3 } from "zod";
605
+
606
+ // ../core/src/app-url.ts
607
+ var PRODUCTION_APP_URL = "https://app.robotrock.io";
608
+ var APP_SIGNUP_URL = `${PRODUCTION_APP_URL}/sign-up`;
609
+
610
+ // ../core/src/attribution/index.ts
611
+ var ATTRIBUTION_COOKIE_MAX_AGE = 60 * 60 * 24 * 90;
612
+ var attributionSchema = z3.object({
613
+ utmSource: z3.string().optional(),
614
+ utmMedium: z3.string().optional(),
615
+ utmCampaign: z3.string().optional(),
616
+ utmContent: z3.string().optional(),
617
+ utmTerm: z3.string().optional(),
618
+ gclid: z3.string().optional(),
619
+ landingUrl: z3.string().optional(),
620
+ referrer: z3.string().optional(),
621
+ capturedAt: z3.number()
622
+ });
623
+
624
+ // src/chats.ts
625
+ function createChatsApi(config) {
626
+ const headers = () => ({
627
+ "Content-Type": "application/json",
628
+ "X-Api-Key": config.apiKey
629
+ });
630
+ return {
631
+ async create(input) {
632
+ const bodyPayload = {
633
+ ...input,
634
+ app: input.app ?? config.app,
635
+ messages: input.messages ?? []
636
+ };
637
+ const validation = createAgentChatBodySchema.safeParse(bodyPayload);
638
+ if (!validation.success) {
639
+ throw new RobotRockError(
640
+ `Invalid chat: ${validation.error.issues[0]?.message}`,
641
+ 400,
642
+ validation.error.issues
643
+ );
644
+ }
645
+ const response = await fetch(`${config.baseUrl}/agent-chats`, {
646
+ method: "POST",
647
+ headers: headers(),
648
+ body: JSON.stringify(validation.data)
649
+ });
650
+ const data = await parseResponseBody(response);
651
+ if (!response.ok) {
652
+ throw new RobotRockError(
653
+ getErrorMessage(data, "Failed to create chat"),
654
+ response.status,
655
+ data
656
+ );
657
+ }
658
+ const result = data;
659
+ return { tenantSlug: result.tenantSlug, chats: result.chats };
660
+ },
661
+ async close(chatId, options) {
662
+ if (!chatId) {
663
+ throw new RobotRockError("chatId is required to close a chat", 400);
664
+ }
665
+ const response = await fetch(
666
+ `${config.baseUrl}/agent-chats/${encodeURIComponent(chatId)}/close`,
667
+ {
668
+ method: "POST",
669
+ headers: headers(),
670
+ body: JSON.stringify({ reason: options?.reason })
671
+ }
672
+ );
673
+ if (!response.ok) {
674
+ const data = await parseResponseBody(response);
675
+ throw new RobotRockError(
676
+ getErrorMessage(data, "Failed to close chat"),
677
+ response.status,
678
+ data
679
+ );
680
+ }
681
+ }
682
+ };
683
+ }
684
+
488
685
  // src/client.ts
489
686
  var DEFAULT_POLL_INTERVAL_MS = 2e3;
490
687
  var DEFAULT_TIMEOUT_MS = 24 * 60 * 60 * 1e3;
@@ -522,14 +719,6 @@ function serializeValidUntil(value) {
522
719
  }
523
720
  throw new RobotRockError("Invalid validUntil: expected a Date or parseable date string", 400);
524
721
  }
525
- var RobotRockError = class extends Error {
526
- constructor(message, statusCode, response) {
527
- super(message);
528
- this.statusCode = statusCode;
529
- this.response = response;
530
- this.name = "RobotRockError";
531
- }
532
- };
533
722
  var RobotRock = class {
534
723
  apiKey;
535
724
  baseUrl;
@@ -538,6 +727,10 @@ var RobotRock = class {
538
727
  contextVersion;
539
728
  webhook;
540
729
  polling;
730
+ /** Task CRUD: `create`, `get`, `cancel`, `sendUpdate`. */
731
+ tasks;
732
+ /** Chat CRUD: `create`, `close`. */
733
+ chats;
541
734
  constructor(config) {
542
735
  if (config.webhook && config.polling) {
543
736
  throw new Error(
@@ -558,11 +751,19 @@ var RobotRock = class {
558
751
  this.contextVersion = config.advanced?.contextVersion ?? TASK_CONTEXT_FORMAT_VERSION2;
559
752
  this.webhook = config.webhook;
560
753
  this.polling = config.polling ?? {};
754
+ this.tasks = {
755
+ create: (task) => this.createTaskRequest(task),
756
+ get: (taskId) => this.getTaskById(taskId),
757
+ cancel: (taskId) => this.cancelTaskRequest(taskId),
758
+ sendUpdate: (input) => this.sendThreadUpdate(input)
759
+ };
760
+ this.chats = createChatsApi({
761
+ baseUrl: this.baseUrl,
762
+ apiKey: this.apiKey,
763
+ app: this.app
764
+ });
561
765
  }
562
- /**
563
- * Create a task via POST /v1 without waiting for a human response.
564
- */
565
- async createTask(task) {
766
+ async createTaskRequest(task) {
566
767
  const normalizedTask = normalizeSendToHumanInput(task, {
567
768
  webhook: this.webhook,
568
769
  app: this.app,
@@ -615,7 +816,7 @@ var RobotRock = class {
615
816
  contextVersion: this.contextVersion,
616
817
  agentVersion: this.agentVersion
617
818
  });
618
- const createdTaskTask = await this.createTask(task);
819
+ const createdTaskTask = await this.createTaskRequest(task);
619
820
  const hasHandlers = normalizedTask.actions.some(
620
821
  (action) => Array.isArray(action.handlers) && action.handlers.length > 0
621
822
  );
@@ -632,7 +833,7 @@ var RobotRock = class {
632
833
  const deadline = validUntilMs !== void 0 ? Math.min(pollingDeadline, validUntilMs) : pollingDeadline;
633
834
  const taskId = createdTaskTask.taskId;
634
835
  while (Date.now() < deadline) {
635
- const existing = await this.getTask(taskId);
836
+ const existing = await this.getTaskById(taskId);
636
837
  if (existing?.status === "handled" && existing.handled) {
637
838
  return {
638
839
  mode: "handled",
@@ -654,10 +855,35 @@ var RobotRock = class {
654
855
  }
655
856
  throw new TaskTimeoutError(`No human response within ${timeoutMs}ms`);
656
857
  }
858
+ /**
859
+ * Create a task via POST /v1 without waiting for a human response.
860
+ * @deprecated Use `client.tasks.create()` instead.
861
+ */
862
+ async createTask(task) {
863
+ return this.tasks.create(task);
864
+ }
657
865
  /**
658
866
  * Get a task by public task id (returned as `task.taskId` from {@link sendToHuman}).
867
+ * @deprecated Use `client.tasks.get()` instead.
659
868
  */
660
869
  async getTask(taskId) {
870
+ return this.tasks.get(taskId);
871
+ }
872
+ /**
873
+ * Log a status update against a thread.
874
+ * @deprecated Use `client.tasks.sendUpdate()` instead.
875
+ */
876
+ async sendUpdate(input) {
877
+ return this.tasks.sendUpdate(input);
878
+ }
879
+ /**
880
+ * Cancel a task by public task id.
881
+ * @deprecated Use `client.tasks.cancel()` instead.
882
+ */
883
+ async cancelTask(taskId) {
884
+ return this.tasks.cancel(taskId);
885
+ }
886
+ async getTaskById(taskId) {
661
887
  const response = await fetch(`${this.baseUrl}/tasks/${taskId}`, {
662
888
  method: "GET",
663
889
  headers: {
@@ -677,11 +903,11 @@ var RobotRock = class {
677
903
  }
678
904
  return data;
679
905
  }
680
- /**
681
- * Log a status update against a thread. The update shows in the inbox status
682
- * bar and thread update log for every task in the thread.
683
- */
684
- async sendUpdate({ threadId, message, status }) {
906
+ async sendThreadUpdate({
907
+ threadId,
908
+ message,
909
+ status
910
+ }) {
685
911
  if (!threadId) {
686
912
  throw new RobotRockError("threadId is required to send an update", 400);
687
913
  }
@@ -714,7 +940,7 @@ var RobotRock = class {
714
940
  }
715
941
  return data.update;
716
942
  }
717
- async cancelTask(taskId) {
943
+ async cancelTaskRequest(taskId) {
718
944
  const response = await fetch(`${this.baseUrl}/tasks/${taskId}/cancel`, {
719
945
  method: "POST",
720
946
  headers: {
@@ -773,38 +999,6 @@ function normalizeSendToHumanInput(task, clientDefaults) {
773
999
  actions: normalizedActions
774
1000
  };
775
1001
  }
776
- async function parseResponseBody(response) {
777
- const contentType = response.headers.get("content-type") ?? "";
778
- const bodyText = await response.text();
779
- if (!bodyText) {
780
- return null;
781
- }
782
- if (contentType.toLowerCase().includes("application/json")) {
783
- try {
784
- return JSON.parse(bodyText);
785
- } catch {
786
- }
787
- }
788
- try {
789
- return JSON.parse(bodyText);
790
- } catch {
791
- return bodyText;
792
- }
793
- }
794
- function getErrorMessage(data, fallback) {
795
- if (data && typeof data === "object" && !Array.isArray(data)) {
796
- const maybeMessage = data.message;
797
- if (typeof maybeMessage === "string" && maybeMessage.trim()) {
798
- return maybeMessage;
799
- }
800
- }
801
- if (typeof data === "string" && data.trim()) {
802
- const compact = data.replace(/\s+/g, " ").trim();
803
- const snippet = compact.length > 180 ? `${compact.slice(0, 180)}...` : compact;
804
- return `${fallback}. Server returned non-JSON response: ${snippet}`;
805
- }
806
- return fallback;
807
- }
808
1002
 
809
1003
  // src/env.ts
810
1004
  var DEFAULT_BASE_URL = "https://api.robotrock.io/v1";
@@ -919,18 +1113,18 @@ function shouldStopAgentForHandledAction(actionId) {
919
1113
 
920
1114
  // src/webhook.ts
921
1115
  import { createHmac, timingSafeEqual } from "crypto";
922
- import { z as z3 } from "zod";
1116
+ import { z as z4 } from "zod";
923
1117
  var ROBOTROCK_SIGNATURE_HEADER = "x-robotrock-signature";
924
- var robotRockWebhookPayloadBodySchema = z3.object({
925
- taskId: z3.string().min(1),
926
- action: z3.object({
927
- id: z3.string().min(1),
928
- title: z3.string().min(1),
929
- data: z3.unknown()
1118
+ var robotRockWebhookPayloadBodySchema = z4.object({
1119
+ taskId: z4.string().min(1),
1120
+ action: z4.object({
1121
+ id: z4.string().min(1),
1122
+ title: z4.string().min(1),
1123
+ data: z4.unknown()
930
1124
  }),
931
- handledBy: z3.string().min(1).optional(),
932
- handledAt: z3.string().min(1),
933
- handlerType: z3.string().min(1)
1125
+ handledBy: z4.string().min(1).optional(),
1126
+ handledAt: z4.string().min(1),
1127
+ handlerType: z4.string().min(1)
934
1128
  });
935
1129
  var RobotRockWebhookError = class extends Error {
936
1130
  constructor(message, code, details) {
@@ -1230,12 +1424,15 @@ export {
1230
1424
  TASK_PRIORITY_RANK,
1231
1425
  TaskExpiredError,
1232
1426
  TaskTimeoutError,
1427
+ agentChatSeedMessageSchema,
1233
1428
  agentTelemetrySchema2 as agentTelemetrySchema,
1234
1429
  assertNotPlatformRejectRequest,
1235
1430
  assignToSchema2 as assignToSchema,
1236
1431
  attachWebhookToActions,
1237
1432
  beginRobotRockHumanWaitOtel,
1238
1433
  captureRobotRockOtelHandle,
1434
+ createAgentChatBodySchema,
1435
+ createChatsApi,
1239
1436
  createClient,
1240
1437
  createTaskBodySchema2 as createTaskBodySchema,
1241
1438
  endRobotRockHumanWaitSpan,