robotrock 0.8.5 → 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.
@@ -259,12 +259,12 @@ function validateUrlValue(value, widget) {
259
259
  }
260
260
  return null;
261
261
  }
262
- function validateContextPublicUrls(context) {
263
- if (!context?.data) {
262
+ function validateContextPublicUrls(context2) {
263
+ if (!context2?.data) {
264
264
  return null;
265
265
  }
266
- for (const [field, value] of Object.entries(context.data)) {
267
- const widget = widgetType(context.ui, field);
266
+ for (const [field, value] of Object.entries(context2.data)) {
267
+ const widget = widgetType(context2.ui, field);
268
268
  if (!widget) {
269
269
  continue;
270
270
  }
@@ -284,6 +284,13 @@ var init_context_urls = __esm({
284
284
 
285
285
  // ../core/src/schemas/task.ts
286
286
  import { z } from "zod";
287
+ function normalizeTaskContextVersion(data) {
288
+ const { version: legacyVersion, contextVersion, ...rest } = data;
289
+ return {
290
+ ...rest,
291
+ contextVersion: contextVersion ?? legacyVersion ?? TASK_CONTEXT_FORMAT_VERSION
292
+ };
293
+ }
287
294
  function refineContextPublicUrls(data, ctx) {
288
295
  const error = validateContextPublicUrls(data.context);
289
296
  if (error) {
@@ -294,7 +301,7 @@ function refineContextPublicUrls(data, ctx) {
294
301
  });
295
302
  }
296
303
  }
297
- var safeUrlSchema, jsonSchema7Schema, uiSchemaSchema, webhookHandlerSchema, triggerHandlerSchema, handlerSchema, taskActionSchema, uiFieldSchemaSchema, contextUiSchema, contextDataSchema, taskContextObjectSchema, taskContextSchema, assignToSchema, threadUpdateMessageSchema, threadUpdateStatuses, threadUpdateStatusSchema, threadUpdateInputSchema, taskPriorities, taskPrioritySchema, nonNegativeInt, agentCostTokensSchema, agentCostSchema, AGENT_INFO_MAX_KEYS, AGENT_TOOL_CALLS_MAX_KEYS, AGENT_OTEL_SPANS_MAX, agentToolCallsSchema, agentOtelSpanStatusSchema, agentOtelSpanSummarySchema, agentOtelSchema, 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;
298
305
  var init_task = __esm({
299
306
  "../core/src/schemas/task.ts"() {
300
307
  "use strict";
@@ -320,13 +327,15 @@ var init_task = __esm({
320
327
  tokenId: z.string().min(1)
321
328
  });
322
329
  handlerSchema = z.discriminatedUnion("type", [webhookHandlerSchema, triggerHandlerSchema]);
323
- taskActionSchema = z.object({
330
+ taskActionInputSchema = z.object({
324
331
  id: z.string().min(1),
325
332
  title: z.string().min(1),
326
333
  description: z.string().optional(),
327
334
  schema: jsonSchema7Schema.optional(),
328
335
  ui: uiSchemaSchema.optional(),
329
- data: z.record(z.string(), z.unknown()).optional(),
336
+ data: z.record(z.string(), z.unknown()).optional()
337
+ });
338
+ taskActionSchema = taskActionInputSchema.extend({
330
339
  // Optional handlers for this action - if present, must have at least 1
331
340
  handlers: z.array(handlerSchema).min(1).optional()
332
341
  });
@@ -342,7 +351,8 @@ var init_task = __esm({
342
351
  data: z.record(z.string(), z.unknown()),
343
352
  ui: contextUiSchema
344
353
  }).optional();
345
- taskContextObjectSchema = z.object({
354
+ TASK_CONTEXT_FORMAT_VERSION = 2;
355
+ taskContextObjectBaseSchema = z.object({
346
356
  /** Source application id; omitted tasks are grouped in the default inbox. */
347
357
  app: z.string().min(1).optional(),
348
358
  type: z.string().min(1),
@@ -350,9 +360,15 @@ var init_task = __esm({
350
360
  description: z.string().optional(),
351
361
  validUntil: z.string().optional(),
352
362
  context: contextDataSchema,
363
+ /** Task context wire format version. @default 2 */
364
+ contextVersion: z.literal(2).optional(),
365
+ /** @deprecated Use `contextVersion`. Accepted on ingest only. */
353
366
  version: z.literal(2).optional(),
354
367
  actions: z.array(taskActionSchema).min(1, "At least one action is required")
355
368
  });
369
+ taskContextObjectSchema = taskContextObjectBaseSchema.transform(
370
+ normalizeTaskContextVersion
371
+ );
356
372
  taskContextSchema = taskContextObjectSchema.superRefine(
357
373
  refineContextPublicUrls
358
374
  );
@@ -386,61 +402,11 @@ var init_task = __esm({
386
402
  });
387
403
  taskPriorities = ["low", "normal", "high", "urgent"];
388
404
  taskPrioritySchema = z.enum(taskPriorities);
389
- nonNegativeInt = z.number().int().nonnegative();
390
- agentCostTokensSchema = z.object({
391
- input: nonNegativeInt.optional(),
392
- output: nonNegativeInt.optional(),
393
- total: nonNegativeInt.optional()
394
- });
395
- agentCostSchema = z.object({
396
- tokens: agentCostTokensSchema.optional(),
397
- eur: z.number().nonnegative().optional(),
398
- usd: z.number().nonnegative().optional()
399
- });
400
- AGENT_INFO_MAX_KEYS = 32;
401
- AGENT_TOOL_CALLS_MAX_KEYS = 64;
402
- AGENT_OTEL_SPANS_MAX = 32;
403
- agentToolCallsSchema = z.record(z.string().min(1), nonNegativeInt);
404
- agentOtelSpanStatusSchema = z.enum(["ok", "error"]);
405
- agentOtelSpanSummarySchema = z.object({
406
- name: z.string().min(1),
407
- durationMs: z.number().nonnegative(),
408
- status: agentOtelSpanStatusSchema
409
- });
410
- agentOtelSchema = z.object({
411
- traceId: z.string().min(1),
412
- spanId: z.string().min(1).optional(),
413
- rootDurationMs: z.number().nonnegative().optional(),
414
- spans: z.array(agentOtelSpanSummarySchema).max(AGENT_OTEL_SPANS_MAX).optional()
415
- });
416
405
  agentTelemetrySchema = z.object({
417
406
  /** Agent or app version (semver, git SHA, deploy tag — caller-defined). */
418
- version: z.string().min(1).optional(),
419
- /** Total tool invocations. When omitted but `toolCalls` is set, the sum of counts is used. */
420
- toolCallCount: nonNegativeInt.optional(),
421
- /** Per-tool invocation counts keyed by tool name. */
422
- toolCalls: agentToolCallsSchema.optional(),
423
- cost: agentCostSchema.optional(),
424
- /** Free-form metadata for experiments (model, prompt variant, etc.). */
425
- info: z.record(z.string(), z.unknown()).optional(),
426
- /** Structured OpenTelemetry span snapshot for feedback analysis. */
427
- otel: agentOtelSchema.optional()
428
- }).refine(
429
- (data) => {
430
- const keys = data.info ? Object.keys(data.info) : [];
431
- return keys.length <= AGENT_INFO_MAX_KEYS;
432
- },
433
- { message: `agent.info may contain at most ${AGENT_INFO_MAX_KEYS} keys` }
434
- ).refine(
435
- (data) => {
436
- const keys = data.toolCalls ? Object.keys(data.toolCalls) : [];
437
- return keys.length <= AGENT_TOOL_CALLS_MAX_KEYS;
438
- },
439
- {
440
- message: `agent.toolCalls may contain at most ${AGENT_TOOL_CALLS_MAX_KEYS} tool names`
441
- }
442
- );
443
- createTaskBodySchema = taskContextObjectSchema.extend({
407
+ version: z.string().min(1)
408
+ });
409
+ createTaskBodySchema = taskContextObjectBaseSchema.extend({
444
410
  assignTo: assignToSchema.optional(),
445
411
  /**
446
412
  * Groups related tasks together. When omitted, the server generates one and
@@ -457,9 +423,59 @@ var init_task = __esm({
457
423
  * the inbox status bar and the thread update log.
458
424
  */
459
425
  update: threadUpdateInputSchema.optional(),
460
- /** Agent telemetry (version, cost, tool calls) — not shown in inbox UI. */
426
+ /** Agent release version — not shown in inbox UI. */
461
427
  agent: agentTelemetrySchema.optional()
462
- }).superRefine(refineContextPublicUrls);
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
+ });
463
479
  }
464
480
  });
465
481
 
@@ -482,6 +498,13 @@ var init_schemas = __esm({
482
498
 
483
499
  // src/schemas/index.ts
484
500
  import { z as z2 } from "zod";
501
+ function normalizeTaskContextVersion2(data) {
502
+ const { version: legacyVersion, contextVersion, ...rest } = data;
503
+ return {
504
+ ...rest,
505
+ contextVersion: contextVersion ?? legacyVersion ?? TASK_CONTEXT_FORMAT_VERSION2
506
+ };
507
+ }
485
508
  function refineContextPublicUrls2(data, ctx) {
486
509
  const error = validateContextPublicUrls(data.context);
487
510
  if (error) {
@@ -492,7 +515,7 @@ function refineContextPublicUrls2(data, ctx) {
492
515
  });
493
516
  }
494
517
  }
495
- var safeUrlSchema2, jsonSchema7Schema2, uiSchemaSchema2, webhookHandlerSchema2, triggerHandlerSchema2, handlerSchema2, taskActionSchema2, uiFieldSchemaSchema2, contextUiSchema2, contextDataSchema2, taskContextObjectSchema2, taskContextSchema2, assignToSchema2, threadUpdateMessageSchema2, threadUpdateStatuses2, threadUpdateStatusSchema2, threadUpdateInputSchema2, taskPriorities2, taskPrioritySchema2, nonNegativeInt2, agentCostTokensSchema2, agentCostSchema2, AGENT_INFO_MAX_KEYS2, AGENT_TOOL_CALLS_MAX_KEYS2, AGENT_OTEL_SPANS_MAX2, agentToolCallsSchema2, agentOtelSpanStatusSchema2, agentOtelSpanSummarySchema2, agentOtelSchema2, 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;
496
519
  var init_schemas2 = __esm({
497
520
  "src/schemas/index.ts"() {
498
521
  "use strict";
@@ -518,13 +541,15 @@ var init_schemas2 = __esm({
518
541
  tokenId: z2.string().min(1)
519
542
  });
520
543
  handlerSchema2 = z2.discriminatedUnion("type", [webhookHandlerSchema2, triggerHandlerSchema2]);
521
- taskActionSchema2 = z2.object({
544
+ taskActionInputSchema2 = z2.object({
522
545
  id: z2.string().min(1),
523
546
  title: z2.string().min(1),
524
547
  description: z2.string().optional(),
525
548
  schema: jsonSchema7Schema2.optional(),
526
549
  ui: uiSchemaSchema2.optional(),
527
- data: z2.record(z2.string(), z2.unknown()).optional(),
550
+ data: z2.record(z2.string(), z2.unknown()).optional()
551
+ });
552
+ taskActionSchema2 = taskActionInputSchema2.extend({
528
553
  handlers: z2.array(handlerSchema2).min(1).optional()
529
554
  });
530
555
  uiFieldSchemaSchema2 = z2.object({
@@ -539,16 +564,22 @@ var init_schemas2 = __esm({
539
564
  data: z2.record(z2.string(), z2.unknown()),
540
565
  ui: contextUiSchema2
541
566
  }).optional();
542
- taskContextObjectSchema2 = z2.object({
567
+ TASK_CONTEXT_FORMAT_VERSION2 = 2;
568
+ taskContextObjectBaseSchema2 = z2.object({
543
569
  app: z2.string().min(1).optional(),
544
570
  type: z2.string().min(1),
545
571
  name: z2.string().min(1),
546
572
  description: z2.string().optional(),
547
573
  validUntil: z2.string().optional(),
548
574
  context: contextDataSchema2,
575
+ contextVersion: z2.literal(2).optional(),
576
+ /** @deprecated Use `contextVersion`. Accepted on ingest only. */
549
577
  version: z2.literal(2).optional(),
550
578
  actions: z2.array(taskActionSchema2).min(1, "At least one action is required")
551
579
  });
580
+ taskContextObjectSchema2 = taskContextObjectBaseSchema2.transform(
581
+ normalizeTaskContextVersion2
582
+ );
552
583
  taskContextSchema2 = taskContextObjectSchema2.superRefine(
553
584
  refineContextPublicUrls2
554
585
  );
@@ -582,56 +613,10 @@ var init_schemas2 = __esm({
582
613
  });
583
614
  taskPriorities2 = ["low", "normal", "high", "urgent"];
584
615
  taskPrioritySchema2 = z2.enum(taskPriorities2);
585
- nonNegativeInt2 = z2.number().int().nonnegative();
586
- agentCostTokensSchema2 = z2.object({
587
- input: nonNegativeInt2.optional(),
588
- output: nonNegativeInt2.optional(),
589
- total: nonNegativeInt2.optional()
590
- });
591
- agentCostSchema2 = z2.object({
592
- tokens: agentCostTokensSchema2.optional(),
593
- eur: z2.number().nonnegative().optional(),
594
- usd: z2.number().nonnegative().optional()
595
- });
596
- AGENT_INFO_MAX_KEYS2 = 32;
597
- AGENT_TOOL_CALLS_MAX_KEYS2 = 64;
598
- AGENT_OTEL_SPANS_MAX2 = 32;
599
- agentToolCallsSchema2 = z2.record(z2.string().min(1), nonNegativeInt2);
600
- agentOtelSpanStatusSchema2 = z2.enum(["ok", "error"]);
601
- agentOtelSpanSummarySchema2 = z2.object({
602
- name: z2.string().min(1),
603
- durationMs: z2.number().nonnegative(),
604
- status: agentOtelSpanStatusSchema2
605
- });
606
- agentOtelSchema2 = z2.object({
607
- traceId: z2.string().min(1),
608
- spanId: z2.string().min(1).optional(),
609
- rootDurationMs: z2.number().nonnegative().optional(),
610
- spans: z2.array(agentOtelSpanSummarySchema2).max(AGENT_OTEL_SPANS_MAX2).optional()
611
- });
612
616
  agentTelemetrySchema2 = z2.object({
613
- version: z2.string().min(1).optional(),
614
- toolCallCount: nonNegativeInt2.optional(),
615
- toolCalls: agentToolCallsSchema2.optional(),
616
- cost: agentCostSchema2.optional(),
617
- info: z2.record(z2.string(), z2.unknown()).optional(),
618
- otel: agentOtelSchema2.optional()
619
- }).refine(
620
- (data) => {
621
- const keys = data.info ? Object.keys(data.info) : [];
622
- return keys.length <= AGENT_INFO_MAX_KEYS2;
623
- },
624
- { message: `agent.info may contain at most ${AGENT_INFO_MAX_KEYS2} keys` }
625
- ).refine(
626
- (data) => {
627
- const keys = data.toolCalls ? Object.keys(data.toolCalls) : [];
628
- return keys.length <= AGENT_TOOL_CALLS_MAX_KEYS2;
629
- },
630
- {
631
- message: `agent.toolCalls may contain at most ${AGENT_TOOL_CALLS_MAX_KEYS2} tool names`
632
- }
633
- );
634
- createTaskBodySchema2 = taskContextObjectSchema2.extend({
617
+ version: z2.string().min(1)
618
+ });
619
+ createTaskBodySchema2 = taskContextObjectBaseSchema2.extend({
635
620
  assignTo: assignToSchema2.optional(),
636
621
  /**
637
622
  * Groups related tasks together. When omitted, the server generates one and
@@ -649,7 +634,7 @@ var init_schemas2 = __esm({
649
634
  */
650
635
  update: threadUpdateInputSchema2.optional(),
651
636
  agent: agentTelemetrySchema2.optional()
652
- }).superRefine(refineContextPublicUrls2);
637
+ }).transform(normalizeTaskContextVersion2).superRefine(refineContextPublicUrls2);
653
638
  threadUpdateBodySchema = threadUpdateInputSchema2;
654
639
  }
655
640
  });
@@ -687,10 +672,248 @@ var init_approval_result = __esm({
687
672
  }
688
673
  });
689
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
+
690
909
  // src/client.ts
691
910
  function sleep(ms) {
692
911
  return new Promise((resolve) => setTimeout(resolve, ms));
693
912
  }
913
+ function resolveAgentVersionFromEnv() {
914
+ const fromEnv = process.env.AGENT_VERSION?.trim() || process.env.ROBOTROCK_AGENT_VERSION?.trim();
915
+ return fromEnv || void 0;
916
+ }
694
917
  function parseValidUntilMs(value) {
695
918
  if (value === void 0) {
696
919
  return void 0;
@@ -744,6 +967,7 @@ function normalizeSendToHumanInput(task2, clientDefaults) {
744
967
  threadId: _threadId,
745
968
  priority: _priority,
746
969
  update: _update,
970
+ version: _version,
747
971
  validUntil,
748
972
  app: taskApp,
749
973
  ...rest
@@ -753,67 +977,35 @@ function normalizeSendToHumanInput(task2, clientDefaults) {
753
977
  const app = taskApp ?? clientDefaults.app;
754
978
  return {
755
979
  ...rest,
756
- version: clientDefaults.version,
980
+ contextVersion: clientDefaults.contextVersion,
757
981
  ...app ? { app } : {},
758
982
  ...validUntil !== void 0 ? { validUntil: serializeValidUntil(validUntil) } : {},
759
983
  actions: normalizedActions
760
984
  };
761
985
  }
762
- async function parseResponseBody(response) {
763
- const contentType = response.headers.get("content-type") ?? "";
764
- const bodyText = await response.text();
765
- if (!bodyText) {
766
- return null;
767
- }
768
- if (contentType.toLowerCase().includes("application/json")) {
769
- try {
770
- return JSON.parse(bodyText);
771
- } catch {
772
- }
773
- }
774
- try {
775
- return JSON.parse(bodyText);
776
- } catch {
777
- return bodyText;
778
- }
779
- }
780
- function getErrorMessage(data, fallback) {
781
- if (data && typeof data === "object" && !Array.isArray(data)) {
782
- const maybeMessage = data.message;
783
- if (typeof maybeMessage === "string" && maybeMessage.trim()) {
784
- return maybeMessage;
785
- }
786
- }
787
- if (typeof data === "string" && data.trim()) {
788
- const compact = data.replace(/\s+/g, " ").trim();
789
- const snippet = compact.length > 180 ? `${compact.slice(0, 180)}...` : compact;
790
- return `${fallback}. Server returned non-JSON response: ${snippet}`;
791
- }
792
- return fallback;
793
- }
794
- var DEFAULT_POLL_INTERVAL_MS, DEFAULT_TIMEOUT_MS, RobotRockError, RobotRock;
986
+ var DEFAULT_POLL_INTERVAL_MS, DEFAULT_TIMEOUT_MS, RobotRock;
795
987
  var init_client = __esm({
796
988
  "src/client.ts"() {
797
989
  "use strict";
798
990
  init_schemas2();
799
991
  init_approval_result();
992
+ init_http();
993
+ init_chats();
994
+ init_http();
800
995
  DEFAULT_POLL_INTERVAL_MS = 2e3;
801
996
  DEFAULT_TIMEOUT_MS = 24 * 60 * 60 * 1e3;
802
- RobotRockError = class extends Error {
803
- constructor(message, statusCode, response) {
804
- super(message);
805
- this.statusCode = statusCode;
806
- this.response = response;
807
- this.name = "RobotRockError";
808
- }
809
- };
810
997
  RobotRock = class {
811
998
  apiKey;
812
999
  baseUrl;
813
1000
  app;
814
- version;
1001
+ agentVersion;
1002
+ contextVersion;
815
1003
  webhook;
816
1004
  polling;
1005
+ /** Task CRUD: `create`, `get`, `cancel`, `sendUpdate`. */
1006
+ tasks;
1007
+ /** Chat CRUD: `create`, `close`. */
1008
+ chats;
817
1009
  constructor(config) {
818
1010
  if (config.webhook && config.polling) {
819
1011
  throw new Error(
@@ -830,26 +1022,37 @@ var init_client = __esm({
830
1022
  const rawBase = config.baseUrl ?? "https://api.robotrock.io/v1";
831
1023
  this.baseUrl = rawBase.replace(/\/+$/, "");
832
1024
  this.app = config.app;
833
- this.version = config.version ?? 2;
1025
+ this.agentVersion = config.version ?? resolveAgentVersionFromEnv();
1026
+ this.contextVersion = config.advanced?.contextVersion ?? TASK_CONTEXT_FORMAT_VERSION2;
834
1027
  this.webhook = config.webhook;
835
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
+ });
836
1040
  }
837
- /**
838
- * Create a task via POST /v1 without waiting for a human response.
839
- */
840
- async createTask(task2) {
1041
+ async createTaskRequest(task2) {
841
1042
  const normalizedTask = normalizeSendToHumanInput(task2, {
842
1043
  webhook: this.webhook,
843
1044
  app: this.app,
844
- version: this.version
1045
+ contextVersion: this.contextVersion,
1046
+ agentVersion: this.agentVersion
845
1047
  });
1048
+ const agentVersion = task2.version ?? this.agentVersion;
846
1049
  const bodyPayload = {
847
1050
  ...normalizedTask,
848
1051
  ...task2.assignTo !== void 0 ? { assignTo: task2.assignTo } : {},
849
1052
  ...task2.threadId !== void 0 ? { threadId: task2.threadId } : {},
850
1053
  ...task2.priority !== void 0 ? { priority: task2.priority } : {},
851
1054
  ...task2.update !== void 0 ? { update: task2.update } : {},
852
- ...task2.agent !== void 0 ? { agent: task2.agent } : {}
1055
+ ...agentVersion !== void 0 ? { agent: { version: agentVersion } } : {}
853
1056
  };
854
1057
  const validation = createTaskBodySchema2.safeParse(bodyPayload);
855
1058
  if (!validation.success) {
@@ -885,9 +1088,10 @@ var init_client = __esm({
885
1088
  const normalizedTask = normalizeSendToHumanInput(task2, {
886
1089
  webhook: this.webhook,
887
1090
  app: this.app,
888
- version: this.version
1091
+ contextVersion: this.contextVersion,
1092
+ agentVersion: this.agentVersion
889
1093
  });
890
- const createdTaskTask = await this.createTask(task2);
1094
+ const createdTaskTask = await this.createTaskRequest(task2);
891
1095
  const hasHandlers = normalizedTask.actions.some(
892
1096
  (action) => Array.isArray(action.handlers) && action.handlers.length > 0
893
1097
  );
@@ -904,7 +1108,7 @@ var init_client = __esm({
904
1108
  const deadline = validUntilMs !== void 0 ? Math.min(pollingDeadline, validUntilMs) : pollingDeadline;
905
1109
  const taskId = createdTaskTask.taskId;
906
1110
  while (Date.now() < deadline) {
907
- const existing = await this.getTask(taskId);
1111
+ const existing = await this.getTaskById(taskId);
908
1112
  if (existing?.status === "handled" && existing.handled) {
909
1113
  return {
910
1114
  mode: "handled",
@@ -926,10 +1130,35 @@ var init_client = __esm({
926
1130
  }
927
1131
  throw new TaskTimeoutError(`No human response within ${timeoutMs}ms`);
928
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
+ }
929
1140
  /**
930
1141
  * Get a task by public task id (returned as `task.taskId` from {@link sendToHuman}).
1142
+ * @deprecated Use `client.tasks.get()` instead.
931
1143
  */
932
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) {
933
1162
  const response = await fetch(`${this.baseUrl}/tasks/${taskId}`, {
934
1163
  method: "GET",
935
1164
  headers: {
@@ -949,11 +1178,11 @@ var init_client = __esm({
949
1178
  }
950
1179
  return data;
951
1180
  }
952
- /**
953
- * Log a status update against a thread. The update shows in the inbox status
954
- * bar and thread update log for every task in the thread.
955
- */
956
- async sendUpdate({ threadId, message, status }) {
1181
+ async sendThreadUpdate({
1182
+ threadId,
1183
+ message,
1184
+ status
1185
+ }) {
957
1186
  if (!threadId) {
958
1187
  throw new RobotRockError("threadId is required to send an update", 400);
959
1188
  }
@@ -986,7 +1215,7 @@ var init_client = __esm({
986
1215
  }
987
1216
  return data.update;
988
1217
  }
989
- async cancelTask(taskId) {
1218
+ async cancelTaskRequest(taskId) {
990
1219
  const response = await fetch(`${this.baseUrl}/tasks/${taskId}/cancel`, {
991
1220
  method: "POST",
992
1221
  headers: {
@@ -1027,6 +1256,212 @@ var init_env = __esm({
1027
1256
  }
1028
1257
  });
1029
1258
 
1259
+ // src/record-handled-to-otel.ts
1260
+ import {
1261
+ context,
1262
+ trace,
1263
+ SpanStatusCode
1264
+ } from "@opentelemetry/api";
1265
+ function isValidTraceId(traceId) {
1266
+ return typeof traceId === "string" && traceId.length > 0 && traceId !== INVALID_TRACE_ID;
1267
+ }
1268
+ function resolveSpanContext(span) {
1269
+ const active = span ?? trace.getActiveSpan();
1270
+ const ctx = active?.spanContext();
1271
+ if (!ctx || !isValidTraceId(ctx.traceId)) {
1272
+ return void 0;
1273
+ }
1274
+ return { traceId: ctx.traceId, spanId: ctx.spanId };
1275
+ }
1276
+ function parseTaskCreatedAt(submittedAt) {
1277
+ if (submittedAt) {
1278
+ const parsed = Date.parse(submittedAt);
1279
+ if (!Number.isNaN(parsed)) {
1280
+ return parsed;
1281
+ }
1282
+ }
1283
+ return Date.now();
1284
+ }
1285
+ function parseHandledAtMs(handledAt) {
1286
+ if (handledAt instanceof Date) {
1287
+ return handledAt.getTime();
1288
+ }
1289
+ if (typeof handledAt === "number") {
1290
+ return handledAt;
1291
+ }
1292
+ const parsed = Date.parse(handledAt);
1293
+ return Number.isNaN(parsed) ? Date.now() : parsed;
1294
+ }
1295
+ function toRobotRockHandledOtelInput(handled) {
1296
+ if ("actionId" in handled) {
1297
+ return {
1298
+ taskId: handled.taskId,
1299
+ action: {
1300
+ id: handled.actionId,
1301
+ data: handled.data
1302
+ },
1303
+ handledBy: handled.handledBy,
1304
+ handledAt: handled.handledAt
1305
+ };
1306
+ }
1307
+ return {
1308
+ taskId: handled.taskId,
1309
+ action: {
1310
+ id: handled.action.id,
1311
+ title: handled.action.title,
1312
+ data: handled.action.data
1313
+ },
1314
+ handledBy: handled.handledBy,
1315
+ handledAt: handled.handledAt
1316
+ };
1317
+ }
1318
+ function captureRobotRockOtelHandle(task2, options) {
1319
+ const spanCtx = resolveSpanContext(options?.span);
1320
+ return {
1321
+ traceId: spanCtx?.traceId ?? "",
1322
+ spanId: spanCtx?.spanId ?? "",
1323
+ taskId: task2.taskId,
1324
+ threadId: task2.threadId,
1325
+ taskCreatedAt: parseTaskCreatedAt(task2.submittedAt)
1326
+ };
1327
+ }
1328
+ function startRobotRockHumanWaitSpan(handle, options) {
1329
+ const parent = options?.span ?? trace.getActiveSpan();
1330
+ if (!parent) {
1331
+ return void 0;
1332
+ }
1333
+ const tracer = trace.getTracer(options?.tracerName ?? TRACER_NAME);
1334
+ const span = tracer.startSpan(
1335
+ WAIT_SPAN_NAME,
1336
+ {},
1337
+ trace.setSpan(context.active(), parent)
1338
+ );
1339
+ span.setAttribute("robotrock.task_id", handle.taskId);
1340
+ if (handle.threadId) {
1341
+ span.setAttribute("robotrock.thread_id", handle.threadId);
1342
+ }
1343
+ return span;
1344
+ }
1345
+ function recordRobotRockHandledToOtel(handled, options = {}) {
1346
+ const span = options.span ?? trace.getActiveSpan();
1347
+ if (!span) {
1348
+ return;
1349
+ }
1350
+ const input = toRobotRockHandledOtelInput(handled);
1351
+ const handledAtMs = parseHandledAtMs(input.handledAt);
1352
+ const handle = options.handle;
1353
+ const humanWaitMs = handle != null ? Math.max(0, handledAtMs - handle.taskCreatedAt) : void 0;
1354
+ span.setAttribute("robotrock.task_id", input.taskId);
1355
+ span.setAttribute("robotrock.action.id", input.action.id);
1356
+ if (input.action.title) {
1357
+ span.setAttribute("robotrock.action.title", input.action.title);
1358
+ }
1359
+ if (input.handledBy) {
1360
+ span.setAttribute("robotrock.handled_by", input.handledBy);
1361
+ }
1362
+ span.setAttribute("robotrock.handled_at", new Date(handledAtMs).toISOString());
1363
+ if (humanWaitMs != null) {
1364
+ span.setAttribute("robotrock.human_wait_ms", humanWaitMs);
1365
+ }
1366
+ if (input.threadId) {
1367
+ span.setAttribute("robotrock.thread_id", input.threadId);
1368
+ }
1369
+ const eventAttributes = {
1370
+ "robotrock.task_id": input.taskId,
1371
+ "robotrock.action.id": input.action.id
1372
+ };
1373
+ if (input.handledBy) {
1374
+ eventAttributes["robotrock.handled_by"] = input.handledBy;
1375
+ }
1376
+ if (humanWaitMs != null) {
1377
+ eventAttributes["robotrock.human_wait_ms"] = humanWaitMs;
1378
+ }
1379
+ if (options.includeActionData && input.action.data != null) {
1380
+ const serialized = JSON.stringify(input.action.data);
1381
+ const truncated = serialized.length > 512 ? `${serialized.slice(0, 512)}\u2026` : serialized;
1382
+ span.setAttribute("robotrock.action.data", truncated);
1383
+ eventAttributes["robotrock.action.data"] = truncated;
1384
+ }
1385
+ span.addEvent(HANDLED_EVENT_NAME, eventAttributes);
1386
+ }
1387
+ function endRobotRockHumanWaitSpan(span, handled, options = {}) {
1388
+ if (!span) {
1389
+ return;
1390
+ }
1391
+ const outcome = options.outcome ?? (handled ? "handled" : "timeout");
1392
+ if (handled && outcome === "handled") {
1393
+ recordRobotRockHandledToOtel(handled, {
1394
+ span,
1395
+ handle: options.handle,
1396
+ includeActionData: options.includeActionData
1397
+ });
1398
+ span.setStatus({ code: SpanStatusCode.OK });
1399
+ } else {
1400
+ span.setAttribute("robotrock.outcome", outcome);
1401
+ span.setStatus({
1402
+ code: SpanStatusCode.ERROR,
1403
+ message: outcome === "timeout" ? "Human response timed out before validUntil" : "Human wait failed"
1404
+ });
1405
+ }
1406
+ span.end();
1407
+ }
1408
+ var TRACER_NAME, WAIT_SPAN_NAME, HANDLED_EVENT_NAME, INVALID_TRACE_ID;
1409
+ var init_record_handled_to_otel = __esm({
1410
+ "src/record-handled-to-otel.ts"() {
1411
+ "use strict";
1412
+ TRACER_NAME = "robotrock";
1413
+ WAIT_SPAN_NAME = "robotrock.wait_for_human";
1414
+ HANDLED_EVENT_NAME = "robotrock.task_handled";
1415
+ INVALID_TRACE_ID = "00000000000000000000000000000000";
1416
+ }
1417
+ });
1418
+
1419
+ // src/otel-platform.ts
1420
+ function shouldRecordRobotRockOtel(recordOtel) {
1421
+ if (recordOtel === true) {
1422
+ return true;
1423
+ }
1424
+ if (recordOtel === false) {
1425
+ return false;
1426
+ }
1427
+ const env = process.env.ROBOTROCK_OTEL_RECORD_HANDLED;
1428
+ return env === "true" || env === "1";
1429
+ }
1430
+ function stripPlatformOtelFields(payload) {
1431
+ const { recordOtel: _recordOtel, otelIncludeActionData: _otelIncludeActionData, ...rest } = payload;
1432
+ return rest;
1433
+ }
1434
+ function beginRobotRockHumanWaitOtel(task2, options = {}) {
1435
+ const recordOtel = shouldRecordRobotRockOtel(options.recordOtel);
1436
+ if (!recordOtel) {
1437
+ return { recordOtel: false };
1438
+ }
1439
+ const handle = captureRobotRockOtelHandle(task2);
1440
+ const waitSpan = startRobotRockHumanWaitSpan(handle);
1441
+ return {
1442
+ recordOtel: true,
1443
+ handle,
1444
+ waitSpan,
1445
+ includeActionData: options.otelIncludeActionData
1446
+ };
1447
+ }
1448
+ function finishRobotRockHumanWaitOtel(session, handled, outcome) {
1449
+ if (!session.recordOtel) {
1450
+ return;
1451
+ }
1452
+ endRobotRockHumanWaitSpan(session.waitSpan, handled, {
1453
+ handle: session.handle,
1454
+ outcome,
1455
+ includeActionData: session.includeActionData
1456
+ });
1457
+ }
1458
+ var init_otel_platform = __esm({
1459
+ "src/otel-platform.ts"() {
1460
+ "use strict";
1461
+ init_record_handled_to_otel();
1462
+ }
1463
+ });
1464
+
1030
1465
  // src/handler-webhook.ts
1031
1466
  function isRobotRockHandlerWebhookPayload(value) {
1032
1467
  if (typeof value !== "object" || value === null) {
@@ -1107,7 +1542,7 @@ __export(trigger_exports, {
1107
1542
  });
1108
1543
  import { task, wait } from "@trigger.dev/sdk";
1109
1544
  async function runSendToHuman(payload) {
1110
- const { validUntil: validUntilInput, ...taskInput } = payload;
1545
+ const { validUntil: validUntilInput, app, recordOtel, otelIncludeActionData, ...taskFields } = payload;
1111
1546
  const { validUntil, timeout } = resolveWaitTiming(validUntilInput);
1112
1547
  const token = await wait.createToken({ timeout });
1113
1548
  const baseConfig = resolveRobotRockConfig();
@@ -1116,45 +1551,74 @@ async function runSendToHuman(payload) {
1116
1551
  baseUrl: baseConfig.baseUrl,
1117
1552
  ...baseConfig.app ? { app: baseConfig.app } : {},
1118
1553
  ...baseConfig.version ? { version: baseConfig.version } : {},
1554
+ ...baseConfig.advanced ? { advanced: baseConfig.advanced } : {},
1119
1555
  webhook: { url: token.url }
1120
1556
  });
1557
+ const taskInput = stripPlatformOtelFields(taskFields);
1121
1558
  const sendResult = await client.sendToHuman({
1122
1559
  ...taskInput,
1123
- validUntil
1560
+ validUntil,
1561
+ ...app !== void 0 ? { app } : {}
1124
1562
  });
1125
- const outcome = await wait.forToken(token.id);
1126
- if (!outcome.ok) {
1127
- throw new Error(`Human response timed out before validUntil (${timeout})`);
1128
- }
1129
- const output = outcome.output;
1130
- if (!isRobotRockHandlerWebhookPayload(output)) {
1131
- throw new Error(
1132
- "Wait token completed with unexpected payload; expected RobotRock handler body (taskId, action.id, action.data)."
1133
- );
1134
- }
1135
- return toDiscriminatedApprovalResult(
1136
- payload.actions,
1137
- {
1138
- id: output.taskId,
1139
- createdAt: /* @__PURE__ */ new Date(),
1140
- status: "handled",
1141
- context: sendResult.task.context,
1142
- validUntil: Date.now(),
1143
- handledAt: new Date(output.handledAt).getTime(),
1144
- handled: {
1145
- action: {
1146
- id: output.action.id,
1147
- data: output.action.data
1148
- },
1149
- handledBy: output.handledBy
1563
+ const otelSession = beginRobotRockHumanWaitOtel(sendResult.task, {
1564
+ recordOtel,
1565
+ otelIncludeActionData
1566
+ });
1567
+ let handledPayload = null;
1568
+ let waitOutcome = "pending";
1569
+ try {
1570
+ const outcome = await wait.forToken(token.id);
1571
+ if (!outcome.ok) {
1572
+ waitOutcome = "timeout";
1573
+ throw new Error(`Human response timed out before validUntil (${timeout})`);
1574
+ }
1575
+ const output = outcome.output;
1576
+ if (!isRobotRockHandlerWebhookPayload(output)) {
1577
+ waitOutcome = "error";
1578
+ throw new Error(
1579
+ "Wait token completed with unexpected payload; expected RobotRock handler body (taskId, action.id, action.data)."
1580
+ );
1581
+ }
1582
+ handledPayload = output;
1583
+ waitOutcome = "handled";
1584
+ return toDiscriminatedApprovalResult(
1585
+ payload.actions,
1586
+ {
1587
+ id: output.taskId,
1588
+ createdAt: /* @__PURE__ */ new Date(),
1589
+ status: "handled",
1590
+ context: sendResult.task.context,
1591
+ validUntil: Date.now(),
1592
+ handledAt: new Date(output.handledAt).getTime(),
1593
+ handled: {
1594
+ action: {
1595
+ id: output.action.id,
1596
+ data: output.action.data
1597
+ },
1598
+ handledBy: output.handledBy
1599
+ }
1150
1600
  }
1601
+ );
1602
+ } catch (error) {
1603
+ if (waitOutcome === "pending") {
1604
+ waitOutcome = "error";
1151
1605
  }
1152
- );
1606
+ throw error;
1607
+ } finally {
1608
+ if (waitOutcome !== "pending") {
1609
+ finishRobotRockHumanWaitOtel(
1610
+ otelSession,
1611
+ handledPayload,
1612
+ waitOutcome === "handled" ? "handled" : waitOutcome
1613
+ );
1614
+ }
1615
+ }
1153
1616
  }
1154
1617
  var APPROVE_BY_HUMAN_ACTIONS, sendToHumanTask, approveByHumanTask;
1155
1618
  var init_trigger = __esm({
1156
1619
  "src/trigger/index.ts"() {
1157
1620
  "use strict";
1621
+ init_otel_platform();
1158
1622
  init_client();
1159
1623
  init_env();
1160
1624
  init_approval_result();
@@ -1194,6 +1658,7 @@ async function createRobotRockTaskForWebhook(input) {
1194
1658
  baseUrl: baseConfig.baseUrl,
1195
1659
  ...input.app ?? baseConfig.app ? { app: input.app ?? baseConfig.app } : {},
1196
1660
  ...baseConfig.version ? { version: baseConfig.version } : {},
1661
+ ...baseConfig.advanced ? { advanced: baseConfig.advanced } : {},
1197
1662
  webhook: { url: input.webhookUrl }
1198
1663
  });
1199
1664
  return client.sendToHuman({
@@ -1214,7 +1679,7 @@ async function parseRobotRockWebhookRequest(request) {
1214
1679
  async function sendToHumanInWorkflow(payload) {
1215
1680
  var _stack = [];
1216
1681
  try {
1217
- const { validUntil: validUntilInput, app, ...taskInput } = payload;
1682
+ const { validUntil: validUntilInput, app, recordOtel, otelIncludeActionData, ...taskFields } = payload;
1218
1683
  const { validUntil, timeout } = resolveWaitTiming(validUntilInput);
1219
1684
  const timeoutMs = parseValidUntilMs2(validUntil) - Date.now();
1220
1685
  const webhook = __using(_stack, createWebhook());
@@ -1222,31 +1687,54 @@ async function sendToHumanInWorkflow(payload) {
1222
1687
  webhookUrl: webhook.url,
1223
1688
  app,
1224
1689
  validUntil,
1225
- taskInput
1690
+ taskInput: stripPlatformOtelFields(taskFields)
1226
1691
  });
1227
- const outcome = await Promise.race([
1228
- webhook.then((request) => parseRobotRockWebhookRequest(request)),
1229
- sleep2(timeoutMs).then(() => ({ timedOut: true }))
1230
- ]);
1231
- if ("timedOut" in outcome) {
1232
- throw new Error(`Human response timed out before validUntil (${timeout})`);
1233
- }
1234
- const output = outcome;
1235
- return toDiscriminatedApprovalResult(payload.actions, {
1236
- id: output.taskId,
1237
- createdAt: /* @__PURE__ */ new Date(),
1238
- status: "handled",
1239
- context: sendResult.task.context,
1240
- validUntil: Date.now(),
1241
- handledAt: new Date(output.handledAt).getTime(),
1242
- handled: {
1243
- action: {
1244
- id: output.action.id,
1245
- data: output.action.data
1246
- },
1247
- handledBy: output.handledBy
1248
- }
1692
+ const otelSession = beginRobotRockHumanWaitOtel(sendResult.task, {
1693
+ recordOtel,
1694
+ otelIncludeActionData
1249
1695
  });
1696
+ let handledPayload = null;
1697
+ let waitOutcome = "pending";
1698
+ try {
1699
+ const outcome = await Promise.race([
1700
+ webhook.then((request) => parseRobotRockWebhookRequest(request)),
1701
+ sleep2(timeoutMs).then(() => ({ timedOut: true }))
1702
+ ]);
1703
+ if ("timedOut" in outcome) {
1704
+ waitOutcome = "timeout";
1705
+ throw new Error(`Human response timed out before validUntil (${timeout})`);
1706
+ }
1707
+ handledPayload = outcome;
1708
+ waitOutcome = "handled";
1709
+ return toDiscriminatedApprovalResult(payload.actions, {
1710
+ id: outcome.taskId,
1711
+ createdAt: /* @__PURE__ */ new Date(),
1712
+ status: "handled",
1713
+ context: sendResult.task.context,
1714
+ validUntil: Date.now(),
1715
+ handledAt: new Date(outcome.handledAt).getTime(),
1716
+ handled: {
1717
+ action: {
1718
+ id: outcome.action.id,
1719
+ data: outcome.action.data
1720
+ },
1721
+ handledBy: outcome.handledBy
1722
+ }
1723
+ });
1724
+ } catch (error) {
1725
+ if (waitOutcome === "pending") {
1726
+ waitOutcome = "error";
1727
+ }
1728
+ throw error;
1729
+ } finally {
1730
+ if (waitOutcome !== "pending") {
1731
+ finishRobotRockHumanWaitOtel(
1732
+ otelSession,
1733
+ handledPayload,
1734
+ waitOutcome === "handled" ? "handled" : waitOutcome
1735
+ );
1736
+ }
1737
+ }
1250
1738
  } catch (_) {
1251
1739
  var _error = _, _hasError = true;
1252
1740
  } finally {
@@ -1267,9 +1755,10 @@ async function sendRobotRockUpdate(payload) {
1267
1755
  apiKey: baseConfig.apiKey,
1268
1756
  baseUrl: baseConfig.baseUrl,
1269
1757
  ...app ?? baseConfig.app ? { app: app ?? baseConfig.app } : {},
1270
- ...baseConfig.version ? { version: baseConfig.version } : {}
1758
+ ...baseConfig.version ? { version: baseConfig.version } : {},
1759
+ ...baseConfig.advanced ? { advanced: baseConfig.advanced } : {}
1271
1760
  });
1272
- return client.sendUpdate(update);
1761
+ return client.tasks.sendUpdate(update);
1273
1762
  }
1274
1763
  async function sendUpdateInWorkflow(payload) {
1275
1764
  return sendRobotRockUpdate(payload);
@@ -1283,6 +1772,7 @@ var init_workflow = __esm({
1283
1772
  init_approval_result();
1284
1773
  init_handler_webhook();
1285
1774
  init_wait_timing();
1775
+ init_otel_platform();
1286
1776
  APPROVE_BY_HUMAN_ACTIONS2 = [
1287
1777
  { id: "approve", title: "Approve" },
1288
1778
  { id: "decline", title: "Decline" }
@@ -1294,7 +1784,7 @@ var init_workflow = __esm({
1294
1784
  import { tool } from "ai";
1295
1785
 
1296
1786
  // src/ai/approve-by-human-tool-core.ts
1297
- import { z as z3 } from "zod";
1787
+ import { z as z4 } from "zod";
1298
1788
 
1299
1789
  // src/ai/context.ts
1300
1790
  init_client();
@@ -1324,27 +1814,27 @@ function normalizeRobotRockAiContext(clientOrContext) {
1324
1814
  }
1325
1815
  throw new Error(`Unknown RobotRock AI mode: ${String(clientOrContext.mode)}`);
1326
1816
  }
1327
- async function sendToHumanForAi(context, payload) {
1328
- if (context.mode === "trigger") {
1817
+ async function sendToHumanForAi(context2, payload) {
1818
+ if (context2.mode === "trigger") {
1329
1819
  const { sendToHumanTask: sendToHumanTask2 } = await Promise.resolve().then(() => (init_trigger(), trigger_exports));
1330
1820
  const waitResult = await sendToHumanTask2.triggerAndWait({
1331
1821
  ...payload,
1332
- ...context.app ? { app: context.app } : {}
1822
+ ...context2.app ? { app: context2.app } : {}
1333
1823
  });
1334
1824
  if (!waitResult.ok) {
1335
1825
  throw waitResult.error;
1336
1826
  }
1337
1827
  return waitResult.output;
1338
1828
  }
1339
- if (context.mode === "workflow") {
1829
+ if (context2.mode === "workflow") {
1340
1830
  const { sendToHumanInWorkflow: sendToHumanInWorkflow2 } = await Promise.resolve().then(() => (init_workflow(), workflow_exports));
1341
1831
  const result2 = await sendToHumanInWorkflow2({
1342
1832
  ...payload,
1343
- ...context.app ? { app: context.app } : {}
1833
+ ...context2.app ? { app: context2.app } : {}
1344
1834
  });
1345
1835
  return result2;
1346
1836
  }
1347
- const result = await context.client.sendToHuman(payload);
1837
+ const result = await context2.client.sendToHuman(payload);
1348
1838
  if (result.mode !== "handled") {
1349
1839
  throw new Error(
1350
1840
  "RobotRock task was created but not handled. Configure client polling or webhook, or use mode: 'trigger' / 'workflow' for durable waits."
@@ -1358,42 +1848,52 @@ async function sendToHumanForAi(context, payload) {
1358
1848
  taskId: result.taskId
1359
1849
  };
1360
1850
  }
1361
- async function sendUpdateForAi(context, payload) {
1362
- if (context.mode === "workflow") {
1851
+ async function sendUpdateForAi(context2, payload) {
1852
+ if (context2.mode === "workflow") {
1363
1853
  const { sendUpdateInWorkflow: sendUpdateInWorkflow2 } = await Promise.resolve().then(() => (init_workflow(), workflow_exports));
1364
1854
  return sendUpdateInWorkflow2({
1365
1855
  ...payload,
1366
- ...context.app ? { app: context.app } : {}
1856
+ ...context2.app ? { app: context2.app } : {}
1367
1857
  });
1368
1858
  }
1369
- if (context.mode === "trigger") {
1859
+ if (context2.mode === "trigger") {
1860
+ const client = createClient(
1861
+ resolveRobotRockConfig(context2.app ? { app: context2.app } : void 0)
1862
+ );
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") {
1370
1869
  const client = createClient(
1371
- resolveRobotRockConfig(context.app ? { app: context.app } : void 0)
1870
+ resolveRobotRockConfig(context2.app ? { app: context2.app } : void 0)
1372
1871
  );
1373
- return client.sendUpdate(payload);
1872
+ await client.chats.close(payload.chatId, { reason: payload.reason });
1873
+ return;
1374
1874
  }
1375
- return context.client.sendUpdate(payload);
1875
+ await context2.client.chats.close(payload.chatId, { reason: payload.reason });
1376
1876
  }
1377
- async function approveByHumanForAi(context, payload) {
1378
- if (context.mode === "trigger") {
1877
+ async function approveByHumanForAi(context2, payload) {
1878
+ if (context2.mode === "trigger") {
1379
1879
  const { approveByHumanTask: approveByHumanTask2 } = await Promise.resolve().then(() => (init_trigger(), trigger_exports));
1380
1880
  const waitResult = await approveByHumanTask2.triggerAndWait({
1381
1881
  ...payload,
1382
- ...context.app ? { app: context.app } : {}
1882
+ ...context2.app ? { app: context2.app } : {}
1383
1883
  });
1384
1884
  if (!waitResult.ok) {
1385
1885
  throw waitResult.error;
1386
1886
  }
1387
1887
  return waitResult.output;
1388
1888
  }
1389
- if (context.mode === "workflow") {
1889
+ if (context2.mode === "workflow") {
1390
1890
  const { approveByHumanInWorkflow: approveByHumanInWorkflow2 } = await Promise.resolve().then(() => (init_workflow(), workflow_exports));
1391
1891
  return await approveByHumanInWorkflow2({
1392
1892
  ...payload,
1393
- ...context.app ? { app: context.app } : {}
1893
+ ...context2.app ? { app: context2.app } : {}
1394
1894
  });
1395
1895
  }
1396
- const result = await context.client.sendToHuman({
1896
+ const result = await context2.client.sendToHuman({
1397
1897
  ...payload,
1398
1898
  actions: APPROVE_BY_HUMAN_ACTIONS3
1399
1899
  });
@@ -1431,11 +1931,11 @@ function toHumanToolResult(result) {
1431
1931
  }
1432
1932
 
1433
1933
  // src/ai/approve-by-human-tool-core.ts
1434
- var approveByHumanInputSchema = z3.object({
1435
- type: z3.string().optional().describe("Task type slug; defaults to ai-approval"),
1436
- name: z3.string().describe("Short title for the approval request"),
1437
- description: z3.string().describe("What needs approval and the consequences of approving or declining"),
1438
- 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")
1439
1939
  });
1440
1940
  function resolveApproveByHumanToolConfig(clientOrContext, maybeOptions = {}) {
1441
1941
  const isDurable = typeof clientOrContext === "object" && clientOrContext !== null && "mode" in clientOrContext && (clientOrContext.mode === "trigger" || clientOrContext.mode === "workflow");
@@ -1485,16 +1985,16 @@ import { tool as tool2 } from "ai";
1485
1985
 
1486
1986
  // src/ai/create-send-to-human-tool-core.ts
1487
1987
  init_schemas2();
1488
- import { z as z4 } from "zod";
1489
- var sendToHumanToolInputSchema = z4.object({
1490
- type: z4.string().describe("Task type slug shown in the RobotRock inbox"),
1491
- name: z4.string().describe("Short title for the human reviewer"),
1492
- description: z4.string().optional().describe("What you need from the human and why you cannot proceed alone"),
1493
- context: z4.object({
1494
- data: z4.record(z4.string(), z4.unknown()).optional(),
1495
- 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()
1496
1996
  }).optional().describe("Optional structured context for the inbox UI"),
1497
- 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"),
1498
1998
  assignTo: assignToSchema2.optional().describe(
1499
1999
  "Assign to tenant member emails and/or group slugs; narrows who sees the task in the inbox"
1500
2000
  )
@@ -1548,12 +2048,12 @@ import { tool as tool3 } from "ai";
1548
2048
  // src/ai/create-send-update-tool-core.ts
1549
2049
  init_schemas2();
1550
2050
  init_client();
1551
- import { z as z5 } from "zod";
1552
- var sendUpdateToolInputSchema = z5.object({
1553
- threadId: z5.string().optional().describe(
2051
+ import { z as z6 } from "zod";
2052
+ var sendUpdateToolInputSchema = z6.object({
2053
+ threadId: z6.string().optional().describe(
1554
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."
1555
2055
  ),
1556
- 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"),
1557
2057
  status: threadUpdateStatusSchema2.optional().describe(
1558
2058
  "Lifecycle status driving the status-bar icon/color: info, queued, running, waiting, succeeded, failed, cancelled"
1559
2059
  )
@@ -1609,16 +2109,105 @@ function createSendUpdateTool(clientOrOptions, maybeOptions = {}) {
1609
2109
  return tool3(buildSendUpdateToolDefinition(clientOrOptions, maybeOptions));
1610
2110
  }
1611
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
+
1612
2207
  // src/ai/format-tool-approval-task.ts
1613
2208
  function defaultFormatToolApprovalTask(toolCall, options = {}) {
1614
2209
  const approveId = options.approveActionId ?? "approve";
1615
2210
  const denyId = options.denyActionId ?? "deny";
1616
- let inputPreview;
1617
- try {
1618
- inputPreview = JSON.stringify(toolCall.input, null, 2);
1619
- } catch {
1620
- inputPreview = String(toolCall.input);
1621
- }
1622
2211
  return {
1623
2212
  type: options.type ?? "ai-tool-approval",
1624
2213
  name: `Approve: ${toolCall.toolName}`,
@@ -1816,7 +2405,7 @@ function resolveToolCallForRequest(request, source, messages) {
1816
2405
  );
1817
2406
  }
1818
2407
  async function resolveToolApprovalsViaRobotRock(clientOrContext, source, options = {}) {
1819
- const context = normalizeRobotRockAiContext(clientOrContext);
2408
+ const context2 = normalizeRobotRockAiContext(clientOrContext);
1820
2409
  const approveId = options.approveActionId ?? "approve";
1821
2410
  const denyId = options.denyActionId ?? "deny";
1822
2411
  const formatTask = options.formatTask ?? defaultFormatToolApprovalTask;
@@ -1829,7 +2418,7 @@ async function resolveToolApprovalsViaRobotRock(clientOrContext, source, options
1829
2418
  approveActionId: approveId,
1830
2419
  denyActionId: denyId
1831
2420
  });
1832
- const result = await sendToHumanForAi(context, taskInput);
2421
+ const result = await sendToHumanForAi(context2, taskInput);
1833
2422
  const approved = result.actionId === approveId;
1834
2423
  const reason = typeof result.data === "object" && result.data !== null && "reason" in result.data && typeof result.data.reason === "string" ? result.data.reason : approved ? "Approved in RobotRock inbox" : "Denied in RobotRock inbox";
1835
2424
  responses.push({
@@ -1867,17 +2456,67 @@ async function runWithRobotRockApprovals(options) {
1867
2456
  throw new Error(`RobotRock approval loop exceeded maxRounds (${maxRounds})`);
1868
2457
  }
1869
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
+
1870
2509
  // src/ai/create-ai-tools.ts
1871
2510
  function createRobotRockAiTools(options) {
1872
2511
  const mode = options.mode ?? (options.client ? "polling" : "trigger");
1873
2512
  if (mode === "polling" && !options.client) {
1874
2513
  throw new Error("createRobotRockAiTools: polling mode requires `client`.");
1875
2514
  }
1876
- const context = mode === "trigger" ? { mode: "trigger", app: options.app } : mode === "workflow" ? { mode: "workflow", app: options.app } : { mode: "polling", client: options.client };
1877
- const durableContext = context.mode === "trigger" || context.mode === "workflow" ? context : null;
1878
- const pollingClient = context.mode === "polling" ? context.client : void 0;
2515
+ const context2 = mode === "trigger" ? { mode: "trigger", app: options.app } : mode === "workflow" ? { mode: "workflow", app: options.app } : { mode: "polling", client: options.client };
2516
+ const durableContext = context2.mode === "trigger" || context2.mode === "workflow" ? context2 : null;
2517
+ const pollingClient = context2.mode === "polling" ? context2.client : void 0;
1879
2518
  return {
1880
- context,
2519
+ context: context2,
1881
2520
  approveByHuman: (toolOptions) => durableContext ? approveByHumanTool({ ...durableContext, ...toolOptions }) : approveByHumanTool(pollingClient, toolOptions),
1882
2521
  sendToHuman: (toolOptions) => durableContext ? createSendToHumanTool({
1883
2522
  ...durableContext,
@@ -1894,7 +2533,19 @@ function createRobotRockAiTools(options) {
1894
2533
  }) : createSendUpdateTool(pollingClient, {
1895
2534
  ...options.threadId ? { threadId: options.threadId } : {},
1896
2535
  ...toolOptions
1897
- })
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
+ }
1898
2549
  };
1899
2550
  }
1900
2551
  export {
@@ -1906,6 +2557,8 @@ export {
1906
2557
  createRobotRockToolApproval,
1907
2558
  createSendToHumanTool,
1908
2559
  createSendUpdateTool,
2560
+ reportStatusTool,
2561
+ requestActionInputTool,
1909
2562
  resolveToolApprovalsViaRobotRock,
1910
2563
  runWithRobotRockApprovals
1911
2564
  };