@codeworksh/harness 0.2.0-dev.20261013041531 → 0.2.0-dev.20261013121551

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/effect.mjs CHANGED
@@ -1,10 +1,10 @@
1
1
  import { n as __reExport, t as __exportAll } from "./rolldown-runtime-B4FMCO8f.mjs";
2
2
  import { a as io_exports, i as errors_exports, n as shell_exports, o as instance_exports, r as filesystem_exports, s as posix } from "./resource-DFZh0e-s.mjs";
3
- import "./daytona-ByTHZelc.mjs";
3
+ import "./daytona-D4sNwzSr.mjs";
4
4
  import "./vercel-3GouQSJI.mjs";
5
5
  import { a as decode, n as dimensionNote, o as load$4, r as resize } from "./resize-Dwv5dCgY.mjs";
6
6
  import { createRequire, registerHooks } from "node:module";
7
- import { Cause, Clock, Config, Context, Crypto, DateTime, Deferred, Duration, Effect, Exit, Fiber, FiberSet, FileSystem, Layer, LayerMap, Option, Path, Predicate, PubSub, Queue, Random, Ref, Result, Schedule, Schema, SchemaIssue, Scope, Semaphore, Stream, String as String$1 } from "effect";
7
+ import { Cause, Clock, Config, Context, Crypto, Data, DateTime, Deferred, Duration, Effect, Exit, Fiber, FiberSet, FileSystem, Layer, LayerMap, Metric, Option, Path, Predicate, PubSub, Queue, Random, Ref, Result, Schedule, Schema, SchemaIssue, Scope, Semaphore, Stream, String as String$1 } from "effect";
8
8
  import { Message, Model, getEnvApiKey, llm, normalizeOptionalNulls, stream } from "@codeworksh/aikit";
9
9
  import { MessageID as ID, SessionID as ID$1 } from "@codeworksh/plugin/ids";
10
10
  import { Migrator, SqlClient, SqlClient as SqlClient$1, SqlError, SqlSchema, SqlSchema as SqlSchema$1 } from "effect/sql";
@@ -2643,6 +2643,114 @@ const layer$22 = Layer.effect(Service$13, Effect.gen(function* () {
2643
2643
  return Service$13.of({ assemble });
2644
2644
  }));
2645
2645
  //#endregion
2646
+ //#region src/telemetry/telemetry.ts
2647
+ const durationBuckets = Metric.boundariesFromIterable([
2648
+ .01,
2649
+ .02,
2650
+ .04,
2651
+ .08,
2652
+ .16,
2653
+ .32,
2654
+ .64,
2655
+ 1.28,
2656
+ 2.56,
2657
+ 5.12,
2658
+ 10.24,
2659
+ 20.48,
2660
+ 40.96,
2661
+ 81.92
2662
+ ]);
2663
+ const llmDuration = Metric.histogram("codework.llm.duration", {
2664
+ description: "Provider request duration",
2665
+ attributes: { unit: "s" },
2666
+ boundaries: durationBuckets
2667
+ });
2668
+ const llmTtft = Metric.histogram("codework.llm.ttft", {
2669
+ description: "Time to first token: from request start to the first streamed content",
2670
+ attributes: { unit: "s" },
2671
+ boundaries: Metric.boundariesFromIterable([
2672
+ .001,
2673
+ .005,
2674
+ .01,
2675
+ .02,
2676
+ .04,
2677
+ .06,
2678
+ .08,
2679
+ .1,
2680
+ .25,
2681
+ .5,
2682
+ .75,
2683
+ 1,
2684
+ 2.5,
2685
+ 5,
2686
+ 7.5,
2687
+ 10
2688
+ ])
2689
+ });
2690
+ const llmTokens = Metric.counter("codework.llm.tokens", {
2691
+ description: "Tokens used, by type; cache and reasoning tokens are part of input and output",
2692
+ attributes: { unit: "{token}" },
2693
+ incremental: true
2694
+ });
2695
+ const llmCost = Metric.counter("codework.llm.cost", {
2696
+ description: "Provider request cost from the model catalog's prices",
2697
+ attributes: { unit: "USD" },
2698
+ incremental: true
2699
+ });
2700
+ const agentDuration = Metric.histogram("codework.agent.duration", {
2701
+ description: "Session run duration, across every turn it drains",
2702
+ attributes: { unit: "s" },
2703
+ boundaries: Metric.boundariesFromIterable([
2704
+ .1,
2705
+ .2,
2706
+ .4,
2707
+ .8,
2708
+ 1.6,
2709
+ 3.2,
2710
+ 6.4,
2711
+ 12.8,
2712
+ 25.6,
2713
+ 51.2,
2714
+ 102.4,
2715
+ 204.8,
2716
+ 409.6
2717
+ ])
2718
+ });
2719
+ const toolDuration = Metric.histogram("codework.tool.duration", {
2720
+ description: "Tool execution duration",
2721
+ attributes: { unit: "s" },
2722
+ boundaries: durationBuckets
2723
+ });
2724
+ const sandboxProvisionDuration = Metric.histogram("codework.sandbox.provision.duration", {
2725
+ description: "Sandbox driver create and wake duration",
2726
+ attributes: { unit: "s" },
2727
+ boundaries: durationBuckets
2728
+ });
2729
+ const sandboxExecDuration = Metric.histogram("codework.sandbox.exec.duration", {
2730
+ description: "Sandbox shell command duration",
2731
+ attributes: { unit: "s" },
2732
+ boundaries: durationBuckets
2733
+ });
2734
+ const sandboxActive = Metric.gauge("codework.sandbox.active", {
2735
+ description: "Managed and remote sandboxes with at least one mount",
2736
+ attributes: { unit: "{sandbox}" }
2737
+ });
2738
+ const record$1 = (metric, value, attributes) => Metric.update(Metric.withAttributes(metric, attributes), value);
2739
+ const adjust = (metric, delta, attributes) => Metric.modify(Metric.withAttributes(metric, attributes), delta);
2740
+ const errorType = (cause) => {
2741
+ if (Cause.hasInterruptsOnly(cause)) return "interrupted";
2742
+ const error = Cause.squash(cause);
2743
+ return typeof error === "object" && error !== null && "_tag" in error && typeof error._tag === "string" ? error._tag : "_OTHER";
2744
+ };
2745
+ const withError = (attributes, exit) => Exit.isSuccess(exit) ? attributes : {
2746
+ ...attributes,
2747
+ error: errorType(exit.cause)
2748
+ };
2749
+ const timed = (self, observe) => Effect.clockWith((clock) => {
2750
+ const start = clock.currentTimeMillisUnsafe();
2751
+ return self.pipe(Effect.onExit((exit) => observe((clock.currentTimeMillisUnsafe() - start) / 1e3, exit)));
2752
+ });
2753
+ //#endregion
2646
2754
  //#region src/sandbox/driver.ts
2647
2755
  var driver_exports = /* @__PURE__ */ __exportAll({});
2648
2756
  import * as import__codeworksh_plugin_sandbox_driver from "@codeworksh/plugin/sandbox/driver";
@@ -3029,6 +3137,22 @@ const parseJson = (driver, operation, value) => decodeUnknownJson(value).pipe(Ef
3029
3137
  operation,
3030
3138
  cause
3031
3139
  })));
3140
+ const provision = (driver, operation, self) => timed(self, (seconds, exit) => record$1(sandboxProvisionDuration, seconds, withError({
3141
+ driver,
3142
+ operation
3143
+ }, exit)));
3144
+ const timedShell = (shell, driver) => {
3145
+ const time = (self) => timed(self, (seconds, exit) => record$1(sandboxExecDuration, seconds, withError({ driver }, exit)));
3146
+ const stream = shell.stream;
3147
+ return {
3148
+ exec: (command, options) => time(shell.exec(command, options)),
3149
+ execArgv: (argv, options) => time(shell.execArgv(argv, options)),
3150
+ ...stream === void 0 ? {} : { stream: (command, options) => Stream.unwrap(Effect.clockWith((clock) => {
3151
+ const start = clock.currentTimeMillisUnsafe();
3152
+ return Effect.succeed(stream(command, options).pipe(Stream.onExit((exit) => record$1(sandboxExecDuration, (clock.currentTimeMillisUnsafe() - start) / 1e3, withError({ driver }, exit)))));
3153
+ })) }
3154
+ };
3155
+ };
3032
3156
  const make$14 = Effect.fn("Sandbox.Controller.make")(function* (options) {
3033
3157
  const sql = yield* SqlClient.SqlClient;
3034
3158
  const registry = yield* Registry;
@@ -3039,20 +3163,22 @@ const make$14 = Effect.fn("Sandbox.Controller.make")(function* (options) {
3039
3163
  const hostCreatedAt = asDate(yield* DateTime.now);
3040
3164
  const refCount = (id) => refs.get(id) ?? 0;
3041
3165
  const usage = (id, count = refCount(id)) => id === instance_exports.SandboxInstance.ID.local ? "pinned" : count > 0 ? "busy" : "idle";
3166
+ const activeChanged = (lease, delta) => lease.driver === void 0 ? Effect.void : adjust(sandboxActive, delta, { driver: lease.driver });
3042
3167
  const acquireLease = (id, lease) => gate.withPermits(1)(Effect.sync(() => {
3043
3168
  const wasIdle = refCount(id) === 0;
3044
3169
  refs.set(id, refCount(id) + 1);
3045
3170
  lease.held = true;
3046
3171
  return wasIdle;
3047
- }));
3172
+ })).pipe(Effect.tap((wasIdle) => wasIdle ? activeChanged(lease, 1) : Effect.void));
3048
3173
  const releaseLease = (id, lease) => Effect.gen(function* () {
3049
3174
  if (!lease.held) return;
3050
- yield* gate.withPermits(1)(Effect.sync(() => {
3175
+ if (yield* gate.withPermits(1)(Effect.sync(() => {
3051
3176
  const next = Math.max(0, refCount(id) - 1);
3052
3177
  if (next === 0) refs.delete(id);
3053
3178
  else refs.set(id, next);
3054
3179
  lease.held = false;
3055
- }));
3180
+ return next === 0;
3181
+ }))) yield* activeChanged(lease, -1);
3056
3182
  if (!lease.mounted) return;
3057
3183
  const now = yield* Effect.clockWith((clock) => clock.currentTimeMillis);
3058
3184
  yield* sql`
@@ -3222,7 +3348,7 @@ const make$14 = Effect.fn("Sandbox.Controller.make")(function* (options) {
3222
3348
  const attached = yield* runtime(row, "resolveCwd");
3223
3349
  return driver_exports.SandboxDriver.AbsolutePath.make(io_exports.SandboxIO.resolveMountCwd(attached.input.runtimeConfig.defaultCwd, override));
3224
3350
  });
3225
- const create = Effect.fn("Sandbox.Controller.create")(function* (input) {
3351
+ const create = Effect.fn("Sandbox.Controller.create", { level: "Info" })(function* (input) {
3226
3352
  const driver = yield* registry.get(input.driver.name);
3227
3353
  const config = yield* registry.decodeCreateConfig(input.driver.name, input.config);
3228
3354
  const id = input.instanceId ?? instance_exports.SandboxInstance.ID.create();
@@ -3238,10 +3364,10 @@ const make$14 = Effect.fn("Sandbox.Controller.make")(function* (options) {
3238
3364
  status: "provisioning",
3239
3365
  metadata: input.metadata
3240
3366
  });
3241
- const provisioned = yield* driver.create({
3367
+ const provisioned = yield* provision(driver.name, "create", driver.create({
3242
3368
  instanceId: id,
3243
3369
  config
3244
- }).pipe(Effect.catch((error) => persistProviderFailure(id, "faulted", error)));
3370
+ })).pipe(Effect.catch((error) => persistProviderFailure(id, "faulted", error)));
3245
3371
  return yield* Effect.gen(function* () {
3246
3372
  const encoded = yield* registry.encodeRuntimeConfig(driver.name, provisioned.runtimeConfig);
3247
3373
  const runtimeConfig = yield* encodeUnknownJson$1(encoded).pipe(Effect.mapError((cause) => (0, errors_exports.providerError)({
@@ -3330,7 +3456,8 @@ const make$14 = Effect.fn("Sandbox.Controller.make")(function* (options) {
3330
3456
  let invalidateBeforeAttach = false;
3331
3457
  const lease = reserved ?? {
3332
3458
  held: false,
3333
- mounted: false
3459
+ mounted: false,
3460
+ driver: void 0
3334
3461
  };
3335
3462
  if (id === instance_exports.SandboxInstance.ID.local) identity = {
3336
3463
  id,
@@ -3355,6 +3482,7 @@ const make$14 = Effect.fn("Sandbox.Controller.make")(function* (options) {
3355
3482
  });
3356
3483
  invalidateBeforeAttach = row.status !== "online";
3357
3484
  const attached = yield* runtime(row, "mount");
3485
+ lease.driver = attached.driver.name;
3358
3486
  identity = {
3359
3487
  id,
3360
3488
  driver: attached.driver.name,
@@ -3377,7 +3505,10 @@ const make$14 = Effect.fn("Sandbox.Controller.make")(function* (options) {
3377
3505
  lease.held = true;
3378
3506
  return idle;
3379
3507
  }));
3380
- if (reserved === void 0) yield* Effect.addFinalizer(() => releaseLease(id, lease));
3508
+ if (reserved === void 0) {
3509
+ yield* Effect.addFinalizer(() => releaseLease(id, lease));
3510
+ if (wasIdle) yield* activeChanged(lease, 1);
3511
+ }
3381
3512
  if (wasIdle && invalidateBeforeAttach) yield* transports.invalidate(id);
3382
3513
  }
3383
3514
  const handleTransportError = (error) => {
@@ -3407,7 +3538,7 @@ const make$14 = Effect.fn("Sandbox.Controller.make")(function* (options) {
3407
3538
  WHERE id = ${id}
3408
3539
  `.pipe(Effect.orDie);
3409
3540
  }
3410
- return Layer.mergeAll(Layer.succeed(io_exports.SandboxIO.Current, identity), Layer.succeed(io_exports.SandboxIO.FileSystem, filesystem_exports.SandboxFileSystem.withCwd(fs, identity.cwd)), Layer.succeed(io_exports.SandboxIO.Shell, (0, shell_exports.withCwd)(shell, identity.cwd))).pipe(io_exports.SandboxIO.withMutation);
3541
+ return Layer.mergeAll(Layer.succeed(io_exports.SandboxIO.Current, identity), Layer.succeed(io_exports.SandboxIO.FileSystem, filesystem_exports.SandboxFileSystem.withCwd(fs, identity.cwd)), Layer.succeed(io_exports.SandboxIO.Shell, timedShell((0, shell_exports.withCwd)(shell, identity.cwd), identity.driver))).pipe(io_exports.SandboxIO.withMutation);
3411
3542
  }));
3412
3543
  const mount = (id, mountOptions) => mountLayer(id, mountOptions);
3413
3544
  const withMount = (id, use, mountOptions) => use.pipe(Effect.provide(mount(id, mountOptions)), Effect.scoped);
@@ -3434,7 +3565,7 @@ const make$14 = Effect.fn("Sandbox.Controller.make")(function* (options) {
3434
3565
  });
3435
3566
  return yield* reload(id);
3436
3567
  });
3437
- const wake = Effect.fn("Sandbox.Controller.wake")(function* (id) {
3568
+ const wake = Effect.fn("Sandbox.Controller.wake", { level: "Info" })(function* (id) {
3438
3569
  if (id === instance_exports.SandboxInstance.ID.local) return hostInfo();
3439
3570
  const row = yield* requireRow(id);
3440
3571
  if (row.status === "removed") return yield* new errors_exports.SandboxRemovedError({
@@ -3448,7 +3579,7 @@ const make$14 = Effect.fn("Sandbox.Controller.make")(function* (options) {
3448
3579
  operation: "wake"
3449
3580
  });
3450
3581
  if (refCount(id) === 0) yield* transports.invalidate(id);
3451
- const observed = yield* attached.driver.wake(attached.input).pipe(Effect.catch((error) => (0, errors_exports.providerErrorIsNotFound)(error) ? markUnavailable(id, error) : persistProviderFailure(id, "faulted", error)));
3582
+ const observed = yield* provision(attached.driver.name, "wake", attached.driver.wake(attached.input)).pipe(Effect.catch((error) => (0, errors_exports.providerErrorIsNotFound)(error) ? markUnavailable(id, error) : persistProviderFailure(id, "faulted", error)));
3452
3583
  yield* updateObservation({
3453
3584
  id,
3454
3585
  status: observed.status,
@@ -3607,7 +3738,8 @@ const make$14 = Effect.fn("Sandbox.Controller.make")(function* (options) {
3607
3738
  const id = input.instanceId ?? instance_exports.SandboxInstance.ID.create();
3608
3739
  const lease = {
3609
3740
  held: false,
3610
- mounted: false
3741
+ mounted: false,
3742
+ driver: input.driver.name
3611
3743
  };
3612
3744
  return Layer.unwrap(Effect.gen(function* () {
3613
3745
  yield* acquireLease(id, lease);
@@ -5650,7 +5782,7 @@ const ancestors = (directory) => {
5650
5782
  * Which of `paths` are files. Absence is the expected answer and stays silent; any other failure
5651
5783
  * (permissions, a remote blip) is logged so a dropped file is not a mystery, then counts as absent.
5652
5784
  */
5653
- const present$1 = (fs, paths) => Effect.forEach(paths, (path) => fs.stat(path).pipe(Effect.map((stat) => stat.isFile), Effect.catch((error) => filesystem_exports.SandboxFileSystem.isNotFoundError(error.cause) ? Effect.succeed(false) : Effect.logWarning(`could not stat instruction file ${path}`, error.cause).pipe(Effect.as(false)))), { concurrency: "unbounded" }).pipe(Effect.map((hits) => new Set(paths.filter((_, index) => hits[index]))));
5785
+ const present$1 = (fs, paths) => Effect.forEach(paths, (path) => fs.exists(path).pipe(Effect.flatMap((exists) => exists ? fs.stat(path).pipe(Effect.map((stat) => stat.isFile)) : Effect.succeed(false)), Effect.catch((error) => filesystem_exports.SandboxFileSystem.isNotFoundError(error.cause) ? Effect.succeed(false) : Effect.logWarning(`could not stat instruction file ${path}`, error.cause).pipe(Effect.as(false)))), { concurrency: "unbounded" }).pipe(Effect.map((hits) => new Set(paths.filter((_, index) => hits[index]))));
5654
5786
  /** A file that stats but does not read falls through to the next candidate. */
5655
5787
  const first = Effect.fnUntraced(function* (fs, directory, files) {
5656
5788
  for (const name of names) {
@@ -9800,31 +9932,116 @@ const openWith = (options = {}) => Effect.fn("LLM.open")(function* (input, signa
9800
9932
  });
9801
9933
  });
9802
9934
  const open = openWith();
9935
+ const isProviderError = Schema.is(ProviderError);
9936
+ /** `error.type` for a request: the provider's failure reason, so a terminal and a thrown failure agree. */
9937
+ const requestErrorType = (input, exit) => {
9938
+ if (Exit.isFailure(exit)) {
9939
+ const error = Cause.squash(exit.cause);
9940
+ return isProviderError(error) ? error.reason._tag : errorType(exit.cause);
9941
+ }
9942
+ if (exit.value.outcome === "ended") return void 0;
9943
+ if (exit.value.reason === "aborted") return "aborted";
9944
+ return providerError(input, messageFailure(exit.value.message)).reason._tag;
9945
+ };
9946
+ const observe = Effect.fnUntraced(function* (input, timing, exit, drained) {
9947
+ const now = yield* Effect.clockWith((clock) => clock.currentTimeMillis);
9948
+ const labels = {
9949
+ provider: input.provider,
9950
+ model: input.model
9951
+ };
9952
+ const errorType = requestErrorType(input, exit);
9953
+ yield* record$1(llmDuration, (now - timing.start) / 1e3, errorType === void 0 ? labels : {
9954
+ ...labels,
9955
+ error: errorType
9956
+ });
9957
+ if (timing.firstToken !== void 0) {
9958
+ const ttft = (timing.firstToken - timing.start) / 1e3;
9959
+ yield* record$1(llmTtft, ttft, labels);
9960
+ yield* Effect.annotateCurrentSpan("gen_ai.response.time_to_first_chunk", ttft);
9961
+ }
9962
+ if (errorType !== void 0) yield* Effect.annotateCurrentSpan("error.type", errorType);
9963
+ const terminal = Exit.isSuccess(exit) ? exit.value : drained;
9964
+ if (terminal === void 0) return;
9965
+ const { message } = terminal;
9966
+ const { usage } = message;
9967
+ const inputTokens = usage.input + usage.cacheRead + usage.cacheWrite;
9968
+ const tokens = {
9969
+ input: inputTokens,
9970
+ output: usage.output,
9971
+ cache_read: usage.cacheRead,
9972
+ cache_write: usage.cacheWrite,
9973
+ reasoning: usage.reasoning ?? 0
9974
+ };
9975
+ for (const [type, count] of Object.entries(tokens)) if (count > 0) yield* record$1(llmTokens, count, {
9976
+ ...labels,
9977
+ type
9978
+ });
9979
+ if (usage.cost.total > 0) yield* record$1(llmCost, usage.cost.total, labels);
9980
+ yield* Effect.annotateCurrentSpan({
9981
+ "gen_ai.response.model": message.responseModel ?? message.model,
9982
+ ...message.responseId === void 0 ? {} : { "gen_ai.response.id": message.responseId },
9983
+ "gen_ai.response.finish_reasons": [terminal.reason],
9984
+ "gen_ai.usage.input_tokens": inputTokens,
9985
+ "gen_ai.usage.output_tokens": usage.output,
9986
+ "gen_ai.usage.cache_read.input_tokens": usage.cacheRead,
9987
+ "gen_ai.usage.cache_write.input_tokens": usage.cacheWrite,
9988
+ ...usage.reasoning === void 0 ? {} : { "gen_ai.usage.reasoning.output_tokens": usage.reasoning }
9989
+ });
9990
+ });
9991
+ const isToken = (event) => event.type === "text.delta" || event.type === "thinking.delta" || event.type === "toolcall.delta";
9992
+ /**
9993
+ * A failed terminal is the request's result, not an error. It leaves the
9994
+ * traced request as a failure so the span records an error status, and is
9995
+ * recovered just outside it.
9996
+ */
9997
+ var FailedTerminal = class extends Data.TaggedError("LLM.FailedTerminal") {};
9803
9998
  /**
9804
9999
  * Build a provider request from an opener. Tests use this with deterministic
9805
10000
  * aikit event streams; production uses `open` above.
9806
10001
  */
9807
10002
  const make$5 = (openStream) => {
9808
- const request = Effect.fn("LLM.run")(function* (input) {
10003
+ const request = Effect.fn("LLM.run", {
10004
+ kind: "client",
10005
+ level: "Info"
10006
+ })(function* (input) {
10007
+ yield* Effect.annotateCurrentSpan({
10008
+ "gen_ai.operation.name": "chat",
10009
+ "gen_ai.provider.name": input.provider,
10010
+ "gen_ai.request.model": input.model,
10011
+ "gen_ai.conversation.id": input.sessionId,
10012
+ "gen_ai.request.stream": true
10013
+ });
10014
+ const start = yield* Effect.clockWith((clock) => clock.currentTimeMillis);
10015
+ let firstToken;
10016
+ let drained;
10017
+ const markToken = (event) => firstToken !== void 0 || !isToken(event) ? Effect.void : Effect.clockWith((clock) => Effect.sync(() => {
10018
+ firstToken = clock.currentTimeMillisUnsafe();
10019
+ }));
9809
10020
  const signalScope = yield* Scope.make();
9810
10021
  yield* Effect.addFinalizer((exit) => Scope.close(signalScope, exit));
9811
10022
  const signal = yield* Effect.abortSignal.pipe(Scope.provide(signalScope));
9812
10023
  const abort = Scope.close(signalScope, Exit.void);
9813
- return yield* Effect.uninterruptibleMask((restore) => Effect.gen(function* () {
10024
+ const terminal = yield* Effect.uninterruptibleMask((restore) => Effect.gen(function* () {
9814
10025
  const iterable = yield* restore(openStream(input, signal)).pipe(Effect.onInterrupt(() => abort));
9815
- const consumer = yield* Stream.fromAsyncIterable(iterable, (cause) => providerErrorFromUnknown(input, cause)).pipe(Stream.runForEach(input.publisher.publish), Effect.andThen(input.publisher.terminal), Effect.interruptible).pipe(Effect.forkScoped({ startImmediately: true }));
10026
+ const consumer = yield* Stream.fromAsyncIterable(iterable, (cause) => providerErrorFromUnknown(input, cause)).pipe(Stream.runForEach((event) => Effect.andThen(markToken(event), input.publisher.publish(event))), Effect.andThen(input.publisher.terminal), Effect.interruptible).pipe(Effect.forkScoped({ startImmediately: true }));
9816
10027
  const awaited = yield* restore(Fiber.await(consumer)).pipe(Effect.onInterrupt(() => abort), Effect.exit);
9817
10028
  if (Exit.isFailure(awaited)) {
9818
- const drained = yield* Fiber.await(consumer);
9819
- if (Exit.isFailure(drained)) return yield* Effect.failCause(drained.cause);
10029
+ const consumed = yield* Fiber.await(consumer);
10030
+ if (Exit.isFailure(consumed)) return yield* Effect.failCause(consumed.cause);
10031
+ drained = consumed.value;
9820
10032
  return yield* Effect.failCause(awaited.cause);
9821
10033
  }
9822
10034
  const completed = awaited.value;
9823
10035
  if (Exit.isFailure(completed)) return yield* Effect.failCause(completed.cause);
9824
10036
  return completed.value;
9825
- }));
9826
- });
9827
- return (input) => request(input).pipe(Effect.scoped);
10037
+ })).pipe(Effect.onExit((exit) => observe(input, {
10038
+ start,
10039
+ firstToken
10040
+ }, exit, drained)));
10041
+ if (terminal.outcome === "failed") return yield* new FailedTerminal({ terminal });
10042
+ return terminal;
10043
+ });
10044
+ return (input) => request(input).pipe(Effect.catchTag("LLM.FailedTerminal", (failed) => Effect.succeed(failed.terminal)), Effect.scoped);
9828
10045
  };
9829
10046
  const run$1 = make$5(open);
9830
10047
  //#endregion
@@ -10735,6 +10952,17 @@ const errorPart = (call, cause, now) => ({
10735
10952
  end: endTime(call, now)
10736
10953
  }
10737
10954
  });
10955
+ const observeTool = (snapshot, call, seconds, exit) => {
10956
+ const tool = snapshot.tools.defs.some((def) => def.name === call.name) ? call.name : "_OTHER";
10957
+ const labels = Exit.isFailure(exit) ? {
10958
+ tool,
10959
+ error: errorType(exit.cause)
10960
+ } : exit.value.status === "completed" ? { tool } : {
10961
+ tool,
10962
+ error: exit.value.status
10963
+ };
10964
+ return record$1(toolDuration, seconds, labels);
10965
+ };
10738
10966
  const layer$3 = (options = {}) => Layer.effect(Service$7, Effect.gen(function* () {
10739
10967
  const context = yield* Service$13;
10740
10968
  const events = yield* Service$16;
@@ -10773,7 +11001,7 @@ const layer$3 = (options = {}) => Layer.effect(Service$7, Effect.gen(function* (
10773
11001
  if (draft === void 0) return;
10774
11002
  yield* publishFailure(draft, { _tag: "interrupted" });
10775
11003
  });
10776
- const settleTools = Effect.fn("Loop.settleTools")(function* (snapshot, message, reason, markCommitted) {
11004
+ const settleTools = Effect.fn("Loop.settleTools", { level: "Info" })(function* (snapshot, message, reason, markCommitted) {
10777
11005
  const pending = message.parts.filter((part) => part.type === "toolCall" && part.status === "pending");
10778
11006
  let interruptedCause;
10779
11007
  const messageId = ID.from(message.messageId);
@@ -10812,7 +11040,12 @@ const layer$3 = (options = {}) => Layer.effect(Service$7, Effect.gen(function* (
10812
11040
  return;
10813
11041
  }
10814
11042
  const settled = /* @__PURE__ */ new Set();
10815
- const handle = Effect.fn("Loop.handleTool")(function* (call) {
11043
+ const handle = Effect.fn("Loop.handleTool", { level: "Info" })(function* (call) {
11044
+ yield* Effect.annotateCurrentSpan({
11045
+ "gen_ai.operation.name": "execute_tool",
11046
+ "gen_ai.tool.name": call.name,
11047
+ "gen_ai.tool.call.id": call.callID
11048
+ });
10816
11049
  yield* events.publish(ToolStarted, {
10817
11050
  timestamp: yield* DateTime.now,
10818
11051
  sessionId: snapshot.sessionId,
@@ -10822,7 +11055,7 @@ const layer$3 = (options = {}) => Layer.effect(Service$7, Effect.gen(function* (
10822
11055
  label: snapshot.tools.defs.find((def) => def.name === call.name)?.label,
10823
11056
  arguments: call.arguments
10824
11057
  });
10825
- const handled = yield* snapshot.tools.handle(call, {
11058
+ const execute = snapshot.tools.handle(call, {
10826
11059
  sessionId: snapshot.sessionId,
10827
11060
  messageId,
10828
11061
  onProgress: (progress) => events.publish(ToolProgress$2, {
@@ -10833,6 +11066,7 @@ const layer$3 = (options = {}) => Layer.effect(Service$7, Effect.gen(function* (
10833
11066
  partial: progress.partial
10834
11067
  }).pipe(Effect.asVoid)
10835
11068
  }).pipe(Effect.catchCauseIf((cause) => !Cause.hasInterrupts(cause), (cause) => Effect.map(Effect.clockWith((clock) => clock.currentTimeMillis), (now) => errorPart(call, cause, now))));
11069
+ const handled = yield* timed(execute, (seconds, exit) => observeTool(snapshot, call, seconds, exit));
10836
11070
  yield* Effect.uninterruptible(Effect.gen(function* () {
10837
11071
  yield* settle(call, handled);
10838
11072
  yield* Effect.sync(() => settled.add(call.callID));
@@ -10855,7 +11089,7 @@ const layer$3 = (options = {}) => Layer.effect(Service$7, Effect.gen(function* (
10855
11089
  }));
10856
11090
  return interruptedCause;
10857
11091
  });
10858
- const runTurnAttempt = Effect.fn("Loop.runTurnAttempt")(function* (promotion, snapshot) {
11092
+ const runTurnAttempt = Effect.fn("Loop.runTurnAttempt", { level: "Info" })(function* (promotion, snapshot) {
10859
11093
  const sessionId = snapshot.sessionId;
10860
11094
  if (promotion !== void 0) {
10861
11095
  const cutoff = yield* events.latestSequence(sessionId);
@@ -10959,27 +11193,35 @@ const layer$3 = (options = {}) => Layer.effect(Service$7, Effect.gen(function* (
10959
11193
  });
10960
11194
  }));
10961
11195
  });
10962
- const runTurn = Effect.fn("Loop.runTurn")(function* (promotion, snapshot) {
11196
+ const runTurn = Effect.fn("Loop.runTurn", { level: "Info" })(function* (promotion, snapshot) {
10963
11197
  return yield* runTurnAttempt(promotion, snapshot);
10964
11198
  });
10965
- const run = Effect.fn("Loop.run")(function* (input) {
11199
+ const run = Effect.fn("Loop.run", { level: "Info" })(function* (input) {
10966
11200
  yield* healDanglingDraft(input.sessionId);
10967
11201
  const hasSteer = yield* inputs.hasPending(input.sessionId, "steer");
10968
11202
  const hasFollowUp = hasSteer ? false : yield* inputs.hasPending(input.sessionId, "followUp");
10969
11203
  if (!input.force && !hasSteer && !hasFollowUp) return;
11204
+ yield* Effect.annotateCurrentSpan({
11205
+ "gen_ai.operation.name": "invoke_agent",
11206
+ "gen_ai.agent.name": "codework",
11207
+ "gen_ai.conversation.id": input.sessionId
11208
+ });
10970
11209
  let promotion = hasSteer ? "steer" : hasFollowUp ? "followUp" : void 0;
10971
- let shouldRun = true;
10972
- while (shouldRun) {
10973
- const snapshot = yield* state.snapshot(input.sessionId);
10974
- let needsContinuation = true;
10975
- while (needsContinuation) {
10976
- const result = yield* runTurn(promotion, snapshot);
10977
- promotion = "steer";
10978
- needsContinuation = result.needsContinuation || (yield* inputs.hasPending(input.sessionId, "steer"));
11210
+ const drain = Effect.gen(function* () {
11211
+ let shouldRun = true;
11212
+ while (shouldRun) {
11213
+ const snapshot = yield* state.snapshot(input.sessionId);
11214
+ let needsContinuation = true;
11215
+ while (needsContinuation) {
11216
+ const result = yield* runTurn(promotion, snapshot);
11217
+ promotion = "steer";
11218
+ needsContinuation = result.needsContinuation || (yield* inputs.hasPending(input.sessionId, "steer"));
11219
+ }
11220
+ shouldRun = yield* inputs.hasPending(input.sessionId, "followUp");
11221
+ promotion = shouldRun ? "followUp" : void 0;
10979
11222
  }
10980
- shouldRun = yield* inputs.hasPending(input.sessionId, "followUp");
10981
- promotion = shouldRun ? "followUp" : void 0;
10982
- }
11223
+ });
11224
+ yield* timed(drain, (seconds, exit) => record$1(agentDuration, seconds, withError({}, exit)));
10983
11225
  });
10984
11226
  return Service$7.of({ run });
10985
11227
  }));
@@ -11300,7 +11542,7 @@ const runtimeOverrides = (driver, value) => {
11300
11542
  }));
11301
11543
  return Effect.succeed(value);
11302
11544
  };
11303
- const create$2 = Effect.fn("Sandbox.create")(function* (input) {
11545
+ const create$2 = Effect.fn("Sandbox.create", { level: "Info" })(function* (input) {
11304
11546
  const controller = yield* Controller;
11305
11547
  const registry = yield* Registry;
11306
11548
  const driverName = name(input.driver);
@@ -11326,7 +11568,7 @@ const Selection = Schema.Union([Schema.Struct({ id: instance_exports.SandboxInst
11326
11568
  driver: Schema.String,
11327
11569
  providerResourceId: (0, schema_exports$9.optional)(Schema.String)
11328
11570
  })]);
11329
- const resolve = Effect.fn("Sandbox.resolve")(function* (input) {
11571
+ const resolve = Effect.fn("Sandbox.resolve", { level: "Info" })(function* (input) {
11330
11572
  if ("id" in input) {
11331
11573
  const found = yield* get$1(input.id);
11332
11574
  if (Option.isNone(found)) return yield* new errors_exports.SandboxNotFoundError({ id: input.id });
@@ -11379,7 +11621,7 @@ const list = Effect.fn("Sandbox.list")(function* (input) {
11379
11621
  const refresh = Effect.fn("Sandbox.refresh")(function* (id) {
11380
11622
  return yield* (yield* Controller).refresh(id);
11381
11623
  });
11382
- const wake = Effect.fn("Sandbox.wake")(function* (id) {
11624
+ const wake = Effect.fn("Sandbox.wake", { level: "Info" })(function* (id) {
11383
11625
  return yield* (yield* Controller).wake(id);
11384
11626
  });
11385
11627
  const stop = Effect.fn("Sandbox.stop")(function* (id) {