robotrock 0.8.4 → 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.
@@ -1,6 +1,200 @@
1
1
  // src/trigger/index.ts
2
2
  import { task, wait } from "@trigger.dev/sdk";
3
3
 
4
+ // src/record-handled-to-otel.ts
5
+ import {
6
+ context,
7
+ trace,
8
+ SpanStatusCode
9
+ } from "@opentelemetry/api";
10
+ var TRACER_NAME = "robotrock";
11
+ var WAIT_SPAN_NAME = "robotrock.wait_for_human";
12
+ var HANDLED_EVENT_NAME = "robotrock.task_handled";
13
+ var INVALID_TRACE_ID = "00000000000000000000000000000000";
14
+ function isValidTraceId(traceId) {
15
+ return typeof traceId === "string" && traceId.length > 0 && traceId !== INVALID_TRACE_ID;
16
+ }
17
+ function resolveSpanContext(span) {
18
+ const active = span ?? trace.getActiveSpan();
19
+ const ctx = active?.spanContext();
20
+ if (!ctx || !isValidTraceId(ctx.traceId)) {
21
+ return void 0;
22
+ }
23
+ return { traceId: ctx.traceId, spanId: ctx.spanId };
24
+ }
25
+ function parseTaskCreatedAt(submittedAt) {
26
+ if (submittedAt) {
27
+ const parsed = Date.parse(submittedAt);
28
+ if (!Number.isNaN(parsed)) {
29
+ return parsed;
30
+ }
31
+ }
32
+ return Date.now();
33
+ }
34
+ function parseHandledAtMs(handledAt) {
35
+ if (handledAt instanceof Date) {
36
+ return handledAt.getTime();
37
+ }
38
+ if (typeof handledAt === "number") {
39
+ return handledAt;
40
+ }
41
+ const parsed = Date.parse(handledAt);
42
+ return Number.isNaN(parsed) ? Date.now() : parsed;
43
+ }
44
+ function toRobotRockHandledOtelInput(handled) {
45
+ if ("actionId" in handled) {
46
+ return {
47
+ taskId: handled.taskId,
48
+ action: {
49
+ id: handled.actionId,
50
+ data: handled.data
51
+ },
52
+ handledBy: handled.handledBy,
53
+ handledAt: handled.handledAt
54
+ };
55
+ }
56
+ return {
57
+ taskId: handled.taskId,
58
+ action: {
59
+ id: handled.action.id,
60
+ title: handled.action.title,
61
+ data: handled.action.data
62
+ },
63
+ handledBy: handled.handledBy,
64
+ handledAt: handled.handledAt
65
+ };
66
+ }
67
+ function captureRobotRockOtelHandle(task2, options) {
68
+ const spanCtx = resolveSpanContext(options?.span);
69
+ return {
70
+ traceId: spanCtx?.traceId ?? "",
71
+ spanId: spanCtx?.spanId ?? "",
72
+ taskId: task2.taskId,
73
+ threadId: task2.threadId,
74
+ taskCreatedAt: parseTaskCreatedAt(task2.submittedAt)
75
+ };
76
+ }
77
+ function startRobotRockHumanWaitSpan(handle, options) {
78
+ const parent = options?.span ?? trace.getActiveSpan();
79
+ if (!parent) {
80
+ return void 0;
81
+ }
82
+ const tracer = trace.getTracer(options?.tracerName ?? TRACER_NAME);
83
+ const span = tracer.startSpan(
84
+ WAIT_SPAN_NAME,
85
+ {},
86
+ trace.setSpan(context.active(), parent)
87
+ );
88
+ span.setAttribute("robotrock.task_id", handle.taskId);
89
+ if (handle.threadId) {
90
+ span.setAttribute("robotrock.thread_id", handle.threadId);
91
+ }
92
+ return span;
93
+ }
94
+ function recordRobotRockHandledToOtel(handled, options = {}) {
95
+ const span = options.span ?? trace.getActiveSpan();
96
+ if (!span) {
97
+ return;
98
+ }
99
+ const input = toRobotRockHandledOtelInput(handled);
100
+ const handledAtMs = parseHandledAtMs(input.handledAt);
101
+ const handle = options.handle;
102
+ const humanWaitMs = handle != null ? Math.max(0, handledAtMs - handle.taskCreatedAt) : void 0;
103
+ span.setAttribute("robotrock.task_id", input.taskId);
104
+ span.setAttribute("robotrock.action.id", input.action.id);
105
+ if (input.action.title) {
106
+ span.setAttribute("robotrock.action.title", input.action.title);
107
+ }
108
+ if (input.handledBy) {
109
+ span.setAttribute("robotrock.handled_by", input.handledBy);
110
+ }
111
+ span.setAttribute("robotrock.handled_at", new Date(handledAtMs).toISOString());
112
+ if (humanWaitMs != null) {
113
+ span.setAttribute("robotrock.human_wait_ms", humanWaitMs);
114
+ }
115
+ if (input.threadId) {
116
+ span.setAttribute("robotrock.thread_id", input.threadId);
117
+ }
118
+ const eventAttributes = {
119
+ "robotrock.task_id": input.taskId,
120
+ "robotrock.action.id": input.action.id
121
+ };
122
+ if (input.handledBy) {
123
+ eventAttributes["robotrock.handled_by"] = input.handledBy;
124
+ }
125
+ if (humanWaitMs != null) {
126
+ eventAttributes["robotrock.human_wait_ms"] = humanWaitMs;
127
+ }
128
+ if (options.includeActionData && input.action.data != null) {
129
+ const serialized = JSON.stringify(input.action.data);
130
+ const truncated = serialized.length > 512 ? `${serialized.slice(0, 512)}\u2026` : serialized;
131
+ span.setAttribute("robotrock.action.data", truncated);
132
+ eventAttributes["robotrock.action.data"] = truncated;
133
+ }
134
+ span.addEvent(HANDLED_EVENT_NAME, eventAttributes);
135
+ }
136
+ function endRobotRockHumanWaitSpan(span, handled, options = {}) {
137
+ if (!span) {
138
+ return;
139
+ }
140
+ const outcome = options.outcome ?? (handled ? "handled" : "timeout");
141
+ if (handled && outcome === "handled") {
142
+ recordRobotRockHandledToOtel(handled, {
143
+ span,
144
+ handle: options.handle,
145
+ includeActionData: options.includeActionData
146
+ });
147
+ span.setStatus({ code: SpanStatusCode.OK });
148
+ } else {
149
+ span.setAttribute("robotrock.outcome", outcome);
150
+ span.setStatus({
151
+ code: SpanStatusCode.ERROR,
152
+ message: outcome === "timeout" ? "Human response timed out before validUntil" : "Human wait failed"
153
+ });
154
+ }
155
+ span.end();
156
+ }
157
+
158
+ // src/otel-platform.ts
159
+ function shouldRecordRobotRockOtel(recordOtel) {
160
+ if (recordOtel === true) {
161
+ return true;
162
+ }
163
+ if (recordOtel === false) {
164
+ return false;
165
+ }
166
+ const env = process.env.ROBOTROCK_OTEL_RECORD_HANDLED;
167
+ return env === "true" || env === "1";
168
+ }
169
+ function stripPlatformOtelFields(payload) {
170
+ const { recordOtel: _recordOtel, otelIncludeActionData: _otelIncludeActionData, ...rest } = payload;
171
+ return rest;
172
+ }
173
+ function beginRobotRockHumanWaitOtel(task2, options = {}) {
174
+ const recordOtel = shouldRecordRobotRockOtel(options.recordOtel);
175
+ if (!recordOtel) {
176
+ return { recordOtel: false };
177
+ }
178
+ const handle = captureRobotRockOtelHandle(task2);
179
+ const waitSpan = startRobotRockHumanWaitSpan(handle);
180
+ return {
181
+ recordOtel: true,
182
+ handle,
183
+ waitSpan,
184
+ includeActionData: options.otelIncludeActionData
185
+ };
186
+ }
187
+ function finishRobotRockHumanWaitOtel(session, handled, outcome) {
188
+ if (!session.recordOtel) {
189
+ return;
190
+ }
191
+ endRobotRockHumanWaitSpan(session.waitSpan, handled, {
192
+ handle: session.handle,
193
+ outcome,
194
+ includeActionData: session.includeActionData
195
+ });
196
+ }
197
+
4
198
  // src/schemas/index.ts
5
199
  import { z as z2 } from "zod";
6
200
 
@@ -167,12 +361,12 @@ function validateUrlValue(value, widget) {
167
361
  }
168
362
  return null;
169
363
  }
170
- function validateContextPublicUrls(context) {
171
- if (!context?.data) {
364
+ function validateContextPublicUrls(context2) {
365
+ if (!context2?.data) {
172
366
  return null;
173
367
  }
174
- for (const [field, value] of Object.entries(context.data)) {
175
- const widget = widgetType(context.ui, field);
368
+ for (const [field, value] of Object.entries(context2.data)) {
369
+ const widget = widgetType(context2.ui, field);
176
370
  if (!widget) {
177
371
  continue;
178
372
  }
@@ -227,7 +421,8 @@ var contextDataSchema = z.object({
227
421
  data: z.record(z.string(), z.unknown()),
228
422
  ui: contextUiSchema
229
423
  }).optional();
230
- var taskContextObjectSchema = z.object({
424
+ var TASK_CONTEXT_FORMAT_VERSION = 2;
425
+ var taskContextObjectBaseSchema = z.object({
231
426
  /** Source application id; omitted tasks are grouped in the default inbox. */
232
427
  app: z.string().min(1).optional(),
233
428
  type: z.string().min(1),
@@ -235,9 +430,22 @@ var taskContextObjectSchema = z.object({
235
430
  description: z.string().optional(),
236
431
  validUntil: z.string().optional(),
237
432
  context: contextDataSchema,
433
+ /** Task context wire format version. @default 2 */
434
+ contextVersion: z.literal(2).optional(),
435
+ /** @deprecated Use `contextVersion`. Accepted on ingest only. */
238
436
  version: z.literal(2).optional(),
239
437
  actions: z.array(taskActionSchema).min(1, "At least one action is required")
240
438
  });
439
+ function normalizeTaskContextVersion(data) {
440
+ const { version: legacyVersion, contextVersion, ...rest } = data;
441
+ return {
442
+ ...rest,
443
+ contextVersion: contextVersion ?? legacyVersion ?? TASK_CONTEXT_FORMAT_VERSION
444
+ };
445
+ }
446
+ var taskContextObjectSchema = taskContextObjectBaseSchema.transform(
447
+ normalizeTaskContextVersion
448
+ );
241
449
  function refineContextPublicUrls(data, ctx) {
242
450
  const error = validateContextPublicUrls(data.context);
243
451
  if (error) {
@@ -281,61 +489,11 @@ var threadUpdateInputSchema = z.object({
281
489
  });
282
490
  var taskPriorities = ["low", "normal", "high", "urgent"];
283
491
  var taskPrioritySchema = z.enum(taskPriorities);
284
- var nonNegativeInt = z.number().int().nonnegative();
285
- var agentCostTokensSchema = z.object({
286
- input: nonNegativeInt.optional(),
287
- output: nonNegativeInt.optional(),
288
- total: nonNegativeInt.optional()
289
- });
290
- var agentCostSchema = z.object({
291
- tokens: agentCostTokensSchema.optional(),
292
- eur: z.number().nonnegative().optional(),
293
- usd: z.number().nonnegative().optional()
294
- });
295
- var AGENT_INFO_MAX_KEYS = 32;
296
- var AGENT_TOOL_CALLS_MAX_KEYS = 64;
297
- var AGENT_OTEL_SPANS_MAX = 32;
298
- var agentToolCallsSchema = z.record(z.string().min(1), nonNegativeInt);
299
- var agentOtelSpanStatusSchema = z.enum(["ok", "error"]);
300
- var agentOtelSpanSummarySchema = z.object({
301
- name: z.string().min(1),
302
- durationMs: z.number().nonnegative(),
303
- status: agentOtelSpanStatusSchema
304
- });
305
- var agentOtelSchema = z.object({
306
- traceId: z.string().min(1),
307
- spanId: z.string().min(1).optional(),
308
- rootDurationMs: z.number().nonnegative().optional(),
309
- spans: z.array(agentOtelSpanSummarySchema).max(AGENT_OTEL_SPANS_MAX).optional()
310
- });
311
492
  var agentTelemetrySchema = z.object({
312
493
  /** Agent or app version (semver, git SHA, deploy tag — caller-defined). */
313
- version: z.string().min(1).optional(),
314
- /** Total tool invocations. When omitted but `toolCalls` is set, the sum of counts is used. */
315
- toolCallCount: nonNegativeInt.optional(),
316
- /** Per-tool invocation counts keyed by tool name. */
317
- toolCalls: agentToolCallsSchema.optional(),
318
- cost: agentCostSchema.optional(),
319
- /** Free-form metadata for experiments (model, prompt variant, etc.). */
320
- info: z.record(z.string(), z.unknown()).optional(),
321
- /** Structured OpenTelemetry span snapshot for feedback analysis. */
322
- otel: agentOtelSchema.optional()
323
- }).refine(
324
- (data) => {
325
- const keys = data.info ? Object.keys(data.info) : [];
326
- return keys.length <= AGENT_INFO_MAX_KEYS;
327
- },
328
- { message: `agent.info may contain at most ${AGENT_INFO_MAX_KEYS} keys` }
329
- ).refine(
330
- (data) => {
331
- const keys = data.toolCalls ? Object.keys(data.toolCalls) : [];
332
- return keys.length <= AGENT_TOOL_CALLS_MAX_KEYS;
333
- },
334
- {
335
- message: `agent.toolCalls may contain at most ${AGENT_TOOL_CALLS_MAX_KEYS} tool names`
336
- }
337
- );
338
- var createTaskBodySchema = taskContextObjectSchema.extend({
494
+ version: z.string().min(1)
495
+ });
496
+ var createTaskBodySchema = taskContextObjectBaseSchema.extend({
339
497
  assignTo: assignToSchema.optional(),
340
498
  /**
341
499
  * Groups related tasks together. When omitted, the server generates one and
@@ -352,9 +510,9 @@ var createTaskBodySchema = taskContextObjectSchema.extend({
352
510
  * the inbox status bar and the thread update log.
353
511
  */
354
512
  update: threadUpdateInputSchema.optional(),
355
- /** Agent telemetry (version, cost, tool calls) — not shown in inbox UI. */
513
+ /** Agent release version — not shown in inbox UI. */
356
514
  agent: agentTelemetrySchema.optional()
357
- }).superRefine(refineContextPublicUrls);
515
+ }).transform(normalizeTaskContextVersion).superRefine(refineContextPublicUrls);
358
516
 
359
517
  // src/schemas/index.ts
360
518
  var safeUrlSchema2 = z2.string().refine((url) => isPublicHttpUrl(url), {
@@ -398,16 +556,29 @@ var contextDataSchema2 = z2.object({
398
556
  data: z2.record(z2.string(), z2.unknown()),
399
557
  ui: contextUiSchema2
400
558
  }).optional();
401
- var taskContextObjectSchema2 = z2.object({
559
+ var TASK_CONTEXT_FORMAT_VERSION2 = 2;
560
+ var taskContextObjectBaseSchema2 = z2.object({
402
561
  app: z2.string().min(1).optional(),
403
562
  type: z2.string().min(1),
404
563
  name: z2.string().min(1),
405
564
  description: z2.string().optional(),
406
565
  validUntil: z2.string().optional(),
407
566
  context: contextDataSchema2,
567
+ contextVersion: z2.literal(2).optional(),
568
+ /** @deprecated Use `contextVersion`. Accepted on ingest only. */
408
569
  version: z2.literal(2).optional(),
409
570
  actions: z2.array(taskActionSchema2).min(1, "At least one action is required")
410
571
  });
572
+ function normalizeTaskContextVersion2(data) {
573
+ const { version: legacyVersion, contextVersion, ...rest } = data;
574
+ return {
575
+ ...rest,
576
+ contextVersion: contextVersion ?? legacyVersion ?? TASK_CONTEXT_FORMAT_VERSION2
577
+ };
578
+ }
579
+ var taskContextObjectSchema2 = taskContextObjectBaseSchema2.transform(
580
+ normalizeTaskContextVersion2
581
+ );
411
582
  function refineContextPublicUrls2(data, ctx) {
412
583
  const error = validateContextPublicUrls(data.context);
413
584
  if (error) {
@@ -451,56 +622,10 @@ var threadUpdateInputSchema2 = z2.object({
451
622
  });
452
623
  var taskPriorities2 = ["low", "normal", "high", "urgent"];
453
624
  var taskPrioritySchema2 = z2.enum(taskPriorities2);
454
- var nonNegativeInt2 = z2.number().int().nonnegative();
455
- var agentCostTokensSchema2 = z2.object({
456
- input: nonNegativeInt2.optional(),
457
- output: nonNegativeInt2.optional(),
458
- total: nonNegativeInt2.optional()
459
- });
460
- var agentCostSchema2 = z2.object({
461
- tokens: agentCostTokensSchema2.optional(),
462
- eur: z2.number().nonnegative().optional(),
463
- usd: z2.number().nonnegative().optional()
464
- });
465
- var AGENT_INFO_MAX_KEYS2 = 32;
466
- var AGENT_TOOL_CALLS_MAX_KEYS2 = 64;
467
- var AGENT_OTEL_SPANS_MAX2 = 32;
468
- var agentToolCallsSchema2 = z2.record(z2.string().min(1), nonNegativeInt2);
469
- var agentOtelSpanStatusSchema2 = z2.enum(["ok", "error"]);
470
- var agentOtelSpanSummarySchema2 = z2.object({
471
- name: z2.string().min(1),
472
- durationMs: z2.number().nonnegative(),
473
- status: agentOtelSpanStatusSchema2
474
- });
475
- var agentOtelSchema2 = z2.object({
476
- traceId: z2.string().min(1),
477
- spanId: z2.string().min(1).optional(),
478
- rootDurationMs: z2.number().nonnegative().optional(),
479
- spans: z2.array(agentOtelSpanSummarySchema2).max(AGENT_OTEL_SPANS_MAX2).optional()
480
- });
481
625
  var agentTelemetrySchema2 = z2.object({
482
- version: z2.string().min(1).optional(),
483
- toolCallCount: nonNegativeInt2.optional(),
484
- toolCalls: agentToolCallsSchema2.optional(),
485
- cost: agentCostSchema2.optional(),
486
- info: z2.record(z2.string(), z2.unknown()).optional(),
487
- otel: agentOtelSchema2.optional()
488
- }).refine(
489
- (data) => {
490
- const keys = data.info ? Object.keys(data.info) : [];
491
- return keys.length <= AGENT_INFO_MAX_KEYS2;
492
- },
493
- { message: `agent.info may contain at most ${AGENT_INFO_MAX_KEYS2} keys` }
494
- ).refine(
495
- (data) => {
496
- const keys = data.toolCalls ? Object.keys(data.toolCalls) : [];
497
- return keys.length <= AGENT_TOOL_CALLS_MAX_KEYS2;
498
- },
499
- {
500
- message: `agent.toolCalls may contain at most ${AGENT_TOOL_CALLS_MAX_KEYS2} tool names`
501
- }
502
- );
503
- var createTaskBodySchema2 = taskContextObjectSchema2.extend({
626
+ version: z2.string().min(1)
627
+ });
628
+ var createTaskBodySchema2 = taskContextObjectBaseSchema2.extend({
504
629
  assignTo: assignToSchema2.optional(),
505
630
  /**
506
631
  * Groups related tasks together. When omitted, the server generates one and
@@ -518,7 +643,7 @@ var createTaskBodySchema2 = taskContextObjectSchema2.extend({
518
643
  */
519
644
  update: threadUpdateInputSchema2.optional(),
520
645
  agent: agentTelemetrySchema2.optional()
521
- }).superRefine(refineContextPublicUrls2);
646
+ }).transform(normalizeTaskContextVersion2).superRefine(refineContextPublicUrls2);
522
647
  var threadUpdateBodySchema = threadUpdateInputSchema2;
523
648
 
524
649
  // src/approval-result.ts
@@ -554,6 +679,10 @@ var DEFAULT_TIMEOUT_MS = 24 * 60 * 60 * 1e3;
554
679
  function sleep(ms) {
555
680
  return new Promise((resolve) => setTimeout(resolve, ms));
556
681
  }
682
+ function resolveAgentVersionFromEnv() {
683
+ const fromEnv = process.env.AGENT_VERSION?.trim() || process.env.ROBOTROCK_AGENT_VERSION?.trim();
684
+ return fromEnv || void 0;
685
+ }
557
686
  function parseValidUntilMs(value) {
558
687
  if (value === void 0) {
559
688
  return void 0;
@@ -593,7 +722,8 @@ var RobotRock = class {
593
722
  apiKey;
594
723
  baseUrl;
595
724
  app;
596
- version;
725
+ agentVersion;
726
+ contextVersion;
597
727
  webhook;
598
728
  polling;
599
729
  constructor(config) {
@@ -612,7 +742,8 @@ var RobotRock = class {
612
742
  const rawBase = config.baseUrl ?? "https://api.robotrock.io/v1";
613
743
  this.baseUrl = rawBase.replace(/\/+$/, "");
614
744
  this.app = config.app;
615
- this.version = config.version ?? 2;
745
+ this.agentVersion = config.version ?? resolveAgentVersionFromEnv();
746
+ this.contextVersion = config.advanced?.contextVersion ?? TASK_CONTEXT_FORMAT_VERSION2;
616
747
  this.webhook = config.webhook;
617
748
  this.polling = config.polling ?? {};
618
749
  }
@@ -623,15 +754,17 @@ var RobotRock = class {
623
754
  const normalizedTask = normalizeSendToHumanInput(task2, {
624
755
  webhook: this.webhook,
625
756
  app: this.app,
626
- version: this.version
757
+ contextVersion: this.contextVersion,
758
+ agentVersion: this.agentVersion
627
759
  });
760
+ const agentVersion = task2.version ?? this.agentVersion;
628
761
  const bodyPayload = {
629
762
  ...normalizedTask,
630
763
  ...task2.assignTo !== void 0 ? { assignTo: task2.assignTo } : {},
631
764
  ...task2.threadId !== void 0 ? { threadId: task2.threadId } : {},
632
765
  ...task2.priority !== void 0 ? { priority: task2.priority } : {},
633
766
  ...task2.update !== void 0 ? { update: task2.update } : {},
634
- ...task2.agent !== void 0 ? { agent: task2.agent } : {}
767
+ ...agentVersion !== void 0 ? { agent: { version: agentVersion } } : {}
635
768
  };
636
769
  const validation = createTaskBodySchema2.safeParse(bodyPayload);
637
770
  if (!validation.success) {
@@ -667,7 +800,8 @@ var RobotRock = class {
667
800
  const normalizedTask = normalizeSendToHumanInput(task2, {
668
801
  webhook: this.webhook,
669
802
  app: this.app,
670
- version: this.version
803
+ contextVersion: this.contextVersion,
804
+ agentVersion: this.agentVersion
671
805
  });
672
806
  const createdTaskTask = await this.createTask(task2);
673
807
  const hasHandlers = normalizedTask.actions.some(
@@ -811,6 +945,7 @@ function normalizeSendToHumanInput(task2, clientDefaults) {
811
945
  threadId: _threadId,
812
946
  priority: _priority,
813
947
  update: _update,
948
+ version: _version,
814
949
  validUntil,
815
950
  app: taskApp,
816
951
  ...rest
@@ -820,7 +955,7 @@ function normalizeSendToHumanInput(task2, clientDefaults) {
820
955
  const app = taskApp ?? clientDefaults.app;
821
956
  return {
822
957
  ...rest,
823
- version: clientDefaults.version,
958
+ contextVersion: clientDefaults.contextVersion,
824
959
  ...app ? { app } : {},
825
960
  ...validUntil !== void 0 ? { validUntil: serializeValidUntil(validUntil) } : {},
826
961
  actions: normalizedActions
@@ -940,7 +1075,7 @@ var APPROVE_BY_HUMAN_ACTIONS = [
940
1075
  { id: "decline", title: "Decline" }
941
1076
  ];
942
1077
  async function runSendToHuman(payload) {
943
- const { validUntil: validUntilInput, ...taskInput } = payload;
1078
+ const { validUntil: validUntilInput, app, recordOtel, otelIncludeActionData, ...taskFields } = payload;
944
1079
  const { validUntil, timeout } = resolveWaitTiming(validUntilInput);
945
1080
  const token = await wait.createToken({ timeout });
946
1081
  const baseConfig = resolveRobotRockConfig();
@@ -949,40 +1084,68 @@ async function runSendToHuman(payload) {
949
1084
  baseUrl: baseConfig.baseUrl,
950
1085
  ...baseConfig.app ? { app: baseConfig.app } : {},
951
1086
  ...baseConfig.version ? { version: baseConfig.version } : {},
1087
+ ...baseConfig.advanced ? { advanced: baseConfig.advanced } : {},
952
1088
  webhook: { url: token.url }
953
1089
  });
1090
+ const taskInput = stripPlatformOtelFields(taskFields);
954
1091
  const sendResult = await client.sendToHuman({
955
1092
  ...taskInput,
956
- validUntil
1093
+ validUntil,
1094
+ ...app !== void 0 ? { app } : {}
957
1095
  });
958
- const outcome = await wait.forToken(token.id);
959
- if (!outcome.ok) {
960
- throw new Error(`Human response timed out before validUntil (${timeout})`);
961
- }
962
- const output = outcome.output;
963
- if (!isRobotRockHandlerWebhookPayload(output)) {
964
- throw new Error(
965
- "Wait token completed with unexpected payload; expected RobotRock handler body (taskId, action.id, action.data)."
966
- );
967
- }
968
- return toDiscriminatedApprovalResult(
969
- payload.actions,
970
- {
971
- id: output.taskId,
972
- createdAt: /* @__PURE__ */ new Date(),
973
- status: "handled",
974
- context: sendResult.task.context,
975
- validUntil: Date.now(),
976
- handledAt: new Date(output.handledAt).getTime(),
977
- handled: {
978
- action: {
979
- id: output.action.id,
980
- data: output.action.data
981
- },
982
- handledBy: output.handledBy
1096
+ const otelSession = beginRobotRockHumanWaitOtel(sendResult.task, {
1097
+ recordOtel,
1098
+ otelIncludeActionData
1099
+ });
1100
+ let handledPayload = null;
1101
+ let waitOutcome = "pending";
1102
+ try {
1103
+ const outcome = await wait.forToken(token.id);
1104
+ if (!outcome.ok) {
1105
+ waitOutcome = "timeout";
1106
+ throw new Error(`Human response timed out before validUntil (${timeout})`);
1107
+ }
1108
+ const output = outcome.output;
1109
+ if (!isRobotRockHandlerWebhookPayload(output)) {
1110
+ waitOutcome = "error";
1111
+ throw new Error(
1112
+ "Wait token completed with unexpected payload; expected RobotRock handler body (taskId, action.id, action.data)."
1113
+ );
1114
+ }
1115
+ handledPayload = output;
1116
+ waitOutcome = "handled";
1117
+ return toDiscriminatedApprovalResult(
1118
+ payload.actions,
1119
+ {
1120
+ id: output.taskId,
1121
+ createdAt: /* @__PURE__ */ new Date(),
1122
+ status: "handled",
1123
+ context: sendResult.task.context,
1124
+ validUntil: Date.now(),
1125
+ handledAt: new Date(output.handledAt).getTime(),
1126
+ handled: {
1127
+ action: {
1128
+ id: output.action.id,
1129
+ data: output.action.data
1130
+ },
1131
+ handledBy: output.handledBy
1132
+ }
983
1133
  }
1134
+ );
1135
+ } catch (error) {
1136
+ if (waitOutcome === "pending") {
1137
+ waitOutcome = "error";
984
1138
  }
985
- );
1139
+ throw error;
1140
+ } finally {
1141
+ if (waitOutcome !== "pending") {
1142
+ finishRobotRockHumanWaitOtel(
1143
+ otelSession,
1144
+ handledPayload,
1145
+ waitOutcome === "handled" ? "handled" : waitOutcome
1146
+ );
1147
+ }
1148
+ }
986
1149
  }
987
1150
  var sendToHumanTask = task({
988
1151
  id: "robotrock/send-to-human",