@tangle-network/agent-app 0.60.197 → 0.60.199

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.
Files changed (45) hide show
  1. package/dist/chat-routes/index.js +6 -4
  2. package/dist/chat-routes/index.js.map +1 -1
  3. package/dist/chat-routes/turn-routes.d.ts +28 -0
  4. package/dist/{chunk-XJEIGVMQ.js → chunk-2OWT3TNV.js} +2 -2
  5. package/dist/{chunk-XP77DJEF.js → chunk-6XXKFYIM.js} +72 -30
  6. package/dist/chunk-6XXKFYIM.js.map +1 -0
  7. package/dist/chunk-7XQIY2LU.js +151 -0
  8. package/dist/chunk-7XQIY2LU.js.map +1 -0
  9. package/dist/chunk-BKPJFES3.js +229 -0
  10. package/dist/chunk-BKPJFES3.js.map +1 -0
  11. package/dist/{chunk-7WEPGGOW.js → chunk-EGBNNTAL.js} +2 -2
  12. package/dist/{chunk-7WEPGGOW.js.map → chunk-EGBNNTAL.js.map} +1 -1
  13. package/dist/{chunk-SUIH5B55.js → chunk-RJJRRR5Z.js} +184 -16
  14. package/dist/chunk-RJJRRR5Z.js.map +1 -0
  15. package/dist/{chunk-FPOOBGRG.js → chunk-T4BLISYH.js} +40 -5
  16. package/dist/chunk-T4BLISYH.js.map +1 -0
  17. package/dist/public-consultation/index.d.ts +1 -1
  18. package/dist/public-consultation/index.js +2 -1
  19. package/dist/public-consultation/index.js.map +1 -1
  20. package/dist/runtime/index.js +4 -2
  21. package/dist/runtime/index.js.map +1 -1
  22. package/dist/runtime/router-turn.d.ts +14 -1
  23. package/dist/runtime/router-turn.js +3 -1
  24. package/dist/sandbox/index.d.ts +19 -0
  25. package/dist/sandbox/index.js +2 -1
  26. package/dist/sandbox/prompt-trace.d.ts +20 -0
  27. package/dist/sandbox/sandbox-run-tool.d.ts +8 -0
  28. package/dist/trace/index.d.ts +0 -1
  29. package/dist/trace/index.js +0 -240
  30. package/dist/trace/index.js.map +1 -1
  31. package/dist/turn-trace/index.d.ts +11 -0
  32. package/dist/turn-trace/index.js +121 -0
  33. package/dist/turn-trace/index.js.map +1 -0
  34. package/dist/turn-trace/otel.d.ts +36 -0
  35. package/dist/turn-trace/recorder.d.ts +122 -0
  36. package/dist/turn-trace/server-timing.d.ts +15 -0
  37. package/dist/turn-trace/traced-fetch.d.ts +31 -0
  38. package/dist/workspace-apps/index.js +1 -1
  39. package/dist/workspace-apps/react.js +1 -1
  40. package/package.json +6 -1
  41. package/dist/chunk-FPOOBGRG.js.map +0 -1
  42. package/dist/chunk-SUIH5B55.js.map +0 -1
  43. package/dist/chunk-XP77DJEF.js.map +0 -1
  44. package/dist/trace/stage-timing.d.ts +0 -61
  45. /package/dist/{chunk-XJEIGVMQ.js.map → chunk-2OWT3TNV.js.map} +0 -0
@@ -21,6 +21,9 @@ import {
21
21
  import {
22
22
  buildAppToolMcpServer
23
23
  } from "./chunk-6A7MYOUI.js";
24
+ import {
25
+ nextCallIndex
26
+ } from "./chunk-7XQIY2LU.js";
24
27
  import {
25
28
  resolveTangleExecutionEnvironment,
26
29
  trimOrNull
@@ -40,6 +43,113 @@ var fail = (error) => ({
40
43
  error: error instanceof Error ? error : new Error(String(error))
41
44
  });
42
45
 
46
+ // src/sandbox/prompt-trace.ts
47
+ var asRec = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
48
+ var asNum = (value) => typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
49
+ var PHASES = ["agent_run_start", "harness_spawn", "harness_ready", "first_model_request", "first_upstream_byte", "first_text"];
50
+ function createPromptTrace(trace, parent, meta) {
51
+ const startedAt = Date.now();
52
+ const prompt = trace.start("sandbox.prompt", "sandbox", meta, parent);
53
+ let firstEventAt;
54
+ let firstTextAt;
55
+ let generation;
56
+ let phases;
57
+ let events = 0;
58
+ const tools = /* @__PURE__ */ new Map();
59
+ const startTool = (id, name) => {
60
+ if (tools.has(id)) return;
61
+ tools.set(id, trace.start("tool.call", "tool", {
62
+ "gen_ai.tool.name": name,
63
+ "tool.call_id": id,
64
+ "call.index": nextCallIndex(trace, "tool.call"),
65
+ where: "sandbox",
66
+ approx: true
67
+ }, generation ?? prompt));
68
+ };
69
+ const observe = (raw) => {
70
+ try {
71
+ const event = asRec(raw);
72
+ if (!event) return;
73
+ const now = Date.now();
74
+ events++;
75
+ firstEventAt ??= now;
76
+ const type = typeof event.type === "string" ? event.type : "";
77
+ const data = asRec(event.data);
78
+ if (type === "result") {
79
+ const report = asRec(data?.firstTextPhases);
80
+ const offsets = asRec(report?.phasesMs);
81
+ if (offsets) {
82
+ phases = {
83
+ offsets,
84
+ warm: typeof report?.harnessWarm === "boolean" ? report.harnessWarm : null,
85
+ ...typeof report?.model === "string" ? { model: report.model } : {}
86
+ };
87
+ }
88
+ return;
89
+ }
90
+ if (type === "tool_call" || type === "tool.call") {
91
+ const id = String(data?.id ?? data?.callId ?? data?.callID ?? data?.name ?? "");
92
+ if (id) startTool(id, String(data?.name ?? data?.tool ?? "tool"));
93
+ return;
94
+ }
95
+ if (type === "tool_result" || type === "tool.result") {
96
+ const id = String(data?.id ?? data?.callId ?? data?.callID ?? data?.name ?? "");
97
+ tools.get(id)?.end(void 0, data?.error ?? (data?.status === "error" ? "tool_error" : void 0));
98
+ return;
99
+ }
100
+ if (type !== "message.part.updated") return;
101
+ const part = asRec(data?.part);
102
+ const partType = String(part?.type ?? "");
103
+ if (partType === "text" && firstTextAt === void 0 && (typeof data?.delta === "string" && data.delta || typeof part?.text === "string" && part.text)) {
104
+ firstTextAt = now;
105
+ generation = prompt.child("sandbox.generation", "model");
106
+ } else if (partType === "tool") {
107
+ const id = String(part?.id ?? part?.callID ?? "");
108
+ if (!id) return;
109
+ const state = asRec(part?.state);
110
+ const status = String(state?.status ?? part?.status ?? "");
111
+ startTool(id, String(part?.tool ?? "tool"));
112
+ if (status === "completed" || status === "error") tools.get(id)?.end(void 0, status === "error" ? "tool_error" : void 0);
113
+ }
114
+ } catch {
115
+ }
116
+ };
117
+ const end = (error) => {
118
+ try {
119
+ for (const span of tools.values()) span.end({ "tool.unclosed": true });
120
+ generation?.end();
121
+ if (phases && firstTextAt !== void 0) {
122
+ const firstText = asNum(phases.offsets.first_text);
123
+ if (firstText !== void 0 && firstTextAt - firstText >= startedAt) {
124
+ const origin = firstTextAt - firstText;
125
+ prompt.record("sandbox.transit", "sandbox", startedAt, origin, { note: "sdk, api, orchestrator and host hops, both ways" });
126
+ let previous = 0;
127
+ for (const phase of PHASES) {
128
+ const offset = asNum(phases.offsets[phase]);
129
+ if (offset === void 0 || offset < previous) continue;
130
+ prompt.record(`sidecar.${phase}`, phase === "first_upstream_byte" || phase === "first_text" ? "model" : "sandbox", origin + previous, origin + offset);
131
+ previous = offset;
132
+ }
133
+ }
134
+ }
135
+ prompt.set({
136
+ events,
137
+ ...firstEventAt !== void 0 ? { first_event_ms: firstEventAt - startedAt } : {},
138
+ ...firstTextAt !== void 0 ? { first_text_ms: firstTextAt - startedAt } : {},
139
+ ...phases?.warm !== null && phases?.warm !== void 0 ? { "sidecar.harness_warm": phases.warm } : {},
140
+ ...phases?.model ? { "sidecar.model": phases.model } : {},
141
+ ...Object.fromEntries(PHASES.flatMap((phase) => {
142
+ const offset = asNum(phases?.offsets[phase]);
143
+ return offset === void 0 ? [] : [[`sidecar.${phase}_ms`, Math.round(offset)]];
144
+ }))
145
+ });
146
+ prompt.end(void 0, error);
147
+ } catch {
148
+ }
149
+ };
150
+ return { observe, end };
151
+ }
152
+
43
153
  // src/sandbox/model.ts
44
154
  function backendModelFields(model) {
45
155
  return {
@@ -427,7 +537,14 @@ var DEFAULT_TIMEOUT_MS = 5 * 6e4;
427
537
  var TASK_LIMIT = 2e4;
428
538
  var SANDBOX_RUN_DESCRIPTION = "Run work that needs a computer in this workspace's sandbox: executing code or scripts, reading or converting PDFs, spreadsheets and archives, filling forms with the installed toolkits, or editing many files. Describe the whole task and the result you need; the sandbox agent does it and returns its answer. Takes seconds to minutes, so use it only when the answer needs the computer.";
429
539
  function sandboxPromptRunner(shell, prompt = {}) {
430
- return (box, task, { signal, timeoutMs }) => runSandboxPrompt(shell, box, task, { ...prompt, signal, timeoutMs, disallowQuestions: true });
540
+ return (box, task, { signal, timeoutMs, trace, span }) => runSandboxPrompt(shell, box, task, {
541
+ ...prompt,
542
+ signal,
543
+ timeoutMs,
544
+ disallowQuestions: true,
545
+ ...trace ? { trace } : {},
546
+ ...span ? { traceParent: span } : {}
547
+ });
431
548
  }
432
549
  function createSandboxRunTool(options) {
433
550
  const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
@@ -466,16 +583,25 @@ function createSandboxRunTool(options) {
466
583
  signal.addEventListener("abort", () => reject(signal.reason), { once: true });
467
584
  })]);
468
585
  let box;
586
+ const claim = context?.span?.child("sandbox.claim", "sandbox");
469
587
  try {
470
588
  box = await capped(options.box(signal));
589
+ claim?.end();
471
590
  } catch (error) {
591
+ claim?.end(void 0, error instanceof SandboxRunError ? error.code : "sandbox_unavailable");
472
592
  if (error instanceof SandboxRunError) return fail2(error.code, error.message, error.cause);
473
593
  return fail2("sandbox_unavailable", "The sandbox for this workspace could not start, so this work was not done.", error);
474
594
  }
475
595
  emit("box_ready", { ms: Date.now() - started });
476
596
  let answer;
477
597
  try {
478
- answer = await capped(runTask(box, task, { signal, timeoutMs: Math.max(1, timeoutMs - (Date.now() - started)), emit }));
598
+ answer = await capped(runTask(box, task, {
599
+ signal,
600
+ timeoutMs: Math.max(1, timeoutMs - (Date.now() - started)),
601
+ emit,
602
+ ...context?.trace ? { trace: context.trace } : {},
603
+ ...context?.span ? { span: context.span } : {}
604
+ }));
479
605
  } catch (error) {
480
606
  const timedOut = signal.aborted && !options.signal?.aborted;
481
607
  return fail2("sandbox_failed", timedOut ? `The sandbox did not finish within ${Math.round(timeoutMs / 1e3)} s, so this work was not done.` : "The sandbox run failed, so this work was not done.", error);
@@ -559,10 +685,13 @@ function createSandboxExecTool(options) {
559
685
  if (options.warm) await options.warm.leases.turn(options.warm.key).catch(() => {
560
686
  });
561
687
  let box;
688
+ const reused = ready !== null;
689
+ const claim = context?.span?.child("sandbox.claim", "sandbox");
562
690
  const starting = place(signal);
563
691
  try {
564
692
  box = await raceAbort(starting, signal);
565
693
  } catch (error) {
694
+ claim?.end(void 0, error instanceof SandboxRunError ? error.code : "sandbox_unavailable");
566
695
  if (ready === starting) ready = null;
567
696
  if (error instanceof SandboxRunError) {
568
697
  options.onFailure?.(error);
@@ -570,15 +699,22 @@ function createSandboxExecTool(options) {
570
699
  }
571
700
  return fail2("sandbox_unavailable", "The sandbox did not start in time, so the command did not run.", error);
572
701
  }
702
+ claim?.end({ "claim.reused": reused });
573
703
  emit("box_ready", { ms: Date.now() - started });
574
704
  let result;
705
+ const execSpan = context?.span?.child("sandbox.exec", "sandbox");
575
706
  try {
576
707
  result = await raceAbort(box.exec(command, {
577
708
  ...cwd ? { cwd } : {},
578
709
  ...options.env ? { env: options.env } : {},
579
- timeoutMs: Math.max(1, timeoutMs - (Date.now() - started))
710
+ timeoutMs: Math.max(1, timeoutMs - (Date.now() - started)),
711
+ // The same id the Router turn sends, so this command's Sandbox API and host records join the turn.
712
+ // SDK 0.62.2 added `traceId` to ExecOptions; an older SDK ignores the field.
713
+ ...context?.trace ? { traceId: context.trace.traceId } : {}
580
714
  }), signal);
715
+ execSpan?.end({ "exec.exit_code": result.exitCode });
581
716
  } catch (error) {
717
+ execSpan?.end(void 0, "exec_failed");
582
718
  const timedOut = signal.aborted && !options.signal?.aborted;
583
719
  return fail2("sandbox_failed", timedOut ? `The command did not finish within ${Math.round(timeoutMs / 1e3)} s.` : "The command could not run in the sandbox.", error);
584
720
  }
@@ -2384,9 +2520,24 @@ async function ensureWorkspaceSandbox(shell, options) {
2384
2520
  workspaceId: options.workspaceId,
2385
2521
  ...options.userId ? { userId: options.userId } : {}
2386
2522
  });
2387
- const box = await bringUpWorkspaceSandbox(shell, options);
2388
- await observeProvisionedBox(shell, options, box);
2389
- return box;
2523
+ const { trace } = options;
2524
+ const claim = trace?.start("sandbox.claim", "sandbox", { "workspace.harness": options.harness }, options.traceParent);
2525
+ const traced = trace && claim ? {
2526
+ ...options,
2527
+ measure: (stage, operation) => {
2528
+ const run = () => trace.measure(stage, "sandbox", () => operation(), void 0, claim);
2529
+ return options.measure ? options.measure(stage, run) : run();
2530
+ }
2531
+ } : options;
2532
+ try {
2533
+ const box = await bringUpWorkspaceSandbox(shell, traced);
2534
+ claim?.end({ "sandbox.id": box.id });
2535
+ await observeProvisionedBox(shell, options, box);
2536
+ return box;
2537
+ } catch (error) {
2538
+ claim?.end(void 0, error);
2539
+ throw error;
2540
+ }
2390
2541
  }
2391
2542
  async function observeProvisionedBox(shell, options, box) {
2392
2543
  if (!options.spend?.onProvisioned) return;
@@ -2521,11 +2672,12 @@ async function provisionWorkspaceSandbox(shell, options) {
2521
2672
  }
2522
2673
  }
2523
2674
  if (!replacementRequested && !forceNew && shell.resumeStopped !== false) {
2524
- const stopped = await listStopped(client, name);
2675
+ const stopped = await measure("list_stopped", () => listStopped(client, name));
2525
2676
  if (!stopped.succeeded) throw stopped.error;
2526
2677
  if (stopped.value) {
2527
- const runtimeEnv2 = await resolveWorkspaceRuntimeEnv(shell, scope);
2528
- const resumed = await resumeStoppedBox(stopped.value, resumeTimeout, runtimeEnv2, onProgress);
2678
+ const stoppedBox = stopped.value;
2679
+ const runtimeEnv2 = await measure("runtime_env", () => resolveWorkspaceRuntimeEnv(shell, scope));
2680
+ const resumed = await measure("resume", () => resumeStoppedBox(stoppedBox, resumeTimeout, runtimeEnv2, onProgress));
2529
2681
  if (!resumed.succeeded) {
2530
2682
  const recovery = await requestSandboxReplacement(
2531
2683
  shell.recoverStoppedSandbox,
@@ -2635,12 +2787,12 @@ async function provisionWorkspaceSandbox(shell, options) {
2635
2787
  SHELL_PROMPT_BUDGET_HINT
2636
2788
  );
2637
2789
  assertProvisionPayloadWithinCap(payload ?? {});
2638
- let box = await client.create(payload);
2639
- await box.waitFor("running", {
2790
+ let box = await measure("create", () => client.create(payload));
2791
+ await measure("wait_running", () => box.waitFor("running", {
2640
2792
  timeoutMs: shell.provisionTimeoutMs ?? DEFAULT_PROVISION_TIMEOUT_MS,
2641
2793
  ...onProgress ? { onProgress } : {}
2642
- });
2643
- box = await refreshRuntimeConnection(client, box);
2794
+ }));
2795
+ box = await measure("refresh_connection", () => refreshRuntimeConnection(client, box));
2644
2796
  if (deferredFiles.length > 0) {
2645
2797
  const written = await writeProfileFilesToBox(box, deferredFiles);
2646
2798
  if (!written.succeeded) {
@@ -2648,7 +2800,8 @@ async function provisionWorkspaceSandbox(shell, options) {
2648
2800
  }
2649
2801
  }
2650
2802
  if (shell.bootstrap) {
2651
- const boot = await shell.bootstrap(box, scope);
2803
+ const bootstrap = shell.bootstrap.bind(shell);
2804
+ const boot = await measure("bootstrap", () => bootstrap(box, scope));
2652
2805
  if (!boot.succeeded) {
2653
2806
  throw new Error(`bootstrap failed on new box ${name}`, { cause: boot.error });
2654
2807
  }
@@ -2793,7 +2946,8 @@ async function* detachedSandboxPromptEvents(box, prompt, options, backend) {
2793
2946
  ...options.expectedSecretProfileReceipt ? { expectedSecretProfileReceipt: options.expectedSecretProfileReceipt } : {},
2794
2947
  ...options.requireVisibleAssistantOutput !== void 0 ? { requireVisibleAssistantOutput: options.requireVisibleAssistantOutput } : {},
2795
2948
  ...options.timeoutMs !== void 0 ? { timeoutMs: options.timeoutMs } : {},
2796
- ...options.ttlMs !== void 0 ? { ttlMs: options.ttlMs } : {}
2949
+ ...options.ttlMs !== void 0 ? { ttlMs: options.ttlMs } : {},
2950
+ ...options.trace ? { traceId: options.trace.traceId } : {}
2797
2951
  });
2798
2952
  const admittedExecutionId = admission?.executionId ?? executionId;
2799
2953
  if (!admittedExecutionId) {
@@ -2810,6 +2964,7 @@ async function* detachedSandboxPromptEvents(box, prompt, options, backend) {
2810
2964
  sessionId: admission?.sessionId ?? sessionId,
2811
2965
  executionId: admittedExecutionId,
2812
2966
  lastEventId: lastEventId || "0",
2967
+ ...options.trace ? { traceId: options.trace.traceId } : {},
2813
2968
  ...options.signal ? { signal: options.signal } : {}
2814
2969
  });
2815
2970
  }
@@ -3031,13 +3186,22 @@ async function* streamSandboxPrompt(shell, box, message, options) {
3031
3186
  ...options?.requireVisibleAssistantOutput !== void 0 ? { requireVisibleAssistantOutput: options.requireVisibleAssistantOutput } : {},
3032
3187
  ...options?.expectedPublicProfileRequestDigest ? { expectedPublicProfileRequestDigest: options.expectedPublicProfileRequestDigest } : {},
3033
3188
  ...options?.expectedSecretProfileReceipt ? { expectedSecretProfileReceipt: options.expectedSecretProfileReceipt } : {},
3189
+ ...options?.trace ? { traceId: options.trace.traceId } : {},
3034
3190
  backend
3035
3191
  });
3036
3192
  const stream = options?.lastEventId ? dispatch() : resendRefusedDispatch(dispatch, options?.signal);
3037
3193
  emitSandboxActivity(options?.spend, box);
3038
3194
  let severedFinishReason = null;
3195
+ const promptTrace = options?.trace ? createPromptTrace(options.trace, options.traceParent, {
3196
+ "sandbox.id": box.id,
3197
+ ...options.model ? { "gen_ai.request.model": options.model } : {},
3198
+ ...options.harness ? { "sandbox.harness": options.harness } : {},
3199
+ ...options.detach ? { "sandbox.detached": true } : {}
3200
+ }) : void 0;
3201
+ let promptError;
3039
3202
  try {
3040
3203
  for await (const event of stream) {
3204
+ promptTrace?.observe(event);
3041
3205
  const step = classifySeveredStream(event);
3042
3206
  if (step) severedFinishReason = step.kind === "step-finish" && step.severed ? step.reason : null;
3043
3207
  if (severedFinishReason && isTerminalPromptEvent(event)) {
@@ -3051,8 +3215,12 @@ async function* streamSandboxPrompt(shell, box, message, options) {
3051
3215
  }
3052
3216
  yield event;
3053
3217
  }
3218
+ } catch (error) {
3219
+ promptError = error;
3220
+ throw error;
3054
3221
  } finally {
3055
3222
  emitSandboxActivity(options?.spend, box);
3223
+ promptTrace?.end(promptError);
3056
3224
  }
3057
3225
  if (severedFinishReason) {
3058
3226
  throw new Error(`sandbox model stream severed mid-turn (reason="${severedFinishReason}")`);
@@ -3345,4 +3513,4 @@ export {
3345
3513
  isTerminalPromptEvent,
3346
3514
  detectInteractiveQuestion
3347
3515
  };
3348
- //# sourceMappingURL=chunk-SUIH5B55.js.map
3516
+ //# sourceMappingURL=chunk-RJJRRR5Z.js.map