@codeworksh/harness 0.0.1-dev.20260923102526 → 0.0.1-dev.20260925125142

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.d.mts CHANGED
@@ -1284,9 +1284,13 @@ declare const defaultLayer: Layer.Layer<Service$9, Config.ConfigError, never>;
1284
1284
  declare const layerWith: (input: Partial<Interface$8>) => Layer.Layer<Service$9, Config.ConfigError, never>;
1285
1285
  import * as import__codeworksh_plugin_settings from "@codeworksh/plugin/settings";
1286
1286
  declare namespace settings_d_exports {
1287
- export { Interface$7 as Interface, Options$4 as Options, Service$8 as Service, settings_d_exports as Settings, SettingsError, schema_d_exports$4.defaults as defaults, layer$7 as layer, load$1 as load, parse$1 as parse, paths, projectRoot };
1287
+ export { Interface$7 as Interface, Options$4 as Options, Service$8 as Service, settings_d_exports as Settings, SettingsError, schema_d_exports$4.defaults as defaults, layer$7 as layer, load$1 as load, modules, parse$1 as parse, paths, projectRoot, userConfigDir };
1288
1288
  }
1289
1289
  interface Options$4 {
1290
+ /**
1291
+ * `--user-config-dir`, already absolute ({@link userConfigDir}): the directory whose settings
1292
+ * file replaces the user layer's.
1293
+ */
1290
1294
  readonly userConfigDir?: string;
1291
1295
  /**
1292
1296
  * The host directory this load discovers the project layer from, supplied by the caller --
@@ -1302,6 +1306,12 @@ interface Options$4 {
1302
1306
  */
1303
1307
  readonly hostDir?: string | undefined;
1304
1308
  }
1309
+ /**
1310
+ * An app-level `--user-config-dir` as the absolute directory it names: `~` is the user's home, and
1311
+ * a relative spelling is relative to `hostCwd` -- never to a session's `hostDir`, which is not
1312
+ * where the flag was typed.
1313
+ */
1314
+ declare const userConfigDir: (raw: string, hostCwd: string) => string;
1305
1315
  /**
1306
1316
  * The project a directory is in: the nearest ancestor holding a `.codework` **directory**.
1307
1317
  *
@@ -1336,9 +1346,8 @@ declare function paths(input: {
1336
1346
  readonly home: string;
1337
1347
  /** The project root, from {@link projectRoot}. */
1338
1348
  readonly root?: string | undefined;
1339
- /** What a relative `--user-config-dir` is resolved against. */
1340
- readonly from?: string | undefined;
1341
- readonly custom?: string | undefined;
1349
+ /** `--user-config-dir`, absolute ({@link userConfigDir}): replaces the home's settings. */
1350
+ readonly userConfigDir?: string | undefined;
1342
1351
  }): ReadonlyArray<ReadonlyArray<string>>;
1343
1352
  declare const SettingsError_base: Schema.Class<SettingsError, Schema.TaggedStruct<"SettingsError", {
1344
1353
  readonly path: Schema.String;
@@ -1565,6 +1574,16 @@ declare const load$1: (options: Options$4 & {
1565
1574
  })[];
1566
1575
  declared: readonly schema_d_exports$4.Declared[];
1567
1576
  }, SettingsError, never>;
1577
+ /**
1578
+ * Each module reference once, owned by the **last** file to declare it (D6).
1579
+ *
1580
+ * The owner is not bookkeeping: its `.npmrc` chain keys the store artifact. The CLI that installs
1581
+ * a reference and the runtime that loads it must pick the same owner, or `plugin update` refreshes
1582
+ * an artifact the runtime never reads. Only a string entry loads a module, so only a string entry
1583
+ * owns one -- a `{ package }` patch in another file configures it and must not move its registry.
1584
+ * A reference keeps the position of its first declaration.
1585
+ */
1586
+ declare const modules: (declared: ReadonlyArray<schema_d_exports$4.Declared>) => ReadonlyMap<string, schema_d_exports$4.Declared>;
1568
1587
  declare const layer$7: (options?: Omit<Options$4, "hostDir">) => Layer.Layer<Service$8, never, Service$9>;
1569
1588
  import * as import__codeworksh_plugin_plugin from "@codeworksh/plugin/plugin";
1570
1589
  declare namespace registry_d_exports {
@@ -1686,6 +1705,11 @@ type Fetchable = Exclude<Target, {
1686
1705
  declare const parse: (reference: string, from: string) => Effect.Effect<Target, SourceError>;
1687
1706
  //#endregion
1688
1707
  //#region src/plugin/loader.d.ts
1708
+ /**
1709
+ * The name a local package declares, read without importing it: the alias the catalog registers
1710
+ * for it, so a `{ package }` entry and a command naming the package both reach it.
1711
+ */
1712
+ declare const localName: (location: string) => Effect.Effect<string | undefined>;
1689
1713
  interface Options$3 {
1690
1714
  readonly cache: string;
1691
1715
  /**
@@ -2060,27 +2084,37 @@ declare class Service$5 extends Service_base$4 {}
2060
2084
  * `references` says which entries the config pass walks. It is a function of the settings that
2061
2085
  * exchange read, so a caller that pinned an explicit list keeps it and everyone else follows the
2062
2086
  * files.
2087
+ *
2088
+ * `pool` is the **process view**: what boot loaded from the user layers. A session in a project
2089
+ * runs from that project's own view, a pool created from the process view the first time the
2090
+ * project is seen and moved only by its own sessions. The same spec can be owned by different
2091
+ * files -- and so name different registries -- in two views; one shared pool could hold only one of
2092
+ * those builds, and sessions in different projects would keep evicting each other's.
2063
2093
  */
2064
2094
  declare const layer$4: (options: Options$2, pool: Ref.Ref<Pool>, references: (settings: schema_d_exports$4.Info) => ReadonlyArray<PluginRef>,
2065
2095
  /**
2066
2096
  * The exchange-boundary check: the new pool when the store has moved, nothing when it has not.
2067
2097
  * State does not know what a store is, so whoever built the pool answers this.
2068
2098
  */
2069
- follow: (refs: ReadonlyArray<PluginRef>, current: Pool, settings: schema_d_exports$4.Info) => Effect.Effect<Option.Option<Pool>, {
2099
+ follow: (refs: ReadonlyArray<PluginRef>, current: Pool, settings: schema_d_exports$4.Info,
2100
+ /** The view's project root, whose host directory the view resolves against. */
2101
+ root: string | undefined) => Effect.Effect<Option.Option<Pool>, {
2070
2102
  readonly message: string;
2071
2103
  }>,
2072
2104
  /**
2073
2105
  * A full load pass, told to re-import even where nothing looks different. Resolve-only, like
2074
2106
  * `follow`: reload re-imports what is on disk, it does not fetch.
2075
2107
  *
2076
- * It takes no references and reads its own settings, because the set it must produce is the
2077
- * *process's* -- every module any session might name -- and State only ever sees one session's
2078
- * view. Handing it `references` from here would quietly narrow the pool to whatever the
2079
- * caller's project declares, and drop every other project's plugins on the floor.
2108
+ * It takes a view rather than references and reads that view's settings itself, because the set
2109
+ * it must produce is the view's -- every module any of its sessions has named -- and State only
2110
+ * ever sees one session. Handing it `references` from here would quietly narrow the pool to
2111
+ * whatever the caller declares, and drop what the view's other sessions loaded on the floor.
2080
2112
  */
2081
- rebuild: () => Effect.Effect<Pool, {
2113
+ rebuild: (root: string | undefined, current: Pool) => Effect.Effect<Pool, {
2082
2114
  readonly message: string;
2083
- }>) => Layer.Layer<Service$5, never, Service$17 | Service$16 | Service$8 | Service$7 | Service$6>;
2115
+ }>,
2116
+ /** The view a session in `hostDir` runs from: its project root, or the process view. */
2117
+ viewOf?: (hostDir: string | undefined) => Effect.Effect<string | undefined>) => Layer.Layer<Service$5, never, Service$17 | Service$16 | Service$8 | Service$7 | Service$6>;
2084
2118
  declare namespace run_d_exports {
2085
2119
  export { Interface$3 as Interface, LLMStreamError, ModelCatalogError, ModelNotFoundError, ProviderAuthenticationError, ProviderAuthorizationError, ProviderConfigurationError, ProviderContentPolicyError, ProviderError, ProviderFailureReason, ProviderInvalidRequestError, ProviderInvalidResponseError, ProviderModelUnavailableError, ProviderQuotaError, ProviderRateLimitError, ProviderTimeoutError, ProviderTransportError, ProviderUnavailableError, ProviderUnknownError, RunError, run_d_exports as Runner, SandboxDirectoryNotFoundError, Service$4 as Service };
2086
2120
  }
@@ -3183,13 +3217,21 @@ interface Options {
3183
3217
  readonly database?: string;
3184
3218
  /**
3185
3219
  * The process-level host directory: the default sandbox mount, the `.npmrc`/parse fallback
3186
- * for plugin references no file declared, and the base a relative `--user-config-dir`
3220
+ * for plugin references no file declared, and the base a relative `home` or `userConfigDir`
3187
3221
  * resolves against. Defaults to the OS process's directory. It is never a project-settings
3188
- * root -- that discovery belongs to a session's `hostDir`.
3222
+ * root -- that discovery belongs to a session's `hostDir` -- and never a sandbox `cwd`.
3189
3223
  */
3190
3224
  readonly hostCwd?: string;
3225
+ /**
3226
+ * `--home`: the shared, user-wide home -- `~/.codework` by default -- holding the user
3227
+ * settings, credentials, plugin cache and data.
3228
+ */
3191
3229
  readonly home?: string;
3192
- /** user provided directory containing the highest-priority config. */
3230
+ /**
3231
+ * `--user-config-dir`: a hard override of the user settings file only. Its `settings.jsonc`
3232
+ * is read *instead of* the home's; the project layer still sits above it, and everything else
3233
+ * the home holds stays in the home.
3234
+ */
3193
3235
  readonly userConfigDir?: string;
3194
3236
  readonly sandboxes?: ReadonlyArray<Entry$1>;
3195
3237
  readonly llm?: Open;
@@ -23391,6 +23433,15 @@ interface Entry extends Marker {
23391
23433
  /** A `file:` URL for the entrypoint, ready to import. */
23392
23434
  readonly url: string;
23393
23435
  }
23436
+ /**
23437
+ * The **full** SHA-256 of the source identity: canonical spec plus effective registry for a
23438
+ * registry package, and the canonical spec alone for git.
23439
+ *
23440
+ * Not truncated. The fast path proves a hit by checking that a marker *exists* rather than by
23441
+ * decoding it, and the index is keyed by digest alone, so a truncated digest would let a collision
23442
+ * return the wrong package silently.
23443
+ */
23444
+ declare const digest: (target: Fetchable, cache: string, from: string) => Effect.Effect<string, InstallError | import("effect/PlatformError").PlatformError, never>;
23394
23445
  /**
23395
23446
  * Disk-only lookup. No network, no install -- this is what boot uses, and the reason booting
23396
23447
  * offline works.
@@ -23490,7 +23541,7 @@ declare const update: <A>(target: Fetchable, cache: string, options: {
23490
23541
  readonly validated?: A & ({} | null);
23491
23542
  }, InstallError | LoadError | StoreError, never>;
23492
23543
  declare namespace index_d_exports {
23493
- export { Checked, Config$1 as Config, Entry, Events, Fetchable, InstallError, LoadError, plugin_d_exports.Mount as Mount, plugin_d_exports.Plugin as Plugin, PluginOptions, PluginRef, PluginRegistry, Probe, schema_d_exports$1 as Prompt, PromptResolver, SetupError, SharedPluginContext, SourceError, StoreError, Target, schema_d_exports$3 as Tool, Updated, anchor, canonical, check, plugin_d_exports.define as define, definition, inspect, parse, probe, resolve, required as resolveCached, lock as settingsLock, update };
23544
+ export { Checked, Config$1 as Config, Entry, Events, Fetchable, InstallError, LoadError, plugin_d_exports.Mount as Mount, plugin_d_exports.Plugin as Plugin, PluginOptions, PluginRef, PluginRegistry, Probe, schema_d_exports$1 as Prompt, PromptResolver, SetupError, SharedPluginContext, SourceError, StoreError, Target, schema_d_exports$3 as Tool, Updated, anchor, canonical, check, plugin_d_exports.define as define, definition, digest as identity, inspect, localName, parse, probe, resolve, required as resolveCached, lock as settingsLock, update };
23494
23545
  }
23495
23546
  type DateTimeUtcFromMillis = import__codeworksh_plugin_schema.DateTimeUtcFromMillis;
23496
23547
  type EventSchema = import__codeworksh_plugin_event.EventSchema;
package/effect.mjs CHANGED
@@ -6746,6 +6746,12 @@ const Manifest = Schema.Struct({
6746
6746
  name: Schema.optional(Schema.String),
6747
6747
  exports: Schema.optional(Schema.Unknown)
6748
6748
  });
6749
+ const readManifest = (manifestPath) => fileSystem.readFileString(manifestPath).pipe(Effect.flatMap(Schema.decodeUnknownEffect(Schema.fromJsonString(Manifest))));
6750
+ /**
6751
+ * The name a local package declares, read without importing it: the alias the catalog registers
6752
+ * for it, so a `{ package }` entry and a command naming the package both reach it.
6753
+ */
6754
+ const localName = (location) => readManifest(hostPath.join(location, "package.json")).pipe(Effect.map((manifest) => manifest.name), Effect.orElseSucceed(() => void 0));
6749
6755
  const localUrl = Effect.fn("PluginLoader.localUrl")(function* (location, origin) {
6750
6756
  const escapes = (message) => new SourceError({
6751
6757
  reason: "plugin-escapes-root",
@@ -6756,7 +6762,7 @@ const localUrl = Effect.fn("PluginLoader.localUrl")(function* (location, origin)
6756
6762
  if ((yield* fileSystem.stat(location)).type !== "Directory") return { url: yield* Effect.try(() => resolveModule(location, hostPath.dirname(location))) };
6757
6763
  const manifestPath = hostPath.join(location, "package.json");
6758
6764
  if (!(yield* fileSystem.exists(manifestPath))) return { url: yield* Effect.try(() => resolveModule("./index", location)) };
6759
- const manifest = yield* fileSystem.readFileString(manifestPath).pipe(Effect.flatMap(Schema.decodeUnknownEffect(Schema.fromJsonString(Manifest))));
6765
+ const manifest = yield* readManifest(manifestPath);
6760
6766
  if (manifest.exports !== void 0) {
6761
6767
  const name = manifest.name;
6762
6768
  if (!name) return yield* escapes("a local package with exports must declare its name");
@@ -7032,7 +7038,9 @@ const select = Effect.fn("PluginCatalog.select")(function* (references, pool) {
7032
7038
  * walks those entries against the loaded set. Two things can be settled right there, and they are
7033
7039
  * the same question asked twice:
7034
7040
  *
7035
- * 1. a settings entry names a module that is not loaded, and the store holds it;
7041
+ * 1. a settings entry names a module that is not loaded, and the store holds it -- or it is loaded
7042
+ * from another file's artifact than the one that owns it in this view, and the store holds
7043
+ * the owner's;
7036
7044
  * 2. a loaded module is not the newest generation the store has.
7037
7045
  *
7038
7046
  * The second is what makes `plugin update` finish. Without it the question is "is this entry
@@ -7051,8 +7059,16 @@ const select = Effect.fn("PluginCatalog.select")(function* (references, pool) {
7051
7059
  const follow = Effect.fn("PluginCatalog.follow")(function* (references, pool, options, filed) {
7052
7060
  let moved = false;
7053
7061
  for (const reference of references) {
7054
- if (typeof reference !== "string" || pool.aliases.has(reference)) continue;
7055
- if (Option.isSome(yield* filed(reference, anchor$1(options.declared?.get(reference), options.hostDir)))) {
7062
+ if (typeof reference !== "string") continue;
7063
+ const owner = options.declared?.get(reference);
7064
+ const from = anchor$1(owner, options.hostDir);
7065
+ const id = pool.aliases.get(reference);
7066
+ if (id !== void 0) {
7067
+ const origin = pool.origins.get(id);
7068
+ if (owner === void 0 || origin?.reference !== reference) continue;
7069
+ if (anchor$1(origin.file, options.hostDir) === from) continue;
7070
+ }
7071
+ if (Option.isSome(yield* filed(reference, from))) {
7056
7072
  moved = true;
7057
7073
  break;
7058
7074
  }
@@ -7785,7 +7801,7 @@ const make$4 = (tools) => {
7785
7801
  if (before.value !== void 0 && !Schema.is(schema_exports$2.ToolBeforeResult)(before.value)) return yield* failureOutcome(call, Cause.die(/* @__PURE__ */ new Error("beforeToolCall returned an invalid result")), "beforeToolCall failed");
7786
7802
  if (before.value?.block) {
7787
7803
  const now = yield* Effect.clockWith((clock) => clock.currentTimeMillis);
7788
- return errored(call, [text(before.value.reason || "Tool execution was blocked")], now);
7804
+ return errored(call, [text(before.value?.reason || "Tool execution was blocked")], now);
7789
7805
  }
7790
7806
  }
7791
7807
  const latest = yield* Ref.make(Option.none());
@@ -8101,10 +8117,18 @@ var settings_exports = /* @__PURE__ */ __exportAll({
8101
8117
  defaults: () => schema_exports$3.defaults,
8102
8118
  layer: () => layer$5,
8103
8119
  load: () => load$1,
8120
+ modules: () => modules,
8104
8121
  parse: () => parse$1,
8105
8122
  paths: () => paths,
8106
- projectRoot: () => projectRoot
8123
+ projectRoot: () => projectRoot,
8124
+ userConfigDir: () => userConfigDir
8107
8125
  });
8126
+ /**
8127
+ * An app-level `--user-config-dir` as the absolute directory it names: `~` is the user's home, and
8128
+ * a relative spelling is relative to `hostCwd` -- never to a session's `hostDir`, which is not
8129
+ * where the flag was typed.
8130
+ */
8131
+ const userConfigDir = (raw, hostCwd) => hostPath.resolve(hostCwd, expandTilde(raw, hostPath));
8108
8132
  /** The settings files a directory may hold, in the order it is searched. */
8109
8133
  const settingsIn = (directory) => [hostPath.join(directory, "settings.jsonc"), hostPath.join(directory, "settings.json")];
8110
8134
  /** A directory and every ancestor above it, nearest first. */
@@ -8154,13 +8178,7 @@ const projectRoot = Effect.fn("Settings.projectRoot")(function* (from, home) {
8154
8178
  * no project layer at all, which is what a session with no host directory gets.
8155
8179
  */
8156
8180
  function paths(input) {
8157
- const expanded = input.custom === void 0 ? void 0 : expandTilde(input.custom, hostPath);
8158
- const explicit = expanded === void 0 ? void 0 : input.from === void 0 ? expanded : hostPath.resolve(input.from, expanded);
8159
- return [
8160
- settingsIn(input.home),
8161
- input.root === void 0 ? [] : settingsIn(hostPath.join(input.root, appConfigDir)),
8162
- ...explicit === void 0 ? [] : [settingsIn(explicit)]
8163
- ];
8181
+ return [settingsIn(input.userConfigDir ?? input.home), input.root === void 0 ? [] : settingsIn(hostPath.join(input.root, appConfigDir))];
8164
8182
  }
8165
8183
  var SettingsError = class extends Schema.TaggedError()("SettingsError", {
8166
8184
  path: Schema.String,
@@ -8238,8 +8256,7 @@ const load$1 = Effect.fn("Settings.load")(function* (options) {
8238
8256
  const files = paths({
8239
8257
  home: options.home,
8240
8258
  root,
8241
- ...options.hostDir === void 0 ? {} : { from: hostPath.resolve(options.hostDir) },
8242
- ...options.userConfigDir === void 0 ? {} : { custom: options.userConfigDir }
8259
+ ...options.userConfigDir === void 0 ? {} : { userConfigDir: options.userConfigDir }
8243
8260
  });
8244
8261
  let settings = merge(schema_exports$3.defaults);
8245
8262
  let declared = schema_exports$3.defaults.declared;
@@ -8265,6 +8282,20 @@ const load$1 = Effect.fn("Settings.load")(function* (options) {
8265
8282
  declared
8266
8283
  };
8267
8284
  });
8285
+ /**
8286
+ * Each module reference once, owned by the **last** file to declare it (D6).
8287
+ *
8288
+ * The owner is not bookkeeping: its `.npmrc` chain keys the store artifact. The CLI that installs
8289
+ * a reference and the runtime that loads it must pick the same owner, or `plugin update` refreshes
8290
+ * an artifact the runtime never reads. Only a string entry loads a module, so only a string entry
8291
+ * owns one -- a `{ package }` patch in another file configures it and must not move its registry.
8292
+ * A reference keeps the position of its first declaration.
8293
+ */
8294
+ const modules = (declared) => {
8295
+ const owners = /* @__PURE__ */ new Map();
8296
+ for (const one of declared) if (typeof one.entry === "string") owners.set(one.entry, one);
8297
+ return owners;
8298
+ };
8268
8299
  const layer$5 = (options = {}) => Layer.effect(Service$2, Effect.gen(function* () {
8269
8300
  const global = yield* Service$17;
8270
8301
  return Service$2.of({ load: (hostDir) => load$1({
@@ -8314,8 +8345,14 @@ const resolver = (input) => typeof input === "string" ? () => input : input;
8314
8345
  * `references` says which entries the config pass walks. It is a function of the settings that
8315
8346
  * exchange read, so a caller that pinned an explicit list keeps it and everyone else follows the
8316
8347
  * files.
8348
+ *
8349
+ * `pool` is the **process view**: what boot loaded from the user layers. A session in a project
8350
+ * runs from that project's own view, a pool created from the process view the first time the
8351
+ * project is seen and moved only by its own sessions. The same spec can be owned by different
8352
+ * files -- and so name different registries -- in two views; one shared pool could hold only one of
8353
+ * those builds, and sessions in different projects would keep evicting each other's.
8317
8354
  */
8318
- const layer$4 = (options, pool, references, follow, rebuild) => {
8355
+ const layer$4 = (options, pool, references, follow, rebuild, viewOf = () => Effect.undefined) => {
8319
8356
  return Layer.effect(Service$1, Effect.gen(function* () {
8320
8357
  const runtime = yield* Service$3;
8321
8358
  const sessions = yield* Service$14;
@@ -8330,10 +8367,31 @@ const layer$4 = (options, pool, references, follow, rebuild) => {
8330
8367
  id,
8331
8368
  ...origin.file === void 0 ? {} : { file: origin.file }
8332
8369
  });
8333
- const activate = Effect.fn("State.activatePlugins")(function* (next) {
8334
- const previous = yield* Ref.get(pool);
8335
- yield* eventRegistry.replace([...next.plugins.values()]);
8336
- yield* Ref.set(pool, next);
8370
+ /** Project root to its view. The process view is `pool` itself. */
8371
+ const views = /* @__PURE__ */ new Map();
8372
+ /** Created from the process view on first use; called under `loading`, so never twice. */
8373
+ const poolOf = Effect.fnUntraced(function* (root) {
8374
+ if (root === void 0) return pool;
8375
+ const existing = views.get(root);
8376
+ if (existing !== void 0) return existing;
8377
+ const created = yield* Ref.make(yield* Ref.get(pool));
8378
+ views.set(root, created);
8379
+ return created;
8380
+ });
8381
+ const everyView = () => [[void 0, pool], ...views];
8382
+ /**
8383
+ * Every plugin some view runs, one per ID. Event types are process-wide, and two views
8384
+ * holding two builds of one plugin register its namespace once.
8385
+ */
8386
+ const union = Effect.fnUntraced(function* (target, next) {
8387
+ const plugins = /* @__PURE__ */ new Map();
8388
+ for (const [, ref] of everyView()) for (const [id, plugin] of ref === target ? next.plugins : (yield* Ref.get(ref)).plugins) plugins.set(id, plugin);
8389
+ return plugins;
8390
+ });
8391
+ const activate = Effect.fn("State.activatePlugins")(function* (target, next) {
8392
+ const previous = yield* Ref.get(target);
8393
+ yield* eventRegistry.replace([...(yield* union(target, next)).values()]);
8394
+ yield* Ref.set(target, next);
8337
8395
  for (const [id, origin] of next.origins) {
8338
8396
  if (next.plugins.get(id) === previous.plugins.get(id)) continue;
8339
8397
  yield* updated(origin, id, "loaded");
@@ -8353,16 +8411,21 @@ const layer$4 = (options, pool, references, follow, rebuild) => {
8353
8411
  });
8354
8412
  return Service$1.of({
8355
8413
  reload: Effect.gen(function* () {
8356
- const rebuilt = yield* rebuild().pipe(Effect.flatMap(activate), loading.withPermits(1), Effect.result);
8357
- if (rebuilt._tag === "Failure") {
8358
- yield* Effect.logWarning(`plugin reload failed, keeping the previous set: ${rebuilt.failure.message}`);
8359
- yield* publishFailed(rebuilt.failure);
8360
- return {
8361
- plugins: (yield* Ref.get(pool)).plugins.size,
8362
- failure: rebuilt.failure.message
8363
- };
8414
+ const failures = /* @__PURE__ */ new Map();
8415
+ for (const [root, target] of everyView()) {
8416
+ const rebuilt = yield* Ref.get(target).pipe(Effect.flatMap((current) => rebuild(root, current)), Effect.flatMap((next) => activate(target, next)), loading.withPermits(1), Effect.result);
8417
+ if (rebuilt._tag === "Failure") failures.set(rebuilt.failure.message, rebuilt.failure);
8418
+ }
8419
+ for (const failure of failures.values()) {
8420
+ yield* Effect.logWarning(`plugin reload failed, keeping the previous set: ${failure.message}`);
8421
+ yield* publishFailed(failure);
8364
8422
  }
8365
- return { plugins: rebuilt.success.plugins.size };
8423
+ const plugins = (yield* union(pool, yield* Ref.get(pool))).size;
8424
+ const [first] = failures.keys();
8425
+ return first === void 0 ? { plugins } : {
8426
+ plugins,
8427
+ failure: first
8428
+ };
8366
8429
  }),
8367
8430
  snapshot: Effect.fn("State.snapshot")(function* (sessionId) {
8368
8431
  const sessionOptions = Option.getOrElse(yield* runtime.get(sessionId), () => ({}));
@@ -8387,10 +8450,12 @@ const layer$4 = (options, pool, references, follow, rebuild) => {
8387
8450
  cause
8388
8451
  });
8389
8452
  const loaded = yield* Effect.gen(function* () {
8390
- const current = yield* Ref.get(pool);
8391
- const moved = yield* follow(refs, current, loadedSettings).pipe(Effect.tapError((cause) => publishFailed(cause, sessionId)));
8453
+ const root = yield* viewOf(hostDir);
8454
+ const target = yield* poolOf(root);
8455
+ const current = yield* Ref.get(target);
8456
+ const moved = yield* follow(refs, current, loadedSettings, root).pipe(Effect.tapError((cause) => publishFailed(cause, sessionId)));
8392
8457
  if (Option.isNone(moved)) return current;
8393
- return yield* activate(moved.value);
8458
+ return yield* activate(target, moved.value);
8394
8459
  }).pipe(loading.withPermits(1), Effect.mapError(failed));
8395
8460
  const chosen = yield* select(refs, loaded).pipe(Effect.mapError(failed));
8396
8461
  for (const reference of chosen.missing) {
@@ -8883,23 +8948,28 @@ var harness_exports = /* @__PURE__ */ __exportAll({
8883
8948
  layer: () => layer
8884
8949
  });
8885
8950
  const layer = (options = {}) => Layer.unwrap(Effect.gen(function* () {
8886
- const paths = yield* resolve$3(options.home === void 0 ? {} : { home: options.home });
8887
8951
  const hostCwd = options.hostCwd ?? process.cwd();
8952
+ const paths = yield* resolve$3(options.home === void 0 ? {} : { home: hostPath.resolve(hostCwd, expandTilde(options.home, hostPath)) });
8888
8953
  const global = layerWith$1(paths);
8889
- const settingsOptions = options.userConfigDir === void 0 ? {} : { userConfigDir: hostPath.resolve(hostCwd, expandTilde(options.userConfigDir, hostPath)) };
8954
+ const settingsOptions = options.userConfigDir === void 0 ? {} : { userConfigDir: userConfigDir(options.userConfigDir, hostCwd) };
8890
8955
  const config = yield* load$1({
8891
8956
  ...settingsOptions,
8892
8957
  home: paths.home
8893
8958
  });
8894
8959
  const references = (settings) => options.plugins ?? [...builtins, ...settings.plugins];
8895
- /** Reference to the file that declared it, for failures that should name one. */
8896
- const declaredIn = (settings) => new Map(settings.declared.flatMap((one) => typeof one.entry === "string" ? [[one.entry, one.file]] : "package" in one.entry ? [[one.entry.package, one.file]] : []));
8960
+ /** Reference to the file that owns it: the `.npmrc` anchor, and what a failure names. */
8961
+ const declaredIn = (settings) => new Map(Array.from(modules(settings.declared), ([reference, one]) => [reference, one.file]));
8897
8962
  const catalogOptions = {
8898
8963
  builtins,
8899
8964
  cache: paths.cache,
8900
8965
  hostDir: hostCwd
8901
8966
  };
8902
- const filed = (reference, from) => parse$2(reference, hostCwd).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()));
8967
+ /**
8968
+ * The host directory a view resolves against: a project view's is the root its sessions'
8969
+ * `hostDir` led to; only the process view falls back to `hostCwd`.
8970
+ */
8971
+ const hostDirOf = (root) => root ?? hostCwd;
8972
+ 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()));
8903
8973
  const resolveOnly = {
8904
8974
  ...catalogOptions,
8905
8975
  install: required
@@ -8908,22 +8978,25 @@ const layer = (options = {}) => Layer.unwrap(Effect.gen(function* () {
8908
8978
  ...resolveOnly,
8909
8979
  declared: declaredIn(config)
8910
8980
  }));
8911
- const followStore = (refs, current, settings) => follow(refs, current, {
8981
+ const followStore = (refs, current, settings, root) => follow(refs, current, {
8912
8982
  ...resolveOnly,
8983
+ hostDir: hostDirOf(root),
8913
8984
  declared: declaredIn(settings)
8914
8985
  }, filed);
8986
+ /** A session's view is its project root; one with no project runs from the process view. */
8987
+ const viewOf = (hostDir) => hostDir === void 0 ? Effect.undefined : projectRoot(hostDir, paths.home).pipe(Effect.orElseSucceed(() => void 0));
8915
8988
  let reloads = 0;
8916
- const rebuild = () => Effect.gen(function* () {
8989
+ const rebuild = (root, loaded) => Effect.gen(function* () {
8917
8990
  reloads += 1;
8918
8991
  const current = yield* load$1({
8919
8992
  ...settingsOptions,
8920
- home: paths.home
8993
+ home: paths.home,
8994
+ ...root === void 0 ? {} : { hostDir: root }
8921
8995
  });
8922
- const loaded = yield* Ref.get(pool);
8923
8996
  const retained = [];
8924
8997
  const configured = references(current);
8925
8998
  for (const origin of loaded.origins.values()) {
8926
- if (configured.includes(origin.reference) || Option.isSome(yield* filed(origin.reference, anchor$1(origin.file, hostCwd)))) {
8999
+ if (configured.includes(origin.reference) || Option.isSome(yield* filed(origin.reference, anchor$1(origin.file, hostDirOf(root))))) {
8927
9000
  retained.push(origin);
8928
9001
  continue;
8929
9002
  }
@@ -8935,6 +9008,7 @@ const layer = (options = {}) => Layer.unwrap(Effect.gen(function* () {
8935
9008
  for (const [reference, file] of declaredIn(current)) known.set(reference, file);
8936
9009
  return yield* load$2(accumulated, {
8937
9010
  ...resolveOnly,
9011
+ hostDir: hostDirOf(root),
8938
9012
  declared: known,
8939
9013
  reload: reloads
8940
9014
  });
@@ -8947,7 +9021,7 @@ const layer = (options = {}) => Layer.unwrap(Effect.gen(function* () {
8947
9021
  const authFile$1 = authFile(options.home === void 0 ? void 0 : paths.home);
8948
9022
  const open = options.llm ?? openWith(authFile$1 === void 0 ? {} : { authFile: authFile$1 });
8949
9023
  const loop = layer$3({ request: make$5(open) });
8950
- return layer$10.pipe(Layer.provideMerge(layer$7.pipe(Layer.provide(loop))), Layer.provideMerge(layer$4({}, pool, references, followStore, rebuild)), Layer.provideMerge(layer$5(settingsOptions)), Layer.provideMerge(layer$6), Layer.provideMerge(sandboxes), Layer.provideMerge(layer$22), Layer.provideMerge(layer$1), Layer.provideMerge(layer$25), Layer.provideMerge(layer$9(definitions)), Layer.provideMerge(database), Layer.provideMerge(global), Layer.provideMerge(layer$8({
9024
+ return layer$10.pipe(Layer.provideMerge(layer$7.pipe(Layer.provide(loop))), Layer.provideMerge(layer$4({}, pool, references, followStore, rebuild, viewOf)), Layer.provideMerge(layer$5(settingsOptions)), Layer.provideMerge(layer$6), Layer.provideMerge(sandboxes), Layer.provideMerge(layer$22), Layer.provideMerge(layer$1), Layer.provideMerge(layer$25), Layer.provideMerge(layer$9(definitions)), Layer.provideMerge(database), Layer.provideMerge(global), Layer.provideMerge(layer$8({
8951
9025
  home: paths.home,
8952
9026
  watch: options.watchModels ?? false
8953
9027
  })));
@@ -9245,7 +9319,9 @@ var plugin_exports = /* @__PURE__ */ __exportAll({
9245
9319
  check: () => check,
9246
9320
  define: () => plugin_exports$1.define,
9247
9321
  definition: () => definition$1,
9322
+ identity: () => digest,
9248
9323
  inspect: () => inspect,
9324
+ localName: () => localName,
9249
9325
  parse: () => parse$2,
9250
9326
  probe: () => probe,
9251
9327
  resolve: () => resolve$2,