@codeworksh/harness 0.2.0 → 0.3.0-canary.29.1.g334eb9ee

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 { a as decode, c as importModule, d as share, l as lendResolve, n as dimensionNote, o as load$5, r as resize, s as load$4, u as resolveModule } from "./resize-D34zcVQl.mjs";
4
+ import "./daytona-D4sNwzSr.mjs";
4
5
  import "./vercel-3GouQSJI.mjs";
5
- import { a as decode, n as dimensionNote, o as load$4, r as resize } from "./resize-Dwv5dCgY.mjs";
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";
6
+ import { createRequire } from "node:module";
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";
@@ -16,21 +16,20 @@ import { NodeChildProcessSpawner, NodeCrypto, NodeFileSystem, NodePath } from "@
16
16
  import { Model as Model$1, VariantSchema } from "effect/schema";
17
17
  import { Hex } from "effect/encoding";
18
18
  import { MemoryProvider, RealFSProvider, SqliteProvider, create } from "@platformatic/vfs";
19
- import fs, { statSync } from "node:fs";
19
+ import fs from "node:fs";
20
20
  import { ChildProcess, ChildProcessSpawner } from "effect/process";
21
21
  import { Buffer as Buffer$1 } from "node:buffer";
22
- import { Bash } from "just-bash";
22
+ import { fileURLToPath, pathToFileURL } from "node:url";
23
23
  import { DirectoryNotFoundError, DirectoryNotFoundError as DirectoryNotFoundError$1, Info, Info as Info$1, NotDirectoryError, NotDirectoryError as NotDirectoryError$1, Service, Service as Service$8 } from "@codeworksh/plugin/location";
24
24
  import { generateModels } from "@codeworksh/aikit/modelgen";
25
25
  import { JsonGitHubCopilotAuthStorage, getGitHubCopilotApiKey } from "@codeworksh/aikit/oauth/github/copilot";
26
26
  import { JsonOpenAICodexAuthStorage, getOpenAICodexApiKey } from "@codeworksh/aikit/oauth/openai/codex";
27
27
  import { parse, printParseErrorCode } from "jsonc-parser";
28
28
  import * as Section from "@codeworksh/plugin/plugin/section";
29
+ import ignore from "ignore";
30
+ import { parse as parse$1 } from "yaml";
29
31
  import * as Diff from "diff";
30
32
  import { FetchHttpClient, HttpClient } from "effect/http";
31
- import { fileURLToPath, pathToFileURL } from "node:url";
32
- import path from "node:path";
33
- import { Script, constants } from "node:vm";
34
33
  import npa from "npm-package-arg";
35
34
  import * as AikitFailure from "@codeworksh/aikit/failure";
36
35
  import "@codeworksh/plugin/tool/error";
@@ -380,7 +379,7 @@ var db_exports = /* @__PURE__ */ __exportAll({
380
379
  defaultLayer: () => defaultLayer$1,
381
380
  layer: () => layer$26,
382
381
  locationConfig: () => locationConfig,
383
- path: () => path$2,
382
+ path: () => path$1,
384
383
  resolveDatabaseLocation: () => resolveDatabaseLocation
385
384
  });
386
385
  const setup = Effect.gen(function* () {
@@ -407,11 +406,11 @@ function resolveDatabaseLocation(configured, data) {
407
406
  if (configured === ":memory:" || posix.isAbsolute(configured)) return configured;
408
407
  return posix.join(data, configured);
409
408
  }
410
- const path$2 = Effect.fn("Database.path")(function* (data) {
409
+ const path$1 = Effect.fn("Database.path")(function* (data) {
411
410
  const directory = data ?? (yield* resolve$4()).data;
412
411
  return resolveDatabaseLocation(yield* locationConfig, directory);
413
412
  });
414
- const defaultLayer$1 = Layer.unwrap(path$2().pipe(Effect.map(layer$26)));
413
+ const defaultLayer$1 = Layer.unwrap(path$1().pipe(Effect.map(layer$26)));
415
414
  //#endregion
416
415
  //#region src/event/schema.ts
417
416
  var schema_exports$7 = /* @__PURE__ */ __exportAll({});
@@ -2643,6 +2642,114 @@ const layer$22 = Layer.effect(Service$13, Effect.gen(function* () {
2643
2642
  return Service$13.of({ assemble });
2644
2643
  }));
2645
2644
  //#endregion
2645
+ //#region src/telemetry/telemetry.ts
2646
+ const durationBuckets = Metric.boundariesFromIterable([
2647
+ .01,
2648
+ .02,
2649
+ .04,
2650
+ .08,
2651
+ .16,
2652
+ .32,
2653
+ .64,
2654
+ 1.28,
2655
+ 2.56,
2656
+ 5.12,
2657
+ 10.24,
2658
+ 20.48,
2659
+ 40.96,
2660
+ 81.92
2661
+ ]);
2662
+ const llmDuration = Metric.histogram("codework.llm.duration", {
2663
+ description: "Provider request duration",
2664
+ attributes: { unit: "s" },
2665
+ boundaries: durationBuckets
2666
+ });
2667
+ const llmTtft = Metric.histogram("codework.llm.ttft", {
2668
+ description: "Time to first token: from request start to the first streamed content",
2669
+ attributes: { unit: "s" },
2670
+ boundaries: Metric.boundariesFromIterable([
2671
+ .001,
2672
+ .005,
2673
+ .01,
2674
+ .02,
2675
+ .04,
2676
+ .06,
2677
+ .08,
2678
+ .1,
2679
+ .25,
2680
+ .5,
2681
+ .75,
2682
+ 1,
2683
+ 2.5,
2684
+ 5,
2685
+ 7.5,
2686
+ 10
2687
+ ])
2688
+ });
2689
+ const llmTokens = Metric.counter("codework.llm.tokens", {
2690
+ description: "Tokens used, by type; cache and reasoning tokens are part of input and output",
2691
+ attributes: { unit: "{token}" },
2692
+ incremental: true
2693
+ });
2694
+ const llmCost = Metric.counter("codework.llm.cost", {
2695
+ description: "Provider request cost from the model catalog's prices",
2696
+ attributes: { unit: "USD" },
2697
+ incremental: true
2698
+ });
2699
+ const agentDuration = Metric.histogram("codework.agent.duration", {
2700
+ description: "Session run duration, across every turn it drains",
2701
+ attributes: { unit: "s" },
2702
+ boundaries: Metric.boundariesFromIterable([
2703
+ .1,
2704
+ .2,
2705
+ .4,
2706
+ .8,
2707
+ 1.6,
2708
+ 3.2,
2709
+ 6.4,
2710
+ 12.8,
2711
+ 25.6,
2712
+ 51.2,
2713
+ 102.4,
2714
+ 204.8,
2715
+ 409.6
2716
+ ])
2717
+ });
2718
+ const toolDuration = Metric.histogram("codework.tool.duration", {
2719
+ description: "Tool execution duration",
2720
+ attributes: { unit: "s" },
2721
+ boundaries: durationBuckets
2722
+ });
2723
+ const sandboxProvisionDuration = Metric.histogram("codework.sandbox.provision.duration", {
2724
+ description: "Sandbox driver create and wake duration",
2725
+ attributes: { unit: "s" },
2726
+ boundaries: durationBuckets
2727
+ });
2728
+ const sandboxExecDuration = Metric.histogram("codework.sandbox.exec.duration", {
2729
+ description: "Sandbox shell command duration",
2730
+ attributes: { unit: "s" },
2731
+ boundaries: durationBuckets
2732
+ });
2733
+ const sandboxActive = Metric.gauge("codework.sandbox.active", {
2734
+ description: "Managed and remote sandboxes with at least one mount",
2735
+ attributes: { unit: "{sandbox}" }
2736
+ });
2737
+ const record$1 = (metric, value, attributes) => Metric.update(Metric.withAttributes(metric, attributes), value);
2738
+ const adjust = (metric, delta, attributes) => Metric.modify(Metric.withAttributes(metric, attributes), delta);
2739
+ const errorType = (cause) => {
2740
+ if (Cause.hasInterruptsOnly(cause)) return "interrupted";
2741
+ const error = Cause.squash(cause);
2742
+ return typeof error === "object" && error !== null && "_tag" in error && typeof error._tag === "string" ? error._tag : "_OTHER";
2743
+ };
2744
+ const withError = (attributes, exit) => Exit.isSuccess(exit) ? attributes : {
2745
+ ...attributes,
2746
+ error: errorType(exit.cause)
2747
+ };
2748
+ const timed = (self, observe) => Effect.clockWith((clock) => {
2749
+ const start = clock.currentTimeMillisUnsafe();
2750
+ return self.pipe(Effect.onExit((exit) => observe((clock.currentTimeMillisUnsafe() - start) / 1e3, exit)));
2751
+ });
2752
+ //#endregion
2646
2753
  //#region src/sandbox/driver.ts
2647
2754
  var driver_exports = /* @__PURE__ */ __exportAll({});
2648
2755
  import * as import__codeworksh_plugin_sandbox_driver from "@codeworksh/plugin/sandbox/driver";
@@ -3029,6 +3136,22 @@ const parseJson = (driver, operation, value) => decodeUnknownJson(value).pipe(Ef
3029
3136
  operation,
3030
3137
  cause
3031
3138
  })));
3139
+ const provision = (driver, operation, self) => timed(self, (seconds, exit) => record$1(sandboxProvisionDuration, seconds, withError({
3140
+ driver,
3141
+ operation
3142
+ }, exit)));
3143
+ const timedShell = (shell, driver) => {
3144
+ const time = (self) => timed(self, (seconds, exit) => record$1(sandboxExecDuration, seconds, withError({ driver }, exit)));
3145
+ const stream = shell.stream;
3146
+ return {
3147
+ exec: (command, options) => time(shell.exec(command, options)),
3148
+ execArgv: (argv, options) => time(shell.execArgv(argv, options)),
3149
+ ...stream === void 0 ? {} : { stream: (command, options) => Stream.unwrap(Effect.clockWith((clock) => {
3150
+ const start = clock.currentTimeMillisUnsafe();
3151
+ return Effect.succeed(stream(command, options).pipe(Stream.onExit((exit) => record$1(sandboxExecDuration, (clock.currentTimeMillisUnsafe() - start) / 1e3, withError({ driver }, exit)))));
3152
+ })) }
3153
+ };
3154
+ };
3032
3155
  const make$14 = Effect.fn("Sandbox.Controller.make")(function* (options) {
3033
3156
  const sql = yield* SqlClient.SqlClient;
3034
3157
  const registry = yield* Registry;
@@ -3039,20 +3162,22 @@ const make$14 = Effect.fn("Sandbox.Controller.make")(function* (options) {
3039
3162
  const hostCreatedAt = asDate(yield* DateTime.now);
3040
3163
  const refCount = (id) => refs.get(id) ?? 0;
3041
3164
  const usage = (id, count = refCount(id)) => id === instance_exports.SandboxInstance.ID.local ? "pinned" : count > 0 ? "busy" : "idle";
3165
+ const activeChanged = (lease, delta) => lease.driver === void 0 ? Effect.void : adjust(sandboxActive, delta, { driver: lease.driver });
3042
3166
  const acquireLease = (id, lease) => gate.withPermits(1)(Effect.sync(() => {
3043
3167
  const wasIdle = refCount(id) === 0;
3044
3168
  refs.set(id, refCount(id) + 1);
3045
3169
  lease.held = true;
3046
3170
  return wasIdle;
3047
- }));
3171
+ })).pipe(Effect.tap((wasIdle) => wasIdle ? activeChanged(lease, 1) : Effect.void));
3048
3172
  const releaseLease = (id, lease) => Effect.gen(function* () {
3049
3173
  if (!lease.held) return;
3050
- yield* gate.withPermits(1)(Effect.sync(() => {
3174
+ if (yield* gate.withPermits(1)(Effect.sync(() => {
3051
3175
  const next = Math.max(0, refCount(id) - 1);
3052
3176
  if (next === 0) refs.delete(id);
3053
3177
  else refs.set(id, next);
3054
3178
  lease.held = false;
3055
- }));
3179
+ return next === 0;
3180
+ }))) yield* activeChanged(lease, -1);
3056
3181
  if (!lease.mounted) return;
3057
3182
  const now = yield* Effect.clockWith((clock) => clock.currentTimeMillis);
3058
3183
  yield* sql`
@@ -3222,7 +3347,7 @@ const make$14 = Effect.fn("Sandbox.Controller.make")(function* (options) {
3222
3347
  const attached = yield* runtime(row, "resolveCwd");
3223
3348
  return driver_exports.SandboxDriver.AbsolutePath.make(io_exports.SandboxIO.resolveMountCwd(attached.input.runtimeConfig.defaultCwd, override));
3224
3349
  });
3225
- const create = Effect.fn("Sandbox.Controller.create")(function* (input) {
3350
+ const create = Effect.fn("Sandbox.Controller.create", { level: "Info" })(function* (input) {
3226
3351
  const driver = yield* registry.get(input.driver.name);
3227
3352
  const config = yield* registry.decodeCreateConfig(input.driver.name, input.config);
3228
3353
  const id = input.instanceId ?? instance_exports.SandboxInstance.ID.create();
@@ -3238,10 +3363,10 @@ const make$14 = Effect.fn("Sandbox.Controller.make")(function* (options) {
3238
3363
  status: "provisioning",
3239
3364
  metadata: input.metadata
3240
3365
  });
3241
- const provisioned = yield* driver.create({
3366
+ const provisioned = yield* provision(driver.name, "create", driver.create({
3242
3367
  instanceId: id,
3243
3368
  config
3244
- }).pipe(Effect.catch((error) => persistProviderFailure(id, "faulted", error)));
3369
+ })).pipe(Effect.catch((error) => persistProviderFailure(id, "faulted", error)));
3245
3370
  return yield* Effect.gen(function* () {
3246
3371
  const encoded = yield* registry.encodeRuntimeConfig(driver.name, provisioned.runtimeConfig);
3247
3372
  const runtimeConfig = yield* encodeUnknownJson$1(encoded).pipe(Effect.mapError((cause) => (0, errors_exports.providerError)({
@@ -3330,7 +3455,8 @@ const make$14 = Effect.fn("Sandbox.Controller.make")(function* (options) {
3330
3455
  let invalidateBeforeAttach = false;
3331
3456
  const lease = reserved ?? {
3332
3457
  held: false,
3333
- mounted: false
3458
+ mounted: false,
3459
+ driver: void 0
3334
3460
  };
3335
3461
  if (id === instance_exports.SandboxInstance.ID.local) identity = {
3336
3462
  id,
@@ -3355,6 +3481,7 @@ const make$14 = Effect.fn("Sandbox.Controller.make")(function* (options) {
3355
3481
  });
3356
3482
  invalidateBeforeAttach = row.status !== "online";
3357
3483
  const attached = yield* runtime(row, "mount");
3484
+ lease.driver = attached.driver.name;
3358
3485
  identity = {
3359
3486
  id,
3360
3487
  driver: attached.driver.name,
@@ -3377,7 +3504,10 @@ const make$14 = Effect.fn("Sandbox.Controller.make")(function* (options) {
3377
3504
  lease.held = true;
3378
3505
  return idle;
3379
3506
  }));
3380
- if (reserved === void 0) yield* Effect.addFinalizer(() => releaseLease(id, lease));
3507
+ if (reserved === void 0) {
3508
+ yield* Effect.addFinalizer(() => releaseLease(id, lease));
3509
+ if (wasIdle) yield* activeChanged(lease, 1);
3510
+ }
3381
3511
  if (wasIdle && invalidateBeforeAttach) yield* transports.invalidate(id);
3382
3512
  }
3383
3513
  const handleTransportError = (error) => {
@@ -3407,7 +3537,7 @@ const make$14 = Effect.fn("Sandbox.Controller.make")(function* (options) {
3407
3537
  WHERE id = ${id}
3408
3538
  `.pipe(Effect.orDie);
3409
3539
  }
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);
3540
+ 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
3541
  }));
3412
3542
  const mount = (id, mountOptions) => mountLayer(id, mountOptions);
3413
3543
  const withMount = (id, use, mountOptions) => use.pipe(Effect.provide(mount(id, mountOptions)), Effect.scoped);
@@ -3434,7 +3564,7 @@ const make$14 = Effect.fn("Sandbox.Controller.make")(function* (options) {
3434
3564
  });
3435
3565
  return yield* reload(id);
3436
3566
  });
3437
- const wake = Effect.fn("Sandbox.Controller.wake")(function* (id) {
3567
+ const wake = Effect.fn("Sandbox.Controller.wake", { level: "Info" })(function* (id) {
3438
3568
  if (id === instance_exports.SandboxInstance.ID.local) return hostInfo();
3439
3569
  const row = yield* requireRow(id);
3440
3570
  if (row.status === "removed") return yield* new errors_exports.SandboxRemovedError({
@@ -3448,7 +3578,7 @@ const make$14 = Effect.fn("Sandbox.Controller.make")(function* (options) {
3448
3578
  operation: "wake"
3449
3579
  });
3450
3580
  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)));
3581
+ 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
3582
  yield* updateObservation({
3453
3583
  id,
3454
3584
  status: observed.status,
@@ -3607,7 +3737,8 @@ const make$14 = Effect.fn("Sandbox.Controller.make")(function* (options) {
3607
3737
  const id = input.instanceId ?? instance_exports.SandboxInstance.ID.create();
3608
3738
  const lease = {
3609
3739
  held: false,
3610
- mounted: false
3740
+ mounted: false,
3741
+ driver: input.driver.name
3611
3742
  };
3612
3743
  return Layer.unwrap(Effect.gen(function* () {
3613
3744
  yield* acquireLease(id, lease);
@@ -3875,7 +4006,8 @@ const fromBash = (bash) => {
3875
4006
  });
3876
4007
  return shell_exports.Shell.of((0, shell_exports.fromExec)({ exec }));
3877
4008
  };
3878
- const make$12 = (vfs, cwd) => fromBash(new Bash({
4009
+ const JustBash = load$4("just-bash").pipe(Effect.map((module) => module), Effect.orDie);
4010
+ const make$12 = (module, vfs, cwd) => fromBash(new module.Bash({
3879
4011
  fs: bridge(vfs, cwd),
3880
4012
  cwd
3881
4013
  }));
@@ -3884,9 +4016,10 @@ const make$12 = (vfs, cwd) => fromBash(new Bash({
3884
4016
  * mount binds its cwd. This matches Flue's BashFactory ownership: mounts share
3885
4017
  * the VFS, but shell state (functions, options, command hashes) is mount-local.
3886
4018
  */
3887
- const transport$1 = (inner) => Layer.provideMerge(Layer.effect(shell_exports.Shell, Effect.map(Vfs, (vfs) => {
3888
- const root = make$12(vfs, "/");
3889
- return (0, shell_exports.perMount)(root, (cwd) => make$12(vfs, cwd));
4019
+ const transport$1 = (inner) => Layer.provideMerge(Layer.effect(shell_exports.Shell, Effect.gen(function* () {
4020
+ const module = yield* JustBash;
4021
+ const vfs = yield* Vfs;
4022
+ return (0, shell_exports.perMount)(make$12(module, vfs, "/"), (cwd) => make$12(module, vfs, cwd));
3890
4023
  })), inner);
3891
4024
  //#endregion
3892
4025
  //#region src/sandbox/virtual.ts
@@ -4333,7 +4466,8 @@ const up = Effect.fn("SandboxFileSystem.up")(function* (fs, options) {
4333
4466
  const isDirectory$1 = Effect.fn("SandboxFileSystem.isDirectory")(function* (fs, path) {
4334
4467
  return yield* fs.stat(path).pipe(Effect.map((stat) => stat.isDirectory), Effect.orElseSucceed(() => false));
4335
4468
  });
4336
- Effect.fn("SandboxFileSystem.isFile")(function* (fs, path) {
4469
+ /** True when `path` exists and is a regular file; false when it is absent or unreadable. */
4470
+ const isFile = Effect.fn("SandboxFileSystem.isFile")(function* (fs, path) {
4337
4471
  return yield* fs.stat(path).pipe(Effect.map((stat) => stat.isFile), Effect.orElseSucceed(() => false));
4338
4472
  });
4339
4473
  /** Read a file, or `undefined` when it is missing or unreadable. */
@@ -5181,7 +5315,7 @@ var settings_exports = /* @__PURE__ */ __exportAll({
5181
5315
  layer: () => layer$8,
5182
5316
  load: () => load$3,
5183
5317
  modules: () => modules,
5184
- parse: () => parse$4,
5318
+ parse: () => parse$5,
5185
5319
  paths: () => paths,
5186
5320
  projectRoot: () => projectRoot,
5187
5321
  userConfigDir: () => userConfigDir
@@ -5270,7 +5404,7 @@ const decodeFormatter = SchemaIssue.makeFormatterStandardSchemaV1({ leafHook: (i
5270
5404
  const message = "annotations" in issue ? issue.annotations?.message : void 0;
5271
5405
  return typeof message === "string" ? message.replaceAll(/\s*\n\s*/g, " ") : "invalid value";
5272
5406
  } });
5273
- const parse$4 = Effect.fn("Settings.parse")(function* (path, source) {
5407
+ const parse$5 = Effect.fn("Settings.parse")(function* (path, source) {
5274
5408
  const errors = [];
5275
5409
  const json = parse(source, errors, {
5276
5410
  allowTrailingComma: true,
@@ -5318,7 +5452,7 @@ const attempt = (path) => fileSystem.readFileString(path).pipe(Effect.mapError((
5318
5452
  path,
5319
5453
  reason: "read",
5320
5454
  detail: error.reason._tag
5321
- })), Effect.flatMap((source) => parse$4(path, source)));
5455
+ })), Effect.flatMap((source) => parse$5(path, source)));
5322
5456
  /**
5323
5457
  * One read of every layer. Exported for the harness constructor, which needs the plugin
5324
5458
  * selection before any layer is built; everything else goes through `Service`.
@@ -5624,6 +5758,22 @@ const defaultPromptPlugin = (0, plugin_exports$1.define)({
5624
5758
  })
5625
5759
  });
5626
5760
  //#endregion
5761
+ //#region src/plugin/builtin/prompt/walk.ts
5762
+ const stripBom$1 = (raw) => raw.charCodeAt(0) === 65279 ? raw.slice(1) : raw;
5763
+ /** `directory` and every ancestor up to the sandbox root, closest first. */
5764
+ const ancestors = (directory) => {
5765
+ const chain = [];
5766
+ for (let current = directory;; current = posix.dirname(current)) {
5767
+ chain.push(current);
5768
+ if (posix.dirname(current) === current) return chain;
5769
+ }
5770
+ };
5771
+ /**
5772
+ * Which of `paths` exist as the requested kind (`entry` takes either). Absence stays silent; any other failure
5773
+ * (permissions, a remote blip) is logged so a dropped entry is not a mystery, then counts as absent.
5774
+ */
5775
+ const present$1 = (fs, paths, kind, label) => Effect.forEach(paths, (path) => fs.exists(path).pipe(Effect.filterOrElse((exists) => !exists || kind === "entry", () => fs.stat(path).pipe(Effect.map((stat) => kind === "file" ? stat.isFile : stat.isDirectory))), Effect.catch((error) => filesystem_exports.SandboxFileSystem.isNotFoundError(error.cause) ? Effect.succeed(false) : Effect.logWarning(`could not stat ${label} ${path}`, error.cause).pipe(Effect.as(false)))), { concurrency: "unbounded" }).pipe(Effect.map((hits) => new Set(paths.filter((_, index) => hits[index]))));
5776
+ //#endregion
5627
5777
  //#region src/plugin/builtin/prompt/instruction.ts
5628
5778
  /** First readable match per directory wins. */
5629
5779
  const names = [
@@ -5635,22 +5785,9 @@ const names = [
5635
5785
  "CLAUDE.MD"
5636
5786
  ];
5637
5787
  const content = (raw) => {
5638
- const text = raw.charCodeAt(0) === 65279 ? raw.slice(1) : raw;
5788
+ const text = stripBom$1(raw);
5639
5789
  return text.trim().length === 0 ? void 0 : text;
5640
5790
  };
5641
- /** `directory` and every ancestor up to the sandbox root, closest first. */
5642
- const ancestors = (directory) => {
5643
- const chain = [];
5644
- for (let current = directory;; current = posix.dirname(current)) {
5645
- chain.push(current);
5646
- if (posix.dirname(current) === current) return chain;
5647
- }
5648
- };
5649
- /**
5650
- * Which of `paths` are files. Absence is the expected answer and stays silent; any other failure
5651
- * (permissions, a remote blip) is logged so a dropped file is not a mystery, then counts as absent.
5652
- */
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]))));
5654
5791
  /** A file that stats but does not read falls through to the next candidate. */
5655
5792
  const first = Effect.fnUntraced(function* (fs, directory, files) {
5656
5793
  for (const name of names) {
@@ -5678,9 +5815,9 @@ const shadowing = (location) => {
5678
5815
  return location.space.location.startsWith(`${main}/`) ? main : void 0;
5679
5816
  };
5680
5817
  /** One file per directory from the sandbox root down to the cwd, closest last so it has the final word. */
5681
- const discover = Effect.fnUntraced(function* (fs, location) {
5818
+ const discover$1 = Effect.fnUntraced(function* (fs, location) {
5682
5819
  const chain = ancestors(location.directory);
5683
- const files = yield* present$1(fs, chain.flatMap((directory) => names.map((name) => posix.join(directory, name))));
5820
+ const files = yield* present$1(fs, chain.flatMap((directory) => names.map((name) => posix.join(directory, name))), "file", "instruction file");
5684
5821
  const found = yield* Effect.forEach(chain, (directory) => first(fs, directory, files), { concurrency: "unbounded" });
5685
5822
  const main = shadowing(location);
5686
5823
  const worktree = main === void 0 ? void 0 : found[chain.indexOf(location.space.location)];
@@ -5696,7 +5833,7 @@ const instructionPlugin = (0, plugin_exports$1.define)({
5696
5833
  kind: "prompt",
5697
5834
  setup: Effect.fn("InstructionPlugin.setup")(function* (ctx) {
5698
5835
  const fs = yield* io_exports.SandboxIO.FileSystem;
5699
- const files = (yield* discover(fs, ctx.location)).filter((file) => file.content !== void 0);
5836
+ const files = (yield* discover$1(fs, ctx.location)).filter((file) => file.content !== void 0);
5700
5837
  if (files.length === 0) return;
5701
5838
  const prompt = ctx.plugin.prompt;
5702
5839
  prompt.sections.append(Section.ProjectContext, "Project-Specific Instructions & Guidelines:");
@@ -5704,6 +5841,236 @@ const instructionPlugin = (0, plugin_exports$1.define)({
5704
5841
  })
5705
5842
  });
5706
5843
  //#endregion
5844
+ //#region src/plugin/builtin/prompt/skill.ts
5845
+ /** Max name length per Agent Skills spec. */
5846
+ const MAX_NAME_LENGTH = 64;
5847
+ /** Max description length per Agent Skills spec. */
5848
+ const MAX_DESCRIPTION_LENGTH = 1024;
5849
+ const IGNORE_FILE_NAMES = [
5850
+ ".gitignore",
5851
+ ".ignore",
5852
+ ".fdignore"
5853
+ ];
5854
+ const normalizeNewlines = (value) => value.replaceAll("\r\n", "\n").replaceAll("\r", "\n");
5855
+ const extractFrontmatter = (content) => {
5856
+ const normalized = normalizeNewlines(stripBom$1(content));
5857
+ if (!normalized.startsWith("---")) return void 0;
5858
+ const endIndex = normalized.indexOf("\n---", 3);
5859
+ if (endIndex === -1) return void 0;
5860
+ return normalized.slice(4, endIndex);
5861
+ };
5862
+ const parseFrontmatter = (raw) => {
5863
+ const yamlString = extractFrontmatter(raw);
5864
+ if (yamlString === void 0) return { frontmatter: {} };
5865
+ try {
5866
+ const parsed = parse$1(yamlString);
5867
+ return { frontmatter: typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? parsed : {} };
5868
+ } catch (error) {
5869
+ return {
5870
+ frontmatter: {},
5871
+ error
5872
+ };
5873
+ }
5874
+ };
5875
+ const validateName = (name) => {
5876
+ const errors = [];
5877
+ if (name.length > MAX_NAME_LENGTH) errors.push(`name exceeds ${MAX_NAME_LENGTH} characters (${name.length})`);
5878
+ if (!/^[a-z0-9-]+$/.test(name)) errors.push("name contains invalid characters (must be lowercase a-z, 0-9, hyphens only)");
5879
+ if (name.startsWith("-") || name.endsWith("-")) errors.push("name must not start or end with a hyphen");
5880
+ if (name.includes("--")) errors.push("name must not contain consecutive hyphens");
5881
+ return errors;
5882
+ };
5883
+ const validateDescription = (description) => {
5884
+ const errors = [];
5885
+ if (typeof description !== "string" || description.trim() === "") errors.push("description is required");
5886
+ else if (description.length > MAX_DESCRIPTION_LENGTH) errors.push(`description exceeds ${MAX_DESCRIPTION_LENGTH} characters (${description.length})`);
5887
+ return errors;
5888
+ };
5889
+ const prefixIgnorePattern = (line, prefix) => {
5890
+ const trimmed = line.trim();
5891
+ if (!trimmed) return void 0;
5892
+ if (trimmed.startsWith("#") && !trimmed.startsWith("\\#")) return void 0;
5893
+ let pattern = line;
5894
+ let negated = false;
5895
+ if (pattern.startsWith("!")) {
5896
+ negated = true;
5897
+ pattern = pattern.slice(1);
5898
+ } else if (pattern.startsWith("\\!")) pattern = pattern.slice(1);
5899
+ if (pattern.startsWith("/")) pattern = pattern.slice(1);
5900
+ const prefixed = prefix ? `${prefix}${pattern}` : pattern;
5901
+ return negated ? `!${prefixed}` : prefixed;
5902
+ };
5903
+ const addIgnoreRules = Effect.fnUntraced(function* (fs, ig, dir, root, entryNames) {
5904
+ const relativeDir = posix.relative(root, dir);
5905
+ const prefix = relativeDir && relativeDir !== "." ? `${relativeDir}/` : "";
5906
+ const presentIgnoreFiles = IGNORE_FILE_NAMES.filter((name) => entryNames.has(name));
5907
+ if (presentIgnoreFiles.length === 0) return;
5908
+ const contents = yield* Effect.forEach(presentIgnoreFiles, (filename) => fs.readFile(posix.join(dir, filename)).pipe(Effect.option), { concurrency: "unbounded" });
5909
+ for (const content of contents) {
5910
+ if (Option.isNone(content)) continue;
5911
+ const patterns = content.value.split(/\r?\n/).map((line) => prefixIgnorePattern(line, prefix)).filter((line) => line !== void 0);
5912
+ if (patterns.length > 0) ig.add(patterns);
5913
+ }
5914
+ });
5915
+ const loadSkillFromFile = Effect.fnUntraced(function* (fs, filePath) {
5916
+ const isDeclaredSkill = posix.basename(filePath) === "SKILL.md";
5917
+ const raw = yield* fs.readFile(filePath).pipe(Effect.tapError((error) => Effect.logWarning(`could not read skill file ${filePath}`, error.cause)), Effect.option);
5918
+ if (Option.isNone(raw)) return void 0;
5919
+ const { frontmatter, error } = parseFrontmatter(raw.value);
5920
+ if (error !== void 0) {
5921
+ if (isDeclaredSkill) yield* Effect.logWarning(`could not parse skill file ${filePath}`, error);
5922
+ return;
5923
+ }
5924
+ const description = frontmatter.description;
5925
+ const hasDescription = typeof description === "string" && description.trim() !== "";
5926
+ if (!isDeclaredSkill && !hasDescription) return void 0;
5927
+ const skillDir = posix.dirname(filePath);
5928
+ const parentDirName = posix.basename(skillDir);
5929
+ for (const error of validateDescription(description)) yield* Effect.logWarning(`skill ${filePath}: ${error}`);
5930
+ const name = (typeof frontmatter.name === "string" ? frontmatter.name : void 0) || parentDirName;
5931
+ for (const error of validateName(name)) yield* Effect.logWarning(`skill ${filePath}: ${error}`);
5932
+ if (!hasDescription) return void 0;
5933
+ return {
5934
+ name,
5935
+ description,
5936
+ filePath,
5937
+ baseDir: skillDir,
5938
+ disableModelInvocation: frontmatter["disable-model-invocation"] === true
5939
+ };
5940
+ });
5941
+ const scanDir = (fs, dir, mode, root, ig, visited) => Effect.gen(function* () {
5942
+ const realDir = yield* realpath(fs, dir);
5943
+ if (visited.has(realDir)) return [];
5944
+ const nextVisited = /* @__PURE__ */ new Set([...visited, realDir]);
5945
+ const listed = yield* fs.readdir(dir).pipe(Effect.tapError((error) => filesystem_exports.SandboxFileSystem.isNotFoundError(error.cause) ? Effect.void : Effect.logWarning(`could not read skill directory ${dir}`, error.cause)), Effect.option);
5946
+ if (Option.isNone(listed)) return [];
5947
+ const entries = [...listed.value].sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
5948
+ const entrySet = new Set(entries);
5949
+ yield* addIgnoreRules(fs, ig, dir, root, entrySet);
5950
+ if (entrySet.has("SKILL.md")) {
5951
+ const fullPath = posix.join(dir, "SKILL.md");
5952
+ const relPath = posix.relative(root, fullPath);
5953
+ if ((yield* isFile(fs, fullPath)) && !ig.ignores(relPath)) {
5954
+ const skill = yield* loadSkillFromFile(fs, fullPath);
5955
+ return skill === void 0 ? [] : [skill];
5956
+ }
5957
+ }
5958
+ const visible = entries.filter((name) => !name.startsWith(".") && name !== "node_modules");
5959
+ const statted = yield* Effect.forEach(visible, (name) => {
5960
+ const fullPath = posix.join(dir, name);
5961
+ return fs.stat(fullPath).pipe(Effect.map((stat) => ({
5962
+ name,
5963
+ fullPath,
5964
+ stat
5965
+ })), Effect.option);
5966
+ }, { concurrency: "unbounded" });
5967
+ const skills = [];
5968
+ for (const item of statted) {
5969
+ if (Option.isNone(item)) continue;
5970
+ const { name, fullPath, stat } = item.value;
5971
+ const relPath = posix.relative(root, fullPath);
5972
+ if (ig.ignores(stat.isDirectory ? `${relPath}/` : relPath)) continue;
5973
+ if (stat.isDirectory) {
5974
+ const nested = yield* scanDir(fs, fullPath, mode, root, ig, nextVisited);
5975
+ skills.push(...nested);
5976
+ continue;
5977
+ }
5978
+ if (stat.isFile && name.endsWith(".md") && (mode === "codework" && dir === root || mode === "agents" && dir !== root)) {
5979
+ const skill = yield* loadSkillFromFile(fs, fullPath);
5980
+ if (skill !== void 0) skills.push(skill);
5981
+ }
5982
+ }
5983
+ return skills;
5984
+ });
5985
+ /**
5986
+ * Load skills from a single directory in the sandbox.
5987
+ *
5988
+ * Discovery rules:
5989
+ * - If a directory contains `SKILL.md`, treat it as a skill root and do not recurse further.
5990
+ * - In `"codework"` mode, direct `.md` children in the root are also loaded; in `"agents"` mode,
5991
+ * `.md` files in subdirectories are loaded while root `.md` files are ignored.
5992
+ * - Recurse into subdirectories (skipping dotfiles, `node_modules`, and ignored paths).
5993
+ */
5994
+ const loadSkillsFromDir = (fs, dir, mode) => scanDir(fs, dir, mode, dir, ignore(), /* @__PURE__ */ new Set());
5995
+ /**
5996
+ * Discover skills inside the sandbox: `.codework/skills` at the cwd, then
5997
+ * `.agents/skills` from the cwd up to the git root (the closest ancestor holding `.git`), or up to
5998
+ * the sandbox root outside a repository. Earlier skills win name collisions.
5999
+ */
6000
+ const discover = Effect.fnUntraced(function* (fs, location) {
6001
+ const chain = ancestors(location.directory);
6002
+ const own = posix.join(location.directory, appConfigDir, "skills");
6003
+ const agents = chain.map((directory) => posix.join(directory, ".agents", "skills"));
6004
+ const [dirs, gits] = yield* Effect.all([present$1(fs, [own, ...agents], "directory", "skill directory"), present$1(fs, chain.map((directory) => posix.join(directory, ".git")), "entry", "git marker")], { concurrency: "unbounded" });
6005
+ const root = chain.findIndex((directory) => gits.has(posix.join(directory, ".git")));
6006
+ const walked = root === -1 ? agents : agents.slice(0, root + 1);
6007
+ const sources = [{
6008
+ dir: own,
6009
+ mode: "codework"
6010
+ }, ...walked.map((dir) => ({
6011
+ dir,
6012
+ mode: "agents"
6013
+ }))].filter(({ dir }) => dirs.has(dir));
6014
+ const found = yield* Effect.forEach(sources, ({ dir, mode }) => loadSkillsFromDir(fs, dir, mode), { concurrency: "unbounded" });
6015
+ const skillMap = /* @__PURE__ */ new Map();
6016
+ const realPathSet = /* @__PURE__ */ new Set();
6017
+ for (const skill of found.flat()) {
6018
+ const realPath = yield* realpath(fs, skill.filePath);
6019
+ if (realPathSet.has(realPath)) continue;
6020
+ const existing = skillMap.get(skill.name);
6021
+ if (existing !== void 0) yield* Effect.logWarning(`skill ${skill.filePath}: name "${skill.name}" collision with ${existing.filePath}`);
6022
+ else {
6023
+ skillMap.set(skill.name, skill);
6024
+ realPathSet.add(realPath);
6025
+ }
6026
+ }
6027
+ return Array.from(skillMap.values());
6028
+ });
6029
+ const escapeXml = (str) => str.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll("\"", "&quot;").replaceAll("'", "&apos;");
6030
+ /**
6031
+ * Format skills for inclusion in `Section.Skills` per the Agent Skills specification.
6032
+ * Skills with `disableModelInvocation: true` are excluded from the prompt.
6033
+ */
6034
+ const formatSkillsForPrompt = (skills, fileReadTool = "read") => {
6035
+ const visibleSkills = skills.filter((s) => !s.disableModelInvocation);
6036
+ if (visibleSkills.length === 0) return "";
6037
+ const lines = [
6038
+ "The following skills provide specialized instructions for specific tasks.",
6039
+ fileReadTool === "read" ? "Use the read tool to load a skill's file when the task matches its description." : fileReadTool === "bash" ? "Use bash to load a skill's file when the task matches its description." : "Load a skill's file when the task matches its description.",
6040
+ "When a skill file references a relative path, resolve it against the skill directory (parent of SKILL.md / dirname of the path) and use that absolute path in tool commands.",
6041
+ "",
6042
+ "<available_skills>"
6043
+ ];
6044
+ for (const skill of visibleSkills) {
6045
+ lines.push(" <skill>");
6046
+ lines.push(` <name>${escapeXml(skill.name)}</name>`);
6047
+ lines.push(` <description>${escapeXml(skill.description)}</description>`);
6048
+ lines.push(` <location>${escapeXml(skill.filePath)}</location>`);
6049
+ lines.push(" </skill>");
6050
+ }
6051
+ lines.push("</available_skills>");
6052
+ return lines.join("\n");
6053
+ };
6054
+ const readers = ["read", "bash"];
6055
+ const skillPlugin = (0, plugin_exports$1.define)({
6056
+ id: "codework.prompt.skill",
6057
+ kind: "prompt",
6058
+ setup: Effect.fn("SkillPlugin.setup")(function* (ctx) {
6059
+ const tools = ctx.plugin.tools;
6060
+ const visible = (name) => {
6061
+ const tool = tools.get(name);
6062
+ return tool !== void 0 && tool.promptSnippet !== void 0 && tool.promptSnippet.length > 0;
6063
+ };
6064
+ const fileReadTool = readers.find(visible) ?? (readers.some((name) => tools.has(name)) ? "indirect" : void 0);
6065
+ if (fileReadTool === void 0) return;
6066
+ const fs = yield* io_exports.SandboxIO.FileSystem;
6067
+ const skills = yield* discover(fs, ctx.location);
6068
+ const formatted = formatSkillsForPrompt(skills, fileReadTool);
6069
+ if (formatted.length === 0) return;
6070
+ ctx.plugin.prompt.sections.append(Section.Skills, formatted);
6071
+ })
6072
+ });
6073
+ //#endregion
5707
6074
  //#region src/tool/truncate.ts
5708
6075
  /**
5709
6076
  * Shared truncation utilities for tool outputs.
@@ -6002,7 +6369,7 @@ var Accumulator = class {
6002
6369
  //#endregion
6003
6370
  //#region src/tool/output.ts
6004
6371
  /** A new, unused output file path under `spillPath`. */
6005
- const path$1 = (spillPath, prefix) => crypto.randomBytes(8).pipe(Effect.map((bytes) => posix.join(spillPath, `${prefix}-${Hex.encode(bytes)}.log`)), Effect.orDie);
6372
+ const path = (spillPath, prefix) => crypto.randomBytes(8).pipe(Effect.map((bytes) => posix.join(spillPath, `${prefix}-${Hex.encode(bytes)}.log`)), Effect.orDie);
6006
6373
  /** First line printed when the output file cannot be created; the command then runs unsaved. */
6007
6374
  const unsaved = (file) => `codework: output not saved, cannot create ${file}`;
6008
6375
  const HEADER = "codework-output";
@@ -6034,7 +6401,7 @@ const script = (command, file, limits, pipeline, report) => [
6034
6401
  "run 2>&1"
6035
6402
  ].join("\n");
6036
6403
  const header = new RegExp(`^${HEADER} (\\d+) (\\d+) ([01])(?: (\\d+))?$`, "m");
6037
- const parse$3 = (match) => ({
6404
+ const parse$4 = (match) => ({
6038
6405
  bytes: Number(match[1]),
6039
6406
  newlines: Number(match[2]),
6040
6407
  kept: match[3] === "1"
@@ -6053,7 +6420,7 @@ const report = (stderr) => {
6053
6420
  rest: stderr
6054
6421
  };
6055
6422
  return {
6056
- report: parse$3(match),
6423
+ report: parse$4(match),
6057
6424
  rest: stderr.slice(0, match.index) + stderr.slice(match.index + match[0].length).replace(/^\n/, "")
6058
6425
  };
6059
6426
  };
@@ -6076,7 +6443,7 @@ const window = (stdout) => {
6076
6443
  const match = header.exec(newline === -1 ? stdout : stdout.slice(0, newline));
6077
6444
  if (match === null || match[4] === void 0) return void 0;
6078
6445
  return {
6079
- ...parse$3(match),
6446
+ ...parse$4(match),
6080
6447
  text: stdout.slice(newline + 1),
6081
6448
  textBytes: Number(match[4])
6082
6449
  };
@@ -6390,7 +6757,7 @@ const runStreaming = (shell, stream, params, file) => Effect.gen(function* () {
6390
6757
  });
6391
6758
  const bashHandler = (params) => Effect.gen(function* () {
6392
6759
  const shell = yield* ToolShell;
6393
- const file = yield* path$1((yield* io_exports.SandboxIO.Current).spillPath, "codework-bash");
6760
+ const file = yield* path((yield* io_exports.SandboxIO.Current).spillPath, "codework-bash");
6394
6761
  return yield* (shell.stream !== void 0 ? runStreaming(shell, shell.stream, params, file) : runBuffered(shell, params, file)).pipe(Effect.onInterrupt(() => cleanUp(shell, file, {
6395
6762
  output: "",
6396
6763
  truncated: false
@@ -7151,7 +7518,7 @@ const pipeline = (argv, limit, errors) => [
7151
7518
  ].join("; ");
7152
7519
  const STATUS = /^\{"type":"rc","code":(\d+)\}$/;
7153
7520
  /** The pipeline's output: match events, ripgrep's status, and anything else, which is its stderr. */
7154
- const parse$2 = (stdout, stderr) => {
7521
+ const parse$3 = (stdout, stderr) => {
7155
7522
  const matches = [];
7156
7523
  const errors = [];
7157
7524
  let status;
@@ -7220,7 +7587,7 @@ const grepHandler = (params) => Effect.gen(function* () {
7220
7587
  const isDirectory = stat.value.isDirectory;
7221
7588
  const context = params.context && params.context > 0 ? Math.trunc(params.context) : 0;
7222
7589
  const limit = Math.max(1, Math.trunc(params.limit ?? DEFAULT_LIMIT$1));
7223
- const errors = yield* path$1(sandbox.spillPath, "codework-grep");
7590
+ const errors = yield* path(sandbox.spillPath, "codework-grep");
7224
7591
  const run = yield* withCommand("rg", sandbox, shell, fs, (command) => Effect.gen(function* () {
7225
7592
  const argv = [
7226
7593
  command,
@@ -7235,7 +7602,7 @@ const grepHandler = (params) => Effect.gen(function* () {
7235
7602
  if (params.glob) argv.push("--glob", params.glob);
7236
7603
  argv.push("-e", params.pattern, searchPath);
7237
7604
  const result = yield* shell.exec(pipeline(argv, limit, errors));
7238
- const parsed = parse$2(result.stdout, result.stderr);
7605
+ const parsed = parse$3(result.stdout, result.stderr);
7239
7606
  return {
7240
7607
  ...parsed,
7241
7608
  exitCode: parsed.status ?? result.exitCode
@@ -7495,7 +7862,7 @@ const INLINE = /* @__PURE__ */ new Set([
7495
7862
  "image/gif",
7496
7863
  "image/webp"
7497
7864
  ]);
7498
- const toPng = (bytes) => load$4.pipe(Effect.map((photon) => {
7865
+ const toPng = (bytes) => load$5.pipe(Effect.map((photon) => {
7499
7866
  if (photon === void 0) return void 0;
7500
7867
  try {
7501
7868
  const image = decode(photon, bytes);
@@ -7800,127 +8167,10 @@ const builtins = Object.freeze([
7800
8167
  findPlugin,
7801
8168
  lsPlugin,
7802
8169
  defaultPromptPlugin,
7803
- instructionPlugin
8170
+ instructionPlugin,
8171
+ skillPlugin
7804
8172
  ]);
7805
8173
  //#endregion
7806
- //#region src/util/module.ts
7807
- /*!
7808
- * Adapted from OpenCode packages/util/src/runtime/import.node.ts.
7809
- *
7810
- * MIT License
7811
- *
7812
- * Copyright (c) 2025 opencode
7813
- *
7814
- * Permission is hereby granted, free of charge, to any person obtaining a copy
7815
- * of this software and associated documentation files (the "Software"), to deal
7816
- * in the Software without restriction, including without limitation the rights
7817
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7818
- * copies of the Software, and to permit persons to whom the Software is
7819
- * furnished to do so, subject to the following conditions:
7820
- *
7821
- * The above copyright notice and this permission notice shall be included in all
7822
- * copies or substantial portions of the Software.
7823
- *
7824
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
7825
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
7826
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
7827
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
7828
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
7829
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
7830
- * SOFTWARE.
7831
- */
7832
- /**
7833
- * Packages a plugin must share with the harness instead of using the copy installed beside it.
7834
- *
7835
- * A plugin is installed into the store as its own npm tree, so `effect` and the plugin SDK land
7836
- * there a second time. Two copies of the *same version* are still two module instances, and
7837
- * Effect Schema's checks do not survive the crossing: a schema built by one copy and evaluated by
7838
- * the other reports every check as failed, so a tool whose success type is `Schema.Finite` dies
7839
- * on "Expected a finite number" for `22800`. Nothing about that is visible at registration --
7840
- * `Schema.isSchema` passes, the JSON Schema is derived correctly, and the tool reaches the model
7841
- * -- so it surfaces on the first call rather than at boot.
7842
- *
7843
- * Resolution is the right place to fix it. A symlink into the store cannot be: a generation is
7844
- * immutable and shared by every project on the machine, while the harness doing the loading is
7845
- * whichever one is running now, and two applications embedding the harness need not agree on
7846
- * where their `effect` lives.
7847
- */
7848
- const sharedPackages = /* @__PURE__ */ new Set(["effect", "@codeworksh/plugin"]);
7849
- /** The package a bare specifier names: `effect/Schema` -> `effect`, `@a/b/c` -> `@a/b`. */
7850
- function packageOf(specifier) {
7851
- if (specifier.startsWith(".") || specifier.startsWith("/") || specifier.includes(":")) return "";
7852
- const segments = specifier.split("/");
7853
- return specifier.startsWith("@") ? segments.slice(0, 2).join("/") : segments[0] ?? "";
7854
- }
7855
- /**
7856
- * Resolve the shared packages as though this module had imported them, for as long as `run`
7857
- * takes. Anything else resolves exactly as it would have.
7858
- */
7859
- function withSharedModules(run) {
7860
- const hook = registerHooks({ resolve(specifier, context, nextResolve) {
7861
- return sharedPackages.has(packageOf(specifier)) ? nextResolve(specifier, {
7862
- ...context,
7863
- parentURL: import.meta.url
7864
- }) : nextResolve(specifier, context);
7865
- } });
7866
- return run().finally(() => hook.deregister());
7867
- }
7868
- /** Use Node's main loader even when the caller runs inside a bundler or test VM. */
7869
- async function importModule(specifier) {
7870
- const imported = await withSharedModules(() => new Script(`import(${JSON.stringify(specifier)})`, { importModuleDynamically: constants.USE_MAIN_CONTEXT_DEFAULT_LOADER }).runInThisContext());
7871
- if (typeof imported !== "object" || imported === null) return imported;
7872
- const module = imported;
7873
- const exports = module["module.exports"];
7874
- if (exports !== module.default || typeof exports !== "object" && typeof exports !== "function" || exports === null) return imported;
7875
- return Object.assign({}, module, exports);
7876
- }
7877
- /** Node resolution scoped synchronously to the caller. */
7878
- function resolveModule(specifier, directory, conditions) {
7879
- const hook = registerHooks({ resolve(target, context, nextResolve) {
7880
- return nextResolve(target, {
7881
- ...context,
7882
- parentURL: pathToFileURL(path.join(directory, "package.json")).href,
7883
- ...conditions === void 0 ? {} : { conditions: [.../* @__PURE__ */ new Set([...context.conditions, ...conditions])] }
7884
- });
7885
- } });
7886
- try {
7887
- const resolve = (target) => {
7888
- const resolved = import.meta.resolve(path.isAbsolute(target) ? pathToFileURL(target).href : target);
7889
- if (resolved.startsWith("file:")) statSync(new URL(resolved));
7890
- return resolved;
7891
- };
7892
- try {
7893
- return resolve(specifier);
7894
- } catch (error) {
7895
- if (path.extname(specifier) || !missing(error)) throw error;
7896
- for (const extension of [
7897
- ".ts",
7898
- ".tsx",
7899
- ".js",
7900
- ".jsx",
7901
- ".mts",
7902
- ".mjs",
7903
- ".cts",
7904
- ".cjs"
7905
- ]) try {
7906
- return resolve(specifier + extension);
7907
- } catch (cause) {
7908
- if (!missing(cause)) throw cause;
7909
- }
7910
- throw error;
7911
- }
7912
- } finally {
7913
- hook.deregister();
7914
- }
7915
- }
7916
- function missing(error) {
7917
- return error instanceof Error && "code" in error && [
7918
- "ENOENT",
7919
- "ENOTDIR",
7920
- "ERR_MODULE_NOT_FOUND"
7921
- ].includes(String(error.code));
7922
- }
7923
- //#endregion
7924
8174
  //#region src/plugin/error.ts
7925
8175
  /**
7926
8176
  * The reference as the person wrote it -- the string they will recognise in their settings file,
@@ -8358,7 +8608,7 @@ const isPath = (reference) => reference.startsWith("file:") || reference.startsW
8358
8608
  * - a **CLI argument** anchors to the process's startup directory, because that is where the
8359
8609
  * person typed it.
8360
8610
  */
8361
- const parse$1 = (reference, from) => Effect.gen(function* () {
8611
+ const parse$2 = (reference, from) => Effect.gen(function* () {
8362
8612
  const fail = (reason, message) => new SourceError({
8363
8613
  reason,
8364
8614
  reference,
@@ -9034,7 +9284,7 @@ const inspect = Effect.fn("PluginLoader.inspect")(function* (reference, options)
9034
9284
  reference,
9035
9285
  file: options.file
9036
9286
  };
9037
- const target = yield* parse$1(reference, options.hostDir);
9287
+ const target = yield* parse$2(reference, options.hostDir);
9038
9288
  const loaded = yield* load$2(target, origin, options);
9039
9289
  return {
9040
9290
  id: loaded.plugin.id,
@@ -9060,7 +9310,7 @@ const load$2 = Effect.fn("PluginLoader.load")(function* (target, origin, options
9060
9310
  * are one package, and `./plugins/x.ts` is the file it resolves to from the directory that
9061
9311
  * declared it. Pure -- nothing is installed, imported or read.
9062
9312
  */
9063
- const canonical = (reference, hostDir) => Effect.map(parse$1(reference, hostDir), canonical$1);
9313
+ const canonical = (reference, hostDir) => Effect.map(parse$2(reference, hostDir), canonical$1);
9064
9314
  //#endregion
9065
9315
  //#region src/plugin/catalog.ts
9066
9316
  /**
@@ -9122,7 +9372,7 @@ const load$1 = Effect.fn("PluginCatalog.load")(function* (references, options) {
9122
9372
  remember(plugin, []);
9123
9373
  continue;
9124
9374
  }
9125
- const target = yield* parse$1(reference, options.hostDir);
9375
+ const target = yield* parse$2(reference, options.hostDir);
9126
9376
  const key = target.kind === "local" ? target.path : target.spec;
9127
9377
  const already = seen.get(key);
9128
9378
  if (already !== void 0) {
@@ -9800,31 +10050,116 @@ const openWith = (options = {}) => Effect.fn("LLM.open")(function* (input, signa
9800
10050
  });
9801
10051
  });
9802
10052
  const open = openWith();
10053
+ const isProviderError = Schema.is(ProviderError);
10054
+ /** `error.type` for a request: the provider's failure reason, so a terminal and a thrown failure agree. */
10055
+ const requestErrorType = (input, exit) => {
10056
+ if (Exit.isFailure(exit)) {
10057
+ const error = Cause.squash(exit.cause);
10058
+ return isProviderError(error) ? error.reason._tag : errorType(exit.cause);
10059
+ }
10060
+ if (exit.value.outcome === "ended") return void 0;
10061
+ if (exit.value.reason === "aborted") return "aborted";
10062
+ return providerError(input, messageFailure(exit.value.message)).reason._tag;
10063
+ };
10064
+ const observe = Effect.fnUntraced(function* (input, timing, exit, drained) {
10065
+ const now = yield* Effect.clockWith((clock) => clock.currentTimeMillis);
10066
+ const labels = {
10067
+ provider: input.provider,
10068
+ model: input.model
10069
+ };
10070
+ const errorType = requestErrorType(input, exit);
10071
+ yield* record$1(llmDuration, (now - timing.start) / 1e3, errorType === void 0 ? labels : {
10072
+ ...labels,
10073
+ error: errorType
10074
+ });
10075
+ if (timing.firstToken !== void 0) {
10076
+ const ttft = (timing.firstToken - timing.start) / 1e3;
10077
+ yield* record$1(llmTtft, ttft, labels);
10078
+ yield* Effect.annotateCurrentSpan("gen_ai.response.time_to_first_chunk", ttft);
10079
+ }
10080
+ if (errorType !== void 0) yield* Effect.annotateCurrentSpan("error.type", errorType);
10081
+ const terminal = Exit.isSuccess(exit) ? exit.value : drained;
10082
+ if (terminal === void 0) return;
10083
+ const { message } = terminal;
10084
+ const { usage } = message;
10085
+ const inputTokens = usage.input + usage.cacheRead + usage.cacheWrite;
10086
+ const tokens = {
10087
+ input: inputTokens,
10088
+ output: usage.output,
10089
+ cache_read: usage.cacheRead,
10090
+ cache_write: usage.cacheWrite,
10091
+ reasoning: usage.reasoning ?? 0
10092
+ };
10093
+ for (const [type, count] of Object.entries(tokens)) if (count > 0) yield* record$1(llmTokens, count, {
10094
+ ...labels,
10095
+ type
10096
+ });
10097
+ if (usage.cost.total > 0) yield* record$1(llmCost, usage.cost.total, labels);
10098
+ yield* Effect.annotateCurrentSpan({
10099
+ "gen_ai.response.model": message.responseModel ?? message.model,
10100
+ ...message.responseId === void 0 ? {} : { "gen_ai.response.id": message.responseId },
10101
+ "gen_ai.response.finish_reasons": [terminal.reason],
10102
+ "gen_ai.usage.input_tokens": inputTokens,
10103
+ "gen_ai.usage.output_tokens": usage.output,
10104
+ "gen_ai.usage.cache_read.input_tokens": usage.cacheRead,
10105
+ "gen_ai.usage.cache_write.input_tokens": usage.cacheWrite,
10106
+ ...usage.reasoning === void 0 ? {} : { "gen_ai.usage.reasoning.output_tokens": usage.reasoning }
10107
+ });
10108
+ });
10109
+ const isToken = (event) => event.type === "text.delta" || event.type === "thinking.delta" || event.type === "toolcall.delta";
10110
+ /**
10111
+ * A failed terminal is the request's result, not an error. It leaves the
10112
+ * traced request as a failure so the span records an error status, and is
10113
+ * recovered just outside it.
10114
+ */
10115
+ var FailedTerminal = class extends Data.TaggedError("LLM.FailedTerminal") {};
9803
10116
  /**
9804
10117
  * Build a provider request from an opener. Tests use this with deterministic
9805
10118
  * aikit event streams; production uses `open` above.
9806
10119
  */
9807
10120
  const make$5 = (openStream) => {
9808
- const request = Effect.fn("LLM.run")(function* (input) {
10121
+ const request = Effect.fn("LLM.run", {
10122
+ kind: "client",
10123
+ level: "Info"
10124
+ })(function* (input) {
10125
+ yield* Effect.annotateCurrentSpan({
10126
+ "gen_ai.operation.name": "chat",
10127
+ "gen_ai.provider.name": input.provider,
10128
+ "gen_ai.request.model": input.model,
10129
+ "gen_ai.conversation.id": input.sessionId,
10130
+ "gen_ai.request.stream": true
10131
+ });
10132
+ const start = yield* Effect.clockWith((clock) => clock.currentTimeMillis);
10133
+ let firstToken;
10134
+ let drained;
10135
+ const markToken = (event) => firstToken !== void 0 || !isToken(event) ? Effect.void : Effect.clockWith((clock) => Effect.sync(() => {
10136
+ firstToken = clock.currentTimeMillisUnsafe();
10137
+ }));
9809
10138
  const signalScope = yield* Scope.make();
9810
10139
  yield* Effect.addFinalizer((exit) => Scope.close(signalScope, exit));
9811
10140
  const signal = yield* Effect.abortSignal.pipe(Scope.provide(signalScope));
9812
10141
  const abort = Scope.close(signalScope, Exit.void);
9813
- return yield* Effect.uninterruptibleMask((restore) => Effect.gen(function* () {
10142
+ const terminal = yield* Effect.uninterruptibleMask((restore) => Effect.gen(function* () {
9814
10143
  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 }));
10144
+ 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
10145
  const awaited = yield* restore(Fiber.await(consumer)).pipe(Effect.onInterrupt(() => abort), Effect.exit);
9817
10146
  if (Exit.isFailure(awaited)) {
9818
- const drained = yield* Fiber.await(consumer);
9819
- if (Exit.isFailure(drained)) return yield* Effect.failCause(drained.cause);
10147
+ const consumed = yield* Fiber.await(consumer);
10148
+ if (Exit.isFailure(consumed)) return yield* Effect.failCause(consumed.cause);
10149
+ drained = consumed.value;
9820
10150
  return yield* Effect.failCause(awaited.cause);
9821
10151
  }
9822
10152
  const completed = awaited.value;
9823
10153
  if (Exit.isFailure(completed)) return yield* Effect.failCause(completed.cause);
9824
10154
  return completed.value;
9825
- }));
9826
- });
9827
- return (input) => request(input).pipe(Effect.scoped);
10155
+ })).pipe(Effect.onExit((exit) => observe(input, {
10156
+ start,
10157
+ firstToken
10158
+ }, exit, drained)));
10159
+ if (terminal.outcome === "failed") return yield* new FailedTerminal({ terminal });
10160
+ return terminal;
10161
+ });
10162
+ return (input) => request(input).pipe(Effect.catchTag("LLM.FailedTerminal", (failed) => Effect.succeed(failed.terminal)), Effect.scoped);
9828
10163
  };
9829
10164
  const run$1 = make$5(open);
9830
10165
  //#endregion
@@ -10735,6 +11070,17 @@ const errorPart = (call, cause, now) => ({
10735
11070
  end: endTime(call, now)
10736
11071
  }
10737
11072
  });
11073
+ const observeTool = (snapshot, call, seconds, exit) => {
11074
+ const tool = snapshot.tools.defs.some((def) => def.name === call.name) ? call.name : "_OTHER";
11075
+ const labels = Exit.isFailure(exit) ? {
11076
+ tool,
11077
+ error: errorType(exit.cause)
11078
+ } : exit.value.status === "completed" ? { tool } : {
11079
+ tool,
11080
+ error: exit.value.status
11081
+ };
11082
+ return record$1(toolDuration, seconds, labels);
11083
+ };
10738
11084
  const layer$3 = (options = {}) => Layer.effect(Service$7, Effect.gen(function* () {
10739
11085
  const context = yield* Service$13;
10740
11086
  const events = yield* Service$16;
@@ -10773,7 +11119,7 @@ const layer$3 = (options = {}) => Layer.effect(Service$7, Effect.gen(function* (
10773
11119
  if (draft === void 0) return;
10774
11120
  yield* publishFailure(draft, { _tag: "interrupted" });
10775
11121
  });
10776
- const settleTools = Effect.fn("Loop.settleTools")(function* (snapshot, message, reason, markCommitted) {
11122
+ const settleTools = Effect.fn("Loop.settleTools", { level: "Info" })(function* (snapshot, message, reason, markCommitted) {
10777
11123
  const pending = message.parts.filter((part) => part.type === "toolCall" && part.status === "pending");
10778
11124
  let interruptedCause;
10779
11125
  const messageId = ID.from(message.messageId);
@@ -10812,7 +11158,12 @@ const layer$3 = (options = {}) => Layer.effect(Service$7, Effect.gen(function* (
10812
11158
  return;
10813
11159
  }
10814
11160
  const settled = /* @__PURE__ */ new Set();
10815
- const handle = Effect.fn("Loop.handleTool")(function* (call) {
11161
+ const handle = Effect.fn("Loop.handleTool", { level: "Info" })(function* (call) {
11162
+ yield* Effect.annotateCurrentSpan({
11163
+ "gen_ai.operation.name": "execute_tool",
11164
+ "gen_ai.tool.name": call.name,
11165
+ "gen_ai.tool.call.id": call.callID
11166
+ });
10816
11167
  yield* events.publish(ToolStarted, {
10817
11168
  timestamp: yield* DateTime.now,
10818
11169
  sessionId: snapshot.sessionId,
@@ -10822,7 +11173,7 @@ const layer$3 = (options = {}) => Layer.effect(Service$7, Effect.gen(function* (
10822
11173
  label: snapshot.tools.defs.find((def) => def.name === call.name)?.label,
10823
11174
  arguments: call.arguments
10824
11175
  });
10825
- const handled = yield* snapshot.tools.handle(call, {
11176
+ const execute = snapshot.tools.handle(call, {
10826
11177
  sessionId: snapshot.sessionId,
10827
11178
  messageId,
10828
11179
  onProgress: (progress) => events.publish(ToolProgress$2, {
@@ -10833,6 +11184,7 @@ const layer$3 = (options = {}) => Layer.effect(Service$7, Effect.gen(function* (
10833
11184
  partial: progress.partial
10834
11185
  }).pipe(Effect.asVoid)
10835
11186
  }).pipe(Effect.catchCauseIf((cause) => !Cause.hasInterrupts(cause), (cause) => Effect.map(Effect.clockWith((clock) => clock.currentTimeMillis), (now) => errorPart(call, cause, now))));
11187
+ const handled = yield* timed(execute, (seconds, exit) => observeTool(snapshot, call, seconds, exit));
10836
11188
  yield* Effect.uninterruptible(Effect.gen(function* () {
10837
11189
  yield* settle(call, handled);
10838
11190
  yield* Effect.sync(() => settled.add(call.callID));
@@ -10855,7 +11207,7 @@ const layer$3 = (options = {}) => Layer.effect(Service$7, Effect.gen(function* (
10855
11207
  }));
10856
11208
  return interruptedCause;
10857
11209
  });
10858
- const runTurnAttempt = Effect.fn("Loop.runTurnAttempt")(function* (promotion, snapshot) {
11210
+ const runTurnAttempt = Effect.fn("Loop.runTurnAttempt", { level: "Info" })(function* (promotion, snapshot) {
10859
11211
  const sessionId = snapshot.sessionId;
10860
11212
  if (promotion !== void 0) {
10861
11213
  const cutoff = yield* events.latestSequence(sessionId);
@@ -10959,27 +11311,35 @@ const layer$3 = (options = {}) => Layer.effect(Service$7, Effect.gen(function* (
10959
11311
  });
10960
11312
  }));
10961
11313
  });
10962
- const runTurn = Effect.fn("Loop.runTurn")(function* (promotion, snapshot) {
11314
+ const runTurn = Effect.fn("Loop.runTurn", { level: "Info" })(function* (promotion, snapshot) {
10963
11315
  return yield* runTurnAttempt(promotion, snapshot);
10964
11316
  });
10965
- const run = Effect.fn("Loop.run")(function* (input) {
11317
+ const run = Effect.fn("Loop.run", { level: "Info" })(function* (input) {
10966
11318
  yield* healDanglingDraft(input.sessionId);
10967
11319
  const hasSteer = yield* inputs.hasPending(input.sessionId, "steer");
10968
11320
  const hasFollowUp = hasSteer ? false : yield* inputs.hasPending(input.sessionId, "followUp");
10969
11321
  if (!input.force && !hasSteer && !hasFollowUp) return;
11322
+ yield* Effect.annotateCurrentSpan({
11323
+ "gen_ai.operation.name": "invoke_agent",
11324
+ "gen_ai.agent.name": "codework",
11325
+ "gen_ai.conversation.id": input.sessionId
11326
+ });
10970
11327
  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"));
11328
+ const drain = Effect.gen(function* () {
11329
+ let shouldRun = true;
11330
+ while (shouldRun) {
11331
+ const snapshot = yield* state.snapshot(input.sessionId);
11332
+ let needsContinuation = true;
11333
+ while (needsContinuation) {
11334
+ const result = yield* runTurn(promotion, snapshot);
11335
+ promotion = "steer";
11336
+ needsContinuation = result.needsContinuation || (yield* inputs.hasPending(input.sessionId, "steer"));
11337
+ }
11338
+ shouldRun = yield* inputs.hasPending(input.sessionId, "followUp");
11339
+ promotion = shouldRun ? "followUp" : void 0;
10979
11340
  }
10980
- shouldRun = yield* inputs.hasPending(input.sessionId, "followUp");
10981
- promotion = shouldRun ? "followUp" : void 0;
10982
- }
11341
+ });
11342
+ yield* timed(drain, (seconds, exit) => record$1(agentDuration, seconds, withError({}, exit)));
10983
11343
  });
10984
11344
  return Service$7.of({ run });
10985
11345
  }));
@@ -11214,7 +11574,7 @@ const layer = (options = {}) => Layer.unwrap(Effect.gen(function* () {
11214
11574
  * `hostDir` led to; only the process view falls back to `hostCwd`.
11215
11575
  */
11216
11576
  const hostDirOf = (root) => root ?? hostCwd;
11217
- const filed = (reference, from) => parse$1(reference, from).pipe(Effect.flatMap((target) => target.kind === "local" ? fileSystem.exists(target.path).pipe(Effect.map((there) => there ? Option.some({}) : Option.none())) : resolve$2(target, paths.cache, from).pipe(Effect.map((entry) => Option.fromUndefinedOr(entry)))), Effect.orElseSucceed(() => Option.none()));
11577
+ const filed = (reference, from) => parse$2(reference, from).pipe(Effect.flatMap((target) => target.kind === "local" ? fileSystem.exists(target.path).pipe(Effect.map((there) => there ? Option.some({}) : Option.none())) : resolve$2(target, paths.cache, from).pipe(Effect.map((entry) => Option.fromUndefinedOr(entry)))), Effect.orElseSucceed(() => Option.none()));
11218
11578
  const resolveOnly = {
11219
11579
  ...catalogOptions,
11220
11580
  install: required
@@ -11300,7 +11660,7 @@ const runtimeOverrides = (driver, value) => {
11300
11660
  }));
11301
11661
  return Effect.succeed(value);
11302
11662
  };
11303
- const create$2 = Effect.fn("Sandbox.create")(function* (input) {
11663
+ const create$2 = Effect.fn("Sandbox.create", { level: "Info" })(function* (input) {
11304
11664
  const controller = yield* Controller;
11305
11665
  const registry = yield* Registry;
11306
11666
  const driverName = name(input.driver);
@@ -11326,7 +11686,7 @@ const Selection = Schema.Union([Schema.Struct({ id: instance_exports.SandboxInst
11326
11686
  driver: Schema.String,
11327
11687
  providerResourceId: (0, schema_exports$9.optional)(Schema.String)
11328
11688
  })]);
11329
- const resolve = Effect.fn("Sandbox.resolve")(function* (input) {
11689
+ const resolve = Effect.fn("Sandbox.resolve", { level: "Info" })(function* (input) {
11330
11690
  if ("id" in input) {
11331
11691
  const found = yield* get$1(input.id);
11332
11692
  if (Option.isNone(found)) return yield* new errors_exports.SandboxNotFoundError({ id: input.id });
@@ -11379,7 +11739,7 @@ const list = Effect.fn("Sandbox.list")(function* (input) {
11379
11739
  const refresh = Effect.fn("Sandbox.refresh")(function* (id) {
11380
11740
  return yield* (yield* Controller).refresh(id);
11381
11741
  });
11382
- const wake = Effect.fn("Sandbox.wake")(function* (id) {
11742
+ const wake = Effect.fn("Sandbox.wake", { level: "Info" })(function* (id) {
11383
11743
  return yield* (yield* Controller).wake(id);
11384
11744
  });
11385
11745
  const stop = Effect.fn("Sandbox.stop")(function* (id) {
@@ -11602,12 +11962,14 @@ var plugin_exports = /* @__PURE__ */ __exportAll({
11602
11962
  definition: () => definition$1,
11603
11963
  identity: () => digest,
11604
11964
  inspect: () => inspect,
11965
+ lendResolve: () => lendResolve,
11605
11966
  localName: () => localName,
11606
- parse: () => parse$1,
11967
+ parse: () => parse$2,
11607
11968
  probe: () => probe,
11608
11969
  resolve: () => resolve$2,
11609
11970
  resolveCached: () => required,
11610
11971
  settingsLock: () => lock,
11972
+ share: () => share,
11611
11973
  update: () => update
11612
11974
  });
11613
11975
  //#endregion