@codeworksh/harness 0.3.0-dev.20261013131320 → 0.3.0-dev.20261019181702

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,7 +1,7 @@
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
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
+ import "./daytona-BqRoRhe7.mjs";
5
5
  import "./vercel-3GouQSJI.mjs";
6
6
  import { createRequire } from "node:module";
7
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";
@@ -21,11 +21,13 @@ import { ChildProcess, ChildProcessSpawner } from "effect/process";
21
21
  import { Buffer as Buffer$1 } from "node:buffer";
22
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
- import { generateModels } from "@codeworksh/aikit/modelgen";
24
+ import { downloadCatalog } 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
33
  import npa from "npm-package-arg";
@@ -134,10 +136,10 @@ var global_exports = /* @__PURE__ */ __exportAll({
134
136
  app: () => app,
135
137
  appConfigDir: () => appConfigDir,
136
138
  authFile: () => authFile,
137
- defaultLayer: () => defaultLayer$2,
139
+ defaultLayer: () => defaultLayer$4,
138
140
  homeConfig: () => homeConfig,
139
141
  layer: () => layer$27,
140
- layerWith: () => layerWith,
142
+ layerWith: () => layerWith$1,
141
143
  make: () => make$18,
142
144
  resolve: () => resolve$4
143
145
  });
@@ -184,8 +186,8 @@ const build = (input) => Effect.gen(function* () {
184
186
  return Service$17.of(paths);
185
187
  });
186
188
  const layer$27 = Layer.effect(Service$17, build({}));
187
- const defaultLayer$2 = layer$27;
188
- const layerWith = (input) => Layer.effect(Service$17, build(input));
189
+ const defaultLayer$4 = layer$27;
190
+ const layerWith$1 = (input) => Layer.effect(Service$17, build(input));
189
191
  //#endregion
190
192
  //#region src/db/migrations.ts
191
193
  const migrations = {
@@ -374,7 +376,7 @@ var db_exports = /* @__PURE__ */ __exportAll({
374
376
  Database: () => db_exports,
375
377
  SqlClient: () => SqlClient$1,
376
378
  SqlSchema: () => SqlSchema$1,
377
- defaultLayer: () => defaultLayer$1,
379
+ defaultLayer: () => defaultLayer$3,
378
380
  layer: () => layer$26,
379
381
  locationConfig: () => locationConfig,
380
382
  path: () => path$1,
@@ -408,7 +410,7 @@ const path$1 = Effect.fn("Database.path")(function* (data) {
408
410
  const directory = data ?? (yield* resolve$4()).data;
409
411
  return resolveDatabaseLocation(yield* locationConfig, directory);
410
412
  });
411
- const defaultLayer$1 = Layer.unwrap(path$1().pipe(Effect.map(layer$26)));
413
+ const defaultLayer$3 = Layer.unwrap(path$1().pipe(Effect.map(layer$26)));
412
414
  //#endregion
413
415
  //#region src/event/schema.ts
414
416
  var schema_exports$7 = /* @__PURE__ */ __exportAll({});
@@ -1579,7 +1581,7 @@ var session_exports$1 = /* @__PURE__ */ __exportAll({
1579
1581
  SessionNotLinkedError: () => SessionNotLinkedError,
1580
1582
  SessionRow: () => SessionRow,
1581
1583
  ToolCallNotFoundError: () => ToolCallNotFoundError,
1582
- defaultLayer: () => defaultLayer,
1584
+ defaultLayer: () => defaultLayer$2,
1583
1585
  entryStates: () => entryStates,
1584
1586
  entryTypes: () => entryTypes,
1585
1587
  layer: () => layer$23,
@@ -1663,6 +1665,16 @@ const layer$23 = Layer.effect(Service$14, Effect.gen(function* () {
1663
1665
  SELECT p.* FROM space p JOIN session s ON s.space_id = p.id WHERE s.id = ${sessionId}
1664
1666
  `
1665
1667
  });
1668
+ const findProject = SqlSchema.findOneOption({
1669
+ Request: Schema.String,
1670
+ Result: ProjectRow,
1671
+ execute: (sessionId) => sql`
1672
+ SELECT p.* FROM project p
1673
+ JOIN space sp ON sp.project_id = p.id
1674
+ JOIN session s ON s.space_id = sp.id
1675
+ WHERE s.id = ${sessionId}
1676
+ `
1677
+ });
1666
1678
  const findSpaceById = SqlSchema.findOneOption({
1667
1679
  Request: Schema.String,
1668
1680
  Result: SpaceRow,
@@ -1811,6 +1823,13 @@ const layer$23 = Layer.effect(Service$14, Effect.gen(function* () {
1811
1823
  const space = Effect.fn("Session.space")(function* (sessionId) {
1812
1824
  return Option.map(yield* findSpace(sessionId).pipe(Effect.orDie), fromRow$1);
1813
1825
  });
1826
+ const project = Effect.fn("Session.project")(function* (sessionId) {
1827
+ return Option.map(yield* findProject(sessionId).pipe(Effect.orDie), (row) => new schema_exports$6.ProjectSchema.Info({
1828
+ id: schema_exports$6.ProjectSchema.ID.make(row.id),
1829
+ name: row.name,
1830
+ status: row.status
1831
+ }));
1832
+ });
1814
1833
  const list = Effect.fn("Session.list")(function* (input) {
1815
1834
  return yield* (input === void 0 ? selectAll() : "spaceId" in input ? selectBySpace(input.spaceId) : selectByProject(input.projectId)).pipe(Effect.orDie);
1816
1835
  });
@@ -2313,6 +2332,7 @@ const layer$23 = Layer.effect(Service$14, Effect.gen(function* () {
2313
2332
  configure,
2314
2333
  get,
2315
2334
  space,
2335
+ project,
2316
2336
  list,
2317
2337
  entry,
2318
2338
  path,
@@ -2329,7 +2349,7 @@ const layer$23 = Layer.effect(Service$14, Effect.gen(function* () {
2329
2349
  setLabel
2330
2350
  });
2331
2351
  }));
2332
- const defaultLayer = layer$23.pipe(Layer.provide(defaultLayer$1));
2352
+ const defaultLayer$2 = layer$23.pipe(Layer.provide(defaultLayer$3));
2333
2353
  //#endregion
2334
2354
  //#region src/context/errors.ts
2335
2355
  var ContextDecodeError = class extends Schema.TaggedError()("ContextDecodeError", {
@@ -4274,6 +4294,17 @@ const make$9 = () => {
4274
4294
  }) };
4275
4295
  };
4276
4296
  //#endregion
4297
+ //#region src/sandbox/sandbox.ts
4298
+ /**
4299
+ * Assemble a local backend into a sandbox with a **real host shell** — the
4300
+ * right choice whenever commands must invoke actual binaries (`git`, package
4301
+ * managers). For a sandbox whose shell should stay inside the VFS, use
4302
+ * `EnvBash.services` instead, which wires just-bash over the same tree.
4303
+ */
4304
+ const services = (backend, identity) => Layer.provideMerge(io_exports.SandboxIO.mount(identity), Layer.provideMerge(Layer.merge(layer$21, layer$18()), backend));
4305
+ /** Default sandbox: the real OS filesystem and processes, mounted at `cwd`. */
4306
+ const defaultLayer$1 = (cwd) => services(layer$20(), io_exports.SandboxIO.host(cwd));
4307
+ //#endregion
4277
4308
  //#region src/git/git.ts
4278
4309
  var AppProcessError = class extends Schema.TaggedError()("AppProcessError", {
4279
4310
  command: Schema.String,
@@ -4464,7 +4495,8 @@ const up = Effect.fn("SandboxFileSystem.up")(function* (fs, options) {
4464
4495
  const isDirectory$1 = Effect.fn("SandboxFileSystem.isDirectory")(function* (fs, path) {
4465
4496
  return yield* fs.stat(path).pipe(Effect.map((stat) => stat.isDirectory), Effect.orElseSucceed(() => false));
4466
4497
  });
4467
- Effect.fn("SandboxFileSystem.isFile")(function* (fs, path) {
4498
+ /** True when `path` exists and is a regular file; false when it is absent or unreadable. */
4499
+ const isFile = Effect.fn("SandboxFileSystem.isFile")(function* (fs, path) {
4468
4500
  return yield* fs.stat(path).pipe(Effect.map((stat) => stat.isFile), Effect.orElseSucceed(() => false));
4469
4501
  });
4470
4502
  /** Read a file, or `undefined` when it is missing or unreadable. */
@@ -4611,6 +4643,16 @@ const layer$13 = Layer.effect(Service$10, Effect.gen(function* () {
4611
4643
  }));
4612
4644
  //#endregion
4613
4645
  //#region src/project/project.ts
4646
+ var project_exports = /* @__PURE__ */ __exportAll({
4647
+ Project: () => project_exports,
4648
+ ProjectNotFoundError: () => ProjectNotFoundError,
4649
+ Service: () => Service$9,
4650
+ defaultLayer: () => defaultLayer,
4651
+ layer: () => layer$12,
4652
+ layerWith: () => layerWith,
4653
+ provisional: () => provisional,
4654
+ rename: () => rename
4655
+ });
4614
4656
  /** Deterministic, env-local id for a directory without a portable identity. Never written to a marker. */
4615
4657
  const provisional = (env, location) => schema_exports$6.ProjectSchema.ID.make(fast(JSON.stringify([
4616
4658
  "dir",
@@ -4664,7 +4706,7 @@ const layer$12 = Layer.effect(Service$9, Effect.gen(function* () {
4664
4706
  main: repo.gitDir === repo.store
4665
4707
  }]).toSorted((a, b) => Number(b.main) - Number(a.main));
4666
4708
  });
4667
- const resolveOrCreate = Effect.fn("Project.resolveOrCreate")(function* (cwd) {
4709
+ const resolveOrCreate = Effect.fn("Project.resolveOrCreate")(function* (cwd, options) {
4668
4710
  if (!(yield* fs.exists(cwd).pipe(Effect.orDie))) return yield* new DirectoryNotFoundError$1({
4669
4711
  directory: cwd,
4670
4712
  sandboxInstanceId: env
@@ -4678,7 +4720,7 @@ const layer$12 = Layer.effect(Service$9, Effect.gen(function* () {
4678
4720
  const ident = repo === void 0 ? void 0 : yield* repos.identity(repo);
4679
4721
  const worktree = repo?.directory ?? location;
4680
4722
  const projectId = ident?.id === void 0 ? provisional(env, worktree) : schema_exports$6.ProjectSchema.ID.make(ident.id);
4681
- const name = ident?.name ?? posix.basename(worktree);
4723
+ const name = options?.name ?? ident?.name ?? posix.basename(worktree);
4682
4724
  const vcs = repo === void 0 ? void 0 : {
4683
4725
  type: "git",
4684
4726
  store: repo.store
@@ -4750,6 +4792,32 @@ const layer$12 = Layer.effect(Service$9, Effect.gen(function* () {
4750
4792
  list
4751
4793
  });
4752
4794
  }));
4795
+ var ProjectNotFoundError = class extends Schema.TaggedError()("ProjectNotFoundError", { projectId: Schema.String }) {};
4796
+ /** Renames a project. Needs only the database, not a mount: a name is the same in every env. */
4797
+ const rename = Effect.fn("Project.rename")(function* (input) {
4798
+ const sql = yield* SqlClient.SqlClient;
4799
+ const now = yield* Effect.clockWith((clock) => clock.currentTimeMillis);
4800
+ const row = yield* SqlSchema.findOneOption({
4801
+ Request: Schema.Struct({
4802
+ id: Schema.String,
4803
+ name: Schema.String
4804
+ }),
4805
+ Result: ProjectRow,
4806
+ execute: ({ id, name }) => sql`UPDATE project SET name = ${name}, updated_at = ${now} WHERE id = ${id} RETURNING *`
4807
+ })({
4808
+ id: input.projectId,
4809
+ name: input.name
4810
+ }).pipe(Effect.orDie);
4811
+ if (Option.isNone(row)) return yield* new ProjectNotFoundError({ projectId: input.projectId });
4812
+ return new schema_exports$6.ProjectSchema.Info({
4813
+ id: schema_exports$6.ProjectSchema.ID.make(row.value.id),
4814
+ name: row.value.name,
4815
+ status: row.value.status
4816
+ });
4817
+ });
4818
+ /** Takes an assembled sandbox — local or remote — not a local backend. */
4819
+ const layerWith = (sandbox) => layer$12.pipe(Layer.provide(Layer.mergeAll(layer$14, layer$13, layer$24)), Layer.provide(layer$15), Layer.provide(sandbox), Layer.provide(defaultLayer$3));
4820
+ const defaultLayer = (path) => layerWith(defaultLayer$1(path));
4753
4821
  //#endregion
4754
4822
  //#region src/location/location.ts
4755
4823
  var location_exports = /* @__PURE__ */ __exportAll({
@@ -4761,10 +4829,10 @@ var location_exports = /* @__PURE__ */ __exportAll({
4761
4829
  layer: () => layer$11,
4762
4830
  layerMounted: () => layerMounted
4763
4831
  });
4764
- const layer$11 = () => Layer.effect(Service, Effect.gen(function* () {
4832
+ const layer$11 = (options) => Layer.effect(Service, Effect.gen(function* () {
4765
4833
  const instance = yield* io_exports.SandboxIO.Current;
4766
4834
  const directory = schema_exports$9.AbsolutePath.make(instance.cwd);
4767
- const resolved = yield* (yield* Service$9).resolveOrCreate(directory);
4835
+ const resolved = yield* (yield* Service$9).resolveOrCreate(directory, options);
4768
4836
  return Service.of(new Info({
4769
4837
  directory: resolved.directory,
4770
4838
  space: resolved.space,
@@ -4776,7 +4844,7 @@ const layer$11 = () => Layer.effect(Service, Effect.gen(function* () {
4776
4844
  * folded in. What remains is exactly the mount and the database — for callers
4777
4845
  * that already hold both, such as the runner and `Session.create`.
4778
4846
  */
4779
- const layerMounted = () => layer$11().pipe(Layer.provide(layer$12.pipe(Layer.provide(Layer.mergeAll(layer$14, layer$13, layer$24)), Layer.provide(layer$15))));
4847
+ const layerMounted = (options) => layer$11(options).pipe(Layer.provide(layer$12.pipe(Layer.provide(Layer.mergeAll(layer$14, layer$13, layer$24)), Layer.provide(layer$15))));
4780
4848
  //#endregion
4781
4849
  //#region src/runner/run.ts
4782
4850
  var run_exports = /* @__PURE__ */ __exportAll({
@@ -4871,7 +4939,6 @@ var ProviderError = class extends Schema.TaggedError()("Runner.ProviderError", {
4871
4939
  var ModelCatalogError = class extends Schema.TaggedError()("Runner.ModelCatalogError", {
4872
4940
  path: Schema.String,
4873
4941
  reason: Schema.Literals([
4874
- "missing",
4875
4942
  "unreadable",
4876
4943
  "empty",
4877
4944
  "invalid"
@@ -5312,7 +5379,7 @@ var settings_exports = /* @__PURE__ */ __exportAll({
5312
5379
  layer: () => layer$8,
5313
5380
  load: () => load$3,
5314
5381
  modules: () => modules,
5315
- parse: () => parse$4,
5382
+ parse: () => parse$5,
5316
5383
  paths: () => paths,
5317
5384
  projectRoot: () => projectRoot,
5318
5385
  userConfigDir: () => userConfigDir
@@ -5401,7 +5468,7 @@ const decodeFormatter = SchemaIssue.makeFormatterStandardSchemaV1({ leafHook: (i
5401
5468
  const message = "annotations" in issue ? issue.annotations?.message : void 0;
5402
5469
  return typeof message === "string" ? message.replaceAll(/\s*\n\s*/g, " ") : "invalid value";
5403
5470
  } });
5404
- const parse$4 = Effect.fn("Settings.parse")(function* (path, source) {
5471
+ const parse$5 = Effect.fn("Settings.parse")(function* (path, source) {
5405
5472
  const errors = [];
5406
5473
  const json = parse(source, errors, {
5407
5474
  allowTrailingComma: true,
@@ -5449,7 +5516,7 @@ const attempt = (path) => fileSystem.readFileString(path).pipe(Effect.mapError((
5449
5516
  path,
5450
5517
  reason: "read",
5451
5518
  detail: error.reason._tag
5452
- })), Effect.flatMap((source) => parse$4(path, source)));
5519
+ })), Effect.flatMap((source) => parse$5(path, source)));
5453
5520
  /**
5454
5521
  * One read of every layer. Exported for the harness constructor, which needs the plugin
5455
5522
  * selection before any layer is built; everything else goes through `Service`.
@@ -5563,8 +5630,13 @@ const stale = Effect.fn("ModelCatalog.stale")(function* (path) {
5563
5630
  const ttl = yield* ttlConfig;
5564
5631
  return (yield* Clock.currentTimeMillis) - mtime.value >= Duration.toMillis(ttl);
5565
5632
  });
5633
+ /** OpenRouter's catalog is narrowed to what the host's key may use, when one is set. */
5634
+ const openRouterKey = () => getEnvApiKey({ env: ["OPENROUTER_API_KEY"] });
5566
5635
  const download$1 = (path) => Effect.tryPromise({
5567
- try: () => generateModels({ path }),
5636
+ try: () => downloadCatalog({
5637
+ path,
5638
+ openRouterKey: openRouterKey()
5639
+ }),
5568
5640
  catch: (cause) => new RefreshError({
5569
5641
  path,
5570
5642
  detail: cause instanceof Error ? cause.message : String(cause)
@@ -5585,8 +5657,7 @@ const refresh$1 = Effect.fn("ModelCatalog.refresh")(function* (home, options = {
5585
5657
  });
5586
5658
  /**
5587
5659
  * The automatic check: refresh a stale catalog unless it is pinned, and never fail. A failed
5588
- * download leaves the old file in place; a missing one surfaces later as a
5589
- * `Runner.ModelCatalogError` from whatever needed it.
5660
+ * download leaves the old file in place; without one, aikit's bundled baseline is read.
5590
5661
  */
5591
5662
  const check$1 = Effect.fn("ModelCatalog.check")(function* ({ path, pinned }) {
5592
5663
  if (pinned || !(yield* stale(path))) return;
@@ -5622,7 +5693,7 @@ const loadError = (cause) => {
5622
5693
  if (typeof cause !== "object" || cause === null || !("name" in cause) || cause.name !== "ModelCatalogLoadError") return;
5623
5694
  if (!("data" in cause) || typeof cause.data !== "object" || cause.data === null) return void 0;
5624
5695
  const data = cause.data;
5625
- if (!("path" in data) || typeof data.path !== "string" || !("message" in data) || typeof data.message !== "string" || !("reason" in data) || data.reason !== "missing" && data.reason !== "unreadable" && data.reason !== "empty" && data.reason !== "invalid") return;
5696
+ if (!("path" in data) || typeof data.path !== "string" || !("message" in data) || typeof data.message !== "string" || !("reason" in data) || data.reason !== "unreadable" && data.reason !== "empty" && data.reason !== "invalid") return;
5626
5697
  return new ModelCatalogError({
5627
5698
  path: data.path,
5628
5699
  reason: data.reason,
@@ -5755,6 +5826,22 @@ const defaultPromptPlugin = (0, plugin_exports$1.define)({
5755
5826
  })
5756
5827
  });
5757
5828
  //#endregion
5829
+ //#region src/plugin/builtin/prompt/walk.ts
5830
+ const stripBom$1 = (raw) => raw.charCodeAt(0) === 65279 ? raw.slice(1) : raw;
5831
+ /** `directory` and every ancestor up to the sandbox root, closest first. */
5832
+ const ancestors = (directory) => {
5833
+ const chain = [];
5834
+ for (let current = directory;; current = posix.dirname(current)) {
5835
+ chain.push(current);
5836
+ if (posix.dirname(current) === current) return chain;
5837
+ }
5838
+ };
5839
+ /**
5840
+ * Which of `paths` exist as the requested kind (`entry` takes either). Absence stays silent; any other failure
5841
+ * (permissions, a remote blip) is logged so a dropped entry is not a mystery, then counts as absent.
5842
+ */
5843
+ 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]))));
5844
+ //#endregion
5758
5845
  //#region src/plugin/builtin/prompt/instruction.ts
5759
5846
  /** First readable match per directory wins. */
5760
5847
  const names = [
@@ -5766,22 +5853,9 @@ const names = [
5766
5853
  "CLAUDE.MD"
5767
5854
  ];
5768
5855
  const content = (raw) => {
5769
- const text = raw.charCodeAt(0) === 65279 ? raw.slice(1) : raw;
5856
+ const text = stripBom$1(raw);
5770
5857
  return text.trim().length === 0 ? void 0 : text;
5771
5858
  };
5772
- /** `directory` and every ancestor up to the sandbox root, closest first. */
5773
- const ancestors = (directory) => {
5774
- const chain = [];
5775
- for (let current = directory;; current = posix.dirname(current)) {
5776
- chain.push(current);
5777
- if (posix.dirname(current) === current) return chain;
5778
- }
5779
- };
5780
- /**
5781
- * Which of `paths` are files. Absence is the expected answer and stays silent; any other failure
5782
- * (permissions, a remote blip) is logged so a dropped file is not a mystery, then counts as absent.
5783
- */
5784
- 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]))));
5785
5859
  /** A file that stats but does not read falls through to the next candidate. */
5786
5860
  const first = Effect.fnUntraced(function* (fs, directory, files) {
5787
5861
  for (const name of names) {
@@ -5809,9 +5883,9 @@ const shadowing = (location) => {
5809
5883
  return location.space.location.startsWith(`${main}/`) ? main : void 0;
5810
5884
  };
5811
5885
  /** One file per directory from the sandbox root down to the cwd, closest last so it has the final word. */
5812
- const discover = Effect.fnUntraced(function* (fs, location) {
5886
+ const discover$1 = Effect.fnUntraced(function* (fs, location) {
5813
5887
  const chain = ancestors(location.directory);
5814
- const files = yield* present$1(fs, chain.flatMap((directory) => names.map((name) => posix.join(directory, name))));
5888
+ const files = yield* present$1(fs, chain.flatMap((directory) => names.map((name) => posix.join(directory, name))), "file", "instruction file");
5815
5889
  const found = yield* Effect.forEach(chain, (directory) => first(fs, directory, files), { concurrency: "unbounded" });
5816
5890
  const main = shadowing(location);
5817
5891
  const worktree = main === void 0 ? void 0 : found[chain.indexOf(location.space.location)];
@@ -5827,7 +5901,7 @@ const instructionPlugin = (0, plugin_exports$1.define)({
5827
5901
  kind: "prompt",
5828
5902
  setup: Effect.fn("InstructionPlugin.setup")(function* (ctx) {
5829
5903
  const fs = yield* io_exports.SandboxIO.FileSystem;
5830
- const files = (yield* discover(fs, ctx.location)).filter((file) => file.content !== void 0);
5904
+ const files = (yield* discover$1(fs, ctx.location)).filter((file) => file.content !== void 0);
5831
5905
  if (files.length === 0) return;
5832
5906
  const prompt = ctx.plugin.prompt;
5833
5907
  prompt.sections.append(Section.ProjectContext, "Project-Specific Instructions & Guidelines:");
@@ -5835,6 +5909,236 @@ const instructionPlugin = (0, plugin_exports$1.define)({
5835
5909
  })
5836
5910
  });
5837
5911
  //#endregion
5912
+ //#region src/plugin/builtin/prompt/skill.ts
5913
+ /** Max name length per Agent Skills spec. */
5914
+ const MAX_NAME_LENGTH = 64;
5915
+ /** Max description length per Agent Skills spec. */
5916
+ const MAX_DESCRIPTION_LENGTH = 1024;
5917
+ const IGNORE_FILE_NAMES = [
5918
+ ".gitignore",
5919
+ ".ignore",
5920
+ ".fdignore"
5921
+ ];
5922
+ const normalizeNewlines = (value) => value.replaceAll("\r\n", "\n").replaceAll("\r", "\n");
5923
+ const extractFrontmatter = (content) => {
5924
+ const normalized = normalizeNewlines(stripBom$1(content));
5925
+ if (!normalized.startsWith("---")) return void 0;
5926
+ const endIndex = normalized.indexOf("\n---", 3);
5927
+ if (endIndex === -1) return void 0;
5928
+ return normalized.slice(4, endIndex);
5929
+ };
5930
+ const parseFrontmatter = (raw) => {
5931
+ const yamlString = extractFrontmatter(raw);
5932
+ if (yamlString === void 0) return { frontmatter: {} };
5933
+ try {
5934
+ const parsed = parse$1(yamlString);
5935
+ return { frontmatter: typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? parsed : {} };
5936
+ } catch (error) {
5937
+ return {
5938
+ frontmatter: {},
5939
+ error
5940
+ };
5941
+ }
5942
+ };
5943
+ const validateName = (name) => {
5944
+ const errors = [];
5945
+ if (name.length > MAX_NAME_LENGTH) errors.push(`name exceeds ${MAX_NAME_LENGTH} characters (${name.length})`);
5946
+ if (!/^[a-z0-9-]+$/.test(name)) errors.push("name contains invalid characters (must be lowercase a-z, 0-9, hyphens only)");
5947
+ if (name.startsWith("-") || name.endsWith("-")) errors.push("name must not start or end with a hyphen");
5948
+ if (name.includes("--")) errors.push("name must not contain consecutive hyphens");
5949
+ return errors;
5950
+ };
5951
+ const validateDescription = (description) => {
5952
+ const errors = [];
5953
+ if (typeof description !== "string" || description.trim() === "") errors.push("description is required");
5954
+ else if (description.length > MAX_DESCRIPTION_LENGTH) errors.push(`description exceeds ${MAX_DESCRIPTION_LENGTH} characters (${description.length})`);
5955
+ return errors;
5956
+ };
5957
+ const prefixIgnorePattern = (line, prefix) => {
5958
+ const trimmed = line.trim();
5959
+ if (!trimmed) return void 0;
5960
+ if (trimmed.startsWith("#") && !trimmed.startsWith("\\#")) return void 0;
5961
+ let pattern = line;
5962
+ let negated = false;
5963
+ if (pattern.startsWith("!")) {
5964
+ negated = true;
5965
+ pattern = pattern.slice(1);
5966
+ } else if (pattern.startsWith("\\!")) pattern = pattern.slice(1);
5967
+ if (pattern.startsWith("/")) pattern = pattern.slice(1);
5968
+ const prefixed = prefix ? `${prefix}${pattern}` : pattern;
5969
+ return negated ? `!${prefixed}` : prefixed;
5970
+ };
5971
+ const addIgnoreRules = Effect.fnUntraced(function* (fs, ig, dir, root, entryNames) {
5972
+ const relativeDir = posix.relative(root, dir);
5973
+ const prefix = relativeDir && relativeDir !== "." ? `${relativeDir}/` : "";
5974
+ const presentIgnoreFiles = IGNORE_FILE_NAMES.filter((name) => entryNames.has(name));
5975
+ if (presentIgnoreFiles.length === 0) return;
5976
+ const contents = yield* Effect.forEach(presentIgnoreFiles, (filename) => fs.readFile(posix.join(dir, filename)).pipe(Effect.option), { concurrency: "unbounded" });
5977
+ for (const content of contents) {
5978
+ if (Option.isNone(content)) continue;
5979
+ const patterns = content.value.split(/\r?\n/).map((line) => prefixIgnorePattern(line, prefix)).filter((line) => line !== void 0);
5980
+ if (patterns.length > 0) ig.add(patterns);
5981
+ }
5982
+ });
5983
+ const loadSkillFromFile = Effect.fnUntraced(function* (fs, filePath) {
5984
+ const isDeclaredSkill = posix.basename(filePath) === "SKILL.md";
5985
+ const raw = yield* fs.readFile(filePath).pipe(Effect.tapError((error) => Effect.logWarning(`could not read skill file ${filePath}`, error.cause)), Effect.option);
5986
+ if (Option.isNone(raw)) return void 0;
5987
+ const { frontmatter, error } = parseFrontmatter(raw.value);
5988
+ if (error !== void 0) {
5989
+ if (isDeclaredSkill) yield* Effect.logWarning(`could not parse skill file ${filePath}`, error);
5990
+ return;
5991
+ }
5992
+ const description = frontmatter.description;
5993
+ const hasDescription = typeof description === "string" && description.trim() !== "";
5994
+ if (!isDeclaredSkill && !hasDescription) return void 0;
5995
+ const skillDir = posix.dirname(filePath);
5996
+ const parentDirName = posix.basename(skillDir);
5997
+ for (const error of validateDescription(description)) yield* Effect.logWarning(`skill ${filePath}: ${error}`);
5998
+ const name = (typeof frontmatter.name === "string" ? frontmatter.name : void 0) || parentDirName;
5999
+ for (const error of validateName(name)) yield* Effect.logWarning(`skill ${filePath}: ${error}`);
6000
+ if (!hasDescription) return void 0;
6001
+ return {
6002
+ name,
6003
+ description,
6004
+ filePath,
6005
+ baseDir: skillDir,
6006
+ disableModelInvocation: frontmatter["disable-model-invocation"] === true
6007
+ };
6008
+ });
6009
+ const scanDir = (fs, dir, mode, root, ig, visited) => Effect.gen(function* () {
6010
+ const realDir = yield* realpath(fs, dir);
6011
+ if (visited.has(realDir)) return [];
6012
+ const nextVisited = /* @__PURE__ */ new Set([...visited, realDir]);
6013
+ 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);
6014
+ if (Option.isNone(listed)) return [];
6015
+ const entries = [...listed.value].sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
6016
+ const entrySet = new Set(entries);
6017
+ yield* addIgnoreRules(fs, ig, dir, root, entrySet);
6018
+ if (entrySet.has("SKILL.md")) {
6019
+ const fullPath = posix.join(dir, "SKILL.md");
6020
+ const relPath = posix.relative(root, fullPath);
6021
+ if ((yield* isFile(fs, fullPath)) && !ig.ignores(relPath)) {
6022
+ const skill = yield* loadSkillFromFile(fs, fullPath);
6023
+ return skill === void 0 ? [] : [skill];
6024
+ }
6025
+ }
6026
+ const visible = entries.filter((name) => !name.startsWith(".") && name !== "node_modules");
6027
+ const statted = yield* Effect.forEach(visible, (name) => {
6028
+ const fullPath = posix.join(dir, name);
6029
+ return fs.stat(fullPath).pipe(Effect.map((stat) => ({
6030
+ name,
6031
+ fullPath,
6032
+ stat
6033
+ })), Effect.option);
6034
+ }, { concurrency: "unbounded" });
6035
+ const skills = [];
6036
+ for (const item of statted) {
6037
+ if (Option.isNone(item)) continue;
6038
+ const { name, fullPath, stat } = item.value;
6039
+ const relPath = posix.relative(root, fullPath);
6040
+ if (ig.ignores(stat.isDirectory ? `${relPath}/` : relPath)) continue;
6041
+ if (stat.isDirectory) {
6042
+ const nested = yield* scanDir(fs, fullPath, mode, root, ig, nextVisited);
6043
+ skills.push(...nested);
6044
+ continue;
6045
+ }
6046
+ if (stat.isFile && name.endsWith(".md") && (mode === "codework" && dir === root || mode === "agents" && dir !== root)) {
6047
+ const skill = yield* loadSkillFromFile(fs, fullPath);
6048
+ if (skill !== void 0) skills.push(skill);
6049
+ }
6050
+ }
6051
+ return skills;
6052
+ });
6053
+ /**
6054
+ * Load skills from a single directory in the sandbox.
6055
+ *
6056
+ * Discovery rules:
6057
+ * - If a directory contains `SKILL.md`, treat it as a skill root and do not recurse further.
6058
+ * - In `"codework"` mode, direct `.md` children in the root are also loaded; in `"agents"` mode,
6059
+ * `.md` files in subdirectories are loaded while root `.md` files are ignored.
6060
+ * - Recurse into subdirectories (skipping dotfiles, `node_modules`, and ignored paths).
6061
+ */
6062
+ const loadSkillsFromDir = (fs, dir, mode) => scanDir(fs, dir, mode, dir, ignore(), /* @__PURE__ */ new Set());
6063
+ /**
6064
+ * Discover skills inside the sandbox: `.codework/skills` at the cwd, then
6065
+ * `.agents/skills` from the cwd up to the git root (the closest ancestor holding `.git`), or up to
6066
+ * the sandbox root outside a repository. Earlier skills win name collisions.
6067
+ */
6068
+ const discover = Effect.fnUntraced(function* (fs, location) {
6069
+ const chain = ancestors(location.directory);
6070
+ const own = posix.join(location.directory, appConfigDir, "skills");
6071
+ const agents = chain.map((directory) => posix.join(directory, ".agents", "skills"));
6072
+ 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" });
6073
+ const root = chain.findIndex((directory) => gits.has(posix.join(directory, ".git")));
6074
+ const walked = root === -1 ? agents : agents.slice(0, root + 1);
6075
+ const sources = [{
6076
+ dir: own,
6077
+ mode: "codework"
6078
+ }, ...walked.map((dir) => ({
6079
+ dir,
6080
+ mode: "agents"
6081
+ }))].filter(({ dir }) => dirs.has(dir));
6082
+ const found = yield* Effect.forEach(sources, ({ dir, mode }) => loadSkillsFromDir(fs, dir, mode), { concurrency: "unbounded" });
6083
+ const skillMap = /* @__PURE__ */ new Map();
6084
+ const realPathSet = /* @__PURE__ */ new Set();
6085
+ for (const skill of found.flat()) {
6086
+ const realPath = yield* realpath(fs, skill.filePath);
6087
+ if (realPathSet.has(realPath)) continue;
6088
+ const existing = skillMap.get(skill.name);
6089
+ if (existing !== void 0) yield* Effect.logWarning(`skill ${skill.filePath}: name "${skill.name}" collision with ${existing.filePath}`);
6090
+ else {
6091
+ skillMap.set(skill.name, skill);
6092
+ realPathSet.add(realPath);
6093
+ }
6094
+ }
6095
+ return Array.from(skillMap.values());
6096
+ });
6097
+ const escapeXml = (str) => str.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll("\"", "&quot;").replaceAll("'", "&apos;");
6098
+ /**
6099
+ * Format skills for inclusion in `Section.Skills` per the Agent Skills specification.
6100
+ * Skills with `disableModelInvocation: true` are excluded from the prompt.
6101
+ */
6102
+ const formatSkillsForPrompt = (skills, fileReadTool = "read") => {
6103
+ const visibleSkills = skills.filter((s) => !s.disableModelInvocation);
6104
+ if (visibleSkills.length === 0) return "";
6105
+ const lines = [
6106
+ "The following skills provide specialized instructions for specific tasks.",
6107
+ 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.",
6108
+ "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.",
6109
+ "",
6110
+ "<available_skills>"
6111
+ ];
6112
+ for (const skill of visibleSkills) {
6113
+ lines.push(" <skill>");
6114
+ lines.push(` <name>${escapeXml(skill.name)}</name>`);
6115
+ lines.push(` <description>${escapeXml(skill.description)}</description>`);
6116
+ lines.push(` <location>${escapeXml(skill.filePath)}</location>`);
6117
+ lines.push(" </skill>");
6118
+ }
6119
+ lines.push("</available_skills>");
6120
+ return lines.join("\n");
6121
+ };
6122
+ const readers = ["read", "bash"];
6123
+ const skillPlugin = (0, plugin_exports$1.define)({
6124
+ id: "codework.prompt.skill",
6125
+ kind: "prompt",
6126
+ setup: Effect.fn("SkillPlugin.setup")(function* (ctx) {
6127
+ const tools = ctx.plugin.tools;
6128
+ const visible = (name) => {
6129
+ const tool = tools.get(name);
6130
+ return tool !== void 0 && tool.promptSnippet !== void 0 && tool.promptSnippet.length > 0;
6131
+ };
6132
+ const fileReadTool = readers.find(visible) ?? (readers.some((name) => tools.has(name)) ? "indirect" : void 0);
6133
+ if (fileReadTool === void 0) return;
6134
+ const fs = yield* io_exports.SandboxIO.FileSystem;
6135
+ const skills = yield* discover(fs, ctx.location);
6136
+ const formatted = formatSkillsForPrompt(skills, fileReadTool);
6137
+ if (formatted.length === 0) return;
6138
+ ctx.plugin.prompt.sections.append(Section.Skills, formatted);
6139
+ })
6140
+ });
6141
+ //#endregion
5838
6142
  //#region src/tool/truncate.ts
5839
6143
  /**
5840
6144
  * Shared truncation utilities for tool outputs.
@@ -6165,7 +6469,7 @@ const script = (command, file, limits, pipeline, report) => [
6165
6469
  "run 2>&1"
6166
6470
  ].join("\n");
6167
6471
  const header = new RegExp(`^${HEADER} (\\d+) (\\d+) ([01])(?: (\\d+))?$`, "m");
6168
- const parse$3 = (match) => ({
6472
+ const parse$4 = (match) => ({
6169
6473
  bytes: Number(match[1]),
6170
6474
  newlines: Number(match[2]),
6171
6475
  kept: match[3] === "1"
@@ -6184,7 +6488,7 @@ const report = (stderr) => {
6184
6488
  rest: stderr
6185
6489
  };
6186
6490
  return {
6187
- report: parse$3(match),
6491
+ report: parse$4(match),
6188
6492
  rest: stderr.slice(0, match.index) + stderr.slice(match.index + match[0].length).replace(/^\n/, "")
6189
6493
  };
6190
6494
  };
@@ -6207,7 +6511,7 @@ const window = (stdout) => {
6207
6511
  const match = header.exec(newline === -1 ? stdout : stdout.slice(0, newline));
6208
6512
  if (match === null || match[4] === void 0) return void 0;
6209
6513
  return {
6210
- ...parse$3(match),
6514
+ ...parse$4(match),
6211
6515
  text: stdout.slice(newline + 1),
6212
6516
  textBytes: Number(match[4])
6213
6517
  };
@@ -7282,7 +7586,7 @@ const pipeline = (argv, limit, errors) => [
7282
7586
  ].join("; ");
7283
7587
  const STATUS = /^\{"type":"rc","code":(\d+)\}$/;
7284
7588
  /** The pipeline's output: match events, ripgrep's status, and anything else, which is its stderr. */
7285
- const parse$2 = (stdout, stderr) => {
7589
+ const parse$3 = (stdout, stderr) => {
7286
7590
  const matches = [];
7287
7591
  const errors = [];
7288
7592
  let status;
@@ -7366,7 +7670,7 @@ const grepHandler = (params) => Effect.gen(function* () {
7366
7670
  if (params.glob) argv.push("--glob", params.glob);
7367
7671
  argv.push("-e", params.pattern, searchPath);
7368
7672
  const result = yield* shell.exec(pipeline(argv, limit, errors));
7369
- const parsed = parse$2(result.stdout, result.stderr);
7673
+ const parsed = parse$3(result.stdout, result.stderr);
7370
7674
  return {
7371
7675
  ...parsed,
7372
7676
  exitCode: parsed.status ?? result.exitCode
@@ -7931,7 +8235,8 @@ const builtins = Object.freeze([
7931
8235
  findPlugin,
7932
8236
  lsPlugin,
7933
8237
  defaultPromptPlugin,
7934
- instructionPlugin
8238
+ instructionPlugin,
8239
+ skillPlugin
7935
8240
  ]);
7936
8241
  //#endregion
7937
8242
  //#region src/plugin/error.ts
@@ -8371,7 +8676,7 @@ const isPath = (reference) => reference.startsWith("file:") || reference.startsW
8371
8676
  * - a **CLI argument** anchors to the process's startup directory, because that is where the
8372
8677
  * person typed it.
8373
8678
  */
8374
- const parse$1 = (reference, from) => Effect.gen(function* () {
8679
+ const parse$2 = (reference, from) => Effect.gen(function* () {
8375
8680
  const fail = (reason, message) => new SourceError({
8376
8681
  reason,
8377
8682
  reference,
@@ -9047,7 +9352,7 @@ const inspect = Effect.fn("PluginLoader.inspect")(function* (reference, options)
9047
9352
  reference,
9048
9353
  file: options.file
9049
9354
  };
9050
- const target = yield* parse$1(reference, options.hostDir);
9355
+ const target = yield* parse$2(reference, options.hostDir);
9051
9356
  const loaded = yield* load$2(target, origin, options);
9052
9357
  return {
9053
9358
  id: loaded.plugin.id,
@@ -9073,7 +9378,7 @@ const load$2 = Effect.fn("PluginLoader.load")(function* (target, origin, options
9073
9378
  * are one package, and `./plugins/x.ts` is the file it resolves to from the directory that
9074
9379
  * declared it. Pure -- nothing is installed, imported or read.
9075
9380
  */
9076
- const canonical = (reference, hostDir) => Effect.map(parse$1(reference, hostDir), canonical$1);
9381
+ const canonical = (reference, hostDir) => Effect.map(parse$2(reference, hostDir), canonical$1);
9077
9382
  //#endregion
9078
9383
  //#region src/plugin/catalog.ts
9079
9384
  /**
@@ -9135,7 +9440,7 @@ const load$1 = Effect.fn("PluginCatalog.load")(function* (references, options) {
9135
9440
  remember(plugin, []);
9136
9441
  continue;
9137
9442
  }
9138
- const target = yield* parse$1(reference, options.hostDir);
9443
+ const target = yield* parse$2(reference, options.hostDir);
9139
9444
  const key = target.kind === "local" ? target.path : target.spec;
9140
9445
  const already = seen.get(key);
9141
9446
  if (already !== void 0) {
@@ -11318,7 +11623,7 @@ var harness_exports = /* @__PURE__ */ __exportAll({
11318
11623
  const layer = (options = {}) => Layer.unwrap(Effect.gen(function* () {
11319
11624
  const hostCwd = options.hostCwd ?? process.cwd();
11320
11625
  const paths = yield* resolve$4(options.home === void 0 ? {} : { home: hostPath.resolve(hostCwd, expandTilde(options.home, hostPath)) });
11321
- const global = layerWith(paths);
11626
+ const global = layerWith$1(paths);
11322
11627
  const settingsOptions = options.userConfigDir === void 0 ? {} : { userConfigDir: userConfigDir(options.userConfigDir, hostCwd) };
11323
11628
  const config = yield* load$3({
11324
11629
  ...settingsOptions,
@@ -11337,7 +11642,7 @@ const layer = (options = {}) => Layer.unwrap(Effect.gen(function* () {
11337
11642
  * `hostDir` led to; only the process view falls back to `hostCwd`.
11338
11643
  */
11339
11644
  const hostDirOf = (root) => root ?? hostCwd;
11340
- 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()));
11645
+ 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()));
11341
11646
  const resolveOnly = {
11342
11647
  ...catalogOptions,
11343
11648
  install: required
@@ -11579,13 +11884,24 @@ const makeHandle = Effect.fn("Session.makeHandle")(function* (id) {
11579
11884
  const space = Option.getOrUndefined(yield* sessions.space(id));
11580
11885
  const sandbox = space === void 0 || space.env === instance_exports.SandboxInstance.ID.local ? void 0 : Option.getOrUndefined(yield* sandboxes.get(space.env));
11581
11886
  const hostDir = Option.getOrUndefined(row.hostDir);
11887
+ const project = Option.getOrUndefined(yield* sessions.project(id));
11888
+ const config = Option.getOrUndefined(row.config);
11889
+ const model = config?.model;
11890
+ const thinkingLevel = config?.thinkingLevel;
11582
11891
  return {
11583
11892
  id,
11584
11893
  title: row.title,
11585
11894
  directory: row.directory,
11586
11895
  ...hostDir === void 0 ? {} : { hostDir },
11587
11896
  hasHostLink: hostDir !== void 0,
11588
- ...sandbox === void 0 ? {} : { sandbox }
11897
+ ...sandbox === void 0 ? {} : { sandbox },
11898
+ ...project === void 0 ? {} : { project: {
11899
+ id: project.id,
11900
+ name: project.name
11901
+ } },
11902
+ updatedAt: DateTime.toEpochMillis(row.updatedAt),
11903
+ ...model === void 0 ? {} : { model },
11904
+ ...thinkingLevel === void 0 ? {} : { thinkingLevel }
11589
11905
  };
11590
11906
  }).pipe(Effect.withSpan("Session.info"));
11591
11907
  return {
@@ -11613,7 +11929,7 @@ const create$1 = Effect.fn("Session.create")(function* (input = {}) {
11613
11929
  const sandboxes = yield* Controller;
11614
11930
  const id = ID$1.create();
11615
11931
  const sandboxId = input.sandbox?.id ?? instance_exports.SandboxInstance.ID.local;
11616
- const location = yield* sandboxes.withMount(sandboxId, Effect.provide(Service$8.use(Effect.succeed), layerMounted()), input.directory === void 0 ? void 0 : { cwd: input.directory });
11932
+ const location = yield* sandboxes.withMount(sandboxId, Effect.provide(Service$8.use(Effect.succeed), layerMounted(input.project === void 0 ? void 0 : { name: input.project.name })), input.directory === void 0 ? void 0 : { cwd: input.directory });
11617
11933
  yield* sessions.create({
11618
11934
  id,
11619
11935
  spaceId: location.space.id,
@@ -11727,7 +12043,7 @@ var plugin_exports = /* @__PURE__ */ __exportAll({
11727
12043
  inspect: () => inspect,
11728
12044
  lendResolve: () => lendResolve,
11729
12045
  localName: () => localName,
11730
- parse: () => parse$1,
12046
+ parse: () => parse$2,
11731
12047
  probe: () => probe,
11732
12048
  resolve: () => resolve$2,
11733
12049
  resolveCached: () => required,
@@ -11740,6 +12056,6 @@ var DateTimeUtcFromMillis = schema_exports$9.DateTimeUtcFromMillis;
11740
12056
  var EventSchema = schema_exports$7.EventSchema;
11741
12057
  var SandboxError = errors_exports.SandboxError;
11742
12058
  var optional = schema_exports$9.optional;
11743
- export { control_exports as Control, db_exports as Database, DateTimeUtcFromMillis, event_exports as Event, list_exports as EventList, registry_exports as EventRegistry, EventSchema, global_exports as Global, harness_exports as Harness, location_exports as Location, catalog_exports as ModelCatalog, plugin_exports as Plugin, schema_exports as PromptSchema, run_exports as Runner, sandbox_exports as Sandbox, SandboxError, session_exports as Session, session_exports$1 as SessionStore, settings_exports as Settings, state_exports as State, tool_exports as Tool, optional };
12059
+ export { control_exports as Control, db_exports as Database, DateTimeUtcFromMillis, event_exports as Event, list_exports as EventList, registry_exports as EventRegistry, EventSchema, global_exports as Global, harness_exports as Harness, location_exports as Location, catalog_exports as ModelCatalog, plugin_exports as Plugin, project_exports as Project, schema_exports as PromptSchema, run_exports as Runner, sandbox_exports as Sandbox, SandboxError, session_exports as Session, session_exports$1 as SessionStore, settings_exports as Settings, state_exports as State, tool_exports as Tool, optional };
11744
12060
 
11745
12061
  //# sourceMappingURL=effect.mjs.map