robotrock 0.8.5 → 0.9.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, taskActionSchema, uiFieldSchemaSchema, contextUiSchema, contextDataSchema, TASK_CONTEXT_FORMAT_VERSION, taskContextObjectBaseSchema, taskContextObjectSchema, taskContextSchema, assignToSchema, threadUpdateMessageSchema, threadUpdateStatuses, threadUpdateStatusSchema, threadUpdateInputSchema, taskPriorities, taskPrioritySchema, agentTelemetrySchema, createTaskBodySchema;
298
305
  var init_task = __esm({
299
306
  "../core/src/schemas/task.ts"() {
300
307
  "use strict";
@@ -342,7 +349,8 @@ var init_task = __esm({
342
349
  data: z.record(z.string(), z.unknown()),
343
350
  ui: contextUiSchema
344
351
  }).optional();
345
- taskContextObjectSchema = z.object({
352
+ TASK_CONTEXT_FORMAT_VERSION = 2;
353
+ taskContextObjectBaseSchema = z.object({
346
354
  /** Source application id; omitted tasks are grouped in the default inbox. */
347
355
  app: z.string().min(1).optional(),
348
356
  type: z.string().min(1),
@@ -350,9 +358,15 @@ var init_task = __esm({
350
358
  description: z.string().optional(),
351
359
  validUntil: z.string().optional(),
352
360
  context: contextDataSchema,
361
+ /** Task context wire format version. @default 2 */
362
+ contextVersion: z.literal(2).optional(),
363
+ /** @deprecated Use `contextVersion`. Accepted on ingest only. */
353
364
  version: z.literal(2).optional(),
354
365
  actions: z.array(taskActionSchema).min(1, "At least one action is required")
355
366
  });
367
+ taskContextObjectSchema = taskContextObjectBaseSchema.transform(
368
+ normalizeTaskContextVersion
369
+ );
356
370
  taskContextSchema = taskContextObjectSchema.superRefine(
357
371
  refineContextPublicUrls
358
372
  );
@@ -386,61 +400,11 @@ var init_task = __esm({
386
400
  });
387
401
  taskPriorities = ["low", "normal", "high", "urgent"];
388
402
  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
403
  agentTelemetrySchema = z.object({
417
404
  /** 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({
405
+ version: z.string().min(1)
406
+ });
407
+ createTaskBodySchema = taskContextObjectBaseSchema.extend({
444
408
  assignTo: assignToSchema.optional(),
445
409
  /**
446
410
  * Groups related tasks together. When omitted, the server generates one and
@@ -457,9 +421,9 @@ var init_task = __esm({
457
421
  * the inbox status bar and the thread update log.
458
422
  */
459
423
  update: threadUpdateInputSchema.optional(),
460
- /** Agent telemetry (version, cost, tool calls) — not shown in inbox UI. */
424
+ /** Agent release version — not shown in inbox UI. */
461
425
  agent: agentTelemetrySchema.optional()
462
- }).superRefine(refineContextPublicUrls);
426
+ }).transform(normalizeTaskContextVersion).superRefine(refineContextPublicUrls);
463
427
  }
464
428
  });
465
429
 
@@ -482,6 +446,13 @@ var init_schemas = __esm({
482
446
 
483
447
  // src/schemas/index.ts
484
448
  import { z as z2 } from "zod";
449
+ function normalizeTaskContextVersion2(data) {
450
+ const { version: legacyVersion, contextVersion, ...rest } = data;
451
+ return {
452
+ ...rest,
453
+ contextVersion: contextVersion ?? legacyVersion ?? TASK_CONTEXT_FORMAT_VERSION2
454
+ };
455
+ }
485
456
  function refineContextPublicUrls2(data, ctx) {
486
457
  const error = validateContextPublicUrls(data.context);
487
458
  if (error) {
@@ -492,7 +463,7 @@ function refineContextPublicUrls2(data, ctx) {
492
463
  });
493
464
  }
494
465
  }
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;
466
+ var safeUrlSchema2, jsonSchema7Schema2, uiSchemaSchema2, webhookHandlerSchema2, triggerHandlerSchema2, handlerSchema2, taskActionSchema2, uiFieldSchemaSchema2, contextUiSchema2, contextDataSchema2, TASK_CONTEXT_FORMAT_VERSION2, taskContextObjectBaseSchema2, taskContextObjectSchema2, taskContextSchema2, assignToSchema2, threadUpdateMessageSchema2, threadUpdateStatuses2, threadUpdateStatusSchema2, threadUpdateInputSchema2, taskPriorities2, taskPrioritySchema2, agentTelemetrySchema2, createTaskBodySchema2, threadUpdateBodySchema;
496
467
  var init_schemas2 = __esm({
497
468
  "src/schemas/index.ts"() {
498
469
  "use strict";
@@ -539,16 +510,22 @@ var init_schemas2 = __esm({
539
510
  data: z2.record(z2.string(), z2.unknown()),
540
511
  ui: contextUiSchema2
541
512
  }).optional();
542
- taskContextObjectSchema2 = z2.object({
513
+ TASK_CONTEXT_FORMAT_VERSION2 = 2;
514
+ taskContextObjectBaseSchema2 = z2.object({
543
515
  app: z2.string().min(1).optional(),
544
516
  type: z2.string().min(1),
545
517
  name: z2.string().min(1),
546
518
  description: z2.string().optional(),
547
519
  validUntil: z2.string().optional(),
548
520
  context: contextDataSchema2,
521
+ contextVersion: z2.literal(2).optional(),
522
+ /** @deprecated Use `contextVersion`. Accepted on ingest only. */
549
523
  version: z2.literal(2).optional(),
550
524
  actions: z2.array(taskActionSchema2).min(1, "At least one action is required")
551
525
  });
526
+ taskContextObjectSchema2 = taskContextObjectBaseSchema2.transform(
527
+ normalizeTaskContextVersion2
528
+ );
552
529
  taskContextSchema2 = taskContextObjectSchema2.superRefine(
553
530
  refineContextPublicUrls2
554
531
  );
@@ -582,56 +559,10 @@ var init_schemas2 = __esm({
582
559
  });
583
560
  taskPriorities2 = ["low", "normal", "high", "urgent"];
584
561
  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
562
  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({
563
+ version: z2.string().min(1)
564
+ });
565
+ createTaskBodySchema2 = taskContextObjectBaseSchema2.extend({
635
566
  assignTo: assignToSchema2.optional(),
636
567
  /**
637
568
  * Groups related tasks together. When omitted, the server generates one and
@@ -649,7 +580,7 @@ var init_schemas2 = __esm({
649
580
  */
650
581
  update: threadUpdateInputSchema2.optional(),
651
582
  agent: agentTelemetrySchema2.optional()
652
- }).superRefine(refineContextPublicUrls2);
583
+ }).transform(normalizeTaskContextVersion2).superRefine(refineContextPublicUrls2);
653
584
  threadUpdateBodySchema = threadUpdateInputSchema2;
654
585
  }
655
586
  });
@@ -691,6 +622,10 @@ var init_approval_result = __esm({
691
622
  function sleep(ms) {
692
623
  return new Promise((resolve) => setTimeout(resolve, ms));
693
624
  }
625
+ function resolveAgentVersionFromEnv() {
626
+ const fromEnv = process.env.AGENT_VERSION?.trim() || process.env.ROBOTROCK_AGENT_VERSION?.trim();
627
+ return fromEnv || void 0;
628
+ }
694
629
  function parseValidUntilMs(value) {
695
630
  if (value === void 0) {
696
631
  return void 0;
@@ -744,6 +679,7 @@ function normalizeSendToHumanInput(task2, clientDefaults) {
744
679
  threadId: _threadId,
745
680
  priority: _priority,
746
681
  update: _update,
682
+ version: _version,
747
683
  validUntil,
748
684
  app: taskApp,
749
685
  ...rest
@@ -753,7 +689,7 @@ function normalizeSendToHumanInput(task2, clientDefaults) {
753
689
  const app = taskApp ?? clientDefaults.app;
754
690
  return {
755
691
  ...rest,
756
- version: clientDefaults.version,
692
+ contextVersion: clientDefaults.contextVersion,
757
693
  ...app ? { app } : {},
758
694
  ...validUntil !== void 0 ? { validUntil: serializeValidUntil(validUntil) } : {},
759
695
  actions: normalizedActions
@@ -811,7 +747,8 @@ var init_client = __esm({
811
747
  apiKey;
812
748
  baseUrl;
813
749
  app;
814
- version;
750
+ agentVersion;
751
+ contextVersion;
815
752
  webhook;
816
753
  polling;
817
754
  constructor(config) {
@@ -830,7 +767,8 @@ var init_client = __esm({
830
767
  const rawBase = config.baseUrl ?? "https://api.robotrock.io/v1";
831
768
  this.baseUrl = rawBase.replace(/\/+$/, "");
832
769
  this.app = config.app;
833
- this.version = config.version ?? 2;
770
+ this.agentVersion = config.version ?? resolveAgentVersionFromEnv();
771
+ this.contextVersion = config.advanced?.contextVersion ?? TASK_CONTEXT_FORMAT_VERSION2;
834
772
  this.webhook = config.webhook;
835
773
  this.polling = config.polling ?? {};
836
774
  }
@@ -841,15 +779,17 @@ var init_client = __esm({
841
779
  const normalizedTask = normalizeSendToHumanInput(task2, {
842
780
  webhook: this.webhook,
843
781
  app: this.app,
844
- version: this.version
782
+ contextVersion: this.contextVersion,
783
+ agentVersion: this.agentVersion
845
784
  });
785
+ const agentVersion = task2.version ?? this.agentVersion;
846
786
  const bodyPayload = {
847
787
  ...normalizedTask,
848
788
  ...task2.assignTo !== void 0 ? { assignTo: task2.assignTo } : {},
849
789
  ...task2.threadId !== void 0 ? { threadId: task2.threadId } : {},
850
790
  ...task2.priority !== void 0 ? { priority: task2.priority } : {},
851
791
  ...task2.update !== void 0 ? { update: task2.update } : {},
852
- ...task2.agent !== void 0 ? { agent: task2.agent } : {}
792
+ ...agentVersion !== void 0 ? { agent: { version: agentVersion } } : {}
853
793
  };
854
794
  const validation = createTaskBodySchema2.safeParse(bodyPayload);
855
795
  if (!validation.success) {
@@ -885,7 +825,8 @@ var init_client = __esm({
885
825
  const normalizedTask = normalizeSendToHumanInput(task2, {
886
826
  webhook: this.webhook,
887
827
  app: this.app,
888
- version: this.version
828
+ contextVersion: this.contextVersion,
829
+ agentVersion: this.agentVersion
889
830
  });
890
831
  const createdTaskTask = await this.createTask(task2);
891
832
  const hasHandlers = normalizedTask.actions.some(
@@ -1027,6 +968,212 @@ var init_env = __esm({
1027
968
  }
1028
969
  });
1029
970
 
971
+ // src/record-handled-to-otel.ts
972
+ import {
973
+ context,
974
+ trace,
975
+ SpanStatusCode
976
+ } from "@opentelemetry/api";
977
+ function isValidTraceId(traceId) {
978
+ return typeof traceId === "string" && traceId.length > 0 && traceId !== INVALID_TRACE_ID;
979
+ }
980
+ function resolveSpanContext(span) {
981
+ const active = span ?? trace.getActiveSpan();
982
+ const ctx = active?.spanContext();
983
+ if (!ctx || !isValidTraceId(ctx.traceId)) {
984
+ return void 0;
985
+ }
986
+ return { traceId: ctx.traceId, spanId: ctx.spanId };
987
+ }
988
+ function parseTaskCreatedAt(submittedAt) {
989
+ if (submittedAt) {
990
+ const parsed = Date.parse(submittedAt);
991
+ if (!Number.isNaN(parsed)) {
992
+ return parsed;
993
+ }
994
+ }
995
+ return Date.now();
996
+ }
997
+ function parseHandledAtMs(handledAt) {
998
+ if (handledAt instanceof Date) {
999
+ return handledAt.getTime();
1000
+ }
1001
+ if (typeof handledAt === "number") {
1002
+ return handledAt;
1003
+ }
1004
+ const parsed = Date.parse(handledAt);
1005
+ return Number.isNaN(parsed) ? Date.now() : parsed;
1006
+ }
1007
+ function toRobotRockHandledOtelInput(handled) {
1008
+ if ("actionId" in handled) {
1009
+ return {
1010
+ taskId: handled.taskId,
1011
+ action: {
1012
+ id: handled.actionId,
1013
+ data: handled.data
1014
+ },
1015
+ handledBy: handled.handledBy,
1016
+ handledAt: handled.handledAt
1017
+ };
1018
+ }
1019
+ return {
1020
+ taskId: handled.taskId,
1021
+ action: {
1022
+ id: handled.action.id,
1023
+ title: handled.action.title,
1024
+ data: handled.action.data
1025
+ },
1026
+ handledBy: handled.handledBy,
1027
+ handledAt: handled.handledAt
1028
+ };
1029
+ }
1030
+ function captureRobotRockOtelHandle(task2, options) {
1031
+ const spanCtx = resolveSpanContext(options?.span);
1032
+ return {
1033
+ traceId: spanCtx?.traceId ?? "",
1034
+ spanId: spanCtx?.spanId ?? "",
1035
+ taskId: task2.taskId,
1036
+ threadId: task2.threadId,
1037
+ taskCreatedAt: parseTaskCreatedAt(task2.submittedAt)
1038
+ };
1039
+ }
1040
+ function startRobotRockHumanWaitSpan(handle, options) {
1041
+ const parent = options?.span ?? trace.getActiveSpan();
1042
+ if (!parent) {
1043
+ return void 0;
1044
+ }
1045
+ const tracer = trace.getTracer(options?.tracerName ?? TRACER_NAME);
1046
+ const span = tracer.startSpan(
1047
+ WAIT_SPAN_NAME,
1048
+ {},
1049
+ trace.setSpan(context.active(), parent)
1050
+ );
1051
+ span.setAttribute("robotrock.task_id", handle.taskId);
1052
+ if (handle.threadId) {
1053
+ span.setAttribute("robotrock.thread_id", handle.threadId);
1054
+ }
1055
+ return span;
1056
+ }
1057
+ function recordRobotRockHandledToOtel(handled, options = {}) {
1058
+ const span = options.span ?? trace.getActiveSpan();
1059
+ if (!span) {
1060
+ return;
1061
+ }
1062
+ const input = toRobotRockHandledOtelInput(handled);
1063
+ const handledAtMs = parseHandledAtMs(input.handledAt);
1064
+ const handle = options.handle;
1065
+ const humanWaitMs = handle != null ? Math.max(0, handledAtMs - handle.taskCreatedAt) : void 0;
1066
+ span.setAttribute("robotrock.task_id", input.taskId);
1067
+ span.setAttribute("robotrock.action.id", input.action.id);
1068
+ if (input.action.title) {
1069
+ span.setAttribute("robotrock.action.title", input.action.title);
1070
+ }
1071
+ if (input.handledBy) {
1072
+ span.setAttribute("robotrock.handled_by", input.handledBy);
1073
+ }
1074
+ span.setAttribute("robotrock.handled_at", new Date(handledAtMs).toISOString());
1075
+ if (humanWaitMs != null) {
1076
+ span.setAttribute("robotrock.human_wait_ms", humanWaitMs);
1077
+ }
1078
+ if (input.threadId) {
1079
+ span.setAttribute("robotrock.thread_id", input.threadId);
1080
+ }
1081
+ const eventAttributes = {
1082
+ "robotrock.task_id": input.taskId,
1083
+ "robotrock.action.id": input.action.id
1084
+ };
1085
+ if (input.handledBy) {
1086
+ eventAttributes["robotrock.handled_by"] = input.handledBy;
1087
+ }
1088
+ if (humanWaitMs != null) {
1089
+ eventAttributes["robotrock.human_wait_ms"] = humanWaitMs;
1090
+ }
1091
+ if (options.includeActionData && input.action.data != null) {
1092
+ const serialized = JSON.stringify(input.action.data);
1093
+ const truncated = serialized.length > 512 ? `${serialized.slice(0, 512)}\u2026` : serialized;
1094
+ span.setAttribute("robotrock.action.data", truncated);
1095
+ eventAttributes["robotrock.action.data"] = truncated;
1096
+ }
1097
+ span.addEvent(HANDLED_EVENT_NAME, eventAttributes);
1098
+ }
1099
+ function endRobotRockHumanWaitSpan(span, handled, options = {}) {
1100
+ if (!span) {
1101
+ return;
1102
+ }
1103
+ const outcome = options.outcome ?? (handled ? "handled" : "timeout");
1104
+ if (handled && outcome === "handled") {
1105
+ recordRobotRockHandledToOtel(handled, {
1106
+ span,
1107
+ handle: options.handle,
1108
+ includeActionData: options.includeActionData
1109
+ });
1110
+ span.setStatus({ code: SpanStatusCode.OK });
1111
+ } else {
1112
+ span.setAttribute("robotrock.outcome", outcome);
1113
+ span.setStatus({
1114
+ code: SpanStatusCode.ERROR,
1115
+ message: outcome === "timeout" ? "Human response timed out before validUntil" : "Human wait failed"
1116
+ });
1117
+ }
1118
+ span.end();
1119
+ }
1120
+ var TRACER_NAME, WAIT_SPAN_NAME, HANDLED_EVENT_NAME, INVALID_TRACE_ID;
1121
+ var init_record_handled_to_otel = __esm({
1122
+ "src/record-handled-to-otel.ts"() {
1123
+ "use strict";
1124
+ TRACER_NAME = "robotrock";
1125
+ WAIT_SPAN_NAME = "robotrock.wait_for_human";
1126
+ HANDLED_EVENT_NAME = "robotrock.task_handled";
1127
+ INVALID_TRACE_ID = "00000000000000000000000000000000";
1128
+ }
1129
+ });
1130
+
1131
+ // src/otel-platform.ts
1132
+ function shouldRecordRobotRockOtel(recordOtel) {
1133
+ if (recordOtel === true) {
1134
+ return true;
1135
+ }
1136
+ if (recordOtel === false) {
1137
+ return false;
1138
+ }
1139
+ const env = process.env.ROBOTROCK_OTEL_RECORD_HANDLED;
1140
+ return env === "true" || env === "1";
1141
+ }
1142
+ function stripPlatformOtelFields(payload) {
1143
+ const { recordOtel: _recordOtel, otelIncludeActionData: _otelIncludeActionData, ...rest } = payload;
1144
+ return rest;
1145
+ }
1146
+ function beginRobotRockHumanWaitOtel(task2, options = {}) {
1147
+ const recordOtel = shouldRecordRobotRockOtel(options.recordOtel);
1148
+ if (!recordOtel) {
1149
+ return { recordOtel: false };
1150
+ }
1151
+ const handle = captureRobotRockOtelHandle(task2);
1152
+ const waitSpan = startRobotRockHumanWaitSpan(handle);
1153
+ return {
1154
+ recordOtel: true,
1155
+ handle,
1156
+ waitSpan,
1157
+ includeActionData: options.otelIncludeActionData
1158
+ };
1159
+ }
1160
+ function finishRobotRockHumanWaitOtel(session, handled, outcome) {
1161
+ if (!session.recordOtel) {
1162
+ return;
1163
+ }
1164
+ endRobotRockHumanWaitSpan(session.waitSpan, handled, {
1165
+ handle: session.handle,
1166
+ outcome,
1167
+ includeActionData: session.includeActionData
1168
+ });
1169
+ }
1170
+ var init_otel_platform = __esm({
1171
+ "src/otel-platform.ts"() {
1172
+ "use strict";
1173
+ init_record_handled_to_otel();
1174
+ }
1175
+ });
1176
+
1030
1177
  // src/handler-webhook.ts
1031
1178
  function isRobotRockHandlerWebhookPayload(value) {
1032
1179
  if (typeof value !== "object" || value === null) {
@@ -1107,7 +1254,7 @@ __export(trigger_exports, {
1107
1254
  });
1108
1255
  import { task, wait } from "@trigger.dev/sdk";
1109
1256
  async function runSendToHuman(payload) {
1110
- const { validUntil: validUntilInput, ...taskInput } = payload;
1257
+ const { validUntil: validUntilInput, app, recordOtel, otelIncludeActionData, ...taskFields } = payload;
1111
1258
  const { validUntil, timeout } = resolveWaitTiming(validUntilInput);
1112
1259
  const token = await wait.createToken({ timeout });
1113
1260
  const baseConfig = resolveRobotRockConfig();
@@ -1116,45 +1263,74 @@ async function runSendToHuman(payload) {
1116
1263
  baseUrl: baseConfig.baseUrl,
1117
1264
  ...baseConfig.app ? { app: baseConfig.app } : {},
1118
1265
  ...baseConfig.version ? { version: baseConfig.version } : {},
1266
+ ...baseConfig.advanced ? { advanced: baseConfig.advanced } : {},
1119
1267
  webhook: { url: token.url }
1120
1268
  });
1269
+ const taskInput = stripPlatformOtelFields(taskFields);
1121
1270
  const sendResult = await client.sendToHuman({
1122
1271
  ...taskInput,
1123
- validUntil
1272
+ validUntil,
1273
+ ...app !== void 0 ? { app } : {}
1124
1274
  });
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
1275
+ const otelSession = beginRobotRockHumanWaitOtel(sendResult.task, {
1276
+ recordOtel,
1277
+ otelIncludeActionData
1278
+ });
1279
+ let handledPayload = null;
1280
+ let waitOutcome = "pending";
1281
+ try {
1282
+ const outcome = await wait.forToken(token.id);
1283
+ if (!outcome.ok) {
1284
+ waitOutcome = "timeout";
1285
+ throw new Error(`Human response timed out before validUntil (${timeout})`);
1286
+ }
1287
+ const output = outcome.output;
1288
+ if (!isRobotRockHandlerWebhookPayload(output)) {
1289
+ waitOutcome = "error";
1290
+ throw new Error(
1291
+ "Wait token completed with unexpected payload; expected RobotRock handler body (taskId, action.id, action.data)."
1292
+ );
1293
+ }
1294
+ handledPayload = output;
1295
+ waitOutcome = "handled";
1296
+ return toDiscriminatedApprovalResult(
1297
+ payload.actions,
1298
+ {
1299
+ id: output.taskId,
1300
+ createdAt: /* @__PURE__ */ new Date(),
1301
+ status: "handled",
1302
+ context: sendResult.task.context,
1303
+ validUntil: Date.now(),
1304
+ handledAt: new Date(output.handledAt).getTime(),
1305
+ handled: {
1306
+ action: {
1307
+ id: output.action.id,
1308
+ data: output.action.data
1309
+ },
1310
+ handledBy: output.handledBy
1311
+ }
1150
1312
  }
1313
+ );
1314
+ } catch (error) {
1315
+ if (waitOutcome === "pending") {
1316
+ waitOutcome = "error";
1151
1317
  }
1152
- );
1318
+ throw error;
1319
+ } finally {
1320
+ if (waitOutcome !== "pending") {
1321
+ finishRobotRockHumanWaitOtel(
1322
+ otelSession,
1323
+ handledPayload,
1324
+ waitOutcome === "handled" ? "handled" : waitOutcome
1325
+ );
1326
+ }
1327
+ }
1153
1328
  }
1154
1329
  var APPROVE_BY_HUMAN_ACTIONS, sendToHumanTask, approveByHumanTask;
1155
1330
  var init_trigger = __esm({
1156
1331
  "src/trigger/index.ts"() {
1157
1332
  "use strict";
1333
+ init_otel_platform();
1158
1334
  init_client();
1159
1335
  init_env();
1160
1336
  init_approval_result();
@@ -1194,6 +1370,7 @@ async function createRobotRockTaskForWebhook(input) {
1194
1370
  baseUrl: baseConfig.baseUrl,
1195
1371
  ...input.app ?? baseConfig.app ? { app: input.app ?? baseConfig.app } : {},
1196
1372
  ...baseConfig.version ? { version: baseConfig.version } : {},
1373
+ ...baseConfig.advanced ? { advanced: baseConfig.advanced } : {},
1197
1374
  webhook: { url: input.webhookUrl }
1198
1375
  });
1199
1376
  return client.sendToHuman({
@@ -1214,7 +1391,7 @@ async function parseRobotRockWebhookRequest(request) {
1214
1391
  async function sendToHumanInWorkflow(payload) {
1215
1392
  var _stack = [];
1216
1393
  try {
1217
- const { validUntil: validUntilInput, app, ...taskInput } = payload;
1394
+ const { validUntil: validUntilInput, app, recordOtel, otelIncludeActionData, ...taskFields } = payload;
1218
1395
  const { validUntil, timeout } = resolveWaitTiming(validUntilInput);
1219
1396
  const timeoutMs = parseValidUntilMs2(validUntil) - Date.now();
1220
1397
  const webhook = __using(_stack, createWebhook());
@@ -1222,31 +1399,54 @@ async function sendToHumanInWorkflow(payload) {
1222
1399
  webhookUrl: webhook.url,
1223
1400
  app,
1224
1401
  validUntil,
1225
- taskInput
1402
+ taskInput: stripPlatformOtelFields(taskFields)
1226
1403
  });
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
- }
1404
+ const otelSession = beginRobotRockHumanWaitOtel(sendResult.task, {
1405
+ recordOtel,
1406
+ otelIncludeActionData
1249
1407
  });
1408
+ let handledPayload = null;
1409
+ let waitOutcome = "pending";
1410
+ try {
1411
+ const outcome = await Promise.race([
1412
+ webhook.then((request) => parseRobotRockWebhookRequest(request)),
1413
+ sleep2(timeoutMs).then(() => ({ timedOut: true }))
1414
+ ]);
1415
+ if ("timedOut" in outcome) {
1416
+ waitOutcome = "timeout";
1417
+ throw new Error(`Human response timed out before validUntil (${timeout})`);
1418
+ }
1419
+ handledPayload = outcome;
1420
+ waitOutcome = "handled";
1421
+ return toDiscriminatedApprovalResult(payload.actions, {
1422
+ id: outcome.taskId,
1423
+ createdAt: /* @__PURE__ */ new Date(),
1424
+ status: "handled",
1425
+ context: sendResult.task.context,
1426
+ validUntil: Date.now(),
1427
+ handledAt: new Date(outcome.handledAt).getTime(),
1428
+ handled: {
1429
+ action: {
1430
+ id: outcome.action.id,
1431
+ data: outcome.action.data
1432
+ },
1433
+ handledBy: outcome.handledBy
1434
+ }
1435
+ });
1436
+ } catch (error) {
1437
+ if (waitOutcome === "pending") {
1438
+ waitOutcome = "error";
1439
+ }
1440
+ throw error;
1441
+ } finally {
1442
+ if (waitOutcome !== "pending") {
1443
+ finishRobotRockHumanWaitOtel(
1444
+ otelSession,
1445
+ handledPayload,
1446
+ waitOutcome === "handled" ? "handled" : waitOutcome
1447
+ );
1448
+ }
1449
+ }
1250
1450
  } catch (_) {
1251
1451
  var _error = _, _hasError = true;
1252
1452
  } finally {
@@ -1267,7 +1467,8 @@ async function sendRobotRockUpdate(payload) {
1267
1467
  apiKey: baseConfig.apiKey,
1268
1468
  baseUrl: baseConfig.baseUrl,
1269
1469
  ...app ?? baseConfig.app ? { app: app ?? baseConfig.app } : {},
1270
- ...baseConfig.version ? { version: baseConfig.version } : {}
1470
+ ...baseConfig.version ? { version: baseConfig.version } : {},
1471
+ ...baseConfig.advanced ? { advanced: baseConfig.advanced } : {}
1271
1472
  });
1272
1473
  return client.sendUpdate(update);
1273
1474
  }
@@ -1283,6 +1484,7 @@ var init_workflow = __esm({
1283
1484
  init_approval_result();
1284
1485
  init_handler_webhook();
1285
1486
  init_wait_timing();
1487
+ init_otel_platform();
1286
1488
  APPROVE_BY_HUMAN_ACTIONS2 = [
1287
1489
  { id: "approve", title: "Approve" },
1288
1490
  { id: "decline", title: "Decline" }
@@ -1324,27 +1526,27 @@ function normalizeRobotRockAiContext(clientOrContext) {
1324
1526
  }
1325
1527
  throw new Error(`Unknown RobotRock AI mode: ${String(clientOrContext.mode)}`);
1326
1528
  }
1327
- async function sendToHumanForAi(context, payload) {
1328
- if (context.mode === "trigger") {
1529
+ async function sendToHumanForAi(context2, payload) {
1530
+ if (context2.mode === "trigger") {
1329
1531
  const { sendToHumanTask: sendToHumanTask2 } = await Promise.resolve().then(() => (init_trigger(), trigger_exports));
1330
1532
  const waitResult = await sendToHumanTask2.triggerAndWait({
1331
1533
  ...payload,
1332
- ...context.app ? { app: context.app } : {}
1534
+ ...context2.app ? { app: context2.app } : {}
1333
1535
  });
1334
1536
  if (!waitResult.ok) {
1335
1537
  throw waitResult.error;
1336
1538
  }
1337
1539
  return waitResult.output;
1338
1540
  }
1339
- if (context.mode === "workflow") {
1541
+ if (context2.mode === "workflow") {
1340
1542
  const { sendToHumanInWorkflow: sendToHumanInWorkflow2 } = await Promise.resolve().then(() => (init_workflow(), workflow_exports));
1341
1543
  const result2 = await sendToHumanInWorkflow2({
1342
1544
  ...payload,
1343
- ...context.app ? { app: context.app } : {}
1545
+ ...context2.app ? { app: context2.app } : {}
1344
1546
  });
1345
1547
  return result2;
1346
1548
  }
1347
- const result = await context.client.sendToHuman(payload);
1549
+ const result = await context2.client.sendToHuman(payload);
1348
1550
  if (result.mode !== "handled") {
1349
1551
  throw new Error(
1350
1552
  "RobotRock task was created but not handled. Configure client polling or webhook, or use mode: 'trigger' / 'workflow' for durable waits."
@@ -1358,42 +1560,42 @@ async function sendToHumanForAi(context, payload) {
1358
1560
  taskId: result.taskId
1359
1561
  };
1360
1562
  }
1361
- async function sendUpdateForAi(context, payload) {
1362
- if (context.mode === "workflow") {
1563
+ async function sendUpdateForAi(context2, payload) {
1564
+ if (context2.mode === "workflow") {
1363
1565
  const { sendUpdateInWorkflow: sendUpdateInWorkflow2 } = await Promise.resolve().then(() => (init_workflow(), workflow_exports));
1364
1566
  return sendUpdateInWorkflow2({
1365
1567
  ...payload,
1366
- ...context.app ? { app: context.app } : {}
1568
+ ...context2.app ? { app: context2.app } : {}
1367
1569
  });
1368
1570
  }
1369
- if (context.mode === "trigger") {
1571
+ if (context2.mode === "trigger") {
1370
1572
  const client = createClient(
1371
- resolveRobotRockConfig(context.app ? { app: context.app } : void 0)
1573
+ resolveRobotRockConfig(context2.app ? { app: context2.app } : void 0)
1372
1574
  );
1373
1575
  return client.sendUpdate(payload);
1374
1576
  }
1375
- return context.client.sendUpdate(payload);
1577
+ return context2.client.sendUpdate(payload);
1376
1578
  }
1377
- async function approveByHumanForAi(context, payload) {
1378
- if (context.mode === "trigger") {
1579
+ async function approveByHumanForAi(context2, payload) {
1580
+ if (context2.mode === "trigger") {
1379
1581
  const { approveByHumanTask: approveByHumanTask2 } = await Promise.resolve().then(() => (init_trigger(), trigger_exports));
1380
1582
  const waitResult = await approveByHumanTask2.triggerAndWait({
1381
1583
  ...payload,
1382
- ...context.app ? { app: context.app } : {}
1584
+ ...context2.app ? { app: context2.app } : {}
1383
1585
  });
1384
1586
  if (!waitResult.ok) {
1385
1587
  throw waitResult.error;
1386
1588
  }
1387
1589
  return waitResult.output;
1388
1590
  }
1389
- if (context.mode === "workflow") {
1591
+ if (context2.mode === "workflow") {
1390
1592
  const { approveByHumanInWorkflow: approveByHumanInWorkflow2 } = await Promise.resolve().then(() => (init_workflow(), workflow_exports));
1391
1593
  return await approveByHumanInWorkflow2({
1392
1594
  ...payload,
1393
- ...context.app ? { app: context.app } : {}
1595
+ ...context2.app ? { app: context2.app } : {}
1394
1596
  });
1395
1597
  }
1396
- const result = await context.client.sendToHuman({
1598
+ const result = await context2.client.sendToHuman({
1397
1599
  ...payload,
1398
1600
  actions: APPROVE_BY_HUMAN_ACTIONS3
1399
1601
  });
@@ -1613,12 +1815,6 @@ function createSendUpdateTool(clientOrOptions, maybeOptions = {}) {
1613
1815
  function defaultFormatToolApprovalTask(toolCall, options = {}) {
1614
1816
  const approveId = options.approveActionId ?? "approve";
1615
1817
  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
1818
  return {
1623
1819
  type: options.type ?? "ai-tool-approval",
1624
1820
  name: `Approve: ${toolCall.toolName}`,
@@ -1816,7 +2012,7 @@ function resolveToolCallForRequest(request, source, messages) {
1816
2012
  );
1817
2013
  }
1818
2014
  async function resolveToolApprovalsViaRobotRock(clientOrContext, source, options = {}) {
1819
- const context = normalizeRobotRockAiContext(clientOrContext);
2015
+ const context2 = normalizeRobotRockAiContext(clientOrContext);
1820
2016
  const approveId = options.approveActionId ?? "approve";
1821
2017
  const denyId = options.denyActionId ?? "deny";
1822
2018
  const formatTask = options.formatTask ?? defaultFormatToolApprovalTask;
@@ -1829,7 +2025,7 @@ async function resolveToolApprovalsViaRobotRock(clientOrContext, source, options
1829
2025
  approveActionId: approveId,
1830
2026
  denyActionId: denyId
1831
2027
  });
1832
- const result = await sendToHumanForAi(context, taskInput);
2028
+ const result = await sendToHumanForAi(context2, taskInput);
1833
2029
  const approved = result.actionId === approveId;
1834
2030
  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
2031
  responses.push({
@@ -1873,11 +2069,11 @@ function createRobotRockAiTools(options) {
1873
2069
  if (mode === "polling" && !options.client) {
1874
2070
  throw new Error("createRobotRockAiTools: polling mode requires `client`.");
1875
2071
  }
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;
2072
+ const context2 = mode === "trigger" ? { mode: "trigger", app: options.app } : mode === "workflow" ? { mode: "workflow", app: options.app } : { mode: "polling", client: options.client };
2073
+ const durableContext = context2.mode === "trigger" || context2.mode === "workflow" ? context2 : null;
2074
+ const pollingClient = context2.mode === "polling" ? context2.client : void 0;
1879
2075
  return {
1880
- context,
2076
+ context: context2,
1881
2077
  approveByHuman: (toolOptions) => durableContext ? approveByHumanTool({ ...durableContext, ...toolOptions }) : approveByHumanTool(pollingClient, toolOptions),
1882
2078
  sendToHuman: (toolOptions) => durableContext ? createSendToHumanTool({
1883
2079
  ...durableContext,