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" }
@@ -1321,27 +1523,27 @@ function normalizeRobotRockAiContext(clientOrContext) {
1321
1523
  }
1322
1524
  throw new Error(`Unknown RobotRock AI mode: ${String(clientOrContext.mode)}`);
1323
1525
  }
1324
- async function sendToHumanForAi(context, payload) {
1325
- if (context.mode === "trigger") {
1526
+ async function sendToHumanForAi(context2, payload) {
1527
+ if (context2.mode === "trigger") {
1326
1528
  const { sendToHumanTask: sendToHumanTask2 } = await Promise.resolve().then(() => (init_trigger(), trigger_exports));
1327
1529
  const waitResult = await sendToHumanTask2.triggerAndWait({
1328
1530
  ...payload,
1329
- ...context.app ? { app: context.app } : {}
1531
+ ...context2.app ? { app: context2.app } : {}
1330
1532
  });
1331
1533
  if (!waitResult.ok) {
1332
1534
  throw waitResult.error;
1333
1535
  }
1334
1536
  return waitResult.output;
1335
1537
  }
1336
- if (context.mode === "workflow") {
1538
+ if (context2.mode === "workflow") {
1337
1539
  const { sendToHumanInWorkflow: sendToHumanInWorkflow2 } = await Promise.resolve().then(() => (init_workflow(), workflow_exports));
1338
1540
  const result2 = await sendToHumanInWorkflow2({
1339
1541
  ...payload,
1340
- ...context.app ? { app: context.app } : {}
1542
+ ...context2.app ? { app: context2.app } : {}
1341
1543
  });
1342
1544
  return result2;
1343
1545
  }
1344
- const result = await context.client.sendToHuman(payload);
1546
+ const result = await context2.client.sendToHuman(payload);
1345
1547
  if (result.mode !== "handled") {
1346
1548
  throw new Error(
1347
1549
  "RobotRock task was created but not handled. Configure client polling or webhook, or use mode: 'trigger' / 'workflow' for durable waits."
@@ -1355,42 +1557,42 @@ async function sendToHumanForAi(context, payload) {
1355
1557
  taskId: result.taskId
1356
1558
  };
1357
1559
  }
1358
- async function sendUpdateForAi(context, payload) {
1359
- if (context.mode === "workflow") {
1560
+ async function sendUpdateForAi(context2, payload) {
1561
+ if (context2.mode === "workflow") {
1360
1562
  const { sendUpdateInWorkflow: sendUpdateInWorkflow2 } = await Promise.resolve().then(() => (init_workflow(), workflow_exports));
1361
1563
  return sendUpdateInWorkflow2({
1362
1564
  ...payload,
1363
- ...context.app ? { app: context.app } : {}
1565
+ ...context2.app ? { app: context2.app } : {}
1364
1566
  });
1365
1567
  }
1366
- if (context.mode === "trigger") {
1568
+ if (context2.mode === "trigger") {
1367
1569
  const client = createClient(
1368
- resolveRobotRockConfig(context.app ? { app: context.app } : void 0)
1570
+ resolveRobotRockConfig(context2.app ? { app: context2.app } : void 0)
1369
1571
  );
1370
1572
  return client.sendUpdate(payload);
1371
1573
  }
1372
- return context.client.sendUpdate(payload);
1574
+ return context2.client.sendUpdate(payload);
1373
1575
  }
1374
- async function approveByHumanForAi(context, payload) {
1375
- if (context.mode === "trigger") {
1576
+ async function approveByHumanForAi(context2, payload) {
1577
+ if (context2.mode === "trigger") {
1376
1578
  const { approveByHumanTask: approveByHumanTask2 } = await Promise.resolve().then(() => (init_trigger(), trigger_exports));
1377
1579
  const waitResult = await approveByHumanTask2.triggerAndWait({
1378
1580
  ...payload,
1379
- ...context.app ? { app: context.app } : {}
1581
+ ...context2.app ? { app: context2.app } : {}
1380
1582
  });
1381
1583
  if (!waitResult.ok) {
1382
1584
  throw waitResult.error;
1383
1585
  }
1384
1586
  return waitResult.output;
1385
1587
  }
1386
- if (context.mode === "workflow") {
1588
+ if (context2.mode === "workflow") {
1387
1589
  const { approveByHumanInWorkflow: approveByHumanInWorkflow2 } = await Promise.resolve().then(() => (init_workflow(), workflow_exports));
1388
1590
  return await approveByHumanInWorkflow2({
1389
1591
  ...payload,
1390
- ...context.app ? { app: context.app } : {}
1592
+ ...context2.app ? { app: context2.app } : {}
1391
1593
  });
1392
1594
  }
1393
- const result = await context.client.sendToHuman({
1595
+ const result = await context2.client.sendToHuman({
1394
1596
  ...payload,
1395
1597
  actions: APPROVE_BY_HUMAN_ACTIONS3
1396
1598
  });
@@ -1596,17 +1798,17 @@ function createSendUpdateTool(clientOrOptions, maybeOptions = {}) {
1596
1798
  return buildSendUpdateToolDefinition(clientOrOptions, maybeOptions);
1597
1799
  }
1598
1800
  function createRobotRockAiTools(options = {}) {
1599
- const context = { mode: "workflow", app: options.app };
1801
+ const context2 = { mode: "workflow", app: options.app };
1600
1802
  return {
1601
- context,
1602
- approveByHuman: (toolOptions) => buildApproveByHumanToolDefinition({ ...context, ...toolOptions }),
1803
+ context: context2,
1804
+ approveByHuman: (toolOptions) => buildApproveByHumanToolDefinition({ ...context2, ...toolOptions }),
1603
1805
  sendToHuman: (toolOptions) => buildSendToHumanToolDefinition({
1604
- ...context,
1806
+ ...context2,
1605
1807
  ...options.threadId ? { threadId: options.threadId } : {},
1606
1808
  ...toolOptions
1607
1809
  }),
1608
1810
  sendUpdate: (toolOptions = {}) => buildSendUpdateToolDefinition({
1609
- ...context,
1811
+ ...context2,
1610
1812
  ...options.threadId ? { threadId: options.threadId } : {},
1611
1813
  ...toolOptions
1612
1814
  })
@@ -1617,12 +1819,6 @@ function createRobotRockAiTools(options = {}) {
1617
1819
  function defaultFormatToolApprovalTask(toolCall, options = {}) {
1618
1820
  const approveId = options.approveActionId ?? "approve";
1619
1821
  const denyId = options.denyActionId ?? "deny";
1620
- let inputPreview;
1621
- try {
1622
- inputPreview = JSON.stringify(toolCall.input, null, 2);
1623
- } catch {
1624
- inputPreview = String(toolCall.input);
1625
- }
1626
1822
  return {
1627
1823
  type: options.type ?? "ai-tool-approval",
1628
1824
  name: `Approve: ${toolCall.toolName}`,
@@ -1820,7 +2016,7 @@ function resolveToolCallForRequest(request, source, messages) {
1820
2016
  );
1821
2017
  }
1822
2018
  async function resolveToolApprovalsViaRobotRock(clientOrContext, source, options = {}) {
1823
- const context = normalizeRobotRockAiContext(clientOrContext);
2019
+ const context2 = normalizeRobotRockAiContext(clientOrContext);
1824
2020
  const approveId = options.approveActionId ?? "approve";
1825
2021
  const denyId = options.denyActionId ?? "deny";
1826
2022
  const formatTask = options.formatTask ?? defaultFormatToolApprovalTask;
@@ -1833,7 +2029,7 @@ async function resolveToolApprovalsViaRobotRock(clientOrContext, source, options
1833
2029
  approveActionId: approveId,
1834
2030
  denyActionId: denyId
1835
2031
  });
1836
- const result = await sendToHumanForAi(context, taskInput);
2032
+ const result = await sendToHumanForAi(context2, taskInput);
1837
2033
  const approved = result.actionId === approveId;
1838
2034
  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";
1839
2035
  responses.push({