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.
package/dist/ai/index.js CHANGED
@@ -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" }
@@ -1327,27 +1529,27 @@ function normalizeRobotRockAiContext(clientOrContext) {
1327
1529
  }
1328
1530
  throw new Error(`Unknown RobotRock AI mode: ${String(clientOrContext.mode)}`);
1329
1531
  }
1330
- async function sendToHumanForAi(context, payload) {
1331
- if (context.mode === "trigger") {
1532
+ async function sendToHumanForAi(context2, payload) {
1533
+ if (context2.mode === "trigger") {
1332
1534
  const { sendToHumanTask: sendToHumanTask2 } = await Promise.resolve().then(() => (init_trigger(), trigger_exports));
1333
1535
  const waitResult = await sendToHumanTask2.triggerAndWait({
1334
1536
  ...payload,
1335
- ...context.app ? { app: context.app } : {}
1537
+ ...context2.app ? { app: context2.app } : {}
1336
1538
  });
1337
1539
  if (!waitResult.ok) {
1338
1540
  throw waitResult.error;
1339
1541
  }
1340
1542
  return waitResult.output;
1341
1543
  }
1342
- if (context.mode === "workflow") {
1544
+ if (context2.mode === "workflow") {
1343
1545
  const { sendToHumanInWorkflow: sendToHumanInWorkflow2 } = await Promise.resolve().then(() => (init_workflow(), workflow_exports));
1344
1546
  const result2 = await sendToHumanInWorkflow2({
1345
1547
  ...payload,
1346
- ...context.app ? { app: context.app } : {}
1548
+ ...context2.app ? { app: context2.app } : {}
1347
1549
  });
1348
1550
  return result2;
1349
1551
  }
1350
- const result = await context.client.sendToHuman(payload);
1552
+ const result = await context2.client.sendToHuman(payload);
1351
1553
  if (result.mode !== "handled") {
1352
1554
  throw new Error(
1353
1555
  "RobotRock task was created but not handled. Configure client polling or webhook, or use mode: 'trigger' / 'workflow' for durable waits."
@@ -1361,42 +1563,42 @@ async function sendToHumanForAi(context, payload) {
1361
1563
  taskId: result.taskId
1362
1564
  };
1363
1565
  }
1364
- async function sendUpdateForAi(context, payload) {
1365
- if (context.mode === "workflow") {
1566
+ async function sendUpdateForAi(context2, payload) {
1567
+ if (context2.mode === "workflow") {
1366
1568
  const { sendUpdateInWorkflow: sendUpdateInWorkflow2 } = await Promise.resolve().then(() => (init_workflow(), workflow_exports));
1367
1569
  return sendUpdateInWorkflow2({
1368
1570
  ...payload,
1369
- ...context.app ? { app: context.app } : {}
1571
+ ...context2.app ? { app: context2.app } : {}
1370
1572
  });
1371
1573
  }
1372
- if (context.mode === "trigger") {
1574
+ if (context2.mode === "trigger") {
1373
1575
  const client = createClient(
1374
- resolveRobotRockConfig(context.app ? { app: context.app } : void 0)
1576
+ resolveRobotRockConfig(context2.app ? { app: context2.app } : void 0)
1375
1577
  );
1376
1578
  return client.sendUpdate(payload);
1377
1579
  }
1378
- return context.client.sendUpdate(payload);
1580
+ return context2.client.sendUpdate(payload);
1379
1581
  }
1380
- async function approveByHumanForAi(context, payload) {
1381
- if (context.mode === "trigger") {
1582
+ async function approveByHumanForAi(context2, payload) {
1583
+ if (context2.mode === "trigger") {
1382
1584
  const { approveByHumanTask: approveByHumanTask2 } = await Promise.resolve().then(() => (init_trigger(), trigger_exports));
1383
1585
  const waitResult = await approveByHumanTask2.triggerAndWait({
1384
1586
  ...payload,
1385
- ...context.app ? { app: context.app } : {}
1587
+ ...context2.app ? { app: context2.app } : {}
1386
1588
  });
1387
1589
  if (!waitResult.ok) {
1388
1590
  throw waitResult.error;
1389
1591
  }
1390
1592
  return waitResult.output;
1391
1593
  }
1392
- if (context.mode === "workflow") {
1594
+ if (context2.mode === "workflow") {
1393
1595
  const { approveByHumanInWorkflow: approveByHumanInWorkflow2 } = await Promise.resolve().then(() => (init_workflow(), workflow_exports));
1394
1596
  return await approveByHumanInWorkflow2({
1395
1597
  ...payload,
1396
- ...context.app ? { app: context.app } : {}
1598
+ ...context2.app ? { app: context2.app } : {}
1397
1599
  });
1398
1600
  }
1399
- const result = await context.client.sendToHuman({
1601
+ const result = await context2.client.sendToHuman({
1400
1602
  ...payload,
1401
1603
  actions: APPROVE_BY_HUMAN_ACTIONS3
1402
1604
  });
@@ -1622,11 +1824,11 @@ function createRobotRockAiTools(options) {
1622
1824
  if (mode === "polling" && !options.client) {
1623
1825
  throw new Error("createRobotRockAiTools: polling mode requires `client`.");
1624
1826
  }
1625
- const context = mode === "trigger" ? { mode: "trigger", app: options.app } : mode === "workflow" ? { mode: "workflow", app: options.app } : { mode: "polling", client: options.client };
1626
- const durableContext = context.mode === "trigger" || context.mode === "workflow" ? context : null;
1627
- const pollingClient = context.mode === "polling" ? context.client : void 0;
1827
+ const context2 = mode === "trigger" ? { mode: "trigger", app: options.app } : mode === "workflow" ? { mode: "workflow", app: options.app } : { mode: "polling", client: options.client };
1828
+ const durableContext = context2.mode === "trigger" || context2.mode === "workflow" ? context2 : null;
1829
+ const pollingClient = context2.mode === "polling" ? context2.client : void 0;
1628
1830
  return {
1629
- context,
1831
+ context: context2,
1630
1832
  approveByHuman: (toolOptions) => durableContext ? approveByHumanTool({ ...durableContext, ...toolOptions }) : approveByHumanTool(pollingClient, toolOptions),
1631
1833
  sendToHuman: (toolOptions) => durableContext ? createSendToHumanTool({
1632
1834
  ...durableContext,
@@ -1655,12 +1857,6 @@ var DEFAULT_APPROVE_ACTIONS = [
1655
1857
  function defaultFormatToolApprovalTask(toolCall, options = {}) {
1656
1858
  const approveId = options.approveActionId ?? "approve";
1657
1859
  const denyId = options.denyActionId ?? "deny";
1658
- let inputPreview;
1659
- try {
1660
- inputPreview = JSON.stringify(toolCall.input, null, 2);
1661
- } catch {
1662
- inputPreview = String(toolCall.input);
1663
- }
1664
1860
  return {
1665
1861
  type: options.type ?? "ai-tool-approval",
1666
1862
  name: `Approve: ${toolCall.toolName}`,
@@ -1858,7 +2054,7 @@ function resolveToolCallForRequest(request, source, messages) {
1858
2054
  );
1859
2055
  }
1860
2056
  async function resolveToolApprovalsViaRobotRock(clientOrContext, source, options = {}) {
1861
- const context = normalizeRobotRockAiContext(clientOrContext);
2057
+ const context2 = normalizeRobotRockAiContext(clientOrContext);
1862
2058
  const approveId = options.approveActionId ?? "approve";
1863
2059
  const denyId = options.denyActionId ?? "deny";
1864
2060
  const formatTask = options.formatTask ?? defaultFormatToolApprovalTask;
@@ -1871,7 +2067,7 @@ async function resolveToolApprovalsViaRobotRock(clientOrContext, source, options
1871
2067
  approveActionId: approveId,
1872
2068
  denyActionId: denyId
1873
2069
  });
1874
- const result = await sendToHumanForAi(context, taskInput);
2070
+ const result = await sendToHumanForAi(context2, taskInput);
1875
2071
  const approved = result.actionId === approveId;
1876
2072
  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";
1877
2073
  responses.push({